Compare commits

...
Author SHA1 Message Date
StandardNotes CI 63f14d318d chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2023-10-30 21:24:10 +00:00
Aman Harwara 95cd682d49 chore: fix changelog [skip e2e] 2023-10-31 02:36:24 +05:30
StandardNotes CI 941eeea4e3 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
 - @standardnotes/[email protected].0
2023-10-30 20:34:09 +00:00
Aman Harwara 991de1ddf5 feat: When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format.
(#2610)
2023-10-31 01:19:04 +05:30
StandardNotes CI 044776d937 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].8
2023-10-30 19:44:10 +00:00
Aman Harwara 83fa6adbeb chore: fix tag rename [skip e2e] 2023-10-31 01:00:36 +05:30
StandardNotes CI ce908c8ec4 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
2023-10-29 14:51:21 +00:00
Aman Harwara 98c61de501 refactor: move overrides into scss file [skip e2e] 2023-10-29 20:08:06 +05:30
Aman Harwara 198efdc41c fix: Fixed issue with checklist items in Super notes not being correctly exported to HTML 2023-10-29 20:05:47 +05:30
StandardNotes CI cace6522f6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].6
2023-10-29 09:29:44 +00:00
Aman Harwara f3a9717883 chore: fix tag options menu opening notes list [skip e2e] 2023-10-29 14:41:44 +05:30
StandardNotes CI 00d30ca003 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].5
2023-10-29 08:46:12 +00:00
Aman Harwara 8574c42706 chore: fix positioning [skip e2e] 2023-10-29 14:03:25 +05:30
Aman Harwara 0f73ea8c4e chore: fix note options menu visibility in plain editor on mobile [skip e2e] 2023-10-29 14:00:45 +05:30
StandardNotes CI 12ccd22d78 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2023-10-28 11:46:29 +00:00
Aman Harwara d891ac7578 chore: remove unused css [skip e2e] 2023-10-28 17:01:18 +05:30
Aman Harwara ce0db04646 fix: Fixed check list indentation in Super notes 2023-10-28 16:30:44 +05:30
StandardNotes CI f9c624cfdc chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].3
2023-10-27 16:55:54 +00:00
Aman Harwara abc8f371aa chore: fix selection issue when deleting items on mobile [skip e2e] 2023-10-27 22:12:28 +05:30
StandardNotes CI 66c7d183ab chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2023-10-27 15:31:40 +00:00
Aman Harwara 96d4d814b2 chore: fix multi selection bar positoning [skip e2e] 2023-10-27 20:44:50 +05:30
StandardNotes CI 49e643b6a0 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2023-10-27 14:49:16 +00:00
Aman Harwara 64d3b01b6b chore: dont add toolbar position update listeners on mobile as its unnecessary [skip e2e] 2023-10-27 20:03:43 +05:30
StandardNotes CI 0b9116934b chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].0
2023-10-27 13:04:48 +00:00
Aman Harwara c8dec9bfec feat: Re-enabled multiple selection on mobile, with improved UI (#2609) 2023-10-27 17:54:51 +05:30
StandardNotes CI 66a6c61ffa chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
2023-10-26 18:30:05 +00:00
Aman Harwara aa31fe483a chore: fix mobile menu colors [skip e2e] 2023-10-26 23:39:05 +05:30
StandardNotes CI 54a4c86a81 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].4
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected].3
2023-10-26 15:59:33 +00:00
Karol Sójko 874961a7cd chore: upgrade @standardnotes/domain-core and @standardnotes/settings (#2607) 2023-10-26 17:14:06 +02:00
StandardNotes CI ad02f90cb1 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].2
2023-10-26 14:40:59 +00:00
Aman Harwara a04590eb78 chore: fix changelog 2023-10-26 19:28:35 +05:30
StandardNotes CI d7a47d01f2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected].1
2023-10-25 22:14:47 +00:00
Aman Harwara 86e8d49368 chore: trigger release [skip e2e] 2023-10-26 03:31:11 +05:30
Aman Harwara c1ee705402 fix: Fixed issue where some menus would get hidden behind the keyboard on iOS 2023-10-26 03:06:12 +05:30
101 changed files with 1731 additions and 932 deletions
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.26.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/api
## [1.26.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/api
## [1.26.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/api
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api",
"version": "1.26.67",
"version": "1.26.69",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -36,7 +36,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/utils": "workspace:*",
+60
View File
@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.1.298](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.297](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.296](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.295](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.294](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.293](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.292](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.291](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.290](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.289](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.288](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.287](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.286](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.285](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.284](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/clipper
## [1.1.283](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/clipper
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/clipper",
"description": "Web clipper browser extension for Standard Notes",
"version": "1.1.283",
"version": "1.1.298",
"private": true,
"scripts": {
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
+60
View File
@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.108.232](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.231](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.230](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.229](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.228](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.227](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.226](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.225](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.224](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.223](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.222](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.221](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.220](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.219](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.218](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/desktop
## [3.108.217](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/desktop
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.108.217",
"version": "3.108.232",
"license": "AGPL-3.0",
"author": "Standard Notes.",
"private": true,
@@ -35,7 +35,7 @@
},
"dependencies": {
"@electron/remote": "^2.0.12",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/electron-clear-data": "1.1.1",
"@standardnotes/web": "workspace:*",
"axios": "^1.4.0",
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.91](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.90](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/encryption
## [1.21.89](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/encryption
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/encryption",
"version": "1.21.89",
"version": "1.21.91",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -29,7 +29,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.60.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/features
## [1.60.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-23)
**Note:** Version bump only for package @standardnotes/features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/features",
"version": "1.60.3",
"version": "1.60.4",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -26,7 +26,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.28.102](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.101](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/filepicker
## [1.28.100](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/filepicker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/filepicker",
"version": "1.28.100",
"version": "1.28.102",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.18.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
### Features
* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format. ([991de1d](https://github.com/standardnotes/app/commit/991de1ddf5dbb1016348d603d244699c676d9b5f)), closes [#2610](https://github.com/standardnotes/app/issues/2610)
## [1.17.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/files
## [1.17.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/files
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files",
"version": "1.17.19",
"version": "1.18.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -1,5 +1,6 @@
export interface SuperConverterServiceInterface {
isValidSuperString(superString: string): boolean
convertSuperStringToOtherFormat: (superString: string, toFormat: 'txt' | 'md' | 'html' | 'json') => string
convertSuperStringToOtherFormat: (superString: string, toFormat: 'txt' | 'md' | 'html' | 'json') => Promise<string>
convertOtherFormatToSuperString: (otherFormatString: string, fromFormat: 'txt' | 'md' | 'html' | 'json') => string
getEmbeddedFileIDsFromSuperString(superString: string): string[]
}
+60
View File
@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.56.211](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.210](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.207](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.206](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.205](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.204](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.203](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.202](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.201](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.200](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.199](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.198](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.197](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/mobile
## [3.56.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/mobile
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.56.196",
"version": "3.56.211",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0",
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.49.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
### Features
* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format. ([991de1d](https://github.com/standardnotes/app/commit/991de1ddf5dbb1016348d603d244699c676d9b5f)), closes [#2610](https://github.com/standardnotes/app/issues/2610)
## [1.48.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/models
# [1.48.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
### Features
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/models",
"version": "1.48.0",
"version": "1.49.0",
"publishConfig": {
"access": "public"
},
@@ -20,7 +20,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:^",
@@ -40,6 +40,8 @@ export const PrefDefaults = {
[PrefKey.ClipperDefaultTagUuid]: undefined,
[PrefKey.DefaultEditorIdentifier]: NativeFeatureIdentifier.TYPES.PlainEditor,
[PrefKey.SuperNoteExportFormat]: 'json',
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference',
[PrefKey.SuperNoteExportUseMDFrontmatter]: true,
[PrefKey.SystemViewPreferences]: {},
[PrefKey.AuthenticatorNames]: '',
[PrefKey.ComponentPreferences]: {},
@@ -41,6 +41,8 @@ export enum PrefKey {
ClipperDefaultTagUuid = 'clipperDefaultTagUuid',
SystemViewPreferences = 'systemViewPreferences',
SuperNoteExportFormat = 'superNoteExportFormat',
SuperNoteExportEmbedBehavior = 'superNoteExportEmbedBehavior',
SuperNoteExportUseMDFrontmatter = 'superNoteExportUseMDFrontmatter',
AuthenticatorNames = 'authenticatorNames',
PaneGesturesEnabled = 'paneGesturesEnabled',
ComponentPreferences = 'componentPreferences',
@@ -83,6 +85,8 @@ export type PrefValue = {
[PrefKey.ClipperDefaultTagUuid]: string | undefined
[PrefKey.SystemViewPreferences]: Partial<Record<SystemViewId, TagPreferences>>
[PrefKey.SuperNoteExportFormat]: 'json' | 'md' | 'html'
[PrefKey.SuperNoteExportEmbedBehavior]: 'reference' | 'inline' | 'separate'
[PrefKey.SuperNoteExportUseMDFrontmatter]: boolean
[PrefKey.AuthenticatorNames]: string
[PrefKey.PaneGesturesEnabled]: boolean
[PrefKey.ComponentPreferences]: AllComponentPreferences
+60
View File
@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.586](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.585](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.584](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.583](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.582](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.581](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.580](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.579](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.578](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.577](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.576](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.575](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.574](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.573](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.572](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.571](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.571",
"version": "1.4.586",
"license": "AGPL-3.0",
"main": "dist/releases.json",
"types": "dist/index.d.ts",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.13.51](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/responses
## [1.13.50](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-23)
**Note:** Version bump only for package @standardnotes/responses
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/responses",
"version": "1.13.50",
"version": "1.13.51",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.65.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
### Features
* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format. ([991de1d](https://github.com/standardnotes/app/commit/991de1ddf5dbb1016348d603d244699c676d9b5f)), closes [#2610](https://github.com/standardnotes/app/issues/2610)
## [1.64.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/services
## [1.64.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/services
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/services",
"version": "1.64.37",
"version": "1.65.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -18,7 +18,7 @@
"dependencies": {
"@standardnotes/api": "workspace:^",
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/encryption": "workspace:^",
"@standardnotes/features": "workspace:^",
"@standardnotes/files": "workspace:^",
@@ -462,7 +462,7 @@ export class FilesBackupService
const tagNames = tags.map((tag) => this.items.getTagLongTitle(tag))
const text =
note.noteType === NoteType.Super
? this.markdownConverter.convertSuperStringToOtherFormat(note.text, 'md')
? await this.markdownConverter.convertSuperStringToOtherFormat(note.text, 'md')
: note.text
await this.device.savePlaintextNoteBackup(location, note.uuid, note.title, tagNames, text)
}
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.202.104](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.103](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/snjs
## [2.202.102](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/snjs
@@ -1,6 +1,5 @@
import { AuthClientInterface } from '@standardnotes/services'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
import { SettingName } from '@standardnotes/settings'
import { Result, SettingName, UseCaseInterface } from '@standardnotes/domain-core'
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
@@ -1,8 +1,7 @@
import { ItemInterface, SNFeatureRepo } from '@standardnotes/models'
import { SyncService } from '../Sync/SyncService'
import { SettingName } from '@standardnotes/settings'
import { FeaturesService } from '@Lib/Services/Features'
import { RoleName, ContentType, Uuid, Result } from '@standardnotes/domain-core'
import { RoleName, ContentType, Uuid, Result, SettingName } from '@standardnotes/domain-core'
import { NativeFeatureIdentifier, GetFeatures } from '@standardnotes/features'
import { SettingsService } from '../Settings'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
@@ -1,7 +1,7 @@
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
import { SettingName } from '@standardnotes/domain-core'
import { SNFeatureRepo } from '@standardnotes/models'
import { MutatorClientInterface } from '@standardnotes/services'
import { SettingName } from '@standardnotes/settings'
export class MigrateFeatureRepoToUserSettingUseCase {
constructor(
@@ -2,7 +2,6 @@ import { isString, lastElement, sleep } from '@standardnotes/utils'
import { UuidString } from '@Lib/Types/UuidString'
import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { DeprecatedHttpService } from '../Api/DeprecatedHttpService'
import { SettingName } from '@standardnotes/settings'
import { SettingsService } from '../Settings/SNSettingsService'
import { ListedClientInterface } from './ListedClientInterface'
import { LegacyApiService } from '../Api/ApiService'
@@ -15,7 +14,7 @@ import {
SyncServiceInterface,
} from '@standardnotes/services'
import { ProtectionService } from '../Protection'
import { ContentType } from '@standardnotes/domain-core'
import { ContentType, SettingName } from '@standardnotes/domain-core'
export class ListedService extends AbstractService implements ListedClientInterface {
constructor(
+1 -2
View File
@@ -1,9 +1,8 @@
import { SettingName } from '@standardnotes/settings'
import { SettingsService } from '../Settings'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { FeaturesService } from '../Features/FeaturesService'
import { AbstractService, InternalEventBusInterface, MfaServiceInterface, SignInStrings } from '@standardnotes/services'
import { SettingName } from '@standardnotes/domain-core'
export class MfaService extends AbstractService implements MfaServiceInterface {
constructor(
@@ -1,9 +1,10 @@
import { LegacyApiService } from '../Api/ApiService'
import { SettingsGateway } from './SettingsGateway'
import { SessionManager } from '../Session/SessionManager'
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings'
import { EmailBackupFrequency } from '@standardnotes/settings'
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
import { SettingsClientInterface } from './SettingsClientInterface'
import { SettingName } from '@standardnotes/domain-core'
export class SettingsService extends AbstractService implements SettingsClientInterface {
private provider!: SettingsGateway
@@ -1,5 +1,6 @@
import { SettingName, EmailBackupFrequency } from '@standardnotes/settings'
import { EmailBackupFrequency } from '@standardnotes/settings'
import { SettingsList } from './SettingsList'
import { SettingName } from '@standardnotes/domain-core'
export interface SettingsClientInterface {
listSettings(): Promise<SettingsList>
@@ -1,8 +1,8 @@
import { SettingsList } from './SettingsList'
import { SettingName } from '@standardnotes/settings'
import { API_MESSAGE_INVALID_SESSION } from '@standardnotes/services'
import { getErrorFromErrorResponse, HttpStatusCode, isErrorResponse, User } from '@standardnotes/responses'
import { SettingsServerInterface } from './SettingsServerInterface'
import { SettingName } from '@standardnotes/domain-core'
/**
* SettingsGateway coordinates communication with the API service
@@ -1,3 +1,4 @@
import { SettingName } from '@standardnotes/domain-core'
import { SettingData } from '@standardnotes/responses'
import {
MuteSignInEmailsOption,
@@ -5,7 +6,6 @@ import {
EmailBackupFrequency,
ListedAuthorSecretsData,
LogSessionUserAgentOption,
SettingName,
MuteMarketingEmailsOption,
} from '@standardnotes/settings'
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.202.102",
"version": "2.202.104",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -37,7 +37,7 @@
"@babel/preset-env": "*",
"@standardnotes/api": "workspace:*",
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/domain-events": "^2.122.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/features": "workspace:*",
@@ -45,7 +45,7 @@
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/services": "workspace:*",
"@standardnotes/settings": "^1.20.0",
"@standardnotes/settings": "^1.22.0",
"@standardnotes/sncrypto-common": "workspace:*",
"@standardnotes/sncrypto-web": "workspace:*",
"@standardnotes/utils": "workspace:*",
+10
View File
@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.33.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
### Features
* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format. ([991de1d](https://github.com/standardnotes/app/commit/991de1ddf5dbb1016348d603d244699c676d9b5f)), closes [#2610](https://github.com/standardnotes/app/issues/2610)
## [1.32.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.32.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
**Note:** Version bump only for package @standardnotes/ui-services
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.32.6",
"version": "1.33.0",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
@@ -16,7 +16,7 @@
},
"dependencies": {
"@standardnotes/common": "^1.50.0",
"@standardnotes/domain-core": "^1.34.1",
"@standardnotes/domain-core": "^1.40.0",
"@standardnotes/features": "workspace:^",
"@standardnotes/filepicker": "workspace:^",
"@standardnotes/models": "workspace:^",
@@ -19,6 +19,11 @@ function zippableFileName(name: string, suffix = '', format = 'txt'): string {
return sanitizedName.slice(0, maxFileNameLength - nameEnd.length) + nameEnd
}
export function parseAndCreateZippableFileName(name: string) {
const { name: parsedName, ext } = parseFileName(name)
return zippableFileName(parsedName, '', ext)
}
type ZippableData = {
name: string
content: Blob
@@ -31,7 +31,8 @@ describe('EvernoteConverter', () => {
const superConverterService: SuperConverterServiceInterface = {
isValidSuperString: () => true,
convertOtherFormatToSuperString: (data: string) => data,
convertSuperStringToOtherFormat: (data: string) => data,
convertSuperStringToOtherFormat: async (data: string) => data,
getEmbeddedFileIDsFromSuperString: () => [],
}
const generateUuid = new GenerateUuid(crypto)
@@ -16,7 +16,8 @@ describe('GoogleKeepConverter', () => {
const superConverterService: SuperConverterServiceInterface = {
isValidSuperString: () => true,
convertOtherFormatToSuperString: (data: string) => data,
convertSuperStringToOtherFormat: (data: string) => data,
convertSuperStringToOtherFormat: async (data: string) => data,
getEmbeddedFileIDsFromSuperString: () => [],
}
const generateUuid = new GenerateUuid(crypto)
@@ -36,7 +36,6 @@ export const SUPER_SHOW_MARKDOWN_PREVIEW = createKeyboardCommand('SUPER_SHOW_MAR
export const SUPER_EXPORT_JSON = createKeyboardCommand('SUPER_EXPORT_JSON')
export const SUPER_EXPORT_MARKDOWN = createKeyboardCommand('SUPER_EXPORT_MARKDOWN')
export const SUPER_EXPORT_HTML = createKeyboardCommand('SUPER_EXPORT_HTML')
export const OPEN_PREFERENCES_COMMAND = createKeyboardCommand('OPEN_PREFERENCES_COMMAND')
export const CHANGE_EDITOR_WIDTH_COMMAND = createKeyboardCommand('CHANGE_EDITOR_WIDTH_COMMAND')
+71 -1
View File
@@ -3,11 +3,81 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.181.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/web
# [3.181.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
### Features
* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format.
## [3.180.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)
**Note:** Version bump only for package @standardnotes/web
## [3.180.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
### Bug Fixes
* Fixed issue with checklist items in Super notes not being correctly exported to HTML ([198efdc](https://github.com/standardnotes/app/commit/198efdc41c403ce10a40fc2f20345d5b44c83462))
## [3.180.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/web
## [3.180.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)
**Note:** Version bump only for package @standardnotes/web
## [3.180.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)
### Bug Fixes
* Fixed check list indentation in Super notes ([ce0db04](https://github.com/standardnotes/app/commit/ce0db04646737ec1dcb88ac989cd12927c46a156))
## [3.180.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/web
## [3.180.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/web
## [3.180.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
**Note:** Version bump only for package @standardnotes/web
# [3.180.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)
### Features
* Re-enabled multiple selection on mobile, with improved UI ([#2609](https://github.com/standardnotes/app/issues/2609)) ([c8dec9b](https://github.com/standardnotes/app/commit/c8dec9bfecc77257e686fe02e36973318145c16e))
## [3.179.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/web
## [3.179.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/web
## [3.179.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)
**Note:** Version bump only for package @standardnotes/web
## [3.179.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
### Bug Fixes
* Fixed issue where some menus would get hidden behind the keyboard on iOS ([c1ee705](https://github.com/standardnotes/app/commit/c1ee705402ed63910ef733464796ab5f848c5ac4))
# [3.179.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
### Features
* Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. [skip e2e] ([a616750](https://github.com/standardnotes/app/commit/a616750aea3666079aa6ae301662ce4cd4d5c954))
* Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. ([a616750](https://github.com/standardnotes/app/commit/a616750aea3666079aa6ae301662ce4cd4d5c954))
## [3.178.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)
+183 -3
View File
@@ -1,16 +1,196 @@
{
"versions": [
{
"version": "3.181.1",
"title": "[3.181.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.181.0",
"title": "[3.181.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)",
"date": null,
"body": "### Features\n\n* When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format.",
"parsed": {
"_": [
"When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format."
],
"Features": [
"When exporting a Super note, embedded files can be inlined in the note or exported along the note in a zip file. You can now also choose to include frontmatter when exporting to Markdown format."
]
}
},
{
"version": "3.180.8",
"title": "[3.180.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-30)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.7",
"title": "[3.180.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed issue with checklist items in Super notes not being correctly exported to HTML ([198efdc](https://github.com/standardnotes/app/commit/198efdc41c403ce10a40fc2f20345d5b44c83462))",
"parsed": {
"_": [
"Fixed issue with checklist items in Super notes not being correctly exported to HTML (198efdc)"
],
"Bug Fixes": [
"Fixed issue with checklist items in Super notes not being correctly exported to HTML (198efdc)"
]
}
},
{
"version": "3.180.6",
"title": "[3.180.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.5",
"title": "[3.180.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-29)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.4",
"title": "[3.180.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-28)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed check list indentation in Super notes ([ce0db04](https://github.com/standardnotes/app/commit/ce0db04646737ec1dcb88ac989cd12927c46a156))",
"parsed": {
"_": [
"Fixed check list indentation in Super notes (ce0db04)"
],
"Bug Fixes": [
"Fixed check list indentation in Super notes (ce0db04)"
]
}
},
{
"version": "3.180.3",
"title": "[3.180.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.2",
"title": "[3.180.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.1",
"title": "[3.180.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.180.0",
"title": "[3.180.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-27)",
"date": null,
"body": "### Features\n\n* Re-enabled multiple selection on mobile, with improved UI ([#2609](https://github.com/standardnotes/app/issues/2609)) ([c8dec9b](https://github.com/standardnotes/app/commit/c8dec9bfecc77257e686fe02e36973318145c16e))",
"parsed": {
"_": [
"Re-enabled multiple selection on mobile, with improved UI (#2609) (c8dec9b)"
],
"Features": [
"Re-enabled multiple selection on mobile, with improved UI (#2609) (c8dec9b)"
]
}
},
{
"version": "3.179.4",
"title": "[3.179.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.179.3",
"title": "[3.179.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.179.2",
"title": "[3.179.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-26)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.179.1",
"title": "[3.179.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)",
"date": null,
"body": "### Bug Fixes\n\n* Fixed issue where some menus would get hidden behind the keyboard on iOS ([c1ee705](https://github.com/standardnotes/app/commit/c1ee705402ed63910ef733464796ab5f848c5ac4))",
"parsed": {
"_": [
"Fixed issue where some menus would get hidden behind the keyboard on iOS (c1ee705)"
],
"Bug Fixes": [
"Fixed issue where some menus would get hidden behind the keyboard on iOS (c1ee705)"
]
}
},
{
"version": "3.179.0",
"title": "[3.179.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-10-25)",
"date": null,
"body": "### Features\n\n* Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. [skip e2e] ([a616750](https://github.com/standardnotes/app/commit/a616750aea3666079aa6ae301662ce4cd4d5c954))",
"body": "### Features\n\n* Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. ([a616750](https://github.com/standardnotes/app/commit/a616750aea3666079aa6ae301662ce4cd4d5c954))",
"parsed": {
"_": [
"Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. skip e2e] ([a616750)"
"Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. (a616750)"
],
"Features": [
"Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. skip e2e] ([a616750)"
"Added preference to toggle Super note toolbar visibility. When toggled off, the toolbar will only be visible when text is selected as a floating toolbar. (a616750)"
]
}
},
+2 -2
View File
@@ -1,9 +1,9 @@
{
"name": "@standardnotes/web",
"version": "3.179.0",
"version": "3.181.1",
"license": "AGPL-3.0",
"main": "dist/app.js",
"author": "Standard Notes",
"author": "Standard Notes.",
"private": true,
"files": [
"dist"
@@ -7,7 +7,7 @@ import { ListableContentItem } from './Types/ListableContentItem'
import ContentListItem from './ContentListItem'
import { ElementIds } from '@/Constants/ElementIDs'
import { classNames } from '@standardnotes/utils'
import { ContentType, SNTag } from '@standardnotes/snjs'
import { SNTag } from '@standardnotes/snjs'
import { ItemListController } from '@/Controllers/ItemList/ItemListController'
type Props = {
@@ -78,15 +78,12 @@ const ContentList: FunctionComponent<Props> = ({ application, items, selectedUui
[hideTags, selectedTag, application],
)
const hasNotes = items.some((item) => item.content_type === ContentType.TYPES.Note)
return (
<div
className={classNames(
'infinite-scroll overflow-y-auto overflow-x-hidden focus:shadow-none focus:outline-none',
'md:max-h-full md:overflow-y-hidden md:hover:overflow-y-auto pointer-coarse:md:overflow-y-auto',
'flex flex-wrap pb-2 md:hover:[overflow-y:_overlay]',
hasNotes ? 'justify-center' : 'justify-center md:justify-start md:pl-1',
'flex-grow pb-2 md:hover:[overflow-y:_overlay]',
)}
id={ElementIds.ContentList}
onScroll={onScroll}
@@ -33,6 +33,9 @@ import EmptyFilesView from './EmptyFilesView'
import { PaneLayout } from '@/Controllers/PaneController/PaneLayout'
import { usePaneSwipeGesture } from '../Panes/usePaneGesture'
import { mergeRefs } from '@/Hooks/mergeRefs'
import Icon from '../Icon/Icon'
import MobileMultiSelectionToolbar from './MobileMultiSelectionToolbar'
import StyledTooltip from '../StyledTooltip/StyledTooltip'
type Props = {
application: WebApplication
@@ -53,6 +56,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
noAccountWarningController,
searchOptionsController,
linkingController,
notesController,
} = application
const { setPaneLayout, panes } = useResponsiveAppPane()
@@ -285,7 +289,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
aria-label={'Notes & Files'}
ref={mergeRefs([innerRef, setElement])}
>
{isMobileScreen && (
{isMobileScreen && !itemListController.isMultipleSelectionMode && (
<FloatingAddButton onClick={addNewItem} label={addButtonLabel} style={dailyMode ? 'danger' : 'info'} />
)}
<div id="items-title-bar" className="section-title-bar border-b border-solid border-border">
@@ -319,6 +323,33 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
/>
</div>
</div>
{itemListController.isMultipleSelectionMode && (
<div className="flex items-center border-b border-l-2 border-border border-l-transparent py-2.5 pr-4">
<div className="px-4">
<StyledTooltip label="Select all items" showOnHover showOnMobile>
<button
className="ml-auto rounded border border-border p-1 hover:bg-contrast"
onClick={() => {
itemListController.selectAll()
}}
>
<Icon type="select-all" size="medium" />
</button>
</StyledTooltip>
</div>
<div className="text-base font-semibold md:text-sm">{itemListController.selectedItemsCount} selected</div>
<StyledTooltip label="Cancel multiple selection" showOnHover showOnMobile>
<button
className="ml-auto rounded border border-border p-1 hover:bg-contrast"
onClick={() => {
itemListController.cancelMultipleSelection()
}}
>
<Icon type="close" size="medium" />
</button>
</StyledTooltip>
</div>
)}
{selectedAsTag && dailyMode && (
<DailyContentList
items={items}
@@ -350,6 +381,9 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
/>
)
) : null}
{isMobileScreen && itemListController.isMultipleSelectionMode && (
<MobileMultiSelectionToolbar notesController={notesController} />
)}
<div className="absolute bottom-0 h-safe-bottom w-full" />
{children}
</div>
@@ -0,0 +1,43 @@
import { NotesController } from '@/Controllers/NotesController/NotesController'
import Icon from '../Icon/Icon'
type Props = {
notesController: NotesController
}
const MobileMultiSelectionToolbar = ({ notesController }: Props) => {
const { selectedNotes } = notesController
const archived = selectedNotes.some((note) => note.archived)
return (
<div className="flex w-full bg-contrast pb-safe-bottom">
<button
className="flex-grow px-2 py-3 active:bg-passive-3"
onClick={() => notesController.togglePinSelectedNotes()}
>
<Icon type="pin" className="mx-auto text-info" size="large" />
</button>
<button
className="flex-grow px-2 py-3 active:bg-passive-3"
onClick={() => notesController.toggleArchiveSelectedNotes().catch(console.error)}
>
<Icon type={archived ? 'unarchive' : 'archive'} className="mx-auto text-info" size="large" />
</button>
<button
className="flex-grow px-2 py-3 active:bg-passive-3"
onClick={() => notesController.setTrashSelectedNotes(true).catch(console.error)}
>
<Icon type="trash" className="mx-auto text-info" size="large" />
</button>
<button
className="flex-grow px-2 py-3 active:bg-passive-3"
onClick={() => notesController.setContextMenuOpen(true)}
>
<Icon type="more" className="mx-auto text-info" size="large" />
</button>
</div>
)
}
export default MobileMultiSelectionToolbar
@@ -1,6 +1,6 @@
import { isFile, SNNote } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent, useCallback, useRef } from 'react'
import { FunctionComponent, MouseEvent, useCallback, useRef } from 'react'
import Icon from '@/Components/Icon/Icon'
import ListItemConflictIndicator from './ListItemConflictIndicator'
import ListItemFlagIcons from './ListItemFlagIcons'
@@ -17,6 +17,7 @@ import ListItemVaultInfo from './ListItemVaultInfo'
import { NoteDragDataFormat } from '../Tags/DragNDrop'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import useItem from '@/Hooks/useItem'
import CheckIndicator from '../Checkbox/CheckIndicator'
const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
application,
@@ -50,7 +51,17 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
notesController.setContextMenuOpen(true)
}
const openContextMenu = async (posX: number, posY: number) => {
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const handleContextMenuEvent = async (posX: number, posY: number) => {
if (isMobileScreen) {
if (!application.itemListController.isMultipleSelectionMode) {
application.itemListController.replaceSelection(item)
}
application.itemListController.enableMultipleSelectionMode()
return
}
let shouldOpenContextMenu = selected
if (!selected) {
@@ -65,17 +76,26 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
}
}
const onClick = useCallback(async () => {
await onSelect(item, true)
}, [item, onSelect])
const onClick = useCallback(
(event: MouseEvent) => {
if ((event.ctrlKey || event.metaKey) && !application.itemListController.isMultipleSelectionMode) {
application.itemListController.enableMultipleSelectionMode()
}
if (selected && !application.itemListController.isMultipleSelectionMode) {
application.itemListController.openSingleSelectedItem({ userTriggered: true }).catch(console.error)
return
}
onSelect(item, true).catch(console.error)
},
[application.itemListController, item, onSelect, selected],
)
useContextMenuEvent(listItemRef, openContextMenu)
useContextMenuEvent(listItemRef, handleContextMenuEvent)
log(LoggingDomain.ItemsList, 'Rendering note list item', item.title)
const hasOffsetBorder = !isNextItemTiled
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const dragPreview = useRef<HTMLDivElement>()
const createDragPreview = () => {
@@ -108,14 +128,18 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
ref={listItemRef}
role="button"
className={classNames(
'content-list-item flex w-full cursor-pointer items-stretch text-text',
selected && `selected border-l-2 border-solid border-accessory-tint-${tint}`,
isPreviousItemTiled && 'mt-3 border-t border-solid border-t-border',
isNextItemTiled && 'mb-3 border-b border-solid border-b-border',
'content-list-item flex w-full cursor-pointer items-stretch border-l-2 text-text',
selected
? `selected ${
application.itemListController.isMultipleSelectionMode ? 'border-info' : `border-accessory-tint-${tint}`
}`
: 'border-transparent',
isPreviousItemTiled && 'mt-3 border-t border-t-border',
isNextItemTiled && 'mb-3 border-b border-b-border',
)}
id={item.uuid}
onClick={onClick}
draggable={!isMobileScreen}
draggable={!isMobileScreen && !application.itemListController.isMultipleSelectionMode}
onDragStart={(event) => {
if (!listItemRef.current) {
return
@@ -133,7 +157,11 @@ const NoteListItem: FunctionComponent<DisplayableListItemProps<SNNote>> = ({
}
}}
>
{!hideIcon ? (
{application.itemListController.isMultipleSelectionMode ? (
<div className="mr-0 flex flex-col items-center justify-between gap-2 p-4 pr-4">
<CheckIndicator className="md:!h-5 md:!w-5" checked={selected} />
</div>
) : !hideIcon ? (
<div className="mr-0 flex flex-col items-center justify-between gap-2 p-4 pr-4">
<Icon type={icon} className={`text-accessory-tint-${tint}`} />
</div>
@@ -65,6 +65,7 @@ export const IconNameToSvgMapping = {
'premium-feature': icons.PremiumFeatureIcon,
'rich-text': icons.RichTextIcon,
'safe-square': icons.SafeSquareIcon,
'select-all': icons.SelectAllIcon,
'sort-descending': icons.SortDescendingIcon,
'star-circle-filled': icons.StarCircleFilled,
'star-filled': icons.StarFilledIcon,
@@ -35,6 +35,7 @@ const LinkedItemsButton = ({ linkingController, onClickPreprocessing }: Props) =
anchorElement={buttonRef}
open={isLinkingPanelOpen}
className="pb-2"
forceFullHeightOnMobile
>
<LinkedItemsPanel item={activeItem} />
</Popover>
@@ -41,7 +41,7 @@ const MenuItem = forwardRef(
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames(
'flex w-full cursor-pointer select-none border-0 bg-transparent px-3 py-2.5 text-left md:py-1.5',
'text-mobile-menu-item text-text enabled:hover:bg-contrast enabled:hover:text-foreground',
'text-mobile-menu-item text-text enabled:hover:bg-passive-3 enabled:hover:text-foreground',
'focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item',
'disabled:cursor-not-allowed disabled:opacity-60',
className,
@@ -75,7 +75,7 @@ const MenuRadioButtonItem = forwardRef(
tabIndex={typeof tabIndex === 'number' ? tabIndex : FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames(
'flex w-full cursor-pointer gap-2 border-0 bg-transparent px-3 py-2.5 text-left md:py-1.5',
'text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground',
'text-mobile-menu-item text-text hover:bg-passive-3 hover:text-foreground',
'focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item',
className,
className?.includes('items-') ? '' : 'items-center',
@@ -18,7 +18,7 @@ const MenuSection = ({
)}
>
{title && <div className="px-3 py-1 text-sm font-semibold uppercase text-text lg:text-xs">{title}</div>}
<div className="divide-y divide-passive-3 overflow-hidden rounded-md bg-passive-4 md:divide-none md:rounded-none md:bg-transparent">
<div className="divide-y divide-passive-3 overflow-hidden rounded-md bg-default md:divide-none md:rounded-none md:bg-transparent">
{children}
</div>
</div>
@@ -37,7 +37,7 @@ const MenuSwitchButtonItem = forwardRef(
ref={ref}
className={classNames(
'flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 md:py-1.5',
'text-left text-text focus:bg-info-backdrop focus:shadow-none enabled:hover:bg-contrast enabled:hover:text-foreground',
'text-left text-text focus:bg-info-backdrop focus:shadow-none enabled:hover:bg-passive-3 enabled:hover:text-foreground',
'text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item',
'disabled:cursor-not-allowed disabled:opacity-60',
className,
@@ -27,25 +27,29 @@ export const DiffView = ({
const [textDiff, setTextDiff] = useState<fastdiff.Diff[]>([])
useEffect(() => {
const firstNote = selectedNotes[0]
const firstTitle = firstNote.title
const firstText =
firstNote.noteType === NoteType.Super && convertSuperToMarkdown
? new HeadlessSuperConverter().convertSuperStringToOtherFormat(firstNote.text, 'md')
: firstNote.text
const setDiffs = async () => {
const firstNote = selectedNotes[0]
const firstTitle = firstNote.title
const firstText =
firstNote.noteType === NoteType.Super && convertSuperToMarkdown
? await new HeadlessSuperConverter().convertSuperStringToOtherFormat(firstNote.text, 'md')
: firstNote.text
const secondNote = selectedNotes[1]
const secondTitle = secondNote.title
const secondText =
secondNote.noteType === NoteType.Super && convertSuperToMarkdown
? new HeadlessSuperConverter().convertSuperStringToOtherFormat(secondNote.text, 'md')
: secondNote.text
const secondNote = selectedNotes[1]
const secondTitle = secondNote.title
const secondText =
secondNote.noteType === NoteType.Super && convertSuperToMarkdown
? await new HeadlessSuperConverter().convertSuperStringToOtherFormat(secondNote.text, 'md')
: secondNote.text
const titleDiff = fastdiff(firstTitle, secondTitle, undefined, true)
const textDiff = fastdiff(firstText, secondText, undefined, true)
const titleDiff = fastdiff(firstTitle, secondTitle, undefined, true)
const textDiff = fastdiff(firstText, secondText, undefined, true)
setTitleDiff(titleDiff)
setTextDiff(textDiff)
setTitleDiff(titleDiff)
setTextDiff(textDiff)
}
setDiffs().catch(console.error)
}, [convertSuperToMarkdown, selectedNotes])
const [preElement, setPreElement] = useState<HTMLPreElement | null>(null)
@@ -29,7 +29,7 @@ import {
VaultUserServiceEvent,
} from '@standardnotes/snjs'
import { confirmDialog, DELETE_NOTE_KEYBOARD_COMMAND, KeyboardKey } from '@standardnotes/ui-services'
import { ChangeEventHandler, createRef, CSSProperties, KeyboardEventHandler, RefObject } from 'react'
import { ChangeEventHandler, createRef, CSSProperties, FocusEvent, KeyboardEventHandler, RefObject } from 'react'
import { SuperEditor } from '../SuperEditor/SuperEditor'
import IndicatorCircle from '../IndicatorCircle/IndicatorCircle'
import LinkedItemBubblesContainer from '../LinkedItems/LinkedItemBubblesContainer'
@@ -826,7 +826,10 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
this.setState({ plainEditorFocused: true })
}
onPlainBlur = () => {
onPlainBlur = (event: FocusEvent) => {
if (event.relatedTarget?.id === ElementIds.NoteOptionsButton) {
return
}
this.setState({ plainEditorFocused: false })
}
@@ -897,8 +900,9 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
>
<div
className={classNames(
'mb-2 flex flex-wrap justify-between gap-2 md:mb-0 md:flex-nowrap md:gap-4 xl:items-center',
'mb-2 flex justify-between md:mb-0 md:flex-nowrap md:gap-4 xl:items-center',
shouldShowConflictsButton ? 'items-center' : 'items-start',
!renderHeaderOptions ? 'flex-nowrap gap-4' : 'flex-wrap gap-2 ',
)}
>
<div className={classNames(this.state.noteLocked && 'locked', 'flex flex-grow items-center')}>
@@ -937,30 +941,33 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
{pluralize(this.state.conflictedNotes.length, 'conflict', 'conflicts')}
</Button>
)}
{renderHeaderOptions && (
<div className="note-view-options-buttons flex items-center gap-3">
{!this.state.readonly && (
<>
<LinkedItemsButton
linkingController={this.application.linkingController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<ChangeEditorButton
noteViewController={this.controller}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<PinNoteButton
notesController={this.application.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
</>
)}
<NotesOptionsPanel
notesController={this.application.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
</div>
)}
<div className="note-view-options-buttons flex items-center gap-3">
{!this.state.readonly && renderHeaderOptions && (
<>
<LinkedItemsButton
linkingController={this.application.linkingController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<ChangeEditorButton
noteViewController={this.controller}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
<PinNoteButton
notesController={this.application.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
/>
</>
)}
<NotesOptionsPanel
notesController={this.application.notesController}
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
onButtonBlur={() => {
this.setState({
plainEditorFocused: false,
})
}}
/>
</div>
</div>
<div className="mb-1 mt-2.5 md:hidden">
<CollaborationInfoHUD item={this.note} />
@@ -16,7 +16,16 @@ import {
PrefDefaults,
} from '@standardnotes/snjs'
import { TAB_COMMAND } from '@standardnotes/ui-services'
import { ChangeEventHandler, forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import {
ChangeEventHandler,
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useRef,
useState,
FocusEvent,
} from 'react'
import { NoteViewController } from '../Controller/NoteViewController'
type Props = {
@@ -25,7 +34,7 @@ type Props = {
controller: NoteViewController
locked: boolean
onFocus: () => void
onBlur: () => void
onBlur: (event: FocusEvent) => void
}
export type PlainEditorInterface = {
@@ -106,13 +115,16 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
onFocus()
}, [application, isAdjustingMobileCursor, lastEditorFocusEventSource, onFocus])
const onContentBlur = useCallback(() => {
if (lastEditorFocusEventSource.current) {
application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: lastEditorFocusEventSource })
}
lastEditorFocusEventSource.current = undefined
onBlur()
}, [application, lastEditorFocusEventSource, onBlur])
const onContentBlur = useCallback(
(event: FocusEvent) => {
if (lastEditorFocusEventSource.current) {
application.notifyWebEvent(WebAppEvent.EditorFocused, { eventSource: lastEditorFocusEventSource })
}
lastEditorFocusEventSource.current = undefined
onBlur(event)
},
[application, lastEditorFocusEventSource, onBlur],
)
const scrollMobileCursorIntoViewAfterWebviewResize = useCallback(() => {
if (needsAdjustMobileCursor.current) {
@@ -1,6 +1,6 @@
import Icon from '@/Components/Icon/Icon'
import { observer } from 'mobx-react-lite'
import { useState, useEffect, useMemo, useCallback, useRef } from 'react'
import { useState, useEffect, useMemo, useCallback } from 'react'
import { NoteType, Platform, SNNote } from '@standardnotes/snjs'
import {
CHANGE_EDITOR_WIDTH_COMMAND,
@@ -8,9 +8,6 @@ import {
PIN_NOTE_COMMAND,
SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
STAR_NOTE_COMMAND,
SUPER_EXPORT_HTML,
SUPER_EXPORT_JSON,
SUPER_EXPORT_MARKDOWN,
SUPER_SHOW_MARKDOWN_PREVIEW,
} from '@standardnotes/ui-services'
import ChangeEditorOption from './ChangeEditorOption'
@@ -20,9 +17,7 @@ import { addToast, dismissToast, ToastType } from '@standardnotes/toast'
import { NotesOptionsProps } from './NotesOptionsProps'
import { useResponsiveAppPane } from '../Panes/ResponsivePaneProvider'
import { AppPaneId } from '../Panes/AppPaneMetadata'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { shareSelectedNotes } from '@/NativeMobileWeb/ShareSelectedNotes'
import { downloadSelectedNotesOnAndroid } from '@/NativeMobileWeb/DownloadSelectedNotesOnAndroid'
import { createNoteExport } from '@/Utils/NoteExportUtils'
import ProtectedUnauthorizedLabel from '../ProtectedItemOverlay/ProtectedUnauthorizedLabel'
import { MenuItemIconSize } from '@/Constants/TailwindClassNames'
import { KeyboardShortcutIndicator } from '../KeyboardShortcutIndicator/KeyboardShortcutIndicator'
@@ -39,9 +34,9 @@ import SuperExportModal from './SuperExportModal'
import { useApplication } from '../ApplicationProvider'
import { MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import AddToVaultMenuOption from '../Vaults/AddToVaultMenuOption'
import Menu from '../Menu/Menu'
import Popover from '../Popover/Popover'
import MenuSection from '../Menu/MenuSection'
import { downloadOrShareBlobBasedOnPlatform } from '@/Utils/DownloadOrShareBasedOnPlatform'
import { shareBlobOnMobile } from '@/NativeMobileWeb/ShareBlobOnMobile'
const iconSize = MenuItemIconSize
const iconClassDanger = `text-danger mr-2 ${iconSize}`
@@ -104,34 +99,40 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
}, [])
const downloadSelectedItems = useCallback(async () => {
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
application.archiveService.downloadData(blob, getNoteFileName(application, note))
return
}
if (notes.length > 1) {
const loadingToastId = addToast({
type: ToastType.Loading,
message: `Exporting ${notes.length} notes...`,
try {
const result = await createNoteExport(application, notes)
if (!result) {
return
}
const { blob, fileName } = result
void downloadOrShareBlobBasedOnPlatform({
archiveService: application.archiveService,
platform: application.platform,
mobileDevice: application.mobileDevice,
blob: blob,
filename: fileName,
isNativeMobileWeb: application.isNativeMobileWeb(),
})
await application.archiveService.downloadDataAsZip(
notes.map((note) => {
return {
name: getNoteFileName(application, note),
content: getNoteBlob(application, note),
}
}),
)
dismissToast(loadingToastId)
} catch (error) {
console.error(error)
addToast({
type: ToastType.Success,
message: `Exported ${notes.length} notes`,
type: ToastType.Error,
message: 'Could not export notes',
})
}
}, [application, notes])
const exportSelectedItems = useCallback(() => {
const hasSuperNote = notes.some((note) => note.noteType === NoteType.Super)
if (hasSuperNote) {
setShowExportSuperModal(true)
return
}
downloadSelectedItems().catch(console.error)
}, [downloadSelectedItems, notes])
const closeMenuAndToggleNotesList = useCallback(() => {
const isMobileScreen = matchMedia(MutuallyExclusiveMediaQueryBreakpoints.sm).matches
if (isMobileScreen) {
@@ -199,9 +200,6 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
[application],
)
const superExportButtonRef = useRef<HTMLButtonElement>(null)
const [isSuperExportMenuOpen, setIsSuperExportMenuOpen] = useState(false)
const unauthorized = notes.some((note) => !application.isAuthorizedToRenderItem(note))
if (unauthorized) {
return <ProtectedUnauthorizedLabel />
@@ -224,8 +222,6 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
return null
}
const isOnlySuperNoteSelected = notes.length === 1 && notes[0].noteType === NoteType.Super
return (
<>
{notes.length === 1 && (
@@ -289,7 +285,7 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
</MenuSection>
)}
<MenuSection>
<MenuSection className={notes.length > 1 ? 'md:!mb-2' : ''}>
{application.featuresController.isVaultsEnabled() && (
<AddToVaultMenuOption
iconClassName={iconClass}
@@ -342,77 +338,35 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
{pinShortcut && <KeyboardShortcutIndicator className="ml-auto" shortcut={pinShortcut} />}
</MenuItem>
)}
{isOnlySuperNoteSelected ? (
<>
<MenuItem
ref={superExportButtonRef}
onClick={() => {
setIsSuperExportMenuOpen((open) => !open)
}}
>
<div className="flex items-center">
<Icon type="download" className={iconClass} />
Export
</div>
<Icon type="chevron-right" className="ml-auto text-neutral" />
</MenuItem>
<Popover
title="Export note"
side="left"
align="start"
open={isSuperExportMenuOpen}
anchorElement={superExportButtonRef.current}
togglePopover={() => {
setIsSuperExportMenuOpen(!isSuperExportMenuOpen)
}}
className="md:py-1"
>
<Menu a11yLabel={'Super note export menu'}>
<MenuSection>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_JSON, notes[0].title)}>
<Icon type="code" className={iconClass} />
Export as JSON
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_MARKDOWN, notes[0].title)}>
<Icon type="markdown" className={iconClass} />
Export as Markdown
</MenuItem>
<MenuItem onClick={() => commandService.triggerCommand(SUPER_EXPORT_HTML, notes[0].title)}>
<Icon type="rich-text" className={iconClass} />
Export as HTML
</MenuItem>
</MenuSection>
</Menu>
</Popover>
</>
) : (
<>
<MenuItem
onClick={() => {
if (application.isNativeMobileWeb()) {
void shareSelectedNotes(application, notes)
} else {
const hasSuperNote = notes.some((note) => note.noteType === NoteType.Super)
if (hasSuperNote) {
setShowExportSuperModal(true)
<MenuItem
onClick={() => {
if (application.isNativeMobileWeb()) {
createNoteExport(application, notes)
.then((result) => {
if (!result) {
return
}
void downloadSelectedItems()
}
}}
>
<Icon type={application.platform === Platform.Android ? 'share' : 'download'} className={iconClass} />
{application.platform === Platform.Android ? 'Share' : 'Export'}
</MenuItem>
{application.platform === Platform.Android && (
<MenuItem onClick={() => downloadSelectedNotesOnAndroid(application, notes)}>
<Icon type="download" className={iconClass} />
Export
</MenuItem>
)}
</>
const { blob, fileName } = result
shareBlobOnMobile(application.mobileDevice, application.isNativeMobileWeb(), blob, fileName).catch(
console.error,
)
})
.catch(console.error)
} else {
exportSelectedItems()
}
}}
>
<Icon type={application.platform === Platform.Android ? 'share' : 'download'} className={iconClass} />
{application.platform === Platform.Android ? 'Share' : 'Export'}
</MenuItem>
{application.platform === Platform.Android && (
<MenuItem onClick={exportSelectedItems}>
<Icon type="download" className={iconClass} />
Export
</MenuItem>
)}
<MenuItem onClick={duplicateSelectedItems} disabled={areSomeNotesInReadonlySharedVault}>
<Icon type="copy" className={iconClass} />
@@ -507,7 +461,7 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
)}
</MenuSection>
{notes.length === 1 ? (
{notes.length === 1 && (
<>
{notes[0].noteType === NoteType.Super && (
<SuperNoteOptions
@@ -538,8 +492,6 @@ const NotesOptions = ({ notes, closeMenu }: NotesOptionsProps) => {
<NoteSizeWarning note={notes[0]} />
</>
) : (
<div className="h-2" />
)}
<ModalOverlay isOpen={showExportSuperModal} close={closeSuperExportModal}>
@@ -1,17 +1,19 @@
import { useCallback, useRef, useState } from 'react'
import { FocusEvent, useCallback, useRef, useState } from 'react'
import { observer } from 'mobx-react-lite'
import NotesOptions from './NotesOptions'
import { NotesController } from '@/Controllers/NotesController/NotesController'
import Popover from '../Popover/Popover'
import RoundIconButton from '../Button/RoundIconButton'
import Menu from '../Menu/Menu'
import { ElementIds } from '@/Constants/ElementIDs'
type Props = {
notesController: NotesController
onClickPreprocessing?: () => Promise<void>
onButtonBlur?: (event: FocusEvent) => void
}
const NotesOptionsPanel = ({ notesController, onClickPreprocessing }: Props) => {
const NotesOptionsPanel = ({ notesController, onClickPreprocessing, onButtonBlur }: Props) => {
const [isOpen, setIsOpen] = useState(false)
const buttonRef = useRef<HTMLButtonElement>(null)
@@ -30,7 +32,14 @@ const NotesOptionsPanel = ({ notesController, onClickPreprocessing }: Props) =>
return (
<>
<RoundIconButton label="Note options menu" onClick={toggleMenu} ref={buttonRef} icon="more" />
<RoundIconButton
id={ElementIds.NoteOptionsButton}
label="Note options menu"
onClick={toggleMenu}
onBlur={onButtonBlur}
ref={buttonRef}
icon="more"
/>
<Popover
title="Note options"
disableClickOutside={disableClickOutside}
@@ -1,8 +1,10 @@
import { PrefKey, PrefValue } from '@standardnotes/snjs'
import { useApplication } from '../ApplicationProvider'
import Dropdown from '../Dropdown/Dropdown'
import Modal from '../Modal/Modal'
import usePreference from '@/Hooks/usePreference'
import RadioButtonGroup from '../RadioButtonGroup/RadioButtonGroup'
import { useEffect } from 'react'
import Switch from '../Switch/Switch'
type Props = {
exportNotes: () => void
@@ -12,6 +14,17 @@ type Props = {
const SuperExportModal = ({ exportNotes, close }: Props) => {
const application = useApplication()
const superNoteExportFormat = usePreference(PrefKey.SuperNoteExportFormat)
const superNoteExportEmbedBehavior = usePreference(PrefKey.SuperNoteExportEmbedBehavior)
const superNoteExportUseMDFrontmatter = usePreference(PrefKey.SuperNoteExportUseMDFrontmatter)
useEffect(() => {
if (superNoteExportFormat === 'json' && superNoteExportEmbedBehavior === 'separate') {
void application.setPreference(PrefKey.SuperNoteExportEmbedBehavior, 'reference')
}
if (superNoteExportFormat === 'md' && superNoteExportEmbedBehavior === 'reference') {
void application.setPreference(PrefKey.SuperNoteExportEmbedBehavior, 'separate')
}
}, [application, superNoteExportEmbedBehavior, superNoteExportFormat])
return (
<Modal
@@ -28,20 +41,21 @@ const SuperExportModal = ({ exportNotes, close }: Props) => {
{
label: 'Export',
type: 'primary',
onClick: exportNotes,
onClick: () => {
close()
exportNotes()
},
mobileSlot: 'right',
},
]}
>
<div className="mb-4">
<div className="mb-1 text-base">
We detected your selection includes Super notes. How do you want to export them?
</div>
<Dropdown
label="Super notes export format"
<div className="mb-2">
<div className="mb-3 text-base">We detected your selection includes Super notes.</div>
<div className="mb-1">What format do you want to export them in?</div>
<RadioButtonGroup
items={[
{ label: 'Keep as Super', value: 'json' },
{ label: 'Markdown', value: 'md' },
{ label: 'Super (.json)', value: 'json' },
{ label: 'Markdown (.md)', value: 'md' },
{ label: 'HTML', value: 'html' },
]}
value={superNoteExportFormat}
@@ -52,12 +66,47 @@ const SuperExportModal = ({ exportNotes, close }: Props) => {
)
}}
/>
{superNoteExportFormat === 'md' && (
<div className="mt-2 text-xs text-passive-0">
Note that conversion to Markdown is not lossless. Some features like collapsible blocks and formatting like
superscript/subscript may not be correctly converted.
</div>
)}
</div>
<div className="text-passive-0">
Note that if you convert Super notes to Markdown then import them back into Standard Notes in the future, you
will lose some formatting that the Markdown format is incapable of expressing, such as collapsible blocks and
embeds.
</div>
{superNoteExportFormat === 'md' && (
<div className="mt-4">
<Switch
checked={superNoteExportUseMDFrontmatter}
onChange={(checked) => {
void application.setPreference(
PrefKey.SuperNoteExportUseMDFrontmatter,
checked as PrefValue[PrefKey.SuperNoteExportUseMDFrontmatter],
)
}}
className="!flex items-center"
>
<span className="ml-2">Export with frontmatter</span>
</Switch>
</div>
)}
{superNoteExportFormat !== 'json' && (
<div className="mb-2 mt-4">
<div className="mb-1">How do you want embedded files to be handled?</div>
<RadioButtonGroup
items={[
{ label: 'Inline', value: 'inline' },
{ label: 'Export separately', value: 'separate' },
].concat(superNoteExportFormat !== 'md' ? [{ label: 'Keep as reference', value: 'reference' }] : [])}
value={superNoteExportEmbedBehavior}
onChange={(value) => {
void application.setPreference(
PrefKey.SuperNoteExportEmbedBehavior,
value as PrefValue[PrefKey.SuperNoteExportEmbedBehavior],
)
}}
/>
</div>
)}
</Modal>
)
}
@@ -24,6 +24,7 @@ const MobilePopoverContent = ({
title,
className,
id,
forceFullHeightOnMobile,
}: {
open: boolean
requestClose: () => void
@@ -31,6 +32,7 @@ const MobilePopoverContent = ({
title: string
id: string
className?: string
forceFullHeightOnMobile?: boolean
}) => {
const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const [isMounted, setPopoverElement, element] = useModalAnimation(open, isMobileScreen)
@@ -164,24 +166,35 @@ const MobilePopoverContent = ({
return (
<Portal>
<DisableScroll />
<div className="fixed inset-0 z-modal">
<div className="absolute inset-0 z-0 bg-passive-4 opacity-0" ref={setUnderlayElement} />
<div className="fixed left-0 top-0 z-modal h-full max-h-[var(--ios-viewport-height,_none)] w-full">
<div className="absolute z-0 h-full w-full bg-passive-4 opacity-0" ref={setUnderlayElement} />
<div
ref={mergeRefs([setPopoverElement, addCloseMethod])}
className="z-1 absolute bottom-0 flex max-h-[calc(100%_-_max(var(--safe-area-inset-top),2rem))] min-h-[40%] w-full flex-col rounded-t-xl bg-default pb-safe-bottom"
className={classNames(
'z-1 absolute bottom-0 flex max-h-[calc(100%_-_max(var(--safe-area-inset-top),2rem))] min-h-[40%] w-full flex-col rounded-t-xl bg-passive-5 pb-safe-bottom',
forceFullHeightOnMobile && 'h-full',
)}
style={{
boxShadow: '0px -4px 8px rgba(0, 0, 0, 0.075)',
}}
id={'popover/' + id}
data-popover={id}
data-mobile-popover
>
<div className="mx-auto mt-2 min-h-[0.3rem] w-12 rounded-full bg-passive-2" />
<MobileModalHeader className="border-b border-border px-2 py-1.5 text-lg">
<div className="w-full rounded-t-xl bg-default">
<div className="mx-auto mt-2 min-h-[0.3rem] w-12 rounded-full bg-passive-2" />
</div>
<MobileModalHeader className="border-b border-border bg-default px-2 py-1.5 text-lg">
<div />
<div className="flex items-center justify-center font-semibold">{title}</div>
<MobileModalAction type="primary" slot="right" action={requestClose}>
Done
</MobileModalAction>
</MobileModalHeader>
<div className={classNames('h-full overflow-y-auto overscroll-none', className)} ref={scrollContainerRef}>
<div
className={classNames('h-full overflow-y-auto overscroll-none bg-passive-5', className)}
ref={scrollContainerRef}
>
{children}
</div>
</div>
@@ -138,6 +138,7 @@ const Popover = (props: PopoverProps) => {
title={props.title}
className={props.className}
id={popoverId}
forceFullHeightOnMobile={props.forceFullHeightOnMobile}
>
{props.children}
</MobilePopoverContent>
@@ -44,6 +44,7 @@ type CommonPopoverProps = {
togglePopover?: () => void
disableMobileFullscreenTakeover?: boolean
disableFlip?: boolean
forceFullHeightOnMobile?: boolean
title: string
portal?: boolean
offset?: number
@@ -2,7 +2,6 @@ import { FunctionComponent, UIEventHandler, useCallback, useState } from 'react'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
import { ContentEditable } from '@lexical/react/LexicalContentEditable'
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin'
import { ClearEditorPlugin } from '@lexical/react/LexicalClearEditorPlugin'
import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPlugin'
import { TablePlugin } from '@lexical/react/LexicalTablePlugin'
@@ -28,6 +27,7 @@ import { RemoveBrokenTablesPlugin } from './Plugins/TablePlugin'
import TableActionMenuPlugin from './Plugins/TableCellActionMenuPlugin'
import ToolbarPlugin from './Plugins/ToolbarPlugin/ToolbarPlugin'
import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import { CheckListPlugin } from './Plugins/List/CheckListPlugin'
type BlocksEditorProps = {
onChange?: (value: string, preview: string) => void
@@ -16,8 +16,11 @@ import { FileNode } from '../../Plugins/EncryptedFilePlugin/Nodes/FileNode'
import { BubbleNode } from '../../Plugins/ItemBubblePlugin/Nodes/BubbleNode'
import { RemoteImageNode } from '../../Plugins/RemoteImagePlugin/RemoteImageNode'
import { InlineFileNode } from '../../Plugins/InlineFilePlugin/InlineFileNode'
import { CreateEditorArgs } from 'lexical'
import { ListHTMLExportNode } from '../../Plugins/List/ListHTMLExportNode'
import { FileExportNode } from './FileExportNode'
export const BlockEditorNodes = [
const CommonNodes = [
AutoLinkNode,
CodeHighlightNode,
CodeNode,
@@ -29,7 +32,6 @@ export const BlockEditorNodes = [
HorizontalRuleNode,
LinkNode,
ListItemNode,
ListNode,
MarkNode,
OverflowNode,
QuoteNode,
@@ -43,3 +45,17 @@ export const BlockEditorNodes = [
RemoteImageNode,
InlineFileNode,
]
export const BlockEditorNodes = [...CommonNodes, ListNode]
export const SuperExportNodes: CreateEditorArgs['nodes'] = [
...CommonNodes,
FileExportNode,
ListHTMLExportNode,
{
replace: ListNode,
with(node) {
return new ListHTMLExportNode(node.getListType(), node.getStart())
},
},
]
@@ -0,0 +1,93 @@
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
import { parseAndCreateZippableFileName } from '@standardnotes/ui-services'
import { DOMExportOutput, Spread } from 'lexical'
type SerializedFileExportNode = Spread<
{
version: 1
type: 'file-export'
name: string
mimeType: string
},
SerializedDecoratorBlockNode
>
export class FileExportNode extends DecoratorBlockNode {
__name: string
__mimeType: string
static getType(): string {
return 'file-export'
}
constructor(name: string, mimeType: string) {
super()
this.__name = name
this.__mimeType = mimeType
}
static clone(node: FileExportNode): FileExportNode {
return new FileExportNode(node.__name, node.__mimeType)
}
static importJSON(serializedNode: SerializedFileExportNode): FileExportNode {
const node = new FileExportNode(serializedNode.name, serializedNode.mimeType)
return node
}
exportJSON(): SerializedFileExportNode {
return {
...super.exportJSON(),
name: this.__name,
mimeType: this.__mimeType,
version: 1,
type: 'file-export',
}
}
getZippableFileName(): string {
return parseAndCreateZippableFileName(this.__name)
}
getTextContent(): string {
return `${this.__mimeType.startsWith('image/') ? '!' : ''}[${this.__name}](./${this.getZippableFileName()})`
}
exportDOM(): DOMExportOutput {
const src = `./${this.getZippableFileName()}`
if (this.__mimeType.startsWith('image/')) {
const img = document.createElement('img')
img.setAttribute('src', src)
return { element: img }
} else if (this.__mimeType.startsWith('audio')) {
const audio = document.createElement('audio')
audio.setAttribute('controls', '')
const source = document.createElement('source')
source.setAttribute('src', src)
source.setAttribute('type', this.__mimeType)
audio.appendChild(source)
return { element: audio }
} else if (this.__mimeType.startsWith('video')) {
const video = document.createElement('video')
video.setAttribute('controls', '')
const source = document.createElement('source')
source.setAttribute('src', src)
source.setAttribute('type', this.__mimeType)
video.appendChild(source)
return { element: video }
}
const object = document.createElement('object')
object.setAttribute('data', src)
object.setAttribute('type', this.__mimeType)
return { element: object }
}
decorate(): JSX.Element {
// Doesn't need to actually render anything since this is only used for export
return <></>
}
}
export function $createFileExportNode(name: string, mimeType: string): FileExportNode {
return new FileExportNode(name, mimeType)
}
@@ -1,3 +1,5 @@
@import 'lists';
.Lexical__ltr {
text-align: left;
}
@@ -163,70 +165,6 @@
box-shadow: 0 0 5px rgba(0, 0, 0, 0.4);
border-radius: 3px;
}
.Lexical__tableAddColumns {
position: absolute;
top: 0;
width: 20px;
background-color: #eee;
height: 100%;
right: 0;
animation: table-controls 0.2s ease;
border: 0;
cursor: pointer;
}
.Lexical__tableAddColumns:after {
background-image: url(#{$blocks-editor-icons-path}/plus.svg);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
display: block;
content: ' ';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.4;
}
.Lexical__tableAddColumns:hover {
background-color: #c9dbf0;
}
.Lexical__tableAddRows {
position: absolute;
bottom: -25px;
width: calc(100% - 25px);
background-color: #eee;
height: 20px;
left: 0;
animation: table-controls 0.2s ease;
border: 0;
cursor: pointer;
}
.Lexical__tableAddRows:after {
background-image: url(#{$blocks-editor-icons-path}/plus.svg);
background-size: contain;
background-position: center;
background-repeat: no-repeat;
display: block;
content: ' ';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.4;
}
.Lexical__tableAddRows:hover {
background-color: #c9dbf0;
}
@keyframes table-controls {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.Lexical__tableCellResizeRuler {
display: block;
position: absolute;
@@ -261,118 +199,6 @@
display: inline;
background-color: #ffbbbb !important;
}
:root {
--lexical-ordered-list-left-margin: 16px;
}
.monospace-font {
--lexical-ordered-list-left-margin: 42px;
}
@for $i from 1 through 5 {
.Lexical__ol#{$i} {
padding: 0;
margin: 0;
margin-left: var(--lexical-ordered-list-left-margin);
list-style-position: outside;
&.Lexical__rtl {
margin-left: 0;
margin-right: var(--lexical-ordered-list-left-margin);
}
}
}
.Lexical__ol2 {
list-style-type: upper-alpha;
}
.Lexical__ol3 {
list-style-type: lower-alpha;
}
.Lexical__ol4 {
list-style-type: upper-roman;
}
.Lexical__ol5 {
list-style-type: lower-roman;
}
.Lexical__ul {
padding: 0;
margin: 0;
margin-left: 16px;
list-style-position: outside;
&.Lexical__rtl {
margin-left: 0;
margin-right: 16px;
}
}
.Lexical__listItem {
margin: 0 0px;
}
.Lexical__listItemChecked,
.Lexical__listItemUnchecked {
position: relative;
margin-left: 8px;
margin-right: 8px;
padding-left: 24px;
padding-right: 24px;
list-style-type: none;
outline: none;
vertical-align: middle;
}
.Lexical__listItemChecked {
text-decoration: line-through;
opacity: 0.4;
}
.Lexical__listItemUnchecked:before,
.Lexical__listItemChecked:before {
content: '';
width: 16px;
height: 16px;
left: 0;
top: 5px;
cursor: pointer;
background-size: cover;
position: absolute;
}
.Lexical__listItemUnchecked[dir='rtl']:before,
.Lexical__listItemChecked[dir='rtl']:before {
left: auto;
right: 0;
}
.Lexical__listItemUnchecked:focus:before,
.Lexical__listItemChecked:focus:before {
box-shadow: 0 0 0 2px #a6cdfe;
border-radius: 2px;
}
.Lexical__listItemUnchecked:before {
border: 1px solid #999;
border-radius: 2px;
}
.Lexical__listItemChecked:before {
border: 1px solid var(--sn-stylekit-info-color);
border-radius: 2px;
background-color: var(--sn-stylekit-info-color);
background-repeat: no-repeat;
}
.Lexical__listItemChecked:after {
content: '';
cursor: pointer;
border-color: var(--sn-stylekit-info-contrast-color);
border-style: solid;
position: absolute;
display: block;
top: 7px;
width: 5px;
left: 6px;
height: 10px;
transform: rotate(45deg);
border-width: 0 2px 2px 0;
}
.Lexical__nestedListItem {
list-style-type: none;
}
.Lexical__nestedListItem:before,
.Lexical__nestedListItem:after {
display: none;
}
.Lexical__tokenComment {
color: slategray;
}
@@ -0,0 +1,15 @@
.Lexical__listItemUnchecked,
.Lexical__listItemChecked {
min-height: 18px;
margin-bottom: 4px;
}
.Lexical__listItemUnchecked:before,
.Lexical__listItemChecked:before {
top: 0px;
}
.Lexical__listItemChecked:after {
top: 1px;
}
.Lexical__table {
margin: 10px 0;
}
@@ -1,117 +0,0 @@
.icon.plus {
background-image: url(#{$blocks-editor-icons-path}/plus.svg);
}
/**
.icon.dropdown-more {
background-image: url(#{$blocks-editor-icons-path}/dropdown-more.svg);
}
.icon.font-color {
background-image: url(#{$blocks-editor-icons-path}/font-color.svg);
}
.icon.font-family {
background-image: url(#{$blocks-editor-icons-path}/font-family.svg);
}
.icon.bg-color {
background-image: url(#{$blocks-editor-icons-path}/bg-color.svg);
}
i.palette {
background-image: url(#{$blocks-editor-icons-path}/palette.svg);
}
i.bucket {
background-image: url(#{$blocks-editor-icons-path}/paint-bucket.svg);
}
i.clear {
background-image: url(#{$blocks-editor-icons-path}/trash.svg);
}
i.image {
background-image: url(#{$blocks-editor-icons-path}/file-image.svg);
}
i.close {
background-image: url(#{$blocks-editor-icons-path}/close.svg);
}
i.figma {
background-image: url(#{$blocks-editor-icons-path}/figma.svg);
}
i.poll {
background-image: url(#{$blocks-editor-icons-path}/card-checklist.svg);
}
i.indent {
background-image: url(#{$blocks-editor-icons-path}/indent.svg);
}
i.markdown {
background-image: url(#{$blocks-editor-icons-path}/markdown.svg);
}
i.outdent {
background-image: url(#{$blocks-editor-icons-path}/outdent.svg);
}
i.undo {
background-image: url(#{$blocks-editor-icons-path}/arrow-counterclockwise.svg);
}
i.redo {
background-image: url(#{$blocks-editor-icons-path}/arrow-clockwise.svg);
}
i.sticky {
background-image: url(#{$blocks-editor-icons-path}/sticky.svg);
}
i.mic {
background-image: url(#{$blocks-editor-icons-path}/mic.svg);
}
i.import {
background-image: url(#{$blocks-editor-icons-path}/upload.svg);
}
i.export {
background-image: url(#{$blocks-editor-icons-path}/download.svg);
}
i.diagram-2 {
background-image: url(#{$blocks-editor-icons-path}/diagram-2.svg);
}
i.user {
background-image: url(#{$blocks-editor-icons-path}/user.svg);
}
i.equation {
background-image: url(#{$blocks-editor-icons-path}/plus-slash-minus.svg);
}
i.gif {
background-image: url(#{$blocks-editor-icons-path}/filetype-gif.svg);
}
i.copy {
background-image: url(#{$blocks-editor-icons-path}/copy.svg);
}
i.success {
background-image: url(#{$blocks-editor-icons-path}/success.svg);
}
i.prettier {
background-image: url(#{$blocks-editor-icons-path}/prettier.svg);
}
i.prettier-error {
background-image: url(#{$blocks-editor-icons-path}/prettier-error.svg);
}
*/
@@ -1,5 +1,3 @@
@import 'base';
@import 'custom';
@import 'editor';
@import 'icons';
@import 'links';
@@ -1,110 +0,0 @@
.button.active,
.toolbar .button.active {
background-color: var(--sn-stylekit-contrast-background-color);
}
.link-editor {
background-color: var(--sn-stylekit-secondary-background-color);
border-radius: 8px;
border: 0;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
display: absolute;
font-family: inherit;
font-size: 15px;
left: 0;
margin: 8px 12px;
max-width: 400px;
opacity: 0;
outline: 0;
padding: 8px 12px;
position: absolute;
top: 0;
transition: opacity 0.5s;
width: 100%;
will-change: transform;
z-index: 10;
}
.link-input {
background-color: var(--sn-stylekit-background-color);
border-radius: 15px;
border: 0;
box-sizing: border-box;
color: var(--sn-stylekit-contrast-color);
display: absolute;
font-family: inherit;
font-size: 15px;
margin: 8px 12px;
outline: 0;
padding: 8px 12px;
position: relative;
width: calc(100% - 24px);
}
.link-editor div.link-edit {
display: flex;
align-items: center;
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
vertical-align: -0.25em;
position: absolute;
right: 12px;
top: 0;
bottom: 0;
cursor: pointer;
}
.link-editor .link-input a {
color: rgb(33, 111, 219);
text-decoration: none;
display: block;
white-space: nowrap;
overflow: hidden;
margin-right: 30px;
text-overflow: ellipsis;
}
.link-editor .link-input a:hover {
text-decoration: underline;
}
.link-editor .font-size-wrapper,
.link-editor .font-family-wrapper {
display: flex;
margin: 0 4px;
}
.link-editor select {
padding: 6px;
border: none;
background-color: rgba(0, 0, 0, 0.075);
border-radius: 4px;
}
.link-editor .button {
width: 20px;
height: 20px;
display: inline-block;
padding: 6px;
border-radius: 8px;
cursor: pointer;
margin: 0 2px;
}
.link-editor .button.hovered {
width: 20px;
height: 20px;
display: inline-block;
background-color: #eee;
}
.link-editor .button i,
.actions i {
background-size: contain;
display: inline-block;
height: 20px;
width: 20px;
vertical-align: -0.25em;
}
@@ -0,0 +1,119 @@
:root {
--lexical-ordered-list-left-margin: 16px;
}
.monospace-font {
--lexical-ordered-list-left-margin: 42px;
}
@for $i from 1 through 5 {
.Lexical__ol#{$i} {
padding: 0;
margin: 0;
margin-left: var(--lexical-ordered-list-left-margin);
list-style-position: outside;
&.Lexical__rtl {
margin-left: 0;
margin-right: var(--lexical-ordered-list-left-margin);
}
}
}
.Lexical__ol2 {
list-style-type: upper-alpha;
}
.Lexical__ol3 {
list-style-type: lower-alpha;
}
.Lexical__ol4 {
list-style-type: upper-roman;
}
.Lexical__ol5 {
list-style-type: lower-roman;
}
.Lexical__ul {
padding: 0;
margin: 0;
margin-left: 16px;
list-style-position: outside;
&.Lexical__rtl {
margin-left: 0;
margin-right: 16px;
}
}
.Lexical__checkList {
margin-left: 0;
.Lexical__nestedListItem & {
margin-left: 16px;
}
}
.Lexical__listItem {
margin: 0 0px;
}
.Lexical__listItemChecked,
.Lexical__listItemUnchecked {
position: relative;
padding-left: 24px;
padding-right: 24px;
list-style-type: none;
outline: none;
vertical-align: middle;
}
.Lexical__listItemChecked {
text-decoration: line-through;
opacity: 0.4;
}
.Lexical__listItemUnchecked:before,
.Lexical__listItemChecked:before {
content: '';
width: 16px;
height: 16px;
left: 0;
top: 7px;
cursor: pointer;
background-size: cover;
position: absolute;
}
.Lexical__listItemUnchecked[dir='rtl']:before,
.Lexical__listItemChecked[dir='rtl']:before {
left: auto;
right: 0;
}
.Lexical__listItemUnchecked:focus:before,
.Lexical__listItemChecked:focus:before {
box-shadow: 0 0 0 2px #a6cdfe;
border-radius: 2px;
}
.Lexical__listItemUnchecked:before {
border: 1px solid #999;
border-radius: 2px;
}
.Lexical__listItemChecked:before {
border: 1px solid var(--sn-stylekit-info-color);
border-radius: 2px;
background-color: var(--sn-stylekit-info-color);
background-repeat: no-repeat;
}
.Lexical__listItemChecked:after {
content: '';
cursor: pointer;
border-color: var(--sn-stylekit-info-contrast-color);
border-style: solid;
position: absolute;
display: block;
top: 9px;
width: 5px;
left: 6px;
height: 10px;
transform: rotate(45deg);
border-width: 0 2px 2px 0;
}
.Lexical__nestedListItem {
list-style-type: none;
&.Lexical__listItemUnchecked {
padding-left: 0;
}
}
.Lexical__nestedListItem:before,
.Lexical__nestedListItem:after {
display: none;
}
@@ -1,111 +0,0 @@
import { useApplication } from '@/Components/ApplicationProvider'
import { downloadBlobOnAndroid } from '@/NativeMobileWeb/DownloadBlobOnAndroid'
import { shareBlobOnMobile } from '@/NativeMobileWeb/ShareBlobOnMobile'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { Platform } from '@standardnotes/snjs'
import {
sanitizeFileName,
SUPER_EXPORT_HTML,
SUPER_EXPORT_JSON,
SUPER_EXPORT_MARKDOWN,
} from '@standardnotes/ui-services'
import { useCallback, useEffect, useRef } from 'react'
import { useCommandService } from '@/Components/CommandProvider'
import { HeadlessSuperConverter } from '../../Tools/HeadlessSuperConverter'
export const ExportPlugin = () => {
const application = useApplication()
const [editor] = useLexicalComposerContext()
const commandService = useCommandService()
const converter = useRef(new HeadlessSuperConverter())
const downloadData = useCallback(
(data: Blob, fileName: string) => {
if (!application.isNativeMobileWeb()) {
application.archiveService.downloadData(data, fileName)
return
}
if (application.platform === Platform.Android) {
downloadBlobOnAndroid(application.mobileDevice, data, fileName).catch(console.error)
} else {
shareBlobOnMobile(application.mobileDevice, application.isNativeMobileWeb(), data, fileName).catch(
console.error,
)
}
},
[application],
)
const exportJson = useCallback(
(title: string) => {
const content = converter.current.convertSuperStringToOtherFormat(JSON.stringify(editor.getEditorState()), 'json')
const blob = new Blob([content], { type: 'application/json' })
downloadData(blob, `${sanitizeFileName(title)}.json`)
},
[downloadData, editor],
)
const exportMarkdown = useCallback(
(title: string) => {
const content = converter.current.convertSuperStringToOtherFormat(JSON.stringify(editor.getEditorState()), 'md')
const blob = new Blob([content], { type: 'text/markdown' })
downloadData(blob, `${sanitizeFileName(title)}.md`)
},
[downloadData, editor],
)
const exportHtml = useCallback(
(title: string) => {
const content = converter.current.convertSuperStringToOtherFormat(JSON.stringify(editor.getEditorState()), 'html')
const blob = new Blob([content], { type: 'text/html' })
downloadData(blob, `${sanitizeFileName(title)}.html`)
},
[downloadData, editor],
)
useEffect(() => {
return commandService.addCommandHandler({
command: SUPER_EXPORT_JSON,
onKeyDown: (_, data) => {
if (!data) {
throw new Error('No data provided for export command')
}
const title = data as string
exportJson(title)
},
})
}, [commandService, exportJson])
useEffect(() => {
return commandService.addCommandHandler({
command: SUPER_EXPORT_MARKDOWN,
onKeyDown: (_, data) => {
if (!data) {
throw new Error('No data provided for export command')
}
const title = data as string
exportMarkdown(title)
},
})
}, [commandService, exportMarkdown])
useEffect(() => {
return commandService.addCommandHandler({
command: SUPER_EXPORT_HTML,
onKeyDown: (_, data) => {
if (!data) {
throw new Error('No data provided for export command')
}
const title = data as string
exportHtml(title)
},
})
}, [commandService, exportHtml])
return null
}
@@ -148,6 +148,10 @@ export class InlineFileNode extends DecoratorBlockNode {
return { element: object }
}
getTextContent(): string {
return `${this.__mimeType.startsWith('image/') ? '!' : ''}[${this.__fileName}](${this.__src})`
}
decorate(_editor: LexicalEditor, config: EditorConfig): JSX.Element {
const embedBlockTheme = config.theme.embedBlock || {}
const className = {
@@ -0,0 +1,25 @@
import { CheckListPlugin as LexicalCheckListPlugin } from '@lexical/react/LexicalCheckListPlugin'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { ListNode } from '@lexical/list'
import { useEffect } from 'react'
export function CheckListPlugin() {
const [editor] = useLexicalComposerContext()
useEffect(() => {
return editor.registerNodeTransform(ListNode, (node) => {
if (node.getListType() !== 'check') {
return
}
editor.getEditorState().read(() => {
const element = editor.getElementByKey(node.getKey())
if (!element) {
return
}
element.classList.add('Lexical__checkList')
})
})
}, [editor])
return <LexicalCheckListPlugin />
}
@@ -0,0 +1,38 @@
import { ListNode, SerializedListNode } from '@lexical/list'
import { DOMExportOutput, LexicalEditor, Spread } from 'lexical'
export type SerializedListHTMLExportNode = Spread<
{
type: 'list-html-export'
},
SerializedListNode
>
export class ListHTMLExportNode extends ListNode {
static getType(): string {
return 'list-html-export'
}
static clone(node: ListNode): ListHTMLExportNode {
return new ListHTMLExportNode(node.getListType(), node.getStart(), node.getKey())
}
static importJSON(serializedNode: SerializedListNode): ListNode {
return super.importJSON(serializedNode)
}
exportDOM(editor: LexicalEditor): DOMExportOutput {
const { element } = super.exportDOM(editor)
if (this.getListType() === 'check' && element instanceof HTMLElement) {
element.classList.add('Lexical__checkList')
}
return { element }
}
exportJSON(): SerializedListHTMLExportNode {
return {
...super.exportJSON(),
type: 'list-html-export',
}
}
}
@@ -366,6 +366,10 @@ const ToolbarPlugin = () => {
}, [])
useEffect(() => {
if (isMobile) {
return
}
const scrollerElem = activeEditor.getRootElement()
const update = () => {
@@ -386,7 +390,7 @@ const ToolbarPlugin = () => {
scrollerElem.removeEventListener('scroll', debouncedUpdate)
}
}
}, [activeEditor, updateToolbarFloatingPosition])
}, [activeEditor, isMobile, updateToolbarFloatingPosition])
useEffect(() => {
return mergeRegister(
@@ -37,7 +37,6 @@ import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
import { useCommandService } from '@/Components/CommandProvider'
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
import { ExportPlugin } from './Plugins/ExportPlugin/ExportPlugin'
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
import ReadonlyPlugin from './Plugins/ReadonlyPlugin/ReadonlyPlugin'
@@ -243,7 +242,6 @@ export const SuperEditor: FunctionComponent<Props> = ({
/>
<NodeObserverPlugin nodeType={BubbleNode} onRemove={handleBubbleRemove} />
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
<ExportPlugin />
{readonly === undefined && <ReadonlyPlugin note={note.current} />}
<AutoFocusPlugin isEnabled={controller.isTemplateNote} />
<SuperSearchContextProvider>
@@ -6,7 +6,7 @@ import {
isUIFeatureAnIframeFeature,
spaceSeparatedStrings,
} from '@standardnotes/snjs'
import { useCallback, useEffect, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useApplication } from '../ApplicationProvider'
import IframeFeatureView from '../ComponentView/IframeFeatureView'
import Icon from '../Icon/Icon'
@@ -52,18 +52,23 @@ const SuperNoteConverter = ({
return 'json'
}, [uiFeature])
const convertedContent = useMemo(() => {
if (note.text.length === 0) {
const [convertedContent, setConvertedContent] = useState<string>('')
useEffect(() => {
const convertContent = async () => {
if (note.text.length === 0) {
return note.text
}
try {
return new HeadlessSuperConverter().convertSuperStringToOtherFormat(note.text, format)
} catch (error) {
console.error(error)
}
return note.text
}
try {
return new HeadlessSuperConverter().convertSuperStringToOtherFormat(note.text, format)
} catch (error) {
console.error(error)
}
return note.text
convertContent().then(setConvertedContent).catch(console.error)
}, [format, note])
const componentViewer = useMemo(() => {
@@ -1,6 +1,6 @@
import { createHeadlessEditor } from '@lexical/headless'
import { $convertToMarkdownString, $convertFromMarkdownString } from '@lexical/markdown'
import { SuperConverterServiceInterface } from '@standardnotes/snjs'
import { FileItem, PrefKey, PrefValue, SuperConverterServiceInterface } from '@standardnotes/snjs'
import {
$createParagraphNode,
$getRoot,
@@ -11,10 +11,12 @@ import {
ParagraphNode,
} from 'lexical'
import BlocksEditorTheme from '../Lexical/Theme/Theme'
import { BlockEditorNodes } from '../Lexical/Nodes/AllNodes'
import { SuperExportNodes } from '../Lexical/Nodes/AllNodes'
import { MarkdownTransformers } from '../MarkdownTransformers'
import { $generateHtmlFromNodes, $generateNodesFromDOM } from '@lexical/html'
import { FileNode } from '../Plugins/EncryptedFilePlugin/Nodes/FileNode'
import { $createFileExportNode } from '../Lexical/Nodes/FileExportNode'
import { $createInlineFileNode } from '../Plugins/InlineFilePlugin/InlineFileNode'
export class HeadlessSuperConverter implements SuperConverterServiceInterface {
private editor: LexicalEditor
@@ -24,7 +26,7 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
theme: BlocksEditorTheme,
editable: false,
onError: (error: Error) => console.error(error),
nodes: [...BlockEditorNodes],
nodes: SuperExportNodes,
})
}
@@ -37,15 +39,80 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
}
}
convertSuperStringToOtherFormat(superString: string, toFormat: 'txt' | 'md' | 'html' | 'json'): string {
async convertSuperStringToOtherFormat(
superString: string,
toFormat: 'txt' | 'md' | 'html' | 'json',
config?: {
embedBehavior?: PrefValue[PrefKey.SuperNoteExportEmbedBehavior]
getFileItem?: (id: string) => FileItem | undefined
getFileBase64?: (id: string) => Promise<string | undefined>
},
): Promise<string> {
if (superString.length === 0) {
return superString
}
const { embedBehavior, getFileItem, getFileBase64 } = config ?? { embedBehavior: 'reference' }
if (embedBehavior === 'separate' && !getFileItem) {
throw new Error('getFileItem must be provided when embedBehavior is "separate"')
}
if (embedBehavior === 'inline' && !getFileItem && !getFileBase64) {
throw new Error('getFileItem and getFileBase64 must be provided when embedBehavior is "inline"')
}
this.editor.setEditorState(this.editor.parseEditorState(superString))
let content: string | undefined
await new Promise<void>((resolve) => {
this.editor.update(
() => {
if (embedBehavior === 'reference') {
resolve()
return
}
if (!getFileItem) {
resolve()
return
}
const fileNodes = $nodesOfType(FileNode)
Promise.all(
fileNodes.map(async (fileNode) => {
const fileItem = getFileItem(fileNode.getId())
if (!fileItem) {
return
}
if (embedBehavior === 'inline' && getFileBase64) {
const fileBase64 = await getFileBase64(fileNode.getId())
if (!fileBase64) {
return
}
this.editor.update(
() => {
const inlineFileNode = $createInlineFileNode(fileBase64, fileItem.mimeType, fileItem.name)
fileNode.replace(inlineFileNode)
},
{ discrete: true },
)
} else {
this.editor.update(
() => {
const fileExportNode = $createFileExportNode(fileItem.name, fileItem.mimeType)
fileNode.replace(fileExportNode)
},
{ discrete: true },
)
}
}),
)
.then(() => resolve())
.catch(console.error)
},
{ discrete: true },
)
})
this.editor.update(
() => {
switch (toFormat) {
@@ -159,4 +226,23 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
return JSON.stringify(this.editor.getEditorState())
}
getEmbeddedFileIDsFromSuperString(superString: string): string[] {
if (superString.length === 0) {
return []
}
this.editor.setEditorState(this.editor.parseEditorState(superString))
const ids: string[] = []
this.editor.getEditorState().read(() => {
const fileNodes = $nodesOfType(FileNode)
fileNodes.forEach((fileNode) => {
ids.push(fileNode.getId())
})
})
return ids
}
}
@@ -8,6 +8,7 @@ const Switch = ({
disabled = false,
tabIndex,
forceDesktopStyle,
children,
}: {
checked: boolean
onChange: (checked: boolean) => void
@@ -15,20 +16,12 @@ const Switch = ({
disabled?: boolean
tabIndex?: number
forceDesktopStyle?: boolean
children?: React.ReactNode
}) => {
const isActive = checked && !disabled
return (
<label
className={classNames(
'relative box-content inline-block flex-shrink-0 cursor-pointer rounded-full border-2 border-solid border-transparent bg-clip-padding transition-colors duration-150 ease-out',
'ring-2 ring-transparent focus-within:border-default focus-within:shadow-none focus-within:outline-none focus-within:ring-info',
disabled ? 'opacity-50' : '',
isActive ? 'bg-info' : 'bg-neutral',
forceDesktopStyle ? 'h-4.5 w-8' : 'h-7 w-12 md:h-4.5 md:w-8',
className,
)}
>
<label className={classNames(disabled ? 'opacity-50' : '', className)}>
<VisuallyHidden>
<Checkbox
checked={checked}
@@ -40,15 +33,25 @@ const Switch = ({
</VisuallyHidden>
<div
className={classNames(
'absolute top-1/2 block -translate-y-1/2 rounded-full bg-default transition-transform duration-150 ease-out',
forceDesktopStyle ? 'left-[2px] h-3.5 w-3.5' : 'left-[0.15rem] h-6 w-6 md:left-[2px] md:h-3.5 md:w-3.5',
checked
? forceDesktopStyle
? 'translate-x-[calc(2rem-1.125rem)]'
: 'translate-x-[calc(3.25rem-1.5rem-0.5rem)] md:translate-x-[calc(2rem-1.125rem)]'
: '',
'relative box-content inline-block flex-shrink-0 cursor-pointer rounded-full border-2 border-solid border-transparent bg-clip-padding transition-colors duration-150 ease-out',
'ring-2 ring-transparent focus-within:border-default focus-within:shadow-none focus-within:outline-none focus-within:ring-info',
isActive ? 'bg-info' : 'bg-neutral',
forceDesktopStyle ? 'h-4.5 w-8' : 'h-7 w-12 md:h-4.5 md:w-8',
)}
/>
>
<div
className={classNames(
'absolute top-1/2 block -translate-y-1/2 rounded-full bg-default transition-transform duration-150 ease-out',
forceDesktopStyle ? 'left-[2px] h-3.5 w-3.5' : 'left-[0.15rem] h-6 w-6 md:left-[2px] md:h-3.5 md:w-3.5',
checked
? forceDesktopStyle
? 'translate-x-[calc(2rem-1.125rem)]'
: 'translate-x-[calc(3.25rem-1.5rem-0.5rem)] md:translate-x-[calc(2rem-1.125rem)]'
: '',
)}
/>
</div>
{children}
</label>
)
}
@@ -115,7 +115,7 @@ const SmartViewsListItem: FunctionComponent<Props> = ({ view, tagsState, setEdit
<div
role="button"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames('tag group px-3.5', isSelected && 'selected', isFaded && 'opacity-50')}
className={classNames('tag group px-3.5 py-1 md:py-0', isSelected && 'selected', isFaded && 'opacity-50')}
onClick={selectCurrentTag}
onContextMenu={(event) => {
event.preventDefault()
@@ -30,7 +30,9 @@ const TagsList: FunctionComponent<Props> = ({ type }: Props) => {
const onContextMenu = useCallback(
(tag: SNTag, posX: number, posY: number) => {
void application.navigationController.setSelectedTag(tag, type)
if (application.navigationController.selected !== tag) {
void application.navigationController.setSelectedTag(tag, type)
}
openTagContextMenu(posX, posY)
},
[application, openTagContextMenu, type],
@@ -260,7 +260,11 @@ export const TagsListItem: FunctionComponent<Props> = observer(
<div
role="button"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
className={classNames('tag group px-3.5', isSelected && 'selected', isBeingDraggedOver && 'is-drag-over')}
className={classNames(
'tag group px-3.5 py-1 md:py-0',
isSelected && 'selected',
isBeingDraggedOver && 'is-drag-over',
)}
onClick={selectCurrentTag}
ref={tagRef}
style={{
@@ -8,6 +8,7 @@ export const ElementIds = {
NavigationColumn: 'navigation',
NoteTextEditor: 'note-text-editor',
NoteTitleEditor: 'note-title-editor',
NoteOptionsButton: 'note-options-button',
RootId: 'app-group-root',
NoteStatusTooltip: 'note-status-tooltip',
ItemLinkAutocompleteInput: 'item-link-autocomplete-input',
@@ -275,6 +275,20 @@ export class FilesController extends AbstractViewController<FilesControllerEvent
}
}
getFileBlob = async (file: FileItem): Promise<Blob | undefined> => {
const chunks: Uint8Array[] = []
const error = await this.files.downloadFile(file, async (decryptedChunk) => {
chunks.push(decryptedChunk)
})
if (error) {
return
}
const finalDecryptedBytes = concatenateUint8Arrays(chunks)
return new Blob([finalDecryptedBytes], {
type: file.mimeType,
})
}
private async downloadFile(file: FileItem): Promise<void> {
let downloadingToastId = ''
@@ -101,6 +101,8 @@ export class ItemListController
selectedUuids: Set<UuidString> = observable(new Set<UuidString>())
selectedItems: Record<UuidString, ListableContentItem> = {}
isMultipleSelectionMode = false
override deinit() {
super.deinit()
;(this.noteFilterText as unknown) = undefined
@@ -169,6 +171,10 @@ export class ItemListController
setSelectedItems: action,
hydrateFromPersistedValue: action,
isMultipleSelectionMode: observable,
enableMultipleSelectionMode: action,
cancelMultipleSelection: action,
})
eventBus.addEventHandler(this, CrossControllerEvent.TagChanged)
@@ -257,6 +263,19 @@ export class ItemListController
),
)
this.disposers.push(
reaction(
() => this.selectedItemsCount,
(count, prevCount) => {
const hasNoSelectedItem = count === 0
const onlyOneSelectedItemAfterChange = prevCount > count && count === 1
if (hasNoSelectedItem || onlyOneSelectedItemAfterChange) {
this.cancelMultipleSelection()
}
},
),
)
window.onresize = () => {
this.resetPagination(true)
}
@@ -1079,6 +1098,9 @@ export class ItemListController
runInAction(() => {
this.setSelectedUuids(this.selectedUuids.add(item.uuid))
this.lastSelectedItem = item
if (this.selectedItemsCount > 1 && !this.isMultipleSelectionMode) {
this.enableMultipleSelectionMode()
}
})
}
}
@@ -1086,6 +1108,8 @@ export class ItemListController
cancelMultipleSelection = () => {
this.keyboardService.cancelAllKeyboardModifiers()
this.isMultipleSelectionMode = false
const firstSelectedItem = this.firstSelectedItem
if (firstSelectedItem) {
@@ -1095,7 +1119,7 @@ export class ItemListController
}
}
private replaceSelection = (item: ListableContentItem): void => {
replaceSelection = (item: ListableContentItem): void => {
this.deselectAll()
runInAction(() => this.setSelectedUuids(this.selectedUuids.add(item.uuid)))
@@ -1103,10 +1127,11 @@ export class ItemListController
}
selectAll = () => {
void this.selectItemsRange({
startingIndex: 0,
endingIndex: this.listLength - 1,
})
const allItems = this.items.filter((item) => !item.protected)
const lastItem = allItems[allItems.length - 1]
this.setSelectedUuids(new Set(Uuids(allItems)))
this.lastSelectedItem = lastItem
this.enableMultipleSelectionMode()
}
deselectAll = (): void => {
@@ -1136,6 +1161,10 @@ export class ItemListController
}
}
enableMultipleSelectionMode = () => {
this.isMultipleSelectionMode = true
}
selectItem = async (
uuid: UuidString,
userTriggered?: boolean,
@@ -1152,14 +1181,13 @@ export class ItemListController
log(LoggingDomain.Selection, 'Select item', item.uuid)
const supportsMultipleSelection = this.options.allowMultipleSelection
const hasMeta = this.keyboardService.activeModifiers.has(KeyboardModifier.Meta)
const hasCtrl = this.keyboardService.activeModifiers.has(KeyboardModifier.Ctrl)
const hasShift = this.keyboardService.activeModifiers.has(KeyboardModifier.Shift)
const hasMoreThanOneSelected = this.selectedItemsCount > 1
const isAuthorizedForAccess = await this.protections.authorizeItemAccess(item)
if (supportsMultipleSelection && userTriggered && (hasMeta || hasCtrl)) {
if (userTriggered && hasShift) {
await this.selectItemsRange({ selectedItem: item })
} else if (userTriggered && this.isMultipleSelectionMode) {
if (this.selectedUuids.has(uuid) && hasMoreThanOneSelected) {
this.removeSelectedItem(uuid)
} else if (isAuthorizedForAccess) {
@@ -1167,17 +1195,14 @@ export class ItemListController
this.setSelectedUuids(this.selectedUuids)
this.lastSelectedItem = item
}
} else if (supportsMultipleSelection && userTriggered && hasShift) {
await this.selectItemsRange({ selectedItem: item })
} else {
const shouldSelectNote = hasMoreThanOneSelected || !this.selectedUuids.has(uuid)
if (shouldSelectNote && isAuthorizedForAccess) {
this.replaceSelection(item)
await this.openSingleSelectedItem({ userTriggered: userTriggered ?? false })
}
}
await this.openSingleSelectedItem({ userTriggered: userTriggered ?? false })
return {
didSelect: this.selectedUuids.has(uuid),
}
@@ -609,7 +609,9 @@ export class NavigationController
public setEditingTag(editingTag: SNTag | SmartView | undefined) {
runInAction(() => {
this.editing_ = editingTag
void this.setSelectedTag(editingTag, this.selectedLocation || 'all')
if (this.selected !== editingTag) {
void this.setSelectedTag(editingTag, this.selectedLocation || 'all')
}
})
}
@@ -293,6 +293,17 @@ export class NotesController
})
}
async toggleArchiveSelectedNotes(): Promise<void> {
const notes = this.selectedNotes
const archived = notes.some((note) => note.archived)
if (!archived) {
await this.setArchiveSelectedNotes(true)
} else {
await this.setArchiveSelectedNotes(false)
}
}
async setProtectSelectedNotes(protect: boolean): Promise<void> {
const selectedNotes = this.getSelectedNotesList()
if (protect) {
@@ -1,32 +0,0 @@
import { WebApplication } from '@/Application/WebApplication'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { parseFileName } from '@standardnotes/filepicker'
import { Platform, SNNote } from '@standardnotes/snjs'
import { sanitizeFileName } from '@standardnotes/ui-services'
import { downloadBlobOnAndroid } from './DownloadBlobOnAndroid'
export const downloadSelectedNotesOnAndroid = async (application: WebApplication, notes: SNNote[]) => {
if (!application.isNativeMobileWeb() || application.platform !== Platform.Android) {
throw new Error('Function being used on non-android platform')
}
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
const { name, ext } = parseFileName(getNoteFileName(application, note))
const filename = `${sanitizeFileName(name)}.${ext}`
await downloadBlobOnAndroid(application.mobileDevice, blob, filename)
return
}
if (notes.length > 1) {
const zippedDataBlob = await application.archiveService.zipData(
notes.map((note) => {
return {
name: getNoteFileName(application, note),
content: getNoteBlob(application, note),
}
}),
)
const filename = `Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`
await downloadBlobOnAndroid(application.mobileDevice, zippedDataBlob, filename)
}
}
@@ -1,36 +0,0 @@
import { WebApplication } from '@/Application/WebApplication'
import { getNoteBlob, getNoteFileName } from '@/Utils/NoteExportUtils'
import { parseFileName } from '@standardnotes/filepicker'
import { SNNote } from '@standardnotes/snjs'
import { sanitizeFileName } from '@standardnotes/ui-services'
import { shareBlobOnMobile } from './ShareBlobOnMobile'
export const shareSelectedNotes = async (application: WebApplication, notes: SNNote[]) => {
if (!application.isNativeMobileWeb()) {
throw new Error('Share function being used outside mobile webview')
}
if (notes.length === 1) {
const note = notes[0]
const blob = getNoteBlob(application, note)
const { name, ext } = parseFileName(getNoteFileName(application, note))
const filename = `${sanitizeFileName(name)}.${ext}`
void shareBlobOnMobile(application.mobileDevice, application.isNativeMobileWeb(), blob, filename)
return
}
if (notes.length > 1) {
const zippedDataBlob = await application.archiveService.zipData(
notes.map((note) => {
return {
name: getNoteFileName(application, note),
content: getNoteBlob(application, note),
}
}),
)
void shareBlobOnMobile(
application.mobileDevice,
application.isNativeMobileWeb(),
zippedDataBlob,
`Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`,
)
}
}
@@ -1,6 +1,8 @@
import { WebApplication } from '@/Application/WebApplication'
import { HeadlessSuperConverter } from '@/Components/SuperEditor/Tools/HeadlessSuperConverter'
import { NoteType, PrefKey, SNNote, PrefDefaults } from '@standardnotes/snjs'
import { WebApplicationInterface } from '@standardnotes/ui-services'
import { NoteType, PrefKey, SNNote, PrefDefaults, FileItem, PrefValue, pluralize } from '@standardnotes/snjs'
import { WebApplicationInterface, parseAndCreateZippableFileName, sanitizeFileName } from '@standardnotes/ui-services'
import { ZipDirectoryEntry } from '@zip.js/zip.js'
export const getNoteFormat = (application: WebApplicationInterface, note: SNNote) => {
if (note.noteType === NoteType.Super) {
@@ -21,7 +23,49 @@ export const getNoteFileName = (application: WebApplicationInterface, note: SNNo
return `${note.title}.${format}`
}
export const getNoteBlob = (application: WebApplicationInterface, note: SNNote) => {
const headlessSuperConverter = new HeadlessSuperConverter()
// @ts-expect-error Using inline loaders to load CSS as string
import superEditorCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/editor.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import snColorsCSS from '!css-loader!sass-loader!@standardnotes/styles/src/Styles/_colors.scss'
// @ts-expect-error Using inline loaders to load CSS as string
import exportOverridesCSS from '!css-loader!sass-loader!../Components/SuperEditor/Lexical/Theme/export-overrides.scss'
import { getBase64FromBlob } from './Utils'
import { ToastType, addToast, dismissToast } from '@standardnotes/toast'
const superHTML = (note: SNNote, content: string) => `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>${note.title}</title>
<style>
${snColorsCSS.toString()}
${superEditorCSS.toString()}
${exportOverridesCSS.toString()}
</style>
</head>
<body>
${content}
</body>
</html>
`
const superMarkdown = (note: SNNote, content: string) => `---
title: ${note.title}
created_at: ${note.created_at.toISOString()}
updated_at: ${note.serverUpdatedAt.toISOString()}
uuid: ${note.uuid}
---
${content}
`
export const getNoteBlob = async (
application: WebApplication,
note: SNNote,
superEmbedBehavior: PrefValue[PrefKey.SuperNoteExportEmbedBehavior],
) => {
const format = getNoteFormat(application, note)
let type: string
switch (format) {
@@ -38,12 +82,162 @@ export const getNoteBlob = (application: WebApplicationInterface, note: SNNote)
type = 'text/plain'
break
}
const content =
note.noteType === NoteType.Super
? new HeadlessSuperConverter().convertSuperStringToOtherFormat(note.text, format)
: note.text
const blob = new Blob([content], {
if (note.noteType === NoteType.Super) {
const content = await headlessSuperConverter.convertSuperStringToOtherFormat(note.text, format, {
embedBehavior: superEmbedBehavior,
getFileItem: (id) => application.items.findItem<FileItem>(id),
getFileBase64: async (id) => {
const fileItem = application.items.findItem<FileItem>(id)
if (!fileItem) {
return
}
const fileBlob = await application.filesController.getFileBlob(fileItem)
if (!fileBlob) {
return
}
return await getBase64FromBlob(fileBlob)
},
})
const useMDFrontmatter =
format === 'md' &&
application.getPreference(
PrefKey.SuperNoteExportUseMDFrontmatter,
PrefDefaults[PrefKey.SuperNoteExportUseMDFrontmatter],
)
const result =
format === 'html' ? superHTML(note, content) : useMDFrontmatter ? superMarkdown(note, content) : content
const blob = new Blob([result], {
type,
})
return blob
}
const blob = new Blob([note.text], {
type,
})
return blob
}
const isSuperNote = (note: SNNote) => {
return note.noteType === NoteType.Super
}
const noteHasEmbeddedFiles = (note: SNNote) => {
return note.text.includes('"type":"snfile"')
}
const noteRequiresFolder = (
note: SNNote,
superExportFormat: PrefValue[PrefKey.SuperNoteExportFormat],
superEmbedBehavior: PrefValue[PrefKey.SuperNoteExportEmbedBehavior],
) => {
if (!isSuperNote(note)) {
return false
}
if (superExportFormat === 'json') {
return false
}
if (superEmbedBehavior !== 'separate') {
return false
}
return noteHasEmbeddedFiles(note)
}
const addEmbeddedFilesToFolder = async (application: WebApplication, note: SNNote, folder: ZipDirectoryEntry) => {
try {
const embeddedFileIDs = headlessSuperConverter.getEmbeddedFileIDsFromSuperString(note.text)
for (const embeddedFileID of embeddedFileIDs) {
const fileItem = application.items.findItem<FileItem>(embeddedFileID)
if (!fileItem) {
continue
}
const embeddedFileBlob = await application.filesController.getFileBlob(fileItem)
if (!embeddedFileBlob) {
continue
}
folder.addBlob(parseAndCreateZippableFileName(fileItem.title), embeddedFileBlob)
}
} catch (error) {
console.error(error)
}
}
export const createNoteExport = async (
application: WebApplication,
notes: SNNote[],
): Promise<
| {
blob: Blob
fileName: string
}
| undefined
> => {
if (notes.length === 0) {
return
}
const toast = addToast({
type: ToastType.Progress,
message: `Exporting ${notes.length} ${pluralize(notes.length, 'note', 'notes')}...`,
})
const superExportFormatPref = application.getPreference(
PrefKey.SuperNoteExportFormat,
PrefDefaults[PrefKey.SuperNoteExportFormat],
)
const superEmbedBehaviorPref = application.getPreference(
PrefKey.SuperNoteExportEmbedBehavior,
PrefDefaults[PrefKey.SuperNoteExportEmbedBehavior],
)
if (notes.length === 1 && !noteRequiresFolder(notes[0], superExportFormatPref, superEmbedBehaviorPref)) {
const blob = await getNoteBlob(application, notes[0], superEmbedBehaviorPref)
const fileName = getNoteFileName(application, notes[0])
dismissToast(toast)
return {
blob,
fileName,
}
}
const zip = await import('@zip.js/zip.js')
const zipFS = new zip.fs.FS()
const { root } = zipFS
if (notes.length === 1 && noteRequiresFolder(notes[0], superExportFormatPref, superEmbedBehaviorPref)) {
const blob = await getNoteBlob(application, notes[0], superEmbedBehaviorPref)
const fileName = parseAndCreateZippableFileName(getNoteFileName(application, notes[0]))
root.addBlob(fileName, blob)
await addEmbeddedFilesToFolder(application, notes[0], root)
const zippedBlob = await zipFS.exportBlob()
dismissToast(toast)
return {
blob: zippedBlob,
fileName: fileName + '.zip',
}
}
for (const note of notes) {
const blob = await getNoteBlob(application, note, superEmbedBehaviorPref)
const fileName = parseAndCreateZippableFileName(getNoteFileName(application, note))
if (!noteRequiresFolder(note, superExportFormatPref, superEmbedBehaviorPref)) {
root.addBlob(fileName, blob)
continue
}
const folder = root.addDirectory(sanitizeFileName(note.title))
folder.addBlob(fileName, blob)
await addEmbeddedFilesToFolder(application, note, folder)
}
const zippedBlob = await zipFS.exportBlob()
dismissToast(toast)
return {
blob: zippedBlob,
fileName: `Standard Notes Export - ${application.archiveService.formattedDateForExports()}.zip`,
}
}
@@ -1,5 +1,3 @@
$blocks-editor-icons-path: '../javascripts/Components/SuperEditor/Lexical/Icons';
@import '../../../styles/src/Styles/_colors.scss';
@import '../../../styles/src/Styles/_panels.scss';
@import '../../../styles/src/Styles/_scrollbar.scss';

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