Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ui/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@
"express": "^5.2.1",
"framer-motion": "^12.34.3",
"goose-acp-types": "file:../acp",
"i18next": "^24.0.0",
"katex": "^0.16.33",
"react-i18next": "^15.4.0",
"lodash": "^4.17.23",
"lucide-react": "^0.575.0",
"qrcode.react": "^4.2.0",
Expand Down
59 changes: 59 additions & 0 deletions ui/desktop/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion ui/desktop/src/components/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Outlet, useLocation } from 'react-router-dom';
import { motion } from 'framer-motion';
import { Menu } from 'lucide-react';
Expand All @@ -20,6 +21,7 @@ interface AppLayoutContentProps {

const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) => {
const location = useLocation();
const { t } = useTranslation();
const safeIsMacOS = (window?.electron?.platform || 'darwin') === 'darwin';
const chatContext = useChatContext();
const isOnPairRoute = location.pathname === '/pair';
Expand Down Expand Up @@ -123,7 +125,7 @@ const AppLayoutContent: React.FC<AppLayoutContentProps> = ({ activeSessions }) =
className="no-drag hover:!bg-background-tertiary"
variant="ghost"
size="xs"
title={isNavExpanded ? 'Close navigation' : 'Open navigation'}
title={isNavExpanded ? t('nav.closeNavigation') : t('nav.openNavigation')}
>
<Menu className="w-5 h-5" />
</Button>
Expand Down
4 changes: 3 additions & 1 deletion ui/desktop/src/components/Layout/CondensedRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { GripVertical, ChevronDown, ChevronRight, Plus } from 'lucide-react';
import { motion } from 'framer-motion';
import { cn } from '../../utils';
Expand Down Expand Up @@ -27,6 +28,7 @@ export const CondensedRenderer: React.FC<NavigationRendererProps> = ({
navFocusRef,
}) => {
const [chatPopoverOpen, setChatPopoverOpen] = useState(false);
const { t } = useTranslation();

const isVertical = navigationPosition === 'left' || navigationPosition === 'right';
const isTopPosition = navigationPosition === 'top';
Expand Down Expand Up @@ -176,7 +178,7 @@ export const CondensedRenderer: React.FC<NavigationRendererProps> = ({
'bg-background-tertiary hover:bg-background-inverse hover:text-text-inverse',
'flex items-center justify-center'
)}
title="New Chat"
title={t('chat.newChat')}
>
<Plus className="w-4 h-4" />
</motion.button>
Expand Down
8 changes: 5 additions & 3 deletions ui/desktop/src/components/Layout/navigation/SessionsList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { MessageSquare, ChefHat, Plus, History } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { SessionIndicators } from '../../SessionIndicators';
Expand Down Expand Up @@ -33,6 +34,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
onShowAll,
}) => {
const [editingSessionId, setEditingSessionId] = useState<string | null>(null);
const { t } = useTranslation();

const handleSaveSessionName = useCallback(
async (sessionId: string, newName: string) => {
Expand Down Expand Up @@ -68,7 +70,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
>
<div className="w-4 flex-shrink-0" />
<Plus className="w-4 h-4 flex-shrink-0 text-text-secondary" />
<span className="text-text-primary">Start New Chat</span>
<span className="text-text-primary">{t('chat.startNewChat')}</span>
</div>
)}

Expand Down Expand Up @@ -105,7 +107,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
<InlineEditText
value={getSessionDisplayName(session)}
onSave={(newName) => handleSaveSessionName(session.id, newName)}
placeholder="Untitled session"
placeholder={t('chat.untitledSession')}
disabled={isStreaming}
singleClickEdit={false}
className="truncate text-text-primary flex-1 !px-0 !py-0 hover:bg-transparent"
Expand Down Expand Up @@ -134,7 +136,7 @@ export const SessionsList: React.FC<SessionsListProps> = ({
>
<div className="w-4 flex-shrink-0" />
<History className="w-4 h-4 flex-shrink-0" />
<span>Show All</span>
<span>{t('chat.showAll')}</span>
</div>
)}
</div>
Expand Down
7 changes: 5 additions & 2 deletions ui/desktop/src/components/SetupModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Button } from './ui/button';

interface SetupModalProps {
Expand All @@ -20,6 +21,8 @@ export function SetupModal({
autoClose,
onClose,
}: SetupModalProps) {
const { t } = useTranslation();

useEffect(() => {
if (autoClose && onClose) {
const timer = window.setTimeout(() => {
Expand All @@ -45,15 +48,15 @@ export function SetupModal({
{onClose && (
<div className="mb-4">
<Button onClick={onClose} className="w-full">
Close
{t('setupModal.close')}
</Button>
<br />
</div>
)}

{showRetry && onRetry && (
<Button onClick={onRetry} className="w-full">
Retry Setup
{t('setupModal.retrySetup')}
</Button>
)}
</div>
Expand Down
34 changes: 17 additions & 17 deletions ui/desktop/src/components/TelemetryOptOutModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { BaseModal } from './ui/BaseModal';
import { Button } from './ui/button';
import { Goose } from './icons/Goose';
Expand All @@ -15,6 +16,7 @@ type TelemetryOptOutModalProps =

export default function TelemetryOptOutModal(props: TelemetryOptOutModalProps) {
const { read, upsert } = useConfig();
const { t } = useTranslation();
const isControlled = props.controlled;
const controlledIsOpen = isControlled ? props.isOpen : undefined;
const onClose = isControlled ? props.onClose : undefined;
Expand All @@ -41,15 +43,15 @@ export default function TelemetryOptOutModal(props: TelemetryOptOutModalProps) {
} catch (error) {
console.error('Failed to check telemetry config:', error);
toastService.error({
title: 'Configuration Error',
msg: 'Failed to check telemetry configuration.',
title: t('telemetry.configErrorTitle'),
msg: t('telemetry.configErrorMessage'),
traceback: error instanceof Error ? error.stack || '' : '',
});
}
};

checkTelemetryChoice();
}, [isControlled, read]);
}, [isControlled, read, t]);

const handleChoice = async (enabled: boolean) => {
setIsLoading(true);
Expand Down Expand Up @@ -88,15 +90,15 @@ export default function TelemetryOptOutModal(props: TelemetryOptOutModalProps) {
disabled={isLoading}
className="w-full h-[44px] rounded-lg"
>
Yes, share anonymous usage data
{t('telemetry.yesShare')}
</Button>
<Button
variant="ghost"
onClick={() => handleChoice(false)}
disabled={isLoading}
className="w-full h-[44px] rounded-lg text-text-secondary hover:text-text-primary"
>
No thanks
{t('telemetry.noThanks')}
</Button>
</div>
}
Expand All @@ -106,25 +108,23 @@ export default function TelemetryOptOutModal(props: TelemetryOptOutModalProps) {
<Goose className="size-10 text-text-primary" />
</div>
<h2 className="text-2xl font-regular dark:text-white text-gray-900 text-center mb-3">
Help improve goose
{t('telemetry.title')}
</h2>
<p className="text-text-primary text-sm mb-3">
Would you like to help improve goose by sharing anonymous usage data? This helps us
understand how goose is used and identify areas for improvement.
{t('telemetry.description')}
</p>
<div className="text-text-secondary text-xs space-y-1">
<p className="font-medium text-text-primary">What we collect:</p>
<p className="font-medium text-text-primary">{t('telemetry.whatWeCollectTitle')}</p>
<ul className="list-disc list-inside space-y-0.5 ml-1">
<li>Operating system, version, and architecture</li>
<li>goose version and install method</li>
<li>Provider and model used</li>
<li>Extensions and tool usage counts (names only)</li>
<li>Session metrics (duration, interaction count, token usage)</li>
<li>Error types (e.g., "rate_limit", "auth" - no details)</li>
<li>{t('telemetry.collectItems.os')}</li>
<li>{t('telemetry.collectItems.version')}</li>
<li>{t('telemetry.collectItems.provider')}</li>
<li>{t('telemetry.collectItems.extensions')}</li>
<li>{t('telemetry.collectItems.sessionMetrics')}</li>
<li>{t('telemetry.collectItems.errorTypes')}</li>
</ul>
<p className="mt-3 text-text-secondary">
We never collect your conversations, code, tool arguments, error messages, or any
personal data. You can change this setting anytime in Settings → App.
{t('telemetry.privacyNote')}
</p>
</div>
</div>
Expand Down
25 changes: 13 additions & 12 deletions ui/desktop/src/components/onboarding/PrivacyInfoModal.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog';
import { useTranslation } from 'react-i18next';

interface PrivacyInfoModalProps {
isOpen: boolean;
onClose: () => void;
}

export default function PrivacyInfoModal({ isOpen, onClose }: PrivacyInfoModalProps) {
const { t } = useTranslation();

return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="w-[440px]">
<DialogHeader>
<DialogTitle className="text-center">Privacy details</DialogTitle>
<DialogTitle className="text-center">{t('privacy.title')}</DialogTitle>
</DialogHeader>

<div>
<p className="text-text-muted text-sm mb-3">
Anonymous usage data helps us understand how goose is used and identify areas for
improvement.
{t('privacy.description')}
</p>
<p className="font-medium text-text-default text-sm mb-1.5">What we collect:</p>
<p className="font-medium text-text-default text-sm mb-1.5">{t('privacy.whatWeCollectTitle')}</p>
<ul className="text-text-muted text-sm list-disc list-outside space-y-0.5 ml-5 mb-3">
<li>Operating system, version, and architecture</li>
<li>goose version and install method</li>
<li>Provider and model used</li>
<li>Extensions and tool usage counts (names only)</li>
<li>Session metrics (duration, interaction count, token usage)</li>
<li>Error types (e.g., "rate_limit", "auth" - no details)</li>
<li>{t('telemetry.collectItems.os')}</li>
<li>{t('telemetry.collectItems.version')}</li>
<li>{t('telemetry.collectItems.provider')}</li>
<li>{t('telemetry.collectItems.extensions')}</li>
<li>{t('telemetry.collectItems.sessionMetrics')}</li>
<li>{t('telemetry.collectItems.errorTypes')}</li>
</ul>
<p className="text-text-muted text-sm">
We never collect your conversations, code, tool arguments, error messages, or any
personal data. You can change this setting anytime in Settings.
{t('privacy.privacyNote')}
</p>
</div>
</DialogContent>
Expand Down
Loading
Loading