@owdproject/cli 0.0.3 → 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.
Files changed (56) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +50 -16
  3. package/bin/bridge.js +129 -0
  4. package/bin/cli.js +491 -0
  5. package/bin/cp.js +3542 -0
  6. package/bin/desktop.js +5 -0
  7. package/bin/lib/catalog.js +504 -0
  8. package/bin/lib/config.js +173 -0
  9. package/bin/lib/cpAscii.js +39 -0
  10. package/bin/lib/cpFormat.js +343 -0
  11. package/bin/lib/create.js +246 -0
  12. package/bin/lib/desktopConfig.js +48 -0
  13. package/bin/lib/generateTemplate.js +427 -0
  14. package/bin/lib/install.js +521 -0
  15. package/bin/lib/installWizardFlow.js +792 -0
  16. package/bin/lib/migrateModulePlayground.config.mjs +187 -0
  17. package/bin/lib/migrateModulePlayground.mjs +318 -0
  18. package/bin/lib/npmVersions.js +124 -0
  19. package/bin/lib/packageSources.js +375 -0
  20. package/bin/lib/playgroundContext.js +188 -0
  21. package/bin/lib/scaffold.js +205 -0
  22. package/bin/lib/status.js +319 -0
  23. package/bin/lib/validateModule.js +978 -0
  24. package/bin/lib/workspace.js +289 -0
  25. package/bin/owd.js +2 -118
  26. package/go/bridge/bridge.go +220 -0
  27. package/go/desktop-go +0 -0
  28. package/go/go.mod +33 -0
  29. package/go/go.sum +52 -0
  30. package/go/main.go +34 -0
  31. package/go/owd-cli +0 -0
  32. package/go/tui/engine.go +472 -0
  33. package/go/tui/engine_test.go +40 -0
  34. package/go/tui/handlers.go +604 -0
  35. package/go/tui/helpers.go +916 -0
  36. package/go/tui/helpers_test.go +97 -0
  37. package/go/tui/modals.go +423 -0
  38. package/go/tui/resolver.go +377 -0
  39. package/go/tui/resolver_test.go +86 -0
  40. package/go/tui/styles.go +153 -0
  41. package/go/tui/tasks.go +850 -0
  42. package/go/tui/tui.go +869 -0
  43. package/go/tui/tui_test.go +69 -0
  44. package/go/tui/types.go +230 -0
  45. package/go/tui/view.go +812 -0
  46. package/go/tui/workqueue.go +179 -0
  47. package/go/tui/workqueue_test.go +90 -0
  48. package/package.json +33 -22
  49. package/src/commands/create.js +0 -11
  50. package/src/commands/generate.js +0 -12
  51. package/src/commands/installApp.js +0 -9
  52. package/src/commands/installTheme.js +0 -9
  53. package/src/commands/start.js +0 -13
  54. package/src/utils/cloneRepo.js +0 -25
  55. package/src/utils/installPackage.js +0 -15
  56. package/src/utils/logging.js +0 -15
