Compare commits

...

84 Commits

Author SHA1 Message Date
thecodrr
ed8271cf9d chore(tinymce-plugins): bump version to 1.5.18 2022-04-01 22:06:44 +05:00
thecodrr
da7723e7e0 fix: make inline code toggleable (fixes #444) 2022-04-01 22:02:00 +05:00
thecodrr
43e817e259 fix(importer): use correct redirect uri for onenote 2022-03-27 16:34:21 +05:00
thecodrr
eb50ebeb81 chore(importer): update packages 2022-03-27 16:26:23 +05:00
thecodrr
16b702294a chore(onenote): bump version to 1.0.1 2022-03-27 16:10:35 +05:00
thecodrr
9bd68e8c5a fix(onenote): set redirectUri via authconfig 2022-03-27 16:10:16 +05:00
thecodrr
f1b2deb893 chore(importer-web): bump version to 1.2.0 2022-03-26 22:39:03 +05:00
thecodrr
64eae3044d chore(importer): bump version to 1.2.1 2022-03-26 22:36:22 +05:00
thecodrr
f1be7a57a1 chore(importer): bump version to 1.2.0 2022-03-26 22:30:38 +05:00
Abdullah Atta
80dd83552a feat: add Zoho Notebook support in Importer (#382)
* feat: add zoho notebook importer

* fix: checklists in todo notes are not converted

* chore: use @notesnook/znel from npm
2022-03-26 22:27:20 +05:00
thecodrr
b4b7f9c519 chore: fix tests and update snjs 2022-03-26 22:14:56 +05:00
Abdullah Atta
5d0e556e1b feat: add support for OneNote in Importer (#296)
* chore: initialize onenote provider package

* add basic support for onenote

* feat: add progress

* refactor: cleanup and finalize onenote api

* feat: add onenote support in importer-web

* refactor: use a single import result component

* fix(onenote): avoid circular references

* test: add onenote tests

* chore: publish @notesnook/onenote to npm

* chore: use @notesnook/onenote from npm
2022-03-26 22:09:01 +05:00
thecodrr
ecd1640c1d fix: use custom curl parser 2022-03-26 15:15:55 +05:00
Abdullah Atta
085b7c4eb1 feat: add vericrypt utility for Notesnook encryption verification (#311)
* feat: add basic version of vericrypt utility

* feat: implement decryption logic & refresh ui

* fix: reinstall favicon

* chore: change icon

* chore: vericrypt -> notesnook vericrypt

* vericrypt: finalize ui & ux

* fix: minor adjustments to the instructions

* ci: add vericrypt deployment github action

Co-authored-by: ammarahm-ed <ammarahmed6506@gmail.com>
2022-03-26 14:15:51 +05:00
thecodrr
9838ad715e chore(tinymce-plugins): bump version to 1.5.17 2022-03-16 12:45:17 +05:00
thecodrr
f370f90a2b fix: simplify mceInsertCodeBlock 2022-03-16 12:44:58 +05:00
thecodrr
02b2a9fb17 chore(tinymce-plugins): correctly bump version to 1.5.16 2022-03-15 23:24:57 +05:00
thecodrr
17d953e59a chore(tinymce-plugins): bump version to 1.15.16 2022-03-15 07:48:22 +05:00
thecodrr
c47436927e feat(tinymce-plugins): many codeblock improvements 2022-03-15 07:38:30 +05:00
thecodrr
8f89414f89 chore(tinymce-plugins): bump version to 1.5.15 2022-03-14 16:09:13 +05:00
thecodrr
557e1de9f0 fix(tinymce-plugins): don't remove 1st line if editor empty 2022-03-14 16:08:42 +05:00
thecodrr
6cc065a0f7 fix(tinymce-plugins): <br> -> \n 2022-03-14 16:08:16 +05:00
thecodrr
cba87d8477 chore(tinymce-plugins): remove console.log 2022-03-14 16:02:35 +05:00
thecodrr
26203bf3da feat(tinymce-plugins): improve codeblock pasting 2022-03-14 16:01:21 +05:00
ammarahm-ed
e9d43f3026 feat(tinymce-plugins): touch input support in collapsibleheaders 2022-03-09 11:35:17 +05:00
thecodrr
57df03e46c chore(tinymce-plugins): bump version to 1.5.14 2022-03-08 14:21:59 +05:00
thecodrr
b673519898 feat(tinymce-plugins): add support for h1 in collapsible headers 2022-03-08 14:21:31 +05:00
thecodrr
5356e9d4ed chore(tinymce-plugins): bump version to 1.5.13 2022-03-07 17:29:51 +05:00
thecodrr
3d1b9de51b fix(tinymce-plugins): do not autofocus on set content 2022-03-07 17:29:31 +05:00
thecodrr
c1741b1924 chore(tinymce-plugins): bump version to 1.5.12 2022-03-04 23:36:54 +05:00
thecodrr
4a17164bd9 fix: minimized state of headings should get saved
(fixes #377)
2022-03-04 23:33:12 +05:00
ammarahm-ed
c26dbefe26 fix: nested checklist items not checked on mobile 2022-02-24 13:29:56 +05:00
thecodrr
f532d6f01b chore(tinymce-plugins): bump version to 1.5.11 2022-02-19 20:27:56 +05:00
thecodrr
351f56dc0d fix(tinymce-plugins): auto check last checklist item
this ensures that if tinymce auto merges two checklists
due to close proximity, the first element of the second list
would get checked instead of the first elemtn of the first list.
2022-02-19 20:27:36 +05:00
thecodrr
feb74ae910 chore(tinymce-plugins): bump version to 1.5.10 2022-02-19 20:22:11 +05:00
thecodrr
4b483d200d feat: allow checking first item via execCommand 2022-02-19 20:21:52 +05:00
Abdullah Atta
5858cae55b feat: open source notesnook crypto module (#329)
* chore: open-source notesnook crypto

* chore(crypto): remove useless webpack config

* chore(crypto): add npmignore file
2022-02-02 10:32:02 +05:00
thecodrr
e7491d1c03 chore(tinymce-plugins): bump version to 1.5.9 2022-01-31 11:52:15 +05:00
thecodrr
e740d56a84 fix(tinymce-plugins): jumpto next p tag on enter press 2022-01-31 11:51:35 +05:00
thecodrr
01c78c654d ci: fix "cd: app/importer: No such file or directory" 2022-01-24 11:15:13 +05:00
thecodrr
db67114a30 chore(importer-web): bump version to 1.1.2 2022-01-24 11:13:11 +05:00
thecodrr
7483286833 chore(importer-web): update @notesnook/importer 2022-01-24 11:12:49 +05:00
thecodrr
9606517f38 ci: deploy importer web app directly 2022-01-24 11:11:01 +05:00
thecodrr
1d86624ce3 test(importer): update snapshots 2022-01-24 11:09:23 +05:00
thecodrr
bdc2af9597 chore(importer): bump version to 1.1.3 2022-01-24 11:07:05 +05:00
thecodrr
41d77b6424 chore(importer): update @notesnook/enex 2022-01-24 11:06:53 +05:00
thecodrr
3bda97b4f5 chore(enex): bump version to 1.0.3 2022-01-24 11:04:58 +05:00
thecodrr
5d5b1e9eac fix(enex): derive md5 hash of att. manually 2022-01-24 11:03:25 +05:00
thecodrr
f46ef8347f chore(tinymce-plugins): @streetwritersco -> @streetwriters 2022-01-22 13:31:40 +05:00
thecodrr
c613e67218 chore(tinymce-plugins): bump version to 1.5.7 2022-01-22 13:27:16 +05:00
thecodrr
a9ff91d51e fix(tinymce-plugins): make inlinecode toggleable 2022-01-22 13:26:45 +05:00
thecodrr
52f51c4087 chore(importer-web): bump version to 1.1.1 2022-01-21 23:54:45 +05:00
thecodrr
f0ec25c296 chore(importer-web): update importer core 2022-01-21 23:54:11 +05:00
thecodrr
d2ae716993 chore(importer): bump version to 1.1.2 2022-01-21 23:53:24 +05:00
thecodrr
8d432d4810 fix(importer): typo in DefaultAppDomain 2022-01-21 23:53:06 +05:00
thecodrr
e2bd1cd1f1 chore(importer-web): bump version to 1.1.0 2022-01-21 23:37:42 +05:00
thecodrr
7286c60961 chore(importer-web): update importer core 2022-01-21 23:25:58 +05:00
thecodrr
8a9cd91d54 chore(importer): bump version 1.1.1 2022-01-21 23:22:03 +05:00
thecodrr
462ae4d8ab fix: move @standardnotes/common & @standardnotes/features to dependencies 2022-01-21 23:21:22 +05:00
thecodrr
45e7d5e46b chore(importer): add Standard Notes to readme 2022-01-21 21:34:51 +05:00
thecodrr
9426e937a5 test(importer): update snapshots 2022-01-21 21:34:31 +05:00
thecodrr
8ec4102395 fix: log errors during unpacking 2022-01-21 21:34:13 +05:00
thecodrr
4832d243da refactor: use snjs types instead of our own 2022-01-21 21:33:11 +05:00
thecodrr
9504f278c3 refactor: move table & codeblock construction logic into domutils 2022-01-21 21:32:44 +05:00
Ammar Ahmed
8cefa1defc feat: add standard notes support in importer (#285)
* add support for standard notes in importer

* rename types

* move editors to constants.ts

* change ContentTypes to enum

* make all properties of SNBackup optional

* add version & backup file check

* refactor buildTableWithRows function

* rename mode to language

* use inverted condition to reduce indent

* refactor table construction html strings
2022-01-20 10:49:29 +05:00
thecodrr
b4e58139e3 ci: do not build before running tests 2022-01-19 17:34:29 +05:00
thecodrr
73f60704dd ci: rename workflows for enex & importer packages 2022-01-19 17:29:51 +05:00
thecodrr
470e3040fc ci: fix syntax errors in workflows 2022-01-19 17:29:15 +05:00
thecodrr
73d9a0f371 ci: add workflows for testing enex & importer packages 2022-01-19 17:27:53 +05:00
thecodrr
7a01898b2b chore(importer-web): update importer core 2022-01-19 17:23:10 +05:00
thecodrr
41f5a4eecf chore(importer): publish v1.0.0 on npm 2022-01-19 17:19:56 +05:00
thecodrr
08744fd0ee test(importer): improve packed payload test 2022-01-19 17:09:43 +05:00
thecodrr
4788060c5a chore(enex): bump version to 1.0.2 2022-01-19 16:44:22 +05:00
thecodrr
7a5e49bcbe chore(enex): use thecodrr/node-html-parser package 2022-01-19 16:43:57 +05:00
thecodrr
536744f74a chore(enex): publish 2022-01-19 16:31:30 +05:00
thecodrr
b1c33ebf28 chore(enex): bump version to 1.0.1 2022-01-19 12:27:19 +05:00
thecodrr
323cd373e5 fix(enex): properly remove newlines from base64 data 2022-01-19 12:26:29 +05:00
thecodrr
6c5cfb1505 docs: fix doc page crash with syntax error 2022-01-19 11:59:55 +05:00
ammarahm-ed
b628e6071e docs: fix output path incorrect in config file 2022-01-17 01:23:20 +05:00
ammarahm-ed
8a2c1c0b2a docs: fix generator output path regex 2022-01-17 01:22:17 +05:00
ammarahm-ed
77734aebba docs: update retype to 1.11.2 2022-01-17 01:09:32 +05:00
ammarahm-ed
31711987c3 docs: remove rich-text-editing route 2022-01-13 11:33:59 +05:00
thecodrr
e193afb97d chore(tinymce-plugins): bump version to 1.5.6 2022-01-10 12:23:02 +05:00
thecodrr
02b8fc4eb5 fix: do not touch data-mce-bogus elements (fixes #279) 2022-01-10 12:22:21 +05:00
236 changed files with 173872 additions and 53285 deletions

32
.github/workflows/enex-tests.yml vendored Normal file
View 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

View File

@@ -27,9 +27,10 @@ jobs:
with:
node-version: 16.x
- run: |
cd apps/importer
npm i
npx turbo run build
mv ./apps/importer/build/ ./apps/importer/public_html/
npm run build
mv ./build/ ./public_html/
- uses: blbecker/rsync-deploy@v1.0
with:

32
.github/workflows/importer-tests.yml vendored Normal file
View 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

41
.github/workflows/vericrypt-deploy.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
# This is a basic workflow to help you get started with Actions
name: Vericrypt Deploy
# Controls when the action will run.
on:
# Triggers the workflow on push events or manually
workflow_dispatch:
push:
branches: [main]
paths:
- "apps/vericrypt/**"
# 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/vericrypt
npm i
npm run build
mv ./build/ ./public_html/
- uses: blbecker/rsync-deploy@v1.0
with:
source_dir: ./apps/vericrypt/public_html/
destination_path: ${{ secrets.REMOTE_VERICRYPT_PATH }}
destination_host: ${{ secrets.REMOTE_HOST }}
deployment_user: ${{ secrets.REMOTE_USER }}
deployment_key: ${{ secrets.REMOTE_KEY }}

View File

@@ -1,9 +1,15 @@
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,

View File

@@ -0,0 +1,2 @@
1. 🥳 Added OneNote support! 🥳
2. 🥳 Added Zoho Notebook support! 🥳

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
{
"name": "@notesnook/importer-web",
"version": "1.0.0",
"version": "1.2.1",
"private": true,
"devDependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/color": "^3.0.2",
"@types/file-saver": "^2.0.4",
"@types/jest": "^26.0.15",
"@types/node": "^12.0.0",
@@ -20,8 +19,8 @@
"typescript": "^4.1.2"
},
"dependencies": {
"@notesnook/importer": "file:../../packages/importer",
"color": "^4.1.0",
"@notesnook/importer": "^1.2.3",
"colord": "^2.9.2",
"file-saver": "^2.0.5",
"hash-wasm": "^4.9.0",
"interweave": "^12.9.0",

View File

@@ -0,0 +1,7 @@
{
"associatedApplications": [
{
"applicationId": "4952c7cf-9c02-4fb7-b867-b87727bb52d8"
}
]
}

View File

@@ -3,19 +3,19 @@ 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 { FileProviderHandler } from "./components/FileProviderHandler";
import { ImportResult } from "./components/ImportResult";
import { Hero } from "./components/Hero";
import { Footer } from "./components/Footer";
import { IProvider } from "@notesnook/importer";
import { NetworkProviderHandler } from "./components/NetworkProviderHandler";
import { ProviderResult } from "@notesnook/importer/dist/src/providers/provider";
function App() {
const [selectedProvider, setSelectedProvider] = useState<
Providers | undefined
>();
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const [selectedProvider, setSelectedProvider] = useState<IProvider>();
const [providerResult, setProviderResult] = useState<ProviderResult>();
return (
<ThemeProvider theme={ThemeFactory.construct()}>
@@ -29,21 +29,28 @@ function App() {
>
<ProviderSelector onProviderChanged={setSelectedProvider} />
{selectedProvider && (
{selectedProvider ? (
<>
<StepSeperator />
<FileSelector
provider={selectedProvider}
onFilesChanged={setSelectedFiles}
/>
{selectedProvider.type === "file" ? (
<FileProviderHandler
provider={selectedProvider}
onTransformFinished={setProviderResult}
/>
) : selectedProvider.type === "network" ? (
<NetworkProviderHandler
provider={selectedProvider}
onTransformFinished={setProviderResult}
/>
) : null}
</>
)}
{selectedFiles.length > 0 && selectedProvider && (
) : null}
{providerResult ? (
<>
<StepSeperator />
<ImportResult files={selectedFiles} provider={selectedProvider} />
<ImportResult result={providerResult} />{" "}
</>
)}
) : null}
</Flex>
<Footer />
</Flex>

View File

@@ -1,21 +1,35 @@
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";
import { IFileProvider, transform } from "@notesnook/importer";
import { xxhash64 } from "hash-wasm";
import {
ProviderResult,
ProviderSettings,
} from "@notesnook/importer/dist/src/providers/provider";
import { IFile } from "@notesnook/importer/dist/src/utils/file";
type FileSelectorProps = {
provider?: Providers;
onFilesChanged: (files: File[]) => void;
type FileProviderHandlerProps = {
provider: IFileProvider;
onTransformFinished: (result: ProviderResult) => void;
};
export function FileSelector(props: FileSelectorProps) {
const { provider: providerName, onFilesChanged } = props;
type Progress = {
total: number;
done: number;
};
const settings: ProviderSettings = {
clientType: "browser",
hasher: { type: "xxh64", hash: (data) => xxhash64(data) },
};
export function FileProviderHandler(props: FileProviderHandlerProps) {
const { provider, onTransformFinished } = props;
const [files, setFiles] = useState<File[]>([]);
const [provider, setProvider] = useState<IProvider | undefined>();
const [progress, setProgress] = useState<Progress>({ total: 0, done: 0 });
const onDrop = useCallback((acceptedFiles: File[]) => {
setFiles((files) => {
@@ -23,22 +37,41 @@ export function FileSelector(props: FileSelectorProps) {
return newFiles;
});
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: provider?.supportedExtensions?.concat(".zip"),
});
useEffect(() => {
onFilesChanged(files);
}, [files, onFilesChanged]);
setFiles([]);
}, [provider]);
useEffect(() => {
setFiles([]);
setProvider(undefined);
(async () => {
if (!provider || !files.length) return;
if (!providerName) return;
setProvider(ProviderFactory.getProvider(providerName));
}, [providerName]);
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(
provider,
[providerFile],
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 });
onTransformFinished(result);
})();
}, [provider, files, onTransformFinished]);
return (
<StepContainer sx={{ flexDirection: "column", alignItems: "stretch" }}>
@@ -84,6 +117,7 @@ export function FileSelector(props: FileSelectorProps) {
>
{files.map((file, index) => (
<Flex
key={file.name}
sx={{
flexDirection: "column",
p: 2,
@@ -108,6 +142,11 @@ export function FileSelector(props: FileSelectorProps) {
</Flex>
</Accordion>
) : null}
{progress.total > 0 ? (
<Text variant="body">
Processing files ({progress.done}/{progress.total})
</Text>
) : null}
</StepContainer>
);
}

View File

@@ -6,7 +6,8 @@ export function Hero() {
<Flex
sx={{
flexDirection: "column",
my: 150,
mt: 150,
mb: 100,
justifyContent: "center",
alignItems: "center",
}}
@@ -42,6 +43,21 @@ export function Hero() {
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>
);
}

View File

@@ -14,14 +14,16 @@ export function ImportErrors(props: ImportErrorsProps) {
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>
{props.errors.map((error, index) => (
<Text
variant="body"
sx={{ color: "error", my: 1, fontFamily: "monospace" }}
>
{index + 1}. {error.message}{" "}
{error.file ? `(${error.file.name})` : ""}
<br />
</Text>
))}
<Button
sx={{ bg: "error", color: "static", alignSelf: "start", mt: 2 }}
onClick={() =>

View File

@@ -1,13 +1,7 @@
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 { pack } from "@notesnook/importer";
import { useCallback, useState } from "react";
import { ProviderResult } from "@notesnook/importer/dist/src/providers/provider";
import { Note } from "@notesnook/importer/dist/src/models/note";
import { StepContainer } from "./StepContainer";
import { NotePreview } from "./NotePreview";
@@ -16,51 +10,13 @@ import { ImportHelp } from "./ImportHelp";
import { saveAs } from "file-saver";
import { NotesList } from "./NotesList";
type ResultProps = {
files: File[];
provider: Providers | undefined;
type ImportResultProps = {
result: ProviderResult;
};
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>();
export function ImportResult(props: ImportResultProps) {
const { result } = props;
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;
@@ -79,16 +35,11 @@ export function ImportResult(props: ResultProps) {
}}
>
<Text variant="title">Your notes are ready for download</Text>
{progress.total > 0 ? (
<Text variant="body">
Processing files ({progress.done}/{progress.total})
</Text>
) : result ? (
{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} />}

View File

@@ -0,0 +1,88 @@
import { Text, Button } from "@theme-ui/components";
import { StepContainer } from "./StepContainer";
import { INetworkProvider } from "@notesnook/importer";
import {
ProviderResult,
ProviderSettings,
} from "@notesnook/importer/dist/src/providers/provider";
import { xxhash64 } from "hash-wasm";
import { OneNote } from "@notesnook/importer/dist/src/providers/onenote";
import { useCallback, useState } from "react";
type NetworkProviderHandlerProps = {
provider: INetworkProvider<unknown>;
onTransformFinished: (result: ProviderResult) => void;
};
const settings: ProviderSettings = {
clientType: "browser",
hasher: { type: "xxh64", hash: (data) => xxhash64(data) },
};
export function NetworkProviderHandler(props: NetworkProviderHandlerProps) {
const { provider, onTransformFinished } = props;
const [progress, setProgress] = useState<string | null>();
const startImport = useCallback(() => {
(async () => {
if (!provider) return;
setProgress(null);
let result: ProviderResult = { notes: [], errors: [] };
if (provider instanceof OneNote) {
result = await provider.process({
...settings,
clientId: "4952c7cf-9c02-4fb7-b867-b87727bb52d8",
redirectUri:
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://importer.notesnook.com",
report: setProgress,
});
}
setProgress(null);
onTransformFinished(result);
})();
}, [onTransformFinished, provider]);
return (
<StepContainer
sx={{
flexDirection: "column",
alignItems: "stretch",
}}
>
{progress ? (
<>
<Text variant="title">Importing your notes from {provider.name}</Text>
<Text
as="pre"
variant="body"
sx={{
textAlign: "center",
my: 4,
bg: "bgSecondary",
p: 4,
fontFamily: "monospace",
whiteSpace: "pre-wrap",
}}
>
{progress}
</Text>
</>
) : (
<>
<Text variant="title">Connect your {provider.name} account</Text>
<Button
variant="primary"
onClick={startImport}
sx={{ my: 4, alignSelf: "center" }}
>
Start importing
</Button>
</>
)}
</StepContainer>
);
}

View File

@@ -100,6 +100,7 @@ export function NotePreview(props: NotePreviewProps) {
<Flex my={1}>
{notebooks?.map((notebook) => (
<Flex
key={notebook.notebook + notebook.topic}
sx={{
alignItems: "center",
bg: "bgSecondary",
@@ -119,6 +120,7 @@ export function NotePreview(props: NotePreviewProps) {
))}
{tags?.map((tag) => (
<Text
key={tag}
variant="subBody"
sx={{
bg: "bgSecondary",

View File

@@ -4,17 +4,14 @@ import { Accordion } from "./Accordion";
type NotesListProps = {
notes: Note[];
filesLength: number;
onNoteSelected: (note: Note) => void;
};
export function NotesList(props: NotesListProps) {
const { notes, filesLength, onNoteSelected } = props;
const { notes, onNoteSelected } = props;
return (
<Accordion
title={`${notes.length} notes found in ${filesLength} ${
filesLength > 1 ? "files" : "file"
}`}
title={`${notes.length} notes found`}
sx={{
border: "1px solid var(--theme-ui-colors-border)",
mt: 2,
@@ -30,6 +27,7 @@ export function NotesList(props: NotesListProps) {
>
{notes.map((note, index) => (
<Flex
key={note.title + note.dateCreated}
sx={{
flexDirection: "column",
p: 2,

View File

@@ -1,10 +1,10 @@
import { Flex, Select, Text } from "@theme-ui/components";
import { ProviderFactory } from "@notesnook/importer";
import { IProvider, ProviderFactory } from "@notesnook/importer";
import { Providers } from "@notesnook/importer/dist/src/providers/providerfactory";
import { StepContainer } from "./StepContainer";
type ProviderSelectorProps = {
onProviderChanged: (provider: Providers) => void;
onProviderChanged: (provider: IProvider) => void;
};
export function ProviderSelector(props: ProviderSelectorProps) {
@@ -31,12 +31,12 @@ export function ProviderSelector(props: ProviderSelectorProps) {
}}
onChange={(e) => {
const providerName: Providers = e.target.value as Providers;
props.onProviderChanged(providerName);
props.onProviderChanged(ProviderFactory.getProvider(providerName));
}}
>
<option value=""></option>
{ProviderFactory.getAvailableProviders().map((provider) => (
<option value={provider}>
<option key={provider} value={provider}>
{ProviderFactory.getProvider(provider as Providers).name}
</option>
))}

View File

@@ -1,11 +1,13 @@
import Color from "color";
import { colord } from "colord";
import { StaticColors } from "./static";
export class DarkColorScheme {
static construct(accent: Color) {
static construct(accent: string) {
return {
primary: accent.hex().toString(),
placeholder: Color("#ffffff").alpha(0.6).hex().toString(),
primary: colord(accent).toHex(),
placeholder: colord("#ffffff")
.alpha(0.6)
.toHex(),
background: "#1f1f1f",
bgTransparent: "#1f1f1f99",
accent: "#000",

View File

@@ -1,4 +1,3 @@
import Color from "color";
import { DarkColorScheme } from "./dark";
import { LightColorScheme } from "./light";
@@ -9,7 +8,7 @@ const colorSchemes = {
class ColorSchemeFactory {
static construct<TTheme extends keyof typeof colorSchemes>(theme: TTheme) {
return colorSchemes[theme].construct(Color("#008837"));
return colorSchemes[theme].construct("#008837");
}
}
export default ColorSchemeFactory;

View File

@@ -1,10 +1,10 @@
import Color from "color";
import { colord } from "colord";
import { StaticColors } from "./static";
export class LightColorScheme {
static construct(accent: Color) {
static construct(accent: string) {
return {
primary: accent.hex().toString(),
primary: colord(accent).toHex(),
background: "white",
bgTransparent: "#ffffff99",
accent: "white",
@@ -20,7 +20,9 @@ export class LightColorScheme {
secondary: "white",
icon: "#3b3b3b",
disabled: "#9b9b9b",
placeholder: Color("#000000").alpha(0.6).hex().toString(),
placeholder: colord("#000000")
.alpha(0.6)
.toHex(),
...StaticColors.construct(accent),
};
}

View File

@@ -1,11 +1,17 @@
import type Color from "color";
import { colord } from "colord";
export class StaticColors {
static construct(accent: Color) {
static construct(accent: string) {
return {
shade: accent.alpha(0.1).rgb().toString(),
textSelection: accent.alpha(0.2).rgb().toString(),
dimPrimary: accent.alpha(0.7).rgb().toString(),
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",

View File

@@ -0,0 +1,13 @@
const { version } = require("./package.json");
const APP_VERSION = version;
module.exports = {
all: {
REACT_APP_VERSION: APP_VERSION,
},
release: {
GENERATE_SOURCEMAP: false,
INLINE_RUNTIME_CHUNK: false,
DISABLE_ESLINT_PLUGIN: true,
},
};

23
apps/vericrypt/.gitignore vendored Normal file
View 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*

0
apps/vericrypt/README.md Normal file
View File

38562
apps/vericrypt/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
{
"name": "@notesnook/vericrypt",
"version": "1.0.0",
"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/platform": "^1.3.4",
"@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/crypto": "^1.0.0",
"clipboard-polyfill": "^3.0.3",
"colord": "^2.9.2",
"platform": "^1.3.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-icons": "^4.3.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"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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="An open source utility to verify Notesnook data encryption in a safe & reliable way."
/>
<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 Vericrypt</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View 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+ */
}

142
apps/vericrypt/src/App.tsx Normal file
View File

@@ -0,0 +1,142 @@
import { Image, Flex, Text } from "@theme-ui/components";
import { ThemeProvider } from "@theme-ui/theme-provider";
import "./App.css";
import { MdVpnKey, MdCheck } from "react-icons/md";
import { BiPlus } from "react-icons/all";
import { ThemeFactory } from "./theme";
import { Hero } from "./components/Hero";
import { LoginToNotesnook } from "./components/Step1";
import { GetAccountSalt } from "./components/Step2";
import { EnterAccountPassword } from "./components/Step3";
import { PasteEncryptedData, SyncRequestBody } from "./components/Step4";
import { StepSeperator } from "./components/StepSeperator";
import { Footer } from "./components/Footer";
import { useState } from "react";
import { NNCrypto } from "@notesnook/crypto";
import { Code } from "./components/Code";
import { Accordion } from "./components/Accordion";
import { DecryptedResult } from "./components/DecryptedResult";
import Map from "./assets/images/map.svg";
const instructions = [
"Go to Notesnook",
"Open Settings",
<>
Click on <Code text="Backup data recovery key" />
</>,
"Enter your account password for verification",
"Confirm that your generated encryption key matches",
];
function App() {
const [password, setPassword] = useState<string>();
const [salt, setSalt] = useState<string>();
const [key, setKey] = useState<string>();
const [data, setData] = useState<SyncRequestBody | undefined>();
return (
<ThemeProvider theme={ThemeFactory.construct()}>
<Flex sx={{ flexDirection: "column" }}>
<Image src={Map} sx={{ position: "absolute", opacity: 0.1 }} />
<Hero />
<Flex
sx={{
flexDirection: "column",
alignItems: "center",
}}
>
<LoginToNotesnook />
<StepSeperator />
<GetAccountSalt onSaltSubmitted={setSalt} />
{salt && (
<>
<StepSeperator icon={BiPlus} />
<EnterAccountPassword onPasswordSubmitted={setPassword} />
</>
)}
{salt && password && (
<>
<StepSeperator
icon={MdVpnKey}
tooltip="Click to see derived encryption key"
onShowPopup={async () => {
if (!password) return false;
const { key } = await new NNCrypto().exportKey(
password,
salt
);
setKey(key);
return true;
}}
popup={{
title: "Your data encryption key",
body: key ? (
<>
<Text
as="code"
sx={{
fontFamily: "monospace",
mt: 2,
bg: "bgSecondary",
p: 1,
borderRadius: 5,
}}
>
{key}
</Text>
<Accordion
title="How to verify your encryption key?"
sx={{
border: "1px solid var(--theme-ui-colors-border)",
mt: 2,
borderRadius: "default",
}}
>
<Text variant="body" sx={{ mx: 2 }}>
Now that you have derived your encryption key, you'll
want to verify it to make sure we didn't just make it
up. To do so, follow these steps:
<Text as="ol" variant="body" sx={{ mb: 2 }}>
{instructions?.map((item) => (
<Text as="li" sx={{ mt: 1 }}>
{item}
</Text>
))}
</Text>
</Text>
</Accordion>
</>
) : (
undefined
),
}}
/>
<PasteEncryptedData onEncryptedDataPasted={setData} />
</>
)}
{password && salt && data && (
<>
<StepSeperator icon={MdCheck} />
<DecryptedResult
password={password}
salt={salt}
data={data}
onRestartProcess={() => {
setSalt(undefined);
setPassword(undefined);
setKey(undefined);
setData(undefined);
}}
/>
</>
)}
</Flex>
<Footer />
</Flex>
</ThemeProvider>
);
}
export default App;

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,43 @@
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 = "icon",
...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",
color: color,
}}
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>
);
}

View File

@@ -0,0 +1,26 @@
import { Text, Link } from "@theme-ui/components";
type CodeProps = { text: string; href?: string };
export function Code(props: CodeProps) {
return (
<Text
as="code"
sx={{
bg: "bgSecondary",
px: 1,
borderRadius: 5,
fontFamily: "monospace",
border: "1px solid var(--theme-ui-colors-border)",
cursor: props.href ? "pointer" : "unset",
}}
>
{props.href ? (
<Link href={props.href} color="primary">
{props.text}
</Link>
) : (
props.text
)}
</Text>
);
}

View File

@@ -0,0 +1,166 @@
import { Flex, Button, Text, Link } from "@theme-ui/components";
import { StepContainer } from "./StepContainer";
import { SyncRequestBody } from "./Step4";
import { NNCrypto } from "@notesnook/crypto";
import { useEffect, useState } from "react";
import { FcDataEncryption } from "react-icons/fc";
import { Code } from "./Code";
import { getSourceUrl } from "../utils/links";
import * as clipboard from "clipboard-polyfill/text";
import { Accordion } from "./Accordion";
import { ErrorsList } from "./ErrorsList";
type DecryptedResultProps = {
password: string;
salt: string;
data: SyncRequestBody;
onRestartProcess: () => void;
};
export function DecryptedResult(props: DecryptedResultProps) {
const [isDecrypting, setIsDecrypting] = useState(true);
const [decryptedData, setDecryptedData] = useState<string>();
const [error, setError] = useState<string | undefined>();
useEffect(() => {
(async function() {
try {
const data: any = {
notes: [],
notebooks: [],
content: [],
attachments: [],
};
const crypto = new NNCrypto();
const key = await crypto.exportKey(props.password, props.salt);
for (let arrayKey in data) {
const array = data[arrayKey];
for (let encryptedItem of (props.data as any)[arrayKey]) {
const { data } = await crypto.decrypt(key, encryptedItem, "text");
array.push(JSON.parse(data as string));
}
}
setDecryptedData(JSON.stringify(data, undefined, " "));
} catch (e) {
const error = e as Error;
setError(error.message);
} finally {
setIsDecrypting(false);
}
})();
}, [props]);
if (error)
return (
<StepContainer
as="form"
sx={{
flexDirection: "column",
}}
>
<Text variant="title">Decryption failed</Text>
<ErrorsList errors={[error]} />
</StepContainer>
);
if (isDecrypting)
return (
<StepContainer
as="form"
sx={{
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
>
<FcDataEncryption size={50} />
<Text variant="title" sx={{ mt: 2 }}>
Decrypting your data...
</Text>
</StepContainer>
);
return (
<StepContainer
sx={{
flexDirection: "column",
border: "2px solid var(--theme-ui-colors-primary)",
}}
>
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
<Text variant="title">Your data has been decrypted</Text>
<Code
text="src/components/DecryptedResult.tsx"
href={getSourceUrl("src/components/DecryptedResult.tsx")}
/>
</Flex>
<Text variant="body">
This is your data in it's raw decrypted format. Feel free to scroll
through and see what it contains.
</Text>
<Text
as="pre"
variant="body"
sx={{
maxHeight: 400,
overflowY: "auto",
fontFamily: "monospace",
whiteSpace: "pre-wrap",
color: "icon",
mt: 2,
}}
>
{decryptedData}
</Text>
<Accordion
title="What happens now?"
sx={{
border: "1px solid var(--theme-ui-colors-border)",
mt: 2,
borderRadius: "default",
}}
>
<Text as="p" variant="body" sx={{ mx: 2 }}>
Congratulations! You successfully verified Notesnook's data encryption
claims.
</Text>
<Text as="p" variant="body" sx={{ mx: 2, mt: 2 }}>
Of course, this is just one part (a very crucial one) of proving that
you can trust Notesnook with your data. If you have any other
preservations, let us know by reaching out to us at{" "}
<Link href="mailto:support@streetwriters.co">
support@streetwriters.co
</Link>{" "}
or{" "}
<Link href="https://discord.gg/">joining our Discord community</Link>.
We'll do our best to alleviate all your worries.
</Text>
<Text as="p" variant="body" sx={{ mx: 2, mt: 2, fontWeight: "bold" }}>
What about open sourcing Notesnook?
</Text>
<Text as="p" variant="body" sx={{ mx: 2, my: 2 }}>
Open sourcing is another part of garnering our users' trust. We have{" "}
<Link href="https://notesnook.com/roadmap">plans</Link> to begin open
sourcing in May but open sourcing will not make this tool obsolete.
Verifying the integrity of encrypted data at any point in time is very
important even if the software is open source.
</Text>
</Accordion>
<Flex sx={{ alignSelf: "center", mt: 4 }}>
<Button
variant="secondary"
sx={{ mr: 2 }}
onClick={async () => {
if (!decryptedData) return;
await clipboard.writeText(decryptedData);
}}
>
Copy data as JSON
</Button>
<Button variant="primary" onClick={props.onRestartProcess}>
Start again
</Button>
</Flex>
</StepContainer>
);
}

View File

@@ -0,0 +1,38 @@
import { Button, Flex, Text } from "@theme-ui/components";
import { Accordion } from "./Accordion";
type ErrorsListProps = {
errors: string[];
};
export function ErrorsList(props: ErrorsListProps) {
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) => (
<>
{error}
<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>
);
}

View File

@@ -0,0 +1,23 @@
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 Vericrypt (v{appVersion})</Text>
<Text variant="subBody">
© {new Date().getFullYear()} Streetwriters LLC
</Text>
</Flex>
);
}

View File

@@ -0,0 +1,109 @@
import { Flex, Box, Link, Text } from "@theme-ui/components";
import { appVersion } from "../utils/version";
import {
MdOutlineShield,
MdVpnKey,
MdOutlineStorage,
MdOutlineEnhancedEncryption,
} from "react-icons/md";
import { getAppUrl } from "../utils/links";
const Algorithms = [
{
key: "argon2",
title: "Encryption key derivation (PKDF)",
name: "Argon2",
icon: MdVpnKey,
},
{
key: "aes-gcm",
title: "Encryption key storage",
name: "AES-GCM 256",
icon: MdOutlineStorage,
},
{
key: "xchacha",
title: "Data encryption",
name: "XChaXCha-Poly1305-IETF",
icon: MdOutlineEnhancedEncryption,
},
];
export function Hero() {
return (
<Flex
sx={{
flexDirection: "column",
mt: 150,
mb: 100,
justifyContent: "center",
alignItems: "center",
}}
>
<Text
variant="heading"
sx={{ textAlign: "center", display: "flex", alignItems: "center" }}
>
<MdOutlineShield /> Notesnook Vericrypt
</Text>
<Text
sx={{
fontSize: "title",
textAlign: "center",
color: "fontTertiary",
}}
>
An open source utility to verify Notesnook data encryption claims in a
provable way.
</Text>
<Flex sx={{ mt: 2, zIndex: 10 }}>
<Text variant="body" sx={{ px: 1 }}>
v{appVersion}
</Text>
<Link
href={getAppUrl("vericrypt")}
variant="text.body"
sx={{ px: 1, borderLeft: "1px solid var(--theme-ui-colors-border)" }}
>
See source code
</Link>
</Flex>
<Box
sx={{
mt: 100,
display: "grid",
gridTemplateColumns: "1fr 1fr 1fr",
rowGap: 2,
}}
>
{Algorithms.map((alg) => (
<Flex
sx={{
flexDirection: "column",
mr: 4,
p: 2,
borderRadius: "default",
bg: "black",
width: 250,
}}
key={alg.key}
>
<alg.icon color={"var(--theme-ui-colors-fontSecondary)"} />
<Text variant="subtitle" sx={{ color: "fontSecondary" }}>
{alg.title}
</Text>
<Text
variant="body"
sx={{
fontSize: "subtitle",
color: "fontSecondary",
}}
>
{alg.name}
</Text>
</Flex>
))}
</Box>
</Flex>
);
}

View File

@@ -0,0 +1,15 @@
import { Code } from "./Code";
type KeyComboProps = { combo: string[] };
export function KeyCombo(props: KeyComboProps) {
return (
<>
{props.combo.map((key, index) => (
<>
<Code text={key} />
{index < props.combo.length - 1 ? " + " : ""}
</>
))}
</>
);
}

View File

@@ -0,0 +1,67 @@
import { Button, Text } from "@theme-ui/components";
import { StepContainer } from "./StepContainer";
import { Code } from "./Code";
import { getPackageUrl } from "../utils/links";
export function LoginToNotesnook() {
return (
<StepContainer
onSubmit={(e) => {
e.preventDefault();
document.getElementById("step_2")?.scrollIntoView({
behavior: "smooth",
block: "center",
});
return false;
}}
onSubmitCapture={() => false}
as="form"
sx={{ flexDirection: "column" }}
>
<Text variant="title">Welcome to Vericrypt</Text>
<Text as="p" variant="body" sx={{ mt: 1 }}>
Trust is a huge problem in closed source end-to-end encrypted
applications. How can you be sure that the app is actually encrypting
your data?
</Text>
<Text as="p" variant="body" sx={{ mt: 1 }}>
The only way to earn a user's trust is by allowing them to see how the
underlying encryption actually works. To do this, we at Notesnook have
taken several steps.
</Text>
<Text as="p" variant="body" sx={{ mt: 1 }}>
The first step has been the{" "}
<b>
open sourcing of our{" "}
<Code text="@notesnook/crypto" href={getPackageUrl("crypto")} />{" "}
module
</b>{" "}
which is responsible for all cryptographic needs of the app.
</Text>
<Text as="p" variant="body" sx={{ mt: 1 }}>
The second step is this tool which is also open source and uses{" "}
<Code text="@notesnook/crypto" href={getPackageUrl("crypto")} />{" "}
underneath. Vericrypt will allow you to verify all encryption claims
made by Notesnook in a practical &amp; provable way right inside your
browser.
</Text>
<Text
as="p"
variant="body"
sx={{ mt: 1, bg: "bgSecondary", p: 2, borderRadius: 5 }}
>
When you use this tool, you'll be guided each step of the way to
extract/insert raw data from raw sources.{" "}
<b>The whole process happens completely in your browser offline</b> and
you can even disconnect your internet to make sure we aren't just saying
that.
</Text>
<Button
sx={{ alignSelf: "center", mt: 2 }}
onClick={() => window.open("https://app.notesnook.com/login", "_blank")}
>
Login to Notesnook
</Button>
</StepContainer>
);
}

View File

@@ -0,0 +1,195 @@
import { Flex, Text, Image, Input } from "@theme-ui/components";
import { StepContainer } from "./StepContainer";
import DevtoolsApplicationTab from "../assets/screenshots/devtools_application_tab.png";
import DevtoolsSelectDB from "../assets/screenshots/devtools_select_db.png";
import DevtoolsSalt from "../assets/screenshots/devtools_salt.png";
import DevtoolsFirefoxStorageTab from "../assets/screenshots/firefox/firefox_storage_tab.png";
import DevtoolsSelectDBFirefox from "../assets/screenshots//firefox/firefox_keyvalue_pairs.png";
import DevtoolsSaltFirefox from "../assets/screenshots/firefox/firefox_user_object.png";
import { Accordion } from "./Accordion";
import { getCombo } from "../utils/keycombos";
import Platform from "platform";
import { KeyCombo } from "./KeyCombo";
import { Code } from "./Code";
import { useState } from "react";
import { getSourceUrl } from "../utils/links";
type GetAccountSaltProps = {
onSaltSubmitted: (salt: string) => void;
};
const steps = {
chromium: [
"Focus the Notesnook tab in your browser.",
<>
Press <KeyCombo combo={getCombo("chromium", "developerTools")} /> to open
Developer Tools.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
Switch to the <Code text="Application" /> tab.
</Text>
<Image src={DevtoolsApplicationTab} width={300} sx={{ mt: 1 }} />
</Flex>,
<>
From the side menu, expand <Code text="IndexedDB" /> which is under the
Storage heading.
</>,
`You'll see a number of databases . Expand the one that starts with "Notesnook".`,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
Click on <Code text="keyvaluepairs" />.
</Text>
<Image src={DevtoolsSelectDB} width={200} sx={{ mt: 1 }} />
</Flex>,
<>
On the right panel, you'll see an input with the placeholder "Start from
key" at the top. Type <Code text="user" /> in that input.
</>,
`At the top of the list, you'll see an entry with key "user". Expand the value.`,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
You'll see a key named <Code text="salt" />. Copy the value against it.
</Text>
<Image src={DevtoolsSalt} width={400} sx={{ mt: 1 }} />
</Flex>,
],
firefox: [
"Focus the Notesnook tab in your browser.",
<>
Press <KeyCombo combo={getCombo("firefox", "developerTools")} /> to open
Developer Tools.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
Switch to the <Code text="Storage" /> tab.
</Text>
<Image src={DevtoolsFirefoxStorageTab} width={300} sx={{ mt: 1 }} />
</Flex>,
<>
From the side menu, expand <Code text="IndexedDB" /> which is under the
Storage heading.
</>,
`You'll see a number of databases . Expand the one that starts with "Notesnook".`,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
Click on <Code text="keyvaluepairs" />.
</Text>
<Image src={DevtoolsSelectDBFirefox} width={200} sx={{ mt: 1 }} />
</Flex>,
<>
On the right panel, you'll see an input with the placeholder "Start from
key" at the top. Type <Code text="user" /> in that input. If you don't see
the 'user' object, scroll down in key-value pair list and the search
again.
</>,
`At the top of the list, you'll see an entry with key "user". Expand the value.`,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
You'll see a key named <Code text="salt" />. Right click and copy the
value against it.
</Text>
<Image src={DevtoolsSaltFirefox} width={400} sx={{ mt: 1 }} />
</Flex>,
],
};
const isChromium = Platform.name === "Chrome";
const isFirefox = Platform.name === "Firefox";
const instructions = isChromium
? steps.chromium
: isFirefox
? steps.firefox
: null;
console.log(Platform.name);
export function GetAccountSalt(props: GetAccountSaltProps) {
const [isSaltValid, setIsSaltValid] = useState<boolean>();
return (
<StepContainer
onSubmit={(e) => {
e.preventDefault();
if (isSaltValid) {
document.getElementById("step_3")?.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
return false;
}}
onSubmitCapture={() => false}
inputMode="text"
id="step_2"
as="form"
sx={{ flexDirection: "column" }}
>
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
<Text variant="title">Account salt</Text>
<Code
text="src/components/Step2.tsx"
href={getSourceUrl("src/components/Step2.tsx")}
/>
</Flex>
<Accordion
title="How to get your account salt?"
sx={{
border: "1px solid var(--theme-ui-colors-border)",
mt: 2,
borderRadius: "default",
}}
>
<Text variant="body" sx={{ mx: 2 }}>
We'll be extracting your account's salt right from Notesnook's local
database that lives in your web browser. So put on your seat belt and
let's get some salt!
</Text>
<Text as="ol" variant="body" sx={{ mb: 2 }}>
{instructions?.map((item) => (
<Text as="li" sx={{ mt: 1 }}>
{item}
</Text>
))}
</Text>
</Accordion>
<Input
variant="forms.clean"
id="salt"
name="salt"
type="text"
placeholder="Enter your account salt"
sx={{
mt: 2,
fontSize: "subheading",
fontFamily: "monospace",
textAlign: "center",
color:
isSaltValid === true
? "primary"
: isSaltValid === false
? "error"
: "text",
}}
spellCheck={false}
onChange={(e) => {
setIsSaltValid(undefined);
try {
const value = e.target.value;
const isValid = Buffer.from(value, "base64").length === 16;
if (!isValid) setIsSaltValid(false);
else {
setIsSaltValid(true);
props.onSaltSubmitted(value);
}
} catch (e) {
setIsSaltValid(false);
}
}}
/>
</StepContainer>
);
}

View File

@@ -0,0 +1,71 @@
import { Flex, Text, Input } from "@theme-ui/components";
import { getSourceUrl } from "../utils/links";
import { Code } from "./Code";
import { StepContainer } from "./StepContainer";
type EnterAccountPasswordProps = {
onPasswordSubmitted: (password: string) => void;
};
export function EnterAccountPassword(props: EnterAccountPasswordProps) {
return (
<StepContainer
onSubmit={(e) => {
e.preventDefault();
document.getElementById("step_4")?.scrollIntoView({
behavior: "smooth",
block: "center",
});
return false;
}}
onSubmitCapture={() => false}
inputMode="text"
id="step_3"
as="form"
sx={{ flexDirection: "column" }}
>
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
<Text variant="title">Account password</Text>
<Code
text="src/components/Step3.tsx"
href={getSourceUrl("src/components/Step3.tsx")}
/>
</Flex>
<Flex
sx={{
bg: "bgSecondary",
mt: 2,
p: 2,
borderRadius: "default",
flexDirection: "column",
}}
>
<Text variant="subtitle">
Does my account password ever gets sent to the server?
</Text>
<Text as="p" variant="body">
Never. Your password never ever leaves the device. Everything is
happening locally. For good measure, you can even disconnect your
Internet. This is the most fundamental part of zero knowledge data
encryption.
</Text>
</Flex>
<Input
variant="forms.clean"
id="password"
name="password"
type="password"
placeholder="Enter your account password"
sx={{
mt: 2,
fontSize: "subheading",
fontFamily: "monospace",
textAlign: "center",
}}
onChange={(e) => {
props.onPasswordSubmitted(e.target.value);
}}
/>
</StepContainer>
);
}

View File

@@ -0,0 +1,241 @@
import { Flex, Text, Image, Textarea } from "@theme-ui/components";
import { StepContainer } from "./StepContainer";
import DevtoolsRequestsFilter from "../assets/screenshots/devtools_requests_filter.png";
import DevtoolsRequestsCopy from "../assets/screenshots/devtools_requests_copy.png";
import DevtoolsRequestsFilterFirefox from "../assets/screenshots/firefox/network_tab_firefox.png";
import DevtoolsRequestsCopyFirefox from "../assets/screenshots/firefox/copy_curl_request.png";
import { Accordion } from "./Accordion";
import { getCombo } from "../utils/keycombos";
import Platform from "platform";
import { KeyCombo } from "./KeyCombo";
import { Code } from "./Code";
import { useState, useEffect } from "react";
import { ErrorsList } from "./ErrorsList";
import { getSourceUrl } from "../utils/links";
import { parseCURL } from "../utils/curlparser";
type PasteEncryptedDataProps = {
onEncryptedDataPasted: (data?: SyncRequestBody) => void;
};
type EncryptedSyncItem = {
alg: string;
cipher: string;
format: "base64";
id: string;
iv: string;
length: number;
salt: string;
v: number;
};
export type SyncRequestBody = {
notes: EncryptedSyncItem[];
content: EncryptedSyncItem[];
notebooks: EncryptedSyncItem[];
attachments: EncryptedSyncItem[];
};
const SAMPLE_CURL = `Paste your cURL request here.
For example:
curl 'https://api.notesnook.com/sync'
-H 'authority: api.notesnook.com'
-H 'authorization: Bearer 84374D98...'
-H ...
-H ...
-H 'content-type: application/json'
--data-raw '{"notes":[{...}], "notebooks": [{...}], "content": [{...}], "lastSynced":1443215713231}'
--compressed`;
const steps = {
chromium: [
"Focus the Notesnook tab in your browser.",
<>
Press <KeyCombo combo={getCombo("chromium", "developerTools")} /> to open
Developer Tools.
</>,
<>
Switch to the <Code text="Network" /> tab.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
In the filter input, type <Code text="sync" /> to filter out sync
requests.
</Text>
<Image src={DevtoolsRequestsFilter} width={400} sx={{ mt: 1 }} />
</Flex>,
<>
At this point, you probably won't see anything useful. Try editing one of
your notes and syncing. Make sure to keep the <Code text="Network" /> tab
open.
</>,
<>
In the requests table in <Code text="Network" /> tab, you'll now see 3 or
more new requests popup.
</>,
<>
We are interested in only the <Code text="sync" /> request. Right click on
this request to open the context menu.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
From the context menu, go to <Code text="Copy" /> &amp; click on{" "}
<Code text="Copy as cURL" />.
</Text>
<Image src={DevtoolsRequestsCopy} width={400} sx={{ mt: 1 }} />
</Flex>,
],
firefox: [
"Focus the Notesnook tab in your browser.",
<>
Press <KeyCombo combo={getCombo("chromium", "developerTools")} /> to open
Developer Tools.
</>,
<>
Switch to the <Code text="Network" /> tab.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
In the filter input, type <Code text="sync" /> to filter out sync
requests.
</Text>
<Image src={DevtoolsRequestsFilterFirefox} width={400} sx={{ mt: 1 }} />
</Flex>,
<>
At this point, you probably won't see anything useful. Try editing one of
your notes and syncing. Make sure to keep the <Code text="Network" /> tab
open.
</>,
<>
In the requests table in <Code text="Network" /> tab, you'll now see 3 or
more new requests popup.
</>,
<>
We are interested in only the <Code text="sync" /> request. Right click on
this request to open the context menu.
</>,
<Flex sx={{ flexDirection: "column" }}>
<Text as="p">
From the context menu, go to <Code text="Copy" /> &amp; click on{" "}
<Code text="Copy as cURL" />.
</Text>
<Image src={DevtoolsRequestsCopyFirefox} width={400} sx={{ mt: 1 }} />
</Flex>,
],
};
const isChromium = Platform.name === "Chrome";
const isFirefox = Platform.name === "Firefox";
const instructions = isChromium
? steps.chromium
: isFirefox
? steps.firefox
: null;
export function PasteEncryptedData(props: PasteEncryptedDataProps) {
const { onEncryptedDataPasted } = props;
const [error, setError] = useState<string | undefined>();
const [encryptedData, setEncryptedData] = useState<
SyncRequestBody | undefined
>();
useEffect(() => {
onEncryptedDataPasted(encryptedData);
}, [encryptedData, onEncryptedDataPasted]);
return (
<StepContainer id="step_4" as="form" sx={{ flexDirection: "column" }}>
<Flex sx={{ justifyContent: "space-between", alignItems: "center" }}>
<Text variant="title">Paste raw encrypted data</Text>
<Code
text="src/components/Step4.tsx"
href={getSourceUrl("src/components/Step4.tsx")}
/>
</Flex>
<Accordion
title="How to get raw encrypted data?"
sx={{
border: "1px solid var(--theme-ui-colors-border)",
mt: 2,
borderRadius: "default",
}}
>
<Text variant="body" sx={{ mx: 2 }}>
To make this whole process verifiable &amp; trustworthy, we need to
extract the raw data that Notesnook sends to its servers during sync.
That way you can be sure that the data leaving your device is actually
encrypted or not.
</Text>
<Text as="ol" variant="body" sx={{ mb: 2 }}>
{instructions?.map((item) => (
<Text as="li" sx={{ mt: 1 }}>
{item}
</Text>
))}
</Text>
</Accordion>
<Textarea
variant="forms.clean"
placeholder={SAMPLE_CURL}
sx={{
mt: 2,
fontSize: "body",
fontFamily: "monospace",
whiteSpace: "pre-wrap",
height: 280,
}}
onChange={(e) => {
try {
setError(undefined);
const curlRequest = e.target.value;
if (!curlRequest) return setEncryptedData(undefined);
const parsed = parseCURL(curlRequest);
if (parsed.url !== "https://api.notesnook.com/sync") {
throw new Error(
"Invalid cURL request. URL must be https://api.notesnook.com/sync."
);
}
if (!parsed.data)
throw new Error("Invalid cURL request. No data found.");
const syncData: SyncRequestBody = JSON.parse(parsed.data);
if (syncData.notes.length <= 0 && syncData.content.length <= 0) {
throw new Error(
"Empty cURL request. Make sure the request body contains at least 1 note or 1 content."
);
}
setEncryptedData(syncData);
} catch (e) {
const error = e as Error;
setError(error.message);
}
}}
/>
{error ? <ErrorsList errors={[error]} /> : null}
{encryptedData && (
<Flex
sx={{
bg: "shade",
border: "2px solid var(--theme-ui-colors-primary)",
mt: 2,
p: 2,
borderRadius: "default",
flexDirection: "column",
}}
>
<Text variant="subtitle">Parsing complete</Text>
<Text as="p" variant="body">
We found {encryptedData.notes.length} note(s),{" "}
{encryptedData.content.length} content(s),{" "}
{encryptedData.notebooks.length} notebook(s) &amp;{" "}
{encryptedData.attachments.length} attachments.
</Text>
</Flex>
)}
</StepContainer>
);
}

View File

@@ -0,0 +1,23 @@
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",
bg: "background",
...sx,
}}
{...restProps}
>
{children}
</Flex>
);
}

View File

@@ -0,0 +1,98 @@
import { Flex, Text } from "@theme-ui/components";
import { useState } from "react";
import { IconType } from "react-icons";
import { MdClose } from "react-icons/md";
import { ThemeProvider } from "theme-ui";
import { ThemeFactory } from "../theme";
type StepSeperatorProps = {
icon?: IconType;
onShowPopup?: () => Promise<boolean>;
tooltip?: string;
popup?: { title: string; body?: JSX.Element };
};
export function StepSeperator(props: StepSeperatorProps) {
const [showPopup, setShowPopup] = useState<boolean>(false);
return (
<Flex
sx={{
height: 200,
width: 2,
bg: "bgSecondary",
justifyContent: "center",
alignItems: "center",
position: "relative",
}}
>
<Flex
sx={{
position: "absolute",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
}}
tabIndex={1}
>
{props.icon && (
<Flex
sx={{
bg: "background",
p: 2,
boxShadow: "0px 0px 10px 0px #00000011",
borderRadius: 50,
transition: "transform 100ms ease-out",
cursor: "pointer",
":hover": {
transform: "scale(1.1)",
},
}}
title={props.tooltip}
onClick={async () => {
if (showPopup) return setShowPopup(false);
if (!showPopup && props.onShowPopup)
setShowPopup(await props.onShowPopup());
}}
>
<props.icon size={20} />
</Flex>
)}
{showPopup && props.popup && (
<Flex
sx={{
position: "absolute",
top: 60,
p: 2,
width: 400,
}}
>
<ThemeProvider theme={ThemeFactory.construct("dark")}>
<Flex
sx={{
bg: "background",
borderRadius: "default",
boxShadow: "0px 0px 10px 0px #00000011",
p: 2,
flexDirection: "column",
color: "icon",
}}
>
<Flex
sx={{ justifyContent: "space-between", alignItems: "center" }}
>
<Text variant="title">{props.popup.title}</Text>
<MdClose
style={{ cursor: "pointer" }}
onClick={() => setShowPopup(false)}
/>
</Flex>
{props.popup.body}
</Flex>
</ThemeProvider>
</Flex>
)}
</Flex>
</Flex>
);
}

View File

View 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/vericrypt/src/react-app-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="react-scripts" />

View 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),
};
}
}

View 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(theme === "light" ? "#000" : "#fff");
}
}
export default ColorSchemeFactory;

View 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),
};
}
}

View 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",
};
}
}

View File

@@ -0,0 +1,13 @@
export class FontSizeFactory {
static construct() {
return {
heading: "48px",
subheading: "24px",
title: "16px",
subtitle: "14px",
body: "14px",
input: "14px",
subBody: "12px",
};
}
}

View 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;`,
},
};
}
}

View File

@@ -0,0 +1,27 @@
import ColorSchemeFactory from "./colorscheme";
import VariantsFactory from "./variants";
import { FontFactory } from "./font";
import { Theme } from "theme-ui";
export class ThemeFactory {
static construct(theme: "dark" | "light" = "light"): 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 },
// config: {
// initialColorModeName: "light",
// },
// rawColors: {
// modes: {
// dark: ColorSchemeFactory.construct("dark"),
// light: ColorSchemeFactory.construct("light"),
// },
// },
rawColors: ColorSchemeFactory.construct(theme),
...FontFactory.construct(),
...new VariantsFactory(),
};
}
}

View 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: 2,
px: 4,
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",
},
};
}
}

View 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;

View File

@@ -0,0 +1,57 @@
class InputFactory {
constructor() {
return {
input: new Default(),
error: new Error(),
clean: new Clean(),
};
}
}
export default InputFactory;
class Default {
constructor() {
return {
borderRadius: 5,
border: "none",
// borderColor: "border",
boxShadow: "0px 0px 0px 1px var(--theme-ui-colors-border) inset",
fontFamily: "body",
fontWeight: "body",
fontSize: "input",
color: "text",
outline: "none",
":focus": {
boxShadow: "0px 0px 0px 1.5px var(--theme-ui-colors-primary) inset",
},
":hover:not(:focus)": {
boxShadow: "0px 0px 0px 1px var(--theme-ui-colors-dimPrimary) inset",
},
};
}
}
class Clean {
constructor() {
return {
outline: "none",
border: "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",
},
};
}
}

View 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" };
}
}

View File

@@ -0,0 +1,169 @@
const LITERAL_NAMES = {
CURL: "curl",
INCLUDE_HEADERS: "include-headers",
SILENT: "run-silent",
USE_GET: "use-get",
COMPRESSED: "compressed",
STATES: {
HEADER: "header-state",
CUSTOM_METHOD: "custom-method-state",
DATA: "data-state",
USER: "user-state",
},
STRING: "string",
OTHERS: "others",
};
const literals = [
{
regex: /^\bcurl\b/,
name: LITERAL_NAMES.CURL,
},
{
regex: /^(-H|--header)/,
name: LITERAL_NAMES.STATES.HEADER,
},
{
regex: /^-X/,
name: LITERAL_NAMES.STATES.CUSTOM_METHOD,
},
{
regex: /^(-u|--user)/,
name: LITERAL_NAMES.STATES.USER,
},
{
regex: /^(-d|--data-raw|--data-ascii|--data)/,
name: LITERAL_NAMES.STATES.DATA,
},
{
regex: /^--compressed/,
name: LITERAL_NAMES.COMPRESSED,
},
{
regex: /^(-i|--include)/,
name: LITERAL_NAMES.INCLUDE_HEADERS,
},
{
regex: /^(-s|--silent)/,
name: LITERAL_NAMES.SILENT,
},
{
regex: /^(-G|--get)/,
name: LITERAL_NAMES.USE_GET,
},
{
regex: /^'[^']*'/,
name: LITERAL_NAMES.STRING,
},
{
regex: /^"[^"]*"/,
name: LITERAL_NAMES.STRING,
},
{
regex: /^[^\n\r\s]*/,
name: LITERAL_NAMES.STRING,
},
{
regex: /^.*/,
name: LITERAL_NAMES.OTHERS,
},
];
const trimValue = (val: string) =>
val
.trim()
.replace(/^('|")\s*/, "")
.replace(/\s*('|")$/, "");
const isURL = (val: string) =>
/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/.test(
val
);
type Token = { name: string; value: RegExpMatchArray | null };
type ParsedCurlRequest = {
url: string;
method: string;
headers: Record<string, string>;
data?: string;
};
export function parseCURL(curlCmd: string) {
let cmd = curlCmd.trim();
const tokens: Token[] = [];
const result: ParsedCurlRequest = {
url: "",
method: "GET",
headers: {} as Record<string, string>,
data: "",
};
while (cmd) {
for (const literal of literals) {
if (literal.regex.test(cmd)) {
tokens.push({
name: literal.name,
value: cmd.match(literal.regex),
});
cmd = cmd.replace(literal.regex, "").trim();
}
}
}
if (tokens.length) {
if (tokens.shift()?.name !== "curl") {
throw new Error("Not a curl command.");
}
let state = "";
tokens.forEach((token) => {
if (!token.value) return;
if (!state) {
switch (token.name) {
case LITERAL_NAMES.STATES.HEADER:
state = LITERAL_NAMES.STATES.HEADER;
break;
case LITERAL_NAMES.STATES.DATA:
state = LITERAL_NAMES.STATES.DATA;
break;
case LITERAL_NAMES.STATES.CUSTOM_METHOD:
state = LITERAL_NAMES.STATES.CUSTOM_METHOD;
break;
case LITERAL_NAMES.STATES.USER:
state = LITERAL_NAMES.STATES.USER;
break;
case LITERAL_NAMES.STRING: {
const val = trimValue(token.value[0]);
if (isURL(val)) {
result.url = val;
}
break;
}
}
} else {
switch (state) {
case LITERAL_NAMES.STATES.HEADER: {
const pair = trimValue(token.value[0]).split(/\s*:\s*/);
result.headers[pair[0]] = pair[1];
break;
}
case LITERAL_NAMES.STATES.DATA: {
console.log(token);
const pair = token.value[0];
result.data = trimValue(pair).trim();
break;
}
case LITERAL_NAMES.STATES.CUSTOM_METHOD: {
const pair = token.value[0];
result.method = trimValue(pair).trim();
break;
}
}
state = "";
}
});
}
return result;
}

View File

@@ -0,0 +1,26 @@
import Platform from "platform";
const isMac = Platform.os
?.toString()
.toLowerCase()
.includes("mac");
const combos = {
macos: {
chromium: { developerTools: ["Cmd", "Opt", "J"] },
firefox: { developerTools: ["Command", "Option", "K"] },
},
others: {
chromium: { developerTools: ["Control", "Shift", "J"] },
firefox: { developerTools: ["Control", "Shift", "K"] },
},
};
type KeyboardTypes = keyof typeof combos;
type Browsers = keyof typeof combos[KeyboardTypes];
type ComboIds = keyof typeof combos[KeyboardTypes][Browsers];
export function getCombo(browser: Browsers, id: ComboIds): string[] {
const keyboardType: KeyboardTypes = isMac ? "macos" : "others";
return combos[keyboardType][browser][id];
}

View File

@@ -0,0 +1,17 @@
type Packages = "crypto" | "importer" | "enex";
type Apps = "vericrypt" | "importer";
export function getSourceUrl(path: string) {
const baseUrl = `https://github.com/streetwriters/notesnook/tree/main/apps/vericrypt`;
return `${baseUrl}/${path}`;
}
export function getPackageUrl(packageId: Packages) {
const baseUrl = `https://github.com/streetwriters/notesnook/tree/main/packages`;
return `${baseUrl}/${packageId}`;
}
export function getAppUrl(appId: Apps) {
const baseUrl = `https://github.com/streetwriters/notesnook/tree/main/apps`;
return `${baseUrl}/${appId}`;
}

View File

@@ -0,0 +1 @@
export const appVersion = process.env.REACT_APP_VERSION;

View 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"]
}

View File

@@ -46,13 +46,14 @@ function createTrackerScript(document) {
(function() {
const output = buildDocs();
console.log(output)
if (!output.includes("0 errors")) {
console.error(output);
return;
}
console.log("Docs generated!");
const outputPath = /Output: (.*$)/gm.exec(output)[1];
const outputPath = /OUTPUT: (.*$)/gm.exec(output)[1];
console.log(`Documentation output path:`, outputPath);

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

Some files were not shown because too many files have changed in this diff Show More