238 lines
7.9 KiB
TypeScript
238 lines
7.9 KiB
TypeScript
import React, { useState, useEffect } from 'react'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { useUiStore } from '../../store/ui.store'
|
|
import TitleBar from '../../components/TitleBar'
|
|
import Button from '../../components/Button'
|
|
import Input from '../../components/Input'
|
|
import Spinner from '../../components/Spinner'
|
|
import type { AppSettings } from '@shared/types/ipc.types'
|
|
|
|
export const SettingsView: React.FC = () => {
|
|
const navigate = useNavigate()
|
|
const { showToast } = useUiStore()
|
|
|
|
const [settings, setSettings] = useState<AppSettings | null>(null)
|
|
const [loading, setLoading] = useState(false)
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setLoading(true)
|
|
window.flowgit.settings
|
|
.get()
|
|
.then((data) => {
|
|
setSettings(data)
|
|
})
|
|
.catch((e: unknown) => {
|
|
showToast({
|
|
type: 'error',
|
|
title: 'Failed to load settings',
|
|
message: e instanceof Error ? e.message : String(e),
|
|
})
|
|
})
|
|
.finally(() => {
|
|
setLoading(false)
|
|
})
|
|
}, [showToast])
|
|
|
|
const handleUpdate = async <K extends keyof AppSettings>(key: K, value: AppSettings[K]) => {
|
|
if (!settings) return
|
|
setSettings({ ...settings, [key]: value })
|
|
try {
|
|
await window.flowgit.settings.set(key, value)
|
|
showToast({ type: 'success', title: 'Setting saved' })
|
|
} catch (e: unknown) {
|
|
showToast({
|
|
type: 'error',
|
|
title: 'Error updating setting',
|
|
message: e instanceof Error ? e.message : String(e),
|
|
})
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100vh',
|
|
width: '100vw',
|
|
overflow: 'hidden',
|
|
background: '#000000',
|
|
fontFamily: 'var(--font-mono, monospace)',
|
|
}}
|
|
>
|
|
<TitleBar showRepoSwitcher={false} />
|
|
|
|
{/* Header bar */}
|
|
<div
|
|
style={{
|
|
padding: '16px 32px',
|
|
background: '#141414',
|
|
borderBottom: '1px solid #202020',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
<button
|
|
onClick={() => navigate(-1)}
|
|
style={{
|
|
background: 'none',
|
|
border: 'none',
|
|
color: '#da5c2c',
|
|
fontSize: '14px',
|
|
fontFamily: 'var(--font-mono, monospace)',
|
|
cursor: 'pointer',
|
|
padding: 0,
|
|
}}
|
|
>
|
|
← Back
|
|
</button>
|
|
<span style={{ color: '#3a3a3a' }}>/</span>
|
|
<span style={{ fontSize: '15px', fontWeight: 700, color: '#eeeeee' }}>SETTINGS</span>
|
|
</div>
|
|
|
|
{loading && <Spinner size="sm" />}
|
|
</div>
|
|
|
|
{/* Settings Form Body */}
|
|
<div style={{ flex: 1, overflowY: 'auto', padding: '32px', maxWidth: '800px', margin: '0 auto', width: '100%' }}>
|
|
{settings ? (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '28px' }}>
|
|
{/* Git binary configuration */}
|
|
<div style={sectionBoxStyle}>
|
|
<div style={sectionTitleStyle}>Git Binary Path</div>
|
|
<div style={sectionDescStyle}>
|
|
Leave blank to automatically detect git from your system PATH.
|
|
</div>
|
|
<Input
|
|
placeholder="Auto-detect (system PATH)"
|
|
value={settings.gitPath ?? ''}
|
|
onChange={(e) => handleUpdate('gitPath', e.target.value.trim() || null)}
|
|
/>
|
|
</div>
|
|
|
|
{/* General Preferences */}
|
|
<div style={sectionBoxStyle}>
|
|
<div style={sectionTitleStyle}>Behavior & Safety</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '14px', marginTop: '12px' }}>
|
|
<label style={checkboxLabelStyle}>
|
|
<input
|
|
type="checkbox"
|
|
checked={settings.confirmDestructive}
|
|
onChange={(e) => handleUpdate('confirmDestructive', e.target.checked)}
|
|
style={{ accentColor: '#da5c2c' }}
|
|
/>
|
|
<span>Confirm before destructive operations (discard, delete branch, drop stash)</span>
|
|
</label>
|
|
|
|
<label style={checkboxLabelStyle}>
|
|
<input
|
|
type="checkbox"
|
|
checked={settings.autoFetch}
|
|
onChange={(e) => handleUpdate('autoFetch', e.target.checked)}
|
|
style={{ accentColor: '#da5c2c' }}
|
|
/>
|
|
<span>Automatically fetch from remotes in background</span>
|
|
</label>
|
|
|
|
<label style={checkboxLabelStyle}>
|
|
<input
|
|
type="checkbox"
|
|
checked={settings.showHiddenFiles}
|
|
onChange={(e) => handleUpdate('showHiddenFiles', e.target.checked)}
|
|
style={{ accentColor: '#da5c2c' }}
|
|
/>
|
|
<span>Show hidden dotfiles (.gitignore, .env, etc.)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Pull Strategy */}
|
|
<div style={sectionBoxStyle}>
|
|
<div style={sectionTitleStyle}>Default Pull Strategy</div>
|
|
<div style={sectionDescStyle}>
|
|
How FlowGit integrates remote branch updates when pulling.
|
|
</div>
|
|
<div style={{ display: 'flex', gap: '16px', marginTop: '10px' }}>
|
|
{(['merge', 'rebase', 'ff-only'] as const).map((strat) => (
|
|
<label key={strat} style={{ ...checkboxLabelStyle, cursor: 'pointer' }}>
|
|
<input
|
|
type="radio"
|
|
name="pullStrategy"
|
|
checked={settings.pullStrategy === strat}
|
|
onChange={() => handleUpdate('pullStrategy', strat)}
|
|
style={{ accentColor: '#da5c2c' }}
|
|
/>
|
|
<span style={{ textTransform: 'uppercase', fontSize: '12px' }}>{strat}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Theme & Aesthetics */}
|
|
<div style={sectionBoxStyle}>
|
|
<div style={sectionTitleStyle}>Appearance</div>
|
|
<div style={sectionDescStyle}>
|
|
FlowGit uses Axiom Dark Design Language exclusively. Monospaced typography with BerkeleyMono.
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginTop: '10px' }}>
|
|
<div
|
|
style={{
|
|
background: '#111111',
|
|
border: '1px solid #da5c2c',
|
|
borderRadius: '2px',
|
|
padding: '8px 16px',
|
|
color: '#eeeeee',
|
|
fontSize: '12px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '8px',
|
|
}}
|
|
>
|
|
<span style={{ color: '#da5c2c' }}>●</span>
|
|
<span>Void Dark (#000000)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const sectionBoxStyle: React.CSSProperties = {
|
|
background: '#111111',
|
|
border: '1px solid #202020',
|
|
borderRadius: '2px',
|
|
padding: '20px',
|
|
}
|
|
|
|
const sectionTitleStyle: React.CSSProperties = {
|
|
color: '#eeeeee',
|
|
fontSize: '14px',
|
|
fontWeight: 700,
|
|
letterSpacing: '0.02em',
|
|
}
|
|
|
|
const sectionDescStyle: React.CSSProperties = {
|
|
color: '#7e7e7e',
|
|
fontSize: '12px',
|
|
marginTop: '4px',
|
|
marginBottom: '12px',
|
|
lineHeight: 1.5,
|
|
}
|
|
|
|
const checkboxLabelStyle: React.CSSProperties = {
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '10px',
|
|
fontSize: '13px',
|
|
color: '#b4b4b4',
|
|
cursor: 'pointer',
|
|
}
|
|
|
|
export default SettingsView
|