fix(tui): use unicode ellipses in interface text (#45126)
This commit is contained in:
parent
d8eb3b80fb
commit
7561b4a050
15 changed files with 34 additions and 34 deletions
|
|
@ -465,7 +465,7 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
|
|||
return
|
||||
}
|
||||
|
||||
const title = session.title.length > 40 ? session.title.slice(0, 37) + "..." : session.title
|
||||
const title = session.title.length > 40 ? session.title.slice(0, 37) + "…" : session.title
|
||||
renderer.setTerminalTitle(`OC | ${title}`)
|
||||
return
|
||||
}
|
||||
|
|
@ -1051,7 +1051,7 @@ function App(props: { onSnapshot?: () => Promise<string[]>; pluginHost: TuiPlugi
|
|||
|
||||
toast.show({
|
||||
variant: "info",
|
||||
message: `Updating to v${version}...`,
|
||||
message: `Updating to v${version}…`,
|
||||
duration: 30000,
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function DialogConsoleOrg() {
|
|||
if (listed === undefined) {
|
||||
return [
|
||||
{
|
||||
title: "Loading orgs...",
|
||||
title: "Loading orgs…",
|
||||
value: "loading",
|
||||
onSelect: () => {},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
if (showError()) return []
|
||||
const data = directoryData()
|
||||
const current = currentRoot()?.directory
|
||||
if (directories.loading && !data && !current) return [{ title: "Loading project directories...", value: undefined }]
|
||||
if (directories.loading && !data && !current) return [{ title: "Loading project directories…", value: undefined }]
|
||||
const roots = [...(data ?? [])]
|
||||
if (current && !roots.some((item) => item.directory === current)) roots.unshift({ directory: current })
|
||||
roots.sort((a, b) => {
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ function AutoMethod(props: AutoMethodProps) {
|
|||
<Link href={props.authorization.url} fg={theme.primary} />
|
||||
<text fg={theme.textMuted}>{props.authorization.instructions}</text>
|
||||
</box>
|
||||
<text fg={theme.textMuted}>Waiting for authorization...</text>
|
||||
<text fg={theme.textMuted}>Waiting for authorization…</text>
|
||||
<text fg={theme.text}>
|
||||
c <span style={{ fg: theme.textMuted }}>copy</span>
|
||||
</text>
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function DialogSkill(props: DialogSkillProps) {
|
|||
return (
|
||||
<DialogSelect
|
||||
title="Skills"
|
||||
placeholder="Search skills..."
|
||||
placeholder="Search skills…"
|
||||
options={options()}
|
||||
renderFilter={!showError()}
|
||||
locked={showError()}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export function DialogWorkspaceList() {
|
|||
return {
|
||||
title:
|
||||
removing() === workspace.id
|
||||
? "Deleting..."
|
||||
? "Deleting…"
|
||||
: deleting() === workspace.id
|
||||
? `Delete ${workspace.name}? Press delete again`
|
||||
: workspace.name,
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ function buildIssueURL(message: string, stack: string) {
|
|||
// clipped trace is obvious. searchParams.set handles encoding without throwing,
|
||||
// so measuring url.toString() is both correct and safe on any input.
|
||||
const MAX_URL_LENGTH = 6000
|
||||
const marker = "\n... (truncated)"
|
||||
const marker = "\n… (truncated)"
|
||||
const head = `The opencode TUI crashed with an unexpected error.\n\n**Error:** ${message}\n\n**Stack trace:**\n`
|
||||
const setBody = (body: string) => url.searchParams.set("description", head + "```\n" + body + "\n```")
|
||||
|
||||
|
|
|
|||
|
|
@ -1313,10 +1313,10 @@ export function Prompt(props: PromptProps) {
|
|||
if (store.mode === "shell") {
|
||||
if (!shell().length) return undefined
|
||||
const example = shell()[store.placeholder % shell().length]
|
||||
return `Run a command... "${example}"`
|
||||
return `Run a command… "${example}"`
|
||||
}
|
||||
if (!list().length) return undefined
|
||||
return `Ask anything... "${list()[store.placeholder % list().length]}"`
|
||||
return `Ask anything… "${list()[store.placeholder % list().length]}"`
|
||||
})
|
||||
|
||||
const spinnerDef = createMemo(() => {
|
||||
|
|
@ -1537,7 +1537,7 @@ export function Prompt(props: PromptProps) {
|
|||
if (!r) return
|
||||
if (r.message.includes("exceeded your current quota") && r.message.includes("gemini"))
|
||||
return "gemini is way too hot right now"
|
||||
if (r.message.length > 80) return r.message.slice(0, 80) + "..."
|
||||
if (r.message.length > 80) return r.message.slice(0, 80) + "…"
|
||||
return r.message
|
||||
})
|
||||
const isTruncated = createMemo(() => {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Spinner } from "./spinner"
|
|||
export function StartupLoading(props: { ready: () => boolean }) {
|
||||
const theme = useTheme().theme
|
||||
const [show, setShow] = createSignal(false)
|
||||
const text = createMemo(() => (props.ready() ? "Finishing startup..." : "Loading plugins..."))
|
||||
const text = createMemo(() => (props.ready() ? "Finishing startup…" : "Loading plugins…"))
|
||||
let wait: NodeJS.Timeout | undefined
|
||||
let hold: NodeJS.Timeout | undefined
|
||||
let stamp = 0
|
||||
|
|
|
|||
|
|
@ -766,7 +766,7 @@ function DiffViewer(props: { api: TuiPluginApi }) {
|
|||
<Match when={diff.loading}>
|
||||
<Separator axis="x" />
|
||||
<box flexGrow={1} paddingLeft={1}>
|
||||
<text fg={theme().textMuted}>Loading diff...</text>
|
||||
<text fg={theme().textMuted}>Loading diff…</text>
|
||||
</box>
|
||||
</Match>
|
||||
<Match when={!diff.loading && files().length === 0}>
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ function Install(props: { api: TuiPluginApi }) {
|
|||
title="Install plugin"
|
||||
placeholder="npm package name"
|
||||
busy={busy()}
|
||||
busyText="Installing plugin..."
|
||||
busyText="Installing plugin…"
|
||||
description={() => (
|
||||
<box flexDirection="row" gap={1}>
|
||||
<text fg={props.api.theme.current.textMuted}>scope:</text>
|
||||
|
|
|
|||
|
|
@ -1812,7 +1812,7 @@ function GenericTool(props: ToolProps) {
|
|||
<Show
|
||||
when={props.output && ctx.showGenericToolOutput()}
|
||||
fallback={
|
||||
<InlineTool icon="⚙" pending="Writing command..." complete={true} part={props.part}>
|
||||
<InlineTool icon="⚙" pending="Writing command…" complete={true} part={props.part}>
|
||||
{props.tool} {input(props.input)}
|
||||
</InlineTool>
|
||||
}
|
||||
|
|
@ -2094,7 +2094,7 @@ function Shell(props: ToolProps) {
|
|||
</BlockTool>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<InlineTool icon="$" pending="Writing command..." complete={stringValue(props.input.command)} part={props.part}>
|
||||
<InlineTool icon="$" pending="Writing command…" complete={stringValue(props.input.command)} part={props.part}>
|
||||
{stringValue(props.input.command)}
|
||||
</InlineTool>
|
||||
</Match>
|
||||
|
|
@ -2128,7 +2128,7 @@ function Write(props: ToolProps) {
|
|||
<Match when={true}>
|
||||
<InlineTool
|
||||
icon="←"
|
||||
pending="Preparing write..."
|
||||
pending="Preparing write…"
|
||||
complete={stringValue(props.input.filePath)}
|
||||
part={props.part}
|
||||
>
|
||||
|
|
@ -2142,7 +2142,7 @@ function Write(props: ToolProps) {
|
|||
function Glob(props: ToolProps) {
|
||||
const pathFormatter = usePathFormatter()
|
||||
return (
|
||||
<InlineTool icon="✱" pending="Finding files..." complete={stringValue(props.input.pattern)} part={props.part}>
|
||||
<InlineTool icon="✱" pending="Finding files…" complete={stringValue(props.input.pattern)} part={props.part}>
|
||||
Glob "{stringValue(props.input.pattern)}"{" "}
|
||||
<Show when={stringValue(props.input.path)}>in {pathFormatter.format(stringValue(props.input.path))} </Show>
|
||||
<Show when={numberValue(props.metadata.count)}>
|
||||
|
|
@ -2167,7 +2167,7 @@ function Read(props: ToolProps) {
|
|||
<>
|
||||
<InlineTool
|
||||
icon="→"
|
||||
pending="Reading file..."
|
||||
pending="Reading file…"
|
||||
complete={stringValue(props.input.filePath)}
|
||||
spinner={isRunning()}
|
||||
part={props.part}
|
||||
|
|
@ -2190,7 +2190,7 @@ function Read(props: ToolProps) {
|
|||
function Grep(props: ToolProps) {
|
||||
const pathFormatter = usePathFormatter()
|
||||
return (
|
||||
<InlineTool icon="✱" pending="Searching content..." complete={stringValue(props.input.pattern)} part={props.part}>
|
||||
<InlineTool icon="✱" pending="Searching content…" complete={stringValue(props.input.pattern)} part={props.part}>
|
||||
Grep "{stringValue(props.input.pattern)}"{" "}
|
||||
<Show when={stringValue(props.input.path)}>in {pathFormatter.format(stringValue(props.input.path))} </Show>
|
||||
<Show when={numberValue(props.metadata.matches)}>
|
||||
|
|
@ -2202,7 +2202,7 @@ function Grep(props: ToolProps) {
|
|||
|
||||
function WebFetch(props: ToolProps) {
|
||||
return (
|
||||
<InlineTool icon="%" pending="Fetching from the web..." complete={stringValue(props.input.url)} part={props.part}>
|
||||
<InlineTool icon="%" pending="Fetching from the web…" complete={stringValue(props.input.url)} part={props.part}>
|
||||
WebFetch {stringValue(props.input.url)}
|
||||
</InlineTool>
|
||||
)
|
||||
|
|
@ -2210,7 +2210,7 @@ function WebFetch(props: ToolProps) {
|
|||
|
||||
function WebSearch(props: ToolProps) {
|
||||
return (
|
||||
<InlineTool icon="◈" pending="Searching web..." complete={stringValue(props.input.query)} part={props.part}>
|
||||
<InlineTool icon="◈" pending="Searching web…" complete={stringValue(props.input.query)} part={props.part}>
|
||||
{webSearchProviderLabel(props.metadata.provider)} "{stringValue(props.input.query)}"{" "}
|
||||
<Show when={numberValue(props.metadata.numResults)}>({numberValue(props.metadata.numResults)} results)</Show>
|
||||
</InlineTool>
|
||||
|
|
@ -2300,7 +2300,7 @@ function Task(props: ToolProps) {
|
|||
color={retry() ? theme.error : undefined}
|
||||
spinner={isRunning()}
|
||||
complete={stringValue(props.input.description)}
|
||||
pending="Delegating..."
|
||||
pending="Delegating…"
|
||||
part={props.part}
|
||||
onClick={() => {
|
||||
if (sessionID()) {
|
||||
|
|
@ -2437,7 +2437,7 @@ function Edit(props: ToolProps) {
|
|||
</BlockTool>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<InlineTool icon="←" pending="Preparing edit..." complete={stringValue(props.input.filePath)} part={props.part}>
|
||||
<InlineTool icon="←" pending="Preparing edit…" complete={stringValue(props.input.filePath)} part={props.part}>
|
||||
Edit {pathFormatter.format(stringValue(props.input.filePath))} {input({ replaceAll: props.input.replaceAll })}
|
||||
</InlineTool>
|
||||
</Match>
|
||||
|
|
@ -2513,7 +2513,7 @@ function ApplyPatch(props: ToolProps) {
|
|||
</For>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<InlineTool icon="%" pending="Preparing patch..." failure="Patch failed" complete={false} part={props.part}>
|
||||
<InlineTool icon="%" pending="Preparing patch…" failure="Patch failed" complete={false} part={props.part}>
|
||||
Patch
|
||||
</InlineTool>
|
||||
</Match>
|
||||
|
|
@ -2535,12 +2535,12 @@ function TodoWrite(props: ToolProps) {
|
|||
<Match when={true}>
|
||||
<InlineTool
|
||||
icon="⚙"
|
||||
pending="Updating todos..."
|
||||
pending="Updating todos…"
|
||||
failure="Todo update failed"
|
||||
complete={false}
|
||||
part={props.part}
|
||||
>
|
||||
Updating todos...
|
||||
Updating todos…
|
||||
</InlineTool>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
|
@ -2575,7 +2575,7 @@ function Question(props: ToolProps) {
|
|||
</BlockTool>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<InlineTool icon="→" pending="Asking questions..." complete={count()} part={props.part}>
|
||||
<InlineTool icon="→" pending="Asking questions…" complete={count()} part={props.part}>
|
||||
Asked {count()} question{count() !== 1 ? "s" : ""}
|
||||
</InlineTool>
|
||||
</Match>
|
||||
|
|
@ -2585,7 +2585,7 @@ function Question(props: ToolProps) {
|
|||
|
||||
function Skill(props: ToolProps) {
|
||||
return (
|
||||
<InlineTool icon="→" pending="Loading skill..." complete={stringValue(props.input.name)} part={props.part}>
|
||||
<InlineTool icon="→" pending="Loading skill…" complete={stringValue(props.input.name)} part={props.part}>
|
||||
Skill "{stringValue(props.input.name)}"
|
||||
</InlineTool>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -99,11 +99,11 @@ export function DialogPrompt(props: DialogPromptProps) {
|
|||
cursorStyle={tuiConfig.cursor}
|
||||
/>
|
||||
<Show when={props.busy}>
|
||||
<Spinner color={theme.textMuted}>{props.busyText ?? "Working..."}</Spinner>
|
||||
<Spinner color={theme.textMuted}>{props.busyText ?? "Working…"}</Spinner>
|
||||
</Show>
|
||||
</box>
|
||||
<box paddingBottom={1} gap={1} flexDirection="row">
|
||||
<Show when={!props.busy} fallback={<text fg={theme.textMuted}>processing...</text>}>
|
||||
<Show when={!props.busy} fallback={<text fg={theme.textMuted}>processing…</text>}>
|
||||
<Show when={submitShortcut()}>
|
||||
<text fg={theme.text}>
|
||||
{submitShortcut()} <span style={{ fg: theme.textMuted }}>submit</span>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ describe("DiffViewerFileTree", () => {
|
|||
<DiffViewerFileTree width={32} files={[]} loading={false} error={undefined} theme={theme} />
|
||||
))
|
||||
|
||||
expect(loading).not.toContain("Loading diff...")
|
||||
expect(loading).not.toContain("Loading diff…")
|
||||
expect(loading).not.toContain("No files")
|
||||
expect(failed).not.toContain("Failed to load diff")
|
||||
expect(failed).not.toContain("No files")
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ function StickyScrollFixture(props: { separated: boolean; scroll: (scroll: Scrol
|
|||
|
||||
function FailedPendingToolFixture() {
|
||||
return (
|
||||
<InlineToolRow icon="%" complete={false} pending="Preparing patch..." failed={true} failure="Patch failed">
|
||||
<InlineToolRow icon="%" complete={false} pending="Preparing patch…" failed={true} failure="Patch failed">
|
||||
Patch
|
||||
</InlineToolRow>
|
||||
)
|
||||
|
|
@ -203,7 +203,7 @@ function FailedPendingToolFixture() {
|
|||
|
||||
function FailedCompleteToolFixture() {
|
||||
return (
|
||||
<InlineToolRow icon="→" complete={true} pending="Reading file..." failed={true} failure="Read failed">
|
||||
<InlineToolRow icon="→" complete={true} pending="Reading file…" failed={true} failure="Read failed">
|
||||
Read src/index.ts
|
||||
</InlineToolRow>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue