From 191a47c2f9b521ff6fe84e603b43d14f212d3e11 Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Thu, 21 May 2026 16:51:21 -0400 Subject: [PATCH 1/3] fix(desktop): auto-setup workspace and skip onboarding for shared identity worktrees SPROUT_SHARE_IDENTITY=1 shares the Nostr private key across worktrees but each worktree gets a unique Tauri identifier, isolating localStorage. This left two gaps: no workspace config (forcing WelcomeSetup on every new worktree) and no onboarding completion marker (forcing re-onboarding despite the user already having a profile on the relay). Add an is_shared_identity Tauri command that exposes the env var to the frontend. When true, useWorkspaceInit auto-creates a workspace from the default relay URL (mirroring WelcomeSetup.handleConnect) and the onboarding gate writes the completion marker immediately after the identity query succeeds, bypassing the fragile relay profile check. --- .gitignore | 3 ++ desktop/src-tauri/src/commands/identity.rs | 7 ++++ desktop/src-tauri/src/lib.rs | 1 + desktop/src/features/onboarding/hooks.ts | 32 ++++++++++++++++-- .../features/workspaces/useWorkspaceInit.ts | 33 +++++++++++++++++-- desktop/src/shared/api/tauri.ts | 4 +++ 6 files changed, 75 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index b5ab3b4f9..88b387549 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ doc/ # Local identity files identity.key **/identity.key + +# Claude Code worktrees +.claude/worktrees/ diff --git a/desktop/src-tauri/src/commands/identity.rs b/desktop/src-tauri/src/commands/identity.rs index 3be94f8fa..b153820df 100644 --- a/desktop/src-tauri/src/commands/identity.rs +++ b/desktop/src-tauri/src/commands/identity.rs @@ -37,6 +37,13 @@ pub fn get_default_relay_url() -> String { relay::relay_ws_url() } +#[tauri::command] +pub fn is_shared_identity() -> bool { + std::env::var("SPROUT_SHARE_IDENTITY") + .map(|v| v == "1") + .unwrap_or(false) +} + #[tauri::command] pub fn get_relay_ws_url(state: State<'_, AppState>) -> String { relay_ws_url_with_override(&state) diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index 93472357e..d27a35914 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -461,6 +461,7 @@ pub fn run() { get_presence, set_presence, get_default_relay_url, + is_shared_identity, get_relay_ws_url, get_relay_http_url, get_media_proxy_port, diff --git a/desktop/src/features/onboarding/hooks.ts b/desktop/src/features/onboarding/hooks.ts index db6f6bf82..8b9b2b17e 100644 --- a/desktop/src/features/onboarding/hooks.ts +++ b/desktop/src/features/onboarding/hooks.ts @@ -4,7 +4,7 @@ import { useQueryClient, type QueryStatus } from "@tanstack/react-query"; import { channelsQueryKey } from "@/features/channels/hooks"; import { useProfileQuery } from "@/features/profile/hooks"; import { useIdentityQuery } from "@/shared/api/hooks"; -import { getChannels, joinChannel } from "@/shared/api/tauri"; +import { getChannels, isSharedIdentity, joinChannel } from "@/shared/api/tauri"; const DEFAULT_AUTO_JOIN_CHANNEL_NAME = "general"; @@ -36,6 +36,7 @@ type UseFirstRunOnboardingGateOptions = { hasExistingProfile: boolean; identityIsFetching: boolean; identityStatus: QueryStatus; + isSharedIdentity: boolean; profileStatus: QueryStatus; }; @@ -129,6 +130,7 @@ export function useFirstRunOnboardingGate({ hasExistingProfile, identityIsFetching, identityStatus, + isSharedIdentity: isSharedIdentityProp, profileStatus, }: UseFirstRunOnboardingGateOptions) { const [gateState, setGateState] = React.useState(() => @@ -146,7 +148,7 @@ export function useFirstRunOnboardingGate({ }, [currentPubkey]); React.useEffect(() => { - if (hasSettledCurrentPubkey || !currentPubkey) { + if ((hasSettledCurrentPubkey && !isSharedIdentityProp) || !currentPubkey) { return; } @@ -164,6 +166,26 @@ export function useFirstRunOnboardingGate({ return; } + // Shared identity worktrees have already onboarded in the main checkout. + // Skip unconditionally without waiting for the relay profile query. + if (isSharedIdentityProp) { + if (typeof window !== "undefined" && currentPubkey) { + window.localStorage.setItem( + onboardingCompletionStorageKey(currentPubkey), + "true", + ); + } + setGateState((current) => + updateActiveGateState(current, currentPubkey, (activeGateState) => ({ + ...activeGateState, + hasCompletedCurrentPubkey: true, + hasSettledCurrentPubkey: true, + isOpen: false, + })), + ); + return; + } + if (!isSettledQueryStatus(profileStatus)) { return; } @@ -197,6 +219,7 @@ export function useFirstRunOnboardingGate({ hasExistingProfile, hasSettledCurrentPubkey, identityStatus, + isSharedIdentityProp, profileStatus, ]); @@ -252,11 +275,16 @@ export function useAppOnboardingState() { const identity = identityQuery.data; const currentPubkey = identity?.pubkey ?? null; const profileQuery = useProfileQuery(); + const [sharedIdentity, setSharedIdentity] = React.useState(false); + React.useEffect(() => { + isSharedIdentity().then(setSharedIdentity).catch(() => {}); + }, []); const onboardingGate = useFirstRunOnboardingGate({ currentPubkey, hasExistingProfile: hasRealDisplayName(profileQuery.data?.displayName), identityIsFetching: identityQuery.fetchStatus === "fetching", identityStatus: identityQuery.status, + isSharedIdentity: sharedIdentity, profileStatus: profileQuery.status, }); const gateComplete = onboardingGate.complete; diff --git a/desktop/src/features/workspaces/useWorkspaceInit.ts b/desktop/src/features/workspaces/useWorkspaceInit.ts index 265004485..3397ae57d 100644 --- a/desktop/src/features/workspaces/useWorkspaceInit.ts +++ b/desktop/src/features/workspaces/useWorkspaceInit.ts @@ -1,12 +1,18 @@ import { useEffect, useRef, useState } from "react"; import { relayClient } from "@/shared/api/relayClient"; -import { applyWorkspace, getDefaultRelayUrl } from "@/shared/api/tauri"; +import { applyWorkspace, getDefaultRelayUrl, getIdentity, isSharedIdentity } from "@/shared/api/tauri"; import { resetMediaCaches } from "@/shared/lib/mediaUrl"; import { clearSearchHitEventCache } from "@/app/navigation/searchHitEventCache"; import { clearAllDrafts } from "@/features/messages/lib/useDrafts"; import { resetAgentObserverStore } from "@/features/agents/observerRelayStore"; +import { + deriveWorkspaceName, + normalizeRelayUrl, + saveActiveWorkspaceId, + saveWorkspaces, +} from "./workspaceStorage"; import type { Workspace } from "./types"; /** @@ -60,9 +66,30 @@ export function useWorkspaceInit( async function init() { if (!activeWorkspace) { - // No workspace — need setup try { - const defaultRelayUrl = await getDefaultRelayUrl(); + const [defaultRelayUrl, sharedIdentity] = await Promise.all([ + getDefaultRelayUrl(), + isSharedIdentity(), + ]); + + if (sharedIdentity) { + // Shared identity worktree: auto-create workspace from the + // default relay URL so the user skips WelcomeSetup entirely. + const identity = await getIdentity(); + const normalizedUrl = normalizeRelayUrl(defaultRelayUrl); + const workspace: Workspace = { + id: crypto.randomUUID(), + name: deriveWorkspaceName(normalizedUrl), + relayUrl: normalizedUrl, + pubkey: identity.pubkey, + addedAt: new Date().toISOString(), + }; + saveWorkspaces([workspace]); + saveActiveWorkspaceId(workspace.id); + window.location.reload(); + return; + } + if (!cancelled) { setResult({ isReady: false, diff --git a/desktop/src/shared/api/tauri.ts b/desktop/src/shared/api/tauri.ts index 7b5f78d7a..8cbee3f76 100644 --- a/desktop/src/shared/api/tauri.ts +++ b/desktop/src/shared/api/tauri.ts @@ -519,6 +519,10 @@ export function getDefaultRelayUrl(): Promise { return invokeTauri("get_default_relay_url"); } +export function isSharedIdentity(): Promise { + return invokeTauri("is_shared_identity"); +} + export function getRelayWsUrl(): Promise { return invokeTauri("get_relay_ws_url"); } From 5b5c7add60954c6a610eab0929ee89d141a39bf8 Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Thu, 21 May 2026 17:21:04 -0400 Subject: [PATCH 2/3] fix(desktop): harden shared-identity worktree onboarding The initial implementation (38d5342d) auto-skipped onboarding for shared-identity worktrees but had several issues surfaced by review: is_shared_identity() returned true without a valid key, double IPC calls raced with stale initial state, the onboarding effect allowed infinite re-entry, and cancelled guards were missing before window.location.reload(). Strengthen is_shared_identity() to require both SPROUT_SHARE_IDENTITY=1 and a parseable SPROUT_PRIVATE_KEY. Hoist the IPC call to App.tsx and gate rendering until it resolves, eliminating the flash-of-WelcomeSetup race. Restructure the onboarding effect so the shared-identity fast-path runs before the hasSettledCurrentPubkey guard with a once-guard on hasCompletedCurrentPubkey. Extract initFirstWorkspace() to deduplicate workspace creation between useWorkspaceInit and WelcomeSetup. --- desktop/src-tauri/src/commands/identity.rs | 4 ++ desktop/src/app/App.tsx | 26 ++++++-- desktop/src/features/onboarding/hooks.ts | 59 ++++++++++--------- .../features/workspaces/ui/WelcomeSetup.tsx | 18 +----- .../features/workspaces/useWorkspaceInit.ts | 42 +++++-------- .../features/workspaces/workspaceStorage.ts | 17 ++++++ 6 files changed, 93 insertions(+), 73 deletions(-) diff --git a/desktop/src-tauri/src/commands/identity.rs b/desktop/src-tauri/src/commands/identity.rs index b153820df..4eec001db 100644 --- a/desktop/src-tauri/src/commands/identity.rs +++ b/desktop/src-tauri/src/commands/identity.rs @@ -42,6 +42,10 @@ pub fn is_shared_identity() -> bool { std::env::var("SPROUT_SHARE_IDENTITY") .map(|v| v == "1") .unwrap_or(false) + && std::env::var("SPROUT_PRIVATE_KEY") + .ok() + .and_then(|k| Keys::parse(k.trim()).ok()) + .is_some() } #[tauri::command] diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 62c9bdd08..598de92ef 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -16,6 +16,7 @@ import { useWorkspaceInit } from "@/features/workspaces/useWorkspaceInit"; import { useWorkspaces } from "@/features/workspaces/useWorkspaces"; import { WelcomeSetup } from "@/features/workspaces/ui/WelcomeSetup"; import { createSproutQueryClient } from "@/shared/api/queryClient"; +import { isSharedIdentity as isSharedIdentityCmd } from "@/shared/api/tauri"; import { listenForDeepLinks } from "@/shared/deep-link"; function AppLoadingGate() { @@ -44,8 +45,8 @@ function WorkspaceQueryProvider({ children }: { children: ReactNode }) { ); } -function AppReady() { - const onboarding = useAppOnboardingState(); +function AppReady({ isSharedIdentity }: { isSharedIdentity: boolean }) { + const onboarding = useAppOnboardingState(isSharedIdentity); if (onboarding.stage === "onboarding") { return ( @@ -69,6 +70,16 @@ export function App() { void getCurrentWindow().show(); }, []); + const [sharedIdentity, setSharedIdentity] = useState(null); + useEffect(() => { + isSharedIdentityCmd() + .then(setSharedIdentity) + .catch((err) => { + console.warn("is_shared_identity command failed:", err); + setSharedIdentity(false); + }); + }, []); + const { activeWorkspace, reinitKey, @@ -90,7 +101,7 @@ export function App() { // Composite key: changes when workspace ID changes OR when // the active workspace's config is updated (relayUrl/token). const workspaceKey = `${activeWorkspace?.id ?? "none"}-${reinitKey}`; - const workspace = useWorkspaceInit(activeWorkspace, workspaceKey); + const workspace = useWorkspaceInit(activeWorkspace, workspaceKey, sharedIdentity ?? false); const handleSetupComplete = useCallback(() => { // Force a full reload so useWorkspaces re-initializes from localStorage. @@ -98,6 +109,13 @@ export function App() { window.location.reload(); }, []); + // Wait for the shared-identity IPC call to resolve before rendering + // anything that depends on it. Without this gate, children briefly see + // isSharedIdentity=false and may flash WelcomeSetup or the onboarding flow. + if (sharedIdentity === null) { + return ; + } + // Show welcome setup for first-run users with no workspaces if (workspace.needsSetup) { return ( @@ -118,7 +136,7 @@ export function App() { return ( - + ); } diff --git a/desktop/src/features/onboarding/hooks.ts b/desktop/src/features/onboarding/hooks.ts index 8b9b2b17e..3f9d6ae2c 100644 --- a/desktop/src/features/onboarding/hooks.ts +++ b/desktop/src/features/onboarding/hooks.ts @@ -4,7 +4,7 @@ import { useQueryClient, type QueryStatus } from "@tanstack/react-query"; import { channelsQueryKey } from "@/features/channels/hooks"; import { useProfileQuery } from "@/features/profile/hooks"; import { useIdentityQuery } from "@/shared/api/hooks"; -import { getChannels, isSharedIdentity, joinChannel } from "@/shared/api/tauri"; +import { getChannels, joinChannel } from "@/shared/api/tauri"; const DEFAULT_AUTO_JOIN_CHANNEL_NAME = "general"; @@ -130,14 +130,15 @@ export function useFirstRunOnboardingGate({ hasExistingProfile, identityIsFetching, identityStatus, - isSharedIdentity: isSharedIdentityProp, + isSharedIdentity, profileStatus, }: UseFirstRunOnboardingGateOptions) { const [gateState, setGateState] = React.useState(() => createOnboardingGateState(currentPubkey), ); const activeGateState = resolveActiveGateState(gateState, currentPubkey); - const { hasSettledCurrentPubkey } = activeGateState; + const { hasCompletedCurrentPubkey, hasSettledCurrentPubkey } = + activeGateState; React.useEffect(() => { setGateState((current) => @@ -148,44 +149,51 @@ export function useFirstRunOnboardingGate({ }, [currentPubkey]); React.useEffect(() => { - if ((hasSettledCurrentPubkey && !isSharedIdentityProp) || !currentPubkey) { - return; - } - - if (identityStatus === "error") { + // Fast-path: shared identity worktrees have already onboarded in the + // main checkout. Skip unconditionally without waiting for the relay + // profile query. Guarded by !hasCompletedCurrentPubkey so it fires once. + if ( + isSharedIdentity && + currentPubkey && + identityStatus === "success" && + !hasCompletedCurrentPubkey + ) { + if (typeof window !== "undefined") { + window.localStorage.setItem( + onboardingCompletionStorageKey(currentPubkey), + "true", + ); + } setGateState((current) => updateActiveGateState(current, currentPubkey, (activeGateState) => ({ ...activeGateState, + hasCompletedCurrentPubkey: true, hasSettledCurrentPubkey: true, + isOpen: false, })), ); return; } - if (identityStatus !== "success") { + // Original guard — restored to simple form. + if (hasSettledCurrentPubkey || !currentPubkey) { return; } - // Shared identity worktrees have already onboarded in the main checkout. - // Skip unconditionally without waiting for the relay profile query. - if (isSharedIdentityProp) { - if (typeof window !== "undefined" && currentPubkey) { - window.localStorage.setItem( - onboardingCompletionStorageKey(currentPubkey), - "true", - ); - } + if (identityStatus === "error") { setGateState((current) => updateActiveGateState(current, currentPubkey, (activeGateState) => ({ ...activeGateState, - hasCompletedCurrentPubkey: true, hasSettledCurrentPubkey: true, - isOpen: false, })), ); return; } + if (identityStatus !== "success") { + return; + } + if (!isSettledQueryStatus(profileStatus)) { return; } @@ -216,10 +224,11 @@ export function useFirstRunOnboardingGate({ ); }, [ currentPubkey, + hasCompletedCurrentPubkey, hasExistingProfile, hasSettledCurrentPubkey, identityStatus, - isSharedIdentityProp, + isSharedIdentity, profileStatus, ]); @@ -269,22 +278,18 @@ function hasRealDisplayName(displayName?: string | null): boolean { return !lower.startsWith("npub1") && !lower.startsWith("nostr:npub1"); } -export function useAppOnboardingState() { +export function useAppOnboardingState(isSharedIdentity: boolean) { const queryClient = useQueryClient(); const identityQuery = useIdentityQuery(); const identity = identityQuery.data; const currentPubkey = identity?.pubkey ?? null; const profileQuery = useProfileQuery(); - const [sharedIdentity, setSharedIdentity] = React.useState(false); - React.useEffect(() => { - isSharedIdentity().then(setSharedIdentity).catch(() => {}); - }, []); const onboardingGate = useFirstRunOnboardingGate({ currentPubkey, hasExistingProfile: hasRealDisplayName(profileQuery.data?.displayName), identityIsFetching: identityQuery.fetchStatus === "fetching", identityStatus: identityQuery.status, - isSharedIdentity: sharedIdentity, + isSharedIdentity, profileStatus: profileQuery.status, }); const gateComplete = onboardingGate.complete; diff --git a/desktop/src/features/workspaces/ui/WelcomeSetup.tsx b/desktop/src/features/workspaces/ui/WelcomeSetup.tsx index fe8ede92d..5d4f78266 100644 --- a/desktop/src/features/workspaces/ui/WelcomeSetup.tsx +++ b/desktop/src/features/workspaces/ui/WelcomeSetup.tsx @@ -4,12 +4,9 @@ import { getIdentity } from "@/shared/api/tauri"; import { Button } from "@/shared/ui/button"; import { Input } from "@/shared/ui/input"; -import type { Workspace } from "../types"; import { + initFirstWorkspace, deriveWorkspaceName, - normalizeRelayUrl, - saveActiveWorkspaceId, - saveWorkspaces, } from "../workspaceStorage"; const LOCAL_RELAY_URL = "ws://localhost:3000"; @@ -35,7 +32,6 @@ export function WelcomeSetup({ return; } - const normalizedUrl = normalizeRelayUrl(trimmedUrl); setIsConnecting(true); setError(null); @@ -44,17 +40,7 @@ export function WelcomeSetup({ // labels, etc.). The private key lives on disk in `identity.key` and // is the single source of truth — never copied into localStorage. const identity = await getIdentity(); - - const workspace: Workspace = { - id: crypto.randomUUID(), - name: deriveWorkspaceName(normalizedUrl), - relayUrl: normalizedUrl, - pubkey: identity.pubkey, - addedAt: new Date().toISOString(), - }; - - saveWorkspaces([workspace]); - saveActiveWorkspaceId(workspace.id); + initFirstWorkspace(trimmedUrl, identity.pubkey); // The reload triggered by onComplete() will re-run useWorkspaceInit, // which calls applyWorkspace with the saved config. No need to apply here. diff --git a/desktop/src/features/workspaces/useWorkspaceInit.ts b/desktop/src/features/workspaces/useWorkspaceInit.ts index 3397ae57d..8da3e949d 100644 --- a/desktop/src/features/workspaces/useWorkspaceInit.ts +++ b/desktop/src/features/workspaces/useWorkspaceInit.ts @@ -1,18 +1,17 @@ import { useEffect, useRef, useState } from "react"; import { relayClient } from "@/shared/api/relayClient"; -import { applyWorkspace, getDefaultRelayUrl, getIdentity, isSharedIdentity } from "@/shared/api/tauri"; +import { + applyWorkspace, + getDefaultRelayUrl, + getIdentity, +} from "@/shared/api/tauri"; import { resetMediaCaches } from "@/shared/lib/mediaUrl"; import { clearSearchHitEventCache } from "@/app/navigation/searchHitEventCache"; import { clearAllDrafts } from "@/features/messages/lib/useDrafts"; import { resetAgentObserverStore } from "@/features/agents/observerRelayStore"; -import { - deriveWorkspaceName, - normalizeRelayUrl, - saveActiveWorkspaceId, - saveWorkspaces, -} from "./workspaceStorage"; +import { initFirstWorkspace } from "./workspaceStorage"; import type { Workspace } from "./types"; /** @@ -49,6 +48,7 @@ type WorkspaceInitResult = export function useWorkspaceInit( activeWorkspace: Workspace | null, workspaceKey: string, + isSharedIdentity: boolean, ): WorkspaceInitResult { const [result, setResult] = useState({ isReady: false, @@ -67,26 +67,15 @@ export function useWorkspaceInit( async function init() { if (!activeWorkspace) { try { - const [defaultRelayUrl, sharedIdentity] = await Promise.all([ - getDefaultRelayUrl(), - isSharedIdentity(), - ]); - - if (sharedIdentity) { - // Shared identity worktree: auto-create workspace from the - // default relay URL so the user skips WelcomeSetup entirely. + const defaultRelayUrl = await getDefaultRelayUrl(); + + if (isSharedIdentity) { const identity = await getIdentity(); - const normalizedUrl = normalizeRelayUrl(defaultRelayUrl); - const workspace: Workspace = { - id: crypto.randomUUID(), - name: deriveWorkspaceName(normalizedUrl), - relayUrl: normalizedUrl, - pubkey: identity.pubkey, - addedAt: new Date().toISOString(), - }; - saveWorkspaces([workspace]); - saveActiveWorkspaceId(workspace.id); - window.location.reload(); + if (cancelled) return; + initFirstWorkspace(defaultRelayUrl, identity.pubkey); + if (!cancelled) { + window.location.reload(); + } return; } @@ -163,6 +152,7 @@ export function useWorkspaceInit( activeWorkspace?.id, activeWorkspace?.relayUrl, activeWorkspace?.token, + isSharedIdentity, workspaceKey, ]); diff --git a/desktop/src/features/workspaces/workspaceStorage.ts b/desktop/src/features/workspaces/workspaceStorage.ts index 37b687452..407dd7514 100644 --- a/desktop/src/features/workspaces/workspaceStorage.ts +++ b/desktop/src/features/workspaces/workspaceStorage.ts @@ -79,3 +79,20 @@ export function deriveWorkspaceName(relayUrl: string): string { return "Workspace"; } } + +export function initFirstWorkspace( + relayUrl: string, + pubkey: string, +): Workspace { + const normalizedUrl = normalizeRelayUrl(relayUrl); + const workspace: Workspace = { + id: crypto.randomUUID(), + name: deriveWorkspaceName(normalizedUrl), + relayUrl: normalizedUrl, + pubkey, + addedAt: new Date().toISOString(), + }; + saveWorkspaces([workspace]); + saveActiveWorkspaceId(workspace.id); + return workspace; +} From 5750e16d550c53ed3daa800b7b64b19e0451ebdd Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Thu, 21 May 2026 17:39:53 -0400 Subject: [PATCH 3/3] style(desktop): fix biome formatting in App.tsx and WelcomeSetup.tsx --- desktop/src/app/App.tsx | 6 +++++- desktop/src/features/workspaces/ui/WelcomeSetup.tsx | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 598de92ef..4f75637f5 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -101,7 +101,11 @@ export function App() { // Composite key: changes when workspace ID changes OR when // the active workspace's config is updated (relayUrl/token). const workspaceKey = `${activeWorkspace?.id ?? "none"}-${reinitKey}`; - const workspace = useWorkspaceInit(activeWorkspace, workspaceKey, sharedIdentity ?? false); + const workspace = useWorkspaceInit( + activeWorkspace, + workspaceKey, + sharedIdentity ?? false, + ); const handleSetupComplete = useCallback(() => { // Force a full reload so useWorkspaces re-initializes from localStorage. diff --git a/desktop/src/features/workspaces/ui/WelcomeSetup.tsx b/desktop/src/features/workspaces/ui/WelcomeSetup.tsx index 5d4f78266..d422a36d1 100644 --- a/desktop/src/features/workspaces/ui/WelcomeSetup.tsx +++ b/desktop/src/features/workspaces/ui/WelcomeSetup.tsx @@ -4,10 +4,7 @@ import { getIdentity } from "@/shared/api/tauri"; import { Button } from "@/shared/ui/button"; import { Input } from "@/shared/ui/input"; -import { - initFirstWorkspace, - deriveWorkspaceName, -} from "../workspaceStorage"; +import { initFirstWorkspace, deriveWorkspaceName } from "../workspaceStorage"; const LOCAL_RELAY_URL = "ws://localhost:3000";