@@ -0,0 +1,377 @@
1
+ package tui
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+
9
+ "owd-cli/bridge"
10
+ )
11
+
12
+ // readDesktopPackageDeps reads @owdproject/* deps with versions from desktop/package.json.
13
+ func readDesktopPackageDeps(packageJsonPath string) (map[string]string, error) {
14
+ data, err := os.ReadFile(packageJsonPath)
15
+ if err != nil {
16
+ return nil, err
17
+ }
18
+ var pkg struct {
19
+ Dependencies map[string]string `json:"dependencies"`
20
+ DevDependencies map[string]string `json:"devDependencies"`
21
+ }
22
+ if err := json.Unmarshal(data, &pkg); err != nil {
23
+ return nil, err
24
+ }
25
+ deps := make(map[string]string)
26
+ for k, v := range pkg.Dependencies {
27
+ if strings.HasPrefix(k, "@owdproject/") {
28
+ deps[k] = v
29
+ }
30
+ }
31
+ for k, v := range pkg.DevDependencies {
32
+ if strings.HasPrefix(k, "@owdproject/") {
33
+ deps[k] = v
34
+ }
35
+ }
36
+ return deps, nil
37
+ }
38
+
39
+ func isNpmVersion(version string) bool {
40
+ if version == "" {
41
+ return false
42
+ }
43
+ if version == "workspace:*" || version == "workspace:^" {
44
+ return false
45
+ }
46
+ return true
47
+ }
48
+
49
+ func shortNameFromPkg(name string) string {
50
+ if idx := strings.LastIndex(name, "/"); idx >= 0 {
51
+ return name[idx+1:]
52
+ }
53
+ return name
54
+ }
55
+
56
+ func kindDirForKind(kind string) string {
57
+ switch kind {
58
+ case "app":
59
+ return "apps"
60
+ case "module":
61
+ return "packages"
62
+ case "theme":
63
+ return "themes"
64
+ default:
65
+ return "packages"
66
+ }
67
+ }
68
+
69
+ func catalogEntryForName(catalog *bridge.CatalogResponse, name string) bridge.CatalogEntry {
70
+ if catalog != nil {
71
+ for _, e := range catalog.Entries {
72
+ if e.Name == name {
73
+ return e
74
+ }
75
+ }
76
+ }
77
+ short := shortNameFromPkg(name)
78
+ kind := "module"
79
+ if strings.HasPrefix(short, "app-") {
80
+ kind = "app"
81
+ } else if strings.HasPrefix(short, "theme-") {
82
+ kind = "theme"
83
+ }
84
+ return bridge.CatalogEntry{
85
+ Name: name,
86
+ ShortName: short,
87
+ Kind: kind,
88
+ }
89
+ }
90
+
91
+ // packagesFromConfig returns all configured package names from desktop.config.ts.
92
+ func packagesFromConfig(config *bridge.Config) []struct {
93
+ Name string
94
+ Kind string
95
+ } {
96
+ var out []struct {
97
+ Name string
98
+ Kind string
99
+ }
100
+ if config == nil {
101
+ return out
102
+ }
103
+ for _, name := range config.Apps {
104
+ out = append(out, struct {
105
+ Name string
106
+ Kind string
107
+ }{Name: name, Kind: "app"})
108
+ }
109
+ for _, name := range config.Modules {
110
+ out = append(out, struct {
111
+ Name string
112
+ Kind string
113
+ }{Name: name, Kind: "module"})
114
+ }
115
+ if config.Theme != nil && *config.Theme != "" {
116
+ out = append(out, struct {
117
+ Name string
118
+ Kind string
119
+ }{Name: *config.Theme, Kind: "theme"})
120
+ }
121
+ return out
122
+ }
123
+
124
+ // BuildQueueFromConfig resolves packages in config against desktop/package.json.
125
+ func BuildQueueFromConfig(
126
+ workspaceRoot string,
127
+ ctx *bridge.WorkspaceContext,
128
+ catalog *bridge.CatalogResponse,
129
+ logFn func(string),
130
+ ) *WorkQueue {
131
+ q := NewWorkQueue()
132
+ if ctx == nil {
133
+ return q
134
+ }
135
+
136
+ deps, err := readDesktopPackageDeps(ctx.Paths.PackageJson)
137
+ if err != nil {
138
+ logFn("✖ Failed to read desktop/package.json: " + err.Error())
139
+ return q
140
+ }
141
+
142
+ seen := map[string]bool{}
143
+ for _, pkg := range packagesFromConfig(&ctx.Config) {
144
+ if seen[pkg.Name] {
145
+ continue
146
+ }
147
+ seen[pkg.Name] = true
148
+ addPackageToQueue(q, workspaceRoot, pkg.Name, pkg.Kind, deps, catalog, false, logFn)
149
+ }
150
+
151
+ // Recursively scan dependencies of all local packages in the queue
152
+ scanned := map[string]bool{}
153
+ for {
154
+ hasNewScan := false
155
+ var names []string
156
+ for _, item := range q.Items {
157
+ if !scanned[item.Name] {
158
+ names = append(names, item.Name)
159
+ }
160
+ }
161
+
162
+ for _, name := range names {
163
+ scanned[name] = true
164
+ item := q.FindByName(name)
165
+ if item != nil && isLocallyAvailable(workspaceRoot, item.ShortName) {
166
+ DiscoverNestedDeps(workspaceRoot, *item, catalog, q, logFn)
167
+ hasNewScan = true
168
+ }
169
+ }
170
+ if !hasNewScan {
171
+ break
172
+ }
173
+ }
174
+
175
+ return q
176
+ }
177
+
178
+ // BuildStartupCheckQueue builds queue for installed-but-not-local packages.
179
+ func BuildStartupCheckQueue(
180
+ workspaceRoot string,
181
+ ctx *bridge.WorkspaceContext,
182
+ catalog *bridge.CatalogResponse,
183
+ logFn func(string),
184
+ ) *WorkQueue {
185
+ q := NewWorkQueue()
186
+ if catalog == nil || ctx == nil {
187
+ return q
188
+ }
189
+
190
+ deps, _ := readDesktopPackageDeps(ctx.Paths.PackageJson)
191
+ seen := map[string]bool{}
192
+
193
+ for _, entry := range catalog.Entries {
194
+ if entry.Installed && !entry.LocalSource {
195
+ if !seen[entry.Name] {
196
+ seen[entry.Name] = true
197
+ addPackageToQueue(q, workspaceRoot, entry.Name, entry.Kind, deps, catalog, false, logFn)
198
+ }
199
+ }
200
+ }
201
+
202
+ // Theme kit dependencies
203
+ var themeShort string
204
+ if ctx.Config.Theme != nil && *ctx.Config.Theme != "" {
205
+ themeShort = shortNameFromPkg(*ctx.Config.Theme)
206
+ }
207
+ if themeShort != "" {
208
+ themeDeps, err := getThemeDependencies(workspaceRoot, themeShort)
209
+ if err == nil {
210
+ for _, dep := range themeDeps {
211
+ if seen[dep] {
212
+ continue
213
+ }
214
+ short := shortNameFromPkg(dep)
215
+ if !strings.HasPrefix(short, "kit-") && !strings.HasPrefix(short, "module-") {
216
+ continue
217
+ }
218
+ if isLocallyAvailable(workspaceRoot, short) {
219
+ logFn("ℹ " + short + " already available")
220
+ continue
221
+ }
222
+ seen[dep] = true
223
+ addPackageToQueue(q, workspaceRoot, dep, "module", deps, catalog, true, logFn)
224
+ }
225
+ }
226
+ }
227
+
228
+ // Recursively scan dependencies of all local packages in the queue
229
+ scanned := map[string]bool{}
230
+ for {
231
+ hasNewScan := false
232
+ var names []string
233
+ for _, item := range q.Items {
234
+ if !scanned[item.Name] {
235
+ names = append(names, item.Name)
236
+ }
237
+ }
238
+
239
+ for _, name := range names {
240
+ scanned[name] = true
241
+ item := q.FindByName(name)
242
+ if item != nil && isLocallyAvailable(workspaceRoot, item.ShortName) {
243
+ DiscoverNestedDeps(workspaceRoot, *item, catalog, q, logFn)
244
+ hasNewScan = true
245
+ }
246
+ }
247
+ if !hasNewScan {
248
+ break
249
+ }
250
+ }
251
+
252
+ return q
253
+ }
254
+
255
+ func addPackageToQueue(
256
+ q *WorkQueue,
257
+ workspaceRoot, name, kind string,
258
+ deps map[string]string,
259
+ catalog *bridge.CatalogResponse,
260
+ discovered bool,
261
+ logFn func(string),
262
+ ) {
263
+ entry := catalogEntryForName(catalog, name)
264
+ if kind != "" {
265
+ entry.Kind = kind
266
+ }
267
+ short := entry.ShortName
268
+ if short == "" {
269
+ short = shortNameFromPkg(name)
270
+ }
271
+
272
+ version, inPkg := deps[name]
273
+
274
+ if (entry.LocalSource || isLocallyAvailable(workspaceRoot, short)) && inPkg {
275
+ logFn("ℹ " + short + " already available and configured in package.json")
276
+ q.AppendUnique(WorkItem{
277
+ Name: name,
278
+ ShortName: short,
279
+ Type: entry.Kind,
280
+ Source: "local",
281
+ Status: StatusSkipped,
282
+ Kind: KindLocal,
283
+ TargetDir: filepath.Join(workspaceRoot, kindDirForKind(entry.Kind), short),
284
+ Discovered: discovered,
285
+ Entry: entry,
286
+ })
287
+ return
288
+ }
289
+
290
+ if !inPkg {
291
+ logFn("⚠ " + short + " missing from package.json")
292
+ q.AppendUnique(WorkItem{
293
+ Name: name,
294
+ ShortName: short,
295
+ Type: entry.Kind,
296
+ Source: SourcePending,
297
+ Status: StatusPending,
298
+ Kind: KindPrompt,
299
+ Discovered: discovered,
300
+ Entry: entry,
301
+ })
302
+ return
303
+ }
304
+
305
+ if version == "workspace:*" || version == "workspace:^" {
306
+ logFn("⚠ " + short + " configured as workspace dependency")
307
+ q.AppendUnique(WorkItem{
308
+ Name: name,
309
+ ShortName: short,
310
+ Type: entry.Kind,
311
+ Source: SourcePending,
312
+ Status: StatusPending,
313
+ Kind: KindClone,
314
+ TargetDir: filepath.Join(workspaceRoot, kindDirForKind(entry.Kind), short),
315
+ Discovered: discovered,
316
+ Entry: entry,
317
+ })
318
+ return
319
+ }
320
+
321
+ if isNpmVersion(version) {
322
+ logFn("ℹ " + short + " resolved as npm package (" + version + ")")
323
+ q.AppendUnique(WorkItem{
324
+ Name: name,
325
+ ShortName: short,
326
+ Type: entry.Kind,
327
+ Source: "npm:" + version,
328
+ Status: StatusSkipped,
329
+ Kind: KindNpm,
330
+ Discovered: discovered,
331
+ Entry: entry,
332
+ })
333
+ return
334
+ }
335
+
336
+ logFn("⚠ " + short + " has unknown source (" + version + ") — needs resolution")
337
+ q.AppendUnique(WorkItem{
338
+ Name: name,
339
+ ShortName: short,
340
+ Type: entry.Kind,
341
+ Source: SourcePending,
342
+ Status: StatusPending,
343
+ Kind: KindPrompt,
344
+ Discovered: discovered,
345
+ Entry: entry,
346
+ })
347
+ }
348
+
349
+ // DiscoverNestedDeps scans a cloned/local package for deps.
350
+ func DiscoverNestedDeps(
351
+ workspaceRoot string,
352
+ item WorkItem,
353
+ catalog *bridge.CatalogResponse,
354
+ existing *WorkQueue,
355
+ logFn func(string),
356
+ ) {
357
+ deps, found := getOwdDepsLocal(workspaceRoot, item.ShortName, item.Type)
358
+ if !found {
359
+ return
360
+ }
361
+
362
+ for _, dep := range deps {
363
+ if existing.FindByName(dep) != nil {
364
+ continue
365
+ }
366
+ depShort := shortNameFromPkg(dep)
367
+ depKind := "module"
368
+ if strings.HasPrefix(depShort, "app-") {
369
+ depKind = "app"
370
+ } else if strings.HasPrefix(depShort, "theme-") {
371
+ depKind = "theme"
372
+ }
373
+ logFn("⚠ Dependency discovered: " + depShort)
374
+ pkgDeps, _ := readDesktopPackageDeps(filepath.Join(workspaceRoot, "desktop", "package.json"))
375
+ addPackageToQueue(existing, workspaceRoot, dep, depKind, pkgDeps, catalog, true, logFn)
376
+ }
377
+ }
@@ -0,0 +1,86 @@
1
+ package tui
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "testing"
7
+
8
+ "owd-cli/bridge"
9
+ )
10
+
11
+ func TestIsNpmVersion(t *testing.T) {
12
+ if !isNpmVersion("^1.2.0") {
13
+ t.Fatal("semver should be npm")
14
+ }
15
+ if !isNpmVersion("latest") {
16
+ t.Fatal("latest should be npm")
17
+ }
18
+ if isNpmVersion("workspace:*") {
19
+ t.Fatal("workspace should not be npm")
20
+ }
21
+ }
22
+
23
+ func TestReadDesktopPackageDeps(t *testing.T) {
24
+ dir := t.TempDir()
25
+ pkgPath := filepath.Join(dir, "package.json")
26
+ content := `{
27
+ "dependencies": {
28
+ "@owdproject/app-about": "workspace:*",
29
+ "@owdproject/theme-nova": "^1.0.0"
30
+ }
31
+ }`
32
+ if err := os.WriteFile(pkgPath, []byte(content), 0644); err != nil {
33
+ t.Fatal(err)
34
+ }
35
+ deps, err := readDesktopPackageDeps(pkgPath)
36
+ if err != nil {
37
+ t.Fatal(err)
38
+ }
39
+ if deps["@owdproject/app-about"] != "workspace:*" {
40
+ t.Fatalf("unexpected workspace dep: %v", deps)
41
+ }
42
+ if deps["@owdproject/theme-nova"] != "^1.0.0" {
43
+ t.Fatalf("unexpected npm dep: %v", deps)
44
+ }
45
+ }
46
+
47
+ func TestBuildQueueFromConfigWorkspaceAndNpm(t *testing.T) {
48
+ dir := t.TempDir()
49
+ desktopDir := filepath.Join(dir, "desktop")
50
+ if err := os.MkdirAll(desktopDir, 0755); err != nil {
51
+ t.Fatal(err)
52
+ }
53
+ pkgPath := filepath.Join(desktopDir, "package.json")
54
+ content := `{
55
+ "dependencies": {
56
+ "@owdproject/app-about": "workspace:*",
57
+ "@owdproject/theme-nova": "^1.2.0"
58
+ }
59
+ }`
60
+ if err := os.WriteFile(pkgPath, []byte(content), 0644); err != nil {
61
+ t.Fatal(err)
62
+ }
63
+
64
+ theme := "@owdproject/theme-nova"
65
+ ctx := &bridge.WorkspaceContext{
66
+ Config: bridge.Config{
67
+ Theme: &theme,
68
+ Apps: []string{"@owdproject/app-about"},
69
+ },
70
+ Paths: bridge.WorkspacePaths{PackageJson: pkgPath},
71
+ }
72
+
73
+ var logs []string
74
+ q := BuildQueueFromConfig(dir, ctx, nil, func(s string) { logs = append(logs, s) })
75
+ if q.TotalCount() != 2 {
76
+ t.Fatalf("expected 2 queue items, got %d", q.TotalCount())
77
+ }
78
+ about := q.FindByName("@owdproject/app-about")
79
+ if about == nil || about.Source != SourcePending {
80
+ t.Fatalf("expected pending workspace item, got %+v", about)
81
+ }
82
+ nova := q.FindByName("@owdproject/theme-nova")
83
+ if nova == nil || nova.Kind != KindNpm {
84
+ t.Fatalf("expected npm item, got %+v", nova)
85
+ }
86
+ }
@@ -0,0 +1,153 @@
1
+ package tui
2
+
3
+ import (
4
+ "github.com/charmbracelet/lipgloss"
5
+ )
6
+
7
+ // ─────────────────────────────────────────────
8
+ // Colors & Styles
9
+ // ─────────────────────────────────────────────
10
+
11
+ var (
12
+ colorAccent = lipgloss.Color("#7ee787")
13
+ colorCyan = lipgloss.Color("#24ebff")
14
+ colorDim = lipgloss.Color("#3a3a4a")
15
+ colorMuted = lipgloss.Color("#6c7086")
16
+ colorWarn = lipgloss.Color("#f9e2af")
17
+ colorErr = lipgloss.Color("#f38ba8")
18
+ colorNpm = lipgloss.Color("#7ee787")
19
+ colorGit = lipgloss.Color("#89dceb")
20
+ colorLocal = lipgloss.Color("#ff79c6")
21
+ colorBorder = lipgloss.Color("#4a4a6a")
22
+ colorBorderAct = lipgloss.Color("#7ee787")
23
+ colorWhite = lipgloss.Color("#cdd6f4")
24
+ colorSubtle = lipgloss.Color("#585b70")
25
+ colorBarBg = lipgloss.Color("#11111b")
26
+ colorBarFg = lipgloss.Color("#cdd6f4")
27
+ )
28
+
29
+ var (
30
+ panelBorderStyle = lipgloss.NewStyle().
31
+ Border(lipgloss.RoundedBorder()).
32
+ BorderForeground(colorBorder)
33
+
34
+ panelBorderActive = lipgloss.NewStyle().
35
+ Border(lipgloss.RoundedBorder()).
36
+ BorderForeground(colorBorderAct)
37
+
38
+ accentStyle = lipgloss.NewStyle().Foreground(colorAccent).Bold(true)
39
+ cyanStyle = lipgloss.NewStyle().Foreground(colorCyan).Bold(true)
40
+ mutedStyle = lipgloss.NewStyle().Foreground(colorMuted)
41
+ warnStyle = lipgloss.NewStyle().Foreground(colorWarn)
42
+ errStyle = lipgloss.NewStyle().Foreground(colorErr)
43
+ boldStyle = lipgloss.NewStyle().Bold(true).Foreground(colorWhite)
44
+ dimStyle = lipgloss.NewStyle().Foreground(colorDim)
45
+ subtleStyle = lipgloss.NewStyle().Foreground(colorSubtle)
46
+
47
+ tabActiveStyle = lipgloss.NewStyle().
48
+ Bold(true).
49
+ Background(colorAccent).
50
+ Foreground(lipgloss.Color("#1e1e2e")).
51
+ Padding(0, 2)
52
+
53
+ tabInactiveStyle = lipgloss.NewStyle().
54
+ Foreground(colorMuted).
55
+ Padding(0, 2)
56
+
57
+ selectedRowStyle = lipgloss.NewStyle().
58
+ Bold(true).
59
+ Foreground(colorAccent)
60
+
61
+ installedBadge = lipgloss.NewStyle().Foreground(colorAccent).Render("●")
62
+ uninstalledBadge = lipgloss.NewStyle().Foreground(colorSubtle).Render("○")
63
+
64
+ // bottom bar — no border, gemini-style
65
+ barStyle = lipgloss.NewStyle().
66
+ Foreground(colorBarFg).
67
+ PaddingLeft(2)
68
+
69
+ barTextStyle = lipgloss.NewStyle().
70
+ Foreground(colorBarFg)
71
+
72
+ barKeyStyle = lipgloss.NewStyle().
73
+ Foreground(colorAccent).
74
+ Bold(true)
75
+
76
+ barSepStyle = lipgloss.NewStyle().
77
+ Foreground(colorSubtle)
78
+
79
+ modalStyle = lipgloss.NewStyle().
80
+ Padding(1, 3)
81
+
82
+ modalOptionActive = lipgloss.NewStyle().
83
+ Background(colorCyan).
84
+ Foreground(lipgloss.Color("#000000")).
85
+ Bold(true).
86
+ Padding(0, 2)
87
+
88
+ modalOptionInactive = lipgloss.NewStyle().
89
+ Background(colorDim).
90
+ Foreground(colorMuted).
91
+ Padding(0, 2)
92
+
93
+ modalConfirmActiveErr = lipgloss.NewStyle().
94
+ Background(colorErr).
95
+ Foreground(lipgloss.Color("#000000")).
96
+ Bold(true).
97
+ Padding(0, 2)
98
+
99
+ modalConfirmActiveAccent = lipgloss.NewStyle().
100
+ Background(colorAccent).
101
+ Foreground(lipgloss.Color("#000000")).
102
+ Bold(true).
103
+ Padding(0, 2)
104
+
105
+ settingsSaveActive = lipgloss.NewStyle().
106
+ Background(colorAccent).
107
+ Foreground(lipgloss.Color("#000000")).
108
+ Bold(true).
109
+ Padding(0, 3)
110
+
111
+ settingsSaveInactive = lipgloss.NewStyle().
112
+ Background(colorDim).
113
+ Foreground(colorWhite).
114
+ Padding(0, 3)
115
+
116
+ settingsCancelActive = lipgloss.NewStyle().
117
+ Background(colorErr).
118
+ Foreground(lipgloss.Color("#000000")).
119
+ Bold(true).
120
+ Padding(0, 3)
121
+
122
+ settingsCancelInactive = lipgloss.NewStyle().
123
+ Background(colorDim).
124
+ Foreground(colorWhite).
125
+ Padding(0, 3)
126
+
127
+ localSourceTag = lipgloss.NewStyle().
128
+ Foreground(colorLocal)
129
+
130
+ managePackageRightBorder = lipgloss.NewStyle().
131
+ Border(lipgloss.NormalBorder(), false, false, false, true).
132
+ BorderForeground(colorDim).
133
+ PaddingLeft(3)
134
+
135
+ progressBarEmptyStyle = lipgloss.NewStyle().Foreground(colorDim)
136
+ progressBarFilledStyle = lipgloss.NewStyle().Foreground(colorCyan)
137
+ spinnerStyle = lipgloss.NewStyle().Foreground(colorCyan).Bold(true)
138
+ managePackageLeftPanelStyle = lipgloss.NewStyle().Width(65)
139
+ desktopSubTitleStyle = lipgloss.NewStyle().Foreground(colorMuted)
140
+ modalWrapperStyle = lipgloss.NewStyle().
141
+ Border(lipgloss.RoundedBorder()).
142
+ BorderForeground(colorCyan).
143
+ Padding(1, 3)
144
+ tableHeaderStyle = lipgloss.NewStyle().Foreground(colorSubtle).Bold(true)
145
+ dirtyIndicatorStyle = lipgloss.NewStyle().Foreground(colorNpm)
146
+ selectedRowBgStyle = lipgloss.NewStyle().Background(colorDim)
147
+ statusGreenStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#2ea043"))
148
+ accentRegularStyle = lipgloss.NewStyle().Foreground(colorAccent)
149
+ cyanRegularStyle = lipgloss.NewStyle().Foreground(colorCyan)
150
+ warnBoldStyle = lipgloss.NewStyle().Foreground(colorWarn).Bold(true)
151
+ cyanBoldStyle = lipgloss.NewStyle().Foreground(colorCyan).Bold(true)
152
+ blinkGreenStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#2ea043")).Bold(true)
153
+ )