@owdproject/cli 0.1.0 → 0.2.1

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,69 @@
1
+ package tui
2
+
3
+ import (
4
+ "testing"
5
+
6
+ "owd-cli/bridge"
7
+ )
8
+
9
+ func TestNewModelEngineState(t *testing.T) {
10
+ model := NewModel("test-root")
11
+ if model.workQueue == nil {
12
+ t.Error("Expected workQueue to be initialized")
13
+ }
14
+ if model.enginePhase != PhaseIdle {
15
+ t.Errorf("Expected enginePhase idle, got %s", model.enginePhase)
16
+ }
17
+ if model.runtime.engineResume == nil {
18
+ t.Error("Expected engineResume channel to be initialized")
19
+ }
20
+ }
21
+
22
+ func TestEngineQueueMessages(t *testing.T) {
23
+ model := NewModel("test-root")
24
+
25
+ q := NewWorkQueue()
26
+ q.AppendUnique(WorkItem{Name: "a", ShortName: "a", Status: StatusPending})
27
+ q.AppendUnique(WorkItem{Name: "b", ShortName: "b", Status: StatusCompleted})
28
+
29
+ m2, _ := (&model).Update(engineQueueUpdatedMsg{Queue: q.Clone()})
30
+ updated := m2.(*TuiModel)
31
+ if updated.workQueue == nil || len(updated.workQueue.Items) != 2 {
32
+ t.Fatalf("expected queue with 2 items, got %+v", updated.workQueue)
33
+ }
34
+ if updated.setupStep != 1 || updated.setupTotalSteps != 2 {
35
+ t.Errorf("expected progress 1/2, got %d/%d", updated.setupStep, updated.setupTotalSteps)
36
+ }
37
+
38
+ m3, _ := updated.Update(enginePhaseMsg{Phase: PhaseProcess})
39
+ phaseModel := m3.(*TuiModel)
40
+ if phaseModel.enginePhase != PhaseProcess {
41
+ t.Errorf("expected PhaseProcess, got %s", phaseModel.enginePhase)
42
+ }
43
+ }
44
+
45
+ func TestBuildConfigFromPending(t *testing.T) {
46
+ theme := "@owdproject/theme-nova"
47
+ ctx := &bridge.WorkspaceContext{
48
+ Config: bridge.Config{
49
+ Theme: &theme,
50
+ Apps: []string{"@owdproject/app-about"},
51
+ Modules: []string{},
52
+ },
53
+ }
54
+ catalog := &bridge.CatalogResponse{
55
+ Entries: []bridge.CatalogEntry{
56
+ {Name: "@owdproject/app-todo", ShortName: "app-todo", Kind: "app"},
57
+ },
58
+ }
59
+ pending := map[string]bool{
60
+ "@owdproject/app-todo": true,
61
+ }
62
+ config, removes := buildConfigFromPending(ctx, catalog, pending, nil)
63
+ if len(removes) != 0 {
64
+ t.Fatalf("expected no removals, got %v", removes)
65
+ }
66
+ if !containsStr(config.Apps, "@owdproject/app-todo") {
67
+ t.Fatalf("expected app-todo in apps, got %v", config.Apps)
68
+ }
69
+ }
@@ -0,0 +1,230 @@
1
+ package tui
2
+
3
+ import (
4
+ "context"
5
+ "os/exec"
6
+ "sync"
7
+ "time"
8
+
9
+ "owd-cli/bridge"
10
+
11
+ "github.com/charmbracelet/bubbles/textinput"
12
+ tea "github.com/charmbracelet/bubbletea"
13
+ )
14
+
15
+ // ─────────────────────────────────────────────
16
+ // Msg types
17
+ // ─────────────────────────────────────────────
18
+
19
+ type contextLoadedMsg struct {
20
+ Ctx *bridge.WorkspaceContext
21
+ Err error
22
+ }
23
+
24
+ type catalogLoadedMsg struct {
25
+ Cat *bridge.CatalogResponse
26
+ Err error
27
+ }
28
+
29
+ type logLineMsg string
30
+ type clearLogsMsg struct{}
31
+
32
+ type taskFinishedMsg struct {
33
+ Success bool
34
+ Err error
35
+ }
36
+
37
+ type serverStatusMsg struct {
38
+ Running bool
39
+ }
40
+
41
+ type tickMsg time.Time
42
+
43
+ type memTickMsg struct {
44
+ MemMB int
45
+ }
46
+
47
+ type GitChanges struct {
48
+ Added int
49
+ Modified int
50
+ Deleted int
51
+ }
52
+
53
+ type UpdateInfo struct {
54
+ LocalGit bool
55
+ BehindCount int
56
+ Npm bool
57
+ }
58
+
59
+ // localChangesMsg is the result of the fast local-only git status check (no network).
60
+ type localChangesMsg struct {
61
+ GitChanges map[string]GitChanges
62
+ LocalGitDirs map[string]bool // shortName -> has .git folder
63
+ }
64
+
65
+ // remoteUpdatesMsg is the result of the slow network check (git behind + npm versions).
66
+ type remoteUpdatesMsg struct {
67
+ Updates map[string]UpdateInfo
68
+ Err error
69
+ }
70
+
71
+ type setupProgressMsg struct {
72
+ Step int
73
+ Total int
74
+ Label string
75
+ }
76
+
77
+ type engineNeedsPromptMsg struct {
78
+ Item WorkItem
79
+ }
80
+
81
+ type engineQueueUpdatedMsg struct {
82
+ Queue WorkQueue
83
+ }
84
+
85
+ type enginePhaseMsg struct {
86
+ Phase EnginePhase
87
+ }
88
+
89
+ type workspaceGitStatusMsg struct {
90
+ Branch string
91
+ Changes string
92
+ }
93
+
94
+ type InstallMethod struct {
95
+ Name string
96
+ Label string
97
+ Desc string
98
+ }
99
+
100
+ // ─────────────────────────────────────────────
101
+ // Prompt states
102
+ // ─────────────────────────────────────────────
103
+
104
+ type PromptType int
105
+
106
+ const (
107
+ PromptNone PromptType = iota
108
+ PromptInstallMethod
109
+ PromptUninstallConfirm
110
+ PromptManagePackage
111
+ PromptForceReinstallConfirm
112
+ PromptSettings
113
+ PromptSetupProgress
114
+ PromptResolveDependency
115
+ PromptWipeWorkspaceConfirm
116
+ )
117
+
118
+ type ActiveTask int
119
+
120
+ const (
121
+ TaskNone ActiveTask = iota
122
+ TaskSetup
123
+ TaskWipe
124
+ TaskServe
125
+ )
126
+
127
+ // RuntimeState holds references to mutable OS/runtime resources.
128
+ type RuntimeState struct {
129
+ serverCmd *exec.Cmd
130
+ serverMu sync.Mutex
131
+ logCancel context.CancelFunc
132
+ msgChan chan tea.Msg
133
+ engineResume chan string
134
+ }
135
+
136
+ // ─────────────────────────────────────────────
137
+ // Menu constants and actions
138
+ // ─────────────────────────────────────────────
139
+
140
+ var ManagePackageActions = []string{
141
+ "Update package",
142
+ "Switch to NPM registry",
143
+ "Switch to Git (HTTPS)",
144
+ "Switch to Git (SSH)",
145
+ "Force Re-download (wipe changes)",
146
+ "Back to catalog",
147
+ }
148
+
149
+ const (
150
+ SettingsFieldCount = 5 // Install Mode, Catalog Sort, Trusted Orgs, GitHub User, Reset Workspace
151
+ SettingsActionCount = 2 // Save, Cancel
152
+ SettingsTotalOptions = SettingsFieldCount + SettingsActionCount
153
+ )
154
+
155
+ // ─────────────────────────────────────────────
156
+ // Model
157
+ // ─────────────────────────────────────────────
158
+
159
+
160
+
161
+ type TuiModel struct {
162
+ workspaceRoot string
163
+ ctx *bridge.WorkspaceContext
164
+ catalog *bridge.CatalogResponse
165
+ runtime *RuntimeState
166
+
167
+ ExitCode int
168
+ activeTab int // 0: Internal, 1: Apps, 2: Modules, 3: Themes
169
+ err error
170
+ loading bool
171
+ statusMsg string
172
+ logLines []string
173
+ memHistory []int
174
+
175
+ // Selection
176
+ selectedIndex int
177
+ scrollOffset int
178
+
179
+ // Prompts/Modals
180
+ activePrompt PromptType
181
+ promptPkg *bridge.CatalogEntry
182
+ promptSel int
183
+
184
+ promptPkgDeps []string
185
+ promptPkgHasPrimevue bool
186
+ promptPkgHasTailwind bool
187
+
188
+ gitChangesMap map[string]GitChanges
189
+ updatesMap map[string]UpdateInfo
190
+ localGitDirs map[string]bool // shortName -> has own .git folder
191
+
192
+ settingsSel int
193
+ settingsInstallMode string
194
+ settingsCatalogSort string
195
+ settingsOrgsInput textinput.Model
196
+ settingsUserInput textinput.Model
197
+
198
+ // Server status
199
+ serverRunning bool
200
+ activeTask ActiveTask
201
+ checkingUpdates bool
202
+
203
+ // Animation
204
+ blink bool
205
+ tickCount int
206
+
207
+ // Layout
208
+ termWidth int
209
+ termHeight int
210
+
211
+ lastInstallMethod string
212
+ logTailerStarted bool
213
+
214
+ // Queued changes
215
+ pendingPackages map[string]bool // pkgName -> install(true)/uninstall(false)
216
+ pendingTheme *string // pointer to theme name to activate
217
+
218
+ // Wizard engine
219
+ workQueue *WorkQueue
220
+ enginePhase EnginePhase
221
+ promptItem *WorkItem
222
+
223
+ startServerAfterSetup bool
224
+ setupStep int
225
+ setupTotalSteps int
226
+ setupLabel string
227
+ workspaceBranch string
228
+ workspaceChanges string
229
+ clientStars int
230
+ }