mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-22 14:39:34 +01:00
Merge branch 'master' into beta
This commit is contained in:
@@ -71,7 +71,7 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
|||||||
);
|
);
|
||||||
await storage.migrate();
|
await storage.migrate();
|
||||||
|
|
||||||
const multiTab = isFeatureSupported("opfs");
|
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
|
||||||
database.setup({
|
database.setup({
|
||||||
sqliteOptions: {
|
sqliteOptions: {
|
||||||
dialect: (name, init) =>
|
dialect: (name, init) =>
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ function errorToString(error: unknown) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function resetDatabase() {
|
async function resetDatabase() {
|
||||||
const multiTab = isFeatureSupported("opfs");
|
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
|
||||||
await useKeyStore.getState().clear();
|
await useKeyStore.getState().clear();
|
||||||
const dialect = createDialect({
|
const dialect = createDialect({
|
||||||
name: "notesnook",
|
name: "notesnook",
|
||||||
|
|||||||
@@ -183,7 +183,8 @@ function StatusBar() {
|
|||||||
sx={{
|
sx={{
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
display: "flex"
|
display: "flex",
|
||||||
|
height: "100%"
|
||||||
}}
|
}}
|
||||||
data-test-id="vault-unlocked"
|
data-test-id="vault-unlocked"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -136,8 +136,11 @@ export function SubscriptionStatus() {
|
|||||||
type: "modal",
|
type: "modal",
|
||||||
title: strings.redeemingGiftCode(),
|
title: strings.redeemingGiftCode(),
|
||||||
subtitle: strings.pleaseWait() + "...",
|
subtitle: strings.pleaseWait() + "...",
|
||||||
action: () => db.subscriptions.redeemCode(giftCode)
|
action: () =>
|
||||||
}).catch((e) => showToast("error", e.message));
|
db.subscriptions
|
||||||
|
.redeemCode(giftCode)
|
||||||
|
.catch((e) => showToast("error", e.message))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
sx={{ bg: "background" }}
|
sx={{ bg: "background" }}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { isFeatureSupported } from "./feature-check";
|
|||||||
|
|
||||||
let logger: typeof _logger = new NoopLogger();
|
let logger: typeof _logger = new NoopLogger();
|
||||||
async function initializeLogger() {
|
async function initializeLogger() {
|
||||||
const multiTab = isFeatureSupported("opfs");
|
const multiTab = !!globalThis.SharedWorker && isFeatureSupported("opfs");
|
||||||
await initialize(
|
await initialize(
|
||||||
{
|
{
|
||||||
dialect: (name, init) =>
|
dialect: (name, init) =>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: Standard Notes
|
||||||
|
---
|
||||||
|
|
||||||
|
# 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. Select all your notes, right click, and select `Export`.
|
||||||
|
3. Open the Notesnook (app or desktop).
|
||||||
|
4. Go to `Settings > Notesnook Importer` and select `Standard Notes` from list of apps.
|
||||||
|

|
||||||
|
5. Drop the .zip file you exported earlier from Standard Notes in the box or click anywhere to open system file picker to select the backup.
|
||||||
|

|
||||||
|
6. Once importing completes you should see all your notes in Notesnook. If you face any issues during importing, [report it on github](https://github.com/streetwriters/notesnook).
|
||||||
|
|
||||||
|
## Supported formats
|
||||||
|
|
||||||
|
- [x] Text files
|
||||||
|
- [x] Authentication notes
|
||||||
|
- [x] Spreadsheets
|
||||||
|
- [ ] Tags
|
||||||
@@ -18,10 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { databaseTest, noteTest, TEST_NOTE } from "./utils/index.js";
|
import { databaseTest, noteTest, TEST_NOTE } from "./utils/index.js";
|
||||||
import { test, expect } from "vitest";
|
import { test, expect, describe } from "vitest";
|
||||||
|
|
||||||
function tag(title: string) {
|
function tag(title: string, dateCreated?: number) {
|
||||||
return { title };
|
return { title, dateCreated };
|
||||||
}
|
}
|
||||||
|
|
||||||
function color(title: string) {
|
function color(title: string) {
|
||||||
@@ -98,3 +98,50 @@ for (const type of ["tag", "color"] as const) {
|
|||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe("sort tags by", () => {
|
||||||
|
const tags = ["apple", "mango", "melon", "orange", "zucchini"];
|
||||||
|
const sortTestCases = [
|
||||||
|
{
|
||||||
|
sortBy: "title",
|
||||||
|
sortDirection: "asc",
|
||||||
|
expectedOrder: ["apple", "mango", "melon", "orange", "zucchini"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sortBy: "title",
|
||||||
|
sortDirection: "desc",
|
||||||
|
expectedOrder: ["zucchini", "orange", "melon", "mango", "apple"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sortBy: "dateCreated",
|
||||||
|
sortDirection: "asc",
|
||||||
|
expectedOrder: ["apple", "mango", "melon", "orange", "zucchini"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sortBy: "dateCreated",
|
||||||
|
sortDirection: "desc",
|
||||||
|
expectedOrder: ["zucchini", "orange", "melon", "mango", "apple"]
|
||||||
|
}
|
||||||
|
] as const;
|
||||||
|
for (const testCase of sortTestCases) {
|
||||||
|
test(`${testCase.sortBy} ${testCase.sortDirection}`, () =>
|
||||||
|
databaseTest().then(async (db) => {
|
||||||
|
let previousCreatedDate = Date.now();
|
||||||
|
for (const title of tags) {
|
||||||
|
await db.tags.add(tag(title, previousCreatedDate++));
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = await db.tags.all.grouped({
|
||||||
|
sortBy: testCase.sortBy,
|
||||||
|
sortDirection: testCase.sortDirection,
|
||||||
|
groupBy: "default"
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < testCase.expectedOrder.length; i++) {
|
||||||
|
expect((await items.item(i)).item?.title).toEqual(
|
||||||
|
testCase.expectedOrder[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -126,6 +126,16 @@ But this post was supposed to be about the "Secrets" of Typescript Generics, rig
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`convert HTML to markdown with leadingWhitespaces > html-to-md-leadingWhitespaces.md 1`] = `
|
||||||
|
"two spaces
|
||||||
|
|
||||||
|
four spaces
|
||||||
|
two spaces
|
||||||
|
four spaces
|
||||||
|
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`convert HTML to markdown with outlinelists > html-to-md-outlinelists.md 1`] = `
|
exports[`convert HTML to markdown with outlinelists > html-to-md-outlinelists.md 1`] = `
|
||||||
"Testing outline list:
|
"Testing outline list:
|
||||||
|
|
||||||
@@ -389,6 +399,14 @@ right? Well, let's get into that.
|
|||||||
4. TYPE INFERENCE USING INTERFACE PROPERTIES"
|
4. TYPE INFERENCE USING INTERFACE PROPERTIES"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`convert HTML to text with leadingWhitespaces > html-to-txt-leadingWhitespaces.txt 1`] = `
|
||||||
|
" two spaces
|
||||||
|
|
||||||
|
four spaces
|
||||||
|
two spaces
|
||||||
|
four spaces"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`convert HTML to text with outlinelists > html-to-txt-outlinelists.txt 1`] = `
|
exports[`convert HTML to text with outlinelists > html-to-txt-outlinelists.txt 1`] = `
|
||||||
"Testing outline list:
|
"Testing outline list:
|
||||||
* My outline list
|
* My outline list
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ const HTMLS = {
|
|||||||
tasklists: `<p>Hello</p><ul data-collapsed="false" class="checklist"><li class="checked checklist--item"><p>Task item 1</p></li><li class="checked checklist--item"><p>Task item 2</p></li><li class="checked checklist--item"><p>Task item 3</p></li><li class="checklist--item"><p>Task item 4</p><ul data-collapsed="false" class="checklist"><li class="checklist--item"><p>Sub task item 1</p></li><li class="checklist--item"><p>Sub task item 2</p></li></ul></li><li class="checklist--item"><p>Task Item 5</p></li></ul><p>Nene</p><ul><li><p>dasvsadv</p></li><li><p>adsva\`sd</p></li><li><p>vasd</p></li><li><p>vsadvdsa</p></li></ul>`,
|
tasklists: `<p>Hello</p><ul data-collapsed="false" class="checklist"><li class="checked checklist--item"><p>Task item 1</p></li><li class="checked checklist--item"><p>Task item 2</p></li><li class="checked checklist--item"><p>Task item 3</p></li><li class="checklist--item"><p>Task item 4</p><ul data-collapsed="false" class="checklist"><li class="checklist--item"><p>Sub task item 1</p></li><li class="checklist--item"><p>Sub task item 2</p></li></ul></li><li class="checklist--item"><p>Task Item 5</p></li></ul><p>Nene</p><ul><li><p>dasvsadv</p></li><li><p>adsva\`sd</p></li><li><p>vasd</p></li><li><p>vsadvdsa</p></li></ul>`,
|
||||||
outlinelists: `<p >Testing outline list:</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >My outline list</p></li><li data-type="outlineListItem"><p >works</p></li><li data-type="outlineListItem"><p >but sometimes</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >It doesn't</p></li><li data-type="outlineListItem"><p >what do I do?</p></li><li data-type="outlineListItem"><p >I need to do something!</p></li></ul></li><li data-type="outlineListItem"><p >Makes no sense!</p></li><li data-type="outlineListItem"><p >Yes it doesn't!</p></li></ul>`,
|
outlinelists: `<p >Testing outline list:</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >My outline list</p></li><li data-type="outlineListItem"><p >works</p></li><li data-type="outlineListItem"><p >but sometimes</p><ul data-collapsed="false" data-type="outlineList"><li data-type="outlineListItem"><p >It doesn't</p></li><li data-type="outlineListItem"><p >what do I do?</p></li><li data-type="outlineListItem"><p >I need to do something!</p></li></ul></li><li data-type="outlineListItem"><p >Makes no sense!</p></li><li data-type="outlineListItem"><p >Yes it doesn't!</p></li></ul>`,
|
||||||
codeblock2: `<pre>hello<br></pre>`,
|
codeblock2: `<pre>hello<br></pre>`,
|
||||||
singleSpacedParagraphs: `<p data-spacing="single">hello world</p><p data-spacing="single">hello world 2</p>`
|
singleSpacedParagraphs: `<p data-spacing="single">hello world</p><p data-spacing="single">hello world 2</p>`,
|
||||||
|
leadingWhitespaces: `<p data-spacing="double"> two spaces</p><p data-spacing="double"> four spaces</p><p data-spacing="single"> two spaces</p><p data-spacing="single"> four spaces</p>`
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const html in HTMLS) {
|
for (const html in HTMLS) {
|
||||||
|
|||||||
@@ -413,6 +413,22 @@ function convertHtmlToTxt(html: string, wrap = true) {
|
|||||||
builder.openBlock({
|
builder.openBlock({
|
||||||
leadingLineBreaks: dataSpacing == "single" ? 1 : 2
|
leadingLineBreaks: dataSpacing == "single" ? 1 : 2
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// convert leading whitespace to NO-BREAK SPACE
|
||||||
|
if (elem.children && elem.children.length > 0) {
|
||||||
|
const firstChild = elem.children[0];
|
||||||
|
if (firstChild.type === "text" && firstChild.data) {
|
||||||
|
firstChild.data = firstChild.data.replace(
|
||||||
|
/^([ \t]+)/,
|
||||||
|
(match) => {
|
||||||
|
return match
|
||||||
|
.replace(/ /g, "\u00A0")
|
||||||
|
.replace(/\t/g, "\u00A0\u00A0\u00A0\u00A0");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
walk(elem.children, builder);
|
walk(elem.children, builder);
|
||||||
builder.closeBlock({
|
builder.closeBlock({
|
||||||
trailingLineBreaks: 1
|
trailingLineBreaks: 1
|
||||||
|
|||||||
@@ -609,10 +609,10 @@ export class FilteredSelector<T extends Item> {
|
|||||||
sanitizeSortOptions(this.type, options);
|
sanitizeSortOptions(this.type, options);
|
||||||
|
|
||||||
const sortBy: Set<SortOptions["sortBy"]> = new Set();
|
const sortBy: Set<SortOptions["sortBy"]> = new Set();
|
||||||
|
sortBy.add(options.sortBy);
|
||||||
if (options.groupBy === "abc") sortBy.add("title");
|
if (options.groupBy === "abc") sortBy.add("title");
|
||||||
else if (options.sortBy === "title" && options.groupBy !== "none")
|
else if (options.sortBy === "title" && options.groupBy !== "none")
|
||||||
sortBy.add("dateCreated");
|
sortBy.add("dateCreated");
|
||||||
sortBy.add(options.sortBy);
|
|
||||||
|
|
||||||
return <T>(
|
return <T>(
|
||||||
qb: SelectQueryBuilder<DatabaseSchema, keyof DatabaseSchema, T>
|
qb: SelectQueryBuilder<DatabaseSchema, keyof DatabaseSchema, T>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Node, mergeAttributes } from "@tiptap/core";
|
import { Node, mergeAttributes, nodePasteRule } from "@tiptap/core";
|
||||||
import { insertMathNode } from "./plugin/index.js";
|
import { insertMathNode } from "./plugin/index.js";
|
||||||
import { NodeSelection } from "prosemirror-state";
|
import { NodeSelection } from "prosemirror-state";
|
||||||
import { tiptapKeys } from "@notesnook/common";
|
import { tiptapKeys } from "@notesnook/common";
|
||||||
@@ -32,6 +32,8 @@ declare module "@tiptap/core" {
|
|||||||
|
|
||||||
// simple inputrule for block math
|
// simple inputrule for block math
|
||||||
const REGEX_BLOCK_MATH_DOLLARS = /\$\$\$\s+$/; //new RegExp("\$\$\s+$", "i");
|
const REGEX_BLOCK_MATH_DOLLARS = /\$\$\$\s+$/; //new RegExp("\$\$\s+$", "i");
|
||||||
|
const REGEX_PASTE_BLOCK_MATH_DOLLARS = /\$\$\$([\s\S]*?)\$\$\$/g;
|
||||||
|
|
||||||
export const MathBlock = Node.create({
|
export const MathBlock = Node.create({
|
||||||
name: "mathBlock",
|
name: "mathBlock",
|
||||||
group: "block math",
|
group: "block math",
|
||||||
@@ -99,5 +101,20 @@ export const MathBlock = Node.create({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
addPasteRules() {
|
||||||
|
return [
|
||||||
|
nodePasteRule({
|
||||||
|
find: REGEX_PASTE_BLOCK_MATH_DOLLARS,
|
||||||
|
type: this.type,
|
||||||
|
getAttributes: (match) => {
|
||||||
|
return { content: match[1] };
|
||||||
|
},
|
||||||
|
getContent: (attrs) => {
|
||||||
|
return attrs.content ? [{ type: "text", text: attrs.content }] : [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Node, mergeAttributes } from "@tiptap/core";
|
import { Node, mergeAttributes, nodePasteRule } from "@tiptap/core";
|
||||||
import { mathPlugin } from "./plugin/index.js";
|
import { mathPlugin } from "./plugin/index.js";
|
||||||
|
|
||||||
declare module "@tiptap/core" {
|
declare module "@tiptap/core" {
|
||||||
@@ -40,6 +40,7 @@ const REGEX_INLINE_MATH_DOLLARS = /\$\$(.+)\$\$/; //new RegExp("\$(.+)\$", "i");
|
|||||||
// return REGEX_INLINE_MATH_DOLLARS;
|
// return REGEX_INLINE_MATH_DOLLARS;
|
||||||
// }
|
// }
|
||||||
// })();
|
// })();
|
||||||
|
const REGEX_PASTE_INLINE_MATH_DOLLARS = /\$\$([\s\S]*?)\$\$/g;
|
||||||
|
|
||||||
export const MathInline = Node.create({
|
export const MathInline = Node.create({
|
||||||
name: "mathInline",
|
name: "mathInline",
|
||||||
@@ -107,5 +108,20 @@ export const MathInline = Node.create({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
addPasteRules() {
|
||||||
|
return [
|
||||||
|
nodePasteRule({
|
||||||
|
find: REGEX_PASTE_INLINE_MATH_DOLLARS,
|
||||||
|
type: this.type,
|
||||||
|
getAttributes: (match) => {
|
||||||
|
return { content: match[1] };
|
||||||
|
},
|
||||||
|
getContent: (attrs) => {
|
||||||
|
return attrs.content ? [{ type: "text", text: attrs.content }] : [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user