glassbox 0.6.0 → 0.7.0
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 +1149 -442
- package/dist/client/app.global.js +8 -8
- package/dist/client/history.global.js +1 -1
- package/dist/client/styles.css +1 -1
- package/package.json +7 -4
package/dist/cli.js
CHANGED
|
@@ -391,11 +391,11 @@ var init_queries = __esm({
|
|
|
391
391
|
});
|
|
392
392
|
|
|
393
393
|
// src/cli.ts
|
|
394
|
-
init_queries();
|
|
395
394
|
init_connection();
|
|
396
|
-
|
|
395
|
+
init_queries();
|
|
396
|
+
import { mkdirSync as mkdirSync8 } from "fs";
|
|
397
397
|
import { tmpdir } from "os";
|
|
398
|
-
import { join as
|
|
398
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
399
399
|
|
|
400
400
|
// src/debug.ts
|
|
401
401
|
var debugEnabled = false;
|
|
@@ -426,70 +426,15 @@ function debugLog(...args) {
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// src/ai/config.ts
|
|
429
|
-
import { spawnSync } from "child_process";
|
|
430
429
|
import { chmodSync, existsSync, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "fs";
|
|
431
430
|
import { homedir } from "os";
|
|
432
431
|
import { join as join2 } from "path";
|
|
433
432
|
|
|
434
|
-
// src/ai/
|
|
435
|
-
|
|
436
|
-
anthropic: "Anthropic",
|
|
437
|
-
openai: "OpenAI",
|
|
438
|
-
google: "Google"
|
|
439
|
-
};
|
|
440
|
-
var MODELS = {
|
|
441
|
-
anthropic: [
|
|
442
|
-
{ id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4", contextWindow: 2e5, isDefault: true },
|
|
443
|
-
{ id: "claude-haiku-4-20250514", name: "Claude Haiku 4", contextWindow: 2e5, isDefault: false }
|
|
444
|
-
],
|
|
445
|
-
openai: [
|
|
446
|
-
{ id: "gpt-4o", name: "GPT-4o", contextWindow: 128e3, isDefault: true },
|
|
447
|
-
{ id: "gpt-4o-mini", name: "GPT-4o Mini", contextWindow: 128e3, isDefault: false }
|
|
448
|
-
],
|
|
449
|
-
google: [
|
|
450
|
-
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", contextWindow: 1e6, isDefault: true },
|
|
451
|
-
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", contextWindow: 1e6, isDefault: false }
|
|
452
|
-
]
|
|
453
|
-
};
|
|
454
|
-
var ENV_KEY_NAMES = {
|
|
455
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
456
|
-
openai: "OPENAI_API_KEY",
|
|
457
|
-
google: "GEMINI_API_KEY"
|
|
458
|
-
};
|
|
459
|
-
function getDefaultModel(platform) {
|
|
460
|
-
const models = MODELS[platform];
|
|
461
|
-
const def = models.find((m) => m.isDefault);
|
|
462
|
-
return def ? def.id : models[0].id;
|
|
463
|
-
}
|
|
464
|
-
function getModelContextWindow(platform, modelId) {
|
|
465
|
-
const model = MODELS[platform].find((m) => m.id === modelId);
|
|
466
|
-
return model ? model.contextWindow : 128e3;
|
|
467
|
-
}
|
|
433
|
+
// src/ai/api-keys.ts
|
|
434
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
468
435
|
|
|
469
|
-
// src/ai/
|
|
470
|
-
|
|
471
|
-
var CONFIG_PATH = join2(CONFIG_DIR, "config.json");
|
|
472
|
-
function readConfigFile() {
|
|
473
|
-
try {
|
|
474
|
-
if (existsSync(CONFIG_PATH)) {
|
|
475
|
-
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
476
|
-
}
|
|
477
|
-
} catch {
|
|
478
|
-
}
|
|
479
|
-
return {};
|
|
480
|
-
}
|
|
481
|
-
function writeConfigFile(config) {
|
|
482
|
-
mkdirSync2(CONFIG_DIR, { recursive: true });
|
|
483
|
-
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
484
|
-
try {
|
|
485
|
-
chmodSync(CONFIG_PATH, 384);
|
|
486
|
-
} catch {
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
function getKeyFromEnv(platform) {
|
|
490
|
-
const envName = ENV_KEY_NAMES[platform];
|
|
491
|
-
return process.env[envName] ?? null;
|
|
492
|
-
}
|
|
436
|
+
// src/ai/keychain.ts
|
|
437
|
+
import { spawnSync } from "child_process";
|
|
493
438
|
var WIN_CRED_READ_PS = `
|
|
494
439
|
Add-Type -TypeDefinition @'
|
|
495
440
|
using System;
|
|
@@ -526,19 +471,19 @@ function getKeyFromKeychain(platform) {
|
|
|
526
471
|
try {
|
|
527
472
|
if (os === "darwin") {
|
|
528
473
|
const r = spawnSync("security", ["find-generic-password", "-s", "glassbox", "-a", account, "-w"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
529
|
-
const result =
|
|
474
|
+
const result = r.stdout.trim();
|
|
530
475
|
return r.status === 0 && result !== "" ? result : null;
|
|
531
476
|
}
|
|
532
477
|
if (os === "linux") {
|
|
533
478
|
const r = spawnSync("secret-tool", ["lookup", "service", "glassbox", "account", account], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
534
|
-
const result =
|
|
479
|
+
const result = r.stdout.trim();
|
|
535
480
|
return r.status === 0 && result !== "" ? result : null;
|
|
536
481
|
}
|
|
537
482
|
if (os === "win32") {
|
|
538
483
|
const target = winCredTarget(platform);
|
|
539
484
|
const script = WIN_CRED_READ_PS + `Write-Output ([CredHelper]::Read('${target}'))`;
|
|
540
485
|
const r = spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
541
|
-
const result =
|
|
486
|
+
const result = r.stdout.trim();
|
|
542
487
|
return r.status === 0 && result !== "" ? result : null;
|
|
543
488
|
}
|
|
544
489
|
} catch {
|
|
@@ -546,6 +491,81 @@ function getKeyFromKeychain(platform) {
|
|
|
546
491
|
}
|
|
547
492
|
return null;
|
|
548
493
|
}
|
|
494
|
+
function saveKeyToKeychain(platform, key) {
|
|
495
|
+
const os = process.platform;
|
|
496
|
+
const account = `${platform}-api-key`;
|
|
497
|
+
if (os === "darwin") {
|
|
498
|
+
spawnSync("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
499
|
+
spawnSync("security", ["add-generic-password", "-s", "glassbox", "-a", account, "-w", key]);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (os === "linux") {
|
|
503
|
+
spawnSync("secret-tool", ["store", "--label=Glassbox API Key", "service", "glassbox", "account", account], { input: key, encoding: "utf-8" });
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (os === "win32") {
|
|
507
|
+
const target = winCredTarget(platform);
|
|
508
|
+
const escapedKey = key.replace(/'/g, "''");
|
|
509
|
+
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedKey}'`;
|
|
510
|
+
spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
function isKeychainAvailable() {
|
|
514
|
+
const os = process.platform;
|
|
515
|
+
if (os === "darwin" || os === "win32") return true;
|
|
516
|
+
if (os === "linux") {
|
|
517
|
+
return spawnSync("which", ["secret-tool"], { stdio: "pipe" }).status === 0;
|
|
518
|
+
}
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
function getKeychainLabel() {
|
|
522
|
+
const os = process.platform;
|
|
523
|
+
if (os === "darwin") return "Keychain";
|
|
524
|
+
if (os === "linux") return "System Keyring";
|
|
525
|
+
if (os === "win32") return "Credential Manager";
|
|
526
|
+
return "System Keychain";
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/ai/models.ts
|
|
530
|
+
var PLATFORMS = {
|
|
531
|
+
anthropic: "Anthropic",
|
|
532
|
+
openai: "OpenAI",
|
|
533
|
+
google: "Google"
|
|
534
|
+
};
|
|
535
|
+
var MODELS = {
|
|
536
|
+
anthropic: [
|
|
537
|
+
{ id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4", contextWindow: 2e5, isDefault: true },
|
|
538
|
+
{ id: "claude-haiku-4-20250514", name: "Claude Haiku 4", contextWindow: 2e5, isDefault: false }
|
|
539
|
+
],
|
|
540
|
+
openai: [
|
|
541
|
+
{ id: "gpt-4o", name: "GPT-4o", contextWindow: 128e3, isDefault: true },
|
|
542
|
+
{ id: "gpt-4o-mini", name: "GPT-4o Mini", contextWindow: 128e3, isDefault: false }
|
|
543
|
+
],
|
|
544
|
+
google: [
|
|
545
|
+
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", contextWindow: 1e6, isDefault: true },
|
|
546
|
+
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", contextWindow: 1e6, isDefault: false }
|
|
547
|
+
]
|
|
548
|
+
};
|
|
549
|
+
var ENV_KEY_NAMES = {
|
|
550
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
551
|
+
openai: "OPENAI_API_KEY",
|
|
552
|
+
google: "GEMINI_API_KEY"
|
|
553
|
+
};
|
|
554
|
+
function getDefaultModel(platform) {
|
|
555
|
+
const models = MODELS[platform];
|
|
556
|
+
const def = models.find((m) => m.isDefault);
|
|
557
|
+
return def ? def.id : models[0].id;
|
|
558
|
+
}
|
|
559
|
+
function getModelContextWindow(platform, modelId) {
|
|
560
|
+
const model = MODELS[platform].find((m) => m.id === modelId);
|
|
561
|
+
return model ? model.contextWindow : 128e3;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/ai/api-keys.ts
|
|
565
|
+
function getKeyFromEnv(platform) {
|
|
566
|
+
const envName = ENV_KEY_NAMES[platform];
|
|
567
|
+
return process.env[envName] ?? null;
|
|
568
|
+
}
|
|
549
569
|
function getKeyFromConfig(platform) {
|
|
550
570
|
const config = readConfigFile();
|
|
551
571
|
const encoded = config.ai?.keys?.[platform];
|
|
@@ -565,20 +585,6 @@ function resolveAPIKey(platform) {
|
|
|
565
585
|
if (configKey !== null) return { key: configKey, source: "config" };
|
|
566
586
|
return { key: null, source: null };
|
|
567
587
|
}
|
|
568
|
-
function loadAIConfig() {
|
|
569
|
-
const config = readConfigFile();
|
|
570
|
-
const platform = config.ai?.platform ?? "anthropic";
|
|
571
|
-
const model = config.ai?.model ?? getDefaultModel(platform);
|
|
572
|
-
const { key, source } = resolveAPIKey(platform);
|
|
573
|
-
return { platform, model, apiKey: key, keySource: source };
|
|
574
|
-
}
|
|
575
|
-
function saveAIConfigPreferences(platform, model) {
|
|
576
|
-
const config = readConfigFile();
|
|
577
|
-
if (config.ai === void 0) config.ai = {};
|
|
578
|
-
config.ai.platform = platform;
|
|
579
|
-
config.ai.model = model;
|
|
580
|
-
writeConfigFile(config);
|
|
581
|
-
}
|
|
582
588
|
function saveAPIKey(platform, key, storage) {
|
|
583
589
|
if (storage === "keychain") {
|
|
584
590
|
saveKeyToKeychain(platform, key);
|
|
@@ -590,36 +596,17 @@ function saveAPIKey(platform, key, storage) {
|
|
|
590
596
|
writeConfigFile(config);
|
|
591
597
|
}
|
|
592
598
|
}
|
|
593
|
-
function saveKeyToKeychain(platform, key) {
|
|
594
|
-
const os = process.platform;
|
|
595
|
-
const account = `${platform}-api-key`;
|
|
596
|
-
if (os === "darwin") {
|
|
597
|
-
spawnSync("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
598
|
-
spawnSync("security", ["add-generic-password", "-s", "glassbox", "-a", account, "-w", key]);
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
if (os === "linux") {
|
|
602
|
-
spawnSync("secret-tool", ["store", "--label=Glassbox API Key", "service", "glassbox", "account", account], { input: key, encoding: "utf-8" });
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
if (os === "win32") {
|
|
606
|
-
const target = winCredTarget(platform);
|
|
607
|
-
const escapedKey = key.replace(/'/g, "''");
|
|
608
|
-
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedKey}'`;
|
|
609
|
-
spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
599
|
function deleteAPIKey(platform) {
|
|
613
600
|
const os = process.platform;
|
|
614
601
|
const account = `${platform}-api-key`;
|
|
615
602
|
try {
|
|
616
603
|
if (os === "darwin") {
|
|
617
|
-
|
|
604
|
+
spawnSync2("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
618
605
|
} else if (os === "linux") {
|
|
619
|
-
|
|
606
|
+
spawnSync2("secret-tool", ["clear", "service", "glassbox", "account", account], { stdio: "pipe" });
|
|
620
607
|
} else if (os === "win32") {
|
|
621
608
|
const target = winCredTarget(platform);
|
|
622
|
-
|
|
609
|
+
spawnSync2("powershell", ["-NoProfile", "-Command", "-"], { input: `cmdkey /delete:'${target}'`, encoding: "utf-8" });
|
|
623
610
|
}
|
|
624
611
|
} catch {
|
|
625
612
|
}
|
|
@@ -639,20 +626,40 @@ function detectAvailablePlatforms() {
|
|
|
639
626
|
}
|
|
640
627
|
return results;
|
|
641
628
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
629
|
+
|
|
630
|
+
// src/ai/config.ts
|
|
631
|
+
var CONFIG_DIR = join2(homedir(), ".glassbox");
|
|
632
|
+
var CONFIG_PATH = join2(CONFIG_DIR, "config.json");
|
|
633
|
+
function readConfigFile() {
|
|
634
|
+
try {
|
|
635
|
+
if (existsSync(CONFIG_PATH)) {
|
|
636
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
637
|
+
}
|
|
638
|
+
} catch {
|
|
647
639
|
}
|
|
648
|
-
return
|
|
640
|
+
return {};
|
|
649
641
|
}
|
|
650
|
-
function
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
642
|
+
function writeConfigFile(config) {
|
|
643
|
+
mkdirSync2(CONFIG_DIR, { recursive: true });
|
|
644
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
645
|
+
try {
|
|
646
|
+
chmodSync(CONFIG_PATH, 384);
|
|
647
|
+
} catch {
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function loadAIConfig() {
|
|
651
|
+
const config = readConfigFile();
|
|
652
|
+
const platform = config.ai?.platform ?? "anthropic";
|
|
653
|
+
const model = config.ai?.model ?? getDefaultModel(platform);
|
|
654
|
+
const { key, source } = resolveAPIKey(platform);
|
|
655
|
+
return { platform, model, apiKey: key, keySource: source };
|
|
656
|
+
}
|
|
657
|
+
function saveAIConfigPreferences(platform, model) {
|
|
658
|
+
const config = readConfigFile();
|
|
659
|
+
if (config.ai === void 0) config.ai = {};
|
|
660
|
+
config.ai.platform = platform;
|
|
661
|
+
config.ai.model = model;
|
|
662
|
+
writeConfigFile(config);
|
|
656
663
|
}
|
|
657
664
|
function loadGuidedReviewConfig() {
|
|
658
665
|
const config = readConfigFile();
|
|
@@ -1266,11 +1273,14 @@ async function setupAnnotations(fileIdMap) {
|
|
|
1266
1273
|
}
|
|
1267
1274
|
|
|
1268
1275
|
// src/git/diff.ts
|
|
1269
|
-
import { spawnSync as
|
|
1276
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
1270
1277
|
import { readFileSync as readFileSync2 } from "fs";
|
|
1271
1278
|
import { resolve } from "path";
|
|
1279
|
+
|
|
1280
|
+
// src/git/repo.ts
|
|
1281
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
1272
1282
|
function git(args, cwd) {
|
|
1273
|
-
const result =
|
|
1283
|
+
const result = spawnSync3("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
1274
1284
|
if (result.status === 0) return result.stdout;
|
|
1275
1285
|
if (result.stdout !== "") return result.stdout;
|
|
1276
1286
|
const err = new Error(result.stderr);
|
|
@@ -1294,6 +1304,21 @@ function isGitRepo(cwd) {
|
|
|
1294
1304
|
return false;
|
|
1295
1305
|
}
|
|
1296
1306
|
}
|
|
1307
|
+
function getHeadCommit(cwd) {
|
|
1308
|
+
return spawnSync3("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8" }).stdout.trim();
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/git/diff.ts
|
|
1312
|
+
function git2(args, cwd) {
|
|
1313
|
+
const result = spawnSync4("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
1314
|
+
if (result.status === 0) return result.stdout;
|
|
1315
|
+
if (result.stdout !== "") return result.stdout;
|
|
1316
|
+
const err = new Error(result.stderr);
|
|
1317
|
+
err.stdout = result.stdout;
|
|
1318
|
+
err.stderr = result.stderr;
|
|
1319
|
+
err.status = result.status;
|
|
1320
|
+
throw err;
|
|
1321
|
+
}
|
|
1297
1322
|
function getDiffArgs(mode) {
|
|
1298
1323
|
switch (mode.type) {
|
|
1299
1324
|
case "uncommitted":
|
|
@@ -1322,13 +1347,13 @@ function getFileDiffs(mode, cwd) {
|
|
|
1322
1347
|
const diffArgs = getDiffArgs(mode);
|
|
1323
1348
|
let rawDiff;
|
|
1324
1349
|
try {
|
|
1325
|
-
rawDiff =
|
|
1350
|
+
rawDiff = git2([...diffArgs, "-U3"], repoRoot);
|
|
1326
1351
|
} catch {
|
|
1327
1352
|
rawDiff = "";
|
|
1328
1353
|
}
|
|
1329
1354
|
const diffs = parseDiff(rawDiff);
|
|
1330
1355
|
if (mode.type === "uncommitted") {
|
|
1331
|
-
const untracked =
|
|
1356
|
+
const untracked = git2(["ls-files", "--others", "--exclude-standard"], repoRoot).trim();
|
|
1332
1357
|
if (untracked) {
|
|
1333
1358
|
for (const file of untracked.split("\n").filter(Boolean)) {
|
|
1334
1359
|
if (!diffs.some((d) => d.filePath === file)) {
|
|
@@ -1340,7 +1365,7 @@ function getFileDiffs(mode, cwd) {
|
|
|
1340
1365
|
return diffs;
|
|
1341
1366
|
}
|
|
1342
1367
|
function getAllFiles(repoRoot) {
|
|
1343
|
-
const files =
|
|
1368
|
+
const files = git2(["ls-files"], repoRoot).trim().split("\n").filter(Boolean);
|
|
1344
1369
|
return files.map((file) => createNewFileDiff(file, repoRoot));
|
|
1345
1370
|
}
|
|
1346
1371
|
function createNewFileDiff(filePath, repoRoot) {
|
|
@@ -1469,14 +1494,11 @@ function getFileContent(filePath, ref, cwd) {
|
|
|
1469
1494
|
if (ref === "working") {
|
|
1470
1495
|
return readFileSync2(resolve(repoRoot, filePath), "utf-8");
|
|
1471
1496
|
}
|
|
1472
|
-
return
|
|
1497
|
+
return git2(["show", `${ref}:${filePath}`], repoRoot);
|
|
1473
1498
|
} catch {
|
|
1474
1499
|
return "";
|
|
1475
1500
|
}
|
|
1476
1501
|
}
|
|
1477
|
-
function getHeadCommit(cwd) {
|
|
1478
|
-
return spawnSync2("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8" }).stdout.trim();
|
|
1479
|
-
}
|
|
1480
1502
|
function parseModeString(modeStr) {
|
|
1481
1503
|
if (modeStr === "uncommitted") return { type: "uncommitted" };
|
|
1482
1504
|
if (modeStr === "staged") return { type: "staged" };
|
|
@@ -1501,7 +1523,7 @@ function getSingleFileDiff(mode, filePath, repoRoot, extraFlags = "") {
|
|
|
1501
1523
|
args.push("--", filePath);
|
|
1502
1524
|
let rawDiff;
|
|
1503
1525
|
try {
|
|
1504
|
-
rawDiff =
|
|
1526
|
+
rawDiff = git2(args, repoRoot);
|
|
1505
1527
|
} catch {
|
|
1506
1528
|
rawDiff = "";
|
|
1507
1529
|
}
|
|
@@ -1573,7 +1595,9 @@ function acquireLock(dataDir) {
|
|
|
1573
1595
|
}
|
|
1574
1596
|
}
|
|
1575
1597
|
writeFileSync2(lockPath, JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
1576
|
-
const cleanup = () =>
|
|
1598
|
+
const cleanup = () => {
|
|
1599
|
+
releaseLock();
|
|
1600
|
+
};
|
|
1577
1601
|
process.on("exit", cleanup);
|
|
1578
1602
|
process.on("SIGINT", () => {
|
|
1579
1603
|
cleanup();
|
|
@@ -1585,7 +1609,7 @@ function acquireLock(dataDir) {
|
|
|
1585
1609
|
});
|
|
1586
1610
|
}
|
|
1587
1611
|
function releaseLock() {
|
|
1588
|
-
if (lockPath) {
|
|
1612
|
+
if (lockPath !== null) {
|
|
1589
1613
|
try {
|
|
1590
1614
|
rmSync2(lockPath, { force: true });
|
|
1591
1615
|
} catch {
|
|
@@ -1701,12 +1725,15 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1701
1725
|
// src/server.ts
|
|
1702
1726
|
import { serve } from "@hono/node-server";
|
|
1703
1727
|
import { exec } from "child_process";
|
|
1704
|
-
import { existsSync as
|
|
1705
|
-
import { Hono as
|
|
1706
|
-
import { dirname, join as
|
|
1728
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
1729
|
+
import { Hono as Hono7 } from "hono";
|
|
1730
|
+
import { dirname, join as join8 } from "path";
|
|
1707
1731
|
import { fileURLToPath } from "url";
|
|
1708
1732
|
|
|
1709
1733
|
// src/routes/ai-api.ts
|
|
1734
|
+
import { Hono as Hono3 } from "hono";
|
|
1735
|
+
|
|
1736
|
+
// src/routes/ai-analysis.ts
|
|
1710
1737
|
import { Hono } from "hono";
|
|
1711
1738
|
|
|
1712
1739
|
// src/ai/client.ts
|
|
@@ -2536,63 +2563,11 @@ async function mockGuidedAnalysisBatch(files) {
|
|
|
2536
2563
|
}));
|
|
2537
2564
|
}
|
|
2538
2565
|
|
|
2539
|
-
// src/routes/ai-
|
|
2566
|
+
// src/routes/ai-analysis.ts
|
|
2540
2567
|
init_queries();
|
|
2541
|
-
var
|
|
2568
|
+
var aiAnalysisRoutes = new Hono();
|
|
2542
2569
|
var cancelledAnalyses = /* @__PURE__ */ new Set();
|
|
2543
|
-
|
|
2544
|
-
const config = loadAIConfig();
|
|
2545
|
-
return c.json({
|
|
2546
|
-
platform: config.platform,
|
|
2547
|
-
model: config.model,
|
|
2548
|
-
keyConfigured: config.apiKey !== null || isAIServiceTest() || getDemoMode() !== null,
|
|
2549
|
-
keySource: config.keySource,
|
|
2550
|
-
guidedReview: loadGuidedReviewConfig()
|
|
2551
|
-
});
|
|
2552
|
-
});
|
|
2553
|
-
aiApiRoutes.post("/config", async (c) => {
|
|
2554
|
-
const body = await c.req.json();
|
|
2555
|
-
saveAIConfigPreferences(body.platform, body.model);
|
|
2556
|
-
if (body.guidedReview !== void 0) {
|
|
2557
|
-
saveGuidedReviewConfig(body.guidedReview);
|
|
2558
|
-
}
|
|
2559
|
-
return c.json({ ok: true });
|
|
2560
|
-
});
|
|
2561
|
-
aiApiRoutes.get("/models", (c) => {
|
|
2562
|
-
return c.json({
|
|
2563
|
-
platforms: PLATFORMS,
|
|
2564
|
-
models: MODELS
|
|
2565
|
-
});
|
|
2566
|
-
});
|
|
2567
|
-
aiApiRoutes.get("/key-status", (c) => {
|
|
2568
|
-
const platforms = ["anthropic", "openai", "google"];
|
|
2569
|
-
const status = {};
|
|
2570
|
-
for (const platform of platforms) {
|
|
2571
|
-
const { source } = resolveAPIKey(platform);
|
|
2572
|
-
status[platform] = { configured: source !== null, source };
|
|
2573
|
-
}
|
|
2574
|
-
return c.json({
|
|
2575
|
-
status,
|
|
2576
|
-
keychainAvailable: isKeychainAvailable(),
|
|
2577
|
-
keychainLabel: getKeychainLabel(),
|
|
2578
|
-
availablePlatforms: detectAvailablePlatforms()
|
|
2579
|
-
});
|
|
2580
|
-
});
|
|
2581
|
-
aiApiRoutes.post("/key", async (c) => {
|
|
2582
|
-
const body = await c.req.json();
|
|
2583
|
-
saveAPIKey(
|
|
2584
|
-
body.platform,
|
|
2585
|
-
body.key,
|
|
2586
|
-
body.storage
|
|
2587
|
-
);
|
|
2588
|
-
return c.json({ ok: true });
|
|
2589
|
-
});
|
|
2590
|
-
aiApiRoutes.delete("/key", (c) => {
|
|
2591
|
-
const platform = c.req.query("platform") ?? "anthropic";
|
|
2592
|
-
deleteAPIKey(platform);
|
|
2593
|
-
return c.json({ ok: true });
|
|
2594
|
-
});
|
|
2595
|
-
aiApiRoutes.post("/analyze", async (c) => {
|
|
2570
|
+
aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
2596
2571
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2597
2572
|
const repoRoot = c.get("repoRoot");
|
|
2598
2573
|
const body = await c.req.json();
|
|
@@ -2844,7 +2819,7 @@ async function runBatchedGuidedAnalysis(analysisId, batches, allFiles, config, r
|
|
|
2844
2819
|
"guided"
|
|
2845
2820
|
);
|
|
2846
2821
|
}
|
|
2847
|
-
|
|
2822
|
+
aiAnalysisRoutes.get("/analysis/:type", async (c) => {
|
|
2848
2823
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2849
2824
|
const analysisType = c.req.param("type");
|
|
2850
2825
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
@@ -2876,7 +2851,7 @@ aiApiRoutes.get("/analysis/:type", async (c) => {
|
|
|
2876
2851
|
}))
|
|
2877
2852
|
});
|
|
2878
2853
|
});
|
|
2879
|
-
|
|
2854
|
+
aiAnalysisRoutes.get("/analysis/:type/status", async (c) => {
|
|
2880
2855
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2881
2856
|
const analysisType = c.req.param("type");
|
|
2882
2857
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
@@ -2900,35 +2875,96 @@ aiApiRoutes.get("/analysis/:type/status", async (c) => {
|
|
|
2900
2875
|
progressTotal: analysis.progress_total
|
|
2901
2876
|
});
|
|
2902
2877
|
});
|
|
2903
|
-
|
|
2878
|
+
aiAnalysisRoutes.get("/debug-status", (c) => {
|
|
2904
2879
|
return c.json({ enabled: isDebug() });
|
|
2905
2880
|
});
|
|
2906
|
-
|
|
2881
|
+
aiAnalysisRoutes.post("/debug-log", async (c) => {
|
|
2907
2882
|
if (!isDebug()) return c.json({ ok: true });
|
|
2908
2883
|
const body = await c.req.json();
|
|
2909
2884
|
debugLog(`[client] ${body.message}`);
|
|
2910
2885
|
return c.json({ ok: true });
|
|
2911
2886
|
});
|
|
2912
|
-
|
|
2887
|
+
aiAnalysisRoutes.get("/preferences", async (c) => {
|
|
2913
2888
|
const prefs = await getUserPreferences();
|
|
2914
2889
|
return c.json(prefs);
|
|
2915
2890
|
});
|
|
2916
|
-
|
|
2891
|
+
aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
2917
2892
|
const body = await c.req.json();
|
|
2918
2893
|
await saveUserPreferences(body);
|
|
2919
2894
|
return c.json({ ok: true });
|
|
2920
2895
|
});
|
|
2921
2896
|
|
|
2897
|
+
// src/routes/ai-config.ts
|
|
2898
|
+
import { Hono as Hono2 } from "hono";
|
|
2899
|
+
var aiConfigRoutes = new Hono2();
|
|
2900
|
+
aiConfigRoutes.get("/config", (c) => {
|
|
2901
|
+
const config = loadAIConfig();
|
|
2902
|
+
return c.json({
|
|
2903
|
+
platform: config.platform,
|
|
2904
|
+
model: config.model,
|
|
2905
|
+
keyConfigured: config.apiKey !== null || isAIServiceTest() || getDemoMode() !== null,
|
|
2906
|
+
keySource: config.keySource,
|
|
2907
|
+
guidedReview: loadGuidedReviewConfig()
|
|
2908
|
+
});
|
|
2909
|
+
});
|
|
2910
|
+
aiConfigRoutes.post("/config", async (c) => {
|
|
2911
|
+
const body = await c.req.json();
|
|
2912
|
+
saveAIConfigPreferences(body.platform, body.model);
|
|
2913
|
+
if (body.guidedReview !== void 0) {
|
|
2914
|
+
saveGuidedReviewConfig(body.guidedReview);
|
|
2915
|
+
}
|
|
2916
|
+
return c.json({ ok: true });
|
|
2917
|
+
});
|
|
2918
|
+
aiConfigRoutes.get("/models", (c) => {
|
|
2919
|
+
return c.json({
|
|
2920
|
+
platforms: PLATFORMS,
|
|
2921
|
+
models: MODELS
|
|
2922
|
+
});
|
|
2923
|
+
});
|
|
2924
|
+
aiConfigRoutes.get("/key-status", (c) => {
|
|
2925
|
+
const platforms = ["anthropic", "openai", "google"];
|
|
2926
|
+
const status = {};
|
|
2927
|
+
for (const platform of platforms) {
|
|
2928
|
+
const { source } = resolveAPIKey(platform);
|
|
2929
|
+
status[platform] = { configured: source !== null, source };
|
|
2930
|
+
}
|
|
2931
|
+
return c.json({
|
|
2932
|
+
status,
|
|
2933
|
+
keychainAvailable: isKeychainAvailable(),
|
|
2934
|
+
keychainLabel: getKeychainLabel(),
|
|
2935
|
+
availablePlatforms: detectAvailablePlatforms()
|
|
2936
|
+
});
|
|
2937
|
+
});
|
|
2938
|
+
aiConfigRoutes.post("/key", async (c) => {
|
|
2939
|
+
const body = await c.req.json();
|
|
2940
|
+
saveAPIKey(
|
|
2941
|
+
body.platform,
|
|
2942
|
+
body.key,
|
|
2943
|
+
body.storage
|
|
2944
|
+
);
|
|
2945
|
+
return c.json({ ok: true });
|
|
2946
|
+
});
|
|
2947
|
+
aiConfigRoutes.delete("/key", (c) => {
|
|
2948
|
+
const platform = c.req.query("platform") ?? "anthropic";
|
|
2949
|
+
deleteAPIKey(platform);
|
|
2950
|
+
return c.json({ ok: true });
|
|
2951
|
+
});
|
|
2952
|
+
|
|
2953
|
+
// src/routes/ai-api.ts
|
|
2954
|
+
var aiApiRoutes = new Hono3();
|
|
2955
|
+
aiApiRoutes.route("/", aiConfigRoutes);
|
|
2956
|
+
aiApiRoutes.route("/", aiAnalysisRoutes);
|
|
2957
|
+
|
|
2922
2958
|
// src/routes/api.ts
|
|
2923
2959
|
init_queries();
|
|
2924
|
-
import { execFileSync, spawnSync as
|
|
2960
|
+
import { execFileSync, spawnSync as spawnSync7 } from "child_process";
|
|
2925
2961
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2926
|
-
import { Hono as
|
|
2962
|
+
import { Hono as Hono4 } from "hono";
|
|
2927
2963
|
import { join as join6, resolve as resolve3 } from "path";
|
|
2928
2964
|
|
|
2929
2965
|
// src/export/generate.ts
|
|
2930
2966
|
init_queries();
|
|
2931
|
-
import { spawnSync as
|
|
2967
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
2932
2968
|
import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2933
2969
|
import { homedir as homedir2 } from "os";
|
|
2934
2970
|
import { join as join4 } from "path";
|
|
@@ -2947,7 +2983,7 @@ function saveDismissals(data) {
|
|
|
2947
2983
|
writeFileSync3(DISMISS_FILE, JSON.stringify(data), "utf-8");
|
|
2948
2984
|
}
|
|
2949
2985
|
function isGlassboxGitignored(repoRoot) {
|
|
2950
|
-
const result =
|
|
2986
|
+
const result = spawnSync5("git", ["check-ignore", "-q", ".glassbox"], { cwd: repoRoot, stdio: "pipe" });
|
|
2951
2987
|
return result.status === 0;
|
|
2952
2988
|
}
|
|
2953
2989
|
function shouldPromptGitignore(repoRoot) {
|
|
@@ -3061,137 +3097,53 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
|
|
|
3061
3097
|
writeFileSync3(latestPath, content, "utf-8");
|
|
3062
3098
|
return latestPath;
|
|
3063
3099
|
}
|
|
3064
|
-
return archivePath;
|
|
3065
|
-
}
|
|
3066
|
-
|
|
3067
|
-
// src/export/auto-export.ts
|
|
3068
|
-
var debounceTimer = null;
|
|
3069
|
-
var DEBOUNCE_MS = 2e3;
|
|
3070
|
-
function scheduleAutoExport(reviewId, repoRoot) {
|
|
3071
|
-
if (debounceTimer !== null) clearTimeout(debounceTimer);
|
|
3072
|
-
debounceTimer = setTimeout(() => {
|
|
3073
|
-
debounceTimer = null;
|
|
3074
|
-
void generateReviewExport(reviewId, repoRoot, true);
|
|
3075
|
-
}, DEBOUNCE_MS);
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
// src/git/image.ts
|
|
3079
|
-
import { spawnSync as spawnSync4 } from "child_process";
|
|
3080
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
3081
|
-
import { resolve as resolve2 } from "path";
|
|
3082
|
-
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
3083
|
-
function isImageFile(filePath) {
|
|
3084
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3085
|
-
return IMAGE_EXTENSIONS.has(ext);
|
|
3086
|
-
}
|
|
3087
|
-
function isSvgFile(filePath) {
|
|
3088
|
-
return filePath.slice(filePath.lastIndexOf(".")).toLowerCase() === ".svg";
|
|
3089
|
-
}
|
|
3090
|
-
function getContentType(filePath) {
|
|
3091
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3092
|
-
switch (ext) {
|
|
3093
|
-
case ".png":
|
|
3094
|
-
return "image/png";
|
|
3095
|
-
case ".jpg":
|
|
3096
|
-
case ".jpeg":
|
|
3097
|
-
return "image/jpeg";
|
|
3098
|
-
case ".gif":
|
|
3099
|
-
return "image/gif";
|
|
3100
|
-
case ".webp":
|
|
3101
|
-
return "image/webp";
|
|
3102
|
-
case ".svg":
|
|
3103
|
-
return "image/svg+xml";
|
|
3104
|
-
default:
|
|
3105
|
-
return "application/octet-stream";
|
|
3106
|
-
}
|
|
3107
|
-
}
|
|
3108
|
-
function getOldRef(mode) {
|
|
3109
|
-
switch (mode.type) {
|
|
3110
|
-
case "uncommitted":
|
|
3111
|
-
return "HEAD";
|
|
3112
|
-
case "staged":
|
|
3113
|
-
return "HEAD";
|
|
3114
|
-
case "unstaged":
|
|
3115
|
-
return null;
|
|
3116
|
-
// old = index, use ':'
|
|
3117
|
-
case "commit":
|
|
3118
|
-
return `${mode.sha}~1`;
|
|
3119
|
-
case "range":
|
|
3120
|
-
return mode.from;
|
|
3121
|
-
case "branch":
|
|
3122
|
-
return mode.name;
|
|
3123
|
-
case "files":
|
|
3124
|
-
return "HEAD";
|
|
3125
|
-
case "all":
|
|
3126
|
-
return null;
|
|
3127
|
-
}
|
|
3128
|
-
}
|
|
3129
|
-
function getNewRef(mode) {
|
|
3130
|
-
switch (mode.type) {
|
|
3131
|
-
case "uncommitted":
|
|
3132
|
-
return null;
|
|
3133
|
-
// working tree
|
|
3134
|
-
case "staged":
|
|
3135
|
-
return null;
|
|
3136
|
-
// index, but git show : works
|
|
3137
|
-
case "unstaged":
|
|
3138
|
-
return null;
|
|
3139
|
-
// working tree
|
|
3140
|
-
case "commit":
|
|
3141
|
-
return mode.sha;
|
|
3142
|
-
case "range":
|
|
3143
|
-
return mode.to;
|
|
3144
|
-
case "branch":
|
|
3145
|
-
return "HEAD";
|
|
3146
|
-
case "files":
|
|
3147
|
-
return null;
|
|
3148
|
-
case "all":
|
|
3149
|
-
return null;
|
|
3150
|
-
}
|
|
3100
|
+
return archivePath;
|
|
3151
3101
|
}
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3102
|
+
|
|
3103
|
+
// src/export/auto-export.ts
|
|
3104
|
+
var debounceTimer = null;
|
|
3105
|
+
var DEBOUNCE_MS = 2e3;
|
|
3106
|
+
function scheduleAutoExport(reviewId, repoRoot) {
|
|
3107
|
+
if (debounceTimer !== null) clearTimeout(debounceTimer);
|
|
3108
|
+
debounceTimer = setTimeout(() => {
|
|
3109
|
+
debounceTimer = null;
|
|
3110
|
+
void generateReviewExport(reviewId, repoRoot, true);
|
|
3111
|
+
}, DEBOUNCE_MS);
|
|
3157
3112
|
}
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3113
|
+
|
|
3114
|
+
// src/git/image.ts
|
|
3115
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
3116
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3117
|
+
import { resolve as resolve2 } from "path";
|
|
3118
|
+
|
|
3119
|
+
// src/git/image-metadata.ts
|
|
3120
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
3121
|
+
function isImageFile(filePath) {
|
|
3122
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3123
|
+
return IMAGE_EXTENSIONS.has(ext);
|
|
3164
3124
|
}
|
|
3165
|
-
function
|
|
3166
|
-
|
|
3167
|
-
const path = oldPath ?? filePath;
|
|
3168
|
-
if (ref === null) {
|
|
3169
|
-
const data2 = readWorkingFile(path, repoRoot);
|
|
3170
|
-
if (!data2) return null;
|
|
3171
|
-
return { data: data2, size: data2.length };
|
|
3172
|
-
}
|
|
3173
|
-
const actualRef = mode.type === "unstaged" ? ":" : ref;
|
|
3174
|
-
const data = gitShowFile(actualRef, path, repoRoot);
|
|
3175
|
-
if (!data) return null;
|
|
3176
|
-
return { data, size: data.length };
|
|
3125
|
+
function isSvgFile(filePath) {
|
|
3126
|
+
return filePath.slice(filePath.lastIndexOf(".")).toLowerCase() === ".svg";
|
|
3177
3127
|
}
|
|
3178
|
-
function
|
|
3179
|
-
const
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3128
|
+
function getContentType(filePath) {
|
|
3129
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3130
|
+
switch (ext) {
|
|
3131
|
+
case ".png":
|
|
3132
|
+
return "image/png";
|
|
3133
|
+
case ".jpg":
|
|
3134
|
+
case ".jpeg":
|
|
3135
|
+
return "image/jpeg";
|
|
3136
|
+
case ".gif":
|
|
3137
|
+
return "image/gif";
|
|
3138
|
+
case ".webp":
|
|
3139
|
+
return "image/webp";
|
|
3140
|
+
case ".svg":
|
|
3141
|
+
return "image/svg+xml";
|
|
3142
|
+
default:
|
|
3143
|
+
return "application/octet-stream";
|
|
3189
3144
|
}
|
|
3190
|
-
const data = gitShowFile(ref, filePath, repoRoot);
|
|
3191
|
-
if (!data) return null;
|
|
3192
|
-
return { data, size: data.length };
|
|
3193
3145
|
}
|
|
3194
|
-
|
|
3146
|
+
function extractMetadata(data, filePath) {
|
|
3195
3147
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3196
3148
|
if (ext === ".svg") {
|
|
3197
3149
|
const text = data.toString("utf-8");
|
|
@@ -3233,9 +3185,9 @@ function formatMetadataLines(meta) {
|
|
|
3233
3185
|
lines.push(`Dimensions: ${meta.width} \xD7 ${meta.height}`);
|
|
3234
3186
|
}
|
|
3235
3187
|
lines.push(`File size: ${formatBytes(meta.fileSize)}`);
|
|
3236
|
-
if (meta.colorSpace) lines.push(`Color space: ${meta.colorSpace}`);
|
|
3188
|
+
if (meta.colorSpace !== null) lines.push(`Color space: ${meta.colorSpace}`);
|
|
3237
3189
|
if (meta.channels !== null) lines.push(`Channels: ${meta.channels}`);
|
|
3238
|
-
if (meta.depth) lines.push(`Bit depth: ${meta.depth}`);
|
|
3190
|
+
if (meta.depth !== null) lines.push(`Bit depth: ${meta.depth}`);
|
|
3239
3191
|
if (meta.hasAlpha !== null) lines.push(`Alpha: ${meta.hasAlpha ? "yes" : "no"}`);
|
|
3240
3192
|
if (meta.density !== null) lines.push(`Density: ${meta.density} DPI`);
|
|
3241
3193
|
if (meta.exif) {
|
|
@@ -3376,7 +3328,95 @@ function parseWebp(data) {
|
|
|
3376
3328
|
width = (data[24] | data[25] << 8 | data[26] << 16) + 1;
|
|
3377
3329
|
height = (data[27] | data[28] << 8 | data[29] << 16) + 1;
|
|
3378
3330
|
}
|
|
3379
|
-
return { format: "webp", width, height, colorSpace: "srgb", channels: hasAlpha ? 4 : 3, depth: null, hasAlpha, density: null, exif: null };
|
|
3331
|
+
return { format: "webp", width, height, colorSpace: "srgb", channels: hasAlpha === true ? 4 : 3, depth: null, hasAlpha, density: null, exif: null };
|
|
3332
|
+
}
|
|
3333
|
+
|
|
3334
|
+
// src/git/image.ts
|
|
3335
|
+
function getOldRef(mode) {
|
|
3336
|
+
switch (mode.type) {
|
|
3337
|
+
case "uncommitted":
|
|
3338
|
+
return "HEAD";
|
|
3339
|
+
case "staged":
|
|
3340
|
+
return "HEAD";
|
|
3341
|
+
case "unstaged":
|
|
3342
|
+
return null;
|
|
3343
|
+
// old = index, use ':'
|
|
3344
|
+
case "commit":
|
|
3345
|
+
return `${mode.sha}~1`;
|
|
3346
|
+
case "range":
|
|
3347
|
+
return mode.from;
|
|
3348
|
+
case "branch":
|
|
3349
|
+
return mode.name;
|
|
3350
|
+
case "files":
|
|
3351
|
+
return "HEAD";
|
|
3352
|
+
case "all":
|
|
3353
|
+
return null;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function getNewRef(mode) {
|
|
3357
|
+
switch (mode.type) {
|
|
3358
|
+
case "uncommitted":
|
|
3359
|
+
return null;
|
|
3360
|
+
// working tree
|
|
3361
|
+
case "staged":
|
|
3362
|
+
return null;
|
|
3363
|
+
// index, but git show : works
|
|
3364
|
+
case "unstaged":
|
|
3365
|
+
return null;
|
|
3366
|
+
// working tree
|
|
3367
|
+
case "commit":
|
|
3368
|
+
return mode.sha;
|
|
3369
|
+
case "range":
|
|
3370
|
+
return mode.to;
|
|
3371
|
+
case "branch":
|
|
3372
|
+
return "HEAD";
|
|
3373
|
+
case "files":
|
|
3374
|
+
return null;
|
|
3375
|
+
case "all":
|
|
3376
|
+
return null;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
function gitShowFile(ref, filePath, repoRoot) {
|
|
3380
|
+
const spec = ref === ":" ? `:${filePath}` : `${ref}:${filePath}`;
|
|
3381
|
+
const result = spawnSync6("git", ["show", spec], { cwd: repoRoot, maxBuffer: 50 * 1024 * 1024 });
|
|
3382
|
+
if (result.status !== 0 || result.stdout.length === 0) return null;
|
|
3383
|
+
return result.stdout;
|
|
3384
|
+
}
|
|
3385
|
+
function readWorkingFile(filePath, repoRoot) {
|
|
3386
|
+
try {
|
|
3387
|
+
return readFileSync5(resolve2(repoRoot, filePath));
|
|
3388
|
+
} catch {
|
|
3389
|
+
return null;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
function getOldImage(mode, filePath, oldPath, repoRoot) {
|
|
3393
|
+
const ref = getOldRef(mode);
|
|
3394
|
+
const path = oldPath ?? filePath;
|
|
3395
|
+
if (ref === null) {
|
|
3396
|
+
const data2 = readWorkingFile(path, repoRoot);
|
|
3397
|
+
if (!data2) return null;
|
|
3398
|
+
return { data: data2, size: data2.length };
|
|
3399
|
+
}
|
|
3400
|
+
const actualRef = mode.type === "unstaged" ? ":" : ref;
|
|
3401
|
+
const data = gitShowFile(actualRef, path, repoRoot);
|
|
3402
|
+
if (!data) return null;
|
|
3403
|
+
return { data, size: data.length };
|
|
3404
|
+
}
|
|
3405
|
+
function getNewImage(mode, filePath, repoRoot) {
|
|
3406
|
+
const ref = getNewRef(mode);
|
|
3407
|
+
if (ref === null) {
|
|
3408
|
+
if (mode.type === "staged") {
|
|
3409
|
+
const data3 = gitShowFile(":", filePath, repoRoot);
|
|
3410
|
+
if (!data3) return null;
|
|
3411
|
+
return { data: data3, size: data3.length };
|
|
3412
|
+
}
|
|
3413
|
+
const data2 = readWorkingFile(filePath, repoRoot);
|
|
3414
|
+
if (!data2) return null;
|
|
3415
|
+
return { data: data2, size: data2.length };
|
|
3416
|
+
}
|
|
3417
|
+
const data = gitShowFile(ref, filePath, repoRoot);
|
|
3418
|
+
if (!data) return null;
|
|
3419
|
+
return { data, size: data.length };
|
|
3380
3420
|
}
|
|
3381
3421
|
|
|
3382
3422
|
// src/git/svg-rasterize.ts
|
|
@@ -3411,63 +3451,62 @@ function loadSystemFonts() {
|
|
|
3411
3451
|
return buffers;
|
|
3412
3452
|
}
|
|
3413
3453
|
function getFontCandidates() {
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
default:
|
|
3469
|
-
return [];
|
|
3454
|
+
const os = process.platform;
|
|
3455
|
+
if (os === "darwin") {
|
|
3456
|
+
const sys = "/System/Library/Fonts";
|
|
3457
|
+
const sup = "/System/Library/Fonts/Supplemental";
|
|
3458
|
+
return [
|
|
3459
|
+
// Core system fonts (serif, sans-serif, monospace)
|
|
3460
|
+
join5(sys, "Helvetica.ttc"),
|
|
3461
|
+
join5(sys, "Times.ttc"),
|
|
3462
|
+
join5(sys, "Courier.ttc"),
|
|
3463
|
+
join5(sys, "Menlo.ttc"),
|
|
3464
|
+
join5(sys, "SFPro.ttf"),
|
|
3465
|
+
join5(sys, "SFNS.ttf"),
|
|
3466
|
+
join5(sys, "SFNSMono.ttf"),
|
|
3467
|
+
// Supplemental (common named fonts in SVGs)
|
|
3468
|
+
join5(sup, "Arial.ttf"),
|
|
3469
|
+
join5(sup, "Arial Bold.ttf"),
|
|
3470
|
+
join5(sup, "Georgia.ttf"),
|
|
3471
|
+
join5(sup, "Verdana.ttf"),
|
|
3472
|
+
join5(sup, "Tahoma.ttf"),
|
|
3473
|
+
join5(sup, "Trebuchet MS.ttf"),
|
|
3474
|
+
join5(sup, "Impact.ttf"),
|
|
3475
|
+
join5(sup, "Comic Sans MS.ttf"),
|
|
3476
|
+
join5(sup, "Courier New.ttf"),
|
|
3477
|
+
join5(sup, "Times New Roman.ttf")
|
|
3478
|
+
];
|
|
3479
|
+
}
|
|
3480
|
+
if (os === "linux") {
|
|
3481
|
+
return [
|
|
3482
|
+
// DejaVu (most common Linux fallback)
|
|
3483
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
|
3484
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
|
3485
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf",
|
|
3486
|
+
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf",
|
|
3487
|
+
// Liberation (metric-compatible with Arial/Times/Courier)
|
|
3488
|
+
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
|
3489
|
+
"/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf",
|
|
3490
|
+
"/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
|
|
3491
|
+
// Noto (common on modern distros)
|
|
3492
|
+
"/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf"
|
|
3493
|
+
];
|
|
3494
|
+
}
|
|
3495
|
+
if (os === "win32") {
|
|
3496
|
+
const winFonts = join5(process.env.WINDIR ?? "C:\\Windows", "Fonts");
|
|
3497
|
+
return [
|
|
3498
|
+
join5(winFonts, "arial.ttf"),
|
|
3499
|
+
join5(winFonts, "arialbd.ttf"),
|
|
3500
|
+
join5(winFonts, "times.ttf"),
|
|
3501
|
+
join5(winFonts, "cour.ttf"),
|
|
3502
|
+
join5(winFonts, "verdana.ttf"),
|
|
3503
|
+
join5(winFonts, "tahoma.ttf"),
|
|
3504
|
+
join5(winFonts, "georgia.ttf"),
|
|
3505
|
+
join5(winFonts, "consola.ttf"),
|
|
3506
|
+
join5(winFonts, "segoeui.ttf")
|
|
3507
|
+
];
|
|
3470
3508
|
}
|
|
3509
|
+
return [];
|
|
3471
3510
|
}
|
|
3472
3511
|
function parseSvgDimensions(svg) {
|
|
3473
3512
|
const widthMatch = svg.match(/\bwidth\s*=\s*["']([^"']+)["']/);
|
|
@@ -3831,7 +3870,7 @@ function pushIndentSymbol(root, stack, sym, indent, lines, lineIdx) {
|
|
|
3831
3870
|
}
|
|
3832
3871
|
|
|
3833
3872
|
// src/routes/api.ts
|
|
3834
|
-
var apiRoutes = new
|
|
3873
|
+
var apiRoutes = new Hono4();
|
|
3835
3874
|
function resolveReviewId(c) {
|
|
3836
3875
|
return c.req.query("reviewId") ?? c.get("reviewId");
|
|
3837
3876
|
}
|
|
@@ -4034,7 +4073,7 @@ apiRoutes.get("/outline/:fileId", async (c) => {
|
|
|
4034
4073
|
apiRoutes.get("/symbol-definition", async (c) => {
|
|
4035
4074
|
const name = c.req.query("name");
|
|
4036
4075
|
const currentFileId = c.req.query("currentFileId");
|
|
4037
|
-
if (
|
|
4076
|
+
if (name === void 0 || name === "") return c.json({ definitions: [] });
|
|
4038
4077
|
const reviewId = resolveReviewId(c);
|
|
4039
4078
|
const repoRoot = c.get("repoRoot");
|
|
4040
4079
|
const definitions = [];
|
|
@@ -4056,7 +4095,7 @@ apiRoutes.get("/symbol-definition", async (c) => {
|
|
|
4056
4095
|
}
|
|
4057
4096
|
if (definitions.length === 0) {
|
|
4058
4097
|
try {
|
|
4059
|
-
const allFiles =
|
|
4098
|
+
const allFiles = spawnSync7("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).stdout.trim().split("\n").filter(Boolean);
|
|
4060
4099
|
for (const filePath of allFiles) {
|
|
4061
4100
|
if (searchedPaths.has(filePath)) continue;
|
|
4062
4101
|
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
@@ -4091,7 +4130,7 @@ function collectDefinitions(symbols, targetName, fileId, filePath, out) {
|
|
|
4091
4130
|
if (sym.name === targetName) {
|
|
4092
4131
|
out.push({ fileId, filePath, name: sym.name, kind: sym.kind, line: sym.line });
|
|
4093
4132
|
}
|
|
4094
|
-
if (sym.children
|
|
4133
|
+
if (sym.children.length > 0) {
|
|
4095
4134
|
collectDefinitions(sym.children, targetName, fileId, filePath, out);
|
|
4096
4135
|
}
|
|
4097
4136
|
}
|
|
@@ -4152,10 +4191,8 @@ apiRoutes.get("/image/:fileId/metadata", async (c) => {
|
|
|
4152
4191
|
const status = diff.status ?? "modified";
|
|
4153
4192
|
const oldImage = status !== "added" ? getOldImage(mode, file.file_path, oldPath, repoRoot) : null;
|
|
4154
4193
|
const newImage = status !== "deleted" ? getNewImage(mode, file.file_path, repoRoot) : null;
|
|
4155
|
-
const
|
|
4156
|
-
|
|
4157
|
-
newImage ? extractMetadata(newImage.data, file.file_path) : null
|
|
4158
|
-
]);
|
|
4194
|
+
const oldMeta = oldImage !== null ? extractMetadata(oldImage.data, oldPath ?? file.file_path) : null;
|
|
4195
|
+
const newMeta = newImage !== null ? extractMetadata(newImage.data, file.file_path) : null;
|
|
4159
4196
|
return c.json({
|
|
4160
4197
|
old: oldMeta ? formatMetadataLines(oldMeta) : null,
|
|
4161
4198
|
new: newMeta ? formatMetadataLines(newMeta) : null
|
|
@@ -4192,8 +4229,8 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
4192
4229
|
});
|
|
4193
4230
|
|
|
4194
4231
|
// src/routes/pages.tsx
|
|
4195
|
-
import { readFileSync as
|
|
4196
|
-
import { Hono as
|
|
4232
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4233
|
+
import { Hono as Hono5 } from "hono";
|
|
4197
4234
|
import { resolve as resolve4 } from "path";
|
|
4198
4235
|
|
|
4199
4236
|
// src/utils/escapeHtml.ts
|
|
@@ -4508,10 +4545,10 @@ function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
|
4508
4545
|
"data-file-path": file.file_path,
|
|
4509
4546
|
"data-has-old": String(hasOld),
|
|
4510
4547
|
"data-has-new": String(hasNew),
|
|
4511
|
-
...baseWidth ? { "data-base-width": String(baseWidth) } : {},
|
|
4512
|
-
...baseHeight ? { "data-base-height": String(baseHeight) } : {},
|
|
4548
|
+
...baseWidth !== void 0 ? { "data-base-width": String(baseWidth) } : {},
|
|
4549
|
+
...baseHeight !== void 0 ? { "data-base-height": String(baseHeight) } : {},
|
|
4513
4550
|
children: [
|
|
4514
|
-
fontWarning && /* @__PURE__ */ jsx("div", { className: "image-font-warning", children: "This SVG uses text that may render differently depending on locally installed fonts." }),
|
|
4551
|
+
fontWarning === true && /* @__PURE__ */ jsx("div", { className: "image-font-warning", children: "This SVG uses text that may render differently depending on locally installed fonts." }),
|
|
4515
4552
|
/* @__PURE__ */ jsx("div", { className: "image-diff-panel image-diff-metadata", "data-panel": "metadata", children: /* @__PURE__ */ jsx("div", { className: "image-metadata-loading", children: "Loading metadata..." }) }),
|
|
4516
4553
|
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: [
|
|
4517
4554
|
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
@@ -4970,9 +5007,570 @@ function FileList({ files, annotationCounts, staleCounts }) {
|
|
|
4970
5007
|
return /* @__PURE__ */ jsx("div", { className: "file-list", children: /* @__PURE__ */ jsx("div", { className: "file-list-items", children: /* @__PURE__ */ jsx(TreeView, { node: tree, depth: 0, annotationCounts, staleCounts }) }) });
|
|
4971
5008
|
}
|
|
4972
5009
|
|
|
5010
|
+
// src/themes/built-in.ts
|
|
5011
|
+
var THEME_VARIABLES = [
|
|
5012
|
+
"bg",
|
|
5013
|
+
"bg-surface",
|
|
5014
|
+
"bg-hover",
|
|
5015
|
+
"bg-active",
|
|
5016
|
+
"text",
|
|
5017
|
+
"text-dim",
|
|
5018
|
+
"text-bright",
|
|
5019
|
+
"accent",
|
|
5020
|
+
"accent-hover",
|
|
5021
|
+
"green",
|
|
5022
|
+
"red",
|
|
5023
|
+
"yellow",
|
|
5024
|
+
"orange",
|
|
5025
|
+
"blue",
|
|
5026
|
+
"purple",
|
|
5027
|
+
"teal",
|
|
5028
|
+
"border",
|
|
5029
|
+
"diff-add-bg",
|
|
5030
|
+
"diff-add-border",
|
|
5031
|
+
"diff-remove-bg",
|
|
5032
|
+
"diff-remove-border",
|
|
5033
|
+
"diff-context-bg",
|
|
5034
|
+
"gutter-bg",
|
|
5035
|
+
"gutter-text"
|
|
5036
|
+
];
|
|
5037
|
+
var dark = {
|
|
5038
|
+
"bg": "#1e1e2e",
|
|
5039
|
+
"bg-surface": "#252536",
|
|
5040
|
+
"bg-hover": "#2d2d44",
|
|
5041
|
+
"bg-active": "#363652",
|
|
5042
|
+
"text": "#cdd6f4",
|
|
5043
|
+
"text-dim": "#8888aa",
|
|
5044
|
+
"text-bright": "#ffffff",
|
|
5045
|
+
"accent": "#89b4fa",
|
|
5046
|
+
"accent-hover": "#74a8fc",
|
|
5047
|
+
"green": "#a6e3a1",
|
|
5048
|
+
"red": "#f38ba8",
|
|
5049
|
+
"yellow": "#f9e2af",
|
|
5050
|
+
"orange": "#fab387",
|
|
5051
|
+
"blue": "#89b4fa",
|
|
5052
|
+
"purple": "#cba6f7",
|
|
5053
|
+
"teal": "#94e2d5",
|
|
5054
|
+
"border": "#363652",
|
|
5055
|
+
"diff-add-bg": "rgba(166, 227, 161, 0.1)",
|
|
5056
|
+
"diff-add-border": "rgba(166, 227, 161, 0.3)",
|
|
5057
|
+
"diff-remove-bg": "rgba(243, 139, 168, 0.1)",
|
|
5058
|
+
"diff-remove-border": "rgba(243, 139, 168, 0.3)",
|
|
5059
|
+
"diff-context-bg": "transparent",
|
|
5060
|
+
"gutter-bg": "#1a1a2e",
|
|
5061
|
+
"gutter-text": "#555577"
|
|
5062
|
+
};
|
|
5063
|
+
var light = {
|
|
5064
|
+
"bg": "#ffffff",
|
|
5065
|
+
"bg-surface": "#f6f8fa",
|
|
5066
|
+
"bg-hover": "#eaeef2",
|
|
5067
|
+
"bg-active": "#dde3e9",
|
|
5068
|
+
"text": "#1f2328",
|
|
5069
|
+
"text-dim": "#656d76",
|
|
5070
|
+
"text-bright": "#000000",
|
|
5071
|
+
"accent": "#0969da",
|
|
5072
|
+
"accent-hover": "#0550ae",
|
|
5073
|
+
"green": "#1a7f37",
|
|
5074
|
+
"red": "#cf222e",
|
|
5075
|
+
"yellow": "#9a6700",
|
|
5076
|
+
"orange": "#bc4c00",
|
|
5077
|
+
"blue": "#0969da",
|
|
5078
|
+
"purple": "#8250df",
|
|
5079
|
+
"teal": "#0e7c6b",
|
|
5080
|
+
"border": "#d0d7de",
|
|
5081
|
+
"diff-add-bg": "rgba(26, 127, 55, 0.08)",
|
|
5082
|
+
"diff-add-border": "rgba(26, 127, 55, 0.25)",
|
|
5083
|
+
"diff-remove-bg": "rgba(207, 34, 46, 0.08)",
|
|
5084
|
+
"diff-remove-border": "rgba(207, 34, 46, 0.25)",
|
|
5085
|
+
"diff-context-bg": "transparent",
|
|
5086
|
+
"gutter-bg": "#f6f8fa",
|
|
5087
|
+
"gutter-text": "#8b949e"
|
|
5088
|
+
};
|
|
5089
|
+
var highContrastDark = {
|
|
5090
|
+
"bg": "#0a0a0a",
|
|
5091
|
+
"bg-surface": "#1a1a1a",
|
|
5092
|
+
"bg-hover": "#2a2a2a",
|
|
5093
|
+
"bg-active": "#3a3a3a",
|
|
5094
|
+
"text": "#f0f0f0",
|
|
5095
|
+
"text-dim": "#b0b0b0",
|
|
5096
|
+
"text-bright": "#ffffff",
|
|
5097
|
+
"accent": "#6db3f2",
|
|
5098
|
+
"accent-hover": "#8ec5f7",
|
|
5099
|
+
"green": "#73e06e",
|
|
5100
|
+
"red": "#ff6b6b",
|
|
5101
|
+
"yellow": "#ffd93d",
|
|
5102
|
+
"orange": "#ffab57",
|
|
5103
|
+
"blue": "#6db3f2",
|
|
5104
|
+
"purple": "#c59eff",
|
|
5105
|
+
"teal": "#5ee6d0",
|
|
5106
|
+
"border": "#555555",
|
|
5107
|
+
"diff-add-bg": "rgba(115, 224, 110, 0.15)",
|
|
5108
|
+
"diff-add-border": "rgba(115, 224, 110, 0.5)",
|
|
5109
|
+
"diff-remove-bg": "rgba(255, 107, 107, 0.15)",
|
|
5110
|
+
"diff-remove-border": "rgba(255, 107, 107, 0.5)",
|
|
5111
|
+
"diff-context-bg": "transparent",
|
|
5112
|
+
"gutter-bg": "#111111",
|
|
5113
|
+
"gutter-text": "#888888"
|
|
5114
|
+
};
|
|
5115
|
+
var highContrastLight = {
|
|
5116
|
+
"bg": "#ffffff",
|
|
5117
|
+
"bg-surface": "#f0f0f0",
|
|
5118
|
+
"bg-hover": "#e0e0e0",
|
|
5119
|
+
"bg-active": "#d0d0d0",
|
|
5120
|
+
"text": "#111111",
|
|
5121
|
+
"text-dim": "#444444",
|
|
5122
|
+
"text-bright": "#000000",
|
|
5123
|
+
"accent": "#0043a8",
|
|
5124
|
+
"accent-hover": "#003080",
|
|
5125
|
+
"green": "#006b1f",
|
|
5126
|
+
"red": "#b80000",
|
|
5127
|
+
"yellow": "#785600",
|
|
5128
|
+
"orange": "#8a3400",
|
|
5129
|
+
"blue": "#0043a8",
|
|
5130
|
+
"purple": "#5b21b6",
|
|
5131
|
+
"teal": "#005e4f",
|
|
5132
|
+
"border": "#767676",
|
|
5133
|
+
"diff-add-bg": "rgba(0, 107, 31, 0.1)",
|
|
5134
|
+
"diff-add-border": "rgba(0, 107, 31, 0.4)",
|
|
5135
|
+
"diff-remove-bg": "rgba(184, 0, 0, 0.1)",
|
|
5136
|
+
"diff-remove-border": "rgba(184, 0, 0, 0.4)",
|
|
5137
|
+
"diff-context-bg": "transparent",
|
|
5138
|
+
"gutter-bg": "#f0f0f0",
|
|
5139
|
+
"gutter-text": "#555555"
|
|
5140
|
+
};
|
|
5141
|
+
var dracula = {
|
|
5142
|
+
"bg": "#282a36",
|
|
5143
|
+
"bg-surface": "#2d303e",
|
|
5144
|
+
"bg-hover": "#343746",
|
|
5145
|
+
"bg-active": "#3e4151",
|
|
5146
|
+
"text": "#f8f8f2",
|
|
5147
|
+
"text-dim": "#8b8da4",
|
|
5148
|
+
"text-bright": "#ffffff",
|
|
5149
|
+
"accent": "#bd93f9",
|
|
5150
|
+
"accent-hover": "#caa5fb",
|
|
5151
|
+
"green": "#50fa7b",
|
|
5152
|
+
"red": "#ff5555",
|
|
5153
|
+
"yellow": "#f1fa8c",
|
|
5154
|
+
"orange": "#ffb86c",
|
|
5155
|
+
"blue": "#8be9fd",
|
|
5156
|
+
"purple": "#bd93f9",
|
|
5157
|
+
"teal": "#8be9fd",
|
|
5158
|
+
"border": "#44475a",
|
|
5159
|
+
"diff-add-bg": "rgba(80, 250, 123, 0.1)",
|
|
5160
|
+
"diff-add-border": "rgba(80, 250, 123, 0.3)",
|
|
5161
|
+
"diff-remove-bg": "rgba(255, 85, 85, 0.1)",
|
|
5162
|
+
"diff-remove-border": "rgba(255, 85, 85, 0.3)",
|
|
5163
|
+
"diff-context-bg": "transparent",
|
|
5164
|
+
"gutter-bg": "#21222c",
|
|
5165
|
+
"gutter-text": "#6272a4"
|
|
5166
|
+
};
|
|
5167
|
+
var tokyoNight = {
|
|
5168
|
+
"bg": "#1a1b26",
|
|
5169
|
+
"bg-surface": "#1f2233",
|
|
5170
|
+
"bg-hover": "#292d42",
|
|
5171
|
+
"bg-active": "#33374e",
|
|
5172
|
+
"text": "#a9b1d6",
|
|
5173
|
+
"text-dim": "#565f89",
|
|
5174
|
+
"text-bright": "#c0caf5",
|
|
5175
|
+
"accent": "#7aa2f7",
|
|
5176
|
+
"accent-hover": "#89b0fa",
|
|
5177
|
+
"green": "#9ece6a",
|
|
5178
|
+
"red": "#f7768e",
|
|
5179
|
+
"yellow": "#e0af68",
|
|
5180
|
+
"orange": "#ff9e64",
|
|
5181
|
+
"blue": "#7aa2f7",
|
|
5182
|
+
"purple": "#bb9af7",
|
|
5183
|
+
"teal": "#73daca",
|
|
5184
|
+
"border": "#2f3351",
|
|
5185
|
+
"diff-add-bg": "rgba(158, 206, 106, 0.1)",
|
|
5186
|
+
"diff-add-border": "rgba(158, 206, 106, 0.3)",
|
|
5187
|
+
"diff-remove-bg": "rgba(247, 118, 142, 0.1)",
|
|
5188
|
+
"diff-remove-border": "rgba(247, 118, 142, 0.3)",
|
|
5189
|
+
"diff-context-bg": "transparent",
|
|
5190
|
+
"gutter-bg": "#16172a",
|
|
5191
|
+
"gutter-text": "#3b4261"
|
|
5192
|
+
};
|
|
5193
|
+
var oneDarkPro = {
|
|
5194
|
+
"bg": "#282c34",
|
|
5195
|
+
"bg-surface": "#2c313a",
|
|
5196
|
+
"bg-hover": "#333842",
|
|
5197
|
+
"bg-active": "#3b4048",
|
|
5198
|
+
"text": "#abb2bf",
|
|
5199
|
+
"text-dim": "#636d83",
|
|
5200
|
+
"text-bright": "#d7dae0",
|
|
5201
|
+
"accent": "#61afef",
|
|
5202
|
+
"accent-hover": "#519fdf",
|
|
5203
|
+
"green": "#98c379",
|
|
5204
|
+
"red": "#e06c75",
|
|
5205
|
+
"yellow": "#e5c07b",
|
|
5206
|
+
"orange": "#d19a66",
|
|
5207
|
+
"blue": "#61afef",
|
|
5208
|
+
"purple": "#c678dd",
|
|
5209
|
+
"teal": "#56b6c2",
|
|
5210
|
+
"border": "#3b4048",
|
|
5211
|
+
"diff-add-bg": "rgba(152, 195, 121, 0.1)",
|
|
5212
|
+
"diff-add-border": "rgba(152, 195, 121, 0.3)",
|
|
5213
|
+
"diff-remove-bg": "rgba(224, 108, 117, 0.1)",
|
|
5214
|
+
"diff-remove-border": "rgba(224, 108, 117, 0.3)",
|
|
5215
|
+
"diff-context-bg": "transparent",
|
|
5216
|
+
"gutter-bg": "#23272e",
|
|
5217
|
+
"gutter-text": "#495162"
|
|
5218
|
+
};
|
|
5219
|
+
var solarizedDark = {
|
|
5220
|
+
"bg": "#002b36",
|
|
5221
|
+
"bg-surface": "#073642",
|
|
5222
|
+
"bg-hover": "#0a4050",
|
|
5223
|
+
"bg-active": "#0d4d5e",
|
|
5224
|
+
"text": "#839496",
|
|
5225
|
+
"text-dim": "#586e75",
|
|
5226
|
+
"text-bright": "#eee8d5",
|
|
5227
|
+
"accent": "#268bd2",
|
|
5228
|
+
"accent-hover": "#1a7cc0",
|
|
5229
|
+
"green": "#859900",
|
|
5230
|
+
"red": "#dc322f",
|
|
5231
|
+
"yellow": "#b58900",
|
|
5232
|
+
"orange": "#cb4b16",
|
|
5233
|
+
"blue": "#268bd2",
|
|
5234
|
+
"purple": "#6c71c4",
|
|
5235
|
+
"teal": "#2aa198",
|
|
5236
|
+
"border": "#0a4050",
|
|
5237
|
+
"diff-add-bg": "rgba(133, 153, 0, 0.12)",
|
|
5238
|
+
"diff-add-border": "rgba(133, 153, 0, 0.3)",
|
|
5239
|
+
"diff-remove-bg": "rgba(220, 50, 47, 0.12)",
|
|
5240
|
+
"diff-remove-border": "rgba(220, 50, 47, 0.3)",
|
|
5241
|
+
"diff-context-bg": "transparent",
|
|
5242
|
+
"gutter-bg": "#002028",
|
|
5243
|
+
"gutter-text": "#4a6568"
|
|
5244
|
+
};
|
|
5245
|
+
var solarizedLight = {
|
|
5246
|
+
"bg": "#fdf6e3",
|
|
5247
|
+
"bg-surface": "#eee8d5",
|
|
5248
|
+
"bg-hover": "#e6dfca",
|
|
5249
|
+
"bg-active": "#ddd6c1",
|
|
5250
|
+
"text": "#657b83",
|
|
5251
|
+
"text-dim": "#93a1a1",
|
|
5252
|
+
"text-bright": "#073642",
|
|
5253
|
+
"accent": "#268bd2",
|
|
5254
|
+
"accent-hover": "#1a7cc0",
|
|
5255
|
+
"green": "#859900",
|
|
5256
|
+
"red": "#dc322f",
|
|
5257
|
+
"yellow": "#b58900",
|
|
5258
|
+
"orange": "#cb4b16",
|
|
5259
|
+
"blue": "#268bd2",
|
|
5260
|
+
"purple": "#6c71c4",
|
|
5261
|
+
"teal": "#2aa198",
|
|
5262
|
+
"border": "#ddd6c1",
|
|
5263
|
+
"diff-add-bg": "rgba(133, 153, 0, 0.1)",
|
|
5264
|
+
"diff-add-border": "rgba(133, 153, 0, 0.25)",
|
|
5265
|
+
"diff-remove-bg": "rgba(220, 50, 47, 0.1)",
|
|
5266
|
+
"diff-remove-border": "rgba(220, 50, 47, 0.25)",
|
|
5267
|
+
"diff-context-bg": "transparent",
|
|
5268
|
+
"gutter-bg": "#eee8d5",
|
|
5269
|
+
"gutter-text": "#93a1a1"
|
|
5270
|
+
};
|
|
5271
|
+
var monokai = {
|
|
5272
|
+
"bg": "#272822",
|
|
5273
|
+
"bg-surface": "#2d2e27",
|
|
5274
|
+
"bg-hover": "#3e3d32",
|
|
5275
|
+
"bg-active": "#49483e",
|
|
5276
|
+
"text": "#f8f8f2",
|
|
5277
|
+
"text-dim": "#75715e",
|
|
5278
|
+
"text-bright": "#ffffff",
|
|
5279
|
+
"accent": "#66d9ef",
|
|
5280
|
+
"accent-hover": "#55c8de",
|
|
5281
|
+
"green": "#a6e22e",
|
|
5282
|
+
"red": "#f92672",
|
|
5283
|
+
"yellow": "#e6db74",
|
|
5284
|
+
"orange": "#fd971f",
|
|
5285
|
+
"blue": "#66d9ef",
|
|
5286
|
+
"purple": "#ae81ff",
|
|
5287
|
+
"teal": "#66d9ef",
|
|
5288
|
+
"border": "#49483e",
|
|
5289
|
+
"diff-add-bg": "rgba(166, 226, 46, 0.1)",
|
|
5290
|
+
"diff-add-border": "rgba(166, 226, 46, 0.3)",
|
|
5291
|
+
"diff-remove-bg": "rgba(249, 38, 114, 0.1)",
|
|
5292
|
+
"diff-remove-border": "rgba(249, 38, 114, 0.3)",
|
|
5293
|
+
"diff-context-bg": "transparent",
|
|
5294
|
+
"gutter-bg": "#222218",
|
|
5295
|
+
"gutter-text": "#575848"
|
|
5296
|
+
};
|
|
5297
|
+
var nord = {
|
|
5298
|
+
"bg": "#2e3440",
|
|
5299
|
+
"bg-surface": "#3b4252",
|
|
5300
|
+
"bg-hover": "#434c5e",
|
|
5301
|
+
"bg-active": "#4c566a",
|
|
5302
|
+
"text": "#d8dee9",
|
|
5303
|
+
"text-dim": "#7b88a1",
|
|
5304
|
+
"text-bright": "#eceff4",
|
|
5305
|
+
"accent": "#88c0d0",
|
|
5306
|
+
"accent-hover": "#81a1c1",
|
|
5307
|
+
"green": "#a3be8c",
|
|
5308
|
+
"red": "#bf616a",
|
|
5309
|
+
"yellow": "#ebcb8b",
|
|
5310
|
+
"orange": "#d08770",
|
|
5311
|
+
"blue": "#81a1c1",
|
|
5312
|
+
"purple": "#b48ead",
|
|
5313
|
+
"teal": "#8fbcbb",
|
|
5314
|
+
"border": "#4c566a",
|
|
5315
|
+
"diff-add-bg": "rgba(163, 190, 140, 0.1)",
|
|
5316
|
+
"diff-add-border": "rgba(163, 190, 140, 0.3)",
|
|
5317
|
+
"diff-remove-bg": "rgba(191, 97, 106, 0.1)",
|
|
5318
|
+
"diff-remove-border": "rgba(191, 97, 106, 0.3)",
|
|
5319
|
+
"diff-context-bg": "transparent",
|
|
5320
|
+
"gutter-bg": "#2a303c",
|
|
5321
|
+
"gutter-text": "#5b6578"
|
|
5322
|
+
};
|
|
5323
|
+
var gruvboxDark = {
|
|
5324
|
+
"bg": "#282828",
|
|
5325
|
+
"bg-surface": "#3c3836",
|
|
5326
|
+
"bg-hover": "#504945",
|
|
5327
|
+
"bg-active": "#665c54",
|
|
5328
|
+
"text": "#ebdbb2",
|
|
5329
|
+
"text-dim": "#928374",
|
|
5330
|
+
"text-bright": "#fbf1c7",
|
|
5331
|
+
"accent": "#83a598",
|
|
5332
|
+
"accent-hover": "#76988b",
|
|
5333
|
+
"green": "#b8bb26",
|
|
5334
|
+
"red": "#fb4934",
|
|
5335
|
+
"yellow": "#fabd2f",
|
|
5336
|
+
"orange": "#fe8019",
|
|
5337
|
+
"blue": "#83a598",
|
|
5338
|
+
"purple": "#d3869b",
|
|
5339
|
+
"teal": "#8ec07c",
|
|
5340
|
+
"border": "#504945",
|
|
5341
|
+
"diff-add-bg": "rgba(184, 187, 38, 0.1)",
|
|
5342
|
+
"diff-add-border": "rgba(184, 187, 38, 0.3)",
|
|
5343
|
+
"diff-remove-bg": "rgba(251, 73, 52, 0.1)",
|
|
5344
|
+
"diff-remove-border": "rgba(251, 73, 52, 0.3)",
|
|
5345
|
+
"diff-context-bg": "transparent",
|
|
5346
|
+
"gutter-bg": "#232323",
|
|
5347
|
+
"gutter-text": "#665c54"
|
|
5348
|
+
};
|
|
5349
|
+
var gruvboxLight = {
|
|
5350
|
+
"bg": "#fbf1c7",
|
|
5351
|
+
"bg-surface": "#f2e5bc",
|
|
5352
|
+
"bg-hover": "#ebdbb2",
|
|
5353
|
+
"bg-active": "#d5c4a1",
|
|
5354
|
+
"text": "#3c3836",
|
|
5355
|
+
"text-dim": "#7c6f64",
|
|
5356
|
+
"text-bright": "#282828",
|
|
5357
|
+
"accent": "#427b58",
|
|
5358
|
+
"accent-hover": "#376b4c",
|
|
5359
|
+
"green": "#79740e",
|
|
5360
|
+
"red": "#9d0006",
|
|
5361
|
+
"yellow": "#b57614",
|
|
5362
|
+
"orange": "#af3a03",
|
|
5363
|
+
"blue": "#076678",
|
|
5364
|
+
"purple": "#8f3f71",
|
|
5365
|
+
"teal": "#427b58",
|
|
5366
|
+
"border": "#d5c4a1",
|
|
5367
|
+
"diff-add-bg": "rgba(121, 116, 14, 0.1)",
|
|
5368
|
+
"diff-add-border": "rgba(121, 116, 14, 0.25)",
|
|
5369
|
+
"diff-remove-bg": "rgba(157, 0, 6, 0.1)",
|
|
5370
|
+
"diff-remove-border": "rgba(157, 0, 6, 0.25)",
|
|
5371
|
+
"diff-context-bg": "transparent",
|
|
5372
|
+
"gutter-bg": "#f2e5bc",
|
|
5373
|
+
"gutter-text": "#928374"
|
|
5374
|
+
};
|
|
5375
|
+
var githubDark = {
|
|
5376
|
+
"bg": "#0d1117",
|
|
5377
|
+
"bg-surface": "#161b22",
|
|
5378
|
+
"bg-hover": "#1c2128",
|
|
5379
|
+
"bg-active": "#262c36",
|
|
5380
|
+
"text": "#c9d1d9",
|
|
5381
|
+
"text-dim": "#8b949e",
|
|
5382
|
+
"text-bright": "#f0f6fc",
|
|
5383
|
+
"accent": "#58a6ff",
|
|
5384
|
+
"accent-hover": "#4090e0",
|
|
5385
|
+
"green": "#3fb950",
|
|
5386
|
+
"red": "#f85149",
|
|
5387
|
+
"yellow": "#d29922",
|
|
5388
|
+
"orange": "#db6d28",
|
|
5389
|
+
"blue": "#58a6ff",
|
|
5390
|
+
"purple": "#bc8cff",
|
|
5391
|
+
"teal": "#39d353",
|
|
5392
|
+
"border": "#30363d",
|
|
5393
|
+
"diff-add-bg": "rgba(63, 185, 80, 0.1)",
|
|
5394
|
+
"diff-add-border": "rgba(63, 185, 80, 0.3)",
|
|
5395
|
+
"diff-remove-bg": "rgba(248, 81, 73, 0.1)",
|
|
5396
|
+
"diff-remove-border": "rgba(248, 81, 73, 0.3)",
|
|
5397
|
+
"diff-context-bg": "transparent",
|
|
5398
|
+
"gutter-bg": "#0a0e14",
|
|
5399
|
+
"gutter-text": "#484f58"
|
|
5400
|
+
};
|
|
5401
|
+
var rosePine = {
|
|
5402
|
+
"bg": "#191724",
|
|
5403
|
+
"bg-surface": "#1f1d2e",
|
|
5404
|
+
"bg-hover": "#26233a",
|
|
5405
|
+
"bg-active": "#2a2740",
|
|
5406
|
+
"text": "#e0def4",
|
|
5407
|
+
"text-dim": "#6e6a86",
|
|
5408
|
+
"text-bright": "#f0efff",
|
|
5409
|
+
"accent": "#c4a7e7",
|
|
5410
|
+
"accent-hover": "#b498d7",
|
|
5411
|
+
"green": "#31748f",
|
|
5412
|
+
"red": "#eb6f92",
|
|
5413
|
+
"yellow": "#f6c177",
|
|
5414
|
+
"orange": "#ea9a97",
|
|
5415
|
+
"blue": "#9ccfd8",
|
|
5416
|
+
"purple": "#c4a7e7",
|
|
5417
|
+
"teal": "#9ccfd8",
|
|
5418
|
+
"border": "#2a2740",
|
|
5419
|
+
"diff-add-bg": "rgba(49, 116, 143, 0.12)",
|
|
5420
|
+
"diff-add-border": "rgba(49, 116, 143, 0.3)",
|
|
5421
|
+
"diff-remove-bg": "rgba(235, 111, 146, 0.12)",
|
|
5422
|
+
"diff-remove-border": "rgba(235, 111, 146, 0.3)",
|
|
5423
|
+
"diff-context-bg": "transparent",
|
|
5424
|
+
"gutter-bg": "#16141f",
|
|
5425
|
+
"gutter-text": "#524f67"
|
|
5426
|
+
};
|
|
5427
|
+
var ayuDark = {
|
|
5428
|
+
"bg": "#0b0e14",
|
|
5429
|
+
"bg-surface": "#0f131a",
|
|
5430
|
+
"bg-hover": "#151a23",
|
|
5431
|
+
"bg-active": "#1c222d",
|
|
5432
|
+
"text": "#bfbdb6",
|
|
5433
|
+
"text-dim": "#636a76",
|
|
5434
|
+
"text-bright": "#e6e1cf",
|
|
5435
|
+
"accent": "#e6b450",
|
|
5436
|
+
"accent-hover": "#d9a740",
|
|
5437
|
+
"green": "#7fd962",
|
|
5438
|
+
"red": "#d95757",
|
|
5439
|
+
"yellow": "#e6b450",
|
|
5440
|
+
"orange": "#ff8f40",
|
|
5441
|
+
"blue": "#59c2ff",
|
|
5442
|
+
"purple": "#d2a6ff",
|
|
5443
|
+
"teal": "#95e6cb",
|
|
5444
|
+
"border": "#1c222d",
|
|
5445
|
+
"diff-add-bg": "rgba(127, 217, 98, 0.1)",
|
|
5446
|
+
"diff-add-border": "rgba(127, 217, 98, 0.3)",
|
|
5447
|
+
"diff-remove-bg": "rgba(217, 87, 87, 0.1)",
|
|
5448
|
+
"diff-remove-border": "rgba(217, 87, 87, 0.3)",
|
|
5449
|
+
"diff-context-bg": "transparent",
|
|
5450
|
+
"gutter-bg": "#080a10",
|
|
5451
|
+
"gutter-text": "#3d424d"
|
|
5452
|
+
};
|
|
5453
|
+
var BUILT_IN_THEMES = [
|
|
5454
|
+
{ id: "dark", name: "Dark", builtIn: true, colors: dark },
|
|
5455
|
+
{ id: "light", name: "Light", builtIn: true, colors: light },
|
|
5456
|
+
{ id: "high-contrast-dark", name: "High Contrast Dark", builtIn: true, colors: highContrastDark },
|
|
5457
|
+
{ id: "high-contrast-light", name: "High Contrast Light", builtIn: true, colors: highContrastLight },
|
|
5458
|
+
{ id: "dracula", name: "Dracula", builtIn: true, colors: dracula },
|
|
5459
|
+
{ id: "tokyo-night", name: "Tokyo Night", builtIn: true, colors: tokyoNight },
|
|
5460
|
+
{ id: "one-dark-pro", name: "One Dark Pro", builtIn: true, colors: oneDarkPro },
|
|
5461
|
+
{ id: "solarized-dark", name: "Solarized Dark", builtIn: true, colors: solarizedDark },
|
|
5462
|
+
{ id: "solarized-light", name: "Solarized Light", builtIn: true, colors: solarizedLight },
|
|
5463
|
+
{ id: "monokai", name: "Monokai", builtIn: true, colors: monokai },
|
|
5464
|
+
{ id: "nord", name: "Nord", builtIn: true, colors: nord },
|
|
5465
|
+
{ id: "gruvbox-dark", name: "Gruvbox Dark", builtIn: true, colors: gruvboxDark },
|
|
5466
|
+
{ id: "gruvbox-light", name: "Gruvbox Light", builtIn: true, colors: gruvboxLight },
|
|
5467
|
+
{ id: "github-dark", name: "GitHub Dark", builtIn: true, colors: githubDark },
|
|
5468
|
+
{ id: "rose-pine", name: "Ros\xE9 Pine", builtIn: true, colors: rosePine },
|
|
5469
|
+
{ id: "ayu-dark", name: "Ayu Dark", builtIn: true, colors: ayuDark }
|
|
5470
|
+
];
|
|
5471
|
+
var DEFAULT_THEME_ID = "dark";
|
|
5472
|
+
function getBuiltInTheme(id) {
|
|
5473
|
+
return BUILT_IN_THEMES.find((t) => t.id === id);
|
|
5474
|
+
}
|
|
5475
|
+
function themeToInlineStyle(colors) {
|
|
5476
|
+
return THEME_VARIABLES.map((v) => `--${v}:${colors[v]}`).join(";");
|
|
5477
|
+
}
|
|
5478
|
+
|
|
5479
|
+
// src/themes/config.ts
|
|
5480
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readdirSync, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
5481
|
+
import { homedir as homedir3 } from "os";
|
|
5482
|
+
import { join as join7 } from "path";
|
|
5483
|
+
var CONFIG_DIR2 = join7(homedir3(), ".glassbox");
|
|
5484
|
+
var CONFIG_PATH2 = join7(CONFIG_DIR2, "config.json");
|
|
5485
|
+
var THEMES_DIR = join7(CONFIG_DIR2, "themes");
|
|
5486
|
+
function readConfigFile2() {
|
|
5487
|
+
try {
|
|
5488
|
+
if (existsSync6(CONFIG_PATH2)) {
|
|
5489
|
+
return JSON.parse(readFileSync8(CONFIG_PATH2, "utf-8"));
|
|
5490
|
+
}
|
|
5491
|
+
} catch {
|
|
5492
|
+
}
|
|
5493
|
+
return {};
|
|
5494
|
+
}
|
|
5495
|
+
function writeConfigFile2(config) {
|
|
5496
|
+
mkdirSync5(CONFIG_DIR2, { recursive: true });
|
|
5497
|
+
writeFileSync5(CONFIG_PATH2, JSON.stringify(config, null, 2), "utf-8");
|
|
5498
|
+
}
|
|
5499
|
+
function getActiveThemeId() {
|
|
5500
|
+
const config = readConfigFile2();
|
|
5501
|
+
const theme = config.theme;
|
|
5502
|
+
const active = theme?.active;
|
|
5503
|
+
return active ?? DEFAULT_THEME_ID;
|
|
5504
|
+
}
|
|
5505
|
+
function setActiveThemeId(id) {
|
|
5506
|
+
const config = readConfigFile2();
|
|
5507
|
+
if (config.theme === void 0) config.theme = {};
|
|
5508
|
+
config.theme.active = id;
|
|
5509
|
+
writeConfigFile2(config);
|
|
5510
|
+
}
|
|
5511
|
+
function loadCustomThemes() {
|
|
5512
|
+
if (!existsSync6(THEMES_DIR)) return [];
|
|
5513
|
+
const themes = [];
|
|
5514
|
+
try {
|
|
5515
|
+
const files = readdirSync(THEMES_DIR).filter((f) => f.endsWith(".json"));
|
|
5516
|
+
for (const file of files) {
|
|
5517
|
+
try {
|
|
5518
|
+
const data = JSON.parse(readFileSync8(join7(THEMES_DIR, file), "utf-8"));
|
|
5519
|
+
if (data.id !== void 0 && data.id !== "" && data.name !== void 0 && data.name !== "" && data.colors !== void 0) {
|
|
5520
|
+
themes.push({ id: data.id, name: data.name, colors: data.colors, builtIn: false, baseTheme: data.baseTheme ?? "" });
|
|
5521
|
+
}
|
|
5522
|
+
} catch {
|
|
5523
|
+
}
|
|
5524
|
+
}
|
|
5525
|
+
} catch {
|
|
5526
|
+
}
|
|
5527
|
+
return themes;
|
|
5528
|
+
}
|
|
5529
|
+
function saveCustomTheme(theme) {
|
|
5530
|
+
mkdirSync5(THEMES_DIR, { recursive: true });
|
|
5531
|
+
const filePath = join7(THEMES_DIR, `${theme.id}.json`);
|
|
5532
|
+
writeFileSync5(filePath, JSON.stringify(theme, null, 2), "utf-8");
|
|
5533
|
+
}
|
|
5534
|
+
function deleteCustomTheme(id) {
|
|
5535
|
+
const filePath = join7(THEMES_DIR, `${id}.json`);
|
|
5536
|
+
if (existsSync6(filePath)) {
|
|
5537
|
+
unlinkSync2(filePath);
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
function getCustomTheme(id) {
|
|
5541
|
+
const filePath = join7(THEMES_DIR, `${id}.json`);
|
|
5542
|
+
if (!existsSync6(filePath)) return void 0;
|
|
5543
|
+
try {
|
|
5544
|
+
const data = JSON.parse(readFileSync8(filePath, "utf-8"));
|
|
5545
|
+
return { ...data, builtIn: false };
|
|
5546
|
+
} catch {
|
|
5547
|
+
return void 0;
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
function getAllThemes() {
|
|
5551
|
+
return [...BUILT_IN_THEMES, ...loadCustomThemes()];
|
|
5552
|
+
}
|
|
5553
|
+
function resolveTheme(id) {
|
|
5554
|
+
return getBuiltInTheme(id) ?? getCustomTheme(id);
|
|
5555
|
+
}
|
|
5556
|
+
function getActiveThemeColors() {
|
|
5557
|
+
const id = getActiveThemeId();
|
|
5558
|
+
const theme = resolveTheme(id);
|
|
5559
|
+
if (theme) return theme.colors;
|
|
5560
|
+
const fallback = getBuiltInTheme(DEFAULT_THEME_ID);
|
|
5561
|
+
if (fallback === void 0) throw new Error(`Default theme '${DEFAULT_THEME_ID}' not found`);
|
|
5562
|
+
return fallback.colors;
|
|
5563
|
+
}
|
|
5564
|
+
function generateThemeId() {
|
|
5565
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
5566
|
+
}
|
|
5567
|
+
|
|
4973
5568
|
// src/components/layout.tsx
|
|
4974
5569
|
function Layout({ title, reviewId, children }) {
|
|
4975
|
-
|
|
5570
|
+
const themeId = getActiveThemeId();
|
|
5571
|
+
const themeColors = getActiveThemeColors();
|
|
5572
|
+
const themeStyle = themeToInlineStyle(themeColors);
|
|
5573
|
+
return /* @__PURE__ */ jsx("html", { lang: "en", style: themeStyle, "data-theme": themeId, children: [
|
|
4976
5574
|
/* @__PURE__ */ jsx("head", { children: [
|
|
4977
5575
|
/* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
|
|
4978
5576
|
/* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
@@ -5053,7 +5651,7 @@ function ReviewHistory({ reviews, currentReviewId }) {
|
|
|
5053
5651
|
|
|
5054
5652
|
// src/routes/pages.tsx
|
|
5055
5653
|
init_queries();
|
|
5056
|
-
var pageRoutes = new
|
|
5654
|
+
var pageRoutes = new Hono5();
|
|
5057
5655
|
pageRoutes.get("/", async (c) => {
|
|
5058
5656
|
const reviewId = c.get("reviewId");
|
|
5059
5657
|
const review = await getReview(reviewId);
|
|
@@ -5207,11 +5805,11 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
5207
5805
|
});
|
|
5208
5806
|
pageRoutes.get("/file-raw", (c) => {
|
|
5209
5807
|
const filePath = c.req.query("path");
|
|
5210
|
-
if (
|
|
5808
|
+
if (filePath === void 0 || filePath === "") return c.text("Missing path", 400);
|
|
5211
5809
|
const repoRoot = c.get("repoRoot");
|
|
5212
5810
|
let content;
|
|
5213
5811
|
try {
|
|
5214
|
-
content =
|
|
5812
|
+
content = readFileSync9(resolve4(repoRoot, filePath), "utf-8");
|
|
5215
5813
|
} catch {
|
|
5216
5814
|
return c.text("File not found", 404);
|
|
5217
5815
|
}
|
|
@@ -5234,7 +5832,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
5234
5832
|
}))
|
|
5235
5833
|
}]
|
|
5236
5834
|
};
|
|
5237
|
-
const fakeFile = { id: "", review_id: "", file_path: filePath, status: "reviewed", diff_data: null };
|
|
5835
|
+
const fakeFile = { id: "", review_id: "", file_path: filePath, status: "reviewed", diff_data: null, created_at: "" };
|
|
5238
5836
|
const html = /* @__PURE__ */ jsx(DiffView, { file: fakeFile, diff, annotations: [], mode: "unified" });
|
|
5239
5837
|
return c.html(html.toString());
|
|
5240
5838
|
});
|
|
@@ -5337,6 +5935,108 @@ pageRoutes.get("/history", async (c) => {
|
|
|
5337
5935
|
return c.html(html.toString());
|
|
5338
5936
|
});
|
|
5339
5937
|
|
|
5938
|
+
// src/routes/theme-api.ts
|
|
5939
|
+
import { Hono as Hono6 } from "hono";
|
|
5940
|
+
var themeApiRoutes = new Hono6();
|
|
5941
|
+
themeApiRoutes.get("/", (c) => {
|
|
5942
|
+
const themes = getAllThemes();
|
|
5943
|
+
const activeId = getActiveThemeId();
|
|
5944
|
+
return c.json({
|
|
5945
|
+
themes: themes.map((t) => ({
|
|
5946
|
+
id: t.id,
|
|
5947
|
+
name: t.name,
|
|
5948
|
+
builtIn: t.builtIn,
|
|
5949
|
+
colors: t.colors
|
|
5950
|
+
})),
|
|
5951
|
+
activeId
|
|
5952
|
+
});
|
|
5953
|
+
});
|
|
5954
|
+
themeApiRoutes.get("/active", (c) => {
|
|
5955
|
+
const id = getActiveThemeId();
|
|
5956
|
+
const colors = getActiveThemeColors();
|
|
5957
|
+
return c.json({ id, colors });
|
|
5958
|
+
});
|
|
5959
|
+
themeApiRoutes.post("/active", async (c) => {
|
|
5960
|
+
const body = await c.req.json();
|
|
5961
|
+
if (!body.id) return c.json({ error: "Missing theme id" }, 400);
|
|
5962
|
+
const theme = resolveTheme(body.id);
|
|
5963
|
+
if (!theme) return c.json({ error: "Theme not found" }, 404);
|
|
5964
|
+
setActiveThemeId(body.id);
|
|
5965
|
+
return c.json({ id: body.id, colors: theme.colors });
|
|
5966
|
+
});
|
|
5967
|
+
themeApiRoutes.post("/", async (c) => {
|
|
5968
|
+
const body = await c.req.json();
|
|
5969
|
+
if (!body.sourceId) return c.json({ error: "Missing sourceId" }, 400);
|
|
5970
|
+
const source = resolveTheme(body.sourceId);
|
|
5971
|
+
if (!source) return c.json({ error: "Source theme not found" }, 404);
|
|
5972
|
+
const baseTheme = source.builtIn ? source.id : source.baseTheme;
|
|
5973
|
+
const name = body.name ?? `${source.name} (Copy)`;
|
|
5974
|
+
const newTheme = {
|
|
5975
|
+
id: generateThemeId(),
|
|
5976
|
+
name,
|
|
5977
|
+
builtIn: false,
|
|
5978
|
+
baseTheme,
|
|
5979
|
+
colors: { ...source.colors }
|
|
5980
|
+
};
|
|
5981
|
+
saveCustomTheme(newTheme);
|
|
5982
|
+
return c.json(newTheme, 201);
|
|
5983
|
+
});
|
|
5984
|
+
themeApiRoutes.post("/:id/edit", async (c) => {
|
|
5985
|
+
const id = c.req.param("id");
|
|
5986
|
+
const body = await c.req.json();
|
|
5987
|
+
const source = resolveTheme(id);
|
|
5988
|
+
if (!source) return c.json({ error: "Theme not found" }, 404);
|
|
5989
|
+
if (source.builtIn) {
|
|
5990
|
+
const newTheme = {
|
|
5991
|
+
id: generateThemeId(),
|
|
5992
|
+
name: `${source.name} (Customized)`,
|
|
5993
|
+
builtIn: false,
|
|
5994
|
+
baseTheme: source.id,
|
|
5995
|
+
colors: body.colors ? { ...source.colors, ...body.colors } : { ...source.colors }
|
|
5996
|
+
};
|
|
5997
|
+
if (body.name !== void 0 && body.name !== "") newTheme.name = body.name;
|
|
5998
|
+
saveCustomTheme(newTheme);
|
|
5999
|
+
setActiveThemeId(newTheme.id);
|
|
6000
|
+
return c.json({ theme: newTheme, copied: true }, 201);
|
|
6001
|
+
}
|
|
6002
|
+
const updated = {
|
|
6003
|
+
...source,
|
|
6004
|
+
name: body.name ?? source.name,
|
|
6005
|
+
colors: body.colors ? { ...source.colors, ...body.colors } : source.colors
|
|
6006
|
+
};
|
|
6007
|
+
saveCustomTheme(updated);
|
|
6008
|
+
return c.json({ theme: updated, copied: false });
|
|
6009
|
+
});
|
|
6010
|
+
themeApiRoutes.patch("/:id", async (c) => {
|
|
6011
|
+
const id = c.req.param("id");
|
|
6012
|
+
if (getBuiltInTheme(id)) {
|
|
6013
|
+
return c.json({ error: "Cannot edit built-in theme" }, 400);
|
|
6014
|
+
}
|
|
6015
|
+
const existing = resolveTheme(id);
|
|
6016
|
+
if (!existing || existing.builtIn) {
|
|
6017
|
+
return c.json({ error: "Theme not found" }, 404);
|
|
6018
|
+
}
|
|
6019
|
+
const body = await c.req.json();
|
|
6020
|
+
const updated = {
|
|
6021
|
+
...existing,
|
|
6022
|
+
name: body.name ?? existing.name,
|
|
6023
|
+
colors: body.colors ? { ...existing.colors, ...body.colors } : existing.colors
|
|
6024
|
+
};
|
|
6025
|
+
saveCustomTheme(updated);
|
|
6026
|
+
return c.json(updated);
|
|
6027
|
+
});
|
|
6028
|
+
themeApiRoutes.delete("/:id", (c) => {
|
|
6029
|
+
const id = c.req.param("id");
|
|
6030
|
+
if (getBuiltInTheme(id)) {
|
|
6031
|
+
return c.json({ error: "Cannot delete built-in theme" }, 400);
|
|
6032
|
+
}
|
|
6033
|
+
deleteCustomTheme(id);
|
|
6034
|
+
if (getActiveThemeId() === id) {
|
|
6035
|
+
setActiveThemeId(BUILT_IN_THEMES[0].id);
|
|
6036
|
+
}
|
|
6037
|
+
return c.json({ ok: true });
|
|
6038
|
+
});
|
|
6039
|
+
|
|
5340
6040
|
// src/server.ts
|
|
5341
6041
|
function tryServe(fetch2, port) {
|
|
5342
6042
|
return new Promise((resolve6, reject) => {
|
|
@@ -5354,7 +6054,7 @@ function tryServe(fetch2, port) {
|
|
|
5354
6054
|
});
|
|
5355
6055
|
}
|
|
5356
6056
|
async function startServer(port, reviewId, repoRoot, options) {
|
|
5357
|
-
const app = new
|
|
6057
|
+
const app = new Hono7();
|
|
5358
6058
|
app.use("*", async (c, next) => {
|
|
5359
6059
|
c.set("reviewId", reviewId);
|
|
5360
6060
|
c.set("currentReviewId", reviewId);
|
|
@@ -5362,24 +6062,25 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
5362
6062
|
await next();
|
|
5363
6063
|
});
|
|
5364
6064
|
const selfDir = dirname(fileURLToPath(import.meta.url));
|
|
5365
|
-
const distDir =
|
|
6065
|
+
const distDir = existsSync7(join8(selfDir, "client", "styles.css")) ? join8(selfDir, "client") : join8(selfDir, "..", "dist", "client");
|
|
5366
6066
|
app.get("/static/styles.css", (c) => {
|
|
5367
|
-
const css =
|
|
6067
|
+
const css = readFileSync10(join8(distDir, "styles.css"), "utf-8");
|
|
5368
6068
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
5369
6069
|
});
|
|
5370
6070
|
app.get("/static/app.js", (c) => {
|
|
5371
|
-
const js =
|
|
6071
|
+
const js = readFileSync10(join8(distDir, "app.global.js"), "utf-8");
|
|
5372
6072
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
5373
6073
|
});
|
|
5374
6074
|
app.get("/static/history.js", (c) => {
|
|
5375
|
-
const js =
|
|
6075
|
+
const js = readFileSync10(join8(distDir, "history.global.js"), "utf-8");
|
|
5376
6076
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
5377
6077
|
});
|
|
5378
6078
|
app.route("/api", apiRoutes);
|
|
5379
6079
|
app.route("/api/ai", aiApiRoutes);
|
|
6080
|
+
app.route("/api/themes", themeApiRoutes);
|
|
5380
6081
|
app.route("/", pageRoutes);
|
|
5381
6082
|
let actualPort = port;
|
|
5382
|
-
if (options?.strictPort) {
|
|
6083
|
+
if (options?.strictPort === true) {
|
|
5383
6084
|
actualPort = await tryServe(app.fetch, port);
|
|
5384
6085
|
} else {
|
|
5385
6086
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
@@ -5401,15 +6102,15 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
5401
6102
|
console.log(`
|
|
5402
6103
|
Glassbox running at ${url}
|
|
5403
6104
|
`);
|
|
5404
|
-
if (
|
|
6105
|
+
if (options?.noOpen !== true) {
|
|
5405
6106
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
5406
6107
|
exec(`${openCmd} ${url}`);
|
|
5407
6108
|
}
|
|
5408
6109
|
}
|
|
5409
6110
|
|
|
5410
6111
|
// src/skills.ts
|
|
5411
|
-
import { existsSync as
|
|
5412
|
-
import { join as
|
|
6112
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
6113
|
+
import { join as join9 } from "path";
|
|
5413
6114
|
var SKILL_VERSION = 1;
|
|
5414
6115
|
function versionHeader() {
|
|
5415
6116
|
return `<!-- glassbox-skill-version: ${SKILL_VERSION} -->`;
|
|
@@ -5420,14 +6121,14 @@ function parseVersionHeader(content) {
|
|
|
5420
6121
|
return parseInt(match[1], 10);
|
|
5421
6122
|
}
|
|
5422
6123
|
function updateFile(path, content) {
|
|
5423
|
-
if (
|
|
5424
|
-
const existing =
|
|
6124
|
+
if (existsSync8(path)) {
|
|
6125
|
+
const existing = readFileSync11(path, "utf-8");
|
|
5425
6126
|
const version = parseVersionHeader(existing);
|
|
5426
6127
|
if (version !== null && version >= SKILL_VERSION) {
|
|
5427
6128
|
return false;
|
|
5428
6129
|
}
|
|
5429
6130
|
}
|
|
5430
|
-
|
|
6131
|
+
writeFileSync6(path, content, "utf-8");
|
|
5431
6132
|
return true;
|
|
5432
6133
|
}
|
|
5433
6134
|
function skillBody() {
|
|
@@ -5447,8 +6148,8 @@ function skillBody() {
|
|
|
5447
6148
|
].join("\n");
|
|
5448
6149
|
}
|
|
5449
6150
|
function ensureClaudeSkills(cwd) {
|
|
5450
|
-
const dir =
|
|
5451
|
-
|
|
6151
|
+
const dir = join9(cwd, ".claude", "skills", "glassbox");
|
|
6152
|
+
mkdirSync6(dir, { recursive: true });
|
|
5452
6153
|
const content = [
|
|
5453
6154
|
"---",
|
|
5454
6155
|
"name: glassbox",
|
|
@@ -5460,11 +6161,11 @@ function ensureClaudeSkills(cwd) {
|
|
|
5460
6161
|
skillBody(),
|
|
5461
6162
|
""
|
|
5462
6163
|
].join("\n");
|
|
5463
|
-
return updateFile(
|
|
6164
|
+
return updateFile(join9(dir, "SKILL.md"), content);
|
|
5464
6165
|
}
|
|
5465
6166
|
function ensureCursorRules(cwd) {
|
|
5466
|
-
const rulesDir =
|
|
5467
|
-
|
|
6167
|
+
const rulesDir = join9(cwd, ".cursor", "rules");
|
|
6168
|
+
mkdirSync6(rulesDir, { recursive: true });
|
|
5468
6169
|
const content = [
|
|
5469
6170
|
"---",
|
|
5470
6171
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -5475,11 +6176,11 @@ function ensureCursorRules(cwd) {
|
|
|
5475
6176
|
skillBody(),
|
|
5476
6177
|
""
|
|
5477
6178
|
].join("\n");
|
|
5478
|
-
return updateFile(
|
|
6179
|
+
return updateFile(join9(rulesDir, "glassbox.mdc"), content);
|
|
5479
6180
|
}
|
|
5480
6181
|
function ensureCopilotPrompts(cwd) {
|
|
5481
|
-
const promptsDir =
|
|
5482
|
-
|
|
6182
|
+
const promptsDir = join9(cwd, ".github", "prompts");
|
|
6183
|
+
mkdirSync6(promptsDir, { recursive: true });
|
|
5483
6184
|
const content = [
|
|
5484
6185
|
"---",
|
|
5485
6186
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -5489,11 +6190,11 @@ function ensureCopilotPrompts(cwd) {
|
|
|
5489
6190
|
skillBody(),
|
|
5490
6191
|
""
|
|
5491
6192
|
].join("\n");
|
|
5492
|
-
return updateFile(
|
|
6193
|
+
return updateFile(join9(promptsDir, "glassbox.prompt.md"), content);
|
|
5493
6194
|
}
|
|
5494
6195
|
function ensureWindsurfRules(cwd) {
|
|
5495
|
-
const rulesDir =
|
|
5496
|
-
|
|
6196
|
+
const rulesDir = join9(cwd, ".windsurf", "rules");
|
|
6197
|
+
mkdirSync6(rulesDir, { recursive: true });
|
|
5497
6198
|
const content = [
|
|
5498
6199
|
"---",
|
|
5499
6200
|
"trigger: manual",
|
|
@@ -5504,39 +6205,39 @@ function ensureWindsurfRules(cwd) {
|
|
|
5504
6205
|
skillBody(),
|
|
5505
6206
|
""
|
|
5506
6207
|
].join("\n");
|
|
5507
|
-
return updateFile(
|
|
6208
|
+
return updateFile(join9(rulesDir, "glassbox.md"), content);
|
|
5508
6209
|
}
|
|
5509
6210
|
function ensureSkills() {
|
|
5510
6211
|
const cwd = process.cwd();
|
|
5511
6212
|
const platforms = [];
|
|
5512
|
-
if (
|
|
6213
|
+
if (existsSync8(join9(cwd, ".claude"))) {
|
|
5513
6214
|
if (ensureClaudeSkills(cwd)) platforms.push("Claude Code");
|
|
5514
6215
|
}
|
|
5515
|
-
if (
|
|
6216
|
+
if (existsSync8(join9(cwd, ".cursor"))) {
|
|
5516
6217
|
if (ensureCursorRules(cwd)) platforms.push("Cursor");
|
|
5517
6218
|
}
|
|
5518
|
-
if (
|
|
6219
|
+
if (existsSync8(join9(cwd, ".github", "prompts")) || existsSync8(join9(cwd, ".github", "copilot-instructions.md"))) {
|
|
5519
6220
|
if (ensureCopilotPrompts(cwd)) platforms.push("GitHub Copilot");
|
|
5520
6221
|
}
|
|
5521
|
-
if (
|
|
6222
|
+
if (existsSync8(join9(cwd, ".windsurf"))) {
|
|
5522
6223
|
if (ensureWindsurfRules(cwd)) platforms.push("Windsurf");
|
|
5523
6224
|
}
|
|
5524
6225
|
return platforms;
|
|
5525
6226
|
}
|
|
5526
6227
|
|
|
5527
6228
|
// src/update-check.ts
|
|
5528
|
-
import { existsSync as
|
|
6229
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
5529
6230
|
import { get } from "https";
|
|
5530
|
-
import { homedir as
|
|
5531
|
-
import { dirname as dirname2, join as
|
|
6231
|
+
import { homedir as homedir4 } from "os";
|
|
6232
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
5532
6233
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5533
|
-
var DATA_DIR =
|
|
5534
|
-
var CHECK_FILE =
|
|
6234
|
+
var DATA_DIR = join10(homedir4(), ".glassbox");
|
|
6235
|
+
var CHECK_FILE = join10(DATA_DIR, "last-update-check");
|
|
5535
6236
|
var PACKAGE_NAME = "glassbox";
|
|
5536
6237
|
function getCurrentVersion() {
|
|
5537
6238
|
try {
|
|
5538
6239
|
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
5539
|
-
const pkg = JSON.parse(
|
|
6240
|
+
const pkg = JSON.parse(readFileSync12(join10(dir, "..", "package.json"), "utf-8"));
|
|
5540
6241
|
return pkg.version;
|
|
5541
6242
|
} catch {
|
|
5542
6243
|
return "0.0.0";
|
|
@@ -5544,16 +6245,16 @@ function getCurrentVersion() {
|
|
|
5544
6245
|
}
|
|
5545
6246
|
function getLastCheckDate() {
|
|
5546
6247
|
try {
|
|
5547
|
-
if (
|
|
5548
|
-
return
|
|
6248
|
+
if (existsSync9(CHECK_FILE)) {
|
|
6249
|
+
return readFileSync12(CHECK_FILE, "utf-8").trim();
|
|
5549
6250
|
}
|
|
5550
6251
|
} catch {
|
|
5551
6252
|
}
|
|
5552
6253
|
return null;
|
|
5553
6254
|
}
|
|
5554
6255
|
function saveCheckDate() {
|
|
5555
|
-
|
|
5556
|
-
|
|
6256
|
+
mkdirSync7(DATA_DIR, { recursive: true });
|
|
6257
|
+
writeFileSync7(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
|
|
5557
6258
|
}
|
|
5558
6259
|
function isFirstUseToday() {
|
|
5559
6260
|
const last = getLastCheckDate();
|
|
@@ -5779,13 +6480,13 @@ async function main() {
|
|
|
5779
6480
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
5780
6481
|
}
|
|
5781
6482
|
if (debug) {
|
|
5782
|
-
console.log(`[debug] Build timestamp: ${"2026-
|
|
6483
|
+
console.log(`[debug] Build timestamp: ${"2026-04-02T08:06:45.351Z"}`);
|
|
5783
6484
|
}
|
|
5784
|
-
if (projectDir) {
|
|
6485
|
+
if (projectDir !== null) {
|
|
5785
6486
|
process.chdir(projectDir);
|
|
5786
6487
|
}
|
|
5787
6488
|
if (dataDir === null) {
|
|
5788
|
-
dataDir =
|
|
6489
|
+
dataDir = join11(process.cwd(), ".glassbox");
|
|
5789
6490
|
}
|
|
5790
6491
|
if (demo !== null) {
|
|
5791
6492
|
const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
|
|
@@ -5797,13 +6498,13 @@ async function main() {
|
|
|
5797
6498
|
}
|
|
5798
6499
|
process.exit(1);
|
|
5799
6500
|
}
|
|
5800
|
-
dataDir =
|
|
6501
|
+
dataDir = join11(tmpdir(), `glassbox-demo-${demo}-${Date.now()}`);
|
|
5801
6502
|
setDemoMode(demo);
|
|
5802
6503
|
console.log(`
|
|
5803
6504
|
DEMO MODE: ${scenario.label}
|
|
5804
6505
|
`);
|
|
5805
6506
|
}
|
|
5806
|
-
|
|
6507
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
5807
6508
|
if (demo === null) {
|
|
5808
6509
|
acquireLock(dataDir);
|
|
5809
6510
|
}
|
|
@@ -5860,8 +6561,14 @@ async function main() {
|
|
|
5860
6561
|
console.log(`Review ${review.id} created.`);
|
|
5861
6562
|
await startServer(port, review.id, repoRoot, { noOpen, strictPort });
|
|
5862
6563
|
}
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
6564
|
+
var isDirectRun = process.argv[1]?.endsWith("cli.js") || process.argv[1]?.endsWith("cli.ts");
|
|
6565
|
+
if (isDirectRun) {
|
|
6566
|
+
main().catch((err) => {
|
|
6567
|
+
console.error(err);
|
|
6568
|
+
process.exit(1);
|
|
6569
|
+
});
|
|
6570
|
+
}
|
|
6571
|
+
export {
|
|
6572
|
+
parseArgs
|
|
6573
|
+
};
|
|
5867
6574
|
//# sourceMappingURL=cli.js.map
|