Compare commits

...
Author SHA1 Message Date
StandardNotes CI 7b9341257c chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-06 14:46:00 +00:00
Aman Harwara ef541cf70a chore: fix account menu font size 2023-04-06 19:48:12 +05:30
StandardNotes CI 4fccc55f25 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-06 11:46:12 +00:00
Aman Harwara 6fea8a3095 chore: update some lexical plugins (#2297) 2023-04-06 16:45:28 +05:30
StandardNotes CI 6fcde388d6 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-06 09:38:29 +00:00
Aman Harwara 616c700b7f chore: fix features e2e test (#2296) 2023-04-06 14:37:50 +05:30
Aman Harwara 41a5b1415a refactor: update roles immediately without waiting for full sync (#2294) 2023-04-05 21:34:06 +05:30
StandardNotes CI b3556ef8d2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-03 16:48:33 +00:00
Mo de8d44f507 chore: android well-known (skip e2e) 2023-04-03 11:30:44 -05:00
StandardNotes CI daf5071706 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-03 16:17:52 +00:00
Mo 67a2627a0d chore: well-known (skip e2e) 2023-04-03 10:59:04 -05:00
StandardNotes CI ba7d0cf4c1 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-03 14:39:20 +00:00
Mo ea5bec4a23 chore: webpack (skip e2e) 2023-04-03 09:25:21 -05:00
Mo 031b0a496f chore: disable spellcheck on input fields 2023-04-03 09:01:44 -05:00
StandardNotes CI 36d136a4d1 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-04-01 09:52:11 +00:00
Aman Harwara f884e2cd66 chore: update collapsible node 2023-04-01 14:53:22 +05:30
StandardNotes CI fbb0dc0cea chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-03-31 14:30:16 +00:00
Mo c50e29db31 chore: improve email toggle copy 2023-03-31 08:55:19 -05:00
StandardNotes CI 541adcf8f8 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-03-31 10:49:10 +00:00
Aman Harwara c832371e3a chore: fix remote image component 2023-03-31 15:49:06 +05:30
StandardNotes CI c3d3633e6b chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].7
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-03-30 07:42:56 +00:00
Aman Harwara 7773becdec fix: Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal 2023-03-30 12:40:50 +05:30
Aman Harwara 860746d4e2 Merge branch 'main' of github.com:standardnotes/app 2023-03-30 12:19:24 +05:30
Aman Harwara 09fd65903b fix: Files uploaded to a protected Super note will also be marked as protected 2023-03-30 12:19:15 +05:30
StandardNotes CI b57d16dd68 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected].6
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-03-29 19:34:21 +00:00
Aman Harwara 08a20e25b4 fix: Added backspace button on Super toolbar on Android as a workaround for backspace issue 2023-03-30 00:35:55 +05:30
StandardNotes CI c3a5b249b2 chore(release): publish
- @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
 - @standardnotes/[email protected]
2023-03-27 08:10:35 +00:00
Aman Harwara 3c209329a3 chore: upgrade lexical 2023-03-27 13:07:00 +05:30
90 changed files with 1144 additions and 2481 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+48
View File
@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.105.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
**Note:** Version bump only for package @standardnotes/desktop
## [3.105.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
**Note:** Version bump only for package @standardnotes/desktop
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@standardnotes/desktop",
"main": "./app/dist/index.js",
"version": "3.105.32",
"version": "3.105.44",
"license": "AGPL-3.0-or-later",
"author": "Standard Notes.",
"private": true,
+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.10.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/icons
## [1.10.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
### Bug Fixes
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
## [1.10.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-02-02)
**Note:** Version bump only for package @standardnotes/icons
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/icons",
"version": "1.10.5",
"version": "1.10.7",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="m11.4 16l2.6-2.6l2.6 2.6l1.4-1.4l-2.6-2.6L18 9.4L16.6 8L14 10.6L11.4 8L10 9.4l2.6 2.6l-2.6 2.6l1.4 1.4ZM3 12l4.35-6.15q.275-.4.713-.625T9 5h10q.825 0 1.413.588T21 7v10q0 .825-.588 1.413T19 19H9q-.5 0-.938-.225t-.712-.625L3 12Z" />
</svg>

After

Width:  |  Height:  |  Size: 330 B

+9 -7
View File
@@ -6,6 +6,7 @@ import AccountVariantIcon from './ic-account-variant.svg'
import AddBoldIcon from './ic-add-bold.svg'
import AddIcon from './ic-add.svg'
import AddTextIcon from './ic-add-text.svg'
import AegisIcon from './ic-aegis.svg'
import ArchiveIcon from './ic-archive.svg'
import ArrowDownCheckmarkIcon from './arrow-down-checkmark.svg'
import ArrowDownIcon from './ic-arrow-down.svg'
@@ -21,6 +22,7 @@ import AttachmentFileIcon from './ic-attachment-file.svg'
import AuthenticatorIcon from './ic-authenticator.svg'
import AuthenticatorVariantIcon from './ic-authenticator-variant.svg'
import BackIosIcon from './ic-back-ios.svg'
import BackspaceIcon from './ic-backspace.svg'
import BlockIcon from './ic-block.svg'
import BlueDotIcon from './blue-dot.svg'
import BoldIcon from './ic-bold.svg'
@@ -57,6 +59,7 @@ import EditorIcon from './ic-editor.svg'
import EmailFilledIcon from './ic-email-filled.svg'
import EmailIcon from './ic-email.svg'
import EnterIcon from './ic-enter.svg'
import EvernoteIcon from './ic-evernote.svg'
import EyeFilledIcon from './ic-eye-filled.svg'
import EyeIcon from './ic-eye.svg'
import EyeOffFilledIcon from './ic-eye-off-filled.svg'
@@ -87,6 +90,7 @@ import ForwardIosIcon from './ic-forward-ios.svg'
import FullscreenExitIcon from './ic-fullscreen-exit.svg'
import FullscreenIcon from './ic-fullscreen.svg'
import GiftOutlineIcon from './ic-gift-outline.svg'
import GoogleKeepIcon from './ic-gkeep.svg'
import HashtagFilledIcon from './ic-hashtag-filled.svg'
import HashtagIcon from './ic-hashtag.svg'
import HashtagOffIcon from './ic-hashtag-off.svg'
@@ -112,9 +116,9 @@ import LineWidthIcon from './ic-line-width.svg'
import LinkIcon from './ic-link.svg'
import LinkOffIcon from './ic-link-off.svg'
import ListBulleted from './ic-list-bulleted.svg'
import ListNumbered from './ic-list-numbered.svg'
import ListedFilledIcon from './ic-listed-filled.svg'
import ListedIcon from './ic-listed.svg'
import ListNumbered from './ic-list-numbered.svg'
import LockFilledIcon from './ic-lock-filled.svg'
import LockIcon from './ic-lock.svg'
import MarkdownIcon from './ic-markdown.svg'
@@ -146,6 +150,8 @@ import PrintIcon from './ic-print.svg'
import ProtectedIllustration from './il-protected.svg'
import RedoIcon from './ic-redo.svg'
import ReorderIcon from './ic-reorder.svg'
import ReplaceAllIcon from './ic-replace-all.svg'
import ReplaceIcon from './ic-replace.svg'
import RestoreIcon from './ic-restore.svg'
import RichTextIcon from './ic-text-rich.svg'
import SafeIcon from './ic-safe.svg'
@@ -164,6 +170,7 @@ import ShareIcon from './ic-share.svg'
import ShortcutButtonIcon from './ic-shortcut-button.svg'
import SignInIcon from './ic-signin.svg'
import SignOutIcon from './ic-signout.svg'
import SimplenoteIcon from './ic-simplenote.svg'
import SNLogoAltIcon from './ic-standard-notes-2.svg'
import SNLogoFull from './ic-sn-logo-full.svg'
import SNLogoIcon from './ic-standard-notes.svg'
@@ -201,12 +208,6 @@ import UserSwitch from './ic-user-switch.svg'
import ViewIcon from './ic-view.svg'
import WarningIcon from './ic-warning.svg'
import WindowIcon from './ic-window.svg'
import EvernoteIcon from './ic-evernote.svg'
import GoogleKeepIcon from './ic-gkeep.svg'
import SimplenoteIcon from './ic-simplenote.svg'
import AegisIcon from './ic-aegis.svg'
import ReplaceIcon from './ic-replace.svg'
import ReplaceAllIcon from './ic-replace-all.svg'
export {
AccessibilityIcon,
@@ -232,6 +233,7 @@ export {
AuthenticatorIcon,
AuthenticatorVariantIcon,
BackIosIcon,
BackspaceIcon,
BlockIcon,
BlueDotIcon,
BoldIcon,
+48
View File
@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.51.77](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.76](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.75](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.74](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.73](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.72](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.71](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.70](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.69](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.68](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
**Note:** Version bump only for package @standardnotes/mobile
## [3.51.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
**Note:** Version bump only for package @standardnotes/mobile
@@ -8,5 +8,6 @@
tools:targetApi="28"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
</application>
</manifest>
@@ -30,18 +30,20 @@
android:theme="@style/AppTheme"
tools:targetApi="n">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<queries>
@@ -1,3 +1,9 @@
<resources>
<string name="app_name">Standard Notes</string>
<string name="asset_statements" translatable="false">
[{
\"include\": \"https://app.standardnotes.com/.well-known/assetlinks.json\"
}]
</string>
</resources>
+2 -2
View File
@@ -637,7 +637,7 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"
SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
boost: a7c83b31436843459a1961bfd74b96033dc77234
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: 60195509584153283780abdac5569feffb8f08cc
@@ -658,7 +658,7 @@ SPEC CHECKSUMS:
MMKV: 9c4663aa7ca255d478ff10f2f5cb7d17c1651ccd
MMKVCore: 89f5c8a66bba2dcd551779dea4d412eeec8ff5bb
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda
RCTRequired: bec48f07daf7bcdc2655a0cde84e07d24d2a9e2a
RCTTypeSafety: 171394eebacf71e1cfad79dbfae7ee8fc16ca80a
React: d7433ccb6a8c36e4cbed59a73c0700fc83c3e98a
@@ -674,7 +674,11 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = HKF9BXSN95;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = StandardNotes/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
@@ -689,6 +693,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = com.standardnotes.standardnotes;
PRODUCT_NAME = StandardNotes;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/mobile",
"version": "3.51.65",
"version": "3.51.77",
"author": "Standard Notes.",
"private": true,
"license": "AGPL-3.0-or-later",
+48
View File
@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.4.220](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.219](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.218](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.217](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.216](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.215](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.214](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.213](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.212](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.211](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.210](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.209](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
**Note:** Version bump only for package @standardnotes/releases
## [1.4.208](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
**Note:** Version bump only for package @standardnotes/releases
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/releases",
"version": "1.4.208",
"version": "1.4.220",
"license": "AGPL-3.0-or-later",
"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.
## [2.166.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/snjs
## [2.166.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
**Note:** Version bump only for package @standardnotes/snjs
@@ -170,7 +170,9 @@ describe('featuresService', () => {
featuresService.getExperimentalFeatures = jest.fn().mockReturnValue([FeatureIdentifier.PlusEditor])
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).not.toHaveBeenCalled()
})
@@ -191,7 +193,9 @@ describe('featuresService', () => {
featuresService.getEnabledExperimentalFeatures = jest.fn().mockReturnValue([FeatureIdentifier.PlusEditor])
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).toHaveBeenCalled()
})
})
@@ -226,9 +230,10 @@ describe('featuresService', () => {
const spy = jest.spyOn(featuresService, 'notifyEvent' as never)
const newRoles = [...roles, RoleName.NAMES.ProUser]
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(spy.mock.calls[2][0]).toEqual(FeaturesEvent.DidPurchaseSubscription)
expect(spy.mock.calls[0][0]).toEqual(FeaturesEvent.DidPurchaseSubscription)
})
it('should not notify of subscription purchase on initial roles load after sign in', async () => {
@@ -240,7 +245,8 @@ describe('featuresService', () => {
const spy = jest.spyOn(featuresService, 'notifyEvent' as never)
const newRoles = [...roles, RoleName.NAMES.ProUser]
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
const triggeredEvents = spy.mock.calls.map((call) => call[0])
expect(triggeredEvents).not.toContain(FeaturesEvent.DidPurchaseSubscription)
@@ -252,8 +258,11 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserRoles, newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(apiService.getUserFeatures).toHaveBeenCalledWith('123')
})
@@ -263,7 +272,9 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserRoles, newRoles)
expect(apiService.getUserFeatures).toHaveBeenCalledWith('123')
})
@@ -274,7 +285,9 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(storageService.setValue).toHaveBeenCalledWith(StorageKey.UserFeatures, features)
})
@@ -284,7 +297,9 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).toHaveBeenCalledTimes(2)
expect(itemManager.createItem).toHaveBeenCalledWith(
ContentType.Theme,
@@ -328,7 +343,8 @@ describe('featuresService', () => {
itemManager.getItems = jest.fn().mockReturnValue([existingItem])
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.changeComponent).toHaveBeenCalledWith(existingItem, expect.any(Function))
})
@@ -354,7 +370,9 @@ describe('featuresService', () => {
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).toHaveBeenCalledWith(
ContentType.Component,
expect.objectContaining({
@@ -401,7 +419,9 @@ describe('featuresService', () => {
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.setItemsToBeDeleted).toHaveBeenCalledWith([existingItem])
})
@@ -424,7 +444,9 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).not.toHaveBeenCalled()
})
@@ -447,7 +469,9 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(itemManager.createItem).not.toHaveBeenCalled()
})
@@ -455,10 +479,14 @@ describe('featuresService', () => {
storageService.getValue = jest.fn().mockReturnValue(roles)
const featuresService = createService()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
await featuresService.updateOnlineRolesAndFetchFeatures('123', roles)
const { didChangeRoles: didChangeRoles1 } = await featuresService.updateOnlineRoles(roles)
await featuresService.fetchFeatures('123', didChangeRoles1)
const { didChangeRoles: didChangeRoles2 } = await featuresService.updateOnlineRoles(roles)
await featuresService.fetchFeatures('123', didChangeRoles2)
const { didChangeRoles: didChangeRoles3 } = await featuresService.updateOnlineRoles(roles)
await featuresService.fetchFeatures('123', didChangeRoles3)
const { didChangeRoles: didChangeRoles4 } = await featuresService.updateOnlineRoles(roles)
await featuresService.fetchFeatures('123', didChangeRoles4)
expect(storageService.setValue).toHaveBeenCalledTimes(2)
})
@@ -482,7 +510,9 @@ describe('featuresService', () => {
const nativeFeature = featuresService['mapRemoteNativeFeatureToStaticFeature'](remoteFeature)
featuresService['mapRemoteNativeFeatureToItem'] = jest.fn()
featuresService.initializeFromDisk()
await featuresService.updateOnlineRolesAndFetchFeatures('123', newRoles)
const { didChangeRoles } = await featuresService.updateOnlineRoles(newRoles)
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService['mapRemoteNativeFeatureToItem']).toHaveBeenCalledWith(
nativeFeature,
expect.anything(),
@@ -522,7 +552,11 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.CoreUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService.getFeatureStatus(FeatureIdentifier.MidnightTheme)).toBe(FeatureStatus.Entitled)
expect(featuresService.getFeatureStatus(FeatureIdentifier.SuperEditor)).toBe(FeatureStatus.Entitled)
@@ -554,7 +588,8 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService.getFeatureStatus(FeatureIdentifier.MidnightTheme)).toBe(FeatureStatus.NoUserSubscription)
expect(featuresService.getFeatureStatus(FeatureIdentifier.PlusEditor)).toBe(FeatureStatus.NoUserSubscription)
@@ -566,7 +601,8 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.ProUser])
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService.getFeatureStatus(FeatureIdentifier.SuperEditor)).toBe(FeatureStatus.NotInCurrentPlan)
})
@@ -615,7 +651,8 @@ describe('featuresService', () => {
} as never),
])
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService.getFeatureStatus(themeFeature.identifier)).toBe(FeatureStatus.Entitled)
expect(featuresService.getFeatureStatus(editorFeature.identifier)).toBe(FeatureStatus.InCurrentPlanButExpired)
@@ -627,7 +664,8 @@ describe('featuresService', () => {
it('feature status should be not entitled if no account or offline repo', async () => {
const featuresService = createService()
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles)
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
@@ -652,7 +690,11 @@ describe('featuresService', () => {
it('feature status for offline subscription', async () => {
const featuresService = createService()
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.CoreUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
featuresService.onlineRolesIncludePaidSubscription = jest.fn().mockReturnValue(false)
@@ -680,7 +722,11 @@ describe('featuresService', () => {
FeatureStatus.NoUserSubscription,
)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.CoreUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
expect(featuresService.getFeatureStatus(FeatureIdentifier.DeprecatedFileSafe as FeatureIdentifier)).toBe(
FeatureStatus.Entitled,
@@ -690,12 +736,17 @@ describe('featuresService', () => {
it('has paid subscription', async () => {
const featuresService = createService()
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles: didChangeRoles1 } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles1)
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
expect(featuresService.hasPaidAnyPartyOnlineOrOfflineSubscription()).toBeFalsy
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser])
const { didChangeRoles: didChangeRoles2 } = await featuresService.updateOnlineRoles([
RoleName.NAMES.CoreUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles2)
expect(featuresService.hasPaidAnyPartyOnlineOrOfflineSubscription()).toEqual(true)
})
@@ -703,7 +754,8 @@ describe('featuresService', () => {
it('has paid subscription should be true if offline repo and signed into third party server', async () => {
const featuresService = createService()
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles)
featuresService.hasOfflineRepo = jest.fn().mockReturnValue(true)
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(false)
@@ -725,7 +777,11 @@ describe('featuresService', () => {
const featuresService = createService()
await featuresService.migrateFeatureRepoToUserSetting([extensionRepoItem])
expect(settingsService.updateSetting).toHaveBeenCalledWith(SettingName.create(SettingName.NAMES.ExtensionKey).getValue(), extensionKey, true)
expect(settingsService.updateSetting).toHaveBeenCalledWith(
SettingName.create(SettingName.NAMES.ExtensionKey).getValue(),
extensionKey,
true,
)
})
})
@@ -789,7 +845,8 @@ describe('featuresService', () => {
it('should be false if core user checks for plus role', async () => {
const featuresService = createService()
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([RoleName.NAMES.CoreUser])
await featuresService.fetchFeatures('123', didChangeRoles)
const hasPlusUserRole = featuresService.hasMinimumRole(RoleName.NAMES.PlusUser)
@@ -801,7 +858,11 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.PlusUser, RoleName.NAMES.CoreUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.PlusUser,
RoleName.NAMES.CoreUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
const hasProUserRole = featuresService.hasMinimumRole(RoleName.NAMES.ProUser)
@@ -813,7 +874,11 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.ProUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
const hasCoreUserRole = featuresService.hasMinimumRole(RoleName.NAMES.CoreUser)
@@ -825,7 +890,11 @@ describe('featuresService', () => {
sessionManager.isSignedIntoFirstPartyServer = jest.fn().mockReturnValue(true)
await featuresService.updateOnlineRolesAndFetchFeatures('123', [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser])
const { didChangeRoles } = await featuresService.updateOnlineRoles([
RoleName.NAMES.ProUser,
RoleName.NAMES.PlusUser,
])
await featuresService.fetchFeatures('123', didChangeRoles)
const hasProUserRole = featuresService.hasMinimumRole(RoleName.NAMES.ProUser)
@@ -88,7 +88,8 @@ export class SNFeaturesService
const {
payload: { userUuid, currentRoles },
} = data as UserRolesChangedEvent
await this.updateOnlineRolesAndFetchFeatures(userUuid, currentRoles)
const { didChangeRoles } = await this.updateOnlineRoles(currentRoles)
await this.fetchFeatures(userUuid, didChangeRoles)
}
})
@@ -143,6 +144,9 @@ export class SNFeaturesService
return
}
const { userUuid, userRoles } = event.payload as MetaReceivedData
const { didChangeRoles } = await this.updateOnlineRoles(userRoles.map((role) => role.name))
/**
* All user data must be downloaded before we map features. Otherwise, feature mapping
* may think a component doesn't exist and create a new one, when in reality the component
@@ -152,11 +156,7 @@ export class SNFeaturesService
return
}
const { userUuid, userRoles } = event.payload as MetaReceivedData
await this.updateOnlineRolesAndFetchFeatures(
userUuid,
userRoles.map((role) => role.name),
)
await this.fetchFeatures(userUuid, didChangeRoles)
}
}
@@ -400,7 +400,9 @@ export class SNFeaturesService
return hasFirstPartyOfflineSubscription || new URL(offlineRepo.content.offlineFeaturesUrl).hostname === 'localhost'
}
async updateOnlineRolesAndFetchFeatures(userUuid: UuidString, roles: string[]): Promise<void> {
async updateOnlineRoles(roles: string[]): Promise<{
didChangeRoles: boolean
}> {
const previousRoles = this.onlineRoles
const userRolesChanged =
@@ -409,13 +411,31 @@ export class SNFeaturesService
const isInitialLoadRolesChange = previousRoles.length === 0 && userRolesChanged
if (!userRolesChanged && !this.needsInitialFeaturesUpdate) {
return {
didChangeRoles: false,
}
}
if (userRolesChanged && !isInitialLoadRolesChange) {
if (this.onlineRolesIncludePaidSubscription()) {
await this.notifyEvent(FeaturesEvent.DidPurchaseSubscription)
}
}
await this.setOnlineRoles(roles)
return {
didChangeRoles: true,
}
}
async fetchFeatures(userUuid: UuidString, didChangeRoles: boolean): Promise<void> {
if (!didChangeRoles && !this.needsInitialFeaturesUpdate) {
return
}
this.needsInitialFeaturesUpdate = false
await this.setOnlineRoles(roles)
const shouldDownloadRoleBasedFeatures = !this.hasOfflineRepo()
if (shouldDownloadRoleBasedFeatures) {
@@ -426,12 +446,6 @@ export class SNFeaturesService
await this.didDownloadFeatures(features)
}
}
if (userRolesChanged && !isInitialLoadRolesChange) {
if (this.onlineRolesIncludePaidSubscription()) {
await this.notifyEvent(FeaturesEvent.DidPurchaseSubscription)
}
}
}
async setOnlineRoles(roles: string[]): Promise<void> {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/snjs",
"version": "2.166.17",
"version": "2.166.18",
"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.7.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/styles
## [1.7.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
### Bug Fixes
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
## [1.7.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-01-16)
**Note:** Version bump only for package @standardnotes/styles
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/styles",
"version": "1.7.4",
"version": "1.7.6",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"publishConfig": {
+4 -2
View File
@@ -41,7 +41,7 @@ const getColorsForPrimaryVariant = (style: AlertButtonStyle) => {
type AlertButton = {
text: string
style: AlertButtonStyle
action: () => void
action?: () => void
primary?: boolean
}
@@ -168,7 +168,9 @@ export class SKAlert {
this.buttons.forEach((buttonDesc, index) => {
const buttonElem = this.element.querySelector(`#button-${index}`) as HTMLButtonElement
buttonElem.onclick = () => {
buttonDesc.action && buttonDesc.action()
if (buttonDesc.action) {
buttonDesc.action()
}
this.dismiss()
}
if (index === 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.3.26](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/toast
## [1.3.25](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
**Note:** Version bump only for package @standardnotes/toast
## [1.3.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-02-02)
**Note:** Version bump only for package @standardnotes/toast
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/toast",
"version": "1.3.24",
"version": "1.3.26",
"private": true,
"main": "./src/index.ts",
"scripts": {
+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.24.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.24.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
**Note:** Version bump only for package @standardnotes/ui-services
## [1.24.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
**Note:** Version bump only for package @standardnotes/ui-services
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/ui-services",
"version": "1.24.39",
"version": "1.24.41",
"engines": {
"node": ">=16.0.0 <17.0.0"
},
+53
View File
@@ -3,6 +3,59 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.150.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/web
## [3.150.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/web
## [3.150.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)
**Note:** Version bump only for package @standardnotes/web
## [3.150.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/web
## [3.150.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/web
## [3.150.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)
**Note:** Version bump only for package @standardnotes/web
## [3.150.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)
**Note:** Version bump only for package @standardnotes/web
## [3.150.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/web
## [3.150.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)
**Note:** Version bump only for package @standardnotes/web
## [3.150.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)
### Bug Fixes
* Files uploaded to a protected Super note will also be marked as protected ([09fd659](https://github.com/standardnotes/app/commit/09fd65903b5e34c7f2b9f805e372efda383c7e4c))
* Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal ([7773bec](https://github.com/standardnotes/app/commit/7773becdec0f5a623337b25a7fd218e5bcfebf13))
## [3.150.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)
### Bug Fixes
* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))
## [3.150.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)
**Note:** Version bump only for package @standardnotes/web
## [3.150.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)
### Bug Fixes
+140
View File
@@ -1,5 +1,145 @@
{
"versions": [
{
"version": "3.150.44",
"title": "[3.150.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.43",
"title": "[3.150.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.42",
"title": "[3.150.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-06)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.41",
"title": "[3.150.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.40",
"title": "[3.150.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.39",
"title": "[3.150.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-03)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.38",
"title": "[3.150.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-04-01)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.37",
"title": "[3.150.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.36",
"title": "[3.150.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-31)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.35",
"title": "[3.150.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-30)",
"date": null,
"body": "### Bug Fixes\n\n* Files uploaded to a protected Super note will also be marked as protected ([09fd659](https://github.com/standardnotes/app/commit/09fd65903b5e34c7f2b9f805e372efda383c7e4c))\n* Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal ([7773bec](https://github.com/standardnotes/app/commit/7773becdec0f5a623337b25a7fd218e5bcfebf13))",
"parsed": {
"_": [
"Files uploaded to a protected Super note will also be marked as protected (09fd659)",
"Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal (7773bec)"
],
"Bug Fixes": [
"Files uploaded to a protected Super note will also be marked as protected (09fd659)",
"Fixed an issue where deleting a file from the preview modal would not correctly navigate to next item or dismiss the modal (7773bec)"
]
}
},
{
"version": "3.150.34",
"title": "[3.150.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-29)",
"date": null,
"body": "### Bug Fixes\n\n* Added backspace button on Super toolbar on Android as a workaround for backspace issue ([08a20e2](https://github.com/standardnotes/app/commit/08a20e25b416e672f0e80518ca66365f7089b4f3))",
"parsed": {
"_": [
"Added backspace button on Super toolbar on Android as a workaround for backspace issue (08a20e2)"
],
"Bug Fixes": [
"Added backspace button on Super toolbar on Android as a workaround for backspace issue (08a20e2)"
]
}
},
{
"version": "3.150.33",
"title": "[3.150.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-27)",
"date": null,
"body": "**Note:** Version bump only for package @standardnotes/web",
"parsed": {
"_": [
"Note: Version bump only for package @standardnotes/web"
]
}
},
{
"version": "3.150.32",
"title": "[3.150.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-03-26)",
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@standardnotes/web",
"version": "3.150.32",
"version": "3.150.44",
"license": "AGPL-3.0-or-later",
"main": "dist/app.js",
"author": "Standard Notes.",
@@ -28,7 +28,7 @@
"@babel/plugin-transform-react-jsx": "^7.19.0",
"@babel/preset-env": "*",
"@babel/preset-typescript": "^7.18.6",
"@lexical/react": "0.9.0",
"@lexical/react": "0.9.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@reach/alert": "^0.18.0",
"@reach/alert-dialog": "^0.18.0",
@@ -84,7 +84,7 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"lexical": "0.9.0",
"lexical": "0.9.1",
"lint-staged": ">=13",
"mini-css-extract-plugin": "^2.7.2",
"minimatch": "^5.1.1",
@@ -116,7 +116,7 @@
"app/**/*.{js,ts,jsx,tsx,css,md}": "prettier --write"
},
"dependencies": {
"@lexical/headless": "0.9.0",
"@lexical/headless": "0.9.1",
"contactjs": "2.1.5"
}
}
@@ -1,7 +1,8 @@
{
"webcredentials": {
"apps": [
"HKF9BXSN95.com.standardnotes.standardnotes"
"HKF9BXSN95.com.standardnotes.standardnotes",
"MVYA8NJCQ5.com.standardnotes.standardnotes"
]
}
}
@@ -0,0 +1,19 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
"site": "https://app.standardnotes.com"
}
},
{
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "android_app",
"package_name": "com.standardnotes",
"sha256_cert_fingerprints": [
"58:3F:C4:1B:49:0C:38:0B:56:FB:E9:EE:47:38:1E:B4:EF:53:E0:37:19:A5:50:3F:C1:F2:9D:CD:8E:28:90:2A"
]
}
}
]
@@ -131,6 +131,7 @@ const CreateAccount: FunctionComponent<Props> = ({
ref={emailInputRef}
type="email"
value={email}
spellcheck={false}
/>
<DecoratedPasswordInput
className={{ container: 'mb-2' }}
@@ -102,9 +102,9 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
<div className="wrap my-0.5 font-bold">{user.email}</div>
<span className="text-neutral">{application.getHost()}</span>
</div>
<div className="mb-2 flex items-start justify-between px-3">
<div className="mb-2 flex items-start justify-between px-3 text-mobile-menu-item md:text-tablet-menu-item lg:text-menu-item">
{isSyncingInProgress ? (
<div className="flex items-center text-base font-semibold text-info lg:text-sm">
<div className="flex items-center font-semibold text-info">
<Spinner className="mr-2 h-5 w-5" />
Syncing...
</div>
@@ -112,8 +112,8 @@ const GeneralAccountMenu: FunctionComponent<Props> = ({
<div className="flex items-start">
<Icon type="check-circle" className={`mr-2 text-success ${MenuItemIconSize}`} />
<div>
<div className="text-base font-semibold text-success lg:text-sm">Last synced:</div>
<div className="text-base text-text lg:text-sm">{lastSyncDate}</div>
<div className="font-semibold text-success">Last synced:</div>
<div className="text-text">{lastSyncDate}</div>
</div>
</div>
)}
@@ -210,6 +210,7 @@ const SignInPane: FunctionComponent<Props> = ({ application, viewControllerManag
onKeyDown={handleKeyDown}
disabled={isSigningIn || isPrivateUsername}
ref={emailInputRef}
spellcheck={false}
/>
<DecoratedPasswordInput
className={{ container: `mb-2 ${error ? 'border-danger' : null}` }}
@@ -56,7 +56,7 @@ const WorkspaceMenuItem: FunctionComponent<Props> = ({
return (
<div className="relative">
<MenuRadioButtonItem
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-sm text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none"
className="flex w-full cursor-pointer items-center border-0 bg-transparent px-3 py-2 text-left text-mobile-menu-item text-text hover:bg-contrast hover:text-foreground focus:bg-info-backdrop focus:shadow-none md:text-tablet-menu-item lg:text-menu-item"
onClick={onClick}
checked={descriptor.primary}
>
@@ -51,7 +51,7 @@ const FilePreviewModal = observer(({ application, viewControllerManager }: Props
event.preventDefault()
const currentFileIndex = otherFiles.findIndex((fileFromArray) => fileFromArray.uuid === currentFile.uuid)
const currentFileIndex = otherFiles.findIndex((file) => file.uuid === currentFile.uuid)
switch (event.key) {
case KeyboardKey.Left: {
@@ -73,6 +73,7 @@ export const IconNameToSvgMapping = {
archive: icons.ArchiveIcon,
asterisk: icons.AsteriskIcon,
authenticator: icons.AuthenticatorIcon,
backspace: icons.BackspaceIcon,
bold: icons.BoldIcon,
camera: icons.CameraIcon,
check: icons.CheckIcon,
@@ -2,6 +2,7 @@ import { FilesController } from '@/Controllers/FilesController'
import { LinkingController } from '@/Controllers/LinkingController'
import { SNNote } from '@standardnotes/snjs'
import { useEffect } from 'react'
import { useApplication } from '../ApplicationProvider'
import { useFileDragNDrop } from '../FileDragNDropProvider'
type Props = {
@@ -12,6 +13,7 @@ type Props = {
}
const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, filesController }: Props) => {
const application = useApplication()
const { isDraggingFiles, addDragTarget, removeDragTarget } = useFileDragNDrop()
useEffect(() => {
@@ -22,6 +24,9 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
tooltipText: 'Drop your files to upload and link them to the current note',
callback: async (uploadedFile) => {
await linkingController.linkItems(note, uploadedFile)
void application.mutator.changeAndSaveItem(uploadedFile, (mutator) => {
mutator.protected = note.protected
})
filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid)
},
})
@@ -32,7 +37,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
removeDragTarget(target)
}
}
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController])
}, [addDragTarget, linkingController, note, noteViewElement, removeDragTarget, filesController, application.mutator])
return isDraggingFiles ? (
// Required to block drag events to editor iframe
@@ -112,7 +112,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
<div>
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>Sign-in notification emails</Subtitle>
<Subtitle>Mute sign-in notification emails</Subtitle>
{isMuteSignInEmailsFeatureAvailable ? (
<Text>
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
@@ -145,7 +145,7 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
<HorizontalSeparator classes="my-4" />
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>Marketing notification emails</Subtitle>
<Subtitle>Mute marketing notification emails</Subtitle>
<Text>Disables email notifications with special deals and promotions.</Text>
</div>
{isLoading ? (
@@ -1,390 +0,0 @@
import {
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
EditorConfig,
LexicalEditor,
LexicalNode,
NodeKey,
SerializedLexicalNode,
Spread,
DecoratorNode,
} from 'lexical'
import * as React from 'react'
import { Suspense } from 'react'
export type Cell = {
colSpan: number
json: string
type: 'normal' | 'header'
id: string
width: number | null
}
export type Row = {
cells: Array<Cell>
height: null | number
id: string
}
export type Rows = Array<Row>
export const cellHTMLCache: Map<string, string> = new Map()
export const cellTextContentCache: Map<string, string> = new Map()
const emptyEditorJSON =
'{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'
const plainTextEditorJSON = (text: string) => {
return text === ''
? emptyEditorJSON
: `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":${text},"type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`
}
const TableComponent = React.lazy(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
() => import('./TableComponent'),
)
export function createUID(): string {
return Math.random()
.toString(36)
.replace(/[^a-z]+/g, '')
.substr(0, 5)
}
function createCell(type: 'normal' | 'header'): Cell {
return {
colSpan: 1,
id: createUID(),
json: emptyEditorJSON,
type,
width: null,
}
}
export function createRow(): Row {
return {
cells: [],
height: null,
id: createUID(),
}
}
export type SerializedTableNode = Spread<
{
rows: Rows
type: 'tablesheet'
version: 1
},
SerializedLexicalNode
>
export function extractRowsFromHTML(tableElem: HTMLTableElement): Rows {
const rowElems = tableElem.querySelectorAll('tr')
const rows: Rows = []
for (let y = 0; y < rowElems.length; y++) {
const rowElem = rowElems[y]
const cellElems = rowElem.querySelectorAll('td,th')
if (!cellElems || cellElems.length === 0) {
continue
}
const cells: Array<Cell> = []
for (let x = 0; x < cellElems.length; x++) {
const cellElem = cellElems[x] as HTMLElement
const isHeader = cellElem.nodeName === 'TH'
const cell = createCell(isHeader ? 'header' : 'normal')
cell.json = plainTextEditorJSON(JSON.stringify(cellElem.innerText.replace(/\n/g, ' ')))
cells.push(cell)
}
const row = createRow()
row.cells = cells
rows.push(row)
}
return rows
}
function convertTableElement(domNode: HTMLElement): null | DOMConversionOutput {
const rowElems = domNode.querySelectorAll('tr')
if (!rowElems || rowElems.length === 0) {
return null
}
const rows: Rows = []
for (let y = 0; y < rowElems.length; y++) {
const rowElem = rowElems[y]
const cellElems = rowElem.querySelectorAll('td,th')
if (!cellElems || cellElems.length === 0) {
continue
}
const cells: Array<Cell> = []
for (let x = 0; x < cellElems.length; x++) {
const cellElem = cellElems[x] as HTMLElement
const isHeader = cellElem.nodeName === 'TH'
const cell = createCell(isHeader ? 'header' : 'normal')
cell.json = plainTextEditorJSON(JSON.stringify(cellElem.innerText.replace(/\n/g, ' ')))
cells.push(cell)
}
const row = createRow()
row.cells = cells
rows.push(row)
}
return { node: $createTableNode(rows) }
}
export function exportTableCellsToHTML(
rows: Rows,
rect?: { startX: number; endX: number; startY: number; endY: number },
): HTMLElement {
const table = document.createElement('table')
const colGroup = document.createElement('colgroup')
const tBody = document.createElement('tbody')
const firstRow = rows[0]
for (let x = rect != null ? rect.startX : 0; x < (rect != null ? rect.endX + 1 : firstRow.cells.length); x++) {
const col = document.createElement('col')
colGroup.append(col)
}
for (let y = rect != null ? rect.startY : 0; y < (rect != null ? rect.endY + 1 : rows.length); y++) {
const row = rows[y]
const cells = row.cells
const rowElem = document.createElement('tr')
for (let x = rect != null ? rect.startX : 0; x < (rect != null ? rect.endX + 1 : cells.length); x++) {
const cell = cells[x]
const cellElem = document.createElement(cell.type === 'header' ? 'th' : 'td')
cellElem.innerHTML = cellHTMLCache.get(cell.json) || ''
rowElem.appendChild(cellElem)
}
tBody.appendChild(rowElem)
}
table.appendChild(colGroup)
table.appendChild(tBody)
return table
}
export class TableNode extends DecoratorNode<JSX.Element> {
__rows: Rows
static override getType(): string {
return 'tablesheet'
}
static override clone(node: TableNode): TableNode {
return new TableNode(Array.from(node.__rows), node.__key)
}
static override importJSON(serializedNode: SerializedTableNode): TableNode {
return $createTableNode(serializedNode.rows)
}
override exportJSON(): SerializedTableNode {
return {
rows: this.__rows,
type: 'tablesheet',
version: 1,
}
}
static importDOM(): DOMConversionMap | null {
return {
table: (_node: Node) => ({
conversion: convertTableElement,
priority: 0,
}),
}
}
override exportDOM(): DOMExportOutput {
return { element: exportTableCellsToHTML(this.__rows) }
}
constructor(rows?: Rows, key?: NodeKey) {
super(key)
this.__rows = rows || []
}
override createDOM(): HTMLElement {
const div = document.createElement('div')
div.style.display = 'contents'
return div
}
override updateDOM(): false {
return false
}
mergeRows(startX: number, startY: number, mergeRows: Rows): void {
const self = this.getWritable()
const rows = self.__rows
const endY = Math.min(rows.length, startY + mergeRows.length)
for (let y = startY; y < endY; y++) {
const row = rows[y]
const mergeRow = mergeRows[y - startY]
const cells = row.cells
const cellsClone = Array.from(cells)
const rowClone = { ...row, cells: cellsClone }
const mergeCells = mergeRow.cells
const endX = Math.min(cells.length, startX + mergeCells.length)
for (let x = startX; x < endX; x++) {
const cell = cells[x]
const mergeCell = mergeCells[x - startX]
const cellClone = { ...cell, json: mergeCell.json, type: mergeCell.type }
cellsClone[x] = cellClone
}
rows[y] = rowClone
}
}
updateCellJSON(x: number, y: number, json: string): void {
const self = this.getWritable()
const rows = self.__rows
const row = rows[y]
const cells = row.cells
const cell = cells[x]
const cellsClone = Array.from(cells)
const cellClone = { ...cell, json }
const rowClone = { ...row, cells: cellsClone }
cellsClone[x] = cellClone
rows[y] = rowClone
}
updateCellType(x: number, y: number, type: 'header' | 'normal'): void {
const self = this.getWritable()
const rows = self.__rows
const row = rows[y]
const cells = row.cells
const cell = cells[x]
const cellsClone = Array.from(cells)
const cellClone = { ...cell, type }
const rowClone = { ...row, cells: cellsClone }
cellsClone[x] = cellClone
rows[y] = rowClone
}
insertColumnAt(x: number): void {
const self = this.getWritable()
const rows = self.__rows
for (let y = 0; y < rows.length; y++) {
const row = rows[y]
const cells = row.cells
const cellsClone = Array.from(cells)
const rowClone = { ...row, cells: cellsClone }
const type = (cells[x] || cells[x - 1]).type
cellsClone.splice(x, 0, createCell(type))
rows[y] = rowClone
}
}
deleteColumnAt(x: number): void {
const self = this.getWritable()
const rows = self.__rows
for (let y = 0; y < rows.length; y++) {
const row = rows[y]
const cells = row.cells
const cellsClone = Array.from(cells)
const rowClone = { ...row, cells: cellsClone }
cellsClone.splice(x, 1)
rows[y] = rowClone
}
}
addColumns(count: number): void {
const self = this.getWritable()
const rows = self.__rows
for (let y = 0; y < rows.length; y++) {
const row = rows[y]
const cells = row.cells
const cellsClone = Array.from(cells)
const rowClone = { ...row, cells: cellsClone }
const type = cells[cells.length - 1].type
for (let x = 0; x < count; x++) {
cellsClone.push(createCell(type))
}
rows[y] = rowClone
}
}
insertRowAt(y: number): void {
const self = this.getWritable()
const rows = self.__rows
const prevRow = rows[y] || rows[y - 1]
const cellCount = prevRow.cells.length
const row = createRow()
for (let x = 0; x < cellCount; x++) {
const cell = createCell(prevRow.cells[x].type)
row.cells.push(cell)
}
rows.splice(y, 0, row)
}
deleteRowAt(y: number): void {
const self = this.getWritable()
const rows = self.__rows
rows.splice(y, 1)
}
addRows(count: number): void {
const self = this.getWritable()
const rows = self.__rows
const prevRow = rows[rows.length - 1]
const cellCount = prevRow.cells.length
for (let y = 0; y < count; y++) {
const row = createRow()
for (let x = 0; x < cellCount; x++) {
const cell = createCell(prevRow.cells[x].type)
row.cells.push(cell)
}
rows.push(row)
}
}
updateColumnWidth(x: number, width: number): void {
const self = this.getWritable()
const rows = self.__rows
for (let y = 0; y < rows.length; y++) {
const row = rows[y]
const cells = row.cells
const cellsClone = Array.from(cells)
const rowClone = { ...row, cells: cellsClone }
cellsClone[x].width = width
rows[y] = rowClone
}
}
override decorate(_: LexicalEditor, config: EditorConfig): JSX.Element {
return (
<Suspense>
<TableComponent nodeKey={this.__key} theme={config.theme} rows={this.__rows} />
</Suspense>
)
}
}
export function $isTableNode(node: LexicalNode | null | undefined): node is TableNode {
return node instanceof TableNode
}
export function $createTableNode(rows: Rows): TableNode {
return new TableNode(rows)
}
export function $createTableNodeWithDimensions(
rowCount: number,
columnCount: number,
includeHeaders = true,
): TableNode {
const rows: Rows = []
for (let y = 0; y < columnCount; y++) {
const row: Row = createRow()
rows.push(row)
for (let x = 0; x < rowCount; x++) {
row.cells.push(createCell(includeHeaders === true && (y === 0 || x === 0) ? 'header' : 'normal'))
}
}
return new TableNode(rows)
}
@@ -6,7 +6,17 @@
*
*/
import type { EditorConfig, ElementFormatType, LexicalEditor, LexicalNode, NodeKey, Spread } from 'lexical'
import type {
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
EditorConfig,
ElementFormatType,
LexicalEditor,
LexicalNode,
NodeKey,
Spread,
} from 'lexical'
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
@@ -46,6 +56,15 @@ export type SerializedYouTubeNode = Spread<
SerializedDecoratorBlockNode
>
function convertYoutubeElement(domNode: HTMLElement): null | DOMConversionOutput {
const videoID = domNode.getAttribute('data-lexical-youtube')
if (videoID) {
const node = $createYouTubeNode(videoID)
return { node }
}
return null
}
export class YouTubeNode extends DecoratorBlockNode {
__id: string
@@ -72,6 +91,36 @@ export class YouTubeNode extends DecoratorBlockNode {
}
}
exportDOM(): DOMExportOutput {
const element = document.createElement('iframe')
element.setAttribute('data-lexical-youtube', this.__id)
element.setAttribute('width', '560')
element.setAttribute('height', '315')
element.setAttribute('src', `https://www.youtube.com/embed/${this.__id}`)
element.setAttribute('frameborder', '0')
element.setAttribute(
'allow',
'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
)
element.setAttribute('allowfullscreen', 'true')
element.setAttribute('title', 'YouTube video')
return { element }
}
static importDOM(): DOMConversionMap | null {
return {
iframe: (domNode: HTMLElement) => {
if (!domNode.hasAttribute('data-lexical-youtube')) {
return null
}
return {
conversion: convertYoutubeElement,
priority: 1,
}
},
}
}
constructor(id: string, format?: ElementFormatType, key?: NodeKey) {
super(format, key)
this.__id = id
@@ -33,5 +33,7 @@ export const IS_IOS: boolean = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.
// Keep these in case we need to use them in the future.
// export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
// export const IS_CHROME: boolean = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
export const IS_CHROME: boolean = CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent)
// export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
export const IS_APPLE_WEBKIT = CAN_USE_DOM && /AppleWebKit\/[\d.]+/.test(navigator.userAgent) && !IS_CHROME
@@ -16,7 +16,7 @@ import {
URL_MATCHER,
} from '@lexical/react/LexicalAutoEmbedPlugin'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { useState } from 'react'
import { useMemo, useState } from 'react'
import * as ReactDOM from 'react-dom'
import useModal from '../../Lexical/Hooks/useModal'
@@ -174,6 +174,18 @@ function AutoEmbedMenu({
)
}
const debounce = (callback: (text: string) => void, delay: number) => {
let timeoutId: number
return (text: string) => {
window.clearTimeout(timeoutId)
timeoutId = window.setTimeout(() => {
callback(text)
}, delay)
}
}
export function AutoEmbedDialog({
embedConfig,
onClose,
@@ -183,14 +195,26 @@ export function AutoEmbedDialog({
}): JSX.Element {
const [text, setText] = useState('')
const [editor] = useLexicalComposerContext()
const [embedResult, setEmbedResult] = useState<EmbedMatchResult | null>(null)
const urlMatch = URL_MATCHER.exec(text)
const embedResult = text != null && urlMatch != null ? embedConfig.parseUrl(text) : null
const validateText = useMemo(
() =>
debounce((inputText: string) => {
const urlMatch = URL_MATCHER.exec(inputText)
if (embedConfig != null && inputText != null && urlMatch != null) {
void Promise.resolve(embedConfig.parseUrl(inputText)).then((parseResult) => {
setEmbedResult(parseResult)
})
} else if (embedResult != null) {
setEmbedResult(null)
}
}, 200),
[embedConfig, embedResult],
)
const onClick = async () => {
const result = await embedResult
if (result != null) {
embedConfig.insertNode(editor, result)
const onClick = () => {
if (embedResult != null) {
embedConfig.insertNode(editor, embedResult)
onClose()
}
}
@@ -205,7 +229,9 @@ export function AutoEmbedDialog({
value={text}
data-test-id={`${embedConfig.type}-embed-modal-url`}
onChange={(e) => {
setText(e.target.value)
const { value } = e.target
setText(value)
validateText(value)
}}
/>
</div>
@@ -1,55 +1,28 @@
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { AutoLinkPlugin as LexicalAutoLinkPlugin } from '@lexical/react/LexicalAutoLinkPlugin'
import { COMMAND_PRIORITY_EDITOR, KEY_MODIFIER_COMMAND, $getSelection } from 'lexical'
import { useEffect } from 'react'
import { TOGGLE_LINK_COMMAND } from '@lexical/link'
import { mergeRegister } from '@lexical/utils'
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
const URL_MATCHER =
import { AutoLinkPlugin, createLinkMatcherWithRegExp } from '@lexical/react/LexicalAutoLinkPlugin'
const URL_REGEX =
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
const EMAIL_REGEX =
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
const MATCHERS = [
(text: string) => {
const match = URL_MATCHER.exec(text)
if (match === null) {
return null
}
const fullMatch = match[0]
return {
index: match.index,
length: fullMatch.length,
text: fullMatch,
url: fullMatch.startsWith('http') ? fullMatch : `https://${fullMatch}`,
}
},
createLinkMatcherWithRegExp(URL_REGEX, (text) => {
return text.startsWith('http') ? text : `https://${text}`
}),
createLinkMatcherWithRegExp(EMAIL_REGEX, (text) => {
return `mailto:${text}`
}),
]
export default function AutoLinkPlugin(): JSX.Element | null {
const [editor] = useLexicalComposerContext()
useEffect(() => {
return mergeRegister(
editor.registerCommand(
KEY_MODIFIER_COMMAND,
(event: KeyboardEvent) => {
const isCmdK = event.key === 'k' && !event.altKey && (event.metaKey || event.ctrlKey)
if (isCmdK) {
const selection = $getSelection()
if (selection) {
editor.dispatchCommand(TOGGLE_LINK_COMMAND, selection.getTextContent())
}
}
return false
},
COMMAND_PRIORITY_EDITOR,
),
)
}, [editor])
return (
<>
<LexicalAutoLinkPlugin matchers={MATCHERS} />
</>
)
export default function LexicalAutoLinkPlugin(): JSX.Element {
return <AutoLinkPlugin matchers={MATCHERS} />
}
@@ -32,7 +32,7 @@
border-style: solid;
border-color: transparent;
border-width: 4px 6px 4px 6px;
border-left-color: #000;
border-left-color: var(--sn-stylekit-contrast-color);
display: block;
content: '';
position: absolute;
@@ -8,8 +8,11 @@
import {
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
EditorConfig,
ElementNode,
LexicalEditor,
LexicalNode,
NodeKey,
SerializedElementNode,
@@ -25,6 +28,14 @@ type SerializedCollapsibleContainerNode = Spread<
SerializedElementNode
>
export function convertDetailsElement(domNode: HTMLDetailsElement): DOMConversionOutput | null {
const isOpen = domNode.open !== undefined ? domNode.open : true
const node = $createCollapsibleContainerNode(isOpen)
return {
node,
}
}
export class CollapsibleContainerNode extends ElementNode {
__open: boolean
@@ -41,10 +52,16 @@ export class CollapsibleContainerNode extends ElementNode {
return new CollapsibleContainerNode(node.__open, node.__key)
}
override createDOM(_: EditorConfig): HTMLElement {
override createDOM(_: EditorConfig, editor: LexicalEditor): HTMLElement {
const dom = document.createElement('details')
dom.classList.add('Collapsible__container')
dom.open = this.__open
dom.addEventListener('toggle', () => {
const open = editor.getEditorState().read(() => this.getOpen())
if (open !== dom.open) {
editor.update(() => this.toggleOpen())
}
})
return dom
}
@@ -56,8 +73,15 @@ export class CollapsibleContainerNode extends ElementNode {
return false
}
static importDOM(): DOMConversionMap | null {
return {}
static importDOM(): DOMConversionMap<HTMLDetailsElement> | null {
return {
details: () => {
return {
conversion: convertDetailsElement,
priority: 1,
}
},
}
}
static override importJSON(serializedNode: SerializedCollapsibleContainerNode): CollapsibleContainerNode {
@@ -65,6 +89,14 @@ export class CollapsibleContainerNode extends ElementNode {
return node
}
exportDOM(): DOMExportOutput {
const element = document.createElement('details')
if (this.getLatest().__open) {
element.setAttribute('open', '')
}
return { element }
}
override exportJSON(): SerializedCollapsibleContainerNode {
return {
...super.exportJSON(),
@@ -80,7 +112,7 @@ export class CollapsibleContainerNode extends ElementNode {
}
getOpen(): boolean {
return this.__open
return this.getLatest().__open
}
toggleOpen(): void {
@@ -6,7 +6,16 @@
*
*/
import { DOMConversionMap, EditorConfig, ElementNode, LexicalNode, SerializedElementNode, Spread } from 'lexical'
import {
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
EditorConfig,
ElementNode,
LexicalNode,
SerializedElementNode,
Spread,
} from 'lexical'
type SerializedCollapsibleContentNode = Spread<
{
@@ -16,6 +25,13 @@ type SerializedCollapsibleContentNode = Spread<
SerializedElementNode
>
export function convertCollapsibleContentElement(): DOMConversionOutput | null {
const node = $createCollapsibleContentNode()
return {
node,
}
}
export class CollapsibleContentNode extends ElementNode {
static override getType(): string {
return 'collapsible-content'
@@ -36,7 +52,17 @@ export class CollapsibleContentNode extends ElementNode {
}
static importDOM(): DOMConversionMap | null {
return {}
return {
div: (domNode: HTMLElement) => {
if (!domNode.hasAttribute('data-lexical-collapsible-content')) {
return null
}
return {
conversion: convertCollapsibleContentElement,
priority: 2,
}
},
}
}
static override importJSON(_serializedNode: SerializedCollapsibleContentNode): CollapsibleContentNode {
@@ -47,6 +73,12 @@ export class CollapsibleContentNode extends ElementNode {
return true
}
exportDOM(): DOMExportOutput {
const element = document.createElement('div')
element.setAttribute('data-lexical-collapsible-content', 'true')
return { element }
}
override exportJSON(): SerializedCollapsibleContentNode {
return {
...super.exportJSON(),
@@ -10,10 +10,10 @@ import {
$createParagraphNode,
$isElementNode,
DOMConversionMap,
EditorConfig,
DOMConversionOutput,
DOMExportOutput,
ElementFormatType,
ElementNode,
LexicalEditor,
LexicalNode,
NodeKey,
RangeSelection,
@@ -32,6 +32,13 @@ type SerializedCollapsibleTitleNode = Spread<
SerializedElementNode
>
export function convertSummaryElement(): DOMConversionOutput | null {
const node = $createCollapsibleTitleNode()
return {
node,
}
}
export class CollapsibleTitleNode extends ElementNode {
static override getType(): string {
return 'collapsible-title'
@@ -48,21 +55,11 @@ export class CollapsibleTitleNode extends ElementNode {
return new CollapsibleTitleNode({ key: node.__key })
}
override createDOM(_config: EditorConfig, editor: LexicalEditor): HTMLElement {
override createDOM(): HTMLElement {
const dom = document.createElement('summary')
dom.classList.add('Collapsible__title')
const format = this.getFormatType()
dom.style.textAlign = format
dom.onclick = (event) => {
event.preventDefault()
event.stopPropagation()
editor.update(() => {
const containerNode = this.getParentOrThrow()
if ($isCollapsibleContainerNode(containerNode)) {
containerNode.toggleOpen()
}
})
}
return dom
}
@@ -71,13 +68,25 @@ export class CollapsibleTitleNode extends ElementNode {
}
static importDOM(): DOMConversionMap | null {
return {}
return {
summary: () => {
return {
conversion: convertSummaryElement,
priority: 1,
}
},
}
}
static override importJSON(serializedNode: SerializedCollapsibleTitleNode): CollapsibleTitleNode {
return $createCollapsibleTitleNode(serializedNode.format)
}
exportDOM(): DOMExportOutput {
const element = document.createElement('summary')
return { element }
}
override exportJSON(): SerializedCollapsibleTitleNode {
return {
...super.exportJSON(),
@@ -91,7 +100,7 @@ export class CollapsibleTitleNode extends ElementNode {
return true
}
override insertNewAfter(): ElementNode {
override insertNewAfter(_: RangeSelection, restoreSelection = true): ElementNode {
const containerNode = this.getParentOrThrow()
if (!$isCollapsibleContainerNode(containerNode)) {
@@ -114,7 +123,7 @@ export class CollapsibleTitleNode extends ElementNode {
}
} else {
const paragraph = $createParagraphNode()
containerNode.insertAfter(paragraph)
containerNode.insertAfter(paragraph, restoreSelection)
return paragraph
}
}
@@ -16,8 +16,11 @@ import { $wrapNodeInElement, mergeRegister } from '@lexical/utils'
import { useFilesController } from '@/Controllers/FilesControllerProvider'
import { FilesControllerEvent } from '@/Controllers/FilesController'
import { useLinkingController } from '@/Controllers/LinkingControllerProvider'
import { useApplication } from '@/Components/ApplicationProvider'
import { SNNote } from '@standardnotes/snjs'
export default function FilePlugin(): JSX.Element | null {
export default function FilePlugin({ currentNote }: { currentNote: SNNote }): JSX.Element | null {
const application = useApplication()
const [editor] = useLexicalComposerContext()
const filesController = useFilesController()
const linkingController = useLinkingController()
@@ -34,6 +37,9 @@ export default function FilePlugin(): JSX.Element | null {
if (uploadedFile) {
editor.dispatchCommand(INSERT_FILE_COMMAND, uploadedFile.uuid)
void linkingController.linkItemToSelectedItem(uploadedFile)
void application.mutator.changeAndSaveItem(uploadedFile, (mutator) => {
mutator.protected = currentNote.protected
})
}
} catch (error) {
console.error(error)
@@ -70,7 +76,7 @@ export default function FilePlugin(): JSX.Element | null {
COMMAND_PRIORITY_NORMAL,
),
)
}, [editor, filesController, linkingController])
}, [application.mutator, currentNote.protected, editor, filesController, linkingController])
useEffect(() => {
const disposer = filesController.addEventObserver((event, data) => {
@@ -200,7 +200,7 @@ function useFloatingLinkEditorToolbar(editor: LexicalEditor, anchorElem: HTMLEle
const linkParent = $findMatchingParent(node, $isLinkNode)
const autoLinkParent = $findMatchingParent(node, $isAutoLinkNode)
if (linkParent != null || autoLinkParent != null) {
if (linkParent != null && autoLinkParent == null) {
setIsLink(true)
} else {
setIsLink(false)
@@ -4,7 +4,7 @@ import useModal from '../../Lexical/Hooks/useModal'
import { InsertTableDialog } from '../../Plugins/TablePlugin'
import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode'
import { sanitizeUrl } from '../../Lexical/Utils/sanitizeUrl'
import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical'
import { $getSelection, $isRangeSelection, DELETE_CHARACTER_COMMAND, FORMAT_TEXT_COMMAND } from 'lexical'
import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { GetAlignmentBlocks } from '../Blocks/Alignment'
@@ -23,11 +23,14 @@ import { GetPasswordBlock } from '../Blocks/Password'
import { GetQuoteBlock } from '../Blocks/Quote'
import { GetTableBlock } from '../Blocks/Table'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { classNames } from '@standardnotes/snjs'
import { classNames, Platform } from '@standardnotes/snjs'
import { SUPER_TOGGLE_SEARCH } from '@standardnotes/ui-services'
import { useApplication } from '@/Components/ApplicationProvider'
import { GetRemoteImageBlock } from '../Blocks/RemoteImage'
import { InsertRemoteImageDialog } from '../RemoteImagePlugin/RemoteImagePlugin'
import { SKAlert } from '@standardnotes/styles'
const DontShowSuperAndroidBackspaceAlertKey = 'dontShowSuperAndroidBackspaceAlert'
const MobileToolbarPlugin = () => {
const application = useApplication()
@@ -37,8 +40,10 @@ const MobileToolbarPlugin = () => {
const [isInEditor, setIsInEditor] = useState(false)
const [isInToolbar, setIsInToolbar] = useState(false)
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const isAndroid = application.platform === Platform.Android
const toolbarRef = useRef<HTMLDivElement>(null)
const backspaceButtonRef = useRef<HTMLButtonElement>(null)
const insertLink = useCallback(() => {
const selection = $getSelection()
@@ -156,7 +161,8 @@ const MobileToolbarPlugin = () => {
const handleFocus = () => setIsInEditor(true)
const handleBlur = (event: FocusEvent) => {
if (toolbarRef.current?.contains(event.relatedTarget as Node)) {
const elementToBeFocused = event.relatedTarget as Node
if (toolbarRef.current?.contains(elementToBeFocused) || elementToBeFocused === backspaceButtonRef.current) {
return
}
setIsInEditor(false)
@@ -179,7 +185,13 @@ const MobileToolbarPlugin = () => {
const toolbar = toolbarRef.current
const handleFocus = () => setIsInToolbar(true)
const handleBlur = () => setIsInToolbar(false)
const handleBlur = (event: FocusEvent) => {
const elementToBeFocused = event.relatedTarget as Node
if (elementToBeFocused === backspaceButtonRef.current) {
return
}
setIsInToolbar(false)
}
toolbar.addEventListener('focus', handleFocus)
toolbar.addEventListener('blur', handleBlur)
@@ -190,6 +202,38 @@ const MobileToolbarPlugin = () => {
}
}, [])
useEffect(() => {
if (!isAndroid) {
return
}
const dontShowAgain = application.getValue(DontShowSuperAndroidBackspaceAlertKey)
if (dontShowAgain) {
return
}
const alert = new SKAlert({
title: 'Android backspace issue',
text: 'There is a known issue with Super on Android where pressing the backspace will also delete the character after the cursor. We are working on a fix for this. Please use the backspace button in the toolbar as a workaround.',
buttons: [
{
text: 'OK',
style: 'default',
},
{
text: "Don't show again",
style: 'default',
action: () => {
application.setValue(DontShowSuperAndroidBackspaceAlertKey, true)
},
},
],
})
alert.present()
}, [application, isAndroid])
const isFocusInEditorOrToolbar = isInEditor || isInToolbar
if (!isMobile || !isFocusInEditorOrToolbar) {
return null
@@ -223,6 +267,20 @@ const MobileToolbarPlugin = () => {
>
<Icon type="keyboard-close" size="medium" />
</button>
{isAndroid && (
<button
className="flex flex-shrink-0 items-center justify-center rounded border-l border-border py-3 px-3"
aria-label="Backspace"
ref={backspaceButtonRef}
onClick={() => {
editor.update(() => {
editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true)
})
}}
>
<Icon type="backspace" size="medium" />
</button>
)}
</div>
</>
)
@@ -2,13 +2,27 @@ import { useApplication } from '@/Components/ApplicationProvider'
import Icon from '@/Components/Icon/Icon'
import Spinner from '@/Components/Spinner/Spinner'
import { isDesktopApplication } from '@/Utils'
import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { classNames } from '@standardnotes/snjs'
import { ElementFormatType, NodeKey } from 'lexical'
import { useCallback, useState } from 'react'
import { $createFileNode } from '../EncryptedFilePlugin/Nodes/FileUtils'
import { RemoteImageNode } from './RemoteImageNode'
const RemoteImageComponent = ({ src, alt, node }: { src: string; alt?: string; node: RemoteImageNode }) => {
type Props = {
src: string
alt?: string
node: RemoteImageNode
className: Readonly<{
base: string
focus: string
}>
format: ElementFormatType | null
nodeKey: NodeKey
}
const RemoteImageComponent = ({ className, src, alt, node, format, nodeKey }: Props) => {
const application = useApplication()
const [editor] = useLexicalComposerContext()
@@ -51,37 +65,39 @@ const RemoteImageComponent = ({ src, alt, node }: { src: string; alt?: string; n
const canShowSaveButton = application.isNativeMobileWeb() || isDesktopApplication()
return (
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
<img
alt={alt}
src={src}
onLoad={() => {
setDidImageLoad(true)
}}
/>
{didImageLoad && canShowSaveButton && (
<button
className={classNames(
'flex items-center gap-2.5 rounded border border-border bg-default px-2.5 py-1.5',
!isSaving && 'hover:bg-info hover:text-info-contrast',
)}
onClick={fetchAndUploadImage}
disabled={isSaving}
>
{isSaving ? (
<>
<Spinner className="h-4 w-4" />
Saving...
</>
) : (
<>
<Icon type="download" />
Save image to Files
</>
)}
</button>
)}
</div>
<BlockWithAlignableContents className={className} format={format} nodeKey={nodeKey}>
<div className="relative flex min-h-[2rem] flex-col items-center gap-2.5">
<img
alt={alt}
src={src}
onLoad={() => {
setDidImageLoad(true)
}}
/>
{didImageLoad && canShowSaveButton && (
<button
className={classNames(
'flex items-center gap-2.5 rounded border border-border bg-default px-2.5 py-1.5',
!isSaving && 'hover:bg-info hover:text-info-contrast',
)}
onClick={fetchAndUploadImage}
disabled={isSaving}
>
{isSaving ? (
<>
<Spinner className="h-4 w-4" />
Saving...
</>
) : (
<>
<Icon type="download" />
Save image to Files
</>
)}
</button>
)}
</div>
</BlockWithAlignableContents>
)
}
@@ -1,5 +1,5 @@
import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode'
import { DOMConversionMap, DOMExportOutput, LexicalNode, Spread } from 'lexical'
import { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalEditor, LexicalNode, Spread } from 'lexical'
import RemoteImageComponent from './RemoteImageComponent'
type SerializedRemoteImageNode = Spread<
@@ -75,8 +75,23 @@ export class RemoteImageNode extends DecoratorBlockNode {
return { element }
}
decorate(): JSX.Element {
return <RemoteImageComponent node={this} src={this.__src} alt={this.__alt} />
decorate(_editor: LexicalEditor, config: EditorConfig): JSX.Element {
const embedBlockTheme = config.theme.embedBlock || {}
const className = {
base: embedBlockTheme.base || '',
focus: embedBlockTheme.focus || '',
}
return (
<RemoteImageComponent
className={className}
format={this.__format}
nodeKey={this.getKey()}
node={this}
src={this.__src}
alt={this.__alt}
/>
)
}
}
@@ -6,90 +6,13 @@
*
*/
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { INSERT_TABLE_COMMAND } from '@lexical/table'
import {
$createNodeSelection,
$createParagraphNode,
$getSelection,
$isRangeSelection,
$isRootOrShadowRoot,
$setSelection,
COMMAND_PRIORITY_EDITOR,
createCommand,
EditorThemeClasses,
Klass,
LexicalCommand,
LexicalEditor,
LexicalNode,
} from 'lexical'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import * as React from 'react'
import invariant from '../Lexical/Shared/invariant'
import { $createTableNodeWithDimensions, TableNode } from '../Lexical/Nodes/TableNode'
import { LexicalEditor } from 'lexical'
import { useState } from 'react'
import Button from '../Lexical/UI/Button'
import { DialogActions } from '../Lexical/UI/Dialog'
import TextInput from '../Lexical/UI/TextInput'
export type InsertTableCommandPayload = Readonly<{
columns: string
rows: string
includeHeaders?: boolean
}>
export type CellContextShape = {
cellEditorConfig: null | CellEditorConfig
cellEditorPlugins: null | JSX.Element | Array<JSX.Element>
set: (cellEditorConfig: null | CellEditorConfig, cellEditorPlugins: null | JSX.Element | Array<JSX.Element>) => void
}
export type CellEditorConfig = Readonly<{
namespace: string
nodes?: ReadonlyArray<Klass<LexicalNode>>
onError: (error: Error, editor: LexicalEditor) => void
readOnly?: boolean
theme?: EditorThemeClasses
}>
export const INSERT_NEW_TABLE_COMMAND: LexicalCommand<InsertTableCommandPayload> =
createCommand('INSERT_NEW_TABLE_COMMAND')
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: not sure why TS doesn't like using null as the value?
export const CellContext: React.Context<CellContextShape> = createContext({
cellEditorConfig: null,
cellEditorPlugins: null,
set: () => {
// Empty
},
})
export function TableContext({ children }: { children: JSX.Element }) {
const [contextValue, setContextValue] = useState<{
cellEditorConfig: null | CellEditorConfig
cellEditorPlugins: null | JSX.Element | Array<JSX.Element>
}>({
cellEditorConfig: null,
cellEditorPlugins: null,
})
return (
<CellContext.Provider
value={useMemo(
() => ({
cellEditorConfig: contextValue.cellEditorConfig,
cellEditorPlugins: contextValue.cellEditorPlugins,
set: (cellEditorConfig, cellEditorPlugins) => {
setContextValue({ cellEditorConfig, cellEditorPlugins })
},
}),
[contextValue.cellEditorConfig, contextValue.cellEditorPlugins],
)}
>
{children}
</CellContext.Provider>
)
}
export function InsertTableDialog({
activeEditor,
onClose,
@@ -115,91 +38,3 @@ export function InsertTableDialog({
</>
)
}
export function InsertNewTableDialog({
activeEditor,
onClose,
}: {
activeEditor: LexicalEditor
onClose: () => void
}): JSX.Element {
const [rows, setRows] = useState('5')
const [columns, setColumns] = useState('5')
const onClick = () => {
activeEditor.dispatchCommand(INSERT_NEW_TABLE_COMMAND, { columns, rows })
onClose()
}
return (
<>
<TextInput label="No of rows" onChange={setRows} value={rows} />
<TextInput label="No of columns" onChange={setColumns} value={columns} />
<DialogActions data-test-id="table-model-confirm-insert">
<Button onClick={onClick}>Confirm</Button>
</DialogActions>
</>
)
}
export function TablePlugin({
cellEditorConfig,
children,
}: {
cellEditorConfig: CellEditorConfig
children: JSX.Element | Array<JSX.Element>
}): JSX.Element | null {
const [editor] = useLexicalComposerContext()
const cellContext = useContext(CellContext)
useEffect(() => {
if (!editor.hasNodes([TableNode])) {
invariant(false, 'TablePlugin: TableNode is not registered on editor')
}
cellContext.set(cellEditorConfig, children)
return editor.registerCommand<InsertTableCommandPayload>(
INSERT_TABLE_COMMAND,
({ columns, rows, includeHeaders }) => {
const selection = $getSelection()
if (!$isRangeSelection(selection)) {
return true
}
const focus = selection.focus
const focusNode = focus.getNode()
if (focusNode !== null) {
const tableNode = $createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders)
if ($isRootOrShadowRoot(focusNode)) {
const target = focusNode.getChildAtIndex(focus.offset)
if (target !== null) {
target.insertBefore(tableNode)
} else {
focusNode.append(tableNode)
}
tableNode.insertBefore($createParagraphNode())
} else {
const topLevelNode = focusNode.getTopLevelElementOrThrow()
topLevelNode.insertAfter(tableNode)
}
tableNode.insertAfter($createParagraphNode())
const nodeSelection = $createNodeSelection()
nodeSelection.add(tableNode.getKey())
$setSelection(nodeSelection)
}
return true
},
COMMAND_PRIORITY_EDITOR,
)
}, [cellContext, cellEditorConfig, children, editor])
return null
}
@@ -183,7 +183,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
spellcheck={spellcheck}
>
<ItemSelectionPlugin currentNote={note.current} />
<FilePlugin />
<FilePlugin currentNote={note.current} />
<ItemBubblePlugin />
<BlockPickerMenuPlugin />
<GetMarkdownPlugin ref={getMarkdownPlugin} />
@@ -1,4 +1,5 @@
import { FileItem } from '@standardnotes/snjs'
import { WebApplication } from '@/Application/Application'
import { ContentType, FileItem } from '@standardnotes/snjs'
import { action, makeObservable, observable } from 'mobx'
export class FilePreviewModalController {
@@ -6,7 +7,9 @@ export class FilePreviewModalController {
currentFile: FileItem | undefined = undefined
otherFiles: FileItem[] = []
constructor() {
eventObservers: (() => void)[] = []
constructor(application: WebApplication) {
makeObservable(this, {
isOpen: observable,
currentFile: observable,
@@ -16,6 +19,35 @@ export class FilePreviewModalController {
dismiss: action,
setCurrentFile: action,
})
this.eventObservers.push(
application.streamItems(ContentType.File, ({ changed, removed }) => {
if (!this.currentFile) {
return
}
if (changed.includes(this.currentFile)) {
this.setCurrentFile(this.currentFile)
}
if (removed.find((f) => f.uuid === this.currentFile?.uuid)) {
if (!this.otherFiles.length) {
this.dismiss()
this.currentFile = undefined
return
}
const currentFileIndex = this.otherFiles.findIndex((file) => file.uuid === this.currentFile?.uuid)
const nextFileIndex = currentFileIndex + 1 < this.otherFiles.length ? currentFileIndex + 1 : 0
this.setCurrentFile(this.otherFiles[nextFileIndex])
this.otherFiles = this.otherFiles.filter((file) => file.uuid !== this.currentFile?.uuid)
}
}),
)
}
deinit = () => {
this.eventObservers.forEach((observer) => observer())
;(this.currentFile as any) = undefined
;(this.otherFiles as any) = undefined
}
setCurrentFile = (currentFile: FileItem) => {
@@ -51,7 +51,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
readonly accountMenuController: AccountMenuController
readonly actionsMenuController = new ActionsMenuController()
readonly featuresController: FeaturesController
readonly filePreviewModalController = new FilePreviewModalController()
readonly filePreviewModalController: FilePreviewModalController
readonly filesController: FilesController
readonly noAccountWarningController: NoAccountWarningController
readonly notesController: NotesController
@@ -88,6 +88,8 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
this.subscriptionManager = application.subscriptions
this.filePreviewModalController = new FilePreviewModalController(application)
this.quickSettingsMenuController = new QuickSettingsController(application, this.eventBus)
this.paneController = new PaneController(application, this.eventBus)
@@ -201,6 +203,7 @@ export class ViewControllerManager implements InternalEventHandlerInterface {
this.appEventObserverRemovers.forEach((remover) => remover())
this.appEventObserverRemovers.length = 0
;(this.device as unknown) = undefined
this.filePreviewModalController.deinit()
;(this.filePreviewModalController as unknown) = undefined
;(this.preferencesController as unknown) = undefined
;(this.quickSettingsMenuController as unknown) = undefined
+1
View File
@@ -46,6 +46,7 @@ module.exports = (env) => {
{ from: 'src/index.html' },
{ from: 'src/manifest.webmanifest' },
{ from: 'src/robots.txt' },
{ from: 'src/.well-known', to: '.well-known' },
],
}),
],
+157 -157
View File
@@ -3219,255 +3219,255 @@ __metadata:
languageName: node
linkType: hard
"@lexical/clipboard@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/clipboard@npm:0.9.0"
"@lexical/clipboard@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/clipboard@npm:0.9.1"
dependencies:
"@lexical/html": 0.9.0
"@lexical/list": 0.9.0
"@lexical/selection": 0.9.0
"@lexical/utils": 0.9.0
"@lexical/html": 0.9.1
"@lexical/list": 0.9.1
"@lexical/selection": 0.9.1
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: e420ad35ebccf8cf3adf389ca75af02ca1698ae91f839f2fad5e22438f7825dd67691f7c8ceb203b3db3eed40efc642b8e72311665d792a0fc836b894029db09
lexical: 0.9.1
checksum: 9bce904afd459b21b3f85f0a7969e2bc18b0ada6ec661fcf7d9f6ae734d10dd6d5bc95310f04153d81d809d856310cd703d9af3072fa929c5a4f84eb1e224357
languageName: node
linkType: hard
"@lexical/code@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/code@npm:0.9.0"
"@lexical/code@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/code@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
prismjs: ^1.27.0
peerDependencies:
lexical: 0.9.0
checksum: 84addf448018b2baecec52c069dc4f19df7f36b097ac837df4eb3ab35bcc0d17083d6290a6bbdd1bdf085c4ee9c5aa5e1909f47732af1c92d47fc1e108105091
lexical: 0.9.1
checksum: ca34880b763d87c7bf5570e83b3e5342fbdafe29cc815e7f39783e6f39ec781f44af33e0a08f286b222b06d1deaa610ad4f5974c650e56460b6d8b80b4825d23
languageName: node
linkType: hard
"@lexical/dragon@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/dragon@npm:0.9.0"
"@lexical/dragon@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/dragon@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: 15c6e94c5a1ecf4661dd9453dfa2af1118040901873a5c185704c914fbd32ce0a45d1fabbbe5210e9b4cacdfc793e1fac57935d6976d60a3409268343086b3fe
lexical: 0.9.1
checksum: e899847eddfa2f7908536062478619266d5506193ba9ba4dd5c14418a4ec9b5cf42e2f44f965d25ff38a8d0581b331dd5841ec46cc36ff37703f4ec2d3710f8c
languageName: node
linkType: hard
"@lexical/hashtag@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/hashtag@npm:0.9.0"
"@lexical/hashtag@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/hashtag@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: ad9159a9c06b577d979bd31691013d064407fb85b78067b9723ccd166c8e96a78570b7dc41f711133116de51c36ab99bb058aa568e28cb3dee6955638c981789
lexical: 0.9.1
checksum: 011c04773ee30c13708aee7aad8fabd12f2cc1b10a53f3876a56a683284ba98442560f8f367dbfaa5b03a9e779bcafd1eace4be6e8cee29ee46cf140b64bcc45
languageName: node
linkType: hard
"@lexical/headless@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/headless@npm:0.9.0"
"@lexical/headless@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/headless@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: d754eada955b8c77dba3303f408ee8aff8480225342cba512a85c50ad252fd7de36aca49347d62c1e0895685b3aa9b06dd3bbb1905baa74f54cce9ba300ebbf2
lexical: 0.9.1
checksum: b5e868cb6d2898d281ae0e8380d64553c6f22a18299a5ae699ea15264f38d7a875a0fca901ef985563b95e37490f9e1df737bb2608e5c6f490165fcbbf6409fe
languageName: node
linkType: hard
"@lexical/history@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/history@npm:0.9.0"
"@lexical/history@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/history@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 496e9028a532d15b6a65e66dee4e2dfebc91e973b3837ecb5bcd79a11734df8f301cc6c13bcea498c55936f6cb83c881416f3d16405e403a8e2937046ca18ac9
lexical: 0.9.1
checksum: 58d17e3e5e24a36ae8164c254a429f8d137251a143173145d4b9ca99e46812ae3243485908b8f3fc0572d0b811fece6b4294d70e95060a62cc83072bbb2caead
languageName: node
linkType: hard
"@lexical/html@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/html@npm:0.9.0"
"@lexical/html@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/html@npm:0.9.1"
dependencies:
"@lexical/selection": 0.9.0
"@lexical/selection": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 18f47f61bae7bdfed78f2f82cf14bc80655dcdb18359efb3378ae0987660d44570ce55c615b4ba05e2909a26f98be613586a4be892ad3c249ea86042a1d942e8
lexical: 0.9.1
checksum: ee16ac244554c8793e81e75253ca214d7c9af29384216fd0084b159fc151e743b132520b5d16c6d796d890d54126fabeb54d0508eb4423db72ecde5c0a4ecf74
languageName: node
linkType: hard
"@lexical/link@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/link@npm:0.9.0"
"@lexical/link@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/link@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 47cbf2c05e07662544d47075fea586b155b8909a54e55897de2d53a19562c5d8481efd996e16837ae56b404f432f3d1295f064789102191437d3b83e2608592e
lexical: 0.9.1
checksum: cfdf16e2a88c67fb95c4db2a26d769fa8792731c647300a14acfab6c0758982e8b55126bc41d567bda53afb804738ec281f398061f5ae314f6cb5126801d99e0
languageName: node
linkType: hard
"@lexical/list@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/list@npm:0.9.0"
"@lexical/list@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/list@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 890d4733b4e8a6460ab69c49968f46732e0fe6b3a8c04b84438fbfb8d92f31d736f3c97522d47830204a02204f47613dfd3a201b1f3c4cb7b554aa958fe3a0be
lexical: 0.9.1
checksum: 6c12d096fbdbf70af122d61068ca355d8cbd9d6beea5212bf6b36abbc36a700e54c8460569a34cd53eaae3d7695943dc45288eb609fd618fe1dfc2a014980421
languageName: node
linkType: hard
"@lexical/mark@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/mark@npm:0.9.0"
"@lexical/mark@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/mark@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 87c648b157571ee320988350410569a98bf55385e2c38d05062cb86a4e0b691a5fe358f426f886ccf853b48a2edc61b74001320170b9412313169e108cd29c55
lexical: 0.9.1
checksum: 3a164d042c855a38e862b22c8f42f1847eda97f36d06b10430ad2e8f4546ce30d884a9b508c242577c100c71a43fbcda9f891b0a54b4b8e1460f2776bef43545
languageName: node
linkType: hard
"@lexical/markdown@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/markdown@npm:0.9.0"
"@lexical/markdown@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/markdown@npm:0.9.1"
dependencies:
"@lexical/code": 0.9.0
"@lexical/link": 0.9.0
"@lexical/list": 0.9.0
"@lexical/rich-text": 0.9.0
"@lexical/text": 0.9.0
"@lexical/utils": 0.9.0
"@lexical/code": 0.9.1
"@lexical/link": 0.9.1
"@lexical/list": 0.9.1
"@lexical/rich-text": 0.9.1
"@lexical/text": 0.9.1
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 00171e7950661ce92e614e12b8285a518558c423c810479fd9d3283d038fcf4898115315aee93c9d40c998377be07764d66c967c730154937a9e35f55d12cf25
lexical: 0.9.1
checksum: f322f9609d774fc35e644e06b3ed4412a11475926d0e15f35f393bbf9f1a2fd2dce549f8fc3f1c9cf31a83fff40de86038c6f84a28a09ba09df643a98da09a71
languageName: node
linkType: hard
"@lexical/offset@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/offset@npm:0.9.0"
"@lexical/offset@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/offset@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: cba756182f35b961bbfc604238466cb4a3e80d45ae275da7ae852403b9a398ee04e1602cd2360ec6f762e8fff0df6b68be0dffbe965c37a0ab68b44565e3ea7a
lexical: 0.9.1
checksum: 3583a5acdc8ce8e16508546e72729eec4bdef509b5d0403a4434b2c10a449fea44c421d7026a54084b0a61687116a196dc999dba329e0555ed96f54d6f21c892
languageName: node
linkType: hard
"@lexical/overflow@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/overflow@npm:0.9.0"
"@lexical/overflow@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/overflow@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: 0076ed1a2fa639318532b28e1739da7a922f09d88546b60adb5c6fb21183883dbaa4c0ca72af0295d0049304a19e9224fb5145eaff438f615d524bb51935b8da
lexical: 0.9.1
checksum: 25f15bff65b6d16ce2ba003cfd858e7625643d06aa3da205ab36ca2380b10a564c0b7727dfc01728fe97bade5075f96b232c8f56ef88c4080b0b256efc6f9aaf
languageName: node
linkType: hard
"@lexical/plain-text@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/plain-text@npm:0.9.0"
"@lexical/plain-text@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/plain-text@npm:0.9.1"
peerDependencies:
"@lexical/clipboard": 0.9.0
"@lexical/selection": 0.9.0
"@lexical/utils": 0.9.0
lexical: 0.9.0
checksum: ab909fd7e58a2440a1c82c8d69abb44afa9580360c09c49e4712445137d8be44f3ebdda92fd1af9e547fdda619c3855cf31dd1b61a93f6a2bea7bd7d85e0c795
"@lexical/clipboard": 0.9.1
"@lexical/selection": 0.9.1
"@lexical/utils": 0.9.1
lexical: 0.9.1
checksum: 8a5c22af4f5309aedce15eddfca43628cdd5deb21081d11aa2e5912d7e055c4292681819fcc257cb694a26a47e5b813aacfa3878199651bc258653f84a9eb729
languageName: node
linkType: hard
"@lexical/react@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/react@npm:0.9.0"
"@lexical/react@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/react@npm:0.9.1"
dependencies:
"@lexical/clipboard": 0.9.0
"@lexical/code": 0.9.0
"@lexical/dragon": 0.9.0
"@lexical/hashtag": 0.9.0
"@lexical/history": 0.9.0
"@lexical/link": 0.9.0
"@lexical/list": 0.9.0
"@lexical/mark": 0.9.0
"@lexical/markdown": 0.9.0
"@lexical/overflow": 0.9.0
"@lexical/plain-text": 0.9.0
"@lexical/rich-text": 0.9.0
"@lexical/selection": 0.9.0
"@lexical/table": 0.9.0
"@lexical/text": 0.9.0
"@lexical/utils": 0.9.0
"@lexical/yjs": 0.9.0
"@lexical/clipboard": 0.9.1
"@lexical/code": 0.9.1
"@lexical/dragon": 0.9.1
"@lexical/hashtag": 0.9.1
"@lexical/history": 0.9.1
"@lexical/link": 0.9.1
"@lexical/list": 0.9.1
"@lexical/mark": 0.9.1
"@lexical/markdown": 0.9.1
"@lexical/overflow": 0.9.1
"@lexical/plain-text": 0.9.1
"@lexical/rich-text": 0.9.1
"@lexical/selection": 0.9.1
"@lexical/table": 0.9.1
"@lexical/text": 0.9.1
"@lexical/utils": 0.9.1
"@lexical/yjs": 0.9.1
react-error-boundary: ^3.1.4
peerDependencies:
lexical: 0.9.0
lexical: 0.9.1
react: ">=17.x"
react-dom: ">=17.x"
checksum: f8df9f93785656d0326b7650d9ffab618f5e2cbf0aff00f419159d079b201f133ac6cbc4b92fc377f31edddf5463f9f127bc5e50ba370cfea0462794bee18003
checksum: e4644c3902dc2f8b77bd2d38543380ea3425d2826e55ff7e6807934630547e9c76e9e3bdf44412d6e49437c900f5fce53ff179a1bdb26ddc988f15e23f54ac44
languageName: node
linkType: hard
"@lexical/rich-text@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/rich-text@npm:0.9.0"
"@lexical/rich-text@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/rich-text@npm:0.9.1"
peerDependencies:
"@lexical/clipboard": 0.9.0
"@lexical/selection": 0.9.0
"@lexical/utils": 0.9.0
lexical: 0.9.0
checksum: a546fb0d990e0db23d4337a2e9f279c05ba42f7364e460b621afc23cd176ec227c85bdf39ad731fd299b8cee81cfcfda17295495b9ad0ab195a4753bef7b6908
"@lexical/clipboard": 0.9.1
"@lexical/selection": 0.9.1
"@lexical/utils": 0.9.1
lexical: 0.9.1
checksum: 45ff431534a5aeac66b8337d78e15448b64e7e55b1bfb472c927ca70a26b61c2b069a19b23c2e8a6da9ce6d64495fcf87f676b33c291abb031428d2a860b7da9
languageName: node
linkType: hard
"@lexical/selection@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/selection@npm:0.9.0"
"@lexical/selection@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/selection@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: 2801bfbeb284138a2803fdd3ebdbd4faca0b4f41b4772afbe600399be7287eae9f06e6e713cd4e32040ad51a0b3dc716a9b4cedba8898827ca0e02213a6ba5e6
lexical: 0.9.1
checksum: e5cff47f5aadbe69a67faa1ab7895a57a688e732e5e734e58104caccf90a17978de18f63c6baa04823ab21f4ce4ce5f72238ec482728f2d1ed65974a6b7c7d28
languageName: node
linkType: hard
"@lexical/table@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/table@npm:0.9.0"
"@lexical/table@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/table@npm:0.9.1"
dependencies:
"@lexical/utils": 0.9.0
"@lexical/utils": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: 269f51a9fe73105d29c2b8440ccbb555f48591be3b7fc778638f55b278754c71272724e8868c63eddfd9e551b6dd4575afaf545a7475ffa48953fc50433a4e24
lexical: 0.9.1
checksum: 76a1b8f0bfcf78a332da201314fa3bc93628b44e39ec867a66566c2194ac05182fda2b1c212389be922241c2731cbdfffe830abfb223fd2ae7fd399e337d532b
languageName: node
linkType: hard
"@lexical/text@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/text@npm:0.9.0"
"@lexical/text@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/text@npm:0.9.1"
peerDependencies:
lexical: 0.9.0
checksum: dd6b28f8627f0896e78a3f0567dc0b8da96c8fcc267f14b8e1eeff00cdf50b48da7a2d4b0bea6227cbd1f626b852f03e2c57e97d616eba16e656974fd478f902
lexical: 0.9.1
checksum: 9f393e0c87c969a2fc5d6092b5818f6f9919e3d00ceea842f1c943b07d34bf4ef58bc48657b10f5fa109ff7e09429bde7dc2081eb8dfdf70ab5d6a1ff9d22328
languageName: node
linkType: hard
"@lexical/utils@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/utils@npm:0.9.0"
"@lexical/utils@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/utils@npm:0.9.1"
dependencies:
"@lexical/list": 0.9.0
"@lexical/selection": 0.9.0
"@lexical/table": 0.9.0
"@lexical/list": 0.9.1
"@lexical/selection": 0.9.1
"@lexical/table": 0.9.1
peerDependencies:
lexical: 0.9.0
checksum: eca60b5f31aa6d7823abdd9414e26e851c677637b9639bff4debb4f252a609422b95b5aeec7784e3c155f66a72e7d9709a1c319fe644bb773c9a555d05f2e206
lexical: 0.9.1
checksum: 4c3f7dba5c508162b655741e2e965e61f535a470c5c770d4165267d02b44bbfc76831595d555dd77ca431c2d51f707166276759dc706bcf0d5ca701a8356f939
languageName: node
linkType: hard
"@lexical/yjs@npm:0.9.0":
version: 0.9.0
resolution: "@lexical/yjs@npm:0.9.0"
"@lexical/yjs@npm:0.9.1":
version: 0.9.1
resolution: "@lexical/yjs@npm:0.9.1"
dependencies:
"@lexical/offset": 0.9.0
"@lexical/offset": 0.9.1
peerDependencies:
lexical: 0.9.0
lexical: 0.9.1
yjs: ">=13.5.22"
checksum: a7e076dc267adb4d8e64d597570f35b94c7037f577be2bbca0e5182e4658f0a61581aabe7ad40c953db9be09dcda3186cbd91c2404a249a768641d4e4dbdcdcc
checksum: b2022bb4020d8602420a36a4fc2c13e5893195a75bfb9545a277390b5ac9326832bdb849d41f59a469b93dbf47459545a79e29e242c8f8365f2245a4f2f889cc
languageName: node
linkType: hard
@@ -5572,8 +5572,8 @@ __metadata:
"@babel/plugin-transform-react-jsx": ^7.19.0
"@babel/preset-env": "*"
"@babel/preset-typescript": ^7.18.6
"@lexical/headless": 0.9.0
"@lexical/react": 0.9.0
"@lexical/headless": 0.9.1
"@lexical/react": 0.9.1
"@pmmmwh/react-refresh-webpack-plugin": ^0.5.10
"@reach/alert": ^0.18.0
"@reach/alert-dialog": ^0.18.0
@@ -5630,7 +5630,7 @@ __metadata:
identity-obj-proxy: ^3.0.0
jest: ^29.3.1
jest-environment-jsdom: ^29.3.1
lexical: 0.9.0
lexical: 0.9.1
lint-staged: ">=13"
mini-css-extract-plugin: ^2.7.2
minimatch: ^5.1.1
@@ -15561,10 +15561,10 @@ __metadata:
languageName: node
linkType: hard
"lexical@npm:0.9.0":
version: 0.9.0
resolution: "lexical@npm:0.9.0"
checksum: b4f33b77477f93f482bd491579a1fdc510add8b1aaf99071cb1c4015b281efee227e3de68e5dd3e3ba9713cdfc8ac0ea34d962883f02abdc21db10cc28552c9b
"lexical@npm:0.9.1":
version: 0.9.1
resolution: "lexical@npm:0.9.1"
checksum: 062b2c3c176072f1a4ba9cf930f1241986c739caa13831b209baa6db32289df70265bce796167864a39de8f75d250ff036b71846814d20c973c3f9610b123f21
languageName: node
linkType: hard