@anymux/connect 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["microsoftService: ServiceDefinition","icloudService: ServiceDefinition","baseURL: string","providerId: string","serviceId: string","token: string","options?: { authClient?: ConnectAuthClient }","serviceId: string","pendingServiceId: string","token: string | null","fallbackProfile: IUserProfile | undefined","profile: IUserProfile","credentialToken: string","capabilityId: CapabilityId","type: ActionType","description: string","undo?: () => Promise<void>","action: ActionRecord","id: string","connectionManager: ConnectionManagerModel","repo: { owner: string; repo: string }","serviceId: string","capabilityId: CapabilityId","path: string","state: GitBrowserState | null","serviceType: CredentialServiceType","prefill?: Record<string, string>","field: string","value: string","creds: Record<string, string>","values: Record<string, string>","AppleIcon: React.FC<LucideProps>","customIcons: Record<string, React.FC<LucideProps>>","ServiceIcon: React.FC<ServiceIconProps>","connectionManager: ConnectionManagerModel","serviceId: string","CredentialForm: React.FC<CredentialFormProps>","e: React.FormEvent","date: Date | undefined","ConnectButton: React.FC<ConnectButtonProps>","err: any","credentialToken: string","values: Record<string, string>","CapabilityCell: React.FC<CapabilityCellProps>","e: React.KeyboardEvent","CAPABILITY_COLUMNS: CapabilityId[]","ServiceRow: React.FC<ServiceRowProps>","CapabilityPill: React.FC<CapabilityPillProps>","CAPABILITY_COLUMNS: { id: CapabilityId; label: string }[]","ServiceCard: React.FC<ServiceCardProps>","serviceId: string","factory: AdapterFactory","token: string","capabilityId: CapabilityId","context: AdapterContext","adapters: Partial<Record<CapabilityId, () => unknown>>","factory: () => Promise<T>","deps: unknown[]","preflight?: (adapter: T) => Promise<void>","error: string","CAPABILITY_LABELS: Record<CapabilityId, string>","branch: string","adapter: any","err: { message: string }","type: 'success' | 'error' | 'warning'","message: string","action: { type: 'create' | 'rename' | 'delete' | 'upload'; message: string; undo?: () => Promise<void> }","CapabilityPanel: React.FC<CapabilityPanelProps>","ACTION_ICONS: Record<ActionType, React.ReactNode>","date: Date","ActionItem: React.FC<{ action: ActionRecord; model: ActionNotificationModel }>","ActionHistoryPanel: React.FC<ActionHistoryPanelProps>","e: MouseEvent","FullScreenBrowser: React.FC<FullScreenBrowserProps>","ServiceDashboard: React.FC<ServiceDashboardProps>","ConnectionStatusIndicator: React.FC<ConnectionStatusProps>","data: GitHubRepoInfo[]","ServiceDashboardDemo: React.FC<ServiceDashboardDemoProps>"],"sources":["../src/registry/services/microsoft.ts","../src/registry/services/icloud.ts","../src/auth/auth-client.ts","../src/auth/token-manager.ts","../src/models/ConnectionManagerModel.ts","../src/models/ActionNotificationModel.ts","../src/models/DashboardModel.ts","../src/models/CredentialFormModel.ts","../src/components/ServiceIcon.tsx","../src/types/connection-state.ts","../src/components/CredentialForm.tsx","../src/components/ConnectedMenu.tsx","../src/components/ConnectButton.tsx","../src/components/CapabilityCell.tsx","../src/components/ServiceRow.tsx","../src/components/CapabilityPill.tsx","../src/components/ServiceCard.tsx","../src/adapters/adapter-registry.ts","../src/components/useAdapter.ts","../src/components/CapabilityError.tsx","../src/components/CapabilityPanel.tsx","../src/components/ActionHistoryPanel.tsx","../src/components/FullScreenBrowser.tsx","../src/components/ServiceDashboard.tsx","../src/components/ConnectionStatus.tsx","../src/components/GitHubRepoPicker.tsx","../src/demos/ServiceDashboardDemo.tsx"],"sourcesContent":["import type { ServiceDefinition } from '../../types/service';\n\nexport const microsoftService: ServiceDefinition = {\n id: 'microsoft',\n name: 'Microsoft',\n icon: 'AppWindow',\n color: '#00A4EF',\n authProvider: 'microsoft',\n grantsUrl: 'https://account.live.com/consent/Manage',\n capabilities: [\n { id: 'file-system', supported: true },\n { id: 'object-storage', supported: false },\n { id: 'git-repo', supported: false },\n { id: 'git-host', supported: false },\n { id: 'media', supported: false },\n { id: 'contacts', supported: true },\n { id: 'calendar', supported: true },\n ],\n scopes: {\n 'file-system': ['Files.ReadWrite.All'],\n 'contacts': ['Contacts.Read'],\n 'calendar': ['Calendars.Read'],\n },\n};\n","import type { ServiceDefinition } from '../../types/service';\n\nexport const icloudService: ServiceDefinition = {\n id: 'icloud',\n name: 'iCloud',\n icon: 'Cloud',\n color: '#007AFF',\n authProvider: 'icloud',\n grantsUrl: 'https://appleid.apple.com/account/manage',\n capabilities: [\n { id: 'file-system', supported: false },\n { id: 'object-storage', supported: false },\n { id: 'git-repo', supported: false },\n { id: 'git-host', supported: false },\n { id: 'media', supported: false },\n { id: 'contacts', supported: true },\n { id: 'calendar', supported: true },\n ],\n scopes: {\n contacts: [],\n calendar: [],\n },\n};\n","import { createAuthClient } from 'better-auth/client';\nimport { genericOAuthClient } from 'better-auth/client/plugins';\n\nconst PENDING_SERVICE_KEY = 'anymux-pending-service';\n\nexport interface ConnectAuthClient {\n signIn(provider: string, serviceId: string): Promise<void>;\n signOut(): Promise<void>;\n getSession(): Promise<{ user: { id: string; name: string; image?: string } } | null>;\n getAccessToken(providerId: string): Promise<string | null>;\n fetchConfiguredProviders(): Promise<{ database: boolean; authSecret: boolean; providers: Record<string, boolean> }>;\n fetchGrantedScopes(): Promise<Record<string, string[]>>;\n fetchTestCredentials(): Promise<Record<string, unknown>>;\n fetchUserProfiles(): Promise<Record<string, { id: string; name: string; email?: string; avatarUrl?: string; profileUrl?: string }>>;\n revokeProvider(providerId: string): Promise<void>;\n getPendingServiceId(): string | null;\n clearPendingServiceId(): void;\n}\n\nexport function createConnectAuthClient(baseURL: string): ConnectAuthClient {\n const authClient = createAuthClient({ baseURL, plugins: [genericOAuthClient()] });\n\n return {\n async signIn(provider, serviceId) {\n sessionStorage.setItem(PENDING_SERVICE_KEY, serviceId);\n const GENERIC_OAUTH_PROVIDERS = ['box', 'bitbucket'];\n if (GENERIC_OAUTH_PROVIDERS.includes(provider)) {\n await authClient.signIn.oauth2({\n providerId: provider,\n callbackURL: window.location.href,\n });\n } else {\n await authClient.signIn.social({\n provider: provider as 'google' | 'github' | 'dropbox' | 'microsoft' | 'apple',\n callbackURL: window.location.href,\n });\n }\n // Browser redirects away — this line won't execute\n },\n\n async signOut() {\n await authClient.signOut();\n },\n\n async getSession() {\n const session = await authClient.getSession();\n if (!session.data) return null;\n return { user: { id: session.data.user.id, name: session.data.user.name, ...(session.data.user.image ? { image: session.data.user.image } : {}) } };\n },\n\n async getAccessToken(providerId: string) {\n const result = await authClient.getAccessToken({ providerId });\n return result.data?.accessToken ?? null;\n },\n\n async fetchConfiguredProviders() {\n const res = await fetch(`${baseURL}/api/auth/configured`);\n if (!res.ok) return { database: false, authSecret: false, providers: {} };\n return res.json();\n },\n\n async fetchGrantedScopes() {\n const res = await fetch(`${baseURL}/api/auth/scopes`, { credentials: 'include' });\n if (!res.ok) return {};\n return res.json();\n },\n\n async fetchTestCredentials() {\n const res = await fetch(`${baseURL}/api/auth/test-credentials`);\n if (!res.ok) return {};\n return res.json();\n },\n\n async fetchUserProfiles() {\n const res = await fetch(`${baseURL}/api/auth/user-profiles`, { credentials: 'include' });\n if (!res.ok) return {};\n return res.json();\n },\n\n async revokeProvider(providerId: string) {\n const res = await fetch(`${baseURL}/api/auth/revoke-provider?provider=${encodeURIComponent(providerId)}`, {\n method: 'DELETE',\n credentials: 'include',\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`revokeProvider ${providerId} failed: ${res.status} ${text}`);\n }\n const data = await res.json().catch(() => ({}));\n console.info(`[AnyMux] revokeProvider(${providerId}): deleted=${(data as Record<string, unknown>).deleted ?? '?'}`);\n },\n\n getPendingServiceId() {\n return sessionStorage.getItem(PENDING_SERVICE_KEY);\n },\n\n clearPendingServiceId() {\n sessionStorage.removeItem(PENDING_SERVICE_KEY);\n },\n };\n}\n","const STORAGE_PREFIX = 'anymux-connect-token:';\n\nexport class TokenManager {\n getToken(serviceId: string): string | null {\n try {\n return localStorage.getItem(STORAGE_PREFIX + serviceId);\n } catch {\n return null;\n }\n }\n\n setToken(serviceId: string, token: string): void {\n try {\n localStorage.setItem(STORAGE_PREFIX + serviceId, token);\n } catch {\n // localStorage unavailable\n }\n }\n\n removeToken(serviceId: string): void {\n try {\n localStorage.removeItem(STORAGE_PREFIX + serviceId);\n } catch {\n // localStorage unavailable\n }\n }\n}\n","import { makeAutoObservable, runInAction, flow } from 'mobx';\nimport type { ConnectedService, ConnectionStatus } from '../types/connection';\nimport type { ConnectAuthClient } from '../auth/auth-client';\nimport type { CapabilityId } from '../types/service';\nimport type { IUserProfile } from '../types/user-profile';\nimport { TokenManager } from '../auth/token-manager';\nimport { serviceRegistry } from '../registry/service-registry';\n\nconst STORAGE_KEY = 'anymux-connect-connections';\nconst USER_PROFILES_STORAGE_KEY = 'anymux-connect-user-profiles';\n\nexport class ConnectionManagerModel {\n connections = new Map<string, ConnectedService>();\n grantedScopes = new Map<string, string[]>();\n initialized = false;\n configuredProviders = new Set<string>();\n configError: string | null = null;\n /** Per-service user profiles with provider-specific name, avatar, and profile link */\n userProfiles = new Map<string, IUserProfile>();\n testCredentials: Record<string, unknown> = {};\n pendingReconnect = new Set<string>();\n private tokenManager = new TokenManager();\n private authClient: ConnectAuthClient | null;\n\n constructor(options?: { authClient?: ConnectAuthClient }) {\n this.authClient = options?.authClient ?? null;\n makeAutoObservable(this);\n this.loadFromStorage();\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // BUG-2: Handle pending OAuth return FIRST so only that service updates immediately\n if (this.authClient) {\n const pendingServiceId = this.authClient.getPendingServiceId();\n if (pendingServiceId) {\n await this.handleOAuthReturn(pendingServiceId);\n }\n }\n\n // BUG-3: Set loading state for services not yet resolved from storage or OAuth\n for (const service of serviceRegistry.getAll()) {\n if (!this.connections.has(service.id)) {\n this.connections.set(service.id, {\n serviceId: service.id,\n status: 'loading',\n scopes: [],\n });\n }\n }\n\n if (this.authClient) {\n // Fetch which OAuth providers have credentials configured on the server\n try {\n const config = await this.authClient.fetchConfiguredProviders();\n runInAction(() => {\n if (!config.database) {\n this.configError = 'Database not configured. Set DATABASE_URL in .env.local';\n return;\n }\n if (!config.authSecret) {\n this.configError = 'Auth secret not configured. Set BETTER_AUTH_SECRET in .env.local';\n return;\n }\n for (const [provider, configured] of Object.entries(config.providers)) {\n if (configured) this.configuredProviders.add(provider);\n }\n // Mark unconfigured OAuth services as not_configured\n for (const service of serviceRegistry.getAll()) {\n if (service.authProvider === 's3' || service.authProvider === 'webdav' || service.authProvider === 'gitea' || service.authProvider === 'browser-fs' || service.authProvider === 'indexeddb') continue;\n if (!this.configuredProviders.has(service.authProvider) && !this.isConnected(service.id)) {\n this.connections.set(service.id, {\n serviceId: service.id,\n status: 'not_configured',\n scopes: [],\n });\n }\n }\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch configured providers:', err instanceof Error ? err.message : err);\n runInAction(() => {\n this.configError = 'Failed to reach server. Check your connection.';\n });\n }\n\n // Fetch granted scopes\n try {\n const scopes = await this.authClient.fetchGrantedScopes();\n runInAction(() => {\n for (const [provider, scopeList] of Object.entries(scopes)) {\n this.grantedScopes.set(provider, scopeList);\n }\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch granted scopes:', err instanceof Error ? err.message : err);\n }\n\n // Fetch test credentials\n try {\n const creds = await this.authClient.fetchTestCredentials();\n runInAction(() => {\n this.testCredentials = creds;\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch test credentials:', err instanceof Error ? err.message : err);\n }\n }\n\n // BUG-3: Resolve any remaining 'loading' services to 'disconnected'\n runInAction(() => {\n for (const [id, conn] of this.connections) {\n if (conn.status === 'loading') {\n this.connections.set(id, { ...conn, status: 'disconnected' });\n }\n }\n this.initialized = true;\n });\n\n // Fetch per-provider user profiles in the background (enriches fallback data with\n // provider-specific avatars, profile URLs, etc.)\n if (this.authClient) {\n this.fetchAndStoreUserProfiles();\n }\n }\n\n /** Fetch per-provider profiles from the server and update the userProfiles map */\n private async fetchAndStoreUserProfiles(): Promise<void> {\n if (!this.authClient) return;\n try {\n const profilesByProvider = await this.authClient.fetchUserProfiles();\n runInAction(() => {\n // Map providerId → serviceId(s) and store profiles\n for (const service of serviceRegistry.getAll()) {\n const providerProfile = profilesByProvider[service.authProvider];\n if (providerProfile && this.isConnected(service.id)) {\n this.userProfiles.set(service.id, {\n ...providerProfile,\n provider: service.id,\n });\n }\n }\n this.persistToStorage();\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch user profiles:', err instanceof Error ? err.message : err);\n }\n }\n\n // BUG-2: Extracted OAuth return handler — only updates the single pending service\n private async handleOAuthReturn(pendingServiceId: string): Promise<void> {\n if (!this.authClient) return;\n const service = serviceRegistry.get(pendingServiceId);\n if (service) {\n // Retry token retrieval — after OAuth redirect the server may not have\n // finished processing the callback yet (race condition)\n let token: string | null = null;\n for (let attempt = 0; attempt < 4; attempt++) {\n try {\n token = await this.authClient.getAccessToken(service.authProvider);\n if (token) break;\n } catch {\n // getAccessToken threw — retry\n }\n if (attempt < 3) {\n await new Promise(r => setTimeout(r, 500 * (attempt + 1)));\n }\n }\n\n if (token) {\n // Capture session user info as an immediate fallback for the profile\n let fallbackProfile: IUserProfile | undefined;\n try {\n const session = await this.authClient.getSession();\n if (session) {\n const profile: IUserProfile = {\n id: session.user.id,\n name: session.user.name,\n provider: pendingServiceId,\n };\n if (session.user.image) {\n profile.avatarUrl = session.user.image;\n }\n fallbackProfile = profile;\n }\n } catch (err) {\n console.warn(`[AnyMux] Failed to fetch session for ${pendingServiceId}:`, err instanceof Error ? err.message : err);\n }\n\n runInAction(() => {\n this.tokenManager.setToken(pendingServiceId, token!);\n this.connections.set(pendingServiceId, {\n serviceId: pendingServiceId,\n status: 'connected',\n accessToken: token!,\n scopes: Object.values(service.scopes).flat(),\n connectedAt: new Date(),\n });\n if (fallbackProfile) {\n this.userProfiles.set(pendingServiceId, fallbackProfile);\n }\n this.persistToStorage();\n });\n } else {\n // OAuth flow completed but token retrieval failed after retries.\n // Check if we have a session — if so, the user authenticated but\n // the token endpoint is having issues.\n try {\n const session = await this.authClient.getSession();\n if (session) {\n console.warn(`[AnyMux] OAuth for ${pendingServiceId}: session exists but getAccessToken returned null after 4 attempts`);\n }\n } catch {\n // ignore\n }\n }\n }\n this.authClient.clearPendingServiceId();\n }\n\n private loadFromStorage() {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) {\n const data = JSON.parse(stored) as Array<[string, ConnectedService]>;\n for (const [key, value] of data) {\n const token = this.tokenManager.getToken(key);\n if (token) {\n this.connections.set(key, { ...value, accessToken: token });\n }\n }\n }\n // Restore per-service user profiles\n const storedProfiles = localStorage.getItem(USER_PROFILES_STORAGE_KEY);\n if (storedProfiles) {\n const entries = JSON.parse(storedProfiles) as Array<[string, IUserProfile]>;\n for (const [key, value] of entries) {\n this.userProfiles.set(key, value);\n }\n }\n } catch {\n // storage unavailable\n }\n }\n\n private persistToStorage() {\n try {\n const data = Array.from(this.connections.entries()).map(\n ([key, conn]) => [key, { ...conn, accessToken: undefined }] as const\n );\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n // Persist per-service user profiles\n const profileData = Array.from(this.userProfiles.entries());\n localStorage.setItem(USER_PROFILES_STORAGE_KEY, JSON.stringify(profileData));\n } catch {\n // storage unavailable\n }\n }\n\n async connect(serviceId: string): Promise<void> {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n if (!this.authClient) {\n this.connections.set(serviceId, {\n serviceId,\n status: 'error',\n scopes: [],\n });\n return;\n }\n\n this.connections.set(serviceId, {\n serviceId,\n status: 'connecting',\n scopes: [],\n });\n\n // Redirect-based flow: browser navigates away\n await this.authClient.signIn(service.authProvider, serviceId);\n }\n\n connectWithCredentials(serviceId: string, credentialToken: string): void {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n this.tokenManager.setToken(serviceId, credentialToken);\n this.connections.set(serviceId, {\n serviceId,\n status: 'connected',\n accessToken: credentialToken,\n scopes: Object.values(service.scopes).flat(),\n connectedAt: new Date(),\n });\n this.persistToStorage();\n }\n\n disconnect = flow(function* (this: ConnectionManagerModel, serviceId: string) {\n const service = serviceRegistry.get(serviceId);\n this.tokenManager.removeToken(serviceId);\n this.connections.set(serviceId, {\n serviceId,\n status: 'disconnected',\n scopes: [],\n });\n if (service) {\n this.grantedScopes.delete(service.authProvider);\n }\n this.userProfiles.delete(serviceId);\n this.persistToStorage();\n\n if (this.authClient) {\n // Delete the account row first so better-auth creates a fresh one\n // with updated scopes on the next OAuth sign-in\n if (service) {\n try {\n console.info(`[AnyMux] disconnect(${serviceId}): revoking provider ${service.authProvider}…`);\n yield this.authClient.revokeProvider(service.authProvider);\n console.info(`[AnyMux] disconnect(${serviceId}): revoke succeeded`);\n } catch (err) {\n console.error('[AnyMux] revokeProvider FAILED:', err instanceof Error ? err.message : err);\n }\n }\n try {\n yield this.authClient.signOut();\n console.info(`[AnyMux] disconnect(${serviceId}): signOut succeeded`);\n } catch (err) {\n console.warn('[AnyMux] signOut failed:', err instanceof Error ? err.message : err);\n }\n }\n });\n\n requestReconnect(serviceId: string): void {\n this.pendingReconnect.add(serviceId);\n }\n\n clearReconnectRequest(serviceId: string): void {\n this.pendingReconnect.delete(serviceId);\n }\n\n isConnected(serviceId: string): boolean {\n return this.connections.get(serviceId)?.status === 'connected';\n }\n\n getStatus(serviceId: string): ConnectionStatus {\n return this.connections.get(serviceId)?.status ?? 'disconnected';\n }\n\n getToken(serviceId: string): string | null {\n return this.tokenManager.getToken(serviceId);\n }\n\n /** Refresh OAuth token from better-auth. Returns fresh token or null. */\n async refreshToken(serviceId: string): Promise<string | null> {\n const service = serviceRegistry.get(serviceId);\n if (!service || !this.authClient) {\n console.warn(`[AnyMux] refreshToken(${serviceId}): no service or authClient`);\n return null;\n }\n // Skip credential-based services — their tokens don't expire\n if (['s3', 'webdav', 'gitea', 'icloud', 'browser-fs', 'indexeddb'].includes(service.authProvider)) {\n return this.tokenManager.getToken(serviceId);\n }\n const oldToken = this.tokenManager.getToken(serviceId);\n try {\n const token = await this.authClient.getAccessToken(service.authProvider);\n if (token) {\n const changed = token !== oldToken;\n console.info(`[AnyMux] refreshToken(${serviceId}): got token (${token.slice(0, 8)}…${token.slice(-4)}), changed=${changed}`);\n this.tokenManager.setToken(serviceId, token);\n const conn = this.connections.get(serviceId);\n if (conn) {\n this.connections.set(serviceId, { ...conn, accessToken: token });\n }\n return token;\n }\n console.warn(`[AnyMux] refreshToken(${serviceId}): getAccessToken returned null`);\n } catch (err) {\n console.warn(`[AnyMux] refreshToken(${serviceId}): error:`, err instanceof Error ? err.message : err);\n }\n console.warn(`[AnyMux] refreshToken(${serviceId}): falling back to stored token (${oldToken ? oldToken.slice(0, 8) + '…' : 'null'})`);\n return oldToken;\n }\n\n /** Get the full user profile for a connected service */\n getUserProfile(serviceId: string): IUserProfile | undefined {\n return this.userProfiles.get(serviceId);\n }\n\n /** @deprecated Use getUserProfile() instead. Kept for backward compat. */\n getUserInfo(serviceId: string): { name: string; image?: string } | null {\n const profile = this.userProfiles.get(serviceId);\n if (!profile) return null;\n return { name: profile.name, ...(profile.avatarUrl ? { image: profile.avatarUrl } : {}) };\n }\n\n hasCapabilityScopes(serviceId: string, capabilityId: CapabilityId): boolean {\n const service = serviceRegistry.get(serviceId);\n if (!service) return false;\n // S3/WebDAV/Gitea don't use OAuth scopes — treat all as granted\n if (service.authProvider === 's3' || service.authProvider === 'webdav' || service.authProvider === 'gitea' || service.authProvider === 'browser-fs' || service.authProvider === 'indexeddb') return true;\n const requiredScopes = service.scopes[capabilityId];\n if (!requiredScopes || requiredScopes.length === 0) return false;\n // If scopes haven't been loaded yet, assume granted to avoid a flash warning\n if (!this.grantedScopes.has(service.authProvider)) return true;\n const granted = this.grantedScopes.get(service.authProvider)!;\n return requiredScopes.every(s => granted.includes(s));\n }\n}\n","import { makeAutoObservable } from 'mobx';\n\nexport type ActionType = 'delete' | 'move' | 'rename' | 'upload' | 'create' | 'copy';\n\nexport interface ActionRecord {\n id: string;\n type: ActionType;\n description: string;\n timestamp: Date;\n /** Undo function — called when user clicks Undo */\n undo?: () => Promise<void>;\n /** Whether undo was executed */\n undone: boolean;\n}\n\nlet nextId = 1;\n\n/**\n * Tracks user actions for toast notifications with undo support.\n * Uses sonner's toast() externally — this model just manages the action log.\n */\nexport class ActionNotificationModel {\n actions: ActionRecord[] = [];\n /** Maximum actions to keep in history */\n maxHistory = 100;\n\n constructor() {\n makeAutoObservable(this, {});\n }\n\n /**\n * Record an action. Returns the action ID for reference.\n * The caller is responsible for showing the toast via sonner's toast().\n */\n record(type: ActionType, description: string, undo?: () => Promise<void>): string {\n const id = `action-${nextId++}`;\n const action: ActionRecord = {\n id,\n type,\n description,\n timestamp: new Date(),\n undo,\n undone: false,\n };\n this.actions.unshift(action);\n\n // Trim history\n if (this.actions.length > this.maxHistory) {\n this.actions = this.actions.slice(0, this.maxHistory);\n }\n\n return id;\n }\n\n /** Mark an action as undone */\n markUndone(id: string) {\n const action = this.actions.find((a) => a.id === id);\n if (action) action.undone = true;\n }\n\n get recentActions(): ActionRecord[] {\n return this.actions.slice(0, 20);\n }\n\n get undoableActions(): ActionRecord[] {\n return this.actions.filter((a) => a.undo && !a.undone);\n }\n\n clear() {\n this.actions = [];\n }\n}\n","import { makeAutoObservable } from 'mobx';\nimport type { CapabilityId } from '../types/service';\nimport type { ConnectionManagerModel } from './ConnectionManagerModel';\nimport { ActionNotificationModel } from './ActionNotificationModel';\nimport { serviceRegistry } from '../registry/service-registry';\n\nexport interface SelectedCell {\n serviceId: string;\n capabilityId: CapabilityId;\n}\n\nexport interface GitBrowserState {\n serviceId: string;\n owner?: string;\n repo?: string;\n ref?: string;\n path?: string;\n}\n\nconst REPO_STORAGE_KEY = 'anymux-selected-repos';\n\nexport class DashboardModel {\n selectedCell: SelectedCell | null = null;\n panelOpen = false;\n /** Generic repo selection per service (replaces githubRepo) */\n selectedRepos: Record<string, { owner: string; repo: string }> = {};\n browserPath = '/';\n gitBrowserState: GitBrowserState | null = null;\n actionNotifications = new ActionNotificationModel();\n private connectionManager: ConnectionManagerModel;\n /** Optional callback when selected cell changes (for URL sync) */\n onCellChange?: (cell: SelectedCell | null) => void;\n /** Optional callback when browser path changes (for URL sync) */\n onPathChange?: (path: string) => void;\n\n constructor(connectionManager: ConnectionManagerModel) {\n this.connectionManager = connectionManager;\n makeAutoObservable(this, { onCellChange: false, onPathChange: false });\n // Load repos from localStorage\n try {\n const stored = localStorage.getItem(REPO_STORAGE_KEY);\n if (stored) this.selectedRepos = JSON.parse(stored);\n // Migrate old github-repo key\n const oldGithub = localStorage.getItem('anymux-github-repo');\n if (oldGithub && !this.selectedRepos['github']) {\n this.selectedRepos['github'] = JSON.parse(oldGithub);\n this.persistRepos();\n localStorage.removeItem('anymux-github-repo');\n }\n } catch {}\n }\n\n /** @deprecated Use getSelectedRepo('github') instead */\n get githubRepo(): { owner: string; repo: string } | null {\n return this.selectedRepos['github'] ?? null;\n }\n\n /** @deprecated Use setSelectedRepo('github', repo) instead */\n setGitHubRepo(repo: { owner: string; repo: string }): void {\n this.setSelectedRepo('github', repo);\n }\n\n /** @deprecated Use clearSelectedRepo('github') instead */\n clearGitHubRepo(): void {\n this.clearSelectedRepo('github');\n }\n\n setSelectedRepo(serviceId: string, repo: { owner: string; repo: string }): void {\n this.selectedRepos[serviceId] = repo;\n this.persistRepos();\n }\n\n clearSelectedRepo(serviceId: string): void {\n delete this.selectedRepos[serviceId];\n this.persistRepos();\n }\n\n getSelectedRepo(serviceId: string): { owner: string; repo: string } | null {\n return this.selectedRepos[serviceId] ?? null;\n }\n\n private persistRepos(): void {\n try {\n localStorage.setItem(REPO_STORAGE_KEY, JSON.stringify(this.selectedRepos));\n } catch {}\n }\n\n selectCell(serviceId: string, capabilityId: CapabilityId): void {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n const capability = service.capabilities.find((c) => c.id === capabilityId);\n if (!capability?.supported) return;\n\n // Clear selected repo so the picker always shows when clicking a cell\n if (capabilityId === 'git-repo' || capabilityId === 'git-host' || capabilityId === 'file-system') {\n this.clearSelectedRepo(serviceId);\n }\n if (!this.connectionManager.isConnected(serviceId)) return;\n\n this.selectedCell = { serviceId, capabilityId };\n this.panelOpen = true;\n this.onCellChange?.({ serviceId, capabilityId });\n }\n\n /** Open a cell without checking connection/capability (used for URL restore) */\n openCell(serviceId: string, capabilityId: CapabilityId): void {\n // Clear selected repo — URL doesn't carry repo info, so always show picker\n if (capabilityId === 'git-repo' || capabilityId === 'git-host' || capabilityId === 'file-system') {\n this.clearSelectedRepo(serviceId);\n }\n this.selectedCell = { serviceId, capabilityId };\n this.panelOpen = true;\n }\n\n setBrowserPath(path: string): void {\n this.browserPath = path;\n this.onPathChange?.(path);\n }\n\n setGitBrowserState(state: GitBrowserState | null): void {\n this.gitBrowserState = state;\n }\n\n closePanel(): void {\n this.selectedCell = null;\n this.panelOpen = false;\n this.browserPath = '/';\n this.gitBrowserState = null;\n this.onCellChange?.(null);\n }\n\n /** Close panel without triggering onCellChange (used for URL-driven state sync, e.g. browser back) */\n closePanelSilent(): void {\n this.selectedCell = null;\n this.panelOpen = false;\n this.browserPath = '/';\n this.gitBrowserState = null;\n }\n\n /** Set browser path without triggering onPathChange (used for URL-driven state sync) */\n setBrowserPathSilent(path: string): void {\n this.browserPath = path;\n }\n\n get selectedService() {\n if (!this.selectedCell) return null;\n return serviceRegistry.get(this.selectedCell.serviceId) ?? null;\n }\n\n get selectedCapability() {\n if (!this.selectedCell || !this.selectedService) return null;\n return this.selectedService.capabilities.find(\n (c) => c.id === this.selectedCell!.capabilityId\n ) ?? null;\n }\n}\n","import { makeAutoObservable } from 'mobx';\n\nexport type CredentialServiceType = 's3' | 'webdav' | 'gitea' | 'icloud';\n\nexport class CredentialFormModel {\n open = false;\n serviceType: CredentialServiceType = 's3';\n\n // S3 fields\n accessKeyId = '';\n secretAccessKey = '';\n region = 'us-east-1';\n bucket = '';\n endpoint = '';\n\n // WebDAV fields\n url = '';\n username = '';\n password = '';\n\n // Gitea fields\n token = '';\n owner = '';\n repo = '';\n // Gitea also uses url, username, password above\n\n // iCloud fields\n email = '';\n appPassword = '';\n\n constructor() {\n makeAutoObservable(this);\n }\n\n openForm(serviceType: CredentialServiceType, prefill?: Record<string, string>): void {\n this.serviceType = serviceType;\n this.resetFields();\n if (prefill) this.applyPrefill(prefill);\n this.open = true;\n }\n\n closeForm(): void {\n this.open = false;\n }\n\n setField(field: string, value: string): void {\n if (field in this) {\n (this as Record<string, unknown>)[field] = value;\n }\n }\n\n /** Serialize current form state to JSON credential string */\n serialize(): string {\n switch (this.serviceType) {\n case 's3': {\n const creds: Record<string, string> = {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n bucket: this.bucket,\n };\n if (this.endpoint) creds.endpoint = this.endpoint;\n return JSON.stringify(creds);\n }\n case 'webdav':\n return JSON.stringify({\n url: this.url,\n username: this.username,\n password: this.password,\n });\n case 'gitea':\n return JSON.stringify({\n url: this.url,\n username: this.username,\n password: this.password,\n token: this.token,\n owner: this.owner,\n repo: this.repo,\n });\n case 'icloud':\n return JSON.stringify({\n email: this.email,\n appPassword: this.appPassword,\n });\n }\n }\n\n private resetFields(): void {\n this.accessKeyId = '';\n this.secretAccessKey = '';\n this.region = 'us-east-1';\n this.bucket = '';\n this.endpoint = '';\n this.url = '';\n this.username = '';\n this.password = '';\n this.token = '';\n this.owner = '';\n this.repo = '';\n this.email = '';\n this.appPassword = '';\n }\n\n private applyPrefill(values: Record<string, string>): void {\n for (const [k, v] of Object.entries(values)) {\n if (typeof v === 'string' && k in this) {\n (this as Record<string, unknown>)[k] = v;\n }\n }\n }\n}\n","import React from 'react';\nimport * as icons from 'lucide-react';\nimport type { LucideProps } from 'lucide-react';\n\n/** Custom icons for services not available in lucide-react */\nconst AppleIcon: React.FC<LucideProps> = ({ size = 24, color = 'currentColor', strokeWidth: _sw, ...props }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill={color}\n {...props}\n >\n <path d=\"M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z\" />\n </svg>\n);\n\nconst customIcons: Record<string, React.FC<LucideProps>> = {\n Apple: AppleIcon,\n};\n\ninterface ServiceIconProps extends LucideProps {\n name: string;\n}\n\nexport const ServiceIcon: React.FC<ServiceIconProps> = ({ name, ...props }) => {\n const CustomIcon = customIcons[name];\n if (CustomIcon) {\n return <CustomIcon {...props} />;\n }\n const Icon = (icons as unknown as Record<string, React.FC<LucideProps>>)[name];\n if (!Icon) {\n return <icons.HelpCircle {...props} />;\n }\n return <Icon {...props} />;\n};\n","import { match } from 'ts-pattern';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { IUserProfile } from './user-profile';\n\nexport type ServiceConnectionState =\n | { status: 'loading' }\n | { status: 'not_configured' }\n | { status: 'disconnected' }\n | { status: 'connecting' }\n | { status: 'connected'; token: string; user: { name: string; image?: string } | null; profile: IUserProfile | undefined }\n | { status: 'error'; error?: string }\n | { status: 'expired' };\n\nexport function getServiceConnectionState(\n connectionManager: ConnectionManagerModel,\n serviceId: string\n): ServiceConnectionState {\n const status = connectionManager.getStatus(serviceId);\n return match(status)\n .with('loading', () => ({ status: 'loading' as const }))\n .with('not_configured', () => ({ status: 'not_configured' as const }))\n .with('disconnected', () => ({ status: 'disconnected' as const }))\n .with('connecting', () => ({ status: 'connecting' as const }))\n .with('connected', () => ({\n status: 'connected' as const,\n token: connectionManager.getToken(serviceId) ?? '',\n user: connectionManager.getUserInfo(serviceId),\n profile: connectionManager.getUserProfile(serviceId),\n }))\n .with('error', () => ({ status: 'error' as const }))\n .with('expired', () => ({ status: 'expired' as const }))\n .exhaustive();\n}\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CredentialFormModel } from '../models/CredentialFormModel';\n\ninterface CredentialFormProps {\n model: CredentialFormModel;\n onSubmit: (credentials: string) => void;\n}\n\nexport const CredentialForm: React.FC<CredentialFormProps> = observer(({ model, onSubmit }) => {\n if (!model.open) return null;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n onSubmit(model.serialize());\n };\n\n const inputClass =\n 'w-full rounded-md border border-border px-3 py-2 text-base sm:text-sm bg-card text-foreground focus:border-ring focus:outline-none focus:ring-1 focus:ring-ring';\n const labelClass = 'block text-sm font-medium text-foreground mb-1';\n\n const title = model.serviceType === 's3' ? 'S3 Credentials' : model.serviceType === 'webdav' ? 'WebDAV Credentials' : model.serviceType === 'icloud' ? 'iCloud Credentials' : 'Gitea Credentials';\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\" onClick={() => model.closeForm()}>\n <div\n className=\"w-[calc(100%-2rem)] max-w-md max-h-[80vh] flex flex-col rounded-lg bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <h2 className=\"px-4 sm:px-6 pt-4 sm:pt-6 pb-3 text-lg font-semibold text-foreground flex-shrink-0\">{title}</h2>\n\n <form onSubmit={handleSubmit} className=\"flex flex-col flex-1 min-h-0\">\n <div className=\"flex-1 overflow-y-auto px-4 sm:px-6 space-y-3\">\n {model.serviceType === 's3' ? (\n <>\n <div>\n <label className={labelClass}>Access Key ID</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.accessKeyId}\n onChange={(e) => model.setField('accessKeyId', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Secret Access Key</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.secretAccessKey}\n onChange={(e) => model.setField('secretAccessKey', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Region</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.region}\n onChange={(e) => model.setField('region', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Bucket</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.bucket}\n onChange={(e) => model.setField('bucket', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Endpoint (optional)</label>\n <input\n type=\"text\"\n className={inputClass}\n value={model.endpoint}\n onChange={(e) => model.setField('endpoint', e.target.value)}\n placeholder=\"https://s3.amazonaws.com\"\n />\n </div>\n </>\n ) : model.serviceType === 'webdav' ? (\n <>\n <div>\n <label className={labelClass}>URL</label>\n <input\n type=\"url\"\n required\n className={inputClass}\n value={model.url}\n onChange={(e) => model.setField('url', e.target.value)}\n placeholder=\"https://example.com/webdav\"\n />\n </div>\n <div>\n <label className={labelClass}>Username</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.username}\n onChange={(e) => model.setField('username', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Password</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.password}\n onChange={(e) => model.setField('password', e.target.value)}\n />\n </div>\n </>\n ) : model.serviceType === 'icloud' ? (\n <>\n <div>\n <label className={labelClass}>Apple ID Email</label>\n <input\n type=\"email\"\n required\n className={inputClass}\n value={model.email}\n onChange={(e) => model.setField('email', e.target.value)}\n placeholder=\"you@icloud.com\"\n />\n </div>\n <div>\n <label className={labelClass}>App-Specific Password</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.appPassword}\n onChange={(e) => model.setField('appPassword', e.target.value)}\n placeholder=\"xxxx-xxxx-xxxx-xxxx\"\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Generate an app-specific password at{' '}\n <a\n href=\"https://appleid.apple.com/account/manage\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline text-primary hover:text-primary/80\"\n >\n appleid.apple.com\n </a>\n {' '}under Sign-In and Security &rarr; App-Specific Passwords.\n </p>\n </>\n ) : (\n <>\n <div>\n <label className={labelClass}>URL</label>\n <input\n type=\"url\"\n required\n className={inputClass}\n value={model.url}\n onChange={(e) => model.setField('url', e.target.value)}\n placeholder=\"https://gitea.example.com\"\n />\n </div>\n <div>\n <label className={labelClass}>Token (or use username/password below)</label>\n <input\n type=\"password\"\n className={inputClass}\n value={model.token}\n onChange={(e) => model.setField('token', e.target.value)}\n placeholder=\"API token\"\n />\n </div>\n {!model.token && (\n <>\n <div>\n <label className={labelClass}>Username</label>\n <input\n type=\"text\"\n className={inputClass}\n value={model.username}\n onChange={(e) => model.setField('username', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Password</label>\n <input\n type=\"password\"\n className={inputClass}\n value={model.password}\n onChange={(e) => model.setField('password', e.target.value)}\n />\n </div>\n </>\n )}\n <div>\n <label className={labelClass}>Owner</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.owner}\n onChange={(e) => model.setField('owner', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Repo</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.repo}\n onChange={(e) => model.setField('repo', e.target.value)}\n />\n </div>\n </>\n )}\n </div>\n\n <div className=\"flex justify-end gap-2 px-4 sm:px-6 py-3 border-t border-border flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => model.closeForm()}\n className=\"rounded-md px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n Connect\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n});\n","import React from 'react';\nimport { ExternalLink, Info, LogOut, MoreHorizontal, RefreshCw, Clock, Shield } from 'lucide-react';\nimport { ConnectionBadge } from '@anymux/ui/components/connection-badge';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@anymux/ui/components/dropdown-menu';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ServiceDefinition } from '../types/service';\nimport type { IUserProfile } from '../types/user-profile';\nimport { ServiceIcon } from './ServiceIcon';\n\ninterface ConnectedMenuProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n /** @deprecated Use profile instead */\n user: { name: string; image?: string } | null;\n profile?: IUserProfile;\n isOAuth: boolean;\n}\n\nfunction formatConnectedDate(date: Date | undefined): string | null {\n if (!date) return null;\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return null;\n return d.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nexport function ConnectedMenu({\n service,\n connectionManager,\n user,\n profile,\n isOAuth,\n}: ConnectedMenuProps) {\n // Prefer profile data (provider-specific) over generic session user data\n const displayName = profile?.name ?? user?.name;\n const avatarUrl = profile?.avatarUrl ?? user?.image;\n const profileUrl = profile?.profileUrl;\n const email = profile?.email;\n\n const handleReconnect = async () => {\n await connectionManager.disconnect(service.id);\n await connectionManager.connect(service.id);\n };\n\n const handleDisconnect = () => {\n connectionManager.disconnect(service.id);\n };\n\n const connection = connectionManager.connections.get(service.id);\n const connectedDate = formatConnectedDate(connection?.connectedAt);\n\n return (\n <div className=\"inline-flex items-center gap-1\">\n <ConnectionBadge\n status=\"connected\"\n {...(displayName ? { name: displayName } : {})}\n {...(avatarUrl ? { avatarUrl } : {})}\n />\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button className=\"inline-flex items-center justify-center rounded-md p-1 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-[200px]\">\n {/* Provider header with per-service profile info */}\n <DropdownMenuLabel className=\"flex items-center gap-2\">\n {avatarUrl ? (\n <img src={avatarUrl} alt=\"\" className=\"h-5 w-5 rounded-full shrink-0\" />\n ) : (\n <ServiceIcon\n name={service.icon}\n className=\"h-4 w-4 shrink-0\"\n style={{ color: service.color }}\n />\n )}\n <div className=\"flex flex-col min-w-0\">\n <span className=\"truncate\">{displayName ?? service.name}</span>\n {email && (\n <span className=\"text-xs font-normal text-muted-foreground truncate\">{email}</span>\n )}\n </div>\n </DropdownMenuLabel>\n\n {/* Connection timestamp */}\n {connectedDate && (\n <div className=\"flex items-center gap-2 px-2 pb-1.5 text-xs text-muted-foreground\">\n <Clock className=\"h-3 w-3 shrink-0\" />\n <span>Connected {connectedDate}</span>\n </div>\n )}\n\n <DropdownMenuSeparator />\n\n {/* Actions */}\n {profileUrl && (\n <DropdownMenuItem asChild>\n <a href={profileUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink />\n Open Profile\n </a>\n </DropdownMenuItem>\n )}\n {service.grantsUrl && (\n <DropdownMenuItem asChild>\n <a href={service.grantsUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <Shield />\n Manage Permissions\n </a>\n </DropdownMenuItem>\n )}\n <DropdownMenuItem asChild>\n <a href={`/providers/${service.id}`}>\n <Info />\n About {service.name}\n </a>\n </DropdownMenuItem>\n {isOAuth && (\n <DropdownMenuItem onClick={handleReconnect}>\n <RefreshCw />\n Reconnect\n </DropdownMenuItem>\n )}\n <DropdownMenuItem variant=\"destructive\" onClick={handleDisconnect}>\n <LogOut />\n Disconnect\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { match } from 'ts-pattern';\nimport { ConnectionBadge } from '@anymux/ui/components/connection-badge';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ServiceDefinition } from '../types/service';\nimport { getServiceConnectionState } from '../types/connection-state';\nimport { CredentialFormModel, type CredentialServiceType } from '../models/CredentialFormModel';\nimport { CredentialForm } from './CredentialForm';\nimport { ConnectedMenu } from './ConnectedMenu';\n\ninterface ConnectButtonProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const ConnectButton: React.FC<ConnectButtonProps> = observer(({ service, connectionManager }) => {\n const [formModel] = useState(() => new CredentialFormModel());\n const state = getServiceConnectionState(connectionManager, service.id);\n\n const needsCredentialForm = service.authProvider === 's3' || service.authProvider === 'webdav' || service.authProvider === 'gitea' || service.authProvider === 'icloud';\n const isBrowserFs = service.authProvider === 'browser-fs';\n const isIndexedDb = service.authProvider === 'indexeddb';\n\n // Auto-trigger connect flow when reconnect is requested (e.g. from CapabilityPanel)\n const pendingReconnect = connectionManager.pendingReconnect.has(service.id);\n useEffect(() => {\n if (pendingReconnect) {\n connectionManager.clearReconnectRequest(service.id);\n handleConnect();\n }\n }, [pendingReconnect]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleConnect = async () => {\n if (isBrowserFs) {\n try {\n const { BrowserFileSystemFactory } = await import('@anymux/browser-fs');\n const factory = new BrowserFileSystemFactory();\n const [_fs, handleInfo] = await factory.createFromPicker();\n connectionManager.connectWithCredentials(service.id, JSON.stringify({ id: handleInfo.id, name: handleInfo.name }));\n } catch (err: any) {\n if (err.name !== 'AbortError') {\n console.error('Failed to pick directory:', err);\n }\n }\n return;\n }\n if (isIndexedDb) {\n connectionManager.connectWithCredentials(service.id, JSON.stringify({ type: 'indexeddb' }));\n return;\n }\n if (needsCredentialForm) {\n formModel.openForm(service.authProvider as CredentialServiceType);\n } else {\n connectionManager.connect(service.id);\n }\n };\n\n const handleCredentialSubmit = (credentialToken: string) => {\n connectionManager.connectWithCredentials(service.id, credentialToken);\n formModel.closeForm();\n };\n\n const credentialFormElement = needsCredentialForm ? (\n <CredentialForm model={formModel} onSubmit={handleCredentialSubmit} />\n ) : null;\n\n return match(state)\n .with({ status: 'loading' }, () => (\n <ConnectionBadge status=\"loading\" />\n ))\n .with({ status: 'not_configured' }, () => (\n <ConnectionBadge status=\"not-configured\" />\n ))\n .with({ status: 'connecting' }, () => (\n <ConnectionBadge status=\"connecting\" />\n ))\n .with({ status: 'connected' }, (s) => {\n const isOAuth = !needsCredentialForm && !isBrowserFs && !isIndexedDb;\n const user = isOAuth ? s.user : null;\n return (\n <ConnectedMenu\n service={service}\n connectionManager={connectionManager}\n user={user}\n {...(s.profile ? { profile: s.profile } : {})}\n isOAuth={isOAuth}\n />\n );\n })\n .with({ status: 'error' }, () => (\n <>\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white bg-destructive hover:bg-destructive/90 transition-colors\"\n >\n Retry\n </button>\n {credentialFormElement}\n </>\n ))\n .with({ status: 'expired' }, () => (\n <>\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white bg-destructive hover:bg-destructive/90 transition-colors\"\n >\n Reconnect\n </button>\n {credentialFormElement}\n </>\n ))\n .with({ status: 'disconnected' }, () => {\n const testCreds = connectionManager.testCredentials[service.authProvider] as Record<string, unknown> | undefined;\n return (\n <>\n <div className=\"inline-flex items-center gap-1.5\">\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white transition-colors\"\n style={{ backgroundColor: service.color }}\n >\n Connect\n </button>\n {testCreds && (\n <button\n onClick={() => {\n if (needsCredentialForm) {\n const values: Record<string, string> = {};\n for (const [k, v] of Object.entries(testCreds)) {\n if (typeof v === 'string') values[k] = v;\n }\n formModel.openForm(service.authProvider as CredentialServiceType, values);\n } else {\n connectionManager.connectWithCredentials(service.id, JSON.stringify(testCreds));\n }\n }}\n className=\"rounded-md px-2 py-1.5 text-xs font-medium text-muted-foreground bg-muted hover:bg-muted/80 transition-colors\"\n >\n Quick Test\n </button>\n )}\n </div>\n {credentialFormElement}\n </>\n );\n })\n .exhaustive();\n});\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { Check, Minus } from 'lucide-react';\nimport { match } from 'ts-pattern';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\n\ninterface CapabilityCellProps {\n service: ServiceDefinition;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const CapabilityCell: React.FC<CapabilityCellProps> = observer(\n ({ service, capabilityId, connectionManager, dashboardModel }) => {\n const capability = service.capabilities.find((c) => c.id === capabilityId);\n // For merged git column: show as supported if either git-repo or git-host is supported\n const gitHostCap = capabilityId === 'git-repo'\n ? service.capabilities.find((c) => c.id === 'git-host')\n : undefined;\n const supported = (capability?.supported ?? false) || (gitHostCap?.supported ?? false);\n // Route to the right capability when clicking: prefer git-repo, fallback to git-host\n const effectiveCapabilityId = capabilityId === 'git-repo' && !capability?.supported && gitHostCap?.supported\n ? 'git-host' as CapabilityId\n : capabilityId;\n const status = connectionManager.getStatus(service.id);\n const isSelected =\n dashboardModel.selectedCell?.serviceId === service.id &&\n (dashboardModel.selectedCell?.capabilityId === effectiveCapabilityId ||\n (capabilityId === 'git-repo' && dashboardModel.selectedCell?.capabilityId === 'git-host'));\n\n if (!supported) {\n return (\n <td className=\"px-3 py-2 text-center align-middle\">\n <Minus className=\"h-4 w-4 text-muted-foreground/30 mx-auto\" />\n </td>\n );\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n dashboardModel.selectCell(service.id, effectiveCapabilityId);\n }\n };\n\n return match(status)\n .with('loading', () => (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-block h-4 w-4 rounded bg-muted animate-pulse mx-auto\" />\n </td>\n ))\n .with('connected', () => {\n // Don't show green/orange until scopes have been fetched\n if (!connectionManager.initialized) {\n return (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-block h-4 w-4 rounded bg-muted animate-pulse mx-auto\" />\n </td>\n );\n }\n const hasScopes = connectionManager.hasCapabilityScopes(service.id, effectiveCapabilityId);\n const hoverBg = hasScopes\n ? 'hover:bg-green-50 dark:hover:bg-green-900/20'\n : 'hover:bg-orange-50 dark:hover:bg-orange-900/20';\n return (\n <td\n className={`px-3 py-2 text-center align-middle cursor-pointer transition-colors ${\n isSelected\n ? 'bg-blue-100 dark:bg-blue-900/40'\n : hoverBg\n }`}\n role=\"button\"\n tabIndex={0}\n onClick={() => dashboardModel.selectCell(service.id, effectiveCapabilityId)}\n onKeyDown={handleKeyDown}\n title={hasScopes ? undefined : 'Scope not granted'}\n >\n <span className={`inline-flex items-center justify-center w-5 h-5 rounded-full mx-auto ${\n hasScopes ? 'bg-green-500' : 'bg-orange-400'\n } text-white`}>\n <Check className=\"h-3 w-3\" />\n </span>\n </td>\n );\n })\n .otherwise(() => (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-flex items-center justify-center w-5 h-5 rounded border border-muted-foreground/30 mx-auto\">\n <Check className=\"h-3 w-3 text-muted-foreground/50\" />\n </span>\n </td>\n ));\n }\n);\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { ServiceIcon } from './ServiceIcon';\nimport { ConnectButton } from './ConnectButton';\nimport { CapabilityCell } from './CapabilityCell';\n\nconst CAPABILITY_COLUMNS: CapabilityId[] = [\n 'file-system',\n 'object-storage',\n 'git-repo',\n 'media',\n 'contacts',\n 'calendar',\n];\n\ninterface ServiceRowProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceRow: React.FC<ServiceRowProps> = observer(\n ({ service, connectionManager, dashboardModel }) => {\n return (\n <tr className=\"border-b border-border h-11\">\n <td className=\"px-3 py-2 align-middle\">\n <div className=\"flex items-center gap-2\">\n <ServiceIcon name={service.icon} className=\"h-4 w-4\" style={{ color: service.color }} />\n <a href={`/providers/${service.id}`} className=\"text-sm font-medium truncate hover:underline\" title={service.name}>{service.name}</a>\n </div>\n </td>\n <td className=\"px-3 py-2 align-middle\">\n <ConnectButton service={service} connectionManager={connectionManager} />\n </td>\n {CAPABILITY_COLUMNS.map((cap) => (\n <CapabilityCell\n key={cap}\n service={service}\n capabilityId={cap}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </tr>\n );\n }\n);\n","import React from 'react';\nimport { Check, Minus } from 'lucide-react';\nimport { match } from 'ts-pattern';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { ConnectionStatus } from '../types/connection';\nimport type { CapabilityId } from '../types/service';\n\ninterface CapabilityPillProps {\n label: string;\n capabilityId: CapabilityId;\n status: ConnectionStatus;\n isSelected: boolean;\n hasScopes: boolean;\n onSelect: () => void;\n disabled?: boolean;\n}\n\nexport const CapabilityPill: React.FC<CapabilityPillProps> = ({\n label,\n status,\n isSelected,\n hasScopes,\n onSelect,\n disabled = false,\n}) => {\n const connected = status === 'connected';\n const loading = status === 'loading';\n\n const className = `inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium transition-colors ${\n loading\n ? 'bg-muted text-muted-foreground'\n : isSelected\n ? 'bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300'\n : connected\n ? 'bg-muted text-foreground hover:bg-muted/80 cursor-pointer'\n : 'bg-muted/50 text-muted-foreground'\n }`;\n\n const icon = match(status)\n .with('loading', () => <LoadingSpinner size=\"sm\" label=\"Loading\" className=\"h-3 w-3\" />)\n .with('connected', () => (\n <Check className={`h-3 w-3 ${hasScopes ? 'text-green-500' : 'text-orange-400'}`} />\n ))\n .otherwise(() => <Minus className=\"h-3 w-3\" />);\n\n return (\n <button\n onClick={onSelect}\n disabled={disabled || !connected}\n className={className}\n >\n {icon}\n {label}\n </button>\n );\n};\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { ServiceIcon } from './ServiceIcon';\nimport { ConnectButton } from './ConnectButton';\nimport { CapabilityPill } from './CapabilityPill';\n\nconst CAPABILITY_COLUMNS: { id: CapabilityId; label: string }[] = [\n { id: 'file-system', label: 'FS' },\n { id: 'object-storage', label: 'Obj' },\n { id: 'git-repo', label: 'Git' },\n { id: 'media', label: 'Media' },\n { id: 'contacts', label: 'Contacts' },\n { id: 'calendar', label: 'Cal' },\n];\n\ninterface ServiceCardProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceCard: React.FC<ServiceCardProps> = observer(\n ({ service, connectionManager, dashboardModel }) => {\n const status = connectionManager.getStatus(service.id);\n const connected = status === 'connected';\n const supportedCaps = CAPABILITY_COLUMNS.filter((cap) => {\n const supported = service.capabilities.find((c) => c.id === cap.id)?.supported;\n // For merged git column: also check git-host\n if (cap.id === 'git-repo' && !supported) {\n return service.capabilities.find((c) => c.id === 'git-host')?.supported ?? false;\n }\n return supported;\n });\n\n return (\n <div className=\"rounded-lg border border-border bg-card p-3\">\n {/* Top row: icon + name + connect button */}\n <div className=\"flex items-center justify-between gap-2 mb-2\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <ServiceIcon name={service.icon} className=\"h-5 w-5 flex-shrink-0\" style={{ color: service.color }} />\n <span className=\"text-sm font-medium truncate\" title={service.name}>{service.name}</span>\n </div>\n <ConnectButton service={service} connectionManager={connectionManager} />\n </div>\n\n {/* Capability pills */}\n {supportedCaps.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {supportedCaps.map((cap) => {\n const isSelected =\n dashboardModel.selectedCell?.serviceId === service.id &&\n dashboardModel.selectedCell?.capabilityId === cap.id;\n const hasScopes = connected && connectionManager.hasCapabilityScopes(service.id, cap.id);\n\n return (\n <CapabilityPill\n key={cap.id}\n label={cap.label}\n capabilityId={cap.id}\n status={status}\n isSelected={isSelected}\n hasScopes={hasScopes}\n onSelect={() => {\n if (connected) dashboardModel.selectCell(service.id, cap.id);\n }}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n }\n);\n","import type { CapabilityId } from '../types/service';\n\nexport interface AdapterContext {\n /** Selected repo for git-backed services */\n selectedRepo?: { owner: string; repo: string };\n /** Override the branch/ref for file-system adapters */\n branch?: string;\n}\n\nexport type AdapterFactory = (\n token: string,\n capabilityId: CapabilityId,\n context: AdapterContext,\n) => Promise<unknown>;\n\nconst factories = new Map<string, AdapterFactory>();\n\nexport const adapterRegistry = {\n register(serviceId: string, factory: AdapterFactory): void {\n factories.set(serviceId, factory);\n },\n\n async create(\n serviceId: string,\n token: string,\n capabilityId: CapabilityId,\n context: AdapterContext = {},\n ): Promise<unknown> {\n const factory = factories.get(serviceId);\n if (!factory) {\n throw new Error(`No adapter factory registered for service: ${serviceId}`);\n }\n return factory(token, capabilityId, context);\n },\n\n has(serviceId: string): boolean {\n return factories.has(serviceId);\n },\n};\n\n// --- Register all adapter factories ---\n// Capability validation is handled by the service registry; factories only create adapters.\n\n// Google Drive / Contacts / Calendar\nadapterRegistry.register('google', async (token, capabilityId) => {\n const m = await import('@anymux/google-drive');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GoogleDriveFileSystem({ accessToken: token }),\n 'contacts': () => new m.GoogleContactsProvider(token),\n 'calendar': () => new m.GoogleCalendarProvider(token),\n };\n return adapters[capabilityId]!();\n});\n\n// Dropbox\nadapterRegistry.register('dropbox', async (token) => {\n const { DropboxFileSystem } = await import('@anymux/dropbox');\n return new DropboxFileSystem({ accessToken: token });\n});\n\n// Box\nadapterRegistry.register('box', async (token) => {\n const { BoxFileSystem } = await import('@anymux/box');\n return new BoxFileSystem({ accessToken: token });\n});\n\n// GitHub\nadapterRegistry.register('github', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, owner: repo.owner, repo: repo.repo, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/github');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GitHubFileSystem(config),\n 'git-repo': () => new m.GitHubGitRepo(config),\n 'git-host': () => new m.GitHubGitHost(config),\n };\n return adapters[capabilityId]!();\n});\n\n// GitLab\nadapterRegistry.register('gitlab', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, projectId: `${repo.owner}/${repo.repo}`, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/gitlab');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GitLabFileSystem(config),\n 'git-repo': () => new m.GitLabGitRepo(config),\n 'git-host': () => new m.GitLabGitHost(config),\n };\n return adapters[capabilityId]!();\n});\n\n// Bitbucket\nadapterRegistry.register('bitbucket', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, workspace: repo.owner, repo: repo.repo, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/bitbucket');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.BitbucketFileSystem(config),\n 'git-repo': () => new m.BitbucketGitRepo(config),\n 'git-host': () => new m.BitbucketGitHost(config),\n };\n return adapters[capabilityId]!();\n});\n\n// Gitea\nadapterRegistry.register('gitea', async (token, capabilityId, ctx) => {\n const creds = JSON.parse(token);\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n const config = { url: creds.url, token: creds.token, username: creds.username, password: creds.password, owner: creds.owner, repo: creds.repo, branch: ctx.branch ?? creds.branch ?? 'main', proxyUrl };\n const m = await import('@anymux/gitea');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GiteaFileSystem(config),\n 'git-repo': () => new m.GiteaGitRepo(config),\n 'git-host': () => new m.GiteaGitHost(config),\n };\n return adapters[capabilityId]!();\n});\n\n// WebDAV\nadapterRegistry.register('webdav', async (token) => {\n const creds = JSON.parse(token);\n const { WebDAVFileSystem } = await import('@anymux/webdav');\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n return new WebDAVFileSystem({ url: creds.url, username: creds.username, password: creds.password, proxyUrl });\n});\n\n// S3\nadapterRegistry.register('s3', async (token) => {\n const creds = JSON.parse(token);\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n if (proxyUrl) {\n // Browser: use proxy to avoid CORS\n const { S3ProxyStorage } = await import('@anymux/s3');\n const storage = new S3ProxyStorage({\n proxyUrl,\n endpoint: creds.endpoint,\n region: creds.region?.trim(),\n accessKeyId: creds.accessKeyId?.trim(),\n secretAccessKey: creds.secretAccessKey?.trim(),\n forcePathStyle: true,\n });\n return { storage, bucket: creds.bucket || 'anymux-test' };\n }\n // Server: use direct SDK\n const { S3ObjectStorage } = await import('@anymux/s3');\n const storage = new S3ObjectStorage({\n region: creds.region?.trim(),\n endpoint: creds.endpoint,\n credentials: { accessKeyId: creds.accessKeyId?.trim(), secretAccessKey: creds.secretAccessKey?.trim() },\n forcePathStyle: true,\n });\n return { storage, bucket: creds.bucket || 'anymux-test' };\n});\n\n// BrowserFS (File System Access API)\nadapterRegistry.register('browser-fs', async () => {\n const { BrowserFileSystemFactory } = await import('@anymux/browser-fs');\n const factory = new BrowserFileSystemFactory();\n const handleInfos = await factory.getAllHandleInfos();\n if (handleInfos.length > 0) {\n return factory.createFromHandleInfo(handleInfos[0]!.id);\n }\n throw new Error('No local file system connected. Please connect first.');\n});\n\n// IndexedDB\nadapterRegistry.register('indexeddb', async () => {\n const { IndexedDBFileSystem } = await import('@anymux/indexeddb');\n const fs = new IndexedDBFileSystem();\n await fs.init();\n return fs;\n});\n\n","import { useState, useEffect } from 'react';\n\n/** Hook to async-create an adapter, run a pre-flight check, and track loading/error state */\nexport function useAdapter<T>(\n factory: () => Promise<T>,\n deps: unknown[],\n preflight?: (adapter: T) => Promise<void>\n): { adapter: T | null; loading: boolean; error: string | null; retry: () => void } {\n const [adapter, setAdapter] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [retryCount, setRetryCount] = useState(0);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n setAdapter(null);\n\n factory()\n .then(async (a) => {\n // Run pre-flight check (e.g. readdir) to catch auth errors before mounting UI\n if (preflight) await preflight(a);\n if (!cancelled) { setAdapter(a); setLoading(false); }\n })\n .catch((e) => { if (!cancelled) { setError(e instanceof Error ? e.message : String(e)); setLoading(false); } });\n\n return () => { cancelled = true; };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...deps, retryCount]);\n\n return { adapter, loading, error, retry: () => setRetryCount((c) => c + 1) };\n}\n","import React from 'react';\nimport { ArrowLeft, RefreshCw } from 'lucide-react';\nimport { ErrorDisplay } from '@anymux/ui/components/error-display';\n\nfunction isAuthError(error: string): boolean {\n return /\\b(401|403|auth|token|expired|unauthorized|forbidden|denied|scope|permission|sign.?in|log.?in)\\b/i.test(error);\n}\n\nexport { isAuthError };\n\n/** Render error with contextual actions for capability panels */\nexport function CapabilityError({ error, onRetry, onReconnect, onGoBack }: {\n error: string;\n onRetry?: () => void;\n onReconnect?: () => void;\n onGoBack?: () => void;\n}) {\n const authError = isAuthError(error);\n return (\n <div className=\"flex flex-col items-center justify-center h-64 gap-4 p-4\">\n <ErrorDisplay\n error={error}\n title={authError ? 'Authentication Required' : 'Failed to Load'}\n variant={authError ? 'warning' as const : 'destructive' as const}\n className=\"max-w-md w-full\"\n {...(onRetry != null ? { onRetry } : {})}\n />\n <div className=\"flex items-center gap-2\">\n {onReconnect && (\n <button\n onClick={onReconnect}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <RefreshCw className=\"h-3 w-3\" />\n Reconnect\n </button>\n )}\n {onGoBack && (\n <button\n onClick={onGoBack}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-muted hover:bg-muted/80 transition-colors\"\n >\n <ArrowLeft className=\"h-3 w-3\" />\n Back to Dashboard\n </button>\n )}\n </div>\n </div>\n );\n}\n","import React, { Suspense, useState, useCallback } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { showActionToast, showErrorToast } from '../utils/action-toast';\nimport { match } from 'ts-pattern';\nimport { X, ArrowLeft, AlertTriangle } from 'lucide-react';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ActionNotificationModel } from '../models/ActionNotificationModel';\nimport type { CapabilityId } from '../types/service';\nimport type { IFileSystem, IObjectStorage } from '@anymux/file-system';\nimport { adapterRegistry } from '../adapters/adapter-registry';\nimport { useAdapter } from './useAdapter';\nimport { CapabilityError, isAuthError } from './CapabilityError';\n\n// Lazy-load browser components\nconst FileBrowser = React.lazy(() =>\n import('@anymux/fs-ui').then((m) => ({ default: m.FileBrowser }))\n);\n\nconst LazyMediaBrowser = React.lazy(() =>\n import('@anymux/object-ui').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.MediaBrowserModel(provider as never);\n return React.createElement(m.MediaBrowser, { model, provider: provider as never, className: 'h-full' });\n },\n }))\n);\n\nconst LazyContactBrowser = React.lazy(() =>\n import('@anymux/object-ui').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.ContactListModel(provider as never);\n return React.createElement(m.ContactBrowser, { model, className: 'h-full' });\n },\n }))\n);\n\nconst LazyCalendarBrowser = React.lazy(() =>\n import('@anymux/object-ui').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.CalendarModel(provider as never);\n return React.createElement(m.CalendarBrowser, { model, className: 'h-full' });\n },\n }))\n);\n\n// Lazy-load RepoPicker\nconst RepoPicker = React.lazy(() =>\n import('./RepoPicker').then((m) => ({ default: m.RepoPicker }))\n);\n\n// Lazy-load browser components from this package\nconst ObjectStorageBrowserLazy = React.lazy(() =>\n import('./ObjectStorageBrowser').then((m) => ({ default: m.ObjectStorageBrowser }))\n);\n\nconst GitRepoBrowserLazy = React.lazy(() =>\n import('./GitBrowser').then((m) => ({ default: m.GitRepoBrowser }))\n);\n\nconst GitHostBrowserLazy = React.lazy(() =>\n import('./GitBrowser').then((m) => ({ default: m.GitHostBrowser }))\n);\n\nconst CAPABILITY_LABELS: Record<CapabilityId, string> = {\n 'file-system': 'File System',\n 'object-storage': 'Object Storage',\n 'git-repo': 'Git',\n 'git-host': 'Git',\n 'media': 'Media',\n 'contacts': 'Contacts',\n 'calendar': 'Calendar',\n};\n\n// Services that need repo selection before showing capability content\nconst REPO_SERVICES = new Set(['github', 'gitlab', 'bitbucket', 'gitea']);\n\nconst LoadingFallback = () => (\n <div className=\"flex items-center justify-center h-64\">\n <LoadingSpinner label=\"Loading...\" />\n </div>\n);\n\n// --- Git repo browser wrapper that provides createFileSystem ---\n\nfunction GitRepoBrowserContent({\n serviceId,\n token,\n gitRepo,\n owner,\n repo,\n selectedRepo,\n onError,\n actionNotifications,\n}: {\n serviceId: string;\n token: string;\n gitRepo: import('@anymux/file-system').IGitRepo;\n owner: string;\n repo: string;\n selectedRepo?: { owner: string; repo: string };\n onError?: (err: { message: string }) => void;\n actionNotifications?: ActionNotificationModel;\n}) {\n const createFileSystem = useCallback(\n async (branch: string): Promise<IFileSystem> => {\n const context = { selectedRepo, branch };\n const fs = await adapterRegistry.create(serviceId, token, 'file-system', context);\n return fs as IFileSystem;\n },\n [serviceId, token, selectedRepo],\n );\n\n // Also load git-host adapter for PRs/Issues tabs\n const [gitHost, setGitHost] = useState<import('@anymux/file-system').IGitHost | undefined>();\n React.useEffect(() => {\n let cancelled = false;\n adapterRegistry.create(serviceId, token, 'git-host', { selectedRepo })\n .then((adapter) => { if (!cancelled) setGitHost(adapter as any); })\n .catch(() => {}); // Silently fail — PRs/Issues just won't show\n return () => { cancelled = true; };\n }, [serviceId, token, selectedRepo]);\n\n return (\n <GitRepoBrowserLazy\n gitRepo={gitRepo}\n owner={owner}\n repo={repo}\n createFileSystem={createFileSystem}\n gitHost={gitHost}\n onError={onError}\n actionNotifications={actionNotifications}\n />\n );\n}\n\n// --- Git Host wrapper that also loads git-repo adapter for unified browser ---\n\nfunction GitHostBrowserWithRepo({\n serviceId,\n token,\n gitHost,\n owner,\n repo,\n selectedRepo,\n onError,\n actionNotifications,\n}: {\n serviceId: string;\n token: string;\n gitHost: import('@anymux/file-system').IGitHost;\n owner: string;\n repo: string;\n selectedRepo?: { owner: string; repo: string };\n onError?: (err: { message: string }) => void;\n actionNotifications?: ActionNotificationModel;\n}) {\n const [gitRepo, setGitRepo] = useState<import('@anymux/file-system').IGitRepo | undefined>();\n const [repoLoaded, setRepoLoaded] = useState(false);\n\n React.useEffect(() => {\n let cancelled = false;\n adapterRegistry.create(serviceId, token, 'git-repo', { selectedRepo })\n .then((adapter) => { if (!cancelled) setGitRepo(adapter as any); })\n .catch(() => {}) // Silently fail — just show PRs/Issues\n .finally(() => { if (!cancelled) setRepoLoaded(true); });\n return () => { cancelled = true; };\n }, [serviceId, token, selectedRepo]);\n\n const createFileSystem = useCallback(\n async (branch: string): Promise<IFileSystem> => {\n const context = { selectedRepo, branch };\n const fs = await adapterRegistry.create(serviceId, token, 'file-system', context);\n return fs as IFileSystem;\n },\n [serviceId, token, selectedRepo],\n );\n\n if (!repoLoaded) return <LoadingFallback />;\n\n // If we have git-repo, show unified browser with all tabs\n if (gitRepo) {\n return (\n <GitRepoBrowserLazy\n gitRepo={gitRepo}\n owner={owner}\n repo={repo}\n createFileSystem={createFileSystem}\n gitHost={gitHost}\n onError={onError}\n actionNotifications={actionNotifications}\n />\n );\n }\n\n // Fallback: just show PRs/Issues if git-repo adapter not available\n return <GitHostBrowserLazy gitHost={gitHost} />;\n}\n\n// --- Generic capability content renderer ---\n\nconst GenericCapabilityContent = observer(function GenericCapabilityContent({\n serviceId,\n capabilityId,\n token,\n dashboardModel,\n connectionManager,\n}: {\n serviceId: string;\n capabilityId: CapabilityId;\n token: string;\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}) {\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const context = { selectedRepo: selectedRepo ?? undefined };\n\n const { adapter, loading, error, retry } = useAdapter(\n async () => {\n // Refresh OAuth token before creating adapter to avoid stale 401s\n console.info(`[AnyMux] CapabilityPanel: refreshing token for ${serviceId}/${capabilityId}`);\n const freshToken = await connectionManager.refreshToken(serviceId) ?? token;\n console.info(`[AnyMux] CapabilityPanel: creating adapter with token ${freshToken.slice(0, 8)}…${freshToken.slice(-4)}`);\n const a = adapterRegistry.create(serviceId, freshToken, capabilityId, context);\n return a;\n },\n [serviceId, capabilityId, token, selectedRepo?.owner, selectedRepo?.repo],\n // Pre-flight: verify the adapter actually works before mounting the browser UI.\n // If we get a 401, try one more refresh — better-auth may not have detected\n // the token as expired (e.g. missing accessTokenExpiresAt in the DB).\n async (a) => {\n const runCheck = async (adapter: any) => {\n if (capabilityId === 'file-system') {\n await (adapter as IFileSystem).readdir('/');\n } else if (capabilityId === 'git-host') {\n if (adapter.listRepositories) await adapter.listRepositories({ page: 1, perPage: 1 });\n } else if (capabilityId === 'git-repo') {\n if (adapter.listBranches) await adapter.listBranches();\n } else if (capabilityId === 'calendar') {\n if (adapter.getCalendars) await adapter.getCalendars();\n } else if (capabilityId === 'contacts') {\n if (adapter.getContacts) await adapter.getContacts({ limit: 1 });\n } else if (capabilityId === 'media') {\n if (adapter.getAlbums) await adapter.getAlbums();\n }\n };\n\n try {\n console.info(`[AnyMux] CapabilityPanel: running pre-flight for ${serviceId}/${capabilityId}`);\n await runCheck(a);\n console.info(`[AnyMux] CapabilityPanel: pre-flight passed`);\n } catch (err) {\n console.warn(`[AnyMux] CapabilityPanel: pre-flight failed for ${serviceId}/${capabilityId}:`, err instanceof Error ? err.message : err);\n // If we get a 401/auth error, try one more refresh and rebuild the adapter\n if (err instanceof Error && isAuthError(err.message)) {\n console.info(`[AnyMux] CapabilityPanel: auth error detected, attempting second refresh`);\n const retryToken = await connectionManager.refreshToken(serviceId);\n if (retryToken && retryToken !== token) {\n console.info(`[AnyMux] CapabilityPanel: got different token on retry, rebuilding adapter`);\n const retryAdapter = await adapterRegistry.create(serviceId, retryToken, capabilityId, context);\n await runCheck(retryAdapter);\n Object.assign(a as any, retryAdapter);\n return;\n }\n console.warn(`[AnyMux] CapabilityPanel: retry token same as original, giving up`);\n }\n throw err;\n }\n }\n );\n\n const handleReconnect = async () => {\n const service = dashboardModel.selectedService;\n await connectionManager.disconnect(serviceId);\n dashboardModel.closePanel();\n // For OAuth services, immediately redirect to provider consent screen\n const isOAuth = service && !['s3', 'webdav', 'gitea', 'icloud', 'browser-fs', 'indexeddb'].includes(service.authProvider);\n if (isOAuth) {\n await connectionManager.connect(serviceId);\n } else {\n // Signal ConnectButton to auto-open credential form / file picker\n connectionManager.requestReconnect(serviceId);\n }\n };\n\n const handleGoBack = () => {\n dashboardModel.closePanel();\n };\n\n // Track runtime auth errors (e.g. 401 from API calls after adapter is created)\n const [runtimeError, setRuntimeError] = useState<string | null>(null);\n\n const handleRuntimeError = (err: { message: string }) => {\n if (isAuthError(err.message)) {\n setRuntimeError(err.message);\n }\n };\n\n const handleNotify = useCallback((type: 'success' | 'error' | 'warning', message: string) => {\n if (type === 'success') {\n showActionToast(dashboardModel.actionNotifications, 'create', message);\n } else {\n showErrorToast(message);\n }\n }, [dashboardModel.actionNotifications]);\n\n const handleAction = useCallback((action: { type: 'create' | 'rename' | 'delete' | 'upload'; message: string; undo?: () => Promise<void> }) => {\n showActionToast(dashboardModel.actionNotifications, action.type, action.message, { undo: action.undo });\n }, [dashboardModel.actionNotifications]);\n\n if (loading) return <LoadingFallback />;\n if (error) return <CapabilityError error={error} onRetry={retry} onReconnect={handleReconnect} onGoBack={handleGoBack} />;\n if (runtimeError) return <CapabilityError error={runtimeError} onReconnect={handleReconnect} onGoBack={handleGoBack} />;\n if (adapter === null) return null; // Should not happen after repo selection\n\n // Route by capability type\n return match(capabilityId)\n .with('file-system', () => (\n <Suspense fallback={<LoadingFallback />}>\n <FileBrowser\n fileSystem={adapter as IFileSystem}\n className=\"h-full\"\n initialPath={dashboardModel.browserPath !== '/' ? dashboardModel.browserPath : undefined}\n onPathChange={(path) => dashboardModel.setBrowserPath(path)}\n onError={handleRuntimeError}\n onNotify={handleNotify}\n onAction={handleAction}\n showBreadcrumbs={false}\n />\n </Suspense>\n ))\n .with('object-storage', () => {\n const s3Result = adapter as { storage: IObjectStorage; bucket: string };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <ObjectStorageBrowserLazy storage={s3Result.storage} bucket={s3Result.bucket} />\n </Suspense>\n );\n })\n .with('git-repo', () => {\n const repo = selectedRepo ?? { owner: '', repo: '' };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <GitRepoBrowserContent\n serviceId={serviceId}\n token={token}\n gitRepo={adapter as any}\n owner={repo.owner}\n repo={repo.repo}\n selectedRepo={selectedRepo ?? undefined}\n onError={handleRuntimeError}\n actionNotifications={dashboardModel.actionNotifications}\n />\n </Suspense>\n );\n })\n .with('git-host', () => {\n // Route git-host to unified browser: show PRs/Issues alongside git-repo tabs\n const repo = selectedRepo ?? { owner: '', repo: '' };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <GitHostBrowserWithRepo\n serviceId={serviceId}\n token={token}\n gitHost={adapter as any}\n owner={repo.owner}\n repo={repo.repo}\n selectedRepo={selectedRepo ?? undefined}\n onError={handleRuntimeError}\n actionNotifications={dashboardModel.actionNotifications}\n />\n </Suspense>\n );\n })\n .with('media', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyMediaBrowser provider={adapter} />\n </Suspense>\n ))\n .with('contacts', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyContactBrowser provider={adapter} />\n </Suspense>\n ))\n .with('calendar', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyCalendarBrowser provider={adapter} />\n </Suspense>\n ))\n .exhaustive();\n});\n\n/** Routes to the correct capability content based on service + capability */\nexport { CAPABILITY_LABELS };\nexport const CapabilityContent = observer(({\n serviceId,\n capabilityId,\n connectionManager,\n dashboardModel,\n}: {\n serviceId: string;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}) => {\n const service = dashboardModel.selectedService;\n if (!service) return null;\n\n const token = connectionManager.getToken(serviceId);\n if (!token) {\n return <CapabilityError error=\"Not connected. Please connect the service first.\" />;\n }\n\n // Services needing repo selection show picker first\n if (REPO_SERVICES.has(serviceId) && (capabilityId === 'file-system' || capabilityId === 'git-repo' || capabilityId === 'git-host')) {\n let selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n // Auto-extract owner/repo from credential token (e.g. Gitea Quick Test includes them)\n if (!selectedRepo) {\n try {\n const creds = JSON.parse(token);\n if (creds.owner && creds.repo) {\n dashboardModel.setSelectedRepo(serviceId, { owner: creds.owner, repo: creds.repo });\n selectedRepo = { owner: creds.owner, repo: creds.repo };\n }\n } catch {\n // Token is not JSON (e.g. OAuth token) — show picker\n }\n }\n if (!selectedRepo) {\n return (\n <Suspense fallback={<LoadingFallback />}>\n <RepoPicker\n serviceId={serviceId}\n accessToken={token}\n onSelectRepo={(repo) => dashboardModel.setSelectedRepo(serviceId, repo)}\n />\n </Suspense>\n );\n }\n }\n\n return (\n <GenericCapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n token={token}\n dashboardModel={dashboardModel}\n connectionManager={connectionManager}\n />\n );\n});\n\nexport const ScopeWarningBanner = observer(({ serviceId, capabilityId, connectionManager }: {\n serviceId: string;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n}) => {\n const token = connectionManager.getToken(serviceId);\n if (!token) return null;\n const hasScopes = connectionManager.hasCapabilityScopes(serviceId, capabilityId);\n if (hasScopes) return null;\n return (\n <div className=\"flex items-center gap-2 px-4 py-2 bg-yellow-50 dark:bg-yellow-900/20 border-b border-yellow-200 dark:border-yellow-800\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-500 flex-shrink-0\" />\n <span className=\"text-xs text-yellow-700 dark:text-yellow-400\">\n Required scopes not granted. Some features may not work. Try reconnecting with the needed permissions.\n </span>\n </div>\n );\n});\n\ninterface CapabilityPanelProps {\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const CapabilityPanel: React.FC<CapabilityPanelProps> = observer(({ dashboardModel, connectionManager }) => {\n if (!dashboardModel.panelOpen || !dashboardModel.selectedCell) {\n return null;\n }\n\n const { serviceId, capabilityId } = dashboardModel.selectedCell;\n const service = dashboardModel.selectedService;\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const hasRepo = REPO_SERVICES.has(serviceId) && selectedRepo;\n\n return (\n <div className=\"border border-border rounded-lg mt-4 overflow-hidden bg-card flex flex-col h-[70vh]\">\n <div className=\"flex items-center justify-between px-4 py-2 bg-muted border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium truncate\" title={`${service?.name} — ${CAPABILITY_LABELS[capabilityId]}`}>\n {service?.name} — {CAPABILITY_LABELS[capabilityId]}\n </span>\n {hasRepo && (\n <>\n <span className=\"text-xs text-muted-foreground truncate\" title={`${selectedRepo.owner}/${selectedRepo.repo}`}>\n ({selectedRepo.owner}/{selectedRepo.repo})\n </span>\n <button\n onClick={() => dashboardModel.clearSelectedRepo(serviceId)}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded bg-muted hover:bg-muted/80 transition-colors\"\n >\n <ArrowLeft className=\"h-3 w-3\" />\n Change repo\n </button>\n </>\n )}\n </div>\n <button\n onClick={() => dashboardModel.closePanel()}\n className=\"p-1 rounded hover:bg-muted transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <ScopeWarningBanner serviceId={serviceId} capabilityId={capabilityId} connectionManager={connectionManager} />\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n <Suspense fallback={<LoadingFallback />}>\n <CapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n </Suspense>\n </div>\n </div>\n );\n});\n","import React, { useState, useRef, useEffect } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { History, Undo2, Trash2, FileEdit, FilePlus, Upload, Copy, Move, X } from 'lucide-react';\nimport type { ActionNotificationModel, ActionRecord, ActionType } from '../models/ActionNotificationModel';\n\nconst ACTION_ICONS: Record<ActionType, React.ReactNode> = {\n delete: <Trash2 className=\"h-3.5 w-3.5 text-destructive\" />,\n rename: <FileEdit className=\"h-3.5 w-3.5 text-blue-500\" />,\n create: <FilePlus className=\"h-3.5 w-3.5 text-green-500\" />,\n upload: <Upload className=\"h-3.5 w-3.5 text-purple-500\" />,\n copy: <Copy className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n move: <Move className=\"h-3.5 w-3.5 text-orange-500\" />,\n};\n\nfunction formatTimeAgo(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n if (seconds < 60) return 'just now';\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n return `${Math.floor(hours / 24)}d ago`;\n}\n\nconst ActionItem: React.FC<{ action: ActionRecord; model: ActionNotificationModel }> = observer(({ action, model }) => {\n const [undoing, setUndoing] = useState(false);\n\n const handleUndo = async () => {\n if (!action.undo || action.undone || undoing) return;\n setUndoing(true);\n try {\n await action.undo();\n model.markUndone(action.id);\n } catch {\n // Error toast is handled by showActionToast\n } finally {\n setUndoing(false);\n }\n };\n\n return (\n <div className={`flex items-start gap-2 px-3 py-2 text-xs ${action.undone ? 'opacity-50' : ''}`}>\n <span className=\"mt-0.5 flex-shrink-0\">{ACTION_ICONS[action.type]}</span>\n <div className=\"flex-1 min-w-0\">\n <p className={`text-foreground truncate ${action.undone ? 'line-through' : ''}`} title={action.description}>\n {action.description}\n </p>\n <p className=\"text-muted-foreground mt-0.5\">{formatTimeAgo(action.timestamp)}</p>\n </div>\n {action.undo && !action.undone && (\n <button\n onClick={handleUndo}\n disabled={undoing}\n className=\"flex-shrink-0 p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n title=\"Undo\"\n >\n <Undo2 className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n});\n\ninterface ActionHistoryPanelProps {\n model: ActionNotificationModel;\n}\n\nexport const ActionHistoryPanel: React.FC<ActionHistoryPanelProps> = observer(({ model }) => {\n const [open, setOpen] = useState(false);\n const panelRef = useRef<HTMLDivElement>(null);\n const actions = model.recentActions;\n\n // Close on click outside\n useEffect(() => {\n if (!open) return;\n const handleClick = (e: MouseEvent) => {\n if (panelRef.current && !panelRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClick);\n return () => document.removeEventListener('mousedown', handleClick);\n }, [open]);\n\n return (\n <div className=\"relative\" ref={panelRef}>\n <button\n onClick={() => setOpen(!open)}\n className=\"relative p-1.5 rounded-md hover:bg-background/80 text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Action history\"\n >\n <History className=\"h-4 w-4\" />\n {actions.length > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-primary text-primary-foreground text-[9px] font-medium flex items-center justify-center\">\n {actions.length > 9 ? '9+' : actions.length}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full mt-1 w-72 rounded-lg border border-border bg-card shadow-lg z-50 animate-in fade-in slide-in-from-top-1 duration-150\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border\">\n <span className=\"text-xs font-medium text-foreground\">Recent Actions</span>\n <div className=\"flex items-center gap-1\">\n {actions.length > 0 && (\n <button\n onClick={() => model.clear()}\n className=\"text-[10px] text-muted-foreground hover:text-foreground px-1.5 py-0.5 rounded hover:bg-muted transition-colors\"\n >\n Clear\n </button>\n )}\n <button\n onClick={() => setOpen(false)}\n className=\"p-0.5 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto divide-y divide-border\">\n {actions.length === 0 ? (\n <div className=\"px-3 py-6 text-center text-xs text-muted-foreground\">\n No actions yet\n </div>\n ) : (\n actions.map((action) => (\n <ActionItem key={action.id} action={action} model={model} />\n ))\n )}\n </div>\n </div>\n )}\n </div>\n );\n});\n","import React, { Suspense } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { ArrowLeft } from 'lucide-react';\nimport { PathBreadcrumb } from '@anymux/ui/components/path-breadcrumb';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport { CapabilityContent, CAPABILITY_LABELS, ScopeWarningBanner } from './CapabilityPanel';\nimport { ActionHistoryPanel } from './ActionHistoryPanel';\n\nconst REPO_SERVICES = new Set(['github', 'gitlab', 'bitbucket', 'gitea']);\n\ninterface FullScreenBrowserProps {\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const FullScreenBrowser: React.FC<FullScreenBrowserProps> = observer(\n ({ dashboardModel, connectionManager }) => {\n const cell = dashboardModel.selectedCell;\n if (!cell) return null;\n\n const { serviceId, capabilityId } = cell;\n const service = dashboardModel.selectedService;\n if (!service) return null;\n\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const hasRepo = REPO_SERVICES.has(serviceId) && selectedRepo;\n const browserPath = dashboardModel.browserPath;\n\n return (\n <div className=\"flex flex-col h-full flex-1 min-h-0\">\n {/* Breadcrumb bar */}\n <div className=\"flex items-center gap-1 px-2 sm:px-4 py-2 bg-muted border-b border-border text-xs sm:text-sm flex-shrink-0 min-h-[40px]\">\n <div className=\"flex items-center gap-1 flex-1 min-w-0 flex-wrap\">\n <PathBreadcrumb\n path={browserPath}\n onNavigate={(path) => dashboardModel.setBrowserPath(path)}\n showHome={false}\n editable\n prefixSegments={[\n { label: 'Dashboard', onClick: () => dashboardModel.closePanel() },\n { label: service.name, onClick: () => dashboardModel.closePanel() },\n { label: CAPABILITY_LABELS[capabilityId], onClick: () => dashboardModel.setBrowserPath('/') },\n ...(hasRepo ? [{ label: `${selectedRepo.owner}/${selectedRepo.repo}` }] : []),\n ]}\n />\n {hasRepo && (\n <button\n onClick={() => dashboardModel.clearSelectedRepo(serviceId)}\n className=\"ml-1 px-1.5 py-0.5 text-[10px] font-medium rounded bg-muted hover:bg-muted/80 transition-colors\"\n >\n <span className=\"hidden sm:inline\">Change</span>\n <ArrowLeft className=\"h-3 w-3 sm:hidden\" />\n </button>\n )}\n </div>\n <ActionHistoryPanel model={dashboardModel.actionNotifications} />\n </div>\n\n {/* Scope warning */}\n <ScopeWarningBanner serviceId={serviceId} capabilityId={capabilityId} connectionManager={connectionManager} />\n\n {/* Browser content */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n <Suspense\n fallback={\n <div className=\"flex items-center justify-center h-64\">\n <LoadingSpinner label=\"Loading...\" />\n </div>\n }\n >\n <CapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n </Suspense>\n </div>\n </div>\n );\n }\n);\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { serviceRegistry } from '../registry/service-registry';\nimport { ServiceRow } from './ServiceRow';\nimport { ServiceCard } from './ServiceCard';\nimport { FullScreenBrowser } from './FullScreenBrowser';\n\nconst COLUMN_HEADERS = [\n { label: 'Service', className: 'text-left' },\n { label: 'Status', className: 'text-left' },\n { label: 'FS', className: 'text-center' },\n { label: 'Obj Storage', className: 'text-center' },\n { label: 'Git', className: 'text-center' },\n { label: 'Media', className: 'text-center' },\n { label: 'Contacts', className: 'text-center' },\n { label: 'Calendar', className: 'text-center' },\n];\n\ninterface ServiceDashboardProps {\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceDashboard: React.FC<ServiceDashboardProps> = observer(\n ({ connectionManager, dashboardModel }) => {\n const services = serviceRegistry.getAll();\n\n if (dashboardModel.panelOpen && dashboardModel.selectedCell) {\n return (\n <div className=\"animate-in fade-in duration-200 h-full flex flex-col min-h-0\">\n <FullScreenBrowser dashboardModel={dashboardModel} connectionManager={connectionManager} />\n </div>\n );\n }\n\n return (\n <div>\n {/* Desktop view — table */}\n <div className=\"hidden md:block\">\n <div className=\"overflow-x-auto rounded-lg border border-border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-muted border-b border-border\">\n {COLUMN_HEADERS.map((col) => (\n <th\n key={col.label}\n className={`px-3 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider ${col.className}`}\n >\n {col.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"bg-card divide-y divide-border\">\n {services.map((service) => (\n <ServiceRow\n key={service.id}\n service={service}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Mobile: card layout (always) */}\n <div className=\"md:hidden space-y-3\">\n {services.map((service) => (\n <ServiceCard\n key={service.id}\n service={service}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </div>\n </div>\n );\n }\n);\n","import React from 'react';\nimport { StatusIndicator } from '@anymux/ui/components/status-indicator';\nimport { match } from 'ts-pattern';\nimport type { ConnectionStatus as Status } from '../types/connection';\n\ninterface ConnectionStatusProps {\n status: Status;\n}\n\nexport const ConnectionStatusIndicator: React.FC<ConnectionStatusProps> = ({ status }) => {\n const config = match(status)\n .with('disconnected', () => ({ status: 'neutral' as const, label: 'Disconnected' }))\n .with('connecting', () => ({ status: 'warning' as const, label: 'Connecting...', pulse: true }))\n .with('connected', () => ({ status: 'success' as const, label: 'Connected' }))\n .with('expired', () => ({ status: 'warning' as const, label: 'Expired' }))\n .with('error', () => ({ status: 'error' as const, label: 'Error' }))\n .with('not_configured', () => ({ status: 'neutral' as const, label: 'Not Configured' }))\n .with('loading', () => ({ status: 'neutral' as const, label: 'Loading...' }))\n .exhaustive();\n\n return (\n <StatusIndicator\n status={config.status}\n label={config.label}\n pulse={config.status === 'warning' && status === 'connecting'}\n />\n );\n};\n","import React, { useState, useEffect, useMemo } from 'react';\nimport { Search, Lock, Loader2 } from 'lucide-react';\n\ninterface GitHubRepoPickerProps {\n accessToken: string;\n onSelectRepo: (repo: { owner: string; repo: string }) => void;\n}\n\ninterface GitHubRepoInfo {\n full_name: string;\n description: string | null;\n language: string | null;\n private: boolean;\n stargazers_count: number;\n}\n\nexport function GitHubRepoPicker({ accessToken, onSelectRepo }: GitHubRepoPickerProps) {\n const [repos, setRepos] = useState<GitHubRepoInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [search, setSearch] = useState('');\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchRepos() {\n setLoading(true);\n setError(null);\n try {\n const res = await fetch('https://api.github.com/user/repos?sort=updated&per_page=50', {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);\n const data: GitHubRepoInfo[] = await res.json();\n if (!cancelled) setRepos(data);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to fetch repos');\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n fetchRepos();\n return () => { cancelled = true; };\n }, [accessToken]);\n\n const filtered = useMemo(\n () => repos.filter((r) => r.full_name.toLowerCase().includes(search.toLowerCase())),\n [repos, search],\n );\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-8 text-gray-400\">\n <Loader2 className=\"h-5 w-5 animate-spin mr-2\" />\n Loading repositories...\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"rounded-lg border border-red-800 bg-red-950/50 p-4 text-red-300 text-sm\">\n {error}\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500\" />\n <input\n type=\"text\"\n placeholder=\"Search repositories...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"w-full rounded-md border border-gray-700 bg-gray-900 py-2 pl-9 pr-3 text-sm text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500\"\n />\n </div>\n <div className=\"max-h-64 overflow-y-auto rounded-md border border-gray-700\">\n {filtered.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-gray-500\">No repositories found</div>\n ) : (\n filtered.map((repo) => {\n const parts = repo.full_name.split('/');\n const owner = parts[0] ?? '';\n const name = parts[1] ?? '';\n return (\n <button\n key={repo.full_name}\n type=\"button\"\n onClick={() => onSelectRepo({ owner, repo: name })}\n className=\"flex w-full items-start gap-3 border-b border-gray-800 px-4 py-3 text-left hover:bg-gray-800/60 last:border-b-0 transition-colors\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-blue-400 truncate\" title={repo.full_name}>\n {repo.full_name}\n </span>\n {repo.private && (\n <Lock className=\"h-3 w-3 flex-shrink-0 text-gray-500\" />\n )}\n </div>\n {repo.description && (\n <p className=\"mt-0.5 text-xs text-gray-500 truncate\" title={repo.description}>\n {repo.description.length > 80\n ? `${repo.description.slice(0, 80)}...`\n : repo.description}\n </p>\n )}\n </div>\n {repo.language && (\n <span className=\"flex-shrink-0 rounded-full bg-gray-800 px-2 py-0.5 text-xs text-gray-400\">\n {repo.language}\n </span>\n )}\n </button>\n );\n })\n )}\n </div>\n </div>\n );\n}\n","import React, { useEffect, useMemo } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport { DashboardModel } from '../models/DashboardModel';\nimport { ServiceDashboard } from '../components/ServiceDashboard';\nimport { createConnectAuthClient } from '../auth/auth-client';\nimport { serviceRegistry } from '../registry/service-registry';\n\nexport interface ServiceDashboardDemoProps {\n authBaseURL: string;\n /** Initial service to open (from URL) */\n initialService?: string;\n /** Initial capability to open (from URL) */\n initialCapability?: string;\n /** Initial file browser path (from URL) */\n initialPath?: string;\n /** Callback when selected cell changes (for URL sync) */\n onCellChange?: (cell: { serviceId: string; capabilityId: string } | null) => void;\n /** Callback when file browser path changes (for URL sync) */\n onPathChange?: (path: string) => void;\n}\n\nexport const ServiceDashboardDemo: React.FC<ServiceDashboardDemoProps> = observer(({ authBaseURL, initialService, initialCapability, initialPath, onCellChange, onPathChange }) => {\n const connectionManager = useMemo(() => {\n const authClient = createConnectAuthClient(authBaseURL);\n return new ConnectionManagerModel({ authClient });\n }, [authBaseURL]);\n\n const dashboardModel = useMemo(() => {\n const model = new DashboardModel(connectionManager);\n // Restore panel state immediately from URL if service is already connected\n // (loadFromStorage runs synchronously in constructor, so tokens are available here)\n if (initialService && initialCapability && connectionManager.isConnected(initialService)) {\n if (initialPath) model.setBrowserPathSilent(initialPath);\n model.openCell(initialService, initialCapability as any);\n }\n return model;\n }, [connectionManager]);\n\n // Wire callbacks directly (no hooks needed — observer re-renders on prop change)\n dashboardModel.onCellChange = onCellChange;\n dashboardModel.onPathChange = onPathChange;\n\n // Initialize connection manager once\n useEffect(() => {\n connectionManager.initialize();\n }, [connectionManager]);\n\n // Bidirectional URL ↔ Model sync (handles browser back/forward, late connections, and cell selection)\n const isConnected = initialService ? connectionManager.isConnected(initialService) : false;\n useEffect(() => {\n // URL has no cell → close panel if open (browser back to dashboard)\n if (!initialService || !initialCapability) {\n if (dashboardModel.panelOpen) {\n dashboardModel.closePanelSilent();\n }\n return;\n }\n\n // Wait for connection manager to be ready and service connected\n if (!connectionManager.initialized || !isConnected) return;\n\n // URL has a cell — open panel if it doesn't match current model state\n const modelCell = dashboardModel.selectedCell;\n if (!dashboardModel.panelOpen ||\n modelCell?.serviceId !== initialService ||\n modelCell?.capabilityId !== initialCapability) {\n if (initialPath) dashboardModel.setBrowserPathSilent(initialPath);\n dashboardModel.openCell(initialService, initialCapability as any);\n }\n }, [initialService, initialCapability, initialPath, isConnected, connectionManager.initialized, dashboardModel]);\n\n if (connectionManager.configError) {\n return (\n <div className=\"p-4\">\n <div className=\"rounded-lg border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/30 p-4\">\n <p className=\"text-sm font-medium text-red-800 dark:text-red-300\">Dashboard cannot start</p>\n <p className=\"mt-1 text-sm text-red-600 dark:text-red-400\">{connectionManager.configError}</p>\n </div>\n </div>\n );\n }\n\n // Show banner when URL requests a service+capability that isn't connected\n const showNotConnectedBanner =\n initialService && initialCapability &&\n connectionManager.initialized &&\n !connectionManager.isConnected(initialService) &&\n !dashboardModel.panelOpen;\n const bannerService = showNotConnectedBanner ? serviceRegistry.get(initialService) : null;\n\n return (\n <div className={dashboardModel.panelOpen ? 'flex-1 flex flex-col min-h-0' : 'flex-1 min-h-0 overflow-auto p-4'}>\n {showNotConnectedBanner && bannerService && (\n <div className=\"mb-4 rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/30 p-4\">\n <p className=\"text-sm font-medium text-amber-800 dark:text-amber-300\">\n {bannerService.name} is not connected\n </p>\n <p className=\"mt-1 text-sm text-amber-600 dark:text-amber-400\">\n Click the <strong>Connect</strong> button next to {bannerService.name} in the table below to sign in.\n The browser will open automatically once connected.\n </p>\n </div>\n )}\n <ServiceDashboard connectionManager={connectionManager} dashboardModel={dashboardModel} />\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["microsoftService: ServiceDefinition","icloudService: ServiceDefinition","baseURL: string","providerId: string","serviceId: string","token: string","options?: { authClient?: ConnectAuthClient }","serviceId: string","pendingServiceId: string","token: string | null","fallbackProfile: IUserProfile | undefined","profile: IUserProfile","credentialToken: string","capabilityId: CapabilityId","type: ActionType","description: string","undo?: () => Promise<void>","action: ActionRecord","id: string","connectionManager: ConnectionManagerModel","repo: { owner: string; repo: string }","serviceId: string","capabilityId: CapabilityId","path: string","state: GitBrowserState | null","S3_COMPATIBLE_TYPES: readonly CredentialServiceType[]","type: CredentialServiceType","serviceType: CredentialServiceType","prefill?: Record<string, string>","field: string","value: string","creds: Record<string, string>","values: Record<string, string>","AppleIcon: React.FC<LucideProps>","customIcons: Record<string, React.FC<LucideProps>>","ServiceIcon: React.FC<ServiceIconProps>","connectionManager: ConnectionManagerModel","serviceId: string","CredentialForm: React.FC<CredentialFormProps>","e: React.FormEvent","titleMap: Record<string, string>","endpointPlaceholder: Record<string, string>","date: Date | undefined","ConnectButton: React.FC<ConnectButtonProps>","err: any","credentialToken: string","values: Record<string, string>","CapabilityCell: React.FC<CapabilityCellProps>","e: React.KeyboardEvent","CAPABILITY_COLUMNS: CapabilityId[]","ServiceRow: React.FC<ServiceRowProps>","CapabilityPill: React.FC<CapabilityPillProps>","CAPABILITY_COLUMNS: { id: CapabilityId; label: string }[]","ServiceCard: React.FC<ServiceCardProps>","serviceId: string","factory: AdapterFactory","token: string","capabilityId: CapabilityId","context: AdapterContext","adapters: Partial<Record<CapabilityId, () => unknown>>","factory: () => Promise<T>","deps: unknown[]","preflight?: (adapter: T) => Promise<void>","error: string","CAPABILITY_LABELS: Record<CapabilityId, string>","branch: string","adapter: any","err: { message: string }","type: 'success' | 'error' | 'warning'","message: string","action: { type: 'create' | 'rename' | 'delete' | 'upload'; message: string; undo?: () => Promise<void> }","CapabilityPanel: React.FC<CapabilityPanelProps>","ACTION_ICONS: Record<ActionType, React.ReactNode>","date: Date","ActionItem: React.FC<{ action: ActionRecord; model: ActionNotificationModel }>","ActionHistoryPanel: React.FC<ActionHistoryPanelProps>","e: MouseEvent","FullScreenBrowser: React.FC<FullScreenBrowserProps>","ServiceDashboard: React.FC<ServiceDashboardProps>","ConnectionStatusIndicator: React.FC<ConnectionStatusProps>","data: GitHubRepoInfo[]","ServiceDashboardDemo: React.FC<ServiceDashboardDemoProps>"],"sources":["../src/registry/services/microsoft.ts","../src/registry/services/icloud.ts","../src/auth/auth-client.ts","../src/auth/token-manager.ts","../src/models/ConnectionManagerModel.ts","../src/models/ActionNotificationModel.ts","../src/models/DashboardModel.ts","../src/models/CredentialFormModel.ts","../src/components/ServiceIcon.tsx","../src/types/connection-state.ts","../src/components/CredentialForm.tsx","../src/components/ConnectedMenu.tsx","../src/components/ConnectButton.tsx","../src/components/CapabilityCell.tsx","../src/components/ServiceRow.tsx","../src/components/CapabilityPill.tsx","../src/components/ServiceCard.tsx","../src/adapters/adapter-registry.ts","../src/components/useAdapter.ts","../src/components/CapabilityError.tsx","../src/components/CapabilityPanel.tsx","../src/components/ActionHistoryPanel.tsx","../src/components/FullScreenBrowser.tsx","../src/components/ServiceDashboard.tsx","../src/components/ConnectionStatus.tsx","../src/components/GitHubRepoPicker.tsx","../src/demos/ServiceDashboardDemo.tsx"],"sourcesContent":["import type { ServiceDefinition } from '../../types/service';\n\nexport const microsoftService: ServiceDefinition = {\n id: 'microsoft',\n name: 'Microsoft',\n icon: 'AppWindow',\n color: '#00A4EF',\n authProvider: 'microsoft',\n grantsUrl: 'https://account.live.com/consent/Manage',\n capabilities: [\n { id: 'file-system', supported: true },\n { id: 'object-storage', supported: false },\n { id: 'git-repo', supported: false },\n { id: 'git-host', supported: false },\n { id: 'media', supported: false },\n { id: 'contacts', supported: true },\n { id: 'calendar', supported: true },\n ],\n scopes: {\n 'file-system': ['Files.ReadWrite.All'],\n 'contacts': ['Contacts.Read'],\n 'calendar': ['Calendars.Read'],\n },\n};\n","import type { ServiceDefinition } from '../../types/service';\n\nexport const icloudService: ServiceDefinition = {\n id: 'icloud',\n name: 'iCloud',\n icon: 'Cloud',\n color: '#007AFF',\n authProvider: 'icloud',\n grantsUrl: 'https://appleid.apple.com/account/manage',\n capabilities: [\n { id: 'file-system', supported: false },\n { id: 'object-storage', supported: false },\n { id: 'git-repo', supported: false },\n { id: 'git-host', supported: false },\n { id: 'media', supported: false },\n { id: 'contacts', supported: true },\n { id: 'calendar', supported: true },\n ],\n scopes: {\n contacts: [],\n calendar: [],\n },\n};\n","import { createAuthClient } from 'better-auth/client';\nimport { genericOAuthClient } from 'better-auth/client/plugins';\n\nconst PENDING_SERVICE_KEY = 'anymux-pending-service';\n\nexport interface ConnectAuthClient {\n signIn(provider: string, serviceId: string): Promise<void>;\n signOut(): Promise<void>;\n getSession(): Promise<{ user: { id: string; name: string; image?: string } } | null>;\n getAccessToken(providerId: string): Promise<string | null>;\n fetchConfiguredProviders(): Promise<{ database: boolean; authSecret: boolean; providers: Record<string, boolean> }>;\n fetchGrantedScopes(): Promise<Record<string, string[]>>;\n fetchTestCredentials(): Promise<Record<string, unknown>>;\n fetchUserProfiles(): Promise<Record<string, { id: string; name: string; email?: string; avatarUrl?: string; profileUrl?: string }>>;\n revokeProvider(providerId: string): Promise<void>;\n getPendingServiceId(): string | null;\n clearPendingServiceId(): void;\n}\n\nexport function createConnectAuthClient(baseURL: string): ConnectAuthClient {\n const authClient = createAuthClient({ baseURL, plugins: [genericOAuthClient()] });\n\n return {\n async signIn(provider, serviceId) {\n sessionStorage.setItem(PENDING_SERVICE_KEY, serviceId);\n const GENERIC_OAUTH_PROVIDERS = ['box', 'bitbucket'];\n if (GENERIC_OAUTH_PROVIDERS.includes(provider)) {\n await authClient.signIn.oauth2({\n providerId: provider,\n callbackURL: window.location.href,\n });\n } else {\n await authClient.signIn.social({\n provider: provider as 'google' | 'github' | 'dropbox' | 'microsoft' | 'apple',\n callbackURL: window.location.href,\n });\n }\n // Browser redirects away — this line won't execute\n },\n\n async signOut() {\n await authClient.signOut();\n },\n\n async getSession() {\n const session = await authClient.getSession();\n if (!session.data) return null;\n return { user: { id: session.data.user.id, name: session.data.user.name, ...(session.data.user.image ? { image: session.data.user.image } : {}) } };\n },\n\n async getAccessToken(providerId: string) {\n const result = await authClient.getAccessToken({ providerId });\n return result.data?.accessToken ?? null;\n },\n\n async fetchConfiguredProviders() {\n const res = await fetch(`${baseURL}/api/auth/configured`);\n if (!res.ok) return { database: false, authSecret: false, providers: {} };\n return res.json();\n },\n\n async fetchGrantedScopes() {\n const res = await fetch(`${baseURL}/api/auth/scopes`, { credentials: 'include' });\n if (!res.ok) return {};\n return res.json();\n },\n\n async fetchTestCredentials() {\n const res = await fetch(`${baseURL}/api/auth/test-credentials`);\n if (!res.ok) return {};\n return res.json();\n },\n\n async fetchUserProfiles() {\n const res = await fetch(`${baseURL}/api/auth/user-profiles`, { credentials: 'include' });\n if (!res.ok) return {};\n return res.json();\n },\n\n async revokeProvider(providerId: string) {\n const res = await fetch(`${baseURL}/api/auth/revoke-provider?provider=${encodeURIComponent(providerId)}`, {\n method: 'DELETE',\n credentials: 'include',\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`revokeProvider ${providerId} failed: ${res.status} ${text}`);\n }\n const data = await res.json().catch(() => ({}));\n console.info(`[AnyMux] revokeProvider(${providerId}): deleted=${(data as Record<string, unknown>).deleted ?? '?'}`);\n },\n\n getPendingServiceId() {\n return sessionStorage.getItem(PENDING_SERVICE_KEY);\n },\n\n clearPendingServiceId() {\n sessionStorage.removeItem(PENDING_SERVICE_KEY);\n },\n };\n}\n","const STORAGE_PREFIX = 'anymux-connect-token:';\n\nexport class TokenManager {\n getToken(serviceId: string): string | null {\n try {\n return localStorage.getItem(STORAGE_PREFIX + serviceId);\n } catch {\n return null;\n }\n }\n\n setToken(serviceId: string, token: string): void {\n try {\n localStorage.setItem(STORAGE_PREFIX + serviceId, token);\n } catch {\n // localStorage unavailable\n }\n }\n\n removeToken(serviceId: string): void {\n try {\n localStorage.removeItem(STORAGE_PREFIX + serviceId);\n } catch {\n // localStorage unavailable\n }\n }\n}\n","import { makeAutoObservable, runInAction, flow } from 'mobx';\nimport type { ConnectedService, ConnectionStatus } from '../types/connection';\nimport type { ConnectAuthClient } from '../auth/auth-client';\nimport type { CapabilityId } from '../types/service';\nimport type { IUserProfile } from '../types/user-profile';\nimport { TokenManager } from '../auth/token-manager';\nimport { serviceRegistry } from '../registry/service-registry';\n\n/** Auth providers that use stored credentials instead of OAuth */\nconst CREDENTIAL_AUTH_PROVIDERS = new Set(['s3', 'backblaze-b2', 'cloudflare-r2', 'wasabi', 'webdav', 'gitea', 'icloud', 'unsplash', 'pexels', 'imgur', 'browser-fs', 'indexeddb']);\n\nconst STORAGE_KEY = 'anymux-connect-connections';\nconst USER_PROFILES_STORAGE_KEY = 'anymux-connect-user-profiles';\n\nexport class ConnectionManagerModel {\n connections = new Map<string, ConnectedService>();\n grantedScopes = new Map<string, string[]>();\n initialized = false;\n configuredProviders = new Set<string>();\n configError: string | null = null;\n /** Per-service user profiles with provider-specific name, avatar, and profile link */\n userProfiles = new Map<string, IUserProfile>();\n testCredentials: Record<string, unknown> = {};\n pendingReconnect = new Set<string>();\n private tokenManager = new TokenManager();\n private authClient: ConnectAuthClient | null;\n\n constructor(options?: { authClient?: ConnectAuthClient }) {\n this.authClient = options?.authClient ?? null;\n makeAutoObservable(this);\n this.loadFromStorage();\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // BUG-2: Handle pending OAuth return FIRST so only that service updates immediately\n if (this.authClient) {\n const pendingServiceId = this.authClient.getPendingServiceId();\n if (pendingServiceId) {\n await this.handleOAuthReturn(pendingServiceId);\n }\n }\n\n // BUG-3: Set loading state for services not yet resolved from storage or OAuth\n for (const service of serviceRegistry.getAll()) {\n if (!this.connections.has(service.id)) {\n this.connections.set(service.id, {\n serviceId: service.id,\n status: 'loading',\n scopes: [],\n });\n }\n }\n\n if (this.authClient) {\n // Fetch which OAuth providers have credentials configured on the server\n try {\n const config = await this.authClient.fetchConfiguredProviders();\n runInAction(() => {\n if (!config.database) {\n this.configError = 'Database not configured. Set DATABASE_URL in .env.local';\n return;\n }\n if (!config.authSecret) {\n this.configError = 'Auth secret not configured. Set BETTER_AUTH_SECRET in .env.local';\n return;\n }\n for (const [provider, configured] of Object.entries(config.providers)) {\n if (configured) this.configuredProviders.add(provider);\n }\n // Mark unconfigured OAuth services as not_configured\n for (const service of serviceRegistry.getAll()) {\n if (CREDENTIAL_AUTH_PROVIDERS.has(service.authProvider)) continue;\n if (!this.configuredProviders.has(service.authProvider) && !this.isConnected(service.id)) {\n this.connections.set(service.id, {\n serviceId: service.id,\n status: 'not_configured',\n scopes: [],\n });\n }\n }\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch configured providers:', err instanceof Error ? err.message : err);\n runInAction(() => {\n this.configError = 'Failed to reach server. Check your connection.';\n });\n }\n\n // Fetch granted scopes\n try {\n const scopes = await this.authClient.fetchGrantedScopes();\n runInAction(() => {\n for (const [provider, scopeList] of Object.entries(scopes)) {\n this.grantedScopes.set(provider, scopeList);\n }\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch granted scopes:', err instanceof Error ? err.message : err);\n }\n\n // Fetch test credentials\n try {\n const creds = await this.authClient.fetchTestCredentials();\n runInAction(() => {\n this.testCredentials = creds;\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch test credentials:', err instanceof Error ? err.message : err);\n }\n }\n\n // BUG-3: Resolve any remaining 'loading' services to 'disconnected'\n runInAction(() => {\n for (const [id, conn] of this.connections) {\n if (conn.status === 'loading') {\n this.connections.set(id, { ...conn, status: 'disconnected' });\n }\n }\n this.initialized = true;\n });\n\n // Fetch per-provider user profiles in the background (enriches fallback data with\n // provider-specific avatars, profile URLs, etc.)\n if (this.authClient) {\n this.fetchAndStoreUserProfiles();\n }\n }\n\n /** Fetch per-provider profiles from the server and update the userProfiles map */\n private async fetchAndStoreUserProfiles(): Promise<void> {\n if (!this.authClient) return;\n try {\n const profilesByProvider = await this.authClient.fetchUserProfiles();\n runInAction(() => {\n // Map providerId → serviceId(s) and store profiles\n for (const service of serviceRegistry.getAll()) {\n const providerProfile = profilesByProvider[service.authProvider];\n if (providerProfile && this.isConnected(service.id)) {\n this.userProfiles.set(service.id, {\n ...providerProfile,\n provider: service.id,\n });\n }\n }\n this.persistToStorage();\n });\n } catch (err) {\n console.warn('[AnyMux] Failed to fetch user profiles:', err instanceof Error ? err.message : err);\n }\n }\n\n // BUG-2: Extracted OAuth return handler — only updates the single pending service\n private async handleOAuthReturn(pendingServiceId: string): Promise<void> {\n if (!this.authClient) return;\n const service = serviceRegistry.get(pendingServiceId);\n if (service) {\n // Retry token retrieval — after OAuth redirect the server may not have\n // finished processing the callback yet (race condition)\n let token: string | null = null;\n for (let attempt = 0; attempt < 4; attempt++) {\n try {\n token = await this.authClient.getAccessToken(service.authProvider);\n if (token) break;\n } catch {\n // getAccessToken threw — retry\n }\n if (attempt < 3) {\n await new Promise(r => setTimeout(r, 500 * (attempt + 1)));\n }\n }\n\n if (token) {\n // Capture session user info as an immediate fallback for the profile\n let fallbackProfile: IUserProfile | undefined;\n try {\n const session = await this.authClient.getSession();\n if (session) {\n const profile: IUserProfile = {\n id: session.user.id,\n name: session.user.name,\n provider: pendingServiceId,\n };\n if (session.user.image) {\n profile.avatarUrl = session.user.image;\n }\n fallbackProfile = profile;\n }\n } catch (err) {\n console.warn(`[AnyMux] Failed to fetch session for ${pendingServiceId}:`, err instanceof Error ? err.message : err);\n }\n\n runInAction(() => {\n this.tokenManager.setToken(pendingServiceId, token!);\n this.connections.set(pendingServiceId, {\n serviceId: pendingServiceId,\n status: 'connected',\n accessToken: token!,\n scopes: Object.values(service.scopes).flat(),\n connectedAt: new Date(),\n });\n if (fallbackProfile) {\n this.userProfiles.set(pendingServiceId, fallbackProfile);\n }\n this.persistToStorage();\n });\n } else {\n // OAuth flow completed but token retrieval failed after retries.\n // Check if we have a session — if so, the user authenticated but\n // the token endpoint is having issues.\n try {\n const session = await this.authClient.getSession();\n if (session) {\n console.warn(`[AnyMux] OAuth for ${pendingServiceId}: session exists but getAccessToken returned null after 4 attempts`);\n }\n } catch {\n // ignore\n }\n }\n }\n this.authClient.clearPendingServiceId();\n }\n\n private loadFromStorage() {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored) {\n const data = JSON.parse(stored) as Array<[string, ConnectedService]>;\n for (const [key, value] of data) {\n const token = this.tokenManager.getToken(key);\n if (token) {\n this.connections.set(key, { ...value, accessToken: token });\n }\n }\n }\n // Restore per-service user profiles\n const storedProfiles = localStorage.getItem(USER_PROFILES_STORAGE_KEY);\n if (storedProfiles) {\n const entries = JSON.parse(storedProfiles) as Array<[string, IUserProfile]>;\n for (const [key, value] of entries) {\n this.userProfiles.set(key, value);\n }\n }\n } catch {\n // storage unavailable\n }\n }\n\n private persistToStorage() {\n try {\n const data = Array.from(this.connections.entries()).map(\n ([key, conn]) => [key, { ...conn, accessToken: undefined }] as const\n );\n localStorage.setItem(STORAGE_KEY, JSON.stringify(data));\n // Persist per-service user profiles\n const profileData = Array.from(this.userProfiles.entries());\n localStorage.setItem(USER_PROFILES_STORAGE_KEY, JSON.stringify(profileData));\n } catch {\n // storage unavailable\n }\n }\n\n async connect(serviceId: string): Promise<void> {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n if (!this.authClient) {\n this.connections.set(serviceId, {\n serviceId,\n status: 'error',\n scopes: [],\n });\n return;\n }\n\n this.connections.set(serviceId, {\n serviceId,\n status: 'connecting',\n scopes: [],\n });\n\n // Redirect-based flow: browser navigates away\n await this.authClient.signIn(service.authProvider, serviceId);\n }\n\n connectWithCredentials(serviceId: string, credentialToken: string): void {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n this.tokenManager.setToken(serviceId, credentialToken);\n this.connections.set(serviceId, {\n serviceId,\n status: 'connected',\n accessToken: credentialToken,\n scopes: Object.values(service.scopes).flat(),\n connectedAt: new Date(),\n });\n this.persistToStorage();\n }\n\n disconnect = flow(function* (this: ConnectionManagerModel, serviceId: string) {\n const service = serviceRegistry.get(serviceId);\n this.tokenManager.removeToken(serviceId);\n this.connections.set(serviceId, {\n serviceId,\n status: 'disconnected',\n scopes: [],\n });\n if (service) {\n this.grantedScopes.delete(service.authProvider);\n }\n this.userProfiles.delete(serviceId);\n this.persistToStorage();\n\n if (this.authClient) {\n // Delete the account row first so better-auth creates a fresh one\n // with updated scopes on the next OAuth sign-in\n if (service) {\n try {\n console.info(`[AnyMux] disconnect(${serviceId}): revoking provider ${service.authProvider}…`);\n yield this.authClient.revokeProvider(service.authProvider);\n console.info(`[AnyMux] disconnect(${serviceId}): revoke succeeded`);\n } catch (err) {\n console.error('[AnyMux] revokeProvider FAILED:', err instanceof Error ? err.message : err);\n }\n }\n try {\n yield this.authClient.signOut();\n console.info(`[AnyMux] disconnect(${serviceId}): signOut succeeded`);\n } catch (err) {\n console.warn('[AnyMux] signOut failed:', err instanceof Error ? err.message : err);\n }\n }\n });\n\n requestReconnect(serviceId: string): void {\n this.pendingReconnect.add(serviceId);\n }\n\n clearReconnectRequest(serviceId: string): void {\n this.pendingReconnect.delete(serviceId);\n }\n\n isConnected(serviceId: string): boolean {\n return this.connections.get(serviceId)?.status === 'connected';\n }\n\n getStatus(serviceId: string): ConnectionStatus {\n return this.connections.get(serviceId)?.status ?? 'disconnected';\n }\n\n getToken(serviceId: string): string | null {\n return this.tokenManager.getToken(serviceId);\n }\n\n /** Refresh OAuth token from better-auth. Returns fresh token or null. */\n async refreshToken(serviceId: string): Promise<string | null> {\n const service = serviceRegistry.get(serviceId);\n if (!service || !this.authClient) {\n console.warn(`[AnyMux] refreshToken(${serviceId}): no service or authClient`);\n return null;\n }\n // Skip credential-based services — their tokens don't expire\n if (CREDENTIAL_AUTH_PROVIDERS.has(service.authProvider)) {\n return this.tokenManager.getToken(serviceId);\n }\n const oldToken = this.tokenManager.getToken(serviceId);\n try {\n const token = await this.authClient.getAccessToken(service.authProvider);\n if (token) {\n const changed = token !== oldToken;\n console.info(`[AnyMux] refreshToken(${serviceId}): got token (${token.slice(0, 8)}…${token.slice(-4)}), changed=${changed}`);\n this.tokenManager.setToken(serviceId, token);\n const conn = this.connections.get(serviceId);\n if (conn) {\n this.connections.set(serviceId, { ...conn, accessToken: token });\n }\n return token;\n }\n console.warn(`[AnyMux] refreshToken(${serviceId}): getAccessToken returned null`);\n } catch (err) {\n console.warn(`[AnyMux] refreshToken(${serviceId}): error:`, err instanceof Error ? err.message : err);\n }\n console.warn(`[AnyMux] refreshToken(${serviceId}): falling back to stored token (${oldToken ? oldToken.slice(0, 8) + '…' : 'null'})`);\n return oldToken;\n }\n\n /** Get the full user profile for a connected service */\n getUserProfile(serviceId: string): IUserProfile | undefined {\n return this.userProfiles.get(serviceId);\n }\n\n /** @deprecated Use getUserProfile() instead. Kept for backward compat. */\n getUserInfo(serviceId: string): { name: string; image?: string } | null {\n const profile = this.userProfiles.get(serviceId);\n if (!profile) return null;\n return { name: profile.name, ...(profile.avatarUrl ? { image: profile.avatarUrl } : {}) };\n }\n\n hasCapabilityScopes(serviceId: string, capabilityId: CapabilityId): boolean {\n const service = serviceRegistry.get(serviceId);\n if (!service) return false;\n // S3/WebDAV/Gitea don't use OAuth scopes — treat all as granted\n if (CREDENTIAL_AUTH_PROVIDERS.has(service.authProvider)) return true;\n const requiredScopes = service.scopes[capabilityId];\n if (!requiredScopes || requiredScopes.length === 0) return false;\n // If scopes haven't been loaded yet, assume granted to avoid a flash warning\n if (!this.grantedScopes.has(service.authProvider)) return true;\n const granted = this.grantedScopes.get(service.authProvider)!;\n return requiredScopes.every(s => granted.includes(s));\n }\n}\n","import { makeAutoObservable } from 'mobx';\n\nexport type ActionType = 'delete' | 'move' | 'rename' | 'upload' | 'create' | 'copy';\n\nexport interface ActionRecord {\n id: string;\n type: ActionType;\n description: string;\n timestamp: Date;\n /** Undo function — called when user clicks Undo */\n undo?: () => Promise<void>;\n /** Whether undo was executed */\n undone: boolean;\n}\n\nlet nextId = 1;\n\n/**\n * Tracks user actions for toast notifications with undo support.\n * Uses sonner's toast() externally — this model just manages the action log.\n */\nexport class ActionNotificationModel {\n actions: ActionRecord[] = [];\n /** Maximum actions to keep in history */\n maxHistory = 100;\n\n constructor() {\n makeAutoObservable(this, {});\n }\n\n /**\n * Record an action. Returns the action ID for reference.\n * The caller is responsible for showing the toast via sonner's toast().\n */\n record(type: ActionType, description: string, undo?: () => Promise<void>): string {\n const id = `action-${nextId++}`;\n const action: ActionRecord = {\n id,\n type,\n description,\n timestamp: new Date(),\n undo,\n undone: false,\n };\n this.actions.unshift(action);\n\n // Trim history\n if (this.actions.length > this.maxHistory) {\n this.actions = this.actions.slice(0, this.maxHistory);\n }\n\n return id;\n }\n\n /** Mark an action as undone */\n markUndone(id: string) {\n const action = this.actions.find((a) => a.id === id);\n if (action) action.undone = true;\n }\n\n get recentActions(): ActionRecord[] {\n return this.actions.slice(0, 20);\n }\n\n get undoableActions(): ActionRecord[] {\n return this.actions.filter((a) => a.undo && !a.undone);\n }\n\n clear() {\n this.actions = [];\n }\n}\n","import { makeAutoObservable } from 'mobx';\nimport type { CapabilityId } from '../types/service';\nimport type { ConnectionManagerModel } from './ConnectionManagerModel';\nimport { ActionNotificationModel } from './ActionNotificationModel';\nimport { serviceRegistry } from '../registry/service-registry';\n\nexport interface SelectedCell {\n serviceId: string;\n capabilityId: CapabilityId;\n}\n\nexport interface GitBrowserState {\n serviceId: string;\n owner?: string;\n repo?: string;\n ref?: string;\n path?: string;\n}\n\nconst REPO_STORAGE_KEY = 'anymux-selected-repos';\n\nexport class DashboardModel {\n selectedCell: SelectedCell | null = null;\n panelOpen = false;\n /** Generic repo selection per service (replaces githubRepo) */\n selectedRepos: Record<string, { owner: string; repo: string }> = {};\n browserPath = '/';\n gitBrowserState: GitBrowserState | null = null;\n actionNotifications = new ActionNotificationModel();\n private connectionManager: ConnectionManagerModel;\n /** Optional callback when selected cell changes (for URL sync) */\n onCellChange?: (cell: SelectedCell | null) => void;\n /** Optional callback when browser path changes (for URL sync) */\n onPathChange?: (path: string) => void;\n\n constructor(connectionManager: ConnectionManagerModel) {\n this.connectionManager = connectionManager;\n makeAutoObservable(this, { onCellChange: false, onPathChange: false });\n // Load repos from localStorage\n try {\n const stored = localStorage.getItem(REPO_STORAGE_KEY);\n if (stored) this.selectedRepos = JSON.parse(stored);\n // Migrate old github-repo key\n const oldGithub = localStorage.getItem('anymux-github-repo');\n if (oldGithub && !this.selectedRepos['github']) {\n this.selectedRepos['github'] = JSON.parse(oldGithub);\n this.persistRepos();\n localStorage.removeItem('anymux-github-repo');\n }\n } catch {}\n }\n\n /** @deprecated Use getSelectedRepo('github') instead */\n get githubRepo(): { owner: string; repo: string } | null {\n return this.selectedRepos['github'] ?? null;\n }\n\n /** @deprecated Use setSelectedRepo('github', repo) instead */\n setGitHubRepo(repo: { owner: string; repo: string }): void {\n this.setSelectedRepo('github', repo);\n }\n\n /** @deprecated Use clearSelectedRepo('github') instead */\n clearGitHubRepo(): void {\n this.clearSelectedRepo('github');\n }\n\n setSelectedRepo(serviceId: string, repo: { owner: string; repo: string }): void {\n this.selectedRepos[serviceId] = repo;\n this.persistRepos();\n }\n\n clearSelectedRepo(serviceId: string): void {\n delete this.selectedRepos[serviceId];\n this.persistRepos();\n }\n\n getSelectedRepo(serviceId: string): { owner: string; repo: string } | null {\n return this.selectedRepos[serviceId] ?? null;\n }\n\n private persistRepos(): void {\n try {\n localStorage.setItem(REPO_STORAGE_KEY, JSON.stringify(this.selectedRepos));\n } catch {}\n }\n\n selectCell(serviceId: string, capabilityId: CapabilityId): void {\n const service = serviceRegistry.get(serviceId);\n if (!service) return;\n\n const capability = service.capabilities.find((c) => c.id === capabilityId);\n if (!capability?.supported) return;\n\n // Clear selected repo so the picker always shows when clicking a cell\n if (capabilityId === 'git-repo' || capabilityId === 'git-host' || capabilityId === 'file-system') {\n this.clearSelectedRepo(serviceId);\n }\n if (!this.connectionManager.isConnected(serviceId)) return;\n\n this.selectedCell = { serviceId, capabilityId };\n this.panelOpen = true;\n this.onCellChange?.({ serviceId, capabilityId });\n }\n\n /** Open a cell without checking connection/capability (used for URL restore) */\n openCell(serviceId: string, capabilityId: CapabilityId): void {\n // Clear selected repo — URL doesn't carry repo info, so always show picker\n if (capabilityId === 'git-repo' || capabilityId === 'git-host' || capabilityId === 'file-system') {\n this.clearSelectedRepo(serviceId);\n }\n this.selectedCell = { serviceId, capabilityId };\n this.panelOpen = true;\n }\n\n setBrowserPath(path: string): void {\n this.browserPath = path;\n this.onPathChange?.(path);\n }\n\n setGitBrowserState(state: GitBrowserState | null): void {\n this.gitBrowserState = state;\n }\n\n closePanel(): void {\n this.selectedCell = null;\n this.panelOpen = false;\n this.browserPath = '/';\n this.gitBrowserState = null;\n this.onCellChange?.(null);\n }\n\n /** Close panel without triggering onCellChange (used for URL-driven state sync, e.g. browser back) */\n closePanelSilent(): void {\n this.selectedCell = null;\n this.panelOpen = false;\n this.browserPath = '/';\n this.gitBrowserState = null;\n }\n\n /** Set browser path without triggering onPathChange (used for URL-driven state sync) */\n setBrowserPathSilent(path: string): void {\n this.browserPath = path;\n }\n\n get selectedService() {\n if (!this.selectedCell) return null;\n return serviceRegistry.get(this.selectedCell.serviceId) ?? null;\n }\n\n get selectedCapability() {\n if (!this.selectedCell || !this.selectedService) return null;\n return this.selectedService.capabilities.find(\n (c) => c.id === this.selectedCell!.capabilityId\n ) ?? null;\n }\n}\n","import { makeAutoObservable } from 'mobx';\n\nexport type CredentialServiceType = 's3' | 'backblaze-b2' | 'cloudflare-r2' | 'wasabi' | 'webdav' | 'gitea' | 'icloud' | 'unsplash' | 'pexels' | 'imgur';\n\n/** S3-compatible service types that share the same credential fields */\nexport const S3_COMPATIBLE_TYPES: readonly CredentialServiceType[] = ['s3', 'backblaze-b2', 'cloudflare-r2', 'wasabi'] as const;\n\nexport function isS3Compatible(type: CredentialServiceType): boolean {\n return (S3_COMPATIBLE_TYPES as readonly string[]).includes(type);\n}\n\nexport class CredentialFormModel {\n open = false;\n serviceType: CredentialServiceType = 's3';\n\n // S3 fields\n accessKeyId = '';\n secretAccessKey = '';\n region = 'us-east-1';\n bucket = '';\n endpoint = '';\n\n // WebDAV fields\n url = '';\n username = '';\n password = '';\n\n // Gitea fields\n token = '';\n owner = '';\n repo = '';\n // Gitea also uses url, username, password above\n\n // iCloud fields\n email = '';\n appPassword = '';\n\n // API key services (Unsplash, Pexels)\n apiKey = '';\n\n constructor() {\n makeAutoObservable(this);\n }\n\n openForm(serviceType: CredentialServiceType, prefill?: Record<string, string>): void {\n this.serviceType = serviceType;\n this.resetFields();\n if (prefill) this.applyPrefill(prefill);\n this.open = true;\n }\n\n closeForm(): void {\n this.open = false;\n }\n\n setField(field: string, value: string): void {\n if (field in this) {\n (this as Record<string, unknown>)[field] = value;\n }\n }\n\n /** Serialize current form state to JSON credential string */\n serialize(): string {\n if (isS3Compatible(this.serviceType)) {\n const creds: Record<string, string> = {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n bucket: this.bucket,\n };\n if (this.endpoint) creds.endpoint = this.endpoint;\n return JSON.stringify(creds);\n }\n switch (this.serviceType) {\n case 'webdav':\n return JSON.stringify({\n url: this.url,\n username: this.username,\n password: this.password,\n });\n case 'gitea':\n return JSON.stringify({\n url: this.url,\n username: this.username,\n password: this.password,\n token: this.token,\n owner: this.owner,\n repo: this.repo,\n });\n case 'icloud':\n return JSON.stringify({\n email: this.email,\n appPassword: this.appPassword,\n });\n case 'unsplash':\n return JSON.stringify({ accessKey: this.apiKey });\n case 'pexels':\n return JSON.stringify({ apiKey: this.apiKey });\n case 'imgur':\n return JSON.stringify({ clientId: this.apiKey });\n }\n }\n\n private resetFields(): void {\n this.accessKeyId = '';\n this.secretAccessKey = '';\n this.region = 'us-east-1';\n this.bucket = '';\n this.endpoint = '';\n this.url = '';\n this.username = '';\n this.password = '';\n this.token = '';\n this.owner = '';\n this.repo = '';\n this.email = '';\n this.appPassword = '';\n this.apiKey = '';\n }\n\n private applyPrefill(values: Record<string, string>): void {\n for (const [k, v] of Object.entries(values)) {\n if (typeof v === 'string' && k in this) {\n (this as Record<string, unknown>)[k] = v;\n }\n }\n }\n}\n","import React from 'react';\nimport * as icons from 'lucide-react';\nimport type { LucideProps } from 'lucide-react';\n\n/** Custom icons for services not available in lucide-react */\nconst AppleIcon: React.FC<LucideProps> = ({ size = 24, color = 'currentColor', strokeWidth: _sw, ...props }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill={color}\n {...props}\n >\n <path d=\"M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z\" />\n </svg>\n);\n\nconst customIcons: Record<string, React.FC<LucideProps>> = {\n Apple: AppleIcon,\n};\n\ninterface ServiceIconProps extends LucideProps {\n name: string;\n}\n\nexport const ServiceIcon: React.FC<ServiceIconProps> = ({ name, ...props }) => {\n const CustomIcon = customIcons[name];\n if (CustomIcon) {\n return <CustomIcon {...props} />;\n }\n const Icon = (icons as unknown as Record<string, React.FC<LucideProps>>)[name];\n if (!Icon) {\n return <icons.HelpCircle {...props} />;\n }\n return <Icon {...props} />;\n};\n","import { match } from 'ts-pattern';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { IUserProfile } from './user-profile';\n\nexport type ServiceConnectionState =\n | { status: 'loading' }\n | { status: 'not_configured' }\n | { status: 'disconnected' }\n | { status: 'connecting' }\n | { status: 'connected'; token: string; user: { name: string; image?: string } | null; profile: IUserProfile | undefined }\n | { status: 'error'; error?: string }\n | { status: 'expired' };\n\nexport function getServiceConnectionState(\n connectionManager: ConnectionManagerModel,\n serviceId: string\n): ServiceConnectionState {\n const status = connectionManager.getStatus(serviceId);\n return match(status)\n .with('loading', () => ({ status: 'loading' as const }))\n .with('not_configured', () => ({ status: 'not_configured' as const }))\n .with('disconnected', () => ({ status: 'disconnected' as const }))\n .with('connecting', () => ({ status: 'connecting' as const }))\n .with('connected', () => ({\n status: 'connected' as const,\n token: connectionManager.getToken(serviceId) ?? '',\n user: connectionManager.getUserInfo(serviceId),\n profile: connectionManager.getUserProfile(serviceId),\n }))\n .with('error', () => ({ status: 'error' as const }))\n .with('expired', () => ({ status: 'expired' as const }))\n .exhaustive();\n}\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CredentialFormModel } from '../models/CredentialFormModel';\nimport { isS3Compatible } from '../models/CredentialFormModel';\n\ninterface CredentialFormProps {\n model: CredentialFormModel;\n onSubmit: (credentials: string) => void;\n}\n\nexport const CredentialForm: React.FC<CredentialFormProps> = observer(({ model, onSubmit }) => {\n if (!model.open) return null;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n onSubmit(model.serialize());\n };\n\n const inputClass =\n 'w-full rounded-md border border-border px-3 py-2 text-base sm:text-sm bg-card text-foreground focus:border-ring focus:outline-none focus:ring-1 focus:ring-ring';\n const labelClass = 'block text-sm font-medium text-foreground mb-1';\n\n const titleMap: Record<string, string> = {\n 's3': 'Amazon S3 Credentials',\n 'backblaze-b2': 'Backblaze B2 Credentials',\n 'cloudflare-r2': 'Cloudflare R2 Credentials',\n 'wasabi': 'Wasabi Credentials',\n 'webdav': 'WebDAV Credentials',\n 'icloud': 'iCloud Credentials',\n 'gitea': 'Gitea Credentials',\n 'unsplash': 'Unsplash API Key',\n 'pexels': 'Pexels API Key',\n 'imgur': 'Imgur Client ID',\n };\n const title = titleMap[model.serviceType] || 'Credentials';\n\n const endpointPlaceholder: Record<string, string> = {\n 's3': 'https://s3.amazonaws.com',\n 'backblaze-b2': 'https://s3.us-west-004.backblazeb2.com',\n 'cloudflare-r2': 'https://<account-id>.r2.cloudflarestorage.com',\n 'wasabi': 'https://s3.wasabisys.com',\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\" onClick={() => model.closeForm()}>\n <div\n className=\"w-[calc(100%-2rem)] max-w-md max-h-[80vh] flex flex-col rounded-lg bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <h2 className=\"px-4 sm:px-6 pt-4 sm:pt-6 pb-3 text-lg font-semibold text-foreground flex-shrink-0\">{title}</h2>\n\n <form onSubmit={handleSubmit} className=\"flex flex-col flex-1 min-h-0\">\n <div className=\"flex-1 overflow-y-auto px-4 sm:px-6 space-y-3\">\n {isS3Compatible(model.serviceType) ? (\n <>\n <div>\n <label className={labelClass}>Access Key ID</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.accessKeyId}\n onChange={(e) => model.setField('accessKeyId', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Secret Access Key</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.secretAccessKey}\n onChange={(e) => model.setField('secretAccessKey', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Region</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.region}\n onChange={(e) => model.setField('region', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Bucket</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.bucket}\n onChange={(e) => model.setField('bucket', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Endpoint{model.serviceType === 's3' ? ' (optional)' : ''}</label>\n <input\n type=\"text\"\n required={model.serviceType !== 's3'}\n className={inputClass}\n value={model.endpoint}\n onChange={(e) => model.setField('endpoint', e.target.value)}\n placeholder={endpointPlaceholder[model.serviceType] || ''}\n />\n </div>\n </>\n ) : model.serviceType === 'webdav' ? (\n <>\n <div>\n <label className={labelClass}>URL</label>\n <input\n type=\"url\"\n required\n className={inputClass}\n value={model.url}\n onChange={(e) => model.setField('url', e.target.value)}\n placeholder=\"https://example.com/webdav\"\n />\n </div>\n <div>\n <label className={labelClass}>Username</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.username}\n onChange={(e) => model.setField('username', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Password</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.password}\n onChange={(e) => model.setField('password', e.target.value)}\n />\n </div>\n </>\n ) : model.serviceType === 'icloud' ? (\n <>\n <div>\n <label className={labelClass}>Apple ID Email</label>\n <input\n type=\"email\"\n required\n className={inputClass}\n value={model.email}\n onChange={(e) => model.setField('email', e.target.value)}\n placeholder=\"you@icloud.com\"\n />\n </div>\n <div>\n <label className={labelClass}>App-Specific Password</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.appPassword}\n onChange={(e) => model.setField('appPassword', e.target.value)}\n placeholder=\"xxxx-xxxx-xxxx-xxxx\"\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Generate an app-specific password at{' '}\n <a\n href=\"https://appleid.apple.com/account/manage\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"underline text-primary hover:text-primary/80\"\n >\n appleid.apple.com\n </a>\n {' '}under Sign-In and Security &rarr; App-Specific Passwords.\n </p>\n </>\n ) : model.serviceType === 'unsplash' || model.serviceType === 'pexels' || model.serviceType === 'imgur' ? (\n <>\n <div>\n <label className={labelClass}>{model.serviceType === 'imgur' ? 'Client ID' : 'API Key'}</label>\n <input\n type=\"password\"\n required\n className={inputClass}\n value={model.apiKey}\n onChange={(e) => model.setField('apiKey', e.target.value)}\n placeholder={model.serviceType === 'unsplash' ? 'Unsplash Access Key' : model.serviceType === 'pexels' ? 'Pexels API Key' : 'Imgur Client ID'}\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {model.serviceType === 'unsplash' ? (\n <>Get a free API key at{' '}\n <a href=\"https://unsplash.com/developers\" target=\"_blank\" rel=\"noopener noreferrer\" className=\"underline text-primary hover:text-primary/80\">\n unsplash.com/developers\n </a>\n </>\n ) : model.serviceType === 'pexels' ? (\n <>Get a free API key at{' '}\n <a href=\"https://www.pexels.com/api/\" target=\"_blank\" rel=\"noopener noreferrer\" className=\"underline text-primary hover:text-primary/80\">\n pexels.com/api\n </a>\n </>\n ) : (\n <>Register an app at{' '}\n <a href=\"https://api.imgur.com/oauth2/addclient\" target=\"_blank\" rel=\"noopener noreferrer\" className=\"underline text-primary hover:text-primary/80\">\n api.imgur.com\n </a>\n </>\n )}\n </p>\n </>\n ) : (\n <>\n <div>\n <label className={labelClass}>URL</label>\n <input\n type=\"url\"\n required\n className={inputClass}\n value={model.url}\n onChange={(e) => model.setField('url', e.target.value)}\n placeholder=\"https://gitea.example.com\"\n />\n </div>\n <div>\n <label className={labelClass}>Token (or use username/password below)</label>\n <input\n type=\"password\"\n className={inputClass}\n value={model.token}\n onChange={(e) => model.setField('token', e.target.value)}\n placeholder=\"API token\"\n />\n </div>\n {!model.token && (\n <>\n <div>\n <label className={labelClass}>Username</label>\n <input\n type=\"text\"\n className={inputClass}\n value={model.username}\n onChange={(e) => model.setField('username', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Password</label>\n <input\n type=\"password\"\n className={inputClass}\n value={model.password}\n onChange={(e) => model.setField('password', e.target.value)}\n />\n </div>\n </>\n )}\n <div>\n <label className={labelClass}>Owner</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.owner}\n onChange={(e) => model.setField('owner', e.target.value)}\n />\n </div>\n <div>\n <label className={labelClass}>Repo</label>\n <input\n type=\"text\"\n required\n className={inputClass}\n value={model.repo}\n onChange={(e) => model.setField('repo', e.target.value)}\n />\n </div>\n </>\n )}\n </div>\n\n <div className=\"flex justify-end gap-2 px-4 sm:px-6 py-3 border-t border-border flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => model.closeForm()}\n className=\"rounded-md px-4 py-2 text-sm font-medium text-muted-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n Connect\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n});\n","import React from 'react';\nimport { ExternalLink, Info, LogOut, MoreHorizontal, RefreshCw, Clock, Shield } from 'lucide-react';\nimport { ConnectionBadge } from '@anymux/ui/components/connection-badge';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '@anymux/ui/components/dropdown-menu';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ServiceDefinition } from '../types/service';\nimport type { IUserProfile } from '../types/user-profile';\nimport { ServiceIcon } from './ServiceIcon';\n\ninterface ConnectedMenuProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n /** @deprecated Use profile instead */\n user: { name: string; image?: string } | null;\n profile?: IUserProfile;\n isOAuth: boolean;\n}\n\nfunction formatConnectedDate(date: Date | undefined): string | null {\n if (!date) return null;\n const d = date instanceof Date ? date : new Date(date);\n if (isNaN(d.getTime())) return null;\n return d.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nexport function ConnectedMenu({\n service,\n connectionManager,\n user,\n profile,\n isOAuth,\n}: ConnectedMenuProps) {\n // Prefer profile data (provider-specific) over generic session user data\n const displayName = profile?.name ?? user?.name;\n const avatarUrl = profile?.avatarUrl ?? user?.image;\n const profileUrl = profile?.profileUrl;\n const email = profile?.email;\n\n const handleReconnect = async () => {\n await connectionManager.disconnect(service.id);\n await connectionManager.connect(service.id);\n };\n\n const handleDisconnect = () => {\n connectionManager.disconnect(service.id);\n };\n\n const connection = connectionManager.connections.get(service.id);\n const connectedDate = formatConnectedDate(connection?.connectedAt);\n\n return (\n <div className=\"inline-flex items-center gap-1\">\n <ConnectionBadge\n status=\"connected\"\n {...(displayName ? { name: displayName } : {})}\n {...(avatarUrl ? { avatarUrl } : {})}\n />\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button className=\"inline-flex items-center justify-center rounded-md p-1 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"min-w-[200px]\">\n {/* Provider header with per-service profile info */}\n <DropdownMenuLabel className=\"flex items-center gap-2\">\n {avatarUrl ? (\n <img src={avatarUrl} alt=\"\" className=\"h-5 w-5 rounded-full shrink-0\" />\n ) : (\n <ServiceIcon\n name={service.icon}\n className=\"h-4 w-4 shrink-0\"\n style={{ color: service.color }}\n />\n )}\n <div className=\"flex flex-col min-w-0\">\n <span className=\"truncate\">{displayName ?? service.name}</span>\n {email && (\n <span className=\"text-xs font-normal text-muted-foreground truncate\">{email}</span>\n )}\n </div>\n </DropdownMenuLabel>\n\n {/* Connection timestamp */}\n {connectedDate && (\n <div className=\"flex items-center gap-2 px-2 pb-1.5 text-xs text-muted-foreground\">\n <Clock className=\"h-3 w-3 shrink-0\" />\n <span>Connected {connectedDate}</span>\n </div>\n )}\n\n <DropdownMenuSeparator />\n\n {/* Actions */}\n {profileUrl && (\n <DropdownMenuItem asChild>\n <a href={profileUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <ExternalLink />\n Open Profile\n </a>\n </DropdownMenuItem>\n )}\n {service.grantsUrl && (\n <DropdownMenuItem asChild>\n <a href={service.grantsUrl} target=\"_blank\" rel=\"noopener noreferrer\">\n <Shield />\n Manage Permissions\n </a>\n </DropdownMenuItem>\n )}\n <DropdownMenuItem asChild>\n <a href={`/providers/${service.id}`}>\n <Info />\n About {service.name}\n </a>\n </DropdownMenuItem>\n {isOAuth && (\n <DropdownMenuItem onClick={handleReconnect}>\n <RefreshCw />\n Reconnect\n </DropdownMenuItem>\n )}\n <DropdownMenuItem variant=\"destructive\" onClick={handleDisconnect}>\n <LogOut />\n Disconnect\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n );\n}\n","import React, { useEffect, useState } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { match } from 'ts-pattern';\nimport { ConnectionBadge } from '@anymux/ui/components/connection-badge';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ServiceDefinition } from '../types/service';\nimport { getServiceConnectionState } from '../types/connection-state';\nimport { CredentialFormModel, type CredentialServiceType, isS3Compatible } from '../models/CredentialFormModel';\nimport { CredentialForm } from './CredentialForm';\nimport { ConnectedMenu } from './ConnectedMenu';\n\ninterface ConnectButtonProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const ConnectButton: React.FC<ConnectButtonProps> = observer(({ service, connectionManager }) => {\n const [formModel] = useState(() => new CredentialFormModel());\n const state = getServiceConnectionState(connectionManager, service.id);\n\n const needsCredentialForm = isS3Compatible(service.authProvider as CredentialServiceType) || service.authProvider === 'webdav' || service.authProvider === 'gitea' || service.authProvider === 'icloud' || service.authProvider === 'unsplash' || service.authProvider === 'pexels' || service.authProvider === 'imgur';\n const isBrowserFs = service.authProvider === 'browser-fs';\n const isIndexedDb = service.authProvider === 'indexeddb';\n\n // Auto-trigger connect flow when reconnect is requested (e.g. from CapabilityPanel)\n const pendingReconnect = connectionManager.pendingReconnect.has(service.id);\n useEffect(() => {\n if (pendingReconnect) {\n connectionManager.clearReconnectRequest(service.id);\n handleConnect();\n }\n }, [pendingReconnect]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleConnect = async () => {\n if (isBrowserFs) {\n try {\n const { BrowserFileSystemFactory } = await import('@anymux/browser-fs');\n const factory = new BrowserFileSystemFactory();\n const [_fs, handleInfo] = await factory.createFromPicker();\n connectionManager.connectWithCredentials(service.id, JSON.stringify({ id: handleInfo.id, name: handleInfo.name }));\n } catch (err: any) {\n if (err.name !== 'AbortError') {\n console.error('Failed to pick directory:', err);\n }\n }\n return;\n }\n if (isIndexedDb) {\n connectionManager.connectWithCredentials(service.id, JSON.stringify({ type: 'indexeddb' }));\n return;\n }\n if (needsCredentialForm) {\n formModel.openForm(service.authProvider as CredentialServiceType);\n } else {\n connectionManager.connect(service.id);\n }\n };\n\n const handleCredentialSubmit = (credentialToken: string) => {\n connectionManager.connectWithCredentials(service.id, credentialToken);\n formModel.closeForm();\n };\n\n const credentialFormElement = needsCredentialForm ? (\n <CredentialForm model={formModel} onSubmit={handleCredentialSubmit} />\n ) : null;\n\n return match(state)\n .with({ status: 'loading' }, () => (\n <ConnectionBadge status=\"loading\" />\n ))\n .with({ status: 'not_configured' }, () => (\n <ConnectionBadge status=\"not-configured\" />\n ))\n .with({ status: 'connecting' }, () => (\n <ConnectionBadge status=\"connecting\" />\n ))\n .with({ status: 'connected' }, (s) => {\n const isOAuth = !needsCredentialForm && !isBrowserFs && !isIndexedDb;\n const user = isOAuth ? s.user : null;\n return (\n <ConnectedMenu\n service={service}\n connectionManager={connectionManager}\n user={user}\n {...(s.profile ? { profile: s.profile } : {})}\n isOAuth={isOAuth}\n />\n );\n })\n .with({ status: 'error' }, () => (\n <>\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white bg-destructive hover:bg-destructive/90 transition-colors\"\n >\n Retry\n </button>\n {credentialFormElement}\n </>\n ))\n .with({ status: 'expired' }, () => (\n <>\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white bg-destructive hover:bg-destructive/90 transition-colors\"\n >\n Reconnect\n </button>\n {credentialFormElement}\n </>\n ))\n .with({ status: 'disconnected' }, () => {\n const testCreds = connectionManager.testCredentials[service.authProvider] as Record<string, unknown> | undefined;\n return (\n <>\n <div className=\"inline-flex items-center gap-1.5\">\n <button\n onClick={handleConnect}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-white transition-colors\"\n style={{ backgroundColor: service.color }}\n >\n Connect\n </button>\n {testCreds && (\n <button\n onClick={() => {\n if (needsCredentialForm) {\n const values: Record<string, string> = {};\n for (const [k, v] of Object.entries(testCreds)) {\n if (typeof v === 'string') values[k] = v;\n }\n formModel.openForm(service.authProvider as CredentialServiceType, values);\n } else {\n connectionManager.connectWithCredentials(service.id, JSON.stringify(testCreds));\n }\n }}\n className=\"rounded-md px-2 py-1.5 text-xs font-medium text-muted-foreground bg-muted hover:bg-muted/80 transition-colors\"\n >\n Quick Test\n </button>\n )}\n </div>\n {credentialFormElement}\n </>\n );\n })\n .exhaustive();\n});\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { Check, Minus } from 'lucide-react';\nimport { match } from 'ts-pattern';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\n\ninterface CapabilityCellProps {\n service: ServiceDefinition;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const CapabilityCell: React.FC<CapabilityCellProps> = observer(\n ({ service, capabilityId, connectionManager, dashboardModel }) => {\n const capability = service.capabilities.find((c) => c.id === capabilityId);\n // For merged git column: show as supported if either git-repo or git-host is supported\n const gitHostCap = capabilityId === 'git-repo'\n ? service.capabilities.find((c) => c.id === 'git-host')\n : undefined;\n const supported = (capability?.supported ?? false) || (gitHostCap?.supported ?? false);\n // Route to the right capability when clicking: prefer git-repo, fallback to git-host\n const effectiveCapabilityId = capabilityId === 'git-repo' && !capability?.supported && gitHostCap?.supported\n ? 'git-host' as CapabilityId\n : capabilityId;\n const status = connectionManager.getStatus(service.id);\n const isSelected =\n dashboardModel.selectedCell?.serviceId === service.id &&\n (dashboardModel.selectedCell?.capabilityId === effectiveCapabilityId ||\n (capabilityId === 'git-repo' && dashboardModel.selectedCell?.capabilityId === 'git-host'));\n\n if (!supported) {\n return (\n <td className=\"px-3 py-2 text-center align-middle\">\n <Minus className=\"h-4 w-4 text-muted-foreground/30 mx-auto\" />\n </td>\n );\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n dashboardModel.selectCell(service.id, effectiveCapabilityId);\n }\n };\n\n return match(status)\n .with('loading', () => (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-block h-4 w-4 rounded bg-muted animate-pulse mx-auto\" />\n </td>\n ))\n .with('connected', () => {\n // Don't show green/orange until scopes have been fetched\n if (!connectionManager.initialized) {\n return (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-block h-4 w-4 rounded bg-muted animate-pulse mx-auto\" />\n </td>\n );\n }\n const hasScopes = connectionManager.hasCapabilityScopes(service.id, effectiveCapabilityId);\n const hoverBg = hasScopes\n ? 'hover:bg-green-50 dark:hover:bg-green-900/20'\n : 'hover:bg-orange-50 dark:hover:bg-orange-900/20';\n return (\n <td\n className={`px-3 py-2 text-center align-middle cursor-pointer transition-colors ${\n isSelected\n ? 'bg-blue-100 dark:bg-blue-900/40'\n : hoverBg\n }`}\n role=\"button\"\n tabIndex={0}\n onClick={() => dashboardModel.selectCell(service.id, effectiveCapabilityId)}\n onKeyDown={handleKeyDown}\n title={hasScopes ? undefined : 'Scope not granted'}\n >\n <span className={`inline-flex items-center justify-center w-5 h-5 rounded-full mx-auto ${\n hasScopes ? 'bg-green-500' : 'bg-orange-400'\n } text-white`}>\n <Check className=\"h-3 w-3\" />\n </span>\n </td>\n );\n })\n .otherwise(() => (\n <td className=\"px-3 py-2 text-center align-middle\">\n <span className=\"inline-flex items-center justify-center w-5 h-5 rounded border border-muted-foreground/30 mx-auto\">\n <Check className=\"h-3 w-3 text-muted-foreground/50\" />\n </span>\n </td>\n ));\n }\n);\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { ServiceIcon } from './ServiceIcon';\nimport { ConnectButton } from './ConnectButton';\nimport { CapabilityCell } from './CapabilityCell';\n\nconst CAPABILITY_COLUMNS: CapabilityId[] = [\n 'file-system',\n 'object-storage',\n 'git-repo',\n 'media',\n 'contacts',\n 'calendar',\n];\n\ninterface ServiceRowProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceRow: React.FC<ServiceRowProps> = observer(\n ({ service, connectionManager, dashboardModel }) => {\n return (\n <tr className=\"border-b border-border h-11\">\n <td className=\"px-3 py-2 align-middle\">\n <div className=\"flex items-center gap-2\">\n <ServiceIcon name={service.icon} className=\"h-4 w-4\" style={{ color: service.color }} />\n <a href={`/providers/${service.id}`} className=\"text-sm font-medium truncate hover:underline\" title={service.name}>{service.name}</a>\n </div>\n </td>\n <td className=\"px-3 py-2 align-middle\">\n <ConnectButton service={service} connectionManager={connectionManager} />\n </td>\n {CAPABILITY_COLUMNS.map((cap) => (\n <CapabilityCell\n key={cap}\n service={service}\n capabilityId={cap}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </tr>\n );\n }\n);\n","import React from 'react';\nimport { Check, Minus } from 'lucide-react';\nimport { match } from 'ts-pattern';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { ConnectionStatus } from '../types/connection';\nimport type { CapabilityId } from '../types/service';\n\ninterface CapabilityPillProps {\n label: string;\n capabilityId: CapabilityId;\n status: ConnectionStatus;\n isSelected: boolean;\n hasScopes: boolean;\n onSelect: () => void;\n disabled?: boolean;\n}\n\nexport const CapabilityPill: React.FC<CapabilityPillProps> = ({\n label,\n status,\n isSelected,\n hasScopes,\n onSelect,\n disabled = false,\n}) => {\n const connected = status === 'connected';\n const loading = status === 'loading';\n\n const className = `inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium transition-colors ${\n loading\n ? 'bg-muted text-muted-foreground'\n : isSelected\n ? 'bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300'\n : connected\n ? 'bg-muted text-foreground hover:bg-muted/80 cursor-pointer'\n : 'bg-muted/50 text-muted-foreground'\n }`;\n\n const icon = match(status)\n .with('loading', () => <LoadingSpinner size=\"sm\" label=\"Loading\" className=\"h-3 w-3\" />)\n .with('connected', () => (\n <Check className={`h-3 w-3 ${hasScopes ? 'text-green-500' : 'text-orange-400'}`} />\n ))\n .otherwise(() => <Minus className=\"h-3 w-3\" />);\n\n return (\n <button\n onClick={onSelect}\n disabled={disabled || !connected}\n className={className}\n >\n {icon}\n {label}\n </button>\n );\n};\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { CapabilityId, ServiceDefinition } from '../types/service';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { ServiceIcon } from './ServiceIcon';\nimport { ConnectButton } from './ConnectButton';\nimport { CapabilityPill } from './CapabilityPill';\n\nconst CAPABILITY_COLUMNS: { id: CapabilityId; label: string }[] = [\n { id: 'file-system', label: 'FS' },\n { id: 'object-storage', label: 'Obj' },\n { id: 'git-repo', label: 'Git' },\n { id: 'media', label: 'Media' },\n { id: 'contacts', label: 'Contacts' },\n { id: 'calendar', label: 'Cal' },\n];\n\ninterface ServiceCardProps {\n service: ServiceDefinition;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceCard: React.FC<ServiceCardProps> = observer(\n ({ service, connectionManager, dashboardModel }) => {\n const status = connectionManager.getStatus(service.id);\n const connected = status === 'connected';\n const supportedCaps = CAPABILITY_COLUMNS.filter((cap) => {\n const supported = service.capabilities.find((c) => c.id === cap.id)?.supported;\n // For merged git column: also check git-host\n if (cap.id === 'git-repo' && !supported) {\n return service.capabilities.find((c) => c.id === 'git-host')?.supported ?? false;\n }\n return supported;\n });\n\n return (\n <div className=\"rounded-lg border border-border bg-card p-3\">\n {/* Top row: icon + name + connect button */}\n <div className=\"flex items-center justify-between gap-2 mb-2\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <ServiceIcon name={service.icon} className=\"h-5 w-5 flex-shrink-0\" style={{ color: service.color }} />\n <span className=\"text-sm font-medium truncate\" title={service.name}>{service.name}</span>\n </div>\n <ConnectButton service={service} connectionManager={connectionManager} />\n </div>\n\n {/* Capability pills */}\n {supportedCaps.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {supportedCaps.map((cap) => {\n const isSelected =\n dashboardModel.selectedCell?.serviceId === service.id &&\n dashboardModel.selectedCell?.capabilityId === cap.id;\n const hasScopes = connected && connectionManager.hasCapabilityScopes(service.id, cap.id);\n\n return (\n <CapabilityPill\n key={cap.id}\n label={cap.label}\n capabilityId={cap.id}\n status={status}\n isSelected={isSelected}\n hasScopes={hasScopes}\n onSelect={() => {\n if (connected) dashboardModel.selectCell(service.id, cap.id);\n }}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n }\n);\n","import type { CapabilityId } from '../types/service';\n\nexport interface AdapterContext {\n /** Selected repo for git-backed services */\n selectedRepo?: { owner: string; repo: string };\n /** Override the branch/ref for file-system adapters */\n branch?: string;\n}\n\nexport type AdapterFactory = (\n token: string,\n capabilityId: CapabilityId,\n context: AdapterContext,\n) => Promise<unknown>;\n\nconst factories = new Map<string, AdapterFactory>();\n\nexport const adapterRegistry = {\n register(serviceId: string, factory: AdapterFactory): void {\n factories.set(serviceId, factory);\n },\n\n async create(\n serviceId: string,\n token: string,\n capabilityId: CapabilityId,\n context: AdapterContext = {},\n ): Promise<unknown> {\n const factory = factories.get(serviceId);\n if (!factory) {\n throw new Error(`No adapter factory registered for service: ${serviceId}`);\n }\n return factory(token, capabilityId, context);\n },\n\n has(serviceId: string): boolean {\n return factories.has(serviceId);\n },\n};\n\n// --- Register all adapter factories ---\n// Capability validation is handled by the service registry; factories only create adapters.\n\n/** Safely invoke a capability factory, throwing a descriptive error for unsupported capabilities */\nfunction invokeCapability(adapters: Partial<Record<CapabilityId, () => unknown>>, capabilityId: CapabilityId, serviceId: string): unknown {\n const factory = adapters[capabilityId];\n if (!factory) {\n throw new Error(\n `Unsupported capability \"${capabilityId}\" for service \"${serviceId}\". ` +\n `Supported: ${Object.keys(adapters).join(', ')}`,\n );\n }\n return factory();\n}\n\n// Google Drive / Contacts / Calendar\nadapterRegistry.register('google', async (token, capabilityId) => {\n const m = await import('@anymux/google-drive');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GoogleDriveFileSystem({ accessToken: token }),\n 'contacts': () => new m.GoogleContactsProvider(token),\n 'calendar': () => new m.GoogleCalendarProvider(token),\n };\n return invokeCapability(adapters, capabilityId, 'google');\n});\n\n// Dropbox\nadapterRegistry.register('dropbox', async (token) => {\n const { DropboxFileSystem } = await import('@anymux/dropbox');\n return new DropboxFileSystem({ accessToken: token });\n});\n\n// Box\nadapterRegistry.register('box', async (token) => {\n const { BoxFileSystem } = await import('@anymux/box');\n return new BoxFileSystem({ accessToken: token });\n});\n\n// GitHub\nadapterRegistry.register('github', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, owner: repo.owner, repo: repo.repo, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/github');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GitHubFileSystem(config),\n 'git-repo': () => new m.GitHubGitRepo(config),\n 'git-host': () => new m.GitHubGitHost(config),\n };\n return invokeCapability(adapters, capabilityId, 'github');\n});\n\n// GitLab\nadapterRegistry.register('gitlab', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, projectId: `${repo.owner}/${repo.repo}`, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/gitlab');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GitLabFileSystem(config),\n 'git-repo': () => new m.GitLabGitRepo(config),\n 'git-host': () => new m.GitLabGitHost(config),\n };\n return invokeCapability(adapters, capabilityId, 'gitlab');\n});\n\n// Bitbucket\nadapterRegistry.register('bitbucket', async (token, capabilityId, ctx) => {\n const repo = ctx.selectedRepo;\n if (!repo) return null; // needs repo selection first\n const config = { token, workspace: repo.owner, repo: repo.repo, branch: ctx.branch ?? 'main' };\n const m = await import('@anymux/bitbucket');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.BitbucketFileSystem(config),\n 'git-repo': () => new m.BitbucketGitRepo(config),\n 'git-host': () => new m.BitbucketGitHost(config),\n };\n return invokeCapability(adapters, capabilityId, 'bitbucket');\n});\n\n// Gitea\nadapterRegistry.register('gitea', async (token, capabilityId, ctx) => {\n const creds = JSON.parse(token);\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n const config = { url: creds.url, token: creds.token, username: creds.username, password: creds.password, owner: creds.owner, repo: creds.repo, branch: ctx.branch ?? creds.branch ?? 'main', proxyUrl };\n const m = await import('@anymux/gitea');\n const adapters: Partial<Record<CapabilityId, () => unknown>> = {\n 'file-system': () => new m.GiteaFileSystem(config),\n 'git-repo': () => new m.GiteaGitRepo(config),\n 'git-host': () => new m.GiteaGitHost(config),\n };\n return invokeCapability(adapters, capabilityId, 'gitea');\n});\n\n// WebDAV\nadapterRegistry.register('webdav', async (token) => {\n const creds = JSON.parse(token);\n const { WebDAVFileSystem } = await import('@anymux/webdav');\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n return new WebDAVFileSystem({ url: creds.url, username: creds.username, password: creds.password, proxyUrl });\n});\n\n// S3-compatible adapter factory (shared by S3, Backblaze B2, Cloudflare R2, Wasabi)\nasync function createS3Adapter(token: string) {\n const creds = JSON.parse(token);\n const proxyUrl = typeof window !== 'undefined' ? window.location.origin : undefined;\n if (proxyUrl) {\n const { S3ProxyStorage } = await import('@anymux/s3');\n const storage = new S3ProxyStorage({\n proxyUrl,\n endpoint: creds.endpoint,\n region: creds.region?.trim(),\n accessKeyId: creds.accessKeyId?.trim(),\n secretAccessKey: creds.secretAccessKey?.trim(),\n forcePathStyle: true,\n });\n return { storage, bucket: creds.bucket || 'anymux-test' };\n }\n const { S3ObjectStorage } = await import('@anymux/s3');\n const storage = new S3ObjectStorage({\n region: creds.region?.trim(),\n endpoint: creds.endpoint,\n credentials: { accessKeyId: creds.accessKeyId?.trim(), secretAccessKey: creds.secretAccessKey?.trim() },\n forcePathStyle: true,\n });\n return { storage, bucket: creds.bucket || 'anymux-test' };\n}\n\nadapterRegistry.register('s3', createS3Adapter);\nadapterRegistry.register('backblaze-b2', createS3Adapter);\nadapterRegistry.register('cloudflare-r2', createS3Adapter);\nadapterRegistry.register('wasabi', createS3Adapter);\n\n// Unsplash\nadapterRegistry.register('unsplash', async (token) => {\n const creds = JSON.parse(token);\n const { UnsplashMediaProvider } = await import('@anymux/unsplash');\n return new UnsplashMediaProvider({ accessKey: creds.accessKey });\n});\n\n// Pexels\nadapterRegistry.register('pexels', async (token) => {\n const creds = JSON.parse(token);\n const { PexelsMediaProvider } = await import('@anymux/pexels');\n return new PexelsMediaProvider({ apiKey: creds.apiKey });\n});\n\n// Imgur\nadapterRegistry.register('imgur', async (token) => {\n const creds = JSON.parse(token);\n const { ImgurMediaProvider } = await import('@anymux/imgur');\n return new ImgurMediaProvider({ clientId: creds.clientId });\n});\n\n// BrowserFS (File System Access API)\nadapterRegistry.register('browser-fs', async () => {\n const { BrowserFileSystemFactory } = await import('@anymux/browser-fs');\n const factory = new BrowserFileSystemFactory();\n const handleInfos = await factory.getAllHandleInfos();\n if (handleInfos.length > 0) {\n return factory.createFromHandleInfo(handleInfos[0]!.id);\n }\n throw new Error('No local file system connected. Please connect first.');\n});\n\n// IndexedDB\nadapterRegistry.register('indexeddb', async () => {\n const { IndexedDBFileSystem } = await import('@anymux/indexeddb');\n const fs = new IndexedDBFileSystem();\n await fs.init();\n return fs;\n});\n\n","import { useState, useEffect } from 'react';\n\n/** Hook to async-create an adapter, run a pre-flight check, and track loading/error state */\nexport function useAdapter<T>(\n factory: () => Promise<T>,\n deps: unknown[],\n preflight?: (adapter: T) => Promise<void>\n): { adapter: T | null; loading: boolean; error: string | null; retry: () => void } {\n const [adapter, setAdapter] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [retryCount, setRetryCount] = useState(0);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n setAdapter(null);\n\n factory()\n .then(async (a) => {\n // Run pre-flight check (e.g. readdir) to catch auth errors before mounting UI\n if (preflight) await preflight(a);\n if (!cancelled) { setAdapter(a); setLoading(false); }\n })\n .catch((e) => { if (!cancelled) { setError(e instanceof Error ? e.message : String(e)); setLoading(false); } });\n\n return () => { cancelled = true; };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...deps, retryCount]);\n\n return { adapter, loading, error, retry: () => setRetryCount((c) => c + 1) };\n}\n","import React from 'react';\nimport { ArrowLeft, RefreshCw } from 'lucide-react';\nimport { ErrorDisplay } from '@anymux/ui/components/error-display';\n\nfunction isAuthError(error: string): boolean {\n return /\\b(401|403|auth|token|expired|unauthorized|forbidden|denied|scope|permission|sign.?in|log.?in)\\b/i.test(error);\n}\n\nexport { isAuthError };\n\n/** Render error with contextual actions for capability panels */\nexport function CapabilityError({ error, onRetry, onReconnect, onGoBack }: {\n error: string;\n onRetry?: () => void;\n onReconnect?: () => void;\n onGoBack?: () => void;\n}) {\n const authError = isAuthError(error);\n return (\n <div className=\"flex flex-col items-center justify-center h-64 gap-4 p-4\">\n <ErrorDisplay\n error={error}\n title={authError ? 'Authentication Required' : 'Failed to Load'}\n variant={authError ? 'warning' as const : 'destructive' as const}\n className=\"max-w-md w-full\"\n {...(onRetry != null ? { onRetry } : {})}\n />\n <div className=\"flex items-center gap-2\">\n {onReconnect && (\n <button\n onClick={onReconnect}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <RefreshCw className=\"h-3 w-3\" />\n Reconnect\n </button>\n )}\n {onGoBack && (\n <button\n onClick={onGoBack}\n className=\"inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium rounded-md bg-muted hover:bg-muted/80 transition-colors\"\n >\n <ArrowLeft className=\"h-3 w-3\" />\n Back to Dashboard\n </button>\n )}\n </div>\n </div>\n );\n}\n","import React, { Suspense, useState, useCallback } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { showActionToast, showErrorToast } from '../utils/action-toast';\nimport { match } from 'ts-pattern';\nimport { X, ArrowLeft, AlertTriangle } from 'lucide-react';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { ActionNotificationModel } from '../models/ActionNotificationModel';\nimport type { CapabilityId } from '../types/service';\nimport type { IFileSystem, IObjectStorage } from '@anymux/file-system';\nimport { adapterRegistry } from '../adapters/adapter-registry';\nimport { useAdapter } from './useAdapter';\nimport { CapabilityError, isAuthError } from './CapabilityError';\n\n// Lazy-load browser components\nconst FileBrowser = React.lazy(() =>\n import('@anymux/ui-kit/file-browser').then((m) => ({ default: m.FileBrowser }))\n);\n\nconst LazyMediaBrowser = React.lazy(() =>\n import('@anymux/ui-kit/media').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.MediaBrowserModel(provider as never);\n return React.createElement(m.MediaBrowser, { model, provider: provider as never, className: 'h-full' });\n },\n }))\n);\n\nconst LazyContactBrowser = React.lazy(() =>\n import('@anymux/ui-kit/contacts').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.ContactListModel(provider as never);\n return React.createElement(m.ContactBrowser, { model, className: 'h-full' });\n },\n }))\n);\n\nconst LazyCalendarBrowser = React.lazy(() =>\n import('@anymux/ui-kit/calendar').then((m) => ({\n default: ({ provider }: { provider: unknown }) => {\n const model = new m.CalendarModel(provider as never);\n return React.createElement(m.CalendarBrowser, { model, className: 'h-full' });\n },\n }))\n);\n\n// Lazy-load RepoPicker\nconst RepoPicker = React.lazy(() =>\n import('./RepoPicker').then((m) => ({ default: m.RepoPicker }))\n);\n\n// Lazy-load browser components from this package\nconst ObjectStorageBrowserLazy = React.lazy(() =>\n import('./ObjectStorageBrowser').then((m) => ({ default: m.ObjectStorageBrowser }))\n);\n\nconst GitRepoBrowserLazy = React.lazy(() =>\n import('./GitBrowser').then((m) => ({ default: m.GitRepoBrowser }))\n);\n\nconst GitHostBrowserLazy = React.lazy(() =>\n import('./GitBrowser').then((m) => ({ default: m.GitHostBrowser }))\n);\n\nconst CAPABILITY_LABELS: Record<CapabilityId, string> = {\n 'file-system': 'File System',\n 'object-storage': 'Object Storage',\n 'git-repo': 'Git',\n 'git-host': 'Git',\n 'media': 'Media',\n 'contacts': 'Contacts',\n 'calendar': 'Calendar',\n};\n\n// Services that need repo selection before showing capability content\nconst REPO_SERVICES = new Set(['github', 'gitlab', 'bitbucket', 'gitea']);\n\nconst LoadingFallback = () => (\n <div className=\"flex items-center justify-center h-64\">\n <LoadingSpinner label=\"Loading...\" />\n </div>\n);\n\n// --- Git repo browser wrapper that provides createFileSystem ---\n\nfunction GitRepoBrowserContent({\n serviceId,\n token,\n gitRepo,\n owner,\n repo,\n selectedRepo,\n onError,\n actionNotifications,\n}: {\n serviceId: string;\n token: string;\n gitRepo: import('@anymux/file-system').IGitRepo;\n owner: string;\n repo: string;\n selectedRepo?: { owner: string; repo: string };\n onError?: (err: { message: string }) => void;\n actionNotifications?: ActionNotificationModel;\n}) {\n const createFileSystem = useCallback(\n async (branch: string): Promise<IFileSystem> => {\n const context = { selectedRepo, branch };\n const fs = await adapterRegistry.create(serviceId, token, 'file-system', context);\n return fs as IFileSystem;\n },\n [serviceId, token, selectedRepo],\n );\n\n // Also load git-host adapter for PRs/Issues tabs\n const [gitHost, setGitHost] = useState<import('@anymux/file-system').IGitHost | undefined>();\n React.useEffect(() => {\n let cancelled = false;\n adapterRegistry.create(serviceId, token, 'git-host', { selectedRepo })\n .then((adapter) => { if (!cancelled) setGitHost(adapter as any); })\n .catch(() => {}); // Silently fail — PRs/Issues just won't show\n return () => { cancelled = true; };\n }, [serviceId, token, selectedRepo]);\n\n return (\n <GitRepoBrowserLazy\n gitRepo={gitRepo}\n owner={owner}\n repo={repo}\n createFileSystem={createFileSystem}\n gitHost={gitHost}\n onError={onError}\n actionNotifications={actionNotifications}\n />\n );\n}\n\n// --- Git Host wrapper that also loads git-repo adapter for unified browser ---\n\nfunction GitHostBrowserWithRepo({\n serviceId,\n token,\n gitHost,\n owner,\n repo,\n selectedRepo,\n onError,\n actionNotifications,\n}: {\n serviceId: string;\n token: string;\n gitHost: import('@anymux/file-system').IGitHost;\n owner: string;\n repo: string;\n selectedRepo?: { owner: string; repo: string };\n onError?: (err: { message: string }) => void;\n actionNotifications?: ActionNotificationModel;\n}) {\n const [gitRepo, setGitRepo] = useState<import('@anymux/file-system').IGitRepo | undefined>();\n const [repoLoaded, setRepoLoaded] = useState(false);\n\n React.useEffect(() => {\n let cancelled = false;\n adapterRegistry.create(serviceId, token, 'git-repo', { selectedRepo })\n .then((adapter) => { if (!cancelled) setGitRepo(adapter as any); })\n .catch(() => {}) // Silently fail — just show PRs/Issues\n .finally(() => { if (!cancelled) setRepoLoaded(true); });\n return () => { cancelled = true; };\n }, [serviceId, token, selectedRepo]);\n\n const createFileSystem = useCallback(\n async (branch: string): Promise<IFileSystem> => {\n const context = { selectedRepo, branch };\n const fs = await adapterRegistry.create(serviceId, token, 'file-system', context);\n return fs as IFileSystem;\n },\n [serviceId, token, selectedRepo],\n );\n\n if (!repoLoaded) return <LoadingFallback />;\n\n // If we have git-repo, show unified browser with all tabs\n if (gitRepo) {\n return (\n <GitRepoBrowserLazy\n gitRepo={gitRepo}\n owner={owner}\n repo={repo}\n createFileSystem={createFileSystem}\n gitHost={gitHost}\n onError={onError}\n actionNotifications={actionNotifications}\n />\n );\n }\n\n // Fallback: just show PRs/Issues if git-repo adapter not available\n return <GitHostBrowserLazy gitHost={gitHost} />;\n}\n\n// --- Generic capability content renderer ---\n\nconst GenericCapabilityContent = observer(function GenericCapabilityContent({\n serviceId,\n capabilityId,\n token,\n dashboardModel,\n connectionManager,\n}: {\n serviceId: string;\n capabilityId: CapabilityId;\n token: string;\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}) {\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const context = { selectedRepo: selectedRepo ?? undefined };\n\n const { adapter, loading, error, retry } = useAdapter(\n async () => {\n // Refresh OAuth token before creating adapter to avoid stale 401s\n console.info(`[AnyMux] CapabilityPanel: refreshing token for ${serviceId}/${capabilityId}`);\n const freshToken = await connectionManager.refreshToken(serviceId) ?? token;\n console.info(`[AnyMux] CapabilityPanel: creating adapter with token ${freshToken.slice(0, 8)}…${freshToken.slice(-4)}`);\n const a = adapterRegistry.create(serviceId, freshToken, capabilityId, context);\n return a;\n },\n [serviceId, capabilityId, token, selectedRepo?.owner, selectedRepo?.repo],\n // Pre-flight: verify the adapter actually works before mounting the browser UI.\n // If we get a 401, try one more refresh — better-auth may not have detected\n // the token as expired (e.g. missing accessTokenExpiresAt in the DB).\n async (a) => {\n const runCheck = async (adapter: any) => {\n if (capabilityId === 'file-system') {\n await (adapter as IFileSystem).readdir('/');\n } else if (capabilityId === 'git-host') {\n if (adapter.listRepositories) await adapter.listRepositories({ page: 1, perPage: 1 });\n } else if (capabilityId === 'git-repo') {\n if (adapter.listBranches) await adapter.listBranches();\n } else if (capabilityId === 'calendar') {\n if (adapter.getCalendars) await adapter.getCalendars();\n } else if (capabilityId === 'contacts') {\n if (adapter.getContacts) await adapter.getContacts({ limit: 1 });\n } else if (capabilityId === 'media') {\n if (adapter.getAlbums) await adapter.getAlbums();\n }\n };\n\n try {\n console.info(`[AnyMux] CapabilityPanel: running pre-flight for ${serviceId}/${capabilityId}`);\n await runCheck(a);\n console.info(`[AnyMux] CapabilityPanel: pre-flight passed`);\n } catch (err) {\n console.warn(`[AnyMux] CapabilityPanel: pre-flight failed for ${serviceId}/${capabilityId}:`, err instanceof Error ? err.message : err);\n // If we get a 401/auth error, try one more refresh and rebuild the adapter\n if (err instanceof Error && isAuthError(err.message)) {\n console.info(`[AnyMux] CapabilityPanel: auth error detected, attempting second refresh`);\n const retryToken = await connectionManager.refreshToken(serviceId);\n if (retryToken && retryToken !== token) {\n console.info(`[AnyMux] CapabilityPanel: got different token on retry, rebuilding adapter`);\n const retryAdapter = await adapterRegistry.create(serviceId, retryToken, capabilityId, context);\n await runCheck(retryAdapter);\n Object.assign(a as any, retryAdapter);\n return;\n }\n console.warn(`[AnyMux] CapabilityPanel: retry token same as original, giving up`);\n }\n throw err;\n }\n }\n );\n\n const handleReconnect = async () => {\n const service = dashboardModel.selectedService;\n await connectionManager.disconnect(serviceId);\n dashboardModel.closePanel();\n // For OAuth services, immediately redirect to provider consent screen\n const CRED_PROVIDERS = new Set(['s3', 'backblaze-b2', 'cloudflare-r2', 'wasabi', 'webdav', 'gitea', 'icloud', 'unsplash', 'pexels', 'imgur', 'browser-fs', 'indexeddb']);\n const isOAuth = service && !CRED_PROVIDERS.has(service.authProvider);\n if (isOAuth) {\n await connectionManager.connect(serviceId);\n } else {\n // Signal ConnectButton to auto-open credential form / file picker\n connectionManager.requestReconnect(serviceId);\n }\n };\n\n const handleGoBack = () => {\n dashboardModel.closePanel();\n };\n\n // Track runtime auth errors (e.g. 401 from API calls after adapter is created)\n const [runtimeError, setRuntimeError] = useState<string | null>(null);\n\n const handleRuntimeError = (err: { message: string }) => {\n if (isAuthError(err.message)) {\n setRuntimeError(err.message);\n }\n };\n\n const handleNotify = useCallback((type: 'success' | 'error' | 'warning', message: string) => {\n if (type === 'success') {\n showActionToast(dashboardModel.actionNotifications, 'create', message);\n } else {\n showErrorToast(message);\n }\n }, [dashboardModel.actionNotifications]);\n\n const handleAction = useCallback((action: { type: 'create' | 'rename' | 'delete' | 'upload'; message: string; undo?: () => Promise<void> }) => {\n showActionToast(dashboardModel.actionNotifications, action.type, action.message, { undo: action.undo });\n }, [dashboardModel.actionNotifications]);\n\n if (loading) return <LoadingFallback />;\n if (error) return <CapabilityError error={error} onRetry={retry} onReconnect={handleReconnect} onGoBack={handleGoBack} />;\n if (runtimeError) return <CapabilityError error={runtimeError} onReconnect={handleReconnect} onGoBack={handleGoBack} />;\n if (adapter === null) return null; // Should not happen after repo selection\n\n // Route by capability type\n return match(capabilityId)\n .with('file-system', () => (\n <Suspense fallback={<LoadingFallback />}>\n <FileBrowser\n fileSystem={adapter as IFileSystem}\n className=\"h-full\"\n initialPath={dashboardModel.browserPath !== '/' ? dashboardModel.browserPath : undefined}\n onPathChange={(path) => dashboardModel.setBrowserPath(path)}\n onError={handleRuntimeError}\n onNotify={handleNotify}\n onAction={handleAction}\n showBreadcrumbs={false}\n />\n </Suspense>\n ))\n .with('object-storage', () => {\n const s3Result = adapter as { storage: IObjectStorage; bucket: string };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <ObjectStorageBrowserLazy storage={s3Result.storage} bucket={s3Result.bucket} />\n </Suspense>\n );\n })\n .with('git-repo', () => {\n const repo = selectedRepo ?? { owner: '', repo: '' };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <GitRepoBrowserContent\n serviceId={serviceId}\n token={token}\n gitRepo={adapter as any}\n owner={repo.owner}\n repo={repo.repo}\n selectedRepo={selectedRepo ?? undefined}\n onError={handleRuntimeError}\n actionNotifications={dashboardModel.actionNotifications}\n />\n </Suspense>\n );\n })\n .with('git-host', () => {\n // Route git-host to unified browser: show PRs/Issues alongside git-repo tabs\n const repo = selectedRepo ?? { owner: '', repo: '' };\n return (\n <Suspense fallback={<LoadingFallback />}>\n <GitHostBrowserWithRepo\n serviceId={serviceId}\n token={token}\n gitHost={adapter as any}\n owner={repo.owner}\n repo={repo.repo}\n selectedRepo={selectedRepo ?? undefined}\n onError={handleRuntimeError}\n actionNotifications={dashboardModel.actionNotifications}\n />\n </Suspense>\n );\n })\n .with('media', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyMediaBrowser provider={adapter} />\n </Suspense>\n ))\n .with('contacts', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyContactBrowser provider={adapter} />\n </Suspense>\n ))\n .with('calendar', () => (\n <Suspense fallback={<LoadingFallback />}>\n <LazyCalendarBrowser provider={adapter} />\n </Suspense>\n ))\n .exhaustive();\n});\n\n/** Routes to the correct capability content based on service + capability */\nexport { CAPABILITY_LABELS };\nexport const CapabilityContent = observer(({\n serviceId,\n capabilityId,\n connectionManager,\n dashboardModel,\n}: {\n serviceId: string;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}) => {\n const service = dashboardModel.selectedService;\n if (!service) return null;\n\n const token = connectionManager.getToken(serviceId);\n if (!token) {\n return <CapabilityError error=\"Not connected. Please connect the service first.\" />;\n }\n\n // Services needing repo selection show picker first\n if (REPO_SERVICES.has(serviceId) && (capabilityId === 'file-system' || capabilityId === 'git-repo' || capabilityId === 'git-host')) {\n let selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n // Auto-extract owner/repo from credential token (e.g. Gitea Quick Test includes them)\n if (!selectedRepo) {\n try {\n const creds = JSON.parse(token);\n if (creds.owner && creds.repo) {\n dashboardModel.setSelectedRepo(serviceId, { owner: creds.owner, repo: creds.repo });\n selectedRepo = { owner: creds.owner, repo: creds.repo };\n }\n } catch {\n // Token is not JSON (e.g. OAuth token) — show picker\n }\n }\n if (!selectedRepo) {\n return (\n <Suspense fallback={<LoadingFallback />}>\n <RepoPicker\n serviceId={serviceId}\n accessToken={token}\n onSelectRepo={(repo) => dashboardModel.setSelectedRepo(serviceId, repo)}\n />\n </Suspense>\n );\n }\n }\n\n return (\n <GenericCapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n token={token}\n dashboardModel={dashboardModel}\n connectionManager={connectionManager}\n />\n );\n});\n\nexport const ScopeWarningBanner = observer(({ serviceId, capabilityId, connectionManager }: {\n serviceId: string;\n capabilityId: CapabilityId;\n connectionManager: ConnectionManagerModel;\n}) => {\n const token = connectionManager.getToken(serviceId);\n if (!token) return null;\n const hasScopes = connectionManager.hasCapabilityScopes(serviceId, capabilityId);\n if (hasScopes) return null;\n return (\n <div className=\"flex items-center gap-2 px-4 py-2 bg-yellow-50 dark:bg-yellow-900/20 border-b border-yellow-200 dark:border-yellow-800\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-500 flex-shrink-0\" />\n <span className=\"text-xs text-yellow-700 dark:text-yellow-400\">\n Required scopes not granted. Some features may not work. Try reconnecting with the needed permissions.\n </span>\n </div>\n );\n});\n\ninterface CapabilityPanelProps {\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const CapabilityPanel: React.FC<CapabilityPanelProps> = observer(({ dashboardModel, connectionManager }) => {\n if (!dashboardModel.panelOpen || !dashboardModel.selectedCell) {\n return null;\n }\n\n const { serviceId, capabilityId } = dashboardModel.selectedCell;\n const service = dashboardModel.selectedService;\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const hasRepo = REPO_SERVICES.has(serviceId) && selectedRepo;\n\n return (\n <div className=\"border border-border rounded-lg mt-4 overflow-hidden bg-card flex flex-col h-[70vh]\">\n <div className=\"flex items-center justify-between px-4 py-2 bg-muted border-b border-border\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium truncate\" title={`${service?.name} — ${CAPABILITY_LABELS[capabilityId]}`}>\n {service?.name} — {CAPABILITY_LABELS[capabilityId]}\n </span>\n {hasRepo && (\n <>\n <span className=\"text-xs text-muted-foreground truncate\" title={`${selectedRepo.owner}/${selectedRepo.repo}`}>\n ({selectedRepo.owner}/{selectedRepo.repo})\n </span>\n <button\n onClick={() => dashboardModel.clearSelectedRepo(serviceId)}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-[11px] font-medium rounded bg-muted hover:bg-muted/80 transition-colors\"\n >\n <ArrowLeft className=\"h-3 w-3\" />\n Change repo\n </button>\n </>\n )}\n </div>\n <button\n onClick={() => dashboardModel.closePanel()}\n className=\"p-1 rounded hover:bg-muted transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <ScopeWarningBanner serviceId={serviceId} capabilityId={capabilityId} connectionManager={connectionManager} />\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n <Suspense fallback={<LoadingFallback />}>\n <CapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n </Suspense>\n </div>\n </div>\n );\n});\n","import React, { useState, useRef, useEffect } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { History, Undo2, Trash2, FileEdit, FilePlus, Upload, Copy, Move, X } from 'lucide-react';\nimport type { ActionNotificationModel, ActionRecord, ActionType } from '../models/ActionNotificationModel';\n\nconst ACTION_ICONS: Record<ActionType, React.ReactNode> = {\n delete: <Trash2 className=\"h-3.5 w-3.5 text-destructive\" />,\n rename: <FileEdit className=\"h-3.5 w-3.5 text-blue-500\" />,\n create: <FilePlus className=\"h-3.5 w-3.5 text-green-500\" />,\n upload: <Upload className=\"h-3.5 w-3.5 text-purple-500\" />,\n copy: <Copy className=\"h-3.5 w-3.5 text-muted-foreground\" />,\n move: <Move className=\"h-3.5 w-3.5 text-orange-500\" />,\n};\n\nfunction formatTimeAgo(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n if (seconds < 60) return 'just now';\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n return `${Math.floor(hours / 24)}d ago`;\n}\n\nconst ActionItem: React.FC<{ action: ActionRecord; model: ActionNotificationModel }> = observer(({ action, model }) => {\n const [undoing, setUndoing] = useState(false);\n\n const handleUndo = async () => {\n if (!action.undo || action.undone || undoing) return;\n setUndoing(true);\n try {\n await action.undo();\n model.markUndone(action.id);\n } catch {\n // Error toast is handled by showActionToast\n } finally {\n setUndoing(false);\n }\n };\n\n return (\n <div className={`flex items-start gap-2 px-3 py-2 text-xs ${action.undone ? 'opacity-50' : ''}`}>\n <span className=\"mt-0.5 flex-shrink-0\">{ACTION_ICONS[action.type]}</span>\n <div className=\"flex-1 min-w-0\">\n <p className={`text-foreground truncate ${action.undone ? 'line-through' : ''}`} title={action.description}>\n {action.description}\n </p>\n <p className=\"text-muted-foreground mt-0.5\">{formatTimeAgo(action.timestamp)}</p>\n </div>\n {action.undo && !action.undone && (\n <button\n onClick={handleUndo}\n disabled={undoing}\n className=\"flex-shrink-0 p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n title=\"Undo\"\n >\n <Undo2 className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </div>\n );\n});\n\ninterface ActionHistoryPanelProps {\n model: ActionNotificationModel;\n}\n\nexport const ActionHistoryPanel: React.FC<ActionHistoryPanelProps> = observer(({ model }) => {\n const [open, setOpen] = useState(false);\n const panelRef = useRef<HTMLDivElement>(null);\n const actions = model.recentActions;\n\n // Close on click outside\n useEffect(() => {\n if (!open) return;\n const handleClick = (e: MouseEvent) => {\n if (panelRef.current && !panelRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClick);\n return () => document.removeEventListener('mousedown', handleClick);\n }, [open]);\n\n return (\n <div className=\"relative\" ref={panelRef}>\n <button\n onClick={() => setOpen(!open)}\n className=\"relative p-1.5 rounded-md hover:bg-background/80 text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Action history\"\n >\n <History className=\"h-4 w-4\" />\n {actions.length > 0 && (\n <span className=\"absolute -top-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-primary text-primary-foreground text-[9px] font-medium flex items-center justify-center\">\n {actions.length > 9 ? '9+' : actions.length}\n </span>\n )}\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full mt-1 w-72 rounded-lg border border-border bg-card shadow-lg z-50 animate-in fade-in slide-in-from-top-1 duration-150\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border\">\n <span className=\"text-xs font-medium text-foreground\">Recent Actions</span>\n <div className=\"flex items-center gap-1\">\n {actions.length > 0 && (\n <button\n onClick={() => model.clear()}\n className=\"text-[10px] text-muted-foreground hover:text-foreground px-1.5 py-0.5 rounded hover:bg-muted transition-colors\"\n >\n Clear\n </button>\n )}\n <button\n onClick={() => setOpen(false)}\n className=\"p-0.5 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n </div>\n\n <div className=\"max-h-64 overflow-y-auto divide-y divide-border\">\n {actions.length === 0 ? (\n <div className=\"px-3 py-6 text-center text-xs text-muted-foreground\">\n No actions yet\n </div>\n ) : (\n actions.map((action) => (\n <ActionItem key={action.id} action={action} model={model} />\n ))\n )}\n </div>\n </div>\n )}\n </div>\n );\n});\n","import React, { Suspense } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { ArrowLeft } from 'lucide-react';\nimport { PathBreadcrumb } from '@anymux/ui/components/path-breadcrumb';\nimport { LoadingSpinner } from '@anymux/ui/components/loading-spinner';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport { CapabilityContent, CAPABILITY_LABELS, ScopeWarningBanner } from './CapabilityPanel';\nimport { ActionHistoryPanel } from './ActionHistoryPanel';\n\nconst REPO_SERVICES = new Set(['github', 'gitlab', 'bitbucket', 'gitea']);\n\ninterface FullScreenBrowserProps {\n dashboardModel: DashboardModel;\n connectionManager: ConnectionManagerModel;\n}\n\nexport const FullScreenBrowser: React.FC<FullScreenBrowserProps> = observer(\n ({ dashboardModel, connectionManager }) => {\n const cell = dashboardModel.selectedCell;\n if (!cell) return null;\n\n const { serviceId, capabilityId } = cell;\n const service = dashboardModel.selectedService;\n if (!service) return null;\n\n const selectedRepo = dashboardModel.getSelectedRepo(serviceId);\n const hasRepo = REPO_SERVICES.has(serviceId) && selectedRepo;\n const browserPath = dashboardModel.browserPath;\n\n return (\n <div className=\"flex flex-col h-full flex-1 min-h-0\">\n {/* Breadcrumb bar */}\n <div className=\"flex items-center gap-1 px-2 sm:px-4 py-2 bg-muted border-b border-border text-xs sm:text-sm flex-shrink-0 min-h-[40px]\">\n <div className=\"flex items-center gap-1 flex-1 min-w-0 flex-wrap\">\n <PathBreadcrumb\n path={browserPath}\n onNavigate={(path) => dashboardModel.setBrowserPath(path)}\n showHome={false}\n editable\n prefixSegments={[\n { label: 'Dashboard', onClick: () => dashboardModel.closePanel() },\n { label: service.name, onClick: () => dashboardModel.closePanel() },\n { label: CAPABILITY_LABELS[capabilityId], onClick: () => dashboardModel.setBrowserPath('/') },\n ...(hasRepo ? [{ label: `${selectedRepo.owner}/${selectedRepo.repo}` }] : []),\n ]}\n />\n {hasRepo && (\n <button\n onClick={() => dashboardModel.clearSelectedRepo(serviceId)}\n className=\"ml-1 px-1.5 py-0.5 text-[10px] font-medium rounded bg-muted hover:bg-muted/80 transition-colors\"\n >\n <span className=\"hidden sm:inline\">Change</span>\n <ArrowLeft className=\"h-3 w-3 sm:hidden\" />\n </button>\n )}\n </div>\n <ActionHistoryPanel model={dashboardModel.actionNotifications} />\n </div>\n\n {/* Scope warning */}\n <ScopeWarningBanner serviceId={serviceId} capabilityId={capabilityId} connectionManager={connectionManager} />\n\n {/* Browser content */}\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n <Suspense\n fallback={\n <div className=\"flex items-center justify-center h-64\">\n <LoadingSpinner label=\"Loading...\" />\n </div>\n }\n >\n <CapabilityContent\n serviceId={serviceId}\n capabilityId={capabilityId}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n </Suspense>\n </div>\n </div>\n );\n }\n);\n","import React from 'react';\nimport { observer } from 'mobx-react-lite';\nimport type { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport type { DashboardModel } from '../models/DashboardModel';\nimport { serviceRegistry } from '../registry/service-registry';\nimport { ServiceRow } from './ServiceRow';\nimport { ServiceCard } from './ServiceCard';\nimport { FullScreenBrowser } from './FullScreenBrowser';\n\nconst COLUMN_HEADERS = [\n { label: 'Service', className: 'text-left' },\n { label: 'Status', className: 'text-left' },\n { label: 'FS', className: 'text-center' },\n { label: 'Obj Storage', className: 'text-center' },\n { label: 'Git', className: 'text-center' },\n { label: 'Media', className: 'text-center' },\n { label: 'Contacts', className: 'text-center' },\n { label: 'Calendar', className: 'text-center' },\n];\n\ninterface ServiceDashboardProps {\n connectionManager: ConnectionManagerModel;\n dashboardModel: DashboardModel;\n}\n\nexport const ServiceDashboard: React.FC<ServiceDashboardProps> = observer(\n ({ connectionManager, dashboardModel }) => {\n const services = serviceRegistry.getAll();\n\n if (dashboardModel.panelOpen && dashboardModel.selectedCell) {\n return (\n <div className=\"animate-in fade-in duration-200 h-full flex flex-col min-h-0\">\n <FullScreenBrowser dashboardModel={dashboardModel} connectionManager={connectionManager} />\n </div>\n );\n }\n\n return (\n <div>\n {/* Desktop view — table */}\n <div className=\"hidden md:block\">\n <div className=\"overflow-x-auto rounded-lg border border-border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-muted border-b border-border\">\n {COLUMN_HEADERS.map((col) => (\n <th\n key={col.label}\n className={`px-3 py-2 text-xs font-medium text-muted-foreground uppercase tracking-wider ${col.className}`}\n >\n {col.label}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"bg-card divide-y divide-border\">\n {services.map((service) => (\n <ServiceRow\n key={service.id}\n service={service}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n\n {/* Mobile: card layout (always) */}\n <div className=\"md:hidden space-y-3\">\n {services.map((service) => (\n <ServiceCard\n key={service.id}\n service={service}\n connectionManager={connectionManager}\n dashboardModel={dashboardModel}\n />\n ))}\n </div>\n </div>\n );\n }\n);\n","import React from 'react';\nimport { StatusIndicator } from '@anymux/ui/components/status-indicator';\nimport { match } from 'ts-pattern';\nimport type { ConnectionStatus as Status } from '../types/connection';\n\ninterface ConnectionStatusProps {\n status: Status;\n}\n\nexport const ConnectionStatusIndicator: React.FC<ConnectionStatusProps> = ({ status }) => {\n const config = match(status)\n .with('disconnected', () => ({ status: 'neutral' as const, label: 'Disconnected' }))\n .with('connecting', () => ({ status: 'warning' as const, label: 'Connecting...', pulse: true }))\n .with('connected', () => ({ status: 'success' as const, label: 'Connected' }))\n .with('expired', () => ({ status: 'warning' as const, label: 'Expired' }))\n .with('error', () => ({ status: 'error' as const, label: 'Error' }))\n .with('not_configured', () => ({ status: 'neutral' as const, label: 'Not Configured' }))\n .with('loading', () => ({ status: 'neutral' as const, label: 'Loading...' }))\n .exhaustive();\n\n return (\n <StatusIndicator\n status={config.status}\n label={config.label}\n pulse={config.status === 'warning' && status === 'connecting'}\n />\n );\n};\n","import React, { useState, useEffect, useMemo } from 'react';\nimport { Search, Lock, Loader2 } from 'lucide-react';\n\ninterface GitHubRepoPickerProps {\n accessToken: string;\n onSelectRepo: (repo: { owner: string; repo: string }) => void;\n}\n\ninterface GitHubRepoInfo {\n full_name: string;\n description: string | null;\n language: string | null;\n private: boolean;\n stargazers_count: number;\n}\n\nexport function GitHubRepoPicker({ accessToken, onSelectRepo }: GitHubRepoPickerProps) {\n const [repos, setRepos] = useState<GitHubRepoInfo[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [search, setSearch] = useState('');\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchRepos() {\n setLoading(true);\n setError(null);\n try {\n const res = await fetch('https://api.github.com/user/repos?sort=updated&per_page=50', {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);\n const data: GitHubRepoInfo[] = await res.json();\n if (!cancelled) setRepos(data);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to fetch repos');\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n fetchRepos();\n return () => { cancelled = true; };\n }, [accessToken]);\n\n const filtered = useMemo(\n () => repos.filter((r) => r.full_name.toLowerCase().includes(search.toLowerCase())),\n [repos, search],\n );\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-8 text-gray-400\">\n <Loader2 className=\"h-5 w-5 animate-spin mr-2\" />\n Loading repositories...\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"rounded-lg border border-red-800 bg-red-950/50 p-4 text-red-300 text-sm\">\n {error}\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col gap-2\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500\" />\n <input\n type=\"text\"\n placeholder=\"Search repositories...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"w-full rounded-md border border-gray-700 bg-gray-900 py-2 pl-9 pr-3 text-sm text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500\"\n />\n </div>\n <div className=\"max-h-64 overflow-y-auto rounded-md border border-gray-700\">\n {filtered.length === 0 ? (\n <div className=\"px-4 py-6 text-center text-sm text-gray-500\">No repositories found</div>\n ) : (\n filtered.map((repo) => {\n const parts = repo.full_name.split('/');\n const owner = parts[0] ?? '';\n const name = parts[1] ?? '';\n return (\n <button\n key={repo.full_name}\n type=\"button\"\n onClick={() => onSelectRepo({ owner, repo: name })}\n className=\"flex w-full items-start gap-3 border-b border-gray-800 px-4 py-3 text-left hover:bg-gray-800/60 last:border-b-0 transition-colors\"\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-blue-400 truncate\" title={repo.full_name}>\n {repo.full_name}\n </span>\n {repo.private && (\n <Lock className=\"h-3 w-3 flex-shrink-0 text-gray-500\" />\n )}\n </div>\n {repo.description && (\n <p className=\"mt-0.5 text-xs text-gray-500 truncate\" title={repo.description}>\n {repo.description.length > 80\n ? `${repo.description.slice(0, 80)}...`\n : repo.description}\n </p>\n )}\n </div>\n {repo.language && (\n <span className=\"flex-shrink-0 rounded-full bg-gray-800 px-2 py-0.5 text-xs text-gray-400\">\n {repo.language}\n </span>\n )}\n </button>\n );\n })\n )}\n </div>\n </div>\n );\n}\n","import React, { useEffect, useMemo } from 'react';\nimport { observer } from 'mobx-react-lite';\nimport { ConnectionManagerModel } from '../models/ConnectionManagerModel';\nimport { DashboardModel } from '../models/DashboardModel';\nimport { ServiceDashboard } from '../components/ServiceDashboard';\nimport { createConnectAuthClient } from '../auth/auth-client';\nimport { serviceRegistry } from '../registry/service-registry';\n\nexport interface ServiceDashboardDemoProps {\n authBaseURL: string;\n /** Initial service to open (from URL) */\n initialService?: string;\n /** Initial capability to open (from URL) */\n initialCapability?: string;\n /** Initial file browser path (from URL) */\n initialPath?: string;\n /** Callback when selected cell changes (for URL sync) */\n onCellChange?: (cell: { serviceId: string; capabilityId: string } | null) => void;\n /** Callback when file browser path changes (for URL sync) */\n onPathChange?: (path: string) => void;\n}\n\nexport const ServiceDashboardDemo: React.FC<ServiceDashboardDemoProps> = observer(({ authBaseURL, initialService, initialCapability, initialPath, onCellChange, onPathChange }) => {\n const connectionManager = useMemo(() => {\n const authClient = createConnectAuthClient(authBaseURL);\n return new ConnectionManagerModel({ authClient });\n }, [authBaseURL]);\n\n const dashboardModel = useMemo(() => {\n const model = new DashboardModel(connectionManager);\n // Restore panel state immediately from URL if service is already connected\n // (loadFromStorage runs synchronously in constructor, so tokens are available here)\n if (initialService && initialCapability && connectionManager.isConnected(initialService)) {\n if (initialPath) model.setBrowserPathSilent(initialPath);\n model.openCell(initialService, initialCapability as any);\n }\n return model;\n }, [connectionManager]);\n\n // Wire callbacks directly (no hooks needed — observer re-renders on prop change)\n dashboardModel.onCellChange = onCellChange;\n dashboardModel.onPathChange = onPathChange;\n\n // Initialize connection manager once\n useEffect(() => {\n connectionManager.initialize();\n }, [connectionManager]);\n\n // Bidirectional URL ↔ Model sync (handles browser back/forward, late connections, and cell selection)\n const isConnected = initialService ? connectionManager.isConnected(initialService) : false;\n useEffect(() => {\n // URL has no cell → close panel if open (browser back to dashboard)\n if (!initialService || !initialCapability) {\n if (dashboardModel.panelOpen) {\n dashboardModel.closePanelSilent();\n }\n return;\n }\n\n // Wait for connection manager to be ready and service connected\n if (!connectionManager.initialized || !isConnected) return;\n\n // URL has a cell — open panel if it doesn't match current model state\n const modelCell = dashboardModel.selectedCell;\n if (!dashboardModel.panelOpen ||\n modelCell?.serviceId !== initialService ||\n modelCell?.capabilityId !== initialCapability) {\n if (initialPath) dashboardModel.setBrowserPathSilent(initialPath);\n dashboardModel.openCell(initialService, initialCapability as any);\n }\n }, [initialService, initialCapability, initialPath, isConnected, connectionManager.initialized, dashboardModel]);\n\n if (connectionManager.configError) {\n return (\n <div className=\"p-4\">\n <div className=\"rounded-lg border border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950/30 p-4\">\n <p className=\"text-sm font-medium text-red-800 dark:text-red-300\">Dashboard cannot start</p>\n <p className=\"mt-1 text-sm text-red-600 dark:text-red-400\">{connectionManager.configError}</p>\n </div>\n </div>\n );\n }\n\n // Show banner when URL requests a service+capability that isn't connected\n const showNotConnectedBanner =\n initialService && initialCapability &&\n connectionManager.initialized &&\n !connectionManager.isConnected(initialService) &&\n !dashboardModel.panelOpen;\n const bannerService = showNotConnectedBanner ? serviceRegistry.get(initialService) : null;\n\n return (\n <div className={dashboardModel.panelOpen ? 'flex-1 flex flex-col min-h-0' : 'flex-1 min-h-0 overflow-auto p-4'}>\n {showNotConnectedBanner && bannerService && (\n <div className=\"mb-4 rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/30 p-4\">\n <p className=\"text-sm font-medium text-amber-800 dark:text-amber-300\">\n {bannerService.name} is not connected\n </p>\n <p className=\"mt-1 text-sm text-amber-600 dark:text-amber-400\">\n Click the <strong>Connect</strong> button next to {bannerService.name} in the table below to sign in.\n The browser will open automatically once connected.\n </p>\n </div>\n )}\n <ServiceDashboard connectionManager={connectionManager} dashboardModel={dashboardModel} />\n </div>\n );\n});\n"],"mappings":"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"}