mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-31 11:08:47 +02:00
Compare commits
130 Commits
v1.6.3
...
1.8.0-andr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f532d6f01b | ||
|
|
351f56dc0d | ||
|
|
feb74ae910 | ||
|
|
4b483d200d | ||
|
|
5858cae55b | ||
|
|
e7491d1c03 | ||
|
|
e740d56a84 | ||
|
|
01c78c654d | ||
|
|
db67114a30 | ||
|
|
7483286833 | ||
|
|
9606517f38 | ||
|
|
1d86624ce3 | ||
|
|
bdc2af9597 | ||
|
|
41d77b6424 | ||
|
|
3bda97b4f5 | ||
|
|
5d5b1e9eac | ||
|
|
f46ef8347f | ||
|
|
c613e67218 | ||
|
|
a9ff91d51e | ||
|
|
52f51c4087 | ||
|
|
f0ec25c296 | ||
|
|
d2ae716993 | ||
|
|
8d432d4810 | ||
|
|
e2bd1cd1f1 | ||
|
|
7286c60961 | ||
|
|
8a9cd91d54 | ||
|
|
462ae4d8ab | ||
|
|
45e7d5e46b | ||
|
|
9426e937a5 | ||
|
|
8ec4102395 | ||
|
|
4832d243da | ||
|
|
9504f278c3 | ||
|
|
8cefa1defc | ||
|
|
b4e58139e3 | ||
|
|
73f60704dd | ||
|
|
470e3040fc | ||
|
|
73d9a0f371 | ||
|
|
7a01898b2b | ||
|
|
41f5a4eecf | ||
|
|
08744fd0ee | ||
|
|
4788060c5a | ||
|
|
7a5e49bcbe | ||
|
|
536744f74a | ||
|
|
b1c33ebf28 | ||
|
|
323cd373e5 | ||
|
|
6c5cfb1505 | ||
|
|
b628e6071e | ||
|
|
8a2c1c0b2a | ||
|
|
77734aebba | ||
|
|
31711987c3 | ||
|
|
e193afb97d | ||
|
|
02b8fc4eb5 | ||
|
|
fd68f02f60 | ||
|
|
6089c58177 | ||
|
|
cfdfbb36cb | ||
|
|
38791a3760 | ||
|
|
e1426d4c73 | ||
|
|
71e45c23b5 | ||
|
|
673e0287bc | ||
|
|
bda62c761a | ||
|
|
412b40d64f | ||
|
|
975bc3e64d | ||
|
|
2832af2753 | ||
|
|
258e9f0a21 | ||
|
|
8a9de63ebc | ||
|
|
49ec85effd | ||
|
|
87e70b2aef | ||
|
|
c6f5276619 | ||
|
|
640f03af97 | ||
|
|
92c2538fbc | ||
|
|
867edbd878 | ||
|
|
6e2bcdd832 | ||
|
|
25e12564b6 | ||
|
|
cf0a12bdb9 | ||
|
|
9dce7c737e | ||
|
|
0bdb135b7b | ||
|
|
d9e3798dd1 | ||
|
|
4d9c108144 | ||
|
|
ba19214307 | ||
|
|
1aee142b59 | ||
|
|
eedf201a61 | ||
|
|
b7acb75686 | ||
|
|
ee8924c7c5 | ||
|
|
6461fac76e | ||
|
|
3c31fcbe6e | ||
|
|
858c92aad5 | ||
|
|
c03e672623 | ||
|
|
ae8720d69a | ||
|
|
6db218fc06 | ||
|
|
77e28d25e3 | ||
|
|
309ca061a2 | ||
|
|
5268485715 | ||
|
|
ee1517e800 | ||
|
|
5770972e88 | ||
|
|
dc24e629c5 | ||
|
|
9dd125656b | ||
|
|
3f548e1086 | ||
|
|
919710c069 | ||
|
|
9b4b28741f | ||
|
|
7d078e9e40 | ||
|
|
bf194ff7d2 | ||
|
|
2e02b57068 | ||
|
|
5696de8a24 | ||
|
|
39b7057bb7 | ||
|
|
78f6d18386 | ||
|
|
f1c4d5a78d | ||
|
|
13ab6ca9f7 | ||
|
|
6920147a17 | ||
|
|
1ba1c52165 | ||
|
|
b7bc1741d6 | ||
|
|
6b4ca9f2ae | ||
|
|
6616e87e1d | ||
|
|
0eb0e4fceb | ||
|
|
481a31ab27 | ||
|
|
255cb89523 | ||
|
|
bda96a7d82 | ||
|
|
8ccc7136e2 | ||
|
|
468498f087 | ||
|
|
1a57ef5177 | ||
|
|
e3b428a3fb | ||
|
|
65e1ced04c | ||
|
|
95d1f17c40 | ||
|
|
1fe3b77ba1 | ||
|
|
0250a86b12 | ||
|
|
bf64300c68 | ||
|
|
6280ba31c3 | ||
|
|
ad4d3c8889 | ||
|
|
2a96b9c615 | ||
|
|
5dcc8b3b12 | ||
|
|
429c6f0e69 |
8
.changeset/README.md
Normal file
8
.changeset/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
10
.changeset/config.json
Normal file
10
.changeset/config.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@1.6.3/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
8
.github/workflows/docs-deploy.yml
vendored
8
.github/workflows/docs-deploy.yml
vendored
@@ -1,12 +1,14 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CI
|
||||
name: Docs Deploy
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the main branch
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
- uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 5.0.x
|
||||
|
||||
|
||||
- name: Use Node.js 15.x
|
||||
uses: actions/setup-node@v2.1.5
|
||||
with:
|
||||
@@ -34,7 +36,7 @@ jobs:
|
||||
- uses: blbecker/rsync-deploy@v1.0
|
||||
with:
|
||||
source_dir: ./docs/public_html
|
||||
destination_path: ${{ secrets.REMOTE_PATH }}
|
||||
destination_path: ${{ secrets.REMOTE_DOCS_PATH }}
|
||||
destination_host: ${{ secrets.REMOTE_HOST }}
|
||||
deployment_user: ${{ secrets.REMOTE_USER }}
|
||||
deployment_key: ${{ secrets.REMOTE_KEY }}
|
||||
|
||||
32
.github/workflows/enex-tests.yml
vendored
Normal file
32
.github/workflows/enex-tests.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: notesnook/enex tests
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push events or manually
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "packages/enex/**"
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v2.1.5
|
||||
with:
|
||||
node-version: 16.x
|
||||
- run: |
|
||||
cd packages/enex
|
||||
npm i
|
||||
npm run test
|
||||
41
.github/workflows/importer-deploy.yml
vendored
Normal file
41
.github/workflows/importer-deploy.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Importer Deploy
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push events or manually
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "apps/importer/**"
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v2.1.5
|
||||
with:
|
||||
node-version: 16.x
|
||||
- run: |
|
||||
cd apps/importer
|
||||
npm i
|
||||
npm run build
|
||||
mv ./build/ ./public_html/
|
||||
|
||||
- uses: blbecker/rsync-deploy@v1.0
|
||||
with:
|
||||
source_dir: ./apps/importer/public_html/
|
||||
destination_path: ${{ secrets.REMOTE_IMPORTER_PATH }}
|
||||
destination_host: ${{ secrets.REMOTE_HOST }}
|
||||
deployment_user: ${{ secrets.REMOTE_USER }}
|
||||
deployment_key: ${{ secrets.REMOTE_KEY }}
|
||||
32
.github/workflows/importer-tests.yml
vendored
Normal file
32
.github/workflows/importer-tests.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: notesnook/importer tests
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push events or manually
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "packages/importer/**"
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
uses: actions/setup-node@v2.1.5
|
||||
with:
|
||||
node-version: 16.x
|
||||
- run: |
|
||||
cd packages/importer
|
||||
npm i
|
||||
npm run test
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.turbo
|
||||
dist
|
||||
node_modules
|
||||
19
apps/importer/.env-cmdrc.js
Normal file
19
apps/importer/.env-cmdrc.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const { version } = require("./package.json");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const APP_VERSION = version;
|
||||
const CHANGELOG = fs.readFileSync(path.join(__dirname, "changelog.txt"), {
|
||||
encoding: "utf8",
|
||||
});
|
||||
module.exports = {
|
||||
all: {
|
||||
REACT_APP_VERSION: APP_VERSION,
|
||||
REACT_APP_CHANGELOG: CHANGELOG,
|
||||
},
|
||||
release: {
|
||||
GENERATE_SOURCEMAP: false,
|
||||
INLINE_RUNTIME_CHUNK: false,
|
||||
DISABLE_ESLINT_PLUGIN: true,
|
||||
},
|
||||
};
|
||||
23
apps/importer/.gitignore
vendored
Normal file
23
apps/importer/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
3
apps/importer/changelog.txt
Normal file
3
apps/importer/changelog.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
1. 🥳 Support for Standard Notes is here! 🥳
|
||||
2. Fixed issue with Evernote provider where images got cut off.
|
||||
3. Fixes issue where some attachments were lost during Evernote .enex import.
|
||||
39146
apps/importer/package-lock.json
generated
Normal file
39146
apps/importer/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
59
apps/importer/package.json
Normal file
59
apps/importer/package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@notesnook/importer-web",
|
||||
"version": "1.1.2",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"@types/file-saver": "^2.0.4",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/react-dropzone": "^5.1.0",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"@types/theme-ui": "^0.6.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@notesnook/importer": "^1.1.3",
|
||||
"colord": "^2.9.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"hash-wasm": "^4.9.0",
|
||||
"interweave": "^12.9.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-modal": "^3.14.4",
|
||||
"react-select": "^5.2.1",
|
||||
"theme-ui": "^0.12.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "env-cmd -e all react-scripts start",
|
||||
"build": "env-cmd -e all,release react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
apps/importer/public/favicon.ico
Normal file
BIN
apps/importer/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
38
apps/importer/public/index.html
Normal file
38
apps/importer/public/index.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Import your notes from any notes app into Notesnook."
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Notesnook Importer</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
BIN
apps/importer/public/logo192.png
Normal file
BIN
apps/importer/public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
3
apps/importer/public/robots.txt
Normal file
3
apps/importer/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
71
apps/importer/src/App.css
Normal file
71
apps/importer/src/App.css
Normal file
@@ -0,0 +1,71 @@
|
||||
/* open-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-regular.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* open-sans-italic - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-italic.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* open-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-600.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-600.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* open-sans-600italic - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-600italic.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-600italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* open-sans-700 - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-700.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-700.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* open-sans-700italic - latin */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local(""),
|
||||
url("./assets/fonts/open-sans-v20-latin-700italic.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url("./assets/fonts/open-sans-v20-latin-700italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
54
apps/importer/src/App.tsx
Normal file
54
apps/importer/src/App.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Flex } from "@theme-ui/components";
|
||||
import { ThemeProvider } from "@theme-ui/theme-provider";
|
||||
import "./App.css";
|
||||
import { ThemeFactory } from "./theme";
|
||||
import { useState } from "react";
|
||||
import { Providers } from "@notesnook/importer/dist/src/providers/providerfactory";
|
||||
import { StepSeperator } from "./components/StepSeperator";
|
||||
import { ProviderSelector } from "./components/ProviderSelector";
|
||||
import { FileSelector } from "./components/FileSelector";
|
||||
import { ImportResult } from "./components/ImportResult";
|
||||
import { Hero } from "./components/Hero";
|
||||
import { Footer } from "./components/Footer";
|
||||
|
||||
function App() {
|
||||
const [selectedProvider, setSelectedProvider] = useState<
|
||||
Providers | undefined
|
||||
>();
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={ThemeFactory.construct()}>
|
||||
<Flex sx={{ flexDirection: "column" }}>
|
||||
<Hero />
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<ProviderSelector onProviderChanged={setSelectedProvider} />
|
||||
|
||||
{selectedProvider && (
|
||||
<>
|
||||
<StepSeperator />
|
||||
<FileSelector
|
||||
provider={selectedProvider}
|
||||
onFilesChanged={setSelectedFiles}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{selectedFiles.length > 0 && selectedProvider && (
|
||||
<>
|
||||
<StepSeperator />
|
||||
<ImportResult files={selectedFiles} provider={selectedProvider} />
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Footer />
|
||||
</Flex>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-600.woff
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-600.woff
Normal file
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-600.woff2
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-600.woff2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-700.woff
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-700.woff
Normal file
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-700.woff2
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-700.woff2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-italic.woff
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-italic.woff
Normal file
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-italic.woff2
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-italic.woff2
Normal file
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-regular.woff
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-regular.woff
Normal file
Binary file not shown.
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-regular.woff2
Normal file
BIN
apps/importer/src/assets/fonts/open-sans-v20-latin-regular.woff2
Normal file
Binary file not shown.
42
apps/importer/src/components/Accordion.tsx
Normal file
42
apps/importer/src/components/Accordion.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Flex, FlexProps, Text } from "@theme-ui/components";
|
||||
import { PropsWithChildren, useState } from "react";
|
||||
import { IoChevronDown, IoChevronUp } from "react-icons/io5";
|
||||
|
||||
type AccordionProps = FlexProps & {
|
||||
title: string;
|
||||
color?: string;
|
||||
};
|
||||
export function Accordion({
|
||||
title,
|
||||
children,
|
||||
sx,
|
||||
color,
|
||||
...restProps
|
||||
}: PropsWithChildren<AccordionProps>) {
|
||||
const [isContentHidden, setIsContentHidden] = useState<boolean>(true);
|
||||
return (
|
||||
<Flex sx={{ flexDirection: "column", ...sx }} {...restProps}>
|
||||
<Flex
|
||||
sx={{
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
p: 2,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsContentHidden((state) => !state);
|
||||
}}
|
||||
>
|
||||
<Text variant="subtitle" sx={{ color }}>
|
||||
{title}
|
||||
</Text>
|
||||
{isContentHidden ? (
|
||||
<IoChevronDown size={16} color={color} />
|
||||
) : (
|
||||
<IoChevronUp size={16} color={color} />
|
||||
)}
|
||||
</Flex>
|
||||
{!isContentHidden && children}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
113
apps/importer/src/components/FileSelector.tsx
Normal file
113
apps/importer/src/components/FileSelector.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { Flex, Input, Text } from "@theme-ui/components";
|
||||
import { ProviderFactory } from "@notesnook/importer";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { IProvider } from "@notesnook/importer/dist/src/providers/provider";
|
||||
import { Providers } from "@notesnook/importer/dist/src/providers/providerfactory";
|
||||
import { StepContainer } from "./StepContainer";
|
||||
import { Accordion } from "./Accordion";
|
||||
|
||||
type FileSelectorProps = {
|
||||
provider?: Providers;
|
||||
onFilesChanged: (files: File[]) => void;
|
||||
};
|
||||
|
||||
export function FileSelector(props: FileSelectorProps) {
|
||||
const { provider: providerName, onFilesChanged } = props;
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
const [provider, setProvider] = useState<IProvider | undefined>();
|
||||
|
||||
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||
setFiles((files) => {
|
||||
const newFiles = [...acceptedFiles, ...files];
|
||||
return newFiles;
|
||||
});
|
||||
}, []);
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
onDrop,
|
||||
accept: provider?.supportedExtensions?.concat(".zip"),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
onFilesChanged(files);
|
||||
}, [files, onFilesChanged]);
|
||||
|
||||
useEffect(() => {
|
||||
setFiles([]);
|
||||
setProvider(undefined);
|
||||
|
||||
if (!providerName) return;
|
||||
setProvider(ProviderFactory.getProvider(providerName));
|
||||
}, [providerName]);
|
||||
|
||||
return (
|
||||
<StepContainer sx={{ flexDirection: "column", alignItems: "stretch" }}>
|
||||
<Text variant="title">Select {provider?.name} files</Text>
|
||||
|
||||
<Flex
|
||||
{...getRootProps()}
|
||||
sx={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: 200,
|
||||
border: "2px dashed var(--theme-ui-colors-border)",
|
||||
borderRadius: "default",
|
||||
mt: 2,
|
||||
}}
|
||||
>
|
||||
<Input {...getInputProps()} />
|
||||
<Text variant="body" sx={{ textAlign: "center" }}>
|
||||
{isDragActive
|
||||
? "Drop the files here"
|
||||
: "Drag & drop files here, or click to select files"}
|
||||
<br />
|
||||
<Text variant="subBody">
|
||||
Only {provider?.supportedExtensions.join(", ")} files are supported.
|
||||
You can also select .zip files containing{" "}
|
||||
{provider?.supportedExtensions.join(", ")} files.
|
||||
</Text>
|
||||
</Text>
|
||||
</Flex>
|
||||
{files.length > 0 ? (
|
||||
<Accordion
|
||||
title={`${files.length} ${
|
||||
files.length > 1 ? "files" : "file"
|
||||
} selected`}
|
||||
sx={{
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
mt: 2,
|
||||
borderRadius: "default",
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{ flexDirection: "column", overflowY: "auto", maxHeight: 400 }}
|
||||
>
|
||||
{files.map((file, index) => (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
p: 2,
|
||||
bg: index % 2 ? "transparent" : "bgSecondary",
|
||||
cursor: "pointer",
|
||||
":hover": {
|
||||
bg: "hover",
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
setFiles((files) => {
|
||||
const _files = files.slice();
|
||||
_files.splice(index, 1);
|
||||
return _files;
|
||||
});
|
||||
}}
|
||||
title="Click to remove"
|
||||
>
|
||||
<Text variant="body">{file.name}</Text>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Accordion>
|
||||
) : null}
|
||||
</StepContainer>
|
||||
);
|
||||
}
|
||||
21
apps/importer/src/components/Footer.tsx
Normal file
21
apps/importer/src/components/Footer.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { appVersion } from "../utils/version";
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
mt: 100,
|
||||
pt: 50,
|
||||
pb: 50,
|
||||
borderTop: "1px solid var(--theme-ui-colors-border)",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Text variant="body">Notesnook Importer (v{appVersion})</Text>
|
||||
<Text variant="subBody">© 2021 Streetwriters LLC</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
63
apps/importer/src/components/Hero.tsx
Normal file
63
apps/importer/src/components/Hero.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Flex, Link, Text } from "@theme-ui/components";
|
||||
import { appVersion } from "../utils/version";
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
mt: 150,
|
||||
mb: 100,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Text variant="heading" sx={{ textAlign: "center" }}>
|
||||
Notesnook Importer
|
||||
</Text>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: "title",
|
||||
textAlign: "center",
|
||||
color: "fontTertiary",
|
||||
}}
|
||||
>
|
||||
Import your notes from any notes app into Notesnook.
|
||||
</Text>
|
||||
<Flex sx={{ mt: 2 }}>
|
||||
<Text variant="body" sx={{ px: 1 }}>
|
||||
v{appVersion}
|
||||
</Text>
|
||||
<Link
|
||||
href="https://github.com/streetwriters/notesnook"
|
||||
variant="text.body"
|
||||
sx={{ px: 1, borderLeft: "1px solid var(--theme-ui-colors-border)" }}
|
||||
>
|
||||
Github
|
||||
</Link>
|
||||
<Link
|
||||
href="https://app.notesnook.com/"
|
||||
variant="text.body"
|
||||
sx={{ px: 1, borderLeft: "1px solid var(--theme-ui-colors-border)" }}
|
||||
>
|
||||
Notesnook Web
|
||||
</Link>
|
||||
</Flex>
|
||||
<Flex
|
||||
sx={{
|
||||
bg: "bgSecondary",
|
||||
flexDirection: "column",
|
||||
width: 400,
|
||||
mt: 4,
|
||||
p: 2,
|
||||
borderRadius: "default",
|
||||
}}
|
||||
>
|
||||
<Text variant="subtitle">What's new in v{appVersion}</Text>
|
||||
<Text variant="body" sx={{ whiteSpace: "pre-wrap" }}>
|
||||
{process.env.REACT_APP_CHANGELOG}
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
39
apps/importer/src/components/ImportErrors.tsx
Normal file
39
apps/importer/src/components/ImportErrors.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { Accordion } from "./Accordion";
|
||||
import { TransformError } from "@notesnook/importer/dist/src/utils/transformerror";
|
||||
|
||||
type ImportErrorsProps = {
|
||||
errors: TransformError[];
|
||||
};
|
||||
|
||||
export function ImportErrors(props: ImportErrorsProps) {
|
||||
return (
|
||||
<Accordion
|
||||
title={`${props.errors.length} errors occured`}
|
||||
sx={{ bg: "errorBg", borderRadius: "default", mt: 2 }}
|
||||
color="#E53935"
|
||||
>
|
||||
<Flex sx={{ flexDirection: "column", px: 2, pb: 2 }}>
|
||||
<Text variant="body" sx={{ color: "error" }}>
|
||||
{props.errors.map((error, index) => (
|
||||
<>
|
||||
{index + 1}. {error.message} ({error.file.name})
|
||||
<br />
|
||||
</>
|
||||
))}
|
||||
</Text>
|
||||
<Button
|
||||
sx={{ bg: "error", color: "static", alignSelf: "start", mt: 2 }}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
"https://github.com/streetwriters/notesnook/issues/new",
|
||||
"_blank"
|
||||
)
|
||||
}
|
||||
>
|
||||
Send us a bug report
|
||||
</Button>
|
||||
</Flex>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
47
apps/importer/src/components/ImportHelp.tsx
Normal file
47
apps/importer/src/components/ImportHelp.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Button, Text } from "@theme-ui/components";
|
||||
import { Accordion } from "./Accordion";
|
||||
|
||||
type ImportHelpProps = {
|
||||
onDownload: () => void;
|
||||
};
|
||||
|
||||
export function ImportHelp(props: ImportHelpProps) {
|
||||
return (
|
||||
<Accordion
|
||||
title="How to import your notes into Notesnook?"
|
||||
sx={{ bg: "bgSecondary", mt: 2, borderRadius: "default" }}
|
||||
>
|
||||
<Text
|
||||
as="ol"
|
||||
variant="body"
|
||||
sx={{
|
||||
lineHeight: "24px",
|
||||
paddingInlineStart: 30,
|
||||
pb: 2,
|
||||
}}
|
||||
>
|
||||
<Text as="li">
|
||||
<Button variant="primary" sx={{ py: 0 }} onClick={props.onDownload}>
|
||||
Download the ZIP file
|
||||
</Button>{" "}
|
||||
containing your notes
|
||||
</Text>
|
||||
<Text as="li">
|
||||
<a href="https://app.notesnook.com/">Open Notesnook</a> and make sure
|
||||
you are logged in.
|
||||
</Text>
|
||||
<Text as="li">
|
||||
Go to <b>Settings</b>
|
||||
</Text>
|
||||
<Text as="li">
|
||||
Expand the section titled <b>Notesnook Importer</b>
|
||||
</Text>
|
||||
<Text as="li">
|
||||
Click on <b>Import from ZIP file</b>
|
||||
</Text>
|
||||
<Text as="li">Select the downloaded ZIP file</Text>
|
||||
<Text as="li">Your notes should appear in the app.</Text>
|
||||
</Text>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
113
apps/importer/src/components/ImportResult.tsx
Normal file
113
apps/importer/src/components/ImportResult.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { Button, Text } from "@theme-ui/components";
|
||||
import { pack, transform } from "@notesnook/importer";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
ProviderResult,
|
||||
ProviderSettings,
|
||||
} from "@notesnook/importer/dist/src/providers/provider";
|
||||
import { Providers } from "@notesnook/importer/dist/src/providers/providerfactory";
|
||||
import { IFile } from "@notesnook/importer/dist/src/utils/file";
|
||||
import { xxhash64 } from "hash-wasm";
|
||||
import { Note } from "@notesnook/importer/dist/src/models/note";
|
||||
import { StepContainer } from "./StepContainer";
|
||||
import { NotePreview } from "./NotePreview";
|
||||
import { ImportErrors } from "./ImportErrors";
|
||||
import { ImportHelp } from "./ImportHelp";
|
||||
import { saveAs } from "file-saver";
|
||||
import { NotesList } from "./NotesList";
|
||||
|
||||
type ResultProps = {
|
||||
files: File[];
|
||||
provider: Providers | undefined;
|
||||
};
|
||||
|
||||
type Progress = {
|
||||
total: number;
|
||||
done: number;
|
||||
};
|
||||
|
||||
const settings: ProviderSettings = {
|
||||
hasher: { type: "xxh64", hash: (data) => xxhash64(data) },
|
||||
};
|
||||
|
||||
export function ImportResult(props: ResultProps) {
|
||||
const { provider, files } = props;
|
||||
|
||||
const [progress, setProgress] = useState<Progress>({ total: 0, done: 0 });
|
||||
const [result, setResult] = useState<ProviderResult | undefined>();
|
||||
const [selectedNote, setSelectedNote] = useState<Note | undefined>();
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!provider) return;
|
||||
|
||||
setProgress({ total: 0, done: 0 });
|
||||
let result: ProviderResult = { notes: [], errors: [] };
|
||||
for (let file of files) {
|
||||
const providerFile: IFile = {
|
||||
name: file.name,
|
||||
data: await file.arrayBuffer(),
|
||||
modifiedAt: file.lastModified,
|
||||
};
|
||||
const transformResult = await transform(
|
||||
[providerFile],
|
||||
provider,
|
||||
settings
|
||||
);
|
||||
result.notes.push(...transformResult.notes);
|
||||
result.errors.push(...transformResult.errors);
|
||||
setProgress((p) => ({ total: files.length, done: p.done + 1 }));
|
||||
}
|
||||
setProgress({ total: 0, done: 0 });
|
||||
setResult(result);
|
||||
})();
|
||||
}, [provider, files]);
|
||||
|
||||
const downloadAsZip = useCallback(() => {
|
||||
if (!result) return;
|
||||
const packed = pack(result.notes);
|
||||
saveAs(
|
||||
new Blob([packed], { type: "application/zip" }),
|
||||
`notesnook-importer.zip`
|
||||
);
|
||||
}, [result]);
|
||||
|
||||
return (
|
||||
<StepContainer
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
alignItems: "stretch",
|
||||
}}
|
||||
>
|
||||
<Text variant="title">Your notes are ready for download</Text>
|
||||
{progress.total > 0 ? (
|
||||
<Text variant="body">
|
||||
Processing files ({progress.done}/{progress.total})
|
||||
</Text>
|
||||
) : result ? (
|
||||
<>
|
||||
<Text variant="body" sx={{ color: "fontTertiary" }}></Text>
|
||||
<NotesList
|
||||
notes={result?.notes}
|
||||
filesLength={files.length}
|
||||
onNoteSelected={(note) => setSelectedNote(note)}
|
||||
/>
|
||||
{result.errors.length > 0 && <ImportErrors errors={result.errors} />}
|
||||
<ImportHelp onDownload={downloadAsZip} />
|
||||
<Button
|
||||
variant="primary"
|
||||
sx={{ alignSelf: "center", mt: 2 }}
|
||||
onClick={downloadAsZip}
|
||||
>
|
||||
Download ZIP file
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
{selectedNote && (
|
||||
<NotePreview
|
||||
note={selectedNote}
|
||||
onClose={() => setSelectedNote(undefined)}
|
||||
/>
|
||||
)}
|
||||
</StepContainer>
|
||||
);
|
||||
}
|
||||
150
apps/importer/src/components/NotePreview.tsx
Normal file
150
apps/importer/src/components/NotePreview.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { IoStarOutline } from "react-icons/io5";
|
||||
import { GrPin } from "react-icons/gr";
|
||||
import { MdCircle, MdClose, MdOutlineBook } from "react-icons/md";
|
||||
import { Markup } from "interweave";
|
||||
import Modal from "react-modal";
|
||||
import { Note } from "@notesnook/importer/dist/src/models/note";
|
||||
Modal.setAppElement("#root");
|
||||
|
||||
type NotePreviewProps = {
|
||||
note: Note;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export function NotePreview(props: NotePreviewProps) {
|
||||
const {
|
||||
title,
|
||||
content,
|
||||
dateEdited,
|
||||
dateCreated,
|
||||
favorite,
|
||||
color,
|
||||
pinned,
|
||||
notebooks,
|
||||
tags,
|
||||
attachments,
|
||||
} = props.note;
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
contentLabel={title}
|
||||
style={{
|
||||
content: {
|
||||
borderRadius: 10,
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
boxShadow: "0px 0px 20px 0px #00000011",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex sx={{ flexDirection: "column" }}>
|
||||
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<Text variant="subBody">
|
||||
This preview is not an exact representation of how the note will
|
||||
appear in Notesnook.
|
||||
</Text>
|
||||
<Button
|
||||
variant="secondary"
|
||||
sx={{
|
||||
bg: "transparent",
|
||||
display: "flex",
|
||||
alignSelf: "end",
|
||||
p: 0,
|
||||
m: 0,
|
||||
alignItems: "center",
|
||||
}}
|
||||
onClick={props.onClose}
|
||||
>
|
||||
<MdClose size={24} />
|
||||
</Button>
|
||||
</Flex>
|
||||
<Text as="h1" variant="subheading">
|
||||
{title}
|
||||
</Text>
|
||||
<Flex sx={{ alignItems: "center" }}>
|
||||
{color && (
|
||||
<MdCircle color={color} size={14} style={{ marginRight: 5 }} />
|
||||
)}
|
||||
{favorite && <IoStarOutline style={{ marginRight: 5 }} />}
|
||||
{pinned && <GrPin size={12} style={{ marginRight: 5 }} />}
|
||||
{dateCreated && (
|
||||
<>
|
||||
<Text variant="subBody">
|
||||
Created on: {formatDate(dateCreated)}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{dateEdited && (
|
||||
<>
|
||||
<MdCircle
|
||||
color="#5b5b5b"
|
||||
size={7}
|
||||
style={{ marginLeft: 3, marginRight: 3 }}
|
||||
/>
|
||||
<Text variant="subBody">
|
||||
Last modified: {formatDate(dateEdited)}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
{attachments && (
|
||||
<>
|
||||
<MdCircle
|
||||
color="#5b5b5b"
|
||||
size={7}
|
||||
style={{ marginLeft: 3, marginRight: 3 }}
|
||||
/>
|
||||
<Text variant="subBody">{attachments.length} attachments</Text>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex my={1}>
|
||||
{notebooks?.map((notebook) => (
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
bg: "bgSecondary",
|
||||
mr: 1,
|
||||
p: 1,
|
||||
py: "2px",
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
borderRadius: "default",
|
||||
color: "fontTertiary",
|
||||
}}
|
||||
>
|
||||
<MdOutlineBook size={11} />
|
||||
<Text variant="subBody" sx={{ ml: "2px" }}>
|
||||
{notebook.notebook} > {notebook.topic}
|
||||
</Text>
|
||||
</Flex>
|
||||
))}
|
||||
{tags?.map((tag) => (
|
||||
<Text
|
||||
variant="subBody"
|
||||
sx={{
|
||||
bg: "bgSecondary",
|
||||
mr: 1,
|
||||
p: 1,
|
||||
py: "2px",
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
borderRadius: "default",
|
||||
color: "fontTertiary",
|
||||
}}
|
||||
>
|
||||
#{tag}
|
||||
</Text>
|
||||
))}
|
||||
</Flex>
|
||||
<Text variant="body">
|
||||
<Markup content={content?.data} />
|
||||
</Text>
|
||||
</Flex>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function formatDate(date: number) {
|
||||
return new Date(date).toLocaleString(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
});
|
||||
}
|
||||
62
apps/importer/src/components/NotesList.tsx
Normal file
62
apps/importer/src/components/NotesList.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { Note } from "@notesnook/importer/dist/src/models/note";
|
||||
import { Accordion } from "./Accordion";
|
||||
|
||||
type NotesListProps = {
|
||||
notes: Note[];
|
||||
filesLength: number;
|
||||
onNoteSelected: (note: Note) => void;
|
||||
};
|
||||
export function NotesList(props: NotesListProps) {
|
||||
const { notes, filesLength, onNoteSelected } = props;
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
title={`${notes.length} notes found in ${filesLength} ${
|
||||
filesLength > 1 ? "files" : "file"
|
||||
}`}
|
||||
sx={{
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
mt: 2,
|
||||
borderRadius: "default",
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
overflowY: "auto",
|
||||
maxHeight: 300,
|
||||
}}
|
||||
>
|
||||
{notes.map((note, index) => (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
p: 2,
|
||||
bg: index % 2 ? "transparent" : "bgSecondary",
|
||||
cursor: "pointer",
|
||||
":hover": {
|
||||
bg: "hover",
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
onNoteSelected(note);
|
||||
}}
|
||||
title="Click to preview"
|
||||
>
|
||||
<Text variant="body">{note.title}</Text>
|
||||
{note.dateEdited && (
|
||||
<Text variant="subBody">
|
||||
Last modified on:{" "}
|
||||
{new Date(note.dateEdited).toLocaleString(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Accordion>
|
||||
);
|
||||
}
|
||||
53
apps/importer/src/components/ProviderSelector.tsx
Normal file
53
apps/importer/src/components/ProviderSelector.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Flex, Select, Text } from "@theme-ui/components";
|
||||
import { ProviderFactory } from "@notesnook/importer";
|
||||
import { Providers } from "@notesnook/importer/dist/src/providers/providerfactory";
|
||||
import { StepContainer } from "./StepContainer";
|
||||
|
||||
type ProviderSelectorProps = {
|
||||
onProviderChanged: (provider: Providers) => void;
|
||||
};
|
||||
|
||||
export function ProviderSelector(props: ProviderSelectorProps) {
|
||||
return (
|
||||
<StepContainer sx={{ flexDirection: "column" }}>
|
||||
<Flex sx={{ justifyContent: "space-between" }}>
|
||||
<Text variant="title">Choose a provider to import from</Text>
|
||||
<Select
|
||||
sx={{
|
||||
p: 0,
|
||||
m: 0,
|
||||
px: 2,
|
||||
border: "1px solid var(--theme-ui-colors-border)",
|
||||
outline: "none",
|
||||
":hover": {
|
||||
borderColor: "hover",
|
||||
},
|
||||
":active": {
|
||||
borderColor: "active",
|
||||
},
|
||||
fontFamily: "body",
|
||||
fontSize: "body",
|
||||
width: 150,
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const providerName: Providers = e.target.value as Providers;
|
||||
props.onProviderChanged(providerName);
|
||||
}}
|
||||
>
|
||||
<option value=""></option>
|
||||
{ProviderFactory.getAvailableProviders().map((provider) => (
|
||||
<option value={provider}>
|
||||
{ProviderFactory.getProvider(provider as Providers).name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Flex>
|
||||
<Text variant="body" sx={{ color: "fontTertiary" }}>
|
||||
Can't find your notes app in the list?{" "}
|
||||
<a href="https://github.com/streetwriters/notesnook/issues/new">
|
||||
Send us a request.
|
||||
</a>
|
||||
</Text>
|
||||
</StepContainer>
|
||||
);
|
||||
}
|
||||
22
apps/importer/src/components/StepContainer.tsx
Normal file
22
apps/importer/src/components/StepContainer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Flex, FlexProps } from "@theme-ui/components";
|
||||
|
||||
export function StepContainer({
|
||||
children,
|
||||
sx,
|
||||
...restProps
|
||||
}: React.PropsWithChildren<FlexProps>) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
width: "40%",
|
||||
boxShadow: "0px 0px 20px 0px #00000011",
|
||||
p: 4,
|
||||
borderRadius: "default",
|
||||
...sx,
|
||||
}}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
5
apps/importer/src/components/StepSeperator.tsx
Normal file
5
apps/importer/src/components/StepSeperator.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Box } from "@theme-ui/components";
|
||||
|
||||
export function StepSeperator() {
|
||||
return <Box sx={{ height: 150, width: 2, bg: "bgSecondary" }} />;
|
||||
}
|
||||
0
apps/importer/src/index.css
Normal file
0
apps/importer/src/index.css
Normal file
11
apps/importer/src/index.tsx
Normal file
11
apps/importer/src/index.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
1
apps/importer/src/react-app-env.d.ts
vendored
Normal file
1
apps/importer/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
28
apps/importer/src/theme/colorscheme/dark.ts
Normal file
28
apps/importer/src/theme/colorscheme/dark.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { colord } from "colord";
|
||||
import { StaticColors } from "./static";
|
||||
|
||||
export class DarkColorScheme {
|
||||
static construct(accent: string) {
|
||||
return {
|
||||
primary: colord(accent).toHex(),
|
||||
placeholder: colord("#ffffff")
|
||||
.alpha(0.6)
|
||||
.toHex(),
|
||||
background: "#1f1f1f",
|
||||
bgTransparent: "#1f1f1f99",
|
||||
accent: "#000",
|
||||
bgSecondary: "#2b2b2b",
|
||||
bgSecondaryText: "#A1A1A1",
|
||||
border: "#2b2b2b",
|
||||
hover: "#3b3b3b",
|
||||
fontSecondary: "#000",
|
||||
fontTertiary: "#A1A1A1",
|
||||
text: "#d3d3d3",
|
||||
overlay: "rgba(53, 53, 53, 0.5)",
|
||||
secondary: "black",
|
||||
icon: "#dbdbdb",
|
||||
disabled: "#5b5b5b",
|
||||
...StaticColors.construct(accent),
|
||||
};
|
||||
}
|
||||
}
|
||||
14
apps/importer/src/theme/colorscheme/index.ts
Normal file
14
apps/importer/src/theme/colorscheme/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { DarkColorScheme } from "./dark";
|
||||
import { LightColorScheme } from "./light";
|
||||
|
||||
const colorSchemes = {
|
||||
dark: DarkColorScheme,
|
||||
light: LightColorScheme,
|
||||
};
|
||||
|
||||
class ColorSchemeFactory {
|
||||
static construct<TTheme extends keyof typeof colorSchemes>(theme: TTheme) {
|
||||
return colorSchemes[theme].construct("#008837");
|
||||
}
|
||||
}
|
||||
export default ColorSchemeFactory;
|
||||
29
apps/importer/src/theme/colorscheme/light.ts
Normal file
29
apps/importer/src/theme/colorscheme/light.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { colord } from "colord";
|
||||
import { StaticColors } from "./static";
|
||||
|
||||
export class LightColorScheme {
|
||||
static construct(accent: string) {
|
||||
return {
|
||||
primary: colord(accent).toHex(),
|
||||
background: "white",
|
||||
bgTransparent: "#ffffff99",
|
||||
accent: "white",
|
||||
bgSecondary: "#f7f7f7",
|
||||
bgSecondaryText: "#5E5E5E",
|
||||
border: "#e7e7e7",
|
||||
hover: "#f0f0f0",
|
||||
active: "#ababab",
|
||||
fontSecondary: "white",
|
||||
fontTertiary: "#656565",
|
||||
text: "#202124",
|
||||
overlay: "rgba(0, 0, 0, 0.1)",
|
||||
secondary: "white",
|
||||
icon: "#3b3b3b",
|
||||
disabled: "#9b9b9b",
|
||||
placeholder: colord("#000000")
|
||||
.alpha(0.6)
|
||||
.toHex(),
|
||||
...StaticColors.construct(accent),
|
||||
};
|
||||
}
|
||||
}
|
||||
31
apps/importer/src/theme/colorscheme/static.ts
Normal file
31
apps/importer/src/theme/colorscheme/static.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { colord } from "colord";
|
||||
|
||||
export class StaticColors {
|
||||
static construct(accent: string) {
|
||||
return {
|
||||
shade: colord(accent)
|
||||
.alpha(0.1)
|
||||
.toRgbString(),
|
||||
textSelection: colord(accent)
|
||||
.alpha(0.2)
|
||||
.toRgbString(),
|
||||
dimPrimary: colord(accent)
|
||||
.alpha(0.7)
|
||||
.toRgbString(),
|
||||
transparent: "transparent",
|
||||
static: "white",
|
||||
error: "#E53935",
|
||||
errorBg: "#E5393520",
|
||||
success: "#4F8A10",
|
||||
warn: "#FF5722",
|
||||
favorite: "#ffd700",
|
||||
red: "#f44336",
|
||||
orange: "#FF9800",
|
||||
yellow: "#f0c800",
|
||||
green: "#4CAF50",
|
||||
blue: "#2196F3",
|
||||
purple: "#9568ED",
|
||||
gray: "#9E9E9E",
|
||||
};
|
||||
}
|
||||
}
|
||||
13
apps/importer/src/theme/font/fontsize.ts
Normal file
13
apps/importer/src/theme/font/fontsize.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export class FontSizeFactory {
|
||||
static construct() {
|
||||
return {
|
||||
heading: "48px",
|
||||
subheading: "24px",
|
||||
title: "16px",
|
||||
subtitle: "14px",
|
||||
body: "14px",
|
||||
input: "12px",
|
||||
subBody: "12px",
|
||||
};
|
||||
}
|
||||
}
|
||||
19
apps/importer/src/theme/font/index.ts
Normal file
19
apps/importer/src/theme/font/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { FontSizeFactory } from "./fontsize";
|
||||
|
||||
export class FontFactory {
|
||||
static construct() {
|
||||
return {
|
||||
fontSizes: FontSizeFactory.construct(),
|
||||
fontWeights: {
|
||||
normal: 400,
|
||||
body: 400,
|
||||
heading: 600,
|
||||
bold: 600,
|
||||
},
|
||||
fonts: {
|
||||
body: `Open Sans,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Oxygen-Sans,Ubuntu,Cantarell,sans-serif;`,
|
||||
heading: `Open Sans,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Oxygen-Sans,Ubuntu,Cantarell,sans-serif;`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
24
apps/importer/src/theme/index.ts
Normal file
24
apps/importer/src/theme/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import ColorSchemeFactory from "./colorscheme";
|
||||
import VariantsFactory from "./variants";
|
||||
import { FontFactory } from "./font";
|
||||
import { Theme } from "theme-ui";
|
||||
|
||||
export class ThemeFactory {
|
||||
static construct(): Theme {
|
||||
return {
|
||||
breakpoints: ["480px", "1000px", "1000px"],
|
||||
space: [0, 5, 10, 15, 20, 25, 30, 35],
|
||||
sizes: { full: "100%", half: "50%" },
|
||||
radii: { none: 0, default: 10 },
|
||||
colors: {
|
||||
modes: {
|
||||
dark: ColorSchemeFactory.construct("dark"),
|
||||
light: ColorSchemeFactory.construct("light"),
|
||||
},
|
||||
},
|
||||
rawColors: ColorSchemeFactory.construct("light"),
|
||||
...FontFactory.construct(),
|
||||
...new VariantsFactory(),
|
||||
};
|
||||
}
|
||||
}
|
||||
76
apps/importer/src/theme/variants/button.ts
Normal file
76
apps/importer/src/theme/variants/button.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
class ButtonFactory {
|
||||
constructor() {
|
||||
return {
|
||||
default: new Default(),
|
||||
primary: new Primary(),
|
||||
secondary: new Secondary(),
|
||||
tertiary: new Tertiary(),
|
||||
};
|
||||
}
|
||||
}
|
||||
export default ButtonFactory;
|
||||
|
||||
class Default {
|
||||
constructor() {
|
||||
return {
|
||||
bg: "transparent",
|
||||
fontFamily: "body",
|
||||
fontWeight: "body",
|
||||
fontSize: "body",
|
||||
borderRadius: "5px",
|
||||
cursor: "pointer",
|
||||
p: 1,
|
||||
px: 2,
|
||||
transition: "filter 200ms ease-in, box-shadow 200ms ease-out",
|
||||
":hover:not(:disabled)": {
|
||||
filter: "brightness(90%)",
|
||||
},
|
||||
":active": {
|
||||
filter: "brightness(98%)",
|
||||
},
|
||||
outline: "none",
|
||||
":focus-visible:not(:active)": {
|
||||
boxShadow: "0px 0px 0px 2px var(--text)",
|
||||
},
|
||||
":disabled": {
|
||||
opacity: 0.5,
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Primary {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "buttons.default",
|
||||
color: "static",
|
||||
bg: "primary",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Secondary {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "buttons.default",
|
||||
color: "text",
|
||||
bg: "border",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Tertiary {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "buttons.default",
|
||||
color: "text",
|
||||
bg: "transparent",
|
||||
border: "2px solid",
|
||||
borderColor: "border",
|
||||
":hover": {
|
||||
borderColor: "primary",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
14
apps/importer/src/theme/variants/index.ts
Normal file
14
apps/importer/src/theme/variants/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import ButtonFactory from "./button";
|
||||
import InputFactory from "./input";
|
||||
import TextFactory from "./text";
|
||||
|
||||
class VariantFactory {
|
||||
constructor() {
|
||||
return {
|
||||
buttons: new ButtonFactory(),
|
||||
forms: new InputFactory(),
|
||||
text: new TextFactory(),
|
||||
};
|
||||
}
|
||||
}
|
||||
export default VariantFactory;
|
||||
64
apps/importer/src/theme/variants/input.ts
Normal file
64
apps/importer/src/theme/variants/input.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
class InputFactory {
|
||||
constructor() {
|
||||
return {
|
||||
input: new Default(),
|
||||
error: new Error(),
|
||||
clean: new Clean(),
|
||||
};
|
||||
}
|
||||
}
|
||||
export default InputFactory;
|
||||
|
||||
class Default {
|
||||
constructor() {
|
||||
return {
|
||||
borderRadius: "default",
|
||||
border: "none",
|
||||
// borderColor: "border",
|
||||
boxShadow: "0px 0px 0px 1px var(--border) inset",
|
||||
fontFamily: "body",
|
||||
fontWeight: "body",
|
||||
fontSize: "input",
|
||||
color: "text",
|
||||
outline: "none",
|
||||
":focus": {
|
||||
boxShadow: "0px 0px 0px 1.5px var(--primary) inset",
|
||||
},
|
||||
":hover:not(:focus)": {
|
||||
boxShadow: "0px 0px 0px 1px var(--dimPrimary) inset",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Clean {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "forms.input",
|
||||
outline: "none",
|
||||
boxShadow: "none",
|
||||
":focus": {
|
||||
boxShadow: "none",
|
||||
},
|
||||
":hover:not(:focus)": {
|
||||
boxShadow: "none",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Error {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "forms.input",
|
||||
boxShadow: "0px 0px 0px 1px var(--error) inset",
|
||||
outline: "none",
|
||||
":focus": {
|
||||
boxShadow: "0px 0px 0px 1.5px var(--error) inset",
|
||||
},
|
||||
":hover:not(:focus)": {
|
||||
boxShadow: "0px 0px 0px 1px var(--error) inset",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
85
apps/importer/src/theme/variants/text.ts
Normal file
85
apps/importer/src/theme/variants/text.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
class TextFactory {
|
||||
constructor() {
|
||||
return {
|
||||
default: new Default(),
|
||||
heading: new Heading(),
|
||||
subheading: new Subheading(),
|
||||
title: new Title(),
|
||||
subtitle: new Subtitle(),
|
||||
body: new Body(),
|
||||
subBody: new SubBody(),
|
||||
error: new Error(),
|
||||
};
|
||||
}
|
||||
}
|
||||
export default TextFactory;
|
||||
|
||||
class Default {
|
||||
constructor() {
|
||||
return {
|
||||
color: "text",
|
||||
fontFamily: "body",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Heading {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "text.default",
|
||||
fontFamily: "heading",
|
||||
fontWeight: "bold",
|
||||
fontSize: "heading",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Subheading {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "text.heading",
|
||||
fontSize: "subheading",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Title {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "text.heading",
|
||||
fontSize: "title",
|
||||
fontWeight: "bold",
|
||||
};
|
||||
}
|
||||
}
|
||||
class Subtitle {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "text.heading",
|
||||
fontSize: "subtitle",
|
||||
fontWeight: "bold",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Body {
|
||||
constructor() {
|
||||
return { variant: "text.default", fontSize: "body" };
|
||||
}
|
||||
}
|
||||
|
||||
class SubBody {
|
||||
constructor() {
|
||||
return {
|
||||
variant: "text.default",
|
||||
fontSize: "subBody",
|
||||
color: "fontTertiary",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Error {
|
||||
constructor() {
|
||||
return { variant: "text.default", fontSize: "subBody", color: "error" };
|
||||
}
|
||||
}
|
||||
1
apps/importer/src/utils/version.ts
Normal file
1
apps/importer/src/utils/version.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const appVersion = process.env.REACT_APP_VERSION;
|
||||
21
apps/importer/tsconfig.json
Normal file
21
apps/importer/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
const { execSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { JSDOM } = require("jsdom");
|
||||
const generateRobot = require("./generateRobot");
|
||||
const generateSitemap = require("./generateSitemap");
|
||||
@@ -43,15 +44,16 @@ function createTrackerScript(document) {
|
||||
return script;
|
||||
}
|
||||
|
||||
(function () {
|
||||
(function() {
|
||||
const output = buildDocs();
|
||||
console.log(output)
|
||||
if (!output.includes("0 errors")) {
|
||||
console.error(output);
|
||||
return;
|
||||
}
|
||||
console.log("Docs generated!");
|
||||
|
||||
const outputPath = output.split("\n")[2].trim().substring(8).trim();
|
||||
const outputPath = /OUTPUT: (.*$)/gm.exec(output)[1];
|
||||
|
||||
console.log(`Documentation output path:`, outputPath);
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ If you face any problem while importing your data from other note apps, please r
|
||||
|
||||
Notesnook importer is your one stop location to import all your notes from any notes app. We have added support for most of the popular note apps and common export formats such as markdown, html and text files.
|
||||
|
||||
<img src="../static/notesnook_importer.png" alt="Notesnook importer"/>
|
||||
<img src="/static/notesnook_importer.png" alt="Notesnook importer"/>
|
||||
|
||||
## Try it out
|
||||
|
||||
You can try out the importer by going to [https://importer.notesnook.com](https://importer.notesnook.com) to import your notes from any notes app.
|
||||
|
||||
## Supported note apps and formats
|
||||
@@ -32,5 +33,5 @@ You can try out the importer by going to [https://importer.notesnook.com](https:
|
||||
**Don't see your notes app?** No worries, create an issue on [github](https://github.com/streetwriters/notesnook/issues)
|
||||
|
||||
## Is it safe to import?
|
||||
Not a single byte of your data from other apps is sent to our servers. Everything is processed 100% on the client side inside this browser.
|
||||
|
||||
Not a single byte of your data from other apps is sent to our servers. Everything is processed 100% on the client side inside this browser.
|
||||
|
||||
@@ -10,24 +10,20 @@ The following steps will help you import your notes from Bear notes easily.
|
||||
1. Open Bear Notes app on Desktop.
|
||||
|
||||
2. Click on `File` and select `Backup Notes`
|
||||
<img style="margin-bottom:15px;" src="../static/bear_import_step_1.png" alt="Click on `File` and select `Backup Notes`"/>
|
||||
<img style="margin-bottom:15px;" src="/static/bear_import_step_1.png" alt="Click on `File` and select `Backup Notes`"/>
|
||||
|
||||
2. Select the location where you want to store the backup file. It will be a `.bearbk` file.
|
||||
<img style="margin-bottom:15px;" src="../static/bear_import_step_2.png" alt="Select the location where you want to store the backup file. It will be a `.bearbk` file."/>
|
||||
3. Select the location where you want to store the backup file. It will be a `.bearbk` file.
|
||||
<img style="margin-bottom:15px;" src="/static/bear_import_step_2.png" alt="Select the location where you want to store the backup file. It will be a `.bearbk` file."/>
|
||||
|
||||
4. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Bear Notes` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/bearnotes_import_step_3.png" alt="Go to https://importer.notesnook.com and select `Bear Notes` from list of apps."/>
|
||||
<img style="margin-bottom:15px;" src="/static/bearnotes_import_step_3.png" alt="Go to https://importer.notesnook.com and select `Bear Notes` from list of apps."/>
|
||||
|
||||
5. Drop the `.bearbk` backup file you exported earlier from Bear Notes in the box or click anywhere to open system file picker to select the file
|
||||
<img style="margin-bottom:15px;" src="../static/bear_import_step_4.png" alt="Drop the `.bearbk` backup file you exported earlier from Bear Notes in the box or click anywhere to open system file picker to select the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/bear_import_step_4.png" alt="Drop the `.bearbk` backup file you exported earlier from Bear Notes in the box or click anywhere to open system file picker to select the file."/>
|
||||
|
||||
6. Click on `Import 1 file` button to start importing.
|
||||
|
||||
7. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,26 +10,22 @@ The following steps will help you import your notes from EverNote easily.
|
||||
1. Open EverNote app on Desktop.
|
||||
|
||||
2. Go to `Notebooks` from the side menu
|
||||
<img style="margin-bottom:15px;" src="../static/evernote_import_step_1.png" alt="Go to `Notebooks` from the side menu"/>
|
||||
<img style="margin-bottom:15px;" src="/static/evernote_import_step_1.png" alt="Go to `Notebooks` from the side menu"/>
|
||||
|
||||
2. Click on the `three-dot` button on each notebook and click on `Export Notebook`
|
||||
<img style="margin-bottom:15px;" src="../static/evernote_import_step_2.png" alt="Click on the `three-dot` button on each notebook and click on `Export Notebook`"/>
|
||||
3. Click on the `three-dot` button on each notebook and click on `Export Notebook`
|
||||
<img style="margin-bottom:15px;" src="/static/evernote_import_step_2.png" alt="Click on the `three-dot` button on each notebook and click on `Export Notebook`"/>
|
||||
|
||||
4. Choose `.enex` format and click on `Export` and save it to desired location.
|
||||
<img style="margin-bottom:15px;" src="../static/evernote_import_step_3.png" alt="Choose `.enex` format and click on `Export`"/>
|
||||
<img style="margin-bottom:15px;" src="/static/evernote_import_step_3.png" alt="Choose `.enex` format and click on `Export`"/>
|
||||
|
||||
5. Repeat this for all your Notebooks.
|
||||
|
||||
6. Drop the `.enex` backup files you exported earlier from EverNote in the box or click anywhere to open system file picker to select the file
|
||||
<img style="margin-bottom:15px;" src="../static/evernote_import_step_4.png" alt="Drop the `.enex` backup files you exported earlier from EverNote in the box or click anywhere to open system file picker to select the file"/>
|
||||
<img style="margin-bottom:15px;" src="/static/evernote_import_step_4.png" alt="Drop the `.enex` backup files you exported earlier from EverNote in the box or click anywhere to open system file picker to select the file"/>
|
||||
|
||||
6. Click on `Import N files` button to start importing.
|
||||
7. Click on `Import N files` button to start importing.
|
||||
|
||||
7. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
8. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,24 +10,23 @@ The following steps will help you import your notes from Google Keep easily.
|
||||
1. Go to [Google Takeout](https://takeout.google.com/settings/takeout) and Login to your account.
|
||||
|
||||
2. On Google Takeout Page, first click on `Deselect all` then scroll down and select `Keep` and click `Next Step`.
|
||||
<img style="margin-bottom:15px;" src="../static/googlekeep_import_step_1.png" alt="On Google Takeout Page, first click on `Deselect all` then scroll down and select `Keep` and click `Next Step`."/>
|
||||
<img style="margin-bottom:15px;" src="/static/googlekeep_import_step_1.png" alt="On Google Takeout Page, first click on `Deselect all` then scroll down and select `Keep` and click `Next Step`."/>
|
||||
|
||||
3. Click on create export
|
||||
<img style="margin-bottom:15px;" src="../static/googlekeep_import_step_2.png" alt="Click on create export"/>
|
||||
<img style="margin-bottom:15px;" src="/static/googlekeep_import_step_2.png" alt="Click on create export"/>
|
||||
|
||||
4. Download your exported file.
|
||||
<img style="margin-bottom:15px;" src="../static/googlekeep_import_step_3.png" alt="Download your exported file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/googlekeep_import_step_3.png" alt="Download your exported file."/>
|
||||
|
||||
5. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Google Keep` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/googlekeep_import_step_4.png" alt="Go to [Notesnook Importer](https://importer.notesnook.com) and select `Google Keep` from list of apps."/>
|
||||
<img style="margin-bottom:15px;" src="/static/googlekeep_import_step_4.png" alt="Go to [Notesnook Importer](https://importer.notesnook.com) and select `Google Keep` from list of apps."/>
|
||||
|
||||
6. Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="../static/googlekeep_import_step_5.png" alt="Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup."/>
|
||||
<img style="margin-bottom:15px;" src="/static/googlekeep_import_step_5.png" alt="Drop the .zip backup file(s) you exported earlier from Google Takeout in the box or click anywhere to open system file picker to select the backup."/>
|
||||
|
||||
7. Click on `Import N files` button to start importing.
|
||||
|
||||
8. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
label: Html files
|
||||
order: 800
|
||||
---
|
||||
|
||||
# How do I import notes from html files?
|
||||
|
||||
To import html files to notesnook go to Notesnook Importer on your PC/Laptop.
|
||||
|
||||
1. From the list of formats to import, select "Html file".
|
||||
<img style="margin-bottom:15px;" src="../static/html_import_step_1.png" alt="From the list of formats to import, select Html file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/html_import_step_1.png" alt="From the list of formats to import, select Html file."/>
|
||||
|
||||
2. Drop your text files or click anywhere inside the box to browse and select your html files.
|
||||
<img style="margin-bottom:15px;" src="../static/html_import_step_2.png" alt="Drop your text files or click anywhere inside the box to browse and select your html files."/>
|
||||
<img style="margin-bottom:15px;" src="/static/html_import_step_2.png" alt="Drop your text files or click anywhere inside the box to browse and select your html files."/>
|
||||
|
||||
3. Click on import files button on bottom right corner to start importing files
|
||||
4. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
@@ -2,27 +2,24 @@
|
||||
label: Joplin
|
||||
order: -100
|
||||
---
|
||||
|
||||
# How do I import notes from Joplin notes app?
|
||||
|
||||
The following steps will help you import your notes from Joplin easily.
|
||||
|
||||
1. Open Joplin app on Desktop.
|
||||
2. Click on `File` and select `Export All -> JEX - Joplin Export File` and save it in desired location.
|
||||
<img style="margin-bottom:15px;" src="../static/joplin_import_step_1.png" alt="Click on `File` and select `Export All -> JEX - Joplin Export File`"/>
|
||||
<img style="margin-bottom:15px;" src="/static/joplin_import_step_1.png" alt="Click on `File` and select `Export All -> JEX - Joplin Export File`"/>
|
||||
|
||||
4) Go to [Notesnook Importer](https://importer.notesnook.com) and select `Joplin` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="/static/joplin_import_step_2.png" alt=" Go to Notesnook Importer https://importer.notesnook.com and select `Joplin` from list of apps."/>
|
||||
|
||||
4. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Joplin` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/joplin_import_step_2.png" alt=" Go to Notesnook Importer https://importer.notesnook.com and select `Joplin` from list of apps."/>
|
||||
5) Drop the .jex backup file you exported earlier from Joplin in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="/static/joplin_import_step_3.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
|
||||
5. Drop the .jex backup file you exported earlier from Joplin in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="../static/joplin_import_step_3.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
6) Click on `Import 1 file` button to start importing.
|
||||
|
||||
6. Click on `Import 1 file` button to start importing.
|
||||
|
||||
7. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
7) Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
label: Markdown files
|
||||
order: 700
|
||||
---
|
||||
|
||||
# How do I import notes from markdown files?
|
||||
|
||||
To import markdown files to notesnook go to Notesnook Importer on your PC/Laptop.
|
||||
|
||||
1. From the list of formats to import, select "Markdown file".
|
||||
<img style="margin-bottom:15px;" src="../static/markdown_import_step_1.png" alt="From the list of formats to import, select Markdown file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/markdown_import_step_1.png" alt="From the list of formats to import, select Markdown file."/>
|
||||
|
||||
2. Drop your markdown files or click anywhere inside the box to browse and select your markdown files.
|
||||
<img style="margin-bottom:15px;" src="../static/markdown_import_step_2.png" alt="Drop your markdown files or click anywhere inside the box to browse and select your markdown files."/>
|
||||
<img style="margin-bottom:15px;" src="/static/markdown_import_step_2.png" alt="Drop your markdown files or click anywhere inside the box to browse and select your markdown files."/>
|
||||
|
||||
3. Click on import files button on bottom right corner to start importing files
|
||||
4. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
@@ -8,22 +8,18 @@ order: 600
|
||||
The following steps will help you import your notes from OneNote easily.
|
||||
|
||||
1. Go to [Notesnook Importer](https://importer.notesnook.com) and select `OneNote` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/onenote_import_step_1.png" alt="Go to Notesnook Importer]https://importer.notesnook.com and select `OneNote` from list of apps."/>
|
||||
<img style="margin-bottom:15px;" src="/static/onenote_import_step_1.png" alt="Go to Notesnook Importer]https://importer.notesnook.com and select `OneNote` from list of apps."/>
|
||||
|
||||
2. Click on `Sign in with Microsoft`
|
||||
<img style="margin-bottom:15px;" src="../static/onenote_import_step_2.png" alt="Click on `Sign in with Microsoft`"/>
|
||||
<img style="margin-bottom:15px;" src="/static/onenote_import_step_2.png" alt="Click on `Sign in with Microsoft`"/>
|
||||
|
||||
3. Login to your account
|
||||
<img style="margin-bottom:15px;" src="../static/onenote_import_step_3.png" alt="Login to your account"/>
|
||||
<img style="margin-bottom:15px;" src="/static/onenote_import_step_3.png" alt="Login to your account"/>
|
||||
|
||||
4. Please wait while all your notes are imported.
|
||||
<img style="margin-bottom:15px;" src="../static/onenote_import_step_4.png" alt="Please wait while all your notes are imported."/>
|
||||
<img style="margin-bottom:15px;" src="/static/onenote_import_step_4.png" alt="Please wait while all your notes are imported."/>
|
||||
|
||||
4. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
5. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
label: Plain text files
|
||||
order: 900
|
||||
---
|
||||
|
||||
# How do I import notes from plain text files?
|
||||
|
||||
To import plain text files to notesnook go to Notesnook Importer on your PC/Laptop.
|
||||
|
||||
1. From the list of formats to import, select "Plain Text".
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_1.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_1.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
|
||||
2. Drop your text files or click anywhere inside the box to browse and select your text files.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_2.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_2.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
|
||||
3. Click on import files button on bottom right corner to start importing files
|
||||
4. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="From the list of formats to import, select Plain Text."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
@@ -2,28 +2,29 @@
|
||||
label: Simplenote
|
||||
order: 100
|
||||
---
|
||||
|
||||
# How to import notes from Simplenote notes app?
|
||||
|
||||
The following steps will help you import your notes from Simplenote easily.
|
||||
|
||||
1. Open Simplenote app on Desktop or Login to [https://app.simplenote.com](https://app.simplenote.com).
|
||||
2. Open sidebar and click on Settings.
|
||||
<img style="margin-bottom:15px;" src="../static/simplenote_import_step_1.png" alt="Open sidebar and click on Settings."/>
|
||||
<img style="margin-bottom:15px;" src="/static/simplenote_import_step_1.png" alt="Open sidebar and click on Settings."/>
|
||||
|
||||
3. Go to `Tools` tab in Settings
|
||||
<img style="margin-bottom:15px;" src="../static/simplenote_import_step_2.png" alt="Go to Tools tab"/>
|
||||
<img style="margin-bottom:15px;" src="/static/simplenote_import_step_2.png" alt="Go to Tools tab"/>
|
||||
|
||||
4. Click on `Export notes` to download your notes as a .zip file.
|
||||
|
||||
5. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Simplenote` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/simplenote_import_step_3.png" alt="Go to [Notesnook Importer](https://importer.notesnook.com) and select `Simplenote` from list of apps."/>
|
||||
<img style="margin-bottom:15px;" src="/static/simplenote_import_step_3.png" alt="Go to [Notesnook Importer](https://importer.notesnook.com) and select `Simplenote` from list of apps."/>
|
||||
|
||||
6. Drop the .zip backup file you exported earlier from Simplenote in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="../static/simplenote_import_step_4.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
<img style="margin-bottom:15px;" src="/static/simplenote_import_step_4.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
|
||||
7. Click on `Import 1 file` button to start importing.
|
||||
|
||||
8. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
@@ -2,31 +2,29 @@
|
||||
label: Standard Notes
|
||||
order: 300
|
||||
---
|
||||
|
||||
# How do I import notes from Standard Notes?
|
||||
|
||||
The following steps will help you import your notes from Standard notes easily.
|
||||
|
||||
1. Open Standard Notes app on Desktop or visit [https://app.standardnotes.org](https://app.standardnotes.org) and Login to your account.
|
||||
2. Click on `Account` on bottom left corner
|
||||
<img style="margin-bottom:15px;" src="../static/snnotes_import_step_1.png" alt="Click on `Account` on bottom left corner"/>
|
||||
<img style="margin-bottom:15px;" src="/static/snnotes_import_step_1.png" alt="Click on `Account` on bottom left corner"/>
|
||||
|
||||
2. Scroll down to `Data backups` section and choose backup type `Decrypted`.
|
||||
<img style="margin-bottom:15px;" src="../static/snnotes_import_step_2.png" alt="Scroll down to `Data backups` section and choose backup type Decrypted."/>
|
||||
3. Scroll down to `Data backups` section and choose backup type `Decrypted`.
|
||||
<img style="margin-bottom:15px;" src="/static/snnotes_import_step_2.png" alt="Scroll down to `Data backups` section and choose backup type Decrypted."/>
|
||||
|
||||
3. Click on `Download backup` to download your backup file. It will be .zip file.
|
||||
4. Click on `Download backup` to download your backup file. It will be .zip file.
|
||||
|
||||
4. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Standard Notes` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="../static/snnotes_import_step_3.png" alt="Go to https://importer.notesnook.com and select Standard Notes from list of apps."/>
|
||||
5. Go to [Notesnook Importer](https://importer.notesnook.com) and select `Standard Notes` from list of apps.
|
||||
<img style="margin-bottom:15px;" src="/static/snnotes_import_step_3.png" alt="Go to https://importer.notesnook.com and select Standard Notes from list of apps."/>
|
||||
|
||||
5. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="../static/snnotes_import_step_4.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
6. Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
|
||||
<img style="margin-bottom:15px;" src="/static/snnotes_import_step_4.png" alt="Drop the .zip backup file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup."/>
|
||||
|
||||
6. Click on `Import 1 file` button to start importing.
|
||||
7. Click on `Import 1 file` button to start importing.
|
||||
|
||||
7. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="../static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
8. Click on `Download .nnbackup` to download the file.
|
||||
<img style="margin-bottom:15px;" src="/static/plain_text_import_step_3.png" alt="Click on `Download .nnbackup` to download the file."/>
|
||||
|
||||
After you have downloaded the `.nnbackup` file, go to Notesnook app and [restore backup](../backup-and-restore-notes-in-notesnook.md) file to get all your notes.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,20 +9,21 @@ Colors are a simple and fast way to organize your notes.
|
||||
## Assign color to note
|
||||
|
||||
+++ Desktop/Web
|
||||
|
||||
1. Click on the `three dot more` button or right click on a note to open `Note properties` menu
|
||||
2. Add note to the desired color
|
||||
|
||||

|
||||

|
||||
|
||||
3. You can click on the color again to remove note from the color
|
||||
+++ Mobile/Tablet
|
||||
+++ Mobile/Tablet
|
||||
1. Press the `three-dot more` button on right side on note to open `Note properties` sheet
|
||||
|
||||
<img style="width:300px;margin-bottom:15px" src="../static/color_note_step_1.jpg" alt="Click on the more button on right side of note."/>
|
||||
<img style="width:300px;margin-bottom:15px" src="/static/color_note_step_1.jpg" alt="Click on the more button on right side of note."/>
|
||||
|
||||
2. Add note to the desired color
|
||||
|
||||
<img style="width:300px;margin-bottom:15px" src="../static/color_note_step_2.jpg" alt="Select the desired color from the Bottom Sheet Menu."/>
|
||||
<img style="width:300px;margin-bottom:15px" src="/static/color_note_step_2.jpg" alt="Select the desired color from the Bottom Sheet Menu."/>
|
||||
|
||||
3. You can press on the color again to remove note from the color
|
||||
+++
|
||||
@@ -31,4 +32,4 @@ Colors are a simple and fast way to organize your notes.
|
||||
|
||||
A shortcut to notes of specific color appears on side menu by default. The shortcut will appear as long as there is at least one note in a color.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
---
|
||||
label: Favorites
|
||||
---
|
||||
|
||||
# How can I add notes to favorites?
|
||||
|
||||
While you organize your notes with all different types of notebooks, tags and colors, favorites give you quick access to the notes that are most important to you in one tap.
|
||||
|
||||
## Add a note to favorites
|
||||
|
||||
+++ Desktop/Web
|
||||
|
||||
1. Righ click on a note or click on the three-dot more button to open `Note properties` menu
|
||||
2. Toggle favorite
|
||||
3. You can click again to remove note from favorites.
|
||||
+++ Mobile/Tablet
|
||||
1. Press the `three-dot more` button on the note to open `Note propreties` sheet
|
||||
4. Press the `three-dot more` button on the note to open `Note propreties` sheet
|
||||
|
||||
<img style="width:300px;margin-bottom:15px" src="../static/color_note_step_1.jpg" alt="Click on the more button on right side of note."/>
|
||||
<img style="width:300px;margin-bottom:15px" src="/static/color_note_step_1.jpg" alt="Click on the more button on right side of note."/>
|
||||
|
||||
2. Toggle favorite in the column.
|
||||
|
||||
<img style="width:300px;margin-bottom:15px" src="../static/favorite_note.jpg" alt="Toggle favorite in the column."/>
|
||||
<img style="width:300px;margin-bottom:15px" src="/static/favorite_note.jpg" alt="Toggle favorite in the column."/>
|
||||
|
||||
4. You can press again on favorite to remove note from favorites
|
||||
+++
|
||||
Now if you go to the Favorites section from Side Menu, you will find your note there.
|
||||

|
||||
Now if you go to the Favorites section from Side Menu, you will find your note there.
|
||||

|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"input": ".",
|
||||
"output": "public_html",
|
||||
"branding": {
|
||||
"title": "Notesnook",
|
||||
"label": "Docs",
|
||||
"colors": {
|
||||
"label": {
|
||||
"text": "#ffffff",
|
||||
"background": "#00c853"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exclude": ["*node_modules/", "*__generator__/"],
|
||||
"favicon": "favicon.png",
|
||||
"meta": {
|
||||
"title": " | Notesnook Docs"
|
||||
},
|
||||
"edit": {
|
||||
"repo": "https://github.com/streetwriters/notesnook",
|
||||
"base": "docs"
|
||||
},
|
||||
"links": [],
|
||||
"footer": {
|
||||
"copyright": "© Copyright {{ year }} [Streetwriters LLC](https://streetwriters.co/). All rights reserved."
|
||||
}
|
||||
}
|
||||
22
docs/retype.yml
Normal file
22
docs/retype.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
input: .
|
||||
output: public_html
|
||||
url: https://docs.notesnook.com
|
||||
branding:
|
||||
title: Notesnook
|
||||
label: Docs
|
||||
colors:
|
||||
label:
|
||||
text: "#ffffff"
|
||||
background: "#00C853"
|
||||
|
||||
exclude:
|
||||
- "*node_modules/"
|
||||
- "*__generator__/"
|
||||
favicon: favicon.png
|
||||
meta:
|
||||
title: " | Notesnook Docs"
|
||||
edit:
|
||||
repo: "https://github.com/streetwriters/notesnook"
|
||||
base: "docs"
|
||||
footer:
|
||||
copyright: "© Copyright {{ year }} [Streetwriters LLC](https://streetwriters.co/). All rights reserved."
|
||||
@@ -1,3 +0,0 @@
|
||||
icon: typography
|
||||
label: Rich text editing
|
||||
order: 950
|
||||
@@ -1,6 +1,6 @@
|
||||
echo "Installing retype"
|
||||
|
||||
dotnet tool install --global retypeapp -- version 1.8.0
|
||||
dotnet tool install --global retypeapp
|
||||
|
||||
echo -n "Changing directory..."
|
||||
|
||||
|
||||
13
docs/versionhistory.md
Normal file
13
docs/versionhistory.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
icon: history
|
||||
label: Version History
|
||||
order: -5
|
||||
---
|
||||
|
||||
To improve the overall reliability of Notesnook we have introduced version history of notes in v1.7.0 of desktop and mobile apps. Version history is enabled by default for all your notes including notes in vault.
|
||||
|
||||
## How it works
|
||||
When you create a new note or open an existing note in the editor, a session is generated from the note opened in editor. All the changes made to the note are stored in that session. So if you open a note and edit it at 10 different times during the day, 10 versions of note history are generated and stored.
|
||||
|
||||
## Is Note history synced to all my devices?
|
||||
No, at the moment we have decided to keep note version history local only and it is not synced. Hence if you logout from your account, note version history will be deleted.
|
||||
31
notesnook.code-workspace
Normal file
31
notesnook.code-workspace
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "✨ notesnook",
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"name": "🚀 @notesnook/importer-web",
|
||||
"path": "apps/importer"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/crypto",
|
||||
"path": "packages/crypto"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/enex",
|
||||
"path": "packages/enex"
|
||||
},
|
||||
{
|
||||
"name": "📦 @notesnook/importer",
|
||||
"path": "packages/importer"
|
||||
},
|
||||
{
|
||||
"name": "📦 @streetwriters/tinymce-plugins",
|
||||
"path": "packages/tinymce-plugins"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"git.ignoreLimitWarning": true
|
||||
}
|
||||
}
|
||||
56575
package-lock.json
generated
Normal file
56575
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
package.json
Normal file
38
package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "notesnook",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/streetwriters/notesnook.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/streetwriters/notesnook/issues"
|
||||
},
|
||||
"homepage": "https://github.com/streetwriters/notesnook#readme",
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.18.1",
|
||||
"turbo": "^1.0.19"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"apps/*"
|
||||
],
|
||||
"turbo": {
|
||||
"baseBranch": "origin/main",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
packages/crypto/.npmignore
Normal file
5
packages/crypto/.npmignore
Normal file
@@ -0,0 +1,5 @@
|
||||
*
|
||||
|
||||
!dist/**/**/*
|
||||
!package.json
|
||||
!README.md
|
||||
124
packages/crypto/index.ts
Normal file
124
packages/crypto/index.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { ready } from "libsodium-wrappers";
|
||||
import Decryption from "./src/decryption";
|
||||
import Encryption from "./src/encryption";
|
||||
import { INNCrypto, IStreamable } from "./src/interfaces";
|
||||
import KeyUtils from "./src/keyutils";
|
||||
import Password from "./src/password";
|
||||
import {
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey,
|
||||
Chunk,
|
||||
} from "./src/types";
|
||||
|
||||
export class NNCrypto implements INNCrypto {
|
||||
private isReady: boolean = false;
|
||||
|
||||
private async init() {
|
||||
if (this.isReady) return;
|
||||
await ready;
|
||||
this.isReady = true;
|
||||
}
|
||||
|
||||
async encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
outputFormat: OutputFormat = "uint8array"
|
||||
): Promise<Cipher> {
|
||||
await this.init();
|
||||
return Encryption.encrypt(key, plaintext, outputFormat);
|
||||
}
|
||||
|
||||
async decrypt(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat: OutputFormat = "text"
|
||||
): Promise<Plaintext> {
|
||||
await this.init();
|
||||
return Decryption.decrypt(key, cipherData, outputFormat);
|
||||
}
|
||||
|
||||
async hash(password: string, salt: string): Promise<string> {
|
||||
await this.init();
|
||||
return Password.hash(password, salt);
|
||||
}
|
||||
|
||||
async deriveKey(password: string, salt?: string): Promise<EncryptionKey> {
|
||||
await this.init();
|
||||
return KeyUtils.deriveKey(password, salt);
|
||||
}
|
||||
|
||||
async exportKey(password: string, salt?: string): Promise<SerializedKey> {
|
||||
await this.init();
|
||||
return KeyUtils.exportKey(password, salt);
|
||||
}
|
||||
|
||||
async createEncryptionStream(
|
||||
key: SerializedKey,
|
||||
stream: IStreamable
|
||||
): Promise<string> {
|
||||
await this.init();
|
||||
const encryptionStream = Encryption.createStream(key);
|
||||
while (true) {
|
||||
const chunk = await stream.read();
|
||||
if (!chunk) break;
|
||||
|
||||
const { data, final } = chunk;
|
||||
if (!data) break;
|
||||
|
||||
const encryptedChunk: Chunk = {
|
||||
data: encryptionStream.write(data, final),
|
||||
final,
|
||||
};
|
||||
await stream.write(encryptedChunk);
|
||||
|
||||
if (final) break;
|
||||
}
|
||||
return encryptionStream.header;
|
||||
}
|
||||
|
||||
async createDecryptionStream(
|
||||
iv: string,
|
||||
key: SerializedKey,
|
||||
stream: IStreamable
|
||||
) {
|
||||
await this.init();
|
||||
const decryptionStream = Decryption.createStream(iv, key);
|
||||
while (true) {
|
||||
const chunk = await stream.read();
|
||||
if (!chunk) break;
|
||||
|
||||
const { data, final } = chunk;
|
||||
if (!data) break;
|
||||
|
||||
const decryptedChunk: Chunk = {
|
||||
data: decryptionStream.read(data),
|
||||
final,
|
||||
};
|
||||
await stream.write(decryptedChunk);
|
||||
|
||||
if (final) break;
|
||||
}
|
||||
}
|
||||
|
||||
async encryptStream(
|
||||
key: SerializedKey,
|
||||
stream: IStreamable,
|
||||
_streamId?: string
|
||||
): Promise<string> {
|
||||
await this.init();
|
||||
return await this.createEncryptionStream(key, stream);
|
||||
}
|
||||
|
||||
async decryptStream(
|
||||
key: SerializedKey,
|
||||
iv: string,
|
||||
stream: IStreamable,
|
||||
_streamId?: string
|
||||
): Promise<void> {
|
||||
await this.init();
|
||||
await this.createDecryptionStream(iv, key, stream);
|
||||
}
|
||||
}
|
||||
71
packages/crypto/package-lock.json
generated
Normal file
71
packages/crypto/package-lock.json
generated
Normal file
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "@notesnook/crypto",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/crypto",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"libsodium-wrappers": "^0.7.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/libsodium-wrappers": "^0.7.9"
|
||||
}
|
||||
},
|
||||
"../streamablefs": {
|
||||
"version": "1.0.0",
|
||||
"extraneous": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"localforage": "^1.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/localforage": "^0.0.34",
|
||||
"typescript": "^4.4.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/libsodium-wrappers": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
|
||||
"integrity": "sha512-LisgKLlYQk19baQwjkBZZXdJL0KbeTpdEnrAfz5hQACbklCY0gVFnsKUyjfNWF1UQsCSjw93Sj5jSbiO8RPfdw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/libsodium": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz",
|
||||
"integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A=="
|
||||
},
|
||||
"node_modules/libsodium-wrappers": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
|
||||
"integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==",
|
||||
"dependencies": {
|
||||
"libsodium": "^0.7.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/libsodium-wrappers": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
|
||||
"integrity": "sha512-LisgKLlYQk19baQwjkBZZXdJL0KbeTpdEnrAfz5hQACbklCY0gVFnsKUyjfNWF1UQsCSjw93Sj5jSbiO8RPfdw==",
|
||||
"dev": true
|
||||
},
|
||||
"libsodium": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz",
|
||||
"integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A=="
|
||||
},
|
||||
"libsodium-wrappers": {
|
||||
"version": "0.7.9",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
|
||||
"integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==",
|
||||
"requires": {
|
||||
"libsodium": "^0.7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
packages/crypto/package.json
Normal file
25
packages/crypto/package.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@notesnook/crypto",
|
||||
"private": false,
|
||||
"description": "An easy to use wrapper around libsodium exposing essential cryptographic methods. Currently only Web is supported.",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc --declaration --outDir ./dist"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"libsodium-wrappers": "^0.7.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/libsodium-wrappers": "^0.7.9"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/streetwriters/notesnook.git",
|
||||
"directory": "packages/crypto"
|
||||
}
|
||||
}
|
||||
94
packages/crypto/src/decryption.ts
Normal file
94
packages/crypto/src/decryption.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
crypto_aead_xchacha20poly1305_ietf_decrypt,
|
||||
crypto_secretstream_xchacha20poly1305_init_pull,
|
||||
crypto_secretstream_xchacha20poly1305_pull,
|
||||
to_base64,
|
||||
from_base64,
|
||||
base64_variants,
|
||||
StateAddress,
|
||||
to_string,
|
||||
from_hex,
|
||||
} from "libsodium-wrappers";
|
||||
import KeyUtils from "./keyutils";
|
||||
import {
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey,
|
||||
} from "./types";
|
||||
|
||||
export default class Decryption {
|
||||
private static transformInput(cipherData: Cipher): Uint8Array {
|
||||
let input: Uint8Array | null = null;
|
||||
if (
|
||||
typeof cipherData.cipher === "string" &&
|
||||
cipherData.format === "base64"
|
||||
) {
|
||||
input = from_base64(
|
||||
cipherData.cipher,
|
||||
base64_variants.URLSAFE_NO_PADDING
|
||||
);
|
||||
} else if (
|
||||
typeof cipherData.cipher === "string" &&
|
||||
cipherData.format === "hex"
|
||||
) {
|
||||
input = from_hex(cipherData.cipher);
|
||||
} else if (cipherData.cipher instanceof Uint8Array) {
|
||||
input = cipherData.cipher;
|
||||
}
|
||||
if (!input) throw new Error("Data cannot be null.");
|
||||
return input;
|
||||
}
|
||||
|
||||
static decrypt(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat: OutputFormat = "text"
|
||||
): Plaintext {
|
||||
if (!key.salt && cipherData.salt) key.salt = cipherData.salt;
|
||||
const encryptionKey = KeyUtils.transform(key);
|
||||
|
||||
const input = this.transformInput(cipherData);
|
||||
const plaintext = crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
null,
|
||||
input,
|
||||
null,
|
||||
from_base64(cipherData.iv),
|
||||
encryptionKey.key
|
||||
);
|
||||
|
||||
return {
|
||||
format: outputFormat,
|
||||
data:
|
||||
outputFormat === "base64"
|
||||
? to_base64(plaintext, base64_variants.ORIGINAL)
|
||||
: outputFormat === "text"
|
||||
? to_string(plaintext)
|
||||
: plaintext,
|
||||
};
|
||||
}
|
||||
|
||||
static createStream(header: string, key: SerializedKey): DecryptionStream {
|
||||
return new DecryptionStream(header, KeyUtils.transform(key));
|
||||
}
|
||||
}
|
||||
|
||||
class DecryptionStream {
|
||||
state: StateAddress;
|
||||
constructor(header: string, key: EncryptionKey) {
|
||||
this.state = crypto_secretstream_xchacha20poly1305_init_pull(
|
||||
from_base64(header),
|
||||
key.key
|
||||
);
|
||||
}
|
||||
|
||||
read(chunk: Uint8Array): Uint8Array {
|
||||
const { message } = crypto_secretstream_xchacha20poly1305_pull(
|
||||
this.state,
|
||||
chunk,
|
||||
null
|
||||
);
|
||||
return message;
|
||||
}
|
||||
}
|
||||
105
packages/crypto/src/encryption.ts
Normal file
105
packages/crypto/src/encryption.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt,
|
||||
crypto_secretstream_xchacha20poly1305_init_push,
|
||||
crypto_secretstream_xchacha20poly1305_push,
|
||||
randombytes_buf,
|
||||
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES,
|
||||
crypto_secretstream_xchacha20poly1305_TAG_FINAL,
|
||||
crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
|
||||
to_base64,
|
||||
from_base64,
|
||||
base64_variants,
|
||||
StateAddress,
|
||||
} from "libsodium-wrappers";
|
||||
import KeyUtils from "./keyutils";
|
||||
import {
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey,
|
||||
} from "./types";
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
export default class Encryption {
|
||||
private static transformInput(plaintext: Plaintext): Uint8Array {
|
||||
let data: Uint8Array | null = null;
|
||||
if (typeof plaintext.data === "string" && plaintext.format === "base64") {
|
||||
data = from_base64(plaintext.data, base64_variants.ORIGINAL);
|
||||
} else if (typeof plaintext.data === "string") {
|
||||
data = encoder.encode(plaintext.data);
|
||||
} else if (plaintext.data instanceof Uint8Array) {
|
||||
data = plaintext.data;
|
||||
}
|
||||
if (!data) throw new Error("Data cannot be null.");
|
||||
return data;
|
||||
}
|
||||
|
||||
static encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
outputFormat: OutputFormat = "uint8array"
|
||||
): Cipher {
|
||||
const encryptionKey = KeyUtils.transform(key);
|
||||
const data = this.transformInput(plaintext);
|
||||
|
||||
const nonce = randombytes_buf(crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
|
||||
|
||||
const cipher: string | Uint8Array =
|
||||
crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
data,
|
||||
null,
|
||||
null,
|
||||
nonce,
|
||||
encryptionKey.key
|
||||
);
|
||||
|
||||
let output: string | Uint8Array = cipher;
|
||||
if (outputFormat === "base64") {
|
||||
output = to_base64(cipher, base64_variants.URLSAFE_NO_PADDING);
|
||||
}
|
||||
|
||||
const iv = to_base64(nonce);
|
||||
return {
|
||||
format: outputFormat,
|
||||
alg: getAlgorithm(base64_variants.URLSAFE_NO_PADDING),
|
||||
cipher: output,
|
||||
iv,
|
||||
salt: encryptionKey.salt,
|
||||
length: data.length,
|
||||
};
|
||||
}
|
||||
|
||||
static createStream(key: SerializedKey): EncryptionStream {
|
||||
return new EncryptionStream(KeyUtils.transform(key));
|
||||
}
|
||||
}
|
||||
|
||||
class EncryptionStream {
|
||||
state: StateAddress;
|
||||
header: string;
|
||||
constructor(key: EncryptionKey) {
|
||||
const { state, header } = crypto_secretstream_xchacha20poly1305_init_push(
|
||||
key.key,
|
||||
"base64"
|
||||
);
|
||||
this.state = state;
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
write(chunk: Uint8Array, final?: boolean): Uint8Array {
|
||||
return crypto_secretstream_xchacha20poly1305_push(
|
||||
this.state,
|
||||
chunk,
|
||||
null,
|
||||
final
|
||||
? crypto_secretstream_xchacha20poly1305_TAG_FINAL
|
||||
: crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getAlgorithm(base64Variant: base64_variants) {
|
||||
//Template: encryptionAlgorithm-kdfAlgorithm-base64variant
|
||||
return `xcha-argon2i13-${base64Variant}`;
|
||||
}
|
||||
46
packages/crypto/src/interfaces.ts
Normal file
46
packages/crypto/src/interfaces.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
Cipher,
|
||||
EncryptionKey,
|
||||
OutputFormat,
|
||||
Plaintext,
|
||||
SerializedKey,
|
||||
Chunk,
|
||||
} from "./types";
|
||||
|
||||
export interface IStreamable {
|
||||
read(): Promise<Chunk | undefined>;
|
||||
write(chunk: Chunk | undefined): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INNCrypto {
|
||||
encrypt(
|
||||
key: SerializedKey,
|
||||
plaintext: Plaintext,
|
||||
outputFormat?: OutputFormat
|
||||
): Promise<Cipher>;
|
||||
|
||||
decrypt(
|
||||
key: SerializedKey,
|
||||
cipherData: Cipher,
|
||||
outputFormat?: OutputFormat
|
||||
): Promise<Plaintext>;
|
||||
|
||||
hash(password: string, salt: string): Promise<string>;
|
||||
|
||||
deriveKey(password: string, salt?: string): Promise<EncryptionKey>;
|
||||
|
||||
exportKey(password: string, salt?: string): Promise<SerializedKey>;
|
||||
|
||||
encryptStream(
|
||||
key: SerializedKey,
|
||||
stream: IStreamable,
|
||||
streamId?: string
|
||||
): Promise<string>;
|
||||
|
||||
decryptStream(
|
||||
key: SerializedKey,
|
||||
iv: string,
|
||||
stream: IStreamable,
|
||||
streamId?: string
|
||||
): Promise<void>;
|
||||
}
|
||||
57
packages/crypto/src/keyutils.ts
Normal file
57
packages/crypto/src/keyutils.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import {
|
||||
from_base64,
|
||||
to_base64,
|
||||
randombytes_buf,
|
||||
crypto_pwhash,
|
||||
crypto_pwhash_SALTBYTES,
|
||||
crypto_pwhash_ALG_ARGON2I13,
|
||||
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
||||
} from "libsodium-wrappers";
|
||||
import { EncryptionKey, SerializedKey } from "./types";
|
||||
|
||||
export default class KeyUtils {
|
||||
static deriveKey(password: string, salt?: string): EncryptionKey {
|
||||
let saltBytes: Uint8Array;
|
||||
if (!salt) saltBytes = randombytes_buf(crypto_pwhash_SALTBYTES);
|
||||
else {
|
||||
saltBytes = from_base64(salt);
|
||||
}
|
||||
|
||||
if (!saltBytes)
|
||||
throw new Error("Could not generate bytes from the given salt.");
|
||||
|
||||
const key = crypto_pwhash(
|
||||
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
|
||||
password,
|
||||
saltBytes,
|
||||
3, // operations limit
|
||||
1024 * 1024 * 8, // memory limit (8MB)
|
||||
crypto_pwhash_ALG_ARGON2I13
|
||||
);
|
||||
|
||||
return {
|
||||
key,
|
||||
salt: typeof salt === "string" ? salt : to_base64(saltBytes),
|
||||
};
|
||||
}
|
||||
|
||||
static exportKey(password: string, salt?: string): SerializedKey {
|
||||
const { key, salt: keySalt } = this.deriveKey(password, salt);
|
||||
return { key: to_base64(key), salt: keySalt };
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in either a password or a serialized encryption key
|
||||
* and spits out a key that can be directly used for encryption/decryption.
|
||||
* @param input
|
||||
*/
|
||||
static transform(input: SerializedKey): EncryptionKey {
|
||||
if ("password" in input && !!input.password) {
|
||||
const { password, salt } = input;
|
||||
return this.deriveKey(password, salt);
|
||||
} else if ("key" in input && !!input.salt && !!input.key) {
|
||||
return { key: from_base64(input.key), salt: input.salt };
|
||||
}
|
||||
throw new Error("Invalid input.");
|
||||
}
|
||||
}
|
||||
22
packages/crypto/src/password.ts
Normal file
22
packages/crypto/src/password.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
crypto_generichash,
|
||||
crypto_pwhash,
|
||||
crypto_pwhash_ALG_ARGON2ID13,
|
||||
crypto_pwhash_SALTBYTES,
|
||||
} from "libsodium-wrappers";
|
||||
|
||||
export default class Password {
|
||||
static hash(password: string, salt: string): string {
|
||||
const saltBytes = crypto_generichash(crypto_pwhash_SALTBYTES, salt);
|
||||
const hash = crypto_pwhash(
|
||||
32,
|
||||
password,
|
||||
saltBytes,
|
||||
3, // operations limit
|
||||
1024 * 1024 * 64, // memory limit (8MB)
|
||||
crypto_pwhash_ALG_ARGON2ID13,
|
||||
"base64"
|
||||
);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
33
packages/crypto/src/types.ts
Normal file
33
packages/crypto/src/types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { StringOutputFormat, Uint8ArrayOutputFormat } from "libsodium-wrappers";
|
||||
|
||||
export type OutputFormat = Uint8ArrayOutputFormat | StringOutputFormat;
|
||||
|
||||
export type Cipher = {
|
||||
format: OutputFormat;
|
||||
alg: string;
|
||||
cipher: string | Uint8Array;
|
||||
iv: string;
|
||||
salt: string;
|
||||
length: number;
|
||||
};
|
||||
|
||||
export type Plaintext = {
|
||||
format: OutputFormat;
|
||||
data: string | Uint8Array;
|
||||
};
|
||||
|
||||
export type SerializedKey = {
|
||||
password?: string;
|
||||
key?: string;
|
||||
salt?: string;
|
||||
};
|
||||
|
||||
export type EncryptionKey = {
|
||||
key: Uint8Array;
|
||||
salt: string;
|
||||
};
|
||||
|
||||
export type Chunk = {
|
||||
data: Uint8Array;
|
||||
final: boolean;
|
||||
};
|
||||
104
packages/crypto/tsconfig.json
Normal file
104
packages/crypto/tsconfig.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"declaration": true,
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files */
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": ["src/", "index.ts"]
|
||||
}
|
||||
3
packages/enex/.gitignore
vendored
Normal file
3
packages/enex/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.nyc_output
|
||||
dist
|
||||
5
packages/enex/.npmignore
Normal file
5
packages/enex/.npmignore
Normal file
@@ -0,0 +1,5 @@
|
||||
*
|
||||
|
||||
!dist/**/**/*
|
||||
!package.json
|
||||
!README.md
|
||||
3
packages/enex/README.md
Normal file
3
packages/enex/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# @notesnook/enex
|
||||
|
||||
A fully tested type-safe Evernote .enex parser used in Notesnook Importer.
|
||||
98105
packages/enex/__tests__/data/202201_TEST_NOTESNOOK.enex
Normal file
98105
packages/enex/__tests__/data/202201_TEST_NOTESNOOK.enex
Normal file
File diff suppressed because it is too large
Load Diff
5
packages/enex/__tests__/data/DirTest1.enex
Normal file
5
packages/enex/__tests__/data/DirTest1.enex
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
|
||||
<en-export export-date="20201025T193452Z" application="Evernote" version="Evernote Mac 7.14 (458244)">
|
||||
<note><title>ExampleNoteInSameDir</title><content><![CDATA[<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>NoteA</div></en-note>]]></content><created>20201025T193016Z</created><updated>20201025T193424Z</updated><note-attributes><latitude>46.7625732421875</latitude><longitude>19.59793272236583</longitude><altitude>114.9751129150391</altitude><author>akos</author><source>desktop.mac</source><reminder-order>0</reminder-order></note-attributes></note>
|
||||
</en-export>
|
||||
5
packages/enex/__tests__/data/DirTest2.enex
Normal file
5
packages/enex/__tests__/data/DirTest2.enex
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
|
||||
<en-export export-date="20201025T193510Z" application="Evernote" version="Evernote Mac 7.14 (458244)">
|
||||
<note><title>ExampleNoteInSameDir</title><content><![CDATA[<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>NoteB</div></en-note>]]></content><created>20201025T193016Z</created><updated>20201025T193458Z</updated><note-attributes><latitude>46.7625732421875</latitude><longitude>19.59793272236583</longitude><altitude>114.9751129150391</altitude><author>akos</author><source>desktop.mac</source><reminder-order>0</reminder-order></note-attributes></note>
|
||||
</en-export>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user