uilint 0.2.58 → 0.2.60

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/index.js CHANGED
@@ -1404,7 +1404,7 @@ async function update(options) {
1404
1404
  }
1405
1405
 
1406
1406
  // src/commands/serve.ts
1407
- import { existsSync as existsSync5, statSync as statSync3, readdirSync, readFileSync } from "fs";
1407
+ import { existsSync as existsSync5, statSync as statSync3, readdirSync, readFileSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
1408
1408
  import { createRequire } from "module";
1409
1409
  import { dirname as dirname5, resolve as resolve5, relative, join as join3, parse as parse2 } from "path";
1410
1410
  import { WebSocketServer, WebSocket } from "ws";
@@ -1897,7 +1897,13 @@ async function handleMessage(ws, data) {
1897
1897
  )}`
1898
1898
  );
1899
1899
  } else if (message.type === "vision:analyze") {
1900
+ } else if (message.type === "vision:check") {
1900
1901
  } else if (message.type === "config:set") {
1902
+ } else if (message.type === "screenshot:save") {
1903
+ const rid = message.requestId;
1904
+ logInfo(
1905
+ `${pc.dim("[ws]")} ${pc.bold("screenshot:save")} ${pc.dim(message.route)}${rid ? ` ${pc.dim(`(req ${rid})`)}` : ""}`
1906
+ );
1901
1907
  }
1902
1908
  switch (message.type) {
1903
1909
  case "lint:file": {
@@ -2159,6 +2165,29 @@ ${stack}` : ""
2159
2165
  }
2160
2166
  break;
2161
2167
  }
2168
+ case "vision:check": {
2169
+ const { requestId } = message;
2170
+ logInfo(
2171
+ `${pc.dim("[ws]")} ${pc.bold("vision:check")}${requestId ? ` ${pc.dim(`(req ${requestId})`)}` : ""}`
2172
+ );
2173
+ try {
2174
+ const analyzer = getVisionAnalyzerInstance();
2175
+ const model = typeof analyzer.getModel === "function" ? analyzer.getModel() : void 0;
2176
+ sendMessage(ws, {
2177
+ type: "vision:status",
2178
+ available: true,
2179
+ model,
2180
+ requestId
2181
+ });
2182
+ } catch (error) {
2183
+ sendMessage(ws, {
2184
+ type: "vision:status",
2185
+ available: false,
2186
+ requestId
2187
+ });
2188
+ }
2189
+ break;
2190
+ }
2162
2191
  case "config:set": {
2163
2192
  const { key, value } = message;
2164
2193
  handleConfigSet(key, value);
@@ -2234,6 +2263,75 @@ ${stack}` : ""
2234
2263
  }
2235
2264
  break;
2236
2265
  }
2266
+ case "screenshot:save": {
2267
+ const { dataUrl, route, timestamp, requestId } = message;
2268
+ try {
2269
+ if (!dataUrl || typeof dataUrl !== "string") {
2270
+ sendMessage(ws, {
2271
+ type: "screenshot:error",
2272
+ error: "Invalid dataUrl: must be a non-empty string",
2273
+ requestId
2274
+ });
2275
+ break;
2276
+ }
2277
+ const dataUrlPattern = /^data:image\/png;base64,/;
2278
+ if (!dataUrlPattern.test(dataUrl)) {
2279
+ sendMessage(ws, {
2280
+ type: "screenshot:error",
2281
+ error: "Invalid dataUrl: must be a base64 PNG data URL (data:image/png;base64,...)",
2282
+ requestId
2283
+ });
2284
+ break;
2285
+ }
2286
+ const base64Data = dataUrl.replace(dataUrlPattern, "");
2287
+ const sanitizedRoute = route.replace(/^\//, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9_-]/g, "_") || "root";
2288
+ const filename = `uilint-${timestamp}-${sanitizedRoute}.png`;
2289
+ if (!isValidScreenshotFilename(filename)) {
2290
+ sendMessage(ws, {
2291
+ type: "screenshot:error",
2292
+ error: `Generated filename is invalid: ${filename}`,
2293
+ requestId
2294
+ });
2295
+ break;
2296
+ }
2297
+ const screenshotsDir = join3(serverAppRootForVision, ".uilint", "screenshots");
2298
+ if (!existsSync5(screenshotsDir)) {
2299
+ mkdirSync4(screenshotsDir, { recursive: true });
2300
+ }
2301
+ const imagePath = join3(screenshotsDir, filename);
2302
+ const imageBuffer = Buffer.from(base64Data, "base64");
2303
+ writeFileSync4(imagePath, imageBuffer);
2304
+ const sidecarFilename = filename.replace(/\.png$/, ".json");
2305
+ const sidecarPath = join3(screenshotsDir, sidecarFilename);
2306
+ const sidecarData = {
2307
+ route,
2308
+ timestamp,
2309
+ filename,
2310
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
2311
+ };
2312
+ writeFileSync4(sidecarPath, JSON.stringify(sidecarData, null, 2));
2313
+ logInfo(
2314
+ `${pc.dim("[ws]")} screenshot:saved ${pc.dim(filename)} ${pc.dim(
2315
+ `(${Math.round(imageBuffer.length / 1024)}kb)`
2316
+ )}`
2317
+ );
2318
+ sendMessage(ws, {
2319
+ type: "screenshot:saved",
2320
+ filename,
2321
+ path: imagePath,
2322
+ requestId
2323
+ });
2324
+ } catch (error) {
2325
+ const errorMessage = error instanceof Error ? error.message : String(error);
2326
+ logError(`${pc.dim("[ws]")} screenshot:error ${errorMessage}`);
2327
+ sendMessage(ws, {
2328
+ type: "screenshot:error",
2329
+ error: errorMessage,
2330
+ requestId
2331
+ });
2332
+ }
2333
+ break;
2334
+ }
2237
2335
  }
2238
2336
  }
2239
2337
  function handleDisconnect(ws) {