glassbox 0.5.1 → 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 +1183 -504
- 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 { execSync } 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;
|
|
@@ -525,33 +470,102 @@ function getKeyFromKeychain(platform) {
|
|
|
525
470
|
const account = `${platform}-api-key`;
|
|
526
471
|
try {
|
|
527
472
|
if (os === "darwin") {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
).trim();
|
|
532
|
-
return result !== "" ? result : null;
|
|
473
|
+
const r = spawnSync("security", ["find-generic-password", "-s", "glassbox", "-a", account, "-w"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
474
|
+
const result = r.stdout.trim();
|
|
475
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
533
476
|
}
|
|
534
477
|
if (os === "linux") {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
).trim();
|
|
539
|
-
return result !== "" ? result : null;
|
|
478
|
+
const r = spawnSync("secret-tool", ["lookup", "service", "glassbox", "account", account], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
479
|
+
const result = r.stdout.trim();
|
|
480
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
540
481
|
}
|
|
541
482
|
if (os === "win32") {
|
|
542
483
|
const target = winCredTarget(platform);
|
|
543
484
|
const script = WIN_CRED_READ_PS + `Write-Output ([CredHelper]::Read('${target}'))`;
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}).trim();
|
|
548
|
-
return result !== "" ? result : null;
|
|
485
|
+
const r = spawnSync("powershell", ["-NoProfile", "-Command", "-"], { input: script, encoding: "utf-8" });
|
|
486
|
+
const result = r.stdout.trim();
|
|
487
|
+
return r.status === 0 && result !== "" ? result : null;
|
|
549
488
|
}
|
|
550
489
|
} catch {
|
|
551
490
|
return null;
|
|
552
491
|
}
|
|
553
492
|
return null;
|
|
554
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
|
+
}
|
|
555
569
|
function getKeyFromConfig(platform) {
|
|
556
570
|
const config = readConfigFile();
|
|
557
571
|
const encoded = config.ai?.keys?.[platform];
|
|
@@ -571,20 +585,6 @@ function resolveAPIKey(platform) {
|
|
|
571
585
|
if (configKey !== null) return { key: configKey, source: "config" };
|
|
572
586
|
return { key: null, source: null };
|
|
573
587
|
}
|
|
574
|
-
function loadAIConfig() {
|
|
575
|
-
const config = readConfigFile();
|
|
576
|
-
const platform = config.ai?.platform ?? "anthropic";
|
|
577
|
-
const model = config.ai?.model ?? getDefaultModel(platform);
|
|
578
|
-
const { key, source } = resolveAPIKey(platform);
|
|
579
|
-
return { platform, model, apiKey: key, keySource: source };
|
|
580
|
-
}
|
|
581
|
-
function saveAIConfigPreferences(platform, model) {
|
|
582
|
-
const config = readConfigFile();
|
|
583
|
-
if (config.ai === void 0) config.ai = {};
|
|
584
|
-
config.ai.platform = platform;
|
|
585
|
-
config.ai.model = model;
|
|
586
|
-
writeConfigFile(config);
|
|
587
|
-
}
|
|
588
588
|
function saveAPIKey(platform, key, storage) {
|
|
589
589
|
if (storage === "keychain") {
|
|
590
590
|
saveKeyToKeychain(platform, key);
|
|
@@ -596,50 +596,17 @@ function saveAPIKey(platform, key, storage) {
|
|
|
596
596
|
writeConfigFile(config);
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
|
-
function saveKeyToKeychain(platform, key) {
|
|
600
|
-
const os = process.platform;
|
|
601
|
-
const account = `${platform}-api-key`;
|
|
602
|
-
if (os === "darwin") {
|
|
603
|
-
try {
|
|
604
|
-
execSync(`security delete-generic-password -s glassbox -a "${account}" 2>/dev/null`);
|
|
605
|
-
} catch {
|
|
606
|
-
}
|
|
607
|
-
execSync(
|
|
608
|
-
`security add-generic-password -s glassbox -a "${account}" -w "${key.replace(/"/g, '\\"')}"`
|
|
609
|
-
);
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (os === "linux") {
|
|
613
|
-
execSync(
|
|
614
|
-
`secret-tool store --label='Glassbox API Key' service glassbox account "${account}"`,
|
|
615
|
-
{ input: key, encoding: "utf-8" }
|
|
616
|
-
);
|
|
617
|
-
return;
|
|
618
|
-
}
|
|
619
|
-
if (os === "win32") {
|
|
620
|
-
const target = winCredTarget(platform);
|
|
621
|
-
const escapedKey = key.replace(/'/g, "''");
|
|
622
|
-
const script = `cmdkey /generic:'${target}' /user:'glassbox' /pass:'${escapedKey}'`;
|
|
623
|
-
execSync("powershell -NoProfile -Command -", {
|
|
624
|
-
input: script,
|
|
625
|
-
encoding: "utf-8"
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
599
|
function deleteAPIKey(platform) {
|
|
630
600
|
const os = process.platform;
|
|
631
601
|
const account = `${platform}-api-key`;
|
|
632
602
|
try {
|
|
633
603
|
if (os === "darwin") {
|
|
634
|
-
|
|
604
|
+
spawnSync2("security", ["delete-generic-password", "-s", "glassbox", "-a", account], { stdio: "pipe" });
|
|
635
605
|
} else if (os === "linux") {
|
|
636
|
-
|
|
606
|
+
spawnSync2("secret-tool", ["clear", "service", "glassbox", "account", account], { stdio: "pipe" });
|
|
637
607
|
} else if (os === "win32") {
|
|
638
608
|
const target = winCredTarget(platform);
|
|
639
|
-
|
|
640
|
-
input: `cmdkey /delete:'${target}'`,
|
|
641
|
-
encoding: "utf-8"
|
|
642
|
-
});
|
|
609
|
+
spawnSync2("powershell", ["-NoProfile", "-Command", "-"], { input: `cmdkey /delete:'${target}'`, encoding: "utf-8" });
|
|
643
610
|
}
|
|
644
611
|
} catch {
|
|
645
612
|
}
|
|
@@ -659,25 +626,40 @@ function detectAvailablePlatforms() {
|
|
|
659
626
|
}
|
|
660
627
|
return results;
|
|
661
628
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return false;
|
|
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"));
|
|
671
637
|
}
|
|
638
|
+
} catch {
|
|
672
639
|
}
|
|
673
|
-
return
|
|
640
|
+
return {};
|
|
674
641
|
}
|
|
675
|
-
function
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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);
|
|
681
663
|
}
|
|
682
664
|
function loadGuidedReviewConfig() {
|
|
683
665
|
const config = readConfigFile();
|
|
@@ -1291,20 +1273,24 @@ async function setupAnnotations(fileIdMap) {
|
|
|
1291
1273
|
}
|
|
1292
1274
|
|
|
1293
1275
|
// src/git/diff.ts
|
|
1294
|
-
import {
|
|
1276
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
1295
1277
|
import { readFileSync as readFileSync2 } from "fs";
|
|
1296
1278
|
import { resolve } from "path";
|
|
1279
|
+
|
|
1280
|
+
// src/git/repo.ts
|
|
1281
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
1297
1282
|
function git(args, cwd) {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1283
|
+
const result = spawnSync3("git", args, { cwd, encoding: "utf-8", maxBuffer: 50 * 1024 * 1024 });
|
|
1284
|
+
if (result.status === 0) return result.stdout;
|
|
1285
|
+
if (result.stdout !== "") return result.stdout;
|
|
1286
|
+
const err = new Error(result.stderr);
|
|
1287
|
+
err.stdout = result.stdout;
|
|
1288
|
+
err.stderr = result.stderr;
|
|
1289
|
+
err.status = result.status;
|
|
1290
|
+
throw err;
|
|
1305
1291
|
}
|
|
1306
1292
|
function getRepoRoot(cwd) {
|
|
1307
|
-
return git("rev-parse --show-toplevel", cwd).trim();
|
|
1293
|
+
return git(["rev-parse", "--show-toplevel"], cwd).trim();
|
|
1308
1294
|
}
|
|
1309
1295
|
function getRepoName(cwd) {
|
|
1310
1296
|
const root = getRepoRoot(cwd);
|
|
@@ -1312,31 +1298,45 @@ function getRepoName(cwd) {
|
|
|
1312
1298
|
}
|
|
1313
1299
|
function isGitRepo(cwd) {
|
|
1314
1300
|
try {
|
|
1315
|
-
git("rev-parse --is-inside-work-tree", cwd);
|
|
1301
|
+
git(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
1316
1302
|
return true;
|
|
1317
1303
|
} catch {
|
|
1318
1304
|
return false;
|
|
1319
1305
|
}
|
|
1320
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
|
+
}
|
|
1321
1322
|
function getDiffArgs(mode) {
|
|
1322
1323
|
switch (mode.type) {
|
|
1323
1324
|
case "uncommitted":
|
|
1324
|
-
return "diff HEAD";
|
|
1325
|
+
return ["diff", "HEAD"];
|
|
1325
1326
|
case "staged":
|
|
1326
|
-
return "diff --cached";
|
|
1327
|
+
return ["diff", "--cached"];
|
|
1327
1328
|
case "unstaged":
|
|
1328
|
-
return "diff";
|
|
1329
|
+
return ["diff"];
|
|
1329
1330
|
case "commit":
|
|
1330
|
-
return
|
|
1331
|
+
return ["diff", `${mode.sha}~1`, mode.sha];
|
|
1331
1332
|
case "range":
|
|
1332
|
-
return
|
|
1333
|
-
case "branch":
|
|
1334
|
-
return
|
|
1335
|
-
}
|
|
1333
|
+
return ["diff", mode.from, mode.to];
|
|
1334
|
+
case "branch":
|
|
1335
|
+
return ["diff", `${mode.name}...HEAD`];
|
|
1336
1336
|
case "files":
|
|
1337
|
-
return
|
|
1337
|
+
return ["diff", "HEAD", "--", ...mode.patterns];
|
|
1338
1338
|
case "all":
|
|
1339
|
-
return "diff --no-index /dev/null .";
|
|
1339
|
+
return ["diff", "--no-index", "/dev/null", "."];
|
|
1340
1340
|
}
|
|
1341
1341
|
}
|
|
1342
1342
|
function getFileDiffs(mode, cwd) {
|
|
@@ -1347,13 +1347,13 @@ function getFileDiffs(mode, cwd) {
|
|
|
1347
1347
|
const diffArgs = getDiffArgs(mode);
|
|
1348
1348
|
let rawDiff;
|
|
1349
1349
|
try {
|
|
1350
|
-
rawDiff =
|
|
1350
|
+
rawDiff = git2([...diffArgs, "-U3"], repoRoot);
|
|
1351
1351
|
} catch {
|
|
1352
1352
|
rawDiff = "";
|
|
1353
1353
|
}
|
|
1354
1354
|
const diffs = parseDiff(rawDiff);
|
|
1355
1355
|
if (mode.type === "uncommitted") {
|
|
1356
|
-
const untracked =
|
|
1356
|
+
const untracked = git2(["ls-files", "--others", "--exclude-standard"], repoRoot).trim();
|
|
1357
1357
|
if (untracked) {
|
|
1358
1358
|
for (const file of untracked.split("\n").filter(Boolean)) {
|
|
1359
1359
|
if (!diffs.some((d) => d.filePath === file)) {
|
|
@@ -1365,7 +1365,7 @@ function getFileDiffs(mode, cwd) {
|
|
|
1365
1365
|
return diffs;
|
|
1366
1366
|
}
|
|
1367
1367
|
function getAllFiles(repoRoot) {
|
|
1368
|
-
const files =
|
|
1368
|
+
const files = git2(["ls-files"], repoRoot).trim().split("\n").filter(Boolean);
|
|
1369
1369
|
return files.map((file) => createNewFileDiff(file, repoRoot));
|
|
1370
1370
|
}
|
|
1371
1371
|
function createNewFileDiff(filePath, repoRoot) {
|
|
@@ -1492,16 +1492,13 @@ function getFileContent(filePath, ref, cwd) {
|
|
|
1492
1492
|
const repoRoot = getRepoRoot(cwd);
|
|
1493
1493
|
try {
|
|
1494
1494
|
if (ref === "working") {
|
|
1495
|
-
return
|
|
1495
|
+
return readFileSync2(resolve(repoRoot, filePath), "utf-8");
|
|
1496
1496
|
}
|
|
1497
|
-
return
|
|
1497
|
+
return git2(["show", `${ref}:${filePath}`], repoRoot);
|
|
1498
1498
|
} catch {
|
|
1499
1499
|
return "";
|
|
1500
1500
|
}
|
|
1501
1501
|
}
|
|
1502
|
-
function getHeadCommit(cwd) {
|
|
1503
|
-
return execSync2("git rev-parse HEAD", { cwd, encoding: "utf-8" }).trim();
|
|
1504
|
-
}
|
|
1505
1502
|
function parseModeString(modeStr) {
|
|
1506
1503
|
if (modeStr === "uncommitted") return { type: "uncommitted" };
|
|
1507
1504
|
if (modeStr === "staged") return { type: "staged" };
|
|
@@ -1521,9 +1518,12 @@ function getSingleFileDiff(mode, filePath, repoRoot, extraFlags = "") {
|
|
|
1521
1518
|
return createNewFileDiff(filePath, repoRoot);
|
|
1522
1519
|
}
|
|
1523
1520
|
const diffArgs = getDiffArgs(mode);
|
|
1521
|
+
const args = [...diffArgs, "-U3"];
|
|
1522
|
+
if (extraFlags) args.push(...extraFlags.split(" ").filter(Boolean));
|
|
1523
|
+
args.push("--", filePath);
|
|
1524
1524
|
let rawDiff;
|
|
1525
1525
|
try {
|
|
1526
|
-
rawDiff =
|
|
1526
|
+
rawDiff = git2(args, repoRoot);
|
|
1527
1527
|
} catch {
|
|
1528
1528
|
rawDiff = "";
|
|
1529
1529
|
}
|
|
@@ -1595,7 +1595,9 @@ function acquireLock(dataDir) {
|
|
|
1595
1595
|
}
|
|
1596
1596
|
}
|
|
1597
1597
|
writeFileSync2(lockPath, JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
1598
|
-
const cleanup = () =>
|
|
1598
|
+
const cleanup = () => {
|
|
1599
|
+
releaseLock();
|
|
1600
|
+
};
|
|
1599
1601
|
process.on("exit", cleanup);
|
|
1600
1602
|
process.on("SIGINT", () => {
|
|
1601
1603
|
cleanup();
|
|
@@ -1607,7 +1609,7 @@ function acquireLock(dataDir) {
|
|
|
1607
1609
|
});
|
|
1608
1610
|
}
|
|
1609
1611
|
function releaseLock() {
|
|
1610
|
-
if (lockPath) {
|
|
1612
|
+
if (lockPath !== null) {
|
|
1611
1613
|
try {
|
|
1612
1614
|
rmSync2(lockPath, { force: true });
|
|
1613
1615
|
} catch {
|
|
@@ -1723,12 +1725,15 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1723
1725
|
// src/server.ts
|
|
1724
1726
|
import { serve } from "@hono/node-server";
|
|
1725
1727
|
import { exec } from "child_process";
|
|
1726
|
-
import { existsSync as
|
|
1727
|
-
import { Hono as
|
|
1728
|
-
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";
|
|
1729
1731
|
import { fileURLToPath } from "url";
|
|
1730
1732
|
|
|
1731
1733
|
// src/routes/ai-api.ts
|
|
1734
|
+
import { Hono as Hono3 } from "hono";
|
|
1735
|
+
|
|
1736
|
+
// src/routes/ai-analysis.ts
|
|
1732
1737
|
import { Hono } from "hono";
|
|
1733
1738
|
|
|
1734
1739
|
// src/ai/client.ts
|
|
@@ -2558,63 +2563,11 @@ async function mockGuidedAnalysisBatch(files) {
|
|
|
2558
2563
|
}));
|
|
2559
2564
|
}
|
|
2560
2565
|
|
|
2561
|
-
// src/routes/ai-
|
|
2566
|
+
// src/routes/ai-analysis.ts
|
|
2562
2567
|
init_queries();
|
|
2563
|
-
var
|
|
2568
|
+
var aiAnalysisRoutes = new Hono();
|
|
2564
2569
|
var cancelledAnalyses = /* @__PURE__ */ new Set();
|
|
2565
|
-
|
|
2566
|
-
const config = loadAIConfig();
|
|
2567
|
-
return c.json({
|
|
2568
|
-
platform: config.platform,
|
|
2569
|
-
model: config.model,
|
|
2570
|
-
keyConfigured: config.apiKey !== null || isAIServiceTest() || getDemoMode() !== null,
|
|
2571
|
-
keySource: config.keySource,
|
|
2572
|
-
guidedReview: loadGuidedReviewConfig()
|
|
2573
|
-
});
|
|
2574
|
-
});
|
|
2575
|
-
aiApiRoutes.post("/config", async (c) => {
|
|
2576
|
-
const body = await c.req.json();
|
|
2577
|
-
saveAIConfigPreferences(body.platform, body.model);
|
|
2578
|
-
if (body.guidedReview !== void 0) {
|
|
2579
|
-
saveGuidedReviewConfig(body.guidedReview);
|
|
2580
|
-
}
|
|
2581
|
-
return c.json({ ok: true });
|
|
2582
|
-
});
|
|
2583
|
-
aiApiRoutes.get("/models", (c) => {
|
|
2584
|
-
return c.json({
|
|
2585
|
-
platforms: PLATFORMS,
|
|
2586
|
-
models: MODELS
|
|
2587
|
-
});
|
|
2588
|
-
});
|
|
2589
|
-
aiApiRoutes.get("/key-status", (c) => {
|
|
2590
|
-
const platforms = ["anthropic", "openai", "google"];
|
|
2591
|
-
const status = {};
|
|
2592
|
-
for (const platform of platforms) {
|
|
2593
|
-
const { source } = resolveAPIKey(platform);
|
|
2594
|
-
status[platform] = { configured: source !== null, source };
|
|
2595
|
-
}
|
|
2596
|
-
return c.json({
|
|
2597
|
-
status,
|
|
2598
|
-
keychainAvailable: isKeychainAvailable(),
|
|
2599
|
-
keychainLabel: getKeychainLabel(),
|
|
2600
|
-
availablePlatforms: detectAvailablePlatforms()
|
|
2601
|
-
});
|
|
2602
|
-
});
|
|
2603
|
-
aiApiRoutes.post("/key", async (c) => {
|
|
2604
|
-
const body = await c.req.json();
|
|
2605
|
-
saveAPIKey(
|
|
2606
|
-
body.platform,
|
|
2607
|
-
body.key,
|
|
2608
|
-
body.storage
|
|
2609
|
-
);
|
|
2610
|
-
return c.json({ ok: true });
|
|
2611
|
-
});
|
|
2612
|
-
aiApiRoutes.delete("/key", (c) => {
|
|
2613
|
-
const platform = c.req.query("platform") ?? "anthropic";
|
|
2614
|
-
deleteAPIKey(platform);
|
|
2615
|
-
return c.json({ ok: true });
|
|
2616
|
-
});
|
|
2617
|
-
aiApiRoutes.post("/analyze", async (c) => {
|
|
2570
|
+
aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
2618
2571
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2619
2572
|
const repoRoot = c.get("repoRoot");
|
|
2620
2573
|
const body = await c.req.json();
|
|
@@ -2866,7 +2819,7 @@ async function runBatchedGuidedAnalysis(analysisId, batches, allFiles, config, r
|
|
|
2866
2819
|
"guided"
|
|
2867
2820
|
);
|
|
2868
2821
|
}
|
|
2869
|
-
|
|
2822
|
+
aiAnalysisRoutes.get("/analysis/:type", async (c) => {
|
|
2870
2823
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2871
2824
|
const analysisType = c.req.param("type");
|
|
2872
2825
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
@@ -2898,7 +2851,7 @@ aiApiRoutes.get("/analysis/:type", async (c) => {
|
|
|
2898
2851
|
}))
|
|
2899
2852
|
});
|
|
2900
2853
|
});
|
|
2901
|
-
|
|
2854
|
+
aiAnalysisRoutes.get("/analysis/:type/status", async (c) => {
|
|
2902
2855
|
const reviewId = c.req.query("reviewId") ?? "";
|
|
2903
2856
|
const analysisType = c.req.param("type");
|
|
2904
2857
|
const analysis = await getLatestAnalysis(reviewId, analysisType);
|
|
@@ -2922,35 +2875,96 @@ aiApiRoutes.get("/analysis/:type/status", async (c) => {
|
|
|
2922
2875
|
progressTotal: analysis.progress_total
|
|
2923
2876
|
});
|
|
2924
2877
|
});
|
|
2925
|
-
|
|
2878
|
+
aiAnalysisRoutes.get("/debug-status", (c) => {
|
|
2926
2879
|
return c.json({ enabled: isDebug() });
|
|
2927
2880
|
});
|
|
2928
|
-
|
|
2881
|
+
aiAnalysisRoutes.post("/debug-log", async (c) => {
|
|
2929
2882
|
if (!isDebug()) return c.json({ ok: true });
|
|
2930
2883
|
const body = await c.req.json();
|
|
2931
2884
|
debugLog(`[client] ${body.message}`);
|
|
2932
2885
|
return c.json({ ok: true });
|
|
2933
2886
|
});
|
|
2934
|
-
|
|
2887
|
+
aiAnalysisRoutes.get("/preferences", async (c) => {
|
|
2935
2888
|
const prefs = await getUserPreferences();
|
|
2936
2889
|
return c.json(prefs);
|
|
2937
2890
|
});
|
|
2938
|
-
|
|
2891
|
+
aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
2939
2892
|
const body = await c.req.json();
|
|
2940
2893
|
await saveUserPreferences(body);
|
|
2941
2894
|
return c.json({ ok: true });
|
|
2942
2895
|
});
|
|
2943
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
|
+
|
|
2944
2958
|
// src/routes/api.ts
|
|
2945
2959
|
init_queries();
|
|
2946
|
-
import {
|
|
2960
|
+
import { execFileSync, spawnSync as spawnSync7 } from "child_process";
|
|
2947
2961
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
2948
|
-
import { Hono as
|
|
2962
|
+
import { Hono as Hono4 } from "hono";
|
|
2949
2963
|
import { join as join6, resolve as resolve3 } from "path";
|
|
2950
2964
|
|
|
2951
2965
|
// src/export/generate.ts
|
|
2952
2966
|
init_queries();
|
|
2953
|
-
import {
|
|
2967
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
2954
2968
|
import { appendFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2955
2969
|
import { homedir as homedir2 } from "os";
|
|
2956
2970
|
import { join as join4 } from "path";
|
|
@@ -2969,12 +2983,8 @@ function saveDismissals(data) {
|
|
|
2969
2983
|
writeFileSync3(DISMISS_FILE, JSON.stringify(data), "utf-8");
|
|
2970
2984
|
}
|
|
2971
2985
|
function isGlassboxGitignored(repoRoot) {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
return true;
|
|
2975
|
-
} catch {
|
|
2976
|
-
return false;
|
|
2977
|
-
}
|
|
2986
|
+
const result = spawnSync5("git", ["check-ignore", "-q", ".glassbox"], { cwd: repoRoot, stdio: "pipe" });
|
|
2987
|
+
return result.status === 0;
|
|
2978
2988
|
}
|
|
2979
2989
|
function shouldPromptGitignore(repoRoot) {
|
|
2980
2990
|
if (isGlassboxGitignored(repoRoot)) return false;
|
|
@@ -3084,142 +3094,56 @@ async function generateReviewExport(reviewId, repoRoot, isCurrent) {
|
|
|
3084
3094
|
writeFileSync3(archivePath, content, "utf-8");
|
|
3085
3095
|
if (isCurrent) {
|
|
3086
3096
|
const latestPath = join4(exportDir, "latest-review.md");
|
|
3087
|
-
writeFileSync3(latestPath, content, "utf-8");
|
|
3088
|
-
return latestPath;
|
|
3089
|
-
}
|
|
3090
|
-
return archivePath;
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
// src/export/auto-export.ts
|
|
3094
|
-
var debounceTimer = null;
|
|
3095
|
-
var DEBOUNCE_MS = 2e3;
|
|
3096
|
-
function scheduleAutoExport(reviewId, repoRoot) {
|
|
3097
|
-
if (debounceTimer !== null) clearTimeout(debounceTimer);
|
|
3098
|
-
debounceTimer = setTimeout(() => {
|
|
3099
|
-
debounceTimer = null;
|
|
3100
|
-
void generateReviewExport(reviewId, repoRoot, true);
|
|
3101
|
-
}, DEBOUNCE_MS);
|
|
3102
|
-
}
|
|
3103
|
-
|
|
3104
|
-
// src/git/image.ts
|
|
3105
|
-
import { execSync as execSync4 } from "child_process";
|
|
3106
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
3107
|
-
import { resolve as resolve2 } from "path";
|
|
3108
|
-
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
3109
|
-
function isImageFile(filePath) {
|
|
3110
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3111
|
-
return IMAGE_EXTENSIONS.has(ext);
|
|
3112
|
-
}
|
|
3113
|
-
function isSvgFile(filePath) {
|
|
3114
|
-
return filePath.slice(filePath.lastIndexOf(".")).toLowerCase() === ".svg";
|
|
3115
|
-
}
|
|
3116
|
-
function getContentType(filePath) {
|
|
3117
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3118
|
-
switch (ext) {
|
|
3119
|
-
case ".png":
|
|
3120
|
-
return "image/png";
|
|
3121
|
-
case ".jpg":
|
|
3122
|
-
case ".jpeg":
|
|
3123
|
-
return "image/jpeg";
|
|
3124
|
-
case ".gif":
|
|
3125
|
-
return "image/gif";
|
|
3126
|
-
case ".webp":
|
|
3127
|
-
return "image/webp";
|
|
3128
|
-
case ".svg":
|
|
3129
|
-
return "image/svg+xml";
|
|
3130
|
-
default:
|
|
3131
|
-
return "application/octet-stream";
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
function getOldRef(mode) {
|
|
3135
|
-
switch (mode.type) {
|
|
3136
|
-
case "uncommitted":
|
|
3137
|
-
return "HEAD";
|
|
3138
|
-
case "staged":
|
|
3139
|
-
return "HEAD";
|
|
3140
|
-
case "unstaged":
|
|
3141
|
-
return null;
|
|
3142
|
-
// old = index, use ':'
|
|
3143
|
-
case "commit":
|
|
3144
|
-
return `${mode.sha}~1`;
|
|
3145
|
-
case "range":
|
|
3146
|
-
return mode.from;
|
|
3147
|
-
case "branch":
|
|
3148
|
-
return mode.name;
|
|
3149
|
-
case "files":
|
|
3150
|
-
return "HEAD";
|
|
3151
|
-
case "all":
|
|
3152
|
-
return null;
|
|
3153
|
-
}
|
|
3154
|
-
}
|
|
3155
|
-
function getNewRef(mode) {
|
|
3156
|
-
switch (mode.type) {
|
|
3157
|
-
case "uncommitted":
|
|
3158
|
-
return null;
|
|
3159
|
-
// working tree
|
|
3160
|
-
case "staged":
|
|
3161
|
-
return null;
|
|
3162
|
-
// index, but git show : works
|
|
3163
|
-
case "unstaged":
|
|
3164
|
-
return null;
|
|
3165
|
-
// working tree
|
|
3166
|
-
case "commit":
|
|
3167
|
-
return mode.sha;
|
|
3168
|
-
case "range":
|
|
3169
|
-
return mode.to;
|
|
3170
|
-
case "branch":
|
|
3171
|
-
return "HEAD";
|
|
3172
|
-
case "files":
|
|
3173
|
-
return null;
|
|
3174
|
-
case "all":
|
|
3175
|
-
return null;
|
|
3097
|
+
writeFileSync3(latestPath, content, "utf-8");
|
|
3098
|
+
return latestPath;
|
|
3176
3099
|
}
|
|
3100
|
+
return archivePath;
|
|
3177
3101
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
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);
|
|
3185
3112
|
}
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
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);
|
|
3192
3124
|
}
|
|
3193
|
-
function
|
|
3194
|
-
|
|
3195
|
-
const path = oldPath ?? filePath;
|
|
3196
|
-
if (ref === null) {
|
|
3197
|
-
const data2 = readWorkingFile(path, repoRoot);
|
|
3198
|
-
if (!data2) return null;
|
|
3199
|
-
return { data: data2, size: data2.length };
|
|
3200
|
-
}
|
|
3201
|
-
const actualRef = mode.type === "unstaged" ? ":" : ref;
|
|
3202
|
-
const data = gitShowFile(actualRef, path, repoRoot);
|
|
3203
|
-
if (!data) return null;
|
|
3204
|
-
return { data, size: data.length };
|
|
3125
|
+
function isSvgFile(filePath) {
|
|
3126
|
+
return filePath.slice(filePath.lastIndexOf(".")).toLowerCase() === ".svg";
|
|
3205
3127
|
}
|
|
3206
|
-
function
|
|
3207
|
-
const
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
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";
|
|
3217
3144
|
}
|
|
3218
|
-
const data = gitShowFile(ref, filePath, repoRoot);
|
|
3219
|
-
if (!data) return null;
|
|
3220
|
-
return { data, size: data.length };
|
|
3221
3145
|
}
|
|
3222
|
-
|
|
3146
|
+
function extractMetadata(data, filePath) {
|
|
3223
3147
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
3224
3148
|
if (ext === ".svg") {
|
|
3225
3149
|
const text = data.toString("utf-8");
|
|
@@ -3261,9 +3185,9 @@ function formatMetadataLines(meta) {
|
|
|
3261
3185
|
lines.push(`Dimensions: ${meta.width} \xD7 ${meta.height}`);
|
|
3262
3186
|
}
|
|
3263
3187
|
lines.push(`File size: ${formatBytes(meta.fileSize)}`);
|
|
3264
|
-
if (meta.colorSpace) lines.push(`Color space: ${meta.colorSpace}`);
|
|
3188
|
+
if (meta.colorSpace !== null) lines.push(`Color space: ${meta.colorSpace}`);
|
|
3265
3189
|
if (meta.channels !== null) lines.push(`Channels: ${meta.channels}`);
|
|
3266
|
-
if (meta.depth) lines.push(`Bit depth: ${meta.depth}`);
|
|
3190
|
+
if (meta.depth !== null) lines.push(`Bit depth: ${meta.depth}`);
|
|
3267
3191
|
if (meta.hasAlpha !== null) lines.push(`Alpha: ${meta.hasAlpha ? "yes" : "no"}`);
|
|
3268
3192
|
if (meta.density !== null) lines.push(`Density: ${meta.density} DPI`);
|
|
3269
3193
|
if (meta.exif) {
|
|
@@ -3404,7 +3328,95 @@ function parseWebp(data) {
|
|
|
3404
3328
|
width = (data[24] | data[25] << 8 | data[26] << 16) + 1;
|
|
3405
3329
|
height = (data[27] | data[28] << 8 | data[29] << 16) + 1;
|
|
3406
3330
|
}
|
|
3407
|
-
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 };
|
|
3408
3420
|
}
|
|
3409
3421
|
|
|
3410
3422
|
// src/git/svg-rasterize.ts
|
|
@@ -3439,63 +3451,62 @@ function loadSystemFonts() {
|
|
|
3439
3451
|
return buffers;
|
|
3440
3452
|
}
|
|
3441
3453
|
function getFontCandidates() {
|
|
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
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
default:
|
|
3497
|
-
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
|
+
];
|
|
3498
3508
|
}
|
|
3509
|
+
return [];
|
|
3499
3510
|
}
|
|
3500
3511
|
function parseSvgDimensions(svg) {
|
|
3501
3512
|
const widthMatch = svg.match(/\bwidth\s*=\s*["']([^"']+)["']/);
|
|
@@ -3859,7 +3870,7 @@ function pushIndentSymbol(root, stack, sym, indent, lines, lineIdx) {
|
|
|
3859
3870
|
}
|
|
3860
3871
|
|
|
3861
3872
|
// src/routes/api.ts
|
|
3862
|
-
var apiRoutes = new
|
|
3873
|
+
var apiRoutes = new Hono4();
|
|
3863
3874
|
function resolveReviewId(c) {
|
|
3864
3875
|
return c.req.query("reviewId") ?? c.get("reviewId");
|
|
3865
3876
|
}
|
|
@@ -3976,11 +3987,11 @@ apiRoutes.post("/files/:fileId/reveal", async (c) => {
|
|
|
3976
3987
|
const fullPath = resolve3(repoRoot, file.file_path);
|
|
3977
3988
|
try {
|
|
3978
3989
|
if (process.platform === "darwin") {
|
|
3979
|
-
|
|
3990
|
+
execFileSync("open", ["-R", fullPath]);
|
|
3980
3991
|
} else if (process.platform === "win32") {
|
|
3981
|
-
|
|
3992
|
+
execFileSync("explorer", ["/select," + fullPath]);
|
|
3982
3993
|
} else {
|
|
3983
|
-
|
|
3994
|
+
execFileSync("xdg-open", [resolve3(fullPath, "..")]);
|
|
3984
3995
|
}
|
|
3985
3996
|
} catch {
|
|
3986
3997
|
}
|
|
@@ -4062,7 +4073,7 @@ apiRoutes.get("/outline/:fileId", async (c) => {
|
|
|
4062
4073
|
apiRoutes.get("/symbol-definition", async (c) => {
|
|
4063
4074
|
const name = c.req.query("name");
|
|
4064
4075
|
const currentFileId = c.req.query("currentFileId");
|
|
4065
|
-
if (
|
|
4076
|
+
if (name === void 0 || name === "") return c.json({ definitions: [] });
|
|
4066
4077
|
const reviewId = resolveReviewId(c);
|
|
4067
4078
|
const repoRoot = c.get("repoRoot");
|
|
4068
4079
|
const definitions = [];
|
|
@@ -4084,7 +4095,7 @@ apiRoutes.get("/symbol-definition", async (c) => {
|
|
|
4084
4095
|
}
|
|
4085
4096
|
if (definitions.length === 0) {
|
|
4086
4097
|
try {
|
|
4087
|
-
const allFiles =
|
|
4098
|
+
const allFiles = spawnSync7("git", ["ls-files"], { cwd: repoRoot, encoding: "utf-8" }).stdout.trim().split("\n").filter(Boolean);
|
|
4088
4099
|
for (const filePath of allFiles) {
|
|
4089
4100
|
if (searchedPaths.has(filePath)) continue;
|
|
4090
4101
|
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
@@ -4119,7 +4130,7 @@ function collectDefinitions(symbols, targetName, fileId, filePath, out) {
|
|
|
4119
4130
|
if (sym.name === targetName) {
|
|
4120
4131
|
out.push({ fileId, filePath, name: sym.name, kind: sym.kind, line: sym.line });
|
|
4121
4132
|
}
|
|
4122
|
-
if (sym.children
|
|
4133
|
+
if (sym.children.length > 0) {
|
|
4123
4134
|
collectDefinitions(sym.children, targetName, fileId, filePath, out);
|
|
4124
4135
|
}
|
|
4125
4136
|
}
|
|
@@ -4180,10 +4191,8 @@ apiRoutes.get("/image/:fileId/metadata", async (c) => {
|
|
|
4180
4191
|
const status = diff.status ?? "modified";
|
|
4181
4192
|
const oldImage = status !== "added" ? getOldImage(mode, file.file_path, oldPath, repoRoot) : null;
|
|
4182
4193
|
const newImage = status !== "deleted" ? getNewImage(mode, file.file_path, repoRoot) : null;
|
|
4183
|
-
const
|
|
4184
|
-
|
|
4185
|
-
newImage ? extractMetadata(newImage.data, file.file_path) : null
|
|
4186
|
-
]);
|
|
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;
|
|
4187
4196
|
return c.json({
|
|
4188
4197
|
old: oldMeta ? formatMetadataLines(oldMeta) : null,
|
|
4189
4198
|
new: newMeta ? formatMetadataLines(newMeta) : null
|
|
@@ -4220,8 +4229,8 @@ apiRoutes.get("/image/:fileId/:side", async (c) => {
|
|
|
4220
4229
|
});
|
|
4221
4230
|
|
|
4222
4231
|
// src/routes/pages.tsx
|
|
4223
|
-
import { readFileSync as
|
|
4224
|
-
import { Hono as
|
|
4232
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
4233
|
+
import { Hono as Hono5 } from "hono";
|
|
4225
4234
|
import { resolve as resolve4 } from "path";
|
|
4226
4235
|
|
|
4227
4236
|
// src/utils/escapeHtml.ts
|
|
@@ -4536,10 +4545,10 @@ function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
|
4536
4545
|
"data-file-path": file.file_path,
|
|
4537
4546
|
"data-has-old": String(hasOld),
|
|
4538
4547
|
"data-has-new": String(hasNew),
|
|
4539
|
-
...baseWidth ? { "data-base-width": String(baseWidth) } : {},
|
|
4540
|
-
...baseHeight ? { "data-base-height": String(baseHeight) } : {},
|
|
4548
|
+
...baseWidth !== void 0 ? { "data-base-width": String(baseWidth) } : {},
|
|
4549
|
+
...baseHeight !== void 0 ? { "data-base-height": String(baseHeight) } : {},
|
|
4541
4550
|
children: [
|
|
4542
|
-
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." }),
|
|
4543
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..." }) }),
|
|
4544
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: [
|
|
4545
4554
|
/* @__PURE__ */ jsx("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
@@ -4998,9 +5007,570 @@ function FileList({ files, annotationCounts, staleCounts }) {
|
|
|
4998
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 }) }) });
|
|
4999
5008
|
}
|
|
5000
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
|
+
|
|
5001
5568
|
// src/components/layout.tsx
|
|
5002
5569
|
function Layout({ title, reviewId, children }) {
|
|
5003
|
-
|
|
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: [
|
|
5004
5574
|
/* @__PURE__ */ jsx("head", { children: [
|
|
5005
5575
|
/* @__PURE__ */ jsx("meta", { charset: "utf-8" }),
|
|
5006
5576
|
/* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
@@ -5081,7 +5651,7 @@ function ReviewHistory({ reviews, currentReviewId }) {
|
|
|
5081
5651
|
|
|
5082
5652
|
// src/routes/pages.tsx
|
|
5083
5653
|
init_queries();
|
|
5084
|
-
var pageRoutes = new
|
|
5654
|
+
var pageRoutes = new Hono5();
|
|
5085
5655
|
pageRoutes.get("/", async (c) => {
|
|
5086
5656
|
const reviewId = c.get("reviewId");
|
|
5087
5657
|
const review = await getReview(reviewId);
|
|
@@ -5235,11 +5805,11 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
5235
5805
|
});
|
|
5236
5806
|
pageRoutes.get("/file-raw", (c) => {
|
|
5237
5807
|
const filePath = c.req.query("path");
|
|
5238
|
-
if (
|
|
5808
|
+
if (filePath === void 0 || filePath === "") return c.text("Missing path", 400);
|
|
5239
5809
|
const repoRoot = c.get("repoRoot");
|
|
5240
5810
|
let content;
|
|
5241
5811
|
try {
|
|
5242
|
-
content =
|
|
5812
|
+
content = readFileSync9(resolve4(repoRoot, filePath), "utf-8");
|
|
5243
5813
|
} catch {
|
|
5244
5814
|
return c.text("File not found", 404);
|
|
5245
5815
|
}
|
|
@@ -5262,7 +5832,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
5262
5832
|
}))
|
|
5263
5833
|
}]
|
|
5264
5834
|
};
|
|
5265
|
-
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: "" };
|
|
5266
5836
|
const html = /* @__PURE__ */ jsx(DiffView, { file: fakeFile, diff, annotations: [], mode: "unified" });
|
|
5267
5837
|
return c.html(html.toString());
|
|
5268
5838
|
});
|
|
@@ -5365,10 +5935,112 @@ pageRoutes.get("/history", async (c) => {
|
|
|
5365
5935
|
return c.html(html.toString());
|
|
5366
5936
|
});
|
|
5367
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
|
+
|
|
5368
6040
|
// src/server.ts
|
|
5369
6041
|
function tryServe(fetch2, port) {
|
|
5370
6042
|
return new Promise((resolve6, reject) => {
|
|
5371
|
-
const server = serve({ fetch: fetch2, port });
|
|
6043
|
+
const server = serve({ fetch: fetch2, port, hostname: "127.0.0.1" });
|
|
5372
6044
|
server.on("listening", () => {
|
|
5373
6045
|
resolve6(port);
|
|
5374
6046
|
});
|
|
@@ -5382,7 +6054,7 @@ function tryServe(fetch2, port) {
|
|
|
5382
6054
|
});
|
|
5383
6055
|
}
|
|
5384
6056
|
async function startServer(port, reviewId, repoRoot, options) {
|
|
5385
|
-
const app = new
|
|
6057
|
+
const app = new Hono7();
|
|
5386
6058
|
app.use("*", async (c, next) => {
|
|
5387
6059
|
c.set("reviewId", reviewId);
|
|
5388
6060
|
c.set("currentReviewId", reviewId);
|
|
@@ -5390,24 +6062,25 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
5390
6062
|
await next();
|
|
5391
6063
|
});
|
|
5392
6064
|
const selfDir = dirname(fileURLToPath(import.meta.url));
|
|
5393
|
-
const distDir =
|
|
6065
|
+
const distDir = existsSync7(join8(selfDir, "client", "styles.css")) ? join8(selfDir, "client") : join8(selfDir, "..", "dist", "client");
|
|
5394
6066
|
app.get("/static/styles.css", (c) => {
|
|
5395
|
-
const css =
|
|
6067
|
+
const css = readFileSync10(join8(distDir, "styles.css"), "utf-8");
|
|
5396
6068
|
return c.text(css, 200, { "Content-Type": "text/css", "Cache-Control": "no-cache" });
|
|
5397
6069
|
});
|
|
5398
6070
|
app.get("/static/app.js", (c) => {
|
|
5399
|
-
const js =
|
|
6071
|
+
const js = readFileSync10(join8(distDir, "app.global.js"), "utf-8");
|
|
5400
6072
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
5401
6073
|
});
|
|
5402
6074
|
app.get("/static/history.js", (c) => {
|
|
5403
|
-
const js =
|
|
6075
|
+
const js = readFileSync10(join8(distDir, "history.global.js"), "utf-8");
|
|
5404
6076
|
return c.text(js, 200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache" });
|
|
5405
6077
|
});
|
|
5406
6078
|
app.route("/api", apiRoutes);
|
|
5407
6079
|
app.route("/api/ai", aiApiRoutes);
|
|
6080
|
+
app.route("/api/themes", themeApiRoutes);
|
|
5408
6081
|
app.route("/", pageRoutes);
|
|
5409
6082
|
let actualPort = port;
|
|
5410
|
-
if (options?.strictPort) {
|
|
6083
|
+
if (options?.strictPort === true) {
|
|
5411
6084
|
actualPort = await tryServe(app.fetch, port);
|
|
5412
6085
|
} else {
|
|
5413
6086
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
@@ -5429,15 +6102,15 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
5429
6102
|
console.log(`
|
|
5430
6103
|
Glassbox running at ${url}
|
|
5431
6104
|
`);
|
|
5432
|
-
if (
|
|
6105
|
+
if (options?.noOpen !== true) {
|
|
5433
6106
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
5434
6107
|
exec(`${openCmd} ${url}`);
|
|
5435
6108
|
}
|
|
5436
6109
|
}
|
|
5437
6110
|
|
|
5438
6111
|
// src/skills.ts
|
|
5439
|
-
import { existsSync as
|
|
5440
|
-
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";
|
|
5441
6114
|
var SKILL_VERSION = 1;
|
|
5442
6115
|
function versionHeader() {
|
|
5443
6116
|
return `<!-- glassbox-skill-version: ${SKILL_VERSION} -->`;
|
|
@@ -5448,14 +6121,14 @@ function parseVersionHeader(content) {
|
|
|
5448
6121
|
return parseInt(match[1], 10);
|
|
5449
6122
|
}
|
|
5450
6123
|
function updateFile(path, content) {
|
|
5451
|
-
if (
|
|
5452
|
-
const existing =
|
|
6124
|
+
if (existsSync8(path)) {
|
|
6125
|
+
const existing = readFileSync11(path, "utf-8");
|
|
5453
6126
|
const version = parseVersionHeader(existing);
|
|
5454
6127
|
if (version !== null && version >= SKILL_VERSION) {
|
|
5455
6128
|
return false;
|
|
5456
6129
|
}
|
|
5457
6130
|
}
|
|
5458
|
-
|
|
6131
|
+
writeFileSync6(path, content, "utf-8");
|
|
5459
6132
|
return true;
|
|
5460
6133
|
}
|
|
5461
6134
|
function skillBody() {
|
|
@@ -5475,8 +6148,8 @@ function skillBody() {
|
|
|
5475
6148
|
].join("\n");
|
|
5476
6149
|
}
|
|
5477
6150
|
function ensureClaudeSkills(cwd) {
|
|
5478
|
-
const dir =
|
|
5479
|
-
|
|
6151
|
+
const dir = join9(cwd, ".claude", "skills", "glassbox");
|
|
6152
|
+
mkdirSync6(dir, { recursive: true });
|
|
5480
6153
|
const content = [
|
|
5481
6154
|
"---",
|
|
5482
6155
|
"name: glassbox",
|
|
@@ -5488,11 +6161,11 @@ function ensureClaudeSkills(cwd) {
|
|
|
5488
6161
|
skillBody(),
|
|
5489
6162
|
""
|
|
5490
6163
|
].join("\n");
|
|
5491
|
-
return updateFile(
|
|
6164
|
+
return updateFile(join9(dir, "SKILL.md"), content);
|
|
5492
6165
|
}
|
|
5493
6166
|
function ensureCursorRules(cwd) {
|
|
5494
|
-
const rulesDir =
|
|
5495
|
-
|
|
6167
|
+
const rulesDir = join9(cwd, ".cursor", "rules");
|
|
6168
|
+
mkdirSync6(rulesDir, { recursive: true });
|
|
5496
6169
|
const content = [
|
|
5497
6170
|
"---",
|
|
5498
6171
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -5503,11 +6176,11 @@ function ensureCursorRules(cwd) {
|
|
|
5503
6176
|
skillBody(),
|
|
5504
6177
|
""
|
|
5505
6178
|
].join("\n");
|
|
5506
|
-
return updateFile(
|
|
6179
|
+
return updateFile(join9(rulesDir, "glassbox.mdc"), content);
|
|
5507
6180
|
}
|
|
5508
6181
|
function ensureCopilotPrompts(cwd) {
|
|
5509
|
-
const promptsDir =
|
|
5510
|
-
|
|
6182
|
+
const promptsDir = join9(cwd, ".github", "prompts");
|
|
6183
|
+
mkdirSync6(promptsDir, { recursive: true });
|
|
5511
6184
|
const content = [
|
|
5512
6185
|
"---",
|
|
5513
6186
|
"description: Read the latest Glassbox code review and apply all feedback annotations",
|
|
@@ -5517,11 +6190,11 @@ function ensureCopilotPrompts(cwd) {
|
|
|
5517
6190
|
skillBody(),
|
|
5518
6191
|
""
|
|
5519
6192
|
].join("\n");
|
|
5520
|
-
return updateFile(
|
|
6193
|
+
return updateFile(join9(promptsDir, "glassbox.prompt.md"), content);
|
|
5521
6194
|
}
|
|
5522
6195
|
function ensureWindsurfRules(cwd) {
|
|
5523
|
-
const rulesDir =
|
|
5524
|
-
|
|
6196
|
+
const rulesDir = join9(cwd, ".windsurf", "rules");
|
|
6197
|
+
mkdirSync6(rulesDir, { recursive: true });
|
|
5525
6198
|
const content = [
|
|
5526
6199
|
"---",
|
|
5527
6200
|
"trigger: manual",
|
|
@@ -5532,39 +6205,39 @@ function ensureWindsurfRules(cwd) {
|
|
|
5532
6205
|
skillBody(),
|
|
5533
6206
|
""
|
|
5534
6207
|
].join("\n");
|
|
5535
|
-
return updateFile(
|
|
6208
|
+
return updateFile(join9(rulesDir, "glassbox.md"), content);
|
|
5536
6209
|
}
|
|
5537
6210
|
function ensureSkills() {
|
|
5538
6211
|
const cwd = process.cwd();
|
|
5539
6212
|
const platforms = [];
|
|
5540
|
-
if (
|
|
6213
|
+
if (existsSync8(join9(cwd, ".claude"))) {
|
|
5541
6214
|
if (ensureClaudeSkills(cwd)) platforms.push("Claude Code");
|
|
5542
6215
|
}
|
|
5543
|
-
if (
|
|
6216
|
+
if (existsSync8(join9(cwd, ".cursor"))) {
|
|
5544
6217
|
if (ensureCursorRules(cwd)) platforms.push("Cursor");
|
|
5545
6218
|
}
|
|
5546
|
-
if (
|
|
6219
|
+
if (existsSync8(join9(cwd, ".github", "prompts")) || existsSync8(join9(cwd, ".github", "copilot-instructions.md"))) {
|
|
5547
6220
|
if (ensureCopilotPrompts(cwd)) platforms.push("GitHub Copilot");
|
|
5548
6221
|
}
|
|
5549
|
-
if (
|
|
6222
|
+
if (existsSync8(join9(cwd, ".windsurf"))) {
|
|
5550
6223
|
if (ensureWindsurfRules(cwd)) platforms.push("Windsurf");
|
|
5551
6224
|
}
|
|
5552
6225
|
return platforms;
|
|
5553
6226
|
}
|
|
5554
6227
|
|
|
5555
6228
|
// src/update-check.ts
|
|
5556
|
-
import { existsSync as
|
|
6229
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
5557
6230
|
import { get } from "https";
|
|
5558
|
-
import { homedir as
|
|
5559
|
-
import { dirname as dirname2, join as
|
|
6231
|
+
import { homedir as homedir4 } from "os";
|
|
6232
|
+
import { dirname as dirname2, join as join10 } from "path";
|
|
5560
6233
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5561
|
-
var DATA_DIR =
|
|
5562
|
-
var CHECK_FILE =
|
|
6234
|
+
var DATA_DIR = join10(homedir4(), ".glassbox");
|
|
6235
|
+
var CHECK_FILE = join10(DATA_DIR, "last-update-check");
|
|
5563
6236
|
var PACKAGE_NAME = "glassbox";
|
|
5564
6237
|
function getCurrentVersion() {
|
|
5565
6238
|
try {
|
|
5566
6239
|
const dir = dirname2(fileURLToPath2(import.meta.url));
|
|
5567
|
-
const pkg = JSON.parse(
|
|
6240
|
+
const pkg = JSON.parse(readFileSync12(join10(dir, "..", "package.json"), "utf-8"));
|
|
5568
6241
|
return pkg.version;
|
|
5569
6242
|
} catch {
|
|
5570
6243
|
return "0.0.0";
|
|
@@ -5572,16 +6245,16 @@ function getCurrentVersion() {
|
|
|
5572
6245
|
}
|
|
5573
6246
|
function getLastCheckDate() {
|
|
5574
6247
|
try {
|
|
5575
|
-
if (
|
|
5576
|
-
return
|
|
6248
|
+
if (existsSync9(CHECK_FILE)) {
|
|
6249
|
+
return readFileSync12(CHECK_FILE, "utf-8").trim();
|
|
5577
6250
|
}
|
|
5578
6251
|
} catch {
|
|
5579
6252
|
}
|
|
5580
6253
|
return null;
|
|
5581
6254
|
}
|
|
5582
6255
|
function saveCheckDate() {
|
|
5583
|
-
|
|
5584
|
-
|
|
6256
|
+
mkdirSync7(DATA_DIR, { recursive: true });
|
|
6257
|
+
writeFileSync7(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
|
|
5585
6258
|
}
|
|
5586
6259
|
function isFirstUseToday() {
|
|
5587
6260
|
const last = getLastCheckDate();
|
|
@@ -5807,13 +6480,13 @@ async function main() {
|
|
|
5807
6480
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
5808
6481
|
}
|
|
5809
6482
|
if (debug) {
|
|
5810
|
-
console.log(`[debug] Build timestamp: ${"2026-
|
|
6483
|
+
console.log(`[debug] Build timestamp: ${"2026-04-02T08:06:45.351Z"}`);
|
|
5811
6484
|
}
|
|
5812
|
-
if (projectDir) {
|
|
6485
|
+
if (projectDir !== null) {
|
|
5813
6486
|
process.chdir(projectDir);
|
|
5814
6487
|
}
|
|
5815
6488
|
if (dataDir === null) {
|
|
5816
|
-
dataDir =
|
|
6489
|
+
dataDir = join11(process.cwd(), ".glassbox");
|
|
5817
6490
|
}
|
|
5818
6491
|
if (demo !== null) {
|
|
5819
6492
|
const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
|
|
@@ -5825,13 +6498,13 @@ async function main() {
|
|
|
5825
6498
|
}
|
|
5826
6499
|
process.exit(1);
|
|
5827
6500
|
}
|
|
5828
|
-
dataDir =
|
|
6501
|
+
dataDir = join11(tmpdir(), `glassbox-demo-${demo}-${Date.now()}`);
|
|
5829
6502
|
setDemoMode(demo);
|
|
5830
6503
|
console.log(`
|
|
5831
6504
|
DEMO MODE: ${scenario.label}
|
|
5832
6505
|
`);
|
|
5833
6506
|
}
|
|
5834
|
-
|
|
6507
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
5835
6508
|
if (demo === null) {
|
|
5836
6509
|
acquireLock(dataDir);
|
|
5837
6510
|
}
|
|
@@ -5888,8 +6561,14 @@ async function main() {
|
|
|
5888
6561
|
console.log(`Review ${review.id} created.`);
|
|
5889
6562
|
await startServer(port, review.id, repoRoot, { noOpen, strictPort });
|
|
5890
6563
|
}
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
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
|
+
};
|
|
5895
6574
|
//# sourceMappingURL=cli.js.map
|