uilint 0.2.59 → 0.2.61
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-3LKX26SH.js → chunk-SQFSFBUP.js} +264 -24
- package/dist/chunk-SQFSFBUP.js.map +1 -0
- package/dist/index.js +140 -3
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-D43GYBCT.js → init-ui-Z54KCM7G.js} +2 -2
- package/dist/{remove-ui-WPXAIH3W.js → remove-ui-U2VUMSP7.js} +2 -2
- package/package.json +5 -5
- package/dist/chunk-3LKX26SH.js.map +0 -1
- /package/dist/{init-ui-D43GYBCT.js.map → init-ui-Z54KCM7G.js.map} +0 -0
- /package/dist/{remove-ui-WPXAIH3W.js.map → remove-ui-U2VUMSP7.js.map} +0 -0
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";
|
|
@@ -1618,6 +1618,37 @@ function pickAppRoot(params) {
|
|
|
1618
1618
|
if (containing) return containing.projectPath;
|
|
1619
1619
|
return matches[0].projectPath;
|
|
1620
1620
|
}
|
|
1621
|
+
function detectPostToolUseHook(projectRoot) {
|
|
1622
|
+
const claudeSettingsPath = join3(projectRoot, ".claude", "settings.json");
|
|
1623
|
+
if (existsSync5(claudeSettingsPath)) {
|
|
1624
|
+
try {
|
|
1625
|
+
const content = readFileSync(claudeSettingsPath, "utf-8");
|
|
1626
|
+
const settings = JSON.parse(content);
|
|
1627
|
+
const hooks = settings.hooks?.PostToolUse;
|
|
1628
|
+
if (Array.isArray(hooks)) {
|
|
1629
|
+
const hasEditHook = hooks.some(
|
|
1630
|
+
(h) => h.matcher?.includes("Edit") || h.matcher?.includes("Write")
|
|
1631
|
+
);
|
|
1632
|
+
if (hasEditHook) {
|
|
1633
|
+
return { enabled: true, provider: "claude" };
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
} catch {
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
const cursorHooksPath = join3(projectRoot, ".cursor", "hooks.json");
|
|
1640
|
+
if (existsSync5(cursorHooksPath)) {
|
|
1641
|
+
try {
|
|
1642
|
+
const content = readFileSync(cursorHooksPath, "utf-8");
|
|
1643
|
+
const hooks = JSON.parse(content);
|
|
1644
|
+
if (hooks.hooks?.afterFileEdit?.length > 0) {
|
|
1645
|
+
return { enabled: true, provider: "cursor" };
|
|
1646
|
+
}
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
return { enabled: false, provider: null };
|
|
1651
|
+
}
|
|
1621
1652
|
var cache = /* @__PURE__ */ new Map();
|
|
1622
1653
|
var eslintInstances = /* @__PURE__ */ new Map();
|
|
1623
1654
|
var visionAnalyzer = null;
|
|
@@ -1897,7 +1928,13 @@ async function handleMessage(ws, data) {
|
|
|
1897
1928
|
)}`
|
|
1898
1929
|
);
|
|
1899
1930
|
} else if (message.type === "vision:analyze") {
|
|
1931
|
+
} else if (message.type === "vision:check") {
|
|
1900
1932
|
} else if (message.type === "config:set") {
|
|
1933
|
+
} else if (message.type === "screenshot:save") {
|
|
1934
|
+
const rid = message.requestId;
|
|
1935
|
+
logInfo(
|
|
1936
|
+
`${pc.dim("[ws]")} ${pc.bold("screenshot:save")} ${pc.dim(message.route)}${rid ? ` ${pc.dim(`(req ${rid})`)}` : ""}`
|
|
1937
|
+
);
|
|
1901
1938
|
}
|
|
1902
1939
|
switch (message.type) {
|
|
1903
1940
|
case "lint:file": {
|
|
@@ -2159,6 +2196,29 @@ ${stack}` : ""
|
|
|
2159
2196
|
}
|
|
2160
2197
|
break;
|
|
2161
2198
|
}
|
|
2199
|
+
case "vision:check": {
|
|
2200
|
+
const { requestId } = message;
|
|
2201
|
+
logInfo(
|
|
2202
|
+
`${pc.dim("[ws]")} ${pc.bold("vision:check")}${requestId ? ` ${pc.dim(`(req ${requestId})`)}` : ""}`
|
|
2203
|
+
);
|
|
2204
|
+
try {
|
|
2205
|
+
const analyzer = getVisionAnalyzerInstance();
|
|
2206
|
+
const model = typeof analyzer.getModel === "function" ? analyzer.getModel() : void 0;
|
|
2207
|
+
sendMessage(ws, {
|
|
2208
|
+
type: "vision:status",
|
|
2209
|
+
available: true,
|
|
2210
|
+
model,
|
|
2211
|
+
requestId
|
|
2212
|
+
});
|
|
2213
|
+
} catch (error) {
|
|
2214
|
+
sendMessage(ws, {
|
|
2215
|
+
type: "vision:status",
|
|
2216
|
+
available: false,
|
|
2217
|
+
requestId
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
break;
|
|
2221
|
+
}
|
|
2162
2222
|
case "config:set": {
|
|
2163
2223
|
const { key, value } = message;
|
|
2164
2224
|
handleConfigSet(key, value);
|
|
@@ -2234,6 +2294,75 @@ ${stack}` : ""
|
|
|
2234
2294
|
}
|
|
2235
2295
|
break;
|
|
2236
2296
|
}
|
|
2297
|
+
case "screenshot:save": {
|
|
2298
|
+
const { dataUrl, route, timestamp, requestId } = message;
|
|
2299
|
+
try {
|
|
2300
|
+
if (!dataUrl || typeof dataUrl !== "string") {
|
|
2301
|
+
sendMessage(ws, {
|
|
2302
|
+
type: "screenshot:error",
|
|
2303
|
+
error: "Invalid dataUrl: must be a non-empty string",
|
|
2304
|
+
requestId
|
|
2305
|
+
});
|
|
2306
|
+
break;
|
|
2307
|
+
}
|
|
2308
|
+
const dataUrlPattern = /^data:image\/png;base64,/;
|
|
2309
|
+
if (!dataUrlPattern.test(dataUrl)) {
|
|
2310
|
+
sendMessage(ws, {
|
|
2311
|
+
type: "screenshot:error",
|
|
2312
|
+
error: "Invalid dataUrl: must be a base64 PNG data URL (data:image/png;base64,...)",
|
|
2313
|
+
requestId
|
|
2314
|
+
});
|
|
2315
|
+
break;
|
|
2316
|
+
}
|
|
2317
|
+
const base64Data = dataUrl.replace(dataUrlPattern, "");
|
|
2318
|
+
const sanitizedRoute = route.replace(/^\//, "").replace(/\//g, "-").replace(/[^a-zA-Z0-9_-]/g, "_") || "root";
|
|
2319
|
+
const filename = `uilint-${timestamp}-${sanitizedRoute}.png`;
|
|
2320
|
+
if (!isValidScreenshotFilename(filename)) {
|
|
2321
|
+
sendMessage(ws, {
|
|
2322
|
+
type: "screenshot:error",
|
|
2323
|
+
error: `Generated filename is invalid: ${filename}`,
|
|
2324
|
+
requestId
|
|
2325
|
+
});
|
|
2326
|
+
break;
|
|
2327
|
+
}
|
|
2328
|
+
const screenshotsDir = join3(serverAppRootForVision, ".uilint", "screenshots");
|
|
2329
|
+
if (!existsSync5(screenshotsDir)) {
|
|
2330
|
+
mkdirSync4(screenshotsDir, { recursive: true });
|
|
2331
|
+
}
|
|
2332
|
+
const imagePath = join3(screenshotsDir, filename);
|
|
2333
|
+
const imageBuffer = Buffer.from(base64Data, "base64");
|
|
2334
|
+
writeFileSync4(imagePath, imageBuffer);
|
|
2335
|
+
const sidecarFilename = filename.replace(/\.png$/, ".json");
|
|
2336
|
+
const sidecarPath = join3(screenshotsDir, sidecarFilename);
|
|
2337
|
+
const sidecarData = {
|
|
2338
|
+
route,
|
|
2339
|
+
timestamp,
|
|
2340
|
+
filename,
|
|
2341
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2342
|
+
};
|
|
2343
|
+
writeFileSync4(sidecarPath, JSON.stringify(sidecarData, null, 2));
|
|
2344
|
+
logInfo(
|
|
2345
|
+
`${pc.dim("[ws]")} screenshot:saved ${pc.dim(filename)} ${pc.dim(
|
|
2346
|
+
`(${Math.round(imageBuffer.length / 1024)}kb)`
|
|
2347
|
+
)}`
|
|
2348
|
+
);
|
|
2349
|
+
sendMessage(ws, {
|
|
2350
|
+
type: "screenshot:saved",
|
|
2351
|
+
filename,
|
|
2352
|
+
path: imagePath,
|
|
2353
|
+
requestId
|
|
2354
|
+
});
|
|
2355
|
+
} catch (error) {
|
|
2356
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2357
|
+
logError(`${pc.dim("[ws]")} screenshot:error ${errorMessage}`);
|
|
2358
|
+
sendMessage(ws, {
|
|
2359
|
+
type: "screenshot:error",
|
|
2360
|
+
error: errorMessage,
|
|
2361
|
+
requestId
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
break;
|
|
2365
|
+
}
|
|
2237
2366
|
}
|
|
2238
2367
|
}
|
|
2239
2368
|
function handleDisconnect(ws) {
|
|
@@ -2519,6 +2648,14 @@ async function serve(options) {
|
|
|
2519
2648
|
workspaceRoot: wsRoot,
|
|
2520
2649
|
serverCwd: cwd
|
|
2521
2650
|
});
|
|
2651
|
+
const hookInfo = detectPostToolUseHook(appRoot);
|
|
2652
|
+
sendMessage(ws, {
|
|
2653
|
+
type: "workspace:capabilities",
|
|
2654
|
+
postToolUseHook: hookInfo
|
|
2655
|
+
});
|
|
2656
|
+
if (hookInfo.enabled) {
|
|
2657
|
+
logInfo(`${pc.dim("[ws]")} Post-tool-use hook detected: ${pc.bold(hookInfo.provider)}`);
|
|
2658
|
+
}
|
|
2522
2659
|
const eslintConfigPath = findEslintConfigFile(appRoot);
|
|
2523
2660
|
const currentRuleConfigs = eslintConfigPath ? readRuleConfigsFromConfig(eslintConfigPath) : /* @__PURE__ */ new Map();
|
|
2524
2661
|
sendMessage(ws, {
|
|
@@ -3679,11 +3816,11 @@ program.command("update").description("Update existing style guide with new styl
|
|
|
3679
3816
|
});
|
|
3680
3817
|
});
|
|
3681
3818
|
program.command("init").description("Initialize UILint integration").option("--force", "Overwrite existing configuration files").action(async (options) => {
|
|
3682
|
-
const { initUI } = await import("./init-ui-
|
|
3819
|
+
const { initUI } = await import("./init-ui-Z54KCM7G.js");
|
|
3683
3820
|
await initUI({ force: options.force });
|
|
3684
3821
|
});
|
|
3685
3822
|
program.command("remove").description("Remove UILint components from your project").option("--dry-run", "Preview changes without removing anything").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
|
|
3686
|
-
const { removeUI } = await import("./remove-ui-
|
|
3823
|
+
const { removeUI } = await import("./remove-ui-U2VUMSP7.js");
|
|
3687
3824
|
await removeUI({ dryRun: options.dryRun, yes: options.yes });
|
|
3688
3825
|
});
|
|
3689
3826
|
program.command("serve").description("Start WebSocket server for real-time UI linting").option("-p, --port <number>", "Port to listen on", "9234").action(async (options) => {
|