Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aebfa7ec3 | ||
|
|
ea340ef141 |
@@ -38,6 +38,15 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
|||||||
locale under `assets/i18n/<locale>/`. A new language is a drop-in folder;
|
locale under `assets/i18n/<locale>/`. A new language is a drop-in folder;
|
||||||
en_US behaviour is unchanged. (T-462)
|
en_US behaviour is unchanged. (T-462)
|
||||||
|
|
||||||
|
## [2.8.1] — 2026-06-22
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Localized the remaining Claude-pane strings.** Running-indicator verbs, the
|
||||||
|
primary/secondary session titles and banner role, tool-card titles, the
|
||||||
|
step/edit/agent counters, and the folded-activity ticker now resolve through
|
||||||
|
the catalog — completing i18n coverage of the conversation surface. (T-462)
|
||||||
|
|
||||||
## [2.7.1] — 2026-06-18
|
## [2.7.1] — 2026-06-18
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -6,13 +6,37 @@
|
|||||||
"status.primary-exited": { "translation": "session exited — restart clide to retry" },
|
"status.primary-exited": { "translation": "session exited — restart clide to retry" },
|
||||||
"banner.title": { "translation": "Claude" },
|
"banner.title": { "translation": "Claude" },
|
||||||
"banner.warmingUp": { "translation": "Warming up — your conversation will appear here." },
|
"banner.warmingUp": { "translation": "Warming up — your conversation will appear here." },
|
||||||
|
"banner.role.primary": { "translation": "primary" },
|
||||||
|
"banner.role.secondary": { "translation": "session {index}" },
|
||||||
"composer.hint": { "translation": "Message Claude… (Enter to send · Shift+Enter for newline)" },
|
"composer.hint": { "translation": "Message Claude… (Enter to send · Shift+Enter for newline)" },
|
||||||
"composer.stop": { "translation": "Stop ⎋" },
|
"composer.stop": { "translation": "Stop ⎋" },
|
||||||
"composer.stop.hint": { "translation": "Interrupt the running turn (Escape)" },
|
"composer.stop.hint": { "translation": "Interrupt the running turn (Escape)" },
|
||||||
"composer.removeAttachment": { "translation": "Remove {name}" },
|
"composer.removeAttachment": { "translation": "Remove {name}" },
|
||||||
|
"pane.title.primary": { "translation": "claude — primary" },
|
||||||
|
"pane.title.secondary": { "translation": "claude — secondary {index}" },
|
||||||
"pane.starting": { "translation": "starting…" },
|
"pane.starting": { "translation": "starting…" },
|
||||||
"pane.modeBadge.semantics": { "translation": "permission mode: {mode}" },
|
"pane.modeBadge.semantics": { "translation": "permission mode: {mode}" },
|
||||||
"running.semantics": { "translation": "Claude is running" },
|
"running.semantics": { "translation": "Claude is running" },
|
||||||
|
"running.verb.pondering": { "translation": "Pondering" },
|
||||||
|
"running.verb.conjuring": { "translation": "Conjuring" },
|
||||||
|
"running.verb.brewing": { "translation": "Brewing" },
|
||||||
|
"running.verb.tinkering": { "translation": "Tinkering" },
|
||||||
|
"running.verb.noodling": { "translation": "Noodling" },
|
||||||
|
"running.verb.percolating": { "translation": "Percolating" },
|
||||||
|
"running.verb.computing": { "translation": "Computing" },
|
||||||
|
"running.verb.wrangling": { "translation": "Wrangling" },
|
||||||
|
"running.verb.untangling": { "translation": "Untangling" },
|
||||||
|
"running.verb.synthesizing": { "translation": "Synthesizing" },
|
||||||
|
"running.verb.cogitating": { "translation": "Cogitating" },
|
||||||
|
"running.verb.whirring": { "translation": "Whirring" },
|
||||||
|
"running.verb.mincing": { "translation": "Mincing" },
|
||||||
|
"running.verb.boiling": { "translation": "Boiling" },
|
||||||
|
"running.verb.humming": { "translation": "Humming" },
|
||||||
|
"running.verb.buzzing": { "translation": "Buzzing" },
|
||||||
|
"running.verb.magicking": { "translation": "Magicking" },
|
||||||
|
"running.verb.cliding": { "translation": "Cliding" },
|
||||||
|
"running.verb.zooming": { "translation": "Zooming" },
|
||||||
|
"running.verb.bouncing": { "translation": "Bouncing" },
|
||||||
"conversation.empty": { "translation": "Waiting for Claude…" },
|
"conversation.empty": { "translation": "Waiting for Claude…" },
|
||||||
"conversation.label.you": { "translation": "you" },
|
"conversation.label.you": { "translation": "you" },
|
||||||
"conversation.label.claude": { "translation": "claude" },
|
"conversation.label.claude": { "translation": "claude" },
|
||||||
@@ -39,6 +63,12 @@
|
|||||||
"conversation.bashTail.label": { "translation": "live tail" },
|
"conversation.bashTail.label": { "translation": "live tail" },
|
||||||
"conversation.cluster.activity": { "translation": "Activity" },
|
"conversation.cluster.activity": { "translation": "Activity" },
|
||||||
"conversation.cluster.edits": { "translation": "Edits" },
|
"conversation.cluster.edits": { "translation": "Edits" },
|
||||||
|
"conversation.counter.step": { "translation": "1 step" },
|
||||||
|
"conversation.counter.steps": { "translation": "{count} steps" },
|
||||||
|
"conversation.counter.edit": { "translation": "1 edit" },
|
||||||
|
"conversation.counter.edits": { "translation": "{count} edits" },
|
||||||
|
"conversation.counter.starting": { "translation": "starting" },
|
||||||
|
"conversation.counter.agents": { "translation": "{done}/{total} agents" },
|
||||||
"prompt.permission.allow": { "translation": "1. Allow" },
|
"prompt.permission.allow": { "translation": "1. Allow" },
|
||||||
"prompt.permission.allowRemember": { "translation": "2. Allow & don't ask again" },
|
"prompt.permission.allowRemember": { "translation": "2. Allow & don't ask again" },
|
||||||
"prompt.permission.deny": { "translation": "{n}. Deny" },
|
"prompt.permission.deny": { "translation": "{n}. Deny" },
|
||||||
@@ -62,6 +92,16 @@
|
|||||||
"tool.edit.before": { "translation": "— before" },
|
"tool.edit.before": { "translation": "— before" },
|
||||||
"tool.edit.after": { "translation": "+ after" },
|
"tool.edit.after": { "translation": "+ after" },
|
||||||
"tool.bash.background": { "translation": "background" },
|
"tool.bash.background": { "translation": "background" },
|
||||||
|
"tool.name.Read": { "translation": "Read" },
|
||||||
|
"tool.name.Edit": { "translation": "Edit" },
|
||||||
|
"tool.name.MultiEdit": { "translation": "MultiEdit" },
|
||||||
|
"tool.name.Write": { "translation": "Write" },
|
||||||
|
"tool.name.NotebookEdit": { "translation": "NotebookEdit" },
|
||||||
|
"tool.name.WebFetch": { "translation": "WebFetch" },
|
||||||
|
"tool.name.WebSearch": { "translation": "WebSearch" },
|
||||||
|
"tool.name.Task": { "translation": "Task" },
|
||||||
|
"tool.name.TodoWrite": { "translation": "TodoWrite" },
|
||||||
|
"tool.name.ExitPlanMode": { "translation": "ExitPlanMode" },
|
||||||
"permissionControl.semantics": { "translation": "permission mode: {mode}. Activate to change." },
|
"permissionControl.semantics": { "translation": "permission mode: {mode}. Activate to change." },
|
||||||
"permissionControl.tooltip": { "translation": "Permission mode: {mode} — change (Ctrl/Cmd+M cycles)" },
|
"permissionControl.tooltip": { "translation": "Permission mode: {mode} — change (Ctrl/Cmd+M cycles)" },
|
||||||
"permissionBadge.tooltip": { "translation": "Permission mode: {mode}. Click to cycle default/acceptEdits/plan; Shift-click for bypassPermissions." },
|
"permissionBadge.tooltip": { "translation": "Permission mode: {mode}. Click to cycle default/acceptEdits/plan; Shift-click for bypassPermissions." },
|
||||||
|
|||||||
@@ -6,13 +6,37 @@
|
|||||||
"status.primary-exited": { "translation": "sessie beëindigd — herstart clide om opnieuw te proberen" },
|
"status.primary-exited": { "translation": "sessie beëindigd — herstart clide om opnieuw te proberen" },
|
||||||
"banner.title": { "translation": "Claude" },
|
"banner.title": { "translation": "Claude" },
|
||||||
"banner.warmingUp": { "translation": "Opstarten — je gesprek verschijnt hier." },
|
"banner.warmingUp": { "translation": "Opstarten — je gesprek verschijnt hier." },
|
||||||
|
"banner.role.primary": { "translation": "primair" },
|
||||||
|
"banner.role.secondary": { "translation": "sessie {index}" },
|
||||||
"composer.hint": { "translation": "Bericht aan Claude… (Enter om te verzenden · Shift+Enter voor een nieuwe regel)" },
|
"composer.hint": { "translation": "Bericht aan Claude… (Enter om te verzenden · Shift+Enter voor een nieuwe regel)" },
|
||||||
"composer.stop": { "translation": "Stop ⎋" },
|
"composer.stop": { "translation": "Stop ⎋" },
|
||||||
"composer.stop.hint": { "translation": "Onderbreek de lopende beurt (Escape)" },
|
"composer.stop.hint": { "translation": "Onderbreek de lopende beurt (Escape)" },
|
||||||
"composer.removeAttachment": { "translation": "{name} verwijderen" },
|
"composer.removeAttachment": { "translation": "{name} verwijderen" },
|
||||||
|
"pane.title.primary": { "translation": "claude — primair" },
|
||||||
|
"pane.title.secondary": { "translation": "claude — secundair {index}" },
|
||||||
"pane.starting": { "translation": "starten…" },
|
"pane.starting": { "translation": "starten…" },
|
||||||
"pane.modeBadge.semantics": { "translation": "permissiemodus: {mode}" },
|
"pane.modeBadge.semantics": { "translation": "permissiemodus: {mode}" },
|
||||||
"running.semantics": { "translation": "Claude is bezig" },
|
"running.semantics": { "translation": "Claude is bezig" },
|
||||||
|
"running.verb.pondering": { "translation": "Peinzen" },
|
||||||
|
"running.verb.conjuring": { "translation": "Toveren" },
|
||||||
|
"running.verb.brewing": { "translation": "Brouwen" },
|
||||||
|
"running.verb.tinkering": { "translation": "Knutselen" },
|
||||||
|
"running.verb.noodling": { "translation": "Prutsen" },
|
||||||
|
"running.verb.percolating": { "translation": "Pruttelen" },
|
||||||
|
"running.verb.computing": { "translation": "Rekenen" },
|
||||||
|
"running.verb.wrangling": { "translation": "Worstelen" },
|
||||||
|
"running.verb.untangling": { "translation": "Ontwarren" },
|
||||||
|
"running.verb.synthesizing": { "translation": "Synthetiseren" },
|
||||||
|
"running.verb.cogitating": { "translation": "Overpeinzen" },
|
||||||
|
"running.verb.whirring": { "translation": "Snorren" },
|
||||||
|
"running.verb.mincing": { "translation": "Hakken" },
|
||||||
|
"running.verb.boiling": { "translation": "Koken" },
|
||||||
|
"running.verb.humming": { "translation": "Neuriën" },
|
||||||
|
"running.verb.buzzing": { "translation": "Zoemen" },
|
||||||
|
"running.verb.magicking": { "translation": "Goochelen" },
|
||||||
|
"running.verb.cliding": { "translation": "Cliden" },
|
||||||
|
"running.verb.zooming": { "translation": "Zoeven" },
|
||||||
|
"running.verb.bouncing": { "translation": "Stuiteren" },
|
||||||
"conversation.empty": { "translation": "Wachten op Claude…" },
|
"conversation.empty": { "translation": "Wachten op Claude…" },
|
||||||
"conversation.label.you": { "translation": "jij" },
|
"conversation.label.you": { "translation": "jij" },
|
||||||
"conversation.label.claude": { "translation": "claude" },
|
"conversation.label.claude": { "translation": "claude" },
|
||||||
@@ -39,6 +63,12 @@
|
|||||||
"conversation.bashTail.label": { "translation": "live tail" },
|
"conversation.bashTail.label": { "translation": "live tail" },
|
||||||
"conversation.cluster.activity": { "translation": "Activiteit" },
|
"conversation.cluster.activity": { "translation": "Activiteit" },
|
||||||
"conversation.cluster.edits": { "translation": "Wijzigingen" },
|
"conversation.cluster.edits": { "translation": "Wijzigingen" },
|
||||||
|
"conversation.counter.step": { "translation": "1 stap" },
|
||||||
|
"conversation.counter.steps": { "translation": "{count} stappen" },
|
||||||
|
"conversation.counter.edit": { "translation": "1 wijziging" },
|
||||||
|
"conversation.counter.edits": { "translation": "{count} wijzigingen" },
|
||||||
|
"conversation.counter.starting": { "translation": "starten" },
|
||||||
|
"conversation.counter.agents": { "translation": "{done}/{total} agents" },
|
||||||
"prompt.permission.allow": { "translation": "1. Toestaan" },
|
"prompt.permission.allow": { "translation": "1. Toestaan" },
|
||||||
"prompt.permission.allowRemember": { "translation": "2. Toestaan en niet meer vragen" },
|
"prompt.permission.allowRemember": { "translation": "2. Toestaan en niet meer vragen" },
|
||||||
"prompt.permission.deny": { "translation": "{n}. Weigeren" },
|
"prompt.permission.deny": { "translation": "{n}. Weigeren" },
|
||||||
@@ -62,6 +92,16 @@
|
|||||||
"tool.edit.before": { "translation": "— voor" },
|
"tool.edit.before": { "translation": "— voor" },
|
||||||
"tool.edit.after": { "translation": "+ na" },
|
"tool.edit.after": { "translation": "+ na" },
|
||||||
"tool.bash.background": { "translation": "achtergrond" },
|
"tool.bash.background": { "translation": "achtergrond" },
|
||||||
|
"tool.name.Read": { "translation": "Lezen" },
|
||||||
|
"tool.name.Edit": { "translation": "Bewerken" },
|
||||||
|
"tool.name.MultiEdit": { "translation": "Meervoudig bewerken" },
|
||||||
|
"tool.name.Write": { "translation": "Schrijven" },
|
||||||
|
"tool.name.NotebookEdit": { "translation": "Notebook bewerken" },
|
||||||
|
"tool.name.WebFetch": { "translation": "Web ophalen" },
|
||||||
|
"tool.name.WebSearch": { "translation": "Web zoeken" },
|
||||||
|
"tool.name.Task": { "translation": "Taak" },
|
||||||
|
"tool.name.TodoWrite": { "translation": "Takenlijst" },
|
||||||
|
"tool.name.ExitPlanMode": { "translation": "Planmodus verlaten" },
|
||||||
"permissionControl.semantics": { "translation": "permissiemodus: {mode}. Activeer om te wijzigen." },
|
"permissionControl.semantics": { "translation": "permissiemodus: {mode}. Activeer om te wijzigen." },
|
||||||
"permissionControl.tooltip": { "translation": "Permissiemodus: {mode} — wijzigen (Ctrl/Cmd+M wisselt)" },
|
"permissionControl.tooltip": { "translation": "Permissiemodus: {mode} — wijzigen (Ctrl/Cmd+M wisselt)" },
|
||||||
"permissionBadge.tooltip": { "translation": "Permissiemodus: {mode}. Klik om te wisselen tussen default/acceptEdits/plan; Shift-klik voor bypassPermissions." },
|
"permissionBadge.tooltip": { "translation": "Permissiemodus: {mode}. Klik om te wisselen tussen default/acceptEdits/plan; Shift-klik voor bypassPermissions." },
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ self:
|
|||||||
# Auto-synced from pubspec.yaml `version:` by `make gen-build-info`
|
# Auto-synced from pubspec.yaml `version:` by `make gen-build-info`
|
||||||
# (runs implicitly on every build/run/test). Don't hand-edit; bump
|
# (runs implicitly on every build/run/test). Don't hand-edit; bump
|
||||||
# pubspec instead.
|
# pubspec instead.
|
||||||
version: "2.8.0"
|
version: "2.8.1"
|
||||||
homepage: https://github.com/postmeridiem/clide
|
homepage: https://github.com/postmeridiem/clide
|
||||||
license: MIT
|
license: MIT
|
||||||
license_file: assets/LICENSE
|
license_file: assets/LICENSE
|
||||||
|
|||||||
@@ -728,7 +728,15 @@ class _ClaudePaneState extends State<ClaudePane> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final title = widget.isPrimary ? 'claude — primary' : 'claude — secondary ${widget.secondaryIndex}';
|
final title = widget.isPrimary
|
||||||
|
? ClideSettings.i18n.string(context, 'pane.title.primary', namespace: 'builtin.claude', placeholder: 'claude — primary')
|
||||||
|
: ClideSettings.i18n.interpolated(
|
||||||
|
context,
|
||||||
|
'pane.title.secondary',
|
||||||
|
namespace: 'builtin.claude',
|
||||||
|
placeholder: 'claude — secondary ${widget.secondaryIndex}',
|
||||||
|
replacers: [I18nReplacer(from: '{index}', replace: '${widget.secondaryIndex}')],
|
||||||
|
);
|
||||||
final tokens = ClideSettings.theme.of(context).surface;
|
final tokens = ClideSettings.theme.of(context).surface;
|
||||||
|
|
||||||
final Widget body;
|
final Widget body;
|
||||||
@@ -761,7 +769,15 @@ class _ClaudePaneState extends State<ClaudePane> {
|
|||||||
quietErrorToolUseIds: _session?.quietErrorToolUseIds ?? const <String>{},
|
quietErrorToolUseIds: _session?.quietErrorToolUseIds ?? const <String>{},
|
||||||
workflows: _session?.workflows ?? const <String, WorkflowRun>{},
|
workflows: _session?.workflows ?? const <String, WorkflowRun>{},
|
||||||
emptyState: ClaudeBanner(
|
emptyState: ClaudeBanner(
|
||||||
role: widget.isPrimary ? 'primary' : 'session ${widget.secondaryIndex}',
|
role: widget.isPrimary
|
||||||
|
? ClideSettings.i18n.string(context, 'banner.role.primary', namespace: 'builtin.claude', placeholder: 'primary')
|
||||||
|
: ClideSettings.i18n.interpolated(
|
||||||
|
context,
|
||||||
|
'banner.role.secondary',
|
||||||
|
namespace: 'builtin.claude',
|
||||||
|
placeholder: 'session ${widget.secondaryIndex}',
|
||||||
|
replacers: [I18nReplacer(from: '{index}', replace: '${widget.secondaryIndex}')],
|
||||||
|
),
|
||||||
workspace: _repoRoot,
|
workspace: _repoRoot,
|
||||||
statusLine: _statusLine,
|
statusLine: _statusLine,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -800,10 +800,10 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
final outcome = toolUseOutcomes[t.toolUseId];
|
final outcome = toolUseOutcomes[t.toolUseId];
|
||||||
final color = outcome == null ? tokens.globalFocus : (outcome ? tokens.statusSuccess : tokens.statusError);
|
final color = outcome == null ? tokens.globalFocus : (outcome ? tokens.statusSuccess : tokens.statusError);
|
||||||
final collapser = ClideCollapserCard(
|
final collapser = ClideCollapserCard(
|
||||||
label: t.name,
|
label: _toolNameLabel(context, t.name),
|
||||||
color: color,
|
color: color,
|
||||||
collapsedSummary: _toolUseSummary(t),
|
collapsedSummary: _toolUseSummary(t),
|
||||||
counter: '1 step',
|
counter: _stepsCounter(context, 1),
|
||||||
status: _toolRunStatus(t),
|
status: _toolRunStatus(t),
|
||||||
children: [_toolContentCard(context, t)],
|
children: [_toolContentCard(context, t)],
|
||||||
);
|
);
|
||||||
@@ -818,8 +818,8 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(left: 12),
|
padding: const EdgeInsets.only(left: 12),
|
||||||
child: ClideCollapserCard(
|
child: ClideCollapserCard(
|
||||||
label: ClideSettings.i18n.string(context, 'conversation.label.agentRun', namespace: 'builtin.claude', placeholder: 'agent run'),
|
label: ClideSettings.i18n.string(context, 'conversation.label.agentRun', namespace: 'builtin.claude', placeholder: 'agent run'),
|
||||||
collapsedSummary: _summarizeActivity(runItems.last),
|
collapsedSummary: _summarizeActivity(context, runItems.last),
|
||||||
counter: runItems.length == 1 ? '1 step' : '${runItems.length} steps',
|
counter: _stepsCounter(context, runItems.length),
|
||||||
children: [
|
children: [
|
||||||
for (final r in runItems)
|
for (final r in runItems)
|
||||||
_ConversationTurn(
|
_ConversationTurn(
|
||||||
@@ -849,7 +849,18 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
Widget _workflowCard(BuildContext context, AssistantToolUse t, WorkflowRun run) {
|
Widget _workflowCard(BuildContext context, AssistantToolUse t, WorkflowRun run) {
|
||||||
final title = run.name ?? ClideSettings.i18n.string(context, 'conversation.label.workflow', namespace: 'builtin.claude', placeholder: 'workflow');
|
final title = run.name ?? ClideSettings.i18n.string(context, 'conversation.label.workflow', namespace: 'builtin.claude', placeholder: 'workflow');
|
||||||
final color = run.done ? tokens.statusSuccess : tokens.globalFocus;
|
final color = run.done ? tokens.statusSuccess : tokens.globalFocus;
|
||||||
final counter = run.agentCount == 0 ? 'starting' : '${run.doneCount}/${run.agentCount} agents';
|
final counter = run.agentCount == 0
|
||||||
|
? ClideSettings.i18n.string(context, 'conversation.counter.starting', namespace: 'builtin.claude', placeholder: 'starting')
|
||||||
|
: ClideSettings.i18n.interpolated(
|
||||||
|
context,
|
||||||
|
'conversation.counter.agents',
|
||||||
|
namespace: 'builtin.claude',
|
||||||
|
placeholder: '${run.doneCount}/${run.agentCount} agents',
|
||||||
|
replacers: [
|
||||||
|
I18nReplacer(from: '{done}', replace: '${run.doneCount}'),
|
||||||
|
I18nReplacer(from: '{total}', replace: '${run.agentCount}'),
|
||||||
|
],
|
||||||
|
);
|
||||||
final detail = run.done ? (run.summary ?? run.description) : run.description;
|
final detail = run.done ? (run.summary ?? run.description) : run.description;
|
||||||
final collapsedSummary = (detail == null || detail == title) ? title : '$title · $detail';
|
final collapsedSummary = (detail == null || detail == title) ? title : '$title · $detail';
|
||||||
return ClideCollapserCard(
|
return ClideCollapserCard(
|
||||||
@@ -990,7 +1001,7 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
variant: ConversationCardVariant.bordered,
|
variant: ConversationCardVariant.bordered,
|
||||||
accent: accent,
|
accent: accent,
|
||||||
borderColor: outcome == null ? null : accent,
|
borderColor: outcome == null ? null : accent,
|
||||||
label: t.name,
|
label: _toolNameLabel(context, t.name),
|
||||||
copyText: const JsonEncoder.withIndent(' ').convert(t.input),
|
copyText: const JsonEncoder.withIndent(' ').convert(t.input),
|
||||||
status: status,
|
status: status,
|
||||||
body: toolInputBody(context, tokens, t.name, t.input, mono),
|
body: toolInputBody(context, tokens, t.name, t.input, mono),
|
||||||
@@ -1047,7 +1058,7 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
variant: ConversationCardVariant.bordered,
|
variant: ConversationCardVariant.bordered,
|
||||||
accent: quiet ? tokens.globalTextMuted : accent,
|
accent: quiet ? tokens.globalTextMuted : accent,
|
||||||
borderColor: quiet ? tokens.panelBorder : tokens.statusError,
|
borderColor: quiet ? tokens.panelBorder : tokens.statusError,
|
||||||
label: paired != null ? '${paired.name} · $errLabel' : errLabel,
|
label: paired != null ? '${_toolNameLabel(context, paired.name)} · $errLabel' : errLabel,
|
||||||
copyText: t.content,
|
copyText: t.content,
|
||||||
collapsible: quiet || multiline,
|
collapsible: quiet || multiline,
|
||||||
collapsedByDefault: quiet, // genuine errors stay expanded; a denial folds
|
collapsedByDefault: quiet, // genuine errors stay expanded; a denial folds
|
||||||
@@ -1061,7 +1072,7 @@ class _ConversationTurn extends StatelessWidget {
|
|||||||
// For Write/Edit, the result is usually "OK" — keep it as plain text.
|
// For Write/Edit, the result is usually "OK" — keep it as plain text.
|
||||||
final multiline = t.content.contains('\n');
|
final multiline = t.content.contains('\n');
|
||||||
final isOutputTool = paired != null && const {'Bash', 'Read', 'Grep', 'LS'}.contains(paired.name);
|
final isOutputTool = paired != null && const {'Bash', 'Read', 'Grep', 'LS'}.contains(paired.name);
|
||||||
final resultLabel = paired != null ? '${paired.name} · $label' : label;
|
final resultLabel = paired != null ? '${_toolNameLabel(context, paired.name)} · $label' : label;
|
||||||
return ConversationCard(
|
return ConversationCard(
|
||||||
variant: ConversationCardVariant.bordered,
|
variant: ConversationCardVariant.bordered,
|
||||||
accent: accent,
|
accent: accent,
|
||||||
@@ -1127,8 +1138,8 @@ class _ActivityCard extends StatelessWidget {
|
|||||||
final count = items.length;
|
final count = items.length;
|
||||||
return ClideCollapserCard(
|
return ClideCollapserCard(
|
||||||
label: ClideSettings.i18n.string(context, 'conversation.cluster.activity', namespace: 'builtin.claude', placeholder: 'Activity'),
|
label: ClideSettings.i18n.string(context, 'conversation.cluster.activity', namespace: 'builtin.claude', placeholder: 'Activity'),
|
||||||
collapsedSummary: _summarizeActivity(items.last),
|
collapsedSummary: _summarizeActivity(context, items.last),
|
||||||
counter: count == 1 ? '1 step' : '$count steps',
|
counter: _stepsCounter(context, count),
|
||||||
status: _runStatus(items, resultByToolUseId),
|
status: _runStatus(items, resultByToolUseId),
|
||||||
children: [
|
children: [
|
||||||
for (final item in items)
|
for (final item in items)
|
||||||
@@ -1150,6 +1161,35 @@ class _ActivityCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Localized display label for a tool name (T-462). File/web/task operations
|
||||||
|
/// have natural translations; command/proper-name tools (Bash, Grep, Glob, LS)
|
||||||
|
/// have no catalog key and fall back to the raw name via the placeholder.
|
||||||
|
String _toolNameLabel(BuildContext context, String name) =>
|
||||||
|
ClideSettings.i18n.string(context, 'tool.name.$name', namespace: 'builtin.claude', placeholder: name);
|
||||||
|
|
||||||
|
/// Localized "N steps" counter for a collapser header (T-462). Singular and
|
||||||
|
/// plural are distinct catalog keys; the English forms double as the fallback.
|
||||||
|
String _stepsCounter(BuildContext context, int n) => n == 1
|
||||||
|
? ClideSettings.i18n.string(context, 'conversation.counter.step', namespace: 'builtin.claude', placeholder: '1 step')
|
||||||
|
: ClideSettings.i18n.interpolated(
|
||||||
|
context,
|
||||||
|
'conversation.counter.steps',
|
||||||
|
namespace: 'builtin.claude',
|
||||||
|
placeholder: '$n steps',
|
||||||
|
replacers: [I18nReplacer(from: '{count}', replace: '$n')],
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Localized "N edits" counter for the edit-run collapser header (T-462).
|
||||||
|
String _editsCounter(BuildContext context, int n) => n == 1
|
||||||
|
? ClideSettings.i18n.string(context, 'conversation.counter.edit', namespace: 'builtin.claude', placeholder: '1 edit')
|
||||||
|
: ClideSettings.i18n.interpolated(
|
||||||
|
context,
|
||||||
|
'conversation.counter.edits',
|
||||||
|
namespace: 'builtin.claude',
|
||||||
|
placeholder: '$n edits',
|
||||||
|
replacers: [I18nReplacer(from: '{count}', replace: '$n')],
|
||||||
|
);
|
||||||
|
|
||||||
/// Aggregate live status for a run's header tick (T-296): error if any tool in
|
/// Aggregate live status for a run's header tick (T-296): error if any tool in
|
||||||
/// the run failed, else running while its last tool awaits a result, else
|
/// the run failed, else running while its last tool awaits a result, else
|
||||||
/// success. Null (no tools) shows no indicator.
|
/// success. Null (no tools) shows no indicator.
|
||||||
@@ -1193,8 +1233,8 @@ class _EditRunCard extends StatelessWidget {
|
|||||||
final count = edits.length;
|
final count = edits.length;
|
||||||
return ClideCollapserCard(
|
return ClideCollapserCard(
|
||||||
label: ClideSettings.i18n.string(context, 'conversation.cluster.edits', namespace: 'builtin.claude', placeholder: 'Edits'),
|
label: ClideSettings.i18n.string(context, 'conversation.cluster.edits', namespace: 'builtin.claude', placeholder: 'Edits'),
|
||||||
collapsedSummary: _summarizeActivity(edits.last),
|
collapsedSummary: _summarizeActivity(context, edits.last),
|
||||||
counter: count == 1 ? '1 edit' : '$count edits',
|
counter: _editsCounter(context, count),
|
||||||
status: _runStatus(edits, resultByToolUseId),
|
status: _runStatus(edits, resultByToolUseId),
|
||||||
children: [
|
children: [
|
||||||
for (final item in edits)
|
for (final item in edits)
|
||||||
@@ -1216,22 +1256,25 @@ class _EditRunCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// One-line summary of a folded item for the collapsed ticker.
|
/// One-line summary of a folded item for the collapsed ticker.
|
||||||
String _summarizeActivity(ConversationItem item) {
|
String _summarizeActivity(BuildContext context, ConversationItem item) {
|
||||||
|
String label(String key, String fallback) => ClideSettings.i18n.string(context, key, namespace: 'builtin.claude', placeholder: fallback);
|
||||||
switch (item) {
|
switch (item) {
|
||||||
case AssistantToolUse(:final name, :final input):
|
case AssistantToolUse(:final name, :final input):
|
||||||
final raw = input['command'] ?? input['file_path'] ?? input['path'] ?? input['pattern'] ?? input['url'];
|
final raw = input['command'] ?? input['file_path'] ?? input['path'] ?? input['pattern'] ?? input['url'];
|
||||||
final detail = raw is String ? raw.split('\n').first.trim() : '';
|
final detail = raw is String ? raw.split('\n').first.trim() : '';
|
||||||
final clipped = detail.length > 72 ? '${detail.substring(0, 72)}…' : detail;
|
final clipped = detail.length > 72 ? '${detail.substring(0, 72)}…' : detail;
|
||||||
return clipped.isEmpty ? name : '$name $clipped';
|
final toolName = _toolNameLabel(context, name);
|
||||||
|
return clipped.isEmpty ? toolName : '$toolName $clipped';
|
||||||
case ToolResultMessage(:final isError):
|
case ToolResultMessage(:final isError):
|
||||||
return isError ? '↳ result · error' : '↳ result';
|
final result = label('conversation.label.result', 'result');
|
||||||
|
return isError ? '↳ $result · ${label('conversation.label.error', 'error')}' : '↳ $result';
|
||||||
case AssistantThinkingMessage():
|
case AssistantThinkingMessage():
|
||||||
return 'thinking…';
|
return '${label('conversation.label.thinking', 'thinking')}…';
|
||||||
case UserMessage(:final text):
|
case UserMessage(:final text):
|
||||||
return text;
|
return text;
|
||||||
case AssistantTextMessage(:final text):
|
case AssistantTextMessage(:final text):
|
||||||
return text;
|
return text;
|
||||||
case ImageMessage(:final path):
|
case ImageMessage(:final path):
|
||||||
return 'image $path';
|
return '${label('conversation.label.image', 'image')} $path';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,17 +90,22 @@ class _RunningIndicatorState extends State<RunningIndicator> with SingleTickerPr
|
|||||||
label: ClideSettings.i18n.string(context, 'running.semantics', namespace: 'builtin.claude', placeholder: 'Claude is running'),
|
label: ClideSettings.i18n.string(context, 'running.semantics', namespace: 'builtin.claude', placeholder: 'Claude is running'),
|
||||||
child: ExcludeSemantics(
|
child: ExcludeSemantics(
|
||||||
child: reduced
|
child: reduced
|
||||||
? ClideText('${_verbs.first}…', color: claudeAccent, fontSize: clideFontMeta)
|
? ClideText('${_verb(context, _verbs.first)}…', color: claudeAccent, fontSize: clideFontMeta)
|
||||||
: AnimatedBuilder(
|
: AnimatedBuilder(
|
||||||
animation: _c,
|
animation: _c,
|
||||||
builder: (ctx, _) {
|
builder: (ctx, _) {
|
||||||
final elapsed = _c.value * _periodSeconds;
|
final elapsed = _c.value * _periodSeconds;
|
||||||
final dots = '.' * (elapsed.floor() % 4);
|
final dots = '.' * (elapsed.floor() % 4);
|
||||||
final word = _verbs[(elapsed ~/ _secondsPerWord) % _verbs.length];
|
final word = _verbs[(elapsed ~/ _secondsPerWord) % _verbs.length];
|
||||||
return ClideText('$word$dots', color: claudeAccent, fontSize: clideFontMeta);
|
return ClideText('${_verb(ctx, word)}$dots', color: claudeAccent, fontSize: clideFontMeta);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Localize a verb through the catalog; the English word doubles as the key
|
||||||
|
/// suffix and the fallback, so a missing translation degrades to English.
|
||||||
|
String _verb(BuildContext context, String word) =>
|
||||||
|
ClideSettings.i18n.string(context, 'running.verb.${word.toLowerCase()}', namespace: 'builtin.claude', placeholder: word);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ description: >-
|
|||||||
subsystem handlers (pane, files, editor, git, pql), and the
|
subsystem handlers (pane, files, editor, git, pql), and the
|
||||||
extension framework.
|
extension framework.
|
||||||
publish_to: none
|
publish_to: none
|
||||||
version: 2.8.0
|
version: 2.8.1
|
||||||
repository: https://github.com/postmeridiem/clide
|
repository: https://github.com/postmeridiem/clide
|
||||||
# Short user-facing tagline (the welcome subtitle, web meta
|
# Short user-facing tagline (the welcome subtitle, web meta
|
||||||
# description, etc.). Baked into lib/src/build_info.g.dart by
|
# description, etc.). Baked into lib/src/build_info.g.dart by
|
||||||
|
|||||||
Reference in New Issue
Block a user