app: add loopback multi-project registry
This commit is contained in:
@@ -40,6 +40,9 @@ type paletteAction struct {
|
||||
|
||||
// For settings actions, the updated settings snapshot to persist.
|
||||
settings *settings
|
||||
|
||||
projectKey string
|
||||
projectPath string
|
||||
}
|
||||
|
||||
// Group ids order the section bands the palette renders when no query
|
||||
@@ -48,6 +51,7 @@ type paletteAction struct {
|
||||
// an equally tight Spawn-section hit.
|
||||
const (
|
||||
groupFocused = iota
|
||||
groupProject
|
||||
groupOpen
|
||||
groupSpawn
|
||||
groupSettings
|
||||
@@ -64,6 +68,14 @@ type paletteItem struct {
|
||||
matches []int
|
||||
}
|
||||
|
||||
type paletteProject struct {
|
||||
Key string
|
||||
Dir string
|
||||
Name string
|
||||
TabCount int
|
||||
IsCurrent bool
|
||||
}
|
||||
|
||||
// paletteMode toggles the palette between its fuzzy-picker UI and the
|
||||
// freeform "spawn process" form. The form lives inside the palette so
|
||||
// it shares the same modal-input contract (every byte intercepted; no
|
||||
@@ -120,10 +132,12 @@ type paletteState struct {
|
||||
|
||||
items []paletteItem
|
||||
|
||||
mode paletteMode
|
||||
form *spawnProcessForm
|
||||
renameForm *renameForm
|
||||
settingsInput *settingsInputForm
|
||||
mode paletteMode
|
||||
form *spawnProcessForm
|
||||
renameForm *renameForm
|
||||
settingsInput *settingsInputForm
|
||||
projects []paletteProject
|
||||
currentProject string
|
||||
|
||||
// showHelp swaps the item list for a static keybinding cheat-sheet
|
||||
// until the next keystroke. Toggled by `?` in picker mode.
|
||||
@@ -189,6 +203,12 @@ func newPalette(children []*Child, focused, focusedPad string, presets preset.Se
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *paletteState) setProjects(current string, projects []paletteProject) {
|
||||
p.currentProject = current
|
||||
p.projects = append(p.projects[:0], projects...)
|
||||
p.rebuild()
|
||||
}
|
||||
|
||||
func (p *paletteState) rebuild() {
|
||||
// Macro is resolved on the *original-case* query; the returned rest
|
||||
// keeps the user's casing intact (useful when Tab cycles chips).
|
||||
@@ -294,7 +314,33 @@ func (p *paletteState) buildItems(macro string) []paletteItem {
|
||||
}
|
||||
}
|
||||
|
||||
// Group 1: Open — switch entries for every running child *other than*
|
||||
if p.projects != nil {
|
||||
// Group 1: Project — move the current client view without tearing
|
||||
// down processes owned by the previous project.
|
||||
for _, pr := range p.projects {
|
||||
if pr.IsCurrent || pr.Key == p.currentProject {
|
||||
continue
|
||||
}
|
||||
hint := pr.Dir
|
||||
if pr.TabCount > 0 {
|
||||
hint = fmt.Sprintf("%s · %d tabs", hint, pr.TabCount)
|
||||
}
|
||||
out = append(out, paletteItem{
|
||||
label: "Switch project: " + pr.Name,
|
||||
hint: hint,
|
||||
action: paletteAction{kind: "project-switch", projectKey: pr.Key},
|
||||
group: groupProject,
|
||||
})
|
||||
}
|
||||
out = append(out, paletteItem{
|
||||
label: "Open project…",
|
||||
hint: "attach this client view to another local directory",
|
||||
action: paletteAction{kind: "project-open-form"},
|
||||
group: groupProject,
|
||||
})
|
||||
}
|
||||
|
||||
// Group 2: Open — switch entries for every running child *other than*
|
||||
// the one already focused (no point offering a no-op switch). Dead
|
||||
// agents are filtered out (no restart path); dead command processes
|
||||
// remain so they can be restarted.
|
||||
@@ -655,6 +701,9 @@ func (p *paletteState) acceptOrEnterForm(adv int) (paletteAction, bool, int) {
|
||||
p.cursor = 0
|
||||
p.rebuildSettings()
|
||||
return paletteAction{}, false, adv
|
||||
case "project-open-form":
|
||||
p.enterRenameForm("project", "", "", "project path")
|
||||
return paletteAction{}, false, adv
|
||||
case "pad-rename-form":
|
||||
p.enterRenameForm("pad", a.padName, a.padName, "scratchpad: "+a.padName)
|
||||
return paletteAction{}, false, adv
|
||||
@@ -913,6 +962,9 @@ func (p *paletteState) submitRename() paletteAction {
|
||||
return paletteAction{kind: "cancel"}
|
||||
}
|
||||
newName := strings.TrimSpace(string(p.renameForm.name))
|
||||
if p.renameForm.subject == "project" {
|
||||
return paletteAction{kind: "project-open-submit", projectPath: newName}
|
||||
}
|
||||
if newName == "" {
|
||||
return paletteAction{kind: "cancel"}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user