diff --git a/README.md b/README.md
index 2e6ea90fa..fd153f659 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,13 @@
Standard Notes is a simple and private notes app available on most platforms, including Web, Mac, Windows, Linux, iOS, and Android. It focuses on simplicity, and encrypts data locally before it ever touches a cloud. This means no one can read your notes but you (not even us).
-
+
[](https://github.com/standardnotes/desktop/releases)
[](https://github.com/standardnotes/web/blob/main/LICENSE)
-[](https://standardnotes.org/slack)
+[](https://standardnotes.com/slack)
[](https://twitter.com/standardnotes)
@@ -19,25 +19,25 @@ Standard Notes is a simple and private notes app available on most platforms, in
- Simple and easy to use
- Fast and encrypted cross-platform sync
- Free sync on unlimited devices
-- Extensible with editors (such as Markdown and Code), themes, and components (like Folders and Autocomplete Tags). Learn more about [Extended](https://standardnotes.org/extensions).
+- Extensible with editors (such as Markdown and Code), themes, and components (like Folders and Autocomplete Tags). Learn more about [Extended](https://standardnotes.com/extensions).
- Open-source and the option to self-host your notes server. You can [host your own Standard Server](https://docs.standardnotes.org/self-hosting/getting-started) in a few easy steps.
-- A strong focus on longevity and sustainability. [Learn more](https://standardnotes.org/longevity).
+- A strong focus on longevity and sustainability. [Learn more](https://standardnotes.com/longevity).
### Creating your private notes account
1. Launch the web app at [app.standardnotes.org](https://app.standardnotes.org).
2. Click Register to create your private notes account.
3. Download Standard Notes on all your devices.
- - [Mac](https://standardnotes.org/download/mac)
- - [Windows](https://standardnotes.org/download/windows)
- - [Linux](https://standardnotes.org/download/linux)
- - [iOS](https://standardnotes.org/download/https://itunes.apple.com/us/app/standard-notes/id1285392450?mt=8)
+ - [Mac](https://standardnotes.com/download/mac)
+ - [Windows](https://standardnotes.com/download/windows)
+ - [Linux](https://standardnotes.com/download/linux)
+ - [iOS](https://standardnotes.com/download/https://itunes.apple.com/us/app/standard-notes/id1285392450?mt=8)
- [Android](https://play.google.com/store/apps/details?id=com.standardnotes)
4. You're all set to begin enjoying a new, more freeing notes life. Standard Notes comes out of the box with end-to-end encrypted sync on all your devices.
### Do More
-If you're looking to power up your experience with extensions, and help support future development, [learn more about Extended](https://standardnotes.org/extensions). Extended offers:
+If you're looking to power up your experience with extensions, and help support future development, [learn more about Extended](https://standardnotes.com/extensions). Extended offers:
- Powerful editors, including the Plus Editor, Simple Markdown, Advanced Markdown, Code Editor, Vim Editor, and the popular Simple Task Editor.
- Beautiful themes to help you find inspiration in any mood, like Midnight, Focused, Futura, Titanium, and Solarized Dark.
@@ -52,11 +52,11 @@ It's no secret we love to write. Standard Notes has become a dependable environm
### Plug In
-Plug in to the community of note-lovers and privacy-enthusiasts. Join us on [Slack](https://standardnotes.org/slack), on our [GitHub forum](https://forum.standardnotes.org), and follow new updates on [Twitter](https://twitter.com/StandardNotes).
+Plug in to the community of note-lovers and privacy-enthusiasts. Join us on [Slack](https://standardnotes.com/slack), on our [GitHub forum](https://forum.standardnotes.org), and follow new updates on [Twitter](https://twitter.com/StandardNotes).
Developers can create and publish their own extensions. Visit the [documentation hub](https://docs.standardnotes.org/) to learn more.
-Questions? Find answers on our [Help page](https://standardnotes.org/help).
+Questions? Find answers on our [Help page](https://standardnotes.com/help).
🙏
diff --git a/SECURITY.md b/SECURITY.md
index 6561eba2f..7b4970d2d 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -12,7 +12,7 @@ Thank you for your work in helping keep Standard Notes safe and secure. If you b
# In-scope
-- Security issues in any current release of Standard Notes. Our product downloads are available on our homepage at https://standardnotes.org, and our source code is available at https://github.com/standardnotes.
+- Security issues in any current release of Standard Notes. Our product downloads are available on our homepage at https://standardnotes.com, and our source code is available at https://github.com/standardnotes.
# Exclusions
diff --git a/app/assets/javascripts/components/NoteTag.tsx b/app/assets/javascripts/components/NoteTag.tsx
index 3afcc3d35..f08c94ef8 100644
--- a/app/assets/javascripts/components/NoteTag.tsx
+++ b/app/assets/javascripts/components/NoteTag.tsx
@@ -97,7 +97,6 @@ export const NoteTag = observer(({ appState, tag }: Props) => {
ref={deleteTagRef}
type="button"
className="ml-2 -mr-1 border-0 p-0 bg-transparent cursor-pointer flex"
- onFocus={onFocus}
onBlur={onBlur}
onClick={onDeleteTagClick}
>
diff --git a/app/assets/javascripts/components/NotesContextMenu.tsx b/app/assets/javascripts/components/NotesContextMenu.tsx
index abd4fa354..3afd7df89 100644
--- a/app/assets/javascripts/components/NotesContextMenu.tsx
+++ b/app/assets/javascripts/components/NotesContextMenu.tsx
@@ -2,13 +2,19 @@ import { AppState } from '@/ui_models/app_state';
import { toDirective, useCloseOnBlur, useCloseOnClickOutside } from './utils';
import { observer } from 'mobx-react-lite';
import { NotesOptions } from './NotesOptions';
-import { useRef } from 'preact/hooks';
+import { useCallback, useEffect, useRef } from 'preact/hooks';
type Props = {
appState: AppState;
};
const NotesContextMenu = observer(({ appState }: Props) => {
+ const {
+ contextMenuOpen,
+ contextMenuPosition,
+ contextMenuMaxHeight,
+ } = appState.notes;
+
const contextMenuRef = useRef();
const [closeOnBlur] = useCloseOnBlur(
contextMenuRef,
@@ -20,13 +26,24 @@ const NotesContextMenu = observer(({ appState }: Props) => {
(open: boolean) => appState.notes.setContextMenuOpen(open)
);
- return appState.notes.contextMenuOpen ? (
+ const reloadContextMenuLayout = useCallback(() => {
+ appState.notes.reloadContextMenuLayout();
+ }, [appState.notes]);
+
+ useEffect(() => {
+ window.addEventListener('resize', reloadContextMenuLayout);
+ return () => {
+ window.removeEventListener('resize', reloadContextMenuLayout);
+ };
+ }, [reloadContextMenuLayout]);
+
+ return contextMenuOpen ? (
diff --git a/app/assets/javascripts/components/SearchOptions.tsx b/app/assets/javascripts/components/SearchOptions.tsx
index 2099169a4..ddd320d93 100644
--- a/app/assets/javascripts/components/SearchOptions.tsx
+++ b/app/assets/javascripts/components/SearchOptions.tsx
@@ -11,6 +11,7 @@ import {
} from '@reach/disclosure';
import { Switch } from './Switch';
import { observer } from 'mobx-react-lite';
+import { useEffect } from 'react';
type Props = {
appState: AppState;
@@ -31,6 +32,7 @@ const SearchOptions = observer(({ appState }: Props) => {
top: 0,
right: 0,
});
+ const [maxWidth, setMaxWidth] = useState('auto');
const buttonRef = useRef();
const panelRef = useRef();
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(panelRef, setOpen);
@@ -44,15 +46,27 @@ const SearchOptions = observer(({ appState }: Props) => {
}
}
+ const updateWidthAndPosition = () => {
+ const rect = buttonRef.current.getBoundingClientRect();
+ setMaxWidth(rect.right - 16);
+ setPosition({
+ top: rect.bottom,
+ right: document.body.clientWidth - rect.right,
+ });
+ };
+
+ useEffect(() => {
+ window.addEventListener('resize', updateWidthAndPosition);
+ return () => {
+ window.removeEventListener('resize', updateWidthAndPosition);
+ };
+ }, []);
+
return (
{
- const rect = buttonRef.current.getBoundingClientRect();
- setPosition({
- top: rect.bottom,
- right: document.body.clientWidth - rect.right,
- });
+ updateWidthAndPosition();
setOpen(!open);
}}
>
@@ -68,8 +82,9 @@ const SearchOptions = observer(({ appState }: Props) => {
ref={panelRef}
style={{
...position,
+ maxWidth,
}}
- className="sn-dropdown sn-dropdown--animated min-w-80 fixed grid gap-2 py-2"
+ className="sn-dropdown sn-dropdown--animated w-80 fixed grid gap-2 py-2"
onBlur={closeOnBlur}
>
{
setRefreshing(true);
const response = await application.getSessions();
- if ('error' in response || !response.data) {
+ if ('error' in response || isNullOrUndefined(response.data)) {
if (response.error?.message) {
setErrorMessage(response.error.message);
} else {
setErrorMessage('An unknown error occured while loading sessions.');
}
} else {
- const sessions = response.data;
+ const sessions = response.data as RemoteSession[];
setSessions(sessions);
setErrorMessage('');
}
diff --git a/app/assets/javascripts/ui_models/app_state/note_tags_state.ts b/app/assets/javascripts/ui_models/app_state/note_tags_state.ts
index 3f46a0781..3ad0900e8 100644
--- a/app/assets/javascripts/ui_models/app_state/note_tags_state.ts
+++ b/app/assets/javascripts/ui_models/app_state/note_tags_state.ts
@@ -30,9 +30,6 @@ export class NoteTagsState {
autocompleteTagHintVisible: computed,
- clearAutocompleteSearch: action,
- focusNextTag: action,
- focusPreviousTag: action,
setAutocompleteInputFocused: action,
setAutocompleteSearchQuery: action,
setAutocompleteTagHintFocused: action,
@@ -41,7 +38,6 @@ export class NoteTagsState {
setFocusedTagUuid: action,
setTags: action,
setTagsContainerMaxWidth: action,
- reloadTags: action,
});
appEventListeners.push(
diff --git a/app/assets/javascripts/ui_models/app_state/notes_state.ts b/app/assets/javascripts/ui_models/app_state/notes_state.ts
index 66d1ad024..9b59fc6c9 100644
--- a/app/assets/javascripts/ui_models/app_state/notes_state.ts
+++ b/app/assets/javascripts/ui_models/app_state/notes_state.ts
@@ -28,6 +28,7 @@ export class NotesState {
top: 0,
left: 0,
};
+ contextMenuClickLocation: { x: number, y: number } = { x: 0, y: 0 };
contextMenuMaxHeight: number | 'auto' = 'auto';
showProtectedWarning = false;
@@ -47,6 +48,7 @@ export class NotesState {
trashedNotesCount: computed,
setContextMenuOpen: action,
+ setContextMenuClickLocation: action,
setContextMenuPosition: action,
setContextMenuMaxHeight: action,
setShowProtectedWarning: action,
@@ -183,6 +185,10 @@ export class NotesState {
this.contextMenuOpen = open;
}
+ setContextMenuClickLocation(location: { x: number, y: number }): void {
+ this.contextMenuClickLocation = location;
+ }
+
setContextMenuPosition(position: {
top?: number;
left: number;
@@ -195,6 +201,60 @@ export class NotesState {
this.contextMenuMaxHeight = maxHeight;
}
+ reloadContextMenuLayout(): void {
+ const { clientHeight } = document.documentElement;
+ const defaultFontSize = window.getComputedStyle(
+ document.documentElement
+ ).fontSize;
+ const maxContextMenuHeight = parseFloat(defaultFontSize) * 30;
+ const footerHeight = 32;
+
+ // Open up-bottom is default behavior
+ let openUpBottom = true;
+
+ const bottomSpace = clientHeight - footerHeight - this.contextMenuClickLocation.y;
+ const upSpace = this.contextMenuClickLocation.y;
+
+ // If not enough space to open up-bottom
+ if (maxContextMenuHeight > bottomSpace) {
+ // If there's enough space, open bottom-up
+ if (upSpace > maxContextMenuHeight) {
+ openUpBottom = false;
+ this.setContextMenuMaxHeight(
+ 'auto'
+ );
+ // Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space
+ } else {
+ if (upSpace > bottomSpace) {
+ this.setContextMenuMaxHeight(
+ upSpace - 2
+ );
+ openUpBottom = false;
+ } else {
+ this.setContextMenuMaxHeight(
+ bottomSpace - 2
+ );
+ }
+ }
+ } else {
+ this.setContextMenuMaxHeight(
+ 'auto'
+ );
+ }
+
+ if (openUpBottom) {
+ this.setContextMenuPosition({
+ top: this.contextMenuClickLocation.y,
+ left: this.contextMenuClickLocation.x,
+ });
+ } else {
+ this.setContextMenuPosition({
+ bottom: clientHeight - this.contextMenuClickLocation.y,
+ left: this.contextMenuClickLocation.x,
+ });
+ }
+ }
+
async changeSelectedNotes(
mutate: (mutator: NoteMutator) => void
): Promise {
diff --git a/app/assets/javascripts/views/application/application_view.ts b/app/assets/javascripts/views/application/application_view.ts
index 3dbf4ab91..4fecc6f9e 100644
--- a/app/assets/javascripts/views/application/application_view.ts
+++ b/app/assets/javascripts/views/application/application_view.ts
@@ -170,7 +170,7 @@ class ApplicationViewCtrl extends PureViewCtrl 0')
+ .note-flags.flex.flex-wrap(ng-show='self.noteFlags[note.uuid].length > 0')
.flag(ng-class='flag.class', ng-repeat='flag in self.noteFlags[note.uuid]')
.label {{flag.text}}
.name(ng-show='note.title')
diff --git a/app/assets/javascripts/views/notes/notes_view.ts b/app/assets/javascripts/views/notes/notes_view.ts
index 6ed9e41c1..5ad8a32c8 100644
--- a/app/assets/javascripts/views/notes/notes_view.ts
+++ b/app/assets/javascripts/views/notes/notes_view.ts
@@ -310,58 +310,11 @@ class NotesViewCtrl extends PureViewCtrl {
await this.selectNote(note, true);
}
if (this.state.selectedNotes[note.uuid]) {
- const { clientHeight } = document.documentElement;
- const defaultFontSize = window.getComputedStyle(
- document.documentElement
- ).fontSize;
- const maxContextMenuHeight = parseFloat(defaultFontSize) * 30;
- const footerHeight = 32;
-
- // Open up-bottom is default behavior
- let openUpBottom = true;
-
- const bottomSpace = clientHeight - footerHeight - e.clientY;
- const upSpace = e.clientY;
-
- // If not enough space to open up-bottom
- if (maxContextMenuHeight > bottomSpace) {
- // If there's enough space, open bottom-up
- if (upSpace > maxContextMenuHeight) {
- openUpBottom = false;
- this.appState.notes.setContextMenuMaxHeight(
- 'auto'
- );
- // Else, reduce max height (menu will be scrollable) and open in whichever direction there's more space
- } else {
- if (upSpace > bottomSpace) {
- this.appState.notes.setContextMenuMaxHeight(
- upSpace - 2
- );
- openUpBottom = false;
- } else {
- this.appState.notes.setContextMenuMaxHeight(
- bottomSpace - 2
- );
- }
- }
- } else {
- this.appState.notes.setContextMenuMaxHeight(
- 'auto'
- );
- }
-
- if (openUpBottom) {
- this.appState.notes.setContextMenuPosition({
- top: e.clientY,
- left: e.clientX,
- });
- } else {
- this.appState.notes.setContextMenuPosition({
- bottom: clientHeight - e.clientY,
- left: e.clientX,
- });
- }
-
+ this.appState.notes.setContextMenuClickLocation({
+ x: e.clientX,
+ y: e.clientY,
+ });
+ this.appState.notes.reloadContextMenuLayout();
this.appState.notes.setContextMenuOpen(true);
}
}
diff --git a/app/assets/stylesheets/_notes.scss b/app/assets/stylesheets/_notes.scss
index aa7a15c44..0ccf25e65 100644
--- a/app/assets/stylesheets/_notes.scss
+++ b/app/assets/stylesheets/_notes.scss
@@ -169,6 +169,7 @@
flex-direction: row;
align-items: center;
margin-bottom: 8px;
+ margin-top: -4px;
.flag {
padding: 4px;
@@ -176,6 +177,7 @@
padding-right: 6px;
border-radius: var(--sn-stylekit-general-border-radius);
margin-right: 4px;
+ margin-top: 4px;
&.info {
background-color: var(--sn-stylekit-info-color);
diff --git a/app/assets/templates/directives/actions-menu.pug b/app/assets/templates/directives/actions-menu.pug
index c3d672a6c..c7f4aa338 100644
--- a/app/assets/templates/directives/actions-menu.pug
+++ b/app/assets/templates/directives/actions-menu.pug
@@ -1,7 +1,7 @@
.sn-component
.sk-menu-panel.dropdown-menu
a.no-decoration(
- href='https://standardnotes.org/extensions',
+ href='https://standardnotes.com/extensions',
ng-if='self.state.extensions.length == 0',
rel='noopener',
target='blank'
diff --git a/app/assets/templates/directives/component-view.pug b/app/assets/templates/directives/component-view.pug
index 17d2b0c5f..e8bd06759 100644
--- a/app/assets/templates/directives/component-view.pug
+++ b/app/assets/templates/directives/component-view.pug
@@ -15,7 +15,7 @@
.sk-app-bar-item-column
div
a.sk-label.sk-base(
- href='https://dashboard.standardnotes.org',
+ href='https://dashboard.standardnotes.com',
rel='noopener',
target='_blank'
)
@@ -29,7 +29,7 @@
.sk-app-bar-item
.sk-app-bar-item-column
a.sn-button.small.warning(
- href='https://standardnotes.org/help/41/expired',
+ href='https://standardnotes.com/help/41/expired',
rel='noopener',
target='_blank'
) Help
diff --git a/app/assets/templates/directives/editor-menu.pug b/app/assets/templates/directives/editor-menu.pug
index d7e8efe65..67710575f 100644
--- a/app/assets/templates/directives/editor-menu.pug
+++ b/app/assets/templates/directives/editor-menu.pug
@@ -26,7 +26,7 @@
ng-if='editor.conflictOf'
) Conflicted copy
a.no-decoration(
- href='https://standardnotes.org/extensions',
+ href='https://standardnotes.com/extensions',
ng-if='self.state.editors.length == 0',
rel='noopener',
target='blank'
diff --git a/app/assets/templates/directives/permissions-modal.pug b/app/assets/templates/directives/permissions-modal.pug
index 0ae709c0e..f449516a7 100644
--- a/app/assets/templates/directives/permissions-modal.pug
+++ b/app/assets/templates/directives/permissions-modal.pug
@@ -17,10 +17,10 @@
| Extensions use an offline messaging system to communicate. Learn more at
|
a.sk-a.info(
- href='https://standardnotes.org/permissions',
+ href='https://standardnotes.com/permissions',
rel='noopener',
target='_blank'
- ) https://standardnotes.org/permissions.
+ ) https://standardnotes.com/permissions.
.sk-panel-footer
button.sn-button.info.block.w-full.text-base.py-3(
ng-click='ctrl.accept()'
diff --git a/package.json b/package.json
index dec3adcb4..b9fa15976 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "standard-notes-web",
- "version": "3.8.9",
+ "version": "3.8.13",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
@@ -71,7 +71,7 @@
"@reach/checkbox": "^0.13.2",
"@reach/dialog": "^0.13.0",
"@standardnotes/sncrypto-web": "1.2.10",
- "@standardnotes/snjs": "2.7.6",
+ "@standardnotes/snjs": "2.7.9",
"mobx": "^6.1.6",
"mobx-react-lite": "^3.2.0",
"preact": "^10.5.12"
diff --git a/yarn.lock b/yarn.lock
index 9b3858e4f..7699cacc1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1936,10 +1936,10 @@
"@standardnotes/sncrypto-common" "^1.2.7"
libsodium-wrappers "^0.7.8"
-"@standardnotes/snjs@2.7.6":
- version "2.7.6"
- resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.7.6.tgz#f0b965bfad61e93af6803ec2698c02b1489d1073"
- integrity sha512-E1Gj02gWvqypVpPed2YCSUnMUi2ZqFsb8NT2Jo8dqFS6Wk3FMzptWlFM/DuTifyzaYsmwPkXv1v5KpeU+dA+CQ==
+"@standardnotes/snjs@2.7.9":
+ version "2.7.9"
+ resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.7.9.tgz#8b2a5d0f6248e1872b2fe68a73307f754528b4c3"
+ integrity sha512-FNiLXdaUb2+WpCteTbfg/iiv4k0UXFzz57RnY1O8X40vEVFVY8VjrJuJxxwDuZqidUZeLIdTMlmfdKWoD+NRDA==
dependencies:
"@standardnotes/auth" "^2.0.0"
"@standardnotes/sncrypto-common" "^1.2.9"