feat(tui): personal TUI rework, integrating upstream model reasoning-effort selection
Consolidates the personal dsh-tui customizations (module split into components/session/extension, prompt template + running-glyph indicator, copyable transcript, tool-card headers, timing placement, XML tool output, status/footer rework) and ports upstream's model reasoning-effort selector (Shift+Tab effort cycling, effort-aware /model, footer, and /status) onto the personal module layout.
This commit is contained in:
213
packages/ui/tui/src/config.ts
Normal file
213
packages/ui/tui/src/config.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Serializable configuration and defaults for the pi-tui terminal mode. Loader
|
||||
* schema validation normally fills defaults; {@link resolveTuiConfig} applies
|
||||
* the same defaults for direct callers that bypass the Loader.
|
||||
* @module @deepseek-ai/dsh-tui/config
|
||||
*/
|
||||
|
||||
import z from 'schemastery'
|
||||
import {
|
||||
DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES,
|
||||
DEFAULT_FILE_SEARCH_MAX_ENTRIES,
|
||||
DEFAULT_FILE_SEARCH_MAX_RESULTS,
|
||||
} from './file-autocomplete.ts'
|
||||
|
||||
/** Theme and prompt-template settings for the pi-tui terminal mode. */
|
||||
export interface TuiThemeConfig {
|
||||
/** Apply the built-in ANSI color palette. */
|
||||
color?: boolean
|
||||
/** Paint the startup banner with the 24-bit DeepSeek brand gradient. */
|
||||
truecolor?: boolean
|
||||
/** Left-aligned template on the row above the editor. */
|
||||
leftPrompt?: string
|
||||
/** Right-aligned template on the row above the editor. */
|
||||
rightPrompt?: string
|
||||
/** Template used as the editor's first-line prefix. */
|
||||
inputPrompt?: string
|
||||
/** Static placeholder shown in an empty editor while the agent is running. */
|
||||
inputPlaceholder?: string
|
||||
}
|
||||
|
||||
/** Interaction and presentation settings for the pi-tui terminal mode. */
|
||||
export interface TuiConfig {
|
||||
/** Render model reasoning blocks. */
|
||||
showReasoning?: boolean
|
||||
/** Maximum tool-card body lines retained in its collapsed head/tail preview. */
|
||||
maxToolOutputLines?: number
|
||||
/** Maximum options visible at once in a user-question panel. */
|
||||
maxQuestionOptions?: number
|
||||
/** Maximum models visible at once in the model selector. */
|
||||
maxModelOptions?: number
|
||||
/** Maximum sessions visible at once in the resume selector. */
|
||||
maxResumeOptions?: number
|
||||
/** User-question panel width in terminal columns, clamped to the terminal. */
|
||||
questionDialogWidth?: number
|
||||
/** User-question panel maximum height in terminal rows. */
|
||||
questionDialogMaxHeight?: number
|
||||
/** Model-selector width in terminal columns. */
|
||||
modelDialogWidth?: number
|
||||
/** Model-selector maximum height in terminal rows. */
|
||||
modelDialogMaxHeight?: number
|
||||
/** Maximum fuzzy file candidates displayed for one `@` query. */
|
||||
fileSearchMaxResults?: number
|
||||
/** Maximum paths retained in one `@` workspace index. */
|
||||
fileSearchMaxEntries?: number
|
||||
/** Directory basenames excluded from `@` traversal and completion. */
|
||||
fileSearchExcludedDirectories?: string[]
|
||||
/** Show the terminal's hardware cursor at the pi editor's IME marker. */
|
||||
showHardwareCursor?: boolean
|
||||
/** Color and prompt-template settings. */
|
||||
theme?: TuiThemeConfig
|
||||
/** Terminal window title while the UI is mounted; a logged session title prefixes it. */
|
||||
title?: string
|
||||
}
|
||||
|
||||
const showReasoningSchema = z.boolean().default(true)
|
||||
const maxToolOutputLinesSchema = z.number().step(1).min(1).default(6)
|
||||
const maxQuestionOptionsSchema = z.number().step(1).min(1).default(8)
|
||||
const maxModelOptionsSchema = z.number().step(1).min(1).default(8)
|
||||
const maxResumeOptionsSchema = z.number().step(1).min(1).default(8)
|
||||
const questionDialogWidthSchema = z.number().step(1).min(20).default(200)
|
||||
const questionDialogMaxHeightSchema = z.number().step(1).min(6).default(20)
|
||||
const modelDialogWidthSchema = z.number().step(1).min(20).default(76)
|
||||
const modelDialogMaxHeightSchema = z.number().step(1).min(6).default(20)
|
||||
const fileSearchMaxResultsSchema = z.number().step(1).min(1).default(DEFAULT_FILE_SEARCH_MAX_RESULTS)
|
||||
const fileSearchMaxEntriesSchema = z.number().step(1).min(1).default(DEFAULT_FILE_SEARCH_MAX_ENTRIES)
|
||||
const fileSearchExcludedDirectoriesSchema = z.array(z.string()).default([...DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES])
|
||||
const showHardwareCursorSchema = z.boolean().default(false)
|
||||
const colorSchema = z.boolean().default(true)
|
||||
// No default: an unset value auto-detects truecolor from COLORTERM in `apply`.
|
||||
const truecolorSchema = z.boolean()
|
||||
const DEFAULT_LEFT_PROMPT = '${cwd}${git/worktree}${model}${token_meter/cache_hit_rate}${context}'
|
||||
const DEFAULT_RIGHT_PROMPT = '${timing}'
|
||||
const DEFAULT_INPUT_PROMPT = '${symbol} ${indicator}'
|
||||
const DEFAULT_INPUT_PLACEHOLDER = 'press enter to steer and esc to cancel'
|
||||
const TuiThemeConfigSchema: z<TuiThemeConfig> = z.object({
|
||||
color: colorSchema,
|
||||
truecolor: truecolorSchema,
|
||||
leftPrompt: z.string().default(DEFAULT_LEFT_PROMPT),
|
||||
rightPrompt: z.string().default(DEFAULT_RIGHT_PROMPT),
|
||||
inputPrompt: z.string().default(DEFAULT_INPUT_PROMPT),
|
||||
inputPlaceholder: z.string().default(DEFAULT_INPUT_PLACEHOLDER),
|
||||
})
|
||||
const titleSchema = z.string().default('DeepSeek Harness')
|
||||
|
||||
const tuiConfigSchemaFields = {
|
||||
showReasoning: showReasoningSchema,
|
||||
maxToolOutputLines: maxToolOutputLinesSchema,
|
||||
maxQuestionOptions: maxQuestionOptionsSchema,
|
||||
maxModelOptions: maxModelOptionsSchema,
|
||||
maxResumeOptions: maxResumeOptionsSchema,
|
||||
questionDialogWidth: questionDialogWidthSchema,
|
||||
questionDialogMaxHeight: questionDialogMaxHeightSchema,
|
||||
modelDialogWidth: modelDialogWidthSchema,
|
||||
modelDialogMaxHeight: modelDialogMaxHeightSchema,
|
||||
fileSearchMaxResults: fileSearchMaxResultsSchema,
|
||||
fileSearchMaxEntries: fileSearchMaxEntriesSchema,
|
||||
fileSearchExcludedDirectories: fileSearchExcludedDirectoriesSchema,
|
||||
showHardwareCursor: showHardwareCursorSchema,
|
||||
theme: TuiThemeConfigSchema,
|
||||
title: titleSchema,
|
||||
}
|
||||
|
||||
/** Schemastery schema for presentation settings embedded by app bundles. */
|
||||
export const TuiConfigSchema: z<TuiConfig> = z.object(tuiConfigSchemaFields)
|
||||
|
||||
/** Serializable plugin configuration. */
|
||||
export interface Config extends TuiConfig {
|
||||
/** Banner subtitle line. When absent, the banner has no subtitle and sweeps in on start. */
|
||||
welcome?: string
|
||||
/** Exact shared agent/session identity driven by this terminal. Defaults to `main`. */
|
||||
sessionId?: string
|
||||
/**
|
||||
* Shell command fallback printed on exit or after selecting a session when
|
||||
* the host cannot hand off in place. Every `{session}` becomes the selected
|
||||
* id; the TUI never executes this text. Absent disables only the fallback,
|
||||
* not the interactive selector.
|
||||
*/
|
||||
resumeCommand?: string
|
||||
}
|
||||
|
||||
/** Schemastery schema for the full plugin configuration. */
|
||||
export const Config: z<Config> = z.object({
|
||||
welcome: z.string(),
|
||||
sessionId: z.string().default('main'),
|
||||
resumeCommand: z.string(),
|
||||
showReasoning: tuiConfigSchemaFields.showReasoning,
|
||||
maxToolOutputLines: tuiConfigSchemaFields.maxToolOutputLines,
|
||||
maxQuestionOptions: tuiConfigSchemaFields.maxQuestionOptions,
|
||||
maxModelOptions: tuiConfigSchemaFields.maxModelOptions,
|
||||
maxResumeOptions: tuiConfigSchemaFields.maxResumeOptions,
|
||||
questionDialogWidth: tuiConfigSchemaFields.questionDialogWidth,
|
||||
questionDialogMaxHeight: tuiConfigSchemaFields.questionDialogMaxHeight,
|
||||
modelDialogWidth: tuiConfigSchemaFields.modelDialogWidth,
|
||||
modelDialogMaxHeight: tuiConfigSchemaFields.modelDialogMaxHeight,
|
||||
fileSearchMaxResults: tuiConfigSchemaFields.fileSearchMaxResults,
|
||||
fileSearchMaxEntries: tuiConfigSchemaFields.fileSearchMaxEntries,
|
||||
fileSearchExcludedDirectories: tuiConfigSchemaFields.fileSearchExcludedDirectories,
|
||||
showHardwareCursor: tuiConfigSchemaFields.showHardwareCursor,
|
||||
theme: tuiConfigSchemaFields.theme,
|
||||
title: tuiConfigSchemaFields.title,
|
||||
})
|
||||
|
||||
/** Fully defaulted TUI theme settings. */
|
||||
export interface ResolvedTuiThemeConfig {
|
||||
color: boolean
|
||||
truecolor: boolean
|
||||
leftPrompt: string
|
||||
rightPrompt: string
|
||||
inputPrompt: string
|
||||
inputPlaceholder: string
|
||||
}
|
||||
|
||||
/** Fully defaulted TUI presentation settings. */
|
||||
export interface ResolvedTuiConfig {
|
||||
showReasoning: boolean
|
||||
maxToolOutputLines: number
|
||||
maxQuestionOptions: number
|
||||
maxModelOptions: number
|
||||
maxResumeOptions: number
|
||||
questionDialogWidth: number
|
||||
questionDialogMaxHeight: number
|
||||
modelDialogWidth: number
|
||||
modelDialogMaxHeight: number
|
||||
fileSearchMaxResults: number
|
||||
fileSearchMaxEntries: number
|
||||
fileSearchExcludedDirectories: string[]
|
||||
showHardwareCursor: boolean
|
||||
theme: ResolvedTuiThemeConfig
|
||||
title: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply direct-call defaults after Loader schema validation has normally run.
|
||||
*
|
||||
* @param config - Deployment-provided terminal presentation settings.
|
||||
* @returns Complete settings consumed by the TUI renderer.
|
||||
*/
|
||||
export function resolveTuiConfig(config: TuiConfig | undefined): ResolvedTuiConfig {
|
||||
return {
|
||||
showReasoning: config?.showReasoning ?? true,
|
||||
maxToolOutputLines: config?.maxToolOutputLines ?? 6,
|
||||
maxQuestionOptions: config?.maxQuestionOptions ?? 8,
|
||||
maxModelOptions: config?.maxModelOptions ?? 8,
|
||||
maxResumeOptions: config?.maxResumeOptions ?? 8,
|
||||
questionDialogWidth: config?.questionDialogWidth ?? 200,
|
||||
questionDialogMaxHeight: config?.questionDialogMaxHeight ?? 20,
|
||||
modelDialogWidth: config?.modelDialogWidth ?? 76,
|
||||
modelDialogMaxHeight: config?.modelDialogMaxHeight ?? 20,
|
||||
fileSearchMaxResults: config?.fileSearchMaxResults ?? DEFAULT_FILE_SEARCH_MAX_RESULTS,
|
||||
fileSearchMaxEntries: config?.fileSearchMaxEntries ?? DEFAULT_FILE_SEARCH_MAX_ENTRIES,
|
||||
fileSearchExcludedDirectories: [...(config?.fileSearchExcludedDirectories ?? DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES)],
|
||||
showHardwareCursor: config?.showHardwareCursor ?? false,
|
||||
theme: {
|
||||
color: config?.theme?.color ?? true,
|
||||
truecolor: config?.theme?.truecolor ?? false,
|
||||
leftPrompt: config?.theme?.leftPrompt ?? DEFAULT_LEFT_PROMPT,
|
||||
rightPrompt: config?.theme?.rightPrompt ?? DEFAULT_RIGHT_PROMPT,
|
||||
inputPrompt: config?.theme?.inputPrompt ?? DEFAULT_INPUT_PROMPT,
|
||||
inputPlaceholder: config?.theme?.inputPlaceholder ?? DEFAULT_INPUT_PLACEHOLDER,
|
||||
},
|
||||
title: config?.title ?? 'DeepSeek Harness',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user