Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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