refactor(tui): simplify config and tool display
This commit is contained in:
parent
8f904c8e9a
commit
ac1ddc3f83
44 changed files with 264 additions and 315 deletions
|
|
@ -6,7 +6,7 @@
|
|||
// history ring. All are async because they read config or hit the SDK, but
|
||||
// none block each other.
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { resolve } from "@opencode-ai/tui/config"
|
||||
import { resolve } from "@opencode-ai/tui/config/v1"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { makeGlobalNode } from "@opencode-ai/core/effect/app-node"
|
||||
import { makeRuntime } from "@opencode-ai/core/effect/runtime"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
// → OpenTUI split-footer renderer writes to terminal
|
||||
import type { OpenCodeClient, ReferenceListOutput } from "@opencode-ai/client/promise"
|
||||
import type { FilePart, PermissionRequest, QuestionRequest, ToolPart } from "@opencode-ai/sdk/v2"
|
||||
import type { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import type { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
|
||||
export type RunFilePart = {
|
||||
type: "file"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export * as TuiConfig from "./tui-config"
|
||||
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
import { Effect, FileSystem, Option, Schema } from "effect"
|
||||
import { parse, type ParseError } from "jsonc-parser"
|
||||
import path from "path"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
|
||||
import { testRender, useRenderer } from "@opentui/solid"
|
||||
import { OpencodeKeymapProvider, registerOpencodeKeymap } from "@opencode-ai/tui/keymap"
|
||||
import { resolve } from "@opencode-ai/tui/config"
|
||||
import { resolve } from "@opencode-ai/tui/config/v1"
|
||||
import { expect, test } from "bun:test"
|
||||
import { createComponent, createSignal } from "solid-js"
|
||||
import { RunFooterView } from "../src/mini/footer.view"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { Config } from "@/config/config"
|
||||
import { ConfigV1 } from "@opencode-ai/core/v1/config/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
import { Schema } from "effect"
|
||||
|
||||
type JsonSchema = Record<string, unknown>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
import { isRecord } from "@opencode-ai/tui/util/record"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { Schema } from "effect"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import path from "path"
|
|||
import { type ParseError as JsoncParseError, applyEdits, modify, parse as parseJsonc } from "jsonc-parser"
|
||||
import { unique } from "remeda"
|
||||
import { Option, Schema } from "effect"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ import { Global } from "@opencode-ai/core/global"
|
|||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import { CurrentWorkingDirectory } from "./tui-cwd"
|
||||
import { ConfigPlugin } from "@/config/plugin"
|
||||
import { TuiKeybind } from "@opencode-ai/tui/config/keybind"
|
||||
import { TuiKeybind } from "@opencode-ai/tui/config/v1/keybind"
|
||||
import { InstallationLocal, InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { makeRuntime } from "@opencode-ai/core/effect/runtime"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { ConfigVariable } from "@/config/variable"
|
||||
import { Npm } from "@opencode-ai/core/npm"
|
||||
import { FormatError, FormatUnknownError } from "@/cli/error"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
|
||||
export const Info = TuiConfig.Info
|
||||
export type Info = TuiConfig.Info
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import type { AudioPlayOptions, AudioSound } from "@opentui/core"
|
||||
import { createTuiAttention } from "@opencode-ai/tui/attention"
|
||||
import type { TuiConfig } from "@opencode-ai/tui/config"
|
||||
import type { TuiConfig } from "@opencode-ai/tui/config/v1"
|
||||
|
||||
type FocusEvent = "focus" | "blur"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { afterEach, describe, expect, mock, spyOn, test } from "bun:test"
|
||||
import { OpenCode } from "@opencode-ai/client/promise"
|
||||
import type { Resolved } from "@opencode-ai/tui/config"
|
||||
import type { Resolved } from "@opencode-ai/tui/config/v1"
|
||||
import { resolveDiffStyle, resolveModelInfo, resolveRunTuiConfig } from "@opencode-ai/cli/mini/runtime.boot"
|
||||
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { spyOn } from "bun:test"
|
||||
import path from "path"
|
||||
import { resolve, type Info, type Resolved } from "@opencode-ai/tui/config"
|
||||
import { resolve, type Info, type Resolved } from "@opencode-ai/tui/config/v1"
|
||||
import { TuiConfig } from "../../src/config/tui"
|
||||
import { TuiKeybind } from "@opencode-ai/tui/config/keybind"
|
||||
import { TuiKeybind } from "@opencode-ai/tui/config/v1/keybind"
|
||||
|
||||
type PluginSpec = string | [string, Record<string, unknown>]
|
||||
type PluginOrigin = {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@
|
|||
"exports": {
|
||||
".": "./src/index.tsx",
|
||||
"./builtins": "./src/feature-plugins/builtins.ts",
|
||||
"./config": "./src/config/index.tsx",
|
||||
"./config/v1": "./src/config/v1/index.tsx",
|
||||
"./config/v1/keybind": "./src/config/v1/keybind.ts",
|
||||
"./config/v2": "./src/config/v2/index.ts",
|
||||
"./config/v2/keybind": "./src/config/v2/keybind.ts",
|
||||
"./context/args": "./src/context/args.tsx",
|
||||
"./context/epilogue": "./src/context/epilogue.tsx",
|
||||
"./context/exit": "./src/context/exit.tsx",
|
||||
|
|
@ -30,7 +33,6 @@
|
|||
"./editor-zed": "./src/editor-zed.ts",
|
||||
"./runtime": "./src/runtime.tsx",
|
||||
"./terminal-win32": "./src/terminal-win32.ts",
|
||||
"./config/keybind": "./src/config/keybind.ts",
|
||||
"./keymap": "./src/keymap.tsx",
|
||||
"./prompt/content": "./src/prompt/content.ts",
|
||||
"./prompt/display": "./src/prompt/display.ts",
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ import * as Model from "./util/model"
|
|||
import { ArgsProvider, useArgs, type Args } from "./context/args"
|
||||
import open from "open"
|
||||
import { PromptRefProvider, usePromptRef } from "./context/prompt"
|
||||
import { TuiConfigProvider, useTuiConfig, type TuiConfig } from "./config"
|
||||
import { TuiConfigProvider, useTuiConfig, type TuiConfig } from "./config/v1"
|
||||
import { createTuiApiAdapters } from "./plugin/adapters"
|
||||
import { createTuiApi } from "./plugin/api"
|
||||
import { createPluginRuntime, PluginRuntimeProvider, usePluginRuntime, type TuiPluginHost } from "./plugin/runtime"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import type {
|
|||
TuiAttentionSoundPack,
|
||||
TuiAttentionSoundPackInfo,
|
||||
} from "@opencode-ai/plugin/tui"
|
||||
import { AttentionSoundName, type TuiConfig } from "./config"
|
||||
import { AttentionSoundName, type TuiConfig } from "./config/v1"
|
||||
import { Schema } from "effect"
|
||||
import stripAnsi from "strip-ansi"
|
||||
import * as TuiAudio from "./audio"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
useKeymapSelector,
|
||||
useOpencodeKeymap,
|
||||
} from "../keymap"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
|
||||
type PaletteCommandEntry = ReturnType<OpenTuiKeymap["getCommandEntries"]>[number]
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type { McpServer } from "@opencode-ai/client"
|
|||
import { useClipboard } from "../context/clipboard"
|
||||
import { useToast } from "../ui/toast"
|
||||
import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
import { useBindings } from "../keymap"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { InputRenderable, TextAttributes } from "@opentui/core"
|
||||
import { Slug } from "@opencode-ai/core/util/slug"
|
||||
import { createSignal, onMount } from "solid-js"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useBindings, useCommandShortcut } from "../keymap"
|
||||
import { useDialog, type DialogContext } from "../ui/dialog"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { createMemo, For } from "solid-js"
|
|||
import { createStore } from "solid-js/store"
|
||||
import { FilePath } from "../ui/file-path"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { useDialog, type DialogContext } from "../ui/dialog"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { useSDK } from "../../context/sdk"
|
|||
import { useData } from "../../context/data"
|
||||
import { getScrollAcceleration } from "../../util/scroll"
|
||||
import { useTuiPaths } from "../../context/runtime"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { useLocation } from "../../context/location"
|
||||
import { useTheme, selectedForeground } from "../../context/theme"
|
||||
import { SplitBorder } from "../../ui/border"
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import { createFadeIn } from "../../util/signal"
|
|||
import { DialogSkill } from "../dialog-skill"
|
||||
import { useArgs } from "../../context/args"
|
||||
import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut, useLeaderActive, useOpencodeKeymap } from "../../keymap"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { usePromptMove } from "./move"
|
||||
import { readLocalAttachment } from "./local-attachment"
|
||||
import { useData } from "../../context/data"
|
||||
|
|
|
|||
|
|
@ -97,8 +97,6 @@ export const Definitions = {
|
|||
session_interrupt: keybind("escape", "Interrupt current session"),
|
||||
session_background: keybind("ctrl+b", "Background blocking session tools"),
|
||||
session_compact: keybind("<leader>c", "Compact the session"),
|
||||
session_toggle_timestamps: keybind("none", "Toggle message timestamps"),
|
||||
session_toggle_generic_tool_output: keybind("none", "Toggle generic tool output"),
|
||||
session_queued_prompts: keybind("<leader>q", "Manage queued prompts"),
|
||||
session_child_first: keybind("down,<leader>down", "Toggle subagent picker"),
|
||||
session_child_cycle: keybind("right", "Go to next child session"),
|
||||
|
|
@ -146,7 +144,6 @@ export const Definitions = {
|
|||
messages_undo: keybind("<leader>u", "Undo message"),
|
||||
messages_redo: keybind("<leader>r", "Redo message"),
|
||||
messages_toggle_conceal: keybind("<leader>h", "Toggle code block concealment in messages"),
|
||||
tool_details: keybind("none", "Toggle tool details visibility"),
|
||||
display_thinking: keybind("none", "Toggle thinking blocks visibility"),
|
||||
|
||||
prompt_submit: keybind("none", "Submit prompt"),
|
||||
|
|
@ -303,8 +300,6 @@ export const CommandMap = {
|
|||
session_interrupt: "session.interrupt",
|
||||
session_background: "session.background",
|
||||
session_compact: "session.compact",
|
||||
session_toggle_timestamps: "session.toggle.timestamps",
|
||||
session_toggle_generic_tool_output: "session.toggle.generic_tool_output",
|
||||
session_queued_prompts: "session.queued_prompts",
|
||||
session_child_first: "session.child.first",
|
||||
session_child_cycle: "session.child.next",
|
||||
|
|
@ -350,7 +345,6 @@ export const CommandMap = {
|
|||
messages_undo: "session.undo",
|
||||
messages_redo: "session.redo",
|
||||
messages_toggle_conceal: "session.toggle.conceal",
|
||||
tool_details: "session.toggle.actions",
|
||||
display_thinking: "session.toggle.thinking",
|
||||
prompt_submit: "prompt.submit",
|
||||
prompt_editor_context_clear: "prompt.editor_context.clear",
|
||||
97
packages/tui/src/config/v2/index.ts
Normal file
97
packages/tui/src/config/v2/index.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
export * as TuiConfigV2 from "."
|
||||
|
||||
import { Schema } from "effect"
|
||||
import { TuiKeybind } from "./keybind"
|
||||
|
||||
export const Plugin = Schema.Union([
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
package: Schema.String,
|
||||
options: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
|
||||
}),
|
||||
])
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
theme: Schema.optional(
|
||||
Schema.Struct({
|
||||
name: Schema.optional(Schema.String),
|
||||
mode: Schema.optional(Schema.Literals(["system", "dark", "light"])),
|
||||
}),
|
||||
),
|
||||
keybinds: Schema.optional(TuiKeybind.KeybindOverrides),
|
||||
plugins: Schema.optional(Schema.Array(Plugin)),
|
||||
leader: Schema.optional(
|
||||
Schema.Struct({
|
||||
timeout: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
|
||||
}),
|
||||
),
|
||||
scroll: Schema.optional(
|
||||
Schema.Struct({
|
||||
speed: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0.001))),
|
||||
acceleration: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
attention: Schema.optional(
|
||||
Schema.Struct({
|
||||
enabled: Schema.optional(Schema.Boolean),
|
||||
notifications: Schema.optional(Schema.Boolean),
|
||||
sound: Schema.optional(Schema.Boolean),
|
||||
volume: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(1))),
|
||||
sound_pack: Schema.optional(Schema.String),
|
||||
sounds: Schema.optional(
|
||||
Schema.Record(
|
||||
Schema.Literals(["default", "question", "permission", "error", "done", "subagent_done"]),
|
||||
Schema.optionalKey(Schema.String),
|
||||
),
|
||||
),
|
||||
}),
|
||||
),
|
||||
diffs: Schema.optional(
|
||||
Schema.Struct({
|
||||
wrap: Schema.optional(Schema.Literals(["word", "none"])),
|
||||
tree: Schema.optional(Schema.Boolean),
|
||||
single: Schema.optional(Schema.Boolean),
|
||||
view: Schema.optional(Schema.Literals(["auto", "split", "unified"])),
|
||||
}),
|
||||
),
|
||||
terminal: Schema.optional(
|
||||
Schema.Struct({
|
||||
title: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
composer: Schema.optional(
|
||||
Schema.Struct({
|
||||
file_context: Schema.optional(Schema.Boolean),
|
||||
paste_summary: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
session: Schema.optional(
|
||||
Schema.Struct({
|
||||
sidebar: Schema.optional(Schema.Literals(["auto", "hide"])),
|
||||
scrollbar: Schema.optional(Schema.Boolean),
|
||||
thinking: Schema.optional(Schema.Literals(["show", "hide"])),
|
||||
group_exploration: Schema.optional(Schema.Boolean),
|
||||
directory_filter: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
which_key: Schema.optional(
|
||||
Schema.Struct({
|
||||
layout: Schema.optional(Schema.Literals(["dock", "overlay"])),
|
||||
pending_preview: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
hints: Schema.optional(
|
||||
Schema.Struct({
|
||||
tips: Schema.optional(Schema.Boolean),
|
||||
getting_started: Schema.optional(Schema.Boolean),
|
||||
}),
|
||||
),
|
||||
updates: Schema.optional(
|
||||
Schema.Struct({
|
||||
skipped: Schema.optional(Schema.String),
|
||||
}),
|
||||
),
|
||||
animations: Schema.optional(Schema.Boolean),
|
||||
mouse: Schema.optional(Schema.Boolean),
|
||||
})
|
||||
export type Info = Schema.Schema.Type<typeof Info>
|
||||
6
packages/tui/src/config/v2/keybind.ts
Normal file
6
packages/tui/src/config/v2/keybind.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export * as TuiKeybind from "./keybind"
|
||||
|
||||
import { Schema } from "effect"
|
||||
|
||||
export const KeybindOverrides = Schema.Struct({})
|
||||
export type KeybindOverrides = Schema.Schema.Type<typeof KeybindOverrides>
|
||||
|
|
@ -23,7 +23,7 @@ import { createEffect, createMemo, onCleanup, onMount } from "solid-js"
|
|||
import { createStore, produce } from "solid-js/store"
|
||||
import { createSimpleContext } from "./helper"
|
||||
import { useKV } from "./kv"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { Glob } from "@opencode-ai/core/util/glob"
|
||||
import { readFile } from "node:fs/promises"
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import {
|
|||
} from "@opentui/keymap/extras"
|
||||
import { KeymapProvider, useKeymap, useKeymapSelector, useBindings } from "@opentui/keymap/solid"
|
||||
import { createMemo, type Accessor } from "solid-js"
|
||||
import { useTuiConfig } from "./config"
|
||||
import { TuiKeybind } from "./config/keybind"
|
||||
import { useTuiConfig } from "./config/v1"
|
||||
import { TuiKeybind } from "./config/v1/keybind"
|
||||
|
||||
export const LEADER_TOKEN = "leader"
|
||||
export const OPENCODE_BASE_MODE = "base"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { TuiDialogSelectOption, TuiPluginApi, TuiSlotProps } from "@opencode-ai/plugin/tui"
|
||||
import type { TuiConfig } from "../config"
|
||||
import type { TuiConfig } from "../config/v1"
|
||||
import type { useEvent } from "../context/event"
|
||||
import type { useRoute } from "../context/route"
|
||||
import type { useSDK } from "../context/sdk"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Legacy `api.command` bridge for v1 plugins; remove in v2.
|
||||
import type { TuiCommand, TuiPluginApi } from "@opencode-ai/plugin/tui"
|
||||
import { TuiKeybind } from "../config/keybind"
|
||||
import { TuiKeybind } from "../config/v1/keybind"
|
||||
import type { DialogContext } from "../ui/dialog"
|
||||
|
||||
const COMMAND_PALETTE_SHOW = "command.palette.show"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type {
|
|||
TuiPluginInstallResult,
|
||||
TuiPluginStatus,
|
||||
} from "@opencode-ai/plugin/tui"
|
||||
import type { TuiConfig } from "../config"
|
||||
import type { TuiConfig } from "../config/v1"
|
||||
import { createContext, createSignal, useContext, type JSX, type ParentProps } from "solid-js"
|
||||
import { createPluginRoutes } from "./api"
|
||||
import { createSlots, type HostSlots } from "./slots"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { useLocal } from "../context/local"
|
|||
import { usePluginRuntime } from "../plugin/runtime"
|
||||
import { useEditorContext } from "../context/editor"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { HomeSessionDestinationProvider } from "./home/session-destination"
|
||||
import { useData } from "../context/data"
|
||||
import { LocationProvider } from "../context/location"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { useSDK } from "../../context/sdk"
|
|||
import { useClipboard } from "../../context/clipboard"
|
||||
import { SplitBorder } from "../../ui/border"
|
||||
import { useToast } from "../../ui/toast"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { useBindings, useOpencodeModeStack } from "../../keymap"
|
||||
|
||||
const FORM_MODE = "form"
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import { FormPrompt } from "./form"
|
|||
import { DialogExportOptions } from "../../ui/dialog-export-options"
|
||||
import { DialogExportResult } from "../../ui/dialog-export-result"
|
||||
import { sessionEpilogue } from "../../util/presentation"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { useClipboard } from "../../context/clipboard"
|
||||
import { nextThinkingMode, reasoningSummary, useThinkingMode, type ThinkingMode } from "../../context/thinking"
|
||||
import { getScrollAcceleration } from "../../util/scroll"
|
||||
|
|
@ -89,11 +89,8 @@ const sessionBindingCommands = [
|
|||
"session.redo",
|
||||
"session.sidebar.toggle",
|
||||
"session.toggle.conceal",
|
||||
"session.toggle.timestamps",
|
||||
"session.toggle.thinking",
|
||||
"session.toggle.actions",
|
||||
"session.toggle.scrollbar",
|
||||
"session.toggle.generic_tool_output",
|
||||
"session.toggle.exploration_grouping",
|
||||
"session.first",
|
||||
"session.last",
|
||||
|
|
@ -127,9 +124,6 @@ const context = createContext<{
|
|||
conceal: () => boolean
|
||||
thinkingMode: () => ThinkingMode
|
||||
showThinking: () => boolean
|
||||
showTimestamps: () => boolean
|
||||
showDetails: () => boolean
|
||||
showGenericToolOutput: () => boolean
|
||||
groupExploration: () => boolean
|
||||
diffWrapMode: () => "word" | "none"
|
||||
models: () => ModelInfo[]
|
||||
|
|
@ -208,13 +202,9 @@ export function Session() {
|
|||
const thinking = useThinkingMode()
|
||||
const thinkingMode = thinking.mode
|
||||
const showThinking = createMemo(() => true)
|
||||
const [timestamps, setTimestamps] = kv.signal<"hide" | "show">("timestamps", "hide")
|
||||
const [showDetails, setShowDetails] = kv.signal("tool_details_visibility", true)
|
||||
const [showAssistantMetadata, _setShowAssistantMetadata] = kv.signal("assistant_metadata_visibility", true)
|
||||
const [showScrollbar, setShowScrollbar] = kv.signal("scrollbar_visible", false)
|
||||
const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word")
|
||||
const [_animationsEnabled, _setAnimationsEnabled] = kv.signal("animations_enabled", true)
|
||||
const [showGenericToolOutput, setShowGenericToolOutput] = kv.signal("generic_tool_output_visibility", false)
|
||||
const [groupExploration, setGroupExploration] = kv.signal("exploration_grouping", true)
|
||||
|
||||
const wide = createMemo(() => dimensions().width > 120)
|
||||
|
|
@ -224,7 +214,6 @@ export function Session() {
|
|||
if (sidebar() === "auto" && wide()) return true
|
||||
return false
|
||||
})
|
||||
const showTimestamps = createMemo(() => timestamps() === "show")
|
||||
const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4)
|
||||
const models = createMemo(() => data.location.model.list(location()) ?? [])
|
||||
|
||||
|
|
@ -491,19 +480,6 @@ export function Session() {
|
|||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: showTimestamps() ? "Hide timestamps" : "Show timestamps",
|
||||
value: "session.toggle.timestamps",
|
||||
category: "Session",
|
||||
slash: {
|
||||
name: "timestamps",
|
||||
aliases: ["toggle-timestamps"],
|
||||
},
|
||||
run: () => {
|
||||
setTimestamps((prev) => (prev === "show" ? "hide" : "show"))
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: (() => {
|
||||
const next = nextThinkingMode(thinkingMode())
|
||||
|
|
@ -521,15 +497,6 @@ export function Session() {
|
|||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: showDetails() ? "Hide tool details" : "Show tool details",
|
||||
value: "session.toggle.actions",
|
||||
category: "Session",
|
||||
run: () => {
|
||||
setShowDetails((prev) => !prev)
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Toggle session scrollbar",
|
||||
value: "session.toggle.scrollbar",
|
||||
|
|
@ -539,15 +506,6 @@ export function Session() {
|
|||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: showGenericToolOutput() ? "Hide generic tool output" : "Show generic tool output",
|
||||
value: "session.toggle.generic_tool_output",
|
||||
category: "Session",
|
||||
run: () => {
|
||||
setShowGenericToolOutput((prev) => !prev)
|
||||
dialog.clear()
|
||||
},
|
||||
},
|
||||
{
|
||||
title: groupExploration() ? "Show exploration tools individually" : "Group exploration tools",
|
||||
value: "session.toggle.exploration_grouping",
|
||||
|
|
@ -727,13 +685,7 @@ export function Session() {
|
|||
try {
|
||||
const sessionData = session()
|
||||
if (!sessionData) return
|
||||
const transcript = formatSessionTranscript(
|
||||
sessionData,
|
||||
messages(),
|
||||
showThinking(),
|
||||
showDetails(),
|
||||
showAssistantMetadata(),
|
||||
)
|
||||
const transcript = formatSessionTranscript(sessionData, messages(), showThinking())
|
||||
await clipboard.write?.(transcript)
|
||||
toast.show({ message: "Session transcript copied to clipboard!", variant: "success" })
|
||||
} catch {
|
||||
|
|
@ -754,19 +706,13 @@ export function Session() {
|
|||
const sessionData = session()
|
||||
if (!sessionData) return
|
||||
|
||||
const options = await DialogExportOptions.show(dialog, showThinking(), showDetails(), showAssistantMetadata())
|
||||
const options = await DialogExportOptions.show(dialog, showThinking())
|
||||
|
||||
if (options === null) return
|
||||
|
||||
const content =
|
||||
options.format === "markdown"
|
||||
? formatSessionTranscript(
|
||||
sessionData,
|
||||
messages(),
|
||||
options.thinking,
|
||||
options.toolDetails,
|
||||
options.assistantMetadata,
|
||||
)
|
||||
? formatSessionTranscript(sessionData, messages(), options.thinking)
|
||||
: await (async () => {
|
||||
if (options.debug) {
|
||||
const events: { readonly created: number }[] = []
|
||||
|
|
@ -922,9 +868,6 @@ export function Session() {
|
|||
conceal,
|
||||
thinkingMode,
|
||||
showThinking,
|
||||
showTimestamps,
|
||||
showDetails,
|
||||
showGenericToolOutput,
|
||||
groupExploration,
|
||||
diffWrapMode,
|
||||
models,
|
||||
|
|
@ -1534,13 +1477,7 @@ function UserMessage(props: { message: SessionMessageUser }) {
|
|||
>
|
||||
<text fg={theme.text}>{props.message.text}</text>
|
||||
<Show when={files().length}>
|
||||
<box
|
||||
flexDirection="row"
|
||||
paddingBottom={ctx.showTimestamps() ? 1 : 0}
|
||||
paddingTop={1}
|
||||
gap={1}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<box flexDirection="row" paddingTop={1} gap={1} flexWrap="wrap">
|
||||
<For each={files()}>
|
||||
{(file) => {
|
||||
const label = file.mime === "application/x-directory" ? "dir" : "file"
|
||||
|
|
@ -1557,11 +1494,6 @@ function UserMessage(props: { message: SessionMessageUser }) {
|
|||
</For>
|
||||
</box>
|
||||
</Show>
|
||||
<Show when={ctx.showTimestamps()}>
|
||||
<text fg={theme.textMuted}>
|
||||
<span style={{ fg: theme.textMuted }}>{Locale.todayTimeOrDateTime(props.message.time.created)}</span>
|
||||
</text>
|
||||
</Show>
|
||||
</box>
|
||||
</box>
|
||||
</Show>
|
||||
|
|
@ -1865,31 +1797,7 @@ function TextPart(props: { last: boolean; part: SessionMessageAssistantText }) {
|
|||
// Pending messages moved to individual tool pending functions
|
||||
|
||||
function ToolPart(props: { part: SessionMessageAssistantTool }) {
|
||||
const ctx = use()
|
||||
const data = useData()
|
||||
const display = createMemo(() => toolDisplay(props.part.name))
|
||||
const activeBackgroundWork = createMemo(() => {
|
||||
if (props.part.state.status === "streaming") return false
|
||||
if (display() === "shell") {
|
||||
const shellID = stringValue(props.part.state.structured.shellID)
|
||||
return Boolean(shellID && data.shell.get(shellID))
|
||||
}
|
||||
if (display() === "subagent") {
|
||||
const sessionID =
|
||||
stringValue(props.part.state.structured.sessionID) ?? stringValue(props.part.state.structured.sessionId)
|
||||
return Boolean(sessionID && data.session.status(sessionID) === "running")
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// Hide tool if showDetails is false and tool completed successfully
|
||||
const shouldHide = createMemo(() => {
|
||||
if (ctx.showDetails()) return false
|
||||
if (activeBackgroundWork()) return false
|
||||
if (props.part.state.status !== "completed") return false
|
||||
if (display() === "shell") return false
|
||||
return true
|
||||
})
|
||||
|
||||
const toolprops = {
|
||||
get metadata() {
|
||||
|
|
@ -1913,52 +1821,50 @@ function ToolPart(props: { part: SessionMessageAssistantTool }) {
|
|||
}
|
||||
|
||||
return (
|
||||
<Show when={!shouldHide()}>
|
||||
<Switch>
|
||||
<Match when={display() === "shell"}>
|
||||
<Shell {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "glob"}>
|
||||
<Glob {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "read"}>
|
||||
<Read {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "grep"}>
|
||||
<Grep {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "webfetch"}>
|
||||
<WebFetch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "websearch"}>
|
||||
<WebSearch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "write"}>
|
||||
<Write {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "edit"}>
|
||||
<Edit {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "subagent"}>
|
||||
<Subagent {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "execute"}>
|
||||
<Execute {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "patch"}>
|
||||
<ApplyPatch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "question"}>
|
||||
<Question {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "skill"}>
|
||||
<Skill {...toolprops} />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<GenericTool {...toolprops} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
<Switch>
|
||||
<Match when={display() === "shell"}>
|
||||
<Shell {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "glob"}>
|
||||
<Glob {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "read"}>
|
||||
<Read {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "grep"}>
|
||||
<Grep {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "webfetch"}>
|
||||
<WebFetch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "websearch"}>
|
||||
<WebSearch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "write"}>
|
||||
<Write {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "edit"}>
|
||||
<Edit {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "subagent"}>
|
||||
<Subagent {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "execute"}>
|
||||
<Execute {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "patch"}>
|
||||
<ApplyPatch {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "question"}>
|
||||
<Question {...toolprops} />
|
||||
</Match>
|
||||
<Match when={display() === "skill"}>
|
||||
<Skill {...toolprops} />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<GenericTool {...toolprops} />
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1970,40 +1876,55 @@ type ToolProps = {
|
|||
part: SessionMessageAssistantTool
|
||||
}
|
||||
function GenericTool(props: ToolProps) {
|
||||
const { theme } = useTheme()
|
||||
const ctx = use()
|
||||
const { theme, syntax } = useTheme()
|
||||
const output = createMemo(() => props.output?.trim() ?? "")
|
||||
const args = createMemo(() => JSON.stringify(props.input, null, 2))
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const maxLines = 3
|
||||
const maxChars = createMemo(() => maxLines * Math.max(20, ctx.width - 6))
|
||||
const collapsed = createMemo(() => collapseToolOutput(output(), maxLines, maxChars()))
|
||||
const limited = createMemo(() => {
|
||||
if (expanded() || !collapsed().overflow) return output()
|
||||
return collapsed().output
|
||||
})
|
||||
const expandable = createMemo(() => Object.keys(props.input).length > 0 || output().length > 0)
|
||||
|
||||
return (
|
||||
<Show
|
||||
when={props.output && ctx.showGenericToolOutput()}
|
||||
fallback={
|
||||
<InlineTool icon="⚙" pending="Writing command..." complete={true} part={props.part}>
|
||||
{props.tool} {input(props.input)}
|
||||
</InlineTool>
|
||||
}
|
||||
<BlockTool
|
||||
title={`◆ ${props.tool}`}
|
||||
part={props.part}
|
||||
spinner={props.part.state.status === "streaming" || props.part.state.status === "running"}
|
||||
onClick={expandable() ? () => setExpanded((value) => !value) : undefined}
|
||||
>
|
||||
<BlockTool
|
||||
title={`# ${props.tool} ${input(props.input)}`}
|
||||
part={props.part}
|
||||
onClick={collapsed().overflow ? () => setExpanded((prev) => !prev) : undefined}
|
||||
>
|
||||
<box gap={1}>
|
||||
<text fg={theme.text}>{limited()}</text>
|
||||
<Show when={collapsed().overflow}>
|
||||
<text fg={theme.textMuted}>{expanded() ? "Click to collapse" : "Click to expand"}</text>
|
||||
<Show when={expanded()}>
|
||||
<box gap={1} paddingTop={1}>
|
||||
<Show when={Object.keys(props.input).length > 0}>
|
||||
<box gap={1}>
|
||||
<text>
|
||||
<span style={{ bg: theme.backgroundElement, fg: theme.textMuted }}> Input </span>
|
||||
</text>
|
||||
<box paddingLeft={1}>
|
||||
<code
|
||||
content={args()}
|
||||
filetype="json"
|
||||
syntaxStyle={syntax()}
|
||||
conceal={false}
|
||||
drawUnstyledText={false}
|
||||
fg={theme.text}
|
||||
/>
|
||||
</box>
|
||||
</box>
|
||||
</Show>
|
||||
<Show when={output()}>
|
||||
{(value) => (
|
||||
<box gap={1}>
|
||||
<text>
|
||||
<span style={{ bg: theme.backgroundElement, fg: theme.textMuted }}> Output </span>
|
||||
</text>
|
||||
<box paddingLeft={1}>
|
||||
<text fg={theme.text} wrapMode="word">
|
||||
{value()}
|
||||
</text>
|
||||
</box>
|
||||
</box>
|
||||
)}
|
||||
</Show>
|
||||
</box>
|
||||
</BlockTool>
|
||||
</Show>
|
||||
</Show>
|
||||
</BlockTool>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2106,12 +2027,7 @@ export function InlineToolRow(props: {
|
|||
<Spinner color={props.color} children={props.children} />
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<Show
|
||||
fallback={
|
||||
<Spinner color={props.color}>{props.pending}</Spinner>
|
||||
}
|
||||
when={props.complete || props.failed}
|
||||
>
|
||||
<Show fallback={<Spinner color={props.color}>{props.pending}</Spinner>} when={props.complete || props.failed}>
|
||||
<box flexDirection="row">
|
||||
<text
|
||||
width={INLINE_TOOL_ICON_WIDTH}
|
||||
|
|
@ -2183,11 +2099,7 @@ function BlockTool(props: {
|
|||
{(title) => (
|
||||
<Show
|
||||
when={props.spinner}
|
||||
fallback={
|
||||
<text fg={permission() ? theme.warning : theme.textMuted}>
|
||||
{title()}
|
||||
</text>
|
||||
}
|
||||
fallback={<text fg={permission() ? theme.warning : theme.textMuted}>{title()}</text>}
|
||||
>
|
||||
<Spinner color={permission() ? theme.warning : theme.textMuted}>{title().replace(/^# /, "")}</Spinner>
|
||||
</Show>
|
||||
|
|
@ -2676,11 +2588,7 @@ function ApplyPatch(props: ToolProps) {
|
|||
: undefined
|
||||
}
|
||||
title={
|
||||
targets().length === 1
|
||||
? undefined
|
||||
: props.part.state.status === "error"
|
||||
? "# Patch failed"
|
||||
: "Patching"
|
||||
targets().length === 1 ? undefined : props.part.state.status === "error" ? "# Patch failed" : "Patching"
|
||||
}
|
||||
part={props.part}
|
||||
spinner={props.part.state.status === "streaming" || props.part.state.status === "running"}
|
||||
|
|
@ -2806,13 +2714,7 @@ function recordValue(value: unknown): Record<string, unknown> | undefined {
|
|||
return value as Record<string, unknown>
|
||||
}
|
||||
|
||||
function formatSessionTranscript(
|
||||
session: SessionInfo,
|
||||
messages: SessionMessageInfo[],
|
||||
thinking: boolean,
|
||||
toolDetails: boolean,
|
||||
assistantMetadata: boolean,
|
||||
) {
|
||||
function formatSessionTranscript(session: SessionInfo, messages: SessionMessageInfo[], thinking: boolean) {
|
||||
const body = messages.flatMap((message) => {
|
||||
if (message.type === "user") return [`## User\n\n${message.text}`]
|
||||
if (message.type === "shell")
|
||||
|
|
@ -2821,7 +2723,6 @@ function formatSessionTranscript(
|
|||
const content = message.content.flatMap((item) => {
|
||||
if (item.type === "text") return [item.text]
|
||||
if (item.type === "reasoning") return thinking ? [`_Thinking:_\n\n${item.text}`] : []
|
||||
if (!toolDetails) return [`**Tool: ${item.name}**`]
|
||||
const input = typeof item.state.input === "string" ? item.state.input : JSON.stringify(item.state.input, null, 2)
|
||||
const output =
|
||||
item.state.status === "error"
|
||||
|
|
@ -2833,13 +2734,7 @@ function formatSessionTranscript(
|
|||
.join("\n")
|
||||
return [`**Tool: ${item.name}**\n\n**Input:**\n\`\`\`json\n${input}\n\`\`\`\n\n${output}`]
|
||||
})
|
||||
const duration = message.time.completed
|
||||
? ` · ${((message.time.completed - message.time.created) / 1000).toFixed(1)}s`
|
||||
: ""
|
||||
const heading = assistantMetadata
|
||||
? `## Assistant (${message.agent} · ${message.model.providerID}/${message.model.id}${duration})`
|
||||
: "## Assistant"
|
||||
return [`${heading}\n\n${content.join("\n\n")}`]
|
||||
return [`## Assistant\n\n${content.join("\n\n")}`]
|
||||
})
|
||||
return `# ${session.title}\n\n**Session ID:** ${session.id}\n**Created:** ${new Date(session.time.created).toLocaleString()}\n**Updated:** ${new Date(session.time.updated).toLocaleString()}\n\n---\n\n${body.join("\n\n---\n\n")}\n`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { filetype } from "../../util/filetype"
|
|||
import { Locale } from "../../util/locale"
|
||||
import { webSearchProviderLabel } from "../../util/tool-display"
|
||||
import { getScrollAcceleration } from "../../util/scroll"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut } from "../../keymap"
|
||||
import { usePathFormatter } from "../../context/path-format"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useData } from "../../context/data"
|
||||
import { createMemo, Show } from "solid-js"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { useTuiConfig } from "../../config"
|
||||
import { useTuiConfig } from "../../config/v1"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { usePluginRuntime } from "../../plugin/runtime"
|
||||
|
||||
|
|
|
|||
|
|
@ -9,20 +9,11 @@ export type ExportFormat = "markdown" | "json"
|
|||
|
||||
export type DialogExportOptionsProps = {
|
||||
defaultThinking: boolean
|
||||
defaultToolDetails: boolean
|
||||
defaultAssistantMetadata: boolean
|
||||
onConfirm?: (options: {
|
||||
action: "copy" | "export"
|
||||
format: ExportFormat
|
||||
debug: boolean
|
||||
thinking: boolean
|
||||
toolDetails: boolean
|
||||
assistantMetadata: boolean
|
||||
}) => void
|
||||
onConfirm?: (options: { action: "copy" | "export"; format: ExportFormat; debug: boolean; thinking: boolean }) => void
|
||||
onCancel?: () => void
|
||||
}
|
||||
|
||||
type Active = ExportFormat | "debug" | "thinking" | "toolDetails" | "assistantMetadata" | "copy" | "export"
|
||||
type Active = ExportFormat | "debug" | "thinking" | "copy" | "export"
|
||||
|
||||
export function DialogExportOptions(props: DialogExportOptionsProps) {
|
||||
const dialog = useDialog()
|
||||
|
|
@ -31,8 +22,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
format: "markdown" as ExportFormat,
|
||||
debug: false,
|
||||
thinking: props.defaultThinking,
|
||||
toolDetails: props.defaultToolDetails,
|
||||
assistantMetadata: props.defaultAssistantMetadata,
|
||||
active: "markdown" as Active,
|
||||
})
|
||||
|
||||
|
|
@ -42,8 +31,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
format: store.format,
|
||||
debug: store.debug,
|
||||
thinking: store.thinking,
|
||||
toolDetails: store.toolDetails,
|
||||
assistantMetadata: store.assistantMetadata,
|
||||
})
|
||||
|
||||
const activate = () => {
|
||||
|
|
@ -53,8 +40,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
}
|
||||
if (store.active === "debug") setStore("debug", !store.debug)
|
||||
if (store.active === "thinking") setStore("thinking", !store.thinking)
|
||||
if (store.active === "toolDetails") setStore("toolDetails", !store.toolDetails)
|
||||
if (store.active === "assistantMetadata") setStore("assistantMetadata", !store.assistantMetadata)
|
||||
if (store.active === "copy" || store.active === "export") confirm(store.active)
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +52,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
cmd: () => {
|
||||
const order: Active[] =
|
||||
store.format === "markdown"
|
||||
? ["markdown", "json", "thinking", "toolDetails", "assistantMetadata", "copy", "export"]
|
||||
? ["markdown", "json", "thinking", "copy", "export"]
|
||||
: ["markdown", "json", "debug", "copy", "export"]
|
||||
setStore("active", order[(order.indexOf(store.active) + 1) % order.length])
|
||||
},
|
||||
|
|
@ -86,11 +71,6 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
setStore("active", format)
|
||||
}
|
||||
|
||||
const toggle = (option: "thinking" | "toolDetails" | "assistantMetadata") => {
|
||||
setStore("active", option)
|
||||
setStore(option, !store[option])
|
||||
}
|
||||
|
||||
return (
|
||||
<box paddingLeft={2} paddingRight={2} gap={1}>
|
||||
<box flexDirection="row" justifyContent="space-between">
|
||||
|
|
@ -121,30 +101,19 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
</box>
|
||||
</box>
|
||||
<Show when={store.format === "markdown"}>
|
||||
<box flexDirection="column">
|
||||
<For
|
||||
each={
|
||||
[
|
||||
["thinking", "Include thinking"],
|
||||
["toolDetails", "Include tool details"],
|
||||
["assistantMetadata", "Include assistant metadata"],
|
||||
] as const
|
||||
}
|
||||
>
|
||||
{(item) => (
|
||||
<box
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
backgroundColor={store.active === item[0] ? theme.backgroundElement : undefined}
|
||||
onMouseUp={() => toggle(item[0])}
|
||||
>
|
||||
<text fg={store.active === item[0] ? theme.primary : theme.textMuted}>
|
||||
{store[item[0]] ? "[x]" : "[ ]"}
|
||||
</text>
|
||||
<text fg={store.active === item[0] ? theme.primary : theme.text}>{item[1]}</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
<box
|
||||
flexDirection="row"
|
||||
gap={1}
|
||||
backgroundColor={store.active === "thinking" ? theme.backgroundElement : undefined}
|
||||
onMouseUp={() => {
|
||||
setStore("active", "thinking")
|
||||
setStore("thinking", !store.thinking)
|
||||
}}
|
||||
>
|
||||
<text fg={store.active === "thinking" ? theme.primary : theme.textMuted}>
|
||||
{store.thinking ? "[x]" : "[ ]"}
|
||||
</text>
|
||||
<text fg={store.active === "thinking" ? theme.primary : theme.text}>Include thinking</text>
|
||||
</box>
|
||||
</Show>
|
||||
<Show when={store.format === "json"}>
|
||||
|
|
@ -170,12 +139,7 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
>
|
||||
<text fg={theme.text}>Copy</text>
|
||||
</box>
|
||||
<box
|
||||
paddingLeft={4}
|
||||
paddingRight={4}
|
||||
backgroundColor={theme.primary}
|
||||
onMouseUp={() => confirm("export")}
|
||||
>
|
||||
<box paddingLeft={4} paddingRight={4} backgroundColor={theme.primary} onMouseUp={() => confirm("export")}>
|
||||
<text fg={theme.selectedListItemText}>Export</text>
|
||||
</box>
|
||||
</box>
|
||||
|
|
@ -183,26 +147,17 @@ export function DialogExportOptions(props: DialogExportOptionsProps) {
|
|||
)
|
||||
}
|
||||
|
||||
DialogExportOptions.show = (
|
||||
dialog: DialogContext,
|
||||
defaultThinking: boolean,
|
||||
defaultToolDetails: boolean,
|
||||
defaultAssistantMetadata: boolean,
|
||||
) => {
|
||||
DialogExportOptions.show = (dialog: DialogContext, defaultThinking: boolean) => {
|
||||
return new Promise<{
|
||||
action: "copy" | "export"
|
||||
format: ExportFormat
|
||||
debug: boolean
|
||||
thinking: boolean
|
||||
toolDetails: boolean
|
||||
assistantMetadata: boolean
|
||||
} | null>((resolve) => {
|
||||
dialog.replace(
|
||||
() => (
|
||||
<DialogExportOptions
|
||||
defaultThinking={defaultThinking}
|
||||
defaultToolDetails={defaultToolDetails}
|
||||
defaultAssistantMetadata={defaultAssistantMetadata}
|
||||
onConfirm={(options) => resolve(options)}
|
||||
onCancel={() => resolve(null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useTheme } from "../context/theme"
|
|||
import { useDialog, type DialogContext } from "./dialog"
|
||||
import { Show, createEffect, createSignal, onMount, type JSX } from "solid-js"
|
||||
import { Spinner } from "../component/spinner"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { useBindings, useCommandShortcut } from "../keymap"
|
||||
|
||||
export type DialogPromptProps = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { isDeepEqual } from "remeda"
|
|||
import { useDialog, type DialogContext } from "./dialog"
|
||||
import { Locale } from "../util/locale"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
import { useTuiConfig } from "../config"
|
||||
import { useTuiConfig } from "../config/v1"
|
||||
import { formatKeyBindings, useBindings, useKeymapSelector } from "../keymap"
|
||||
|
||||
export interface DialogSelectProps<T> {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import path from "node:path"
|
|||
import { onCleanup } from "solid-js"
|
||||
import { tmpdir } from "../../fixture/fixture"
|
||||
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
|
||||
import type { TuiKeybind } from "../../../src/config/keybind"
|
||||
import type { TuiKeybind } from "../../../src/config/v1/keybind"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
|
||||
async function wait(fn: () => boolean, timeout = 2000) {
|
||||
|
|
@ -41,7 +41,7 @@ async function mountPrompt(input: {
|
|||
import("../../../src/ui/dialog-prompt"),
|
||||
import("../../../src/context/kv"),
|
||||
import("../../../src/context/theme"),
|
||||
import("../../../src/config"),
|
||||
import("../../../src/config/v1"),
|
||||
import("../../../src/ui/toast"),
|
||||
import("../../../src/keymap"),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { JSX } from "solid-js"
|
|||
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
|
||||
import { KVProvider } from "../../../src/context/kv"
|
||||
import { ThemeProvider } from "../../../src/context/theme"
|
||||
import { TuiConfigProvider } from "../../../src/config"
|
||||
import { TuiConfigProvider } from "../../../src/config/v1"
|
||||
import { DiffViewerFileTree } from "../../../src/feature-plugins/system/diff-viewer-file-tree"
|
||||
import { TestTuiContexts } from "../../fixture/tui-environment"
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import type { TuiPluginApi, TuiPluginMeta, TuiRouteCurrent, TuiRouteDefinition }
|
|||
import type { Session } from "@opencode-ai/sdk/v2"
|
||||
import { KVProvider } from "../../../src/context/kv"
|
||||
import { ThemeProvider } from "../../../src/context/theme"
|
||||
import { TuiConfigProvider } from "../../../src/config"
|
||||
import { TuiConfigProvider } from "../../../src/config/v1"
|
||||
import { SDKProvider } from "../../../src/context/sdk"
|
||||
import { TuiKeybind } from "../../../src/config/keybind"
|
||||
import { TuiKeybind } from "../../../src/config/v1/keybind"
|
||||
import { OpencodeKeymapProvider } from "../../../src/keymap"
|
||||
import diffViewerPlugin from "../../../src/feature-plugins/system/diff-viewer"
|
||||
import { createTuiPluginApi } from "../../fixture/tui-plugin"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import type { FormWithLocation } from "../../../src/context/data"
|
|||
import { KVProvider } from "../../../src/context/kv"
|
||||
import { SDKProvider } from "../../../src/context/sdk"
|
||||
import { ThemeProvider } from "../../../src/context/theme"
|
||||
import { TuiConfigProvider } from "../../../src/config"
|
||||
import { TuiConfigProvider } from "../../../src/config/v1"
|
||||
import { OpencodeKeymapProvider, registerOpencodeKeymap } from "../../../src/keymap"
|
||||
import { ToastProvider } from "../../../src/ui/toast"
|
||||
import { tmpdir } from "../../fixture/fixture"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
TuiConfigProvider,
|
||||
type Info as TuiConfigInfo,
|
||||
useTuiConfig,
|
||||
} from "../src/config"
|
||||
} from "../src/config/v1"
|
||||
|
||||
const decodeInfo = Schema.decodeUnknownSync(Info)
|
||||
const decodePlugin = Schema.decodeUnknownSync(PluginSpec)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { resolve, type Info, type Resolved } from "../../src/config"
|
||||
import { TuiKeybind } from "../../src/config/keybind"
|
||||
import { resolve, type Info, type Resolved } from "../../src/config/v1"
|
||||
import { TuiKeybind } from "../../src/config/v1/keybind"
|
||||
|
||||
type ResolvedInput = Omit<Info, "attention" | "keybinds" | "leader_timeout"> & {
|
||||
attention?: Partial<Resolved["attention"]>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { createBindingLookup } from "@opentui/keymap/extras"
|
|||
import { testRender, useRenderer } from "@opentui/solid"
|
||||
import { expect, test } from "bun:test"
|
||||
import { onCleanup } from "solid-js"
|
||||
import { TuiKeybind } from "../src/config/keybind"
|
||||
import { TuiKeybind } from "../src/config/v1/keybind"
|
||||
import {
|
||||
formatKeySequence,
|
||||
getOpencodeModeStack,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue