@@ -11,6 +11,13 @@ import { serverConfigQueryOptions } from "../lib/serverReactQuery";
1111import { ensureNativeApi } from "../nativeApi" ;
1212import { Button } from "../components/ui/button" ;
1313import { Input } from "../components/ui/input" ;
14+ import {
15+ Select ,
16+ SelectItem ,
17+ SelectPopup ,
18+ SelectTrigger ,
19+ SelectValue ,
20+ } from "../components/ui/select" ;
1421import { Switch } from "../components/ui/switch" ;
1522import { APP_VERSION } from "../branding" ;
1623import { SidebarInset } from "~/components/ui/sidebar" ;
@@ -49,6 +56,12 @@ const MODEL_PROVIDER_SETTINGS: Array<{
4956 } ,
5057] as const ;
5158
59+ const TIMESTAMP_FORMAT_LABELS = {
60+ locale : "System default" ,
61+ "12-hour" : "12-hour" ,
62+ "24-hour" : "24-hour" ,
63+ } as const ;
64+
5265function getCustomModelsForProvider (
5366 settings : ReturnType < typeof useAppSettings > [ "settings" ] ,
5467 provider : ProviderKind ,
@@ -210,43 +223,89 @@ function SettingsRouteView() {
210223 < div className = "mb-4" >
211224 < h2 className = "text-sm font-medium text-foreground" > Appearance</ h2 >
212225 < p className = "mt-1 text-xs text-muted-foreground" >
213- Choose how T3 Code handles light and dark mode .
226+ Choose how T3 Code looks across the app .
214227 </ p >
215228 </ div >
216229
217- < div className = "space-y-2" role = "radiogroup" aria-label = "Theme preference" >
218- { THEME_OPTIONS . map ( ( option ) => {
219- const selected = theme === option . value ;
220- return (
221- < button
222- key = { option . value }
223- type = "button"
224- role = "radio"
225- aria-checked = { selected }
226- className = { `flex w-full items-start justify-between rounded-lg border px-3 py-2 text-left transition-colors ${
227- selected
228- ? "border-primary/60 bg-primary/8 text-foreground"
229- : "border-border bg-background text-muted-foreground hover:bg-accent"
230- } `}
231- onClick = { ( ) => setTheme ( option . value ) }
232- >
233- < span className = "flex flex-col" >
234- < span className = "text-sm font-medium" > { option . label } </ span >
235- < span className = "text-xs" > { option . description } </ span >
236- </ span >
237- { selected ? (
238- < span className = "rounded bg-primary/14 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-primary" >
239- Selected
230+ < div className = "space-y-4" >
231+ < div className = "space-y-2" role = "radiogroup" aria-label = "Theme preference" >
232+ { THEME_OPTIONS . map ( ( option ) => {
233+ const selected = theme === option . value ;
234+ return (
235+ < button
236+ key = { option . value }
237+ type = "button"
238+ role = "radio"
239+ aria-checked = { selected }
240+ className = { `flex w-full items-start justify-between rounded-lg border px-3 py-2 text-left transition-colors ${
241+ selected
242+ ? "border-primary/60 bg-primary/8 text-foreground"
243+ : "border-border bg-background text-muted-foreground hover:bg-accent"
244+ } `}
245+ onClick = { ( ) => setTheme ( option . value ) }
246+ >
247+ < span className = "flex flex-col" >
248+ < span className = "text-sm font-medium" > { option . label } </ span >
249+ < span className = "text-xs" > { option . description } </ span >
240250 </ span >
241- ) : null }
242- </ button >
243- ) ;
244- } ) }
245- </ div >
251+ { selected ? (
252+ < span className = "rounded bg-primary/14 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-primary" >
253+ Selected
254+ </ span >
255+ ) : null }
256+ </ button >
257+ ) ;
258+ } ) }
259+ </ div >
246260
247- < p className = "mt-4 text-xs text-muted-foreground" >
248- Active theme: < span className = "font-medium text-foreground" > { resolvedTheme } </ span >
249- </ p >
261+ < p className = "text-xs text-muted-foreground" >
262+ Active theme: < span className = "font-medium text-foreground" > { resolvedTheme } </ span >
263+ </ p >
264+
265+ < div className = "flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2" >
266+ < div >
267+ < p className = "text-sm font-medium text-foreground" > Timestamp format</ p >
268+ < p className = "text-xs text-muted-foreground" >
269+ System default follows your browser or OS time format. < code > 12-hour</ code > { " " }
270+ and < code > 24-hour</ code > force the hour cycle.
271+ </ p >
272+ </ div >
273+ < Select
274+ value = { settings . timestampFormat }
275+ onValueChange = { ( value ) => {
276+ if ( value !== "locale" && value !== "12-hour" && value !== "24-hour" ) return ;
277+ updateSettings ( {
278+ timestampFormat : value ,
279+ } ) ;
280+ } }
281+ >
282+ < SelectTrigger className = "w-40" aria-label = "Timestamp format" >
283+ < SelectValue > { TIMESTAMP_FORMAT_LABELS [ settings . timestampFormat ] } </ SelectValue >
284+ </ SelectTrigger >
285+ < SelectPopup align = "end" >
286+ < SelectItem value = "locale" > { TIMESTAMP_FORMAT_LABELS . locale } </ SelectItem >
287+ < SelectItem value = "12-hour" > { TIMESTAMP_FORMAT_LABELS [ "12-hour" ] } </ SelectItem >
288+ < SelectItem value = "24-hour" > { TIMESTAMP_FORMAT_LABELS [ "24-hour" ] } </ SelectItem >
289+ </ SelectPopup >
290+ </ Select >
291+ </ div >
292+
293+ { settings . timestampFormat !== defaults . timestampFormat ? (
294+ < div className = "flex justify-end" >
295+ < Button
296+ size = "xs"
297+ variant = "outline"
298+ onClick = { ( ) =>
299+ updateSettings ( {
300+ timestampFormat : defaults . timestampFormat ,
301+ } )
302+ }
303+ >
304+ Restore default
305+ </ Button >
306+ </ div >
307+ ) : null }
308+ </ div >
250309 </ section >
251310
252311 < section className = "rounded-2xl border border-border bg-card p-5" >
0 commit comments