@owdproject/core 3.3.1 → 4.0.0-alpha.3
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.
- package/README.md +91 -59
- package/dist/dom/index.d.mts +65 -0
- package/dist/dom/index.d.ts +65 -0
- package/dist/dom/index.mjs +224 -0
- package/dist/events/index.d.mts +70 -0
- package/dist/events/index.d.ts +70 -0
- package/dist/events/index.mjs +3 -0
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.mjs +115 -0
- package/dist/layout/index.d.mts +41 -0
- package/dist/layout/index.d.ts +41 -0
- package/dist/layout/index.mjs +64 -0
- package/dist/machines/index.d.mts +31 -0
- package/dist/machines/index.d.ts +31 -0
- package/dist/machines/index.mjs +2 -0
- package/dist/managers/index.d.mts +117 -0
- package/dist/managers/index.d.ts +117 -0
- package/dist/managers/index.mjs +6 -0
- package/dist/session/index.d.mts +57 -0
- package/dist/session/index.d.ts +57 -0
- package/dist/session/index.mjs +65 -0
- package/dist/shared/core.B0B0KfVK.mjs +865 -0
- package/dist/shared/core.B1XxB289.mjs +239 -0
- package/dist/shared/core.B3mp7ztE.mjs +237 -0
- package/dist/shared/core.BOvFdEVQ.d.ts +114 -0
- package/dist/shared/core.CsDFgGnX.mjs +40 -0
- package/dist/shared/core.Di1eFcr_.d.mts +114 -0
- package/dist/shared/core.Pd_Ogt4g.mjs +101 -0
- package/dist/shared/core.b3Xldzyu.mjs +29 -0
- package/dist/types/index.d.mts +266 -0
- package/dist/types/index.d.ts +266 -0
- package/dist/types/index.mjs +1 -0
- package/package.json +60 -87
- package/bin/cli.js +0 -395
- package/bin/desktop.js +0 -5
- package/bin/lib/catalog.js +0 -293
- package/bin/lib/config.js +0 -109
- package/bin/lib/desktopConfig.js +0 -56
- package/bin/lib/generateTemplate.js +0 -389
- package/bin/lib/install.js +0 -408
- package/bin/lib/migrateModulePlayground.config.mjs +0 -164
- package/bin/lib/migrateModulePlayground.mjs +0 -313
- package/bin/lib/npmVersions.js +0 -124
- package/bin/lib/playgroundContext.js +0 -188
- package/bin/lib/scaffold.js +0 -197
- package/bin/lib/status.js +0 -289
- package/bin/lib/validateModule.js +0 -697
- package/bin/lib/workspace.js +0 -182
- package/bin/owd.js +0 -5
- package/bin/tui.js +0 -1379
- package/dist/module.d.mts +0 -13
- package/dist/module.json +0 -9
- package/dist/module.mjs +0 -268
- package/dist/types.d.mts +0 -7
- package/index.ts +0 -5
- package/module.ts +0 -223
- package/runtime/components/Desktop/Application/DesktopApplicationRender.vue +0 -20
- package/runtime/components/Desktop/Application/DesktopApplicationWindowsRender.vue +0 -38
- package/runtime/components/Desktop/Background/DesktopBackground.vue +0 -16
- package/runtime/components/Desktop/Core/DesktopCore.vue +0 -90
- package/runtime/components/Desktop/Time/DesktopTime.vue +0 -56
- package/runtime/components/Desktop/Window/DesktopWindow.vue +0 -204
- package/runtime/components/Desktop/Window/DesktopWindowContent.vue +0 -28
- package/runtime/components/Desktop/Window/DesktopWindowNav.vue +0 -66
- package/runtime/composables/useApplicationEntries.ts +0 -51
- package/runtime/composables/useApplicationManager.ts +0 -285
- package/runtime/composables/useDesktopManager.ts +0 -49
- package/runtime/composables/useTerminalManager.ts +0 -66
- package/runtime/composables/useWorkspaceManager.ts +0 -41
- package/runtime/internal/controllers/ApplicationController.ts +0 -345
- package/runtime/internal/controllers/WindowController.ts +0 -462
- package/runtime/plugins/02.desktop-register-desktop-apps.client.ts +0 -17
- package/runtime/plugins/resize.client.ts +0 -6
- package/runtime/stores/storeApplicationMeta.ts +0 -59
- package/runtime/stores/storeApplicationWindows.ts +0 -20
- package/runtime/stores/storeDesktop.ts +0 -111
- package/runtime/stores/storeDesktopDefaultApps.ts +0 -53
- package/runtime/stores/storeDesktopVolume.ts +0 -18
- package/runtime/stores/storeDesktopWindow.ts +0 -20
- package/runtime/stores/storeDesktopWorkspace.ts +0 -75
- package/runtime/utils/captureElementToCanvas.ts +0 -21
- package/runtime/utils/explorerEntryPath.ts +0 -10
- package/runtime/utils/resolveDesktopConfigPath.ts +0 -52
- package/runtime/utils/splitDesktopUserConfig.ts +0 -85
- package/runtime/utils/utilApp.ts +0 -37
- package/runtime/utils/utilCommon.ts +0 -26
- package/runtime/utils/utilDebug.ts +0 -30
- package/runtime/utils/utilDesktop.ts +0 -35
- package/runtime/utils/utilTerminal.ts +0 -6
- package/runtime/utils/utilWindow.ts +0 -15
- package/runtime/utils/validateDesktopUserConfig.ts +0 -44
- package/types/index.d.ts +0 -420
package/bin/tui.js
DELETED
|
@@ -1,1379 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process'
|
|
2
|
-
import { dirname, join } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import blessed from 'neo-blessed'
|
|
5
|
-
import {
|
|
6
|
-
findWorkspaceRoot,
|
|
7
|
-
loadSettings,
|
|
8
|
-
saveSettings,
|
|
9
|
-
desktopPaths,
|
|
10
|
-
KINDS,
|
|
11
|
-
shortName,
|
|
12
|
-
isWorkspaceInstallMode,
|
|
13
|
-
isInstallableDesktopModule,
|
|
14
|
-
normalizeInstallMode,
|
|
15
|
-
hasDocsModuleInstalled,
|
|
16
|
-
docsBasePathFromConfig,
|
|
17
|
-
} from './lib/workspace.js'
|
|
18
|
-
import {
|
|
19
|
-
readDesktopConfig,
|
|
20
|
-
writeDesktopConfig,
|
|
21
|
-
readDesktopDependencies,
|
|
22
|
-
resolveConfigPathForWrite,
|
|
23
|
-
} from './lib/config.js'
|
|
24
|
-
import { warnLegacyDesktopConfig } from './lib/desktopConfig.js'
|
|
25
|
-
import {
|
|
26
|
-
loadCatalog,
|
|
27
|
-
filterCatalog,
|
|
28
|
-
mergeInstalled,
|
|
29
|
-
readThemeMeta,
|
|
30
|
-
} from './lib/catalog.js'
|
|
31
|
-
import {
|
|
32
|
-
installPackage,
|
|
33
|
-
materializeToWorkspace,
|
|
34
|
-
hasLocalWorkspaceSource,
|
|
35
|
-
runPrepareModules,
|
|
36
|
-
resolveInstallPlan,
|
|
37
|
-
installTag,
|
|
38
|
-
} from './lib/install.js'
|
|
39
|
-
import {
|
|
40
|
-
getClientStatus,
|
|
41
|
-
startDev,
|
|
42
|
-
stopDev,
|
|
43
|
-
waitForDev,
|
|
44
|
-
devLogPath,
|
|
45
|
-
formatSparkline,
|
|
46
|
-
formatBar,
|
|
47
|
-
} from './lib/status.js'
|
|
48
|
-
import { resolveDevTarget } from './lib/playgroundContext.js'
|
|
49
|
-
|
|
50
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
51
|
-
|
|
52
|
-
const C = {
|
|
53
|
-
border: 'cyan',
|
|
54
|
-
borderDim: '#3a3a4a',
|
|
55
|
-
focus: 'green',
|
|
56
|
-
accent: '#7ee787',
|
|
57
|
-
warn: 'yellow',
|
|
58
|
-
err: 'red',
|
|
59
|
-
muted: '#6c7086',
|
|
60
|
-
title: 'white',
|
|
61
|
-
userMode: '#7ee787',
|
|
62
|
-
devMode: 'cyan',
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const SPINNER = ['|', '/', '-', '\\']
|
|
66
|
-
const MEM_HISTORY_LEN = 30
|
|
67
|
-
|
|
68
|
-
/** @param {string} commandName */
|
|
69
|
-
export async function runTui(commandName = 'desktop') {
|
|
70
|
-
const workspaceRoot = findWorkspaceRoot()
|
|
71
|
-
if (!workspaceRoot) {
|
|
72
|
-
console.error('Not inside an OWD workspace.')
|
|
73
|
-
process.exit(1)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const paths = desktopPaths(workspaceRoot)
|
|
77
|
-
const devTarget = resolveDevTarget(process.cwd(), workspaceRoot)
|
|
78
|
-
const playgroundActive = devTarget?.mode === 'playground'
|
|
79
|
-
const playgroundLabel = devTarget?.packageName ?? null
|
|
80
|
-
warnLegacyDesktopConfig(
|
|
81
|
-
paths.configLegacy ? { legacy: true, file: 'owd.config.ts' } : null,
|
|
82
|
-
)
|
|
83
|
-
let settings = loadSettings(workspaceRoot)
|
|
84
|
-
let config = readDesktopConfig(paths.config, workspaceRoot)
|
|
85
|
-
let deps = readDesktopDependencies(paths.packageJson)
|
|
86
|
-
|
|
87
|
-
let catalog = []
|
|
88
|
-
let catalogCacheAge = ''
|
|
89
|
-
/** @type {'catalog' | 'theme'} */
|
|
90
|
-
let focusPanel = 'catalog'
|
|
91
|
-
let activeTab = 'module'
|
|
92
|
-
let pendingTheme = config.theme
|
|
93
|
-
/** @type {Map<string, boolean>} */
|
|
94
|
-
const pendingPackages = new Map()
|
|
95
|
-
let statusLine = ''
|
|
96
|
-
let devPhase = 'idle'
|
|
97
|
-
let clientStatus = await getClientStatus(workspaceRoot, settings.devPort)
|
|
98
|
-
/** @type {number[]} */
|
|
99
|
-
const memHistory = []
|
|
100
|
-
let spinnerFrame = 0
|
|
101
|
-
let spinnerTimer = null
|
|
102
|
-
let pulseOn = true
|
|
103
|
-
let modeFlash = 0
|
|
104
|
-
let saveProgress = null
|
|
105
|
-
let settingsOpen = false
|
|
106
|
-
let menuOpen = false
|
|
107
|
-
|
|
108
|
-
/** @type {{ id: string, label: string, key: string }[]} */
|
|
109
|
-
const MENU_ITEMS = [
|
|
110
|
-
{ id: 'start', label: 'Start dev server', key: 's' },
|
|
111
|
-
{ id: 'stop', label: 'Stop dev server', key: 'x' },
|
|
112
|
-
{ id: 'reboot', label: 'Reboot dev server', key: 'R' },
|
|
113
|
-
{ id: 'save', label: 'Save catalog / theme', key: 'w' },
|
|
114
|
-
{ id: 'refresh', label: 'Refresh package list', key: 'r' },
|
|
115
|
-
{ id: 'docs', label: 'Open documentation', key: 'i' },
|
|
116
|
-
{ id: 'settings', label: 'Settings', key: 'g' },
|
|
117
|
-
{ id: 'mode', label: 'Toggle install mode (USER/DEV)', key: 'd' },
|
|
118
|
-
{ id: 'build', label: 'Build (pnpm generate)', key: 'b' },
|
|
119
|
-
{ id: 'quit', label: 'Quit control panel', key: 'q' },
|
|
120
|
-
]
|
|
121
|
-
|
|
122
|
-
const screen = blessed.screen({
|
|
123
|
-
smartCSR: true,
|
|
124
|
-
fullUnicode: true,
|
|
125
|
-
title: 'Open Web Desktop',
|
|
126
|
-
dockCorners: false,
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const HEADER_ROWS = playgroundActive ? 4 : 3
|
|
130
|
-
const MAIN_TOP = HEADER_ROWS
|
|
131
|
-
const CLIENT_PANEL_H = 10
|
|
132
|
-
const PANELS_TOP = MAIN_TOP + CLIENT_PANEL_H
|
|
133
|
-
|
|
134
|
-
const titleBar = blessed.box({
|
|
135
|
-
parent: screen,
|
|
136
|
-
top: 0,
|
|
137
|
-
left: 0,
|
|
138
|
-
width: '100%',
|
|
139
|
-
height: HEADER_ROWS,
|
|
140
|
-
tags: true,
|
|
141
|
-
content: '',
|
|
142
|
-
style: { fg: C.title, bg: 'black' },
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const clientBox = blessed.box({
|
|
146
|
-
parent: screen,
|
|
147
|
-
top: MAIN_TOP,
|
|
148
|
-
left: 0,
|
|
149
|
-
width: '58%',
|
|
150
|
-
height: CLIENT_PANEL_H,
|
|
151
|
-
tags: true,
|
|
152
|
-
border: { type: 'line' },
|
|
153
|
-
style: {
|
|
154
|
-
fg: 'white',
|
|
155
|
-
bg: 'black',
|
|
156
|
-
border: { fg: C.border },
|
|
157
|
-
},
|
|
158
|
-
label: ' Dev server ',
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
const metricsBox = blessed.box({
|
|
162
|
-
parent: screen,
|
|
163
|
-
top: MAIN_TOP,
|
|
164
|
-
left: '58%',
|
|
165
|
-
width: '42%',
|
|
166
|
-
height: CLIENT_PANEL_H,
|
|
167
|
-
tags: true,
|
|
168
|
-
border: { type: 'line' },
|
|
169
|
-
style: {
|
|
170
|
-
fg: 'white',
|
|
171
|
-
bg: 'black',
|
|
172
|
-
border: { fg: C.borderDim },
|
|
173
|
-
},
|
|
174
|
-
label: ' Metrics ',
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
const themeBox = blessed.box({
|
|
178
|
-
parent: screen,
|
|
179
|
-
top: PANELS_TOP,
|
|
180
|
-
left: 0,
|
|
181
|
-
width: '32%',
|
|
182
|
-
height: '100%-15',
|
|
183
|
-
tags: true,
|
|
184
|
-
border: { type: 'line' },
|
|
185
|
-
style: {
|
|
186
|
-
fg: 'white',
|
|
187
|
-
bg: 'black',
|
|
188
|
-
border: { fg: C.borderDim },
|
|
189
|
-
},
|
|
190
|
-
label: ' Theme ',
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
const themeList = blessed.list({
|
|
194
|
-
parent: themeBox,
|
|
195
|
-
top: 1,
|
|
196
|
-
left: 1,
|
|
197
|
-
width: '100%-2',
|
|
198
|
-
bottom: 9,
|
|
199
|
-
tags: true,
|
|
200
|
-
keys: true,
|
|
201
|
-
vi: true,
|
|
202
|
-
mouse: true,
|
|
203
|
-
scrollbar: true,
|
|
204
|
-
alwaysScroll: true,
|
|
205
|
-
style: {
|
|
206
|
-
fg: 'white',
|
|
207
|
-
bg: 'black',
|
|
208
|
-
selected: { bg: '#313244', fg: C.accent, bold: true },
|
|
209
|
-
},
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
const themeInfo = blessed.box({
|
|
213
|
-
parent: themeBox,
|
|
214
|
-
bottom: 1,
|
|
215
|
-
left: 1,
|
|
216
|
-
width: '100%-2',
|
|
217
|
-
height: 7,
|
|
218
|
-
tags: true,
|
|
219
|
-
content: '',
|
|
220
|
-
style: { fg: C.muted, bg: 'black' },
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
let themeListSyncing = false
|
|
224
|
-
|
|
225
|
-
const catalogBox = blessed.box({
|
|
226
|
-
parent: screen,
|
|
227
|
-
top: PANELS_TOP,
|
|
228
|
-
left: '32%',
|
|
229
|
-
width: '68%',
|
|
230
|
-
height: '100%-15',
|
|
231
|
-
tags: true,
|
|
232
|
-
border: { type: 'line' },
|
|
233
|
-
style: {
|
|
234
|
-
fg: 'white',
|
|
235
|
-
bg: 'black',
|
|
236
|
-
border: { fg: C.focus },
|
|
237
|
-
},
|
|
238
|
-
label: ' Apps & modules ',
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
const tabBar = blessed.box({
|
|
242
|
-
parent: catalogBox,
|
|
243
|
-
top: 1,
|
|
244
|
-
left: 1,
|
|
245
|
-
width: '100%-2',
|
|
246
|
-
height: 1,
|
|
247
|
-
tags: true,
|
|
248
|
-
content: '',
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
const catalogList = blessed.list({
|
|
252
|
-
parent: catalogBox,
|
|
253
|
-
top: 2,
|
|
254
|
-
left: 1,
|
|
255
|
-
width: '100%-2',
|
|
256
|
-
bottom: 9,
|
|
257
|
-
tags: true,
|
|
258
|
-
keys: true,
|
|
259
|
-
vi: true,
|
|
260
|
-
mouse: true,
|
|
261
|
-
scrollbar: true,
|
|
262
|
-
alwaysScroll: true,
|
|
263
|
-
style: {
|
|
264
|
-
fg: 'white',
|
|
265
|
-
bg: 'black',
|
|
266
|
-
selected: { bg: '#45475a', fg: 'white', bold: true },
|
|
267
|
-
},
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
const detailBox = blessed.box({
|
|
271
|
-
parent: catalogBox,
|
|
272
|
-
bottom: 1,
|
|
273
|
-
left: 1,
|
|
274
|
-
width: '100%-2',
|
|
275
|
-
height: 7,
|
|
276
|
-
tags: true,
|
|
277
|
-
content: '',
|
|
278
|
-
style: { fg: C.muted, bg: 'black' },
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
/** Keep list above detail; shrink detail on short terminals. */
|
|
282
|
-
function layoutCatalogPanel() {
|
|
283
|
-
const rows = screen.height ?? 24
|
|
284
|
-
const detailH = rows < 26 ? 4 : rows < 34 ? 5 : 7
|
|
285
|
-
|
|
286
|
-
Object.assign(detailBox.position, {
|
|
287
|
-
bottom: 1,
|
|
288
|
-
left: 1,
|
|
289
|
-
width: '100%-2',
|
|
290
|
-
height: detailH,
|
|
291
|
-
top: undefined,
|
|
292
|
-
right: undefined,
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
Object.assign(catalogList.position, {
|
|
296
|
-
top: 2,
|
|
297
|
-
left: 1,
|
|
298
|
-
width: '100%-2',
|
|
299
|
-
bottom: detailH + 2,
|
|
300
|
-
height: undefined,
|
|
301
|
-
right: undefined,
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
detailBox.emit('resize')
|
|
305
|
-
catalogList.emit('resize')
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
layoutCatalogPanel()
|
|
309
|
-
screen.on('resize', () => {
|
|
310
|
-
layoutCatalogPanel()
|
|
311
|
-
screen.render()
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
const helpBar = blessed.box({
|
|
315
|
-
parent: screen,
|
|
316
|
-
bottom: 0,
|
|
317
|
-
left: 0,
|
|
318
|
-
width: '100%',
|
|
319
|
-
height: 5,
|
|
320
|
-
tags: true,
|
|
321
|
-
border: { type: 'line' },
|
|
322
|
-
label: ' Keys ',
|
|
323
|
-
style: { fg: C.muted, bg: '#11111b', border: { fg: C.borderDim } },
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
const settingsOverlay = blessed.box({
|
|
327
|
-
parent: screen,
|
|
328
|
-
top: 'center',
|
|
329
|
-
left: 'center',
|
|
330
|
-
width: 54,
|
|
331
|
-
height: 14,
|
|
332
|
-
hidden: true,
|
|
333
|
-
tags: true,
|
|
334
|
-
border: { type: 'line' },
|
|
335
|
-
label: ' Settings ',
|
|
336
|
-
style: { fg: 'white', bg: '#1e1e2e', border: { fg: C.accent } },
|
|
337
|
-
content: '',
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
const settingsInfo = blessed.box({
|
|
341
|
-
parent: settingsOverlay,
|
|
342
|
-
top: 1,
|
|
343
|
-
left: 2,
|
|
344
|
-
width: '100%-4',
|
|
345
|
-
height: 3,
|
|
346
|
-
tags: true,
|
|
347
|
-
style: { fg: C.muted, bg: '#1e1e2e' },
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
const githubInput = blessed.textbox({
|
|
351
|
-
parent: settingsOverlay,
|
|
352
|
-
top: 5,
|
|
353
|
-
left: 2,
|
|
354
|
-
width: '100%-4',
|
|
355
|
-
height: 3,
|
|
356
|
-
inputOnFocus: true,
|
|
357
|
-
keys: true,
|
|
358
|
-
mouse: true,
|
|
359
|
-
style: { fg: 'white', bg: '#313244', focus: { border: { fg: C.accent } } },
|
|
360
|
-
border: { type: 'line' },
|
|
361
|
-
label: ' GitHub username ',
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
const menuOverlay = blessed.box({
|
|
365
|
-
parent: screen,
|
|
366
|
-
top: 'center',
|
|
367
|
-
left: 'center',
|
|
368
|
-
width: 52,
|
|
369
|
-
height: MENU_ITEMS.length + 4,
|
|
370
|
-
hidden: true,
|
|
371
|
-
tags: true,
|
|
372
|
-
border: { type: 'line' },
|
|
373
|
-
label: ' Menu ',
|
|
374
|
-
style: { fg: 'white', bg: '#1e1e2e', border: { fg: C.accent } },
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
const menuList = blessed.list({
|
|
378
|
-
parent: menuOverlay,
|
|
379
|
-
top: 1,
|
|
380
|
-
left: 1,
|
|
381
|
-
width: '100%-2',
|
|
382
|
-
height: MENU_ITEMS.length + 1,
|
|
383
|
-
tags: true,
|
|
384
|
-
keys: true,
|
|
385
|
-
vi: true,
|
|
386
|
-
mouse: true,
|
|
387
|
-
scrollbar: true,
|
|
388
|
-
style: {
|
|
389
|
-
fg: 'white',
|
|
390
|
-
bg: '#1e1e2e',
|
|
391
|
-
selected: { bg: '#313244', fg: C.accent, bold: true },
|
|
392
|
-
},
|
|
393
|
-
items: MENU_ITEMS.map(({ label, key }) => `[${key}] ${label}`),
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
function overlayBlocksKeys() {
|
|
397
|
-
return settingsOpen || menuOpen
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function installModeLabel() {
|
|
401
|
-
return isWorkspaceInstallMode(settings) ? 'DEV' : 'USER'
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function installModeColor() {
|
|
405
|
-
return isWorkspaceInstallMode(settings) ? C.devMode : C.userMode
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function renderModeHeader() {
|
|
409
|
-
const isDev = isWorkspaceInstallMode(settings)
|
|
410
|
-
const mode = installModeLabel()
|
|
411
|
-
const color = installModeColor()
|
|
412
|
-
|
|
413
|
-
const installLine = isDev
|
|
414
|
-
? settings.githubUser
|
|
415
|
-
? `save → git clone {bold}github.com/${settings.githubUser}/…{/}`
|
|
416
|
-
: 'save → git clone {bold}github.com/owdproject/…{/}'
|
|
417
|
-
: 'save → {bold}npm install{/} (registry)'
|
|
418
|
-
|
|
419
|
-
const listCount = catalog.length
|
|
420
|
-
? `{bold}${catalog.length}{/} packages`
|
|
421
|
-
: 'loading…'
|
|
422
|
-
const listCache = catalogCacheAge ? ` · ${catalogCacheAge}` : ''
|
|
423
|
-
|
|
424
|
-
const borderFlash = modeFlash > 0 ? 'white' : C.title
|
|
425
|
-
titleBar.style.fg = borderFlash
|
|
426
|
-
|
|
427
|
-
const playgroundLine = playgroundActive && playgroundLabel
|
|
428
|
-
? ` {${C.accent}-fg}Playground:{/} {bold}${playgroundLabel}{/} {${C.muted}-fg}[s] starts playground · catalog [w] → monorepo desktop{/}`
|
|
429
|
-
: null
|
|
430
|
-
|
|
431
|
-
titleBar.setContent(
|
|
432
|
-
[
|
|
433
|
-
` {bold}{white-fg}Open Web Desktop{/}{/} {${C.muted}-fg}control panel{/} {${C.muted}-fg}[m] menu{/}`,
|
|
434
|
-
playgroundLine,
|
|
435
|
-
` {${C.muted}-fg}Install mode{/} {bold}{${color}-fg}${mode}{/}{/} ${installLine} {${C.muted}-fg}[d] switch{/}`,
|
|
436
|
-
` {${C.muted}-fg}Package list{/} ${listCount} from GitHub${listCache} {${C.muted}-fg}[r] refresh{/}`,
|
|
437
|
-
]
|
|
438
|
-
.filter(Boolean)
|
|
439
|
-
.join('\n'),
|
|
440
|
-
)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function themeEntries() {
|
|
444
|
-
return mergeInstalled(filterCatalog(catalog, 'theme'), config, deps, workspaceRoot)
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function catalogEntries() {
|
|
448
|
-
return mergeInstalled(filterCatalog(catalog, activeTab), config, deps, workspaceRoot)
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
function applyFocusStyles() {
|
|
452
|
-
const themeFocused = focusPanel === 'theme'
|
|
453
|
-
themeBox.style.border.fg = themeFocused ? C.focus : C.borderDim
|
|
454
|
-
catalogBox.style.border.fg = themeFocused ? C.borderDim : C.focus
|
|
455
|
-
themeBox.setLabel(themeFocused ? ' {bold}Theme ◀{/} ' : ' Theme ')
|
|
456
|
-
const tab = KINDS[activeTab].label
|
|
457
|
-
catalogBox.setLabel(
|
|
458
|
-
themeFocused ? ' Apps & modules ' : ` {bold}Apps & modules · ${tab} ◀{/} `,
|
|
459
|
-
)
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function panelLabel() {
|
|
463
|
-
if (focusPanel === 'theme') return 'THEME'
|
|
464
|
-
return `APPS & MODULES · ${KINDS[activeTab].label.toUpperCase()}`
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function isDevServerUp() {
|
|
468
|
-
return (
|
|
469
|
-
devPhase === 'running' ||
|
|
470
|
-
devPhase === 'starting' ||
|
|
471
|
-
clientStatus.running ||
|
|
472
|
-
clientStatus.http.up
|
|
473
|
-
)
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function serverKeyHints() {
|
|
477
|
-
return `{${C.muted}-fg}[s]{/} start/stop {${C.muted}-fg}[m]{/} menu`
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function defaultStatusHint() {
|
|
481
|
-
return 'Press [s] start/stop · [m] menu · [w] save catalog changes'
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function renderHelp(tone = 'info') {
|
|
485
|
-
const statusColor = tone === 'error' ? C.err : tone === 'ok' ? C.accent : C.warn
|
|
486
|
-
const msg = statusLine || defaultStatusHint()
|
|
487
|
-
const progress =
|
|
488
|
-
saveProgress !== null
|
|
489
|
-
? ` ${formatBar(saveProgress.step, saveProgress.total, 16)} ${saveProgress.label}`
|
|
490
|
-
: ''
|
|
491
|
-
|
|
492
|
-
const navLine =
|
|
493
|
-
focusPanel === 'theme'
|
|
494
|
-
? `{${C.muted}-fg}Navigate{/} {bold}↑↓{/} {bold}PgUp/PgDn{/} scroll {bold}Enter{/}/{bold}Space{/} apply {bold}Tab{/} → apps & modules`
|
|
495
|
-
: `{${C.muted}-fg}Navigate{/} {bold}↑↓{/} {bold}j/k{/} {bold}PgUp/PgDn{/} scroll {bold}Space{/} toggle {bold}Tab{/} → theme {bold}1{/} apps {bold}2{/} modules {bold}3{/} theme panel`
|
|
496
|
-
|
|
497
|
-
helpBar.setContent(
|
|
498
|
-
[
|
|
499
|
-
`{bold}{${statusColor}-fg}${msg}{/}{/}${progress}`,
|
|
500
|
-
`{${C.focus}-fg}▸{/} {bold}${panelLabel()}{/}`,
|
|
501
|
-
navLine,
|
|
502
|
-
`{${C.muted}-fg}Server{/} {bold}s{/} start/stop {bold}m{/} menu {${C.muted}-fg}│{/} {bold}w{/} save {bold}r{/} refresh {bold}i{/} docs {bold}g{/} settings {bold}q{/} quit`,
|
|
503
|
-
].join('\n'),
|
|
504
|
-
)
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function setStatus(msg, tone = 'info') {
|
|
508
|
-
statusLine = msg
|
|
509
|
-
renderHelp(tone)
|
|
510
|
-
screen.render()
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/** Clear stray CLI output and redraw the full TUI (after save, build, etc.). */
|
|
514
|
-
function refreshTerminalUi() {
|
|
515
|
-
try {
|
|
516
|
-
screen.program.clear()
|
|
517
|
-
screen.program.home()
|
|
518
|
-
} catch {
|
|
519
|
-
process.stdout.write('\x1b[2J\x1b[H')
|
|
520
|
-
}
|
|
521
|
-
layoutCatalogPanel()
|
|
522
|
-
if (focusPanel === 'theme') themeList.focus()
|
|
523
|
-
else catalogList.focus()
|
|
524
|
-
renderAll()
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function startSpinner(message) {
|
|
528
|
-
stopSpinner()
|
|
529
|
-
spinnerFrame = 0
|
|
530
|
-
setStatus(message)
|
|
531
|
-
spinnerTimer = setInterval(() => {
|
|
532
|
-
spinnerFrame = (spinnerFrame + 1) % SPINNER.length
|
|
533
|
-
statusLine = `${SPINNER[spinnerFrame]} ${message}`
|
|
534
|
-
renderHelp('info')
|
|
535
|
-
screen.render()
|
|
536
|
-
}, 120)
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function stopSpinner() {
|
|
540
|
-
if (spinnerTimer) {
|
|
541
|
-
clearInterval(spinnerTimer)
|
|
542
|
-
spinnerTimer = null
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
async function flashModeBorder() {
|
|
547
|
-
for (let i = 3; i > 0; i--) {
|
|
548
|
-
modeFlash = i
|
|
549
|
-
renderModeHeader()
|
|
550
|
-
screen.render()
|
|
551
|
-
await new Promise((r) => setTimeout(r, 80))
|
|
552
|
-
}
|
|
553
|
-
modeFlash = 0
|
|
554
|
-
renderModeHeader()
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function toggleInstallMode() {
|
|
558
|
-
const next = isWorkspaceInstallMode(settings) ? 'npm' : 'workspace'
|
|
559
|
-
settings = { ...settings, installMode: normalizeInstallMode(next) }
|
|
560
|
-
saveSettings(workspaceRoot, settings)
|
|
561
|
-
renderModeHeader()
|
|
562
|
-
renderCatalogList()
|
|
563
|
-
renderDetail()
|
|
564
|
-
flashModeBorder()
|
|
565
|
-
setStatus(
|
|
566
|
-
next === 'workspace'
|
|
567
|
-
? 'Dev mode — saves will git clone into apps/packages/themes'
|
|
568
|
-
: 'User mode — saves will install from npm',
|
|
569
|
-
'ok',
|
|
570
|
-
)
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function renderSettingsPanel() {
|
|
574
|
-
const mode = installModeLabel()
|
|
575
|
-
const color = installModeColor()
|
|
576
|
-
const saveBehavior = isWorkspaceInstallMode(settings)
|
|
577
|
-
? settings.githubUser
|
|
578
|
-
? `git clone from github.com/${settings.githubUser}/…`
|
|
579
|
-
: 'git clone from github.com/owdproject/…'
|
|
580
|
-
: 'install from npm registry'
|
|
581
|
-
|
|
582
|
-
settingsInfo.setContent(
|
|
583
|
-
[
|
|
584
|
-
`{bold}Install mode{/} {${color}-fg}${mode}{/}`,
|
|
585
|
-
`{${C.muted}-fg}When you press {bold}w{/} (save): ${saveBehavior}{/}`,
|
|
586
|
-
`{${C.muted}-fg}[d] toggle USER/DEV · GitHub username below = your fork org (optional){/}`,
|
|
587
|
-
].join('\n'),
|
|
588
|
-
)
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function openSettings() {
|
|
592
|
-
settingsOpen = true
|
|
593
|
-
settingsOverlay.show()
|
|
594
|
-
renderSettingsPanel()
|
|
595
|
-
githubInput.setValue(settings.githubUser ?? '')
|
|
596
|
-
githubInput.focus()
|
|
597
|
-
setStatus('Settings — Enter save · Esc cancel · d toggles USER/DEV')
|
|
598
|
-
screen.render()
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function closeSettings() {
|
|
602
|
-
settingsOpen = false
|
|
603
|
-
settingsOverlay.hide()
|
|
604
|
-
focusCatalog()
|
|
605
|
-
screen.render()
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function openMenu() {
|
|
609
|
-
if (settingsOpen) return
|
|
610
|
-
menuOpen = true
|
|
611
|
-
menuOverlay.show()
|
|
612
|
-
menuList.focus()
|
|
613
|
-
setStatus('Menu — ↑↓ select · Enter run · Esc close')
|
|
614
|
-
screen.render()
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function closeMenu() {
|
|
618
|
-
menuOpen = false
|
|
619
|
-
menuOverlay.hide()
|
|
620
|
-
focusCatalog()
|
|
621
|
-
screen.render()
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
async function runMenuAction(id) {
|
|
625
|
-
closeMenu()
|
|
626
|
-
switch (id) {
|
|
627
|
-
case 'start':
|
|
628
|
-
await startDevServer()
|
|
629
|
-
break
|
|
630
|
-
case 'stop':
|
|
631
|
-
await stopDevServer()
|
|
632
|
-
break
|
|
633
|
-
case 'reboot':
|
|
634
|
-
await rebootDevServer()
|
|
635
|
-
break
|
|
636
|
-
case 'save':
|
|
637
|
-
await applyChanges()
|
|
638
|
-
break
|
|
639
|
-
case 'refresh':
|
|
640
|
-
await refreshCatalog()
|
|
641
|
-
break
|
|
642
|
-
case 'docs':
|
|
643
|
-
openDocsInBrowser()
|
|
644
|
-
break
|
|
645
|
-
case 'settings':
|
|
646
|
-
openSettings()
|
|
647
|
-
break
|
|
648
|
-
case 'mode':
|
|
649
|
-
toggleInstallMode()
|
|
650
|
-
break
|
|
651
|
-
case 'build':
|
|
652
|
-
stopSpinner()
|
|
653
|
-
setStatus('Running pnpm run generate…')
|
|
654
|
-
screen.render()
|
|
655
|
-
try {
|
|
656
|
-
execSync('pnpm run generate', { cwd: workspaceRoot, stdio: 'pipe' })
|
|
657
|
-
setStatus('Generate finished', 'ok')
|
|
658
|
-
} catch (err) {
|
|
659
|
-
setStatus(`Generate failed: ${err.message}`, 'error')
|
|
660
|
-
} finally {
|
|
661
|
-
refreshTerminalUi()
|
|
662
|
-
}
|
|
663
|
-
break
|
|
664
|
-
case 'quit':
|
|
665
|
-
process.exit(0)
|
|
666
|
-
break
|
|
667
|
-
default:
|
|
668
|
-
break
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
function saveSettingsFromOverlay() {
|
|
673
|
-
const user = githubInput.getValue().trim() || null
|
|
674
|
-
settings = { ...settings, githubUser: user }
|
|
675
|
-
saveSettings(workspaceRoot, settings)
|
|
676
|
-
closeSettings()
|
|
677
|
-
renderModeHeader()
|
|
678
|
-
renderCatalogList()
|
|
679
|
-
renderDetail()
|
|
680
|
-
setStatus(user ? `GitHub user → ${user}` : 'GitHub user cleared', 'ok')
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function formatThemeInfo(meta) {
|
|
684
|
-
const name = meta.name?.replace('@owdproject/', '') ?? '—'
|
|
685
|
-
const authorShort = String(meta.author ?? '—')
|
|
686
|
-
.replace(/\s*·\s*https?:\/\/\S+/g, '')
|
|
687
|
-
.slice(0, 48)
|
|
688
|
-
const gitShort = meta.remote
|
|
689
|
-
? meta.remote
|
|
690
|
-
.replace(/^git\+/, '')
|
|
691
|
-
.replace(/^https?:\/\/github\.com\//, '')
|
|
692
|
-
.replace(/\.git$/, '')
|
|
693
|
-
: null
|
|
694
|
-
const desc = meta.description
|
|
695
|
-
? meta.description.length > 72
|
|
696
|
-
? `${meta.description.slice(0, 72)}…`
|
|
697
|
-
: meta.description
|
|
698
|
-
: null
|
|
699
|
-
|
|
700
|
-
return [
|
|
701
|
-
`{${C.borderDim}-fg}{underline} ── selected ── {/}`,
|
|
702
|
-
`{bold}{${C.accent}-fg}${name}{/}{/} {${C.muted}-fg}v${meta.version} · ${meta.license}{/}`,
|
|
703
|
-
desc ? `{${C.muted}-fg}${desc}{/}` : null,
|
|
704
|
-
gitShort
|
|
705
|
-
? `{${C.muted}-fg}${authorShort}{/} · {${C.accent}-fg}${gitShort}{/}`
|
|
706
|
-
: `{${C.muted}-fg}${authorShort}{/}`,
|
|
707
|
-
]
|
|
708
|
-
.filter(Boolean)
|
|
709
|
-
.join('\n')
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function renderTabs() {
|
|
713
|
-
const tabs = ['app', 'module'].map((key, i) => {
|
|
714
|
-
const active = activeTab === key
|
|
715
|
-
const label = KINDS[key].label
|
|
716
|
-
return active
|
|
717
|
-
? `{bold}{${C.accent}-fg} ${i + 1}:${label} {/}{/}`
|
|
718
|
-
: `{${C.muted}-fg} ${i + 1}:${label}{/}`
|
|
719
|
-
})
|
|
720
|
-
tabBar.setContent(` ${tabs.join(' ')} {${C.muted}-fg}│ {bold}3{/} or {bold}Tab{/} → theme (left){/}`)
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function renderClient() {
|
|
724
|
-
const http = clientStatus.http
|
|
725
|
-
let stateLabel
|
|
726
|
-
let dotChar
|
|
727
|
-
let dotColor
|
|
728
|
-
|
|
729
|
-
if (devPhase === 'starting') {
|
|
730
|
-
stateLabel = 'STARTING…'
|
|
731
|
-
dotChar = SPINNER[spinnerFrame % SPINNER.length]
|
|
732
|
-
dotColor = C.warn
|
|
733
|
-
} else if (clientStatus.running || http.up) {
|
|
734
|
-
stateLabel = 'RUNNING'
|
|
735
|
-
dotChar = pulseOn ? '●' : '◉'
|
|
736
|
-
dotColor = C.accent
|
|
737
|
-
devPhase = 'running'
|
|
738
|
-
} else {
|
|
739
|
-
stateLabel = 'STOPPED'
|
|
740
|
-
dotChar = '○'
|
|
741
|
-
dotColor = C.err
|
|
742
|
-
devPhase = 'stopped'
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
const serverRunning = clientStatus.running || http.up
|
|
746
|
-
const pidLine = clientStatus.pid
|
|
747
|
-
? `PID ${clientStatus.pid} ${clientStatus.stats.memMb} MiB ${clientStatus.stats.threads} thr`
|
|
748
|
-
: serverRunning
|
|
749
|
-
? 'HTTP responding (process not matched)'
|
|
750
|
-
: 'dev server stopped'
|
|
751
|
-
|
|
752
|
-
const warnDev =
|
|
753
|
-
isWorkspaceInstallMode(settings) && !settings.githubUser
|
|
754
|
-
? `\n {${C.warn}-fg}⚠ DEV mode: open settings [g] to set your GitHub username for fork clones{/}`
|
|
755
|
-
: ''
|
|
756
|
-
|
|
757
|
-
const docsInstalled = hasDocsModuleInstalled(config, deps)
|
|
758
|
-
const docsLine = docsInstalled
|
|
759
|
-
? clientStatus.running || http.up
|
|
760
|
-
? ` {${C.muted}-fg}Documentation installed{/} {${C.accent}-fg}[i]{/} open ${docsBasePathFromConfig(config)}`
|
|
761
|
-
: ` {${C.muted}-fg}Documentation installed{/} {${C.muted}-fg}[s] start dev server, then [i] open docs{/}`
|
|
762
|
-
: ''
|
|
763
|
-
|
|
764
|
-
const devTargetLine = playgroundActive && playgroundLabel
|
|
765
|
-
? ` {${C.accent}-fg}Dev target:{/} {bold}${playgroundLabel}{/} playground`
|
|
766
|
-
: null
|
|
767
|
-
|
|
768
|
-
clientBox.setContent(
|
|
769
|
-
[
|
|
770
|
-
` {${dotColor}-fg}${dotChar}{/} {bold}${stateLabel}{/} http://127.0.0.1:${settings.devPort} HTTP ${http.status || '—'}`,
|
|
771
|
-
` ${pidLine}`,
|
|
772
|
-
devTargetLine,
|
|
773
|
-
` ${serverKeyHints()}`,
|
|
774
|
-
` {${C.muted}-fg}Theme:{/} {bold}${(pendingTheme ?? config.theme ?? '—').replace('@owdproject/', '')}{/} {${C.muted}-fg}│ [w] save{/}`,
|
|
775
|
-
docsLine,
|
|
776
|
-
warnDev,
|
|
777
|
-
]
|
|
778
|
-
.filter(Boolean)
|
|
779
|
-
.join('\n'),
|
|
780
|
-
)
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
function getDesiredPackages(kind) {
|
|
784
|
-
if (kind === 'theme') {
|
|
785
|
-
return pendingTheme ?? config.theme ? [pendingTheme ?? config.theme] : []
|
|
786
|
-
}
|
|
787
|
-
return filterCatalog(catalog, kind)
|
|
788
|
-
.filter((item) => {
|
|
789
|
-
const pending = pendingPackages.get(item.name)
|
|
790
|
-
if (pending !== undefined) return pending
|
|
791
|
-
return kind === 'app'
|
|
792
|
-
? (config.apps ?? []).includes(item.name)
|
|
793
|
-
: (config.modules ?? []).includes(item.name)
|
|
794
|
-
})
|
|
795
|
-
.map((item) => item.name)
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function enabledCountForTab(kind) {
|
|
799
|
-
return getDesiredPackages(kind).length
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
function renderMetrics() {
|
|
803
|
-
const tabLabel = KINDS[activeTab].label
|
|
804
|
-
const onDesktop = enabledCountForTab(activeTab)
|
|
805
|
-
|
|
806
|
-
const spark = memHistory.length ? formatSparkline(memHistory) : '—'
|
|
807
|
-
const memLabel =
|
|
808
|
-
memHistory.length > 0
|
|
809
|
-
? `${memHistory[memHistory.length - 1]} MiB`
|
|
810
|
-
: clientStatus.stats.memMb
|
|
811
|
-
? `${clientStatus.stats.memMb} MiB`
|
|
812
|
-
: '—'
|
|
813
|
-
|
|
814
|
-
const apps = enabledCountForTab('app')
|
|
815
|
-
const mods = enabledCountForTab('module')
|
|
816
|
-
const themeShort = (pendingTheme ?? config.theme ?? '—').replace('@owdproject/', '')
|
|
817
|
-
|
|
818
|
-
const unsaved = pendingPackages.size
|
|
819
|
-
const unsavedLine =
|
|
820
|
-
unsaved > 0
|
|
821
|
-
? ` {${C.warn}-fg}${unsaved}{/} {${C.muted}-fg}unsaved — [w] save{/}`
|
|
822
|
-
: null
|
|
823
|
-
|
|
824
|
-
metricsBox.setContent(
|
|
825
|
-
[
|
|
826
|
-
` {${C.muted}-fg}Memory{/} {bold}${memLabel}{/}`,
|
|
827
|
-
` {${C.accent}-fg}${spark}{/}`,
|
|
828
|
-
` {${C.muted}-fg}${tabLabel}{/} {bold}${onDesktop}{/} {${C.muted}-fg}on desktop{/}`,
|
|
829
|
-
` {${C.muted}-fg}Stack{/} ${themeShort} · ${apps} apps · ${mods} modules`,
|
|
830
|
-
unsavedLine,
|
|
831
|
-
]
|
|
832
|
-
.filter(Boolean)
|
|
833
|
-
.join('\n'),
|
|
834
|
-
)
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
function restoreThemeListSelection(entries, themeName) {
|
|
838
|
-
if (!themeName || !entries.length) return
|
|
839
|
-
const idx = entries.findIndex((e) => e.name === themeName)
|
|
840
|
-
if (idx < 0) return
|
|
841
|
-
themeList._listInitialized = false
|
|
842
|
-
themeList.select(idx)
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
function renderThemeList() {
|
|
846
|
-
const entries = themeEntries()
|
|
847
|
-
const activeTheme = pendingTheme ?? config.theme
|
|
848
|
-
const items = entries.map((item) => {
|
|
849
|
-
const isActive = activeTheme === item.name
|
|
850
|
-
const bullet = isActive ? `{${C.accent}-fg}●{/}` : '○'
|
|
851
|
-
const tag = item.org === 'workspace' ? `{${C.muted}-fg}[local]{/}` : `{${C.muted}-fg}[${installTag(settings, item)}]{/}`
|
|
852
|
-
return `${bullet} ${item.shortName} ${tag}`
|
|
853
|
-
})
|
|
854
|
-
|
|
855
|
-
themeListSyncing = true
|
|
856
|
-
themeList.setItems(items.length ? items : ['{gray-fg}(no themes in catalog){/}'])
|
|
857
|
-
restoreThemeListSelection(entries, activeTheme)
|
|
858
|
-
themeListSyncing = false
|
|
859
|
-
|
|
860
|
-
const meta = readThemeMeta(workspaceRoot, activeTheme ?? '')
|
|
861
|
-
themeInfo.setContent(formatThemeInfo(meta))
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
function previewThemeAt(index, announce = false) {
|
|
865
|
-
const entries = themeEntries()
|
|
866
|
-
const item = entries[index]
|
|
867
|
-
if (!item) return
|
|
868
|
-
pendingTheme = item.name
|
|
869
|
-
renderThemeList()
|
|
870
|
-
renderClient()
|
|
871
|
-
renderMetrics()
|
|
872
|
-
if (announce) {
|
|
873
|
-
setStatus(`Theme → ${item.shortName} (press [w] to save)`, 'ok')
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function restoreCatalogListSelection(entries, pkgName) {
|
|
878
|
-
if (!pkgName || !entries.length) return
|
|
879
|
-
const idx = entries.findIndex((e) => e.name === pkgName)
|
|
880
|
-
if (idx < 0) return
|
|
881
|
-
catalogList._listInitialized = false
|
|
882
|
-
catalogList.select(idx)
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
function listPageStep(list) {
|
|
886
|
-
return Math.max(1, list.height - list.iheight - 1)
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
function renderCatalogList() {
|
|
890
|
-
const entries = catalogEntries()
|
|
891
|
-
const prevPkg = entries[catalogList.selected]?.name
|
|
892
|
-
const items = entries.map((item) => {
|
|
893
|
-
const pending = pendingPackages.get(item.name)
|
|
894
|
-
const on = pending !== undefined ? pending : item.installed
|
|
895
|
-
const box = on ? `{${C.accent}-fg}[✓]{/}` : '[ ]'
|
|
896
|
-
const tag = `{${C.muted}-fg}[${installTag(settings, item)}]{/}`
|
|
897
|
-
const desc = (item.description ?? '').slice(0, 36)
|
|
898
|
-
return `${box} {bold}${item.shortName}{/} ${tag} ${desc}`
|
|
899
|
-
})
|
|
900
|
-
catalogList.setItems(items.length ? items : ['{gray-fg}Loading…{/}'])
|
|
901
|
-
restoreCatalogListSelection(entries, prevPkg)
|
|
902
|
-
renderTabs()
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
async function renderDetail() {
|
|
906
|
-
const entries = catalogEntries()
|
|
907
|
-
const idx = catalogList.selected
|
|
908
|
-
const item = entries[idx]
|
|
909
|
-
if (!item) {
|
|
910
|
-
detailBox.setContent(`{${C.muted}-fg}Select a package for install preview{/}`)
|
|
911
|
-
return
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
const plan = await resolveInstallPlan(item.name, settings, workspaceRoot)
|
|
915
|
-
if (plan.error) {
|
|
916
|
-
detailBox.setContent(`{${C.err}-fg}${plan.error}{/}`)
|
|
917
|
-
return
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
const kind = KINDS[activeTab]
|
|
921
|
-
const target = plan.targetDir ?? `${kind.workspaceDir}/${item.shortName}`
|
|
922
|
-
const npmOnly =
|
|
923
|
-
isWorkspaceInstallMode(settings) && item.installed && !item.localSource
|
|
924
|
-
detailBox.setContent(
|
|
925
|
-
[
|
|
926
|
-
`{bold}{${C.accent}-fg}${item.shortName}{/}{/}`,
|
|
927
|
-
`{${C.muted}-fg}Source:{/} ${plan.label ?? plan.mode}`,
|
|
928
|
-
`{${C.muted}-fg}Target:{/} ${target}/`,
|
|
929
|
-
npmOnly
|
|
930
|
-
? `{${C.warn}-fg}[npm] in config — save [w] to clone into workspace{/}`
|
|
931
|
-
: null,
|
|
932
|
-
item.description ? `{${C.muted}-fg}${item.description.slice(0, 120)}{/}` : null,
|
|
933
|
-
]
|
|
934
|
-
.filter(Boolean)
|
|
935
|
-
.join('\n'),
|
|
936
|
-
)
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
function renderAll() {
|
|
940
|
-
renderModeHeader()
|
|
941
|
-
applyFocusStyles()
|
|
942
|
-
renderClient()
|
|
943
|
-
renderMetrics()
|
|
944
|
-
renderThemeList()
|
|
945
|
-
renderCatalogList()
|
|
946
|
-
renderDetail()
|
|
947
|
-
renderHelp('info')
|
|
948
|
-
screen.render()
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
function focusTheme() {
|
|
952
|
-
focusPanel = 'theme'
|
|
953
|
-
themeList.focus()
|
|
954
|
-
applyFocusStyles()
|
|
955
|
-
setStatus('Theme panel — ↑↓ to pick, Enter to apply, Tab for packages')
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
function focusCatalog() {
|
|
959
|
-
focusPanel = 'catalog'
|
|
960
|
-
catalogList.focus()
|
|
961
|
-
applyFocusStyles()
|
|
962
|
-
setStatus(`${KINDS[activeTab].label} — Space toggles, [w] saves when done`)
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
function selectThemeAt(index) {
|
|
966
|
-
previewThemeAt(index, true)
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
function toggleCatalogAt(index) {
|
|
970
|
-
const entries = catalogEntries()
|
|
971
|
-
const item = entries[index]
|
|
972
|
-
if (!item) return
|
|
973
|
-
const pending = pendingPackages.get(item.name)
|
|
974
|
-
const next = pending !== undefined ? !pending : !item.installed
|
|
975
|
-
pendingPackages.set(item.name, next)
|
|
976
|
-
renderCatalogList()
|
|
977
|
-
renderMetrics()
|
|
978
|
-
renderDetail()
|
|
979
|
-
setStatus(`${next ? 'Will install' : 'Will remove'} ${item.shortName}`)
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
async function applyChanges() {
|
|
983
|
-
stopSpinner()
|
|
984
|
-
const packagesToInstall = []
|
|
985
|
-
const packagesToMaterialize = []
|
|
986
|
-
const nextApps = getDesiredPackages('app')
|
|
987
|
-
const nextModules = getDesiredPackages('module').filter(isInstallableDesktopModule)
|
|
988
|
-
const nextTheme = pendingTheme ?? config.theme
|
|
989
|
-
const prevApps = new Set(config.apps ?? [])
|
|
990
|
-
const prevModules = new Set(config.modules ?? [])
|
|
991
|
-
|
|
992
|
-
for (const pkg of nextApps) {
|
|
993
|
-
if (!prevApps.has(pkg)) packagesToInstall.push(pkg)
|
|
994
|
-
}
|
|
995
|
-
for (const pkg of nextModules) {
|
|
996
|
-
if (!prevModules.has(pkg)) packagesToInstall.push(pkg)
|
|
997
|
-
}
|
|
998
|
-
if (nextTheme && nextTheme !== config.theme) packagesToInstall.push(nextTheme)
|
|
999
|
-
|
|
1000
|
-
if (isWorkspaceInstallMode(settings)) {
|
|
1001
|
-
const enabled = [...nextApps, ...nextModules, nextTheme].filter(Boolean)
|
|
1002
|
-
for (const pkg of enabled) {
|
|
1003
|
-
if (packagesToInstall.includes(pkg)) continue
|
|
1004
|
-
if (!hasLocalWorkspaceSource(workspaceRoot, pkg)) {
|
|
1005
|
-
packagesToMaterialize.push(pkg)
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
const totalSteps = packagesToInstall.length + packagesToMaterialize.length + 2
|
|
1011
|
-
let step = 0
|
|
1012
|
-
let didClone = false
|
|
1013
|
-
|
|
1014
|
-
const bump = (label) => {
|
|
1015
|
-
step++
|
|
1016
|
-
saveProgress = { step, total: totalSteps, label }
|
|
1017
|
-
setStatus(label)
|
|
1018
|
-
screen.render()
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
try {
|
|
1022
|
-
bump('Preparing…')
|
|
1023
|
-
|
|
1024
|
-
for (const pkg of packagesToInstall) {
|
|
1025
|
-
bump(`Installing ${shortName(pkg)}…`)
|
|
1026
|
-
await installPackage(pkg, settings, workspaceRoot, { stdio: 'pipe' })
|
|
1027
|
-
if (isWorkspaceInstallMode(settings)) didClone = true
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
for (const pkg of packagesToMaterialize) {
|
|
1031
|
-
const plan = await resolveInstallPlan(pkg, settings, workspaceRoot)
|
|
1032
|
-
if (plan.error) throw new Error(plan.error)
|
|
1033
|
-
const from = plan.label ?? plan.source?.label ?? shortName(pkg)
|
|
1034
|
-
bump(`Cloning ${shortName(pkg)} from ${from}…`)
|
|
1035
|
-
await materializeToWorkspace(pkg, settings, workspaceRoot, { stdio: 'pipe' })
|
|
1036
|
-
didClone = true
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
if (isWorkspaceInstallMode(settings) && didClone) {
|
|
1040
|
-
bump('prepare:modules…')
|
|
1041
|
-
runPrepareModules(workspaceRoot, 'pipe')
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
bump('Writing config…')
|
|
1045
|
-
writeDesktopConfig(resolveConfigPathForWrite(paths), workspaceRoot, {
|
|
1046
|
-
theme: nextTheme,
|
|
1047
|
-
apps: nextApps,
|
|
1048
|
-
modules: nextModules,
|
|
1049
|
-
})
|
|
1050
|
-
|
|
1051
|
-
for (const pkg of config.apps ?? []) {
|
|
1052
|
-
if (!nextApps.includes(pkg)) {
|
|
1053
|
-
execSync(`pnpm remove ${pkg}`, { cwd: paths.desktop, stdio: 'pipe' })
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
for (const pkg of config.modules ?? []) {
|
|
1057
|
-
if (!nextModules.includes(pkg)) {
|
|
1058
|
-
execSync(`pnpm remove ${pkg}`, { cwd: paths.desktop, stdio: 'pipe' })
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
pendingPackages.clear()
|
|
1063
|
-
paths.config = resolveConfigPathForWrite(paths)
|
|
1064
|
-
config = readDesktopConfig(paths.config, workspaceRoot)
|
|
1065
|
-
deps = readDesktopDependencies(paths.packageJson)
|
|
1066
|
-
pendingTheme = config.theme
|
|
1067
|
-
saveProgress = null
|
|
1068
|
-
renderAll()
|
|
1069
|
-
setStatus('Saved successfully', 'ok')
|
|
1070
|
-
} catch (err) {
|
|
1071
|
-
saveProgress = null
|
|
1072
|
-
setStatus(`Save failed: ${err.message}`, 'error')
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
async function startDevServer() {
|
|
1077
|
-
if (devPhase === 'starting') {
|
|
1078
|
-
setStatus('Dev server is already starting…')
|
|
1079
|
-
return
|
|
1080
|
-
}
|
|
1081
|
-
if (isDevServerUp()) {
|
|
1082
|
-
setStatus('Dev server already running — [m] menu to stop or reboot', 'info')
|
|
1083
|
-
return
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
devPhase = 'starting'
|
|
1087
|
-
startSpinner('Starting dev server…')
|
|
1088
|
-
renderAll()
|
|
1089
|
-
startDev(devTarget)
|
|
1090
|
-
clientStatus = await waitForDev(workspaceRoot, settings.devPort)
|
|
1091
|
-
stopSpinner()
|
|
1092
|
-
devPhase = clientStatus.http.up ? 'running' : 'stopped'
|
|
1093
|
-
renderAll()
|
|
1094
|
-
const targetHint = playgroundActive && playgroundLabel ? ` (${playgroundLabel})` : ''
|
|
1095
|
-
setStatus(
|
|
1096
|
-
clientStatus.http.up
|
|
1097
|
-
? `Dev server up on port ${settings.devPort}${targetHint} — [m] menu to stop or reboot`
|
|
1098
|
-
: `Dev not responding — check ${devLogPath(workspaceRoot)}`,
|
|
1099
|
-
clientStatus.http.up ? 'ok' : 'error',
|
|
1100
|
-
)
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
async function stopDevServer() {
|
|
1104
|
-
if (devPhase === 'starting') {
|
|
1105
|
-
setStatus('Dev server is still starting…')
|
|
1106
|
-
return
|
|
1107
|
-
}
|
|
1108
|
-
if (!isDevServerUp()) {
|
|
1109
|
-
setStatus('Dev server not running — press [s] to start', 'info')
|
|
1110
|
-
return
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
stopDev(workspaceRoot, clientStatus.pid)
|
|
1114
|
-
devPhase = 'stopped'
|
|
1115
|
-
memHistory.length = 0
|
|
1116
|
-
clientStatus = await getClientStatus(workspaceRoot, settings.devPort)
|
|
1117
|
-
renderAll()
|
|
1118
|
-
setStatus('Dev server stopped — press [s] to start', 'ok')
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
async function rebootDevServer() {
|
|
1122
|
-
if (devPhase === 'starting') {
|
|
1123
|
-
setStatus('Cannot reboot while dev server is starting…')
|
|
1124
|
-
return
|
|
1125
|
-
}
|
|
1126
|
-
if (!isDevServerUp()) {
|
|
1127
|
-
setStatus('Dev server not running — press [s] to start', 'info')
|
|
1128
|
-
return
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
devPhase = 'starting'
|
|
1132
|
-
startSpinner('Rebooting dev server…')
|
|
1133
|
-
renderAll()
|
|
1134
|
-
stopDev(workspaceRoot, clientStatus.pid)
|
|
1135
|
-
memHistory.length = 0
|
|
1136
|
-
await new Promise((r) => setTimeout(r, 800))
|
|
1137
|
-
startDev(devTarget)
|
|
1138
|
-
clientStatus = await waitForDev(workspaceRoot, settings.devPort)
|
|
1139
|
-
stopSpinner()
|
|
1140
|
-
devPhase = clientStatus.http.up ? 'running' : 'stopped'
|
|
1141
|
-
renderAll()
|
|
1142
|
-
const targetHint = playgroundActive && playgroundLabel ? ` (${playgroundLabel})` : ''
|
|
1143
|
-
setStatus(
|
|
1144
|
-
clientStatus.http.up
|
|
1145
|
-
? `Dev server rebooted on port ${settings.devPort}${targetHint}`
|
|
1146
|
-
: `Reboot failed — check ${devLogPath(workspaceRoot)}`,
|
|
1147
|
-
clientStatus.http.up ? 'ok' : 'error',
|
|
1148
|
-
)
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
async function refreshCatalog() {
|
|
1152
|
-
startSpinner('Fetching GitHub catalog…')
|
|
1153
|
-
try {
|
|
1154
|
-
const result = await loadCatalog(workspaceRoot, settings)
|
|
1155
|
-
catalog = result.entries
|
|
1156
|
-
catalogCacheAge = result.cacheAge ?? ''
|
|
1157
|
-
config = readDesktopConfig(paths.config, workspaceRoot)
|
|
1158
|
-
deps = readDesktopDependencies(paths.packageJson)
|
|
1159
|
-
pendingTheme = pendingTheme ?? config.theme
|
|
1160
|
-
stopSpinner()
|
|
1161
|
-
renderAll()
|
|
1162
|
-
setStatus(`Package list: ${catalog.length} from GitHub`, 'ok')
|
|
1163
|
-
} catch (err) {
|
|
1164
|
-
stopSpinner()
|
|
1165
|
-
setStatus(`Package list error: ${err.message}`, 'error')
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
screen.key(['escape'], () => {
|
|
1170
|
-
if (settingsOpen) {
|
|
1171
|
-
closeSettings()
|
|
1172
|
-
return
|
|
1173
|
-
}
|
|
1174
|
-
if (menuOpen) {
|
|
1175
|
-
closeMenu()
|
|
1176
|
-
}
|
|
1177
|
-
})
|
|
1178
|
-
|
|
1179
|
-
screen.key(['q', 'C-c'], () => {
|
|
1180
|
-
if (settingsOpen) {
|
|
1181
|
-
closeSettings()
|
|
1182
|
-
return
|
|
1183
|
-
}
|
|
1184
|
-
if (menuOpen) {
|
|
1185
|
-
closeMenu()
|
|
1186
|
-
return
|
|
1187
|
-
}
|
|
1188
|
-
process.exit(0)
|
|
1189
|
-
})
|
|
1190
|
-
|
|
1191
|
-
screen.key(['tab'], () => {
|
|
1192
|
-
if (overlayBlocksKeys()) return
|
|
1193
|
-
if (focusPanel === 'catalog') focusTheme()
|
|
1194
|
-
else focusCatalog()
|
|
1195
|
-
})
|
|
1196
|
-
screen.key(['S-tab'], () => {
|
|
1197
|
-
if (!overlayBlocksKeys()) focusCatalog()
|
|
1198
|
-
})
|
|
1199
|
-
|
|
1200
|
-
screen.key(['m', 'M'], () => {
|
|
1201
|
-
if (settingsOpen) return
|
|
1202
|
-
if (menuOpen) closeMenu()
|
|
1203
|
-
else openMenu()
|
|
1204
|
-
})
|
|
1205
|
-
|
|
1206
|
-
screen.key(['d', 'D'], () => {
|
|
1207
|
-
if (!overlayBlocksKeys()) toggleInstallMode()
|
|
1208
|
-
})
|
|
1209
|
-
screen.key(['s', 'S'], () => {
|
|
1210
|
-
if (!overlayBlocksKeys()) {
|
|
1211
|
-
if (isDevServerUp()) stopDevServer()
|
|
1212
|
-
else startDevServer()
|
|
1213
|
-
}
|
|
1214
|
-
})
|
|
1215
|
-
screen.key(['x', 'X'], () => {
|
|
1216
|
-
if (!overlayBlocksKeys()) stopDevServer()
|
|
1217
|
-
})
|
|
1218
|
-
screen.key(['R'], () => {
|
|
1219
|
-
if (!overlayBlocksKeys()) rebootDevServer()
|
|
1220
|
-
})
|
|
1221
|
-
screen.key(['g'], () => {
|
|
1222
|
-
if (!overlayBlocksKeys()) openSettings()
|
|
1223
|
-
})
|
|
1224
|
-
|
|
1225
|
-
function openDocsInBrowser() {
|
|
1226
|
-
if (!hasDocsModuleInstalled(config, deps)) {
|
|
1227
|
-
setStatus('Install @owdproject/module-docs first', 'error')
|
|
1228
|
-
return
|
|
1229
|
-
}
|
|
1230
|
-
const http = clientStatus.http
|
|
1231
|
-
if (!clientStatus.running && !http.up) {
|
|
1232
|
-
setStatus('Start dev server [s] before opening docs', 'error')
|
|
1233
|
-
return
|
|
1234
|
-
}
|
|
1235
|
-
const base = docsBasePathFromConfig(config)
|
|
1236
|
-
const url = `http://127.0.0.1:${settings.devPort}${base}`
|
|
1237
|
-
const platform = process.platform
|
|
1238
|
-
try {
|
|
1239
|
-
if (platform === 'darwin') {
|
|
1240
|
-
execSync('open', [url])
|
|
1241
|
-
} else if (platform === 'win32') {
|
|
1242
|
-
execSync('cmd', ['/c', 'start', '', url])
|
|
1243
|
-
} else {
|
|
1244
|
-
execSync('xdg-open', [url])
|
|
1245
|
-
}
|
|
1246
|
-
setStatus(`Opened ${url}`, 'ok')
|
|
1247
|
-
} catch (err) {
|
|
1248
|
-
setStatus(`Could not open browser: ${err.message}`, 'error')
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
screen.key(['i', 'I'], () => {
|
|
1253
|
-
if (!overlayBlocksKeys()) openDocsInBrowser()
|
|
1254
|
-
})
|
|
1255
|
-
screen.key(['b'], () => {
|
|
1256
|
-
if (!overlayBlocksKeys()) {
|
|
1257
|
-
stopSpinner()
|
|
1258
|
-
setStatus('Running pnpm run generate…')
|
|
1259
|
-
screen.render()
|
|
1260
|
-
try {
|
|
1261
|
-
execSync('pnpm run generate', { cwd: workspaceRoot, stdio: 'pipe' })
|
|
1262
|
-
setStatus('Generate finished', 'ok')
|
|
1263
|
-
} catch (err) {
|
|
1264
|
-
setStatus(`Generate failed: ${err.message}`, 'error')
|
|
1265
|
-
} finally {
|
|
1266
|
-
refreshTerminalUi()
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
})
|
|
1270
|
-
screen.key(['w', 'W'], () => {
|
|
1271
|
-
if (!overlayBlocksKeys()) applyChanges()
|
|
1272
|
-
})
|
|
1273
|
-
screen.key(['r'], () => {
|
|
1274
|
-
if (!overlayBlocksKeys()) refreshCatalog()
|
|
1275
|
-
})
|
|
1276
|
-
|
|
1277
|
-
screen.key(['1'], () => {
|
|
1278
|
-
if (overlayBlocksKeys()) return
|
|
1279
|
-
activeTab = 'app'
|
|
1280
|
-
focusCatalog()
|
|
1281
|
-
renderAll()
|
|
1282
|
-
})
|
|
1283
|
-
screen.key(['2'], () => {
|
|
1284
|
-
if (overlayBlocksKeys()) return
|
|
1285
|
-
activeTab = 'module'
|
|
1286
|
-
focusCatalog()
|
|
1287
|
-
renderAll()
|
|
1288
|
-
})
|
|
1289
|
-
screen.key(['3', 't'], () => {
|
|
1290
|
-
if (overlayBlocksKeys()) return
|
|
1291
|
-
focusTheme()
|
|
1292
|
-
renderAll()
|
|
1293
|
-
})
|
|
1294
|
-
|
|
1295
|
-
githubInput.key(['enter'], () => saveSettingsFromOverlay())
|
|
1296
|
-
githubInput.key(['escape'], () => closeSettings())
|
|
1297
|
-
|
|
1298
|
-
menuList.on('select', (_item, index) => {
|
|
1299
|
-
const entry = MENU_ITEMS[index]
|
|
1300
|
-
if (entry) runMenuAction(entry.id)
|
|
1301
|
-
})
|
|
1302
|
-
menuList.key(['escape'], () => closeMenu())
|
|
1303
|
-
|
|
1304
|
-
themeList.on('select item', (_item, index) => {
|
|
1305
|
-
if (themeListSyncing) return
|
|
1306
|
-
previewThemeAt(index)
|
|
1307
|
-
})
|
|
1308
|
-
themeList.on('select', (_item, index) => {
|
|
1309
|
-
if (themeListSyncing) return
|
|
1310
|
-
selectThemeAt(index)
|
|
1311
|
-
})
|
|
1312
|
-
themeList.key(['space'], function () {
|
|
1313
|
-
selectThemeAt(this.selected)
|
|
1314
|
-
})
|
|
1315
|
-
themeList.key(['pageup'], function () {
|
|
1316
|
-
this.up(listPageStep(this))
|
|
1317
|
-
this.screen.render()
|
|
1318
|
-
})
|
|
1319
|
-
themeList.key(['pagedown'], function () {
|
|
1320
|
-
this.down(listPageStep(this))
|
|
1321
|
-
this.screen.render()
|
|
1322
|
-
})
|
|
1323
|
-
|
|
1324
|
-
catalogList.on('select item', () => {
|
|
1325
|
-
renderDetail()
|
|
1326
|
-
})
|
|
1327
|
-
catalogList.on('select', (_item, index) => toggleCatalogAt(index))
|
|
1328
|
-
catalogList.key(['space'], function () {
|
|
1329
|
-
toggleCatalogAt(this.selected)
|
|
1330
|
-
})
|
|
1331
|
-
catalogList.key(['pageup'], function () {
|
|
1332
|
-
this.up(listPageStep(this))
|
|
1333
|
-
renderDetail()
|
|
1334
|
-
this.screen.render()
|
|
1335
|
-
})
|
|
1336
|
-
catalogList.key(['pagedown'], function () {
|
|
1337
|
-
this.down(listPageStep(this))
|
|
1338
|
-
renderDetail()
|
|
1339
|
-
this.screen.render()
|
|
1340
|
-
})
|
|
1341
|
-
|
|
1342
|
-
await refreshCatalog()
|
|
1343
|
-
clientStatus = await getClientStatus(workspaceRoot, settings.devPort)
|
|
1344
|
-
devPhase = clientStatus.running ? 'running' : 'stopped'
|
|
1345
|
-
focusCatalog()
|
|
1346
|
-
renderAll()
|
|
1347
|
-
|
|
1348
|
-
const serverHint = ' Press [s] start/stop · [m] menu.'
|
|
1349
|
-
|
|
1350
|
-
if (!isWorkspaceInstallMode(settings)) {
|
|
1351
|
-
setStatus(
|
|
1352
|
-
`USER mode: save installs from npm. Press [d] to switch to DEV (git clone).${serverHint}`,
|
|
1353
|
-
'info',
|
|
1354
|
-
)
|
|
1355
|
-
} else if (!settings.githubUser) {
|
|
1356
|
-
setStatus(
|
|
1357
|
-
`DEV mode: save clones from github.com/owdproject. Press [g] for your GitHub username.${serverHint}`,
|
|
1358
|
-
'ok',
|
|
1359
|
-
)
|
|
1360
|
-
} else if (serverHint) {
|
|
1361
|
-
setStatus(serverHint.trim(), 'info')
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
setInterval(async () => {
|
|
1365
|
-
pulseOn = !pulseOn
|
|
1366
|
-
if (devPhase !== 'starting' && !settingsOpen) {
|
|
1367
|
-
clientStatus = await getClientStatus(workspaceRoot, settings.devPort)
|
|
1368
|
-
if (clientStatus.stats.memMb > 0) {
|
|
1369
|
-
memHistory.push(clientStatus.stats.memMb)
|
|
1370
|
-
if (memHistory.length > MEM_HISTORY_LEN) memHistory.shift()
|
|
1371
|
-
}
|
|
1372
|
-
if (clientStatus.http.up) devPhase = 'running'
|
|
1373
|
-
else if (devPhase === 'running') devPhase = 'stopped'
|
|
1374
|
-
}
|
|
1375
|
-
renderClient()
|
|
1376
|
-
renderMetrics()
|
|
1377
|
-
screen.render()
|
|
1378
|
-
}, 2000)
|
|
1379
|
-
}
|