diff --git a/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx b/frontend/libs/components/gallery-modal/src/lib/gallery-modal.tsx index f04448e0f2..b17abae8fa 100644 --- 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 index b0176357c6..b387e48cce 100644 --- 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((a, b) => a.localeCompare(b)).join(","); useEffect(() => { - if (key.length > 0) fetchMissing(key.split(",")); + if (key.length > 0) void fetchMissing(key.split(",")); }, [key]); const result = new Map(); diff --git a/frontend/libs/components/generation-list/src/lib/prompts-cache.ts b/frontend/libs/components/generation-list/src/lib/prompts-cache.ts index 070086fcd7..b6e5bbb38a 100644 --- 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((a, b) => a.localeCompare(b)).join(","); useEffect(() => { - if (key.length > 0) fetchMissing(key.split(",")); + if (key.length > 0) void fetchMissing(key.split(",")); }, [key]); const result = new Map(); diff --git a/frontend/libs/components/help-menu/src/lib/help-menu.tsx b/frontend/libs/components/help-menu/src/lib/help-menu.tsx index 89e0da5c5b..298dc3e686 100644 --- a/frontend/libs/components/help-menu/src/lib/help-menu.tsx +++ b/frontend/libs/components/help-menu/src/lib/help-menu.tsx @@ -43,7 +43,7 @@ const NewsView = ({ onBack }: { onBack: () => void }) => { console.warn("Failed to fetch news feed, using bundled content.", e); } }; - fetchNews(); + void fetchNews(); }, []); const selectedPost = useMemo( @@ -316,7 +316,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 index 8bb9174d94..627c3ce279 100644 --- a/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx +++ b/frontend/libs/components/lightbox-modal/src/lib/lightbox-modal.tsx @@ -180,7 +180,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 index 936932f68c..dc2d84222d 100644 --- 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 index 55d69c7c3c..ff8d5c1d5c 100644 --- a/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx +++ b/frontend/libs/components/pagedraw/src/lib/PageDraw.tsx @@ -1041,7 +1041,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 index c9d8f9f804..86f476313a 100644 --- 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 layer diff --git a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts index d975453b8c..12db75a0c0 100644 --- a/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts +++ b/frontend/libs/components/pagedraw/src/lib/stores/SceneState.ts @@ -1165,7 +1165,7 @@ export const useSceneStore = create((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 index ac46b740cf..99d13962c5 100644 --- a/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx +++ b/frontend/libs/components/pagescene/src/lib/Stage3DBody.tsx @@ -387,7 +387,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 index 54591584c5..b7fc5f9f8d 100644 --- 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 index 075cb39d43..98d127cee6 100644 --- 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 index 9e3f0ea2b5..de6457f16e 100644 --- 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 index ac824f5140..9c8156e203 100644 --- 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 index bc89e1723e..39bfa30971 100644 --- a/frontend/libs/components/pagescene/src/lib/engine/scene.ts +++ b/frontend/libs/components/pagescene/src/lib/engine/scene.ts @@ -111,7 +111,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 index 3d81a64f12..64a0fae864 100644 --- 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/promptbox/src/lib/ImagePromptRow.tsx b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx index 873959e901..358ad77232 100644 --- a/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx +++ b/frontend/libs/components/promptbox/src/lib/ImagePromptRow.tsx @@ -103,7 +103,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 index e552fb7ede..6bfba3eb9e 100644 --- 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 index eab9cd347f..86208dfa07 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBox3D.tsx @@ -537,7 +537,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 index 8473ce4ae6..9b05e164c6 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBoxAudio.tsx @@ -353,7 +353,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 index 40827a1d6f..603fdbfe64 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBoxImage.tsx @@ -460,7 +460,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 index ae3a190344..11ad2145dc 100644 --- a/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx +++ b/frontend/libs/components/promptbox/src/lib/PromptBoxVideo.tsx @@ -1230,7 +1230,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); }; @@ -1281,7 +1281,7 @@ export const PromptBoxVideo = ({ return; } - handleEnqueue(); + void handleEnqueue(); } }; @@ -1391,7 +1391,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 index 63771cc05b..eb1bddb444 100644 --- a/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx +++ b/frontend/libs/components/promptbox/src/lib/deck/DeckCard.tsx @@ -69,7 +69,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 index f2945ecccc..a342d388be 100644 --- a/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx +++ b/frontend/libs/components/promptbox/src/lib/deck/useDeckMedia.tsx @@ -516,8 +516,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/provider-setup-modal/src/lib/provider-setup-modal.tsx b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx index dc5be93da5..974dafbe89 100644 --- a/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx +++ b/frontend/libs/components/provider-setup-modal/src/lib/provider-setup-modal.tsx @@ -92,7 +92,7 @@ export function ProviderSetupModal({ icon={ArrowRightIcon} iconFlip={true} onClick={() => { - buttonOnClick(); + void buttonOnClick(); }} > {modalButtonText} diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AboutSettingsPane.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AboutSettingsPane.tsx index 7453490152..1f58906206 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/AboutSettingsPane.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/AboutSettingsPane.tsx @@ -27,7 +27,7 @@ export const AboutSettingsPane = (args: AboutSettingsPaneProps) => { const prefs = await GetAppInfo(); setAppInfo(prefs.payload); }; - fetchData(); + void fetchData(); }, []); const handleVersionClick = () => { diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/AccountSettingsPane.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/AccountSettingsPane.tsx index c430de7f0c..bf13584d15 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/AccountSettingsPane.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/AccountSettingsPane.tsx @@ -1,6 +1,6 @@ import { ArtcraftAccountBlock } from "./ArtcraftAccountBlock"; -import { MidjourneyAccountBlock } from "./MidjourneyAccountBlock"; -import { GrokAccountBlock } from "./GrokAccountBlock"; +import { ProviderAccountBlock } from "./ProviderAccountBlock"; +import { GrokGetCredentialInfo, MidjourneyGetCredentialInfo } from "@storyteller/tauri-api"; interface AccountSettingsPaneProps { globalAccountLogoutCallback: () => void; @@ -15,9 +15,17 @@ export const AccountSettingsPane = ({ globalAccountLogoutCallback={globalAccountLogoutCallback} />
- +
- + ); }; diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ArtcraftAccountBlock.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ArtcraftAccountBlock.tsx index c0dbf3cef2..ff05bf14c5 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ArtcraftAccountBlock.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ArtcraftAccountBlock.tsx @@ -40,7 +40,7 @@ export const ArtcraftAccountBlock = ({ setIsCheckingArtcraftSession(false); } }; - fetchSession(); + void fetchSession(); }, []); const handleArtcraftButton = async () => { diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/GrokAccountBlock.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/GrokAccountBlock.tsx deleted file mode 100644 index 14e3b0d363..0000000000 --- a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/GrokAccountBlock.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Button } from "@storyteller/ui-button"; -import { invoke } from "@tauri-apps/api/core"; -import { useEffect, useState } from "react"; -import { LoaderCircleIcon } from "lucide-react"; -import { useRefreshAccountStateEvent } from "@storyteller/tauri-events"; -import { RefreshAccountStateEvent } from "@storyteller/tauri-events"; -import { GrokGetCredentialInfo, GrokGetCredentialInfoSuccess } from "@storyteller/tauri-api"; - -export const GrokAccountBlock = () => { - const [grokSession, setGrokSession] = useState(undefined); - const [isCheckingGrokSession, setIsCheckingGrokSession] = useState(false); - - const fetchSession = async () => { - setIsCheckingGrokSession(true); - try { - const result = await GrokGetCredentialInfo(); - setGrokSession(result); - } catch (e) { - console.error("Error fetching Grok session", e); - setGrokSession(undefined); - } finally { - setIsCheckingGrokSession(false); - } - }; - - useEffect(() => { - fetchSession(); - }, []); - - useRefreshAccountStateEvent(async (event: RefreshAccountStateEvent) => { - fetchSession(); - }); - - const clearState = async() => { - try { - await invoke("grok_clear_credentials_command"); - } catch (e) { - console.error("Error clearing Grok credentials", e); - } - } - - const openLogin = async() => { - try { - await invoke("grok_open_login_command"); - } catch (e) { - console.error("Error opening Grok login", e); - } - } - - const handleGrokButton = async () => { - if (grokSession?.payload?.can_clear_state) { - await clearState(); - setGrokSession(undefined); - } else { - await openLogin(); - } - }; - - return( -
-
-

- Grok account -

-

- {grokSession?.payload?.maybe_email || "Not logged in"} -

-
- -
- ) -} \ No newline at end of file diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/MidjourneyAccountBlock.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/MidjourneyAccountBlock.tsx deleted file mode 100644 index a98a0878da..0000000000 --- a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/MidjourneyAccountBlock.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Button } from "@storyteller/ui-button"; -import { invoke } from "@tauri-apps/api/core"; -import { useEffect, useState } from "react"; -import { MidjourneyGetCredentialInfo, MidjourneyGetCredentialInfoSuccess } from "@storyteller/tauri-api"; -import { LoaderCircleIcon } from "lucide-react"; -import { useRefreshAccountStateEvent } from "@storyteller/tauri-events"; -import { RefreshAccountStateEvent } from "@storyteller/tauri-events"; - -export const MidjourneyAccountBlock = () => { - const [midjourneySession, setMidjourneySession] = useState(undefined); - const [isCheckingMidjourneySession, setIsCheckingMidjourneySession] = useState(false); - - const fetchSession = async () => { - setIsCheckingMidjourneySession(true); - try { - const result = await MidjourneyGetCredentialInfo(); - setMidjourneySession(result); - } catch (e) { - console.error("Error fetching Midjourney session", e); - setMidjourneySession(undefined); - } finally { - setIsCheckingMidjourneySession(false); - } - }; - - useEffect(() => { - fetchSession(); - }, []); - - useRefreshAccountStateEvent(async (event: RefreshAccountStateEvent) => { - fetchSession(); - }); - - const clearState = async() => { - try { - await invoke("midjourney_clear_credentials_command"); - } catch (e) { - console.error("Error clearing Midjourney credentials", e); - } - } - - const openLogin = async() => { - try { - await invoke("midjourney_open_login_command"); - } catch (e) { - console.error("Error opening Midjourney login", e); - } - } - - const handleMidjourneyButton = async () => { - if (midjourneySession?.payload?.can_clear_state) { - await clearState(); - setMidjourneySession(undefined); - } else { - await openLogin(); - } - }; - - return( -
-
-

- Midjourney account -

-

- {midjourneySession?.payload?.maybe_email || "Not logged in"} -

-
- -
- ) -} \ No newline at end of file diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/OpenAIKeyBlock.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/OpenAIKeyBlock.tsx index d8e237c01b..cf2656b483 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/OpenAIKeyBlock.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/OpenAIKeyBlock.tsx @@ -28,7 +28,7 @@ export const OpenAIApiKeyBlock = () => { setIsCheckingOpenAIApiKey(false); } }; - fetchKey(); + void fetchKey(); }, []); const syncOpenAIApiKey = async (key: string) => { diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ProviderAccountBlock.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ProviderAccountBlock.tsx new file mode 100644 index 0000000000..6c78f2c38f --- /dev/null +++ b/frontend/libs/components/settings-modal/src/lib/panes/AccountSettings/ProviderAccountBlock.tsx @@ -0,0 +1,109 @@ +import { Button } from "@storyteller/ui-button"; +import { invoke } from "@tauri-apps/api/core"; +import { useEffect, useState } from "react"; +import { LoaderCircleIcon } from "lucide-react"; +import { useRefreshAccountStateEvent } from "@storyteller/tauri-events"; + +interface CredentialInfo { + payload: { maybe_email?: string; can_clear_state: boolean }; +} + +interface ProviderAccountBlockProps { + /** Shown in the heading and in logged errors, e.g. "Grok". */ + name: string; + /** Prefix of the provider's Tauri commands, e.g. "grok" for `grok_open_login_command`. */ + commandPrefix: string; + getCredentialInfo: () => Promise; +} + +/** A provider account signed in through the app's own login window: who is signed in, and Connect / Disconnect. */ +export const ProviderAccountBlock = ({ + name, + commandPrefix, + getCredentialInfo, +}: ProviderAccountBlockProps) => { + const [session, setSession] = useState(undefined); + const [isChecking, setIsChecking] = useState(false); + + const fetchSession = async () => { + setIsChecking(true); + try { + setSession(await getCredentialInfo()); + } catch (e) { + console.error(`Error fetching ${name} session`, e); + setSession(undefined); + } finally { + setIsChecking(false); + } + }; + + useEffect(() => { + void fetchSession(); + }, []); + + useRefreshAccountStateEvent(async () => { + void fetchSession(); + }); + + /** Whether the stored credentials were cleared. */ + const clearState = async () => { + try { + await invoke(`${commandPrefix}_clear_credentials_command`); + return true; + } catch (e) { + console.error(`Error clearing ${name} credentials`, e); + return false; + } + }; + + const openLogin = async () => { + try { + await invoke(`${commandPrefix}_open_login_command`); + } catch (e) { + console.error(`Error opening ${name} login`, e); + } + }; + + const handleButton = async () => { + if (session?.payload?.can_clear_state) { + // A failed clear may have cleared part of the state; show what is left (Disconnect again retries). + if (await clearState()) setSession(undefined); + else await fetchSession(); + } else { + await openLogin(); + } + }; + + return ( +
+
+

+ {name} account +

+

+ {session?.payload?.maybe_email || "Not logged in"} +

+
+ +
+ ); +}; diff --git a/frontend/libs/components/settings-modal/src/lib/panes/AudioSettingsPane.tsx b/frontend/libs/components/settings-modal/src/lib/panes/AudioSettingsPane.tsx index c18ffbd3df..dd9f5f7e0b 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/AudioSettingsPane.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/AudioSettingsPane.tsx @@ -23,7 +23,7 @@ export const AudioSettingsPane = (args: AudioSettingsPaneProps) => { const prefs = await GetAppPreferences(); setPreferences(prefs.preferences); }; - fetchData(); + void fetchData(); }, []); const playSounds = preferences?.play_sounds || false; diff --git a/frontend/libs/components/settings-modal/src/lib/panes/DownloadsSettingsPane.tsx b/frontend/libs/components/settings-modal/src/lib/panes/DownloadsSettingsPane.tsx index 3175d5dadd..09747a8e52 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/DownloadsSettingsPane.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/DownloadsSettingsPane.tsx @@ -221,7 +221,7 @@ export const DownloadsSettingsPane = () => { if (value === "custom") { setFilenameMode("custom"); } else { - saveFilenamePreference("artcraft_convention"); + void saveFilenamePreference("artcraft_convention"); } }} /> diff --git a/frontend/libs/components/settings-modal/src/lib/panes/ProviderPrioritySettingsPane.tsx b/frontend/libs/components/settings-modal/src/lib/panes/ProviderPrioritySettingsPane.tsx index 2b7dc750a1..c076cbb25c 100644 --- a/frontend/libs/components/settings-modal/src/lib/panes/ProviderPrioritySettingsPane.tsx +++ b/frontend/libs/components/settings-modal/src/lib/panes/ProviderPrioritySettingsPane.tsx @@ -139,7 +139,7 @@ export const ProviderPrioritySettingsPane = () => { setItems(items); }; - fetchData(); + void fetchData(); }, []); const sensors = useSensors( @@ -178,7 +178,7 @@ export const ProviderPrioritySettingsPane = () => { const newOrder = arrayMove(prevItems, oldIndex, newIndex); // Send update to backend - updateProviderPriorityOnBackend(newOrder); + void updateProviderPriorityOnBackend(newOrder); return newOrder; }); diff --git a/frontend/libs/components/upload-modal/src/lib/UploadFilesPanels.tsx b/frontend/libs/components/upload-modal/src/lib/UploadFilesPanels.tsx new file mode 100644 index 0000000000..93fd1b5a2b --- /dev/null +++ b/frontend/libs/components/upload-modal/src/lib/UploadFilesPanels.tsx @@ -0,0 +1,128 @@ +import type { Dispatch, ReactNode, SetStateAction } from "react"; +import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; +import { Button } from "@storyteller/ui-button"; +import { CheckIcon, ChevronLeftIcon, ChevronRightIcon, CircleAlertIcon, LoaderIcon, RotateCwIcon, XIcon } from "lucide-react"; + +interface UploadFileRowProps { + entry: { file: File; status: string }; + selected: boolean; + onSelect: () => void; + onRemove: () => void; + onRetry: () => void; +} + +/** One file in an upload modal's list: its name, Remove while idle, then a spinner, a check, or an error with Retry. */ +const UploadFileRow = ({ entry, selected, onSelect, onRemove, onRetry }: UploadFileRowProps) => ( +
  • + + {entry.file.name.slice(0, entry.file.name.lastIndexOf("."))} + + + {entry.status === "idle" && ( + + )} + {(entry.status === "uploading" || entry.status === "converting") && ( + + )} + {entry.status === "success" && } + {entry.status === "error" && ( + + + + + )} + +
  • +); + +interface UploadFilesPanelsProps { + entries: { file: File; status: string }[]; + previewIndex: number; + setPreviewIndex: Dispatch>; + onRemove: (index: number) => void; + onRetry: (index: number) => void; + /** The preview of the selected file, above the previous / next buttons. */ + children: ReactNode; +} + +/** An upload modal with several files: the file list, a resize handle, and the selected file's preview with previous / next. */ +export const UploadFilesPanels = ({ + entries, + previewIndex, + setPreviewIndex, + onRemove, + onRetry, + children, +}: UploadFilesPanelsProps) => ( + + +
      + {entries.map((entry, i) => ( + setPreviewIndex(i)} + onRemove={() => onRemove(i)} + onRetry={() => onRetry(i)} + /> + ))} +
    +
    + + +
    e.stopPropagation()} + /> + + + +
    + {children} +
    + + + {previewIndex + 1} / {entries.length} + + +
    +
    +
    + +); diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx index dd2efe88ae..403073e447 100644 --- a/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx +++ b/frontend/libs/components/upload-modal/src/lib/UploadModal3D/UploadFiles3D.tsx @@ -1,10 +1,9 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { PanelGroup, Panel, PanelResizeHandle } from "react-resizable-panels"; import { ListDropdown } from "@storyteller/ui-list-dropdown"; import { Select } from "@storyteller/ui-select"; import { Button } from "@storyteller/ui-button"; import { FileUploader } from "@storyteller/ui-file-uploader"; -import { BoneIcon, BoxIcon, CheckIcon, ChevronLeftIcon, ChevronRightIcon, CircleAlertIcon, LoaderIcon, RotateCwIcon, XIcon } from "lucide-react"; +import { BoneIcon, BoxIcon, LoaderIcon } from "lucide-react"; import * as THREE from "three"; import { convertFbxToGlb, @@ -21,6 +20,7 @@ import { UploaderState, UploaderStates, } from "../Types"; +import { UploadFilesPanels } from "../UploadFilesPanels"; interface FileEntry { file: File; @@ -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, @@ -695,123 +695,35 @@ export const UploadFiles3D = ({ {selectionError &&
    {selectionError}
    } {isMulti ? ( - - -
      - {fileEntries.map((entry, i) => ( -
    • setPreviewIndex(i)} - > - - {entry.file.name.slice(0, entry.file.name.lastIndexOf("."))} - - - {entry.status === "idle" && ( - - )} - {(entry.status === "uploading" || - entry.status === "converting") && ( - - )} - {entry.status === "success" && ( - - )} - {entry.status === "error" && ( - - - - - )} - -
    • - ))} -
    -
    - - -
    e.stopPropagation()} + void retrySingleFile(i)} + > +
    + - - - -
    -
    - - {animationPicker} - {convertingOverlay} - {!currentFile && ( -
    - - Your model preview will appear here -
    - )} - {previewStatus.type.includes("Error") && ( -
    - {previewStatus.type} - {previewStatus.message &&
    } - {previewStatus.message} -
    - )} -
    -
    - - - {previewIndex + 1} / {fileEntries.length} - - -
    + {animationPicker} + {convertingOverlay} + {!currentFile && ( +
    + + Your model preview will appear here +
    + )} + {previewStatus.type.includes("Error") && ( +
    + {previewStatus.type} + {previewStatus.message &&
    } + {previewStatus.message} +
    + )}
    -
    - + ) : (
    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 +127,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 +158,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), @@ -202,111 +201,24 @@ export const UploadFilesImage = ({ {fileEntries.length > 0 && ( isMulti ? ( - - -
      - {fileEntries.map((entry, i) => ( -
    • setPreviewIndex(i)} - > - - {entry.file.name.slice(0, entry.file.name.lastIndexOf("."))} - - - {entry.status === "idle" && ( - - )} - {entry.status === "uploading" && ( - - )} - {entry.status === "success" && ( - - )} - {entry.status === "error" && ( - - - - - )} - -
    • - ))} -
    -
    - - -
    e.stopPropagation()} - /> - - - -
    -
    - {previewUrl && ( - Preview - )} -
    -
    - - - {previewIndex + 1} / {fileEntries.length} - - -
    + void retrySingleFile(i)} + > +
    + {previewUrl && ( + Preview + )}
    - - +
    ) : ( previewUrl && (
    diff --git a/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx b/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx index dbf4020293..5b0f5225c3 100644 --- a/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx +++ b/frontend/libs/components/upload-modal/src/lib/UploadModalSplat/UploadFilesSplat.tsx @@ -1,9 +1,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; -import { PanelGroup, Panel, PanelResizeHandle } from "react-resizable-panels"; import { ListDropdown } from "@storyteller/ui-list-dropdown"; import { Button } from "@storyteller/ui-button"; import { FileUploader } from "@storyteller/ui-file-uploader"; -import { BoxIcon, CheckIcon, ChevronLeftIcon, ChevronRightIcon, CircleAlertIcon, LoaderIcon, RotateCwIcon, XIcon } from "lucide-react"; +import { BoxIcon } from "lucide-react"; import { WebGLRenderer, PerspectiveCamera } from "three"; import { loadPreviewOnCanvas, snapshotCanvasAsThumbnail } from "../UploadModal3D/utilities"; import { upload3DObjects } from "../UploadModal3D/utilities/upload3DObjects"; @@ -15,6 +14,7 @@ import { UploaderState, UploaderStates, } from "../Types"; +import { UploadFilesPanels } from "../UploadFilesPanels"; interface FileEntry { file: File; @@ -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, @@ -331,118 +331,33 @@ export const UploadFilesSplat = ({ )} {isMulti ? ( - - -
      - {fileEntries.map((entry, i) => ( -
    • setPreviewIndex(i)} - > - - {entry.file.name.slice(0, entry.file.name.lastIndexOf("."))} - - - {entry.status === "idle" && ( - - )} - {entry.status === "uploading" && ( - - )} - {entry.status === "success" && ( - - )} - {entry.status === "error" && ( - - - - - )} - -
    • - ))} -
    -
    - - -
    e.stopPropagation()} + void retrySingleFile(i)} + > +
    + - - - -
    -
    - - {!currentFile && ( -
    - - Your model preview will appear here -
    - )} - {previewStatus.type.includes("Error") && ( -
    - {previewStatus.type} - {previewStatus.message &&
    } - {previewStatus.message} -
    - )} -
    -
    - - - {previewIndex + 1} / {fileEntries.length} - - -
    + {!currentFile && ( +
    + + Your model preview will appear here +
    + )} + {previewStatus.type.includes("Error") && ( +
    + {previewStatus.type} + {previewStatus.message &&
    } + {previewStatus.message} +
    + )}
    -
    - + ) : (
    { - 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 index a4d7fc12ae..b65f199101 100644 --- 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 index e42b84d6df..bebebc5adf 100644 --- 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 index 70fa622b38..45ab9c7e5c 100644 --- 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 index e57651ff4e..17727b4c36 100644 --- 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, });