blob: 101c15ac5b8608d114d61a4997e47919bd154c03 [file]
import type {
DeepResearchReportSectionKey,
PermissionMode,
SessionBlockedReason,
SessionStatus,
ThinkingLevel,
UiCatalog,
UiLocale,
} from '@maka/core';
import {
DEEP_RESEARCH_EVIDENCE_CHECKLIST,
DEEP_RESEARCH_PROGRESS_CHECKPOINTS,
DEEP_RESEARCH_REPORT_SECTIONS,
DEEP_RESEARCH_SCOPE_OPTIONS,
DEEP_RESEARCH_STARTER_PROMPTS,
DEEP_RESEARCH_WORKFLOW_STEPS,
} from '@maka/core';
export type DayPeriod = 'morning' | 'noon' | 'afternoon' | 'evening';
type ResearchItem = Readonly<{ title: string; body: string }>;
type ResearchOption = Readonly<{ label: string; body: string }>;
type ResearchStarter = Readonly<{ label: string; prompt: string }>;
export interface ConversationCopy {
empty: {
ariaLabel: string;
greeting: Record<DayPeriod, string>;
greetingTail: Record<DayPeriod, string>;
headlineWithLabel: (greeting: string, label: string) => string;
headlineFallback: (greeting: string, tail: string) => string;
};
deepResearchEmpty: {
ariaLabel: string;
eyebrow: string;
title: string;
intro: string;
workflowAriaLabel: string;
workflow: readonly ResearchItem[];
reportAriaLabel: string;
reportTitle: string;
report: readonly ResearchItem[];
scopeAriaLabel: string;
scopeTitle: string;
scope: readonly ResearchOption[];
evidenceAriaLabel: string;
evidenceTitle: string;
evidence: readonly ResearchItem[];
progressAriaLabel: string;
progressTitle: string;
progress: readonly ResearchItem[];
startersAriaLabel: string;
starters: readonly ResearchStarter[];
};
composer: {
placeholder: string;
textareaAriaLabel: string;
pastedQuoteLabel: string;
selectedSkillsAriaLabel: string;
removeSkillAriaLabel(name: string): string;
awaitingPermission: string;
sending: string;
importing: string;
sendLabel: string;
steerLabel: string;
stopLabel: string;
stopping: string;
streaming: string;
processing: string;
continuing: string;
interruptHint: string;
addContext: string;
/** Noun label for the composer drawer's staged quotes/attachments — the
* collapsed badge, the drawer group's accessible name, and the collapse
* toggle's "收起{label}" interpolation. `addContext` stays the + menu's
* ACTION label; reusing the verb phrase here made the collapsed drawer
* read like a button. */
stagedContext: string;
selectModel: string;
dropToImport: string;
addingAttachment: string;
addFileOrDirectory: string;
/** The + menu entry that opens the `/` Skill menu. */
chooseSkill: string;
/** Why that entry is unavailable: the catalog is empty. */
noSkillsAvailable: string;
switchDisabledStreaming: string;
switchDisabledRunning: string;
switchDisabledPermission: string;
/** Same three lock states as the model switcher, worded for the thinking-level menu beside it. */
thinkingDisabledStreaming: string;
thinkingDisabledRunning: string;
thinkingDisabledPermission: string;
planModeLabel: string;
enablePlanMode: string;
disablePlanMode: string;
planModeOnTitle: string;
swarmModeLabel: string;
enableSwarmMode: string;
disableSwarmMode: string;
swarmModeOnTitle: string;
graphModeLabel: string;
enableGraphMode: string;
disableGraphMode: string;
graphModeOnTitle: string;
/** Inline hint shown above the composer when no model connection exists yet. */
noModelHint: string;
/** Link-button on that hint that opens Settings · 模型. */
noModelAction: string;
/** Explanatory title on the disabled Send button in the no-model state. */
noModelSendTitle: string;
};
model: {
thinkingLevel: string;
thinkingUnsupported: string;
changeThinkingLevel: string;
defaultLevel: string;
level: Record<ThinkingLevel, string>;
switching: string;
model: string;
switchAriaLabel: string;
switchSession: string;
pinnedSession: (connection: string, model: string) => string;
switchTitle: (sessionTitle: string) => string;
newChatAriaLabel: (label: string) => string;
newChatTitle: (label: string) => string;
configureAriaLabel: (label: string) => string;
configureTitle: string;
};
permissions: {
mode: Record<PermissionMode, { label: string; hint: string }>;
modeAriaLabel: (label: string) => string;
};
sandboxBoundary: {
title: string;
access: Record<'read' | 'write', string>;
scope: Record<'exact' | 'subtree', string>;
network: string;
enabled: string;
reject: string;
allowSession: string;
};
questions: {
other: string;
otherDescription: string;
otherAriaLabel: string;
otherPlaceholder: string;
stop: string;
stopping: string;
previous: string;
submitting: string;
submit: string;
next: string;
};
mentions: {
noFiles: string;
noSkills: string;
noCommandsOrSkills: string;
filesAriaLabel: string;
skillsAriaLabel: string;
commandsAndSkillsAriaLabel: string;
loading: string;
};
workspace: {
choose: string;
current: string;
addProject: string;
noProject: string;
relink: string;
chooseTitle: (branch?: string) => string;
chooseAriaLabel: (label: string, branch?: string) => string;
};
messages: {
you: string;
assistant: string;
processing: string;
continuing: string;
/**
* The live turn's rotating working phrases. One is shown at a time beside
* the elapsed clock and swapped every `WORKING_PHRASE_INTERVAL_MS`.
*
* The zh pool is uniform in width on purpose — every entry is 「正在 + two
* characters + …」 — so a swap changes glyphs without moving the elapsed
* time that follows it. en is proportional anyway, so it only keeps the
* entries short.
*/
workingPhrases: readonly string[];
providerRetryScheduled: (seconds: number, attempt: number, maxAttempts: number) => string;
providerRetryStarted: (attempt: number, maxAttempts: number) => string;
safeResumePending: string;
safeResume: string;
thinking: string;
truncated: string;
copied: string;
copying: string;
copyFailed: string;
copy: string;
editMessage: string;
editMessageDisabledRunning: string;
editMessageDisabledAttachments: string;
editMessageDisabledQuotes: string;
editMessageDisabledTransformedText: string;
userAriaLabel: string;
systemAriaLabel: string;
assistantAriaLabel: string;
answerActionsAriaLabel: string;
sourceAriaLabel: string;
derivativesAriaLabel: string;
automationTriggered: string;
automationTitle: (id: string) => string;
goalContinued: string;
goalTitle: (id: string) => string;
agentGraphTriggered: string;
agentGraphTitle: (graphId: string) => string;
thinkingTruncatedTitle: string;
outputTruncatedTitle: string;
removeAttachmentAriaLabel: (name: string) => string;
quoteLabel: string;
quoteExpandAriaLabel: string;
quoteCollapseAriaLabel: string;
removeQuoteAriaLabel: string;
aborted: string;
abortedByStop: string;
};
chat: {
memory: string;
memoryAriaLabel: string;
memoryTitle: string;
deepResearch: string;
deepResearchAriaLabel: string;
deepResearchTitle: string;
deepResearchProgress: {
ariaLabel: string;
title: string;
completedSummary: string;
activeSummary: (stage: string, scope: string, round: number) => string;
handoffTitle: string;
handoffAction: string;
checklistTitle: string;
reportTitle: string;
inspectedTitle: string;
inspectedEmpty: string;
executionTitle: string;
executionSummary: (steps: number, artifacts: number) => string;
workersLabel: string;
noBlockers: string;
sectionLabels: Record<DeepResearchReportSectionKey, string>;
};
clearGoal: (condition: string, iteration: number, max: number, status: string) => string;
clearGoalAriaLabel: (iteration: number, max: number) => string;
goalProgress: (iteration: number, max: number) => string;
goalRunningAriaLabel: string;
loadFailed: string;
loading: string;
retryLoad: string;
quoteSelection: string;
askInSidePanel: string;
noMessages: string;
branchBeforeInterrupt: string;
sessionContextAriaLabel: string;
sessionLineageAriaLabel: string;
titlebarIdentityAriaLabel: string;
openProjectFolder: (name: string) => string;
/** Action phrase appended to the titlebar project crumb's accessible name. */
openProjectFolderAction: string;
/** Tooltip / accessible name for the parent crumb when a linked child is open. */
openParentSession: (name: string) => string;
/** Action phrase appended to the titlebar parent crumb's accessible name. */
openParentSessionAction: string;
sessionContextMore: (count: number) => string;
revisionVersionsAriaLabel: string;
revisionVersion: (current: number, total: number) => string;
previousRevision: string;
nextRevision: string;
};
sessions: {
status: Record<SessionStatus, string>;
blockedReason: Record<SessionBlockedReason, string>;
listAriaLabel: string;
title: string;
showMore: string;
showMoreAriaLabel: (count: number) => string;
renameAriaLabel: string;
/**
* Title of the rename dialog when the subject is a project; a session's
* reuses `renameAriaLabel`, which is the same phrase the sidebar and the
* titlebar already use for it.
*/
renameProjectTitle: string;
/** The rename dialog's submit button. */
renameSubmit: string;
respondingAriaLabel: string;
respondingTitle: string;
staleTitle: string;
staleAriaLabel: string;
stale: string;
unreadAriaLabel: string;
actionsAriaLabel: string;
pin: string;
unpin: string;
rename: string;
archive: string;
unarchive: string;
delete: string;
pinned: string;
/** Time-sort unpinned section title (SideNavSection). */
recent: string;
groupByTime: string;
groupByProject: string;
groupingAriaLabel: string;
projectActionsAriaLabel: (name: string) => string;
projectNewTask: string;
projectRename: string;
projectArchive: string;
projectRestore: string;
projectRelink: string;
projectUnavailable: string;
archivedProjects: string;
archivedProjectsAriaLabel: string;
worktreeAriaLabel: string;
promptRailAriaLabel: string;
emptyPrompt: string;
jumpToPrompt: (preview: string) => string;
};
}
const CONVERSATION_COPY = {
zh: {
empty: {
ariaLabel: '开始对话',
greeting: { morning: '早上好', noon: '中午好', afternoon: '下午好', evening: '晚上好' },
greetingTail: { morning: '清醒的早晨适合理清思路', noon: '专注的午间适合一鼓作气', afternoon: '舒缓的下午适合慢慢推进', evening: '安静的夜晚适合深度思考' },
headlineWithLabel: (greeting, label) => `${greeting} ${label},今天想做点什么?`, headlineFallback: (greeting, tail) => `${greeting},${tail}。`,
},
deepResearchEmpty: {
ariaLabel: '深度研究空会话', eyebrow: '深度研究 · 只读探索', title: '先把项目读透,再决定怎么改。', intro: '这个会话固定在只读权限:优先阅读、搜索和分析代码;需要动手实现时,先输出文件、风险和验证命令。',
workflowAriaLabel: '深度研究流程', workflow: DEEP_RESEARCH_WORKFLOW_STEPS,
reportAriaLabel: '深度研究输出结构', reportTitle: '输出必须能直接落地', report: DEEP_RESEARCH_REPORT_SECTIONS,
scopeAriaLabel: '深度研究范围', scopeTitle: '默认按标准深度研究', scope: DEEP_RESEARCH_SCOPE_OPTIONS,
evidenceAriaLabel: '深度研究证据清单', evidenceTitle: '每次研究都要留证据', evidence: DEEP_RESEARCH_EVIDENCE_CHECKLIST,
progressAriaLabel: '深度研究检查点', progressTitle: '多步研究要按检查点推进', progress: DEEP_RESEARCH_PROGRESS_CHECKPOINTS,
startersAriaLabel: '深度研究起手式', starters: DEEP_RESEARCH_STARTER_PROMPTS,
},
composer: {
placeholder: '描述任务,@ 引用文件,/ 选择技能…', textareaAriaLabel: '消息输入框', pastedQuoteLabel: '粘贴的文本', selectedSkillsAriaLabel: '已选择的 Skill', removeSkillAriaLabel: (name) => `移除 Skill:${name}`, awaitingPermission: '等待你确认权限…',
sending: '正在发送…', importing: '正在导入…', sendLabel: '发送', steerLabel: '插入消息', stopLabel: '停止', stopping: '停止中…',
streaming: 'Maka 正在回答…', processing: 'Maka 正在处理…', continuing: 'Maka 继续中…',
interruptHint: '或点停止中断', addContext: '添加上下文', stagedContext: '附加内容',
selectModel: '选择模型', dropToImport: '松开以导入文件内容', addingAttachment: '正在添加附件', addFileOrDirectory: '添加文件或目录',
chooseSkill: '选择技能', noSkillsAvailable: '当前没有可用技能',
switchDisabledStreaming: '当前对话正在流式输出,等结束后再切换模型。', switchDisabledRunning: '当前对话正在运行,等结束后再切换模型。', switchDisabledPermission: '当前有工具调用正在等待确认,处理后再切换模型。',
thinkingDisabledStreaming: '当前对话正在流式输出,等结束后再切换思考级别。', thinkingDisabledRunning: '当前对话正在运行,等结束后再切换思考级别。', thinkingDisabledPermission: '当前有工具调用正在等待确认,处理后再切换思考级别。',
planModeLabel: 'Plan', enablePlanMode: '开启 Plan Mode', disablePlanMode: '退出 Plan Mode',
planModeOnTitle: 'Plan 模式已启用,点击关闭',
swarmModeLabel: 'Swarm', enableSwarmMode: '开启 Swarm Mode', disableSwarmMode: '退出 Swarm Mode',
swarmModeOnTitle: 'Swarm 模式已启用,点击关闭',
graphModeLabel: 'Graph', enableGraphMode: '开启 Graph Mode', disableGraphMode: '退出 Graph Mode',
graphModeOnTitle: 'Graph 模式已启用,点击关闭',
noModelHint: '还没有可用的模型连接,无法发送。', noModelAction: '前往模型设置', noModelSendTitle: '先添加一个模型连接才能发送。',
},
model: {
thinkingLevel: '思考级别', thinkingUnsupported: '当前模型不支持思考级别切换', changeThinkingLevel: '切换当前模型的思考级别', defaultLevel: '模型默认',
// Short single-token labels — trigger + popout size to content.
// Canonical ladder: 默认 / 关 / 低 / 中 / 高 / 超高 (minimal/max when offered).
level: { off: '关', minimal: '最少', low: '低', medium: '中', high: '高', xhigh: '超高', max: '最高' },
switching: '切换中', model: '模型', switchAriaLabel: '切换当前会话模型', switchSession: '切换当前会话使用的模型',
pinnedSession: (connection, model) => `本会话固定模型:${connection} · ${model}`,
switchTitle: (title) => `${title}。设置里的默认模型只影响新建会话;这里会更新当前会话。`,
newChatAriaLabel: (label) => `选择新对话模型,当前 ${label}`, newChatTitle: (label) => `新对话使用的模型:${label}`,
configureAriaLabel: (label) => `配置模型连接,当前 ${label}`, configureTitle: '配置模型连接',
},
permissions: {
mode: {
explore: { label: '只读', hint: '只读搜索,不写文件、不上网;需要时先问你。' },
ask: { label: '自动', hint: '保护层内自动执行,越权先问你。' },
execute: { label: '自动执行', hint: '常见工具直接执行;危险操作仍会确认。' },
bypass: { label: '完全权限', hint: '直接访问文件和网络,仅限可信任务。' },
},
modeAriaLabel: (label) => `权限模式:${label}`,
},
sandboxBoundary: {
title: '允许访问工作区以外的内容?',
access: { read: '读取', write: '写入' },
scope: { exact: '仅此路径', subtree: '目录及子目录' },
network: '网络访问',
enabled: '已启用',
reject: '拒绝',
allowSession: '本会话允许',
},
questions: { other: '其他', otherDescription: '输入一个不同的答案。', otherAriaLabel: '其他答案', otherPlaceholder: '输入你的答案', stop: '停止', stopping: '停止中…', previous: '上一题', submitting: '正在提交…', submit: '提交答案', next: '下一题' },
mentions: { noFiles: '未找到文件', noSkills: '暂无技能', noCommandsOrSkills: '没有匹配的命令或技能', filesAriaLabel: '工作区文件', skillsAriaLabel: '技能', commandsAndSkillsAriaLabel: '命令和技能', loading: '加载中…' },
workspace: {
choose: '选择项目', current: '当前项目', addProject: '添加项目', noProject: '无项目', relink: '重新定位',
chooseTitle: (branch) => branch ? `选择项目 · ${branch}` : '选择项目',
chooseAriaLabel: (label, branch) => branch ? `选择项目:${label},当前分支 ${branch}` : `选择项目:${label}`,
},
messages: {
you: '你', assistant: 'Maka', processing: '正在处理…', continuing: '继续中…', workingPhrases: ['正在琢磨…', '正在推敲…', '正在盘算…', '正在钻研…', '正在忙活…', '正在梳理…', '正在打磨…', '正在鼓捣…', '正在酝酿…', '正在攻坚…', '正在权衡…', '正在拾掇…'], providerRetryScheduled: (seconds, attempt, maxAttempts) => `${seconds} 秒后重试(${attempt}/${maxAttempts})`, providerRetryStarted: (attempt, maxAttempts) => `正在重试(${attempt}/${maxAttempts})`, safeResumePending: '正在验证…', safeResume: '安全恢复', thinking: '深度思考', truncated: '已截断', copied: '已复制', copying: '复制中', copyFailed: '复制失败', copy: '复制', editMessage: '编辑并重发', editMessageDisabledRunning: '当前回答仍在进行中,结束后再编辑', editMessageDisabledAttachments: '包含附件的历史消息暂不支持编辑并重发', editMessageDisabledQuotes: '包含引用的历史消息暂不支持编辑并重发', editMessageDisabledTransformedText: '通过显式技能发送的历史消息暂不支持编辑并重发',
userAriaLabel: '你发送的消息', systemAriaLabel: '系统消息', assistantAriaLabel: 'Maka 的回答', answerActionsAriaLabel: '本轮回答操作', sourceAriaLabel: '本轮回答的来源', derivativesAriaLabel: '本轮回答的衍生', automationTriggered: '定时任务触发', automationTitle: (id) => `由定时任务触发 · ${id}`, goalContinued: 'Goal 自动继续', goalTitle: (id) => `由 Goal 继续执行 · ${id}`, agentGraphTriggered: 'Agent Graph 自动继续', agentGraphTitle: (graphId) => `由 Agent Graph 调度器触发 · ${graphId}`,
thinkingTruncatedTitle: '部分 reasoning 已截断;显示的是最近的内容', outputTruncatedTitle: '助手输出已超过单次回合上限,超出部分未渲染。如需完整内容请重新生成或查看持久化的会话日志。', removeAttachmentAriaLabel: (name) => `移除 ${name}`, quoteLabel: '引用', quoteExpandAriaLabel: '展开引用全文', quoteCollapseAriaLabel: '收起引用', removeQuoteAriaLabel: '移除引用', aborted: '(已中断)', abortedByStop: '(已中断 · 由停止按钮触发)',
},
chat: {
memory: '记忆', memoryAriaLabel: '本地记忆已启用', memoryTitle: '本地 MEMORY.md 已加入 agent 系统提示。点击进入设置 · 记忆管理。', deepResearch: '深度研究', deepResearchAriaLabel: '深度研究,只读探索', deepResearchTitle: '深度研究会话使用只读探索边界:先阅读和分析,默认不改文件。',
deepResearchProgress: {
ariaLabel: '深度研究实时进度',
title: '研究进度',
completedSummary: '研究完成 · 原会话保持只读',
activeSummary: (stage, scope, round) => `${stage} · ${scope} · 第 ${round} 轮`,
handoffTitle: '新建普通任务并填入研究 handoff;不会自动发送,也不会改变原研究会话权限',
handoffAction: '在新任务中继续实现',
checklistTitle: '检查清单',
reportTitle: '报告草稿',
inspectedTitle: '已检查位置',
inspectedEmpty: '等待记录文件、符号或来源。',
executionTitle: '执行与阻塞',
executionSummary: (steps, artifacts) => `${steps} 个研究步骤 · ${artifacts} 个持久化证据`,
workersLabel: 'Workers',
noBlockers: '当前无阻塞。',
sectionLabels: {
conclusion: '结论',
source_evidence: '证据',
borrow_diverge_risk_gate: '取舍与风险',
implementation_recommendations: '实施建议',
verification: '验证',
},
},
clearGoal: (condition, iteration, max, status) => `自主执行目标进行中:「${condition}」(第 ${iteration}/${max} 轮,${status})。系统每轮后自动续行;点击可清除目标、停止续行。`, clearGoalAriaLabel: (iteration, max) => `清除自主执行目标(已进行 ${iteration}/${max} 轮)`, goalProgress: (iteration, max) => `目标 ${iteration} / ${max}`, goalRunningAriaLabel: '自主目标正在运行',
loadFailed: '对话载入失败', loading: '载入中…', retryLoad: '重试载入', quoteSelection: '引用', askInSidePanel: '在侧栏追问', noMessages: '暂无消息',
branchBeforeInterrupt: '从中断前分支', sessionContextAriaLabel: '会话上下文', sessionLineageAriaLabel: '会话来源', sessionContextMore: (count) => `更多会话上下文(${count})`,
titlebarIdentityAriaLabel: '当前会话', openProjectFolder: (name) => `在文件管理器中打开「${name}」`, openProjectFolderAction: '打开项目文件夹',
openParentSession: (name) => `返回父会话「${name}」`, openParentSessionAction: '打开父会话',
revisionVersionsAriaLabel: '对话版本', revisionVersion: (current, total) => `版本 ${current} / ${total}`, previousRevision: '查看上一版本', nextRevision: '查看下一版本',
},
sessions: {
status: { active: '可继续', running: '进行中', waiting_for_user: '等你确认', blocked: '需要处理', review: '待审核', done: '已完成', archived: '已归档', aborted: '已中止' },
blockedReason: { NO_REAL_CONNECTION: '等待配置可用模型连接', auth: '需要重新登录', permission_required: '等待权限确认', tool_failed: '工具调用失败', unknown: '运行中断,可重试' },
listAriaLabel: '对话列表', title: '会话', showMore: '显示更多', showMoreAriaLabel: (count) => `显示 ${count} 条更多对话`, renameAriaLabel: '重命名对话', renameProjectTitle: '重命名项目', renameSubmit: '保存', respondingAriaLabel: '正在响应', respondingTitle: '对话正在流式响应中', staleTitle: '此会话使用的模型连接已不可用,发送时会切换到默认连接', staleAriaLabel: '会话已过期', stale: '已过期', unreadAriaLabel: '未读消息', actionsAriaLabel: '对话操作', pin: '置顶', unpin: '取消置顶', rename: '重命名', archive: '归档', unarchive: '取消归档', delete: '删除', pinned: '置顶', recent: '最近', groupByTime: '按时间', groupByProject: '按项目', groupingAriaLabel: '会话分组方式', projectActionsAriaLabel: (name) => `${name} 项目操作`, projectNewTask: '新建任务', projectRename: '重命名', projectArchive: '归档', projectRestore: '恢复', projectRelink: '重新定位', projectUnavailable: '项目目录不可用', archivedProjects: '已归档项目', archivedProjectsAriaLabel: '展开已归档项目', worktreeAriaLabel: 'Git 工作树', promptRailAriaLabel: '按提问跳转', emptyPrompt: '(空提问)', jumpToPrompt: (preview) => `跳到提问:${preview}`,
},
},
en: {
empty: {
ariaLabel: 'Start a conversation',
greeting: { morning: 'Good morning', noon: 'Good afternoon', afternoon: 'Good afternoon', evening: 'Good evening' },
greetingTail: { morning: 'A clear morning is good for untangling ideas', noon: 'A focused midday is good for a single big push', afternoon: 'A calm afternoon is good for steady progress', evening: 'A quiet evening is good for deep thinking' },
headlineWithLabel: (greeting, label) => `${greeting} ${label} — what shall we tackle today?`, headlineFallback: (greeting, tail) => `${greeting} — ${tail}.`,
},
deepResearchEmpty: {
ariaLabel: 'Empty Deep Research conversation', eyebrow: 'Deep Research · Read-only exploration', title: 'Understand the project before deciding what to change.', intro: 'This conversation stays read only: inspect, search, and analyze first. When implementation is needed, report the files, risks, and verification commands.',
workflowAriaLabel: 'Deep Research workflow', workflow: [
{ title: 'Find the entry points', body: 'Read the directory layout, configuration, startup path, and test entry points to build a project map.' },
{ title: 'Trace the data flow', body: 'Follow key modules through IPC, storage, permissions, and runtime boundaries to the real implementation.' },
{ title: 'Compare references', body: 'Break each reusable idea into borrow / diverge / risk / gate.' },
{ title: 'Propose a mergeable plan', body: 'List files, risk boundaries, and verification commands without changing files in read-only mode.' },
],
reportAriaLabel: 'Deep Research report structure', reportTitle: 'The report must be actionable', report: [
{ title: 'Lead with conclusions', body: 'Use three to five points to explain the current state, major gaps, and priorities.' },
{ title: 'Cite source evidence', body: 'Name files, functions, configuration, tests, and runtime paths instead of relying on impressions.' },
{ title: 'Break down what to borrow', body: 'Describe each idea as borrow / diverge / risk / gate.' },
{ title: 'Make it implementable', body: 'Give a small-step file plan, boundaries, and verification commands.' },
],
scopeAriaLabel: 'Deep Research scope', scopeTitle: 'Standard depth by default', scope: [
{ label: 'Quick', body: 'Scan entry points, key files, and the likeliest data flow for a narrowly scoped question.' },
{ label: 'Standard', body: 'Trace the core path, related tests, and major risks before recommending changes.' },
{ label: 'Deep', body: 'Run multi-pass investigation across modules, references, and edge cases only when explicitly requested.' },
],
evidenceAriaLabel: 'Deep Research evidence checklist', evidenceTitle: 'Leave evidence for every investigation', evidence: [
{ title: 'Project entry points', body: 'Check the README, package/config files, startup scripts, and directory layers to confirm how the project runs.' },
{ title: 'Core path', body: 'Trace UI entry points, IPC/services, storage, runtime calls, and error handling.' },
{ title: 'Boundaries', body: 'Check permissions, privacy mode, token/path exposure, retries, and user-visible feedback.' },
{ title: 'Verification evidence', body: 'Find tests, fixtures, smoke documentation, and reproducible commands; call out missing evidence.' },
],
progressAriaLabel: 'Deep Research checkpoints', progressTitle: 'Advance multi-step research through checkpoints', progress: [
{ title: 'Build a checklist', body: 'When the scope has more than three related areas, list verifiable checks before tracing code.' },
{ title: 'Mark the current check', body: 'State what is being verified and move on only after collecting evidence.' },
{ title: 'Record blockers', body: 'Mark missing source, runtime, or test evidence as blocked instead of guessing.' },
{ title: 'Converge on a plan', body: 'Roll completed checks into borrow / diverge / risk / gate and actionable improvements.' },
],
startersAriaLabel: 'Deep Research starters', starters: [
{ label: 'Research a reference project', prompt: 'Read this project without changing files. Map its structure, core modules, startup path, data flow, and tests; then list reusable design ideas, risks, and an implementation order for Maka.' },
{ label: 'Read a reference project end to end', prompt: 'Perform a deep, read-only study of this project. Map modules and trace core features, runtime, storage, permissions, UI, tests, and docs. Express each idea as borrow / diverge / risk / gate and recommend an implementation order for Maka.' },
{ label: 'Compare a feature implementation', prompt: 'Compare this feature in the reference project and Maka without changing files. Identify key files, runtime boundaries, UI entry points, persistence, tests, and the smallest mergeable improvement.' },
{ label: 'Audit security boundaries', prompt: 'Audit this feature read only: permissions, token and secret flow, IPC/renderer exposure, file paths, privacy mode, logs, and telemetry. Report blocking risks and corresponding contract tests.' },
],
},
composer: {
placeholder: 'Describe a task, @ to reference files, / for skills…', textareaAriaLabel: 'Message input', pastedQuoteLabel: 'Pasted text', selectedSkillsAriaLabel: 'Selected Skills', removeSkillAriaLabel: (name) => `Remove Skill: ${name}`, awaitingPermission: 'Waiting for your permission decision…',
sending: 'Sending…', importing: 'Importing…', sendLabel: 'Send', steerLabel: 'Steer', stopLabel: 'Stop', stopping: 'Stopping…',
streaming: 'Maka is responding…', processing: 'Maka is working…', continuing: 'Maka is continuing…',
interruptHint: 'or click Stop to interrupt', addContext: 'Add context', stagedContext: 'staged items',
selectModel: 'Choose model', dropToImport: 'Drop to import file contents', addingAttachment: 'Adding attachment', addFileOrDirectory: 'Add file or directory',
chooseSkill: 'Choose skills', noSkillsAvailable: 'No skills available',
switchDisabledStreaming: 'Wait for the current response to finish before switching models.', switchDisabledRunning: 'Wait for the current run to finish before switching models.', switchDisabledPermission: 'Resolve the pending tool permission before switching models.',
thinkingDisabledStreaming: 'Wait for the current response to finish before changing the thinking level.', thinkingDisabledRunning: 'Wait for the current run to finish before changing the thinking level.', thinkingDisabledPermission: 'Resolve the pending tool permission before changing the thinking level.',
planModeLabel: 'Plan', enablePlanMode: 'Enable Plan Mode', disablePlanMode: 'Disable Plan Mode',
planModeOnTitle: 'Plan mode is on — click to turn off',
swarmModeLabel: 'Swarm', enableSwarmMode: 'Enable Swarm Mode', disableSwarmMode: 'Disable Swarm Mode',
swarmModeOnTitle: 'Swarm mode is on — click to turn off',
graphModeLabel: 'Graph', enableGraphMode: 'Enable Graph Mode', disableGraphMode: 'Disable Graph Mode',
graphModeOnTitle: 'Graph mode is on — click to turn off',
noModelHint: 'No model connection yet, so sending is unavailable.', noModelAction: 'Go to model settings', noModelSendTitle: 'Add a model connection before sending.',
},
model: {
thinkingLevel: 'Thinking level', thinkingUnsupported: 'This model does not support thinking-level changes', changeThinkingLevel: 'Change the current model thinking level', defaultLevel: 'Model default',
level: { off: 'Off', minimal: 'Minimal', low: 'Low', medium: 'Medium', high: 'High', xhigh: 'Extra high', max: 'Maximum' },
switching: 'Switching', model: 'Model', switchAriaLabel: 'Switch model for this conversation', switchSession: 'Switch the model used by this conversation',
pinnedSession: (connection, model) => `Model fixed for this conversation: ${connection} · ${model}`,
switchTitle: (title) => `${title}. The default model in Settings affects only new conversations; this updates the current conversation.`,
newChatAriaLabel: (label) => `Choose a model for the new conversation, currently ${label}`, newChatTitle: (label) => `Model for the new conversation: ${label}`,
configureAriaLabel: (label) => `Configure model connections, currently ${label}`, configureTitle: 'Configure model connections',
},
permissions: {
mode: {
explore: { label: 'Read only', hint: 'Read and search only; asks before write or network.' },
ask: { label: 'Auto', hint: "Runs inside Maka's protection; asks before going further." },
execute: { label: 'Auto execute', hint: 'Common tools run; risky actions still confirm.' },
bypass: { label: 'Full access', hint: 'Direct file and network access. Trust-only tasks.' },
},
modeAriaLabel: (label) => `Permission mode: ${label}`,
},
sandboxBoundary: {
title: 'Allow access outside the workspace?',
access: { read: 'Read', write: 'Write' },
scope: { exact: 'Exact path', subtree: 'Directory subtree' },
network: 'Network access',
enabled: 'Enabled',
reject: 'Reject',
allowSession: 'Allow for this session',
},
questions: { other: 'Other', otherDescription: 'Enter a different answer.', otherAriaLabel: 'Other answer', otherPlaceholder: 'Enter your answer', stop: 'Stop', stopping: 'Stopping…', previous: 'Previous', submitting: 'Submitting…', submit: 'Submit answers', next: 'Next' },
mentions: { noFiles: 'No files found', noSkills: 'No skills available', noCommandsOrSkills: 'No matching commands or skills', filesAriaLabel: 'Workspace files', skillsAriaLabel: 'Skills', commandsAndSkillsAriaLabel: 'Commands and skills', loading: 'Loading…' },
workspace: {
choose: 'Choose project', current: 'Current project', addProject: 'Add project', noProject: 'No project', relink: 'Relink',
chooseTitle: (branch) => branch ? `Choose project · ${branch}` : 'Choose project',
chooseAriaLabel: (label, branch) => branch ? `Choose project: ${label}, current branch ${branch}` : `Choose project: ${label}`,
},
messages: {
you: 'You', assistant: 'Maka', processing: 'Working…', continuing: 'Continuing…', workingPhrases: ['Pondering…', 'Tinkering…', 'Untangling…', 'Digging in…', 'Mulling…', 'Chewing on it…', 'Wrangling…', 'Piecing it together…'], providerRetryScheduled: (seconds, attempt, maxAttempts) => `Retrying in ${seconds}s (${attempt}/${maxAttempts})`, providerRetryStarted: (attempt, maxAttempts) => `Retrying (${attempt}/${maxAttempts})`, safeResumePending: 'Checking…', safeResume: 'Safe recovery', thinking: 'Thinking', truncated: 'Truncated', copied: 'Copied', copying: 'Copying', copyFailed: 'Copy failed', copy: 'Copy', editMessage: 'Edit & resend', editMessageDisabledRunning: 'Wait for this answer to finish before editing', editMessageDisabledAttachments: 'Edit & resend does not yet support messages with attachments', editMessageDisabledQuotes: 'Edit & resend does not yet support messages with quotes', editMessageDisabledTransformedText: 'Edit & resend does not yet support messages sent with an explicit skill',
userAriaLabel: 'Your message', systemAriaLabel: 'System message', assistantAriaLabel: "Maka's response", answerActionsAriaLabel: 'Response actions', sourceAriaLabel: 'Source of this response', derivativesAriaLabel: 'Responses derived from this one', automationTriggered: 'Triggered by automation', automationTitle: (id) => `Triggered by automation · ${id}`, goalContinued: 'Continued by Goal', goalTitle: (id) => `Continued by Goal · ${id}`, agentGraphTriggered: 'Continued by Agent Graph', agentGraphTitle: (graphId) => `Triggered by the Agent Graph scheduler · ${graphId}`,
thinkingTruncatedTitle: 'Some reasoning was truncated; showing the most recent content', outputTruncatedTitle: 'The assistant output exceeded the per-turn limit. Regenerate it or inspect the persisted session log for the complete content.', removeAttachmentAriaLabel: (name) => `Remove ${name}`, quoteLabel: 'Quote', quoteExpandAriaLabel: 'Show the full quoted excerpt', quoteCollapseAriaLabel: 'Collapse the quoted excerpt', removeQuoteAriaLabel: 'Remove quote', aborted: '(Interrupted)', abortedByStop: '(Interrupted · Stop button)',
},
chat: {
memory: 'Memory', memoryAriaLabel: 'Local memory enabled', memoryTitle: 'Local MEMORY.md is included in the agent system prompt. Click to manage it in Settings · Memory.', deepResearch: 'Deep Research', deepResearchAriaLabel: 'Deep Research, read-only exploration', deepResearchTitle: 'Deep Research uses a read-only boundary: inspect and analyze first, without changing files by default.',
deepResearchProgress: {
ariaLabel: 'Live Deep Research progress',
title: 'Research progress',
completedSummary: 'Research complete · Original session remains read-only',
activeSummary: (stage, scope, round) => `${stage} · ${scope} · Round ${round}`,
handoffTitle: 'Create a normal task with the research handoff. It will not send automatically or change the original research session permissions.',
handoffAction: 'Continue implementation in a new task',
checklistTitle: 'Checklist',
reportTitle: 'Report draft',
inspectedTitle: 'Inspected locations',
inspectedEmpty: 'Waiting for recorded files, symbols, or sources.',
executionTitle: 'Execution and blockers',
executionSummary: (steps, artifacts) => `${steps} research steps · ${artifacts} persisted evidence items`,
workersLabel: 'Workers',
noBlockers: 'No current blockers.',
sectionLabels: {
conclusion: 'Conclusion',
source_evidence: 'Evidence',
borrow_diverge_risk_gate: 'Tradeoffs and risks',
implementation_recommendations: 'Implementation recommendations',
verification: 'Verification',
},
},
clearGoal: (condition, iteration, max, status) => `Autonomous goal in progress: “${condition}” (iteration ${iteration}/${max}, ${status}). Maka continues after each iteration; click to clear the goal and stop continuing.`, clearGoalAriaLabel: (iteration, max) => `Clear autonomous goal after ${iteration}/${max} iterations`, goalProgress: (iteration, max) => `Goal ${iteration} of ${max}`, goalRunningAriaLabel: 'Autonomous goal running',
loadFailed: 'Conversation failed to load', loading: 'Loading…', retryLoad: 'Retry', quoteSelection: 'Quote', askInSidePanel: 'Ask in side panel', noMessages: 'No messages yet',
branchBeforeInterrupt: 'Branched before interruption', sessionContextAriaLabel: 'Session context', sessionLineageAriaLabel: 'Session origin', sessionContextMore: (count) => `More session context (${count})`,
titlebarIdentityAriaLabel: 'Current conversation', openProjectFolder: (name) => `Open “${name}” in the file manager`, openProjectFolderAction: 'Open project folder',
openParentSession: (name) => `Return to parent session “${name}”`, openParentSessionAction: 'Open parent session',
revisionVersionsAriaLabel: 'Conversation versions', revisionVersion: (current, total) => `Version ${current} of ${total}`, previousRevision: 'View previous version', nextRevision: 'View next version',
},
sessions: {
status: { active: 'Ready', running: 'Running', waiting_for_user: 'Waiting for you', blocked: 'Needs attention', review: 'Review', done: 'Done', archived: 'Archived', aborted: 'Stopped' },
blockedReason: { NO_REAL_CONNECTION: 'Waiting for an available model connection', auth: 'Sign in again', permission_required: 'Waiting for permission', tool_failed: 'Tool call failed', unknown: 'Run interrupted; retry available' },
listAriaLabel: 'Conversation list', title: 'Conversations', showMore: 'Show more', showMoreAriaLabel: (count) => `Show ${count} more conversations`, renameAriaLabel: 'Rename conversation', renameProjectTitle: 'Rename project', renameSubmit: 'Save', respondingAriaLabel: 'Responding', respondingTitle: 'This conversation is streaming a response', staleTitle: 'This conversation\'s model connection is unavailable; sending will switch to the default connection', staleAriaLabel: 'Stale conversation', stale: 'Stale', unreadAriaLabel: 'Unread messages', actionsAriaLabel: 'Conversation actions', pin: 'Pin', unpin: 'Unpin', rename: 'Rename', archive: 'Archive', unarchive: 'Unarchive', delete: 'Delete', pinned: 'Pinned', recent: 'Recent', groupByTime: 'By time', groupByProject: 'By project', groupingAriaLabel: 'Conversation grouping', projectActionsAriaLabel: (name) => `${name} project actions`, projectNewTask: 'New task', projectRename: 'Rename', projectArchive: 'Archive', projectRestore: 'Restore', projectRelink: 'Relocate', projectUnavailable: 'Project directory unavailable', archivedProjects: 'Archived projects', archivedProjectsAriaLabel: 'Expand archived projects', worktreeAriaLabel: 'Git worktree', promptRailAriaLabel: 'Jump by prompt', emptyPrompt: '(empty prompt)', jumpToPrompt: (preview) => `Jump to prompt: ${preview}`,
},
},
} satisfies UiCatalog<ConversationCopy>;
export function getConversationCopy(locale: UiLocale): ConversationCopy {
return CONVERSATION_COPY[locale];
}