@crab-dev/wake 0.1.19 → 0.1.21
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/CHANGELOG.md +24 -0
- package/README.md +9 -0
- package/bin/console.mjs +247 -0
- package/bin/terminal.mjs +298 -43
- package/bin/wake.mjs +7 -1
- package/index.cjs +3 -5
- package/index.d.ts +39 -0
- package/package.json +11 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.21
|
|
4
|
+
|
|
5
|
+
- Added aggregate Wake Docs workspaces with isolated production bundles, deterministic manifests,
|
|
6
|
+
transactional output commits, one-port lazy/eager development mounts, scoped HMR events, and
|
|
7
|
+
embedded or standalone workbench presentation.
|
|
8
|
+
- Fixed MDX JSX structure generation by lifting block JSX and splitting mixed paragraphs at AST
|
|
9
|
+
boundaries, preventing invalid paragraph/block nesting without changing inline JSX or component
|
|
10
|
+
Markdown children.
|
|
11
|
+
- Fixed constrained, defaulted, and comma-disambiguated generic arrow functions in TSX so Wake
|
|
12
|
+
parses them as TypeScript instead of emitting cascading JSX diagnostics.
|
|
13
|
+
- Added a TypeScript 7 compatibility gate covering strict type checking, TS/TSX erasure, module
|
|
14
|
+
extensions, value transforms, and production/development JSX runtime execution.
|
|
15
|
+
- Fixed top-level TypeScript overload signatures so only their implementation is emitted, and
|
|
16
|
+
removed false runtime dependencies from imports and exports containing only inline `type`
|
|
17
|
+
specifiers.
|
|
18
|
+
|
|
19
|
+
## 0.1.20
|
|
20
|
+
|
|
21
|
+
- Added a modern interactive terminal console with editable commands, history, Unicode-aware selection, clipboard copy and paste, and safe development-server opening across Rust and npm CLIs.
|
|
22
|
+
- Added source-located compiler diagnostics with numbered code frames across Rust and npm build, bundle, watch, and development terminals.
|
|
23
|
+
- Added binding-aware Crab CSS semantic values, scoped automatic property and value completions, and deterministic suggestion ranking in the VS Code language service.
|
|
24
|
+
- Made Wake Docs accent colors optional and confined accent-derived component tokens to Demo previews without rewriting global workbench semantics.
|
|
25
|
+
- Preserved side effects inside `void` expressions during minification so editor and application commands cannot be deleted.
|
|
26
|
+
|
|
3
27
|
## 0.1.19
|
|
4
28
|
|
|
5
29
|
- Fixed preserve-module CommonJS output so every generated file defines the default and namespace interop helpers it uses.
|
package/README.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
Wake is a Rust-native web builder exposed as both a Node.js library and the
|
|
4
4
|
`wake` command.
|
|
5
5
|
|
|
6
|
+
Interactive `dev` and `docs dev` sessions use the full-screen TUI when stdin
|
|
7
|
+
and stderr are terminals. The command line accepts `help`, `clear`, `open`,
|
|
8
|
+
and `quit` (with optional `/` prefixes), keeps in-memory history, supports
|
|
9
|
+
paste, and copies mouse-selected screen text to the clipboard. Use
|
|
10
|
+
`--ui plain` for stable non-interactive logs; Ctrl-C always interrupts.
|
|
11
|
+
Compiler failures include `path:line:column`, a numbered source line, and a caret when the diagnostic
|
|
12
|
+
has a valid source span. The same structured location is available from Node build errors and server
|
|
13
|
+
`diagnostic` events.
|
|
14
|
+
|
|
6
15
|
```sh
|
|
7
16
|
npm install --save-dev @crab-dev/wake
|
|
8
17
|
npx wake build
|
package/bin/console.mjs
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { StringDecoder } from 'node:string_decoder'
|
|
2
|
+
import stringWidth from 'string-width'
|
|
3
|
+
|
|
4
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
5
|
+
const MAX_INPUT = 4096
|
|
6
|
+
const MAX_HISTORY = 50
|
|
7
|
+
|
|
8
|
+
export function graphemes(value) {
|
|
9
|
+
return [...segmenter.segment(String(value))].map((part) => part.segment)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function parseConsoleCommand(value) {
|
|
13
|
+
const original = String(value).trim()
|
|
14
|
+
if (!original) throw new Error('Enter a command. Type help to list available commands.')
|
|
15
|
+
const normalized = (original.startsWith('/') ? original.slice(1) : original).toLowerCase()
|
|
16
|
+
if (normalized === 'help') return 'help'
|
|
17
|
+
if (normalized === 'clear') return 'clear'
|
|
18
|
+
if (normalized === 'open') return 'open'
|
|
19
|
+
if (normalized === 'quit' || normalized === 'q') return 'quit'
|
|
20
|
+
throw new Error(`Unknown command: ${original}. Type help for available commands.`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class InputEditor {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.value = ''
|
|
26
|
+
this.cursor = 0
|
|
27
|
+
this.history = []
|
|
28
|
+
this.historyIndex = undefined
|
|
29
|
+
this.draft = ''
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
clear() {
|
|
33
|
+
this.value = ''
|
|
34
|
+
this.cursor = 0
|
|
35
|
+
this.historyIndex = undefined
|
|
36
|
+
this.draft = ''
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
insert(value) {
|
|
40
|
+
const current = graphemes(this.value)
|
|
41
|
+
const addition = graphemes(value).slice(0, Math.max(0, MAX_INPUT - current.length))
|
|
42
|
+
current.splice(this.cursor, 0, ...addition)
|
|
43
|
+
this.value = current.join('')
|
|
44
|
+
this.cursor += addition.length
|
|
45
|
+
this.historyIndex = undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
insertPaste(value) {
|
|
49
|
+
this.insert(String(value).replace(/[\r\n\t\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, ' '))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
moveLeft() { this.cursor = Math.max(0, this.cursor - 1) }
|
|
53
|
+
moveRight() { this.cursor = Math.min(graphemes(this.value).length, this.cursor + 1) }
|
|
54
|
+
moveHome() { this.cursor = 0 }
|
|
55
|
+
moveEnd() { this.cursor = graphemes(this.value).length }
|
|
56
|
+
|
|
57
|
+
backspace() {
|
|
58
|
+
if (this.cursor === 0) return
|
|
59
|
+
const values = graphemes(this.value)
|
|
60
|
+
values.splice(this.cursor - 1, 1)
|
|
61
|
+
this.value = values.join('')
|
|
62
|
+
this.cursor -= 1
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
delete() {
|
|
66
|
+
const values = graphemes(this.value)
|
|
67
|
+
if (this.cursor >= values.length) return
|
|
68
|
+
values.splice(this.cursor, 1)
|
|
69
|
+
this.value = values.join('')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
historyPrevious() {
|
|
73
|
+
if (this.history.length === 0) return
|
|
74
|
+
if (this.historyIndex === undefined) {
|
|
75
|
+
this.draft = this.value
|
|
76
|
+
this.historyIndex = this.history.length - 1
|
|
77
|
+
} else {
|
|
78
|
+
this.historyIndex = Math.max(0, this.historyIndex - 1)
|
|
79
|
+
}
|
|
80
|
+
this.value = this.history[this.historyIndex]
|
|
81
|
+
this.moveEnd()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
historyNext() {
|
|
85
|
+
if (this.historyIndex === undefined) return
|
|
86
|
+
if (this.historyIndex + 1 < this.history.length) {
|
|
87
|
+
this.historyIndex += 1
|
|
88
|
+
this.value = this.history[this.historyIndex]
|
|
89
|
+
} else {
|
|
90
|
+
this.historyIndex = undefined
|
|
91
|
+
this.value = this.draft
|
|
92
|
+
this.draft = ''
|
|
93
|
+
}
|
|
94
|
+
this.moveEnd()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
submit() {
|
|
98
|
+
const submitted = this.value.trim()
|
|
99
|
+
let result
|
|
100
|
+
try {
|
|
101
|
+
result = { command: parseConsoleCommand(submitted) }
|
|
102
|
+
} catch (error) {
|
|
103
|
+
result = { error: error.message }
|
|
104
|
+
}
|
|
105
|
+
if (submitted && this.history.at(-1) !== submitted) {
|
|
106
|
+
this.history.push(submitted)
|
|
107
|
+
if (this.history.length > MAX_HISTORY) this.history.shift()
|
|
108
|
+
}
|
|
109
|
+
this.clear()
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
cursorCell() {
|
|
114
|
+
return graphemes(this.value).slice(0, this.cursor).reduce((sum, value) => sum + stringWidth(value), 0)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setCursorFromCell(cell) {
|
|
118
|
+
let width = 0
|
|
119
|
+
let cursor = 0
|
|
120
|
+
for (const value of graphemes(this.value)) {
|
|
121
|
+
const next = width + stringWidth(value)
|
|
122
|
+
if (cell < next) break
|
|
123
|
+
width = next
|
|
124
|
+
cursor += 1
|
|
125
|
+
}
|
|
126
|
+
this.cursor = cursor
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
visible(width) {
|
|
130
|
+
if (width <= 0) return { text: '', cursor: 0 }
|
|
131
|
+
const cursor = this.cursorCell()
|
|
132
|
+
const start = cursor >= width ? cursor + 1 - width : 0
|
|
133
|
+
let position = 0
|
|
134
|
+
let text = ''
|
|
135
|
+
for (const value of graphemes(this.value)) {
|
|
136
|
+
const next = position + stringWidth(value)
|
|
137
|
+
if (next > start && position < start + width) text += value
|
|
138
|
+
position = next
|
|
139
|
+
if (position >= start + width) break
|
|
140
|
+
}
|
|
141
|
+
return { text, cursor: Math.min(width, Math.max(0, cursor - start)) }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function lineToCells(value, width) {
|
|
146
|
+
const cells = []
|
|
147
|
+
for (const valuePart of graphemes(value)) {
|
|
148
|
+
const cellWidth = Math.max(0, stringWidth(valuePart))
|
|
149
|
+
if (cells.length + cellWidth > width) break
|
|
150
|
+
cells.push(valuePart)
|
|
151
|
+
for (let index = 1; index < cellWidth; index += 1) cells.push('')
|
|
152
|
+
}
|
|
153
|
+
while (cells.length < width) cells.push(' ')
|
|
154
|
+
return cells.slice(0, width)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function orderedSelection(selection) {
|
|
158
|
+
const forward = selection.start.y < selection.end.y
|
|
159
|
+
|| (selection.start.y === selection.end.y && selection.start.x <= selection.end.x)
|
|
160
|
+
return forward ? [selection.start, selection.end] : [selection.end, selection.start]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function selectionContains(selection, x, y) {
|
|
164
|
+
if (!selection) return false
|
|
165
|
+
const [start, end] = orderedSelection(selection)
|
|
166
|
+
if (y < start.y || y > end.y) return false
|
|
167
|
+
return (y !== start.y || x >= start.x) && (y !== end.y || x <= end.x)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function extractSelection(rows, selection) {
|
|
171
|
+
if (!selection || (selection.start.x === selection.end.x && selection.start.y === selection.end.y)) return ''
|
|
172
|
+
const [start, end] = orderedSelection(selection)
|
|
173
|
+
const lines = []
|
|
174
|
+
for (let y = start.y; y <= Math.min(end.y, rows.length - 1); y += 1) {
|
|
175
|
+
const row = rows[y] || []
|
|
176
|
+
const from = y === start.y ? start.x : 0
|
|
177
|
+
const to = y === end.y ? Math.min(end.x, row.length - 1) : row.length - 1
|
|
178
|
+
lines.push(row.slice(from, to + 1).join('').replace(/ +$/u, ''))
|
|
179
|
+
}
|
|
180
|
+
return lines.join('\n').replace(/\n+$/u, '')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const KEY_SEQUENCES = new Map([
|
|
184
|
+
['\x1b[A', 'up'], ['\x1b[B', 'down'], ['\x1b[C', 'right'], ['\x1b[D', 'left'],
|
|
185
|
+
['\x1bOA', 'up'], ['\x1bOB', 'down'], ['\x1bOC', 'right'], ['\x1bOD', 'left'],
|
|
186
|
+
['\x1b[H', 'home'], ['\x1b[F', 'end'], ['\x1b[1~', 'home'], ['\x1b[4~', 'end'],
|
|
187
|
+
['\x1bOH', 'home'], ['\x1bOF', 'end'],
|
|
188
|
+
['\x1b[3~', 'delete'], ['\x1b[5~', 'pageup'], ['\x1b[6~', 'pagedown'],
|
|
189
|
+
['\x1b[1;5F', 'ctrl-end'],
|
|
190
|
+
])
|
|
191
|
+
|
|
192
|
+
export class TerminalInputDecoder {
|
|
193
|
+
constructor() {
|
|
194
|
+
this.decoder = new StringDecoder('utf8')
|
|
195
|
+
this.buffer = ''
|
|
196
|
+
this.paste = false
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
push(chunk) {
|
|
200
|
+
this.buffer += this.decoder.write(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))
|
|
201
|
+
const events = []
|
|
202
|
+
while (this.buffer) {
|
|
203
|
+
if (this.paste) {
|
|
204
|
+
const end = this.buffer.indexOf('\x1b[201~')
|
|
205
|
+
if (end === -1) break
|
|
206
|
+
events.push({ type: 'paste', value: this.buffer.slice(0, end) })
|
|
207
|
+
this.buffer = this.buffer.slice(end + 6)
|
|
208
|
+
this.paste = false
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
if (this.buffer.startsWith('\x1b[200~')) {
|
|
212
|
+
this.buffer = this.buffer.slice(6)
|
|
213
|
+
this.paste = true
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
const mouse = /^\x1b\[<(\d+);(\d+);(\d+)([mM])/.exec(this.buffer)
|
|
217
|
+
if (mouse) {
|
|
218
|
+
const code = Number(mouse[1])
|
|
219
|
+
const suffix = mouse[4]
|
|
220
|
+
let kind = 'down'
|
|
221
|
+
let button = ['left', 'middle', 'right'][code & 3] || 'left'
|
|
222
|
+
if ((code & 64) !== 0) kind = (code & 1) === 0 ? 'scroll-up' : 'scroll-down'
|
|
223
|
+
else if ((code & 32) !== 0) kind = 'drag'
|
|
224
|
+
else if (suffix === 'm') kind = 'up'
|
|
225
|
+
events.push({ type: 'mouse', kind, button, x: Number(mouse[2]) - 1, y: Number(mouse[3]) - 1 })
|
|
226
|
+
this.buffer = this.buffer.slice(mouse[0].length)
|
|
227
|
+
continue
|
|
228
|
+
}
|
|
229
|
+
const matched = [...KEY_SEQUENCES].find(([sequence]) => this.buffer.startsWith(sequence))
|
|
230
|
+
if (matched) {
|
|
231
|
+
events.push({ type: 'key', key: matched[1] })
|
|
232
|
+
this.buffer = this.buffer.slice(matched[0].length)
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
if (this.buffer.startsWith('\x1b') && [...KEY_SEQUENCES.keys(), '\x1b[200~', '\x1b[<'].some((value) => value.startsWith(this.buffer))) break
|
|
236
|
+
const first = this.buffer.codePointAt(0)
|
|
237
|
+
const value = String.fromCodePoint(first)
|
|
238
|
+
this.buffer = this.buffer.slice(value.length)
|
|
239
|
+
const control = {
|
|
240
|
+
'\u0003': 'ctrl-c', '\u0019': 'ctrl-y', '\u0016': 'ctrl-v', '\r': 'enter', '\n': 'enter',
|
|
241
|
+
'\u007f': 'backspace', '\u0008': 'backspace', '\u001b': 'escape',
|
|
242
|
+
}[value]
|
|
243
|
+
events.push(control ? { type: 'key', key: control } : { type: 'text', value })
|
|
244
|
+
}
|
|
245
|
+
return events
|
|
246
|
+
}
|
|
247
|
+
}
|
package/bin/terminal.mjs
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import clipboard from 'clipboardy'
|
|
2
|
+
import open from 'open'
|
|
3
|
+
import stringWidth from 'string-width'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
InputEditor,
|
|
7
|
+
TerminalInputDecoder,
|
|
8
|
+
extractSelection,
|
|
9
|
+
lineToCells,
|
|
10
|
+
selectionContains,
|
|
11
|
+
} from './console.mjs'
|
|
12
|
+
|
|
1
13
|
const RESET = '\x1b[0m'
|
|
2
14
|
const BOLD = '\x1b[1m'
|
|
3
15
|
const DIM = '\x1b[2m'
|
|
@@ -81,7 +93,7 @@ export function formatBuildResult(ui, result, label = 'Built', extra = '') {
|
|
|
81
93
|
]
|
|
82
94
|
if (result.outputDir) lines.push(` ${ui.dim('Output')} ${ui.accent(result.outputDir)}`)
|
|
83
95
|
for (const diagnostic of result.diagnostics || []) {
|
|
84
|
-
lines.push(
|
|
96
|
+
lines.push(...formatDiagnostic(ui, diagnostic).map((line) => ` ${line}`))
|
|
85
97
|
}
|
|
86
98
|
lines.push('')
|
|
87
99
|
return lines
|
|
@@ -119,16 +131,69 @@ export function formatError(ui, error) {
|
|
|
119
131
|
]
|
|
120
132
|
if (error?.path) lines.push(` ${ui.dim('Path')} ${ui.accent(error.path)}`)
|
|
121
133
|
for (const diagnostic of error?.diagnostics || []) {
|
|
122
|
-
|
|
123
|
-
lines.push(
|
|
124
|
-
` ${ui.warn(String(diagnostic.severity || 'error').toUpperCase())} ${diagnosticCode}${diagnostic.message}`,
|
|
125
|
-
)
|
|
126
|
-
for (const note of diagnostic.notes || []) lines.push(` ${ui.dim('·')} ${note}`)
|
|
134
|
+
lines.push(...formatDiagnostic(ui, diagnostic).map((line) => ` ${line}`))
|
|
127
135
|
}
|
|
128
136
|
lines.push('')
|
|
129
137
|
return lines
|
|
130
138
|
}
|
|
131
139
|
|
|
140
|
+
export function formatDiagnostic(ui, diagnostic) {
|
|
141
|
+
const severity = String(diagnostic?.severity || 'error').toUpperCase()
|
|
142
|
+
const heading = diagnostic?.code
|
|
143
|
+
? `${severity} [${diagnostic.code}]: ${diagnostic.message}`
|
|
144
|
+
: `${severity}: ${diagnostic?.message || ''}`
|
|
145
|
+
const lines = [
|
|
146
|
+
diagnostic?.severity === 'warning'
|
|
147
|
+
? ui.warn(heading)
|
|
148
|
+
: diagnostic?.severity === 'error'
|
|
149
|
+
? ui.error(heading)
|
|
150
|
+
: ui.accent(heading),
|
|
151
|
+
]
|
|
152
|
+
const location = diagnostic?.location
|
|
153
|
+
if (diagnostic?.path && location) {
|
|
154
|
+
lines.push(` ${ui.dim('-->')} ${ui.accent(diagnostic.path)}:${location.line}:${location.column}`)
|
|
155
|
+
} else if (diagnostic?.path) {
|
|
156
|
+
lines.push(` ${ui.dim('-->')} ${ui.accent(diagnostic.path)}`)
|
|
157
|
+
}
|
|
158
|
+
if (location) {
|
|
159
|
+
const lineText = expandTabs(String(location.lineText || ''))
|
|
160
|
+
const gutter = String(location.line).length
|
|
161
|
+
const start = displayColumn(String(location.lineText || ''), location.column)
|
|
162
|
+
const end = location.endLine === location.line
|
|
163
|
+
? displayColumn(String(location.lineText || ''), location.endColumn)
|
|
164
|
+
: stringWidth(lineText)
|
|
165
|
+
const width = Math.max(1, end - start)
|
|
166
|
+
const label = location.label ? ` ${location.label}` : ''
|
|
167
|
+
lines.push(`${' '.repeat(gutter)} ${ui.dim('|')}`)
|
|
168
|
+
lines.push(`${String(location.line).padStart(gutter)} ${ui.dim('|')} ${lineText}`)
|
|
169
|
+
lines.push(`${' '.repeat(gutter)} ${ui.dim('|')} ${ui.error(`${' '.repeat(start)}${'^'.repeat(width)}${label}`)}`)
|
|
170
|
+
}
|
|
171
|
+
for (const note of diagnostic?.notes || []) lines.push(` ${ui.dim('=')} note: ${note}`)
|
|
172
|
+
return lines
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function displayColumn(line, oneBasedColumn) {
|
|
176
|
+
const prefix = [...line].slice(0, Math.max(0, Number(oneBasedColumn || 1) - 1)).join('')
|
|
177
|
+
return stringWidth(expandTabs(prefix))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function expandTabs(line) {
|
|
181
|
+
const tabStop = 4
|
|
182
|
+
let width = 0
|
|
183
|
+
let value = ''
|
|
184
|
+
for (const character of line) {
|
|
185
|
+
if (character === '\t') {
|
|
186
|
+
const spaces = tabStop - (width % tabStop)
|
|
187
|
+
value += ' '.repeat(spaces)
|
|
188
|
+
width += spaces
|
|
189
|
+
} else {
|
|
190
|
+
value += character
|
|
191
|
+
width += stringWidth(character)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return value
|
|
195
|
+
}
|
|
196
|
+
|
|
132
197
|
export function formatFinalSummary(ui, state, reason, label = 'Server stopped') {
|
|
133
198
|
const lines = [
|
|
134
199
|
'',
|
|
@@ -161,8 +226,9 @@ export function observeServer(server, ui, output = console) {
|
|
|
161
226
|
)
|
|
162
227
|
}
|
|
163
228
|
const onDiagnostic = (diagnostic) => {
|
|
164
|
-
const
|
|
165
|
-
|
|
229
|
+
for (const [index, line] of formatDiagnostic(ui, diagnostic).entries()) {
|
|
230
|
+
output.error(index === 0 ? ` ${ui.error('✗')} ${ui.bold(line)}` : ` ${line}`)
|
|
231
|
+
}
|
|
166
232
|
}
|
|
167
233
|
|
|
168
234
|
server.on('rebuildStart', onRebuildStart)
|
|
@@ -201,6 +267,7 @@ export function createDashboardState({
|
|
|
201
267
|
rebuilds: 0,
|
|
202
268
|
startedAt: Date.now(),
|
|
203
269
|
activity: [],
|
|
270
|
+
workspaceState: undefined,
|
|
204
271
|
scrollFromBottom: 0,
|
|
205
272
|
}
|
|
206
273
|
pushActivity(state, 'info', 'Starting Wake…')
|
|
@@ -240,7 +307,18 @@ export function applyDashboardEvent(state, event) {
|
|
|
240
307
|
)
|
|
241
308
|
} else if (event.type === 'diagnostic') {
|
|
242
309
|
state.status = 'error'
|
|
243
|
-
pushActivity(state, 'error', event.
|
|
310
|
+
pushActivity(state, 'error', formatDiagnostic(createUi(false), event.diagnostic).join('\n'))
|
|
311
|
+
} else if (event.type === 'workspaceState') {
|
|
312
|
+
state.workspaceState = {
|
|
313
|
+
total: event.total,
|
|
314
|
+
loaded: event.loaded,
|
|
315
|
+
failed: event.failed,
|
|
316
|
+
current: event.current,
|
|
317
|
+
failedNames: event.failedNames || [],
|
|
318
|
+
}
|
|
319
|
+
if (event.current) {
|
|
320
|
+
pushActivity(state, 'info', `Loading workspace ${event.current}…`)
|
|
321
|
+
}
|
|
244
322
|
} else if (event.type === 'closed') {
|
|
245
323
|
state.status = 'stopped'
|
|
246
324
|
pushActivity(state, 'info', 'Wake stopped')
|
|
@@ -286,14 +364,19 @@ function elapsedStamp(durationMs) {
|
|
|
286
364
|
}
|
|
287
365
|
|
|
288
366
|
function charLength(text) {
|
|
289
|
-
return
|
|
367
|
+
return stringWidth(stripAnsi(String(text)))
|
|
290
368
|
}
|
|
291
369
|
|
|
292
370
|
function truncate(text, width) {
|
|
293
371
|
const chars = [...String(text)]
|
|
294
|
-
if (
|
|
372
|
+
if (charLength(text) <= width) return String(text)
|
|
295
373
|
if (width <= 1) return chars.slice(0, width).join('')
|
|
296
|
-
|
|
374
|
+
let value = ''
|
|
375
|
+
for (const character of chars) {
|
|
376
|
+
if (charLength(value + character) > width - 1) break
|
|
377
|
+
value += character
|
|
378
|
+
}
|
|
379
|
+
return `${value}…`
|
|
297
380
|
}
|
|
298
381
|
|
|
299
382
|
function pad(text, width) {
|
|
@@ -330,15 +413,30 @@ function metricsText(state) {
|
|
|
330
413
|
}
|
|
331
414
|
|
|
332
415
|
function activityRows(state, available) {
|
|
333
|
-
const
|
|
334
|
-
const start = Math.max(0, end - Math.max(1, available))
|
|
335
|
-
return state.activity.slice(start, end).map((item) => {
|
|
416
|
+
const rows = state.activity.flatMap((item) => {
|
|
336
417
|
const symbol = { info: '·', success: '✓', warning: '↻', error: '✗' }[item.level]
|
|
337
|
-
return
|
|
418
|
+
return String(item.message).split('\n').map((line, index) => index === 0
|
|
419
|
+
? `${elapsedStamp(item.elapsedMs)} ${symbol} ${line}`
|
|
420
|
+
: ` ${line}`)
|
|
338
421
|
})
|
|
422
|
+
const end = Math.max(0, rows.length - state.scrollFromBottom)
|
|
423
|
+
const start = Math.max(0, end - Math.max(1, available))
|
|
424
|
+
return rows.slice(start, end)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function workspaceText(state) {
|
|
428
|
+
const workspaces = state.workspaceState
|
|
429
|
+
if (!workspaces) return undefined
|
|
430
|
+
const current = workspaces.current ? ` · loading ${workspaces.current}` : ''
|
|
431
|
+
const failed = workspaces.failed ? ` · ${workspaces.failed} failed` : ''
|
|
432
|
+
return `WORKSPACES ${workspaces.loaded}/${workspaces.total} loaded${failed}${current}`
|
|
339
433
|
}
|
|
340
434
|
|
|
341
|
-
function
|
|
435
|
+
function activityRowCount(state) {
|
|
436
|
+
return state.activity.reduce((count, item) => count + String(item.message).split('\n').length, 0)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function plainFrame(state, width, height, editor = new InputEditor(), notice) {
|
|
342
440
|
width = Math.max(10, width || 80)
|
|
343
441
|
height = Math.max(6, height || 24)
|
|
344
442
|
const runtime = humanRuntime(Date.now() - state.startedAt)
|
|
@@ -348,13 +446,13 @@ function plainFrame(state, width, height) {
|
|
|
348
446
|
let activityHeight
|
|
349
447
|
|
|
350
448
|
if (width < 60 || height < 14) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
boxLine(
|
|
356
|
-
|
|
357
|
-
|
|
449
|
+
const latest = String(state.activity.at(-1)?.message || 'Starting Wake…').split('\n')
|
|
450
|
+
fixed = [topBorder(state, width), boxLine(header, width)]
|
|
451
|
+
const diagnosticRows = latest.length > 1
|
|
452
|
+
for (const line of latest.slice(0, Math.max(1, height - (diagnosticRows ? 5 : 6)))) {
|
|
453
|
+
fixed.push(boxLine(line, width))
|
|
454
|
+
}
|
|
455
|
+
if (!diagnosticRows) fixed.push(boxLine('Resize for details · type help for commands', width))
|
|
358
456
|
activityHeight = 0
|
|
359
457
|
} else if (width < 80 || height < 20) {
|
|
360
458
|
fixed = [
|
|
@@ -364,6 +462,7 @@ function plainFrame(state, width, height) {
|
|
|
364
462
|
boxLine(metricsText(state), width),
|
|
365
463
|
separator(width, 'ACTIVITY'),
|
|
366
464
|
]
|
|
465
|
+
if (workspaceText(state)) fixed.splice(-1, 0, boxLine(workspaceText(state), width))
|
|
367
466
|
activityHeight = Math.max(1, height - fixed.length - 2)
|
|
368
467
|
} else {
|
|
369
468
|
fixed = [
|
|
@@ -377,14 +476,23 @@ function plainFrame(state, width, height) {
|
|
|
377
476
|
boxLine(metricsText(state), width),
|
|
378
477
|
separator(width, 'ACTIVITY'),
|
|
379
478
|
]
|
|
479
|
+
if (workspaceText(state)) fixed.splice(-1, 0, boxLine(workspaceText(state), width))
|
|
380
480
|
activityHeight = Math.max(1, height - fixed.length - 2)
|
|
381
481
|
}
|
|
382
482
|
|
|
383
483
|
for (const row of activityRows(state, activityHeight)) fixed.push(boxLine(row, width))
|
|
384
|
-
while (
|
|
385
|
-
|
|
484
|
+
while (fixed.length < height - 3) fixed.push(boxLine('', width))
|
|
485
|
+
fixed.splice(Math.max(0, height - 3))
|
|
486
|
+
const visible = editor.visible(Math.max(0, width - 6))
|
|
487
|
+
fixed.push(boxLine(
|
|
488
|
+
notice
|
|
489
|
+
? `${notice.error ? '✗' : '✓'} ${notice.message}`
|
|
490
|
+
: 'Enter command · PgUp/PgDn logs · drag to copy · Ctrl-C quit',
|
|
491
|
+
width,
|
|
492
|
+
))
|
|
493
|
+
fixed.push(boxLine(`› ${visible.text}`, width))
|
|
386
494
|
fixed.push(bottomBorder(width))
|
|
387
|
-
return fixed.slice(0, height)
|
|
495
|
+
return { lines: fixed.slice(0, height), cursor: visible.cursor }
|
|
388
496
|
}
|
|
389
497
|
|
|
390
498
|
function colorizeLine(line, ui) {
|
|
@@ -398,16 +506,67 @@ function colorizeLine(line, ui) {
|
|
|
398
506
|
return value
|
|
399
507
|
}
|
|
400
508
|
|
|
509
|
+
function renderDashboardFrame(
|
|
510
|
+
state,
|
|
511
|
+
width = 80,
|
|
512
|
+
height = 24,
|
|
513
|
+
ui = createUi(false),
|
|
514
|
+
editor = new InputEditor(),
|
|
515
|
+
selection,
|
|
516
|
+
notice,
|
|
517
|
+
) {
|
|
518
|
+
const frame = plainFrame(state, width, height, editor, notice)
|
|
519
|
+
const rows = frame.lines.map((line) => lineToCells(line, width))
|
|
520
|
+
const rendered = rows.map((cells, y) => {
|
|
521
|
+
let selected = false
|
|
522
|
+
let line = ''
|
|
523
|
+
for (let x = 0; x < cells.length; x += 1) {
|
|
524
|
+
const nextSelected = selectionContains(selection, x, y)
|
|
525
|
+
if (nextSelected !== selected) {
|
|
526
|
+
line += nextSelected ? '\x1b[7m' : RESET
|
|
527
|
+
selected = nextSelected
|
|
528
|
+
}
|
|
529
|
+
line += cells[x]
|
|
530
|
+
}
|
|
531
|
+
if (selected) line += RESET
|
|
532
|
+
return colorizeLine(line, ui)
|
|
533
|
+
})
|
|
534
|
+
return {
|
|
535
|
+
text: rendered.join('\n'),
|
|
536
|
+
rows,
|
|
537
|
+
inputY: Math.max(0, rows.length - 2),
|
|
538
|
+
inputX: 4,
|
|
539
|
+
cursorX: Math.min(width - 2, 4 + frame.cursor),
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
401
543
|
export function renderDashboard(state, width = 80, height = 24, ui = createUi(false)) {
|
|
402
|
-
return
|
|
544
|
+
return renderDashboardFrame(state, width, height, ui).text
|
|
403
545
|
}
|
|
404
546
|
|
|
405
547
|
export function createDashboardSession(
|
|
406
548
|
state,
|
|
407
|
-
{
|
|
549
|
+
{
|
|
550
|
+
input = process.stdin,
|
|
551
|
+
output = process.stderr,
|
|
552
|
+
ui = createUi(),
|
|
553
|
+
clipboardAdapter = clipboard,
|
|
554
|
+
openUrl = open,
|
|
555
|
+
env = process.env,
|
|
556
|
+
} = {},
|
|
408
557
|
) {
|
|
409
558
|
let closed = false
|
|
410
559
|
let previousRaw = false
|
|
560
|
+
const editor = new InputEditor()
|
|
561
|
+
const decoder = new TerminalInputDecoder()
|
|
562
|
+
let rows = []
|
|
563
|
+
let inputY = 0
|
|
564
|
+
let inputX = 0
|
|
565
|
+
let selection
|
|
566
|
+
let dragStart
|
|
567
|
+
let lastSelection = ''
|
|
568
|
+
let notice
|
|
569
|
+
let eventQueue = Promise.resolve()
|
|
411
570
|
let resolveExit
|
|
412
571
|
const exit = new Promise((resolve) => { resolveExit = resolve })
|
|
413
572
|
|
|
@@ -415,26 +574,122 @@ export function createDashboardSession(
|
|
|
415
574
|
if (closed) return
|
|
416
575
|
const width = output.columns || 80
|
|
417
576
|
const height = output.rows || 24
|
|
418
|
-
|
|
577
|
+
if (notice && Date.now() - notice.startedAt >= 1500) notice = undefined
|
|
578
|
+
const frame = renderDashboardFrame(state, width, height, ui, editor, selection, notice)
|
|
579
|
+
rows = frame.rows
|
|
580
|
+
inputY = frame.inputY
|
|
581
|
+
inputX = frame.inputX
|
|
582
|
+
output.write(`\x1b[H${frame.text}\x1b[J\x1b[${inputY + 1};${frame.cursorX + 1}H\x1b[?25h`)
|
|
419
583
|
}
|
|
420
584
|
const requestExit = (reason) => {
|
|
421
585
|
if (!closed) resolveExit(reason)
|
|
422
586
|
}
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
587
|
+
const setNotice = (message, error = false) => {
|
|
588
|
+
notice = { message, error, startedAt: Date.now() }
|
|
589
|
+
draw()
|
|
590
|
+
}
|
|
591
|
+
const osc52 = (value) => {
|
|
592
|
+
const sequence = `\x1b]52;c;${Buffer.from(value).toString('base64')}\x07`
|
|
593
|
+
output.write(env.TMUX ? `\x1bPtmux;${sequence.replaceAll('\x1b', '\x1b\x1b')}\x1b\\` : sequence)
|
|
594
|
+
}
|
|
595
|
+
const copyText = async (value) => {
|
|
596
|
+
if (env.SSH_CONNECTION || env.SSH_TTY) {
|
|
597
|
+
osc52(value)
|
|
598
|
+
setNotice('Copied to clipboard')
|
|
599
|
+
return
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
await clipboardAdapter.write(value)
|
|
603
|
+
setNotice('Copied to clipboard')
|
|
604
|
+
} catch {
|
|
605
|
+
try {
|
|
606
|
+
osc52(value)
|
|
607
|
+
setNotice('Copied to clipboard')
|
|
608
|
+
} catch {
|
|
609
|
+
setNotice('Failed to copy; terminal clipboard is unavailable', true)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const pasteClipboard = async () => {
|
|
614
|
+
try {
|
|
615
|
+
editor.insertPaste(await clipboardAdapter.read())
|
|
616
|
+
selection = undefined
|
|
617
|
+
draw()
|
|
618
|
+
} catch {
|
|
619
|
+
setNotice('Clipboard paste is unavailable; use terminal paste', true)
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const submit = async () => {
|
|
623
|
+
const result = editor.submit()
|
|
624
|
+
if (result.error) pushActivity(state, 'warning', result.error)
|
|
625
|
+
else if (result.command === 'help') pushActivity(state, 'info', 'Commands: help · clear · open · quit (a leading / is optional)')
|
|
626
|
+
else if (result.command === 'clear') {
|
|
428
627
|
state.activity.length = 0
|
|
429
628
|
state.scrollFromBottom = 0
|
|
430
|
-
|
|
431
|
-
} else if (
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
629
|
+
setNotice('Activity cleared')
|
|
630
|
+
} else if (result.command === 'open') {
|
|
631
|
+
if (!/^https?:\/\//u.test(state.endpoint)) pushActivity(state, 'warning', 'No development-server URL is available to open')
|
|
632
|
+
else {
|
|
633
|
+
try {
|
|
634
|
+
await openUrl(state.endpoint)
|
|
635
|
+
setNotice('Opened development server')
|
|
636
|
+
} catch (error) {
|
|
637
|
+
pushActivity(state, 'warning', `Failed to open ${state.endpoint}: ${error.message || error}`)
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
} else if (result.command === 'quit') requestExit('q')
|
|
641
|
+
draw()
|
|
642
|
+
}
|
|
643
|
+
const handleEvent = async (event) => {
|
|
644
|
+
if (event.type === 'paste') editor.insertPaste(event.value)
|
|
645
|
+
else if (event.type === 'text') {
|
|
646
|
+
editor.insert(event.value)
|
|
647
|
+
selection = undefined
|
|
648
|
+
} else if (event.type === 'mouse') {
|
|
649
|
+
const position = { x: event.x, y: event.y }
|
|
650
|
+
if (event.kind === 'down' && event.button === 'left') {
|
|
651
|
+
dragStart = position
|
|
652
|
+
selection = { start: position, end: position }
|
|
653
|
+
} else if (event.kind === 'drag' && dragStart) selection = { start: dragStart, end: position }
|
|
654
|
+
else if (event.kind === 'up' && event.button === 'left' && dragStart) {
|
|
655
|
+
selection = { start: dragStart, end: position }
|
|
656
|
+
dragStart = undefined
|
|
657
|
+
const value = extractSelection(rows, selection)
|
|
658
|
+
if (value) {
|
|
659
|
+
lastSelection = value
|
|
660
|
+
await copyText(value)
|
|
661
|
+
} else {
|
|
662
|
+
if (position.y === inputY) editor.setCursorFromCell(Math.max(0, position.x - inputX))
|
|
663
|
+
selection = undefined
|
|
664
|
+
}
|
|
665
|
+
} else if (event.kind === 'down' && event.button === 'right' && position.y === inputY) await pasteClipboard()
|
|
666
|
+
else if (event.kind === 'scroll-up') state.scrollFromBottom = Math.max(0, Math.min(activityRowCount(state) - 1, state.scrollFromBottom + 3))
|
|
667
|
+
else if (event.kind === 'scroll-down') state.scrollFromBottom = Math.max(0, state.scrollFromBottom - 3)
|
|
668
|
+
} else if (event.type === 'key') {
|
|
669
|
+
if (event.key === 'ctrl-c') requestExit('SIGINT')
|
|
670
|
+
else if (event.key === 'ctrl-y') lastSelection ? await copyText(lastSelection) : setNotice('No selected text to copy', true)
|
|
671
|
+
else if (event.key === 'ctrl-v') await pasteClipboard()
|
|
672
|
+
else if (event.key === 'enter') await submit()
|
|
673
|
+
else if (event.key === 'escape') { editor.clear(); selection = undefined }
|
|
674
|
+
else if (event.key === 'left') editor.moveLeft()
|
|
675
|
+
else if (event.key === 'right') editor.moveRight()
|
|
676
|
+
else if (event.key === 'home') editor.moveHome()
|
|
677
|
+
else if (event.key === 'end') editor.moveEnd()
|
|
678
|
+
else if (event.key === 'ctrl-end') state.scrollFromBottom = 0
|
|
679
|
+
else if (event.key === 'backspace') editor.backspace()
|
|
680
|
+
else if (event.key === 'delete') editor.delete()
|
|
681
|
+
else if (event.key === 'up') editor.historyPrevious()
|
|
682
|
+
else if (event.key === 'down') editor.historyNext()
|
|
683
|
+
else if (event.key === 'pageup') state.scrollFromBottom = Math.max(0, Math.min(activityRowCount(state) - 1, state.scrollFromBottom + 10))
|
|
684
|
+
else if (event.key === 'pagedown') state.scrollFromBottom = Math.max(0, state.scrollFromBottom - 10)
|
|
685
|
+
}
|
|
436
686
|
draw()
|
|
437
687
|
}
|
|
688
|
+
const onData = (chunk) => {
|
|
689
|
+
for (const event of decoder.push(chunk)) {
|
|
690
|
+
eventQueue = eventQueue.then(() => handleEvent(event))
|
|
691
|
+
}
|
|
692
|
+
}
|
|
438
693
|
const onResize = () => draw()
|
|
439
694
|
|
|
440
695
|
previousRaw = input.isRaw === true
|
|
@@ -442,7 +697,7 @@ export function createDashboardSession(
|
|
|
442
697
|
input.resume?.()
|
|
443
698
|
input.on('data', onData)
|
|
444
699
|
output.on?.('resize', onResize)
|
|
445
|
-
output.write('\x1b[?1049h\x1b[?25l\x1b[2J')
|
|
700
|
+
output.write('\x1b[?1049h\x1b[?1002h\x1b[?1006h\x1b[?2004h\x1b[?25l\x1b[2J')
|
|
446
701
|
const timer = setInterval(draw, 100)
|
|
447
702
|
timer.unref?.()
|
|
448
703
|
draw()
|
|
@@ -459,7 +714,7 @@ export function createDashboardSession(
|
|
|
459
714
|
output.off?.('resize', onResize)
|
|
460
715
|
if (typeof input.setRawMode === 'function') input.setRawMode(previousRaw)
|
|
461
716
|
if (!previousRaw) input.pause?.()
|
|
462
|
-
output.write('\x1b[?25h\x1b[?1049l')
|
|
717
|
+
output.write('\x1b[?2004l\x1b[?1006l\x1b[?1002l\x1b[?25h\x1b[?1049l')
|
|
463
718
|
},
|
|
464
719
|
}
|
|
465
720
|
}
|
package/bin/wake.mjs
CHANGED
|
@@ -184,7 +184,11 @@ async function runServer(factory, options, command, root, ui, uiMode) {
|
|
|
184
184
|
dashboard?.draw()
|
|
185
185
|
}
|
|
186
186
|
const onDiagnostic = (diagnostic) => {
|
|
187
|
-
applyDashboardEvent(state, { type: 'diagnostic',
|
|
187
|
+
applyDashboardEvent(state, { type: 'diagnostic', diagnostic })
|
|
188
|
+
dashboard?.draw()
|
|
189
|
+
}
|
|
190
|
+
const onWorkspaceState = (event) => {
|
|
191
|
+
applyDashboardEvent(state, event)
|
|
188
192
|
dashboard?.draw()
|
|
189
193
|
}
|
|
190
194
|
const onClosed = () => {
|
|
@@ -194,6 +198,7 @@ async function runServer(factory, options, command, root, ui, uiMode) {
|
|
|
194
198
|
server.on('rebuildStart', onRebuildStart)
|
|
195
199
|
server.on('rebuilt', onRebuilt)
|
|
196
200
|
server.on('diagnostic', onDiagnostic)
|
|
201
|
+
server.on('workspaceState', onWorkspaceState)
|
|
197
202
|
server.on('closed', onClosed)
|
|
198
203
|
|
|
199
204
|
if (!useTui) stopObserving = observeServer(server, ui)
|
|
@@ -235,6 +240,7 @@ async function runServer(factory, options, command, root, ui, uiMode) {
|
|
|
235
240
|
server.off('rebuildStart', onRebuildStart)
|
|
236
241
|
server.off('rebuilt', onRebuilt)
|
|
237
242
|
server.off('diagnostic', onDiagnostic)
|
|
243
|
+
server.off('workspaceState', onWorkspaceState)
|
|
238
244
|
server.off('closed', onClosed)
|
|
239
245
|
}
|
|
240
246
|
} catch (error) {
|
package/index.cjs
CHANGED
|
@@ -123,12 +123,10 @@ class DevServer extends EventEmitter {
|
|
|
123
123
|
this.emit('rebuildStart', event)
|
|
124
124
|
} else if (event.type === 'rebuilt') {
|
|
125
125
|
this.emit('rebuilt', event)
|
|
126
|
+
} else if (event.type === 'workspaceState') {
|
|
127
|
+
this.emit('workspaceState', event)
|
|
126
128
|
} else if (event.type === 'diagnostic') {
|
|
127
|
-
this.emit('diagnostic',
|
|
128
|
-
severity: 'error',
|
|
129
|
-
code: 'WAKE_BUILD',
|
|
130
|
-
message: event.message,
|
|
131
|
-
})
|
|
129
|
+
this.emit('diagnostic', event.diagnostic)
|
|
132
130
|
} else if (event.type === 'closed' && !this.#closed) {
|
|
133
131
|
this.#closed = true
|
|
134
132
|
clearInterval(this.#eventTimer)
|
package/index.d.ts
CHANGED
|
@@ -21,6 +21,20 @@ export type WakeErrorCode =
|
|
|
21
21
|
| 'WAKE_LIBRARY_TYPE'
|
|
22
22
|
| 'WAKE_LIBRARY_OUTPUT'
|
|
23
23
|
|
|
24
|
+
export interface DiagnosticLocation {
|
|
25
|
+
/** One-based source line. */
|
|
26
|
+
line: number
|
|
27
|
+
/** One-based Unicode-scalar column. */
|
|
28
|
+
column: number
|
|
29
|
+
/** One-based line containing the exclusive end position. */
|
|
30
|
+
endLine: number
|
|
31
|
+
/** One-based Unicode-scalar column of the exclusive end position. */
|
|
32
|
+
endColumn: number
|
|
33
|
+
/** Exact source line without its line terminator. */
|
|
34
|
+
lineText: string
|
|
35
|
+
label?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
export interface Diagnostic {
|
|
25
39
|
severity: 'error' | 'warning' | 'note' | 'help'
|
|
26
40
|
code?: string
|
|
@@ -28,6 +42,7 @@ export interface Diagnostic {
|
|
|
28
42
|
path?: string
|
|
29
43
|
start?: number
|
|
30
44
|
end?: number
|
|
45
|
+
location?: DiagnosticLocation
|
|
31
46
|
notes?: string[]
|
|
32
47
|
}
|
|
33
48
|
|
|
@@ -168,6 +183,15 @@ export interface DocsDemo {
|
|
|
168
183
|
warnings: string[]
|
|
169
184
|
}
|
|
170
185
|
|
|
186
|
+
export interface DocsWorkspaceBuildInfo {
|
|
187
|
+
name: string
|
|
188
|
+
root: string
|
|
189
|
+
basePath: string
|
|
190
|
+
mode: 'components'
|
|
191
|
+
presentation: 'embedded' | 'standalone'
|
|
192
|
+
demos: number
|
|
193
|
+
}
|
|
194
|
+
|
|
171
195
|
export interface DocsBuildOptions extends ProjectOptions {
|
|
172
196
|
outdir?: string
|
|
173
197
|
basePath?: string
|
|
@@ -178,6 +202,7 @@ export interface DocsBuildResult extends BuildResult {
|
|
|
178
202
|
routes: DocsRoute[]
|
|
179
203
|
mode: DocsMode
|
|
180
204
|
demos: DocsDemo[]
|
|
205
|
+
workspaces: DocsWorkspaceBuildInfo[]
|
|
181
206
|
}
|
|
182
207
|
|
|
183
208
|
export interface DevServerOptions extends ProjectOptions {
|
|
@@ -193,6 +218,8 @@ export interface DocsDevServerOptions extends DevServerOptions {
|
|
|
193
218
|
export interface DevServerRebuildStartEvent {
|
|
194
219
|
type: 'rebuildStart'
|
|
195
220
|
changedPaths: string[]
|
|
221
|
+
workspace?: string
|
|
222
|
+
basePath?: string
|
|
196
223
|
}
|
|
197
224
|
|
|
198
225
|
export interface DevServerRebuiltEvent {
|
|
@@ -204,6 +231,17 @@ export interface DevServerRebuiltEvent {
|
|
|
204
231
|
chunks: number
|
|
205
232
|
assets: number
|
|
206
233
|
durationMs: number
|
|
234
|
+
workspace?: string
|
|
235
|
+
basePath?: string
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface DevServerWorkspaceStateEvent {
|
|
239
|
+
type: 'workspaceState'
|
|
240
|
+
total: number
|
|
241
|
+
loaded: number
|
|
242
|
+
failed: number
|
|
243
|
+
current?: string
|
|
244
|
+
failedNames: string[]
|
|
207
245
|
}
|
|
208
246
|
|
|
209
247
|
export class BuildContext {
|
|
@@ -222,6 +260,7 @@ export class DevServer extends EventEmitter {
|
|
|
222
260
|
[Symbol.asyncDispose](): Promise<void>
|
|
223
261
|
on(event: 'rebuildStart', listener: (event: DevServerRebuildStartEvent) => void): this
|
|
224
262
|
on(event: 'rebuilt', listener: (event: DevServerRebuiltEvent) => void): this
|
|
263
|
+
on(event: 'workspaceState', listener: (event: DevServerWorkspaceStateEvent) => void): this
|
|
225
264
|
on(event: 'diagnostic', listener: (diagnostic: Diagnostic) => void): this
|
|
226
265
|
on(event: 'closed', listener: () => void): this
|
|
227
266
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crab-dev/wake",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Wake native web build tools for Node.js",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"node": ">=22.14 <27"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"@crab-dev/css": "0.1.21",
|
|
53
54
|
"@crab-dev/rc-alert": "^0.0.2",
|
|
54
55
|
"@crab-dev/rc-button": "^0.0.2",
|
|
55
56
|
"@crab-dev/rc-dialog": "^0.0.2",
|
|
@@ -64,19 +65,21 @@
|
|
|
64
65
|
"@crab-dev/rc-text-edit": "^0.0.1",
|
|
65
66
|
"@crab-dev/rc-tooltip": "^0.0.2",
|
|
66
67
|
"@crab-dev/rc-tree": "^0.1.2",
|
|
67
|
-
"
|
|
68
|
-
"lucide-react": "^1.23.0"
|
|
68
|
+
"clipboardy": "5.3.2",
|
|
69
|
+
"lucide-react": "^1.23.0",
|
|
70
|
+
"open": "11.0.0",
|
|
71
|
+
"string-width": "8.2.2"
|
|
69
72
|
},
|
|
70
73
|
"peerDependencies": {
|
|
71
74
|
"react": "^19.2.8",
|
|
72
75
|
"react-dom": "^19.2.8"
|
|
73
76
|
},
|
|
74
77
|
"optionalDependencies": {
|
|
75
|
-
"@crab-dev/wake-darwin-arm64": "0.1.
|
|
76
|
-
"@crab-dev/wake-darwin-x64": "0.1.
|
|
77
|
-
"@crab-dev/wake-linux-arm64-gnu": "0.1.
|
|
78
|
-
"@crab-dev/wake-linux-x64-gnu": "0.1.
|
|
79
|
-
"@crab-dev/wake-win32-x64-msvc": "0.1.
|
|
78
|
+
"@crab-dev/wake-darwin-arm64": "0.1.21",
|
|
79
|
+
"@crab-dev/wake-darwin-x64": "0.1.21",
|
|
80
|
+
"@crab-dev/wake-linux-arm64-gnu": "0.1.21",
|
|
81
|
+
"@crab-dev/wake-linux-x64-gnu": "0.1.21",
|
|
82
|
+
"@crab-dev/wake-win32-x64-msvc": "0.1.21"
|
|
80
83
|
},
|
|
81
84
|
"publishConfig": {
|
|
82
85
|
"access": "public",
|