Merge branch 'release/3.8.3' into main

This commit is contained in:
Antonella Sgarlatta
2021-06-16 18:50:20 -03:00
22 changed files with 66 additions and 522 deletions

View File

@@ -9,7 +9,6 @@ SECRET_KEY_BASE=test
APP_HOST=http://localhost:3001
EXTENSIONS_MANAGER_LOCATION=extensions/extensions-manager/dist/index.html
BATCH_MANAGER_LOCATION=extensions/batch-manager/dist/index.min.html
SF_DEFAULT_SERVER=http://localhost:3000
SF_NEXT_VERSION_SERVER=http://localhost:3000
@@ -17,7 +16,6 @@ SF_NEXT_VERSION_SERVER=http://localhost:3000
DEV_DEFAULT_SYNC_SERVER=https://sync.standardnotes.org
DEV_NEXT_VERSION_SYNC_SERVER=https://api.standardnotes.com
DEV_EXTENSIONS_MANAGER_LOCATION=public/extensions/extensions-manager/dist/index.html
DEV_BATCH_MANAGER_LOCATION=public/extensions/batch-manager/dist/index.min.html
# NewRelic (Optional)
NEW_RELIC_ENABLED=false

4
.gitmodules vendored
View File

@@ -7,6 +7,4 @@
[submodule "public/extensions/extensions-manager"]
path = public/extensions/extensions-manager
url = https://github.com/sn-extensions/extensions-manager.git
[submodule "public/extensions/batch-manager"]
path = public/extensions/batch-manager
url = https://github.com/sn-extensions/batch-manager.git

View File

@@ -105,7 +105,6 @@ The web app makes use of two optional native extensions, which, when running the
1. Set the following environment variables in the .env file:
```
EXTENSIONS_MANAGER_LOCATION=extensions/extensions-manager/dist/index.html
BATCH_MANAGER_LOCATION=extensions/batch-manager/dist/index.min.html
```
You can also set the `SF_DEFAULT_SERVER` and `SF_NEXT_VERSION_SERVER` environment variables to set the default servers for login and registration.

View File

