glassbox 0.3.6 → 0.4.1
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/cli.js +308 -70
- package/dist/client/app.global.js +9 -9
- package/dist/client/styles.css +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -88,7 +88,9 @@ var init_schema = __esm({
|
|
|
88
88
|
sort_mode TEXT NOT NULL DEFAULT 'folder',
|
|
89
89
|
risk_sort_dimension TEXT NOT NULL DEFAULT 'aggregate',
|
|
90
90
|
show_risk_scores BOOLEAN NOT NULL DEFAULT FALSE,
|
|
91
|
-
ignore_whitespace BOOLEAN NOT NULL DEFAULT FALSE
|
|
91
|
+
ignore_whitespace BOOLEAN NOT NULL DEFAULT FALSE,
|
|
92
|
+
svg_view_mode TEXT NOT NULL DEFAULT 'code',
|
|
93
|
+
last_image_mode TEXT NOT NULL DEFAULT 'metadata'
|
|
92
94
|
);
|
|
93
95
|
`;
|
|
94
96
|
}
|
|
@@ -144,6 +146,8 @@ async function initSchema(db2) {
|
|
|
144
146
|
await addColumnIfMissing(db2, "ai_analyses", "progress_completed", "INTEGER NOT NULL DEFAULT 0");
|
|
145
147
|
await addColumnIfMissing(db2, "ai_analyses", "progress_total", "INTEGER NOT NULL DEFAULT 0");
|
|
146
148
|
await addColumnIfMissing(db2, "user_preferences", "ignore_whitespace", "BOOLEAN NOT NULL DEFAULT FALSE");
|
|
149
|
+
await addColumnIfMissing(db2, "user_preferences", "svg_view_mode", "TEXT NOT NULL DEFAULT 'code'");
|
|
150
|
+
await addColumnIfMissing(db2, "user_preferences", "last_image_mode", "TEXT NOT NULL DEFAULT 'metadata'");
|
|
147
151
|
await db2.exec(
|
|
148
152
|
`UPDATE ai_analyses SET status = 'failed', error_message = 'Interrupted (server restarted)' WHERE status = 'running'`
|
|
149
153
|
);
|
|
@@ -391,7 +395,7 @@ init_queries();
|
|
|
391
395
|
init_connection();
|
|
392
396
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
393
397
|
import { tmpdir } from "os";
|
|
394
|
-
import { join as
|
|
398
|
+
import { join as join10, resolve as resolve4 } from "path";
|
|
395
399
|
|
|
396
400
|
// src/debug.ts
|
|
397
401
|
var debugEnabled = false;
|
|
@@ -800,7 +804,7 @@ async function getUserPreferences() {
|
|
|
800
804
|
["singleton"]
|
|
801
805
|
);
|
|
802
806
|
if (result.rows.length === 0) {
|
|
803
|
-
return { sort_mode: "folder", risk_sort_dimension: "aggregate", show_risk_scores: false, ignore_whitespace: false };
|
|
807
|
+
return { sort_mode: "folder", risk_sort_dimension: "aggregate", show_risk_scores: false, ignore_whitespace: false, svg_view_mode: "code", last_image_mode: "metadata" };
|
|
804
808
|
}
|
|
805
809
|
return result.rows[0];
|
|
806
810
|
}
|
|
@@ -809,14 +813,16 @@ async function saveUserPreferences(prefs) {
|
|
|
809
813
|
const current = await getUserPreferences();
|
|
810
814
|
const merged = { ...current, ...prefs };
|
|
811
815
|
await db2.query(
|
|
812
|
-
`INSERT INTO user_preferences (id, sort_mode, risk_sort_dimension, show_risk_scores, ignore_whitespace)
|
|
813
|
-
VALUES ($1, $2, $3, $4, $5)
|
|
816
|
+
`INSERT INTO user_preferences (id, sort_mode, risk_sort_dimension, show_risk_scores, ignore_whitespace, svg_view_mode, last_image_mode)
|
|
817
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
814
818
|
ON CONFLICT (id) DO UPDATE SET
|
|
815
819
|
sort_mode = EXCLUDED.sort_mode,
|
|
816
820
|
risk_sort_dimension = EXCLUDED.risk_sort_dimension,
|
|
817
821
|
show_risk_scores = EXCLUDED.show_risk_scores,
|
|
818
|
-
ignore_whitespace = EXCLUDED.ignore_whitespace
|
|
819
|
-
|
|
822
|
+
ignore_whitespace = EXCLUDED.ignore_whitespace,
|
|
823
|
+
svg_view_mode = EXCLUDED.svg_view_mode,
|
|
824
|
+
last_image_mode = EXCLUDED.last_image_mode`,
|
|
825
|
+
["singleton", merged.sort_mode, merged.risk_sort_dimension, merged.show_risk_scores, merged.ignore_whitespace, merged.svg_view_mode, merged.last_image_mode]
|
|
820
826
|
);
|
|
821
827
|
}
|
|
822
828
|
|
|
@@ -1717,9 +1723,9 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1717
1723
|
// src/server.ts
|
|
1718
1724
|
import { serve } from "@hono/node-server";
|
|
1719
1725
|
import { exec } from "child_process";
|
|
1720
|
-
import { existsSync as
|
|
1726
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "fs";
|
|
1721
1727
|
import { Hono as Hono4 } from "hono";
|
|
1722
|
-
import { dirname, join as
|
|
1728
|
+
import { dirname, join as join7 } from "path";
|
|
1723
1729
|
import { fileURLToPath } from "url";
|
|
1724
1730
|
|
|
1725
1731
|
// src/routes/ai-api.ts
|
|
@@ -2439,8 +2445,8 @@ function isRetriable(err) {
|
|
|
2439
2445
|
return msg.includes("429") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("rate_limit");
|
|
2440
2446
|
}
|
|
2441
2447
|
function sleep(ms) {
|
|
2442
|
-
return new Promise((
|
|
2443
|
-
setTimeout(
|
|
2448
|
+
return new Promise((resolve5) => {
|
|
2449
|
+
setTimeout(resolve5, ms);
|
|
2444
2450
|
});
|
|
2445
2451
|
}
|
|
2446
2452
|
|
|
@@ -2484,8 +2490,8 @@ function randomLines(count) {
|
|
|
2484
2490
|
return lines.sort((a, b) => a.line - b.line);
|
|
2485
2491
|
}
|
|
2486
2492
|
function sleep2(ms) {
|
|
2487
|
-
return new Promise((
|
|
2488
|
-
setTimeout(
|
|
2493
|
+
return new Promise((resolve5) => {
|
|
2494
|
+
setTimeout(resolve5, ms);
|
|
2489
2495
|
});
|
|
2490
2496
|
}
|
|
2491
2497
|
async function mockRiskAnalysisBatch(files) {
|
|
@@ -2937,9 +2943,10 @@ aiApiRoutes.post("/preferences", async (c) => {
|
|
|
2937
2943
|
|
|
2938
2944
|
// src/routes/api.ts
|
|
2939
2945
|
init_queries();
|
|
2940
|
-
import {
|
|
2946
|
+
import { execSync as execSync5 } from "child_process";
|
|
2947
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2941
2948
|
import { Hono as Hono2 } from "hono";
|
|
2942
|
-
import { join as
|
|
2949
|
+
import { join as join6, resolve as resolve3 } from "path";
|
|
2943
2950
|
|
|
2944
2951
|
// src/export/generate.ts
|
|
2945
2952
|
init_queries();
|
|
@@ -3103,6 +3110,9 @@ function isImageFile(filePath) {
|
|
|
3103
3110
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3104
3111
|
return IMAGE_EXTENSIONS.has(ext);
|
|
3105
3112
|
}
|
|
3113
|
+
function isSvgFile(filePath) {
|
|
3114
|
+
return filePath.slice(filePath.lastIndexOf(".")).toLowerCase() === ".svg";
|
|
3115
|
+
}
|
|
3106
3116
|
function getContentType(filePath) {
|
|
3107
3117
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3108
3118
|
switch (ext) {
|
|
@@ -3397,6 +3407,139 @@ function parseWebp(data) {
|
|
|
3397
3407
|
return { format: "webp", width, height, colorSpace: "srgb", channels: hasAlpha ? 4 : 3, depth: null, hasAlpha, density: null, exif: null };
|
|
3398
3408
|
}
|
|
3399
3409
|
|
|
3410
|
+
// src/git/svg-rasterize.ts
|
|
3411
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
3412
|
+
import { createRequire } from "module";
|
|
3413
|
+
import { join as join5 } from "path";
|
|
3414
|
+
var initialized = false;
|
|
3415
|
+
var ResvgClass;
|
|
3416
|
+
var fontBuffers = [];
|
|
3417
|
+
async function ensureInit() {
|
|
3418
|
+
if (initialized) return;
|
|
3419
|
+
const require2 = createRequire(import.meta.url);
|
|
3420
|
+
const resvgPath = require2.resolve("@resvg/resvg-wasm");
|
|
3421
|
+
const wasmPath = resvgPath.replace(/index\.(js|mjs)$/, "index_bg.wasm");
|
|
3422
|
+
const wasmBuffer = readFileSync6(wasmPath);
|
|
3423
|
+
const mod = await import("@resvg/resvg-wasm");
|
|
3424
|
+
await mod.initWasm(wasmBuffer);
|
|
3425
|
+
ResvgClass = mod.Resvg;
|
|
3426
|
+
fontBuffers = loadSystemFonts();
|
|
3427
|
+
initialized = true;
|
|
3428
|
+
}
|
|
3429
|
+
function loadSystemFonts() {
|
|
3430
|
+
const buffers = [];
|
|
3431
|
+
const candidates = getFontCandidates();
|
|
3432
|
+
for (const path of candidates) {
|
|
3433
|
+
if (!existsSync4(path)) continue;
|
|
3434
|
+
try {
|
|
3435
|
+
buffers.push(readFileSync6(path));
|
|
3436
|
+
} catch {
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
return buffers;
|
|
3440
|
+
}
|
|
3441
|
+
function getFontCandidates() {
|
|
3442
|
+
switch (process.platform) {
|
|
3443
|
+
case "darwin": {
|
|
3444
|
+
const sys = "/System/Library/Fonts";
|
|
3445
|
+
const sup = "/System/Library/Fonts/Supplemental";
|
|
3446
|
+
return [
|
|
3447
|
+
// Core system fonts (serif, sans-serif, monospace)
|
|
3448
|
+
join5(sys, "Helvetica.ttc"),
|
|
3449
|
+
join5(sys, "Times.ttc"),
|
|
3450
|
+
join5(sys, "Courier.ttc"),
|
|
3451
|
+
join5(sys, "Menlo.ttc"),
|
|
3452
|
+
join5(sys, "SFPro.ttf"),
|
|
3453
|
+
join5(sys, "SFNS.ttf"),
|
|
3454
|
+
join5(sys, "SFNSMono.ttf"),
|
|
3455
|
+
// Supplemental (common named fonts in SVGs)
|
|
3456
|
+
join5(sup, "Arial.ttf"),
|
|
3457
|
+
join5(sup, "Arial Bold.ttf"),
|
|
3458
|
+
join5(sup, "Georgia.ttf"),
|
|
3459
|
+
join5(sup, "Verdana.ttf"),
|
|
3460
|
+
join5(sup, "Tahoma.ttf"),
|
|
3461
|
+
join5(sup, "Trebuchet MS.ttf"),
|
|
3462
|
+
join5(sup, "Impact.ttf"),
|
|
3463
|
+
join5(sup, "Comic Sans MS.ttf"),
|
|
3464
|
+
join5(sup, "Courier New.ttf"),
|
|
3465
|
+
join5(sup, "Times New Roman.ttf")
|
|
3466
|
+
];
|
|
3467
|
+
}
|
|
3468
|
+
case "linux":
|
|
3469
|
+
return [
|
|
3470
|
+
// DejaVu (most common Linux fallback)
|
|
3471
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
3472
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
|
3473
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf",
|
|
3474
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
|
3475
|
+
// Liberation (metric-compatible with Arial/Times/Courier)
|
|
3476
|
+
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
|
3477
|
+
"/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf",
|
|
3478
|
+
"/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
|
|
3479
|
+
// Noto (common on modern distros)
|
|
3480
|
+
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf"
|
|
3481
|
+
];
|
|
3482
|
+
case "win32": {
|
|
3483
|
+
const winFonts = join5(process.env.WINDIR ?? "C:\\Windows", "Fonts");
|
|
3484
|
+
return [
|
|
3485
|
+
join5(winFonts, "arial.ttf"),
|
|
3486
|
+
join5(winFonts, "arialbd.ttf"),
|
|
3487
|
+
join5(winFonts, "times.ttf"),
|
|
3488
|
+
join5(winFonts, "cour.ttf"),
|
|
3489
|
+
join5(winFonts, "verdana.ttf"),
|
|
3490
|
+
join5(winFonts, "tahoma.ttf"),
|
|
3491
|
+
join5(winFonts, "georgia.ttf"),
|
|
3492
|
+
join5(winFonts, "consola.ttf"),
|
|
3493
|
+
join5(winFonts, "segoeui.ttf")
|
|
3494
|
+
];
|
|
3495
|
+
}
|
|
3496
|
+
default:
|
|
3497
|
+
return [];
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
function parseSvgDimensions(svg) {
|
|
3501
|
+
const widthMatch = svg.match(/\bwidth\s*=\s*["']([^"']+)["']/);
|
|
3502
|
+
const heightMatch = svg.match(/\bheight\s*=\s*["']([^"']+)["']/);
|
|
3503
|
+
const viewBoxMatch = svg.match(/\bviewBox\s*=\s*["']([^"']+)["']/);
|
|
3504
|
+
let width = widthMatch ? parseFloat(widthMatch[1]) : NaN;
|
|
3505
|
+
let height = heightMatch ? parseFloat(heightMatch[1]) : NaN;
|
|
3506
|
+
if ((isNaN(width) || isNaN(height)) && viewBoxMatch) {
|
|
3507
|
+
const parts = viewBoxMatch[1].split(/[\s,]+/);
|
|
3508
|
+
if (parts.length >= 4) {
|
|
3509
|
+
if (isNaN(width)) width = parseFloat(parts[2]);
|
|
3510
|
+
if (isNaN(height)) height = parseFloat(parts[3]);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
if (isNaN(width)) width = 300;
|
|
3514
|
+
if (isNaN(height)) height = 150;
|
|
3515
|
+
return { width, height };
|
|
3516
|
+
}
|
|
3517
|
+
function svgUsesExternalFonts(svgData) {
|
|
3518
|
+
const svg = svgData.toString("utf-8");
|
|
3519
|
+
return /<text[\s>]/i.test(svg) || /font-family/i.test(svg) || /@font-face/i.test(svg);
|
|
3520
|
+
}
|
|
3521
|
+
async function rasterizeSvg(svgData) {
|
|
3522
|
+
await ensureInit();
|
|
3523
|
+
const svgString = svgData.toString("utf-8");
|
|
3524
|
+
const { width, height } = parseSvgDimensions(svgString);
|
|
3525
|
+
const maxDim = Math.max(width, height);
|
|
3526
|
+
const scale = Math.min(10, 8e3 / maxDim);
|
|
3527
|
+
const targetWidth = Math.round(width * scale);
|
|
3528
|
+
const resvg = new ResvgClass(svgString, {
|
|
3529
|
+
fitTo: { mode: "width", value: targetWidth },
|
|
3530
|
+
font: {
|
|
3531
|
+
loadSystemFonts: false,
|
|
3532
|
+
fontBuffers,
|
|
3533
|
+
defaultFontFamily: "Helvetica"
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
const rendered = resvg.render();
|
|
3537
|
+
const png = Buffer.from(rendered.asPng());
|
|
3538
|
+
rendered.free();
|
|
3539
|
+
resvg.free();
|
|
3540
|
+
return png;
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3400
3543
|
// src/outline/parser.ts
|
|
3401
3544
|
var BRACE_LANGS = /* @__PURE__ */ new Set([
|
|
3402
3545
|
"javascript",
|
|
@@ -3826,6 +3969,23 @@ apiRoutes.patch("/files/:fileId/status", async (c) => {
|
|
|
3826
3969
|
await updateFileStatus(c.req.param("fileId"), status);
|
|
3827
3970
|
return c.json({ ok: true });
|
|
3828
3971
|
});
|
|
3972
|
+
apiRoutes.post("/files/:fileId/reveal", async (c) => {
|
|
3973
|
+
const file = await getReviewFile(c.req.param("fileId"));
|
|
3974
|
+
if (!file) return c.json({ error: "Not found" }, 404);
|
|
3975
|
+
const repoRoot = c.get("repoRoot");
|
|
3976
|
+
const fullPath = resolve3(repoRoot, file.file_path);
|
|
3977
|
+
try {
|
|
3978
|
+
if (process.platform === "darwin") {
|
|
3979
|
+
execSync5(`open -R "${fullPath}"`);
|
|
3980
|
+
} else if (process.platform === "win32") {
|
|
3981
|
+
execSync5(`explorer /select,"${fullPath}"`);
|
|
3982
|
+
} else {
|
|
3983
|
+
execSync5(`xdg-open "${resolve3(fullPath, "..")}"`);
|
|
3984
|
+
}
|
|
3985
|
+
} catch {
|
|
3986
|
+
}
|
|
3987
|
+
return c.json({ ok: true });
|
|
3988
|
+
});
|
|
3829
3989
|
function autoExport(c) {
|
|
3830
3990
|
scheduleAutoExport(c.get("reviewId"), c.get("repoRoot"));
|
|
3831
3991
|
}
|
|
@@ -3916,19 +4076,19 @@ apiRoutes.get("/context/:fileId", async (c) => {
|
|
|
3916
4076
|
return c.json({ lines });
|
|
3917
4077
|
});
|
|
3918
4078
|
function readProjectSettings(repoRoot) {
|
|
3919
|
-
const settingsPath =
|
|
4079
|
+
const settingsPath = join6(repoRoot, ".glassbox", "settings.json");
|
|
3920
4080
|
try {
|
|
3921
|
-
if (
|
|
3922
|
-
return JSON.parse(
|
|
4081
|
+
if (existsSync5(settingsPath)) {
|
|
4082
|
+
return JSON.parse(readFileSync7(settingsPath, "utf-8"));
|
|
3923
4083
|
}
|
|
3924
4084
|
} catch {
|
|
3925
4085
|
}
|
|
3926
4086
|
return {};
|
|
3927
4087
|
}
|
|
3928
4088
|
function writeProjectSettings(repoRoot, settings) {
|
|
3929
|
-
const dir =
|
|
4089
|
+
const dir = join6(repoRoot, ".glassbox");
|
|
3930
4090
|
mkdirSync4(dir, { recursive: true });
|
|
3931
|
-
writeFileSync4(
|
|
4091
|
+
writeFileSync4(join6(dir, "settings.json"), JSON.stringify(settings, null, 2), "utf-8");
|
|
3932
4092
|
}
|
|
3933
4093
|
apiRoutes.get("/project-settings", (c) => {
|
|
3934
4094
|
const repoRoot = c.get("repoRoot");
|
|
@@ -3978,6 +4138,16 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
3978
4138
|
const oldPath = diff.oldPath ?? null;
|
|
3979
4139
|
const image = side === "old" ? getOldImage(mode, file.file_path, oldPath, repoRoot) : getNewImage(mode, file.file_path, repoRoot);
|
|
3980
4140
|
if (!image) return c.text("Image not available", 404);
|
|
4141
|
+
if (isSvgFile(file.file_path)) {
|
|
4142
|
+
try {
|
|
4143
|
+
const png = await rasterizeSvg(image.data);
|
|
4144
|
+
return new Response(png, {
|
|
4145
|
+
headers: { "Content-Type": "image/png", "Cache-Control": "no-cache" }
|
|
4146
|
+
});
|
|
4147
|
+
} catch {
|
|
4148
|
+
return c.text("SVG rasterization failed", 500);
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
3981
4151
|
const contentType = getContentType(file.file_path);
|
|
3982
4152
|
return new Response(image.data, {
|
|
3983
4153
|
headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
|
|
@@ -4057,7 +4227,7 @@ function jsx(tag, props) {
|
|
|
4057
4227
|
}
|
|
4058
4228
|
|
|
4059
4229
|
// src/components/imageDiff.tsx
|
|
4060
|
-
function ImageDiff({ file, diff }) {
|
|
4230
|
+
function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
4061
4231
|
const fileId = file.id;
|
|
4062
4232
|
const isAdded = diff.status === "added";
|
|
4063
4233
|
const isDeleted = diff.status === "deleted";
|
|
@@ -4072,8 +4242,11 @@ function ImageDiff({ file, diff }) {
|
|
|
4072
4242
|
"data-file-path": file.file_path,
|
|
4073
4243
|
"data-has-old": String(hasOld),
|
|
4074
4244
|
"data-has-new": String(hasNew),
|
|
4245
|
+
...baseWidth ? { "data-base-width": String(baseWidth) } : {},
|
|
4246
|
+
...baseHeight ? { "data-base-height": String(baseHeight) } : {},
|
|
4075
4247
|
children: [
|
|
4076
|
-
/* @__PURE__ */ jsx("div", { className: "image-
|
|
4248
|
+
fontWarning && /* @__PURE__ */ jsx("div", { className: "image-font-warning", children: "This SVG uses text that may render differently depending on locally installed fonts." }),
|
|
4249
|
+
/* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-metadata", "data-panel": "metadata", children: /* @__PURE__ */ jsx("div", { className: "image-metadata-loading", children: "Loading metadata..." }) }),
|
|
4077
4250
|
hasComparison && /* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-visual", "data-panel": "difference", children: /* @__PURE__ */ jsx("div", { className: "image-visual-canvas", "data-zoomable": "true", children: /* @__PURE__ */ jsx("div", { className: "image-zoom-wrap", children: [
|
|
4078
4251
|
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
4079
4252
|
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-new image-blend", src: `/api/image/${fileId}/new`, alt: "New version" })
|
|
@@ -4101,6 +4274,7 @@ function ImageDiff({ file, diff }) {
|
|
|
4101
4274
|
}
|
|
4102
4275
|
|
|
4103
4276
|
// src/components/diffView.tsx
|
|
4277
|
+
var revealSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><line x1="12" y1="11" x2="12" y2="17"/><polyline points="9 14 12 11 15 14"/></svg>';
|
|
4104
4278
|
function DiffView({ file, diff, annotations, mode }) {
|
|
4105
4279
|
const annotationsByLine = {};
|
|
4106
4280
|
for (const a of annotations) {
|
|
@@ -4108,13 +4282,25 @@ function DiffView({ file, diff, annotations, mode }) {
|
|
|
4108
4282
|
if (!(key in annotationsByLine)) annotationsByLine[key] = [];
|
|
4109
4283
|
annotationsByLine[key].push(a);
|
|
4110
4284
|
}
|
|
4111
|
-
return /* @__PURE__ */ jsx(
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4285
|
+
return /* @__PURE__ */ jsx(
|
|
4286
|
+
"div",
|
|
4287
|
+
{
|
|
4288
|
+
className: "diff-view",
|
|
4289
|
+
"data-file-id": file.id,
|
|
4290
|
+
"data-file-path": file.file_path,
|
|
4291
|
+
...isSvgFile(diff.filePath) ? { "data-is-svg": "true" } : {},
|
|
4292
|
+
children: [
|
|
4293
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header", children: [
|
|
4294
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header-file", children: [
|
|
4295
|
+
/* @__PURE__ */ jsx("span", { className: "file-path", children: diff.filePath }),
|
|
4296
|
+
/* @__PURE__ */ jsx("button", { className: "reveal-btn", "data-file-id": file.id, title: "Reveal in file manager", children: raw(revealSvg) })
|
|
4297
|
+
] }),
|
|
4298
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header-actions", children: /* @__PURE__ */ jsx("span", { className: `file-status ${diff.status}`, children: diff.status }) })
|
|
4299
|
+
] }),
|
|
4300
|
+
diff.isBinary && isImageFile(diff.filePath) ? /* @__PURE__ */ jsx(ImageDiff, { file, diff }) : diff.isBinary ? /* @__PURE__ */ jsx("div", { className: "hunk-separator", children: "Binary file" }) : diff.status === "added" || diff.status === "deleted" || mode === "unified" ? /* @__PURE__ */ jsx(UnifiedDiff, { hunks: diff.hunks, annotationsByLine }) : /* @__PURE__ */ jsx(SplitDiff, { hunks: diff.hunks, annotationsByLine })
|
|
4301
|
+
]
|
|
4302
|
+
}
|
|
4303
|
+
);
|
|
4118
4304
|
}
|
|
4119
4305
|
function getAnnotations(pair, annotationsByLine) {
|
|
4120
4306
|
const leftAnns = pair.left ? annotationsByLine[`${pair.left.oldNum}:old`] ?? [] : [];
|
|
@@ -4637,6 +4823,7 @@ var zoomOutSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
|
|
|
4637
4823
|
var zoomInSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>';
|
|
4638
4824
|
var actualSizeSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><text x="12" y="15.5" text-anchor="middle" font-size="9" font-weight="bold" fill="currentColor" stroke="none">1:1</text></svg>';
|
|
4639
4825
|
var fitSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>';
|
|
4826
|
+
var revealSvgIcon = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><line x1="12" y1="11" x2="12" y2="17"/><polyline points="9 14 12 11 15 14"/></svg>';
|
|
4640
4827
|
var pageRoutes = new Hono3();
|
|
4641
4828
|
pageRoutes.get("/", async (c) => {
|
|
4642
4829
|
const reviewId = c.get("reviewId");
|
|
@@ -4684,6 +4871,10 @@ pageRoutes.get("/", async (c) => {
|
|
|
4684
4871
|
] }),
|
|
4685
4872
|
/* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
4686
4873
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
4874
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-svg-toggle", style: "display:none", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
4875
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-svg-mode": "code", children: "Code" }),
|
|
4876
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-svg-mode": "rendered", children: "Rendered" })
|
|
4877
|
+
] }) }),
|
|
4687
4878
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
|
|
4688
4879
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
|
|
4689
4880
|
/* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
@@ -4719,10 +4910,53 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
4719
4910
|
const fileId = c.req.param("fileId");
|
|
4720
4911
|
const mode = c.req.query("mode") === "unified" ? "unified" : "split";
|
|
4721
4912
|
const ignoreWhitespace = c.req.query("ignoreWhitespace") === "1";
|
|
4913
|
+
const view = c.req.query("view");
|
|
4722
4914
|
const file = await getReviewFile(fileId);
|
|
4723
4915
|
if (!file) return c.text("File not found", 404);
|
|
4916
|
+
const diff = JSON.parse(file.diff_data ?? "{}");
|
|
4917
|
+
if (view === "rendered" && isSvgFile(file.file_path)) {
|
|
4918
|
+
const repoRoot = c.get("repoRoot");
|
|
4919
|
+
const review = await getReview(file.review_id);
|
|
4920
|
+
let fontWarning = false;
|
|
4921
|
+
let svgBaseWidth = 300;
|
|
4922
|
+
let svgBaseHeight = 150;
|
|
4923
|
+
if (review) {
|
|
4924
|
+
const reviewMode = parseModeString(review.mode);
|
|
4925
|
+
const oldImg = diff.status !== "added" ? getOldImage(reviewMode, file.file_path, diff.oldPath ?? null, repoRoot) : null;
|
|
4926
|
+
const newImg = diff.status !== "deleted" ? getNewImage(reviewMode, file.file_path, repoRoot) : null;
|
|
4927
|
+
const svgData = newImg ?? oldImg;
|
|
4928
|
+
if (svgData) {
|
|
4929
|
+
const dims = parseSvgDimensions(svgData.data.toString("utf-8"));
|
|
4930
|
+
svgBaseWidth = dims.width;
|
|
4931
|
+
svgBaseHeight = dims.height;
|
|
4932
|
+
}
|
|
4933
|
+
if (oldImg && svgUsesExternalFonts(oldImg.data) || newImg && svgUsesExternalFonts(newImg.data)) {
|
|
4934
|
+
fontWarning = true;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
const html2 = /* @__PURE__ */ jsx("div", { className: "diff-view", "data-file-id": file.id, "data-file-path": file.file_path, "data-is-svg": "true", children: [
|
|
4938
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header", children: [
|
|
4939
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header-file", children: [
|
|
4940
|
+
/* @__PURE__ */ jsx("span", { className: "file-path", children: diff.filePath }),
|
|
4941
|
+
/* @__PURE__ */ jsx("button", { className: "reveal-btn", "data-file-id": file.id, title: "Reveal in file manager", children: raw(revealSvgIcon) })
|
|
4942
|
+
] }),
|
|
4943
|
+
/* @__PURE__ */ jsx("div", { className: "diff-header-actions", children: /* @__PURE__ */ jsx("span", { className: `file-status ${diff.status}`, children: diff.status }) })
|
|
4944
|
+
] }),
|
|
4945
|
+
/* @__PURE__ */ jsx(
|
|
4946
|
+
ImageDiff,
|
|
4947
|
+
{
|
|
4948
|
+
file,
|
|
4949
|
+
diff,
|
|
4950
|
+
fontWarning,
|
|
4951
|
+
baseWidth: svgBaseWidth,
|
|
4952
|
+
baseHeight: svgBaseHeight
|
|
4953
|
+
}
|
|
4954
|
+
)
|
|
4955
|
+
] });
|
|
4956
|
+
return c.html(html2.toString());
|
|
4957
|
+
}
|
|
4724
4958
|
const annotations = await getAnnotationsForFile(fileId);
|
|
4725
|
-
let
|
|
4959
|
+
let finalDiff = diff;
|
|
4726
4960
|
if (ignoreWhitespace) {
|
|
4727
4961
|
const repoRoot = c.get("repoRoot");
|
|
4728
4962
|
const review = await getReview(file.review_id);
|
|
@@ -4730,11 +4964,11 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
4730
4964
|
const reviewMode = parseModeString(review.mode);
|
|
4731
4965
|
const regenerated = getSingleFileDiff(reviewMode, file.file_path, repoRoot, "-w");
|
|
4732
4966
|
if (regenerated) {
|
|
4733
|
-
|
|
4967
|
+
finalDiff = regenerated;
|
|
4734
4968
|
}
|
|
4735
4969
|
}
|
|
4736
4970
|
}
|
|
4737
|
-
const html = /* @__PURE__ */ jsx(DiffView, { file, diff, annotations, mode });
|
|
4971
|
+
const html = /* @__PURE__ */ jsx(DiffView, { file, diff: finalDiff, annotations, mode });
|
|
4738
4972
|
return c.html(html.toString());
|
|
4739
4973
|
});
|
|
4740
4974
|
pageRoutes.get("/review/:reviewId", async (c) => {
|
|
@@ -4788,6 +5022,10 @@ pageRoutes.get("/review/:reviewId", async (c) => {
|
|
|
4788
5022
|
] }),
|
|
4789
5023
|
/* @__PURE__ */ jsx("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
4790
5024
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
5025
|
+
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-svg-toggle", style: "display:none", children: /* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
5026
|
+
/* @__PURE__ */ jsx("button", { className: "segment active", "data-svg-mode": "code", children: "Code" }),
|
|
5027
|
+
/* @__PURE__ */ jsx("button", { className: "segment", "data-svg-mode": "rendered", children: "Rendered" })
|
|
5028
|
+
] }) }),
|
|
4791
5029
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-text", children: [
|
|
4792
5030
|
/* @__PURE__ */ jsx("div", { className: "diff-toolbar-left", children: [
|
|
4793
5031
|
/* @__PURE__ */ jsx("div", { className: "segmented-control", children: [
|
|
@@ -4829,10 +5067,10 @@ pageRoutes.get("/history", async (c) => {
|
|
|
4829
5067
|
|
|
4830
5068
|
// src/server.ts
|
|
4831
5069
|
function tryServe(fetch2, port) {
|
|
4832
|
-
return new Promise((
|
|
5070
|
+
return new Promise((resolve5, reject) => {
|
|
4833
5071
|
const server = serve({ fetch: fetch2, port });
|
|
4834
5072
|
server.on("listening", () => {
|
|
4835
|
-
|
|
5073
|
+
resolve5(port);
|
|
4836
5074
|
});
|
|
4837
5075
|
server.on("error", (err) => {
|
|
4838
5076
|
if (err.code === "EADDRINUSE") {
|
|
@@ -4852,13 +5090,13 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
4852
5090
|
await next();
|
|
4853
5091
|
});
|
|
4854
5092
|
const selfDir = dirname(fileURLToPath(import.meta.url));
|
|
4855
|
-
const distDir =
|
|
5093
|
+
const distDir = existsSync6(join7(selfDir, "client", "styles.css")) ? join7(selfDir, "client") : join7(selfDir, "..", "dist", "client");
|
|
4856
5094
|
app.get("/static/styles.css", (c) => {
|
|
4857
|
-
const css =
|
|
5095
|
+
const css = readFileSync8(join7(distDir, "styles.css"), "utf-8");
|
|
4858
5096
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
4859
5097
|
});
|
|
4860
5098
|
app.get("/static/app.js", (c) => {
|
|
4861
|
-
const js =
|
|
5099
|
+
const js = readFileSync8(join7(distDir, "app.global.js"), "utf-8");
|
|
4862
5100
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
4863
5101
|
});
|
|
4864
5102
|
app.route("/api", apiRoutes);
|
|
@@ -4894,8 +5132,8 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
4894
5132
|
}
|
|
4895
5133
|
|
|
4896
5134
|
// src/skills.ts
|
|
4897
|
-
import { existsSync as
|
|
4898
|
-
import { join as
|
|
5135
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
5136
|
+
import { join as join8 } from "path";
|
|
4899
5137
|
var SKILL_VERSION = 1;
|
|
4900
5138
|
function versionHeader() {
|
|
4901
5139
|
return `<!-- glassbox-skill-version: ${SKILL_VERSION} -->`;
|
|
@@ -4906,8 +5144,8 @@ function parseVersionHeader(content) {
|
|
|
4906
5144
|
return parseInt(match[1], 10);
|
|
4907
5145
|
}
|
|
4908
5146
|
function updateFile(path, content) {
|
|
4909
|
-
if (
|
|
4910
|
-
const existing =
|
|
5147
|
+
if (existsSync7(path)) {
|
|
5148
|
+
const existing = readFileSync9(path, "utf-8");
|
|
4911
5149
|
const version = parseVersionHeader(existing);
|
|
4912
5150
|
if (version !== null && version >= SKILL_VERSION) {
|
|
4913
5151
|
return false;
|
|
@@ -4933,7 +5171,7 @@ function skillBody() {
|
|
|
4933
5171
|
].join("\n");
|
|
4934
5172
|
}
|
|
4935
5173
|
function ensureClaudeSkills(cwd) {
|
|
4936
|
-
const dir =
|
|
5174
|
+
const dir = join8(cwd, ".claude", "skills", "glassbox");
|
|
4937
5175
|
mkdirSync5(dir, { recursive: true });
|
|
4938
5176
|
const content = [
|
|
4939
5177
|
"---",
|
|
@@ -4946,10 +5184,10 @@ function ensureClaudeSkills(cwd) {
|
|
|
4946
5184
|
skillBody(),
|
|
4947
5185
|
""
|
|
4948
5186
|
].join("\n");
|
|
4949
|
-
return updateFile(
|
|
5187
|
+
return updateFile(join8(dir, "SKILL.md"), content);
|
|
4950
5188
|
}
|
|
4951
5189
|
function ensureCursorRules(cwd) {
|
|
4952
|
-
const rulesDir =
|
|
5190
|
+
const rulesDir = join8(cwd, ".cursor", "rules");
|
|
4953
5191
|
mkdirSync5(rulesDir, { recursive: true });
|
|
4954
5192
|
const content = [
|
|
4955
5193
|
"---",
|
|
@@ -4961,10 +5199,10 @@ function ensureCursorRules(cwd) {
|
|
|
4961
5199
|
skillBody(),
|
|
4962
5200
|
""
|
|
4963
5201
|
].join("\n");
|
|
4964
|
-
return updateFile(
|
|
5202
|
+
return updateFile(join8(rulesDir, "glassbox.mdc"), content);
|
|
4965
5203
|
}
|
|
4966
5204
|
function ensureCopilotPrompts(cwd) {
|
|
4967
|
-
const promptsDir =
|
|
5205
|
+
const promptsDir = join8(cwd, ".github", "prompts");
|
|
4968
5206
|
mkdirSync5(promptsDir, { recursive: true });
|
|
4969
5207
|
const content = [
|
|
4970
5208
|
"---",
|
|
@@ -4975,10 +5213,10 @@ function ensureCopilotPrompts(cwd) {
|
|
|
4975
5213
|
skillBody(),
|
|
4976
5214
|
""
|
|
4977
5215
|
].join("\n");
|
|
4978
|
-
return updateFile(
|
|
5216
|
+
return updateFile(join8(promptsDir, "glassbox.prompt.md"), content);
|
|
4979
5217
|
}
|
|
4980
5218
|
function ensureWindsurfRules(cwd) {
|
|
4981
|
-
const rulesDir =
|
|
5219
|
+
const rulesDir = join8(cwd, ".windsurf", "rules");
|
|
4982
5220
|
mkdirSync5(rulesDir, { recursive: true });
|
|
4983
5221
|
const content = [
|
|
4984
5222
|
"---",
|
|
@@ -4990,39 +5228,39 @@ function ensureWindsurfRules(cwd) {
|
|
|
4990
5228
|
skillBody(),
|
|
4991
5229
|
""
|
|
4992
5230
|
].join("\n");
|
|
4993
|
-
return updateFile(
|
|
5231
|
+
return updateFile(join8(rulesDir, "glassbox.md"), content);
|
|
4994
5232
|
}
|
|
4995
5233
|
function ensureSkills() {
|
|
4996
5234
|
const cwd = process.cwd();
|
|
4997
5235
|
const platforms = [];
|
|
4998
|
-
if (
|
|
5236
|
+
if (existsSync7(join8(cwd, ".claude"))) {
|
|
4999
5237
|
if (ensureClaudeSkills(cwd)) platforms.push("Claude Code");
|
|
5000
5238
|
}
|
|
5001
|
-
if (
|
|
5239
|
+
if (existsSync7(join8(cwd, ".cursor"))) {
|
|
5002
5240
|
if (ensureCursorRules(cwd)) platforms.push("Cursor");
|
|
5003
5241
|
}
|
|
5004
|
-
if (
|
|
5242
|
+
if (existsSync7(join8(cwd, ".github", "prompts")) || existsSync7(join8(cwd, ".github", "copilot-instructions.md"))) {
|
|
5005
5243
|
if (ensureCopilotPrompts(cwd)) platforms.push("GitHub Copilot");
|
|
5006
5244
|
}
|
|
5007
|
-
if (
|
|
5245
|
+
if (existsSync7(join8(cwd, ".windsurf"))) {
|
|
5008
5246
|
if (ensureWindsurfRules(cwd)) platforms.push("Windsurf");
|
|
5009
5247
|
}
|
|
5010
5248
|
return platforms;
|
|
5011
5249
|
}
|
|
5012
5250
|
|
|
5013
5251
|
// src/update-check.ts
|
|
5014
|
-
import { existsSync as
|
|
5252
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
5015
5253
|
import { get } from "https";
|
|
5016
5254
|
import { homedir as homedir3 } from "os";
|
|
5017
|
-
import { dirname as dirname2, join as
|
|
5255
|
+
import { dirname as dirname2, join as join9 } from "path";
|
|
5018
5256
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5019
|
-
var DATA_DIR =
|
|
5020
|
-
var CHECK_FILE =
|
|
5257
|
+
var DATA_DIR = join9(homedir3(), ".glassbox");
|
|
5258
|
+
var CHECK_FILE = join9(DATA_DIR, "last-update-check");
|
|
5021
5259
|
var PACKAGE_NAME = "glassbox";
|
|
5022
5260
|
function getCurrentVersion() {
|
|
5023
5261
|
try {
|
|
5024
5262
|
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
5025
|
-
const pkg = JSON.parse(
|
|
5263
|
+
const pkg = JSON.parse(readFileSync10(join9(dir, "..", "package.json"), "utf-8"));
|
|
5026
5264
|
return pkg.version;
|
|
5027
5265
|
} catch {
|
|
5028
5266
|
return "0.0.0";
|
|
@@ -5030,8 +5268,8 @@ function getCurrentVersion() {
|
|
|
5030
5268
|
}
|
|
5031
5269
|
function getLastCheckDate() {
|
|
5032
5270
|
try {
|
|
5033
|
-
if (
|
|
5034
|
-
return
|
|
5271
|
+
if (existsSync8(CHECK_FILE)) {
|
|
5272
|
+
return readFileSync10(CHECK_FILE, "utf-8").trim();
|
|
5035
5273
|
}
|
|
5036
5274
|
} catch {
|
|
5037
5275
|
}
|
|
@@ -5048,10 +5286,10 @@ function isFirstUseToday() {
|
|
|
5048
5286
|
return last !== today;
|
|
5049
5287
|
}
|
|
5050
5288
|
function fetchLatestVersion() {
|
|
5051
|
-
return new Promise((
|
|
5289
|
+
return new Promise((resolve5) => {
|
|
5052
5290
|
const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
5053
5291
|
if (res.statusCode !== 200) {
|
|
5054
|
-
|
|
5292
|
+
resolve5(null);
|
|
5055
5293
|
return;
|
|
5056
5294
|
}
|
|
5057
5295
|
let data = "";
|
|
@@ -5060,18 +5298,18 @@ function fetchLatestVersion() {
|
|
|
5060
5298
|
});
|
|
5061
5299
|
res.on("end", () => {
|
|
5062
5300
|
try {
|
|
5063
|
-
|
|
5301
|
+
resolve5(JSON.parse(data).version);
|
|
5064
5302
|
} catch {
|
|
5065
|
-
|
|
5303
|
+
resolve5(null);
|
|
5066
5304
|
}
|
|
5067
5305
|
});
|
|
5068
5306
|
});
|
|
5069
5307
|
req.on("error", () => {
|
|
5070
|
-
|
|
5308
|
+
resolve5(null);
|
|
5071
5309
|
});
|
|
5072
5310
|
req.on("timeout", () => {
|
|
5073
5311
|
req.destroy();
|
|
5074
|
-
|
|
5312
|
+
resolve5(null);
|
|
5075
5313
|
});
|
|
5076
5314
|
});
|
|
5077
5315
|
}
|
|
@@ -5209,7 +5447,7 @@ function parseArgs(argv) {
|
|
|
5209
5447
|
port = parseInt(args[++i], 10);
|
|
5210
5448
|
break;
|
|
5211
5449
|
case "--data-dir":
|
|
5212
|
-
dataDir =
|
|
5450
|
+
dataDir = resolve4(args[++i]);
|
|
5213
5451
|
break;
|
|
5214
5452
|
case "--resume":
|
|
5215
5453
|
resume = true;
|
|
@@ -5265,13 +5503,13 @@ async function main() {
|
|
|
5265
5503
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
5266
5504
|
}
|
|
5267
5505
|
if (debug) {
|
|
5268
|
-
console.log(`[debug] Build timestamp: ${"2026-03-
|
|
5506
|
+
console.log(`[debug] Build timestamp: ${"2026-03-19T04:05:06.487Z"}`);
|
|
5269
5507
|
}
|
|
5270
5508
|
if (projectDir) {
|
|
5271
5509
|
process.chdir(projectDir);
|
|
5272
5510
|
}
|
|
5273
5511
|
if (dataDir === null) {
|
|
5274
|
-
dataDir =
|
|
5512
|
+
dataDir = join10(process.cwd(), ".glassbox");
|
|
5275
5513
|
}
|
|
5276
5514
|
if (demo !== null) {
|
|
5277
5515
|
const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
|
|
@@ -5283,7 +5521,7 @@ async function main() {
|
|
|
5283
5521
|
}
|
|
5284
5522
|
process.exit(1);
|
|
5285
5523
|
}
|
|
5286
|
-
dataDir =
|
|
5524
|
+
dataDir = join10(tmpdir(), `glassbox-demo-${demo}-${Date.now()}`);
|
|
5287
5525
|
setDemoMode(demo);
|
|
5288
5526
|
console.log(`
|
|
5289
5527
|
DEMO MODE: ${scenario.label}
|