Compare commits

...

18 Commits

Author SHA1 Message Date
Ammar Ahmed
04f6e1068b mobile: fix saving incoming note in merge conflict dialog does not work 2026-04-23 10:43:07 +05:00
01zulfi
9bea4adcf9 web: show titlebar on auth pages (#9093)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-22 13:51:21 +05:00
Chloe Oletto
a5790de23c desktop: Stop urgent reminder causes segfault & app freeze on Linux. (#9737)
* desktop: Stop urgent reminder causes segfault & app freeze on Linux.

Signed-off by: Chloe Oletto <NeedsChloesure@riseup.net>

* desktop: Linux segfault
Signed-off-by: Chloe Oletto <NeedsChloesure@riseup.net>

* 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 <thecodrr@protonmail.com>
2026-04-22 11:56:47 +05:00
Abdullah Atta
eb4edeea8f web: fix crash on non-paddle related iframe event (#9747)
Stack trace:
```
undefined is not an object (evaluating 'w.data')
E@https://app.notesnook.com/assets/paddle-Z43isEce-h-M.js:23:45428
E@https://app.notesnook.com/assets/paddle-Z43isEce-h-M.js:23:45846
```
2026-04-22 09:46:29 +05:00
01zulfi
e3ccd504da ui: fix submenu cutting off when there are a lot of lazy items (#9739)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-22 09:11:28 +05:00
Ammar Ahmed
4627d7d5df Merge pull request #9736 from streetwriters/mobile/release-3.3.20
mobile: release 3.3.20
2026-04-22 08:46:43 +05:00
01zulfi
f216c1b65d web: fix scrollbar blocking date in compact note list item (#9719)
* web: fix scrollbar blocking date in compact note list item
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>

* web: adjust notes list scrollbar width, compact item padding, & group header padding
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-21 11:08:30 +05:00
Abdullah Atta
4692ba7183 desktop: use newer appimage bundler (#9744)
this should fix libfuse and libz errors
2026-04-21 10:55:52 +05:00
Ammar Ahmed
543825918c mobile: fix useEffect deps in use-feature-manager hook 2026-04-21 08:46:19 +05:00
Ammar Ahmed
9f995b861b mobile: fix toast position incorrect with keyboard opened 2026-04-21 08:45:45 +05:00
Ammar Ahmed
9963a6c655 mobile: delete vault action keeps loading when entering incorrect password 2026-04-21 08:45:29 +05:00
Ammar Ahmed
1696ec5c72 mobile: fix unable to switch between yearly/monthly plans 2026-04-21 08:14:38 +05:00
Ammar Ahmed
7c95faad21 mobile: fix clear trash option is disabled on login 2026-04-20 15:43:39 +05:00
Ammar Ahmed
ca80441c14 mobile: fix trash count does not update on calling clear trash 2026-04-20 15:30:26 +05:00
01zulfi
9c497fd2bb Merge pull request #9724 from streetwriters/release/3.3.15
web: bump version to 3.3.15
2026-04-20 12:21:46 +05:00
Ammar Ahmed
ef5e5e5b4c mobile: release 3.3.20 2026-04-20 12:03:12 +05:00
Abdullah Atta
ede900fdbd ci: add linux arm64 preview builds (#9735) 2026-04-20 11:56:10 +05:00
01zulfi
1758f03f93 intl: regen locale files (#9734)
Signed-off-by: 01zulfi <85733202+01zulfi@users.noreply.github.com>
2026-04-20 11:39:13 +05:00
34 changed files with 1554 additions and 1139 deletions

View File

@@ -101,7 +101,6 @@ jobs:
- name: Build dmg
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CSC_LINK: ${{ secrets.mac_certs }}
CSC_KEY_PASSWORD: ${{ secrets.mac_certs_password }}
APPLE_API_KEY: ~/private_keys/AuthKey_${{ secrets.api_key_id }}.p8
@@ -153,8 +152,6 @@ jobs:
run: npm run tx @notesnook/desktop:release
- name: Build AppImage
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn electron-builder --config=electron-builder.config.js --linux AppImage:x64 -p never
working-directory: ./apps/desktop
@@ -165,6 +162,49 @@ jobs:
name: linux-x64-build
path: apps/desktop/output/*.AppImage
build-linux-arm64:
name: Build for Linux arm64
needs: build
runs-on: ubuntu-22.04-arm
outputs:
linux-arm64-artifact-url: ${{ steps.artifact-upload-step.outputs.artifact-url }}
steps:
- name: Check out Git repository
uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node-with-cache
- name: Download build
uses: actions/download-artifact@v4
with:
name: build
path: ./apps/web/build
- name: Install packages
run: |
npm ci --ignore-scripts --prefer-offline --no-audit
npm run bootstrap -- --scope=desktop
- name: Install sqlite-better-trigram for all arch
run: |
npm i --cpu arm64 sqlite-better-trigram
working-directory: ./apps/desktop
- name: Build Electron wrapper
run: npm run tx @notesnook/desktop:release
- name: Build AppImage
run: yarn electron-builder --config=electron-builder.config.js --linux AppImage:arm64 -p never
working-directory: ./apps/desktop
- name: Upload AppImage artifact
id: artifact-upload-step
uses: actions/upload-artifact@v4
with:
name: linux-arm64-build
path: apps/desktop/output/*.AppImage
build-windows:
name: Build for Windows
needs: build
@@ -216,7 +256,8 @@ jobs:
post-pr-comment:
name: Post PR comment with preview URLs
needs: [build, build-macos, build-linux-x64, build-windows]
needs:
[build, build-macos, build-linux-x64, build-linux-arm64, build-windows]
runs-on: ubuntu-latest
steps:
- name: Post or update PR comment
@@ -224,6 +265,7 @@ jobs:
env:
macos_artifact_url: ${{ needs.build-macos.outputs.macos-artifact-url }}
linux_x64_artifact_url: ${{ needs.build-linux-x64.outputs.linux-x64-artifact-url }}
linux_arm64_artifact_url: ${{ needs.build-linux-arm64.outputs.linux-arm64-artifact-url }}
windows_artifact_url: ${{ needs.build-windows.outputs.windows-artifact-url }}
with:
script: |
@@ -232,6 +274,7 @@ jobs:
const previewUrl = [
{ platform: 'macOS', url: process.env.macos_artifact_url },
{ platform: 'Linux x64', url: process.env.linux_x64_artifact_url },
{ platform: 'Linux arm64', url: process.env.linux_arm64_artifact_url },
{ platform: 'Windows x64', url: process.env.windows_artifact_url },
].filter(u => u.url).map(u => `- [${u.platform}](${u.url})`).join('\n');
const body = `${marker}\n**Desktop Previews**\n\n${previewUrl || 'Preview URL unavailable — check workflow logs.'}\n\nCommit: ${process.env.GITHUB_SHA}\n`;

View File

@@ -198,6 +198,9 @@ module.exports = {
}
}
},
toolsets: {
appimage: "1.0.2"
},
snap: {
autoStart: false,
confinement: "strict",

File diff suppressed because it is too large Load Diff

View File

@@ -48,7 +48,7 @@
"@types/yargs": "^17.0.33",
"chokidar": "^4.0.3",
"electron": "^37.0.0",
"electron-builder": "^26.0.12",
"electron-builder": "^26.8.1",
"esbuild": "0.21.5",
"node-abi": "^4.5.0",
"node-gyp-build": "^4.8.4",

View File

@@ -178,7 +178,9 @@ export const osIntegrationRouter = t.router({
})
});
notification.show();
if (input.urgency === "critical") {
if (input.urgency === "critical" && process.platform !== "linux") {
// due to an Electron bug in versions below 40.x, shell.beep() causes a segfault on Linux.
// TODO: Remove when migrating to Electron 40.x or newer.
shell.beep();
}

View File

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

View File

@@ -1,8 +1,3 @@
- Fix clearing note title adds generated title instead of staying empty
- Improved table cell selection, table scrolling, and table cell editing on mobile
- Fixed app on launch randomly focuses editor instead of notes list
- Fixed file size check shows error but continues adding the file to editor
- Note title headline format should get content from first paragraph
- Minor bug fixes and performance improvements
- Bug fixes and improvements
Thank you for using Notesnook!
Thank you for using Notesnook!

View File

@@ -194,6 +194,7 @@ export const VaultDialog: React.FC = () => {
close();
}, 100);
} else {
setLoading(false);
ToastManager.show({
heading: strings.passwordIncorrect(),
type: "error",
@@ -227,6 +228,10 @@ export const VaultDialog: React.FC = () => {
setLoading(false);
close();
eSendEvent("vaultUpdated");
ToastManager.show({
message: strings.vaultCleared(),
type: "success"
});
} catch (e) {
ToastManager.show({
heading: strings.passwordIncorrect(),
@@ -741,7 +746,8 @@ export const VaultDialog: React.FC = () => {
width: DDS.isTab ? 350 : "85%",
borderRadius: 10,
backgroundColor: colors.primary.background,
paddingTop: 12
paddingTop: 12,
overflow: "hidden"
}}
>
<DialogHeader
@@ -808,7 +814,8 @@ export const VaultDialog: React.FC = () => {
!isBiometryAvailable ||
isCreateVault ||
isChangePassword ||
isCustomAction ? null : (
isCustomAction ||
isDeleteVault ? null : (
<Button
onPress={() =>
onPressFingerprintAuth(strings.unlockNote(), "")

View File

@@ -76,7 +76,9 @@ const MergeConflicts = () => {
const applyChanges = async () => {
const contentToSave = selectedContent;
if (!contentToSave) return;
const note = await db.notes.note(contentToSave.noteId);
const note = await db.notes.note(
(content.current as UnencryptedContentItem).noteId
);
if (!note) return;
await db.notes.add({
id: note.id,

View File

@@ -18,13 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getFeaturesTable } from "@notesnook/common";
import {
EVENTS,
Plan,
SKUResponse,
SubscriptionPlan,
User
} from "@notesnook/core";
import { EVENTS, Plan, SubscriptionPlan, User } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React, { useEffect, useState } from "react";
@@ -54,6 +48,7 @@ import {
TECHLORE_SVG,
XDA_SVG
} from "../../assets/images/assets";
import { db } from "../../common/database";
import { useNavigationFocus } from "../../hooks/use-navigation-focus";
import usePricingPlans, {
PlanOverView,
@@ -75,7 +70,6 @@ import { IconButton } from "../ui/icon-button";
import { SvgView } from "../ui/svg";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { db } from "../../common/database";
const Steps = {
select: 1,
@@ -935,7 +929,10 @@ const PricingPlanCard = ({
setStep: (step: number) => void;
}) => {
const { colors } = useThemeColors();
const [regionalDiscount, setRegionaDiscount] = useState<SKUResponse>();
const regionalDiscount =
annualBilling && plan.id === "pro"
? pricingPlans?.regionalDiscount
: undefined;
const { width } = useWindowDimensions();
const isTablet = width > 600;
@@ -958,26 +955,6 @@ const PricingPlanCard = ({
annualBilling
);
useEffect(() => {
if (pricingPlans?.isGithubRelease || !annualBilling) return;
pricingPlans
?.getRegionalDiscount(
plan.id,
pricingPlans.isGithubRelease
? (WebPlan?.period as string)
: `notesnook.${plan.id}.${annualBilling ? "yearly" : "monthly"}`
)
.then((value) => {
setRegionaDiscount(value);
});
}, [WebPlan?.period, annualBilling, plan.id, pricingPlans]);
useEffect(() => {
if (!annualBilling) {
setRegionaDiscount(undefined);
}
}, [annualBilling]);
const isSubscribed =
product?.productId &&
pricingPlans?.user?.subscription?.productId?.includes(plan.id) &&

View File

@@ -16,11 +16,11 @@ 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 { Plan, SKUResponse } from "@notesnook/core";
import { Plan } from "@notesnook/core";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import dayjs from "dayjs";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import {
Linking,
Platform,
@@ -330,7 +330,10 @@ const ProductItem = (props: {
productId: string;
}) => {
const { colors } = useThemeColors();
const [regionalDiscount, setRegionaDiscount] = useState<SKUResponse>();
const regionalDiscount =
props.productId === "notesnook.pro.yearly"
? props.pricingPlans.regionalDiscount
: undefined;
const product =
props.pricingPlans?.currentPlan?.subscriptions?.[
regionalDiscount?.sku || props.productId
@@ -367,30 +370,6 @@ const ProductItem = (props: {
props.pricingPlans.user?.subscription?.productId ===
(product as Plan)?.id);
useEffect(() => {
if (product) {
props.pricingPlans
?.getRegionalDiscount(
props.pricingPlans.currentPlan?.id as string,
props.pricingPlans.isGithubRelease
? ((product as Plan)?.period as string)
: props.productId
)
.then((value) => {
if (
value &&
value.sku?.startsWith(
(props.pricingPlans.selectedProduct as RNIap.Subscription)
?.productId
)
) {
props.pricingPlans.selectProduct(value?.sku as string);
}
setRegionaDiscount(value);
});
}
}, [product, props.pricingPlans, props.productId]);
const discountValue =
(isAnnual && !isGithubRelease) ||
(isGithubRelease && (product as Plan)?.discount?.amount)

View File

@@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { useAreFeaturesAvailable } from "@notesnook/common";
import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import React, { useEffect } from "react";
import { View } from "react-native";
import {
eSendEvent,
@@ -28,7 +28,11 @@ import {
ToastManager
} from "../../../services/event-manager";
import SettingsService from "../../../services/settings";
import { eAfterSync, eCloseSheet } from "../../../utils/events";
import {
eAfterSync,
eCloseSheet,
eMenuItemUpdate
} from "../../../utils/events";
import { SideMenuItem } from "../../../utils/menu-items";
import { AppFontSize } from "../../../utils/size";
import { DefaultAppStyles } from "../../../utils/styles";
@@ -40,14 +44,22 @@ import PaywallSheet from "../paywall";
import { presentDialog } from "../../dialog/functions";
import { db } from "../../../common/database";
import { useTrashStore } from "../../../stores/use-trash-store";
import { useSettingStore } from "../../../stores/use-setting-store";
export const MenuItemProperties = ({ item }: { item: SideMenuItem }) => {
const { colors } = useThemeColors();
const featuresAvailable = useAreFeaturesAvailable([
"customHomepage",
"customizableSidebar"
]);
const isAppLoading = useSettingStore((state) => state.isAppLoading);
const trash = useTrashStore((state) => state.items);
useEffect(() => {
if (!isAppLoading) {
useTrashStore.getState().refresh();
}
}, [isAppLoading]);
return !featuresAvailable ? null : (
<View
style={{
@@ -121,6 +133,7 @@ export const MenuItemProperties = ({ item }: { item: SideMenuItem }) => {
positivePress: async () => {
await db.trash.clear();
useTrashStore.getState().clear();
eSendEvent(eMenuItemUpdate);
eSendEvent(eAfterSync);
ToastManager.show({
message: strings.trashCleared(),

View File

@@ -71,7 +71,7 @@ export function MenuItem({
if (item.data) {
getTotalNotesRef.current([item.data?.id]);
}
}, [update]);
}, [item.data, update]);
useEffect(() => {
const onSyncComplete = async () => {
@@ -94,7 +94,9 @@ export function MenuItem({
setItemCount(await db.notes.archived.count());
break;
case "Trash":
setItemCount((await db.trash.all()).length);
{
setItemCount((await db.trash.all()).length);
}
break;
}
} else {

View File

@@ -38,10 +38,10 @@ import {
import { getElevationStyle } from "../../utils/elevation";
import { eHideToast, eShowToast } from "../../utils/events";
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
import { DefaultAppStyles } from "../../utils/styles";
import { Button } from "../ui/button";
import Heading from "../ui/typography/heading";
import Paragraph from "../ui/typography/paragraph";
import { DefaultAppStyles } from "../../utils/styles";
export const Toast = ({ context = "global" }) => {
const { colors, isDark } = useThemeColors();
@@ -119,10 +119,7 @@ export const Toast = ({ context = "global" }) => {
width: DDS.isTab ? dimensions.width / 2 : "100%",
alignItems: "center",
alignSelf: "center",
bottom:
insets.bottom +
15 +
(keyboard.keyboardShown ? Math.max(0, keyboard.keyboardHeight) : 0),
bottom: insets.bottom + 15,
position: "absolute",
zIndex: 999,
elevation: 15

View File

@@ -37,11 +37,10 @@ export default function useFeatureManager() {
"disableTrashCleanup",
"fullOfflineMode"
]);
const user = useUserStore((state) => state.user);
const plan = useUserStore((state) => state.user?.subscription?.plan);
useEffect(() => {
if (!user || !features) return;
if (!useUserStore.getState().user || !features) return;
if (!features.createNoteFromNotificationDrawer.isAllowed) {
SettingsService.setProperty("notifNotes", false);
@@ -82,7 +81,8 @@ export default function useFeatureManager() {
db.settings.setDefaultTag(undefined);
}
}
}, [features, plan, user]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [features, plan]);
return true;
}

View File

@@ -16,16 +16,17 @@ 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 { Plan, SubscriptionPlan, SubscriptionPlanId } from "@notesnook/core";
import { useEffect, useState } from "react";
import { Plan, SubscriptionPlan } from "@notesnook/core";
import React, { useEffect, useState } from "react";
import { useAsync } from "react-async-hook";
import { Platform } from "react-native";
import Config from "react-native-config";
import * as RNIap from "react-native-iap";
import { DatabaseLogger, db } from "../common/database";
import PremiumService from "../services/premium";
import SettingsService from "../services/settings";
import { useSettingStore } from "../stores/use-setting-store";
import { useUserStore } from "../stores/use-user-store";
import SettingsService from "../services/settings";
function numberWithCommas(x: string) {
const parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
@@ -155,9 +156,18 @@ const planIdToIndex = (planId: string) => {
return planIndex;
};
let WebPlanCache: Plan[];
const isGithubRelease = Config.GITHUB_RELEASE === "true";
const usePricingPlans = (options?: PricingPlansOptions) => {
const isGithubRelease = Config.GITHUB_RELEASE === "true";
const user = useUserStore((state) => state.user);
const regionalDiscount = useAsync(
() =>
db.pricing.sku(
Platform.OS === "android" ? "google" : "apple",
"yearly",
"pro"
),
[]
);
const [currentPlan, setCurrentPlan] = useState<string>(
options?.planId || pricingPlans[2].id
);
@@ -172,17 +182,20 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
const [userCanRequestTrial, setUserCanRequestTrial] = useState(false);
const [webPricingPlans, setWebPricingPlans] = useState<Plan[]>([]);
const getProduct = (planId: string, skuId: string) => {
if (isGithubRelease)
return webPricingPlans.find(
(plan) => planIdToIndex(planId) === plan.plan && skuId === plan.period
);
const getProduct = React.useCallback(
(planId: string, skuId: string) => {
if (isGithubRelease)
return webPricingPlans.find(
(plan) => planIdToIndex(planId) === plan.plan && skuId === plan.period
);
return (
plans.find((p) => p.id === planId)?.subscriptions?.[skuId] ||
plans.find((p) => p.id === planId)?.products?.[skuId]
);
};
return (
plans.find((p) => p.id === planId)?.subscriptions?.[skuId] ||
plans.find((p) => p.id === planId)?.products?.[skuId]
);
},
[plans, webPricingPlans]
);
const getProductAndroid = (planId: string, skuId: string) => {
if (isGithubRelease)
@@ -196,37 +209,45 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
return getProduct(planId, skuId) as RNIap.SubscriptionIOS;
};
const hasTrialOffer = (planId?: string, productId?: string) => {
if (!selectedProductSku && !productId) return false;
const hasTrialOffer = React.useCallback(
(planId?: string, productId?: string) => {
if (!selectedProductSku && !productId) return false;
if (productId?.includes("5year")) return false;
if (isGithubRelease) {
if (
user?.subscription?.trialsAvailed?.some(
(plan) => plan === planIdToIndex(planId || currentPlan)
)
) {
return false;
} else {
return true;
if (productId?.includes("5year")) return false;
if (isGithubRelease) {
if (
user?.subscription?.trialsAvailed?.some(
(plan) => plan === planIdToIndex(planId || currentPlan)
)
) {
return false;
} else {
return true;
}
}
}
return Platform.OS === "ios"
? (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionIOS
)?.introductoryPricePaymentModeIOS === "FREETRIAL"
: (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionAndroid
)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList
?.length > 1;
};
return Platform.OS === "ios"
? (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionIOS
)?.introductoryPricePaymentModeIOS === "FREETRIAL"
: (
getProduct(
planId || currentPlan,
productId || selectedProductSku
) as RNIap.SubscriptionAndroid
)?.subscriptionOfferDetails?.[0]?.pricingPhases?.pricingPhaseList
?.length > 1;
},
[
currentPlan,
getProduct,
selectedProductSku,
user?.subscription?.trialsAvailed
]
);
// user && (!user.subscription || !user.subscription.expiry) ? true : false;
@@ -649,21 +670,6 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
);
};
async function getRegionalDiscount(plan: string, productId: string) {
if (productId !== "notesnook.pro.yearly") {
return;
}
try {
return await db.pricing.sku(
Platform.OS === "android" ? "google" : "apple",
"yearly",
plan as SubscriptionPlanId
);
} catch (e) {
console.log(e);
}
}
function isSubscribedToPlan(planId: string) {
if (!PremiumService.get()) return false;
return user?.subscription?.productId?.includes(planId);
@@ -734,7 +740,7 @@ const usePricingPlans = (options?: PricingPlansOptions) => {
(plan) => plan.plan === planIndex && plan.period === period
);
},
getRegionalDiscount,
regionalDiscount: regionalDiscount.result,
isGithubRelease: isGithubRelease,
isSubscribed: () => user?.subscription?.plan !== SubscriptionPlan.FREE,
finish: () => options?.onBuy?.()

View File

@@ -1,6 +1,6 @@
// Production iOS build identifiers
IOS_CURRENT_PROJECT_VERSION = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
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 = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
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 = 2179
IOS_MARKETING_VERSION = 3.3.19
IOS_CURRENT_PROJECT_VERSION = 2180
IOS_MARKETING_VERSION = 3.3.20
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.3.19",
"version": "3.3.20",
"private": true,
"license": "GPL-3.0-or-later",
"scripts": {
@@ -202,4 +202,4 @@
"engines": {
"node": ">=20"
}
}
}

View File

@@ -324,6 +324,7 @@ function GroupHeader(props: GroupHeaderProps) {
cursor: "pointer",
px: 1,
py: 1,
pr: 2,
borderBottom: "1px solid var(--border)",
// border: isMenuTarget ? "1px solid" : "none",
// borderColor: isMenuTarget ? "accent" : "transparent",

View File

@@ -55,6 +55,8 @@ export const CustomScrollbarsVirtualList = forwardRef<
return (
<ScrollContainer
{...props}
trackStyle={() => ({ width: 6 })}
thumbStyle={() => ({ width: 6 })}
forwardedRef={(sRef) => {
if (typeof ref === "function") ref(sRef);
else if (ref) ref.current = sRef;

View File

@@ -148,6 +148,7 @@ function ListItem<TItem extends Item, TContext>(
sx={{
px: 1,
py: isCompact ? 0 : 1,
pr: isCompact ? 10 : 0,
height: isCompact ? 25 : "inherit",
cursor: "pointer",
position: "relative",

View File

@@ -713,7 +713,7 @@ function colorsToMenuItems(
const isChecked = !!noteColor && noteColor.id === color.id;
return {
type: "button",
key: color.title,
key: color.id,
title: color.title,
icon: Circle.path,
styles: { icon: { color: color.colorCode } },

View File

@@ -68,13 +68,23 @@ export function getWindowControls(
];
}
export const TITLE_BAR_HEIGHT = 37;
export function TitleBar({ isUnderlay = isMac() }: { isUnderlay?: boolean }) {
export function TitleBar({
isUnderlay = isMac(),
force = false
}: {
isUnderlay?: boolean;
force?: boolean;
}) {
const { isFullscreen, hasNativeWindowControls, isMaximized } =
useWindowControls();
const isTablet = useTablet();
const isMobile = useMobile();
if ((!isMac() && !isMobile && !isTablet) || (isFullscreen && isMac()))
if (
!force &&
((!isMac() && !isMobile && !isTablet) || (isFullscreen && isMac()))
) {
return null;
}
const tools = getWindowControls(
hasNativeWindowControls,

View File

@@ -128,7 +128,10 @@ export function PaddleCheckout(props: PaddleCheckoutProps) {
return;
}
if (!callback_data.data || SUBSCRIBED_EVENTS.indexOf(event_name) === -1) {
if (
!callback_data?.data ||
SUBSCRIBED_EVENTS.indexOf(event_name) === -1
) {
return;
}

View File

@@ -47,7 +47,7 @@ export interface Checkout {
export type PaddleEvent = {
action: "event";
event_name: CheckoutEventNames;
callback_data: PaddleEventData;
callback_data?: PaddleEventData;
};
export type PlanMetadata = Record<

View File

@@ -28,7 +28,7 @@ import {
import { desktop } from "./common/desktop-bridge";
import { useKeyStore } from "./interfaces/key-store";
import { usePromise } from "@notesnook/common";
import { AuthProps } from "./views/auth";
import { AuthProps, isAuthRouteActive } from "./views/auth";
import { loadDatabase } from "./hooks/use-database";
import AppLock from "./views/app-lock";
import { Text } from "@theme-ui/components";
@@ -57,7 +57,7 @@ export async function startApp(children?: React.ReactNode) {
root.render(
<>
<TitleBar />
<TitleBar force={isAuthRouteActive()} />
<ErrorBoundary>
<GlobalErrorHandler>
<BaseThemeProvider

View File

@@ -30,7 +30,12 @@ import {
Warn
} from "../components/icons";
import Field, { FieldProps } from "../components/field";
import { getQueryParams, hardNavigate, makeURL } from "../navigation";
import {
getCurrentPath,
getQueryParams,
hardNavigate,
makeURL
} from "../navigation";
import { store as userstore } from "../stores/user-store";
import { db } from "../common/db";
import Config from "../utils/config";
@@ -163,6 +168,11 @@ const routePaths: Record<AuthRoutes, string> = {
signup: "/signup"
};
export function isAuthRouteActive() {
const path = getCurrentPath();
return Object.values(routePaths).includes(path);
}
const authorizedRoutes: AuthRoutes[] = [
"login:email",
"login:password",

View File

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

View File

@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-09-22 09:18+0500\n"
"POT-Creation-Date: 2026-04-20 11:33+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2025-09-22 09:18+0500\n"
"POT-Creation-Date: 2026-04-20 11:33+0500\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -626,7 +626,7 @@ msgstr ""
#: src/strings.ts:1008
msgid "2FA code sent via {method}"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2585
msgid "5 year plan (One time purchase)"
@@ -738,7 +738,7 @@ msgstr ""
#: src/strings.ts:2245
msgid "Add to dictionary"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2618
msgid "Add to home"
@@ -754,7 +754,7 @@ msgstr ""
#: src/strings.ts:975
msgid "Add your first notebook"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2621
msgid "Adjust the line height of the editor"
@@ -846,7 +846,7 @@ msgstr ""
#: src/strings.ts:250
msgid "An error occurred while migrating your data. You can logout of your account and try to relogin. However this is not recommended as it may result in some data loss if your data was not synced."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2579
msgid "and"
@@ -866,7 +866,7 @@ msgstr ""
#: src/strings.ts:1397
msgid "and we will manually confirm your account."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2596
msgid "ANNOUNCEMENT"
@@ -1108,7 +1108,7 @@ msgstr ""
#: src/strings.ts:2155
msgid "Available on iOS & Android"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2644
msgid "Back"
@@ -1204,7 +1204,7 @@ msgstr ""
#: src/strings.ts:2472
msgid "Believer plan"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2582
msgid "Best value"
@@ -1268,7 +1268,7 @@ msgstr ""
#: src/strings.ts:405
msgid "By"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2577
msgid "By joining you agree to our"
@@ -1288,7 +1288,7 @@ msgstr ""
#: src/strings.ts:549
msgid "Cancel"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2575
msgid "Cancel anytime, subscription auto-renews."
@@ -1377,7 +1377,7 @@ msgstr ""
#: src/strings.ts:434
msgid "Change password"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2589
msgid "Change plan"
@@ -1489,7 +1489,7 @@ msgstr ""
#: src/strings.ts:1133
msgid "Choose how dates are displayed in the app"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2624
msgid "Choose how day is displayed in the app"
@@ -1505,7 +1505,7 @@ msgstr ""
#: src/strings.ts:402
msgid "Choose how you want to secure your notes locally."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2629
msgid "Choose what day to display as the first day of the week"
@@ -1588,7 +1588,7 @@ msgid ""
"---\n"
"\n"
"**Only use this for troubleshooting purposes. If you are having persistent issues, it is recommended that you reach out to us via support@streetwriters.co so we can help you resolve it permanently.**"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2229
msgid "Clearing trash will permanently delete all the items in your trash. This action is IRREVERSIBLE."
@@ -1612,7 +1612,7 @@ msgstr ""
#: src/strings.ts:2393
msgid "Click to reset {title}"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2619
msgid "Click to save"
@@ -1761,7 +1761,7 @@ msgstr ""
#: src/strings.ts:1487
msgid "Confirm pin"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2643
msgid "Confirmation email sent"
@@ -2048,7 +2048,7 @@ msgstr ""
#: src/strings.ts:1841
msgid "Day"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2623
msgid "Day format"
@@ -2133,7 +2133,7 @@ msgstr ""
#: src/strings.ts:2293
msgid "Delete column"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2640
msgid "Delete data"
@@ -2390,7 +2390,7 @@ msgstr ""
#: src/strings.ts:1777
msgid "Edit"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2630
msgid "Edit creation date"
@@ -2420,7 +2420,7 @@ msgstr ""
#: src/strings.ts:1142
#: src/strings.ts:1526
msgid "Editor"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2586
msgid "Education plan"
@@ -2608,7 +2608,7 @@ msgstr ""
#: src/strings.ts:120
msgid "Enter the recovery code to continue logging in"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2622
msgid "Enter title"
@@ -2704,7 +2704,7 @@ msgstr ""
#: src/strings.ts:2072
msgid "Experience the next level of private note taking\""
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2636
msgid "Expiry date"
@@ -2733,7 +2733,7 @@ msgstr ""
#. placeholder {0}: format ? " " + format : ""
#: src/strings.ts:2035
msgid "Export as{0}"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2637
msgid "Export CSV"
@@ -2761,7 +2761,7 @@ msgstr ""
#: src/strings.ts:2404
msgid "Failed"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2641
msgid "Failed to attach file"
@@ -2898,7 +2898,7 @@ msgstr ""
#: src/strings.ts:1833
msgid "Filter languages"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2608
msgid "Finish your purchase in the browser."
@@ -3081,7 +3081,7 @@ msgstr ""
#: src/strings.ts:2162
msgid "GNU GENERAL PUBLIC LICENSE Version 3"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2609
msgid "Go back"
@@ -3125,7 +3125,7 @@ msgstr ""
#: src/strings.ts:2539
msgid "Google will remind you 2 days before your trial ends."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:586
msgid "Got it"
@@ -3339,7 +3339,7 @@ msgstr ""
#: src/strings.ts:1751
msgid "Import completed"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2638
msgid "Import CSV"
@@ -3427,7 +3427,7 @@ msgstr ""
#: src/strings.ts:1482
msgid "Invalid email"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2646
msgid "Invalid recovery key. Make sure to input your account recovery key, not a 2FA recovery code."
@@ -3577,7 +3577,7 @@ msgstr ""
#: src/strings.ts:2357
msgid "Line {line}, Column {column}"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2620
msgid "Line height"
@@ -3750,7 +3750,7 @@ msgstr ""
#: src/strings.ts:1362
msgid "Login to encrypt and sync notes"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2613
msgid "Login to upload attachments. [Read more](https://help.notesnook.com/faqs/login-to-upload-attachments)"
@@ -4148,7 +4148,7 @@ msgstr ""
#: src/strings.ts:202
msgid "No one can view this {type} except you."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2616
msgid "No password"
@@ -4285,7 +4285,7 @@ msgstr ""
#: src/strings.ts:2543
msgid "Notesnook"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2601
msgid "Notesnook Circle"
@@ -4580,7 +4580,7 @@ msgstr ""
#: src/strings.ts:523
msgid "Pinned"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2583
msgid "Plan limits"
@@ -4844,7 +4844,7 @@ msgstr ""
#: src/strings.ts:1180
msgid "Privacy mode"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2578
msgid "privacy policy"
@@ -4916,7 +4916,7 @@ msgstr ""
#: src/strings.ts:487
msgid "Publish note"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2617
msgid "Publish to the web"
@@ -4940,7 +4940,7 @@ msgstr ""
#: src/strings.ts:271
msgid "Published note link will be automatically deleted once it is viewed by someone."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2571
msgid "Purchase"
@@ -5080,7 +5080,7 @@ msgstr ""
#: src/strings.ts:2436
msgid "Redeem"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2600
msgid "Redeem code"
@@ -5856,7 +5856,7 @@ msgstr ""
#: src/strings.ts:1333
msgid "Set automatic trash cleanup interval from Settings > Behaviour > Clean trash interval."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2634
msgid "Set expiry"
@@ -6046,7 +6046,7 @@ msgstr ""
#: src/strings.ts:2355
msgid "Spaces"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2593
msgid "Special Offer"
@@ -6126,7 +6126,7 @@ msgstr ""
#: src/strings.ts:580
msgid "Submit"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2572
msgid "Subscribe"
@@ -6382,7 +6382,7 @@ msgstr ""
#: src/strings.ts:102
msgid "Terms of Service "
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2580
msgid "terms of use."
@@ -6414,7 +6414,7 @@ msgstr ""
#: src/strings.ts:2551
msgid "Thank you for subscribing"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2588
msgid "Thank you for the purchase"
@@ -6430,7 +6430,7 @@ msgstr ""
#: src/strings.ts:1643
msgid "The {title} at {url} is not compatible with this client."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2633
msgid "The incoming note could not be unlocked with the provided password. Enter the correct password for the incoming note"
@@ -6438,7 +6438,7 @@ msgstr ""
#: src/strings.ts:231
msgid "The information above will be publically available at"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2607
msgid "The Notesnook Circle is exclusive to subscribers. Please consider subscribing to gain access to Notesnook Circle and enjoy additional benefits."
@@ -6518,7 +6518,7 @@ msgstr ""
#: src/strings.ts:1934
msgid "This image cannot be previewed"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2574
msgid "This is a one time purchase, no subscription."
@@ -6531,7 +6531,7 @@ msgstr ""
#: src/strings.ts:1101
#: src/strings.ts:1110
msgid "This must only be used for troubleshooting. Using it regularly for sync is not recommended and will lead to unexpected data loss and other issues. If you are having persistent issues with sync, please report them to us at support@streetwriters.co."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2639
msgid "This note is empty"
@@ -6725,7 +6725,7 @@ msgstr ""
#: src/strings.ts:854
msgid "Unfavorite"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2584
msgid "Unlimited"
@@ -6741,7 +6741,7 @@ msgstr ""
#: src/strings.ts:157
msgid "Unlock"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2631
msgid "Unlock incoming note"
@@ -6758,7 +6758,7 @@ msgstr ""
#: src/strings.ts:888
msgid "Unlock note to delete it"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2642
msgid "Unlock note to merge conflicts"
@@ -6810,7 +6810,7 @@ msgstr ""
#: src/strings.ts:2086
msgid "Unregister"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2635
msgid "Unset expiry"
@@ -6859,7 +6859,7 @@ msgstr ""
#: src/strings.ts:749
msgid "Upgrade to Pro"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2599
msgid "Upgrade to redeem"
@@ -7090,7 +7090,7 @@ msgstr ""
#: src/strings.ts:1062
msgid "View your recovery codes to recover your account in case you lose access to your two-factor authentication methods."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2614
msgid "Views"
@@ -7158,7 +7158,7 @@ msgstr ""
#: src/strings.ts:664
msgid "Week"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2627
msgid "Week format"
@@ -7175,7 +7175,7 @@ msgstr ""
#: src/strings.ts:1888
msgid "Welcome back!"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2587
msgid "Welcome to Notesnook {plan}"
@@ -7252,7 +7252,7 @@ msgstr ""
#: src/strings.ts:106
msgid "You also agree to recieve marketing emails from us which you can opt-out of from app settings."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2592
msgid "You are already subscribed to this plan."
@@ -7284,7 +7284,7 @@ msgstr ""
#: src/strings.ts:2064
msgid "You can change the theme at any time from Settings or the side menu."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2595
msgid "You can change your subscription plan from the web app"
@@ -7349,7 +7349,7 @@ msgstr ""
#: src/strings.ts:2192
msgid "You have been logged out."
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2591
msgid "You have made a one time purchase. To change your plan please contact support."
@@ -7470,7 +7470,7 @@ msgstr ""
#: src/strings.ts:2099
msgid "Your current 2FA method is {method}"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2598
msgid "Your current subscription does not allow changing plans"
@@ -7518,7 +7518,7 @@ msgstr ""
#: src/strings.ts:1403
msgid "Your free trial is ending soon"
msgstr "<<<<<<< HEAD"
msgstr ""
#: src/strings.ts:2626
msgid "Your free trial is on-going. Your subscription will start on {trialExpiryDate}"

View File

@@ -21,8 +21,8 @@ import React, {
useCallback,
useRef,
useEffect,
PropsWithChildren,
useState
useState,
PropsWithChildren
} from "react";
import { Box, FlexProps, Text } from "@theme-ui/components";
import { getPosition } from "../../utils/position.js";
@@ -68,6 +68,7 @@ export function Menu(props: MenuProps) {
const focusedItem = items[focusIndex];
const subMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const item = items[focusIndex];
if (!item || !subMenuRef.current) return;
@@ -80,15 +81,23 @@ export function Menu(props: MenuProps) {
return;
}
const { top, left } = getPosition(subMenuRef.current, {
// yOffset: menuItemElement.offsetHeight,
target: menuItemElement,
location: "right"
});
function repositionSubmenu() {
if (!subMenuRef.current || !menuItemElement) return;
const { top, left } = getPosition(subMenuRef.current, {
target: menuItemElement,
location: "right"
});
subMenuRef.current.style.top = `${top}px`;
subMenuRef.current.style.left = `${left}px`;
}
repositionSubmenu();
subMenuRef.current.style.visibility = "visible";
subMenuRef.current.style.top = `${top}px`;
subMenuRef.current.style.left = `${left}px`;
const observer = new ResizeObserver(() => repositionSubmenu());
observer.observe(subMenuRef.current);
return () => observer.disconnect();
}, [isSubmenuOpen, focusIndex, items]);
return (

View File

@@ -209,14 +209,21 @@ Options:
// ---------------------------------------------------------------------------
// Stack frame parsing
//
// Handles frames like:
// Handles frames like (V8/Node):
// at XB (https://example.com/assets/app-HASH.js:136:27466)
// at https://example.com/assets/app-HASH.js:12:345 (anonymous)
// at https://example.com/assets/app-HASH.js:12:345
// at+XB+(https://example.com/assets/app-HASH.js:136:27466) (URL-encoded)
// And Safari/WebKit:
// E@https://example.com/assets/app-HASH.js:23:45428
// @https://example.com/assets/app-HASH.js:23:45428 (anonymous)
// ---------------------------------------------------------------------------
// Groups:
// V8 named: m[1]=symbol m[2]=url m[3]=line m[4]=col
// V8 anonymous: m[5]=url m[6]=line m[7]=col
// Safari: m[8]=symbol m[9]=url m[10]=line m[11]=col
const FRAME_RE =
/at\s+(?:async\s+)?(\S+)\s+\(?(https?:\/\/[^\s):]+):(\d+):(\d+)\)?|at\s+(?:async\s+)?(https?:\/\/[^\s):]+):(\d+):(\d+)/g;
/at\s+(?:async\s+)?(\S+)\s+\(?(https?:\/\/[^\s):]+):(\d+):(\d+)\)?|at\s+(?:async\s+)?(https?:\/\/[^\s):]+):(\d+):(\d+)|(\S*)@(https?:\/\/[^\s:)]+):(\d+):(\d+)/g;
function parseStackTrace(raw) {
// Replace URL-encoded + with spaces so the regex works uniformly
@@ -233,7 +240,7 @@ function parseStackTrace(raw) {
}
if (m[2]) {
// "at symbol (url:line:col)"
// V8: "at symbol (url:line:col)"
frames.push({
raw: line,
parsed: true,
@@ -242,8 +249,8 @@ function parseStackTrace(raw) {
line: parseInt(m[3], 10),
col: parseInt(m[4], 10)
});
} else {
// "at url:line:col"
} else if (m[5]) {
// V8: "at url:line:col"
frames.push({
raw: line,
parsed: true,
@@ -252,6 +259,16 @@ function parseStackTrace(raw) {
line: parseInt(m[6], 10),
col: parseInt(m[7], 10)
});
} else {
// Safari/WebKit: "symbol@url:line:col"
frames.push({
raw: line,
parsed: true,
symbol: m[8] || null,
url: m[9],
line: parseInt(m[10], 10),
col: parseInt(m[11], 10)
});
}
}
@@ -338,7 +355,10 @@ function tryDemangleDotted(consumer, generatedLine, generatedCol, symbol) {
let col = generatedCol;
for (let i = parts.length - 1; i >= 0; i--) {
const pos = consumer.originalPositionFor({ line: generatedLine, column: col });
const pos = consumer.originalPositionFor({
line: generatedLine,
column: col
});
if (pos.name) resolved[i] = pos.name;
// Step back past this identifier and the dot that precedes it
col -= parts[i].length + 1;
@@ -507,7 +527,13 @@ async function main() {
console.log(` at ${name} (${src}:${pos.line}:${pos.column})`);
// Source context
const ctx = getSourceContext(consumer, pos.source, pos.line, pos.column, opts.context);
const ctx = getSourceContext(
consumer,
pos.source,
pos.line,
pos.column,
opts.context
);
if (ctx) {
for (const l of ctx) {
const gutter = l.isTarget ? ">" : " ";