@burdenoff/microfe-vibecontrols 2026.623.2 → 2026.623.3
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/components/actions/ActionTypeBadge.js +1 -1
- package/dist/components/actions/ActionTypeBadge.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js +1 -1
- package/dist/components/agent-manager/AgentManagerAdminBar.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js +1 -1
- package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js +1 -1
- package/dist/components/agent-manager/AgentManagerInputArea.js.map +1 -1
- package/dist/components/agent-manager/BridleConfigPanel.js +2 -2
- package/dist/components/agent-manager/BridleConfigPanel.js.map +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js +1 -1
- package/dist/components/agent-manager/ProfilesPanel.js.map +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js +1 -1
- package/dist/components/agent-manager/controls/AutonomySelector.js.map +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js +1 -1
- package/dist/components/agent-manager/controls/SdkSelector.js.map +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js +1 -1
- package/dist/components/agent-manager/controls/VoiceToggle.js.map +1 -1
- package/dist/components/agent-manager/detail/SessionLogsPanel.js +2 -2
- package/dist/components/agent-manager/detail/SessionLogsPanel.js.map +1 -1
- package/dist/components/ai/AIContextsTab.js +1 -1
- package/dist/components/ai/AIContextsTab.js.map +1 -1
- package/dist/components/ai/AIOverviewTab.js +1 -1
- package/dist/components/ai/AIOverviewTab.js.map +1 -1
- package/dist/components/ai/AIPromptsTab.js +1 -1
- package/dist/components/ai/AIPromptsTab.js.map +1 -1
- package/dist/components/ai/AIQueueTab.js +1 -1
- package/dist/components/ai/AIQueueTab.js.map +1 -1
- package/dist/components/ai/AISessionsTab.js +1 -1
- package/dist/components/ai/AISessionsTab.js.map +1 -1
- package/dist/components/audit/AuditLogTable.js +1 -1
- package/dist/components/audit/AuditLogTable.js.map +1 -1
- package/dist/components/docs/DocsPageTree.js +1 -1
- package/dist/components/docs/DocsPageTree.js.map +1 -1
- package/dist/components/graph/ConnectionPermissionsDialog.js +2 -2
- package/dist/components/graph/ConnectionPermissionsDialog.js.map +1 -1
- package/dist/components/graph/MeshDeployWizard.js +3 -3
- package/dist/components/graph/MeshDeployWizard.js.map +1 -1
- package/dist/components/graph/MeshRequestPanel.js +2 -2
- package/dist/components/graph/MeshRequestPanel.js.map +1 -1
- package/dist/components/notes/NoteCard.js +1 -1
- package/dist/components/notes/NoteCard.js.map +1 -1
- package/dist/components/notes/NoteEditor.js +1 -1
- package/dist/components/notes/NoteEditor.js.map +1 -1
- package/dist/components/notes/NoteList.js +1 -1
- package/dist/components/notes/NoteList.js.map +1 -1
- package/dist/components/shared/AgentSetupBanner.js +3 -3
- package/dist/components/shared/AgentSetupBanner.js.map +1 -1
- package/dist/components/templates/TemplateCard.js +1 -1
- package/dist/components/templates/TemplateCard.js.map +1 -1
- package/dist/components/vibecalendar/types.js +1 -1
- package/dist/components/vibecalendar/types.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js +1 -1
- package/dist/components/vibedeck/VibeDeckButtonForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckExecutionLog.js +2 -2
- package/dist/components/vibedeck/VibeDeckExecutionLog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js +1 -1
- package/dist/components/vibedeck/VibeDeckForm.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js +1 -1
- package/dist/components/vibedeck/VibeDeckGrid.js.map +1 -1
- package/dist/pages/AgentsPage.js +2 -2
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogDetailPage.js +1 -1
- package/dist/pages/CatalogDetailPage.js.map +1 -1
- package/dist/pages/ComponentDetailPage.js +1 -1
- package/dist/pages/ComponentDetailPage.js.map +1 -1
- package/dist/pages/OverviewPage.js +10 -10
- package/dist/pages/OverviewPage.js.map +1 -1
- package/dist/pages/PluginsCatalogPage.js +1 -1
- package/dist/pages/PluginsCatalogPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +3 -3
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/SharedSessionPage.js +2 -2
- package/dist/pages/SharedSessionPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +2 -2
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/TemplateDetailPage.js +2 -2
- package/dist/pages/TemplateDetailPage.js.map +1 -1
- package/dist/pages/VibesPage.js +2 -2
- package/dist/pages/VibesPage.js.map +1 -1
- package/dist/pages/WebhooksPage.js +2 -2
- package/dist/pages/WebhooksPage.js.map +1 -1
- package/dist/pages/ai/AISessionDetailPage.js +2 -2
- package/dist/pages/ai/AISessionDetailPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -211,7 +211,7 @@ function x() {
|
|
|
211
211
|
/* @__PURE__ */ y("div", {
|
|
212
212
|
className: "flex justify-center mb-6",
|
|
213
213
|
children: /* @__PURE__ */ y("div", {
|
|
214
|
-
className: `inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm ${R.permission === a.View ? "bg-bg-sunken text-text-secondary" : "bg-[var(--color-accent-soft)] text-
|
|
214
|
+
className: `inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm ${R.permission === a.View ? "bg-bg-sunken text-text-secondary" : "bg-[var(--color-accent-soft)] text-text-primary"}`,
|
|
215
215
|
children: R.permission === a.View ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(f, { className: "size-4" }), x("sharedSession.viewOnly", "View Only")] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "size-4" }), x("sharedSession.canControl", "Can Control")] })
|
|
216
216
|
})
|
|
217
217
|
}),
|
|
@@ -349,7 +349,7 @@ function x() {
|
|
|
349
349
|
}), /* @__PURE__ */ b("div", {
|
|
350
350
|
className: "flex items-center gap-3",
|
|
351
351
|
children: [/* @__PURE__ */ y("div", {
|
|
352
|
-
className: `inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${Q === a.View ? "bg-bg-sunken text-text-secondary" : "bg-[var(--color-accent-soft)] text-
|
|
352
|
+
className: `inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${Q === a.View ? "bg-bg-sunken text-text-secondary" : "bg-[var(--color-accent-soft)] text-text-primary"}`,
|
|
353
353
|
children: Q === a.View ? /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(f, { className: "size-3.5" }), x("sharedSession.viewOnly", "View Only")] }) : /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(p, { className: "size-3.5" }), x("sharedSession.interactive", "Interactive")] })
|
|
354
354
|
}), /* @__PURE__ */ b("div", {
|
|
355
355
|
className: "flex items-center gap-1.5 px-2.5 py-1 bg-bg-sunken rounded-full text-xs text-text-secondary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SharedSessionPage.js","names":[],"sources":["../../src/pages/SharedSessionPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Terminal,\n Loader2,\n AlertCircle,\n Lock,\n Eye,\n Gamepad2,\n Users,\n Clock,\n ArrowLeft,\n LogIn,\n} from 'lucide-react';\nimport {\n useValidateShareLinkQuery,\n useJoinSharedSessionMutation,\n useLeaveSharedSessionMutation,\n useRequestSessionControlMutation,\n useReleaseSessionControlMutation,\n useGetSessionCollaboratorsQuery,\n useCollaboratorJoinedSubscription,\n useCollaboratorLeftSubscription,\n useSessionControlChangedSubscription,\n type SessionCollaboratorFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { SessionSharePermission } from '@/generated/wspace-types';\nimport { SharedTerminalPanel } from '@/components/sessions/SharedTerminalPanel';\nimport { CollaboratorsList } from '@/components/sessions/CollaboratorsList';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\ninterface PasswordFormProps {\n onSubmit: (password: string) => void;\n isLoading: boolean;\n error?: string | null;\n}\n\n/**\n * Password entry form for protected share links\n */\nfunction PasswordForm({ onSubmit, isLoading, error }: PasswordFormProps) {\n const [password, setPassword] = useState('');\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (password.trim()) {\n onSubmit(password);\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4 w-full max-w-sm\">\n <div>\n <label\n htmlFor=\"shared-session-password\"\n className=\"block text-sm font-medium text-text-primary mb-2\"\n >\n This session is password protected\n </label>\n <div className=\"relative\">\n <Lock className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n aria-label=\"Enter password\"\n id=\"shared-session-password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Enter password\"\n className=\"w-full pl-10 pr-4 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n {error && (\n <div className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm text-status-error-text\">{error}</span>\n </div>\n )}\n <button\n type=\"submit\"\n disabled={isLoading || !password.trim()}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading ? (\n <span className=\"flex items-center justify-center gap-2\">\n <Loader2 className=\"size-4 animate-spin\" />\n Joining...\n </span>\n ) : (\n 'Join Session'\n )}\n </button>\n </form>\n );\n}\n\n/**\n * SharedSessionPage - Page for accessing shared terminal sessions via link\n *\n * URL: /share/:token\n *\n * Flow:\n * 1. Validate the share link token\n * 2. If password required, show password form\n * 3. Join the shared session\n * 4. Display terminal with collaboration UI\n */\nexport function SharedSessionPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { token } = useParams<{ token: string }>();\n const { currentUser } = useVibeControls();\n // State\n const [joined, setJoined] = useState(false);\n const [sessionId, setSessionId] = useState<string | null>(null);\n const [password, setPassword] = useState('');\n const [passwordError, setPasswordError] = useState<string | null>(null);\n const [joinError, setJoinError] = useState<string | null>(null);\n const [hasLeft, setHasLeft] = useState(false);\n const joiningRef = useRef(false);\n const hasJoinedRef = useRef(false);\n\n // Get currentUserId from auth context\n const currentUserId = currentUser?.id ?? '';\n\n // Validate the share link\n const {\n data: validationData,\n loading: validating,\n error: validationError,\n refetch: refetchValidation,\n } = useValidateShareLinkQuery({\n variables: { token: token || '' },\n skip: !token,\n });\n\n const validation = validationData?.validateShareLink;\n\n // Mutations\n const [joinSession, { loading: joining }] = useJoinSharedSessionMutation();\n const [leaveSession] = useLeaveSharedSessionMutation();\n const [requestControl, { loading: requestingControl }] = useRequestSessionControlMutation();\n const [releaseControl, { loading: releasingControl }] = useReleaseSessionControlMutation();\n\n // Get collaborators once joined\n const { data: collaboratorsData, refetch: refetchCollaborators } =\n useGetSessionCollaboratorsQuery({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n // Subscription handles the primary signal (collaboratorJoined /\n // collaboratorLeft). Poll is a slow safety-net to recover if the\n // SSE stream drops silently — 60s is enough since presence flips\n // are pushed through the subscription.\n pollInterval: 60_000,\n });\n\n const collaborators = collaboratorsData?.sessionCollaborators || [];\n\n // Real-time subscriptions for collaboration events\n const { data: joinedData } = useCollaboratorJoinedSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n const { data: leftData } = useCollaboratorLeftSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n const { data: controlChangedData } = useSessionControlChangedSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n // Refetch collaborators when subscription events arrive\n useEffect(() => {\n if (joinedData || leftData || controlChangedData) {\n refetchCollaborators();\n }\n }, [joinedData, leftData, controlChangedData, refetchCollaborators]);\n\n // Cleanup: Leave session only on unmount, and only if we actually joined\n useEffect(() => {\n return () => {\n if (hasJoinedRef.current && sessionId) {\n leaveSession({ variables: { sessionId } }).catch(console.error);\n }\n };\n }, [sessionId, leaveSession]);\n\n const handleJoinSession = useCallback(\n async (passwordInput?: string) => {\n if (!token || joiningRef.current) return;\n\n joiningRef.current = true;\n const pwd = passwordInput || password;\n setPasswordError(null);\n setJoinError(null);\n\n try {\n const result = await joinSession({\n variables: {\n input: {\n shareToken: token,\n password: pwd || undefined,\n },\n },\n });\n\n if (result.data?.joinSharedSession) {\n hasJoinedRef.current = true;\n setJoined(true);\n setSessionId(result.data.joinSharedSession.sessionId);\n void refetchValidation();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to join session';\n if (message.toLowerCase().includes('password')) {\n setPasswordError('Incorrect password');\n } else {\n setPasswordError(message);\n setJoinError(message);\n }\n } finally {\n joiningRef.current = false;\n }\n },\n [joinSession, password, refetchValidation, token]\n );\n\n // Auto-join if no password required and link is valid\n useEffect(() => {\n if (\n validation?.isValid &&\n !validation.requiresPassword &&\n !joined &&\n !hasLeft &&\n !joiningRef.current &&\n token\n ) {\n // Set sessionId early from validation so collaborators query can start\n if (validation.sessionId && !sessionId) {\n setSessionId(validation.sessionId);\n }\n void handleJoinSession();\n }\n }, [\n validation?.isValid,\n validation?.requiresPassword,\n validation?.sessionId,\n joined,\n hasLeft,\n token,\n sessionId,\n handleJoinSession,\n ]);\n\n const handleRequestControl = useCallback(async () => {\n if (!sessionId) return;\n try {\n await requestControl({ variables: { sessionId } });\n refetchCollaborators();\n } catch (err) {\n console.error('Failed to request control:', err);\n }\n }, [sessionId, requestControl, refetchCollaborators]);\n\n const handleReleaseControl = useCallback(async () => {\n if (!sessionId) return;\n try {\n await releaseControl({ variables: { sessionId } });\n refetchCollaborators();\n } catch (err) {\n console.error('Failed to release control:', err);\n }\n }, [sessionId, releaseControl, refetchCollaborators]);\n\n const handleLeaveSession = useCallback(async () => {\n if (!sessionId) return;\n try {\n await leaveSession({ variables: { sessionId } });\n hasJoinedRef.current = false; // Prevent cleanup from firing a second leave\n setHasLeft(true);\n setJoined(false);\n setSessionId(null);\n window.history.back();\n } catch (err) {\n console.error('Failed to leave session:', err);\n }\n }, [sessionId, leaveSession]);\n\n // Determine current user's collaborator entry\n const currentCollaborator = collaborators.find(\n (c: SessionCollaboratorFieldsFragment) => c.userId === currentUserId\n );\n const permission = validation?.permission || SessionSharePermission.View;\n\n // Stabilize terminal URL to prevent iframe reloads on parent re-render\n const terminalUrl = useMemo(() => validation?.ttydUrl || null, [validation?.ttydUrl]);\n\n // Loading state\n if (validating) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"size-8 text-action-primary-bg animate-spin mx-auto mb-4\" />\n <p className=\"text-text-secondary\">\n {tr('sharedSession.validating', 'Validating share link...')}\n </p>\n </div>\n </div>\n );\n }\n\n // Error state\n if (validationError || !validation) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-error-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-error-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.unableToLoad', 'Unable to Load Session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {validationError?.message ||\n tr('sharedSession.failedValidate', 'Failed to validate the share link')}\n </p>\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n // Invalid link\n if (!validation.isValid) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-warning-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.invalidExpiredLink', 'Invalid or Expired Link')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {validation.error ||\n tr('sharedSession.linkNoLongerValid', 'This share link is no longer valid.')}\n </p>\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n // Password required - show password form\n if (validation.requiresPassword && !joined) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"w-full max-w-md\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card p-6 shadow-elevation-1\">\n {/* Header */}\n <div className=\"text-center mb-6\">\n <div className=\"size-12 bg-[var(--color-accent-soft)] rounded-full flex items-center justify-center mx-auto mb-4\">\n <Terminal className=\"size-6 text-action-primary-bg\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {validation.sessionName || 'Shared Session'}\n </h2>\n {validation.ownerName && (\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('sharedSession.sharedBy', 'Shared by')} {validation.ownerName}\n </p>\n )}\n </div>\n\n {/* Permission badge */}\n <div className=\"flex justify-center mb-6\">\n <div\n className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm ${\n validation.permission === SessionSharePermission.View\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-action-primary-bg'\n }`}\n >\n {validation.permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-4\" />\n {tr('sharedSession.viewOnly', 'View Only')}\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-4\" />\n {tr('sharedSession.canControl', 'Can Control')}\n </>\n )}\n </div>\n </div>\n\n {/* Expiry info */}\n {validation.expiresAt && (\n <div className=\"flex items-center justify-center gap-2 mb-6 text-xs text-text-secondary\">\n <Clock className=\"size-3.5\" />\n <span>\n {tr('sharedSession.expires', 'Expires')}{' '}\n {new Date(validation.expiresAt).toLocaleString()}\n </span>\n </div>\n )}\n\n {/* Password form */}\n <PasswordForm\n onSubmit={(pwd) => {\n setPassword(pwd);\n handleJoinSession(pwd);\n }}\n isLoading={joining}\n error={passwordError}\n />\n </div>\n </div>\n </div>\n );\n }\n\n // Post-leave state — user explicitly left, don't show join spinner\n if (hasLeft) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-bg-sunken rounded-full flex items-center justify-center mx-auto mb-4\">\n <LogIn className=\"size-8 text-text-secondary\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.youLeft', 'You left the session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {tr('sharedSession.rejoinHint', 'You can rejoin using the same share link.')}\n </p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setHasLeft(false);\n setJoinError(null);\n handleJoinSession();\n }}\n disabled={joining}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <LogIn className=\"size-4\" />\n {tr('sharedSession.rejoin', 'Rejoin')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Joining state - show error if join failed, otherwise spinner\n if (!joined && !validation.requiresPassword) {\n if (joinError) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-error-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-error-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.failedJoin', 'Failed to Join Session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">{joinError}</p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setJoinError(null);\n handleJoinSession();\n }}\n disabled={joining}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <LogIn className=\"size-4\" />\n {tr('sharedSession.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"size-8 text-action-primary-bg animate-spin mx-auto mb-4\" />\n <p className=\"text-text-secondary\">{tr('sharedSession.joining', 'Joining session...')}</p>\n </div>\n </div>\n );\n }\n\n // Main shared session view\n return (\n <MotionPage className=\"min-h-screen bg-bg-canvas flex flex-col\">\n {/* Header */}\n <header className=\"bg-bg-surface border-b border-border-default px-4 py-3\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 max-w-screen-2xl mx-auto\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={handleLeaveSession}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n title=\"Leave session\"\n aria-label=\"Leave session\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-[var(--color-accent-soft)] rounded-lg\">\n <Terminal className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <h1 className=\"font-semibold text-text-primary\">\n {validation.sessionName || 'Shared Session'}\n </h1>\n {validation.ownerName && (\n <p className=\"text-xs text-text-secondary\">\n {tr('sharedSession.sharedBy', 'Shared by')} {validation.ownerName}\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Permission and collaborator count */}\n <div className=\"flex items-center gap-3\">\n <div\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${\n permission === SessionSharePermission.View\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-action-primary-bg'\n }`}\n >\n {permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-3.5\" />\n {tr('sharedSession.viewOnly', 'View Only')}\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-3.5\" />\n {tr('sharedSession.interactive', 'Interactive')}\n </>\n )}\n </div>\n <div className=\"flex items-center gap-1.5 px-2.5 py-1 bg-bg-sunken rounded-full text-xs text-text-secondary\">\n <Users className=\"size-3.5\" />\n <span>{collaborators.length}</span>\n </div>\n </div>\n </div>\n </header>\n\n {/* Main content */}\n <div className=\"flex-1 flex\">\n {/* Terminal area */}\n <div className=\"flex-1 flex flex-col\">\n <SharedTerminalPanel\n isShared={true}\n collaborators={collaborators}\n currentUserId={currentUserId}\n permission={permission}\n onRequestControl={handleRequestControl}\n onReleaseControl={handleReleaseControl}\n isRequestingControl={requestingControl}\n isReleasingControl={releasingControl}\n className=\"flex-1\"\n >\n {/* Terminal iframe - in production this would be the ttyd URL */}\n <div className=\"w-full h-full bg-bg-inverse text-text-inverse flex items-center justify-center\">\n {terminalUrl ? (\n <iframe\n src={terminalUrl}\n title=\"Shared Terminal\"\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n sandbox=\"allow-scripts allow-same-origin allow-forms\"\n />\n ) : (\n <div className=\"text-center\">\n <Terminal className=\"size-12 text-text-secondary mx-auto mb-4\" />\n <p className=\"text-text-tertiary\">\n {tr('sharedSession.terminalLoading', 'Terminal loading...')}\n </p>\n <p className=\"text-text-secondary text-sm mt-1\">Session ID: {sessionId}</p>\n </div>\n )}\n </div>\n </SharedTerminalPanel>\n </div>\n\n {/* Sidebar - Collaborators */}\n <aside className=\"w-72 bg-bg-surface border-l border-border-default p-4 hidden lg:block\">\n <CollaboratorsList\n collaborators={collaborators}\n currentUserId={currentUserId}\n onRequestControl={\n permission === SessionSharePermission.Interact ? handleRequestControl : undefined\n }\n isRequestingControl={requestingControl}\n />\n\n {/* Session info */}\n <div className=\"mt-6 pt-6 border-t border-border-default\">\n <h4 className=\"text-xs font-medium text-text-secondary uppercase tracking-wide mb-3\">\n {tr('sharedSession.sessionInfo', 'Session Info')}\n </h4>\n <div className=\"space-y-2 text-sm\">\n {validation.expiresAt && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">\n {tr('sharedSession.expires', 'Expires')}\n </span>\n <span className=\"text-text-primary\">\n {new Date(validation.expiresAt).toLocaleTimeString()}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">\n {tr('sharedSession.yourRole', 'Your role')}\n </span>\n <span className=\"text-text-primary capitalize\">\n {currentCollaborator?.role?.toLowerCase() || tr('sharedSession.viewer', 'Viewer')}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">{tr('sharedSession.status', 'Status')}</span>\n <span className=\"text-status-success-text\">\n {tr('sharedSession.connected', 'Connected')}\n </span>\n </div>\n </div>\n </div>\n </aside>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;AA0CA,SAAS,GAAa,EAAE,aAAU,cAAW,YAA4B;CACvE,IAAM,CAAC,GAAU,KAAe,EAAS,GAAG;AAS5C,QACE,kBAAC,QAAD;EAAM,WARc,MAAuB;AAE3C,GADA,EAAE,gBAAgB,EACd,EAAS,MAAM,IACjB,EAAS,EAAS;;EAKU,WAAU;YAAxC;GACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;IACE,SAAQ;IACR,WAAU;cACX;IAEO,CAAA,EACR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAM,WAAU,uEAAwE,CAAA,EACxF,kBAAC,SAAD;KACE,cAAW;KACX,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;KAC5C,aAAY;KACZ,WAAU;KACV,CAAA,CACE;MACF,EAAA,CAAA;GACL,KACC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAa,WAAU,iCAAkC,CAAA,EACzD,kBAAC,QAAD;KAAM,WAAU;eAAkC;KAAa,CAAA,CAC3D;;GAER,kBAAC,UAAD;IACE,MAAK;IACL,UAAU,KAAa,CAAC,EAAS,MAAM;IACvC,WAAU;cAET,IACC,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,EAAA,aAEtC;SAEP;IAEK,CAAA;GACJ;;;AAeX,SAAgB,IAAoB;CAClC,IAAM,EAAE,SAAM,IAAS,EACjB,IAAK,GACR,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAE,CACJ,EACK,EAAE,aAAU,IAA8B,EAC1C,EAAE,oBAAgB,IAAiB,EAEnC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,GAAU,MAAe,EAAS,GAAG,EACtC,CAAC,IAAe,KAAoB,EAAwB,KAAK,EACjE,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,IAAa,EAAO,GAAM,EAC1B,IAAe,EAAO,GAAM,EAG5B,IAAgB,IAAa,MAAM,IAGnC,EACJ,MAAM,IACN,SAAS,IACT,OAAO,GACP,SAAS,MACP,GAA0B;EAC5B,WAAW,EAAE,OAAO,KAAS,IAAI;EACjC,MAAM,CAAC;EACR,CAAC,EAEI,IAAa,IAAgB,mBAG7B,CAAC,GAAa,EAAE,SAAS,OAAa,GAA8B,EACpE,CAAC,KAAgB,GAA+B,EAChD,CAAC,GAAgB,EAAE,SAAS,OAAuB,IAAkC,EACrF,CAAC,GAAgB,EAAE,SAAS,QAAsB,IAAkC,EAGpF,EAAE,MAAM,IAAmB,SAAS,MACxC,EAAgC;EAC9B,WAAW,EAAE,WAAW,KAAa,IAAI;EACzC,MAAM,CAAC,KAAa,CAAC;EAKrB,cAAc;EACf,CAAC,EAEE,IAAgB,IAAmB,wBAAwB,EAAE,EAG7D,EAAE,MAAM,MAAe,EAAkC;EAC7D,WAAW,EAAE,WAAW,KAAa,IAAI;EACzC,MAAM,CAAC,KAAa,CAAC;EACtB,CAAC,EAEI,EAAE,MAAM,MAAa,EAAgC;EACzD,WAAW,EAAE,WAAW,KAAa,IAAI;EACzC,MAAM,CAAC,KAAa,CAAC;EACtB,CAAC,EAEI,EAAE,MAAM,MAAuB,GAAqC;EACxE,WAAW,EAAE,WAAW,KAAa,IAAI;EACzC,MAAM,CAAC,KAAa,CAAC;EACtB,CAAC;AAUF,CAPA,QAAgB;AACd,GAAI,KAAc,KAAY,MAC5B,GAAsB;IAEvB;EAAC;EAAY;EAAU;EAAoB;EAAqB,CAAC,EAGpE,cACe;AACX,EAAI,EAAa,WAAW,KAC1B,EAAa,EAAE,WAAW,EAAE,cAAW,EAAE,CAAC,CAAC,MAAM,QAAQ,MAAM;IAGlE,CAAC,GAAW,EAAa,CAAC;CAE7B,IAAM,IAAoB,EACxB,OAAO,MAA2B;AAChC,MAAI,CAAC,KAAS,EAAW,QAAS;AAElC,IAAW,UAAU;EACrB,IAAM,IAAM,KAAiB;AAE7B,EADA,EAAiB,KAAK,EACtB,EAAa,KAAK;AAElB,MAAI;GACF,IAAM,IAAS,MAAM,EAAY,EAC/B,WAAW,EACT,OAAO;IACL,YAAY;IACZ,UAAU,KAAO,KAAA;IAClB,EACF,EACF,CAAC;AAEF,GAAI,EAAO,MAAM,sBACf,EAAa,UAAU,IACvB,EAAU,GAAK,EACf,EAAa,EAAO,KAAK,kBAAkB,UAAU,EAChD,GAAmB;WAEnB,GAAK;GACZ,IAAM,IAAU,aAAe,QAAQ,EAAI,UAAU;AACrD,GAAI,EAAQ,aAAa,CAAC,SAAS,WAAW,GAC5C,EAAiB,qBAAqB,IAEtC,EAAiB,EAAQ,EACzB,EAAa,EAAQ;YAEf;AACR,KAAW,UAAU;;IAGzB;EAAC;EAAa;EAAU;EAAmB;EAAM,CAClD;AAGD,SAAgB;AACd,EACE,GAAY,WACZ,CAAC,EAAW,oBACZ,CAAC,KACD,CAAC,KACD,CAAC,EAAW,WACZ,MAGI,EAAW,aAAa,CAAC,KAC3B,EAAa,EAAW,UAAU,EAE/B,GAAmB;IAEzB;EACD,GAAY;EACZ,GAAY;EACZ,GAAY;EACZ;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAM,IAAuB,EAAY,YAAY;AAC9C,QACL,KAAI;AAEF,GADA,MAAM,EAAe,EAAE,WAAW,EAAE,cAAW,EAAE,CAAC,EAClD,GAAsB;WACf,GAAK;AACZ,WAAQ,MAAM,8BAA8B,EAAI;;IAEjD;EAAC;EAAW;EAAgB;EAAqB,CAAC,EAE/C,KAAuB,EAAY,YAAY;AAC9C,QACL,KAAI;AAEF,GADA,MAAM,EAAe,EAAE,WAAW,EAAE,cAAW,EAAE,CAAC,EAClD,GAAsB;WACf,GAAK;AACZ,WAAQ,MAAM,8BAA8B,EAAI;;IAEjD;EAAC;EAAW;EAAgB;EAAqB,CAAC,EAE/C,KAAqB,EAAY,YAAY;AAC5C,QACL,KAAI;AAMF,GALA,MAAM,EAAa,EAAE,WAAW,EAAE,cAAW,EAAE,CAAC,EAChD,EAAa,UAAU,IACvB,EAAW,GAAK,EAChB,EAAU,GAAM,EAChB,EAAa,KAAK,EAClB,OAAO,QAAQ,MAAM;WACd,GAAK;AACZ,WAAQ,MAAM,4BAA4B,EAAI;;IAE/C,CAAC,GAAW,EAAa,CAAC,EAGvB,KAAsB,EAAc,MACvC,MAAyC,EAAE,WAAW,EACxD,EACK,IAAa,GAAY,cAAc,EAAuB,MAG9D,IAAc,SAAc,GAAY,WAAW,MAAM,CAAC,GAAY,QAAQ,CAAC;AA2OrF,QAxOI,KAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAS,WAAU,2DAA4D,CAAA,EAC/E,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,4BAA4B,2BAA2B;IACzD,CAAA,CACA;;EACF,CAAA,GAKN,KAAmB,CAAC,IAEpB,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAa,WAAU,iCAAkC,CAAA;KACrD,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,8BAA8B,yBAAyB;KACxD,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,GAAiB,WAChB,EAAG,gCAAgC,oCAAoC;KACvE,CAAA;IACJ,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,OAAO,QAAQ,MAAM;KACpC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,wBAAwB,UAAU,CAC/B;;IACL;;EACF,CAAA,GAKL,EAAW,UA4BZ,EAAW,oBAAoB,CAAC,IAEhC,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;QAClD,CAAA;OACN,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAW,eAAe;QACxB,CAAA;OACJ,EAAW,aACV,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAG,0BAA0B,YAAY;SAAC;SAAE,EAAW;SACtD;;OAEF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAW,qEACT,EAAW,eAAe,EAAuB,OAC7C,qCACA;iBAGL,EAAW,eAAe,EAAuB,OAChD,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAK,WAAU,UAAW,CAAA,EACzB,EAAG,0BAA0B,YAAY,CACzC,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,EAAG,4BAA4B,cAAc,CAC7C,EAAA,CAAA;OAED,CAAA;MACF,CAAA;KAGL,EAAW,aACV,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD,EAAO,WAAU,YAAa,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAA;OACG,EAAG,yBAAyB,UAAU;OAAE;OACxC,IAAI,KAAK,EAAW,UAAU,CAAC,gBAAgB;OAC3C,EAAA,CAAA,CACH;;KAIR,kBAAC,IAAD;MACE,WAAW,MAAQ;AAEjB,OADA,GAAY,EAAI,EAChB,EAAkB,EAAI;;MAExB,WAAW;MACX,OAAO;MACP,CAAA;KACE;;GACF,CAAA;EACF,CAAA,GAKN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAO,WAAU,8BAA+B,CAAA;KAC5C,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,yBAAyB,uBAAuB;KACjD,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,4BAA4B,4CAA4C;KAC1E,CAAA;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,OAAO,QAAQ,MAAM;MACpC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,wBAAwB,UAAU,CAC/B;SACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe;AAGb,OAFA,EAAW,GAAM,EACjB,EAAa,KAAK,EAClB,GAAmB;;MAErB,UAAU;MACV,WAAU;gBARZ,CAUE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAG,wBAAwB,SAAS,CAC9B;QACL;;IACF;;EACF,CAAA,GAKN,CAAC,KAAU,CAAC,EAAW,mBACrB,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAa,WAAU,iCAAkC,CAAA;KACrD,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,4BAA4B,yBAAyB;KACtD,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAA4B;KAAc,CAAA;IACvD,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,OAAO,QAAQ,MAAM;MACpC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,wBAAwB,UAAU,CAC/B;SACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe;AAEb,OADA,EAAa,KAAK,EAClB,GAAmB;;MAErB,UAAU;MACV,WAAU;gBAPZ,CASE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EAAG,uBAAuB,QAAQ,CAC5B;QACL;;IACF;;EACF,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAS,WAAU,2DAA4D,CAAA,EAC/E,kBAAC,KAAD;IAAG,WAAU;cAAuB,EAAG,yBAAyB,qBAAqB;IAAK,CAAA,CACtF;;EACF,CAAA,GAMR,kBAAC,IAAD;EAAY,WAAU;YAAtB,CAEE,kBAAC,UAAD;GAAQ,WAAU;aAChB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,WAAU;MACV,OAAM;MACN,cAAW;gBAEX,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;MACzB,CAAA,EACT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA;OAClD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAW,eAAe;OACxB,CAAA,EACJ,EAAW,aACV,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAG,0BAA0B,YAAY;QAAC;QAAE,EAAW;QACtD;SAEF,EAAA,CAAA,CACF;QACF;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MACE,WAAW,iFACT,MAAe,EAAuB,OAClC,qCACA;gBAGL,MAAe,EAAuB,OACrC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAK,WAAU,YAAa,CAAA,EAC3B,EAAG,0BAA0B,YAAY,CACzC,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAChC,EAAG,6BAA6B,cAAc,CAC9C,EAAA,CAAA;MAED,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD,EAAO,WAAU,YAAa,CAAA,EAC9B,kBAAC,QAAD,EAAA,UAAO,EAAc,QAAc,CAAA,CAC/B;QACF;OACF;;GACC,CAAA,EAGT,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,IAAD;KACE,UAAU;KACK;KACA;KACH;KACZ,kBAAkB;KAClB,kBAAkB;KAClB,qBAAqB;KACrB,oBAAoB;KACpB,WAAU;eAGV,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACC,kBAAC,UAAD;OACE,KAAK;OACL,OAAM;OACN,WAAU;OACV,OAAM;OACN,SAAQ;OACR,CAAA,GAEF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA;QACjE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAG,iCAAiC,sBAAsB;SACzD,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAAgD,gBAAa,EAAc;;QACvE;;MAEJ,CAAA;KACc,CAAA;IAClB,CAAA,EAGN,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,IAAD;KACiB;KACA;KACf,kBACE,MAAe,EAAuB,WAAW,IAAuB,KAAA;KAE1E,qBAAqB;KACrB,CAAA,EAGF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,6BAA6B,eAAe;MAC7C,CAAA,EACL,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,EAAW,aACV,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,yBAAyB,UAAU;SAClC,CAAA,EACP,kBAAC,QAAD;SAAM,WAAU;mBACb,IAAI,KAAK,EAAW,UAAU,CAAC,oBAAoB;SAC/C,CAAA,CACH;;OAER,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,0BAA0B,YAAY;SACrC,CAAA,EACP,kBAAC,QAAD;SAAM,WAAU;mBACb,IAAqB,MAAM,aAAa,IAAI,EAAG,wBAAwB,SAAS;SAC5E,CAAA,CACH;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAuB,EAAG,wBAAwB,SAAS;SAAQ,CAAA,EACnF,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,2BAA2B,YAAY;SACtC,CAAA,CACH;;OACF;QACF;OACA;MACJ;KACK;MA5UX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAa,WAAU,mCAAoC,CAAA;KACvD,CAAA;IACN,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,oCAAoC,0BAA0B;KAC/D,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAW,SACV,EAAG,mCAAmC,sCAAsC;KAC5E,CAAA;IACJ,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,OAAO,QAAQ,MAAM;KACpC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAC/B,EAAG,wBAAwB,UAAU,CAC/B;;IACL;;EACF,CAAA"}
|
|
1
|
+
{"version":3,"file":"SharedSessionPage.js","names":[],"sources":["../../src/pages/SharedSessionPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Terminal,\n Loader2,\n AlertCircle,\n Lock,\n Eye,\n Gamepad2,\n Users,\n Clock,\n ArrowLeft,\n LogIn,\n} from 'lucide-react';\nimport {\n useValidateShareLinkQuery,\n useJoinSharedSessionMutation,\n useLeaveSharedSessionMutation,\n useRequestSessionControlMutation,\n useReleaseSessionControlMutation,\n useGetSessionCollaboratorsQuery,\n useCollaboratorJoinedSubscription,\n useCollaboratorLeftSubscription,\n useSessionControlChangedSubscription,\n type SessionCollaboratorFieldsFragment,\n} from '@/generated/wspace-operations';\nimport { SessionSharePermission } from '@/generated/wspace-types';\nimport { SharedTerminalPanel } from '@/components/sessions/SharedTerminalPanel';\nimport { CollaboratorsList } from '@/components/sessions/CollaboratorsList';\nimport { MotionPage } from '@burdenoff/fe-libs/shared/motion';\nimport { useVibeControls } from '@/providers/VibeControlsProvider';\n\ninterface PasswordFormProps {\n onSubmit: (password: string) => void;\n isLoading: boolean;\n error?: string | null;\n}\n\n/**\n * Password entry form for protected share links\n */\nfunction PasswordForm({ onSubmit, isLoading, error }: PasswordFormProps) {\n const [password, setPassword] = useState('');\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (password.trim()) {\n onSubmit(password);\n }\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-4 w-full max-w-sm\">\n <div>\n <label\n htmlFor=\"shared-session-password\"\n className=\"block text-sm font-medium text-text-primary mb-2\"\n >\n This session is password protected\n </label>\n <div className=\"relative\">\n <Lock className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <input\n aria-label=\"Enter password\"\n id=\"shared-session-password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Enter password\"\n className=\"w-full pl-10 pr-4 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n {error && (\n <div className=\"flex items-center gap-2 p-3 bg-status-error-bg-subtle border border-status-error-border rounded-lg\">\n <AlertCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm text-status-error-text\">{error}</span>\n </div>\n )}\n <button\n type=\"submit\"\n disabled={isLoading || !password.trim()}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {isLoading ? (\n <span className=\"flex items-center justify-center gap-2\">\n <Loader2 className=\"size-4 animate-spin\" />\n Joining...\n </span>\n ) : (\n 'Join Session'\n )}\n </button>\n </form>\n );\n}\n\n/**\n * SharedSessionPage - Page for accessing shared terminal sessions via link\n *\n * URL: /share/:token\n *\n * Flow:\n * 1. Validate the share link token\n * 2. If password required, show password form\n * 3. Join the shared session\n * 4. Display terminal with collaboration UI\n */\nexport function SharedSessionPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { token } = useParams<{ token: string }>();\n const { currentUser } = useVibeControls();\n // State\n const [joined, setJoined] = useState(false);\n const [sessionId, setSessionId] = useState<string | null>(null);\n const [password, setPassword] = useState('');\n const [passwordError, setPasswordError] = useState<string | null>(null);\n const [joinError, setJoinError] = useState<string | null>(null);\n const [hasLeft, setHasLeft] = useState(false);\n const joiningRef = useRef(false);\n const hasJoinedRef = useRef(false);\n\n // Get currentUserId from auth context\n const currentUserId = currentUser?.id ?? '';\n\n // Validate the share link\n const {\n data: validationData,\n loading: validating,\n error: validationError,\n refetch: refetchValidation,\n } = useValidateShareLinkQuery({\n variables: { token: token || '' },\n skip: !token,\n });\n\n const validation = validationData?.validateShareLink;\n\n // Mutations\n const [joinSession, { loading: joining }] = useJoinSharedSessionMutation();\n const [leaveSession] = useLeaveSharedSessionMutation();\n const [requestControl, { loading: requestingControl }] = useRequestSessionControlMutation();\n const [releaseControl, { loading: releasingControl }] = useReleaseSessionControlMutation();\n\n // Get collaborators once joined\n const { data: collaboratorsData, refetch: refetchCollaborators } =\n useGetSessionCollaboratorsQuery({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n // Subscription handles the primary signal (collaboratorJoined /\n // collaboratorLeft). Poll is a slow safety-net to recover if the\n // SSE stream drops silently — 60s is enough since presence flips\n // are pushed through the subscription.\n pollInterval: 60_000,\n });\n\n const collaborators = collaboratorsData?.sessionCollaborators || [];\n\n // Real-time subscriptions for collaboration events\n const { data: joinedData } = useCollaboratorJoinedSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n const { data: leftData } = useCollaboratorLeftSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n const { data: controlChangedData } = useSessionControlChangedSubscription({\n variables: { sessionId: sessionId || '' },\n skip: !sessionId || !joined,\n });\n\n // Refetch collaborators when subscription events arrive\n useEffect(() => {\n if (joinedData || leftData || controlChangedData) {\n refetchCollaborators();\n }\n }, [joinedData, leftData, controlChangedData, refetchCollaborators]);\n\n // Cleanup: Leave session only on unmount, and only if we actually joined\n useEffect(() => {\n return () => {\n if (hasJoinedRef.current && sessionId) {\n leaveSession({ variables: { sessionId } }).catch(console.error);\n }\n };\n }, [sessionId, leaveSession]);\n\n const handleJoinSession = useCallback(\n async (passwordInput?: string) => {\n if (!token || joiningRef.current) return;\n\n joiningRef.current = true;\n const pwd = passwordInput || password;\n setPasswordError(null);\n setJoinError(null);\n\n try {\n const result = await joinSession({\n variables: {\n input: {\n shareToken: token,\n password: pwd || undefined,\n },\n },\n });\n\n if (result.data?.joinSharedSession) {\n hasJoinedRef.current = true;\n setJoined(true);\n setSessionId(result.data.joinSharedSession.sessionId);\n void refetchValidation();\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to join session';\n if (message.toLowerCase().includes('password')) {\n setPasswordError('Incorrect password');\n } else {\n setPasswordError(message);\n setJoinError(message);\n }\n } finally {\n joiningRef.current = false;\n }\n },\n [joinSession, password, refetchValidation, token]\n );\n\n // Auto-join if no password required and link is valid\n useEffect(() => {\n if (\n validation?.isValid &&\n !validation.requiresPassword &&\n !joined &&\n !hasLeft &&\n !joiningRef.current &&\n token\n ) {\n // Set sessionId early from validation so collaborators query can start\n if (validation.sessionId && !sessionId) {\n setSessionId(validation.sessionId);\n }\n void handleJoinSession();\n }\n }, [\n validation?.isValid,\n validation?.requiresPassword,\n validation?.sessionId,\n joined,\n hasLeft,\n token,\n sessionId,\n handleJoinSession,\n ]);\n\n const handleRequestControl = useCallback(async () => {\n if (!sessionId) return;\n try {\n await requestControl({ variables: { sessionId } });\n refetchCollaborators();\n } catch (err) {\n console.error('Failed to request control:', err);\n }\n }, [sessionId, requestControl, refetchCollaborators]);\n\n const handleReleaseControl = useCallback(async () => {\n if (!sessionId) return;\n try {\n await releaseControl({ variables: { sessionId } });\n refetchCollaborators();\n } catch (err) {\n console.error('Failed to release control:', err);\n }\n }, [sessionId, releaseControl, refetchCollaborators]);\n\n const handleLeaveSession = useCallback(async () => {\n if (!sessionId) return;\n try {\n await leaveSession({ variables: { sessionId } });\n hasJoinedRef.current = false; // Prevent cleanup from firing a second leave\n setHasLeft(true);\n setJoined(false);\n setSessionId(null);\n window.history.back();\n } catch (err) {\n console.error('Failed to leave session:', err);\n }\n }, [sessionId, leaveSession]);\n\n // Determine current user's collaborator entry\n const currentCollaborator = collaborators.find(\n (c: SessionCollaboratorFieldsFragment) => c.userId === currentUserId\n );\n const permission = validation?.permission || SessionSharePermission.View;\n\n // Stabilize terminal URL to prevent iframe reloads on parent re-render\n const terminalUrl = useMemo(() => validation?.ttydUrl || null, [validation?.ttydUrl]);\n\n // Loading state\n if (validating) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"size-8 text-action-primary-bg animate-spin mx-auto mb-4\" />\n <p className=\"text-text-secondary\">\n {tr('sharedSession.validating', 'Validating share link...')}\n </p>\n </div>\n </div>\n );\n }\n\n // Error state\n if (validationError || !validation) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-error-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-error-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.unableToLoad', 'Unable to Load Session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {validationError?.message ||\n tr('sharedSession.failedValidate', 'Failed to validate the share link')}\n </p>\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n // Invalid link\n if (!validation.isValid) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-warning-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-warning-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.invalidExpiredLink', 'Invalid or Expired Link')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {validation.error ||\n tr('sharedSession.linkNoLongerValid', 'This share link is no longer valid.')}\n </p>\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n </div>\n </div>\n );\n }\n\n // Password required - show password form\n if (validation.requiresPassword && !joined) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"w-full max-w-md\">\n <div className=\"bg-bg-surface border border-border-subtle rounded-card p-6 shadow-elevation-1\">\n {/* Header */}\n <div className=\"text-center mb-6\">\n <div className=\"size-12 bg-[var(--color-accent-soft)] rounded-full flex items-center justify-center mx-auto mb-4\">\n <Terminal className=\"size-6 text-action-primary-bg\" />\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {validation.sessionName || 'Shared Session'}\n </h2>\n {validation.ownerName && (\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr('sharedSession.sharedBy', 'Shared by')} {validation.ownerName}\n </p>\n )}\n </div>\n\n {/* Permission badge */}\n <div className=\"flex justify-center mb-6\">\n <div\n className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm ${\n validation.permission === SessionSharePermission.View\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-text-primary'\n }`}\n >\n {validation.permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-4\" />\n {tr('sharedSession.viewOnly', 'View Only')}\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-4\" />\n {tr('sharedSession.canControl', 'Can Control')}\n </>\n )}\n </div>\n </div>\n\n {/* Expiry info */}\n {validation.expiresAt && (\n <div className=\"flex items-center justify-center gap-2 mb-6 text-xs text-text-secondary\">\n <Clock className=\"size-3.5\" />\n <span>\n {tr('sharedSession.expires', 'Expires')}{' '}\n {new Date(validation.expiresAt).toLocaleString()}\n </span>\n </div>\n )}\n\n {/* Password form */}\n <PasswordForm\n onSubmit={(pwd) => {\n setPassword(pwd);\n handleJoinSession(pwd);\n }}\n isLoading={joining}\n error={passwordError}\n />\n </div>\n </div>\n </div>\n );\n }\n\n // Post-leave state — user explicitly left, don't show join spinner\n if (hasLeft) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-bg-sunken rounded-full flex items-center justify-center mx-auto mb-4\">\n <LogIn className=\"size-8 text-text-secondary\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.youLeft', 'You left the session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">\n {tr('sharedSession.rejoinHint', 'You can rejoin using the same share link.')}\n </p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setHasLeft(false);\n setJoinError(null);\n handleJoinSession();\n }}\n disabled={joining}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <LogIn className=\"size-4\" />\n {tr('sharedSession.rejoin', 'Rejoin')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Joining state - show error if join failed, otherwise spinner\n if (!joined && !validation.requiresPassword) {\n if (joinError) {\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center p-4\">\n <div className=\"text-center max-w-md\">\n <div className=\"size-16 bg-status-error-bg-subtle rounded-full flex items-center justify-center mx-auto mb-4\">\n <AlertCircle className=\"size-8 text-status-error-text\" />\n </div>\n <h2 className=\"text-xl font-semibold text-text-primary mb-2\">\n {tr('sharedSession.failedJoin', 'Failed to Join Session')}\n </h2>\n <p className=\"text-text-secondary mb-6\">{joinError}</p>\n <div className=\"flex items-center justify-center gap-3\">\n <button\n type=\"button\"\n onClick={() => window.history.back()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken border border-border-default rounded-button hover:bg-bg-elevated hover:border-border-strong transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('sharedSession.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setJoinError(null);\n handleJoinSession();\n }}\n disabled={joining}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <LogIn className=\"size-4\" />\n {tr('sharedSession.retry', 'Retry')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-canvas flex items-center justify-center\">\n <div className=\"text-center\">\n <Loader2 className=\"size-8 text-action-primary-bg animate-spin mx-auto mb-4\" />\n <p className=\"text-text-secondary\">{tr('sharedSession.joining', 'Joining session...')}</p>\n </div>\n </div>\n );\n }\n\n // Main shared session view\n return (\n <MotionPage className=\"min-h-screen bg-bg-canvas flex flex-col\">\n {/* Header */}\n <header className=\"bg-bg-surface border-b border-border-default px-4 py-3\">\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 max-w-screen-2xl mx-auto\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={handleLeaveSession}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-bg-sunken rounded-lg transition-colors\"\n title=\"Leave session\"\n aria-label=\"Leave session\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-[var(--color-accent-soft)] rounded-lg\">\n <Terminal className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <h1 className=\"font-semibold text-text-primary\">\n {validation.sessionName || 'Shared Session'}\n </h1>\n {validation.ownerName && (\n <p className=\"text-xs text-text-secondary\">\n {tr('sharedSession.sharedBy', 'Shared by')} {validation.ownerName}\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Permission and collaborator count */}\n <div className=\"flex items-center gap-3\">\n <div\n className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${\n permission === SessionSharePermission.View\n ? 'bg-bg-sunken text-text-secondary'\n : 'bg-[var(--color-accent-soft)] text-text-primary'\n }`}\n >\n {permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-3.5\" />\n {tr('sharedSession.viewOnly', 'View Only')}\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-3.5\" />\n {tr('sharedSession.interactive', 'Interactive')}\n </>\n )}\n </div>\n <div className=\"flex items-center gap-1.5 px-2.5 py-1 bg-bg-sunken rounded-full text-xs text-text-secondary\">\n <Users className=\"size-3.5\" />\n <span>{collaborators.length}</span>\n </div>\n </div>\n </div>\n </header>\n\n {/* Main content */}\n <div className=\"flex-1 flex\">\n {/* Terminal area */}\n <div className=\"flex-1 flex flex-col\">\n <SharedTerminalPanel\n isShared={true}\n collaborators={collaborators}\n currentUserId={currentUserId}\n permission={permission}\n onRequestControl={handleRequestControl}\n onReleaseControl={handleReleaseControl}\n isRequestingControl={requestingControl}\n isReleasingControl={releasingControl}\n className=\"flex-1\"\n >\n {/* Terminal iframe - in production this would be the ttyd URL */}\n <div className=\"w-full h-full bg-bg-inverse text-text-inverse flex items-center justify-center\">\n {terminalUrl ? (\n <iframe\n src={terminalUrl}\n title=\"Shared Terminal\"\n className=\"w-full h-full border-0\"\n allow=\"clipboard-read; clipboard-write\"\n sandbox=\"allow-scripts allow-same-origin allow-forms\"\n />\n ) : (\n <div className=\"text-center\">\n <Terminal className=\"size-12 text-text-secondary mx-auto mb-4\" />\n <p className=\"text-text-tertiary\">\n {tr('sharedSession.terminalLoading', 'Terminal loading...')}\n </p>\n <p className=\"text-text-secondary text-sm mt-1\">Session ID: {sessionId}</p>\n </div>\n )}\n </div>\n </SharedTerminalPanel>\n </div>\n\n {/* Sidebar - Collaborators */}\n <aside className=\"w-72 bg-bg-surface border-l border-border-default p-4 hidden lg:block\">\n <CollaboratorsList\n collaborators={collaborators}\n currentUserId={currentUserId}\n onRequestControl={\n permission === SessionSharePermission.Interact ? handleRequestControl : undefined\n }\n isRequestingControl={requestingControl}\n />\n\n {/* Session info */}\n <div className=\"mt-6 pt-6 border-t border-border-default\">\n <h4 className=\"text-xs font-medium text-text-secondary uppercase tracking-wide mb-3\">\n {tr('sharedSession.sessionInfo', 'Session Info')}\n </h4>\n <div className=\"space-y-2 text-sm\">\n {validation.expiresAt && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">\n {tr('sharedSession.expires', 'Expires')}\n </span>\n <span className=\"text-text-primary\">\n {new Date(validation.expiresAt).toLocaleTimeString()}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">\n {tr('sharedSession.yourRole', 'Your role')}\n </span>\n <span className=\"text-text-primary capitalize\">\n {currentCollaborator?.role?.toLowerCase() || tr('sharedSession.viewer', 'Viewer')}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">{tr('sharedSession.status', 'Status')}</span>\n <span className=\"text-status-success-text\">\n {tr('sharedSession.connected', 'Connected')}\n </span>\n </div>\n </div>\n </div>\n </aside>\n </div>\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
@@ -494,7 +494,7 @@ function g() {
|
|
|
494
494
|
"aria-pressed": C === "grid",
|
|
495
495
|
"aria-label": m("targets.gridViewTitle", "Grid view"),
|
|
496
496
|
title: m("targets.gridViewTitle", "Grid view"),
|
|
497
|
-
className: `flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${C === "grid" ? "bg-[var(--color-accent-soft)] text-
|
|
497
|
+
className: `flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${C === "grid" ? "bg-[var(--color-accent-soft)] text-text-primary" : "text-text-secondary hover:bg-bg-surface/60"}`,
|
|
498
498
|
children: /* @__PURE__ */ d(Pe, {
|
|
499
499
|
className: "size-4 text-text-secondary",
|
|
500
500
|
"aria-hidden": "true"
|
|
@@ -505,7 +505,7 @@ function g() {
|
|
|
505
505
|
"aria-pressed": C === "list",
|
|
506
506
|
"aria-label": m("targets.listViewTitle", "List view"),
|
|
507
507
|
title: m("targets.listViewTitle", "List view"),
|
|
508
|
-
className: `flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${C === "list" ? "bg-[var(--color-accent-soft)] text-
|
|
508
|
+
className: `flex size-8 max-md:size-9 items-center justify-center rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${C === "list" ? "bg-[var(--color-accent-soft)] text-text-primary" : "text-text-secondary hover:bg-bg-surface/60"}`,
|
|
509
509
|
children: /* @__PURE__ */ d(Fe, {
|
|
510
510
|
className: "size-4 text-text-secondary",
|
|
511
511
|
"aria-hidden": "true"
|