diff --git a/apps/extension/entrypoints/sidepanel/agents-new-session.test.ts b/apps/extension/entrypoints/sidepanel/agents-new-session.test.ts index 77ad8b2d87..5c88e09614 100644 --- a/apps/extension/entrypoints/sidepanel/agents-new-session.test.ts +++ b/apps/extension/entrypoints/sidepanel/agents-new-session.test.ts @@ -1,3 +1,4 @@ +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import { describe, expect, it, vi } from 'vitest'; import { @@ -73,8 +74,8 @@ describe('constants', () => { expect(PROMPT_MIN_LENGTH).toBe(3); }); - it('promptMaxLength is 4000', () => { - expect(PROMPT_MAX_LENGTH).toBe(4000); + it('promptMaxLength is the shared cloud agent prompt cap', () => { + expect(PROMPT_MAX_LENGTH).toBe(CLOUD_AGENT_PROMPT_MAX_LENGTH); }); it('mode is "code"', () => { diff --git a/apps/extension/entrypoints/sidepanel/agents-new-session.tsx b/apps/extension/entrypoints/sidepanel/agents-new-session.tsx index 96869881d5..2395304f52 100644 --- a/apps/extension/entrypoints/sidepanel/agents-new-session.tsx +++ b/apps/extension/entrypoints/sidepanel/agents-new-session.tsx @@ -32,6 +32,7 @@ import { fetchModelPreferences } from '@/src/shared/model-preferences-client'; import { isGatewayModelId } from '@/src/shared/model-picker-rows'; import { getModelPreferencesQueryKey } from '@/src/shared/side-panel-query-options'; import { thinkingEffortLabel } from '@/src/shared/kilo-api-client'; +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import type { KiloGatewayModelOption } from '@/src/shared/kilo-api-client'; import { useExtensionAgents } from './agents-provider'; import { activeSessionsQueryKey, sessionHistoryQueryKey } from './agents-session-list'; @@ -43,7 +44,7 @@ import { useGatewayModels } from './use-gateway-models'; // --------------------------------------------------------------------------- const PROMPT_MIN_LENGTH = 3; -const PROMPT_MAX_LENGTH = 4000; +const PROMPT_MAX_LENGTH = CLOUD_AGENT_PROMPT_MAX_LENGTH; const MODE = 'code' as const; /** diff --git a/apps/mobile/AGENTS.md b/apps/mobile/AGENTS.md index 5e259f6f74..36d657321f 100644 --- a/apps/mobile/AGENTS.md +++ b/apps/mobile/AGENTS.md @@ -68,6 +68,7 @@ git diff --check - iOS: never control text with `value` plus state. Store text in a ref via `onChangeText`, use state only for derived UI, read the ref on submit. - Use `defaultValue` only for initial content. - Single-line inputs: use `leading-[normal]`. A `lineHeight` above the font's natural one (which `text-sm`/`text-base` set on their own) makes iOS draw the placeholder lower than the typed text and clip it. Multi-line inputs keep an explicit `leading-*`. +- Single-line inputs: set the height with `min-h-*`, not `py-*`. iOS insets the already-centered text rect by the padding, so vertical padding draws the text and the placeholder low. - Put input screens in a `ScrollView` with `automaticallyAdjustKeyboardInsets`. ## UI and UX Rules diff --git a/apps/mobile/src/components/agents/chat-composer-input-row.tsx b/apps/mobile/src/components/agents/chat-composer-input-row.tsx index edbe988e10..9b878c3949 100644 --- a/apps/mobile/src/components/agents/chat-composer-input-row.tsx +++ b/apps/mobile/src/components/agents/chat-composer-input-row.tsx @@ -1,4 +1,5 @@ import { ArrowUp, Paperclip, Square } from '@/components/ui/icons'; +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import { type RefObject } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -119,7 +120,7 @@ export function ChatComposerInputRow({ placeholder={placeholder} placeholderTextColor={colors.mutedForeground} multiline - maxLength={4000} + maxLength={CLOUD_AGENT_PROMPT_MAX_LENGTH} onChangeText={onChangeText} onFocus={onInputFocus} onBlur={onInputBlur} diff --git a/apps/mobile/src/components/agents/chat-composer.tsx b/apps/mobile/src/components/agents/chat-composer.tsx index 21bcbd5863..cb90212fa9 100644 --- a/apps/mobile/src/components/agents/chat-composer.tsx +++ b/apps/mobile/src/components/agents/chat-composer.tsx @@ -6,6 +6,7 @@ import * as Haptics from 'expo-haptics'; import { useActionSheet } from '@expo/react-native-action-sheet'; import { type SlashCommandInfo } from '@kilocode/cloud-agent-sdk'; +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import { type RemoteCommandState } from '@kilocode/cloud-agent-sdk/remote-command-catalog'; import { type Ref, @@ -474,7 +475,7 @@ export function ChatComposer({ useSharePrefill({ shareId, inputRef, - maxLength: 4000, + maxLength: CLOUD_AGENT_PROMPT_MAX_LENGTH, onChangeText: handleChangeText, addCandidates, onDelivered: () => { @@ -500,7 +501,7 @@ export function ChatComposer({ applyVoiceDraftToInput({ input: inputRef.current, draft, - maxLength: 4000, + maxLength: CLOUD_AGENT_PROMPT_MAX_LENGTH, onChangeText: handleChangeText, }); }, @@ -537,7 +538,7 @@ export function ChatComposer({ input: inputRef.current, draft: textRef.current, selection: selectionRef.current, - maxLength: 4000, + maxLength: CLOUD_AGENT_PROMPT_MAX_LENGTH, onChangeText: handleChangeText, }); }, diff --git a/apps/mobile/src/components/agents/new-session-configure-form.test.ts b/apps/mobile/src/components/agents/new-session-configure-form.test.ts index 48aefec66a..a296fdac63 100644 --- a/apps/mobile/src/components/agents/new-session-configure-form.test.ts +++ b/apps/mobile/src/components/agents/new-session-configure-form.test.ts @@ -548,4 +548,29 @@ describe('NewSessionConfigureForm', () => { expect(findElementByType(element, 'SegmentedControl')).toBeNull(); expect(findTextContent(element, t => t === 'Changes')).toBe(false); }); + + // ── Case 12: kilo remote hint ── + it('names both `kilo remote` and `/remote` for cloud and remote targets', async () => { + const { NewSessionConfigureForm } = await import('./new-session-configure-form'); + + // eslint-disable-next-line new-cap -- plain function call, matching repo test convention + const cloud = NewSessionConfigureForm({ + ...defaultProps(), + runOnInstance: null, + showRunOnSelector: true, + }) as Node; + expect(findTextContent(cloud, t => t.includes('kilo remote') && t.includes('/remote'))).toBe( + true + ); + + // eslint-disable-next-line new-cap -- plain function call, matching repo test convention + const remote = NewSessionConfigureForm({ + ...defaultProps(), + runOnInstance: INSTANCE, + showRunOnSelector: false, + }) as Node; + expect(findTextContent(remote, t => t.includes('kilo remote') && t.includes('/remote'))).toBe( + true + ); + }); }); diff --git a/apps/mobile/src/components/agents/new-session-configure-form.tsx b/apps/mobile/src/components/agents/new-session-configure-form.tsx index 7652de02ea..f5677b4c9c 100644 --- a/apps/mobile/src/components/agents/new-session-configure-form.tsx +++ b/apps/mobile/src/components/agents/new-session-configure-form.tsx @@ -252,6 +252,10 @@ export function NewSessionConfigureForm({ {runTargetBlock} + + {t('agentChat.newSession.remoteHint')} + + {showInstanceDisconnectedNote ? ( {remoteSpawnInstanceDisconnectedNote()} diff --git a/apps/mobile/src/components/agents/new-session-prompt.tsx b/apps/mobile/src/components/agents/new-session-prompt.tsx index b1247b2d67..54abe70388 100644 --- a/apps/mobile/src/components/agents/new-session-prompt.tsx +++ b/apps/mobile/src/components/agents/new-session-prompt.tsx @@ -1,3 +1,4 @@ +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import { useCallback, useEffect, useRef, useState } from 'react'; import { type LayoutChangeEvent, @@ -45,7 +46,7 @@ const PROMPT_INPUT_LINE_HEIGHT = 24; const PROMPT_INPUT_VERTICAL_PADDING = 16; const PROMPT_INPUT_HORIZONTAL_PADDING = Platform.OS === 'android' ? 48 : 16; const PROMPT_INPUT_ANDROID_HORIZONTAL_INSET = 24; -const PROMPT_INPUT_MAX_CHARS = 4000; +const PROMPT_INPUT_MAX_CHARS = CLOUD_AGENT_PROMPT_MAX_LENGTH; const PROMPT_INPUT_MIN_HEIGHT = PROMPT_INPUT_LINE_HEIGHT * PROMPT_INPUT_DEFAULT_LINES + PROMPT_INPUT_VERTICAL_PADDING; const PROMPT_INPUT_MAX_HEIGHT = diff --git a/apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts b/apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts index 3a0d649907..5ad374de1a 100644 --- a/apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts +++ b/apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines -- spawn-input, navigation, and admission suites share the hook harness. */ import * as React from 'react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { type ModelSelection } from '@kilocode/cloud-agent-sdk'; +import { type KiloSessionId, type ModelSelection } from '@kilocode/cloud-agent-sdk'; import { type InstancePickerInstance } from '@/lib/picker-bridge'; import { @@ -9,17 +9,21 @@ import { peekSharePayload, type SharePayload, } from '@/lib/share-payload'; -import { buildCreateRemoteSessionInput } from '@/lib/hooks/remote-instance-spawn-classifier'; +import { + buildCreateRemoteSessionInput, + type CreateSessionOutcome, +} from '@/lib/hooks/remote-instance-spawn-classifier'; import { remoteSpawnFilesNotSupportedToast } from '@/lib/remote-spawn-admission'; +import { remoteSpawnRetryableToast } from '@/lib/remote-submit-outcome'; import { useRemoteSpawnDispatch } from './use-remote-spawn-dispatch'; const spawnMock = vi.hoisted(() => - vi.fn(async () => { + vi.fn(async (): Promise => { await Promise.resolve(); return { status: 'ready' as const, - sessionID: 'ses_12345678901234567890123456', + sessionID: 'ses_12345678901234567890123456' as KiloSessionId, }; }) ); @@ -125,7 +129,11 @@ function runHook(args: { selection?: ModelSelection; getSubmitPayload?: () => SharePayload | null; onSpawnAdmitted?: () => void; + onSpawnFailed?: () => void; runOnInstance?: InstancePickerInstance | null; + setRunOnInstance?: (next: InstancePickerInstance | null) => void; + refetchInstances?: () => Promise<{ data: { instances: InstancePickerInstance[] } | undefined }>; + instanceList?: InstancePickerInstance[]; }) { const reactInternals = React as typeof React & ReactInternals; const hookState: unknown[] = []; @@ -170,30 +178,50 @@ function runHook(args: { }, }; - const previousDispatcher = - reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H; - hookIndex = 0; - refIndex = 0; - reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H = dispatcher; - try { - const mountDispatch = useRemoteSpawnDispatch; - return mountDispatch({ - organizationId: args.organizationId, - mode: args.mode, - selection: args.selection, - runOnInstance: args.runOnInstance === undefined ? INSTANCE : args.runOnInstance, - // eslint-disable-next-line no-empty-function -- no-op setter for harness - setRunOnInstance: (_next: InstancePickerInstance | null) => {}, - // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules - refetchInstances: () => Promise.resolve({ data: { instances: [INSTANCE] } }), - instanceList: [INSTANCE], - getSubmitPayload: args.getSubmitPayload, - onSpawnAdmitted: args.onSpawnAdmitted, - }); - } finally { - reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H = - previousDispatcher; - } + // `runOnInstance` is parent state in the real route: `setRunOnInstance` + // re-renders the hook with the new value so the `runOnInstanceRef` effect + // sees it. Mirror that here; otherwise the async tail's remap would leave + // the ref on the stale press-time id and the reset guard (which reads the + // ref) could never be exercised by a remap test. + let currentRunOnInstance: InstancePickerInstance | null = + args.runOnInstance === undefined ? INSTANCE : args.runOnInstance; + + const render = () => { + hookIndex = 0; + refIndex = 0; + const previousDispatcher = + reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H; + reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H = dispatcher; + try { + const mountDispatch = useRemoteSpawnDispatch; + return mountDispatch({ + organizationId: args.organizationId, + mode: args.mode, + selection: args.selection, + runOnInstance: currentRunOnInstance, + setRunOnInstance: next => { + args.setRunOnInstance?.(next); + if (next !== currentRunOnInstance) { + currentRunOnInstance = next; + render(); + } + }, + refetchInstances: + args.refetchInstances ?? + // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules + (() => Promise.resolve({ data: { instances: [INSTANCE] } })), + instanceList: args.instanceList ?? [INSTANCE], + getSubmitPayload: args.getSubmitPayload, + onSpawnAdmitted: args.onSpawnAdmitted, + onSpawnFailed: args.onSpawnFailed, + }); + } finally { + reactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H = + previousDispatcher; + } + }; + + return render(); } describe('useRemoteSpawnDispatch spawn input chain', () => { @@ -428,3 +456,131 @@ describe('useRemoteSpawnDispatch spawn input chain', () => { expect(spawnMock).not.toHaveBeenCalled(); }); }); + +describe('useRemoteSpawnDispatch live-instance remap', () => { + const LIVE_INSTANCE: InstancePickerInstance = { + connectionId: 'conn-live', + name: 'laptop', + projectName: 'kilo', + }; + + beforeEach(() => { + spawnMock.mockClear(); + useRemoteInstanceSpawnMock.mockClear(); + routerReplace.mockClear(); + toastErrorMock.mockClear(); + __resetSharePayloadStoreForTests(); + }); + + it('spawns with the same connectionId when the refetched list still has it', async () => { + const { onStart } = runHook({ + organizationId: 'org-xyz', + // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules + refetchInstances: () => Promise.resolve({ data: { instances: [INSTANCE] } }), + }); + + expect(await captureSpawnCall(onStart)).toEqual([ + 'conn-abc', + { orgId: 'org-xyz' }, + { operationKey: expect.any(String) }, + ]); + }); + + it('remaps to the live connectionId when the id changed but name + project match', async () => { + const setRunOnInstanceMock = vi.fn(); + const { onStart } = runHook({ + organizationId: 'org-xyz', + setRunOnInstance: next => { + setRunOnInstanceMock(next); + }, + // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules + refetchInstances: () => Promise.resolve({ data: { instances: [LIVE_INSTANCE] } }), + }); + + expect(await captureSpawnCall(onStart)).toEqual([ + 'conn-live', + { orgId: 'org-xyz' }, + { operationKey: expect.any(String) }, + ]); + expect(setRunOnInstanceMock).toHaveBeenCalledWith(LIVE_INSTANCE); + }); + + it('falls back to the last-known instanceList when the refetch throws', async () => { + const { onStart } = runHook({ + organizationId: 'org-xyz', + // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules + refetchInstances: () => Promise.reject(new Error('network down')), + instanceList: [INSTANCE], + }); + + expect(await captureSpawnCall(onStart)).toEqual([ + 'conn-abc', + { orgId: 'org-xyz' }, + { operationKey: expect.any(String) }, + ]); + }); + + it('keeps the live selection when a remap is followed by a failing post-spawn refetch', async () => { + const setRunOnInstanceMock = vi.fn(); + // First (pre-spawn) refetch resolves the live row so the id remaps; the + // second (post-spawn) refetch fails. + const refetchInstancesMock = vi + .fn() + .mockResolvedValueOnce({ data: { instances: [LIVE_INSTANCE] } }) + .mockRejectedValueOnce(new Error('network down')); + spawnMock.mockResolvedValueOnce({ + status: 'retryable', + reason: 'transport failure', + cause: new Error('socket gone'), + }); + + const { onStart } = runHook({ + organizationId: 'org-xyz', + setRunOnInstance: next => { + setRunOnInstanceMock(next); + }, + refetchInstances: refetchInstancesMock, + instanceList: [INSTANCE], + }); + + onStart(); + await vi.waitFor(() => { + expect(refetchInstancesMock).toHaveBeenCalledTimes(2); + }); + // Flush the rejected-refetch continuation (outcome classification and the + // reset guard) before asserting the selection did not move to null. + await new Promise(resolve => { + setTimeout(resolve, 0); + }); + + // The remap applied the live row... + expect(setRunOnInstanceMock).toHaveBeenCalledWith(LIVE_INSTANCE); + // ...and the failing refetch must not reset the selection to Cloud Agent. + expect(setRunOnInstanceMock).toHaveBeenCalledTimes(1); + expect(setRunOnInstanceMock).not.toHaveBeenCalledWith(null); + }); + + it('toasts the retryable copy and calls onSpawnFailed when no live instance resolves', async () => { + const onSpawnFailedMock = vi.fn(); + const setRunOnInstanceMock = vi.fn(); + const { onStart } = runHook({ + organizationId: 'org-xyz', + setRunOnInstance: next => { + setRunOnInstanceMock(next); + }, + // eslint-disable-next-line promise-function-async, prefer-await-to-then -- tension between lint rules + refetchInstances: () => Promise.resolve({ data: { instances: [] } }), + onSpawnFailed: () => { + onSpawnFailedMock(); + }, + }); + + onStart(); + await vi.waitFor(() => { + expect(onSpawnFailedMock).toHaveBeenCalledTimes(1); + }); + expect(toastErrorMock).toHaveBeenCalledWith(remoteSpawnRetryableToast()); + expect(spawnMock).not.toHaveBeenCalled(); + expect(setRunOnInstanceMock).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts b/apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts index 904c4b9b72..75b4359d27 100644 --- a/apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts +++ b/apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts @@ -14,6 +14,7 @@ import { useRemoteInstanceSpawn, } from '@/lib/hooks/use-remote-instance-spawn'; import { useHoistedOperationKey } from '@/lib/operation-key'; +import { resolveLiveInstance } from '@/lib/resolve-live-instance'; import { remoteSpawnNonRetryableToast, remoteSpawnRetryableToast, @@ -158,6 +159,10 @@ export function useRemoteSpawnDispatch({ ) => Promise; } = useRemoteInstanceSpawn(organizationId ?? null); const [showInstanceDisconnectedNote, setShowInstanceDisconnectedNote] = useState(false); + // `true` while `onStart`'s async head refetches the instance list and + // resolves the live instance (a rebooted host's new connectionId). Surfaced + // so the route keeps the Start button disabled during that window. + const [isResolvingInstance, setIsResolvingInstance] = useState(false); // P1-A-08b: one `operationKey` per spawn intent, so a retryable failure keeps // the key and the relay dedupes the retry. const { getKey, rotateKey } = useHoistedOperationKey(); @@ -193,7 +198,6 @@ export function useRemoteSpawnDispatch({ if (runOnInstance === null) { return; } - const selectedConnectionId = runOnInstance.connectionId; const fields = spawnFieldsRef.current; // Press-time snapshot. Read once, here, before any await. const submitPayload = getSubmitPayloadRef.current?.() ?? null; @@ -214,76 +218,114 @@ export function useRemoteSpawnDispatch({ selection: fields.selection, organizationId: fields.organizationId, }); - const operationKey = getKey( - JSON.stringify({ - connectionId: selectedConnectionId, - mode: fields.mode, - selection: fields.selection, - organizationId: fields.organizationId, - }) - ); void (async () => { - const outcome = await remoteSpawn.spawn(selectedConnectionId, createInput, { operationKey }); - if (outcome.status === 'ready') { - // The spawn settled; the next submit is a fresh intent. - rotateKey(); - const spawnedPath = getSpawnedAgentSessionPath(outcome.sessionID, organizationId); - if (submitPayload === null) { - router.replace(spawnedPath); + setIsResolvingInstance(true); + try { + // Refetch the live instance list so a rebooted host's new + // connectionId replaces the stale selection before spawn. + let freshList = instanceList; + try { + const result = await refetchInstances(); + freshList = result.data?.instances ?? instanceList; + } catch { + // Refetch failed; continue with the last-known list. + } + const live = resolveLiveInstance(runOnInstance, freshList); + if (live === null) { + // No live row matches the selection (neither the connectionId nor + // the name + projectName pair). Surface the retryable copy and let + // the next Start tap re-resolve against a refreshed list. + toast.error(remoteSpawnRetryableToast()); + onSpawnFailedRef.current?.(); return; } - const shareId = putSharePayload(submitPayload); - router.replace( - appendShareParams(spawnedPath as string, shareId, { - autoSend: true, + const selectedConnectionId = live.connectionId; + // A rebooted host advertises the same name + projectName on a new + // connectionId. Remap the selection to the live row so the spawn and + // a later refetch agree. The unchanged-id case keeps the same id and + // the same hoisted operation key. + if (live.connectionId !== runOnInstance.connectionId) { + setRunOnInstance(live); + } + const operationKey = getKey( + JSON.stringify({ + connectionId: selectedConnectionId, mode: fields.mode, - }) as Href + selection: fields.selection, + organizationId: fields.organizationId, + }) ); - return; - } - // Not ready: the spawn settled without navigating. Re-arm the caller's - // abandon guard so a later back/swipe still confirms. - onSpawnFailedRef.current?.(); - if (outcome.status === 'nonRetryable') { - // A typed non-retryable rejection ends the intent. - rotateKey(); - toast.error(remoteSpawnNonRetryableToast()); - return; - } - // outcome.status === 'retryable': refetch the instance list and - // re-evaluate whether the previously-selected instance is still - // present. - toast.error(remoteSpawnRetryableToast()); - let refetchedInstances: InstancePickerInstance[] = instanceList; - try { - const result = await refetchInstances(); - refetchedInstances = result.data?.instances ?? instanceList; - } catch { - // Refetch failed; fall through with the last-known list. The - // mapping helper treats an empty list as "disconnected", which - // is the right conservative default for a network blip. - } - const action = resolveRemoteSubmitOutcome({ - outcome, - refetchedInstances, - selectedConnectionId, - }); - if (action.kind !== 'retryable') { - // Defensive: outcome.status === 'retryable' must produce a - // retryable action. If this ever changes we'll want to know. - return; - } - // kilocode_change - only apply the reset if the selection this - // dispatch was FOR is still the CURRENT one (read from the ref, not - // the closure-captured `runOnInstance` — see the ref's comment - // above). Without this check, a stale tail's reset could clobber a - // newer, unrelated selection the user already made. - if ( - action.shouldResetSelectionToCloudAgent && - runOnInstanceRef.current?.connectionId === selectedConnectionId - ) { - setRunOnInstance(null); - setShowInstanceDisconnectedNote(action.showInstanceDisconnectedNote); + const outcome = await remoteSpawn.spawn(selectedConnectionId, createInput, { + operationKey, + }); + if (outcome.status === 'ready') { + // The spawn settled; the next submit is a fresh intent. + rotateKey(); + const spawnedPath = getSpawnedAgentSessionPath(outcome.sessionID, organizationId); + if (submitPayload === null) { + router.replace(spawnedPath); + return; + } + const shareId = putSharePayload(submitPayload); + router.replace( + appendShareParams(spawnedPath as string, shareId, { + autoSend: true, + mode: fields.mode, + }) as Href + ); + return; + } + // Not ready: the spawn settled without navigating. Re-arm the caller's + // abandon guard so a later back/swipe still confirms. + onSpawnFailedRef.current?.(); + if (outcome.status === 'nonRetryable') { + // A typed non-retryable rejection ends the intent. + rotateKey(); + toast.error(remoteSpawnNonRetryableToast()); + return; + } + // outcome.status === 'retryable': refetch the instance list and + // re-evaluate whether the previously-selected instance is still + // present. + toast.error(remoteSpawnRetryableToast()); + // Seed from the fresh pre-spawn list, not the press-time + // `instanceList`: if the spawn remapped the selection to a live + // connectionId above, the press-time list still holds the stale id. A + // post-spawn refetch throw would then make the live id look missing + // and reset the selection to Cloud Agent even though the host is live. + let refetchedInstances: InstancePickerInstance[] = freshList; + try { + const result = await refetchInstances(); + refetchedInstances = result.data?.instances ?? freshList; + } catch { + // Refetch failed; fall through with the fresh pre-spawn list. The + // mapping helper treats an empty list as "disconnected", which + // is the right conservative default for a network blip. + } + const action = resolveRemoteSubmitOutcome({ + outcome, + refetchedInstances, + selectedConnectionId, + }); + if (action.kind !== 'retryable') { + // Defensive: outcome.status === 'retryable' must produce a + // retryable action. If this ever changes we'll want to know. + return; + } + // kilocode_change - only apply the reset if the selection this + // dispatch was FOR is still the CURRENT one (read from the ref, not + // the closure-captured `runOnInstance` — see the ref's comment + // above). Without this check, a stale tail's reset could clobber a + // newer, unrelated selection the user already made. + if ( + action.shouldResetSelectionToCloudAgent && + runOnInstanceRef.current?.connectionId === selectedConnectionId + ) { + setRunOnInstance(null); + setShowInstanceDisconnectedNote(action.showInstanceDisconnectedNote); + } + } finally { + setIsResolvingInstance(false); } })(); }, [ @@ -309,7 +351,7 @@ export function useRemoteSpawnDispatch({ ); return { - isSpawningRemote: remoteSpawn.status.status === 'inFlight', + isSpawningRemote: remoteSpawn.status.status === 'inFlight' || isResolvingInstance, showInstanceDisconnectedNote, onStart, onChangeRunOnInstance, diff --git a/apps/mobile/src/components/home/agent-sessions-section.test.ts b/apps/mobile/src/components/home/agent-sessions-section.test.ts index be0a804419..c2e25dc057 100644 --- a/apps/mobile/src/components/home/agent-sessions-section.test.ts +++ b/apps/mobile/src/components/home/agent-sessions-section.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import { hasDisplayableAgentSessions } from '@/components/home/agent-sessions-section'; +import { buildRows } from '@/components/home/agent-sessions-section'; import { type ActiveSession, type StoredSession } from '@/lib/hooks/use-agent-sessions'; vi.mock('expo-router', () => ({ @@ -20,14 +20,14 @@ vi.mock('@/components/agents/remote-session-row', () => ({ RemoteSessionRow: () => null, })); -vi.mock('@/components/agents/session-row', () => ({ - StoredSessionRow: () => null, -})); - vi.mock('@/components/ui/text', () => ({ Text: () => null, })); +vi.mock('@/components/agents/session-row', () => ({ + StoredSessionRow: () => null, +})); + vi.mock('@/lib/hooks/use-agent-sessions', () => ({ useAgentSessions: () => ({ activeSessions: [], @@ -68,27 +68,59 @@ function makeStored(over: Partial = {}): StoredSession { }; } -describe('hasDisplayableAgentSessions', () => { - it('returns true when there is at least one active session', () => { - expect(hasDisplayableAgentSessions([], [makeActive()])).toBe(true); +describe('buildRows', () => { + it('yields no rows when there are no live sessions', () => { + const rows = buildRows({ + activeSessions: [], + storedSessions: [], + activeSessionIds: new Set(), + }); + expect(rows).toEqual([]); + }); + + it('yields one row for one active session', () => { + const active = makeActive(); + const rows = buildRows({ + activeSessions: [active], + storedSessions: [], + activeSessionIds: new Set([active.id]), + }); + expect(rows.map(row => row.key)).toEqual(['active:a1']); }); - it('returns true when a cloud-agent stored session exists', () => { - expect( - hasDisplayableAgentSessions([makeStored({ created_on_platform: 'cloud-agent' })], []) - ).toBe(true); - expect( - hasDisplayableAgentSessions([makeStored({ created_on_platform: 'cloud-agent-web' })], []) - ).toBe(true); + it('caps the rows at three live sessions', () => { + const activeSessions = [ + makeActive({ id: 'a1' }), + makeActive({ id: 'a2' }), + makeActive({ id: 'a3' }), + makeActive({ id: 'a4' }), + ]; + const rows = buildRows({ + activeSessions, + storedSessions: [], + activeSessionIds: new Set(['a1', 'a2', 'a3', 'a4']), + }); + expect(rows).toHaveLength(3); + expect(rows.every(row => row.kind === 'active')).toBe(true); }); - it('returns false when only non-cloud-agent stored sessions exist', () => { - expect(hasDisplayableAgentSessions([makeStored({ created_on_platform: 'cli' })], [])).toBe( - false - ); + it('drops an offline stored session', () => { + const offline = makeStored({ session_id: 'off1', created_on_platform: 'cloud-agent' }); + const rows = buildRows({ + activeSessions: [], + storedSessions: [offline], + activeSessionIds: new Set(), + }); + expect(rows).toEqual([]); }); - it('returns false when both arrays are empty', () => { - expect(hasDisplayableAgentSessions([], [])).toBe(false); + it('keeps a live cloud-agent stored session', () => { + const live = makeStored({ session_id: 'on1', created_on_platform: 'cloud-agent' }); + const rows = buildRows({ + activeSessions: [], + storedSessions: [live], + activeSessionIds: new Set(['on1']), + }); + expect(rows.map(row => row.key)).toEqual(['stored:on1']); }); }); diff --git a/apps/mobile/src/components/home/agent-sessions-section.tsx b/apps/mobile/src/components/home/agent-sessions-section.tsx index 9778dfe142..c6368f60bf 100644 --- a/apps/mobile/src/components/home/agent-sessions-section.tsx +++ b/apps/mobile/src/components/home/agent-sessions-section.tsx @@ -3,9 +3,9 @@ import { useTranslation } from 'react-i18next'; import { View } from 'react-native'; import { RemoteSessionRow } from '@/components/agents/remote-session-row'; -import { useAgentSessionNavigator } from '@/components/agents/use-agent-session-navigator'; import { expandPlatformFilter } from '@/components/agents/session-list-helpers'; import { StoredSessionRow } from '@/components/agents/session-row'; +import { useAgentSessionNavigator } from '@/components/agents/use-agent-session-navigator'; import { SectionHeader } from '@/components/home/section-header'; import { Text } from '@/components/ui/text'; import { @@ -13,7 +13,9 @@ import { type StoredSession, useAgentSessions, } from '@/lib/hooks/use-agent-sessions'; -import { parseTimestamp } from '@/lib/utils'; +import { cn, parseTimestamp } from '@/lib/utils'; + +export const HOME_LIVE_SLOT_MIN_CLASS = 'min-h-[72px]'; const MAX_ROWS = 3; const CLOUD_AGENT_PLATFORMS = new Set(expandPlatformFilter(['cloud-agent'])); @@ -30,7 +32,7 @@ type Row = session: StoredSession; }; -function buildRows(params: { +export function buildRows(params: { activeSessions: ActiveSession[]; storedSessions: StoredSession[]; activeSessionIds: Set; @@ -51,7 +53,6 @@ function buildRows(params: { CLOUD_AGENT_PLATFORMS.has(s.created_on_platform) ); const live = cloudAgentStored.filter(s => activeSessionIds.has(s.session_id)); - const offline = cloudAgentStored.filter(s => !activeSessionIds.has(s.session_id)); const sortByUpdated = (a: StoredSession, b: StoredSession) => parseTimestamp(b.status_updated_at ?? b.updated_at).getTime() - @@ -68,35 +69,9 @@ function buildRows(params: { } } - // eslint-disable-next-line unicorn/no-array-sort -- Hermes does not implement Array.prototype.toSorted; spread already prevents mutation of the source - for (const session of [...offline].sort(sortByUpdated)) { - if (rows.length >= MAX_ROWS) { - break; - } - if (!seenSessionIds.has(session.session_id)) { - rows.push({ key: `stored:${session.session_id}`, kind: 'stored', session }); - seenSessionIds.add(session.session_id); - } - } - return rows; } -// Whether the Home "Agent sessions" section has anything to render — mirrors -// buildRows' inclusion rule (any active session, or a cloud-agent stored -// session; stored CLI/other-platform sessions live on the Agents tab, not -// Home). The Home screen gates its section/promo/new-task button on this so a -// CLI-only account shows the first-use promo instead of an empty section. -export function hasDisplayableAgentSessions( - storedSessions: StoredSession[], - activeSessions: ActiveSession[] -): boolean { - return ( - activeSessions.length > 0 || - storedSessions.some(s => CLOUD_AGENT_PLATFORMS.has(s.created_on_platform)) - ); -} - type AgentSessionsSectionProps = { organizationId: string | null; }; @@ -104,17 +79,13 @@ type AgentSessionsSectionProps = { export function AgentSessionsSection({ organizationId }: Readonly) { const router = useRouter(); const { t } = useTranslation(); - const { activeSessions, storedSessions, activeSessionIds, activeIsError } = useAgentSessions({ + const { activeSessions, storedSessions, activeSessionIds } = useAgentSessions({ organizationId, }); const navigateToSession = useAgentSessionNavigator(); const rows = buildRows({ activeSessions, storedSessions, activeSessionIds }); - if (rows.length === 0) { - return null; - } - return ( - {activeIsError ? ( - - {t('home.showingSavedSessions')} - - ) : null} + {rows.length === 0 && } {rows.map(row => { if (row.kind === 'active') { const { session } = row; return ( ); } + +function LiveNowEmpty() { + const { t } = useTranslation(); + + return ( + + + {t('home.noLiveSessions')} + + + ); +} diff --git a/apps/mobile/src/components/home/agents-promo-card.tsx b/apps/mobile/src/components/home/agents-promo-card.tsx deleted file mode 100644 index 19210df4be..0000000000 --- a/apps/mobile/src/components/home/agents-promo-card.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { type Href, useRouter } from 'expo-router'; -import { useTranslation } from 'react-i18next'; -import { Bot } from '@/components/ui/icons'; -import { DirectionalChevronRight } from '@/components/ui/directional-icons'; -import { Pressable, View } from 'react-native'; - -import { Text } from '@/components/ui/text'; -import { agentColor } from '@/lib/agent-color'; -import { useThemeColors } from '@/lib/hooks/use-theme-colors'; -import { cn } from '@/lib/utils'; - -type AgentsPromoCardProps = { - organizationId: string | null; -}; - -export function AgentsPromoCard({ organizationId }: Readonly) { - const router = useRouter(); - const { t } = useTranslation(); - const colors = useThemeColors(); - const title = t('home.kiloAgents'); - const tint = agentColor('Kilo Agents'); - - return ( - { - const path = organizationId - ? `/(app)/agent-chat/new?organizationId=${organizationId}` - : '/(app)/agent-chat/new'; - router.push(path as Href); - }} - className="mx-4 gap-3 rounded-2xl border border-border bg-card p-4 active:opacity-80" - accessibilityLabel={t('home.startNewAgentSession')} - > - - - - - - {title} - - {t('home.aiCodingSessions')} - - - - - {t('home.startCodingTaskFromPhone')} - - - - {t('home.tryIt')} - - - - - ); -} diff --git a/apps/mobile/src/components/home/home-screen.mounted.test.tsx b/apps/mobile/src/components/home/home-screen.mounted.test.tsx index dddb23b39d..7ca7508536 100644 --- a/apps/mobile/src/components/home/home-screen.mounted.test.tsx +++ b/apps/mobile/src/components/home/home-screen.mounted.test.tsx @@ -6,10 +6,8 @@ import { describe, expect, it, vi } from 'vitest'; import '@/i18n'; import { HomeScreen } from '@/components/home/home-screen'; -const hasSessions = vi.hoisted(() => ({ value: true })); const activeIsError = vi.hoisted(() => ({ value: false })); const storedIsError = vi.hoisted(() => ({ value: false })); -const storedIsSuccess = vi.hoisted(() => ({ value: true })); const sessionsLoading = vi.hoisted(() => ({ value: false })); const orgLoaded = vi.hoisted(() => ({ value: true })); @@ -29,10 +27,7 @@ vi.mock('react-native-reanimated', () => ({ })); vi.mock('@/components/home/agent-sessions-section', () => ({ AgentSessionsSection: 'AgentSessionsSection', - hasDisplayableAgentSessions: () => hasSessions.value, -})); -vi.mock('@/components/home/agents-promo-card', () => ({ - AgentsPromoCard: 'AgentsPromoCard', + HOME_LIVE_SLOT_MIN_CLASS: 'min-h-[72px]', })); vi.mock('@/components/home/greeting', () => ({ buildTimedGreeting: () => 'Good morning', @@ -61,7 +56,7 @@ vi.mock('@/lib/hooks/use-agent-sessions', () => ({ isLoading: sessionsLoading.value, storedSessions: [{}], storedIsError: storedIsError.value, - storedIsSuccess: storedIsSuccess.value, + storedIsSuccess: true, activeIsError: activeIsError.value, refetch: vi.fn(), }), @@ -98,14 +93,16 @@ async function mountHome(): Promise { } describe('HomeScreen composition', () => { - it('renders the sessions section and new-task button when sessions are present', async () => { - hasSessions.value = true; + it('renders the sessions section and new-task button on an empty load', async () => { + storedIsError.value = false; + activeIsError.value = false; sessionsLoading.value = false; orgLoaded.value = true; const renderer = await mountHome(); expect(nodeCount(renderer.root, 'AgentSessionsSection')).toBe(1); expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(1); expect(nodeCount(renderer.root, 'ProductChoices')).toBe(1); + expect(nodeCount(renderer.root, 'AgentsPromoCard')).toBe(0); expect(nodeCount(renderer.root, 'Skeleton')).toBe(0); await act(async () => { @@ -114,19 +111,16 @@ describe('HomeScreen composition', () => { }); }); - it('renders only the agents promo card when there are no sessions', async () => { - hasSessions.value = false; + it('renders the sessions section and new-task button when sessions are present', async () => { storedIsError.value = false; - storedIsSuccess.value = true; activeIsError.value = false; sessionsLoading.value = false; orgLoaded.value = true; const renderer = await mountHome(); - expect(nodeCount(renderer.root, 'AgentsPromoCard')).toBe(1); - expect(nodeCount(renderer.root, 'QueryError')).toBe(0); - expect(nodeCount(renderer.root, 'AgentSessionsSection')).toBe(0); - expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(0); + expect(nodeCount(renderer.root, 'AgentSessionsSection')).toBe(1); + expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(1); expect(nodeCount(renderer.root, 'ProductChoices')).toBe(1); + expect(nodeCount(renderer.root, 'Skeleton')).toBe(0); await act(async () => { await Promise.resolve(); @@ -134,10 +128,8 @@ describe('HomeScreen composition', () => { }); }); - it('shows unavailable, not the promo, on active error with zero rows', async () => { - hasSessions.value = false; + it('shows the active-sessions error, not the section, on active error', async () => { storedIsError.value = false; - storedIsSuccess.value = true; activeIsError.value = true; sessionsLoading.value = false; orgLoaded.value = true; @@ -146,9 +138,28 @@ describe('HomeScreen composition', () => { expect(queryError).toBeDefined(); expect(queryError?.props.title).toBe("Couldn't load active sessions"); expect(typeof queryError?.props.onRetry).toBe('function'); - expect(nodeCount(renderer.root, 'AgentsPromoCard')).toBe(0); expect(nodeCount(renderer.root, 'AgentSessionsSection')).toBe(0); - expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(0); + expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(1); + expect(nodeCount(renderer.root, 'ProductChoices')).toBe(1); + + await act(async () => { + await Promise.resolve(); + renderer.unmount(); + }); + }); + + it('shows the stored-sessions error, not the section, on stored error', async () => { + storedIsError.value = true; + activeIsError.value = false; + sessionsLoading.value = false; + orgLoaded.value = true; + const renderer = await mountHome(); + const queryError = findNode(renderer.root, 'QueryError'); + expect(queryError).toBeDefined(); + expect(queryError?.props.title).toBe("Couldn't load sessions"); + expect(typeof queryError?.props.onRetry).toBe('function'); + expect(nodeCount(renderer.root, 'AgentSessionsSection')).toBe(0); + expect(nodeCount(renderer.root, 'NewTaskButton')).toBe(1); expect(nodeCount(renderer.root, 'ProductChoices')).toBe(1); await act(async () => { diff --git a/apps/mobile/src/components/home/home-screen.tsx b/apps/mobile/src/components/home/home-screen.tsx index f3cec1887f..589c529046 100644 --- a/apps/mobile/src/components/home/home-screen.tsx +++ b/apps/mobile/src/components/home/home-screen.tsx @@ -9,9 +9,8 @@ import { TabScreenScrollView } from '@/components/tab-screen'; import { AgentSessionsSection, - hasDisplayableAgentSessions, + HOME_LIVE_SLOT_MIN_CLASS, } from '@/components/home/agent-sessions-section'; -import { AgentsPromoCard } from '@/components/home/agents-promo-card'; import { buildTimedGreeting } from '@/components/home/greeting'; import { NewTaskButton } from '@/components/home/new-task-button'; import { ProductChoices } from '@/components/home/product-choices'; @@ -20,6 +19,7 @@ import { ScreenHeader } from '@/components/screen-header'; import { Skeleton } from '@/components/ui/skeleton'; import { useAgentSessions } from '@/lib/hooks/use-agent-sessions'; import { useOrganization } from '@/lib/organization-context'; +import { cn } from '@/lib/utils'; export function HomeScreen() { const queryClient = useQueryClient(); @@ -29,11 +29,8 @@ export function HomeScreen() { const { organizationId, isLoaded: orgLoaded } = useOrganization(); const { - storedSessions, - activeSessions, isLoading: sessionsLoading, storedIsError, - storedIsSuccess, activeIsError, refetch: refetchSessions, } = useAgentSessions({ @@ -42,11 +39,6 @@ export function HomeScreen() { }); const isLoading = sessionsLoading || !orgLoaded; - - // Match what the Home Agent-sessions section actually renders (cloud-agent - // stored + any active), so a CLI-only account shows the first-use promo - // instead of an empty section + orphaned "New coding task" button. - const hasAnySession = hasDisplayableAgentSessions(storedSessions, activeSessions); const headerTitle = buildTimedGreeting(); const handleRefresh = useCallback(() => { @@ -75,27 +67,24 @@ export function HomeScreen() { - - + {/* One slot: the loaded state is one row per live session, or a + single empty card, so a single skeleton avoids a jump. */} + ) : ( - {renderSessionsOrPromo({ - hasAnySession, + {renderSessionsOrError({ organizationId, - sessionsError: storedIsError, - sessionsLoadedEmpty: storedIsSuccess && !hasAnySession, + storedIsError, activeIsError, handleRetrySessions: () => void refetchSessions(), t, })} - {hasAnySession ? ( - - - - ) : null} + + + @@ -106,23 +95,18 @@ export function HomeScreen() { ); } -function renderSessionsOrPromo(params: { - hasAnySession: boolean; +function renderSessionsOrError(params: { organizationId: string | null; - sessionsError: boolean; - sessionsLoadedEmpty: boolean; + storedIsError: boolean; activeIsError: boolean; handleRetrySessions: () => void; t: TFunction; }) { - // Stale stored history always wins over an error (e.g. a live-poll blip - // on the active-sessions query) — never blank out sessions we already - // have. The first-use promo only appears after a confirmed empty - // response, never merely because the fetch hasn't succeeded yet. - if (params.hasAnySession) { - return ; - } - if (params.sessionsError) { + // A stored-list failure blocks all sessions, so it wins. A cold active poll + // failure is retryable but still hides the section until it recovers. + // Otherwise Home always renders the section, which shows an empty card when + // nothing is live. + if (params.storedIsError) { return ( ); } - // Cold active-only failure: the stored query succeeded empty but the active - // poll failed before any data loaded. Retryable, so never claim first-use. if (params.activeIsError) { return ( ); } - if (params.sessionsLoadedEmpty) { - return ; - } - return null; + return ; } diff --git a/apps/mobile/src/components/pr-review/pr-review-pending-comment-row.tsx b/apps/mobile/src/components/pr-review/pr-review-pending-comment-row.tsx index d011b952f2..180bda2726 100644 --- a/apps/mobile/src/components/pr-review/pr-review-pending-comment-row.tsx +++ b/apps/mobile/src/components/pr-review/pr-review-pending-comment-row.tsx @@ -2,7 +2,7 @@ // location label, 2-line body excerpt, trash → confirm-delete, and a // pressable body that opens the comment composer in edit mode. -import { type RefObject } from 'react'; +import { type RefObject, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { Trash2 } from '@/components/ui/icons'; import { Pressable, TextInput, View } from 'react-native'; @@ -138,19 +138,25 @@ export function ReviewSummaryField({ inputRef, isDisabled, onChange, + defaultValue = '', }: { bodyRef: RefObject; inputRef: RefObject; isDisabled: boolean; onChange: () => void; + /** Initial summary content (e.g. the attribution footer). Empty by default. */ + defaultValue?: string; }) { const colors = useThemeColors(); const { t } = useTranslation(); const keyboardVisible = useFormSheetKeyboardVisible(); + // Place the caret at the start of a prefilled summary once, so the user + // begins editing at the top instead of after the footer. + const placedCaretRef = useRef(false); return ( { + if (!placedCaretRef.current && defaultValue.length > 0) { + placedCaretRef.current = true; + inputRef.current?.setNativeProps({ selection: { start: 0, end: 0 } }); + } + }} multiline textAlignVertical="top" // Compact so half-detent and keyboard-open keep footer CTAs at y=0. className={cn( 'rounded-md border border-input bg-background px-3 py-2 text-sm leading-5 text-foreground', 'focus:border-ring', - keyboardVisible ? 'max-h-16 min-h-12' : 'min-h-14 max-h-24' + keyboardVisible ? 'max-h-16 min-h-12' : 'min-h-14 max-h-32' )} /> ); diff --git a/apps/mobile/src/components/pr-review/pr-review-submit.test.tsx b/apps/mobile/src/components/pr-review/pr-review-submit.test.tsx index 51421b73be..e7db25ebab 100644 --- a/apps/mobile/src/components/pr-review/pr-review-submit.test.tsx +++ b/apps/mobile/src/components/pr-review/pr-review-submit.test.tsx @@ -22,6 +22,15 @@ vi.mock('@/lib/pr-review/use-pr-review-mutations', () => ({ }), })); +const footerPreferenceMock = vi.hoisted(() => ({ + hasLoaded: true, + prReviewFooter: false, +})); + +vi.mock('@/lib/hooks/use-pr-review-footer-preference', () => ({ + usePrReviewFooterPreference: () => footerPreferenceMock, +})); + vi.mock('@/components/pr-review/discussion/reply-input', () => ({ ensureTermsAcceptedOutcome: vi.fn(async () => ({ kind: 'accepted' as const })), })); @@ -58,6 +67,7 @@ vi.mock('expo-router', () => ({ vi.mock('react-native', () => ({ Alert: { alert: vi.fn() }, Keyboard: { addListener: () => ({ remove: vi.fn() }) }, + Platform: { OS: 'ios' }, ScrollView: 'ScrollView', TextInput: 'TextInput', View: 'View', @@ -73,6 +83,7 @@ vi.mock('@/components/pr-review/review-event-chips', () => ({ })); vi.mock('@/components/ui/button', () => ({ Button: 'Button' })); vi.mock('@/components/ui/accessible-status', () => ({ AccessibleStatus: 'AccessibleStatus' })); +vi.mock('@/components/ui/skeleton', () => ({ Skeleton: 'Skeleton' })); vi.mock('@/components/ui/text', () => ({ Text: 'Text' })); vi.mock('@/components/pr-review/pr-review-pending-comment-row', () => ({ focusAfterPendingCommentRemoval: vi.fn(), @@ -164,11 +175,29 @@ function submitOnPress(renderer: TestRenderer.ReactTestRenderer): () => void { return button.props.onPress as () => void; } +function findSubmitButton(renderer: TestRenderer.ReactTestRenderer): Record { + const button = renderer.root.findAll(node => Object.hasOwn(node.props, 'loading'))[0]; + if (!button) { + throw new Error('Submit button not found'); + } + return button.props as Record; +} + +function findSummaryField(renderer: TestRenderer.ReactTestRenderer): Record { + const summary = renderer.root.findAll(node => Object.hasOwn(node.props, 'defaultValue'))[0]; + if (!summary) { + throw new Error('Summary field not found'); + } + return summary.props as Record; +} + beforeEach(() => { latestItems = []; addCommentFn = null; submitMutationMock.mutateAsync.mockReset(); submitMutationMock.mutateAsync.mockResolvedValue(undefined); + footerPreferenceMock.hasLoaded = true; + footerPreferenceMock.prReviewFooter = false; }); describe('PrReviewSubmit queue retention', () => { @@ -213,3 +242,29 @@ describe('PrReviewSubmit queue retention', () => { expect(latestItems.map(item => item.id)).toEqual(['stale-c']); }); }); + +describe('PrReviewSubmit footer preference', () => { + it('prefills the platform footer and enables submit when the setting is on', () => { + footerPreferenceMock.prReviewFooter = true; + const renderer = mount(); + + expect(findSummaryField(renderer).defaultValue).toBe( + '\n\n---\nReviewed via the [Kilo iOS app](https://apps.apple.com/app/id6761193135)' + ); + + // hasSummary was set from the prefilled footer, so a comment review with no + // queued comments is not blocked: the submit button is enabled. + expect(findSubmitButton(renderer).disabled).toBe(false); + }); + + it('leaves the body empty and blocks submit when the setting is off', () => { + footerPreferenceMock.prReviewFooter = false; + const renderer = mount(); + + expect(findSummaryField(renderer).defaultValue).toBe(''); + + // No prefilled footer and no queued comments: the comment review is + // blocked, so the submit button is disabled. + expect(findSubmitButton(renderer).disabled).toBe(true); + }); +}); diff --git a/apps/mobile/src/components/pr-review/pr-review-submit.tsx b/apps/mobile/src/components/pr-review/pr-review-submit.tsx index ba9309d548..d14a8b8c8d 100644 --- a/apps/mobile/src/components/pr-review/pr-review-submit.tsx +++ b/apps/mobile/src/components/pr-review/pr-review-submit.tsx @@ -12,7 +12,7 @@ import * as Haptics from 'expo-haptics'; import { type Href, useRouter } from 'expo-router'; import { type ReactNode, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Alert, Keyboard, ScrollView, type TextInput, View } from 'react-native'; +import { Alert, Keyboard, Platform, ScrollView, type TextInput, View } from 'react-native'; import { PrFormSheetFooter, @@ -22,6 +22,7 @@ import { import { ReviewEventChips } from '@/components/pr-review/review-event-chips'; import { Button } from '@/components/ui/button'; import { AccessibleStatus } from '@/components/ui/accessible-status'; +import { Skeleton } from '@/components/ui/skeleton'; import { Text } from '@/components/ui/text'; import { focusAfterPendingCommentRemoval, @@ -43,6 +44,8 @@ import { mutationErrorDisplay } from '@/lib/pr-review/mutation-error-display'; import { type PendingReviewItem, usePendingReview } from '@/lib/pr-review/pending-review-provider'; import { partitionPendingItems } from '@/lib/pr-review/partition-pending-items'; import { useSubmitReviewMutation } from '@/lib/pr-review/use-pr-review-mutations'; +import { usePrReviewFooterPreference } from '@/lib/hooks/use-pr-review-footer-preference'; +import { buildReviewFooter } from '@/lib/pr-review/review-footer'; import { selectPartialSubmitMessage, selectSubmitCtaLabel, @@ -66,6 +69,7 @@ export function PrReviewSubmit(props: PrReviewSubmitProps) { const pending = usePendingReview(); const { t } = useTranslation(); const submitReview = useSubmitReviewMutation({ owner, repo, number }); + const { prReviewFooter, hasLoaded: prReviewFooterLoaded } = usePrReviewFooterPreference(); const [event, setEvent] = useState('COMMENT'); const [hasSummary, setHasSummary] = useState(false); @@ -79,6 +83,18 @@ export function PrReviewSubmit(props: PrReviewSubmitProps) { const bodyInputRef = useRef(null); const scrollRef = useRef(null); + // Seed the summary refs from the default-on footer preference once it + // resolves, before the summary field mounts, so a prefilled footer survives + // the async load without flashing an empty input. + const footerSeededRef = useRef(false); + const initialFooter = + prReviewFooterLoaded && prReviewFooter ? buildReviewFooter(Platform.OS) : ''; + if (prReviewFooterLoaded && !footerSeededRef.current) { + footerSeededRef.current = true; + bodyRef.current = initialFooter; + setHasSummary(initialFooter.trim().length > 0); + } + const isSubmitting = submitReview.isPending; const queuedCount = pending.items.length; const { fresh, stale } = partitionPendingItems(pending.items, headSha); @@ -267,15 +283,20 @@ export function PrReviewSubmit(props: PrReviewSubmitProps) { {t('prReview.submit.summaryOptional')} - { - setHasSummary(bodyRef.current.trim().length > 0); - clearRecoverableError(); - }} - /> + {prReviewFooterLoaded ? ( + { + setHasSummary(bodyRef.current.trim().length > 0); + clearRecoverableError(); + }} + /> + ) : ( + + )} diff --git a/apps/mobile/src/components/preferences-screen.mounted.test.tsx b/apps/mobile/src/components/preferences-screen.mounted.test.tsx index 2eaebc9f68..399a1c0321 100644 --- a/apps/mobile/src/components/preferences-screen.mounted.test.tsx +++ b/apps/mobile/src/components/preferences-screen.mounted.test.tsx @@ -17,6 +17,7 @@ vi.mock('@/components/ui/icons', () => ({ Bell: 'Bell', Brain: 'Brain', Globe: 'Globe', + MessageSquare: 'MessageSquare', Smartphone: 'Smartphone', })); vi.mock('@/components/language-picker-sheet', () => ({ @@ -44,6 +45,13 @@ vi.mock('@/lib/hooks/use-keep-screen-on-preference', () => ({ setKeepScreenOn: vi.fn(), }), })); +vi.mock('@/lib/hooks/use-pr-review-footer-preference', () => ({ + usePrReviewFooterPreference: () => ({ + prReviewFooter: true, + hasLoaded: true, + setPrReviewFooter: vi.fn(), + }), +})); vi.mock('@/lib/hooks/use-reasoning-preference', () => ({ useReasoningPreference: () => ({ defaultExpanded: false, diff --git a/apps/mobile/src/components/preferences-screen.tsx b/apps/mobile/src/components/preferences-screen.tsx index f7a642f20b..4e098c6f4f 100644 --- a/apps/mobile/src/components/preferences-screen.tsx +++ b/apps/mobile/src/components/preferences-screen.tsx @@ -1,5 +1,12 @@ import { type Href, useRouter } from 'expo-router'; -import { Bell, Brain, Globe, type LucideIcon, Smartphone } from '@/components/ui/icons'; +import { + Bell, + Brain, + Globe, + type LucideIcon, + MessageSquare, + Smartphone, +} from '@/components/ui/icons'; import { useState } from 'react'; import { Switch, View } from 'react-native'; import { useTranslation } from 'react-i18next'; @@ -14,6 +21,7 @@ import { attemptPushRegistrationReconciliation } from '@/lib/auth/push-registrat import { useCurrentUserId } from '@/lib/hooks/use-current-user-id'; import { getResolvedLanguage, useLanguagePreference } from '@/lib/hooks/use-language-preference'; import { useKeepScreenOnPreference } from '@/lib/hooks/use-keep-screen-on-preference'; +import { usePrReviewFooterPreference } from '@/lib/hooks/use-pr-review-footer-preference'; import { useReasoningPreference } from '@/lib/hooks/use-reasoning-preference'; import { cn } from '@/lib/utils'; import { LANGUAGE_ENDONYMS } from '@/i18n/languages'; @@ -80,6 +88,11 @@ export function PreferencesScreen() { hasLoaded: keepScreenOnLoaded, setKeepScreenOn, } = useKeepScreenOnPreference(); + const { + prReviewFooter, + hasLoaded: prReviewFooterLoaded, + setPrReviewFooter, + } = usePrReviewFooterPreference(); const { t } = useTranslation(); const { userId } = useCurrentUserId(); const { preference: languagePreference } = useLanguagePreference(); @@ -114,6 +127,14 @@ export function PreferencesScreen() { disabled={!keepScreenOnLoaded} onValueChange={setKeepScreenOn} /> + {/* Appearance */} diff --git a/apps/mobile/src/components/ui/form-field.tsx b/apps/mobile/src/components/ui/form-field.tsx index bf52634398..fa89f4633f 100644 --- a/apps/mobile/src/components/ui/form-field.tsx +++ b/apps/mobile/src/components/ui/form-field.tsx @@ -77,7 +77,11 @@ function FormField({ } }} className={cn( - 'rounded-md border border-input bg-background px-3 py-2.5 text-sm leading-[normal] text-foreground', + // min-h-[44px] with no vertical padding: the 44pt height meets the + // Apple HIG touch floor and centers the text, while the padding + // draws the single-line text below the middle. min-h (not h) still + // lets Dynamic Type grow the field past the floor. + 'min-h-[44px] rounded-md border border-input bg-background px-3 text-sm leading-[normal] text-foreground', 'focus:border-ring', displayedError && 'border-destructive', disabled && 'opacity-50', diff --git a/apps/mobile/src/i18n/locales/af.json b/apps/mobile/src/i18n/locales/af.json index 179eb5574f..e425398a4e 100644 --- a/apps/mobile/src/i18n/locales/af.json +++ b/apps/mobile/src/i18n/locales/af.json @@ -148,7 +148,9 @@ "appearanceSystem": "Stelsel", "appearanceLight": "Lig", "appearanceDark": "Donker", - "account": "Rekening" + "account": "Rekening", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Trekversoek onbeskikbaar", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Jou aansporing sal verlore gaan.", "keepEditing": "Bly redigeer", "discard": "Gooi weg", - "discardFailed": "Kon die konsep nie weggooi nie. Probeer asseblief weer." + "discardFailed": "Kon die konsep nie weggooi nie. Probeer asseblief weer.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} lêer bygewerk", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Kon nie sessies laai nie", "couldNotLoadActiveSessions": "Kon nie aktiewe sessies laai nie", "agentSessions": "Agentsessies", - "showingSavedSessions": "Wys gestoorde sessies — lewende status mag verouderd wees", - "startNewAgentSession": "Begin 'n nuwe Kilo Agent-sessie", - "aiCodingSessions": "AI-koderingsessies", - "startCodingTaskFromPhone": "Begin 'n koderingstaak van jou foon af of gaan voort met 'n sessie vanaf jou CLI.", - "tryIt": "Probeer dit", "newCodingTask": "Nuwe koderingstaak", "explore": "Verken", "seeAll": "Sien alles", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Maak {{filename}} toe", diff --git a/apps/mobile/src/i18n/locales/am.json b/apps/mobile/src/i18n/locales/am.json index 1f3a1d7709..d63b554ba1 100644 --- a/apps/mobile/src/i18n/locales/am.json +++ b/apps/mobile/src/i18n/locales/am.json @@ -148,7 +148,9 @@ "appearanceSystem": "ስርዓት", "appearanceLight": "ብርሃን", "appearanceDark": "ጨለማ", - "account": "መለያ" + "account": "መለያ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "የጎተራ ጥያቄ የለም", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ጥያቄዎ ይጠፋል።", "keepEditing": "ማስተካከል ይቀጥሉ", "discard": "ያስወግዱ", - "discardFailed": "ረቂቁን ማስወገድ አልተቻለም። እባክዎ እንደገና ይሞክሩ።" + "discardFailed": "ረቂቁን ማስወገድ አልተቻለም። እባክዎ እንደገና ይሞክሩ።", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ፋይል ተዘምኗል", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "ክፍለ-ጊዜዎችን መጫን አልተቻለም", "couldNotLoadActiveSessions": "ንቁ ክፍለ-ጊዜዎችን መጫን አልተቻለም", "agentSessions": "የወኪል ክፍለ-ጊዜዎች", - "showingSavedSessions": "የተቀመጡ ክፍለ-ጊዜዎችን በማሳየት ላይ — የቀጥታ ሁኔታ ጊዜው ያለፈ ሊሆን ይችላል", - "startNewAgentSession": "አዲስ የKilo Agent ክፍለ-ጊዜ ይጀምሩ", - "aiCodingSessions": "የAI ኮዲንግ ክፍለ-ጊዜዎች", - "startCodingTaskFromPhone": "ከስልክዎ የኮዲንግ ተግባር ይጀምሩ ወይም ከCLIዎ ክፍለ-ጊዜ ይቀጥሉ።", - "tryIt": "ይሞክሩት", "newCodingTask": "አዲስ የኮዲንግ ተግባር", "explore": "ይቃኙ", "seeAll": "ሁሉንም ይመልከቱ", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}}ን ይዝጉ", diff --git a/apps/mobile/src/i18n/locales/ar.json b/apps/mobile/src/i18n/locales/ar.json index 0fdea59424..c377e80be4 100644 --- a/apps/mobile/src/i18n/locales/ar.json +++ b/apps/mobile/src/i18n/locales/ar.json @@ -148,7 +148,9 @@ "appearanceSystem": "النظام", "appearanceLight": "فاتح", "appearanceDark": "داكن", - "account": "الحساب" + "account": "الحساب", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "الإشعارات", @@ -1754,7 +1756,8 @@ "discardDraftMessage": "ستفقد مطالبتك.", "discardDraftTitle": "تجاهل المسودة؟", "discardFailed": "تعذّر تجاهل المسودة. يرجى المحاولة مرة أخرى.", - "keepEditing": "متابعة التعديل" + "keepEditing": "متابعة التعديل", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "تم تحديث {{displayCount}} ملف", @@ -2260,15 +2263,10 @@ "couldNotLoadSessions": "تعذّر تحميل الجلسات", "couldNotLoadActiveSessions": "تعذّر تحميل الجلسات النشطة", "agentSessions": "جلسات الوكيل", - "showingSavedSessions": "عرض الجلسات المحفوظة — قد تكون الحالة المباشرة قديمة", - "startNewAgentSession": "بدء جلسة Kilo Agent جديدة", - "aiCodingSessions": "جلسات البرمجة بالذكاء الاصطناعي", - "startCodingTaskFromPhone": "ابدأ مهمة برمجة من هاتفك أو تابع جلسة من CLI.", - "tryIt": "جرّبه", "newCodingTask": "مهمة برمجة جديدة", "explore": "استكشف", "seeAll": "عرض الكل", - "kiloAgents": "وكلاء Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "إغلاق {{filename}}", diff --git a/apps/mobile/src/i18n/locales/az.json b/apps/mobile/src/i18n/locales/az.json index 2a098b38f8..1e95f491c2 100644 --- a/apps/mobile/src/i18n/locales/az.json +++ b/apps/mobile/src/i18n/locales/az.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "İşıq", "appearanceDark": "Qaranlıq", - "account": "Hesab" + "account": "Hesab", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request əlçatan deyil", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "İstəyiniz itiriləcək.", "keepEditing": "Redaktəyə davam et", "discard": "Ləğv et", - "discardFailed": "Qaralama ləğv edilə bilmədi. Zəhmət olmasa yenidən cəhd edin." + "discardFailed": "Qaralama ləğv edilə bilmədi. Zəhmət olmasa yenidən cəhd edin.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fayl yeniləndi", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Sessiyalar yüklənə bilmədi", "couldNotLoadActiveSessions": "Aktiv sessiyalar yüklənə bilmədi", "agentSessions": "Agent sessiyaları", - "showingSavedSessions": "Saxlanılmış sessiyalar göstərilir — canlı status köhnəlmiş ola bilər", - "startNewAgentSession": "Yeni Kilo Agent sessiyasına başla", - "aiCodingSessions": "AI kodlaşdırma sessiyaları", - "startCodingTaskFromPhone": "Telefonunuzdan kodlaşdırma tapşırığı başlayın və ya CLI-nizdən sessiyanı davam etdirin.", - "tryIt": "Cəhd edin", "newCodingTask": "Yeni kodlaşdırma tapşırığı", "explore": "Kəşf et", "seeAll": "Hamısına bax", - "kiloAgents": "Kilo Agentlər" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} bağla", diff --git a/apps/mobile/src/i18n/locales/be.json b/apps/mobile/src/i18n/locales/be.json index 682d9f8275..7509d4c404 100644 --- a/apps/mobile/src/i18n/locales/be.json +++ b/apps/mobile/src/i18n/locales/be.json @@ -148,7 +148,9 @@ "appearanceSystem": "Сістэма", "appearanceLight": "Светлая", "appearanceDark": "Цёмная", - "account": "Уліковы запіс" + "account": "Уліковы запіс", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request недаступны", @@ -2206,7 +2208,8 @@ "discardDraftMessage": "Ваш запыт будзе страчаны.", "keepEditing": "Працягваць рэдагаванне", "discard": "Адхіліць", - "discardFailed": "Не атрымалася адхіліць чарнавік. Паспрабуйце зноў." + "discardFailed": "Не атрымалася адхіліць чарнавік. Паспрабуйце зноў.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Абноўлены {{displayCount}} файл", @@ -2702,15 +2705,10 @@ "couldNotLoadSessions": "Не ўдалося загрузіць сеансы", "couldNotLoadActiveSessions": "Не ўдалося загрузіць актыўныя сеансы", "agentSessions": "Сеансы агента", - "showingSavedSessions": "Паказаныя захаваныя сеансы — жывы статус можа быць састарэлым", - "startNewAgentSession": "Пачаць новы сеанс агента Kilo", - "aiCodingSessions": "Сеансы AI-праграмавання", - "startCodingTaskFromPhone": "Пачніце задачу праграмавання з тэлефона або працягніце сеанс з вашага CLI.", - "tryIt": "Паспрабаваць", "newCodingTask": "Новая задача праграмавання", "explore": "Даследаваць", "seeAll": "Паказаць усё", - "kiloAgents": "Агенты Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Закрыць {{filename}}", diff --git a/apps/mobile/src/i18n/locales/bg.json b/apps/mobile/src/i18n/locales/bg.json index 7f41626925..208cc99f7a 100644 --- a/apps/mobile/src/i18n/locales/bg.json +++ b/apps/mobile/src/i18n/locales/bg.json @@ -148,7 +148,9 @@ "appearanceSystem": "Система", "appearanceLight": "Светъл", "appearanceDark": "Тъмен", - "account": "Акаунт" + "account": "Акаунт", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request-ът е недостъпен", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Твоята подкана ще бъде загубена.", "keepEditing": "Продължи редактирането", "discard": "Отхвърли", - "discardFailed": "Не можа да се отхвърли черновата. Моля, опитай отново." + "discardFailed": "Не можа да се отхвърли черновата. Моля, опитай отново.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Актуализиран {{displayCount}} файл", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Не можаха да се заредят сесиите", "couldNotLoadActiveSessions": "Не можаха да се заредят активните сесии", "agentSessions": "Агентни сесии", - "showingSavedSessions": "Показване на запазените сесии — актуалният статус може да е остарял", - "startNewAgentSession": "Започнете нова сесия на Kilo Agent", - "aiCodingSessions": "AI сесии за кодиране", - "startCodingTaskFromPhone": "Започнете задача за кодиране от телефона си или продължете сесия от вашия CLI.", - "tryIt": "Опитайте", "newCodingTask": "Нова задача за кодиране", "explore": "Разгледайте", "seeAll": "Вижте всички", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Затворете {{filename}}", diff --git a/apps/mobile/src/i18n/locales/bn.json b/apps/mobile/src/i18n/locales/bn.json index 08204183f5..268fa20f15 100644 --- a/apps/mobile/src/i18n/locales/bn.json +++ b/apps/mobile/src/i18n/locales/bn.json @@ -148,7 +148,9 @@ "appearanceSystem": "সিস্টেম", "appearanceLight": "লাইট", "appearanceDark": "ডার্ক", - "account": "অ্যাকাউন্ট" + "account": "অ্যাকাউন্ট", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "পুল রিকোয়েস্ট অনুপলব্ধ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "আপনার প্রম্পট হারিয়ে যাবে।", "keepEditing": "সম্পাদনা চালিয়ে যান", "discard": "বাতিল করুন", - "discardFailed": "ড্রাফ্টটি বাতিল করা যায়নি। আবার চেষ্টা করুন।" + "discardFailed": "ড্রাফ্টটি বাতিল করা যায়নি। আবার চেষ্টা করুন।", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}}টি ফাইল আপডেট হয়েছে", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "সেশন লোড করা যায়নি", "couldNotLoadActiveSessions": "সক্রিয় সেশন লোড করা যায়নি", "agentSessions": "এজেন্ট সেশন", - "showingSavedSessions": "সংরক্ষিত সেশন দেখানো হচ্ছে — লাইভ স্ট্যাটাস পুরানো হতে পারে", - "startNewAgentSession": "একটি নতুন Kilo Agent সেশন শুরু করুন", - "aiCodingSessions": "AI কোডিং সেশন", - "startCodingTaskFromPhone": "আপনার ফোন থেকে একটি কোডিং কাজ শুরু করুন বা আপনার CLI থেকে একটি সেশন চালিয়ে যান।", - "tryIt": "চেষ্টা করুন", "newCodingTask": "নতুন কোডিং কাজ", "explore": "অন্বেষণ", "seeAll": "সব দেখুন", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} বন্ধ করুন", diff --git a/apps/mobile/src/i18n/locales/bs.json b/apps/mobile/src/i18n/locales/bs.json index b1af1fd817..0cb33253ec 100644 --- a/apps/mobile/src/i18n/locales/bs.json +++ b/apps/mobile/src/i18n/locales/bs.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Svijetlo", "appearanceDark": "Tamno", - "account": "Nalog" + "account": "Nalog", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nedostupan", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "Vaš upit bit će izgubljen.", "keepEditing": "Nastavi uređivati", "discard": "Odbaci", - "discardFailed": "Nije moguće odbaciti nacrt. Pokušajte ponovo." + "discardFailed": "Nije moguće odbaciti nacrt. Pokušajte ponovo.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ažurirano {{displayCount}} datoteka", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Sesije nisu mogle biti učitane", "couldNotLoadActiveSessions": "Aktivne sesije nisu mogle biti učitane", "agentSessions": "Sesije agenta", - "showingSavedSessions": "Prikazuju se sačuvane sesije — status uživo može biti zastario", - "startNewAgentSession": "Započni novu sesiju Kilo Agenta", - "aiCodingSessions": "AI sesije kodiranja", - "startCodingTaskFromPhone": "Započnite zadatak kodiranja sa svog telefona ili nastavite sesiju sa svog CLI-ja.", - "tryIt": "Isprobaj", "newCodingTask": "Novi zadatak kodiranja", "explore": "Istraži", "seeAll": "Pogledaj sve", - "kiloAgents": "Kilo Agenti" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zatvori {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ca.json b/apps/mobile/src/i18n/locales/ca.json index 45ca358412..fb585caf6b 100644 --- a/apps/mobile/src/i18n/locales/ca.json +++ b/apps/mobile/src/i18n/locales/ca.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Clar", "appearanceDark": "Fosc", - "account": "Compte" + "account": "Compte", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request no disponible", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "El teu missatge es perdrà.", "keepEditing": "Continua editant", "discard": "Descarta", - "discardFailed": "No s'ha pogut descartar l'esborrany. Torna-ho a provar." + "discardFailed": "No s'ha pogut descartar l'esborrany. Torna-ho a provar.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fitxer actualitzat", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "No s'han pogut carregar les sessions", "couldNotLoadActiveSessions": "No s'han pogut carregar les sessions actives", "agentSessions": "Sessions d'agent", - "showingSavedSessions": "S'estan mostrant les sessions desades: l'estat en directe pot estar desactualitzat", - "startNewAgentSession": "Inicia una nova sessió d'agent Kilo", - "aiCodingSessions": "Sessions de programació d'IA", - "startCodingTaskFromPhone": "Inicia una tasca de programació des del teu telèfon o continua una sessió des del teu CLI.", - "tryIt": "Prova-ho", "newCodingTask": "Nova tasca de programació", "explore": "Explora", "seeAll": "Veure-ho tot", - "kiloAgents": "Agents de Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Tanca {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ckb.json b/apps/mobile/src/i18n/locales/ckb.json index a9f94bb7ce..f43d78275e 100644 --- a/apps/mobile/src/i18n/locales/ckb.json +++ b/apps/mobile/src/i18n/locales/ckb.json @@ -148,7 +148,9 @@ "appearanceSystem": "سیستەم", "appearanceLight": "ڕووناک", "appearanceDark": "تاریک", - "account": "هەژمار" + "account": "هەژمار", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "داواکاری ڕاکێشان بەردەست نییە", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "نووسینەکەت لەدەست دەچێت.", "keepEditing": "بەردەوام بە لە دەستکاری", "discard": "وازلێهێنان", - "discardFailed": "نەتوانرا لە نوسینەکە وازبهێنرێت. تکایە دووبارە هەوڵ بدە." + "discardFailed": "نەتوانرا لە نوسینەکە وازبهێنرێت. تکایە دووبارە هەوڵ بدە.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} پەڕگە نوێکرایەوە", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "نەتوانرا دانیشتنەکان باربکرێن", "couldNotLoadActiveSessions": "نەتوانرا دانیشتنە چالاکەکان باربکرێن", "agentSessions": "دانیشتنەکانی ئەجێنت", - "showingSavedSessions": "نیشاندانی دانیشتنە پاشەکەوتکراوەکان — دۆخی ڕاستەوخۆ لەوانەیە کۆن بێت", - "startNewAgentSession": "دەستپێکردنی دانیشتنێکی نوێی ئەجێنتی Kilo", - "aiCodingSessions": "دانیشتنەکانی کۆدکردنی AI", - "startCodingTaskFromPhone": "ئەرکێکی کۆدکردن لە مۆبایلەکەتەوە دەستپێبکە یان لە CLIـی خۆتەوە دانیشتنێک بەردەوام بکە.", - "tryIt": "تاقی بکەرەوە", "newCodingTask": "ئەرکی کۆدکردنی نوێ", "explore": "گەڕان", "seeAll": "بینینی هەموو", - "kiloAgents": "ئەجێنتەکانی Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "داخستنی {{filename}}", diff --git a/apps/mobile/src/i18n/locales/cs.json b/apps/mobile/src/i18n/locales/cs.json index 9d1353bec0..2309480e45 100644 --- a/apps/mobile/src/i18n/locales/cs.json +++ b/apps/mobile/src/i18n/locales/cs.json @@ -148,7 +148,9 @@ "appearanceSystem": "Systém", "appearanceLight": "Světlý", "appearanceDark": "Tmavý", - "account": "Účet" + "account": "Účet", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request není dostupný", @@ -2206,7 +2208,8 @@ "discardDraftMessage": "Váš dotaz bude ztracen.", "keepEditing": "Pokračovat v úpravách", "discard": "Zahodit", - "discardFailed": "Koncept se nepodařilo zahodit. Zkuste to znovu." + "discardFailed": "Koncept se nepodařilo zahodit. Zkuste to znovu.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Aktualizován {{displayCount}} soubor", @@ -2702,15 +2705,10 @@ "couldNotLoadSessions": "Relace se nepodařilo načíst", "couldNotLoadActiveSessions": "Aktivní relace se nepodařilo načíst", "agentSessions": "Relace agenta", - "showingSavedSessions": "Zobrazují se uložené relace — živý stav může být zastaralý", - "startNewAgentSession": "Zahájit novou relaci Kilo Agent", - "aiCodingSessions": "AI programovací relace", - "startCodingTaskFromPhone": "Spusťte programovací úkol z telefonu nebo pokračujte v relaci z CLI.", - "tryIt": "Vyzkoušet", "newCodingTask": "Nový programovací úkol", "explore": "Prozkoumat", "seeAll": "Zobrazit vše", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zavřít {{filename}}", diff --git a/apps/mobile/src/i18n/locales/cy.json b/apps/mobile/src/i18n/locales/cy.json index ec08d0a88a..9919239e69 100644 --- a/apps/mobile/src/i18n/locales/cy.json +++ b/apps/mobile/src/i18n/locales/cy.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Golau", "appearanceDark": "Tywyll", - "account": "Cyfrif" + "account": "Cyfrif", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Ni ellir cael y cais tynnu", @@ -2236,7 +2238,8 @@ "discardDraftMessage": "Bydd eich anogiad yn cael ei golli.", "keepEditing": "Parhau i olygu", "discard": "Taflu", - "discardFailed": "Methwyd taflu'r drafft. Ceisiwch eto." + "discardFailed": "Methwyd taflu'r drafft. Ceisiwch eto.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Diweddarwyd {{displayCount}} ffeil", @@ -2742,15 +2745,10 @@ "couldNotLoadSessions": "Methwyd llwytho sesiynau", "couldNotLoadActiveSessions": "Methwyd llwytho sesiynau gweithredol", "agentSessions": "Sesiynau asiant", - "showingSavedSessions": "Yn dangos sesiynau wedi'u cadw — gall y statws byw fod yn hen", - "startNewAgentSession": "Dechrau sesiwn Kilo Agent newydd", - "aiCodingSessions": "Sesiynau codio AI", - "startCodingTaskFromPhone": "Dechreuwch dasg codio o'ch ffôn neu barhewch â sesiwn o'ch CLI.", - "tryIt": "Rhoi cynnig arno", "newCodingTask": "Tasg codio newydd", "explore": "Archwilio", "seeAll": "Gweld y cyfan", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Cau {{filename}}", diff --git a/apps/mobile/src/i18n/locales/da.json b/apps/mobile/src/i18n/locales/da.json index 610558c4a3..df65fc86d1 100644 --- a/apps/mobile/src/i18n/locales/da.json +++ b/apps/mobile/src/i18n/locales/da.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Lys", "appearanceDark": "Mørk", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request utilgængelig", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Din prompt vil gå tabt.", "keepEditing": "Fortsæt med at redigere", "discard": "Kassér", - "discardFailed": "Kunne ikke kassere kladden. Prøv igen." + "discardFailed": "Kunne ikke kassere kladden. Prøv igen.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Opdaterede {{displayCount}} fil", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Kunne ikke indlæse sessioner", "couldNotLoadActiveSessions": "Kunne ikke indlæse aktive sessioner", "agentSessions": "Agentsessioner", - "showingSavedSessions": "Viser gemte sessioner — live-status kan være forældet", - "startNewAgentSession": "Start en ny Kilo Agent-session", - "aiCodingSessions": "AI-kodningssessioner", - "startCodingTaskFromPhone": "Start en kodningsopgave fra din telefon eller fortsæt en session fra din CLI.", - "tryIt": "Prøv det", "newCodingTask": "Ny kodningsopgave", "explore": "Udforsk", "seeAll": "Se alle", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Luk {{filename}}", diff --git a/apps/mobile/src/i18n/locales/de.json b/apps/mobile/src/i18n/locales/de.json index 20587c6f96..0f89fba067 100644 --- a/apps/mobile/src/i18n/locales/de.json +++ b/apps/mobile/src/i18n/locales/de.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Hell", "appearanceDark": "Dunkel", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Benachrichtigungen", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "Dein Prompt geht verloren.", "discardDraftTitle": "Entwurf verwerfen?", "discardFailed": "Der Entwurf konnte nicht verworfen werden. Bitte versuche es erneut.", - "keepEditing": "Weiter bearbeiten" + "keepEditing": "Weiter bearbeiten", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} Datei aktualisiert", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "Sitzungen konnten nicht geladen werden", "couldNotLoadActiveSessions": "Aktive Sitzungen konnten nicht geladen werden", "agentSessions": "Agentensitzungen", - "showingSavedSessions": "Gespeicherte Sitzungen werden angezeigt — der Live-Status kann veraltet sein", - "startNewAgentSession": "Neue Kilo-Agent-Sitzung starten", - "aiCodingSessions": "KI-Codiersitzungen", - "startCodingTaskFromPhone": "Starte eine Codieraufgabe von deinem Telefon oder setze eine Sitzung von deiner CLI fort.", - "tryIt": "Ausprobieren", "newCodingTask": "Neue Codieraufgabe", "explore": "Entdecken", "seeAll": "Alle ansehen", - "kiloAgents": "Kilo-Agenten" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} schließen", diff --git a/apps/mobile/src/i18n/locales/el.json b/apps/mobile/src/i18n/locales/el.json index 3187e81a0a..276971f50c 100644 --- a/apps/mobile/src/i18n/locales/el.json +++ b/apps/mobile/src/i18n/locales/el.json @@ -148,7 +148,9 @@ "appearanceSystem": "Σύστημα", "appearanceLight": "Ανοιχτό", "appearanceDark": "Σκούρο", - "account": "Λογαριασμός" + "account": "Λογαριασμός", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Το pull request δεν είναι διαθέσιμο", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Το μήνυμά σας θα χαθεί.", "keepEditing": "Συνέχιση επεξεργασίας", "discard": "Απόρριψη", - "discardFailed": "Δεν ήταν δυνατή η απόρριψη του πρόχειρου. Δοκιμάστε ξανά." + "discardFailed": "Δεν ήταν δυνατή η απόρριψη του πρόχειρου. Δοκιμάστε ξανά.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ενημερώθηκε {{displayCount}} αρχείο", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Δεν ήταν δυνατή η φόρτωση συνεδριών", "couldNotLoadActiveSessions": "Δεν ήταν δυνατή η φόρτωση ενεργών συνεδριών", "agentSessions": "Συνεδρίες πράκτορα", - "showingSavedSessions": "Εμφάνιση αποθηκευμένων συνεδριών — η ζωντανή κατάσταση ενδέχεται να είναι ξεπερασμένη", - "startNewAgentSession": "Έναρξη νέας συνεδρίας Kilo Agent", - "aiCodingSessions": "Συνεδρίες προγραμματισμού με AI", - "startCodingTaskFromPhone": "Ξεκινήστε μια εργασία προγραμματισμού από το τηλέφωνό σας ή συνεχίστε μια συνεδρία από το CLI σας.", - "tryIt": "Δοκιμάστε το", "newCodingTask": "Νέα εργασία προγραμματισμού", "explore": "Εξερεύνηση", "seeAll": "Προβολή όλων", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Κλείσιμο {{filename}}", diff --git a/apps/mobile/src/i18n/locales/en.json b/apps/mobile/src/i18n/locales/en.json index 4306062a21..3275ef0d6b 100644 --- a/apps/mobile/src/i18n/locales/en.json +++ b/apps/mobile/src/i18n/locales/en.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Light", "appearanceDark": "Dark", - "account": "Account" + "account": "Account", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request unavailable", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Your prompt will be lost.", "keepEditing": "Keep editing", "discard": "Discard", - "discardFailed": "Could not discard the draft. Please try again." + "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." }, "partRenderer": { "updatedFileCount_one": "Updated {{displayCount}} file", @@ -2661,16 +2664,11 @@ "home": { "couldNotLoadSessions": "Couldn't load sessions", "couldNotLoadActiveSessions": "Couldn't load active sessions", - "agentSessions": "Agent sessions", - "showingSavedSessions": "Showing saved sessions — live status may be out of date", - "startNewAgentSession": "Start a new Kilo Agent session", - "aiCodingSessions": "AI coding sessions", - "startCodingTaskFromPhone": "Start a coding task from your phone or continue a session from your CLI.", - "tryIt": "Try it", + "agentSessions": "Live now", "newCodingTask": "New coding task", "explore": "Explore", "seeAll": "See all", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Close {{filename}}", diff --git a/apps/mobile/src/i18n/locales/es.json b/apps/mobile/src/i18n/locales/es.json index 62ca5ce446..64f2c479fb 100644 --- a/apps/mobile/src/i18n/locales/es.json +++ b/apps/mobile/src/i18n/locales/es.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Claro", "appearanceDark": "Oscuro", - "account": "Cuenta" + "account": "Cuenta", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "channel": { @@ -1730,7 +1732,8 @@ "discardDraftMessage": "Tu prompt se perderá.", "discardDraftTitle": "¿Descartar borrador?", "discardFailed": "No se pudo descartar el borrador. Inténtalo de nuevo.", - "keepEditing": "Seguir editando" + "keepEditing": "Seguir editando", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} archivo actualizado", @@ -2221,15 +2224,10 @@ "couldNotLoadSessions": "No se pudieron cargar las sesiones", "couldNotLoadActiveSessions": "No se pudieron cargar las sesiones activas", "agentSessions": "Sesiones de agentes", - "showingSavedSessions": "Mostrando sesiones guardadas: el estado en vivo puede estar desactualizado", - "startNewAgentSession": "Iniciar una nueva sesión de Kilo Agent", - "aiCodingSessions": "Sesiones de codificación con IA", - "startCodingTaskFromPhone": "Inicia una tarea de codificación desde tu teléfono o continúa una sesión desde tu CLI.", - "tryIt": "Pruébalo", "newCodingTask": "Nueva tarea de codificación", "explore": "Explorar", "seeAll": "Ver todo", - "kiloAgents": "Agentes de Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Cerrar {{filename}}", diff --git a/apps/mobile/src/i18n/locales/et.json b/apps/mobile/src/i18n/locales/et.json index 8b84b06de0..8447eddd15 100644 --- a/apps/mobile/src/i18n/locales/et.json +++ b/apps/mobile/src/i18n/locales/et.json @@ -148,7 +148,9 @@ "appearanceSystem": "Süsteem", "appearanceLight": "Heledas", "appearanceDark": "Tumedas", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ei ole saadaval", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Sinu päring läheb kaotsi.", "keepEditing": "Jätka muutmist", "discard": "Loobu", - "discardFailed": "Mustandist loobumine ebaõnnestus. Proovi uuesti." + "discardFailed": "Mustandist loobumine ebaõnnestus. Proovi uuesti.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Uuendatud {{displayCount}} fail", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Seansse ei õnnestunud laadida", "couldNotLoadActiveSessions": "Aktiivseid seansse ei õnnestunud laadida", "agentSessions": "Agendi seansid", - "showingSavedSessions": "Kuvatakse salvestatud seansid — otseolek võib olla aegunud", - "startNewAgentSession": "Alusta uut Kilo Agent seanssi", - "aiCodingSessions": "AI kodeerimisseansid", - "startCodingTaskFromPhone": "Alustage kodeerimisülesannet telefonist või jätkake seanssi CLI-st.", - "tryIt": "Proovi", "newCodingTask": "Uus kodeerimisülesanne", "explore": "Avasta", "seeAll": "Vaata kõiki", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Sulge {{filename}}", diff --git a/apps/mobile/src/i18n/locales/eu.json b/apps/mobile/src/i18n/locales/eu.json index 7147fe6c11..9420fa4d32 100644 --- a/apps/mobile/src/i18n/locales/eu.json +++ b/apps/mobile/src/i18n/locales/eu.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Argia", "appearanceDark": "Iluna", - "account": "Kontua" + "account": "Kontua", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request-a ez dago eskuragarri", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Zure proposamena galduko da.", "keepEditing": "Editatzen jarraitu", "discard": "Baztertu", - "discardFailed": "Ezin da zirriborroa baztertu. Saiatu berriro." + "discardFailed": "Ezin da zirriborroa baztertu. Saiatu berriro.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fitxategi eguneratu da", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Ezin izan dira saioak kargatu", "couldNotLoadActiveSessions": "Ezin izan dira saio aktiboak kargatu", "agentSessions": "Agentearen saioak", - "showingSavedSessions": "Gordetako saioak erakusten — egoera zuzena ez egon daiteke", - "startNewAgentSession": "Hasi Kilo Agent saio berri bat", - "aiCodingSessions": "AI kodetze-saioak", - "startCodingTaskFromPhone": "Hasi kodetze-lan bat zure telefonotik edo jarraitu saio bat zure CLI-tik.", - "tryIt": "Probatu", "newCodingTask": "Kodetze-lan berria", "explore": "Arakatu", "seeAll": "Ikusi guztiak", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Itxi {{filename}}", diff --git a/apps/mobile/src/i18n/locales/fa.json b/apps/mobile/src/i18n/locales/fa.json index 4842f3291e..c84b933cb7 100644 --- a/apps/mobile/src/i18n/locales/fa.json +++ b/apps/mobile/src/i18n/locales/fa.json @@ -148,7 +148,9 @@ "appearanceSystem": "سیستم", "appearanceLight": "روشن", "appearanceDark": "تیره", - "account": "حساب" + "account": "حساب", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "درخواست pull در دسترس نیست", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "پیام شما از بین خواهد رفت.", "keepEditing": "ادامه ویرایش", "discard": "دور انداختن", - "discardFailed": "دور انداختن پیشنویس ممکن نشد. لطفاً دوباره تلاش کنید." + "discardFailed": "دور انداختن پیشنویس ممکن نشد. لطفاً دوباره تلاش کنید.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} فایل بهروزرسانی شد", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "جلسات بارگذاری نشدند", "couldNotLoadActiveSessions": "جلسات فعال بارگذاری نشدند", "agentSessions": "جلسات عامل", - "showingSavedSessions": "نمایش جلسات ذخیره‌شده — وضعیت زنده ممکن است به‌روز نباشد", - "startNewAgentSession": "شروع یک جلسه جدید Kilo Agent", - "aiCodingSessions": "جلسات کدنویسی هوش مصنوعی", - "startCodingTaskFromPhone": "از تلفن خود یک کار کدنویسی را شروع کنید یا جلسه را از CLI ادامه دهید.", - "tryIt": "امتحان کنید", "newCodingTask": "کار کدنویسی جدید", "explore": "کاوش", "seeAll": "مشاهده همه", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "بستن {{filename}}", diff --git a/apps/mobile/src/i18n/locales/fi.json b/apps/mobile/src/i18n/locales/fi.json index 5e80ef31c4..7ea3d16800 100644 --- a/apps/mobile/src/i18n/locales/fi.json +++ b/apps/mobile/src/i18n/locales/fi.json @@ -148,7 +148,9 @@ "appearanceSystem": "Järjestelmä", "appearanceLight": "Vaalea", "appearanceDark": "Tumma", - "account": "Tili" + "account": "Tili", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ei ole käytettävissä", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Kehotteesi menetetään.", "keepEditing": "Jatka muokkausta", "discard": "Hylkää", - "discardFailed": "Luonnosta ei voitu hylätä. Yritä uudelleen." + "discardFailed": "Luonnosta ei voitu hylätä. Yritä uudelleen.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Päivitetty {{displayCount}} tiedosto", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Istuntoja ei voitu ladata", "couldNotLoadActiveSessions": "Aktiivisia istuntoja ei voitu ladata", "agentSessions": "Agentti-istunnot", - "showingSavedSessions": "Näytetään tallennetut istunnot — reaaliaikainen tila voi olla vanhentunut", - "startNewAgentSession": "Aloita uusi Kilo Agent -istunto", - "aiCodingSessions": "AI-koodausistunnot", - "startCodingTaskFromPhone": "Aloita koodaustehtävä puhelimestasi tai jatka istuntoa CLI:stäsi.", - "tryIt": "Kokeile", "newCodingTask": "Uusi koodaustehtävä", "explore": "Tutustu", "seeAll": "Näytä kaikki", - "kiloAgents": "Kilo Agentit" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Sulje {{filename}}", diff --git a/apps/mobile/src/i18n/locales/fil.json b/apps/mobile/src/i18n/locales/fil.json index 5b4b0890c4..6a49e56200 100644 --- a/apps/mobile/src/i18n/locales/fil.json +++ b/apps/mobile/src/i18n/locales/fil.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Light", "appearanceDark": "Dark", - "account": "Account" + "account": "Account", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Hindi available ang pull request", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Mawawala ang iyong prompt.", "keepEditing": "Patuloy na mag-edit", "discard": "I-discard", - "discardFailed": "Hindi ma-discard ang draft. Pakisubukang muli." + "discardFailed": "Hindi ma-discard ang draft. Pakisubukang muli.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Na-update ang {{displayCount}} file", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Hindi ma-load ang mga session", "couldNotLoadActiveSessions": "Hindi ma-load ang mga aktibong session", "agentSessions": "Mga agent session", - "showingSavedSessions": "Ipinapakita ang mga naka-save na session — maaaring luma na ang live status", - "startNewAgentSession": "Magsimula ng bagong Kilo Agent session", - "aiCodingSessions": "Mga AI coding session", - "startCodingTaskFromPhone": "Magsimula ng coding task mula sa iyong telepono o magpatuloy ng session mula sa iyong CLI.", - "tryIt": "Subukan ito", "newCodingTask": "Bagong coding task", "explore": "Mag-explore", "seeAll": "Tingnan lahat", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Isara ang {{filename}}", diff --git a/apps/mobile/src/i18n/locales/fr.json b/apps/mobile/src/i18n/locales/fr.json index 0bf00bcac6..f4e27261b1 100644 --- a/apps/mobile/src/i18n/locales/fr.json +++ b/apps/mobile/src/i18n/locales/fr.json @@ -1648,7 +1648,8 @@ "discardDraftMessage": "Votre prompt sera perdu.", "discardDraftTitle": "Abandonner le brouillon ?", "discardFailed": "Impossible d'abandonner le brouillon. Veuillez réessayer.", - "keepEditing": "Continuer à modifier" + "keepEditing": "Continuer à modifier", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fichier mis à jour", @@ -2114,7 +2115,9 @@ "appearanceSystem": "Système", "appearanceLight": "Clair", "appearanceDark": "Sombre", - "account": "Compte" + "account": "Compte", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "addCredits": { "cta": "Ajouter des crédits", @@ -2153,15 +2156,10 @@ "couldNotLoadSessions": "Impossible de charger les sessions", "couldNotLoadActiveSessions": "Impossible de charger les sessions actives", "agentSessions": "Sessions d'agents", - "showingSavedSessions": "Affichage des sessions enregistrées — le statut en direct peut être obsolète", - "startNewAgentSession": "Démarrer une nouvelle session Kilo Agent", - "aiCodingSessions": "Sessions de codage IA", - "startCodingTaskFromPhone": "Démarrez une tâche de codage depuis votre téléphone ou continuez une session depuis votre CLI.", - "tryIt": "Essayer", "newCodingTask": "Nouvelle tâche de codage", "explore": "Explorer", "seeAll": "Tout voir", - "kiloAgents": "Agents Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Fermer {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ga.json b/apps/mobile/src/i18n/locales/ga.json index 603abe730b..2aaab158d1 100644 --- a/apps/mobile/src/i18n/locales/ga.json +++ b/apps/mobile/src/i18n/locales/ga.json @@ -148,7 +148,9 @@ "appearanceSystem": "Córas", "appearanceLight": "Geal", "appearanceDark": "Dorcha", - "account": "Cuntas" + "account": "Cuntas", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Iarratas tarraingthe ar fáil", @@ -2221,7 +2223,8 @@ "discardDraftMessage": "Caillfear do leid.", "keepEditing": "Coinnigh ag eagarthóireacht", "discard": "Caith ar lár", - "discardFailed": "Níorbh fhéidir an dréacht a chaitheamh ar lár. Bain triail as arís le do thoil." + "discardFailed": "Níorbh fhéidir an dréacht a chaitheamh ar lár. Bain triail as arís le do thoil.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Nuashonraíodh {{displayCount}} comhad", @@ -2722,15 +2725,10 @@ "couldNotLoadSessions": "Níorbh fhéidir seisiúin a lódáil", "couldNotLoadActiveSessions": "Níorbh fhéidir seisiúin ghníomhacha a lódáil", "agentSessions": "Seisiúin ghníomhaire", - "showingSavedSessions": "Ag taispeáint seisiúin shábháilte — b'fhéidir go bhfuil an stádas beo as dáta", - "startNewAgentSession": "Tosaigh seisiún Kilo Agent nua", - "aiCodingSessions": "Seisiúin chódaithe AI", - "startCodingTaskFromPhone": "Tosaigh tasc códaithe ó do ghuthán nó lean ar aghaidh le seisiún ó do CLI.", - "tryIt": "Bain triail as", "newCodingTask": "Tasc códaithe nua", "explore": "Taiscéal", "seeAll": "Féach ar gach ceann", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Dún {{filename}}", diff --git a/apps/mobile/src/i18n/locales/gl.json b/apps/mobile/src/i18n/locales/gl.json index 866a18691c..a1ed5184f4 100644 --- a/apps/mobile/src/i18n/locales/gl.json +++ b/apps/mobile/src/i18n/locales/gl.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Claro", "appearanceDark": "Escuro", - "account": "Conta" + "account": "Conta", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request non dispoñible", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Perderase o teu indicación.", "keepEditing": "Continuar editando", "discard": "Descartar", - "discardFailed": "Non se puido descartar o borrador. Téntao de novo." + "discardFailed": "Non se puido descartar o borrador. Téntao de novo.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Actualizouse {{displayCount}} ficheiro", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Non se puideron cargar as sesións", "couldNotLoadActiveSessions": "Non se puideron cargar as sesións activas", "agentSessions": "Sesións de axente", - "showingSavedSessions": "Mostrando sesións gardadas — o estado en directo pode estar desactualizado", - "startNewAgentSession": "Iniciar unha nova sesión de Kilo Agent", - "aiCodingSessions": "Sesións de codificación con IA", - "startCodingTaskFromPhone": "Inicia unha tarefa de codificación desde o teu teléfono ou continúa unha sesión desde o teu CLI.", - "tryIt": "Probala", "newCodingTask": "Nova tarefa de codificación", "explore": "Explorar", "seeAll": "Ver todo", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Pechar {{filename}}", diff --git a/apps/mobile/src/i18n/locales/gu.json b/apps/mobile/src/i18n/locales/gu.json index c3a428ca02..fc93eb503e 100644 --- a/apps/mobile/src/i18n/locales/gu.json +++ b/apps/mobile/src/i18n/locales/gu.json @@ -148,7 +148,9 @@ "appearanceSystem": "સિસ્ટમ", "appearanceLight": "લાઇટ", "appearanceDark": "ડાર્ક", - "account": "એકાઉન્ટ" + "account": "એકાઉન્ટ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "પુલ રિક્વેસ્ટ ઉપલબ્ધ નથી", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "તમારો પ્રોમ્પ્ટ ખોવાઈ જશે.", "keepEditing": "સંપાદન ચાલુ રાખો", "discard": "કાઢી નાખો", - "discardFailed": "ડ્રાફ્ટ કાઢી નાખી શકાયો નથી. કૃપા કરીને ફરી પ્રયાસ કરો." + "discardFailed": "ડ્રાફ્ટ કાઢી નાખી શકાયો નથી. કૃપા કરીને ફરી પ્રયાસ કરો.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ફાઇલ અપડેટ કરેલ", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "સત્રો લોડ કરી શકાયા નહીં", "couldNotLoadActiveSessions": "સક્રિય સત્રો લોડ કરી શકાયા નહીં", "agentSessions": "એજન્ટ સત્રો", - "showingSavedSessions": "સાચવેલા સત્રો બતાવી રહ્યા છીએ — લાઇવ સ્થિતિ જૂની હોઈ શકે છે", - "startNewAgentSession": "નવું Kilo એજન્ટ સત્ર શરૂ કરો", - "aiCodingSessions": "AI કોડિંગ સત્રો", - "startCodingTaskFromPhone": "તમારા ફોનથી કોડિંગ કાર્ય શરૂ કરો અથવા તમારા CLI માંથી સત્ર ચાલુ રાખો.", - "tryIt": "અજમાવો", "newCodingTask": "નવું કોડિંગ કાર્ય", "explore": "શોધો", "seeAll": "બધા જુઓ", - "kiloAgents": "Kilo એજન્ટ્સ" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} બંધ કરો", diff --git a/apps/mobile/src/i18n/locales/ha.json b/apps/mobile/src/i18n/locales/ha.json index 388ed226fd..d6b2757baf 100644 --- a/apps/mobile/src/i18n/locales/ha.json +++ b/apps/mobile/src/i18n/locales/ha.json @@ -148,7 +148,9 @@ "appearanceSystem": "Tsarin", "appearanceLight": "Haske", "appearanceDark": "Duhu", - "account": "Asusu" + "account": "Asusu", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Buƙatar jan ba ta samuwa", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Za a ɓata bayaninka.", "keepEditing": "Ci gaba da gyarawa", "discard": "Watsi", - "discardFailed": "An kasa watsi da daftarin. Da fatan za a sake gwadawa." + "discardFailed": "An kasa watsi da daftarin. Da fatan za a sake gwadawa.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "An sabunta {{displayCount}} fayil", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "An kasa ɗora zaman", "couldNotLoadActiveSessions": "An kasa ɗora zaman aiki", "agentSessions": "Zaman wakilai", - "showingSavedSessions": "Ana nuna zaman da aka adana — matsayin kai tsaye na iya zama tsoho", - "startNewAgentSession": "Fara sabon zaman Kilo Agent", - "aiCodingSessions": "Zaman shirye-shiryen AI", - "startCodingTaskFromPhone": "Fara aikin shirye-shirye daga wayarka ko ci gaba da zama daga CLI ɗinka.", - "tryIt": "Gwada shi", "newCodingTask": "Sabon aikin shirye-shirye", "explore": "Bincika", "seeAll": "Duba duka", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Rufe {{filename}}", diff --git a/apps/mobile/src/i18n/locales/he.json b/apps/mobile/src/i18n/locales/he.json index 9fca20e1be..2cc596b63d 100644 --- a/apps/mobile/src/i18n/locales/he.json +++ b/apps/mobile/src/i18n/locales/he.json @@ -148,7 +148,9 @@ "appearanceSystem": "מערכת", "appearanceLight": "בהיר", "appearanceDark": "כהה", - "account": "חשבון" + "account": "חשבון", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "התראות", @@ -1730,7 +1732,8 @@ "discardDraftMessage": "ההנחיה שלך תאבד.", "discardDraftTitle": "לבטל את הטיוטה?", "discardFailed": "לא ניתן היה לבטל את הטיוטה. נסה שוב.", - "keepEditing": "המשך לערוך" + "keepEditing": "המשך לערוך", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "עודכן {{displayCount}} קובץ", @@ -2221,15 +2224,10 @@ "couldNotLoadSessions": "לא ניתן לטעון הפעלות", "couldNotLoadActiveSessions": "לא ניתן לטעון הפעלות פעילות", "agentSessions": "הפעלות סוכן", - "showingSavedSessions": "מציג הפעלות שמורות — המצב החי עשוי להיות לא מעודכן", - "startNewAgentSession": "התחל הפעלת Kilo Agent חדשה", - "aiCodingSessions": "הפעלות קידוד AI", - "startCodingTaskFromPhone": "התחל משימת קידוד מהטלפון שלך או המשך הפעלה מה-CLI שלך.", - "tryIt": "נסה זאת", "newCodingTask": "משימת קידוד חדשה", "explore": "חקור", "seeAll": "ראה הכל", - "kiloAgents": "סוכני Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "סגור את {{filename}}", diff --git a/apps/mobile/src/i18n/locales/hi.json b/apps/mobile/src/i18n/locales/hi.json index 94107c0c25..abda782006 100644 --- a/apps/mobile/src/i18n/locales/hi.json +++ b/apps/mobile/src/i18n/locales/hi.json @@ -148,7 +148,9 @@ "appearanceSystem": "सिस्टम", "appearanceLight": "हल्का", "appearanceDark": "गहरा", - "account": "खाता" + "account": "खाता", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "सूचनाएँ", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "आपका प्रॉम्प्ट खो जाएगा।", "discardDraftTitle": "ड्राफ़्ट छोड़ें?", "discardFailed": "ड्राफ़्ट छोड़ा नहीं जा सका। कृपया फिर से कोशिश करें।", - "keepEditing": "संपादन जारी रखें" + "keepEditing": "संपादन जारी रखें", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} फ़ाइल अपडेट की गई", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "सत्र लोड नहीं हो सके", "couldNotLoadActiveSessions": "सक्रिय सत्र लोड नहीं हो सके", "agentSessions": "एजेंट सत्र", - "showingSavedSessions": "सहेजे गए सत्र दिखाए जा रहे हैं — लाइव स्थिति पुरानी हो सकती है", - "startNewAgentSession": "नया Kilo एजेंट सत्र शुरू करें", - "aiCodingSessions": "AI कोडिंग सत्र", - "startCodingTaskFromPhone": "अपने फ़ोन से कोडिंग कार्य शुरू करें या अपने CLI से सत्र जारी रखें।", - "tryIt": "इसे आज़माएँ", "newCodingTask": "नया कोडिंग कार्य", "explore": "एक्सप्लोर करें", "seeAll": "सभी देखें", - "kiloAgents": "Kilo एजेंट" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} बंद करें", diff --git a/apps/mobile/src/i18n/locales/hr.json b/apps/mobile/src/i18n/locales/hr.json index a326f7c7f8..c5062788d7 100644 --- a/apps/mobile/src/i18n/locales/hr.json +++ b/apps/mobile/src/i18n/locales/hr.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sustav", "appearanceLight": "Svijetlo", "appearanceDark": "Tamno", - "account": "Račun" + "account": "Račun", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nedostupan", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "Vaš će upit biti izgubljen.", "keepEditing": "Nastavi s uređivanjem", "discard": "Odbaci", - "discardFailed": "Nije moguće odbaciti nacrt. Pokušajte ponovno." + "discardFailed": "Nije moguće odbaciti nacrt. Pokušajte ponovno.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ažurirana {{displayCount}} datoteka", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Nije moguće učitati sesije", "couldNotLoadActiveSessions": "Nije moguće učitati aktivne sesije", "agentSessions": "Sesije agenata", - "showingSavedSessions": "Prikazuju se spremljene sesije — živi status može biti zastario", - "startNewAgentSession": "Pokreni novu Kilo Agent sesiju", - "aiCodingSessions": "AI sesije kodiranja", - "startCodingTaskFromPhone": "Pokrenite zadatak kodiranja s telefona ili nastavite sesiju sa svog CLI-ja.", - "tryIt": "Isprobaj", "newCodingTask": "Novi zadatak kodiranja", "explore": "Istraži", "seeAll": "Prikaži sve", - "kiloAgents": "Kilo Agenti" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zatvori {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ht.json b/apps/mobile/src/i18n/locales/ht.json index 1623abeae8..a40618b668 100644 --- a/apps/mobile/src/i18n/locales/ht.json +++ b/apps/mobile/src/i18n/locales/ht.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistèm", "appearanceLight": "Limye", "appearanceDark": "Fènwa", - "account": "Kont" + "account": "Kont", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Demand rale pa disponib", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Pwòp prompt ou a pral pèdi.", "keepEditing": "Kontinye Editè", "discard": "Jete", - "discardFailed": "Pa t 'kapab jete bouyon an. Tanpri eseye ankò." + "discardFailed": "Pa t 'kapab jete bouyon an. Tanpri eseye ankò.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Miz ajou {{displayCount}} fichye", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Pa t ka chaje sesyon", "couldNotLoadActiveSessions": "Pa t ka chaje sesyon aktif", "agentSessions": "Sesyon ajant", - "showingSavedSessions": "Ap montre sesyon sove — estati vivan ka pa fèt rive", - "startNewAgentSession": "Kòmanse yon nouvo sesyon Kilo Ajant", - "aiCodingSessions": "Sesyon kodaj AI", - "startCodingTaskFromPhone": "Kòmanse yon travay kodaj soti nan telefòn ou oswa kontinye yon sesyon soti nan CLI ou.", - "tryIt": "Eseye li", "newCodingTask": "Nouvo travay kodaj", "explore": "Eksplore", "seeAll": "Wè tout", - "kiloAgents": "Kilo Ajant" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Fèmen {{filename}}", diff --git a/apps/mobile/src/i18n/locales/hu.json b/apps/mobile/src/i18n/locales/hu.json index 68344e07ca..a0b7f54735 100644 --- a/apps/mobile/src/i18n/locales/hu.json +++ b/apps/mobile/src/i18n/locales/hu.json @@ -148,7 +148,9 @@ "appearanceSystem": "Rendszer", "appearanceLight": "Világos", "appearanceDark": "Sötét", - "account": "Fiók" + "account": "Fiók", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nem érhető el", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "A promptja el fog veszni.", "keepEditing": "Szerkesztés folytatása", "discard": "Elvetés", - "discardFailed": "A piszkozat elvetése nem sikerült. Próbálja újra." + "discardFailed": "A piszkozat elvetése nem sikerült. Próbálja újra.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fájl frissítve", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "A munkameneteket nem sikerült betölteni", "couldNotLoadActiveSessions": "Az aktív munkameneteket nem sikerült betölteni", "agentSessions": "Ügynök munkamenetek", - "showingSavedSessions": "Mentett munkamenetek megjelenítése — az élő állapot elavult lehet", - "startNewAgentSession": "Új Kilo Agent munkamenet indítása", - "aiCodingSessions": "AI kódolási munkamenetek", - "startCodingTaskFromPhone": "Indíts kódolási feladatot a telefonodról, vagy folytass egy munkamenetet a CLI-dből.", - "tryIt": "Kipróbálom", "newCodingTask": "Új kódolási feladat", "explore": "Felfedezés", "seeAll": "Összes megtekintése", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} bezárása", diff --git a/apps/mobile/src/i18n/locales/hy.json b/apps/mobile/src/i18n/locales/hy.json index 1ef4ba1003..7efc2b9fcd 100644 --- a/apps/mobile/src/i18n/locales/hy.json +++ b/apps/mobile/src/i18n/locales/hy.json @@ -148,7 +148,9 @@ "appearanceSystem": "Համակարգ", "appearanceLight": "Բաց", "appearanceDark": "Մուգ", - "account": "Հաշիվ" + "account": "Հաշիվ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request-ը հասանելի չէ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Ձեր հուշումը կկորչի։", "keepEditing": "Շարունակել խմբագրել", "discard": "Մերժել", - "discardFailed": "Չհաջողվեց մերժել սևագիրը։ Խնդրում ենք կրկին փորձել։" + "discardFailed": "Չհաջողվեց մերժել սևագիրը։ Խնդրում ենք կրկին փորձել։", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Թարմացվել է {{displayCount}} ֆայլ", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Հնարավոր չեղավ բեռնել նիստերը", "couldNotLoadActiveSessions": "Հնարավոր չեղավ բեռնել ակտիվ նիստերը", "agentSessions": "Գործակալի նիստեր", - "showingSavedSessions": "Ցուցադրվում են պահված նիստերը — կենդանի կարգավիճակը կարող է հնացած լինել", - "startNewAgentSession": "Սկսել նոր Kilo Agent նիստ", - "aiCodingSessions": "AI ծրագրավորման նիստեր", - "startCodingTaskFromPhone": "Սկսեք ծրագրավորման առաջադրանք ձեր հեռախոսից կամ շարունակեք նիստը ձեր CLI-ից:", - "tryIt": "Փորձեք", "newCodingTask": "Նոր ծրագրավորման առաջադրանք", "explore": "Ուսումնասիրել", "seeAll": "Տեսնել բոլորը", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Փակել {{filename}}-ը", diff --git a/apps/mobile/src/i18n/locales/id.json b/apps/mobile/src/i18n/locales/id.json index a37c7dabea..c7747e0c6b 100644 --- a/apps/mobile/src/i18n/locales/id.json +++ b/apps/mobile/src/i18n/locales/id.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Terang", "appearanceDark": "Gelap", - "account": "Akun" + "account": "Akun", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Notifikasi", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "Prompt Anda akan hilang.", "discardDraftTitle": "Buang draf?", "discardFailed": "Tidak dapat membuang draf. Silakan coba lagi.", - "keepEditing": "Tetap mengedit" + "keepEditing": "Tetap mengedit", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Memperbarui {{displayCount}} file", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "Tidak dapat memuat sesi", "couldNotLoadActiveSessions": "Tidak dapat memuat sesi aktif", "agentSessions": "Sesi agen", - "showingSavedSessions": "Menampilkan sesi tersimpan — status langsung mungkin kedaluwarsa", - "startNewAgentSession": "Mulai sesi Agen Kilo baru", - "aiCodingSessions": "Sesi coding AI", - "startCodingTaskFromPhone": "Mulai tugas coding dari ponsel Anda atau lanjutkan sesi dari CLI Anda.", - "tryIt": "Coba", "newCodingTask": "Tugas coding baru", "explore": "Jelajahi", "seeAll": "Lihat semua", - "kiloAgents": "Agen Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Tutup {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ig.json b/apps/mobile/src/i18n/locales/ig.json index 6648d92eaa..9bd05e1fd4 100644 --- a/apps/mobile/src/i18n/locales/ig.json +++ b/apps/mobile/src/i18n/locales/ig.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistemụ", "appearanceLight": "Ìhè", "appearanceDark": "Ọchịchịrị", - "account": "Akaụntụ" + "account": "Akaụntụ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Arịrịọ dọrọ adịghị", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Ihe ị dere ga-efu.", "keepEditing": "Nọgide na-edezi", "discard": "Tụfuo", - "discardFailed": "Enweghị ike ịtụfu akwụkwọ a na-ede. Biko nwaa ọzọ." + "discardFailed": "Enweghị ike ịtụfu akwụkwọ a na-ede. Biko nwaa ọzọ.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Emelitere {{displayCount}} faịlụ", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Enweghị ike ibutu nnọkọ", "couldNotLoadActiveSessions": "Enweghị ike ibutu nnọkọ na-arụ ọrụ", "agentSessions": "Nnọkọ onye nnọchite", - "showingSavedSessions": "Na-egosi nnọkọ echekwara — ọnọdụ ndụ nwere ike ịgafe oge", - "startNewAgentSession": "Malite nnọkọ Kilo Agent ọhụrụ", - "aiCodingSessions": "Nnọkọ ịde koodu AI", - "startCodingTaskFromPhone": "Malite ọrụ ịde koodu site na ekwentị gị ma ọ bụ gaa n'ihu na nnọkọ site na CLI gị.", - "tryIt": "Nwaa ya", "newCodingTask": "Ọrụ ịde koodu ọhụrụ", "explore": "Chọpụta", "seeAll": "Hụ niile", - "kiloAgents": "Ndị nnọchite Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Mechie {{filename}}", diff --git a/apps/mobile/src/i18n/locales/is.json b/apps/mobile/src/i18n/locales/is.json index 0edb0a5e81..b231258f38 100644 --- a/apps/mobile/src/i18n/locales/is.json +++ b/apps/mobile/src/i18n/locales/is.json @@ -148,7 +148,9 @@ "appearanceSystem": "Kerfi", "appearanceLight": "Ljóst", "appearanceDark": "Dökkt", - "account": "Reikningur" + "account": "Reikningur", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ótiltækur", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Skilaboðin þín glatast.", "keepEditing": "Halda áfram að breyta", "discard": "Farga", - "discardFailed": "Ekki tókst að farga drögunum. Reyndu aftur." + "discardFailed": "Ekki tókst að farga drögunum. Reyndu aftur.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Uppfærði {{displayCount}} skrá", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Ekki tókst að hlaða lotur", "couldNotLoadActiveSessions": "Ekki tókst að hlaða virkar lotur", "agentSessions": "Umboðslotur", - "showingSavedSessions": "Sýni vistaðar lotur — rauntímastaða getur verið úrelt", - "startNewAgentSession": "Byrja nýja Kilo-umboðslotu", - "aiCodingSessions": "AI-forritunarlotur", - "startCodingTaskFromPhone": "Byrjaðu forritunarverkefni úr símanum þínum eða haltu áfram lotu frá CLI.", - "tryIt": "Prófaðu það", "newCodingTask": "Nýtt forritunarverkefni", "explore": "Kanna", "seeAll": "Sjá allt", - "kiloAgents": "Kilo-umboð" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Loka {{filename}}", diff --git a/apps/mobile/src/i18n/locales/it.json b/apps/mobile/src/i18n/locales/it.json index a79bcc768d..7cb64b5d5b 100644 --- a/apps/mobile/src/i18n/locales/it.json +++ b/apps/mobile/src/i18n/locales/it.json @@ -1716,7 +1716,8 @@ "discardDraftMessage": "Il tuo prompt andrà perso.", "discardDraftTitle": "Scartare la bozza?", "discardFailed": "Impossibile scartare la bozza. Riprova.", - "keepEditing": "Continua a modificare" + "keepEditing": "Continua a modificare", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Aggiornato {{displayCount}} file", @@ -2182,7 +2183,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Chiaro", "appearanceDark": "Scuro", - "account": "Account" + "account": "Account", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "addCredits": { "cta": "Aggiungi crediti", @@ -2221,15 +2224,10 @@ "couldNotLoadSessions": "Impossibile caricare le sessioni", "couldNotLoadActiveSessions": "Impossibile caricare le sessioni attive", "agentSessions": "Sessioni agente", - "showingSavedSessions": "Visualizzazione delle sessioni salvate — lo stato live potrebbe non essere aggiornato", - "startNewAgentSession": "Avvia una nuova sessione Kilo Agent", - "aiCodingSessions": "Sessioni di codifica IA", - "startCodingTaskFromPhone": "Avvia un'attività di codifica dal telefono o continua una sessione dalla CLI.", - "tryIt": "Provalo", "newCodingTask": "Nuova attività di codifica", "explore": "Esplora", "seeAll": "Vedi tutto", - "kiloAgents": "Agenti Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Chiudi {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ja.json b/apps/mobile/src/i18n/locales/ja.json index 86643aa019..a74bf2be2e 100644 --- a/apps/mobile/src/i18n/locales/ja.json +++ b/apps/mobile/src/i18n/locales/ja.json @@ -148,7 +148,9 @@ "appearanceSystem": "システム", "appearanceLight": "ライト", "appearanceDark": "ダーク", - "account": "アカウント" + "account": "アカウント", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "通知", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "プロンプトは失われます。", "discardDraftTitle": "下書きを破棄しますか?", "discardFailed": "下書きを破棄できませんでした。もう一度お試しください。", - "keepEditing": "編集を続ける" + "keepEditing": "編集を続ける", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}}件のファイルを更新しました", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "セッションを読み込めませんでした", "couldNotLoadActiveSessions": "アクティブなセッションを読み込めませんでした", "agentSessions": "エージェントセッション", - "showingSavedSessions": "保存されたセッションを表示中 — ライブステータスが古い場合があります", - "startNewAgentSession": "新しいKiloエージェントセッションを開始", - "aiCodingSessions": "AIコーディングセッション", - "startCodingTaskFromPhone": "スマートフォンからコーディングタスクを開始するか、CLIからセッションを続行します。", - "tryIt": "試す", "newCodingTask": "新しいコーディングタスク", "explore": "探索", "seeAll": "すべて表示", - "kiloAgents": "Kiloエージェント" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}}を閉じる", diff --git a/apps/mobile/src/i18n/locales/ka.json b/apps/mobile/src/i18n/locales/ka.json index 6e5b5e15b9..be67cb3713 100644 --- a/apps/mobile/src/i18n/locales/ka.json +++ b/apps/mobile/src/i18n/locales/ka.json @@ -148,7 +148,9 @@ "appearanceSystem": "სისტემა", "appearanceLight": "ღია", "appearanceDark": "მუქი", - "account": "ანგარიში" + "account": "ანგარიში", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "პულ-რექვესთი მიუწვდომელია", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "თქვენი შეტყობინება დაიკარგება.", "keepEditing": "რედაქტირების გაგრძელება", "discard": "გაუქმება", - "discardFailed": "დრაფტის გაუქმება ვერ მოხერხდა. გთხოვთ, სცადეთ ხელახლა." + "discardFailed": "დრაფტის გაუქმება ვერ მოხერხდა. გთხოვთ, სცადეთ ხელახლა.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "განახლდა {{displayCount}} ფაილი", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "სესიების ჩატვირთვა ვერ მოხერხდა", "couldNotLoadActiveSessions": "აქტიური სესიების ჩატვირთვა ვერ მოხერხდა", "agentSessions": "აგენტის სესიები", - "showingSavedSessions": "ნაჩვენებია შენახული სესიები — პირდაპირი სტატუსი შეიძლება მოძველებული იყოს", - "startNewAgentSession": "ახალი Kilo აგენტის სესიის დაწყება", - "aiCodingSessions": "AI კოდირების სესიები", - "startCodingTaskFromPhone": "დაიწყეთ კოდირების ამოცანა ტელეფონიდან ან გააგრძელეთ სესია CLI-დან.", - "tryIt": "სცადეთ", "newCodingTask": "ახალი კოდირების ამოცანა", "explore": "გამოკვლევა", "seeAll": "ყველას ნახვა", - "kiloAgents": "Kilo აგენტები" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} დახურვა", diff --git a/apps/mobile/src/i18n/locales/kk.json b/apps/mobile/src/i18n/locales/kk.json index 3de9aff0e1..5872837111 100644 --- a/apps/mobile/src/i18n/locales/kk.json +++ b/apps/mobile/src/i18n/locales/kk.json @@ -148,7 +148,9 @@ "appearanceSystem": "Жүйе", "appearanceLight": "Жарық", "appearanceDark": "Қараңғы", - "account": "Тіркелгі" + "account": "Тіркелгі", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request қолжетімсіз", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Сұрағыңыз жоғалады.", "keepEditing": "Өңдеуді жалғастыру", "discard": "Жою", - "discardFailed": "Жобаны жою мүмкін болмады. Қайталап көріңіз." + "discardFailed": "Жобаны жою мүмкін болмады. Қайталап көріңіз.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} файл жаңартылды", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Сессияларды жүктеу мүмкін болмады", "couldNotLoadActiveSessions": "Белсенді сессияларды жүктеу мүмкін болмады", "agentSessions": "Агент сессиялары", - "showingSavedSessions": "Сақталған сессиялар көрсетілуде — тірі күй ескіруі мүмкін", - "startNewAgentSession": "Жаңа Kilo Agent сессиясын бастау", - "aiCodingSessions": "AI бағдарламалау сессиялары", - "startCodingTaskFromPhone": "Телефоннан бағдарламалау тапсырмасын бастаңыз немесе CLI-ден сессияны жалғастырыңыз.", - "tryIt": "Көру", "newCodingTask": "Жаңа бағдарламалау тапсырмасы", "explore": "Зерттеу", "seeAll": "Барлығын қарау", - "kiloAgents": "Kilo Agent" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} жабу", diff --git a/apps/mobile/src/i18n/locales/km.json b/apps/mobile/src/i18n/locales/km.json index 5a0f3fe509..18872d2a18 100644 --- a/apps/mobile/src/i18n/locales/km.json +++ b/apps/mobile/src/i18n/locales/km.json @@ -148,7 +148,9 @@ "appearanceSystem": "ប្រព័ន្ធ", "appearanceLight": "ភ្លឺ", "appearanceDark": "ងងឹត", - "account": "គណនី" + "account": "គណនី", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "សំណើទាញ (pull request) មិនអាចប្រើបានទេ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ប្រអប់បញ្ចូលរបស់អ្នកនឹងត្រូវបាត់បង់។", "keepEditing": "បន្តកែសម្រួល", "discard": "បោះបង់", - "discardFailed": "មិនអាចបោះបង់សេចក្តីព្រាងបានទេ។ សូមព្យាយាមម្តងទៀត។" + "discardFailed": "មិនអាចបោះបង់សេចក្តីព្រាងបានទេ។ សូមព្យាយាមម្តងទៀត។", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "បានធ្វើបច្ចុប្បន្នភាពឯកសារ {{displayCount}}", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "មិនអាចផ្ទុកវគ្គបានទេ", "couldNotLoadActiveSessions": "មិនអាចផ្ទុកវគ្គសកម្មបានទេ", "agentSessions": "វគ្គភ្នាក់ងារ", - "showingSavedSessions": "កំពុងបង្ហាញវគ្គដែលបានរក្សាទុក — ស្ថានភាពបន្តផ្ទាល់អាចហួសសម័យ", - "startNewAgentSession": "ចាប់ផ្តើមវគ្គ Kilo Agent ថ្មី", - "aiCodingSessions": "វគ្គសរសេរកូដ AI", - "startCodingTaskFromPhone": "ចាប់ផ្តើមកិច្ចការសរសេរកូដពីទូរសព្ទរបស់អ្នក ឬបន្តវគ្គពី CLI របស់អ្នក។", - "tryIt": "សាកល្បង", "newCodingTask": "កិច្ចការសរសេរកូដថ្មី", "explore": "ស្វែងយល់", "seeAll": "មើលទាំងអស់", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "បិទ {{filename}}", diff --git a/apps/mobile/src/i18n/locales/kn.json b/apps/mobile/src/i18n/locales/kn.json index 8418878554..82aaa5f329 100644 --- a/apps/mobile/src/i18n/locales/kn.json +++ b/apps/mobile/src/i18n/locales/kn.json @@ -148,7 +148,9 @@ "appearanceSystem": "ಸಿಸ್ಟಮ್", "appearanceLight": "ಲೈಟ್", "appearanceDark": "ಡಾರ್ಕ್", - "account": "ಖಾತೆ" + "account": "ಖಾತೆ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "ಪುಲ್ ರಿಕ್ವೆಸ್ಟ್ ಲಭ್ಯವಿಲ್ಲ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ನಿಮ್ಮ ಪ್ರಾಂಪ್ಟ್ ಕಳೆದುಹೋಗುತ್ತದೆ.", "keepEditing": "ಸಂಪಾದನೆಯನ್ನು ಮುಂದುವರಿಸಿ", "discard": "ತೆಗೆದುಹಾಕಿ", - "discardFailed": "ಡ್ರಾಫ್ಟ್ ತೆಗೆದುಹಾಕಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ." + "discardFailed": "ಡ್ರಾಫ್ಟ್ ತೆಗೆದುಹಾಕಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ಫೈಲ್ ನವೀಕರಿಸಲಾಗಿದೆ", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "ಸೆಷನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ", "couldNotLoadActiveSessions": "ಸಕ್ರಿಯ ಸೆಷನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ", "agentSessions": "ಏಜೆಂಟ್ ಸೆಷನ್‌ಗಳು", - "showingSavedSessions": "ಉಳಿಸಿದ ಸೆಷನ್‌ಗಳನ್ನು ತೋರಿಸುತ್ತಿದೆ — ಲೈವ್ ಸ್ಥಿತಿ ಹಳೆಯದಾಗಿರಬಹುದು", - "startNewAgentSession": "ಹೊಸ Kilo Agent ಸೆಷನ್ ಪ್ರಾರಂಭಿಸಿ", - "aiCodingSessions": "AI ಕೋಡಿಂಗ್ ಸೆಷನ್‌ಗಳು", - "startCodingTaskFromPhone": "ನಿಮ್ಮ ಫೋನ್‌ನಿಂದ ಕೋಡಿಂಗ್ ಕಾರ್ಯ ಪ್ರಾರಂಭಿಸಿ ಅಥವಾ ನಿಮ್ಮ CLI ಯಿಂದ ಸೆಷನ್ ಮುಂದುವರಿಸಿ.", - "tryIt": "ಪ್ರಯತ್ನಿಸಿ", "newCodingTask": "ಹೊಸ ಕೋಡಿಂಗ್ ಕಾರ್ಯ", "explore": "ಅನ್ವೇಷಿಸಿ", "seeAll": "ಎಲ್ಲವನ್ನೂ ನೋಡಿ", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} ಮುಚ್ಚಿ", diff --git a/apps/mobile/src/i18n/locales/ko.json b/apps/mobile/src/i18n/locales/ko.json index a4e9241094..80153760f0 100644 --- a/apps/mobile/src/i18n/locales/ko.json +++ b/apps/mobile/src/i18n/locales/ko.json @@ -148,7 +148,9 @@ "appearanceSystem": "시스템", "appearanceLight": "밝음", "appearanceDark": "어두움", - "account": "계정" + "account": "계정", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "알림", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "프롬프트가 사라집니다.", "discardDraftTitle": "초안을 삭제할까요?", "discardFailed": "초안을 삭제할 수 없습니다. 다시 시도하세요.", - "keepEditing": "계속 수정" + "keepEditing": "계속 수정", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}}개 파일 업데이트됨", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "세션을 불러올 수 없습니다", "couldNotLoadActiveSessions": "활성 세션을 불러올 수 없습니다", "agentSessions": "에이전트 세션", - "showingSavedSessions": "저장된 세션 표시 중 — 실시간 상태가 오래되었을 수 있습니다", - "startNewAgentSession": "새 Kilo 에이전트 세션 시작", - "aiCodingSessions": "AI 코딩 세션", - "startCodingTaskFromPhone": "휴대폰에서 코딩 작업을 시작하거나 CLI에서 세션을 계속하세요.", - "tryIt": "사용해 보기", "newCodingTask": "새 코딩 작업", "explore": "탐색", "seeAll": "모두 보기", - "kiloAgents": "Kilo 에이전트" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} 닫기", diff --git a/apps/mobile/src/i18n/locales/lo.json b/apps/mobile/src/i18n/locales/lo.json index 9e49fa8614..dcace57225 100644 --- a/apps/mobile/src/i18n/locales/lo.json +++ b/apps/mobile/src/i18n/locales/lo.json @@ -148,7 +148,9 @@ "appearanceSystem": "ລະບົບ", "appearanceLight": "ສະຫວ່າງ", "appearanceDark": "ມືດ", - "account": "ບັນຊີ" + "account": "ບັນຊີ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "pull request ບໍ່ພ້ອມໃຊ້", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ຂໍ້ຄວາມຂອງທ່ານຈະຖືກລຶບ.", "keepEditing": "ແກ້ໄຂຕໍ່ໄປ", "discard": "ລະເລີຍ", - "discardFailed": "ບໍ່ສາມາດລະເລີຍຮ່າງ. ກະລຸນາລອງໃໝ່." + "discardFailed": "ບໍ່ສາມາດລະເລີຍຮ່າງ. ກະລຸນາລອງໃໝ່.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "ອັບເດດ {{displayCount}} ໄຟລ໌", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "ບໍ່ສາມາດໂຫລດເຊດຊັນ", "couldNotLoadActiveSessions": "ບໍ່ສາມາດໂຫລດເຊດຊັນທີ່ໃຊ້ງານ", "agentSessions": "ເຊດຊັນຕົວແທນ", - "showingSavedSessions": "ສະແດງເຊດຊັນທີ່ບັນທຶກ — ສະຖານະສົດອາດລ້າສະໄຫມ", - "startNewAgentSession": "ເລີ່ມເຊດຊັນ Kilo Agent ໃໝ່", - "aiCodingSessions": "ເຊດຊັນການຂຽນໂຄ້ດ AI", - "startCodingTaskFromPhone": "ເລີ່ມວຽກການຂຽນໂຄ້ດຈາກໂທລະສັບຂອງທ່ານ ຫຼື ສືບຕໍ່ເຊດຊັນຈາກ CLI ຂອງທ່ານ.", - "tryIt": "ລອງໃຊ້", "newCodingTask": "ວຽກການຂຽນໂຄ້ດໃໝ່", "explore": "ສຳຫຼວດ", "seeAll": "ເບິ່ງທັງໝົດ", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "ປິດ {{filename}}", diff --git a/apps/mobile/src/i18n/locales/lt.json b/apps/mobile/src/i18n/locales/lt.json index 7fc514cef3..58f6148de3 100644 --- a/apps/mobile/src/i18n/locales/lt.json +++ b/apps/mobile/src/i18n/locales/lt.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Šviesi", "appearanceDark": "Tamsi", - "account": "Paskyra" + "account": "Paskyra", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nepasiekiamas", @@ -2206,7 +2208,8 @@ "discardDraftMessage": "Jūsų užklausa bus prarasta.", "keepEditing": "Tęsti redagavimą", "discard": "Atmesti", - "discardFailed": "Nepavyko atmesti juodraščio. Bandykite dar kartą." + "discardFailed": "Nepavyko atmesti juodraščio. Bandykite dar kartą.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Atnaujinta {{displayCount}} rinkmena", @@ -2702,15 +2705,10 @@ "couldNotLoadSessions": "Nepavyko įkelti sesijų", "couldNotLoadActiveSessions": "Nepavyko įkelti aktyvių sesijų", "agentSessions": "Agentų sesijos", - "showingSavedSessions": "Rodyti išsaugotos sesijos — tiesioginis statusas gali būti pasenęs", - "startNewAgentSession": "Pradėti naują Kilo agento sesiją", - "aiCodingSessions": "AI kodavimo sesijos", - "startCodingTaskFromPhone": "Pradėkite kodavimo užduotį iš savo telefono arba tęskite sesiją iš savo CLI.", - "tryIt": "Išbandyti", "newCodingTask": "Nauja kodavimo užduotis", "explore": "Naršyti", "seeAll": "Rodyti viską", - "kiloAgents": "Kilo agentai" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Uždaryti {{filename}}", diff --git a/apps/mobile/src/i18n/locales/lv.json b/apps/mobile/src/i18n/locales/lv.json index a3cecd5137..7f3da3ecc4 100644 --- a/apps/mobile/src/i18n/locales/lv.json +++ b/apps/mobile/src/i18n/locales/lv.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistēma", "appearanceLight": "Gaišs", "appearanceDark": "Tumšs", - "account": "Konts" + "account": "Konts", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nav pieejams", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "Tavs jautājums tiks zaudēts.", "keepEditing": "Turpināt rediģēšanu", "discard": "Atmest", - "discardFailed": "Neizdevās atmest melnrakstu. Lūdzu, mēģini vēlreiz." + "discardFailed": "Neizdevās atmest melnrakstu. Lūdzu, mēģini vēlreiz.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Atjaunināts {{displayCount}} fails", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Neizdevās ielādēt sesijas", "couldNotLoadActiveSessions": "Neizdevās ielādēt aktīvās sesijas", "agentSessions": "Aģenta sesijas", - "showingSavedSessions": "Tiek rādītas saglabātās sesijas — tiešais statuss var būt novecojis", - "startNewAgentSession": "Sākt jaunu Kilo Agent sesiju", - "aiCodingSessions": "AI kodēšanas sesijas", - "startCodingTaskFromPhone": "Sāc kodēšanas uzdevumu no tālruņa vai turpini sesiju no sava CLI.", - "tryIt": "Izmēģini", "newCodingTask": "Jauns kodēšanas uzdevums", "explore": "Izpētīt", "seeAll": "Skatīt visus", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Aizvērt {{filename}}", diff --git a/apps/mobile/src/i18n/locales/mg.json b/apps/mobile/src/i18n/locales/mg.json index 146db4af7b..dd5f9508a9 100644 --- a/apps/mobile/src/i18n/locales/mg.json +++ b/apps/mobile/src/i18n/locales/mg.json @@ -148,7 +148,9 @@ "appearanceSystem": "Rafitra", "appearanceLight": "Mazava", "appearanceDark": "Maizina", - "account": "Kaonty" + "account": "Kaonty", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Tsy misy ny pull request", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Ho very ny hafatrao.", "keepEditing": "Tohizo ny fanitsiana", "discard": "Ario", - "discardFailed": "Tsy afaka nandao ny draft. Andramo indray azafady." + "discardFailed": "Tsy afaka nandao ny draft. Andramo indray azafady.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Nohavaozina ny rakitra {{displayCount}}", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Tsy afaka nampidirina ny session", "couldNotLoadActiveSessions": "Tsy afaka nampidirina ny session mavitrika", "agentSessions": "Session agent", - "showingSavedSessions": "Maneho ny session voatahiry — mety tsy manaraka fotoana ny toerana velona", - "startNewAgentSession": "Atombohy session Kilo Agent vaovao", - "aiCodingSessions": "Session coding AI", - "startCodingTaskFromPhone": "Atombohy asa coding avy amin'ny findainao na tohizo session avy amin'ny CLI-nao.", - "tryIt": "Andramo", "newCodingTask": "Asa coding vaovao", "explore": "Mijerena", "seeAll": "Jereo daholo", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Hanidy {{filename}}", diff --git a/apps/mobile/src/i18n/locales/mi.json b/apps/mobile/src/i18n/locales/mi.json index b95694790f..a309025f98 100644 --- a/apps/mobile/src/i18n/locales/mi.json +++ b/apps/mobile/src/i18n/locales/mi.json @@ -148,7 +148,9 @@ "appearanceSystem": "Pūnaha", "appearanceLight": "Mārama", "appearanceDark": "Pōuri", - "account": "Pūkete" + "account": "Pūkete", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Kāore i te wātea te tono huna", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Ka ngaro tō pātai.", "keepEditing": "Me whakatika tonu", "discard": "Whakarere", - "discardFailed": "Kāore i taea te whakarere i te tauira. Tēnā whakamātau anō." + "discardFailed": "Kāore i taea te whakarere i te tauira. Tēnā whakamātau anō.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Kua whakahoutia {{displayCount}} kōnae", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Kāore i taea te uta ngā wātū", "couldNotLoadActiveSessions": "Kāore i taea te uta ngā wātū mahi", "agentSessions": "Ngā wātū kaihoko", - "showingSavedSessions": "E whakaatu ana i ngā wātū kua tiakina — kua tawhito pea te tūnga ora", - "startNewAgentSession": "Tīmata he wātū Kaihoko Kilo hōu", - "aiCodingSessions": "Ngā wātū whakawaehere AI", - "startCodingTaskFromPhone": "Tīmata he mahi whakawaehere mai i tō waea, haere tonu rānei i tētahi wātū mai i tō CLI.", - "tryIt": "Whakamātau", "newCodingTask": "Mahi whakawaehere hōu", "explore": "Torotoro", "seeAll": "Tirohia katoa", - "kiloAgents": "Ngā Kaihoko Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Katia {{filename}}", diff --git a/apps/mobile/src/i18n/locales/mk.json b/apps/mobile/src/i18n/locales/mk.json index 575c19ec55..2efb6c5730 100644 --- a/apps/mobile/src/i18n/locales/mk.json +++ b/apps/mobile/src/i18n/locales/mk.json @@ -148,7 +148,9 @@ "appearanceSystem": "Системски", "appearanceLight": "Светло", "appearanceDark": "Темно", - "account": "Сметка" + "account": "Сметка", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Барањето за повлекување е недостапно", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Твојот предлог ќе биде изгубен.", "keepEditing": "Продолжи со уредување", "discard": "Отфрли", - "discardFailed": "Не можеше да се отфрли нацртот. Обиди се повторно." + "discardFailed": "Не можеше да се отфрли нацртот. Обиди се повторно.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ажурирана {{displayCount}} датотека", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Не можев да ги вчитам сесиите", "couldNotLoadActiveSessions": "Не можев да ги вчитам активните сесии", "agentSessions": "Сесии на агенти", - "showingSavedSessions": "Прикажани се зачуваните сесии — живиот статус може да е застарен", - "startNewAgentSession": "Започни нова сесија на Kilo Agent", - "aiCodingSessions": "AI сесии за кодирање", - "startCodingTaskFromPhone": "Започнете задача за кодирање од вашиот телефон или продолжете сесија од вашиот CLI.", - "tryIt": "Пробај", "newCodingTask": "Нова задача за кодирање", "explore": "Истражувај", "seeAll": "Види ги сите", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Затвори {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ml.json b/apps/mobile/src/i18n/locales/ml.json index b61686c9e5..42bfa8ba61 100644 --- a/apps/mobile/src/i18n/locales/ml.json +++ b/apps/mobile/src/i18n/locales/ml.json @@ -148,7 +148,9 @@ "appearanceSystem": "സിസ്റ്റം", "appearanceLight": "ലൈറ്റ്", "appearanceDark": "ഡാർക്ക്", - "account": "അക്കൗണ്ട്" + "account": "അക്കൗണ്ട്", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "പുൾ റിക്വസ്റ്റ് ലഭ്യമല്ല", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "നിങ്ങളുടെ പ്രോംപ്റ്റ് നഷ്ടപ്പെടും.", "keepEditing": "എഡിറ്റിംഗ് തുടരുക", "discard": "ഉപേക്ഷിക്കുക", - "discardFailed": "ഡ്രാഫ്റ്റ് ഉപേക്ഷിക്കാൻ കഴിഞ്ഞില്ല. വീണ്ടും ശ്രമിക്കുക." + "discardFailed": "ഡ്രാഫ്റ്റ് ഉപേക്ഷിക്കാൻ കഴിഞ്ഞില്ല. വീണ്ടും ശ്രമിക്കുക.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ഫയൽ അപ്ഡേറ്റ് ചെയ്തു", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "സെഷനുകൾ ലോഡ് ചെയ്യാൻ കഴിഞ്ഞില്ല", "couldNotLoadActiveSessions": "സജീവ സെഷനുകൾ ലോഡ് ചെയ്യാൻ കഴിഞ്ഞില്ല", "agentSessions": "ഏജന്റ് സെഷനുകൾ", - "showingSavedSessions": "സംരക്ഷിച്ച സെഷനുകൾ കാണിക്കുന്നു — തത്സമയ നില കാലഹരണപ്പെട്ടതായിരിക്കാം", - "startNewAgentSession": "ഒരു പുതിയ Kilo ഏജന്റ് സെഷൻ ആരംഭിക്കുക", - "aiCodingSessions": "AI കോഡിംഗ് സെഷനുകൾ", - "startCodingTaskFromPhone": "നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഒരു കോഡിംഗ് ടാസ്ക് ആരംഭിക്കുക അല്ലെങ്കിൽ നിങ്ങളുടെ CLI-യിൽ നിന്ന് ഒരു സെഷൻ തുടരുക.", - "tryIt": "പരീക്ഷിച്ചുനോക്കൂ", "newCodingTask": "പുതിയ കോഡിംഗ് ടാസ്ക്", "explore": "പര്യവേക്ഷണം ചെയ്യുക", "seeAll": "എല്ലാം കാണുക", - "kiloAgents": "Kilo ഏജന്റുകൾ" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} അടയ്ക്കുക", diff --git a/apps/mobile/src/i18n/locales/mn.json b/apps/mobile/src/i18n/locales/mn.json index 416206c11e..3d1d664f8d 100644 --- a/apps/mobile/src/i18n/locales/mn.json +++ b/apps/mobile/src/i18n/locales/mn.json @@ -148,7 +148,9 @@ "appearanceSystem": "Систем", "appearanceLight": "Гэрэлтэй", "appearanceDark": "Харанхуй", - "account": "Бүртгэл" + "account": "Бүртгэл", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request боломжгүй", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Таны заавар алдагдах болно.", "keepEditing": "Засварлахыг үргэлжлүүлэх", "discard": "Хаях", - "discardFailed": "Нооргийг хаях боломжгүй. Дахин оролдоно уу." + "discardFailed": "Нооргийг хаях боломжгүй. Дахин оролдоно уу.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} файл шинэчлэгдсэн", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Сессийг ачаалж чадсангүй", "couldNotLoadActiveSessions": "Идэвхтэй сессийг ачаалж чадсангүй", "agentSessions": "Агентын сессий", - "showingSavedSessions": "Хадгалсан сессийг харуулж байна — шууд статус хуучирсан байж болно", - "startNewAgentSession": "Шинэ Kilo Agent сесси эхлүүлэх", - "aiCodingSessions": "AI кодчилолын сессий", - "startCodingTaskFromPhone": "Утаснаасаа кодчилолын даалгавар эхлүүлэх эсвэл CLI-ээсээ сессийг үргэлжлүүлэх.", - "tryIt": "Оролдоод үзэх", "newCodingTask": "Шинэ кодчилолын даалгавар", "explore": "Судлах", "seeAll": "Бүгдийг үзэх", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} хаах", diff --git a/apps/mobile/src/i18n/locales/mr.json b/apps/mobile/src/i18n/locales/mr.json index 94fff6def3..12971529c3 100644 --- a/apps/mobile/src/i18n/locales/mr.json +++ b/apps/mobile/src/i18n/locales/mr.json @@ -148,7 +148,9 @@ "appearanceSystem": "सिस्टम", "appearanceLight": "लाइट", "appearanceDark": "डार्क", - "account": "खाते" + "account": "खाते", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "पुल रिक्वेस्ट अनुपलब्ध", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "तुमचा प्रॉम्प्ट गमावला जाईल.", "keepEditing": "संपादन सुरू ठेवा", "discard": "रद्द करा", - "discardFailed": "मसुदा रद्द करता आला नाही. कृपया पुन्हा प्रयत्न करा." + "discardFailed": "मसुदा रद्द करता आला नाही. कृपया पुन्हा प्रयत्न करा.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} फाइल अद्ययावत केली", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "सत्रे लोड करता आली नाहीत", "couldNotLoadActiveSessions": "सक्रिय सत्रे लोड करता आली नाहीत", "agentSessions": "एजंट सत्रे", - "showingSavedSessions": "जतन केलेली सत्रे दाखवत आहे — लाइव्ह स्थिती कालबाह्य असू शकते", - "startNewAgentSession": "नवीन Kilo एजंट सत्र सुरू करा", - "aiCodingSessions": "AI कोडिंग सत्रे", - "startCodingTaskFromPhone": "तुमच्या फोनवरून कोडिंग कार्य सुरू करा किंवा तुमच्या CLI वरून सत्र सुरू ठेवा.", - "tryIt": "वापरून पहा", "newCodingTask": "नवीन कोडिंग कार्य", "explore": "एक्सप्लोर करा", "seeAll": "सर्व पहा", - "kiloAgents": "Kilo एजंट्स" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} बंद करा", diff --git a/apps/mobile/src/i18n/locales/ms.json b/apps/mobile/src/i18n/locales/ms.json index 24685b4c3a..da26a624da 100644 --- a/apps/mobile/src/i18n/locales/ms.json +++ b/apps/mobile/src/i18n/locales/ms.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Cerah", "appearanceDark": "Gelap", - "account": "Akaun" + "account": "Akaun", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Permintaan tarik tidak tersedia", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Prompt anda akan hilang.", "keepEditing": "Teruskan mengedit", "discard": "Buang", - "discardFailed": "Tidak dapat membuang draf. Sila cuba lagi." + "discardFailed": "Tidak dapat membuang draf. Sila cuba lagi.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fail dikemas kini", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Tidak dapat memuatkan sesi", "couldNotLoadActiveSessions": "Tidak dapat memuatkan sesi aktif", "agentSessions": "Sesi ejen", - "showingSavedSessions": "Menunjukkan sesi tersimpan — status langsung mungkin sudah lapuk", - "startNewAgentSession": "Mulakan sesi Kilo Agent baharu", - "aiCodingSessions": "Sesi pengekodan AI", - "startCodingTaskFromPhone": "Mulakan tugas pengekodan daripada telefon anda atau teruskan sesi daripada CLI anda.", - "tryIt": "Cubalah", "newCodingTask": "Tugas pengekodan baharu", "explore": "Teroka", "seeAll": "Lihat semua", - "kiloAgents": "Ejen Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Tutup {{filename}}", diff --git a/apps/mobile/src/i18n/locales/mt.json b/apps/mobile/src/i18n/locales/mt.json index 91adac54cd..7ab175605f 100644 --- a/apps/mobile/src/i18n/locales/mt.json +++ b/apps/mobile/src/i18n/locales/mt.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Dawl", "appearanceDark": "Skur", - "account": "Kont" + "account": "Kont", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request mhux disponibbli", @@ -2221,7 +2223,8 @@ "discardDraftMessage": "Il-prompt tiegħek se jintilef.", "keepEditing": "Kompli ipproċessa", "discard": "Warrab", - "discardFailed": "Ma setax jintrema l-abbozz. Jekk jogħġbok erġa' pprova." + "discardFailed": "Ma setax jintrema l-abbozz. Jekk jogħġbok erġa' pprova.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Aġġornat {{displayCount}} fajl", @@ -2722,15 +2725,10 @@ "couldNotLoadSessions": "Ma setgħux jitgħabbew is-sessjonijiet", "couldNotLoadActiveSessions": "Ma setgħux jitgħabbew is-sessjonijiet attivi", "agentSessions": "Sessjonijiet tal-aġent", - "showingSavedSessions": "Qed juri s-sessjonijiet salvati — l-istatus dirett jista' jkun skadut", - "startNewAgentSession": "Ibda sessjoni ġdida ta' Kilo Agent", - "aiCodingSessions": "Sessjonijiet ta' kodifikazzjoni bl-AI", - "startCodingTaskFromPhone": "Ibda kompitu ta' kodifikazzjoni mit-telefon tiegħek jew kompli sessjoni mill-CLI tiegħek.", - "tryIt": "Ipprova", "newCodingTask": "Kompitu ġdid ta' kodifikazzjoni", "explore": "Esplora", "seeAll": "Ara kollha", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Agħlaq {{filename}}", diff --git a/apps/mobile/src/i18n/locales/my.json b/apps/mobile/src/i18n/locales/my.json index 0a45c4f612..d794b3b67e 100644 --- a/apps/mobile/src/i18n/locales/my.json +++ b/apps/mobile/src/i18n/locales/my.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Light", "appearanceDark": "Dark", - "account": "အကောင့်" + "account": "အကောင့်", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request မရနိုင်ပါ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "သင့်မေးမြန်းချက် ပျောက်သွားမည်။", "keepEditing": "ဆက်လက်တည်းဖြတ်ပါ", "discard": "ပယ်ဖျက်ပါ", - "discardFailed": "မူကြမ်းကို ပယ်ဖျက်၍မရခဲ့ပါ။ ထပ်ကြိုးစားပါ။" + "discardFailed": "မူကြမ်းကို ပယ်ဖျက်၍မရခဲ့ပါ။ ထပ်ကြိုးစားပါ။", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "ဖိုင် {{displayCount}} ခု မွမ်းမံပြီး", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "session များကို မတင်နိုင်ပါ", "couldNotLoadActiveSessions": "လက်ရှိ session များကို မတင်နိုင်ပါ", "agentSessions": "Agent session များ", - "showingSavedSessions": "သိမ်းထားသော session များ ပြနေသည် — တိုက်ရိုက် အခြေအနေ နောက်ကျနိုင်သည်", - "startNewAgentSession": "Kilo Agent session အသစ် စတင်ရန်", - "aiCodingSessions": "AI coding session များ", - "startCodingTaskFromPhone": "သင့်ဖုန်းမှ coding အလုပ်တစ်ခု စတင်ပါ သို့မဟုတ် သင့် CLI မှ session တစ်ခု ဆက်ပါ။", - "tryIt": "စမ်းကြည့်ပါ", "newCodingTask": "coding အလုပ်အသစ်", "explore": "ရှာဖွေပါ", "seeAll": "အားလုံး ကြည့်ရန်", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} ပိတ်ရန်", diff --git a/apps/mobile/src/i18n/locales/nb.json b/apps/mobile/src/i18n/locales/nb.json index d5943f515b..e428693202 100644 --- a/apps/mobile/src/i18n/locales/nb.json +++ b/apps/mobile/src/i18n/locales/nb.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Lys", "appearanceDark": "Mørk", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request utilgjengelig", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Forespørselen din vil gå tapt.", "keepEditing": "Fortsett å redigere", "discard": "Forkast", - "discardFailed": "Kunne ikke forkaste utkastet. Prøv igjen." + "discardFailed": "Kunne ikke forkaste utkastet. Prøv igjen.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Oppdaterte {{displayCount}} fil", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Kunne ikke laste økter", "couldNotLoadActiveSessions": "Kunne ikke laste aktive økter", "agentSessions": "Agentøkter", - "showingSavedSessions": "Viser lagrede økter — sanntidsstatus kan være utdatert", - "startNewAgentSession": "Start en ny Kilo Agent-økt", - "aiCodingSessions": "AI-kodeøkter", - "startCodingTaskFromPhone": "Start en kodeoppgave fra telefonen din eller fortsett en økt fra CLI-en din.", - "tryIt": "Prøv det", "newCodingTask": "Ny kodeoppgave", "explore": "Utforsk", "seeAll": "Se alle", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Lukk {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ne.json b/apps/mobile/src/i18n/locales/ne.json index 0903b6966c..7f32a1c3af 100644 --- a/apps/mobile/src/i18n/locales/ne.json +++ b/apps/mobile/src/i18n/locales/ne.json @@ -148,7 +148,9 @@ "appearanceSystem": "प्रणाली", "appearanceLight": "हल्का", "appearanceDark": "गाढा", - "account": "खाता" + "account": "खाता", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "पुल अनुरोध उपलब्ध छैन", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "तपाईंको प्रम्प्ट हराउनेछ।", "keepEditing": "सम्पादन जारी राख्नुहोस्", "discard": "खारेज गर्नुहोस्", - "discardFailed": "ड्राफ्ट खारेज गर्न सकिएन। फेरि प्रयास गर्नुहोस्।" + "discardFailed": "ड्राफ्ट खारेज गर्न सकिएन। फेरि प्रयास गर्नुहोस्।", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} फाइल अद्यावधिक गरियो", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "सत्रहरू लोड गर्न सकिएन", "couldNotLoadActiveSessions": "सक्रिय सत्रहरू लोड गर्न सकिएन", "agentSessions": "एजेन्ट सत्रहरू", - "showingSavedSessions": "सुरक्षित सत्रहरू देखाइँदै — लाइभ स्थिति पुरानो हुन सक्छ", - "startNewAgentSession": "नयाँ Kilo Agent सत्र सुरु गर्नुहोस्", - "aiCodingSessions": "AI कोडिङ सत्रहरू", - "startCodingTaskFromPhone": "आफ्नो फोनबाट कोडिङ कार्य सुरु गर्नुहोस् वा आफ्नो CLI बाट सत्र जारी गर्नुहोस्।", - "tryIt": "प्रयास गर्नुहोस्", "newCodingTask": "नयाँ कोडिङ कार्य", "explore": "अन्वेषण", "seeAll": "सबै हेर्नुहोस्", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} बन्द गर्नुहोस्", diff --git a/apps/mobile/src/i18n/locales/nl.json b/apps/mobile/src/i18n/locales/nl.json index 7a0dcc79ab..4314075ee9 100644 --- a/apps/mobile/src/i18n/locales/nl.json +++ b/apps/mobile/src/i18n/locales/nl.json @@ -148,7 +148,9 @@ "appearanceSystem": "Systeem", "appearanceLight": "Licht", "appearanceDark": "Donker", - "account": "Account" + "account": "Account", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "channel": { @@ -1722,7 +1724,8 @@ "discardDraftMessage": "Je prompt gaat verloren.", "discardDraftTitle": "Concept weggooien?", "discardFailed": "Kon het concept niet weggooien. Probeer het opnieuw.", - "keepEditing": "Blijven bewerken" + "keepEditing": "Blijven bewerken", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} bestand bijgewerkt", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "Sessies konden niet worden geladen", "couldNotLoadActiveSessions": "Actieve sessies konden niet worden geladen", "agentSessions": "Agentsessies", - "showingSavedSessions": "Opgeslagen sessies worden getoond — de live status kan verouderd zijn", - "startNewAgentSession": "Een nieuwe Kilo Agent-sessie starten", - "aiCodingSessions": "AI-codeersessies", - "startCodingTaskFromPhone": "Start een codeertaak vanaf je telefoon of ga verder met een sessie vanaf je CLI.", - "tryIt": "Probeer het", "newCodingTask": "Nieuwe codeertaak", "explore": "Verkennen", "seeAll": "Alles bekijken", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} sluiten", diff --git a/apps/mobile/src/i18n/locales/om.json b/apps/mobile/src/i18n/locales/om.json index 0395e03a59..f99cb992a7 100644 --- a/apps/mobile/src/i18n/locales/om.json +++ b/apps/mobile/src/i18n/locales/om.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sirna", "appearanceLight": "Ifa", "appearanceDark": "Dukkana", - "account": "Herrega" + "account": "Herrega", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request hin jiraatu", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Prompt kee dhabama.", "keepEditing": "Gulaaluu itti fufi", "discard": "Haqi", - "discardFailed": "Daaftarii haquu hin dandeenye. Mee deebi'ee yaali." + "discardFailed": "Daaftarii haquu hin dandeenye. Mee deebi'ee yaali.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Faayilii {{displayCount}} haaromfame", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Shumarra fudhachuu hin danda'ine", "couldNotLoadActiveSessions": "Shumarra hojii fudhachuu hin danda'ine", "agentSessions": "Shumarra agent", - "showingSavedSessions": "Shumarra kuusame agarsiisaa jira — haalli lubbuu yeroo ittiin gadi aanaa ta'uu danda'a", - "startNewAgentSession": "Shumarra Kilo Agent haaraa jalqabi", - "aiCodingSessions": "Shumarra code AI", - "startCodingTaskFromPhone": "Hojii code bilbilaa kee irraa jalqabi ykn shumarra CLI kee itti fufi.", - "tryIt": "Yaali", "newCodingTask": "Hojii code haaraa", "explore": "Qoradhu", "seeAll": "Hunda ilaali", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} cufi", diff --git a/apps/mobile/src/i18n/locales/or.json b/apps/mobile/src/i18n/locales/or.json index d8557abb26..cb637a0576 100644 --- a/apps/mobile/src/i18n/locales/or.json +++ b/apps/mobile/src/i18n/locales/or.json @@ -148,7 +148,9 @@ "appearanceSystem": "ସିଷ୍ଟମ୍", "appearanceLight": "ହାଲୁକା", "appearanceDark": "ଗାଢ଼", - "account": "ଆକାଉଣ୍ଟ" + "account": "ଆକାଉଣ୍ଟ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "ପୁଲ୍ ରିକ୍ୱେଷ୍ଟ ଉପଲବ୍ଧ ନାହିଁ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ଆପଣଙ୍କ ପ୍ରମ୍ପ୍ଟ ହଜିଯିବ।", "keepEditing": "ସମ୍ପାଦନ ଜାରି ରଖନ୍ତୁ", "discard": "ପରିତ୍ୟାଗ କରନ୍ତୁ", - "discardFailed": "ଡ୍ରାଫ୍ଟ ପରିତ୍ୟାଗ ହୋଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁନଃଚେଷ୍ଟା କରନ୍ତୁ।" + "discardFailed": "ଡ୍ରାଫ୍ଟ ପରିତ୍ୟାଗ ହୋଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁନଃଚେଷ୍ଟା କରନ୍ତୁ।", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}}ଟି ଫାଇଲ୍ ଅଦ୍ୟତନ ହୋଇଛି", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "ସେସନ୍ ଲୋଡ୍ ହୋଇପାରିଲା ନାହିଁ", "couldNotLoadActiveSessions": "ସକ୍ରିୟ ସେସନ୍ ଲୋଡ୍ ହୋଇପାରିଲା ନାହିଁ", "agentSessions": "ଏଜେଣ୍ଟ ସେସନ୍", - "showingSavedSessions": "ସେଭ୍ ହୋଇଥିବା ସେସନ୍ ଦେଖାଉଛି — ଲାଇଭ୍ ସ୍ଥିତି ଅପଡେଟ୍ ନହୋଇପାରେ", - "startNewAgentSession": "ନୂତନ Kilo Agent ସେସନ୍ ଆରମ୍ଭ କରନ୍ତୁ", - "aiCodingSessions": "AI କୋଡିଂ ସେସନ୍", - "startCodingTaskFromPhone": "ଆପଣଙ୍କ ଫୋନରୁ କୋଡିଂ କାର୍ଯ୍ୟ ଆରମ୍ଭ କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ CLI ରୁ ସେସନ୍ ଜାରି କରନ୍ତୁ।", - "tryIt": "ଚେଷ୍ଟା କରନ୍ତୁ", "newCodingTask": "ନୂତନ କୋଡିଂ କାର୍ଯ୍ୟ", "explore": "ଅନୁସନ୍ଧାନ", "seeAll": "ସବୁ ଦେଖନ୍ତୁ", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} ବନ୍ଦ କରନ୍ତୁ", diff --git a/apps/mobile/src/i18n/locales/pa.json b/apps/mobile/src/i18n/locales/pa.json index f3a5f5c699..4304204b24 100644 --- a/apps/mobile/src/i18n/locales/pa.json +++ b/apps/mobile/src/i18n/locales/pa.json @@ -148,7 +148,9 @@ "appearanceSystem": "ਸਿਸਟਮ", "appearanceLight": "ਹਲਕਾ", "appearanceDark": "ਗੂੜ੍ਹਾ", - "account": "ਖਾਤਾ" + "account": "ਖਾਤਾ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "ਪੁੱਲ ਰਿਕਵੈਸਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ਤੁਹਾਡਾ ਪ੍ਰੋਂਪਟ ਗੁਆਚ ਜਾਵੇਗਾ।", "keepEditing": "ਸੰਪਾਦਨ ਜਾਰੀ ਰੱਖੋ", "discard": "ਰੱਦ ਕਰੋ", - "discardFailed": "ਡਰਾਫਟ ਰੱਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" + "discardFailed": "ਡਰਾਫਟ ਰੱਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ਫਾਈਲ ਅੱਪਡੇਟ ਕੀਤੀ ਗਈ", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "ਸੈਸ਼ਨ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕੇ", "couldNotLoadActiveSessions": "ਸਰਗਰਮ ਸੈਸ਼ਨ ਲੋਡ ਨਹੀਂ ਹੋ ਸਕੇ", "agentSessions": "ਏਜੰਟ ਸੈਸ਼ਨ", - "showingSavedSessions": "ਸੇਵ ਕੀਤੇ ਸੈਸ਼ਨ ਦਿਖਾਏ ਜਾ ਰਹੇ ਹਨ — ਲਾਈਵ ਸਥਿਤੀ ਪੁਰਾਣੀ ਹੋ ਸਕਦੀ ਹੈ", - "startNewAgentSession": "ਨਵਾਂ Kilo ਏਜੰਟ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ", - "aiCodingSessions": "AI ਕੋਡਿੰਗ ਸੈਸ਼ਨ", - "startCodingTaskFromPhone": "ਆਪਣੇ ਫ਼ੋਨ ਤੋਂ ਕੋਡਿੰਗ ਕੰਮ ਸ਼ੁਰੂ ਕਰੋ ਜਾਂ ਆਪਣੇ CLI ਤੋਂ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖੋ।", - "tryIt": "ਕੋਸ਼ਿਸ਼ ਕਰੋ", "newCodingTask": "ਨਵਾਂ ਕੋਡਿੰਗ ਕੰਮ", "explore": "ਪੜਚੋਲ ਕਰੋ", "seeAll": "ਸਭ ਵੇਖੋ", - "kiloAgents": "Kilo ਏਜੰਟ" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} ਬੰਦ ਕਰੋ", diff --git a/apps/mobile/src/i18n/locales/pl.json b/apps/mobile/src/i18n/locales/pl.json index 5da88aa35e..f4d8bca627 100644 --- a/apps/mobile/src/i18n/locales/pl.json +++ b/apps/mobile/src/i18n/locales/pl.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Jasny", "appearanceDark": "Ciemny", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Powiadomienia", @@ -1738,7 +1740,8 @@ "discardDraftMessage": "Twoje polecenie zostanie utracone.", "discardDraftTitle": "Odrzucić szkic?", "discardFailed": "Nie udało się odrzucić szkicu. Spróbuj ponownie.", - "keepEditing": "Kontynuuj edycję" + "keepEditing": "Kontynuuj edycję", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Zaktualizowano {{displayCount}} plik", @@ -2234,15 +2237,10 @@ "couldNotLoadSessions": "Nie można załadować sesji", "couldNotLoadActiveSessions": "Nie można załadować aktywnych sesji", "agentSessions": "Sesje agenta", - "showingSavedSessions": "Wyświetlanie zapisanych sesji — status na żywo może być nieaktualny", - "startNewAgentSession": "Rozpocznij nową sesję agenta Kilo", - "aiCodingSessions": "Sesje kodowania AI", - "startCodingTaskFromPhone": "Rozpocznij zadanie kodowania z telefonu lub kontynuuj sesję z CLI.", - "tryIt": "Wypróbuj", "newCodingTask": "Nowe zadanie kodowania", "explore": "Odkrywaj", "seeAll": "Zobacz wszystkie", - "kiloAgents": "Agenci Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zamknij {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ps.json b/apps/mobile/src/i18n/locales/ps.json index 739206755a..e05dae1dca 100644 --- a/apps/mobile/src/i18n/locales/ps.json +++ b/apps/mobile/src/i18n/locales/ps.json @@ -148,7 +148,9 @@ "appearanceSystem": "سیسټم", "appearanceLight": "روښانه", "appearanceDark": "تیاره", - "account": "حساب" + "account": "حساب", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "پل ریکویسټ شتون نلري", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ستاسو پرامپټ به له لاسه ورکړل شي.", "keepEditing": "سمون ته دوام ورکړئ", "discard": "لغوه کړئ", - "discardFailed": "مسوده لغوه نه شوه. مهرباني وکړئ بیا هڅه وکړئ." + "discardFailed": "مسوده لغوه نه شوه. مهرباني وکړئ بیا هڅه وکړئ.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} فایل تازه شو", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "غونډې نه شول پورته کېدلای", "couldNotLoadActiveSessions": "فعالې غونډې نه شول پورته کېدلای", "agentSessions": "د اجنټ غونډې", - "showingSavedSessions": "خوندي شوې غونډې ښودل کېږي — ژوندی حالت ممکن زوړ وي", - "startNewAgentSession": "د Kilo اجنټ نوې غونډه پیل کړئ", - "aiCodingSessions": "د AI کوډ کولو غونډې", - "startCodingTaskFromPhone": "له خپل تلیفون څخه د کوډ کولو دنده پیل کړئ یا له خپل CLI څخه یوه غونډه دوام ورکړئ.", - "tryIt": "هڅه یې وکړئ", "newCodingTask": "د کوډ کولو نوې دنده", "explore": "وپلټئ", "seeAll": "ټول وګورئ", - "kiloAgents": "د Kilo اجنټان" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} وتړئ", diff --git a/apps/mobile/src/i18n/locales/pt-BR.json b/apps/mobile/src/i18n/locales/pt-BR.json index 1c9d7ba94d..7a7a0d76e5 100644 --- a/apps/mobile/src/i18n/locales/pt-BR.json +++ b/apps/mobile/src/i18n/locales/pt-BR.json @@ -1716,7 +1716,8 @@ "discardDraftMessage": "Seu prompt será perdido.", "discardDraftTitle": "Descartar rascunho?", "discardFailed": "Não foi possível descartar o rascunho. Tente novamente.", - "keepEditing": "Continuar editando" + "keepEditing": "Continuar editando", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} arquivo atualizado", @@ -2182,7 +2183,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Claro", "appearanceDark": "Escuro", - "account": "Conta" + "account": "Conta", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "addCredits": { "cta": "Adicionar créditos", @@ -2221,15 +2224,10 @@ "couldNotLoadSessions": "Não foi possível carregar as sessões", "couldNotLoadActiveSessions": "Não foi possível carregar as sessões ativas", "agentSessions": "Sessões de agentes", - "showingSavedSessions": "Mostrando sessões salvas — o status ao vivo pode estar desatualizado", - "startNewAgentSession": "Iniciar uma nova sessão do Kilo Agent", - "aiCodingSessions": "Sessões de codificação com IA", - "startCodingTaskFromPhone": "Inicie uma tarefa de codificação pelo celular ou continue uma sessão pela CLI.", - "tryIt": "Experimente", "newCodingTask": "Nova tarefa de codificação", "explore": "Explorar", "seeAll": "Ver tudo", - "kiloAgents": "Agentes Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Fechar {{filename}}", diff --git a/apps/mobile/src/i18n/locales/pt.json b/apps/mobile/src/i18n/locales/pt.json index 60a4d727b9..f73cdf79eb 100644 --- a/apps/mobile/src/i18n/locales/pt.json +++ b/apps/mobile/src/i18n/locales/pt.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistema", "appearanceLight": "Claro", "appearanceDark": "Escuro", - "account": "Conta" + "account": "Conta", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request indisponível", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "O seu prompt será perdido.", "keepEditing": "Continuar a editar", "discard": "Descartar", - "discardFailed": "Não foi possível descartar o rascunho. Tente novamente." + "discardFailed": "Não foi possível descartar o rascunho. Tente novamente.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ficheiro atualizado", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Não foi possível carregar as sessões", "couldNotLoadActiveSessions": "Não foi possível carregar as sessões ativas", "agentSessions": "Sessões de agente", - "showingSavedSessions": "A mostrar sessões guardadas — o estado ao vivo pode estar desatualizado", - "startNewAgentSession": "Iniciar uma nova sessão do Kilo Agent", - "aiCodingSessions": "Sessões de programação com IA", - "startCodingTaskFromPhone": "Inicie uma tarefa de programação a partir do seu telemóvel ou continue uma sessão a partir da sua CLI.", - "tryIt": "Experimentar", "newCodingTask": "Nova tarefa de programação", "explore": "Explorar", "seeAll": "Ver tudo", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Fechar {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ro.json b/apps/mobile/src/i18n/locales/ro.json index 3557f51480..3b62249aaf 100644 --- a/apps/mobile/src/i18n/locales/ro.json +++ b/apps/mobile/src/i18n/locales/ro.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Luminos", "appearanceDark": "Întunecat", - "account": "Cont" + "account": "Cont", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request indisponibil", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "Mesajul tău va fi pierdut.", "keepEditing": "Continuă editarea", "discard": "Renunță", - "discardFailed": "Nu s-a putut renunța la ciornă. Încearcă din nou." + "discardFailed": "Nu s-a putut renunța la ciornă. Încearcă din nou.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Actualizat {{displayCount}} fișier", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Sesiunile nu au putut fi încărcate", "couldNotLoadActiveSessions": "Sesiunile active nu au putut fi încărcate", "agentSessions": "Sesiuni de agent", - "showingSavedSessions": "Se afișează sesiunile salvate — starea live poate fi neactualizată", - "startNewAgentSession": "Începe o sesiune nouă de Kilo Agent", - "aiCodingSessions": "Sesiuni de codare AI", - "startCodingTaskFromPhone": "Începe o sarcină de codare de pe telefon sau continuă o sesiune de pe CLI.", - "tryIt": "Încearcă", "newCodingTask": "Sarcină de codare nouă", "explore": "Explorează", "seeAll": "Vezi tot", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Închide {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ru.json b/apps/mobile/src/i18n/locales/ru.json index 66bc2eeb13..3b198e2a52 100644 --- a/apps/mobile/src/i18n/locales/ru.json +++ b/apps/mobile/src/i18n/locales/ru.json @@ -148,7 +148,9 @@ "appearanceSystem": "Системная", "appearanceLight": "Светлая", "appearanceDark": "Темная", - "account": "Аккаунт" + "account": "Аккаунт", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Уведомления", @@ -1738,7 +1740,8 @@ "discardDraftMessage": "Ваш запрос будет потерян.", "discardDraftTitle": "Отменить черновик?", "discardFailed": "Не удалось отменить черновик. Попробуйте снова.", - "keepEditing": "Продолжить редактирование" + "keepEditing": "Продолжить редактирование", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Обновлен {{displayCount}} файл", @@ -2234,15 +2237,10 @@ "couldNotLoadSessions": "Не удалось загрузить сеансы", "couldNotLoadActiveSessions": "Не удалось загрузить активные сеансы", "agentSessions": "Сеансы агента", - "showingSavedSessions": "Показаны сохранённые сеансы — актуальный статус может быть устаревшим", - "startNewAgentSession": "Начать новый сеанс агента Kilo", - "aiCodingSessions": "Сеансы ИИ-кодирования", - "startCodingTaskFromPhone": "Начните задачу кодирования с телефона или продолжите сеанс из CLI.", - "tryIt": "Попробовать", "newCodingTask": "Новая задача кодирования", "explore": "Исследовать", "seeAll": "Смотреть все", - "kiloAgents": "Агенты Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Закрыть {{filename}}", diff --git a/apps/mobile/src/i18n/locales/si.json b/apps/mobile/src/i18n/locales/si.json index 20e21e0903..d22f98f226 100644 --- a/apps/mobile/src/i18n/locales/si.json +++ b/apps/mobile/src/i18n/locales/si.json @@ -148,7 +148,9 @@ "appearanceSystem": "පද්ධතිය", "appearanceLight": "ආලෝකය", "appearanceDark": "අඳුරු", - "account": "ගිණුම" + "account": "ගිණුම", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ලබා ගත නොහැක", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ඔබේ ඉල්ලීම අහිමි වනු ඇත.", "keepEditing": "දිගටම සංස්කරණය කරන්න", "discard": "ඉවතලන්න", - "discardFailed": "කටු සටහන ඉවතලිය නොහැක. කරුණාකර නැවත උත්සාහ කරන්න." + "discardFailed": "කටු සටහන ඉවතලිය නොහැක. කරුණාකර නැවත උත්සාහ කරන්න.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ගොනුවක් යාවත්කාලීන කර ඇත", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "සැසි පූරණය කළ නොහැකි විය", "couldNotLoadActiveSessions": "ක්‍රියාකාරී සැසි පූරණය කළ නොහැකි විය", "agentSessions": "ඒජන්ත සැසි", - "showingSavedSessions": "සුරැකි සැසි පෙන්වමින් — සජීවී තත්ත්වය කල් ඉකුත් විය හැක", - "startNewAgentSession": "නව Kilo Agent සැසියක් ආරම්භ කරන්න", - "aiCodingSessions": "AI කේතීකරණ සැසි", - "startCodingTaskFromPhone": "ඔබගේ දුරකථනයෙන් කේතීකරණ කාර්යයක් ආරම්භ කරන්න හෝ ඔබගේ CLI වෙතින් සැසියක් දිගටම කරගෙන යන්න.", - "tryIt": "උත්සාහ කරන්න", "newCodingTask": "නව කේතීකරණ කාර්යය", "explore": "ගවේෂණය", "seeAll": "සියල්ල බලන්න", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} වසන්න", diff --git a/apps/mobile/src/i18n/locales/sk.json b/apps/mobile/src/i18n/locales/sk.json index d24ddeeb9a..1c7d3bb019 100644 --- a/apps/mobile/src/i18n/locales/sk.json +++ b/apps/mobile/src/i18n/locales/sk.json @@ -148,7 +148,9 @@ "appearanceSystem": "Systémový", "appearanceLight": "Svetlý", "appearanceDark": "Tmavý", - "account": "Účet" + "account": "Účet", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nie je dostupný", @@ -2206,7 +2208,8 @@ "discardDraftMessage": "Váš prompt sa stratí.", "keepEditing": "Pokračovať v úpravách", "discard": "Zahodiť", - "discardFailed": "Koncept sa nepodarilo zahodiť. Skúste to znova." + "discardFailed": "Koncept sa nepodarilo zahodiť. Skúste to znova.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Aktualizovaný {{displayCount}} súbor", @@ -2702,15 +2705,10 @@ "couldNotLoadSessions": "Relácie sa nepodarilo načítať", "couldNotLoadActiveSessions": "Aktívne relácie sa nepodarilo načítať", "agentSessions": "Relácie agenta", - "showingSavedSessions": "Zobrazujú sa uložené relácie — živý stav môže byť zastaraný", - "startNewAgentSession": "Spustiť novú reláciu agenta Kilo", - "aiCodingSessions": "AI programovacie relácie", - "startCodingTaskFromPhone": "Spustite programovaciu úlohu z telefónu alebo pokračujte v relácii z CLI.", - "tryIt": "Vyskúšať", "newCodingTask": "Nová programovacia úloha", "explore": "Preskúmať", "seeAll": "Zobraziť všetko", - "kiloAgents": "Agenti Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zavrieť {{filename}}", diff --git a/apps/mobile/src/i18n/locales/sl.json b/apps/mobile/src/i18n/locales/sl.json index 2d75260db9..2627387638 100644 --- a/apps/mobile/src/i18n/locales/sl.json +++ b/apps/mobile/src/i18n/locales/sl.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Svetlo", "appearanceDark": "Temno", - "account": "Račun" + "account": "Račun", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ni na voljo", @@ -2206,7 +2208,8 @@ "discardDraftMessage": "Tvoja zahteva bo izgubljena.", "keepEditing": "Nadaljuj z urejanjem", "discard": "Zavrzi", - "discardFailed": "Osnutka ni bilo mogoče zavreči. Poskusi znova." + "discardFailed": "Osnutka ni bilo mogoče zavreči. Poskusi znova.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Posodobljena {{displayCount}} datoteka", @@ -2702,15 +2705,10 @@ "couldNotLoadSessions": "Sej ni bilo mogoče naložiti", "couldNotLoadActiveSessions": "Aktivnih sej ni bilo mogoče naložiti", "agentSessions": "Seje agenta", - "showingSavedSessions": "Prikaz shranjenih sej — živi status je morda zastarel", - "startNewAgentSession": "Začni novo sejo Kilo Agenta", - "aiCodingSessions": "Seje kodiranja z umetno inteligenco", - "startCodingTaskFromPhone": "Začni nalogo kodiranja s telefona ali nadaljuj sejo s svojega CLI.", - "tryIt": "Preizkusi", "newCodingTask": "Nova naloga kodiranja", "explore": "Razišči", "seeAll": "Prikaži vse", - "kiloAgents": "Kilo Agenti" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zapri {{filename}}", diff --git a/apps/mobile/src/i18n/locales/so.json b/apps/mobile/src/i18n/locales/so.json index d164157755..d909e0c1c3 100644 --- a/apps/mobile/src/i18n/locales/so.json +++ b/apps/mobile/src/i18n/locales/so.json @@ -148,7 +148,9 @@ "appearanceSystem": "Nidaamka", "appearanceLight": "Iftiin", "appearanceDark": "Madow", - "account": "Xisaabta" + "account": "Xisaabta", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request ma heli karo", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Wax-soo-galkaaga wuu lumi doonaa.", "keepEditing": "Si wad wax ku tafatir", "discard": "Isku tuur", - "discardFailed": "Qabyada lama tuuri karin. Fadlan mar kale isku day." + "discardFailed": "Qabyada lama tuuri karin. Fadlan mar kale isku day.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} fayl ayaa la cusboonaysiiyay", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Kalfadhiyada lama soo dejin karin", "couldNotLoadActiveSessions": "Kalfadhiyada firfircoon lama soo dejin karin", "agentSessions": "Kalfadhiyada wakiilka", - "showingSavedSessions": "Waxaa la tusi doonaa kalfadhiyo la kaydiyay — xaaladda tooska ah waxaa laga yaabaa inay ka da'do", - "startNewAgentSession": "Bilow kalfadhi cusub oo Kilo Agent", - "aiCodingSessions": "Kalfadhiyada codeynta AI", - "startCodingTaskFromPhone": "Ka bilow hawshooda codeynta taleefankaaga ama sii wad kalfadhi CLI-gaaga.", - "tryIt": "Isku day", "newCodingTask": "Hawshooda cusub ee codeynta", "explore": "Sahan", "seeAll": "Fiiri dhammaan", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Xidh {{filename}}", diff --git a/apps/mobile/src/i18n/locales/sq.json b/apps/mobile/src/i18n/locales/sq.json index a7c6d60719..779ed0066d 100644 --- a/apps/mobile/src/i18n/locales/sq.json +++ b/apps/mobile/src/i18n/locales/sq.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistemi", "appearanceLight": "E çelët", "appearanceDark": "E errët", - "account": "Llogaria" + "account": "Llogaria", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Kërkesa e tërheqjes e padisponueshme", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Mesazhi juaj do të humbasë.", "keepEditing": "Vazhdo redaktimin", "discard": "Hiq", - "discardFailed": "Nuk u hoq drafti. Ju lutemi provoni përsëri." + "discardFailed": "Nuk u hoq drafti. Ju lutemi provoni përsëri.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "U përditësua {{displayCount}} skedar", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Seancat nuk u ngarkuan", "couldNotLoadActiveSessions": "Seancat aktive nuk u ngarkuan", "agentSessions": "Seancat e agjentit", - "showingSavedSessions": "Duke shfaqur seancat e ruajtura — statusi i drejtpërdrejtë mund të jetë i vjetëruar", - "startNewAgentSession": "Filloni një seancë të re Kilo Agent", - "aiCodingSessions": "Seancat e kodimit me AI", - "startCodingTaskFromPhone": "Filloni një detyrë kodimi nga telefoni juaj ose vazhdoni një seancë nga CLI juaj.", - "tryIt": "Provojeni", "newCodingTask": "Detyrë e re kodimi", "explore": "Eksploro", "seeAll": "Shiko të gjitha", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Mbyll {{filename}}", diff --git a/apps/mobile/src/i18n/locales/sr.json b/apps/mobile/src/i18n/locales/sr.json index 417d06e9c5..90df4ea8db 100644 --- a/apps/mobile/src/i18n/locales/sr.json +++ b/apps/mobile/src/i18n/locales/sr.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Svetlo", "appearanceDark": "Tamno", - "account": "Nalog" + "account": "Nalog", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request nije dostupan", @@ -2191,7 +2193,8 @@ "discardDraftMessage": "Tvoj upit će biti izgubljen.", "keepEditing": "Nastavi sa uređivanjem", "discard": "Odbaci", - "discardFailed": "Nacrt se ne može odbaciti. Pokušaj ponovo." + "discardFailed": "Nacrt se ne može odbaciti. Pokušaj ponovo.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ažuriran {{displayCount}} fajl", @@ -2682,15 +2685,10 @@ "couldNotLoadSessions": "Učitavanje sesija nije uspelo", "couldNotLoadActiveSessions": "Učitavanje aktivnih sesija nije uspelo", "agentSessions": "Agentske sesije", - "showingSavedSessions": "Prikazuju se sačuvane sesije — status uživo može biti zastareo", - "startNewAgentSession": "Pokreni novu Kilo Agent sesiju", - "aiCodingSessions": "AI sesije kodiranja", - "startCodingTaskFromPhone": "Pokrenite zadatak kodiranja sa telefona ili nastavite sesiju sa vašeg CLI-ja.", - "tryIt": "Probaj", "newCodingTask": "Novi zadatak kodiranja", "explore": "Istraži", "seeAll": "Vidi sve", - "kiloAgents": "Kilo Agenti" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Zatvori {{filename}}", diff --git a/apps/mobile/src/i18n/locales/sv.json b/apps/mobile/src/i18n/locales/sv.json index 78b40dc984..620cef8021 100644 --- a/apps/mobile/src/i18n/locales/sv.json +++ b/apps/mobile/src/i18n/locales/sv.json @@ -148,7 +148,9 @@ "appearanceSystem": "System", "appearanceLight": "Ljust", "appearanceDark": "Mörkt", - "account": "Konto" + "account": "Konto", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request inte tillgänglig", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Din prompt går förlorad.", "keepEditing": "Fortsätt redigera", "discard": "Ignorera", - "discardFailed": "Kunde inte ignorera utkastet. Försök igen." + "discardFailed": "Kunde inte ignorera utkastet. Försök igen.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Uppdaterade {{displayCount}} fil", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Det gick inte att läsa in sessioner", "couldNotLoadActiveSessions": "Det gick inte att läsa in aktiva sessioner", "agentSessions": "Agentsessioner", - "showingSavedSessions": "Visar sparade sessioner — live-status kan vara inaktuell", - "startNewAgentSession": "Starta en ny Kilo Agent-session", - "aiCodingSessions": "AI-kodningssessioner", - "startCodingTaskFromPhone": "Starta en kodningsuppgift från din telefon eller fortsätt en session från din CLI.", - "tryIt": "Prova", "newCodingTask": "Ny kodningsuppgift", "explore": "Utforska", "seeAll": "Visa alla", - "kiloAgents": "Kilo-agenter" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Stäng {{filename}}", diff --git a/apps/mobile/src/i18n/locales/sw.json b/apps/mobile/src/i18n/locales/sw.json index 2a1845532c..e5bed7b6f7 100644 --- a/apps/mobile/src/i18n/locales/sw.json +++ b/apps/mobile/src/i18n/locales/sw.json @@ -148,7 +148,9 @@ "appearanceSystem": "Mfumo", "appearanceLight": "Nyepesi", "appearanceDark": "Nyeusi", - "account": "Akaunti" + "account": "Akaunti", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request haipatikani", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Maagizo yako yatapotea.", "keepEditing": "Endelea kuhariri", "discard": "Tupa", - "discardFailed": "Haikuweza kutupa rasimu. Jaribu tena." + "discardFailed": "Haikuweza kutupa rasimu. Jaribu tena.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Imesasisha faili {{displayCount}}", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Haikuweza kupakia vikao", "couldNotLoadActiveSessions": "Haikuweza kupakia vikao vya sasa", "agentSessions": "Vikao vya wakala", - "showingSavedSessions": "Inaonyesha vikao vilivyohifadhiwa — hali ya moja kwa moja inaweza kuwa imepitwa na wakati", - "startNewAgentSession": "Anza kikao kipya cha Kilo Agent", - "aiCodingSessions": "Vikao vya kuandika msimbo kwa AI", - "startCodingTaskFromPhone": "Anza kazi ya kuandika msimbo kutoka kwenye simu yako au endelea kikao kutoka kwenye CLI yako.", - "tryIt": "Jaribu", "newCodingTask": "Kazi mpya ya kuandika msimbo", "explore": "Chunguza", "seeAll": "Ona zote", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Funga {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ta.json b/apps/mobile/src/i18n/locales/ta.json index c6bf6952f0..95793a1aae 100644 --- a/apps/mobile/src/i18n/locales/ta.json +++ b/apps/mobile/src/i18n/locales/ta.json @@ -148,7 +148,9 @@ "appearanceSystem": "கணினி", "appearanceLight": "வெளிச்சம்", "appearanceDark": "இருள்", - "account": "கணக்கு" + "account": "கணக்கு", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "இழுப்பு கோரிக்கை கிடைக்கவில்லை", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "உங்கள் வினவல் இழக்கப்படும்.", "keepEditing": "தொடர்ந்து திருத்து", "discard": "நிராகரி", - "discardFailed": "வரைவை நிராகரிக்க முடியவில்லை. மீண்டும் முயற்சிக்கவும்." + "discardFailed": "வரைவை நிராகரிக்க முடியவில்லை. மீண்டும் முயற்சிக்கவும்.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} கோப்பு புதுப்பிக்கப்பட்டது", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "அமர்வுகளை ஏற்ற முடியவில்லை", "couldNotLoadActiveSessions": "செயலில் உள்ள அமர்வுகளை ஏற்ற முடியவில்லை", "agentSessions": "முகவர் அமர்வுகள்", - "showingSavedSessions": "சேமித்த அமர்வுகளைக் காட்டுகிறது — நேரடி நிலை காலாவதியானதாக இருக்கலாம்", - "startNewAgentSession": "புதிய Kilo முகவர் அமர்வைத் தொடங்கு", - "aiCodingSessions": "AI குறியீட்டு அமர்வுகள்", - "startCodingTaskFromPhone": "உங்கள் தொலைபேசியிலிருந்து குறியீட்டு பணியைத் தொடங்கவும் அல்லது உங்கள் CLIயிலிருந்து ஒரு அமர்வைத் தொடரவும்.", - "tryIt": "முயற்சிக்கவும்", "newCodingTask": "புதிய குறியீட்டு பணி", "explore": "ஆராய்க", "seeAll": "அனைத்தையும் காண்க", - "kiloAgents": "Kilo முகவர்கள்" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} மூடு", diff --git a/apps/mobile/src/i18n/locales/te.json b/apps/mobile/src/i18n/locales/te.json index cfe5ee6d6c..34a0ac702d 100644 --- a/apps/mobile/src/i18n/locales/te.json +++ b/apps/mobile/src/i18n/locales/te.json @@ -148,7 +148,9 @@ "appearanceSystem": "సిస్టమ్", "appearanceLight": "లైట్", "appearanceDark": "డార్క్", - "account": "ఖాతా" + "account": "ఖాతా", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "పుల్ రిక్వెస్ట్ అందుబాటులో లేదు", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "మీ ప్రాంప్ట్ కోల్పోతుంది.", "keepEditing": "ఎడిటింగ్ కొనసాగించండి", "discard": "విస్మరించండి", - "discardFailed": "డ్రాఫ్ట్‌ను విస్మరించలేకపోయింది. దయచేసి మళ్ళీ ప్రయత్నించండి." + "discardFailed": "డ్రాఫ్ట్‌ను విస్మరించలేకపోయింది. దయచేసి మళ్ళీ ప్రయత్నించండి.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ఫైల్ నవీకరించబడింది", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "సెషన్లను లోడ్ చేయలేకపోయాము", "couldNotLoadActiveSessions": "యాక్టివ్ సెషన్లను లోడ్ చేయలేకపోయాము", "agentSessions": "ఏజెంట్ సెషన్లు", - "showingSavedSessions": "సేవ్ చేసిన సెషన్లను చూపిస్తోంది — లైవ్ స్థితి పాతది కావచ్చు", - "startNewAgentSession": "కొత్త Kilo ఏజెంట్ సెషన్ను ప్రారంభించండి", - "aiCodingSessions": "AI కోడింగ్ సెషన్లు", - "startCodingTaskFromPhone": "మీ ఫోన్ నుండి కోడింగ్ పనిని ప్రారంభించండి లేదా మీ CLI నుండి సెషన్ను కొనసాగించండి.", - "tryIt": "ప్రయత్నించండి", "newCodingTask": "కొత్త కోడింగ్ పని", "explore": "అన్వేషించండి", "seeAll": "అన్నీ చూడండి", - "kiloAgents": "Kilo ఏజెంట్లు" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} మూసివేయండి", diff --git a/apps/mobile/src/i18n/locales/th.json b/apps/mobile/src/i18n/locales/th.json index 6f4ba0c805..8484d75a7c 100644 --- a/apps/mobile/src/i18n/locales/th.json +++ b/apps/mobile/src/i18n/locales/th.json @@ -148,7 +148,9 @@ "appearanceSystem": "ระบบ", "appearanceLight": "สว่าง", "appearanceDark": "มืด", - "account": "บัญชี" + "account": "บัญชี", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "ไม่สามารถใช้ pull request ได้", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "ข้อความของคุณจะสูญหาย", "keepEditing": "แก้ไขต่อไป", "discard": "ทิ้ง", - "discardFailed": "ไม่สามารถทิ้งฉบับร่างได้ กรุณาลองอีกครั้ง" + "discardFailed": "ไม่สามารถทิ้งฉบับร่างได้ กรุณาลองอีกครั้ง", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "อัปเดต {{displayCount}} ไฟล์", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "โหลดเซสชันไม่สำเร็จ", "couldNotLoadActiveSessions": "โหลดเซสชันที่ใช้งานอยู่ไม่สำเร็จ", "agentSessions": "เซสชันเอเจนต์", - "showingSavedSessions": "กำลังแสดงเซสชันที่บันทึกไว้ — สถานะสดอาจไม่เป็นปัจจุบัน", - "startNewAgentSession": "เริ่มเซสชัน Kilo Agent ใหม่", - "aiCodingSessions": "เซสชันการเขียนโค้ด AI", - "startCodingTaskFromPhone": "เริ่มงานการเขียนโค้ดจากโทรศัพท์ของคุณ หรือดำเนินการต่อจากเซสชันใน CLI ของคุณ", - "tryIt": "ลองเลย", "newCodingTask": "งานการเขียนโค้ดใหม่", "explore": "สำรวจ", "seeAll": "ดูทั้งหมด", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "ปิด {{filename}}", diff --git a/apps/mobile/src/i18n/locales/tr.json b/apps/mobile/src/i18n/locales/tr.json index 47e56220c7..8cf83558cf 100644 --- a/apps/mobile/src/i18n/locales/tr.json +++ b/apps/mobile/src/i18n/locales/tr.json @@ -148,7 +148,9 @@ "appearanceSystem": "Sistem", "appearanceLight": "Açık", "appearanceDark": "Koyu", - "account": "Hesap" + "account": "Hesap", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Bildirimler", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "İsteminiz kaybolacak.", "discardDraftTitle": "Taslak silinsin mi?", "discardFailed": "Taslak silinemedi. Lütfen tekrar deneyin.", - "keepEditing": "Düzenlemeye devam et" + "keepEditing": "Düzenlemeye devam et", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} dosya güncellendi", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "Oturumlar yüklenemedi", "couldNotLoadActiveSessions": "Etkin oturumlar yüklenemedi", "agentSessions": "Ajan oturumları", - "showingSavedSessions": "Kayıtlı oturumlar gösteriliyor — canlı durum güncel olmayabilir", - "startNewAgentSession": "Yeni bir Kilo Ajanı oturumu başlat", - "aiCodingSessions": "Yapay zeka kodlama oturumları", - "startCodingTaskFromPhone": "Telefonunuzdan bir kodlama görevi başlatın veya CLI'nizden bir oturuma devam edin.", - "tryIt": "Dene", "newCodingTask": "Yeni kodlama görevi", "explore": "Keşfet", "seeAll": "Tümünü gör", - "kiloAgents": "Kilo Ajanları" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} kapat", diff --git a/apps/mobile/src/i18n/locales/uk.json b/apps/mobile/src/i18n/locales/uk.json index 8cdac07bbf..a7fc262d56 100644 --- a/apps/mobile/src/i18n/locales/uk.json +++ b/apps/mobile/src/i18n/locales/uk.json @@ -148,7 +148,9 @@ "appearanceSystem": "Системна", "appearanceLight": "Світла", "appearanceDark": "Темна", - "account": "Обліковий запис" + "account": "Обліковий запис", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Сповіщення", @@ -1738,7 +1740,8 @@ "discardDraftMessage": "Ваш запит буде втрачено.", "discardDraftTitle": "Відкинути чернетку?", "discardFailed": "Не вдалося відкинути чернетку. Спробуйте ще раз.", - "keepEditing": "Продовжити редагування" + "keepEditing": "Продовжити редагування", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Оновлено {{displayCount}} файл", @@ -2234,15 +2237,10 @@ "couldNotLoadSessions": "Не вдалося завантажити сеанси", "couldNotLoadActiveSessions": "Не вдалося завантажити активні сеанси", "agentSessions": "Сеанси агента", - "showingSavedSessions": "Показано збережені сеанси — живий статус може бути застарілим", - "startNewAgentSession": "Розпочати новий сеанс Kilo Agent", - "aiCodingSessions": "Сеанси кодування з ШІ", - "startCodingTaskFromPhone": "Розпочніть завдання кодування з телефона або продовжте сеанс зі свого CLI.", - "tryIt": "Спробувати", "newCodingTask": "Нове завдання кодування", "explore": "Дослідити", "seeAll": "Показати всі", - "kiloAgents": "Агенти Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Закрити {{filename}}", diff --git a/apps/mobile/src/i18n/locales/ur.json b/apps/mobile/src/i18n/locales/ur.json index 0e3729148d..83621b5946 100644 --- a/apps/mobile/src/i18n/locales/ur.json +++ b/apps/mobile/src/i18n/locales/ur.json @@ -148,7 +148,9 @@ "appearanceSystem": "سسٹم", "appearanceLight": "روشنی", "appearanceDark": "تاریک", - "account": "اکاؤنٹ" + "account": "اکاؤنٹ", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "پل ریکویسٹ دستیاب نہیں", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "آپ کا پرامپٹ ضائع ہو جائے گا۔", "keepEditing": "ترمیم جاری رکھیں", "discard": "خارج کریں", - "discardFailed": "ڈرافٹ خارج نہیں ہو سکا۔ براہ کرم دوبارہ کوشش کریں۔" + "discardFailed": "ڈرافٹ خارج نہیں ہو سکا۔ براہ کرم دوبارہ کوشش کریں۔", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} فائل اپ ڈیٹ ہوئی", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "سیشنز لوڈ نہیں ہو سکے", "couldNotLoadActiveSessions": "فعال سیشنز لوڈ نہیں ہو سکے", "agentSessions": "ایجنٹ سیشنز", - "showingSavedSessions": "محفوظ شدہ سیشن دکھا رہے ہیں — لائیو سٹیٹس پرانی ہو سکتی ہے", - "startNewAgentSession": "نیا Kilo ایجنٹ سیشن شروع کریں", - "aiCodingSessions": "AI کوڈنگ سیشنز", - "startCodingTaskFromPhone": "اپنے فون سے کوڈنگ کا کام شروع کریں یا اپنے CLI سے سیشن جاری رکھیں۔", - "tryIt": "آزمائیں", "newCodingTask": "نیا کوڈنگ کام", "explore": "دریافت کریں", "seeAll": "سب دیکھیں", - "kiloAgents": "Kilo ایجنٹس" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} بند کریں", diff --git a/apps/mobile/src/i18n/locales/uz.json b/apps/mobile/src/i18n/locales/uz.json index 92f2916dad..c028c1b171 100644 --- a/apps/mobile/src/i18n/locales/uz.json +++ b/apps/mobile/src/i18n/locales/uz.json @@ -148,7 +148,9 @@ "appearanceSystem": "Tizim", "appearanceLight": "Yorug'", "appearanceDark": "Qorong'i", - "account": "Hisob" + "account": "Hisob", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Pull request mavjud emas", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Promptingiz yo'qoladi.", "keepEditing": "Tahrirlashda davom etish", "discard": "Bekor qilish", - "discardFailed": "Qoralamani bekor qilib bo'lmadi. Iltimos, qayta urinib ko'ring." + "discardFailed": "Qoralamani bekor qilib bo'lmadi. Iltimos, qayta urinib ko'ring.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "{{displayCount}} ta fayl yangilandi", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Sessiyalarni yuklab bo'lmadi", "couldNotLoadActiveSessions": "Faol sessiyalarni yuklab bo'lmadi", "agentSessions": "Agent sessiyalari", - "showingSavedSessions": "Saqlangan sessiyalar ko'rsatilmoqda — jonli holat eskirgan bo'lishi mumkin", - "startNewAgentSession": "Yangi Kilo Agent sessiyasini boshlash", - "aiCodingSessions": "AI kodlash sessiyalari", - "startCodingTaskFromPhone": "Telefoningizdan kodlash vazifasini boshlang yoki CLI'ingizdan sessiyani davom ettiring.", - "tryIt": "Sinab ko'ring", "newCodingTask": "Yangi kodlash vazifasi", "explore": "O'rganish", "seeAll": "Barchasini ko'rish", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "{{filename}} ni yopish", diff --git a/apps/mobile/src/i18n/locales/vi.json b/apps/mobile/src/i18n/locales/vi.json index 455691db46..50651ac217 100644 --- a/apps/mobile/src/i18n/locales/vi.json +++ b/apps/mobile/src/i18n/locales/vi.json @@ -148,7 +148,9 @@ "appearanceSystem": "Hệ thống", "appearanceLight": "Sáng", "appearanceDark": "Tối", - "account": "Tài khoản" + "account": "Tài khoản", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "Thông báo", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "Lời nhắc của bạn sẽ bị mất.", "discardDraftTitle": "Hủy bản nháp?", "discardFailed": "Không thể hủy bản nháp. Vui lòng thử lại.", - "keepEditing": "Tiếp tục chỉnh sửa" + "keepEditing": "Tiếp tục chỉnh sửa", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Đã cập nhật {{displayCount}} tệp", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "Không thể tải các phiên", "couldNotLoadActiveSessions": "Không thể tải các phiên đang hoạt động", "agentSessions": "Phiên tác nhân", - "showingSavedSessions": "Đang hiển thị các phiên đã lưu — trạng thái trực tiếp có thể đã cũ", - "startNewAgentSession": "Bắt đầu phiên Kilo Agent mới", - "aiCodingSessions": "Phiên lập trình AI", - "startCodingTaskFromPhone": "Bắt đầu tác vụ lập trình từ điện thoại hoặc tiếp tục phiên từ CLI của bạn.", - "tryIt": "Thử ngay", "newCodingTask": "Tác vụ lập trình mới", "explore": "Khám phá", "seeAll": "Xem tất cả", - "kiloAgents": "Tác nhân Kilo" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Đóng {{filename}}", diff --git a/apps/mobile/src/i18n/locales/yo.json b/apps/mobile/src/i18n/locales/yo.json index e9e8b9b53a..1263060626 100644 --- a/apps/mobile/src/i18n/locales/yo.json +++ b/apps/mobile/src/i18n/locales/yo.json @@ -148,7 +148,9 @@ "appearanceSystem": "Eto", "appearanceLight": "Imọlẹ", "appearanceDark": "Dudu", - "account": "Àkọọ́lẹ̀" + "account": "Àkọọ́lẹ̀", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Ibeere fifa ko si", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "A yoo padanu iyanilenu rẹ.", "keepEditing": "Pa ṣatunkọ mọ", "discard": "Jù silẹ", - "discardFailed": "Ko le fi osere silẹ. Jọwọ gbiyanju lẹẹkansi." + "discardFailed": "Ko le fi osere silẹ. Jọwọ gbiyanju lẹẹkansi.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Ti ṣe imudojuiwọn {{displayCount}} faili", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Ko le ṣagbewọ awọn igba-ijiroro", "couldNotLoadActiveSessions": "Ko le ṣagbewọ awọn igba-ijiroro ti n ṣiṣẹ", "agentSessions": "Awọn igba-ijiroro aṣoju", - "showingSavedSessions": "Nfi awọn igba-ijiroro ti o ti fi pamọ han — ipo laaye le ti atijo", - "startNewAgentSession": "Bẹrẹ igba-ijiroro Kilo Agent tuntun", - "aiCodingSessions": "Awọn igba-ijiroro koodu AI", - "startCodingTaskFromPhone": "Bẹrẹ iṣẹ-ṣiṣe koodu lati foonu rẹ tabi tẹsiwaju igba-ijiroro lati CLI rẹ.", - "tryIt": "Gbiyanju", "newCodingTask": "Iṣẹ-ṣiṣe koodu tuntun", "explore": "Ṣàwárí", "seeAll": "Wo gbogbo", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Pa {{filename}}", diff --git a/apps/mobile/src/i18n/locales/zh-Hans.json b/apps/mobile/src/i18n/locales/zh-Hans.json index e6f7067bd6..aac301cdd8 100644 --- a/apps/mobile/src/i18n/locales/zh-Hans.json +++ b/apps/mobile/src/i18n/locales/zh-Hans.json @@ -148,7 +148,9 @@ "appearanceSystem": "跟随系统", "appearanceLight": "浅色", "appearanceDark": "深色", - "account": "账户" + "account": "账户", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "通知", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "您的提示词将会丢失。", "discardDraftTitle": "丢弃草稿?", "discardFailed": "无法丢弃草稿。请重试。", - "keepEditing": "继续编辑" + "keepEditing": "继续编辑", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "已更新 {{displayCount}} 个文件", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "无法加载会话", "couldNotLoadActiveSessions": "无法加载活动会话", "agentSessions": "智能体会话", - "showingSavedSessions": "显示已保存的会话 — 实时状态可能已过时", - "startNewAgentSession": "开始新的 Kilo Agent 会话", - "aiCodingSessions": "AI 编程会话", - "startCodingTaskFromPhone": "从手机开始编程任务,或从 CLI 继续会话。", - "tryIt": "试一试", "newCodingTask": "新编程任务", "explore": "探索", "seeAll": "查看全部", - "kiloAgents": "Kilo 代理" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "关闭 {{filename}}", diff --git a/apps/mobile/src/i18n/locales/zh-Hant.json b/apps/mobile/src/i18n/locales/zh-Hant.json index d05487d6ce..f49afa3883 100644 --- a/apps/mobile/src/i18n/locales/zh-Hant.json +++ b/apps/mobile/src/i18n/locales/zh-Hant.json @@ -148,7 +148,9 @@ "appearanceSystem": "系統", "appearanceLight": "淺色", "appearanceDark": "深色", - "account": "帳戶" + "account": "帳戶", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "notifications": { "title": "通知", @@ -1722,7 +1724,8 @@ "discardDraftMessage": "您的提示將遺失。", "discardDraftTitle": "捨棄草稿?", "discardFailed": "無法捨棄草稿。請再試一次。", - "keepEditing": "繼續編輯" + "keepEditing": "繼續編輯", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "已更新 {{displayCount}} 個檔案", @@ -2208,15 +2211,10 @@ "couldNotLoadSessions": "無法載入工作階段", "couldNotLoadActiveSessions": "無法載入使用中的工作階段", "agentSessions": "代理工作階段", - "showingSavedSessions": "顯示已儲存的工作階段 — 即時狀態可能已過時", - "startNewAgentSession": "開始新的 Kilo Agent 工作階段", - "aiCodingSessions": "AI 程式設計工作階段", - "startCodingTaskFromPhone": "從手機開始程式設計任務,或從 CLI 繼續工作階段。", - "tryIt": "試試看", "newCodingTask": "新程式設計任務", "explore": "探索", "seeAll": "查看全部", - "kiloAgents": "Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "關閉 {{filename}}", diff --git a/apps/mobile/src/i18n/locales/zu.json b/apps/mobile/src/i18n/locales/zu.json index af6c8cdbaf..07c3972877 100644 --- a/apps/mobile/src/i18n/locales/zu.json +++ b/apps/mobile/src/i18n/locales/zu.json @@ -148,7 +148,9 @@ "appearanceSystem": "Isistimu", "appearanceLight": "Ukukhanya", "appearanceDark": "Ubumnyama", - "account": "Akhawunti" + "account": "Akhawunti", + "prReviewAttribution": "Add app attribution to PR reviews", + "prReviewAttributionSubtitle": "Append a Reviewed via Kilo footer when you submit a review." }, "prReview": { "pullRequestUnavailable": "Isicelo sokudonsa asitholakali", @@ -2176,7 +2178,8 @@ "discardDraftMessage": "Umphakamo wakho uzolahleka.", "keepEditing": "Qhubeka uhlela", "discard": "Lahla", - "discardFailed": "Akukwazanga ukulahla idrafuthi. Sicela uzame futhi." + "discardFailed": "Akukwazanga ukulahla idrafuthi. Sicela uzame futhi.", + "remoteHint": "Run `kilo remote` on your computer, or `/remote` in a running CLI session, to control a local kilo process." }, "partRenderer": { "updatedFileCount_one": "Kubuyekezwe ifayela eli-{{displayCount}}", @@ -2662,15 +2665,10 @@ "couldNotLoadSessions": "Asikwazanga ukulayisha amaseshini", "couldNotLoadActiveSessions": "Asikwazanga ukulayisha amaseshini asebenzayo", "agentSessions": "Amaseshini e-agent", - "showingSavedSessions": "Kuboniswa amaseshini agciniwe — isimo esibukhoma singase siphelelwe yisikhathi", - "startNewAgentSession": "Qala iseshini entsha ye-Kilo Agent", - "aiCodingSessions": "Amaseshini okubhala ikhodi e-AI", - "startCodingTaskFromPhone": "Qala umsebenzi wokubhala ikhodi usuka kufoni yakho noma uqhubeke ngeseshini esuka ku-CLI yakho.", - "tryIt": "Zama", "newCodingTask": "Umsebenzi omusha wokubhala ikhodi", "explore": "Hlola", "seeAll": "Bona konke", - "kiloAgents": "Ama-Kilo Agents" + "noLiveSessions": "Nothing running right now" }, "imageViewer": { "close": "Vala {{filename}}", diff --git a/apps/mobile/src/lib/auth/auth-context.test.tsx b/apps/mobile/src/lib/auth/auth-context.test.tsx index 9f67ed1c21..b5504300d3 100644 --- a/apps/mobile/src/lib/auth/auth-context.test.tsx +++ b/apps/mobile/src/lib/auth/auth-context.test.tsx @@ -147,14 +147,18 @@ vi.mock('@/lib/hooks/use-persisted-agent-model', () => ({ clearAgentModelPreference: vi.fn(), })); -const { clearKeepScreenOnPreference, clearReasoningPreference } = vi.hoisted(() => ({ - clearKeepScreenOnPreference: vi.fn(), - clearReasoningPreference: vi.fn(), -})); +const { clearKeepScreenOnPreference, clearReasoningPreference, clearPrReviewFooterPreference } = + vi.hoisted(() => ({ + clearKeepScreenOnPreference: vi.fn(), + clearReasoningPreference: vi.fn(), + clearPrReviewFooterPreference: vi.fn(), + })); vi.mock('@/lib/hooks/use-keep-screen-on-preference', () => ({ clearKeepScreenOnPreference })); vi.mock('@/lib/hooks/use-reasoning-preference', () => ({ clearReasoningPreference })); +vi.mock('@/lib/hooks/use-pr-review-footer-preference', () => ({ clearPrReviewFooterPreference })); + vi.mock('@/lib/last-active-instance', () => ({ clearLastActiveInstance: vi.fn().mockResolvedValue(undefined), })); @@ -385,7 +389,7 @@ describe('sign-out teardown ordering', () => { unmount(); }); - it('clears both local preferences on sign-out', async () => { + it('clears the local preferences on sign-out', async () => { const { ctx } = await mountAndGetContext(); await act(async () => { @@ -394,6 +398,7 @@ describe('sign-out teardown ordering', () => { expect(clearKeepScreenOnPreference).toHaveBeenCalled(); expect(clearReasoningPreference).toHaveBeenCalled(); + expect(clearPrReviewFooterPreference).toHaveBeenCalled(); }); it('closes the ownership gate before any await and blocks a late persist', async () => { diff --git a/apps/mobile/src/lib/auth/auth-context.tsx b/apps/mobile/src/lib/auth/auth-context.tsx index dec04a63a1..fdc87a2e29 100644 --- a/apps/mobile/src/lib/auth/auth-context.tsx +++ b/apps/mobile/src/lib/auth/auth-context.tsx @@ -37,6 +37,7 @@ import { import { chainSave } from '@/lib/hooks/save-chain'; import { clearAgentModelPreference } from '@/lib/hooks/use-persisted-agent-model'; import { clearKeepScreenOnPreference } from '@/lib/hooks/use-keep-screen-on-preference'; +import { clearPrReviewFooterPreference } from '@/lib/hooks/use-pr-review-footer-preference'; import { clearReasoningPreference } from '@/lib/hooks/use-reasoning-preference'; import { clearKiloClawOwned, gateKiloClawOwned } from '@/lib/kiloclaw-tab-ownership'; import { clearLastActiveInstance } from '@/lib/last-active-instance'; @@ -337,6 +338,7 @@ export function AuthProvider({ children }: { readonly children: ReactNode }) { clearAgentModelPreference(); clearReasoningPreference(); clearKeepScreenOnPreference(); + clearPrReviewFooterPreference(); } finally { queryClient.clear(); setSessionEnded(ended); diff --git a/apps/mobile/src/lib/auth/credentials.test.ts b/apps/mobile/src/lib/auth/credentials.test.ts index b8a572b75c..df7048efb2 100644 --- a/apps/mobile/src/lib/auth/credentials.test.ts +++ b/apps/mobile/src/lib/auth/credentials.test.ts @@ -55,6 +55,9 @@ vi.mock('@/lib/hooks/use-persisted-agent-model', () => ({ clearAgentModelPrefere vi.mock('@/lib/hooks/use-keep-screen-on-preference', () => ({ clearKeepScreenOnPreference: vi.fn(), })); +vi.mock('@/lib/hooks/use-pr-review-footer-preference', () => ({ + clearPrReviewFooterPreference: vi.fn(), +})); vi.mock('@/lib/hooks/use-reasoning-preference', () => ({ clearReasoningPreference: vi.fn() })); vi.mock('@/lib/kiloclaw-tab-ownership', () => ({ gateKiloClawOwned: vi.fn(), diff --git a/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.test.ts b/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.test.ts new file mode 100644 index 0000000000..e43e357fb5 --- /dev/null +++ b/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.test.ts @@ -0,0 +1,112 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +const { getItemAsync, setItemAsync, deleteItemAsync } = vi.hoisted(() => ({ + getItemAsync: vi.fn(), + setItemAsync: vi.fn(), + deleteItemAsync: vi.fn(), +})); +vi.mock('expo-secure-store', () => ({ getItemAsync, setItemAsync, deleteItemAsync })); + +const { captureException } = vi.hoisted(() => ({ captureException: vi.fn() })); +vi.mock('@sentry/react-native', () => ({ captureException })); + +const { toastError } = vi.hoisted(() => ({ toastError: vi.fn() })); +vi.mock('sonner-native', () => ({ toast: { error: toastError } })); + +// eslint-disable-next-line typescript-eslint/promise-function-async -- conflicting require-await rule +function flushMicrotasks(): Promise { + return new Promise(resolve => { + setImmediate(resolve); + }); +} + +// eslint-disable-next-line no-empty-function -- listener body is irrelevant, only subscribe()'s side effect (starting the load) is under test +function noopListener(): void {} + +// eslint-disable-next-line typescript-eslint/promise-function-async -- conflicting require-await rule +function makeStore() { + // Re-import lazily so the mock wiring above is in effect. + return import('./secure-store-preference').then(({ createSecureStorePreference }) => + createSecureStorePreference({ + key: 'pr-review-footer-enabled', + defaultValue: true, + parse: raw => raw !== 'false', + serialize: value => (value ? 'true' : 'false'), + }) + ); +} + +describe('parsePrReviewFooter', () => { + it('defaults to on for a missing value (fresh install and unreadable read)', async () => { + const { parsePrReviewFooter } = await import('./use-pr-review-footer-preference'); + expect(parsePrReviewFooter(null)).toBe(true); + }); + + it("reads 'true' as on", async () => { + const { parsePrReviewFooter } = await import('./use-pr-review-footer-preference'); + expect(parsePrReviewFooter('true')).toBe(true); + }); + + it("reads 'false' as off — the only value that turns the preference off", async () => { + const { parsePrReviewFooter } = await import('./use-pr-review-footer-preference'); + expect(parsePrReviewFooter('false')).toBe(false); + }); + + it('treats any other stored string as on', async () => { + const { parsePrReviewFooter } = await import('./use-pr-review-footer-preference'); + expect(parsePrReviewFooter('')).toBe(true); + expect(parsePrReviewFooter('nonsense')).toBe(true); + }); +}); + +describe('pr-review-footer store', () => { + beforeEach(() => { + getItemAsync.mockReset(); + setItemAsync.mockReset(); + deleteItemAsync.mockReset(); + captureException.mockReset(); + toastError.mockReset(); + }); + + it('defaults to on when SecureStore returns null', async () => { + getItemAsync.mockResolvedValue(null); + const store = await makeStore(); + + const unsubscribe = store.subscribe(noopListener); + await flushMicrotasks(); + + expect(store.get()).toBe(true); + expect(store.getHasLoaded()).toBe(true); + unsubscribe(); + }); + + it("turns off only for the stored string 'false'", async () => { + getItemAsync.mockResolvedValue('false'); + const store = await makeStore(); + + const unsubscribe = store.subscribe(noopListener); + await flushMicrotasks(); + + expect(store.get()).toBe(false); + expect(store.getHasLoaded()).toBe(true); + unsubscribe(); + }); + + it('persists a set value and clears back to the default on sign-out', async () => { + getItemAsync.mockResolvedValue(null); + const store = await makeStore(); + + store.set(false); + await flushMicrotasks(); + expect(setItemAsync).toHaveBeenCalledWith('pr-review-footer-enabled', 'false'); + + store.set(true); + await flushMicrotasks(); + expect(setItemAsync).toHaveBeenCalledWith('pr-review-footer-enabled', 'true'); + + store.clear(); + await flushMicrotasks(); + expect(deleteItemAsync).toHaveBeenCalledWith('pr-review-footer-enabled'); + expect(store.get()).toBe(true); + }); +}); diff --git a/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.ts b/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.ts new file mode 100644 index 0000000000..8fae001f02 --- /dev/null +++ b/apps/mobile/src/lib/hooks/use-pr-review-footer-preference.ts @@ -0,0 +1,34 @@ +import { useSyncExternalStore } from 'react'; + +import { createSecureStorePreference } from '@/lib/hooks/secure-store-preference'; +import { PR_REVIEW_FOOTER_KEY } from '@/lib/storage-keys'; + +/** + * Default-on preference: only the exact stored string 'false' turns it off, so a + * missing or unreadable value keeps the PR-review attribution footer the app + * ships with. + */ +export function parsePrReviewFooter(raw: string | null): boolean { + return raw !== 'false'; +} + +const store = createSecureStorePreference({ + key: PR_REVIEW_FOOTER_KEY, + defaultValue: true, + parse: parsePrReviewFooter, + serialize: value => (value ? 'true' : 'false'), +}); + +export function clearPrReviewFooterPreference() { + store.clear(); +} + +function setPrReviewFooter(value: boolean) { + store.set(value); +} + +export function usePrReviewFooterPreference() { + const prReviewFooter = useSyncExternalStore(store.subscribe, store.get); + const hasLoaded = useSyncExternalStore(store.subscribe, store.getHasLoaded); + return { prReviewFooter, hasLoaded, setPrReviewFooter }; +} diff --git a/apps/mobile/src/lib/pr-review/review-footer.test.ts b/apps/mobile/src/lib/pr-review/review-footer.test.ts new file mode 100644 index 0000000000..538e2f5d34 --- /dev/null +++ b/apps/mobile/src/lib/pr-review/review-footer.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; + +import { buildReviewFooter } from './review-footer'; + +const IOS_URL = 'https://apps.apple.com/app/id6761193135'; +const ANDROID_URL = 'https://play.google.com/store/apps/details?id=com.kilocode.kiloapp'; + +describe('buildReviewFooter', () => { + it('builds the iOS footer with the App Store link for the ios platform', () => { + expect(buildReviewFooter('ios')).toBe(`\n\n---\nReviewed via the [Kilo iOS app](${IOS_URL})`); + }); + + it('builds the Android footer with the Play Store link for the android platform', () => { + expect(buildReviewFooter('android')).toBe( + `\n\n---\nReviewed via the [Kilo Android app](${ANDROID_URL})` + ); + }); + + it('builds the Android footer for any non-iOS platform string', () => { + expect(buildReviewFooter('windows')).toBe( + `\n\n---\nReviewed via the [Kilo Android app](${ANDROID_URL})` + ); + }); +}); diff --git a/apps/mobile/src/lib/pr-review/review-footer.ts b/apps/mobile/src/lib/pr-review/review-footer.ts new file mode 100644 index 0000000000..66d647aada --- /dev/null +++ b/apps/mobile/src/lib/pr-review/review-footer.ts @@ -0,0 +1,14 @@ +// PR-review attribution footer appended to the review summary when the +// default-on preference is enabled. The input is the running platform's OS, +// so iOS gets the iOS App Store link and every other platform (Android +// included) gets the Play Store link. + +const IOS_APP_URL = 'https://apps.apple.com/app/id6761193135'; +const ANDROID_APP_URL = 'https://play.google.com/store/apps/details?id=com.kilocode.kiloapp'; + +export function buildReviewFooter(os: 'ios' | 'android' | string): string { + if (os === 'ios') { + return `\n\n---\nReviewed via the [Kilo iOS app](${IOS_APP_URL})`; + } + return `\n\n---\nReviewed via the [Kilo Android app](${ANDROID_APP_URL})`; +} diff --git a/apps/mobile/src/lib/resolve-live-instance.test.ts b/apps/mobile/src/lib/resolve-live-instance.test.ts new file mode 100644 index 0000000000..0f00165318 --- /dev/null +++ b/apps/mobile/src/lib/resolve-live-instance.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; + +import { type InstancePickerInstance } from '@/lib/picker-bridge'; + +import { resolveLiveInstance } from './resolve-live-instance'; + +const SELECTED: InstancePickerInstance = { + connectionId: 'conn-stale', + name: 'host-a', + projectName: 'proj', +}; + +describe('resolveLiveInstance', () => { + it('returns the instance with the same connectionId', () => { + const live: InstancePickerInstance = { + connectionId: 'conn-stale', + name: 'host-a', + projectName: 'proj', + version: '1.2.3', + }; + + expect(resolveLiveInstance(SELECTED, [live])).toBe(live); + }); + + it('returns the instance with the same name and projectName when the id differs', () => { + const live: InstancePickerInstance = { + connectionId: 'conn-live', + name: 'host-a', + projectName: 'proj', + }; + + expect(resolveLiveInstance(SELECTED, [live])).toBe(live); + }); + + it('returns null when no instance matches the id or the name/project pair', () => { + const unrelated: InstancePickerInstance = { + connectionId: 'conn-other', + name: 'host-b', + projectName: 'other', + }; + + expect(resolveLiveInstance(SELECTED, [unrelated])).toBeNull(); + expect(resolveLiveInstance(SELECTED, [])).toBeNull(); + }); +}); diff --git a/apps/mobile/src/lib/resolve-live-instance.ts b/apps/mobile/src/lib/resolve-live-instance.ts new file mode 100644 index 0000000000..37ae5705d0 --- /dev/null +++ b/apps/mobile/src/lib/resolve-live-instance.ts @@ -0,0 +1,31 @@ +import { type InstancePickerInstance } from '@/lib/picker-bridge'; + +/** + * Resolve the live instance row for a press-time selection against a freshly + * refetched instance list. + * + * A host reboot reconnects its CLI with the same instance `name` and + * `projectName` but a new `connectionId`. The selector keeps the old + * `connectionId`; the refetched list carries the live one. This helper maps + * the selection onto the live row: + * + * - identical `connectionId` -> the live row for that id + * - same `name`+`projectName` -> the live row (connectionId remapped) + * - otherwise -> null (the host has fully disconnected) + */ +export function resolveLiveInstance( + selected: InstancePickerInstance, + instances: InstancePickerInstance[] +): InstancePickerInstance | null { + const sameConnectionId = instances.find( + instance => instance.connectionId === selected.connectionId + ); + if (sameConnectionId) { + return sameConnectionId; + } + + const sameHost = instances.find( + instance => instance.name === selected.name && instance.projectName === selected.projectName + ); + return sameHost ?? null; +} diff --git a/apps/mobile/src/lib/share-payload.ts b/apps/mobile/src/lib/share-payload.ts index 595d4ad490..9a46e03901 100644 --- a/apps/mobile/src/lib/share-payload.ts +++ b/apps/mobile/src/lib/share-payload.ts @@ -1,3 +1,4 @@ +import { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; import * as Crypto from 'expo-crypto'; import { cacheDirectory, copyAsync, deleteAsync } from 'expo-file-system/legacy'; import { type ShareIntent } from 'expo-share-intent'; @@ -13,8 +14,8 @@ export type SharePayload = { failedFiles: string[]; }; -/** Mirrors PROMPT_INPUT_MAX_CHARS in new-session-prompt.tsx (module-local; composer clamps again). */ -export const SHARE_TEXT_MAX_CHARS = 4000; +/** The cloud agent prompt cap; the composer clamps again on delivery. */ +export const SHARE_TEXT_MAX_CHARS = CLOUD_AGENT_PROMPT_MAX_LENGTH; export const SHARE_PAYLOAD_MAX_ENTRIES = 5; diff --git a/apps/mobile/src/lib/storage-keys.ts b/apps/mobile/src/lib/storage-keys.ts index 8896f725a7..b81a4c0de7 100644 --- a/apps/mobile/src/lib/storage-keys.ts +++ b/apps/mobile/src/lib/storage-keys.ts @@ -26,6 +26,7 @@ export const LOGIN_EMAIL_DRAFT_KEY = 'login-email-draft'; /** Login SSO-recovery banner draft, persisted before an RTL language reload. */ export const LOGIN_SSO_RECOVERY_DRAFT_KEY = 'login-sso-recovery-draft'; export const KEEP_SCREEN_ON_KEY = 'keep-session-screen-on'; +export const PR_REVIEW_FOOTER_KEY = 'pr-review-footer-enabled'; /** SQLCipher database key for the encrypted persistence store (DEC-01). */ export const PERSIST_DB_KEY = 'persist-db-key'; /** diff --git a/apps/web/src/lib/cloud-agent/constants.ts b/apps/web/src/lib/cloud-agent/constants.ts index 3e5c5fe9b9..cb88aea974 100644 --- a/apps/web/src/lib/cloud-agent/constants.ts +++ b/apps/web/src/lib/cloud-agent/constants.ts @@ -178,11 +178,7 @@ export function normalizeAttachmentExtension(extension: string | undefined | nul export type { CloudAgentAttachments } from '@kilocode/app-shared/cloud-agent'; /** - * Maximum prompt length (in characters) accepted by the cloud agent. - * - * Mirrors the server-side cap in `services/cloud-agent-next/src/schema.ts` - * (`Limits.MAX_PROMPT_LENGTH`). Prompts exceeding this would be rejected by - * the worker, so we enforce the same limit client-side to give users - * immediate feedback. + * Maximum prompt length (in characters) accepted by the cloud agent. Defined + * once in the SDK so the web, mobile, and extension composers share it. */ -export const CLOUD_AGENT_PROMPT_MAX_LENGTH = 100_000; +export { CLOUD_AGENT_PROMPT_MAX_LENGTH } from '@kilocode/cloud-agent-sdk/limits'; diff --git a/packages/cloud-agent-sdk/src/limits.ts b/packages/cloud-agent-sdk/src/limits.ts new file mode 100644 index 0000000000..e41f2601a3 --- /dev/null +++ b/packages/cloud-agent-sdk/src/limits.ts @@ -0,0 +1,8 @@ +/** + * Maximum prompt length (in characters) accepted by the cloud agent. + * + * Mirrors the server-side cap in `services/cloud-agent-next/src/schema.ts` + * (`Limits.MAX_PROMPT_LENGTH`). Every client enforces the same limit so a + * composer never silently drops text the worker would have accepted. + */ +export const CLOUD_AGENT_PROMPT_MAX_LENGTH = 100_000; diff --git a/services/session-ingest/src/dos/UserConnectionDO.test.ts b/services/session-ingest/src/dos/UserConnectionDO.test.ts index eeb0571fd0..5b4714ed36 100644 --- a/services/session-ingest/src/dos/UserConnectionDO.test.ts +++ b/services/session-ingest/src/dos/UserConnectionDO.test.ts @@ -4117,6 +4117,140 @@ describe('UserConnectionDO', () => { }); }); + // ------------------------------------------------------------------------- + // Same-host reconnect: a rebooted host advertises the same instance name and + // projectName on a fresh connectionId. The stale socket must be closed so + // `getConnectedInstances` lists the host exactly once. + // ------------------------------------------------------------------------- + + describe('same-host replace', () => { + it('closes the stale socket when a second connectionId heartbeats the same instance identity', async () => { + const { doInstance, mockCtx } = setup(); + const first = addCliSocket(mockCtx, 'conn-1'); + const second = addCliSocket(mockCtx, 'conn-2'); + + sendHeartbeat(doInstance, first, [], { + instance: { name: 'host-a', projectName: 'proj' }, + }); + sendHeartbeat(doInstance, second, [], { + instance: { name: 'host-a', projectName: 'proj' }, + }); + + expect(first.close).toHaveBeenCalledWith(1000, 'replaced by same-host reconnect'); + expect(second.close).not.toHaveBeenCalled(); + + // The mock close does not drop the socket; remove it to mirror the + // runtime close before asserting the live-instance scan. + mockCtx.removeSocket(first); + + const { instances } = doInstance.getConnectedInstances(); + expect(instances).toHaveLength(1); + expect(instances[0].connectionId).toBe('conn-2'); + expect(instances[0]).toEqual({ + connectionId: 'conn-2', + name: 'host-a', + projectName: 'proj', + }); + }); + + it('keeps both sockets open when the projectName differs', async () => { + const { doInstance, mockCtx } = setup(); + const first = addCliSocket(mockCtx, 'conn-1'); + const second = addCliSocket(mockCtx, 'conn-2'); + + sendHeartbeat(doInstance, first, [], { + instance: { name: 'host-a', projectName: 'proj-1' }, + }); + sendHeartbeat(doInstance, second, [], { + instance: { name: 'host-a', projectName: 'proj-2' }, + }); + + expect(first.close).not.toHaveBeenCalled(); + expect(second.close).not.toHaveBeenCalled(); + }); + + it('keeps both sockets open when the name differs', async () => { + const { doInstance, mockCtx } = setup(); + const first = addCliSocket(mockCtx, 'conn-1'); + const second = addCliSocket(mockCtx, 'conn-2'); + + sendHeartbeat(doInstance, first, [], { + instance: { name: 'host-a', projectName: 'proj' }, + }); + sendHeartbeat(doInstance, second, [], { + instance: { name: 'host-b', projectName: 'proj' }, + }); + + expect(first.close).not.toHaveBeenCalled(); + expect(second.close).not.toHaveBeenCalled(); + }); + + it('does not broadcast cli.disconnected or overwrite the owner-change terminal result for a same-host replaced socket', async () => { + const { doInstance, mockCtx } = setup(); + const first = addCliSocket(mockCtx, 'conn-1'); + const webWs = addWebSocket(mockCtx, 'web-1'); + + sendHeartbeat(doInstance, first, [makeSession('s1')], { + instance: { name: 'host-a', projectName: 'proj' }, + }); + + // An owner-fenced pending command targets the first socket. + await sendCommand(doInstance, webWs, { + id: 'cmd-1', + command: 'send_message', + sessionId: 's1', + connectionId: 'conn-1', + }); + const correlationId = getCorrelationId(first); + webWs.send.mockClear(); + + // A second socket on a different connectionId claims the same host. + const second = addCliSocket(mockCtx, 'conn-2'); + sendHeartbeat(doInstance, second, [], { + instance: { name: 'host-a', projectName: 'proj' }, + }); + + expect(first.close).toHaveBeenCalledWith(1000, 'replaced by same-host reconnect'); + + // Drain the stale-close failPendingCommandsForSocket waitUntil so the + // owner-change terminal result settles before the close event runs. + await flushAsync(); + + // The stale close delivers the owner-change error, not 'CLI disconnected'. + const preClose = allSent(webWs); + expect(preClose.find(m => m.type === 'response' && m.id === 'cmd-1')).toEqual({ + type: 'response', + id: 'cmd-1', + error: { + source: 'relay', + code: 'SESSION_OWNER_CHANGED', + message: 'Session owner changed', + }, + }); + webWs.send.mockClear(); + + // The close event for the stale first socket now fires. + mockCtx.removeSocket(first); + await disconnectCli(doInstance, first); + + const msgs = allSent(webWs); + expect(msgs.some(m => m.type === 'system' && m.event === 'cli.disconnected')).toBe(false); + expect(msgs.filter(m => m.type === 'response' && m.id === 'cmd-1')).toHaveLength(0); + + // Durable terminal entry keeps the owner-change error. + const durable = mockCtx.storage.store.get(`pendingCommand/${correlationId}`) as { + state: string; + error?: unknown; + }; + expect(durable.state).toBe('done'); + expect(durable.error).toEqual({ + source: 'relay', + code: 'SESSION_OWNER_CHANGED', + message: 'Session owner changed', + }); + }); + }); + // ------------------------------------------------------------------------- // WS attachment size guardrail (W3) // ------------------------------------------------------------------------- diff --git a/services/session-ingest/src/dos/UserConnectionDO.ts b/services/session-ingest/src/dos/UserConnectionDO.ts index 0f090233a6..3e33ec9a42 100644 --- a/services/session-ingest/src/dos/UserConnectionDO.ts +++ b/services/session-ingest/src/dos/UserConnectionDO.ts @@ -42,6 +42,11 @@ type WSAttachment = role: 'cli'; connectionId: string; sessions: HeartbeatSession[]; + // Set on a stale same-host socket just before its close so + // `handleCliDisconnect` treats the close as a replacement (the new + // socket already heartbeated) and skips the `cliGone` pending-command + // sweep and the `cli.disconnected` broadcast. + replaced?: true; // Undefined means no protocolVersion has been reported yet — either the // CLI hasn't sent its first heartbeat, or it's a legacy build that // predates this field entirely. Both cases fall back to legacy behavior. @@ -575,6 +580,11 @@ export class UserConnectionDO extends DurableObject { instance: Instance | undefined ): void { const { connectionId } = attachment; + // Legacy CLIs omit `instance` entirely; only close a stale same-host + // socket when the heartbeat actually carries an instance identity. + if (instance) { + this.closeStaleSocketsForInstance(instance.name, instance.projectName, connectionId); + } const now = Date.now(); this.lastHeartbeatAt.set(connectionId, now); this.connectionProtocolVersion.set(connectionId, protocolVersion); @@ -1731,11 +1741,16 @@ export class UserConnectionDO extends DurableObject { // Exclude the closing socket: under wrangler/workerd, getWebSockets() still // includes it during webSocketClose, so matching self would always look "replaced" // and skip ownership cleanup + attention reset (DEF-5 E2E failure). - const replaced = this.ctx.getWebSockets('cli').some(ws => { - if (ws === disconnectedWs) return false; - const att = ws.deserializeAttachment() as WSAttachment | null; - return att?.role === 'cli' && att.connectionId === connectionId; - }); + // A same-host replacement sets the `replaced` marker on this socket's own + // attachment before closing (see closeStaleSocketsForInstance), because its + // replacement carries a different connectionId. + const replaced = + attachment.replaced || + this.ctx.getWebSockets('cli').some(ws => { + if (ws === disconnectedWs) return false; + const att = ws.deserializeAttachment() as WSAttachment | null; + return att?.role === 'cli' && att.connectionId === connectionId; + }); // Fail pending commands that targeted this specific socket. // Await so the durable terminal entries are persisted before we proceed @@ -2025,6 +2040,41 @@ export class UserConnectionDO extends DurableObject { return false; } + // Old form: one live CLI socket per connectionId, including a rebooted host's stale socket. Remove when every CLI advertises a stable host id. + private closeStaleSocketsForInstance( + name: string, + projectName: string, + keepConnectionId: string + ): void { + if (!name || !projectName) return; + + for (const ws of this.ctx.getWebSockets('cli')) { + const att = ws.deserializeAttachment() as WSAttachment | null; + if (att?.role !== 'cli' || att.connectionId === keepConnectionId) continue; + if (att.instance?.name !== name || att.instance?.projectName !== projectName) continue; + + console.log('Closing stale CLI socket for same-host reconnect', { + connectionId: att.connectionId, + name, + projectName, + }); + this.ctx.waitUntil( + this.failPendingCommandsForSocket(ws, false).catch((error: unknown) => { + console.error('Failed to persist terminal commands for stale socket', { + connectionId: att.connectionId, + error: error instanceof Error ? error.message : String(error), + }); + }) + ); + // Preserve session ownership — the rebooting host's replacement socket + // re-claims the same sessions in its first heartbeat. + // Mark the attachment so `handleCliDisconnect` routes this close through + // the `replaced` early-return (the replacement already heartbeated). + ws.serializeAttachment({ ...att, replaced: true }); + ws.close(1000, 'replaced by same-host reconnect'); + } + } + private replaceWebSocket(connectionId: string): void { for (const ws of this.ctx.getWebSockets('web')) { const attachment = ws.deserializeAttachment() as WSAttachment | null;