feat: 品牌替换 + 启动优化 + AGENTS.md 模板定制
- 品牌替换:OpenCode/opencode → AirCoding/aircoding(16+ 文件) - Logo ASCII art:修复 left/right 行数不匹配导致的启动崩溃 - 启动诊断:添加 OPENCODE_PRINT_TIMING 计时探针 - dev 模式默认 --pure 跳过外部插件加载 - AGENTS.md 模板:追加 AirCoding 多 Agent 专项段落 - architect prompt + plugin:强化 AGENTS.md 产出验证
This commit is contained in:
129
packages/tui/src/config/index.tsx
Normal file
129
packages/tui/src/config/index.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
export * as TuiConfig from "."
|
||||
|
||||
import { createBindingLookup } from "@opentui/keymap/extras"
|
||||
import { Schema } from "effect"
|
||||
import { createContext, type JSX, useContext } from "solid-js"
|
||||
import { TuiKeybind } from "./keybind"
|
||||
|
||||
export const AttentionSoundName = Schema.Literals([
|
||||
"default",
|
||||
"question",
|
||||
"permission",
|
||||
"error",
|
||||
"done",
|
||||
"subagent_done",
|
||||
])
|
||||
export type AttentionSoundName = Schema.Schema.Type<typeof AttentionSoundName>
|
||||
|
||||
export const PluginOptions = Schema.Record(Schema.String, Schema.Unknown)
|
||||
export const PluginSpec = Schema.Union([Schema.String, Schema.mutable(Schema.Tuple([Schema.String, PluginOptions]))])
|
||||
|
||||
export const LeaderTimeoutDefault = 2000
|
||||
export const LeaderTimeout = Schema.Int.check(Schema.isGreaterThan(0)).annotate({
|
||||
description: "Leader key timeout in milliseconds",
|
||||
})
|
||||
|
||||
export const ScrollSpeed = Schema.Number.check(Schema.isGreaterThanOrEqualTo(0.001))
|
||||
export const ScrollAcceleration = Schema.Struct({
|
||||
enabled: Schema.Boolean.annotate({ description: "Enable scroll acceleration" }),
|
||||
}).annotate({ description: "Scroll acceleration settings" })
|
||||
export const DiffStyle = Schema.Literals(["auto", "stacked"]).annotate({
|
||||
description: "Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column",
|
||||
})
|
||||
|
||||
export const AttentionSounds = Schema.Record(AttentionSoundName, Schema.optionalKey(Schema.String))
|
||||
export type AttentionSoundPaths = Schema.Schema.Type<typeof AttentionSounds>
|
||||
export const Attention = 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(AttentionSounds),
|
||||
}).annotate({ description: "Attention notification and sound settings" })
|
||||
|
||||
const PromptSize = Schema.Int.check(Schema.isGreaterThan(0))
|
||||
export const Prompt = Schema.Struct({
|
||||
max_height: Schema.optional(PromptSize).annotate({ description: "Prompt textarea max height" }),
|
||||
max_width: Schema.optional(Schema.Union([PromptSize, Schema.Literal("auto")])).annotate({
|
||||
description: "Home prompt max width: a positive integer for a fixed cap, or 'auto' to scale with terminal width",
|
||||
}),
|
||||
}).annotate({ description: "Prompt size settings" })
|
||||
|
||||
export const Info = Schema.Struct({
|
||||
$schema: Schema.optional(Schema.String),
|
||||
theme: Schema.optional(Schema.String),
|
||||
keybinds: Schema.optional(TuiKeybind.KeybindOverrides),
|
||||
plugin: Schema.optional(Schema.Array(PluginSpec)),
|
||||
plugin_enabled: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)),
|
||||
leader_timeout: Schema.optional(LeaderTimeout),
|
||||
attention: Schema.optional(Attention),
|
||||
prompt: Schema.optional(Prompt),
|
||||
scroll_speed: Schema.optional(ScrollSpeed).annotate({ description: "TUI scroll speed" }),
|
||||
scroll_acceleration: Schema.optional(ScrollAcceleration),
|
||||
diff_style: Schema.optional(DiffStyle),
|
||||
mouse: Schema.optional(Schema.Boolean).annotate({ description: "Enable or disable mouse capture (default: true)" }),
|
||||
})
|
||||
export type Info = Schema.Schema.Type<typeof Info>
|
||||
|
||||
export type Resolved = Omit<Info, "attention" | "keybinds" | "leader_timeout" | "mouse"> & {
|
||||
attention: {
|
||||
enabled: boolean
|
||||
notifications: boolean
|
||||
sound: boolean
|
||||
volume: number
|
||||
sound_pack: string
|
||||
sounds: AttentionSoundPaths
|
||||
}
|
||||
keybinds: TuiKeybind.BindingLookupView
|
||||
leader_timeout: number
|
||||
mouse: boolean
|
||||
}
|
||||
|
||||
export const ResolveOptions = Schema.Struct({
|
||||
terminalSuspend: Schema.Boolean,
|
||||
})
|
||||
export type ResolveOptions = Schema.Schema.Type<typeof ResolveOptions>
|
||||
|
||||
export function resolve(input: Info, options: ResolveOptions): Resolved {
|
||||
const keybinds: TuiKeybind.KeybindOverrides = { ...input.keybinds }
|
||||
if (!options.terminalSuspend) {
|
||||
keybinds.terminal_suspend = "none"
|
||||
if (keybinds.input_undo === undefined) {
|
||||
const inputUndo = TuiKeybind.defaultValue("input_undo")
|
||||
keybinds.input_undo = ["ctrl+z", ...(typeof inputUndo === "string" ? inputUndo.split(",") : [])]
|
||||
.filter((value, index, values) => values.indexOf(value) === index)
|
||||
.join(",")
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...input,
|
||||
attention: {
|
||||
enabled: input.attention?.enabled ?? false,
|
||||
notifications: input.attention?.notifications ?? true,
|
||||
sound: input.attention?.sound ?? true,
|
||||
volume: input.attention?.volume ?? 0.4,
|
||||
sound_pack: input.attention?.sound_pack ?? "opencode.default",
|
||||
sounds: input.attention?.sounds ?? {},
|
||||
},
|
||||
keybinds: createBindingLookup(TuiKeybind.toBindingConfig(TuiKeybind.parse(keybinds)), {
|
||||
commandMap: TuiKeybind.CommandMap,
|
||||
bindingDefaults: TuiKeybind.bindingDefaults(),
|
||||
}),
|
||||
leader_timeout: input.leader_timeout ?? LeaderTimeoutDefault,
|
||||
mouse: input.mouse ?? true,
|
||||
}
|
||||
}
|
||||
|
||||
const ConfigContext = createContext<Resolved>()
|
||||
|
||||
export function TuiConfigProvider(props: { config: Resolved; children: JSX.Element }) {
|
||||
return <ConfigContext.Provider value={props.config}>{props.children}</ConfigContext.Provider>
|
||||
}
|
||||
|
||||
export function useTuiConfig() {
|
||||
const value = useContext(ConfigContext)
|
||||
if (!value) throw new Error("TuiConfigProvider is missing")
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user