diff --git a/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts b/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts index 8c648531e59..1988845cda8 100644 --- a/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts +++ b/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.test.ts @@ -66,6 +66,34 @@ describe('contextual tour overlay measurement', () => { ) }) + it('localizes the title, shortcut body, and action label of non-automation steps', async () => { + await setRendererUiLanguage('ko') + const target = document.createElement('div') + target.setAttribute('data-contextual-tour-target', 'terminal-pane-split-target') + target.getBoundingClientRect = () => new DOMRect(0, 0, 20, 20) + document.body.appendChild(target) + + const result = measureContextualTourOverlayRenderState({ + tour: getContextualTour('workspace-agent-sessions'), + activeStepIndex: 0, + sidebarOpen: true, + keybindings: undefined, + previousTelemetryTotalSteps: 0 + }) + + expect(result.kind).toBe('render') + if (result.kind !== 'render') { + throw new Error(`Expected render result, received ${result.kind}`) + } + expect(result.renderState.title).toBe('터미널 창 분할') + expect(result.renderState.body).not.toContain('{terminal.splitRight}') + expect(result.renderState.body).toContain('두 번째 터미널 창을 열거나') + expect(result.renderState.primaryAction).toEqual({ + kind: 'split-terminal-pane', + label: '터미널 분할' + }) + }) + it('keeps localized copy on its own step when a step is inserted before it', async () => { await setRendererUiLanguage('ko') const target = document.createElement('button') @@ -79,6 +107,7 @@ describe('contextual tour overlay measurement', () => { ...automations, steps: [ { + id: 'inserted-step', title: 'Inserted step', body: 'Added ahead of the localized steps.', targetSelector: '[data-contextual-tour-target="automations-create"]' diff --git a/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.ts b/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.ts index b90145682f0..d1491b6f519 100644 --- a/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.ts +++ b/src/renderer/src/components/contextual-tours/contextual-tour-overlay-measurement.ts @@ -1,5 +1,9 @@ import { formatShortcutLabel } from '@/hooks/useShortcutLabel' -import type { ContextualTour, ContextualTourId } from '../../../../shared/contextual-tours' +import type { + ContextualTour, + ContextualTourId, + ContextualTourStepAction +} from '../../../../shared/contextual-tours' import type { ContextualTourOutcome } from '../../../../shared/feature-education-telemetry' import { useAppStore } from '@/store' import { @@ -11,6 +15,7 @@ import { getVisibleContextualTourStepIndexes } from './contextual-tour-gate' import type { ActiveTourRenderState } from './ContextualTourOverlaySurface' +import { getLocalizedContextualTourStepCopy } from './contextual-tour-step-copy' import { translate } from '@/i18n/i18n' export type ContextualTourMeasurementAction = @@ -28,46 +33,6 @@ export type ContextualTourOverlayMeasurementResult = telemetryTotalSteps: number } -// Key translated thunks by stable step IDs so insertions cannot shift copy and language resolves at render time. -const LOCALIZED_STEP_COPY: Record string; body: () => string }> = { - 'automations-intro': { - title: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.intro.title', - 'What is an automation?' - ), - body: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.intro.body', - 'Automations run agent work on a schedule. Add an automation by clicking this button.' - ) - }, - 'client-hosted-browser-intro': { - title: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.client.hosted.browser.intro.title', - 'This page renders on your desktop' - ), - body: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.client.hosted.browser.intro.body', - 'Remote browser tabs now render on this device. Network traffic still goes through the remote host.' - ) - }, - 'automations-results': { - title: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.results.title', - 'Find the results' - ), - body: () => - translate( - 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.results.body', - 'Runs show when automations ran, what happened, and where to inspect their output.' - ) - } -} - export function getContextualTourDisplayProgress(args: { tour: ContextualTour visibleStepIndexes: readonly number[] @@ -128,13 +93,6 @@ export function measureContextualTourOverlayRenderState(args: { ) const activeStep = args.tour.steps[args.activeStepIndex] const target = activeStep ? getMeasurableContextualTourTarget(activeStep.targetSelector) : null - const localizedCopy = activeStep?.id ? LOCALIZED_STEP_COPY[activeStep.id] : undefined - const localizedTitle = localizedCopy ? localizedCopy.title() : activeStep?.title - const localizedBody = localizedCopy - ? localizedCopy.body() - : activeStep - ? getContextualTourStepCopy(activeStep) - : undefined const progress = getContextualTourDisplayProgress({ tour: args.tour, visibleStepIndexes, @@ -161,6 +119,7 @@ export function measureContextualTourOverlayRenderState(args: { return { kind: 'cancel' } } + const localizedCopy = getLocalizedContextualTourStepCopy(activeStep.id) const sidebarAlreadyVisible = activeStep.primaryAction?.kind === 'show-worktrees' && args.sidebarOpen const primaryAction = sidebarAlreadyVisible @@ -171,8 +130,10 @@ export function measureContextualTourOverlayRenderState(args: { 'Next' ) } as const) - : activeStep.primaryAction - const secondaryAction = sidebarAlreadyVisible ? undefined : activeStep.secondaryAction + : withLocalizedLabel(activeStep.primaryAction, localizedCopy?.primaryActionLabel) + const secondaryAction = sidebarAlreadyVisible + ? undefined + : withLocalizedLabel(activeStep.secondaryAction, localizedCopy?.secondaryActionLabel) return { kind: 'render', @@ -181,9 +142,9 @@ export function measureContextualTourOverlayRenderState(args: { rect: target.rect, targetElement: target.element, progress, - title: localizedTitle ?? activeStep.title, + title: localizedCopy?.title ?? activeStep.title, body: formatContextualTourStepCopy( - localizedBody ?? getContextualTourStepCopy(activeStep), + localizedCopy?.body ?? getContextualTourStepCopy(activeStep), args.keybindings ), control: activeStep.control, @@ -269,6 +230,13 @@ export function getContextualTourCleanupOutcome( : 'cancelled' } +function withLocalizedLabel( + action: ContextualTourStepAction | undefined, + label: string | undefined +): ContextualTourStepAction | undefined { + return action && label ? { ...action, label } : action +} + function formatContextualTourStepCopy( copy: string, keybindings: Parameters[1] diff --git a/src/renderer/src/components/contextual-tours/contextual-tour-step-copy.ts b/src/renderer/src/components/contextual-tours/contextual-tour-step-copy.ts new file mode 100644 index 00000000000..5b61a4e6205 --- /dev/null +++ b/src/renderer/src/components/contextual-tours/contextual-tour-step-copy.ts @@ -0,0 +1,213 @@ +import { translate } from '@/i18n/i18n' +import type { ContextualTourStepId } from '../../../../shared/contextual-tours' + +export type LocalizedContextualTourStepCopy = { + title: string + body: string + primaryActionLabel?: string + secondaryActionLabel?: string +} + +type StepCopyThunk = () => LocalizedContextualTourStepCopy + +// Thunks resolve translate() in the language active at render time; `satisfies` makes a step without copy a type error. +const LOCALIZED_STEP_COPY: Partial> = { + 'workspace-board-plan': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.board.plan.title', + 'Plan work on the board' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.board.plan.body', + 'Use the board when you want to see workspaces by status instead of by project.' + ) + }), + 'workspace-board-lanes': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.board.lanes.title', + 'Move work through lanes' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.board.lanes.body', + 'Drag workspaces between lanes as their status changes.' + ) + }), + 'agent-sessions-split': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.agent.sessions.split.title', + 'Split a terminal pane' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.agent.sessions.split.body', + 'Open a second terminal pane with {terminal.splitRight}, or right-click the pane for split options.' + ), + primaryActionLabel: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.agent.sessions.split.action', + 'Split terminal' + ) + }), + 'agent-sessions-parallel': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.agent.sessions.parallel.title', + 'Start another task in parallel' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.agent.sessions.parallel.body', + 'Each worktree gets its own branch, so parallel work stays separate.' + ) + }), + 'browser-grab': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.grab.title', + 'Grab page context for agents' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.grab.body', + "Use the grab tool to copy a page element's context for agents." + ) + }), + 'browser-annotate': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.annotate.title', + 'Mark design feedback in place' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.annotate.body', + 'Annotate elements and send those notes to an agent.' + ) + }), + 'browser-logins': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.logins.title', + 'Stay logged in' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.browser.logins.body', + 'Bring your existing logins into Orca to stay signed in immediately.' + ) + }), + 'client-hosted-browser-intro': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.client.hosted.browser.intro.title', + 'This page renders on your desktop' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.client.hosted.browser.intro.body', + 'Remote browser tabs now render on this device. Network traffic still goes through the remote host.' + ), + primaryActionLabel: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.client.hosted.browser.intro.action', + 'Got it' + ), + secondaryActionLabel: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.client.hosted.browser.intro.secondaryAction', + 'Browser settings' + ) + }), + 'tasks-source': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.source.title', + 'Choose the work source' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.source.body', + 'Switch between connected providers and project filters without changing pages.' + ) + }), + 'tasks-filter': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.filter.title', + 'Filter to the work you need' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.filter.body', + 'Use presets and search to narrow issues, reviews, merge requests, or tasks.' + ) + }), + 'tasks-start': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.start.title', + 'Start from work items' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.tasks.start.body', + 'Use Start or Open on a task, issue, review, or merge request to bring its context into a workspace.' + ) + }), + 'automations-intro': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.intro.title', + 'What is an automation?' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.intro.body', + 'Automations run agent work on a schedule. Add an automation by clicking this button.' + ) + }), + 'automations-results': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.results.title', + 'Find the results' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.overlay.measurement.automations.results.body', + 'Runs show when automations ran, what happened, and where to inspect their output.' + ) + }), + 'floating-workspace-repos': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.floating.workspace.repos.title', + 'Run an agent across every repo' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.floating.workspace.repos.body', + 'Agents here run in any folder you choose. Point one at the directory above your services to work across all your repos at once.' + ) + }), + 'floating-workspace-scratchpad': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.floating.workspace.scratchpad.title', + 'Or use it as a scratchpad' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.floating.workspace.scratchpad.body', + 'Open agents, scratch terminals, notes, and browser tabs without cluttering the worktree you’re focused on.' + ) + }), + 'workspace-creation-project': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.project.title', + 'Pick a project' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.project.body', + 'Orca isolates each task in its own worktree, branched off your base.' + ) + }), + 'workspace-creation-name': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.name.title', + 'Name it, or start from existing work' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.name.body', + 'Start from a linked task for a short issue or PR name. Or leave it blank to auto-name it from your first agent message.' + ) + }), + 'workspace-creation-agent': () => ({ + title: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.agent.title', + 'Choose what agent starts the work' + ), + body: translate( + 'auto.components.contextual.tours.contextual.tour.step.copy.workspace.creation.agent.body', + 'Pick the agent that should be opened when this worktree is created.' + ) + }) +} satisfies Record + +export function getLocalizedContextualTourStepCopy( + stepId: string +): LocalizedContextualTourStepCopy | undefined { + return LOCALIZED_STEP_COPY[stepId]?.() +} diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 4f35129944d..ca469b3b9d7 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -17259,6 +17259,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "Plan work on the board", + "body": "Use the board when you want to see workspaces by status instead of by project." + }, + "lanes": { + "title": "Move work through lanes", + "body": "Drag workspaces between lanes as their status changes." + } + }, + "creation": { + "project": { + "title": "Pick a project", + "body": "Orca isolates each task in its own worktree, branched off your base." + }, + "name": { + "title": "Name it, or start from existing work", + "body": "Start from a linked task for a short issue or PR name. Or leave it blank to auto-name it from your first agent message." + }, + "agent": { + "title": "Choose what agent starts the work", + "body": "Pick the agent that should be opened when this worktree is created." + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "Split a terminal pane", + "body": "Open a second terminal pane with {terminal.splitRight}, or right-click the pane for split options.", + "action": "Split terminal" + }, + "parallel": { + "title": "Start another task in parallel", + "body": "Each worktree gets its own branch, so parallel work stays separate." + } + } + }, + "browser": { + "grab": { + "title": "Grab page context for agents", + "body": "Use the grab tool to copy a page element's context for agents." + }, + "annotate": { + "title": "Mark design feedback in place", + "body": "Annotate elements and send those notes to an agent." + }, + "logins": { + "title": "Stay logged in", + "body": "Bring your existing logins into Orca to stay signed in immediately." + } + }, + "tasks": { + "source": { + "title": "Choose the work source", + "body": "Switch between connected providers and project filters without changing pages." + }, + "filter": { + "title": "Filter to the work you need", + "body": "Use presets and search to narrow issues, reviews, merge requests, or tasks." + }, + "start": { + "title": "Start from work items", + "body": "Use Start or Open on a task, issue, review, or merge request to bring its context into a workspace." + } + }, + "floating": { + "workspace": { + "repos": { + "title": "Run an agent across every repo", + "body": "Agents here run in any folder you choose. Point one at the directory above your services to work across all your repos at once." + }, + "scratchpad": { + "title": "Or use it as a scratchpad", + "body": "Open agents, scratch terminals, notes, and browser tabs without cluttering the worktree you’re focused on." + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "Got it", + "secondaryAction": "Browser settings" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index 5b52b72d8b8..8ce02f0e3ed 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -16830,6 +16830,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "Planifica el trabajo en el tablero", + "body": "Usa el tablero cuando quieras ver los espacios de trabajo por estado en lugar de por proyecto." + }, + "lanes": { + "title": "Mueve el trabajo entre carriles", + "body": "Arrastra los espacios de trabajo entre carriles a medida que cambia su estado." + } + }, + "creation": { + "project": { + "title": "Elige un proyecto", + "body": "Orca aísla cada tarea en su propio worktree, creado a partir de tu rama base." + }, + "name": { + "title": "Ponle nombre o usa un elemento de trabajo existente", + "body": "Parte de una tarea vinculada para usar un nombre corto de incidencia o PR. O déjalo en blanco para nombrarlo automáticamente a partir de tu primer mensaje al agente." + }, + "agent": { + "title": "Elige qué agente empieza el trabajo", + "body": "Elige el agente que debe abrirse cuando se cree este worktree." + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "Divide un panel de terminal", + "body": "Abre un segundo panel de terminal con {terminal.splitRight} o haz clic derecho en el panel para ver las opciones de división.", + "action": "Dividir terminal" + }, + "parallel": { + "title": "Empieza otra tarea en paralelo", + "body": "Cada worktree tiene su propia rama, así que el trabajo en paralelo se mantiene separado." + } + } + }, + "browser": { + "grab": { + "title": "Captura el contexto de la página para los agentes", + "body": "Usa la herramienta de captura para copiar el contexto de un elemento de la página para los agentes." + }, + "annotate": { + "title": "Marca el feedback de diseño sobre la página", + "body": "Anota elementos y envía esas notas a un agente." + }, + "logins": { + "title": "Mantén la sesión iniciada", + "body": "Lleva tus inicios de sesión existentes a Orca para seguir conectado de inmediato." + } + }, + "tasks": { + "source": { + "title": "Elige el origen del trabajo", + "body": "Cambia entre proveedores conectados y filtros de proyecto sin cambiar de página." + }, + "filter": { + "title": "Filtra hasta el trabajo que necesitas", + "body": "Usa los preajustes y la búsqueda para acotar incidencias, revisiones, merge requests o tareas." + }, + "start": { + "title": "Empieza desde los elementos de trabajo", + "body": "Usa Iniciar o Abrir en una tarea, incidencia, revisión o merge request para llevar su contexto a un espacio de trabajo." + } + }, + "floating": { + "workspace": { + "repos": { + "title": "Ejecuta un agente en todos los repositorios", + "body": "Los agentes de aquí se ejecutan en la carpeta que elijas. Apunta uno al directorio superior a tus servicios para trabajar en todos tus repositorios a la vez." + }, + "scratchpad": { + "title": "O úsalo como bloc de notas", + "body": "Abre agentes, terminales temporales, notas y pestañas del navegador sin saturar el worktree en el que estás concentrado." + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "Entendido", + "secondaryAction": "Configuración del navegador" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index 8406e85efa7..7e28695bf2f 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -16975,6 +16975,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "Planifiez le travail sur le tableau", + "body": "Utilisez le tableau pour voir les espaces de travail par statut plutôt que par projet." + }, + "lanes": { + "title": "Faites avancer le travail entre les colonnes", + "body": "Faites glisser les espaces de travail d’une colonne à l’autre à mesure que leur statut change." + } + }, + "creation": { + "project": { + "title": "Choisissez un projet", + "body": "Orca isole chaque tâche dans son propre worktree, créé à partir de votre branche de base." + }, + "name": { + "title": "Nommez-le, ou partez d’un travail existant", + "body": "Partez d’une tâche liée pour obtenir un nom court d’issue ou de PR. Ou laissez le champ vide pour le nommer automatiquement à partir de votre premier message à l’agent." + }, + "agent": { + "title": "Choisissez l’agent qui démarre le travail", + "body": "Choisissez l’agent à ouvrir lors de la création de ce worktree." + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "Scindez un volet de terminal", + "body": "Ouvrez un second volet de terminal avec {terminal.splitRight}, ou faites un clic droit sur le volet pour afficher les options de division.", + "action": "Scinder le terminal" + }, + "parallel": { + "title": "Lancez une autre tâche en parallèle", + "body": "Chaque worktree a sa propre branche, ce qui garde le travail en parallèle séparé." + } + } + }, + "browser": { + "grab": { + "title": "Récupérez le contexte de la page pour les agents", + "body": "Utilisez l’outil de capture pour copier le contexte d’un élément de la page pour les agents." + }, + "annotate": { + "title": "Annotez vos retours de design sur place", + "body": "Annotez des éléments et envoyez ces notes à un agent." + }, + "logins": { + "title": "Restez connecté", + "body": "Importez vos connexions existantes dans Orca pour rester connecté immédiatement." + } + }, + "tasks": { + "source": { + "title": "Choisissez la source du travail", + "body": "Passez d’un fournisseur connecté ou d’un filtre de projet à l’autre sans changer de page." + }, + "filter": { + "title": "Filtrez le travail dont vous avez besoin", + "body": "Utilisez les préréglages et la recherche pour affiner les issues, revues, merge requests ou tâches." + }, + "start": { + "title": "Partez des éléments de travail", + "body": "Utilisez Démarrer ou Ouvrir sur une tâche, une issue, une revue ou une merge request pour importer son contexte dans un espace de travail." + } + }, + "floating": { + "workspace": { + "repos": { + "title": "Lancez un agent sur tous vos dépôts", + "body": "Ici, les agents s’exécutent dans le dossier de votre choix. Pointez-en un vers le dossier parent de vos services pour travailler sur tous vos dépôts à la fois." + }, + "scratchpad": { + "title": "Ou utilisez-le comme brouillon", + "body": "Ouvrez des agents, des terminaux temporaires, des notes et des onglets de navigateur sans encombrer le worktree sur lequel vous êtes concentré." + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "Compris", + "secondaryAction": "Paramètres du navigateur" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index 31c0a7f44f2..f7071bf6051 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -16814,6 +16814,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "ボードで作業を計画する", + "body": "プロジェクト別ではなく状態別にワークスペースを見たいときはボードを使います。" + }, + "lanes": { + "title": "レーン間で作業を動かす", + "body": "状態が変わったら、ワークスペースをレーン間にドラッグします。" + } + }, + "creation": { + "project": { + "title": "プロジェクトを選ぶ", + "body": "Orca は各タスクをベースブランチから分岐した専用のワークツリーに分離します。" + }, + "name": { + "title": "名前を付けるか、既存の作業から始める", + "body": "リンクされたタスクから始めると、短い issue 名や PR 名が使われます。空欄のままにすると、最初の Agent メッセージから自動で名前が付きます。" + }, + "agent": { + "title": "作業を開始する Agent を選ぶ", + "body": "このワークツリーが作成されたときに開く Agent を選びます。" + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "ターミナルペインを分割する", + "body": "{terminal.splitRight} で 2 つ目のターミナルペインを開くか、ペインを右クリックして分割オプションを表示します。", + "action": "ターミナルを分割" + }, + "parallel": { + "title": "別のタスクを並行して始める", + "body": "ワークツリーごとに専用のブランチがあるため、並行して進める作業が混ざりません。" + } + } + }, + "browser": { + "grab": { + "title": "Agent 向けにページのコンテキストを取得する", + "body": "grab ツールでページ要素のコンテキストをコピーして Agent に渡します。" + }, + "annotate": { + "title": "デザインのフィードバックをその場で残す", + "body": "要素に注釈を付けて、そのメモを Agent に送ります。" + }, + "logins": { + "title": "ログイン状態を保つ", + "body": "既存のログイン情報を Orca に取り込んで、すぐにログイン状態を保ちます。" + } + }, + "tasks": { + "source": { + "title": "作業のソースを選ぶ", + "body": "ページを移動せずに、接続済みのプロバイダーとプロジェクトフィルターを切り替えます。" + }, + "filter": { + "title": "必要な作業だけに絞り込む", + "body": "プリセットと検索で issues、レビュー、merge requests、タスクを絞り込みます。" + }, + "start": { + "title": "作業アイテムから始める", + "body": "タスク、issue、レビュー、merge request で開始または開くを押すと、そのコンテキストをワークスペースに取り込めます。" + } + }, + "floating": { + "workspace": { + "repos": { + "title": "すべてのリポジトリで Agent を実行する", + "body": "ここの Agent は選んだ任意のフォルダーで実行されます。サービスの 1 つ上のディレクトリを指定すれば、すべてのリポジトリをまとめて扱えます。" + }, + "scratchpad": { + "title": "スクラッチパッドとしても使える", + "body": "集中しているワークツリーを散らかさずに、Agent、一時的なターミナル、ノート、ブラウザタブを開けます。" + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "了解", + "secondaryAction": "ブラウザ設定" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index c5b35d0ad9e..5b2cad126d4 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -16814,6 +16814,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "보드에서 작업 계획하기", + "body": "프로젝트별이 아니라 상태별로 워크스페이스를 보고 싶을 때 보드를 사용하세요." + }, + "lanes": { + "title": "레인을 따라 작업 옮기기", + "body": "상태가 바뀌면 워크스페이스를 레인 사이로 끌어다 놓으세요." + } + }, + "creation": { + "project": { + "title": "프로젝트 선택", + "body": "Orca는 각 작업을 기준 브랜치에서 만든 자체 워크트리로 분리합니다." + }, + "name": { + "title": "이름을 정하거나 기존 작업에서 시작하기", + "body": "연결된 작업에서 시작하면 짧은 이슈나 PR 이름을 사용합니다. 비워 두면 첫 agent 메시지에서 이름이 자동으로 지정됩니다." + }, + "agent": { + "title": "작업을 시작할 agent 선택", + "body": "이 워크트리가 생성될 때 열릴 agent를 선택하세요." + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "터미널 창 분할", + "body": "{terminal.splitRight} 키로 두 번째 터미널 창을 열거나, 창을 우클릭해 분할 옵션을 확인하세요.", + "action": "터미널 분할" + }, + "parallel": { + "title": "다른 작업을 병렬로 시작하기", + "body": "워크트리마다 자체 브랜치를 가지므로 병렬로 진행하는 작업이 서로 섞이지 않습니다." + } + } + }, + "browser": { + "grab": { + "title": "agent를 위한 페이지 컨텍스트 가져오기", + "body": "grab 도구로 페이지 요소의 컨텍스트를 복사해 agent에 전달하세요." + }, + "annotate": { + "title": "디자인 피드백을 화면에 바로 남기기", + "body": "요소에 주석을 달고 그 메모를 agent에 보내세요." + }, + "logins": { + "title": "로그인 상태 유지", + "body": "기존 로그인 정보를 Orca로 가져와 바로 로그인 상태를 유지하세요." + } + }, + "tasks": { + "source": { + "title": "작업 소스 선택", + "body": "페이지를 이동하지 않고 연결된 제공자와 프로젝트 필터를 전환하세요." + }, + "filter": { + "title": "필요한 작업만 필터링하기", + "body": "프리셋과 검색으로 이슈, 리뷰, merge requests, 작업의 범위를 좁히세요." + }, + "start": { + "title": "작업 항목에서 시작하기", + "body": "작업, 이슈, 리뷰, merge request에서 시작 또는 열기를 눌러 해당 컨텍스트를 워크스페이스로 가져오세요." + } + }, + "floating": { + "workspace": { + "repos": { + "title": "agent 하나로 모든 repo 다루기", + "body": "이곳의 agent는 선택한 어떤 폴더에서도 실행됩니다. 서비스들의 상위 디렉터리를 지정하면 모든 repo에서 한 번에 작업할 수 있습니다." + }, + "scratchpad": { + "title": "메모장처럼 사용하기", + "body": "집중하고 있는 워크트리를 어지럽히지 않고 agent, 임시 터미널, 노트, 브라우저 탭을 열 수 있습니다." + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "확인", + "secondaryAction": "브라우저 설정" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index 4218c93441c..c5b8673b604 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -18518,6 +18518,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "Planeje o trabalho no quadro", + "body": "Use o quadro quando quiser ver os espaços de trabalho por status em vez de por projeto." + }, + "lanes": { + "title": "Mova o trabalho entre as colunas", + "body": "Arraste os espaços de trabalho entre as colunas conforme o status deles muda." + } + }, + "creation": { + "project": { + "title": "Escolha um projeto", + "body": "O Orca isola cada tarefa em seu próprio worktree, criado a partir da sua branch base." + }, + "name": { + "title": "Dê um nome ou comece de um trabalho existente", + "body": "Comece de uma tarefa vinculada para usar um nome curto de issue ou PR. Ou deixe em branco para nomeá-lo automaticamente a partir da sua primeira mensagem ao agente." + }, + "agent": { + "title": "Escolha qual agente inicia o trabalho", + "body": "Escolha o agente que deve ser aberto quando este worktree for criado." + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "Divida um painel de terminal", + "body": "Abra um segundo painel de terminal com {terminal.splitRight} ou clique com o botão direito no painel para ver as opções de divisão.", + "action": "Dividir terminal" + }, + "parallel": { + "title": "Comece outra tarefa em paralelo", + "body": "Cada worktree tem sua própria branch, então o trabalho em paralelo fica separado." + } + } + }, + "browser": { + "grab": { + "title": "Capture o contexto da página para os agentes", + "body": "Use a ferramenta de captura para copiar o contexto de um elemento da página para os agentes." + }, + "annotate": { + "title": "Marque o feedback de design no lugar", + "body": "Anote elementos e envie essas notas para um agente." + }, + "logins": { + "title": "Continue conectado", + "body": "Traga seus logins existentes para o Orca e continue conectado imediatamente." + } + }, + "tasks": { + "source": { + "title": "Escolha a origem do trabalho", + "body": "Alterne entre provedores conectados e filtros de projeto sem mudar de página." + }, + "filter": { + "title": "Filtre o trabalho de que você precisa", + "body": "Use predefinições e a busca para restringir issues, revisões, merge requests ou tarefas." + }, + "start": { + "title": "Comece a partir dos itens de trabalho", + "body": "Use Iniciar ou Abrir em uma tarefa, issue, revisão ou merge request para levar o contexto dela a um espaço de trabalho." + } + }, + "floating": { + "workspace": { + "repos": { + "title": "Execute um agente em todos os repositórios", + "body": "Os agentes aqui são executados em qualquer pasta que você escolher. Aponte um para o diretório acima dos seus serviços para trabalhar em todos os seus repositórios de uma vez." + }, + "scratchpad": { + "title": "Ou use como rascunho", + "body": "Abra agentes, terminais temporários, notas e abas do navegador sem bagunçar o worktree em que você está focado." + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "Entendi", + "secondaryAction": "Configurações do navegador" + } + } + } + } + } } } } diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 2de5651e6e9..cb8451e0f8a 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -16814,6 +16814,99 @@ } } } + }, + "step": { + "copy": { + "workspace": { + "board": { + "plan": { + "title": "在看板中规划工作", + "body": "想按状态而不是按项目查看工作区时,请使用看板。" + }, + "lanes": { + "title": "在泳道之间移动工作", + "body": "状态变化时,把工作区拖到相应的泳道。" + } + }, + "creation": { + "project": { + "title": "选择项目", + "body": "Orca 会把每个任务隔离到基于基础分支创建的独立工作树中。" + }, + "name": { + "title": "命名,或从已有工作开始", + "body": "从关联的任务开始可获得简短的 issue 或 PR 名称。留空则会根据你的第一条智能体消息自动命名。" + }, + "agent": { + "title": "选择由哪个智能体开始工作", + "body": "选择创建此工作树时要打开的智能体。" + } + } + }, + "agent": { + "sessions": { + "split": { + "title": "拆分终端窗格", + "body": "使用 {terminal.splitRight} 打开第二个终端窗格,或右键点击窗格查看拆分选项。", + "action": "拆分终端" + }, + "parallel": { + "title": "并行开始另一个任务", + "body": "每个工作树都有自己的分支,因此并行进行的工作互不干扰。" + } + } + }, + "browser": { + "grab": { + "title": "为智能体抓取页面上下文", + "body": "使用抓取工具复制页面元素的上下文,供智能体使用。" + }, + "annotate": { + "title": "就地标注设计反馈", + "body": "为元素添加批注,并把这些备注发送给智能体。" + }, + "logins": { + "title": "保持登录状态", + "body": "把现有登录信息导入 Orca,立即保持登录状态。" + } + }, + "tasks": { + "source": { + "title": "选择工作来源", + "body": "无需切换页面即可在已连接的提供方和项目筛选之间切换。" + }, + "filter": { + "title": "筛选出你需要的工作", + "body": "使用预设和搜索来缩小 issues、评审、merge requests 或任务的范围。" + }, + "start": { + "title": "从工作项开始", + "body": "在任务、issue、评审或 merge request 上点击开始或打开,即可把其上下文带入工作区。" + } + }, + "floating": { + "workspace": { + "repos": { + "title": "在所有存储库中运行智能体", + "body": "这里的智能体可在你选择的任意文件夹中运行。把它指向服务所在目录的上一级,即可一次性处理所有存储库。" + }, + "scratchpad": { + "title": "也可当作草稿区使用", + "body": "打开智能体、临时终端、笔记和浏览器标签页,同时不会弄乱你正在专注的工作树。" + } + } + }, + "client": { + "hosted": { + "browser": { + "intro": { + "action": "知道了", + "secondaryAction": "浏览器设置" + } + } + } + } + } } } } diff --git a/src/shared/contextual-tours.ts b/src/shared/contextual-tours.ts index 77519a6b8cf..10e0876598a 100644 --- a/src/shared/contextual-tours.ts +++ b/src/shared/contextual-tours.ts @@ -33,7 +33,7 @@ export type ContextualTourStepPlacement = 'top' | 'right' | 'bottom' | 'left' export type ContextualTourStep = { // Stable anchor for localized copy — position-keyed translations shift onto the wrong step when one is inserted. - id?: string + id: string title: string body: string targetSelector: string @@ -59,6 +59,7 @@ export const CONTEXTUAL_TOURS = [ id: 'workspace-board', steps: [ { + id: 'workspace-board-plan', title: 'Plan work on the board', body: 'Use the board when you want to see workspaces by status instead of by project.', targetSelector: '[data-contextual-tour-target="workspace-board-center"]', @@ -66,6 +67,7 @@ export const CONTEXTUAL_TOURS = [ preferredPlacement: 'bottom' }, { + id: 'workspace-board-lanes', title: 'Move work through lanes', body: 'Drag workspaces between lanes as their status changes.', targetSelector: @@ -77,6 +79,7 @@ export const CONTEXTUAL_TOURS = [ id: 'workspace-agent-sessions', steps: [ { + id: 'agent-sessions-split', title: 'Split a terminal pane', body: 'Open a second terminal pane with {terminal.splitRight}, or right-click the pane for split options.', targetSelector: @@ -87,6 +90,7 @@ export const CONTEXTUAL_TOURS = [ advanceOnFeatureInteraction: 'terminal-pane-split' }, { + id: 'agent-sessions-parallel', title: 'Start another task in parallel', body: 'Each worktree gets its own branch, so parallel work stays separate.', targetSelector: '[data-contextual-tour-target="workspace-create-control"]', @@ -100,6 +104,7 @@ export const CONTEXTUAL_TOURS = [ id: 'browser', steps: [ { + id: 'browser-grab', title: 'Grab page context for agents', body: "Use the grab tool to copy a page element's context for agents.", targetSelector: '[data-contextual-tour-target="browser-grab-control"]', @@ -107,12 +112,14 @@ export const CONTEXTUAL_TOURS = [ preferredPlacement: 'bottom' }, { + id: 'browser-annotate', title: 'Mark design feedback in place', body: 'Annotate elements and send those notes to an agent.', targetSelector: '[data-contextual-tour-target="browser-annotation-control"]', preferredPlacement: 'bottom' }, { + id: 'browser-logins', title: 'Stay logged in', body: 'Bring your existing logins into Orca to stay signed in immediately.', // Prefer visible Import; use overflow after the hint is dismissed. @@ -141,17 +148,20 @@ export const CONTEXTUAL_TOURS = [ id: 'tasks', steps: [ { + id: 'tasks-source', title: 'Choose the work source', body: 'Switch between connected providers and project filters without changing pages.', targetSelector: '[data-contextual-tour-target="tasks-source-filters"]', requiredForStart: true }, { + id: 'tasks-filter', title: 'Filter to the work you need', body: 'Use presets and search to narrow issues, reviews, merge requests, or tasks.', targetSelector: '[data-contextual-tour-target="tasks-search-presets"]' }, { + id: 'tasks-start', title: 'Start from work items', body: 'Use Start or Open on a task, issue, review, or merge request to bring its context into a workspace.', targetSelector: @@ -181,6 +191,7 @@ export const CONTEXTUAL_TOURS = [ id: 'floating-workspace', steps: [ { + id: 'floating-workspace-repos', title: 'Run an agent across every repo', body: 'Agents here run in any folder you choose. Point one at the directory above your services to work across all your repos at once.', // Action anchors exist only in empty state; use the panel when floating tabs already exist. @@ -190,6 +201,7 @@ export const CONTEXTUAL_TOURS = [ preferredPlacement: 'left' }, { + id: 'floating-workspace-scratchpad', title: 'Or use it as a scratchpad', body: 'Open agents, scratch terminals, notes, and browser tabs without cluttering the worktree you’re focused on.', targetSelector: @@ -203,18 +215,21 @@ export const CONTEXTUAL_TOURS = [ allowedActiveModals: ['new-workspace-composer'], steps: [ { + id: 'workspace-creation-project', title: 'Pick a project', body: 'Orca isolates each task in its own worktree, branched off your base.', targetSelector: '[data-contextual-tour-target="workspace-creation-project"]', requiredForStart: true }, { + id: 'workspace-creation-name', title: 'Name it, or start from existing work', body: 'Start from a linked task for a short issue or PR name. Or leave it blank to auto-name it from your first agent message.', targetSelector: '[data-contextual-tour-target="workspace-creation-name"]', control: { kind: 'auto-rename-branch-from-work' } }, { + id: 'workspace-creation-agent', title: 'Choose what agent starts the work', body: 'Pick the agent that should be opened when this worktree is created.', targetSelector: '[data-contextual-tour-target="workspace-creation-agent"]' @@ -223,6 +238,8 @@ export const CONTEXTUAL_TOURS = [ } ] as const satisfies readonly ContextualTour[] +export type ContextualTourStepId = (typeof CONTEXTUAL_TOURS)[number]['steps'][number]['id'] + export const CONTEXTUAL_TOUR_IDS = CONTEXTUAL_TOURS.map((tour) => tour.id) export function isContextualTourId(value: unknown): value is ContextualTourId {