@owdproject/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,604 @@
1
+ package tui
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+
9
+ "owd-cli/bridge"
10
+
11
+ tea "github.com/charmbracelet/bubbletea"
12
+ )
13
+
14
+ // ─────────────────────────────────────────────
15
+ // Prompt keys & Navigation
16
+ // ─────────────────────────────────────────────
17
+
18
+ func (m *TuiModel) handlePromptKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
19
+ promptToShow := m.activePrompt
20
+ if (m.activeTask == TaskSetup || m.activeTask == TaskWipe) &&
21
+ promptToShow != PromptResolveDependency &&
22
+ (promptToShow == PromptNone || promptToShow == PromptUninstallConfirm || promptToShow == PromptInstallMethod || promptToShow == PromptWipeWorkspaceConfirm) {
23
+ promptToShow = PromptSetupProgress
24
+ }
25
+ if promptToShow == PromptSetupProgress {
26
+ if msg.String() == "ctrl+c" {
27
+ return m, tea.Quit
28
+ }
29
+ return m, nil
30
+ }
31
+
32
+ switch promptToShow {
33
+ case PromptSettings:
34
+ return m.handleSettingsKeys(msg)
35
+ case PromptManagePackage:
36
+ return m.handleManagePackageKeys(msg)
37
+ case PromptUninstallConfirm:
38
+ return m.handleUninstallConfirmKeys(msg)
39
+ case PromptInstallMethod:
40
+ return m.handleInstallMethodKeys(msg)
41
+ case PromptResolveDependency:
42
+ return m.handleResolveDependencyKeys(msg)
43
+ case PromptForceReinstallConfirm:
44
+ return m.handleForceReinstallConfirmKeys(msg)
45
+ case PromptWipeWorkspaceConfirm:
46
+ return m.handleWipeWorkspaceConfirmKeys(msg)
47
+ default:
48
+ if msg.String() == "esc" || msg.String() == "q" {
49
+ m.activePrompt = PromptNone
50
+ m.promptPkg = nil
51
+ m.promptItem = nil
52
+ }
53
+ return m, nil
54
+ }
55
+ }
56
+
57
+ func (m *TuiModel) handleSettingsKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
58
+ keyStr := msg.String()
59
+ isInputFocused := m.settingsSel == 2 || m.settingsSel == 3
60
+
61
+ if isInputFocused {
62
+ switch keyStr {
63
+ case "esc":
64
+ m.activePrompt = PromptNone
65
+ m.settingsOrgsInput.Blur()
66
+ m.settingsUserInput.Blur()
67
+ return m, nil
68
+ case "up", "down", "enter":
69
+ // Intercept and fall through to navigation/actions below
70
+ default:
71
+ var cmd tea.Cmd
72
+ if m.settingsSel == 2 {
73
+ m.settingsOrgsInput, cmd = m.settingsOrgsInput.Update(msg)
74
+ } else {
75
+ m.settingsUserInput, cmd = m.settingsUserInput.Update(msg)
76
+ }
77
+ return m, cmd
78
+ }
79
+ }
80
+
81
+ switch keyStr {
82
+ case "esc", "q":
83
+ m.activePrompt = PromptNone
84
+ m.settingsOrgsInput.Blur()
85
+ m.settingsUserInput.Blur()
86
+ return m, nil
87
+ case "up", "k":
88
+ if m.settingsSel >= SettingsFieldCount {
89
+ m.settingsSel = SettingsFieldCount - 1
90
+ } else if m.settingsSel > 0 {
91
+ m.settingsSel--
92
+ }
93
+ m.updateSettingsFocus()
94
+ case "down", "j", "enter":
95
+ if keyStr == "enter" && isInputFocused {
96
+ m.settingsSel = 5 // Jump directly to SAVE button
97
+ m.updateSettingsFocus()
98
+ return m, nil
99
+ }
100
+
101
+ if keyStr == "down" || keyStr == "j" {
102
+ if m.settingsSel == SettingsFieldCount-1 {
103
+ m.settingsSel = SettingsFieldCount
104
+ } else if m.settingsSel < SettingsFieldCount-1 {
105
+ m.settingsSel++
106
+ }
107
+ m.updateSettingsFocus()
108
+ } else if keyStr == "enter" {
109
+ switch m.settingsSel {
110
+ case 0: // Install Mode
111
+ if m.settingsInstallMode == "npm" {
112
+ m.settingsInstallMode = "workspace"
113
+ } else {
114
+ m.settingsInstallMode = "npm"
115
+ }
116
+ case 1: // Catalog Sort
117
+ modes := []string{"updated", "name", "stars", "installed"}
118
+ idx := 0
119
+ for i, mth := range modes {
120
+ if mth == m.settingsCatalogSort {
121
+ idx = (i + 1) % len(modes)
122
+ break
123
+ }
124
+ }
125
+ m.settingsCatalogSort = modes[idx]
126
+ case 2: // Trusted Orgs
127
+ // Handled by textinput
128
+ case 3: // GitHub User
129
+ // Handled by textinput
130
+ case 4: // Reset Workspace
131
+ m.activePrompt = PromptWipeWorkspaceConfirm
132
+ m.promptSel = 1 // default to No
133
+ case 5: // Save
134
+ m.activePrompt = PromptNone
135
+ m.settingsOrgsInput.Blur()
136
+ m.settingsUserInput.Blur()
137
+ if m.ctx != nil {
138
+ settings := m.ctx.Settings
139
+ settings.InstallMode = m.settingsInstallMode
140
+ settings.CatalogSort = m.settingsCatalogSort
141
+
142
+ // Parse Trusted Orgs (GithubOrgs) from comma-separated list
143
+ rawOrgs := m.settingsOrgsInput.Value()
144
+ var orgs []string
145
+ for _, part := range strings.Split(rawOrgs, ",") {
146
+ trimmed := strings.TrimSpace(part)
147
+ if trimmed != "" {
148
+ orgs = append(orgs, trimmed)
149
+ }
150
+ }
151
+ settings.GithubOrgs = orgs
152
+
153
+ // Parse GitHub User (GithubUser)
154
+ ghUserVal := m.settingsUserInput.Value()
155
+ if ghUserVal == "" {
156
+ settings.GithubUser = nil
157
+ } else {
158
+ settings.GithubUser = &ghUserVal
159
+ }
160
+
161
+ payload := &bridge.WritePayload{
162
+ Settings: &settings,
163
+ }
164
+ m.statusMsg = "Saving settings…"
165
+ m.addLog("ℹ Saving settings configuration…")
166
+ if err := bridge.WriteChanges(m.workspaceRoot, payload); err != nil {
167
+ m.statusMsg = fmt.Sprintf("Save failed: %v", err)
168
+ m.addLog(fmt.Sprintf("✗ Save settings failed: %v", err))
169
+ } else {
170
+ m.statusMsg = "Settings saved successfully."
171
+ m.addLog("✓ Settings saved successfully.")
172
+ }
173
+ }
174
+ m.loading = true
175
+ return m, tea.Batch(m.loadContextCmd(), m.loadCatalogCmd(true))
176
+ case 6: // Cancel
177
+ m.activePrompt = PromptNone
178
+ m.settingsOrgsInput.Blur()
179
+ m.settingsUserInput.Blur()
180
+ }
181
+ }
182
+ case "left", "h":
183
+ if m.settingsSel == SettingsFieldCount+1 {
184
+ m.settingsSel = SettingsFieldCount
185
+ }
186
+ case "right", "l":
187
+ if m.settingsSel == SettingsFieldCount {
188
+ m.settingsSel = SettingsFieldCount + 1
189
+ }
190
+ }
191
+ return m, nil
192
+ }
193
+
194
+ func (m *TuiModel) handleManagePackageKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
195
+ actionsCount := len(ManagePackageActions)
196
+
197
+ switch msg.String() {
198
+ case "esc", "q":
199
+ m.activePrompt = PromptNone
200
+ m.promptPkg = nil
201
+ return m, nil
202
+ case "up", "k":
203
+ if m.promptSel > 0 {
204
+ m.promptSel--
205
+ }
206
+ case "down", "j":
207
+ if m.promptSel < actionsCount-1 {
208
+ m.promptSel++
209
+ }
210
+ case "enter":
211
+ pkg := m.promptPkg
212
+ m.activePrompt = PromptNone
213
+ m.promptPkg = nil
214
+ switch m.promptSel {
215
+ case 0: // Update Package
216
+ return m.triggerUpdate(pkg)
217
+ case 1: // Switch to NPM
218
+ m.lastInstallMethod = "npm"
219
+ return m.triggerInstall(pkg, "npm")
220
+ case 2: // Switch to Git HTTPS
221
+ m.lastInstallMethod = "git-https"
222
+ return m.triggerInstall(pkg, "git-https")
223
+ case 3: // Switch to Git SSH
224
+ m.lastInstallMethod = "git-ssh"
225
+ return m.triggerInstall(pkg, "git-ssh")
226
+ case 4: // Force Re-download
227
+ m.promptPkg = pkg
228
+ m.activePrompt = PromptForceReinstallConfirm
229
+ m.promptSel = 1 // default to No
230
+ return m, nil
231
+ case 5: // Back
232
+ return m, nil
233
+ }
234
+ }
235
+ return m, nil
236
+ }
237
+
238
+ func (m *TuiModel) handleUninstallConfirmKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
239
+ switch msg.String() {
240
+ case "esc", "q":
241
+ m.activePrompt = PromptNone
242
+ m.promptPkg = nil
243
+ m.addLog("✗ Uninstall cancelled")
244
+ return m, nil
245
+ case "left", "h", "up", "k":
246
+ m.promptSel = 0
247
+ case "right", "l", "down", "j":
248
+ m.promptSel = 1
249
+ case "enter":
250
+ pkg := m.promptPkg
251
+ m.activePrompt = PromptNone
252
+ m.promptPkg = nil
253
+ if m.promptSel == 0 {
254
+ return m.triggerUninstall(pkg)
255
+ }
256
+ }
257
+ return m, nil
258
+ }
259
+
260
+ func (m *TuiModel) handleResolveDependencyKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
261
+ shortName := "package"
262
+ if m.promptItem != nil {
263
+ shortName = m.promptItem.ShortName
264
+ }
265
+ if shortName == "" && m.promptPkg != nil {
266
+ shortName = m.promptPkg.ShortName
267
+ }
268
+
269
+ var methods []struct {
270
+ Name string
271
+ Label string
272
+ }
273
+ if isLocallyAvailable(m.workspaceRoot, shortName) {
274
+ methods = append(methods, struct{ Name, Label string }{"local", "Use Existing Local Folder"})
275
+ }
276
+ methods = append(methods,
277
+ struct{ Name, Label string }{"git-ssh", "Git SSH"},
278
+ struct{ Name, Label string }{"git-https", "Git HTTPS"},
279
+ struct{ Name, Label string }{"npm", "NPM Package"},
280
+ )
281
+
282
+ switch msg.String() {
283
+ case "esc", "q":
284
+ m.activePrompt = PromptNone
285
+ m.promptItem = nil
286
+ m.promptPkg = nil
287
+ m.startServerAfterSetup = false
288
+ m.addLog("✖ Wizard aborted by user")
289
+ m.resolveEnginePrompt("abort")
290
+ return m, nil
291
+ case "up", "k":
292
+ if m.promptSel > 0 {
293
+ m.promptSel--
294
+ }
295
+ case "down", "j":
296
+ if m.promptSel < len(methods)-1 {
297
+ m.promptSel++
298
+ }
299
+ case "enter":
300
+ if m.promptSel >= 0 && m.promptSel < len(methods) {
301
+ selected := methods[m.promptSel].Name
302
+ m.lastInstallMethod = selected
303
+ m.activePrompt = PromptNone
304
+ item := m.promptItem
305
+ m.promptItem = nil
306
+ m.promptPkg = nil
307
+ if item != nil {
308
+ m.addLog(fmt.Sprintf("ℹ Download method selected: %s", resolveMethodLabel(selected)))
309
+ }
310
+ m.resolveEnginePrompt(selected)
311
+ }
312
+ }
313
+ return m, nil
314
+ }
315
+
316
+ func (m *TuiModel) handleInstallMethodKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
317
+ pkg := m.promptPkg
318
+ if pkg == nil {
319
+ m.activePrompt = PromptNone
320
+ return m, nil
321
+ }
322
+ methods := m.getInstallMethods(pkg)
323
+ methodsCount := len(methods)
324
+
325
+ switch msg.String() {
326
+ case "esc", "q":
327
+ m.activePrompt = PromptNone
328
+ m.promptPkg = nil
329
+ m.addLog("✗ Install cancelled")
330
+ return m, nil
331
+ case "up", "k":
332
+ if m.promptSel > 0 {
333
+ m.promptSel--
334
+ }
335
+ case "down", "j":
336
+ if m.promptSel < methodsCount-1 {
337
+ m.promptSel++
338
+ }
339
+ case "enter":
340
+ if m.promptSel >= 0 && m.promptSel < methodsCount {
341
+ selectedMethod := methods[m.promptSel].Name
342
+ m.activePrompt = PromptNone
343
+ m.promptPkg = nil
344
+ m.lastInstallMethod = selectedMethod
345
+ return m.triggerInstall(pkg, selectedMethod)
346
+ }
347
+ }
348
+ return m, nil
349
+ }
350
+
351
+ func (m *TuiModel) handleForceReinstallConfirmKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
352
+ switch msg.String() {
353
+ case "esc", "q":
354
+ m.activePrompt = PromptNone
355
+ m.promptPkg = nil
356
+ return m, nil
357
+ case "left", "h", "up", "k":
358
+ m.promptSel = 0
359
+ case "right", "l", "down", "j":
360
+ m.promptSel = 1
361
+ case "enter":
362
+ pkg := m.promptPkg
363
+ m.activePrompt = PromptNone
364
+ m.promptPkg = nil
365
+ if m.promptSel == 0 {
366
+ return m.triggerForceReinstall(pkg)
367
+ }
368
+ }
369
+ return m, nil
370
+ }
371
+
372
+ func (m *TuiModel) handleWipeWorkspaceConfirmKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
373
+ switch msg.String() {
374
+ case "esc", "q":
375
+ m.activePrompt = PromptSettings
376
+ return m, nil
377
+ case "left", "h", "up", "k":
378
+ m.promptSel = 0
379
+ case "right", "l", "down", "j":
380
+ m.promptSel = 1
381
+ case "enter":
382
+ if m.promptSel == 0 { // Yes
383
+ m.activePrompt = PromptNone
384
+ m.activeTask = TaskWipe
385
+ m.statusMsg = "Resetting workspace…"
386
+ m.addLog("ℹ Initiating workspace reset task…")
387
+ return m, m.runWipeWorkspaceCmd()
388
+ } else { // No
389
+ m.activePrompt = PromptSettings
390
+ return m, nil
391
+ }
392
+ }
393
+ return m, nil
394
+ }
395
+
396
+ // ─────────────────────────────────────────────
397
+ // Installer Actions
398
+ // ─────────────────────────────────────────────
399
+
400
+ func (m *TuiModel) triggerUninstall(pkg *bridge.CatalogEntry) (tea.Model, tea.Cmd) {
401
+ m.activeTask = TaskSetup
402
+ m.statusMsg = fmt.Sprintf("Uninstalling %s…", pkg.ShortName)
403
+ m.addLog(fmt.Sprintf("⚙ Selected Option: Uninstall package %s", pkg.Name))
404
+
405
+ payload := &bridge.WritePayload{
406
+ Config: &bridge.Config{Theme: m.ctx.Config.Theme, Apps: m.ctx.Config.Apps, Modules: m.ctx.Config.Modules},
407
+ DepsToRemove: []string{pkg.Name},
408
+ }
409
+ if pkg.Kind == "app" {
410
+ var next []string
411
+ for _, a := range m.ctx.Config.Apps {
412
+ if a != pkg.Name {
413
+ next = append(next, a)
414
+ }
415
+ }
416
+ payload.Config.Apps = next
417
+ } else if pkg.Kind == "module" {
418
+ var next []string
419
+ for _, mod := range m.ctx.Config.Modules {
420
+ if mod != pkg.Name {
421
+ next = append(next, mod)
422
+ }
423
+ }
424
+ payload.Config.Modules = next
425
+ } else if pkg.Kind == "theme" {
426
+ e := ""
427
+ payload.Config.Theme = &e
428
+ }
429
+
430
+ if err := bridge.WriteChanges(m.workspaceRoot, payload); err != nil {
431
+ m.activeTask = TaskNone
432
+ m.statusMsg = fmt.Sprintf("Uninstall failed: %v", err)
433
+ return m, nil
434
+ }
435
+ m.RunSetupTask(make(map[string]string))
436
+ return m, m.listenToChannel()
437
+ }
438
+
439
+ func (m *TuiModel) triggerInstall(pkg *bridge.CatalogEntry, method string) (tea.Model, tea.Cmd) {
440
+ m.activeTask = TaskSetup
441
+ m.statusMsg = fmt.Sprintf("Installing %s via %s…", pkg.ShortName, method)
442
+ m.addLog(fmt.Sprintf("⚙ Selected Option: Install package %s via %s", pkg.Name, method))
443
+
444
+ payload := &bridge.WritePayload{
445
+ Config: &bridge.Config{Theme: m.ctx.Config.Theme, Apps: m.ctx.Config.Apps, Modules: m.ctx.Config.Modules},
446
+ DepsToAdd: make(map[string]string),
447
+ }
448
+ version := "latest"
449
+ if pkg.Version != nil {
450
+ version = *pkg.Version
451
+ }
452
+
453
+ if method == "npm" {
454
+ payload.DepsToAdd[pkg.Name] = version
455
+ } else if method == "local" {
456
+ payload.DepsToAdd[pkg.Name] = "workspace:*"
457
+ } else {
458
+ owner := m.resolveOwner(pkg.Name)
459
+ var gitUrl string
460
+ if method == "git-ssh" {
461
+ gitUrl = fmt.Sprintf("git@github.com:%s/%s.git", owner, pkg.ShortName)
462
+ } else {
463
+ gitUrl = fmt.Sprintf("https://github.com/%s/%s.git", owner, pkg.ShortName)
464
+ }
465
+ payload.DepsToAdd[pkg.Name] = "workspace:*"
466
+ settings := m.ctx.Settings
467
+ if settings.LastInstallChoices == nil {
468
+ settings.LastInstallChoices = make(map[string]interface{})
469
+ }
470
+ settings.LastInstallChoices[pkg.Name] = map[string]string{"type": "git", "gitUrl": gitUrl}
471
+ payload.Settings = &settings
472
+ }
473
+
474
+ if pkg.Kind == "app" {
475
+ payload.Config.Apps = append(payload.Config.Apps, pkg.Name)
476
+ } else if pkg.Kind == "module" {
477
+ payload.Config.Modules = append(payload.Config.Modules, pkg.Name)
478
+ } else if pkg.Kind == "theme" {
479
+ payload.Config.Theme = &pkg.Name
480
+ }
481
+
482
+ if err := bridge.WriteChanges(m.workspaceRoot, payload); err != nil {
483
+ m.activeTask = TaskNone
484
+ m.statusMsg = fmt.Sprintf("Install failed: %v", err)
485
+ return m, nil
486
+ }
487
+ m.RunSetupTask(map[string]string{pkg.Name: method})
488
+ return m, m.listenToChannel()
489
+ }
490
+
491
+ func (m *TuiModel) triggerForceReinstall(pkg *bridge.CatalogEntry) (tea.Model, tea.Cmd) {
492
+ m.activeTask = TaskSetup
493
+ m.statusMsg = fmt.Sprintf("Force reinstalling %s…", pkg.ShortName)
494
+ m.addLog(fmt.Sprintf("ℹ Deleting local folder and reinstalling %s", pkg.Name))
495
+
496
+ // Delete local folder
497
+ short := pkg.ShortName
498
+ kindDir := ""
499
+ switch pkg.Kind {
500
+ case "app":
501
+ kindDir = "apps"
502
+ case "module":
503
+ kindDir = "packages"
504
+ case "theme":
505
+ kindDir = "themes"
506
+ }
507
+ if kindDir != "" {
508
+ pkgPath := filepath.Join(m.workspaceRoot, kindDir, short)
509
+ m.addLog(fmt.Sprintf("ℹ Removing directory: %s", pkgPath))
510
+ _ = os.RemoveAll(pkgPath)
511
+ }
512
+
513
+ // Reinstall using the original source method
514
+ method := "npm"
515
+ if pkg.LocalSource {
516
+ if m.ctx != nil && m.ctx.Settings.LastInstallChoices != nil {
517
+ if choice, exists := m.ctx.Settings.LastInstallChoices[pkg.Name]; exists {
518
+ if choiceMap, ok := choice.(map[string]interface{}); ok {
519
+ if gitUrl, hasGit := choiceMap["gitUrl"].(string); hasGit {
520
+ if strings.HasPrefix(gitUrl, "git@") {
521
+ method = "git-ssh"
522
+ } else {
523
+ method = "git-https"
524
+ }
525
+ }
526
+ }
527
+ }
528
+ }
529
+ }
530
+ m.RunSetupTask(map[string]string{pkg.Name: method})
531
+ return m, m.listenToChannel()
532
+ }
533
+
534
+ func (m *TuiModel) triggerUpdate(pkg *bridge.CatalogEntry) (tea.Model, tea.Cmd) {
535
+ m.activeTask = TaskSetup
536
+ m.statusMsg = fmt.Sprintf("Updating %s…", pkg.ShortName)
537
+ m.addLog(fmt.Sprintf("ℹ Updating %s", pkg.Name))
538
+
539
+ workspaceRoot := m.workspaceRoot
540
+ runtime := m.runtime
541
+ pkgName := pkg.Name
542
+ short := pkg.ShortName
543
+ kind := pkg.Kind
544
+
545
+ go func() {
546
+ kindDir := ""
547
+ switch kind {
548
+ case "app":
549
+ kindDir = "apps"
550
+ case "module":
551
+ kindDir = "packages"
552
+ case "theme":
553
+ kindDir = "themes"
554
+ }
555
+ if kindDir != "" {
556
+ pkgPath := filepath.Join(workspaceRoot, kindDir, short)
557
+ gitDir := filepath.Join(pkgPath, ".git")
558
+ if _, err := os.Stat(gitDir); err == nil {
559
+ runtime.msgChan <- logLineMsg("ℹ Local Git repository detected. Running git pull…")
560
+ runtime.runProcessAndStreamLogs(pkgPath, "git", []string{"pull"})
561
+ return
562
+ }
563
+ }
564
+
565
+ runtime.msgChan <- logLineMsg(fmt.Sprintf("ℹ Running pnpm install %s@latest…", pkgName))
566
+ if err := runtime.runProcessAndStreamLogsSilent(workspaceRoot, "pnpm", []string{"install", pkgName + "@latest"}); err != nil {
567
+ runtime.msgChan <- taskFinishedMsg{Success: false, Err: err}
568
+ return
569
+ }
570
+ runtime.msgChan <- logLineMsg("ℹ Preparing workspace modules…")
571
+ err := runtime.runProcessAndStreamLogsSilent(workspaceRoot, "pnpm", []string{"run", "prepare:modules"})
572
+ runtime.msgChan <- taskFinishedMsg{Success: err == nil, Err: err}
573
+ }()
574
+ return m, m.listenToChannel()
575
+ }
576
+
577
+ // ─────────────────────────────────────────────
578
+ // Wipe workspace
579
+ // ─────────────────────────────────────────────
580
+
581
+ func (m *TuiModel) runWipeWorkspaceCmd() tea.Cmd {
582
+ return func() tea.Msg {
583
+ m.RunWipeWorkspaceTask()
584
+ return logLineMsg("ℹ Wipe workspace task started…")
585
+ }
586
+ }
587
+
588
+ func (m *TuiModel) updateSettingsFocus() {
589
+ if m.activePrompt == PromptSettings {
590
+ if m.settingsSel == 2 {
591
+ m.settingsOrgsInput.Focus()
592
+ m.settingsUserInput.Blur()
593
+ } else if m.settingsSel == 3 {
594
+ m.settingsUserInput.Focus()
595
+ m.settingsOrgsInput.Blur()
596
+ } else {
597
+ m.settingsOrgsInput.Blur()
598
+ m.settingsUserInput.Blur()
599
+ }
600
+ } else {
601
+ m.settingsOrgsInput.Blur()
602
+ m.settingsUserInput.Blur()
603
+ }
604
+ }