uilint 0.2.145 → 0.2.147
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/dist/{chunk-ZUOFUPGT.js → chunk-5EDE3J6O.js} +2 -1
- package/dist/chunk-5QUW7BNW.js +80 -0
- package/dist/chunk-5QUW7BNW.js.map +1 -0
- package/dist/{chunk-ZOLQHFVQ.js → chunk-JEYSQ5KF.js} +20 -1
- package/dist/chunk-JEYSQ5KF.js.map +1 -0
- package/dist/index.js +475 -248
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-KJYYI5DH.js → init-ui-QQXIZSKI.js} +123 -22
- package/dist/init-ui-QQXIZSKI.js.map +1 -0
- package/dist/{plugin-loader-O6PNFN6D.js → plugin-loader-LUIV7MLR.js} +2 -2
- package/dist/{remove-ui-ZHW4GUFL.js → remove-ui-GZRFA2AC.js} +2 -2
- package/dist/{render-43OMCORR.js → render-2P4YWHXV.js} +112 -97
- package/dist/render-2P4YWHXV.js.map +1 -0
- package/dist/{upgrade-TPZ62BT2.js → upgrade-2UKW3SIQ.js} +2 -2
- package/package.json +8 -8
- package/dist/chunk-WG2WZTB2.js +0 -56
- package/dist/chunk-WG2WZTB2.js.map +0 -1
- package/dist/chunk-ZOLQHFVQ.js.map +0 -1
- package/dist/init-ui-KJYYI5DH.js.map +0 -1
- package/dist/render-43OMCORR.js.map +0 -1
- /package/dist/{chunk-ZUOFUPGT.js.map → chunk-5EDE3J6O.js.map} +0 -0
- /package/dist/{plugin-loader-O6PNFN6D.js.map → plugin-loader-LUIV7MLR.js.map} +0 -0
- /package/dist/{remove-ui-ZHW4GUFL.js.map → remove-ui-GZRFA2AC.js.map} +0 -0
- /package/dist/{upgrade-TPZ62BT2.js.map → upgrade-2UKW3SIQ.js.map} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getDashboardStore
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JEYSQ5KF.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/serve/dashboard/render.tsx
|
|
7
7
|
import { render } from "ink";
|
|
8
8
|
|
|
9
9
|
// src/commands/serve/dashboard/ServeDashboard.tsx
|
|
10
10
|
import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
|
|
11
|
-
import { Box as
|
|
11
|
+
import { Box as Box9, useInput, useApp } from "ink";
|
|
12
12
|
|
|
13
13
|
// src/commands/serve/dashboard/components/ServerHeader.tsx
|
|
14
14
|
import { Box, Text } from "ink";
|
|
@@ -148,78 +148,92 @@ function StatsBar({
|
|
|
148
148
|
// src/commands/serve/dashboard/components/BackgroundTasks.tsx
|
|
149
149
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
150
150
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
151
|
+
|
|
152
|
+
// src/commands/serve/dashboard/components/PluginStatusPanel.tsx
|
|
153
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
154
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
155
|
+
var statusDisplay = {
|
|
156
|
+
idle: { icon: "\u25CB", color: "gray", label: "Idle" },
|
|
157
|
+
"waiting-for-ollama": { icon: "\u25CC", color: "yellow", label: "Queued" },
|
|
158
|
+
"using-ollama": { icon: "\u25CF", color: "cyan", label: "Using Ollama" },
|
|
159
|
+
processing: { icon: "\u25CF", color: "blue", label: "Processing" },
|
|
160
|
+
complete: { icon: "\u2713", color: "green", label: "Complete" },
|
|
161
|
+
error: { icon: "\u2717", color: "red", label: "Error" }
|
|
162
|
+
};
|
|
151
163
|
function ProgressBar({
|
|
152
164
|
progress,
|
|
153
|
-
width =
|
|
165
|
+
width = 12
|
|
154
166
|
}) {
|
|
155
167
|
const filled = Math.round(progress / 100 * width);
|
|
156
168
|
const empty = width - filled;
|
|
157
|
-
return /* @__PURE__ */
|
|
158
|
-
/* @__PURE__ */
|
|
159
|
-
/* @__PURE__ */
|
|
169
|
+
return /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
170
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "\u2588".repeat(filled) }),
|
|
171
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2591".repeat(empty) })
|
|
160
172
|
] });
|
|
161
173
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const statusColor = {
|
|
174
|
-
idle: "gray",
|
|
175
|
-
running: "cyan",
|
|
176
|
-
complete: "green",
|
|
177
|
-
error: "red"
|
|
178
|
-
}[task.status];
|
|
179
|
-
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
180
|
-
/* @__PURE__ */ jsxs4(Text4, { color: statusColor, children: [
|
|
181
|
-
statusIcon,
|
|
174
|
+
function truncate(str, maxLen) {
|
|
175
|
+
if (str.length <= maxLen) return str;
|
|
176
|
+
return str.slice(0, maxLen - 1) + "\u2026";
|
|
177
|
+
}
|
|
178
|
+
function PluginRow({
|
|
179
|
+
plugin
|
|
180
|
+
}) {
|
|
181
|
+
const display = statusDisplay[plugin.status];
|
|
182
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
183
|
+
/* @__PURE__ */ jsxs5(Text5, { color: display.color, children: [
|
|
184
|
+
display.icon,
|
|
182
185
|
" "
|
|
183
186
|
] }),
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
/* @__PURE__ */ jsx5(Box5, { width: 13, children: /* @__PURE__ */ jsx5(Text5, { bold: plugin.status !== "idle", children: plugin.name }) }),
|
|
188
|
+
/* @__PURE__ */ jsx5(Box5, { width: 16, children: /* @__PURE__ */ jsx5(Text5, { color: display.color, children: display.label }) }),
|
|
189
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
190
|
+
"(",
|
|
191
|
+
plugin.model,
|
|
192
|
+
")"
|
|
187
193
|
] }),
|
|
188
|
-
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
/* @__PURE__ */
|
|
191
|
-
|
|
194
|
+
(plugin.status === "waiting-for-ollama" || plugin.status === "using-ollama" || plugin.status === "processing") && plugin.progress !== void 0 && /* @__PURE__ */ jsxs5(Box5, { marginLeft: 1, gap: 1, children: [
|
|
195
|
+
/* @__PURE__ */ jsx5(ProgressBar, { progress: plugin.progress }),
|
|
196
|
+
/* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
197
|
+
plugin.progress.toFixed(0),
|
|
192
198
|
"%",
|
|
193
|
-
|
|
199
|
+
plugin.current !== void 0 && plugin.total !== void 0 ? ` (${plugin.current}/${plugin.total})` : ""
|
|
194
200
|
] })
|
|
195
|
-
] })
|
|
201
|
+
] }),
|
|
202
|
+
plugin.message && plugin.status !== "idle" && /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
|
|
203
|
+
" ",
|
|
204
|
+
truncate(plugin.message, 40)
|
|
205
|
+
] }),
|
|
206
|
+
plugin.status === "error" && plugin.error && /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
207
|
+
" ",
|
|
208
|
+
truncate(plugin.error, 40)
|
|
209
|
+
] })
|
|
196
210
|
] });
|
|
197
211
|
}
|
|
198
|
-
function
|
|
199
|
-
|
|
212
|
+
function PluginStatusPanel({
|
|
213
|
+
plugins
|
|
200
214
|
}) {
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
215
|
+
const pluginArray = Array.from(plugins.values());
|
|
216
|
+
if (pluginArray.length === 0) {
|
|
203
217
|
return null;
|
|
204
218
|
}
|
|
205
|
-
return /* @__PURE__ */
|
|
206
|
-
|
|
219
|
+
return /* @__PURE__ */ jsxs5(
|
|
220
|
+
Box5,
|
|
207
221
|
{
|
|
208
222
|
flexDirection: "column",
|
|
209
223
|
borderStyle: "single",
|
|
210
224
|
borderColor: "gray",
|
|
211
225
|
paddingX: 1,
|
|
212
226
|
children: [
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
-
|
|
227
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, dimColor: true, children: "Plugins" }),
|
|
228
|
+
pluginArray.map((plugin) => /* @__PURE__ */ jsx5(PluginRow, { plugin }, plugin.id))
|
|
215
229
|
]
|
|
216
230
|
}
|
|
217
231
|
);
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
// src/commands/serve/dashboard/components/ActivityLog.tsx
|
|
221
|
-
import { Box as
|
|
222
|
-
import { jsx as
|
|
235
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
236
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
223
237
|
var filterLabels = {
|
|
224
238
|
all: "All",
|
|
225
239
|
errors: "Errors",
|
|
@@ -239,7 +253,7 @@ function formatTime(date) {
|
|
|
239
253
|
function stripAnsi(str) {
|
|
240
254
|
return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").replace(/\r/g, "");
|
|
241
255
|
}
|
|
242
|
-
function
|
|
256
|
+
function truncate2(str, maxLen) {
|
|
243
257
|
const clean = stripAnsi(str).replace(/\n/g, " ");
|
|
244
258
|
if (clean.length <= maxLen) return clean;
|
|
245
259
|
return clean.slice(0, maxLen - 1) + "\u2026";
|
|
@@ -257,6 +271,7 @@ function getTypeDisplay(type) {
|
|
|
257
271
|
"semantic:analyze": { label: "semant", color: "blue" },
|
|
258
272
|
"semantic:done": { label: "semant", color: "green" },
|
|
259
273
|
"semantic:error": { label: "semant", color: "red" },
|
|
274
|
+
"semantic:skip": { label: "semant", color: "yellow" },
|
|
260
275
|
"config:set": { label: "config", color: "yellow" },
|
|
261
276
|
"rule:config:set": { label: "rule", color: "yellow" },
|
|
262
277
|
"screenshot:save": { label: "screen", color: "cyan" },
|
|
@@ -280,16 +295,16 @@ function ActivityRow({
|
|
|
280
295
|
}) {
|
|
281
296
|
const { label, color } = getTypeDisplay(entry.type);
|
|
282
297
|
const showDetail = (verbose || entry.isError) && entry.detail;
|
|
283
|
-
return /* @__PURE__ */
|
|
284
|
-
/* @__PURE__ */
|
|
285
|
-
/* @__PURE__ */
|
|
298
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
299
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
300
|
+
/* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
286
301
|
formatTime(entry.timestamp),
|
|
287
302
|
" "
|
|
288
303
|
] }),
|
|
289
|
-
/* @__PURE__ */
|
|
290
|
-
/* @__PURE__ */
|
|
304
|
+
/* @__PURE__ */ jsx6(Text6, { color, bold: true, children: label.padEnd(7) }),
|
|
305
|
+
/* @__PURE__ */ jsx6(Text6, { color: entry.isError ? "red" : entry.isWarning ? "yellow" : void 0, children: truncate2(entry.message, MAX_MSG_WIDTH) })
|
|
291
306
|
] }),
|
|
292
|
-
showDetail && /* @__PURE__ */
|
|
307
|
+
showDetail && /* @__PURE__ */ jsx6(Box6, { paddingLeft: 16, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: truncate2(entry.detail, MAX_DETAIL_WIDTH) }) })
|
|
293
308
|
] });
|
|
294
309
|
}
|
|
295
310
|
function ActivityLog({
|
|
@@ -306,8 +321,8 @@ function ActivityLog({
|
|
|
306
321
|
const visibleActivities = filtered.slice(start, end);
|
|
307
322
|
const isScrolled = start > 0;
|
|
308
323
|
const hasMoreBelow = end < total;
|
|
309
|
-
return /* @__PURE__ */
|
|
310
|
-
|
|
324
|
+
return /* @__PURE__ */ jsxs6(
|
|
325
|
+
Box6,
|
|
311
326
|
{
|
|
312
327
|
flexDirection: "column",
|
|
313
328
|
borderStyle: "single",
|
|
@@ -315,36 +330,36 @@ function ActivityLog({
|
|
|
315
330
|
paddingX: 1,
|
|
316
331
|
flexGrow: 1,
|
|
317
332
|
children: [
|
|
318
|
-
/* @__PURE__ */
|
|
319
|
-
/* @__PURE__ */
|
|
320
|
-
/* @__PURE__ */
|
|
321
|
-
activeFilter !== "all" && /* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ jsxs6(Box6, { justifyContent: "space-between", children: [
|
|
334
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 1, children: [
|
|
335
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, dimColor: true, children: "Activity" }),
|
|
336
|
+
activeFilter !== "all" && /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
|
|
322
337
|
"[",
|
|
323
338
|
filterLabels[activeFilter],
|
|
324
339
|
"]"
|
|
325
340
|
] })
|
|
326
341
|
] }),
|
|
327
|
-
/* @__PURE__ */
|
|
328
|
-
isScrolled && /* @__PURE__ */
|
|
329
|
-
total > maxVisible && /* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 1, children: [
|
|
343
|
+
isScrolled && /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2191" }),
|
|
344
|
+
total > maxVisible && /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
|
|
330
345
|
start + 1,
|
|
331
346
|
"-",
|
|
332
347
|
end,
|
|
333
348
|
" of ",
|
|
334
349
|
total
|
|
335
350
|
] }),
|
|
336
|
-
hasMoreBelow && /* @__PURE__ */
|
|
351
|
+
hasMoreBelow && /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: "\u2193" })
|
|
337
352
|
] })
|
|
338
353
|
] }),
|
|
339
|
-
visibleActivities.length === 0 ? /* @__PURE__ */
|
|
354
|
+
visibleActivities.length === 0 ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: activeFilter !== "all" ? `No ${filterLabels[activeFilter].toLowerCase()} activity yet...` : "No activity yet..." }) : visibleActivities.map((entry) => /* @__PURE__ */ jsx6(ActivityRow, { entry, verbose }, entry.id))
|
|
340
355
|
]
|
|
341
356
|
}
|
|
342
357
|
);
|
|
343
358
|
}
|
|
344
359
|
|
|
345
360
|
// src/commands/serve/dashboard/components/HelpBar.tsx
|
|
346
|
-
import { Box as
|
|
347
|
-
import { jsx as
|
|
361
|
+
import { Box as Box7, Text as Text7 } from "ink";
|
|
362
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
348
363
|
var filterLabels2 = {
|
|
349
364
|
all: "all",
|
|
350
365
|
errors: "errors",
|
|
@@ -354,47 +369,47 @@ var filterLabels2 = {
|
|
|
354
369
|
system: "system"
|
|
355
370
|
};
|
|
356
371
|
function HelpBar({ verbose, activeFilter = "all" }) {
|
|
357
|
-
return /* @__PURE__ */
|
|
358
|
-
/* @__PURE__ */
|
|
359
|
-
/* @__PURE__ */
|
|
360
|
-
/* @__PURE__ */
|
|
372
|
+
return /* @__PURE__ */ jsxs7(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1, gap: 2, children: [
|
|
373
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
374
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "q" }),
|
|
375
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " quit" })
|
|
361
376
|
] }),
|
|
362
|
-
/* @__PURE__ */
|
|
363
|
-
/* @__PURE__ */
|
|
364
|
-
/* @__PURE__ */
|
|
377
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
378
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "c" }),
|
|
379
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " clear log" })
|
|
365
380
|
] }),
|
|
366
|
-
/* @__PURE__ */
|
|
367
|
-
/* @__PURE__ */
|
|
368
|
-
/* @__PURE__ */
|
|
381
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
382
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "v" }),
|
|
383
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
369
384
|
" verbose ",
|
|
370
385
|
verbose ? "(on)" : "(off)"
|
|
371
386
|
] })
|
|
372
387
|
] }),
|
|
373
|
-
/* @__PURE__ */
|
|
374
|
-
/* @__PURE__ */
|
|
375
|
-
/* @__PURE__ */
|
|
388
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
389
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "f" }),
|
|
390
|
+
/* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
|
|
376
391
|
" filter (",
|
|
377
392
|
filterLabels2[activeFilter],
|
|
378
393
|
")"
|
|
379
394
|
] })
|
|
380
395
|
] }),
|
|
381
|
-
/* @__PURE__ */
|
|
382
|
-
/* @__PURE__ */
|
|
383
|
-
/* @__PURE__ */
|
|
396
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
397
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "r" }),
|
|
398
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " rebuild index" })
|
|
384
399
|
] }),
|
|
385
|
-
/* @__PURE__ */
|
|
386
|
-
/* @__PURE__ */
|
|
387
|
-
/* @__PURE__ */
|
|
400
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
401
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, color: "cyan", children: "\u2191\u2193" }),
|
|
402
|
+
/* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " scroll" })
|
|
388
403
|
] })
|
|
389
404
|
] });
|
|
390
405
|
}
|
|
391
406
|
|
|
392
407
|
// src/commands/serve/dashboard/components/OllamaStatus.tsx
|
|
393
|
-
import { Box as
|
|
394
|
-
import { jsx as
|
|
408
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
409
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
395
410
|
|
|
396
411
|
// src/commands/serve/dashboard/ServeDashboard.tsx
|
|
397
|
-
import { jsx as
|
|
412
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
398
413
|
function ServeDashboard({
|
|
399
414
|
onQuit,
|
|
400
415
|
onRebuildIndex
|
|
@@ -476,16 +491,16 @@ function ServeDashboard({
|
|
|
476
491
|
[exit, onQuit, onRebuildIndex, store, state.activities.length]
|
|
477
492
|
)
|
|
478
493
|
);
|
|
479
|
-
return /* @__PURE__ */
|
|
480
|
-
/* @__PURE__ */
|
|
481
|
-
/* @__PURE__ */
|
|
494
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", width: "100%", children: [
|
|
495
|
+
/* @__PURE__ */ jsx9(ServerHeader, { port: state.port, isRunning: state.isRunning }),
|
|
496
|
+
/* @__PURE__ */ jsx9(
|
|
482
497
|
WorkspaceInfo,
|
|
483
498
|
{
|
|
484
499
|
workspaceRoot: state.workspace?.workspaceRoot ?? null,
|
|
485
500
|
appRoot: state.workspace?.appRoot ?? null
|
|
486
501
|
}
|
|
487
502
|
),
|
|
488
|
-
/* @__PURE__ */
|
|
503
|
+
/* @__PURE__ */ jsx9(
|
|
489
504
|
StatsBar,
|
|
490
505
|
{
|
|
491
506
|
connectedClients: state.stats.connectedClients,
|
|
@@ -496,8 +511,8 @@ function ServeDashboard({
|
|
|
496
511
|
ollamaModel: state.ollamaStatus.model
|
|
497
512
|
}
|
|
498
513
|
),
|
|
499
|
-
/* @__PURE__ */
|
|
500
|
-
/* @__PURE__ */
|
|
514
|
+
/* @__PURE__ */ jsx9(PluginStatusPanel, { plugins: state.pluginStates }),
|
|
515
|
+
/* @__PURE__ */ jsx9(
|
|
501
516
|
ActivityLog,
|
|
502
517
|
{
|
|
503
518
|
activities: state.activities,
|
|
@@ -507,12 +522,12 @@ function ServeDashboard({
|
|
|
507
522
|
activeFilter: state.activeFilter
|
|
508
523
|
}
|
|
509
524
|
),
|
|
510
|
-
/* @__PURE__ */
|
|
525
|
+
/* @__PURE__ */ jsx9(HelpBar, { verbose: state.verbose, activeFilter: state.activeFilter })
|
|
511
526
|
] });
|
|
512
527
|
}
|
|
513
528
|
|
|
514
529
|
// src/commands/serve/dashboard/render.tsx
|
|
515
|
-
import { jsx as
|
|
530
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
516
531
|
function interceptConsole() {
|
|
517
532
|
const origLog = console.log;
|
|
518
533
|
const origError = console.error;
|
|
@@ -540,7 +555,7 @@ function renderDashboard(options = {}) {
|
|
|
540
555
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
541
556
|
const restoreConsole = interceptConsole();
|
|
542
557
|
const { unmount: inkUnmount, waitUntilExit: inkWaitUntilExit } = render(
|
|
543
|
-
/* @__PURE__ */
|
|
558
|
+
/* @__PURE__ */ jsx10(
|
|
544
559
|
ServeDashboard,
|
|
545
560
|
{
|
|
546
561
|
onQuit: options.onQuit,
|
|
@@ -559,4 +574,4 @@ function renderDashboard(options = {}) {
|
|
|
559
574
|
export {
|
|
560
575
|
renderDashboard
|
|
561
576
|
};
|
|
562
|
-
//# sourceMappingURL=render-
|
|
577
|
+
//# sourceMappingURL=render-2P4YWHXV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/serve/dashboard/render.tsx","../src/commands/serve/dashboard/ServeDashboard.tsx","../src/commands/serve/dashboard/components/ServerHeader.tsx","../src/commands/serve/dashboard/components/WorkspaceInfo.tsx","../src/commands/serve/dashboard/components/StatsBar.tsx","../src/commands/serve/dashboard/components/BackgroundTasks.tsx","../src/commands/serve/dashboard/components/PluginStatusPanel.tsx","../src/commands/serve/dashboard/components/ActivityLog.tsx","../src/commands/serve/dashboard/components/HelpBar.tsx","../src/commands/serve/dashboard/components/OllamaStatus.tsx"],"sourcesContent":["/**\n * Render the dashboard using Ink\n */\n\nimport React from \"react\";\nimport { render } from \"ink\";\nimport { ServeDashboard } from \"./ServeDashboard.js\";\nimport { getDashboardStore } from \"./store.js\";\n\nexport interface RenderOptions {\n onQuit?: () => void;\n onRebuildIndex?: () => void;\n}\n\n/**\n * Intercept console.log/error/warn so stray output from ESLint rules,\n * child processes, etc. is routed to the dashboard activity log instead\n * of corrupting the Ink terminal UI. Returns a restore function.\n */\nfunction interceptConsole(): () => void {\n const origLog = console.log;\n const origError = console.error;\n const origWarn = console.warn;\n\n const route = (type: \"info\" | \"error\" | \"warning\", args: unknown[]) => {\n const message = args.map(String).join(\" \");\n if (!message.trim()) return;\n getDashboardStore().addActivity({\n type,\n message,\n isError: type === \"error\",\n isWarning: type === \"warning\",\n });\n };\n\n console.log = (...args: unknown[]) => route(\"info\", args);\n console.error = (...args: unknown[]) => route(\"error\", args);\n console.warn = (...args: unknown[]) => route(\"warning\", args);\n\n return () => {\n console.log = origLog;\n console.error = origError;\n console.warn = origWarn;\n };\n}\n\n/**\n * Render the dashboard and return cleanup function\n */\nexport function renderDashboard(options: RenderOptions = {}): {\n unmount: () => void;\n waitUntilExit: () => Promise<void>;\n} {\n // Clear the terminal so prior output doesn't mix with the Ink dashboard\n process.stdout.write(\"\\x1B[2J\\x1B[H\");\n\n // Intercept console methods so stray output goes to the activity log\n const restoreConsole = interceptConsole();\n\n const { unmount: inkUnmount, waitUntilExit: inkWaitUntilExit } = render(\n <ServeDashboard\n onQuit={options.onQuit}\n onRebuildIndex={options.onRebuildIndex}\n />\n );\n\n return {\n unmount: () => {\n restoreConsole();\n inkUnmount();\n },\n waitUntilExit: () => inkWaitUntilExit().finally(restoreConsole),\n };\n}\n","/**\n * ServeDashboard - main Ink component for the WebSocket server CLI dashboard\n */\n\nimport React, { useState, useEffect, useCallback } from \"react\";\nimport { Box, useInput, useApp } from \"ink\";\nimport {\n ServerHeader,\n WorkspaceInfo,\n StatsBar,\n PluginStatusPanel,\n ActivityLog,\n HelpBar,\n} from \"./components/index.js\";\nimport type { DashboardState } from \"./types.js\";\nimport { getDashboardStore } from \"./store.js\";\n\nexport interface ServeDashboardProps {\n /** Callback when user requests to quit */\n onQuit?: () => void;\n /** Callback when user requests to rebuild index */\n onRebuildIndex?: () => void;\n}\n\nexport function ServeDashboard({\n onQuit,\n onRebuildIndex,\n}: ServeDashboardProps): React.ReactElement {\n const { exit } = useApp();\n const store = getDashboardStore();\n\n // Subscribe to store updates\n const [state, setState] = useState<DashboardState>(store.getState());\n const [scrollOffset, setScrollOffset] = useState(0);\n\n useEffect(() => {\n const unsubscribe = store.subscribe(() => {\n setState(store.getState());\n });\n return unsubscribe;\n }, [store]);\n\n // Clamp scrollOffset when activities change\n useEffect(() => {\n setScrollOffset((prev) => {\n const maxOffset = Math.max(0, state.activities.length - 15);\n return Math.min(prev, maxOffset);\n });\n }, [state.activities.length]);\n\n // Check Ollama status periodically\n useEffect(() => {\n const checkOllamaStatus = async () => {\n try {\n const response = await fetch(\"http://localhost:11434/api/tags\", {\n method: \"GET\",\n signal: AbortSignal.timeout(5000),\n });\n\n if (response.ok) {\n const data = (await response.json()) as { models?: Array<{ name?: string }> };\n const models = data.models || [];\n // Get the first model name as the \"active\" model indicator\n const modelName = models.length > 0 ? models[0].name : undefined;\n store.setOllamaStatus({\n status: \"connected\",\n model: modelName,\n lastChecked: new Date(),\n });\n } else {\n store.setOllamaStatus({\n status: \"error\",\n lastChecked: new Date(),\n });\n }\n } catch {\n store.setOllamaStatus({\n status: \"offline\",\n lastChecked: new Date(),\n });\n }\n };\n\n // Initial check\n checkOllamaStatus();\n\n // Check every 30 seconds\n const interval = setInterval(checkOllamaStatus, 30000);\n return () => clearInterval(interval);\n }, [store]);\n\n // Handle keyboard input\n useInput(\n useCallback(\n (input, key) => {\n if (input === \"q\" || (key.ctrl && input === \"c\")) {\n onQuit?.();\n exit();\n } else if (input === \"c\") {\n store.clearActivities();\n setScrollOffset(0);\n } else if (input === \"v\") {\n store.toggleVerbose();\n } else if (input === \"f\") {\n store.cycleFilter();\n setScrollOffset(0);\n } else if (input === \"r\") {\n onRebuildIndex?.();\n } else if (key.downArrow || input === \"j\") {\n setScrollOffset((prev) => {\n const maxOffset = Math.max(0, state.activities.length - 15);\n return Math.min(maxOffset, prev + 1);\n });\n } else if (key.upArrow || input === \"k\") {\n setScrollOffset((prev) => Math.max(0, prev - 1));\n }\n },\n [exit, onQuit, onRebuildIndex, store, state.activities.length]\n )\n );\n\n return (\n <Box flexDirection=\"column\" width=\"100%\">\n <ServerHeader port={state.port} isRunning={state.isRunning} />\n\n <WorkspaceInfo\n workspaceRoot={state.workspace?.workspaceRoot ?? null}\n appRoot={state.workspace?.appRoot ?? null}\n />\n\n <StatsBar\n connectedClients={state.stats.connectedClients}\n subscriptions={state.stats.subscriptions}\n cacheEntries={state.stats.cacheEntries}\n startTime={state.stats.startTime}\n ollamaStatus={state.ollamaStatus.status}\n ollamaModel={state.ollamaStatus.model}\n />\n\n <PluginStatusPanel plugins={state.pluginStates} />\n\n <ActivityLog\n activities={state.activities}\n maxVisible={15}\n verbose={state.verbose}\n scrollOffset={scrollOffset}\n activeFilter={state.activeFilter}\n />\n\n <HelpBar verbose={state.verbose} activeFilter={state.activeFilter} />\n </Box>\n );\n}\n","/**\n * ServerHeader component - displays server status and URL\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport interface ServerHeaderProps {\n port: number;\n isRunning: boolean;\n}\n\nexport function ServerHeader({\n port,\n isRunning,\n}: ServerHeaderProps): React.ReactElement {\n const url = `ws://localhost:${port}`;\n const statusColor = isRunning ? \"green\" : \"red\";\n const statusIcon = isRunning ? \"\\u25CF\" : \"\\u25CB\"; // Filled/hollow circle\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"cyan\"\n paddingX={1}\n justifyContent=\"space-between\"\n >\n <Text bold color=\"cyan\">\n UILint Server\n </Text>\n <Box>\n <Text dimColor>{url}</Text>\n <Text> </Text>\n <Text color={statusColor}>{statusIcon}</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * WorkspaceInfo component - displays workspace and app root paths\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\n\nexport interface WorkspaceInfoProps {\n workspaceRoot: string | null;\n appRoot: string | null;\n}\n\nfunction truncatePath(path: string, maxLen: number = 60): string {\n if (path.length <= maxLen) return path;\n return \"...\" + path.slice(-(maxLen - 3));\n}\n\nexport function WorkspaceInfo({\n workspaceRoot,\n appRoot,\n}: WorkspaceInfoProps): React.ReactElement {\n if (!workspaceRoot && !appRoot) {\n return (\n <Box paddingX={1}>\n <Text dimColor>Initializing...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingX={1}>\n <Box>\n <Text dimColor>Workspace: </Text>\n <Text>{truncatePath(workspaceRoot || \"\")}</Text>\n </Box>\n {appRoot && appRoot !== workspaceRoot && (\n <Box>\n <Text dimColor>App Root: </Text>\n <Text>{truncatePath(appRoot)}</Text>\n </Box>\n )}\n </Box>\n );\n}\n","/**\n * StatsBar component - displays server statistics\n */\n\nimport React, { useState, useEffect } from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { OllamaStatusState } from \"../types.js\";\n\nexport interface StatsBarProps {\n connectedClients: number;\n subscriptions: number;\n cacheEntries: number;\n startTime: Date;\n ollamaStatus?: OllamaStatusState;\n ollamaModel?: string;\n}\n\nfunction formatUptime(startTime: Date): string {\n const diff = Date.now() - startTime.getTime();\n const seconds = Math.floor(diff / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n\n if (hours > 0) {\n return `${hours}h ${minutes % 60}m`;\n }\n if (minutes > 0) {\n return `${minutes}m ${seconds % 60}s`;\n }\n return `${seconds}s`;\n}\n\nconst ollamaStatusConfig: Record<\n OllamaStatusState,\n { icon: string; color: string }\n> = {\n checking: { icon: \"\\u25CB\", color: \"gray\" },\n connected: { icon: \"\\u25CF\", color: \"green\" },\n offline: { icon: \"\\u25CF\", color: \"red\" },\n error: { icon: \"\\u25CF\", color: \"yellow\" },\n};\n\nexport function StatsBar({\n connectedClients,\n subscriptions,\n cacheEntries,\n startTime,\n ollamaStatus,\n ollamaModel,\n}: StatsBarProps): React.ReactElement {\n const [, setTick] = useState(0);\n\n // Update uptime display every second\n useEffect(() => {\n const timer = setInterval(() => {\n setTick((t) => t + 1);\n }, 1000);\n return () => clearInterval(timer);\n }, []);\n\n const ollamaConfig = ollamaStatus\n ? ollamaStatusConfig[ollamaStatus]\n : ollamaStatusConfig.checking;\n\n return (\n <Box\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n justifyContent=\"space-between\"\n >\n <Box gap={2}>\n <Box>\n <Text dimColor>Clients: </Text>\n <Text bold color={connectedClients > 0 ? \"green\" : \"gray\"}>\n {connectedClients}\n </Text>\n </Box>\n <Box>\n <Text dimColor>Subs: </Text>\n <Text>{subscriptions}</Text>\n </Box>\n <Box>\n <Text dimColor>Ollama: </Text>\n <Text color={ollamaConfig.color}>{ollamaConfig.icon}</Text>\n {ollamaModel && ollamaStatus === \"connected\" && (\n <Text dimColor> {ollamaModel}</Text>\n )}\n </Box>\n </Box>\n <Box gap={2}>\n <Box>\n <Text dimColor>Cache: </Text>\n <Text>{cacheEntries}</Text>\n </Box>\n <Box>\n <Text dimColor>Uptime: </Text>\n <Text>{formatUptime(startTime)}</Text>\n </Box>\n </Box>\n </Box>\n );\n}\n","/**\n * BackgroundTasks component - displays background task progress\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { BackgroundTask } from \"../types.js\";\n\nexport interface BackgroundTasksProps {\n tasks: Map<string, BackgroundTask>;\n}\n\nfunction ProgressBar({\n progress,\n width = 20,\n}: {\n progress: number;\n width?: number;\n}): React.ReactElement {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n\n return (\n <Text>\n <Text color=\"cyan\">{\"\\u2588\".repeat(filled)}</Text>\n <Text dimColor>{\"\\u2591\".repeat(empty)}</Text>\n </Text>\n );\n}\n\nfunction TaskRow({ task }: { task: BackgroundTask }): React.ReactElement {\n const statusIcon = {\n idle: \"\\u25CB\", // hollow circle\n running: \"\\u25CF\", // filled circle\n complete: \"\\u2713\", // checkmark\n error: \"\\u2717\", // x mark\n }[task.status];\n\n const statusColor = {\n idle: \"gray\",\n running: \"cyan\",\n complete: \"green\",\n error: \"red\",\n }[task.status] as \"gray\" | \"cyan\" | \"green\" | \"red\";\n\n return (\n <Box>\n <Text color={statusColor}>{statusIcon} </Text>\n <Text>{task.name}: </Text>\n {task.status === \"running\" && task.progress !== undefined ? (\n <Box gap={1}>\n <ProgressBar progress={task.progress} width={15} />\n <Text dimColor>\n {task.progress.toFixed(0)}%\n {task.current !== undefined && task.total !== undefined\n ? ` (${task.current}/${task.total})`\n : \"\"}\n </Text>\n </Box>\n ) : task.status === \"complete\" ? (\n <Text color=\"green\">Complete</Text>\n ) : task.status === \"error\" ? (\n <Text color=\"red\">{task.error || \"Failed\"}</Text>\n ) : (\n <Text dimColor>{task.message || \"Idle\"}</Text>\n )}\n </Box>\n );\n}\n\nexport function BackgroundTasks({\n tasks,\n}: BackgroundTasksProps): React.ReactElement | null {\n const taskArray = Array.from(tasks.values());\n\n if (taskArray.length === 0) {\n return null;\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n >\n <Text bold dimColor>\n Background Tasks\n </Text>\n {taskArray.map((task) => (\n <TaskRow key={task.id} task={task} />\n ))}\n </Box>\n );\n}\n","/**\n * PluginStatusPanel - persistent at-a-glance view of Ollama plugin states.\n *\n * Shows each plugin's lifecycle status, model, and progress in the CLI dashboard.\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { PluginState, PluginLifecycle } from \"../types.js\";\n\nexport interface PluginStatusPanelProps {\n plugins: Map<string, PluginState>;\n}\n\nconst statusDisplay: Record<\n PluginLifecycle,\n { icon: string; color: string; label: string }\n> = {\n idle: { icon: \"\\u25CB\", color: \"gray\", label: \"Idle\" },\n \"waiting-for-ollama\": { icon: \"\\u25CC\", color: \"yellow\", label: \"Queued\" },\n \"using-ollama\": { icon: \"\\u25CF\", color: \"cyan\", label: \"Using Ollama\" },\n processing: { icon: \"\\u25CF\", color: \"blue\", label: \"Processing\" },\n complete: { icon: \"\\u2713\", color: \"green\", label: \"Complete\" },\n error: { icon: \"\\u2717\", color: \"red\", label: \"Error\" },\n};\n\nfunction ProgressBar({\n progress,\n width = 12,\n}: {\n progress: number;\n width?: number;\n}): React.ReactElement {\n const filled = Math.round((progress / 100) * width);\n const empty = width - filled;\n return (\n <Text>\n <Text color=\"cyan\">{\"\\u2588\".repeat(filled)}</Text>\n <Text dimColor>{\"\\u2591\".repeat(empty)}</Text>\n </Text>\n );\n}\n\nfunction truncate(str: string, maxLen: number): string {\n if (str.length <= maxLen) return str;\n return str.slice(0, maxLen - 1) + \"\\u2026\";\n}\n\nfunction PluginRow({\n plugin,\n}: {\n plugin: PluginState;\n}): React.ReactElement {\n const display = statusDisplay[plugin.status];\n\n return (\n <Box>\n {/* Status icon */}\n <Text color={display.color}>{display.icon} </Text>\n\n {/* Plugin name — fixed width */}\n <Box width={13}>\n <Text bold={plugin.status !== \"idle\"}>{plugin.name}</Text>\n </Box>\n\n {/* Status label */}\n <Box width={16}>\n <Text color={display.color}>{display.label}</Text>\n </Box>\n\n {/* Model */}\n <Text dimColor>({plugin.model})</Text>\n\n {/* Progress bar for active states */}\n {(plugin.status === \"waiting-for-ollama\" ||\n plugin.status === \"using-ollama\" ||\n plugin.status === \"processing\") &&\n plugin.progress !== undefined && (\n <Box marginLeft={1} gap={1}>\n <ProgressBar progress={plugin.progress} />\n <Text dimColor>\n {plugin.progress.toFixed(0)}%\n {plugin.current !== undefined && plugin.total !== undefined\n ? ` (${plugin.current}/${plugin.total})`\n : \"\"}\n </Text>\n </Box>\n )}\n\n {/* Message */}\n {plugin.message && plugin.status !== \"idle\" && (\n <Text dimColor>{\" \"}{truncate(plugin.message, 40)}</Text>\n )}\n\n {/* Error */}\n {plugin.status === \"error\" && plugin.error && (\n <Text color=\"red\">{\" \"}{truncate(plugin.error, 40)}</Text>\n )}\n </Box>\n );\n}\n\nexport function PluginStatusPanel({\n plugins,\n}: PluginStatusPanelProps): React.ReactElement | null {\n const pluginArray = Array.from(plugins.values());\n\n if (pluginArray.length === 0) {\n return null;\n }\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n >\n <Text bold dimColor>\n Plugins\n </Text>\n {pluginArray.map((plugin) => (\n <PluginRow key={plugin.id} plugin={plugin} />\n ))}\n </Box>\n );\n}\n","/**\n * ActivityLog component - displays recent server activity\n *\n * Supports filtering by category (errors, vision, semantic, lint)\n * and auto-expanding error details.\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityEntry, ActivityType, ActivityCategory } from \"../types.js\";\n\nexport interface ActivityLogProps {\n activities: ActivityEntry[];\n maxVisible?: number;\n verbose?: boolean;\n scrollOffset?: number;\n activeFilter?: ActivityCategory;\n}\n\nconst filterLabels: Record<ActivityCategory, string> = {\n all: \"All\",\n errors: \"Errors\",\n vision: \"Vision\",\n semantic: \"Semantic\",\n lint: \"Lint\",\n system: \"System\",\n};\n\nfunction formatTime(date: Date): string {\n return date.toLocaleTimeString(\"en-US\", {\n hour12: false,\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n}\n\n/** Strip ANSI escape sequences from a string */\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\x1B\\[[0-9;]*[a-zA-Z]/g, \"\").replace(/\\r/g, \"\");\n}\n\n/** Truncate a string to maxLen, appending \"…\" if truncated */\nfunction truncate(str: string, maxLen: number): string {\n const clean = stripAnsi(str).replace(/\\n/g, \" \");\n if (clean.length <= maxLen) return clean;\n return clean.slice(0, maxLen - 1) + \"…\";\n}\n\ntype InkColor =\n | \"black\" | \"red\" | \"green\" | \"yellow\" | \"blue\"\n | \"magenta\" | \"cyan\" | \"white\" | \"gray\" | \"grey\";\n\nfunction getTypeDisplay(type: ActivityType): { label: string; color: InkColor } {\n const displays: Record<ActivityType, { label: string; color: InkColor }> = {\n \"lint:file\": { label: \"lint\", color: \"blue\" },\n \"lint:element\": { label: \"lint\", color: \"blue\" },\n \"lint:done\": { label: \"lint\", color: \"green\" },\n subscribe: { label: \"sub\", color: \"cyan\" },\n \"cache:invalidate\": { label: \"cache\", color: \"yellow\" },\n \"vision:analyze\": { label: \"vision\", color: \"magenta\" },\n \"vision:done\": { label: \"vision\", color: \"green\" },\n \"vision:check\": { label: \"vision\", color: \"magenta\" },\n \"semantic:analyze\": { label: \"semant\", color: \"blue\" },\n \"semantic:done\": { label: \"semant\", color: \"green\" },\n \"semantic:error\": { label: \"semant\", color: \"red\" },\n \"semantic:skip\": { label: \"semant\", color: \"yellow\" },\n \"config:set\": { label: \"config\", color: \"yellow\" },\n \"rule:config:set\": { label: \"rule\", color: \"yellow\" },\n \"screenshot:save\": { label: \"screen\", color: \"cyan\" },\n \"screenshot:saved\": { label: \"screen\", color: \"green\" },\n \"coverage:request\": { label: \"cov\", color: \"blue\" },\n \"coverage:result\": { label: \"cov\", color: \"green\" },\n \"file:changed\": { label: \"change\", color: \"yellow\" },\n \"client:connect\": { label: \"client\", color: \"green\" },\n \"client:disconnect\": { label: \"client\", color: \"red\" },\n error: { label: \"error\", color: \"red\" },\n warning: { label: \"warn\", color: \"yellow\" },\n info: { label: \"info\", color: \"gray\" },\n };\n\n return displays[type] || { label: type, color: \"gray\" };\n}\n\n// Max width for message/detail lines (leaves room for time + type label + padding)\nconst MAX_MSG_WIDTH = 120;\nconst MAX_DETAIL_WIDTH = 100;\n\nfunction ActivityRow({\n entry,\n verbose,\n}: {\n entry: ActivityEntry;\n verbose?: boolean;\n}): React.ReactElement {\n const { label, color } = getTypeDisplay(entry.type);\n\n // Always show detail for errors, otherwise respect verbose mode\n const showDetail = (verbose || entry.isError) && entry.detail;\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text dimColor>{formatTime(entry.timestamp)} </Text>\n <Text color={color} bold>\n {label.padEnd(7)}\n </Text>\n <Text color={entry.isError ? \"red\" : entry.isWarning ? \"yellow\" : undefined}>\n {truncate(entry.message, MAX_MSG_WIDTH)}\n </Text>\n </Box>\n {showDetail && (\n <Box paddingLeft={16}>\n <Text dimColor>{truncate(entry.detail!, MAX_DETAIL_WIDTH)}</Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport function ActivityLog({\n activities,\n maxVisible = 15,\n verbose = false,\n scrollOffset = 0,\n activeFilter = \"all\",\n}: ActivityLogProps): React.ReactElement {\n // Apply category filter\n const filtered =\n activeFilter !== \"all\"\n ? activities.filter((a) => a.category === activeFilter)\n : activities;\n\n const total = filtered.length;\n const start = Math.min(scrollOffset, Math.max(0, total - 1));\n const end = Math.min(start + maxVisible, total);\n const visibleActivities = filtered.slice(start, end);\n\n const isScrolled = start > 0;\n const hasMoreBelow = end < total;\n\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"single\"\n borderColor=\"gray\"\n paddingX={1}\n flexGrow={1}\n >\n <Box justifyContent=\"space-between\">\n <Box gap={1}>\n <Text bold dimColor>\n Activity\n </Text>\n {activeFilter !== \"all\" && (\n <Text color=\"yellow\">[{filterLabels[activeFilter]}]</Text>\n )}\n </Box>\n <Box gap={1}>\n {isScrolled && (\n <Text color=\"cyan\">{\"\\u2191\"}</Text>\n )}\n {total > maxVisible && (\n <Text dimColor>\n {start + 1}-{end} of {total}\n </Text>\n )}\n {hasMoreBelow && (\n <Text color=\"cyan\">{\"\\u2193\"}</Text>\n )}\n </Box>\n </Box>\n {visibleActivities.length === 0 ? (\n <Text dimColor>\n {activeFilter !== \"all\"\n ? `No ${filterLabels[activeFilter].toLowerCase()} activity yet...`\n : \"No activity yet...\"}\n </Text>\n ) : (\n visibleActivities.map((entry) => (\n <ActivityRow key={entry.id} entry={entry} verbose={verbose} />\n ))\n )}\n </Box>\n );\n}\n","/**\n * HelpBar component - displays keyboard shortcuts\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { ActivityCategory } from \"../types.js\";\n\nexport interface HelpBarProps {\n verbose: boolean;\n activeFilter?: ActivityCategory;\n}\n\nconst filterLabels: Record<ActivityCategory, string> = {\n all: \"all\",\n errors: \"errors\",\n vision: \"vision\",\n semantic: \"semantic\",\n lint: \"lint\",\n system: \"system\",\n};\n\nexport function HelpBar({ verbose, activeFilter = \"all\" }: HelpBarProps): React.ReactElement {\n return (\n <Box borderStyle=\"single\" borderColor=\"gray\" paddingX={1} gap={2}>\n <Box>\n <Text bold color=\"cyan\">\n q\n </Text>\n <Text dimColor> quit</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n c\n </Text>\n <Text dimColor> clear log</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n v\n </Text>\n <Text dimColor> verbose {verbose ? \"(on)\" : \"(off)\"}</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n f\n </Text>\n <Text dimColor> filter ({filterLabels[activeFilter]})</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n r\n </Text>\n <Text dimColor> rebuild index</Text>\n </Box>\n <Box>\n <Text bold color=\"cyan\">\n {\"\\u2191\\u2193\"}\n </Text>\n <Text dimColor> scroll</Text>\n </Box>\n </Box>\n );\n}\n","/**\n * OllamaStatus component - displays Ollama connection status\n */\n\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport type { OllamaStatusState } from \"../types.js\";\n\nexport interface OllamaStatusProps {\n status: OllamaStatusState;\n model?: string;\n}\n\nconst statusConfig: Record<\n OllamaStatusState,\n { icon: string; color: string; label: string }\n> = {\n checking: { icon: \"\\u25CB\", color: \"gray\", label: \"checking\" },\n connected: { icon: \"\\u25CF\", color: \"green\", label: \"connected\" },\n offline: { icon: \"\\u25CF\", color: \"red\", label: \"offline\" },\n error: { icon: \"\\u25CF\", color: \"red\", label: \"error\" },\n};\n\nexport function OllamaStatus({\n status,\n model,\n}: OllamaStatusProps): React.ReactElement {\n const config = statusConfig[status];\n\n return (\n <Box gap={1}>\n <Text dimColor>Ollama:</Text>\n <Text color={config.color}>{config.icon}</Text>\n <Text color={config.color}>{config.label}</Text>\n {model && status === \"connected\" && (\n <Text dimColor>({model})</Text>\n )}\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","useEffect","Box","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","truncate","Box","Text","jsx","jsxs","filterLabels","Box","Text","jsx","jsxs","jsx","jsxs","useState","useEffect","Box","jsx"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
discoverPlugins,
|
|
4
4
|
loadPluginESLintRules
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5QUW7BNW.js";
|
|
6
6
|
import {
|
|
7
7
|
getInstalledRuleVersions,
|
|
8
8
|
updateManifestRule
|
|
@@ -590,4 +590,4 @@ async function upgrade(options) {
|
|
|
590
590
|
export {
|
|
591
591
|
upgrade
|
|
592
592
|
};
|
|
593
|
-
//# sourceMappingURL=upgrade-
|
|
593
|
+
//# sourceMappingURL=upgrade-2UKW3SIQ.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uilint",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.147",
|
|
4
4
|
"description": "CLI for UILint - AI-powered UI consistency checking",
|
|
5
5
|
"author": "Peter Suggate",
|
|
6
6
|
"repository": {
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
"react": "^19.2.3",
|
|
50
50
|
"typescript": "^5.9.3",
|
|
51
51
|
"ws": "^8.19.0",
|
|
52
|
-
"uilint-core": "0.2.
|
|
53
|
-
"uilint-
|
|
54
|
-
"uilint-
|
|
52
|
+
"uilint-core": "0.2.147",
|
|
53
|
+
"uilint-duplicates": "0.2.147",
|
|
54
|
+
"uilint-eslint": "0.2.147"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"uilint-vision": "0.2.
|
|
58
|
-
"uilint-semantic": "0.2.
|
|
59
|
-
"uilint-duplicates": "0.2.
|
|
57
|
+
"uilint-vision": "0.2.147",
|
|
58
|
+
"uilint-semantic": "0.2.147",
|
|
59
|
+
"uilint-duplicates": "0.2.147"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
62
62
|
"uilint-vision": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"ink-testing-library": "^4.0.0",
|
|
84
84
|
"tsup": "^8.5.1",
|
|
85
85
|
"vitest": "^4.0.17",
|
|
86
|
-
"uilint-react": "0.2.
|
|
86
|
+
"uilint-react": "0.2.147"
|
|
87
87
|
},
|
|
88
88
|
"keywords": [
|
|
89
89
|
"cli",
|
package/dist/chunk-WG2WZTB2.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
logInfo
|
|
4
|
-
} from "./chunk-CZNPG4UI.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/plugin-loader.ts
|
|
7
|
-
import { createRequire } from "module";
|
|
8
|
-
import { join } from "path";
|
|
9
|
-
var KNOWN_PLUGIN_PACKAGES = ["uilint-vision", "uilint-semantic", "uilint-duplicates"];
|
|
10
|
-
async function discoverPlugins(resolveFrom) {
|
|
11
|
-
const manifests = [];
|
|
12
|
-
for (const pkg of KNOWN_PLUGIN_PACKAGES) {
|
|
13
|
-
const specifier = `${pkg}/cli-manifest`;
|
|
14
|
-
try {
|
|
15
|
-
let mod;
|
|
16
|
-
if (resolveFrom) {
|
|
17
|
-
const req = createRequire(join(resolveFrom, "package.json"));
|
|
18
|
-
const resolved = req.resolve(specifier);
|
|
19
|
-
mod = await import(resolved);
|
|
20
|
-
} else {
|
|
21
|
-
mod = await import(specifier);
|
|
22
|
-
}
|
|
23
|
-
if (mod.cliManifest) {
|
|
24
|
-
manifests.push(mod.cliManifest);
|
|
25
|
-
}
|
|
26
|
-
} catch {
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return manifests;
|
|
30
|
-
}
|
|
31
|
-
async function loadPluginESLintRules(manifests, resolveFrom) {
|
|
32
|
-
const loaded = [];
|
|
33
|
-
for (const manifest of manifests) {
|
|
34
|
-
try {
|
|
35
|
-
if (resolveFrom) {
|
|
36
|
-
const req = createRequire(join(resolveFrom, "package.json"));
|
|
37
|
-
const resolved = req.resolve(manifest.registerSpecifier);
|
|
38
|
-
await import(resolved);
|
|
39
|
-
} else {
|
|
40
|
-
await import(manifest.registerSpecifier);
|
|
41
|
-
}
|
|
42
|
-
loaded.push(manifest.packageName);
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (loaded.length > 0) {
|
|
47
|
-
logInfo(`Loaded plugin rules: ${loaded.join(", ")}`);
|
|
48
|
-
}
|
|
49
|
-
return loaded;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
discoverPlugins,
|
|
54
|
-
loadPluginESLintRules
|
|
55
|
-
};
|
|
56
|
-
//# sourceMappingURL=chunk-WG2WZTB2.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/plugin-loader.ts"],"sourcesContent":["/**\n * Dynamic plugin loader for the CLI.\n *\n * Probes known plugin package names for a `cli-manifest` subpath export.\n * Each manifest describes the CLI flag, help text, and registration entry\n * point, keeping the core CLI free of plugin-specific knowledge.\n */\n\nimport { createRequire } from \"module\";\nimport { join } from \"path\";\nimport { logInfo } from \"./prompts.js\";\n\n/**\n * Metadata a plugin exposes via its `<pkg>/cli-manifest` subpath export.\n * Plugins define a plain object matching this shape — no shared type import needed.\n */\nexport interface PluginCLIManifest {\n /** npm package name, e.g. \"uilint-vision\" */\n packageName: string;\n /** CLI flag name (without --), e.g. \"vision\" */\n cliFlag: string;\n /** Description shown in --help */\n cliDescription: string;\n /** Import specifier for the register module, e.g. \"uilint-vision/eslint-rules/register\" */\n registerSpecifier: string;\n}\n\n/** Package names to probe for CLI manifests */\nconst KNOWN_PLUGIN_PACKAGES = [\"uilint-vision\", \"uilint-semantic\", \"uilint-duplicates\"];\n\n/**\n * Discover available plugin manifests by probing `<pkg>/cli-manifest`.\n *\n * @param resolveFrom - Optional project path to resolve plugins from.\n * When provided, uses createRequire anchored to the project's package.json\n * so plugins installed in the project's node_modules can be found.\n * @returns Array of discovered plugin manifests\n */\nexport async function discoverPlugins(\n resolveFrom?: string,\n): Promise<PluginCLIManifest[]> {\n const manifests: PluginCLIManifest[] = [];\n\n for (const pkg of KNOWN_PLUGIN_PACKAGES) {\n const specifier = `${pkg}/cli-manifest`;\n try {\n let mod: { cliManifest?: PluginCLIManifest };\n if (resolveFrom) {\n const req = createRequire(join(resolveFrom, \"package.json\"));\n const resolved = req.resolve(specifier);\n mod = (await import(resolved)) as typeof mod;\n } else {\n mod = (await import(specifier)) as typeof mod;\n }\n if (mod.cliManifest) {\n manifests.push(mod.cliManifest);\n }\n } catch {\n // Plugin not installed — skip silently\n }\n }\n\n return manifests;\n}\n\n/**\n * Load ESLint rules from discovered plugins by importing their register modules.\n *\n * @param manifests - Plugin manifests (from discoverPlugins)\n * @param resolveFrom - Optional project path to resolve plugins from.\n * @returns Array of loaded plugin package names\n */\nexport async function loadPluginESLintRules(\n manifests: PluginCLIManifest[],\n resolveFrom?: string,\n): Promise<string[]> {\n const loaded: string[] = [];\n\n for (const manifest of manifests) {\n try {\n if (resolveFrom) {\n const req = createRequire(join(resolveFrom, \"package.json\"));\n const resolved = req.resolve(manifest.registerSpecifier);\n await import(resolved);\n } else {\n await import(manifest.registerSpecifier);\n }\n loaded.push(manifest.packageName);\n } catch {\n // Plugin register module not available — skip silently\n }\n }\n\n if (loaded.length > 0) {\n logInfo(`Loaded plugin rules: ${loaded.join(\", \")}`);\n }\n\n return loaded;\n}\n"],"mappings":";;;;;;AAQA,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AAmBrB,IAAM,wBAAwB,CAAC,iBAAiB,mBAAmB,mBAAmB;AAUtF,eAAsB,gBACpB,aAC8B;AAC9B,QAAM,YAAiC,CAAC;AAExC,aAAW,OAAO,uBAAuB;AACvC,UAAM,YAAY,GAAG,GAAG;AACxB,QAAI;AACF,UAAI;AACJ,UAAI,aAAa;AACf,cAAM,MAAM,cAAc,KAAK,aAAa,cAAc,CAAC;AAC3D,cAAM,WAAW,IAAI,QAAQ,SAAS;AACtC,cAAO,MAAM,OAAO;AAAA,MACtB,OAAO;AACL,cAAO,MAAM,OAAO;AAAA,MACtB;AACA,UAAI,IAAI,aAAa;AACnB,kBAAU,KAAK,IAAI,WAAW;AAAA,MAChC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AASA,eAAsB,sBACpB,WACA,aACmB;AACnB,QAAM,SAAmB,CAAC;AAE1B,aAAW,YAAY,WAAW;AAChC,QAAI;AACF,UAAI,aAAa;AACf,cAAM,MAAM,cAAc,KAAK,aAAa,cAAc,CAAC;AAC3D,cAAM,WAAW,IAAI,QAAQ,SAAS,iBAAiB;AACvD,cAAM,OAAO;AAAA,MACf,OAAO;AACL,cAAM,OAAO,SAAS;AAAA,MACxB;AACA,aAAO,KAAK,SAAS,WAAW;AAAA,IAClC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,wBAAwB,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EACrD;AAEA,SAAO;AACT;","names":[]}
|