* feat(app): workspace labels baseline Existing labels implementation, committed as-is as the baseline for the UI redesign. Server, protocol, catalog store and sync are sound; the UI layer is reworked in the commits that follow. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): show workspace labels on the sidebar meta line Labels could be assigned and then appeared nowhere. They now draw as a chip on the workspace meta row, after the host, change request, checks and service, and they can be switched off from the Show submenu like any other item on that line. One palette, not two. `baseColors.workspaceLabel` was ten raw Tailwind 500s spanning a 1.9:1 to 4.5:1 contrast range — the exact set identity-colors.ts was written to replace. Label color is now the identity color of the same name, so a chip, a host badge and a CI check on one row sit in one contrast band. `workspace-labels/colors.ts` and its per-file `labelColorViolet` blocks are gone; `chip.tsx` and `swatch.tsx` are the only places a label color becomes ink. A row resolves its labels against its own host's catalog and nothing else. The cross-host projection answers too, but two hosts may give one name two colors and the merge settles that by target precedence and then alphabetical serverId — a row painted in a color its host never assigned is lying about the one thing the color is for. The meta row's doc comment argued against tinted chrome on that line. The chip is a deliberate exception — everything else there reports state and shares the state colors, a label is a name a person chose — so the comment now says that instead of contradicting the code. Alignment: - The chip is exactly as tall as the line: text at the row's 16pt lineHeight, no vertical padding, so a labelled row is the same height as an unlabelled one. - A label run that opens the line hangs left by the chip's own padding, landing the name on the rail the title above it uses. Mid-line the ground stays in the flow. - The swatch row reaches 44pt through hitSlop growing outward from a 20pt swatch, replacing ten 44pt colored blobs in the manager modal. - The picker's raw `<Plus>`, `<CheckSquare>` and `<Square>` go through withUnistyles with module-level mappings, at the menu's 14pt. Also fixes a bug this change exposed: `SidebarRowItemsSchema` is a strict object, so adding an item without a key there fails the whole settings write and silently loses every other Show toggle with it. A test now covers each item in SIDEBAR_ROW_ITEMS. Phase 1 of the workspace labels redesign. The filter menu, the picker's interaction model and the manager modal's layout are later phases. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): one Labels filter page with tri-state rows Filtering by label cost three levels: Display -> Labels -> Include -> a page of labels, or -> Exclude -> the same page again. It is now one page: every label across every host, one row each, leading with its colour dot. A row cycles off -> include -> exclude -> off. Include is MenuItem's own check; exclude is lucide's X, the check's own pair, at the same 16 in the same trailing slot, so the column does not shift as a row cycles. Exclude also dims the row, because a state you have to identify by its glyph is not one you read at a glance. Unlabelled is a row in the same list, with a hollow ring where the others carry their colour. Match any / all still appears only once two labels are included. Clear is absent, not disabled, when nothing is filtered. Manage labels sits at the bottom below a separator. The whole root row is gone when no host has a label — unless a filter is still active, since the merged catalog only counts online hosts and losing one would otherwise strand a filter that is still hiding workspaces. The page is titled Labels, so its rows stop repeating the word: "Match any", "Match all", "Clear filter". The two Match rows sit directly under the list they qualify and only appear once two labels are included, so the subject is already on screen. All nine locales move together, translated rather than transliterated — Arabic keeps its noun, where "أي" is a determiner that needs one to stay grammatical. SidebarLabelFilter collapses from four collections plus match to one Record<labelKey, "include" | "exclude"> plus match: a tri-state row is one value per label, so include and exclude can no longer disagree about the same name. Unlabelled keys off the empty string, which normalizeWorkspaceLabelName makes unreachable for a real label. toggleSidebarLabelSelection, countSidebarLabelSelections, sidebarLabelSelectionIncludes and deduplicateSidebarLabelNames go with the old shape; setLabelFilter becomes cycleLabelFilter and setLabelMatch, so the store owns its own transitions. No migration: labels are unreleased, so the old persisted shape never shipped, and migrateSidebarViewState already resets on a safeParse failure. The e2e manager step asserted "Red label color" on a swatch that Phase 1 renamed to "Red"; the selector follows the code, and the accessibility.color key behind the old string is deleted from all nine locales as unreferenced. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): assign labels on the menu's own rails The assign picker was a search box, a create row that unfolded an inline form under it, and hand-rolled label rows whose 44x44 checkbox pushed every dot and name off the rail the rows above them sat on. Nothing lined up with anything. Rows are `MenuItem` now: the colour dot in the 16pt leading slot, assignment as the trailing check the engine already draws for a chosen row. Using the engine's row is the alignment fix; there is no second row implementation to keep in step. Every press toggles and the menu stays open — assigning labels is multi-select, so the dual-target model (`source: "row" | "checkbox"`, `shouldCloseWorkspaceLabelPicker`) is gone along with the model's `close` dependency. You leave by dismissing the menu. Search is deleted. Nobody has enough labels to search, and the field was the thing pushing the list down the surface. `workspaceLabels.search` and `createNamed` go with it across all nine locales; `clearSearch` stays because the manager modal still owns a search field (Phase 4's copy, untouched here). `Create label` moves below a separator at the bottom and opens a page that replaces the list: a name field, the swatch row, one commit row. Picking a colour picks a colour — it no longer creates the label. A failed create keeps the draft and shows the error on the page; a successful one creates, assigns, and returns to the list with the row ticked. Both menus share one page definition. `SidebarWorkspaceMenu` and `SidebarWorkspaceContextMenu` each built the same `pages` memo; they now call `useWorkspaceLabelMenuPages`. Menu engine, all of it forced by putting a text input on a menu page: - `MenuTextField` — the form kit's `FormTextInput` carries a screen's geometry and lands its text 4pt inside the labels above it. This one is drawn as a row's own fill, so the box and the text sit on the row's two rails at once. - `MenuPageDefinition.hoverIntent` — a page you type into is not one the pointer opens or dismisses. Without it the create flyout opened on a pointer passing over the row, and threw the draft away when the mouse drifted off it while the hands were on the keyboard. - The sheet keeps `keyboardBehavior="interactive"`, not `extend`. With `enableDynamicSizing` the largest snap point is the content's own height, so a short page did not grow and the keyboard came up over the field. Caught on an iOS simulator, fixed, and re-shot. - `MenuItem` announces `selected` as `aria-checked`. The deleted checkbox said so; a check drawn and never announced would have been an accessibility regression. Tri-state rows (the filter page's exclude) still cannot announce themselves — menu-wide, still Phase 5's. `sidebar-workspace-row.tsx`'s `isTouchPlatform = platformIsNative || isCompact` is kept deliberately. On a compact web viewport there is no hover, so the row's trailing actions and kebab were unreachable and the labels menu with them; this is the documented `isHovered || isNative || isCompact` shape from CLAUDE.md. Evidence: /tmp/labels-p3-{wide,compact}-{light,dark}-{list,create}.png, the create page on iOS with the keyboard up (/tmp/labels-p3-native-compact-keyboard.png) and the clipped sheet it replaced (-before.png). `packages/app/e2e/browser/workspace-labels.spec.ts` rewritten for the new model, daemon-backed failure coverage kept for both the toggle and the create path, green with the menu unit suites. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): edit a label in one operation `workspace.label.rename.request` and `workspace.label.recolor.request` become one `workspace.label.update.request { name, newName?, color? }`. Both old RPCs are deleted outright — no COMPAT shim, because the labels feature is unreleased and neither name has ever been on a wire a user owns. The `workspaceLabels` capability flag stays; that one is how a new app detects an old daemon. Two calls could land half-applied, which left the catalog in a state nobody asked for and the UI with nothing true to say. One catalog commit now applies the name, the colour, or both. Renaming onto a name the host already has is rejected rather than merged, and the check runs inside the planner so a refused collision applies neither field. A label keeping its own key is not colliding with itself, so case-only edits pass. The manager modal follows: - The host selector is `HostFilter`, the pill History and Schedules use. `includeAllHost` is now a prop: a surface that edits one host's catalog cannot carry out "all". - Search moves to the sheet header, where it spans the header's own rails. As a body control it capped at the component's 420pt and left a hole on the right whenever a host filter wasn't beside it. `workspaceLabels.clearSearch` goes with the box that had a clear button. - A row no longer opens an editor. It carries a pencil in its trailing slot, hover-revealed on web and always visible on native and compact, on the canonical plain-View hover pattern with the press on an inner Pressable. - The edit view is a name field, the swatch row, and one Save that issues one update. Delete keeps its confirm and its affected-workspace count, and moves to `outline` — red belongs to the dialog, not to the surface that opens it. - Copy says edit, never rename or recolor, in all nine locales. `workspaceLabelErrorMessage` drops the `requestType=… code=…` the daemon client appends to every RPC failure. That is what a log wants, not what a user reads under a name field. `workspaceLabels.accessibility.color` stays deleted: the swatch's accessible name is already the colour's name, so nothing needed it back. Co-authored-by: Christoph Leiter <christoph@leiter.io> * chore(app): converge the labels phases The sweep at the end of the labels redesign: the three items earlier phases found and kept out of their own commits, one cross-phase pass, and the docs. `AdaptiveModalSheet`'s close button reaches its target with `hitSlop` instead of `padding`, which had inset its glyph 8pt from the rail the title, the search field and every trailing glyph below it sit on. This is shared chrome, so it moves the close glyph in every sheet in the app; measured on four of them across both paths — the labels manager desktop and compact, the shortcuts dialog, the rename sheet — and each one's trailing rail now agrees with its leading one. `MenuItem` takes `selected: boolean | "mixed"`. A tri-state row was silent to a screen reader: the check announced itself after the last phase, but exclude had no state at all, so an excluded label was indistinguishable from an untouched one. `"mixed"` is ARIA's own third state, the engine still draws the check only for `true`, and the row keeps supplying its own trailing mark — nothing in the menu engine learns what a label is. CLAUDE.md's platform-gating rules said "NEVER use `onPointerEnter`/ `onPointerLeave`", which is the opposite of what `docs/hover.md` and `constants/platform.ts` both mandate. It now names the real rule — hover on a plain `View`, press on a separate inner `Pressable`, and no `onHoverIn`/ `onHoverOut` on a `Pressable` that contains other pressables — and links the doc that owns it rather than restating it. The cross-phase pass turned up one real defect: the replica cache never learned about labels. `StoredWorkspaceSchema` had no `labels`, so a restored workspace came back unlabelled, and since the directory cursor is current on reconnect the daemon has nothing newer to send — the chips stayed gone for the whole session and the filter matched nothing. An optional field, a serializer line, and an assertion in the restore spec. Everything else measured onto its rails already. Left alone and logged: the per-row `Map` in `useWorkspaceLabelDefinitions`, and the display menu closing when the sidebar swaps to its empty state, which is confined to that swap and predates every phase here. `docs/architecture.md` and `docs/data-model.md` described rename and recolor; they now describe the one update commit, which fields it rewrites, and the per-host resolution a row's chips use. `docs/menus.md` documents the third item state where it already documents the other two. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): split the labels filter into include and exclude controls The Labels page rotated one row through off -> include -> exclude -> off, which billed the rare operation to the common one: clearing an include cost two presses and routed through exclude, so the sidebar visibly re-filtered into a state nobody asked for on the way out. The row now owns include and a control on its trailing rail owns exclude. Each is one press each way and neither is reached through the other. `cycleLabelFilter` becomes `toggleLabelInclude` and `toggleLabelExclude`; the store's map shape is unchanged, and setting either state clears the other for that label, so including an excluded row is still one press. A row is never both, so the trailing rail holds exactly one 16pt glyph in every state: the check, a permanent `X`, or the same `X` revealed on hover. `X` rather than a circle-slash for the reason Phase 2 settled it — `Ban` fills its box where the check does not, and the column twitches. The control fades rather than mounts and takes its 44pt target from `hitSlop`, so neither revealing it nor changing state moves anything: measured 453-469 on every row, wide and compact. The control carries a tooltip, gated to web where hover exists. It portals into the overlay root above the flyout with no backdrop of its own, and resting on it does not read as the pointer leaving the surface, so the menu stays up. Accessibility: the control is a `checkbox` named "Exclude {label}" with its own checked state, and it stays in the tree at opacity 0 on rows that have no visible mark, so it is reachable without hover. Keyboard focus reveals it, so tabbing to it never means pressing something invisible. The row keeps Phase 5's `selected="mixed"` for exclude. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): put include and exclude on their own rails on every label row Phase 6 gave the row include and its trailing rail a hover-revealed exclude. The rotation was gone but the awkwardness was not: `X` is the glyph this app dismisses things with, so on a filter row it reads as delete rather than exclude, and one rail with a changing occupant still means the row's trailing area keeps changing identity as you read down the column. Both controls are now on every row in fixed positions, and state is which one is lit. Nothing appears, disappears, swaps places or shifts: the check box is 429-445 and the ban box 453-469 on an included row, an excluded row and an untouched one alike, wide and compact. Because the ban is always there, include -> exclude is one press instead of two. The row still includes, and the check is that same action named. `Ban` rather than `X`. Phase 2 rejected it because its ink fills its 16pt box where a check's does not, so the column twitched when the two swapped on one rail. Nothing swaps here, so what is left is a weight difference: the ban is drawn at 14 inside a 16pt box, which is the visible optical correction available before it stops being a glyph. The box is what the rail is measured from and it stays 16. Lit is `foreground`, unlit `foregroundExtraMuted`. Not `foregroundMuted`, which is the obvious choice and is wrong here specifically: the Match rows further down the same page carry `MenuItem`'s own check in that colour to mean *selected*, so an unlit check in the same ink would say the opposite thing eight pixels above it. Excluded keeps Phase 2's dimmed row. Accessibility: the row keeps `selected` — `true` include, `"mixed"` exclude — and the two buttons carry no checked state of their own, so a label's state is announced once and its two controls read as the actions they are. Both buttons take a tooltip, gated to web with `enabledOnDesktop={isWeb}`, because on non-compact native `TooltipContent` falls back to a `Modal` with its own backdrop. Deleted with it: the `isHovered || isFocused || isNative || isCompact` reveal, the `opacity: 0` mounting, the hover-tracking `View` and the focus plumbing that existed to keep an invisible control reachable. Net -7 lines of code on this row, all of the growth in comments. The store is unchanged; both buttons and the row press map onto `toggleLabelInclude` / `toggleLabelExclude` as they are. The e2e's hover-reveal and `toHaveCount(0)` steps encoded the deleted behaviour and are replaced by a rail measurement taken in every state — relative to the row, since filtering moves the anchor the menu hangs off, and behind a settle poll, since the flyout scales in and a box read mid-animation is not a rail. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): reveal the label filter's controls, and stop the empty state closing the menu Phase 7 put the check and the ban on every label row at all times, which made a resting list of labels read as two columns of buttons with a filter somewhere behind them. A row now shows its own state and nothing else — a marked row its mark, an untouched row nothing — and hovering any row brings both controls up, the mark you have lit and the other one unlit beside it. So the column you read down is the filter, and the row you are pointing at is the one offering to change it. Nothing about the rails changed. The two boxes are laid out in every state and only their ink comes and goes: check 429-445, ban 453-469 on an included row, an excluded row, an untouched one, hovered or not, wide and compact. The reveal is opacity, never mounting, so it cannot move the row out from under the pointer that fired it. Hover is tracked on a plain `View` wrapping the `MenuItem` with the presses on the buttons inside it, since `MenuItem` is itself a `Pressable` (docs/hover.md); each control counts its own keyboard focus as revealed, so tabbing to one never means pressing something invisible; and `pointerEvents` follows visibility so a hidden control cannot be clicked. On native and compact there is no pointer, so both simply stand there. Everything Phase 7 settled is kept: `Ban` at 14 in a 16pt box, `foregroundExtraMuted` for unlit against `foregroundMuted` meaning *selected* eight pixels below, `spacing[2]` between the two with `hitSlop` tiling the gap, the row owning `aria-checked` while the buttons stay plain named actions, and both tooltips gated `enabledOnDesktop={isWeb}`. --- Excluding a label that empties the sidebar closed the display menu. Logged since Phase 2, narrowed in Phase 5 to the empty-state swap, owned by nobody. `SidebarLabelFilterEmptyState` was a component rendered *instead of* the list, with its own container and scroll view, and both branches were handed the same header element. Different subtrees, so React unmounted the header on the swap — and the header holds the display menu's trigger, so the popover went with its anchor. The empty state is now a body: both list modes render it where their groups would go, inside the scroll they already own and below the header they already render, and the swap can no longer reach anything above it. Cleared from that same still-open page, the way back out is the swap in reverse and it holds too. `ProjectModeList` was already the right shape — its "no projects yet" body sits below the header — but its header was conditional on `unpinnedProjects.length > 0 || hasActiveHostFilter`, which is the same bug written as a guard: keep the header when there is a filter to undo. The label filter joins that condition rather than getting a second mechanism. Both empty states move into `sidebar/empty-states.tsx` so the two list modes can render either without importing each other, which also leaves the card's geometry defined once instead of once per body. Still conditional, and left alone: a sidebar with no projects and no filter has no header at all, so clearing the last host filter on an empty sidebar closes the menu the same way. That one is the onboarding empty state, where a section heading over "Add a project" would be a heading over nothing, and no menu is reachable in the state it lands in. The e2e gets the step that would have caught this: exclude into the empty state, assert the page you pressed on is still up, clear from it, assert the same. Verified by reverting both list files to Phase 7 and watching it fail on "element(s) not found". Phase 7's rail assertions are kept as they are — offsets within the row, behind a settle poll — and joined by computed-opacity reads for the four states of the reveal. Evidence: /tmp/labels-qa-p8/, 28 shots plus INDEX.md — the resting list, a hovered row, an included row with the pointer away, and the empty state with the menu still open over it, at wide and compact in both schemes. Co-authored-by: Christoph Leiter <christoph@leiter.io> * feat(app): make the label filter include-only Phases 2, 6, 7 and 8 each tried to give exclusion a comfortable shape inside a 232pt menu row: tri-state rotation, a hover-revealed control, two permanent buttons, two hover-or-lit buttons. Every fix bought a new awkwardness, because two controls in one menu row is a lot of machinery for a filter. The label filter is now the host filter — one tap in, one tap out — and nobody has ever complained about that one. A row is a plain `MenuItem` with the colour dot leading and the engine's own check trailing. Nothing else. Which makes the alignment law trivial to satisfy, and that is the point. Deleted rather than disabled, since 6, 7 and 8 are in the branch history if exclusion ever comes back: both trailing controls, the hover-tracking `View`, the `opacity: 0` mounting, the focus/blur plumbing, the `pointerEvents` gating, both tooltips and their `enabledOnDesktop={isWeb}` gates, the lit/unlit token pair, `Ban`, and the two rails they held. `SidebarLabelFilter` collapses onto the shape of `hostFilters` sitting three fields above it in the same store: `labels: string[]` plus `match`, one `toggleLabelFilter`, one `clearLabelFilter`. The exclude arm of `filterWorkspacesByLabels` goes with it, and `filter.include` / `includeLabel` / `exclude` / `excludeLabel` leave all nine locales. Persisted version 4 with no migration — the record shape never shipped, and `createValidatedPersistStorage` drops state that fails the current schema before `migrate` ever runs. Two things this leaves unused, both deleted: - `MenuItem`'s `selected: boolean | "mixed"`, added in Phase 5 so a tri-state row could announce itself. The filter row was the only caller, so the union collapses back to `boolean` and the two `selected === true` guards that existed for `"mixed"` go back to plain truthiness. - The `docs/menus.md` paragraphs from Phases 7 and 8 on hover-revealing a pressable trailing control inside a menu row. No caller does that any more, so the doc was describing a pattern the codebase no longer has. Kept: `SIDEBAR_UNLABELLED_LABEL_KEY`, because `Unlabelled` is still a row and now includes like any other; `Match any` / `Match all`, because multi-select include still means two different things. Phase 8's empty-state fix is untouched and still holds — `hasActiveSidebarLabelFilter` is what the header's survival condition reads, and it answers the same question against the new shape. Evidence: /tmp/labels-qa-p9/, 12 shots plus INDEX.md — resting, two selected, and the empty state with the menu still open over it, at wide and compact in both schemes. Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): give label-grouped rows their project icons, and label groups a tag Grouping the sidebar by label rendered every workspace row with its placeholder initial. The same workspace under status or project grouping showed its real project icon. The components were reused; the data feeding them was not. One component served both grouping modes and threaded the icon map through correctly. Fourteen lines above it, a second `useProjectIcons` call was gated on `groupMode === "status"`, written when status was the only mode that put icons on rows. Phase 2 extended the render branch to `status || label` and left the gate alone. Nothing forces a render branch and the data it renders to agree, and these two were far enough apart to drift in silence. Reuse buys you one implementation of the drawing; it buys you nothing about who decides what to fetch. So the fix is the shape, not the line. There is now one `useProjectIcons` call for every mode, and its targets come from `buildSidebarProjection` — the same function that produced the rows, from the same `projects` list. A mode can only render a row for a project the projection already knows about, so the map cannot disagree with the screen. The gate is gone rather than extended: extending it would leave the identical trap for the next grouping mode. `useProjectIcons` dedupes by `serverId:projectId` and caches through `useQueries`, so the gate was a premature optimization that bought a bug. Pinned by `sidebar-projection.test.ts`: for each of the three modes, every project view key the projection will render — pinned rows, grouped rows, project headers and the rows under them — has an icon target. Red on the old gate: restoring `groupMode === "status" ? … : []` in its new home fails project and label grouping with `['project', 'other-project']` uncovered, and status passes, which is exactly the bug's signature. The sibling sweep over everything `SidebarStatusModeWrapper` is handed found one more of the same shape, correct today: the projection chose its shortcut sections with `status || label`, so a fourth mode would have silently walked the project rows. Both the groups and the sections now come from one exhaustive `switch` on the mode, which makes the next mode a compile error rather than an empty list. `workspaceEntriesByKey` is already written the safe way round (`!== "project"`), and the rest — host badges, pinning support, the empty-filter state — never read the mode at all. Two names moved with the shape: `SidebarStatusModeWrapper` is `SidebarGroupedModeList`, and its branch is `groupMode !== "project"`, so a new mode lands in the grouped list instead of the project list's `else`. Also: label group headers draw lucide `Tag` instead of `CircleDot`, in the label's colour, muted for `Unlabelled` — the glyph the Labels menu and the workspace menu already use for this feature. It keeps `StatusGroupIcon`'s 14 with no optical nudge, and that is measured rather than assumed: in the header's leading slot both ink a 14x14 box at the same offset, and the tag lays down 76 ink pixels to the circle's 84, so reaching the corners of its box costs it no weight beside the status glyphs. QA: /tmp/labels-qa-p10/ — wide and compact, light and dark, label grouping and the same workspaces under status grouping, plus the hover chevron swap, the glyph zooms, and a before/after pair taken with the fix reverted to the bug's shape. Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): align label chip typography Co-authored-by: Christoph Leiter <christoph@leiter.io> * docs: format architecture module table Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): narrow workspace labels workflow Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): mirror bottom sheet test exports Co-authored-by: Christoph Leiter <christoph@leiter.io> * test(desktop): assert project browse by destination Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): retain empty projects without label filters Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): preserve status grouping test contract Co-authored-by: Christoph Leiter <christoph@leiter.io> * fix(app): retain filters after label load failures Co-authored-by: Christoph Leiter <christoph@leiter.io> --------- Co-authored-by: Christoph Leiter <christoph@leiter.io>
2631 lines
92 KiB
TypeScript
2631 lines
92 KiB
TypeScript
export const en = {
|
|
common: {
|
|
back: "Back",
|
|
loading: "Loading...",
|
|
actions: {
|
|
back: "Back",
|
|
cancel: "Cancel",
|
|
close: "Close",
|
|
copy: "Copy",
|
|
dismiss: "Dismiss",
|
|
retry: "Retry",
|
|
search: "Search",
|
|
select: "Select",
|
|
},
|
|
placeholders: {
|
|
search: "Search...",
|
|
},
|
|
empty: {
|
|
noResults: "No results found",
|
|
noOptionsMatchSearch: "No options match your search.",
|
|
},
|
|
states: {
|
|
loading: "Loading...",
|
|
starting: "Starting...",
|
|
copied: "Copied",
|
|
copiedLabel: "Copied {{label}}",
|
|
downloadComplete: "Download complete",
|
|
downloadFailed: "Download failed",
|
|
},
|
|
errors: {
|
|
error: "Error",
|
|
unableToSave: "Unable to save",
|
|
nameRequired: "Name is required",
|
|
daemonUnavailable: "Daemon unavailable",
|
|
daemonClientUnavailable: "Daemon client unavailable",
|
|
daemonClientDisconnected: "Daemon client is disconnected",
|
|
noFileFound: "No file found for {{token}}",
|
|
unexpectedDictationError: "An unexpected error occurred while handling dictation.",
|
|
},
|
|
connectionStatus: {
|
|
online: "Online",
|
|
connecting: "Connecting",
|
|
offline: "Offline",
|
|
error: "Error",
|
|
idle: "Idle",
|
|
},
|
|
},
|
|
shell: {
|
|
menu: {
|
|
toggleSidebar: "Toggle sidebar",
|
|
open: "Open menu",
|
|
close: "Close menu",
|
|
},
|
|
commandCenter: {
|
|
placeholder: "Search commands, files, workspaces, and agents...",
|
|
filePlaceholder: "Search files...",
|
|
searchingFiles: "Searching files...",
|
|
noMatches: "No matches",
|
|
actions: "Actions",
|
|
files: "Files",
|
|
workspaces: "Workspaces",
|
|
agents: "Agents",
|
|
newAgent: "New agent",
|
|
addProject: "Add project",
|
|
home: "Home",
|
|
groupByProject: "Group by project",
|
|
groupByStatus: "Group by status",
|
|
modelGroupLabel: "Model",
|
|
modelSearchKeywords: "switch model change model set model select model",
|
|
thinkingGroupLabel: "Thinking",
|
|
thinkingSearchKeywords: "reasoning effort thinking think",
|
|
modeGroupLabel: "Mode",
|
|
modeSearchKeywords: "access permission approval mode",
|
|
planModeGroupLabel: "Plan mode",
|
|
planModeSearchKeywords: "plan planning plan mode",
|
|
fastModeGroupLabel: "Fast",
|
|
fastModeSearchKeywords: "fast speed low latency",
|
|
settingOn: "On",
|
|
settingOff: "Off",
|
|
},
|
|
},
|
|
composer: {
|
|
placeholders: {
|
|
desktop: "Message the agent, tag @files, or use /commands and /skills",
|
|
mobile: "Message, @files, /commands",
|
|
fallback: "Message...",
|
|
terminal: "Prompt",
|
|
},
|
|
input: {
|
|
accessibilityLabel: "Message agent...",
|
|
terminalAccessibilityLabel: "Terminal prompt",
|
|
focusHint: "{{shortcut}} to focus",
|
|
addAttachment: "Add attachment",
|
|
interruptAgent: "Interrupt agent",
|
|
queueMessage: "Queue message",
|
|
sendAndInterrupt: "Send and interrupt",
|
|
sendAndSteer: "Send and steer",
|
|
sendMessage: "Send message",
|
|
queue: "Queue",
|
|
send: "Send",
|
|
},
|
|
cancel: {
|
|
cancelingAgent: "Canceling agent",
|
|
stopAgent: "Stop agent",
|
|
interrupt: "Interrupt",
|
|
},
|
|
voice: {
|
|
enableVoiceMode: "Enable Voice mode",
|
|
voiceMode: "Voice mode",
|
|
unmuteVoiceMode: "Unmute Voice mode",
|
|
muteVoiceMode: "Mute Voice mode",
|
|
stopDictation: "Stop dictation",
|
|
startDictation: "Start dictation",
|
|
unmuteVoice: "Unmute voice",
|
|
muteVoice: "Mute voice",
|
|
dictation: "Dictation",
|
|
interruptBeforeVoice: "Interrupt the agent before starting voice mode",
|
|
},
|
|
attachments: {
|
|
addImage: "Add image",
|
|
pasteImage: "Paste image",
|
|
addFile: "Upload file",
|
|
addIssueOrPr: "Add issue or PR",
|
|
addIssueOrPr_mr: "Add issue or MR",
|
|
dropImagesHere: "Drop images here",
|
|
dropFilesHere: "Drop files here",
|
|
editQueuedMessage: "Edit queued message",
|
|
sendQueuedMessageNow: "Send queued message now",
|
|
openImage: "Open image attachment",
|
|
removeImage: "Remove image attachment",
|
|
removeFile: "Remove file attachment",
|
|
openGithub: "Open {{kind}} {{number}}",
|
|
removeGithub: "Remove {{kind}} {{number}}",
|
|
element: "Element",
|
|
openBrowserElement: "Open browser element attachment",
|
|
removeBrowserElement: "Remove browser element attachment",
|
|
openReview: "Open review attachment",
|
|
removeReview: "Remove review attachment",
|
|
},
|
|
errors: {
|
|
failedToSend: "Failed to send message",
|
|
failedToCreateAgent: "Failed to create agent",
|
|
noHostSelected: "No host selected",
|
|
initialPromptRequired: "Initial prompt is required",
|
|
alreadyLoading: "Already loading",
|
|
uploadFailed: "Failed to upload file",
|
|
noClipboardImage: "No image in clipboard",
|
|
pasteImageFailed: "Failed to paste image",
|
|
fileTooLarge: "{{fileName}} is too large (max {{size}})",
|
|
},
|
|
clientCommands: {
|
|
archiveAgent: "Archive the current agent",
|
|
freshDraft: "Archive this agent and start a fresh draft",
|
|
},
|
|
github: {
|
|
searching: "Searching...",
|
|
noResults: "No results found.",
|
|
searchPlaceholder: "Search issues and PRs...",
|
|
searchPlaceholder_mr: "Search issues and MRs...",
|
|
title: "Attach issue or PR",
|
|
title_mr: "Attach issue or MR",
|
|
},
|
|
},
|
|
agentControls: {
|
|
provider: {
|
|
fallback: "Provider",
|
|
select: "Select agent provider",
|
|
},
|
|
thinking: {
|
|
title: "Thinking",
|
|
unknown: "Unknown",
|
|
extraHigh: "Extra high",
|
|
select: "Select thinking option",
|
|
selectWithValue: "Select thinking option ({{value}})",
|
|
},
|
|
model: {
|
|
unknown: "Unknown model",
|
|
},
|
|
features: {
|
|
title: "Features",
|
|
open: "Open agent features",
|
|
on: "On",
|
|
off: "Off",
|
|
},
|
|
mode: {
|
|
title: "Mode",
|
|
searchPlaceholder: "Search modes...",
|
|
selectWithValue: "Select agent mode ({{value}})",
|
|
},
|
|
hints: {
|
|
thinking: "Thinking mode",
|
|
model: "Change model",
|
|
mode: "Change mode",
|
|
},
|
|
},
|
|
agentStream: {
|
|
empty: "Start chatting with this agent...",
|
|
scrollToBottom: "Scroll to bottom",
|
|
historyLoadFailed: "Couldn't load agent history",
|
|
permission: {
|
|
plan: "Plan",
|
|
required: "Permission Required",
|
|
deny: "Deny",
|
|
accept: "Accept",
|
|
implement: "Implement",
|
|
question: "How would you like to proceed?",
|
|
proposedPlan: "Proposed plan",
|
|
},
|
|
},
|
|
agentPanel: {
|
|
states: {
|
|
notFound: "Agent not found",
|
|
failedToLoad: "Failed to load agent",
|
|
reconnecting: "Reconnecting",
|
|
timelineSyncFailed: "Couldn't refresh agent history.",
|
|
timelineSyncRetrying: "Retrying…",
|
|
archivingTitle: "Archiving agent...",
|
|
archivingSubtitle: "Please wait while we archive this agent.",
|
|
},
|
|
unavailable: {
|
|
selectedHost: "Selected host",
|
|
unknownHost:
|
|
"Cannot open this agent because {{serverLabel}} is not configured on this device.",
|
|
addHost: "Add the host in Settings or open an agent on a configured server to continue.",
|
|
preparingSession: "Preparing {{serverLabel}} session...",
|
|
connecting: "Connecting to {{serverLabel}}...",
|
|
showSoon: "We will show this agent in a moment.",
|
|
showWhenOnline: "We will show this agent once the host is online.",
|
|
reconnectingTo: "Reconnecting to {{serverLabel}}...",
|
|
showAgainWhenReachable: "We will show this agent again as soon as the host is reachable.",
|
|
},
|
|
archived: {
|
|
callout: "This agent is archived",
|
|
unarchive: "Unarchive",
|
|
},
|
|
},
|
|
sessions: {
|
|
title: "History",
|
|
empty: "No sessions yet",
|
|
noMatches: "No sessions match",
|
|
tooManyMatches: "Too many matches — narrow your search",
|
|
hostLoadFailed: "{{host}}: Could not load history",
|
|
searchPlaceholder: "Search history",
|
|
actions: {
|
|
loadMore: "Load more",
|
|
clearSearch: "Clear search",
|
|
},
|
|
},
|
|
agentList: {
|
|
fallbackTitle: "New session",
|
|
dateSections: {
|
|
recent: "Recent",
|
|
today: "Today",
|
|
yesterday: "Yesterday",
|
|
thisWeek: "This week",
|
|
thisMonth: "This month",
|
|
older: "Older",
|
|
},
|
|
status: {
|
|
initializing: "Starting",
|
|
idle: "Idle",
|
|
running: "Running",
|
|
error: "Error",
|
|
closed: "Closed",
|
|
},
|
|
badges: {
|
|
archived: "Archived",
|
|
pending: "{{count}} pending",
|
|
attention: "Attention",
|
|
},
|
|
archiveSheet: {
|
|
hostOffline: "Host offline",
|
|
runningAgent: "This agent is still running. Archiving it will stop the agent.",
|
|
archive: "Archive",
|
|
},
|
|
},
|
|
message: {
|
|
diagram: {
|
|
diagram: "Diagram",
|
|
zoomIn: "Zoom in",
|
|
zoomOut: "Zoom out",
|
|
resetZoom: "Reset view",
|
|
viewSource: "View source",
|
|
viewDiagram: "View diagram",
|
|
},
|
|
actions: {
|
|
copyCode: "Copy code",
|
|
copyTurn: "Copy turn",
|
|
copyMessage: "Copy message",
|
|
forkMenu: "Fork chat from here",
|
|
forkInNewTab: "Fork in a new tab",
|
|
forkInNewWorkspace: "Fork in a new workspace",
|
|
forkUnavailable: "Update the host to use this.",
|
|
forkMissingWorkspace: "This agent is not in a workspace.",
|
|
forkFailed: "Failed to fork chat",
|
|
openFile: "Open file",
|
|
copied: "Copied",
|
|
},
|
|
attachments: {
|
|
dismissImage: "Dismiss image",
|
|
closeImage: "Close image",
|
|
imageLoadFailed: "Couldn't load image",
|
|
imageUnavailable: "Image unavailable",
|
|
imagePreviewUnavailable: "Image preview unavailable.",
|
|
imagePreviewLoadFailed: "Unable to load image preview.",
|
|
review: "Review",
|
|
commentsOne: "1 comment",
|
|
commentsMany: "{{count}} comments",
|
|
textAttachment: "Text attachment",
|
|
text: "Text",
|
|
file: "File",
|
|
},
|
|
speak: {
|
|
header: "Spoke",
|
|
},
|
|
activity: {
|
|
details: "Details",
|
|
},
|
|
dictation: {
|
|
start: "Start voice dictation",
|
|
cancel: "Cancel dictation",
|
|
retry: "Retry dictation",
|
|
insert: "Insert transcription",
|
|
insertAndSend: "Insert transcription and send",
|
|
failed: "Dictation failed: {{error}}",
|
|
failedRetry: "Dictation failed. Tap retry.",
|
|
},
|
|
question: {
|
|
submit: "Submit",
|
|
next: "Next",
|
|
answerPlaceholder: "Type your answer...",
|
|
otherPlaceholder: "Other...",
|
|
},
|
|
todo: {
|
|
title: "Tasks",
|
|
empty: "No tasks yet.",
|
|
tasksProgress: "{{completed}}/{{total}} tasks",
|
|
activity: {
|
|
created: "Created {{count}} tasks",
|
|
added: "Added",
|
|
started: "Started",
|
|
completed: "Completed",
|
|
reopened: "Reopened",
|
|
},
|
|
},
|
|
compaction: {
|
|
loading: "Compacting...",
|
|
auto: "Context automatically compacted",
|
|
manual: "Context manually compacted",
|
|
withTokens: "Context compacted ({{tokens}}K tokens)",
|
|
completed: "Context compacted",
|
|
},
|
|
},
|
|
importSession: {
|
|
title: "Import session",
|
|
filters: {
|
|
all: "All",
|
|
},
|
|
status: {
|
|
connectHost: "Connect to a host to import sessions",
|
|
updateHost: "Update the host to import sessions.",
|
|
noProviders: "No importable providers are enabled.",
|
|
loading: "Loading recent sessions...",
|
|
failedAll: "Could not load recent sessions.",
|
|
failedProviders: "Could not load sessions for {{providers}}.",
|
|
failedImport: "Could not import selected session.",
|
|
},
|
|
actions: {
|
|
refresh: "Refresh sessions",
|
|
},
|
|
preview: {
|
|
untitledSession: "Untitled session",
|
|
noPrompt: "No prompt preview",
|
|
},
|
|
empty: {
|
|
noRecent: "No recent sessions to import.",
|
|
alreadyImported: "All recent sessions are already imported.",
|
|
noProviderSessions: "No {{provider}} sessions found.",
|
|
},
|
|
row: {
|
|
importing: "Importing...",
|
|
},
|
|
},
|
|
workspace: {
|
|
route: {
|
|
loading: "Loading workspace",
|
|
connecting: "Connecting",
|
|
hostOffline: "{{hostName}} is offline",
|
|
cannotReachHost: "Cannot reach {{hostName}}",
|
|
hostStatus: "Host status: {{status}}",
|
|
needsHostUpgrade: "Update your host to restore this workspace",
|
|
manageHost: "Manage host",
|
|
recovery: {
|
|
archivedTitle: "Workspace archived",
|
|
restoreDescription:
|
|
"{{workspaceName}} was archived and its worktree was removed. Restore branch {{branch}} to open it again.",
|
|
unarchiveDescription: "{{workspaceName}} is archived. Unarchive it to open it again.",
|
|
restoreAction: "Restore",
|
|
unarchiveAction: "Unarchive",
|
|
restoringTitle: "Restoring workspace",
|
|
restoringAction: "Restoring...",
|
|
unavailableTitle: "Workspace unavailable",
|
|
checkFailedTitle: "Couldn't check workspace",
|
|
},
|
|
},
|
|
hoverCard: {
|
|
scriptsAccessibility: "Workspace scripts",
|
|
copyPath: "Copy path",
|
|
copyBranchName: "Copy branch name",
|
|
copied: "Copied",
|
|
},
|
|
fileActions: {
|
|
openFile: "Open file",
|
|
copyPath: "Copy path",
|
|
copyRelativePath: "Copy relative path",
|
|
revealIn: "Reveal in {{target}}",
|
|
download: "Download",
|
|
addToChat: "Add to chat",
|
|
moreActions: "More actions",
|
|
newFile: "New file",
|
|
newFolder: "New folder",
|
|
collapseFolder: "Collapse folder",
|
|
rename: "Rename",
|
|
duplicate: "Duplicate",
|
|
revert: "Discard changes",
|
|
delete: "Delete",
|
|
confirmDelete: {
|
|
fileTitle: "Delete file?",
|
|
folderTitle: "Delete folder?",
|
|
message: '"{{name}}" will be permanently deleted.',
|
|
confirm: "Delete",
|
|
cancel: "Cancel",
|
|
},
|
|
confirmRevert: {
|
|
title: "Discard changes?",
|
|
message: 'Changes to "{{name}}" will be permanently discarded.',
|
|
confirm: "Discard",
|
|
cancel: "Cancel",
|
|
failed: "Failed to discard changes",
|
|
},
|
|
},
|
|
fileExplorer: {
|
|
sort: {
|
|
name: "Name",
|
|
modified: "Modified",
|
|
size: "Size",
|
|
},
|
|
context: {
|
|
size: "Size",
|
|
modified: "Modified",
|
|
},
|
|
actions: {
|
|
back: "Back",
|
|
retry: "Retry",
|
|
refresh: "Refresh files",
|
|
refreshing: "Refreshing files",
|
|
hideHiddenFiles: "Hide hidden files",
|
|
showHiddenFiles: "Show hidden files",
|
|
},
|
|
empty: {
|
|
noFiles: "No files",
|
|
noVisibleFiles: "No visible files",
|
|
},
|
|
states: {
|
|
unavailable: "Workspace is unavailable",
|
|
loading: "Loading files...",
|
|
},
|
|
errors: {
|
|
failedToListDirectory: "Failed to list directory",
|
|
createFailed: "Failed to create entry",
|
|
renameFailed: "Failed to rename entry",
|
|
duplicateFailed: "Failed to duplicate entry",
|
|
revealFailed: "Failed to reveal entry",
|
|
deleteFailed: "Failed to delete entry",
|
|
},
|
|
draft: {
|
|
filePlaceholder: "File name",
|
|
folderPlaceholder: "Folder name",
|
|
},
|
|
},
|
|
setup: {
|
|
descriptor: {
|
|
label: "Setup",
|
|
completed: "Setup completed",
|
|
failed: "Setup failed",
|
|
workspace: "Workspace setup",
|
|
},
|
|
status: {
|
|
running: "Running",
|
|
completed: "Completed",
|
|
failed: "Failed",
|
|
waiting: "Waiting for setup output",
|
|
},
|
|
waiting: "Setting up workspace...",
|
|
empty: {
|
|
noCommands: "No setup commands ran for this workspace.",
|
|
},
|
|
accessibility: {
|
|
noCommands: "No setup commands ran for this workspace",
|
|
log: "Workspace setup log",
|
|
},
|
|
log: {
|
|
noOutput: "No output",
|
|
},
|
|
},
|
|
browser: {
|
|
unavailable: {
|
|
title: "Browser is desktop-only",
|
|
subtitle: "Open this workspace in Electron to use the built-in browser.",
|
|
},
|
|
session: "Browser session {{browserId}}",
|
|
controls: {
|
|
back: "Back",
|
|
forward: "Forward",
|
|
stopLoading: "Stop loading",
|
|
refresh: "Refresh",
|
|
browserUrl: "Browser URL",
|
|
enterUrl: "Enter URL",
|
|
openDevTools: "Open browser dev tools",
|
|
cancelSelector: "Cancel element selector",
|
|
annotateElement: "Annotate element",
|
|
screenshotElement: "Screenshot element",
|
|
screenshotCopied: "Copied screenshot to clipboard",
|
|
elementCopied: "Copied element to clipboard",
|
|
screenshotFailed: "Couldn't copy screenshot",
|
|
selectorLoading: "Wait for the page to finish loading",
|
|
selectorFailed: "Couldn't start element selector",
|
|
},
|
|
annotate: {
|
|
title: "Annotate element",
|
|
placeholder: "Message to the agent about this element…",
|
|
submit: "Attach",
|
|
cancel: "Cancel",
|
|
},
|
|
devices: {
|
|
label: "Device size",
|
|
responsive: "Responsive",
|
|
},
|
|
errors: {
|
|
failedToLoad: "Failed to load page",
|
|
invalidUrl: "Invalid browser URL",
|
|
unsupportedProtocol: "Blocked unsupported browser URL: {{protocol}}",
|
|
},
|
|
},
|
|
terminal: {
|
|
hostDisconnected: "Host is not connected",
|
|
updateHost: "Update the host to use the native terminal renderer.",
|
|
unableToSubscribe: "Unable to subscribe to terminal",
|
|
},
|
|
tabs: {
|
|
loading: "Loading...",
|
|
modified: "Unsaved changes",
|
|
loadingAgentTitle: "Loading agent title",
|
|
emptyPane: "No tabs in this pane.",
|
|
fallback: {
|
|
newAgent: "New agent",
|
|
setup: "Setup",
|
|
workspaceSetup: "Workspace setup",
|
|
terminal: "Terminal",
|
|
browser: "Browser",
|
|
agent: "Agent",
|
|
workspace: "Workspace",
|
|
},
|
|
switcher: {
|
|
trigger: "Switch tabs ({{count}} open)",
|
|
title: "Switch tab",
|
|
searchPlaceholder: "Search tabs",
|
|
},
|
|
menu: {
|
|
openFor: "Open menu for {{label}}",
|
|
copyResumeCommand: "Copy resume command",
|
|
copyAgentId: "Copy agent id",
|
|
copyTerminalId: "Copy terminal id",
|
|
copyFilePath: "Copy file path",
|
|
rename: "Rename",
|
|
closeAbove: "Close tabs above",
|
|
closeBelow: "Close tabs below",
|
|
closeLeft: "Close to the left",
|
|
closeRight: "Close to the right",
|
|
closeOthers: "Close other tabs",
|
|
reloadAgent: "Reload agent",
|
|
reloadAgentTooltip: "Reload agent to update skills, MCPs or login status.",
|
|
close: "Close",
|
|
renameTerminal: "Rename terminal",
|
|
renameAgent: "Rename agent",
|
|
},
|
|
actions: {
|
|
newTab: "New tab",
|
|
newAgent: "New agent",
|
|
newTerminal: "New terminal",
|
|
preparingTerminal: "Preparing terminal tab",
|
|
preparingTerminalTooltip: "Preparing terminal...",
|
|
newBrowser: "New browser",
|
|
maximizePane: "Maximize pane",
|
|
restorePane: "Restore pane",
|
|
exitFocusMode: "Exit focus mode",
|
|
splitRight: "Split pane right",
|
|
splitDown: "Split pane down",
|
|
changes: "Changes",
|
|
files: "Files",
|
|
pullRequest: "Pull request",
|
|
terminalProfilesMenu: "Terminal profiles",
|
|
editTerminalProfiles: "Edit profiles…",
|
|
},
|
|
explorer: {
|
|
open: "Open explorer",
|
|
close: "Close explorer",
|
|
toggle: "Toggle explorer",
|
|
changes: "Changes",
|
|
files: "Files",
|
|
},
|
|
toasts: {
|
|
copyFailed: "Copy failed",
|
|
agentIdCopiedLabel: "Agent ID",
|
|
terminalIdCopiedLabel: "Terminal ID",
|
|
resumeCommandCopiedLabel: "resume command",
|
|
filePathCopiedLabel: "File path",
|
|
resumeIdUnavailable: "Resume ID not available",
|
|
resumeCommandUnavailable: "Resume command not available",
|
|
reloadingAgent: "Reloading agent...",
|
|
reloadedAgent: "Reloaded agent",
|
|
failedToReloadAgent: "Failed to reload agent",
|
|
failedToCloseAgent: "Failed to close agent",
|
|
},
|
|
confirmations: {
|
|
close: "Close",
|
|
cancel: "Cancel",
|
|
archive: "Archive",
|
|
unsavedTitle: "Unsaved changes",
|
|
unsavedMessage:
|
|
"This tab has changes that have not been saved. Closing it will discard the draft.",
|
|
closeWithoutSaving: "Close without saving",
|
|
closePaneTitle: "Close pane?",
|
|
bulkUnsaved: "{{count}} tab(s) have unsaved changes. Closing will discard those drafts.",
|
|
closeTerminalTitle: "Close terminal?",
|
|
closeTerminalMessage: "Any running process in this terminal will be stopped immediately.",
|
|
archiveRunningAgentTitle: "Archive running agent?",
|
|
archiveRunningAgentMessage:
|
|
"This agent is still running. Archiving it will stop the agent and close the tab.",
|
|
closeTabsLeftTitle: "Close tabs to the left?",
|
|
closeTabsRightTitle: "Close tabs to the right?",
|
|
closeOtherTabsTitle: "Close other tabs?",
|
|
bulk: {
|
|
all: "This will archive {{agents}} agent(s), close {{terminals}} terminal(s), and close {{tabs}} tab(s). Any running process in a closed terminal will be stopped immediately.",
|
|
agentsAndTerminals:
|
|
"This will archive {{agents}} agent(s) and close {{terminals}} terminal(s). Any running process in a closed terminal will be stopped immediately.",
|
|
terminalsAndTabs:
|
|
"This will close {{terminals}} terminal(s) and close {{tabs}} tab(s). Any running process in a closed terminal will be stopped immediately.",
|
|
agentsAndTabs: "This will archive {{agents}} agent(s) and close {{tabs}} tab(s).",
|
|
terminals:
|
|
"This will close {{terminals}} terminal(s). Any running process in a closed terminal will be stopped immediately.",
|
|
tabs: "This will close {{tabs}} tab(s).",
|
|
agents: "This will archive {{agents}} agent(s).",
|
|
},
|
|
},
|
|
},
|
|
header: {
|
|
actions: {
|
|
workspaceActions: "Workspace actions",
|
|
newAgent: "New agent",
|
|
newTerminal: "New terminal",
|
|
newBrowser: "New browser tab",
|
|
importSession: "Import session",
|
|
copyPath: "Copy workspace path",
|
|
copyBranchName: "Copy branch name",
|
|
showSetup: "Show setup",
|
|
},
|
|
toasts: {
|
|
workspacePathUnavailable: "Workspace path is not available yet",
|
|
branchNameUnavailable: "Branch name not available",
|
|
terminalQueued: "Preparing workspace, opening terminal when ready...",
|
|
workspacePathCopiedLabel: "Workspace path",
|
|
branchNameCopiedLabel: "Branch name",
|
|
},
|
|
},
|
|
scripts: {
|
|
title: "Scripts",
|
|
actions: {
|
|
chooseUrl: "Choose URL",
|
|
copyUrl: "Copy URL",
|
|
openService: "View service",
|
|
restart: "Restart",
|
|
run: "Run",
|
|
stop: "Stop",
|
|
view: "View terminal",
|
|
},
|
|
accessibility: {
|
|
trigger: "Workspace scripts",
|
|
openService: "View {{scriptName}} service",
|
|
viewTerminal: "View {{scriptName}} terminal",
|
|
runScript: "Run {{scriptName}} script",
|
|
stopScript: "Stop {{scriptName}}",
|
|
restartScript: "Restart {{scriptName}}",
|
|
copyUrl: "Copy {{scriptName}} URL",
|
|
chooseUrl: "Choose URL for {{scriptName}}",
|
|
script: "{{scriptName}} script",
|
|
},
|
|
routes: {
|
|
public: "Reverse proxy",
|
|
paseo: "Memorable",
|
|
direct: "Direct",
|
|
},
|
|
states: {
|
|
exitCode: "exit {{code}}",
|
|
startFailed: "Failed to start {{scriptName}}",
|
|
stopFailed: "Failed to stop {{scriptName}}",
|
|
},
|
|
},
|
|
git: {
|
|
actions: {
|
|
moreOptions: "More options",
|
|
moreActions: "More actions",
|
|
commit: {
|
|
label: "Commit",
|
|
pending: "Committing...",
|
|
success: "Committed",
|
|
},
|
|
pull: {
|
|
label: "Pull",
|
|
pending: "Pulling...",
|
|
success: "Pulled",
|
|
},
|
|
push: {
|
|
label: "Push",
|
|
pending: "Pushing...",
|
|
success: "Pushed",
|
|
},
|
|
pullAndPush: {
|
|
label: "Pull and push",
|
|
pending: "Pulling and pushing...",
|
|
success: "Pulled and pushed",
|
|
},
|
|
viewPr: "View PR",
|
|
viewPr_mr: "View MR",
|
|
createPr: {
|
|
label: "Create PR",
|
|
pending: "Creating PR...",
|
|
success: "PR Created",
|
|
label_mr: "Create MR",
|
|
pending_mr: "Creating MR...",
|
|
success_mr: "MR Created",
|
|
},
|
|
mergeBranch: {
|
|
label: "Merge locally",
|
|
pending: "Merging...",
|
|
success: "Merged",
|
|
},
|
|
mergeFromBase: {
|
|
label: "Update from {{baseRef}}",
|
|
pending: "Updating...",
|
|
success: "Updated",
|
|
},
|
|
archive: {
|
|
label: "Archive workspace",
|
|
pending: "Archiving...",
|
|
success: "Archived",
|
|
},
|
|
mergePr: {
|
|
squash: "Merge PR (squash)",
|
|
merge: "Merge PR (merge)",
|
|
rebase: "Merge PR (rebase)",
|
|
pending: "Merging PR...",
|
|
success: "PR merged",
|
|
squash_mr: "Merge MR (squash)",
|
|
merge_mr: "Merge MR (merge)",
|
|
rebase_mr: "Merge MR (rebase)",
|
|
pending_mr: "Merging MR...",
|
|
success_mr: "MR merged",
|
|
},
|
|
autoMerge: {
|
|
enableSquash: "Auto merge (squash)",
|
|
enableMerge: "Auto merge (merge)",
|
|
enableRebase: "Auto merge (rebase)",
|
|
enabled: "Auto-merge enabled",
|
|
enabling: "Enabling auto-merge...",
|
|
disabling: "Disabling auto-merge...",
|
|
disabled: "Auto-merge disabled",
|
|
},
|
|
unavailable: {
|
|
viewPrNoForge:
|
|
"View {{noun}} isn't available right now because {{brand}} isn't connected",
|
|
pullNoRemote:
|
|
"Pull isn't available here because this branch is not connected to a remote yet",
|
|
pullDirty:
|
|
"Pull isn't available while you have local changes so commit or stash them first",
|
|
pullUpToDate: "Pull isn't available because this branch is already up to date",
|
|
pushNoRemote:
|
|
"Push isn't available here because this branch is not connected to a remote yet",
|
|
pushBehind: "Push isn't available yet because there are newer changes to bring in first",
|
|
pushNothing: "Push isn't available because there is nothing new to send",
|
|
pullAndPushNoRemote:
|
|
"Pull and push isn't available here because this branch is not connected to a remote yet",
|
|
pullAndPushDirty:
|
|
"Pull and push isn't available while you have local changes so commit or stash them first",
|
|
pullAndPushNoIncoming:
|
|
"Pull and push isn't available because there are no incoming changes to pull first",
|
|
pullAndPushInSync: "Pull and push isn't available because this branch is already in sync",
|
|
pullAndPushNothingToPush:
|
|
"Pull and push isn't available because there is nothing new to send after pulling",
|
|
createPrNoForge:
|
|
"Create {{noun}} isn't available right now because {{brand}} isn't connected",
|
|
createPrNoCommits:
|
|
"Create PR isn't available because this branch doesn't have any new commits yet",
|
|
mergeNoBase: "Merge isn't available because we couldn't determine the base branch",
|
|
mergeDirty:
|
|
"Merge isn't available while you have local changes so commit or stash them first",
|
|
mergeNothing:
|
|
"Merge isn't available because this branch doesn't have anything new to merge yet",
|
|
updateNoBase: "Update isn't available because we couldn't determine the base branch",
|
|
updateDirty:
|
|
"Update isn't available while you have local changes so commit or stash them first",
|
|
updateCurrent:
|
|
"Update isn't available because this branch is already up to date with {{baseRef}}",
|
|
mergePrNoGithub: "Merge PR isn't available right now because GitHub isn't connected",
|
|
archiveNotWorktree:
|
|
"Archive isn't available here because this workspace was not created as a Paseo worktree",
|
|
mergePrNoForge:
|
|
"Merge {{noun}} isn't available right now because {{brand}} isn't connected",
|
|
mergePrMissing: "Merge PR isn't available because there isn't a pull request yet",
|
|
mergePrDraft: "Merge PR isn't available because the pull request is still a draft",
|
|
mergePrMerged: "Merge PR isn't available because the pull request is already merged",
|
|
mergePrClosed: "Merge PR isn't available because the pull request is closed",
|
|
mergePrConflicts: "Merge PR isn't available because the pull request has conflicts",
|
|
mergePrQueue: "Merge PR isn't available here because this repository uses a merge queue",
|
|
mergePrNotReady:
|
|
"Merge {{noun}} isn't available until {{brand}} reports the {{noun}} is ready to merge",
|
|
autoMergeCannotDisable: "Auto-merge is enabled, but this account can't disable it",
|
|
},
|
|
toasts: {
|
|
failedCommit: "Failed to commit",
|
|
failedPull: "Failed to pull",
|
|
failedPush: "Failed to push",
|
|
failedPullAndPush: "Failed to pull and push",
|
|
failedCreatePr: "Failed to create PR",
|
|
failedMergePr: "Failed to merge PR",
|
|
failedEnableAutoMerge: "Failed to enable auto-merge",
|
|
failedDisableAutoMerge: "Failed to disable auto-merge",
|
|
baseRefUnavailable: "Base ref unavailable",
|
|
failedMerge: "Failed to merge",
|
|
failedMergeFromBase: "Failed to merge from base",
|
|
},
|
|
archiveWarning: {
|
|
title: 'Archive "{{workspaceName}}"?',
|
|
confirm: "Archive",
|
|
cancel: "Cancel",
|
|
uncommittedChanges: "Uncommitted changes",
|
|
uncommittedChangesWithDiff: "Uncommitted changes ({{diffStat}})",
|
|
addedLine: "{{count}} added line",
|
|
addedLines: "{{count}} added lines",
|
|
deletedLine: "{{count}} deleted line",
|
|
deletedLines: "{{count}} deleted lines",
|
|
unpushedCommit: "{{count}} unpushed commit",
|
|
unpushedCommits: "{{count}} unpushed commits",
|
|
},
|
|
},
|
|
diff: {
|
|
openChangesTab: "Open Changes tab",
|
|
closeChangesTab: "Close Changes tab",
|
|
binaryFile: "Binary file",
|
|
tooLarge: "Diff too large to display",
|
|
previewTooLargeTitle: "This diff is too large to preview",
|
|
previewTooLargeDescription: "Narrow the comparison to preview it here",
|
|
unified: "Unified diff",
|
|
split: "Side-by-side diff",
|
|
switchToUnified: "Switch to unified diff",
|
|
switchToSplit: "Switch to side-by-side diff",
|
|
showTreeView: "Show folder tree",
|
|
hideTreeView: "Hide folder tree",
|
|
options: "Diff options",
|
|
hideWhitespace: "Hide whitespace",
|
|
showWhitespace: "Show whitespace",
|
|
scrollLongLines: "Scroll long lines",
|
|
wrapLongLines: "Wrap long lines",
|
|
collapseAllFolders: "Collapse all folders",
|
|
expandAllFolders: "Expand all folders",
|
|
collapseAllFiles: "Collapse all files",
|
|
expandAllFiles: "Expand all files",
|
|
refreshing: "Refreshing",
|
|
refresh: "Refresh",
|
|
refreshState: "Refresh git and {{brand}} state",
|
|
failedRefresh: "Failed to refresh git state.",
|
|
emptyHiddenWhitespace: "No visible changes after hiding whitespace",
|
|
emptyUncommitted: "No uncommitted changes",
|
|
emptyAgainstBase: "No changes vs {{baseRef}}",
|
|
checkingRepository: "Checking repository...",
|
|
notRepository: "Not a git repository",
|
|
diffMode: "Diff mode",
|
|
uncommitted: "Uncommitted",
|
|
committed: "Committed",
|
|
branchUnknown: "Unknown",
|
|
base: "base",
|
|
newFile: "New",
|
|
deletedFile: "Deleted",
|
|
modifiedFile: "Modified",
|
|
commits: {
|
|
title: "Commits",
|
|
countLabel: "{{count}} workspace commits",
|
|
noneAhead: "No commits ahead of {{baseRef}} yet",
|
|
fileDiffEmpty: "No changes to display",
|
|
fileDiffError: "Failed to load file diff",
|
|
loading: "Loading commits…",
|
|
loadError: "Failed to load commits",
|
|
empty: "No commits yet",
|
|
},
|
|
},
|
|
openInEditor: {
|
|
open: "Open",
|
|
chooseEditor: "Choose editor",
|
|
openIn: "Open workspace in {{target}}",
|
|
openFileIn: "Open {{fileName}} in {{target}}",
|
|
failedOpen: "Failed to open workspace",
|
|
},
|
|
pr: {
|
|
actions: {
|
|
viewPullRequest: "View",
|
|
openOn: "Open on {{brand}}",
|
|
},
|
|
checksSummary: {
|
|
passedLabel: "passed",
|
|
failedLabel: "failed",
|
|
runningLabel: "running",
|
|
passedAccessible: "Checks passed",
|
|
failedAccessible: "Checks failed",
|
|
runningAccessible: "Checks running",
|
|
},
|
|
sections: {
|
|
checks: "Checks",
|
|
pipeline: "Pipeline",
|
|
reviews: "Reviews",
|
|
},
|
|
empty: {
|
|
noJobs: "No jobs",
|
|
loadingPipeline: "Loading pipeline…",
|
|
pipelineJobsLoadFailed: "Could not load pipeline jobs",
|
|
allowedToFail: "allowed to fail",
|
|
},
|
|
approvals: "{{given}} of {{required}} approvals",
|
|
accessibility: {
|
|
pullRequest: "Pull request #{{number}}",
|
|
pullRequest_mr: "Merge request !{{number}}",
|
|
},
|
|
states: {
|
|
draft: "Draft",
|
|
merged: "Merged",
|
|
closed: "Closed",
|
|
open: "Open",
|
|
},
|
|
activity: {
|
|
commented: "Commented",
|
|
approved: "Approved",
|
|
requestedChanges: "Requested changes",
|
|
reviewed: "Reviewed",
|
|
},
|
|
time: {
|
|
justNow: "just now",
|
|
},
|
|
thread: {
|
|
discussion: "Discussion thread",
|
|
},
|
|
errors: {
|
|
statusLoadFailed: "Unable to load pull request status",
|
|
activityLoadFailed: "Unable to load pull request activity",
|
|
},
|
|
},
|
|
forgeSetup: {
|
|
installCli: "Install the {{cli}} CLI to use {{brand}} features.",
|
|
signIn: "Run {{command}} to use {{brand}} features.",
|
|
generic: "Set up {{brand}} on this host to use its features.",
|
|
},
|
|
},
|
|
},
|
|
workspaceLabels: {
|
|
title: "Labels",
|
|
unlabelled: "Unlabelled",
|
|
create: "Create label",
|
|
createConfirm: "Create",
|
|
creating: "Creating…",
|
|
name: "Label name",
|
|
updateHostUse: "Update this host to use labels.",
|
|
errors: { update: "Unable to update label", load: "Unable to load labels" },
|
|
colors: {
|
|
violet: "Violet",
|
|
sky: "Sky",
|
|
emerald: "Emerald",
|
|
orange: "Orange",
|
|
pink: "Pink",
|
|
indigo: "Indigo",
|
|
teal: "Teal",
|
|
red: "Red",
|
|
amber: "Amber",
|
|
blue: "Blue",
|
|
},
|
|
filter: {
|
|
clear: "Clear filter",
|
|
noMatchesTitle: "No workspaces match",
|
|
noMatchesDescription: "Change or clear the label filter to see workspaces.",
|
|
},
|
|
manage: {
|
|
open: "Manage labels…",
|
|
title: "Manage labels",
|
|
search: "Search labels",
|
|
empty: "No labels on this host.",
|
|
edit: "Edit label",
|
|
editLabel: "Edit {{name}}",
|
|
name: "Name",
|
|
color: "Color",
|
|
save: "Save",
|
|
delete: "Delete",
|
|
deleteTitle: "Delete {{name}}?",
|
|
deleteMessage_one: "This removes the label from {{count}} workspace on this host.",
|
|
deleteMessage_other: "This removes the label from {{count}} workspaces on this host.",
|
|
offline: "This host is offline.",
|
|
updateHost: "Update this host to manage labels.",
|
|
},
|
|
},
|
|
sidebar: {
|
|
display: {
|
|
trigger: "Display preferences",
|
|
heading: "Display",
|
|
grouping: {
|
|
label: "Grouping",
|
|
project: "Project",
|
|
status: "Status",
|
|
labels: "Labels",
|
|
},
|
|
titleSource: {
|
|
label: "Title",
|
|
title: "Title",
|
|
branch: "Branch name",
|
|
},
|
|
show: {
|
|
label: "Show",
|
|
branch: "Branch",
|
|
project: "Project",
|
|
host: "Host",
|
|
changeRequest: "Pull request",
|
|
checks: "Checks",
|
|
services: "Services",
|
|
labels: "Labels",
|
|
diff: "Diff stats",
|
|
timestamp: "Last activity",
|
|
},
|
|
checks: {
|
|
iconAndText: "Icon and text",
|
|
icon: "Icon only",
|
|
none: "Hidden",
|
|
},
|
|
hostFilter: {
|
|
label: "Host",
|
|
all: "All hosts",
|
|
},
|
|
},
|
|
pinned: {
|
|
title: "Pinned",
|
|
},
|
|
host: {
|
|
noHost: "No host",
|
|
switchTitle: "Switch host",
|
|
searchPlaceholder: "Search hosts...",
|
|
},
|
|
actions: {
|
|
addProject: "Add project",
|
|
newWorkspace: "New workspace",
|
|
hosts: "Hosts",
|
|
home: "Home",
|
|
settings: "Settings",
|
|
closeSidebar: "Close sidebar",
|
|
},
|
|
help: {
|
|
trigger: "Help and support",
|
|
sectionHelp: "Help",
|
|
diagnostics: "Run diagnostics",
|
|
shortcuts: "Keyboard shortcuts",
|
|
reportIssue: "Report an issue",
|
|
discord: "Discord",
|
|
github: "Create GitHub issue",
|
|
whatsNew: "What's new",
|
|
appName: "Paseo",
|
|
},
|
|
sections: {
|
|
sessions: "History",
|
|
schedules: "Schedules",
|
|
},
|
|
worktreeSetup: {
|
|
title: "Set up worktree scripts",
|
|
description:
|
|
"Add setup commands so new worktrees can install dependencies and prepare themselves automatically.",
|
|
openProjectSettings: "Open project settings",
|
|
},
|
|
project: {
|
|
actions: {
|
|
menu: "Project actions",
|
|
openSettings: "Open project settings",
|
|
openNewWindow: "Open in new window",
|
|
openNewWindowFailed: "Couldn't open a new window",
|
|
openFolder: "Open in file manager",
|
|
openFolderFailed: "Couldn't open folder",
|
|
remove: "Remove project",
|
|
removing: "Removing...",
|
|
},
|
|
confirmations: {
|
|
removeTitle: "Remove project?",
|
|
removeMessage:
|
|
'Remove "{{projectName}}" from the sidebar?\n\nFiles on disk will not be changed.',
|
|
removeConfirm: "Remove",
|
|
cancel: "Cancel",
|
|
},
|
|
toasts: {
|
|
hostDisconnected: "Host is not connected",
|
|
removeFailed: "Failed to remove some workspaces",
|
|
updateHostToRemove: "Update the host to remove projects.",
|
|
},
|
|
empty: {
|
|
title: "No projects yet",
|
|
description: "Add a project to get started",
|
|
},
|
|
},
|
|
workspace: {
|
|
status: {
|
|
serviceRunning: "Service {{name}} running",
|
|
serviceUnhealthy: "Service {{name}} unhealthy",
|
|
creating: "Creating...",
|
|
},
|
|
actions: {
|
|
menu: "Workspace actions",
|
|
newWorkspace: "New workspace",
|
|
showMore: "Show more",
|
|
showLess: "Show less",
|
|
createWorkspaceFor: "Create a new workspace for {{projectName}}",
|
|
copyPath: "Copy path",
|
|
copyBranchName: "Copy branch name",
|
|
rename: "Rename workspace",
|
|
pin: "Pin to top",
|
|
unpin: "Unpin",
|
|
archive: "Archive",
|
|
archiveWorkspace: "Archive workspace",
|
|
hideFromSidebar: "Hide from sidebar",
|
|
archiving: "Archiving...",
|
|
hiding: "Hiding...",
|
|
},
|
|
confirmations: {
|
|
hideTitle: "Hide workspace?",
|
|
hideMessage:
|
|
'Hide "{{workspaceName}}" from the sidebar?\n\nFiles on disk will not be changed.',
|
|
hideConfirm: "Hide",
|
|
cancel: "Cancel",
|
|
},
|
|
rename: {
|
|
title: "Rename workspace",
|
|
submit: "Rename",
|
|
invalidBranchName: "Invalid branch name",
|
|
},
|
|
toasts: {
|
|
workspacePathUnavailable: "Workspace path not available",
|
|
pathCopied: "Path copied",
|
|
branchNameCopied: "Branch name copied",
|
|
hostDisconnected: "Host is not connected",
|
|
hideFailed: "Failed to hide workspace",
|
|
archiveFailed: "Failed to archive workspace",
|
|
},
|
|
},
|
|
},
|
|
newWorkspace: {
|
|
title: "New workspace",
|
|
create: "Create",
|
|
isolation: {
|
|
local: "Local",
|
|
worktree: "New worktree",
|
|
label: "Isolation",
|
|
},
|
|
fields: {
|
|
project: "Project",
|
|
base: "Base",
|
|
baseNotApplicable: "Not applicable",
|
|
},
|
|
titlePlaceholder: "Title (optional)",
|
|
errors: {
|
|
hostDisconnected: "Host is not connected",
|
|
createWorktreeFailed: "Failed to create worktree",
|
|
composerStateRequired: "Composer state is required",
|
|
selectModel: "Select a model",
|
|
},
|
|
tooltips: {
|
|
project: "Choose the project",
|
|
host: "Choose the host",
|
|
isolation: "Choose the isolation level",
|
|
startingRef: "Choose where to start from",
|
|
launch: "Choose what to launch",
|
|
},
|
|
refPicker: {
|
|
startingRef: "Starting ref",
|
|
intoBase: "into {{baseRef}}",
|
|
searching: "Searching...",
|
|
noMatchingRefs: "No matching refs.",
|
|
searchPlaceholder: "Search branches and PRs",
|
|
title: "Start from",
|
|
},
|
|
launch: {
|
|
title: "What to launch",
|
|
chat: "Chat",
|
|
terminal: "Terminal",
|
|
manageProfiles: "Manage profiles",
|
|
submit: "Launch",
|
|
promptPlaceholder: "Prompt {{name}}",
|
|
commandPlaceholder: "Run a command, or leave empty for a blank terminal",
|
|
},
|
|
},
|
|
desktop: {
|
|
quitting: {
|
|
title: "Quitting Paseo...",
|
|
detail: "Stopping the local daemon.",
|
|
},
|
|
daemon: {
|
|
title: "Daemon",
|
|
status: {
|
|
title: "Status",
|
|
builtInOnly: "Only the built-in desktop daemon is shown here",
|
|
running: "running",
|
|
notRunning: "not running",
|
|
pid: "PID {{pid}}",
|
|
},
|
|
management: {
|
|
title: "Manage built-in daemon",
|
|
hint: "Let Paseo start and stop the built-in daemon",
|
|
pauseTitle: "Pause built-in daemon",
|
|
pauseMessage:
|
|
"This will stop the built-in daemon immediately. Running agents and terminals connected to the built-in daemon will be stopped.",
|
|
pauseAndStop: "Pause and stop",
|
|
registrationFailed:
|
|
"Built-in daemon started, but Paseo could not save the localhost connection. Toggle daemon management off and on again, or add localhost manually.",
|
|
pausedStopFailed:
|
|
"Built-in daemon management was paused, but Paseo could not stop the daemon.",
|
|
updateFailed: "Unable to update built-in daemon management.",
|
|
},
|
|
keepRunning: {
|
|
title: "Keep daemon running after quit",
|
|
hint: "Daemon keeps running when you quit Paseo",
|
|
},
|
|
logs: {
|
|
title: "Log file",
|
|
modalTitle: "Daemon logs",
|
|
unavailable: "Log path unavailable",
|
|
empty: "(log file is empty)",
|
|
copied: "Log path copied.",
|
|
copyFailed: "Unable to copy log path.",
|
|
open: "Open logs",
|
|
copyPath: "Copy path",
|
|
},
|
|
fullStatus: {
|
|
title: "Full status",
|
|
modalTitle: "Daemon status",
|
|
hint: "Runs `paseo daemon status` and shows the output",
|
|
view: "View status",
|
|
copied: "Status copied to clipboard.",
|
|
fetchFailed: "Failed to fetch daemon status: {{message}}",
|
|
},
|
|
advancedSettings: "Advanced settings",
|
|
openAdvancedSettings: "Open advanced daemon settings",
|
|
versionMismatch:
|
|
"App and daemon versions don't match. Update both to the same version for the best experience.",
|
|
loadFailed: "Unable to load desktop daemon status.",
|
|
},
|
|
updates: {
|
|
status: {
|
|
checking: "Checking for app updates...",
|
|
installing: "Installing app update...",
|
|
upToDate: "App is up to date.",
|
|
upToDateWithLastChecked: "Up to date. Last checked at {{time}}.",
|
|
pending: "We'll let you know when the update is ready.",
|
|
pendingWithLastChecked:
|
|
"We'll let you know when the update is ready. Last checked at {{time}}.",
|
|
pendingWithVersion: "Update found: {{version}}. Downloading...",
|
|
pendingWithVersionAndLastChecked:
|
|
"Update found: {{version}}. Downloading... Last checked at {{time}}.",
|
|
availableWithVersion: "Update ready: {{version}}",
|
|
availableWithVersionAndLastChecked: "Update ready: {{version}}. Last checked at {{time}}.",
|
|
available: "An app update is ready to install.",
|
|
availableWithLastChecked: "An app update is ready to install. Last checked at {{time}}.",
|
|
installed: "App update installed. Restart required.",
|
|
failed: "Failed to update app.",
|
|
idle: "Update status has not been checked yet.",
|
|
},
|
|
installError: "Unable to install the desktop app update.",
|
|
callout: {
|
|
installingTitle: "Installing update",
|
|
failedTitle: "Update failed",
|
|
availableTitle: "Update available",
|
|
genericError: "Something went wrong.",
|
|
whatsNew: "What's new",
|
|
installingAction: "Installing...",
|
|
installAndRestart: "Install & restart",
|
|
installingDescription: "Installing and restarting...",
|
|
versionReady: "{{version}} is ready to install.",
|
|
newVersionReady: "A new version is ready to install.",
|
|
restartWarning: "Upgrading the app will stop running agents and close terminal sessions.",
|
|
},
|
|
},
|
|
settings: {
|
|
loadFailed: "Unable to load desktop settings.",
|
|
saveFailed: "Unable to save desktop settings.",
|
|
},
|
|
rosetta: {
|
|
title: "Download the Apple Silicon build",
|
|
runningIntel: "You're running the Intel build of Paseo under Rosetta on Apple Silicon.",
|
|
highCpu: "This causes high CPU usage. Download the Apple Silicon build to fix it.",
|
|
download: "Download",
|
|
},
|
|
permissions: {
|
|
notifications: {
|
|
allowed: "Notifications are allowed by the OS.",
|
|
denied: "Notifications are denied in system settings.",
|
|
notGranted: "Notifications have not been granted yet.",
|
|
webOnly: "Desktop notification status is only available on web runtime.",
|
|
supported: "Desktop notifications are supported.",
|
|
unsupported: "Desktop notifications are not supported on this platform.",
|
|
apiUnavailable: "Web Notification API is unavailable in this environment.",
|
|
requestsWebOnly: "Desktop notification requests are only available on web runtime.",
|
|
requestUnavailable: "Web Notification API requestPermission() is unavailable.",
|
|
requestFailed: "Failed to request notification permission: {{message}}",
|
|
unexpectedState: "Unexpected notification permission state: {{state}}",
|
|
},
|
|
microphone: {
|
|
webOnly: "Desktop microphone status is only available on web runtime.",
|
|
navigatorUnavailable: "Navigator is unavailable in this environment.",
|
|
granted: "Microphone access is granted.",
|
|
denied: "Microphone access is denied in system settings.",
|
|
notGranted: "Microphone permission has not been granted yet.",
|
|
unexpectedState: "Unexpected microphone permission state: {{state}}",
|
|
statusApiUnavailable:
|
|
"Microphone status API is unavailable in this runtime. Use Request to check access.",
|
|
queryFailed: "Failed to query microphone status: {{message}}",
|
|
captureUnavailable: "Microphone capture is unavailable in this environment.",
|
|
permissionApiUnavailable:
|
|
"Permission status API is unavailable. Use Request to check access.",
|
|
requestsWebOnly: "Desktop microphone requests are only available on web runtime.",
|
|
captureApiUnavailable: "Microphone capture API is unavailable in this environment.",
|
|
requestDenied: "Microphone permission was denied by the user or system.",
|
|
noDevice: "No microphone device was found.",
|
|
requestFailed: "Failed to request microphone permission: {{message}}",
|
|
},
|
|
empty: {
|
|
notifications: "Notification status has not been checked yet.",
|
|
microphone: "Microphone status has not been checked yet.",
|
|
},
|
|
testNotification: {
|
|
title: "Paseo notification test",
|
|
body: "If you can see this, desktop notifications work.",
|
|
notDelivered: "Notification was not delivered. Check System Settings > Notifications.",
|
|
failed: "Failed to send notification.",
|
|
},
|
|
},
|
|
integrations: {
|
|
cli: {
|
|
statusFailed: "Unable to check CLI install status.",
|
|
installFailed: "Unable to install the Paseo CLI.",
|
|
},
|
|
},
|
|
},
|
|
rootError: {
|
|
title: "Paseo ran into a problem.",
|
|
body: "Try again to reload the app. If this keeps happening, include the details below when you report it.",
|
|
details: "Details",
|
|
},
|
|
startup: {
|
|
errorTitle: "Something went wrong",
|
|
errorDescription:
|
|
"The local server failed to start. If this keeps happening, please report the issue on GitHub and include the logs below.",
|
|
logs: {
|
|
loading: "Loading daemon logs...",
|
|
unavailable: "No daemon logs available.",
|
|
loadFailed: "Unable to load daemon logs: {{message}}",
|
|
},
|
|
},
|
|
openProject: {
|
|
tiles: {
|
|
addProject: {
|
|
title: "Add a project",
|
|
description: "Open a folder on your machine",
|
|
},
|
|
importSession: {
|
|
title: "Import session",
|
|
description: "Bring in recent external CLI sessions",
|
|
},
|
|
setupProviders: {
|
|
title: "Setup providers",
|
|
description: "Configure Claude Code, Codex, and more",
|
|
},
|
|
pairDevice: {
|
|
title: "Pair device",
|
|
description: "Connect your phone to this daemon",
|
|
},
|
|
},
|
|
},
|
|
projectPicker: {
|
|
placeholder: "Type to search...",
|
|
browse: "Browse…",
|
|
opening: "Opening project...",
|
|
searching: "Searching...",
|
|
empty: "Start typing a path",
|
|
errors: {
|
|
directory_not_found: "Directory not found.",
|
|
open_failed: "Could not open project.",
|
|
},
|
|
openPath: "Open path",
|
|
},
|
|
branchSwitcher: {
|
|
triggerTooltip: "Switch workspace branch",
|
|
currentBranch: "Current branch: {{branchName}}. Press to switch branch.",
|
|
placeholder: "Switch branch...",
|
|
searchPlaceholder: "Filter branches...",
|
|
empty: "No branches found.",
|
|
title: "Switch branch",
|
|
uncommittedTitle: "Uncommitted changes",
|
|
uncommittedMessage: "You have uncommitted changes. Stash them before switching branches?",
|
|
stashAndSwitch: "Stash & Switch",
|
|
failedToStash: "Failed to stash changes",
|
|
failedToSwitch: "Failed to switch branch",
|
|
restoreStashTitle: "Restore stashed changes?",
|
|
restoreStashMessage:
|
|
"This branch has stashed changes from a previous session. Would you like to restore them?",
|
|
restore: "Restore",
|
|
later: "Later",
|
|
stashRestored: "Stashed changes restored",
|
|
},
|
|
agentAutocomplete: {
|
|
searchingWorkspace: "Searching workspace...",
|
|
loadingCommands: "Loading commands...",
|
|
noFiles: "No files or directories found",
|
|
noCommands: "No commands found",
|
|
failedToLoad: "Failed to load",
|
|
},
|
|
loadOlderHistory: {
|
|
failed: "Couldn't load older history",
|
|
},
|
|
imageAttachmentPicker: {
|
|
permissionTitle: "Permission required",
|
|
permissionMessage: "Please allow access to your photo library to attach images.",
|
|
errorTitle: "Error",
|
|
failedToSelect: "Failed to select image",
|
|
dialogTitle: "Attach images",
|
|
dialogFilterName: "Images",
|
|
},
|
|
workspaceSetup: {
|
|
title: "Create workspace",
|
|
errors: {
|
|
failedCreateWorktree: "Failed to create worktree",
|
|
failedOpenProject: "Failed to open project",
|
|
selectModel: "Select a model",
|
|
hostDisconnected: "Host is not connected",
|
|
pendingRequired: "No workspace setup is pending",
|
|
composerStateRequired: "Workspace setup composer state is required",
|
|
},
|
|
},
|
|
onboarding: {
|
|
title: "Welcome to Paseo",
|
|
subtitle: "Connect your computer to get started",
|
|
actions: {
|
|
settings: "Settings",
|
|
},
|
|
},
|
|
modelSelector: {
|
|
title: "Select provider",
|
|
selectModel: "Select model",
|
|
selectedModel: "Select model ({{model}})",
|
|
loading: "Loading...",
|
|
loadingShort: "Loading",
|
|
loadingSelector: "Loading model selector...",
|
|
error: "Error",
|
|
defaultModel: "Default",
|
|
profiles: "Profiles",
|
|
providers: "Providers",
|
|
model: "Model",
|
|
editProfiles: "Edit",
|
|
editProfilesLabel: "Edit agent profiles",
|
|
createProfile: "Create profile",
|
|
modelCount: "{{count}} model",
|
|
modelCountPlural: "{{count}} models",
|
|
retry: "Retry",
|
|
retrying: "Retrying...",
|
|
noMatches: "No models match your search",
|
|
noMatchesForQuery: 'No models match "{{query}}"',
|
|
searchAllPlaceholder: "Search all models...",
|
|
searchPlaceholder: "Search models...",
|
|
openProviderSettings: "Open {{provider}} settings",
|
|
},
|
|
providerCatalog: {
|
|
title: "Add provider",
|
|
search: "Search providers",
|
|
noProviders: "No providers found",
|
|
actions: {
|
|
add: "Add",
|
|
adding: "Adding",
|
|
installed: "Installed",
|
|
cancel: "Cancel",
|
|
installInstructions: "Install instructions",
|
|
installInstructionsFor: "{{provider}} install instructions",
|
|
},
|
|
errors: {
|
|
unableToInstall: "Unable to install provider",
|
|
},
|
|
},
|
|
providerSelection: {
|
|
defaultModel: "Default",
|
|
selectModel: "Select model",
|
|
loading: "Loading...",
|
|
error: "Error",
|
|
unavailable: "Unavailable",
|
|
unknownError: "Unknown error",
|
|
readiness: {
|
|
initialPromptRequired: "Initial prompt is required",
|
|
noProviders: "No available providers on the selected host",
|
|
modelDefaultsLoading: "Model defaults are still loading",
|
|
noModelAvailable: "No model is available for the selected provider",
|
|
workspaceDirectoryNotFound: "Workspace directory not found",
|
|
hostDisconnected: "Host is not connected",
|
|
},
|
|
},
|
|
pairing: {
|
|
connectionMethods: {
|
|
title: "Add connection",
|
|
direct: {
|
|
title: "Direct connection",
|
|
description: "Local network or VPN.",
|
|
},
|
|
scanQr: {
|
|
title: "Scan QR code",
|
|
description: "Encrypted relay connection.",
|
|
},
|
|
pasteLink: {
|
|
title: "Paste pairing link",
|
|
description: "Encrypted relay connection.",
|
|
},
|
|
},
|
|
direct: {
|
|
title: "Direct connection",
|
|
helper: "Enter the address of a Paseo server.",
|
|
fields: {
|
|
host: "Host",
|
|
port: "Port",
|
|
password: "Password",
|
|
optional: "Optional",
|
|
useSsl: "Use SSL",
|
|
connectionUri: "Connection URI",
|
|
},
|
|
advanced: {
|
|
label: "Advanced",
|
|
show: "Show advanced",
|
|
hide: "Hide advanced",
|
|
},
|
|
passwordVisibility: {
|
|
show: "Show password",
|
|
hide: "Hide password",
|
|
},
|
|
actions: {
|
|
cancel: "Cancel",
|
|
connect: "Connect",
|
|
connecting: "Connecting...",
|
|
},
|
|
errors: {
|
|
hostRequired: "Host is required",
|
|
invalidPort: "Port must be between 1 and 65535",
|
|
invalidConnection: "Invalid connection",
|
|
failedTitle: "Connection failed",
|
|
failedToConnect: "We failed to connect to {{endpoint}}.",
|
|
noAdditionalDetails: "{{detail}} (no additional details provided)",
|
|
timedOut: "Connection timed out. Check the host/port and your network.",
|
|
refused: "Connection refused. Is the server running at this address?",
|
|
hostNotFound: "Host not found. Check the hostname and try again.",
|
|
hostUnreachable: "Host is unreachable. Check your network and firewall.",
|
|
tlsError:
|
|
"TLS error. Direct connections use SSL only when a TLS terminator is in front of the daemon.",
|
|
unableToConnect: "Unable to connect. Check the host/port and that the daemon is reachable.",
|
|
details: "Details: {{detail}}",
|
|
},
|
|
},
|
|
link: {
|
|
title: "Paste pairing link",
|
|
helper: "Paste the pairing link from your server.",
|
|
label: "Pairing link",
|
|
errors: {
|
|
required: "Paste a pairing link (.../#offer=...)",
|
|
missingOffer: "Link must include #offer=...",
|
|
emptyOffer: "Offer payload is empty",
|
|
invalid: "Invalid pairing link",
|
|
unableToPair: "Unable to pair host",
|
|
},
|
|
alert: {
|
|
failedTitle: "Pairing failed",
|
|
},
|
|
actions: {
|
|
cancel: "Cancel",
|
|
pair: "Pair",
|
|
pairing: "Pairing...",
|
|
},
|
|
},
|
|
scan: {
|
|
title: "Scan QR",
|
|
webUnavailableTitle: "Not available on web",
|
|
webUnavailableBody:
|
|
'QR scanning is not supported in the web build. Use "Paste link" instead.',
|
|
backToSettings: "Back to Settings",
|
|
cameraPermissionTitle: "Camera permission",
|
|
cameraPermissionBody: "Allow camera access to scan the pairing QR code from your daemon.",
|
|
grantPermission: "Grant permission",
|
|
pairing: "Pairing...",
|
|
unableToPair: "Unable to pair host",
|
|
errorTitle: "Error",
|
|
},
|
|
device: {
|
|
loadingOffer: "Loading pairing offer...",
|
|
failedToLoadOffer: "Failed to load pairing offer.",
|
|
relayDisabled: "Relay is not enabled. Enable relay to pair a device.",
|
|
enableTitle: "Enable relay?",
|
|
enableDescription:
|
|
"Relay lets this device connect from anywhere. Pairing traffic is end-to-end encrypted.",
|
|
relayDocs: "How relay works",
|
|
relayDocsAccessibility: "Read how Paseo relay works",
|
|
enableRelay: "Enable relay",
|
|
enablingRelay: "Enabling...",
|
|
notNow: "Not now",
|
|
directConnectionHint:
|
|
"Without relay, connect directly over TCP, Tailscale, or another VPN. No QR code is created.",
|
|
updateRequired: "Update the host to enable relay from Paseo Desktop.",
|
|
unavailable: "Pairing offer unavailable.",
|
|
hint: "Scan this QR code with Paseo on your phone, or copy the link below.",
|
|
qrUnavailable: "QR code unavailable.",
|
|
qrAccessibility: "Pairing QR code",
|
|
retry: "Retry",
|
|
copy: "Copy",
|
|
copied: "Copied",
|
|
},
|
|
},
|
|
realtimeVoice: {
|
|
actions: {
|
|
mute: "Mute realtime voice",
|
|
unmute: "Unmute realtime voice",
|
|
stop: "Stop realtime voice and interrupt turn",
|
|
},
|
|
},
|
|
rewind: {
|
|
tooltip: "Rewind to this message",
|
|
warning: "This action cannot be undone",
|
|
actions: {
|
|
conversation: "Rewind conversation",
|
|
files: "Rewind files",
|
|
both: "Rewind conversation and files",
|
|
},
|
|
errors: {
|
|
failed: "Failed to rewind agent",
|
|
},
|
|
},
|
|
diffViewer: {
|
|
empty: "No changes to display",
|
|
},
|
|
serviceUrl: {
|
|
title: "Open service URL",
|
|
message: "Open {{url}}?",
|
|
inPaseo: "In Paseo",
|
|
externalBrowser: "External browser",
|
|
dontAskAgain: "Don't ask again",
|
|
},
|
|
downloads: {
|
|
requestTokenFailed: "Failed to request download token.",
|
|
hostUnavailable: "Download host is unavailable.",
|
|
cancelled: "Download was cancelled.",
|
|
failed: "Failed to download file.",
|
|
shareFile: "Share file",
|
|
shareFileNamed: "Share {{fileName}}",
|
|
},
|
|
menu: {
|
|
backdrop: "Menu backdrop",
|
|
},
|
|
subagents: {
|
|
title: "Subagents",
|
|
pillLabelOne: "1 subagent",
|
|
pillLabelMany: "{{count}} subagents",
|
|
pillLabelRunning: "{{count}} running",
|
|
pillLabelFailed: "{{count}} failed",
|
|
pillLabelNeedsInputOne: "1 needs input",
|
|
pillLabelNeedsInputMany: "{{count}} need input",
|
|
pillLabelReadyToReview: "{{count}} ready to review",
|
|
detachAction: "Detach {{label}}",
|
|
detachTooltip: "Detach subagent",
|
|
archiveAction: "Archive {{label}}",
|
|
archiveTooltip: "Archive subagent",
|
|
archiveFinishedAction: "Archive finished subagents",
|
|
archiveFinishedRetry: "Retry ({{failed}}/{{total}})",
|
|
},
|
|
panels: {
|
|
draft: {
|
|
newAgent: "New Agent",
|
|
creatingAgent: "Creating agent",
|
|
},
|
|
file: {
|
|
directoryMissing: "Workspace directory not found.",
|
|
loading: "Loading file...",
|
|
noPreview: "No preview available",
|
|
binaryPreviewUnavailable: "Binary preview unavailable",
|
|
failedToLoad: "Failed to load file",
|
|
failedToLoadPreview: "Failed to load file preview",
|
|
editor: {
|
|
fileSize: "File size {{size}}",
|
|
lines: "{{count}} lines",
|
|
editorStatus: "Editor status {{status}}",
|
|
unsavedChanges: "Unsaved changes",
|
|
saving: "Saving...",
|
|
saveFailed: "Save failed",
|
|
changedOnDisk: "Changed on disk",
|
|
vimMode: "Vim mode {{mode}}",
|
|
cursor: "Line {{line}}, column {{column}}",
|
|
preview: "Preview",
|
|
source: "Source",
|
|
deletedTitle: "File deleted on disk",
|
|
checkFailedTitle: "Couldn't check file on disk",
|
|
preservedDescription: "The open copy is preserved.",
|
|
conflictDescription: "The local buffer was preserved. Choose which version to keep.",
|
|
overwrite: "Overwrite",
|
|
reload: "Reload",
|
|
reloadTitle: "Reload from disk?",
|
|
reloadMessage: "Your local changes will be lost.",
|
|
},
|
|
},
|
|
files: {
|
|
label: "Files",
|
|
subtitle: "Workspace files",
|
|
tooltip: "Browse workspace files",
|
|
},
|
|
pullRequest: {
|
|
label: "Pull request",
|
|
subtitle: "Pull request details",
|
|
emptyTitle: "No pull request yet",
|
|
emptyDescription: "Create a pull request for this checkout to see its details here.",
|
|
},
|
|
diff: {
|
|
changesLabel: "Changes",
|
|
changesSubtitle: "Working tree diff",
|
|
commitSubtitle: "Commit diff",
|
|
uncommittedSubtitle: "Uncommitted changes",
|
|
baseSubtitle: "Compared with {{baseRef}}",
|
|
directoryMissing: "Workspace directory not found.",
|
|
empty: "No changes",
|
|
loadError: "Failed to load diff",
|
|
capabilityMissing: "Update the host to view commit diffs.",
|
|
},
|
|
},
|
|
toolCallDetails: {
|
|
error: "Error",
|
|
empty: "No additional details available",
|
|
subAgentActivity: "Sub-agent activity",
|
|
input: "Input",
|
|
output: "Output",
|
|
},
|
|
toolCallGroup: {
|
|
editedFiles: {
|
|
one: "edited {{count}} file",
|
|
other: "edited {{count}} files",
|
|
},
|
|
commands: {
|
|
one: "ran {{count}} command",
|
|
other: "ran {{count}} commands",
|
|
},
|
|
readFiles: {
|
|
one: "read {{count}} file",
|
|
other: "read {{count}} files",
|
|
},
|
|
searches: {
|
|
one: "searched {{count}} time",
|
|
other: "searched {{count}} times",
|
|
},
|
|
otherTools: {
|
|
one: "used {{count}} other tool",
|
|
other: "used {{count}} other tools",
|
|
},
|
|
paseoCalls: {
|
|
one: "called Paseo {{count}} time",
|
|
other: "called Paseo {{count}} times",
|
|
},
|
|
and: "and",
|
|
},
|
|
renameModal: {
|
|
rename: "Rename",
|
|
saving: "Saving...",
|
|
},
|
|
sidebarCallout: {
|
|
dismiss: "Dismiss",
|
|
},
|
|
contextWindow: {
|
|
title: "Context window",
|
|
used: "{{percentage}}% used",
|
|
tokens: "{{used}} / {{max}} tokens",
|
|
sessionCost: "Session cost {{cost}}",
|
|
accessibility: "Context window {{percentage}}% used",
|
|
},
|
|
review: {
|
|
comment: {
|
|
add: "Add review comment",
|
|
edit: "Edit review comment",
|
|
delete: "Delete review comment",
|
|
label: "Review comment",
|
|
placeholder: "Leave a comment",
|
|
cancel: "Cancel",
|
|
cancelAccessibility: "Cancel review comment",
|
|
save: "Comment",
|
|
saveAccessibility: "Save review comment",
|
|
},
|
|
},
|
|
settings: {
|
|
title: "Settings",
|
|
loading: "Loading settings...",
|
|
groups: {
|
|
app: "App",
|
|
host: "Host",
|
|
},
|
|
hostPicker: {
|
|
switchHost: "Switch host",
|
|
local: "Local",
|
|
},
|
|
backToWorkspace: "Back",
|
|
addHost: "Add host",
|
|
enableBuiltInDaemon: "Enable built-in daemon",
|
|
projects: "Projects",
|
|
projectList: {
|
|
hostLoadFailed: "Couldn't load projects from host {{hostName}}: {{message}}",
|
|
editProject: "Edit {{projectName}}",
|
|
},
|
|
groupInfo: "About {{title}}",
|
|
sections: {
|
|
general: "General",
|
|
appearance: "Appearance",
|
|
editor: "Editor",
|
|
shortcuts: "Shortcuts",
|
|
integrations: "Integrations",
|
|
notifications: "Notifications",
|
|
permissions: "Permissions",
|
|
diagnostics: "Diagnostics",
|
|
about: "About",
|
|
},
|
|
editor: {
|
|
title: "Editor",
|
|
vimKeybindings: "Vim keybindings",
|
|
vimHint: "Applies to source files on web and desktop.",
|
|
},
|
|
notifications: {
|
|
title: "Notifications",
|
|
permission: "Notification permission",
|
|
refreshAccessibility: "Refresh notification permission",
|
|
playSound: "Play sound",
|
|
playSoundHint: "Play a sound when a desktop notification arrives",
|
|
test: "Test notification",
|
|
testHint: "Send a notification using these settings",
|
|
permissionRequired: "Allow notification access before testing",
|
|
send: "Send",
|
|
sending: "Sending...",
|
|
sentTitle: "Test notification sent",
|
|
sentDescription: "Paseo handed the notification to the operating system.",
|
|
sendFailedTitle: "Unable to send test notification",
|
|
},
|
|
hostSections: {
|
|
projects: "Projects",
|
|
connections: "Connections",
|
|
agents: "Agents",
|
|
metadata: "Metadata",
|
|
workspaces: "Workspaces",
|
|
providers: "Providers",
|
|
usage: "Usage",
|
|
terminals: "Terminals",
|
|
plugins: "Plugins",
|
|
host: "Overview",
|
|
},
|
|
plugins: {
|
|
title: "Plugins",
|
|
trustedTitle: "Plugins are trusted code",
|
|
trustedDescription:
|
|
"They run on this host and inside the app without sandboxing. Install only code you trust.",
|
|
globalTitle: "Enable plugins",
|
|
globalHint: "Global switch for every configured plugin",
|
|
directoryLabel: "Plugin directory",
|
|
directoryPlaceholder: "/absolute/path/on/host",
|
|
idLabel: "Plugin installation ID",
|
|
idHint: "Leave blank to use paseo-plugin.json",
|
|
idPlaceholder: "Manifest default",
|
|
install: "Install directory",
|
|
installing: "Installing…",
|
|
logs: {
|
|
action: "Logs",
|
|
title: "Logs: {{id}}",
|
|
loading: "Loading logs…",
|
|
refresh: "Refresh",
|
|
refreshing: "Refreshing…",
|
|
errorTitle: "Unable to load plugin logs",
|
|
empty: "No plugin output yet",
|
|
},
|
|
status: { running: "running", disabled: "disabled", failed: "failed" },
|
|
actions: {
|
|
reload: "Reload",
|
|
reloading: "Reloading…",
|
|
enable: "Enable",
|
|
enabling: "Enabling…",
|
|
disable: "Disable",
|
|
disabling: "Disabling…",
|
|
remove: "Remove",
|
|
removing: "Removing…",
|
|
},
|
|
feedback: {
|
|
installed: "Installed {{id}}",
|
|
reload: "Reloaded {{id}}",
|
|
enable: "Enabled {{id}}",
|
|
disable: "Disabled {{id}}",
|
|
remove: "Removed {{id}}",
|
|
kept: "Kept {{id}}",
|
|
globalEnabled: "Plugins enabled",
|
|
globalDisabled: "Plugins disabled",
|
|
},
|
|
removeConfirmTitle: "Remove {{id}}?",
|
|
removeConfirmMessage: "This removes the configuration. The source directory is not deleted.",
|
|
states: {
|
|
offlineTitle: "Plugin host is offline",
|
|
offlineDescription: "Reconnect to this host to manage its plugins.",
|
|
updateTitle: "Update this host to manage plugins",
|
|
loading: "Loading plugins…",
|
|
errorTitle: "Unable to load plugins",
|
|
retry: "Retry",
|
|
empty: "No plugins configured",
|
|
},
|
|
},
|
|
metadataGeneration: {
|
|
title: "Metadata generation",
|
|
description:
|
|
"Choose the model Paseo uses for workspace titles, branch names, commit messages, and pull request drafts",
|
|
selection: "Model selection",
|
|
automatic: "Automatic",
|
|
preferred: "Manual",
|
|
automaticHint: "Paseo picks a fast available model",
|
|
preferredHint: "Choose the model Paseo uses",
|
|
model: "Model",
|
|
fallbackHint: "If it is unavailable, Paseo falls back to another available model",
|
|
docs: "Docs",
|
|
saveError: "Unable to update metadata generation",
|
|
},
|
|
general: {
|
|
title: "General",
|
|
browserData: {
|
|
title: "Browser data",
|
|
siteData: "Cookies and site data",
|
|
description: "Browser tabs share sign-ins and site data across Paseo.",
|
|
clear: "Clear browser data",
|
|
clearing: "Clearing...",
|
|
confirmTitle: "Clear browser data?",
|
|
confirmMessage: "Sites will be signed out and open browser tabs will reload.",
|
|
success: "Browser data cleared.",
|
|
error: "Couldn't clear browser data.",
|
|
},
|
|
defaultSend: {
|
|
label: "Default send",
|
|
descriptions: {
|
|
interrupt: "When the agent is running, Enter interrupts. Command/Ctrl+Enter queues.",
|
|
steer:
|
|
"When the agent is running, Enter steers the active turn. Command/Ctrl+Enter queues.",
|
|
queue: "When the agent is running, Enter queues. Command/Ctrl+Enter submits.",
|
|
},
|
|
options: {
|
|
interrupt: "Interrupt",
|
|
steer: "Steer",
|
|
queue: "Queue",
|
|
},
|
|
},
|
|
serviceUrls: {
|
|
label: "Service URLs",
|
|
description: "Where to open URLs from running scripts",
|
|
options: {
|
|
ask: "Ask",
|
|
inApp: "In Paseo",
|
|
external: "External browser",
|
|
},
|
|
},
|
|
terminalScrollback: {
|
|
label: "Terminal scrollback",
|
|
description: "Lines kept in the built-in terminal buffer",
|
|
accessibilityLabel: "Terminal scrollback lines",
|
|
},
|
|
autoExpandReasoning: {
|
|
label: "Always expand reasoning",
|
|
description: "Show agent thinking and chain-of-thought blocks fully expanded by default",
|
|
},
|
|
toolCallDetail: {
|
|
label: "Tool call display",
|
|
description: "How tool calls appear in the timeline",
|
|
accessibilityLabel: "Select tool call display ({{value}})",
|
|
options: {
|
|
overview: "Summary",
|
|
detailed: "Full detail",
|
|
},
|
|
},
|
|
language: {
|
|
label: "Language",
|
|
description: "App language",
|
|
options: {
|
|
system: "System",
|
|
ar: "Arabic",
|
|
en: "English",
|
|
es: "Spanish",
|
|
fr: "French",
|
|
ja: "Japanese",
|
|
ko: "Korean",
|
|
ptBR: "Brazilian Portuguese",
|
|
ru: "Russian",
|
|
zhCN: "Simplified Chinese",
|
|
},
|
|
},
|
|
},
|
|
diagnostics: {
|
|
title: "Diagnostics",
|
|
legacyTerminalRenderer: {
|
|
label: "Use legacy terminal renderer",
|
|
description: "Use the previous WebView terminal after reopening a terminal",
|
|
accessibilityLabel: "Use legacy terminal renderer",
|
|
},
|
|
testAudio: "Test audio",
|
|
playTest: "Play test",
|
|
playing: "Playing...",
|
|
playbackFailed: "Playback failed: {{message}}",
|
|
app: {
|
|
title: "App diagnostic",
|
|
rowTitle: "App diagnostic",
|
|
rowHint: "Collect connection, daemon, provider, desktop, and log details",
|
|
run: "Run",
|
|
running: "Running diagnostic...",
|
|
copyLabel: "diagnostic",
|
|
copyAccessibility: "Copy diagnostic",
|
|
copyFailed: "Failed to copy diagnostic",
|
|
refreshAccessibility: "Refresh diagnostic",
|
|
refreshingAccessibility: "Refreshing diagnostic",
|
|
progress: {
|
|
client: "Client",
|
|
desktop: "Desktop",
|
|
},
|
|
},
|
|
},
|
|
about: {
|
|
title: "About",
|
|
appVersion: "App version",
|
|
thisDevice: "This device",
|
|
connectedHosts: "Connected hosts",
|
|
offline: "Offline",
|
|
versionDiffers: "Version differs from this device",
|
|
releaseChannel: {
|
|
label: "Release channel",
|
|
description: "Switch to Beta to get updates sooner and help shape them",
|
|
stable: "Stable",
|
|
beta: "Beta",
|
|
},
|
|
updates: {
|
|
label: "App updates",
|
|
readyToInstall: "Ready to install: {{version}}",
|
|
installTitle: "Install desktop update",
|
|
installMessage: "This updates Paseo on this computer",
|
|
installConfirm: "Install update",
|
|
update: "Update",
|
|
updateTo: "Update to {{version}}",
|
|
installing: "Installing...",
|
|
check: "Check",
|
|
checking: "Checking...",
|
|
alertTitle: "Error",
|
|
alertMessage: "Unable to open the update confirmation dialog.",
|
|
},
|
|
},
|
|
appearance: {
|
|
theme: {
|
|
title: "Theme",
|
|
accessibilityLabel: "Theme: {{value}}",
|
|
options: {
|
|
light: "Light",
|
|
dark: "Dark",
|
|
zinc: "Zinc",
|
|
midnight: "Midnight",
|
|
claude: "Claude",
|
|
ghostty: "Ghostty",
|
|
pureBlack: "Pure black",
|
|
auto: "System",
|
|
},
|
|
},
|
|
detailLevel: {
|
|
title: "Detail level",
|
|
},
|
|
chatOutline: {
|
|
title: "Chat outline",
|
|
description: "Show an outline for jumping between prompts",
|
|
},
|
|
fonts: {
|
|
title: "Fonts",
|
|
systemDefault: "System default",
|
|
interfaceFont: "Interface font",
|
|
interfaceFontHint: "Used across the app. Leave empty for the system default",
|
|
interfaceFontAccessibility: "Interface font family",
|
|
baseSize: "Base size",
|
|
baseSizeAccessibility: "Base font size",
|
|
codeFont: "Code font",
|
|
codeFontHint:
|
|
"Used in code, diffs, and the terminal output. Leave empty for the system default",
|
|
codeFontAccessibility: "Code font family",
|
|
codeSize: "Code size",
|
|
codeSizeAccessibility: "Code font size",
|
|
},
|
|
syntax: {
|
|
title: "Syntax",
|
|
highlightTheme: "Highlight theme",
|
|
highlightThemeHint: "Colors for code, independent of the app theme",
|
|
highlightThemeAccessibility: "Highlight theme: {{value}}",
|
|
previewAccessibility: "Live preview of the syntax theme and code font",
|
|
},
|
|
},
|
|
shortcuts: {
|
|
dialogTitle: "Shortcuts",
|
|
searchPlaceholder: "Search shortcuts",
|
|
unavailableOnMobile: "Keyboard shortcuts are only available on desktop",
|
|
capturePrompt: "Press shortcut...",
|
|
unassigned: "Not set",
|
|
actions: {
|
|
menu: "Actions for {{name}}",
|
|
done: "Done",
|
|
cancel: "Cancel",
|
|
bind: "Bind",
|
|
rebind: "Rebind",
|
|
clear: "Clear",
|
|
reset: "Reset to default",
|
|
resetAll: "Reset all",
|
|
},
|
|
sections: {
|
|
general: "General",
|
|
workspaces: "Projects & Workspaces",
|
|
tabsPanes: "Tabs & Panes",
|
|
layout: "Layout",
|
|
agentInput: "Agent Input",
|
|
},
|
|
help: {
|
|
openProject: "Open project",
|
|
newWorkspace: "New workspace",
|
|
newWorktree: "New worktree",
|
|
archiveWorkspace: "Archive workspace",
|
|
newTab: "New tab",
|
|
closeCurrentTab: "Close current tab",
|
|
jumpToWorkspace: "Jump to workspace",
|
|
jumpToTab: "Jump to tab",
|
|
previousWorkspace: "Previous workspace",
|
|
nextWorkspace: "Next workspace",
|
|
previousTab: "Previous tab",
|
|
nextTab: "Next tab",
|
|
splitPaneRight: "Split pane right",
|
|
splitPaneDown: "Split pane down",
|
|
focusPaneLeft: "Focus pane left",
|
|
focusPaneRight: "Focus pane right",
|
|
focusPaneUp: "Focus pane up",
|
|
focusPaneDown: "Focus pane down",
|
|
moveTabLeft: "Move tab left",
|
|
moveTabRight: "Move tab right",
|
|
moveTabUp: "Move tab up",
|
|
moveTabDown: "Move tab down",
|
|
closePane: "Close pane",
|
|
newTerminal: "New terminal",
|
|
searchFiles: "Search files",
|
|
toggleCommandCenter: "Toggle command center",
|
|
showKeyboardShortcuts: "Show keyboard shortcuts",
|
|
toggleLeftSidebar: "Toggle left sidebar",
|
|
toggleRightSidebar: "Toggle right sidebar",
|
|
toggleBothSidebars: "Toggle both sidebars",
|
|
toggleSettings: "Toggle settings",
|
|
toggleFocusMode: "Toggle focus mode",
|
|
toggleExplorerPaneMaximization: "Toggle Explorer pane maximization",
|
|
cycleTheme: "Cycle theme",
|
|
focusMessageInput: "Focus message input",
|
|
cycleAgentMode: "Cycle agent mode",
|
|
toggleVoiceMode: "Toggle voice mode",
|
|
startStopDictation: "Start/stop dictation",
|
|
interruptAgent: "Interrupt agent",
|
|
sendMessage: "Send message",
|
|
queueMessage: "Queue message",
|
|
muteUnmuteVoiceMode: "Mute/unmute voice mode",
|
|
switchProject: "Switch project",
|
|
},
|
|
helpNotes: {
|
|
showKeyboardShortcuts: "Available when focus is not in a text field or terminal.",
|
|
},
|
|
},
|
|
integrations: {
|
|
title: "Integrations",
|
|
docs: {
|
|
cli: "CLI docs",
|
|
openCli: "Open CLI documentation",
|
|
},
|
|
commandLine: {
|
|
title: "Command line",
|
|
description: "Control and script agents from your terminal",
|
|
},
|
|
actions: {
|
|
install: "Install",
|
|
installing: "Installing...",
|
|
installed: "Installed",
|
|
},
|
|
},
|
|
permissions: {
|
|
title: "Permissions",
|
|
microphone: "Microphone",
|
|
refresh: "Refresh",
|
|
refreshing: "Refreshing...",
|
|
refreshAccessibility: "Refresh desktop permissions",
|
|
actions: {
|
|
granted: "Granted",
|
|
request: "Request",
|
|
requesting: "Requesting...",
|
|
},
|
|
},
|
|
host: {
|
|
appearance: {
|
|
title: "Appearance",
|
|
name: {
|
|
label: "Name",
|
|
},
|
|
color: {
|
|
label: "Color",
|
|
accessibilityLabel: "Color, {{value}}",
|
|
options: {
|
|
none: "Default",
|
|
violet: "Violet",
|
|
sky: "Sky",
|
|
emerald: "Emerald",
|
|
orange: "Orange",
|
|
pink: "Pink",
|
|
indigo: "Indigo",
|
|
teal: "Teal",
|
|
red: "Red",
|
|
amber: "Amber",
|
|
blue: "Blue",
|
|
},
|
|
},
|
|
badge: {
|
|
label: "Sidebar badge",
|
|
accessibilityLabel: "Sidebar badge, {{value}}",
|
|
options: {
|
|
name: "Name",
|
|
icon: "Icon only",
|
|
hidden: "Hidden",
|
|
},
|
|
},
|
|
preview: {
|
|
workspaceName: "my-workspace",
|
|
},
|
|
},
|
|
notFound: "Host not found",
|
|
badges: {
|
|
relay: "Relay",
|
|
local: "Local",
|
|
},
|
|
connections: {
|
|
title: "Connections",
|
|
removeTitle: "Remove connection",
|
|
removeMessage: "Remove {{name}}? This cannot be undone.",
|
|
removeAction: "Remove",
|
|
removeErrorTitle: "Error",
|
|
removeErrorMessage: "Unable to remove connection",
|
|
timeout: "Timeout",
|
|
},
|
|
pairDevices: {
|
|
title: "Pair devices",
|
|
rowTitle: "Pair a device",
|
|
rowHint: "Scan a QR code or copy a link to connect your phone to this host",
|
|
},
|
|
skills: {
|
|
sectionTitle: "Orchestration skills",
|
|
title: "Orchestration skills",
|
|
description: "Teach your agents to orchestrate through the CLI",
|
|
docs: "Skills",
|
|
openDocs: "Open skills documentation",
|
|
unavailable: "Connect to this host to manage orchestration skills",
|
|
unsupported: "Update this host to manage orchestration skills",
|
|
statusFailed: "Unable to check orchestration skills status.",
|
|
updateFailed: "Unable to update orchestration skills.",
|
|
uninstallFailed: "Unable to uninstall orchestration skills.",
|
|
saveSelectionFailed: "Unable to save the orchestration skills selection.",
|
|
updateAvailable: "Update available",
|
|
updateTitle: "Update Paseo skills?",
|
|
updateFallback: "Sync bundled skills to this host.",
|
|
uninstallTitle: "Uninstall Paseo skills?",
|
|
uninstallMessage:
|
|
"Removes all Paseo orchestration skills from ~/.agents, ~/.claude, ~/.codex on this host.",
|
|
choose: "Choose skills",
|
|
chooseAll: "All skills",
|
|
chooseAllHint: "Keep every bundled skill installed, including ones added later.",
|
|
chooseList: "Bundled skills",
|
|
chooseEmpty: "This host bundles no skills.",
|
|
removeTitle: "Remove deselected skills?",
|
|
removeMessage:
|
|
"{{skills}} will be deleted from ~/.agents, ~/.claude, and ~/.codex on this host. Anything you added inside those skill folders is deleted too.",
|
|
saveFailed: "Could not save your skill selection.",
|
|
actions: {
|
|
install: "Install",
|
|
installing: "Installing...",
|
|
installed: "Installed",
|
|
update: "Update",
|
|
working: "Working...",
|
|
remove: "Remove",
|
|
uninstall: "Uninstall",
|
|
save: "Save",
|
|
saving: "Saving...",
|
|
cancel: "Cancel",
|
|
},
|
|
operations: {
|
|
add: "Add skill",
|
|
update: "Update skill",
|
|
delete: "Delete skill",
|
|
},
|
|
},
|
|
orchestration: {
|
|
title: "Orchestration",
|
|
unavailable: "Connect to this host to manage orchestration",
|
|
enableTools: {
|
|
title: "Enable Paseo tools",
|
|
hint: "Agents will be able to manage worktrees, agents and schedules",
|
|
accessibilityLabel: "Inject Paseo tools",
|
|
},
|
|
systemPrompt: {
|
|
title: "System prompt",
|
|
hint: "Adds a system prompt to all agents",
|
|
sheetTitle: "Append system prompt",
|
|
accessibilityLabel: "Append system prompt",
|
|
placeholder: "Always keep replies concise.",
|
|
edit: "Edit",
|
|
reset: "Reset",
|
|
save: "Save",
|
|
saving: "Saving...",
|
|
},
|
|
},
|
|
agents: {
|
|
unavailable: "Connect to this host to manage agents",
|
|
},
|
|
workspaces: {
|
|
unavailable: "Connect to this host to manage workspaces",
|
|
},
|
|
terminalProfiles: {
|
|
unavailable: "Connect to this host to manage terminal profiles",
|
|
sectionTitle: "Terminal profiles",
|
|
editProfile: "Edit profile",
|
|
addProfileTitle: "Add terminal profile",
|
|
editProfileTitle: "Edit terminal profile",
|
|
namePlaceholder: "Claude Code",
|
|
commandPlaceholder: "claude",
|
|
argsPlaceholder: "--dangerously-skip-permissions",
|
|
nameLabel: "Name",
|
|
commandLabel: "Command",
|
|
argsLabel: "Arguments",
|
|
nameRequired: "Name is required",
|
|
commandRequired: "Command is required",
|
|
argsHint: "Space-separated arguments passed to the command",
|
|
saving: "Saving...",
|
|
remove: "Remove",
|
|
removeConfirmTitle: "Remove profile?",
|
|
removeConfirmMessage: 'Remove "{{name}}"?',
|
|
moveUp: "Move up",
|
|
moveDown: "Move down",
|
|
save: "Save",
|
|
emptyState: "No profiles yet. Add one to launch terminals with a specific command.",
|
|
},
|
|
agentProfiles: {
|
|
sectionTitle: "Agent profiles",
|
|
unavailable: "Connect to this host to manage agent profiles",
|
|
unsupported: "This host runs a daemon that does not support agent profiles yet",
|
|
emptyState: "No profiles yet. Add one to start agents from a saved provider and model.",
|
|
addProfileTitle: "Add agent profile",
|
|
newProfile: "New profile",
|
|
editProfile: "Edit profile",
|
|
editProfileTitle: "Edit agent profile",
|
|
nameLabel: "Name",
|
|
namePlaceholder: "UI work",
|
|
iconLabel: "Icon",
|
|
noIcon: "None",
|
|
providerLabel: "Provider",
|
|
providerPlaceholder: "Select a provider",
|
|
noProviders: "No providers are available on this host",
|
|
modelLabel: "Model",
|
|
noModels: "This provider has no models to choose from",
|
|
modeLabel: "Mode",
|
|
noModes: "This provider has no modes to choose from",
|
|
thinkingLabel: "Thinking",
|
|
noThinkingOptions: "This model has no thinking levels",
|
|
featuresLabel: "Features",
|
|
featureCount: "{{count}} features",
|
|
featureCountOne: "{{count}} feature",
|
|
notesLabel: "Notes for agents",
|
|
notesPlaceholder: "Use for UI work — components, layout and design tokens.",
|
|
notesHint:
|
|
"Returned by the list_profiles MCP tool. Write it as an instruction to another agent.",
|
|
save: "Save",
|
|
saving: "Saving...",
|
|
remove: "Remove",
|
|
removeConfirmTitle: "Remove profile?",
|
|
removeConfirmMessage: 'Remove "{{name}}"?',
|
|
moveUp: "Move up",
|
|
moveDown: "Move down",
|
|
},
|
|
daemon: {
|
|
rename: {
|
|
editLabel: "Edit label",
|
|
title: "Rename host",
|
|
placeholder: "My Host",
|
|
submit: "Rename",
|
|
},
|
|
restart: {
|
|
title: "Restart daemon",
|
|
hint: "Restarts the daemon process. The app will reconnect automatically",
|
|
confirm: "Restart",
|
|
confirmTitle: "Restart {{name}}",
|
|
confirmMessage:
|
|
"This will restart the daemon. Agents running on it will keep going; the app will reconnect automatically.",
|
|
restarting: "Restarting...",
|
|
unableToReconnectTitle: "Unable to reconnect",
|
|
unableToReconnectMessage:
|
|
"{{name}} did not come back online. Please verify it restarted.",
|
|
unavailableTitle: "Host unavailable",
|
|
unavailableMessage:
|
|
"This host is not connected. Wait for it to come online before restarting.",
|
|
offlineTitle: "Host offline",
|
|
offlineMessage:
|
|
"This host is offline. Paseo reconnects automatically-wait until it's back online before restarting.",
|
|
requestFailedTitle: "Error",
|
|
requestFailedMessage:
|
|
"Failed to send the restart request. Paseo reconnects automatically-try again once the host shows as online.",
|
|
dialogFailedMessage: "Unable to open the restart confirmation dialog.",
|
|
},
|
|
update: {
|
|
desktopManagedHint:
|
|
"This daemon is managed by Paseo Desktop. Update Paseo Desktop on the host.",
|
|
title: "Update daemon",
|
|
hint: "Update the daemon to the latest version and restart it",
|
|
confirm: "Update",
|
|
confirmTitle: "Update {{name}}",
|
|
confirmMessage:
|
|
"This will update the daemon to the latest version and restart it. Running agents will be briefly interrupted.",
|
|
updating: "Updating...",
|
|
phaseStarting: "Preparing update...",
|
|
phaseDownloading: "Downloading packages...",
|
|
phaseInstalling: "Installing...",
|
|
phaseComplete: "Update complete, restarting...",
|
|
unableToReconnectTitle: "Unable to reconnect",
|
|
unableToReconnectMessage:
|
|
"{{name}} did not come back online after updating. Please verify the daemon restarted.",
|
|
unavailableTitle: "Host unavailable",
|
|
unavailableMessage:
|
|
"This host is not connected. Wait for it to come online before updating.",
|
|
offlineTitle: "Host offline",
|
|
offlineMessage: "This host is offline. Wait until it is back online before updating.",
|
|
requestFailedTitle: "Update failed",
|
|
requestFailedMessage: "Failed to update the daemon: {{error}}",
|
|
dialogFailedMessage: "Unable to open the update confirmation dialog.",
|
|
},
|
|
dangerZone: "Danger zone",
|
|
remove: {
|
|
title: "Remove host",
|
|
localTitle: "Remove localhost connection",
|
|
hint: "Removes this host and its saved connections from this device",
|
|
localHint: "Removes localhost from this device and stops the built-in daemon",
|
|
localConfirmTitle: "Remove localhost connection and stop daemon?",
|
|
confirmMessage: "Remove {{name}}? This will delete its saved connections.",
|
|
localConfirmMessage:
|
|
"This will remove the localhost connection, turn off built-in daemon management, and stop the managed daemon. Remote hosts remain connected.",
|
|
errorTitle: "Error",
|
|
errorMessage: "Unable to remove host",
|
|
localErrorMessage: "Unable to remove localhost connection",
|
|
},
|
|
},
|
|
},
|
|
providers: {
|
|
title: "Providers",
|
|
addProvider: "Add provider",
|
|
providerDetails: "{{name}} provider details",
|
|
enableProvider: "Enable {{name}}",
|
|
unavailable: "Connect to this host to see providers",
|
|
loading: "Loading...",
|
|
addErrorTitle: "Unable to add provider",
|
|
updateErrorTitle: "Unable to update provider",
|
|
actions: {
|
|
menu: "{{name}} actions",
|
|
remove: "Remove provider",
|
|
removing: "Removing...",
|
|
},
|
|
remove: {
|
|
confirmTitle: "Remove {{name}}?",
|
|
confirmMessage: "This deletes the provider entry from config.json. It cannot be undone.",
|
|
confirm: "Remove",
|
|
errorTitle: "Unable to remove provider",
|
|
},
|
|
statuses: {
|
|
disabled: "Disabled",
|
|
loading: "Loading",
|
|
error: "Error",
|
|
available: "Available",
|
|
notInstalled: "Not installed",
|
|
},
|
|
models: {
|
|
one: "1 model",
|
|
many: "{{count}} models",
|
|
addModel: "Add model",
|
|
addCustomTitle: "Add custom model",
|
|
modelId: "Model ID",
|
|
modelIdPlaceholder: "e.g. openai/gpt-5",
|
|
add: "Add",
|
|
adding: "Adding...",
|
|
failedToSave: "Failed to save model",
|
|
removeModel: "Remove {{id}}",
|
|
searchPlaceholder: "Search models",
|
|
loading: "Loading models...",
|
|
retry: "Retry",
|
|
retrying: "Retrying...",
|
|
noSearchMatches: "No models match your search",
|
|
noneDetected: "No models detected",
|
|
discovered: "Discovered",
|
|
custom: "Custom models",
|
|
updated: "Updated {{time}}",
|
|
},
|
|
diagnostic: {
|
|
title: "Diagnostic",
|
|
button: "Diagnostic",
|
|
refresh: "Refresh",
|
|
refreshing: "Refreshing...",
|
|
copyLabel: "diagnostic",
|
|
copyAccessibility: "Copy diagnostic",
|
|
copyFailed: "Failed to copy diagnostic",
|
|
refreshAccessibility: "Refresh diagnostic",
|
|
refreshingAccessibility: "Refreshing diagnostic",
|
|
running: "Running diagnostic...",
|
|
none: "No diagnostic available",
|
|
failedToFetch: "Failed to fetch diagnostic",
|
|
unknownError: "Unknown error",
|
|
},
|
|
},
|
|
project: {
|
|
noEditableTarget: "This project isn't editable on this host.",
|
|
backToProjects: "Back to projects",
|
|
edit: {
|
|
title: "Edit project",
|
|
name: "Name",
|
|
nameLabel: "Project name",
|
|
icon: "Icon",
|
|
chooseImage: "Choose image",
|
|
useAutomatic: "Use automatic",
|
|
imageUrl: "Image or website URL",
|
|
save: "Save changes",
|
|
savedToast: "Project updated",
|
|
},
|
|
readFailures: {
|
|
invalidTitle: "paseo.json couldn't be parsed",
|
|
invalidDescription: "Fix the file on disk, then reload.",
|
|
missingTitle: "This host doesn't have this project",
|
|
missingSingleHost: "The selected host has no record of this project.",
|
|
transportTitle: "Couldn't load paseo.json",
|
|
transportFallback: "The host didn't respond.",
|
|
failedTitle: "Couldn't load paseo.json",
|
|
failedDescription: "Reload to try again.",
|
|
},
|
|
worktree: {
|
|
title: "Worktree lifecycle hooks",
|
|
info: "Commands that run when a worktree is created or torn down for this project",
|
|
docs: "Docs",
|
|
docsTooltip:
|
|
"See docs for more details and the environment variables available to these commands",
|
|
setup: "Setup",
|
|
setupAccessibility: "Worktree setup commands",
|
|
uncommittedTitle: "Commit paseo.json changes",
|
|
uncommittedDescription:
|
|
"New worktrees use the setup script from the base branch you select.",
|
|
teardown: "Teardown",
|
|
teardownAccessibility: "Worktree teardown commands",
|
|
},
|
|
scripts: {
|
|
title: "Scripts",
|
|
info: "Long-running services and one-off commands you can launch from any agent in this project",
|
|
empty: "No scripts yet.",
|
|
untitled: "Untitled script",
|
|
port: "port {{port}}",
|
|
menuAccessibility: "Open script menu",
|
|
removeTitle: "Remove script?",
|
|
removeMessage: "Remove {{name}}?",
|
|
removeFallbackName: "this script",
|
|
name: "Name",
|
|
command: "Command",
|
|
nameAccessibility: "Script name",
|
|
commandAccessibility: "Script command",
|
|
nameRequired: "Name is required",
|
|
commandRequired: "Command is required",
|
|
newScript: "New script",
|
|
editScript: "Edit {{name}}",
|
|
runAsService: "Run as a service",
|
|
serviceHint: "Paseo supervises the process and assigns a port via $PASEO_PORT",
|
|
actions: {
|
|
add: "Add script",
|
|
edit: "Edit",
|
|
remove: "Remove",
|
|
},
|
|
},
|
|
metadata: {
|
|
title: "Metadata generation",
|
|
info: "Project-specific instructions injected into the AI prompts Paseo uses to generate metadata - use them to enforce your team's conventions like branch naming, commit style, or PR format",
|
|
branchName: "Branch names",
|
|
branchNamePlaceholder: "Prefix branches with feat/ or fix/, mb/ for personal branches",
|
|
commitMessage: "Commit messages",
|
|
commitMessagePlaceholder: "Use Conventional Commits with a scope",
|
|
pullRequest: "Pull requests",
|
|
pullRequestPlaceholder: "Lead with a one-paragraph summary, include a Test plan section",
|
|
},
|
|
writeFailures: {
|
|
staleTitle: "Config changed on disk",
|
|
staleDescription: "Reload to fetch the latest paseo.json before saving.",
|
|
failedTitle: "Couldn't save paseo.json",
|
|
failedDescription: "Try again, or reload the latest version from disk.",
|
|
},
|
|
actions: {
|
|
reload: "Reload",
|
|
tryAgain: "Try again",
|
|
save: "Save",
|
|
saved: "Project saved",
|
|
saving: "Saving...",
|
|
cancel: "Cancel",
|
|
},
|
|
},
|
|
},
|
|
} as const;
|
|
|
|
type WidenStringLeaves<T> = {
|
|
[K in keyof T]: T[K] extends string ? string : WidenStringLeaves<T[K]>;
|
|
};
|
|
|
|
export type TranslationResources = WidenStringLeaves<typeof en>;
|