Merge branch 'release/10.16.0'

This commit is contained in:
Mo
2022-03-10 11:18:01 -06:00
55 changed files with 1752 additions and 520 deletions

View File

@@ -4,7 +4,6 @@ declare global {
interface Window { interface Window {
bugsnagApiKey?: string; bugsnagApiKey?: string;
dashboardUrl?: string; dashboardUrl?: string;
defaultFilesHost: string;
defaultSyncServer: string; defaultSyncServer: string;
devAccountEmail?: string; devAccountEmail?: string;
devAccountPassword?: string; devAccountPassword?: string;
@@ -30,7 +29,6 @@ import { isDev } from './utils';
const startApplication: StartApplication = async function startApplication( const startApplication: StartApplication = async function startApplication(
defaultSyncServerHost: string, defaultSyncServerHost: string,
defaultFilesHostHost: string,
bridge: Bridge, bridge: Bridge,
enableUnfinishedFeatures: boolean, enableUnfinishedFeatures: boolean,
webSocketUrl: string webSocketUrl: string
@@ -40,7 +38,6 @@ const startApplication: StartApplication = async function startApplication(
const mainApplicationGroup = new ApplicationGroup( const mainApplicationGroup = new ApplicationGroup(
defaultSyncServerHost, defaultSyncServerHost,
defaultFilesHostHost,
bridge, bridge,
enableUnfinishedFeatures ? Runtime.Dev : Runtime.Prod, enableUnfinishedFeatures ? Runtime.Dev : Runtime.Prod,
webSocketUrl webSocketUrl
@@ -75,7 +72,6 @@ const startApplication: StartApplication = async function startApplication(
if (IsWebPlatform) { if (IsWebPlatform) {
startApplication( startApplication(
window.defaultSyncServer, window.defaultSyncServer,
window.defaultFilesHost,
new BrowserBridge(WebAppVersion), new BrowserBridge(WebAppVersion),
window.enabledUnfinishedFeatures, window.enabledUnfinishedFeatures,
window.websocketUrl window.websocketUrl

View File

@@ -24,23 +24,23 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
({ application, appState, setMenuPane, closeMenu }) => { ({ application, appState, setMenuPane, closeMenu }) => {
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false); const [isSyncingInProgress, setIsSyncingInProgress] = useState(false);
const [lastSyncDate, setLastSyncDate] = useState( const [lastSyncDate, setLastSyncDate] = useState(
formatLastSyncDate(application.getLastSyncDate() as Date) formatLastSyncDate(application.sync.getLastSyncDate() as Date)
); );
const doSynchronization = async () => { const doSynchronization = async () => {
setIsSyncingInProgress(true); setIsSyncingInProgress(true);
application application.sync
.sync({ .sync({
queueStrategy: SyncQueueStrategy.ForceSpawnNew, queueStrategy: SyncQueueStrategy.ForceSpawnNew,
checkIntegrity: true, checkIntegrity: true,
}) })
.then((res) => { .then((res) => {
if (res && res.error) { if (res && (res as any).error) {
throw new Error(); throw new Error();
} else { } else {
setLastSyncDate( setLastSyncDate(
formatLastSyncDate(application.getLastSyncDate() as Date) formatLastSyncDate(application.sync.getLastSyncDate() as Date)
); );
} }
}) })

View File

@@ -8,7 +8,6 @@ import {
removeFromArray, removeFromArray,
} from '@standardnotes/snjs'; } from '@standardnotes/snjs';
import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/constants'; import { PANEL_NAME_NOTES, PANEL_NAME_NAVIGATION } from '@/constants';
import { STRING_DEFAULT_FILE_ERROR } from '@/strings';
import { alertDialog } from '@/services/alertService'; import { alertDialog } from '@/services/alertService';
import { WebAppEvent, WebApplication } from '@/ui_models/application'; import { WebAppEvent, WebApplication } from '@/ui_models/application';
import { PureComponent } from '@/components/Abstract/PureComponent'; import { PureComponent } from '@/components/Abstract/PureComponent';
@@ -51,17 +50,10 @@ export class ApplicationView extends PureComponent<Props, State> {
appClass: '', appClass: '',
challenges: [], challenges: [],
}; };
this.onDragDrop = this.onDragDrop.bind(this);
this.onDragOver = this.onDragOver.bind(this);
this.addDragDropHandlers();
} }
deinit() { deinit() {
(this.application as unknown) = undefined; (this.application as unknown) = undefined;
window.removeEventListener('dragover', this.onDragOver, true);
window.removeEventListener('drop', this.onDragDrop, true);
(this.onDragDrop as unknown) = undefined;
(this.onDragOver as unknown) = undefined;
super.deinit(); super.deinit();
} }
@@ -145,36 +137,11 @@ export class ApplicationView extends PureComponent<Props, State> {
this.setState({ appClass }); this.setState({ appClass });
} else if (eventName === AppStateEvent.WindowDidFocus) { } else if (eventName === AppStateEvent.WindowDidFocus) {
if (!(await this.application.isLocked())) { if (!(await this.application.isLocked())) {
this.application.sync(); this.application.sync.sync();
} }
} }
} }
addDragDropHandlers() {
/**
* Disable dragging and dropping of files (but allow text) into main SN interface.
* both 'dragover' and 'drop' are required to prevent dropping of files.
* This will not prevent extensions from receiving drop events.
*/
window.addEventListener('dragover', this.onDragOver, true);
window.addEventListener('drop', this.onDragDrop, true);
}
onDragOver(event: DragEvent) {
if (event.dataTransfer?.files.length) {
event.preventDefault();
}
}
onDragDrop(event: DragEvent) {
if (event.dataTransfer?.files.length) {
event.preventDefault();
void alertDialog({
text: STRING_DEFAULT_FILE_ERROR,
});
}
}
async handleDemoSignInFromParams() { async handleDemoSignInFromParams() {
if ( if (
window.location.href.includes('demo') && window.location.href.includes('demo') &&

View File

@@ -0,0 +1,260 @@
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { MENU_MARGIN_FROM_APP_BORDER } from '@/constants';
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from '@reach/disclosure';
import VisuallyHidden from '@reach/visually-hidden';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { Icon } from '../Icon';
import { useCloseOnClickOutside } from '../utils';
import { ChallengeReason, ContentType, SNFile } from '@standardnotes/snjs';
import { confirmDialog } from '@/services/alertService';
import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit';
import { parseFileName } from '@standardnotes/filepicker';
import {
PopoverFileItemAction,
PopoverFileItemActionType,
} from './PopoverFileItemAction';
import { PopoverDragNDropWrapper } from './PopoverDragNDropWrapper';
type Props = {
application: WebApplication;
appState: AppState;
onClickPreprocessing?: () => Promise<void>;
};
export const AttachedFilesButton: FunctionComponent<Props> = observer(
({ application, appState, onClickPreprocessing }) => {
const note = Object.values(appState.notes.selectedNotes)[0];
const [open, setOpen] = useState(false);
const [position, setPosition] = useState({
top: 0,
right: 0,
});
const [maxHeight, setMaxHeight] = useState<number | 'auto'>('auto');
const buttonRef = useRef<HTMLButtonElement>(null);
const panelRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
useCloseOnClickOutside(containerRef, () => {
setOpen(false);
});
const [attachedFilesCount, setAttachedFilesCount] = useState(
note ? application.items.getFilesForNote(note).length : 0
);
const reloadAttachedFilesCount = useCallback(() => {
setAttachedFilesCount(
note ? application.items.getFilesForNote(note).length : 0
);
}, [application.items, note]);
useEffect(() => {
const unregisterFileStream = application.streamItems(
ContentType.File,
() => {
reloadAttachedFilesCount();
}
);
return () => {
unregisterFileStream();
};
}, [application, reloadAttachedFilesCount]);
const toggleAttachedFilesMenu = async () => {
const rect = buttonRef.current?.getBoundingClientRect();
if (rect) {
const { clientHeight } = document.documentElement;
const footerElementRect = document
.getElementById('footer-bar')
?.getBoundingClientRect();
const footerHeightInPx = footerElementRect?.height;
if (footerHeightInPx) {
setMaxHeight(
clientHeight -
rect.bottom -
footerHeightInPx -
MENU_MARGIN_FROM_APP_BORDER
);
}
setPosition({
top: rect.bottom,
right: document.body.clientWidth - rect.right,
});
const newOpenState = !open;
if (newOpenState && onClickPreprocessing) {
await onClickPreprocessing();
}
setOpen(newOpenState);
}
};
const deleteFile = async (file: SNFile) => {
const shouldDelete = await confirmDialog({
text: `Are you sure you want to permanently delete "${file.nameWithExt}"?`,
confirmButtonStyle: 'danger',
});
if (shouldDelete) {
const deletingToastId = addToast({
type: ToastType.Loading,
message: `Deleting file "${file.nameWithExt}"...`,
});
await application.deleteItem(file);
addToast({
type: ToastType.Success,
message: `Deleted file "${file.nameWithExt}"`,
});
dismissToast(deletingToastId);
}
};
const downloadFile = async (file: SNFile) => {
appState.files.downloadFile(file);
};
const attachFileToNote = async (file: SNFile) => {
await application.items.associateFileWithNote(file, note);
};
const detachFileFromNote = async (file: SNFile) => {
await application.items.disassociateFileWithNote(file, note);
};
const toggleFileProtection = async (file: SNFile) => {
let result: SNFile | undefined;
if (file.protected) {
result = await application.protections.unprotectFile(file);
} else {
result = await application.protections.protectFile(file);
}
const isProtected = result ? result.protected : file.protected;
return isProtected;
};
const authorizeProtectedActionForFile = async (
file: SNFile,
challengeReason: ChallengeReason
) => {
const authorizedFiles =
await application.protections.authorizeProtectedActionForFiles(
[file],
challengeReason
);
const isAuthorized =
authorizedFiles.length > 0 && authorizedFiles.includes(file);
return isAuthorized;
};
const renameFile = async (file: SNFile, fileName: string) => {
const { name, ext } = parseFileName(fileName);
await application.items.renameFile(file, name, ext);
};
const handleFileAction = async (action: PopoverFileItemAction) => {
const file =
action.type !== PopoverFileItemActionType.RenameFile
? action.payload
: action.payload.file;
let isAuthorizedForAction = true;
if (
file.protected &&
action.type !== PopoverFileItemActionType.ToggleFileProtection
) {
isAuthorizedForAction = await authorizeProtectedActionForFile(
file,
ChallengeReason.AccessProtectedFile
);
}
if (!isAuthorizedForAction) {
return false;
}
switch (action.type) {
case PopoverFileItemActionType.AttachFileToNote:
await attachFileToNote(file);
break;
case PopoverFileItemActionType.DetachFileToNote:
await detachFileFromNote(file);
break;
case PopoverFileItemActionType.DeleteFile:
await deleteFile(file);
break;
case PopoverFileItemActionType.DownloadFile:
await downloadFile(file);
break;
case PopoverFileItemActionType.ToggleFileProtection: {
const isProtected = await toggleFileProtection(file);
action.callback(isProtected);
break;
}
case PopoverFileItemActionType.RenameFile:
await renameFile(file, action.payload.name);
break;
}
application.sync.sync();
return true;
};
return (
<div ref={containerRef}>
<Disclosure open={open} onChange={toggleAttachedFilesMenu}>
<DisclosureButton
onKeyDown={(event) => {
if (event.key === 'Escape') {
setOpen(false);
}
}}
ref={buttonRef}
className={`sn-icon-button border-contrast ${
attachedFilesCount > 0 ? 'py-1 px-3' : ''
}`}
>
<VisuallyHidden>Attached files</VisuallyHidden>
<Icon type="attachment-file" className="block" />
{attachedFilesCount > 0 && (
<span className="ml-2">{attachedFilesCount}</span>
)}
</DisclosureButton>
<DisclosurePanel
onKeyDown={(event) => {
if (event.key === 'Escape') {
setOpen(false);
buttonRef.current?.focus();
}
}}
ref={panelRef}
style={{
...position,
maxHeight,
}}
className="sn-dropdown sn-dropdown--animated min-w-80 max-h-120 max-w-xs flex flex-col overflow-y-auto fixed"
>
{open && (
<PopoverDragNDropWrapper
application={application}
appState={appState}
note={note}
fileActionHandler={handleFileAction}
/>
)}
</DisclosurePanel>
</Disclosure>
</div>
);
}
);

View File

@@ -0,0 +1,195 @@
import { ContentType, SNFile } from '@standardnotes/snjs';
import { FilesIllustration } from '@standardnotes/stylekit';
import { observer } from 'mobx-react-lite';
import { FunctionComponent } from 'preact';
import { StateUpdater, useCallback, useEffect, useState } from 'preact/hooks';
import { Button } from '../Button';
import { Icon } from '../Icon';
import { PopoverTabs, PopoverWrapperProps } from './PopoverDragNDropWrapper';
import { PopoverFileItem } from './PopoverFileItem';
import { PopoverFileItemActionType } from './PopoverFileItemAction';
type Props = PopoverWrapperProps & {
currentTab: PopoverTabs;
setCurrentTab: StateUpdater<PopoverTabs>;
};
export const AttachedFilesPopover: FunctionComponent<Props> = observer(
({
application,
appState,
note,
fileActionHandler,
currentTab,
setCurrentTab,
}) => {
const [attachedFiles, setAttachedFiles] = useState<SNFile[]>([]);
const [allFiles, setAllFiles] = useState<SNFile[]>([]);
const [searchQuery, setSearchQuery] = useState('');
const filesList =
currentTab === PopoverTabs.AttachedFiles ? attachedFiles : allFiles;
const filteredList =
searchQuery.length > 0
? filesList.filter(
(file) => file.nameWithExt.toLowerCase().indexOf(searchQuery) !== -1
)
: filesList;
const reloadAttachedFiles = useCallback(() => {
setAttachedFiles(
application.items
.getFilesForNote(note)
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1))
);
}, [application.items, note]);
const reloadAllFiles = useCallback(() => {
setAllFiles(
application
.getItems(ContentType.File)
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1)) as SNFile[]
);
}, [application]);
useEffect(() => {
const unregisterFileStream = application.streamItems(
ContentType.File,
() => {
reloadAttachedFiles();
reloadAllFiles();
}
);
return () => {
unregisterFileStream();
};
}, [application, reloadAllFiles, reloadAttachedFiles]);
const handleAttachFilesClick = async () => {
const uploadedFiles = await appState.files.uploadNewFile();
if (!uploadedFiles) {
return;
}
if (currentTab === PopoverTabs.AttachedFiles) {
uploadedFiles.forEach((file) => {
fileActionHandler({
type: PopoverFileItemActionType.AttachFileToNote,
payload: file,
});
});
}
};
return (
<div className="flex flex-col">
<div className="flex border-0 border-b-1 border-solid border-main">
<button
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
currentTab === PopoverTabs.AttachedFiles
? 'color-info font-medium shadow-bottom'
: 'color-text'
}`}
onClick={() => {
setCurrentTab(PopoverTabs.AttachedFiles);
}}
>
Attached
</button>
<button
className={`bg-default border-0 cursor-pointer px-3 py-2.5 relative focus:bg-info-backdrop focus:shadow-bottom ${
currentTab === PopoverTabs.AllFiles
? 'color-info font-medium shadow-bottom'
: 'color-text'
}`}
onClick={() => {
setCurrentTab(PopoverTabs.AllFiles);
}}
>
All files
</button>
</div>
<div className="min-h-0 max-h-110 overflow-y-auto">
{filteredList.length > 0 || searchQuery.length > 0 ? (
<div className="sticky top-0 left-0 p-3 bg-default border-0 border-b-1 border-solid border-main">
<div className="relative">
<input
type="text"
className="w-full rounded py-1.5 px-3 text-input bg-default border-solid border-1 border-main"
placeholder="Search files..."
value={searchQuery}
onInput={(e) => {
setSearchQuery((e.target as HTMLInputElement).value);
}}
/>
{searchQuery.length > 0 && (
<button
className="flex absolute right-2 p-0 bg-transparent border-0 top-1/2 -translate-y-1/2 cursor-pointer"
onClick={() => {
setSearchQuery('');
}}
>
<Icon
type="clear-circle-filled"
className="color-neutral"
/>
</button>
)}
</div>
</div>
) : null}
{filteredList.length > 0 ? (
filteredList.map((file: SNFile) => {
return (
<PopoverFileItem
key={file.uuid}
file={file}
isAttachedToNote={attachedFiles.includes(file)}
handleFileAction={fileActionHandler}
/>
);
})
) : (
<div className="flex flex-col items-center justify-center w-full py-8">
<div className="w-18 h-18 mb-2">
<FilesIllustration
style={{
transform: 'scale(0.6)',
transformOrigin: 'top left',
}}
/>
</div>
<div className="text-sm font-medium mb-3">
{searchQuery.length > 0
? 'No result found'
: currentTab === PopoverTabs.AttachedFiles
? 'No files attached to this note'
: 'No files found in this account'}
</div>
<Button type="normal" onClick={handleAttachFilesClick}>
{currentTab === PopoverTabs.AttachedFiles ? 'Attach' : 'Upload'}{' '}
files
</Button>
<div className="text-xs color-grey-0 mt-3">
Or drop your files here
</div>
</div>
)}
</div>
{filteredList.length > 0 && (
<button
className="sn-dropdown-item py-3 border-0 border-t-1px border-solid border-main focus:bg-info-backdrop"
onClick={handleAttachFilesClick}
>
<Icon type="add" className="mr-2 color-neutral" />
{currentTab === PopoverTabs.AttachedFiles
? 'Attach'
: 'Upload'}{' '}
files
</button>
)}
</div>
);
}
);

View File

@@ -0,0 +1,143 @@
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants';
import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state';
import { StreamingFileReader } from '@standardnotes/filepicker';
import { SNNote } from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
import { AttachedFilesPopover } from './AttachedFilesPopover';
import {
PopoverFileItemAction,
PopoverFileItemActionType,
} from './PopoverFileItemAction';
export enum PopoverTabs {
AttachedFiles,
AllFiles,
}
export type PopoverWrapperProps = {
application: WebApplication;
appState: AppState;
note: SNNote;
fileActionHandler: (action: PopoverFileItemAction) => Promise<boolean>;
};
export const PopoverDragNDropWrapper: FunctionComponent<
PopoverWrapperProps
> = ({ fileActionHandler, appState, application, note }) => {
const dropzoneRef = useRef<HTMLDivElement>(null);
const [isDragging, setIsDragging] = useState(false);
const [currentTab, setCurrentTab] = useState(PopoverTabs.AttachedFiles);
const dragCounter = useRef(0);
const handleDrag = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
};
const handleDragIn = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
dragCounter.current = dragCounter.current + 1;
if (event.dataTransfer?.items.length) {
setIsDragging(true);
}
};
const handleDragOut = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
dragCounter.current = dragCounter.current - 1;
if (dragCounter.current > 0) {
return;
}
setIsDragging(false);
};
const handleDrop = useCallback(
(event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
setIsDragging(false);
if (event.dataTransfer?.items.length) {
Array.from(event.dataTransfer.items).forEach(async (item) => {
let fileOrHandle;
if (StreamingFileReader.available()) {
fileOrHandle =
(await item.getAsFileSystemHandle()) as FileSystemFileHandle;
} else {
fileOrHandle = item.getAsFile();
}
if (fileOrHandle) {
const uploadedFiles = await appState.files.uploadNewFile(
fileOrHandle
);
if (!uploadedFiles) {
return;
}
if (currentTab === PopoverTabs.AttachedFiles) {
uploadedFiles.forEach((file) => {
fileActionHandler({
type: PopoverFileItemActionType.AttachFileToNote,
payload: file,
});
});
}
}
});
event.dataTransfer.clearData();
dragCounter.current = 0;
}
},
[appState.files, currentTab, fileActionHandler]
);
useEffect(() => {
const dropzoneElement = dropzoneRef.current;
if (dropzoneElement) {
dropzoneElement.addEventListener('dragenter', handleDragIn);
dropzoneElement.addEventListener('dragleave', handleDragOut);
dropzoneElement.addEventListener('dragover', handleDrag);
dropzoneElement.addEventListener('drop', handleDrop);
}
return () => {
dropzoneElement?.removeEventListener('dragenter', handleDragIn);
dropzoneElement?.removeEventListener('dragleave', handleDragOut);
dropzoneElement?.removeEventListener('dragover', handleDrag);
dropzoneElement?.removeEventListener('drop', handleDrop);
};
}, [handleDrop]);
return (
<div
ref={dropzoneRef}
className="focus:shadow-none"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
style={{
border: isDragging ? '2px dashed var(--sn-stylekit-info-color)' : '',
}}
>
<AttachedFilesPopover
application={application}
appState={appState}
note={note}
fileActionHandler={fileActionHandler}
currentTab={currentTab}
setCurrentTab={setCurrentTab}
/>
</div>
);
};

View File

@@ -0,0 +1,129 @@
import { KeyboardKey } from '@/services/ioService';
import { formatSizeToReadableString } from '@standardnotes/filepicker';
import { SNFile } from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
import { ICONS } from '../Icon';
import {
PopoverFileItemAction,
PopoverFileItemActionType,
} from './PopoverFileItemAction';
import { PopoverFileSubmenu } from './PopoverFileSubmenu';
const getIconForFileType = (fileType: string) => {
let iconType = 'file-other';
if (fileType === 'pdf') {
iconType = 'file-pdf';
}
if (/^(docx?|odt)/.test(fileType)) {
iconType = 'file-doc';
}
if (/^pptx?/.test(fileType)) {
iconType = 'file-ppt';
}
if (/^(xlsx?|ods)/.test(fileType)) {
iconType = 'file-xls';
}
if (/^(jpe?g|a?png|webp|gif)/.test(fileType)) {
iconType = 'file-image';
}
if (/^(mov|mp4|mkv)/.test(fileType)) {
iconType = 'file-mov';
}
if (/^(wav|mp3|flac|ogg)/.test(fileType)) {
iconType = 'file-music';
}
if (/^(zip|rar|7z)/.test(fileType)) {
iconType = 'file-zip';
}
const IconComponent = ICONS[iconType as keyof typeof ICONS];
return <IconComponent className="flex-shrink-0" />;
};
export type PopoverFileItemProps = {
file: SNFile;
isAttachedToNote: boolean;
handleFileAction: (action: PopoverFileItemAction) => Promise<boolean>;
};
export const PopoverFileItem: FunctionComponent<PopoverFileItemProps> = ({
file,
isAttachedToNote,
handleFileAction,
}) => {
const [fileName, setFileName] = useState(file.nameWithExt);
const [isRenamingFile, setIsRenamingFile] = useState(false);
const fileNameInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (isRenamingFile) {
fileNameInputRef.current?.focus();
}
}, [isRenamingFile]);
const renameFile = async (file: SNFile, name: string) => {
const didRename = await handleFileAction({
type: PopoverFileItemActionType.RenameFile,
payload: {
file,
name,
},
});
if (didRename) {
setIsRenamingFile(false);
}
};
const handleFileNameInput = (event: Event) => {
setFileName((event.target as HTMLInputElement).value);
};
const handleFileNameInputKeyDown = (event: KeyboardEvent) => {
if (event.key === KeyboardKey.Enter) {
renameFile(file, fileName);
return;
}
};
return (
<div className="flex items-center justify-between p-3">
<div className="flex items-center">
{getIconForFileType(file.ext ?? '')}
<div className="flex flex-col mx-4">
{isRenamingFile ? (
<input
type="text"
className="text-input px-1.5 py-1 mb-1 border-1 border-solid border-main bg-transparent color-foreground"
value={fileName}
ref={fileNameInputRef}
onInput={handleFileNameInput}
onKeyDown={handleFileNameInputKeyDown}
/>
) : (
<div className="text-sm mb-1">{file.nameWithExt}</div>
)}
<div className="text-xs color-grey-0">
{file.created_at.toLocaleString()} ·{' '}
{formatSizeToReadableString(file.size)}
</div>
</div>
</div>
<PopoverFileSubmenu
file={file}
isAttachedToNote={isAttachedToNote}
handleFileAction={handleFileAction}
setIsRenamingFile={setIsRenamingFile}
/>
</div>
);
};

View File

@@ -0,0 +1,32 @@
import { SNFile } from '@standardnotes/snjs';
export enum PopoverFileItemActionType {
AttachFileToNote,
DetachFileToNote,
DeleteFile,
DownloadFile,
RenameFile,
ToggleFileProtection,
}
export type PopoverFileItemAction =
| {
type: Exclude<
PopoverFileItemActionType,
| PopoverFileItemActionType.RenameFile
| PopoverFileItemActionType.ToggleFileProtection
>;
payload: SNFile;
}
| {
type: PopoverFileItemActionType.ToggleFileProtection;
payload: SNFile;
callback: (isProtected: boolean) => void;
}
| {
type: PopoverFileItemActionType.RenameFile;
payload: {
file: SNFile;
name: string;
};
};

View File

@@ -0,0 +1,188 @@
import { FOCUSABLE_BUT_NOT_TABBABLE } from '@/constants';
import {
calculateSubmenuStyle,
SubmenuStyle,
} from '@/utils/calculateSubmenuStyle';
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from '@reach/disclosure';
import { FunctionComponent } from 'preact';
import {
StateUpdater,
useCallback,
useEffect,
useRef,
useState,
} from 'preact/hooks';
import { Icon } from '../Icon';
import { Switch } from '../Switch';
import { useCloseOnBlur } from '../utils';
import { PopoverFileItemProps } from './PopoverFileItem';
import { PopoverFileItemActionType } from './PopoverFileItemAction';
type Props = Omit<PopoverFileItemProps, 'renameFile'> & {
setIsRenamingFile: StateUpdater<boolean>;
};
export const PopoverFileSubmenu: FunctionComponent<Props> = ({
file,
isAttachedToNote,
handleFileAction,
setIsRenamingFile,
}) => {
const menuContainerRef = useRef<HTMLDivElement>(null);
const menuButtonRef = useRef<HTMLButtonElement>(null);
const menuRef = useRef<HTMLDivElement>(null);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isFileProtected, setIsFileProtected] = useState(file.protected);
const [menuStyle, setMenuStyle] = useState<SubmenuStyle>({
right: 0,
bottom: 0,
maxHeight: 'auto',
});
const [closeOnBlur] = useCloseOnBlur(menuContainerRef, setIsMenuOpen);
const closeMenu = () => {
setIsMenuOpen(false);
};
const toggleMenu = () => {
if (!isMenuOpen) {
const menuPosition = calculateSubmenuStyle(menuButtonRef.current);
if (menuPosition) {
setMenuStyle(menuPosition);
}
}
setIsMenuOpen(!isMenuOpen);
};
const recalculateMenuStyle = useCallback(() => {
const newMenuPosition = calculateSubmenuStyle(
menuButtonRef.current,
menuRef.current
);
if (newMenuPosition) {
setMenuStyle(newMenuPosition);
}
}, []);
useEffect(() => {
if (isMenuOpen) {
setTimeout(() => {
recalculateMenuStyle();
});
}
}, [isMenuOpen, recalculateMenuStyle]);
return (
<div ref={menuContainerRef}>
<Disclosure open={isMenuOpen} onChange={toggleMenu}>
<DisclosureButton
ref={menuButtonRef}
onBlur={closeOnBlur}
className="w-7 h-7 p-1 rounded-full border-0 bg-transparent hover:bg-contrast cursor-pointer"
>
<Icon type="more" className="color-neutral" />
</DisclosureButton>
<DisclosurePanel
ref={menuRef}
style={{
...menuStyle,
position: 'fixed',
}}
className="sn-dropdown flex flex-col max-h-120 min-w-60 py-1 fixed overflow-y-auto"
>
{isMenuOpen && (
<>
{isAttachedToNote ? (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.DetachFileToNote,
payload: file,
});
closeMenu();
}}
>
<Icon type="link-off" className="mr-2 color-neutral" />
Detach from note
</button>
) : (
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.AttachFileToNote,
payload: file,
});
closeMenu();
}}
>
<Icon type="link" className="mr-2 color-neutral" />
Attach to note
</button>
)}
<div className="min-h-1px my-1 bg-border"></div>
<button
className="sn-dropdown-item justify-between focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.ToggleFileProtection,
payload: file,
callback: (isProtected: boolean) => {
setIsFileProtected(isProtected);
},
});
}}
onBlur={closeOnBlur}
>
<span className="flex items-center">
<Icon type="password" className="mr-2 color-neutral" />
Password protection
</span>
<Switch
className="px-0 pointer-events-none"
tabIndex={FOCUSABLE_BUT_NOT_TABBABLE}
checked={isFileProtected}
/>
</button>
<div className="min-h-1px my-1 bg-border"></div>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
onClick={() => {
handleFileAction({
type: PopoverFileItemActionType.DownloadFile,
payload: file,
});
closeMenu();
}}
>
<Icon type="download" className="mr-2 color-neutral" />
Download
</button>
<button
onBlur={closeOnBlur}
className="sn-dropdown-item focus:bg-info-backdrop"
onClick={() => {
setIsRenamingFile(true);
}}
>
<Icon type="pencil" className="mr-2 color-neutral" />
Rename
</button>
</>
)}
</DisclosurePanel>
</Disclosure>
</div>
);
};

View File

@@ -0,0 +1,25 @@
interface BubbleProperties {
label: string;
selected: boolean;
onSelect: () => void;
}
const styles = {
base: 'px-2 py-1.5 text-center rounded-full cursor-pointer transition border-1 border-solid active:border-info active:bg-info active:color-neutral-contrast',
unselected: 'color-neutral border-secondary',
selected: 'border-info bg-info color-neutral-contrast',
};
const Bubble = ({ label, selected, onSelect }: BubbleProperties) => (
<span
role="tab"
className={`bubble ${styles.base} ${
selected ? styles.selected : styles.unselected
}`}
onClick={onSelect}
>
{label}
</span>
);
export default Bubble;

View File

@@ -256,7 +256,7 @@ export class Footer extends PureComponent<Props, State> {
updateSyncStatus() { updateSyncStatus() {
const statusManager = this.application.getStatusManager(); const statusManager = this.application.getStatusManager();
const syncStatus = this.application.getSyncStatus(); const syncStatus = this.application.sync.getSyncStatus();
const stats = syncStatus.getStats(); const stats = syncStatus.getStats();
if (syncStatus.hasError()) { if (syncStatus.hasError()) {
statusManager.setMessage('Unable to Sync'); statusManager.setMessage('Unable to Sync');
@@ -290,7 +290,7 @@ export class Footer extends PureComponent<Props, State> {
updateLocalDataStatus() { updateLocalDataStatus() {
const statusManager = this.application.getStatusManager(); const statusManager = this.application.getStatusManager();
const syncStatus = this.application.getSyncStatus(); const syncStatus = this.application.sync.getSyncStatus();
const stats = syncStatus.getStats(); const stats = syncStatus.getStats();
const encryption = this.application.isEncryptionAvailable(); const encryption = this.application.isEncryptionAvailable();
if (stats.localDataDone) { if (stats.localDataDone) {
@@ -312,7 +312,7 @@ export class Footer extends PureComponent<Props, State> {
findErrors() { findErrors() {
this.setState({ this.setState({
hasError: this.application.getSyncStatus().hasError(), hasError: this.application.sync.getSyncStatus().hasError(),
}); });
} }

View File

@@ -9,6 +9,7 @@ import {
ArrowLeftIcon, ArrowLeftIcon,
ArrowsSortDownIcon, ArrowsSortDownIcon,
ArrowsSortUpIcon, ArrowsSortUpIcon,
AttachmentFileIcon,
AuthenticatorIcon, AuthenticatorIcon,
CheckBoldIcon, CheckBoldIcon,
CheckCircleIcon, CheckCircleIcon,
@@ -17,6 +18,7 @@ import {
ChevronRightIcon, ChevronRightIcon,
CloseIcon, CloseIcon,
CloudOffIcon, CloudOffIcon,
ClearCircleFilledIcon,
CodeIcon, CodeIcon,
CopyIcon, CopyIcon,
DashboardIcon, DashboardIcon,
@@ -25,12 +27,22 @@ import {
EmailIcon, EmailIcon,
EyeIcon, EyeIcon,
EyeOffIcon, EyeOffIcon,
FileDocIcon,
FileImageIcon,
FileMovIcon,
FileMusicIcon,
FileOtherIcon,
FilePdfIcon,
FilePptIcon,
FileXlsIcon,
FileZipIcon,
HashtagIcon, HashtagIcon,
HashtagOffIcon, HashtagOffIcon,
HelpIcon, HelpIcon,
HistoryIcon, HistoryIcon,
InfoIcon, InfoIcon,
KeyboardIcon, KeyboardIcon,
LinkIcon,
LinkOffIcon, LinkOffIcon,
ListBulleted, ListBulleted,
ListedIcon, ListedIcon,
@@ -44,9 +56,9 @@ import {
MoreIcon, MoreIcon,
NotesIcon, NotesIcon,
PasswordIcon, PasswordIcon,
PencilOffIcon,
PencilFilledIcon, PencilFilledIcon,
PencilIcon, PencilIcon,
PencilOffIcon,
PinFilledIcon, PinFilledIcon,
PinIcon, PinIcon,
PlainTextIcon, PlainTextIcon,
@@ -75,17 +87,28 @@ import {
WindowIcon, WindowIcon,
} from '@standardnotes/stylekit'; } from '@standardnotes/stylekit';
const ICONS = { export const ICONS = {
'account-circle': AccountCircleIcon, 'account-circle': AccountCircleIcon,
'arrow-left': ArrowLeftIcon, 'arrow-left': ArrowLeftIcon,
'arrows-sort-down': ArrowsSortDownIcon, 'arrows-sort-down': ArrowsSortDownIcon,
'arrows-sort-up': ArrowsSortUpIcon, 'arrows-sort-up': ArrowsSortUpIcon,
'attachment-file': AttachmentFileIcon,
'check-bold': CheckBoldIcon, 'check-bold': CheckBoldIcon,
'check-circle': CheckCircleIcon, 'check-circle': CheckCircleIcon,
'chevron-down': ChevronDownIcon, 'chevron-down': ChevronDownIcon,
'chevron-right': ChevronRightIcon, 'chevron-right': ChevronRightIcon,
'cloud-off': CloudOffIcon, 'cloud-off': CloudOffIcon,
'clear-circle-filled': ClearCircleFilledIcon,
'eye-off': EyeOffIcon, 'eye-off': EyeOffIcon,
'file-doc': FileDocIcon,
'file-image': FileImageIcon,
'file-mov': FileMovIcon,
'file-music': FileMusicIcon,
'file-other': FileOtherIcon,
'file-pdf': FilePdfIcon,
'file-ppt': FilePptIcon,
'file-xls': FileXlsIcon,
'file-zip': FileZipIcon,
'hashtag-off': HashtagOffIcon, 'hashtag-off': HashtagOffIcon,
'link-off': LinkOffIcon, 'link-off': LinkOffIcon,
'list-bulleted': ListBulleted, 'list-bulleted': ListBulleted,
@@ -121,6 +144,7 @@ const ICONS = {
history: HistoryIcon, history: HistoryIcon,
info: InfoIcon, info: InfoIcon,
keyboard: KeyboardIcon, keyboard: KeyboardIcon,
link: LinkIcon,
listed: ListedIcon, listed: ListedIcon,
lock: LockIcon, lock: LockIcon,
markdown: MarkdownIcon, markdown: MarkdownIcon,

View File

@@ -17,6 +17,8 @@ import {
ItemMutator, ItemMutator,
ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction, ProposedSecondsToDeferUILevelSessionExpirationDuringActiveInteraction,
NoteViewController, NoteViewController,
FeatureIdentifier,
FeatureStatus,
} from '@standardnotes/snjs'; } from '@standardnotes/snjs';
import { debounce, isDesktopApplication } from '@/utils'; import { debounce, isDesktopApplication } from '@/utils';
import { KeyboardModifier, KeyboardKey } from '@/services/ioService'; import { KeyboardModifier, KeyboardKey } from '@/services/ioService';
@@ -37,6 +39,7 @@ import { ComponentView } from '../ComponentView';
import { PanelSide, PanelResizer, PanelResizeType } from '../PanelResizer'; import { PanelSide, PanelResizer, PanelResizeType } from '../PanelResizer';
import { ElementIds } from '@/element_ids'; import { ElementIds } from '@/element_ids';
import { ChangeEditorButton } from '../ChangeEditorButton'; import { ChangeEditorButton } from '../ChangeEditorButton';
import { AttachedFilesButton } from '../AttachedFilesPopover/AttachedFilesButton';
const MINIMUM_STATUS_DURATION = 400; const MINIMUM_STATUS_DURATION = 400;
const TEXTAREA_DEBOUNCE = 100; const TEXTAREA_DEBOUNCE = 100;
@@ -100,6 +103,7 @@ type State = {
editorTitle: string; editorTitle: string;
editorText: string; editorText: string;
isDesktop?: boolean; isDesktop?: boolean;
isEntitledToFiles: boolean;
lockText: string; lockText: string;
marginResizersEnabled?: boolean; marginResizersEnabled?: boolean;
monospaceFont?: boolean; monospaceFont?: boolean;
@@ -168,6 +172,9 @@ export class NoteView extends PureComponent<Props, State> {
editorText: '', editorText: '',
editorTitle: '', editorTitle: '',
isDesktop: isDesktopApplication(), isDesktop: isDesktopApplication(),
isEntitledToFiles:
this.application.features.getFeatureStatus(FeatureIdentifier.Files) ===
FeatureStatus.Entitled,
lockText: 'Note Editing Disabled', lockText: 'Note Editing Disabled',
noteStatus: undefined, noteStatus: undefined,
noteLocked: this.controller.note.locked, noteLocked: this.controller.note.locked,
@@ -321,6 +328,15 @@ export class NoteView extends PureComponent<Props, State> {
/** @override */ /** @override */
async onAppEvent(eventName: ApplicationEvent) { async onAppEvent(eventName: ApplicationEvent) {
switch (eventName) { switch (eventName) {
case ApplicationEvent.FeaturesUpdated:
case ApplicationEvent.UserRolesChanged:
this.setState({
isEntitledToFiles:
this.application.features.getFeatureStatus(
FeatureIdentifier.Files
) === FeatureStatus.Entitled,
});
break;
case ApplicationEvent.PreferencesChanged: case ApplicationEvent.PreferencesChanged:
this.reloadPreferences(); this.reloadPreferences();
break; break;
@@ -681,7 +697,7 @@ export class NoteView extends PureComponent<Props, State> {
if (left !== undefined && left !== null) { if (left !== undefined && left !== null) {
await this.application.setPreference(PrefKey.EditorLeft, left); await this.application.setPreference(PrefKey.EditorLeft, left);
} }
this.application.sync(); this.application.sync.sync();
}; };
async reloadSpellcheck() { async reloadSpellcheck() {
@@ -797,7 +813,7 @@ export class NoteView extends PureComponent<Props, State> {
} else { } else {
await this.disassociateComponentWithCurrentNote(component); await this.disassociateComponentWithCurrentNote(component);
} }
this.application.sync(); this.application.sync.sync();
}; };
async disassociateComponentWithCurrentNote(component: SNComponent) { async disassociateComponentWithCurrentNote(component: SNComponent) {
@@ -1027,6 +1043,18 @@ export class NoteView extends PureComponent<Props, State> {
)} )}
</div> </div>
</div> </div>
{this.state.isEntitledToFiles &&
window.enabledUnfinishedFeatures && (
<div className="mr-3">
<AttachedFilesButton
application={this.application}
appState={this.appState}
onClickPreprocessing={
this.ensureNoteIsInsertedBeforeUIAction
}
/>
</div>
)}
<div className="mr-3"> <div className="mr-3">
<ChangeEditorButton <ChangeEditorButton
application={this.application} application={this.application}

View File

@@ -38,7 +38,6 @@ export const AddTagOption: FunctionComponent<Props> = observer(
const menuPosition = calculateSubmenuStyle(menuButtonRef.current); const menuPosition = calculateSubmenuStyle(menuButtonRef.current);
if (menuPosition) { if (menuPosition) {
setMenuStyle(menuPosition); setMenuStyle(menuPosition);
console.log(menuPosition);
} }
} }
@@ -53,7 +52,6 @@ export const AddTagOption: FunctionComponent<Props> = observer(
if (newMenuPosition) { if (newMenuPosition) {
setMenuStyle(newMenuPosition); setMenuStyle(newMenuPosition);
console.log(newMenuPosition);
} }
}, []); }, []);

View File

@@ -153,7 +153,7 @@ export const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
await application.runTransactionalMutations(transactions); await application.runTransactionalMutations(transactions);
/** Dirtying can happen above */ /** Dirtying can happen above */
application.sync(); application.sync.sync();
setCurrentEditor(application.componentManager.editorForNote(note)); setCurrentEditor(application.componentManager.editorForNote(note));
}; };

View File

@@ -59,11 +59,13 @@ export const createEditorMenuGroups = (
feature.area === ComponentArea.Editor feature.area === ComponentArea.Editor
) )
.forEach((editorFeature) => { .forEach((editorFeature) => {
if ( const notInstalled = !editors.find(
!editors.find( (editor) => editor.identifier === editorFeature.identifier
(editor) => editor.identifier === editorFeature.identifier );
) const isExperimental = application.features.isExperimentalFeature(
) { editorFeature.identifier
);
if (notInstalled && !isExperimental) {
editorItems[getEditorGroup(editorFeature)].push({ editorItems[getEditorGroup(editorFeature)].push({
name: editorFeature.name as string, name: editorFeature.name as string,
isEntitled: false, isEntitled: false,

View File

@@ -48,13 +48,13 @@ export const NotesView: FunctionComponent<Props> = observer(
selectPreviousNote, selectPreviousNote,
onFilterEnter, onFilterEnter,
handleFilterTextChanged, handleFilterTextChanged,
onSearchInputBlur,
clearFilterText, clearFilterText,
paginate, paginate,
panelWidth, panelWidth,
} = appState.notesView; } = appState.notesView;
const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false); const [showDisplayOptionsMenu, setShowDisplayOptionsMenu] = useState(false);
const [focusedSearch, setFocusedSearch] = useState(false);
const [closeDisplayOptMenuOnBlur] = useCloseOnBlur( const [closeDisplayOptMenuOnBlur] = useCloseOnBlur(
displayOptionsMenuRef, displayOptionsMenuRef,
@@ -130,6 +130,9 @@ export const NotesView: FunctionComponent<Props> = observer(
setNoteFilterText((e.target as HTMLInputElement).value); setNoteFilterText((e.target as HTMLInputElement).value);
}; };
const onSearchFocused = () => setFocusedSearch(true);
const onSearchBlurred = () => setFocusedSearch(false);
const onNoteFilterKeyUp = (e: KeyboardEvent) => { const onNoteFilterKeyUp = (e: KeyboardEvent) => {
if (e.key === KeyboardKey.Enter) { if (e.key === KeyboardKey.Enter) {
onFilterEnter(); onFilterEnter();
@@ -179,32 +182,38 @@ export const NotesView: FunctionComponent<Props> = observer(
</button> </button>
</div> </div>
<div className="filter-section" role="search"> <div className="filter-section" role="search">
<input <div>
type="text" <input
id="search-bar" type="text"
className="filter-bar" id="search-bar"
placeholder="Search" className="filter-bar"
title="Searches notes in the currently selected tag" placeholder="Search"
value={noteFilterText} title="Searches notes in the currently selected tag"
onChange={onNoteFilterTextChange} value={noteFilterText}
onKeyUp={onNoteFilterKeyUp} onChange={onNoteFilterTextChange}
onBlur={() => onSearchInputBlur()} onKeyUp={onNoteFilterKeyUp}
/> onFocus={onSearchFocused}
{noteFilterText ? ( onBlur={onSearchBlurred}
<button
onClick={clearFilterText}
aria-role="button"
id="search-clear-button"
>
</button>
) : null}
<div className="ml-2">
<SearchOptions
application={application}
appState={appState}
/> />
{noteFilterText && (
<button
onClick={clearFilterText}
aria-role="button"
id="search-clear-button"
>
</button>
)}
</div> </div>
{(focusedSearch || noteFilterText) && (
<div className="animate-fade-from-top">
<SearchOptions
application={application}
appState={appState}
/>
</div>
)}
</div> </div>
<NoAccountWarning appState={appState} /> <NoAccountWarning appState={appState} />
</div> </div>

View File

@@ -1,16 +1,7 @@
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
import { Icon } from './Icon';
import { useCloseOnBlur } from './utils';
import { useEffect, useRef, useState } from 'preact/hooks';
import { WebApplication } from '@/ui_models/application'; import { WebApplication } from '@/ui_models/application';
import VisuallyHidden from '@reach/visually-hidden';
import {
Disclosure,
DisclosureButton,
DisclosurePanel,
} from '@reach/disclosure';
import { Switch } from './Switch';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import Bubble from './Bubble';
type Props = { type Props = {
appState: AppState; appState: AppState;
@@ -23,94 +14,33 @@ export const SearchOptions = observer(({ appState }: Props) => {
const { includeProtectedContents, includeArchived, includeTrashed } = const { includeProtectedContents, includeArchived, includeTrashed } =
searchOptions; searchOptions;
const [open, setOpen] = useState(false);
const [position, setPosition] = useState({
top: 0,
right: 0,
});
const [maxWidth, setMaxWidth] = useState<number | 'auto'>('auto');
const buttonRef = useRef<HTMLButtonElement>(null);
const panelRef = useRef<HTMLDivElement>(null);
const [closeOnBlur, setLockCloseOnBlur] = useCloseOnBlur(
panelRef as any,
setOpen
);
async function toggleIncludeProtectedContents() { async function toggleIncludeProtectedContents() {
setLockCloseOnBlur(true); await searchOptions.toggleIncludeProtectedContents();
try {
await searchOptions.toggleIncludeProtectedContents();
} finally {
setLockCloseOnBlur(false);
}
} }
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 ( return (
<Disclosure <div
open={open} role="tablist"
onChange={() => { className="search-options justify-center"
updateWidthAndPosition(); onMouseDown={(e) => e.preventDefault()}
setOpen(!open);
}}
> >
<DisclosureButton <Bubble
ref={buttonRef} label="Protected Contents"
onBlur={closeOnBlur} selected={includeProtectedContents}
className="border-0 p-0 bg-transparent cursor-pointer color-neutral hover:color-info" onSelect={toggleIncludeProtectedContents}
> />
<VisuallyHidden>Search options</VisuallyHidden>
<Icon type="tune" className="block" /> <Bubble
</DisclosureButton> label="Archived"
<DisclosurePanel selected={includeArchived}
ref={panelRef} onSelect={searchOptions.toggleIncludeArchived}
style={{ />
...position,
maxWidth, <Bubble
}} label="Trashed"
className="sn-dropdown sn-dropdown--animated w-80 fixed grid gap-2 py-2" selected={includeTrashed}
onBlur={closeOnBlur} onSelect={searchOptions.toggleIncludeTrashed}
> />
<Switch </div>
className="h-10"
checked={includeProtectedContents}
onChange={toggleIncludeProtectedContents}
onBlur={closeOnBlur}
>
<p className="capitalize">Include protected contents</p>
</Switch>
<Switch
className="h-10"
checked={includeArchived}
onChange={searchOptions.toggleIncludeArchived}
onBlur={closeOnBlur}
>
<p className="capitalize">Include archived notes</p>
</Switch>
<Switch
className="h-10"
checked={includeTrashed}
onChange={searchOptions.toggleIncludeTrashed}
onBlur={closeOnBlur}
>
<p className="capitalize">Include trashed notes</p>
</Switch>
</DisclosurePanel>
</Disclosure>
); );
}); });

View File

@@ -1,6 +1,5 @@
import { WebApplication } from '@/ui_models/application'; import { WebApplication } from '@/ui_models/application';
import { PureComponent } from './Abstract/PureComponent'; import { PureComponent } from './Abstract/PureComponent';
import { Fragment } from 'preact';
type Props = { type Props = {
application: WebApplication; application: WebApplication;
@@ -8,43 +7,13 @@ type Props = {
}; };
export class SyncResolutionMenu extends PureComponent<Props> { export class SyncResolutionMenu extends PureComponent<Props> {
private status: Partial<{
backupFinished: boolean;
resolving: boolean;
attemptedResolution: boolean;
success: boolean;
fail: boolean;
}> = {};
constructor(props: Props) { constructor(props: Props) {
super(props, props.application); super(props, props.application);
} }
downloadBackup(encrypted: boolean) { close = () => {
this.props.application.getArchiveService().downloadBackup(encrypted);
this.status.backupFinished = true;
}
skipBackup() {
this.status.backupFinished = true;
}
async performSyncResolution() {
this.status.resolving = true;
await this.props.application.resolveOutOfSync();
this.status.resolving = false;
this.status.attemptedResolution = true;
if (this.props.application.isOutOfSync()) {
this.status.fail = true;
} else {
this.status.success = true;
}
}
close() {
this.props.close(); this.props.close();
} };
render() { render() {
return ( return (
@@ -59,13 +28,15 @@ export class SyncResolutionMenu extends PureComponent<Props> {
<div className="sk-panel-content"> <div className="sk-panel-content">
<div className="sk-panel-section"> <div className="sk-panel-section">
<div className="sk-panel-row sk-p"> <div className="sk-panel-row sk-p">
We've detected that the data on the server may not match the We've detected that the data in the current application session
data in the current application session. may not match the data on the server. An attempt was made to
auto-resolve the issue, but it was unable to reconcile the
differences.
</div> </div>
<div className="sk-p sk-panel-row"> <div className="sk-p sk-panel-row">
<div className="sk-panel-column"> <div className="sk-panel-column">
<strong className="sk-panel-row"> <strong className="sk-panel-row">
Option 1 Restart App: Option 1 Restart Application:
</strong> </strong>
<div className="sk-p"> <div className="sk-p">
Quit the application and re-open it. Sometimes, this may Quit the application and re-open it. Sometimes, this may
@@ -76,108 +47,15 @@ export class SyncResolutionMenu extends PureComponent<Props> {
<div className="sk-p sk-panel-row"> <div className="sk-p sk-panel-row">
<div className="sk-panel-column"> <div className="sk-panel-column">
<strong className="sk-panel-row"> <strong className="sk-panel-row">
Option 2 (recommended) Sign Out: Option 2 Sign Out and Back In:
</strong> </strong>
<div className="sk-p"> <div className="sk-p">
Sign out of your account, then sign back in. This will Sign out of your account, then sign back in. This will
ensure your data is consistent with the server. ensure your data is consistent with the server. Be sure to
</div> download a backup of your data before doing so.
Be sure to download a backup of your data before doing so.
</div>
</div>
<div className="sk-p sk-panel-row">
<div className="sk-panel-column">
<strong className="sk-panel-row">
Option 3 Sync Resolution:
</strong>
<div className="sk-p">
We can attempt to reconcile changes by downloading all data
from the server. No existing data will be overwritten. If
the local contents of an item differ from what the server
has, a conflicted copy will be created.
</div> </div>
</div> </div>
</div> </div>
{!this.status.backupFinished && (
<Fragment>
<div className="sk-p sk-panel-row">
Please download a backup before we attempt to perform a full
account sync resolution.
</div>
<div className="sk-panel-row">
<div className="flex gap-2">
<button
onClick={() => this.downloadBackup(true)}
className="sn-button small info"
>
Encrypted
</button>
<button
onClick={() => this.downloadBackup(false)}
className="sn-button small info"
>
Decrypted
</button>
<button
onClick={this.skipBackup}
className="sn-button small danger"
>
Skip
</button>
</div>
</div>
</Fragment>
)}
{this.status.backupFinished && (
<div>
{!this.status.resolving && !this.status.attemptedResolution && (
<div className="sk-panel-row">
<button
onClick={this.performSyncResolution}
className="sn-button small info"
>
Perform Sync Resolution
</button>
</div>
)}
{this.status.resolving && (
<div className="sk-panel-row justify-left">
<div className="sk-horizontal-group">
<div className="sk-spinner small info" />
<div className="sk-label">
Attempting sync resolution...
</div>
</div>
</div>
)}
{this.status.fail && (
<div className="sk-panel-column">
<div className="sk-panel-row sk-label danger">
Sync Resolution Failed
</div>
<div className="sk-p sk-panel-row">
We attempted to reconcile local content and server
content, but were unable to do so. At this point, we
recommend signing out of your account and signing back
in. You may wish to download a data backup before doing
so.
</div>
</div>
)}
{this.status.success && (
<div className="sk-panel-column">
<div className="sk-panel-row sk-label success">
Sync Resolution Success
</div>
<div className="sk-p sk-panel-row">
Your local data is now in sync with the server. You may
close this window.
</div>
</div>
)}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -46,8 +46,13 @@ export function useCloseOnClickOutside(
if (!container.current) { if (!container.current) {
return; return;
} }
const isDescendant = container.current.contains(event.target as Node); const isDescendantOfContainer = container.current.contains(
if (!isDescendant) { event.target as Node
);
const isDescendantOfDialog = (event.target as HTMLElement).closest(
'[role="dialog"]'
);
if (!isDescendantOfContainer && !isDescendantOfDialog) {
callback(); callback();
} }
}, },

View File

@@ -13,4 +13,6 @@ export const NOTES_LIST_SCROLL_THRESHOLD = 200;
export const MILLISECONDS_IN_A_DAY = 1000 * 60 * 60 * 24; export const MILLISECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
export const DAYS_IN_A_WEEK = 7; export const DAYS_IN_A_WEEK = 7;
export const DAYS_IN_A_YEAR = 365; export const DAYS_IN_A_YEAR = 365;
export const BYTES_IN_ONE_MEGABYTE = 1000000;
export const BYTES_IN_ONE_KILOBYTE = 1_000;
export const BYTES_IN_ONE_MEGABYTE = 1_000_000;

View File

@@ -74,7 +74,7 @@ export const Extensions: FunctionComponent<{
const confirmExtension = async () => { const confirmExtension = async () => {
await application.insertItem(confirmableExtension as SNComponent); await application.insertItem(confirmableExtension as SNComponent);
application.sync(); application.sync.sync();
setExtensions(loadExtensions(application)); setExtensions(loadExtensions(application));
}; };

View File

@@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application';
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
import { FunctionComponent } from 'preact'; import { FunctionComponent } from 'preact';
import { PreferencesPane } from '../components'; import { PreferencesPane } from '../components';
import { ErrorReporting, Tools, Defaults } from './general-segments'; import { ErrorReporting, Tools, Defaults, LabsPane } from './general-segments';
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments'; import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
import { Advanced } from '@/preferences/panes/account'; import { Advanced } from '@/preferences/panes/account';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
@@ -19,6 +19,7 @@ export const General: FunctionComponent<GeneralProps> = observer(
<Tools application={application} /> <Tools application={application} />
<Defaults application={application} /> <Defaults application={application} />
<ErrorReporting appState={appState} /> <ErrorReporting appState={appState} />
<LabsPane application={application} />
<Advanced <Advanced
application={application} application={application}
appState={appState} appState={appState}

View File

@@ -24,22 +24,22 @@ export const Sync: FunctionComponent<Props> = observer(
({ application }: Props) => { ({ application }: Props) => {
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false); const [isSyncingInProgress, setIsSyncingInProgress] = useState(false);
const [lastSyncDate, setLastSyncDate] = useState( const [lastSyncDate, setLastSyncDate] = useState(
formatLastSyncDate(application.getLastSyncDate() as Date) formatLastSyncDate(application.sync.getLastSyncDate() as Date)
); );
const doSynchronization = async () => { const doSynchronization = async () => {
setIsSyncingInProgress(true); setIsSyncingInProgress(true);
const response = await application.sync({ const response = await application.sync.sync({
queueStrategy: SyncQueueStrategy.ForceSpawnNew, queueStrategy: SyncQueueStrategy.ForceSpawnNew,
checkIntegrity: true, checkIntegrity: true,
}); });
setIsSyncingInProgress(false); setIsSyncingInProgress(false);
if (response && response.error) { if (response && (response as any).error) {
application.alertService!.alert(STRING_GENERIC_SYNC_ERROR); application.alertService.alert(STRING_GENERIC_SYNC_ERROR);
} else { } else {
setLastSyncDate( setLastSyncDate(
formatLastSyncDate(application.getLastSyncDate() as Date) formatLastSyncDate(application.sync.getLastSyncDate() as Date)
); );
} }
}; };

View File

@@ -0,0 +1,112 @@
import { FindNativeFeature } from '@standardnotes/features';
import { Switch } from '@/components/Switch';
import {
PreferencesGroup,
PreferencesSegment,
Subtitle,
Text,
Title,
} from '@/preferences/components';
import { WebApplication } from '@/ui_models/application';
import { FeatureIdentifier, FeatureStatus } from '@standardnotes/snjs';
import { FunctionComponent } from 'preact';
import { useCallback, useEffect, useState } from 'preact/hooks';
import { usePremiumModal } from '@/components/Premium';
import { HorizontalSeparator } from '@/components/shared/HorizontalSeparator';
type ExperimentalFeatureItem = {
identifier: FeatureIdentifier;
name: string;
description: string;
isEnabled: boolean;
isEntitled: boolean;
};
type Props = {
application: WebApplication;
};
export const LabsPane: FunctionComponent<Props> = ({ application }) => {
const [experimentalFeatures, setExperimentalFeatures] = useState<
ExperimentalFeatureItem[]
>([]);
const reloadExperimentalFeatures = useCallback(() => {
const experimentalFeatures = application.features
.getExperimentalFeatures()
.map((featureIdentifier) => {
const feature = FindNativeFeature(featureIdentifier);
return {
identifier: featureIdentifier,
name: feature?.name ?? featureIdentifier,
description: feature?.description ?? '',
isEnabled:
application.features.isExperimentalFeatureEnabled(
featureIdentifier
),
isEntitled:
application.features.getFeatureStatus(featureIdentifier) ===
FeatureStatus.Entitled,
};
});
setExperimentalFeatures(experimentalFeatures);
}, [application.features]);
useEffect(() => {
reloadExperimentalFeatures();
}, [reloadExperimentalFeatures]);
const premiumModal = usePremiumModal();
return (
<PreferencesGroup>
<PreferencesSegment>
<Title>Labs</Title>
<div>
{experimentalFeatures.map(
(
{ identifier, name, description, isEnabled, isEntitled },
index: number
) => {
const toggleFeature = () => {
if (!isEntitled) {
premiumModal.activate(name);
return;
}
application.features.toggleExperimentalFeature(identifier);
reloadExperimentalFeatures();
};
const showHorizontalSeparator =
experimentalFeatures.length > 1 &&
index !== experimentalFeatures.length - 1;
return (
<>
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Subtitle>{name}</Subtitle>
<Text>{description}</Text>
</div>
<Switch onChange={toggleFeature} checked={isEnabled} />
</div>
{showHorizontalSeparator && (
<HorizontalSeparator classes="mt-5 mb-3" />
)}
</>
);
}
)}
{experimentalFeatures.length === 0 && (
<div className="flex items-center justify-between">
<div className="flex flex-col">
<Text>No experimental features available.</Text>
</div>
</div>
)}
</div>
</PreferencesSegment>
</PreferencesGroup>
);
};

View File

@@ -1,3 +1,4 @@
export * from './ErrorReporting'; export * from './ErrorReporting';
export * from './Tools'; export * from './Tools';
export * from './Defaults'; export * from './Defaults';
export * from './Labs';

View File

@@ -2,7 +2,6 @@ import { Bridge } from './services/bridge';
export type StartApplication = ( export type StartApplication = (
defaultSyncServerHost: string, defaultSyncServerHost: string,
defaultFilesHostHost: string,
bridge: Bridge, bridge: Bridge,
enableUnfinishedFeatures: boolean, enableUnfinishedFeatures: boolean,
webSocketUrl: string webSocketUrl: string

View File

@@ -26,6 +26,7 @@ import {
} from 'mobx'; } from 'mobx';
import { ActionsMenuState } from './actions_menu_state'; import { ActionsMenuState } from './actions_menu_state';
import { FeaturesState } from './features_state'; import { FeaturesState } from './features_state';
import { FilesState } from './files_state';
import { NotesState } from './notes_state'; import { NotesState } from './notes_state';
import { NotesViewState } from './notes_view_state'; import { NotesViewState } from './notes_view_state';
import { NoteTagsState } from './note_tags_state'; import { NoteTagsState } from './note_tags_state';
@@ -89,6 +90,7 @@ export class AppState {
readonly tags: TagsState; readonly tags: TagsState;
readonly notesView: NotesViewState; readonly notesView: NotesViewState;
readonly subscription: SubscriptionState; readonly subscription: SubscriptionState;
readonly files: FilesState;
isSessionsModalVisible = false; isSessionsModalVisible = false;
@@ -139,6 +141,7 @@ export class AppState {
this, this,
this.appEventObserverRemovers this.appEventObserverRemovers
); );
this.files = new FilesState(application);
this.addAppEventObserver(); this.addAppEventObserver();
this.streamNotesAndTags(); this.streamNotesAndTags();
this.onVisibilityChange = () => { this.onVisibilityChange = () => {
@@ -381,7 +384,7 @@ export class AppState {
} }
break; break;
case ApplicationEvent.SyncStatusChanged: case ApplicationEvent.SyncStatusChanged:
this.sync.update(this.application.getSyncStatus()); this.sync.update(this.application.sync.getSyncStatus());
break; break;
} }
}); });

View File

@@ -0,0 +1,142 @@
import {
ClassicFileReader,
StreamingFileReader,
StreamingFileSaver,
ClassicFileSaver,
} from '@standardnotes/filepicker';
import { SNFile } from '@standardnotes/snjs';
import { addToast, dismissToast, ToastType } from '@standardnotes/stylekit';
import { WebApplication } from '../application';
export class FilesState {
constructor(private application: WebApplication) {}
public async downloadFile(file: SNFile): Promise<void> {
let downloadingToastId = '';
try {
const saver = StreamingFileSaver.available()
? new StreamingFileSaver(file.nameWithExt)
: new ClassicFileSaver();
const isUsingStreamingSaver = saver instanceof StreamingFileSaver;
if (isUsingStreamingSaver) {
await saver.selectFileToSaveTo();
}
downloadingToastId = addToast({
type: ToastType.Loading,
message: `Downloading file...`,
});
await this.application.files.downloadFile(
file,
async (decryptedBytes: Uint8Array) => {
if (isUsingStreamingSaver) {
await saver.pushBytes(decryptedBytes);
} else {
saver.saveFile(file.nameWithExt, decryptedBytes);
}
}
);
if (isUsingStreamingSaver) {
await saver.finish();
}
addToast({
type: ToastType.Success,
message: 'Successfully downloaded file',
});
} catch (error) {
console.error(error);
addToast({
type: ToastType.Error,
message: 'There was an error while downloading the file',
});
}
if (downloadingToastId.length > 0) {
dismissToast(downloadingToastId);
}
}
public async uploadNewFile(fileOrHandle?: File | FileSystemFileHandle) {
let toastId = '';
try {
const minimumChunkSize = this.application.files.minimumChunkSize();
const picker = StreamingFileReader.available()
? StreamingFileReader
: ClassicFileReader;
const selectedFiles =
fileOrHandle instanceof File
? [fileOrHandle]
: StreamingFileReader.available() &&
fileOrHandle instanceof FileSystemFileHandle
? await StreamingFileReader.getFilesFromHandles([fileOrHandle])
: await picker.selectFiles();
const uploadedFiles: SNFile[] = [];
for (const file of selectedFiles) {
const operation = await this.application.files.beginNewFileUpload();
const onChunk = async (
chunk: Uint8Array,
index: number,
isLast: boolean
) => {
await this.application.files.pushBytesForUpload(
operation,
chunk,
index,
isLast
);
};
toastId = addToast({
type: ToastType.Loading,
message: `Uploading file "${file.name}"...`,
});
const fileResult = await picker.readFile(
file,
minimumChunkSize,
onChunk
);
const uploadedFile = await this.application.files.finishUpload(
operation,
fileResult.name,
fileResult.ext
);
uploadedFiles.push(uploadedFile);
dismissToast(toastId);
addToast({
type: ToastType.Success,
message: `Uploaded file "${uploadedFile.nameWithExt}"`,
});
}
return uploadedFiles;
} catch (error) {
console.error(error);
if (toastId.length > 0) {
dismissToast(toastId);
}
addToast({
type: ToastType.Error,
message: 'There was an error while uploading the file',
});
}
}
}

View File

@@ -181,7 +181,7 @@ export class NoteTagsState {
if (activeNote) { if (activeNote) {
await this.application.addTagHierarchyToNote(activeNote, tag); await this.application.addTagHierarchyToNote(activeNote, tag);
this.application.sync(); this.application.sync.sync();
this.reloadTags(); this.reloadTags();
} }
} }
@@ -192,7 +192,7 @@ export class NoteTagsState {
await this.application.changeItem(tag.uuid, (mutator) => { await this.application.changeItem(tag.uuid, (mutator) => {
mutator.removeItemAsRelationship(activeNote); mutator.removeItemAsRelationship(activeNote);
}); });
this.application.sync(); this.application.sync.sync();
this.reloadTags(); this.reloadTags();
} }
} }

View File

@@ -265,7 +265,7 @@ export class NotesState {
mutate, mutate,
false false
); );
this.application.sync(); this.application.sync.sync();
} }
setHideSelectedNotePreviews(hide: boolean): void { setHideSelectedNotePreviews(hide: boolean): void {
@@ -403,7 +403,7 @@ export class NotesState {
}, },
false false
); );
this.application.sync(); this.application.sync.sync();
} }
async addTagToSelectedNotes(tag: SNTag): Promise<void> { async addTagToSelectedNotes(tag: SNTag): Promise<void> {
@@ -419,7 +419,7 @@ export class NotesState {
}); });
}) })
); );
this.application.sync(); this.application.sync.sync();
} }
async removeTagFromSelectedNotes(tag: SNTag): Promise<void> { async removeTagFromSelectedNotes(tag: SNTag): Promise<void> {
@@ -429,7 +429,7 @@ export class NotesState {
mutator.removeItemAsRelationship(note); mutator.removeItemAsRelationship(note);
} }
}); });
this.application.sync(); this.application.sync.sync();
} }
isTagInSelectedNotes(tag: SNTag): boolean { isTagInSelectedNotes(tag: SNTag): boolean {
@@ -453,7 +453,7 @@ export class NotesState {
}) })
) { ) {
this.application.emptyTrash(); this.application.emptyTrash();
this.application.sync(); this.application.sync.sync();
} }
} }

View File

@@ -559,10 +559,6 @@ export class NotesViewState {
this.reloadNotes(); this.reloadNotes();
}; };
onSearchInputBlur = () => {
this.appState.searchOptions.refreshIncludeProtectedContents();
};
clearFilterText = () => { clearFilterText = () => {
this.setNoteFilterText(''); this.setNoteFilterText('');
this.onFilterEnter(); this.onFilterEnter();

View File

@@ -208,7 +208,7 @@ export class TagsState {
this.assignParent(createdTag.uuid, parent.uuid); this.assignParent(createdTag.uuid, parent.uuid);
this.application.sync(); this.application.sync.sync();
runInAction(() => { runInAction(() => {
this.selected = createdTag as SNTag; this.selected = createdTag as SNTag;
@@ -364,7 +364,7 @@ export class TagsState {
await this.application.setTagParent(futureParent, tag); await this.application.setTagParent(futureParent, tag);
} }
await this.application.sync(); await this.application.sync.sync();
} }
get rootTags(): SNTag[] { get rootTags(): SNTag[] {
@@ -507,7 +507,7 @@ export class TagsState {
} }
const insertedTag = await this.application.createTagOrSmartView(newTitle); const insertedTag = await this.application.createTagOrSmartView(newTitle);
this.application.sync(); this.application.sync.sync();
runInAction(() => { runInAction(() => {
this.selected = insertedTag as SNTag; this.selected = insertedTag as SNTag;
}); });

View File

@@ -48,7 +48,6 @@ export class WebApplication extends SNApplication {
platform: Platform, platform: Platform,
identifier: string, identifier: string,
defaultSyncServerHost: string, defaultSyncServerHost: string,
defaultFilesHostHost: string,
public bridge: Bridge, public bridge: Bridge,
webSocketUrl: string, webSocketUrl: string,
runtime: Runtime runtime: Runtime
@@ -61,7 +60,6 @@ export class WebApplication extends SNApplication {
alertService: new AlertService(), alertService: new AlertService(),
identifier, identifier,
defaultHost: defaultSyncServerHost, defaultHost: defaultSyncServerHost,
defaultFilesHost: defaultFilesHostHost,
appVersion: bridge.appVersion, appVersion: bridge.appVersion,
webSocketUrl: webSocketUrl, webSocketUrl: webSocketUrl,
runtime, runtime,

View File

@@ -21,7 +21,6 @@ import { InternalEventBus } from '@standardnotes/services';
export class ApplicationGroup extends SNApplicationGroup { export class ApplicationGroup extends SNApplicationGroup {
constructor( constructor(
private defaultSyncServerHost: string, private defaultSyncServerHost: string,
private defaultFilesHostHost: string,
private bridge: Bridge, private bridge: Bridge,
private runtime: Runtime, private runtime: Runtime,
private webSocketUrl: string private webSocketUrl: string
@@ -52,7 +51,6 @@ export class ApplicationGroup extends SNApplicationGroup {
platform, platform,
descriptor.identifier, descriptor.identifier,
this.defaultSyncServerHost, this.defaultSyncServerHost,
this.defaultFilesHostHost,
this.bridge, this.bridge,
this.webSocketUrl, this.webSocketUrl,
this.runtime this.runtime

View File

@@ -1,6 +1,10 @@
import { Platform, platformFromString } from '@standardnotes/snjs'; import { Platform, platformFromString } from '@standardnotes/snjs';
import { IsDesktopPlatform, IsWebPlatform } from '@/version'; import { IsDesktopPlatform, IsWebPlatform } from '@/version';
import { EMAIL_REGEX } from '../constants'; import {
BYTES_IN_ONE_KILOBYTE,
BYTES_IN_ONE_MEGABYTE,
EMAIL_REGEX,
} from '../constants';
export { isMobile } from './isMobile'; export { isMobile } from './isMobile';
declare const process: { declare const process: {

View File

@@ -52,11 +52,11 @@
.filter-section { .filter-section {
clear: left; clear: left;
height: 28px; max-height: 80px;
margin-top: 14px; margin-top: 10px;
position: relative; position: relative;
display: flex; display: flex;
align-items: center; flex-direction: column;
.filter-bar { .filter-bar {
background-color: var(--sn-stylekit-contrast-background-color); background-color: var(--sn-stylekit-contrast-background-color);
@@ -71,6 +71,20 @@
border-color: transparent; border-color: transparent;
width: 100%; width: 100%;
position: relative; position: relative;
height: 28px;
}
.search-options {
margin-top: 10px;
display: grid;
grid-template-columns: repeat( 3, 1fr );
gap: .5rem;
font-size: var(--sn-stylekit-font-size-p);
white-space: nowrap;
overflow-x: auto;
} }
#search-clear-button { #search-clear-button {
@@ -86,9 +100,9 @@
line-height: 17px; line-height: 17px;
text-align: center; text-align: center;
position: absolute; position: absolute;
top: 50%; top: 20%;
transform: translateY(-50%); transform: translateY(-50%);
right: 36px; right: 10px;
cursor: pointer; cursor: pointer;
transition: background-color 0.15s linear; transition: background-color 0.15s linear;

View File

@@ -258,6 +258,11 @@
margin-right: 3rem; margin-right: 3rem;
} }
.mx-4 {
margin-left: 1rem;
margin-right: 1rem;
}
.my-0\.5 { .my-0\.5 {
margin-top: 0.125rem; margin-top: 0.125rem;
margin-bottom: 0.125rem; margin-bottom: 0.125rem;
@@ -328,6 +333,10 @@
width: 0.75rem; width: 0.75rem;
} }
.w-18 {
width: 4.5rem;
}
.w-26 { .w-26 {
width: 6.5rem; width: 6.5rem;
} }
@@ -428,6 +437,10 @@
max-height: 1.25rem; max-height: 1.25rem;
} }
.max-h-110 {
max-height: 27.5rem;
}
.border-danger { .border-danger {
border-color: var(--sn-stylekit-danger-color); border-color: var(--sn-stylekit-danger-color);
} }
@@ -436,6 +449,10 @@
border-color: var(--sn-stylekit-neutral-contrast-color); border-color: var(--sn-stylekit-neutral-contrast-color);
} }
.border-secondary {
border-color: var(--sn-stylekit-secondary-border-color);
}
.sn-component .border-r-1px { .sn-component .border-r-1px {
border-right-width: 1px; border-right-width: 1px;
} }
@@ -513,6 +530,11 @@
padding-right: 0; padding-right: 0;
} }
.px-1\.5 {
padding-left: 0.375rem;
padding-right: 0.375rem;
}
.px-2\.5 { .px-2\.5 {
padding-left: 0.625rem; padding-left: 0.625rem;
padding-right: 0.625rem; padding-right: 0.625rem;
@@ -572,6 +594,10 @@
font-weight: 500; font-weight: 500;
} }
.sticky {
position: sticky;
}
.top-30\% { .top-30\% {
top: 30%; top: 30%;
} }
@@ -636,6 +662,10 @@
right: 0; right: 0;
} }
.right-2 {
right: 0.5rem;
}
.-right-2 { .-right-2 {
right: -0.5rem; right: -0.5rem;
} }
@@ -902,6 +932,10 @@
var(--sn-stylekit-info-color) -1px -1px 0px 0px inset; var(--sn-stylekit-info-color) -1px -1px 0px 0px inset;
} }
.focus\:shadow-bottom:focus {
box-shadow: currentcolor 0px -1px 0px 0px inset, currentcolor 0px 1px 0px 0px;
}
.bg-note-size-warning { .bg-note-size-warning {
background-color: rgba(235, 173, 0, 0.08); background-color: rgba(235, 173, 0, 0.08);
} }
@@ -938,3 +972,44 @@
.invisible { .invisible {
visibility: hidden; visibility: hidden;
} }
.color-neutral-contrast {
color: var(--sn-stylekit-neutral-contrast-color);
}
.active\:bg-info:active {
background-color: var(--sn-stylekit-info-color);
}
.active\:border-info:active {
border-color: var(--sn-stylekit-info-color);
}
.active\:color-neutral-contrast:active {
color: var(--sn-stylekit-neutral-contrast-color);
}
.transition {
transition-property: color, background-color, border-color,
text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter,
backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 100ms;
}
.animate-fade-from-top {
animation: fade-from-top 0.2s ease-out;
}
@keyframes fade-from-top {
0% {
opacity: 0;
transform: translateY(-20%);
}
75% {
opacity: 1;
}
100% {
transform: translateY(0%);
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "standard-notes-web", "name": "standard-notes-web",
"version": "3.12.1", "version": "3.13.0",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -27,21 +27,22 @@
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
"@reach/disclosure": "^0.16.2", "@reach/disclosure": "^0.16.2",
"@reach/visually-hidden": "^0.16.0", "@reach/visually-hidden": "^0.16.0",
"@standardnotes/responses": "^1.1.7", "@standardnotes/responses": "1.3.7",
"@standardnotes/services": "^1.4.0", "@standardnotes/services": "1.5.9",
"@standardnotes/stylekit": "5.14.0", "@standardnotes/stylekit": "5.15.0",
"@svgr/webpack": "^6.2.1", "@svgr/webpack": "^6.2.1",
"@types/jest": "^27.4.1", "@types/jest": "^27.4.1",
"@types/lodash": "^4.14.179", "@types/lodash": "^4.14.179",
"@types/react": "^17.0.39", "@types/react": "^17.0.40",
"@typescript-eslint/eslint-plugin": "^5.13.0", "@types/wicg-file-system-access": "^2020.9.5",
"@typescript-eslint/parser": "^5.13.0", "@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
"apply-loader": "^2.0.0", "apply-loader": "^2.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-loader": "^8.2.3", "babel-loader": "^8.2.3",
"connect": "^3.7.0", "connect": "^3.7.0",
"copy-webpack-plugin": "^10.2.4", "copy-webpack-plugin": "^10.2.4",
"css-loader": "^6.7.0", "css-loader": "^6.7.1",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"eslint": "^8.10.0", "eslint": "^8.10.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
@@ -71,22 +72,23 @@
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"@bugsnag/js": "^7.16.1", "@bugsnag/js": "^7.16.2",
"@reach/alert": "^0.16.0", "@reach/alert": "^0.16.0",
"@reach/alert-dialog": "^0.16.2", "@reach/alert-dialog": "^0.16.2",
"@reach/checkbox": "^0.16.0", "@reach/checkbox": "^0.16.0",
"@reach/dialog": "^0.16.2", "@reach/dialog": "^0.16.2",
"@reach/listbox": "^0.16.2", "@reach/listbox": "^0.16.2",
"@reach/tooltip": "^0.16.2", "@reach/tooltip": "^0.16.2",
"@standardnotes/components": "1.7.9", "@standardnotes/components": "1.7.11",
"@standardnotes/features": "1.34.1", "@standardnotes/features": "1.34.8",
"@standardnotes/settings": "^1.11.5", "@standardnotes/filepicker": "1.8.0",
"@standardnotes/settings": "1.12.0",
"@standardnotes/sncrypto-web": "1.7.3", "@standardnotes/sncrypto-web": "1.7.3",
"@standardnotes/snjs": "2.73.2", "@standardnotes/snjs": "2.79.5",
"mobx": "^6.4.2", "mobx": "^6.4.2",
"mobx-react-lite": "^3.3.0", "mobx-react-lite": "^3.3.0",
"preact": "^10.6.6", "preact": "^10.6.6",
"qrcode.react": "^1.0.1", "qrcode.react": "^2.0.0",
"react-dnd": "^15.1.1", "react-dnd": "^15.1.1",
"react-dnd-html5-backend": "^15.1.2", "react-dnd-html5-backend": "^15.1.2",
"react-dnd-touch-backend": "^15.1.1" "react-dnd-touch-backend": "^15.1.1"

View File

@@ -30,9 +30,9 @@
"binary": "21df7e16f57f6aede482b1281ff1c6d2ad9747fc9c3059e483aea7a03a3a8224" "binary": "21df7e16f57f6aede482b1281ff1c6d2ad9747fc9c3059e483aea7a03a3a8224"
}, },
"org.standardnotes.theme-dynamic": { "org.standardnotes.theme-dynamic": {
"version": "1.0.3", "version": "1.0.4",
"base64": "81d1465772d0233e3a9648ed0e0ea289f51d1dcf12fef8b29b25488268256521", "base64": "92038664655c6cb1d6b422b9546da1a98da58b0251394cf2a485aed9b1774c07",
"binary": "42d28f3598669b801821879f0b7e26389300f151bfbc566b11c214e6091719ed" "binary": "d05185aca67768f35227565dacce030fdff233563f119781baaf4e95f9198115"
}, },
"org.standardnotes.code-editor": { "org.standardnotes.code-editor": {
"version": "1.3.12", "version": "1.3.12",
@@ -95,8 +95,8 @@
"binary": "88d4b9a6ff94b1f6cba884787e051cda3cd8073022984b63f7d3b13157aa6e2a" "binary": "88d4b9a6ff94b1f6cba884787e051cda3cd8073022984b63f7d3b13157aa6e2a"
}, },
"org.standardnotes.markdown-visual-editor": { "org.standardnotes.markdown-visual-editor": {
"version": "1.0.1", "version": "1.0.2",
"base64": "b0984c4a35b45eb4035a2dcdc0ba71b7b68b5dffcdeea53d0c5fbcbc0ff85894", "base64": "40e25db3e2b2d3ce18088ff80b5039a9f32e9a2151d08af7a29a5beb520edc3f",
"binary": "d8ac1028cf50d60f7a80010dd1c6047f57e01cd066ea163f169d514d1e7d2e71" "binary": "9327b7f312974c754f979493dfd6f3652f6b98c2be44d6b82b9d3e27d5a901c3"
} }
} }

View File

@@ -1,26 +1,25 @@
{ {
"files": { "files": {
"main.css": "./static/css/main.f7535a78.chunk.css", "main.css": "./static/css/main.f7535a78.chunk.css",
"main.js": "./static/js/main.7192e457.chunk.js", "main.js": "./static/js/main.18531fb0.chunk.js",
"main.js.map": "./static/js/main.7192e457.chunk.js.map", "main.js.map": "./static/js/main.18531fb0.chunk.js.map",
"runtime-main.js": "./static/js/runtime-main.54ae45a0.js", "runtime-main.js": "./static/js/runtime-main.0f6dab54.js",
"runtime-main.js.map": "./static/js/runtime-main.54ae45a0.js.map", "runtime-main.js.map": "./static/js/runtime-main.0f6dab54.js.map",
"static/css/2.4ff8d936.chunk.css": "./static/css/2.4ff8d936.chunk.css", "static/css/2.4ff8d936.chunk.css": "./static/css/2.4ff8d936.chunk.css",
"static/js/2.2cfce575.chunk.js": "./static/js/2.2cfce575.chunk.js", "static/js/2.6a3180fe.chunk.js": "./static/js/2.6a3180fe.chunk.js",
"static/js/2.2cfce575.chunk.js.map": "./static/js/2.2cfce575.chunk.js.map", "static/js/2.6a3180fe.chunk.js.map": "./static/js/2.6a3180fe.chunk.js.map",
"static/js/3.bf2ee733.chunk.js": "./static/js/3.bf2ee733.chunk.js",
"static/js/3.bf2ee733.chunk.js.map": "./static/js/3.bf2ee733.chunk.js.map",
"index.html": "./index.html", "index.html": "./index.html",
"static/css/2.4ff8d936.chunk.css.map": "./static/css/2.4ff8d936.chunk.css.map", "static/css/2.4ff8d936.chunk.css.map": "./static/css/2.4ff8d936.chunk.css.map",
"static/css/main.f7535a78.chunk.css.map": "./static/css/main.f7535a78.chunk.css.map", "static/css/main.f7535a78.chunk.css.map": "./static/css/main.f7535a78.chunk.css.map",
"static/js/2.2cfce575.chunk.js.LICENSE.txt": "./static/js/2.2cfce575.chunk.js.LICENSE.txt", "static/js/2.6a3180fe.chunk.js.LICENSE.txt": "./static/js/2.6a3180fe.chunk.js.LICENSE.txt",
"static/js/main.18531fb0.chunk.js.LICENSE.txt": "./static/js/main.18531fb0.chunk.js.LICENSE.txt",
"static/media/main.scss": "./static/media/roboto-vietnamese-400-normal.c0bec65d.woff2" "static/media/main.scss": "./static/media/roboto-vietnamese-400-normal.c0bec65d.woff2"
}, },
"entrypoints": [ "entrypoints": [
"static/js/runtime-main.54ae45a0.js", "static/js/runtime-main.0f6dab54.js",
"static/css/2.4ff8d936.chunk.css", "static/css/2.4ff8d936.chunk.css",
"static/js/2.2cfce575.chunk.js", "static/js/2.6a3180fe.chunk.js",
"static/css/main.f7535a78.chunk.css", "static/css/main.f7535a78.chunk.css",
"static/js/main.7192e457.chunk.js" "static/js/main.18531fb0.chunk.js"
] ]
} }

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A lightweight WYSIWYG markdown editor, derivated from Milkdown editor"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Markdown Visual</title><link href="./static/css/2.4ff8d936.chunk.css" rel="stylesheet"><link href="./static/css/main.f7535a78.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],f=0,p=[];f<i.length;f++)a=i[f],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&p.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(s&&s(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{3:"bf2ee733"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./",a.oe=function(e){throw console.error(e),e};var i=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var s=c;t()}([])</script><script src="./static/js/2.2cfce575.chunk.js"></script><script src="./static/js/main.7192e457.chunk.js"></script></body></html> <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="A lightweight WYSIWYG markdown editor, derivated from Milkdown editor"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>Markdown Visual</title><link href="./static/css/2.4ff8d936.chunk.css" rel="stylesheet"><link href="./static/css/main.f7535a78.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var l=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([])</script><script src="./static/js/2.6a3180fe.chunk.js"></script><script src="./static/js/main.18531fb0.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,57 @@
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/*!
* Determine if an object is a Buffer
*
* @author Feross Aboukhadijeh <https://feross.org>
* @license MIT
*/
/**
* Prism: Lightweight, robust, elegant syntax highlighting
*
* @license MIT <https://opensource.org/licenses/MIT>
* @author Lea Verou <https://lea.verou.me>
* @namespace
* @public
*/
/** @license React v0.20.2
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v17.0.2
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
/**
* @license
* Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
* Build: `lodash include="includes,merge,filter,map,remove,find,omit,pull,cloneDeep,pick,uniq,sortedIndexBy,mergeWith"`
*/

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
!function(e){function r(r){for(var n,a,l=r[0],i=r[1],f=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="./";var l=this["webpackJsonp@standardnotes/markdown-visual"]=this["webpackJsonp@standardnotes/markdown-visual"]||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var f=0;f<l.length;f++)r(l[f]);var p=i;t()}([]);
//# sourceMappingURL=runtime-main.0f6dab54.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
{ {
"name": "@standardnotes/markdown-visual", "name": "@standardnotes/markdown-visual",
"version": "1.0.1", "version": "1.0.2",
"author": "Johnny Almonte <johnny@standardnotes.com>", "author": "Johnny Almonte <johnny@standardnotes.com>",
"description": "A lightweight WYSIWYG markdown editor, derivated from Milkdown editor", "description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
"keywords": [ "keywords": [
"Standard Notes", "Standard Notes",
"Standard Notes Extensions" "Standard Notes Extensions"
@@ -39,7 +39,6 @@
"@milkdown/plugin-clipboard": "^5.5.0", "@milkdown/plugin-clipboard": "^5.5.0",
"@milkdown/plugin-cursor": "^5.5.0", "@milkdown/plugin-cursor": "^5.5.0",
"@milkdown/plugin-diagram": "^5.5.0", "@milkdown/plugin-diagram": "^5.5.0",
"@milkdown/plugin-emoji": "^5.5.0",
"@milkdown/plugin-history": "^5.5.0", "@milkdown/plugin-history": "^5.5.0",
"@milkdown/plugin-indent": "^5.5.0", "@milkdown/plugin-indent": "^5.5.0",
"@milkdown/plugin-listener": "^5.5.0", "@milkdown/plugin-listener": "^5.5.0",

View File

@@ -2,7 +2,7 @@
.section.tags, .section.tags,
navigation { navigation {
flex: none !important; flex: none !important;
width: 120px !important; width: 190px !important;
transition: width 0.25s; transition: width 0.25s;
} }
@@ -10,21 +10,21 @@ navigation {
.section.tags:hover, .section.tags:hover,
navigation:hover { navigation:hover {
flex: initial; flex: initial;
width: 180px !important; width: 250px !important;
transition: width 0.25s; transition: width 0.25s;
} }
.section.notes, .section.notes,
notes-view { notes-view {
flex: none !important; flex: none !important;
width: 200px !important; width: 270px !important;
transition: width 0.25s; transition: width 0.25s;
} }
.section.notes:hover, .section.notes:hover,
notes-view:hover { notes-view:hover {
flex: initial; flex: initial;
width: 350px !important; width: 420px !important;
transition: width 0.25s; transition: width 0.25s;
} }

View File

@@ -1,7 +1 @@
{ {"version":3,"sourceRoot":"","sources":["../src/main.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA","file":"dist.css"}
"version": 3,
"mappings": "AAAA;;UAEW;EACT,IAAI,EAAE,eAAe;EACrB,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;;gBAEiB;EACf,IAAI,EAAE,OAAO;EACb,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;UACW;EACT,IAAI,EAAE,eAAe;EACrB,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW;;;AAGzB;gBACiB;EACf,IAAI,EAAE,OAAO;EACb,KAAK,EAAE,gBAAgB;EACvB,UAAU,EAAE,WAAW",
"sources": ["../src/main.scss"],
"names": [],
"file": "dist.css"
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "sn-theme-dynamic", "name": "@standardnotes/dynamic-theme",
"version": "1.0.3", "version": "1.0.4",
"main": "dist/dist.css", "main": "dist/dist.css",
"devDependencies": { "devDependencies": {
"grunt": "^1.0.1", "grunt": "^1.0.1",

265
yarn.lock
View File

@@ -1751,10 +1751,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@bugsnag/browser@^7.16.1": "@bugsnag/browser@^7.16.2":
version "7.16.1" version "7.16.2"
resolved "https://registry.yarnpkg.com/@bugsnag/browser/-/browser-7.16.1.tgz#652aa3ed64e51ba0015878d252a08917429bba03" resolved "https://registry.yarnpkg.com/@bugsnag/browser/-/browser-7.16.2.tgz#b6fc7ebaeae4800d195b660abc770caf33670e03"
integrity sha512-Tq9fWpwmqdOsbedYL67GzsTKrG5MERIKtnKCi5FyvFjTj143b6as0pwj7LWQ+Eh8grWlR7S11+VvJmb8xnY8Tg== integrity sha512-iBbAmjTDe0I6WPTHi3wIcmKu3ykydtT6fc8atJA65rzgDLMlTM1Wnwz4Ny1cn0bVouLGa48BRiOJ27Rwy7QRYA==
dependencies: dependencies:
"@bugsnag/core" "^7.16.1" "@bugsnag/core" "^7.16.1"
@@ -1774,18 +1774,18 @@
resolved "https://registry.yarnpkg.com/@bugsnag/cuid/-/cuid-3.0.0.tgz#2ee7642a30aee6dc86f5e7f824653741e42e5c35" resolved "https://registry.yarnpkg.com/@bugsnag/cuid/-/cuid-3.0.0.tgz#2ee7642a30aee6dc86f5e7f824653741e42e5c35"
integrity sha512-LOt8aaBI+KvOQGneBtpuCz3YqzyEAehd1f3nC5yr9TIYW1+IzYKa2xWS4EiMz5pPOnRPHkyyS5t/wmSmN51Gjg== integrity sha512-LOt8aaBI+KvOQGneBtpuCz3YqzyEAehd1f3nC5yr9TIYW1+IzYKa2xWS4EiMz5pPOnRPHkyyS5t/wmSmN51Gjg==
"@bugsnag/js@^7.16.1": "@bugsnag/js@^7.16.2":
version "7.16.1" version "7.16.2"
resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.16.1.tgz#4a4ec2c7f3e047333e7d15eb53cb11f165b7067f" resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-7.16.2.tgz#fb15ec9cc5980f0b210aecc7b740274e50400a91"
integrity sha512-yb83OmsbIMDJhX3hHhbHl5StN72feqdr/Ctq7gqsdcfOHNb2121Edf2EbegPJKZhFqSik66vWwiVbGJ6CdS/UQ== integrity sha512-AzV0PtG3SZt+HnA2JmRJeI60aDNZsIJbEEAZIWZeATvWBt5RdVdsWKllM1SkTvURfxfdAVd4Xry3BgVrh8nEbg==
dependencies: dependencies:
"@bugsnag/browser" "^7.16.1" "@bugsnag/browser" "^7.16.2"
"@bugsnag/node" "^7.16.1" "@bugsnag/node" "^7.16.2"
"@bugsnag/node@^7.16.1": "@bugsnag/node@^7.16.2":
version "7.16.1" version "7.16.2"
resolved "https://registry.yarnpkg.com/@bugsnag/node/-/node-7.16.1.tgz#473bb6eeb346b418295b49e4c4576e0004af4901" resolved "https://registry.yarnpkg.com/@bugsnag/node/-/node-7.16.2.tgz#8ac1b41786306d8917fb9fe222ada74fe0c4c6d5"
integrity sha512-9zBA1IfDTbLKMoDltdhELpTd1e+b5+vUW4j40zGA+4SYIe64XNZKShfqRdvij7embvC1iHQ9UpuPRSk60P6Dng== integrity sha512-V5pND701cIYGzjjTwt0tuvAU1YyPB9h7vo5F/DzrDHRPmCINA/oVbc0Twco87knc2VPe8ntGFqTicTY65iOWzg==
dependencies: dependencies:
"@bugsnag/core" "^7.16.1" "@bugsnag/core" "^7.16.1"
byline "^5.0.0" byline "^5.0.0"
@@ -2313,10 +2313,10 @@
dependencies: dependencies:
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/auth@^3.17.3": "@standardnotes/auth@^3.17.4":
version "3.17.3" version "3.17.4"
resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.17.3.tgz#a00f10faa0fb2a7dd76509d3b678f85818aad63c" resolved "https://registry.yarnpkg.com/@standardnotes/auth/-/auth-3.17.4.tgz#ab2449a280ee6ec794fe397c9d8387e105c6c644"
integrity sha512-tb5ylXuDBPhgeZZynNsMk83N74NMMV9z6M9hyrwuK5HbKWM5r5L9U8lwFawG8flqTKpYzPeWxmaRFZT/5qR22Q== integrity sha512-0710hUiYoRFjABfUFPlyOIyCMx0gC0rlJtFdPYK7WHXf0bfxO0JiSXeWbNSvV0QVGqHIkcUjGmdyE6cJEKTh9g==
dependencies: dependencies:
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
jsonwebtoken "^8.5.1" jsonwebtoken "^8.5.1"
@@ -2326,60 +2326,66 @@
resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.15.3.tgz#0b8ce48b81b260abe2d405431fb04aacb44b5a01" resolved "https://registry.yarnpkg.com/@standardnotes/common/-/common-1.15.3.tgz#0b8ce48b81b260abe2d405431fb04aacb44b5a01"
integrity sha512-9oh/W3sFQYyA5Vabcbu6BUkLVkFq/25Q5EK9KCd4aT9QnDJ9JQlTtzDmTk1jYuM6rnccsJ6SW2pcWjbi9FVniw== integrity sha512-9oh/W3sFQYyA5Vabcbu6BUkLVkFq/25Q5EK9KCd4aT9QnDJ9JQlTtzDmTk1jYuM6rnccsJ6SW2pcWjbi9FVniw==
"@standardnotes/components@1.7.9": "@standardnotes/components@1.7.11":
version "1.7.9" version "1.7.11"
resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.9.tgz#41e5fdbcee250b9b3c18045dad8998c6f668307b" resolved "https://registry.yarnpkg.com/@standardnotes/components/-/components-1.7.11.tgz#791c1e1bef5bc223f34c423e4ddd443fe8a699d3"
integrity sha512-/+Paw6ry/IS9ldYUM/lgC4O6qwl1fukWvNw65IMKyB9LMY3+xTh/I2BfnWynP117pVPxtu3/2+FBEnx04KvQwg== integrity sha512-V8gtuLMbn0ldRQLZj0iKrm5PRufHdRGbSQ32/u0w1M6dTE42wKUYuMNhPwkn9cNEaYzhhcHRD01L/lYEepBZBQ==
"@standardnotes/domain-events@^2.23.21": "@standardnotes/domain-events@^2.24.5":
version "2.23.21" version "2.24.5"
resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.23.21.tgz#bbf752ee7a0fd08b9fb675e81b46d3c10bbf89e9" resolved "https://registry.yarnpkg.com/@standardnotes/domain-events/-/domain-events-2.24.5.tgz#f4699b2241e0e51829d88ec8afe1b8d00d5fe37d"
integrity sha512-YPpwy+QrDziBOpjt5cOIZwY47fOddN3038+NTRSqxi4h/D8hU+U5O8dGl2XktENEq9DqVJ78OVmWBjkA2FlsEQ== integrity sha512-NrbeaQ0Yl+56cMlXOLSISHCpkiRTAcmmRtIEPAqn0V7RBeRXqKfy6Fo5OUPSuGtYinQfbKBLi5rxIC/rqhMZFg==
dependencies: dependencies:
"@standardnotes/auth" "^3.17.3" "@standardnotes/auth" "^3.17.4"
"@standardnotes/features" "^1.34.1" "@standardnotes/features" "^1.34.8"
"@standardnotes/features@1.34.1", "@standardnotes/features@^1.34.1": "@standardnotes/features@1.34.8", "@standardnotes/features@^1.34.8":
version "1.34.1" version "1.34.8"
resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.34.1.tgz#da2e37a437ba274dfbdb9e0f5ab9de3ee95689ee" resolved "https://registry.yarnpkg.com/@standardnotes/features/-/features-1.34.8.tgz#a82b7648706bdf62355eff6df1f4ccadf0b83afa"
integrity sha512-ILc4mSLoAE6S24GquNAS5TNLqM+RBKx8zyfFVgKEUB2sF00341HDi+/l5MuCCds2VnhTvIuVcIsCrPlj9NVmFg== integrity sha512-0ttEnBjod7zANER0sU6TzCz0RHSt+p1lQ9xzg9a3z6Azjz8WAJkQAMeIBJNgym8EPVI8QUjKFyz/Rdo0PNAyng==
dependencies: dependencies:
"@standardnotes/auth" "^3.17.3" "@standardnotes/auth" "^3.17.4"
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/payloads@^1.3.2": "@standardnotes/filepicker@1.8.0":
version "1.3.2" version "1.8.0"
resolved "https://registry.yarnpkg.com/@standardnotes/payloads/-/payloads-1.3.2.tgz#3255abf6e2a2385c73e7998705066b828e5a74e0" resolved "https://registry.yarnpkg.com/@standardnotes/filepicker/-/filepicker-1.8.0.tgz#f8d85350c4b4022235e3017b0b2c7841882eef4f"
integrity sha512-SnDqdQXyEWett/Y33crvNnDGwv4BfCkoHId99fLd+UL5uTgXpuFgkd/AVHg+mOT5xC3+KAR8zdUCmQSXTRuysg== integrity sha512-xgFoD+aHFCKV5pAbhKNCyyhUL18G9l2Aep6eiQ5gxB55l8CcNHlLBi5qw5i1we07NdCwIJ3yP3aVKI+7qe22yQ==
"@standardnotes/payloads@^1.4.7":
version "1.4.7"
resolved "https://registry.yarnpkg.com/@standardnotes/payloads/-/payloads-1.4.7.tgz#e722a117a1bb02b76555cc6b164eefdb765bea9f"
integrity sha512-0y7fDqu1OBMhvIrQDkJbsYEzpPqodSM9X8c5s6hVCs4GtriQcw1JAjsekRyV2/9iKd8GiOldNNWBDDYre+78gQ==
dependencies: dependencies:
"@standardnotes/applications" "^1.1.3" "@standardnotes/applications" "^1.1.3"
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/features" "^1.34.1" "@standardnotes/features" "^1.34.8"
"@standardnotes/utils" "^1.2.3" "@standardnotes/utils" "^1.2.3"
"@standardnotes/responses@^1.1.7": "@standardnotes/responses@1.3.7", "@standardnotes/responses@^1.3.7":
version "1.1.7" version "1.3.7"
resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.1.7.tgz#ab982f94693f2e1f967809bdb5e3863182770b14" resolved "https://registry.yarnpkg.com/@standardnotes/responses/-/responses-1.3.7.tgz#6304d55613df77342c21336b8c4000a65d3bb42f"
integrity sha512-1q8+eGjvttleesciHAOTe6u478W3UpEy+0StT8ZL3miFWzIyCLXJmnxNbYfYCm6oZ+t2rwhW+AzmnMRhG2cOUA== integrity sha512-RYIWNB6RinZmOSre6lfLmFAdpN0cSWb5PCjmYAThedkor21iK//9yhOG5xKChqIfB+Y+kaKFfnm8+BtONcMD0A==
dependencies: dependencies:
"@standardnotes/auth" "^3.17.3" "@standardnotes/auth" "^3.17.4"
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/features" "^1.34.1" "@standardnotes/features" "^1.34.8"
"@standardnotes/payloads" "^1.3.2" "@standardnotes/payloads" "^1.4.7"
"@standardnotes/services@^1.4.0": "@standardnotes/services@1.5.9", "@standardnotes/services@^1.5.9":
version "1.4.0" version "1.5.9"
resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.4.0.tgz#776ee5d022e4512844af1a284a2e90f599217218" resolved "https://registry.yarnpkg.com/@standardnotes/services/-/services-1.5.9.tgz#4356c739659145553fafe87d59f98db6c9dc4ad8"
integrity sha512-wO0LQ+qMG0bfH0HNPulsO8nZ2Z1Y84NLP0fZdMdtqiuaCi1GrM/PUlcL/fpXCJKNeTKoYa8Dh4PfF8DOjalKXg== integrity sha512-EiiAbFGwsqwgb2cFLDKt1Jb9LpuKynrVwYtDUoxZlM9FiRvcmQdK9w+0upS/mthcreaiKHjdkSuQMwJkCaU3rw==
dependencies: dependencies:
"@standardnotes/applications" "^1.1.3" "@standardnotes/applications" "^1.1.3"
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/responses" "^1.3.7"
"@standardnotes/utils" "^1.2.3" "@standardnotes/utils" "^1.2.3"
"@standardnotes/settings@^1.11.5": "@standardnotes/settings@1.12.0", "@standardnotes/settings@^1.12.0":
version "1.11.5" version "1.12.0"
resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.11.5.tgz#792bf3e0505065486f521b2f19c2bf1081b8fa5e" resolved "https://registry.yarnpkg.com/@standardnotes/settings/-/settings-1.12.0.tgz#43f3dd7f015f726b1ed88a48fcc3737899116cd5"
integrity sha512-n6StAS3nBgs7Lia5mOt3+H4Xd6hatCcHFx83paFq9kdI1cKbqn7oFF4g/rUbWPy4nsx+96zBehB6EhsJ2MGzyQ== integrity sha512-w6S5TT7KRpvUb+JsXZ7ucWPjlWRtpKQdsyT7cLs66ynKRXxUn40hf4kA8T9FhuLAKbG+wIYDrAZl3FRk+HvDWQ==
"@standardnotes/sncrypto-common@^1.7.3": "@standardnotes/sncrypto-common@^1.7.3":
version "1.7.3" version "1.7.3"
@@ -2395,27 +2401,27 @@
buffer "^6.0.3" buffer "^6.0.3"
libsodium-wrappers "^0.7.9" libsodium-wrappers "^0.7.9"
"@standardnotes/snjs@2.73.2": "@standardnotes/snjs@2.79.5":
version "2.73.2" version "2.79.5"
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.73.2.tgz#5361d73c861b990d06821d5a822dcb0893830a43" resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.79.5.tgz#2df9ef88d0c0ffead2e0044249c5a833921d94dc"
integrity sha512-oGPZmzX+tNWQFxmvrQRtsqlofbW25YUOapyEPN8oVgfwV98r8EVzpUGXHWkog3ZW6oO3aO22Rhtk0D92d0CqtQ== integrity sha512-IEZtHg6Ga4/R2QUMbJhD4MEOvkNuBtiNYrokpqrf5kzGGOvL3b/RwcSWUW8zjBS1WxDSGmIo3WBKze3FXhfdEA==
dependencies: dependencies:
"@standardnotes/applications" "^1.1.3" "@standardnotes/applications" "^1.1.3"
"@standardnotes/auth" "^3.17.3" "@standardnotes/auth" "^3.17.4"
"@standardnotes/common" "^1.15.3" "@standardnotes/common" "^1.15.3"
"@standardnotes/domain-events" "^2.23.21" "@standardnotes/domain-events" "^2.24.5"
"@standardnotes/features" "^1.34.1" "@standardnotes/features" "^1.34.8"
"@standardnotes/payloads" "^1.3.2" "@standardnotes/payloads" "^1.4.7"
"@standardnotes/responses" "^1.1.7" "@standardnotes/responses" "^1.3.7"
"@standardnotes/services" "^1.4.0" "@standardnotes/services" "^1.5.9"
"@standardnotes/settings" "^1.11.5" "@standardnotes/settings" "^1.12.0"
"@standardnotes/sncrypto-common" "^1.7.3" "@standardnotes/sncrypto-common" "^1.7.3"
"@standardnotes/utils" "^1.2.3" "@standardnotes/utils" "^1.2.3"
"@standardnotes/stylekit@5.14.0": "@standardnotes/stylekit@5.15.0":
version "5.14.0" version "5.15.0"
resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.14.0.tgz#806b7d896fb94de8be72e762dd205beefff05fd7" resolved "https://registry.yarnpkg.com/@standardnotes/stylekit/-/stylekit-5.15.0.tgz#14c0ff5c4e40d4afa9ea6fec0431934e1184a18f"
integrity sha512-zCB8QFPUTe+063RsLNCudkP6FY6ujKC3iE2pD6ak4htpXtW6DndwYbhEffuiWIjbRzDGAy0YyQ7y8V19Jw7ElQ== integrity sha512-BR78DIdXo8fxzNPruiugiQJfgT7usUFWxJ0CPnjhk3hshYM2/kleHdcrahYdi2Rs5MR7chhbsvUFUWHxykOaRg==
dependencies: dependencies:
"@nanostores/preact" "^0.1.3" "@nanostores/preact" "^0.1.3"
"@reach/listbox" "^0.16.2" "@reach/listbox" "^0.16.2"
@@ -2767,10 +2773,10 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
"@types/react@^17.0.39": "@types/react@^17.0.40":
version "17.0.39" version "17.0.40"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad"
integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==
dependencies: dependencies:
"@types/prop-types" "*" "@types/prop-types" "*"
"@types/scheduler" "*" "@types/scheduler" "*"
@@ -2813,6 +2819,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/wicg-file-system-access@^2020.9.5":
version "2020.9.5"
resolved "https://registry.yarnpkg.com/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.5.tgz#4a0c8f3d1ed101525f329e86c978f7735404474f"
integrity sha512-UYK244awtmcUYQfs7FR8710MJcefL2WvkyHMjA8yJzxd1mo0Gfn88sRZ1Bls7hiUhA2w7ne1gpJ9T5g3G0wOyA==
"@types/ws@^8.2.2": "@types/ws@^8.2.2":
version "8.2.2" version "8.2.2"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
@@ -2832,14 +2843,14 @@
dependencies: dependencies:
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^5.13.0": "@typescript-eslint/eslint-plugin@^5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.13.0.tgz#2809052b85911ced9c54a60dac10e515e9114497" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz#5119b67152356231a0e24b998035288a9cd21335"
integrity sha512-vLktb2Uec81fxm/cfz2Hd6QaWOs8qdmVAZXLdOBX6JFJDhf6oDZpMzZ4/LZ6SFM/5DgDcxIMIvy3F+O9yZBuiQ== integrity sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.13.0" "@typescript-eslint/scope-manager" "5.14.0"
"@typescript-eslint/type-utils" "5.13.0" "@typescript-eslint/type-utils" "5.14.0"
"@typescript-eslint/utils" "5.13.0" "@typescript-eslint/utils" "5.14.0"
debug "^4.3.2" debug "^4.3.2"
functional-red-black-tree "^1.0.1" functional-red-black-tree "^1.0.1"
ignore "^5.1.8" ignore "^5.1.8"
@@ -2847,69 +2858,69 @@
semver "^7.3.5" semver "^7.3.5"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/parser@^5.13.0": "@typescript-eslint/parser@^5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.14.0.tgz#7c79f898aa3cff0ceee6f1d34eeed0f034fb9ef3"
integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg== integrity sha512-aHJN8/FuIy1Zvqk4U/gcO/fxeMKyoSv/rS46UXMXOJKVsLQ+iYPuXNbpbH7cBLcpSbmyyFbwrniLx5+kutu1pw==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.13.0" "@typescript-eslint/scope-manager" "5.14.0"
"@typescript-eslint/types" "5.13.0" "@typescript-eslint/types" "5.14.0"
"@typescript-eslint/typescript-estree" "5.13.0" "@typescript-eslint/typescript-estree" "5.14.0"
debug "^4.3.2" debug "^4.3.2"
"@typescript-eslint/scope-manager@5.13.0": "@typescript-eslint/scope-manager@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz#ea518962b42db8ed0a55152ea959c218cb53ca7b"
integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA== integrity sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==
dependencies: dependencies:
"@typescript-eslint/types" "5.13.0" "@typescript-eslint/types" "5.14.0"
"@typescript-eslint/visitor-keys" "5.13.0" "@typescript-eslint/visitor-keys" "5.14.0"
"@typescript-eslint/type-utils@5.13.0": "@typescript-eslint/type-utils@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.13.0.tgz#b0efd45c85b7bab1125c97b752cab3a86c7b615d" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz#711f08105860b12988454e91df433567205a8f0b"
integrity sha512-/nz7qFizaBM1SuqAKb7GLkcNn2buRdDgZraXlkhz+vUGiN1NZ9LzkA595tHHeduAiS2MsHqMNhE2zNzGdw43Yg== integrity sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==
dependencies: dependencies:
"@typescript-eslint/utils" "5.13.0" "@typescript-eslint/utils" "5.14.0"
debug "^4.3.2" debug "^4.3.2"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/types@5.13.0": "@typescript-eslint/types@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.14.0.tgz#96317cf116cea4befabc0defef371a1013f8ab11"
integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg== integrity sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==
"@typescript-eslint/typescript-estree@5.13.0": "@typescript-eslint/typescript-estree@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz#78b7f7385d5b6f2748aacea5c9b7f6ae62058314"
integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA== integrity sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==
dependencies: dependencies:
"@typescript-eslint/types" "5.13.0" "@typescript-eslint/types" "5.14.0"
"@typescript-eslint/visitor-keys" "5.13.0" "@typescript-eslint/visitor-keys" "5.14.0"
debug "^4.3.2" debug "^4.3.2"
globby "^11.0.4" globby "^11.0.4"
is-glob "^4.0.3" is-glob "^4.0.3"
semver "^7.3.5" semver "^7.3.5"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/utils@5.13.0": "@typescript-eslint/utils@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.13.0.tgz#2328feca700eb02837298339a2e49c46b41bd0af" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.14.0.tgz#6c8bc4f384298cbbb32b3629ba7415f9f80dc8c4"
integrity sha512-+9oHlPWYNl6AwwoEt5TQryEHwiKRVjz7Vk6kaBeD3/kwHE5YqTGHtm/JZY8Bo9ITOeKutFaXnBlMgSATMJALUQ== integrity sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==
dependencies: dependencies:
"@types/json-schema" "^7.0.9" "@types/json-schema" "^7.0.9"
"@typescript-eslint/scope-manager" "5.13.0" "@typescript-eslint/scope-manager" "5.14.0"
"@typescript-eslint/types" "5.13.0" "@typescript-eslint/types" "5.14.0"
"@typescript-eslint/typescript-estree" "5.13.0" "@typescript-eslint/typescript-estree" "5.14.0"
eslint-scope "^5.1.1" eslint-scope "^5.1.1"
eslint-utils "^3.0.0" eslint-utils "^3.0.0"
"@typescript-eslint/visitor-keys@5.13.0": "@typescript-eslint/visitor-keys@5.14.0":
version "5.13.0" version "5.14.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz#1927005b3434ccd0d3ae1b2ecf60e65943c36986"
integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g== integrity sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==
dependencies: dependencies:
"@typescript-eslint/types" "5.13.0" "@typescript-eslint/types" "5.14.0"
eslint-visitor-keys "^3.0.0" eslint-visitor-keys "^3.0.0"
"@webassemblyjs/ast@1.11.1": "@webassemblyjs/ast@1.11.1":
@@ -4115,10 +4126,10 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
css-loader@^6.7.0: css-loader@^6.7.1:
version "6.7.0" version "6.7.1"
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.0.tgz#c1200da1dfffe6643b18bda20fdd84cad3e36d39" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
integrity sha512-S7HCfCiDHLA+VXKqdZwyRZgoO0R9BnKDnVIoHMq5grl3N86zAu7MB+FBWHr5xOJC8SmvpTLha/2NpfFkFEN/ig== integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
dependencies: dependencies:
icss-utils "^5.1.0" icss-utils "^5.1.0"
postcss "^8.4.7" postcss "^8.4.7"
@@ -8002,10 +8013,10 @@ qr.js@0.0.0:
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8= integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
qrcode.react@^1.0.1: qrcode.react@^2.0.0:
version "1.0.1" version "2.0.0"
resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-1.0.1.tgz#2834bb50e5e275ffe5af6906eff15391fe9e38a5" resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-2.0.0.tgz#20b738eddcfc6958673bbbd1a0c5568ee5c399f5"
integrity sha512-8d3Tackk8IRLXTo67Y+c1rpaiXjoz/Dd2HpcMdW//62/x8J1Nbho14Kh8x974t9prsLHN6XqVgcnRiBGFptQmg== integrity sha512-1CCzwC4KHYCzOLb7M+lKYqHzDMVsppKJBhrU1ZDCuJRHKiD99LeDHOkuvUKCRvnJgDzp9SnB6/WNA7qp6I7ozA==
dependencies: dependencies:
loose-envify "^1.4.0" loose-envify "^1.4.0"
prop-types "^15.6.0" prop-types "^15.6.0"