Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/mobile/src/app/(app)/agent-chat/instance-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Button } from '@/components/ui/button';
import { radioItemA11y } from '@/components/ui/radio-group';
import { Skeleton } from '@/components/ui/skeleton';
import { Text } from '@/components/ui/text';
import { stripInlineCodeMarkers } from '@/i18n/plain-copy';
import { formatList } from '@/lib/format';
import { useThemeColors } from '@/lib/hooks/use-theme-colors';
import { type InstancePickerInstance } from '@/lib/picker-bridge';
Expand Down Expand Up @@ -314,7 +315,9 @@ export default function InstancePickerScreen() {
icon={Server}
placement="top"
title={t('agentChat.instancePicker.noCliInstances')}
description={t('agentChat.instancePicker.noCliInstancesDescription')}
description={stripInlineCodeMarkers(
t('agentChat.instancePicker.noCliInstancesDescription')
)}
action={
<Button
variant="outline"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,9 @@ describe('NewSessionConfigureForm', () => {
expect(findTextContent(cloud, t => t.includes('kilo remote') && t.includes('/remote'))).toBe(
true
);
// The help draws the commands as prose: the authoring markers must not
// reach the screen.
expect(findTextContent(cloud, t => t.includes('`'))).toBe(false);

// eslint-disable-next-line new-cap -- plain function call, matching repo test convention
const remote = NewSessionConfigureForm({
Expand All @@ -842,6 +845,7 @@ describe('NewSessionConfigureForm', () => {
expect(findTextContent(remote, t => t.includes('kilo remote') && t.includes('/remote'))).toBe(
true
);
expect(findTextContent(remote, t => t.includes('`'))).toBe(false);
});

// ── Case 14: reorder wiring lock ──
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useComposerRevealScroll } from '@/components/agents/use-composer-reveal
import { AppAwareKeyboardPaddingView } from '@/components/kilo-chat/app-aware-keyboard-padding';
import { SegmentedControl } from '@/components/ui/segmented-control';
import { Text } from '@/components/ui/text';
import { stripInlineCodeMarkers } from '@/i18n/plain-copy';
import { remoteSpawnInstanceDisconnectedNote } from '@/lib/remote-submit-outcome';

/**
Expand Down Expand Up @@ -197,7 +198,7 @@ export function NewSessionConfigureForm({
) : null}

<Text className="mt-2 text-xs text-muted-foreground">
{t('agentChat.newSession.remoteHint')}
{stripInlineCodeMarkers(t('agentChat.newSession.remoteHint'))}
</Text>

{runOnNote ? <Text className="mt-2 text-sm text-muted-foreground">{runOnNote}</Text> : null}
Expand Down
3 changes: 2 additions & 1 deletion apps/mobile/src/components/tour/tour-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button';
import { ChoiceRow } from '@/components/ui/choice-row';
import { Cloud, type LucideIcon, Monitor, Sparkles } from '@/components/ui/icons';
import { Text } from '@/components/ui/text';
import { stripInlineCodeMarkers } from '@/i18n/plain-copy';
import { useCurrentUserId } from '@/lib/hooks/use-current-user-id';
import { useThemeColors } from '@/lib/hooks/use-theme-colors';
import { useStackSafeReplace } from '@/lib/navigation/stack-safe-replace';
Expand Down Expand Up @@ -103,7 +104,7 @@ function ForkStep({ onChoose }: Readonly<ForkStepProps>) {
<ForkOption
icon={Monitor}
title={t('tour.remoteOptionTitle')}
body={t('tour.remoteOptionBody')}
body={stripInlineCodeMarkers(t('tour.remoteOptionBody'))}
onPress={() => {
onChoose('remote');
}}
Expand Down
6 changes: 3 additions & 3 deletions apps/mobile/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2197,7 +2197,7 @@
"discardDraftMessage": "Your prompt will be lost.",
"discardWithUploadsMessage": "Your prompt and any unclaimed uploads will be deleted.",
"discardFailed": "Could not discard the draft. Please try again.",
"remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process.",
"remoteHint": "Run kilo remote on your computer, or /remote in a running CLI session, to control a local kilo process.",
"continueFrom": "Continuing {{title}}",
"importingSession": "Importing session",
"cliCannotContinue": "This CLI cannot continue a session. Pick Cloud Agent or another CLI.",
Expand Down Expand Up @@ -2255,7 +2255,7 @@
"cloudAgent": "Cloud Agent",
"cloudAgentDescription": "Run on Kilo's cloud sandbox",
"noCliInstances": "No CLI instances connected",
"noCliInstancesDescription": "Run `kilo remote` in a project on your computer, or update Kilo CLI if one is already running.",
"noCliInstancesDescription": "Run kilo remote in a project on your computer, or update Kilo CLI if one is already running.",
"instanceOnProject": "{{name}} on {{project}}",
"instanceOnProjectSuffix": "{{name}} on {{project}} ({{suffix}})",
"runOnCloudAgent": "Run on Cloud Agent"
Expand Down Expand Up @@ -3186,7 +3186,7 @@
"cloudOptionTitle": "$t(agentChat.instancePicker.cloudAgent)",
"cloudOptionBody": "Run Kilo in a sandboxed cloud environment with nothing to install.",
"remoteOptionTitle": "Your computer",
"remoteOptionBody": "Run Kilo on your own machine through the `kilo remote` CLI.",
"remoteOptionBody": "Run Kilo on your own machine through the kilo remote CLI.",
"skip": "Skip tour",
"tutorialLabel": "Tutorial"
},
Expand Down
52 changes: 52 additions & 0 deletions apps/mobile/src/i18n/plain-copy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { describe, expect, it } from 'vitest';

import { CATALOG_LOADERS } from './catalogs';
import { SUPPORTED_LANGUAGES } from './languages';
import { stripInlineCodeMarkers } from './plain-copy';

/**
* The run-location help the new-task screen, the tour's fork card and the
* instance picker show. Every catalog used to wrap `kilo remote` and `/remote`
* in backticks, and the native `Text` drew the punctuation.
*/
const RUN_LOCATION_COPY_KEYS: string[][] = [
['agentChat', 'newSession', 'remoteHint'],
['agentChat', 'instancePicker', 'noCliInstancesDescription'],
['tour', 'remoteOptionBody'],
];

function valueAt(catalog: unknown, path: string[]): unknown {
let node: unknown = catalog;
for (const key of path) {
node = (node as Record<string, unknown> | undefined)?.[key];
}
return node;
}

describe('stripInlineCodeMarkers', () => {
it('drops the backticks and keeps the command text', () => {
expect(stripInlineCodeMarkers('Run `kilo remote`, or `/remote` in a CLI session.')).toBe(
'Run kilo remote, or /remote in a CLI session.'
);
});

it('leaves copy without markers unchanged', () => {
expect(stripInlineCodeMarkers('Run kilo remote on your computer.')).toBe(
'Run kilo remote on your computer.'
);
});
});

describe('run-location help copy', () => {
it.each(SUPPORTED_LANGUAGES)('%s never renders an authoring marker', tag => {
const catalog = CATALOG_LOADERS[tag]();

for (const path of RUN_LOCATION_COPY_KEYS) {
const value = valueAt(catalog, path);
expect(typeof value, `${tag} ${path.join('.')}`).toBe('string');
const visible = stripInlineCodeMarkers(value as string);
expect(visible, `${tag} ${path.join('.')}`).not.toContain('`');
expect(visible, `${tag} ${path.join('.')}`).toContain('kilo remote');
}
});
});
9 changes: 9 additions & 0 deletions apps/mobile/src/i18n/plain-copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Plain copy for a native `Text`. The catalogs mark commands and paths with
* backticks, and nothing in the app renders that markup, so the reader sees the
* punctuation itself. English ships without markers; the translation slice
* removes them from the other catalogs, and this keeps those readable first.
*/
export function stripInlineCodeMarkers(value: string): string {
return value.replaceAll('`', '');
}
Loading