chore: Remove share session via base url (#9994)
This commit is contained in:
+12
-81
@@ -8,8 +8,7 @@ import {
|
||||
useLocation,
|
||||
useSearchParams,
|
||||
} from 'react-router-dom';
|
||||
import { openSharedSessionFromDeepLink, importNostrSessionFromDeepLink } from './sessionLinks';
|
||||
import { type SharedSessionDetails } from './sharedSessions';
|
||||
import { importNostrSessionFromDeepLink } from './sessionLinks';
|
||||
import { ErrorUI } from './components/ErrorBoundary';
|
||||
import { ExtensionInstallModal } from './components/ExtensionInstallModal';
|
||||
import RecipeParamsModalContainer from './components/RecipeParamsModalContainer';
|
||||
@@ -31,7 +30,6 @@ interface PairRouteState {
|
||||
}
|
||||
import SettingsView, { SettingsViewOptions } from './components/settings/SettingsView';
|
||||
import SessionsView from './components/sessions/SessionsView';
|
||||
import SharedSessionView from './components/sessions/SharedSessionView';
|
||||
import SchedulesView from './components/schedule/SchedulesView';
|
||||
import ProviderSettings from './components/settings/providers/ProviderSettingsPage';
|
||||
import { AppLayout } from './components/Layout/AppLayout';
|
||||
@@ -272,47 +270,6 @@ const ConfigureProvidersRoute = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Wrapper component for SharedSessionRoute to access parent state
|
||||
const SharedSessionRouteWrapper = ({
|
||||
isLoadingSharedSession,
|
||||
setIsLoadingSharedSession,
|
||||
sharedSessionError,
|
||||
}: {
|
||||
isLoadingSharedSession: boolean;
|
||||
setIsLoadingSharedSession: (loading: boolean) => void;
|
||||
sharedSessionError: string | null;
|
||||
}) => {
|
||||
const location = useLocation();
|
||||
const setView = useNavigation();
|
||||
|
||||
const historyState = window.history.state;
|
||||
const sessionDetails = (location.state?.sessionDetails ||
|
||||
historyState?.sessionDetails) as SharedSessionDetails | null;
|
||||
const error = location.state?.error || historyState?.error || sharedSessionError;
|
||||
const shareToken = location.state?.shareToken || historyState?.shareToken;
|
||||
const baseUrl = location.state?.baseUrl || historyState?.baseUrl;
|
||||
|
||||
return (
|
||||
<SharedSessionView
|
||||
session={sessionDetails}
|
||||
isLoading={isLoadingSharedSession}
|
||||
error={error}
|
||||
onRetry={async () => {
|
||||
if (shareToken && baseUrl) {
|
||||
setIsLoadingSharedSession(true);
|
||||
try {
|
||||
await openSharedSessionFromDeepLink(`goose://sessions/${shareToken}`, setView, baseUrl);
|
||||
} catch (error) {
|
||||
console.error('Failed to retry loading shared session:', error);
|
||||
} finally {
|
||||
setIsLoadingSharedSession(false);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ExtensionsRoute = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -348,8 +305,6 @@ const ExtensionsRoute = () => {
|
||||
|
||||
export function AppInner() {
|
||||
const [fatalError, setFatalError] = useState<string | null>(null);
|
||||
const [isLoadingSharedSession, setIsLoadingSharedSession] = useState(false);
|
||||
const [sharedSessionError, setSharedSessionError] = useState<string | null>(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const setView = useNavigation();
|
||||
@@ -439,46 +394,32 @@ export function AppInner() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleOpenSharedSession = async (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||
const handleOpenSessionShare = async (_event: IpcRendererEvent, ...args: unknown[]) => {
|
||||
const link = args[0] as string;
|
||||
window.electron.logInfo(`Opening shared session from deep link ${link}`);
|
||||
setIsLoadingSharedSession(true);
|
||||
setSharedSessionError(null);
|
||||
window.electron.logInfo('Opening session share link');
|
||||
try {
|
||||
if (link.startsWith('goose://sessions/nostr')) {
|
||||
await importNostrSessionFromDeepLink(link);
|
||||
navigate('/sessions');
|
||||
return;
|
||||
}
|
||||
await openSharedSessionFromDeepLink(link, (_view: View, options?: ViewOptions) => {
|
||||
navigate('/shared-session', { state: options });
|
||||
});
|
||||
|
||||
toast.error('Unsupported session share link');
|
||||
navigate('/sessions');
|
||||
} catch (error) {
|
||||
console.error('Unexpected error opening shared session:', error);
|
||||
console.error('Unexpected error opening Nostr session share:', error);
|
||||
trackErrorWithContext(error, {
|
||||
component: 'AppInner',
|
||||
action: 'open_shared_session',
|
||||
action: 'open_nostr_session_share',
|
||||
recoverable: true,
|
||||
});
|
||||
if (link.startsWith('goose://sessions/nostr')) {
|
||||
toast.error(`Failed to import Nostr session: ${errorMessage(error, 'Unknown error')}`);
|
||||
navigate('/sessions');
|
||||
} else {
|
||||
const shareToken = link.replace('goose://sessions/', '');
|
||||
const options = {
|
||||
sessionDetails: null,
|
||||
error: errorMessage(error, 'Unknown error'),
|
||||
shareToken,
|
||||
};
|
||||
navigate('/shared-session', { state: options });
|
||||
}
|
||||
} finally {
|
||||
setIsLoadingSharedSession(false);
|
||||
toast.error(`Failed to import Nostr session: ${errorMessage(error, 'Unknown error')}`);
|
||||
navigate('/sessions');
|
||||
}
|
||||
};
|
||||
window.electron.on('open-shared-session', handleOpenSharedSession);
|
||||
window.electron.on('open-shared-session', handleOpenSessionShare);
|
||||
return () => {
|
||||
window.electron.off('open-shared-session', handleOpenSharedSession);
|
||||
window.electron.off('open-shared-session', handleOpenSessionShare);
|
||||
};
|
||||
}, [navigate]);
|
||||
|
||||
@@ -692,16 +633,6 @@ export function AppInner() {
|
||||
<Route path="schedules" element={<SchedulesRoute />} />
|
||||
<Route path="recipes" element={<RecipesRoute />} />
|
||||
<Route path="skills" element={<SkillsRoute />} />
|
||||
<Route
|
||||
path="shared-session"
|
||||
element={
|
||||
<SharedSessionRouteWrapper
|
||||
isLoadingSharedSession={isLoadingSharedSession}
|
||||
setIsLoadingSharedSession={setIsLoadingSharedSession}
|
||||
sharedSessionError={sharedSessionError}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="permission" element={<PermissionRoute />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@@ -1,419 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Calendar,
|
||||
MessageSquareText,
|
||||
Folder,
|
||||
Share2,
|
||||
Sparkles,
|
||||
Copy,
|
||||
Check,
|
||||
Target,
|
||||
LoaderCircle,
|
||||
AlertCircle,
|
||||
} from 'lucide-react';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import { resumeSession } from '../../sessions';
|
||||
import { Button } from '../ui/button';
|
||||
import { toast } from 'react-toastify';
|
||||
import { MainPanelLayout } from '../Layout/MainPanelLayout';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { formatMessageTimestamp } from '../../utils/timeUtils';
|
||||
import { createSharedSession } from '../../sharedSessions';
|
||||
import { errorMessage } from '../../utils/conversionUtils';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '../ui/dialog';
|
||||
import ProgressiveMessageList from '../ProgressiveMessageList';
|
||||
import { SearchView } from '../conversation/SearchView';
|
||||
import BackButton from '../ui/BackButton';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/Tooltip';
|
||||
import { Message, Session } from '../../api';
|
||||
import { useNavigation } from '../../hooks/useNavigation';
|
||||
|
||||
const i18n = defineMessages({
|
||||
errorLoadingDetails: {
|
||||
id: 'sessionHistory.error.loading',
|
||||
defaultMessage: 'Error Loading Session Details',
|
||||
},
|
||||
tryAgain: {
|
||||
id: 'sessionHistory.error.tryAgain',
|
||||
defaultMessage: 'Try Again',
|
||||
},
|
||||
searchPlaceholder: {
|
||||
id: 'sessionHistory.searchPlaceholder',
|
||||
defaultMessage: 'Search history...',
|
||||
},
|
||||
noMessages: {
|
||||
id: 'sessionHistory.empty.title',
|
||||
defaultMessage: 'No messages found',
|
||||
},
|
||||
noMessagesDesc: {
|
||||
id: 'sessionHistory.empty.description',
|
||||
defaultMessage: "This session doesn't contain any messages",
|
||||
},
|
||||
loadingDetails: {
|
||||
id: 'sessionHistory.loading',
|
||||
defaultMessage: 'Loading session details...',
|
||||
},
|
||||
sharing: {
|
||||
id: 'sessionHistory.sharing',
|
||||
defaultMessage: 'Sharing...',
|
||||
},
|
||||
share: {
|
||||
id: 'sessionHistory.share',
|
||||
defaultMessage: 'Share',
|
||||
},
|
||||
shareTooltip: {
|
||||
id: 'sessionHistory.shareTooltip',
|
||||
defaultMessage: 'To enable session sharing, go to <b>Settings</b> > <b>Session</b> > <b>Session Sharing</b>.',
|
||||
},
|
||||
resume: {
|
||||
id: 'sessionHistory.resume',
|
||||
defaultMessage: 'Resume',
|
||||
},
|
||||
shareSessionTitle: {
|
||||
id: 'sessionHistory.shareModal.title',
|
||||
defaultMessage: 'Share Session (beta)',
|
||||
},
|
||||
shareSessionDescription: {
|
||||
id: 'sessionHistory.shareModal.description',
|
||||
defaultMessage: 'Share this session link to give others a read only view of your goose chat.',
|
||||
},
|
||||
copy: {
|
||||
id: 'sessionHistory.copy',
|
||||
defaultMessage: 'Copy',
|
||||
},
|
||||
cancel: {
|
||||
id: 'sessionHistory.cancel',
|
||||
defaultMessage: 'Cancel',
|
||||
},
|
||||
failedToShare: {
|
||||
id: 'sessionHistory.toast.shareFailed',
|
||||
defaultMessage: 'Failed to share session: {error}',
|
||||
},
|
||||
failedToCopy: {
|
||||
id: 'sessionHistory.toast.copyFailed',
|
||||
defaultMessage: 'Failed to copy link to clipboard',
|
||||
},
|
||||
couldNotLaunch: {
|
||||
id: 'sessionHistory.toast.launchFailed',
|
||||
defaultMessage: 'Could not launch session: {error}',
|
||||
},
|
||||
});
|
||||
|
||||
const isUserMessage = (message: Message): boolean => {
|
||||
if (message.role === 'assistant') {
|
||||
return false;
|
||||
}
|
||||
return !message.content.every(
|
||||
(c) => c.type === 'actionRequired' && c.data.actionType === 'toolConfirmation'
|
||||
);
|
||||
};
|
||||
|
||||
const filterMessagesForDisplay = (messages: Message[]): Message[] => {
|
||||
return messages;
|
||||
};
|
||||
|
||||
interface SessionHistoryViewProps {
|
||||
session: Session;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
onBack: () => void;
|
||||
onRetry: () => void;
|
||||
showActionButtons?: boolean;
|
||||
}
|
||||
|
||||
// Custom SessionHeader component similar to SessionListView style
|
||||
const SessionHeader: React.FC<{
|
||||
onBack: () => void;
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
actionButtons?: React.ReactNode;
|
||||
}> = ({ onBack, children, title, actionButtons }) => {
|
||||
return (
|
||||
<div className="flex flex-col pb-8 border-b">
|
||||
<div className="flex items-center pt-0 mb-1">
|
||||
<BackButton onClick={onBack} />
|
||||
</div>
|
||||
<h1 className="text-4xl font-light mb-4 pt-6">{title}</h1>
|
||||
<div className="flex items-center">{children}</div>
|
||||
{actionButtons && <div className="flex items-center space-x-3 mt-4">{actionButtons}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SessionMessages: React.FC<{
|
||||
messages: Message[];
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
onRetry: () => void;
|
||||
}> = ({ messages, isLoading, error, onRetry }) => {
|
||||
const intl = useIntl();
|
||||
const filteredMessages = filterMessagesForDisplay(messages);
|
||||
|
||||
return (
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="pb-24 pt-8">
|
||||
<div className="flex flex-col space-y-6">
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<LoaderCircle className="animate-spin h-8 w-8 text-text-primary" />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-text-secondary">
|
||||
<div className="text-red-500 mb-4">
|
||||
<AlertCircle size={32} />
|
||||
</div>
|
||||
<p className="text-md mb-2">{intl.formatMessage(i18n.errorLoadingDetails)}</p>
|
||||
<p className="text-sm text-center mb-4">{error}</p>
|
||||
<Button onClick={onRetry} variant="default">
|
||||
{intl.formatMessage(i18n.tryAgain)}
|
||||
</Button>
|
||||
</div>
|
||||
) : filteredMessages?.length > 0 ? (
|
||||
<div className="max-w-4xl mx-auto w-full">
|
||||
<SearchView placeholder={intl.formatMessage(i18n.searchPlaceholder)}>
|
||||
<ProgressiveMessageList
|
||||
messages={filteredMessages}
|
||||
chat={{
|
||||
sessionId: 'session-preview',
|
||||
}}
|
||||
toolCallNotifications={new Map()}
|
||||
append={() => {}} // Read-only for session history
|
||||
isUserMessage={isUserMessage} // Use the same function as BaseChat
|
||||
batchSize={15} // Same as BaseChat default
|
||||
batchDelay={30} // Same as BaseChat default
|
||||
showLoadingThreshold={30} // Same as BaseChat default
|
||||
/>
|
||||
</SearchView>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-text-secondary">
|
||||
<MessageSquareText className="w-12 h-12 mb-4" />
|
||||
<p className="text-lg mb-2">{intl.formatMessage(i18n.noMessages)}</p>
|
||||
<p className="text-sm">{intl.formatMessage(i18n.noMessagesDesc)}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
);
|
||||
};
|
||||
|
||||
const SessionHistoryView: React.FC<SessionHistoryViewProps> = ({
|
||||
session,
|
||||
isLoading,
|
||||
error,
|
||||
onBack,
|
||||
onRetry,
|
||||
showActionButtons = true,
|
||||
}) => {
|
||||
const [isShareModalOpen, setIsShareModalOpen] = useState(false);
|
||||
const [shareLink, setShareLink] = useState<string>('');
|
||||
const [isSharing, setIsSharing] = useState(false);
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const [canShare, setCanShare] = useState(false);
|
||||
|
||||
const intl = useIntl();
|
||||
const messages = session.conversation || [];
|
||||
|
||||
const setView = useNavigation();
|
||||
|
||||
useEffect(() => {
|
||||
window.electron.getSetting('sessionSharing').then((config) => {
|
||||
if (config.enabled && config.baseUrl) {
|
||||
setCanShare(true);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleShare = async () => {
|
||||
setIsSharing(true);
|
||||
|
||||
try {
|
||||
const config = await window.electron.getSetting('sessionSharing');
|
||||
if (!config.enabled || !config.baseUrl) {
|
||||
throw new Error('Session sharing is not enabled or base URL is not configured.');
|
||||
}
|
||||
|
||||
const shareToken = await createSharedSession(
|
||||
config.baseUrl,
|
||||
session.working_dir,
|
||||
messages,
|
||||
session.name || 'Shared Session',
|
||||
session.usage?.total_tokens || 0
|
||||
);
|
||||
|
||||
const shareableLink = `goose://sessions/${shareToken}`;
|
||||
setShareLink(shareableLink);
|
||||
setIsShareModalOpen(true);
|
||||
} catch (error) {
|
||||
console.error('Error sharing session:', error);
|
||||
toast.error(intl.formatMessage(i18n.failedToShare, { error: errorMessage(error, 'Unknown error') }));
|
||||
} finally {
|
||||
setIsSharing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopyLink = () => {
|
||||
navigator.clipboard
|
||||
.writeText(shareLink)
|
||||
.then(() => {
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to copy link:', err);
|
||||
toast.error(intl.formatMessage(i18n.failedToCopy));
|
||||
});
|
||||
};
|
||||
|
||||
const handleResumeSession = () => {
|
||||
try {
|
||||
resumeSession(session, setView);
|
||||
} catch (error) {
|
||||
toast.error(intl.formatMessage(i18n.couldNotLaunch, { error: errorMessage(error) }));
|
||||
}
|
||||
};
|
||||
|
||||
const actionButtons = showActionButtons ? (
|
||||
<>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={handleShare}
|
||||
disabled={!canShare || isSharing}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className={canShare ? '' : 'cursor-not-allowed opacity-50'}
|
||||
>
|
||||
{isSharing ? (
|
||||
<>
|
||||
<LoaderCircle className="w-4 h-4 mr-2 animate-spin" />
|
||||
{intl.formatMessage(i18n.sharing)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Share2 className="w-4 h-4" />
|
||||
{intl.formatMessage(i18n.share)}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
{!canShare ? (
|
||||
<TooltipContent>
|
||||
<p>
|
||||
{intl.formatMessage(i18n.shareTooltip, {
|
||||
b: (chunks: React.ReactNode) => <b>{chunks}</b>,
|
||||
})}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
) : null}
|
||||
</Tooltip>
|
||||
<Button onClick={handleResumeSession} size="sm" variant="outline">
|
||||
<Sparkles className="w-4 h-4" />
|
||||
{intl.formatMessage(i18n.resume)}
|
||||
</Button>
|
||||
</>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainPanelLayout>
|
||||
<div className="flex-1 flex flex-col min-h-0 px-8">
|
||||
<SessionHeader
|
||||
onBack={onBack}
|
||||
title={session.name}
|
||||
actionButtons={!isLoading ? actionButtons : null}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{!isLoading ? (
|
||||
<>
|
||||
<div className="flex items-center text-text-secondary text-sm space-x-5 font-mono">
|
||||
<span className="flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-1" />
|
||||
{formatMessageTimestamp(messages[0]?.created)}
|
||||
</span>
|
||||
<span className="flex items-center">
|
||||
<MessageSquareText className="w-4 h-4 mr-1" />
|
||||
{session.message_count}
|
||||
</span>
|
||||
{session.usage?.total_tokens != null && (
|
||||
<span className="flex items-center">
|
||||
<Target className="w-4 h-4 mr-1" />
|
||||
{session.usage.total_tokens.toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center text-text-secondary text-sm mt-1 font-mono">
|
||||
<span className="flex items-center">
|
||||
<Folder className="w-4 h-4 mr-1" />
|
||||
{session.working_dir}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center text-text-secondary text-sm">
|
||||
<LoaderCircle className="w-4 h-4 mr-2 animate-spin" />
|
||||
<span>{intl.formatMessage(i18n.loadingDetails)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SessionHeader>
|
||||
|
||||
<SessionMessages
|
||||
messages={messages}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
onRetry={onRetry}
|
||||
/>
|
||||
</div>
|
||||
</MainPanelLayout>
|
||||
|
||||
<Dialog open={isShareModalOpen} onOpenChange={setIsShareModalOpen}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex justify-center items-center gap-2">
|
||||
<Share2 className="w-6 h-6 text-text-primary" />
|
||||
{intl.formatMessage(i18n.shareSessionTitle)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{intl.formatMessage(i18n.shareSessionDescription)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="py-4">
|
||||
<div className="relative rounded-full border border-border-primary px-3 py-2 flex items-center bg-gray-100 dark:bg-gray-600">
|
||||
<code className="text-sm text-text-primary dark:text-text-inverse overflow-x-hidden break-all pr-8 w-full">
|
||||
{shareLink}
|
||||
</code>
|
||||
<Button
|
||||
shape="pill"
|
||||
variant="ghost"
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2"
|
||||
onClick={handleCopyLink}
|
||||
disabled={isCopied}
|
||||
>
|
||||
{isCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
<span className="sr-only">{intl.formatMessage(i18n.copy)}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setIsShareModalOpen(false)}>
|
||||
{intl.formatMessage(i18n.cancel)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SessionHistoryView;
|
||||
@@ -232,11 +232,10 @@ function useDebounce<T>(value: T, delay: number): T {
|
||||
|
||||
interface SessionListViewProps {
|
||||
onSelectSession: (sessionId: string) => void;
|
||||
selectedSessionId?: string | null;
|
||||
}
|
||||
|
||||
const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
({ onSelectSession, selectedSessionId }) => {
|
||||
({ onSelectSession }) => {
|
||||
const intl = useIntl();
|
||||
const [sessions, setSessions] = useState<SessionListItem[]>([]);
|
||||
const [isPrefetchingSessions, setIsPrefetchingSessions] = useState(false);
|
||||
@@ -274,16 +273,6 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
const loadGenerationRef = useRef(0);
|
||||
const hasLoadedRef = useRef(false);
|
||||
|
||||
// Track session to element ref
|
||||
const sessionRefs = useRef<Record<string, HTMLElement>>({});
|
||||
const setSessionRefs = (itemId: string, element: HTMLDivElement | null) => {
|
||||
if (element) {
|
||||
sessionRefs.current[itemId] = element;
|
||||
} else {
|
||||
delete sessionRefs.current[itemId];
|
||||
}
|
||||
};
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const visibleDateGroups = useMemo(() => {
|
||||
@@ -435,18 +424,6 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
});
|
||||
}, [memoizedDateGroups]);
|
||||
|
||||
// Scroll to the selected session when returning from session history view
|
||||
useEffect(() => {
|
||||
if (selectedSessionId) {
|
||||
const element = sessionRefs.current[selectedSessionId];
|
||||
if (element) {
|
||||
element.scrollIntoView({
|
||||
block: 'center',
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [selectedSessionId, sessions]);
|
||||
|
||||
// Handle immediate search input (updates search term for debouncing).
|
||||
const handleSearch = useCallback((term: string) => {
|
||||
setSearchTerm(term);
|
||||
@@ -727,7 +704,6 @@ const SessionListView: React.FC<SessionListViewProps> = React.memo(
|
||||
<Card
|
||||
onClick={handleCardClick}
|
||||
className="h-full py-3 px-4 hover:shadow-default cursor-pointer transition-all duration-150 flex flex-col justify-between relative group"
|
||||
ref={(el) => setSessionRefs(session.id, el)}
|
||||
>
|
||||
<div>
|
||||
<h3 className="text-base break-words line-clamp-2 w-full mb-1">{displayName}</h3>
|
||||
|
||||
@@ -1,54 +1,10 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import React, { useCallback } from 'react';
|
||||
import SessionListView from './SessionListView';
|
||||
import SessionHistoryView from './SessionHistoryView';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { getSession, Session } from '../../api';
|
||||
import { useNavigation } from '../../hooks/useNavigation';
|
||||
|
||||
const i18n = defineMessages({
|
||||
loading: {
|
||||
id: 'sessionsView.loading',
|
||||
defaultMessage: 'Loading...',
|
||||
},
|
||||
failedToLoad: {
|
||||
id: 'sessionsView.error.failedToLoad',
|
||||
defaultMessage: 'Failed to load session details. Please try again later.',
|
||||
},
|
||||
});
|
||||
|
||||
const SessionsView: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const [selectedSession, setSelectedSession] = useState<Session | null>(null);
|
||||
const [showSessionHistory, setShowSessionHistory] = useState(false);
|
||||
const [isLoadingSession, setIsLoadingSession] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [initialSessionId, setInitialSessionId] = useState<string | null>(null);
|
||||
const location = useLocation();
|
||||
const setView = useNavigation();
|
||||
|
||||
const loadSessionDetails = async (sessionId: string) => {
|
||||
setIsLoadingSession(true);
|
||||
setError(null);
|
||||
setShowSessionHistory(true);
|
||||
try {
|
||||
const response = await getSession<true>({
|
||||
path: { session_id: sessionId },
|
||||
throwOnError: true,
|
||||
});
|
||||
setSelectedSession(response.data);
|
||||
} catch (err) {
|
||||
console.error(`Failed to load session details for ${sessionId}:`, err);
|
||||
setError(intl.formatMessage(i18n.failedToLoad));
|
||||
// Keep the selected session null if there's an error
|
||||
setSelectedSession(null);
|
||||
setShowSessionHistory(false);
|
||||
} finally {
|
||||
setIsLoadingSession(false);
|
||||
setInitialSessionId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectSession = useCallback(
|
||||
async (sessionId: string) => {
|
||||
setView('pair', {
|
||||
@@ -59,59 +15,7 @@ const SessionsView: React.FC = () => {
|
||||
[setView]
|
||||
);
|
||||
|
||||
// Check if a session ID was passed in the location state.
|
||||
useEffect(() => {
|
||||
const state = location.state as { selectedSessionId?: string } | null;
|
||||
if (state?.selectedSessionId) {
|
||||
// Set immediate loading state to prevent flash of session list
|
||||
setIsLoadingSession(true);
|
||||
setInitialSessionId(state.selectedSessionId);
|
||||
handleSelectSession(state.selectedSessionId);
|
||||
// Clear the state to prevent reloading on navigation
|
||||
window.history.replaceState({}, document.title);
|
||||
}
|
||||
}, [location.state, handleSelectSession]);
|
||||
|
||||
const handleBackToSessions = () => {
|
||||
setShowSessionHistory(false);
|
||||
setError(null);
|
||||
};
|
||||
|
||||
const handleRetryLoadSession = () => {
|
||||
if (selectedSession) {
|
||||
loadSessionDetails(selectedSession.id);
|
||||
}
|
||||
};
|
||||
|
||||
// If we're loading an initial session or have a selected showSessionHistory, show the session history view
|
||||
// Otherwise, show the sessions list view
|
||||
return (showSessionHistory && selectedSession) || (isLoadingSession && initialSessionId) ? (
|
||||
<SessionHistoryView
|
||||
session={
|
||||
selectedSession || {
|
||||
id: initialSessionId || '',
|
||||
conversation: [],
|
||||
name: intl.formatMessage(i18n.loading),
|
||||
working_dir: '',
|
||||
message_count: 0,
|
||||
usage: {},
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
extension_data: {},
|
||||
user_set_name: false,
|
||||
}
|
||||
}
|
||||
isLoading={isLoadingSession}
|
||||
error={error}
|
||||
onBack={handleBackToSessions}
|
||||
onRetry={handleRetryLoadSession}
|
||||
/>
|
||||
) : (
|
||||
<SessionListView
|
||||
onSelectSession={handleSelectSession}
|
||||
selectedSessionId={selectedSession?.id ?? null}
|
||||
/>
|
||||
);
|
||||
return <SessionListView onSelectSession={handleSelectSession} />;
|
||||
};
|
||||
|
||||
export default SessionsView;
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Calendar, MessageSquareText, Folder, Target, LoaderCircle, Share2 } from 'lucide-react';
|
||||
import { defineMessages, useIntl } from '../../i18n';
|
||||
import { type SharedSessionDetails } from '../../sharedSessions';
|
||||
import { SessionMessages } from './SessionViewComponents';
|
||||
import { formatMessageTimestamp } from '../../utils/timeUtils';
|
||||
import { MainPanelLayout } from '../Layout/MainPanelLayout';
|
||||
|
||||
const i18n = defineMessages({
|
||||
sharedSession: {
|
||||
id: 'sharedSession.title',
|
||||
defaultMessage: 'Shared Session',
|
||||
},
|
||||
loadingDetails: {
|
||||
id: 'sharedSession.loading',
|
||||
defaultMessage: 'Loading session details...',
|
||||
},
|
||||
});
|
||||
|
||||
interface SharedSessionViewProps {
|
||||
session: SharedSessionDetails | null;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
onRetry: () => void;
|
||||
}
|
||||
|
||||
// Custom SessionHeader component matching SessionHistoryView style
|
||||
const SessionHeader: React.FC<{
|
||||
children: React.ReactNode;
|
||||
title: string;
|
||||
}> = ({ children, title }) => {
|
||||
return (
|
||||
<div className="flex flex-col pb-8 border-b">
|
||||
<h1 className="text-4xl font-light mb-4 pt-6">{title}</h1>
|
||||
<div className="flex items-center">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SharedSessionView: React.FC<SharedSessionViewProps> = ({
|
||||
session,
|
||||
isLoading,
|
||||
error,
|
||||
onRetry,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<MainPanelLayout>
|
||||
<div className="flex-1 flex flex-col min-h-0 px-8">
|
||||
<div className="flex items-center py-4 border-b border-border-primary mb-6">
|
||||
<div className="flex items-center text-text-secondary">
|
||||
<Share2 className="w-5 h-5 mr-2" />
|
||||
<span className="text-sm font-medium">{intl.formatMessage(i18n.sharedSession)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SessionHeader title={session ? session.description : intl.formatMessage(i18n.sharedSession)}>
|
||||
<div className="flex flex-col">
|
||||
{!isLoading && session && session.messages.length > 0 ? (
|
||||
<>
|
||||
<div className="flex items-center text-text-secondary text-sm space-x-5 font-mono">
|
||||
<span className="flex items-center">
|
||||
<Calendar className="w-4 h-4 mr-1" />
|
||||
{formatMessageTimestamp(session.messages[0]?.created)}
|
||||
</span>
|
||||
<span className="flex items-center">
|
||||
<MessageSquareText className="w-4 h-4 mr-1" />
|
||||
{session.message_count}
|
||||
</span>
|
||||
{session.total_tokens !== null && (
|
||||
<span className="flex items-center">
|
||||
<Target className="w-4 h-4 mr-1" />
|
||||
{session.total_tokens.toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center text-text-secondary text-sm mt-1 font-mono">
|
||||
<span className="flex items-center">
|
||||
<Folder className="w-4 h-4 mr-1" />
|
||||
{session.working_dir}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center text-text-secondary text-sm">
|
||||
<LoaderCircle className="w-4 h-4 mr-2 animate-spin" />
|
||||
<span>{intl.formatMessage(i18n.loadingDetails)}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</SessionHeader>
|
||||
|
||||
<SessionMessages
|
||||
messages={session?.messages || []}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
onRetry={onRetry}
|
||||
/>
|
||||
</div>
|
||||
</MainPanelLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default SharedSessionView;
|
||||
@@ -2,7 +2,6 @@ import { ScrollArea } from '../ui/scroll-area';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
|
||||
import { View, ViewOptions } from '../../utils/navigationUtils';
|
||||
import ModelsSection from './models/ModelsSection';
|
||||
import SessionSharingSection from './sessions/SessionSharingSection';
|
||||
import ExternalBackendSection from './app/ExternalBackendSection';
|
||||
import AppSettingsSection from './app/AppSettingsSection';
|
||||
import ConfigSettings from './config/ConfigSettings';
|
||||
@@ -253,7 +252,6 @@ export default function SettingsView({
|
||||
className="mt-0 focus-visible:outline-none focus-visible:ring-0"
|
||||
>
|
||||
<div className="space-y-8 pb-8">
|
||||
<SessionSharingSection />
|
||||
<ExternalBackendSection />
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
@@ -1,321 +0,0 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Input } from '../../ui/input';
|
||||
import { Check, Lock, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { Switch } from '../../ui/switch';
|
||||
import { Button } from '../../ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../ui/card';
|
||||
import { trackSettingToggled } from '../../../utils/analytics';
|
||||
import { defineMessages, useIntl } from '../../../i18n';
|
||||
|
||||
const i18n = defineMessages({
|
||||
title: {
|
||||
id: 'sessionSharingSection.title',
|
||||
defaultMessage: 'Session Sharing',
|
||||
},
|
||||
descriptionConfigured: {
|
||||
id: 'sessionSharingSection.descriptionConfigured',
|
||||
defaultMessage:
|
||||
'Session sharing is configured but fully opt-in — your sessions are only shared when you explicitly click the share button.',
|
||||
},
|
||||
descriptionDefault: {
|
||||
id: 'sessionSharingSection.descriptionDefault',
|
||||
defaultMessage: 'You can enable session sharing to share your sessions with others.',
|
||||
},
|
||||
alreadyConfigured: {
|
||||
id: 'sessionSharingSection.alreadyConfigured',
|
||||
defaultMessage: 'Session sharing has already been configured',
|
||||
},
|
||||
enableSharing: {
|
||||
id: 'sessionSharingSection.enableSharing',
|
||||
defaultMessage: 'Enable session sharing',
|
||||
},
|
||||
baseUrl: {
|
||||
id: 'sessionSharingSection.baseUrl',
|
||||
defaultMessage: 'Base URL',
|
||||
},
|
||||
urlPlaceholder: {
|
||||
id: 'sessionSharingSection.urlPlaceholder',
|
||||
defaultMessage: 'https://example.com/api',
|
||||
},
|
||||
invalidUrl: {
|
||||
id: 'sessionSharingSection.invalidUrl',
|
||||
defaultMessage:
|
||||
'Invalid URL format. Please enter a valid URL (e.g. https://example.com/api).',
|
||||
},
|
||||
testingConnection: {
|
||||
id: 'sessionSharingSection.testingConnection',
|
||||
defaultMessage: 'Testing connection...',
|
||||
},
|
||||
connectionSuccess: {
|
||||
id: 'sessionSharingSection.connectionSuccess',
|
||||
defaultMessage: 'Connection successful!',
|
||||
},
|
||||
serverError: {
|
||||
id: 'sessionSharingSection.serverError',
|
||||
defaultMessage:
|
||||
'Server error: HTTP {status}. The server may not be configured correctly.',
|
||||
},
|
||||
connectionFailed: {
|
||||
id: 'sessionSharingSection.connectionFailed',
|
||||
defaultMessage: 'Connection failed. ',
|
||||
},
|
||||
unreachableServer: {
|
||||
id: 'sessionSharingSection.unreachableServer',
|
||||
defaultMessage:
|
||||
'Unable to reach the server. Please check the URL and your network connection.',
|
||||
},
|
||||
connectionTimedOut: {
|
||||
id: 'sessionSharingSection.connectionTimedOut',
|
||||
defaultMessage: 'Connection timed out. The server may be slow or unreachable.',
|
||||
},
|
||||
unknownError: {
|
||||
id: 'sessionSharingSection.unknownError',
|
||||
defaultMessage: 'Unknown error occurred.',
|
||||
},
|
||||
testing: {
|
||||
id: 'sessionSharingSection.testing',
|
||||
defaultMessage: 'Testing...',
|
||||
},
|
||||
testConnection: {
|
||||
id: 'sessionSharingSection.testConnection',
|
||||
defaultMessage: 'Test Connection',
|
||||
},
|
||||
});
|
||||
|
||||
export default function SessionSharingSection() {
|
||||
const intl = useIntl();
|
||||
const envBaseUrlShare = window.appConfig.get('GOOSE_BASE_URL_SHARE');
|
||||
|
||||
// If env is set, force sharing enabled and set the baseUrl accordingly.
|
||||
const [sessionSharingConfig, setSessionSharingConfig] = useState({
|
||||
enabled: envBaseUrlShare ? true : false,
|
||||
baseUrl: typeof envBaseUrlShare === 'string' ? envBaseUrlShare : '',
|
||||
});
|
||||
const [urlError, setUrlError] = useState('');
|
||||
const [testResult, setTestResult] = useState<{
|
||||
status: 'success' | 'error' | 'testing' | null;
|
||||
message: string;
|
||||
}>({ status: null, message: '' });
|
||||
|
||||
// isUrlConfigured is true if the user has configured a baseUrl and it is valid.
|
||||
const isUrlConfigured =
|
||||
!envBaseUrlShare &&
|
||||
sessionSharingConfig.enabled &&
|
||||
isValidUrl(String(sessionSharingConfig.baseUrl));
|
||||
|
||||
// Only load saved config from settings if the env variable is not provided.
|
||||
useEffect(() => {
|
||||
if (envBaseUrlShare) {
|
||||
// If env variable is set, save the forced configuration to settings
|
||||
const forcedConfig = {
|
||||
enabled: true,
|
||||
baseUrl: typeof envBaseUrlShare === 'string' ? envBaseUrlShare : '',
|
||||
};
|
||||
window.electron.setSetting('sessionSharing', forcedConfig);
|
||||
} else {
|
||||
window.electron.getSetting('sessionSharing').then((config) => {
|
||||
setSessionSharingConfig(config);
|
||||
});
|
||||
}
|
||||
}, [envBaseUrlShare]);
|
||||
|
||||
// Helper to check if the user's input is a valid URL
|
||||
function isValidUrl(value: string): boolean {
|
||||
if (!value) return false;
|
||||
try {
|
||||
new URL(value);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle sharing (only allowed when env is not set).
|
||||
const toggleSharing = async () => {
|
||||
if (envBaseUrlShare) {
|
||||
return; // Do nothing if the environment variable forces sharing.
|
||||
}
|
||||
const updated = { ...sessionSharingConfig, enabled: !sessionSharingConfig.enabled };
|
||||
setSessionSharingConfig(updated);
|
||||
await window.electron.setSetting('sessionSharing', updated);
|
||||
trackSettingToggled('session_sharing', updated.enabled);
|
||||
};
|
||||
|
||||
// Handle changes to the base URL field
|
||||
const handleBaseUrlChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newBaseUrl = e.target.value;
|
||||
setSessionSharingConfig((prev) => ({
|
||||
...prev,
|
||||
baseUrl: newBaseUrl,
|
||||
}));
|
||||
|
||||
// Clear previous test results when URL changes
|
||||
setTestResult({ status: null, message: '' });
|
||||
|
||||
if (isValidUrl(newBaseUrl)) {
|
||||
setUrlError('');
|
||||
const updated = { ...sessionSharingConfig, baseUrl: newBaseUrl };
|
||||
await window.electron.setSetting('sessionSharing', updated);
|
||||
} else {
|
||||
setUrlError(intl.formatMessage(i18n.invalidUrl));
|
||||
}
|
||||
};
|
||||
|
||||
// Test connection to the configured URL
|
||||
const testConnection = async () => {
|
||||
const baseUrl = sessionSharingConfig.baseUrl;
|
||||
if (!baseUrl) return;
|
||||
|
||||
setTestResult({ status: 'testing', message: intl.formatMessage(i18n.testingConnection) });
|
||||
|
||||
try {
|
||||
// Create an AbortController for timeout
|
||||
const controller = new AbortController();
|
||||
const timeoutId = window.setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
||||
|
||||
const response = await fetch(baseUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
window.clearTimeout(timeoutId);
|
||||
|
||||
// Consider any response (even 404) as a successful connection
|
||||
// since it means we can reach the server
|
||||
if (response.status < 500) {
|
||||
setTestResult({
|
||||
status: 'success',
|
||||
message: intl.formatMessage(i18n.connectionSuccess),
|
||||
});
|
||||
} else {
|
||||
setTestResult({
|
||||
status: 'error',
|
||||
message: intl.formatMessage(i18n.serverError, { status: response.status }),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Connection test failed:', error);
|
||||
let errorMessage = intl.formatMessage(i18n.connectionFailed);
|
||||
|
||||
if (error instanceof TypeError && error.message.includes('fetch')) {
|
||||
errorMessage += intl.formatMessage(i18n.unreachableServer);
|
||||
} else if (error instanceof Error) {
|
||||
if (error.name === 'AbortError') {
|
||||
errorMessage += intl.formatMessage(i18n.connectionTimedOut);
|
||||
} else {
|
||||
errorMessage += error.message;
|
||||
}
|
||||
} else {
|
||||
errorMessage += intl.formatMessage(i18n.unknownError);
|
||||
}
|
||||
|
||||
setTestResult({
|
||||
status: 'error',
|
||||
message: errorMessage,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="session-sharing" className="space-y-4 pr-4 mt-1">
|
||||
<Card className="pb-2">
|
||||
<CardHeader className="pb-0">
|
||||
<CardTitle>{intl.formatMessage(i18n.title)}</CardTitle>
|
||||
<CardDescription>
|
||||
{(envBaseUrlShare as string)
|
||||
? intl.formatMessage(i18n.descriptionConfigured)
|
||||
: intl.formatMessage(i18n.descriptionDefault)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="px-4 py-2">
|
||||
<div className="space-y-4">
|
||||
{/* Toggle for enabling session sharing */}
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="text-sm cursor-pointer">
|
||||
{(envBaseUrlShare as string)
|
||||
? intl.formatMessage(i18n.alreadyConfigured)
|
||||
: intl.formatMessage(i18n.enableSharing)}
|
||||
</label>
|
||||
|
||||
{envBaseUrlShare ? (
|
||||
<Lock className="w-5 h-5 text-text-secondary" />
|
||||
) : (
|
||||
<Switch
|
||||
checked={sessionSharingConfig.enabled}
|
||||
disabled={!!envBaseUrlShare}
|
||||
onCheckedChange={toggleSharing}
|
||||
variant="mono"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Base URL field (only visible if enabled) */}
|
||||
{sessionSharingConfig.enabled && (
|
||||
<div className="space-y-2 relative">
|
||||
<div className="flex items-center space-x-2">
|
||||
<label htmlFor="session-sharing-url" className="text-sm text-text-primary">
|
||||
{intl.formatMessage(i18n.baseUrl)}
|
||||
</label>
|
||||
{isUrlConfigured && <Check className="w-5 h-5 text-green-500" />}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
id="session-sharing-url"
|
||||
type="url"
|
||||
placeholder={intl.formatMessage(i18n.urlPlaceholder)}
|
||||
value={sessionSharingConfig.baseUrl}
|
||||
disabled={!!envBaseUrlShare}
|
||||
{...(envBaseUrlShare ? {} : { onChange: handleBaseUrlChange })}
|
||||
/>
|
||||
</div>
|
||||
{urlError && <p className="text-red-500 text-sm">{urlError}</p>}
|
||||
|
||||
{(isUrlConfigured || (envBaseUrlShare as string)) && (
|
||||
<div className="space-y-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={testConnection}
|
||||
disabled={testResult.status === 'testing'}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{testResult.status === 'testing' ? (
|
||||
<>
|
||||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
{intl.formatMessage(i18n.testing)}
|
||||
</>
|
||||
) : (
|
||||
intl.formatMessage(i18n.testConnection)
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* Test Results */}
|
||||
{testResult.status && testResult.status !== 'testing' && (
|
||||
<div
|
||||
className={`flex items-start gap-2 p-3 rounded-md text-sm ${
|
||||
testResult.status === 'success'
|
||||
? 'bg-green-50 text-green-800 border border-green-200'
|
||||
: 'bg-red-50 text-red-800 border border-red-200'
|
||||
}`}
|
||||
>
|
||||
{testResult.status === 'success' ? (
|
||||
<Check className="w-4 h-4 mt-0.5 flex-shrink-0" />
|
||||
) : (
|
||||
<AlertCircle className="w-4 h-4 mt-0.5 flex-shrink-0" />
|
||||
)}
|
||||
<span>{testResult.message}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "View session JSON"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "Cancel"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "Copy"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "This session doesn't contain any messages"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "No messages found"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "Error Loading Session Details"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "Try Again"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "Loading session details..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "Resume"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "Search history..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "Share"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "Share this session link to give others a read only view of your goose chat."
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "Share Session (beta)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "To enable session sharing, go to <b>Settings</b> > <b>Session</b> > <b>Session Sharing</b>."
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "Sharing..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "Failed to copy link to clipboard"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "Could not launch session: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "Failed to share session: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "Session encountered an error"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "Streaming"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "Session sharing has already been configured"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "Base URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "Connection failed."
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "Connection successful!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "Connection timed out. The server may be slow or unreachable."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "Session sharing is configured but fully opt-in — your sessions are only shared when you explicitly click the share button."
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "You can enable session sharing to share your sessions with others."
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "Enable session sharing"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "Invalid URL format. Please enter a valid URL (e.g. https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "Server error: HTTP {status}. The server may not be configured correctly."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "Test Connection"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "Testing..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "Testing connection..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "Session Sharing"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "Unknown error occurred."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "Unable to reach the server. Please check the URL and your network connection."
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "This session doesn't contain any messages"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "Session description updated successfully"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "Failed to load session details. Please try again later."
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "Loading..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "Configure how goose appears on your system"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "Settings"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "Loading session details..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "Shared Session"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "Close"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "Ver el JSON de la sesión"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "Cancelar"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "Copiar"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "Esta sesión no contiene ningún mensaje"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "No se encontraron mensajes"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "Error al cargar los detalles de la sesión"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "Reintentar"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "Cargando detalles de la sesión..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "Reanudar"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "Buscar en el historial..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "Compartir"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "Comparte este enlace de sesión para dar a otros una vista de solo lectura de tu chat de goose."
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "Compartir sesión (beta)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "Para habilitar el uso compartido de sesiones, ve a <b>Ajustes</b> > <b>Sesión</b> > <b>Compartir sesión</b>."
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "Compartiendo..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "No se pudo copiar el enlace al portapapeles"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "No se pudo abrir la sesión: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "No se pudo compartir la sesión: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "La sesión encontró un error"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "Streaming"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "El uso compartido de sesiones ya se configuró"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "URL base"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "Falló la conexión."
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "¡Conexión exitosa!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "Se agotó el tiempo de conexión. Puede que el servidor esté lento o inaccesible."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "El uso compartido de sesiones está configurado pero es totalmente opcional: tus sesiones solo se comparten cuando haces clic explícitamente en el botón de compartir."
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "Puedes habilitar el uso compartido de sesiones para compartir tus sesiones con otros."
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "Habilitar el uso compartido de sesiones"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "Formato de URL no válido. Ingresa una URL válida (p. ej., https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "Error del servidor: HTTP {status}. Puede que el servidor no esté configurado correctamente."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "Probar conexión"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "Probando..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "Probando la conexión..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "Compartir sesión"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "Ocurrió un error desconocido."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "No se pudo conectar con el servidor. Revisa la URL y tu conexión de red."
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "Esta sesión no contiene ningún mensaje"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "Descripción de la sesión actualizada correctamente"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "No se pudieron cargar los detalles de la sesión. Inténtalo de nuevo más tarde."
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "Cargando..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "Configura cómo se ve goose en tu sistema"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "Ajustes"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "Cargando detalles de la sesión..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "Sesión compartida"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "Cerrar"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "View session JSON"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "रद्द करें"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "प्रतिलिपि"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "इस सत्र में कोई संदेश नहीं है"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "कोई संदेश नहीं मिला"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "सत्र विवरण लोड करने में त्रुटि"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "पुनः प्रयास करें"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "सत्र विवरण लोड हो रहा है..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "बायोडाटा"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "खोज इतिहास..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "साझा करें"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "दूसरों को अपनी goose चैट का केवल पढ़ने योग्य दृश्य देने के लिए इस सत्र लिंक को साझा करें।"
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "शेयर सत्र (बीटा)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "सत्र साझाकरण सक्षम करने के लिए, <b>Settings</b> > <b>सत्र</b> > <b>सत्र साझाकरण</b> पर जाएं।"
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "साझा कर रहा हूँ..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "क्लिपबोर्ड पर लिंक कॉपी करने में विफल"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "सत्र लॉन्च नहीं किया जा सका: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "सत्र साझा करने में विफल: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "सत्र में एक त्रुटि आई"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "स्ट्रीमिंग"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "सत्र साझाकरण पहले ही कॉन्फ़िगर किया जा चुका है"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "आधार URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "कनेक्शन विफल।"
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "कनेक्शन सफल!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "कनेक्शन का समय समाप्त। सर्वर धीमा या पहुंच योग्य नहीं हो सकता है."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "सत्र साझाकरण कॉन्फ़िगर किया गया है लेकिन पूरी तरह से ऑप्ट-इन है - आपके सत्र केवल तभी साझा किए जाते हैं जब आप स्पष्ट रूप से शेयर बटन पर क्लिक करते हैं।"
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "आप अपने सत्रों को दूसरों के साथ साझा करने के लिए सत्र साझाकरण सक्षम कर सकते हैं।"
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "सत्र साझाकरण सक्षम करें"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "अमान्य URL प्रारूप. कृपया वैध URL दर्ज करें (उदा. https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "सर्वर त्रुटि: HTTP {status}। हो सकता है कि सर्वर ठीक से कॉन्फ़िगर न किया गया हो."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "परीक्षण कनेक्शन"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "परीक्षण..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "कनेक्शन का परीक्षण किया जा रहा है..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "सत्र साझाकरण"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "अज्ञात त्रुटि उत्पन्न हुई."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "सर्वर तक पहुंचने में असमर्थ. कृपया URL और अपने नेटवर्क कनेक्शन की जाँच करें।"
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "इस सत्र में कोई संदेश नहीं है"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "सत्र विवरण सफलतापूर्वक अपडेट किया गया"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "सत्र विवरण लोड करने में विफल. कृपया बाद में पुन: प्रयास करें।"
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "लोड हो रहा है..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "कॉन्फ़िगर करें कि goose आपके सिस्टम पर कैसे दिखाई देता है"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "Settings"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "सत्र विवरण लोड हो रहा है..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "साझा सत्र"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "बंद करें"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "セッションJSONを表示"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "キャンセル"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "コピー"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "このセッションにはメッセージがありません"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "メッセージが見つかりません"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "セッション詳細の読み込みエラー"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "再試行"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "セッション詳細を読み込み中..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "再開"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "履歴を検索..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "共有"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "このセッションリンクを共有すると、他のユーザーがあなたのgooseチャットを読み取り専用で表示できます。"
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "セッションを共有(ベータ)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "セッション共有を有効にするには、<b>設定</b> > <b>セッション</b> > <b>セッション共有</b> に移動してください。"
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "共有中..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "リンクをクリップボードにコピーできませんでした"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "セッションを起動できませんでした: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "セッションの共有に失敗しました: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "セッションでエラーが発生しました"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "ストリーミング中"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "セッション共有は既に設定済みです"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "ベースURL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "接続に失敗しました。"
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "接続に成功しました!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "接続がタイムアウトしました。サーバーが遅いか、到達できない可能性があります。"
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "セッション共有は設定済みですが、共有は完全に任意です。明示的に共有ボタンをクリックした場合にのみ、セッションが共有されます。"
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "セッション共有を有効にすると、セッションを他のユーザーと共有できます。"
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "セッション共有を有効化"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "URL形式が無効です。有効なURL(例: https://example.com/api)を入力してください。"
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "サーバーエラー: HTTP {status}。サーバーが正しく設定されていない可能性があります。"
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "接続をテスト"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "テスト中..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "接続をテスト中..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "セッション共有"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "不明なエラーが発生しました。"
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "サーバーに到達できません。URLとネットワーク接続を確認してください。"
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "このセッションにはメッセージがありません"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "セッションの説明を更新しました"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "セッションの詳細を読み込めませんでした。後でもう一度お試しください。"
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "読み込み中..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "システム上でのgooseの表示を設定します"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "設定"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "セッションの詳細を読み込み中..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "共有セッション"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "閉じる"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "세션 JSON 보기"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "취소"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "복사"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "이 세션에는 메시지가 없습니다."
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "메시지를 찾을 수 없습니다."
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "세션 세부정보를 로드하는 중 오류가 발생했습니다."
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "다시 시도"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "세션 세부정보 로드 중..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "재개"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "기록 검색..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "공유"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "이 세션 링크를 공유하여 다른 사람이 goose 채팅을 읽기 전용으로 볼 수 있게 하세요."
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "공유 세션(베타)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "세션 공유를 활성화하려면 <b>설정</b> > <b>세션</b> > <b>세션 공유</b>로 이동하세요."
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "공유 중..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "링크를 클립보드에 복사하지 못했습니다."
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "세션을 시작할 수 없습니다: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "세션 공유 실패: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "세션에 오류가 발생했습니다."
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "스트리밍"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "세션 공유가 이미 구성되었습니다."
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "기본 URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "연결에 실패했습니다."
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "연결 성공!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "연결 시간이 초과되었습니다. 서버가 느리거나 연결이 불가능할 수 있습니다."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "세션 공유가 설정되었지만, 세션은 공유 버튼을 직접 클릭할 때만 공유됩니다."
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "세션 공유를 켜면 내 세션을 다른 사람과 공유할 수 있습니다."
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "세션 공유 켜기"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "URL 형식이 잘못되었습니다. 올바른 URL을 입력하세요(예: https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "서버 오류: HTTP {status}. 서버가 올바르게 구성되지 않았을 수 있습니다."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "연결 테스트"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "테스트 중..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "연결 테스트 중..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "세션 공유"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "알 수 없는 오류가 발생했습니다."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "서버에 연결할 수 없습니다. URL과 네트워크 연결을 확인해주세요."
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "이 세션에는 메시지가 없습니다."
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "세션 설명이 업데이트되었습니다."
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "세션 세부정보를 로드하지 못했습니다. 나중에 다시 시도해 주세요."
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "로드 중..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "시스템에서 goose가 표시되는 방식을 설정합니다."
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "설정"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "세션 세부정보 로드 중..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "공유 세션"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "닫기"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "View session JSON"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "Отмена"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "Копировать"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "Этот сеанс не содержит сообщений"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "Сообщения не найдены"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "Ошибка загрузки сведений о сеансе"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "Повторить попытку"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "Загрузка сведений о сеансе..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "Продолжить"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "Поиск по истории..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "Поделиться"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "Поделитесь ссылкой на этот сеанс, чтобы другие могли просматривать ваш чат goose только для чтения."
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "Поделиться сеансом (бета)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "Чтобы включить общий доступ к сеансам, перейдите в <b>Настройки</b> > <b>Сеанс</b> > <b>Общий доступ к сеансам</b>."
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "Публикация..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "Не удалось скопировать ссылку в буфер обмена"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "Не удалось запустить сеанс: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "Не удалось поделиться сеансом: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "В сеансе возникла ошибка"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "Потоковая передача"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "Общий доступ к сеансам уже настроен"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "Базовый URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "Соединение не удалось."
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "Соединение успешно!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "Время соединения истекло. Сервер может быть медленным или недоступным."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "Общий доступ к сеансам настроен, но полностью доброволен — сеансы публикуются только когда вы явно нажимаете кнопку «Поделиться»."
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "Вы можете включить общий доступ к сеансам, чтобы делиться ими с другими."
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "Включить общий доступ к сеансам"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "Неверный формат URL. Введите корректный URL (например, https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "Ошибка сервера: HTTP {status}. Возможно, сервер настроен неверно."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "Проверить соединение"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "Проверка..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "Проверка соединения..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "Общий доступ к сеансам"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "Произошла неизвестная ошибка."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "Не удалось связаться с сервером. Проверьте URL и сетевое подключение."
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "Этот сеанс не содержит сообщений"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "Описание сеанса успешно обновлено"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "Не удалось загрузить сведения о сеансе. Повторите попытку позже."
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "Загрузка..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "Настройте, как goose отображается в вашей системе"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "Настройки"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "Загрузка сведений о сеансе..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "Общий сеанс"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "Закрыть"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "View session JSON"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "İptal"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "Kopyala"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "Bu oturum herhangi bir mesaj içermiyor"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "Mesaj bulunamadı"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "Oturum Ayrıntıları Yüklenirken Hata Oluştu"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "Tekrar Deneyin"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "Oturum ayrıntıları yükleniyor..."
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "Devam et"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "Arama geçmişi..."
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "Paylaş"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "Başkalarına goose sohbetinizin salt okunur görünümünü vermek için bu oturum bağlantısını paylaşın."
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "Oturumu Paylaş (beta)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "Oturum paylaşımını etkinleştirmek için <b>Ayarlar</b> > <b>Oturum</b> > <b>Oturum Paylaşımı</b> bölümüne gidin."
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "Paylaşılıyor..."
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "Bağlantı panoya kopyalanamadı"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "Oturum başlatılamadı: {error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "Oturum paylaşılamadı: {error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "Oturum bir hatayla karşılaştı"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "Akış"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "Oturum paylaşımı zaten yapılandırılmış"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "Temel URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "Bağlantı başarısız oldu."
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "Bağlantı başarılı!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "Bağlantı zaman aşımına uğradı. Sunucu yavaş olabilir veya erişilemez olabilir."
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "Oturum paylaşımı yapılandırılmıştır ancak tamamen etkinleştirilmiştir; oturumlarınız yalnızca paylaş düğmesini açıkça tıklattığınızda paylaşılır."
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "Oturumlarınızı başkalarıyla paylaşmak için oturum paylaşımını etkinleştirebilirsiniz."
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "Oturum paylaşımını etkinleştir"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "Geçersiz URL biçimi. Lütfen geçerli bir URL girin (ör. https://example.com/api)."
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "Sunucu hatası: HTTP {status}. Sunucu doğru yapılandırılmamış olabilir."
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "Bağlantıyı Test Et"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "Test ediliyor..."
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "Bağlantı test ediliyor..."
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "Oturum Paylaşımı"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "Bilinmeyen bir hata oluştu."
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "Sunucuya ulaşılamıyor. Lütfen URL'yi ve ağ bağlantınızı kontrol edin."
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "Bu oturum herhangi bir mesaj içermiyor"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "Oturum açıklaması başarıyla güncellendi"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "Oturum ayrıntıları yüklenemedi. Lütfen daha sonra tekrar deneyin."
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "Yükleniyor..."
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "goose'nin sisteminizde nasıl görüneceğini yapılandırın"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "Ayarlar"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "Oturum ayrıntıları yükleniyor..."
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "Paylaşılan Oturum"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "Kapat"
|
||||
},
|
||||
|
||||
@@ -3689,57 +3689,6 @@
|
||||
"sessionActionsHeader.viewJson": {
|
||||
"defaultMessage": "View session JSON"
|
||||
},
|
||||
"sessionHistory.cancel": {
|
||||
"defaultMessage": "取消"
|
||||
},
|
||||
"sessionHistory.copy": {
|
||||
"defaultMessage": "复制"
|
||||
},
|
||||
"sessionHistory.empty.description": {
|
||||
"defaultMessage": "此会话不包含任何消息"
|
||||
},
|
||||
"sessionHistory.empty.title": {
|
||||
"defaultMessage": "未找到消息"
|
||||
},
|
||||
"sessionHistory.error.loading": {
|
||||
"defaultMessage": "加载会话详情出错"
|
||||
},
|
||||
"sessionHistory.error.tryAgain": {
|
||||
"defaultMessage": "重试"
|
||||
},
|
||||
"sessionHistory.loading": {
|
||||
"defaultMessage": "正在加载会话详情…"
|
||||
},
|
||||
"sessionHistory.resume": {
|
||||
"defaultMessage": "恢复"
|
||||
},
|
||||
"sessionHistory.searchPlaceholder": {
|
||||
"defaultMessage": "搜索历史…"
|
||||
},
|
||||
"sessionHistory.share": {
|
||||
"defaultMessage": "分享"
|
||||
},
|
||||
"sessionHistory.shareModal.description": {
|
||||
"defaultMessage": "分享此会话链接,让其他人只读查看你的 goose 聊天。"
|
||||
},
|
||||
"sessionHistory.shareModal.title": {
|
||||
"defaultMessage": "分享会话(测试版)"
|
||||
},
|
||||
"sessionHistory.shareTooltip": {
|
||||
"defaultMessage": "若要启用会话分享,请前往 <b>设置</b> > <b>会话</b> > <b>会话分享</b>。"
|
||||
},
|
||||
"sessionHistory.sharing": {
|
||||
"defaultMessage": "分享中…"
|
||||
},
|
||||
"sessionHistory.toast.copyFailed": {
|
||||
"defaultMessage": "复制链接到剪贴板失败"
|
||||
},
|
||||
"sessionHistory.toast.launchFailed": {
|
||||
"defaultMessage": "无法启动会话:{error}"
|
||||
},
|
||||
"sessionHistory.toast.shareFailed": {
|
||||
"defaultMessage": "分享会话失败:{error}"
|
||||
},
|
||||
"sessionIndicators.error": {
|
||||
"defaultMessage": "会话发生错误"
|
||||
},
|
||||
@@ -3749,57 +3698,6 @@
|
||||
"sessionIndicators.streaming": {
|
||||
"defaultMessage": "流式传输中"
|
||||
},
|
||||
"sessionSharingSection.alreadyConfigured": {
|
||||
"defaultMessage": "会话分享已配置"
|
||||
},
|
||||
"sessionSharingSection.baseUrl": {
|
||||
"defaultMessage": "基础 URL"
|
||||
},
|
||||
"sessionSharingSection.connectionFailed": {
|
||||
"defaultMessage": "连接失败。"
|
||||
},
|
||||
"sessionSharingSection.connectionSuccess": {
|
||||
"defaultMessage": "连接成功!"
|
||||
},
|
||||
"sessionSharingSection.connectionTimedOut": {
|
||||
"defaultMessage": "连接超时。服务器可能响应缓慢或无法访问。"
|
||||
},
|
||||
"sessionSharingSection.descriptionConfigured": {
|
||||
"defaultMessage": "会话分享已配置,但完全是可选的 —— 你的会话只在你明确点击分享按钮时才会被分享。"
|
||||
},
|
||||
"sessionSharingSection.descriptionDefault": {
|
||||
"defaultMessage": "你可以启用会话分享,把会话分享给他人。"
|
||||
},
|
||||
"sessionSharingSection.enableSharing": {
|
||||
"defaultMessage": "启用会话分享"
|
||||
},
|
||||
"sessionSharingSection.invalidUrl": {
|
||||
"defaultMessage": "URL 格式无效。请输入有效的 URL(例如 https://example.com/api)。"
|
||||
},
|
||||
"sessionSharingSection.serverError": {
|
||||
"defaultMessage": "服务器错误:HTTP {status}。服务器可能未正确配置。"
|
||||
},
|
||||
"sessionSharingSection.testConnection": {
|
||||
"defaultMessage": "测试连接"
|
||||
},
|
||||
"sessionSharingSection.testing": {
|
||||
"defaultMessage": "测试中…"
|
||||
},
|
||||
"sessionSharingSection.testingConnection": {
|
||||
"defaultMessage": "正在测试连接…"
|
||||
},
|
||||
"sessionSharingSection.title": {
|
||||
"defaultMessage": "会话分享"
|
||||
},
|
||||
"sessionSharingSection.unknownError": {
|
||||
"defaultMessage": "发生未知错误。"
|
||||
},
|
||||
"sessionSharingSection.unreachableServer": {
|
||||
"defaultMessage": "无法连接到服务器。请检查 URL 和网络连接。"
|
||||
},
|
||||
"sessionSharingSection.urlPlaceholder": {
|
||||
"defaultMessage": "https://example.com/api"
|
||||
},
|
||||
"sessionViewComponents.empty.description": {
|
||||
"defaultMessage": "此会话不包含任何消息"
|
||||
},
|
||||
@@ -3950,12 +3848,6 @@
|
||||
"sessions.toast.updated": {
|
||||
"defaultMessage": "会话描述更新成功"
|
||||
},
|
||||
"sessionsView.error.failedToLoad": {
|
||||
"defaultMessage": "加载会话详情失败,请稍后重试。"
|
||||
},
|
||||
"sessionsView.loading": {
|
||||
"defaultMessage": "加载中…"
|
||||
},
|
||||
"settings.appearance.description": {
|
||||
"defaultMessage": "配置 goose 在你系统中的外观"
|
||||
},
|
||||
@@ -4127,12 +4019,6 @@
|
||||
"settingsView.title": {
|
||||
"defaultMessage": "设置"
|
||||
},
|
||||
"sharedSession.loading": {
|
||||
"defaultMessage": "正在加载会话详情…"
|
||||
},
|
||||
"sharedSession.title": {
|
||||
"defaultMessage": "共享会话"
|
||||
},
|
||||
"sheet.close": {
|
||||
"defaultMessage": "关闭"
|
||||
},
|
||||
|
||||
+1
-11
@@ -205,10 +205,6 @@ function getSettings(): Settings {
|
||||
...defaultSettings.keyboardShortcuts,
|
||||
...(stored.keyboardShortcuts ?? {}),
|
||||
},
|
||||
sessionSharing: {
|
||||
...defaultSettings.sessionSharing,
|
||||
...(stored.sessionSharing ?? {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
return defaultSettings;
|
||||
@@ -747,7 +743,6 @@ interface BundledConfig {
|
||||
defaultProvider?: string;
|
||||
defaultModel?: string;
|
||||
predefinedModels?: string;
|
||||
baseUrlShare?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
@@ -759,13 +754,11 @@ const getBundledConfig = (): BundledConfig => {
|
||||
defaultProvider: process.env.GOOSE_DEFAULT_PROVIDER,
|
||||
defaultModel: process.env.GOOSE_DEFAULT_MODEL,
|
||||
predefinedModels: process.env.GOOSE_PREDEFINED_MODELS,
|
||||
baseUrlShare: process.env.GOOSE_BASE_URL_SHARE,
|
||||
version: process.env.GOOSE_VERSION,
|
||||
};
|
||||
};
|
||||
|
||||
const { defaultProvider, defaultModel, predefinedModels, baseUrlShare, version } =
|
||||
getBundledConfig();
|
||||
const { defaultProvider, defaultModel, predefinedModels, version } = getBundledConfig();
|
||||
|
||||
const resolveGoosePathRoot = (): string | undefined => {
|
||||
const pathRoot = process.env.GOOSE_PATH_ROOT?.trim();
|
||||
@@ -985,7 +978,6 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => {
|
||||
GOOSE_API_HOST: baseUrl,
|
||||
GOOSE_WORKING_DIR: workingDir,
|
||||
REQUEST_DIR: dir,
|
||||
GOOSE_BASE_URL_SHARE: baseUrlShare,
|
||||
GOOSE_VERSION: version,
|
||||
recipeDeeplink: recipeDeeplink,
|
||||
recipeId: recipeId,
|
||||
@@ -1202,7 +1194,6 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => {
|
||||
skills: '/skills',
|
||||
permission: '/permission',
|
||||
ConfigureProviders: '/configure-providers',
|
||||
sharedSession: '/shared-session',
|
||||
};
|
||||
|
||||
if (viewType) {
|
||||
@@ -1721,7 +1712,6 @@ const validSettingKeys: Set<string> = new Set([
|
||||
'language',
|
||||
'responseStyle',
|
||||
'showPricing',
|
||||
'sessionSharing',
|
||||
'seenAnnouncementIds',
|
||||
'disableAutoDownload',
|
||||
]);
|
||||
|
||||
@@ -10,7 +10,6 @@ const localStorageKeyMap: Partial<Record<SettingKey, string>> = {
|
||||
useSystemTheme: 'use_system_theme',
|
||||
responseStyle: 'response_style',
|
||||
showPricing: 'show_pricing',
|
||||
sessionSharing: 'session_sharing_config',
|
||||
seenAnnouncementIds: 'seenAnnouncementIds',
|
||||
};
|
||||
|
||||
@@ -29,8 +28,6 @@ function parseLocalStorageValue<K extends SettingKey>(
|
||||
return rawValue as Settings[K];
|
||||
case 'showPricing':
|
||||
return (rawValue === 'true') as unknown as Settings[K];
|
||||
case 'sessionSharing':
|
||||
return JSON.parse(rawValue) as Settings[K];
|
||||
case 'seenAnnouncementIds':
|
||||
return JSON.parse(rawValue) as Settings[K];
|
||||
default:
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { fetchSharedSessionDetails, SharedSessionDetails } from './sharedSessions';
|
||||
import { View, ViewOptions } from './utils/navigationUtils';
|
||||
import { errorMessage } from './utils/conversionUtils';
|
||||
import { importSessionNostr } from './api';
|
||||
|
||||
/**
|
||||
* Imports a session from an encrypted Nostr deep link.
|
||||
* Separated from shared-session handling so callers can route independently.
|
||||
*/
|
||||
export async function importNostrSessionFromDeepLink(url: string): Promise<void> {
|
||||
await importSessionNostr({
|
||||
@@ -13,67 +9,3 @@ export async function importNostrSessionFromDeepLink(url: string): Promise<void>
|
||||
throwOnError: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles opening a shared session from a deep link
|
||||
* @param url The deep link URL (goose://sessions/:shareToken)
|
||||
* @param setView Function to set the current view
|
||||
* @param baseUrl Optional base URL for the session sharing API
|
||||
* @returns Promise that resolves when the session is opened
|
||||
*/
|
||||
export async function openSharedSessionFromDeepLink(
|
||||
url: string,
|
||||
setView: (view: View, options?: ViewOptions) => void,
|
||||
baseUrl?: string
|
||||
): Promise<SharedSessionDetails | null> {
|
||||
try {
|
||||
if (!url.startsWith('goose://sessions/')) {
|
||||
throw new Error('Invalid URL: URL must use the goose://sessions/ scheme');
|
||||
}
|
||||
|
||||
// Extract the share token from the URL
|
||||
const shareToken: string = url.replace('goose://sessions/', '');
|
||||
|
||||
if (!shareToken || shareToken.trim() === '') {
|
||||
throw new Error('Invalid URL: Missing share token');
|
||||
}
|
||||
|
||||
// If no baseUrl is provided, check if there's one in settings
|
||||
if (!baseUrl) {
|
||||
const config = await window.electron.getSetting('sessionSharing');
|
||||
if (config.enabled && config.baseUrl) {
|
||||
baseUrl = config.baseUrl;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Session sharing is not enabled or base URL is not configured. Check the settings page.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the shared session details
|
||||
const sessionDetails = await fetchSharedSessionDetails(baseUrl!, shareToken);
|
||||
|
||||
// Navigate to the shared session view
|
||||
setView('sharedSession', {
|
||||
sessionDetails,
|
||||
shareToken,
|
||||
baseUrl,
|
||||
});
|
||||
|
||||
return sessionDetails;
|
||||
} catch (error) {
|
||||
const errMsg = errorMessage(error, 'Unknown error');
|
||||
const fullErrorMessage = `Failed to open shared session: ${errMsg}`;
|
||||
console.error(fullErrorMessage);
|
||||
|
||||
// Navigate to the shared session view with the error instead of throwing
|
||||
setView('sharedSession', {
|
||||
sessionDetails: null,
|
||||
error: errMsg,
|
||||
shareToken: url.replace('goose://sessions/', ''),
|
||||
baseUrl,
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import { safeJsonParse } from './utils/conversionUtils';
|
||||
import { Message } from './api';
|
||||
|
||||
export interface SharedSessionDetails {
|
||||
share_token: string;
|
||||
created_at: number;
|
||||
base_url: string;
|
||||
description: string;
|
||||
working_dir: string;
|
||||
messages: Message[];
|
||||
message_count: number;
|
||||
total_tokens: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches details for a specific shared session
|
||||
* @param baseUrl The base URL for session sharing API
|
||||
* @param shareToken The share token of the session to fetch
|
||||
* @returns Promise with shared session details
|
||||
*/
|
||||
export async function fetchSharedSessionDetails(
|
||||
baseUrl: string,
|
||||
shareToken: string
|
||||
): Promise<SharedSessionDetails> {
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/sessions/share/${shareToken}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Origin: 'http://localhost:5173', // required to bypass Cloudflare security filter
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch shared session: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await safeJsonParse<SharedSessionDetails>(
|
||||
response,
|
||||
'Failed to parse shared session'
|
||||
);
|
||||
|
||||
if (baseUrl != data.base_url) {
|
||||
throw new Error(`Base URL mismatch for shared session: ${baseUrl} != ${data.base_url}`);
|
||||
}
|
||||
|
||||
return {
|
||||
share_token: data.share_token,
|
||||
created_at: data.created_at,
|
||||
base_url: data.base_url,
|
||||
description: data.description,
|
||||
working_dir: data.working_dir,
|
||||
messages: data.messages,
|
||||
message_count: data.message_count,
|
||||
total_tokens: data.total_tokens,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching shared session:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shared session
|
||||
* @param baseUrl The base URL for session sharing API
|
||||
* @param workingDir The working directory for the shared session
|
||||
* @param messages The messages to include in the shared session
|
||||
* @param description Description for the shared session
|
||||
* @param totalTokens Total token count for the session, or null if not available
|
||||
* @param userName The user name for who is sharing the session
|
||||
* @returns Promise with the share token
|
||||
*/
|
||||
export async function createSharedSession(
|
||||
baseUrl: string,
|
||||
workingDir: string,
|
||||
messages: Message[],
|
||||
description: string,
|
||||
totalTokens: number | null
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}/sessions/share`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
working_dir: workingDir,
|
||||
messages,
|
||||
description: description,
|
||||
base_url: baseUrl,
|
||||
total_tokens: totalTokens ?? null,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 302) {
|
||||
throw new Error(
|
||||
`Failed to create shared session. Please check that you are connected to VPN - ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
throw new Error(`Failed to create shared session: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await safeJsonParse<{ share_token: string }>(
|
||||
response,
|
||||
'Failed to parse shared session response'
|
||||
);
|
||||
return data.share_token;
|
||||
} catch (error) {
|
||||
console.error('Error creating shared session:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -70,10 +70,6 @@ const mockSettings: Record<string, unknown> = {
|
||||
language: 'system',
|
||||
responseStyle: 'concise',
|
||||
showPricing: true,
|
||||
sessionSharing: {
|
||||
enabled: false,
|
||||
baseUrl: '',
|
||||
},
|
||||
seenAnnouncementIds: [],
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ export type View =
|
||||
| 'settingsV2'
|
||||
| 'sessions'
|
||||
| 'schedules'
|
||||
| 'sharedSession'
|
||||
| 'loading'
|
||||
| 'recipes'
|
||||
| 'skills'
|
||||
@@ -23,15 +22,12 @@ export type View =
|
||||
export type ViewOptions = {
|
||||
showEnvVars?: boolean;
|
||||
deepLinkConfig?: unknown;
|
||||
sessionDetails?: unknown;
|
||||
error?: string;
|
||||
baseUrl?: string;
|
||||
recipe?: Recipe;
|
||||
parentView?: View;
|
||||
parentViewOptions?: ViewOptions;
|
||||
disableAnimation?: boolean;
|
||||
initialMessage?: UserInput;
|
||||
shareToken?: string;
|
||||
resumeSessionId?: string;
|
||||
pendingScheduleDeepLink?: string;
|
||||
};
|
||||
@@ -76,10 +72,6 @@ export const createNavigationHandler = (navigate: NavigateFunction) => {
|
||||
case 'ConfigureProviders':
|
||||
navigate('/configure-providers', { state: options });
|
||||
break;
|
||||
case 'sharedSession':
|
||||
navigate('/shared-session', { state: options });
|
||||
break;
|
||||
|
||||
case 'extensions':
|
||||
navigate('/extensions', { state: options });
|
||||
break;
|
||||
|
||||
@@ -23,11 +23,6 @@ export type DefaultKeyboardShortcuts = {
|
||||
[K in keyof KeyboardShortcuts]: string;
|
||||
};
|
||||
|
||||
export interface SessionSharingConfig {
|
||||
enabled: boolean;
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
export type LanguageSetting = 'system' | 'en' | 'es' | 'hi' | 'ja' | 'ko' | 'ru' | 'tr' | 'zh-CN';
|
||||
|
||||
export interface Settings {
|
||||
@@ -48,7 +43,6 @@ export interface Settings {
|
||||
language: LanguageSetting;
|
||||
responseStyle: string;
|
||||
showPricing: boolean;
|
||||
sessionSharing: SessionSharingConfig;
|
||||
seenAnnouncementIds: string[];
|
||||
}
|
||||
|
||||
@@ -89,10 +83,6 @@ export const defaultSettings: Settings = {
|
||||
language: 'system',
|
||||
responseStyle: 'concise',
|
||||
showPricing: true,
|
||||
sessionSharing: {
|
||||
enabled: false,
|
||||
baseUrl: '',
|
||||
},
|
||||
seenAnnouncementIds: [],
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user