Showing preview only (790K chars total). Download the full file or copy to clipboard to get everything.
Repository: webclipper/web-clipper
Branch: master
Commit: c8be915332f2
Files: 346
Total size: 707.6 KB
Directory structure:
gitextract_obtgc54t/
├── .dockerignore
├── .eslintignore
├── .eslintrc.js
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report_en-US.md
│ │ ├── bug_report_zh-CN.md
│ │ ├── feature_request_en-US.md
│ │ └── feature_request_zh-CN.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .prettierrc
├── .yarnrc
├── LICENSE
├── README.md
├── bin/
│ ├── index.js
│ ├── main.ts
│ └── scripts/
│ ├── format.ts
│ └── index.ts
├── chrome/
│ ├── html/
│ │ └── error.html
│ └── js/
│ └── icon.js
├── config.json
├── global.d.ts
├── package.json
├── script/
│ ├── build.js
│ ├── release.ts
│ └── utils/
│ └── pack.ts
├── src/
│ ├── __test__/
│ │ └── utils.ts
│ ├── actions/
│ │ ├── account.ts
│ │ ├── clipper.ts
│ │ └── userPreference.ts
│ ├── common/
│ │ ├── backend/
│ │ │ ├── clients/
│ │ │ │ ├── github/
│ │ │ │ │ ├── client.test.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── joplin/
│ │ │ │ │ ├── LegacyJoplinClient.ts
│ │ │ │ │ ├── basic.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── leanote/
│ │ │ │ │ ├── client.test.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ └── interface.ts
│ │ │ │ └── siyuan/
│ │ │ │ ├── client.ts
│ │ │ │ └── types.ts
│ │ │ ├── imageHosting/
│ │ │ │ ├── baklib/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── github/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── service.ts
│ │ │ │ │ └── type.ts
│ │ │ │ ├── imgur/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── interface.ts
│ │ │ │ ├── joplin/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── leanote/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── piclist/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── qcloud/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── siyuan/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── sm.ms/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── wiznote/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ └── yuque_oauth/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── services/
│ │ │ ├── baklib/
│ │ │ │ ├── complete.tsx
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── bear/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── buildin/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── confluence/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── dida365/
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── flomo/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── flowus/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── github/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── github_repository/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── interface.ts
│ │ │ ├── joplin/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── leanote/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── memos/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── notion/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── types.ts
│ │ │ ├── obsidian/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── onenote_oauth/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── server_chan/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── siyuan/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── ticktick/
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── ulysses/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── webdav/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── wiznote/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── wolai/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── youdao/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── yuque/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ └── yuque_oauth/
│ │ │ ├── form.tsx
│ │ │ ├── headerForm.tsx
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── service.ts
│ │ ├── blob.ts
│ │ ├── buffer.ts
│ │ ├── chrome/
│ │ │ └── storage.ts
│ │ ├── error.ts
│ │ ├── getResource.ts
│ │ ├── hooks/
│ │ │ ├── useFilterExtensions.ts
│ │ │ ├── useFilterImageHostingServices.ts
│ │ │ ├── useOriginPermission.ts
│ │ │ └── useVerifiedAccount.tsx
│ │ ├── loading.test.ts
│ │ ├── loading.ts
│ │ ├── locales/
│ │ │ ├── antd.ts
│ │ │ ├── data/
│ │ │ │ ├── de-DE.json
│ │ │ │ ├── de-DE.ts
│ │ │ │ ├── en-US.json
│ │ │ │ ├── en-US.ts
│ │ │ │ ├── ja-JP.json
│ │ │ │ ├── ja-JP.ts
│ │ │ │ ├── ko-KR.json
│ │ │ │ ├── ko-KR.ts
│ │ │ │ ├── ru-RU.json
│ │ │ │ ├── ru-RU.ts
│ │ │ │ ├── zh-CN.json
│ │ │ │ ├── zh-CN.ts
│ │ │ │ ├── zh-TW.json
│ │ │ │ └── zh-TW.ts
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── interface.ts
│ │ ├── matchUrl.test.ts
│ │ ├── matchUrl.ts
│ │ ├── modelTypes/
│ │ │ ├── account.ts
│ │ │ ├── clipper.ts
│ │ │ ├── extensions.ts
│ │ │ └── userPreference.ts
│ │ ├── object.ts
│ │ ├── storage/
│ │ │ ├── __test__/
│ │ │ │ └── index.spec.ts
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── typedCommonStorage.ts
│ │ ├── strings.ts
│ │ ├── types.ts
│ │ └── version/
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── components/
│ │ ├── ExtensionCard/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── IconFont.tsx
│ │ ├── ImageHostingSelect.less
│ │ ├── ImageHostingSelect.tsx
│ │ ├── LinkRender/
│ │ │ └── index.tsx
│ │ ├── RepositorySelect.tsx
│ │ ├── accountItem/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── avatar/
│ │ │ └── index.tsx
│ │ ├── container/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── imageHostingSelectOption/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── imagehostingListItem/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── section/
│ │ │ ├── __snapshots__/
│ │ │ │ └── index.test.tsx.snap
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── share/
│ │ │ └── index.tsx
│ │ └── userItem/
│ │ ├── index.less
│ │ └── index.tsx
│ ├── config.ts
│ ├── extensions/
│ │ ├── common.ts
│ │ ├── contextMenus/
│ │ │ └── saveSelection/
│ │ │ └── saveSelection.ts
│ │ ├── contextMenus.ts
│ │ ├── extensions/
│ │ │ ├── bookmark.ts
│ │ │ ├── extensions/
│ │ │ │ ├── remove.ts
│ │ │ │ ├── selectTool.ts
│ │ │ │ └── uploadImage.ts
│ │ │ ├── fullPage.ts
│ │ │ ├── qrcode.ts
│ │ │ ├── readability.ts
│ │ │ ├── screenshot.ts
│ │ │ ├── select.ts
│ │ │ └── web-clipper/
│ │ │ ├── clear.ts
│ │ │ ├── copyToClipboard.ts
│ │ │ ├── download.ts
│ │ │ ├── link.tsx
│ │ │ └── pangu.ts
│ │ └── index.ts
│ ├── hooks/
│ │ └── useOriginForm.tsx
│ ├── index.html
│ ├── main/
│ │ ├── background.worker.ts
│ │ ├── contentScript.main.ts
│ │ └── tool.main.chrome.ts
│ ├── models/
│ │ ├── account.ts
│ │ ├── clipper.tsx
│ │ └── userPreference.ts
│ ├── pages/
│ │ ├── app.less
│ │ ├── app.tsx
│ │ ├── auth.tsx
│ │ ├── complete/
│ │ │ ├── complete.less
│ │ │ └── complete.tsx
│ │ ├── locale.tsx
│ │ ├── plugin/
│ │ │ ├── Page.tsx
│ │ │ ├── TextEditor.tsx
│ │ │ └── index.less
│ │ ├── preference/
│ │ │ ├── account/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ └── modal/
│ │ │ │ ├── createAccountModal.tsx
│ │ │ │ ├── editAccountModal.tsx
│ │ │ │ └── index.less
│ │ │ ├── base.tsx
│ │ │ ├── changelog/
│ │ │ │ └── index.tsx
│ │ │ ├── extensions/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── imageHosting/
│ │ │ │ ├── form/
│ │ │ │ │ └── addImageHosting.tsx
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ └── privacy/
│ │ │ └── index.tsx
│ │ └── tool/
│ │ ├── ClipExtension.tsx
│ │ ├── Header.tsx
│ │ ├── index.less
│ │ ├── index.tsx
│ │ └── toolExtensions.tsx
│ ├── service/
│ │ ├── common/
│ │ │ ├── config.ts
│ │ │ ├── configuration.ts
│ │ │ ├── contentScript.ts
│ │ │ ├── cookie.ts
│ │ │ ├── extension.ts
│ │ │ ├── ipc.ts
│ │ │ ├── locale.ts
│ │ │ ├── permissions.ts
│ │ │ ├── preference.ts
│ │ │ ├── request.ts
│ │ │ ├── storage.ts
│ │ │ ├── tab.ts
│ │ │ └── webRequest.ts
│ │ ├── config/
│ │ │ └── browser/
│ │ │ └── configService.ts
│ │ ├── configuration/
│ │ │ ├── common/
│ │ │ │ └── generate-local-config.ts
│ │ │ └── configuration.ts
│ │ ├── contentScript/
│ │ │ ├── browser/
│ │ │ │ └── contentScript/
│ │ │ │ ├── contentScript.less
│ │ │ │ └── contentScript.ts
│ │ │ └── common/
│ │ │ └── contentScriptIPC.ts
│ │ ├── cookie/
│ │ │ ├── background/
│ │ │ │ └── cookieService.ts
│ │ │ └── common/
│ │ │ └── cookieIpc.ts
│ │ ├── extension/
│ │ │ └── browser/
│ │ │ ├── extensionContainer.ts
│ │ │ └── extensionService.ts
│ │ ├── ipc/
│ │ │ └── browser/
│ │ │ ├── background-main/
│ │ │ │ └── ipcService.ts
│ │ │ ├── contentScript/
│ │ │ │ └── contentScriptIPCServer.ts
│ │ │ └── popup/
│ │ │ └── ipcClient.ts
│ │ ├── permissions/
│ │ │ ├── chrome/
│ │ │ │ └── permissionsService.ts
│ │ │ └── common/
│ │ │ └── permissionsIpc.ts
│ │ ├── preference/
│ │ │ └── browser/
│ │ │ └── preferenceService.ts
│ │ ├── request/
│ │ │ ├── common/
│ │ │ │ ├── request.test.ts
│ │ │ │ └── request.ts
│ │ │ └── tool/
│ │ │ └── basic.ts
│ │ ├── tab/
│ │ │ ├── browser/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ └── common/
│ │ │ └── tabIpc.ts
│ │ ├── webRequest/
│ │ │ ├── browser/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ ├── chrome/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ └── common/
│ │ │ └── webRequestIPC.ts
│ │ └── worker/
│ │ ├── common/
│ │ │ ├── index.ts
│ │ │ └── workserServiceIPC.ts
│ │ └── worker/
│ │ └── workerService.ts
│ ├── services/
│ │ ├── account/
│ │ │ └── common.ts
│ │ ├── configuration/
│ │ │ └── common/
│ │ │ ├── configuration.ts
│ │ │ └── configurationService.ts
│ │ ├── environment/
│ │ │ └── common/
│ │ │ ├── changelog/
│ │ │ │ ├── CHANGELOG.en-US.md
│ │ │ │ └── CHANGELOG.zh-CN.md
│ │ │ ├── environment.ts
│ │ │ ├── environmentService.ts
│ │ │ └── privacy/
│ │ │ ├── PRIVACY.en-US.md
│ │ │ └── PRIVACY.zh-CN.md
│ │ └── log/
│ │ └── common/
│ │ └── index.ts
│ ├── setupTests.ts
│ └── vendor/
│ └── global.d.ts
├── tsconfig.json
├── vitest.config.ts
└── webpack/
├── plugin/
│ └── webpack-create-extension-manifest-plugin.js
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
node_modules
release
lib
================================================
FILE: .eslintignore
================================================
lib/
dist/
coverage/
node_modules/
chrome/js/icon.js
releases/
================================================
FILE: .eslintrc.js
================================================
module.exports = {
extends: ['@diamondyuan/react-typescript', 'prettier'],
plugins: ['eslint-plugin-prettier'],
rules: {
'no-use-before-define': 'off',
'arrow-body-style': 'off',
'no-redeclare': 'off',
'prettier/prettier': 'error',
'@typescript-eslint/no-unused-vars': 'off',
},
settings: {
'import/resolver': {
webpack: {
config: './webpack/webpack.common.js',
},
},
},
};
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report_en-US.md
================================================
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**please complete the following information**
- Notebook: [e.g. notion,yuque]
- Browser [e.g. chrome, safari]
- Version [e.g. 1.23.0]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report_zh-CN.md
================================================
---
name: Bug 报告
about: 创建报告以帮助我们改进
---
**Bug 描述**
清楚简明地描述错误是什么。
**复现步骤**
重现的步骤:
1. 打开 '...'
2. 点击按钮 '....'
3. 滚动到 '....'
4. 看到错误
**预期行为**
对您期望发生的事情的简洁明了的描述。
**截图**
如果适用,请添加屏幕截图以帮助解释您的问题。
**请填写以下信息**
- 笔记平台: [e.g. notion,yuque]
- 浏览器 [e.g. chrome, safari]
- 版本 [e.g. 1.23.0]
**其他背景**
在此处添加有关该问题的任何其他上下文。
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request_en-US.md
================================================
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request_zh-CN.md
================================================
---
name: 功能请求
about: 为这个项目提出一个想法
---
**您的功能要求与问题有关吗? 请描述。**
清楚,简洁地说明问题所在。 例如 当[...]时,我总是感到沮丧
**描述您想要的解决方案**
对您想要发生的事情的简洁明了的描述。
**描述您考虑过的替代方案**
对您考虑过的所有替代解决方案或功能的简洁明了的描述。
**其他内容**
在此处添加有关功能请求的其他任何上下文或屏幕截图。
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI Test
on:
pull_request_target:
types: [opened, edited, reopened]
push:
branches:
- '**'
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v1
with:
node-version: '16.x'
- name: Install Dependencies
run: npm install --force
- run: npm run cov
env:
GITHUB_BRANCH: ${{ github.ref }}
================================================
FILE: .gitignore
================================================
npm-debug.log
node_modules/
dist/*
!dist/.gitkeep
lib/
coverage/
tmp/
.DS_Store
.idea
.vscode
yarn-error.log
dll/
webclipper.zip
.now
release
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"proseWrap": "never"
}
================================================
FILE: .yarnrc
================================================
version-git-message "chore(release): %s :tada:"
================================================
FILE: LICENSE
================================================
Software License Agreement
Copyright (c) 2020-2020, DiamondYuan. All rights reserved.
Licensed under the terms of GNU General Public License Version 2 or later.
================================================
FILE: README.md
================================================
<h1 align="center">Web Clipper</h1>
<p align="center">
<a href="https://github.com/webclipper/web-clipper/actions">
<img src="https://github.com/webclipper/web-clipper/workflows/CI%20Test/badge.svg" alt="CI Test Status">
</a>
<a href="https://github.com/webclipper/web-clipper/actions">
<img src="https://github.com/webclipper/web-clipper/workflows/Release resource/badge.svg" alt="Release resource status">
</a>
<a href="https://codecov.io/gh/webclipper/web-clipper">
<img src="https://img.shields.io/codecov/c/github/webclipper/web-clipper/master.svg?style=flat-square" alt="Codecov">
</a>
</p>
You can use Web Clipper to save anything on the web to anywhere.
<img src="https://clipper.website/static/image/screenshot.png">
### Support Site
- [FlowUs](https://flowus.cn/)
- [Obsidian](https://obsidian.md/)
- [Github](https://github.com)
- [Yuque](https://www.yuque.com)
- [Buildin.AI](https://buildin.ai/product)
- [Notion](https://www.notion.so/)
- [Youdao](https://note.youdao.com/)
- [OneNote](https://www.onenote.com/)
- [Bear](https://bear.app)
- [Joplin](https://joplinapp.org/)
- [Server Chan](http://sc.ftqq.com/3.version)
- [dida365](https://dida365.com/)
- [baklib](https://www.baklib-free.com/)
- [wolai](https://www.wolai.com/)
- [Leanote](https://github.com/leanote/leanote)
- [Flomo](https://flomoapp.com/)
- [Siyuan](https://b3log.org/siyuan)
- [Ulysses](https://ulysses.app/)
- [Confluence](https://www.atlassian.com/software/confluence)
### Install
- [Chrome](https://chrome.google.com/webstore/detail/web-clipper/mhfbofiokmppgdliakminbgdgcmbhbac)
- [Edge](https://microsoftedge.microsoft.com/addons/detail/opejamnnohhbjflpbhnmdlknhjkfhfdp)
ps: Because the review takes a week, the version will fall behind.
#### From Github
1. Download the webclipper.zip from [release page](https://github.com/webclipper/web-clipper/releases)
2. Go to **chrome://extensions/** and check the box for **Developer mode** in the top right.
3. Locate the ZIP file on your computer and unzip it.
4. Go back to the chrome://extensions/ page and click the **Load unpacked extension** button and select the unzipped folder for your extension to install it.
### Develop
```bash
$ git clone https://github.com/webclipper/web-clipper.git
$ cd web-clipper
$ npm i
$ npm run dev
```
- You should load the 'dist/chrome' folder in Chrome.
- You should load the 'dist/manifest.json' folder in Firefox.
### Test
```bash
$ npm run test
```
### Feedback
| Type | Link |
| -------- | ---------------------------------------------------- |
| Telegram | [Link](https://t.me/joinchat/HoVttRRUIA6aXASixzoqAw) |
================================================
FILE: bin/index.js
================================================
#!/usr/bin/env node
require('ts-node').register({
transpileOnly: true,
});
require('./main');
================================================
FILE: bin/main.ts
================================================
import { hideBin } from 'yargs/helpers';
import { format } from './scripts';
const [command] = hideBin(process.argv);
switch (command) {
case 'format': {
format();
break;
}
default: {
throw new Error('unknown command');
}
}
================================================
FILE: bin/scripts/format.ts
================================================
import * as fs from 'fs';
import * as path from 'path';
export function format() {
const localsPath = path.resolve(__dirname, '../../src/common/locales/data');
const files = fs.readdirSync(localsPath);
const sortedKeys = Object.keys(
JSON.parse(fs.readFileSync(path.resolve(localsPath, 'en-US.json'), { encoding: 'utf-8' }))
).sort((a, b) => a.localeCompare(b));
files
.filter(file => path.extname(file) === '.json')
.map(file => path.resolve(localsPath, file))
.forEach(file => {
const messages = JSON.parse(fs.readFileSync(file, 'utf-8'));
const result = {};
sortedKeys.forEach(key => {
result[key] = messages[key] || '';
});
fs.writeFileSync(file, JSON.stringify(result, null, 2));
});
}
================================================
FILE: bin/scripts/index.ts
================================================
export { format } from './format';
================================================
FILE: chrome/html/error.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plugin Installation Notice</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f9;
}
.container {
text-align: center;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
max-width: 400px;
width: 100%;
}
h1 {
font-size: 24px;
color: #333;
}
p {
font-size: 16px;
color: #666;
line-height: 1.5;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h1>Plugin Installation Notice</h1>
<p>
After installing the plugin, if you want to use it on pages that were already open before installation, please
refresh the page.
</p>
</div>
</body>
</html>
================================================
FILE: chrome/js/icon.js
================================================
window._iconfont_svg_string_1402208='<svg><symbol id="obsidian" viewBox="0 0 1024 1024"><path d="M574.34 7.74l-313.83 168-127.71 268.8 194.04 432 275.22 91.2 49.5-97.92L756.5 261.18 574.34 7.74z" fill="#34208C" ></path><path d="M757.31 260.46l-194.04-115.2-267.3 204.48 306.9 617.28 49.5-97.92 104.94-608.64z" fill="#6C56CC" ></path><path d="M757.13 261.08L574.97 7.64l-11.88 138.24 194.04 115.2z" fill="#AF9FF4" ></path><path d="M562.82 146.02L574.7 7.78l-313.83 168 34.65 174.72 267.3-204.48zM295.9 350.66l31.68 526.08 275.22 91.2-306.9-617.28z" fill="#4A37A0" ></path></symbol><symbol id="siyuan" viewBox="0 0 1024 1024"><path d="M277.344 89.984c-7.072 7.008-12.672 13.344-12.48 14.016s-0.128 0.96-0.704 0.64c-1.504-0.928-12.704 10.784-11.584 12.032 0.512 0.576 0.352 0.704-0.416 0.256-1.44-0.8-7.808 5.536-7.808 7.776 0 0.736-0.416 1.056-0.928 0.736-1.184-0.736-4.064 2.368-3.072 3.328 0.416 0.416 0.064 0.736-0.736 0.736-1.92 0-7.488 5.568-7.488 7.488 0 0.8-0.32 1.152-0.736 0.768-0.768-0.768-10.752 8.448-24.096 22.144-22.4 23.04-41.152 41.312-41.728 40.768-0.384-0.384-0.672 0.288-0.672 1.472s-0.544 1.76-1.216 1.344c-1.12-0.672-1.76 0.928-1.344 3.36 0.064 0.512-0.544 0.768-1.376 0.608s-1.376 0.416-1.184 1.312c0.192 0.896-0.192 1.312-0.8 0.928s-4.096 2.4-7.68 6.176c-10.112 10.656-23.04 23.488-32.768 32.544-4.864 4.544-8.48 8.256-8.032 8.256s-0.96 1.504-3.2 3.36c-7.968 6.56-22.048 21.696-23.328 25.056-0.448 1.184-1.664 2.144-2.688 2.144s-1.888 0.864-1.888 1.888-0.448 1.632-0.96 1.312c-1.248-0.768-23.616 20.704-22.592 21.696 0.416 0.416 0.064 0.768-0.768 0.768s-4.288 2.88-7.648 6.4c-16.064 16.768-25.664 26.592-26.048 26.592-0.224 0-5.12 4.704-10.912 10.432l-10.496 10.432v289.024c0 197.632 0.384 289.76 1.248 291.392 1.856 3.456 3.776 2.848 7.904-2.432 2.016-2.592 4.096-4.448 4.576-4.128s0.928-0.416 0.928-1.568 0.288-1.824 0.672-1.44c0.64 0.64 92.768-90.208 157.888-155.712 5.536-5.568 25.472-25.472 44.288-44.192s33.856-34.624 33.408-35.328c-0.416-0.704-0.256-0.928 0.384-0.544 1.664 1.024 8-5.088 6.976-6.72-0.48-0.768-0.256-0.992 0.512-0.512 1.376 0.864 6.368-3.648 6.368-5.792 0-0.64 0.288-0.896 0.672-0.544 2.272 2.304 27.616-26.656 26.944-30.816-0.384-2.368-0.704-133.92-0.704-292.352s-0.416-288.032-0.928-287.968-6.688 5.856-13.728 12.864z m433.504 2.656c-8.576 8.576-15.584 16.32-15.584 17.28s-0.352 1.344-0.736 0.928c-0.576-0.608-39.328 37.28-88.48 86.496l-48.864 49.024-43.968 44.128v292.8c0 161.056 0.288 292.8 0.672 292.8 1.344 0 6.496-5.856 5.792-6.592-0.416-0.416 0.032-0.736 0.992-0.768s5.312-3.968 9.664-8.8c4.352-4.8 11.456-12.064 15.744-16.16s15.584-15.296 25.056-24.96c9.472-9.664 20.704-20.928 24.96-25.056 21.728-21.12 36.896-36.224 36.448-36.224-0.288 0 4.512-5.12 10.656-11.392s11.584-11.2 12.096-10.912 0.928-0.48 0.928-1.632 0.352-1.76 0.736-1.344c0.928 0.928 4.128-2.144 4.128-4 0-0.736 0.416-1.152 0.928-0.864 0.864 0.512 7.36-5.44 6.72-6.144-0.16-0.192 0.096-0.448 0.608-0.576 1.696-0.448 3.712-2.816 3.168-3.744-0.32-0.512 0.352-0.928 1.44-0.928s1.632-0.544 1.216-1.216c-0.416-0.672-0.256-1.216 0.352-1.216s13.024-11.808 27.584-26.208l26.464-26.208v-290.176c0-159.616-0.352-291.04-0.736-292.096-1.152-3.008-1.312-2.848-17.888 13.696z" fill="#D23E31" ></path><path d="M292.032 75.776c0 0.672 0.832 1.216 1.824 1.216s1.824-0.544 1.824-1.216c0-0.672-0.832-1.216-1.824-1.216s-1.824 0.544-1.824 1.216z m3.264 2.016c-0.448 0.448-0.8 132.288-0.768 292.992l0.064 292.16 52.032 51.328c43.52 42.912 109.12 107.712 135.136 133.44 3.52 3.488 11.104 11.296 16.8 17.344s11.072 11.04 11.904 11.072c1.216 0.064 1.536-59.232 1.536-292.64V290.784l-5.632-5.152a411.008 411.008 0 0 1-13.152-12.8c-6.528-6.624-24.256-24.416-49.824-49.952-3.936-3.936-14.88-15.04-24.288-24.608s-17.088-17.12-17.088-16.736-3.712-3.2-8.256-7.968a1937.92 1937.92 0 0 0-27.2-27.52c-20.928-20.832-25.6-25.536-51.104-51.328-17.184-17.376-18.56-18.528-20.192-16.896z m436.192 1.92c-0.416 1.504-0.736 133.248-0.736 292.768v290.016l143.584 143.488c113.824 113.728 144.224 143.488 146.656 143.488h3.04V366.496l-144.8-144.768c-79.648-79.616-145.28-144.768-145.888-144.768s-1.408 1.248-1.824 2.752z" fill="#3B3E43" ></path><path d="M729.472 76.672c-0.544 104.512-0.32 585.248 0.288 584.608 1.184-1.184 1.824-585.536 0.608-585.536a0.928 0.928 0 0 0-0.928 0.928zM292.128 366.304c0.064 159.104 0.544 290.688 1.088 292.352s1.152-128.512 1.408-289.312c0.352-233.28 0.128-292.352-1.088-292.352s-1.504 58.592-1.408 289.312z m220.448 217.184c0 161.312 0.16 227.104 0.32 146.24s0.192-212.832 0-293.28c-0.192-80.416-0.32-14.272-0.32 147.04z m180.224 113.952l-7.904 8.256 8.256-7.904c7.68-7.328 8.8-8.608 7.904-8.608-0.192 0-3.904 3.712-8.256 8.256zM131.616 234.624c-1.152 1.344-1.664 2.432-1.152 2.432s1.888-1.12 3.04-2.432c1.152-1.344 1.664-2.432 1.152-2.432s-1.888 1.12-3.04 2.432zM82.496 283.424c-0.448 0.736-0.384 1.312 0.192 1.312s0.16 0.544-0.832 1.216c-1.28 0.832-1.344 1.184-0.256 1.216 0.864 0 1.888-0.832 2.272-1.888 0.864-2.272-0.192-3.712-1.344-1.856z m560.64 462.816c-6.176 6.272-10.816 11.584-10.4 11.84s5.856-4.704 12.064-10.944c6.208-6.24 10.88-11.584 10.4-11.84s-5.92 4.672-12.064 10.944zM121.472 833.696l-3.584 3.968 3.968-3.584a30.816 30.816 0 0 0 3.968-3.968c0-0.928-0.992-0.128-4.352 3.584zM7.904 945.824c-1.216 1.344-1.92 2.432-1.6 2.432s1.888-1.12 3.424-2.432 2.272-2.432 1.6-2.432c-0.672 0-2.208 1.12-3.424 2.432z" ></path></symbol><symbol id="leanote" viewBox="0 0 1024 1024"><path d="M900.98255 0H123.01745C55.417206 0 0.187431 55.354728 0.187431 122.830018v778.215009c0 67.600244 55.229774 122.830018 122.830019 122.830019h777.9651c67.600244 0 122.830018-55.229774 122.830019-122.830019V122.830018c0-67.47529-55.229774-122.830018-122.830019-122.830018zM675.815009 798.707505L532.492495 901.045027h-40.98499l-143.322514-102.337522V552.922514l102.337523 20.492495h143.322513l81.845028-20.492495v245.784991z m143.322514-552.922514c-40.984991 0 0-20.492495-81.845028-20.492496-61.477486 0-122.830018 143.322514-122.830018 143.322514 40.984991 0 61.477486 40.984991 61.477486 40.984991v102.337523s-81.845027 20.492495-163.815009 20.492495c-81.845027 0-163.815009-20.492495-163.815009-20.492495v-102.337523S368.802441 368.615009 409.787431 368.615009c0 0-61.477486-143.322514-122.830018-143.322514-81.845027 0-67.600244 20.492495-81.845027 20.492496-40.984991 0-47.107749-102.337523 0-102.337523 204.675046 0 225.167541 204.8 225.167541 204.8h163.815009s20.492495-204.8 225.167541-204.8c34.612325-0.124954 40.860037 102.337523-0.124954 102.337523z" ></path></symbol><symbol id="flomo" viewBox="0 0 1024 1024"><path d="M0 0h1024v1024H0z" fill="#FAFAFA" ></path><path d="M709.461333 507.211852H332.069926V399.559111H779.567407l-65.422222 105.263408c0 2.389333-2.341926 2.389333-4.683852 2.389333zM807.604148 339.749926H450.066963L515.508148 234.477037c2.341926 0 4.67437-2.389333 7.016296-2.389333H877.700741l-65.422222 105.263407c0 2.398815-2.341926 2.398815-4.683852 2.398815z" fill="#30CF79" ></path><path d="M337.910519 791.912296c-105.159111 0-191.620741-88.519111-191.620741-196.181333 0-107.662222 86.46163-196.171852 191.620741-196.171852 105.14963 0 191.620741 88.50963 191.62074 196.171852s-86.471111 196.171852-191.62074 196.171852z m0-282.311111c-46.743704 0-86.471111 38.276741-86.471112 88.519111 0 47.853037 37.394963 88.528593 86.471112 88.528593 49.066667 0 86.46163-38.286222 86.461629-88.528593-2.341926-50.24237-39.727407-88.519111-86.471111-88.519111z" fill="#30CF79" ></path></symbol><symbol id="github_repository" viewBox="0 0 1024 1024"><path d="M881 112c17.673 0 32 14.327 32 32v736c0 17.673-14.327 32-32 32H144c-17.673 0-32-14.327-32-32V144c0-17.673 14.327-32 32-32h737z m-564.5 72H184v656h132.5V184z m524.5 0h-56.032v286.08c0 8.837-7.163 16-16 16a16 16 0 0 1-9.008-2.777l-87.009-59.275-88.035 59.085c-7.337 4.924-17.277 2.968-22.201-4.369a16 16 0 0 1-2.715-8.916V184H388.5v656H841V184z m-120.032 0H624v195.804l49.22-33.033 47.748 32.528V184z" ></path></symbol><symbol id="wolai" viewBox="0 0 1024 1024"><path d="M736.08853333 85.33333333c70.44053333 0 95.984 7.3344 121.73653334 21.1072 25.75146667 13.77173333 45.96266667 33.98293333 59.7344 59.7344l2.43306666 4.69013334C932.1952 195.18826667 938.66666667 221.73973333 938.66666667 287.91146667v448.17706666l-0.08106667 12.32213334c-0.832 61.216-8.08746667 85.2224-21.02613333 109.4144-13.77173333 25.75146667-33.98293333 45.96266667-59.7344 59.7344l-4.69013334 2.43306666C828.81173333 932.1952 802.26026667 938.66666667 736.08853333 938.66666667H287.91146667l-12.32213334-0.08106667c-61.216-0.832-85.2224-8.08746667-109.4144-21.02613333-25.75146667-13.77173333-45.96266667-33.98293333-59.7344-59.7344l-2.43306666-4.69013334c-11.808-23.5392-18.25066667-49.1648-18.65386667-110.76586666L85.33333333 287.91146667c0-70.44053333 7.3344-95.984 21.1072-121.73653334 13.77173333-25.75146667 33.98293333-45.96266667 59.7344-59.7344l4.69013334-2.43306666c23.5392-11.808 49.1648-18.25066667 110.76586666-18.65386667L736.08853333 85.33333333z" fill="#FFFFFF" ></path><path d="M736.08853333 85.33333333c70.44053333 0 95.984 7.3344 121.73653334 21.1072 25.75146667 13.77173333 45.96266667 33.98293333 59.7344 59.7344l2.43306666 4.69013334C932.1952 195.18826667 938.66666667 221.73973333 938.66666667 287.91146667v448.17706666l-0.08106667 12.32213334c-0.832 61.216-8.08746667 85.2224-21.02613333 109.4144-13.77173333 25.75146667-33.98293333 45.96266667-59.7344 59.7344l-4.69013334 2.43306666C828.81173333 932.1952 802.26026667 938.66666667 736.08853333 938.66666667H287.91146667l-12.32213334-0.08106667c-61.216-0.832-85.2224-8.08746667-109.4144-21.02613333-25.75146667-13.77173333-45.96266667-33.98293333-59.7344-59.7344l-2.43306666-4.69013334c-11.808-23.5392-18.25066667-49.1648-18.65386667-110.76586666L85.33333333 287.91146667c0-70.44053333 7.3344-95.984 21.1072-121.73653334 13.77173333-25.75146667 33.98293333-45.96266667 59.7344-59.7344l4.69013334-2.43306666c23.5392-11.808 49.1648-18.25066667 110.76586666-18.65386667L736.08853333 85.33333333z m5.03466667 94.82453334H282.8768l-13.63413333 0.144c-34.95786667 0.6528-46.58453333 3.4528-58.35413334 9.7472-9.2288 4.93546667-15.904 11.61173333-20.84053333 20.84053333-7.05066667 13.18186667-9.71626667 26.18453333-9.8912 71.98826667v458.24426666l0.144 13.63413334c0.6528 34.95786667 3.4528 46.58453333 9.7472 58.35413333 4.93546667 9.2288 11.61173333 15.904 20.84053333 20.84053333 13.18186667 7.05066667 26.18453333 9.71626667 71.98826667 9.8912l453.21066667 0.01066667c49.90933333 0 63.36853333-2.60053333 77.02186666-9.90186667 9.2288-4.93546667 15.904-11.61173333 20.84053334-20.84053333 7.05066667-13.18186667 9.71626667-26.18453333 9.8912-71.98826667l0.01066666-453.21066666c0-49.90933333-2.60053333-63.36853333-9.90186666-77.02186667-4.93546667-9.2288-11.61173333-15.904-20.84053334-20.84053333-13.18186667-7.05066667-26.18453333-9.71626667-71.98826666-9.8912z m-406.90133333 431.39733333c43.20106667 0 78.22293333 35.02186667 78.22293333 78.22293333 0 43.2-35.02186667 78.22186667-78.22293333 78.22186667-43.2 0-78.22186667-35.0208-78.22186667-78.22186667s35.0208-78.22293333 78.22186667-78.22293333z" fill="#000000" ></path></symbol><symbol id="baklib" viewBox="0 0 1024 1024"><path d="M769.62929778 269.31048297c-2.66998518-17.58549333-3.05834667-34.97680592-1.45635556-51.90693927 0.92235852 3.14330075 1.45635555 6.85700741 1.43208296 11.22607408 20.46179555 35.11030518 55.62064592 59.77125925 95.87674074 66.79817481 0.03640889 0.07281778 0.08495408 0.14563555 0.12136297 0.21845333 36.45743408 4.28411259 68.36375703 26.78480592 84.79630222 59.35862519a136.50906075 136.50906075 0 0 1-60.68148148 25.15854222c-26.67557925 4.05352297-52.72007111 0.07281778-75.80330666-10.08526222-22.65846518-28.14407111-38.45992297-62.45338075-44.28534519-100.76766814z" fill="#FFA404" ></path><path d="M834.7769363 270.34206815c4.56324741-0.46117925 9.19931259-0.69176889 13.88392295-0.6917689 59.81980445 0 110.65874963 38.42351408 129.21514667 91.92030816 28.69020445-42.18576592 42.16149333-94.88156445 33.89667556-149.30071704-11.4566637-75.41494518-61.74947555-135.28329482-127.4796563-162.42005334-22.24583111 10.36439703-42.02799408 24.68522667-58.66685629 41.88235852-0.78885925 0.83740445-1.57771852 1.66267259-2.35444148 2.51221334-1.26217482 1.34712889-2.46366815 2.73066667-3.68943408 4.10206814a212.83058725 212.83058725 0 0 0-21.17783703 28.79943112c-0.54613333 0.89808592-1.11653925 1.78403555-1.6505363 2.69425777-0.74031408 1.26217482-1.44421925 2.54862222-2.16026075 3.82293333-11.42025482 20.55888592-19.29671111 43.19307852-22.82837333 67.01662816-0.04854518 0.29127111-0.06068148 0.41263408 0.06068149 0.48545184 11.91784297 30.37714963 34.4185363 54.74683259 62.95096889 69.1768889z" fill="#FFA404" ></path><path d="M911.97591703 61.94972445c-41.71245037 85.26961778-65.15977482 181.10994963-65.15977481 282.4358874 0 87.32065185 25.48622222 129.40932741 25.48622223 216.9605689 0 0 0.03640889-0.14563555 0.03640888 1.82044443 0 250.72374518-203.24655408 453.97029925-453.97029925 453.97029927-107.68535703 0-211.37787259-30.93541925-307.76433778-119.3119289 145.64769185-60.32952889 203.92618667-152.00711111 229.55804445-195.49146073-194.24142222-56.61582222-330.77475555-237.37381925-327.78922667-443.6908563 71.60414815 24.72163555 147.26181925 41.42117925 226.05065481 48.75150223 57.12554667 5.32783408 113.52291555 5.46133333 168.77947259 0.9466311C423.47785482 141.30896592 564.27102815 10.74669037 735.59912297 10.74669037c64.46800592 0 124.57908148 18.55639703 175.39375406 50.52340148-0.57040592 0.18204445-1.11653925 0.40049778-1.67480888 0.58254223 0.88594963 0.0121363 1.77189925 0.06068148 2.65784888 0.09709037z" fill="#03C9A9" ></path><path d="M812.1670163 47.21626075c-63.46069333 0-114.90645333 51.42148741-114.90645333 114.85790814s51.44576 114.85790815 114.90645333 114.85790814c13.65333333 0 26.73626075-2.37871408 38.88469333-6.74778073a639.56946489 639.56946489 0 0 1 48.90927407-182.19008c-21.08074667-24.94008889-52.58657185-40.77795555-87.7939674-40.77795555z" fill="#FFFFFF" ></path><path d="M814.01173333 101.90241185c12.07561482 0 21.86960592 9.80612741 21.86960592 21.89387852s-9.79399111 21.89387852-21.86960592 21.89387852-21.86960592-9.80612741-21.86960592-21.89387852 9.79399111-21.89387852 21.86960592-21.89387852z" ></path></symbol><symbol id="wiznote" viewBox="0 0 1024 1024"><path d="M67.328 189.44h125.44l133.632 402.688L468.224 189.44h87.552l141.824 402.688L831.232 189.44h125.44L742.4 834.56h-87.04L512 426.24l-142.848 409.6H281.6z" fill="#000000" ></path></symbol><symbol id="webdav" viewBox="0 0 1024 1024"><path d="M1024 896H608.000181v-63.999639h351.999458V64.000361H653.248305L525.248305 192.000361H64.000361v640h351.999458v63.999639H0V128h498.751695L626.751695 0h397.248305zM831.999639 960.000361h64.000361v63.999639h-64.000361zM959.999639 960.000361h64.000361v63.999639h-64.000361zM0 960.000361h64.000361v63.999639H0zM128 960.000361h64.000361v63.999639h-64.000361z" ></path><path d="M256 960.000361h512v63.999639H256z" ></path><path d="M480.000181 832.000361h63.999638v191.999639h-63.999638z" ></path><path d="M32.000542 704.000361h959.999639v63.999639H32.000542z" ></path></symbol><symbol id="dida365" viewBox="0 0 1024 1024"><path d="M526.564641 27.133015v110.702703c-219.415651 5.60749-391.258082 189.931108-383.751281 399.036213 7.054584 198.975446 173.560855 362.858859 372.355414 368.466348 208.472001 5.87882 390.444091-164.516517 397.136902-381.851969H1023.008379c-4.522169 283.540011-241.212507 508.201378-513.627981 500.24236-260.567391-7.597244-476.003533-226.108461-481.158806-486.856739C22.794989 265.722664 245.918819 32.469175 526.564641 27.133015z" ></path><path d="M381.493452 399.48843l-84.021904 98.945062 189.026674 158.45681a66.928105 66.928105 0 0 0 47.754107 20.982865 66.204557 66.204557 0 0 0 43.955484-17.184243l357.070483-446.79032-106.904081-80.223282L524.66533 519.778131z" ></path><path d="M499.431626 0v110.702703c-219.415651 5.60749-391.258082 189.931108-383.751281 399.036212 7.054584 198.975446 173.560855 362.858859 372.355414 368.466349 208.472001 5.87882 390.444091-164.516517 397.136902-381.85197H995.875363c-4.522169 283.540011-241.212507 508.201378-513.627981 500.24236-260.567391-7.597244-476.003533-226.108461-481.158805-486.856739C-4.338026 238.589648 218.785803 5.33616 499.431626 0z" fill="#617FDE" ></path><path d="M354.360437 372.355414l-84.021905 98.945063 189.026674 158.45681A66.928105 66.928105 0 0 0 507.481087 651.101925a66.204557 66.204557 0 0 0 43.955485-17.184243l357.070482-446.790319-106.904081-80.223282L497.532314 492.645116z" fill="#FFB000" ></path></symbol><symbol id="confluence" viewBox="0 0 1024 1024"><path d="M908.66688 905.073778C826.078436 988.344889 704.649102 1024 512.293547 1024c-189.127111 0-313.770667-35.655111-396.344889-118.926222-82.588444-83.271111-121.358222-217.6-115.342222-396.999111-4.579556-189.511111 32.753778-313.742222 115.342222-396.999111C198.537102 27.804444 327.475769 0 512.009102 0c181.248 0 314.069333 27.804444 396.657778 111.075556C991.255324 194.346667 1024.009102 325.361778 1024.009102 508.074667c0 178.801778-32.753778 313.728-115.342222 396.999111z" fill="#5CB4FF" ></path><path d="M235.13088 668.245333c-6.215111 10.140444-13.198222 21.902222-19.114667 31.260445a19.114667 19.114667 0 0 0 6.4 25.998222l124.273778 76.472889a19.114667 19.114667 0 0 0 26.467556-6.499556c4.977778-8.32 11.377778-19.114667 18.346666-30.677333 49.237333-81.265778 98.759111-71.324444 188.046223-28.686222l123.207111 58.595555a19.114667 19.114667 0 0 0 25.728-9.557333l59.164444-133.831111a19.114667 19.114667 0 0 0-9.557333-25.031111 15014.968889 15014.968889 0 0 1-124.273778-59.079111c-167.480889-81.351111-309.816889-76.088889-418.702222 101.034666z" fill="#DBEFFF" ></path><path d="M792.329102 364.544c6.215111-10.126222 13.198222-21.888 19.128889-31.246222a19.114667 19.114667 0 0 0-6.4-26.012445l-124.273778-76.472889a19.114667 19.114667 0 0 0-27.249777 6.314667c-4.977778 8.32-11.377778 19.114667-18.346667 30.677333-49.237333 81.265778-98.759111 71.324444-188.032 28.686223l-122.837333-58.311111a19.114667 19.114667 0 0 0-25.713778 9.557333l-59.164445 133.831111a19.114667 19.114667 0 0 0 9.543111 25.031111c26.012444 12.245333 77.724444 36.622222 124.273778 59.093333 167.864889 81.251556 310.186667 75.804444 419.072-101.148444z" fill="#9ED2FF" ></path></symbol><symbol id="medium" viewBox="0 0 1024 1024"><path d="M512 1024C229.21216 1024 0 794.78784 0 512S229.21216 0 512 0 1024 229.21216 1024 512 794.78784 1024 512 1024z m274.432-353.15712h-21.42208c-4.01408 0-8.192-2.048-12.53376-6.144-4.3008-4.13696-6.47168-8.11008-6.47168-11.91936V372.81792c0-3.76832 2.21184-8.02816 6.63552-12.6976 4.42368-4.66944 8.56064-6.9632 12.36992-6.9632h21.42208V286.72h-202.1376l-67.25632 260.87424h-1.8432L448.512 286.72H245.76v66.43712h20.80768c4.21888 0 8.6016 2.33472 12.98432 6.9632 4.42368 4.66944 6.63552 8.88832 6.63552 12.6976v279.9616c0 3.80928-2.21184 7.7824-6.63552 11.8784-4.42368 4.13696-8.76544 6.18496-12.98432 6.18496H245.76V737.28h162.32448v-66.43712h-40.7552V376.66816h2.37568L463.2576 737.28h73.3184l94.74048-360.61184h1.80224v294.17472h-40.42752V737.28h193.7408v-66.43712z" fill="#666666" ></path></symbol><symbol id="ulysses" viewBox="0 0 1024 1024"><path d="M463.4 471.3c0-114-169.3-407.3-334.5-407.3-82.6 0 24.8 162.9 41.3 285.1 4.1 40.7-33 162.9-33 203.6s24.8 81.5 148.7 81.5c144.6 0 161.1-81.5 161.1-81.5s16.4-20.3 16.4-81.4zM562.6 471.3c0-114 169.3-407.3 334.5-407.3 82.6 0-24.8 162.9-41.3 285.1-4.1 40.7 33 162.9 33 203.6s-24.8 81.5-148.7 81.5c-144.6 0-161.1-81.5-161.1-81.5s-16.4-20.3-16.4-81.4zM471.7 674.9s-198.3-16.3-289.1 73.3C100 829.7 137.1 960 306.5 960c185.9 0 165.2-195.5 165.2-195.5v-89.6zM554.3 674.9s198.3-16.3 289.1 73.3C926 829.7 888.9 960 719.5 960c-185.9 0-165.2-195.5-165.2-195.5v-89.6z" ></path></symbol><symbol id="coffee" viewBox="0 0 1027 1024"><path d="M559.854933 278.493867a23.210667 23.210667 0 0 0 23.210667-23.210667v-139.264a23.210667 23.210667 0 1 0-46.421333 0v139.264c0 12.834133 10.376533 23.210667 23.210666 23.210667z m-139.264 0a23.210667 23.210667 0 0 0 23.210667-23.210667v-232.106667a23.210667 23.210667 0 1 0-46.421333 0v232.106667c0 12.834133 10.410667 23.210667 23.210666 23.210667z m278.493867 46.421333a23.210667 23.210667 0 0 0 23.210667-23.210667v-232.106666a23.210667 23.210667 0 1 0-46.421334 0v232.106666c0 12.8 10.410667 23.210667 23.210667 23.210667zM142.1312 278.459733a23.210667 23.210667 0 0 0 23.210667-23.210666v-139.264a23.210667 23.210667 0 1 0-46.421334 0v139.264c0 12.834133 10.376533 23.210667 23.210667 23.210666z m742.6048 208.861867h-46.421333v-69.632c0-25.6-20.7872-46.421333-46.421334-46.421333H49.288533c-25.6 0-46.421333 20.821333-46.421333 46.421333v278.493867c0 118.3744 63.556267 221.696 158.208 278.459733H26.077867a23.210667 23.210667 0 1 0 0 46.421333H815.104a23.210667 23.210667 0 1 0 0-46.421333h-134.792533a325.256533 325.256533 0 0 0 150.357333-208.861867h54.0672a139.264 139.264 0 1 0 0-278.459733zM513.4336 974.677333h-185.685333a277.504 277.504 0 0 1-207.121067-92.842666H720.554667a277.504 277.504 0 0 1-207.189334 92.842666z m278.493867-278.459733a276.7872 276.7872 0 0 1-37.5808 139.229867H86.869333a276.7872 276.7872 0 0 1-37.5808-139.264v-278.459734h742.638934v278.493867z m92.842666 23.210667h-47.616c0.546133-7.714133 1.160533-15.36 1.160534-23.210667v-162.474667h46.421333a92.842667 92.842667 0 0 1 0 185.685334zM281.326933 324.881067a23.210667 23.210667 0 0 0 23.210667-23.210667V69.632a23.210667 23.210667 0 1 0-46.421333 0v232.072533c0 12.8 10.376533 23.210667 23.210666 23.210667z" fill="#333333" ></path></symbol><symbol id="jianguo" viewBox="0 0 1024 1024"><path d="M873.728 322.56c0 0-1.6-2.24-2.496-3.072-10.624-10.624-28.224-10.24-38.912 0.448-8.96 8.896-9.792 24.96-4.224 32.896 0.512 0.832 0.768 1.408 1.024 1.664 73.216 117.632 94.656 262.528 55.232 399.168l-0.64 2.176L883.2 758.144c-1.984 10.304-7.04 19.712-14.464 27.2-10.112 9.856-23.296 15.424-37.568 15.424-14.08 0-27.456-5.568-37.504-15.424-2.304-2.304-4.288-4.864-6.464-7.936l-8.192-12.032c47.808-86.72 32.512-306.88-89.28-428.48C598.848 246.016 494.976 221.824 403.456 221.824c-3.264 0-11.392 0.064-12.224 0.128-0.064 0-0.128 0-0.192 0l0 0c-6.4 0.384-12.672 2.88-17.536 7.808-10.688 10.688-10.688 27.968 0 38.592 5.312 5.312 12.288 8 19.328 8 2.944-0.064 7.744 0 10.688 0 99.968 0 180.992 32.384 247.68 99.072 50.88 50.752 85.76 130.112 95.68 217.728 9.408 82.112-7.04 139.712-21.76 154.432-95.04 94.848-241.088 158.72-363.264 158.72-73.984 0-133.76-22.208-177.664-65.984-54.976-54.912-65.088-131.392-63.808-185.856C123.072 535.68 181.888 398.016 266.112 308.032c0 0 0-0.064 0.128-0.064 19.52-21.44 33.6-37.12 33.6-37.12S272.192 254.016 255.232 243.52l0.256-0.064c-0.064 0-0.128 0.064-0.256 0.064l-9.216-6.4C243.136 235.136 240.704 233.152 238.656 231.104c-20.608-20.608-20.608-54.208 0-74.816 5.44-5.44 12.032-9.6 19.52-12.224l17.216-6.208c42.816-11.712 87.04-17.664 131.776-17.664 132.608 0 257.408 51.584 351.232 145.216 7.68 7.68 14.912 15.616 22.016 23.68l25.792-25.664 0 0 10.624-10.56 2.368-2.496 0 0 71.872-71.744c12.352-12.288 12.352-32.32 0-44.672-12.48-12.288-32.512-12.288-44.8 0l-71.808 71.616c-104.384-93.312-235.84-140.032-367.296-140.032-56.448 0-112.832 8.64-167.104 25.792-0.192 0.384-0.256 0.832-0.448 1.28C225.216 97.856 211.584 106.112 200 117.632c-42.048 41.984-42.048 110.08 0 152.064C204.608 274.368 209.6 278.4 214.784 281.92c0 0.512 0 0.896 0 1.344-143.232 163.392-215.104 450.176-69.312 595.712 57.728 57.664 134.464 82.048 216.32 82.048 132.096 0 277.504-63.488 378.688-153.088 0.576 0 1.088 0 1.536 0.064 3.776 5.632 8.064 10.944 12.928 15.872 21.056 20.928 48.64 31.488 76.16 31.488s55.168-10.56 76.16-31.488c15.744-15.68 25.536-34.88 29.504-55.168C979.776 619.968 958.848 456.96 873.728 322.56z" ></path></symbol><symbol id="joplin" viewBox="0 0 1024 1024"><path d="M513.8176 140.032c-4.96896 5.66784-4.97664 5.74976-4.41344 49.33888 0.69632 53.7856-1.77664 49.14176 26.18112 49.18784 29.54752 0.04864 35.59936 2.46016 42.4192 16.90112l3.39968 7.19616 0.55808 210.75712c0.33536 127.19616 0.18176 213.06368-0.38912 216.576-1.39776 8.576-7.41632 26.08896-10.26304 29.8624-3.3792 4.47744-16.25344 17.42848-17.32608 17.42848-0.47616 0-2.33472 1.23648-4.13184 2.74688-2.92352 2.46016-8.64768 4.56192-25.05216 9.19808-7.05536 1.99424-31.8976 1.91488-39.424-0.12544-3.0976-0.83968-9.7792-2.51904-14.848-3.73504-8.47104-2.03008-29.4144-11.09504-30.7968-13.32992-0.32256-0.51968-3.28192-2.30656-6.5792-3.97056-3.29728-1.664-6.2976-3.51488-6.66624-4.11392-0.3712-0.59904-2.36288-2.17088-4.4288-3.49184-3.77088-2.41408-18.27328-16.50176-24.50176-23.80544-15.56224-18.24-27.27424-47.7952-27.32544-68.9408-0.02816-11.33824 5.3888-31.68256 9.29536-34.92608 0.85504-0.70912 2.39616-2.944 3.42784-4.9664 1.74848-3.42528 8.2688-9.93024 15.0528-15.01696 17.10848-12.82816 57.8432-18.06848 79.05792-10.16832 21.01248 7.82592 24.08448 8.7168 27.91424 8.10496 6.21312-0.99584 6.16448-0.41472 5.80608-70.88384l-0.32-62.83264-3.584-2.99264c-10.27328-8.576-71.93088-13.42208-99.328-7.81056-22.1056 4.5312-35.94496 8.01024-39.52384 9.93792-2.19904 1.18528-5.83936 2.63168-8.09216 3.21536-3.73248 0.96768-13.9264 5.48864-23.04 10.21952-12.51072 6.49216-17.2544 9.20064-17.56672 10.03264-0.19456 0.51968-4.50048 3.74016-9.56928 7.15776-9.20064 6.20032-30.10304 25.6896-35.8656 33.44128-1.6768 2.2528-4.8896 6.4768-7.1424 9.38496-7.70816 9.95584-10.48064 14.1696-11.12832 16.90368-0.35584 1.50528-1.81248 4.12416-3.2384 5.81632-1.42336 1.69216-3.48928 5.69344-4.58752 8.88576-1.1008 3.19488-2.9824 7.3984-4.18304 9.34144-2.0736 3.35616-6.53312 16.768-9.64864 29.02784-6.11328 24.04608-4.93568 94.70976 1.7152 103.06048 0.51456 0.64512 2.0352 5.61408 3.3792 11.0464 1.344 5.42976 3.39712 11.48416 4.55936 13.45792 1.1648 1.9712 2.97984 6.21568 4.03712 9.4336 1.05728 3.21792 3.3664 8.13056 5.1328 10.91584s3.21024 5.82144 3.21024 6.75072c0 0.92672 1.92256 4.63872 4.27008 8.24576 2.35008 3.6096 6.48448 10.01728 9.18784 14.24128 11.46368 17.89952 33.87648 43.17184 50.40384 56.832 2.7264 2.2528 6.17472 5.2864 7.6672 6.74304 1.48992 1.45664 4.43904 3.68384 6.55104 4.94848 2.112 1.26464 3.84 2.6368 3.84 3.05152 0 0.41472 1.9584 1.85088 4.352 3.19488 2.3936 1.344 6.34624 3.96288 8.78336 5.82144 2.43456 1.856 5.0176 3.37664 5.73696 3.37664 0.71936 0 2.51904 1.12128 3.99616 2.49088 2.61888 2.42688 24.75264 13.89312 26.81856 13.89312 0.57088 0 2.64192 1.08288 4.60288 2.40384 1.96096 1.32352 6.79168 3.46112 10.73408 4.75136 3.9424 1.29024 9.24416 3.38432 11.77856 4.65152 2.53696 1.26976 8.75776 3.25888 13.824 4.42112a405.1456 405.1456 0 0 1 16.38144 4.14976c30.49728 8.66816 99.04384 8.19456 123.55328-0.84992 3.008-1.11104 8.69632-2.79552 12.63872-3.74272 3.9424-0.94976 9.7024-3.03616 12.8-4.63872 3.0976-1.60256 7.69792-3.63008 10.22208-4.50816 2.52672-0.87808 5.75232-2.36544 7.168-3.3024 1.41824-0.93952 4.88192-2.94144 7.69792-4.44672 14.82752-7.93088 19.02848-10.432 20.20864-12.02176 0.71168-0.96256 2.67776-2.61888 4.36736-3.68128 8.3584-5.25312 33.14688-29.38624 37.75744-36.75904 0.99072-1.58464 2.48832-3.65824 3.32288-4.608 2.432-2.75456 8.24064-11.55072 11.06176-16.74752a123.6992 123.6992 0 0 1 5.17888-8.59136c1.4208-2.09408 2.58304-4.57472 2.58304-5.5168 0-0.93952 1.6128-4.98176 3.58144-8.98048s4.3136-10.1248 5.20704-13.61664a149.20192 149.20192 0 0 1 4.03712-12.75392c4.71552-12.52864 4.68992-11.53536 5.64736-204.31616 0.50432-101.0944 1.04448-202.7008 1.20576-225.792l0.28928-41.984 2.91584-5.84704c6.69952-13.42976 13.68832-16.15616 41.536-16.20224 29.76-0.04864 27.73248 3.62496 27.7376-50.24512l0.00256-40.5504-2.9568-4.08064-2.9568-4.0832-32.5376-0.6784c-76.0448-1.58976-241.67424-0.832-243.3792 1.11104" fill="#F8FAFB" ></path><path d="M196.608 2.048c0 1.94816-0.68352 2.048-13.89056 2.048-15.35488 0-17.29536 0.54272-18.20416 5.08416L163.8912 12.288H148.48V20.48h-16.384v6.9376l-7.22688 0.40704c-7.54176 0.42752-9.11104 1.37984-9.14176 5.55008-0.0128 1.8688-0.90112 2.26048-6.92736 3.072-6.59456 0.88576-6.95552 1.08032-7.87712 4.20608-0.82432 2.79808-1.50272 3.32288-4.608 3.584-3.39456 0.28672-3.66336 0.56576-3.95776 4.08832-0.2944 3.55328-0.54272 3.8016-4.096 4.096-3.55328 0.29696-3.79904 0.54272-4.096 4.096-0.2944 3.55328-0.54272 3.8016-4.096 4.096-3.44064 0.28672-3.80928 0.6144-4.10112 3.64288-0.26368 2.7264-0.76032 3.328-2.74432 3.328a5.6576 5.6576 0 0 0-5.63968 5.63968c0 1.984-0.6016 2.48064-3.328 2.74432-3.02848 0.29184-3.35616 0.66048-3.64288 4.10112-0.2944 3.55328-0.54272 3.8016-4.096 4.096-3.55328 0.29696-3.79904 0.54272-4.096 4.096-0.2944 3.55328-0.54272 3.8016-4.096 4.096-3.52256 0.2944-3.8016 0.5632-4.08832 3.95776-0.26112 3.10528-0.78592 3.78368-3.584 4.608-3.12576 0.9216-3.32032 1.28256-4.20608 7.87712-0.81152 6.02624-1.2032 6.91456-3.072 6.92736-3.72224 0.02816-4.9664 1.65632-5.49888 7.18848C27.34848 128.40192 24.99072 132.096 22.016 132.096c-1.1776 0-1.536 1.30304-1.536 5.58592C20.48 143.4624 17.38496 148.48 13.82144 148.48c-1.09568 0-1.52064 1.92512-1.74592 7.936-0.28672 7.60064-0.40704 7.9488-2.85952 8.26368-4.42624 0.56832-5.12 2.69824-5.12 15.73888 0 11.40992-0.1152 12.09344-2.048 12.09344-2.04288 0-2.048 0.68352-2.048 319.488s0.00512 319.488 2.048 319.488c1.9328 0 2.048 0.68352 2.048 12.09344 0 13.04064 0.69376 15.17056 5.12 15.73888 2.45248 0.31488 2.5728 0.66304 2.85952 8.26368 0.22528 6.01088 0.65024 7.936 1.74592 7.936C17.38496 875.52 20.48 880.5376 20.48 886.31808c0 4.28288 0.3584 5.58592 1.536 5.58592 2.92096 0 5.2992 3.6864 5.95456 9.23392 0.67072 5.66272 1.7664 7.10656 5.40416 7.13472 1.8688 0.0128 2.26048 0.90112 3.072 6.92736 0.88576 6.59456 1.08032 6.95552 4.20608 7.87712 2.79808 0.82432 3.32288 1.50272 3.584 4.608 0.28672 3.39456 0.56576 3.66336 4.08832 3.95776 3.55328 0.2944 3.8016 0.54272 4.096 4.096 0.29696 3.55328 0.54272 3.79904 4.096 4.096 3.55328 0.2944 3.8016 0.54272 4.096 4.096 0.28672 3.44064 0.6144 3.80928 3.64288 4.10112 2.53952 0.24576 3.328 0.8064 3.328 2.37312 0 3.70688 1.95072 6.01088 5.09184 6.01088 2.60352 0 3.01056 0.41216 3.29216 3.328 0.29184 3.02848 0.66048 3.35616 4.10112 3.64288 3.55328 0.2944 3.8016 0.54272 4.096 4.096 0.29696 3.55328 0.54272 3.79904 4.096 4.096 3.55328 0.2944 3.8016 0.54272 4.096 4.096 0.2944 3.52256 0.5632 3.8016 3.95776 4.08832 3.10528 0.26112 3.78368 0.78592 4.608 3.584 0.9216 3.12576 1.28256 3.32032 7.87712 4.20608 6.23104 0.83712 6.912 1.15968 6.912 3.25376 0 4.07808 2.59584 5.54496 9.80224 5.54496H132.096v7.168H148.48v8.192h15.4112l0.62208 3.10784c0.9088 4.54144 2.84928 5.08416 18.20416 5.08416 13.20704 0 13.89056 0.09984 13.89056 2.048 0 2.04288 0.68352 2.048 315.904 2.048 315.22048 0 315.904-0.00512 315.904-2.048 0-1.94304 0.68352-2.048 13.37856-2.048 14.83264 0 16.78848-0.56064 17.69216-5.08416l0.62208-3.10784H875.52V1003.52h16.384v-7.168h6.58176c7.21408 0 9.80224-1.46688 9.80224-5.56032 0-2.11712 0.60928-2.40384 6.4-3.00032 6.55616-0.6784 8.96-2.26304 8.96-5.90848 0-1.3312 0.98816-1.87392 3.84-2.10944 3.61984-0.29952 3.85792-0.53504 4.15488-4.09856 0.2944-3.55328 0.54272-3.8016 4.096-4.096 3.55328-0.29696 3.79904-0.54272 4.096-4.096 0.2944-3.55328 0.54272-3.8016 4.096-4.096 3.42528-0.28416 3.81184-0.62208 4.09856-3.584 0.28672-2.96192 0.67328-3.29984 4.096-3.584 3.54816-0.29696 3.79648-0.54528 4.09344-4.09344 0.28416-3.42272 0.62208-3.80928 3.584-4.096 2.96192-0.28672 3.29984-0.67328 3.584-4.09856 0.2944-3.55328 0.54272-3.8016 4.096-4.096 3.55328-0.29696 3.79904-0.54272 4.096-4.096 0.2944-3.55328 0.54272-3.8016 4.096-4.096 3.56352-0.29696 3.79904-0.53504 4.09856-4.15488 0.23552-2.85184 0.77824-3.84 2.10944-3.84 3.64544 0 5.23008-2.40384 5.90848-8.96 0.59648-5.79072 0.8832-6.4 3.00032-6.4 3.64288 0 5.56032-2.67264 5.56032-7.75424 0-4.736 2.54208-8.62976 5.632-8.62976 1.1776 0 1.536-1.30304 1.536-5.58592 0-5.78048 3.09504-10.79808 6.65856-10.79808 1.09568 0 1.52064-1.92512 1.74592-7.936 0.28672-7.60064 0.40704-7.9488 2.85952-8.26368 4.42624-0.56832 5.12-2.69824 5.12-15.73888 0-11.40992 0.1152-12.09344 2.048-12.09344 2.04288 0 2.048-0.68352 2.048-319.488s-0.00512-319.488-2.048-319.488c-1.9328 0-2.048-0.68352-2.048-12.09344 0-13.04064-0.69376-15.17056-5.12-15.73888-2.45248-0.31488-2.5728-0.66304-2.85952-8.26368-0.22528-6.01088-0.65024-7.936-1.74592-7.936-3.56352 0-6.65856-5.0176-6.65856-10.79808 0-4.28288-0.3584-5.58592-1.536-5.58592-3.08992 0-5.632-3.89376-5.632-8.62976 0-5.07392-1.91488-7.75424-5.54496-7.75424-2.09408 0-2.41664-0.68096-3.25376-6.912-0.88576-6.59456-1.08032-6.95552-4.20608-7.87712-2.79808-0.82432-3.32288-1.50272-3.584-4.608-0.28672-3.39456-0.56576-3.66336-4.08832-3.95776-3.55328-0.2944-3.8016-0.54272-4.096-4.096-0.29696-3.55328-0.54272-3.79904-4.096-4.096-3.55328-0.2944-3.8016-0.54272-4.096-4.096-0.28672-3.44064-0.6144-3.80928-3.64288-4.10112-2.91584-0.2816-3.328-0.68864-3.328-3.29216 0-3.14112-2.304-5.09184-6.01088-5.09184-1.56672 0-2.12736-0.78848-2.37312-3.328-0.29184-3.02848-0.66048-3.35616-4.10112-3.64288-3.55328-0.2944-3.8016-0.54272-4.096-4.096-0.29696-3.55328-0.54272-3.79904-4.096-4.096-3.55328-0.2944-3.8016-0.54272-4.096-4.096-0.2944-3.52256-0.5632-3.8016-3.95776-4.08832-3.10528-0.26112-3.78368-0.78592-4.608-3.584-0.9216-3.12576-1.28256-3.32032-7.87712-4.20608-6.02624-0.81152-6.91456-1.2032-6.92736-3.072-0.03072-4.17024-1.6-5.12256-9.14176-5.55008l-7.22688-0.40704V20.48H875.52V12.288h-7.56992c-7.87968 0-8.81408-0.5376-8.81408-5.08416 0-2.59584-2.74688-3.07712-17.664-3.09504-12.36992-0.0128-13.056-0.12032-13.056-2.0608 0-2.04288-0.68352-2.048-315.904-2.048C197.29152 0 196.608 0.00512 196.608 2.048m560.5888 136.87296l32.5376 0.6784 2.9568 4.0832 2.9568 4.08064-0.00256 40.5504c-0.00512 53.87008 2.0224 50.19648-27.7376 50.24512-27.84768 0.04608-34.83648 2.77248-41.536 16.20224L723.456 260.608l-0.28928 41.984c-0.16128 23.0912-0.70144 124.6976-1.20576 225.792-0.95744 192.7808-0.93184 191.78752-5.64736 204.31616a149.20192 149.20192 0 0 0-4.03712 12.75392c-0.89344 3.49184-3.2384 9.61792-5.20704 13.61664-1.96864 3.99872-3.58144 8.04096-3.58144 8.98048 0 0.94208-1.16224 3.42272-2.58304 5.5168a123.6992 123.6992 0 0 0-5.17888 8.59136c-2.82112 5.1968-8.62976 13.99296-11.06176 16.74752-0.83456 0.94976-2.33216 3.02336-3.32288 4.608-4.61056 7.3728-29.39904 31.50592-37.75744 36.75904-1.6896 1.0624-3.65568 2.71872-4.36736 3.68128-1.18016 1.58976-5.38112 4.09088-20.20864 12.02176-2.816 1.50528-6.27968 3.5072-7.69792 4.44672-1.41568 0.93696-4.64128 2.42432-7.168 3.3024-2.52416 0.87808-7.12448 2.9056-10.22208 4.50816-3.0976 1.60256-8.8576 3.68896-12.8 4.63872-3.9424 0.9472-9.63072 2.63168-12.63872 3.74272-24.50944 9.04448-93.056 9.51808-123.55328 0.84992a405.1456 405.1456 0 0 0-16.38144-4.14976c-5.06624-1.16224-11.28704-3.15136-13.824-4.42112-2.5344-1.2672-7.83616-3.36128-11.77856-4.65152s-8.77312-3.42784-10.73408-4.75136c-1.96096-1.32096-4.032-2.40384-4.60288-2.40384-2.06592 0-24.19968-11.46624-26.81856-13.89312-1.47712-1.3696-3.2768-2.49088-3.99616-2.49088-0.71936 0-3.3024-1.52064-5.73696-3.37664-2.43712-1.85856-6.38976-4.47744-8.78336-5.82144-2.3936-1.344-4.352-2.78016-4.352-3.19488 0-0.41472-1.728-1.78688-3.84-3.05152s-5.06112-3.49184-6.55104-4.94848c-1.49248-1.45664-4.9408-4.49024-7.6672-6.74304-16.52736-13.66016-38.94016-38.93248-50.40384-56.832-2.70336-4.224-6.83776-10.63168-9.18784-14.24128-2.34752-3.60704-4.27008-7.31904-4.27008-8.24576 0-0.92928-1.44384-3.96544-3.21024-6.75072-1.7664-2.78528-4.07552-7.69792-5.1328-10.91584-1.05728-3.21792-2.87232-7.4624-4.03712-9.4336-1.16224-1.97376-3.21536-8.02816-4.55936-13.45792-1.344-5.43232-2.86464-10.40128-3.3792-11.0464-6.65088-8.35072-7.82848-79.0144-1.7152-103.06048 3.11552-12.25984 7.57504-25.67168 9.64864-29.02784 1.20064-1.94304 3.08224-6.14656 4.18304-9.34144 1.09824-3.19232 3.16416-7.1936 4.58752-8.88576 1.42592-1.69216 2.88256-4.31104 3.2384-5.81632 0.64768-2.73408 3.42016-6.94784 11.12832-16.90368 2.2528-2.90816 5.4656-7.13216 7.1424-9.38496 5.76256-7.75168 26.66496-27.24096 35.8656-33.44128 5.0688-3.4176 9.37472-6.63808 9.56928-7.15776 0.31232-0.832 5.056-3.54048 17.56672-10.03264 9.1136-4.73088 19.30752-9.25184 23.04-10.21952 2.2528-0.58368 5.89312-2.03008 8.09216-3.21536 3.57888-1.92768 17.41824-5.40672 39.52384-9.93792 27.39712-5.61152 89.05472-0.76544 99.328 7.81056l3.584 2.99264 0.32 62.83264c0.3584 70.46912 0.40704 69.888-5.80608 70.88384-3.82976 0.61184-6.90176-0.27904-27.91424-8.10496-21.21472-7.90016-61.94944-2.65984-79.05792 10.16832-6.784 5.08672-13.30432 11.59168-15.0528 15.01696-1.03168 2.0224-2.5728 4.25728-3.42784 4.9664-3.90656 3.24352-9.32352 23.58784-9.29536 34.92608 0.0512 21.1456 11.7632 50.7008 27.32544 68.9408 6.22848 7.30368 20.73088 21.39136 24.50176 23.80544 2.06592 1.32096 4.0576 2.8928 4.4288 3.49184 0.36864 0.59904 3.36896 2.44992 6.66624 4.11392s6.25664 3.45088 6.5792 3.97056c1.3824 2.23488 22.32576 11.29984 30.7968 13.32992 5.0688 1.216 11.7504 2.89536 14.848 3.73504 7.5264 2.04032 32.36864 2.11968 39.424 0.12544 16.40448-4.63616 22.12864-6.73792 25.05216-9.19808 1.79712-1.5104 3.65568-2.74688 4.13184-2.74688 1.07264 0 13.94688-12.95104 17.32608-17.42848 2.84672-3.77344 8.86528-21.2864 10.26304-29.8624 0.57088-3.51232 0.72448-89.37984 0.38912-216.576l-0.55808-210.75712-3.39968-7.19616c-6.81984-14.44096-12.87168-16.85248-42.4192-16.90112-27.95776-0.04608-25.4848 4.59776-26.18112-49.18784-0.5632-43.58912-0.55552-43.67104 4.41344-49.33888 1.70496-1.94304 167.3344-2.7008 243.3792-1.11104" fill="#0C69CC" ></path></symbol><symbol id="qq_docs" viewBox="0 0 1181 1024"><path d="M944.643052 0H194.788816a21.266874 21.266874 0 0 0-20.95181 17.564863L0.315065 998.952348a21.266874 21.266874 0 0 0 20.991192 24.929503h413.365025l32.76674-10.121457h191.953232l26.819892 10.121457h318.412368a21.306257 21.306257 0 0 0 20.991193-17.564863l142.724357-807.196031L944.643052 0z" fill="#0188FB" ></path><path d="M934.83666 199.120957h233.502404L944.643052 0l-30.797585 174.152071a21.266874 21.266874 0 0 0 20.991193 24.968886" fill="#00DCFF" ></path><path d="M608.429522 42.021768L566.447137 280.013846l-409.032884 0.708895 177.735934 191.795701h197.230568l-97.67009 551.363409h251.500481l97.827622-551.363409h296.987962l10.318372-0.236299z" fill="#FFFFFF" ></path></symbol><symbol id="wechat" viewBox="0 0 1024 1024"><path d="M332.820602 413.878041c-12.798544 4.266181-29.86327-4.266181-34.129451-21.330907-4.266181-17.064726 4.266181-34.129451 21.330906-34.129451h12.798545c17.064726 0 29.86327 12.798544 29.863269 29.86327 0 12.798544-12.798544 25.597088-29.863269 25.597088z m174.913437 0c-17.064726 4.266181-34.129451-4.266181-38.395633-21.330907-4.266181-17.064726 4.266181-34.129451 21.330907-38.395632h12.798544c17.064726 0 29.86327 12.798544 29.86327 29.86327 4.266181 17.064726-8.532363 29.86327-25.597088 29.863269z m81.057446 162.114893c-12.798544 0-25.597088-8.532363-25.597088-21.330907s8.532363-25.597088 21.330907-25.597088c12.798544-4.266181 25.597088 4.266181 29.863269 17.064725 4.266181 12.798544-4.266181 25.597088-17.064725 29.86327h-8.532363z m136.517804 0c-12.798544 0-25.597088-8.532363-25.597088-21.330907s8.532363-25.597088 21.330907-25.597088c12.798544-4.266181 25.597088 4.266181 29.86327 17.064725 4.266181 12.798544-4.266181 25.597088-17.064726 29.86327H725.309289z m-68.258902-140.783986c-119.453079 0-209.042888 76.791265-209.042888 170.647256s93.855991 170.647256 209.042888 170.647255c25.597088 0 51.194177-4.266181 72.525084-12.798544l68.258902 34.129451-17.064726-59.726539c46.927995-29.86327 81.057446-81.057446 85.323628-136.517805 0-89.589809-98.122172-166.381074-209.042888-166.381074zM413.878048 256.02933c-136.517804 0-247.438521 89.589809-247.438521 200.510525 4.266181 68.258902 38.395632 127.985442 98.122172 157.848712l-25.597088 72.525083 85.323628-42.661814c29.86327 8.532363 55.460358 12.798544 85.323628 12.798545h21.330907c-4.266181-17.064726-8.532363-34.129451-8.532363-51.194177 4.266181-110.920716 98.122172-191.978162 209.042888-187.711981h25.597088C644.251843 328.554414 537.597308 256.02933 413.878048 256.02933z m102.388353 767.91265C234.69843 1028.208161 4.324635 797.834366 0.058453 516.266395-4.207728 234.698423 226.166067 4.324628 507.734039 0.058447s511.941767 226.107614 516.207948 507.675585v4.266181c0 281.567972-226.107614 511.941767-507.675586 511.941767z" fill="#67CC79" ></path></symbol><symbol id="ocr" viewBox="0 0 1024 1024"><path d="M960.553 96.837v259.378h43.211V96.837c0-47.769-38.698-86.468-86.468-86.468H679.521v43.209h237.821c23.874 0.025 43.211 19.386 43.211 43.259z m-907.908 0c0-11.485 4.536-22.482 12.656-30.579a43.152 43.152 0 0 1 30.553-12.68h237.969V10.369H95.854c-47.745 0-86.467 38.699-86.467 86.468v259.378h43.258V96.837z m0 821.442V658.852H9.583V918.28c0 47.745 38.722 86.467 86.468 86.467l237.773 0.463v-43.722H95.854c-23.873-0.001-43.209-19.337-43.209-43.209z m951.166 0V658.852h-43.258V918.28c0 23.799-19.264 43.136-43.062 43.208H679.717v44.429l240.237 0.463a85.83 85.83 0 0 0 60.229-26.627 85.866 85.866 0 0 0 23.628-61.474z m-270.278-632.17H259.109v49.939h224.728v474.426h49.94V336.048h199.757v-49.939z" ></path></symbol><symbol id="pangu" viewBox="0 0 1024 1024"><path d="M64 635.2V388.8h82v165.8h731.9V388.8H960v246.4H64z m0 0" ></path></symbol><symbol id="kindle" viewBox="0 0 1024 1024"><path d="M123.6385189 924.63407445V99.36592555c0-53.39970333 43.69066667-97.09036999 97.09036999-97.09037h582.54222222c53.39970333 0 97.09036999 43.69066667 97.09036999 97.09037v825.2681489c0 53.39970333-43.69066667 97.09036999-97.09036999 97.09037H220.72888889c-53.39970333 0-97.09036999-43.69066667-97.09036999-97.09037z" fill="#37474F" ></path><path d="M778.9985189 75.09333333H245.0014811c-14.56355555 0-24.27259221 9.70903666-24.27259221 24.27259222v703.90518556c0 14.56355555 9.70903666 24.27259221 24.27259221 24.27259222h533.9970378c14.56355555 0 24.27259221-9.70903666 24.27259221-24.27259222V99.36592555c0-14.56355555-9.70903666-24.27259221-24.27259221-24.27259222z" fill="#EEEEEE" ></path><path d="M426.666667 853.333333h170.666666v42.666667h-170.666666z" fill="#546E7A" ></path><path d="M341.333333 234.666667h341.333334v64H341.333333zM341.333333 384h341.333334v42.666667H341.333333zM341.333333 469.333333h256v42.666667H341.333333zM341.333333 554.666667h341.333334v42.666666H341.333333zM341.333333 640h256v42.666667H341.333333z" fill="#A1A1A1" ></path></symbol><symbol id="auto" viewBox="0 0 1024 1024"><path d="M213.333333 341.333333V230.4A102.4 102.4 0 0 1 315.733333 128h392.533334A102.4 102.4 0 0 1 810.666667 230.4V341.333333h85.333333v85.333334h-85.333333v68.266666a102.4 102.4 0 0 1-102.4 102.4H315.733333A102.4 102.4 0 0 1 213.333333 494.933333V426.666667H128V341.333333h85.333333z m102.4-128a17.066667 17.066667 0 0 0-17.066666 17.066667v264.533333c0 9.386667 7.68 17.066667 17.066666 17.066667h392.533334a17.066667 17.066667 0 0 0 17.066666-17.066667V230.4a17.066667 17.066667 0 0 0-17.066666-17.066667H315.733333z m89.6 256a64 64 0 1 1 0-128 64 64 0 0 1 0 128z m213.333334 0a64 64 0 1 1 0-128 64 64 0 0 1 0 128z m-334.464 298.666667L213.333333 830.037333V938.666667H128v-147.370667L252.16 682.666667h519.68L896 791.296V938.666667h-85.333333v-108.629334L739.797333 768H284.16z" ></path></symbol><symbol id="clipper" viewBox="0 0 1024 1024"><path d="M334.4 107.3L317.3 115.8c-7 3.6-14.8 9.7-20 18.7-5.4 9.1-8.1 20.3-8.1 33.4 0.1 18.9 6.3 42.6 20.8 71.6l118.4 251.6 60.4-107.5L334.4 107.3z m319.6 768l0.2 0.3c8.9 16.8 18.3 29.9 31.2 39.1 12.9 9.2 28.4 12.7 43.5 12.6 28.3 0 58.2-7.4 80.7-29.7 22.5-22.3 34.9-56.9 34.8-104.4 0.1-0.5 0.1-1.1 0.2-1.6-0.1-47.6-17.3-82.8-44.8-103.7-27.5-21.1-61.6-28.3-95.2-31.5-23.9-2.1-41.4-3.4-53.5-7.6-11.9-4.4-20.1-9.8-30.9-26.3l-27.8-49.9-47.9 85.1 109.5 217.6z m110.2-127.1c23.4 43.9 7 98.4-37 121.8l-84.9-158.6c43.8-23.6 98.4-7 121.9 36.8zM410.4 622.5c-10.6 16.3-18.8 21.9-30.7 26.2-12.1 4.2-29.7 5.4-53.6 7.5-33.5 3.3-67.8 10.4-95.3 31.4-27.8 21.1-45 56.8-44.6 105.2-0.2 47.5 12.1 82.1 34.6 104.6 22.5 22.2 52.4 29.8 80.6 29.7 15.4 0.1 30.8-3.5 43.7-12.6 12.8-9.2 22.3-22.3 31.2-38.9v-0.4L495 640.4l7-14c3.5-7.2 12.2-10 19.3-6.5 1 0.6 1.7 1.2 2.6 1.9l0.4 0.2 126-224.8 73.2-157.5c14.5-29.2 20.7-52.7 20.7-71.8 0.1-13-2.6-24.2-8-33.3-5.3-9-12.9-15.2-20.1-18.7l-17.1-8.7-246.4 440-42.2 75.3z m-19.6 87.7l-86.2 158c-43.7-23.8-59.8-78.5-35.9-122 23.9-43.6 78.6-59.7 122.1-36z" ></path></symbol><symbol id="OneNote" viewBox="0 0 1084 1024"><path d="M1000.75096677 598.6589525c9.54840938 60.17244563 9.66485344 121.88777437 3.28954312 182.23488656-4.54131656 32.60432437-43.43361844 30.33366656-67.974195 34.90409344-3.60976406 17.11727062-3.26043281 35.89386844-12.51773156 51.11892375-18.95126344 11.84817844-42.44384438 9.05352187-63.57843375 9.95596313-77.20238344-0.72777469-154.40476688-0.37844344-231.49070531-0.37844251-0.11644406 27.975675-0.11644406 56.03868281-0.11644406 84.01435782H562.48479739C402.31605177 931.28128719 241.71064208 904.47005188 81.33811958 876.58170969 81.22167552 631.75816437 81.22167552 387.02195187 81.22167552 142.19840562 242.64219364 114.13539781 404.12093427 86.71283187 565.39589802 58.0384925h62.96710218c0 28.00478625 0 56.03868281 0.11644407 84.04346906 98.56986 3.78443062 198.27505031-7.94730375 296.11713468 6.75375281 3.49332094 11.0912925 7.1613075 22.15347469 10.85840532 33.15743438 24.8607975 4.39576125 64.83020625 1.309995 68.73108093 34.90409344 8.00552625 61.42421906 2.41621313 123.51799125-1.60110562 185.02954312 5.70575719 65.35420406 9.60663094 131.81462719-1.83399281 196.73216719z m-530.98471781-268.63634719c-21.13458938 0.96066281-42.15273375 2.12510344-63.14176782 3.26043188-0.436665 77.66815969 0.40755375 155.42365125-0.436665 233.09181093-39.99852-75.89238844-81.80192156-150.70766906-122.99399343-225.84317062l-65.09220563 3.46420969c0.23288813 106.63360875 0.34933219 213.38366156 0 319.98815906 19.09681875 1.13532937 38.04808219 2.29976906 57.23223469 3.5515425-0.23288813-77.66815969 1.13532937-155.42365125-1.16444063-233.09181188 40.55162906 81.04503562 85.87746281 159.52830281 127.82641969 239.78734219 22.50280594 1.25177344 45.09294562 2.53265719 67.77041813 3.49332094V330.02260531zM933.65010177 782.87338719l41.36673656-2.56176844c1.48466156-54.84513187 1.54288313-109.74848625 0-164.65183969-13.85683781-0.66955312-27.68456531-1.48466156-41.36673656-2.06688187-0.11644406 56.4462375-0.11644406 112.77603 0 169.28049z m41.36673656-566.20903219a1960.10213813 1960.10213813 0 0 0-72.71928844-1.54288406c-0.11644406-13.97328187-0.11644406-27.83011969 0-41.80340157h-273.93454968v73.67995125c66.69331031 0.11644406 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.83011969 0 41.74518094-66.75153281 0-133.38662156-0.05822156-200.07993187 0v52.72002844c66.69331031 0.11644406 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.83011969 0 41.80340156-66.75153281 0-133.38662156-0.11644406-200.07993187 0v52.66180688c66.69331031 0.11644406 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.80100937-0.11644406 41.77429125-66.63508875 0-133.2701775-0.11644406-199.96348781 0v52.66180687c66.69331031 0.11644406 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.83011969 0 41.80340156-66.75153281 0-133.38662156-0.11644406-200.07993187 0v52.66180688c66.69331031 0.11644406 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.83011969 0 41.77429125-66.75153281 0-133.38662156-0.08733281-200.07993187 0v52.69091719c66.69331031 0.05822156 133.32839906 0 200.07993187 0-0.11644406 13.97328187-0.11644406 27.80100937 0 41.71606968-66.75153281 0-133.38662156-0.05822156-200.07993187 0v84.21813469c91.32122062 0 182.61333 0.05822156 273.93454968 0 0.05822156-153.88076813 0-307.70331469 0-461.58408281a2226.70071469 2226.70071469 0 0 0 72.31173469-1.3682175c2.35799156-55.19446406 1.77577125-110.35981688 0.40755375-165.6125025z m-0.17466562 199.43949l-41.19207094-1.83399375c-0.11644406 56.30068219-0.11644406 112.63047563 0 168.98937937 13.56572812-0.4948875 27.18967781-1.13532937 40.84273875-1.77577124 2.47443562-55.10713125 2.06688094-110.18515125 0.34933219-165.37961438z" fill="#733781" ></path></symbol><symbol id="youdao" viewBox="0 0 1024 1024"><path d="M552.96 545.28h-115.2c-23.04 0-23.04 0-23.04-23.04v-40.96H563.2v-161.28c0-12.8-7.68-12.8-15.36-12.8h-99.84c-17.92 0-23.04-2.56-23.04-23.04V243.2h140.8c-2.56-12.8-2.56-25.6-2.56-40.96h53.76c2.56 0 7.68 10.24 7.68 15.36 2.56 28.16 0 28.16 30.72 28.16H691.2c56.32 0 89.6 30.72 89.6 89.6v143.36c23.04-2.56 23.04 10.24 23.04 25.6-2.56 46.08 10.24 40.96-43.52 40.96h-89.6c7.68 25.6 15.36 46.08 23.04 66.56 17.92 43.52 51.2 71.68 97.28 87.04 10.24 2.56 12.8 7.68 12.8 17.92v46.08c-51.2-5.12-92.16-25.6-128-58.88-35.84-35.84-51.2-79.36-64-128-30.72 104.96-92.16 171.52-207.36 186.88v-58.88c2.56-2.56 0 0 17.92-7.68 61.44-20.48 99.84-64 120.32-122.88 7.68-5.12 7.68-15.36 10.24-28.16z m74.24-235.52v163.84c0 2.56 7.68 7.68 12.8 7.68h61.44c12.8 0 17.92-5.12 17.92-17.92v-117.76c0-23.04-12.8-35.84-35.84-35.84h-56.32zM291.84 204.8H353.28v181.76h53.76v64h-15.36c-38.4 2.56-38.4 2.56-38.4 40.96v256c0 23.04 0 23.04-23.04 23.04h-35.84c-2.56-192-2.56-378.88-2.56-565.76zM248.32 755.2H192V212.48h56.32v542.72z" fill="#FFFFFF" ></path><path d="M998.4 711.68V25.6H296.96c-102.4 7.68-202.24 89.6-250.88 212.48-10.24 28.16-17.92 58.88-20.48 87.04v371.2c0 58.88 23.04 120.32 64 176.64 20.48 25.6 43.52 48.64 69.12 66.56 35.84 25.6 76.8 40.96 115.2 48.64h478.72c69.12-10.24 140.8-51.2 192-122.88 28.16-48.64 48.64-102.4 53.76-153.6z" fill="#3BA1FF" ></path><path d="M637.44 250.88l53.76 53.76s-23.04 25.6-53.76 53.76L299.52 691.2l-53.76 53.76s2.56-35.84 5.12-76.8l7.68-143.36c2.56-40.96 28.16-99.84 58.88-130.56l181.76-176.64c30.72-28.16 79.36-28.16 110.08 0l28.16 33.28z" fill="#FFFFFF" ></path><path d="M775.68 389.12c30.72 30.72 30.72 79.36 0 107.52l-181.76 181.76c-30.72 30.72-89.6 56.32-130.56 56.32l-143.36 7.68c-40.96 2.56-76.8 2.56-76.8 2.56s25.6-25.6 56.32-53.76l337.92-332.8c30.72-30.72 53.76-53.76 56.32-53.76l53.76 53.76 28.16 30.72z" fill="#EBF6FA" ></path></symbol><symbol id="coda" viewBox="0 0 1024 1024"><path d="M181.81808602 932.04571968c0 48.59204229 30.29378395 81.52890833 72.17646611 81.52890834 37.00314539 0 60.3842542-21.14465413 64.85716095-52.45500792h-34.97000572c-2.8463958 14.02866529-14.231979 21.75459655-29.68384154 21.75459655-23.38110881 0-37.6130878-21.55128285-37.61308779-51.03181067 0-29.48052783 14.231979-51.03181068 37.61308779-51.03181067 15.24854883 0 26.83744574 7.72593127 29.68384154 21.75459655H318.85171308c-4.47290806-31.31035378-27.85401556-52.45500793-64.85716095-52.45500792-41.88268085 0.20331371-72.17646482 33.34349346-72.17646611 81.93553574zM486.38243781 932.04571968c0-47.77878617-30.09047025-81.52890833-73.59966337-81.52890833S339.18311109 884.06361851 339.18311109 932.04571968c0 47.77878617 30.09047025 81.52890833 73.59966335 81.52890834 43.71250683 0 73.59966337-33.75012217 73.59966337-81.52890834z m-34.76669201 0c0 29.48052783-13.62203659 51.43843809-38.62965764 51.4384381s-38.62965764-21.75459655-38.62965634-51.4384381c0-29.48052783 13.62203659-51.43843809 38.62965634-51.43843809s38.62965764 21.75459655 38.62965764 51.43843809zM620.56966908 1010.11829111h34.97000571V781.39005605h-34.97000571V867.18855872c-6.91267515-8.7425011-22.36453899-16.87506109-39.44291377-16.87506107-45.33901907 0-71.7698374 37.00314539-71.7698374 81.52890833 0 44.93239037 26.43081831 81.52890833 71.7698374 81.52890835 17.07837479 0 32.53023733-8.13255998 39.44291377-16.8750611v13.62203788z m0-48.5920436c-5.89610531 12.40215302-19.11151446 20.73802672-33.54680846 20.73802673-26.83744574 0-42.89925069-21.55128285-42.8992507-50.21855456 0-28.8705854 16.06180496-50.21855455 42.8992507-50.21855455 14.231979 0 27.65070187 8.53918739 33.54680846 20.73802673v58.96105565zM817.78423534 1010.11829111V907.85135605c0-35.57994814-22.9744801-57.33454469-62.01076645-57.3345447-31.71698121 0-56.11465985 20.1280843-60.79088163 46.76221632h33.54680847c3.86296563-10.97895578 12.60546674-16.87506109 26.43081832-16.87505978 19.9247706 0 29.68384154 12.40215302 29.68384153 28.05732929v12.60546803c-6.30273402-4.47290806-21.95791026-9.55575723-35.98657555-9.55575854-34.15674959 0-60.3842542 20.73802672-60.3842542 50.21855455 0 31.92029621 26.22750462 51.03181068 57.94448582 51.03181068 17.48500222 0 33.14017975-6.30273402 38.42634393-11.58889691v8.74250111l33.14017976 0.20331501z m-33.14017976-41.47605345c-3.86296563 9.75907094-17.68831722 15.85849124-31.71698249 15.85849125-15.65517755 0-32.12360992-6.70936144-32.12360862-22.56785269 0-15.45186254 16.46843237-22.16122398 32.12360862-22.16122526 14.02866529 0 27.85401556 5.89610531 31.71698249 15.85849124v13.01209546zM766.54911095-2.99530225H233.0532104c-31.92029621 0-58.14779953 26.02418961-58.14779953 57.94448582v580.25811689c0 31.92029621 26.22750462 57.94448582 58.14779953 57.9444858h533.49590055c31.92029621 0 58.14779953-26.02418961 58.14779954-57.9444858v-27.04076074c-1.01656983-34.76669201-2.03313968-107.34978423-2.03313968-140.28665029 0-18.29825834-13.62203659-33.75012217-31.10704008-33.75012216-19.31482817 0-31.92029621 11.58889691-41.67936716 21.34796913-29.07390041 26.02418961-72.78640723 30.90372638-110.60280873 24.19436366-17.48500222-3.86296563-33.95343588-8.7425011-47.57547246-17.48500222-41.67936716-24.19436494-68.9234416-69.53338272-68.9234416-117.9221126 0-48.38872859 27.24407445-92.91449154 68.9234416-117.92211131 14.63860641-8.7425011 31.10704009-13.62203659 47.57547246-17.48500351 36.79983168-6.70936144 81.52890833-2.03313968 110.60280873 24.19436494 10.57232707 9.75907094 23.17779381 21.34796914 41.67936716 21.34796785 17.48500222 0 31.10704009-15.45186254 31.10704008-33.75012087 0-31.92029621 1.01656983-105.31664457 2.03313968-140.28665029v-25.21093478c0-32.12360992-26.22750462-58.14779953-58.14779954-58.14779952z" fill="#F46A54" ></path></symbol><symbol id="smms" viewBox="0 0 1024 1024"><path d="M794.819048 136.533333h19.504762a48.761905 48.761905 0 0 1 48.761904 48.761905v604.647619a48.761905 48.761905 0 0 1-48.761904 48.761905h-19.504762a48.761905 48.761905 0 0 1-48.761905-48.761905V185.295238a48.761905 48.761905 0 0 1 48.761905-48.761905z m-195.047619 234.057143h19.504761a48.761905 48.761905 0 0 1 48.761905 48.761905v370.590476a48.761905 48.761905 0 0 1-48.761905 48.761905h-19.504761a48.761905 48.761905 0 0 1-48.761905-48.761905V419.352381a48.761905 48.761905 0 0 1 48.761905-48.761905z m-195.047619 117.028572h19.504761a48.761905 48.761905 0 0 1 48.761905 48.761904v253.561905a48.761905 48.761905 0 0 1-48.761905 48.761905h-19.504761a48.761905 48.761905 0 0 1-48.761905-48.761905V536.380952a48.761905 48.761905 0 0 1 48.761905-48.761904z m-195.04762 117.028571h19.504762a48.761905 48.761905 0 0 1 48.761905 48.761905v136.533333a48.761905 48.761905 0 0 1-48.761905 48.761905h-19.504762a48.761905 48.761905 0 0 1-48.761904-48.761905v-136.533333a48.761905 48.761905 0 0 1 48.761904-48.761905z" fill="#00C074" ></path></symbol><symbol id="imgur" viewBox="0 0 2927 1024"><path d="M102.789113 184.614534C45.176197 184.614534 0 144.046254 0 92.307267 0 41.573644 46.311885 0 102.789113 0c56.467919 0 101.653424 41.582953 101.653424 92.307267 0 51.738987-45.185506 92.307267-101.653424 92.307267m0 61.299249c59.856366 0 88.090326 37.542881 88.090326 115.654043v321.56739c0 78.111162-27.088962 116.668715-88.090326 116.668715-60.982746 0-89.226014-38.557553-89.226014-116.668715V361.558517c0-78.111162 28.243268-115.654043 89.226014-115.654043M1075.27354 799.81324c-60.973437 0-88.090326-38.557553-88.090325-116.668715V502.579371c0-81.155179-23.719132-121.714151-81.332049-121.71415-64.371193 0-89.216705 40.568281-89.216705 119.712732v182.575881c0 78.111162-28.233959 116.668715-89.226014 116.668715-61.010673 0-89.235323-38.557553-89.235323-116.668715V502.58868c0-81.155179-22.583444-121.714151-80.19636-121.71415-64.371193 0-89.235323 40.568281-89.235323 119.712732v182.57588c0 78.111162-28.243268 116.668715-89.216705 116.668716-61.001364 0-89.226014-38.557553-89.226014-116.668716V361.595753c0-78.111162 28.22465-115.654043 89.226014-115.654043 35.010854 0 63.235505 16.22545 84.692569 50.715005 37.282231-36.500282 82.467737-52.735041 142.333412-52.735041 76.798604 0 135.537899 25.366812 177.325649 75.067144 54.21516-53.768331 106.17756-75.067144 179.587716-75.067144 123.110489 0 201.035472 72.023127 201.035473 209.981346v229.260122c0 78.101853-28.22465 116.640788-89.226015 116.640789m705.942074 104.474028c-51.943783 78.111162-144.567553 119.703424-271.075798 119.703423-162.654788 0-260.929074-53.749714-260.929074-116.650097 0-40.577589 32.758095-70.003091 76.798603-70.003091 44.059126 0 97.157215 42.616243 190.898057 42.616243 86.973255 0 138.917038-47.670988 138.917038-134.914201 0-6.078726-1.12638-12.194687-1.12638-19.278777-38.380684 49.709642-91.488082 75.067144-161.509791 75.067144-140.062035 0-257.522009-115.644734-257.522008-277.936474 0-162.319667 125.372557-283.047072 276.735623-283.047071 60.973437 0 106.158943 18.264104 142.296176 56.803039 20.349303-33.474882 51.97171-50.715005 79.069981-50.715005 60.992055 0 89.226014 37.542881 89.226014 115.654043v321.56739c0.009309 96.365957-6.767586 169.394447-41.778441 221.133434m-245.085289-512.288577c-65.516191 0-116.352212 50.733623-116.352212 130.87413 0 82.160543 49.709642 133.89022 116.361521 133.89022 66.633261 0 117.441356-52.753659 117.441355-133.89022 0-80.149815-51.943783-130.87413-117.450664-130.87413m646.039163 416.946601c-172.792204 0-268.804422-81.173797-268.804422-234.333485V361.577135c0-78.111162 28.233959-115.654043 89.235323-115.654043 59.856366 0 88.090326 37.542881 88.090326 115.654043V542.151597c0 75.067144 19.222923 116.650097 91.488082 116.650098 72.293086 0 91.488082-41.582953 91.488082-116.650098V361.577135c0-78.111162 28.243268-115.654043 89.226014-115.654043 61.010673 0 88.118252 37.542881 88.118252 115.654043v213.025363c0 153.178306-96.012218 234.333485-268.841657 234.333485m594.132616-387.5211c-49.709642 21.298813-55.350848 40.577589-55.350849 95.360594v166.35043c0 78.120471-28.243268 116.678024-89.226014 116.678024-60.992055 0-89.226014-38.557553-89.226014-116.668715V361.558517c0-78.111162 28.233959-115.654043 89.226014-115.654043 35.001545 0 63.244814 16.22545 84.711187 50.715006 37.263613-35.485609 79.060672-53.759023 120.867039-53.759023 49.691024 0 90.361703 36.5189 90.361703 79.125834 0 63.915056-79.069981 68.979109-151.363066 99.419283" fill="#2c2c2c" ></path><path d="M102.37952 187.975055C44.999327 187.975055 0 147.565026 0 96.040145 0 45.520627 46.135016 4.095926 102.37952 4.095926c56.244505 0 101.243832 41.424701 101.243832 91.944219 0 51.524881-44.999327 91.93491-101.243832 91.93491" fill="#2c2c2c" ></path></symbol><symbol id="yuque" viewBox="0 0 1024 1024"><path d="M877.408 357.312c-10.592-43.104 10.592-111.712 78.688-136.096l-72.8-4s-27.488-98.4-153.888-107.2c-126.4-8.8-209.088-3.296-209.088-3.296s93.792 60.8 56.192 169.184c-27.392 57.504-70.496 104.608-116.704 158.304-1.408 1.504-2.688 2.816-3.808 4.096C303.488 613.6 64 892.416 64 892.416c263.712 70.496 440.384-6.784 544.896-99.616 22.016-0.192 38.496-0.288 49.6-0.288 145.6 0 268.704-128.608 263.712-271.616-3.488-98.4-34.208-120.608-44.8-163.616z" ></path></symbol><symbol id="privacy" viewBox="0 0 1024 1024"><path d="M322.285714 877.714286H121.325714c-23.277714 0-42.697143-15.177143-47.195428-35.364572A40.886857 40.886857 0 0 1 73.142857 833.444571V117.412571C73.142857 92.964571 94.701714 73.142857 121.307429 73.142857h561.938285c19.968 0 37.065143 11.154286 44.397715 27.044572 2.432 5.284571 3.785143 11.117714 3.785142 17.225142V365.714286h73.142858V86.619429C804.571429 38.765714 760.649143 0 706.450286 0H98.121143C43.922286 0 0 38.784 0 86.619429v777.636571C0 912.091429 43.922286 950.857143 98.121143 950.857143h224.164571v-73.142857z" ></path><path d="M274.285714 219.428571h365.714286v73.142858H274.285714zM274.285714 402.285714h219.428572v73.142857H274.285714zM219.428571 256a36.571429 36.571429 0 0 1-73.142857 0 36.571429 36.571429 0 0 1 73.142857 0zM214.857143 438.857143a36.571429 36.571429 0 0 1-73.142857 0 36.571429 36.571429 0 0 1 73.142857 0z" ></path><path d="M662.857143 843.428571m-54.857143 0a54.857143 54.857143 0 1 0 109.714286 0 54.857143 54.857143 0 1 0-109.714286 0Z" ></path><path d="M841.142857 658.285714h-18.395428c0.054857-0.731429 0.109714-1.426286 0.109714-2.176a164.571429 164.571429 0 1 0-329.142857 0c0 0.749714 0.073143 1.444571 0.109714 2.176H475.428571a54.857143 54.857143 0 0 0-54.857142 54.857143v256a54.857143 54.857143 0 0 0 54.857142 54.857143h365.714286a54.857143 54.857143 0 0 0 54.857143-54.857143V713.142857a54.857143 54.857143 0 0 0-54.857143-54.857143z m-274.285714-2.176c0-50.505143 40.923429-91.428571 91.428571-91.428571s91.428571 40.923429 91.428572 91.428571c0 0.749714-0.128 1.462857-0.219429 2.176h-182.436571c-0.073143-0.713143-0.201143-1.426286-0.201143-2.176zM822.857143 932.571429a18.285714 18.285714 0 0 1-18.285714 18.285714H512a18.285714 18.285714 0 0 1-18.285714-18.285714V749.714286a18.285714 18.285714 0 0 1 18.285714-18.285715h292.571429a18.285714 18.285714 0 0 1 18.285714 18.285715v182.857143z" ></path></symbol><symbol id="mail" viewBox="0 0 1024 1024"><path d="M948.90666667 147.91111111L75.09333333 147.91111111C35.04355555 147.91111111 2.27555555 180.67911111 2.27555555 220.72888889l0 582.54222222c0 40.04977778 32.768 72.81777778 72.81777778 72.81777778l873.81333334 0c40.04977778 0 72.81777778-32.768 72.81777778-72.81777778L1021.72444445 220.72888889C1021.72444445 180.67911111 988.95644445 147.91111111 948.90666667 147.91111111zM887.01155555 220.72888889L512 537.48622222 136.98844445 220.72888889 887.01155555 220.72888889zM75.09333333 803.27111111L75.09333333 264.41955555l436.90666667 371.37066667L948.90666667 264.41955555 948.90666667 803.27111111 75.09333333 803.27111111z" ></path></symbol><symbol id="115pan" viewBox="0 0 1024 1024"><path d="M701.004655 35.208896c29.184693-0.150426 57.568137-7.509021 84.900645-17.220197 20.924565 9.561773-3.704368 30.886451-7.559163 44.903699-9.81146 18.4717-18.371416 37.594223-29.084409 55.516408-10.562567 15.618723-29.785374 24.128537-48.4075 23.228027-75.139328-0.100284-150.228513 0-225.367841-0.050142-21.425985-0.851391-42.250266 12.565177-50.960649 32.03767-11.763928 24.329105-25.080212 47.957245-36.193317 72.586179 67.680449 13.216 137.012514 25.430182 198.886723 57.31845 58.670238 29.9358 109.880573 76.740802 139.615804 136.01172 37.294394 72.836889 39.797401 162.54298 5.106298 236.882082C696.149067 753.612827 625.013937 810.830993 545.56956 838.764182c-95.764064 32.989345-205.795063 33.939997-297.453622-12.214183-0.550539-0.751107-1.701758-2.252297-2.302439-3.053546 89.155553 11.914354 183.668113-3.604084 260.560364-51.611471 49.659003-30.836309 89.305979-80.195483 101.069907-138.214898 11.062963-44.803415 2.753717-93.411483-22.577205-131.957381-38.845727-60.021002-102.421694-100.519368-169.70203-121.895211-63.825654-21.926382-131.005706-30.036084-197.685361-37.594223 47.306422-93.110631 93.66117-186.721659 140.567479-280.032858 8.760524-17.020652 28.234041-27.432793 47.206138-26.78197C503.81969 35.208896 602.437755 35.609009 701.004655 35.208896z" ></path></symbol><symbol id="github" viewBox="0 0 1024 1024"><path d="M512 73.142857q119.428571 0 220.285714 58.857143T892 291.714286 950.857143 512q0 143.428571-83.714286 258T650.857143 928.571429q-15.428571 2.857143-22.857143-4t-7.428571-17.142858q0-1.714286 0.285714-43.714285t0.285714-76.857143q0-55.428571-29.714286-81.142857 32.571429-3.428571 58.571429-10.285715t53.714286-22.285714 46.285714-38 30.285714-60T792 489.142857q0-68-45.142857-117.714286 21.142857-52-4.571429-116.571428-16-5.142857-46.285714 6.285714t-52.571429 25.142857l-21.714285 13.714286q-53.142857-14.857143-109.714286-14.857143t-109.714286 14.857143q-9.142857-6.285714-24.285714-15.428571T330.285714 262.571429 281.714286 254.857143q-25.714286 64.571429-4.571429 116.571428-45.142857 49.714286-45.142857 117.714286 0 48.571429 11.714286 85.714286t30 60 46 38.285714 53.714285 22.285714 58.571429 10.285715q-22.285714 20.571429-28 58.857143-12 5.714286-25.714286 8.571428t-32.571428 2.857143-37.428572-12.285714T276.571429 728q-10.857143-18.285714-27.714286-29.714286t-28.285714-13.714285l-11.428572-1.714286q-12 0-16.571428 2.571428t-2.857143 6.571429 5.142857 8 7.428571 6.857143l4 2.857143q12.571429 5.714286 24.857143 21.714285t18 29.142858l5.714286 13.142857q7.428571 21.714286 25.142857 35.142857t38.285714 17.142857 39.714286 4 31.714286-2l13.142857-2.285714q0 21.714286 0.285714 50.571428t0.285714 31.142857q0 10.285714-7.428571 17.142858t-22.857143 4q-132.571429-44-216.285714-158.571429T73.142857 512q0-119.428571 58.857143-220.285714T291.714286 132 512 73.142857zM239.428571 703.428571q1.714286-4-4-6.857142-5.714286-1.714286-7.428571 1.142857-1.714286 4 4 6.857143 5.142857 3.428571 7.428571-1.142858z m17.714286 19.428572q4-2.857143-1.142857-9.142857-5.714286-5.142857-9.142857-1.714286-4 2.857143 1.142857 9.142857 5.714286 5.714286 9.142857 1.714286z m17.142857 25.714286q5.142857-4 0-10.857143-4.571429-7.428571-9.714285-3.428572-5.142857 2.857143 0 10.285715t9.714285 4z m24 24q4.571429-4.571429-2.285714-10.857143-6.857143-6.857143-11.428571-1.714286-5.142857 4.571429 2.285714 10.857143 6.857143 6.857143 11.428571 1.714286z m32.571429 14.285714q1.714286-6.285714-7.428572-9.142857-8.571429-2.285714-10.857142 4t7.428571 8.571428q8.571429 3.428571 10.857143-3.428571z m36 2.857143q0-7.428571-9.714286-6.285715-9.142857 0-9.142857 6.285715 0 7.428571 9.714286 6.285714 9.142857 0 9.142857-6.285714z m33.142857-5.714286q-1.142857-6.285714-10.285714-5.142857-9.142857 1.714286-8 8.571428t10.285714 4.571429 8-8z" ></path></symbol><symbol id="iconfont" viewBox="0 0 1025 1024"><path d="M1.703827 0h1022.296173v1022.296173H1.703827z" fill="#E94618" ></path><path d="M501.799188 865.690622c-38.777398-23.478735-30.512133-64.416586-55.495348-90.18356-45.955621-47.388539-126.495521-49.818196-173.427434-104.054416-157.356938-181.844339-6.25986-521.808932 291.356113-471.721531 173.442769 29.18826 305.577957 272.184652 187.299993 443.973005-50.872865 73.896679-148.708313 79.747621-208.112239 159.553171-17.406296 17.273398-8.592399 60.780619-41.621085 62.433331zM252.06416 470.276686c-6.982283 131.959694 180.363714 125.277285 180.363714 6.937983 0-60.245617-54.101617-108.603634-124.866663-83.245577-49.779008 17.835661-53.437125 37.388779-55.497051 76.307594z m381.53797 97.11984c158.950017 44.573817 162.964233-230.536306 0-173.427434-77.096466 27.017584-68.110483 154.329238 0 173.427434z m-159.551468 83.247281c25.407468 5.697597 32.996313-68.502363 6.93628-69.374722-14.470602 14.327481-48.605072 59.225025-6.93628 69.374722z m62.429924 0h13.879374c17.914037-28.413018-6.004286-65.42014-34.686509-69.374722 2.320612 27.740007-8.91783 69.037364 20.807135 69.374722z" fill="#FFFFFF" ></path></symbol><symbol id="powerpack" viewBox="0 0 1024 1024"><path d="M384.156483 720.268521l275.365669-243.511323h-124.486442l104.827678-173.025719-275.394264 243.525621h124.50074l-104.813381 173.011421z" ></path><path d="M355.790673 52.070705v52.056407H251.663561A52.070705 52.070705 0 0 0 199.592857 156.197816v815.717182a52.056407 52.056407 0 0 0 52.070704 52.070705h520.664154a52.056407 52.056407 0 0 0 52.070704-52.070705V156.212114a52.070705 52.070705 0 0 0-52.070704-52.070705h-104.127112V52.070705A52.070705 52.070705 0 0 0 616.129899 0H407.832783a52.070705 52.070705 0 0 0-52.04211 52.070705z m373.159085 121.526905a26.04965 26.04965 0 0 1 26.021055 26.021055v728.918377a26.035352 26.035352 0 0 1-26.035352 25.978163H295.055815a26.035352 26.035352 0 0 1-26.035352-26.035352V199.618665a26.035352 26.035352 0 0 1 26.035352-26.035353z" ></path></symbol><symbol id="store" viewBox="0 0 1024 1024"><path d="M925.355 188.459A132.01 132.01 0 0 0 794.709 77.78H229.291A132.01 132.01 0 0 0 98.645 188.416l-38.912 234.71A131.84 131.84 0 0 0 112.3 528v285.696a132.608 132.608 0 0 0 132.437 132.48h534.4a132.608 132.608 0 0 0 132.437-132.48V527.659A135.68 135.68 0 0 0 963.84 418.9L925.355 188.46zM593.28 895.019H430.677v-162.56H593.28v162.56z m267.221-81.28c0 44.8-36.437 81.28-81.28 81.28H644.48v-188.16a25.6 25.6 0 0 0-25.6-25.6H405.12a25.6 25.6 0 0 0-25.6 25.6v188.16H244.779a81.323 81.323 0 0 1-81.28-81.28v-261.59c9.216 2.048 18.688 3.286 28.501 3.286 43.733 0 82.603-21.334 106.667-54.144a132.096 132.096 0 0 0 213.333 0 132.096 132.096 0 0 0 213.333 0A132.096 132.096 0 0 0 832 555.435c9.813 0 19.285-1.238 28.501-3.286v261.59z m16-322.944c-0.469 0.17-0.853 0.469-1.28 0.682-12.544 7.936-27.306 12.715-43.178 12.715-44.715 0-81.067-36.352-81.067-81.067a25.6 25.6 0 1 0-51.2 0c0 44.715-36.395 81.067-81.067 81.067s-81.066-36.352-81.066-81.067a25.6 25.6 0 1 0-51.2 0c0 44.715-36.395 81.067-81.067 81.067s-81.067-36.352-81.067-81.067a25.6 25.6 0 1 0-51.2 0c0 44.715-36.394 81.067-81.066 81.067-15.318 0-29.526-4.523-41.771-11.733-1.152-0.64-2.176-1.323-3.413-1.792-21.675-14.208-36.054-37.931-36.267-63.275l38.528-230.485a80.939 80.939 0 0 1 80.17-67.883h565.42c39.935 0 73.642 28.544 80.17 67.883l38.187 226.218c0 28.288-14.592 53.163-36.566 67.67z" ></path></symbol><symbol id="douban" viewBox="0 0 1024 1024"><path d="M380.47491161 431.35026569h265.5097995v92.17112492H380.47491161zM404.68277728 570.90149136c23.04278124 38.83614815 44.79102419 81.16754963 63.04401383 124.79348939h88.54641778c26.66748839-41.16631703 48.41573136-82.4620879 64.20909828-124.79348939H404.68277728z" fill="#228A31" ></path><path d="M512 14.8973037c-274.57156741 0-497.1026963 222.53112889-497.1026963 497.1026963s222.53112889 497.1026963 497.1026963 497.1026963 497.1026963-222.53112889 497.1026963-497.1026963-222.53112889-497.1026963-497.1026963-497.1026963zM283.51399506 279.8892879h459.43163259v47.25064692H283.51399506v-47.25064692z m471.60029235 464.2214242h-1.29453827v0.12945382H269.01516642v-47.2506469h145.50610173c-18.12353581-38.83614815-36.37652543-72.75305086-55.79459951-100.58562371l37.54160988-24.20786568H330.76464197V382.93453431h368.55504593v189.1320415h-69.12834371l37.54160989 24.20786567c-18.12353581 38.83614815-36.37652543 72.75305086-54.50006125 100.5856237h141.88139458v47.25064692z" fill="#228A31" ></path></symbol><symbol id="weibo" viewBox="0 0 1024 1024"><path d="M757.76 507.44888853c-18.2044448 0-31.85777813-9.10222187-18.2044448-27.30666666 9.10222187-18.2044448 31.85777813-72.81777813-9.10222187-109.22666667-36.40888853-36.40888853-122.88-22.7555552-182.0444448 0-59.1644448 22.7555552-54.61333333 9.10222187-54.61333333-9.10222187s31.85777813-118.32888853-63.7155552-127.43111146C302.64888853 220.72888853 138.80888853 375.46666667 84.1955552 466.48888853-2.2755552 603.02222187 11.37777813 694.0444448 11.37777813 694.0444448c9.10222187 127.43111147 200.24888853 227.5555552 427.80444374 227.5555552 200.24888853 0 364.08888853-77.36888853 414.15111146-177.49333333 4.55111147-9.10222187 9.10222187-22.7555552 13.65333334-31.85777814 9.10222187-27.30666667 13.65333333-68.26666667 0-104.6755552-22.7555552-77.36888853-95.57333333-100.1244448-109.22666667-100.1244448z m-345.8844448 359.53777814c-163.84 0-295.82222187-86.47111147-295.82222187-195.69777814s131.98222187-195.69777813 295.82222187-195.69777706 295.82222187 86.47111147 295.82222293 195.69777706c0 104.6755552-131.98222187 195.69777813-295.82222293 195.69777814zM816.9244448 430.08h9.10222187c9.10222187 0 18.2044448-4.55111147 22.7555552-13.65333333 4.55111147-13.65333333 9.10222187-31.85777813 9.10222293-45.51111147 0-63.7155552-54.61333333-118.32888853-118.3288896-118.32888853-18.2044448 0-31.85777813 4.55111147-45.5111104 9.10222186-13.65333333 4.55111147-18.2044448 18.2044448-13.65333333 31.85777814 4.55111147 13.65333333 18.2044448 18.2044448 31.85777706 13.65333333 9.10222187-4.55111147 18.2044448-4.55111147 27.30666667-4.55111147 40.96 0 72.81777813 31.85777813 72.81777813 72.81777814 0 9.10222187 0 18.2044448-4.55111146 27.30666666-9.10222187 9.10222187-4.55111147 22.7555552 9.10222293 27.30666667z" fill="#FF5E6B" ></path><path d="M384.56888853 543.85777813c-86.47111147 0-159.28888853 63.7155552-159.28888853 141.08444374 0 77.36888853 72.81777813 141.0844448 159.28888853 141.0844448s159.28888853-63.7155552 159.2888896-141.0844448c0-77.36888853-72.81777813-141.0844448-159.2888896-141.08444374z m-54.61333333 227.5555552c-31.85777813 0-54.61333333-22.7555552-54.61333333-54.61333333s22.7555552-54.61333333 54.61333333-54.61333333 54.61333333 22.7555552 54.61333333 54.61333333c4.55111147 31.85777813-22.7555552 54.61333333-54.61333333 54.61333333z m104.67555627-100.1244448c-4.55111147 13.65333333-18.2044448 18.2044448-31.85777814 9.10222294-9.10222187-4.55111147-13.65333333-18.2044448-4.55111146-31.85777814 4.55111147-13.65333333 18.2044448-18.2044448 31.85777813-9.10222186 9.10222187 9.10222187 13.65333333 22.7555552 4.55111147 31.85777706zM735.0044448 102.4c-27.30666667 0-54.61333333 4.55111147-77.3688896 13.65333333-18.2044448 4.55111147-31.85777813 27.30666667-22.7555552 45.51111147 4.55111147 18.2044448 27.30666667 31.85777813 45.51111147 22.7555552 18.2044448-4.55111147 40.96-9.10222187 59.16444373-9.10222187 113.77777813 0 204.8 91.02222187 204.8 204.8 0 18.2044448-4.55111147 40.96-9.10222187 59.16444374s4.55111147 40.96 22.7555552 45.51111146h9.10222294c13.65333333 0 31.85777813-9.10222187 36.40888853-27.30666666 9.10222187-27.30666667 13.65333333-54.61333333 13.65333333-81.92 0-150.18666667-127.43111147-273.06666667-282.16888853-273.06666667z" fill="#FF5E6B" ></path></symbol><symbol id="twitter" viewBox="0 0 1024 1024"><path d="M875.99179852 247.4002609c38.62190206-4.2046603 75.40814885-14.52083579 109.37294948-29.95949921A361.84545406 361.84545406 0 0 1 890.19935605 317.97848747a558.43338871 558.43338871 0 0 1 0.77737023 29.29151747c0 303.85467038-246.32409505 550.17876543-550.17876543 550.17876543-111.32576048 0-214.92701108-33.0676856-301.52255969-89.91215565a362.52055577 362.52055577 0 0 0 56.85482636 4.47262973c87.3839224 0 167.49059792-31.14853262 229.83167746-82.94300888-83.94821783-2.45250275-154.52126625-58.18690623-179.09613351-134.55754428a215.16585339 215.16585339 0 0 0 33.4094437 2.6065528c18.1824373 0 35.83152482-2.27903463 52.68447132-6.55424727C144.52071538 572.41869022 78.00604445 494.15025904 78.00604445 400.34607408c0-0.90682405 0.02200715-1.80911724 0.03430526-2.71335221 26.06358629 14.23474284 55.41983067 23.19294768 86.63373749 25.44738606C113.88870352 388.05572773 80.59512098 329.48304909 80.59512098 263.12501728c0-36.40241619 10.02619891-70.45848178 27.45392041-99.57588385 95.31167478 117.11040475 237.8422803 194.24611493 398.57797751 202.40623692a194.6577781 194.6577781 0 0 1-5.30695965-45.22404725c0-107.24343404 86.9373067-194.18074075 194.18074075-194.18074073 55.80042493 0 106.09453132 23.54441482 141.51439297 61.23166024 44.41625537-7.78211682 86.03307173-23.69717033 123.24716341-46.17423834-15.98301677 43.72950282-45.85384012 80.77142092-84.27055786 105.79225663z" fill="#00A0E9" ></path></symbol><symbol id="changelog" viewBox="0 0 1024 1024"><path d="M846.1312 981.7088H183.0912a102.4 102.4 0 0 1-102.4-102.4V236.8512a102.4 102.4 0 0 1 102.4-102.4h663.04a102.4 102.4 0 0 1 102.4 102.4v642.56a102.4 102.4 0 0 1-102.4 102.2976z m-663.04-788.48a43.6224 43.6224 0 0 0-43.6224 43.6224v642.56a43.6224 43.6224 0 0 0 43.6224 43.52h663.04a43.6224 43.6224 0 0 0 43.52-43.52V236.8512a43.6224 43.6224 0 0 0-43.52-43.6224z" fill="#333333" ></path><path d="M514.56 265.6256A30.0032 30.0032 0 0 1 484.4544 235.52V61.44a30.1056 30.1056 0 1 1 60.2112 0v174.08a30.0032 30.0032 0 0 1-30.1056 30.1056zM307.2 265.6256A30.0032 30.0032 0 0 1 277.0944 235.52V61.44a30.1056 30.1056 0 1 1 60.2112 0v174.08A30.0032 30.0032 0 0 1 307.2 265.6256zM727.04 265.6256A30.0032 30.0032 0 0 1 696.9344 235.52V61.44a30.1056 30.1056 0 0 1 60.2112 0v174.08A30.0032 30.0032 0 0 1 727.04 265.6256zM778.24 428.7488H256a29.3888 29.3888 0 0 1 0-58.7776h522.24a29.3888 29.3888 0 1 1 0 58.7776zM634.88 592.5888H256a29.3888 29.3888 0 0 1 0-58.7776h378.88a29.3888 29.3888 0 1 1 0 58.7776zM491.52 766.6688H256a29.3888 29.3888 0 1 1 0-58.7776h235.52a29.3888 29.3888 0 0 1 0 58.7776z" fill="#333333" ></path></symbol><symbol id="bear" viewBox="0 0 1034 1024"><path d="M275.772462 298.225849l-0.275296 27.560523-10.759719 15.663598c-104.399116 151.997106-129.307015 208.605266-175.775517 399.473206l-6.499056 26.69604 2.8919 5.207477c72.143116 129.924503 228.722492 229.823678 381.362814 243.308061 5.621709 0.496563 12.787136 1.13206 14.464643 0 2.222955-1.502553-1.674935-11.19196 0-19.638674 14.094151-71.062513 23.196945-119.802854 16.129287-161.635055v-8.130251l46.198351-10.353206 59.875699-0.265005-4.106292-9.776885c-30.578492-72.801769-33.781709-123.361126-9.349789-147.597507 12.522131-12.424362 22.939658-15.473206 58.036101-16.991196 110.381025-4.775236 184.21194-50.81407 212.196985-132.319839 17.636985-51.364663 11.112201-79.002372-18.730452-79.336845-11.997266-0.136362-18.697005-2.081447-52.15196-15.146452-69.549668-27.164302-115.179417-55.275417-152.753528-94.110231-17.572663-18.164422-28.867538-25.785246-49.885266-33.66593-37.098131-13.906332-99.904322-18.915698-162.010694-12.923497l-10.695397 1.031718-7.314653-6.941587c-33.90006-32.178814-81.415719-53.248-104.072362-46.146895-18.46802 5.786372-26.297246 28.021065-26.775799 76.038432z" fill="#F4F3F3" ></path><path d="M477.009045 3.71007C172.989106 30.231156-40.049206 303.047397 9.370372 602.564824c9.730573 58.985487 30.964422 118.526714 60.832804 170.58605 6.133709 10.687678 8.518754 13.116462 8.531618 8.680845 0.002573-0.957106 5.063397-22.116342 11.243417-47.021669 27.825528-112.133146 44.628905-166.80394 64.375638-209.431156 21.326472-46.038834 62.81391-115.722291 106.923096-179.585929l13.504965-19.553769 0.084904-24.699498c0.244422-69.832683 16.255357-90.22006 60.634694-77.211658 22.404503 6.56595 46.123739 20.93797 70.496482 42.714694l8.747739 7.816361 5.660301-0.730693c26.986774-3.47594 79.39602-3.650894 107.545729-0.362774 49.607397 5.799236 81.824804 18.650693 103.429146 41.261025 25.965347 27.17202 42.791879 40.661548 73.761447 59.124422 43.867337 26.155739 117.963256 56.296844 136.809487 55.653629 28.229467-0.964824 35.711357 26.559678 20.397669 75.035015-27.040804 85.596623-110.635739 137.465568-222.220865 137.887517-84.665246 0.319035-88.238955 84.248442-9.506733 223.298895 19.270754 34.036422 74.759719 125.982874 77.733949 128.807879 1.425367 1.353327 42.526874-18.043497 63.444262-29.940422 190.739296-108.477106 291.883739-326.697166 251.577246-542.776603C975.367719 164.725065 735.643658-18.85395 477.009045 3.71007" fill="#DC4C4C" ></path><path d="M497.308945 826.730774c-9.499015-91.344402-54.804583-137.748583-74.170533-167.526915C375.638191 587.405508 375.638191 535.155779 375.638191 535.155779s39.462593 65.952804 78.73994 89.520241c39.27992 23.567437 158.758593 25.208925 158.758593 25.208925-24.094874 2.251256-19.919116 4.795819-20.094071 28.996181 0.728121 32.564744-14.199638 82.179859 0 111.495075 23.930211 54.807156 113.404141 204.524704 113.404141 204.524704s-37.898291 13.96808-66.971658 21.964542c-10.80603 2.971658-20.351357 6.830955-26.338412 6.375558-80.82396-6.12599-113.862111-5.222915-126.379096-6.375558-7.319799-0.67409-6.864402 5.127719-6.864402-5.099417 0.838754-4.422754 1.574593-8.999879 2.225528-13.708221 7.219457-52.267739 3.962211-120.662191 15.192764-171.327035z" fill="#D1D1D1" ></path></symbol><symbol id="notion" viewBox="0 0 1024 1024"><path d="M647.165837 2.332635L78.85493 44.179432C33.04873 48.138835 17.074585 78.005024 17.074585 113.810318v621.148461c0 27.886487 9.966774 51.745305 33.893858 83.625329l133.595729 173.189762c21.947382 27.886487 41.88093 33.859725 83.795993 31.880024l659.957441-39.832963c55.807107-3.959403 71.781251-29.866189 71.781251-73.658555V211.361824c0-22.630038-8.976923-29.1494-35.3957-48.468558a1594.683818 1594.683818 0 0 1-4.505528-3.276748L778.781865 32.198823C734.8871 0.387066 716.967387-3.640603 647.165837 2.332635zM283.310326 199.92734c-53.895671 3.618075-66.115209 4.437262-96.732319-20.377274l-77.822755-61.712079c-7.918807-7.987072-3.925271-17.953846 15.974144-19.933548l546.363525-39.79883c45.840333-3.993536 69.767417 11.946476 87.721263 25.872653l93.728634 67.71945c3.993536 1.979702 13.926177 13.892044 1.979702 13.892044l-564.249106 33.859725-6.963088 0.477859zM220.471864 904.189138V310.961297c0-25.906785 7.952939-37.853261 31.880024-39.867096l648.010965-37.819128c21.981515-1.979702 31.948289 11.946476 31.948289 37.819128v589.268439c0 25.906785-3.993536 47.820035-39.935361 49.799736l-620.090346 35.839427c-35.873559 1.979702-51.813571-9.932641-51.813571-41.812665z m612.171539-561.416084c3.959403 17.919713 0 35.839427-17.987979 37.887394l-29.900321 5.904972v437.991926c-25.940918 13.926177-49.833869 21.879117-69.767417 21.879116-31.914156 0-39.935361-9.966774-63.828313-39.79883l-195.444339-306.580694v296.613921l61.84861 13.96031s0 35.839427-49.902135 35.839426l-137.555132 7.95294c-3.959403-7.987072 0-27.886487 13.994443-31.845891l35.839426-9.932641v-392.185725l-49.833869-4.027669c-3.959403-17.919713 5.973238-43.792366 33.92799-45.772068l147.55604-9.966773 203.397279 310.608363v-274.768937l-51.881837-5.939105c-3.959403-21.947382 11.946476-37.853261 31.914156-39.832962l137.623398-7.987073z" fill="#000000" ></path></symbol><symbol id="premium" viewBox="0 0 1024 1024"><path d="M876.8 409.6l-136.96 42.24c-23.04 7.68-48.64-1.28-62.72-21.76L558.08 256c-21.76-32-70.4-32-92.16 0l-119.04 172.8c-14.08 20.48-39.68 29.44-62.72 21.76L147.2 409.6c-39.68-12.8-79.36 23.04-71.68 64l79.36 418.56c7.68 39.68 42.24 67.84 81.92 67.84h547.84c40.96 0 75.52-28.16 81.92-67.84L947.2 473.6c7.68-40.96-30.72-76.8-70.4-64zM638.72 608l-103.68 204.8c-3.84 8.96-12.8 14.08-23.04 14.08s-17.92-5.12-23.04-14.08l-103.68-204.8c-6.4-12.8-1.28-28.16 11.52-34.56s28.16-1.28 34.56 11.52L512 744.96l80.64-160c6.4-12.8 21.76-17.92 34.56-11.52 12.8 6.4 17.92 21.76 11.52 34.56z" fill="#FF5511" ></path><path d="M512 120.32m-56.32 0a56.32 56.32 0 1 0 112.64 0 56.32 56.32 0 1 0-112.64 0Z" fill="#FF5511" ></path><path d="M75.52 307.2m-56.32 0a56.32 56.32 0 1 0 112.64 0 56.32 56.32 0 1 0-112.64 0Z" fill="#FF5511" ></path><path d="M948.48 307.2m-56.32 0a56.32 56.32 0 1 0 112.64 0 56.32 56.32 0 1 0-112.64 0Z" fill="#FF5511" ></path></symbol><symbol id="google" viewBox="0 0 1024 1024"><path d="M509.8 231.8c86.2 0 144 36.8 176.9 68.5l120.5-116.1C729.9 111.7 629.7 68 509.9 68c-172.3 0-321.4 98-395 241.2L249.4 422c37-110.5 139.7-190.2 260.4-190.2z" fill="#EA4335" ></path><path d="M234.6 512.1c0-31.6 5.4-61.8 14.9-90.1L114.8 309.2c-31.2 60.8-49 129.7-49 202.8 0 67.4 15.1 131.2 41.9 188.4l137.1-113.6c-6.5-23.8-10.2-48.8-10.2-74.7z" fill="#FBBC05" ></path><path d="M509.8 792.3c-126.2 0-232.7-87.1-265-205.5L107.7 700.4C178.6 851.5 331.8 956 509.9 956c120.6 0 221.7-40.3 295.4-109L667.1 746.6c-38.2 26.8-89.5 45.7-157.3 45.7z" fill="#34A853" ></path><path d="M936.1 522.2c0-29.1-3.1-51.3-6.9-73.5l-419.4-0.2-0.1 0.1v152.3h251.8C755.2 641 727.8 704 667 746.7L805.3 847c82.8-77.3 130.8-190.6 130.8-324.8z" fill="#4285F4" ></path></symbol></svg>',function(a){var c=(c=document.getElementsByTagName("script"))[c.length-1],l=c.getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,v,s=function(c,l){l.parentNode.insertBefore(c,l)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(c){console&&console.log(c)}}h=function(){var c,l=document.createElement("div");l.innerHTML=a._iconfont_svg_string_1402208,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(c=document.body).firstChild?s(l,c.firstChild):c.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,v=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){v||(v=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(c){return void setTimeout(z,50)}p()}}(window);
================================================
FILE: config.json
================================================
{
"iconfont": "https://at.alicdn.com/t/font_1402208_ghcp6tuu13c.js",
"chromeWebStoreVersion": "1.28.4",
"edgeWebStoreVersion": "1.28.4",
"firefoxWebStoreVersion": "1.28.4",
"privacyLocale": ["en-US", "zh-CN"],
"changelogLocale": ["en-US", "zh-CN"]
}
================================================
FILE: global.d.ts
================================================
declare module '*.md' {
const src: string;
export default src;
}
================================================
FILE: package.json
================================================
{
"name": "web-clipper",
"version": "1.42.0",
"description": "Universal open source web clipper.",
"bin": {
"web-clipper": "bin/index.js"
},
"scripts": {
"test": "vitest",
"cov": "vitest --coverage",
"dev": "webpack --config webpack/webpack.dev.js --watch",
"release": "ts-node script/release.ts",
"format": "web-clipper format"
},
"author": "DiamondYuan",
"license": "GPL-2.0-or-later",
"dependencies": {
"@ant-design/compatible": "^1.0.8",
"@ant-design/icons": "^4.2.2",
"@formily/antd": "^2.0.0-beta.47",
"@formily/core": "^2.0.0-beta.47",
"@formily/react": "^2.0.0-beta.47",
"@shihengtech/hooks": "^0.0.16",
"@web-clipper/area-selector": "^0.1.3",
"@web-clipper/chrome-promise": "^0.1.2",
"@web-clipper/highlight": "^0.1.3",
"@web-clipper/readability": "^0.3.0",
"@web-clipper/remark-pangu": "^1.0.2",
"@web-clipper/shared": "^0.0.20",
"@web-clipper/turndown": "^0.4.8",
"antd": "4.16.3",
"classnames": "^2.2.6",
"codemirror": "^5.47.0",
"copy-to-clipboard": "^3.2.0",
"cos-js-sdk-v5": "^1.8.1",
"dayjs": "^1.10.4",
"dva": "^2.6.0-beta.19",
"dva-loading": "^3.0.19",
"dva-model-creator": "^0.4.3",
"filenamify": "^4.1.0",
"form-data": "^2.3.3",
"history": "4.10.1",
"hypermd": "^0.3.11",
"immutability-helper": "^3.0.1",
"jquery": "^3.4.0",
"lodash": "^4.17.20",
"mobx": "^5.15.1",
"mobx-react": "^6.1.4",
"qrcode": "^1.4.1",
"qs": "^6.7.0",
"query-string": "7",
"raw-loader": "^4.0.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-intl": "^3.9.2",
"react-markdown": "^6.0.2",
"redux": "4.2.1",
"redux-saga": "^0.16.2",
"reflect-metadata": "^0.1.13",
"regenerator-runtime": "^0.13.3",
"remark": "^11.0.2",
"short-uuid": "^3.1.1",
"showdown": "^1.9.0",
"tldjs": "^2.3.1",
"turndown": "5.0.3",
"typedi": "^0.8.0",
"umi-request": "^1.2.15",
"webdav": "^5.2.2"
},
"devDependencies": {
"@types/chrome": "^0.0.268",
"@types/classnames": "^2.2.9",
"@types/codemirror": "^0.0.76",
"@types/history": "^4.7.2",
"@types/jquery": "^3.3.6",
"@types/lodash": "^4.14.161",
"@types/qrcode": "^1.3.3",
"@types/qs": "^6.9.0",
"@types/react": "^17.0.6",
"@types/react-dom": "^16.9.9",
"@types/react-redux": "^7.0.8",
"@types/react-router": "^5.1.3",
"@types/showdown": "^1.9.3",
"@types/tldjs": "^2.3.0",
"@types/yargs": "^17.0.2",
"@vitest/coverage-v8": "^0.32.2",
"axios": "^0.21.1",
"clean-webpack-plugin": "^0.1.19",
"compressing": "^1.4.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^1.0.0",
"html-webpack-plugin": "^3.2.0",
"less": "^3.8.1",
"less-loader": "^7.0.2",
"prettier": "^3.3.2",
"pump": "^3.0.0",
"style-loader": "^0.23.1",
"terser-webpack-plugin": "^2.3.1",
"ts-loader": "^6.2.1",
"ts-node": "^10.9.2",
"typescript": "^5.1.6",
"url-loader": "^3.0.0",
"vitest": "^0.32.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.2",
"webpack-merge": "^4.2.2",
"yargs": "^17.1.1"
},
"packageManager": "pnpm@8.14.0+sha512.5d4bf97b349faf1a51318aa1ba887e99d9c36e203dbcb55938a91fddd2454246cb00723d6642f54d463a0f52a2701dadf8de002a37fc613c9cdc94ed5675ddce"
}
================================================
FILE: script/build.js
================================================
const webpack = require('webpack');
const prodConfig = require('../webpack/webpack.prod');
const compiler = webpack(prodConfig);
function send(data) {
if (!process.send) {
return;
}
return new Promise((r) => {
process.send(data, null, {}, r);
});
}
compiler.run((err) => {
if (err) {
console.log(err);
}
send({
type: 'Success',
});
});
================================================
FILE: script/release.ts
================================================
import { fork } from 'child_process';
import fs from 'fs';
import path from 'path';
import { pack } from './utils/pack';
(async () => {
const releaseDir = path.join(__dirname, '../release');
if (!fs.existsSync(releaseDir)) {
fs.mkdirSync(releaseDir);
}
await build();
await pack({
releaseDir,
distDir: path.join(__dirname, '../dist/chrome'),
fileName: 'web-clipper-chrome.zip',
});
await pack({
releaseDir,
distDir: path.join(__dirname, '../dist'),
fileName: 'web-clipper-firefox.zip',
});
const manifestConfig = path.join(__dirname, '../dist/manifest.json');
const content = fs.readFileSync(manifestConfig, 'utf-8');
const manifest = JSON.parse(content);
manifest.browser_specific_settings = {
gecko: {
id: '{3fbb1f97-0acf-49a0-8348-36e91bef22ea}',
},
};
manifest.name = 'Universal Web Clipper';
fs.writeFileSync(manifestConfig, JSON.stringify(manifest, null, 2));
await pack({
releaseDir,
distDir: path.join(__dirname, '../dist'),
fileName: 'web-clipper-firefox-store.zip',
});
})();
function build() {
const buildScript = require.resolve('./build');
const buildEnv = Object.create(process.env);
buildEnv.NODE_ENV = 'production';
const cp = fork(buildScript, [], {
env: buildEnv as unknown as typeof process.env,
stdio: 'inherit',
});
return new Promise((r) => {
cp.on('message', r);
});
}
================================================
FILE: script/utils/pack.ts
================================================
import compressing from 'compressing';
import fs from 'fs';
import path from 'path';
const pump = require('pump');
interface IPackOptions {
distDir: string;
releaseDir: string;
fileName: string;
}
export function pack(options: IPackOptions) {
const zipStream = new compressing.zip.Stream();
const files = fs.readdirSync(options.distDir).filter((p) => !p.match(/^\./));
for (const file of files) {
zipStream.addEntry(path.join(options.distDir, file));
}
const dest = path.join(options.releaseDir, options.fileName);
const destStream = fs.createWriteStream(dest);
return new Promise((r) => {
pump(zipStream, destStream, r);
});
}
================================================
FILE: src/__test__/utils.ts
================================================
import { IRequestService, TRequestOption } from '@/service/common/request';
import { vi, Mock } from 'vitest';
type TMockRequestServiceHandler = (url: string, options?: TRequestOption) => any;
export class MockRequestService implements IRequestService {
public mock: {
request: Mock;
};
private handler: TMockRequestServiceHandler;
constructor(handler: TMockRequestServiceHandler) {
this.mock = {
request: vi.fn(),
};
this.handler = handler;
}
request(url: string, options: TRequestOption) {
this.mock.request(url, options);
return this.handler(url, options);
}
download(url: string): Promise<Blob> {
return Promise.resolve(new Blob([url]));
}
}
================================================
FILE: src/actions/account.ts
================================================
import { UserInfo } from '@/common/backend/services/interface';
import { AccountPreference } from '@/common/types';
import { actionCreatorFactory } from 'dva-model-creator';
const actionCreator = actionCreatorFactory('account');
export const asyncAddAccount = actionCreator.async<
{
id: string;
info: any;
imageHosting?: string;
defaultRepositoryId?: string;
userInfo: UserInfo;
type: string;
callback(): void;
},
{
accounts: AccountPreference[];
defaultAccountId: string;
},
void
>('asyncAddAccount');
export const initAccounts = actionCreator.async<
void,
{ accounts: AccountPreference[]; defaultAccountId: string }
>('initAccounts');
export const asyncDeleteAccount = actionCreator.async<
{ id: string },
{
accounts: AccountPreference[];
defaultAccountId: string;
},
void
>('asyncDeleteAccount');
export const asyncUpdateDefaultAccountId = actionCreator.async<{ id?: string }, void>(
'asyncUpdateDefaultAccountId'
);
export const asyncUpdateAccount = actionCreator<{
id: string;
account: {
info: any;
imageHosting?: string;
defaultRepositoryId?: string;
type: string;
};
newId: string;
userInfo: UserInfo;
callback(): void;
}>('asyncUpdateAccount');
================================================
FILE: src/actions/clipper.ts
================================================
import { ClipperHeaderForm } from 'common/modelTypes/clipper';
import { Repository, CompleteStatus, CreateDocumentRequest } from 'common/backend/index';
import { actionCreatorFactory } from 'dva-model-creator';
const actionCreator = actionCreatorFactory('clipper');
export const asyncCreateDocument = actionCreator.async<
{ pathname: string },
{
result: CompleteStatus;
request: CreateDocumentRequest;
},
null
>('ASYNC_CREATE_DOCUMENT');
export const asyncUploadImage = actionCreator.async<void, void, void>('ASYNC_UPLOAD_IMAGE');
export const selectRepository = actionCreator<{ repositoryId: string }>('SELECT_REPOSITORY');
export const initTabInfo = actionCreator<{ title: string; url: string }>('INIT_TAB_INFO');
export const asyncChangeAccount = actionCreator.async<
{
id: string;
},
{
repositories: Repository[];
currentImageHostingService?: { type: string };
},
any
>('ASYNC_CHANGE_ACCOUNT');
export const changeData = actionCreator<{ data: any; pathName: string }>('CHANGE_DATA');
export const watchActionChannel = actionCreator('watchActionChannel');
export const updateClipperHeader = actionCreator<ClipperHeaderForm>('updateClipperHeader');
================================================
FILE: src/actions/userPreference.ts
================================================
import { IExtensionWithId } from './../extensions/common';
import { ImageHosting, GlobalStore } from '@/common/types';
import { PreferenceStorage } from 'common/storage/interface';
import { actionCreatorFactory } from 'dva-model-creator';
const actionCreator = actionCreatorFactory('userPreference');
export const initUserPreference = actionCreator<PreferenceStorage>('INIT_USER_PREFERENCE');
export const asyncChangeDefaultRepository = actionCreator.async<
{
defaultRepositoryId: string;
},
void
>('ASYNC_CHANGE_DEFAULT_REPOSITORY');
export const asyncSetEditorLiveRendering = actionCreator.async<
{
value: boolean;
},
{
value: boolean;
},
void
>('ASYNC_SET_EDITOR_LIVE_RENDERING');
export const asyncSetIconColor = actionCreator.async<
{
value: 'dark' | 'light' | 'auto';
},
{
value: 'dark' | 'light' | 'auto';
},
void
>('ASYNC_SET_ICON_COLOR');
export const asyncRunExtension = actionCreator.async<
{
pathname: string;
extension: IExtensionWithId;
},
{
result: unknown;
pathname: string;
},
void
>('ASYNC_RUN_EXTENSION');
export const asyncRunScript = actionCreator.async<string, void, void>('ASYNC_RUN_SCRIPT');
export const asyncAddImageHosting = actionCreator.async<
{ closeModal: () => void } & Omit<ImageHosting, 'id'>,
ImageHosting[],
void
>('ASYNC_ADD_IMAGE_HOSTING');
export const asyncDeleteImageHosting = actionCreator.async<{ id: string }, ImageHosting[], void>(
'ASYNC_DELETE_IMAGE_HOSTING'
);
export const asyncEditImageHosting = actionCreator.async<
{ id: string; value: Omit<ImageHosting, 'id'>; closeModal: () => void },
ImageHosting[],
void
>('ASYNC_EDIT_IMAGE_HOSTING');
export const setLocale = actionCreator<string>('setLocale');
export const asyncSetLocaleToStorage = actionCreator<string>('asyncSetLocaleToStorage');
export const initServices = actionCreator<
Pick<GlobalStore['userPreference'], 'servicesMeta' | 'imageHostingServicesMeta'>
>('initServices');
================================================
FILE: src/common/backend/clients/github/client.test.ts
================================================
import { MockRequestService } from '@/__test__/utils';
import { GithubClient } from './client';
import { IRepository } from './types';
describe('test GithubClient', () => {
test('test generateNewTokenUrl', () => {
expect(GithubClient.generateNewTokenUrl).toEqual(
'https://github.com/settings/tokens/new?scopes=repo&description=Web%20Clipper'
);
});
function getTestFixtures(response?: any) {
let handler = () => response;
if (typeof response === 'function') {
handler = response;
}
const mockRequestService = new MockRequestService(handler);
const githubClient = new GithubClient({
token: 'DiamondYuan',
request: mockRequestService,
});
return {
mockRequestService: mockRequestService.mock.request,
githubClient,
};
}
test('test getUserInfo', async () => {
const expectResult = { login: 'DiamondYuan' };
const testFixtures = getTestFixtures(expectResult);
const result = await testFixtures.githubClient.getUserInfo();
expect(testFixtures.mockRequestService.mock.calls[0]).toEqual([
'https://api.github.com/user',
{
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: 'token DiamondYuan',
},
method: 'get',
},
]);
expect(result).toEqual(expectResult);
});
test('test createIssue', async () => {
const expectResult = { html_url: 'html_url', id: 'id' };
const testFixtures = getTestFixtures(expectResult);
const result = await testFixtures.githubClient.createIssue({
title: 'title',
body: 'body',
labels: ['label1', 'label2'],
namespace: 'webclipper/web-clipper',
});
expect(testFixtures.mockRequestService.mock.calls[0]).toEqual([
'https://api.github.com/repos/webclipper/web-clipper/issues',
{
data: { title: 'title', body: 'body', labels: ['label1', 'label2'] },
method: 'post',
requestType: 'json',
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: 'token DiamondYuan',
},
},
]);
expect(result).toEqual(expectResult);
});
test('test createIssue', async () => {
const expectResult = { html_url: 'html_url', id: 'id' };
const testFixtures = getTestFixtures(expectResult);
const result = await testFixtures.githubClient.uploadFile({
owner: 'owner',
repo: 'repo',
path: 'path',
message: 'message',
content: 'content',
branch: 'branch',
});
expect(testFixtures.mockRequestService.mock.calls[0]).toEqual([
'https://api.github.com/repos/owner/repo/contents/path',
{
data: { message: 'message', content: 'content', branch: 'branch' },
method: 'put',
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: 'token DiamondYuan',
},
},
]);
expect(result).toEqual(expectResult);
});
test('test list branches', async () => {
const testFixtures = getTestFixtures([]);
await testFixtures.githubClient.listBranch({
owner: 'owner',
repo: 'repo',
protected: false,
page: 1,
per_page: 100,
});
expect(testFixtures.mockRequestService.mock.calls[0]).toEqual([
'https://api.github.com/repos/owner/repo/branches?protected=false&per_page=100&page=1',
{
method: 'get',
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: 'token DiamondYuan',
},
},
]);
});
test('test getRepos', async () => {
const testFixtures = getTestFixtures([]);
await testFixtures.githubClient.getRepos({
visibility: 'all',
affiliation: 'owner',
page: 1,
per_page: 100,
});
expect(testFixtures.mockRequestService.mock.calls[0]).toEqual([
'https://api.github.com/user/repos?affiliation=owner&per_page=100&page=1',
{
method: 'get',
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: 'token DiamondYuan',
},
},
]);
});
test('test git all', async () => {
const result: IRepository[] = [];
for (let i = 0; i < 670; i++) {
result.push({
name: `${i}`,
full_name: `webclipper/${i}`,
default_branch: 'main',
});
}
const testFixtures = getTestFixtures((...args: [string, Object]) => {
const url = new URL(args[0]);
const page = parseInt(url.searchParams.get('page')!, 10);
const per_page = parseInt(url.searchParams.get('per_page')!, 10);
return result.slice(per_page * (page - 1), per_page * page);
});
const res = await testFixtures.githubClient.queryAll(
{
visibility: 'all',
affiliation: 'owner',
},
testFixtures.githubClient.getRepos
);
expect(res).toEqual(result);
});
});
================================================
FILE: src/common/backend/clients/github/client.ts
================================================
import { IExtendRequestHelper } from '@/service/common/request';
import { RequestHelper } from '@/service/request/common/request';
import { stringify } from 'qs';
import {
ICreateIssueOptions,
ICreateIssueResponse,
IGithubClientOptions,
IGithubUserInfoResponse,
IUploadFileOptions,
IUploadFileResponse,
IListBranchesOptions,
IBranch,
TPageRequest,
IPageQuery,
TOmitPage,
IGetGithubRepositoryOptions,
IRepository,
} from './types';
export class GithubClient {
private options: IGithubClientOptions;
private request: IExtendRequestHelper;
constructor(options: IGithubClientOptions) {
this.options = options;
this.request = new RequestHelper({
baseURL: 'https://api.github.com/',
headers: {
Accept: 'application/vnd.github.v3+json',
Authorization: `token ${this.options.token}`,
},
request: this.options.request,
});
}
createIssue = async (options: ICreateIssueOptions) => {
const data = { title: options.title, body: options.body, labels: options.labels };
const response = await this.request.post<ICreateIssueResponse>(
`repos/${options.namespace}/issues`,
{ data }
);
return response;
};
getUserInfo = () => {
return this.request.get<IGithubUserInfoResponse>('user');
};
queryAll = async <O extends IPageQuery, T>(
args: TOmitPage<O>,
pageRequest: TPageRequest<O, T>
): Promise<T[]> => {
const startPage: number = 1;
const pageSize: number = 50;
const baseArgs = { ...args, page: startPage, per_page: pageSize } as O;
let result: T[] = [];
while (true) {
const response = await pageRequest(baseArgs);
result = result.concat(response);
if (response.length === pageSize) {
baseArgs.page = baseArgs.page + 1;
continue;
}
return result;
}
};
/**
* Create or update file contents
*
* @see https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#create-or-update-file-contents
*/
uploadFile = (options: IUploadFileOptions) => {
return this.request.put<IUploadFileResponse>(
`repos/${options.owner}/${options.repo}/contents/${options.path}`,
{
data: {
message: options.message,
content: options.content,
branch: options.branch,
},
}
);
};
listBranch = (options: IListBranchesOptions) => {
return this.request.get<IBranch[]>(
`repos/${options.owner}/${options.repo}/branches?${stringify({
protected: options.protected,
per_page: options.per_page,
page: options.page,
})}`
);
};
/**
*
* @see https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#list-repositories-for-the-authenticated-user
* @param options IGetGithubRepositoryOptions
*/
getRepos = (options: IGetGithubRepositoryOptions) => {
return this.request.get<IRepository[]>(
`user/repos?${stringify({
affiliation: options.affiliation,
per_page: options.per_page,
type: options.type,
page: options.page,
})}`
);
};
static get generateNewTokenUrl() {
return `https://github.com/settings/tokens/new?${stringify({
scopes: 'repo',
description: 'Web Clipper',
})}`;
}
}
================================================
FILE: src/common/backend/clients/github/types.ts
================================================
import { IRequestService } from '@/service/common/request';
export interface IGithubClientOptions {
token: string;
request: IRequestService;
}
export interface ICreateIssueOptions {
title: string;
body: string;
labels: string[];
namespace: string;
}
export interface ICreateIssueResponse {
html_url: string;
id: number;
}
export interface IGithubUserInfoResponse {
login: string;
avatar_url: string;
name: string;
bio: string;
html_url: string;
}
export interface IUploadFileOptions {
owner: string;
repo: string;
path: string;
message: string;
content: string;
branch?: string;
}
export interface IUploadFileResponse {
content: {
html_url: string;
};
}
export interface IListBranchesOptions extends IPageQuery {
owner: string;
repo: string;
protected: boolean;
}
export interface IBranch {
name: string;
protected: boolean;
}
export interface IPageQuery {
per_page: number;
page: number;
}
export type TOmitPage<T> = Omit<T, 'page' | 'per_page'>;
export type TPageRequest<O extends IPageQuery, R> = (option: O) => Promise<R[]>;
export interface IGetGithubRepositoryOptions extends IPageQuery {
visibility?: 'all' | 'public' | 'private';
affiliation?: 'owner' | 'collaborator' | 'organization_member';
type?: 'all' | 'owner' | 'public' | 'private' | 'member';
}
export interface IRepository {
name: string;
/**
*like webclipper/web-clipper
*/
full_name: string;
default_branch: string;
}
================================================
FILE: src/common/backend/clients/joplin/LegacyJoplinClient.ts
================================================
import { Repository } from './../../services/interface';
import { JoplinFolderItem, JoplinTag, JoplinCreateDocumentRequest } from './types';
import { AbstractJoplinClient } from './basic';
export class LegacyJoplinClient extends AbstractJoplinClient {
getRepositories = async () => {
const repositories: Repository[] = [];
const folders = await this.request.get<JoplinFolderItem[]>('folders');
folders.forEach(folder => {
repositories.push({
id: folder.id,
name: folder.title,
groupId: folder.id,
groupName: folder.title,
});
if (Array.isArray(folder.children)) {
folder.children.forEach(subFolder => {
repositories.push({
id: subFolder.id,
name: subFolder.title,
groupId: folder.id,
groupName: folder.title,
});
});
}
});
return repositories;
};
getTags = async (filterTags: boolean) => {
let tags = await this.request.get<JoplinTag[]>('tags');
if (filterTags) {
tags = (
await Promise.all(
tags.map(async tag => {
console.log(this);
const notes = await this.request.get<unknown[]>(`tags/${tag.id}/notes`);
if (notes.length === 0) {
return null;
}
return tag;
})
)
).filter((tag): tag is JoplinTag => !!tag);
}
return tags;
};
createDocument = async (data: JoplinCreateDocumentRequest) => {
await this.request.post('notes', {
data: {
parent_id: data.repositoryId,
title: data.title,
body: data.content,
tags: data.tags.join(','),
source_url: data.url,
},
});
};
}
================================================
FILE: src/common/backend/clients/joplin/basic.ts
================================================
import { generateUuid } from '@web-clipper/shared/lib/uuid';
import { IExtendRequestHelper } from '@/service/common/request';
import { Repository, IJoplinClient, JoplinTag, JoplinCreateDocumentRequest } from './types';
export abstract class AbstractJoplinClient implements IJoplinClient {
constructor(protected request: IExtendRequestHelper) {}
public uploadBlob = async (blob: Blob): Promise<string> => {
let formData = new FormData();
formData.append('data', blob);
formData.append(
'props',
JSON.stringify({
title: generateUuid(),
})
);
const result = await this.request.postForm<{ id: string }>(`resources`, {
data: formData,
});
return `:/${result.id}`;
};
abstract getTags(filterTags: boolean): Promise<JoplinTag[]>;
abstract getRepositories(): Promise<Repository[]>;
abstract createDocument(data: JoplinCreateDocumentRequest): Promise<void>;
}
================================================
FILE: src/common/backend/clients/joplin/client.ts
================================================
import { AbstractJoplinClient } from './basic';
import {
Repository,
JoplinFolderItem,
JoplinTag,
JoplinCreateDocumentRequest,
IPageRes,
} from './types';
export class JoplinClient extends AbstractJoplinClient {
support = async (): Promise<boolean> => {
let tags = await this.request.get<IPageRes<JoplinTag>>('tags');
return typeof tags.has_more === 'boolean';
};
getRepositories = async () => {
const repositories: Repository[] = [];
const folders = await this.pageToAllList(this.getFolderByPageNumber);
folders.forEach(folder => {
repositories.push({
id: folder.id,
name: folder.title,
groupId: folder.id,
groupName: folder.title,
});
if (Array.isArray(folder.children)) {
folder.children.forEach(subFolder => {
repositories.push({
id: subFolder.id,
name: subFolder.title,
groupId: folder.id,
groupName: folder.title,
});
});
}
});
return repositories;
};
getTags = async (filterTags: boolean) => {
let tags = await this.pageToAllList<JoplinTag>(this.getTagsByPageNumber);
if (filterTags) {
tags = (
await Promise.all(
tags.map(async tag => {
const notes = await this.request.get<unknown[]>(`tags/${tag.id}/notes`);
console.log('notes', notes);
if (notes.length === 0) {
return null;
}
return tag;
})
)
).filter((tag): tag is JoplinTag => !!tag);
}
return tags;
};
createDocument = async (data: JoplinCreateDocumentRequest) => {
await this.request.post('notes', {
data: {
parent_id: data.repositoryId,
title: data.title,
body: data.content,
tags: data.tags.join(','),
source_url: data.url,
},
});
};
private getTagsByPageNumber = async (page: number) => {
return this.request.get<IPageRes<JoplinTag>>(`tags?page=${page}`);
};
private getFolderByPageNumber = async (page: number) => {
return this.request.get<IPageRes<JoplinFolderItem>>(`folders?page=${page}`);
};
private pageToAllList = async <T>(
getOnePage: (page: number) => Promise<IPageRes<T>>
): Promise<T[]> => {
let hasMore = true;
let startPageNumber = 1;
let result: T[] = [];
while (hasMore) {
const response = await getOnePage(startPageNumber);
result = result.concat(response.items);
hasMore = response.has_more;
startPageNumber++;
}
return result;
};
}
================================================
FILE: src/common/backend/clients/joplin/index.ts
================================================
export { JoplinClient } from './client';
export { LegacyJoplinClient } from './LegacyJoplinClient';
export * from './types';
================================================
FILE: src/common/backend/clients/joplin/types.ts
================================================
import type { Repository, CreateDocumentRequest } from '../../services/interface';
export type { Repository } from '../../services/interface';
export type { CreateDocumentRequest } from '../../services/interface';
export interface IJoplinClient {
getTags(filterTags: boolean): Promise<JoplinTag[]>;
getRepositories(): Promise<Repository[]>;
createDocument(data: JoplinCreateDocumentRequest): Promise<void>;
uploadBlob(blob: Blob): Promise<string>;
}
export interface JoplinTag {
id: string;
title: string;
}
export interface JoplinCreateDocumentRequest extends CreateDocumentRequest {
tags: string[];
}
export interface JoplinBackendServiceConfig {
token: string;
filterTags: boolean;
}
export interface JoplinFolderItem {
id: string;
title: string;
children: JoplinFolderItem[];
}
export interface JoplinTag {
id: string;
title: string;
}
export interface IJoplinClient {
getTags(filterTags: boolean): Promise<JoplinTag[]>;
getRepositories(): Promise<Repository[]>;
createDocument(data: JoplinCreateDocumentRequest): Promise<void>;
}
export interface JoplinCreateDocumentRequest extends CreateDocumentRequest {
tags: string[];
}
export interface IPageRes<T> {
has_more: boolean;
items: T[];
}
================================================
FILE: src/common/backend/clients/leanote/client.test.ts
================================================
import { MockRequestService } from '@/__test__/utils';
import LeanoteClient from './client';
const FormData = require('form-data');
describe('test LeanoteClient', () => {
test('test login', async () => {
const expectedResponseToken = { Token: 'SomeToken' };
const mockRequestService = new MockRequestService(() => expectedResponseToken);
const inputStub = {
leanote_host: 'https://localhost',
email: 'email',
pwd: 'pwd',
token_cached: 'OldToken',
};
const client = new LeanoteClient(inputStub, mockRequestService);
const result = await client.login();
const expectUrlGenerated = `${inputStub.leanote_host}/api/auth/login`;
expect(mockRequestService.mock.request.mock.calls[0][0]).toEqual(expectUrlGenerated);
//TODO add test
expect(result).toEqual(expectedResponseToken.Token);
});
test('test repository', async () => {
const notebookListStubResponse = [{ NotebookId: 1, Title: 'some_notebook_title' }];
const inputStub = {
leanote_host: 'https://localhost',
email: '',
pwd: '',
token_cached: 'some_token_when_already_logged',
};
const mockRequestService = new MockRequestService(() => notebookListStubResponse);
const client = new LeanoteClient(inputStub, mockRequestService);
const result = await client.getSyncNotebooks();
const expectUrlGenerated = `${inputStub.leanote_host}/api/notebook/getSyncNotebooks?token=${inputStub.token_cached}`;
expect(mockRequestService.mock.request.mock.calls[0][0]).toEqual(expectUrlGenerated);
expect(notebookListStubResponse[0].Title).toEqual(result[0].Title);
});
test('test create document', async () => {
const inputStub = {
leanote_host: 'https://localhost',
email: '',
pwd: '',
token_cached: 'some_token_when_already_logged',
};
const mockRequestService = new MockRequestService(function() {
// No return expected
});
const client = new LeanoteClient(inputStub, mockRequestService);
await client.createDocument({
title: 'some_title',
content: 'some_content',
repositoryId: 'some_repo',
});
const expectUrlGenerated = `${inputStub.leanote_host}/api/note/addNote?token=${inputStub.token_cached}`;
const calledUrl = mockRequestService.mock.request.mock.calls[0][0];
const callRequest = mockRequestService.mock.request.mock.calls[0][1];
expect(calledUrl).toEqual(expectUrlGenerated);
expect(Object.keys(callRequest.data).length).toBeGreaterThan(0);
expect(callRequest.data.constructor).toEqual(FormData);
expect(callRequest.requestType).toEqual('form');
expect(callRequest.method).toEqual('post');
});
});
================================================
FILE: src/common/backend/clients/leanote/client.ts
================================================
import { IExtendRequestHelper, IRequestService } from '@/service/common/request';
import { CreateDocumentRequest } from '../../index';
import { RequestHelper } from '@/service/request/common/request';
import showdown from 'showdown';
import {
LeanoteBackendServiceConfig,
LeanoteCreateDocumentResponse,
LeanoteNotebook,
LeanoteResponse,
} from './interface';
const FormData = require('form-data');
const converter = new showdown.Converter();
/**
* Client for self hosted leanote or leanote.com
*/
export default class LeanoteClient {
private config: LeanoteBackendServiceConfig;
private request: IExtendRequestHelper;
private formData: FormData;
private imagesCount: number;
/**
* This class wrap a IExtendRequestHelper to perform HTTP request
*/
constructor(
{ leanote_host, email, pwd, token_cached }: LeanoteBackendServiceConfig,
request: IRequestService
) {
this.config = { leanote_host, email, pwd, token_cached };
this.formData = new FormData();
this.imagesCount = 0;
this.request = new RequestHelper({
baseURL: this.config.leanote_host,
request: request,
});
}
/**
* @TODO: Support markdown
*
* Perform a POST with document request as formData to leanote server to create a note
*
* @see documentation https://github.com/leanote/leanote/wiki/leanote-api
*/
createDocument = async (info: CreateDocumentRequest): Promise<LeanoteCreateDocumentResponse> => {
this.formData.append('NotebookId', info.repositoryId);
this.formData.append('Title', info.title);
this.formData.append('Content', converter.makeHtml(info.content));
const formData = this.formData;
this.formData = new FormData();
this.imagesCount = 0;
return this.request.postForm<LeanoteCreateDocumentResponse>(
`/api/note/addNote?token=${this.config.token_cached}`,
{
data: formData,
}
);
};
/**
* Append blob accordingly in the formData and guess computed image url
*
* @see documentation https://github.com/leanote/leanote/wiki/leanote-api
*/
uploadBlob = async (blob: Blob): Promise<string> => {
const ext = blob.type.split('/').pop();
const filename = `${this.imagesCount}.${ext}`;
const localFileId = `${this.imagesCount}`;
this.formData.append(`Files[${localFileId}][LocalFileId]`, localFileId);
this.formData.append(`Files[${localFileId}][Title]`, filename);
this.formData.append(`Files[${localFileId}][Type]`, blob.type);
this.formData.append(`Files[${localFileId}][HasBody]`, 'true');
this.imagesCount++;
this.formData.append(`FileDatas[${localFileId}]`, blob, filename);
return `${this.config.leanote_host}/api/file/getImage?fileId=${localFileId}`;
};
/**
* Perform a GET in login api
*
* @see documentation https://github.com/leanote/leanote/wiki/leanote-api
*/
login = async () => {
if (!this.config.email || !this.config.pwd || this.config.email === '') {
throw new Error('Cannot login');
}
/**
* change: get method=>postForm method
* Remark :
* The username and password fields need to be placed in the request body
* 用户名和密码的字段需要放在请求体
*/
let formData = new FormData();
formData.append('email', this.config.email);
formData.append('pwd', this.config.pwd);
const data = await this.request.postForm<LeanoteResponse>(`/api/auth/login`, {
data: formData,
});
this.config.token_cached = data.Token;
return data.Token;
};
/**
* Perform a GET in getSyncNotebooks api to find notebooks
* Get notebooks which need be synced
* need Params: afterUsn(int, the usn bigger than it is need be synced)
* maxEntry(int) Number returned by getSyncNotebooks api
* leanote:
* afterUsn : Default value = 0
* maxEntry : if maxEntry==0;maxEntry=100
* @see documentation https://github.com/leanote/leanote/wiki/leanote-api
*/
getSyncNotebooks = async () => {
return this.request.get<LeanoteNotebook[]>(
`/api/notebook/getSyncNotebooks?token=${this.config.token_cached}`
);
};
/**
* Perform a GET in getNotebooks api to find all notebooks
*
* @see documentation https://github.com/leanote/leanote/wiki/leanote-api
*/
getNotebooks = async () => {
return this.request.get<LeanoteNotebook[]>(
`/api/notebook/getNotebooks?token=${this.config.token_cached}`
);
};
}
================================================
FILE: src/common/backend/clients/leanote/interface.ts
================================================
export interface LeanoteBackendServiceConfig {
leanote_host: string;
email: string;
pwd: string;
token_cached: string;
}
export interface LeanoteResponse {
Ok: string;
Msg: string;
Token: string;
}
export interface LeanoteCreateDocumentResponse {
NoteId: string;
}
export interface LeanoteNotebook {
NotebookId: string;
Title: string;
}
export interface LeanoteNote {
NotebookId: string;
Title: string;
Content: string;
}
================================================
FILE: src/common/backend/clients/siyuan/client.ts
================================================
import { CreateDocumentRequest } from './../../services/interface';
import { IExtendRequestHelper } from '@/service/common/request';
import { RequestHelper } from '@/service/request/common/request';
import {
ISiyuanClientOptions,
ISiyuanUploadImageResponse,
ISiyuanFetchNotesResponse,
} from './types';
const SIYUAN_BASE_URL = 'http://127.0.0.1:6806/';
export class SiYuanClient {
private options: ISiyuanClientOptions;
private request: IExtendRequestHelper;
constructor(options: ISiyuanClientOptions) {
this.options = options;
const headers: Record<string, string> = {};
if (options.accessToken) {
headers.Authorization = `Token ${options.accessToken}`;
}
this.request = new RequestHelper({
baseURL: SIYUAN_BASE_URL,
headers: headers,
request: this.options.request,
});
}
listNotebooks = async (): Promise<{ id: string; name: string }[]> => {
const res = await this.request.post<ISiyuanFetchNotesResponse>(`api/notebook/lsNotebooks`, {
data: {},
});
return (res.data.notebooks ?? res.data.files ?? [])
.map(p => {
if (typeof p === 'object') {
return p;
}
return {
name: p.split('/')[p.split('/').length - 1],
id: p.split('/')[p.split('/').length - 1],
closed: false,
};
})
.filter(e => {
return !e.closed;
});
};
createNote = async (data: CreateDocumentRequest) => {
const response = await this.request.post<{ code: number; msg: string; data: string }>(
`api/filetree/createDocWithMd`,
{
data: {
notebook: data.repositoryId,
path: `${data.title}.sy`,
markdown: data.content.replaceAll(SIYUAN_BASE_URL, ''),
},
}
);
if (response.code !== 0) {
throw new Error(response.msg);
}
return response.data;
};
uploadImage = async (blob: Blob) => {
let formData = new FormData();
formData.append('assetsDirPath', '/assets/');
const fileName = `${Date.now()}.png`;
formData.append('file[]', new File([blob], fileName));
const response = await this.request.postForm<ISiyuanUploadImageResponse>(`api/asset/upload`, {
data: formData,
});
return `${SIYUAN_BASE_URL}${response.data.succMap[fileName]}`;
};
}
================================================
FILE: src/common/backend/clients/siyuan/types.ts
================================================
import { IRequestService } from '@/service/common/request';
export interface ISiyuanClientOptions {
request: IRequestService;
accessToken?: string;
}
export interface ISiyuanUploadImageResponse {
data: {
succMap: {
[key: string]: string;
};
};
}
export interface ISiyuanFetchNotesResponse {
data: {
files?: string[] | { name: string; id: string; closed?: boolean }[];
notebooks?: string[] | { name: string; id: string; closed?: boolean }[];
};
}
================================================
FILE: src/common/backend/imageHosting/baklib/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.baklib.name',
defaultMessage: 'Baklib',
}),
icon: 'baklib',
type: 'baklib',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.baklib.builtInRemark',
defaultMessage: 'Baklib built in image hosting service.',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/baklib/service.ts
================================================
import { Base64ImageToBlob, BlobToBase64 } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import md5 from '@web-clipper/shared/lib/md5';
import { extend, RequestMethod } from 'umi-request';
import { BaklibBackendServiceConfig } from '../../services/baklib/interface';
import { Repository } from '../../services/interface';
export interface YuqueImageHostingOption {
access_token: string;
}
export default class YuqueImageHostingService implements ImageHostingService {
private accessToken: string;
private request: RequestMethod;
private context?: { currentRepository: Repository };
constructor({ accessToken }: BaklibBackendServiceConfig) {
this.accessToken = accessToken;
this.request = extend({
prefix: 'https://www.baklib-free.com/api/',
headers: { Authorization: `Bearer ${accessToken}` },
timeout: 5000,
});
this.request.interceptors.response.use(
async response => {
const json = await response.clone().json();
if (json.code !== 0) {
throw new Error(json.message || json.error);
}
return response;
},
{ global: false }
);
}
getId() {
return md5(this.accessToken);
}
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
const res = await extend({}).get(url, {
responseType: 'blob',
});
let blob: Blob = res;
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.uploadBlob(blob);
};
updateContext = (context: { currentRepository: Repository }) => {
this.context = context;
};
private uploadBlob = async (blob: Blob): Promise<string> => {
if (!this.context?.currentRepository.id) {
throw new Error('请选择站点');
}
console.log('this.context?.currentRepository.id', this.context?.currentRepository.id);
let formData = new FormData();
formData.append('base64', await BlobToBase64(blob));
formData.append('tenant_id', this.context?.currentRepository.id);
const result = await this.request.post(`v1/image/upload`, {
data: formData,
requestType: 'form',
});
return result.message.url;
};
}
================================================
FILE: src/common/backend/imageHosting/github/form.tsx
================================================
import React, { Fragment } from 'react';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import { Input, Select, Tooltip } from 'antd';
import { Form } from '@ant-design/compatible';
import { FormattedMessage } from 'react-intl';
import locale from '@/common/locales';
import IconFont from '@/components/IconFont';
import { GithubClient } from '../../clients/github/client';
import { IBasicRequestService } from '@/service/common/request';
import Container from 'typedi';
import {
IBranch,
IGetGithubRepositoryOptions,
IRepository,
IListBranchesOptions,
} from '../../clients/github/types';
import { useFetch } from '@shihengtech/hooks';
import { GithubImageHostingOption } from './type';
interface Props extends FormComponentProps {
info: GithubImageHostingOption;
}
interface IRepositoryState {
init: boolean;
repos: IRepository[];
}
async function fetchAllRepos(accessToken?: string): Promise<IRepositoryState> {
if (!accessToken) {
return {
init: false,
repos: [],
};
}
const githubClient = new GithubClient({
token: accessToken,
request: Container.get(IBasicRequestService),
});
const repos = await githubClient.queryAll<IGetGithubRepositoryOptions, IRepository>(
{ visibility: 'all' },
githubClient.getRepos
);
return {
init: true,
repos: repos,
};
}
interface IBranchState {
init: boolean;
branches: IBranch[];
default_branch?: string;
}
interface IFetchBranchesOptions {
accessToken?: string;
currentRepo?: string;
repositoryState: IRepositoryState;
}
async function fetchBranches(options: IFetchBranchesOptions): Promise<IBranchState> {
if (!options.accessToken || !options.repositoryState.init || !options.currentRepo) {
return {
init: false,
branches: [],
};
}
const currentRepository = options.repositoryState.repos?.filter(
o => o.full_name === options.currentRepo
)[0];
const githubClient = new GithubClient({
token: options.accessToken,
request: Container.get(IBasicRequestService),
});
const branches = await githubClient.queryAll<IListBranchesOptions, IBranch>(
{
owner: options.currentRepo.split('/')[0],
repo: options.currentRepo.split('/')[1],
protected: false,
},
githubClient.listBranch
);
return {
init: true,
branches,
default_branch: currentRepository.default_branch,
};
}
export default ({ form: { getFieldDecorator }, info, form }: Props) => {
const initInfo: Partial<Props['info']> = info || {};
const accessToken = form.getFieldValue('accessToken');
const { data: reposResult, loading } = useFetch(() => fetchAllRepos(accessToken), [accessToken], {
auto: true,
initialState: {
data: { init: false, repos: [] },
},
onError: () => {
return {
data: { init: false, repos: [] },
};
},
});
const currentRepo = form.getFieldValue('repo');
const { data: branchResponse, loading: branchLoading } = useFetch(
() =>
fetchBranches({
accessToken,
currentRepo,
repositoryState: reposResult!,
}),
[accessToken, currentRepo, reposResult],
{
onError: () => {
return {
data: {
init: false,
branches: [],
},
};
},
auto: true,
initialState: {
data: {
init: false,
branches: [],
},
},
}
);
return (
<Fragment>
<Form.Item label={<FormattedMessage id="backend.imageHosting.github.form.accessToken" />}>
{getFieldDecorator('accessToken', {
initialValue: initInfo.accessToken,
rules: [
{
required: true,
message: (
<FormattedMessage id="backend.imageHosting.github.form.accessToken.errorMessage" />
),
},
],
})(
<Input
onChange={() => form.setFields({ repo: null, branch: null })}
suffix={
<Tooltip
title={
<span
style={{
whiteSpace: 'nowrap',
}}
>
{locale.format({
id: 'backend.imageHosting.github.form.generateNewToken',
})}
</span>
}
>
<a
href={GithubClient.generateNewTokenUrl}
target={GithubClient.generateNewTokenUrl}
>
<IconFont type="key" />
</a>
</Tooltip>
}
/>
)}
</Form.Item>
<Form.Item label={<FormattedMessage id="backend.imageHosting.github.form.repo" />}>
{getFieldDecorator('repo', {
initialValue: initInfo.repo,
rules: [
{
required: true,
message: <FormattedMessage id="backend.imageHosting.github.form.repo.errorMessage" />,
},
],
})(
<Select
showSearch
optionFilterProp="label"
onChange={() => form.setFields({ branch: null })}
disabled={loading || !reposResult?.init}
loading={loading}
options={reposResult?.repos?.map(o => {
return {
value: o.full_name,
key: o.full_name,
label: o.full_name,
};
})}
/>
)}
</Form.Item>
<Form.Item
label={
<FormattedMessage defaultMessage="Branch" id="backend.imageHosting.github.form.branch" />
}
>
{getFieldDecorator('branch', {
initialValue: initInfo.branch,
})(
<Select
disabled={loading || !reposResult?.init || !branchResponse?.init}
placeholder={branchResponse?.default_branch}
loading={branchLoading}
options={branchResponse?.branches?.map((o: IBranch) => {
return {
value: o.name,
key: o.name,
};
})}
/>
)}
</Form.Item>
<Form.Item
label={
<FormattedMessage
id="backend.imageHosting.github.form.savePath"
defaultMessage="Save Path"
/>
}
>
{getFieldDecorator('savePath', {
initialValue: initInfo.savePath,
rules: [
{
required: false,
},
],
})(<Input />)}
</Form.Item>
</Fragment>
);
};
================================================
FILE: src/common/backend/imageHosting/github/index.ts
================================================
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
import Form from './form';
export default (): ImageHostingServiceMeta => {
return {
name: 'Github',
icon: 'github',
type: 'github',
form: Form,
service: Service,
permission: {
origins: ['https://api.github.com/*'],
},
};
};
================================================
FILE: src/common/backend/imageHosting/github/service.ts
================================================
import { generateUuid } from '@web-clipper/shared/lib/uuid';
import { BlobToBase64 } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import { isUndefined } from 'lodash';
import { GithubClient } from '../../clients/github/client';
import Container from 'typedi';
import { IBasicRequestService } from '@/service/common/request';
import { GithubImageHostingOption } from './type';
import localeService from '@/common/locales';
export default class GithubImageHostingService implements ImageHostingService {
private config: GithubImageHostingOption;
private date: Date;
private githubClient: GithubClient;
constructor(config: GithubImageHostingOption) {
this.config = config;
this.date = new Date();
this.githubClient = new GithubClient({
token: this.config.accessToken,
request: Container.get(IBasicRequestService),
});
}
getId() {
return this.config.accessToken;
}
uploadImage = async ({ data }: UploadImageRequest) => {
return this.uploadAsBase64(data);
};
uploadImageUrl = async (url: string) => {
const imageBlob = await Container.get(IBasicRequestService).download(url);
const imageBase64 = await BlobToBase64(imageBlob);
return this.uploadAsBase64(imageBase64);
};
private generateFilename = (data: string): string => {
const matchedSuffix: any = data.match(/^data:image\/(.*);base64,/);
const suffix: string = matchedSuffix[1];
return `${generateUuid()}\.${suffix}`;
};
private uploadAsBase64 = async (data: string): Promise<string> => {
if (!this.config.repo) {
throw new Error(
localeService.format({
id: 'backend.imageHosting.github.repo.errorMessage',
defaultMessage: 'Please config the github imageHosting again.',
})
);
}
const [username, repo] = this.config.repo.split('/');
if (isUndefined(this.config.savePath)) this.config.savePath = '';
if (this.config.savePath.startsWith('/')) this.config.savePath.substr(1);
if (!this.config.savePath.endsWith('/') && this.config.savePath.length > 0)
this.config.savePath += '/';
const fileName = this.generateFilename(data);
const folderName = this.date
.toLocaleString('chinese', { hour12: false })
.replace(new RegExp('/', 'g'), '-')
.replace(new RegExp(':', 'g'), '-');
const filteredImage = data.replace(/^data:image\/.*;base64,/, '');
const response = await this.githubClient.uploadFile({
owner: username,
repo,
branch: this.config.branch,
path: `${this.config.savePath}${folderName}/${fileName}`,
message: `Upload image "${fileName}"`,
content: filteredImage,
});
return `${response.content.html_url}?raw=true`;
};
}
================================================
FILE: src/common/backend/imageHosting/github/type.ts
================================================
export interface GithubImageHostingOption {
accessToken: string;
repo: string;
branch?: string;
savePath: string;
}
================================================
FILE: src/common/backend/imageHosting/imgur/form.tsx
================================================
import React from 'react';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { Input } from 'antd';
interface Props extends FormComponentProps {
info: {
clientId: string;
};
}
export default ({ form: { getFieldDecorator }, info }: Props) => {
const initInfo: Partial<Props['info']> = info || {};
return (
<Form.Item label="ClientId">
{getFieldDecorator('clientId', {
initialValue: initInfo.clientId,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input clientId"></Input>)}
</Form.Item>
);
};
================================================
FILE: src/common/backend/imageHosting/imgur/index.ts
================================================
import Form from './form';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: 'Imgur',
icon: 'imgur',
type: 'imgur',
service: Service,
form: Form,
permission: {
origins: ['https://api.imgur.com/*'],
},
};
};
================================================
FILE: src/common/backend/imageHosting/imgur/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { RequestHelper } from '@/service/request/common/request';
import { UploadImageRequest, ImageHostingService } from '../interface';
import { Base64ImageToBlob } from '@/common/blob';
import Container from 'typedi';
export interface ImgurImageHostingOption {
clientId: string;
}
export default class ImgurImageHostingService implements ImageHostingService {
private config: ImgurImageHostingOption;
constructor(config: ImgurImageHostingOption) {
this.config = config;
}
getId = () => {
return this.config.clientId;
};
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
return this.uploadBlob(url);
};
private uploadBlob = async (blob: Blob | string): Promise<string> => {
let formData = new FormData();
formData.append('image', blob);
const request = new RequestHelper({ request: Container.get(IBasicRequestService) });
const result = await request.postForm<{ data: { link: string } }>(
`https://api.imgur.com/3/image`,
{
data: formData,
headers: {
Authorization: `Client-ID ${this.config.clientId}`,
},
}
);
return result.data.link;
};
}
================================================
FILE: src/common/backend/imageHosting/interface.ts
================================================
import { Repository } from '../services/interface';
export interface ImageHostingServiceConstructAble {
new (info: any): ImageHostingService;
}
export interface ImageHostingService {
getId(): string;
uploadImage(request: UploadImageRequest): Promise<string>;
uploadImageUrl(url: string): Promise<string>;
updateContext?({ currentRepository }: { currentRepository: Repository }): void;
}
export interface UploadImageRequest {
data: string;
}
export interface ImageHostingServiceMeta {
name: string;
icon: string;
type: string;
service: ImageHostingServiceConstructAble;
form?: any;
support?: (type: string) => boolean;
builtIn?: boolean;
builtInRemark?: string;
permission?: chrome.permissions.Permissions;
}
export const BUILT_IN_IMAGE_HOSTING_ID = 'BUILT_IN_IMAGE_HOSTING_ID';
================================================
FILE: src/common/backend/imageHosting/joplin/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.joplin.name',
}),
icon: 'joplin',
type: 'joplin',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.joplin.builtInRemark',
defaultMessage: 'Joplin built in image hosting service.',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/joplin/service.ts
================================================
import { RequestHelper } from '@/service/request/common/request';
import { JoplinClient } from './../../clients/joplin/index';
import { IJoplinClient } from './../../clients/joplin/types';
import { IBasicRequestService } from '@/service/common/request';
import { Base64ImageToBlob } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import Container from 'typedi';
export interface JoplinImageHostingOption {
token: string;
}
export default class JoplinImageHostingService implements ImageHostingService {
private client: IJoplinClient;
private token: string;
constructor({ token }: JoplinImageHostingOption) {
this.token = token;
const request = new RequestHelper({
baseURL: 'http://localhost:41184/',
request: Container.get(IBasicRequestService),
params: {
token: token,
},
});
this.client = new JoplinClient(request);
}
getId() {
return this.token;
}
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.client.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.client.uploadBlob(blob);
};
}
================================================
FILE: src/common/backend/imageHosting/leanote/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.leanote.name',
}),
icon: 'leanote',
type: 'leanote',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.leanote.builtInRemark',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/leanote/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { Base64ImageToBlob } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import backend from '../..';
import { message } from 'antd';
import localeService from '@/common/locales';
import Container from 'typedi';
/**
* Use leanote as image hosting service by embbeding images to note body
*/
export default class LeanoteImageHostingService implements ImageHostingService {
getId = () => {
return 'leanote';
};
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.uploadBlob(blob);
};
/**
* Delegate image saving to document service
*
* @param blob
*
* @return string image url once hosted
*/
private uploadBlob = async (blob: Blob): Promise<string> => {
message.destroy();
message.warning(
localeService.format({
id: 'backend.services.leanote.warning.image.host.saving.delayed',
defaultMessage: 'Image will be attached only if the current clipping is saved',
})
);
return (backend.getDocumentService()! as any).uploadBlob(blob);
};
}
================================================
FILE: src/common/backend/imageHosting/piclist/form.tsx
================================================
import React, { Fragment } from 'react';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { Input } from 'antd';
interface Props extends FormComponentProps {
info: {
uploadUrl: string;
key: string;
};
}
export default ({ form: { getFieldDecorator }, info }: Props) => {
const initInfo: Partial<Props['info']> = info || {};
return (
<Fragment>
<Form.Item label="UploadUrl">
{getFieldDecorator('uploadUrl', {
initialValue: initInfo.uploadUrl,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input piclist upload URL"></Input>)}
</Form.Item>
<Form.Item label="Key">
{getFieldDecorator('key', {
initialValue: initInfo.key,
})(<Input placeholder="please input upload key (if needed)"></Input>)}
</Form.Item>
</Fragment>
);
};
================================================
FILE: src/common/backend/imageHosting/piclist/index.ts
================================================
import Form from './form';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: 'piclist',
icon: 'icons/piclist.png',
type: 'piclist',
service: Service,
form: Form,
permission: {
origins: ['<all_urls>'], // often to be self-hosted
},
};
};
================================================
FILE: src/common/backend/imageHosting/piclist/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { RequestHelper } from '@/service/request/common/request';
import { UploadImageRequest, ImageHostingService } from '../interface';
import { Base64ImageToBlob } from '@/common/blob';
import Container from 'typedi';
import md5 from '@web-clipper/shared/lib/md5';
export interface PiclistImageHostingOption {
uploadUrl: string;
key: string;
}
export default class PiclistImageHostingService implements ImageHostingService {
private config: PiclistImageHostingOption;
constructor(config: PiclistImageHostingOption) {
this.config = config;
}
getId = () => {
let uploadUrl = this.config.uploadUrl;
if (this.config.key) uploadUrl += `?key=${this.config.key}`;
return md5(uploadUrl); // as id
};
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob, `web_cliper_image.png`);
};
uploadImageUrl = async (url: string) => {
const imageBlob = await Container.get(IBasicRequestService).download(url);
return this.uploadBlob(imageBlob, this._getImageFileName(url));
};
private uploadBlob = async (blob: Blob, fileName?: string): Promise<string> => {
const request = new RequestHelper({ request: Container.get(IBasicRequestService) });
let uploadUrl = this.config.uploadUrl;
if (this.config.key) uploadUrl += `?key=${this.config.key}`;
let formData = new FormData();
formData.append('image', blob, fileName);
let result = await request.postForm<{ success: boolean; result: string[] }>(uploadUrl, {
data: formData,
});
if (!result.success) throw new Error('Upload failed');
return result.result[0];
};
private _getImageFileName(url: string) {
// 分割路径和查询参数
const queryIndex = url.indexOf('?');
const pathPart = queryIndex === -1 ? url : url.slice(0, queryIndex);
const queryPart = queryIndex === -1 ? '' : url.slice(queryIndex + 1);
// 处理路径部分
const segments = pathPart.split('/');
let lastSegment = segments.pop() || '';
// 移除可能的哈希片段
const hashIndex = lastSegment.indexOf('#');
if (hashIndex !== -1) {
lastSegment = lastSegment.slice(0, hashIndex);
}
// 检查最后一段是否为文件名
if (lastSegment.includes('.')) {
return lastSegment;
}
let fileName = "web_cliper_image"
let fileExt: string = "png";
// 解析查询参数中的后缀
const queryParams = new URLSearchParams(queryPart);
const formatKeys = ['wx_fmt', 'format', 'fm', 'type'];
for (const key of formatKeys) {
if (queryParams.has(key)) {
fileExt = queryParams.get(key) as string;
if (fileExt) {
break;
}
}
}
// 检查路径中的其他段是否有已知图片后缀
const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'];
for (const seg of segments) {
const dotIndex = seg.lastIndexOf('.');
if (dotIndex !== -1) {
const ext = seg.slice(dotIndex + 1).toLowerCase();
if (imageExts.includes(ext)) {
fileExt = ext;
break;
}
}
}
// 默认返回空字符串
return `${fileName}.${fileExt}`;
}
}
================================================
FILE: src/common/backend/imageHosting/qcloud/form.tsx
================================================
import React, { Fragment } from 'react';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { Input, InputNumber, Checkbox } from 'antd';
import { QcloudCosImageHostingOption } from './service';
interface Props extends FormComponentProps {
info: QcloudCosImageHostingOption;
}
export default ({ form: { getFieldDecorator }, info }: Props) => {
const initInfo: Partial<Props['info']> = info || {};
return (
<Fragment>
<Form.Item label="Bucket">
{getFieldDecorator('bucket', {
initialValue: initInfo.bucket,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input Bucket"></Input>)}
</Form.Item>
<Form.Item label="Region">
{getFieldDecorator('region', {
initialValue: initInfo.region,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input Region"></Input>)}
</Form.Item>
<Form.Item label="Folder">
{getFieldDecorator('folder', {
initialValue: initInfo.folder,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input Folder"></Input>)}
</Form.Item>
<Form.Item label="SecretId">
{getFieldDecorator('secretId', {
initialValue: initInfo.secretId,
rules: [
{
required: true,
},
],
})(<Input placeholder="please input SecretId"></Input>)}
</Form.Item>
<Form.Item label="SecretKey">
{getFieldDecorator('secretKey', {
initialValue: initInfo.secretKey,
rules: [
{
required: true,
},
],
})(<Input.Password placeholder="please input SecretKey" min={0}></Input.Password>)}
</Form.Item>
<Form.Item label="PrivateRead">
{getFieldDecorator('privateRead', {
initialValue: initInfo.privateRead,
valuePropName: 'checked',
rules: [
{
required: false,
},
],
})(<Checkbox />)}
</Form.Item>
<Form.Item label="Expires">
{getFieldDecorator('expires', {
initialValue: initInfo.expires,
rules: [
{
required: true,
},
],
})(<InputNumber placeholder="please input Expires"></InputNumber>)}
</Form.Item>
</Fragment>
);
};
================================================
FILE: src/common/backend/imageHosting/qcloud/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
import form from './form';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.services.qcloud.name',
}),
icon: 'qcloud',
type: 'qcloud',
form,
service: Service,
permission: {
origins: ['https://*.myqcloud.com/*'],
},
};
};
================================================
FILE: src/common/backend/imageHosting/qcloud/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { Base64ImageToBlob } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import { generateUuid } from '@web-clipper/shared/lib/uuid';
import Container from 'typedi';
import { isUndefined } from 'lodash';
import COS from 'cos-js-sdk-v5';
export interface QcloudCosImageHostingOption {
bucket: string;
region: string;
folder: string;
secretId: string;
secretKey: string;
privateRead: boolean;
expires: number;
}
export default class QcloudCosImageHostingService implements ImageHostingService {
private config: QcloudCosImageHostingOption;
constructor(config: QcloudCosImageHostingOption) {
this.config = config;
}
getId = () => {
return this.config.bucket;
};
uploadImage = async ({ data }: UploadImageRequest) => {
console.log('uploading...');
const blob = Base64ImageToBlob(data);
return this.uploadAsBlob(blob);
};
uploadImageUrl = async (url: string) => {
const imageBlob = await Container.get(IBasicRequestService).download(url);
return this.uploadAsBlob(imageBlob);
};
private generateFilename = (blob: Blob): string => {
const matchedSuffix: any = blob.type.match(/^image\/(.*)/);
const suffix: string = matchedSuffix[1];
return `${generateUuid()}.${suffix}`;
};
private uploadAsBlob = async (blob: Blob): Promise<string> => {
if (isUndefined(this.config.folder)) this.config.folder = '';
if (this.config.folder.startsWith('/')) this.config.folder.substr(1);
if (!this.config.folder.endsWith('/') && this.config.folder.length > 0)
this.config.folder += '/';
const fileName = this.generateFilename(blob);
const date = new Date();
const folderName = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(
2,
'0'
)}${String(date.getDate()).padStart(2, '0')}`;
let cos = new COS({
SecretId: this.config.secretId,
SecretKey: this.config.secretKey,
});
await cos.putObject({
Bucket: this.config.bucket,
Region: this.config.region,
Key: `${this.config.folder}${folderName}/${fileName}`,
Body: blob,
});
return cos.getObjectUrl(
{
Bucket: this.config.bucket,
Region: this.config.region,
Key: `${this.config.folder}${folderName}/${fileName}`,
Sign: this.config.privateRead,
Expires: this.config.expires,
},
(err, data) => {
if (err) throw err;
return data.Url;
}
);
};
}
================================================
FILE: src/common/backend/imageHosting/siyuan/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.siyuan.name',
}),
icon: 'siyuan',
type: 'siyuan',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.siyuan.builtInRemark',
defaultMessage: 'Siyuan Note built in image hosting service.',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/siyuan/service.ts
================================================
import { Base64ImageToBlob } from '@/common/blob';
import { Container } from 'typedi';
import { IBasicRequestService } from './../../../../service/common/request';
import { SiYuanClient } from './../../clients/siyuan/client';
import { UploadImageRequest, ImageHostingService } from '../interface';
import { Repository } from '../../services/interface';
export interface YuqueImageHostingOption {
access_token: string;
}
export default class SiYuanImageHostingService implements ImageHostingService {
private context: { currentRepository: Repository } | null = null;
private siyuan: SiYuanClient;
constructor(config: { accessToken?: string }) {
this.siyuan = new SiYuanClient({
request: Container.get(IBasicRequestService),
accessToken: config.accessToken,
});
}
getId() {
return 'siyuan';
}
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.siyuan.uploadImage(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.siyuan.uploadImage(blob);
};
updateContext = (context: { currentRepository: Repository }) => {
this.context = context;
};
}
================================================
FILE: src/common/backend/imageHosting/sm.ms/form.tsx
================================================
import React from 'react';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { Input } from 'antd';
interface Props extends FormComponentProps {
info: {
secretToken: string;
};
}
export default ({ form: { getFieldDecorator }, info }: Props) => {
const initInfo: Partial<Props['info']> = info || {};
return (
<Form.Item label="Secret Token">
{getFieldDecorator('secretToken', {
initialValue: initInfo.secretToken,
})(<Input placeholder="please input Secret Token"></Input>)}
</Form.Item>
);
};
================================================
FILE: src/common/backend/imageHosting/sm.ms/index.ts
================================================
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
import form from './form';
export default (): ImageHostingServiceMeta => {
return {
name: 'sm.ms',
icon: 'smms',
type: 'sm.ms',
form,
service: Service,
permission: {
origins: ['https://sm.ms/*'],
},
};
};
================================================
FILE: src/common/backend/imageHosting/sm.ms/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { Base64ImageToBlob } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import md5 from '@web-clipper/shared/lib/md5';
import Container from 'typedi';
import { RequestHelper } from '@/service/request/common/request';
export interface YuqueImageHostingOption {
host: string;
}
export default class YuqueImageHostingService implements ImageHostingService {
private secretToken?: string;
constructor(info: { secretToken?: string }) {
this.secretToken = info.secretToken;
}
getId = () => {
return md5(this.secretToken ?? 'sm.ms');
};
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.uploadBlob(blob);
};
private uploadBlob = async (blob: Blob): Promise<string> => {
let formData = new FormData();
formData.append('smfile', blob);
formData.append('ssl', 'true');
let headers: { Authorization?: string } = {};
if (this.secretToken) {
headers.Authorization = this.secretToken;
}
const request = new RequestHelper({
request: Container.get(IBasicRequestService),
headers: headers,
});
const result = await request.postForm<
{ data: { url: string } } | { code: string; success: false; images: string; message: string }
>(`https://sm.ms/api/v2/upload`, { data: formData });
if (isFail(result)) {
if (result.code !== 'image_repeated') {
throw new Error(result.message);
}
return result.images;
}
return result.data.url;
};
}
function isFail(
rs: { data: { url: string } } | { code: string; success: false; images: string }
): rs is { code: string; success: false; images: string } {
if (!(rs as { code: string; success: false; images: string }).success) {
return true;
}
return false;
}
================================================
FILE: src/common/backend/imageHosting/wiznote/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.wiznote.name',
}),
icon: 'wiznote',
type: 'WizNote',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.wiznote.builtInRemark',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/wiznote/service.ts
================================================
import { UploadImageRequest, ImageHostingService } from '../interface';
import { Base64ImageToBlob } from 'common/blob';
import Container from 'typedi';
import { IBasicRequestService } from '@/service/common/request';
import backend from 'common/backend';
import WizNoteDocumentService from 'common/backend/services/wiznote/service';
export interface WizImageHostingOption {
token: string;
}
export default class WizNoteImageHostingService implements ImageHostingService {
getId() {
return 'wiznote';
}
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.uploadBlob(blob);
};
private uploadBlob = async (blob: Blob): Promise<string> => {
return (backend.getDocumentService()! as WizNoteDocumentService).uploadBlob(blob);
};
}
================================================
FILE: src/common/backend/imageHosting/yuque_oauth/index.ts
================================================
import localeService from '@/common/locales';
import { ImageHostingServiceMeta } from '../interface';
import Service from './service';
export default (): ImageHostingServiceMeta => {
return {
name: localeService.format({
id: 'backend.imageHosting.yuque_oauth.name',
}),
icon: 'yuque',
type: 'yuque_oauth',
service: Service,
builtIn: true,
builtInRemark: localeService.format({
id: 'backend.imageHosting.yuque_oauth.builtInRemark',
}),
};
};
================================================
FILE: src/common/backend/imageHosting/yuque_oauth/service.ts
================================================
import { IBasicRequestService } from '@/service/common/request';
import { Base64ImageToBlob } from '@/common/blob';
import { UploadImageRequest, ImageHostingService } from '../interface';
import md5 from '@web-clipper/shared/lib/md5';
import { extend } from 'umi-request';
import localeService from '@/common/locales';
import Container from 'typedi';
const request = extend({});
request.interceptors.response.use(
response => {
const codeMaps: {
[code: number]: string;
} = {
429: localeService.format({
id: 'backend.imageHosting.yuque_oauth.error_429',
}),
401: localeService.format({
id: 'backend.imageHosting.yuque_oauth.error_401',
}),
403: localeService.format({
id: 'backend.imageHosting.yuque_oauth.error_403',
}),
};
if (codeMaps[response.status]) {
throw new Error(codeMaps[response.status]);
}
return response;
},
{ global: false }
);
export interface YuqueImageHostingOption {
access_token: string;
}
const HOST = 'https://www.yuque.com';
const BASE_URL = `${HOST}/api/v2/`;
export default class YuqueImageHostingService implements ImageHostingService {
private accessToken: string;
constructor({ access_token }: YuqueImageHostingOption) {
this.accessToken = access_token;
}
getId() {
return md5(this.accessToken);
}
uploadImage = async ({ data }: UploadImageRequest) => {
const blob = Base64ImageToBlob(data);
return this.uploadBlob(blob);
};
uploadImageUrl = async (url: string) => {
let blob: Blob = await Container.get(IBasicRequestService).download(url);
if (blob.type === 'image/webp') {
blob = blob.slice(0, blob.size, 'image/jpeg');
}
return this.uploadBlob(blob);
};
private uploadBlob = async (blob: Blob): Promise<string> => {
let formData = new FormData();
formData.append('file', blob, 'file.png');
const result = await request.post(`${BASE_URL}upload/attach`, {
data: formData,
requestType: 'form',
headers: {
'X-Auth-Token': this.accessToken,
},
});
return result.data.url;
};
}
================================================
FILE: src/common/backend/index.ts
================================================
import {
ServiceMeta,
ImageHostingServiceMeta,
ImageHostingService,
DocumentService,
} from './interface';
export * from './interface';
const serviceContext = require.context('./services', true, /index.ts$/);
const getServices = (): ServiceMeta[] => {
return serviceContext.keys().map(key => {
return serviceContext(key).default() as ServiceMeta;
});
};
const imageHostingContext = require.context('./imageHosting', true, /index.ts$/);
const getImageHostingServices = (): ImageHostingServiceMeta[] => {
return imageHostingContext.keys().map(key => {
return imageHostingContext(key).default() as ImageHostingServiceMeta;
});
};
export function documentServiceFactory(type: string, info?: any) {
const service = getServices().find(o => o.type === type);
if (service) {
const Service = service.service;
return new Service(info);
}
throw new Error('unSupport type');
}
export function imageHostingServiceFactory(type: string, info?: any) {
const service = getImageHostingServices().find(o => o.type === type);
if (service) {
const Service = service.service;
return new Service(info);
}
throw new Error('un support image hosting type');
}
export { getServices, getImageHostingServices };
export class BackendContext {
private documentService?: DocumentService;
private imageHostingService?: ImageHostingService;
setDocumentService(documentService: DocumentService) {
this.documentService = documentService;
}
getDocumentService() {
return this.documentService;
}
setImageHostingService(imageHostingService: ImageHostingService) {
this.imageHostingService = imageHostingService;
}
getImageHostingService() {
return this.imageHostingService;
}
}
export default new BackendContext();
================================================
FILE: src/common/backend/interface.ts
================================================
export * from './imageHosting/interface';
export * from './services/interface';
================================================
FILE: src/common/backend/services/baklib/complete.tsx
================================================
import React from 'react';
export default ({ status: { edit_url } }: any) => {
return (
<div style={{ marginTop: 8 }}>
<a className="ant-btn-link" type="link" href={edit_url} target="_blank">
编辑
</a>
</div>
);
};
================================================
FILE: src/common/backend/services/baklib/form.tsx
================================================
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { Input } from 'antd';
import { FormComponentProps } from '@ant-design/compatible/es/form';
import React, { Fragment } from 'react';
import { BaklibBackendServiceConfig } from './interface';
import useOriginForm from '@/hooks/useOriginForm';
import { FormattedMessage } from 'react-intl';
interface BaklibFormProps {
verified?: boolean;
info?: BaklibBackendServiceConfig;
}
const FormItem: React.FC<BaklibFormProps & FormComponentProps> = props => {
const {
form,
form: { getFieldDecorator },
info,
verified,
} = props;
const { verified: formVerified, handleAuthentication, formRules } = useOriginForm({
form,
initStatus: !!info,
});
let initData: Partial<BaklibBackendServiceConfig> = {};
if (info) {
initData = info;
}
let editMode = info ? true : false;
return (
<Fragment>
<Form.Item label="Host">
{getFieldDecorator('origin', {
initialValue: initData.origin || 'https://www.baklib.com',
rules: [
{
required: true,
message: 'Host is required!',
},
...formRules,
],
})(
<Input.Search
enterButton={
<FormattedMessage
id="backend.services.baklib.form.authentication"
defaultMessage="Authentication"
/>
}
disabled={editMode || formVerified}
onSearch={handleAuthentication}
/>
)}
</Form.Item>
<Form.Item label="AccessToken">
{getFieldDecorator('accessToken', {
initialValue: initData.accessToken,
rules: [
{
required: true,
message: 'AccessToken is required!',
},
],
})(<Input disabled={editMode || verified || !formVerified} />)}
</Form.Item>
</Fragment>
);
};
export default FormItem;
================================================
FILE: src/common/backend/services/baklib/headerForm.tsx
================================================
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.less';
import { TreeSelect } from 'antd';
import { FormComponentProps } from '@ant-design/compatible/lib/form';
import React, { Fragment, useEffect } from 'react';
import locale from '@/common/locales';
import { Repository } from '../interface';
import { useFetch } from '@shihengtech/hooks';
import backend from '../..';
import BaklibDocumentService from './service';
const HeaderForm: React.FC<FormComponentProps & { currentRepository: Repository }> = ({
form: { getFieldDecorator, setFieldsValue, getFieldValue },
currentRepository,
}) => {
const service = backend.getDocumentService() as BaklibDocumentService;
const channals = useFetch(() => {
if (currentRepository) {
return service.getTentChannel(currentRepository.id);
}
return [];
}, [currentRepository]);
useEffect(() => {
setFieldsValue({
channel: null,
});
}, [currentRepository, setFieldsValue]);
useEffect(() => {
if (Array.isArray(channals.data) && channals.data.length > 0 && !getFieldValue('channel')) {
setFieldsValue({
channel: channals.data[0].value,
});
}
}, [channals.data, getFieldValue, setFieldsValue]);
return (
<Fragment>
<Form.Item>
{getFieldDecorator('channel', {
rules: [],
})(
<TreeSelect
disabled={channals.loading}
loading={channals.loading}
allowClear
treeData={channals.data}
style={{ width: '100%' }}
placeholder={locale.format({
id: 'backend.services.baklib.headerForm.channel',
defaultMessage: 'Channel',
})}
/>
)}
</Form.Item>
</Fragment>
);
};
export default HeaderForm;
================================================
FILE: src/common/backend/services/baklib/index.ts
================================================
import { ServiceMeta } from './../interface';
import Service from './service';
import Form from './form';
import localeService from '@/common/locales';
import headerForm from './headerForm';
import complete from './complete';
export default (): ServiceMeta => {
return {
name: localeService.format({
id: 'backend.services.baklib.name',
}),
complete,
icon: 'baklib',
type: 'baklib',
service: Service,
form: Form,
headerForm,
homePage: 'https://www.baklib.com/',
};
};
================================================
FILE: src/common/backend/services/baklib/interface.ts
================================================
export interface BaklibBackendServiceConfig {
accessToken: string;
origin: string;
}
export interface BaklibTenantsResponse {
current_tenants: { id: string; name: string; member_role: string[] }[];
share_tenants: { id: string; name: string; member_role: string[] }[];
}
================================================
FILE: src/common/backend/services/baklib/service.ts
================================================
import { DocumentService, CreateDocumentRequest } from './../../index';
import { extend, RequestMethod } from 'umi-request';
import md5 from '@web-clipper/shared/lib/md5';
import { BaklibBackendServiceConfig, BaklibTenantsResponse } from './interface';
import { CompleteStatus, Repository } from '../interface';
interface Channel {
id: string;
name: string;
ordinal: number;
child_channels: Channel[];
}
export default class BaklibDocumentService implements DocumentService {
private request: RequestMethod;
private token: string;
private cache: Map<string, any>;
private origin: string;
constructor({ accessToken, origin }: BaklibBackendServiceConfig) {
const realHost = origin || 'https://www.baklib.com';
this.request = extend({
prefix: `${realHost}/api/`,
headers: { Authorization: `Bearer ${accessToken}` },
timeout: 5000,
});
this.request.interceptors.response.use(
async response => {
const json = await response.clone().json();
if (json.code !== 0) {
throw new Error(json.message);
}
return response;
},
{ global: false }
);
this.token = accessToken;
this.cache = new Map<string, any>();
this.origin = origin;
}
getId = () => md5(this.token);
getUserInfo = async () => {
return {
name: 'Baklib',
avatar: '',
homePage: `${this.origin}/-/groups`,
description: 'Baklib',
};
};
getRepositories = async () => {
const {
message: { current_tenants, share_tenants },
} = await this.request.get<{
message: BaklibTenantsResponse;
}>('v1/tenants');
function tenantToRepo(tenants: any, groupName: string) {
return tenants.map(
({ id, name, member_role }: any): Repository => {
const readOnly = Array.isArray(member_role) && member_role[0] === '只能阅读';
return {
id,
name: readOnly ? `${name} (只读)` : name,
disabled: readOnly,
groupId: groupName,
groupName,
};
}
);
}
return tenantToRepo(current_tenants, '我的站点').concat(
tenantToRepo(share_tenants, '共享站点')
);
};
async getTentChannel(tenant_id: string) {
if (this.cache.has(tenant_id)) {
return this.cache.get(tenant_id);
}
const response = await this.request.get<{
message: Channel[];
}>(`v1/channels?tenant_id=${tenant_id}`);
const { message } = response;
function channelToTree(tree: Channel[], parent: string): any {
tree.sort((a, b) => a.ordinal - b.ordinal);
return tree.map((o, index) => ({
title: o.name,
value: o.id,
key: `${parent}-${index}`,
children: channelToTree(o.child_channels, `${parent}-${index}`),
}));
}
this.cache.set(tenant_id, channelToTree(message, '0'));
return channelToTree(message, '0');
}
createDocument = async (
info: CreateDocumentRequest & {
channel: string;
status: number;
}
): Promise<CompleteStatus & { edit_url: string }> => {
const response = await this.request.post<{
message: {
id: string;
frontend_url: string;
edit_url: string;
};
}>('v1/articles', {
data: {
content_type: 'markdown',
tenant_id: info.repositoryId,
name: info.title,
channel_id: info.channel,
content: info.content,
status: 1,
},
});
return {
href: response.message.frontend_url,
edit_url: response.message.edit_url,
};
};
}
================================================
FILE: src/common/backend/services/bear/form.tsx
================================================
import React from 'react';
import { FormattedMessage } from 'react-intl';
export default () => (
<div style={{ textAlign: 'right' }}>
<FormattedMessage
id="backend.services.bear.form.confirm"
defaultMessage="Please confirm that the Bear client is installed."
/>
</div>
);
================================================
FILE: src/common/backend/services/bear/index.ts
================================================
import Service from './service';
import Form from './form';
export default () => {
return {
name: 'Bear',
icon: 'bear',
type: 'bear',
service: Service,
form: Form,
homePage: 'https://bear.app/',
};
};
================================================
FILE: src/common/backend/services/bear/service.ts
================================================
import { CompleteStatus } from 'common/backend/interface';
import { DocumentService, CreateDocumentRequest } from '../../index';
export default class GithubDocumentService implements DocumentService {
getId = () => {
return 'bear';
};
getUserInfo = async () => {
return {
name: 'BEAR',
avatar: '',
homePage: 'bear://x-callback-url/search',
description: 'Bear app',
};
};
getRepositories = async () => {
return [
{
id: 'bear',
name: 'Bear',
groupId: 'bear',
groupName: 'Bear',
},
];
};
createDocument = async (info: CreateDocumentRequest): Promise<CompleteStatus> => {
const url = `bear://x-callback-url/create?title=${encodeURIComponent(
info.title
)}&text=${encodeURIComponent(info.content)}&open_note=no`;
window.location.href = url;
return {
href: `bear://x-callback-url/open-note?title=${info.title}`,
};
};
}
================================================
FILE: src/common/backend/services/buildin/index.ts
================================================
import { ServiceMeta } from '@/common/backend';
import Service from './service';
export const buildinOrigin = 'https://buildin.ai';
export default (): ServiceMeta => {
return {
name: 'Buildin.AI',
icon: 'https://cdn.buildin.ai/s3-public/8ebf3bb6-08c9-40b1-93d5-6d5c5c2fe49c/logo.svg',
type: 'buildin',
homePage: 'https://buildin.ai/',
service: Service,
permission: {
origins: [`${buildinOrigin}/*`, '<all_urls>'],
permissions: ['cookies'],
},
};
};
================================================
FILE: src/common/backend/services/buildin/service.ts
================================================
import { CompleteStatus, UnauthorizedError } from '../interface';
import { DocumentService, CreateDocumentRequest } from '../../index';
import localeService from '@/common/locales';
import { extend, RequestMethod } from 'umi-request';
import { IWebRequestService, WebBlockHeader } from '@/service/common/webRequest';
import Container from 'typedi';
import { ICookieService } from '@/service/common/cookie';
import {
BuildinToc,
BuildinRepository,
BuildinSpace,
BuildinUserInfo,
Block,
OSSInfo,
TaskResult,
BuildinResponse,
ROLE_WEIGHT,
Share,
} from './type';
import { generateUuid } from '@web-clipper/shared/lib/uuid';
import { buildinOrigin } from '.';
import showdown from 'showdown';
const converter = new showdown.Converter({});
export default class BuildinDocumentService implements DocumentService {
private request: RequestMethod;
private repositories: BuildinRepository[];
private userSpaces?: BuildinSpace;
private tocPageBlocks?: Record<string, Block>;
private userInfo?: BuildinUserInfo;
private webRequestService: IWebRequestService;
private cookieService: ICookieService;
constructor() {
const request = extend({
prefix: `${buildinOrigin}/api/`,
timeout: 10000,
credentials: 'include',
});
this.request = request;
this.repositories = [];
this.webRequestService = Container.get(IWebRequestService);
this.cookieService = Container.get(ICookieService);
request.interceptors.response.use(
response => {
if (response.status === 401) {
throw new UnauthorizedError(
localeService.format({
id: 'backend.services.buildin.unauthorizedErrorMessage',
defaultMessage: 'Unauthorized! Please Login Buildin.ai Web.',
})
);
}
return response;
},
{ global: false }
);
}
getId = () => {
return 'Buildin.AI';
};
getUserInfo = async () => {
if (!this.userInfo) {
const res = await this.fetchUserInfo();
this.userInfo = res.data;
}
const { nickname, avatar, ext } = this.userInfo;
return {
name: nickname,
avatar: avatar?.startsWith('http') ? avatar : getImageCdnUrl(avatar),
homePage: 'https://buildin.ai',
description: ext?.email?.email,
};
};
getRepositories = async () => {
if (!this.userInfo) {
const res = await this.fetchUserInfo();
this.userInfo = res.data;
}
if (!this.userSpaces) {
const res = await this.getUserSpaces();
this.userSpaces = res.data;
}
const { spaceViews, spaces } = this.userSpaces;
if (!spaceViews || !spaces) {
this.repositories = [];
return [];
}
const result: BuildinRepository[] = [];
//fetch spaces
const userSpaces = Object.values(spaceViews)
.filter(spaceView => spaces[spaceView.spaceId])
.map(spaceView => spaces[spaceView.spaceId]);
if (!this.tocPageBlocks) {
const allPromise = userSpaces.map(space => {
return this.getSpaceRoot(space.uuid);
});
const allToc = await Promise.all(allPromise);
this.tocPageBlocks = allToc.reduce((pre, cur) => {
if (!cur.data.blocks) return pre;
Object.values(cur.data.blocks).forEach(b => {
//the pages which can be saved
if ([0, 18, 19].includes(b.type)) {
pre[b.uuid] = b;
}
});
return pre;
}, {} as Record<string, Block>);
userSpaces.forEach(sp => {
sp.subNodes.forEach(id => {
const block = this.tocPageBlocks?.[id];
if (!block) return;
if (block.permissions.some(o => o.type === 'illegal')) return;
if (block.permissions.length === 0) return;
const { role } = getPermission(block, this.userInfo?.uuid!, sp.permissionGroups ?? []);
if (role === 'editor' || role === 'writer') {
const spaceId = block.spaceId ?? sp.uuid;
let groupName = sp.title;
result.push({
id: block.uuid,
name: block.title || 'Untitled',
groupId: spaceId,
groupName,
});
}
});
});
}
this.repositories = result;
return result;
};
createDocument = async ({
repositoryId,
title,
content,
}: CreateDocumentRequest): Promise<CompleteStatus> => {
const repository = this.repositories.find(o => o.id === repositoryId);
if (!repository) {
throw new Error('Illegal repository');
}
const documentId = await this.createEmptyPage(repository, title);
const html = `<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body>
${converter.makeHtml(`${content}`)}
</body>
</html>`;
const ossInfo = await this.requestWithCookie<BuildinResponse<OSSInfo>>(header => {
return this.request.post(`import_temp_file?source=web-clipper`, {
headers: {
[header.name]: header.value,
},
data: {
content: html,
extName: 'html',
},
});
});
if (ossInfo.code !== 200) {
throw new Error('upload md content failed');
}
//call import api
const res = await this.requestWithCookie<BuildinResponse<{ taskId: string }>>(header => {
return this.request.post('enqueueTask', {
headers: {
[header.name]: header.value,
},
data: {
eventName: 'import',
request: {
blockId: documentId,
spaceId: repository.groupId,
importOptions: {
type: 'html',
ossName: ossInfo.data.ossName,
},
},
}
gitextract_obtgc54t/
├── .dockerignore
├── .eslintignore
├── .eslintrc.js
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report_en-US.md
│ │ ├── bug_report_zh-CN.md
│ │ ├── feature_request_en-US.md
│ │ └── feature_request_zh-CN.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .prettierrc
├── .yarnrc
├── LICENSE
├── README.md
├── bin/
│ ├── index.js
│ ├── main.ts
│ └── scripts/
│ ├── format.ts
│ └── index.ts
├── chrome/
│ ├── html/
│ │ └── error.html
│ └── js/
│ └── icon.js
├── config.json
├── global.d.ts
├── package.json
├── script/
│ ├── build.js
│ ├── release.ts
│ └── utils/
│ └── pack.ts
├── src/
│ ├── __test__/
│ │ └── utils.ts
│ ├── actions/
│ │ ├── account.ts
│ │ ├── clipper.ts
│ │ └── userPreference.ts
│ ├── common/
│ │ ├── backend/
│ │ │ ├── clients/
│ │ │ │ ├── github/
│ │ │ │ │ ├── client.test.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── joplin/
│ │ │ │ │ ├── LegacyJoplinClient.ts
│ │ │ │ │ ├── basic.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── types.ts
│ │ │ │ ├── leanote/
│ │ │ │ │ ├── client.test.ts
│ │ │ │ │ ├── client.ts
│ │ │ │ │ └── interface.ts
│ │ │ │ └── siyuan/
│ │ │ │ ├── client.ts
│ │ │ │ └── types.ts
│ │ │ ├── imageHosting/
│ │ │ │ ├── baklib/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── github/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── service.ts
│ │ │ │ │ └── type.ts
│ │ │ │ ├── imgur/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── interface.ts
│ │ │ │ ├── joplin/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── leanote/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── piclist/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── qcloud/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── siyuan/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── sm.ms/
│ │ │ │ │ ├── form.tsx
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ ├── wiznote/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── service.ts
│ │ │ │ └── yuque_oauth/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── services/
│ │ │ ├── baklib/
│ │ │ │ ├── complete.tsx
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── bear/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── buildin/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── confluence/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── dida365/
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── flomo/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── flowus/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── github/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── github_repository/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── interface.ts
│ │ │ ├── joplin/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── leanote/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── memos/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── notion/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── types.ts
│ │ │ ├── obsidian/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── onenote_oauth/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── server_chan/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── siyuan/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── ticktick/
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── ulysses/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── webdav/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── wiznote/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ ├── wolai/
│ │ │ │ ├── index.ts
│ │ │ │ ├── service.ts
│ │ │ │ └── type.ts
│ │ │ ├── youdao/
│ │ │ │ ├── index.ts
│ │ │ │ └── service.ts
│ │ │ ├── yuque/
│ │ │ │ ├── form.tsx
│ │ │ │ ├── headerForm.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── interface.ts
│ │ │ │ └── service.ts
│ │ │ └── yuque_oauth/
│ │ │ ├── form.tsx
│ │ │ ├── headerForm.tsx
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── service.ts
│ │ ├── blob.ts
│ │ ├── buffer.ts
│ │ ├── chrome/
│ │ │ └── storage.ts
│ │ ├── error.ts
│ │ ├── getResource.ts
│ │ ├── hooks/
│ │ │ ├── useFilterExtensions.ts
│ │ │ ├── useFilterImageHostingServices.ts
│ │ │ ├── useOriginPermission.ts
│ │ │ └── useVerifiedAccount.tsx
│ │ ├── loading.test.ts
│ │ ├── loading.ts
│ │ ├── locales/
│ │ │ ├── antd.ts
│ │ │ ├── data/
│ │ │ │ ├── de-DE.json
│ │ │ │ ├── de-DE.ts
│ │ │ │ ├── en-US.json
│ │ │ │ ├── en-US.ts
│ │ │ │ ├── ja-JP.json
│ │ │ │ ├── ja-JP.ts
│ │ │ │ ├── ko-KR.json
│ │ │ │ ├── ko-KR.ts
│ │ │ │ ├── ru-RU.json
│ │ │ │ ├── ru-RU.ts
│ │ │ │ ├── zh-CN.json
│ │ │ │ ├── zh-CN.ts
│ │ │ │ ├── zh-TW.json
│ │ │ │ └── zh-TW.ts
│ │ │ ├── index.test.ts
│ │ │ ├── index.ts
│ │ │ └── interface.ts
│ │ ├── matchUrl.test.ts
│ │ ├── matchUrl.ts
│ │ ├── modelTypes/
│ │ │ ├── account.ts
│ │ │ ├── clipper.ts
│ │ │ ├── extensions.ts
│ │ │ └── userPreference.ts
│ │ ├── object.ts
│ │ ├── storage/
│ │ │ ├── __test__/
│ │ │ │ └── index.spec.ts
│ │ │ ├── index.ts
│ │ │ ├── interface.ts
│ │ │ └── typedCommonStorage.ts
│ │ ├── strings.ts
│ │ ├── types.ts
│ │ └── version/
│ │ ├── index.test.ts
│ │ └── index.ts
│ ├── components/
│ │ ├── ExtensionCard/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── IconFont.tsx
│ │ ├── ImageHostingSelect.less
│ │ ├── ImageHostingSelect.tsx
│ │ ├── LinkRender/
│ │ │ └── index.tsx
│ │ ├── RepositorySelect.tsx
│ │ ├── accountItem/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── avatar/
│ │ │ └── index.tsx
│ │ ├── container/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── imageHostingSelectOption/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── imagehostingListItem/
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── section/
│ │ │ ├── __snapshots__/
│ │ │ │ └── index.test.tsx.snap
│ │ │ ├── index.less
│ │ │ └── index.tsx
│ │ ├── share/
│ │ │ └── index.tsx
│ │ └── userItem/
│ │ ├── index.less
│ │ └── index.tsx
│ ├── config.ts
│ ├── extensions/
│ │ ├── common.ts
│ │ ├── contextMenus/
│ │ │ └── saveSelection/
│ │ │ └── saveSelection.ts
│ │ ├── contextMenus.ts
│ │ ├── extensions/
│ │ │ ├── bookmark.ts
│ │ │ ├── extensions/
│ │ │ │ ├── remove.ts
│ │ │ │ ├── selectTool.ts
│ │ │ │ └── uploadImage.ts
│ │ │ ├── fullPage.ts
│ │ │ ├── qrcode.ts
│ │ │ ├── readability.ts
│ │ │ ├── screenshot.ts
│ │ │ ├── select.ts
│ │ │ └── web-clipper/
│ │ │ ├── clear.ts
│ │ │ ├── copyToClipboard.ts
│ │ │ ├── download.ts
│ │ │ ├── link.tsx
│ │ │ └── pangu.ts
│ │ └── index.ts
│ ├── hooks/
│ │ └── useOriginForm.tsx
│ ├── index.html
│ ├── main/
│ │ ├── background.worker.ts
│ │ ├── contentScript.main.ts
│ │ └── tool.main.chrome.ts
│ ├── models/
│ │ ├── account.ts
│ │ ├── clipper.tsx
│ │ └── userPreference.ts
│ ├── pages/
│ │ ├── app.less
│ │ ├── app.tsx
│ │ ├── auth.tsx
│ │ ├── complete/
│ │ │ ├── complete.less
│ │ │ └── complete.tsx
│ │ ├── locale.tsx
│ │ ├── plugin/
│ │ │ ├── Page.tsx
│ │ │ ├── TextEditor.tsx
│ │ │ └── index.less
│ │ ├── preference/
│ │ │ ├── account/
│ │ │ │ ├── index.less
│ │ │ │ ├── index.tsx
│ │ │ │ └── modal/
│ │ │ │ ├── createAccountModal.tsx
│ │ │ │ ├── editAccountModal.tsx
│ │ │ │ └── index.less
│ │ │ ├── base.tsx
│ │ │ ├── changelog/
│ │ │ │ └── index.tsx
│ │ │ ├── extensions/
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── imageHosting/
│ │ │ │ ├── form/
│ │ │ │ │ └── addImageHosting.tsx
│ │ │ │ ├── index.less
│ │ │ │ └── index.tsx
│ │ │ ├── index.less
│ │ │ ├── index.tsx
│ │ │ └── privacy/
│ │ │ └── index.tsx
│ │ └── tool/
│ │ ├── ClipExtension.tsx
│ │ ├── Header.tsx
│ │ ├── index.less
│ │ ├── index.tsx
│ │ └── toolExtensions.tsx
│ ├── service/
│ │ ├── common/
│ │ │ ├── config.ts
│ │ │ ├── configuration.ts
│ │ │ ├── contentScript.ts
│ │ │ ├── cookie.ts
│ │ │ ├── extension.ts
│ │ │ ├── ipc.ts
│ │ │ ├── locale.ts
│ │ │ ├── permissions.ts
│ │ │ ├── preference.ts
│ │ │ ├── request.ts
│ │ │ ├── storage.ts
│ │ │ ├── tab.ts
│ │ │ └── webRequest.ts
│ │ ├── config/
│ │ │ └── browser/
│ │ │ └── configService.ts
│ │ ├── configuration/
│ │ │ ├── common/
│ │ │ │ └── generate-local-config.ts
│ │ │ └── configuration.ts
│ │ ├── contentScript/
│ │ │ ├── browser/
│ │ │ │ └── contentScript/
│ │ │ │ ├── contentScript.less
│ │ │ │ └── contentScript.ts
│ │ │ └── common/
│ │ │ └── contentScriptIPC.ts
│ │ ├── cookie/
│ │ │ ├── background/
│ │ │ │ └── cookieService.ts
│ │ │ └── common/
│ │ │ └── cookieIpc.ts
│ │ ├── extension/
│ │ │ └── browser/
│ │ │ ├── extensionContainer.ts
│ │ │ └── extensionService.ts
│ │ ├── ipc/
│ │ │ └── browser/
│ │ │ ├── background-main/
│ │ │ │ └── ipcService.ts
│ │ │ ├── contentScript/
│ │ │ │ └── contentScriptIPCServer.ts
│ │ │ └── popup/
│ │ │ └── ipcClient.ts
│ │ ├── permissions/
│ │ │ ├── chrome/
│ │ │ │ └── permissionsService.ts
│ │ │ └── common/
│ │ │ └── permissionsIpc.ts
│ │ ├── preference/
│ │ │ └── browser/
│ │ │ └── preferenceService.ts
│ │ ├── request/
│ │ │ ├── common/
│ │ │ │ ├── request.test.ts
│ │ │ │ └── request.ts
│ │ │ └── tool/
│ │ │ └── basic.ts
│ │ ├── tab/
│ │ │ ├── browser/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ └── common/
│ │ │ └── tabIpc.ts
│ │ ├── webRequest/
│ │ │ ├── browser/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ ├── chrome/
│ │ │ │ └── background/
│ │ │ │ └── tabService.ts
│ │ │ └── common/
│ │ │ └── webRequestIPC.ts
│ │ └── worker/
│ │ ├── common/
│ │ │ ├── index.ts
│ │ │ └── workserServiceIPC.ts
│ │ └── worker/
│ │ └── workerService.ts
│ ├── services/
│ │ ├── account/
│ │ │ └── common.ts
│ │ ├── configuration/
│ │ │ └── common/
│ │ │ ├── configuration.ts
│ │ │ └── configurationService.ts
│ │ ├── environment/
│ │ │ └── common/
│ │ │ ├── changelog/
│ │ │ │ ├── CHANGELOG.en-US.md
│ │ │ │ └── CHANGELOG.zh-CN.md
│ │ │ ├── environment.ts
│ │ │ ├── environmentService.ts
│ │ │ └── privacy/
│ │ │ ├── PRIVACY.en-US.md
│ │ │ └── PRIVACY.zh-CN.md
│ │ └── log/
│ │ └── common/
│ │ └── index.ts
│ ├── setupTests.ts
│ └── vendor/
│ └── global.d.ts
├── tsconfig.json
├── vitest.config.ts
└── webpack/
├── plugin/
│ └── webpack-create-extension-manifest-plugin.js
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
SYMBOL INDEX (705 symbols across 198 files)
FILE: bin/scripts/format.ts
function format (line 4) | function format() {
FILE: chrome/js/icon.js
function p (line 1) | function p(){v||(v=!0,i())}
function z (line 1) | function z(){try{o.documentElement.doScroll("left")}catch(c){return void...
FILE: script/build.js
function send (line 5) | function send(data) {
FILE: script/release.ts
function build (line 39) | function build() {
FILE: script/utils/pack.ts
type IPackOptions (line 5) | interface IPackOptions {
function pack (line 11) | function pack(options: IPackOptions) {
FILE: src/__test__/utils.ts
type TMockRequestServiceHandler (line 4) | type TMockRequestServiceHandler = (url: string, options?: TRequestOption...
class MockRequestService (line 6) | class MockRequestService implements IRequestService {
method constructor (line 11) | constructor(handler: TMockRequestServiceHandler) {
method request (line 18) | request(url: string, options: TRequestOption) {
method download (line 23) | download(url: string): Promise<Blob> {
FILE: src/common/backend/clients/github/client.test.ts
function getTestFixtures (line 12) | function getTestFixtures(response?: any) {
FILE: src/common/backend/clients/github/client.ts
class GithubClient (line 20) | class GithubClient {
method constructor (line 24) | constructor(options: IGithubClientOptions) {
method generateNewTokenUrl (line 112) | static get generateNewTokenUrl() {
FILE: src/common/backend/clients/github/types.ts
type IGithubClientOptions (line 3) | interface IGithubClientOptions {
type ICreateIssueOptions (line 7) | interface ICreateIssueOptions {
type ICreateIssueResponse (line 14) | interface ICreateIssueResponse {
type IGithubUserInfoResponse (line 19) | interface IGithubUserInfoResponse {
type IUploadFileOptions (line 27) | interface IUploadFileOptions {
type IUploadFileResponse (line 36) | interface IUploadFileResponse {
type IListBranchesOptions (line 42) | interface IListBranchesOptions extends IPageQuery {
type IBranch (line 48) | interface IBranch {
type IPageQuery (line 53) | interface IPageQuery {
type TOmitPage (line 58) | type TOmitPage<T> = Omit<T, 'page' | 'per_page'>;
type TPageRequest (line 60) | type TPageRequest<O extends IPageQuery, R> = (option: O) => Promise<R[]>;
type IGetGithubRepositoryOptions (line 62) | interface IGetGithubRepositoryOptions extends IPageQuery {
type IRepository (line 68) | interface IRepository {
FILE: src/common/backend/clients/joplin/LegacyJoplinClient.ts
class LegacyJoplinClient (line 5) | class LegacyJoplinClient extends AbstractJoplinClient {
FILE: src/common/backend/clients/joplin/basic.ts
method constructor (line 6) | constructor(protected request: IExtendRequestHelper) {}
FILE: src/common/backend/clients/joplin/client.ts
class JoplinClient (line 10) | class JoplinClient extends AbstractJoplinClient {
FILE: src/common/backend/clients/joplin/types.ts
type IJoplinClient (line 6) | interface IJoplinClient {
type JoplinTag (line 13) | interface JoplinTag {
type JoplinCreateDocumentRequest (line 18) | interface JoplinCreateDocumentRequest extends CreateDocumentRequest {
type JoplinBackendServiceConfig (line 22) | interface JoplinBackendServiceConfig {
type JoplinFolderItem (line 27) | interface JoplinFolderItem {
type JoplinTag (line 33) | interface JoplinTag {
type IJoplinClient (line 38) | interface IJoplinClient {
type JoplinCreateDocumentRequest (line 44) | interface JoplinCreateDocumentRequest extends CreateDocumentRequest {
type IPageRes (line 48) | interface IPageRes<T> {
FILE: src/common/backend/clients/leanote/client.ts
class LeanoteClient (line 17) | class LeanoteClient {
method constructor (line 26) | constructor(
FILE: src/common/backend/clients/leanote/interface.ts
type LeanoteBackendServiceConfig (line 1) | interface LeanoteBackendServiceConfig {
type LeanoteResponse (line 8) | interface LeanoteResponse {
type LeanoteCreateDocumentResponse (line 14) | interface LeanoteCreateDocumentResponse {
type LeanoteNotebook (line 18) | interface LeanoteNotebook {
type LeanoteNote (line 23) | interface LeanoteNote {
FILE: src/common/backend/clients/siyuan/client.ts
constant SIYUAN_BASE_URL (line 10) | const SIYUAN_BASE_URL = 'http://127.0.0.1:6806/';
class SiYuanClient (line 12) | class SiYuanClient {
method constructor (line 16) | constructor(options: ISiyuanClientOptions) {
FILE: src/common/backend/clients/siyuan/types.ts
type ISiyuanClientOptions (line 3) | interface ISiyuanClientOptions {
type ISiyuanUploadImageResponse (line 8) | interface ISiyuanUploadImageResponse {
type ISiyuanFetchNotesResponse (line 16) | interface ISiyuanFetchNotesResponse {
FILE: src/common/backend/imageHosting/baklib/service.ts
type YuqueImageHostingOption (line 7) | interface YuqueImageHostingOption {
class YuqueImageHostingService (line 11) | class YuqueImageHostingService implements ImageHostingService {
method constructor (line 16) | constructor({ accessToken }: BaklibBackendServiceConfig) {
method getId (line 35) | getId() {
FILE: src/common/backend/imageHosting/github/form.tsx
type Props (line 20) | interface Props extends FormComponentProps {
type IRepositoryState (line 24) | interface IRepositoryState {
function fetchAllRepos (line 29) | async function fetchAllRepos(accessToken?: string): Promise<IRepositoryS...
type IBranchState (line 50) | interface IBranchState {
type IFetchBranchesOptions (line 56) | interface IFetchBranchesOptions {
function fetchBranches (line 62) | async function fetchBranches(options: IFetchBranchesOptions): Promise<IB...
FILE: src/common/backend/imageHosting/github/service.ts
class GithubImageHostingService (line 11) | class GithubImageHostingService implements ImageHostingService {
method constructor (line 15) | constructor(config: GithubImageHostingOption) {
method getId (line 24) | getId() {
FILE: src/common/backend/imageHosting/github/type.ts
type GithubImageHostingOption (line 1) | interface GithubImageHostingOption {
FILE: src/common/backend/imageHosting/imgur/form.tsx
type Props (line 7) | interface Props extends FormComponentProps {
FILE: src/common/backend/imageHosting/imgur/service.ts
type ImgurImageHostingOption (line 7) | interface ImgurImageHostingOption {
class ImgurImageHostingService (line 11) | class ImgurImageHostingService implements ImageHostingService {
method constructor (line 14) | constructor(config: ImgurImageHostingOption) {
FILE: src/common/backend/imageHosting/interface.ts
type ImageHostingServiceConstructAble (line 3) | interface ImageHostingServiceConstructAble {
type ImageHostingService (line 7) | interface ImageHostingService {
type UploadImageRequest (line 17) | interface UploadImageRequest {
type ImageHostingServiceMeta (line 21) | interface ImageHostingServiceMeta {
constant BUILT_IN_IMAGE_HOSTING_ID (line 33) | const BUILT_IN_IMAGE_HOSTING_ID = 'BUILT_IN_IMAGE_HOSTING_ID';
FILE: src/common/backend/imageHosting/joplin/service.ts
type JoplinImageHostingOption (line 9) | interface JoplinImageHostingOption {
class JoplinImageHostingService (line 13) | class JoplinImageHostingService implements ImageHostingService {
method constructor (line 17) | constructor({ token }: JoplinImageHostingOption) {
method getId (line 29) | getId() {
FILE: src/common/backend/imageHosting/leanote/service.ts
class LeanoteImageHostingService (line 12) | class LeanoteImageHostingService implements ImageHostingService {
FILE: src/common/backend/imageHosting/piclist/form.tsx
type Props (line 7) | interface Props extends FormComponentProps {
FILE: src/common/backend/imageHosting/piclist/service.ts
type PiclistImageHostingOption (line 8) | interface PiclistImageHostingOption {
class PiclistImageHostingService (line 13) | class PiclistImageHostingService implements ImageHostingService {
method constructor (line 16) | constructor(config: PiclistImageHostingOption) {
method _getImageFileName (line 48) | private _getImageFileName(url: string) {
FILE: src/common/backend/imageHosting/qcloud/form.tsx
type Props (line 8) | interface Props extends FormComponentProps {
FILE: src/common/backend/imageHosting/qcloud/service.ts
type QcloudCosImageHostingOption (line 9) | interface QcloudCosImageHostingOption {
class QcloudCosImageHostingService (line 19) | class QcloudCosImageHostingService implements ImageHostingService {
method constructor (line 22) | constructor(config: QcloudCosImageHostingOption) {
FILE: src/common/backend/imageHosting/siyuan/service.ts
type YuqueImageHostingOption (line 7) | interface YuqueImageHostingOption {
class SiYuanImageHostingService (line 11) | class SiYuanImageHostingService implements ImageHostingService {
method constructor (line 14) | constructor(config: { accessToken?: string }) {
method getId (line 21) | getId() {
FILE: src/common/backend/imageHosting/sm.ms/form.tsx
type Props (line 7) | interface Props extends FormComponentProps {
FILE: src/common/backend/imageHosting/sm.ms/service.ts
type YuqueImageHostingOption (line 8) | interface YuqueImageHostingOption {
class YuqueImageHostingService (line 12) | class YuqueImageHostingService implements ImageHostingService {
method constructor (line 14) | constructor(info: { secretToken?: string }) {
function isFail (line 59) | function isFail(
FILE: src/common/backend/imageHosting/wiznote/service.ts
type WizImageHostingOption (line 8) | interface WizImageHostingOption {
class WizNoteImageHostingService (line 12) | class WizNoteImageHostingService implements ImageHostingService {
method getId (line 13) | getId() {
FILE: src/common/backend/imageHosting/yuque_oauth/service.ts
type YuqueImageHostingOption (line 34) | interface YuqueImageHostingOption {
constant HOST (line 38) | const HOST = 'https://www.yuque.com';
constant BASE_URL (line 39) | const BASE_URL = `${HOST}/api/v2/`;
class YuqueImageHostingService (line 41) | class YuqueImageHostingService implements ImageHostingService {
method constructor (line 44) | constructor({ access_token }: YuqueImageHostingOption) {
method getId (line 48) | getId() {
FILE: src/common/backend/index.ts
function documentServiceFactory (line 24) | function documentServiceFactory(type: string, info?: any) {
function imageHostingServiceFactory (line 33) | function imageHostingServiceFactory(type: string, info?: any) {
class BackendContext (line 44) | class BackendContext {
method setDocumentService (line 48) | setDocumentService(documentService: DocumentService) {
method getDocumentService (line 52) | getDocumentService() {
method setImageHostingService (line 56) | setImageHostingService(imageHostingService: ImageHostingService) {
method getImageHostingService (line 60) | getImageHostingService() {
FILE: src/common/backend/services/baklib/form.tsx
type BaklibFormProps (line 10) | interface BaklibFormProps {
FILE: src/common/backend/services/baklib/interface.ts
type BaklibBackendServiceConfig (line 1) | interface BaklibBackendServiceConfig {
type BaklibTenantsResponse (line 6) | interface BaklibTenantsResponse {
FILE: src/common/backend/services/baklib/service.ts
type Channel (line 7) | interface Channel {
class BaklibDocumentService (line 14) | class BaklibDocumentService implements DocumentService {
method constructor (line 20) | constructor({ accessToken, origin }: BaklibBackendServiceConfig) {
method tenantToRepo (line 59) | function tenantToRepo(tenants: any, groupName: string) {
method getTentChannel (line 78) | async getTentChannel(tenant_id: string) {
FILE: src/common/backend/services/bear/service.ts
class GithubDocumentService (line 4) | class GithubDocumentService implements DocumentService {
FILE: src/common/backend/services/buildin/service.ts
class BuildinDocumentService (line 24) | class BuildinDocumentService implements DocumentService {
method constructor (line 33) | constructor() {
function getImageCdnUrl (line 388) | function getImageCdnUrl(ossName?: string) {
FILE: src/common/backend/services/buildin/type.ts
type Space (line 3) | interface Space {
type SpaceView (line 9) | interface SpaceView {
type BlockType (line 14) | type BlockType = number;
type Block (line 15) | interface Block {
type BuildinSpace (line 29) | interface BuildinSpace {
type OSSInfo (line 33) | interface OSSInfo {
type BuildinToc (line 36) | interface BuildinToc {
type BuildinUserInfo (line 42) | interface BuildinUserInfo {
type BuildinRepository (line 57) | interface BuildinRepository extends Repository {}
type TaskResult (line 59) | interface TaskResult {
type BuildinResponse (line 78) | interface BuildinResponse<DATA> {
constant ROLE_WEIGHT (line 84) | const ROLE_WEIGHT = {
type Share (line 92) | interface Share {
FILE: src/common/backend/services/confluence/form.tsx
type ConfluenceFormProps (line 16) | interface ConfluenceFormProps extends FormComponentProps {
FILE: src/common/backend/services/confluence/interface.ts
type ConfluenceListResult (line 1) | interface ConfluenceListResult<T> {
type ConfluenceSpace (line 8) | interface ConfluenceSpace {
type ConfluencePage (line 17) | interface ConfluencePage {
type ConfluenceServiceConfig (line 23) | interface ConfluenceServiceConfig {
type ConfluenceSpace (line 28) | interface ConfluenceSpace {
type ConfluenceUserInfo (line 34) | interface ConfluenceUserInfo {
type ConfluenceSpaceContent (line 44) | interface ConfluenceSpaceContent {
FILE: src/common/backend/services/confluence/service.ts
class GithubDocumentService (line 16) | class GithubDocumentService implements DocumentService {
method constructor (line 20) | constructor(config: ConfluenceServiceConfig) {
FILE: src/common/backend/services/dida365/service.ts
type Dida365Profile (line 14) | interface Dida365Profile {
type Dida365CheckResponse (line 20) | interface Dida365CheckResponse {
type Dida365CreateDocumentRequest (line 36) | interface Dida365CreateDocumentRequest extends CreateDocumentRequest {
class Dida365DocumentService (line 40) | class Dida365DocumentService implements DocumentService {
method constructor (line 43) | constructor() {
FILE: src/common/backend/services/flomo/service.ts
class GithubDocumentService (line 9) | class GithubDocumentService implements DocumentService {
method getXSRFToken (line 73) | private async getXSRFToken(): Promise<string> {
FILE: src/common/backend/services/flowus/service.ts
class FlowUsDocumentService (line 24) | class FlowUsDocumentService implements DocumentService {
method constructor (line 33) | constructor() {
function getImageCdnUrl (line 398) | function getImageCdnUrl(ossName?: string) {
FILE: src/common/backend/services/flowus/type.ts
type Space (line 3) | interface Space {
type SpaceView (line 9) | interface SpaceView {
type BlockType (line 14) | type BlockType = number;
type Block (line 15) | interface Block {
type FlowUsSpace (line 29) | interface FlowUsSpace {
type OSSInfo (line 33) | interface OSSInfo {
type FlowUsToc (line 36) | interface FlowUsToc {
type FlowUsUserInfo (line 42) | interface FlowUsUserInfo {
type FlowUsRepository (line 57) | interface FlowUsRepository extends Repository {}
type TaskResult (line 59) | interface TaskResult {
type FlowUsResponse (line 78) | interface FlowUsResponse<DATA> {
constant ROLE_WEIGHT (line 84) | const ROLE_WEIGHT = {
type Share (line 92) | interface Share {
FILE: src/common/backend/services/github/form.tsx
type GithubFormProps (line 12) | interface GithubFormProps {
FILE: src/common/backend/services/github/interface.ts
type GithubBackendServiceConfig (line 3) | interface GithubBackendServiceConfig {
type GithubCreateDocumentRequest (line 8) | interface GithubCreateDocumentRequest extends CreateDocumentRequest {
type GithubUserInfoResponse (line 12) | interface GithubUserInfoResponse {
type GithubRepository (line 19) | interface GithubRepository extends Repository {
type GithubRepositoryResponse (line 23) | interface GithubRepositoryResponse {
type GithubLabel (line 32) | interface GithubLabel {
FILE: src/common/backend/services/github/service.ts
constant PAGE_LIMIT (line 15) | const PAGE_LIMIT = 100;
class GithubDocumentService (line 17) | class GithubDocumentService implements DocumentService {
method constructor (line 22) | constructor(config: GithubBackendServiceConfig) {
FILE: src/common/backend/services/github_repository/form.tsx
type GithubFormProps (line 12) | interface GithubFormProps {
FILE: src/common/backend/services/github_repository/interface.ts
type GithubBackendServiceConfig (line 3) | interface GithubBackendServiceConfig {
type GithubCreateDocumentRequest (line 10) | interface GithubCreateDocumentRequest extends CreateDocumentRequest {
type GithubUserInfoResponse (line 14) | interface GithubUserInfoResponse {
type GithubRepository (line 21) | interface GithubRepository extends Repository {
type GithubRepositoryResponse (line 25) | interface GithubRepositoryResponse {
FILE: src/common/backend/services/github_repository/service.ts
constant PAGE_LIMIT (line 16) | const PAGE_LIMIT = 100;
class GithubRepositoryDocumentService (line 18) | class GithubRepositoryDocumentService implements DocumentService {
method constructor (line 23) | constructor(config: GithubBackendServiceConfig) {
FILE: src/common/backend/services/interface.ts
type CreateDocumentRequest (line 1) | interface CreateDocumentRequest {
type CompleteStatus (line 8) | interface CompleteStatus {
type UserInfo (line 12) | interface UserInfo {
type Repository (line 19) | interface Repository {
type ServiceMeta (line 39) | interface ServiceMeta {
type UpdateTOCRequest (line 70) | interface UpdateTOCRequest {}
type DocumentService (line 72) | interface DocumentService<T = any> {
type ErrorOptions (line 84) | interface ErrorOptions {
class BaseError (line 88) | class BaseError<T extends ErrorOptions> extends Error {
method constructor (line 91) | constructor(options?: T) {
method from (line 98) | public static from(err: Error): BaseError<ErrorOptions> {
type HttpErrorOptions (line 107) | interface HttpErrorOptions extends ErrorOptions {
class HttpError (line 111) | class HttpError extends BaseError<HttpErrorOptions> {
method constructor (line 115) | constructor(options: HttpErrorOptions) {
class UnauthorizedError (line 122) | class UnauthorizedError extends HttpError {
method constructor (line 123) | constructor(message?: string) {
FILE: src/common/backend/services/joplin/form.tsx
type FormProps (line 9) | interface FormProps extends FormComponentProps {
FILE: src/common/backend/services/joplin/service.ts
constant HOST (line 14) | const HOST = 'http://localhost:41184/';
class JoplinDocumentService (line 16) | class JoplinDocumentService implements DocumentService {
method constructor (line 18) | constructor(private config: JoplinBackendServiceConfig) {}
method getId (line 20) | getId() {
method getJoplinClient (line 48) | private async getJoplinClient(): Promise<IJoplinClient> {
method getSupportToken (line 55) | private async getSupportToken() {
method _getJoplinClient (line 79) | private async _getJoplinClient(token: string): Promise<IJoplinClient> {
FILE: src/common/backend/services/leanote/form.tsx
type OneNoteProps (line 11) | interface OneNoteProps {
FILE: src/common/backend/services/leanote/service.ts
class LeanoteDocumentService (line 13) | class LeanoteDocumentService implements DocumentService {
method constructor (line 21) | constructor(config: LeanoteBackendServiceConfig) {
FILE: src/common/backend/services/memos/form.tsx
type MemosFormProps (line 10) | interface MemosFormProps {
FILE: src/common/backend/services/memos/interface.ts
type VisibilityType (line 9) | type VisibilityType = typeof VisibilityType[number];
type MemosBackendServiceConfig (line 11) | interface MemosBackendServiceConfig {
type MemosUserResponse (line 16) | interface MemosUserResponse {
type MemosUserInfo (line 24) | interface MemosUserInfo {
type MemoCreateDocumentRequest (line 31) | interface MemoCreateDocumentRequest extends CreateDocumentRequest {
FILE: src/common/backend/services/memos/service.ts
class MemosDocumentService (line 13) | class MemosDocumentService implements DocumentService {
method constructor (line 19) | constructor({ accessToken, origin }: MemosBackendServiceConfig) {
FILE: src/common/backend/services/notion/service.ts
constant PAGE (line 11) | const PAGE = 'page';
constant COLLECTION_VIEW_PAGE (line 12) | const COLLECTION_VIEW_PAGE = 'collection_view_page';
class NotionDocumentService (line 15) | class NotionDocumentService implements DocumentService {
method constructor (line 22) | constructor() {
method loadSpace (line 331) | private async loadSpace(
method getRecentPageVisits (line 395) | private async getRecentPageVisits(spaceId: string, userId: string): Pr...
method requestWithCookie (line 411) | private get requestWithCookie() {
FILE: src/common/backend/services/notion/types.ts
type NotionUserContent (line 3) | interface NotionUserContent {
type RecentPages (line 58) | interface RecentPages {
type NotionRepository (line 74) | interface NotionRepository extends Repository {
FILE: src/common/backend/services/obsidian/form.tsx
type OneNoteProps (line 8) | interface OneNoteProps {
method render (line 13) | render() {
FILE: src/common/backend/services/obsidian/interface.ts
type ObsidianFormConfig (line 1) | interface ObsidianFormConfig {
FILE: src/common/backend/services/obsidian/service.ts
class ObsidianService (line 6) | class ObsidianService implements DocumentService {
method constructor (line 7) | constructor(private config: ObsidianFormConfig) {}
FILE: src/common/backend/services/onenote_oauth/form.tsx
type OneNoteProps (line 8) | interface OneNoteProps {
method render (line 14) | render() {
FILE: src/common/backend/services/onenote_oauth/interface.ts
type OneNoteBackendServiceConfig (line 1) | interface OneNoteBackendServiceConfig {
type OneNoteNotebooksResponse (line 6) | interface OneNoteNotebooksResponse {
type OneNoteUserInfoResponse (line 17) | interface OneNoteUserInfoResponse {
type OneNoteCreateDocumentResponse (line 23) | interface OneNoteCreateDocumentResponse {
type OneNoteRefreshTokenResponse (line 35) | interface OneNoteRefreshTokenResponse {
FILE: src/common/backend/services/onenote_oauth/service.ts
constant BASE_URL (line 19) | const BASE_URL = `https://graph.microsoft.com/`;
class YuqueDocumentService (line 21) | class YuqueDocumentService implements DocumentService<OneNoteBackendServ...
method constructor (line 26) | constructor({ access_token, refresh_token }: OneNoteBackendServiceConf...
FILE: src/common/backend/services/server_chan/form.tsx
type FormProps (line 9) | interface FormProps extends FormComponentProps {
FILE: src/common/backend/services/server_chan/service.ts
class GithubDocumentService (line 6) | class GithubDocumentService implements DocumentService {
method constructor (line 9) | constructor(config: { accessToken: string }) {
FILE: src/common/backend/services/siyuan/form.tsx
type SiyuanFormProps (line 8) | interface SiyuanFormProps {
type SiyuanBackendServiceConfig (line 12) | interface SiyuanBackendServiceConfig {
FILE: src/common/backend/services/siyuan/service.ts
class SiYuanDocumentService (line 13) | class SiYuanDocumentService implements DocumentService {
method constructor (line 15) | constructor(config: { accessToken?: string }) {
FILE: src/common/backend/services/ticktick/service.ts
type TickTickProfile (line 14) | interface TickTickProfile {
type TickTickCheckResponse (line 20) | interface TickTickCheckResponse {
type TickTickCreateDocumentRequest (line 36) | interface TickTickCreateDocumentRequest extends CreateDocumentRequest {
class TickTickDocumentService (line 40) | class TickTickDocumentService implements DocumentService {
method constructor (line 43) | constructor() {
FILE: src/common/backend/services/ulysses/service.ts
class GithubDocumentService (line 6) | class GithubDocumentService implements DocumentService {
FILE: src/common/backend/services/webdav/form.tsx
type FormProps (line 9) | interface FormProps extends FormComponentProps {
FILE: src/common/backend/services/webdav/interface.ts
type WebDAVServiceConfig (line 1) | interface WebDAVServiceConfig {
FILE: src/common/backend/services/webdav/service.ts
class WebDAVDocumentService (line 7) | class WebDAVDocumentService implements DocumentService {
method constructor (line 11) | constructor(private config: WebDAVServiceConfig) {
FILE: src/common/backend/services/wiznote/form.tsx
type WizNoteFormProps (line 10) | interface WizNoteFormProps extends FormComponentProps {
FILE: src/common/backend/services/wiznote/interface.ts
type WizNoteConfig (line 3) | interface WizNoteConfig {
type WizNoteUserInfo (line 8) | interface WizNoteUserInfo {
type WizNoteCreateDocumentRequest (line 18) | interface WizNoteCreateDocumentRequest extends CreateDocumentRequest {
type WizNoteCreateTagResponse (line 22) | interface WizNoteCreateTagResponse {
type WizNoteGetTagsResponse (line 28) | interface WizNoteGetTagsResponse {
type WizNoteGetRepositoriesResponse (line 36) | interface WizNoteGetRepositoriesResponse {
FILE: src/common/backend/services/wiznote/service.ts
type WizTempDoc (line 18) | interface WizTempDoc {
class WizNoteDocumentService (line 23) | class WizNoteDocumentService implements DocumentService {
method constructor (line 30) | constructor(config: WizNoteConfig) {
method request (line 211) | private request<T>(
FILE: src/common/backend/services/wolai/service.ts
constant PAGE (line 11) | const PAGE = 'page';
class WolaiDocumentService (line 14) | class WolaiDocumentService implements DocumentService {
method constructor (line 22) | constructor() {
FILE: src/common/backend/services/wolai/type.ts
type WolaiUserContent (line 3) | interface WolaiUserContent {
type WolaiUserInfo (line 62) | interface WolaiUserInfo {
type WolaiRepository (line 80) | interface WolaiRepository extends Repository {
FILE: src/common/backend/services/youdao/service.ts
type YouDaoRepository (line 10) | interface YouDaoRepository {
class YoudaoDocumentService (line 18) | class YoudaoDocumentService implements DocumentService {
method constructor (line 21) | constructor() {
FILE: src/common/backend/services/yuque/form.tsx
type YuqueFormProps (line 9) | interface YuqueFormProps {
method render (line 45) | render() {
FILE: src/common/backend/services/yuque/interface.ts
type RepositoryType (line 4) | enum RepositoryType {
type YuqueBackendServiceConfig (line 10) | interface YuqueBackendServiceConfig {
type YuqueUserInfoResponse (line 15) | interface YuqueUserInfoResponse {
type YuqueGroupResponse (line 23) | interface YuqueGroupResponse {
type YuqueRepository (line 29) | interface YuqueRepository extends Repository {
type YuqueRepositoryResponse (line 33) | interface YuqueRepositoryResponse {
type YuqueCreateDocumentResponse (line 39) | interface YuqueCreateDocumentResponse {
type YuqueCompleteStatus (line 47) | interface YuqueCompleteStatus extends CompleteStatus {
type YuqueCreateDocumentRequest (line 53) | interface YuqueCreateDocumentRequest extends CreateDocumentRequest {
type YuqueUpdateTOCRequest (line 57) | interface YuqueUpdateTOCRequest extends UpdateTOCRequest{
FILE: src/common/backend/services/yuque/service.ts
constant HOST (line 21) | const HOST = 'https://www.yuque.com';
constant BASE_URL (line 22) | const BASE_URL = `${HOST}/api/v2/`;
class YuqueDocumentService (line 24) | class YuqueDocumentService implements DocumentService {
method constructor (line 30) | constructor({ accessToken, repositoryType = RepositoryType.all }: Yuqu...
FILE: src/common/backend/services/yuque_oauth/form.tsx
type YuqueFormProps (line 9) | interface YuqueFormProps {
method render (line 45) | render() {
FILE: src/common/backend/services/yuque_oauth/interface.ts
type RepositoryType (line 4) | enum RepositoryType {
type YuqueBackendServiceConfig (line 10) | interface YuqueBackendServiceConfig {
type YuqueUserInfoResponse (line 15) | interface YuqueUserInfoResponse {
type YuqueGroupResponse (line 23) | interface YuqueGroupResponse {
type YuqueRepository (line 29) | interface YuqueRepository extends Repository {
type YuqueRepositoryResponse (line 33) | interface YuqueRepositoryResponse {
type YuqueCreateDocumentResponse (line 39) | interface YuqueCreateDocumentResponse {
type YuqueCompleteStatus (line 47) | interface YuqueCompleteStatus extends CompleteStatus {
type YuqueCreateDocumentRequest (line 52) | interface YuqueCreateDocumentRequest extends CreateDocumentRequest {
FILE: src/common/backend/services/yuque_oauth/service.ts
constant HOST (line 20) | const HOST = 'https://www.yuque.com';
constant BASE_URL (line 21) | const BASE_URL = `${HOST}/api/v2/`;
class YuqueDocumentService (line 23) | class YuqueDocumentService implements DocumentService {
method constructor (line 29) | constructor({ access_token, repositoryType = RepositoryType.all }: Yuq...
FILE: src/common/blob.ts
function loadImage (line 26) | function loadImage(date: string): Promise<HTMLImageElement> {
FILE: src/common/buffer.ts
class VSBuffer (line 20) | class VSBuffer {
method alloc (line 21) | static alloc(byteLength: number): VSBuffer {
method wrap (line 28) | static wrap(actual: Uint8Array): VSBuffer {
method fromString (line 37) | static fromString(source: string, options?: { dontUseNodeBuffer?: bool...
method concat (line 51) | static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
method constructor (line 73) | private constructor(buffer: Uint8Array) {
method toString (line 78) | toString(): string {
method slice (line 91) | slice(start?: number, end?: number): VSBuffer {
method set (line 100) | set(array: VSBuffer | Uint8Array, offset?: number): void {
method readUInt32BE (line 108) | readUInt32BE(offset: number): number {
method writeUInt32BE (line 112) | writeUInt32BE(value: number, offset: number): void {
method readUInt32LE (line 116) | readUInt32LE(offset: number): number {
method writeUInt32LE (line 120) | writeUInt32LE(value: number, offset: number): void {
method readUInt8 (line 124) | readUInt8(offset: number): number {
method writeUInt8 (line 128) | writeUInt8(value: number, offset: number): void {
function readUInt16LE (line 133) | function readUInt16LE(source: Uint8Array, offset: number): number {
function writeUInt16LE (line 137) | function writeUInt16LE(destination: Uint8Array, value: number, offset: n...
function readUInt32BE (line 143) | function readUInt32BE(source: Uint8Array, offset: number): number {
function writeUInt32BE (line 152) | function writeUInt32BE(destination: Uint8Array, value: number, offset: n...
function readUInt32LE (line 162) | function readUInt32LE(source: Uint8Array, offset: number): number {
function writeUInt32LE (line 171) | function writeUInt32LE(destination: Uint8Array, value: number, offset: n...
function readUInt8 (line 181) | function readUInt8(source: Uint8Array, offset: number): number {
function writeUInt8 (line 185) | function writeUInt8(destination: Uint8Array, value: number, offset: numb...
FILE: src/common/chrome/storage.ts
class LocalStorageService (line 4) | class LocalStorageService extends AbstractStorageService {
method constructor (line 5) | constructor() {
class SyncStorageService (line 10) | class SyncStorageService extends AbstractStorageService {
method constructor (line 11) | constructor() {
FILE: src/common/error.ts
type SerializedError (line 1) | interface SerializedError {
function transformErrorForSerialization (line 10) | function transformErrorForSerialization(error: any): any {
FILE: src/common/getResource.ts
function getResourcePath (line 1) | function getResourcePath(name: string) {
FILE: src/common/hooks/useFilterImageHostingServices.ts
type Props (line 4) | interface Props {
type ImageHostingWithMeta (line 12) | type ImageHostingWithMeta = {
FILE: src/common/hooks/useVerifiedAccount.tsx
type UseVerifiedAccountProps (line 9) | type UseVerifiedAccountProps = FormComponentProps & {
function useDeepCompareMemoize (line 14) | function useDeepCompareMemoize<T>(value: T) {
FILE: src/common/loading.test.ts
function flushPromises (line 6) | function flushPromises() {
class Test (line 12) | class Test {
method actionAfterLoading (line 26) | actionAfterLoading() {
method actionBeforeLoading (line 33) | actionBeforeLoading() {
FILE: src/common/loading.ts
type FunctionKeys (line 4) | type FunctionKeys<T> = {
function loadingStatus (line 12) | function loadingStatus<T>(
function LoadingHoc (line 35) | function LoadingHoc(uuidKey: string, fn: Function) {
function loading (line 54) | function loading(target: any, key: string, descriptor?: any) {
FILE: src/common/locales/index.ts
class LocaleService (line 32) | class LocaleService {
method init (line 35) | async init() {
method locale (line 50) | get locale() {
method format (line 54) | format(descriptor: MessageDescriptor, values?: Record<string, any>): s...
method getMessage (line 61) | getMessage(key: string): string {
FILE: src/common/locales/interface.ts
type LocaleModel (line 1) | interface LocaleModel {
function removeEmptyKeys (line 10) | function removeEmptyKeys(
FILE: src/common/matchUrl.ts
constant RE_MATCH_PARTS (line 3) | const RE_MATCH_PARTS = /(.*?):\/\/([^/]*)\/(.*)/;
constant RE_HTTP_OR_HTTPS (line 5) | const RE_HTTP_OR_HTTPS = /^https?$/i;
function str2RE (line 7) | function str2RE(str: string): string {
function matchScheme (line 11) | function matchScheme(rule: string, data: string) {
constant RE_STR_ANY (line 21) | const RE_STR_ANY = '(?:|.*?\\.)';
constant RE_STR_TLD (line 22) | const RE_STR_TLD = '((?:\\.\\w+)+)';
function hostMatcher (line 23) | function hostMatcher(rule: string) {
function pathMatcher (line 56) | function pathMatcher(rule: string) {
function matchTester (line 69) | function matchTester(rule: string) {
FILE: src/common/modelTypes/account.ts
type AccountPreference (line 1) | interface AccountPreference {
type AccountStore (line 13) | interface AccountStore {
FILE: src/common/modelTypes/clipper.ts
type ClipperHeaderForm (line 8) | interface ClipperHeaderForm {
type ClipperStore (line 13) | interface ClipperStore {
FILE: src/common/modelTypes/extensions.ts
type ExtensionStore (line 3) | interface ExtensionStore {
constant LOCAL_EXTENSIONS_DISABLED_EXTENSIONS_KEY (line 8) | const LOCAL_EXTENSIONS_DISABLED_EXTENSIONS_KEY = 'local.extensions.disab...
constant LOCAL_EXTENSIONS_ENABLE_AUTOMATIC_EXTENSIONS_KEY (line 10) | const LOCAL_EXTENSIONS_ENABLE_AUTOMATIC_EXTENSIONS_KEY =
FILE: src/common/modelTypes/userPreference.ts
type UserPreferenceStore (line 3) | interface UserPreferenceStore {
type ImageHosting (line 19) | interface ImageHosting {
type ImageClipperData (line 28) | interface ImageClipperData {
type ClipperDataType (line 34) | type ClipperDataType = string | ImageClipperData;
constant LOCAL_USER_PREFERENCE_LOCALE_KEY (line 36) | const LOCAL_USER_PREFERENCE_LOCALE_KEY = 'local.userPreference.locale';
constant LOCAL_ACCESS_TOKEN_LOCALE_KEY (line 41) | const LOCAL_ACCESS_TOKEN_LOCALE_KEY = 'local.access.token.locale';
FILE: src/common/object.ts
function isUndefined (line 1) | function isUndefined(data: any) {
FILE: src/common/storage/__test__/index.spec.ts
class MockStorage (line 7) | class MockStorage implements CommonStorage {
method constructor (line 9) | constructor() {
FILE: src/common/storage/index.ts
class ChromeSyncStorageImpl (line 6) | class ChromeSyncStorageImpl implements CommonStorage {
method set (line 7) | public async set(key: string, item: Object): Promise<void> {
method get (line 13) | public async get<T>(key: string): Promise<T> {
FILE: src/common/storage/interface.ts
type PreferenceStorage (line 2) | interface PreferenceStorage {
type CommonStorage (line 10) | interface CommonStorage {
type TypedCommonStorageInterface (line 15) | interface TypedCommonStorageInterface {
FILE: src/common/storage/typedCommonStorage.ts
class TypedCommonStorage (line 15) | class TypedCommonStorage implements TypedCommonStorageInterface {
method constructor (line 18) | constructor(store: CommonStorage) {
FILE: src/common/strings.ts
type Constants (line 6) | const enum Constants {
function encodeUTF8 (line 44) | function encodeUTF8(str: string): Uint8Array {
function decodeUTF8 (line 98) | function decodeUTF8(buffer: Uint8Array): string {
function getNextCodePoint (line 149) | function getNextCodePoint(str: string, len: number, offset: number): num...
function isHighSurrogate (line 163) | function isHighSurrogate(charCode: number): boolean {
function isLowSurrogate (line 170) | function isLowSurrogate(charCode: number): boolean {
function computeCodePoint (line 177) | function computeCodePoint(highSurrogate: number, lowSurrogate: number): ...
FILE: src/common/types.ts
type DvaRouterProps (line 13) | type DvaRouterProps = {
type DvaLoadingState (line 17) | interface DvaLoadingState {
type GlobalStore (line 23) | interface GlobalStore {
type IResponse (line 37) | interface IResponse<T> {
FILE: src/common/version/index.ts
function hasUpdate (line 1) | function hasUpdate(remote: string, local: string): boolean {
FILE: src/components/ExtensionCard/index.tsx
type ExtensionCardProps (line 17) | interface ExtensionCardProps {
FILE: src/components/ImageHostingSelect.tsx
type ImageHostingSelectProps (line 7) | interface ImageHostingSelectProps extends SelectProps<string> {
FILE: src/components/LinkRender/index.tsx
type LinkRenderProps (line 3) | interface LinkRenderProps {
FILE: src/components/RepositorySelect.tsx
type RepositoryInGroup (line 7) | interface RepositoryInGroup {
type RepositorySelectProps (line 15) | interface RepositorySelectProps extends SelectProps<string> {
FILE: src/components/accountItem/index.tsx
type PageProps (line 8) | interface PageProps {
class Page (line 19) | class Page extends React.Component<PageProps> {
method render (line 32) | render() {
FILE: src/components/avatar/index.tsx
type IconAvatarProps (line 5) | interface IconAvatarProps {
FILE: src/components/container/index.tsx
type ToolContainerProps (line 9) | interface ToolContainerProps {
class ToolContainer (line 14) | class ToolContainer extends React.Component<ToolContainerProps> {
method render (line 27) | public render() {
FILE: src/components/imageHostingSelectOption/index.tsx
type PageProps (line 7) | interface PageProps {
class Page (line 14) | class Page extends React.Component<PageProps> {
method render (line 15) | render() {
FILE: src/components/imagehostingListItem/index.tsx
type PageProps (line 7) | interface PageProps {
class Page (line 16) | class Page extends React.Component<PageProps> {
method render (line 31) | render() {
FILE: src/components/section/index.tsx
type Props (line 4) | interface Props {
FILE: src/components/share/index.tsx
type ShareProps (line 3) | interface ShareProps {
FILE: src/components/userItem/index.tsx
type UserItemProps (line 5) | interface UserItemProps {
FILE: src/config.ts
type WebClipperConfig (line 1) | interface WebClipperConfig {
type RemoteConfig (line 11) | interface RemoteConfig {
FILE: src/extensions/common.ts
type InitContext (line 10) | interface InitContext {
type ContentScriptContext (line 22) | interface ContentScriptContext {
type Message (line 35) | interface Message {
type UploadImageRequest (line 39) | interface UploadImageRequest {
type ImageHostingService (line 43) | interface ImageHostingService {
type CopyToClipboardOptions (line 51) | interface CopyToClipboardOptions {
type ToolContext (line 57) | interface ToolContext<T, Out> {
type IExtensionLifeCycle (line 73) | interface IExtensionLifeCycle<T, U> {
type IExtensionManifest (line 95) | interface IExtensionManifest {
type ExtensionType (line 130) | enum ExtensionType {
type SerializedExtension (line 136) | interface SerializedExtension {
type SerializedExtensionWithId (line 145) | interface SerializedExtensionWithId extends SerializedExtension {
type SerializedExtensionInfo (line 151) | type SerializedExtensionInfo = Pick<SerializedExtensionWithId, 'type' | ...
type Type (line 153) | interface Type<T> extends Function {
type IExtension (line 157) | interface IExtension<T, U> {
type IExtensionWithId (line 162) | interface IExtensionWithId<T = any, U = any> {
class AbstractExtension (line 171) | class AbstractExtension<T, U> {
method constructor (line 176) | constructor(
class TextExtension (line 187) | class TextExtension<T = string> extends AbstractExtension<T, string> {
method constructor (line 188) | constructor(manifest: IExtensionManifest, methods: IExtensionLifeCycle...
class ToolExtension (line 193) | class ToolExtension<T = string> extends AbstractExtension<T, string> {
method constructor (line 194) | constructor(manifest: IExtensionManifest, methods: IExtensionLifeCycle...
type IContextMenuContext (line 199) | interface IContextMenuContext {
type IContextMenuExtensionFactory (line 204) | interface IContextMenuExtensionFactory {
type IContextMenusWithId (line 209) | interface IContextMenusWithId {
function getLocaleExtensionManifest (line 214) | function getLocaleExtensionManifest(manifest: IExtensionManifest, locale...
FILE: src/extensions/contextMenus.ts
type IContextMenuProperties (line 4) | interface IContextMenuProperties {
type IContextMenuExtensionManifest (line 10) | interface IContextMenuExtensionManifest extends IExtensionManifest {
type IContextMenuExtension (line 14) | interface IContextMenuExtension {
type IContextMenuContext (line 19) | interface IContextMenuContext {
method constructor (line 26) | constructor(public manifest: IContextMenuExtensionManifest) {}
type IContextMenuExtensionFactory (line 31) | interface IContextMenuExtensionFactory {
type IContextMenusWithId (line 36) | interface IContextMenusWithId {
FILE: src/extensions/contextMenus/saveSelection/saveSelection.ts
class ContextMenu (line 5) | class ContextMenu extends ContextMenuExtension {
method constructor (line 8) | constructor() {
method run (line 41) | async run(tab: chrome.tabs.Tab, context: IContextMenuContext): Promise...
FILE: src/extensions/extensions/web-clipper/link.tsx
class Link (line 4) | class Link extends ToolExtension<any> {
method constructor (line 5) | constructor() {
FILE: src/hooks/useOriginForm.tsx
type UseOriginFormProps (line 6) | interface UseOriginFormProps extends FormComponentProps {
method validator (line 33) | validator(_r: any, value: string, callback: Function) {
FILE: src/main/background.worker.ts
function main (line 41) | function main() {
FILE: src/main/contentScript.main.ts
function updateColor (line 51) | async function updateColor() {
function updateMenu (line 64) | async function updateMenu() {
FILE: src/models/userPreference.ts
function createAndDownloadFile (line 206) | function createAndDownloadFile(fileName: string, content: string | Blob) {
function pangu (line 219) | async function pangu(document: string): Promise<string> {
FILE: src/pages/app.tsx
function withTool (line 29) | function withTool(WrappedComponent: any): any {
FILE: src/pages/auth.tsx
type PageQuery (line 22) | interface PageQuery {
type PageStateProps (line 36) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageProps (line 37) | type PageProps = PageStateProps & DvaRouterProps & FormComponentProps;
function useDeepCompareMemoize (line 39) | function useDeepCompareMemoize<T>(value: T) {
FILE: src/pages/complete/complete.tsx
function closeTool (line 29) | function closeTool() {
FILE: src/pages/locale.tsx
type PageStateProps (line 14) | type PageStateProps = ReturnType<typeof mapStateToProps>;
FILE: src/pages/plugin/TextEditor.tsx
type PageOwnProps (line 27) | type PageOwnProps = {
type PageProps (line 32) | type PageProps = ReturnType<typeof mapStateToProps> & typeof useActions ...
class ClipperPluginPage (line 36) | class ClipperPluginPage extends React.Component<PageProps, { markdown: s...
method constructor (line 39) | constructor(props: any) {
method render (line 125) | render() {
FILE: src/pages/preference/account/index.tsx
type PageState (line 45) | type PageState = {
type PageStateProps (line 50) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageDispatchProps (line 51) | type PageDispatchProps = typeof useActions;
type PageProps (line 52) | type PageProps = PageStateProps & PageDispatchProps & FormComponentProps;
class Page (line 56) | class Page extends React.Component<PageProps, PageState> {
method constructor (line 57) | constructor(props: PageProps) {
method render (line 172) | render() {
FILE: src/pages/preference/account/modal/createAccountModal.tsx
type PageOwnProps (line 19) | type PageOwnProps = {
type PageProps (line 29) | type PageProps = PageOwnProps & FormComponentProps;
FILE: src/pages/preference/account/modal/editAccountModal.tsx
type PageOwnProps (line 17) | type PageOwnProps = {
type PageProps (line 28) | type PageProps = PageOwnProps & FormComponentProps;
FILE: src/pages/preference/base.tsx
type PageStateProps (line 20) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageProps (line 22) | type PageProps = PageStateProps & DvaRouterProps;
FILE: src/pages/preference/imageHosting/form/addImageHosting.tsx
type PageOwnProps (line 12) | type PageOwnProps = {
type PageProps (line 21) | type PageProps = PageOwnProps & FormComponentProps;
FILE: src/pages/preference/imageHosting/index.tsx
type PageState (line 36) | type PageState = {
type PageStateProps (line 41) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageDispatchProps (line 42) | type PageDispatchProps = typeof useActions;
type PageOwnProps (line 43) | type PageOwnProps = {};
type PageProps (line 44) | type PageProps = PageStateProps & PageDispatchProps & PageOwnProps & For...
class Page (line 48) | class Page extends React.Component<PageProps, PageState> {
method constructor (line 49) | constructor(props: PageProps) {
method render (line 134) | render() {
FILE: src/pages/preference/index.tsx
type PageStateProps (line 38) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageProps (line 81) | type PageProps = DvaRouterProps & PageStateProps;
FILE: src/pages/tool/ClipExtension.tsx
type PageProps (line 10) | type PageProps = {
FILE: src/pages/tool/Header.tsx
type PageProps (line 17) | type PageProps = FormComponentProps & {
FILE: src/pages/tool/index.tsx
type PageStateProps (line 58) | type PageStateProps = ReturnType<typeof mapStateToProps>;
type PageProps (line 59) | type PageProps = PageStateProps & DvaRouterProps;
FILE: src/pages/tool/toolExtensions.tsx
type ToolExtensionsProps (line 9) | type ToolExtensionsProps = {
FILE: src/service/common/config.ts
type RemoteConfig (line 4) | interface RemoteConfig {
type IConfigService (line 14) | interface IConfigService {
FILE: src/service/common/configuration.ts
type WebClipperConfiguration (line 1) | interface WebClipperConfiguration {
type IConfigurationService (line 26) | interface IConfigurationService {}
type GetLocalConfiguration (line 28) | type GetLocalConfiguration = () => {};
FILE: src/service/common/contentScript.ts
type IToggleConfig (line 3) | interface IToggleConfig {
type IContentScriptService (line 7) | interface IContentScriptService {
FILE: src/service/common/cookie.ts
type ICookieService (line 3) | interface ICookieService {
FILE: src/service/common/extension.ts
type Extension (line 4) | interface Extension {
type IExtensionService (line 8) | interface IExtensionService {
type IExtensionContainer (line 27) | interface IExtensionContainer {
FILE: src/service/common/ipc.ts
type IServerChannel (line 4) | interface IServerChannel<C = any> {
type IChannel (line 8) | interface IChannel {
type IChannelClient (line 12) | interface IChannelClient {
type IChannelServer (line 16) | interface IChannelServer {
type IPCMessageRequest (line 20) | interface IPCMessageRequest<T = any> {
type IPCMessageResponse (line 26) | interface IPCMessageResponse<T = any> {
class ChannelClient (line 36) | class ChannelClient implements IChannel {
method constructor (line 37) | constructor(private channelName: string) {}
method call (line 39) | async call<T>(command: string, arg?: any): Promise<T> {
FILE: src/service/common/locale.ts
type ILocaleService (line 4) | interface ILocaleService {
FILE: src/service/common/permissions.ts
type Permissions (line 3) | interface Permissions {
type IPermissionsService (line 9) | interface IPermissionsService {
FILE: src/service/common/preference.ts
type TIconColor (line 3) | type TIconColor = 'dark' | 'light' | 'auto';
type IUserPreference (line 5) | interface IUserPreference {
type IPreferenceService (line 9) | interface IPreferenceService {
FILE: src/service/common/request.ts
type Method (line 3) | type Method = 'get' | 'post' | 'put';
type BaseRequestOptions (line 5) | interface BaseRequestOptions {
type IPostFormRequestOptions (line 10) | interface IPostFormRequestOptions extends BaseRequestOptions {
type IPostRequestOptions (line 15) | interface IPostRequestOptions extends BaseRequestOptions {
type IGetFormRequestOptions (line 21) | interface IGetFormRequestOptions extends BaseRequestOptions {
type IPutRequestOptions (line 25) | interface IPutRequestOptions extends BaseRequestOptions {
type TRequestOption (line 30) | type TRequestOption =
type IRequestService (line 36) | interface IRequestService {
type RequestInterceptor (line 41) | type RequestInterceptor = (
type ResponseInterceptor (line 49) | type ResponseInterceptor = (data: unknown) => unknown;
type IExtendRequestHelper (line 51) | interface IExtendRequestHelper {
type IHelperOptions (line 63) | interface IHelperOptions {
FILE: src/service/common/tab.ts
type Tab (line 2) | interface Tab {
type CaptureVisibleTabOptions (line 8) | interface CaptureVisibleTabOptions {
type ITabService (line 13) | interface ITabService {
FILE: src/service/common/webRequest.ts
type WebBlockHeader (line 3) | interface WebBlockHeader {
type WebRequestBlockOption (line 8) | interface WebRequestBlockOption {
type RequestInBackgroundOptions (line 13) | interface RequestInBackgroundOptions {
type IWebRequestService (line 20) | interface IWebRequestService {
FILE: src/service/config/browser/configService.ts
type RemoteConfig (line 10) | type RemoteConfig = _RemoteConfig;
class BrowserConfigService (line 12) | class BrowserConfigService implements IConfigService {
method id (line 42) | get id() {
FILE: src/service/configuration/common/generate-local-config.ts
type IGenerateLocalConfigOptions (line 3) | interface IGenerateLocalConfigOptions {
FILE: src/service/configuration/configuration.ts
class ConfigurationService (line 6) | class ConfigurationService implements IConfigurationService {
method constructor (line 8) | constructor(@Inject(ILocalStorageService) private localStorageService:...
method init (line 12) | public async init(): Promise<void> {
method doInitialize (line 19) | private async doInitialize(): Promise<void> {
FILE: src/service/contentScript/browser/contentScript/contentScript.ts
class ContentScriptService (line 18) | class ContentScriptService implements IContentScriptService {
method constructor (line 19) | constructor(@Inject(IExtensionContainer) private extensionContainer: I...
method remove (line 21) | async remove() {
method hide (line 24) | async hide() {
method toggle (line 27) | async toggle(config: IToggleConfig) {
method getSelectionMarkdown (line 48) | async getSelectionMarkdown() {
method checkStatus (line 59) | async checkStatus() {
method getPageUrl (line 62) | async getPageUrl() {
method toggleLoading (line 65) | async toggleLoading() {
method runScript (line 85) | async runScript(id: string, lifeCycle: 'run' | 'destroy') {
FILE: src/service/contentScript/common/contentScriptIPC.ts
class ContentScriptChannel (line 4) | class ContentScriptChannel implements IServerChannel {
method constructor (line 5) | constructor(private service: IContentScriptService) {}
class ContentScriptChannelClient (line 36) | class ContentScriptChannelClient implements IContentScriptService {
method constructor (line 37) | constructor(private channel: IChannel) {}
FILE: src/service/cookie/background/cookieService.ts
class ChromeCookieService (line 4) | class ChromeCookieService implements ICookieService {
method get (line 5) | get(detail: chrome.cookies.Details): Promise<chrome.cookies.Cookie | n...
method getAll (line 11) | getAll(detail: chrome.cookies.GetAllDetails): Promise<chrome.cookies.C...
method getAllCookieStores (line 16) | getAllCookieStores(): Promise<chrome.cookies.CookieStore[]> {
FILE: src/service/cookie/common/cookieIpc.ts
class CookieChannel (line 4) | class CookieChannel implements IServerChannel {
method constructor (line 5) | constructor(private service: ICookieService) {}
class CookieChannelClient (line 26) | class CookieChannelClient implements ICookieService {
method constructor (line 27) | constructor(private channel: IChannel) {}
FILE: src/service/extension/browser/extensionContainer.ts
class ExtensionContainer (line 12) | class ExtensionContainer implements IExtensionContainer {
method constructor (line 19) | constructor(
method init (line 33) | async init() {
FILE: src/service/extension/browser/extensionService.ts
class ExtensionService (line 11) | class ExtensionService implements IExtensionService {
method constructor (line 21) | constructor(
method getExtensionConfig (line 43) | getExtensionConfig<T>(id: string): T | undefined {
method setExtensionConfig (line 51) | async setExtensionConfig(id: string, data: any): Promise<void> {
method toggleDefault (line 57) | async toggleDefault(id: string) {
method toggleDisableExtension (line 65) | async toggleDisableExtension(id: string) {
method toggleAutomaticExtension (line 69) | async toggleAutomaticExtension(id: string) {
method toggleStorageData (line 73) | private async toggleStorageData(key: string, id: string) {
method init (line 82) | async init() {
FILE: src/service/ipc/browser/background-main/ipcService.ts
class BackgroundIPCServer (line 4) | class BackgroundIPCServer implements IChannelServer {
method registerChannel (line 5) | public registerChannel(channelName: string, server: IServerChannel) {
FILE: src/service/ipc/browser/contentScript/contentScriptIPCServer.ts
class ContentScriptIPCServer (line 9) | class ContentScriptIPCServer implements IChannelServer {
method registerChannel (line 10) | public registerChannel(
FILE: src/service/ipc/browser/popup/ipcClient.ts
class PopupIpcClient (line 11) | class PopupIpcClient implements IChannelClient {
method getChannel (line 12) | getChannel(channelName: string) {
class PopupContentScriptChannelClient (line 17) | class PopupContentScriptChannelClient implements IChannel {
method constructor (line 18) | constructor(private namespace: string, private tabService: ITabService...
method call (line 20) | async call<T>(command: string, arg?: any): Promise<T> {
class PopupContentScriptIPCClient (line 53) | class PopupContentScriptIPCClient implements IChannelClient {
method constructor (line 54) | constructor(private tabService: ITabService) {}
method getChannel (line 55) | getChannel(channelName: string) {
FILE: src/service/permissions/chrome/permissionsService.ts
class PermissionsService (line 4) | class PermissionsService implements IPermissionsService {
method contains (line 5) | contains(p: Permissions) {
method remove (line 15) | remove(p: Permissions) {
method request (line 25) | request(p: Permissions) {
FILE: src/service/permissions/common/permissionsIpc.ts
class PermissionsChannel (line 4) | class PermissionsChannel implements IServerChannel {
method constructor (line 5) | constructor(private service: IPermissionsService) {}
class PermissionsChannelClient (line 26) | class PermissionsChannelClient implements IPermissionsService {
method constructor (line 27) | constructor(private channel: IChannel) {}
FILE: src/service/preference/browser/preferenceService.ts
class PreferenceService (line 8) | class PreferenceService implements IPreferenceService {
method constructor (line 14) | constructor(@Inject(ISyncStorageService) private syncStorageService: I...
FILE: src/service/request/common/request.ts
class RequestHelper (line 13) | class RequestHelper implements IExtendRequestHelper {
method constructor (line 14) | constructor(private options: IHelperOptions) {
method post (line 18) | post<T>(url: string, options: Omit<IPostRequestOptions, 'method' | 're...
method postForm (line 26) | postForm<T>(url: string, options: Omit<IPostFormRequestOptions, 'metho...
method put (line 34) | put<T>(url: string, options: Omit<IPutRequestOptions, 'method'>) {
method get (line 41) | get<T>(url: string, options?: Omit<IGetFormRequestOptions, 'method'>) {
method request (line 48) | private async request<T>(url: string, options: TRequestOption): Promis...
method basicRequestInterceptors (line 76) | private basicRequestInterceptors(
FILE: src/service/request/tool/basic.ts
class BasicRequestService (line 5) | class BasicRequestService implements IRequestService {
method constructor (line 7) | constructor() {
method download (line 11) | async download(url: string) {
method request (line 26) | request(url: string, options: TRequestOption) {
FILE: src/service/tab/browser/background/tabService.ts
class ChromeTabService (line 10) | class ChromeTabService extends AbstractTabService {
method getCurrent (line 11) | getCurrent() {
method remove (line 23) | remove(tabId: number): Promise<void> {
method captureVisibleTab (line 27) | captureVisibleTab(option: CaptureVisibleTabOptions | number) {
method sendMessage (line 31) | sendMessage<T>(tabId: number, message: any): Promise<T> {
method create (line 35) | create(createProperties: chrome.tabs.CreateProperties): Promise<chrome...
FILE: src/service/tab/common/tabIpc.ts
class TabChannel (line 9) | class TabChannel implements IServerChannel {
method constructor (line 10) | constructor(private service: ITabService) {}
class TabChannelClient (line 35) | class TabChannelClient extends AbstractTabService {
method constructor (line 36) | constructor(private channel: IChannel) {
FILE: src/service/webRequest/browser/background/tabService.ts
class BackgroundWebRequestService (line 11) | class BackgroundWebRequestService implements IWebRequestService {
method constructor (line 15) | constructor() {
method getRuleId (line 20) | private getRuleId() {
method startChangeHeader (line 24) | async startChangeHeader(option: WebRequestBlockOption): Promise<WebBlo...
method requestInBackground (line 56) | requestInBackground<T>(url: string, options: RequestInBackgroundOption...
method changeUrl (line 60) | async changeUrl(url: string, query: WebBlockHeader): Promise<string> {
method end (line 64) | async end(webBlockHeader: WebBlockHeader): Promise<void> {
FILE: src/service/webRequest/chrome/background/tabService.ts
class ChromeBackgroundWebRequestService (line 5) | class ChromeBackgroundWebRequestService extends BackgroundWebRequestServ...
method constructor (line 6) | constructor() {
FILE: src/service/webRequest/common/webRequestIPC.ts
class WebRequestChannel (line 9) | class WebRequestChannel implements IServerChannel {
method constructor (line 10) | constructor(private service: IWebRequestService) {}
class WebRequestChannelClient (line 34) | class WebRequestChannelClient implements IWebRequestService {
method constructor (line 35) | constructor(private channel: IChannel) {}
FILE: src/service/worker/common/index.ts
type IWorkerService (line 3) | interface IWorkerService {
FILE: src/service/worker/common/workserServiceIPC.ts
class WorkerServiceChannel (line 4) | class WorkerServiceChannel implements IServerChannel {
method constructor (line 5) | constructor(private service: IWorkerService) {}
class WorkerServiceChannelClient (line 20) | class WorkerServiceChannelClient implements IWorkerService {
method constructor (line 21) | constructor(private channel: IChannel) {}
FILE: src/service/worker/worker/workerService.ts
class WorkerService (line 6) | class WorkerService implements IWorkerService {
method constructor (line 7) | constructor() {}
method changeIcon (line 8) | async changeIcon(iconColor: string): Promise<void> {
method initContextMenu (line 15) | async initContextMenu(): Promise<void> {
FILE: src/services/account/common.ts
function unpackAccountPreference (line 3) | function unpackAccountPreference(account: AccountPreference) {
FILE: src/services/configuration/common/configuration.ts
type IWebClipperConfiguration (line 3) | interface IWebClipperConfiguration {
type IConfigurationService (line 10) | interface IConfigurationService {
FILE: src/services/environment/common/environment.ts
type IEnvironmentService (line 5) | interface IEnvironmentService {
FILE: src/services/environment/common/environmentService.ts
type Locale (line 24) | type Locale = 'en-US' | 'zh-CN';
function keys (line 26) | function keys<T>(data: T): (keyof T)[] {
class EnvironmentService (line 30) | class EnvironmentService implements IEnvironmentService {
method constructor (line 31) | constructor(@Inject(ILocaleService) private localeService: ILocaleServ...
method privacy (line 33) | async privacy(): Promise<string> {
method changelog (line 41) | async changelog(): Promise<string> {
FILE: src/services/log/common/index.ts
type LogLevel (line 1) | enum LogLevel {
constant DEFAULT_LOG_LEVEL (line 11) | const DEFAULT_LOG_LEVEL: LogLevel = LogLevel.Info;
type ILogger (line 13) | interface ILogger {
FILE: src/vendor/global.d.ts
type PromiseType (line 8) | type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? ...
type Unpack (line 10) | type Unpack<T> = T extends Promise<infer U> ? U : T;
type CallResult (line 12) | type CallResult<T extends (...args: any[]) => any> = Unpack<ReturnType<T>>;
type Type (line 14) | interface Type<T> extends Function {
type Window (line 20) | interface Window {
FILE: webpack/plugin/webpack-create-extension-manifest-plugin.js
function getPackageJsonVersion (line 4) | async function getPackageJsonVersion() {
class WebpackCreateExtensionManifestPlugin (line 10) | class WebpackCreateExtensionManifestPlugin {
method constructor (line 11) | constructor({ output, extra }) {
method apply (line 15) | apply(compiler) {
function getChromeManifest (line 30) | async function getChromeManifest() {
function getFirefoxManifest (line 69) | async function getFirefoxManifest() {
FILE: webpack/webpack.common.js
function resolve (line 11) | function resolve(dir) {
method chunks (line 33) | chunks(chunk) {
Condensed preview — 346 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (778K chars).
[
{
"path": ".dockerignore",
"chars": 26,
"preview": "node_modules\nrelease\nlib\n\n"
},
{
"path": ".eslintignore",
"chars": 63,
"preview": "lib/\ndist/\ncoverage/\nnode_modules/\nchrome/js/icon.js\nreleases/\n"
},
{
"path": ".eslintrc.js",
"chars": 434,
"preview": "module.exports = {\n extends: ['@diamondyuan/react-typescript', 'prettier'],\n plugins: ['eslint-plugin-prettier'],\n ru"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report_en-US.md",
"chars": 641,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\n---\n\n**Describe the bug**\n\nA clear and concise descriptio"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report_zh-CN.md",
"chars": 317,
"preview": "---\nname: Bug 报告\nabout: 创建报告以帮助我们改进\n---\n\n**Bug 描述**\n\n清楚简明地描述错误是什么。\n\n**复现步骤**\n\n重现的步骤:\n\n1. 打开 '...'\n2. 点击按钮 '....'\n3. 滚动到 "
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request_en-US.md",
"chars": 563,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\n---\n\n**Is your feature request related to a problem? P"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request_zh-CN.md",
"chars": 214,
"preview": "---\nname: 功能请求\nabout: 为这个项目提出一个想法\n---\n\n**您的功能要求与问题有关吗? 请描述。**\n\n清楚,简洁地说明问题所在。 例如 当[...]时,我总是感到沮丧\n\n**描述您想要的解决方案**\n\n对您想要发生的"
},
{
"path": ".github/workflows/ci.yml",
"chars": 503,
"preview": "name: CI Test\n\non:\n pull_request_target:\n types: [opened, edited, reopened]\n push:\n branches:\n - '**'\n\njobs"
},
{
"path": ".gitignore",
"chars": 143,
"preview": "npm-debug.log\nnode_modules/\ndist/*\n!dist/.gitkeep\nlib/\ncoverage/\ntmp/\n.DS_Store\n.idea\n.vscode\nyarn-error.log\ndll/\nwebcli"
},
{
"path": ".prettierrc",
"chars": 97,
"preview": "{\n \"singleQuote\": true,\n \"trailingComma\": \"es5\",\n \"printWidth\": 100,\n \"proseWrap\": \"never\"\n}\n"
},
{
"path": ".yarnrc",
"chars": 48,
"preview": "version-git-message \"chore(release): %s :tada:\"\n"
},
{
"path": "LICENSE",
"chars": 162,
"preview": "Software License Agreement\nCopyright (c) 2020-2020, DiamondYuan. All rights reserved.\n\nLicensed under the terms of GNU G"
},
{
"path": "README.md",
"chars": 2704,
"preview": "<h1 align=\"center\">Web Clipper</h1>\n<p align=\"center\">\n <a href=\"https://github.com/webclipper/web-clipper/actions\">\n"
},
{
"path": "bin/index.js",
"chars": 96,
"preview": "#!/usr/bin/env node\nrequire('ts-node').register({\n transpileOnly: true,\n});\nrequire('./main');\n"
},
{
"path": "bin/main.ts",
"chars": 246,
"preview": "import { hideBin } from 'yargs/helpers';\nimport { format } from './scripts';\n\nconst [command] = hideBin(process.argv);\n\n"
},
{
"path": "bin/scripts/format.ts",
"chars": 764,
"preview": "import * as fs from 'fs';\nimport * as path from 'path';\n\nexport function format() {\n const localsPath = path.resolve(__"
},
{
"path": "bin/scripts/index.ts",
"chars": 35,
"preview": "export { format } from './format';\n"
},
{
"path": "chrome/html/error.html",
"chars": 1183,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, i"
},
{
"path": "chrome/js/icon.js",
"chars": 84032,
"preview": "window._iconfont_svg_string_1402208='<svg><symbol id=\"obsidian\" viewBox=\"0 0 1024 1024\"><path d=\"M574.34 7.74l-313.83 16"
},
{
"path": "config.json",
"chars": 262,
"preview": "{\n \"iconfont\": \"https://at.alicdn.com/t/font_1402208_ghcp6tuu13c.js\",\n \"chromeWebStoreVersion\": \"1.28.4\",\n \"edgeWebSt"
},
{
"path": "global.d.ts",
"chars": 69,
"preview": "declare module '*.md' {\n const src: string;\n export default src;\n}\n"
},
{
"path": "package.json",
"chars": 3356,
"preview": "{\n \"name\": \"web-clipper\",\n \"version\": \"1.42.0\",\n \"description\": \"Universal open source web clipper.\",\n \"bin\": {\n "
},
{
"path": "script/build.js",
"chars": 370,
"preview": "const webpack = require('webpack');\nconst prodConfig = require('../webpack/webpack.prod');\nconst compiler = webpack(prod"
},
{
"path": "script/release.ts",
"chars": 1408,
"preview": "import { fork } from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\nimport { pack } from './utils/pack';"
},
{
"path": "script/utils/pack.ts",
"chars": 659,
"preview": "import compressing from 'compressing';\nimport fs from 'fs';\nimport path from 'path';\nconst pump = require('pump');\ninter"
},
{
"path": "src/__test__/utils.ts",
"chars": 702,
"preview": "import { IRequestService, TRequestOption } from '@/service/common/request';\nimport { vi, Mock } from 'vitest';\n\ntype TMo"
},
{
"path": "src/actions/account.ts",
"chars": 1255,
"preview": "import { UserInfo } from '@/common/backend/services/interface';\nimport { AccountPreference } from '@/common/types';\nimpo"
},
{
"path": "src/actions/clipper.ts",
"chars": 1198,
"preview": "import { ClipperHeaderForm } from 'common/modelTypes/clipper';\nimport { Repository, CompleteStatus, CreateDocumentReques"
},
{
"path": "src/actions/userPreference.ts",
"chars": 1985,
"preview": "import { IExtensionWithId } from './../extensions/common';\nimport { ImageHosting, GlobalStore } from '@/common/types';\ni"
},
{
"path": "src/common/backend/clients/github/client.test.ts",
"chars": 4907,
"preview": "import { MockRequestService } from '@/__test__/utils';\nimport { GithubClient } from './client';\nimport { IRepository } f"
},
{
"path": "src/common/backend/clients/github/client.ts",
"chars": 3293,
"preview": "import { IExtendRequestHelper } from '@/service/common/request';\nimport { RequestHelper } from '@/service/request/common"
},
{
"path": "src/common/backend/clients/github/types.ts",
"chars": 1480,
"preview": "import { IRequestService } from '@/service/common/request';\n\nexport interface IGithubClientOptions {\n token: string;\n "
},
{
"path": "src/common/backend/clients/joplin/LegacyJoplinClient.ts",
"chars": 1738,
"preview": "import { Repository } from './../../services/interface';\nimport { JoplinFolderItem, JoplinTag, JoplinCreateDocumentReque"
},
{
"path": "src/common/backend/clients/joplin/basic.ts",
"chars": 926,
"preview": "import { generateUuid } from '@web-clipper/shared/lib/uuid';\nimport { IExtendRequestHelper } from '@/service/common/requ"
},
{
"path": "src/common/backend/clients/joplin/client.ts",
"chars": 2593,
"preview": "import { AbstractJoplinClient } from './basic';\nimport {\n Repository,\n JoplinFolderItem,\n JoplinTag,\n JoplinCreateDo"
},
{
"path": "src/common/backend/clients/joplin/index.ts",
"chars": 125,
"preview": "export { JoplinClient } from './client';\nexport { LegacyJoplinClient } from './LegacyJoplinClient';\nexport * from './typ"
},
{
"path": "src/common/backend/clients/joplin/types.ts",
"chars": 1244,
"preview": "import type { Repository, CreateDocumentRequest } from '../../services/interface';\n\nexport type { Repository } from '../"
},
{
"path": "src/common/backend/clients/leanote/client.test.ts",
"chars": 2691,
"preview": "import { MockRequestService } from '@/__test__/utils';\nimport LeanoteClient from './client';\nconst FormData = require('f"
},
{
"path": "src/common/backend/clients/leanote/client.ts",
"chars": 4428,
"preview": "import { IExtendRequestHelper, IRequestService } from '@/service/common/request';\nimport { CreateDocumentRequest } from "
},
{
"path": "src/common/backend/clients/leanote/interface.ts",
"chars": 452,
"preview": "export interface LeanoteBackendServiceConfig {\n leanote_host: string;\n email: string;\n pwd: string;\n token_cached: s"
},
{
"path": "src/common/backend/clients/siyuan/client.ts",
"chars": 2318,
"preview": "import { CreateDocumentRequest } from './../../services/interface';\nimport { IExtendRequestHelper } from '@/service/comm"
},
{
"path": "src/common/backend/clients/siyuan/types.ts",
"chars": 483,
"preview": "import { IRequestService } from '@/service/common/request';\n\nexport interface ISiyuanClientOptions {\n request: IRequest"
},
{
"path": "src/common/backend/imageHosting/baklib/index.ts",
"chars": 572,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/baklib/service.ts",
"chars": 2332,
"preview": "import { Base64ImageToBlob, BlobToBase64 } from '@/common/blob';\nimport { UploadImageRequest, ImageHostingService } from"
},
{
"path": "src/common/backend/imageHosting/github/form.tsx",
"chars": 6885,
"preview": "import React, { Fragment } from 'react';\r\nimport { FormComponentProps } from '@ant-design/compatible/lib/form';\r\nimport "
},
{
"path": "src/common/backend/imageHosting/github/index.ts",
"chars": 368,
"preview": "import { ImageHostingServiceMeta } from '../interface';\r\nimport Service from './service';\r\nimport Form from './form';\r\n\r"
},
{
"path": "src/common/backend/imageHosting/github/service.ts",
"chars": 2850,
"preview": "import { generateUuid } from '@web-clipper/shared/lib/uuid';\r\nimport { BlobToBase64 } from '@/common/blob';\r\nimport { Up"
},
{
"path": "src/common/backend/imageHosting/github/type.ts",
"chars": 124,
"preview": "export interface GithubImageHostingOption {\n accessToken: string;\n repo: string;\n branch?: string;\n savePath: string"
},
{
"path": "src/common/backend/imageHosting/imgur/form.tsx",
"chars": 726,
"preview": "import React from 'react';\nimport { FormComponentProps } from '@ant-design/compatible/lib/form';\nimport { Form } from '@"
},
{
"path": "src/common/backend/imageHosting/imgur/index.ts",
"chars": 348,
"preview": "import Form from './form';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service from './service';\n\nexp"
},
{
"path": "src/common/backend/imageHosting/imgur/service.ts",
"chars": 1354,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { RequestHelper } from '@/service/request/common"
},
{
"path": "src/common/backend/imageHosting/interface.ts",
"chars": 816,
"preview": "import { Repository } from '../services/interface';\n\nexport interface ImageHostingServiceConstructAble {\n new (info: an"
},
{
"path": "src/common/backend/imageHosting/joplin/index.ts",
"chars": 540,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/joplin/service.ts",
"chars": 1383,
"preview": "import { RequestHelper } from '@/service/request/common/request';\nimport { JoplinClient } from './../../clients/joplin/i"
},
{
"path": "src/common/backend/imageHosting/leanote/index.ts",
"chars": 480,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/leanote/service.ts",
"chars": 1453,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { Base64ImageToBlob } from '@/common/blob';\nimpo"
},
{
"path": "src/common/backend/imageHosting/piclist/form.tsx",
"chars": 1024,
"preview": "import React, { Fragment } from 'react';\nimport { FormComponentProps } from '@ant-design/compatible/lib/form';\nimport { "
},
{
"path": "src/common/backend/imageHosting/piclist/index.ts",
"chars": 381,
"preview": "import Form from './form';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service from './service';\n\nexp"
},
{
"path": "src/common/backend/imageHosting/piclist/service.ts",
"chars": 3140,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { RequestHelper } from '@/service/request/common"
},
{
"path": "src/common/backend/imageHosting/qcloud/form.tsx",
"chars": 2656,
"preview": "import React, { Fragment } from 'react';\nimport { FormComponentProps } from '@ant-design/compatible/lib/form';\nimport { "
},
{
"path": "src/common/backend/imageHosting/qcloud/index.ts",
"chars": 455,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/qcloud/service.ts",
"chars": 2565,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { Base64ImageToBlob } from '@/common/blob';\nimpo"
},
{
"path": "src/common/backend/imageHosting/siyuan/index.ts",
"chars": 545,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/siyuan/service.ts",
"chars": 1350,
"preview": "import { Base64ImageToBlob } from '@/common/blob';\nimport { Container } from 'typedi';\nimport { IBasicRequestService } f"
},
{
"path": "src/common/backend/imageHosting/sm.ms/form.tsx",
"chars": 662,
"preview": "import React from 'react';\nimport { FormComponentProps } from '@ant-design/compatible/lib/form';\nimport { Form } from '@"
},
{
"path": "src/common/backend/imageHosting/sm.ms/index.ts",
"chars": 333,
"preview": "import { ImageHostingServiceMeta } from '../interface';\nimport Service from './service';\nimport form from './form';\n\nexp"
},
{
"path": "src/common/backend/imageHosting/sm.ms/service.ts",
"chars": 2162,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { Base64ImageToBlob } from '@/common/blob';\nimpo"
},
{
"path": "src/common/backend/imageHosting/wiznote/index.ts",
"chars": 480,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/wiznote/service.ts",
"chars": 1073,
"preview": "import { UploadImageRequest, ImageHostingService } from '../interface';\nimport { Base64ImageToBlob } from 'common/blob';"
},
{
"path": "src/common/backend/imageHosting/yuque_oauth/index.ts",
"chars": 490,
"preview": "import localeService from '@/common/locales';\nimport { ImageHostingServiceMeta } from '../interface';\nimport Service fro"
},
{
"path": "src/common/backend/imageHosting/yuque_oauth/service.ts",
"chars": 2131,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { Base64ImageToBlob } from '@/common/blob';\nimpo"
},
{
"path": "src/common/backend/index.ts",
"chars": 1780,
"preview": "import {\n ServiceMeta,\n ImageHostingServiceMeta,\n ImageHostingService,\n DocumentService,\n} from './interface';\nexpor"
},
{
"path": "src/common/backend/interface.ts",
"chars": 80,
"preview": "export * from './imageHosting/interface';\nexport * from './services/interface';\n"
},
{
"path": "src/common/backend/services/baklib/complete.tsx",
"chars": 246,
"preview": "import React from 'react';\n\nexport default ({ status: { edit_url } }: any) => {\n return (\n <div style={{ marginTop: "
},
{
"path": "src/common/backend/services/baklib/form.tsx",
"chars": 2024,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/baklib/headerForm.tsx",
"chars": 1824,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { TreeSelect } "
},
{
"path": "src/common/backend/services/baklib/index.ts",
"chars": 514,
"preview": "import { ServiceMeta } from './../interface';\nimport Service from './service';\nimport Form from './form';\nimport localeS"
},
{
"path": "src/common/backend/services/baklib/interface.ts",
"chars": 279,
"preview": "export interface BaklibBackendServiceConfig {\n accessToken: string;\n origin: string;\n}\n\nexport interface BaklibTenants"
},
{
"path": "src/common/backend/services/baklib/service.ts",
"chars": 3578,
"preview": "import { DocumentService, CreateDocumentRequest } from './../../index';\nimport { extend, RequestMethod } from 'umi-reque"
},
{
"path": "src/common/backend/services/bear/form.tsx",
"chars": 297,
"preview": "import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default () => (\n <div style={{ textAl"
},
{
"path": "src/common/backend/services/bear/index.ts",
"chars": 230,
"preview": "import Service from './service';\nimport Form from './form';\n\nexport default () => {\n return {\n name: 'Bear',\n ico"
},
{
"path": "src/common/backend/services/bear/service.ts",
"chars": 954,
"preview": "import { CompleteStatus } from 'common/backend/interface';\nimport { DocumentService, CreateDocumentRequest } from '../.."
},
{
"path": "src/common/backend/services/buildin/index.ts",
"chars": 495,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport Service from './service';\n\nexport const buildinOrigin = 'https://"
},
{
"path": "src/common/backend/services/buildin/service.ts",
"chars": 14126,
"preview": "import { CompleteStatus, UnauthorizedError } from '../interface';\nimport { DocumentService, CreateDocumentRequest } from"
},
{
"path": "src/common/backend/services/buildin/type.ts",
"chars": 1842,
"preview": "import { Repository } from '../interface';\n\ninterface Space {\n uuid: string;\n title: string;\n subNodes: string[];\n p"
},
{
"path": "src/common/backend/services/confluence/form.tsx",
"chars": 2893,
"preview": "import React from 'react';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.le"
},
{
"path": "src/common/backend/services/confluence/index.ts",
"chars": 213,
"preview": "import Service from './service';\nimport Form from './form';\n\nexport default () => {\n return {\n name: 'Confluence',\n "
},
{
"path": "src/common/backend/services/confluence/interface.ts",
"chars": 754,
"preview": "export interface ConfluenceListResult<T> {\n results: T[];\n start: number;\n limit: number;\n size: number;\n}\n\nexport i"
},
{
"path": "src/common/backend/services/confluence/service.ts",
"chars": 2420,
"preview": "import md5 from '@web-clipper/shared/lib/md5';\nimport {\n ConfluenceServiceConfig,\n ConfluenceUserInfo,\n ConfluenceLis"
},
{
"path": "src/common/backend/services/dida365/headerForm.tsx",
"chars": 1414,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Select } from"
},
{
"path": "src/common/backend/services/dida365/index.ts",
"chars": 480,
"preview": "import localeService from '@/common/locales';\nimport { ServiceMeta } from '@/common/backend';\nimport Service from './ser"
},
{
"path": "src/common/backend/services/dida365/service.ts",
"chars": 4611,
"preview": "import { Container } from 'typedi';\nimport { generateUuid } from '@web-clipper/shared/lib/uuid';\nimport localeService fr"
},
{
"path": "src/common/backend/services/flomo/index.ts",
"chars": 294,
"preview": "import Service from './service';\n\nexport default () => {\n return {\n name: 'Flomo',\n icon: 'flomo',\n type: 'flo"
},
{
"path": "src/common/backend/services/flomo/service.ts",
"chars": 2264,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { CompleteStatus } from 'common/backend/interfac"
},
{
"path": "src/common/backend/services/flowus/index.ts",
"chars": 440,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport Service from './service';\n\nexport const flowusOrigin = 'https://f"
},
{
"path": "src/common/backend/services/flowus/service.ts",
"chars": 14606,
"preview": "import { CompleteStatus, UnauthorizedError } from '../interface';\nimport { DocumentService, CreateDocumentRequest } from"
},
{
"path": "src/common/backend/services/flowus/type.ts",
"chars": 1837,
"preview": "import { Repository } from '../interface';\n\ninterface Space {\n uuid: string;\n title: string;\n subNodes: string[];\n p"
},
{
"path": "src/common/backend/services/github/form.tsx",
"chars": 3290,
"preview": "import { KeyOutlined } from '@ant-design/icons';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/comp"
},
{
"path": "src/common/backend/services/github/headerForm.tsx",
"chars": 1709,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Select, Badge"
},
{
"path": "src/common/backend/services/github/index.ts",
"chars": 455,
"preview": "import { ServiceMeta } from './../interface';\r\nimport Service from './service';\r\nimport Form from './form';\r\nimport head"
},
{
"path": "src/common/backend/services/github/interface.ts",
"chars": 730,
"preview": "import { Repository, CreateDocumentRequest } from '../interface';\n\nexport interface GithubBackendServiceConfig {\n acces"
},
{
"path": "src/common/backend/services/github/service.ts",
"chars": 3452,
"preview": "import { CompleteStatus } from 'common/backend/interface';\nimport {\n GithubBackendServiceConfig,\n GithubUserInfoRespon"
},
{
"path": "src/common/backend/services/github_repository/form.tsx",
"chars": 4165,
"preview": "import { KeyOutlined } from '@ant-design/icons';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/comp"
},
{
"path": "src/common/backend/services/github_repository/index.ts",
"chars": 417,
"preview": "import { ServiceMeta } from '../interface';\r\nimport Service from './service';\r\nimport Form from './form';\r\n\r\nexport defa"
},
{
"path": "src/common/backend/services/github_repository/interface.ts",
"chars": 667,
"preview": "import { Repository, CreateDocumentRequest } from '../interface';\n\nexport interface GithubBackendServiceConfig {\n acces"
},
{
"path": "src/common/backend/services/github_repository/service.ts",
"chars": 4568,
"preview": "import { CompleteStatus } from 'common/backend/interface';\nimport {\n GithubBackendServiceConfig,\n GithubUserInfoRespon"
},
{
"path": "src/common/backend/services/interface.ts",
"chars": 2264,
"preview": "export interface CreateDocumentRequest {\n title: string;\n content: string;\n url?: string;\n repositoryId: string;\n}\n\n"
},
{
"path": "src/common/backend/services/joplin/form.tsx",
"chars": 1349,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input, Checkb"
},
{
"path": "src/common/backend/services/joplin/headerForm.tsx",
"chars": 1380,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Select } from"
},
{
"path": "src/common/backend/services/joplin/index.ts",
"chars": 534,
"preview": "import { ServiceMeta } from './../interface';\nimport Service from './service';\nimport Form from './form';\nimport localeS"
},
{
"path": "src/common/backend/services/joplin/service.ts",
"chars": 2483,
"preview": "import { RequestHelper } from '@/service/request/common/request';\nimport { IBasicRequestService } from './../../../../se"
},
{
"path": "src/common/backend/services/leanote/form.tsx",
"chars": 2506,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/leanote/index.ts",
"chars": 459,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport localeService from '@/common/locales';\nimport Service from './ser"
},
{
"path": "src/common/backend/services/leanote/service.ts",
"chars": 2817,
"preview": "import { CompleteStatus } from 'common/backend/interface';\nimport { DocumentService, CreateDocumentRequest } from '../.."
},
{
"path": "src/common/backend/services/memos/form.tsx",
"chars": 2412,
"preview": "import { Form } from '@ant-design/compatible';\r\nimport '@ant-design/compatible/assets/index.less';\r\nimport { Input } fro"
},
{
"path": "src/common/backend/services/memos/headerForm.tsx",
"chars": 2056,
"preview": "import { Input, Tooltip, Select } from 'antd';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/compat"
},
{
"path": "src/common/backend/services/memos/index.ts",
"chars": 486,
"preview": "import { ServiceMeta } from '../interface';\r\nimport Service from './service';\r\nimport Form from './form';\r\nimport locale"
},
{
"path": "src/common/backend/services/memos/interface.ts",
"chars": 992,
"preview": "import { CreateDocumentRequest } from './../interface';\r\nimport locales from '@/common/locales';\r\n\r\nexport const Visibil"
},
{
"path": "src/common/backend/services/memos/service.ts",
"chars": 3312,
"preview": "import { DocumentService } from '../../index';\r\nimport { extend, RequestMethod } from 'umi-request';\r\nimport { CompleteS"
},
{
"path": "src/common/backend/services/notion/index.ts",
"chars": 394,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport Service from './service';\n\nexport default (): ServiceMeta => {\n "
},
{
"path": "src/common/backend/services/notion/service.ts",
"chars": 12406,
"preview": "import localeService from '@/common/locales';\nimport { ICookieService } from '@/service/common/cookie';\nimport { IWebReq"
},
{
"path": "src/common/backend/services/notion/types.ts",
"chars": 1446,
"preview": "import { Repository } from '../interface';\n\nexport interface NotionUserContent {\n recordMap: {\n notion_user: {\n "
},
{
"path": "src/common/backend/services/obsidian/form.tsx",
"chars": 1422,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { FormComponent"
},
{
"path": "src/common/backend/services/obsidian/index.ts",
"chars": 409,
"preview": "import localeService from '@/common/locales';\nimport { ServiceMeta } from './../interface';\nimport Service from './servi"
},
{
"path": "src/common/backend/services/obsidian/interface.ts",
"chars": 75,
"preview": "export interface ObsidianFormConfig {\n vault: string;\n folder: string;\n}\n"
},
{
"path": "src/common/backend/services/obsidian/service.ts",
"chars": 1359,
"preview": "import md5 from '@web-clipper/shared/lib/md5';\nimport { CreateDocumentRequest, DocumentService } from '../../index';\nimp"
},
{
"path": "src/common/backend/services/onenote_oauth/form.tsx",
"chars": 1530,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/onenote_oauth/index.ts",
"chars": 1124,
"preview": "import { IConfigService } from '@/service/common/config';\nimport { Container } from 'typedi';\nimport config from '@/conf"
},
{
"path": "src/common/backend/services/onenote_oauth/interface.ts",
"chars": 666,
"preview": "export interface OneNoteBackendServiceConfig {\n refresh_token: string;\n access_token: string;\n}\n\nexport interface OneN"
},
{
"path": "src/common/backend/services/onenote_oauth/service.ts",
"chars": 5600,
"preview": "import { DocumentService, CreateDocumentRequest } from './../../index';\nimport axios, { AxiosInstance } from 'axios';\nim"
},
{
"path": "src/common/backend/services/server_chan/form.tsx",
"chars": 1454,
"preview": "import { KeyOutlined } from '@ant-design/icons';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/comp"
},
{
"path": "src/common/backend/services/server_chan/index.ts",
"chars": 425,
"preview": "import Service from './service';\nimport Form from './form';\nimport localeService from '@/common/locales';\n\nexport defaul"
},
{
"path": "src/common/backend/services/server_chan/service.ts",
"chars": 1601,
"preview": "import localeService from '@/common/locales';\nimport { CompleteStatus } from 'common/backend/interface';\nimport { Docume"
},
{
"path": "src/common/backend/services/siyuan/form.tsx",
"chars": 1051,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/siyuan/index.ts",
"chars": 593,
"preview": "import localeService from '@/common/locales';\r\nimport { ServiceMeta } from './../interface';\r\nimport Service from './ser"
},
{
"path": "src/common/backend/services/siyuan/service.ts",
"chars": 1543,
"preview": "import { CompleteStatus } from 'common/backend/interface';\nimport { Repository, CreateDocumentRequest } from './../inter"
},
{
"path": "src/common/backend/services/ticktick/headerForm.tsx",
"chars": 1414,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Select } from"
},
{
"path": "src/common/backend/services/ticktick/index.ts",
"chars": 517,
"preview": "import localeService from '@/common/locales';\nimport { ServiceMeta } from '@/common/backend';\nimport Service from './ser"
},
{
"path": "src/common/backend/services/ticktick/service.ts",
"chars": 4546,
"preview": "import { Container } from 'typedi';\nimport { generateUuid } from '@web-clipper/shared/lib/uuid';\nimport localeService fr"
},
{
"path": "src/common/backend/services/ulysses/form.tsx",
"chars": 303,
"preview": "import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default () => (\n <div style={{ textAl"
},
{
"path": "src/common/backend/services/ulysses/index.ts",
"chars": 204,
"preview": "import Service from './service';\nimport Form from './form';\n\nexport default () => {\n return {\n name: 'Ulysses',\n "
},
{
"path": "src/common/backend/services/ulysses/service.ts",
"chars": 1012,
"preview": "import { ITabService } from './../../../../service/common/tab';\nimport { CompleteStatus } from 'common/backend/interface"
},
{
"path": "src/common/backend/services/webdav/form.tsx",
"chars": 2057,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/webdav/index.ts",
"chars": 219,
"preview": "import Service from './service';\nimport Form from './form';\n\nexport default () => {\n return {\n name: 'WebDAV',\n i"
},
{
"path": "src/common/backend/services/webdav/interface.ts",
"chars": 99,
"preview": "export interface WebDAVServiceConfig {\n origin: string;\n username: string;\n password: string;\n}\n"
},
{
"path": "src/common/backend/services/webdav/service.ts",
"chars": 1857,
"preview": "import { WebDAVServiceConfig } from './interface';\nimport { DocumentService, CreateDocumentRequest, Repository } from '."
},
{
"path": "src/common/backend/services/wiznote/form.tsx",
"chars": 1387,
"preview": "import React from 'react';\nimport { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.le"
},
{
"path": "src/common/backend/services/wiznote/headerForm.tsx",
"chars": 1419,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Select } from"
},
{
"path": "src/common/backend/services/wiznote/index.ts",
"chars": 425,
"preview": "import localeService from '@/common/locales';\nimport Service from './service';\nimport Form from './form';\nimport headerF"
},
{
"path": "src/common/backend/services/wiznote/interface.ts",
"chars": 663,
"preview": "import { CreateDocumentRequest } from '../interface';\n\nexport interface WizNoteConfig {\n origin: string;\n spaceId: num"
},
{
"path": "src/common/backend/services/wiznote/service.ts",
"chars": 7676,
"preview": "import { IWebRequestService, RequestInBackgroundOptions } from '@/service/common/webRequest';\nimport { Container } from "
},
{
"path": "src/common/backend/services/wolai/index.ts",
"chars": 391,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport Service from './service';\n\nexport default (): ServiceMeta => {\n "
},
{
"path": "src/common/backend/services/wolai/service.ts",
"chars": 8907,
"preview": "import { CompleteStatus, UnauthorizedError } from '../interface';\nimport { DocumentService, CreateDocumentRequest } from"
},
{
"path": "src/common/backend/services/wolai/type.ts",
"chars": 1775,
"preview": "import { Repository } from '../interface';\n\nexport interface WolaiUserContent {\n code: number;\n message: string;\n dat"
},
{
"path": "src/common/backend/services/youdao/index.ts",
"chars": 542,
"preview": "import { ServiceMeta } from '@/common/backend';\nimport localeService from '@/common/locales';\nimport Service from './ser"
},
{
"path": "src/common/backend/services/youdao/service.ts",
"chars": 4890,
"preview": "import { CompleteStatus, UnauthorizedError } from './../interface';\nimport { DocumentService, Repository, CreateDocument"
},
{
"path": "src/common/backend/services/yuque/form.tsx",
"chars": 2591,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input, Select"
},
{
"path": "src/common/backend/services/yuque/headerForm.tsx",
"chars": 958,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/yuque/index.ts",
"chars": 572,
"preview": "import { ServiceMeta } from './../interface';\nimport Service from './service';\nimport Form from './form';\nimport localeS"
},
{
"path": "src/common/backend/services/yuque/interface.ts",
"chars": 1207,
"preview": "import { CompleteStatus, CreateDocumentRequest, UpdateTOCRequest } from './../interface';\nimport { Repository } from '.."
},
{
"path": "src/common/backend/services/yuque/service.ts",
"chars": 5244,
"preview": "import { IBasicRequestService } from '@/service/common/request';\nimport { Container } from 'typedi';\nimport { RequestHel"
},
{
"path": "src/common/backend/services/yuque_oauth/form.tsx",
"chars": 2615,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input, Select"
},
{
"path": "src/common/backend/services/yuque_oauth/headerForm.tsx",
"chars": 958,
"preview": "import { Form } from '@ant-design/compatible';\nimport '@ant-design/compatible/assets/index.less';\nimport { Input } from "
},
{
"path": "src/common/backend/services/yuque_oauth/index.ts",
"chars": 971,
"preview": "import config from '@/config';\nimport { ServiceMeta } from './../interface';\nimport Service from './service';\nimport loc"
},
{
"path": "src/common/backend/services/yuque_oauth/interface.ts",
"chars": 1051,
"preview": "import { CompleteStatus, CreateDocumentRequest } from './../interface';\nimport { Repository } from '../interface';\n\nexpo"
},
{
"path": "src/common/backend/services/yuque_oauth/service.ts",
"chars": 4742,
"preview": "import { RequestHelper } from './../../../../service/request/common/request';\nimport { IBasicRequestService } from './.."
},
{
"path": "src/common/blob.ts",
"chars": 948,
"preview": "const Base64ImageToBlob = (image: string): Blob => {\n const arr = image.split(',');\n const mime = arr[0].match(/:(.*?)"
},
{
"path": "src/common/buffer.ts",
"chars": 5870,
"preview": "/* eslint-disable @typescript-eslint/member-ordering */\n/* eslint-disable no-dupe-class-members */\n/* eslint-disable no-"
},
{
"path": "src/common/chrome/storage.ts",
"chars": 597,
"preview": "import { AbstractStorageService } from '@web-clipper/shared/lib/storage';\nimport * as browser from '@web-clipper/chrome-"
},
{
"path": "src/common/error.ts",
"chars": 617,
"preview": "export interface SerializedError {\n readonly $isError: true;\n readonly name: string;\n readonly message: string;\n rea"
},
{
"path": "src/common/getResource.ts",
"chars": 192,
"preview": "export function getResourcePath(name: string) {\n let isFirefox = chrome.runtime.getURL(name).startsWith('moz-extension'"
},
{
"path": "src/common/hooks/useFilterExtensions.ts",
"chars": 567,
"preview": "import { useMemo } from 'react';\nimport { ExtensionType, SerializedExtensionInfo } from '@/extensions/common';\n\nconst us"
},
{
"path": "src/common/hooks/useFilterImageHostingServices.ts",
"chars": 1013,
"preview": "import { ImageHostingServiceMeta } from '../backend';\nimport { ImageHosting } from '../modelTypes/userPreference';\n\ninte"
},
{
"path": "src/common/hooks/useOriginPermission.ts",
"chars": 588,
"preview": "import { IPermissionsService } from '@/service/common/permissions';\nimport { useState } from 'react';\nimport Container f"
},
{
"path": "src/common/hooks/useVerifiedAccount.tsx",
"chars": 3836,
"preview": "import { UserPreferenceStore } from '@/common/types';\nimport { FormComponentProps } from '@ant-design/compatible/lib/for"
},
{
"path": "src/common/loading.test.ts",
"chars": 3151,
"preview": "/* eslint-disable no-loop-func */\nimport { loading, loadingStatus } from './loading';\nimport { autorun, action, observab"
},
{
"path": "src/common/loading.ts",
"chars": 1963,
"preview": "import { observable } from 'mobx';\nimport { generateUuid } from '@web-clipper/shared/lib/uuid';\n\ntype FunctionKeys<T> = "
},
{
"path": "src/common/locales/antd.ts",
"chars": 407,
"preview": "import en_US from 'antd/lib/locale-provider/en_US';\nimport ja_JP from 'antd/lib/locale-provider/ja_JP';\nimport ru_RU fro"
},
{
"path": "src/common/locales/data/de-DE.json",
"chars": 10561,
"preview": "{\n \"auth.modal.title\": \"Konto konfigurieren\",\n \"backend.error.store\": \"Die Erweiterungsgalerie kann nicht skriptgesteu"
},
{
"path": "src/common/locales/data/de-DE.ts",
"chars": 218,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './de-DE.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/en-US.json",
"chars": 10348,
"preview": "{\n \"auth.modal.title\": \"Account Config\",\n \"backend.error.store\": \"The extensions gallery cannot be scripted.\",\n \"back"
},
{
"path": "src/common/locales/data/en-US.ts",
"chars": 218,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './en-US.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/ja-JP.json",
"chars": 8643,
"preview": "{\n \"auth.modal.title\": \"\",\n \"backend.error.store\": \"\",\n \"backend.imageHosting.baklib.builtInRemark\": \"\",\n \"backend.i"
},
{
"path": "src/common/locales/data/ja-JP.ts",
"chars": 214,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './ja-JP.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/ko-KR.json",
"chars": 8989,
"preview": "{\n \"auth.modal.title\": \"계정 설정\",\n \"backend.error.store\": \"\",\n \"backend.imageHosting.baklib.builtInRemark\": \"\",\n \"back"
},
{
"path": "src/common/locales/data/ko-KR.ts",
"chars": 210,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './ko-KR.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/ru-RU.json",
"chars": 8678,
"preview": "{\n \"auth.modal.title\": \"\",\n \"backend.error.store\": \"\",\n \"backend.imageHosting.baklib.builtInRemark\": \"\",\n \"backend.i"
},
{
"path": "src/common/locales/data/ru-RU.ts",
"chars": 214,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './ru-RU.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/zh-CN.json",
"chars": 10842,
"preview": "{\n \"auth.modal.title\": \"账户配置\",\n \"backend.error.store\": \"插件商店不允许执行脚本\",\n \"backend.imageHosting.baklib.builtInRemark\": \""
},
{
"path": "src/common/locales/data/zh-CN.ts",
"chars": 215,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './zh-CN.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/data/zh-TW.json",
"chars": 9844,
"preview": "{\n \"auth.modal.title\": \"賬戶配置\",\n \"backend.error.store\": \"\",\n \"backend.imageHosting.baklib.builtInRemark\": \"\",\n \"backe"
},
{
"path": "src/common/locales/data/zh-TW.ts",
"chars": 215,
"preview": "import { LocaleModel } from '@/common/locales/interface';\nimport messages from './zh-TW.json';\n\nconst model: LocaleModel"
},
{
"path": "src/common/locales/index.test.ts",
"chars": 241,
"preview": "import { removeEmptyKeys } from './interface';\n\nit('test remove PR_IS_WELCOME', () => {\n const messages = {\n a: '1',"
},
{
"path": "src/common/locales/index.ts",
"chars": 1871,
"preview": "import { LOCAL_USER_PREFERENCE_LOCALE_KEY } from '@/common/modelTypes/userPreference';\nimport { createIntlCache, createI"
},
{
"path": "src/common/locales/interface.ts",
"chars": 555,
"preview": "export interface LocaleModel {\n name: string;\n locale: string;\n alias: string[];\n messages: {\n [key: string]: str"
},
{
"path": "src/common/matchUrl.test.ts",
"chars": 1630,
"preview": "/* eslint-disable no-loop-func */\nimport matchUrl from './matchUrl';\n\ndescribe('test matchUrl', () => {\n describe('shou"
},
{
"path": "src/common/matchUrl.ts",
"chars": 2360,
"preview": "import * as tld from 'tldjs';\n\nconst RE_MATCH_PARTS = /(.*?):\\/\\/([^/]*)\\/(.*)/;\n\nconst RE_HTTP_OR_HTTPS = /^https?$/i;\n"
},
{
"path": "src/common/modelTypes/account.ts",
"chars": 367,
"preview": "export interface AccountPreference {\n [key: string]: string | undefined;\n id: string;\n type: string;\n name: string;\n"
},
{
"path": "src/common/modelTypes/clipper.ts",
"chars": 647,
"preview": "import { ClipperDataType } from '@/common/modelTypes/userPreference';\nimport {\n Repository,\n CompleteStatus,\n CreateD"
},
{
"path": "src/common/modelTypes/extensions.ts",
"chars": 377,
"preview": "import { IExtensionWithId } from '@/extensions/common';\n\nexport interface ExtensionStore {\n extensions: IExtensionWithI"
}
]
// ... and 146 more files (download for full content)
About this extraction
This page contains the full source code of the webclipper/web-clipper GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 346 files (707.6 KB), approximately 212.5k tokens, and a symbol index with 705 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.