mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-01 06:42:26 +00:00
chore: format code
This commit is contained in:
@@ -115,11 +115,7 @@ export function tui(input: {
|
||||
render(
|
||||
() => {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallback={(error, reset) => (
|
||||
<ErrorComponent error={error} reset={reset} onExit={onExit} />
|
||||
)}
|
||||
>
|
||||
<ErrorBoundary fallback={(error, reset) => <ErrorComponent error={error} reset={reset} onExit={onExit} />}>
|
||||
<ExitProvider onExit={onExit}>
|
||||
<KVProvider>
|
||||
<ToastProvider>
|
||||
@@ -413,12 +409,7 @@ function App() {
|
||||
flexShrink={0}
|
||||
>
|
||||
<box flexDirection="row">
|
||||
<box
|
||||
flexDirection="row"
|
||||
backgroundColor={theme.backgroundElement}
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
>
|
||||
<box flexDirection="row" backgroundColor={theme.backgroundElement} paddingLeft={1} paddingRight={1}>
|
||||
<text fg={theme.textMuted}>open</text>
|
||||
<text fg={theme.text} attributes={TextAttributes.BOLD}>
|
||||
code{" "}
|
||||
@@ -434,11 +425,7 @@ function App() {
|
||||
tab
|
||||
</text>
|
||||
<text fg={local.agent.color(local.agent.current().name)}>{""}</text>
|
||||
<text
|
||||
bg={local.agent.color(local.agent.current().name)}
|
||||
fg={theme.background}
|
||||
wrapMode={undefined}
|
||||
>
|
||||
<text bg={local.agent.color(local.agent.current().name)} fg={theme.background} wrapMode={undefined}>
|
||||
<span style={{ bold: true }}> {local.agent.current().name.toUpperCase()}</span>
|
||||
<span> AGENT </span>
|
||||
</text>
|
||||
|
||||
@@ -52,11 +52,7 @@ export function DialogModel() {
|
||||
description: provider.name,
|
||||
category: provider.name,
|
||||
})),
|
||||
filter(
|
||||
(x) =>
|
||||
Boolean(ref()?.filter) ||
|
||||
!local.model.recent().find((y) => isDeepEqual(y, x.value)),
|
||||
),
|
||||
filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,9 +20,7 @@ export function DialogSessionList() {
|
||||
|
||||
const deleteKeybind = "ctrl+d"
|
||||
|
||||
const currentSessionID = createMemo(() =>
|
||||
route.data.type === "session" ? route.data.sessionID : undefined,
|
||||
)
|
||||
const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
|
||||
|
||||
const options = createMemo(() => {
|
||||
const today = new Date().toDateString()
|
||||
|
||||
@@ -77,10 +77,7 @@ export function DialogStatus() {
|
||||
</For>
|
||||
</box>
|
||||
)}
|
||||
<Show
|
||||
when={enabledFormatters().length > 0}
|
||||
fallback={<text fg={theme.text}>No Formatters</text>}
|
||||
>
|
||||
<Show when={enabledFormatters().length > 0} fallback={<text fg={theme.text}>No Formatters</text>}>
|
||||
<box>
|
||||
<text fg={theme.text}>{enabledFormatters().length} Formatters</text>
|
||||
<For each={enabledFormatters()}>
|
||||
|
||||
@@ -3,19 +3,9 @@ import { TextAttributes } from "@opentui/core"
|
||||
import { For } from "solid-js"
|
||||
import { useTheme } from "@tui/context/theme"
|
||||
|
||||
const LOGO_LEFT = [
|
||||
` `,
|
||||
`█▀▀█ █▀▀█ █▀▀█ █▀▀▄`,
|
||||
`█░░█ █░░█ █▀▀▀ █░░█`,
|
||||
`▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`,
|
||||
]
|
||||
const LOGO_LEFT = [` `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`]
|
||||
|
||||
const LOGO_RIGHT = [
|
||||
` ▄ `,
|
||||
`█▀▀▀ █▀▀█ █▀▀█ █▀▀█`,
|
||||
`█░░░ █░░█ █░░█ █▀▀▀`,
|
||||
`▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`,
|
||||
]
|
||||
const LOGO_RIGHT = [` ▄ `, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`]
|
||||
|
||||
export function Logo() {
|
||||
const { theme } = useTheme()
|
||||
|
||||
@@ -83,12 +83,7 @@ export function Autocomplete(props: {
|
||||
const extmarkStart = store.index
|
||||
const extmarkEnd = extmarkStart + Bun.stringWidth(virtualText)
|
||||
|
||||
const styleId =
|
||||
part.type === "file"
|
||||
? props.fileStyleId
|
||||
: part.type === "agent"
|
||||
? props.agentStyleId
|
||||
: undefined
|
||||
const styleId = part.type === "file" ? props.fileStyleId : part.type === "agent" ? props.agentStyleId : undefined
|
||||
|
||||
const extmarkId = input.extmarks.create({
|
||||
start: extmarkStart,
|
||||
@@ -185,9 +180,7 @@ export function Autocomplete(props: {
|
||||
)
|
||||
})
|
||||
|
||||
const session = createMemo(() =>
|
||||
props.sessionID ? sync.session.get(props.sessionID) : undefined,
|
||||
)
|
||||
const session = createMemo(() => (props.sessionID ? sync.session.get(props.sessionID) : undefined))
|
||||
const commands = createMemo((): AutocompleteOption[] => {
|
||||
const results: AutocompleteOption[] = []
|
||||
const s = session()
|
||||
@@ -324,9 +317,7 @@ export function Autocomplete(props: {
|
||||
|
||||
const options = createMemo(() => {
|
||||
const mixed: AutocompleteOption[] = (
|
||||
store.visible === "@"
|
||||
? [...agents(), ...(files.loading ? files.latest || [] : files())]
|
||||
: [...commands()]
|
||||
store.visible === "@" ? [...agents(), ...(files.loading ? files.latest || [] : files())] : [...commands()]
|
||||
).filter((x) => x.disabled !== true)
|
||||
const currentFilter = filter()
|
||||
if (!currentFilter) return mixed.slice(0, 10)
|
||||
@@ -393,9 +384,7 @@ export function Autocomplete(props: {
|
||||
return
|
||||
}
|
||||
// Check if a space was typed after the trigger character
|
||||
const currentText = props
|
||||
.input()
|
||||
.getTextRange(store.index + 1, props.input().cursorOffset + 1)
|
||||
const currentText = props.input().getTextRange(store.index + 1, props.input().cursorOffset + 1)
|
||||
if (currentText.includes(" ")) {
|
||||
hide()
|
||||
}
|
||||
@@ -433,13 +422,8 @@ export function Autocomplete(props: {
|
||||
if (e.name === "@") {
|
||||
const cursorOffset = props.input().cursorOffset
|
||||
const charBeforeCursor =
|
||||
cursorOffset === 0
|
||||
? undefined
|
||||
: props.input().getTextRange(cursorOffset - 1, cursorOffset)
|
||||
const canTrigger =
|
||||
charBeforeCursor === undefined ||
|
||||
charBeforeCursor === "" ||
|
||||
/\s/.test(charBeforeCursor)
|
||||
cursorOffset === 0 ? undefined : props.input().getTextRange(cursorOffset - 1, cursorOffset)
|
||||
const canTrigger = charBeforeCursor === undefined || charBeforeCursor === "" || /\s/.test(charBeforeCursor)
|
||||
if (canTrigger) show("@")
|
||||
}
|
||||
|
||||
@@ -487,10 +471,7 @@ export function Autocomplete(props: {
|
||||
{option.display}
|
||||
</text>
|
||||
<Show when={option.description}>
|
||||
<text
|
||||
fg={index() === store.selected ? theme.background : theme.textMuted}
|
||||
wrapMode="none"
|
||||
>
|
||||
<text fg={index() === store.selected ? theme.background : theme.textMuted} wrapMode="none">
|
||||
{option.description}
|
||||
</text>
|
||||
</Show>
|
||||
|
||||
@@ -334,9 +334,7 @@ export function Prompt(props: PromptProps) {
|
||||
|
||||
// Expand pasted text inline before submitting
|
||||
const allExtmarks = input.extmarks.getAllForTypeId(promptPartTypeId)
|
||||
const sortedExtmarks = allExtmarks.sort(
|
||||
(a: { start: number }, b: { start: number }) => b.start - a.start,
|
||||
)
|
||||
const sortedExtmarks = allExtmarks.sort((a: { start: number }, b: { start: number }) => b.start - a.start)
|
||||
|
||||
for (const extmark of sortedExtmarks) {
|
||||
const partIndex = store.extmarkToPartIndex.get(extmark.id)
|
||||
@@ -499,28 +497,15 @@ export function Prompt(props: PromptProps) {
|
||||
<box
|
||||
flexDirection="row"
|
||||
{...SplitBorder}
|
||||
borderColor={
|
||||
keybind.leader ? theme.accent : store.mode === "shell" ? theme.secondary : theme.border
|
||||
}
|
||||
borderColor={keybind.leader ? theme.accent : store.mode === "shell" ? theme.secondary : theme.border}
|
||||
justifyContent="space-evenly"
|
||||
>
|
||||
<box
|
||||
backgroundColor={theme.backgroundElement}
|
||||
width={3}
|
||||
height="100%"
|
||||
alignItems="center"
|
||||
paddingTop={1}
|
||||
>
|
||||
<box backgroundColor={theme.backgroundElement} width={3} height="100%" alignItems="center" paddingTop={1}>
|
||||
<text attributes={TextAttributes.BOLD} fg={theme.primary}>
|
||||
{store.mode === "normal" ? ">" : "!"}
|
||||
</text>
|
||||
</box>
|
||||
<box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
backgroundColor={theme.backgroundElement}
|
||||
flexGrow={1}
|
||||
>
|
||||
<box paddingTop={1} paddingBottom={1} backgroundColor={theme.backgroundElement} flexGrow={1}>
|
||||
<textarea
|
||||
placeholder={
|
||||
props.showPlaceholder
|
||||
@@ -575,10 +560,7 @@ export function Prompt(props: PromptProps) {
|
||||
return
|
||||
}
|
||||
if (store.mode === "shell") {
|
||||
if (
|
||||
(e.name === "backspace" && input.visualCursor.offset === 0) ||
|
||||
e.name === "escape"
|
||||
) {
|
||||
if ((e.name === "backspace" && input.visualCursor.offset === 0) || e.name === "escape") {
|
||||
setStore("mode", "normal")
|
||||
e.preventDefault()
|
||||
return
|
||||
@@ -588,8 +570,7 @@ export function Prompt(props: PromptProps) {
|
||||
if (!autocomplete.visible) {
|
||||
if (
|
||||
(keybind.match("history_previous", e) && input.cursorOffset === 0) ||
|
||||
(keybind.match("history_next", e) &&
|
||||
input.cursorOffset === input.plainText.length)
|
||||
(keybind.match("history_next", e) && input.cursorOffset === input.plainText.length)
|
||||
) {
|
||||
const direction = keybind.match("history_previous", e) ? -1 : 1
|
||||
const item = history.move(direction, input.plainText)
|
||||
@@ -605,12 +586,8 @@ export function Prompt(props: PromptProps) {
|
||||
return
|
||||
}
|
||||
|
||||
if (keybind.match("history_previous", e) && input.visualCursor.visualRow === 0)
|
||||
input.cursorOffset = 0
|
||||
if (
|
||||
keybind.match("history_next", e) &&
|
||||
input.visualCursor.visualRow === input.height - 1
|
||||
)
|
||||
if (keybind.match("history_previous", e) && input.visualCursor.visualRow === 0) input.cursorOffset = 0
|
||||
if (keybind.match("history_next", e) && input.visualCursor.visualRow === input.height - 1)
|
||||
input.cursorOffset = input.plainText.length
|
||||
}
|
||||
}}
|
||||
@@ -701,12 +678,7 @@ export function Prompt(props: PromptProps) {
|
||||
syntaxStyle={syntax()}
|
||||
/>
|
||||
</box>
|
||||
<box
|
||||
backgroundColor={theme.backgroundElement}
|
||||
width={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
></box>
|
||||
<box backgroundColor={theme.backgroundElement} width={1} justifyContent="center" alignItems="center"></box>
|
||||
</box>
|
||||
<box flexDirection="row" justifyContent="space-between">
|
||||
<text flexShrink={0} wrapMode="none" fg={theme.text}>
|
||||
@@ -727,8 +699,7 @@ export function Prompt(props: PromptProps) {
|
||||
<Match when={props.hint}>{props.hint!}</Match>
|
||||
<Match when={true}>
|
||||
<text fg={theme.text}>
|
||||
{keybind.print("command_list")}{" "}
|
||||
<span style={{ fg: theme.textMuted }}>commands</span>
|
||||
{keybind.print("command_list")} <span style={{ fg: theme.textMuted }}>commands</span>
|
||||
</text>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
@@ -22,9 +22,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
return !!provider?.models[model.modelID]
|
||||
}
|
||||
|
||||
function getFirstValidModel(
|
||||
...modelFns: (() => { providerID: string; modelID: string } | undefined)[]
|
||||
) {
|
||||
function getFirstValidModel(...modelFns: (() => { providerID: string; modelID: string } | undefined)[]) {
|
||||
for (const modelFn of modelFns) {
|
||||
const model = modelFn()
|
||||
if (!model) continue
|
||||
@@ -213,9 +211,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
||||
const current = currentModel()
|
||||
if (!current) return
|
||||
const recent = modelStore.recent
|
||||
const index = recent.findIndex(
|
||||
(x) => x.providerID === current.providerID && x.modelID === current.modelID,
|
||||
)
|
||||
const index = recent.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
|
||||
if (index === -1) return
|
||||
let next = index + direction
|
||||
if (next < 0) next = recent.length - 1
|
||||
|
||||
@@ -146,12 +146,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
}
|
||||
const result = Binary.search(messages, event.properties.info.id, (m) => m.id)
|
||||
if (result.found) {
|
||||
setStore(
|
||||
"message",
|
||||
event.properties.info.sessionID,
|
||||
result.index,
|
||||
reconcile(event.properties.info),
|
||||
)
|
||||
setStore("message", event.properties.info.sessionID, result.index, reconcile(event.properties.info))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
@@ -186,12 +181,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||
}
|
||||
const result = Binary.search(parts, event.properties.part.id, (p) => p.id)
|
||||
if (result.found) {
|
||||
setStore(
|
||||
"part",
|
||||
event.properties.part.messageID,
|
||||
result.index,
|
||||
reconcile(event.properties.part),
|
||||
)
|
||||
setStore("part", event.properties.part.messageID, result.index, reconcile(event.properties.part))
|
||||
break
|
||||
}
|
||||
setStore(
|
||||
|
||||
@@ -528,13 +528,7 @@ function generateSyntax(theme: Theme) {
|
||||
},
|
||||
},
|
||||
{
|
||||
scope: [
|
||||
"variable.builtin",
|
||||
"type.builtin",
|
||||
"function.builtin",
|
||||
"module.builtin",
|
||||
"constant.builtin",
|
||||
],
|
||||
scope: ["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin"],
|
||||
style: {
|
||||
foreground: theme.error,
|
||||
},
|
||||
|
||||
@@ -26,11 +26,7 @@ export function Home() {
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<span style={{ fg: theme.success }}>•</span>{" "}
|
||||
{Locale.pluralize(
|
||||
Object.values(sync.data.mcp).length,
|
||||
"{} mcp server",
|
||||
"{} mcp servers",
|
||||
)}
|
||||
{Locale.pluralize(Object.values(sync.data.mcp).length, "{} mcp server", "{} mcp servers")}
|
||||
</Match>
|
||||
</Switch>
|
||||
</text>
|
||||
@@ -39,14 +35,7 @@ export function Home() {
|
||||
)
|
||||
|
||||
return (
|
||||
<box
|
||||
flexGrow={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
gap={1}
|
||||
>
|
||||
<box flexGrow={1} justifyContent="center" alignItems="center" paddingLeft={2} paddingRight={2} gap={1}>
|
||||
<Logo />
|
||||
<box width={39}>
|
||||
<HelpRow keybind="command_list">Commands</HelpRow>
|
||||
|
||||
@@ -7,9 +7,7 @@ import { useRoute } from "@tui/context/route"
|
||||
export function DialogMessage(props: { messageID: string; sessionID: string }) {
|
||||
const sync = useSync()
|
||||
const sdk = useSDK()
|
||||
const message = createMemo(() =>
|
||||
sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID),
|
||||
)
|
||||
const message = createMemo(() => sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID))
|
||||
const route = useRoute()
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,9 +19,7 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s
|
||||
const result = [] as DialogSelectOption<string>[]
|
||||
for (const message of messages) {
|
||||
if (message.role !== "user") continue
|
||||
const part = (sync.data.part[message.id] ?? []).find(
|
||||
(x) => x.type === "text" && !x.synthetic,
|
||||
) as TextPart
|
||||
const part = (sync.data.part[message.id] ?? []).find((x) => x.type === "text" && !x.synthetic) as TextPart
|
||||
if (!part) continue
|
||||
result.push({
|
||||
title: part.text.replace(/\n/g, " "),
|
||||
@@ -35,11 +33,5 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s
|
||||
return result
|
||||
})
|
||||
|
||||
return (
|
||||
<DialogSelect
|
||||
onMove={(option) => props.onMove(option.value)}
|
||||
title="Timeline"
|
||||
options={options()}
|
||||
/>
|
||||
)
|
||||
return <DialogSelect onMove={(option) => props.onMove(option.value)} title="Timeline" options={options()} />
|
||||
}
|
||||
|
||||
@@ -46,16 +46,10 @@ export function Header() {
|
||||
})
|
||||
|
||||
const context = createMemo(() => {
|
||||
const last = messages().findLast(
|
||||
(x) => x.role === "assistant" && x.tokens.output > 0,
|
||||
) as AssistantMessage
|
||||
const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
|
||||
if (!last) return
|
||||
const total =
|
||||
last.tokens.input +
|
||||
last.tokens.output +
|
||||
last.tokens.reasoning +
|
||||
last.tokens.cache.read +
|
||||
last.tokens.cache.write
|
||||
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
|
||||
const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID]
|
||||
let result = total.toLocaleString()
|
||||
if (model?.limit.context) {
|
||||
@@ -67,13 +61,7 @@ export function Header() {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<box
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
{...SplitBorder}
|
||||
borderColor={theme.backgroundElement}
|
||||
flexShrink={0}
|
||||
>
|
||||
<box paddingLeft={1} paddingRight={1} {...SplitBorder} borderColor={theme.backgroundElement} flexShrink={0}>
|
||||
<Show
|
||||
when={shareEnabled()}
|
||||
fallback={
|
||||
|
||||
@@ -19,14 +19,7 @@ import { SplitBorder } from "@tui/component/border"
|
||||
import { useTheme } from "@tui/context/theme"
|
||||
import { BoxRenderable, ScrollBoxRenderable, addDefaultParsers } from "@opentui/core"
|
||||
import { Prompt, type PromptRef } from "@tui/component/prompt"
|
||||
import type {
|
||||
AssistantMessage,
|
||||
Part,
|
||||
ToolPart,
|
||||
UserMessage,
|
||||
TextPart,
|
||||
ReasoningPart,
|
||||
} from "@opencode-ai/sdk"
|
||||
import type { AssistantMessage, Part, ToolPart, UserMessage, TextPart, ReasoningPart } from "@opencode-ai/sdk"
|
||||
import { useLocal } from "@tui/context/local"
|
||||
import { Locale } from "@/util/locale"
|
||||
import type { Tool } from "@/tool/tool"
|
||||
@@ -41,13 +34,7 @@ import type { EditTool } from "@/tool/edit"
|
||||
import type { PatchTool } from "@/tool/patch"
|
||||
import type { WebFetchTool } from "@/tool/webfetch"
|
||||
import type { TaskTool } from "@/tool/task"
|
||||
import {
|
||||
useKeyboard,
|
||||
useRenderer,
|
||||
useTerminalDimensions,
|
||||
type BoxProps,
|
||||
type JSX,
|
||||
} from "@opentui/solid"
|
||||
import { useKeyboard, useRenderer, useTerminalDimensions, type BoxProps, type JSX } from "@opentui/solid"
|
||||
import { useSDK } from "@tui/context/sdk"
|
||||
import { useCommandDialog } from "@tui/component/dialog-command"
|
||||
import { Shimmer } from "@tui/ui/shimmer"
|
||||
@@ -653,14 +640,7 @@ export function Session() {
|
||||
conceal,
|
||||
}}
|
||||
>
|
||||
<box
|
||||
flexDirection="row"
|
||||
paddingBottom={1}
|
||||
paddingTop={1}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
gap={2}
|
||||
>
|
||||
<box flexDirection="row" paddingBottom={1} paddingTop={1} paddingLeft={2} paddingRight={2} gap={2}>
|
||||
<box flexGrow={1} gap={1}>
|
||||
<Show when={session()}>
|
||||
<Show when={session().parentID}>
|
||||
@@ -675,19 +655,13 @@ export function Session() {
|
||||
paddingRight={2}
|
||||
>
|
||||
<text fg={theme.text}>
|
||||
Previous{" "}
|
||||
<span style={{ fg: theme.textMuted }}>
|
||||
{keybind.print("session_child_cycle_reverse")}
|
||||
</span>
|
||||
Previous <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
|
||||
</text>
|
||||
<text fg={theme.text}>
|
||||
<b>Viewing subagent session</b>
|
||||
</text>
|
||||
<text fg={theme.text}>
|
||||
<span style={{ fg: theme.textMuted }}>
|
||||
{keybind.print("session_child_cycle")}
|
||||
</span>{" "}
|
||||
Next
|
||||
<span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span> Next
|
||||
</text>
|
||||
</box>
|
||||
</Show>
|
||||
@@ -743,18 +717,12 @@ export function Session() {
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingLeft={2}
|
||||
backgroundColor={
|
||||
hover() ? theme.backgroundElement : theme.backgroundPanel
|
||||
}
|
||||
backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel}
|
||||
>
|
||||
<text fg={theme.textMuted}>{revert()!.reverted.length} message reverted</text>
|
||||
<text fg={theme.textMuted}>
|
||||
{revert()!.reverted.length} message reverted
|
||||
</text>
|
||||
<text fg={theme.textMuted}>
|
||||
<span style={{ fg: theme.text }}>
|
||||
{keybind.print("messages_redo")}
|
||||
</span>{" "}
|
||||
or /redo to restore
|
||||
<span style={{ fg: theme.text }}>{keybind.print("messages_redo")}</span> or /redo to
|
||||
restore
|
||||
</text>
|
||||
<Show when={revert()!.diffFiles?.length}>
|
||||
<box marginTop={1}>
|
||||
@@ -763,16 +731,10 @@ export function Session() {
|
||||
<text>
|
||||
{file.filename}
|
||||
<Show when={file.additions > 0}>
|
||||
<span style={{ fg: theme.diffAdded }}>
|
||||
{" "}
|
||||
+{file.additions}
|
||||
</span>
|
||||
<span style={{ fg: theme.diffAdded }}> +{file.additions}</span>
|
||||
</Show>
|
||||
<Show when={file.deletions > 0}>
|
||||
<span style={{ fg: theme.diffRemoved }}>
|
||||
{" "}
|
||||
-{file.deletions}
|
||||
</span>
|
||||
<span style={{ fg: theme.diffRemoved }}> -{file.deletions}</span>
|
||||
</Show>
|
||||
</text>
|
||||
)}
|
||||
@@ -792,9 +754,7 @@ export function Session() {
|
||||
index={index()}
|
||||
onMouseUp={() => {
|
||||
if (renderer.getSelection()?.getSelectedText()) return
|
||||
dialog.replace(() => (
|
||||
<DialogMessage messageID={message.id} sessionID={route.sessionID} />
|
||||
))
|
||||
dialog.replace(() => <DialogMessage messageID={message.id} sessionID={route.sessionID} />)
|
||||
}}
|
||||
message={message as UserMessage}
|
||||
parts={sync.data.part[message.id] ?? []}
|
||||
@@ -850,9 +810,7 @@ function UserMessage(props: {
|
||||
index: number
|
||||
pending?: string
|
||||
}) {
|
||||
const text = createMemo(
|
||||
() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0],
|
||||
)
|
||||
const text = createMemo(() => props.parts.flatMap((x) => (x.type === "text" && !x.synthetic ? [x] : []))[0])
|
||||
const files = createMemo(() => props.parts.flatMap((x) => (x.type === "file" ? [x] : [])))
|
||||
const sync = useSync()
|
||||
const { theme } = useTheme()
|
||||
@@ -893,14 +851,8 @@ function UserMessage(props: {
|
||||
})
|
||||
return (
|
||||
<text fg={theme.text}>
|
||||
<span style={{ bg: bg(), fg: theme.background }}>
|
||||
{" "}
|
||||
{MIME_BADGE[file.mime] ?? file.mime}{" "}
|
||||
</span>
|
||||
<span style={{ bg: theme.backgroundElement, fg: theme.textMuted }}>
|
||||
{" "}
|
||||
{file.filename}{" "}
|
||||
</span>
|
||||
<span style={{ bg: bg(), fg: theme.background }}> {MIME_BADGE[file.mime] ?? file.mime} </span>
|
||||
<span style={{ bg: theme.backgroundElement, fg: theme.textMuted }}> {file.filename} </span>
|
||||
</text>
|
||||
)
|
||||
}}
|
||||
@@ -911,16 +863,9 @@ function UserMessage(props: {
|
||||
{sync.data.config.username ?? "You"}{" "}
|
||||
<Show
|
||||
when={queued()}
|
||||
fallback={
|
||||
<span style={{ fg: theme.textMuted }}>
|
||||
({Locale.time(props.message.time.created)})
|
||||
</span>
|
||||
}
|
||||
fallback={<span style={{ fg: theme.textMuted }}>({Locale.time(props.message.time.created)})</span>}
|
||||
>
|
||||
<span style={{ bg: theme.accent, fg: theme.backgroundPanel, bold: true }}>
|
||||
{" "}
|
||||
QUEUED{" "}
|
||||
</span>
|
||||
<span style={{ bg: theme.accent, fg: theme.backgroundPanel, bold: true }}> QUEUED </span>
|
||||
</Show>
|
||||
</text>
|
||||
</box>
|
||||
@@ -960,8 +905,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
|
||||
<Show
|
||||
when={
|
||||
!props.message.time.completed ||
|
||||
(props.last &&
|
||||
props.parts.some((item) => item.type === "step-finish" && item.reason === "tool-calls"))
|
||||
(props.last && props.parts.some((item) => item.type === "step-finish" && item.reason === "tool-calls"))
|
||||
}
|
||||
>
|
||||
<box
|
||||
@@ -973,9 +917,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
|
||||
customBorderChars={SplitBorder.customBorderChars}
|
||||
borderColor={theme.backgroundElement}
|
||||
>
|
||||
<text fg={local.agent.color(props.message.mode)}>
|
||||
{Locale.titlecase(props.message.mode)}
|
||||
</text>
|
||||
<text fg={local.agent.color(props.message.mode)}>{Locale.titlecase(props.message.mode)}</text>
|
||||
<Shimmer text={`${props.message.modelID}`} color={theme.text} />
|
||||
</box>
|
||||
</Show>
|
||||
@@ -987,9 +929,7 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
|
||||
>
|
||||
<box paddingLeft={3}>
|
||||
<text marginTop={1}>
|
||||
<span style={{ fg: local.agent.color(props.message.mode) }}>
|
||||
{Locale.titlecase(props.message.mode)}
|
||||
</span>{" "}
|
||||
<span style={{ fg: local.agent.color(props.message.mode) }}>{Locale.titlecase(props.message.mode)}</span>{" "}
|
||||
<span style={{ fg: theme.textMuted }}>{props.message.modelID}</span>
|
||||
</text>
|
||||
</box>
|
||||
@@ -1016,12 +956,7 @@ function ReasoningPart(props: { part: ReasoningPart; message: AssistantMessage }
|
||||
customBorderChars={SplitBorder.customBorderChars}
|
||||
borderColor={theme.backgroundPanel}
|
||||
>
|
||||
<box
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
paddingLeft={2}
|
||||
backgroundColor={theme.backgroundPanel}
|
||||
>
|
||||
<box paddingTop={1} paddingBottom={1} paddingLeft={2} backgroundColor={theme.backgroundPanel}>
|
||||
<text fg={theme.text}>{props.part.text.trim()}</text>
|
||||
</box>
|
||||
</box>
|
||||
@@ -1261,16 +1196,10 @@ ToolRegistry.register<typeof WriteTool>({
|
||||
</ToolTitle>
|
||||
<box flexDirection="row">
|
||||
<box flexShrink={0}>
|
||||
<For each={numbers()}>
|
||||
{(value) => <text style={{ fg: theme.textMuted }}>{value}</text>}
|
||||
</For>
|
||||
<For each={numbers()}>{(value) => <text style={{ fg: theme.textMuted }}>{value}</text>}</For>
|
||||
</box>
|
||||
<box paddingLeft={1} flexGrow={1}>
|
||||
<code
|
||||
filetype={filetype(props.input.filePath!)}
|
||||
syntaxStyle={syntax()}
|
||||
content={code()}
|
||||
/>
|
||||
<code filetype={filetype(props.input.filePath!)} syntaxStyle={syntax()} content={code()} />
|
||||
</box>
|
||||
</box>
|
||||
</>
|
||||
@@ -1285,8 +1214,7 @@ ToolRegistry.register<typeof GlobTool>({
|
||||
return (
|
||||
<>
|
||||
<ToolTitle icon="✱" fallback="Finding files..." when={props.input.pattern}>
|
||||
Glob "{props.input.pattern}"{" "}
|
||||
<Show when={props.input.path}>in {normalizePath(props.input.path)} </Show>
|
||||
Glob "{props.input.pattern}" <Show when={props.input.path}>in {normalizePath(props.input.path)} </Show>
|
||||
<Show when={props.metadata.count}>({props.metadata.count} matches)</Show>
|
||||
</ToolTitle>
|
||||
</>
|
||||
@@ -1300,8 +1228,7 @@ ToolRegistry.register<typeof GrepTool>({
|
||||
render(props) {
|
||||
return (
|
||||
<ToolTitle icon="✱" fallback="Searching content..." when={props.input.pattern}>
|
||||
Grep "{props.input.pattern}"{" "}
|
||||
<Show when={props.input.path}>in {normalizePath(props.input.path)} </Show>
|
||||
Grep "{props.input.pattern}" <Show when={props.input.path}>in {normalizePath(props.input.path)} </Show>
|
||||
<Show when={props.metadata.matches}>({props.metadata.matches} matches)</Show>
|
||||
</ToolTitle>
|
||||
)
|
||||
@@ -1337,11 +1264,7 @@ ToolRegistry.register<typeof TaskTool>({
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToolTitle
|
||||
icon="%"
|
||||
fallback="Delegating..."
|
||||
when={props.input.subagent_type ?? props.input.description}
|
||||
>
|
||||
<ToolTitle icon="%" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}>
|
||||
Task [{props.input.subagent_type ?? "unknown"}] {props.input.description}
|
||||
</ToolTitle>
|
||||
<Show when={props.metadata.summary?.length}>
|
||||
|
||||
@@ -22,16 +22,10 @@ export function Sidebar(props: { sessionID: string }) {
|
||||
})
|
||||
|
||||
const context = createMemo(() => {
|
||||
const last = messages().findLast(
|
||||
(x) => x.role === "assistant" && x.tokens.output > 0,
|
||||
) as AssistantMessage
|
||||
const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
|
||||
if (!last) return
|
||||
const total =
|
||||
last.tokens.input +
|
||||
last.tokens.output +
|
||||
last.tokens.reasoning +
|
||||
last.tokens.cache.read +
|
||||
last.tokens.cache.write
|
||||
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
|
||||
const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID]
|
||||
return {
|
||||
tokens: total.toLocaleString(),
|
||||
@@ -84,9 +78,7 @@ export function Sidebar(props: { sessionID: string }) {
|
||||
<span style={{ fg: theme.textMuted }}>
|
||||
<Switch>
|
||||
<Match when={item.status === "connected"}>Connected</Match>
|
||||
<Match when={item.status === "failed" && item}>
|
||||
{(val) => <i>{val().error}</i>}
|
||||
</Match>
|
||||
<Match when={item.status === "failed" && item}>{(val) => <i>{val().error}</i>}</Match>
|
||||
<Match when={item.status === "disabled"}>Disabled in configuration</Match>
|
||||
</Switch>
|
||||
</span>
|
||||
@@ -162,9 +154,7 @@ export function Sidebar(props: { sessionID: string }) {
|
||||
</text>
|
||||
<For each={todo()}>
|
||||
{(todo) => (
|
||||
<text
|
||||
style={{ fg: todo.status === "in_progress" ? theme.success : theme.textMuted }}
|
||||
>
|
||||
<text style={{ fg: todo.status === "in_progress" ? theme.success : theme.textMuted }}>
|
||||
[{todo.status === "completed" ? "✓" : " "}] {todo.content}
|
||||
</text>
|
||||
)}
|
||||
|
||||
@@ -41,12 +41,7 @@ export const TuiSpawnCommand = cmd({
|
||||
)
|
||||
cwd = new URL("../../../../", import.meta.url).pathname
|
||||
} else cmd.push(process.execPath)
|
||||
cmd.push(
|
||||
"attach",
|
||||
server.url.toString(),
|
||||
"--dir",
|
||||
args.project ? path.resolve(args.project) : process.cwd(),
|
||||
)
|
||||
cmd.push("attach", server.url.toString(), "--dir", args.project ? path.resolve(args.project) : process.cwd())
|
||||
const proc = Bun.spawn({
|
||||
cmd,
|
||||
cwd,
|
||||
|
||||
@@ -99,9 +99,7 @@ export const TuiThreadCommand = cmd({
|
||||
|
||||
const worker = new Worker(workerPath, {
|
||||
env: Object.fromEntries(
|
||||
Object.entries(process.env).filter(
|
||||
(entry): entry is [string, string] => entry[1] !== undefined,
|
||||
),
|
||||
Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined),
|
||||
),
|
||||
})
|
||||
worker.onerror = console.error
|
||||
|
||||
@@ -53,9 +53,7 @@ export function DialogConfirm(props: DialogConfirmProps) {
|
||||
dialog.clear()
|
||||
}}
|
||||
>
|
||||
<text fg={key === store.active ? theme.background : theme.textMuted}>
|
||||
{Locale.titlecase(key)}
|
||||
</text>
|
||||
<text fg={key === store.active ? theme.background : theme.textMuted}>{Locale.titlecase(key)}</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
|
||||
@@ -20,17 +20,10 @@ export function DialogHelp() {
|
||||
<text fg={theme.textMuted}>esc/enter</text>
|
||||
</box>
|
||||
<box paddingBottom={1}>
|
||||
<text fg={theme.textMuted}>
|
||||
Press Ctrl+P to see all available actions and commands in any context.
|
||||
</text>
|
||||
<text fg={theme.textMuted}>Press Ctrl+P to see all available actions and commands in any context.</text>
|
||||
</box>
|
||||
<box flexDirection="row" justifyContent="flex-end" paddingBottom={1}>
|
||||
<box
|
||||
paddingLeft={3}
|
||||
paddingRight={3}
|
||||
backgroundColor={theme.primary}
|
||||
onMouseUp={() => dialog.clear()}
|
||||
>
|
||||
<box paddingLeft={3} paddingRight={3} backgroundColor={theme.primary} onMouseUp={() => dialog.clear()}>
|
||||
<text fg={theme.background}>ok</text>
|
||||
</box>
|
||||
</box>
|
||||
|
||||
@@ -57,8 +57,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
||||
const result = pipe(
|
||||
props.options,
|
||||
filter((x) => x.disabled !== true),
|
||||
(x) =>
|
||||
!needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj),
|
||||
(x) => (!needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj)),
|
||||
)
|
||||
return result
|
||||
})
|
||||
@@ -214,15 +213,11 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
||||
props.onSelect?.(option)
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
const index = filtered().findIndex((x) =>
|
||||
isDeepEqual(x.value, option.value),
|
||||
)
|
||||
const index = filtered().findIndex((x) => isDeepEqual(x.value, option.value))
|
||||
if (index === -1) return
|
||||
moveTo(index)
|
||||
}}
|
||||
backgroundColor={
|
||||
active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)
|
||||
}
|
||||
backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)}
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
gap={1}
|
||||
@@ -230,9 +225,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
||||
<Option
|
||||
title={option.title}
|
||||
footer={option.footer}
|
||||
description={
|
||||
option.description !== category ? option.description : undefined
|
||||
}
|
||||
description={option.description !== category ? option.description : undefined}
|
||||
active={active()}
|
||||
current={isDeepEqual(option.value, props.current)}
|
||||
/>
|
||||
@@ -248,9 +241,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
||||
<For each={props.keybind ?? []}>
|
||||
{(item) => (
|
||||
<text>
|
||||
<span style={{ fg: theme.text, attributes: TextAttributes.BOLD }}>
|
||||
{Keybind.toString(item.keybind)}
|
||||
</span>
|
||||
<span style={{ fg: theme.text, attributes: TextAttributes.BOLD }}>{Keybind.toString(item.keybind)}</span>
|
||||
<span style={{ fg: theme.textMuted }}> {item.title}</span>
|
||||
</text>
|
||||
)}
|
||||
@@ -284,10 +275,7 @@ function Option(props: {
|
||||
wrapMode="none"
|
||||
>
|
||||
{Locale.truncate(props.title, 62)}
|
||||
<span style={{ fg: props.active ? theme.background : theme.textMuted }}>
|
||||
{" "}
|
||||
{props.description}
|
||||
</span>
|
||||
<span style={{ fg: props.active ? theme.background : theme.textMuted }}> {props.description}</span>
|
||||
</text>
|
||||
<Show when={props.footer}>
|
||||
<box flexShrink={0}>
|
||||
|
||||
@@ -5,10 +5,7 @@ import { join } from "node:path"
|
||||
import { CliRenderer } from "@opentui/core"
|
||||
|
||||
export namespace Editor {
|
||||
export async function open(opts: {
|
||||
value: string
|
||||
renderer: CliRenderer
|
||||
}): Promise<string | undefined> {
|
||||
export async function open(opts: { value: string; renderer: CliRenderer }): Promise<string | undefined> {
|
||||
const editor = process.env["EDITOR"]
|
||||
if (!editor) return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user