From ed3570166c1f05cfdc28ad583a791e1ebffbd5da Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Sun, 1 Feb 2026 13:11:45 +0100 Subject: [PATCH 1/8] chore: update discord URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbe290a..d6c2a00 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ShiggyCord [![Discord](https://img.shields.io/discord/1427396621905432699?style=social&logo=discord&label=ShiggyCord)](https://discord.gg/nQykFF9Ud6) +# ShiggyCord [![Discord](https://img.shields.io/discord/1427396621905432699?style=social&logo=discord&label=ShiggyCord)](https://discord.shiggycord.dev) Bundle based on Kettu, made just for fun From 23e5efc446053a5940834e9b919fc1dd0afedd12 Mon Sep 17 00:00:00 2001 From: cocobo1 Date: Tue, 3 Feb 2026 22:35:22 +0200 Subject: [PATCH 2/8] fix: update the removed activity icon --- src/core/ui/settings/index.ts | 2 +- src/core/ui/settings/pages/Themes/index.tsx | 44 +++++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/core/ui/settings/index.ts b/src/core/ui/settings/index.ts index 494e9cd..c5274b9 100644 --- a/src/core/ui/settings/index.ts +++ b/src/core/ui/settings/index.ts @@ -24,7 +24,7 @@ export default function initSettings() { { key: "BUNNY_PLUGINS", title: () => Strings.PLUGINS, - icon: findAssetId("ActivitiesIcon"), + icon: findAssetId("AppsIcon"), render: () => import("@core/ui/settings/pages/Plugins") }, { diff --git a/src/core/ui/settings/pages/Themes/index.tsx b/src/core/ui/settings/pages/Themes/index.tsx index 95e7092..6f472e8 100644 --- a/src/core/ui/settings/pages/Themes/index.tsx +++ b/src/core/ui/settings/pages/Themes/index.tsx @@ -21,6 +21,7 @@ import { TableRowGroup, TableCheckboxRow, TableRowIcon, + TableSwitchRow, } from "@metro/common/components"; import { View } from "react-native"; @@ -74,12 +75,14 @@ export default function Themes() { {/* Changed from TableRadioGroup to individual TableSwitchRow components for better UX - users can now toggle options individually */} - } - checked={!colorsPref.type} - onPress={() => { - if (!colorsPref.type) { + value={!colorsPref.type} + onValueChange={(enabled: boolean) => { + if (enabled) { + colorsPref.type = undefined; + } else { colorsPref.type = "dark"; } getCurrentTheme()?.data && @@ -88,24 +91,24 @@ export default function Themes() { }); }} /> - } - checked={colorsPref.type === "dark"} - onPress={() => { - colorsPref.type = colorsPref.type === "dark" ? undefined : "dark"; + value={colorsPref.type === "dark"} + onValueChange={(enabled: boolean) => { + colorsPref.type = enabled ? "dark" : undefined; getCurrentTheme()?.data && updateBunnyColor(getCurrentTheme()!.data!, { update: true, }); }} /> - } - checked={colorsPref.type === "light"} - onPress={() => { - colorsPref.type = colorsPref.type === "light" ? undefined : "light"; + value={colorsPref.type === "light"} + onValueChange={(enabled: boolean) => { + colorsPref.type = enabled ? "light" : undefined; getCurrentTheme()?.data && updateBunnyColor(getCurrentTheme()!.data!, { update: true, @@ -114,13 +117,20 @@ export default function Themes() { /> - } - checked={!colorsPref.customBackground} - onPress={() => { - colorsPref.customBackground = !colorsPref.customBackground ? "hidden" : null; + value={!colorsPref.customBackground} + onValueChange={(enabled: boolean) => { + colorsPref.customBackground = enabled ? null : "hidden"; + }} + /> + } + value={colorsPref.customBackground === "hidden"} + onValueChange={(enabled: boolean) => { + colorsPref.customBackground = enabled ? "hidden" : null; }} /> From bd7b595d1c0785a48996d35dab1aa3f75adddd48 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 6 Feb 2026 19:21:56 +0100 Subject: [PATCH 3/8] fix: debugger connection in Developer options and React DevTools --- .../ui/settings/pages/Developer/index.tsx | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/core/ui/settings/pages/Developer/index.tsx b/src/core/ui/settings/pages/Developer/index.tsx index 1fd71d2..043f035 100644 --- a/src/core/ui/settings/pages/Developer/index.tsx +++ b/src/core/ui/settings/pages/Developer/index.tsx @@ -7,6 +7,9 @@ import { connectToDebugger, disconnectFromDebugger, isConnectedToDebugger, + connectRdt, + disconnectRdt, + useIsRdtConnected, } from "@lib/api/debug"; import { getReactDevToolsProp, @@ -33,7 +36,7 @@ import { ErrorBoundary } from "@ui/components"; import ErrorBoundaryScreen from "@core/ui/reporter/components/ErrorBoundaryScreen"; import { createStyles, TextStyleSheet } from "@ui/styles"; import { NativeModules } from "react-native"; -import { ScrollView, StyleSheet } from "react-native"; +import { ScrollView } from "react-native"; import { showToast } from "@ui/toasts"; import { useState, useEffect } from "react"; @@ -72,6 +75,7 @@ export default function Developer() { const [isDebuggerConnected, setIsDebuggerConnected] = useState( isConnectedToDebugger(), ); + const isRdtConnected = useIsRdtConnected(); const styles = useStyles(); const navigation = NavigationNative.useNavigation(); @@ -96,6 +100,18 @@ export default function Developer() { } }; + const handleReactDevToolsConnect = () => { + if (isRdtConnected) { + disconnectRdt(); + } else { + if (!settings.devToolsUrl?.trim()) { + showToast("Invalid devTools URL!", findAssetId("Small")); + return; + } + connectRdt(settings.devToolsUrl); + } + }; + return ( } - onPress={() => connectToDebugger(settings.debuggerUrl)} + onPress={handleDebuggerConnect} /> @@ -159,39 +175,7 @@ export default function Developer() { label={Strings.CONNECT_TO_REACT_DEVTOOLS} subLabel="Connect React DevTools for component debugging" icon={} - onPress={async () => { - if (!settings.devToolsUrl?.trim()) { - showToast("Invalid devTools URL!", findAssetId("Small")); - return; - } - - try { - const devTools = - window[getReactDevToolsProp() || "__vendetta_rdc"]; - - if (!devTools?.connectToDevTools) { - showToast( - "React DevTools not available", - findAssetId("Small"), - ); - return; - } - - await devTools.connectToDevTools({ - host: settings.devToolsUrl.split(":")?.[0], - resolveRNStyle: StyleSheet.flatten, - }); - showToast( - "Connected to React DevTools!", - findAssetId("Check"), - ); - } catch (error) { - showToast( - "Failed to connect to React DevTools", - findAssetId("Small"), - ); - } - }} + onPress={handleReactDevToolsConnect} /> {isLoaderConfigSupported() && isVendettaLoader() && ( From 5bdc55a8b055f5557a116f3d77daa97c2836b424 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 6 Feb 2026 19:33:37 +0100 Subject: [PATCH 4/8] feat: bump to 1.4.1.1 --- .github/workflows/release.yml | 12 ++++++------ scripts/build.mjs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8023de5..0904b99 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,14 +42,14 @@ jobs: echo '--- scripts/build.mjs (snippet) ---' >&2 sed -n '1,200p' scripts/build.mjs >&2 || true - # Primary extraction: find the first quoted semver (may include prerelease suffix, but we strip it) - raw=$(grep -m1 -oE '\"[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?\"' scripts/build.mjs | tr -d '\"' | head -n1 || true) + # Primary extraction: find the first quoted version (supports major.minor.patch or major.minor.patch.patch formats, may include prerelease suffix) + raw=$(grep -m1 -oE '\"[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9A-Za-z.-]+)?\"' scripts/build.mjs | tr -d '\"' | head -n1 || true) - # Strip any prerelease suffix, keep only major.minor.patch - version=$(printf '%s' "$raw" | sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/') + # Strip any prerelease suffix, keep only major.minor.patch or major.minor.patch.patch + version=$(printf '%s' "$raw" | sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?).*/\1/') - # Validate final result is exactly major.minor.patch - if ! printf '%s' "$version" | grep -xE '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then + # Validate final result is either major.minor.patch or major.minor.patch.patch + if ! printf '%s' "$version" | grep -xE '^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?$' >/dev/null; then echo "Failed to extract a valid base version from scripts/build.mjs" >&2 sed -n '1,200p' scripts/build.mjs >&2 || true exit 1 diff --git a/scripts/build.mjs b/scripts/build.mjs index 6c03b18..2dd3de7 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -75,7 +75,7 @@ const config = { constModules: { globals: { "bunny-build-info": { - version: `"1.4.1"`, + version: `"1.4.1.1"`, }, }, }, From f7d4061b86f080aeda0638382d8aabb9faefc55d Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 6 Feb 2026 20:13:55 +0100 Subject: [PATCH 5/8] feat: fix buttons in Developer settings looking bad --- .../ui/settings/pages/Developer/index.tsx | 130 ++++++++++-------- src/metro/common/types/components.ts | 2 +- 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/src/core/ui/settings/pages/Developer/index.tsx b/src/core/ui/settings/pages/Developer/index.tsx index 043f035..5ef3c0f 100644 --- a/src/core/ui/settings/pages/Developer/index.tsx +++ b/src/core/ui/settings/pages/Developer/index.tsx @@ -122,76 +122,90 @@ export default function Developer() { style={{ paddingVertical: 24, paddingHorizontal: 12 }} spacing={24} > - - ( - - ws:// - - )} - defaultValue={settings.debuggerUrl} - onChange={(v: string) => (settings.debuggerUrl = v)} - /> - } - value={settings.autoDebugger} - onValueChange={(v: boolean) => { - settings.autoDebugger = v; - }} - /> - } - onPress={handleDebuggerConnect} - /> - + + + + ( + + ws:// + + )} + defaultValue={settings.debuggerUrl} + onChange={(v: string) => (settings.debuggerUrl = v)} + /> + + - {isReactDevToolsPreloaded() && ( - - ( - - ws:// - - )} - defaultValue={settings.devToolsUrl} - onChange={(v: string) => (settings.devToolsUrl = v)} - /> + } - value={settings.autoDevTools} + label={Strings.AUTO_DEBUGGER} + icon={} + value={settings.autoDebugger} onValueChange={(v: boolean) => { - settings.autoDevTools = v; + settings.autoDebugger = v; }} /> } - onPress={handleReactDevToolsConnect} + label={Strings.CONNECT_TO_DEBUG_WEBSOCKET} + subLabel="Connect DevTools for debugging" + icon={} + onPress={handleDebuggerConnect} /> + + - {isLoaderConfigSupported() && isVendettaLoader() && ( + {isReactDevToolsPreloaded() && ( + + + + ( + + ws:// + + )} + defaultValue={settings.devToolsUrl} + onChange={(v: string) => (settings.devToolsUrl = v)} + /> + + + + - } - value={loaderConfig.loadReactDevTools} + label={Strings.AUTO_DEVTOOLS} + icon={} + value={settings.autoDevTools} onValueChange={(v: boolean) => { - loaderConfig.loadReactDevTools = v; + settings.autoDevTools = v; }} /> - )} - + } + onPress={handleReactDevToolsConnect} + /> + + {isLoaderConfigSupported() && isVendettaLoader() && ( + + } + value={loaderConfig.loadReactDevTools} + onValueChange={(v: boolean) => { + loaderConfig.loadReactDevTools = v; + }} + /> + )} + + )} {isLoaderConfigSupported() && ( diff --git a/src/metro/common/types/components.ts b/src/metro/common/types/components.ts index 254fa34..87a3bf3 100644 --- a/src/metro/common/types/components.ts +++ b/src/metro/common/types/components.ts @@ -211,7 +211,7 @@ interface TableRowIconProps { export type TableRowIcon = React.FC; interface TableRowGroupProps { - title: string; + title?: string; children: ReactNode; } From 4b0f64131c4593e489c0ef56fe112f9331355495 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 6 Feb 2026 20:23:01 +0100 Subject: [PATCH 6/8] feat: update Asset Browser file sorting --- src/core/i18n/default.json | 1 + .../settings/pages/Developer/AssetBrowser.tsx | 104 +++++++++++++++--- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/src/core/i18n/default.json b/src/core/i18n/default.json index 5764edf..b20e33e 100644 --- a/src/core/i18n/default.json +++ b/src/core/i18n/default.json @@ -7,6 +7,7 @@ "AUTO_DEBUGGER": "Automatically connect to Debugger", "AUTO_DEVTOOLS": "Automatically connect to Devtools", "ASSET_BROWSER": "Asset Browser", + "ASSET_TYPES": "Asset Types", "BRAND": "Brand", "SHIGGYCORD": "ShiggyCord", "SHIGGYCORD_URL": "ShiggyCord URL", diff --git a/src/core/ui/settings/pages/Developer/AssetBrowser.tsx b/src/core/ui/settings/pages/Developer/AssetBrowser.tsx index 639dcb5..53645c6 100644 --- a/src/core/ui/settings/pages/Developer/AssetBrowser.tsx +++ b/src/core/ui/settings/pages/Developer/AssetBrowser.tsx @@ -1,21 +1,98 @@ import AssetDisplay from "@core/ui/settings/pages/Developer/AssetDisplay"; import { iterateAssets } from "@lib/api/assets"; -import { Text } from "@metro/common/components"; +import { findAssetId } from "@lib/api/assets"; +import { Strings } from "@core/i18n"; +import { IconButton, TableCheckboxRow, TableRowGroup, ActionSheet, BottomSheetTitleHeader } from "@metro/common/components"; import { ErrorBoundary, Search } from "@ui/components"; -import { useMemo } from "react"; +import { showSheet } from "@lib/ui/sheets"; +import { useProxy, createProxy } from "@core/vendetta/storage"; +import { useState, useMemo, useCallback } from "react"; import { FlatList, View } from "react-native"; +// Asset type categories +const DISPLAYABLE_IMAGE_TYPES = new Set(["png", "jpg", "jpeg", "svg", "gif"]); +const DISPLAYABLE_TEXT_TYPES = new Set(["jsona", "json", "lottie"]); + +// Define all available filters with their display names +const FILTER_OPTIONS = [ + { id: "images", label: "Images (PNG, JPG, SVG, GIF)", types: DISPLAYABLE_IMAGE_TYPES, defaultEnabled: true }, + { id: "text", label: "Text Types (JSON, JSONA, Lottie)", types: DISPLAYABLE_TEXT_TYPES, defaultEnabled: false }, +]; + +const { proxy: assetBrowserStorage } = createProxy({ + enabledFilters: { + images: true, + text: false, + } +}); + export default function AssetBrowser() { - const [search, setSearch] = React.useState(""); - const all = useMemo(() => Array.from(iterateAssets()), []); + const [search, setSearch] = useState(""); + const [updateTick, setUpdateTick] = useState(0); + + // Use useProxy to make the state reactive + useProxy(assetBrowserStorage); + + const getFilteredAssets = useCallback(() => { + return Array.from(iterateAssets()).filter((asset) => { + const type = String(asset.type ?? "").toLowerCase(); + + // Check if this asset type is enabled in any filter + for (const filter of FILTER_OPTIONS) { + if (assetBrowserStorage.enabledFilters[filter.id as keyof typeof assetBrowserStorage.enabledFilters] && filter.types.has(type)) { + return true; + } + } + return false; + }); + }, [updateTick]); + + const all = useMemo(() => getFilteredAssets(), [getFilteredAssets]); + + const toggleFilter = (filterId: string) => { + assetBrowserStorage.enabledFilters[filterId as keyof typeof assetBrowserStorage.enabledFilters] = !assetBrowserStorage.enabledFilters[filterId as keyof typeof assetBrowserStorage.enabledFilters]; + setUpdateTick(prev => prev + 1); + }; + + const handleFilterPress = () => { + showSheet("AssetBrowserFilter", () => { + useProxy(assetBrowserStorage); + + return ( + + + + + {FILTER_OPTIONS.map(filter => ( + toggleFilter(filter.id)} + /> + ))} + + + + ); + }); + }; return ( - setSearch(v)} - /> + + setSearch(v)} + /> + + - - Some assets types cannot be displayed and will be marked in red. - a.name.includes(search) || a.id.toString() === search, + (a) => (a.name.includes(search) || a.id.toString() === search), )} renderItem={({ item }: any) => } contentContainerStyle={{ From b8c751951970f8ef9c3ca83c7dffffb2f7b214ac Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Fri, 6 Feb 2026 20:33:33 +0100 Subject: [PATCH 7/8] feat: change Sheet Menu look totally in Asset Browser --- src/core/i18n/default.json | 2 + .../settings/pages/Developer/AssetBrowser.tsx | 71 +++++++++++-------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/core/i18n/default.json b/src/core/i18n/default.json index b20e33e..8d49a1e 100644 --- a/src/core/i18n/default.json +++ b/src/core/i18n/default.json @@ -8,6 +8,8 @@ "AUTO_DEVTOOLS": "Automatically connect to Devtools", "ASSET_BROWSER": "Asset Browser", "ASSET_TYPES": "Asset Types", + "IMAGE_FILES": "Image Files", + "TEXT_FILES": "Text Files", "BRAND": "Brand", "SHIGGYCORD": "ShiggyCord", "SHIGGYCORD_URL": "ShiggyCord URL", diff --git a/src/core/ui/settings/pages/Developer/AssetBrowser.tsx b/src/core/ui/settings/pages/Developer/AssetBrowser.tsx index 53645c6..c85dd37 100644 --- a/src/core/ui/settings/pages/Developer/AssetBrowser.tsx +++ b/src/core/ui/settings/pages/Developer/AssetBrowser.tsx @@ -9,21 +9,33 @@ import { useProxy, createProxy } from "@core/vendetta/storage"; import { useState, useMemo, useCallback } from "react"; import { FlatList, View } from "react-native"; -// Asset type categories -const DISPLAYABLE_IMAGE_TYPES = new Set(["png", "jpg", "jpeg", "svg", "gif"]); -const DISPLAYABLE_TEXT_TYPES = new Set(["jsona", "json", "lottie"]); - -// Define all available filters with their display names -const FILTER_OPTIONS = [ - { id: "images", label: "Images (PNG, JPG, SVG, GIF)", types: DISPLAYABLE_IMAGE_TYPES, defaultEnabled: true }, - { id: "text", label: "Text Types (JSON, JSONA, Lottie)", types: DISPLAYABLE_TEXT_TYPES, defaultEnabled: false }, +const IMAGE_FILES = [ + { id: "png", label: "PNG", defaultEnabled: true }, + { id: "jpg", label: "JPG", defaultEnabled: true }, + { id: "jpeg", label: "JPEG", defaultEnabled: true }, + { id: "svg", label: "SVG", defaultEnabled: true }, + { id: "gif", label: "GIF", defaultEnabled: true }, ]; +const TEXT_FILES = [ + { id: "jsona", label: "JSONA", defaultEnabled: false }, + { id: "json", label: "JSON", defaultEnabled: false }, + { id: "lottie", label: "Lottie", defaultEnabled: false }, +]; + +const ALL_FILE_TYPES = [...IMAGE_FILES, ...TEXT_FILES]; + +// Create initial enabled filters state +const createInitialFilters = () => { + const filters: Record = {}; + ALL_FILE_TYPES.forEach(type => { + filters[type.id] = type.defaultEnabled; + }); + return filters; +}; + const { proxy: assetBrowserStorage } = createProxy({ - enabledFilters: { - images: true, - text: false, - } + enabledFilters: createInitialFilters() }); export default function AssetBrowser() { @@ -36,21 +48,14 @@ export default function AssetBrowser() { const getFilteredAssets = useCallback(() => { return Array.from(iterateAssets()).filter((asset) => { const type = String(asset.type ?? "").toLowerCase(); - - // Check if this asset type is enabled in any filter - for (const filter of FILTER_OPTIONS) { - if (assetBrowserStorage.enabledFilters[filter.id as keyof typeof assetBrowserStorage.enabledFilters] && filter.types.has(type)) { - return true; - } - } - return false; + return assetBrowserStorage.enabledFilters[type] === true; }); }, [updateTick]); const all = useMemo(() => getFilteredAssets(), [getFilteredAssets]); const toggleFilter = (filterId: string) => { - assetBrowserStorage.enabledFilters[filterId as keyof typeof assetBrowserStorage.enabledFilters] = !assetBrowserStorage.enabledFilters[filterId as keyof typeof assetBrowserStorage.enabledFilters]; + assetBrowserStorage.enabledFilters[filterId] = !assetBrowserStorage.enabledFilters[filterId]; setUpdateTick(prev => prev + 1); }; @@ -61,18 +66,26 @@ export default function AssetBrowser() { return ( - - - {FILTER_OPTIONS.map(filter => ( + + {IMAGE_FILES.map(fileType => ( toggleFilter(filter.id)} + key={fileType.id} + label={fileType.label} + checked={assetBrowserStorage.enabledFilters[fileType.id] === true} + onPress={() => toggleFilter(fileType.id)} + /> + ))} + + + {TEXT_FILES.map(fileType => ( + toggleFilter(fileType.id)} /> ))} - ); }); From 6f8604d6a3b62a09ec27c0aa7379fcf3c0a4a1cc Mon Sep 17 00:00:00 2001 From: meqativ Date: Sat, 7 Feb 2026 13:31:46 +0200 Subject: [PATCH 8/8] feat: better error logging in debugger --- src/lib/api/debug.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib/api/debug.ts b/src/lib/api/debug.ts index 8949589..7df9bf0 100644 --- a/src/lib/api/debug.ts +++ b/src/lib/api/debug.ts @@ -224,13 +224,14 @@ export function connectToDebugger(url: string) { } }); - socket.addEventListener("close", () => { - showToast("Disconnected from debugger.", findAssetId("Small")); + socket.addEventListener("close", (e: any) => { + logger.error(`Debugger disconnected. (${e?.code}, ${e?.reason || "no reason specified"})`); unpatchConsoleAndLogger(); }); - socket.addEventListener("error", (err: any) => { - console.log(`Debugger error: ${err.message}`); + socket.addEventListener("error", (e: any) => { + const err = e?.message || e?.error?.message || "WebSocket Connection Errored"; + logger.error('Debugger error:', err); showToast("An error occurred with the debugger connection!", findAssetId("Small")); unpatchConsoleAndLogger(); }); @@ -281,13 +282,14 @@ export function connectRdt(url: string, quiet?: boolean) { bump(); }); - ws.addEventListener('close', () => { + ws.addEventListener('close', (e: any) => { + logger.error(`React DevTools disconnected. (${e?.code}, ${e?.reason || "no reason specified"})`); cleanupRdt(); }); ws.addEventListener('error', (e: any) => { cleanupRdt(); - const err = e?.message ?? e?.stack ?? String(e); + const err = e?.message || e?.error?.message || "WebSocket Connection Errored"; logger.error('React DevTools error:', err); if (!quiet) showToast(err, findAssetId("CircleXIcon-primary")); });