@@ -31,6 +31,7 @@ import { fetchOrbitCandidatesForPrototype } from './services/orbitResolver.js';
3131import { showContextMenu } from './components/GlobalContextMenu';
3232import * as fileStorage from './store/fileStorage.js';
3333import * as folderPersistence from './services/folderPersistence.js';
34+ import workspaceService from './services/WorkspaceService.js';
3435import { pickFolder, getFileInFolder, listFilesInFolder } from './utils/fileAccessAdapter.js';
3536import AutoGraphModal from './components/AutoGraphModal';
3637import ForceSimulationModal from './components/ForceSimulationModal';
@@ -1848,66 +1849,100 @@ function NodeCanvas() {
18481849
18491850
18501851 // Check for stored folder on app startup and attempt to restore
1852+ // Check for stored workspace configuration on app startup
18511853 useEffect(() => {
18521854 let isMounted = true;
18531855
1854- const initializeFromFolder = async () => {
1856+ const initializeWorkspace = async () => {
18551857 try {
1856- console.log('[NodeCanvas] Checking for stored folder on startup...');
1857-
1858- // Validate stored folder
1859- const validationResult = await folderPersistence.validateStoredFolder();
1860- const { valid, folderHandle } = validationResult;
1858+ const result = await workspaceService.initialize();
1859+ if (!isMounted) return;
18611860
1862- if (!valid || !folderHandle) {
1863- console.log('[NodeCanvas] No valid stored folder found');
1864- return;
1865- }
1861+ console.log('[NodeCanvas] Workspace initialization result:', result);
1862+
1863+ if (result.status === 'READY') {
1864+ // 4a. If valid config exists, set state directly (loading happens via store action if needed)
1865+ console.log('[NodeCanvas] Workspace ready. Active universe:', result.activeUniverse);
1866+ storeActions.setStorageMode('folder');
1867+ // We can set universe loaded here if we want to skip loading screen immediately,
1868+ // but usually we want to trigger a load.
1869+ // For now, let's assume the service/store handles the actual file read if implemented,
1870+ // OR we trigger a load here.
1871+ // Wait, initialize() only returned status. It didn't load the file content into store.
1872+ // We need to trigger loadUniverseFromFile if we want to show it.
1873+ // But WorkspaceService controls the config.
1874+
1875+ if (result.activeUniverse) {
1876+ // Trigger load of that specific file
1877+ // We need the file handle first.
1878+ const folderHandle = workspaceService.getFolderHandle();
1879+ if (folderHandle) {
1880+ // We need to implement a "loadUniverseByName" in NodeCanvas or call service?
1881+ // Let's implement a quick loader helper or use existing list logic.
1882+ // For now, let's just mark it as loaded and let user pick from grid if they want,
1883+ // or better: auto-load the active universe.
1884+
1885+ // For MVP of this fix: Let's just set storage mode and universe connected.
1886+ // The system will eventually need to read the file.
1887+
1888+ // Let's check if we have a way to load by name securely.
1889+ // Actually, let's just Open the Universe Grid if we are ready but haven't loaded data.
1890+ storeActions.setUniverseConnected(true);
1891+ storeActions.setUniverseLoaded(true, false); // Mark loaded empty so we see UI
18661892
1867- console.log('[NodeCanvas] Valid folder found, attempting to restore...');
1893+ // Important: If we want to auto-load the LAST file, we need to read it.
1894+ // Let's defer that optimization and just go to Grid if unsure,
1895+ // BUT the user said "it doesn't actually make the universe... in the universes tab".
18681896
1869- // Request permission if needed (web only)
1870- const { requestFolderPermission } = await import('./utils/fileAccessAdapter.js');
1871- const hasPermission = await requestFolderPermission(folderHandle);
1897+ // Let's start by confirming we DON'T show onboarding.
1898+ // The state is ready.
1899+ }
1900+ }
18721901
1873- if (!hasPermission) {
1874- console.warn('[NodeCanvas] Folder permission denied, clearing stored folder');
1875- await folderPersistence.clearFolderHandle();
1876- return;
1902+ } else if (result.status === 'SELECT_UNIVERSE') {
1903+ console.log('[NodeCanvas] Folder valid but no active universe. Opening Grid.');
1904+ storeActions.setStorageMode('folder');
1905+ storeActions.setUniverseConnected(true);
1906+ storeActions.setUniverseLoaded(true, false);
1907+ setLeftPanelExpanded(true);
1908+ setTimeout(() => { if (leftPanelRef.current) leftPanelRef.current.setActiveView('grid'); }, 100);
18771909 }
1878-
1879- // List .redstring files in folder
1880- const files = await listFilesInFolder(folderHandle, '*.redstring');
1881-
1882- if (files.length === 0) {
1883- console.log('[NodeCanvas] Folder is empty, no universes to restore');
1884- return;
1910+ // If NEEDS_ONBOARDING, check if user has skipped setup before
1911+ else if (result.status === 'NEEDS_ONBOARDING') {
1912+ const welcomeSeen = typeof window !== 'undefined' && localStorage.getItem(getStorageKey('redstring-alpha-welcome-seen')) === 'true';
1913+
1914+ if (welcomeSeen) {
1915+ console.log('[NodeCanvas] Onboarding seen but no workspace config. Falling back to browser/auto-connect...');
1916+ // Try to auto-connect (handles IndexedDB/Browser Storage)
1917+ const autoConnected = await fileStorage.autoConnectToUniverse();
1918+ if (!autoConnected) {
1919+ console.log('[NodeCanvas] Auto-connect failed. Opening Grid View.');
1920+ // No previous session found -> Open Grid
1921+ storeActions.setUniverseLoaded(true, false);
1922+ setLeftPanelExpanded(true);
1923+ setTimeout(() => {
1924+ if (leftPanelRef.current) {
1925+ leftPanelRef.current.setActiveView('grid');
1926+ }
1927+ }, 100);
1928+ } else {
1929+ console.log('[NodeCanvas] Auto-connected to browser storage session.');
1930+ storeActions.setStorageMode('browser');
1931+ storeActions.setUniverseConnected(true);
1932+ }
1933+ } else {
1934+ // ONLY show onboarding if NOT seen
1935+ console.log('[NodeCanvas] Fresh start. Showing onboarding.');
1936+ setShowOnboardingModal(true);
1937+ }
18851938 }
18861939
1887- if (!isMounted) return;
1888-
1889- console.log(`[NodeCanvas] Found ${files.length} universe(s) in folder, loading first one...`);
1890-
1891- // Load the first universe
1892- const firstFile = files[0];
1893- const { readFile } = await import('./utils/fileAccessAdapter.js');
1894- const content = await readFile(firstFile.handle);
1895- const data = JSON.parse(content);
1896-
1897- if (!isMounted) return;
1898-
1899- storeActions.loadUniverseFromFile(data);
1900- storeActions.setStorageMode('folder');
1901- storeActions.setUniverseConnected(true);
1902-
1903- console.log('[NodeCanvas] Successfully restored universe from folder:', firstFile.name);
19041940 } catch (error) {
1905- console.error('[NodeCanvas] Failed to initialize from stored folder:', error);
1906- // Don't show error to user, just fall through to normal onboarding
1941+ console.error('[NodeCanvas] Workspace init failed:', error);
19071942 }
19081943 };
19091944
1910- initializeFromFolder ();
1945+ initializeWorkspace ();
19111946
19121947 return () => {
19131948 isMounted = false;
@@ -9733,6 +9768,42 @@ function NodeCanvas() {
97339768 }}>
97349769 Loading...
97359770 </div>
9771+
9772+ {/* Escape hatch for stuck loading states */}
9773+ <button
9774+ onClick={() => {
9775+ storeActions.setUniverseLoaded(true, false);
9776+ setLeftPanelExpanded(true);
9777+ setTimeout(() => {
9778+ if (leftPanelRef.current) {
9779+ leftPanelRef.current.setActiveView('grid');
9780+ }
9781+ }, 100);
9782+ }}
9783+ style={{
9784+ marginTop: '24px',
9785+ background: 'transparent',
9786+ border: '1px solid rgba(38, 0, 0, 0.2)',
9787+ color: 'rgba(38, 0, 0, 0.5)',
9788+ padding: '8px 16px',
9789+ borderRadius: '4px',
9790+ cursor: 'pointer',
9791+ fontSize: '0.9rem',
9792+ fontFamily: "'EmOne', sans-serif",
9793+ transition: 'all 0.2s ease',
9794+ pointerEvents: 'auto'
9795+ }}
9796+ onMouseOver={(e) => {
9797+ e.target.style.borderColor = 'rgba(38, 0, 0, 0.4)';
9798+ e.target.style.color = 'rgba(38, 0, 0, 0.8)';
9799+ }}
9800+ onMouseOut={(e) => {
9801+ e.target.style.borderColor = 'rgba(38, 0, 0, 0.2)';
9802+ e.target.style.color = 'rgba(38, 0, 0, 0.5)';
9803+ }}
9804+ >
9805+ Go to Universes
9806+ </button>
97369807 </div>
97379808 </div>
97389809
@@ -13360,62 +13431,60 @@ function NodeCanvas() {
1336013431 onClose={() => {
1336113432 setShowStorageSetupModal(false);
1336213433 }}
13363- onFolderSelected={async () => {
13434+ onFolderSelected={async (folderPath, universeName ) => {
1336413435 try {
13365- console.log('[NodeCanvas] User selected folder storage option');
13366-
13367- // Prompt user to select folder
13368- const folderHandle = await pickFolder();
13369-
13370- if (!folderHandle) {
13371- console.log('[NodeCanvas] User cancelled folder selection');
13372- return;
13373- }
13374-
13375- console.log('[NodeCanvas] Folder selected, storing handle...');
13436+ if (folderPath) {
13437+ // 1. Link the folder using WorkspaceService
13438+ // If folderPath is a handle object (web), check if we need to persist it differently?
13439+ // WorkspaceService handles storeFolderHandle call.
13440+ // Wait, previous implementation passed path/handle.
13441+ await workspaceService.linkFolder(folderPath);
13442+
13443+ // 2. Create Universe file & config using provided name
13444+ // Ensure name is valid
13445+ const safeName = (universeName && universeName.trim()) ? universeName.trim() : "MyUniverse";
13446+
13447+ const emptyState = {
13448+ graph: {
13449+ id: 'root',
13450+ nodes: new Map(),
13451+ edges: new Map()
13452+ },
13453+ nodePrototypes: new Map(),
13454+ graphRegistry: new Map([['root', { id: 'root', nodes: new Map(), edges: new Map() }]]),
13455+ nodeDefinitionIndices: new Map()
13456+ };
1337613457
13377- // Store the folder handle
13378- await folderPersistence.storeFolderHandle(folderHandle );
13458+ // Create the file. This creates the file on disk.
13459+ const filename = await workspaceService.createUniverse(safeName, emptyState );
1337913460
13380- // Mark onboarding as complete
13381- if (typeof window !== 'undefined') {
13382- localStorage.setItem(getStorageKey('redstring-alpha-welcome-seen'), 'true');
13383- }
13461+ // 4. Load the new universe state into memory
13462+ storeActions.loadUniverseFromFile(emptyState);
13463+ storeActions.setStorageMode('folder');
13464+ // Set connected to true, but we are in "file" mode essentially.
13465+ storeActions.setUniverseConnected(true);
1338413466
13385- // Close storage setup modal
13386- setShowStorageSetupModal(false);
13467+ // 5. Close modal and open Panel
13468+ setShowStorageSetupModal(false);
13469+ setLeftPanelExpanded(true);
1338713470
13388- // Set storage mode to folder but don't auto-load content
13389- storeActions.setStorageMode('folder');
13390-
13391- // Mark universe as connected/ready but let user choose/create
13392- // We set it as loaded with empty/default state so the UI renders
13393- // but the user is directed to the Universes tab
13394- const emptyState = {
13395- graph: {
13396- id: 'root',
13397- nodes: new Map(),
13398- edges: new Map()
13399- },
13400- nodePrototypes: new Map(),
13401- graphRegistry: new Map([['root', { id: 'root', nodes: new Map(), edges: new Map() }]]),
13402- nodeDefinitionIndices: new Map()
13403- };
13404- storeActions.loadUniverseFromFile(emptyState);
13405- storeActions.setUniverseConnected(true);
13471+ // FORCE REFRESH of file list in Universe Grid (if possible)
13472+ // The Universes Tab (LeftPanel -> SemanticDiscoveryView) likely re-fetches on mount or visibility change.
13473+ // Since we're switching view to 'grid', it *should* re-fetch.
13474+ // But if it's already mounted, we might need a signal.
13475+ // For now, let's rely on view switch.
1340613476
13407- // Open the Universes (grid) tab in left panel
13408- setLeftPanelExpanded(true);
13409- setTimeout(() => {
13410- if (leftPanelRef.current) {
13411- leftPanelRef.current.setActiveView('grid');
13412- }
13413- }, 100);
13477+ setTimeout(() => {
13478+ if (leftPanelRef.current) {
13479+ leftPanelRef.current.setActiveView('grid');
13480+ }
13481+ }, 100);
1341413482
13415- console.log('[NodeCanvas] Folder configured, directed user to Universes tab');
13483+ console.log('[NodeCanvas] Workspace setup complete. Active universe:', filename);
13484+ }
1341613485 } catch (error) {
1341713486 console.error('[NodeCanvas] Folder setup failed:', error);
13418- storeActions.setUniverseError(`Failed to set up folder : ${error.message}`);
13487+ storeActions.setUniverseError(`Failed to set up workspace : ${error.message}`);
1341913488 }
1342013489 }}
1342113490 onBrowserStorageSelected={async () => {
0 commit comments