Add log level configuration support (#272)
Add log level configuration support via config.yaml and UI settings. --------- Co-authored-by: Shantur Rathore <i@shantur.com>
This commit is contained in:
@@ -1,14 +1,30 @@
|
||||
import { createEffect, createSignal, type Component } from "solid-js"
|
||||
import { Terminal } from "lucide-solid"
|
||||
import { Select } from "@kobalte/core/select"
|
||||
import { createEffect, createMemo, createSignal, type Component } from "solid-js"
|
||||
import { ChevronDown, Terminal } from "lucide-solid"
|
||||
import OpenCodeBinarySelector from "../opencode-binary-selector"
|
||||
import EnvironmentVariablesEditor from "../environment-variables-editor"
|
||||
import { useConfig } from "../../stores/preferences"
|
||||
import type { ServerLogLevel } from "../../stores/preferences"
|
||||
import { useI18n } from "../../lib/i18n"
|
||||
|
||||
type LogLevelOption = {
|
||||
value: ServerLogLevel
|
||||
label: string
|
||||
}
|
||||
|
||||
export const OpenCodeSettingsSection: Component = () => {
|
||||
const { t } = useI18n()
|
||||
const { serverSettings, updateLastUsedBinary } = useConfig()
|
||||
const { serverSettings, updateLastUsedBinary, updateLogLevel } = useConfig()
|
||||
const [selectedBinary, setSelectedBinary] = createSignal(serverSettings().opencodeBinary || "opencode")
|
||||
const logLevelOptions = createMemo<LogLevelOption[]>(() => [
|
||||
{ value: "DEBUG", label: t("settings.opencode.logLevel.option.debug") },
|
||||
{ value: "INFO", label: t("settings.opencode.logLevel.option.info") },
|
||||
{ value: "WARN", label: t("settings.opencode.logLevel.option.warn") },
|
||||
{ value: "ERROR", label: t("settings.opencode.logLevel.option.error") },
|
||||
])
|
||||
const selectedLogLevel = createMemo(
|
||||
() => logLevelOptions().find((option) => option.value === serverSettings().logLevel) ?? logLevelOptions()[0],
|
||||
)
|
||||
|
||||
createEffect(() => {
|
||||
const binary = serverSettings().opencodeBinary || "opencode"
|
||||
@@ -37,6 +53,60 @@ export const OpenCodeSettingsSection: Component = () => {
|
||||
<OpenCodeBinarySelector selectedBinary={selectedBinary()} onBinaryChange={handleBinaryChange} isVisible />
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<div class="settings-card-header">
|
||||
<div>
|
||||
<h3 class="settings-card-title">{t("settings.opencode.logLevel.title")}</h3>
|
||||
<p class="settings-card-subtitle">{t("settings.opencode.logLevel.subtitle")}</p>
|
||||
</div>
|
||||
<span class="settings-scope-badge settings-scope-badge-server">{t("settings.scope.server")}</span>
|
||||
</div>
|
||||
<div class="settings-card-body">
|
||||
<div class="settings-toggle-row settings-toggle-row-compact">
|
||||
<div>
|
||||
<div class="settings-toggle-title">{t("settings.opencode.logLevel.selector.title")}</div>
|
||||
<div class="settings-toggle-caption">{t("settings.opencode.logLevel.selector.subtitle")}</div>
|
||||
</div>
|
||||
<Select<LogLevelOption>
|
||||
value={selectedLogLevel()}
|
||||
onChange={(option) => {
|
||||
if (!option) return
|
||||
updateLogLevel(option.value)
|
||||
}}
|
||||
options={logLevelOptions()}
|
||||
optionValue="value"
|
||||
optionTextValue="label"
|
||||
itemComponent={(itemProps) => (
|
||||
<Select.Item item={itemProps.item} class="selector-option">
|
||||
<Select.ItemLabel class="selector-option-label">{itemProps.item.rawValue.label}</Select.ItemLabel>
|
||||
</Select.Item>
|
||||
)}
|
||||
>
|
||||
<Select.Trigger class="selector-trigger" aria-label={t("settings.opencode.logLevel.title")}>
|
||||
<div class="flex-1 min-w-0">
|
||||
<Select.Value<LogLevelOption>>
|
||||
{(state) => (
|
||||
<span class="selector-trigger-primary selector-trigger-primary--align-left">
|
||||
{state.selectedOption()?.label}
|
||||
</span>
|
||||
)}
|
||||
</Select.Value>
|
||||
</div>
|
||||
<Select.Icon class="selector-trigger-icon">
|
||||
<ChevronDown class="w-3 h-3" />
|
||||
</Select.Icon>
|
||||
</Select.Trigger>
|
||||
|
||||
<Select.Portal>
|
||||
<Select.Content class="selector-popover">
|
||||
<Select.Listbox class="selector-listbox" />
|
||||
</Select.Content>
|
||||
</Select.Portal>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card">
|
||||
<div class="settings-card-header">
|
||||
<div>
|
||||
|
||||
@@ -113,6 +113,15 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "OpenCode Log Level",
|
||||
"settings.opencode.logLevel.subtitle": "Control the log verbosity used when launching new OpenCode instances.",
|
||||
"settings.opencode.logLevel.selector.title": "Default log level",
|
||||
"settings.opencode.logLevel.selector.subtitle": "Choose how verbose new OpenCode instances should be.",
|
||||
"settings.opencode.logLevel.option.debug": "Debug",
|
||||
"settings.opencode.logLevel.option.info": "Info",
|
||||
"settings.opencode.logLevel.option.warn": "Warn",
|
||||
"settings.opencode.logLevel.option.error": "Error",
|
||||
|
||||
|
||||
"settings.appearance.behavior.title": "Interaction",
|
||||
"settings.appearance.behavior.subtitle": "Message, diff, and input defaults.",
|
||||
|
||||
@@ -113,6 +113,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "Nivel de logs de OpenCode",
|
||||
"settings.opencode.logLevel.subtitle": "Define el nivel de logs usado al iniciar nuevas instancias de OpenCode.",
|
||||
"settings.opencode.logLevel.selector.title": "Verbosidad de logs",
|
||||
"settings.opencode.logLevel.selector.subtitle": "Elige cuanta informacion deben registrar las nuevas instancias de OpenCode.",
|
||||
"settings.opencode.logLevel.option.debug": "Depuracion",
|
||||
"settings.opencode.logLevel.option.info": "Informacion",
|
||||
"settings.opencode.logLevel.option.warn": "Advertencia",
|
||||
"settings.opencode.logLevel.option.error": "Error",
|
||||
|
||||
"settings.appearance.behavior.title": "Interaccion",
|
||||
"settings.appearance.behavior.subtitle": "Valores predeterminados de mensajes, diffs y entrada.",
|
||||
|
||||
@@ -113,6 +113,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "Niveau de logs OpenCode",
|
||||
"settings.opencode.logLevel.subtitle": "Definir le niveau de logs utilise au lancement des nouvelles instances OpenCode.",
|
||||
"settings.opencode.logLevel.selector.title": "Verbosite des logs",
|
||||
"settings.opencode.logLevel.selector.subtitle": "Choisir la quantite de journaux emise par les nouvelles instances OpenCode.",
|
||||
"settings.opencode.logLevel.option.debug": "Debogage",
|
||||
"settings.opencode.logLevel.option.info": "Info",
|
||||
"settings.opencode.logLevel.option.warn": "Avertissement",
|
||||
"settings.opencode.logLevel.option.error": "Erreur",
|
||||
|
||||
"settings.appearance.behavior.title": "Interaction",
|
||||
"settings.appearance.behavior.subtitle": "Parametres par defaut pour les messages, les diffs et la saisie.",
|
||||
|
||||
@@ -112,6 +112,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "בחר את הקובץ הבינארי של OpenCode והסביבה לשימוש במופעים חדשים.",
|
||||
"settings.opencode.runtime.title": "סביבת ריצה",
|
||||
"settings.opencode.runtime.subtitle": "הגדר עם איזה קובץ בינארי של OpenCode מופעים חדשים יופעלו.",
|
||||
"settings.opencode.logLevel.title": "רמת הלוגים של OpenCode",
|
||||
"settings.opencode.logLevel.subtitle": "הגדר את רמת הלוגים שבה ייעשה שימוש בעת הפעלת מופעי OpenCode חדשים.",
|
||||
"settings.opencode.logLevel.selector.title": "פירוט לוגים",
|
||||
"settings.opencode.logLevel.selector.subtitle": "בחר כמה לוגים מופעי OpenCode חדשים צריכים להפיק.",
|
||||
"settings.opencode.logLevel.option.debug": "ניפוי שגיאות",
|
||||
"settings.opencode.logLevel.option.info": "מידע",
|
||||
"settings.opencode.logLevel.option.warn": "אזהרה",
|
||||
"settings.opencode.logLevel.option.error": "שגיאה",
|
||||
|
||||
"settings.appearance.behavior.title": "אינטראקציה",
|
||||
"settings.appearance.behavior.subtitle": "ברירות מחדל להודעות, diff וקלט.",
|
||||
|
||||
@@ -113,6 +113,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "OpenCode のログレベル",
|
||||
"settings.opencode.logLevel.subtitle": "新しい OpenCode インスタンスの起動時に使うログレベルを設定します。",
|
||||
"settings.opencode.logLevel.selector.title": "ログ出力の詳細度",
|
||||
"settings.opencode.logLevel.selector.subtitle": "新しい OpenCode インスタンスがどの程度ログを出力するかを選択します。",
|
||||
"settings.opencode.logLevel.option.debug": "デバッグ",
|
||||
"settings.opencode.logLevel.option.info": "情報",
|
||||
"settings.opencode.logLevel.option.warn": "警告",
|
||||
"settings.opencode.logLevel.option.error": "エラー",
|
||||
|
||||
"settings.appearance.behavior.title": "操作",
|
||||
"settings.appearance.behavior.subtitle": "メッセージ、差分、入力の既定値。",
|
||||
|
||||
@@ -113,6 +113,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "Уровень логирования OpenCode",
|
||||
"settings.opencode.logLevel.subtitle": "Задайте уровень логирования, используемый при запуске новых экземпляров OpenCode.",
|
||||
"settings.opencode.logLevel.selector.title": "Подробность логов",
|
||||
"settings.opencode.logLevel.selector.subtitle": "Выберите, сколько логов должны выводить новые экземпляры OpenCode.",
|
||||
"settings.opencode.logLevel.option.debug": "Отладка",
|
||||
"settings.opencode.logLevel.option.info": "Информация",
|
||||
"settings.opencode.logLevel.option.warn": "Предупреждение",
|
||||
"settings.opencode.logLevel.option.error": "Ошибка",
|
||||
|
||||
"settings.appearance.behavior.title": "Взаимодействие",
|
||||
"settings.appearance.behavior.subtitle": "Значения по умолчанию для сообщений, диффов и ввода.",
|
||||
|
||||
@@ -113,6 +113,14 @@ export const settingsMessages = {
|
||||
"settings.section.opencode.subtitle": "Choose the OpenCode binary and environment used for new instances.",
|
||||
"settings.opencode.runtime.title": "Runtime",
|
||||
"settings.opencode.runtime.subtitle": "Configure which OpenCode binary new instances launch with.",
|
||||
"settings.opencode.logLevel.title": "OpenCode 日志级别",
|
||||
"settings.opencode.logLevel.subtitle": "设置启动新的 OpenCode 实例时使用的日志级别。",
|
||||
"settings.opencode.logLevel.selector.title": "日志详细程度",
|
||||
"settings.opencode.logLevel.selector.subtitle": "选择新的 OpenCode 实例应输出多少日志信息。",
|
||||
"settings.opencode.logLevel.option.debug": "调试",
|
||||
"settings.opencode.logLevel.option.info": "信息",
|
||||
"settings.opencode.logLevel.option.warn": "警告",
|
||||
"settings.opencode.logLevel.option.error": "错误",
|
||||
|
||||
"settings.appearance.behavior.title": "交互",
|
||||
"settings.appearance.behavior.subtitle": "消息、差异与输入的默认值。",
|
||||
|
||||
@@ -28,6 +28,7 @@ export type DiffViewMode = "split" | "unified"
|
||||
export type ExpansionPreference = "expanded" | "collapsed"
|
||||
export type ToolInputsVisibilityPreference = "hidden" | "collapsed" | "expanded"
|
||||
export type ListeningMode = "local" | "all"
|
||||
export type ServerLogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"
|
||||
export type SpeechProviderPreference = "openai-compatible"
|
||||
export type SpeechPlaybackMode = "streaming" | "buffered"
|
||||
export type SpeechTtsFormat = "mp3" | "wav" | "opus" | "aac"
|
||||
@@ -94,6 +95,7 @@ interface UiConfigBucket {
|
||||
|
||||
interface ServerConfigBucket {
|
||||
listeningMode?: ListeningMode
|
||||
logLevel?: ServerLogLevel
|
||||
environmentVariables?: Record<string, string>
|
||||
opencodeBinary?: string
|
||||
speech?: Partial<SpeechSettings>
|
||||
@@ -272,13 +274,17 @@ function normalizeUiState(input?: UiStateBucket | null): NormalizedUiState {
|
||||
|
||||
function normalizeServerConfig(
|
||||
input?: ServerConfigBucket | null,
|
||||
): Required<Pick<ServerConfigBucket, "listeningMode" | "environmentVariables" | "opencodeBinary">> & { speech: SpeechSettings } {
|
||||
): Required<Pick<ServerConfigBucket, "listeningMode" | "logLevel" | "environmentVariables" | "opencodeBinary">> & { speech: SpeechSettings } {
|
||||
const source = input ?? {}
|
||||
const listeningMode = source.listeningMode === "all" ? "all" : "local"
|
||||
const logLevel =
|
||||
source.logLevel === "INFO" || source.logLevel === "WARN" || source.logLevel === "ERROR" || source.logLevel === "DEBUG"
|
||||
? source.logLevel
|
||||
: "DEBUG"
|
||||
const opencodeBinary = typeof source.opencodeBinary === "string" && source.opencodeBinary.trim() ? source.opencodeBinary : "opencode"
|
||||
const environmentVariables = normalizeRecord(source.environmentVariables)
|
||||
const speech = normalizeSpeechSettings(source.speech)
|
||||
return { listeningMode, opencodeBinary, environmentVariables, speech }
|
||||
return { listeningMode, logLevel, opencodeBinary, environmentVariables, speech }
|
||||
}
|
||||
|
||||
function getModelKey(model: { providerId: string; modelId: string }): string {
|
||||
@@ -409,6 +415,11 @@ function updateLastUsedBinary(path: string): void {
|
||||
void patchStateOwner("ui", { opencodeBinaries: nextList }).catch((error) => log.error("Failed to update binary list", error))
|
||||
}
|
||||
|
||||
function updateLogLevel(level: ServerLogLevel): void {
|
||||
const target = level ?? "DEBUG"
|
||||
void patchConfigOwner("server", { logLevel: target }).catch((error) => log.error("Failed to set log level", error))
|
||||
}
|
||||
|
||||
async function updateSpeechSettings(updates: SpeechSettingsUpdate): Promise<void> {
|
||||
const apiKeyPatch = updates.apiKey
|
||||
const { apiKey: _apiKey, ...restUpdates } = updates
|
||||
@@ -612,8 +623,9 @@ interface ConfigContextValue {
|
||||
updateEnvironmentVariables: typeof updateEnvironmentVariables
|
||||
addEnvironmentVariable: typeof addEnvironmentVariable
|
||||
removeEnvironmentVariable: typeof removeEnvironmentVariable
|
||||
updateLastUsedBinary: typeof updateLastUsedBinary
|
||||
updateSpeechSettings: typeof updateSpeechSettings
|
||||
updateLastUsedBinary: typeof updateLastUsedBinary
|
||||
updateLogLevel: typeof updateLogLevel
|
||||
updateSpeechSettings: typeof updateSpeechSettings
|
||||
|
||||
// ui-owned state
|
||||
recentFolders: typeof recentFolders
|
||||
@@ -663,6 +675,7 @@ const configContextValue: ConfigContextValue = {
|
||||
addEnvironmentVariable,
|
||||
removeEnvironmentVariable,
|
||||
updateLastUsedBinary,
|
||||
updateLogLevel,
|
||||
updateSpeechSettings,
|
||||
recentFolders,
|
||||
opencodeBinaries,
|
||||
@@ -746,6 +759,7 @@ export {
|
||||
addEnvironmentVariable,
|
||||
removeEnvironmentVariable,
|
||||
updateLastUsedBinary,
|
||||
updateLogLevel,
|
||||
updateSpeechSettings,
|
||||
addRecentFolder,
|
||||
removeRecentFolder,
|
||||
|
||||
Reference in New Issue
Block a user