mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-08-29 10:09:26 +02:00
Compare commits
43 Commits
3.0.24-and
...
fix-loadin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41605c308a | ||
|
|
77a2f86119 | ||
|
|
71db85458d | ||
|
|
0be59c71de | ||
|
|
2357e3de0b | ||
|
|
53b89a59ed | ||
|
|
202c9135c2 | ||
|
|
69e6263d34 | ||
|
|
ebe830bbcc | ||
|
|
44bb9752d2 | ||
|
|
2f6af50fe2 | ||
|
|
1d80ca7d3c | ||
|
|
d33baebac8 | ||
|
|
25aaef361a | ||
|
|
f3af39a093 | ||
|
|
149c2013a2 | ||
|
|
8fb0b6f94a | ||
|
|
f09e5cadcf | ||
|
|
724c69655b | ||
|
|
800f3ca5d3 | ||
|
|
193007370a | ||
|
|
c8aff3764b | ||
|
|
d72b502977 | ||
|
|
263c970320 | ||
|
|
aeebc9878d | ||
|
|
4f37c92ecd | ||
|
|
3180a88ef0 | ||
|
|
6672db6ac2 | ||
|
|
b6da62469e | ||
|
|
e67e6374c3 | ||
|
|
ff1fc5b391 | ||
|
|
2f8bf191aa | ||
|
|
c4af2b2159 | ||
|
|
690b157387 | ||
|
|
5a276a43a4 | ||
|
|
a364ae2e9d | ||
|
|
1e2055c81b | ||
|
|
31d110252c | ||
|
|
702976c6cf | ||
|
|
3ec8500515 | ||
|
|
2c03420f33 | ||
|
|
868ae597ae | ||
|
|
59d3e06935 |
@@ -36,7 +36,7 @@ await fs.rm("./build/", { force: true, recursive: true });
|
||||
|
||||
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
|
||||
await exec(
|
||||
"yarn nx build:desktop @notesnook/web",
|
||||
"npx nx build:desktop @notesnook/web",
|
||||
path.join(__dirname, "..", "..", "..")
|
||||
);
|
||||
}
|
||||
@@ -58,11 +58,13 @@ if (args.variant === "mas") {
|
||||
await exec(`yarn run build`);
|
||||
|
||||
if (args.run) {
|
||||
await exec(`yarn electron-builder --dir --x64`);
|
||||
await exec(`yarn electron-builder --dir --${process.arch}`);
|
||||
if (process.platform === "win32") {
|
||||
await exec(`.\\output\\win-unpacked\\Notesnook.exe`);
|
||||
} else if (process.platform === "darwin") {
|
||||
await exec(`./output/mac/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
if (process.arch === "arm64")
|
||||
await exec(`./output/mac-arm64/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
else await exec(`./output/mac/Notesnook.app/Contents/MacOS/Notesnook`);
|
||||
} else {
|
||||
await exec(`./output/linux-unpacked/Notesnook`);
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ async function onChange(first) {
|
||||
|
||||
if (first) {
|
||||
await spawnAndWaitUntil(
|
||||
["yarn", "nx", "start:desktop", "@notesnook/web"],
|
||||
path.join(__dirname, "..", "..", ".."),
|
||||
["npm", "run", "start:desktop"],
|
||||
path.join(__dirname, "..", "..", "web"),
|
||||
(data) => data.includes("Network: use --host to expose")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,79 +159,77 @@ export function clearDatabaseKey() {
|
||||
|
||||
export async function getDatabaseKey(appLockPassword?: string) {
|
||||
if (DB_KEY) return DB_KEY;
|
||||
try {
|
||||
if (appLockPassword) {
|
||||
const databaseKeyCipher: Cipher =
|
||||
CipherStorage.getMap("databaseKeyCipher");
|
||||
const databaseKey = await decrypt(
|
||||
{
|
||||
password: appLockPassword
|
||||
},
|
||||
databaseKeyCipher
|
||||
);
|
||||
DatabaseLogger.info("Getting database key from cipher");
|
||||
DB_KEY = databaseKey;
|
||||
}
|
||||
if (appLockPassword) {
|
||||
const databaseKeyCipher: Cipher = CipherStorage.getMap("databaseKeyCipher");
|
||||
const databaseKey = await decrypt(
|
||||
{
|
||||
password: appLockPassword
|
||||
},
|
||||
databaseKeyCipher
|
||||
);
|
||||
DatabaseLogger.info("Getting database key from cipher");
|
||||
DB_KEY = databaseKey;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
const hasKey = await Keychain.hasInternetCredentials(
|
||||
if (!DB_KEY) {
|
||||
const hasKey = await Keychain.hasInternetCredentials(KEYCHAIN_SERVER_DBKEY);
|
||||
if (hasKey) {
|
||||
const credentials = await Keychain.getInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
);
|
||||
if (hasKey) {
|
||||
const credentials = await Keychain.getInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
);
|
||||
|
||||
DatabaseLogger.info("Getting database key from Keychain");
|
||||
DB_KEY = (credentials as Keychain.UserCredentials).password;
|
||||
}
|
||||
DatabaseLogger.info("Getting database key from Keychain");
|
||||
DB_KEY = (credentials as Keychain.UserCredentials).password;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
DatabaseLogger.info("Generating new database key");
|
||||
const password = generatePassword();
|
||||
const derivedDatabaseKey = await Sodium.deriveKey(
|
||||
password,
|
||||
NOTESNOOK_DB_KEY_SALT
|
||||
);
|
||||
|
||||
DB_KEY = derivedDatabaseKey.key as string;
|
||||
|
||||
await Keychain.setInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY,
|
||||
"notesnook",
|
||||
DB_KEY,
|
||||
KEYSTORE_CONFIG
|
||||
);
|
||||
}
|
||||
|
||||
if (await Keychain.hasInternetCredentials("notesnook")) {
|
||||
const userKeyCredentials = await Keychain.getInternetCredentials(
|
||||
"notesnook"
|
||||
);
|
||||
|
||||
if (userKeyCredentials) {
|
||||
const userKeyCipher: Cipher = (await encrypt(
|
||||
{
|
||||
key: DB_KEY,
|
||||
salt: NOTESNOOK_DB_KEY_SALT
|
||||
},
|
||||
userKeyCredentials.password
|
||||
)) as Cipher;
|
||||
// Store encrypted user key in MMKV
|
||||
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
|
||||
await Keychain.resetInternetCredentials("notesnook");
|
||||
}
|
||||
DatabaseLogger.info("Migrated user credentials to cipher storage");
|
||||
}
|
||||
|
||||
return DB_KEY;
|
||||
} catch (e) {
|
||||
ToastManager.error(e as Error, "Error getting database key");
|
||||
console.log(e, "error");
|
||||
DatabaseLogger.error(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
DatabaseLogger.info("Generating new database key");
|
||||
const password = generatePassword();
|
||||
const derivedDatabaseKey = await Sodium.deriveKey(
|
||||
password,
|
||||
NOTESNOOK_DB_KEY_SALT
|
||||
);
|
||||
|
||||
DB_KEY = derivedDatabaseKey.key as string;
|
||||
|
||||
await Keychain.setInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY,
|
||||
"notesnook",
|
||||
DB_KEY,
|
||||
KEYSTORE_CONFIG
|
||||
);
|
||||
}
|
||||
|
||||
if (await Keychain.hasInternetCredentials("notesnook")) {
|
||||
const userKeyCredentials = await Keychain.getInternetCredentials(
|
||||
"notesnook"
|
||||
);
|
||||
|
||||
if (userKeyCredentials) {
|
||||
const userKeyCipher: Cipher = (await encrypt(
|
||||
{
|
||||
key: DB_KEY,
|
||||
salt: NOTESNOOK_DB_KEY_SALT
|
||||
},
|
||||
userKeyCredentials.password
|
||||
)) as Cipher;
|
||||
// Store encrypted user key in MMKV
|
||||
MMKV.setMap(USER_KEY_CIPHER, userKeyCipher);
|
||||
await Keychain.resetInternetCredentials("notesnook");
|
||||
}
|
||||
DatabaseLogger.info("Migrated user credentials to cipher storage");
|
||||
}
|
||||
|
||||
if (!DB_KEY) {
|
||||
throw new Error(
|
||||
`Failed to get database key, ${await Keychain.hasInternetCredentials(
|
||||
KEYCHAIN_SERVER_DBKEY
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
return DB_KEY;
|
||||
}
|
||||
|
||||
export async function deriveCryptoKeyFallback(data: SerializedKey) {
|
||||
|
||||
@@ -47,7 +47,8 @@ export default {
|
||||
deleteCacheFileByPath,
|
||||
getCacheSize,
|
||||
requestPermission,
|
||||
checkAndCreateDir
|
||||
checkAndCreateDir,
|
||||
getUploadedFileSize
|
||||
};
|
||||
|
||||
export const FileStorage: IFileStorage = {
|
||||
|
||||
@@ -38,7 +38,7 @@ import { useAppState } from "../../hooks/use-app-state";
|
||||
|
||||
export interface BaseDialogProps extends PropsWithChildren {
|
||||
animation?: "fade" | "none" | "slide" | undefined;
|
||||
visible: boolean;
|
||||
visible?: boolean;
|
||||
onRequestClose?: () => void;
|
||||
onShow?: () => void;
|
||||
premium?: boolean;
|
||||
|
||||
@@ -179,6 +179,7 @@ const ColorPicker = ({
|
||||
title: title.current,
|
||||
colorCode: selectedColor
|
||||
});
|
||||
if (!id) return;
|
||||
useRelationStore.getState().update();
|
||||
useMenuStore.getState().setColorNotes();
|
||||
setVisible(false);
|
||||
|
||||
@@ -297,7 +297,6 @@ function getDate(item: Item, groupType?: GroupingKey): number {
|
||||
groupType
|
||||
? db.settings.getGroupOptions(groupType)
|
||||
: {
|
||||
groupBy: "default",
|
||||
sortBy: "dateEdited",
|
||||
sortDirection: "desc"
|
||||
},
|
||||
|
||||
@@ -129,7 +129,10 @@ export const PricingPlans = ({
|
||||
if (code.startsWith("com.streetwriters.notesnook")) {
|
||||
skuId = code;
|
||||
} else {
|
||||
skuId = await db.offers?.getCode(code.split(":")[0], Platform.OS);
|
||||
skuId = await db.offers?.getCode(
|
||||
code.split(":")[0],
|
||||
Platform.OS as "ios" | "android"
|
||||
);
|
||||
}
|
||||
|
||||
const products = await PremiumService.getProducts();
|
||||
|
||||
@@ -150,7 +150,7 @@ const PublishNoteSheet = ({
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{isPublished && (
|
||||
{isPublished && publishUrl ? (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
@@ -204,7 +204,7 @@ const PublishNoteSheet = ({
|
||||
name="content-copy"
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
|
||||
@@ -556,9 +556,9 @@ export default function ReminderSheet({
|
||||
{Object.keys(ReminderNotificationModes).map((mode) => (
|
||||
<Button
|
||||
key={mode}
|
||||
title={strings.reminderNotificationModes[
|
||||
title={strings.reminderNotificationModes(
|
||||
mode as keyof typeof ReminderNotificationModes
|
||||
]()}
|
||||
)}
|
||||
style={{
|
||||
marginRight: 12,
|
||||
borderRadius: 100
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { ActivityIndicator, DimensionValue, ViewStyle } from "react-native";
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
Layout,
|
||||
LightSpeedInLeft
|
||||
} from "react-native-reanimated";
|
||||
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
|
||||
import { SIZE } from "../../../utils/size";
|
||||
import NativeTooltip from "../../../utils/tooltip";
|
||||
import { ButtonProps } from "../button";
|
||||
import { Pressable, useButton } from "../pressable";
|
||||
import Heading from "../typography/heading";
|
||||
import Paragraph from "../typography/paragraph";
|
||||
const AnimatedIcon = Animated.createAnimatedComponent(Icon);
|
||||
|
||||
export const AnimatedButton = ({
|
||||
height = 45,
|
||||
width = null,
|
||||
onPress,
|
||||
loading = false,
|
||||
title = null,
|
||||
icon,
|
||||
fontSize = SIZE.sm,
|
||||
type = "transparent",
|
||||
iconSize = SIZE.md,
|
||||
style = {},
|
||||
accentColor = "accent",
|
||||
accentText = "light",
|
||||
onLongPress,
|
||||
tooltipText,
|
||||
textStyle,
|
||||
iconPosition = "left",
|
||||
buttonType,
|
||||
bold,
|
||||
iconColor,
|
||||
fwdRef,
|
||||
...restProps
|
||||
}: ButtonProps) => {
|
||||
const { text } = useButton({
|
||||
type,
|
||||
accent: accentColor,
|
||||
text: accentText
|
||||
});
|
||||
const textColor = buttonType?.text ? buttonType.text : text;
|
||||
const Component = bold ? Heading : Paragraph;
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
entering={FadeIn}
|
||||
exiting={FadeOut}
|
||||
layout={Layout.springify()}
|
||||
>
|
||||
<Pressable
|
||||
{...restProps}
|
||||
fwdRef={fwdRef}
|
||||
onPress={onPress}
|
||||
onLongPress={(event) => {
|
||||
if (onLongPress) {
|
||||
onLongPress(event);
|
||||
return;
|
||||
}
|
||||
if (tooltipText) {
|
||||
NativeTooltip.show(event, tooltipText, NativeTooltip.POSITIONS.TOP);
|
||||
}
|
||||
}}
|
||||
disabled={loading}
|
||||
type={type}
|
||||
accentColor={accentColor}
|
||||
accentText={accentText}
|
||||
customColor={buttonType?.color}
|
||||
customSelectedColor={buttonType?.selected}
|
||||
customOpacity={buttonType?.opacity}
|
||||
customAlpha={buttonType?.alpha}
|
||||
style={{
|
||||
height: height,
|
||||
width: (width as DimensionValue) || undefined,
|
||||
paddingHorizontal: 12,
|
||||
borderRadius: 5,
|
||||
alignSelf: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
...(style as ViewStyle)
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<ActivityIndicator color={textColor} size={fontSize + 4} />
|
||||
) : null}
|
||||
{icon && !loading && iconPosition === "left" ? (
|
||||
<AnimatedIcon
|
||||
exiting={FadeOut.duration(100)}
|
||||
entering={LightSpeedInLeft}
|
||||
layout={Layout.springify()}
|
||||
name={icon}
|
||||
style={{
|
||||
marginRight: 0
|
||||
}}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
size={iconSize}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!title ? null : (
|
||||
<Component
|
||||
layout={Layout.springify()}
|
||||
animated={true}
|
||||
color={textColor as string}
|
||||
size={fontSize}
|
||||
numberOfLines={1}
|
||||
style={[
|
||||
{
|
||||
marginLeft:
|
||||
icon || (loading && iconPosition === "left") ? 5 : 0,
|
||||
marginRight:
|
||||
icon || (loading && iconPosition === "right") ? 5 : 0
|
||||
},
|
||||
textStyle
|
||||
]}
|
||||
>
|
||||
{title}
|
||||
</Component>
|
||||
)}
|
||||
|
||||
{icon && !loading && iconPosition === "right" ? (
|
||||
<Icon
|
||||
name={icon}
|
||||
style={{
|
||||
marginLeft: 0
|
||||
}}
|
||||
color={iconColor || buttonType?.text || textColor}
|
||||
size={iconSize}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
@@ -18,15 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { View } from "react-native";
|
||||
import { DimensionValue, View } from "react-native";
|
||||
import { SvgXml } from "./lazy";
|
||||
export const SvgView = ({
|
||||
width = 250,
|
||||
height = 250,
|
||||
src
|
||||
}: {
|
||||
width?: number | string;
|
||||
height?: number | number;
|
||||
width?: DimensionValue;
|
||||
height?: DimensionValue;
|
||||
src?: string;
|
||||
}) => {
|
||||
if (!src) return null;
|
||||
|
||||
@@ -497,7 +497,7 @@ export const useAppEvents = () => {
|
||||
}
|
||||
|
||||
if (fullBackup) {
|
||||
await BackupService.run(true, undefined, "full");
|
||||
await BackupService.run(false, undefined, "full");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,18 +32,11 @@ export function useAppState() {
|
||||
const subscription = AppState.addEventListener("change", onChange);
|
||||
|
||||
return () => {
|
||||
// @ts-expect-error - React Native >= 0.65
|
||||
if (typeof subscription?.remove === "function") {
|
||||
// @ts-expect-error - need update @types/react-native@0.65.x
|
||||
subscription.remove();
|
||||
} else {
|
||||
// React Native < 0.65
|
||||
AppState.removeEventListener("change", onChange);
|
||||
}
|
||||
subscription.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return appState;
|
||||
}
|
||||
|
||||
export { AppStateStatus };
|
||||
export type { AppStateStatus };
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Keyboard, KeyboardEventListener, ScreenRect } from "react-native";
|
||||
import { Keyboard, KeyboardEventListener, KeyboardMetrics } from "react-native";
|
||||
|
||||
const emptyCoordinates = Object.freeze({
|
||||
screenX: 0,
|
||||
@@ -34,8 +34,8 @@ const initialValue = {
|
||||
export default function useKeyboard() {
|
||||
const [shown, setShown] = useState(false);
|
||||
const [coordinates, setCoordinates] = useState<{
|
||||
start: undefined | ScreenRect;
|
||||
end: ScreenRect;
|
||||
start: undefined | KeyboardMetrics;
|
||||
end: KeyboardMetrics;
|
||||
}>(initialValue);
|
||||
const [keyboardHeight, setKeyboardHeight] = useState<number>(0);
|
||||
|
||||
|
||||
@@ -388,7 +388,6 @@ export async function attachFile(
|
||||
encryptionInfo.mimeType = type;
|
||||
encryptionInfo.filename = filename;
|
||||
encryptionInfo.alg = "xcha-stream";
|
||||
encryptionInfo.size = encryptionInfo.length;
|
||||
encryptionInfo.key = key;
|
||||
if (options?.reupload && exists) {
|
||||
const attachment = await db.attachments.attachment(hash);
|
||||
@@ -397,6 +396,7 @@ export async function attachFile(
|
||||
} else {
|
||||
encryptionInfo = { hash: hash };
|
||||
}
|
||||
console.log("FILE ENCRYPTED....", encryptionInfo);
|
||||
await db.attachments.add(encryptionInfo);
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
||||
@@ -59,15 +59,38 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
const type =
|
||||
route.params.type === "trash"
|
||||
? "trash"
|
||||
: ((route.params?.type + "s") as keyof typeof db.lookup);
|
||||
console.log(`Searching in ${type} for ${query}`);
|
||||
const results = await db.lookup[type](
|
||||
query,
|
||||
route.params.items as FilteredSelector<Note>
|
||||
).sorted();
|
||||
let results: VirtualizedGrouping<Item> | undefined;
|
||||
|
||||
switch (route.params.type) {
|
||||
case "note":
|
||||
results = await db.lookup
|
||||
.notes(query, route.params.items as FilteredSelector<Note>)
|
||||
.sorted();
|
||||
break;
|
||||
case "notebook":
|
||||
results = await db.lookup.notebooks(query).sorted();
|
||||
break;
|
||||
case "tag":
|
||||
results = await db.lookup.tags(query).sorted();
|
||||
break;
|
||||
case "reminder":
|
||||
results = await db.lookup.reminders(query).sorted();
|
||||
break;
|
||||
case "trash":
|
||||
results = await db.lookup.trash(query).sorted();
|
||||
break;
|
||||
case "attachment":
|
||||
results = await db.lookup.attachments(query).sorted();
|
||||
break;
|
||||
default:
|
||||
results = undefined;
|
||||
}
|
||||
|
||||
if (!results) {
|
||||
setSearchStatus(strings.noResultsFound(query));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Found ${results.placeholders?.length} results for ${query}`
|
||||
|
||||
@@ -75,7 +75,7 @@ export const TrashIntervalPicker = createSettingsPicker({
|
||||
? strings.never()
|
||||
: item === 1
|
||||
? strings.reminderRecurringMode.day()
|
||||
: item + " " + strings.days();
|
||||
: strings.days(item);
|
||||
},
|
||||
getItemKey: (item) => item.toString(),
|
||||
options: [-1, 1, 7, 30, 365],
|
||||
|
||||
@@ -48,7 +48,6 @@ export async function attachFile(uri, hash, type, filename, options) {
|
||||
encryptionInfo.mimeType = type;
|
||||
encryptionInfo.filename = filename;
|
||||
encryptionInfo.alg = "xcha-stream";
|
||||
encryptionInfo.size = encryptionInfo.length;
|
||||
encryptionInfo.key = key;
|
||||
console.log(encryptionInfo);
|
||||
if (options?.reupload && exists) await db.attachments.reset(hash);
|
||||
|
||||
@@ -116,7 +116,7 @@ android {
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
multiDexEnabled true
|
||||
versionCode 3034
|
||||
versionCode 3036
|
||||
versionName getNpmVersion()
|
||||
testBuildType System.getProperty('testBuildType', 'debug')
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/note_widget_info" />
|
||||
android:resource="@xml/new_note_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
/**
|
||||
@@ -24,21 +25,29 @@ public class NoteWidget extends AppWidgetProvider {
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
|
||||
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId, newOptions);
|
||||
}
|
||||
|
||||
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle options) {
|
||||
int minWidth = options != null ? options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) : 0;
|
||||
// int minHeight = options != null ? options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) : 0;
|
||||
|
||||
int layoutId = (minWidth < 100) ? R.layout.note_widget_icon : R.layout.note_widget;
|
||||
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), layoutId);
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId,null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp" android:tint="@color/text"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,16h-3v3h-2v-3L8,16v-2h3v-3h2v3h3v2zM13,9L13,3.5L18.5,9L13,9z"/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#CCFFFFFF"/>
|
||||
<solid android:color="@color/background"/>
|
||||
<stroke android:width="0dp" android:color="#B1BCBE" />
|
||||
<corners android:radius="10dp"/>
|
||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
android:padding="@dimen/widget_margin"
|
||||
android:theme="@style/ThemeOverlay.Notesnook.AppWidgetContainer">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/widget_button"
|
||||
@@ -18,21 +17,21 @@
|
||||
android:elevation="5dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textSize="16sp"
|
||||
android:text="Take a quick note." />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:src="@drawable/add_note"
|
||||
android:contentDescription="New note icon" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textColor="@color/text"
|
||||
android:textSize="16sp"
|
||||
android:text="@string/take_a_quick_note" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,13 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/layout_bg"
|
||||
android:padding="@dimen/widget_margin">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/add_note" />
|
||||
</FrameLayout>
|
||||
@@ -5,4 +5,6 @@
|
||||
<color name="light_blue_600">#FF039BE5</color>
|
||||
<color name="light_blue_900">#FF01579B</color>
|
||||
<color name="bootsplash_background">#1f1f1f</color>
|
||||
<color name="background">#1D1D1D</color>
|
||||
<color name="text">#B4B4B4</color>
|
||||
</resources>
|
||||
@@ -5,4 +5,6 @@
|
||||
<color name="light_blue_600">#FF039BE5</color>
|
||||
<color name="light_blue_900">#FF01579B</color>
|
||||
<color name="bootsplash_background">#FFFFFF</color>
|
||||
<color name="background">#FFFFFF</color>
|
||||
<color name="text">#000000</color>
|
||||
</resources>
|
||||
@@ -3,4 +3,5 @@
|
||||
<string name="title_activity_share">NotesnookShare</string>
|
||||
<string name="appwidget_text">EXAMPLE</string>
|
||||
<string name="add_widget">Add widget</string>
|
||||
<string name="take_a_quick_note">Take a quick note.</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialKeyguardLayout="@layout/note_widget"
|
||||
android:initialLayout="@layout/note_widget"
|
||||
android:minWidth="50dp"
|
||||
android:minHeight="50dp"
|
||||
android:targetCellWidth="5"
|
||||
android:targetCellHeight="1"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:widgetCategory="home_screen"/>
|
||||
@@ -2,9 +2,9 @@
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialKeyguardLayout="@layout/note_widget"
|
||||
android:initialLayout="@layout/note_widget"
|
||||
android:minWidth="300dp"
|
||||
android:minWidth="50dp"
|
||||
android:minHeight="50dp"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:resizeMode="horizontal"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:widgetCategory="home_screen"></appwidget-provider>
|
||||
android:widgetCategory="home_screen"/>
|
||||
@@ -1,5 +1,3 @@
|
||||
- You can now share multiple files to Notesnook
|
||||
- Fix file and image sharing not working
|
||||
- Many other bug fixes and small improvements
|
||||
- Bug fixes and small improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -1063,7 +1063,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
ENABLE_BITCODE = NO;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1137,7 +1137,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -1168,7 +1168,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1242,7 +1242,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1401,7 +1401,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1413,7 +1413,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
@@ -1444,7 +1444,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1457,7 +1457,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -1487,7 +1487,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 53CWBG3QUC;
|
||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
|
||||
@@ -1561,7 +1561,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
@@ -1592,7 +1592,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 2121;
|
||||
CURRENT_PROJECT_VERSION = 2123;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
|
||||
@@ -1667,7 +1667,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 3.0.24;
|
||||
MARKETING_VERSION = 3.0.26;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -1019,7 +1019,7 @@ PODS:
|
||||
- react-native-screenguard (1.0.0):
|
||||
- React-Core
|
||||
- SDWebImage (~> 5.11.1)
|
||||
- react-native-share-extension (2.7.0):
|
||||
- react-native-share-extension (2.8.0):
|
||||
- React
|
||||
- react-native-sodium (1.6.1):
|
||||
- React
|
||||
@@ -1858,7 +1858,7 @@ SPEC CHECKSUMS:
|
||||
react-native-quick-sqlite: 18e1367c34faac90e37f6eb3e78c196e9b674b5d
|
||||
react-native-safe-area-context: b7daa1a8df36095a032dff095a1ea8963cb48371
|
||||
react-native-screenguard: 8b36a3df84c76cd2b82c477f71c26fa1c8cc14a0
|
||||
react-native-share-extension: 17e42444d0d9fbfeb0a7392899def70f9534c9c4
|
||||
react-native-share-extension: 01b25fc15faf2510cc2529e98ca444f64e46238d
|
||||
react-native-sodium: 4cb76086943a7f60c42b40ebca866695b360a196
|
||||
react-native-theme-switch-animation: d3eb50365a3829ce5572628888fa514752703f61
|
||||
react-native-webview: 553abd09f58e340fdc7746c9e2ae096839e99911
|
||||
@@ -1915,8 +1915,8 @@ SPEC CHECKSUMS:
|
||||
SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6
|
||||
TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654
|
||||
toolbar-android: 2a73856e98b750d7e71ce4644d3f41cc98211719
|
||||
Yoga: 2246eea72aaf1b816a68a35e6e4b74563653ae09
|
||||
Yoga: 950bbfd7e6f04790fdb51149ed51df41f329fcc8
|
||||
|
||||
PODFILE CHECKSUM: aa07aca16c1d92394d8ce12f2c5fcaa8a3ae4997
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@@ -88,7 +88,6 @@
|
||||
"@tsconfig/react-native": "^3.0.2",
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-native": "^0.69.1",
|
||||
"@types/react-native-vector-icons": "^6.4.10",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
|
||||
172
apps/mobile/package-lock.json
generated
172
apps/mobile/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.23",
|
||||
"version": "3.0.25",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.23",
|
||||
"version": "3.0.25",
|
||||
"hasInstallScript": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -27,11 +27,13 @@
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/react-query": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@types/validator": "^13.12.2",
|
||||
"diffblazer": "^1.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.39",
|
||||
"fonteditor-core": "^2.1.11",
|
||||
"listr": "^0.14.3",
|
||||
"otplib": "12.0.1",
|
||||
@@ -3709,7 +3711,6 @@
|
||||
"../../packages/crypto": {
|
||||
"name": "@notesnook/crypto",
|
||||
"version": "2.1.3",
|
||||
"dev": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@notesnook/sodium": "file:../sodium"
|
||||
@@ -7739,7 +7740,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/prop-types": {
|
||||
"version": "15.7.11",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/q": {
|
||||
@@ -7759,7 +7760,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/react": {
|
||||
"version": "18.2.39",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
@@ -7790,7 +7791,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/scheduler": {
|
||||
"version": "0.16.8",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/@types/semver": {
|
||||
@@ -12615,7 +12616,7 @@
|
||||
},
|
||||
"../../packages/editor-mobile/node_modules/immer": {
|
||||
"version": "9.0.21",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -23087,6 +23088,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"../../packages/editor/node_modules/jsesc": {
|
||||
@@ -23137,6 +23139,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
@@ -23648,6 +23651,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@@ -23666,6 +23670,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
@@ -23804,6 +23809,7 @@
|
||||
},
|
||||
"../../packages/editor/node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@@ -29007,7 +29013,6 @@
|
||||
"@types/html-to-text": "^8.0.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/metro-config": "^0.76.3",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-native": "^0.69.1",
|
||||
"@types/react-native-vector-icons": "^6.4.10",
|
||||
"@types/react-test-renderer": "^18.0.0",
|
||||
@@ -29207,6 +29212,7 @@
|
||||
},
|
||||
"node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -29326,6 +29332,7 @@
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
@@ -29585,6 +29592,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
|
||||
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
@@ -29599,6 +29607,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz",
|
||||
"integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
|
||||
@@ -29747,6 +29756,7 @@
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
@@ -29759,6 +29769,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==",
|
||||
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
@@ -29793,6 +29804,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-syntax-class-properties": {
|
||||
"version": "7.12.13",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.12.13"
|
||||
@@ -29805,6 +29817,7 @@
|
||||
"version": "7.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
|
||||
"integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.14.5"
|
||||
},
|
||||
@@ -29842,6 +29855,7 @@
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
|
||||
"integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
},
|
||||
@@ -29866,6 +29880,7 @@
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.6.tgz",
|
||||
"integrity": "sha512-aABl0jHw9bZ2karQ/uUD6XP4u0SG22SJrOHFoL6XB1R7dTovOP4TzTlsxOYC5yQ1pdscVK2JTUnF6QL3ARoAiQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29880,6 +29895,7 @@
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz",
|
||||
"integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -29894,6 +29910,7 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
|
||||
"integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.10.4"
|
||||
},
|
||||
@@ -29905,6 +29922,7 @@
|
||||
"version": "7.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
|
||||
"integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.8.0"
|
||||
},
|
||||
@@ -30004,6 +30022,7 @@
|
||||
"version": "7.14.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
|
||||
"integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.14.5"
|
||||
},
|
||||
@@ -30031,6 +30050,7 @@
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
@@ -30059,6 +30079,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
|
||||
"integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -30089,6 +30110,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-block-scoped-functions": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30117,6 +30139,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
|
||||
"integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30132,6 +30155,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz",
|
||||
"integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -30196,6 +30220,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz",
|
||||
"integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30211,6 +30236,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz",
|
||||
"integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30225,6 +30251,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
|
||||
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
@@ -30237,6 +30264,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-exponentiation-operator": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5",
|
||||
@@ -30253,6 +30281,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz",
|
||||
"integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-export-namespace-from": "^7.8.3"
|
||||
@@ -30280,6 +30309,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-for-of": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30310,6 +30340,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz",
|
||||
"integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-json-strings": "^7.8.3"
|
||||
@@ -30338,6 +30369,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz",
|
||||
"integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
|
||||
@@ -30351,6 +30383,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-member-expression-literals": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30366,6 +30399,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz",
|
||||
"integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30396,6 +30430,7 @@
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz",
|
||||
"integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
@@ -30413,6 +30448,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz",
|
||||
"integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30442,6 +30478,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz",
|
||||
"integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30471,6 +30508,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz",
|
||||
"integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-numeric-separator": "^7.10.4"
|
||||
@@ -30486,6 +30524,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz",
|
||||
"integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.5",
|
||||
"@babel/helper-compilation-targets": "^7.22.5",
|
||||
@@ -30502,6 +30541,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-object-super": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -30518,6 +30558,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz",
|
||||
"integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
|
||||
@@ -30591,6 +30632,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-property-literals": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30660,6 +30702,7 @@
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.22.5",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
@@ -30676,6 +30719,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz",
|
||||
"integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30768,6 +30812,7 @@
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz",
|
||||
"integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30798,6 +30843,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz",
|
||||
"integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
},
|
||||
@@ -30812,6 +30858,7 @@
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz",
|
||||
"integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30841,6 +30888,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
|
||||
"integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.22.5",
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
@@ -30856,6 +30904,7 @@
|
||||
"version": "7.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
|
||||
"integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.5",
|
||||
"@babel/helper-compilation-targets": "^7.22.5",
|
||||
@@ -30949,6 +30998,7 @@
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz",
|
||||
"integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
|
||||
@@ -30964,6 +31014,7 @@
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -34245,7 +34296,8 @@
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
|
||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/graceful-fs": {
|
||||
"version": "4.1.6",
|
||||
@@ -34351,16 +34403,17 @@
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.5",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.2.13",
|
||||
"devOptional": true,
|
||||
"version": "18.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
|
||||
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
@@ -34397,11 +34450,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/scheduler": {
|
||||
"version": "0.16.3",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.0",
|
||||
"dev": true,
|
||||
@@ -34411,6 +34459,12 @@
|
||||
"version": "2.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/validator": {
|
||||
"version": "13.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz",
|
||||
"integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/yargs": {
|
||||
"version": "17.0.24",
|
||||
"license": "MIT",
|
||||
@@ -34712,6 +34766,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
|
||||
"integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/helper-numbers": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6"
|
||||
@@ -34720,22 +34775,26 @@
|
||||
"node_modules/@webassemblyjs/floating-point-hex-parser": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
|
||||
"integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-api-error": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
|
||||
"integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-buffer": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
|
||||
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
|
||||
"integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-numbers": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
|
||||
"integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/floating-point-hex-parser": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
@@ -34745,12 +34804,14 @@
|
||||
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
|
||||
"integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/helper-wasm-section": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
|
||||
"integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34762,6 +34823,7 @@
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
|
||||
"integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@xtuc/ieee754": "^1.2.0"
|
||||
}
|
||||
@@ -34770,6 +34832,7 @@
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
|
||||
"integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@xtuc/long": "4.2.2"
|
||||
}
|
||||
@@ -34777,12 +34840,14 @@
|
||||
"node_modules/@webassemblyjs/utf8": {
|
||||
"version": "1.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
|
||||
"integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@webassemblyjs/wasm-edit": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
|
||||
"integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34798,6 +34863,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
|
||||
"integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-wasm-bytecode": "1.11.6",
|
||||
@@ -34810,6 +34876,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
|
||||
"integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-buffer": "1.11.6",
|
||||
@@ -34821,6 +34888,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
|
||||
"integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@webassemblyjs/helper-api-error": "1.11.6",
|
||||
@@ -34834,6 +34902,7 @@
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
|
||||
"integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@webassemblyjs/ast": "1.11.6",
|
||||
"@xtuc/long": "4.2.2"
|
||||
@@ -34891,12 +34960,14 @@
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
|
||||
"integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@xtuc/long": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
|
||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@yarnpkg/lockfile": {
|
||||
"version": "1.1.0",
|
||||
@@ -35886,6 +35957,7 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
|
||||
"integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
@@ -36316,7 +36388,7 @@
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
@@ -36915,6 +36987,7 @@
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
|
||||
"integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
@@ -37034,7 +37107,8 @@
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.0",
|
||||
@@ -37379,6 +37453,7 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esrecurse": "^4.3.0",
|
||||
"estraverse": "^4.1.1"
|
||||
@@ -37391,6 +37466,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
@@ -37554,6 +37630,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
@@ -37565,6 +37642,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
@@ -37573,6 +37651,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -38331,7 +38410,8 @@
|
||||
"node_modules/glob-to-regexp": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/global": {
|
||||
"version": "4.4.0",
|
||||
@@ -40420,7 +40500,8 @@
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-schema-ref-resolver": {
|
||||
"version": "1.0.1",
|
||||
@@ -40896,6 +40977,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.11.5"
|
||||
}
|
||||
@@ -43124,6 +43206,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
@@ -43145,28 +43228,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom/node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-freeze": {
|
||||
"version": "1.0.3",
|
||||
"license": "MIT",
|
||||
@@ -44080,6 +44141,7 @@
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
"version": "0.15.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.4"
|
||||
@@ -44432,6 +44494,7 @@
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.1",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
@@ -45089,6 +45152,7 @@
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -45166,6 +45230,7 @@
|
||||
"version": "5.3.10",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz",
|
||||
"integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jest-worker": "^27.4.5",
|
||||
@@ -45199,6 +45264,7 @@
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
|
||||
"integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"merge-stream": "^2.0.0",
|
||||
@@ -45212,6 +45278,7 @@
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
@@ -45787,6 +45854,7 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.1.2"
|
||||
@@ -45810,6 +45878,7 @@
|
||||
"version": "5.94.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
|
||||
"integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.0",
|
||||
@@ -45925,6 +45994,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
|
||||
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@notesnook/mobile",
|
||||
"version": "3.0.24",
|
||||
"version": "3.0.26",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-or-later",
|
||||
"workspaces": [
|
||||
@@ -24,6 +24,7 @@
|
||||
"release-android-bundle": "cd native/android && ./gradlew bundleRelease --no-daemon"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.39",
|
||||
"fonteditor-core": "^2.1.11",
|
||||
"listr": "^0.14.3",
|
||||
"otplib": "12.0.1",
|
||||
@@ -37,19 +38,20 @@
|
||||
"dependencies": {
|
||||
"@notesnook/common": "file:../../packages/common",
|
||||
"@notesnook/core": "file:../../packages/core",
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
"@notesnook/editor": "file:../../packages/editor",
|
||||
"@notesnook/editor-mobile": "file:../../packages/editor-mobile",
|
||||
"@notesnook/intl": "file:../../packages/intl",
|
||||
"@notesnook/logger": "file:../../packages/logger",
|
||||
"@notesnook/theme": "file:../../packages/theme",
|
||||
"@notesnook/themes-server": "file:../../servers/themes",
|
||||
"@notesnook/crypto": "file:../../packages/crypto",
|
||||
"diffblazer": "^1.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/react-query": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@tanstack/react-query": "^4.36.1"
|
||||
"@types/validator": "^13.12.2",
|
||||
"diffblazer": "^1.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.74.5"
|
||||
}
|
||||
}
|
||||
@@ -496,6 +496,7 @@ export const Search = ({
|
||||
const tagId = await db.tags.add({
|
||||
title: searchKeyword
|
||||
});
|
||||
if (!tagId) return;
|
||||
SearchSetters.selectTags(tagId);
|
||||
onSearch();
|
||||
checkQueryExists(searchKeyword);
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"maxNodeModuleJsDepth": 5,
|
||||
"downlevelIteration": true
|
||||
},
|
||||
"exclude": ["native"]
|
||||
"exclude": ["native", "e2e"]
|
||||
}
|
||||
|
||||
@@ -370,3 +370,57 @@ test("when autosave is disabled, closing the note should save it", async ({
|
||||
await expect(notes.editor.savedIcon).toBeVisible();
|
||||
expect(await notes.editor.getContent("text")).toBe(content.trim());
|
||||
});
|
||||
|
||||
test("control + alt + right arrow should go to next note", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note1 = await notes.createNote({
|
||||
title: "Note 1",
|
||||
content: "Note 1 content"
|
||||
});
|
||||
const note2 = await notes.createNote({
|
||||
title: "Note 2",
|
||||
content: "Note 2 content"
|
||||
});
|
||||
|
||||
await note1?.openNote();
|
||||
await note2?.openNote();
|
||||
await page.keyboard.press("Control+Alt+ArrowRight");
|
||||
|
||||
expect(await notes.editor.getTitle()).toBe("Note 1");
|
||||
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
|
||||
|
||||
await page.keyboard.press("Control+Alt+ArrowRight");
|
||||
|
||||
expect(await notes.editor.getTitle()).toBe("Note 2");
|
||||
expect(await notes.editor.getContent("text")).toBe("Note 2 content");
|
||||
});
|
||||
|
||||
test("control + alt + left arrow should go to previous note", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note1 = await notes.createNote({
|
||||
title: "Note 1",
|
||||
content: "Note 1 content"
|
||||
});
|
||||
const note2 = await notes.createNote({
|
||||
title: "Note 2",
|
||||
content: "Note 2 content"
|
||||
});
|
||||
|
||||
await note1?.openNote();
|
||||
await note2?.openNote();
|
||||
await page.keyboard.press("Control+Alt+ArrowLeft");
|
||||
|
||||
expect(await notes.editor.getTitle()).toBe("Note 1");
|
||||
expect(await notes.editor.getContent("text")).toBe("Note 1 content");
|
||||
|
||||
await page.keyboard.press("Control+Alt+ArrowLeft");
|
||||
|
||||
expect(await notes.editor.getTitle()).toBe("Note 2");
|
||||
expect(await notes.editor.getContent("text")).toBe("Note 2 content");
|
||||
});
|
||||
|
||||
@@ -242,4 +242,23 @@ export class EditorModel {
|
||||
if ((await tabModel.getId()) === id) return tabModel;
|
||||
}
|
||||
}
|
||||
|
||||
async attachImage() {
|
||||
await this.page
|
||||
.context()
|
||||
.grantPermissions(["clipboard-read", "clipboard-write"]);
|
||||
await this.page.evaluate(async () => {
|
||||
const resp = await fetch("https://dummyjson.com/image/150");
|
||||
const blob = await resp.blob();
|
||||
window.navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
"image/png": new Blob([blob], { type: "image/png" })
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
await this.page.keyboard.down("Control");
|
||||
await this.page.keyboard.press("KeyV");
|
||||
await this.page.keyboard.up("Control");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,4 +139,15 @@ export class SettingsViewModel {
|
||||
|
||||
await waitForDialog(this.page, "Restoring backup");
|
||||
}
|
||||
|
||||
async selectImageCompression(option: { value: string; label: string }) {
|
||||
const item = await this.navigation.findItem("Behaviour");
|
||||
await item?.click();
|
||||
|
||||
const imageCompressionDropdown = this.page
|
||||
.locator(getTestId("setting-image-compression"))
|
||||
.locator("select");
|
||||
|
||||
await imageCompressionDropdown.selectOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
79
apps/web/__e2e__/settings.test.ts
Normal file
79
apps/web/__e2e__/settings.test.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { AppModel } from "./models/app.model";
|
||||
import { NOTE } from "./utils";
|
||||
|
||||
test("ask for image compression during image upload when 'Image Compression' setting is 'Ask every time'", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const settings = await app.goToSettings();
|
||||
await settings.selectImageCompression({
|
||||
value: "0",
|
||||
label: "Ask every time"
|
||||
});
|
||||
await settings.close();
|
||||
|
||||
const notes = await app.goToNotes();
|
||||
await notes.createNote(NOTE);
|
||||
await notes.editor.attachImage();
|
||||
|
||||
await expect(page.getByText("Enable compression")).toBeVisible();
|
||||
});
|
||||
|
||||
test("do not ask for image compression during image upload when 'Image Compression' setting is 'Enable (Recommended)'", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const settings = await app.goToSettings();
|
||||
await settings.selectImageCompression({
|
||||
value: "1",
|
||||
label: "Enable (Recommended)"
|
||||
});
|
||||
await settings.close();
|
||||
|
||||
const notes = await app.goToNotes();
|
||||
await notes.createNote(NOTE);
|
||||
await notes.editor.attachImage();
|
||||
|
||||
await expect(page.getByText("Enable compression")).toBeHidden();
|
||||
});
|
||||
|
||||
test("do not ask for image compression during image upload when 'Image Compression' setting is 'Disable'", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const settings = await app.goToSettings();
|
||||
await settings.selectImageCompression({
|
||||
value: "2",
|
||||
label: "Disable"
|
||||
});
|
||||
await settings.close();
|
||||
|
||||
const notes = await app.goToNotes();
|
||||
await notes.createNote(NOTE);
|
||||
await notes.editor.attachImage();
|
||||
|
||||
await expect(page.getByText("Enable compression")).toBeHidden();
|
||||
});
|
||||
88
apps/web/__e2e__/vault.test.ts
Normal file
88
apps/web/__e2e__/vault.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { AppModel } from "./models/app.model";
|
||||
import { getTestId, NOTE, PASSWORD } from "./utils";
|
||||
|
||||
test("locking a note should show vault unlocked status", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
const vaultUnlockedStatus = page.locator(getTestId("vault-unlocked"));
|
||||
|
||||
await note?.contextMenu.lock(PASSWORD);
|
||||
|
||||
await expect(vaultUnlockedStatus).toBeVisible();
|
||||
});
|
||||
|
||||
test("clicking on vault unlocked status should lock the vault", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
const vaultUnlockedStatus = page.locator(getTestId("vault-unlocked"));
|
||||
|
||||
await note?.contextMenu.lock(PASSWORD);
|
||||
await note?.openLockedNote(PASSWORD);
|
||||
await vaultUnlockedStatus.waitFor({ state: "visible" });
|
||||
await vaultUnlockedStatus.click();
|
||||
|
||||
await expect(vaultUnlockedStatus).toBeHidden();
|
||||
expect(await note?.contextMenu.isLocked()).toBe(true);
|
||||
});
|
||||
|
||||
test("opening a locked note should show vault unlocked status", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
const vaultUnlockedStatus = page.locator(getTestId("vault-unlocked"));
|
||||
|
||||
await note?.contextMenu.lock(PASSWORD);
|
||||
await vaultUnlockedStatus.waitFor({ state: "visible" });
|
||||
await vaultUnlockedStatus.click();
|
||||
await vaultUnlockedStatus.waitFor({ state: "hidden" });
|
||||
await note?.openLockedNote(PASSWORD);
|
||||
|
||||
await expect(vaultUnlockedStatus).toBeVisible();
|
||||
});
|
||||
|
||||
test("unlocking a note permanently should not show vault unlocked status", async ({
|
||||
page
|
||||
}) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
const note = await notes.createNote(NOTE);
|
||||
const vaultUnlockedStatus = page.locator(getTestId("vault-unlocked"));
|
||||
|
||||
await note?.contextMenu.lock(PASSWORD);
|
||||
await vaultUnlockedStatus.waitFor({ state: "visible" });
|
||||
await vaultUnlockedStatus.click();
|
||||
await vaultUnlockedStatus.waitFor({ state: "hidden" });
|
||||
await note?.contextMenu.unlock(PASSWORD);
|
||||
|
||||
await expect(vaultUnlockedStatus).toBeHidden();
|
||||
});
|
||||
16545
apps/web/package-lock.json
generated
16545
apps/web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -43,9 +43,9 @@
|
||||
"@theme-ui/core": "^0.16.1",
|
||||
"@trpc/client": "10.45.2",
|
||||
"@trpc/react-query": "10.45.2",
|
||||
"@zip.js/zip.js": "^2.7.32",
|
||||
"@zip.js/zip.js": "^2.7.54",
|
||||
"async-mutex": "^0.4.0",
|
||||
"axios": "^1.3.4",
|
||||
"axios": "^1.7.9",
|
||||
"clipboard-polyfill": "4.0.0",
|
||||
"comlink": "^4.3.1",
|
||||
"cronosjs": "^1.7.1",
|
||||
@@ -55,11 +55,11 @@
|
||||
"event-source-polyfill": "^1.0.25",
|
||||
"fflate": "^0.8.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"hash-wasm": "^4.9.0",
|
||||
"hash-wasm": "^4.12.0",
|
||||
"hotkeys-js": "^3.8.3",
|
||||
"katex": "0.16.2",
|
||||
"mac-scrollbar": "^0.13.5",
|
||||
"mutative": "^1.0.6",
|
||||
"mac-scrollbar": "^0.13.6",
|
||||
"mutative": "^1.1.0",
|
||||
"pdfjs-dist": "3.6.172",
|
||||
"phone": "^3.1.14",
|
||||
"platform": "^1.3.6",
|
||||
@@ -69,12 +69,11 @@
|
||||
"react-dom": "18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-error-boundary": "^4.0.12",
|
||||
"react-freeze": "^1.0.3",
|
||||
"react-freeze": "^1.0.4",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-loading-skeleton": "^3.3.1",
|
||||
"react-modal": "3.16.1",
|
||||
"react-modal": "3.16.3",
|
||||
"react-qrcode-logo": "^2.9.0",
|
||||
"react-resizable-panels": "^2.0.17",
|
||||
"react-scroll-sync": "^0.11.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"snarkdown": "^2.0.0",
|
||||
@@ -82,50 +81,49 @@
|
||||
"w3c-keyname": "^2.2.6",
|
||||
"wouter": "2.12.1",
|
||||
"zustand": "4.4.7",
|
||||
"zustand-mutative": "^1.0.1"
|
||||
"zustand-mutative": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@playwright/test": "^1.48.2",
|
||||
"@swc/core": "^1.5.24",
|
||||
"@swc/plugin-react-remove-properties": "^2.0.4",
|
||||
"@swc/plugin-react-remove-properties": "^6.0.2",
|
||||
"@trpc/server": "10.45.2",
|
||||
"@types/babel__core": "^7.20.1",
|
||||
"@types/event-source-polyfill": "^1.0.1",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/marked": "^4.0.7",
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"@types/platform": "^1.3.4",
|
||||
"@types/event-source-polyfill": "^1.0.5",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/marked": "^6.0.0",
|
||||
"@types/node-fetch": "^2.6.12",
|
||||
"@types/platform": "^1.3.6",
|
||||
"@types/react": "^18.2.39",
|
||||
"@types/react-avatar-editor": "^13.0.2",
|
||||
"@types/react-avatar-editor": "^13.0.3",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@types/react-modal": "3.16.3",
|
||||
"@types/react-scroll-sync": "^0.9.0",
|
||||
"@types/wicg-file-system-access": "^2020.9.6",
|
||||
"@vitejs/plugin-react-swc": "^3.7.0",
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@vitejs/plugin-react-swc": "^3.7.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"better-sqlite3-multiple-ciphers": "^9.4.0",
|
||||
"buffer": "^6.0.3",
|
||||
"chalk": "^4.1.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"esbuild": "^0.24.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"find-process": "^1.4.4",
|
||||
"happy-dom": "^8.9.0",
|
||||
"ip": "^1.1.8",
|
||||
"happy-dom": "^16.0.1",
|
||||
"ip": "^2.0.1",
|
||||
"lorem-ipsum": "^2.0.4",
|
||||
"otplib": "^12.0.1",
|
||||
"rollup": "^4.18.0",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"vite": "^5.2.12",
|
||||
"rollup-plugin-visualizer": "^5.13.1",
|
||||
"vite": "^5.4.11",
|
||||
"vite-plugin-env-compatible": "^2.0.1",
|
||||
"vite-plugin-pwa": "^0.20.0",
|
||||
"vite-plugin-svgr": "^4.2.0",
|
||||
"vitest": "^1.6.0",
|
||||
"workbox-core": "^7.0.0",
|
||||
"workbox-expiration": "^7.0.0",
|
||||
"workbox-precaching": "^7.0.0",
|
||||
"workbox-routing": "^7.0.0",
|
||||
"workbox-strategies": "^7.0.0"
|
||||
"vite-plugin-pwa": "^0.21.1",
|
||||
"vite-plugin-svgr": "^4.3.0",
|
||||
"vitest": "^2.1.8",
|
||||
"workbox-core": "^7.3.0",
|
||||
"workbox-expiration": "^7.3.0",
|
||||
"workbox-precaching": "^7.3.0",
|
||||
"workbox-routing": "^7.3.0",
|
||||
"workbox-strategies": "^7.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env PLATFORM=web vite",
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useStore } from "./stores/app-store";
|
||||
import useSlider from "./hooks/use-slider";
|
||||
import useMobile from "./hooks/use-mobile";
|
||||
import useTablet from "./hooks/use-tablet";
|
||||
|
||||
type MobileAppEffectsProps = {
|
||||
sliderId: string;
|
||||
overlayId: string;
|
||||
setShow: (show: boolean) => void;
|
||||
};
|
||||
export default function MobileAppEffects({
|
||||
sliderId,
|
||||
overlayId,
|
||||
setShow
|
||||
}: MobileAppEffectsProps) {
|
||||
const isMobile = useMobile();
|
||||
const isTablet = useTablet();
|
||||
const toggleSideMenu = useStore((store) => store.toggleSideMenu);
|
||||
const setIsEditorOpen = useStore((store) => store.setIsEditorOpen);
|
||||
const isEditorOpen = useStore((store) => store.isEditorOpen);
|
||||
const isSideMenuOpen = useStore((store) => store.isSideMenuOpen);
|
||||
const isFocusMode = useStore((store) => store.isFocusMode);
|
||||
|
||||
const [slideToIndex] = useSlider(sliderId, {
|
||||
onSliding: (_e, { position }) => {
|
||||
if (!isMobile) return;
|
||||
const offset = 70;
|
||||
const width = 300;
|
||||
|
||||
const percent = offset - (position / width) * offset;
|
||||
const overlay = document.getElementById("overlay");
|
||||
if (!overlay) return;
|
||||
if (percent > 0) {
|
||||
overlay.style.opacity = `${percent}%`;
|
||||
overlay.style.pointerEvents = "all";
|
||||
} else {
|
||||
overlay.style.opacity = "0%";
|
||||
overlay.style.pointerEvents = "none";
|
||||
}
|
||||
},
|
||||
onChange: (e, { slide }) => {
|
||||
toggleSideMenu(slide?.index === 0 ? true : false);
|
||||
setIsEditorOpen(slide?.index === 3 ? true : false);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
slideToIndex(isSideMenuOpen ? 1 : 2);
|
||||
}, [isMobile, slideToIndex, isSideMenuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
slideToIndex(isEditorOpen ? 3 : 2);
|
||||
}, [isMobile, slideToIndex, isEditorOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
toggleSideMenu(!isMobile);
|
||||
if (!isMobile && !isTablet && !isFocusMode) setShow(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMobile, isTablet, isFocusMode, toggleSideMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!overlayId) return;
|
||||
const overlay = document.getElementById(overlayId);
|
||||
if (!overlay) return;
|
||||
overlay.onclick = () => toggleSideMenu(false);
|
||||
return () => {
|
||||
overlay.onclick = null;
|
||||
};
|
||||
}, [overlayId, toggleSideMenu]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -5,40 +5,19 @@
|
||||
|
||||
.tabsScroll,
|
||||
.titlebarLogo,
|
||||
.theme-scope-titleBar {
|
||||
.theme-scope-titleBar,
|
||||
.route-container-header {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.theme-scope-titleBar button,
|
||||
.tabsScroll .tab {
|
||||
.tabsScroll .tab,
|
||||
.editor-action-bar button,
|
||||
.route-container-header button,
|
||||
.search-container {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.panel-resize-handle {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.panel-resize-handle::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
transition: background-color 300ms ease-out;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.panel-resize-handle[data-panel-group-direction="horizontal"]::before {
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.panel-resize-handle[data-panel-group-direction="vertical"]::before {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.panel-resize-handle[data-resize-handle-state="hover"]::before {
|
||||
background-color: var(--accent);
|
||||
}
|
||||
|
||||
/* open-sans-regular - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
|
||||
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, Suspense, useRef, useEffect } from "react";
|
||||
import { useState, Suspense, useEffect, useRef } from "react";
|
||||
import { Box, Flex } from "@theme-ui/components";
|
||||
import { ScopedThemeProvider } from "./components/theme-provider";
|
||||
import useMobile from "./hooks/use-mobile";
|
||||
@@ -30,38 +30,84 @@ import { FlexScrollContainer } from "./components/scroll-container";
|
||||
import CachedRouter from "./components/cached-router";
|
||||
import { WebExtensionRelay } from "./utils/web-extension-relay";
|
||||
import {
|
||||
PanelGroup,
|
||||
Panel,
|
||||
PanelResizeHandle,
|
||||
ImperativePanelHandle
|
||||
} from "react-resizable-panels";
|
||||
Pane,
|
||||
SplitPane,
|
||||
SplitPaneImperativeHandle
|
||||
} from "./components/split-pane";
|
||||
import GlobalMenuWrapper from "./components/global-menu-wrapper";
|
||||
import AppEffects from "./app-effects";
|
||||
import HashRouter from "./components/hash-router";
|
||||
import { useWindowFocus } from "./hooks/use-window-focus";
|
||||
import { Global } from "@emotion/react";
|
||||
import { isMac } from "./utils/platform";
|
||||
import useSlider from "./hooks/use-slider";
|
||||
import { AppEventManager, AppEvents } from "./common/app-events";
|
||||
import { TITLE_BAR_HEIGHT } from "./components/title-bar";
|
||||
import { getFontSizes } from "@notesnook/theme/theme/font/fontsize.js";
|
||||
import { useWindowControls } from "./hooks/use-window-controls";
|
||||
|
||||
new WebExtensionRelay();
|
||||
|
||||
const MobileAppEffects = React.lazy(() => import("./app-effects.mobile"));
|
||||
|
||||
function App() {
|
||||
const isMobile = useMobile();
|
||||
const [show, setShow] = useState(true);
|
||||
const isFocusMode = useStore((store) => store.isFocusMode);
|
||||
const { isFocused } = useWindowFocus();
|
||||
const { isFullscreen } = useWindowControls();
|
||||
console.timeEnd("loading app");
|
||||
|
||||
return (
|
||||
<>
|
||||
{isFocused ? null : (
|
||||
<Global
|
||||
styles={`
|
||||
body {
|
||||
filter: brightness(0.95) grayscale(1);
|
||||
}
|
||||
.titlebar {
|
||||
background: var(--background-secondary) !important;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
{IS_DESKTOP_APP && isMac() && !isFullscreen ? (
|
||||
<Global
|
||||
// These styles to make sure the app content doesn't overlap with the traffic lights.
|
||||
styles={`
|
||||
.nav-pane,
|
||||
.mobile-nav-pane {
|
||||
margin-top: env(titlebar-area-height) !important;
|
||||
}
|
||||
.nav-pane.collapsed + .list-pane .route-container-header,
|
||||
.nav-pane.collapsed + .list-pane.collapsed + .editor-pane .editor-action-bar,
|
||||
.nav-pane.collapsed + .editor-pane .editor-action-bar {
|
||||
padding-left: 25px;
|
||||
}
|
||||
.editor-pane:first-of-type .editor-action-bar,
|
||||
.mobile-editor-pane.pane-active .editor-action-bar,
|
||||
.mobile-list-pane.pane-active .route-container-header {
|
||||
padding-left: 80px;
|
||||
}
|
||||
.route-container-header, .editor-action-bar {
|
||||
transition: padding-left 0.4s ease-out;
|
||||
}
|
||||
.editor-action-bar {
|
||||
border-bottom: none;
|
||||
}
|
||||
.route-container-header .routeHeader {
|
||||
font-size: ${getFontSizes().title};
|
||||
}
|
||||
.global-split-pane .react-split__sash {
|
||||
height: calc(100% - ${TITLE_BAR_HEIGHT}px);
|
||||
}
|
||||
`}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<Suspense fallback={<div style={{ display: "none" }} />}>
|
||||
<div id="menu-wrapper">
|
||||
<GlobalMenuWrapper />
|
||||
</div>
|
||||
{isMobile && (
|
||||
<MobileAppEffects
|
||||
sliderId="slider"
|
||||
overlayId="overlay"
|
||||
setShow={setShow}
|
||||
/>
|
||||
)}
|
||||
</Suspense>
|
||||
<AppEffects setShow={setShow} />
|
||||
|
||||
@@ -69,7 +115,11 @@ function App() {
|
||||
id="app"
|
||||
bg="background"
|
||||
className={isFocusMode ? "app-focus-mode" : ""}
|
||||
sx={{ overflow: "hidden", flexDirection: "column", height: "100%" }}
|
||||
sx={{
|
||||
overflow: "hidden",
|
||||
flexDirection: "column",
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
{isMobile ? (
|
||||
<MobileAppContents />
|
||||
@@ -92,13 +142,11 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
|
||||
const isFocusMode = useStore((store) => store.isFocusMode);
|
||||
const isTablet = useTablet();
|
||||
const [isNarrow, setIsNarrow] = useState(isTablet || false);
|
||||
const navPane = useRef<ImperativePanelHandle>(null);
|
||||
const middlePane = useRef<ImperativePanelHandle>(null);
|
||||
const navPane = useRef<SplitPaneImperativeHandle>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const size = navPane.current?.getSize();
|
||||
// Toggle `isNarrow` to true if panel size isn't set to narrow by user
|
||||
setIsNarrow((size && size <= 5) || isTablet);
|
||||
if (isTablet) navPane.current?.collapse(0);
|
||||
else navPane.current?.expand(0);
|
||||
}, [isTablet]);
|
||||
|
||||
// useEffect(() => {
|
||||
@@ -123,81 +171,71 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<PanelGroup autoSaveId="global-panel-group" direction="horizontal">
|
||||
{!isFocusMode && isTablet ? (
|
||||
<Flex sx={{ width: 50 }}>
|
||||
<SplitPane
|
||||
className="global-split-pane"
|
||||
ref={navPane}
|
||||
autoSaveId="global-panel-group"
|
||||
direction="vertical"
|
||||
onChange={(sizes) => {
|
||||
setIsNarrow(sizes[0] <= 70);
|
||||
}}
|
||||
>
|
||||
{isFocusMode ? null : (
|
||||
<Pane
|
||||
id="nav-pane"
|
||||
initialSize={180}
|
||||
className={`nav-pane`}
|
||||
minSize={50}
|
||||
snapSize={120}
|
||||
maxSize={300}
|
||||
>
|
||||
<NavigationMenu
|
||||
toggleNavigationContainer={(state) => {
|
||||
setShow(state || !show);
|
||||
}}
|
||||
isTablet={isNarrow}
|
||||
/>
|
||||
</Flex>
|
||||
) : (
|
||||
!isFocusMode && (
|
||||
<>
|
||||
<Panel
|
||||
ref={navPane}
|
||||
order={1}
|
||||
className="nav-pane"
|
||||
defaultSize={10}
|
||||
minSize={3.5}
|
||||
// maxSize={isNarrow ? 5 : undefined}
|
||||
onResize={(size) => setIsNarrow(size <= 5)}
|
||||
collapsible
|
||||
collapsedSize={3.5}
|
||||
>
|
||||
<NavigationMenu
|
||||
toggleNavigationContainer={(state) => {
|
||||
setShow(state || !show);
|
||||
}}
|
||||
isTablet={isNarrow}
|
||||
/>
|
||||
</Panel>
|
||||
<PanelResizeHandle className="panel-resize-handle" />
|
||||
</>
|
||||
)
|
||||
</Pane>
|
||||
)}
|
||||
{!isFocusMode && show && (
|
||||
<>
|
||||
<Panel
|
||||
ref={middlePane}
|
||||
className="middle-pane"
|
||||
order={2}
|
||||
collapsible
|
||||
defaultSize={20}
|
||||
>
|
||||
<ScopedThemeProvider
|
||||
className="listMenu"
|
||||
scope="list"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: 1,
|
||||
bg: "background",
|
||||
borderRight: "1px solid var(--separator)"
|
||||
}}
|
||||
>
|
||||
<CachedRouter />
|
||||
</ScopedThemeProvider>
|
||||
</Panel>
|
||||
<PanelResizeHandle className="panel-resize-handle" />
|
||||
</>
|
||||
)}
|
||||
<Panel className="editor-pane" order={3} defaultSize={70}>
|
||||
<Flex
|
||||
sx={{
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
flex: 1,
|
||||
flexDirection: "column",
|
||||
bg: "background"
|
||||
}}
|
||||
{!isFocusMode && show ? (
|
||||
<Pane
|
||||
id="list-pane"
|
||||
initialSize={380}
|
||||
style={{ flex: 1, display: "flex" }}
|
||||
snapSize={200}
|
||||
maxSize={500}
|
||||
className="list-pane"
|
||||
>
|
||||
<HashRouter />
|
||||
</Flex>
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
<ScopedThemeProvider
|
||||
className="listMenu"
|
||||
scope="list"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
flex: 1,
|
||||
bg: "background",
|
||||
borderRight: "1px solid var(--separator)"
|
||||
}}
|
||||
>
|
||||
<CachedRouter />
|
||||
</ScopedThemeProvider>
|
||||
</Pane>
|
||||
) : null}
|
||||
|
||||
<Pane
|
||||
id="editor-pane"
|
||||
className="editor-pane"
|
||||
style={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
backgroundColor: "var(--background)",
|
||||
overflow: "hidden",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
{<HashRouter />}
|
||||
</Pane>
|
||||
</SplitPane>
|
||||
</Flex>
|
||||
<StatusBar />
|
||||
</>
|
||||
@@ -205,8 +243,46 @@ function DesktopAppContents({ show, setShow }: DesktopAppContentsProps) {
|
||||
}
|
||||
|
||||
function MobileAppContents() {
|
||||
const { ref, slideToIndex } = useSlider({
|
||||
onSliding: (_e, { position }) => {
|
||||
const offset = 70;
|
||||
const width = 300;
|
||||
|
||||
const percent = offset - (position / width) * offset;
|
||||
const overlay = document.getElementById("overlay");
|
||||
if (!overlay) return;
|
||||
if (percent > 0) {
|
||||
overlay.style.opacity = `${percent}%`;
|
||||
overlay.style.pointerEvents = "all";
|
||||
} else {
|
||||
overlay.style.opacity = "0%";
|
||||
overlay.style.pointerEvents = "none";
|
||||
}
|
||||
},
|
||||
onChange: (e, { slide, lastSlide }) => {
|
||||
slide.node.classList.add("pane-active");
|
||||
lastSlide?.node.classList.remove("pane-active");
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const toggleSideMenuEvent = AppEventManager.subscribe(
|
||||
AppEvents.toggleSideMenu,
|
||||
(state) => slideToIndex(state ? 0 : 1)
|
||||
);
|
||||
const toggleEditorEvent = AppEventManager.subscribe(
|
||||
AppEvents.toggleEditor,
|
||||
(state) => slideToIndex(state ? 2 : 1)
|
||||
);
|
||||
return () => {
|
||||
toggleSideMenuEvent.unsubscribe();
|
||||
toggleEditorEvent.unsubscribe();
|
||||
};
|
||||
}, [slideToIndex]);
|
||||
|
||||
return (
|
||||
<FlexScrollContainer
|
||||
scrollRef={ref}
|
||||
id="slider"
|
||||
suppressScrollX
|
||||
style={{
|
||||
@@ -223,17 +299,18 @@ function MobileAppContents() {
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
className="mobile-nav-pane"
|
||||
sx={{
|
||||
scrollSnapAlign: "start",
|
||||
scrollSnapStop: "always",
|
||||
width: [300, 60],
|
||||
width: 300,
|
||||
flexShrink: 0
|
||||
}}
|
||||
>
|
||||
<NavigationMenu toggleNavigationContainer={() => {}} isTablet={false} />
|
||||
<NavigationMenu toggleNavigationContainer={() => { }} isTablet={false} />
|
||||
</Flex>
|
||||
<Flex
|
||||
className="listMenu"
|
||||
className="mobile-list-pane"
|
||||
variant="columnFill"
|
||||
sx={{
|
||||
position: "relative",
|
||||
@@ -246,6 +323,7 @@ function MobileAppContents() {
|
||||
<CachedRouter />
|
||||
<Box
|
||||
id="overlay"
|
||||
onClick={() => slideToIndex(1)}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
width: "100%",
|
||||
@@ -261,6 +339,7 @@ function MobileAppContents() {
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
className="mobile-editor-pane"
|
||||
sx={{
|
||||
scrollSnapAlign: "start",
|
||||
scrollSnapStop: "always",
|
||||
|
||||
@@ -38,5 +38,8 @@ export const AppEvents = {
|
||||
|
||||
changeNoteTitle: "changeNoteTitle",
|
||||
|
||||
revealItemInList: "list:revealItem"
|
||||
revealItemInList: "list:revealItem",
|
||||
|
||||
toggleSideMenu: "app:openSideMenu",
|
||||
toggleEditor: "app:toggleEditor"
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Cross,
|
||||
@@ -79,11 +79,15 @@ import { showPublishView } from "../publish-view";
|
||||
import { restrictToHorizontalAxis } from "@dnd-kit/modifiers";
|
||||
import useMobile from "../../hooks/use-mobile";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { TITLE_BAR_HEIGHT, getWindowControls } from "../title-bar";
|
||||
import useTablet from "../../hooks/use-tablet";
|
||||
import { isMac } from "../../utils/platform";
|
||||
|
||||
export function EditorActionBar() {
|
||||
const { isMaximized, isFullscreen, hasNativeWindowControls } =
|
||||
useWindowControls();
|
||||
const editorMargins = useEditorStore((store) => store.editorMargins);
|
||||
const isFocusMode = useAppStore((store) => store.isFocusMode);
|
||||
const { isFullscreen } = useWindowControls();
|
||||
const activeSession = useEditorStore((store) =>
|
||||
store.activeSessionId ? store.getSession(store.activeSessionId) : undefined
|
||||
);
|
||||
@@ -95,7 +99,7 @@ export function EditorActionBar() {
|
||||
const isNotePublished =
|
||||
activeSession && db.monographs.isPublished(activeSession.id);
|
||||
const isMobile = useMobile();
|
||||
const setIsEditorOpen = useAppStore((store) => store.setIsEditorOpen);
|
||||
const isTablet = useTablet();
|
||||
|
||||
const tools = [
|
||||
{
|
||||
@@ -195,7 +199,14 @@ export function EditorActionBar() {
|
||||
activeSession.type !== "conflicted" &&
|
||||
!isFocusMode,
|
||||
onClick: () => useEditorStore.getState().toggleProperties()
|
||||
}
|
||||
},
|
||||
...getWindowControls(
|
||||
hasNativeWindowControls,
|
||||
isFullscreen,
|
||||
isMaximized,
|
||||
isTablet,
|
||||
isMobile
|
||||
)
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -210,7 +221,9 @@ export function EditorActionBar() {
|
||||
borderRadius: 0,
|
||||
flexShrink: 0
|
||||
}}
|
||||
onClick={() => setIsEditorOpen(false)}
|
||||
onClick={() =>
|
||||
AppEventManager.publish(AppEvents.toggleEditor, false)
|
||||
}
|
||||
>
|
||||
<ArrowLeft size={18} />
|
||||
</Button>
|
||||
@@ -218,35 +231,46 @@ export function EditorActionBar() {
|
||||
) : (
|
||||
<TabStrip />
|
||||
)}
|
||||
{tools.map((tool) => (
|
||||
<Button
|
||||
data-test-id={tool.title}
|
||||
disabled={!tool.enabled}
|
||||
variant={tool.title === "Close" ? "error" : "secondary"}
|
||||
title={tool.title}
|
||||
key={tool.title}
|
||||
sx={{
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
bg: "transparent",
|
||||
display: [
|
||||
tool.hideOnMobile ? "none" : "flex",
|
||||
tool.hidden ? "none" : "flex"
|
||||
],
|
||||
borderRadius: 0,
|
||||
flexShrink: 0,
|
||||
"&:hover svg path": {
|
||||
fill:
|
||||
tool.title === "Close"
|
||||
? "var(--accentForeground-error) !important"
|
||||
: "var(--icon)"
|
||||
}
|
||||
}}
|
||||
onClick={tool.onClick}
|
||||
>
|
||||
<tool.icon size={18} />
|
||||
</Button>
|
||||
))}
|
||||
<Flex
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
mr:
|
||||
hasNativeWindowControls && !isMac() && !isMobile && !isTablet
|
||||
? `calc(100vw - env(titlebar-area-width))`
|
||||
: 0
|
||||
}}
|
||||
>
|
||||
{tools.map((tool) => (
|
||||
<Button
|
||||
data-test-id={tool.title}
|
||||
disabled={!tool.enabled}
|
||||
variant={tool.title === "Close" ? "error" : "secondary"}
|
||||
title={tool.title}
|
||||
key={tool.title}
|
||||
sx={{
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
bg: "transparent",
|
||||
display: [
|
||||
"hideOnMobile" in tool && tool.hideOnMobile ? "none" : "flex",
|
||||
tool.hidden ? "none" : "flex"
|
||||
],
|
||||
borderRadius: 0,
|
||||
flexShrink: 0,
|
||||
"&:hover svg path": {
|
||||
fill:
|
||||
tool.title === "Close"
|
||||
? "var(--accentForeground-error) !important"
|
||||
: "var(--icon)"
|
||||
}
|
||||
}}
|
||||
onClick={tool.onClick}
|
||||
>
|
||||
<tool.icon size={18} />
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -259,7 +283,7 @@ function TabStrip() {
|
||||
<ScrollContainer
|
||||
className="tabsScroll"
|
||||
suppressScrollY
|
||||
style={{ flex: 1 }}
|
||||
style={{ flex: 1, height: TITLE_BAR_HEIGHT }}
|
||||
trackStyle={() => ({
|
||||
backgroundColor: "transparent",
|
||||
"--ms-track-size": "6px"
|
||||
@@ -275,9 +299,7 @@ function TabStrip() {
|
||||
<Flex
|
||||
sx={{
|
||||
flex: 1,
|
||||
my: "2.5px",
|
||||
gap: 1,
|
||||
height: 32
|
||||
height: "100%"
|
||||
}}
|
||||
onDoubleClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
@@ -452,28 +474,38 @@ function Tab(props: TabProps) {
|
||||
: Note;
|
||||
const { attributes, listeners, setNodeRef, transform, transition, active } =
|
||||
useSortable({ id });
|
||||
const activeTabRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTabRef.current && isActive) {
|
||||
const tab = activeTabRef.current;
|
||||
tab.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "nearest"
|
||||
});
|
||||
}
|
||||
}, [isActive]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
ref={setNodeRef}
|
||||
ref={(el) => {
|
||||
setNodeRef(el);
|
||||
activeTabRef.current = el;
|
||||
}}
|
||||
className="tab"
|
||||
data-test-id={`tab-${id}`}
|
||||
sx={{
|
||||
borderRadius: "default",
|
||||
height: "100%",
|
||||
cursor: "pointer",
|
||||
px: 2,
|
||||
py: "7px",
|
||||
borderRight: "1px solid var(--border)",
|
||||
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
visibility: active?.id === id ? "hidden" : "visible",
|
||||
|
||||
bg: isActive ? "background-selected" : "background-secondary",
|
||||
// borderTopLeftRadius: "default",
|
||||
// borderTopRightRadius: "default",
|
||||
// borderBottom: isActive ? "none" : "1px solid var(--border)",
|
||||
border: "1px solid",
|
||||
borderColor: isActive ? "border-selected" : "transparent",
|
||||
bg: isActive ? "background-selected" : "transparent",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexShrink: 0,
|
||||
|
||||
@@ -68,14 +68,15 @@ import { scrollIntoViewById } from "@notesnook/editor";
|
||||
import { IEditor } from "./types";
|
||||
import { EditorActionBar } from "./action-bar";
|
||||
import { logger } from "../../utils/logger";
|
||||
import { PanelGroup, Panel, PanelResizeHandle } from "react-resizable-panels";
|
||||
import { NoteLinkingDialog } from "../../dialogs/note-linking-dialog";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { onPageVisibilityChanged } from "../../utils/page-visibility";
|
||||
import { Pane, SplitPane } from "../split-pane";
|
||||
import { TITLE_BAR_HEIGHT } from "../title-bar";
|
||||
|
||||
const PDFPreview = React.lazy(() => import("../pdf-preview"));
|
||||
|
||||
const autoSaveToast = { show: true, hide: () => {} };
|
||||
const autoSaveToast = { show: true, hide: () => { } };
|
||||
|
||||
async function saveContent(
|
||||
noteId: string,
|
||||
@@ -124,30 +125,57 @@ export default function TabsView() {
|
||||
const isTOCVisible = useEditorStore((store) => store.isTOCVisible);
|
||||
const [dropRef, overlayRef] = useDragOverlay();
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.altKey &&
|
||||
event.key === "ArrowRight"
|
||||
) {
|
||||
event.preventDefault();
|
||||
useEditorStore.getState().openNextSession();
|
||||
}
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.altKey &&
|
||||
event.key === "ArrowLeft"
|
||||
) {
|
||||
event.preventDefault();
|
||||
useEditorStore.getState().openPreviousSession();
|
||||
}
|
||||
};
|
||||
document.body.addEventListener("keydown", onKeyDown);
|
||||
return () => {
|
||||
document.body.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!hasNativeTitlebar ? (
|
||||
<EditorActionBarPortal />
|
||||
) : (
|
||||
<Flex sx={{ px: 1 }}>
|
||||
<EditorActionBar />
|
||||
</Flex>
|
||||
)}
|
||||
<Flex
|
||||
className="editor-action-bar"
|
||||
sx={{
|
||||
zIndex: 2,
|
||||
height: TITLE_BAR_HEIGHT,
|
||||
borderBottom: "1px solid var(--border)"
|
||||
}}
|
||||
>
|
||||
<EditorActionBar />
|
||||
</Flex>
|
||||
|
||||
<ScopedThemeProvider
|
||||
scope="editor"
|
||||
ref={dropRef}
|
||||
sx={{
|
||||
bg: "background",
|
||||
pt: 1,
|
||||
flex: 1,
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
<PanelGroup direction="horizontal" autoSaveId={"editor-panels"}>
|
||||
<Panel id="editor-panel" className="editor-pane" order={1}>
|
||||
<SplitPane direction="vertical" autoSaveId={"editor-panels"}>
|
||||
<Pane id="editor-panel" className="editor-pane">
|
||||
{sessions.map((session) => (
|
||||
<Freeze key={session.id} freeze={session.id !== activeSessionId}>
|
||||
{session.type === "locked" ? (
|
||||
@@ -159,64 +187,51 @@ export default function TabsView() {
|
||||
)}
|
||||
</Freeze>
|
||||
))}
|
||||
</Panel>
|
||||
</Pane>
|
||||
|
||||
{documentPreview && (
|
||||
<>
|
||||
<PanelResizeHandle className="panel-resize-handle" />
|
||||
<Panel
|
||||
id="pdf-preview-panel"
|
||||
order={2}
|
||||
minSize={35}
|
||||
defaultSize={35}
|
||||
{documentPreview ? (
|
||||
<Pane id="pdf-preview-panel" initialSize={435} minSize={435}>
|
||||
<ScopedThemeProvider
|
||||
scope="editorSidebar"
|
||||
id="editorSidebar"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderLeft: "1px solid var(--border)",
|
||||
height: "100%",
|
||||
bg: "background"
|
||||
}}
|
||||
>
|
||||
<ScopedThemeProvider
|
||||
scope="editorSidebar"
|
||||
id="editorSidebar"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderLeft: "1px solid var(--border)",
|
||||
height: "100%",
|
||||
bg: "background"
|
||||
}}
|
||||
>
|
||||
{documentPreview.url ? (
|
||||
<Suspense
|
||||
fallback={
|
||||
<DownloadAttachmentProgress
|
||||
hash={documentPreview.hash}
|
||||
/>
|
||||
{documentPreview.url ? (
|
||||
<Suspense
|
||||
fallback={
|
||||
<DownloadAttachmentProgress hash={documentPreview.hash} />
|
||||
}
|
||||
>
|
||||
<PDFPreview
|
||||
fileUrl={documentPreview.url}
|
||||
hash={documentPreview.hash}
|
||||
onClose={() =>
|
||||
useEditorStore.setState({
|
||||
documentPreview: undefined
|
||||
})
|
||||
}
|
||||
>
|
||||
<PDFPreview
|
||||
fileUrl={documentPreview.url}
|
||||
hash={documentPreview.hash}
|
||||
onClose={() =>
|
||||
useEditorStore.setState({
|
||||
documentPreview: undefined
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<DownloadAttachmentProgress hash={documentPreview.hash} />
|
||||
)}
|
||||
</ScopedThemeProvider>
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<DownloadAttachmentProgress hash={documentPreview.hash} />
|
||||
)}
|
||||
</ScopedThemeProvider>
|
||||
</Pane>
|
||||
) : null}
|
||||
|
||||
{isTOCVisible && activeSessionId && (
|
||||
<>
|
||||
<PanelResizeHandle className="panel-resize-handle" />
|
||||
<Panel id="toc-panel" order={3} defaultSize={25} minSize={15}>
|
||||
<TableOfContents sessionId={activeSessionId} />
|
||||
</Panel>
|
||||
</>
|
||||
)}
|
||||
</PanelGroup>
|
||||
{isTOCVisible && activeSessionId ? (
|
||||
<Pane id="table-of-contents-pane" initialSize={300} minSize={300}>
|
||||
<TableOfContents sessionId={activeSessionId} />
|
||||
</Pane>
|
||||
) : null}
|
||||
</SplitPane>
|
||||
<DropZone overlayRef={overlayRef} />
|
||||
{arePropertiesVisible && activeSessionId && (
|
||||
<Properties sessionId={activeSessionId} />
|
||||
@@ -237,10 +252,10 @@ function EditorView({
|
||||
session
|
||||
}: {
|
||||
session:
|
||||
| DefaultEditorSession
|
||||
| NewEditorSession
|
||||
| ReadonlyEditorSession
|
||||
| DeletedEditorSession;
|
||||
| DefaultEditorSession
|
||||
| NewEditorSession
|
||||
| ReadonlyEditorSession
|
||||
| DeletedEditorSession;
|
||||
}) {
|
||||
const lastChangedTime = useRef<number>(0);
|
||||
const root = useRef<HTMLDivElement>(null);
|
||||
@@ -891,9 +906,3 @@ function UnlockNoteView(props: UnlockNoteViewProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function EditorActionBarPortal() {
|
||||
const container = document.getElementById("titlebar-portal-container");
|
||||
if (!container) return null;
|
||||
return ReactDOM.createPortal(<EditorActionBar />, container);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ import {
|
||||
hashStream,
|
||||
writeEncryptedFile
|
||||
} from "../../interfaces/fs";
|
||||
import Config from "../../utils/config";
|
||||
import { compressImage, FileWithURI } from "../../utils/image-compressor";
|
||||
import { ImageCompressionOptions } from "../../stores/setting-store";
|
||||
|
||||
const FILE_SIZE_LIMIT = 500 * 1024 * 1024;
|
||||
const IMAGE_SIZE_LIMIT = 50 * 1024 * 1024;
|
||||
@@ -58,12 +61,43 @@ export async function attachFiles(files: File[]) {
|
||||
}
|
||||
|
||||
let images = files.filter((f) => f.type.startsWith("image/"));
|
||||
images =
|
||||
images.length > 0
|
||||
? (await ImagePickerDialog.show({
|
||||
images
|
||||
})) || []
|
||||
: [];
|
||||
const imageCompressionConfig = Config.get<ImageCompressionOptions>(
|
||||
"imageCompression",
|
||||
ImageCompressionOptions.ASK_EVERY_TIME
|
||||
);
|
||||
|
||||
switch (imageCompressionConfig) {
|
||||
case ImageCompressionOptions.ENABLE: {
|
||||
let compressedImages: FileWithURI[] = [];
|
||||
for (const image of images) {
|
||||
const compressed = await compressImage(image, {
|
||||
maxWidth: (naturalWidth) => Math.min(1920, naturalWidth * 0.7),
|
||||
width: (naturalWidth) => naturalWidth,
|
||||
height: (_, naturalHeight) => naturalHeight,
|
||||
resize: "contain",
|
||||
quality: 0.7
|
||||
});
|
||||
compressedImages.push(
|
||||
new FileWithURI([compressed], image.name, {
|
||||
lastModified: image.lastModified,
|
||||
type: image.type
|
||||
})
|
||||
);
|
||||
}
|
||||
images = compressedImages;
|
||||
break;
|
||||
}
|
||||
case ImageCompressionOptions.DISABLE:
|
||||
break;
|
||||
default:
|
||||
images =
|
||||
images.length > 0
|
||||
? (await ImagePickerDialog.show({
|
||||
images
|
||||
})) || []
|
||||
: [];
|
||||
}
|
||||
|
||||
const documents = files.filter((f) => !f.type.startsWith("image/"));
|
||||
const attachments: Attachment[] = [];
|
||||
for (const file of [...images, ...documents]) {
|
||||
|
||||
@@ -166,7 +166,7 @@ function TipTap(props: TipTapProps) {
|
||||
const tiptapOptions = useMemo<Partial<TiptapOptions>>(() => {
|
||||
return {
|
||||
editorProps: {
|
||||
handleKeyDown(view, event) {
|
||||
handleKeyDown(_, event) {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
|
||||
event.preventDefault();
|
||||
onChange?.(
|
||||
|
||||
@@ -24,6 +24,13 @@ import { ThemeUIStyleObject } from "@theme-ui/css";
|
||||
import { PasswordVisible, PasswordInvisible, Icon } from "../icons";
|
||||
import { useStore as useThemeStore } from "../../stores/theme-store";
|
||||
|
||||
type Action = {
|
||||
testId?: string;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
disabled?: boolean;
|
||||
icon?: Icon;
|
||||
component?: JSX.Element;
|
||||
};
|
||||
export type FieldProps = InputProps & {
|
||||
label?: string;
|
||||
helpText?: string;
|
||||
@@ -35,13 +42,9 @@ export type FieldProps = InputProps & {
|
||||
label?: ThemeUIStyleObject;
|
||||
helpText?: ThemeUIStyleObject;
|
||||
};
|
||||
action?: {
|
||||
testId?: string;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
disabled?: boolean;
|
||||
icon?: Icon;
|
||||
component?: JSX.Element;
|
||||
};
|
||||
action?: Action;
|
||||
rightActions?: Action[];
|
||||
leftActions?: Action[];
|
||||
};
|
||||
|
||||
function Field(props: FieldProps) {
|
||||
@@ -61,6 +64,8 @@ function Field(props: FieldProps) {
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
const colorScheme = useThemeStore((state) => state.colorScheme);
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const rightActions = props.rightActions || (action ? [action] : []);
|
||||
const leftActions = props.leftActions || [];
|
||||
|
||||
return (
|
||||
<Flex
|
||||
@@ -68,6 +73,7 @@ function Field(props: FieldProps) {
|
||||
m: "2px",
|
||||
mr: "2px",
|
||||
opacity: disabled ? 0.7 : 1,
|
||||
gap: 1,
|
||||
...sx,
|
||||
flexDirection: "column"
|
||||
}}
|
||||
@@ -98,7 +104,7 @@ function Field(props: FieldProps) {
|
||||
)}
|
||||
</Label>
|
||||
|
||||
<Flex mt={1} sx={{ position: "relative" }}>
|
||||
<Flex sx={{ position: "relative", flex: 1 }}>
|
||||
<Input
|
||||
{...inputProps}
|
||||
variant={isValid ? inputProps.variant || "input" : "error"}
|
||||
@@ -139,32 +145,89 @@ function Field(props: FieldProps) {
|
||||
{isPasswordVisible ? <PasswordVisible /> : <PasswordInvisible />}
|
||||
</Flex>
|
||||
)}
|
||||
{action && (
|
||||
<Button
|
||||
type="button"
|
||||
variant={"secondary"}
|
||||
data-test-id={action.testId}
|
||||
onClick={action.onClick}
|
||||
|
||||
{rightActions.length > 0 ? (
|
||||
<Flex
|
||||
sx={{
|
||||
bg: "transparent",
|
||||
position: "absolute",
|
||||
margin: 0,
|
||||
top: 0,
|
||||
right: "2px",
|
||||
left: 0,
|
||||
height: "100%",
|
||||
px: 1,
|
||||
borderRadius: "default",
|
||||
":hover": { bg: "border" }
|
||||
borderTopLeftRadius: "default",
|
||||
borderBottomLeftRadius: "default",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
disabled={action.disabled}
|
||||
>
|
||||
{action.component ? (
|
||||
action.component
|
||||
) : action.icon ? (
|
||||
<action.icon size={20} />
|
||||
) : null}
|
||||
</Button>
|
||||
)}
|
||||
{leftActions.map((action) => (
|
||||
<Button
|
||||
key={action.testId}
|
||||
type="button"
|
||||
variant={"secondary"}
|
||||
data-test-id={action.testId}
|
||||
onClick={action.onClick}
|
||||
sx={{
|
||||
p: 0,
|
||||
px: 1,
|
||||
height: "100%",
|
||||
bg: "transparent",
|
||||
borderRadius: 0,
|
||||
margin: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
disabled={action.disabled}
|
||||
>
|
||||
{action.component ? (
|
||||
action.component
|
||||
) : action.icon ? (
|
||||
<action.icon size={20} />
|
||||
) : null}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
) : null}
|
||||
{rightActions.length > 0 ? (
|
||||
<Flex
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
height: "100%",
|
||||
borderTopRightRadius: "default",
|
||||
borderBottomRightRadius: "default",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
{rightActions.map((action) => (
|
||||
<Button
|
||||
key={action.testId}
|
||||
type="button"
|
||||
variant={"secondary"}
|
||||
data-test-id={action.testId}
|
||||
onClick={action.onClick}
|
||||
sx={{
|
||||
p: 0,
|
||||
px: 1,
|
||||
height: "100%",
|
||||
bg: "transparent",
|
||||
borderRadius: 0,
|
||||
margin: 0,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
disabled={action.disabled}
|
||||
>
|
||||
{action.component ? (
|
||||
action.component
|
||||
) : action.icon ? (
|
||||
<action.icon size={20} />
|
||||
) : null}
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
) : null}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Button, Flex, FlexProps, Image, Text } from "@theme-ui/components";
|
||||
import { useStore as useAppStore } from "../../stores/app-store";
|
||||
import { Menu } from "../../hooks/use-menu";
|
||||
import useMobile from "../../hooks/use-mobile";
|
||||
import { PropsWithChildren } from "react";
|
||||
@@ -27,6 +26,7 @@ import { SchemeColors, createButtonVariant } from "@notesnook/theme";
|
||||
import { MenuItem } from "@notesnook/ui";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { AppEventManager, AppEvents } from "../../common/app-events";
|
||||
|
||||
type NavigationItemProps = {
|
||||
icon?: Icon;
|
||||
@@ -66,7 +66,6 @@ function NavigationItem(
|
||||
containerRef,
|
||||
...restProps
|
||||
} = props;
|
||||
const toggleSideMenu = useAppStore((store) => store.toggleSideMenu);
|
||||
const isMobile = useMobile();
|
||||
|
||||
return (
|
||||
@@ -120,7 +119,7 @@ function NavigationItem(
|
||||
Menu.openMenu(menuItems);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isMobile) toggleSideMenu(false);
|
||||
AppEventManager.publish(AppEvents.toggleSideMenu, false);
|
||||
if (onClick) onClick();
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { PropsWithChildren } from "react";
|
||||
import { Flex, Text } from "@theme-ui/components";
|
||||
import { Button, Flex, Text } from "@theme-ui/components";
|
||||
import { ArrowLeft, Menu, Search, Plus, Close } from "../icons";
|
||||
import { useStore } from "../../stores/app-store";
|
||||
import { useStore as useSearchStore } from "../../stores/search-store";
|
||||
@@ -26,6 +26,8 @@ import useMobile from "../../hooks/use-mobile";
|
||||
import { debounce, usePromise } from "@notesnook/common";
|
||||
import Field from "../field";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { TITLE_BAR_HEIGHT } from "../title-bar";
|
||||
import { AppEventManager, AppEvents } from "../../common/app-events";
|
||||
|
||||
export type RouteContainerButtons = {
|
||||
search?: {
|
||||
@@ -64,7 +66,6 @@ function Header(props: RouteContainerProps) {
|
||||
() => (typeof props.title === "string" ? props.title : props.title?.()),
|
||||
[props.title]
|
||||
);
|
||||
const toggleSideMenu = useStore((store) => store.toggleSideMenu);
|
||||
const isMobile = useMobile();
|
||||
const isSearching = useSearchStore((store) => store.isSearching);
|
||||
const query = useSearchStore((store) => store.query);
|
||||
@@ -72,16 +73,24 @@ function Header(props: RouteContainerProps) {
|
||||
if (isSearching)
|
||||
return (
|
||||
<Flex
|
||||
sx={{ alignItems: "center", justifyContent: "center", mx: 1, my: 1 }}
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: TITLE_BAR_HEIGHT,
|
||||
zIndex: 2,
|
||||
px: 1
|
||||
}}
|
||||
className="route-container-header search-container"
|
||||
>
|
||||
<Field
|
||||
data-test-id="search-input"
|
||||
autoFocus
|
||||
id="search"
|
||||
name="search"
|
||||
variant="borderless"
|
||||
type="text"
|
||||
sx={{ m: 0, flex: 1 }}
|
||||
styles={{ input: { p: "7px" } }}
|
||||
sx={{ m: 0, flex: 1, gap: 0 }}
|
||||
styles={{ input: { p: "5px", m: 0 } }}
|
||||
defaultValue={query}
|
||||
placeholder={strings.typeAKeyword()}
|
||||
onChange={debounce(
|
||||
@@ -111,11 +120,12 @@ function Header(props: RouteContainerProps) {
|
||||
return (
|
||||
<Flex
|
||||
className="route-container-header"
|
||||
mx={2}
|
||||
sx={{
|
||||
px: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
height: 42.8
|
||||
height: TITLE_BAR_HEIGHT,
|
||||
zIndex: 2
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
@@ -123,65 +133,80 @@ function Header(props: RouteContainerProps) {
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
overflow: "hidden"
|
||||
overflow: "hidden",
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
{buttons?.back ? (
|
||||
<ArrowLeft
|
||||
size={24}
|
||||
<Button
|
||||
{...buttons.back}
|
||||
sx={{ flexShrink: 0, mr: 2, cursor: "pointer" }}
|
||||
data-test-id="go-back"
|
||||
/>
|
||||
sx={{ p: 0, flexShrink: 0 }}
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
</Button>
|
||||
) : (
|
||||
<Menu
|
||||
onClick={() => toggleSideMenu(true)}
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
ml: 0,
|
||||
mr: 4,
|
||||
mt: 1,
|
||||
display: ["block", "none", "none"]
|
||||
}}
|
||||
size={30}
|
||||
/>
|
||||
<Button
|
||||
onClick={() =>
|
||||
AppEventManager.publish(AppEvents.toggleSideMenu, true)
|
||||
}
|
||||
sx={{ p: 0, flexShrink: 0 }}
|
||||
>
|
||||
<Menu
|
||||
sx={{
|
||||
display: ["block", "none", "none"],
|
||||
size: 23
|
||||
}}
|
||||
size={24}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
{titlePromise.status === "fulfilled" && titlePromise.value && (
|
||||
<Text variant="heading" data-test-id="routeHeader" color="heading">
|
||||
<Text
|
||||
className="routeHeader"
|
||||
variant="heading"
|
||||
data-test-id="routeHeader"
|
||||
color="heading"
|
||||
>
|
||||
{titlePromise.value}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex sx={{ flexShrink: 0 }}>
|
||||
<Flex sx={{ flexShrink: 0, gap: 2 }}>
|
||||
{buttons?.search && (
|
||||
<Search
|
||||
data-test-id={"open-search"}
|
||||
size={24}
|
||||
<Button
|
||||
title={buttons.search.title}
|
||||
onClick={() =>
|
||||
useSearchStore.setState({ isSearching: true, searchType: type })
|
||||
}
|
||||
sx={{
|
||||
size: 24,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
/>
|
||||
data-test-id={"open-search"}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<Search
|
||||
size={24}
|
||||
sx={{
|
||||
size: 24
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
{!isMobile && buttons?.create && (
|
||||
<Plus
|
||||
data-test-id={`${type}-action-button`}
|
||||
color="accentForeground"
|
||||
size={18}
|
||||
sx={{
|
||||
bg: "accent",
|
||||
ml: 2,
|
||||
borderRadius: 100,
|
||||
size: 28,
|
||||
cursor: "pointer",
|
||||
":hover": { boxShadow: "0px 0px 5px 0px var(--accent)" }
|
||||
}}
|
||||
<Button
|
||||
{...buttons.create}
|
||||
/>
|
||||
data-test-id={`${type}-action-button`}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<Plus
|
||||
color="accentForeground"
|
||||
size={18}
|
||||
sx={{
|
||||
height: 24,
|
||||
width: 24,
|
||||
bg: "accent",
|
||||
borderRadius: 100
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
104
apps/web/src/components/split-pane/base.ts
Normal file
104
apps/web/src/components/split-pane/base.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function getBEMElement(block: string, element: string) {
|
||||
return `${block}__${element}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSS class is formed as block’s or element’s name plus two dashes:
|
||||
* .block--mod or .block__elem--mod and .block--color-black with .block--color-red.
|
||||
* Spaces in complicated modifiers are replaced by dash.
|
||||
* @param blockOrElement
|
||||
* @param modifier
|
||||
*/
|
||||
function getBEMModifier<TBlock extends string, TModifier extends string>(
|
||||
blockOrElement: TBlock,
|
||||
modifier: TModifier
|
||||
): `${TBlock}--${TModifier}` {
|
||||
return `${blockOrElement}--${modifier}`;
|
||||
}
|
||||
|
||||
export const splitClassName = "react-split";
|
||||
export const splitDragClassName = getBEMModifier(splitClassName, "dragging");
|
||||
export const splitVerticalClassName = getBEMModifier(
|
||||
splitClassName,
|
||||
"vertical"
|
||||
);
|
||||
export const splitHorizontalClassName = getBEMModifier(
|
||||
splitClassName,
|
||||
"horizontal"
|
||||
);
|
||||
|
||||
export const bodyDisableUserSelect = getBEMModifier(splitClassName, "disabled");
|
||||
export const paneClassName = getBEMElement(splitClassName, "pane");
|
||||
export const sashClassName = getBEMElement(splitClassName, "sash");
|
||||
|
||||
export const sashVerticalClassName = getBEMModifier(sashClassName, "vertical");
|
||||
export const sashHorizontalClassName = getBEMModifier(
|
||||
sashClassName,
|
||||
"horizontal"
|
||||
);
|
||||
export const sashDisabledClassName = getBEMModifier(sashClassName, "disabled");
|
||||
export const sashHoverClassName = getBEMModifier(sashClassName, "hover");
|
||||
|
||||
export function classNames(...args: any[]) {
|
||||
const classList: string[] = [];
|
||||
for (const arg of args) {
|
||||
if (!arg) continue;
|
||||
const argType = typeof arg;
|
||||
if (argType === "string" || argType === "number") {
|
||||
classList.push(`${arg}`);
|
||||
continue;
|
||||
}
|
||||
if (argType === "object") {
|
||||
if (arg.toString !== Object.prototype.toString) {
|
||||
classList.push(arg.toString());
|
||||
continue;
|
||||
}
|
||||
for (const key in arg) {
|
||||
if (Object.hasOwnProperty.call(arg, key) && arg[key]) {
|
||||
classList.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return classList.join(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert size to absolute number or Infinity
|
||||
* SplitPane allows sizes in string and number, but the state sizes only support number,
|
||||
* so convert string and number to number in here
|
||||
* 'auto' -> divide the remaining space equally
|
||||
* 'xxxpx' -> xxx
|
||||
* 'xxx%' -> wrapper.size * xxx/100
|
||||
* xxx -> xxx
|
||||
*/
|
||||
export function assertsSize(
|
||||
size: string | number | undefined,
|
||||
sum: number,
|
||||
defaultValue = Infinity
|
||||
) {
|
||||
if (typeof size === "undefined") return defaultValue;
|
||||
if (typeof size === "number") return size;
|
||||
if (size.endsWith("%")) return sum * (+size.replace("%", "") / 100);
|
||||
if (size.endsWith("px")) return +size.replace("px", "");
|
||||
return defaultValue;
|
||||
}
|
||||
432
apps/web/src/components/split-pane/index.tsx
Normal file
432
apps/web/src/components/split-pane/index.tsx
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
import React, {
|
||||
useEffect,
|
||||
useMemo,
|
||||
useCallback,
|
||||
useRef,
|
||||
PropsWithChildren,
|
||||
useLayoutEffect,
|
||||
useImperativeHandle
|
||||
} from "react";
|
||||
import Pane from "./pane";
|
||||
import Sash from "./sash";
|
||||
import {
|
||||
classNames,
|
||||
bodyDisableUserSelect,
|
||||
paneClassName,
|
||||
splitClassName,
|
||||
splitDragClassName,
|
||||
splitVerticalClassName,
|
||||
splitHorizontalClassName,
|
||||
sashDisabledClassName,
|
||||
sashHorizontalClassName,
|
||||
sashVerticalClassName,
|
||||
assertsSize
|
||||
} from "./base";
|
||||
import { IAxis, ISplitProps, IPaneConfigs } from "./types";
|
||||
import Config from "../../utils/config";
|
||||
export { Pane };
|
||||
|
||||
type PaneOptions = {
|
||||
min: number;
|
||||
max: number;
|
||||
snap: number;
|
||||
size: number;
|
||||
nextSize?: number;
|
||||
initialSize: number;
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
export type SplitPaneImperativeHandle = {
|
||||
collapse: (index: number) => void;
|
||||
expand: (index: number) => void;
|
||||
};
|
||||
export const SplitPane = React.forwardRef<
|
||||
SplitPaneImperativeHandle,
|
||||
PropsWithChildren<ISplitProps>
|
||||
>(function SplitPane(
|
||||
{
|
||||
children,
|
||||
allowResize = true,
|
||||
direction = "vertical",
|
||||
className: wrapClassName,
|
||||
sashStyle,
|
||||
sashRender = (_, active) => (
|
||||
<div
|
||||
className={classNames(
|
||||
"split-sash-content",
|
||||
active && "split-sash-content-active"
|
||||
)}
|
||||
/>
|
||||
),
|
||||
sashSize: resizerSize = 5,
|
||||
onChange = () => null,
|
||||
onDragStart = () => null,
|
||||
onDragEnd = () => null,
|
||||
autoSaveId,
|
||||
...others
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const axis = useRef<IAxis>({ x: 0, y: 0 });
|
||||
const wrapper = useRef<HTMLDivElement>(null);
|
||||
const sashPosSizes = useRef<number[]>([]);
|
||||
const panes = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const sashes = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const paneSizes = useRef<PaneOptions[]>([]);
|
||||
const wrapSize = useRef(0);
|
||||
const childrenLength = childrenToArray(children).length;
|
||||
const autoSaveKey = autoSaveId ? `csp:${autoSaveId}` : undefined;
|
||||
|
||||
const { sizeName, splitPos, splitAxis } = useMemo(
|
||||
() =>
|
||||
({
|
||||
sizeName: direction === "vertical" ? "width" : "height",
|
||||
splitPos: direction === "vertical" ? "left" : "top",
|
||||
splitAxis: direction === "vertical" ? "x" : "y"
|
||||
} as const),
|
||||
[direction]
|
||||
);
|
||||
|
||||
const updatePaneLimitSizes = useCallback(
|
||||
(children: React.ReactNode) => {
|
||||
paneSizes.current =
|
||||
childrenToArray(children).map((childNode) => {
|
||||
const limits: PaneOptions = {
|
||||
min: 0,
|
||||
max: Infinity,
|
||||
snap: 0,
|
||||
size: Infinity,
|
||||
initialSize: Infinity,
|
||||
collapsed: false
|
||||
};
|
||||
if (React.isValidElement(childNode) && childNode.type === Pane) {
|
||||
const { minSize, maxSize, snapSize, initialSize, id, collapsed } =
|
||||
childNode.props as IPaneConfigs;
|
||||
limits.min = assertsSize(minSize, wrapSize.current, 0);
|
||||
limits.max = assertsSize(maxSize, wrapSize.current);
|
||||
limits.snap = assertsSize(snapSize, wrapSize.current, 0);
|
||||
limits.initialSize = assertsSize(initialSize, wrapSize.current);
|
||||
|
||||
Object.defineProperty(limits, "collapsed", {
|
||||
get() {
|
||||
return Config.get(`${autoSaveKey}-${id}:collapsed`, collapsed);
|
||||
},
|
||||
set(v) {
|
||||
if (v == null) Config.remove(`${autoSaveKey}-${id}:collapsed`);
|
||||
else Config.set(`${autoSaveKey}-${id}:collapsed`, v);
|
||||
}
|
||||
});
|
||||
Object.defineProperty(limits, "size", {
|
||||
get() {
|
||||
return Config.get(
|
||||
`${autoSaveKey}-${id}`,
|
||||
assertsSize(initialSize, wrapSize.current)
|
||||
);
|
||||
},
|
||||
set(v) {
|
||||
if (v === null || v === undefined || v === Infinity)
|
||||
Config.remove(`${autoSaveKey}-${id}`);
|
||||
else Config.set(`${autoSaveKey}-${id}`, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
return limits;
|
||||
}) || [];
|
||||
},
|
||||
[autoSaveKey]
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (wrapSize.current === 0) {
|
||||
wrapSize.current =
|
||||
wrapper.current?.getBoundingClientRect()[sizeName] || 0;
|
||||
}
|
||||
|
||||
if (wrapSize.current === 0) return;
|
||||
updatePaneLimitSizes(children);
|
||||
setSizes(paneSizes.current, wrapSize.current, false);
|
||||
}, [children, childrenLength]);
|
||||
|
||||
const setSizes = useCallback(
|
||||
function setSizes(
|
||||
paneLimits: PaneOptions[],
|
||||
wrapSize: number,
|
||||
notify = true
|
||||
) {
|
||||
const normalized = normalizeSizes(children, paneLimits, wrapSize);
|
||||
sashPosSizes.current = normalized.reduce(
|
||||
(a, b) => [...a, a[a.length - 1] + b],
|
||||
[0]
|
||||
);
|
||||
|
||||
for (let i = 0; i < panes.current.length; ++i) {
|
||||
const pane = panes.current[i];
|
||||
if (!pane) continue;
|
||||
const size = normalized[i];
|
||||
const sashPos = sashPosSizes.current[i];
|
||||
const limits = paneSizes.current[i];
|
||||
pane.style[sizeName] = `${size}px`;
|
||||
pane.style[splitPos] = `${sashPos}px`;
|
||||
if (limits.collapsed || size === limits.min)
|
||||
pane.classList.add("collapsed");
|
||||
else pane.classList.remove("collapsed");
|
||||
}
|
||||
|
||||
for (let i = 0; i < sashes.current.length; ++i) {
|
||||
const sash = sashes.current[i];
|
||||
const sashPos = sashPosSizes.current[i + 1];
|
||||
if (sash) {
|
||||
sash.style[splitPos] = `${sashPos - resizerSize / 2}px`;
|
||||
}
|
||||
}
|
||||
|
||||
paneSizes.current.forEach((limits, index) => {
|
||||
limits.size = normalized[index];
|
||||
});
|
||||
|
||||
if (notify) onChange(normalized);
|
||||
},
|
||||
[children, onChange, sizeName, splitPos, resizerSize]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wrapper.current) return;
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
if (!paneSizes.current.length) return;
|
||||
|
||||
const [entry] = entries;
|
||||
const newSize = entry.contentRect ? entry.contentRect[sizeName] : 0;
|
||||
|
||||
const delta = newSize - wrapSize.current;
|
||||
if (delta === 0) return;
|
||||
|
||||
// TODO: responsiveness
|
||||
// const nextSizes = [...sizes.current];
|
||||
// const i = sizes.current.length - 1;
|
||||
// const currentSize = sizes.current[i];
|
||||
// const currentPaneLimits = paneLimitSizes.current[i];
|
||||
// const prevPaneLimits = paneLimitSizes.current[i - 1];
|
||||
|
||||
// if (
|
||||
// delta > 0 &&
|
||||
// prevPaneLimits &&
|
||||
// prevPaneLimits.max < Infinity &&
|
||||
// nextSizes[i - 1] + delta <= prevPaneLimits.max
|
||||
// ) {
|
||||
// nextSizes[i - 1] += delta;
|
||||
// } else if (delta < 0 && currentSize + delta <= currentPaneLimits.min) {
|
||||
// nextSizes[i - 1] += delta;
|
||||
// } else {
|
||||
// nextSizes[i] += delta;
|
||||
// }
|
||||
|
||||
wrapSize.current = newSize;
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
});
|
||||
resizeObserver.observe(wrapper.current);
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [sizeName, setSizes]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => {
|
||||
return {
|
||||
collapse: (index: number) => {
|
||||
paneSizes.current[index].collapsed = true;
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
},
|
||||
expand: (index: number) => {
|
||||
paneSizes.current[index].collapsed = false;
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
}
|
||||
};
|
||||
},
|
||||
[setSizes]
|
||||
);
|
||||
|
||||
const dragStart = useCallback(
|
||||
function (e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
document?.body?.classList?.add(bodyDisableUserSelect);
|
||||
axis.current = { x: e.pageX, y: e.pageY };
|
||||
wrapper.current?.classList.toggle(splitDragClassName, true);
|
||||
onDragStart(e);
|
||||
},
|
||||
[onDragStart]
|
||||
);
|
||||
|
||||
const dragEnd = useCallback(
|
||||
function (e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||
document?.body?.classList?.remove(bodyDisableUserSelect);
|
||||
wrapper.current?.classList.toggle(splitDragClassName);
|
||||
onDragEnd(e);
|
||||
},
|
||||
[onDragEnd]
|
||||
);
|
||||
|
||||
const onDragging = useCallback(
|
||||
function onDragging(
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
i: number
|
||||
) {
|
||||
const curAxis = { x: e.pageX, y: e.pageY };
|
||||
let distanceX = curAxis[splitAxis] - axis.current[splitAxis];
|
||||
axis.current = { x: e.pageX, y: e.pageY };
|
||||
|
||||
const currentPane = paneSizes.current[i];
|
||||
const nextPane = paneSizes.current[i + 1];
|
||||
const rightBorder = sashPosSizes.current[i + 2];
|
||||
|
||||
if (currentPane.size + distanceX >= rightBorder)
|
||||
distanceX = rightBorder - currentPane.size;
|
||||
|
||||
// if current pane size is out of limit, adjust the previous pane
|
||||
if (
|
||||
currentPane.size + distanceX >= currentPane.max ||
|
||||
currentPane.size + distanceX <= currentPane.min
|
||||
) {
|
||||
if (i > 0) {
|
||||
// reset axis
|
||||
axis.current[splitAxis] += -distanceX;
|
||||
onDragging(e, i - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
currentPane.nextSize =
|
||||
(currentPane.nextSize || currentPane.size) + distanceX;
|
||||
// keep the next pane size in the min-max range
|
||||
nextPane.nextSize = Math.min(
|
||||
nextPane.max,
|
||||
Math.max(nextPane.min, (nextPane.nextSize || nextPane.size) - distanceX)
|
||||
);
|
||||
|
||||
// snapping logic
|
||||
if (currentPane.snap > 0) {
|
||||
if (distanceX < 0 && currentPane.nextSize <= currentPane.snap / 2) {
|
||||
currentPane.nextSize = currentPane.min;
|
||||
} else if (currentPane.nextSize < currentPane.snap) {
|
||||
// reset axis
|
||||
axis.current[splitAxis] += -distanceX;
|
||||
return;
|
||||
}
|
||||
}
|
||||
nextPane.size = nextPane.nextSize;
|
||||
currentPane.size = currentPane.nextSize;
|
||||
nextPane.nextSize = undefined;
|
||||
currentPane.nextSize = undefined;
|
||||
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
},
|
||||
[paneSizes, setSizes, splitAxis]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
splitClassName,
|
||||
direction === "vertical" && splitVerticalClassName,
|
||||
direction === "horizontal" && splitHorizontalClassName,
|
||||
wrapClassName
|
||||
)}
|
||||
ref={wrapper}
|
||||
{...others}
|
||||
>
|
||||
{childrenToArray(children).map((childNode, childIndex) => {
|
||||
const isPane = React.isValidElement(childNode)
|
||||
? childNode.type === Pane
|
||||
: false;
|
||||
const paneProps =
|
||||
isPane && React.isValidElement(childNode) ? childNode.props : {};
|
||||
|
||||
return (
|
||||
<Pane
|
||||
id={paneProps.id}
|
||||
key={childIndex}
|
||||
paneRef={(e) => (panes.current[childIndex] = e)}
|
||||
className={classNames(paneClassName, paneProps.className)}
|
||||
style={{ ...paneProps.style }}
|
||||
>
|
||||
{isPane ? paneProps.children : childNode}
|
||||
</Pane>
|
||||
);
|
||||
})}
|
||||
{new Array(childrenLength - 1).fill(0).map((_, index) => (
|
||||
<Sash
|
||||
key={index}
|
||||
sashRef={(e) => (sashes.current[index] = e)}
|
||||
className={classNames(
|
||||
!allowResize && sashDisabledClassName,
|
||||
direction === "vertical"
|
||||
? sashVerticalClassName
|
||||
: sashHorizontalClassName
|
||||
)}
|
||||
style={{
|
||||
[sizeName]: resizerSize,
|
||||
...sashStyle
|
||||
}}
|
||||
render={sashRender.bind(null, index)}
|
||||
onDragStart={dragStart}
|
||||
onDragging={(e) => onDragging(e, index)}
|
||||
onDragEnd={dragEnd}
|
||||
onDoubleClick={() => {
|
||||
paneSizes.current[index].size =
|
||||
paneSizes.current[index].initialSize;
|
||||
setSizes(paneSizes.current, wrapSize.current);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
function childrenToArray(children: React.ReactNode) {
|
||||
return React.Children.toArray(children);
|
||||
}
|
||||
|
||||
function normalizeSizes(
|
||||
children: React.ReactNode,
|
||||
panes: PaneOptions[],
|
||||
wrapSize: number
|
||||
): number[] {
|
||||
let count = 0;
|
||||
let curSum = 0;
|
||||
const res = childrenToArray(children).map((_, index) => {
|
||||
const initialSize = panes[index].initialSize;
|
||||
const size = panes[index].collapsed ? panes[index].min : panes[index].size;
|
||||
initialSize === Infinity ? count++ : (curSum += size);
|
||||
return size;
|
||||
});
|
||||
|
||||
if (count > 0 || curSum > wrapSize) {
|
||||
const average = (wrapSize - curSum) / count;
|
||||
return res.map((size, index) => {
|
||||
const initialSize = panes[index].initialSize;
|
||||
return initialSize === Infinity ? average : size;
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
44
apps/web/src/components/split-pane/pane.tsx
Normal file
44
apps/web/src/components/split-pane/pane.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { PropsWithChildren } from "react";
|
||||
import { HTMLElementProps, IPaneConfigs } from "./types";
|
||||
|
||||
export default function Pane({
|
||||
paneRef,
|
||||
children,
|
||||
style,
|
||||
className,
|
||||
role,
|
||||
title,
|
||||
id
|
||||
}: PropsWithChildren<HTMLElementProps & IPaneConfigs>) {
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
ref={paneRef}
|
||||
role={role}
|
||||
title={title}
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
75
apps/web/src/components/split-pane/sash.tsx
Normal file
75
apps/web/src/components/split-pane/sash.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { useRef, useState } from "react";
|
||||
import { classNames, sashClassName } from "./base";
|
||||
import { ISashProps } from "./types";
|
||||
|
||||
export default function Sash({
|
||||
className,
|
||||
render,
|
||||
onDragStart,
|
||||
onDragging,
|
||||
onDragEnd,
|
||||
sashRef,
|
||||
...others
|
||||
}: ISashProps) {
|
||||
const timeout = useRef<number | null>(null);
|
||||
const [active, setActive] = useState(false);
|
||||
const [draging, setDrag] = useState(false);
|
||||
|
||||
const handleMouseMove = function (e: MouseEvent) {
|
||||
onDragging(e as any);
|
||||
};
|
||||
|
||||
const handleMouseUp = function (e: MouseEvent) {
|
||||
setDrag(false);
|
||||
onDragEnd(e as any);
|
||||
window.removeEventListener("mousemove", handleMouseMove);
|
||||
window.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={sashRef}
|
||||
role="Resizer"
|
||||
className={classNames(sashClassName, className)}
|
||||
onMouseEnter={() => {
|
||||
timeout.current = setTimeout(() => {
|
||||
setActive(true);
|
||||
}, 150) as unknown as number;
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
if (timeout.current) {
|
||||
setActive(false);
|
||||
clearTimeout(timeout.current);
|
||||
}
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
setDrag(true);
|
||||
onDragStart(e);
|
||||
|
||||
window.addEventListener("mousemove", handleMouseMove);
|
||||
window.addEventListener("mouseup", handleMouseUp);
|
||||
}}
|
||||
{...others}
|
||||
>
|
||||
{render(draging || active)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
59
apps/web/src/components/split-pane/styles.css
Normal file
59
apps/web/src/components/split-pane/styles.css
Normal file
@@ -0,0 +1,59 @@
|
||||
.react-split {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.react-split__pane {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.react-split__sash {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
transition: background-color 0.1s;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
.react-split__sash--disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
.react-split__sash--vertical {
|
||||
cursor: col-resize;
|
||||
}
|
||||
.react-split__sash--horizontal {
|
||||
cursor: row-resize;
|
||||
}
|
||||
.react-split__sash-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.react-split--dragging.react-split--vertical {
|
||||
cursor: col-resize;
|
||||
}
|
||||
.react-split--dragging.react-split--horizontal {
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
body.react-split--disabled {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.split-sash-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.split-sash-content.split-sash-content-active {
|
||||
background-color: var(--accent);
|
||||
}
|
||||
78
apps/web/src/components/split-pane/types.ts
Normal file
78
apps/web/src/components/split-pane/types.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
export interface HTMLElementProps {
|
||||
title?: string;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
export interface IAxis {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface ICacheSizes {
|
||||
sizes: (string | number)[];
|
||||
sashPosSizes: (string | number)[];
|
||||
}
|
||||
|
||||
export interface ISplitProps extends HTMLElementProps {
|
||||
autoSaveId?: string;
|
||||
allowResize?: boolean;
|
||||
direction: "vertical" | "horizontal";
|
||||
sashRender?: (index: number, active: boolean) => React.ReactNode;
|
||||
onChange?: (sizes: number[]) => void;
|
||||
onDragStart?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
onDragEnd?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
className?: string;
|
||||
sashClassName?: string;
|
||||
sashStyle?: React.CSSProperties;
|
||||
sashSize?: number;
|
||||
}
|
||||
|
||||
export interface ISashProps {
|
||||
sashRef?: React.LegacyRef<HTMLDivElement>;
|
||||
className?: string;
|
||||
style: React.CSSProperties;
|
||||
render: (active: boolean) => React.ReactNode;
|
||||
onDragStart: React.MouseEventHandler<HTMLDivElement>;
|
||||
onDragging: React.MouseEventHandler<HTMLDivElement>;
|
||||
onDragEnd: React.MouseEventHandler<HTMLDivElement>;
|
||||
onDoubleClick: React.MouseEventHandler<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export interface ISashContentProps {
|
||||
className?: string;
|
||||
active?: boolean;
|
||||
children?: JSX.Element[];
|
||||
}
|
||||
|
||||
export interface IPaneConfigs {
|
||||
id: string;
|
||||
paneRef?: React.LegacyRef<HTMLDivElement>;
|
||||
maxSize?: number | string;
|
||||
minSize?: number | string;
|
||||
snapSize?: number | string;
|
||||
initialSize?: number | string;
|
||||
collapsed?: boolean;
|
||||
}
|
||||
@@ -61,6 +61,7 @@ function StatusBar() {
|
||||
borderTopColor: "separator",
|
||||
justifyContent: "space-between",
|
||||
display: ["none", "none", "flex"],
|
||||
flexShrink: 0,
|
||||
height: 24
|
||||
}}
|
||||
px={2}
|
||||
@@ -174,6 +175,7 @@ function StatusBar() {
|
||||
justifyContent: "center",
|
||||
display: "flex"
|
||||
}}
|
||||
data-test-id="vault-unlocked"
|
||||
>
|
||||
<Unlock size={10} />
|
||||
<Text variant="subBody" ml={1} sx={{ color: "paragraph" }}>
|
||||
|
||||
@@ -17,25 +17,30 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Button } from "@theme-ui/components";
|
||||
import { desktop } from "../../common/desktop-bridge";
|
||||
import { useWindowControls } from "../../hooks/use-window-controls";
|
||||
import { getPlatform } from "../../utils/platform";
|
||||
import { isMac } from "../../utils/platform";
|
||||
import { BaseThemeProvider } from "../theme-provider";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { desktop } from "../../common/desktop-bridge";
|
||||
import {
|
||||
WindowClose,
|
||||
WindowMaximize,
|
||||
WindowMinimize,
|
||||
WindowRestore
|
||||
} from "../icons";
|
||||
import { BaseThemeProvider } from "../theme-provider";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { Button, Flex } from "@theme-ui/components";
|
||||
import useMobile from "../../hooks/use-mobile";
|
||||
import useTablet from "../../hooks/use-tablet";
|
||||
|
||||
export const TITLE_BAR_HEIGHT = IS_DESKTOP_APP ? 37.8 : 0;
|
||||
export function TitleBar() {
|
||||
const { isMaximized, isFullscreen, hasNativeWindowControls } =
|
||||
useWindowControls();
|
||||
|
||||
const tools = [
|
||||
export function getWindowControls(
|
||||
hasNativeWindowControls: boolean,
|
||||
isFullscreen?: boolean,
|
||||
isMaximized?: boolean,
|
||||
isTablet?: boolean,
|
||||
isMobile?: boolean
|
||||
) {
|
||||
if (isMobile || isTablet) return [];
|
||||
return [
|
||||
{
|
||||
title: strings.minimize(),
|
||||
icon: WindowMinimize,
|
||||
@@ -61,27 +66,46 @@ export function TitleBar() {
|
||||
onClick: () => window.close()
|
||||
}
|
||||
];
|
||||
}
|
||||
export const TITLE_BAR_HEIGHT = 37;
|
||||
export function TitleBar({ isUnderlay = isMac() }: { isUnderlay?: boolean }) {
|
||||
const { isFullscreen, hasNativeWindowControls, isMaximized } =
|
||||
useWindowControls();
|
||||
const isTablet = useTablet();
|
||||
const isMobile = useMobile();
|
||||
if ((!isMac() && !isMobile && !isTablet) || (isFullscreen && isMac()))
|
||||
return null;
|
||||
|
||||
const tools = getWindowControls(
|
||||
hasNativeWindowControls,
|
||||
isFullscreen,
|
||||
isMaximized
|
||||
);
|
||||
return (
|
||||
<BaseThemeProvider
|
||||
scope="titleBar"
|
||||
className="titlebar"
|
||||
sx={{
|
||||
background: "background",
|
||||
height: TITLE_BAR_HEIGHT,
|
||||
minHeight: TITLE_BAR_HEIGHT,
|
||||
maxHeight: TITLE_BAR_HEIGHT,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexShrink: 0,
|
||||
width: "100%",
|
||||
zIndex: 1,
|
||||
borderBottom: "1px solid var(--border)",
|
||||
...(!isFullscreen && hasNativeWindowControls
|
||||
? getPlatform() === "darwin"
|
||||
? { pl: "calc(100vw - env(titlebar-area-width))" }
|
||||
: { pr: "calc(100vw - env(titlebar-area-width))" }
|
||||
: { pr: 0 })
|
||||
...(isUnderlay
|
||||
? {
|
||||
position: "absolute",
|
||||
top: 0
|
||||
}
|
||||
: {})
|
||||
}}
|
||||
injectCssVars
|
||||
>
|
||||
{getPlatform() !== "darwin" || isFullscreen ? (
|
||||
{tools.filter((t) => !t.hidden).length > 0 ? (
|
||||
<svg
|
||||
className="titlebarLogo"
|
||||
style={{
|
||||
@@ -95,40 +119,34 @@ export function TitleBar() {
|
||||
<use href="#themed-logo" />
|
||||
</svg>
|
||||
) : null}
|
||||
<div
|
||||
id="titlebar-portal-container"
|
||||
style={{
|
||||
flex: 1,
|
||||
display: "flex",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
/>
|
||||
{tools.map((tool) => (
|
||||
<Button
|
||||
data-test-id={tool.title}
|
||||
disabled={!tool.enabled}
|
||||
variant={tool.title === "Close" ? "error" : "secondary"}
|
||||
title={tool.title}
|
||||
key={tool.title}
|
||||
sx={{
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
bg: "transparent",
|
||||
display: tool.hidden ? "none" : "flex",
|
||||
borderRadius: 0,
|
||||
flexShrink: 0,
|
||||
"&:hover svg path": {
|
||||
fill:
|
||||
tool.title === "Close"
|
||||
? "var(--accentForeground-error) !important"
|
||||
: "var(--icon)"
|
||||
}
|
||||
}}
|
||||
onClick={tool.onClick}
|
||||
>
|
||||
<tool.icon size={18} />
|
||||
</Button>
|
||||
))}
|
||||
<Flex sx={{ alignItems: "center" }}>
|
||||
{tools.map((tool) => (
|
||||
<Button
|
||||
data-test-id={tool.title}
|
||||
disabled={!tool.enabled}
|
||||
variant={tool.title === "Close" ? "error" : "secondary"}
|
||||
title={tool.title}
|
||||
key={tool.title}
|
||||
sx={{
|
||||
height: "100%",
|
||||
alignItems: "center",
|
||||
bg: "transparent",
|
||||
display: tool.hidden ? "none" : "flex",
|
||||
borderRadius: 0,
|
||||
flexShrink: 0,
|
||||
"&:hover svg path": {
|
||||
fill:
|
||||
tool.title === "Close"
|
||||
? "var(--accentForeground-error) !important"
|
||||
: "var(--icon)"
|
||||
}
|
||||
}}
|
||||
onClick={tool.onClick}
|
||||
>
|
||||
<tool.icon size={18} />
|
||||
</Button>
|
||||
))}
|
||||
</Flex>
|
||||
</BaseThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,24 +22,13 @@ import Dialog from "../components/dialog";
|
||||
import { ScrollContainer } from "@notesnook/ui";
|
||||
import { Flex, Image, Label, Text } from "@theme-ui/components";
|
||||
import { formatBytes } from "@notesnook/common";
|
||||
import { compressImage } from "../utils/image-compressor";
|
||||
import { compressImage, FileWithURI } from "../utils/image-compressor";
|
||||
import { BaseDialogProps, DialogManager } from "../common/dialog-manager";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
export type ImagePickerDialogProps = BaseDialogProps<false | File[]> & {
|
||||
images: File[];
|
||||
};
|
||||
class FileWithURI extends File {
|
||||
uri: string;
|
||||
constructor(
|
||||
fileBits: BlobPart[],
|
||||
fileName: string,
|
||||
options?: FilePropertyBag
|
||||
) {
|
||||
super(fileBits, fileName, options);
|
||||
this.uri = URL.createObjectURL(this);
|
||||
}
|
||||
}
|
||||
|
||||
export const ImagePickerDialog = DialogManager.register(
|
||||
function ImagePickerDialog(props: ImagePickerDialogProps) {
|
||||
|
||||
@@ -19,7 +19,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { DATE_FORMATS } from "@notesnook/core";
|
||||
import { SettingsGroup } from "./types";
|
||||
import { useStore as useSettingStore } from "../../stores/setting-store";
|
||||
import {
|
||||
ImageCompressionOptions,
|
||||
useStore as useSettingStore
|
||||
} from "../../stores/setting-store";
|
||||
import dayjs from "dayjs";
|
||||
import { isUserPremium } from "../../hooks/use-is-user-premium";
|
||||
import { TimeFormat } from "@notesnook/core";
|
||||
@@ -55,6 +58,37 @@ export const BehaviourSettings: SettingsGroup[] = [
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: "image-compression",
|
||||
title: strings.imageCompression(),
|
||||
description: strings.imageCompressionDesc(),
|
||||
keywords: ["compress images", "image quality"],
|
||||
onStateChange: (listener) =>
|
||||
useSettingStore.subscribe((s) => s.imageCompression, listener),
|
||||
components: [
|
||||
{
|
||||
type: "dropdown",
|
||||
onSelectionChanged: (value) =>
|
||||
useSettingStore.getState().setImageCompression(parseInt(value)),
|
||||
selectedOption: () =>
|
||||
useSettingStore.getState().imageCompression.toString(),
|
||||
options: [
|
||||
{
|
||||
value: ImageCompressionOptions.ASK_EVERY_TIME.toString(),
|
||||
title: strings.askEveryTime()
|
||||
},
|
||||
{
|
||||
value: ImageCompressionOptions.ENABLE.toString(),
|
||||
title: strings.enableRecommended()
|
||||
},
|
||||
{
|
||||
value: ImageCompressionOptions.DISABLE.toString(),
|
||||
title: strings.disable()
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -26,27 +26,24 @@ type Slide = {
|
||||
width: number;
|
||||
};
|
||||
|
||||
export default function useSlider(
|
||||
sliderId: string,
|
||||
{
|
||||
onSliding,
|
||||
onChange
|
||||
}: {
|
||||
onSliding?: (
|
||||
e: Event,
|
||||
options: {
|
||||
lastSlide: Slide | null;
|
||||
lastPosition: number;
|
||||
position: number;
|
||||
}
|
||||
) => void;
|
||||
onChange?: (
|
||||
e: Event,
|
||||
options: { position: number; slide: Slide; lastSlide: Slide | null }
|
||||
) => void;
|
||||
} = {}
|
||||
) {
|
||||
const ref = useRef(document.getElementById(sliderId));
|
||||
export default function useSlider({
|
||||
onSliding,
|
||||
onChange
|
||||
}: {
|
||||
onSliding?: (
|
||||
e: Event,
|
||||
options: {
|
||||
lastSlide: Slide | null;
|
||||
lastPosition: number;
|
||||
position: number;
|
||||
}
|
||||
) => void;
|
||||
onChange?: (
|
||||
e: Event,
|
||||
options: { position: number; slide: Slide; lastSlide: Slide | null }
|
||||
) => void;
|
||||
} = {}) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const slides: Slide[] = useMemo(() => [], []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -57,7 +54,11 @@ export default function useSlider(
|
||||
let last = 0;
|
||||
if (!slides.length) {
|
||||
for (const node of slider.childNodes) {
|
||||
if (!(node instanceof HTMLElement)) continue;
|
||||
if (
|
||||
!(node instanceof HTMLElement) ||
|
||||
node.classList.contains("ms-track-box")
|
||||
)
|
||||
continue;
|
||||
slides.push({
|
||||
index: slides.length,
|
||||
node,
|
||||
@@ -102,5 +103,5 @@ export default function useSlider(
|
||||
[ref, slides]
|
||||
);
|
||||
|
||||
return [slideToIndex];
|
||||
return { ref, slideToIndex };
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ export function useWindowControls() {
|
||||
|
||||
function onFullscreenChange() {
|
||||
setIsFullscreen(!!document.fullscreenElement);
|
||||
console.log(document.fullscreenElement);
|
||||
}
|
||||
document.addEventListener("fullscreenchange", onFullscreenChange);
|
||||
return () => {
|
||||
|
||||
43
apps/web/src/hooks/use-window-focus.ts
Normal file
43
apps/web/src/hooks/use-window-focus.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useWindowFocus() {
|
||||
const [isFocused, setIsFocused] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
function onFocus() {
|
||||
setIsFocused(true);
|
||||
}
|
||||
function onBlur() {
|
||||
setIsFocused(false);
|
||||
}
|
||||
window.addEventListener("focus", onFocus);
|
||||
window.addEventListener("blur", onBlur);
|
||||
return () => {
|
||||
document.removeEventListener("focus", onFocus);
|
||||
document.removeEventListener("blur", onBlur);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isFocused
|
||||
};
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export async function startApp() {
|
||||
console.error(e);
|
||||
root.render(
|
||||
<>
|
||||
<TitleBar />
|
||||
<TitleBar isUnderlay={false} />
|
||||
<ErrorComponent
|
||||
error={e}
|
||||
resetErrorBoundary={() => window.location.reload()}
|
||||
|
||||
@@ -61,10 +61,7 @@ let syncTimeout = 0;
|
||||
let pendingSync: SyncOptions | undefined = undefined;
|
||||
|
||||
class AppStore extends BaseStore<AppStore> {
|
||||
// default state
|
||||
isSideMenuOpen = false;
|
||||
isFocusMode = false;
|
||||
isEditorOpen = false;
|
||||
isVaultCreated = false;
|
||||
isAutoSyncEnabled = Config.get("autoSyncEnabled", true);
|
||||
isSyncEnabled = Config.get("syncEnabled", true);
|
||||
@@ -199,17 +196,6 @@ class AppStore extends BaseStore<AppStore> {
|
||||
);
|
||||
};
|
||||
|
||||
toggleSideMenu = (toggleState: boolean) => {
|
||||
console.log("toggling side menu");
|
||||
this.set(
|
||||
(state) => (state.isSideMenuOpen = toggleState ?? !state.isSideMenuOpen)
|
||||
);
|
||||
};
|
||||
|
||||
setIsEditorOpen = (toggleState: boolean) => {
|
||||
this.set((state) => (state.isEditorOpen = toggleState));
|
||||
};
|
||||
|
||||
setIsVaultCreated = (toggleState: boolean) => {
|
||||
this.set((state) => (state.isVaultCreated = toggleState));
|
||||
};
|
||||
|
||||
@@ -518,7 +518,7 @@ class EditorStore extends BaseStore<EditorStore> {
|
||||
} else setDocumentTitle();
|
||||
|
||||
this.set({ activeSessionId: id });
|
||||
appStore.setIsEditorOpen(!!id);
|
||||
AppEventManager.publish(AppEvents.toggleEditor, true);
|
||||
|
||||
if (id) {
|
||||
const { history } = this.get();
|
||||
@@ -717,6 +717,32 @@ class EditorStore extends BaseStore<EditorStore> {
|
||||
}
|
||||
};
|
||||
|
||||
openNextSession = () => {
|
||||
const { sessions, activeSessionId } = this.get();
|
||||
if (sessions.length === 0 || sessions.length === 1) return;
|
||||
|
||||
const index = sessions.findIndex((s) => s.id === activeSessionId);
|
||||
if (index === -1) return;
|
||||
|
||||
if (index === sessions.length - 1) {
|
||||
return this.openSession(sessions[0].id);
|
||||
}
|
||||
return this.openSession(sessions[index + 1].id);
|
||||
};
|
||||
|
||||
openPreviousSession = () => {
|
||||
const { sessions, activeSessionId } = this.get();
|
||||
if (sessions.length === 0 || sessions.length === 1) return;
|
||||
|
||||
const index = sessions.findIndex((s) => s.id === activeSessionId);
|
||||
if (index === -1) return;
|
||||
|
||||
if (index === 0) {
|
||||
return this.openSession(sessions[sessions.length - 1].id);
|
||||
}
|
||||
return this.openSession(sessions[index - 1].id);
|
||||
};
|
||||
|
||||
addSession = (session: EditorSession, activate = true) => {
|
||||
let oldSessionId: string | null = null;
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ export const HostIds = [
|
||||
"MONOGRAPH_HOST"
|
||||
] as const;
|
||||
export type HostId = (typeof HostIds)[number];
|
||||
|
||||
export enum ImageCompressionOptions {
|
||||
ASK_EVERY_TIME,
|
||||
ENABLE,
|
||||
DISABLE
|
||||
}
|
||||
|
||||
class SettingStore extends BaseStore<SettingStore> {
|
||||
encryptBackups = Config.get("encryptBackups", false);
|
||||
backupReminderOffset = Config.get("backupReminderOffset", 0);
|
||||
@@ -60,6 +67,10 @@ class SettingStore extends BaseStore<SettingStore> {
|
||||
|
||||
trashCleanupInterval: TrashCleanupInterval = 7;
|
||||
homepage = Config.get("homepage", 0);
|
||||
imageCompression = Config.get(
|
||||
"imageCompression",
|
||||
ImageCompressionOptions.ASK_EVERY_TIME
|
||||
);
|
||||
desktopIntegrationSettings?: DesktopIntegration;
|
||||
autoUpdates = true;
|
||||
isFlatpak = false;
|
||||
@@ -125,6 +136,11 @@ class SettingStore extends BaseStore<SettingStore> {
|
||||
Config.set("homepage", homepage);
|
||||
};
|
||||
|
||||
setImageCompression = (imageCompression: ImageCompressionOptions) => {
|
||||
this.set({ imageCompression });
|
||||
Config.set("imageCompression", imageCompression);
|
||||
};
|
||||
|
||||
setDesktopIntegration = async (settings: DesktopIntegration) => {
|
||||
const { desktopIntegrationSettings } = this.get();
|
||||
|
||||
|
||||
@@ -17,6 +17,18 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export class FileWithURI extends File {
|
||||
uri: string;
|
||||
constructor(
|
||||
fileBits: BlobPart[],
|
||||
fileName: string,
|
||||
options?: FilePropertyBag
|
||||
) {
|
||||
super(fileBits, fileName, options);
|
||||
this.uri = URL.createObjectURL(this);
|
||||
}
|
||||
}
|
||||
|
||||
type DeriveDimension = (naturalWidth: number, naturalHeight: number) => number;
|
||||
|
||||
interface CompressorOptions {
|
||||
|
||||
@@ -39,12 +39,6 @@ import SubNotebook from "../components/sub-notebook";
|
||||
import { NotebookContext } from "../components/list-container/types";
|
||||
import { Menu } from "../hooks/use-menu";
|
||||
import Notes from "./notes";
|
||||
import {
|
||||
PanelGroup,
|
||||
Panel,
|
||||
PanelResizeHandle,
|
||||
ImperativePanelHandle
|
||||
} from "react-resizable-panels";
|
||||
import { AddNotebookDialog } from "../dialogs/add-notebook-dialog";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import { Notebook as NotebookType } from "@notesnook/core";
|
||||
@@ -54,6 +48,11 @@ import {
|
||||
VirtualizedTree,
|
||||
VirtualizedTreeHandle
|
||||
} from "../components/virtualized-tree";
|
||||
import {
|
||||
Pane,
|
||||
SplitPane,
|
||||
SplitPaneImperativeHandle
|
||||
} from "../components/split-pane";
|
||||
|
||||
type NotebookProps = {
|
||||
rootId: string;
|
||||
@@ -63,7 +62,7 @@ function Notebook(props: NotebookProps) {
|
||||
const { rootId, notebookId } = props;
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
const subNotebooksPane = useRef<ImperativePanelHandle>(null);
|
||||
const pane = useRef<SplitPaneImperativeHandle>(null);
|
||||
|
||||
const context = useNotesStore((store) => store.context);
|
||||
const notes = useNotesStore((store) => store.contextNotes);
|
||||
@@ -93,11 +92,19 @@ function Notebook(props: NotebookProps) {
|
||||
|
||||
if (!context || !notes || context.type !== "notebook") return null;
|
||||
return (
|
||||
<PanelGroup
|
||||
direction="vertical"
|
||||
<SplitPane
|
||||
ref={pane}
|
||||
direction="horizontal"
|
||||
autoSaveId={`notebook-panel-sizes:${rootId}`}
|
||||
onChange={([_, subnotebooksPane]) => {
|
||||
setIsCollapsed((isCollapsed) => {
|
||||
if (subnotebooksPane <= 34 && !isCollapsed) return true;
|
||||
else if (subnotebooksPane >= 35 && isCollapsed) return false;
|
||||
return isCollapsed;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Panel style={{ display: "flex" }}>
|
||||
<Pane id="notes-pane" style={{ display: "flex" }}>
|
||||
<Notes
|
||||
header={
|
||||
<NotebookHeader
|
||||
@@ -107,26 +114,18 @@ function Notebook(props: NotebookProps) {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Panel>
|
||||
<PanelResizeHandle className="panel-resize-handle" />
|
||||
<Panel
|
||||
ref={subNotebooksPane}
|
||||
defaultSize={25}
|
||||
collapsedSize={7}
|
||||
collapsible
|
||||
minSize={7}
|
||||
onResize={(size) => setIsCollapsed(size <= 7)}
|
||||
>
|
||||
</Pane>
|
||||
<Pane id="subnotebooks-pane" initialSize={250} minSize={30}>
|
||||
<SubNotebooks
|
||||
isCollapsed={isCollapsed}
|
||||
rootId={rootId}
|
||||
onClick={() => {
|
||||
if (isCollapsed) subNotebooksPane.current?.expand();
|
||||
else subNotebooksPane.current?.collapse();
|
||||
if (isCollapsed) pane.current?.expand(1);
|
||||
else pane.current?.collapse(1);
|
||||
}}
|
||||
/>
|
||||
</Panel>
|
||||
</PanelGroup>
|
||||
</Pane>
|
||||
</SplitPane>
|
||||
);
|
||||
}
|
||||
export default Notebook;
|
||||
|
||||
5
fastlane/metadata/android/en-US/changelogs/15164.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15164.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
- You can now share multiple files to Notesnook
|
||||
- Fix file and image sharing not working
|
||||
- Many other bug fixes and small improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
5
fastlane/metadata/android/en-US/changelogs/15169.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/15169.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
- You can now share multiple files to Notesnook
|
||||
- Fix file and image sharing not working
|
||||
- Many other bug fixes and small improvements
|
||||
|
||||
Thank you for using Notesnook!
|
||||
@@ -189,7 +189,7 @@ class Database {
|
||||
tokenManager = new TokenManager(this.kv);
|
||||
mfa = new MFAManager(this.tokenManager);
|
||||
subscriptions = new Subscriptions(this.tokenManager);
|
||||
offers = new Offers();
|
||||
offers = Offers;
|
||||
debug = new Debug();
|
||||
pricing = Pricing;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export default class Lookup {
|
||||
eb
|
||||
.selectFrom("content_fts")
|
||||
.$if(!!notes, (eb) =>
|
||||
eb.where("id", "in", notes!.filter.select("id"))
|
||||
eb.where("noteId", "in", notes!.filter.select("id"))
|
||||
)
|
||||
.$if(excludedIds.length > 0, (eb) =>
|
||||
eb.where("id", "not in", excludedIds)
|
||||
|
||||
@@ -48,6 +48,7 @@ export default class Vault {
|
||||
}
|
||||
|
||||
private startEraser() {
|
||||
EV.publish(EVENTS.vaultUnlocked);
|
||||
clearTimeout(this.erasureTimeout);
|
||||
this.erasureTimeout = setTimeout(() => {
|
||||
this.lock();
|
||||
@@ -95,7 +96,6 @@ export default class Vault {
|
||||
throw new Error(VAULT_ERRORS.wrongPassword);
|
||||
}
|
||||
this.password = password;
|
||||
EV.publish(EVENTS.vaultUnlocked);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
23
packages/editor-mobile/package-lock.json
generated
23
packages/editor-mobile/package-lock.json
generated
@@ -4441,7 +4441,7 @@
|
||||
"version": "15.7.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
|
||||
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/q": {
|
||||
"version": "1.5.8",
|
||||
@@ -4465,7 +4465,7 @@
|
||||
"version": "18.2.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.39.tgz",
|
||||
"integrity": "sha512-Oiw+ppED6IremMInLV4HXGbfbG6GyziY3kqAwJYOR0PNbkYDmLWQA3a95EhdSmamsvbkJN96ZNN+YD+fGjzSBA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "*",
|
||||
@@ -4500,7 +4500,7 @@
|
||||
"version": "0.16.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.6",
|
||||
@@ -9762,7 +9762,7 @@
|
||||
"version": "9.0.21",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
@@ -17856,6 +17856,21 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
|
||||
@@ -269,6 +269,7 @@ const Tiptap = ({
|
||||
? []
|
||||
: getTableOfContents(containerRef.current);
|
||||
},
|
||||
scrollTop: () => containerRef.current?.scrollTop || 0,
|
||||
scrollTo: (top) => {
|
||||
containerRef.current?.scrollTo({ top, behavior: "auto" });
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Editor, scrollIntoViewById } from "@notesnook/editor";
|
||||
import { strings } from "@notesnook/intl";
|
||||
import {
|
||||
ThemeDefinition,
|
||||
useThemeColors,
|
||||
@@ -41,7 +42,6 @@ import {
|
||||
import { injectCss, transform } from "../utils/css";
|
||||
import { pendingSaveRequests } from "../utils/pending-saves";
|
||||
import { useTabContext, useTabStore } from "./useTabStore";
|
||||
import { strings } from "@notesnook/intl";
|
||||
|
||||
type Attachment = {
|
||||
hash: string;
|
||||
@@ -126,11 +126,13 @@ export type EditorController = {
|
||||
export function useEditorController({
|
||||
update,
|
||||
getTableOfContents,
|
||||
scrollTo
|
||||
scrollTo,
|
||||
scrollTop
|
||||
}: {
|
||||
update: () => void;
|
||||
getTableOfContents: () => any[];
|
||||
scrollTo: (top: number) => void;
|
||||
scrollTop: () => number;
|
||||
}): EditorController {
|
||||
const passwordInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const tab = useTabContext();
|
||||
@@ -342,30 +344,28 @@ export function useEditorController({
|
||||
updateTabOnFocus.current = true;
|
||||
} else {
|
||||
if (!editor) break;
|
||||
|
||||
const noteState = tabRef.current?.noteId
|
||||
? useTabStore.getState().noteState[tabRef.current?.noteId]
|
||||
: null;
|
||||
|
||||
const top = scrollTop() || noteState?.top || 0;
|
||||
editor?.commands.setContent(htmlContentRef.current, false, {
|
||||
preserveWhitespace: true
|
||||
});
|
||||
|
||||
if (noteState) {
|
||||
if (noteState && editor.isFocused) {
|
||||
editor.commands.setTextSelection({
|
||||
from: noteState.from,
|
||||
to: noteState.to
|
||||
});
|
||||
}
|
||||
|
||||
scrollTo?.(noteState?.top || 0);
|
||||
scrollTo?.(top || 0);
|
||||
countWords(0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "native:html":
|
||||
if (htmlContentRef.current === value) break;
|
||||
htmlContentRef.current = value;
|
||||
logger("info", "LOADING NOTE HTML");
|
||||
if (!editor) break;
|
||||
|
||||
769
packages/editor/package-lock.json
generated
769
packages/editor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -35,33 +35,33 @@
|
||||
"@notesnook/theme": "file:../theme",
|
||||
"@notesnook/ui": "file:../ui",
|
||||
"@social-embed/lib": "^0.1.0-next.7",
|
||||
"@tiptap/core": "2.6.6",
|
||||
"@tiptap/extension-blockquote": "^2.6.6",
|
||||
"@tiptap/extension-bullet-list": "^2.6.6",
|
||||
"@tiptap/extension-character-count": "2.6.6",
|
||||
"@tiptap/extension-code": "^2.6.6",
|
||||
"@tiptap/extension-color": "2.6.6",
|
||||
"@tiptap/extension-font-family": "2.6.6",
|
||||
"@tiptap/extension-heading": "^2.6.6",
|
||||
"@tiptap/extension-history": "2.6.6",
|
||||
"@tiptap/extension-horizontal-rule": "2.6.6",
|
||||
"@tiptap/extension-list-item": "^2.6.6",
|
||||
"@tiptap/extension-list-keymap": "2.6.6",
|
||||
"@tiptap/extension-ordered-list": "^2.6.6",
|
||||
"@tiptap/extension-placeholder": "2.6.6",
|
||||
"@tiptap/extension-subscript": "2.6.6",
|
||||
"@tiptap/extension-superscript": "2.6.6",
|
||||
"@tiptap/extension-table": "2.6.6",
|
||||
"@tiptap/extension-table-cell": "2.6.6",
|
||||
"@tiptap/extension-table-header": "2.6.6",
|
||||
"@tiptap/extension-table-row": "2.6.6",
|
||||
"@tiptap/extension-task-item": "2.6.6",
|
||||
"@tiptap/extension-task-list": "2.6.6",
|
||||
"@tiptap/extension-text-align": "2.6.6",
|
||||
"@tiptap/extension-text-style": "2.6.6",
|
||||
"@tiptap/extension-underline": "2.6.6",
|
||||
"@tiptap/pm": "2.6.6",
|
||||
"@tiptap/starter-kit": "2.6.6",
|
||||
"@tiptap/core": "^2.10.4",
|
||||
"@tiptap/extension-blockquote": "2.10.4",
|
||||
"@tiptap/extension-bullet-list": "2.10.4",
|
||||
"@tiptap/extension-character-count": "2.10.4",
|
||||
"@tiptap/extension-code": "2.10.4",
|
||||
"@tiptap/extension-color": "2.10.4",
|
||||
"@tiptap/extension-font-family": "2.10.4",
|
||||
"@tiptap/extension-heading": "2.10.4",
|
||||
"@tiptap/extension-history": "2.10.4",
|
||||
"@tiptap/extension-horizontal-rule": "2.10.4",
|
||||
"@tiptap/extension-list-item": "2.10.4",
|
||||
"@tiptap/extension-list-keymap": "2.10.4",
|
||||
"@tiptap/extension-ordered-list": "2.10.4",
|
||||
"@tiptap/extension-placeholder": "2.10.4",
|
||||
"@tiptap/extension-subscript": "2.10.4",
|
||||
"@tiptap/extension-superscript": "2.10.4",
|
||||
"@tiptap/extension-table": "2.10.4",
|
||||
"@tiptap/extension-table-cell": "2.10.4",
|
||||
"@tiptap/extension-table-header": "2.10.4",
|
||||
"@tiptap/extension-table-row": "2.10.4",
|
||||
"@tiptap/extension-task-item": "2.10.4",
|
||||
"@tiptap/extension-task-list": "2.10.4",
|
||||
"@tiptap/extension-text-align": "2.10.4",
|
||||
"@tiptap/extension-text-style": "2.10.4",
|
||||
"@tiptap/extension-underline": "2.10.4",
|
||||
"@tiptap/pm": "2.10.4",
|
||||
"@tiptap/starter-kit": "2.10.4",
|
||||
"alfaaz": "^1.1.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"clipboard-polyfill": "4.1.0",
|
||||
@@ -73,7 +73,7 @@
|
||||
"nanoid": "^5.0.7",
|
||||
"prism-themes": "^1.9.0",
|
||||
"prosemirror-codemark": "^0.4.2",
|
||||
"prosemirror-view": "1.34.2",
|
||||
"prosemirror-view": "1.37.1",
|
||||
"re-resizable": "^6.9.18",
|
||||
"react-colorful": "^5.6.1",
|
||||
"redent": "^4.0.0",
|
||||
@@ -128,4 +128,4 @@
|
||||
"url": "git://github.com/streetwriters/notesnook.git",
|
||||
"directory": "packages/editor"
|
||||
}
|
||||
}
|
||||
}
|
||||
601
packages/editor/patches/@tiptap+core+2.10.4.patch
Normal file
601
packages/editor/patches/@tiptap+core+2.10.4.patch
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,54 +0,0 @@
|
||||
diff --git a/node_modules/@tiptap/extension-list-keymap/dist/index.cjs b/node_modules/@tiptap/extension-list-keymap/dist/index.cjs
|
||||
index 0f8ae36..adeee92 100644
|
||||
--- a/node_modules/@tiptap/extension-list-keymap/dist/index.cjs
|
||||
+++ b/node_modules/@tiptap/extension-list-keymap/dist/index.cjs
|
||||
@@ -82,7 +82,9 @@ const handleBackspace = (editor, name, parentListTypes) => {
|
||||
// the previous item is a list (orderedList or bulletList)
|
||||
// move the cursor into the list and delete the current item
|
||||
if (!core.isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
|
||||
- const { $anchor } = editor.state.selection;
|
||||
+ const { $anchor, empty } = editor.state.selection;
|
||||
+ if (!empty) return false;
|
||||
+
|
||||
const $listPos = editor.state.doc.resolve($anchor.before() - 1);
|
||||
const listDescendants = [];
|
||||
$listPos.node().descendants((node, pos) => {
|
||||
@@ -111,6 +113,11 @@ const handleBackspace = (editor, name, parentListTypes) => {
|
||||
if (!listItemPos) {
|
||||
return false;
|
||||
}
|
||||
+ // if the current position is not at the start of the list item
|
||||
+ // then join backward i.e. join within the list item
|
||||
+ if (listItemPos.$pos.parentOffset !== 0) {
|
||||
+ return editor.commands.joinBackward();
|
||||
+ }
|
||||
const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
|
||||
const prevNode = $prev.node(listItemPos.depth);
|
||||
const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
|
||||
diff --git a/node_modules/@tiptap/extension-list-keymap/dist/index.js b/node_modules/@tiptap/extension-list-keymap/dist/index.js
|
||||
index f7ab1e4..6ea03d5 100644
|
||||
--- a/node_modules/@tiptap/extension-list-keymap/dist/index.js
|
||||
+++ b/node_modules/@tiptap/extension-list-keymap/dist/index.js
|
||||
@@ -78,7 +78,9 @@ const handleBackspace = (editor, name, parentListTypes) => {
|
||||
// the previous item is a list (orderedList or bulletList)
|
||||
// move the cursor into the list and delete the current item
|
||||
if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
|
||||
- const { $anchor } = editor.state.selection;
|
||||
+ const { $anchor, empty } = editor.state.selection;
|
||||
+ if (!empty) return false;
|
||||
+
|
||||
const $listPos = editor.state.doc.resolve($anchor.before() - 1);
|
||||
const listDescendants = [];
|
||||
$listPos.node().descendants((node, pos) => {
|
||||
@@ -107,6 +109,11 @@ const handleBackspace = (editor, name, parentListTypes) => {
|
||||
if (!listItemPos) {
|
||||
return false;
|
||||
}
|
||||
+ // if the current position is not at the start of the list item
|
||||
+ // then join backward i.e. join within the list item
|
||||
+ if (listItemPos.$pos.parentOffset !== 0) {
|
||||
+ return editor.commands.joinBackward();
|
||||
+ }
|
||||
const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
|
||||
const prevNode = $prev.node(listItemPos.depth);
|
||||
const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/node_modules/prosemirror-view/dist/index.cjs b/node_modules/prosemirror-view/dist/index.cjs
|
||||
index 8ea57c7..aeda01d 100644
|
||||
index 5903e89..6a85c3d 100644
|
||||
--- a/node_modules/prosemirror-view/dist/index.cjs
|
||||
+++ b/node_modules/prosemirror-view/dist/index.cjs
|
||||
@@ -3456,7 +3456,7 @@ editHandlers.drop = function (view, _event) {
|
||||
@@ -3482,7 +3482,7 @@ editHandlers.drop = function (view, _event) {
|
||||
});
|
||||
tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)));
|
||||
}
|
||||
@@ -12,10 +12,10 @@ index 8ea57c7..aeda01d 100644
|
||||
};
|
||||
handlers.focus = function (view) {
|
||||
diff --git a/node_modules/prosemirror-view/dist/index.js b/node_modules/prosemirror-view/dist/index.js
|
||||
index 9583dc3..991bf0a 100644
|
||||
index d0c1f9d..f5b3f53 100644
|
||||
--- a/node_modules/prosemirror-view/dist/index.js
|
||||
+++ b/node_modules/prosemirror-view/dist/index.js
|
||||
@@ -3731,7 +3731,7 @@ editHandlers.drop = (view, _event) => {
|
||||
@@ -3754,7 +3754,7 @@ editHandlers.drop = (view, _event) => {
|
||||
tr.mapping.maps[tr.mapping.maps.length - 1].forEach((_from, _to, _newFrom, newTo) => end = newTo);
|
||||
tr.setSelection(selectionBetween(view, $pos, tr.doc.resolve(end)));
|
||||
}
|
||||
@@ -16,7 +16,10 @@ GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
import { mergeAttributes, Node, wrappingInputRule } from "@tiptap/core";
|
||||
import { inputRegex } from "@tiptap/extension-task-item";
|
||||
import { getParentAttributes } from "../../utils/prosemirror.js";
|
||||
import { ListItem } from "../list-item/index.js";
|
||||
|
||||
export interface CheckListOptions {
|
||||
itemTypeName: string;
|
||||
@@ -79,6 +82,45 @@ export const CheckList = Node.create<CheckListOptions>({
|
||||
};
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
const inputRule = wrappingInputRule({
|
||||
find: inputRegex,
|
||||
type: this.type,
|
||||
getAttributes: () => {
|
||||
return getParentAttributes(this.editor, true, true);
|
||||
}
|
||||
});
|
||||
const oldHandler = inputRule.handler;
|
||||
inputRule.handler = ({ state, range, match, chain, can, commands }) => {
|
||||
const $from = state.selection.$from;
|
||||
const parentNode = $from.node($from.depth - 1);
|
||||
if (parentNode.type.name !== ListItem.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tr = state.tr;
|
||||
// reset nodes before converting them to a check list.
|
||||
commands.clearNodes();
|
||||
|
||||
oldHandler({
|
||||
state,
|
||||
range: {
|
||||
from: tr.mapping.map(range.from),
|
||||
to: tr.mapping.map(range.to)
|
||||
},
|
||||
match,
|
||||
chain,
|
||||
can,
|
||||
commands
|
||||
});
|
||||
|
||||
tr.setNodeMarkup(state.tr.selection.to - 2, undefined, {
|
||||
checked: match[match.length - 1] === "x"
|
||||
});
|
||||
};
|
||||
return [inputRule];
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-Shift-9": () => this.editor.commands.toggleCheckList()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -60,6 +60,7 @@ export function ImageComponent(
|
||||
});
|
||||
|
||||
const dom = editor.view.dom.parentElement || editor.view.dom;
|
||||
|
||||
const size =
|
||||
editor.view.dom.clientWidth === 0
|
||||
? node.attrs
|
||||
@@ -266,7 +267,7 @@ export function ImageComponent(
|
||||
title={title}
|
||||
sx={{
|
||||
animation: bloburl || src ? "0.2s ease-in 0s 1 fadeIn" : "none",
|
||||
objectFit: "contain",
|
||||
objectFit: "scale-down",
|
||||
width: editor.isEditable ? "100%" : size.width,
|
||||
height: editor.isEditable ? "100%" : size.height,
|
||||
border: selected
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`paste text > with markdown link 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" href="example.com">test</a></p></div></div>"`;
|
||||
exports[`paste text > with markdown link 1`] = `"<div><div contenteditable="true" role="textbox" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" href="example.com">test</a></p></div></div>"`;
|
||||
|
||||
exports[`paste text > with multiple markdown links 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" href="example.com">test</a> some text <a target="_blank" rel="noopener noreferrer nofollow" href="example2.com">test2</a></p></div></div>"`;
|
||||
exports[`paste text > with multiple markdown links 1`] = `"<div><div contenteditable="true" role="textbox" translate="no" class="tiptap ProseMirror" tabindex="0"><p><a target="_blank" rel="noopener noreferrer nofollow" href="example.com">test</a> some text <a target="_blank" rel="noopener noreferrer nofollow" href="example2.com">test2</a></p></div></div>"`;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`hitting backspace at the start of first list item 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><p>item1</p><ul><li><p>item2</p></li></ul></div></div>"`;
|
||||
exports[`hitting backspace at the start of first list item 1`] = `"<div><div contenteditable="true" role="textbox" translate="no" class="tiptap ProseMirror" tabindex="0"><p>item1</p><ul><li><p>item2</p></li></ul></div></div>"`;
|
||||
|
||||
exports[`hitting backspace at the start of the second (or next) list item 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item1item2</p></li></ul></div></div>"`;
|
||||
exports[`hitting backspace at the start of the second (or next) list item 1`] = `"<div><div contenteditable="true" role="textbox" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item1item2</p></li></ul></div></div>"`;
|
||||
|
||||
exports[`hitting backspace at the start of the second (or next) paragraph inside the list item 1`] = `"<div><div contenteditable="true" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item 1item 2</p></li></ul></div></div>"`;
|
||||
exports[`hitting backspace at the start of the second (or next) paragraph inside the list item 1`] = `"<div><div contenteditable="true" role="textbox" translate="no" class="tiptap ProseMirror" tabindex="0"><ul><li><p>item 1item 2</p></li></ul></div></div>"`;
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
} from "./utils.js";
|
||||
import { Node as ProsemirrorNode } from "@tiptap/pm/model";
|
||||
import { TaskItemNode } from "../task-item/index.js";
|
||||
import { ListItem } from "../list-item/list-item.js";
|
||||
|
||||
type TaskListStats = { checked: number; total: number };
|
||||
export type TaskListAttributes = {
|
||||
@@ -335,6 +336,12 @@ export const TaskListNode = TaskList.extend({
|
||||
});
|
||||
const oldHandler = inputRule.handler;
|
||||
inputRule.handler = ({ state, range, match, chain, can, commands }) => {
|
||||
const $from = state.selection.$from;
|
||||
const parentNode = $from.node($from.depth - 1);
|
||||
if (parentNode.type.name === ListItem.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tr = state.tr;
|
||||
// reset nodes before converting them to a task list.
|
||||
commands.clearNodes();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user