@@ -53,21 +53,129 @@ func (m *TuiModel) handlePromptKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
5353}
5454
5555func (m * TuiModel ) handleSettingsKeys (msg tea.KeyMsg ) (tea.Model , tea.Cmd ) {
56- switch msg .String () {
56+ keyStr := msg .String ()
57+ isInputFocused := m .settingsSel == 2 || m .settingsSel == 3
58+
59+ if isInputFocused {
60+ switch keyStr {
61+ case "esc" :
62+ m .activePrompt = PromptNone
63+ m .settingsOrgsInput .Blur ()
64+ m .settingsUserInput .Blur ()
65+ return m , nil
66+ case "up" , "down" , "enter" :
67+ // Intercept and fall through to navigation/actions below
68+ default :
69+ var cmd tea.Cmd
70+ if m .settingsSel == 2 {
71+ m .settingsOrgsInput , cmd = m .settingsOrgsInput .Update (msg )
72+ } else {
73+ m .settingsUserInput , cmd = m .settingsUserInput .Update (msg )
74+ }
75+ return m , cmd
76+ }
77+ }
78+
79+ switch keyStr {
5780 case "esc" , "q" :
5881 m .activePrompt = PromptNone
82+ m .settingsOrgsInput .Blur ()
83+ m .settingsUserInput .Blur ()
5984 return m , nil
6085 case "up" , "k" :
6186 if m .settingsSel >= SettingsFieldCount {
6287 m .settingsSel = SettingsFieldCount - 1
6388 } else if m .settingsSel > 0 {
6489 m .settingsSel --
6590 }
66- case "down" , "j" :
67- if m .settingsSel == SettingsFieldCount - 1 {
68- m .settingsSel = SettingsFieldCount
69- } else if m .settingsSel < SettingsFieldCount - 1 {
70- m .settingsSel ++
91+ m .updateSettingsFocus ()
92+ case "down" , "j" , "enter" :
93+ if keyStr == "enter" && isInputFocused {
94+ m .settingsSel = 5 // Jump directly to SAVE button
95+ m .updateSettingsFocus ()
96+ return m , nil
97+ }
98+
99+ if keyStr == "down" || keyStr == "j" {
100+ if m .settingsSel == SettingsFieldCount - 1 {
101+ m .settingsSel = SettingsFieldCount
102+ } else if m .settingsSel < SettingsFieldCount - 1 {
103+ m .settingsSel ++
104+ }
105+ m .updateSettingsFocus ()
106+ } else if keyStr == "enter" {
107+ switch m .settingsSel {
108+ case 0 : // Install Mode
109+ if m .settingsInstallMode == "npm" {
110+ m .settingsInstallMode = "workspace"
111+ } else {
112+ m .settingsInstallMode = "npm"
113+ }
114+ case 1 : // Catalog Sort
115+ modes := []string {"updated" , "name" , "stars" , "installed" }
116+ idx := 0
117+ for i , mth := range modes {
118+ if mth == m .settingsCatalogSort {
119+ idx = (i + 1 ) % len (modes )
120+ break
121+ }
122+ }
123+ m .settingsCatalogSort = modes [idx ]
124+ case 2 : // Trusted Orgs
125+ // Handled by textinput
126+ case 3 : // GitHub User
127+ // Handled by textinput
128+ case 4 : // Reset Workspace
129+ m .activePrompt = PromptWipeWorkspaceConfirm
130+ m .promptSel = 1 // default to No
131+ case 5 : // Save
132+ m .activePrompt = PromptNone
133+ m .settingsOrgsInput .Blur ()
134+ m .settingsUserInput .Blur ()
135+ if m .ctx != nil {
136+ settings := m .ctx .Settings
137+ settings .InstallMode = m .settingsInstallMode
138+ settings .CatalogSort = m .settingsCatalogSort
139+
140+ // Parse Trusted Orgs (GithubOrgs) from comma-separated list
141+ rawOrgs := m .settingsOrgsInput .Value ()
142+ var orgs []string
143+ for _ , part := range strings .Split (rawOrgs , "," ) {
144+ trimmed := strings .TrimSpace (part )
145+ if trimmed != "" {
146+ orgs = append (orgs , trimmed )
147+ }
148+ }
149+ settings .GithubOrgs = orgs
150+
151+ // Parse GitHub User (GithubUser)
152+ ghUserVal := m .settingsUserInput .Value ()
153+ if ghUserVal == "" {
154+ settings .GithubUser = nil
155+ } else {
156+ settings .GithubUser = & ghUserVal
157+ }
158+
159+ payload := & bridge.WritePayload {
160+ Settings : & settings ,
161+ }
162+ m .statusMsg = "Saving settings…"
163+ m .addLog (">>> Saving settings configuration…" )
164+ if err := bridge .WriteChanges (m .workspaceRoot , payload ); err != nil {
165+ m .statusMsg = fmt .Sprintf ("Save failed: %v" , err )
166+ m .addLog (fmt .Sprintf (">>> Save settings failed: %v" , err ))
167+ } else {
168+ m .statusMsg = "Settings saved successfully."
169+ m .addLog (">>> Settings saved successfully." )
170+ }
171+ }
172+ m .loading = true
173+ return m , tea .Batch (m .loadContextCmd (), m .loadCatalogCmd (false ))
174+ case 6 : // Cancel
175+ m .activePrompt = PromptNone
176+ m .settingsOrgsInput .Blur ()
177+ m .settingsUserInput .Blur ()
178+ }
71179 }
72180 case "left" , "h" :
73181 if m .settingsSel == SettingsFieldCount + 1 {
@@ -77,55 +185,6 @@ func (m *TuiModel) handleSettingsKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
77185 if m .settingsSel == SettingsFieldCount {
78186 m .settingsSel = SettingsFieldCount + 1
79187 }
80- case "enter" :
81- switch m .settingsSel {
82- case 0 : // Install Mode
83- if m .settingsInstallMode == "npm" {
84- m .settingsInstallMode = "workspace"
85- } else {
86- m .settingsInstallMode = "npm"
87- }
88- case 1 : // Catalog Sort
89- modes := []string {"updated" , "name" , "stars" , "installed" }
90- idx := 0
91- for i , mth := range modes {
92- if mth == m .settingsCatalogSort {
93- idx = (i + 1 ) % len (modes )
94- break
95- }
96- }
97- m .settingsCatalogSort = modes [idx ]
98- case 2 : // Trusted Orgs
99- m .addLog (">>> To configure Trusted Orgs, modify .desktop/settings.json (githubOrgs field)." )
100- case 3 : // GitHub User
101- m .addLog (">>> To configure GitHub User, modify .desktop/settings.json or use OWD_GITHUB_USER env var." )
102- case 4 : // Reset Workspace
103- m .activePrompt = PromptWipeWorkspaceConfirm
104- m .promptSel = 1 // default to No
105- case 5 : // Save
106- m .activePrompt = PromptNone
107- if m .ctx != nil {
108- settings := m .ctx .Settings
109- settings .InstallMode = m .settingsInstallMode
110- settings .CatalogSort = m .settingsCatalogSort
111- payload := & bridge.WritePayload {
112- Settings : & settings ,
113- }
114- m .statusMsg = "Saving settings…"
115- m .addLog (">>> Saving settings configuration…" )
116- if err := bridge .WriteChanges (m .workspaceRoot , payload ); err != nil {
117- m .statusMsg = fmt .Sprintf ("Save failed: %v" , err )
118- m .addLog (fmt .Sprintf (">>> Save settings failed: %v" , err ))
119- } else {
120- m .statusMsg = "Settings saved successfully."
121- m .addLog (">>> Settings saved successfully." )
122- }
123- }
124- m .loading = true
125- return m , tea .Batch (m .loadContextCmd (), m .loadCatalogCmd (false ))
126- case 6 : // Cancel
127- m .activePrompt = PromptNone
128- }
129188 }
130189 return m , nil
131190}
@@ -181,6 +240,7 @@ func (m *TuiModel) handleUninstallConfirmKeys(msg tea.KeyMsg) (tea.Model, tea.Cm
181240 m .promptPkg = nil
182241 m .promptQueue = nil
183242 m .promptQueueIndex = 0
243+ m .startServerAfterSetup = false
184244 return m , nil
185245 case "left" , "h" , "up" , "k" :
186246 m .promptSel = 0
@@ -220,6 +280,7 @@ func (m *TuiModel) handleInstallMethodKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd)
220280 m .promptPkg = nil
221281 m .promptQueue = nil
222282 m .promptQueueIndex = 0
283+ m .startServerAfterSetup = false
223284 return m , nil
224285 case "up" , "k" :
225286 if m .promptSel > 0 {
@@ -333,7 +394,7 @@ func (m *TuiModel) triggerUninstall(pkg *bridge.CatalogEntry) (tea.Model, tea.Cm
333394 return m , nil
334395 }
335396 m .RunSetupTask (make (map [string ]string ))
336- return m , nil
397+ return m , m . listenToChannel ()
337398}
338399
339400func (m * TuiModel ) triggerInstall (pkg * bridge.CatalogEntry , method string ) (tea.Model , tea.Cmd ) {
@@ -388,7 +449,7 @@ func (m *TuiModel) triggerInstall(pkg *bridge.CatalogEntry, method string) (tea.
388449 return m , nil
389450 }
390451 m .RunSetupTask (map [string ]string {pkg .Name : method })
391- return m , nil
452+ return m , m . listenToChannel ()
392453}
393454
394455func (m * TuiModel ) triggerForceReinstall (pkg * bridge.CatalogEntry ) (tea.Model , tea.Cmd ) {
@@ -432,7 +493,7 @@ func (m *TuiModel) triggerForceReinstall(pkg *bridge.CatalogEntry) (tea.Model, t
432493 }
433494
434495 m .RunSetupTask (map [string ]string {pkg .Name : method })
435- return m , nil
496+ return m , m . listenToChannel ()
436497}
437498
438499func (m * TuiModel ) triggerUpdate (pkg * bridge.CatalogEntry ) (tea.Model , tea.Cmd ) {
@@ -475,7 +536,7 @@ func (m *TuiModel) triggerUpdate(pkg *bridge.CatalogEntry) (tea.Model, tea.Cmd)
475536 err := runtime .runProcessAndStreamLogsSilent (workspaceRoot , "pnpm" , []string {"run" , "prepare:modules" })
476537 runtime .msgChan <- taskFinishedMsg {Success : err == nil , Err : err }
477538 }()
478- return m , nil
539+ return m , m . listenToChannel ()
479540}
480541
481542// ─────────────────────────────────────────────
@@ -587,7 +648,8 @@ func (m *TuiModel) startStartupCheck() tea.Cmd {
587648func (m * TuiModel ) processNextQueueDecision () tea.Cmd {
588649 if m .promptQueueIndex >= len (m .promptQueue ) {
589650 m .activePrompt = PromptNone
590- return m .applyQueueChangesCmd ()
651+ // IMPORTANT: batch with listenToChannel so the setup task's messages are consumed
652+ return tea .Batch (m .applyQueueChangesCmd (), m .listenToChannel ())
591653 }
592654
593655 dec := m .promptQueue [m .promptQueueIndex ]
@@ -760,15 +822,16 @@ func (m *TuiModel) applyQueueChangesCmd() tea.Cmd {
760822 return taskFinishedMsg {Success : false , Err : err }
761823 }
762824
825+ m .runtime .msgChan <- logLineMsg (">>> Workspace changes written. Starting setup…" )
763826 m .RunSetupTask (finalAdds )
764- return logLineMsg ( ">>> Workspace changes written. Running setup task…" )
827+ return nil
765828 }
766829}
767830
768831func (m * TuiModel ) runWipeWorkspaceCmd () tea.Cmd {
769832 return func () tea.Msg {
770833 m .RunWipeWorkspaceTask ()
771- return nil
834+ return logLineMsg ( ">>> Wipe workspace task started…" )
772835 }
773836}
774837
@@ -912,3 +975,21 @@ func (m *TuiModel) startQueueReview() tea.Cmd {
912975
913976 return m .processNextQueueDecision ()
914977}
978+
979+ func (m * TuiModel ) updateSettingsFocus () {
980+ if m .activePrompt == PromptSettings {
981+ if m .settingsSel == 2 {
982+ m .settingsOrgsInput .Focus ()
983+ m .settingsUserInput .Blur ()
984+ } else if m .settingsSel == 3 {
985+ m .settingsUserInput .Focus ()
986+ m .settingsOrgsInput .Blur ()
987+ } else {
988+ m .settingsOrgsInput .Blur ()
989+ m .settingsUserInput .Blur ()
990+ }
991+ } else {
992+ m .settingsOrgsInput .Blur ()
993+ m .settingsUserInput .Blur ()
994+ }
995+ }
0 commit comments