Compare commits

...

18 Commits

Author SHA1 Message Date
01zulfi
15390ca8e6 ui: rewrap @theme-ui's flex, box, slider && add theme colors strict typecheck
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-08 12:00:01 +05:00
01zulfi
e636b71ef2 ui: rewrap @theme-ui's button component (#10063)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-08 10:24:08 +05:00
01zulfi
108bf75081 theme: add fontsize, spacing, radius variables (#10062)
* theme: add fontsize, spacing, radius variables
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>

* Apply suggestion from @thecodrr

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

* Apply suggestion from @thecodrr

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>

---------

Signed-off-by: Abdullah Atta <thecodrr@protonmail.com>
Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
2026-07-07 12:24:38 +05:00
Ammar Ahmed
aadb6e3a01 mobile: prepare v3.4.5 for release (#10060) 2026-07-07 09:30:31 +05:00
Ammar Ahmed
7c3fdab6ee Merge pull request #10053 from kashaf-ansari-dev/fix-7348-unlink-notebook-on-relink
mobile: unlink note from previous notebook in single-select mode
2026-07-07 09:25:56 +05:00
Ammar Ahmed
14f727d6e6 Merge pull request #10016 from kashaf-ansari-dev/fix-9742-duplicate-pinned-note
core: do not preserve pin state when duplicating notes
2026-07-07 09:25:23 +05:00
01zulfi
688a646181 Merge pull request #10058 from streetwriters/release/3.4.3
web: bump version to 3.4.3
2026-07-06 15:10:47 +05:00
Kashaf Ansari
7a7f0db941 mobile: use FileViewer for image sharing on Android (#10018)
Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>
2026-07-06 13:37:29 +05:00
Kashaf Ansari
96be35c105 mobile: fix long tag names overflow and display in tag items (#9950)
* mobile: fix long tag names overflow and display in tag items

Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>

* mobile: fix long title in side menu

Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>

* mobile: fix long notebook title display

Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>

* mobile: prevent tag list overlap on first render

Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>

---------

Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>
2026-07-06 13:36:31 +05:00
01zulfi
36bacce3ac web: bump version to 3.4.3
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-06 10:47:13 +05:00
kashaf-ansari-dev
c89e333c11 mobile: unlink note from previous notebook in single-select mode
Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>
2026-07-03 13:12:10 +05:00
Abdullah Atta
d521eb3bd8 desktop: regenerate package-lock (#10051) 2026-07-03 05:45:48 +05:00
Abdullah Atta
20bf090c7e desktop: fix source generation for electron (#10046)
* desktop: fix source generation for electron

* ci: upload generated-sources as build artifact
2026-07-03 05:14:11 +05:00
01zulfi
6cc9b8d1e1 web: add top margin to unlock view error text (#10030)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-02 08:45:49 +05:00
01zulfi
e931423eba web: show success toast when copying note link in context menu (#10027)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-02 08:45:41 +05:00
01zulfi
743d47e9e9 web: verify old password before creating backup during password update (#10031)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-07-02 08:45:26 +05:00
Ammar Ahmed
37c96177a7 Mobile release/3.4.4 (#10040)
* mobile: add bump-version script

* mobile: bump version to v3.4.4
2026-07-01 15:54:10 +05:00
kashaf-ansari-dev
3aa23d3215 core: do not preserve pin state when duplicating notes
Signed-off-by: kashaf-ansari-dev <kashafansari3108@gmail.com>
2026-06-25 17:33:36 +05:00
34 changed files with 3508 additions and 7513 deletions

View File

@@ -107,7 +107,7 @@ jobs:
target_commitish: ${{ github.ref }}
- name: Generate flatpak sources
if: inputs.publish-github && inputs.build-linux
if: inputs.build-linux
run: |
sudo apt-get update
sudo apt-get install -y --quiet flatpak flatpak-builder pipx
@@ -127,6 +127,13 @@ jobs:
files: ./generated-sources.json
prerelease: ${{ endsWith(steps.app_metadata.outputs.app_version, '-beta') }}
- name: Upload generated-sources.json as artifact
uses: actions/upload-artifact@v4
if: inputs.build-linux
with:
name: generated-sources
path: ./generated-sources.json
build-macos:
name: Build for macOS
needs: build

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "@notesnook/desktop",
"productName": "Notesnook",
"description": "Your private note taking space",
"version": "3.4.2",
"version": "3.4.3",
"appAppleId": "1544027013",
"private": true,
"main": "./dist/cjs/index.js",
@@ -59,7 +59,7 @@
"@types/node": "22.15.3",
"@types/yargs": "^17.0.33",
"chokidar": "^4.0.3",
"electron": "^37.0.0",
"electron": "^37.10.3",
"electron-builder": "^26.8.1",
"esbuild": "0.21.5",
"fkill": "^10.0.3",

View File

@@ -140,7 +140,7 @@ android {
if (project.hasProperty("prBuildNumber")) {
versionCode Integer.parseInt(prBuildNumber())
} else {
versionCode 3109
versionCode 3111
}
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')

View File

@@ -1,7 +1,3 @@
- Inbox API is now in beta. Learn more about it at https://help.notesnook.com/inbox-api/getting-started
- Added close all tabs button in editor
- Deeplinking support for nn:// urls
- Set default image compression mode in Settings -> Behavior
- Minor bug fixes and performance improvements
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -39,6 +39,7 @@ import Share from "react-native-share";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
import { useSettingStore } from "../../stores/use-setting-store";
import { DefaultAppStyles } from "../../utils/styles";
import FileViewer from "react-native-file-viewer";
const ImagePreview = () => {
const { colors } = useThemeColors("dialog");
@@ -87,9 +88,8 @@ const ImagePreview = () => {
return;
}
const attachment = await db.attachments.attachment(hash);
const path = `${cacheDir}/${
"NN_" + (await getFileNameWithExtension(hash, attachment?.mimeType))
}`;
const path = `${cacheDir}/${"NN_" + (await getFileNameWithExtension(hash, attachment?.mimeType))
}`;
await RNFetchBlob.fs.mv(`${cacheDir}/${uri}`, path).catch(() => {
/* empty */
});
@@ -136,17 +136,23 @@ const ImagePreview = () => {
borderWidth: 0
}}
onPress={async () => {
useSettingStore
.getState()
.setAppDidEnterBackgroundForAction(true);
await Share.open({
url: image
}).catch(() => {
/* empty */
});
useSettingStore
.getState()
.setAppDidEnterBackgroundForAction(false);
if (!image) return;
try {
if (Platform.OS === "android") {
await FileViewer.open(image.replace("file://", ""), {
showOpenWithDialog: true,
showAppsSuggestions: true,
shareFile: true
} as any);
} else {
await Share.open({
url: image
});
}
} catch (error) {
console.error(error);
}
}}
/>
<IconButton

View File

@@ -156,7 +156,8 @@ export function MenuItem({
style={{
flexDirection: "row",
alignItems: "center",
gap: DefaultAppStyles.GAP_SMALL
gap: DefaultAppStyles.GAP_SMALL,
flexShrink: 1
}}
>
{renderIcon ? (
@@ -185,6 +186,7 @@ export function MenuItem({
isFocused ? colors.selected.paragraph : colors.primary.paragraph
}
size={AppFontSize.sm}
style={{ flexShrink: 1 }}
>
{item.title}
</Paragraph>
@@ -195,6 +197,9 @@ export function MenuItem({
color={
isFocused ? colors.primary.paragraph : colors.secondary.paragraph
}
style={{
marginLeft: DefaultAppStyles.GAP_SMALL
}}
>
{menuItemCount}
</Paragraph>

View File

@@ -72,7 +72,7 @@ export const NotebookItem = ({
const notebook = item.notebook;
const isFocused = focused;
const { totalNotes, getTotalNotes } = useTotalNotes("notebook");
const updater = useRelationStore(state => state.updater);
const updater = useRelationStore((state) => state.updater);
const getTotalNotesRef = React.useRef(getTotalNotes);
getTotalNotesRef.current = getTotalNotes;
const { colors } = useThemeColors();
@@ -101,8 +101,8 @@ export const NotebookItem = ({
item.depth === 0
? undefined
: item.depth < 6
? 15 * item.depth
: 15 * 5,
? 15 * item.depth
: 15 * 5,
width: "100%",
marginTop: 2,
opacity: item.disabled ? 0.5 : 1
@@ -162,7 +162,8 @@ export const NotebookItem = ({
<View
style={{
flexDirection: "row",
alignItems: "center"
alignItems: "center",
flexShrink: 1
}}
>
<IconButton
@@ -191,8 +192,8 @@ export const NotebookItem = ({
!item.hasChildren || disableExpand
? "book-outline"
: expanded
? "chevron-down"
: "chevron-right"
? "chevron-down"
: "chevron-right"
}
/>
@@ -201,6 +202,7 @@ export const NotebookItem = ({
isFocused ? colors.selected.paragraph : colors.primary.paragraph
}
size={AppFontSize.sm}
style={{ flexShrink: 1 }}
>
{notebook?.title}
</Paragraph>
@@ -211,7 +213,8 @@ export const NotebookItem = ({
gap: DefaultAppStyles.GAP_SMALL,
flexDirection: "row",
alignItems: "center",
justifyContent: "center"
justifyContent: "center",
marginLeft: DefaultAppStyles.GAP_SMALL
}}
>
{selectionEnabled ? (

View File

@@ -56,7 +56,7 @@ const TagItem = (props: {
const totalNotes = useTotalNotes("tag");
const totalNotesRef = React.useRef(totalNotes);
totalNotesRef.current = totalNotes;
const updater = useRelationStore(state => state.updater);
const updater = useRelationStore((state) => state.updater);
useEffect(() => {
if (item?.id) {
@@ -67,6 +67,8 @@ const TagItem = (props: {
return (
<View
style={{
minHeight: 35,
justifyContent: "center",
paddingHorizontal: DefaultAppStyles.GAP,
marginTop:
(props.id as number) === 0 ? DefaultAppStyles.GAP_VERTICAL : 2
@@ -123,7 +125,8 @@ const TagItem = (props: {
<View
style={{
flexDirection: "row",
alignItems: "center"
alignItems: "center",
flexShrink: 1
}}
>
<View
@@ -146,6 +149,9 @@ const TagItem = (props: {
isFocused ? colors.selected.paragraph : colors.primary.paragraph
}
size={AppFontSize.sm}
style={{
flexShrink: 1
}}
>
{item?.title}
</Paragraph>
@@ -173,6 +179,9 @@ const TagItem = (props: {
<Paragraph
size={AppFontSize.xxs}
color={colors.secondary.paragraph}
style={{
marginLeft: DefaultAppStyles.GAP_SMALL
}}
>
{totalNotes.totalNotes(item?.id)}
</Paragraph>

View File

@@ -163,7 +163,20 @@ const LinkNotebooks = (props: NavigationProps<"LinkNotebooks">) => {
}, [noteIds]);
const onSave = async () => {
const changedNotebooks = useNotebookSelectionStore.getState().selection;
const selectionState = useNotebookSelectionStore.getState();
const changedNotebooks = { ...selectionState.selection };
if (!selectionState.multiSelect) {
for (const id in selectionState.initialState) {
if (
selectionState.initialState[id] === "selected" &&
changedNotebooks[id] !== "selected"
) {
changedNotebooks[id] = "deselected";
}
}
}
for (const id in changedNotebooks) {
const item = await db.notebooks.notebook(id);
if (!item) continue;
@@ -207,11 +220,11 @@ const LinkNotebooks = (props: NavigationProps<"LinkNotebooks">) => {
rightButton={
hasSelection
? {
name: "restore",
onPress: () => {
updateInitialSelectionState(noteIds);
}
name: "restore",
onPress: () => {
updateInitialSelectionState(noteIds);
}
}
: undefined
}
/>

View File

@@ -398,7 +398,8 @@ const TagItem = ({
flexDirection: "row",
marginVertical: 5,
justifyContent: "flex-start",
height: 40
minHeight: 40,
alignItems: "center"
}}
onPress={() => {
if (!tag) return;
@@ -438,7 +439,9 @@ const TagItem = ({
/>
)}
{tag ? (
<Paragraph size={AppFontSize.sm}>{"#" + tag?.title}</Paragraph>
<Paragraph style={{ flexShrink: 1 }} size={AppFontSize.sm}>
{"#" + tag?.title}
</Paragraph>
) : (
<View
style={{

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2187
IOS_MARKETING_VERSION = 3.4.3
IOS_CURRENT_PROJECT_VERSION = 2189
IOS_MARKETING_VERSION = 3.4.5
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2187
IOS_MARKETING_VERSION = 3.4.3
IOS_CURRENT_PROJECT_VERSION = 2189
IOS_MARKETING_VERSION = 3.4.5
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
// Staging iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2187
IOS_MARKETING_VERSION = 3.4.3
IOS_CURRENT_PROJECT_VERSION = 2189
IOS_MARKETING_VERSION = 3.4.5
IOS_MAIN_BUNDLE_ID = org.streetwriters.notesnook
IOS_WIDGET_BUNDLE_ID = org.streetwriters.notesnook.notewidget
IOS_SHARE_BUNDLE_ID = org.streetwriters.notesnook.share

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.4.3",
"version": "3.4.5",
"private": true,
"license": "GPL-3.0-or-later",
"scripts": {
@@ -18,7 +18,7 @@
"e2e-android": "detox test --configuration android.emu.release --detectOpenHandles",
"e2e-android-debug": "detox test --configuration android.emu.debug --detectOpenHandles",
"e2e-ios": "detox test -c ios.sim.release --detectOpenHandles",
"bump": "npx react-native bump-version --skip-semver-for android",
"bump": "node ./scripts/bump-version.mjs",
"release-android": "cd android && GITHUB_RELEASE=true ENVFILE=.env.public ./gradlew assembleRelease --no-daemon",
"release-android-bundle": "cd android && ./gradlew bundleRelease --no-daemon",
"lint": "eslint --ext .js,.jsx,.tsx,.ts ./app"

View File

@@ -0,0 +1,194 @@
/*
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 { promises as fs } from "fs";
import path from "path";
const PROJECT_ROOT = path.resolve(
new URL(import.meta.url).pathname,
"..",
".."
); // scripts/.. -> mobile
const REPO_ROOT = path.resolve(PROJECT_ROOT, "..", ".."); // mobile/../.. -> repo root
const IOS_BUILD_CONFIGS_DIR = path.resolve(PROJECT_ROOT, "ios/build-configs");
const ANDROID_GRADLE = path.resolve(PROJECT_ROOT, "android/app/build.gradle");
const PACKAGE_JSON = path.resolve(PROJECT_ROOT, "package.json");
const FDROID_CHANGELOGS_DIR = path.resolve(
REPO_ROOT,
"fastlane/metadata/android/en-US/changelogs"
);
async function resolveVersion() {
const provided = process.argv[2];
if (provided) {
if (!/^\d+\.\d+\.\d+$/.test(provided)) {
console.error(`Invalid version "${provided}". Expected format: x.y.z`);
process.exit(1);
}
return provided;
}
// No version provided: bump the current package.json version as a patch.
const pkg = JSON.parse(await fs.readFile(PACKAGE_JSON, "utf8"));
const match = String(pkg.version).match(/^(\d+)\.(\d+)\.(\d+)$/);
if (!match) {
console.error(
`Cannot auto-bump: package.json version "${pkg.version}" is not x.y.z`
);
process.exit(1);
}
const [, major, minor, patch] = match;
return `${major}.${minor}.${Number(patch) + 1}`;
}
const version = await resolveVersion();
async function updatePackageJson() {
const raw = await fs.readFile(PACKAGE_JSON, "utf8");
const pkg = JSON.parse(raw);
const from = pkg.version;
pkg.version = version;
// Preserve trailing newline if present.
const trailingNewline = raw.endsWith("\n") ? "\n" : "";
await fs.writeFile(
PACKAGE_JSON,
JSON.stringify(pkg, null, 2) + trailingNewline
);
console.log(`package.json: version ${from} -> ${version}`);
}
async function updateAndroid() {
let content = await fs.readFile(ANDROID_GRADLE, "utf8");
const match = content.match(/versionCode\s+(\d+)/);
if (!match) {
throw new Error("Could not find `versionCode <number>` in build.gradle");
}
const current = parseInt(match[1], 10);
const next = current + 1;
content = content.replace(match[0], `versionCode ${next}`);
await fs.writeFile(ANDROID_GRADLE, content);
console.log(`android/app/build.gradle: versionCode ${current} -> ${next}`);
return next;
}
// Derives the F-Droid release version code from build.gradle. F-Droid builds a
// separate APK per ABI (see `applicationVariants.all` in build.gradle) using:
// output.versionCodeOverride = defaultConfig.versionCode * <mult> + versionCodes[abi]
// and publishes the highest of those. So the changelog file is named after
// `versionCode * <mult> + max(versionCodes)`. Both are parsed here rather than
// hardcoded so this stays in sync with the formula in build.gradle.
async function computeFdroidVersionCode(versionCode) {
const gradle = await fs.readFile(ANDROID_GRADLE, "utf8");
const multMatch = gradle.match(/defaultConfig\.versionCode\s*\*\s*(\d+)/);
if (!multMatch) {
throw new Error(
"Could not find the F-Droid versionCode multiplier in build.gradle"
);
}
const multiplier = parseInt(multMatch[1], 10);
const mapMatch = gradle.match(/versionCodes\s*=\s*\[([^\]]*)\]/);
if (!mapMatch) {
throw new Error("Could not find the ABI versionCodes map in build.gradle");
}
const abiCodes = [...mapMatch[1].matchAll(/:\s*(\d+)/g)].map((m) =>
parseInt(m[1], 10)
);
if (abiCodes.length === 0) {
throw new Error("ABI versionCodes map in build.gradle is empty");
}
const maxAbiCode = Math.max(...abiCodes);
return versionCode * multiplier + maxAbiCode;
}
async function createFdroidChangelog(versionCode) {
const fdroidVersionCode = await computeFdroidVersionCode(versionCode);
const file = path.resolve(FDROID_CHANGELOGS_DIR, `${fdroidVersionCode}.txt`);
const rel = path.relative(REPO_ROOT, file);
try {
await fs.access(file);
console.log(`${rel}: already exists, skipped`);
return;
} catch {
// File does not exist yet — create it.
}
const placeholder = "- Minor bug fixes and improvements\n\nThank you for using Notesnook!\n";
await fs.mkdir(FDROID_CHANGELOGS_DIR, { recursive: true });
await fs.writeFile(file, placeholder);
console.log(`${rel}: created (edit with this release's changelog)`);
}
async function updateIosConfig(file) {
let content = await fs.readFile(file, "utf8");
const rel = path.relative(PROJECT_ROOT, file);
const buildMatch = content.match(/IOS_CURRENT_PROJECT_VERSION\s*=\s*(\d+)/);
if (!buildMatch) {
throw new Error(`Could not find IOS_CURRENT_PROJECT_VERSION in ${rel}`);
}
const currentBuild = parseInt(buildMatch[1], 10);
const nextBuild = currentBuild + 1;
content = content.replace(
buildMatch[0],
`IOS_CURRENT_PROJECT_VERSION = ${nextBuild}`
);
const marketingMatch = content.match(/IOS_MARKETING_VERSION\s*=\s*(\S+)/);
if (!marketingMatch) {
throw new Error(`Could not find IOS_MARKETING_VERSION in ${rel}`);
}
const currentMarketing = marketingMatch[1];
content = content.replace(
marketingMatch[0],
`IOS_MARKETING_VERSION = ${version}`
);
await fs.writeFile(file, content);
console.log(
`${rel}: version ${currentMarketing} -> ${version}, build ${currentBuild} -> ${nextBuild}`
);
}
async function updateIos() {
const entries = await fs.readdir(IOS_BUILD_CONFIGS_DIR);
const configs = entries.filter((e) => e.endsWith(".xcconfig"));
if (configs.length === 0) {
throw new Error("No .xcconfig files found in ios/build-configs");
}
for (const config of configs) {
await updateIosConfig(path.resolve(IOS_BUILD_CONFIGS_DIR, config));
}
}
async function run() {
console.log(`Bumping to version ${version}...\n`);
await updatePackageJson();
const androidVersionCode = await updateAndroid();
await updateIos();
await createFdroidChangelog(androidVersionCode);
console.log("\nDone.");
}
run().catch((err) => {
console.error("Failed:", err?.message || err);
process.exitCode = 1;
});

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/web",
"version": "3.4.2",
"version": "3.4.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/web",
"version": "3.4.2",
"version": "3.4.3",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "@notesnook/web",
"description": "Your private note taking space",
"version": "3.4.2",
"version": "3.4.3",
"private": true,
"main": "./src/app.js",
"homepage": "https://notesnook.com/",

View File

@@ -496,6 +496,7 @@ export const noteMenuItems: (
"text/html": `<a href="${url}">${note.title}</a>`,
"text/markdown": `[${note.title}](${url})`
});
showToast("success", strings.linkCopied());
}
},
{
@@ -594,6 +595,7 @@ export const noteMenuItems: (
"text/html": `<a href="${link}">${note.title}</a>`,
"text/markdown": `[${note.title}](${link})`
});
showToast("success", strings.linkCopied());
}
},
{

View File

@@ -115,7 +115,7 @@ export function UnlockView(props: UnlockViewProps) {
}
}}
/>
{isWrong && <ErrorText error="Wrong password" />}
{isWrong && <ErrorText sx={{ mt: 1 }} error="Wrong password" />}
<Button
mt={3}
variant="accent"

View File

@@ -97,34 +97,7 @@ const features: Record<FeatureKeys, Feature> = {
)
}
]
: [
{
icon: Inbox,
title: "Inbox API",
subtitle: "A new API to send data to Notesnook from other apps."
},
{
icon: LinkedTo,
title: "Deep links on Desktop",
subtitle: "Reference Notesnook notes from other apps."
},
{
icon: Plus,
title: "More features, bug fixes, and security improvments",
subtitle: (
<>
This release contains much much more.{" "}
<Link
href="https://blog.notesnook.com/notesnook-desktop-v3.4.0"
target="_blank"
title="Read the full release notes"
>
Read the full release notes
</Link>
</>
)
}
],
: [],
cta: {
title: strings.gotIt(),
icon: Checkmark,

View File

@@ -59,6 +59,9 @@ export const AuthenticationSettings: SettingsGroup[] = [
}
},
validate: async ({ oldPassword, newPassword }) => {
const verify = await db.user.verifyPassword(oldPassword);
if (!verify) return false;
try {
if (!(await createBackup({ noVerify: true }))) return false;
return (

View File

@@ -0,0 +1,7 @@
- Inbox API is now in beta. Learn more about it at https://help.notesnook.com/inbox-api/getting-started
- Added close all tabs button in editor
- Deeplinking support for nn:// urls
- Set default image compression mode in Settings -> Behavior
- Minor bug fixes and performance improvements
Thank you for using Notesnook!

View File

@@ -0,0 +1,3 @@
- Minor bug fixes and improvements
Thank you for using Notesnook!

View File

@@ -437,6 +437,7 @@ export class Notes implements ICollection {
const duplicateId = await this.db.notes.add({
...clone(note),
id: undefined,
pinned: false,
isGeneratedTitle: false,
contentId: undefined,
title: note.title + " (Copy)",

View File

@@ -27,7 +27,17 @@ export function getFontSizes(): FontSizes {
body: "12px",
subBody: "10px",
menu: "12px",
code: "14px"
code: "14px",
xxxs: "10px",
xxs: "11px",
xs: "13px",
sm: "15px",
md: "17px",
lg: "20px",
xl: "25px",
"2xl": "30px",
"3xl": "35px",
"4xl": "40px"
};
}
@@ -41,4 +51,14 @@ export type FontSizes = {
menu: string;
subBody: string;
code: string;
xxxs: string;
xxs: string;
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
"2xl": string;
"3xl": string;
"4xl": string;
};

View File

@@ -27,7 +27,22 @@ export { createButtonVariant } from "./variants/button.js";
export { getFontConfig } from "./font/index.js";
export type Theme = {
breakpoints: string[];
space: (number | string)[] & { small?: number | string };
space: (number | string)[] & {
small?: number | string;
spacing1: number;
spacing2: number;
spacing3: number;
spacing4: number;
spacing5: number;
spacing6: number;
spacing7: number;
spacing8: number;
spacing9: number;
spacing10: number;
spacing11: number;
spacing12: number;
spacing13: number;
};
sizes: { full: "100%"; half: "50%" };
radii: {
none: number;
@@ -35,6 +50,15 @@ export type Theme = {
dialog: number;
large: number;
small: number;
radius1: number;
radius2: number;
radius3: number;
radius4: number;
radius5: number;
radius6: number;
radius7: number;
radius8: number;
radius9: number;
};
shadows: { menu: string };
colors: Record<ThemeColor, string>;
@@ -51,9 +75,38 @@ export class ThemeFactory {
static construct(config: ThemeConfig): Theme {
const theme: Theme = {
breakpoints: ["480px", "1000px", "1000px"],
space: [0, "6px", 10, 15, 20, 25, 30, 35],
space: Object.assign([0, "6px", 10, 15, 20, 25, 30, 35], {
spacing1: 3,
spacing2: 5,
spacing3: 7,
spacing4: 10,
spacing5: 13,
spacing6: 15,
spacing7: 20,
spacing8: 25,
spacing9: 30,
spacing10: 35,
spacing11: 40,
spacing12: 45,
spacing13: 50
}),
sizes: { full: "100%", half: "50%" },
radii: { none: 0, default: 5, large: 7, dialog: 10, small: 2.5 },
radii: {
none: 0,
default: 5,
large: 7,
dialog: 10,
small: 2.5,
radius1: 5,
radius2: 10,
radius3: 15,
radius4: 20,
radius5: 25,
radius6: 30,
radius7: 35,
radius8: 40,
radius9: 45
},
iconSizes: { big: 16, medium: 14, small: 12 },
colors: flattenVariants(config.scope),
shadows:

View File

@@ -0,0 +1,45 @@
/*
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 {
Box as ThemeUIBox,
BoxProps as ThemeUIBoxProps
} from "@theme-ui/components";
import { Theme } from "@notesnook/theme";
import {
RestrictedColorProps,
RestrictedSpaceProps,
RestrictedSxProp
} from "../utils/types.js";
export interface BoxProps
extends Omit<
ThemeUIBoxProps,
"variant" | "sx" | keyof RestrictedSpaceProps | keyof RestrictedColorProps
>,
RestrictedSpaceProps,
RestrictedColorProps {
variant?: keyof Theme["variants"];
sx?: RestrictedSxProp;
}
export const Box = React.forwardRef<HTMLDivElement, BoxProps>((props, ref) => {
return <ThemeUIBox ref={ref} {...props} />;
});

View File

@@ -0,0 +1,47 @@
/*
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 {
Button as ThemeUIButton,
ButtonProps as ThemeUIButtonProps
} from "@theme-ui/components";
import { Theme } from "@notesnook/theme";
import {
RestrictedColorProps,
RestrictedSpaceProps,
RestrictedSxProp
} from "../utils/types.js";
export interface ButtonProps
extends Omit<
ThemeUIButtonProps,
"variant" | "sx" | keyof RestrictedSpaceProps | keyof RestrictedColorProps
>,
RestrictedSpaceProps,
RestrictedColorProps {
variant?: keyof Theme["buttons"];
sx?: RestrictedSxProp;
}
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
return <ThemeUIButton ref={ref} {...props} />;
}
);

View File

@@ -0,0 +1,47 @@
/*
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 {
Flex as ThemeUIFlex,
FlexProps as ThemeUIFlexProps
} from "@theme-ui/components";
import { Theme } from "@notesnook/theme";
import {
RestrictedColorProps,
RestrictedSpaceProps,
RestrictedSxProp
} from "../utils/types.js";
export interface FlexProps
extends Omit<
ThemeUIFlexProps,
"variant" | "sx" | keyof RestrictedSpaceProps | keyof RestrictedColorProps
>,
RestrictedSpaceProps,
RestrictedColorProps {
variant?: keyof Theme["variants"];
sx?: RestrictedSxProp;
}
export const Flex = React.forwardRef<HTMLDivElement, FlexProps>(
(props, ref) => {
return <ThemeUIFlex ref={ref} {...props} />;
}
);

View File

@@ -21,3 +21,7 @@ export * from "./icon/index.js";
export * from "./menu/index.js";
export * from "./popup-presenter/index.js";
export * from "./scroll-container/index.js";
export * from "./button.js";
export * from "./box.js";
export * from "./flex.js";
export * from "./slider.js";

View File

@@ -0,0 +1,47 @@
/*
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 {
Slider as ThemeUISlider,
SliderProps as ThemeUISliderProps
} from "@theme-ui/components";
import { Theme } from "@notesnook/theme";
import {
RestrictedColorProps,
RestrictedSpaceProps,
RestrictedSxProp
} from "../utils/types.js";
export interface SliderProps
extends Omit<
ThemeUISliderProps,
"variant" | "sx" | keyof RestrictedSpaceProps | keyof RestrictedColorProps
>,
RestrictedSpaceProps,
RestrictedColorProps {
variant?: keyof Theme["forms"];
sx?: RestrictedSxProp;
}
export const Slider = React.forwardRef<HTMLInputElement, SliderProps>(
(props, ref) => {
return <ThemeUISlider ref={ref} {...(props as any)} />;
}
);

View File

@@ -18,3 +18,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export * from "./position.js";
export * from "./types.js";

View File

@@ -0,0 +1,73 @@
/*
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 { Theme, ThemeColor } from "@notesnook/theme";
import { ThemeUIStyleObject } from "@theme-ui/core";
type ThemeSpace = Theme["space"];
type ValidSpaceValues =
| Exclude<keyof ThemeSpace, keyof Array<any> | "small">
| "small"
| number;
export type RestrictedColorProps = {
color?: ThemeColor;
bg?: ThemeColor | (ThemeColor | null)[];
backgroundColor?: ThemeColor | (ThemeColor | null)[];
};
export type RestrictedSpaceProps = {
[K in
| "m"
| "mt"
| "mr"
| "mb"
| "ml"
| "mx"
| "my"
| "margin"
| "marginTop"
| "marginRight"
| "marginBottom"
| "marginLeft"
| "marginX"
| "marginY"
| "p"
| "pt"
| "pr"
| "pb"
| "pl"
| "px"
| "py"
| "padding"
| "paddingTop"
| "paddingRight"
| "paddingBottom"
| "paddingLeft"
| "paddingX"
| "paddingY"]?: ValidSpaceValues | (ValidSpaceValues | null)[];
};
export type RestrictedSxProp = Omit<
ThemeUIStyleObject,
keyof RestrictedSpaceProps | keyof RestrictedColorProps
> &
RestrictedSpaceProps &
RestrictedColorProps;