@@ -19,6 +19,7 @@ export const AutocompleteTagHint = observer(
const onTagHintClick = async () => {
await appState.noteTags.createAndAddNewTag();
appState.noteTags.setAutocompleteInputFocused(true);
};
const onFocus = () => {

View File

@@ -5,6 +5,7 @@ import { AppState } from '@/ui_models/app_state';
import { AutocompleteTagResult } from './AutocompleteTagResult';
import { AutocompleteTagHint } from './AutocompleteTagHint';
import { observer } from 'mobx-react-lite';
import { SNTag } from '@standardnotes/snjs';
type Props = {
appState: AppState;
@@ -41,8 +42,13 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
const onSearchQueryChange = (event: Event) => {
const query = (event.target as HTMLInputElement).value;
appState.noteTags.setAutocompleteSearchQuery(query);
appState.noteTags.searchActiveNoteAutocompleteTags();
if (query === '') {
appState.noteTags.clearAutocompleteSearch();
} else {
appState.noteTags.setAutocompleteSearchQuery(query);
appState.noteTags.searchActiveNoteAutocompleteTags();
}
};
const onFormSubmit = async (event: Event) => {
@@ -83,10 +89,6 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
appState.noteTags.setAutocompleteInputFocused(false);
};
useEffect(() => {
appState.noteTags.searchActiveNoteAutocompleteTags();
}, [appState.noteTags]);
useEffect(() => {
if (autocompleteInputFocused) {
inputRef.current.focus();
@@ -120,7 +122,7 @@ export const AutocompleteTagInput = observer(({ appState }: Props) => {
onBlur={closeOnBlur}
>
<div className="overflow-y-auto">
{autocompleteTagResults.map((tagResult) => (
{autocompleteTagResults.map((tagResult: SNTag) => (
<AutocompleteTagResult
key={tagResult.uuid}
appState={appState}

View File

@@ -229,6 +229,7 @@ class AccountMenuCtrl extends PureViewCtrl<unknown, AccountMenuState> {
onHostInputChange() {
const url = this.getState().formData.url!;
this.application!.setHost(url);
this.application!.setNextVersionHost(url);
}
refreshEncryptionStatus() {

View File

@@ -1,10 +1,8 @@
import { ApplicationService } from '@standardnotes/snjs';
import { WebApplication } from '@/ui_models/application';
import { isDesktopApplication } from '@/utils';
import { AppStateEvent } from '@/ui_models/app_state';
const MILLISECONDS_PER_SECOND = 1000;
const FOCUS_POLL_INTERVAL = 1 * MILLISECONDS_PER_SECOND;
const POLL_INTERVAL = 50;
const LOCK_INTERVAL_NONE = 0;
const LOCK_INTERVAL_IMMEDIATE = 1;
const LOCK_INTERVAL_ONE_MINUTE = 60 * MILLISECONDS_PER_SECOND;
@@ -15,37 +13,21 @@ const STORAGE_KEY_AUTOLOCK_INTERVAL = "AutoLockIntervalKey";
export class AutolockService extends ApplicationService {
private unsubState?: () => void;
private pollFocusInterval: any
private pollInterval: any
private lastFocusState?: 'hidden' | 'visible'
private lockAfterDate?: Date
private lockTimeout?: any
onAppLaunch() {
this.observeVisibility();
if (!isDesktopApplication()) {
this.beginPolling();
}
return super.onAppLaunch();
}
observeVisibility() {
this.unsubState = (this.application as WebApplication).getAppState().addObserver(
async (eventName) => {
if (eventName === AppStateEvent.WindowDidBlur) {
this.documentVisibilityChanged(false);
} else if (eventName === AppStateEvent.WindowDidFocus) {
this.documentVisibilityChanged(true);
}
}
);
if (!isDesktopApplication()) {
this.beginWebFocusPolling();
}
}
deinit() {
this.unsubState?.();
this.cancelAutoLockTimer();
if (this.pollFocusInterval) {
clearInterval(this.pollFocusInterval);
if (this.pollInterval) {
clearInterval(this.pollInterval);
}
}
@@ -85,11 +67,15 @@ export class AutolockService extends ApplicationService {
* Verify document is in focus every so often as visibilitychange event is
* not triggered on a typical window blur event but rather on tab changes.
*/
beginWebFocusPolling() {
this.pollFocusInterval = setInterval(() => {
if (document.hidden) {
/** Native event listeners will have fired */
return;
beginPolling() {
this.pollInterval = setInterval(async () => {
const locked = await this.application.isLocked();
if (
!locked &&
this.lockAfterDate &&
new Date() > this.lockAfterDate
) {
this.lockApplication();
}
const hasFocus = document.hasFocus();
if (hasFocus && this.lastFocusState === 'hidden') {
@@ -99,7 +85,7 @@ export class AutolockService extends ApplicationService {
}
/* Save this to compare against next time around */
this.lastFocusState = hasFocus ? 'visible' : 'hidden';
}, FOCUS_POLL_INTERVAL);
}, POLL_INTERVAL);
}
getAutoLockIntervalOptions() {
@@ -129,14 +115,6 @@ export class AutolockService extends ApplicationService {
async documentVisibilityChanged(visible: boolean) {
if (visible) {
const locked = await this.application.isLocked();
if (
!locked &&
this.lockAfterDate &&
new Date() > this.lockAfterDate
) {
this.lockApplication();
}
this.cancelAutoLockTimer();
} else {
this.beginAutoLockTimer();
@@ -148,29 +126,15 @@ export class AutolockService extends ApplicationService {
if (interval === LOCK_INTERVAL_NONE) {
return;
}
/**
* Use a timeout if possible, but if the computer is put to sleep, timeouts won't
* work. Need to set a date as backup. this.lockAfterDate does not need to be
* persisted, as living in memory is sufficient. If memory is cleared, then the
* application will lock anyway.
*/
const addToNow = (seconds: number) => {
const date = new Date();
date.setSeconds(date.getSeconds() + seconds);
return date;
};
this.lockAfterDate = addToNow(interval / MILLISECONDS_PER_SECOND);
clearTimeout(this.lockTimeout);
this.lockTimeout = setTimeout(() => {
this.cancelAutoLockTimer();
this.lockApplication();
this.lockAfterDate = undefined;
}, interval);
}
cancelAutoLockTimer() {
clearTimeout(this.lockTimeout);
this.lockAfterDate = undefined;
this.lockTimeout = undefined;
}
}

View File

@@ -8,15 +8,12 @@ import {
FillItemContent,
ComponentMutator,
Copy,
dictToArray
, PayloadContent , ComponentPermission } from '@standardnotes/snjs';
PayloadContent,
ComponentPermission } from '@standardnotes/snjs';
/** A class for handling installation of system extensions */
export class NativeExtManager extends ApplicationService {
extManagerId = 'org.standardnotes.extensions-manager';
batchManagerId = 'org.standardnotes.batch-manager';
/** @override */
async onAppLaunch() {
@@ -32,27 +29,13 @@ export class NativeExtManager extends ApplicationService {
]);
}
get batchManagerPred() {
const batchMgrId = 'org.standardnotes.batch-manager';
return SNPredicate.CompoundPredicate([
new SNPredicate('content_type', '=', ContentType.Component),
new SNPredicate('package_info.identifier', '=', batchMgrId)
]);
}
get extMgrUrl() {
return (window as any)._extensions_manager_location;
}
get batchMgrUrl() {
return (window as any)._batch_manager_location;
}
reload() {
this.application!.singletonManager!.registerPredicate(this.extManagerPred);
this.application!.singletonManager!.registerPredicate(this.batchManagerPred);
this.resolveExtensionsManager();
this.resolveBatchManager();
}
async resolveExtensionsManager() {
@@ -132,75 +115,4 @@ export class NativeExtManager extends ApplicationService {
}
return content;
}
async resolveBatchManager() {
const batchManager = (await this.application!.singletonManager!.findOrCreateSingleton(
this.batchManagerPred,
ContentType.Component,
this.batchManagerTemplateContent()
)) as SNComponent;
let needsSync = false;
if (isDesktopApplication()) {
if (!batchManager.local_url) {
await this.application!.changeItem(batchManager.uuid, (m) => {
const mutator = m as ComponentMutator;
mutator.local_url = this.batchMgrUrl;
});
needsSync = true;
}
} else {
if (!batchManager.hosted_url) {
await this.application!.changeItem(batchManager.uuid, (m) => {
const mutator = m as ComponentMutator;
mutator.hosted_url = this.batchMgrUrl;
});
needsSync = true;
}
}
// Handle addition of SN|ExtensionRepo permission
const permissions = Copy(batchManager!.permissions) as ComponentPermission[];
const permission = permissions.find((p) => {
return p.name === ComponentAction.StreamItems;
});
if (permission && !permission.content_types!.includes(ContentType.ExtensionRepo)) {
permission.content_types!.push(ContentType.ExtensionRepo);
await this.application!.changeItem(batchManager.uuid, (m) => {
const mutator = m as ComponentMutator;
mutator.permissions = permissions;
});
needsSync = true;
}
if (needsSync) {
this.application!.saveItem(batchManager.uuid);
}
}
batchManagerTemplateContent() {
const url = this.batchMgrUrl;
if (!url) {
throw Error('window._batch_manager_location must be set.');
}
const packageInfo = {
name: 'Batch Manager',
identifier: this.batchManagerId
};
const allContentType = dictToArray(ContentType);
const content = FillItemContent({
name: packageInfo.name,
area: 'modal',
package_info: packageInfo,
permissions: [
{
name: ComponentAction.StreamItems,
content_types: allContentType
}
]
});
if (isDesktopApplication()) {
content.local_url = this.batchMgrUrl;
} else {
content.hosted_url = this.batchMgrUrl;
}
return content;
}
}

View File

@@ -142,7 +142,7 @@ export class AppState {
this.noAccountWarning.reset();
}
this.actionsMenu.reset();
this.unsubApp();
this.unsubApp?.();
this.unsubApp = undefined;
this.observers.length = 0;
this.appEventObserverRemovers.forEach((remover) => remover());

View File

@@ -98,7 +98,7 @@ export class NoteTagsState {
clearAutocompleteSearch(): void {
this.setAutocompleteSearchQuery('');
this.searchActiveNoteAutocompleteTags();
this.setAutocompleteTagResults([]);
}
async createAndAddNewTag(): Promise<void> {
@@ -198,15 +198,9 @@ export class NoteTagsState {
async removeTagFromActiveNote(tag: SNTag): Promise<void> {
const { activeNote } = this;
if (activeNote) {
const descendantTags = this.application.getTagDescendants(tag);
const tagsToRemove = [...descendantTags, tag];
await Promise.all(
tagsToRemove.map(async (tag) => {
await this.application.changeItem(tag.uuid, (mutator) => {
mutator.removeItemAsRelationship(activeNote);
});
})
);
await this.application.changeItem(tag.uuid, (mutator) => {
mutator.removeItemAsRelationship(activeNote);
});
this.application.sync();
this.reloadTags();
}

View File

@@ -346,17 +346,11 @@ export class NotesState {
async removeTagFromSelectedNotes(tag: SNTag): Promise<void> {
const selectedNotes = Object.values(this.selectedNotes);
const descendantTags = this.application.getTagDescendants(tag);
const tagsToRemove = [...descendantTags, tag];
await Promise.all(
tagsToRemove.map(async (tag) => {
await this.application.changeItem(tag.uuid, (mutator) => {
for (const note of selectedNotes) {
mutator.removeItemAsRelationship(note);
}
});
})
);
await this.application.changeItem(tag.uuid, (mutator) => {
for (const note of selectedNotes) {
mutator.removeItemAsRelationship(note);
}
});
this.application.sync();
}

View File

@@ -213,11 +213,6 @@ export class WebApplication extends SNApplication {
async openModalComponent(component: SNComponent): Promise<void> {
switch (component.package_info?.identifier) {
case 'org.standardnotes.batch-manager':
if (!await this.authorizeBatchManagerAccess()) {
return;
}
break;
case 'org.standardnotes.cloudlink':
if (!await this.authorizeCloudLinkAccess()) {
return;

View File

@@ -40,8 +40,8 @@ export class PureViewCtrl<P = CtrlProps, S = CtrlState> {
}
deinit(): void {
this.unsubApp();
this.unsubState();
this.unsubApp?.();
this.unsubState?.();
for (const disposer of this.reactionDisposers) {
disposer();
}

View File

@@ -92,7 +92,6 @@
action="self.selectedMenuItem(true); self.toggleWebPrefKey(self.prefKeyMarginResizers)"
circle="self.state.marginResizersEnabled ? 'success' : 'neutral'",
desc="'Allows for editor left and right margins to be resized'",
faded='!self.state.marginResizersEnabled',
label="'Margin Resizers'"
)
.sk-app-bar-item(

View File

@@ -285,9 +285,6 @@ class EditorViewCtrl extends PureViewCtrl<unknown, EditorState> {
if (note.dirty) {
this.showSavingStatus();
}
if (note.safeText().length === 0 && !showProtectedWarning) {
this.focusTitle();
}
}
async dismissProtectedWarning() {

View File

@@ -411,6 +411,8 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
await this.flushUI();
await this.reloadNotes();
await this.appState.noteTags.reloadTags();
const noteTitleEditorElement = document.getElementById('note-title-editor');
noteTitleEditorElement?.focus();
}
async handleTagChange(tag: SNTag) {
@@ -789,7 +791,10 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
return candidate.uuid === this.activeEditorNote?.uuid;
});
if (currentIndex + 1 < displayableNotes.length) {
this.selectNote(displayableNotes[currentIndex + 1]);
const nextNote = displayableNotes[currentIndex + 1];
this.selectNote(nextNote);
const nextNoteElement = document.getElementById(`note-${nextNote.uuid}`);
nextNoteElement?.focus();
}
}
@@ -806,7 +811,10 @@ class NotesViewCtrl extends PureViewCtrl<unknown, NotesCtrlState> {
const displayableNotes = this.state.notes;
const currentIndex = displayableNotes.indexOf(this.activeEditorNote!);
if (currentIndex - 1 >= 0) {
this.selectNote(displayableNotes[currentIndex - 1]);
const previousNote = displayableNotes[currentIndex - 1];
this.selectNote(previousNote);
const previousNoteElement = document.getElementById(`note-${previousNote.uuid}`);
previousNoteElement?.focus();
return true;
} else {
return false;

View File

@@ -136,7 +136,7 @@ $footer-height: 32px;
top: 0;
right: 0;
z-index: $z-index-panel-resizer;
width: 8px;
width: 2px;
height: 100%;
position: absolute;
cursor: col-resize;

View File

@@ -1,324 +1,9 @@
/* Components and utilities that are good candidates for extraction to StyleKit. */
.border-2 {
border-width: 2px;
}
.border-b-1 {
border-bottom-width: 1px;
}
.border-background {
border-color: var(--sn-stylekit-background-color);
}
.border-transparent {
border-color: transparent;
}
.border-info {
border-color: var(--sn-stylekit-info-color);
}
.border-neutral {
border-color: var(--sn-stylekit-neutral-color);
}
.bg-border {
background-color: var(--sn-stylekit-border-color);
}
.bg-clip-padding {
background-clip: padding-box;
}
.bg-neutral {
background-color: var(--sn-stylekit-neutral-color);
}
.focus-within\:border-background:focus-within {
border-color: var(--sn-stylekit-background-color);
}
.focus\:border-bottom:focus {
border-bottom: 2px solid var(--sn-stylekit-info-color);
}
.grid {
display: grid;
}
.justify-start {
justify-content: flex-start;
}
.my-2 {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.ml-1 {
margin-left: 0.25rem;
}
.mr-1 {
margin-right: 0.25rem;
}
.mr-10 {
margin-right: 2.5rem;
}
.-mt-1 {
margin-top: -0.25rem;
}
.-mr-1 {
margin-right: -0.25rem;
}
.-mr-2 {
margin-right: -0.5rem;
}
.py-1 {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.pl-1 {
padding-left: 0.25rem;
}
.pr-2 {
padding-right: 0.5rem;
}
.px-1 {
padding-left: 0.25rem;
padding-right: 0.25rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.py-1\.5 {
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.py-8 {
padding-top: 2rem;
padding-bottom: 2rem;
}
.outline-none {
outline: none;
}
.color-foreground {
color: var(--sn-stylekit-foreground-color);
}
.color-danger {
color: var(--sn-stylekit-danger-color);
}
.color-info {
color: var(--sn-stylekit-info-color);
}
.ring-info {
box-shadow: 0 0 0 2px var(--sn-stylekit-info-color);
}
.inner-ring-info {
box-shadow: inset 0 0 0 2px var(--sn-stylekit-info-color);
}
.focus\:bg-contrast:focus {
@extend .bg-contrast;
}
.hover\:color-text:hover {
@extend .color-text;
}
.focus\:color-text:focus {
@extend .color-text;
}
.hover\:bg-secondary-contrast:hover {
@extend .bg-secondary-contrast;
}
.focus\:bg-secondary-contrast:focus {
@extend .bg-secondary-contrast;
}
.focus\:inner-ring-info:focus {
@extend .inner-ring-info;
}
.focus\:ring-info:focus {
@extend .ring-info;
}
.focus-within\:ring-info:focus-within {
@extend .ring-info;
}
.text-left {
text-align: left;
}
.text-3xl {
font-size: 1.875rem;
line-height: 2.25rem;
}
.w-0 {
width: 0;
}
.w-3\.5 {
width: 0.875rem;
}
.w-5 {
width: 1.25rem;
}
.w-8 {
width: 2rem;
}
.max-w-290px {
max-width: 290px;
}
.max-w-xs {
max-width: 20rem;
}
.max-w-40 {
max-width: 10rem;
}
.min-w-5 {
min-width: 1.25rem;
}
.min-w-40 {
min-width: 10rem;
}
.h-1px {
height: 1px;
}
.h-0 {
height: 0;
}
.h-3\.5 {
height: 0.875rem;
}
.h-4\.5 {
height: 1.125rem;
}
.h-5 {
height: 1.25rem;
}
.h-6 {
height: 1.5rem;
}
.h-7 {
height: 1.75rem;
}
.h-8 {
height: 2rem;
}
.h-9 {
height: 2.25rem;
}
.h-10 {
height: 2.5rem;
}
.h-18 {
height: 4.5rem;
}
.h-90vh {
height: 90vh;
}
.max-h-120 {
max-height: 30rem;
}
.min-h-5 {
min-height: 1.25rem;
}
.fixed {
position: fixed;
}
.overflow-y-auto {
overflow-y: auto;
}
.overflow-auto {
overflow: auto;
}
.overflow-hidden {
overflow: hidden;
}
.overflow-ellipsis {
text-overflow: ellipsis;
}
.items-start {
align-items: flex-start;
}
.p-2 {
padding: 0.5rem;
}
.flex-wrap {
flex-wrap: wrap;
}
.whitespace-pre-wrap {
white-space: pre-wrap;
}
.whitespace-nowrap {
white-space: nowrap;
}
.w-80 {
width: 20rem;
}
.w-70 {
width: 17.5rem;
}
/**
* A button that is just an icon. Separated from .sn-button because there
* is almost no style overlap.

View File

@@ -32,14 +32,12 @@
data-default-sync-server="<%= env.DEV_DEFAULT_SYNC_SERVER %>"
data-next-version-sync-server="<%= env.DEV_NEXT_VERSION_SYNC_SERVER %>"
data-extensions-manager-location="<%= env.DEV_EXTENSIONS_MANAGER_LOCATION %>"
data-batch-manager-location="<%= env.DEV_BATCH_MANAGER_LOCATION %>"
data-bugsnag-api-key="<%= env.DEV_BUGSNAG_API_KEY %>"
>
<script>
window._default_sync_server = document.body.dataset.defaultSyncServer || "https://sync.standardnotes.org";
window._next_version_sync_server = document.body.dataset.nextVersionSyncServer || "https://api.standardnotes.com";
window._extensions_manager_location = document.body.dataset.extensionsManagerLocation || "public/extensions/extensions-manager/dist/index.html";
window._batch_manager_location = document.body.dataset.batchManagerLocation || "public/extensions/batch-manager/dist/index.min.html";
window._bugsnag_api_key = document.body.dataset.bugsnagApiKey;
</script>
<application-group-view />

View File

@@ -1,6 +1,6 @@
{
"name": "standard-notes-web",
"version": "3.8.2",
"version": "3.8.3",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
@@ -14,7 +14,7 @@
"bundle": "webpack --config webpack.prod.js && yarn tsc",
"bundle:desktop": "webpack --config webpack.prod.js --env.platform='desktop'",
"bundle:desktop:beta": "webpack --config webpack.prod.js --env.platform='desktop' --env.public_beta='true'",
"build": "bundle install && yarn install --pure-lockfile && bundle exec rails assets:precompile && yarn bundle",
"build": "bundle install && yarn install --frozen-lockfile && bundle exec rails assets:precompile && yarn bundle",
"submodules": "git submodule update --init --force",
"lint": "eslint --fix app/assets/javascripts",
"tsc": "tsc --project app/assets/javascripts/tsconfig.json"
@@ -55,7 +55,7 @@
"pug-loader": "^2.4.0",
"sass-loader": "^8.0.2",
"serve-static": "^1.14.1",
"sn-stylekit": "5.1.0",
"sn-stylekit": "5.2.1",
"ts-loader": "^8.0.17",
"typescript": "4.2.3",
"typescript-eslint": "0.0.1-alpha.0",
@@ -71,7 +71,7 @@
"@reach/checkbox": "^0.13.2",
"@reach/dialog": "^0.13.0",
"@standardnotes/sncrypto-web": "1.2.10",
"@standardnotes/snjs": "2.6.3",
"@standardnotes/snjs": "2.7.1",
"mobx": "^6.1.6",
"mobx-react-lite": "^3.2.0",
"preact": "^10.5.12"

View File

@@ -1936,10 +1936,10 @@
"@standardnotes/sncrypto-common" "^1.2.7"
libsodium-wrappers "^0.7.8"
"@standardnotes/snjs@2.6.3":
version "2.6.3"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.6.3.tgz#7677899c050b0616d994423fd4ec9caf03394f35"
integrity sha512-5pWh+BPVPpd6JlP3avo2puGk9EWUaH0+6Y1fN9rMR8oLZ2oc8Dffiy5S4TLNm8zL4q504oMlm1/ALkwwZpKLEQ==
"@standardnotes/snjs@2.7.1":
version "2.7.1"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.7.1.tgz#e8073942fe9a8f762989ab75df94407b9934ea3e"
integrity sha512-kCZU4Tx6vNYcEeh22ax8Vfr/LYEbgAqI9nSC6jB8MTg6nJzCuh4b/nngW+yVUz91phu156XN0YeMyj7HJ3DWVw==
dependencies:
"@standardnotes/auth" "^2.0.0"
"@standardnotes/sncrypto-common" "^1.2.9"
@@ -7815,10 +7815,10 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
sn-stylekit@5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/sn-stylekit/-/sn-stylekit-5.1.0.tgz#97ce7323834ff7f3645ed4463beb3ad4e42f7e7e"
integrity sha512-SjKJYGRnR1iCVtllqJyW9/gWV7V56MJrkXHEo5+C9Ch3syRRlG3k+AwC0vC8ms6PWxpHJUdCHLQROvFOBPQuCw==
sn-stylekit@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/sn-stylekit/-/sn-stylekit-5.2.1.tgz#d56a38017d6b45f5c2ebcf7b2c2db94c50a26e27"
integrity sha512-kupuH9XlPy5TuO2a2E+sLQnF41VQ0a4cwHRltNhQSI6O135n4HkkDYgyEAvy5DKcc0aun4KHy5mz9kYefQvciw==
snapdragon-node@^2.0.1:
version "2.1.1"