feat(tui): add interactive run mode for checklists and fill mode for templates
Run mode (r key on checklist cards): cursor navigates steps, space toggles done/undone, r resets all, esc saves changes to DB and exits. Persists step state — improvement over web which discards on exit. Fill mode (f key on template cards): tab/shift-tab navigates slots, type to fill values, enter resolves template and copies to clipboard with use count increment. Esc cancels without copying. Both modes are sub-states of detail view, keeping architecture simple.
This commit is contained in:
+43
-8
@@ -11,11 +11,22 @@ import (
|
||||
"github.com/lerko/nib/internal/display"
|
||||
)
|
||||
|
||||
type detailMode int
|
||||
|
||||
const (
|
||||
detailPreview detailMode = iota
|
||||
detailRun
|
||||
detailFill
|
||||
)
|
||||
|
||||
type detailModel struct {
|
||||
entity *db.Entity
|
||||
scroll int
|
||||
height int
|
||||
width int
|
||||
mode detailMode
|
||||
run runModel
|
||||
fill fillModel
|
||||
}
|
||||
|
||||
func newDetailModel() detailModel {
|
||||
@@ -25,6 +36,7 @@ func newDetailModel() detailModel {
|
||||
func (d *detailModel) setEntity(e *db.Entity) {
|
||||
d.entity = e
|
||||
d.scroll = 0
|
||||
d.mode = detailPreview
|
||||
}
|
||||
|
||||
func (d *detailModel) setSize(width, height int) {
|
||||
@@ -32,19 +44,40 @@ func (d *detailModel) setSize(width, height int) {
|
||||
d.height = height
|
||||
}
|
||||
|
||||
func (d detailModel) update(msg tea.KeyMsg) detailModel {
|
||||
switch msg.String() {
|
||||
case "up", "k":
|
||||
if d.scroll > 0 {
|
||||
d.scroll--
|
||||
func (d detailModel) update(msg tea.KeyMsg) (detailModel, tea.Cmd) {
|
||||
switch d.mode {
|
||||
case detailRun:
|
||||
d.run = d.run.update(msg.String())
|
||||
return d, nil
|
||||
case detailFill:
|
||||
var cmd tea.Cmd
|
||||
d.fill, cmd = d.fill.update(msg)
|
||||
return d, cmd
|
||||
default:
|
||||
switch msg.String() {
|
||||
case "up", "k":
|
||||
if d.scroll > 0 {
|
||||
d.scroll--
|
||||
}
|
||||
case "down", "j":
|
||||
d.scroll++
|
||||
}
|
||||
case "down", "j":
|
||||
d.scroll++
|
||||
return d, nil
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (d detailModel) view(width int) string {
|
||||
switch d.mode {
|
||||
case detailRun:
|
||||
return d.run.view(width)
|
||||
case detailFill:
|
||||
return d.fill.view(width)
|
||||
default:
|
||||
return d.previewView(width)
|
||||
}
|
||||
}
|
||||
|
||||
func (d detailModel) previewView(width int) string {
|
||||
if d.entity == nil {
|
||||
return ""
|
||||
}
|
||||
@@ -154,6 +187,7 @@ func renderCardData(e *db.Entity) string {
|
||||
}
|
||||
progress := fmt.Sprintf(" %d/%d steps", done, len(steps))
|
||||
b.WriteString(detailLabelStyle.Render(progress))
|
||||
b.WriteString(" " + helpStyle.Render("r:run"))
|
||||
|
||||
case db.CardTemplate:
|
||||
slots, ok := data["slots"].([]interface{})
|
||||
@@ -174,6 +208,7 @@ func renderCardData(e *db.Entity) string {
|
||||
}
|
||||
b.WriteString(line + "\n")
|
||||
}
|
||||
b.WriteString(" " + helpStyle.Render("f:fill"))
|
||||
|
||||
case db.CardDecision:
|
||||
if chose, ok := data["chose"].(string); ok && chose != "" {
|
||||
|
||||
Reference in New Issue
Block a user