FN-6770: localize dashboard workflow and task UI strings
Localize dashboard workflow, task, setup, and pull request UI copy through the shared i18n resources. - Replace hardcoded dashboard strings in workflow, task detail/chat/comment, setup wizard, PR, and related modal components with translation lookups. - Add app/common namespace keys across English and secondary locale catalogs, plus regenerated i18n resource types. - Add the published package changeset and clear the now-localized component files from the i18n lint deferral list. Files changed: .changeset/fn-6770-localize-workflow-task-pr.md | 5 + i18next.config.ts | 17 +- packages/dashboard/app/components/Board.tsx | 10 +- .../dashboard/app/components/PrCreateModal.tsx | 38 +-- packages/dashboard/app/components/PrPanel.tsx | 2 +- .../dashboard/app/components/PullRequestView.tsx | 51 ++-- .../dashboard/app/components/SettingsModal.tsx | 92 +++---- .../dashboard/app/components/SetupWizardModal.tsx | 8 +- packages/dashboard/app/components/TaskChatTab.tsx | 107 ++++---- packages/dashboard/app/components/TaskComments.tsx | 2 +- .../dashboard/app/components/TaskDetailModal.tsx | 54 ++-- .../app/components/WorkflowNodeEditor.tsx | 109 +++++---- .../app/components/WorkflowResultsTab.tsx | 190 +++++++------- .../dashboard/app/components/WorkflowSelector.tsx | 20 +- packages/i18n/locales/en/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/en/common.json | 28 +-- packages/i18n/locales/es/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/es/common.json | 28 +-- packages/i18n/locales/fr/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/fr/common.json | 28 +-- packages/i18n/locales/ko/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/ko/common.json | 28 +-- packages/i18n/locales/zh-CN/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/zh-CN/common.json | 28 +-- packages/i18n/locales/zh-TW/app.json | 268 ++++++++++++++++++-- packages/i18n/locales/zh-TW/common.json | 28 +-- packages/i18n/src/resources.d.ts | 272 ++++++++++++++++++--- 27 files changed, 2144 insertions(+), 609 deletions(-) Fusion-Task-Id: FN-6770 Fusion-Task-Lineage: 393b6b8a-1902-4e46-a53e-b4a0793022ca
This commit is contained in:
5
.changeset/fn-6770-localize-workflow-task-pr.md
Normal file
5
.changeset/fn-6770-localize-workflow-task-pr.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Localized the dashboard workflow/task/setup/PR component cluster and removed the obsolete i18n lint deferrals for those files so the hardcoded-string guardrail scans them again.
|
||||||
@@ -6,23 +6,8 @@ import {
|
|||||||
|
|
||||||
|
|
||||||
const DEFERRED_I18N_LINT_FILES = [
|
const DEFERRED_I18N_LINT_FILES = [
|
||||||
// FNXC:i18n-LintBaseline 2026-06-19-00:00:
|
|
||||||
// These exact files still carry pre-existing user-facing copy debt after FN-6749 restored the guardrail scope and token suppression.
|
|
||||||
// Keep the deferral file-scoped and remove entries as those follow-ups localize each cluster.
|
|
||||||
// FNXC:i18n-LintBaseline 2026-06-20-00:00:
|
// FNXC:i18n-LintBaseline 2026-06-20-00:00:
|
||||||
// FN-6771 localized the settings/sections cluster, so those files are no longer deferred and must stay covered by i18n lint.
|
// FN-6770 and FN-6771 localized the remaining workflow/task/setup/PR and settings/sections clusters, so no dashboard component files remain deferred from hardcoded-string lint.
|
||||||
"packages/dashboard/app/components/WorkflowSelector.tsx",
|
|
||||||
"packages/dashboard/app/components/WorkflowResultsTab.tsx",
|
|
||||||
"packages/dashboard/app/components/WorkflowNodeEditor.tsx",
|
|
||||||
"packages/dashboard/app/components/TaskDetailModal.tsx",
|
|
||||||
"packages/dashboard/app/components/TaskComments.tsx",
|
|
||||||
"packages/dashboard/app/components/TaskChatTab.tsx",
|
|
||||||
"packages/dashboard/app/components/SetupWizardModal.tsx",
|
|
||||||
"packages/dashboard/app/components/SettingsModal.tsx",
|
|
||||||
"packages/dashboard/app/components/PullRequestView.tsx",
|
|
||||||
"packages/dashboard/app/components/PrPanel.tsx",
|
|
||||||
"packages/dashboard/app/components/PrCreateModal.tsx",
|
|
||||||
"packages/dashboard/app/components/Board.tsx",
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Column } from "./Column";
|
|||||||
import "./Lane.css";
|
import "./Lane.css";
|
||||||
import type { ToastType } from "../hooks/useToast";
|
import type { ToastType } from "../hooks/useToast";
|
||||||
import { useState, useMemo, useEffect, useCallback, useRef } from "react";
|
import { useState, useMemo, useEffect, useCallback, useRef } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Pencil, Plus } from "lucide-react";
|
import { Pencil, Plus } from "lucide-react";
|
||||||
import { fetchWorkflowSteps, fetchBoardWorkflows, promoteTask, type ModelInfo, type BoardWorkflowDefinition, type BoardWorkflowsPayload } from "../api";
|
import { fetchWorkflowSteps, fetchBoardWorkflows, promoteTask, type ModelInfo, type BoardWorkflowDefinition, type BoardWorkflowsPayload } from "../api";
|
||||||
import { useBlockerFanout } from "../hooks/useBlockerFanout";
|
import { useBlockerFanout } from "../hooks/useBlockerFanout";
|
||||||
@@ -124,6 +125,7 @@ function areWorkflowNameLookupsEqual(previous: ReadonlyMap<string, string>, next
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask, onOpenDetail, onOpenGroupModal, addToast, onQuickCreate, onNewTask, autoMerge, onToggleAutoMerge, globalPaused, onUpdateTask, onRetryTask, onArchiveTask, onUnarchiveTask, onDeleteTask, onArchiveAllDone, onLoadArchivedTasks, searchQuery = "", availableModels, onPlanningMode, onSubtaskBreakdown, onOpenDetailWithTab, favoriteProviders, favoriteModels, onToggleFavorite, onToggleModelFavorite, taskStuckTimeoutMs, onOpenMission, staleHighFanoutBlockerAgeThresholdMs, lastFetchTimeMs, prAuthAvailable, onOpenWorkflowEditor, onCreateWorkflow }: BoardProps) {
|
export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask, onOpenDetail, onOpenGroupModal, addToast, onQuickCreate, onNewTask, autoMerge, onToggleAutoMerge, globalPaused, onUpdateTask, onRetryTask, onArchiveTask, onUnarchiveTask, onDeleteTask, onArchiveAllDone, onLoadArchivedTasks, searchQuery = "", availableModels, onPlanningMode, onSubtaskBreakdown, onOpenDetailWithTab, favoriteProviders, favoriteModels, onToggleFavorite, onToggleModelFavorite, taskStuckTimeoutMs, onOpenMission, staleHighFanoutBlockerAgeThresholdMs, lastFetchTimeMs, prAuthAvailable, onOpenWorkflowEditor, onCreateWorkflow }: BoardProps) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const [archivedCollapsed, setArchivedCollapsed] = useState(true);
|
const [archivedCollapsed, setArchivedCollapsed] = useState(true);
|
||||||
const archivedLoadedRef = useRef(false);
|
const archivedLoadedRef = useRef(false);
|
||||||
const [workflowStepNameLookup, setWorkflowStepNameLookup] = useState<ReadonlyMap<string, string>>(EMPTY_WORKFLOW_STEP_NAME_LOOKUP);
|
const [workflowStepNameLookup, setWorkflowStepNameLookup] = useState<ReadonlyMap<string, string>>(EMPTY_WORKFLOW_STEP_NAME_LOOKUP);
|
||||||
@@ -527,8 +529,8 @@ export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask
|
|||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-sm board-workflow-edit-btn"
|
className="btn btn-icon btn-sm board-workflow-edit-btn"
|
||||||
onClick={() => onOpenWorkflowEditor(selectedWorkflow.id)}
|
onClick={() => onOpenWorkflowEditor(selectedWorkflow.id)}
|
||||||
title="Edit workflows"
|
title={t("board.workflow.edit", "Edit workflows")}
|
||||||
aria-label="Edit workflows"
|
aria-label={t("board.workflow.edit", "Edit workflows")}
|
||||||
>
|
>
|
||||||
<Pencil size={15} />
|
<Pencil size={15} />
|
||||||
</button>
|
</button>
|
||||||
@@ -538,8 +540,8 @@ export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask
|
|||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-sm board-workflow-create-btn"
|
className="btn btn-icon btn-sm board-workflow-create-btn"
|
||||||
onClick={onCreateWorkflow}
|
onClick={onCreateWorkflow}
|
||||||
title="New workflow"
|
title={t("board.workflow.new", "New workflow")}
|
||||||
aria-label="New workflow"
|
aria-label={t("board.workflow.new", "New workflow")}
|
||||||
>
|
>
|
||||||
<Plus size={15} />
|
<Plus size={15} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ export function PrCreateModal({
|
|||||||
<>
|
<>
|
||||||
<section className="pr-create-modal__section">
|
<section className="pr-create-modal__section">
|
||||||
<h3 className="pr-create-modal__section-title">{t("pr.preflightChecks", "Pre-flight checks")}</h3>
|
<h3 className="pr-create-modal__section-title">{t("pr.preflightChecks", "Pre-flight checks")}</h3>
|
||||||
{preflightLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />Loading pre-flight checks…</div> : null}
|
{preflightLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />{t("pr.loadingPreflight", "Loading pre-flight checks…")}</div> : null}
|
||||||
{preflightError ? <div className="form-error pr-error" role="alert"><p>{preflightError}</p></div> : null}
|
{preflightError ? <div className="form-error pr-error" role="alert"><p>{preflightError}</p></div> : null}
|
||||||
{!preflightLoading && !preflightError ? (
|
{!preflightLoading && !preflightError ? (
|
||||||
<>
|
<>
|
||||||
@@ -511,8 +511,8 @@ export function PrCreateModal({
|
|||||||
{!preflight?.branchOnRemote ? (
|
{!preflight?.branchOnRemote ? (
|
||||||
<div className="card pr-create-modal__preflight-remediation">
|
<div className="card pr-create-modal__preflight-remediation">
|
||||||
<div className="pr-create-modal__conflict-copy">
|
<div className="pr-create-modal__conflict-copy">
|
||||||
<p className="pr-create-modal__conflict-title">Push branch to remote</p>
|
<p className="pr-create-modal__conflict-title">{t("pr.pushBranch.title", "Push branch to remote")}</p>
|
||||||
<p className="pr-create-modal__conflict-message">Fusion will push this task's branch to origin so the PR can be created.</p>
|
<p className="pr-create-modal__conflict-message">{t("pr.pushBranch.message", "Fusion will push this task's branch to origin so the PR can be created.")}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -521,15 +521,15 @@ export function PrCreateModal({
|
|||||||
disabled={pushingBranch || preflightLoading || !baseBranch}
|
disabled={pushingBranch || preflightLoading || !baseBranch}
|
||||||
>
|
>
|
||||||
{pushingBranch ? <RefreshCw size={14} className="spin" /> : null}
|
{pushingBranch ? <RefreshCw size={14} className="spin" /> : null}
|
||||||
Push branch to remote
|
{t("pr.pushBranch.button", "Push branch to remote")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{preflight?.conflictsWithBase ? (
|
{preflight?.conflictsWithBase ? (
|
||||||
<div className="card pr-create-modal__conflict-resolution">
|
<div className="card pr-create-modal__conflict-resolution">
|
||||||
<div className="pr-create-modal__conflict-copy">
|
<div className="pr-create-modal__conflict-copy">
|
||||||
<p className="pr-create-modal__conflict-title">Resolve conflicts with AI</p>
|
<p className="pr-create-modal__conflict-title">{t("pr.resolveConflicts.title", "Resolve conflicts with AI")}</p>
|
||||||
<p className="pr-create-modal__conflict-message">Fusion will use AI to resolve conflicts on this branch and push it.</p>
|
<p className="pr-create-modal__conflict-message">{t("pr.resolveConflicts.message", "Fusion will use AI to resolve conflicts on this branch and push it.")}</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -538,7 +538,7 @@ export function PrCreateModal({
|
|||||||
disabled={resolvingConflicts || preflightLoading || !baseBranch}
|
disabled={resolvingConflicts || preflightLoading || !baseBranch}
|
||||||
>
|
>
|
||||||
{resolvingConflicts ? <RefreshCw size={14} className="spin" /> : null}
|
{resolvingConflicts ? <RefreshCw size={14} className="spin" /> : null}
|
||||||
Resolve conflicts with AI
|
{t("pr.resolveConflicts.button", "Resolve conflicts with AI")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -554,7 +554,7 @@ export function PrCreateModal({
|
|||||||
{userEditedTitle && <button type="button" className="btn btn-sm" onClick={() => { setTitle(aiTitle); setUserEditedTitle(false); }}>{t("pr.revertToAi", "Revert to AI version")}</button>}
|
{userEditedTitle && <button type="button" className="btn btn-sm" onClick={() => { setTitle(aiTitle); setUserEditedTitle(false); }}>{t("pr.revertToAi", "Revert to AI version")}</button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{metadataLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />Generating AI title…</div> : null}
|
{metadataLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />{t("pr.generatingTitle", "Generating AI title…")}</div> : null}
|
||||||
{metadataError ? <div className="form-error pr-error" role="alert"><p>{metadataError}</p></div> : null}
|
{metadataError ? <div className="form-error pr-error" role="alert"><p>{metadataError}</p></div> : null}
|
||||||
<input id="pr-create-modal-title" className="input" value={title} onChange={(event) => { setTitle(event.target.value); setUserEditedTitle(true); }} />
|
<input id="pr-create-modal-title" className="input" value={title} onChange={(event) => { setTitle(event.target.value); setUserEditedTitle(true); }} />
|
||||||
</section>
|
</section>
|
||||||
@@ -567,7 +567,7 @@ export function PrCreateModal({
|
|||||||
{userEditedBody && <button type="button" className="btn btn-sm" onClick={() => { setBody(aiBody); setUserEditedBody(false); }}>{t("pr.revertToAi", "Revert to AI version")}</button>}
|
{userEditedBody && <button type="button" className="btn btn-sm" onClick={() => { setBody(aiBody); setUserEditedBody(false); }}>{t("pr.revertToAi", "Revert to AI version")}</button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{metadataLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />Generating AI body…</div> : null}
|
{metadataLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />{t("pr.generatingBody", "Generating AI body…")}</div> : null}
|
||||||
<textarea id="pr-create-modal-body" className="input pr-create-modal__body-input" value={body} onChange={(event) => { setBody(event.target.value); setUserEditedBody(true); }} rows={8} />
|
<textarea id="pr-create-modal-body" className="input pr-create-modal__body-input" value={body} onChange={(event) => { setBody(event.target.value); setUserEditedBody(true); }} rows={8} />
|
||||||
{templateUsed && <p className="pr-create-template-hint">{t("pr.usingTemplate", "Using <code>.github/pull_request_template.md</code>")}</p>}
|
{templateUsed && <p className="pr-create-template-hint">{t("pr.usingTemplate", "Using <code>.github/pull_request_template.md</code>")}</p>}
|
||||||
</section>
|
</section>
|
||||||
@@ -575,7 +575,7 @@ export function PrCreateModal({
|
|||||||
<section className="pr-create-modal__section pr-create-modal__grid-two">
|
<section className="pr-create-modal__section pr-create-modal__grid-two">
|
||||||
<div>
|
<div>
|
||||||
<label className="pr-create-modal__label" htmlFor="pr-create-modal-base">{t("pr.baseBranch", "Base branch")}</label>
|
<label className="pr-create-modal__label" htmlFor="pr-create-modal-base">{t("pr.baseBranch", "Base branch")}</label>
|
||||||
{optionsLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />Loading PR options…</div> : null}
|
{optionsLoading ? <div className="pr-create-modal__loading pr-create-modal__section-loading"><span className="status-dot status-dot--pending" aria-hidden="true" />{t("pr.loadingOptions", "Loading PR options…")}</div> : null}
|
||||||
{optionsError ? <div className="form-error pr-error" role="alert"><p>{optionsError}</p></div> : null}
|
{optionsError ? <div className="form-error pr-error" role="alert"><p>{optionsError}</p></div> : null}
|
||||||
<select id="pr-create-modal-base" className="select" value={baseBranch} onChange={(event) => void handleBaseChange(event.target.value)} disabled={optionsLoading || Boolean(optionsError) || (options?.baseBranches?.length ?? 0) === 0}>
|
<select id="pr-create-modal-base" className="select" value={baseBranch} onChange={(event) => void handleBaseChange(event.target.value)} disabled={optionsLoading || Boolean(optionsError) || (options?.baseBranches?.length ?? 0) === 0}>
|
||||||
{(options?.baseBranches ?? []).map((branch) => <option key={branch} value={branch}>{branch}</option>)}
|
{(options?.baseBranches ?? []).map((branch) => <option key={branch} value={branch}>{branch}</option>)}
|
||||||
@@ -588,7 +588,7 @@ export function PrCreateModal({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<OptionChips
|
<OptionChips
|
||||||
label="Reviewers"
|
label={t("pr.reviewers", "Reviewers")}
|
||||||
options={options?.reviewers ?? []}
|
options={options?.reviewers ?? []}
|
||||||
selected={reviewers}
|
selected={reviewers}
|
||||||
onChange={setReviewers}
|
onChange={setReviewers}
|
||||||
@@ -596,7 +596,7 @@ export function PrCreateModal({
|
|||||||
getLabel={(option) => option.name ? `${option.name} (@${option.login})` : `@${option.login}`}
|
getLabel={(option) => option.name ? `${option.name} (@${option.login})` : `@${option.login}`}
|
||||||
/>
|
/>
|
||||||
<OptionChips
|
<OptionChips
|
||||||
label="Assignees"
|
label={t("pr.assignees", "Assignees")}
|
||||||
options={options?.assignees ?? []}
|
options={options?.assignees ?? []}
|
||||||
selected={assignees}
|
selected={assignees}
|
||||||
onChange={setAssignees}
|
onChange={setAssignees}
|
||||||
@@ -604,7 +604,7 @@ export function PrCreateModal({
|
|||||||
getLabel={(option) => option.name ? `${option.name} (@${option.login})` : `@${option.login}`}
|
getLabel={(option) => option.name ? `${option.name} (@${option.login})` : `@${option.login}`}
|
||||||
/>
|
/>
|
||||||
<OptionChips
|
<OptionChips
|
||||||
label="Labels"
|
label={t("pr.labels", "Labels")}
|
||||||
options={options?.labels ?? []}
|
options={options?.labels ?? []}
|
||||||
selected={labels}
|
selected={labels}
|
||||||
onChange={setLabels}
|
onChange={setLabels}
|
||||||
@@ -617,7 +617,7 @@ export function PrCreateModal({
|
|||||||
<summary>{t("pr.previewTitle", "Diff & commit preview")}</summary>
|
<summary>{t("pr.previewTitle", "Diff & commit preview")}</summary>
|
||||||
<div className="pr-create-modal__preview">
|
<div className="pr-create-modal__preview">
|
||||||
<h4>{t("pr.commitsLabel", "Commits")}</h4>
|
<h4>{t("pr.commitsLabel", "Commits")}</h4>
|
||||||
{!preflightLoading && !preflightError && (preflight?.commits?.length ?? 0) === 0 ? <p className="pr-create-template-hint">No commits found.</p> : null}
|
{!preflightLoading && !preflightError && (preflight?.commits?.length ?? 0) === 0 ? <p className="pr-create-template-hint">{t("pr.noCommits", "No commits found.")}</p> : null}
|
||||||
{(preflight?.commits ?? []).map((commit) => (
|
{(preflight?.commits ?? []).map((commit) => (
|
||||||
<div className="pr-create-modal__commit-row" key={commit.sha}>
|
<div className="pr-create-modal__commit-row" key={commit.sha}>
|
||||||
<code>{commit.sha.slice(0, 7)}</code>
|
<code>{commit.sha.slice(0, 7)}</code>
|
||||||
@@ -626,7 +626,7 @@ export function PrCreateModal({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<h4>{t("pr.changedFilesLabel", "Changed files")}</h4>
|
<h4>{t("pr.changedFilesLabel", "Changed files")}</h4>
|
||||||
{!preflightLoading && !preflightError && (preflight?.changedFiles?.length ?? 0) === 0 ? <p className="pr-create-template-hint">No changed files detected.</p> : null}
|
{!preflightLoading && !preflightError && (preflight?.changedFiles?.length ?? 0) === 0 ? <p className="pr-create-template-hint">{t("pr.noChangedFiles", "No changed files detected.")}</p> : null}
|
||||||
{(preflight?.changedFiles ?? []).map((file) => (
|
{(preflight?.changedFiles ?? []).map((file) => (
|
||||||
<div className="pr-create-modal__file-row" key={file.path}>
|
<div className="pr-create-modal__file-row" key={file.path}>
|
||||||
<span>{file.path}</span>
|
<span>{file.path}</span>
|
||||||
@@ -641,7 +641,7 @@ export function PrCreateModal({
|
|||||||
<div className="form-error pr-error" role="alert">
|
<div className="form-error pr-error" role="alert">
|
||||||
<p>{pushBranchError}</p>
|
<p>{pushBranchError}</p>
|
||||||
<div className="pr-error__actions">
|
<div className="pr-error__actions">
|
||||||
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => setPushBranchError(null)} aria-label="Dismiss push branch error">×</button>
|
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => setPushBranchError(null)} aria-label={t("pr.dismissPushBranchError", "Dismiss push branch error")}>×</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -650,7 +650,7 @@ export function PrCreateModal({
|
|||||||
<div className="form-error pr-error" role="alert">
|
<div className="form-error pr-error" role="alert">
|
||||||
<p>{resolveConflictError}</p>
|
<p>{resolveConflictError}</p>
|
||||||
<div className="pr-error__actions">
|
<div className="pr-error__actions">
|
||||||
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => setResolveConflictError(null)} aria-label="Dismiss conflict resolution error">×</button>
|
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => setResolveConflictError(null)} aria-label={t("pr.dismissConflictResolutionError", "Dismiss conflict resolution error")}>×</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -660,8 +660,8 @@ export function PrCreateModal({
|
|||||||
<p>{submitError}</p>
|
<p>{submitError}</p>
|
||||||
{lastGhError?.hint ? <p className="pr-error__hint">{lastGhError.hint}</p> : null}
|
{lastGhError?.hint ? <p className="pr-error__hint">{lastGhError.hint}</p> : null}
|
||||||
<div className="pr-error__actions">
|
<div className="pr-error__actions">
|
||||||
{lastGhError?.action?.kind === "shell" ? <p>Action: run <code>{lastGhError.action.command}</code></p> : null}
|
{lastGhError?.action?.kind === "shell" ? <p>{t("pr.error.actionRun", "Action: run")} <code>{lastGhError.action.command}</code></p> : null}
|
||||||
{lastGhError?.action?.kind === "open" ? <p>Action: open <a href={lastGhError.action.url} target="_blank" rel="noreferrer">docs</a></p> : null}
|
{lastGhError?.action?.kind === "open" ? <p>{t("pr.error.actionOpen", "Action: open")} <a href={lastGhError.action.url} target="_blank" rel="noreferrer">{t("pr.error.docs", "docs")}</a></p> : null}
|
||||||
{lastGhError?.retryable ? <button type="button" className="btn btn-sm pr-error__retry" onClick={() => void submit()}>{t("actions.retry", "Retry")}</button> : null}
|
{lastGhError?.retryable ? <button type="button" className="btn btn-sm pr-error__retry" onClick={() => void submit()}>{t("actions.retry", "Retry")}</button> : null}
|
||||||
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => { setLastGhError(null); setSubmitError(null); }} aria-label={t("pr.dismissError", "Dismiss PR error")}>×</button>
|
<button type="button" className="btn btn-sm pr-error__dismiss" onClick={() => { setLastGhError(null); setSubmitError(null); }} aria-label={t("pr.dismissError", "Dismiss PR error")}>×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ function PrCard({
|
|||||||
<div>{lastGhError.message}</div>
|
<div>{lastGhError.message}</div>
|
||||||
{lastGhError.hint ? <div className="pr-error__hint">{lastGhError.hint}</div> : null}
|
{lastGhError.hint ? <div className="pr-error__hint">{lastGhError.hint}</div> : null}
|
||||||
<div className="pr-error__actions">
|
<div className="pr-error__actions">
|
||||||
{lastGhError.action?.kind === "shell" ? <div>Action: run <code>{lastGhError.action.command}</code></div> : null}
|
{lastGhError.action?.kind === "shell" ? <div>{t("pr.error.actionRun", "Action: run")} <code>{lastGhError.action.command}</code></div> : null}
|
||||||
{lastGhError.retryable ? <button className="btn btn-sm pr-error__retry" onClick={() => void handleRefresh()}>{t("git.retryButton", "Retry")}</button> : null}
|
{lastGhError.retryable ? <button className="btn btn-sm pr-error__retry" onClick={() => void handleRefresh()}>{t("git.retryButton", "Retry")}</button> : null}
|
||||||
<button className="btn btn-sm pr-error__dismiss" onClick={() => setLastGhError(null)} aria-label={t("git.dismissPrError", "Dismiss PR error")}>×</button>
|
<button className="btn btn-sm pr-error__dismiss" onClick={() => setLastGhError(null)} aria-label={t("git.dismissPrError", "Dismiss PR error")}>×</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
GitPullRequest,
|
GitPullRequest,
|
||||||
GitMerge,
|
GitMerge,
|
||||||
@@ -101,6 +102,7 @@ function ChecksIcon({ rollup }: { rollup: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PullRequestView(props: PullRequestViewProps) {
|
export function PullRequestView(props: PullRequestViewProps) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const { detail: detailProp, pullRequestId, projectId, onAction, loadPullRequest } = props;
|
const { detail: detailProp, pullRequestId, projectId, onAction, loadPullRequest } = props;
|
||||||
const [detail, setDetail] = useState<PrDetail | null>(detailProp ?? null);
|
const [detail, setDetail] = useState<PrDetail | null>(detailProp ?? null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -166,7 +168,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
if (!detail) {
|
if (!detail) {
|
||||||
return (
|
return (
|
||||||
<div className="pr-view pr-view--loading" data-testid="pr-view-loading">
|
<div className="pr-view pr-view--loading" data-testid="pr-view-loading">
|
||||||
Loading PR…
|
{t("pr.view.loading", "Loading PR…")}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -179,7 +181,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
<div className="pr-view" data-testid="pr-view" data-state="creating">
|
<div className="pr-view" data-testid="pr-view" data-state="creating">
|
||||||
<PrIdentityHeader detail={detail} />
|
<PrIdentityHeader detail={detail} />
|
||||||
<div className="pr-placeholder" data-testid="pr-creating">
|
<div className="pr-placeholder" data-testid="pr-creating">
|
||||||
<Clock size={16} /> Creating PR…
|
<Clock size={16} /> {t("pr.view.creating", "Creating PR…")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -192,7 +194,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
<PrIdentityHeader detail={detail} />
|
<PrIdentityHeader detail={detail} />
|
||||||
<div className="pr-error-reason" data-testid="pr-failed">
|
<div className="pr-error-reason" data-testid="pr-failed">
|
||||||
<AlertTriangle size={16} className="pr-icon-failure" />
|
<AlertTriangle size={16} className="pr-icon-failure" />
|
||||||
<span>{detail.failureReason ?? "PR creation failed"}</span>
|
<span>{detail.failureReason ?? t("pr.view.creationFailed", "PR creation failed")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="pr-action-bar">
|
<div className="pr-action-bar">
|
||||||
<button
|
<button
|
||||||
@@ -202,7 +204,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
disabled={busy === "retry-create"}
|
disabled={busy === "retry-create"}
|
||||||
onClick={() => void runAction("retry-create")}
|
onClick={() => void runAction("retry-create")}
|
||||||
>
|
>
|
||||||
<RotateCcw size={14} /> Retry PR creation
|
<RotateCcw size={14} /> {t("pr.view.retryCreation", "Retry PR creation")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="pr-inline-error">{error}</div>}
|
{error && <div className="pr-inline-error">{error}</div>}
|
||||||
@@ -216,7 +218,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
<div className="pr-view" data-testid="pr-view" data-state="unverified">
|
<div className="pr-view" data-testid="pr-view" data-state="unverified">
|
||||||
<PrIdentityHeader detail={detail} />
|
<PrIdentityHeader detail={detail} />
|
||||||
<div className="pr-notice pr-notice--unverified" data-testid="pr-unverified">
|
<div className="pr-notice pr-notice--unverified" data-testid="pr-unverified">
|
||||||
<Clock size={16} /> Verifying with GitHub…
|
<Clock size={16} /> {t("pr.view.verifyingGithub", "Verifying with GitHub…")}
|
||||||
</div>
|
</div>
|
||||||
<div className="pr-action-bar">
|
<div className="pr-action-bar">
|
||||||
<button
|
<button
|
||||||
@@ -224,9 +226,9 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
className="pr-action"
|
className="pr-action"
|
||||||
data-testid="pr-merge"
|
data-testid="pr-merge"
|
||||||
disabled
|
disabled
|
||||||
title="Merge is disabled until GitHub verifies this PR"
|
title={t("pr.view.mergeDisabledUntilVerified", "Merge is disabled until GitHub verifies this PR")}
|
||||||
>
|
>
|
||||||
<GitMerge size={14} /> Merge
|
<GitMerge size={14} /> {t("pr.view.merge", "Merge")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* checks/threads hidden while unverified */}
|
{/* checks/threads hidden while unverified */}
|
||||||
@@ -243,8 +245,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
{/* responding banner */}
|
{/* responding banner */}
|
||||||
{state === "responding" && (
|
{state === "responding" && (
|
||||||
<div className="pr-banner pr-banner--responding" data-testid="pr-responding">
|
<div className="pr-banner pr-banner--responding" data-testid="pr-responding">
|
||||||
<MessageSquare size={16} /> Response run in progress — {summary.pendingThreads} threads
|
<MessageSquare size={16} /> {t("pr.view.responsePending", "Response run in progress — {{count}} threads pending", { count: summary.pendingThreads })}
|
||||||
pending
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -257,17 +258,17 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
disabled={state === "responding" || busy === "approve"}
|
disabled={state === "responding" || busy === "approve"}
|
||||||
onClick={() => void runAction("approve")}
|
onClick={() => void runAction("approve")}
|
||||||
>
|
>
|
||||||
<ThumbsUp size={14} /> Approve
|
<ThumbsUp size={14} /> {t("pr.view.approve", "Approve")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="pr-action pr-action--retry"
|
className="pr-action pr-action--retry"
|
||||||
data-testid="pr-retry"
|
data-testid="pr-retry"
|
||||||
disabled={state === "responding" || busy === "retry"}
|
disabled={state === "responding" || busy === "retry"}
|
||||||
title={state === "responding" ? "A response run is already in progress" : undefined}
|
title={state === "responding" ? t("pr.view.responseAlreadyInProgress", "A response run is already in progress") : undefined}
|
||||||
onClick={() => void runAction("retry")}
|
onClick={() => void runAction("retry")}
|
||||||
>
|
>
|
||||||
<RotateCcw size={14} /> Request retry
|
<RotateCcw size={14} /> {t("pr.view.requestRetry", "Request retry")}
|
||||||
</button>
|
</button>
|
||||||
{!confirmingMerge ? (
|
{!confirmingMerge ? (
|
||||||
<button
|
<button
|
||||||
@@ -275,10 +276,10 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
className="pr-action pr-action--merge"
|
className="pr-action pr-action--merge"
|
||||||
data-testid="pr-merge"
|
data-testid="pr-merge"
|
||||||
disabled={conflicting || state === "responding" || busy === "merge"}
|
disabled={conflicting || state === "responding" || busy === "merge"}
|
||||||
title={conflicting ? "Resolve conflicts on GitHub before merging" : undefined}
|
title={conflicting ? t("pr.view.resolveConflictsBeforeMerge", "Resolve conflicts on GitHub before merging") : undefined}
|
||||||
onClick={() => setConfirmingMerge(true)}
|
onClick={() => setConfirmingMerge(true)}
|
||||||
>
|
>
|
||||||
<GitMerge size={14} /> Merge
|
<GitMerge size={14} /> {t("pr.view.merge", "Merge")}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
@@ -288,7 +289,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
disabled={busy === "merge"}
|
disabled={busy === "merge"}
|
||||||
onClick={() => void runAction("merge")}
|
onClick={() => void runAction("merge")}
|
||||||
>
|
>
|
||||||
<GitMerge size={14} /> Confirm merge
|
<GitMerge size={14} /> {t("pr.view.confirmMerge", "Confirm merge")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
@@ -298,7 +299,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
disabled={busy === "close"}
|
disabled={busy === "close"}
|
||||||
onClick={() => void runAction("close")}
|
onClick={() => void runAction("close")}
|
||||||
>
|
>
|
||||||
<XCircle size={14} /> Close
|
<XCircle size={14} /> {t("pr.view.close", "Close")}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<label className="pr-automerge-toggle" data-testid="pr-automerge">
|
<label className="pr-automerge-toggle" data-testid="pr-automerge">
|
||||||
@@ -308,7 +309,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
disabled={busy === "automerge"}
|
disabled={busy === "automerge"}
|
||||||
onChange={(e) => void runAction("automerge", { enabled: e.target.checked })}
|
onChange={(e) => void runAction("automerge", { enabled: e.target.checked })}
|
||||||
/>
|
/>
|
||||||
<span>Auto-merge</span>
|
<span>{t("pr.view.autoMerge", "Auto-merge")}</span>
|
||||||
<span className="pr-automerge-gate" data-testid="pr-automerge-gate">
|
<span className="pr-automerge-gate" data-testid="pr-automerge-gate">
|
||||||
{summary.autoMergeReason}
|
{summary.autoMergeReason}
|
||||||
</span>
|
</span>
|
||||||
@@ -324,17 +325,17 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
Resolve conflicts on GitHub <ExternalLink size={12} />
|
{t("pr.view.resolveConflictsOnGithub", "Resolve conflicts on GitHub")} <ExternalLink size={12} />
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ── merge-readiness summary ─────────────────────────────────────── */}
|
{/* ── merge-readiness summary ─────────────────────────────────────── */}
|
||||||
<div className="pr-summary" data-testid="pr-summary">
|
<div className="pr-summary" data-testid="pr-summary">
|
||||||
<span className="pr-summary-item" data-testid="pr-summary-mergeable">
|
<span className="pr-summary-item" data-testid="pr-summary-mergeable">
|
||||||
Mergeable: {summary.mergeable}
|
{t("pr.view.mergeableLabel", "Mergeable:")} {summary.mergeable}
|
||||||
</span>
|
</span>
|
||||||
<span className="pr-summary-item" data-testid="pr-summary-review">
|
<span className="pr-summary-item" data-testid="pr-summary-review">
|
||||||
Review: {summary.reviewDecision ?? "none"}
|
{t("pr.view.reviewLabel", "Review:")} {summary.reviewDecision ?? t("pr.view.none", "none")}
|
||||||
</span>
|
</span>
|
||||||
<span className="pr-summary-item" data-testid="pr-summary-checks">
|
<span className="pr-summary-item" data-testid="pr-summary-checks">
|
||||||
<ChecksIcon rollup={summary.checksRollup} /> {summary.checksRollup}
|
<ChecksIcon rollup={summary.checksRollup} /> {summary.checksRollup}
|
||||||
@@ -344,7 +345,7 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
{/* ── threads (agent replies nested) ───────────────────────────────── */}
|
{/* ── threads (agent replies nested) ───────────────────────────────── */}
|
||||||
<div className="pr-threads" data-testid="pr-threads">
|
<div className="pr-threads" data-testid="pr-threads">
|
||||||
{detail.threads.length === 0 ? (
|
{detail.threads.length === 0 ? (
|
||||||
<div className="pr-threads-empty">No review threads.</div>
|
<div className="pr-threads-empty">{t("pr.view.noReviewThreads", "No review threads.")}</div>
|
||||||
) : (
|
) : (
|
||||||
detail.threads.map((thread) => (
|
detail.threads.map((thread) => (
|
||||||
<div
|
<div
|
||||||
@@ -358,24 +359,24 @@ export function PullRequestView(props: PullRequestViewProps) {
|
|||||||
<div className="pr-thread-head">
|
<div className="pr-thread-head">
|
||||||
{thread.outcome === "pending" && (
|
{thread.outcome === "pending" && (
|
||||||
<span className="pr-thread-pending">
|
<span className="pr-thread-pending">
|
||||||
<Clock size={12} /> pending
|
<Clock size={12} /> {t("pr.view.threadPending", "pending")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{thread.outcome === "disagreed" && (
|
{thread.outcome === "disagreed" && (
|
||||||
<span className="pr-thread-disagreed">
|
<span className="pr-thread-disagreed">
|
||||||
<AlertTriangle size={12} /> agent disagreed
|
<AlertTriangle size={12} /> {t("pr.view.agentDisagreed", "agent disagreed")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{thread.outcome === "fixed" && (
|
{thread.outcome === "fixed" && (
|
||||||
<span className="pr-thread-fixed">
|
<span className="pr-thread-fixed">
|
||||||
<CheckCircle size={12} /> fixed
|
<CheckCircle size={12} /> {t("pr.view.threadFixed", "fixed")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span className="pr-thread-id">{thread.threadId}</span>
|
<span className="pr-thread-id">{thread.threadId}</span>
|
||||||
</div>
|
</div>
|
||||||
{thread.fixCommitSha && (
|
{thread.fixCommitSha && (
|
||||||
<div className="pr-thread-reply" data-testid="pr-thread-reply">
|
<div className="pr-thread-reply" data-testid="pr-thread-reply">
|
||||||
Agent reply — fix {thread.fixCommitSha.slice(0, 8)}
|
{t("pr.view.agentReplyFix", "Agent reply — fix {{sha}}", { sha: thread.fixCommitSha.slice(0, 8) })}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1850,10 +1850,10 @@ export function SettingsModal({
|
|||||||
const info = await fetchBackups(projectId);
|
const info = await fetchBackups(projectId);
|
||||||
setBackupInfo(info);
|
setBackupInfo(info);
|
||||||
} else {
|
} else {
|
||||||
addToast(result.error || "Failed to create backup", "error");
|
addToast(result.error || t("settings.backups.createFailed", "Failed to create backup"), "error");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to create backup", "error");
|
addToast(getErrorMessage(err) || t("settings.backups.createFailed", "Failed to create backup"), "error");
|
||||||
} finally {
|
} finally {
|
||||||
setBackupLoading(false);
|
setBackupLoading(false);
|
||||||
}
|
}
|
||||||
@@ -1879,10 +1879,14 @@ export function SettingsModal({
|
|||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
const scopeLabel = scope === "global" ? "global" : scope === "project" ? "project" : "all";
|
const scopeLabel = scope === "global"
|
||||||
addToast(`Settings exported (${scopeLabel} scope)`, "success");
|
? t("settings.importExport.scopeLabel.global", "global")
|
||||||
|
: scope === "project"
|
||||||
|
? t("settings.importExport.scopeLabel.project", "project")
|
||||||
|
: t("settings.importExport.scopeLabel.all", "all");
|
||||||
|
addToast(t("settings.importExport.exported", "Settings exported ({{scope}} scope)", { scope: scopeLabel }), "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to export settings", "error");
|
addToast(getErrorMessage(err) || t("settings.importExport.exportFailed", "Failed to export settings"), "error");
|
||||||
}
|
}
|
||||||
}, [addToast, activeSectionScope, projectId]);
|
}, [addToast, activeSectionScope, projectId]);
|
||||||
|
|
||||||
@@ -1899,7 +1903,7 @@ export function SettingsModal({
|
|||||||
setImportPreview(data);
|
setImportPreview(data);
|
||||||
setImportDialogOpen(true);
|
setImportDialogOpen(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(`Invalid JSON file: ${getErrorMessage(err)}`, "error");
|
addToast(t("settings.importExport.invalidJson", "Invalid JSON file: {{error}}", { error: getErrorMessage(err) }), "error");
|
||||||
setImportFile(null);
|
setImportFile(null);
|
||||||
} finally {
|
} finally {
|
||||||
setImportLoading(false);
|
setImportLoading(false);
|
||||||
@@ -1914,10 +1918,10 @@ export function SettingsModal({
|
|||||||
const result = await importSettings(importPreview, { scope: importScope, merge: importMerge }, projectId);
|
const result = await importSettings(importPreview, { scope: importScope, merge: importMerge }, projectId);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
if (result.globalCount > 0) parts.push(`${result.globalCount} global`);
|
if (result.globalCount > 0) parts.push(t("settings.importExport.counts.global", "{{count}} global", { count: result.globalCount }));
|
||||||
if (result.projectCount > 0) parts.push(`${result.projectCount} project`);
|
if (result.projectCount > 0) parts.push(t("settings.importExport.counts.project", "{{count}} project", { count: result.projectCount }));
|
||||||
if (result.workflowSettingsCount > 0) parts.push(`${result.workflowSettingsCount} workflow setting value(s)`);
|
if (result.workflowSettingsCount > 0) parts.push(t("settings.importExport.counts.workflowSettings", "{{count}} workflow setting value", { count: result.workflowSettingsCount }));
|
||||||
addToast(`Imported ${parts.join(", ")} setting(s)`, "success");
|
addToast(t("settings.importExport.imported", "Imported {{counts}} setting(s)", { counts: parts.join(", ") }), "success");
|
||||||
setImportDialogOpen(false);
|
setImportDialogOpen(false);
|
||||||
setImportPreview(null);
|
setImportPreview(null);
|
||||||
setImportFile(null);
|
setImportFile(null);
|
||||||
@@ -1925,10 +1929,10 @@ export function SettingsModal({
|
|||||||
const refreshed = await fetchSettings(projectId);
|
const refreshed = await fetchSettings(projectId);
|
||||||
setForm(refreshed);
|
setForm(refreshed);
|
||||||
} else {
|
} else {
|
||||||
addToast(result.error || "Import failed", "error");
|
addToast(result.error || t("settings.importExport.importFailed", "Import failed"), "error");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to import settings", "error");
|
addToast(getErrorMessage(err) || t("settings.importExport.importFailedDetailed", "Failed to import settings"), "error");
|
||||||
} finally {
|
} finally {
|
||||||
setImportLoading(false);
|
setImportLoading(false);
|
||||||
}
|
}
|
||||||
@@ -2373,11 +2377,11 @@ export function SettingsModal({
|
|||||||
const result = await installQmd(projectId);
|
const result = await installQmd(projectId);
|
||||||
await refreshMemoryBackend();
|
await refreshMemoryBackend();
|
||||||
addToast(
|
addToast(
|
||||||
result.qmdAvailable ? "qmd installed successfully" : "qmd install finished, but qmd is still unavailable",
|
result.qmdAvailable ? t("settings.memory.qmdInstalled", "qmd installed successfully") : t("settings.memory.qmdInstallUnavailable", "qmd install finished, but qmd is still unavailable"),
|
||||||
result.qmdAvailable ? "success" : "warning",
|
result.qmdAvailable ? "success" : "warning",
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to install qmd", "error");
|
addToast(getErrorMessage(err) || t("settings.memory.qmdInstallFailed", "Failed to install qmd"), "error");
|
||||||
} finally {
|
} finally {
|
||||||
setQmdInstallLoading(false);
|
setQmdInstallLoading(false);
|
||||||
}
|
}
|
||||||
@@ -2471,14 +2475,14 @@ export function SettingsModal({
|
|||||||
try {
|
try {
|
||||||
const result = await installCloudflared(projectId);
|
const result = await installCloudflared(projectId);
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
setCloudflaredInstallError(result.error ?? "Installation failed");
|
setCloudflaredInstallError(result.error ?? t("settings.remote.installationFailed", "Installation failed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const status = await fetchRemoteStatus(projectId);
|
const status = await fetchRemoteStatus(projectId);
|
||||||
setRemoteStatus(status);
|
setRemoteStatus(status);
|
||||||
addToast(t("settings.remote.cloudflaredInstalled", "cloudflared installed successfully"), "success");
|
addToast(t("settings.remote.cloudflaredInstalled", "cloudflared installed successfully"), "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setCloudflaredInstallError(err instanceof Error ? err.message : "Installation failed");
|
setCloudflaredInstallError(err instanceof Error ? err.message : t("settings.remote.installationFailed", "Installation failed"));
|
||||||
} finally {
|
} finally {
|
||||||
setCloudflaredInstalling(false);
|
setCloudflaredInstalling(false);
|
||||||
}
|
}
|
||||||
@@ -2871,15 +2875,15 @@ export function SettingsModal({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="settings-github-star-btn"
|
className="settings-github-star-btn"
|
||||||
aria-label="Star Fusion on GitHub"
|
aria-label={t("settings.header.starFusion", "Star Fusion on GitHub")}
|
||||||
title="Star Fusion on GitHub"
|
title={t("settings.header.starFusion", "Star Fusion on GitHub")}
|
||||||
onClick={markStarClicked}
|
onClick={markStarClicked}
|
||||||
data-clicked={starClicked ? "true" : "false"}
|
data-clicked={starClicked ? "true" : "false"}
|
||||||
>
|
>
|
||||||
<span className="settings-github-star-btn__action">
|
<span className="settings-github-star-btn__action">
|
||||||
<ProviderIcon provider="github" size="sm" />
|
<ProviderIcon provider="github" size="sm" />
|
||||||
<Star size={11} aria-hidden="true" />
|
<Star size={11} aria-hidden="true" />
|
||||||
Star
|
{t("settings.header.star", "Star")}
|
||||||
</span>
|
</span>
|
||||||
{gitHubStarCount !== null && (
|
{gitHubStarCount !== null && (
|
||||||
<span className="settings-github-star-btn__count" aria-label={`${gitHubStarCount.toLocaleString()} stars`}>
|
<span className="settings-github-star-btn__count" aria-label={`${gitHubStarCount.toLocaleString()} stars`}>
|
||||||
@@ -2894,14 +2898,14 @@ export function SettingsModal({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="btn btn-sm settings-header-discord-btn"
|
className="btn btn-sm settings-header-discord-btn"
|
||||||
aria-label="Join our Discord"
|
aria-label={t("settings.header.joinDiscord", "Join our Discord")}
|
||||||
title="Join our Discord"
|
title={t("settings.header.joinDiscord", "Join our Discord")}
|
||||||
>
|
>
|
||||||
<DiscordIcon size={13} />
|
<DiscordIcon size={13} />
|
||||||
{t("settings.header.discord", "Discord")}
|
{t("settings.header.discord", "Discord")}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<button className="modal-close" onClick={onClose} aria-label="Close">
|
<button className="modal-close" onClick={onClose} aria-label={t("actions.close", "Close")}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -2971,8 +2975,8 @@ export function SettingsModal({
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="btn btn-sm settings-footer-help-btn"
|
className="btn btn-sm settings-footer-help-btn"
|
||||||
aria-label="Help and discussions"
|
aria-label={t("settings.footer.helpDiscussions", "Help and discussions")}
|
||||||
title="Help and discussions"
|
title={t("settings.footer.helpDiscussions", "Help and discussions")}
|
||||||
>
|
>
|
||||||
<HelpCircle size={13} aria-hidden="true" />
|
<HelpCircle size={13} aria-hidden="true" />
|
||||||
{t("settings.footer.help", "Help")}
|
{t("settings.footer.help", "Help")}
|
||||||
@@ -2986,8 +2990,8 @@ export function SettingsModal({
|
|||||||
void handleCheckForUpdates();
|
void handleCheckForUpdates();
|
||||||
}}
|
}}
|
||||||
disabled={updateCheckLoading}
|
disabled={updateCheckLoading}
|
||||||
aria-label="Check for updates"
|
aria-label={t("settings.footer.checkUpdates", "Check for updates")}
|
||||||
title="Check for updates"
|
title={t("settings.footer.checkUpdates", "Check for updates")}
|
||||||
>
|
>
|
||||||
<span className="settings-modal-version">{t("settings.footer.version", "Version {{version}}", { version: appVersion })}</span>
|
<span className="settings-modal-version">{t("settings.footer.version", "Version {{version}}", { version: appVersion })}</span>
|
||||||
<RefreshCw size={12} className={updateCheckLoading ? "spinning" : undefined} />
|
<RefreshCw size={12} className={updateCheckLoading ? "spinning" : undefined} />
|
||||||
@@ -3030,7 +3034,7 @@ export function SettingsModal({
|
|||||||
className="btn btn-sm"
|
className="btn btn-sm"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
disabled={importLoading}
|
disabled={importLoading}
|
||||||
title="Import settings from JSON file"
|
title={t("settings.importExport.importTitleAttr", "Import settings from JSON file")}
|
||||||
>
|
>
|
||||||
{importLoading ? t("settings.importExport.loadingFile", "Loading…") : t("settings.importExport.importBtn", "Import")}
|
{importLoading ? t("settings.importExport.loadingFile", "Loading…") : t("settings.importExport.importBtn", "Import")}
|
||||||
</button>
|
</button>
|
||||||
@@ -3052,18 +3056,18 @@ export function SettingsModal({
|
|||||||
onClick={handleOverlapPathPickerOverlayClick}
|
onClick={handleOverlapPathPickerOverlayClick}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-label="Browse workspace path"
|
aria-label={t("settings.scheduling.browseWorkspacePath", "Browse workspace path")}
|
||||||
>
|
>
|
||||||
<div className="modal modal-lg settings-overlap-path-picker-modal" onClick={(event) => event.stopPropagation()}>
|
<div className="modal modal-lg settings-overlap-path-picker-modal" onClick={(event) => event.stopPropagation()}>
|
||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<h3>{t("settings.scheduling.selectIgnoredOverlapPath", "Select ignored overlap path")}</h3>
|
<h3>{t("settings.scheduling.selectIgnoredOverlapPath", "Select ignored overlap path")}</h3>
|
||||||
<button className="modal-close" onClick={closeOverlapPathPicker} aria-label="Close">
|
<button className="modal-close" onClick={closeOverlapPathPicker} aria-label={t("actions.close", "Close")}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body settings-overlap-path-picker-body">
|
<div className="modal-body settings-overlap-path-picker-body">
|
||||||
<p className="settings-overlap-path-picker-note">
|
<p className="settings-overlap-path-picker-note">
|
||||||
Choose a file to ignore directly, or navigate into a folder and select the current directory.
|
{t("settings.scheduling.overlapPickerNote", "Choose a file to ignore directly, or navigate into a folder and select the current directory.")}
|
||||||
</p>
|
</p>
|
||||||
<FileBrowser
|
<FileBrowser
|
||||||
entries={overlapPathPickerEntries}
|
entries={overlapPathPickerEntries}
|
||||||
@@ -3080,7 +3084,7 @@ export function SettingsModal({
|
|||||||
<div className="modal-actions">
|
<div className="modal-actions">
|
||||||
<div className="modal-actions-left">
|
<div className="modal-actions-left">
|
||||||
<small>
|
<small>
|
||||||
Current directory: <code>{overlapPathPickerCurrentPath === "." ? "(project root)" : overlapPathPickerCurrentPath}</code>
|
{t("settings.fileBrowser.currentDirectory", "Current directory:")} <code>{overlapPathPickerCurrentPath === "." ? t("settings.fileBrowser.projectRoot", "(project root)") : overlapPathPickerCurrentPath}</code>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-actions-right">
|
<div className="modal-actions-right">
|
||||||
@@ -3106,18 +3110,18 @@ export function SettingsModal({
|
|||||||
onClick={handleWorktreesDirPickerOverlayClick}
|
onClick={handleWorktreesDirPickerOverlayClick}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-label="Browse worktrees directory"
|
aria-label={t("settings.worktrees.browseWorktreesDirectory", "Browse worktrees directory")}
|
||||||
>
|
>
|
||||||
<div className="modal modal-lg settings-overlap-path-picker-modal" onClick={(event) => event.stopPropagation()}>
|
<div className="modal modal-lg settings-overlap-path-picker-modal" onClick={(event) => event.stopPropagation()}>
|
||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<h3>{t("settings.worktrees.selectWorktreesDir", "Select worktrees directory")}</h3>
|
<h3>{t("settings.worktrees.selectWorktreesDir", "Select worktrees directory")}</h3>
|
||||||
<button className="modal-close" onClick={closeWorktreesDirPicker} aria-label="Close">
|
<button className="modal-close" onClick={closeWorktreesDirPicker} aria-label={t("actions.close", "Close")}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body settings-overlap-path-picker-body">
|
<div className="modal-body settings-overlap-path-picker-body">
|
||||||
<p className="settings-overlap-path-picker-note">
|
<p className="settings-overlap-path-picker-note">
|
||||||
Navigate to the folder where Fusion should create task worktrees, then select the current directory.
|
{t("settings.worktrees.worktreesPickerNote", "Navigate to the folder where Fusion should create task worktrees, then select the current directory.")}
|
||||||
</p>
|
</p>
|
||||||
<FileBrowser
|
<FileBrowser
|
||||||
entries={worktreesDirPickerEntries}
|
entries={worktreesDirPickerEntries}
|
||||||
@@ -3134,7 +3138,7 @@ export function SettingsModal({
|
|||||||
<div className="modal-actions">
|
<div className="modal-actions">
|
||||||
<div className="modal-actions-left">
|
<div className="modal-actions-left">
|
||||||
<small>
|
<small>
|
||||||
Current directory: <code>{worktreesDirPickerCurrentPath === "." ? "(project root)" : worktreesDirPickerCurrentPath}</code>
|
{t("settings.fileBrowser.currentDirectory", "Current directory:")} <code>{worktreesDirPickerCurrentPath === "." ? t("settings.fileBrowser.projectRoot", "(project root)") : worktreesDirPickerCurrentPath}</code>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-actions-right">
|
<div className="modal-actions-right">
|
||||||
@@ -3156,7 +3160,7 @@ export function SettingsModal({
|
|||||||
<div className="modal modal-md">
|
<div className="modal modal-md">
|
||||||
<div className="modal-header">
|
<div className="modal-header">
|
||||||
<h3>{t("settings.importExport.importTitle", "Import Settings")}</h3>
|
<h3>{t("settings.importExport.importTitle", "Import Settings")}</h3>
|
||||||
<button className="modal-close" onClick={() => setImportDialogOpen(false)} aria-label="Close">
|
<button className="modal-close" onClick={() => setImportDialogOpen(false)} aria-label={t("actions.close", "Close")}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -3165,7 +3169,7 @@ export function SettingsModal({
|
|||||||
|
|
||||||
{importPreview.global && Object.keys(importPreview.global).length > 0 && (
|
{importPreview.global && Object.keys(importPreview.global).length > 0 && (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<strong>Global Settings:</strong>
|
<strong>{t("settings.importExport.globalSettings", "Global Settings:")}</strong>
|
||||||
<ul className="import-preview-list">
|
<ul className="import-preview-list">
|
||||||
{Object.entries(importPreview.global)
|
{Object.entries(importPreview.global)
|
||||||
.filter(([, v]) => v !== undefined)
|
.filter(([, v]) => v !== undefined)
|
||||||
@@ -3178,7 +3182,7 @@ export function SettingsModal({
|
|||||||
|
|
||||||
{importPreview.project && Object.keys(importPreview.project).length > 0 && (
|
{importPreview.project && Object.keys(importPreview.project).length > 0 && (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<strong>Project Settings:</strong>
|
<strong>{t("settings.importExport.projectSettings", "Project Settings:")}</strong>
|
||||||
<ul className="import-preview-list">
|
<ul className="import-preview-list">
|
||||||
{Object.entries(importPreview.project)
|
{Object.entries(importPreview.project)
|
||||||
.filter(([, v]) => v !== undefined)
|
.filter(([, v]) => v !== undefined)
|
||||||
@@ -3190,15 +3194,15 @@ export function SettingsModal({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label htmlFor="import-scope">Import Scope:</label>
|
<label htmlFor="import-scope">{t("settings.importExport.importScope", "Import Scope:")}</label>
|
||||||
<select
|
<select
|
||||||
id="import-scope"
|
id="import-scope"
|
||||||
value={importScope}
|
value={importScope}
|
||||||
onChange={(e) => setImportScope(e.target.value as 'global' | 'project' | 'both')}
|
onChange={(e) => setImportScope(e.target.value as 'global' | 'project' | 'both')}
|
||||||
>
|
>
|
||||||
<option value="both">Both global and project settings</option>
|
<option value="both">{t("settings.importExport.scopeBoth", "Both global and project settings")}</option>
|
||||||
<option value="global">Global settings only</option>
|
<option value="global">{t("settings.importExport.scopeGlobal", "Global settings only")}</option>
|
||||||
<option value="project">Project settings only</option>
|
<option value="project">{t("settings.importExport.scopeProject", "Project settings only")}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -3210,9 +3214,9 @@ export function SettingsModal({
|
|||||||
checked={importMerge}
|
checked={importMerge}
|
||||||
onChange={(e) => setImportMerge(e.target.checked)}
|
onChange={(e) => setImportMerge(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
Merge with existing settings (recommended)
|
{t("settings.importExport.mergeExisting", "Merge with existing settings (recommended)")}
|
||||||
</label>
|
</label>
|
||||||
<small>If unchecked, existing settings will be replaced with imported values.</small>
|
<small>{t("settings.importExport.replaceWarning", "If unchecked, existing settings will be replaced with imported values.")}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-actions">
|
<div className="modal-actions">
|
||||||
|
|||||||
@@ -152,14 +152,14 @@ export function SetupWizardModal({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="setup-wizard-header">
|
<div className="setup-wizard-header">
|
||||||
<div className="setup-wizard-heading">
|
<div className="setup-wizard-heading">
|
||||||
<div className="setup-wizard-brand" aria-label="Fusion">
|
<div className="setup-wizard-brand" aria-label={t("setup.brandName", "Fusion")}>
|
||||||
<svg
|
<svg
|
||||||
className="setup-wizard-brand-logo"
|
className="setup-wizard-brand-logo"
|
||||||
width={28}
|
width={28}
|
||||||
height={28}
|
height={28}
|
||||||
viewBox="0 0 128 128"
|
viewBox="0 0 128 128"
|
||||||
fill="none"
|
fill="none"
|
||||||
aria-label="Fusion logo"
|
aria-label={t("setup.brandLogo", "Fusion logo")}
|
||||||
role="img"
|
role="img"
|
||||||
>
|
>
|
||||||
<circle
|
<circle
|
||||||
@@ -174,12 +174,12 @@ export function SetupWizardModal({
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span className="setup-wizard-brand-name">Fusion</span>
|
<span className="setup-wizard-brand-name">{t("setup.brandName", "Fusion")}</span>
|
||||||
</div>
|
</div>
|
||||||
<h2 id="wizard-title" className="setup-wizard-title">
|
<h2 id="wizard-title" className="setup-wizard-title">
|
||||||
{state.step === "auth" && t("setup.setAuthToken", "Set Auth Token")}
|
{state.step === "auth" && t("setup.setAuthToken", "Set Auth Token")}
|
||||||
{state.step === "manual" && t("setup.welcomeToFusion", "Welcome to Fusion")}
|
{state.step === "manual" && t("setup.welcomeToFusion", "Welcome to Fusion")}
|
||||||
{state.step === "complete" && t("setup.setupComplete", "Setup Complete!")}
|
{state.step === "complete" && t("setup.setupCompleteTitle", "Setup Complete!")}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
{state.step !== "complete" && (
|
{state.step !== "complete" && (
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from "
|
|||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import { ChevronDown, Loader2, Maximize2, Minimize2, Send } from "lucide-react";
|
import { ChevronDown, Loader2, Maximize2, Minimize2, Send } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import type { TFunction } from "i18next";
|
||||||
import { addSteeringComment, refineTask } from "../api";
|
import { addSteeringComment, refineTask } from "../api";
|
||||||
import { useAgentLogs } from "../hooks/useAgentLogs";
|
import { useAgentLogs } from "../hooks/useAgentLogs";
|
||||||
import type { ToastType } from "../hooks/useToast";
|
import type { ToastType } from "../hooks/useToast";
|
||||||
@@ -58,18 +60,18 @@ function isTranscriptNearBottom(container: HTMLElement): boolean {
|
|||||||
return container.scrollHeight - (container.scrollTop + container.clientHeight) <= BOTTOM_FOLLOW_THRESHOLD;
|
return container.scrollHeight - (container.scrollTop + container.clientHeight) <= BOTTOM_FOLLOW_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoleLabel(role: AgentLogRole): string {
|
function getRoleLabel(role: AgentLogRole, t: TFunction<"app">): string {
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case "triage":
|
case "triage":
|
||||||
return "Planner";
|
return t("taskChat.roles.planner", "Planner");
|
||||||
case "executor":
|
case "executor":
|
||||||
return "Executor";
|
return t("taskChat.roles.executor", "Executor");
|
||||||
case "reviewer":
|
case "reviewer":
|
||||||
return "Reviewer";
|
return t("taskChat.roles.reviewer", "Reviewer");
|
||||||
case "merger":
|
case "merger":
|
||||||
return "Merger";
|
return t("taskChat.roles.merger", "Merger");
|
||||||
default:
|
default:
|
||||||
return "Agent";
|
return t("taskChat.roles.agent", "Agent");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +139,7 @@ function mergeUserMessages(persistedComments: readonly SteeringComment[] | undef
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTranscriptItems(entries: readonly AgentLogEntry[], userMessages: readonly UserChatMessage[]): TaskChatTranscriptItem[] {
|
function buildTranscriptItems(entries: readonly AgentLogEntry[], userMessages: readonly UserChatMessage[], t: TFunction<"app">): TaskChatTranscriptItem[] {
|
||||||
const orderedItems = [
|
const orderedItems = [
|
||||||
...entries.map((entry, index) => ({ kind: "agent" as const, entry, index, timestamp: getTimestampMs(entry.timestamp) })),
|
...entries.map((entry, index) => ({ kind: "agent" as const, entry, index, timestamp: getTimestampMs(entry.timestamp) })),
|
||||||
...userMessages.map((message, index) => ({ kind: "user" as const, message, index, timestamp: getTimestampMs(message.createdAt) })),
|
...userMessages.map((message, index) => ({ kind: "user" as const, message, index, timestamp: getTimestampMs(message.createdAt) })),
|
||||||
@@ -155,7 +157,7 @@ function buildTranscriptItems(entries: readonly AgentLogEntry[], userMessages: r
|
|||||||
previousItem.entries.push(item.entry);
|
previousItem.entries.push(item.entry);
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
items.push({ kind: "agent", role, label: getRoleLabel(role), entries: [item.entry] });
|
items.push({ kind: "agent", role, label: getRoleLabel(role, t), entries: [item.entry] });
|
||||||
return items;
|
return items;
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
@@ -178,25 +180,29 @@ function isToolLikeEntry(entry: AgentLogEntry): boolean {
|
|||||||
return entry.type === "tool" || entry.type === "tool_result" || entry.type === "tool_error";
|
return entry.type === "tool" || entry.type === "tool_result" || entry.type === "tool_error";
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEntryLabel(entry: AgentLogEntry): string {
|
function formatEntryLabel(entry: AgentLogEntry, t: TFunction<"app">): string {
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case "tool":
|
case "tool":
|
||||||
return "Tool call";
|
return t("taskChat.toolCall", "Tool call");
|
||||||
case "tool_result":
|
case "tool_result":
|
||||||
return "Tool result";
|
return t("taskChat.toolResult", "Tool result");
|
||||||
case "tool_error":
|
case "tool_error":
|
||||||
return "Tool error";
|
return t("taskChat.toolError", "Tool error");
|
||||||
case "thinking":
|
case "thinking":
|
||||||
return "Thinking";
|
return t("taskChat.thinking", "Thinking");
|
||||||
default:
|
default:
|
||||||
return "Message";
|
return t("taskChat.message", "Message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatCompletionLabel(entry: AgentLogEntry, t: TFunction<"app">): string {
|
||||||
|
return entry.type === "tool_error" ? t("taskChat.error", "Error") : t("taskChat.result", "Result");
|
||||||
|
}
|
||||||
|
|
||||||
const TOOL_NAME_SUMMARY_LIMIT = 5;
|
const TOOL_NAME_SUMMARY_LIMIT = 5;
|
||||||
|
|
||||||
function formatToolCallCount(count: number): string {
|
function formatToolCallCount(count: number, t: TFunction<"app">): string {
|
||||||
return count === 1 ? "1 tool call" : `${count} tool calls`;
|
return t("taskChat.toolCallCount", "{{count}} tool call", { count });
|
||||||
}
|
}
|
||||||
|
|
||||||
function getToolInvocationEntries(entries: AgentLogEntry[]): AgentLogEntry[] {
|
function getToolInvocationEntries(entries: AgentLogEntry[]): AgentLogEntry[] {
|
||||||
@@ -270,12 +276,14 @@ function TaskChatText({ entries }: { entries: AgentLogEntry[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TaskChatToolEntry({ entry }: { entry: AgentLogEntry }) {
|
function TaskChatToolEntry({ entry }: { entry: AgentLogEntry }) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
className={`task-chat-tool-entry task-chat-tool-entry--${entry.type.replace("_", "-")}`}
|
className={`task-chat-tool-entry task-chat-tool-entry--${entry.type.replace("_", "-")}`}
|
||||||
data-testid={`task-chat-entry-${entry.type}`}
|
data-testid={`task-chat-entry-${entry.type}`}
|
||||||
>
|
>
|
||||||
<div className="task-chat-entry-kicker">{formatEntryLabel(entry)}</div>
|
<div className="task-chat-entry-kicker">{formatEntryLabel(entry, t)}</div>
|
||||||
<div className="task-chat-entry-text">{entry.text}</div>
|
<div className="task-chat-entry-text">{entry.text}</div>
|
||||||
{entry.detail ? <pre className="task-chat-tool-detail">{linkifyFilePaths(entry.detail)}</pre> : null}
|
{entry.detail ? <pre className="task-chat-tool-detail">{linkifyFilePaths(entry.detail)}</pre> : null}
|
||||||
</article>
|
</article>
|
||||||
@@ -310,25 +318,26 @@ function getToolGroupRows(entries: AgentLogEntry[]): TaskChatToolGroupRow[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TaskChatToolInvocation({ row }: { row: Extract<TaskChatToolGroupRow, { kind: "invocation" }> }) {
|
function TaskChatToolInvocation({ row }: { row: Extract<TaskChatToolGroupRow, { kind: "invocation" }> }) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const completion = row.completion;
|
const completion = row.completion;
|
||||||
const completionLabel = completion ? formatEntryLabel(completion).replace("Tool ", "") : undefined;
|
const completionLabel = completion ? formatCompletionLabel(completion, t) : undefined;
|
||||||
const className = `task-chat-tool-entry task-chat-tool-invocation${completion?.type === "tool_error" ? " task-chat-tool-entry--tool-error" : ""}`;
|
const className = `task-chat-tool-entry task-chat-tool-invocation${completion?.type === "tool_error" ? " task-chat-tool-entry--tool-error" : ""}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={className} data-testid="task-chat-tool-invocation">
|
<article className={className} data-testid="task-chat-tool-invocation">
|
||||||
<div className="task-chat-entry-kicker">
|
<div className="task-chat-entry-kicker">
|
||||||
{completionLabel ? `Tool call → ${completionLabel}` : "Tool call"}
|
{completionLabel ? t("taskChat.toolCallTo", "Tool call → {{label}}", { label: completionLabel }) : t("taskChat.toolCall", "Tool call")}
|
||||||
</div>
|
</div>
|
||||||
<div className="task-chat-entry-text">{row.call.text}</div>
|
<div className="task-chat-entry-text">{row.call.text}</div>
|
||||||
{row.call.detail ? (
|
{row.call.detail ? (
|
||||||
<div className="task-chat-tool-detail-block">
|
<div className="task-chat-tool-detail-block">
|
||||||
<div className="task-chat-tool-detail-label">Arguments</div>
|
<div className="task-chat-tool-detail-label">{t("taskChat.arguments", "Arguments")}</div>
|
||||||
<pre className="task-chat-tool-detail">{linkifyFilePaths(row.call.detail)}</pre>
|
<pre className="task-chat-tool-detail">{linkifyFilePaths(row.call.detail)}</pre>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{completion?.detail ? (
|
{completion?.detail ? (
|
||||||
<div className="task-chat-tool-detail-block">
|
<div className="task-chat-tool-detail-block">
|
||||||
<div className="task-chat-tool-detail-label">{completion.type === "tool_error" ? "Error" : "Result"}</div>
|
<div className="task-chat-tool-detail-label">{completion.type === "tool_error" ? t("taskChat.error", "Error") : t("taskChat.result", "Result")}</div>
|
||||||
<pre className="task-chat-tool-detail">{linkifyFilePaths(completion.detail)}</pre>
|
<pre className="task-chat-tool-detail">{linkifyFilePaths(completion.detail)}</pre>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -337,6 +346,7 @@ function TaskChatToolInvocation({ row }: { row: Extract<TaskChatToolGroupRow, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TaskChatToolGroup({ entries }: { entries: AgentLogEntry[] }) {
|
function TaskChatToolGroup({ entries }: { entries: AgentLogEntry[] }) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const invocationEntries = getToolInvocationEntries(entries);
|
const invocationEntries = getToolInvocationEntries(entries);
|
||||||
const invocationCount = invocationEntries.length;
|
const invocationCount = invocationEntries.length;
|
||||||
const errorCount = entries.filter((entry) => entry.type === "tool_error").length;
|
const errorCount = entries.filter((entry) => entry.type === "tool_error").length;
|
||||||
@@ -346,16 +356,16 @@ function TaskChatToolGroup({ entries }: { entries: AgentLogEntry[] }) {
|
|||||||
return (
|
return (
|
||||||
<details className="task-chat-tool-group" data-testid="task-chat-tool-group">
|
<details className="task-chat-tool-group" data-testid="task-chat-tool-group">
|
||||||
<summary className="task-chat-tool-group-summary">
|
<summary className="task-chat-tool-group-summary">
|
||||||
<span className="task-chat-tool-group-count">{formatToolCallCount(invocationCount)}</span>
|
<span className="task-chat-tool-group-count">{formatToolCallCount(invocationCount, t)}</span>
|
||||||
{visibleNames.length > 0 ? (
|
{visibleNames.length > 0 ? (
|
||||||
<span className="task-chat-tool-group-names" aria-label="Tool names">
|
<span className="task-chat-tool-group-names" aria-label={t("taskChat.toolNames", "Tool names")}>
|
||||||
{visibleNames.join(", ")}
|
{visibleNames.join(", ")}
|
||||||
{overflowCount > 0 ? <span className="task-chat-tool-group-overflow">, +{overflowCount} more</span> : null}
|
{overflowCount > 0 ? <span className="task-chat-tool-group-overflow">{t("taskChat.moreTools", ", +{{count}} more", { count: overflowCount })}</span> : null}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{errorCount > 0 ? (
|
{errorCount > 0 ? (
|
||||||
<span className="task-chat-tool-group-error-count">
|
<span className="task-chat-tool-group-error-count">
|
||||||
{errorCount === 1 ? "1 error" : `${errorCount} errors`}
|
{t("taskChat.errorCount", "{{count}} error", { count: errorCount })}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</summary>
|
</summary>
|
||||||
@@ -373,11 +383,12 @@ function TaskChatToolGroup({ entries }: { entries: AgentLogEntry[] }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TaskChatThinking({ entries }: { entries: AgentLogEntry[] }) {
|
function TaskChatThinking({ entries }: { entries: AgentLogEntry[] }) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const combinedThinkingText = entries.map((entry) => entry.text).join("");
|
const combinedThinkingText = entries.map((entry) => entry.text).join("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<details className="task-chat-thinking" data-testid="task-chat-thinking" open>
|
<details className="task-chat-thinking" data-testid="task-chat-thinking" open>
|
||||||
<summary className="task-chat-thinking-summary">Thinking</summary>
|
<summary className="task-chat-thinking-summary">{t("taskChat.thinking", "Thinking")}</summary>
|
||||||
<div className="task-chat-thinking-body">
|
<div className="task-chat-thinking-body">
|
||||||
<div
|
<div
|
||||||
className="markdown-body task-chat-markdown task-chat-thinking-markdown"
|
className="markdown-body task-chat-markdown task-chat-thinking-markdown"
|
||||||
@@ -407,12 +418,13 @@ FNXC:TaskChatTimestamps 2026-06-17-15:43:
|
|||||||
FN-6597 requires small relative timestamps on both task-chat agent group headers and user message headers, computed at render time from existing transcript timestamps without adding a live timer.
|
FN-6597 requires small relative timestamps on both task-chat agent group headers and user message headers, computed at render time from existing transcript timestamps without adding a live timer.
|
||||||
*/
|
*/
|
||||||
function TaskChatUserMessage({ message }: { message: UserChatMessage }) {
|
function TaskChatUserMessage({ message }: { message: UserChatMessage }) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const relativeTime = formatRelativeTimeAgo(message.createdAt);
|
const relativeTime = formatRelativeTimeAgo(message.createdAt);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="task-chat-user-group" aria-label="You message">
|
<section className="task-chat-user-group" aria-label={t("taskChat.youMessage", "You message")}>
|
||||||
<div className="task-chat-user-header">
|
<div className="task-chat-user-header">
|
||||||
<div className="task-chat-role-label">You</div>
|
<div className="task-chat-role-label">{t("taskChat.you", "You")}</div>
|
||||||
{relativeTime ? (
|
{relativeTime ? (
|
||||||
<span className="task-chat-timestamp" data-testid="task-chat-user-time">
|
<span className="task-chat-timestamp" data-testid="task-chat-user-time">
|
||||||
{relativeTime}
|
{relativeTime}
|
||||||
@@ -431,6 +443,7 @@ function TaskChatUserMessage({ message }: { message: UserChatMessage }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TaskChatTab({ task, projectId, active, addToast, sessionLive, onTaskUpdated, expanded = false, onToggleExpanded }: TaskChatTabProps) {
|
export function TaskChatTab({ task, projectId, active, addToast, sessionLive, onTaskUpdated, expanded = false, onToggleExpanded }: TaskChatTabProps) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const { entries, loading, loadMore, hasMore, loadingMore } = useAgentLogs(task.id, active, projectId);
|
const { entries, loading, loadMore, hasMore, loadingMore } = useAgentLogs(task.id, active, projectId);
|
||||||
const [draft, setDraft] = useState("");
|
const [draft, setDraft] = useState("");
|
||||||
const [sending, setSending] = useState(false);
|
const [sending, setSending] = useState(false);
|
||||||
@@ -452,7 +465,7 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
() => mergeUserMessages(task.steeringComments, optimisticMessages),
|
() => mergeUserMessages(task.steeringComments, optimisticMessages),
|
||||||
[optimisticMessages, task.steeringComments],
|
[optimisticMessages, task.steeringComments],
|
||||||
);
|
);
|
||||||
const transcriptItems = useMemo(() => buildTranscriptItems(entries, userMessages), [entries, userMessages]);
|
const transcriptItems = useMemo(() => buildTranscriptItems(entries, userMessages, t), [entries, t, userMessages]);
|
||||||
const transcriptItemCount = entries.length + userMessages.length;
|
const transcriptItemCount = entries.length + userMessages.length;
|
||||||
const firstEntryKey = entries[0] ? getEntryKey(entries[0], 0) : null;
|
const firstEntryKey = entries[0] ? getEntryKey(entries[0], 0) : null;
|
||||||
const activeSession = isActiveAgentSession(task, { sessionLive });
|
const activeSession = isActiveAgentSession(task, { sessionLive });
|
||||||
@@ -463,13 +476,13 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
* The task-detail chat must never silently accept a question when no agent session will consume it. Keep idle chats sendable, but surface that the message is saved as guidance for the next task run instead of implying a live reply.
|
* The task-detail chat must never silently accept a question when no agent session will consume it. Keep idle chats sendable, but surface that the message is saved as guidance for the next task run instead of implying a live reply.
|
||||||
*/
|
*/
|
||||||
const sessionHint = isDoneTask
|
const sessionHint = isDoneTask
|
||||||
? "Send a message to start a refinement task for this completed task."
|
? t("taskChat.doneSessionHint", "Send a message to start a refinement task for this completed task.")
|
||||||
: activeSession
|
: activeSession
|
||||||
? "Message the active agent session. Guidance is delivered to the running session in real time."
|
? t("taskChat.activeSessionHint", "Message the active agent session. Guidance is delivered to the running session in real time.")
|
||||||
: "No agent is working on this task right now. Your message is saved as guidance and will reach an agent the next time this task runs.";
|
: t("taskChat.idleSessionHint", "No agent is working on this task right now. Your message is saved as guidance and will reach an agent the next time this task runs.");
|
||||||
const composerPlaceholder = isDoneTask
|
const composerPlaceholder = isDoneTask
|
||||||
? "Start a refinement task for this completed task"
|
? t("taskChat.donePlaceholder", "Start a refinement task for this completed task")
|
||||||
: "Steer the currently executing agent";
|
: t("taskChat.activePlaceholder", "Steer the currently executing agent");
|
||||||
const canSend = draft.trim().length > 0 && !sending;
|
const canSend = draft.trim().length > 0 && !sending;
|
||||||
|
|
||||||
const resizeComposer = useCallback(() => {
|
const resizeComposer = useCallback(() => {
|
||||||
@@ -711,7 +724,7 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-sm task-chat-expand-toggle task-chat-expand-toggle--overlay"
|
className="btn btn-icon btn-sm task-chat-expand-toggle task-chat-expand-toggle--overlay"
|
||||||
onClick={onToggleExpanded}
|
onClick={onToggleExpanded}
|
||||||
aria-label={expanded ? "Collapse chat" : "Expand chat to full modal"}
|
aria-label={expanded ? t("taskChat.collapseChat", "Collapse chat") : t("taskChat.expandChat", "Expand chat to full modal")}
|
||||||
aria-pressed={expanded}
|
aria-pressed={expanded}
|
||||||
data-testid="task-chat-expand-toggle"
|
data-testid="task-chat-expand-toggle"
|
||||||
>
|
>
|
||||||
@@ -731,17 +744,17 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
{loadingMore ? (
|
{loadingMore ? (
|
||||||
<div className="task-chat-load-previous-status" role="status" data-testid="task-chat-load-previous-loading">
|
<div className="task-chat-load-previous-status" role="status" data-testid="task-chat-load-previous-loading">
|
||||||
<Loader2 className="animate-spin" aria-hidden="true" />
|
<Loader2 className="animate-spin" aria-hidden="true" />
|
||||||
<span>Loading earlier messages…</span>
|
<span>{t("taskChat.loadingEarlierMessages", "Loading earlier messages…")}</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary btn-sm task-chat-load-previous"
|
className="btn btn-secondary btn-sm task-chat-load-previous"
|
||||||
onClick={() => { void loadPreviousMessages(); }}
|
onClick={() => { void loadPreviousMessages(); }}
|
||||||
aria-label="Load previous messages"
|
aria-label={t("taskChat.loadPreviousMessages", "Load previous messages")}
|
||||||
data-testid="task-chat-load-previous"
|
data-testid="task-chat-load-previous"
|
||||||
>
|
>
|
||||||
Load previous messages
|
{t("taskChat.loadPreviousMessages", "Load previous messages")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -749,10 +762,10 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
{loading && transcriptItemCount === 0 ? (
|
{loading && transcriptItemCount === 0 ? (
|
||||||
<div className="task-chat-empty" role="status">
|
<div className="task-chat-empty" role="status">
|
||||||
<Loader2 className="animate-spin" aria-hidden="true" />
|
<Loader2 className="animate-spin" aria-hidden="true" />
|
||||||
<span>Loading agent output…</span>
|
<span>{t("taskChat.loadingAgentOutput", "Loading agent output…")}</span>
|
||||||
</div>
|
</div>
|
||||||
) : transcriptItemCount === 0 ? (
|
) : transcriptItemCount === 0 ? (
|
||||||
<div className="task-chat-empty">No agent output yet. Live messages from Planner, Executor, Reviewer, and Merger agents will appear here.</div>
|
<div className="task-chat-empty">{t("taskChat.emptyAgentOutput", "No agent output yet. Live messages from Planner, Executor, Reviewer, and Merger agents will appear here.")}</div>
|
||||||
) : (
|
) : (
|
||||||
transcriptItems.map((item, itemIndex) => {
|
transcriptItems.map((item, itemIndex) => {
|
||||||
if (item.kind === "user") {
|
if (item.kind === "user") {
|
||||||
@@ -768,13 +781,13 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
const latestEntryTimestamp = item.entries[item.entries.length - 1]?.timestamp ?? "";
|
const latestEntryTimestamp = item.entries[item.entries.length - 1]?.timestamp ?? "";
|
||||||
const relativeTime = formatRelativeTimeAgo(latestEntryTimestamp);
|
const relativeTime = formatRelativeTimeAgo(latestEntryTimestamp);
|
||||||
return (
|
return (
|
||||||
<section className="task-chat-group" key={`${item.role ?? "agent"}-${itemIndex}`} aria-label={`${item.label} messages`}>
|
<section className="task-chat-group" key={`${item.role ?? "agent"}-${itemIndex}`} aria-label={t("taskChat.agentMessages", "{{label}} messages", { label: item.label })}>
|
||||||
<header className="task-chat-group-header">
|
<header className="task-chat-group-header">
|
||||||
<AgentAvatar agent={avatarAgent} className="task-chat-avatar" />
|
<AgentAvatar agent={avatarAgent} className="task-chat-avatar" />
|
||||||
<div>
|
<div>
|
||||||
<div className="task-chat-role-label">{item.label}</div>
|
<div className="task-chat-role-label">{item.label}</div>
|
||||||
<div className="task-chat-group-meta">
|
<div className="task-chat-group-meta">
|
||||||
<span>{item.entries.length === 1 ? "1 entry" : `${item.entries.length} entries`}</span>
|
<span>{t("taskChat.entryCount", "{{count}} entry", { count: item.entries.length })}</span>
|
||||||
{relativeTime ? (
|
{relativeTime ? (
|
||||||
<span className="task-chat-timestamp" data-testid="task-chat-group-time">
|
<span className="task-chat-timestamp" data-testid="task-chat-group-time">
|
||||||
{relativeTime}
|
{relativeTime}
|
||||||
@@ -798,11 +811,11 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
type="button"
|
type="button"
|
||||||
className="task-chat-jump-to-bottom"
|
className="task-chat-jump-to-bottom"
|
||||||
onClick={scrollTranscriptToBottom}
|
onClick={scrollTranscriptToBottom}
|
||||||
aria-label="Jump to latest message"
|
aria-label={t("taskChat.jumpToLatestMessage", "Jump to latest message")}
|
||||||
data-testid="task-chat-jump-to-bottom"
|
data-testid="task-chat-jump-to-bottom"
|
||||||
>
|
>
|
||||||
<ChevronDown aria-hidden="true" />
|
<ChevronDown aria-hidden="true" />
|
||||||
<span>Latest</span>
|
<span>{t("taskChat.latest", "Latest")}</span>
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -826,15 +839,15 @@ export function TaskChatTab({ task, projectId, active, addToast, sessionLive, on
|
|||||||
onChange={(event) => setDraft(event.target.value)}
|
onChange={(event) => setDraft(event.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
disabled={sending}
|
disabled={sending}
|
||||||
aria-label="Message active agent session"
|
aria-label={t("taskChat.messageActiveAgentSession", "Message active agent session")}
|
||||||
rows={1}
|
rows={1}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary btn-icon task-chat-send"
|
className="btn btn-primary btn-icon task-chat-send"
|
||||||
disabled={!canSend}
|
disabled={!canSend}
|
||||||
aria-label={sending ? "Sending" : "Send"}
|
aria-label={sending ? t("taskChat.sending", "Sending") : t("common:actions.send", "Send")}
|
||||||
title={sending ? "Sending" : "Send"}
|
title={sending ? t("taskChat.sending", "Sending") : t("common:actions.send", "Send")}
|
||||||
>
|
>
|
||||||
{sending ? <Loader2 className="animate-spin" aria-hidden="true" /> : <Send aria-hidden="true" />}
|
{sending ? <Loader2 className="animate-spin" aria-hidden="true" /> : <Send aria-hidden="true" />}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export function TaskComments({ task, onTaskUpdated, addToast, currentAuthor = "u
|
|||||||
<div className="detail-log-header comments-header-row">
|
<div className="detail-log-header comments-header-row">
|
||||||
<div className="comments-author-row">
|
<div className="comments-author-row">
|
||||||
{isAIGuidance ? (
|
{isAIGuidance ? (
|
||||||
<span className="ai-guidance-badge" data-testid="ai-guidance-badge">AI Guidance</span>
|
<span className="ai-guidance-badge" data-testid="ai-guidance-badge">{t("comments.aiGuidance", "AI Guidance")}</span>
|
||||||
) : (
|
) : (
|
||||||
<strong>{comment.author}</strong>
|
<strong>{comment.author}</strong>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -71,14 +71,20 @@ interface ModelSelection {
|
|||||||
|
|
||||||
const ACTIVE_STATUSES = new Set(["planning", "researching", "executing", "finalizing", "merging", "merging-fix"]);
|
const ACTIVE_STATUSES = new Set(["planning", "researching", "executing", "finalizing", "merging", "merging-fix"]);
|
||||||
const STALE_PAUSED_REVIEW_LOG_REGEX = /^Stale paused review surfaced \[([^\]]+)\]/;
|
const STALE_PAUSED_REVIEW_LOG_REGEX = /^Stale paused review surfaced \[([^\]]+)\]/;
|
||||||
|
const EMPTY_MARKDOWN_CHILD_SEPARATOR = "";
|
||||||
|
const STRING_OBJECT_TAG = "[object String]";
|
||||||
|
|
||||||
|
function isStringValue(value: unknown): value is string {
|
||||||
|
return Object.prototype.toString.call(value) === STRING_OBJECT_TAG;
|
||||||
|
}
|
||||||
|
|
||||||
const markdownLinkifyComponents: Components = {
|
const markdownLinkifyComponents: Components = {
|
||||||
p: ({ children, ...props }) => <p {...props}>{linkifyReactChildren(children)}</p>,
|
p: ({ children, ...props }) => <p {...props}>{linkifyReactChildren(children)}</p>,
|
||||||
li: ({ children, ...props }) => <li {...props}>{linkifyReactChildren(children)}</li>,
|
li: ({ children, ...props }) => <li {...props}>{linkifyReactChildren(children)}</li>,
|
||||||
code: ({ children, ...props }) => {
|
code: ({ children, ...props }) => {
|
||||||
const text = typeof children === "string" ? children : React.Children.toArray(children).join("");
|
const text = React.Children.toArray(children).join(EMPTY_MARKDOWN_CHILD_SEPARATOR);
|
||||||
const linkedChildren = linkifyFilePaths(text);
|
const linkedChildren = linkifyFilePaths(text);
|
||||||
if (linkedChildren.length === 1 && typeof linkedChildren[0] === "string") {
|
if (linkedChildren.length === 1 && linkedChildren[0]?.constructor === String) {
|
||||||
return <code {...props}>{children}</code>;
|
return <code {...props}>{children}</code>;
|
||||||
}
|
}
|
||||||
return <code {...props}>{linkedChildren}</code>;
|
return <code {...props}>{linkedChildren}</code>;
|
||||||
@@ -92,25 +98,25 @@ const markdownLinkifyComponents: Components = {
|
|||||||
*/
|
*/
|
||||||
function extractExecutorModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
function extractExecutorModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
||||||
let result: { provider: string; modelId: string } | null = null;
|
let result: { provider: string; modelId: string } | null = null;
|
||||||
for (const entry of entries) {
|
entries.forEach((entry) => {
|
||||||
if (entry.agent !== "executor" || entry.type !== "text") continue;
|
if (entry.agent !== "executor" || entry.type !== "text") return;
|
||||||
const match = entry.text.match(/^Executor using model: (.+?)\/(.+)$/);
|
const match = entry.text.match(/^Executor using model: (.+?)\/(.+)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
result = { provider: match[1], modelId: match[2] };
|
result = { provider: match[1], modelId: match[2] };
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractReviewerModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
function extractReviewerModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
||||||
let result: { provider: string; modelId: string } | null = null;
|
let result: { provider: string; modelId: string } | null = null;
|
||||||
for (const entry of entries) {
|
entries.forEach((entry) => {
|
||||||
if (entry.agent !== "reviewer" || entry.type !== "text") continue;
|
if (entry.agent !== "reviewer" || entry.type !== "text") return;
|
||||||
const match = entry.text.match(/^Reviewer using model: (.+?)\/(.+)$/);
|
const match = entry.text.match(/^Reviewer using model: (.+?)\/(.+)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
result = { provider: match[1], modelId: match[2] };
|
result = { provider: match[1], modelId: match[2] };
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +135,7 @@ function hasUsableTrackingTitle(task: { title?: string | null; description?: str
|
|||||||
|
|
||||||
function extractAssignedRuntimeModel(agent: Agent | null | undefined): ModelSelection {
|
function extractAssignedRuntimeModel(agent: Agent | null | undefined): ModelSelection {
|
||||||
const runtimeConfig = (agent?.runtimeConfig ?? undefined) as Record<string, unknown> | undefined;
|
const runtimeConfig = (agent?.runtimeConfig ?? undefined) as Record<string, unknown> | undefined;
|
||||||
const model = typeof runtimeConfig?.model === "string" ? runtimeConfig.model.trim() : "";
|
const model = isStringValue(runtimeConfig?.model) ? runtimeConfig.model.trim() : "";
|
||||||
if (model) {
|
if (model) {
|
||||||
const slashIdx = model.indexOf("/");
|
const slashIdx = model.indexOf("/");
|
||||||
if (slashIdx > 0 && slashIdx < model.length - 1) {
|
if (slashIdx > 0 && slashIdx < model.length - 1) {
|
||||||
@@ -140,8 +146,8 @@ function extractAssignedRuntimeModel(agent: Agent | null | undefined): ModelSele
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = typeof runtimeConfig?.modelProvider === "string" ? runtimeConfig.modelProvider.trim() : "";
|
const provider = isStringValue(runtimeConfig?.modelProvider) ? runtimeConfig.modelProvider.trim() : "";
|
||||||
const modelId = typeof runtimeConfig?.modelId === "string" ? runtimeConfig.modelId.trim() : "";
|
const modelId = isStringValue(runtimeConfig?.modelId) ? runtimeConfig.modelId.trim() : "";
|
||||||
return {
|
return {
|
||||||
provider: provider || undefined,
|
provider: provider || undefined,
|
||||||
modelId: modelId || undefined,
|
modelId: modelId || undefined,
|
||||||
@@ -209,13 +215,13 @@ function resolveEffectiveValidator(
|
|||||||
function extractPlanningModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
function extractPlanningModelFromLog(entries: AgentLogEntry[]): { provider: string; modelId: string } | null {
|
||||||
// Iterate in chronological order; last match wins
|
// Iterate in chronological order; last match wins
|
||||||
let result: { provider: string; modelId: string } | null = null;
|
let result: { provider: string; modelId: string } | null = null;
|
||||||
for (const entry of entries) {
|
entries.forEach((entry) => {
|
||||||
if (entry.agent !== "triage" || entry.type !== "text") continue;
|
if (entry.agent !== "triage" || entry.type !== "text") return;
|
||||||
const match = entry.text.match(/^Triage using model: (.+?)\/(.+)$/);
|
const match = entry.text.match(/^Triage using model: (.+?)\/(.+)$/);
|
||||||
if (match) {
|
if (match) {
|
||||||
result = { provider: match[1], modelId: match[2] };
|
result = { provider: match[1], modelId: match[2] };
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +437,7 @@ function normalizeSourceIssueUrl(value: string): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTaskPriorityValue(priority: Task["priority"]): TaskPriority {
|
function normalizeTaskPriorityValue(priority: Task["priority"]): TaskPriority {
|
||||||
return typeof priority === "string" && (TASK_PRIORITIES as readonly string[]).includes(priority)
|
return isStringValue(priority) && (TASK_PRIORITIES as readonly string[]).includes(priority)
|
||||||
? (priority as TaskPriority)
|
? (priority as TaskPriority)
|
||||||
: DEFAULT_TASK_PRIORITY;
|
: DEFAULT_TASK_PRIORITY;
|
||||||
}
|
}
|
||||||
@@ -456,7 +462,7 @@ interface ProvenanceLabelOptions {
|
|||||||
|
|
||||||
function getIssueUrlFromMetadata(metadata: Task["sourceMetadata"]): string | undefined {
|
function getIssueUrlFromMetadata(metadata: Task["sourceMetadata"]): string | undefined {
|
||||||
const issueUrl = metadata?.issueUrl;
|
const issueUrl = metadata?.issueUrl;
|
||||||
return typeof issueUrl === "string" && issueUrl.length > 0 ? issueUrl : undefined;
|
return isStringValue(issueUrl) && issueUrl.length > 0 ? issueUrl : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseGithubIssueLabel(url: string): { label: string; href: string } | null {
|
function parseGithubIssueLabel(url: string): { label: string; href: string } | null {
|
||||||
@@ -474,12 +480,12 @@ function parseGithubIssueLabel(url: string): { label: string; href: string } | n
|
|||||||
|
|
||||||
function getResearchContextInfo(metadata: Task["sourceMetadata"]): string | undefined {
|
function getResearchContextInfo(metadata: Task["sourceMetadata"]): string | undefined {
|
||||||
const findingLabel = metadata?.findingLabel;
|
const findingLabel = metadata?.findingLabel;
|
||||||
if (typeof findingLabel === "string" && findingLabel.length > 0) {
|
if (isStringValue(findingLabel) && findingLabel.length > 0) {
|
||||||
return findingLabel;
|
return findingLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const runId = metadata?.runId;
|
const runId = metadata?.runId;
|
||||||
return typeof runId === "string" && runId.length > 0 ? runId : undefined;
|
return isStringValue(runId) && runId.length > 0 ? runId : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AgentDetailView = lazy(() => import("./AgentDetailView").then((m) => ({ default: m.AgentDetailView })));
|
const AgentDetailView = lazy(() => import("./AgentDetailView").then((m) => ({ default: m.AgentDetailView })));
|
||||||
@@ -659,7 +665,7 @@ export function TaskDetailContent({
|
|||||||
(task.stuckKillCount ?? 0) > 0 ||
|
(task.stuckKillCount ?? 0) > 0 ||
|
||||||
(task.recoveryRetryCount ?? 0) > 0 ||
|
(task.recoveryRetryCount ?? 0) > 0 ||
|
||||||
Boolean(task.nextRecoveryAt);
|
Boolean(task.nextRecoveryAt);
|
||||||
const nearDuplicateOf = typeof workingTask.sourceMetadata?.nearDuplicateOf === "string"
|
const nearDuplicateOf = isStringValue(workingTask.sourceMetadata?.nearDuplicateOf)
|
||||||
? workingTask.sourceMetadata.nearDuplicateOf
|
? workingTask.sourceMetadata.nearDuplicateOf
|
||||||
: null;
|
: null;
|
||||||
const nearDuplicateCanonical = nearDuplicateOf
|
const nearDuplicateCanonical = nearDuplicateOf
|
||||||
@@ -810,11 +816,11 @@ export function TaskDetailContent({
|
|||||||
setCustomFieldValues(updated.customFields ?? {});
|
setCustomFieldValues(updated.customFields ?? {});
|
||||||
onTaskUpdated?.(updated);
|
onTaskUpdated?.(updated);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ApiRequestError && err.details && typeof err.details.fieldId === "string") {
|
if (err instanceof ApiRequestError && err.details && isStringValue(err.details.fieldId)) {
|
||||||
setCustomFieldError({
|
setCustomFieldError({
|
||||||
code: (err.details.code as CustomFieldRejection["code"]) ?? "type-mismatch",
|
code: (err.details.code as CustomFieldRejection["code"]) ?? "type-mismatch",
|
||||||
fieldId: err.details.fieldId,
|
fieldId: err.details.fieldId,
|
||||||
detail: typeof err.details.detail === "string" ? err.details.detail : err.message,
|
detail: isStringValue(err.details.detail) ? err.details.detail : err.message,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -909,7 +915,7 @@ export function TaskDetailContent({
|
|||||||
tabId: `plugin-${entry.pluginId}-${index}` as TabId,
|
tabId: `plugin-${entry.pluginId}-${index}` as TabId,
|
||||||
}));
|
}));
|
||||||
const activePluginTab =
|
const activePluginTab =
|
||||||
typeof activeTab === "string" && activeTab.startsWith("plugin-")
|
isStringValue(activeTab) && activeTab.startsWith("plugin-")
|
||||||
? pluginTabs.find((tab) => tab.tabId === activeTab) ?? null
|
? pluginTabs.find((tab) => tab.tabId === activeTab) ?? null
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -2981,7 +2987,7 @@ export function TaskDetailContent({
|
|||||||
)}
|
)}
|
||||||
{provenanceDisplay.parentTaskId && (
|
{provenanceDisplay.parentTaskId && (
|
||||||
<>
|
<>
|
||||||
{" "}of{" "}
|
{" "}{t("taskDetail.provenance.parentTaskOf", "of")}{" "}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="detail-provenance-link"
|
className="detail-provenance-link"
|
||||||
@@ -3021,7 +3027,7 @@ export function TaskDetailContent({
|
|||||||
<div className="detail-provenance detail-pr-link-row">
|
<div className="detail-provenance detail-pr-link-row">
|
||||||
<GitBranch aria-hidden="true" />
|
<GitBranch aria-hidden="true" />
|
||||||
<span>
|
<span>
|
||||||
PR{" "}
|
{t("taskDetail.pr.label", "PR")} {" "}
|
||||||
{task.prInfo?.url ? (
|
{task.prInfo?.url ? (
|
||||||
<a
|
<a
|
||||||
className="detail-provenance-link"
|
className="detail-provenance-link"
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ import type { DiscoveredSkill } from "../api";
|
|||||||
import type { ToastType } from "../hooks/useToast";
|
import type { ToastType } from "../hooks/useToast";
|
||||||
import { useOverlayDismiss } from "../hooks/useOverlayDismiss";
|
import { useOverlayDismiss } from "../hooks/useOverlayDismiss";
|
||||||
import { useConfirm } from "../hooks/useConfirm";
|
import { useConfirm } from "../hooks/useConfirm";
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:i18n-Localize 2026-06-20-00:00:
|
||||||
|
FN-6770 localizes this workflow surface through t() and authored en catalog keys so hardcoded user-facing copy does not need a lint.ignore deferral.
|
||||||
|
*/
|
||||||
import { useModalResizePersist } from "../hooks/useModalResizePersist";
|
import { useModalResizePersist } from "../hooks/useModalResizePersist";
|
||||||
import { useAppSettings } from "../hooks/useAppSettings";
|
import { useAppSettings } from "../hooks/useAppSettings";
|
||||||
import { isMobileViewport, useViewportMode } from "../hooks/useViewportMode";
|
import { isMobileViewport, useViewportMode } from "../hooks/useViewportMode";
|
||||||
@@ -210,6 +215,9 @@ const NOTIFY_EVENT_OPTIONS = [
|
|||||||
"workflow-notify",
|
"workflow-notify",
|
||||||
] as const;
|
] as const;
|
||||||
const NOTIFY_CUSTOM_EVENT_VALUE = "__custom";
|
const NOTIFY_CUSTOM_EVENT_VALUE = "__custom";
|
||||||
|
const WORKFLOW_CLI_COMMAND_PLACEHOLDER = "npm test -- --runInBand";
|
||||||
|
const WORKFLOW_CODE_SOURCE_PLACEHOLDER = "export default async (ctx) => ({ outcome: \"success\" });";
|
||||||
|
const WORKFLOW_NOTIFY_MESSAGE_PLACEHOLDER = "Task {{taskId}} reached {{workflowName}}";
|
||||||
|
|
||||||
const PALETTE: Array<{ kind: WorkflowEditorNodeKind; label: string; icon: typeof MessageSquare; presetConfig?: Record<string, unknown> }> = [
|
const PALETTE: Array<{ kind: WorkflowEditorNodeKind; label: string; icon: typeof MessageSquare; presetConfig?: Record<string, unknown> }> = [
|
||||||
{ kind: "prompt", label: "Prompt", icon: MessageSquare },
|
{ kind: "prompt", label: "Prompt", icon: MessageSquare },
|
||||||
@@ -1019,11 +1027,11 @@ function InnerEditor({
|
|||||||
return isMobileMode ? null : data[0]?.id ?? null;
|
return isMobileMode ? null : data[0]?.id ?? null;
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to load workflows", "error");
|
addToast(getErrorMessage(err) || t("workflows.loadFailed", "Failed to load workflows"), "error");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [projectId, addToast, isMobileMode, initialAction, initialWorkflowId]);
|
}, [projectId, addToast, isMobileMode, initialAction, initialWorkflowId, t]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void loadWorkflows();
|
void loadWorkflows();
|
||||||
@@ -1752,11 +1760,11 @@ function InnerEditor({
|
|||||||
setWorkflows((ws) => [...ws, created]);
|
setWorkflows((ws) => [...ws, created]);
|
||||||
setActiveId(created.id);
|
setActiveId(created.id);
|
||||||
setWorkflowListStageOpen(false);
|
setWorkflowListStageOpen(false);
|
||||||
addToast(`Duplicated to "${created.name}" — editable`, "success");
|
addToast(t("workflows.duplicatedEditable", "Duplicated to \"{{name}}\" — editable", { name: created.name }), "success");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast(getErrorMessage(err) || "Failed to duplicate workflow", "error");
|
addToast(getErrorMessage(err) || t("workflows.duplicateFailed", "Failed to duplicate workflow"), "error");
|
||||||
}
|
}
|
||||||
}, [activeWorkflow, projectId, addToast]);
|
}, [activeWorkflow, projectId, addToast, t]);
|
||||||
|
|
||||||
const handleSave = useCallback(async () => {
|
const handleSave = useCallback(async () => {
|
||||||
if (!activeWorkflow) return;
|
if (!activeWorkflow) return;
|
||||||
@@ -2107,25 +2115,25 @@ function InnerEditor({
|
|||||||
// step-review offers an optional review model picker (KTD-4).
|
// step-review offers an optional review model picker (KTD-4).
|
||||||
if (selectedNode?.data.kind === "step-review" && models.length === 0) {
|
if (selectedNode?.data.kind === "step-review" && models.length === 0) {
|
||||||
fetchModels().then((res) => setModels(res.models)).catch((err) => {
|
fetchModels().then((res) => setModels(res.models)).catch((err) => {
|
||||||
addToast(getErrorMessage(err) || "Failed to load models", "error");
|
addToast(getErrorMessage(err) || t("workflowEditor.modelsLoadFailed", "Failed to load models"), "error");
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!selectedNode || (selectedNode.data.kind !== "prompt" && selectedNode.data.kind !== "gate")) return;
|
if (!selectedNode || (selectedNode.data.kind !== "prompt" && selectedNode.data.kind !== "gate")) return;
|
||||||
if (currentExecutor === "model" && models.length === 0) {
|
if (currentExecutor === "model" && models.length === 0) {
|
||||||
fetchModels().then((res) => setModels(res.models)).catch((err) => {
|
fetchModels().then((res) => setModels(res.models)).catch((err) => {
|
||||||
addToast(getErrorMessage(err) || "Failed to load models", "error");
|
addToast(getErrorMessage(err) || t("workflowEditor.modelsLoadFailed", "Failed to load models"), "error");
|
||||||
});
|
});
|
||||||
} else if (currentExecutor === "agent" && agents.length === 0) {
|
} else if (currentExecutor === "agent" && agents.length === 0) {
|
||||||
// Project-scoped, matching WorkflowColumnPanel's fetchAgents(undefined,
|
// Project-scoped, matching WorkflowColumnPanel's fetchAgents(undefined,
|
||||||
// projectId) — an unscoped fetch returns the wrong registry in
|
// projectId) — an unscoped fetch returns the wrong registry in
|
||||||
// multi-project deployments (PR #1432 review).
|
// multi-project deployments (PR #1432 review).
|
||||||
fetchAgents(undefined, projectId).then(setAgents).catch((err) => {
|
fetchAgents(undefined, projectId).then(setAgents).catch((err) => {
|
||||||
addToast(getErrorMessage(err) || "Failed to load agents", "error");
|
addToast(getErrorMessage(err) || t("workflowEditor.agentsLoadFailed", "Failed to load agents"), "error");
|
||||||
});
|
});
|
||||||
} else if (currentExecutor === "skill" && skills.length === 0) {
|
} else if (currentExecutor === "skill" && skills.length === 0) {
|
||||||
fetchDiscoveredSkills(projectId).then(setSkills).catch((err) => {
|
fetchDiscoveredSkills(projectId).then(setSkills).catch((err) => {
|
||||||
addToast(getErrorMessage(err) || "Failed to load skills", "error");
|
addToast(getErrorMessage(err) || t("workflowEditor.skillsLoadFailed", "Failed to load skills"), "error");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
@@ -2137,6 +2145,7 @@ function InnerEditor({
|
|||||||
models.length,
|
models.length,
|
||||||
agents.length,
|
agents.length,
|
||||||
skills.length,
|
skills.length,
|
||||||
|
t,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// ── Dirty-state dismissal guard (U4, R7) ────────────────────────────────────
|
// ── Dirty-state dismissal guard (U4, R7) ────────────────────────────────────
|
||||||
@@ -2198,12 +2207,12 @@ function InnerEditor({
|
|||||||
Promise.resolve(fetchAgents(undefined, projectId)).then((list) => {
|
Promise.resolve(fetchAgents(undefined, projectId)).then((list) => {
|
||||||
if (!cancelled) setAgents(list ?? []);
|
if (!cancelled) setAgents(list ?? []);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
if (!cancelled) addToast(getErrorMessage(err) || "Failed to load agents", "error");
|
if (!cancelled) addToast(getErrorMessage(err) || t("workflowEditor.agentsLoadFailed", "Failed to load agents"), "error");
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [overrideColumnBinding, agents.length, projectId, addToast]);
|
}, [overrideColumnBinding, agents.length, projectId, addToast, t]);
|
||||||
|
|
||||||
const overlayProps = useOverlayDismiss(requestClose);
|
const overlayProps = useOverlayDismiss(requestClose);
|
||||||
const promptFullscreenOverlay =
|
const promptFullscreenOverlay =
|
||||||
@@ -2226,7 +2235,7 @@ function InnerEditor({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Prompt</span>
|
<span>{t("workflowEditor.prompt", "Prompt")}</span>
|
||||||
<textarea
|
<textarea
|
||||||
rows={undefined}
|
rows={undefined}
|
||||||
value={selectedNodePromptValue}
|
value={selectedNodePromptValue}
|
||||||
@@ -2263,8 +2272,8 @@ function InnerEditor({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<header className="wf-editor-header">
|
<header className="wf-editor-header">
|
||||||
<h2>Workflows</h2>
|
<h2>{t("workflows.title", "Workflows")}</h2>
|
||||||
<button className="wf-editor-close" onClick={requestClose} aria-label="Close workflow editor">
|
<button className="wf-editor-close" onClick={requestClose} aria-label={t("workflows.closeEditor", "Close workflow editor")}>
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
@@ -2349,10 +2358,10 @@ function InnerEditor({
|
|||||||
) : null}
|
) : null}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="wf-editor-empty">
|
<div className="wf-editor-empty">
|
||||||
<Loader2 size={16} className="wf-spin" /> Loading…
|
<Loader2 size={16} className="wf-spin" /> {t("workflows.loading", "Loading…")}
|
||||||
</div>
|
</div>
|
||||||
) : workflows.length === 0 ? (
|
) : workflows.length === 0 ? (
|
||||||
<div className="wf-editor-empty">No workflows yet.</div>
|
<div className="wf-editor-empty">{t("workflows.noneYet", "No workflows yet.")}</div>
|
||||||
) : (
|
) : (
|
||||||
<ul className="wf-editor-list">
|
<ul className="wf-editor-list">
|
||||||
{workflows.map((w) => (
|
{workflows.map((w) => (
|
||||||
@@ -3236,7 +3245,7 @@ function InnerEditor({
|
|||||||
!(compactLayoutEnabled && !isMobileMode) && (
|
!(compactLayoutEnabled && !isMobileMode) && (
|
||||||
<aside className="wf-editor-inspector" data-testid="wf-node-inspector">
|
<aside className="wf-editor-inspector" data-testid="wf-node-inspector">
|
||||||
<div className="wf-inspector-heading">
|
<div className="wf-inspector-heading">
|
||||||
<h3>Node</h3>
|
<h3>{t("workflowNodes.nodeInspector", "Node")}</h3>
|
||||||
{isMobileMode && (
|
{isMobileMode && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -3258,14 +3267,14 @@ function InnerEditor({
|
|||||||
</div>
|
</div>
|
||||||
{isBuiltin && (
|
{isBuiltin && (
|
||||||
<p className="wf-inspector-note wf-inspector-note--info">
|
<p className="wf-inspector-note wf-inspector-note--info">
|
||||||
Read-only built-in — duplicate the workflow to edit nodes.
|
{t("workflowNodes.readOnlyDuplicateToEdit", "Read-only built-in — duplicate the workflow to edit nodes.")}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<fieldset className="wf-inspector-fields" disabled={isBuiltin}>
|
<fieldset className="wf-inspector-fields" disabled={isBuiltin}>
|
||||||
{/* FNXC:WorkflowEditor 2026-06-17-00:20: Start labels are structural and ignored by flowToIr, so exposing the generic Name editor would create a no-op rename. */}
|
{/* FNXC:WorkflowEditor 2026-06-17-00:20: Start labels are structural and ignored by flowToIr, so exposing the generic Name editor would create a no-op rename. */}
|
||||||
{selectedNode.data.kind !== "start" && (
|
{selectedNode.data.kind !== "start" && (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Name</span>
|
<span>{t("common:labels.name", "Name")}</span>
|
||||||
<input
|
<input
|
||||||
value={selectedNode.data.label}
|
value={selectedNode.data.label}
|
||||||
onChange={(e) => updateSelectedData({ label: e.target.value })}
|
onChange={(e) => updateSelectedData({ label: e.target.value })}
|
||||||
@@ -3307,7 +3316,7 @@ function InnerEditor({
|
|||||||
{selectedNode.data.kind === "prompt" || selectedNode.data.kind === "gate" ? (
|
{selectedNode.data.kind === "prompt" || selectedNode.data.kind === "gate" ? (
|
||||||
<div className="wf-prompt-editor">
|
<div className="wf-prompt-editor">
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Prompt</span>
|
<span>{t("workflowEditor.prompt", "Prompt")}</span>
|
||||||
<textarea
|
<textarea
|
||||||
rows={5}
|
rows={5}
|
||||||
value={selectedNodePromptValue}
|
value={selectedNodePromptValue}
|
||||||
@@ -3334,15 +3343,15 @@ function InnerEditor({
|
|||||||
{selectedNode.data.kind === "prompt" ? (
|
{selectedNode.data.kind === "prompt" ? (
|
||||||
<>
|
<>
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Executor</span>
|
<span>{t("workflowEditor.executor", "Executor")}</span>
|
||||||
<select
|
<select
|
||||||
value={currentExecutor}
|
value={currentExecutor}
|
||||||
onChange={(e) => updateSelectedData({ config: { executor: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { executor: e.target.value } })}
|
||||||
>
|
>
|
||||||
<option value="model">Model</option>
|
<option value="model">{t("workflowEditor.model", "Model")}</option>
|
||||||
<option value="agent">Agent</option>
|
<option value="agent">{t("workflowEditor.agent", "Agent")}</option>
|
||||||
<option value="skill">Skill</option>
|
<option value="skill">{t("workflowEditor.skill", "Skill")}</option>
|
||||||
<option value="cli">CLI / script</option>
|
<option value="cli">{t("workflowEditor.cliScript", "CLI / script")}</option>
|
||||||
<option value="cli-agent">{t("workflowEditor.cliAgent.executorOption")}</option>
|
<option value="cli-agent">{t("workflowEditor.cliAgent.executorOption")}</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
@@ -3362,9 +3371,9 @@ function InnerEditor({
|
|||||||
|
|
||||||
{currentExecutor === "model" && (
|
{currentExecutor === "model" && (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Model</span>
|
<span>{t("workflowEditor.model", "Model")}</span>
|
||||||
<CustomModelDropdown
|
<CustomModelDropdown
|
||||||
label="Model"
|
label={t("workflowEditor.model", "Model")}
|
||||||
models={models}
|
models={models}
|
||||||
value={getModelDropdownValue(
|
value={getModelDropdownValue(
|
||||||
String(selectedNode.data.config?.modelProvider ?? ""),
|
String(selectedNode.data.config?.modelProvider ?? ""),
|
||||||
@@ -3386,12 +3395,12 @@ function InnerEditor({
|
|||||||
const nodeAgentStale = nodeAgentId !== "" && !agents.some((a) => a.id === nodeAgentId);
|
const nodeAgentStale = nodeAgentId !== "" && !agents.some((a) => a.id === nodeAgentId);
|
||||||
return (
|
return (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Agent</span>
|
<span>{t("workflowEditor.agent", "Agent")}</span>
|
||||||
<select
|
<select
|
||||||
value={nodeAgentId}
|
value={nodeAgentId}
|
||||||
onChange={(e) => updateSelectedData({ config: { agentId: e.target.value || undefined } })}
|
onChange={(e) => updateSelectedData({ config: { agentId: e.target.value || undefined } })}
|
||||||
>
|
>
|
||||||
<option value="">— select agent —</option>
|
<option value="">{t("workflowEditor.selectAgent", "— select agent —")}</option>
|
||||||
{nodeAgentStale && (
|
{nodeAgentStale && (
|
||||||
<option value={nodeAgentId}>
|
<option value={nodeAgentId}>
|
||||||
{t("workflowColumns.agentNotFound", "Agent not found — {{id}}", { id: nodeAgentId })}
|
{t("workflowColumns.agentNotFound", "Agent not found — {{id}}", { id: nodeAgentId })}
|
||||||
@@ -3412,12 +3421,12 @@ function InnerEditor({
|
|||||||
|
|
||||||
{currentExecutor === "skill" && (
|
{currentExecutor === "skill" && (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Skill</span>
|
<span>{t("workflowEditor.skill", "Skill")}</span>
|
||||||
<select
|
<select
|
||||||
value={String(selectedNode.data.config?.skillName ?? "")}
|
value={String(selectedNode.data.config?.skillName ?? "")}
|
||||||
onChange={(e) => updateSelectedData({ config: { skillName: e.target.value || undefined } })}
|
onChange={(e) => updateSelectedData({ config: { skillName: e.target.value || undefined } })}
|
||||||
>
|
>
|
||||||
<option value="">— select skill —</option>
|
<option value="">{t("workflowEditor.selectSkill", "— select skill —")}</option>
|
||||||
{skills.map((s) => (
|
{skills.map((s) => (
|
||||||
<option key={s.id} value={s.name}>{s.name}</option>
|
<option key={s.id} value={s.name}>{s.name}</option>
|
||||||
))}
|
))}
|
||||||
@@ -3428,26 +3437,26 @@ function InnerEditor({
|
|||||||
{currentExecutor === "cli" && (
|
{currentExecutor === "cli" && (
|
||||||
<>
|
<>
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>CLI mode</span>
|
<span>{t("workflowEditor.cliMode", "CLI mode")}</span>
|
||||||
<select
|
<select
|
||||||
value={String(selectedNode.data.config?.cliMode ?? "command")}
|
value={String(selectedNode.data.config?.cliMode ?? "command")}
|
||||||
onChange={(e) => updateSelectedData({ config: { cliMode: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { cliMode: e.target.value } })}
|
||||||
>
|
>
|
||||||
<option value="command">Command</option>
|
<option value="command">{t("workflowEditor.command", "Command")}</option>
|
||||||
<option value="script">Named script</option>
|
<option value="script">{t("workflowEditor.namedScript", "Named script")}</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
{(selectedNode.data.config?.cliMode ?? "command") === "command" ? (
|
{(selectedNode.data.config?.cliMode ?? "command") === "command" ? (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Command</span>
|
<span>{t("workflowEditor.command", "Command")}</span>
|
||||||
<textarea
|
<textarea
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder="npm test -- --runInBand"
|
placeholder={WORKFLOW_CLI_COMMAND_PLACEHOLDER}
|
||||||
value={String(selectedNode.data.config?.cliCommand ?? "")}
|
value={String(selectedNode.data.config?.cliCommand ?? "")}
|
||||||
onChange={(e) => updateSelectedData({ config: { cliCommand: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { cliCommand: e.target.value } })}
|
||||||
/>
|
/>
|
||||||
<p className="wf-inspector-note wf-inspector-note--info">
|
<p className="wf-inspector-note wf-inspector-note--info">
|
||||||
Runs an arbitrary command in the task worktree. The first time this exact command runs, the task pauses for your approval. The node prompt is passed via FUSION_NODE_PROMPT.
|
{t("workflowEditor.cliCommandNote", "Runs an arbitrary command in the task worktree. The first time this exact command runs, the task pauses for your approval. The node prompt is passed via FUSION_NODE_PROMPT.")}
|
||||||
</p>
|
</p>
|
||||||
<label className="wf-field wf-field--checkbox">
|
<label className="wf-field wf-field--checkbox">
|
||||||
<input
|
<input
|
||||||
@@ -3455,17 +3464,17 @@ function InnerEditor({
|
|||||||
checked={selectedNode.data.config?.cliSkipApproval === true}
|
checked={selectedNode.data.config?.cliSkipApproval === true}
|
||||||
onChange={(e) => updateSelectedData({ config: { cliSkipApproval: e.target.checked } })}
|
onChange={(e) => updateSelectedData({ config: { cliSkipApproval: e.target.checked } })}
|
||||||
/>
|
/>
|
||||||
<span>Skip first-run approval (runs without pausing)</span>
|
<span>{t("workflowEditor.skipFirstRunApproval", "Skip first-run approval (runs without pausing)")}</span>
|
||||||
</label>
|
</label>
|
||||||
</label>
|
</label>
|
||||||
) : (
|
) : (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Script name</span>
|
<span>{t("workflowEditor.scriptName", "Script name")}</span>
|
||||||
<input
|
<input
|
||||||
value={String(selectedNode.data.config?.scriptName ?? "")}
|
value={String(selectedNode.data.config?.scriptName ?? "")}
|
||||||
onChange={(e) => updateSelectedData({ config: { scriptName: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { scriptName: e.target.value } })}
|
||||||
/>
|
/>
|
||||||
<span className="wf-inspector-note">Named script from project settings. The node prompt is passed via FUSION_NODE_PROMPT.</span>
|
<span className="wf-inspector-note">{t("workflowEditor.namedScriptNote", "Named script from project settings. The node prompt is passed via FUSION_NODE_PROMPT.")}</span>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -3554,16 +3563,16 @@ function InnerEditor({
|
|||||||
checked={Boolean(selectedNode.data.config?.autoApprove)}
|
checked={Boolean(selectedNode.data.config?.autoApprove)}
|
||||||
onChange={(e) => updateSelectedData({ config: { autoApprove: e.target.checked } })}
|
onChange={(e) => updateSelectedData({ config: { autoApprove: e.target.checked } })}
|
||||||
/>
|
/>
|
||||||
<span>Auto-approve requests</span>
|
<span>{t("workflowEditor.autoApproveRequests", "Auto-approve requests")}</span>
|
||||||
</label>
|
</label>
|
||||||
{Boolean(selectedNode.data.config?.autoApprove) && (
|
{Boolean(selectedNode.data.config?.autoApprove) && (
|
||||||
<p className="wf-inspector-note">
|
<p className="wf-inspector-note">
|
||||||
Runs without pausing for approval — e.g. a CLI command executes on its first run without waiting for your sign-off.
|
{t("workflowEditor.autoApproveRequestsNote", "Runs without pausing for approval — e.g. a CLI command executes on its first run without waiting for your sign-off.")}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Max retries</span>
|
<span>{t("workflowEditor.maxRetries", "Max retries")}</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min={1}
|
min={1}
|
||||||
@@ -3594,11 +3603,11 @@ function InnerEditor({
|
|||||||
checked={Boolean(selectedNode.data.config?.awaitInput)}
|
checked={Boolean(selectedNode.data.config?.awaitInput)}
|
||||||
onChange={(e) => updateSelectedData({ config: { awaitInput: e.target.checked } })}
|
onChange={(e) => updateSelectedData({ config: { awaitInput: e.target.checked } })}
|
||||||
/>
|
/>
|
||||||
<span>Wait for user input</span>
|
<span>{t("workflowEditor.waitForUserInput", "Wait for user input")}</span>
|
||||||
</label>
|
</label>
|
||||||
{Boolean(selectedNode.data.config?.awaitInput) && (
|
{Boolean(selectedNode.data.config?.awaitInput) && (
|
||||||
<p className="wf-inspector-note wf-inspector-note--info">
|
<p className="wf-inspector-note wf-inspector-note--info">
|
||||||
This node pauses the task until you reply in the task's comments and unpause. The Prompt field above is shown to the user as the question.
|
{t("workflowEditor.waitForUserInputNote", "This node pauses the task until you reply in the task's comments and unpause. The Prompt field above is shown to the user as the question.")}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -3606,7 +3615,7 @@ function InnerEditor({
|
|||||||
|
|
||||||
{selectedNode.data.kind === "script" ? (
|
{selectedNode.data.kind === "script" ? (
|
||||||
<label className="wf-field">
|
<label className="wf-field">
|
||||||
<span>Script name</span>
|
<span>{t("workflowEditor.scriptName", "Script name")}</span>
|
||||||
<input
|
<input
|
||||||
value={String(selectedNode.data.config?.scriptName ?? "")}
|
value={String(selectedNode.data.config?.scriptName ?? "")}
|
||||||
onChange={(e) => updateSelectedData({ config: { scriptName: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { scriptName: e.target.value } })}
|
||||||
@@ -4086,7 +4095,7 @@ function InnerEditor({
|
|||||||
className="wf-code-source"
|
className="wf-code-source"
|
||||||
rows={8}
|
rows={8}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
placeholder={"export default async (ctx) => ({ outcome: \"success\" });"}
|
placeholder={WORKFLOW_CODE_SOURCE_PLACEHOLDER}
|
||||||
value={String(selectedNode.data.config?.source ?? "")}
|
value={String(selectedNode.data.config?.source ?? "")}
|
||||||
onChange={(e) => updateSelectedData({ config: { source: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { source: e.target.value } })}
|
||||||
/>
|
/>
|
||||||
@@ -4176,7 +4185,7 @@ function InnerEditor({
|
|||||||
<textarea
|
<textarea
|
||||||
rows={4}
|
rows={4}
|
||||||
value={String(selectedNode.data.config?.message ?? "")}
|
value={String(selectedNode.data.config?.message ?? "")}
|
||||||
placeholder="Task {{taskId}} reached {{workflowName}}"
|
placeholder={WORKFLOW_NOTIFY_MESSAGE_PLACEHOLDER}
|
||||||
onChange={(e) => updateSelectedData({ config: { message: e.target.value } })}
|
onChange={(e) => updateSelectedData({ config: { message: e.target.value } })}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
@@ -4299,8 +4308,8 @@ function InnerEditor({
|
|||||||
value={String(selectedEdge.data?.condition ?? "success")}
|
value={String(selectedEdge.data?.condition ?? "success")}
|
||||||
onChange={(e) => updateSelectedEdge({ condition: e.target.value })}
|
onChange={(e) => updateSelectedEdge({ condition: e.target.value })}
|
||||||
>
|
>
|
||||||
<option value="success">success</option>
|
<option value="success">{t("workflowNodes.conditionSuccess", "success")}</option>
|
||||||
<option value="failure">failure</option>
|
<option value="failure">{t("workflowNodes.conditionFailure", "failure")}</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import "@xyflow/react/dist/style.css";
|
|||||||
import "./WorkflowResultsTab.css";
|
import "./WorkflowResultsTab.css";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:i18n-Localize 2026-06-20-00:00:
|
||||||
|
FN-6770 requires the workflow/task/setup/PR dashboard cluster to render user-facing copy through locale catalogs so i18n:lint can scan these files again without narrow deferrals.
|
||||||
|
*/
|
||||||
import { Check, ChevronDown, ChevronRight, ChevronUp, Maximize2, Pencil, X } from "lucide-react";
|
import { Check, ChevronDown, ChevronRight, ChevronUp, Maximize2, Pencil, X } from "lucide-react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
@@ -62,10 +67,11 @@ interface WorkflowResultsTabProps {
|
|||||||
|
|
||||||
/** Extract the user-facing question from a workflow-input paused reason.
|
/** Extract the user-facing question from a workflow-input paused reason.
|
||||||
* Strips the leading "workflow-input:<nodeId>: " prefix if present. */
|
* Strips the leading "workflow-input:<nodeId>: " prefix if present. */
|
||||||
function parseWorkflowInputQuestion(pausedReason?: string): string {
|
function parseWorkflowInputQuestion(pausedReason: string | undefined, t: ReturnType<typeof useTranslation>["t"]): string {
|
||||||
if (!pausedReason) return "Reply in the comments and unpause the task to continue.";
|
const fallback = t("app:workflow.replyInComments", "Reply in the comments and unpause the task to continue.");
|
||||||
|
if (!pausedReason) return fallback;
|
||||||
const match = /^workflow-input:[^:]+:\s*(.*)$/s.exec(pausedReason);
|
const match = /^workflow-input:[^:]+:\s*(.*)$/s.exec(pausedReason);
|
||||||
if (match) return match[1].trim() || "Reply in the comments and unpause the task to continue.";
|
if (match) return match[1].trim() || fallback;
|
||||||
return pausedReason;
|
return pausedReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,15 +95,15 @@ interface WorkflowStepOption {
|
|||||||
function getStatusLabel(status: WorkflowStepResult["status"], t: ReturnType<typeof useTranslation>["t"]): string {
|
function getStatusLabel(status: WorkflowStepResult["status"], t: ReturnType<typeof useTranslation>["t"]): string {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "passed":
|
case "passed":
|
||||||
return t("workflow.statusPassed", "Passed");
|
return t("app:workflow.statusPassed", "Passed");
|
||||||
case "failed":
|
case "failed":
|
||||||
return t("workflow.statusFailed", "Failed");
|
return t("app:workflow.statusFailed", "Failed");
|
||||||
case "advisory_failure":
|
case "advisory_failure":
|
||||||
return t("workflow.statusAdvisory", "Advisory failure");
|
return t("app:workflow.statusAdvisory", "Advisory failure");
|
||||||
case "skipped":
|
case "skipped":
|
||||||
return t("workflow.statusSkipped", "Skipped");
|
return t("app:workflow.statusSkipped", "Skipped");
|
||||||
case "pending":
|
case "pending":
|
||||||
return t("workflow.statusRunning", "Running…");
|
return t("app:workflow.statusRunning", "Running…");
|
||||||
default:
|
default:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -135,7 +141,7 @@ function phaseBadge(phase: "pre-merge" | "post-merge", id: string, prefix: strin
|
|||||||
className={`phase-badge ${phaseClass}`}
|
className={`phase-badge ${phaseClass}`}
|
||||||
data-testid={`${prefix}-${id}`}
|
data-testid={`${prefix}-${id}`}
|
||||||
>
|
>
|
||||||
{phase === "post-merge" ? t("workflow.postMerge", "Post-merge") : t("workflow.preMerge", "Pre-merge")}
|
{phase === "post-merge" ? t("app:workflow.postMerge", "Post-merge") : t("app:workflow.preMerge", "Pre-merge")}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -145,9 +151,9 @@ function getWorkflowName(
|
|||||||
workflows: WorkflowDefinition[],
|
workflows: WorkflowDefinition[],
|
||||||
t: ReturnType<typeof useTranslation>["t"],
|
t: ReturnType<typeof useTranslation>["t"],
|
||||||
): string {
|
): string {
|
||||||
if (!selectedWorkflowId) return t("workflow.defaultWorkflow", "Default");
|
if (!selectedWorkflowId) return t("app:workflow.defaultWorkflow", "Default");
|
||||||
const match = workflows.find((workflow) => workflow.id === selectedWorkflowId);
|
const match = workflows.find((workflow) => workflow.id === selectedWorkflowId);
|
||||||
return match?.name || t("workflow.customWorkflowFallback", "Custom workflow");
|
return match?.name || t("app:workflow.customWorkflowFallback", "Custom workflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAggregateWorkflowResult(
|
function getAggregateWorkflowResult(
|
||||||
@@ -155,18 +161,18 @@ function getAggregateWorkflowResult(
|
|||||||
t: ReturnType<typeof useTranslation>["t"],
|
t: ReturnType<typeof useTranslation>["t"],
|
||||||
): { label: string; badgeClass: string; testId: string } {
|
): { label: string; badgeClass: string; testId: string } {
|
||||||
if (results.some((result) => result.status === "failed")) {
|
if (results.some((result) => result.status === "failed")) {
|
||||||
return { label: t("workflow.statusFailed", "Failed"), badgeClass: "workflow-result-badge--failed", testId: "failed" };
|
return { label: t("app:workflow.statusFailed", "Failed"), badgeClass: "workflow-result-badge--failed", testId: "failed" };
|
||||||
}
|
}
|
||||||
if (results.some((result) => result.status === "advisory_failure")) {
|
if (results.some((result) => result.status === "advisory_failure")) {
|
||||||
return { label: t("workflow.statusAdvisory", "Advisory"), badgeClass: "workflow-result-badge--advisory_failure", testId: "advisory" };
|
return { label: t("app:workflow.aggregateAdvisory", "Advisory"), badgeClass: "workflow-result-badge--advisory_failure", testId: "advisory" };
|
||||||
}
|
}
|
||||||
if (results.some((result) => result.status === "pending")) {
|
if (results.some((result) => result.status === "pending")) {
|
||||||
return { label: t("workflow.aggregateInProgress", "In progress"), badgeClass: "workflow-result-badge--pending", testId: "pending" };
|
return { label: t("app:workflow.aggregateInProgress", "In progress"), badgeClass: "workflow-result-badge--pending", testId: "pending" };
|
||||||
}
|
}
|
||||||
if (results.length === 0) {
|
if (results.length === 0) {
|
||||||
return { label: t("workflow.aggregateNoResults", "No results"), badgeClass: "workflow-result-badge--skipped", testId: "no-results" };
|
return { label: t("app:workflow.aggregateNoResults", "No results"), badgeClass: "workflow-result-badge--skipped", testId: "no-results" };
|
||||||
}
|
}
|
||||||
return { label: t("workflow.aggregateAllPassed", "All passed"), badgeClass: "workflow-result-badge--passed", testId: "passed" };
|
return { label: t("app:workflow.aggregateAllPassed", "All passed"), badgeClass: "workflow-result-badge--passed", testId: "passed" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecutionPhase(
|
function getExecutionPhase(
|
||||||
@@ -177,13 +183,13 @@ function getExecutionPhase(
|
|||||||
t: ReturnType<typeof useTranslation>["t"],
|
t: ReturnType<typeof useTranslation>["t"],
|
||||||
): { label: string; badgeClass: string; testId: string } {
|
): { label: string; badgeClass: string; testId: string } {
|
||||||
if (taskStatus === "awaiting-user-input") {
|
if (taskStatus === "awaiting-user-input") {
|
||||||
return { label: t("workflow.executionAwaitingInput", "Awaiting input"), badgeClass: "workflow-result-badge--pending", testId: "awaiting-input" };
|
return { label: t("app:workflow.executionAwaitingInput", "Awaiting input"), badgeClass: "workflow-result-badge--pending", testId: "awaiting-input" };
|
||||||
}
|
}
|
||||||
if (taskStatus === "awaiting-cli-approval") {
|
if (taskStatus === "awaiting-cli-approval") {
|
||||||
return { label: t("workflow.executionAwaitingCliApproval", "Awaiting CLI approval"), badgeClass: "workflow-result-badge--pending", testId: "awaiting-cli-approval" };
|
return { label: t("app:workflow.executionAwaitingCliApproval", "Awaiting CLI approval"), badgeClass: "workflow-result-badge--pending", testId: "awaiting-cli-approval" };
|
||||||
}
|
}
|
||||||
if (taskStatus === "paused" || taskPausedReason) {
|
if (taskStatus === "paused" || taskPausedReason) {
|
||||||
return { label: t("workflow.executionPaused", "Paused"), badgeClass: "workflow-result-badge--pending", testId: "paused" };
|
return { label: t("app:workflow.executionPaused", "Paused"), badgeClass: "workflow-result-badge--pending", testId: "paused" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const pendingResult = results.find((result) => result.status === "pending");
|
const pendingResult = results.find((result) => result.status === "pending");
|
||||||
@@ -191,8 +197,8 @@ function getExecutionPhase(
|
|||||||
const isPostMerge = (pendingResult.phase || "pre-merge") === "post-merge";
|
const isPostMerge = (pendingResult.phase || "pre-merge") === "post-merge";
|
||||||
return {
|
return {
|
||||||
label: isPostMerge
|
label: isPostMerge
|
||||||
? t("workflow.executionPostMerge", "Post-merge steps running")
|
? t("app:workflow.executionPostMerge", "Post-merge steps running")
|
||||||
: t("workflow.executionPreMerge", "Pre-merge steps running"),
|
: t("app:workflow.executionPreMerge", "Pre-merge steps running"),
|
||||||
badgeClass: "workflow-result-badge--pending",
|
badgeClass: "workflow-result-badge--pending",
|
||||||
testId: isPostMerge ? "post-merge" : "pre-merge",
|
testId: isPostMerge ? "post-merge" : "pre-merge",
|
||||||
};
|
};
|
||||||
@@ -200,14 +206,14 @@ function getExecutionPhase(
|
|||||||
|
|
||||||
const hasTerminalResults = results.length > 0 && results.every((result) => ["passed", "failed", "advisory_failure", "skipped"].includes(result.status));
|
const hasTerminalResults = results.length > 0 && results.every((result) => ["passed", "failed", "advisory_failure", "skipped"].includes(result.status));
|
||||||
if (hasTerminalResults || taskStatus === "done" || task?.column === "done" || task?.column === "in-review") {
|
if (hasTerminalResults || taskStatus === "done" || task?.column === "done" || task?.column === "in-review") {
|
||||||
return { label: t("workflow.executionCompleted", "Completed"), badgeClass: "workflow-result-badge--passed", testId: "completed" };
|
return { label: t("app:workflow.executionCompleted", "Completed"), badgeClass: "workflow-result-badge--passed", testId: "completed" };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { label: t("workflow.executionNotStarted", "Not started"), badgeClass: "workflow-result-badge--pending", testId: "not-started" };
|
return { label: t("app:workflow.executionNotStarted", "Not started"), badgeClass: "workflow-result-badge--pending", testId: "not-started" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatModelValue(selection: { provider?: string; modelId?: string } | undefined): string {
|
function formatModelValue(selection: { provider?: string; modelId?: string } | undefined, t: ReturnType<typeof useTranslation>["t"]): string {
|
||||||
if (!selection?.provider || !selection.modelId) return "Default";
|
if (!selection?.provider || !selection.modelId) return t("app:workflow.modelDefault", "Default");
|
||||||
return `${selection.provider}/${selection.modelId}`;
|
return `${selection.provider}/${selection.modelId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +251,7 @@ function LiveAgentLogOutput({
|
|||||||
if (stepEntries.length === 0) {
|
if (stepEntries.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className="workflow-live-log" data-testid={`workflow-live-log-${stepId}`}>
|
<div className="workflow-live-log" data-testid={`workflow-live-log-${stepId}`}>
|
||||||
<div className="workflow-live-log-empty">{t("workflow.waitingForOutput", "Waiting for agent output…")}</div>
|
<div className="workflow-live-log-empty">{t("app:workflow.waitingForOutput", "Waiting for agent output…")}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -570,7 +576,7 @@ export function WorkflowResultsTab({
|
|||||||
return {
|
return {
|
||||||
id: stepId,
|
id: stepId,
|
||||||
name: stepInfo?.name || stepId,
|
name: stepInfo?.name || stepId,
|
||||||
description: stepInfo?.description || t("workflow.stepDefinitionNotFound", "Step definition not found."),
|
description: stepInfo?.description || t("app:workflow.stepDefinitionNotFound", "Step definition not found."),
|
||||||
phase: stepInfo?.phase || "pre-merge",
|
phase: stepInfo?.phase || "pre-merge",
|
||||||
} as WorkflowStepOption;
|
} as WorkflowStepOption;
|
||||||
});
|
});
|
||||||
@@ -595,7 +601,7 @@ export function WorkflowResultsTab({
|
|||||||
<div className="workflow-results-editor" data-testid="workflow-steps-editor">
|
<div className="workflow-results-editor" data-testid="workflow-steps-editor">
|
||||||
<div className="workflow-steps-section">
|
<div className="workflow-steps-section">
|
||||||
<small className="workflow-steps-description">
|
<small className="workflow-steps-description">
|
||||||
{t("workflow.selectStepsDescription", "Select steps to run after task implementation completes")}
|
{t("app:workflow.selectStepsDescription", "Select steps to run after task implementation completes")}
|
||||||
</small>
|
</small>
|
||||||
<div className="workflow-steps-list">
|
<div className="workflow-steps-list">
|
||||||
{workflowStepOptions.map((step) => (
|
{workflowStepOptions.map((step) => (
|
||||||
@@ -625,7 +631,7 @@ export function WorkflowResultsTab({
|
|||||||
|
|
||||||
{selectedWorkflowSteps.length > 1 && (
|
{selectedWorkflowSteps.length > 1 && (
|
||||||
<div className="workflow-step-order" data-testid="workflow-step-order">
|
<div className="workflow-step-order" data-testid="workflow-step-order">
|
||||||
<small className="workflow-step-order-label">{t("workflow.executionOrder", "Execution order:")}</small>
|
<small className="workflow-step-order-label">{t("app:workflow.executionOrder", "Execution order:")}</small>
|
||||||
{selectedWorkflowSteps.map((stepId, index) => {
|
{selectedWorkflowSteps.map((stepId, index) => {
|
||||||
const stepInfo = workflowStepLookup.get(stepId);
|
const stepInfo = workflowStepLookup.get(stepId);
|
||||||
return (
|
return (
|
||||||
@@ -639,7 +645,7 @@ export function WorkflowResultsTab({
|
|||||||
onClick={() => moveWorkflowStepUp(index)}
|
onClick={() => moveWorkflowStepUp(index)}
|
||||||
disabled={index === 0}
|
disabled={index === 0}
|
||||||
data-testid={`workflow-step-move-up-${stepId}`}
|
data-testid={`workflow-step-move-up-${stepId}`}
|
||||||
title={t("workflow.moveUp", "Move up")}
|
title={t("app:workflow.moveUp", "Move up")}
|
||||||
>
|
>
|
||||||
<ChevronUp />
|
<ChevronUp />
|
||||||
</button>
|
</button>
|
||||||
@@ -649,7 +655,7 @@ export function WorkflowResultsTab({
|
|||||||
onClick={() => moveWorkflowStepDown(index)}
|
onClick={() => moveWorkflowStepDown(index)}
|
||||||
disabled={index === selectedWorkflowSteps.length - 1}
|
disabled={index === selectedWorkflowSteps.length - 1}
|
||||||
data-testid={`workflow-step-move-down-${stepId}`}
|
data-testid={`workflow-step-move-down-${stepId}`}
|
||||||
title={t("workflow.moveDown", "Move down")}
|
title={t("app:workflow.moveDown", "Move down")}
|
||||||
>
|
>
|
||||||
<ChevronDown />
|
<ChevronDown />
|
||||||
</button>
|
</button>
|
||||||
@@ -658,7 +664,7 @@ export function WorkflowResultsTab({
|
|||||||
className="btn btn-icon btn-sm"
|
className="btn btn-icon btn-sm"
|
||||||
onClick={() => removeWorkflowStep(stepId)}
|
onClick={() => removeWorkflowStep(stepId)}
|
||||||
data-testid={`workflow-step-remove-${stepId}`}
|
data-testid={`workflow-step-remove-${stepId}`}
|
||||||
title={t("workflow.remove", "Remove")}
|
title={t("app:workflow.remove", "Remove")}
|
||||||
>
|
>
|
||||||
<X />
|
<X />
|
||||||
</button>
|
</button>
|
||||||
@@ -677,7 +683,7 @@ export function WorkflowResultsTab({
|
|||||||
return (
|
return (
|
||||||
<div className="workflow-results-loading" data-testid="workflow-results-loading">
|
<div className="workflow-results-loading" data-testid="workflow-results-loading">
|
||||||
<div className="workflow-results-spinner" />
|
<div className="workflow-results-spinner" />
|
||||||
<span>{t("workflow.loadingResults", "Loading workflow results…")}</span>
|
<span>{t("app:workflow.loadingResults", "Loading workflow results…")}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -685,9 +691,9 @@ export function WorkflowResultsTab({
|
|||||||
if (!hasResults) {
|
if (!hasResults) {
|
||||||
return (
|
return (
|
||||||
<div className="workflow-results-empty" data-testid="workflow-results-empty">
|
<div className="workflow-results-empty" data-testid="workflow-results-empty">
|
||||||
<p>{t("workflow.noStepsConfigured", "No workflow steps configured for this task.")}</p>
|
<p>{t("app:workflow.noStepsConfigured", "No workflow steps configured for this task.")}</p>
|
||||||
<p className="workflow-results-empty-hint">
|
<p className="workflow-results-empty-hint">
|
||||||
{t("workflow.stepsExplanation", "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.")}
|
{t("app:workflow.stepsExplanation", "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -699,26 +705,26 @@ export function WorkflowResultsTab({
|
|||||||
const skipped = results.filter((r) => r.status === "skipped").length;
|
const skipped = results.filter((r) => r.status === "skipped").length;
|
||||||
const pending = results.filter((r) => r.status === "pending").length;
|
const pending = results.filter((r) => r.status === "pending").length;
|
||||||
|
|
||||||
const summaryParts: string[] = [t("workflow.summaryStepCount", { count: results.length, defaultValue_one: "{{count}} step", defaultValue_other: "{{count}} steps" })];
|
const summaryParts: string[] = [t("app:workflow.summaryStepCount", { count: results.length, defaultValue_one: "{{count}} step", defaultValue_other: "{{count}} steps" })];
|
||||||
if (passed > 0) summaryParts.push(t("workflow.summaryPassed", "{{count}} passed", { count: passed }));
|
if (passed > 0) summaryParts.push(t("app:workflow.summaryPassed", "{{count}} passed", { count: passed }));
|
||||||
if (failed > 0) summaryParts.push(t("workflow.summaryFailed", "{{count}} failed", { count: failed }));
|
if (failed > 0) summaryParts.push(t("app:workflow.summaryFailed", "{{count}} failed", { count: failed }));
|
||||||
if (advisoryFailures.length > 0) summaryParts.push(t("workflow.summaryAdvisory", "{{count}} advisory", { count: advisoryFailures.length }));
|
if (advisoryFailures.length > 0) summaryParts.push(t("app:workflow.summaryAdvisory", "{{count}} advisory", { count: advisoryFailures.length }));
|
||||||
if (skipped > 0) summaryParts.push(t("workflow.summarySkipped", "{{count}} skipped", { count: skipped }));
|
if (skipped > 0) summaryParts.push(t("app:workflow.summarySkipped", "{{count}} skipped", { count: skipped }));
|
||||||
if (pending > 0) summaryParts.push(t("workflow.summaryRunning", "{{count}} running", { count: pending }));
|
if (pending > 0) summaryParts.push(t("app:workflow.summaryRunning", "{{count}} running", { count: pending }));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="workflow-results-list" data-testid="workflow-results-list">
|
<div className="workflow-results-list" data-testid="workflow-results-list">
|
||||||
<div className="workflow-results-summary-bar" data-testid="workflow-results-summary">
|
<div className="workflow-results-summary-bar" data-testid="workflow-results-summary">
|
||||||
{summaryParts.join(t("workflow.summarySeparator", " · "))}
|
{summaryParts.join(t("app:workflow.summarySeparator", " · "))}
|
||||||
</div>
|
</div>
|
||||||
{advisoryFailures.length > 0 && (
|
{advisoryFailures.length > 0 && (
|
||||||
<div className="workflow-polish-notes" data-testid="workflow-polish-notes">
|
<div className="workflow-polish-notes" data-testid="workflow-polish-notes">
|
||||||
<h4>{t("workflow.polishNotes", "Polish notes")}</h4>
|
<h4>{t("app:workflow.polishNotes", "Polish notes")}</h4>
|
||||||
<p>{t("workflow.advisoryExplanation", "Advisory workflow steps flagged non-blocking improvements:")}</p>
|
<p>{t("app:workflow.advisoryExplanation", "Advisory workflow steps flagged non-blocking improvements:")}</p>
|
||||||
<ul>
|
<ul>
|
||||||
{advisoryFailures.map((result, index) => (
|
{advisoryFailures.map((result, index) => (
|
||||||
<li key={`advisory-${result.workflowStepId}-${index}`}>
|
<li key={`advisory-${result.workflowStepId}-${index}`}>
|
||||||
<strong>{result.workflowStepName}:</strong> {result.output || t("workflow.needsReview", "Needs follow-up review.")}
|
<strong>{result.workflowStepName}:</strong> {result.output || t("app:workflow.needsReview", "Needs follow-up review.")}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -758,7 +764,7 @@ export function WorkflowResultsTab({
|
|||||||
|
|
||||||
{result.notes && result.status !== "pending" && (
|
{result.notes && result.status !== "pending" && (
|
||||||
<div className="workflow-result-notes" data-testid={`workflow-result-notes-${result.workflowStepId}`}>
|
<div className="workflow-result-notes" data-testid={`workflow-result-notes-${result.workflowStepId}`}>
|
||||||
<span className="workflow-result-notes-label">{t("workflow.notes", "Notes:")} </span>
|
<span className="workflow-result-notes-label">{t("app:workflow.notes", "Notes:")} </span>
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||||
{result.notes}
|
{result.notes}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
@@ -767,7 +773,7 @@ export function WorkflowResultsTab({
|
|||||||
|
|
||||||
<div className="workflow-result-meta">
|
<div className="workflow-result-meta">
|
||||||
{result.startedAt && (
|
{result.startedAt && (
|
||||||
<span className="workflow-result-timestamp">{t("workflow.started", "Started:")} {formatTimestamp(result.startedAt)}</span>
|
<span className="workflow-result-timestamp">{t("app:workflow.started", "Started:")} {formatTimestamp(result.startedAt)}</span>
|
||||||
)}
|
)}
|
||||||
{result.completedAt && (
|
{result.completedAt && (
|
||||||
<span className="workflow-result-duration">{formatDuration(result.startedAt, result.completedAt)}</span>
|
<span className="workflow-result-duration">{formatDuration(result.startedAt, result.completedAt)}</span>
|
||||||
@@ -785,14 +791,14 @@ export function WorkflowResultsTab({
|
|||||||
) : result.output ? (
|
) : result.output ? (
|
||||||
<div className="workflow-result-output-section">
|
<div className="workflow-result-output-section">
|
||||||
<div className="workflow-result-output-header">
|
<div className="workflow-result-output-header">
|
||||||
<span className="workflow-result-output-label">{t("workflow.output", "Output:")} </span>
|
<span className="workflow-result-output-label">{t("app:workflow.output", "Output:")} </span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-sm workflow-result-toggle"
|
className="btn btn-sm workflow-result-toggle"
|
||||||
onClick={() => toggleOutput(result.workflowStepId)}
|
onClick={() => toggleOutput(result.workflowStepId)}
|
||||||
data-testid={`workflow-result-toggle-${result.workflowStepId}`}
|
data-testid={`workflow-result-toggle-${result.workflowStepId}`}
|
||||||
>
|
>
|
||||||
{isExpanded ? t("workflow.hideOutput", "Hide output") : t("workflow.showOutput", "Show output")}
|
{isExpanded ? t("app:workflow.hideOutput", "Hide output") : t("app:workflow.showOutput", "Show output")}
|
||||||
</button>
|
</button>
|
||||||
{!isExpanded && (
|
{!isExpanded && (
|
||||||
<span
|
<span
|
||||||
@@ -809,16 +815,16 @@ export function WorkflowResultsTab({
|
|||||||
className="btn btn-sm workflow-result-mode-toggle"
|
className="btn btn-sm workflow-result-mode-toggle"
|
||||||
onClick={() => toggleRenderMode(result.workflowStepId)}
|
onClick={() => toggleRenderMode(result.workflowStepId)}
|
||||||
data-testid={`workflow-result-mode-toggle-${result.workflowStepId}`}
|
data-testid={`workflow-result-mode-toggle-${result.workflowStepId}`}
|
||||||
title={(renderModes[result.workflowStepId] ?? "markdown") === "markdown" ? t("workflow.switchToPlain", "Switch to plain text") : t("workflow.switchToMarkdown", "Switch to markdown")}
|
title={(renderModes[result.workflowStepId] ?? "markdown") === "markdown" ? t("app:workflow.switchToPlain", "Switch to plain text") : t("app:workflow.switchToMarkdown", "Switch to markdown")}
|
||||||
>
|
>
|
||||||
{(renderModes[result.workflowStepId] ?? "markdown") === "markdown" ? t("workflow.markdown", "Markdown") : t("workflow.plain", "Plain")}
|
{(renderModes[result.workflowStepId] ?? "markdown") === "markdown" ? t("app:workflow.markdown", "Markdown") : t("app:workflow.plain", "Plain")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-icon btn-sm workflow-result-expand-toggle"
|
className="btn btn-icon btn-sm workflow-result-expand-toggle"
|
||||||
onClick={() => openExpandedView(result.workflowStepId)}
|
onClick={() => openExpandedView(result.workflowStepId)}
|
||||||
data-testid={`workflow-result-expand-${result.workflowStepId}`}
|
data-testid={`workflow-result-expand-${result.workflowStepId}`}
|
||||||
title={t("workflow.expandOutput", "Expand output")}
|
title={t("app:workflow.expandOutput", "Expand output")}
|
||||||
>
|
>
|
||||||
<Maximize2 size={12} />
|
<Maximize2 size={12} />
|
||||||
</button>
|
</button>
|
||||||
@@ -858,18 +864,18 @@ export function WorkflowResultsTab({
|
|||||||
className="btn btn-sm workflow-results-edit-toggle"
|
className="btn btn-sm workflow-results-edit-toggle"
|
||||||
onClick={() => setIsEditing((prev) => !prev)}
|
onClick={() => setIsEditing((prev) => !prev)}
|
||||||
data-testid="workflow-steps-edit-toggle"
|
data-testid="workflow-steps-edit-toggle"
|
||||||
aria-label={isEditing ? t("workflow.doneEditingAriaLabel", "Done editing workflow steps") : t("workflow.editAriaLabel", "Edit workflow steps")}
|
aria-label={isEditing ? t("app:workflow.doneEditingAriaLabel", "Done editing workflow steps") : t("app:workflow.editAriaLabel", "Edit workflow steps")}
|
||||||
title={isEditing ? t("workflow.done", "Done") : t("workflow.edit", "Edit")}
|
title={isEditing ? t("app:workflow.done", "Done") : t("app:workflow.edit", "Edit")}
|
||||||
>
|
>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<>
|
<>
|
||||||
<Check size={14} />
|
<Check size={14} />
|
||||||
{t("workflow.done", "Done")}
|
{t("app:workflow.done", "Done")}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Pencil size={14} />
|
<Pencil size={14} />
|
||||||
{t("workflow.edit", "Edit")}
|
{t("app:workflow.edit", "Edit")}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
@@ -891,7 +897,7 @@ export function WorkflowResultsTab({
|
|||||||
setInputText("");
|
setInputText("");
|
||||||
setSubmitted(true);
|
setSubmitted(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setResumeError(getErrorMessage(err) || "Failed to resume task");
|
setResumeError(getErrorMessage(err) || t("app:workflow.resumeTaskError", "Failed to resume task"));
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -905,7 +911,7 @@ export function WorkflowResultsTab({
|
|||||||
await approveTaskWorkflowCli(taskId, projectId);
|
await approveTaskWorkflowCli(taskId, projectId);
|
||||||
setSubmitted(true);
|
setSubmitted(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setResumeError(getErrorMessage(err) || "Failed to approve command");
|
setResumeError(getErrorMessage(err) || t("app:workflow.approveCommandError", "Failed to approve command"));
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -915,16 +921,16 @@ export function WorkflowResultsTab({
|
|||||||
<div className="workflow-results-tab" data-task-id={taskId}>
|
<div className="workflow-results-tab" data-task-id={taskId}>
|
||||||
{isAwaitingInput && (
|
{isAwaitingInput && (
|
||||||
<div className="workflow-input-banner" role="alert">
|
<div className="workflow-input-banner" role="alert">
|
||||||
<strong>Waiting for your input</strong>
|
<strong>{t("app:workflow.awaitingInputTitle", "Waiting for your input")}</strong>
|
||||||
<span>{parseWorkflowInputQuestion(taskPausedReason)}</span>
|
<span>{parseWorkflowInputQuestion(taskPausedReason, t)}</span>
|
||||||
{submitted ? (
|
{submitted ? (
|
||||||
<span className="workflow-input-resuming">Resuming…</span>
|
<span className="workflow-input-resuming">{t("app:workflow.resuming", "Resuming…")}</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="workflow-input-actions">
|
<div className="workflow-input-actions">
|
||||||
<textarea
|
<textarea
|
||||||
className="workflow-input-textarea"
|
className="workflow-input-textarea"
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder="Type your reply…"
|
placeholder={t("app:workflow.inputPlaceholder", "Type your reply…")}
|
||||||
value={inputText}
|
value={inputText}
|
||||||
onChange={(e) => setInputText(e.target.value)}
|
onChange={(e) => setInputText(e.target.value)}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
@@ -935,7 +941,7 @@ export function WorkflowResultsTab({
|
|||||||
onClick={handleSubmitInput}
|
onClick={handleSubmitInput}
|
||||||
disabled={submitting || !inputText.trim()}
|
disabled={submitting || !inputText.trim()}
|
||||||
>
|
>
|
||||||
{submitting ? "Submitting…" : "Submit & resume"}
|
{submitting ? t("app:workflow.submitting", "Submitting…") : t("app:workflow.submitAndResume", "Submit & resume")}
|
||||||
</button>
|
</button>
|
||||||
{resumeError && (
|
{resumeError && (
|
||||||
<span className="workflow-input-error" role="alert">{resumeError}</span>
|
<span className="workflow-input-error" role="alert">{resumeError}</span>
|
||||||
@@ -946,13 +952,13 @@ export function WorkflowResultsTab({
|
|||||||
)}
|
)}
|
||||||
{isAwaitingCliApproval && (
|
{isAwaitingCliApproval && (
|
||||||
<div className="workflow-input-banner workflow-input-banner--approval" role="alert">
|
<div className="workflow-input-banner workflow-input-banner--approval" role="alert">
|
||||||
<strong>Approve CLI command?</strong>
|
<strong>{t("app:workflow.cliApprovalTitle", "Approve CLI command?")}</strong>
|
||||||
<span className="workflow-input-approval-warning">
|
<span className="workflow-input-approval-warning">
|
||||||
This command will run in the task worktree. Approving trusts this exact command for future runs.
|
{t("app:workflow.cliApprovalWarning", "This command will run in the task worktree. Approving trusts this exact command for future runs.")}
|
||||||
</span>
|
</span>
|
||||||
<pre className="workflow-input-approval-command"><code>{parseCliApprovalCommand(taskPausedReason)}</code></pre>
|
<pre className="workflow-input-approval-command"><code>{parseCliApprovalCommand(taskPausedReason)}</code></pre>
|
||||||
{submitted ? (
|
{submitted ? (
|
||||||
<span className="workflow-input-resuming">Resuming…</span>
|
<span className="workflow-input-resuming">{t("app:workflow.resuming", "Resuming…")}</span>
|
||||||
) : (
|
) : (
|
||||||
<div className="workflow-input-actions">
|
<div className="workflow-input-actions">
|
||||||
<button
|
<button
|
||||||
@@ -961,9 +967,9 @@ export function WorkflowResultsTab({
|
|||||||
onClick={handleApproveCli}
|
onClick={handleApproveCli}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
>
|
>
|
||||||
{submitting ? "Approving…" : "Approve & run"}
|
{submitting ? t("app:workflow.approving", "Approving…") : t("app:workflow.approveAndRun", "Approve & run")}
|
||||||
</button>
|
</button>
|
||||||
<span className="workflow-input-keep-paused">To reject, keep the task paused and do not approve.</span>
|
<span className="workflow-input-keep-paused">{t("app:workflow.cliApprovalRejectHint", "To reject, keep the task paused and do not approve.")}</span>
|
||||||
{resumeError && (
|
{resumeError && (
|
||||||
<span className="workflow-input-error" role="alert">{resumeError}</span>
|
<span className="workflow-input-error" role="alert">{resumeError}</span>
|
||||||
)}
|
)}
|
||||||
@@ -973,28 +979,28 @@ export function WorkflowResultsTab({
|
|||||||
)}
|
)}
|
||||||
<section className="card workflow-state-summary" data-testid="workflow-state-summary">
|
<section className="card workflow-state-summary" data-testid="workflow-state-summary">
|
||||||
<div className="workflow-state-summary__header">
|
<div className="workflow-state-summary__header">
|
||||||
<h4>{t("workflow.overview", "Workflow overview")}</h4>
|
<h4>{t("app:workflow.overview", "Workflow overview")}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div className="workflow-state-summary__grid">
|
<div className="workflow-state-summary__grid">
|
||||||
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-name">
|
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-name">
|
||||||
<span className="workflow-state-summary__label">{t("workflow.workflowName", "Workflow")}</span>
|
<span className="workflow-state-summary__label">{t("app:workflow.workflowName", "Workflow")}</span>
|
||||||
<span className="workflow-state-summary__value">{workflowName}</span>
|
<span className="workflow-state-summary__value">{workflowName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-phase">
|
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-phase">
|
||||||
<span className="workflow-state-summary__label">{t("workflow.executionPhase", "Execution phase")}</span>
|
<span className="workflow-state-summary__label">{t("app:workflow.executionPhase", "Execution phase")}</span>
|
||||||
<span className={`workflow-result-badge ${executionPhase.badgeClass}`} data-testid={`workflow-phase-badge-${executionPhase.testId}`}>
|
<span className={`workflow-result-badge ${executionPhase.badgeClass}`} data-testid={`workflow-phase-badge-${executionPhase.testId}`}>
|
||||||
{executionPhase.label}
|
{executionPhase.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-aggregate">
|
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-aggregate">
|
||||||
<span className="workflow-state-summary__label">{t("workflow.aggregateResult", "Aggregate result")}</span>
|
<span className="workflow-state-summary__label">{t("app:workflow.aggregateResult", "Aggregate result")}</span>
|
||||||
<span className={`workflow-result-badge ${aggregateResult.badgeClass}`} data-testid={`workflow-aggregate-badge-${aggregateResult.testId}`}>
|
<span className={`workflow-result-badge ${aggregateResult.badgeClass}`} data-testid={`workflow-aggregate-badge-${aggregateResult.testId}`}>
|
||||||
{aggregateResult.label}
|
{aggregateResult.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-count">
|
<div className="workflow-state-summary__item" data-testid="workflow-state-summary-count">
|
||||||
<span className="workflow-state-summary__label">{t("workflow.stepProgress", "Step count")}</span>
|
<span className="workflow-state-summary__label">{t("app:workflow.stepProgress", "Step count")}</span>
|
||||||
<span className="workflow-state-summary__value">{t("workflow.stepProgressValue", "{{completed}} of {{total}} steps completed", { completed: completedStepCount, total: results.length })}</span>
|
<span className="workflow-state-summary__value">{t("app:workflow.stepProgressValue", "{{completed}} of {{total}} steps completed", { completed: completedStepCount, total: results.length })}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -1008,19 +1014,19 @@ export function WorkflowResultsTab({
|
|||||||
>
|
>
|
||||||
<span className="workflow-disclosure__title">
|
<span className="workflow-disclosure__title">
|
||||||
{graphExpanded ? <ChevronDown aria-hidden /> : <ChevronRight aria-hidden />}
|
{graphExpanded ? <ChevronDown aria-hidden /> : <ChevronRight aria-hidden />}
|
||||||
{t("workflow.graph", "Workflow graph")}
|
{t("app:workflow.graph", "Workflow graph")}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{graphExpanded && (
|
{graphExpanded && (
|
||||||
<div className="workflow-disclosure__content">
|
<div className="workflow-disclosure__content">
|
||||||
{!selectedWorkflowId ? (
|
{!selectedWorkflowId ? (
|
||||||
<p className="workflow-disclosure__empty" data-testid="workflow-graph-empty">
|
<p className="workflow-disclosure__empty" data-testid="workflow-graph-empty">
|
||||||
{t("workflow.noWorkflowAssigned", "No workflow assigned")}
|
{t("app:workflow.noWorkflowAssigned", "No workflow assigned")}
|
||||||
</p>
|
</p>
|
||||||
) : workflowGraphLoading && !graphWorkflow ? (
|
) : workflowGraphLoading && !graphWorkflow ? (
|
||||||
<div className="workflow-results-loading" data-testid="workflow-graph-loading">
|
<div className="workflow-results-loading" data-testid="workflow-graph-loading">
|
||||||
<div className="workflow-results-spinner" />
|
<div className="workflow-results-spinner" />
|
||||||
<span>{t("workflow.loadingGraph", "Loading workflow graph…")}</span>
|
<span>{t("app:workflow.loadingGraph", "Loading workflow graph…")}</span>
|
||||||
</div>
|
</div>
|
||||||
) : graphFlow ? (
|
) : graphFlow ? (
|
||||||
<div className="workflow-graph-preview" data-testid="workflow-graph-preview">
|
<div className="workflow-graph-preview" data-testid="workflow-graph-preview">
|
||||||
@@ -1042,7 +1048,7 @@ export function WorkflowResultsTab({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p className="workflow-disclosure__empty" data-testid="workflow-graph-unavailable">
|
<p className="workflow-disclosure__empty" data-testid="workflow-graph-unavailable">
|
||||||
{t("workflow.graphUnavailable", "Workflow graph unavailable")}
|
{t("app:workflow.graphUnavailable", "Workflow graph unavailable")}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1051,7 +1057,7 @@ export function WorkflowResultsTab({
|
|||||||
|
|
||||||
<section className="card workflow-management" data-testid="workflow-management-section">
|
<section className="card workflow-management" data-testid="workflow-management-section">
|
||||||
<div className="workflow-management__header">
|
<div className="workflow-management__header">
|
||||||
<h4>{t("workflow.workflowName", "Workflow")}</h4>
|
<h4>{t("app:workflow.workflowName", "Workflow")}</h4>
|
||||||
{canEdit && selectedWorkflowId && onEditWorkflow && (
|
{canEdit && selectedWorkflowId && onEditWorkflow && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1060,7 +1066,7 @@ export function WorkflowResultsTab({
|
|||||||
data-testid="workflow-edit-button"
|
data-testid="workflow-edit-button"
|
||||||
>
|
>
|
||||||
<Pencil aria-hidden />
|
<Pencil aria-hidden />
|
||||||
{t("workflow.editWorkflow", "Edit workflow")}
|
{t("app:workflow.editWorkflow", "Edit workflow")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1068,7 +1074,7 @@ export function WorkflowResultsTab({
|
|||||||
value={selectedWorkflowId}
|
value={selectedWorkflowId}
|
||||||
onChange={handleWorkflowSelect}
|
onChange={handleWorkflowSelect}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
label="Custom workflow"
|
label={t("app:workflow.customWorkflowLabel", "Custom workflow")}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
@@ -1082,16 +1088,16 @@ export function WorkflowResultsTab({
|
|||||||
>
|
>
|
||||||
<span className="workflow-disclosure__title">
|
<span className="workflow-disclosure__title">
|
||||||
{modelSettingsExpanded ? <ChevronDown aria-hidden /> : <ChevronRight aria-hidden />}
|
{modelSettingsExpanded ? <ChevronDown aria-hidden /> : <ChevronRight aria-hidden />}
|
||||||
{t("workflow.modelSettings", "Model settings")}
|
{t("app:workflow.modelSettings", "Model settings")}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{modelSettingsExpanded && (
|
{modelSettingsExpanded && (
|
||||||
<div className="workflow-disclosure__content workflow-state-summary__grid" data-testid="workflow-model-settings-content">
|
<div className="workflow-disclosure__content workflow-state-summary__grid" data-testid="workflow-model-settings-content">
|
||||||
{[
|
{[
|
||||||
{ key: "executor", label: t("models.targetLabels.executor", "Executor"), value: formatModelValue(effectiveExecutor), provider: effectiveExecutor?.provider },
|
{ key: "executor", label: t("models.targetLabels.executor", "Executor"), value: formatModelValue(effectiveExecutor, t), provider: effectiveExecutor?.provider },
|
||||||
{ key: "reviewer", label: t("models.targetLabels.validator", "Reviewer"), value: formatModelValue(effectiveValidator), provider: effectiveValidator?.provider },
|
{ key: "reviewer", label: t("models.targetLabels.validator", "Reviewer"), value: formatModelValue(effectiveValidator, t), provider: effectiveValidator?.provider },
|
||||||
{ key: "planning", label: t("models.targetLabels.planning", "Planning"), value: formatModelValue(effectivePlanning), provider: effectivePlanning?.provider },
|
{ key: "planning", label: t("models.targetLabels.planning", "Planning"), value: formatModelValue(effectivePlanning, t), provider: effectivePlanning?.provider },
|
||||||
{ key: "thinking", label: t("workflow.thinkingLevel", "Thinking level"), value: task?.thinkingLevel || "Default" },
|
{ key: "thinking", label: t("app:workflow.thinkingLevel", "Thinking level"), value: task?.thinkingLevel || "Default" },
|
||||||
].map((item) => (
|
].map((item) => (
|
||||||
<div className="workflow-state-summary__item" key={item.key} data-testid={`workflow-model-setting-${item.key}`}>
|
<div className="workflow-state-summary__item" key={item.key} data-testid={`workflow-model-setting-${item.key}`}>
|
||||||
<span className="workflow-state-summary__label">{item.label}</span>
|
<span className="workflow-state-summary__label">{item.label}</span>
|
||||||
@@ -1108,9 +1114,9 @@ export function WorkflowResultsTab({
|
|||||||
<div className="workflow-configured-steps" data-testid="workflow-configured-steps">
|
<div className="workflow-configured-steps" data-testid="workflow-configured-steps">
|
||||||
<div className="workflow-configured-header" data-testid="workflow-configured-header">
|
<div className="workflow-configured-header" data-testid="workflow-configured-header">
|
||||||
<div className="workflow-configured-title-row">
|
<div className="workflow-configured-title-row">
|
||||||
<h4>{t("workflow.configuredSteps", "Configured Workflow Steps")}</h4>
|
<h4>{t("app:workflow.configuredSteps", "Configured Workflow Steps")}</h4>
|
||||||
<span className="workflow-configured-count" data-testid="workflow-configured-count">
|
<span className="workflow-configured-count" data-testid="workflow-configured-count">
|
||||||
{t("workflow.stepCount", { count: configuredSteps.length, defaultValue_one: "{{count}} step", defaultValue_other: "{{count}} steps" })}
|
{t("app:workflow.stepCount", { count: configuredSteps.length, defaultValue_one: "{{count}} step", defaultValue_other: "{{count}} steps" })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{editButton}
|
{editButton}
|
||||||
@@ -1133,7 +1139,7 @@ export function WorkflowResultsTab({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="workflow-results-empty-hint">
|
<p className="workflow-results-empty-hint">
|
||||||
{t("workflow.stepsExplanation", "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.")}
|
{t("app:workflow.stepsExplanation", "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.")}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{renderEditor()}
|
{renderEditor()}
|
||||||
@@ -1142,7 +1148,7 @@ export function WorkflowResultsTab({
|
|||||||
<>
|
<>
|
||||||
{showEditHeaderForResults && (
|
{showEditHeaderForResults && (
|
||||||
<div className="workflow-results-edit-header" data-testid="workflow-results-edit-header">
|
<div className="workflow-results-edit-header" data-testid="workflow-results-edit-header">
|
||||||
<h4>{t("workflow.steps", "Workflow Steps")}</h4>
|
<h4>{t("app:workflow.steps", "Workflow Steps")}</h4>
|
||||||
{editButton}
|
{editButton}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1179,9 +1185,9 @@ export function WorkflowResultsTab({
|
|||||||
className="btn btn-sm workflow-result-mode-toggle"
|
className="btn btn-sm workflow-result-mode-toggle"
|
||||||
onClick={() => toggleRenderMode(result.workflowStepId)}
|
onClick={() => toggleRenderMode(result.workflowStepId)}
|
||||||
data-testid="workflow-output-modal-mode-toggle"
|
data-testid="workflow-output-modal-mode-toggle"
|
||||||
title={renderMode === "markdown" ? t("workflow.switchToPlain", "Switch to plain text") : t("workflow.switchToMarkdown", "Switch to markdown")}
|
title={renderMode === "markdown" ? t("app:workflow.switchToPlain", "Switch to plain text") : t("app:workflow.switchToMarkdown", "Switch to markdown")}
|
||||||
>
|
>
|
||||||
{renderMode === "markdown" ? t("workflow.markdown", "Markdown") : t("workflow.plain", "Plain")}
|
{renderMode === "markdown" ? t("app:workflow.markdown", "Markdown") : t("app:workflow.plain", "Plain")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import { fetchWorkflow, fetchWorkflows, fetchProjectDefaultWorkflow, setProjectD
|
|||||||
import type { ToastType } from "../hooks/useToast";
|
import type { ToastType } from "../hooks/useToast";
|
||||||
import { useConfirm } from "../hooks/useConfirm";
|
import { useConfirm } from "../hooks/useConfirm";
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:i18n-Localize 2026-06-20-00:00:
|
||||||
|
FN-6770 localizes this workflow surface through t() and authored en catalog keys so hardcoded user-facing copy does not need a lint.ignore deferral.
|
||||||
|
*/
|
||||||
|
|
||||||
interface WorkflowSelectorProps {
|
interface WorkflowSelectorProps {
|
||||||
/** Currently selected workflow id, or null for none. */
|
/** Currently selected workflow id, or null for none. */
|
||||||
value: string | null;
|
value: string | null;
|
||||||
@@ -63,7 +68,7 @@ export function WorkflowSelector({
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (!cancelled) setWorkflows([]);
|
if (!cancelled) setWorkflows([]);
|
||||||
addToast?.(getErrorMessage(err) || "Failed to load workflows", "error");
|
addToast?.(getErrorMessage(err) || t("workflowSelector.loadFailed", "Failed to load workflows"), "error");
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (!cancelled) setLoading(false);
|
if (!cancelled) setLoading(false);
|
||||||
@@ -93,7 +98,7 @@ export function WorkflowSelector({
|
|||||||
try {
|
try {
|
||||||
await onChange(workflowId);
|
await onChange(workflowId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addToast?.(getErrorMessage(err) || "Failed to apply workflow", "error");
|
addToast?.(getErrorMessage(err) || t("workflowSelector.applyFailed", "Failed to apply workflow"), "error");
|
||||||
} finally {
|
} finally {
|
||||||
setApplying(false);
|
setApplying(false);
|
||||||
}
|
}
|
||||||
@@ -115,7 +120,7 @@ export function WorkflowSelector({
|
|||||||
disabled={disabled || loading || applying}
|
disabled={disabled || loading || applying}
|
||||||
onChange={(e) => void handleChange(e.target.value)}
|
onChange={(e) => void handleChange(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="">None</option>
|
<option value="">{t("workflowSelector.none", "None")}</option>
|
||||||
{workflows.map((w) => (
|
{workflows.map((w) => (
|
||||||
<option key={w.id} value={w.id}>
|
<option key={w.id} value={w.id}>
|
||||||
{w.name}
|
{w.name}
|
||||||
@@ -125,7 +130,7 @@ export function WorkflowSelector({
|
|||||||
</div>
|
</div>
|
||||||
{onManage && (
|
{onManage && (
|
||||||
<button type="button" className="workflow-selector-manage" onClick={onManage}>
|
<button type="button" className="workflow-selector-manage" onClick={onManage}>
|
||||||
Manage…
|
{t("workflowSelector.manage", "Manage…")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -140,6 +145,7 @@ interface ProjectDefaultWorkflowFieldProps {
|
|||||||
|
|
||||||
/** Self-contained project-default workflow picker for the settings modal. */
|
/** Self-contained project-default workflow picker for the settings modal. */
|
||||||
export function ProjectDefaultWorkflowField({ projectId, addToast, onManage }: ProjectDefaultWorkflowFieldProps) {
|
export function ProjectDefaultWorkflowField({ projectId, addToast, onManage }: ProjectDefaultWorkflowFieldProps) {
|
||||||
|
const { t } = useTranslation("app");
|
||||||
const [value, setValue] = useState<string | null>(null);
|
const [value, setValue] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -162,9 +168,9 @@ export function ProjectDefaultWorkflowField({ projectId, addToast, onManage }: P
|
|||||||
async (workflowId: string | null) => {
|
async (workflowId: string | null) => {
|
||||||
const res = await setProjectDefaultWorkflow(workflowId, projectId);
|
const res = await setProjectDefaultWorkflow(workflowId, projectId);
|
||||||
setValue(res.workflowId);
|
setValue(res.workflowId);
|
||||||
addToast?.(workflowId ? "Default workflow set" : "Default workflow cleared", "success");
|
addToast?.(workflowId ? t("workflowSelector.defaultSet", "Default workflow set") : t("workflowSelector.defaultCleared", "Default workflow cleared"), "success");
|
||||||
},
|
},
|
||||||
[projectId, addToast],
|
[projectId, addToast, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -173,7 +179,7 @@ export function ProjectDefaultWorkflowField({ projectId, addToast, onManage }: P
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
addToast={addToast}
|
addToast={addToast}
|
||||||
label="Default workflow for new tasks"
|
label={t("workflowSelector.defaultWorkflowLabel", "Default workflow for new tasks")}
|
||||||
onManage={onManage}
|
onManage={onManage}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": "Drag can't move a card between workflows. Use the workflow switcher instead."
|
"workflowMismatch": "Drag can't move a card between workflows. Use the workflow switcher instead."
|
||||||
},
|
},
|
||||||
"todo": "To Do",
|
"todo": "To Do",
|
||||||
"triage": "Triage"
|
"triage": "Triage",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "Edit workflows",
|
||||||
|
"new": "New workflow"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "Abandon group",
|
"abandonGroup": "Abandon group",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "Comments",
|
"heading": "Comments",
|
||||||
"placeholder": "Add a comment",
|
"placeholder": "Add a comment",
|
||||||
"postingButton": "Posting…",
|
"postingButton": "Posting…",
|
||||||
"updatedSuccess": "Comment updated"
|
"updatedSuccess": "Comment updated",
|
||||||
|
"aiGuidance": "AI Guidance"
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "Files Changed ({{count}})",
|
"filesChanged_one": "Files Changed ({{count}})",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "Re-run preflight",
|
"rerunPreflight": "Re-run preflight",
|
||||||
"revertToAi": "Revert to AI version",
|
"revertToAi": "Revert to AI version",
|
||||||
"titleLabel": "Title",
|
"titleLabel": "Title",
|
||||||
"usingTemplate": "Using <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "Using <code>.github/pull_request_template.md</code>",
|
||||||
|
"assignees": "Assignees",
|
||||||
|
"dismissConflictResolutionError": "Dismiss conflict resolution error",
|
||||||
|
"dismissPushBranchError": "Dismiss push branch error",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "Action: open",
|
||||||
|
"actionRun": "Action: run",
|
||||||
|
"docs": "docs"
|
||||||
|
},
|
||||||
|
"generatingBody": "Generating AI body…",
|
||||||
|
"generatingTitle": "Generating AI title…",
|
||||||
|
"labels": "Labels",
|
||||||
|
"loadingOptions": "Loading PR options…",
|
||||||
|
"loadingPreflight": "Loading pre-flight checks…",
|
||||||
|
"noChangedFiles": "No changed files detected.",
|
||||||
|
"noCommits": "No commits found.",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "Push branch to remote",
|
||||||
|
"message": "Fusion will push this task's branch to origin so the PR can be created.",
|
||||||
|
"title": "Push branch to remote"
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "Resolve conflicts with AI",
|
||||||
|
"message": "Fusion will use AI to resolve conflicts on this branch and push it.",
|
||||||
|
"title": "Resolve conflicts with AI"
|
||||||
|
},
|
||||||
|
"reviewers": "Reviewers",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "agent disagreed",
|
||||||
|
"agentReplyFix": "Agent reply — fix {{sha}}",
|
||||||
|
"approve": "Approve",
|
||||||
|
"autoMerge": "Auto-merge",
|
||||||
|
"close": "Close",
|
||||||
|
"confirmMerge": "Confirm merge",
|
||||||
|
"creating": "Creating PR…",
|
||||||
|
"creationFailed": "PR creation failed",
|
||||||
|
"loading": "Loading PR…",
|
||||||
|
"merge": "Merge",
|
||||||
|
"mergeableLabel": "Mergeable:",
|
||||||
|
"mergeDisabledUntilVerified": "Merge is disabled until GitHub verifies this PR",
|
||||||
|
"none": "none",
|
||||||
|
"noReviewThreads": "No review threads.",
|
||||||
|
"requestRetry": "Request retry",
|
||||||
|
"resolveConflictsBeforeMerge": "Resolve conflicts on GitHub before merging",
|
||||||
|
"resolveConflictsOnGithub": "Resolve conflicts on GitHub",
|
||||||
|
"responseAlreadyInProgress": "A response run is already in progress",
|
||||||
|
"responsePending_one": "Response run in progress — {{count}} threads pending",
|
||||||
|
"responsePending_other": "Response run in progress — {{count}} threads pending",
|
||||||
|
"retryCreation": "Retry PR creation",
|
||||||
|
"reviewLabel": "Review:",
|
||||||
|
"threadFixed": "fixed",
|
||||||
|
"threadPending": "pending",
|
||||||
|
"verifyingGithub": "Verifying with GitHub…"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "You can view the preview in a separate browser tab.",
|
"blockedDescription": "You can view the preview in a separate browser tab.",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "total size",
|
"totalSize": "total size",
|
||||||
"view": "View ",
|
"view": "View ",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "When enabled, project and agent memory files are backed up automatically on a schedule.",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "When enabled, project and agent memory files are backed up automatically on a schedule.",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": "When enabled, the database is backed up automatically on a schedule"
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "When enabled, the database is backed up automatically on a schedule",
|
||||||
|
"createFailed": "Failed to create backup"
|
||||||
},
|
},
|
||||||
"clearToDefault": "Reset to default",
|
"clearToDefault": "Reset to default",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"version": "Version {{version}}"
|
"version": "Version {{version}}",
|
||||||
|
"checkUpdates": "Check for updates",
|
||||||
|
"helpDiscussions": "Help and discussions"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "25",
|
"25": "25",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": " When enabled, startup refreshes models through the local "
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": " When enabled, startup refreshes models through the local "
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "Join our Discord",
|
||||||
|
"star": "Star",
|
||||||
|
"starFusion": "Star Fusion on GitHub"
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "Confirm Import",
|
"confirmImport": "Confirm Import",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "Importing…",
|
"importing": "Importing…",
|
||||||
"importTitle": "Import Settings",
|
"importTitle": "Import Settings",
|
||||||
"loadingFile": "Loading…",
|
"loadingFile": "Loading…",
|
||||||
"reviewPrompt": "Review the settings to be imported:"
|
"reviewPrompt": "Review the settings to be imported:",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "{{count}} global",
|
||||||
|
"global_other": "{{count}} global",
|
||||||
|
"project_one": "{{count}} project",
|
||||||
|
"project_other": "{{count}} project",
|
||||||
|
"workflowSettings_one": "{{count}} workflow setting value",
|
||||||
|
"workflowSettings_other": "{{count}} workflow setting value"
|
||||||
|
},
|
||||||
|
"exported": "Settings exported ({{scope}} scope)",
|
||||||
|
"exportFailed": "Failed to export settings",
|
||||||
|
"globalSettings": "Global Settings:",
|
||||||
|
"imported": "Imported {{counts}} setting(s)",
|
||||||
|
"importFailed": "Import failed",
|
||||||
|
"importFailedDetailed": "Failed to import settings",
|
||||||
|
"importScope": "Import Scope:",
|
||||||
|
"importTitleAttr": "Import settings from JSON file",
|
||||||
|
"invalidJson": "Invalid JSON file: {{error}}",
|
||||||
|
"mergeExisting": "Merge with existing settings (recommended)",
|
||||||
|
"projectSettings": "Project Settings:",
|
||||||
|
"replaceWarning": "If unchecked, existing settings will be replaced with imported values.",
|
||||||
|
"scopeBoth": "Both global and project settings",
|
||||||
|
"scopeGlobal": "Global settings only",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "all",
|
||||||
|
"global": "global",
|
||||||
|
"project": "project"
|
||||||
|
},
|
||||||
|
"scopeProject": "Project settings only"
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "Enter JSON value...",
|
"jsonPlaceholder": "Enter JSON value...",
|
||||||
"keepLocal": "Keep Local",
|
"keepLocal": "Keep Local",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "Search memory with qmd",
|
"searchMemoryWithQmd": "Search memory with qmd",
|
||||||
"testing": "Testing…",
|
"testing": "Testing…",
|
||||||
"testRetrieval": "Test Retrieval",
|
"testRetrieval": "Test Retrieval",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": "Turns daily notes into DREAMS.md and promotes reusable lessons into MEMORY.md."
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "Turns daily notes into DREAMS.md and promotes reusable lessons into MEMORY.md.",
|
||||||
|
"qmdInstalled": "qmd installed successfully",
|
||||||
|
"qmdInstallFailed": "Failed to install qmd",
|
||||||
|
"qmdInstallUnavailable": "qmd install finished, but qmd is still unavailable"
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "Abort",
|
"abort": "Abort",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": " URL and QR generation use the selected token type. ",
|
"uRLAndQRGenerationUseTheSelectedToken": " URL and QR generation use the selected token type. ",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": " URL — no hostname or port configuration needed.",
|
"uRLNoHostnameOrPortConfigurationNeeded": " URL — no hostname or port configuration needed.",
|
||||||
"useExisting": "Use Existing",
|
"useExisting": "Use Existing",
|
||||||
"usingQuickTunnel": "Using Quick Tunnel — automatically creates a random trycloudflare.com URL, no account needed."
|
"usingQuickTunnel": "Using Quick Tunnel — automatically creates a random trycloudflare.com URL, no account needed.",
|
||||||
|
"installationFailed": "Installation failed"
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "Advanced — external search providers",
|
"advancedExternalSearchProviders": "Advanced — external search providers",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "Timeout in minutes for detecting stuck tasks. When a task's agent session shows no activity for longer than this duration, the task is terminated and retried. Leave empty to disable. Suggested: 10.",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "Timeout in minutes for detecting stuck tasks. When a task's agent session shows no activity for longer than this duration, the task is terminated and retried. Leave empty to disable. Suggested: 10.",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "When enabled, tasks that modify the same files are queued serially to avoid merge conflicts",
|
"whenEnabledTasksThatModifyTheSameFiles": "When enabled, tasks that modify the same files are queued serially to avoid merge conflicts",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "When enabled, tasks with stale plans (PROMPT.md older than the threshold) are automatically sent back to planning for replanning",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "When enabled, tasks with stale plans (PROMPT.md older than the threshold) are automatically sent back to planning for replanning",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": "When the stuck detector kills and re-queues a task, keep completed step statuses so the agent can resume from where it left off. Disable to reset every step to pending on each stuck retry. Default: enabled."
|
"whenTheStuckDetectorKillsAndReQueues": "When the stuck detector kills and re-queues a task, keep completed step statuses so the agent can resume from where it left off. Disable to reset every step to pending on each stuck retry. Default: enabled.",
|
||||||
|
"browseWorkspacePath": "Browse workspace path",
|
||||||
|
"overlapPickerNote": "Choose a file to ignore directly, or navigate into a folder and select the current directory."
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "These settings are shared across all your Fusion projects.",
|
"globalBanner": "These settings are shared across all your Fusion projects.",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": " worktrunk ",
|
"worktrunk": " worktrunk ",
|
||||||
"worktrunkBinaryPath": "Worktrunk binary path",
|
"worktrunkBinaryPath": "Worktrunk binary path",
|
||||||
"worktrunkFailureBehavior": "Worktrunk failure behavior",
|
"worktrunkFailureBehavior": "Worktrunk failure behavior",
|
||||||
"worktrunkIntegration": "Worktrunk integration"
|
"worktrunkIntegration": "Worktrunk integration",
|
||||||
|
"worktreesPickerNote": "Navigate to the folder where Fusion should create task worktrees, then select the current directory."
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "Current directory:",
|
||||||
|
"projectRoot": "(project root)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "Create tasks manually",
|
"withoutGitHub1": "Create tasks manually",
|
||||||
"withoutGitHub2": "Describe work for AI agents",
|
"withoutGitHub2": "Describe work for AI agents",
|
||||||
"withoutGitHub3": "Track progress on the board",
|
"withoutGitHub3": "Track progress on the board",
|
||||||
"withoutGitHubHeading": "Without GitHub (available now):"
|
"withoutGitHubHeading": "Without GitHub (available now):",
|
||||||
|
"brandLogo": "Fusion logo",
|
||||||
|
"brandName": "Fusion",
|
||||||
|
"setupCompleteTitle": "Setup Complete!"
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "Active",
|
"activePill": "Active",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "Merging fixes…",
|
"mergingFixes": "Merging fixes…",
|
||||||
"mergingPr": "Merging PR…",
|
"mergingPr": "Merging PR…",
|
||||||
"startPrReview": "Start PR Review",
|
"startPrReview": "Start PR Review",
|
||||||
"statusRefreshed": "PR status refreshed"
|
"statusRefreshed": "PR status refreshed",
|
||||||
|
"label": "PR"
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "Task priority",
|
"ariaLabel": "Task priority",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "Created by",
|
"createdBy": "Created by",
|
||||||
"createdVia": "Created via"
|
"createdVia": "Created via",
|
||||||
|
"parentTaskOf": "of"
|
||||||
},
|
},
|
||||||
"recoveryState": "Recovery state",
|
"recoveryState": "Recovery state",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "Switch to markdown",
|
"switchToMarkdown": "Switch to markdown",
|
||||||
"switchToPlain": "Switch to plain text",
|
"switchToPlain": "Switch to plain text",
|
||||||
"thinkingLevel": "Thinking level",
|
"thinkingLevel": "Thinking level",
|
||||||
"workflowName": "Workflow"
|
"workflowName": "Workflow",
|
||||||
|
"aggregateAdvisory": "Advisory",
|
||||||
|
"aggregateAllPassed": "All passed",
|
||||||
|
"aggregateInProgress": "In progress",
|
||||||
|
"aggregateNoResults": "No results",
|
||||||
|
"approveAndRun": "Approve & run",
|
||||||
|
"approveCommandError": "Failed to approve command",
|
||||||
|
"approving": "Approving…",
|
||||||
|
"awaitingInputTitle": "Waiting for your input",
|
||||||
|
"cliApprovalRejectHint": "To reject, keep the task paused and do not approve.",
|
||||||
|
"cliApprovalTitle": "Approve CLI command?",
|
||||||
|
"cliApprovalWarning": "This command will run in the task worktree. Approving trusts this exact command for future runs.",
|
||||||
|
"customWorkflowFallback": "Custom workflow",
|
||||||
|
"customWorkflowLabel": "Custom workflow",
|
||||||
|
"defaultWorkflow": "Default",
|
||||||
|
"executionAwaitingCliApproval": "Awaiting CLI approval",
|
||||||
|
"executionAwaitingInput": "Awaiting input",
|
||||||
|
"executionCompleted": "Completed",
|
||||||
|
"executionNotStarted": "Not started",
|
||||||
|
"executionPaused": "Paused",
|
||||||
|
"executionPostMerge": "Post-merge steps running",
|
||||||
|
"executionPreMerge": "Pre-merge steps running",
|
||||||
|
"inputPlaceholder": "Type your reply…",
|
||||||
|
"modelDefault": "Default",
|
||||||
|
"postMerge": "Post-merge",
|
||||||
|
"preMerge": "Pre-merge",
|
||||||
|
"replyInComments": "Reply in the comments and unpause the task to continue.",
|
||||||
|
"resumeTaskError": "Failed to resume task",
|
||||||
|
"resuming": "Resuming…",
|
||||||
|
"statusAdvisory": "Advisory failure",
|
||||||
|
"statusFailed": "Failed",
|
||||||
|
"statusPassed": "Passed",
|
||||||
|
"statusRunning": "Running…",
|
||||||
|
"statusSkipped": "Skipped",
|
||||||
|
"submitAndResume": "Submit & resume",
|
||||||
|
"submitting": "Submitting…",
|
||||||
|
"waitingForOutput": "Waiting for agent output…"
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "Add column",
|
"add": "Add column",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "Collapse prompt editor",
|
"collapsePrompt": "Collapse prompt editor",
|
||||||
"editingPrompt": "Editing Prompt",
|
"editingPrompt": "Editing Prompt",
|
||||||
"expandPrompt": "Expand prompt editor"
|
"expandPrompt": "Expand prompt editor",
|
||||||
|
"agent": "Agent",
|
||||||
|
"agentsLoadFailed": "Failed to load agents",
|
||||||
|
"autoApproveRequests": "Auto-approve requests",
|
||||||
|
"autoApproveRequestsNote": "Runs without pausing for approval — e.g. a CLI command executes on its first run without waiting for your sign-off.",
|
||||||
|
"cliCommandNote": "Runs an arbitrary command in the task worktree. The first time this exact command runs, the task pauses for your approval. The node prompt is passed via FUSION_NODE_PROMPT.",
|
||||||
|
"cliMode": "CLI mode",
|
||||||
|
"cliScript": "CLI / script",
|
||||||
|
"command": "Command",
|
||||||
|
"executor": "Executor",
|
||||||
|
"maxRetries": "Max retries",
|
||||||
|
"model": "Model",
|
||||||
|
"modelsLoadFailed": "Failed to load models",
|
||||||
|
"namedScript": "Named script",
|
||||||
|
"namedScriptNote": "Named script from project settings. The node prompt is passed via FUSION_NODE_PROMPT.",
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"scriptName": "Script name",
|
||||||
|
"selectAgent": "— select agent —",
|
||||||
|
"selectSkill": "— select skill —",
|
||||||
|
"skill": "Skill",
|
||||||
|
"skillsLoadFailed": "Failed to load skills",
|
||||||
|
"skipFirstRunApproval": "Skip first-run approval (runs without pausing)",
|
||||||
|
"waitForUserInput": "Wait for user input",
|
||||||
|
"waitForUserInputNote": "This node pauses the task until you reply in the task's comments and unpause. The Prompt field above is shown to the user as the question."
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "Add field",
|
"add": "Add field",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "Plugin steps",
|
"templatesPluginSteps": "Plugin steps",
|
||||||
"templatesSection": "Templates",
|
"templatesSection": "Templates",
|
||||||
"timeoutMs": "{{timeout}}ms",
|
"timeoutMs": "{{timeout}}ms",
|
||||||
"trivialGraphHint": "This workflow only runs start → end. Add steps from the palette above to build it out."
|
"trivialGraphHint": "This workflow only runs start → end. Add steps from the palette above to build it out.",
|
||||||
|
"conditionFailure": "failure",
|
||||||
|
"conditionSuccess": "success",
|
||||||
|
"nodeInspector": "Node",
|
||||||
|
"readOnlyDuplicateToEdit": "Read-only built-in — duplicate the workflow to edit nodes."
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "Design with AI",
|
"aiEdit": "Design with AI",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "{{count}} nodes",
|
"templateNodeCount_other": "{{count}} nodes",
|
||||||
"templatePickerLabel": "Start from",
|
"templatePickerLabel": "Start from",
|
||||||
"templateSectionBuiltin": "Built-in workflows",
|
"templateSectionBuiltin": "Built-in workflows",
|
||||||
"templateSectionYours": "Your workflows"
|
"templateSectionYours": "Your workflows",
|
||||||
|
"closeEditor": "Close workflow editor",
|
||||||
|
"duplicatedEditable": "Duplicated to \"{{name}}\" — editable",
|
||||||
|
"duplicateFailed": "Failed to duplicate workflow",
|
||||||
|
"loadFailed": "Failed to load workflows",
|
||||||
|
"loading": "Loading…",
|
||||||
|
"noneYet": "No workflows yet.",
|
||||||
|
"title": "Workflows"
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "This task has an active session. Switching workflows aborts it and re-homes the card into the new workflow's entry column. Continue?",
|
"switchActiveMessage": "This task has an active session. Switching workflows aborts it and re-homes the card into the new workflow's entry column. Continue?",
|
||||||
"switchActiveTitle": "Switch workflow?",
|
"switchActiveTitle": "Switch workflow?",
|
||||||
"switchCancel": "Cancel",
|
"switchCancel": "Cancel",
|
||||||
"switchConfirm": "Switch and abort"
|
"switchConfirm": "Switch and abort",
|
||||||
|
"applyFailed": "Failed to apply workflow",
|
||||||
|
"defaultCleared": "Default workflow cleared",
|
||||||
|
"defaultSet": "Default workflow set",
|
||||||
|
"defaultWorkflowLabel": "Default workflow for new tasks",
|
||||||
|
"loadFailed": "Failed to load workflows",
|
||||||
|
"manage": "Manage…",
|
||||||
|
"none": "None"
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "Add setting",
|
"add": "Add setting",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Worktrunk install request",
|
"installRequestTitle": "Worktrunk install request",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "Version"
|
"version": "Version"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "Steer the currently executing agent",
|
||||||
|
"activeSessionHint": "Message the active agent session. Guidance is delivered to the running session in real time.",
|
||||||
|
"agentMessages": "{{label}} messages",
|
||||||
|
"arguments": "Arguments",
|
||||||
|
"collapseChat": "Collapse chat",
|
||||||
|
"donePlaceholder": "Start a refinement task for this completed task",
|
||||||
|
"doneSessionHint": "Send a message to start a refinement task for this completed task.",
|
||||||
|
"emptyAgentOutput": "No agent output yet. Live messages from Planner, Executor, Reviewer, and Merger agents will appear here.",
|
||||||
|
"entryCount_one": "{{count}} entry",
|
||||||
|
"entryCount_other": "{{count}} entries",
|
||||||
|
"error": "Error",
|
||||||
|
"errorCount_one": "{{count}} error",
|
||||||
|
"errorCount_other": "{{count}} errors",
|
||||||
|
"expandChat": "Expand chat to full modal",
|
||||||
|
"idleSessionHint": "No agent is working on this task right now. Your message is saved as guidance and will reach an agent the next time this task runs.",
|
||||||
|
"jumpToLatestMessage": "Jump to latest message",
|
||||||
|
"latest": "Latest",
|
||||||
|
"loadingAgentOutput": "Loading agent output…",
|
||||||
|
"loadingEarlierMessages": "Loading earlier messages…",
|
||||||
|
"loadPreviousMessages": "Load previous messages",
|
||||||
|
"message": "Message",
|
||||||
|
"messageActiveAgentSession": "Message active agent session",
|
||||||
|
"moreTools_one": ", +{{count}} more",
|
||||||
|
"moreTools_other": ", +{{count}} more",
|
||||||
|
"result": "Result",
|
||||||
|
"roles": {
|
||||||
|
"agent": "Agent",
|
||||||
|
"executor": "Executor",
|
||||||
|
"merger": "Merger",
|
||||||
|
"planner": "Planner",
|
||||||
|
"reviewer": "Reviewer"
|
||||||
|
},
|
||||||
|
"sending": "Sending",
|
||||||
|
"thinking": "Thinking",
|
||||||
|
"toolCall": "Tool call",
|
||||||
|
"toolCallCount_one": "{{count}} tool call",
|
||||||
|
"toolCallCount_other": "{{count}} tool calls",
|
||||||
|
"toolCallTo": "Tool call → {{label}}",
|
||||||
|
"toolError": "Tool error",
|
||||||
|
"toolNames": "Tool names",
|
||||||
|
"toolResult": "Tool result",
|
||||||
|
"you": "You",
|
||||||
|
"youMessage": "You message"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": "Send"
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "↓ Declining",
|
"trendDeclining": "↓ Declining",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"online": "Online"
|
"online": "Online"
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": "Name"
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": "Unknown"
|
"unknown": "Unknown"
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "Initial load",
|
"refreshSourceInitialLoad": "Initial load",
|
||||||
"refreshSourceManual": "Manual"
|
"refreshSourceManual": "Manual"
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "All passed",
|
|
||||||
"aggregateInProgress": "In progress",
|
|
||||||
"aggregateNoResults": "No results",
|
|
||||||
"customWorkflowFallback": "Custom workflow",
|
|
||||||
"defaultWorkflow": "Default",
|
|
||||||
"executionAwaitingCliApproval": "Awaiting CLI approval",
|
|
||||||
"executionAwaitingInput": "Awaiting input",
|
|
||||||
"executionCompleted": "Completed",
|
|
||||||
"executionNotStarted": "Not started",
|
|
||||||
"executionPaused": "Paused",
|
|
||||||
"executionPostMerge": "Post-merge steps running",
|
|
||||||
"executionPreMerge": "Pre-merge steps running",
|
|
||||||
"postMerge": "Post-merge",
|
|
||||||
"preMerge": "Pre-merge",
|
|
||||||
"statusAdvisory": "Advisory failure",
|
|
||||||
"statusFailed": "Failed",
|
|
||||||
"statusPassed": "Passed",
|
|
||||||
"statusRunning": "Running…",
|
|
||||||
"statusSkipped": "Skipped",
|
|
||||||
"waitingForOutput": "Waiting for agent output…"
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "Waits for user input",
|
"summaryAwaitInput": "Waits for user input",
|
||||||
"summaryCodeDefault": "TypeScript",
|
"summaryCodeDefault": "TypeScript",
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": ""
|
"workflowMismatch": ""
|
||||||
},
|
},
|
||||||
"todo": "Por hacer",
|
"todo": "Por hacer",
|
||||||
"triage": "Triaje"
|
"triage": "Triaje",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "",
|
"abandonGroup": "",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "Comentarios",
|
"heading": "Comentarios",
|
||||||
"placeholder": "Añadir un comentario",
|
"placeholder": "Añadir un comentario",
|
||||||
"postingButton": "Publicando…",
|
"postingButton": "Publicando…",
|
||||||
"updatedSuccess": "Comentario actualizado"
|
"updatedSuccess": "Comentario actualizado",
|
||||||
|
"aiGuidance": ""
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "",
|
"filesChanged_one": "",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "Volver a ejecutar la comprobación previa",
|
"rerunPreflight": "Volver a ejecutar la comprobación previa",
|
||||||
"revertToAi": "Revertir a la versión de IA",
|
"revertToAi": "Revertir a la versión de IA",
|
||||||
"titleLabel": "Título",
|
"titleLabel": "Título",
|
||||||
"usingTemplate": "Usando <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "Usando <code>.github/pull_request_template.md</code>",
|
||||||
|
"assignees": "",
|
||||||
|
"dismissConflictResolutionError": "",
|
||||||
|
"dismissPushBranchError": "",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "",
|
||||||
|
"actionRun": "",
|
||||||
|
"docs": ""
|
||||||
|
},
|
||||||
|
"generatingBody": "",
|
||||||
|
"generatingTitle": "",
|
||||||
|
"labels": "",
|
||||||
|
"loadingOptions": "",
|
||||||
|
"loadingPreflight": "",
|
||||||
|
"noChangedFiles": "",
|
||||||
|
"noCommits": "",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"reviewers": "",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "",
|
||||||
|
"agentReplyFix": "",
|
||||||
|
"approve": "",
|
||||||
|
"autoMerge": "",
|
||||||
|
"close": "",
|
||||||
|
"confirmMerge": "",
|
||||||
|
"creating": "",
|
||||||
|
"creationFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"merge": "",
|
||||||
|
"mergeableLabel": "",
|
||||||
|
"mergeDisabledUntilVerified": "",
|
||||||
|
"none": "",
|
||||||
|
"noReviewThreads": "",
|
||||||
|
"requestRetry": "",
|
||||||
|
"resolveConflictsBeforeMerge": "",
|
||||||
|
"resolveConflictsOnGithub": "",
|
||||||
|
"responseAlreadyInProgress": "",
|
||||||
|
"responsePending_one": "",
|
||||||
|
"responsePending_other": "",
|
||||||
|
"retryCreation": "",
|
||||||
|
"reviewLabel": "",
|
||||||
|
"threadFixed": "",
|
||||||
|
"threadPending": "",
|
||||||
|
"verifyingGithub": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "Puede ver la vista previa en una pestaña del navegador separada.",
|
"blockedDescription": "Puede ver la vista previa en una pestaña del navegador separada.",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "",
|
"totalSize": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": ""
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "",
|
||||||
|
"createFailed": ""
|
||||||
},
|
},
|
||||||
"clearToDefault": "",
|
"clearToDefault": "",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "Ayuda",
|
"help": "Ayuda",
|
||||||
"version": "Versión {{version}}"
|
"version": "Versión {{version}}",
|
||||||
|
"checkUpdates": "",
|
||||||
|
"helpDiscussions": ""
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "",
|
"25": "",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "",
|
||||||
|
"star": "",
|
||||||
|
"starFusion": ""
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "Confirmar importación",
|
"confirmImport": "Confirmar importación",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "Importando…",
|
"importing": "Importando…",
|
||||||
"importTitle": "Importar configuración",
|
"importTitle": "Importar configuración",
|
||||||
"loadingFile": "Cargando…",
|
"loadingFile": "Cargando…",
|
||||||
"reviewPrompt": "Revisa la configuración a importar:"
|
"reviewPrompt": "Revisa la configuración a importar:",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "",
|
||||||
|
"global_other": "",
|
||||||
|
"project_one": "",
|
||||||
|
"project_other": "",
|
||||||
|
"workflowSettings_one": "",
|
||||||
|
"workflowSettings_other": ""
|
||||||
|
},
|
||||||
|
"exported": "",
|
||||||
|
"exportFailed": "",
|
||||||
|
"globalSettings": "",
|
||||||
|
"imported": "",
|
||||||
|
"importFailed": "",
|
||||||
|
"importFailedDetailed": "",
|
||||||
|
"importScope": "",
|
||||||
|
"importTitleAttr": "",
|
||||||
|
"invalidJson": "",
|
||||||
|
"mergeExisting": "",
|
||||||
|
"projectSettings": "",
|
||||||
|
"replaceWarning": "",
|
||||||
|
"scopeBoth": "",
|
||||||
|
"scopeGlobal": "",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "",
|
||||||
|
"global": "",
|
||||||
|
"project": ""
|
||||||
|
},
|
||||||
|
"scopeProject": ""
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "Ingresa un valor JSON...",
|
"jsonPlaceholder": "Ingresa un valor JSON...",
|
||||||
"keepLocal": "Mantener local",
|
"keepLocal": "Mantener local",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "",
|
"searchMemoryWithQmd": "",
|
||||||
"testing": "Probando…",
|
"testing": "Probando…",
|
||||||
"testRetrieval": "Probar recuperación",
|
"testRetrieval": "Probar recuperación",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": ""
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "",
|
||||||
|
"qmdInstalled": "",
|
||||||
|
"qmdInstallFailed": "",
|
||||||
|
"qmdInstallUnavailable": ""
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "",
|
"abort": "",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": "",
|
"uRLAndQRGenerationUseTheSelectedToken": "",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
||||||
"useExisting": "Usar existente",
|
"useExisting": "Usar existente",
|
||||||
"usingQuickTunnel": ""
|
"usingQuickTunnel": "",
|
||||||
|
"installationFailed": ""
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "",
|
"advancedExternalSearchProviders": "",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "",
|
"whenEnabledTasksThatModifyTheSameFiles": "",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": ""
|
"whenTheStuckDetectorKillsAndReQueues": "",
|
||||||
|
"browseWorkspacePath": "",
|
||||||
|
"overlapPickerNote": ""
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "Estos ajustes se comparten entre todos tus proyectos de Fusion.",
|
"globalBanner": "Estos ajustes se comparten entre todos tus proyectos de Fusion.",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": "",
|
"worktrunk": "",
|
||||||
"worktrunkBinaryPath": "",
|
"worktrunkBinaryPath": "",
|
||||||
"worktrunkFailureBehavior": "",
|
"worktrunkFailureBehavior": "",
|
||||||
"worktrunkIntegration": ""
|
"worktrunkIntegration": "",
|
||||||
|
"worktreesPickerNote": ""
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "",
|
||||||
|
"projectRoot": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "Crear tareas manualmente",
|
"withoutGitHub1": "Crear tareas manualmente",
|
||||||
"withoutGitHub2": "Describir trabajo para agentes de IA",
|
"withoutGitHub2": "Describir trabajo para agentes de IA",
|
||||||
"withoutGitHub3": "Seguir el progreso en el tablero",
|
"withoutGitHub3": "Seguir el progreso en el tablero",
|
||||||
"withoutGitHubHeading": "Sin GitHub (disponible ahora):"
|
"withoutGitHubHeading": "Sin GitHub (disponible ahora):",
|
||||||
|
"brandLogo": "",
|
||||||
|
"brandName": "",
|
||||||
|
"setupCompleteTitle": ""
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "Activo",
|
"activePill": "Activo",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "Fusionando correcciones…",
|
"mergingFixes": "Fusionando correcciones…",
|
||||||
"mergingPr": "Fusionando PR…",
|
"mergingPr": "Fusionando PR…",
|
||||||
"startPrReview": "Iniciar revisión del PR",
|
"startPrReview": "Iniciar revisión del PR",
|
||||||
"statusRefreshed": "Estado del PR actualizado"
|
"statusRefreshed": "Estado del PR actualizado",
|
||||||
|
"label": ""
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "Prioridad de la tarea",
|
"ariaLabel": "Prioridad de la tarea",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "Creado por",
|
"createdBy": "Creado por",
|
||||||
"createdVia": "Creado mediante"
|
"createdVia": "Creado mediante",
|
||||||
|
"parentTaskOf": ""
|
||||||
},
|
},
|
||||||
"recoveryState": "Estado de recuperación",
|
"recoveryState": "Estado de recuperación",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "Cambiar a Markdown",
|
"switchToMarkdown": "Cambiar a Markdown",
|
||||||
"switchToPlain": "Cambiar a texto sin formato",
|
"switchToPlain": "Cambiar a texto sin formato",
|
||||||
"thinkingLevel": "",
|
"thinkingLevel": "",
|
||||||
"workflowName": ""
|
"workflowName": "",
|
||||||
|
"aggregateAdvisory": "",
|
||||||
|
"aggregateAllPassed": "",
|
||||||
|
"aggregateInProgress": "",
|
||||||
|
"aggregateNoResults": "",
|
||||||
|
"approveAndRun": "",
|
||||||
|
"approveCommandError": "",
|
||||||
|
"approving": "",
|
||||||
|
"awaitingInputTitle": "",
|
||||||
|
"cliApprovalRejectHint": "",
|
||||||
|
"cliApprovalTitle": "",
|
||||||
|
"cliApprovalWarning": "",
|
||||||
|
"customWorkflowFallback": "",
|
||||||
|
"customWorkflowLabel": "",
|
||||||
|
"defaultWorkflow": "",
|
||||||
|
"executionAwaitingCliApproval": "",
|
||||||
|
"executionAwaitingInput": "",
|
||||||
|
"executionCompleted": "",
|
||||||
|
"executionNotStarted": "",
|
||||||
|
"executionPaused": "",
|
||||||
|
"executionPostMerge": "",
|
||||||
|
"executionPreMerge": "",
|
||||||
|
"inputPlaceholder": "",
|
||||||
|
"modelDefault": "",
|
||||||
|
"postMerge": "",
|
||||||
|
"preMerge": "",
|
||||||
|
"replyInComments": "",
|
||||||
|
"resumeTaskError": "",
|
||||||
|
"resuming": "",
|
||||||
|
"statusAdvisory": "",
|
||||||
|
"statusFailed": "",
|
||||||
|
"statusPassed": "",
|
||||||
|
"statusRunning": "",
|
||||||
|
"statusSkipped": "",
|
||||||
|
"submitAndResume": "",
|
||||||
|
"submitting": "",
|
||||||
|
"waitingForOutput": ""
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "",
|
"collapsePrompt": "",
|
||||||
"editingPrompt": "",
|
"editingPrompt": "",
|
||||||
"expandPrompt": ""
|
"expandPrompt": "",
|
||||||
|
"agent": "",
|
||||||
|
"agentsLoadFailed": "",
|
||||||
|
"autoApproveRequests": "",
|
||||||
|
"autoApproveRequestsNote": "",
|
||||||
|
"cliCommandNote": "",
|
||||||
|
"cliMode": "",
|
||||||
|
"cliScript": "",
|
||||||
|
"command": "",
|
||||||
|
"executor": "",
|
||||||
|
"maxRetries": "",
|
||||||
|
"model": "",
|
||||||
|
"modelsLoadFailed": "",
|
||||||
|
"namedScript": "",
|
||||||
|
"namedScriptNote": "",
|
||||||
|
"prompt": "",
|
||||||
|
"scriptName": "",
|
||||||
|
"selectAgent": "",
|
||||||
|
"selectSkill": "",
|
||||||
|
"skill": "",
|
||||||
|
"skillsLoadFailed": "",
|
||||||
|
"skipFirstRunApproval": "",
|
||||||
|
"waitForUserInput": "",
|
||||||
|
"waitForUserInputNote": ""
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "Agregar campo",
|
"add": "Agregar campo",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "Pasos de plugin",
|
"templatesPluginSteps": "Pasos de plugin",
|
||||||
"templatesSection": "Plantillas",
|
"templatesSection": "Plantillas",
|
||||||
"timeoutMs": "",
|
"timeoutMs": "",
|
||||||
"trivialGraphHint": "Este flujo de trabajo solo ejecuta inicio → fin. Añade pasos desde la paleta superior para construirlo."
|
"trivialGraphHint": "Este flujo de trabajo solo ejecuta inicio → fin. Añade pasos desde la paleta superior para construirlo.",
|
||||||
|
"conditionFailure": "",
|
||||||
|
"conditionSuccess": "",
|
||||||
|
"nodeInspector": "",
|
||||||
|
"readOnlyDuplicateToEdit": ""
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "Diseñar con IA",
|
"aiEdit": "Diseñar con IA",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "{{count}} nodos",
|
"templateNodeCount_other": "{{count}} nodos",
|
||||||
"templatePickerLabel": "Comenzar desde",
|
"templatePickerLabel": "Comenzar desde",
|
||||||
"templateSectionBuiltin": "Flujos de trabajo integrados",
|
"templateSectionBuiltin": "Flujos de trabajo integrados",
|
||||||
"templateSectionYours": "Tus flujos de trabajo"
|
"templateSectionYours": "Tus flujos de trabajo",
|
||||||
|
"closeEditor": "",
|
||||||
|
"duplicatedEditable": "",
|
||||||
|
"duplicateFailed": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"noneYet": "",
|
||||||
|
"title": ""
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "",
|
"switchActiveMessage": "",
|
||||||
"switchActiveTitle": "",
|
"switchActiveTitle": "",
|
||||||
"switchCancel": "",
|
"switchCancel": "",
|
||||||
"switchConfirm": ""
|
"switchConfirm": "",
|
||||||
|
"applyFailed": "",
|
||||||
|
"defaultCleared": "",
|
||||||
|
"defaultSet": "",
|
||||||
|
"defaultWorkflowLabel": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"manage": "",
|
||||||
|
"none": ""
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Solicitud de instalación de Worktrunk",
|
"installRequestTitle": "Solicitud de instalación de Worktrunk",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "Versión"
|
"version": "Versión"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "",
|
||||||
|
"activeSessionHint": "",
|
||||||
|
"agentMessages": "",
|
||||||
|
"arguments": "",
|
||||||
|
"collapseChat": "",
|
||||||
|
"donePlaceholder": "",
|
||||||
|
"doneSessionHint": "",
|
||||||
|
"emptyAgentOutput": "",
|
||||||
|
"entryCount_one": "",
|
||||||
|
"entryCount_other": "",
|
||||||
|
"error": "",
|
||||||
|
"errorCount_one": "",
|
||||||
|
"errorCount_other": "",
|
||||||
|
"expandChat": "",
|
||||||
|
"idleSessionHint": "",
|
||||||
|
"jumpToLatestMessage": "",
|
||||||
|
"latest": "",
|
||||||
|
"loadingAgentOutput": "",
|
||||||
|
"loadingEarlierMessages": "",
|
||||||
|
"loadPreviousMessages": "",
|
||||||
|
"message": "",
|
||||||
|
"messageActiveAgentSession": "",
|
||||||
|
"moreTools_one": "",
|
||||||
|
"moreTools_other": "",
|
||||||
|
"result": "",
|
||||||
|
"roles": {
|
||||||
|
"agent": "",
|
||||||
|
"executor": "",
|
||||||
|
"merger": "",
|
||||||
|
"planner": "",
|
||||||
|
"reviewer": ""
|
||||||
|
},
|
||||||
|
"sending": "",
|
||||||
|
"thinking": "",
|
||||||
|
"toolCall": "",
|
||||||
|
"toolCallCount_one": "",
|
||||||
|
"toolCallCount_other": "",
|
||||||
|
"toolCallTo": "",
|
||||||
|
"toolError": "",
|
||||||
|
"toolNames": "",
|
||||||
|
"toolResult": "",
|
||||||
|
"you": "",
|
||||||
|
"youMessage": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": ""
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "",
|
"trendDeclining": "",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "",
|
"offline": "",
|
||||||
"online": ""
|
"online": ""
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": ""
|
"unknown": ""
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "",
|
"refreshSourceInitialLoad": "",
|
||||||
"refreshSourceManual": ""
|
"refreshSourceManual": ""
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "",
|
|
||||||
"aggregateInProgress": "",
|
|
||||||
"aggregateNoResults": "",
|
|
||||||
"customWorkflowFallback": "",
|
|
||||||
"defaultWorkflow": "",
|
|
||||||
"executionAwaitingCliApproval": "",
|
|
||||||
"executionAwaitingInput": "",
|
|
||||||
"executionCompleted": "",
|
|
||||||
"executionNotStarted": "",
|
|
||||||
"executionPaused": "",
|
|
||||||
"executionPostMerge": "",
|
|
||||||
"executionPreMerge": "",
|
|
||||||
"postMerge": "",
|
|
||||||
"preMerge": "",
|
|
||||||
"statusAdvisory": "",
|
|
||||||
"statusFailed": "",
|
|
||||||
"statusPassed": "",
|
|
||||||
"statusRunning": "",
|
|
||||||
"statusSkipped": "",
|
|
||||||
"waitingForOutput": ""
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "",
|
"summaryAwaitInput": "",
|
||||||
"summaryCodeDefault": "",
|
"summaryCodeDefault": "",
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": ""
|
"workflowMismatch": ""
|
||||||
},
|
},
|
||||||
"todo": "À faire",
|
"todo": "À faire",
|
||||||
"triage": "Triage"
|
"triage": "Triage",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "",
|
"abandonGroup": "",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "Commentaires",
|
"heading": "Commentaires",
|
||||||
"placeholder": "Ajouter un commentaire",
|
"placeholder": "Ajouter un commentaire",
|
||||||
"postingButton": "Publication en cours…",
|
"postingButton": "Publication en cours…",
|
||||||
"updatedSuccess": "Commentaire mis à jour"
|
"updatedSuccess": "Commentaire mis à jour",
|
||||||
|
"aiGuidance": ""
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "",
|
"filesChanged_one": "",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "Relancer les vérifications de pré-vol",
|
"rerunPreflight": "Relancer les vérifications de pré-vol",
|
||||||
"revertToAi": "Revenir à la version IA",
|
"revertToAi": "Revenir à la version IA",
|
||||||
"titleLabel": "Titre",
|
"titleLabel": "Titre",
|
||||||
"usingTemplate": "Utilisation de <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "Utilisation de <code>.github/pull_request_template.md</code>",
|
||||||
|
"assignees": "",
|
||||||
|
"dismissConflictResolutionError": "",
|
||||||
|
"dismissPushBranchError": "",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "",
|
||||||
|
"actionRun": "",
|
||||||
|
"docs": ""
|
||||||
|
},
|
||||||
|
"generatingBody": "",
|
||||||
|
"generatingTitle": "",
|
||||||
|
"labels": "",
|
||||||
|
"loadingOptions": "",
|
||||||
|
"loadingPreflight": "",
|
||||||
|
"noChangedFiles": "",
|
||||||
|
"noCommits": "",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"reviewers": "",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "",
|
||||||
|
"agentReplyFix": "",
|
||||||
|
"approve": "",
|
||||||
|
"autoMerge": "",
|
||||||
|
"close": "",
|
||||||
|
"confirmMerge": "",
|
||||||
|
"creating": "",
|
||||||
|
"creationFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"merge": "",
|
||||||
|
"mergeableLabel": "",
|
||||||
|
"mergeDisabledUntilVerified": "",
|
||||||
|
"none": "",
|
||||||
|
"noReviewThreads": "",
|
||||||
|
"requestRetry": "",
|
||||||
|
"resolveConflictsBeforeMerge": "",
|
||||||
|
"resolveConflictsOnGithub": "",
|
||||||
|
"responseAlreadyInProgress": "",
|
||||||
|
"responsePending_one": "",
|
||||||
|
"responsePending_other": "",
|
||||||
|
"retryCreation": "",
|
||||||
|
"reviewLabel": "",
|
||||||
|
"threadFixed": "",
|
||||||
|
"threadPending": "",
|
||||||
|
"verifyingGithub": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "Vous pouvez afficher l'aperçu dans un onglet du navigateur séparé.",
|
"blockedDescription": "Vous pouvez afficher l'aperçu dans un onglet du navigateur séparé.",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "",
|
"totalSize": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": ""
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "",
|
||||||
|
"createFailed": ""
|
||||||
},
|
},
|
||||||
"clearToDefault": "",
|
"clearToDefault": "",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "Aide",
|
"help": "Aide",
|
||||||
"version": "Version {{version}}"
|
"version": "Version {{version}}",
|
||||||
|
"checkUpdates": "",
|
||||||
|
"helpDiscussions": ""
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "",
|
"25": "",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "",
|
||||||
|
"star": "",
|
||||||
|
"starFusion": ""
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "Confirmer l'importation",
|
"confirmImport": "Confirmer l'importation",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "Importation…",
|
"importing": "Importation…",
|
||||||
"importTitle": "Importer les paramètres",
|
"importTitle": "Importer les paramètres",
|
||||||
"loadingFile": "Chargement…",
|
"loadingFile": "Chargement…",
|
||||||
"reviewPrompt": "Vérifiez les paramètres à importer :"
|
"reviewPrompt": "Vérifiez les paramètres à importer :",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "",
|
||||||
|
"global_other": "",
|
||||||
|
"project_one": "",
|
||||||
|
"project_other": "",
|
||||||
|
"workflowSettings_one": "",
|
||||||
|
"workflowSettings_other": ""
|
||||||
|
},
|
||||||
|
"exported": "",
|
||||||
|
"exportFailed": "",
|
||||||
|
"globalSettings": "",
|
||||||
|
"imported": "",
|
||||||
|
"importFailed": "",
|
||||||
|
"importFailedDetailed": "",
|
||||||
|
"importScope": "",
|
||||||
|
"importTitleAttr": "",
|
||||||
|
"invalidJson": "",
|
||||||
|
"mergeExisting": "",
|
||||||
|
"projectSettings": "",
|
||||||
|
"replaceWarning": "",
|
||||||
|
"scopeBoth": "",
|
||||||
|
"scopeGlobal": "",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "",
|
||||||
|
"global": "",
|
||||||
|
"project": ""
|
||||||
|
},
|
||||||
|
"scopeProject": ""
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "Entrez une valeur JSON...",
|
"jsonPlaceholder": "Entrez une valeur JSON...",
|
||||||
"keepLocal": "Garder la version locale",
|
"keepLocal": "Garder la version locale",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "",
|
"searchMemoryWithQmd": "",
|
||||||
"testing": "Test en cours…",
|
"testing": "Test en cours…",
|
||||||
"testRetrieval": "Tester la récupération",
|
"testRetrieval": "Tester la récupération",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": ""
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "",
|
||||||
|
"qmdInstalled": "",
|
||||||
|
"qmdInstallFailed": "",
|
||||||
|
"qmdInstallUnavailable": ""
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "",
|
"abort": "",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": "",
|
"uRLAndQRGenerationUseTheSelectedToken": "",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
||||||
"useExisting": "Utiliser l'existant",
|
"useExisting": "Utiliser l'existant",
|
||||||
"usingQuickTunnel": ""
|
"usingQuickTunnel": "",
|
||||||
|
"installationFailed": ""
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "",
|
"advancedExternalSearchProviders": "",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "",
|
"whenEnabledTasksThatModifyTheSameFiles": "",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": ""
|
"whenTheStuckDetectorKillsAndReQueues": "",
|
||||||
|
"browseWorkspacePath": "",
|
||||||
|
"overlapPickerNote": ""
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "Ces paramètres sont partagés entre tous vos projets Fusion.",
|
"globalBanner": "Ces paramètres sont partagés entre tous vos projets Fusion.",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": "",
|
"worktrunk": "",
|
||||||
"worktrunkBinaryPath": "",
|
"worktrunkBinaryPath": "",
|
||||||
"worktrunkFailureBehavior": "",
|
"worktrunkFailureBehavior": "",
|
||||||
"worktrunkIntegration": ""
|
"worktrunkIntegration": "",
|
||||||
|
"worktreesPickerNote": ""
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "",
|
||||||
|
"projectRoot": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "Créer des tâches manuellement",
|
"withoutGitHub1": "Créer des tâches manuellement",
|
||||||
"withoutGitHub2": "Décrire le travail pour les agents IA",
|
"withoutGitHub2": "Décrire le travail pour les agents IA",
|
||||||
"withoutGitHub3": "Suivre la progression sur le tableau",
|
"withoutGitHub3": "Suivre la progression sur le tableau",
|
||||||
"withoutGitHubHeading": "Sans GitHub (disponible maintenant) :"
|
"withoutGitHubHeading": "Sans GitHub (disponible maintenant) :",
|
||||||
|
"brandLogo": "",
|
||||||
|
"brandName": "",
|
||||||
|
"setupCompleteTitle": ""
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "Actif",
|
"activePill": "Actif",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "Fusion des corrections…",
|
"mergingFixes": "Fusion des corrections…",
|
||||||
"mergingPr": "Fusion du PR…",
|
"mergingPr": "Fusion du PR…",
|
||||||
"startPrReview": "Démarrer la révision du PR",
|
"startPrReview": "Démarrer la révision du PR",
|
||||||
"statusRefreshed": "Statut du PR actualisé"
|
"statusRefreshed": "Statut du PR actualisé",
|
||||||
|
"label": ""
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "Priorité de la tâche",
|
"ariaLabel": "Priorité de la tâche",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "Créé par",
|
"createdBy": "Créé par",
|
||||||
"createdVia": "Créé via"
|
"createdVia": "Créé via",
|
||||||
|
"parentTaskOf": ""
|
||||||
},
|
},
|
||||||
"recoveryState": "État de récupération",
|
"recoveryState": "État de récupération",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "Basculer vers Markdown",
|
"switchToMarkdown": "Basculer vers Markdown",
|
||||||
"switchToPlain": "Basculer vers texte brut",
|
"switchToPlain": "Basculer vers texte brut",
|
||||||
"thinkingLevel": "",
|
"thinkingLevel": "",
|
||||||
"workflowName": ""
|
"workflowName": "",
|
||||||
|
"aggregateAdvisory": "",
|
||||||
|
"aggregateAllPassed": "",
|
||||||
|
"aggregateInProgress": "",
|
||||||
|
"aggregateNoResults": "",
|
||||||
|
"approveAndRun": "",
|
||||||
|
"approveCommandError": "",
|
||||||
|
"approving": "",
|
||||||
|
"awaitingInputTitle": "",
|
||||||
|
"cliApprovalRejectHint": "",
|
||||||
|
"cliApprovalTitle": "",
|
||||||
|
"cliApprovalWarning": "",
|
||||||
|
"customWorkflowFallback": "",
|
||||||
|
"customWorkflowLabel": "",
|
||||||
|
"defaultWorkflow": "",
|
||||||
|
"executionAwaitingCliApproval": "",
|
||||||
|
"executionAwaitingInput": "",
|
||||||
|
"executionCompleted": "",
|
||||||
|
"executionNotStarted": "",
|
||||||
|
"executionPaused": "",
|
||||||
|
"executionPostMerge": "",
|
||||||
|
"executionPreMerge": "",
|
||||||
|
"inputPlaceholder": "",
|
||||||
|
"modelDefault": "",
|
||||||
|
"postMerge": "",
|
||||||
|
"preMerge": "",
|
||||||
|
"replyInComments": "",
|
||||||
|
"resumeTaskError": "",
|
||||||
|
"resuming": "",
|
||||||
|
"statusAdvisory": "",
|
||||||
|
"statusFailed": "",
|
||||||
|
"statusPassed": "",
|
||||||
|
"statusRunning": "",
|
||||||
|
"statusSkipped": "",
|
||||||
|
"submitAndResume": "",
|
||||||
|
"submitting": "",
|
||||||
|
"waitingForOutput": ""
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "",
|
"collapsePrompt": "",
|
||||||
"editingPrompt": "",
|
"editingPrompt": "",
|
||||||
"expandPrompt": ""
|
"expandPrompt": "",
|
||||||
|
"agent": "",
|
||||||
|
"agentsLoadFailed": "",
|
||||||
|
"autoApproveRequests": "",
|
||||||
|
"autoApproveRequestsNote": "",
|
||||||
|
"cliCommandNote": "",
|
||||||
|
"cliMode": "",
|
||||||
|
"cliScript": "",
|
||||||
|
"command": "",
|
||||||
|
"executor": "",
|
||||||
|
"maxRetries": "",
|
||||||
|
"model": "",
|
||||||
|
"modelsLoadFailed": "",
|
||||||
|
"namedScript": "",
|
||||||
|
"namedScriptNote": "",
|
||||||
|
"prompt": "",
|
||||||
|
"scriptName": "",
|
||||||
|
"selectAgent": "",
|
||||||
|
"selectSkill": "",
|
||||||
|
"skill": "",
|
||||||
|
"skillsLoadFailed": "",
|
||||||
|
"skipFirstRunApproval": "",
|
||||||
|
"waitForUserInput": "",
|
||||||
|
"waitForUserInputNote": ""
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "Ajouter un champ",
|
"add": "Ajouter un champ",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "Étapes de plugin",
|
"templatesPluginSteps": "Étapes de plugin",
|
||||||
"templatesSection": "Modèles",
|
"templatesSection": "Modèles",
|
||||||
"timeoutMs": "",
|
"timeoutMs": "",
|
||||||
"trivialGraphHint": "Ce workflow ne fait qu'exécuter début → fin. Ajoutez des étapes depuis la palette ci-dessus pour le développer."
|
"trivialGraphHint": "Ce workflow ne fait qu'exécuter début → fin. Ajoutez des étapes depuis la palette ci-dessus pour le développer.",
|
||||||
|
"conditionFailure": "",
|
||||||
|
"conditionSuccess": "",
|
||||||
|
"nodeInspector": "",
|
||||||
|
"readOnlyDuplicateToEdit": ""
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "Concevoir avec l'IA",
|
"aiEdit": "Concevoir avec l'IA",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "{{count}} nœuds",
|
"templateNodeCount_other": "{{count}} nœuds",
|
||||||
"templatePickerLabel": "Partir de",
|
"templatePickerLabel": "Partir de",
|
||||||
"templateSectionBuiltin": "Workflows intégrés",
|
"templateSectionBuiltin": "Workflows intégrés",
|
||||||
"templateSectionYours": "Vos workflows"
|
"templateSectionYours": "Vos workflows",
|
||||||
|
"closeEditor": "",
|
||||||
|
"duplicatedEditable": "",
|
||||||
|
"duplicateFailed": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"noneYet": "",
|
||||||
|
"title": ""
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "",
|
"switchActiveMessage": "",
|
||||||
"switchActiveTitle": "",
|
"switchActiveTitle": "",
|
||||||
"switchCancel": "",
|
"switchCancel": "",
|
||||||
"switchConfirm": ""
|
"switchConfirm": "",
|
||||||
|
"applyFailed": "",
|
||||||
|
"defaultCleared": "",
|
||||||
|
"defaultSet": "",
|
||||||
|
"defaultWorkflowLabel": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"manage": "",
|
||||||
|
"none": ""
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Demande d'installation de Worktrunk",
|
"installRequestTitle": "Demande d'installation de Worktrunk",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "Version"
|
"version": "Version"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "",
|
||||||
|
"activeSessionHint": "",
|
||||||
|
"agentMessages": "",
|
||||||
|
"arguments": "",
|
||||||
|
"collapseChat": "",
|
||||||
|
"donePlaceholder": "",
|
||||||
|
"doneSessionHint": "",
|
||||||
|
"emptyAgentOutput": "",
|
||||||
|
"entryCount_one": "",
|
||||||
|
"entryCount_other": "",
|
||||||
|
"error": "",
|
||||||
|
"errorCount_one": "",
|
||||||
|
"errorCount_other": "",
|
||||||
|
"expandChat": "",
|
||||||
|
"idleSessionHint": "",
|
||||||
|
"jumpToLatestMessage": "",
|
||||||
|
"latest": "",
|
||||||
|
"loadingAgentOutput": "",
|
||||||
|
"loadingEarlierMessages": "",
|
||||||
|
"loadPreviousMessages": "",
|
||||||
|
"message": "",
|
||||||
|
"messageActiveAgentSession": "",
|
||||||
|
"moreTools_one": "",
|
||||||
|
"moreTools_other": "",
|
||||||
|
"result": "",
|
||||||
|
"roles": {
|
||||||
|
"agent": "",
|
||||||
|
"executor": "",
|
||||||
|
"merger": "",
|
||||||
|
"planner": "",
|
||||||
|
"reviewer": ""
|
||||||
|
},
|
||||||
|
"sending": "",
|
||||||
|
"thinking": "",
|
||||||
|
"toolCall": "",
|
||||||
|
"toolCallCount_one": "",
|
||||||
|
"toolCallCount_other": "",
|
||||||
|
"toolCallTo": "",
|
||||||
|
"toolError": "",
|
||||||
|
"toolNames": "",
|
||||||
|
"toolResult": "",
|
||||||
|
"you": "",
|
||||||
|
"youMessage": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": ""
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "",
|
"trendDeclining": "",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "",
|
"offline": "",
|
||||||
"online": ""
|
"online": ""
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": ""
|
"unknown": ""
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "",
|
"refreshSourceInitialLoad": "",
|
||||||
"refreshSourceManual": ""
|
"refreshSourceManual": ""
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "",
|
|
||||||
"aggregateInProgress": "",
|
|
||||||
"aggregateNoResults": "",
|
|
||||||
"customWorkflowFallback": "",
|
|
||||||
"defaultWorkflow": "",
|
|
||||||
"executionAwaitingCliApproval": "",
|
|
||||||
"executionAwaitingInput": "",
|
|
||||||
"executionCompleted": "",
|
|
||||||
"executionNotStarted": "",
|
|
||||||
"executionPaused": "",
|
|
||||||
"executionPostMerge": "",
|
|
||||||
"executionPreMerge": "",
|
|
||||||
"postMerge": "",
|
|
||||||
"preMerge": "",
|
|
||||||
"statusAdvisory": "",
|
|
||||||
"statusFailed": "",
|
|
||||||
"statusPassed": "",
|
|
||||||
"statusRunning": "",
|
|
||||||
"statusSkipped": "",
|
|
||||||
"waitingForOutput": ""
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "",
|
"summaryAwaitInput": "",
|
||||||
"summaryCodeDefault": "",
|
"summaryCodeDefault": "",
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": ""
|
"workflowMismatch": ""
|
||||||
},
|
},
|
||||||
"todo": "할 일",
|
"todo": "할 일",
|
||||||
"triage": "트리아지"
|
"triage": "트리아지",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "",
|
"abandonGroup": "",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "댓글",
|
"heading": "댓글",
|
||||||
"placeholder": "댓글 추가",
|
"placeholder": "댓글 추가",
|
||||||
"postingButton": "게시 중…",
|
"postingButton": "게시 중…",
|
||||||
"updatedSuccess": "댓글이 업데이트되었습니다"
|
"updatedSuccess": "댓글이 업데이트되었습니다",
|
||||||
|
"aiGuidance": ""
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "",
|
"filesChanged_one": "",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "사전 확인 다시 실행",
|
"rerunPreflight": "사전 확인 다시 실행",
|
||||||
"revertToAi": "AI 버전으로 되돌리기",
|
"revertToAi": "AI 버전으로 되돌리기",
|
||||||
"titleLabel": "제목",
|
"titleLabel": "제목",
|
||||||
"usingTemplate": "<code>.github/pull_request_template.md</code> 템플릿 사용 중"
|
"usingTemplate": "<code>.github/pull_request_template.md</code> 템플릿 사용 중",
|
||||||
|
"assignees": "",
|
||||||
|
"dismissConflictResolutionError": "",
|
||||||
|
"dismissPushBranchError": "",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "",
|
||||||
|
"actionRun": "",
|
||||||
|
"docs": ""
|
||||||
|
},
|
||||||
|
"generatingBody": "",
|
||||||
|
"generatingTitle": "",
|
||||||
|
"labels": "",
|
||||||
|
"loadingOptions": "",
|
||||||
|
"loadingPreflight": "",
|
||||||
|
"noChangedFiles": "",
|
||||||
|
"noCommits": "",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"reviewers": "",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "",
|
||||||
|
"agentReplyFix": "",
|
||||||
|
"approve": "",
|
||||||
|
"autoMerge": "",
|
||||||
|
"close": "",
|
||||||
|
"confirmMerge": "",
|
||||||
|
"creating": "",
|
||||||
|
"creationFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"merge": "",
|
||||||
|
"mergeableLabel": "",
|
||||||
|
"mergeDisabledUntilVerified": "",
|
||||||
|
"none": "",
|
||||||
|
"noReviewThreads": "",
|
||||||
|
"requestRetry": "",
|
||||||
|
"resolveConflictsBeforeMerge": "",
|
||||||
|
"resolveConflictsOnGithub": "",
|
||||||
|
"responseAlreadyInProgress": "",
|
||||||
|
"responsePending_one": "",
|
||||||
|
"responsePending_other": "",
|
||||||
|
"retryCreation": "",
|
||||||
|
"reviewLabel": "",
|
||||||
|
"threadFixed": "",
|
||||||
|
"threadPending": "",
|
||||||
|
"verifyingGithub": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "별도의 브라우저 탭에서 미리보기를 볼 수 있습니다.",
|
"blockedDescription": "별도의 브라우저 탭에서 미리보기를 볼 수 있습니다.",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "",
|
"totalSize": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": ""
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "",
|
||||||
|
"createFailed": ""
|
||||||
},
|
},
|
||||||
"clearToDefault": "",
|
"clearToDefault": "",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "도움말",
|
"help": "도움말",
|
||||||
"version": "버전 {{version}}"
|
"version": "버전 {{version}}",
|
||||||
|
"checkUpdates": "",
|
||||||
|
"helpDiscussions": ""
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "",
|
"25": "",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "",
|
||||||
|
"star": "",
|
||||||
|
"starFusion": ""
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "가져오기 확인",
|
"confirmImport": "가져오기 확인",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "가져오는 중…",
|
"importing": "가져오는 중…",
|
||||||
"importTitle": "설정 가져오기",
|
"importTitle": "설정 가져오기",
|
||||||
"loadingFile": "불러오는 중…",
|
"loadingFile": "불러오는 중…",
|
||||||
"reviewPrompt": "가져올 설정을 검토하세요:"
|
"reviewPrompt": "가져올 설정을 검토하세요:",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "",
|
||||||
|
"global_other": "",
|
||||||
|
"project_one": "",
|
||||||
|
"project_other": "",
|
||||||
|
"workflowSettings_one": "",
|
||||||
|
"workflowSettings_other": ""
|
||||||
|
},
|
||||||
|
"exported": "",
|
||||||
|
"exportFailed": "",
|
||||||
|
"globalSettings": "",
|
||||||
|
"imported": "",
|
||||||
|
"importFailed": "",
|
||||||
|
"importFailedDetailed": "",
|
||||||
|
"importScope": "",
|
||||||
|
"importTitleAttr": "",
|
||||||
|
"invalidJson": "",
|
||||||
|
"mergeExisting": "",
|
||||||
|
"projectSettings": "",
|
||||||
|
"replaceWarning": "",
|
||||||
|
"scopeBoth": "",
|
||||||
|
"scopeGlobal": "",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "",
|
||||||
|
"global": "",
|
||||||
|
"project": ""
|
||||||
|
},
|
||||||
|
"scopeProject": ""
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "JSON 값을 입력하세요...",
|
"jsonPlaceholder": "JSON 값을 입력하세요...",
|
||||||
"keepLocal": "로컬 유지",
|
"keepLocal": "로컬 유지",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "",
|
"searchMemoryWithQmd": "",
|
||||||
"testing": "테스트 중…",
|
"testing": "테스트 중…",
|
||||||
"testRetrieval": "검색 테스트",
|
"testRetrieval": "검색 테스트",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": ""
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "",
|
||||||
|
"qmdInstalled": "",
|
||||||
|
"qmdInstallFailed": "",
|
||||||
|
"qmdInstallUnavailable": ""
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "",
|
"abort": "",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": "",
|
"uRLAndQRGenerationUseTheSelectedToken": "",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
||||||
"useExisting": "기존 사용",
|
"useExisting": "기존 사용",
|
||||||
"usingQuickTunnel": ""
|
"usingQuickTunnel": "",
|
||||||
|
"installationFailed": ""
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "",
|
"advancedExternalSearchProviders": "",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "",
|
"whenEnabledTasksThatModifyTheSameFiles": "",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": ""
|
"whenTheStuckDetectorKillsAndReQueues": "",
|
||||||
|
"browseWorkspacePath": "",
|
||||||
|
"overlapPickerNote": ""
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "이 설정은 모든 Fusion 프로젝트에서 공유됩니다.",
|
"globalBanner": "이 설정은 모든 Fusion 프로젝트에서 공유됩니다.",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": "",
|
"worktrunk": "",
|
||||||
"worktrunkBinaryPath": "",
|
"worktrunkBinaryPath": "",
|
||||||
"worktrunkFailureBehavior": "",
|
"worktrunkFailureBehavior": "",
|
||||||
"worktrunkIntegration": ""
|
"worktrunkIntegration": "",
|
||||||
|
"worktreesPickerNote": ""
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "",
|
||||||
|
"projectRoot": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "작업 수동 생성",
|
"withoutGitHub1": "작업 수동 생성",
|
||||||
"withoutGitHub2": "AI 에이전트를 위한 작업 설명",
|
"withoutGitHub2": "AI 에이전트를 위한 작업 설명",
|
||||||
"withoutGitHub3": "보드에서 진행 상황 추적",
|
"withoutGitHub3": "보드에서 진행 상황 추적",
|
||||||
"withoutGitHubHeading": "GitHub 없이 (지금 사용 가능):"
|
"withoutGitHubHeading": "GitHub 없이 (지금 사용 가능):",
|
||||||
|
"brandLogo": "",
|
||||||
|
"brandName": "",
|
||||||
|
"setupCompleteTitle": ""
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "활성",
|
"activePill": "활성",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "수정 사항 병합 중…",
|
"mergingFixes": "수정 사항 병합 중…",
|
||||||
"mergingPr": "PR 병합 중…",
|
"mergingPr": "PR 병합 중…",
|
||||||
"startPrReview": "PR 검토 시작",
|
"startPrReview": "PR 검토 시작",
|
||||||
"statusRefreshed": "PR 상태가 새로 고침되었습니다"
|
"statusRefreshed": "PR 상태가 새로 고침되었습니다",
|
||||||
|
"label": ""
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "작업 우선순위",
|
"ariaLabel": "작업 우선순위",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "작성자",
|
"createdBy": "작성자",
|
||||||
"createdVia": "생성 경로"
|
"createdVia": "생성 경로",
|
||||||
|
"parentTaskOf": ""
|
||||||
},
|
},
|
||||||
"recoveryState": "복구 상태",
|
"recoveryState": "복구 상태",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "Markdown으로 전환",
|
"switchToMarkdown": "Markdown으로 전환",
|
||||||
"switchToPlain": "일반 텍스트로 전환",
|
"switchToPlain": "일반 텍스트로 전환",
|
||||||
"thinkingLevel": "",
|
"thinkingLevel": "",
|
||||||
"workflowName": ""
|
"workflowName": "",
|
||||||
|
"aggregateAdvisory": "",
|
||||||
|
"aggregateAllPassed": "",
|
||||||
|
"aggregateInProgress": "",
|
||||||
|
"aggregateNoResults": "",
|
||||||
|
"approveAndRun": "",
|
||||||
|
"approveCommandError": "",
|
||||||
|
"approving": "",
|
||||||
|
"awaitingInputTitle": "",
|
||||||
|
"cliApprovalRejectHint": "",
|
||||||
|
"cliApprovalTitle": "",
|
||||||
|
"cliApprovalWarning": "",
|
||||||
|
"customWorkflowFallback": "",
|
||||||
|
"customWorkflowLabel": "",
|
||||||
|
"defaultWorkflow": "",
|
||||||
|
"executionAwaitingCliApproval": "",
|
||||||
|
"executionAwaitingInput": "",
|
||||||
|
"executionCompleted": "",
|
||||||
|
"executionNotStarted": "",
|
||||||
|
"executionPaused": "",
|
||||||
|
"executionPostMerge": "",
|
||||||
|
"executionPreMerge": "",
|
||||||
|
"inputPlaceholder": "",
|
||||||
|
"modelDefault": "",
|
||||||
|
"postMerge": "",
|
||||||
|
"preMerge": "",
|
||||||
|
"replyInComments": "",
|
||||||
|
"resumeTaskError": "",
|
||||||
|
"resuming": "",
|
||||||
|
"statusAdvisory": "",
|
||||||
|
"statusFailed": "",
|
||||||
|
"statusPassed": "",
|
||||||
|
"statusRunning": "",
|
||||||
|
"statusSkipped": "",
|
||||||
|
"submitAndResume": "",
|
||||||
|
"submitting": "",
|
||||||
|
"waitingForOutput": ""
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "",
|
"collapsePrompt": "",
|
||||||
"editingPrompt": "",
|
"editingPrompt": "",
|
||||||
"expandPrompt": ""
|
"expandPrompt": "",
|
||||||
|
"agent": "",
|
||||||
|
"agentsLoadFailed": "",
|
||||||
|
"autoApproveRequests": "",
|
||||||
|
"autoApproveRequestsNote": "",
|
||||||
|
"cliCommandNote": "",
|
||||||
|
"cliMode": "",
|
||||||
|
"cliScript": "",
|
||||||
|
"command": "",
|
||||||
|
"executor": "",
|
||||||
|
"maxRetries": "",
|
||||||
|
"model": "",
|
||||||
|
"modelsLoadFailed": "",
|
||||||
|
"namedScript": "",
|
||||||
|
"namedScriptNote": "",
|
||||||
|
"prompt": "",
|
||||||
|
"scriptName": "",
|
||||||
|
"selectAgent": "",
|
||||||
|
"selectSkill": "",
|
||||||
|
"skill": "",
|
||||||
|
"skillsLoadFailed": "",
|
||||||
|
"skipFirstRunApproval": "",
|
||||||
|
"waitForUserInput": "",
|
||||||
|
"waitForUserInputNote": ""
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "필드 추가",
|
"add": "필드 추가",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "플러그인 단계",
|
"templatesPluginSteps": "플러그인 단계",
|
||||||
"templatesSection": "템플릿",
|
"templatesSection": "템플릿",
|
||||||
"timeoutMs": "",
|
"timeoutMs": "",
|
||||||
"trivialGraphHint": "이 워크플로는 시작 → 끝만 실행합니다. 위 팔레트에서 단계를 추가하여 구성하세요."
|
"trivialGraphHint": "이 워크플로는 시작 → 끝만 실행합니다. 위 팔레트에서 단계를 추가하여 구성하세요.",
|
||||||
|
"conditionFailure": "",
|
||||||
|
"conditionSuccess": "",
|
||||||
|
"nodeInspector": "",
|
||||||
|
"readOnlyDuplicateToEdit": ""
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "AI로 디자인",
|
"aiEdit": "AI로 디자인",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "노드 {{count}}개",
|
"templateNodeCount_other": "노드 {{count}}개",
|
||||||
"templatePickerLabel": "시작점",
|
"templatePickerLabel": "시작점",
|
||||||
"templateSectionBuiltin": "기본 제공 워크플로",
|
"templateSectionBuiltin": "기본 제공 워크플로",
|
||||||
"templateSectionYours": "내 워크플로"
|
"templateSectionYours": "내 워크플로",
|
||||||
|
"closeEditor": "",
|
||||||
|
"duplicatedEditable": "",
|
||||||
|
"duplicateFailed": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"noneYet": "",
|
||||||
|
"title": ""
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "",
|
"switchActiveMessage": "",
|
||||||
"switchActiveTitle": "",
|
"switchActiveTitle": "",
|
||||||
"switchCancel": "",
|
"switchCancel": "",
|
||||||
"switchConfirm": ""
|
"switchConfirm": "",
|
||||||
|
"applyFailed": "",
|
||||||
|
"defaultCleared": "",
|
||||||
|
"defaultSet": "",
|
||||||
|
"defaultWorkflowLabel": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"manage": "",
|
||||||
|
"none": ""
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Worktrunk 설치 요청",
|
"installRequestTitle": "Worktrunk 설치 요청",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "버전"
|
"version": "버전"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "",
|
||||||
|
"activeSessionHint": "",
|
||||||
|
"agentMessages": "",
|
||||||
|
"arguments": "",
|
||||||
|
"collapseChat": "",
|
||||||
|
"donePlaceholder": "",
|
||||||
|
"doneSessionHint": "",
|
||||||
|
"emptyAgentOutput": "",
|
||||||
|
"entryCount_one": "",
|
||||||
|
"entryCount_other": "",
|
||||||
|
"error": "",
|
||||||
|
"errorCount_one": "",
|
||||||
|
"errorCount_other": "",
|
||||||
|
"expandChat": "",
|
||||||
|
"idleSessionHint": "",
|
||||||
|
"jumpToLatestMessage": "",
|
||||||
|
"latest": "",
|
||||||
|
"loadingAgentOutput": "",
|
||||||
|
"loadingEarlierMessages": "",
|
||||||
|
"loadPreviousMessages": "",
|
||||||
|
"message": "",
|
||||||
|
"messageActiveAgentSession": "",
|
||||||
|
"moreTools_one": "",
|
||||||
|
"moreTools_other": "",
|
||||||
|
"result": "",
|
||||||
|
"roles": {
|
||||||
|
"agent": "",
|
||||||
|
"executor": "",
|
||||||
|
"merger": "",
|
||||||
|
"planner": "",
|
||||||
|
"reviewer": ""
|
||||||
|
},
|
||||||
|
"sending": "",
|
||||||
|
"thinking": "",
|
||||||
|
"toolCall": "",
|
||||||
|
"toolCallCount_one": "",
|
||||||
|
"toolCallCount_other": "",
|
||||||
|
"toolCallTo": "",
|
||||||
|
"toolError": "",
|
||||||
|
"toolNames": "",
|
||||||
|
"toolResult": "",
|
||||||
|
"you": "",
|
||||||
|
"youMessage": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": ""
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "",
|
"trendDeclining": "",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "",
|
"offline": "",
|
||||||
"online": ""
|
"online": ""
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": ""
|
"unknown": ""
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "",
|
"refreshSourceInitialLoad": "",
|
||||||
"refreshSourceManual": ""
|
"refreshSourceManual": ""
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "",
|
|
||||||
"aggregateInProgress": "",
|
|
||||||
"aggregateNoResults": "",
|
|
||||||
"customWorkflowFallback": "",
|
|
||||||
"defaultWorkflow": "",
|
|
||||||
"executionAwaitingCliApproval": "",
|
|
||||||
"executionAwaitingInput": "",
|
|
||||||
"executionCompleted": "",
|
|
||||||
"executionNotStarted": "",
|
|
||||||
"executionPaused": "",
|
|
||||||
"executionPostMerge": "",
|
|
||||||
"executionPreMerge": "",
|
|
||||||
"postMerge": "",
|
|
||||||
"preMerge": "",
|
|
||||||
"statusAdvisory": "",
|
|
||||||
"statusFailed": "",
|
|
||||||
"statusPassed": "",
|
|
||||||
"statusRunning": "",
|
|
||||||
"statusSkipped": "",
|
|
||||||
"waitingForOutput": ""
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "",
|
"summaryAwaitInput": "",
|
||||||
"summaryCodeDefault": "",
|
"summaryCodeDefault": "",
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": ""
|
"workflowMismatch": ""
|
||||||
},
|
},
|
||||||
"todo": "待办",
|
"todo": "待办",
|
||||||
"triage": "分诊"
|
"triage": "分诊",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "",
|
"abandonGroup": "",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "评论",
|
"heading": "评论",
|
||||||
"placeholder": "添加评论",
|
"placeholder": "添加评论",
|
||||||
"postingButton": "发布中…",
|
"postingButton": "发布中…",
|
||||||
"updatedSuccess": "评论已更新"
|
"updatedSuccess": "评论已更新",
|
||||||
|
"aiGuidance": ""
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "",
|
"filesChanged_one": "",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "重新运行飞行前检查",
|
"rerunPreflight": "重新运行飞行前检查",
|
||||||
"revertToAi": "恢复为 AI 版本",
|
"revertToAi": "恢复为 AI 版本",
|
||||||
"titleLabel": "标题",
|
"titleLabel": "标题",
|
||||||
"usingTemplate": "使用 <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "使用 <code>.github/pull_request_template.md</code>",
|
||||||
|
"assignees": "",
|
||||||
|
"dismissConflictResolutionError": "",
|
||||||
|
"dismissPushBranchError": "",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "",
|
||||||
|
"actionRun": "",
|
||||||
|
"docs": ""
|
||||||
|
},
|
||||||
|
"generatingBody": "",
|
||||||
|
"generatingTitle": "",
|
||||||
|
"labels": "",
|
||||||
|
"loadingOptions": "",
|
||||||
|
"loadingPreflight": "",
|
||||||
|
"noChangedFiles": "",
|
||||||
|
"noCommits": "",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"reviewers": "",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "",
|
||||||
|
"agentReplyFix": "",
|
||||||
|
"approve": "",
|
||||||
|
"autoMerge": "",
|
||||||
|
"close": "",
|
||||||
|
"confirmMerge": "",
|
||||||
|
"creating": "",
|
||||||
|
"creationFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"merge": "",
|
||||||
|
"mergeableLabel": "",
|
||||||
|
"mergeDisabledUntilVerified": "",
|
||||||
|
"none": "",
|
||||||
|
"noReviewThreads": "",
|
||||||
|
"requestRetry": "",
|
||||||
|
"resolveConflictsBeforeMerge": "",
|
||||||
|
"resolveConflictsOnGithub": "",
|
||||||
|
"responseAlreadyInProgress": "",
|
||||||
|
"responsePending_one": "",
|
||||||
|
"responsePending_other": "",
|
||||||
|
"retryCreation": "",
|
||||||
|
"reviewLabel": "",
|
||||||
|
"threadFixed": "",
|
||||||
|
"threadPending": "",
|
||||||
|
"verifyingGithub": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "您可以在单独的浏览器标签页中查看预览。",
|
"blockedDescription": "您可以在单独的浏览器标签页中查看预览。",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "",
|
"totalSize": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": ""
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "",
|
||||||
|
"createFailed": ""
|
||||||
},
|
},
|
||||||
"clearToDefault": "",
|
"clearToDefault": "",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "帮助",
|
"help": "帮助",
|
||||||
"version": "版本 {{version}}"
|
"version": "版本 {{version}}",
|
||||||
|
"checkUpdates": "",
|
||||||
|
"helpDiscussions": ""
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "",
|
"25": "",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "",
|
||||||
|
"star": "",
|
||||||
|
"starFusion": ""
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "确认导入",
|
"confirmImport": "确认导入",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "导入中…",
|
"importing": "导入中…",
|
||||||
"importTitle": "导入设置",
|
"importTitle": "导入设置",
|
||||||
"loadingFile": "加载中…",
|
"loadingFile": "加载中…",
|
||||||
"reviewPrompt": "查看要导入的设置:"
|
"reviewPrompt": "查看要导入的设置:",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "",
|
||||||
|
"global_other": "",
|
||||||
|
"project_one": "",
|
||||||
|
"project_other": "",
|
||||||
|
"workflowSettings_one": "",
|
||||||
|
"workflowSettings_other": ""
|
||||||
|
},
|
||||||
|
"exported": "",
|
||||||
|
"exportFailed": "",
|
||||||
|
"globalSettings": "",
|
||||||
|
"imported": "",
|
||||||
|
"importFailed": "",
|
||||||
|
"importFailedDetailed": "",
|
||||||
|
"importScope": "",
|
||||||
|
"importTitleAttr": "",
|
||||||
|
"invalidJson": "",
|
||||||
|
"mergeExisting": "",
|
||||||
|
"projectSettings": "",
|
||||||
|
"replaceWarning": "",
|
||||||
|
"scopeBoth": "",
|
||||||
|
"scopeGlobal": "",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "",
|
||||||
|
"global": "",
|
||||||
|
"project": ""
|
||||||
|
},
|
||||||
|
"scopeProject": ""
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "输入 JSON 值...",
|
"jsonPlaceholder": "输入 JSON 值...",
|
||||||
"keepLocal": "保留本地",
|
"keepLocal": "保留本地",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "",
|
"searchMemoryWithQmd": "",
|
||||||
"testing": "测试中…",
|
"testing": "测试中…",
|
||||||
"testRetrieval": "测试检索",
|
"testRetrieval": "测试检索",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": ""
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "",
|
||||||
|
"qmdInstalled": "",
|
||||||
|
"qmdInstallFailed": "",
|
||||||
|
"qmdInstallUnavailable": ""
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "",
|
"abort": "",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": "",
|
"uRLAndQRGenerationUseTheSelectedToken": "",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
||||||
"useExisting": "使用现有",
|
"useExisting": "使用现有",
|
||||||
"usingQuickTunnel": ""
|
"usingQuickTunnel": "",
|
||||||
|
"installationFailed": ""
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "",
|
"advancedExternalSearchProviders": "",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "",
|
"whenEnabledTasksThatModifyTheSameFiles": "",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": ""
|
"whenTheStuckDetectorKillsAndReQueues": "",
|
||||||
|
"browseWorkspacePath": "",
|
||||||
|
"overlapPickerNote": ""
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "这些设置在所有 Fusion 项目中共享。",
|
"globalBanner": "这些设置在所有 Fusion 项目中共享。",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": "",
|
"worktrunk": "",
|
||||||
"worktrunkBinaryPath": "",
|
"worktrunkBinaryPath": "",
|
||||||
"worktrunkFailureBehavior": "",
|
"worktrunkFailureBehavior": "",
|
||||||
"worktrunkIntegration": ""
|
"worktrunkIntegration": "",
|
||||||
|
"worktreesPickerNote": ""
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "",
|
||||||
|
"projectRoot": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "手动创建任务",
|
"withoutGitHub1": "手动创建任务",
|
||||||
"withoutGitHub2": "为 AI 代理描述工作",
|
"withoutGitHub2": "为 AI 代理描述工作",
|
||||||
"withoutGitHub3": "在看板上跟踪进度",
|
"withoutGitHub3": "在看板上跟踪进度",
|
||||||
"withoutGitHubHeading": "不使用 GitHub(现在可用):"
|
"withoutGitHubHeading": "不使用 GitHub(现在可用):",
|
||||||
|
"brandLogo": "",
|
||||||
|
"brandName": "",
|
||||||
|
"setupCompleteTitle": ""
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "活跃",
|
"activePill": "活跃",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "正在合并修复…",
|
"mergingFixes": "正在合并修复…",
|
||||||
"mergingPr": "正在合并 PR…",
|
"mergingPr": "正在合并 PR…",
|
||||||
"startPrReview": "开始 PR 审查",
|
"startPrReview": "开始 PR 审查",
|
||||||
"statusRefreshed": "PR 状态已刷新"
|
"statusRefreshed": "PR 状态已刷新",
|
||||||
|
"label": ""
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "任务优先级",
|
"ariaLabel": "任务优先级",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "创建者:",
|
"createdBy": "创建者:",
|
||||||
"createdVia": "通过…创建"
|
"createdVia": "通过…创建",
|
||||||
|
"parentTaskOf": ""
|
||||||
},
|
},
|
||||||
"recoveryState": "恢复状态",
|
"recoveryState": "恢复状态",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "切换到 Markdown",
|
"switchToMarkdown": "切换到 Markdown",
|
||||||
"switchToPlain": "切换到纯文本",
|
"switchToPlain": "切换到纯文本",
|
||||||
"thinkingLevel": "",
|
"thinkingLevel": "",
|
||||||
"workflowName": ""
|
"workflowName": "",
|
||||||
|
"aggregateAdvisory": "",
|
||||||
|
"aggregateAllPassed": "",
|
||||||
|
"aggregateInProgress": "",
|
||||||
|
"aggregateNoResults": "",
|
||||||
|
"approveAndRun": "",
|
||||||
|
"approveCommandError": "",
|
||||||
|
"approving": "",
|
||||||
|
"awaitingInputTitle": "",
|
||||||
|
"cliApprovalRejectHint": "",
|
||||||
|
"cliApprovalTitle": "",
|
||||||
|
"cliApprovalWarning": "",
|
||||||
|
"customWorkflowFallback": "",
|
||||||
|
"customWorkflowLabel": "",
|
||||||
|
"defaultWorkflow": "",
|
||||||
|
"executionAwaitingCliApproval": "",
|
||||||
|
"executionAwaitingInput": "",
|
||||||
|
"executionCompleted": "",
|
||||||
|
"executionNotStarted": "",
|
||||||
|
"executionPaused": "",
|
||||||
|
"executionPostMerge": "",
|
||||||
|
"executionPreMerge": "",
|
||||||
|
"inputPlaceholder": "",
|
||||||
|
"modelDefault": "",
|
||||||
|
"postMerge": "",
|
||||||
|
"preMerge": "",
|
||||||
|
"replyInComments": "",
|
||||||
|
"resumeTaskError": "",
|
||||||
|
"resuming": "",
|
||||||
|
"statusAdvisory": "",
|
||||||
|
"statusFailed": "",
|
||||||
|
"statusPassed": "",
|
||||||
|
"statusRunning": "",
|
||||||
|
"statusSkipped": "",
|
||||||
|
"submitAndResume": "",
|
||||||
|
"submitting": "",
|
||||||
|
"waitingForOutput": ""
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "",
|
"collapsePrompt": "",
|
||||||
"editingPrompt": "",
|
"editingPrompt": "",
|
||||||
"expandPrompt": ""
|
"expandPrompt": "",
|
||||||
|
"agent": "",
|
||||||
|
"agentsLoadFailed": "",
|
||||||
|
"autoApproveRequests": "",
|
||||||
|
"autoApproveRequestsNote": "",
|
||||||
|
"cliCommandNote": "",
|
||||||
|
"cliMode": "",
|
||||||
|
"cliScript": "",
|
||||||
|
"command": "",
|
||||||
|
"executor": "",
|
||||||
|
"maxRetries": "",
|
||||||
|
"model": "",
|
||||||
|
"modelsLoadFailed": "",
|
||||||
|
"namedScript": "",
|
||||||
|
"namedScriptNote": "",
|
||||||
|
"prompt": "",
|
||||||
|
"scriptName": "",
|
||||||
|
"selectAgent": "",
|
||||||
|
"selectSkill": "",
|
||||||
|
"skill": "",
|
||||||
|
"skillsLoadFailed": "",
|
||||||
|
"skipFirstRunApproval": "",
|
||||||
|
"waitForUserInput": "",
|
||||||
|
"waitForUserInputNote": ""
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "添加字段",
|
"add": "添加字段",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "插件步骤",
|
"templatesPluginSteps": "插件步骤",
|
||||||
"templatesSection": "模板",
|
"templatesSection": "模板",
|
||||||
"timeoutMs": "",
|
"timeoutMs": "",
|
||||||
"trivialGraphHint": "此工作流仅运行开始→结束。从上方面板添加步骤以构建工作流。"
|
"trivialGraphHint": "此工作流仅运行开始→结束。从上方面板添加步骤以构建工作流。",
|
||||||
|
"conditionFailure": "",
|
||||||
|
"conditionSuccess": "",
|
||||||
|
"nodeInspector": "",
|
||||||
|
"readOnlyDuplicateToEdit": ""
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "用 AI 设计",
|
"aiEdit": "用 AI 设计",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "{{count}} 个节点",
|
"templateNodeCount_other": "{{count}} 个节点",
|
||||||
"templatePickerLabel": "从…开始",
|
"templatePickerLabel": "从…开始",
|
||||||
"templateSectionBuiltin": "内置工作流",
|
"templateSectionBuiltin": "内置工作流",
|
||||||
"templateSectionYours": "我的工作流"
|
"templateSectionYours": "我的工作流",
|
||||||
|
"closeEditor": "",
|
||||||
|
"duplicatedEditable": "",
|
||||||
|
"duplicateFailed": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"noneYet": "",
|
||||||
|
"title": ""
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "",
|
"switchActiveMessage": "",
|
||||||
"switchActiveTitle": "",
|
"switchActiveTitle": "",
|
||||||
"switchCancel": "",
|
"switchCancel": "",
|
||||||
"switchConfirm": ""
|
"switchConfirm": "",
|
||||||
|
"applyFailed": "",
|
||||||
|
"defaultCleared": "",
|
||||||
|
"defaultSet": "",
|
||||||
|
"defaultWorkflowLabel": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"manage": "",
|
||||||
|
"none": ""
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Worktrunk 安装请求",
|
"installRequestTitle": "Worktrunk 安装请求",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "版本"
|
"version": "版本"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "",
|
||||||
|
"activeSessionHint": "",
|
||||||
|
"agentMessages": "",
|
||||||
|
"arguments": "",
|
||||||
|
"collapseChat": "",
|
||||||
|
"donePlaceholder": "",
|
||||||
|
"doneSessionHint": "",
|
||||||
|
"emptyAgentOutput": "",
|
||||||
|
"entryCount_one": "",
|
||||||
|
"entryCount_other": "",
|
||||||
|
"error": "",
|
||||||
|
"errorCount_one": "",
|
||||||
|
"errorCount_other": "",
|
||||||
|
"expandChat": "",
|
||||||
|
"idleSessionHint": "",
|
||||||
|
"jumpToLatestMessage": "",
|
||||||
|
"latest": "",
|
||||||
|
"loadingAgentOutput": "",
|
||||||
|
"loadingEarlierMessages": "",
|
||||||
|
"loadPreviousMessages": "",
|
||||||
|
"message": "",
|
||||||
|
"messageActiveAgentSession": "",
|
||||||
|
"moreTools_one": "",
|
||||||
|
"moreTools_other": "",
|
||||||
|
"result": "",
|
||||||
|
"roles": {
|
||||||
|
"agent": "",
|
||||||
|
"executor": "",
|
||||||
|
"merger": "",
|
||||||
|
"planner": "",
|
||||||
|
"reviewer": ""
|
||||||
|
},
|
||||||
|
"sending": "",
|
||||||
|
"thinking": "",
|
||||||
|
"toolCall": "",
|
||||||
|
"toolCallCount_one": "",
|
||||||
|
"toolCallCount_other": "",
|
||||||
|
"toolCallTo": "",
|
||||||
|
"toolError": "",
|
||||||
|
"toolNames": "",
|
||||||
|
"toolResult": "",
|
||||||
|
"you": "",
|
||||||
|
"youMessage": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": ""
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "",
|
"trendDeclining": "",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "",
|
"offline": "",
|
||||||
"online": ""
|
"online": ""
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": ""
|
"unknown": ""
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "",
|
"refreshSourceInitialLoad": "",
|
||||||
"refreshSourceManual": ""
|
"refreshSourceManual": ""
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "",
|
|
||||||
"aggregateInProgress": "",
|
|
||||||
"aggregateNoResults": "",
|
|
||||||
"customWorkflowFallback": "",
|
|
||||||
"defaultWorkflow": "",
|
|
||||||
"executionAwaitingCliApproval": "",
|
|
||||||
"executionAwaitingInput": "",
|
|
||||||
"executionCompleted": "",
|
|
||||||
"executionNotStarted": "",
|
|
||||||
"executionPaused": "",
|
|
||||||
"executionPostMerge": "",
|
|
||||||
"executionPreMerge": "",
|
|
||||||
"postMerge": "",
|
|
||||||
"preMerge": "",
|
|
||||||
"statusAdvisory": "",
|
|
||||||
"statusFailed": "",
|
|
||||||
"statusPassed": "",
|
|
||||||
"statusRunning": "",
|
|
||||||
"statusSkipped": "",
|
|
||||||
"waitingForOutput": ""
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "",
|
"summaryAwaitInput": "",
|
||||||
"summaryCodeDefault": "",
|
"summaryCodeDefault": "",
|
||||||
|
|||||||
@@ -1181,7 +1181,11 @@
|
|||||||
"workflowMismatch": ""
|
"workflowMismatch": ""
|
||||||
},
|
},
|
||||||
"todo": "待辦",
|
"todo": "待辦",
|
||||||
"triage": "分診"
|
"triage": "分診",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "",
|
"abandonGroup": "",
|
||||||
@@ -1810,7 +1814,8 @@
|
|||||||
"heading": "評論",
|
"heading": "評論",
|
||||||
"placeholder": "新增評論",
|
"placeholder": "新增評論",
|
||||||
"postingButton": "發佈中…",
|
"postingButton": "發佈中…",
|
||||||
"updatedSuccess": "評論已更新"
|
"updatedSuccess": "評論已更新",
|
||||||
|
"aiGuidance": ""
|
||||||
},
|
},
|
||||||
"commit": {
|
"commit": {
|
||||||
"filesChanged_one": "",
|
"filesChanged_one": "",
|
||||||
@@ -4686,7 +4691,60 @@
|
|||||||
"rerunPreflight": "重新執行飛行前檢查",
|
"rerunPreflight": "重新執行飛行前檢查",
|
||||||
"revertToAi": "恢復為 AI 版本",
|
"revertToAi": "恢復為 AI 版本",
|
||||||
"titleLabel": "標題",
|
"titleLabel": "標題",
|
||||||
"usingTemplate": "使用 <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "使用 <code>.github/pull_request_template.md</code>",
|
||||||
|
"assignees": "",
|
||||||
|
"dismissConflictResolutionError": "",
|
||||||
|
"dismissPushBranchError": "",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "",
|
||||||
|
"actionRun": "",
|
||||||
|
"docs": ""
|
||||||
|
},
|
||||||
|
"generatingBody": "",
|
||||||
|
"generatingTitle": "",
|
||||||
|
"labels": "",
|
||||||
|
"loadingOptions": "",
|
||||||
|
"loadingPreflight": "",
|
||||||
|
"noChangedFiles": "",
|
||||||
|
"noCommits": "",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "",
|
||||||
|
"message": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"reviewers": "",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "",
|
||||||
|
"agentReplyFix": "",
|
||||||
|
"approve": "",
|
||||||
|
"autoMerge": "",
|
||||||
|
"close": "",
|
||||||
|
"confirmMerge": "",
|
||||||
|
"creating": "",
|
||||||
|
"creationFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"merge": "",
|
||||||
|
"mergeableLabel": "",
|
||||||
|
"mergeDisabledUntilVerified": "",
|
||||||
|
"none": "",
|
||||||
|
"noReviewThreads": "",
|
||||||
|
"requestRetry": "",
|
||||||
|
"resolveConflictsBeforeMerge": "",
|
||||||
|
"resolveConflictsOnGithub": "",
|
||||||
|
"responseAlreadyInProgress": "",
|
||||||
|
"responsePending_one": "",
|
||||||
|
"responsePending_other": "",
|
||||||
|
"retryCreation": "",
|
||||||
|
"reviewLabel": "",
|
||||||
|
"threadFixed": "",
|
||||||
|
"threadPending": "",
|
||||||
|
"verifyingGithub": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "您可以在單獨的瀏覽器分頁中查看預覽。",
|
"blockedDescription": "您可以在單獨的瀏覽器分頁中查看預覽。",
|
||||||
@@ -5553,7 +5611,8 @@
|
|||||||
"totalSize": "",
|
"totalSize": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
"whenEnabledProjectAndAgentMemoryFilesAre": "",
|
||||||
"whenEnabledTheDatabaseIsBackedUpAutomatically": ""
|
"whenEnabledTheDatabaseIsBackedUpAutomatically": "",
|
||||||
|
"createFailed": ""
|
||||||
},
|
},
|
||||||
"clearToDefault": "",
|
"clearToDefault": "",
|
||||||
"cliAgents": {
|
"cliAgents": {
|
||||||
@@ -5597,7 +5656,9 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"help": "說明",
|
"help": "說明",
|
||||||
"version": "版本 {{version}}"
|
"version": "版本 {{version}}",
|
||||||
|
"checkUpdates": "",
|
||||||
|
"helpDiscussions": ""
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"25": "",
|
"25": "",
|
||||||
@@ -5746,7 +5807,10 @@
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": ""
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "",
|
||||||
|
"star": "",
|
||||||
|
"starFusion": ""
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "確認匯入",
|
"confirmImport": "確認匯入",
|
||||||
@@ -5756,7 +5820,35 @@
|
|||||||
"importing": "匯入中…",
|
"importing": "匯入中…",
|
||||||
"importTitle": "匯入設定",
|
"importTitle": "匯入設定",
|
||||||
"loadingFile": "載入中…",
|
"loadingFile": "載入中…",
|
||||||
"reviewPrompt": "查看要匯入的設定:"
|
"reviewPrompt": "查看要匯入的設定:",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "",
|
||||||
|
"global_other": "",
|
||||||
|
"project_one": "",
|
||||||
|
"project_other": "",
|
||||||
|
"workflowSettings_one": "",
|
||||||
|
"workflowSettings_other": ""
|
||||||
|
},
|
||||||
|
"exported": "",
|
||||||
|
"exportFailed": "",
|
||||||
|
"globalSettings": "",
|
||||||
|
"imported": "",
|
||||||
|
"importFailed": "",
|
||||||
|
"importFailedDetailed": "",
|
||||||
|
"importScope": "",
|
||||||
|
"importTitleAttr": "",
|
||||||
|
"invalidJson": "",
|
||||||
|
"mergeExisting": "",
|
||||||
|
"projectSettings": "",
|
||||||
|
"replaceWarning": "",
|
||||||
|
"scopeBoth": "",
|
||||||
|
"scopeGlobal": "",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "",
|
||||||
|
"global": "",
|
||||||
|
"project": ""
|
||||||
|
},
|
||||||
|
"scopeProject": ""
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "輸入 JSON 值...",
|
"jsonPlaceholder": "輸入 JSON 值...",
|
||||||
"keepLocal": "保留本機",
|
"keepLocal": "保留本機",
|
||||||
@@ -5806,7 +5898,10 @@
|
|||||||
"searchMemoryWithQmd": "",
|
"searchMemoryWithQmd": "",
|
||||||
"testing": "測試中…",
|
"testing": "測試中…",
|
||||||
"testRetrieval": "測試擷取",
|
"testRetrieval": "測試擷取",
|
||||||
"turnsDailyNotesIntoDREAMSMdAndPromotes": ""
|
"turnsDailyNotesIntoDREAMSMdAndPromotes": "",
|
||||||
|
"qmdInstalled": "",
|
||||||
|
"qmdInstallFailed": "",
|
||||||
|
"qmdInstallUnavailable": ""
|
||||||
},
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"abort": "",
|
"abort": "",
|
||||||
@@ -6155,7 +6250,8 @@
|
|||||||
"uRLAndQRGenerationUseTheSelectedToken": "",
|
"uRLAndQRGenerationUseTheSelectedToken": "",
|
||||||
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
"uRLNoHostnameOrPortConfigurationNeeded": "",
|
||||||
"useExisting": "使用現有",
|
"useExisting": "使用現有",
|
||||||
"usingQuickTunnel": ""
|
"usingQuickTunnel": "",
|
||||||
|
"installationFailed": ""
|
||||||
},
|
},
|
||||||
"researchGlobal": {
|
"researchGlobal": {
|
||||||
"advancedExternalSearchProviders": "",
|
"advancedExternalSearchProviders": "",
|
||||||
@@ -6275,7 +6371,9 @@
|
|||||||
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
"timeoutInMinutesForDetectingStuckTasksWhen": "",
|
||||||
"whenEnabledTasksThatModifyTheSameFiles": "",
|
"whenEnabledTasksThatModifyTheSameFiles": "",
|
||||||
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
"whenEnabledTasksWithStalePlansPROMPTMd": "",
|
||||||
"whenTheStuckDetectorKillsAndReQueues": ""
|
"whenTheStuckDetectorKillsAndReQueues": "",
|
||||||
|
"browseWorkspacePath": "",
|
||||||
|
"overlapPickerNote": ""
|
||||||
},
|
},
|
||||||
"scope": {
|
"scope": {
|
||||||
"globalBanner": "這些設定在所有 Fusion 專案中共用。",
|
"globalBanner": "這些設定在所有 Fusion 專案中共用。",
|
||||||
@@ -6335,7 +6433,12 @@
|
|||||||
"worktrunk": "",
|
"worktrunk": "",
|
||||||
"worktrunkBinaryPath": "",
|
"worktrunkBinaryPath": "",
|
||||||
"worktrunkFailureBehavior": "",
|
"worktrunkFailureBehavior": "",
|
||||||
"worktrunkIntegration": ""
|
"worktrunkIntegration": "",
|
||||||
|
"worktreesPickerNote": ""
|
||||||
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "",
|
||||||
|
"projectRoot": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
@@ -6655,7 +6758,10 @@
|
|||||||
"withoutGitHub1": "手動建立任務",
|
"withoutGitHub1": "手動建立任務",
|
||||||
"withoutGitHub2": "為 AI 代理描述工作",
|
"withoutGitHub2": "為 AI 代理描述工作",
|
||||||
"withoutGitHub3": "在看板上追蹤進度",
|
"withoutGitHub3": "在看板上追蹤進度",
|
||||||
"withoutGitHubHeading": "不使用 GitHub(現在可用):"
|
"withoutGitHubHeading": "不使用 GitHub(現在可用):",
|
||||||
|
"brandLogo": "",
|
||||||
|
"brandName": "",
|
||||||
|
"setupCompleteTitle": ""
|
||||||
},
|
},
|
||||||
"shell": {
|
"shell": {
|
||||||
"activePill": "作用中",
|
"activePill": "作用中",
|
||||||
@@ -7201,7 +7307,8 @@
|
|||||||
"mergingFixes": "正在合併修復…",
|
"mergingFixes": "正在合併修復…",
|
||||||
"mergingPr": "正在合併 PR…",
|
"mergingPr": "正在合併 PR…",
|
||||||
"startPrReview": "開始 PR 審查",
|
"startPrReview": "開始 PR 審查",
|
||||||
"statusRefreshed": "PR 狀態已刷新"
|
"statusRefreshed": "PR 狀態已刷新",
|
||||||
|
"label": ""
|
||||||
},
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"ariaLabel": "任務優先級",
|
"ariaLabel": "任務優先級",
|
||||||
@@ -7216,7 +7323,8 @@
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "創建者:",
|
"createdBy": "創建者:",
|
||||||
"createdVia": "通過…建立"
|
"createdVia": "通過…建立",
|
||||||
|
"parentTaskOf": ""
|
||||||
},
|
},
|
||||||
"recoveryState": "恢復狀態",
|
"recoveryState": "恢復狀態",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7955,7 +8063,43 @@
|
|||||||
"switchToMarkdown": "切換為 Markdown",
|
"switchToMarkdown": "切換為 Markdown",
|
||||||
"switchToPlain": "切換為純文字",
|
"switchToPlain": "切換為純文字",
|
||||||
"thinkingLevel": "",
|
"thinkingLevel": "",
|
||||||
"workflowName": ""
|
"workflowName": "",
|
||||||
|
"aggregateAdvisory": "",
|
||||||
|
"aggregateAllPassed": "",
|
||||||
|
"aggregateInProgress": "",
|
||||||
|
"aggregateNoResults": "",
|
||||||
|
"approveAndRun": "",
|
||||||
|
"approveCommandError": "",
|
||||||
|
"approving": "",
|
||||||
|
"awaitingInputTitle": "",
|
||||||
|
"cliApprovalRejectHint": "",
|
||||||
|
"cliApprovalTitle": "",
|
||||||
|
"cliApprovalWarning": "",
|
||||||
|
"customWorkflowFallback": "",
|
||||||
|
"customWorkflowLabel": "",
|
||||||
|
"defaultWorkflow": "",
|
||||||
|
"executionAwaitingCliApproval": "",
|
||||||
|
"executionAwaitingInput": "",
|
||||||
|
"executionCompleted": "",
|
||||||
|
"executionNotStarted": "",
|
||||||
|
"executionPaused": "",
|
||||||
|
"executionPostMerge": "",
|
||||||
|
"executionPreMerge": "",
|
||||||
|
"inputPlaceholder": "",
|
||||||
|
"modelDefault": "",
|
||||||
|
"postMerge": "",
|
||||||
|
"preMerge": "",
|
||||||
|
"replyInComments": "",
|
||||||
|
"resumeTaskError": "",
|
||||||
|
"resuming": "",
|
||||||
|
"statusAdvisory": "",
|
||||||
|
"statusFailed": "",
|
||||||
|
"statusPassed": "",
|
||||||
|
"statusRunning": "",
|
||||||
|
"statusSkipped": "",
|
||||||
|
"submitAndResume": "",
|
||||||
|
"submitting": "",
|
||||||
|
"waitingForOutput": ""
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8007,7 +8151,30 @@
|
|||||||
},
|
},
|
||||||
"collapsePrompt": "",
|
"collapsePrompt": "",
|
||||||
"editingPrompt": "",
|
"editingPrompt": "",
|
||||||
"expandPrompt": ""
|
"expandPrompt": "",
|
||||||
|
"agent": "",
|
||||||
|
"agentsLoadFailed": "",
|
||||||
|
"autoApproveRequests": "",
|
||||||
|
"autoApproveRequestsNote": "",
|
||||||
|
"cliCommandNote": "",
|
||||||
|
"cliMode": "",
|
||||||
|
"cliScript": "",
|
||||||
|
"command": "",
|
||||||
|
"executor": "",
|
||||||
|
"maxRetries": "",
|
||||||
|
"model": "",
|
||||||
|
"modelsLoadFailed": "",
|
||||||
|
"namedScript": "",
|
||||||
|
"namedScriptNote": "",
|
||||||
|
"prompt": "",
|
||||||
|
"scriptName": "",
|
||||||
|
"selectAgent": "",
|
||||||
|
"selectSkill": "",
|
||||||
|
"skill": "",
|
||||||
|
"skillsLoadFailed": "",
|
||||||
|
"skipFirstRunApproval": "",
|
||||||
|
"waitForUserInput": "",
|
||||||
|
"waitForUserInputNote": ""
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "新增欄位",
|
"add": "新增欄位",
|
||||||
@@ -8141,7 +8308,11 @@
|
|||||||
"templatesPluginSteps": "外掛步驟",
|
"templatesPluginSteps": "外掛步驟",
|
||||||
"templatesSection": "範本",
|
"templatesSection": "範本",
|
||||||
"timeoutMs": "",
|
"timeoutMs": "",
|
||||||
"trivialGraphHint": "這個工作流程只會執行 start → end。請從上方的選盤新增步驟來建構它。"
|
"trivialGraphHint": "這個工作流程只會執行 start → end。請從上方的選盤新增步驟來建構它。",
|
||||||
|
"conditionFailure": "",
|
||||||
|
"conditionSuccess": "",
|
||||||
|
"nodeInspector": "",
|
||||||
|
"readOnlyDuplicateToEdit": ""
|
||||||
},
|
},
|
||||||
"workflows": {
|
"workflows": {
|
||||||
"aiEdit": "使用 AI 設計",
|
"aiEdit": "使用 AI 設計",
|
||||||
@@ -8204,13 +8375,27 @@
|
|||||||
"templateNodeCount_other": "{{count}} 個節點",
|
"templateNodeCount_other": "{{count}} 個節點",
|
||||||
"templatePickerLabel": "起始來源",
|
"templatePickerLabel": "起始來源",
|
||||||
"templateSectionBuiltin": "內建工作流程",
|
"templateSectionBuiltin": "內建工作流程",
|
||||||
"templateSectionYours": "你的工作流程"
|
"templateSectionYours": "你的工作流程",
|
||||||
|
"closeEditor": "",
|
||||||
|
"duplicatedEditable": "",
|
||||||
|
"duplicateFailed": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"loading": "",
|
||||||
|
"noneYet": "",
|
||||||
|
"title": ""
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
"switchActiveMessage": "",
|
"switchActiveMessage": "",
|
||||||
"switchActiveTitle": "",
|
"switchActiveTitle": "",
|
||||||
"switchCancel": "",
|
"switchCancel": "",
|
||||||
"switchConfirm": ""
|
"switchConfirm": "",
|
||||||
|
"applyFailed": "",
|
||||||
|
"defaultCleared": "",
|
||||||
|
"defaultSet": "",
|
||||||
|
"defaultWorkflowLabel": "",
|
||||||
|
"loadFailed": "",
|
||||||
|
"manage": "",
|
||||||
|
"none": ""
|
||||||
},
|
},
|
||||||
"workflowSettings": {
|
"workflowSettings": {
|
||||||
"add": "",
|
"add": "",
|
||||||
@@ -8288,5 +8473,50 @@
|
|||||||
"installRequestTitle": "Worktrunk 安裝請求",
|
"installRequestTitle": "Worktrunk 安裝請求",
|
||||||
"sha256": "SHA-256",
|
"sha256": "SHA-256",
|
||||||
"version": "版本"
|
"version": "版本"
|
||||||
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "",
|
||||||
|
"activeSessionHint": "",
|
||||||
|
"agentMessages": "",
|
||||||
|
"arguments": "",
|
||||||
|
"collapseChat": "",
|
||||||
|
"donePlaceholder": "",
|
||||||
|
"doneSessionHint": "",
|
||||||
|
"emptyAgentOutput": "",
|
||||||
|
"entryCount_one": "",
|
||||||
|
"entryCount_other": "",
|
||||||
|
"error": "",
|
||||||
|
"errorCount_one": "",
|
||||||
|
"errorCount_other": "",
|
||||||
|
"expandChat": "",
|
||||||
|
"idleSessionHint": "",
|
||||||
|
"jumpToLatestMessage": "",
|
||||||
|
"latest": "",
|
||||||
|
"loadingAgentOutput": "",
|
||||||
|
"loadingEarlierMessages": "",
|
||||||
|
"loadPreviousMessages": "",
|
||||||
|
"message": "",
|
||||||
|
"messageActiveAgentSession": "",
|
||||||
|
"moreTools_one": "",
|
||||||
|
"moreTools_other": "",
|
||||||
|
"result": "",
|
||||||
|
"roles": {
|
||||||
|
"agent": "",
|
||||||
|
"executor": "",
|
||||||
|
"merger": "",
|
||||||
|
"planner": "",
|
||||||
|
"reviewer": ""
|
||||||
|
},
|
||||||
|
"sending": "",
|
||||||
|
"thinking": "",
|
||||||
|
"toolCall": "",
|
||||||
|
"toolCallCount_one": "",
|
||||||
|
"toolCallCount_other": "",
|
||||||
|
"toolCallTo": "",
|
||||||
|
"toolError": "",
|
||||||
|
"toolNames": "",
|
||||||
|
"toolResult": "",
|
||||||
|
"you": "",
|
||||||
|
"youMessage": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"actions": {
|
||||||
|
"send": ""
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "",
|
"trendDeclining": "",
|
||||||
@@ -78,6 +81,9 @@
|
|||||||
"offline": "",
|
"offline": "",
|
||||||
"online": ""
|
"online": ""
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": ""
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": ""
|
"unknown": ""
|
||||||
},
|
},
|
||||||
@@ -214,28 +220,6 @@
|
|||||||
"refreshSourceInitialLoad": "",
|
"refreshSourceInitialLoad": "",
|
||||||
"refreshSourceManual": ""
|
"refreshSourceManual": ""
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "",
|
|
||||||
"aggregateInProgress": "",
|
|
||||||
"aggregateNoResults": "",
|
|
||||||
"customWorkflowFallback": "",
|
|
||||||
"defaultWorkflow": "",
|
|
||||||
"executionAwaitingCliApproval": "",
|
|
||||||
"executionAwaitingInput": "",
|
|
||||||
"executionCompleted": "",
|
|
||||||
"executionNotStarted": "",
|
|
||||||
"executionPaused": "",
|
|
||||||
"executionPostMerge": "",
|
|
||||||
"executionPreMerge": "",
|
|
||||||
"postMerge": "",
|
|
||||||
"preMerge": "",
|
|
||||||
"statusAdvisory": "",
|
|
||||||
"statusFailed": "",
|
|
||||||
"statusPassed": "",
|
|
||||||
"statusRunning": "",
|
|
||||||
"statusSkipped": "",
|
|
||||||
"waitingForOutput": ""
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "",
|
"summaryAwaitInput": "",
|
||||||
"summaryCodeDefault": "",
|
"summaryCodeDefault": "",
|
||||||
|
|||||||
272
packages/i18n/src/resources.d.ts
vendored
272
packages/i18n/src/resources.d.ts
vendored
@@ -1183,7 +1183,11 @@ export default interface Resources {
|
|||||||
"workflowMismatch": "Drag can't move a card between workflows. Use the workflow switcher instead."
|
"workflowMismatch": "Drag can't move a card between workflows. Use the workflow switcher instead."
|
||||||
},
|
},
|
||||||
"todo": "To Do",
|
"todo": "To Do",
|
||||||
"triage": "Triage"
|
"triage": "Triage",
|
||||||
|
"workflow": {
|
||||||
|
"edit": "Edit workflows",
|
||||||
|
"new": "New workflow"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"branchGroup": {
|
"branchGroup": {
|
||||||
"abandonGroup": "Abandon group",
|
"abandonGroup": "Abandon group",
|
||||||
@@ -1805,6 +1809,7 @@ export default interface Resources {
|
|||||||
"comments": {
|
"comments": {
|
||||||
"addButton": "Add Comment",
|
"addButton": "Add Comment",
|
||||||
"addedSuccess": "Comment added",
|
"addedSuccess": "Comment added",
|
||||||
|
"aiGuidance": "AI Guidance",
|
||||||
"deletedSuccess": "Comment deleted",
|
"deletedSuccess": "Comment deleted",
|
||||||
"deletingButton": "Deleting…",
|
"deletingButton": "Deleting…",
|
||||||
"editedSuffix": "(edited)",
|
"editedSuffix": "(edited)",
|
||||||
@@ -4661,6 +4666,7 @@ export default interface Resources {
|
|||||||
"viewUnavailable": "Plugin view unavailable"
|
"viewUnavailable": "Plugin view unavailable"
|
||||||
},
|
},
|
||||||
"pr": {
|
"pr": {
|
||||||
|
"assignees": "Assignees",
|
||||||
"authFail": "Run gh auth login and try again.",
|
"authFail": "Run gh auth login and try again.",
|
||||||
"authOk": "GitHub CLI auth is available.",
|
"authOk": "GitHub CLI auth is available.",
|
||||||
"baseBranch": "Base branch",
|
"baseBranch": "Base branch",
|
||||||
@@ -4680,15 +4686,67 @@ export default interface Resources {
|
|||||||
"createDraftPr": "Create draft PR",
|
"createDraftPr": "Create draft PR",
|
||||||
"createPr": "Create PR",
|
"createPr": "Create PR",
|
||||||
"createTitle": "Create Pull Request",
|
"createTitle": "Create Pull Request",
|
||||||
|
"dismissConflictResolutionError": "Dismiss conflict resolution error",
|
||||||
"dismissError": "Dismiss PR error",
|
"dismissError": "Dismiss PR error",
|
||||||
|
"dismissPushBranchError": "Dismiss push branch error",
|
||||||
|
"error": {
|
||||||
|
"actionOpen": "Action: open",
|
||||||
|
"actionRun": "Action: run",
|
||||||
|
"docs": "docs"
|
||||||
|
},
|
||||||
|
"generatingBody": "Generating AI body…",
|
||||||
|
"generatingTitle": "Generating AI title…",
|
||||||
|
"labels": "Labels",
|
||||||
|
"loadingOptions": "Loading PR options…",
|
||||||
|
"loadingPreflight": "Loading pre-flight checks…",
|
||||||
|
"noChangedFiles": "No changed files detected.",
|
||||||
|
"noCommits": "No commits found.",
|
||||||
"noConflicts": "No merge conflicts detected.",
|
"noConflicts": "No merge conflicts detected.",
|
||||||
"preflightChecks": "Pre-flight checks",
|
"preflightChecks": "Pre-flight checks",
|
||||||
"previewTitle": "Diff & commit preview",
|
"previewTitle": "Diff & commit preview",
|
||||||
|
"pushBranch": {
|
||||||
|
"button": "Push branch to remote",
|
||||||
|
"message": "Fusion will push this task's branch to origin so the PR can be created.",
|
||||||
|
"title": "Push branch to remote"
|
||||||
|
},
|
||||||
"regenerate": "Regenerate",
|
"regenerate": "Regenerate",
|
||||||
"rerunPreflight": "Re-run preflight",
|
"rerunPreflight": "Re-run preflight",
|
||||||
|
"resolveConflicts": {
|
||||||
|
"button": "Resolve conflicts with AI",
|
||||||
|
"message": "Fusion will use AI to resolve conflicts on this branch and push it.",
|
||||||
|
"title": "Resolve conflicts with AI"
|
||||||
|
},
|
||||||
"revertToAi": "Revert to AI version",
|
"revertToAi": "Revert to AI version",
|
||||||
|
"reviewers": "Reviewers",
|
||||||
"titleLabel": "Title",
|
"titleLabel": "Title",
|
||||||
"usingTemplate": "Using <code>.github/pull_request_template.md</code>"
|
"usingTemplate": "Using <code>.github/pull_request_template.md</code>",
|
||||||
|
"view": {
|
||||||
|
"agentDisagreed": "agent disagreed",
|
||||||
|
"agentReplyFix": "Agent reply — fix {{sha}}",
|
||||||
|
"approve": "Approve",
|
||||||
|
"autoMerge": "Auto-merge",
|
||||||
|
"close": "Close",
|
||||||
|
"confirmMerge": "Confirm merge",
|
||||||
|
"creating": "Creating PR…",
|
||||||
|
"creationFailed": "PR creation failed",
|
||||||
|
"loading": "Loading PR…",
|
||||||
|
"merge": "Merge",
|
||||||
|
"mergeDisabledUntilVerified": "Merge is disabled until GitHub verifies this PR",
|
||||||
|
"mergeableLabel": "Mergeable:",
|
||||||
|
"noReviewThreads": "No review threads.",
|
||||||
|
"none": "none",
|
||||||
|
"requestRetry": "Request retry",
|
||||||
|
"resolveConflictsBeforeMerge": "Resolve conflicts on GitHub before merging",
|
||||||
|
"resolveConflictsOnGithub": "Resolve conflicts on GitHub",
|
||||||
|
"responseAlreadyInProgress": "A response run is already in progress",
|
||||||
|
"responsePending_one": "Response run in progress — {{count}} threads pending",
|
||||||
|
"responsePending_other": "Response run in progress — {{count}} threads pending",
|
||||||
|
"retryCreation": "Retry PR creation",
|
||||||
|
"reviewLabel": "Review:",
|
||||||
|
"threadFixed": "fixed",
|
||||||
|
"threadPending": "pending",
|
||||||
|
"verifyingGithub": "Verifying with GitHub…"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"preview": {
|
"preview": {
|
||||||
"blockedDescription": "You can view the preview in a separate browser tab.",
|
"blockedDescription": "You can view the preview in a separate browser tab.",
|
||||||
@@ -5527,6 +5585,7 @@ export default interface Resources {
|
|||||||
"backupS": " backup(s)",
|
"backupS": " backup(s)",
|
||||||
"backupScheduleCron": "Backup Schedule (Cron)",
|
"backupScheduleCron": "Backup Schedule (Cron)",
|
||||||
"backups": "backups",
|
"backups": "backups",
|
||||||
|
"createFailed": "Failed to create backup",
|
||||||
"creating": "Creating…",
|
"creating": "Creating…",
|
||||||
"cronExpressionForBackupTimingDefault02": " Cron expression for backup timing. Default: 0 2 * * * (daily at 2 AM). Examples: 0 * * * * (hourly), 0 0 * * 0 (weekly), */15 * * * * (every 15 min) ",
|
"cronExpressionForBackupTimingDefault02": " Cron expression for backup timing. Default: 0 2 * * * (daily at 2 AM). Examples: 0 * * * * (hourly), 0 0 * * 0 (weekly), */15 * * * * (every 15 min) ",
|
||||||
"cronExpressionForMemoryBackupTimingDefault0": "Cron expression for memory backup timing. Default: 0 3 * * * (daily at 3 AM).",
|
"cronExpressionForMemoryBackupTimingDefault0": "Cron expression for memory backup timing. Default: 0 3 * * * (daily at 3 AM).",
|
||||||
@@ -5597,8 +5656,14 @@ export default interface Resources {
|
|||||||
"experimentalFeaturesAreEarlyCapabilitiesThatAreNot": " Experimental features are early capabilities that are not yet fully stable. Enable them to test new functionality, but be aware they may change or be removed. ",
|
"experimentalFeaturesAreEarlyCapabilitiesThatAreNot": " Experimental features are early capabilities that are not yet fully stable. Enable them to test new functionality, but be aware they may change or be removed. ",
|
||||||
"featureFlags": "Feature Flags"
|
"featureFlags": "Feature Flags"
|
||||||
},
|
},
|
||||||
|
"fileBrowser": {
|
||||||
|
"currentDirectory": "Current directory:",
|
||||||
|
"projectRoot": "(project root)"
|
||||||
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
|
"checkUpdates": "Check for updates",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
|
"helpDiscussions": "Help and discussions",
|
||||||
"version": "Version {{version}}"
|
"version": "Version {{version}}"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
@@ -5748,17 +5813,48 @@ export default interface Resources {
|
|||||||
"whenEnabledStartupRefreshesModelsThroughTheLocal": " When enabled, startup refreshes models through the local "
|
"whenEnabledStartupRefreshesModelsThroughTheLocal": " When enabled, startup refreshes models through the local "
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"discord": "Discord"
|
"discord": "Discord",
|
||||||
|
"joinDiscord": "Join our Discord",
|
||||||
|
"star": "Star",
|
||||||
|
"starFusion": "Star Fusion on GitHub"
|
||||||
},
|
},
|
||||||
"importExport": {
|
"importExport": {
|
||||||
"confirmImport": "Confirm Import",
|
"confirmImport": "Confirm Import",
|
||||||
|
"counts": {
|
||||||
|
"global_one": "{{count}} global",
|
||||||
|
"global_other": "{{count}} global",
|
||||||
|
"project_one": "{{count}} project",
|
||||||
|
"project_other": "{{count}} project",
|
||||||
|
"workflowSettings_one": "{{count}} workflow setting value",
|
||||||
|
"workflowSettings_other": "{{count}} workflow setting value"
|
||||||
|
},
|
||||||
"exportBtn": "Export",
|
"exportBtn": "Export",
|
||||||
|
"exportFailed": "Failed to export settings",
|
||||||
"exportTitle": "Export settings to JSON file",
|
"exportTitle": "Export settings to JSON file",
|
||||||
|
"exported": "Settings exported ({{scope}} scope)",
|
||||||
|
"globalSettings": "Global Settings:",
|
||||||
"importBtn": "Import",
|
"importBtn": "Import",
|
||||||
|
"importFailed": "Import failed",
|
||||||
|
"importFailedDetailed": "Failed to import settings",
|
||||||
|
"importScope": "Import Scope:",
|
||||||
"importTitle": "Import Settings",
|
"importTitle": "Import Settings",
|
||||||
|
"importTitleAttr": "Import settings from JSON file",
|
||||||
|
"imported": "Imported {{counts}} setting(s)",
|
||||||
"importing": "Importing…",
|
"importing": "Importing…",
|
||||||
|
"invalidJson": "Invalid JSON file: {{error}}",
|
||||||
"loadingFile": "Loading…",
|
"loadingFile": "Loading…",
|
||||||
"reviewPrompt": "Review the settings to be imported:"
|
"mergeExisting": "Merge with existing settings (recommended)",
|
||||||
|
"projectSettings": "Project Settings:",
|
||||||
|
"replaceWarning": "If unchecked, existing settings will be replaced with imported values.",
|
||||||
|
"reviewPrompt": "Review the settings to be imported:",
|
||||||
|
"scopeBoth": "Both global and project settings",
|
||||||
|
"scopeGlobal": "Global settings only",
|
||||||
|
"scopeLabel": {
|
||||||
|
"all": "all",
|
||||||
|
"global": "global",
|
||||||
|
"project": "project"
|
||||||
|
},
|
||||||
|
"scopeProject": "Project settings only"
|
||||||
},
|
},
|
||||||
"jsonPlaceholder": "Enter JSON value...",
|
"jsonPlaceholder": "Enter JSON value...",
|
||||||
"keepLocal": "Keep Local",
|
"keepLocal": "Keep Local",
|
||||||
@@ -5800,6 +5896,9 @@ export default interface Resources {
|
|||||||
"noMatchingMemoryFound": "No matching memory found.",
|
"noMatchingMemoryFound": "No matching memory found.",
|
||||||
"processDreamsFromDailyMemory": " Process dreams from daily memory ",
|
"processDreamsFromDailyMemory": " Process dreams from daily memory ",
|
||||||
"qmd": " qmd ",
|
"qmd": " qmd ",
|
||||||
|
"qmdInstallFailed": "Failed to install qmd",
|
||||||
|
"qmdInstallUnavailable": "qmd install finished, but qmd is still unavailable",
|
||||||
|
"qmdInstalled": "qmd installed successfully",
|
||||||
"qmdIsNotInstalledSearchWillUseLocal": " qmd is not installed. Search will use local files. Install indexed retrieval: ",
|
"qmdIsNotInstalledSearchWillUseLocal": " qmd is not installed. Search will use local files. Install indexed retrieval: ",
|
||||||
"result": " result",
|
"result": " result",
|
||||||
"runsTheSameQmdBackedMemorySearchPath": "Runs the same qmd-backed memory_search path agents use.",
|
"runsTheSameQmdBackedMemorySearchPath": "Runs the same qmd-backed memory_search path agents use.",
|
||||||
@@ -6115,6 +6214,7 @@ export default interface Resources {
|
|||||||
"ifHomebrewIsUnavailable": "If Homebrew is unavailable: ",
|
"ifHomebrewIsUnavailable": "If Homebrew is unavailable: ",
|
||||||
"ingressURL": "Ingress URL",
|
"ingressURL": "Ingress URL",
|
||||||
"installCloudflared": "Install cloudflared",
|
"installCloudflared": "Install cloudflared",
|
||||||
|
"installationFailed": "Installation failed",
|
||||||
"installing": "Installing…",
|
"installing": "Installing…",
|
||||||
"lastShortLivedTokenExpiresAt": "Last short-lived token expires at ",
|
"lastShortLivedTokenExpiresAt": "Last short-lived token expires at ",
|
||||||
"manualInstall": "Manual install: ",
|
"manualInstall": "Manual install: ",
|
||||||
@@ -6235,6 +6335,7 @@ export default interface Resources {
|
|||||||
"archiveCompletedTasksAfterDays": "Archive Completed Tasks After (days)",
|
"archiveCompletedTasksAfterDays": "Archive Completed Tasks After (days)",
|
||||||
"backlogNoTaskAutoClaimIsExecutorOnly": "Backlog/no-task auto-claim is executor-only by default. Enable to let engineer-role agents auto-claim unowned backlog tasks; explicit routing and delegation are unchanged. Default: off.",
|
"backlogNoTaskAutoClaimIsExecutorOnly": "Backlog/no-task auto-claim is executor-only by default. Enable to let engineer-role agents auto-claim unowned backlog tasks; explicit routing and delegation are unchanged. Default: off.",
|
||||||
"browse": " Browse ",
|
"browse": " Browse ",
|
||||||
|
"browseWorkspacePath": "Browse workspace path",
|
||||||
"closeParenPeriod": ").",
|
"closeParenPeriod": ").",
|
||||||
"compactModeKeepsArchiveSizeLowWhilePreserving": "Compact mode keeps archive size low while preserving recent agent activity for context.",
|
"compactModeKeepsArchiveSizeLowWhilePreserving": "Compact mode keeps archive size low while preserving recent agent activity for context.",
|
||||||
"compactSummaryAndRecentEntries": "Compact summary and recent entries",
|
"compactSummaryAndRecentEntries": "Compact summary and recent entries",
|
||||||
@@ -6261,6 +6362,7 @@ export default interface Resources {
|
|||||||
"off": "Off",
|
"off": "Off",
|
||||||
"optionalFileOrDirectoryPathsToIgnoreWhen": " Optional file or directory paths to ignore when overlap serialization is enabled. Paths are project-relative (for example ",
|
"optionalFileOrDirectoryPathsToIgnoreWhen": " Optional file or directory paths to ignore when overlap serialization is enabled. Paths are project-relative (for example ",
|
||||||
"or": " or ",
|
"or": " or ",
|
||||||
|
"overlapPickerNote": "Choose a file to ignore directly, or navigate into a folder and select the current directory.",
|
||||||
"pollIntervalMs": "Poll Interval (ms)",
|
"pollIntervalMs": "Poll Interval (ms)",
|
||||||
"preserveStepProgressOnStuckTaskRequeue": " Preserve step progress on stuck-task requeue ",
|
"preserveStepProgressOnStuckTaskRequeue": " Preserve step progress on stuck-task requeue ",
|
||||||
"remove": " Remove ",
|
"remove": " Remove ",
|
||||||
@@ -6334,6 +6436,7 @@ export default interface Resources {
|
|||||||
"worktreeNamingStyle": "Worktree Naming Style",
|
"worktreeNamingStyle": "Worktree Naming Style",
|
||||||
"worktrees": "Worktrees",
|
"worktrees": "Worktrees",
|
||||||
"worktreesDirectory": "Worktrees Directory",
|
"worktreesDirectory": "Worktrees Directory",
|
||||||
|
"worktreesPickerNote": "Navigate to the folder where Fusion should create task worktrees, then select the current directory.",
|
||||||
"worktrunk": " worktrunk ",
|
"worktrunk": " worktrunk ",
|
||||||
"worktrunkBinaryPath": "Worktrunk binary path",
|
"worktrunkBinaryPath": "Worktrunk binary path",
|
||||||
"worktrunkFailureBehavior": "Worktrunk failure behavior",
|
"worktrunkFailureBehavior": "Worktrunk failure behavior",
|
||||||
@@ -6362,6 +6465,8 @@ export default interface Resources {
|
|||||||
"authToken": "Auth Token",
|
"authToken": "Auth Token",
|
||||||
"authTokenOptional": "Auth token (optional)",
|
"authTokenOptional": "Auth token (optional)",
|
||||||
"back": "← Back",
|
"back": "← Back",
|
||||||
|
"brandLogo": "Fusion logo",
|
||||||
|
"brandName": "Fusion",
|
||||||
"browserAuthToken": "Browser Auth Token",
|
"browserAuthToken": "Browser Auth Token",
|
||||||
"cancelLogin": "Cancel",
|
"cancelLogin": "Cancel",
|
||||||
"childProcess": "Child-Process",
|
"childProcess": "Child-Process",
|
||||||
@@ -6602,6 +6707,7 @@ export default interface Resources {
|
|||||||
"setUpAi": "Set Up AI",
|
"setUpAi": "Set Up AI",
|
||||||
"setUpProject": "Set Up Project",
|
"setUpProject": "Set Up Project",
|
||||||
"setupComplete": "Setup complete! Head to the board to create your first task, or explore the dashboard to see what's available.",
|
"setupComplete": "Setup complete! Head to the board to create your first task, or explore the dashboard to see what's available.",
|
||||||
|
"setupCompleteTitle": "Setup Complete!",
|
||||||
"setupMode": "Setup Mode",
|
"setupMode": "Setup Mode",
|
||||||
"setupWizardHint": "In the setup wizard, pick an existing directory or paste a GitHub clone URL.",
|
"setupWizardHint": "In the setup wizard, pick an existing directory or paste a GitHub clone URL.",
|
||||||
"skip": "Skip",
|
"skip": "Skip",
|
||||||
@@ -6925,6 +7031,51 @@ export default interface Resources {
|
|||||||
"toggleWordWrap": "Toggle word wrap",
|
"toggleWordWrap": "Toggle word wrap",
|
||||||
"unavailable": "Detailed file changes unavailable."
|
"unavailable": "Detailed file changes unavailable."
|
||||||
},
|
},
|
||||||
|
"taskChat": {
|
||||||
|
"activePlaceholder": "Steer the currently executing agent",
|
||||||
|
"activeSessionHint": "Message the active agent session. Guidance is delivered to the running session in real time.",
|
||||||
|
"agentMessages": "{{label}} messages",
|
||||||
|
"arguments": "Arguments",
|
||||||
|
"collapseChat": "Collapse chat",
|
||||||
|
"donePlaceholder": "Start a refinement task for this completed task",
|
||||||
|
"doneSessionHint": "Send a message to start a refinement task for this completed task.",
|
||||||
|
"emptyAgentOutput": "No agent output yet. Live messages from Planner, Executor, Reviewer, and Merger agents will appear here.",
|
||||||
|
"entryCount_one": "{{count}} entry",
|
||||||
|
"entryCount_other": "{{count}} entries",
|
||||||
|
"error": "Error",
|
||||||
|
"errorCount_one": "{{count}} error",
|
||||||
|
"errorCount_other": "{{count}} errors",
|
||||||
|
"expandChat": "Expand chat to full modal",
|
||||||
|
"idleSessionHint": "No agent is working on this task right now. Your message is saved as guidance and will reach an agent the next time this task runs.",
|
||||||
|
"jumpToLatestMessage": "Jump to latest message",
|
||||||
|
"latest": "Latest",
|
||||||
|
"loadPreviousMessages": "Load previous messages",
|
||||||
|
"loadingAgentOutput": "Loading agent output…",
|
||||||
|
"loadingEarlierMessages": "Loading earlier messages…",
|
||||||
|
"message": "Message",
|
||||||
|
"messageActiveAgentSession": "Message active agent session",
|
||||||
|
"moreTools_one": ", +{{count}} more",
|
||||||
|
"moreTools_other": ", +{{count}} more",
|
||||||
|
"result": "Result",
|
||||||
|
"roles": {
|
||||||
|
"agent": "Agent",
|
||||||
|
"executor": "Executor",
|
||||||
|
"merger": "Merger",
|
||||||
|
"planner": "Planner",
|
||||||
|
"reviewer": "Reviewer"
|
||||||
|
},
|
||||||
|
"sending": "Sending",
|
||||||
|
"thinking": "Thinking",
|
||||||
|
"toolCall": "Tool call",
|
||||||
|
"toolCallCount_one": "{{count}} tool call",
|
||||||
|
"toolCallCount_other": "{{count}} tool calls",
|
||||||
|
"toolCallTo": "Tool call → {{label}}",
|
||||||
|
"toolError": "Tool error",
|
||||||
|
"toolNames": "Tool names",
|
||||||
|
"toolResult": "Tool result",
|
||||||
|
"you": "You",
|
||||||
|
"youMessage": "You message"
|
||||||
|
},
|
||||||
"taskDetail": {
|
"taskDetail": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"menuBtn": "Actions"
|
"menuBtn": "Actions"
|
||||||
@@ -7199,6 +7350,7 @@ export default interface Resources {
|
|||||||
"checkPrStatus": "Check PR Status",
|
"checkPrStatus": "Check PR Status",
|
||||||
"creatingPr": "Creating PR…",
|
"creatingPr": "Creating PR…",
|
||||||
"finishAndClose": "Finish & Close",
|
"finishAndClose": "Finish & Close",
|
||||||
|
"label": "PR",
|
||||||
"mergeAndClose": "Merge & Close",
|
"mergeAndClose": "Merge & Close",
|
||||||
"mergingFixes": "Merging fixes…",
|
"mergingFixes": "Merging fixes…",
|
||||||
"mergingPr": "Merging PR…",
|
"mergingPr": "Merging PR…",
|
||||||
@@ -7218,7 +7370,8 @@ export default interface Resources {
|
|||||||
},
|
},
|
||||||
"provenance": {
|
"provenance": {
|
||||||
"createdBy": "Created by",
|
"createdBy": "Created by",
|
||||||
"createdVia": "Created via"
|
"createdVia": "Created via",
|
||||||
|
"parentTaskOf": "of"
|
||||||
},
|
},
|
||||||
"recoveryState": "Recovery state",
|
"recoveryState": "Recovery state",
|
||||||
"refine": {
|
"refine": {
|
||||||
@@ -7903,22 +8056,45 @@ export default interface Resources {
|
|||||||
},
|
},
|
||||||
"workflow": {
|
"workflow": {
|
||||||
"advisoryExplanation": "Advisory workflow steps flagged non-blocking improvements:",
|
"advisoryExplanation": "Advisory workflow steps flagged non-blocking improvements:",
|
||||||
|
"aggregateAdvisory": "Advisory",
|
||||||
|
"aggregateAllPassed": "All passed",
|
||||||
|
"aggregateInProgress": "In progress",
|
||||||
|
"aggregateNoResults": "No results",
|
||||||
"aggregateResult": "Aggregate result",
|
"aggregateResult": "Aggregate result",
|
||||||
|
"approveAndRun": "Approve & run",
|
||||||
|
"approveCommandError": "Failed to approve command",
|
||||||
|
"approving": "Approving…",
|
||||||
|
"awaitingInputTitle": "Waiting for your input",
|
||||||
|
"cliApprovalRejectHint": "To reject, keep the task paused and do not approve.",
|
||||||
|
"cliApprovalTitle": "Approve CLI command?",
|
||||||
|
"cliApprovalWarning": "This command will run in the task worktree. Approving trusts this exact command for future runs.",
|
||||||
"configuredSteps": "Configured Workflow Steps",
|
"configuredSteps": "Configured Workflow Steps",
|
||||||
|
"customWorkflowFallback": "Custom workflow",
|
||||||
|
"customWorkflowLabel": "Custom workflow",
|
||||||
|
"defaultWorkflow": "Default",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"doneEditingAriaLabel": "Done editing workflow steps",
|
"doneEditingAriaLabel": "Done editing workflow steps",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"editAriaLabel": "Edit workflow steps",
|
"editAriaLabel": "Edit workflow steps",
|
||||||
"editWorkflow": "Edit workflow",
|
"editWorkflow": "Edit workflow",
|
||||||
|
"executionAwaitingCliApproval": "Awaiting CLI approval",
|
||||||
|
"executionAwaitingInput": "Awaiting input",
|
||||||
|
"executionCompleted": "Completed",
|
||||||
|
"executionNotStarted": "Not started",
|
||||||
"executionOrder": "Execution order:",
|
"executionOrder": "Execution order:",
|
||||||
|
"executionPaused": "Paused",
|
||||||
"executionPhase": "Execution phase",
|
"executionPhase": "Execution phase",
|
||||||
|
"executionPostMerge": "Post-merge steps running",
|
||||||
|
"executionPreMerge": "Pre-merge steps running",
|
||||||
"expandOutput": "Expand output",
|
"expandOutput": "Expand output",
|
||||||
"graph": "Workflow graph",
|
"graph": "Workflow graph",
|
||||||
"graphUnavailable": "Workflow graph unavailable",
|
"graphUnavailable": "Workflow graph unavailable",
|
||||||
"hideOutput": "Hide output",
|
"hideOutput": "Hide output",
|
||||||
|
"inputPlaceholder": "Type your reply…",
|
||||||
"loadingGraph": "Loading workflow graph…",
|
"loadingGraph": "Loading workflow graph…",
|
||||||
"loadingResults": "Loading workflow results…",
|
"loadingResults": "Loading workflow results…",
|
||||||
"markdown": "Markdown",
|
"markdown": "Markdown",
|
||||||
|
"modelDefault": "Default",
|
||||||
"modelSettings": "Model settings",
|
"modelSettings": "Model settings",
|
||||||
"moveDown": "Move down",
|
"moveDown": "Move down",
|
||||||
"moveUp": "Move up",
|
"moveUp": "Move up",
|
||||||
@@ -7930,10 +8106,20 @@ export default interface Resources {
|
|||||||
"overview": "Workflow overview",
|
"overview": "Workflow overview",
|
||||||
"plain": "Plain",
|
"plain": "Plain",
|
||||||
"polishNotes": "Polish notes",
|
"polishNotes": "Polish notes",
|
||||||
|
"postMerge": "Post-merge",
|
||||||
|
"preMerge": "Pre-merge",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
|
"replyInComments": "Reply in the comments and unpause the task to continue.",
|
||||||
|
"resumeTaskError": "Failed to resume task",
|
||||||
|
"resuming": "Resuming…",
|
||||||
"selectStepsDescription": "Select steps to run after task implementation completes",
|
"selectStepsDescription": "Select steps to run after task implementation completes",
|
||||||
"showOutput": "Show output",
|
"showOutput": "Show output",
|
||||||
"started": "Started:",
|
"started": "Started:",
|
||||||
|
"statusAdvisory": "Advisory failure",
|
||||||
|
"statusFailed": "Failed",
|
||||||
|
"statusPassed": "Passed",
|
||||||
|
"statusRunning": "Running…",
|
||||||
|
"statusSkipped": "Skipped",
|
||||||
"stepCount_one": "{{count}} step",
|
"stepCount_one": "{{count}} step",
|
||||||
"stepCount_other": "{{count}} steps",
|
"stepCount_other": "{{count}} steps",
|
||||||
"stepDefinitionNotFound": "Step definition not found.",
|
"stepDefinitionNotFound": "Step definition not found.",
|
||||||
@@ -7941,6 +8127,8 @@ export default interface Resources {
|
|||||||
"stepProgressValue": "{{completed}} of {{total}} steps completed",
|
"stepProgressValue": "{{completed}} of {{total}} steps completed",
|
||||||
"steps": "Workflow Steps",
|
"steps": "Workflow Steps",
|
||||||
"stepsExplanation": "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.",
|
"stepsExplanation": "Pre-merge steps run after implementation, before merge. Post-merge steps run after merge succeeds.",
|
||||||
|
"submitAndResume": "Submit & resume",
|
||||||
|
"submitting": "Submitting…",
|
||||||
"summaryAdvisory_one": "{{count}} advisory",
|
"summaryAdvisory_one": "{{count}} advisory",
|
||||||
"summaryAdvisory_other": "{{count}} advisory",
|
"summaryAdvisory_other": "{{count}} advisory",
|
||||||
"summaryFailed_one": "{{count}} failed",
|
"summaryFailed_one": "{{count}} failed",
|
||||||
@@ -7957,6 +8145,7 @@ export default interface Resources {
|
|||||||
"switchToMarkdown": "Switch to markdown",
|
"switchToMarkdown": "Switch to markdown",
|
||||||
"switchToPlain": "Switch to plain text",
|
"switchToPlain": "Switch to plain text",
|
||||||
"thinkingLevel": "Thinking level",
|
"thinkingLevel": "Thinking level",
|
||||||
|
"waitingForOutput": "Waiting for agent output…",
|
||||||
"workflowName": "Workflow"
|
"workflowName": "Workflow"
|
||||||
},
|
},
|
||||||
"workflowColumns": {
|
"workflowColumns": {
|
||||||
@@ -7993,6 +8182,10 @@ export default interface Resources {
|
|||||||
"unplacedCount_other": "{{count}} nodes not placed in a column"
|
"unplacedCount_other": "{{count}} nodes not placed in a column"
|
||||||
},
|
},
|
||||||
"workflowEditor": {
|
"workflowEditor": {
|
||||||
|
"agent": "Agent",
|
||||||
|
"agentsLoadFailed": "Failed to load agents",
|
||||||
|
"autoApproveRequests": "Auto-approve requests",
|
||||||
|
"autoApproveRequestsNote": "Runs without pausing for approval — e.g. a CLI command executes on its first run without waiting for your sign-off.",
|
||||||
"cliAgent": {
|
"cliAgent": {
|
||||||
"adapterLabel": "CLI adapter",
|
"adapterLabel": "CLI adapter",
|
||||||
"adapterNote": "Drives a CLI coding agent in an engine-owned terminal for this step.",
|
"adapterNote": "Drives a CLI coding agent in an engine-owned terminal for this step.",
|
||||||
@@ -8007,9 +8200,28 @@ export default interface Resources {
|
|||||||
"notifyLabel": "Waiting-on-input notification",
|
"notifyLabel": "Waiting-on-input notification",
|
||||||
"notifyNote": "How you are alerted when the agent pauses waiting for input on this step."
|
"notifyNote": "How you are alerted when the agent pauses waiting for input on this step."
|
||||||
},
|
},
|
||||||
|
"cliCommandNote": "Runs an arbitrary command in the task worktree. The first time this exact command runs, the task pauses for your approval. The node prompt is passed via FUSION_NODE_PROMPT.",
|
||||||
|
"cliMode": "CLI mode",
|
||||||
|
"cliScript": "CLI / script",
|
||||||
"collapsePrompt": "Collapse prompt editor",
|
"collapsePrompt": "Collapse prompt editor",
|
||||||
|
"command": "Command",
|
||||||
"editingPrompt": "Editing Prompt",
|
"editingPrompt": "Editing Prompt",
|
||||||
"expandPrompt": "Expand prompt editor"
|
"executor": "Executor",
|
||||||
|
"expandPrompt": "Expand prompt editor",
|
||||||
|
"maxRetries": "Max retries",
|
||||||
|
"model": "Model",
|
||||||
|
"modelsLoadFailed": "Failed to load models",
|
||||||
|
"namedScript": "Named script",
|
||||||
|
"namedScriptNote": "Named script from project settings. The node prompt is passed via FUSION_NODE_PROMPT.",
|
||||||
|
"prompt": "Prompt",
|
||||||
|
"scriptName": "Script name",
|
||||||
|
"selectAgent": "— select agent —",
|
||||||
|
"selectSkill": "— select skill —",
|
||||||
|
"skill": "Skill",
|
||||||
|
"skillsLoadFailed": "Failed to load skills",
|
||||||
|
"skipFirstRunApproval": "Skip first-run approval (runs without pausing)",
|
||||||
|
"waitForUserInput": "Wait for user input",
|
||||||
|
"waitForUserInputNote": "This node pauses the task until you reply in the task's comments and unpause. The Prompt field above is shown to the user as the question."
|
||||||
},
|
},
|
||||||
"workflowFields": {
|
"workflowFields": {
|
||||||
"add": "Add field",
|
"add": "Add field",
|
||||||
@@ -8052,6 +8264,8 @@ export default interface Resources {
|
|||||||
"codeSource": "Source (TypeScript)",
|
"codeSource": "Source (TypeScript)",
|
||||||
"codeTimeout": "Timeout (ms)",
|
"codeTimeout": "Timeout (ms)",
|
||||||
"collapseInspector": "Collapse",
|
"collapseInspector": "Collapse",
|
||||||
|
"conditionFailure": "failure",
|
||||||
|
"conditionSuccess": "success",
|
||||||
"cycleBlocked": "That connection would create a cycle — only rework edges inside a for-each template may loop back",
|
"cycleBlocked": "That connection would create a cycle — only rework edges inside a for-each template may loop back",
|
||||||
"deleteEdge": "Delete edge",
|
"deleteEdge": "Delete edge",
|
||||||
"deleteNode": "Delete node",
|
"deleteNode": "Delete node",
|
||||||
@@ -8108,6 +8322,7 @@ export default interface Resources {
|
|||||||
"mobileMoveDown": "Move down",
|
"mobileMoveDown": "Move down",
|
||||||
"mobileMoveUp": "Move up",
|
"mobileMoveUp": "Move up",
|
||||||
"mobileNodeKinds": "Node types",
|
"mobileNodeKinds": "Node types",
|
||||||
|
"nodeInspector": "Node",
|
||||||
"notifyCustom": "Custom",
|
"notifyCustom": "Custom",
|
||||||
"notifyCustomEvent": "Custom event",
|
"notifyCustomEvent": "Custom event",
|
||||||
"notifyEvent": "Event type",
|
"notifyEvent": "Event type",
|
||||||
@@ -8117,6 +8332,7 @@ export default interface Resources {
|
|||||||
"parseArtifact": "Artifact",
|
"parseArtifact": "Artifact",
|
||||||
"parseParser": "Parser",
|
"parseParser": "Parser",
|
||||||
"quorumN": "Quorum count (n)",
|
"quorumN": "Quorum count (n)",
|
||||||
|
"readOnlyDuplicateToEdit": "Read-only built-in — duplicate the workflow to edit nodes.",
|
||||||
"releaseCapacity": "Downstream capacity",
|
"releaseCapacity": "Downstream capacity",
|
||||||
"releaseCondition": "Release condition",
|
"releaseCondition": "Release condition",
|
||||||
"releaseDependency": "Dependency complete",
|
"releaseDependency": "Dependency complete",
|
||||||
@@ -8146,6 +8362,13 @@ export default interface Resources {
|
|||||||
"trivialGraphHint": "This workflow only runs start → end. Add steps from the palette above to build it out."
|
"trivialGraphHint": "This workflow only runs start → end. Add steps from the palette above to build it out."
|
||||||
},
|
},
|
||||||
"workflowSelector": {
|
"workflowSelector": {
|
||||||
|
"applyFailed": "Failed to apply workflow",
|
||||||
|
"defaultCleared": "Default workflow cleared",
|
||||||
|
"defaultSet": "Default workflow set",
|
||||||
|
"defaultWorkflowLabel": "Default workflow for new tasks",
|
||||||
|
"loadFailed": "Failed to load workflows",
|
||||||
|
"manage": "Manage…",
|
||||||
|
"none": "None",
|
||||||
"switchActiveMessage": "This task has an active session. Switching workflows aborts it and re-homes the card into the new workflow's entry column. Continue?",
|
"switchActiveMessage": "This task has an active session. Switching workflows aborts it and re-homes the card into the new workflow's entry column. Continue?",
|
||||||
"switchActiveTitle": "Switch workflow?",
|
"switchActiveTitle": "Switch workflow?",
|
||||||
"switchCancel": "Cancel",
|
"switchCancel": "Cancel",
|
||||||
@@ -8226,6 +8449,7 @@ export default interface Resources {
|
|||||||
"backToWorkflowList": "Back to workflows",
|
"backToWorkflowList": "Back to workflows",
|
||||||
"clickToEditDescription": "Click to edit description",
|
"clickToEditDescription": "Click to edit description",
|
||||||
"clickToRename": "Click to rename",
|
"clickToRename": "Click to rename",
|
||||||
|
"closeEditor": "Close workflow editor",
|
||||||
"createDescription": "Description (optional)",
|
"createDescription": "Description (optional)",
|
||||||
"createFailed": "Failed to create workflow",
|
"createFailed": "Failed to create workflow",
|
||||||
"createName": "Name",
|
"createName": "Name",
|
||||||
@@ -8242,7 +8466,9 @@ export default interface Resources {
|
|||||||
"discardConfirm": "Discard",
|
"discardConfirm": "Discard",
|
||||||
"discardMessage": "You have unsaved changes to this workflow. Discard them?",
|
"discardMessage": "You have unsaved changes to this workflow. Discard them?",
|
||||||
"discardTitle": "Discard unsaved changes?",
|
"discardTitle": "Discard unsaved changes?",
|
||||||
|
"duplicateFailed": "Failed to duplicate workflow",
|
||||||
"duplicateToCustomize": "Duplicate to customize",
|
"duplicateToCustomize": "Duplicate to customize",
|
||||||
|
"duplicatedEditable": "Duplicated to \"{{name}}\" — editable",
|
||||||
"emptyDescription": "Workflows orchestrate the steps and gates that run around task execution. Create one to start arranging that flow.",
|
"emptyDescription": "Workflows orchestrate the steps and gates that run around task execution. Create one to start arranging that flow.",
|
||||||
"emptyTitle": "No workflow selected",
|
"emptyTitle": "No workflow selected",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
@@ -8255,11 +8481,14 @@ export default interface Resources {
|
|||||||
"importStripped": "Auto-approval flags were removed from imported nodes",
|
"importStripped": "Auto-approval flags were removed from imported nodes",
|
||||||
"importTooltip": "Import a workflow from a JSON file",
|
"importTooltip": "Import a workflow from a JSON file",
|
||||||
"imported": "Imported workflow \"{{name}}\"",
|
"imported": "Imported workflow \"{{name}}\"",
|
||||||
|
"loadFailed": "Failed to load workflows",
|
||||||
|
"loading": "Loading…",
|
||||||
"migrationNotice": "Your legacy workflow steps were converted — find them as templates in the palette and as the \"Migrated steps\" workflow.",
|
"migrationNotice": "Your legacy workflow steps were converted — find them as templates in the palette and as the \"Migrated steps\" workflow.",
|
||||||
"mobileEditorNav": "Workflow editor sections",
|
"mobileEditorNav": "Workflow editor sections",
|
||||||
"mobileSelectNote": "Select a workflow to edit.",
|
"mobileSelectNote": "Select a workflow to edit.",
|
||||||
"nameLabel": "Workflow name",
|
"nameLabel": "Workflow name",
|
||||||
"newWorkflow": "New workflow",
|
"newWorkflow": "New workflow",
|
||||||
|
"noneYet": "No workflows yet.",
|
||||||
"readOnlyBuiltin": "Read-only built-in workflow",
|
"readOnlyBuiltin": "Read-only built-in workflow",
|
||||||
"saveFailed": "Failed to save workflow",
|
"saveFailed": "Failed to save workflow",
|
||||||
"saved": "Workflow saved",
|
"saved": "Workflow saved",
|
||||||
@@ -8273,7 +8502,8 @@ export default interface Resources {
|
|||||||
"templateNodeCount_other": "{{count}} nodes",
|
"templateNodeCount_other": "{{count}} nodes",
|
||||||
"templatePickerLabel": "Start from",
|
"templatePickerLabel": "Start from",
|
||||||
"templateSectionBuiltin": "Built-in workflows",
|
"templateSectionBuiltin": "Built-in workflows",
|
||||||
"templateSectionYours": "Your workflows"
|
"templateSectionYours": "Your workflows",
|
||||||
|
"title": "Workflows"
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"projectRoot": "Project Root",
|
"projectRoot": "Project Root",
|
||||||
@@ -8519,6 +8749,9 @@ export default interface Resources {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"actions": {
|
||||||
|
"send": "Send"
|
||||||
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
"ratings": {
|
"ratings": {
|
||||||
"trendDeclining": "↓ Declining",
|
"trendDeclining": "↓ Declining",
|
||||||
@@ -8598,6 +8831,9 @@ export default interface Resources {
|
|||||||
"offline": "Offline",
|
"offline": "Offline",
|
||||||
"online": "Online"
|
"online": "Online"
|
||||||
},
|
},
|
||||||
|
"labels": {
|
||||||
|
"name": "Name"
|
||||||
|
},
|
||||||
"merge": {
|
"merge": {
|
||||||
"unknown": "Unknown"
|
"unknown": "Unknown"
|
||||||
},
|
},
|
||||||
@@ -8734,28 +8970,6 @@ export default interface Resources {
|
|||||||
"refreshSourceInitialLoad": "Initial load",
|
"refreshSourceInitialLoad": "Initial load",
|
||||||
"refreshSourceManual": "Manual"
|
"refreshSourceManual": "Manual"
|
||||||
},
|
},
|
||||||
"workflow": {
|
|
||||||
"aggregateAllPassed": "All passed",
|
|
||||||
"aggregateInProgress": "In progress",
|
|
||||||
"aggregateNoResults": "No results",
|
|
||||||
"customWorkflowFallback": "Custom workflow",
|
|
||||||
"defaultWorkflow": "Default",
|
|
||||||
"executionAwaitingCliApproval": "Awaiting CLI approval",
|
|
||||||
"executionAwaitingInput": "Awaiting input",
|
|
||||||
"executionCompleted": "Completed",
|
|
||||||
"executionNotStarted": "Not started",
|
|
||||||
"executionPaused": "Paused",
|
|
||||||
"executionPostMerge": "Post-merge steps running",
|
|
||||||
"executionPreMerge": "Pre-merge steps running",
|
|
||||||
"postMerge": "Post-merge",
|
|
||||||
"preMerge": "Pre-merge",
|
|
||||||
"statusAdvisory": "Advisory failure",
|
|
||||||
"statusFailed": "Failed",
|
|
||||||
"statusPassed": "Passed",
|
|
||||||
"statusRunning": "Running…",
|
|
||||||
"statusSkipped": "Skipped",
|
|
||||||
"waitingForOutput": "Waiting for agent output…"
|
|
||||||
},
|
|
||||||
"workflowNodes": {
|
"workflowNodes": {
|
||||||
"summaryAwaitInput": "Waits for user input",
|
"summaryAwaitInput": "Waits for user input",
|
||||||
"summaryCodeDefault": "TypeScript",
|
"summaryCodeDefault": "TypeScript",
|
||||||
|
|||||||
Reference in New Issue
Block a user