collective

Browse / github.com/storytold/artcraft-services / patch

fix: mark un-awaited promises explicitly (SonarCloud S9383)

fix: mark un-awaited promises explicitly (SonarCloud S9383) Resolves the 165 `typescript:S9383` findings across the dashboard, webapp, genhub and shared libs. Each flagged expression is a fire-and-forget call (`handleConfirm()`, `loadData()`, a generated API client, `navigator.clipboard.writeText(...).then(...)`, or an async IIFE). `void` marks the ignored promise explicitly, which is the fix the rule documents; it is deliberately not `.catch()` everywhere, because the components already surface failures through their own error state and adding empty handlers would hide them. Two shapes were handled by hand: the twelve async IIFEs (`void (async () =>

Status
open
Score
0.00
Adopters
0 other operators
Author
dylanmtaylor (imported from pull request #179, open)
Base
e57ee3d63ac478a24149384bf1473ef97aa067f8
Size
+227 −383 in 99 files
Review
pending
Shared
2026-10-28
Id
90f39c59b1de7ae7f62a497b

How it went

No other operator has reported using it yet.

Diff

raw · frontend/apps/artcraft-dashboard/src/components/BanUserDialog.tsx frontend/apps/artcraft-dashboard/src/components/BulkBanUsersDialog.tsx frontend/apps/artcraft-dashboard/src/components/ChangeUserEmailDialog.tsx frontend/apps/artcraft-dashboard/src/components/GiveCreditsDialog.tsx frontend/apps/artcraft-dashboard/src/components/ImpersonateUserDialog.tsx frontend/apps/artcraft-dashboard/src/components/JobsTable.tsx frontend/apps/artcraft-dashboard/src/components/MediaDetailModal.tsx frontend/apps/artcraft-dashboard/src/components/UserBillingSection.tsx frontend/apps/artcraft-dashboard/src/components/UserFeatureFlagsDialog.tsx frontend/apps/artcraft-dashboard/src/context/AuthContext.tsx frontend/apps/artcraft-dashboard/src/hooks/useInfiniteScroll.ts frontend/apps/artcraft-dashboard/src/pages/DashboardHome.tsx frontend/apps/artcraft-dashboard/src/pages/DebugLogs.tsx frontend/apps/artcraft-dashboard/src/pages/DebugLogsAll.tsx frontend/apps/artcraft-dashboard/src/pages/ExploreMedia.tsx frontend/apps/artcraft-dashboard/src/pages/FeatureFlags.tsx frontend/apps/artcraft-dashboard/src/pages/Impersonation.tsx frontend/apps/artcraft-dashboard/src/pages/JobHistory.tsx frontend/apps/artcraft-dashboard/src/pages/JobInfo.tsx frontend/apps/artcraft-dashboard/src/pages/ReengagementList.tsx frontend/apps/artcraft-dashboard/src/pages/ReferralsAnalytics.tsx frontend/apps/artcraft-dashboard/src/pages/SendPager.tsx frontend/apps/artcraft-dashboard/src/pages/SpendEvents.tsx frontend/apps/artcraft-dashboard/src/pages/StaffAuditLogs.tsx frontend/apps/artcraft-dashboard/src/pages/SubscriberSignups.tsx frontend/apps/artcraft-dashboard/src/pages/TopSpenders.tsx frontend/apps/artcraft-dashboard/src/pages/UserCreations.tsx frontend/apps/artcraft-dashboard/src/pages/UserDebugLogs.tsx frontend/apps/artcraft-dashboard/src/pages/UserEmailChanges.tsx frontend/apps/artcraft-dashboard/src/pages/UserProfile.tsx frontend/apps/artcraft-dashboard/src/pages/UserReferrals.tsx frontend/apps/artcraft-dashboard/src/pages/UserSearch.tsx frontend/apps/artcraft-dashboard/src/pages/UserSignups.tsx frontend/apps/artcraft-dashboard/src/pages/UserSpendHistory.tsx frontend/apps/artcraft-dashboard/src/pages/UserSpendSummary.tsx frontend/apps/artcraft-dashboard/src/pages/WalletHistory.tsx frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx frontend/apps/artcraft-webapp/src/components/lightbox/LightboxDetails.tsx frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx frontend/apps/artcraft-webapp/src/components/settings-modal/ApiKeySection.tsx frontend/apps/artcraft-webapp/src/components/settings-modal/BillingSection.tsx frontend/apps/artcraft-webapp/src/components/topbar/TopBarActions.tsx frontend/apps/artcraft-webapp/src/components/topbar/task-queue.tsx frontend/apps/artcraft-webapp/src/lib/session.ts frontend/apps/artcraft-webapp/src/pages/checkout/success.tsx frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx frontend/apps/artcraft-webapp/src/pages/forgot-password/forgot-password.tsx frontend/apps/artcraft-webapp/src/pages/forgot-password/verify-reset.tsx frontend/apps/artcraft-webapp/src/pages/frame-extractor/frame-extractor.tsx frontend/apps/artcraft-webapp/src/pages/library/library.tsx frontend/apps/artcraft-webapp/src/pages/login/login.tsx frontend/apps/artcraft-webapp/src/pages/onboarding/onboarding.tsx frontend/apps/artcraft-webapp/src/pages/pagescene/demo-output-overlay.tsx frontend/apps/artcraft-webapp/src/pages/pagescene/splash/SceneSplashCard.tsx frontend/apps/artcraft-webapp/src/pages/pagescene/use-prompt-prefill-from-output.ts frontend/apps/artcraft-webapp/src/pages/pricing/pricing.tsx frontend/apps/artcraft-webapp/src/pages/set-password/set-password.tsx frontend/apps/artcraft-website-next/src/components/media/media-details.tsx frontend/apps/artcraft-website-next/src/lib/use-account.ts frontend/apps/genhub/src/components/feed/media-detail.tsx frontend/apps/genhub/src/components/layout/navbar.tsx frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx frontend/libs/components/generation-list/src/lib/media-prompt-token-cache.ts frontend/libs/components/generation-list/src/lib/prompts-cache.ts frontend/libs/components/help-menu/src/lib/help-menu.tsx frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx frontend/libs/components/lightbox-modal/src/lib/tags/TagsSection.tsx frontend/libs/components/pagedraw/src/lib/PageDraw.tsx frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx frontend/libs/components/pagescene/src/lib/actions/toggleObjectVisibility.ts frontend/libs/components/pagescene/src/lib/engine/editor.ts frontend/libs/components/pagescene/src/lib/engine/editor/ModalTransformController.ts frontend/libs/components/pagescene/src/lib/engine/placeholder_manager.ts frontend/libs/components/pagescene/src/lib/engine/scene.ts frontend/libs/components/pricing-modal/src/lib/use-currency.ts frontend/libs/components/pricing-table/src/lib/pricing-table.tsx frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx frontend/libs/components/upload-modal/src/lib/UploadModalImage/UploadFilesImage.tsx frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx frontend/libs/components/video-editor/src/lib/actions/use-editor-actions.ts frontend/libs/components/video-editor/src/lib/fonts/use-font-atlas.ts frontend/libs/components/video-editor/src/lib/hooks/use-fullscreen.ts frontend/libs/components/video-editor/src/lib/timeline/bookmarks/components/bookmarks.tsx frontend/libs/components/video-editor/src/lib/timeline/bookmarks/hooks/use-bookmark-drag.ts frontend/libs/tauri-api/src/lib/events/CanvasBgRemovedEvent.ts frontend/libs/tauri-api/src/lib/sora/useSoraLoginListener.ts frontend/libs/tauri-utils/src/lib/useWindowControls.ts

diff --git a/frontend/apps/artcraft-dashboard/src/components/BanUserDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/BanUserDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/BanUserDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/BanUserDialog.tsx@@ -194,7 +194,7 @@ export function BanUserDialog({               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleConfirm();+                void handleConfirm();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/components/BulkBanUsersDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/BulkBanUsersDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/BulkBanUsersDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/BulkBanUsersDialog.tsx@@ -204,7 +204,7 @@ export function BulkBanUsersDialog({               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleConfirm();+                void handleConfirm();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/components/ChangeUserEmailDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/ChangeUserEmailDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/ChangeUserEmailDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/ChangeUserEmailDialog.tsx@@ -189,7 +189,7 @@ export function ChangeUserEmailDialog({               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleConfirm();+                void handleConfirm();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/components/GiveCreditsDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/GiveCreditsDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/GiveCreditsDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/GiveCreditsDialog.tsx@@ -156,7 +156,7 @@ export function GiveCreditsDialog({               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleConfirm();+                void handleConfirm();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/components/ImpersonateUserDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/ImpersonateUserDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/ImpersonateUserDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/ImpersonateUserDialog.tsx@@ -55,7 +55,7 @@ export function ImpersonateUserDialog({     setImpersonationToken(null);      const modApi = new ModerationApi();-    modApi.ImpersonateUser({ user_token: userToken }).then((resp) => {+    void modApi.ImpersonateUser({ user_token: userToken }).then((resp) => {       if (cancelled) return;       if (resp.success && resp.data) {         setImpersonationToken(resp.data.password_token);@@ -71,7 +71,7 @@ export function ImpersonateUserDialog({   }, [open, userToken]);    const copy = (text: string, field: string) => {-    navigator.clipboard.writeText(text).then(() => {+    void navigator.clipboard.writeText(text).then(() => {       setCopiedField(field);       setTimeout(() => setCopiedField(null), 2000);     });diff --git a/frontend/apps/artcraft-dashboard/src/components/JobsTable.tsx b/frontend/apps/artcraft-dashboard/src/components/JobsTable.tsx--- a/frontend/apps/artcraft-dashboard/src/components/JobsTable.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/JobsTable.tsx@@ -71,7 +71,7 @@ export function JobsTable({   const [copiedToken, setCopiedToken] = useState<string | null>(null);    const copyToClipboard = (text: string) => {-    navigator.clipboard.writeText(text).then(() => {+    void navigator.clipboard.writeText(text).then(() => {       setCopiedToken(text);       setTimeout(() => setCopiedToken(null), 2000);     });diff --git a/frontend/apps/artcraft-dashboard/src/components/MediaDetailModal.tsx b/frontend/apps/artcraft-dashboard/src/components/MediaDetailModal.tsx--- a/frontend/apps/artcraft-dashboard/src/components/MediaDetailModal.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/MediaDetailModal.tsx@@ -130,14 +130,14 @@ export function MediaDetailModal({     if (!open || !item) return;     let cancelled = false;     const api = new MediaApi();-    api.GetMediaFile(item.token).then((res) => {+    void api.GetMediaFile(item.token).then((res) => {       if (cancelled) return;       const promptToken = res.data?.maybe_prompt_token;       if (!promptToken) {         setPromptFailed(true);         return;       }-      api.GetPrompt(promptToken).then((promptRes) => {+      void api.GetPrompt(promptToken).then((promptRes) => {         if (cancelled) return;         if (promptRes.success && promptRes.data) {           setPromptInfo(promptRes.data);diff --git a/frontend/apps/artcraft-dashboard/src/components/UserBillingSection.tsx b/frontend/apps/artcraft-dashboard/src/components/UserBillingSection.tsx--- a/frontend/apps/artcraft-dashboard/src/components/UserBillingSection.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/UserBillingSection.tsx@@ -47,7 +47,7 @@ export function UserBillingSection({ userToken }: { userToken: string }) {   }, [userToken]);    const copyToClipboard = (text: string, id: string) => {-    navigator.clipboard.writeText(text).then(() => {+    void navigator.clipboard.writeText(text).then(() => {       setCopiedStates((prev) => ({ ...prev, [id]: true }));       setTimeout(() => {         setCopiedStates((prev) => ({ ...prev, [id]: false }));diff --git a/frontend/apps/artcraft-dashboard/src/components/UserFeatureFlagsDialog.tsx b/frontend/apps/artcraft-dashboard/src/components/UserFeatureFlagsDialog.tsx--- a/frontend/apps/artcraft-dashboard/src/components/UserFeatureFlagsDialog.tsx+++ b/frontend/apps/artcraft-dashboard/src/components/UserFeatureFlagsDialog.tsx@@ -80,7 +80,7 @@ export function UserFeatureFlagsDialog({     setSelectedFlags(new Set());      const api = new ModerationApi();-    Promise.all([+    void Promise.all([       api.ListAvailableFeatureFlags(),       api.ListUserFeatureFlags(usernameOrToken),     ])@@ -360,7 +360,7 @@ export function UserFeatureFlagsDialog({               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleClearAll();+                void handleClearAll();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/context/AuthContext.tsx b/frontend/apps/artcraft-dashboard/src/context/AuthContext.tsx--- a/frontend/apps/artcraft-dashboard/src/context/AuthContext.tsx+++ b/frontend/apps/artcraft-dashboard/src/context/AuthContext.tsx@@ -59,7 +59,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {   };    useEffect(() => {-    checkSession();+    void checkSession();   }, []);    const loginState = (userData: UserInfo) => {diff --git a/frontend/apps/artcraft-dashboard/src/hooks/useInfiniteScroll.ts b/frontend/apps/artcraft-dashboard/src/hooks/useInfiniteScroll.tsnew file mode 100644--- /dev/null+++ b/frontend/apps/artcraft-dashboard/src/hooks/useInfiniteScroll.ts@@ -0,0 +1,26 @@+import { useEffect, useRef } from "react";++/**+ * Calls `onLoadMore` when the element the returned ref is attached to (at the end of a list)+ * scrolls into view.+ */+export function useInfiniteScroll<T extends Element>(onLoadMore: () => void) {+  const sentinelRef = useRef<T | null>(null);++  useEffect(() => {+    const observer = new IntersectionObserver(+      (entries) => {+        if (entries[0].isIntersecting) onLoadMore();+      },+      { threshold: 0.1 },+    );++    if (sentinelRef.current) {+      observer.observe(sentinelRef.current);+    }++    return () => observer.disconnect();+  }, [onLoadMore]);++  return sentinelRef;+}diff --git a/frontend/apps/artcraft-dashboard/src/pages/DashboardHome.tsx b/frontend/apps/artcraft-dashboard/src/pages/DashboardHome.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/DashboardHome.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/DashboardHome.tsx@@ -16,7 +16,7 @@ export function DashboardHome() {   useEffect(() => {     let cancelled = false;     const api = new ModerationApi();-    api.ListDataboxDashboards().then((resp) => {+    void api.ListDataboxDashboards().then((resp) => {       if (cancelled) return;       setDataboards(resp.success && resp.data ? resp.data : []);       setIsLoading(false);diff --git a/frontend/apps/artcraft-dashboard/src/pages/DebugLogs.tsx b/frontend/apps/artcraft-dashboard/src/pages/DebugLogs.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/DebugLogs.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/DebugLogs.tsx@@ -32,7 +32,7 @@ export function DebugLogs() {   const [filter, setFilter] = useState("");    const copy = (value: string, id: string) => {-    navigator.clipboard.writeText(value).then(() => {+    void navigator.clipboard.writeText(value).then(() => {       setCopiedId(id);       setTimeout(() => setCopiedId((c) => (c === id ? null : c)), 2000);     });@@ -59,7 +59,7 @@ export function DebugLogs() {   };    useEffect(() => {-    loadLogs();+    void loadLogs();   }, [eventToken]);    const filteredLogs = useMemo(() => {diff --git a/frontend/apps/artcraft-dashboard/src/pages/DebugLogsAll.tsx b/frontend/apps/artcraft-dashboard/src/pages/DebugLogsAll.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/DebugLogsAll.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/DebugLogsAll.tsx@@ -53,7 +53,7 @@ export function DebugLogsAll() {   const loadingRef = useRef(false);    const copy = (value: string, id: string) => {-    navigator.clipboard.writeText(value).then(() => {+    void navigator.clipboard.writeText(value).then(() => {       setCopiedId(id);       setTimeout(() => setCopiedId((c) => (c === id ? null : c)), 2000);     });diff --git a/frontend/apps/artcraft-dashboard/src/pages/ExploreMedia.tsx b/frontend/apps/artcraft-dashboard/src/pages/ExploreMedia.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/ExploreMedia.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/ExploreMedia.tsx@@ -21,6 +21,7 @@ import {   IconPhoto, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  const MEDIA_CLASS_OPTIONS = [   { value: "", label: "All" },@@ -52,9 +53,7 @@ export function ExploreMedia() {   const [selectedItem, setSelectedItem] = useState<ExploreMediaFile | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLDivElement | null>(null);-+     // Stable filter key to detect changes   const filterKey = useMemo(     () => `${filterMediaClasses}|${filterMediaType}|${includeUserUploads}`,@@ -122,22 +121,7 @@ export function ExploreMedia() {   }, [nextCursor, isLoadingMore, loadData]);    // IntersectionObserver for infinite scroll-  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLDivElement>(loadMore);    // Sync URL mediaToken → selectedItem   useEffect(() => {@@ -152,7 +136,7 @@ export function ExploreMedia() {     } else {       // Fetch single media file for direct URL access       const api = new MediaApi();-      api.GetMediaFile(mediaToken).then((res) => {+      void api.GetMediaFile(mediaToken).then((res) => {         if (res.success && res.data) {           setSelectedItem(res.data as unknown as ExploreMediaFile);         }diff --git a/frontend/apps/artcraft-dashboard/src/pages/FeatureFlags.tsx b/frontend/apps/artcraft-dashboard/src/pages/FeatureFlags.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/FeatureFlags.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/FeatureFlags.tsx@@ -49,7 +49,7 @@ export function FeatureFlags() {   useEffect(() => {     let cancelled = false;     const api = new ModerationApi();-    api+    void api       .ListAvailableFeatureFlags()       .then((resp) => {         if (cancelled) return;diff --git a/frontend/apps/artcraft-dashboard/src/pages/Impersonation.tsx b/frontend/apps/artcraft-dashboard/src/pages/Impersonation.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/Impersonation.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/Impersonation.tsx@@ -23,6 +23,7 @@ import {   IconArrowRight, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  export function Impersonation() {   usePageTitle("Impersonation Logs");@@ -33,9 +34,7 @@ export function Impersonation() {   const [nextCursor, setNextCursor] = useState<string | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: string | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -74,12 +73,12 @@ export function Impersonation() {   const handleRefresh = () => {     setRequests([]);     setNextCursor(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -87,26 +86,11 @@ export function Impersonation() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/JobHistory.tsx b/frontend/apps/artcraft-dashboard/src/pages/JobHistory.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/JobHistory.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/JobHistory.tsx@@ -64,7 +64,7 @@ export function JobHistory() {    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };diff --git a/frontend/apps/artcraft-dashboard/src/pages/JobInfo.tsx b/frontend/apps/artcraft-dashboard/src/pages/JobInfo.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/JobInfo.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/JobInfo.tsx@@ -162,7 +162,7 @@ export function JobInfo() {   const [copiedId, setCopiedId] = useState<string | null>(null);    const copy = (value: string, id: string) => {-    navigator.clipboard.writeText(value).then(() => {+    void navigator.clipboard.writeText(value).then(() => {       setCopiedId(id);       setTimeout(() => setCopiedId((c) => (c === id ? null : c)), 2000);     });@@ -189,7 +189,7 @@ export function JobInfo() {   };    useEffect(() => {-    loadJob();+    void loadJob();   }, [jobToken]);    const badge = job ? getJobStatusBadgeProps(job.status) : null;diff --git a/frontend/apps/artcraft-dashboard/src/pages/ReengagementList.tsx b/frontend/apps/artcraft-dashboard/src/pages/ReengagementList.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/ReengagementList.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/ReengagementList.tsx@@ -23,6 +23,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  const COLUMN_COUNT = 7; @@ -53,9 +54,7 @@ export function ReengagementList() {   const [nextOffset, setNextOffset] = useState<number | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (offset?: number | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -90,12 +89,12 @@ export function ReengagementList() {   const handleRefresh = () => {     setCandidates([]);     setNextOffset(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -103,21 +102,11 @@ export function ReengagementList() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextOffset != null) {-      loadData(nextOffset, true);+      void loadData(nextOffset, true);     }   }, [nextOffset, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();-    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );-    if (sentinelRef.current) observerRef.current.observe(sentinelRef.current);-    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/ReferralsAnalytics.tsx b/frontend/apps/artcraft-dashboard/src/pages/ReferralsAnalytics.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/ReferralsAnalytics.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/ReferralsAnalytics.tsx@@ -24,6 +24,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  function formatDate(iso: string) {   return new Date(iso).toLocaleString("en-US", {@@ -48,9 +49,7 @@ export function ReferralsAnalytics() {   const [nextCursor, setNextCursor] = useState<string | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: string | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -87,12 +86,12 @@ export function ReferralsAnalytics() {   const handleRefresh = () => {     setReferrals([]);     setNextCursor(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -100,26 +99,11 @@ export function ReferralsAnalytics() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/SendPager.tsx b/frontend/apps/artcraft-dashboard/src/pages/SendPager.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/SendPager.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/SendPager.tsx@@ -232,7 +232,7 @@ export function SendPager() {               disabled={isSubmitting}               onClick={(e) => {                 e.preventDefault();-                handleConfirm();+                void handleConfirm();               }}             >               {isSubmitting ? (diff --git a/frontend/apps/artcraft-dashboard/src/pages/SpendEvents.tsx b/frontend/apps/artcraft-dashboard/src/pages/SpendEvents.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/SpendEvents.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/SpendEvents.tsx@@ -24,6 +24,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  const COLUMN_COUNT = 8; @@ -84,9 +85,7 @@ export function SpendEvents() {   const [nextOffset, setNextOffset] = useState<number | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (offset?: number | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -123,12 +122,12 @@ export function SpendEvents() {   const handleRefresh = () => {     setEvents([]);     setNextOffset(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -136,26 +135,11 @@ export function SpendEvents() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextOffset != null) {-      loadData(nextOffset, true);+      void loadData(nextOffset, true);     }   }, [nextOffset, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/StaffAuditLogs.tsx b/frontend/apps/artcraft-dashboard/src/pages/StaffAuditLogs.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/StaffAuditLogs.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/StaffAuditLogs.tsx@@ -22,6 +22,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  export function StaffAuditLogs() {   usePageTitle("Staff Audit Logs");@@ -32,9 +33,7 @@ export function StaffAuditLogs() {   const [nextCursor, setNextCursor] = useState<string | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: string | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -71,12 +70,12 @@ export function StaffAuditLogs() {   const handleRefresh = () => {     setLogs([]);     setNextCursor(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -84,26 +83,11 @@ export function StaffAuditLogs() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/SubscriberSignups.tsx b/frontend/apps/artcraft-dashboard/src/pages/SubscriberSignups.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/SubscriberSignups.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/SubscriberSignups.tsx@@ -23,6 +23,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  function getSubscriptionStatusBadge(status: string | null) {   if (!status) return { className: "text-muted-foreground", label: "Unknown" };@@ -49,9 +50,7 @@ export function SubscriberSignups() {   const [nextCursor, setNextCursor] = useState<number | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: number | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -86,12 +85,12 @@ export function SubscriberSignups() {   const handleRefresh = () => {     setUsers([]);     setNextCursor(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -99,26 +98,11 @@ export function SubscriberSignups() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/TopSpenders.tsx b/frontend/apps/artcraft-dashboard/src/pages/TopSpenders.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/TopSpenders.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/TopSpenders.tsx@@ -23,6 +23,7 @@ import {   IconTrophy, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  const COLUMN_COUNT = 7; @@ -58,9 +59,7 @@ export function TopSpenders() {   const [nextOffset, setNextOffset] = useState<number | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (     timeWindow: string,     offset?: number | null,@@ -102,18 +101,18 @@ export function TopSpenders() {     setWindow(value);     setSpenders([]);     setNextOffset(null);-    loadData(value);+    void loadData(value);   };    const handleRefresh = () => {     setSpenders([]);     setNextOffset(null);-    loadData(window);+    void loadData(window);   };    useEffect(() => {     cancelledRef.current = false;-    loadData(DEFAULT_WINDOW);+    void loadData(DEFAULT_WINDOW);     return () => {       cancelledRef.current = true;     };@@ -121,26 +120,11 @@ export function TopSpenders() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextOffset != null) {-      loadData(window, nextOffset, true);+      void loadData(window, nextOffset, true);     }   }, [window, nextOffset, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserCreations.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserCreations.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserCreations.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserCreations.tsx@@ -110,7 +110,7 @@ export function UserCreations() {       setIsLoading(false);     } -    init();+    void init();     return () => {       cancelledRef.current = true;     };diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserDebugLogs.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserDebugLogs.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserDebugLogs.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserDebugLogs.tsx@@ -51,7 +51,7 @@ export function UserDebugLogs() {   const loadingRef = useRef(false);    const copy = (value: string, id: string) => {-    navigator.clipboard.writeText(value).then(() => {+    void navigator.clipboard.writeText(value).then(() => {       setCopiedId(id);       setTimeout(() => setCopiedId((c) => (c === id ? null : c)), 2000);     });@@ -102,7 +102,7 @@ export function UserDebugLogs() {      if (userToken) {       const api = new ModerationApi();-      api.UserLookup(userToken).then((resp) => {+      void api.UserLookup(userToken).then((resp) => {         if (resp.success && resp.data?.maybe_user) {           setUser({             display_name: resp.data.maybe_user.display_name,diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserEmailChanges.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserEmailChanges.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserEmailChanges.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserEmailChanges.tsx@@ -32,6 +32,7 @@ import {   IconLoader2,   IconPencil, } from "@tabler/icons-react";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  function gravatarUrl(hash?: string): string | undefined {   return hash ? `https://gravatar.com/avatar/${hash}?s=48&d=404` : undefined;@@ -80,9 +81,7 @@ export function UserEmailChanges() {   const [nextCursor, setNextCursor] = useState<string | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadHistory = async (     username: string,     cursor?: string | null,@@ -123,40 +122,25 @@ export function UserEmailChanges() {    const loadMore = useCallback(() => {     if (user && !isLoadingMore && nextCursor != null) {-      loadHistory(user.username, nextCursor, true);+      void loadHistory(user.username, nextCursor, true);     }   }, [user, nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const handleSelect = (next: ModeratorUserLookupResponse | null) => {     setUser(next);     setCurrentEmail(next?.email_address ?? null);     setChanges([]);     setNextCursor(null);-    if (next) loadHistory(next.username);+    if (next) void loadHistory(next.username);   };    const handleChangeSuccess = (newEmail: string) => {     setCurrentEmail(newEmail);     setUser((prev) => (prev ? { ...prev, email_address: newEmail } : prev));     toast.success("Email address changed");-    if (user) loadHistory(user.username);+    if (user) void loadHistory(user.username);   };    const { ref: tableRef, height: tableHeight } = useTableHeight();diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserProfile.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserProfile.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserProfile.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserProfile.tsx@@ -150,7 +150,7 @@ export function UserProfile() {   );    const copyToClipboard = (text: string, id: string) => {-    navigator.clipboard.writeText(text).then(() => {+    void navigator.clipboard.writeText(text).then(() => {       setCopiedStates((prev) => ({ ...prev, [id]: true }));       setTimeout(() => {         setCopiedStates((prev) => ({ ...prev, [id]: false }));@@ -185,7 +185,7 @@ export function UserProfile() {           setUser(fetchedUser);            setIsWalletsLoading(true);-          modApi.ListUserWallets(fetchedUser.token).then((resp) => {+          void modApi.ListUserWallets(fetchedUser.token).then((resp) => {             if (cancelled) return;             if (resp.success && resp.data) {               setWallets(resp.data.wallets);@@ -194,7 +194,7 @@ export function UserProfile() {           });            setIsJobsLoading(true);-          modApi.ListUserJobs(fetchedUser.token).then((resp) => {+          void modApi.ListUserJobs(fetchedUser.token).then((resp) => {             if (cancelled) return;             if (resp.success && resp.data) {               setJobs(resp.data.jobs);@@ -220,7 +220,7 @@ export function UserProfile() {       }     } -    loadData();+    void loadData();     return () => {       cancelled = true;     };diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserReferrals.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserReferrals.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserReferrals.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserReferrals.tsx@@ -25,6 +25,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  function formatDate(iso: string) {   return new Date(iso).toLocaleString("en-US", {@@ -52,9 +53,7 @@ export function UserReferrals() {   const [nextCursor, setNextCursor] = useState<string | null>(null);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: string | null, append = false) => {     if (!username) return;     if (!append) {@@ -92,12 +91,12 @@ export function UserReferrals() {   const handleRefresh = () => {     setReferrals([]);     setNextCursor(null);-    loadData();+    void loadData();   };    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -105,26 +104,11 @@ export function UserReferrals() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore, username]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserSearch.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserSearch.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserSearch.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserSearch.tsx@@ -104,7 +104,7 @@ export function UserSearch() {         setSearchResult(foundUser);          setIsWalletsLoading(true);-        api.ListUserWallets(foundUser.token).then((walletResp) => {+        void api.ListUserWallets(foundUser.token).then((walletResp) => {           if (walletResp.success && walletResp.data) {             setWallets(walletResp.data.wallets);           }@@ -122,7 +122,7 @@ export function UserSearch() {    useEffect(() => {     if (queryParam) {-      performSearch(queryParam);+      void performSearch(queryParam);     }   }, []); @@ -137,7 +137,7 @@ export function UserSearch() {   const copyProfileLink = () => {     if (!searchResult) return;     const profileUrl = `${window.location.origin}/user/profile/${searchResult.username}`;-    navigator.clipboard.writeText(profileUrl).then(() => {+    void navigator.clipboard.writeText(profileUrl).then(() => {       setIsCopied(true);       setTimeout(() => setIsCopied(false), 2000);     });@@ -382,7 +382,7 @@ export function UserSearch() {             onOpenChange={setGiveCreditsOpen}             onSuccess={() => {               const api = new ModerationApi();-              api.ListUserWallets(searchResult.token).then((resp) => {+              void api.ListUserWallets(searchResult.token).then((resp) => {                 if (resp.success && resp.data) {                   setWallets(resp.data.wallets);                 }diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserSignups.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserSignups.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserSignups.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserSignups.tsx@@ -27,6 +27,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  export function UserSignups() {   usePageTitle("User Signups");@@ -41,9 +42,7 @@ export function UserSignups() {   const [banDialogOpen, setBanDialogOpen] = useState(false);    const cancelledRef = useRef(false);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadData = async (cursor?: number | null, append = false) => {     if (!append) {       setIsLoading(true);@@ -81,7 +80,7 @@ export function UserSignups() {     setUsers([]);     setNextCursor(null);     setSelectedUsernames(new Set());-    loadData();+    void loadData();   };    const toggleUser = (username: string, checked: boolean) => {@@ -112,7 +111,7 @@ export function UserSignups() {    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };@@ -120,26 +119,11 @@ export function UserSignups() {    const loadMore = useCallback(() => {     if (!isLoadingMore && nextCursor != null) {-      loadData(nextCursor, true);+      void loadData(nextCursor, true);     }   }, [nextCursor, isLoadingMore]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();--    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );--    if (sentinelRef.current) {-      observerRef.current.observe(sentinelRef.current);-    }--    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserSpendHistory.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserSpendHistory.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserSpendHistory.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserSpendHistory.tsx@@ -26,6 +26,7 @@ import {   IconLoader2, } from "@tabler/icons-react"; import { usePageTitle } from "@/hooks/usePageTitle";+import { useInfiniteScroll } from "@/hooks/useInfiniteScroll";  const PAGE_SIZE = 200; const COLUMN_COUNT = 8;@@ -62,9 +63,7 @@ export function UserSpendHistory() {    const cancelledRef = useRef(false);   const tokenRef = useRef<string | null>(null);-  const observerRef = useRef<IntersectionObserver | null>(null);-  const sentinelRef = useRef<HTMLTableRowElement | null>(null);-+     const loadPage = useCallback(async (offset: number, append: boolean) => {     const token = tokenRef.current;     if (!token) return;@@ -94,7 +93,7 @@ export function UserSpendHistory() {     if (!username) return;     cancelledRef.current = false; -    (async () => {+    void (async () => {       setIsLoading(true);       setError(null);       setRows([]);@@ -130,17 +129,7 @@ export function UserSpendHistory() {     }   }, [nextOffset, isLoadingMore, loadPage]); -  useEffect(() => {-    if (observerRef.current) observerRef.current.disconnect();-    observerRef.current = new IntersectionObserver(-      (entries) => {-        if (entries[0].isIntersecting) loadMore();-      },-      { threshold: 0.1 },-    );-    if (sentinelRef.current) observerRef.current.observe(sentinelRef.current);-    return () => observerRef.current?.disconnect();-  }, [loadMore]);+  const sentinelRef = useInfiniteScroll<HTMLTableRowElement>(loadMore);    const { ref: tableRef, height: tableHeight } = useTableHeight(); diff --git a/frontend/apps/artcraft-dashboard/src/pages/UserSpendSummary.tsx b/frontend/apps/artcraft-dashboard/src/pages/UserSpendSummary.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/UserSpendSummary.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/UserSpendSummary.tsx@@ -108,7 +108,7 @@ export function UserSpendSummary() {     if (!username) return;     cancelledRef.current = false; -    (async () => {+    void (async () => {       setIsLoading(true);       setError(null);       try {diff --git a/frontend/apps/artcraft-dashboard/src/pages/WalletHistory.tsx b/frontend/apps/artcraft-dashboard/src/pages/WalletHistory.tsx--- a/frontend/apps/artcraft-dashboard/src/pages/WalletHistory.tsx+++ b/frontend/apps/artcraft-dashboard/src/pages/WalletHistory.tsx@@ -105,7 +105,7 @@ export function WalletHistory() {   const totalCredits = totalMonthly + totalBanked;    const copyToClipboard = (text: string) => {-    navigator.clipboard.writeText(text).then(() => {+    void navigator.clipboard.writeText(text).then(() => {       setCopiedToken(text);       setTimeout(() => setCopiedToken(null), 2000);     });@@ -170,7 +170,7 @@ export function WalletHistory() {    useEffect(() => {     cancelledRef.current = false;-    loadData();+    void loadData();     return () => {       cancelledRef.current = true;     };diff --git a/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx b/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx--- a/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx+++ b/frontend/apps/artcraft-webapp/src/components/auth/signup-form.tsx@@ -110,7 +110,7 @@ export const SignupForm = ({         className="space-y-4"         onSubmit={(e) => {           e.preventDefault();-          handleSignup();+          void handleSignup();         }}       >         {error && (diff --git a/frontend/apps/artcraft-webapp/src/components/lightbox/LightboxDetails.tsx b/frontend/apps/artcraft-webapp/src/components/lightbox/LightboxDetails.tsx--- a/frontend/apps/artcraft-webapp/src/components/lightbox/LightboxDetails.tsx+++ b/frontend/apps/artcraft-webapp/src/components/lightbox/LightboxDetails.tsx@@ -120,7 +120,7 @@ export function LightboxDetails({           audioPlayerRef.current = null;         };         audioPlayerRef.current = audio;-        audio.play();+        void audio.play();         setPlayingAudioToken(token);       }     },diff --git a/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx b/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx--- a/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx+++ b/frontend/apps/artcraft-webapp/src/components/prompt-box/MediaReferenceRow.tsx@@ -381,7 +381,7 @@ const AudioRefTile = ({         el.volume = 0.2;         audioElRef.current = el;         el.onended = () => setIsPlaying(false);-        el.play();+        void el.play();         setIsPlaying(true);       }     },diff --git a/frontend/apps/artcraft-webapp/src/components/settings-modal/ApiKeySection.tsx b/frontend/apps/artcraft-webapp/src/components/settings-modal/ApiKeySection.tsx--- a/frontend/apps/artcraft-webapp/src/components/settings-modal/ApiKeySection.tsx+++ b/frontend/apps/artcraft-webapp/src/components/settings-modal/ApiKeySection.tsx@@ -73,7 +73,7 @@ export function ApiKeySection(_props: ApiKeySectionProps) {   };    useEffect(() => {-    load(true);+    void load(true);     // Load once on mount; pagination drives subsequent loads explicitly.     // eslint-disable-next-line react-hooks/exhaustive-deps   }, []);@@ -82,7 +82,7 @@ export function ApiKeySection(_props: ApiKeySectionProps) {     setCreating(false);     // Reveal the full secret once; the truncated key then appears in the list.     setCreatedKey(created);-    load(true);+    void load(true);   };    const handleDeleteConfirmed = async () => {diff --git a/frontend/apps/artcraft-webapp/src/components/settings-modal/BillingSection.tsx b/frontend/apps/artcraft-webapp/src/components/settings-modal/BillingSection.tsx--- a/frontend/apps/artcraft-webapp/src/components/settings-modal/BillingSection.tsx+++ b/frontend/apps/artcraft-webapp/src/components/settings-modal/BillingSection.tsx@@ -36,7 +36,7 @@ export function BillingSection({ onCloseModal }: BillingSectionProps) {         if (!cancelled) setIsLoading(false);       }     };-    fetchSubscription();+    void fetchSubscription();     return () => {       cancelled = true;     };diff --git a/frontend/apps/artcraft-webapp/src/components/topbar/TopBarActions.tsx b/frontend/apps/artcraft-webapp/src/components/topbar/TopBarActions.tsx--- a/frontend/apps/artcraft-webapp/src/components/topbar/TopBarActions.tsx+++ b/frontend/apps/artcraft-webapp/src/components/topbar/TopBarActions.tsx@@ -73,8 +73,8 @@ export function TopBarActions({ className }: { className?: string }) {    useEffect(() => {     if (user) {-      fetchCredits().then(setCredits);-      fetchHasPaidPlan().then(setHasPaidPlan);+      void fetchCredits().then(setCredits);+      void fetchHasPaidPlan().then(setHasPaidPlan);     } else {       setCredits(null);       setHasPaidPlan(null);@@ -83,7 +83,7 @@ export function TopBarActions({ className }: { className?: string }) {    useEffect(() => {     const handler = () => {-      fetchCredits().then(setCredits);+      void fetchCredits().then(setCredits);     };     window.addEventListener("credits-change", handler);     return () => window.removeEventListener("credits-change", handler);diff --git a/frontend/apps/artcraft-webapp/src/components/topbar/task-queue.tsx b/frontend/apps/artcraft-webapp/src/components/topbar/task-queue.tsx--- a/frontend/apps/artcraft-webapp/src/components/topbar/task-queue.tsx+++ b/frontend/apps/artcraft-webapp/src/components/topbar/task-queue.tsx@@ -195,7 +195,7 @@ const CopyPromptButton = ({ prompt }: { prompt: string }) => {         aria-label="Copy prompt"         onClick={(e) => {           e.stopPropagation();-          navigator.clipboard.writeText(prompt);+          void navigator.clipboard.writeText(prompt);           setCopied(true);           setTimeout(() => setCopied(false), 3000);         }}@@ -930,11 +930,11 @@ export const TaskQueue = () => {       }     }; -    load();+    void load();     const intervalId = setInterval(load, 5000);      const handleTaskUpdate = () => {-      if (!cancelled) load();+      if (!cancelled) void load();     };     window.addEventListener("task-queue-update", handleTaskUpdate); diff --git a/frontend/apps/artcraft-webapp/src/lib/session.ts b/frontend/apps/artcraft-webapp/src/lib/session.ts--- a/frontend/apps/artcraft-webapp/src/lib/session.ts+++ b/frontend/apps/artcraft-webapp/src/lib/session.ts@@ -90,7 +90,7 @@ function ensureAuthChangeListener(): void {   listenerAttached = true;   window.addEventListener("auth-change", () => {     invalidateSession();-    refreshSession(true);+    void refreshSession(true);   }); } @@ -114,7 +114,7 @@ export function useSession(): UseSessionResult {     ensureAuthChangeListener();     if (!bootRequested) {       bootRequested = true;-      refreshSession();+      void refreshSession();     }   }, []); diff --git a/frontend/apps/artcraft-webapp/src/pages/checkout/success.tsx b/frontend/apps/artcraft-webapp/src/pages/checkout/success.tsx--- a/frontend/apps/artcraft-webapp/src/pages/checkout/success.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/checkout/success.tsx@@ -64,7 +64,7 @@ const CheckoutSuccess = () => {       setIsCheckingOnboarding(false);     }; -    checkOnboarding();+    void checkOnboarding();   }, [searchParams]);    if (isCheckingOnboarding) {diff --git a/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts b/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts--- a/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts+++ b/frontend/apps/artcraft-webapp/src/pages/create-object/components/mesh-upload.ts@@ -25,7 +25,7 @@ export async function uploadViewImage( ): Promise<RefImage | null> {   const url = await readAsDataUrl(file);   return new Promise((resolve) => {-    uploadImage({+    void uploadImage({       title: `${label.toLowerCase()}-view-${Math.random().toString(36).substring(2, 10)}`,       assetFile: file,       progressCallback: (state) => {diff --git a/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx b/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx--- a/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/create-vfx/create-vfx.tsx@@ -88,7 +88,7 @@ export default function CreateVFX() {       pollers.set(resultId, cancel);     }; -    (async () => {+    void (async () => {       try {         const sessionJobs = await listSessionBackgroundChangeJobs();         if (cancelled) return;diff --git a/frontend/apps/artcraft-webapp/src/pages/forgot-password/forgot-password.tsx b/frontend/apps/artcraft-webapp/src/pages/forgot-password/forgot-password.tsx--- a/frontend/apps/artcraft-webapp/src/pages/forgot-password/forgot-password.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/forgot-password/forgot-password.tsx@@ -63,7 +63,7 @@ const ForgotPassword = () => {                 className="space-y-4"                 onSubmit={(e) => {                   e.preventDefault();-                  handleRequestReset();+                  void handleRequestReset();                 }}               >                 {error && (diff --git a/frontend/apps/artcraft-webapp/src/pages/forgot-password/verify-reset.tsx b/frontend/apps/artcraft-webapp/src/pages/forgot-password/verify-reset.tsx--- a/frontend/apps/artcraft-webapp/src/pages/forgot-password/verify-reset.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/forgot-password/verify-reset.tsx@@ -130,7 +130,7 @@ const VerifyReset = () => {                 className="space-y-4"                 onSubmit={(e) => {                   e.preventDefault();-                  handleRedeemReset();+                  void handleRedeemReset();                 }}               >                 {error && (diff --git a/frontend/apps/artcraft-webapp/src/pages/frame-extractor/frame-extractor.tsx b/frontend/apps/artcraft-webapp/src/pages/frame-extractor/frame-extractor.tsx--- a/frontend/apps/artcraft-webapp/src/pages/frame-extractor/frame-extractor.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/frame-extractor/frame-extractor.tsx@@ -133,7 +133,7 @@ export default function FrameExtractor() {     if (!token) return;     setSearchParams({}, { replace: true }); -    (async () => {+    void (async () => {       try {         const response = await new MediaFilesApi().GetMediaFileByToken({           mediaFileToken: token,diff --git a/frontend/apps/artcraft-webapp/src/pages/library/library.tsx b/frontend/apps/artcraft-webapp/src/pages/library/library.tsx--- a/frontend/apps/artcraft-webapp/src/pages/library/library.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/library/library.tsx@@ -348,7 +348,7 @@ export default function Library() {    // ── Auth ──────────────────────────────────────────────────────────────────   useEffect(() => {-    (async () => {+    void (async () => {       const usersApi = new UsersApi();       const response = await usersApi.GetSession();       if (response.success && response.data?.loggedIn && response.data.user) {diff --git a/frontend/apps/artcraft-webapp/src/pages/login/login.tsx b/frontend/apps/artcraft-webapp/src/pages/login/login.tsx--- a/frontend/apps/artcraft-webapp/src/pages/login/login.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/login/login.tsx@@ -82,7 +82,7 @@ const Login = () => {         className="space-y-4"         onSubmit={(e) => {           e.preventDefault();-          handleLogin();+          void handleLogin();         }}       >         {error && (diff --git a/frontend/apps/artcraft-webapp/src/pages/onboarding/onboarding.tsx b/frontend/apps/artcraft-webapp/src/pages/onboarding/onboarding.tsx--- a/frontend/apps/artcraft-webapp/src/pages/onboarding/onboarding.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/onboarding/onboarding.tsx@@ -36,7 +36,7 @@ const Onboarding = () => {   const [error, setError] = useState("");    useEffect(() => {-    checkOnboardingStatus();+    void checkOnboardingStatus();   }, []);    const getSuccessUrl = (afterUsernameStep = false) => {diff --git a/frontend/apps/artcraft-webapp/src/pages/pagescene/demo-output-overlay.tsx b/frontend/apps/artcraft-webapp/src/pages/pagescene/demo-output-overlay.tsx--- a/frontend/apps/artcraft-webapp/src/pages/pagescene/demo-output-overlay.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/pagescene/demo-output-overlay.tsx@@ -72,7 +72,7 @@ export function DemoOutputOverlay({     setVideo(null);     setView(videoToken ? "video" : "image"); -    (async () => {+    void (async () => {       const [imageResult, videoResult] = await Promise.all([         imageToken ? resolveAsset(imageToken) : Promise.resolve(null),         videoToken ? resolveAsset(videoToken) : Promise.resolve(null),diff --git a/frontend/apps/artcraft-webapp/src/pages/pagescene/splash/SceneSplashCard.tsx b/frontend/apps/artcraft-webapp/src/pages/pagescene/splash/SceneSplashCard.tsx--- a/frontend/apps/artcraft-webapp/src/pages/pagescene/splash/SceneSplashCard.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/pagescene/splash/SceneSplashCard.tsx@@ -135,7 +135,7 @@ function useSceneCardImages(outputToken: string): {     setPreviewUrl(null);     setHoverUrl(null); -    (async () => {+    void (async () => {       try {         const mediaResp = await new MediaFilesApi().GetMediaFileByToken({           mediaFileToken: outputToken,diff --git a/frontend/apps/artcraft-webapp/src/pages/pagescene/use-prompt-prefill-from-output.ts b/frontend/apps/artcraft-webapp/src/pages/pagescene/use-prompt-prefill-from-output.ts--- a/frontend/apps/artcraft-webapp/src/pages/pagescene/use-prompt-prefill-from-output.ts+++ b/frontend/apps/artcraft-webapp/src/pages/pagescene/use-prompt-prefill-from-output.ts@@ -36,7 +36,7 @@ export function usePromptPrefillFromOutput(     if (!outputToken) return;     let cancelled = false; -    (async () => {+    void (async () => {       try {         const mediaResp = await new MediaFilesApi().GetMediaFileByToken({           mediaFileToken: outputToken,diff --git a/frontend/apps/artcraft-webapp/src/pages/pricing/pricing.tsx b/frontend/apps/artcraft-webapp/src/pages/pricing/pricing.tsx--- a/frontend/apps/artcraft-webapp/src/pages/pricing/pricing.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/pricing/pricing.tsx@@ -35,7 +35,7 @@ const Pricing = () => {         // not logged in       }     };-    check();+    void check();   }, []);    return (diff --git a/frontend/apps/artcraft-webapp/src/pages/set-password/set-password.tsx b/frontend/apps/artcraft-webapp/src/pages/set-password/set-password.tsx--- a/frontend/apps/artcraft-webapp/src/pages/set-password/set-password.tsx+++ b/frontend/apps/artcraft-webapp/src/pages/set-password/set-password.tsx@@ -83,7 +83,7 @@ const SetPassword = () => {             className="space-y-4"             onSubmit={(e) => {               e.preventDefault();-              handleSetPassword();+              void handleSetPassword();             }}           >             {error && (diff --git a/frontend/apps/artcraft-website-next/src/components/media/media-details.tsx b/frontend/apps/artcraft-website-next/src/components/media/media-details.tsx--- a/frontend/apps/artcraft-website-next/src/components/media/media-details.tsx+++ b/frontend/apps/artcraft-website-next/src/components/media/media-details.tsx@@ -105,7 +105,7 @@ export default function MediaDetails({ media, prompt, promptLoading, dimensions             {prompt?.maybe_positive_prompt && (               <button type="button" className="text-muted hover:text-ink" aria-label={copied === "prompt" ? "Prompt copied" : "Copy prompt"}                 onClick={() => {-                  copy(prompt.maybe_positive_prompt!, "prompt");+                  void copy(prompt.maybe_positive_prompt!, "prompt");                   trackEvent("copy_prompt", { media_type: kind });                 }}>                 {copied === "prompt" ? <CheckIcon className="h-4 w-4" /> : <CopyIcon className="h-4 w-4" />}@@ -149,7 +149,7 @@ export default function MediaDetails({ media, prompt, promptLoading, dimensions           <ArrowDownToLineIcon aria-hidden className="h-4 w-4" />{downloading ? "Downloading" : "Download file"}         </Button>         <Button variant="secondary" className="w-full" onClick={() => {-          copy(`${SHARE_URL_BASE}${encodeURIComponent(media.token)}`, "link");+          void copy(`${SHARE_URL_BASE}${encodeURIComponent(media.token)}`, "link");           trackEvent("share", { method: "copy_link", content_type: kind, item_id: media.token });         }}>           {copied === "link" ? <CheckIcon aria-hidden className="h-4 w-4" /> : <CopyIcon aria-hidden className="h-4 w-4" />}diff --git a/frontend/apps/artcraft-website-next/src/lib/use-account.ts b/frontend/apps/artcraft-website-next/src/lib/use-account.ts--- a/frontend/apps/artcraft-website-next/src/lib/use-account.ts+++ b/frontend/apps/artcraft-website-next/src/lib/use-account.ts@@ -50,7 +50,7 @@ export function useAccount(): Account {    useEffect(() => {     let live = true;-    loadAccount().then((a) => {+    void loadAccount().then((a) => {       if (live) setAccount(a);     });     return () => {diff --git a/frontend/apps/genhub/src/components/feed/media-detail.tsx b/frontend/apps/genhub/src/components/feed/media-detail.tsx--- a/frontend/apps/genhub/src/components/feed/media-detail.tsx+++ b/frontend/apps/genhub/src/components/feed/media-detail.tsx@@ -39,7 +39,7 @@ export function MediaDetail({ item }: MediaDetailProps) {   const likeCount = liked ? item.likes + 1 : item.likes;    function handleCopy() {-    navigator.clipboard.writeText(item.prompt);+    void navigator.clipboard.writeText(item.prompt);     setCopied(true);     setTimeout(() => setCopied(false), 2000);   }diff --git a/frontend/apps/genhub/src/components/layout/navbar.tsx b/frontend/apps/genhub/src/components/layout/navbar.tsx--- a/frontend/apps/genhub/src/components/layout/navbar.tsx+++ b/frontend/apps/genhub/src/components/layout/navbar.tsx@@ -126,7 +126,7 @@ export function Navbar() {                 variant="ghost"                 className="justify-start"                 onClick={() => {-                  logout();+                  void logout();                   setMobileOpen(false);                 }}               >diff --git a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx--- a/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx+++ b/frontend/libs/components/create-3d-modal/src/lib/create-3d-modal.tsx@@ -94,16 +94,16 @@ export const Create3dModal = ({ onModelComplete }: Create3dModalProps = {}) => {       });        if (isUnmounted) {-        unlisten.then((f) => f()); // Unsubscribe if unmounted early.+        void unlisten.then((f) => f()); // Unsubscribe if unmounted early.       }     }; -    setup();+    void setup();      return () => {       isUnmounted = true;       if (unlisten) {-        unlisten.then((f) => f());+        void unlisten.then((f) => f());       }     };   }, []);diff --git a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx--- a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx+++ b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx@@ -948,7 +948,7 @@ export const GalleryModal = React.memo(       // couldn't see it — making the library button appear broken.       galleryModalDraggingUnder.value = false;       let cancelled = false;-      (async () => {+      void (async () => {         setUsernameError(false);         const name = await fetchCurrentUsername();         if (cancelled) return;diff --git a/frontend/libs/components/generation-list/src/lib/media-prompt-token-cache.ts b/frontend/libs/components/generation-list/src/lib/media-prompt-token-cache.ts--- a/frontend/libs/components/generation-list/src/lib/media-prompt-token-cache.ts+++ b/frontend/libs/components/generation-list/src/lib/media-prompt-token-cache.ts@@ -90,7 +90,7 @@ export function useMediaPromptTokens(   const key = [...new Set(cleaned)].sort().join(",");    useEffect(() => {-    if (key.length > 0) fetchMissing(key.split(","));+    if (key.length > 0) void fetchMissing(key.split(","));   }, [key]);    const result = new Map<string, string>();diff --git a/frontend/libs/components/generation-list/src/lib/prompts-cache.ts b/frontend/libs/components/generation-list/src/lib/prompts-cache.ts--- a/frontend/libs/components/generation-list/src/lib/prompts-cache.ts+++ b/frontend/libs/components/generation-list/src/lib/prompts-cache.ts@@ -63,7 +63,7 @@ export function usePrompts(   const key = [...new Set(cleaned)].sort().join(",");    useEffect(() => {-    if (key.length > 0) fetchMissing(key.split(","));+    if (key.length > 0) void fetchMissing(key.split(","));   }, [key]);    const result = new Map<string, Prompts>();diff --git a/frontend/libs/components/help-menu/src/lib/help-menu.tsx b/frontend/libs/components/help-menu/src/lib/help-menu.tsx--- a/frontend/libs/components/help-menu/src/lib/help-menu.tsx+++ b/frontend/libs/components/help-menu/src/lib/help-menu.tsx@@ -42,7 +42,7 @@ const NewsView = ({ onBack }: { onBack: () => void }) => {         console.warn("Failed to fetch news feed, using bundled content.", e);       }     };-    fetchNews();+    void fetchNews();   }, []);    const selectedPost = useMemo(@@ -312,7 +312,7 @@ export function HelpMenuButton({       });     }; -    run();+    void run();      return () => {       if (progressTimerRef.current) {diff --git a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx--- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx+++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx@@ -179,7 +179,7 @@ export function LightboxModal({           audioPlayerRef.current = null;         };         audioPlayerRef.current = audio;-        audio.play();+        void audio.play();         setPlayingAudioToken(token);       }     },diff --git a/frontend/libs/components/lightbox-modal/src/lib/tags/TagsSection.tsx b/frontend/libs/components/lightbox-modal/src/lib/tags/TagsSection.tsx--- a/frontend/libs/components/lightbox-modal/src/lib/tags/TagsSection.tsx+++ b/frontend/libs/components/lightbox-modal/src/lib/tags/TagsSection.tsx@@ -83,7 +83,7 @@ export function TagsSection({   useEffect(() => {     if (currentUsername !== undefined) return;     let cancelled = false;-    resolveSessionUsername().then((username) => {+    void resolveSessionUsername().then((username) => {       if (!cancelled) setResolvedUsername(username);     });     return () => {@@ -133,7 +133,7 @@ export function TagsSection({     }     ownTagsLoadedRef.current = true;     let cancelled = false;-    (async () => {+    void (async () => {       const all: TagDetails[] = [];       let cursor: string | undefined = undefined;       for (let page = 0; page < 50; page++) {diff --git a/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx b/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx--- a/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx+++ b/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx@@ -1040,7 +1040,7 @@ const PageDraw = ({       await tryFit();     }; -    autoFitCanvas();+    void autoFitCanvas();     // eslint-disable-next-line react-hooks/exhaustive-deps   }, [baseImageBitmap, baseImageInfo?.isBlankCanvas]); diff --git a/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx b/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx--- a/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx+++ b/frontend/libs/components/pagedraw/src/lib/PaintSurface.tsx@@ -1501,7 +1501,7 @@ export const PaintSurface = ({    // Load the checkboard image for transparency background   useEffect(() => {-    loadImageFromUrl(checkerboard).then(setCheckerImage);+    void loadImageFromUrl(checkerboard).then(setCheckerImage);   }, []);    // Separate function to render transformers on their own layerdiff --git a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts--- a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts+++ b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts@@ -1167,7 +1167,7 @@ export const useSceneStore = create<SceneState>((set, get, store) => ({                 console.error("Failed to restore image:", error);               }             };-            loadImage();+            void loadImage();           }            return node;diff --git a/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx b/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx--- a/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx+++ b/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx@@ -386,7 +386,7 @@ export const Stage3DBody = ({   useEffect(() => {     const handler = onImageDrop(       (item: GalleryItem, position: { x: number; y: number }) => {-        (async () => {+        void (async () => {           if (!editor) {             console.warn("Cannot drop asset: editor engine not ready");             return;diff --git a/frontend/libs/components/pagescene/src/lib/actions/toggleObjectVisibility.ts b/frontend/libs/components/pagescene/src/lib/actions/toggleObjectVisibility.ts--- a/frontend/libs/components/pagescene/src/lib/actions/toggleObjectVisibility.ts+++ b/frontend/libs/components/pagescene/src/lib/actions/toggleObjectVisibility.ts@@ -14,7 +14,7 @@ export function toggleObjectVisibility(editor: Editor, uuid: string): void {   if (!obj) return;   const before = obj.visible;   editor.bus.emit(new OutlinerItemVisibilityToggledEvent(uuid));-  editor.sceneManager?.hideObject(uuid);+  void editor.sceneManager?.hideObject(uuid);   editor.history.record(     new VisibilityAction(editor, uuid, before, !before),   );diff --git a/frontend/libs/components/pagescene/src/lib/engine/editor.ts b/frontend/libs/components/pagescene/src/lib/engine/editor.ts--- a/frontend/libs/components/pagescene/src/lib/engine/editor.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/editor.ts@@ -190,7 +190,7 @@ class Editor {     // Ammo.js WASM is a global side-effect script the physics path     // depends on. The singleton ensures it's appended to document.body     // exactly once, even across Editor re-construction.-    ensureAmmoLoaded();+    void ensureAmmoLoaded();      // Version and name.     this.version = 2.0;@@ -667,9 +667,9 @@ class Editor {     };      if (!this.utils.isEmpty(cacheJson)) {-      this.save_manager.loadCache(cacheJson).then(applyIfLoaded);+      void this.save_manager.loadCache(cacheJson).then(applyIfLoaded);     } else if (!this.utils.isEmpty(sceneToken)) {-      this.save_manager.loadScene(sceneToken).then(applyIfLoaded);+      void this.save_manager.loadScene(sceneToken).then(applyIfLoaded);     } else {       this.adapter.onSceneTitleChange?.({         title: "Untitled New Scene",@@ -1065,7 +1065,7 @@ class Editor {     }      this.lastFrameTime = frameTime;-    this.renderSingleFrame();+    void this.renderSingleFrame();   }    startRenderLoop() {diff --git a/frontend/libs/components/pagescene/src/lib/engine/editor/ModalTransformController.ts b/frontend/libs/components/pagescene/src/lib/engine/editor/ModalTransformController.ts--- a/frontend/libs/components/pagescene/src/lib/engine/editor/ModalTransformController.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/editor/ModalTransformController.ts@@ -151,7 +151,7 @@ export class ModalTransformController {       }     }     this.target.position.copy(this.startPos).add(delta);-    this.editor.renderScene();+    void this.editor.renderScene();   }    private axisVector(axis: "x" | "y" | "z"): THREE.Vector3 {@@ -190,7 +190,7 @@ export class ModalTransformController {    private cancel() {     if (this.target) this.target.position.copy(this.startPos);-    this.editor.renderScene();+    void this.editor.renderScene();     this.cleanup();   } diff --git a/frontend/libs/components/pagescene/src/lib/engine/placeholder_manager.ts b/frontend/libs/components/pagescene/src/lib/engine/placeholder_manager.ts--- a/frontend/libs/components/pagescene/src/lib/engine/placeholder_manager.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/placeholder_manager.ts@@ -77,7 +77,7 @@ export class LoadingPlaceHolderManager {     if (result === undefined) {       return;     }-    result.destroyWithScene(this.scene);+    void result.destroyWithScene(this.scene);     this.messages.delete(id);   } diff --git a/frontend/libs/components/pagescene/src/lib/engine/scene.ts b/frontend/libs/components/pagescene/src/lib/engine/scene.ts--- a/frontend/libs/components/pagescene/src/lib/engine/scene.ts+++ b/frontend/libs/components/pagescene/src/lib/engine/scene.ts@@ -112,7 +112,7 @@ class Scene {   initialize() {     this.scene = new THREE.Scene();     this.placeholder_manager = new LoadingPlaceHolderManager(this.scene);-    this.placeholder_manager.initialize();+    void this.placeholder_manager.initialize();      this._createGrid();     this._create_base_lighting();diff --git a/frontend/libs/components/pricing-modal/src/lib/use-currency.ts b/frontend/libs/components/pricing-modal/src/lib/use-currency.ts--- a/frontend/libs/components/pricing-modal/src/lib/use-currency.ts+++ b/frontend/libs/components/pricing-modal/src/lib/use-currency.ts@@ -170,7 +170,7 @@ export function useCurrency(): UseCurrencyResult {    useEffect(() => {     let cancelled = false;-    fetchRates().then((r) => {+    void fetchRates().then((r) => {       if (!cancelled) {         setRates(r);         setRatesLoaded(true);diff --git a/frontend/libs/components/pricing-table/src/lib/pricing-table.tsx b/frontend/libs/components/pricing-table/src/lib/pricing-table.tsx--- a/frontend/libs/components/pricing-table/src/lib/pricing-table.tsx+++ b/frontend/libs/components/pricing-table/src/lib/pricing-table.tsx@@ -130,7 +130,7 @@ const PricingTable = ({       }     }; -    fetchUserData();+    void fetchUserData();   }, []);    const plans = includeFreediff --git a/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx--- a/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx+++ b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx@@ -102,7 +102,7 @@ const AudioRefTile = ({         el.volume = 0.2;         audioRef.current = el;         el.onended = () => setIsPlaying(false);-        el.play();+        void el.play();         setIsPlaying(true);       }     },diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBox2D.tsx@@ -347,7 +347,7 @@ export const PromptBox2D = ({     const isSubmitCombo = enterToGenerate && !e.shiftKey;     if (isSubmitCombo) {       e.preventDefault();-      handleGenerate();+      void handleGenerate();     }   }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx@@ -543,7 +543,7 @@ export const PromptBox3D = ({     const isSubmitCombo = enterToGenerate && !e.shiftKey;     if (isSubmitCombo) {       e.preventDefault();-      handleEnqueue();+      void handleEnqueue();     }   }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx@@ -352,7 +352,7 @@ export const PromptBoxAudio = ({     if (e.key !== "Enter") return;     if (enterToGenerate && !e.shiftKey) {       e.preventDefault();-      handleEnqueue();+      void handleEnqueue();     }   }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx@@ -454,7 +454,7 @@ export const PromptBoxImage = ({     const isSubmitCombo = enterToGenerate && !e.shiftKey;     if (isSubmitCombo) {       e.preventDefault();-      handleEnqueue();+      void handleEnqueue();     }   }; diff --git a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx--- a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx+++ b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx@@ -1211,7 +1211,7 @@ export const PromptBoxVideo = ({       console.error("PromptBoxVideo - enqueue failed", failure.reason);       toast.error(commandErrorMessage(failure.reason, "Failed to start video generation. Please try again."));     }-    if (acceptedIds.length) onEnqueuePressed?.(prompt, acceptedIds);+    if (acceptedIds.length) void onEnqueuePressed?.(prompt, acceptedIds);      setIsEnqueueing(false);   };@@ -1262,7 +1262,7 @@ export const PromptBoxVideo = ({         return;       } -      handleEnqueue();+      void handleEnqueue();     }   }; @@ -1374,7 +1374,7 @@ export const PromptBoxVideo = ({                       )                         return;                       if (!prompt.trim()) return;-                      handleEnqueue();+                      void handleEnqueue();                     }                   }}                   onFocus={() => setIsFocused(true)}diff --git a/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx b/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx--- a/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx+++ b/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx@@ -68,7 +68,7 @@ export const DeckCard = ({       el.volume = 0.2;       audioRef.current = el;       el.onended = () => setIsPlaying(false);-      el.play();+      void el.play();       setIsPlaying(true);     }   }, [isPlaying, item.url, stopAudio]);diff --git a/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx b/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx--- a/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx+++ b/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx@@ -524,8 +524,8 @@ export function useDeckMedia<     );      if (images.length > 0) processImageFiles(images, "start");-    if (videos.length > 0 && setReferenceVideos) processVideoFiles(videos);-    if (audios.length > 0 && setReferenceAudios) processAudioFiles(audios);+    if (videos.length > 0 && setReferenceVideos) void processVideoFiles(videos);+    if (audios.length > 0 && setReferenceAudios) void processAudioFiles(audios);   };    // maxImages of 0 means the page's model takes no image refs at all.diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx--- a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx+++ b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx@@ -270,7 +270,7 @@ export const UploadFiles3D = ({   // B's key.   useEffect(() => {     if (previewStatus.type === "OK" && canvasRef.current) {-      snapshotCanvasAsThumbnail({+      void snapshotCanvasAsThumbnail({         targetNode: canvasRef.current,         resultCallback: (blob) => {           const currentFile = fileEntries[previewIndex]?.file;@@ -503,7 +503,7 @@ export const UploadFiles3D = ({     // can only ever be a clean single-file submission; the excludedCount     // check makes that invariant explicit rather than incidental.     if (excludedCount === 0 && files.length === 1 && fileEntries.length === 1) {-      upload3DObjects({+      void upload3DObjects({         title: files[0].name.slice(0, files[0].name.lastIndexOf(".")),         assetFile: files[0],         engineCategory,@@ -521,7 +521,7 @@ export const UploadFiles3D = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: files.length }); -    upload3DObjectsBatch({+    void upload3DObjectsBatch({       files,       thumbnails: pendingThumbnails,       engineCategory,@@ -581,7 +581,7 @@ export const UploadFiles3D = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: failedFiles.length }); -    upload3DObjectsBatch({+    void upload3DObjectsBatch({       files: failedFiles,       thumbnails: failedThumbnails,       engineCategory,@@ -742,7 +742,7 @@ export const UploadFiles3D = ({                         className="hidden items-center text-xs text-white/60 transition-colors hover:text-white group-hover:inline-flex"                         onClick={(e) => {                           e.stopPropagation();-                          retrySingleFile(i);+                          void retrySingleFile(i);                         }}                         title="Retry"                       >diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModalImage/UploadFilesImage.tsx b/frontend/libs/components/upload-modal/src/lib/UploadModalImage/UploadFilesImage.tsx--- a/frontend/libs/components/upload-modal/src/lib/UploadModalImage/UploadFilesImage.tsx+++ b/frontend/libs/components/upload-modal/src/lib/UploadModalImage/UploadFilesImage.tsx@@ -117,7 +117,7 @@ export const UploadFilesImage = ({       .filter((i) => i !== -1);      if (files.length === 1 && fileEntries.length === 1) {-      uploadImage({+      void uploadImage({         title: files[0].name.slice(0, files[0].name.lastIndexOf(".")),         assetFile: files[0],         progressCallback: onUploadProgress,@@ -128,7 +128,7 @@ export const UploadFilesImage = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: files.length }); -    uploadImagesBatch({+    void uploadImagesBatch({       files,       onFileStatusChange: (batchIndex, status, errorMessage) =>         updateFileStatus(originalIndices[batchIndex], status, errorMessage),@@ -159,7 +159,7 @@ export const UploadFilesImage = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: failedFiles.length }); -    uploadImagesBatch({+    void uploadImagesBatch({       files: failedFiles,       onFileStatusChange: (batchIndex, status, errorMessage) =>         updateFileStatus(failedIndices[batchIndex], status, errorMessage),@@ -248,7 +248,7 @@ export const UploadFilesImage = ({                           className="hidden group-hover:inline-flex items-center text-xs text-white/60 hover:text-white transition-colors"                           onClick={(e) => {                             e.stopPropagation();-                            retrySingleFile(i);+                            void retrySingleFile(i);                           }}                           title="Retry"                         >diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx b/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx--- a/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx+++ b/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx@@ -127,7 +127,7 @@ export const UploadFilesSplat = ({       const canvas = canvasRef.current;       const currentFile = fileEntries[previewIndex]?.file;       const rafId = requestAnimationFrame(() => {-        snapshotCanvasAsThumbnail({+        void snapshotCanvasAsThumbnail({           targetNode: canvas,           resultCallback: (blob) => {             if (blob && currentFile) {@@ -222,7 +222,7 @@ export const UploadFilesSplat = ({     );      if (files.length === 1 && fileEntries.length === 1) {-      upload3DObjects({+      void upload3DObjects({         title: files[0].name.slice(0, files[0].name.lastIndexOf(".")),         assetFile: files[0],         engineCategory,@@ -236,7 +236,7 @@ export const UploadFilesSplat = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: files.length }); -    upload3DObjectsBatch({+    void upload3DObjectsBatch({       files,       thumbnails: pendingThumbnails,       engineCategory,@@ -277,7 +277,7 @@ export const UploadFilesSplat = ({     setIsUploading(true);     setOverallProgress({ current: 0, total: failedFiles.length }); -    upload3DObjectsBatch({+    void upload3DObjectsBatch({       files: failedFiles,       thumbnails: failedThumbnails,       engineCategory,@@ -375,7 +375,7 @@ export const UploadFilesSplat = ({                         className="hidden group-hover:inline-flex items-center text-xs text-white/60 hover:text-white transition-colors"                         onClick={(e) => {                           e.stopPropagation();-                          retrySingleFile(i);+                          void retrySingleFile(i);                         }}                         title="Retry"                       >diff --git a/frontend/libs/components/video-editor/src/lib/actions/use-editor-actions.ts b/frontend/libs/components/video-editor/src/lib/actions/use-editor-actions.ts--- a/frontend/libs/components/video-editor/src/lib/actions/use-editor-actions.ts+++ b/frontend/libs/components/video-editor/src/lib/actions/use-editor-actions.ts@@ -441,7 +441,7 @@ export function useEditorActions() {   useActionHandler(     "toggle-bookmark",     () => {-      editor.scenes.toggleBookmark({ time: editor.playback.getCurrentTime() });+      void editor.scenes.toggleBookmark({ time: editor.playback.getCurrentTime() });     },     undefined,   );diff --git a/frontend/libs/components/video-editor/src/lib/fonts/use-font-atlas.ts b/frontend/libs/components/video-editor/src/lib/fonts/use-font-atlas.ts--- a/frontend/libs/components/video-editor/src/lib/fonts/use-font-atlas.ts+++ b/frontend/libs/components/video-editor/src/lib/fonts/use-font-atlas.ts@@ -21,7 +21,7 @@ export function useFontAtlas({ open }: { open: boolean }) {     if (!open || atlas) return;      setStatus("loading");-    loadFontAtlas().then((data) => {+    void loadFontAtlas().then((data) => {       if (data) {         setAtlas(data);         setStatus("idle");@@ -34,7 +34,7 @@ export function useFontAtlas({ open }: { open: boolean }) {   const retry = useCallback(() => {     clearFontAtlasCache();     setStatus("loading");-    loadFontAtlas().then((data) => {+    void loadFontAtlas().then((data) => {       if (data) {         setAtlas(data);         setStatus("idle");diff --git a/frontend/libs/components/video-editor/src/lib/hooks/use-fullscreen.ts b/frontend/libs/components/video-editor/src/lib/hooks/use-fullscreen.ts--- a/frontend/libs/components/video-editor/src/lib/hooks/use-fullscreen.ts+++ b/frontend/libs/components/video-editor/src/lib/hooks/use-fullscreen.ts@@ -20,7 +20,7 @@ export function useFullscreen({   const toggleFullscreen = useCallback(() => {     if (!containerRef.current) return;     if (document.fullscreenElement) {-      document.exitFullscreen();+      void document.exitFullscreen();     } else {       containerRef.current.requestFullscreen();     }diff --git a/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/components/bookmarks.tsx b/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/components/bookmarks.tsx--- a/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/components/bookmarks.tsx+++ b/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/components/bookmarks.tsx@@ -313,7 +313,7 @@ function BookmarkPopoverContent({   }, [bookmark.color]);    const handleRemove = () => {-    editor.scenes.removeBookmark({ time });+    void editor.scenes.removeBookmark({ time });     onPopoverClose();   }; @@ -334,7 +334,7 @@ function BookmarkPopoverContent({       updates.duration = duration;     }     if (Object.keys(updates).length === 0) return;-    editor.scenes.updateBookmark({ time, updates });+    void editor.scenes.updateBookmark({ time, updates });   };   const maxDuration = mediaTimeToSeconds({     time:diff --git a/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/hooks/use-bookmark-drag.ts b/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/hooks/use-bookmark-drag.ts--- a/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/hooks/use-bookmark-drag.ts+++ b/frontend/libs/components/video-editor/src/lib/timeline/bookmarks/hooks/use-bookmark-drag.ts@@ -240,7 +240,7 @@ export function useBookmarkDrag({       const clampedTime =         dragState.currentTime > duration ? duration : dragState.currentTime; -      editor.scenes.moveBookmark({+      void editor.scenes.moveBookmark({         fromTime: dragState.bookmarkTime,         toTime: clampedTime,       });diff --git a/frontend/libs/tauri-api/src/lib/events/CanvasBgRemovedEvent.ts b/frontend/libs/tauri-api/src/lib/events/CanvasBgRemovedEvent.ts--- a/frontend/libs/tauri-api/src/lib/events/CanvasBgRemovedEvent.ts+++ b/frontend/libs/tauri-api/src/lib/events/CanvasBgRemovedEvent.ts@@ -22,15 +22,15 @@ export const useCanvasBgRemovedEvent = (asyncCallback: (event: CanvasBgRemovedEv       });        if (isUnmounted) {-        unlisten.then(f => f()); // Unsubscribe if unmounted early.+        void unlisten.then(f => f()); // Unsubscribe if unmounted early.       }     }; -    setup();+    void setup();          return () => {       isUnmounted = true;-      unlisten.then(f => f());+      void unlisten.then(f => f());     };    }, []);diff --git a/frontend/libs/tauri-api/src/lib/sora/useSoraLoginListener.ts b/frontend/libs/tauri-api/src/lib/sora/useSoraLoginListener.ts--- a/frontend/libs/tauri-api/src/lib/sora/useSoraLoginListener.ts+++ b/frontend/libs/tauri-api/src/lib/sora/useSoraLoginListener.ts@@ -21,7 +21,7 @@ export function useSoraLoginListener(       unlisten = stop;     }; -    setupListener();+    void setupListener();      return () => {       if (unlisten) unlisten();diff --git a/frontend/libs/tauri-utils/src/lib/useWindowControls.ts b/frontend/libs/tauri-utils/src/lib/useWindowControls.ts--- a/frontend/libs/tauri-utils/src/lib/useWindowControls.ts+++ b/frontend/libs/tauri-utils/src/lib/useWindowControls.ts@@ -16,7 +16,7 @@ export function useTauriWindowControls() {     let unlistenUnmax: (() => void) | undefined;     let unlistenResize: (() => void) | undefined; -    (async () => {+    void (async () => {       try {         const win = await getCurrentWindowSafe();         setIsMaximized(await win.isMaximized());