2026-02-28 20:42:40 +00:00
import { useState , useEffect } from "react" ;
2026-02-27 04:29:51 +00:00
import DockerSettings from "./DockerSettings" ;
2026-02-27 15:22:49 +00:00
import AwsSettings from "./AwsSettings" ;
2026-02-27 18:39:20 -08:00
import { useSettings } from "../../hooks/useSettings" ;
2026-02-28 21:18:33 +00:00
import { useUpdates } from "../../hooks/useUpdates" ;
2026-03-01 01:21:33 +00:00
import ClaudeInstructionsModal from "../projects/ClaudeInstructionsModal" ;
import EnvVarsModal from "../projects/EnvVarsModal" ;
2026-03-01 15:57:22 +00:00
import { detectHostTimezone } from "../../lib/tauri-commands" ;
2026-03-01 01:21:33 +00:00
import type { EnvVar } from "../../lib/types" ;
2026-03-12 09:35:04 -07:00
import Tooltip from "../ui/Tooltip" ;
2026-02-27 04:29:51 +00:00
export default function SettingsPanel() {
2026-02-27 18:39:20 -08:00
const { appSettings , saveSettings } = useSettings ( ) ;
2026-03-12 09:26:58 -07:00
const { appVersion , imageUpdateInfo , checkForUpdates , checkImageUpdate } = useUpdates ( ) ;
2026-02-28 20:42:40 +00:00
const [ globalInstructions , setGlobalInstructions ] = useState ( appSettings ? . global_claude_instructions ? ? "" ) ;
2026-03-01 01:21:33 +00:00
const [ globalEnvVars , setGlobalEnvVars ] = useState < EnvVar [ ] > ( appSettings ? . global_custom_env_vars ? ? [ ] ) ;
2026-02-28 21:18:33 +00:00
const [ checkingUpdates , setCheckingUpdates ] = useState ( false ) ;
2026-03-01 15:57:22 +00:00
const [ timezone , setTimezone ] = useState ( appSettings ? . timezone ? ? "" ) ;
2026-03-01 01:21:33 +00:00
const [ showInstructionsModal , setShowInstructionsModal ] = useState ( false ) ;
const [ showEnvVarsModal , setShowEnvVarsModal ] = useState ( false ) ;
2026-02-28 20:42:40 +00:00
// Sync local state when appSettings change
useEffect ( ( ) = > {
setGlobalInstructions ( appSettings ? . global_claude_instructions ? ? "" ) ;
2026-03-01 01:21:33 +00:00
setGlobalEnvVars ( appSettings ? . global_custom_env_vars ? ? [ ] ) ;
2026-03-01 15:57:22 +00:00
setTimezone ( appSettings ? . timezone ? ? "" ) ;
} , [ appSettings ? . global_claude_instructions , appSettings ? . global_custom_env_vars , appSettings ? . timezone ] ) ;
// Auto-detect timezone on first load if not yet set
useEffect ( ( ) = > {
if ( appSettings && ! appSettings . timezone ) {
detectHostTimezone ( ) . then ( ( tz ) = > {
setTimezone ( tz ) ;
saveSettings ( { . . . appSettings , timezone : tz } ) ;
} ) . catch ( ( ) = > { } ) ;
}
} , [ appSettings ? . timezone ] ) ;
2026-02-27 18:39:20 -08:00
2026-02-28 21:18:33 +00:00
const handleCheckNow = async ( ) = > {
setCheckingUpdates ( true ) ;
try {
2026-03-12 09:26:58 -07:00
await Promise . all ( [ checkForUpdates ( ) , checkImageUpdate ( ) ] ) ;
2026-02-28 21:18:33 +00:00
} finally {
setCheckingUpdates ( false ) ;
}
} ;
const handleAutoCheckToggle = async ( ) = > {
if ( ! appSettings ) return ;
await saveSettings ( { . . . appSettings , auto_check_updates : ! appSettings . auto_check_updates } ) ;
} ;
2026-02-27 04:29:51 +00:00
return (
< div className = "p-4 space-y-6" >
< h2 className = "text-xs font-semibold uppercase text-[var(--text-secondary)]" >
Settings
< / h2 >
< DockerSettings / >
2026-02-27 15:22:49 +00:00
< AwsSettings / >
2026-03-01 01:21:33 +00:00
2026-03-01 15:57:22 +00:00
{ /* Container Timezone */ }
< div >
2026-03-12 09:35:04 -07:00
< label className = "block text-sm font-medium mb-1" > Container Timezone < Tooltip text = "Sets the timezone inside containers. Affects scheduled task timing and log timestamps." / > < / label >
2026-03-01 15:57:22 +00:00
< p className = "text-xs text-[var(--text-secondary)] mb-1.5" >
Timezone for containers — affects scheduled task timing ( IANA format , e . g . America / New_York )
< / p >
< input
type = "text"
value = { timezone }
onChange = { ( e ) = > setTimezone ( e . target . value ) }
onBlur = { async ( ) = > {
if ( appSettings ) {
await saveSettings ( { . . . appSettings , timezone : timezone || null } ) ;
}
} }
placeholder = "UTC"
className = "w-full px-2 py-1 text-sm bg-[var(--bg-primary)] border border-[var(--border-color)] rounded focus:outline-none focus:border-[var(--accent)]"
/ >
< / div >
2026-03-01 01:21:33 +00:00
{ /* Global Claude Instructions */ }
2026-02-27 18:39:20 -08:00
< div >
2026-03-12 09:35:04 -07:00
< label className = "block text-sm font-medium mb-1" > Claude Instructions < Tooltip text = "Global instructions applied to all projects. Written to ~/.claude/CLAUDE.md in every container." / > < / label >
2026-02-27 18:39:20 -08:00
< p className = "text-xs text-[var(--text-secondary)] mb-1.5" >
Global instructions applied to all projects ( written to ~ / . c l a u d e / C L A U D E . m d i n c o n t a i n e r s )
< / p >
2026-03-01 01:21:33 +00:00
< div className = "flex items-center justify-between" >
< span className = "text-xs text-[var(--text-secondary)]" >
{ globalInstructions ? "Configured" : "Not set" }
< / span >
< button
onClick = { ( ) = > setShowInstructionsModal ( true ) }
className = "text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
>
Edit
< / button >
< / div >
< / div >
{ /* Global Environment Variables */ }
< div >
2026-03-12 09:35:04 -07:00
< label className = "block text-sm font-medium mb-1" > Global Environment Variables < Tooltip text = "Env vars injected into all containers. Per-project vars with the same key take precedence." / > < / label >
2026-03-01 01:21:33 +00:00
< p className = "text-xs text-[var(--text-secondary)] mb-1.5" >
Applied to all project containers . Per - project variables override global ones with the same key .
< / p >
< div className = "flex items-center justify-between" >
< span className = "text-xs text-[var(--text-secondary)]" >
{ globalEnvVars . length > 0 ? ` ${ globalEnvVars . length } variable ${ globalEnvVars . length === 1 ? "" : "s" } ` : "None" }
< / span >
< button
onClick = { ( ) = > setShowEnvVarsModal ( true ) }
className = "text-xs px-2 py-0.5 text-[var(--accent)] hover:text-[var(--accent-hover)] hover:bg-[var(--bg-primary)] rounded transition-colors"
>
Edit
< / button >
< / div >
2026-02-27 18:39:20 -08:00
< / div >
2026-02-28 21:18:33 +00:00
{ /* Updates section */ }
< div >
2026-03-12 09:35:04 -07:00
< label className = "block text-sm font-medium mb-2" > Updates < Tooltip text = "Check for new versions of the Triple-C app and container image." / > < / label >
2026-02-28 21:18:33 +00:00
< div className = "space-y-2" >
{ appVersion && (
< p className = "text-xs text-[var(--text-secondary)]" >
Current version : < span className = "text-[var(--text-primary)] font-mono" > { appVersion } < / span >
< / p >
) }
< div className = "flex items-center gap-2" >
< label className = "text-xs text-[var(--text-secondary)]" > Auto - check for updates < / label >
< button
onClick = { handleAutoCheckToggle }
className = { ` px-2 py-0.5 text-xs rounded transition-colors ${
appSettings ? . auto_check_updates !== false
? "bg-[var(--success)] text-white"
: "bg-[var(--bg-primary)] border border-[var(--border-color)] text-[var(--text-secondary)]"
} ` }
>
{ appSettings ? . auto_check_updates !== false ? "ON" : "OFF" }
< / button >
< / div >
< button
onClick = { handleCheckNow }
disabled = { checkingUpdates }
className = "px-3 py-1.5 text-xs bg-[var(--bg-primary)] border border-[var(--border-color)] rounded hover:bg-[var(--border-color)] disabled:opacity-50 transition-colors"
>
{ checkingUpdates ? "Checking..." : "Check now" }
< / button >
2026-03-12 09:26:58 -07:00
{ imageUpdateInfo && (
< div className = "flex items-center gap-2 px-3 py-2 text-xs bg-[var(--bg-primary)] border border-[var(--warning,#f59e0b)] rounded" >
< span className = "inline-block w-2 h-2 rounded-full bg-[var(--warning,#f59e0b)]" / >
< span > A newer container image is available . Re - pull the image in Docker settings above to update . < / span >
< / div >
) }
2026-02-28 21:18:33 +00:00
< / div >
< / div >
2026-03-01 01:21:33 +00:00
{ showInstructionsModal && (
< ClaudeInstructionsModal
instructions = { globalInstructions }
disabled = { false }
onSave = { async ( instructions ) = > {
setGlobalInstructions ( instructions ) ;
if ( appSettings ) {
await saveSettings ( { . . . appSettings , global_claude_instructions : instructions || null } ) ;
}
} }
onClose = { ( ) = > setShowInstructionsModal ( false ) }
/ >
) }
{ showEnvVarsModal && (
< EnvVarsModal
envVars = { globalEnvVars }
disabled = { false }
onSave = { async ( vars ) = > {
setGlobalEnvVars ( vars ) ;
if ( appSettings ) {
await saveSettings ( { . . . appSettings , global_custom_env_vars : vars } ) ;
}
} }
onClose = { ( ) = > setShowEnvVarsModal ( false ) }
/ >
) }
2026-02-27 04:29:51 +00:00
< / div >
) ;
}