feat: complete session preferences and PAM fixture gates
This commit is contained in:
@@ -18,8 +18,6 @@ import (
|
||||
"latchd/tui/bg"
|
||||
)
|
||||
|
||||
// ── pixelated user avatar ───────────────────────────────────────────────
|
||||
|
||||
var avatarArt = []string{
|
||||
` ▄▄▄▄▄ `,
|
||||
` ▄███████▄ `,
|
||||
@@ -31,8 +29,6 @@ var avatarArt = []string{
|
||||
` ██ ██ `,
|
||||
}
|
||||
|
||||
// ── messages ─────────────────────────────────────────────────────────────
|
||||
|
||||
type tickMsg time.Time
|
||||
|
||||
type authResult struct {
|
||||
@@ -57,8 +53,6 @@ const (
|
||||
modeAdminPass
|
||||
)
|
||||
|
||||
// ── result (sent back to main on successful login) ──────────────────────
|
||||
|
||||
type Result struct {
|
||||
Username string
|
||||
Password string
|
||||
@@ -67,8 +61,6 @@ type Result struct {
|
||||
Preview bool
|
||||
}
|
||||
|
||||
// ── model ───────────────────────────────────────────────────────────────
|
||||
|
||||
type Model struct {
|
||||
cfg config.Config
|
||||
preview bool
|
||||
@@ -137,8 +129,6 @@ func (m *Model) Init() tea.Cmd {
|
||||
return tea.Batch(tick(), tea.EnterAltScreen)
|
||||
}
|
||||
|
||||
// ── update ──────────────────────────────────────────────────────────────
|
||||
|
||||
func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
@@ -222,8 +212,6 @@ func (m *Model) handleBypass(msg adminBypassResult) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// ── keys ────────────────────────────────────────────────────────────────
|
||||
|
||||
func (m *Model) handleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
k := msg.String()
|
||||
|
||||
@@ -392,8 +380,6 @@ func (m *Model) selectedEnv() session.PostLoginEnv {
|
||||
return m.envs[m.selIdx]
|
||||
}
|
||||
|
||||
// ── commands ────────────────────────────────────────────────────────────
|
||||
|
||||
func (m *Model) loginCmd() tea.Cmd {
|
||||
if m.preview {
|
||||
return func() tea.Msg { time.Sleep(800 * time.Millisecond); return authResult{ok: true} }
|
||||
@@ -422,14 +408,10 @@ func (m *Model) adminBypassCmd() tea.Cmd {
|
||||
}
|
||||
}
|
||||
|
||||
// ── queries ────────────────────────────────────────────────────────────
|
||||
|
||||
func (m *Model) Result() *Result { return m.result }
|
||||
func (m *Model) Quitting() bool { return m.quitting }
|
||||
func (m *Model) SetAutomata(a *bg.Automata) { m.automata = a }
|
||||
|
||||
// ── view ───────────────────────────────────────────────────────────────
|
||||
|
||||
var (
|
||||
accent = lipgloss.Color("208") // orange
|
||||
subtle = lipgloss.Color("8") // grey
|
||||
@@ -527,7 +509,6 @@ func overlayCenter(w, h int, content string) string {
|
||||
func (m *Model) renderLayout() string {
|
||||
clock := m.renderClock()
|
||||
|
||||
// top bar: power hints on left, clock on right
|
||||
var topParts []string
|
||||
topParts = append(topParts, m.renderPowerHints())
|
||||
if clock != "" {
|
||||
@@ -535,24 +516,19 @@ func (m *Model) renderLayout() string {
|
||||
}
|
||||
top := strings.Join(topParts, " ")
|
||||
|
||||
// avatar + form side by side
|
||||
avatar := m.renderAvatar()
|
||||
form := m.renderForm()
|
||||
mid := lipgloss.JoinHorizontal(lipgloss.Top, avatar, " ", form)
|
||||
|
||||
// DE switcher with specifier label
|
||||
sw := m.renderSwitcher()
|
||||
|
||||
// caps lock
|
||||
caps := m.renderCapsLock()
|
||||
|
||||
// admin unlock
|
||||
var admin string
|
||||
if m.mode == modeAdminUser || m.mode == modeAdminPass {
|
||||
admin = m.renderAdminForm()
|
||||
}
|
||||
|
||||
// status
|
||||
st := m.renderStatus()
|
||||
|
||||
var parts []string
|
||||
@@ -655,7 +631,6 @@ func (m *Model) renderSwitcher() string {
|
||||
|
||||
var parts []string
|
||||
|
||||
// session specifier (like "x11", "wayland")
|
||||
spec := swDot.Render(m.envs[m.selIdx].Kind + " ")
|
||||
|
||||
if cfg.ShowMovers && m.selIdx > 0 {
|
||||
@@ -679,7 +654,6 @@ func (m *Model) renderSwitcher() string {
|
||||
|
||||
swLine := lipgloss.JoinHorizontal(lipgloss.Center, parts...)
|
||||
|
||||
// Dot indicators
|
||||
var dots []string
|
||||
for i := 0; i < len(m.envs); i++ {
|
||||
if i == m.selIdx {
|
||||
@@ -747,8 +721,6 @@ func (m *Model) renderStatus() string {
|
||||
return st.Align(lipgloss.Center).Width(70).Render(m.status)
|
||||
}
|
||||
|
||||
// ── caps lock detection ────────────────────────────────────────────────
|
||||
|
||||
const kdgkbled = 0x4B64
|
||||
|
||||
func capsLockOn() bool {
|
||||
@@ -762,8 +734,6 @@ func capsLockOn() bool {
|
||||
return leds&0x04 != 0
|
||||
}
|
||||
|
||||
// ── field editing ──────────────────────────────────────────────────────
|
||||
|
||||
func handleField(k string, buf *string, cur, scroll *int) {
|
||||
switch {
|
||||
case k == "backspace" || k == "ctrl+h":
|
||||
@@ -846,8 +816,6 @@ func abs(n int) int {
|
||||
return n
|
||||
}
|
||||
|
||||
// ── tick ────────────────────────────────────────────────────────────────
|
||||
|
||||
func tick() tea.Cmd {
|
||||
return tea.Tick(time.Second/30, func(t time.Time) tea.Msg {
|
||||
return tickMsg(t)
|
||||
|
||||
Reference in New Issue
Block a user