glassbox 0.8.2 → 0.9.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/cli.js +480 -578
- package/dist/client/app.global.js +11 -8
- package/dist/client/history.global.js +4 -1
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -97,11 +97,6 @@ var init_schema = __esm({
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
// src/db/connection.ts
|
|
100
|
-
var connection_exports = {};
|
|
101
|
-
__export(connection_exports, {
|
|
102
|
-
getDb: () => getDb,
|
|
103
|
-
setDataDir: () => setDataDir
|
|
104
|
-
});
|
|
105
100
|
import { PGlite } from "@electric-sql/pglite";
|
|
106
101
|
import { mkdirSync, rmSync } from "fs";
|
|
107
102
|
import { join } from "path";
|
|
@@ -171,6 +166,16 @@ var init_connection = __esm({
|
|
|
171
166
|
}
|
|
172
167
|
});
|
|
173
168
|
|
|
169
|
+
// src/db/ids.ts
|
|
170
|
+
function generateId() {
|
|
171
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
172
|
+
}
|
|
173
|
+
var init_ids = __esm({
|
|
174
|
+
"src/db/ids.ts"() {
|
|
175
|
+
"use strict";
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
174
179
|
// src/db/queries.ts
|
|
175
180
|
var queries_exports = {};
|
|
176
181
|
__export(queries_exports, {
|
|
@@ -181,6 +186,7 @@ __export(queries_exports, {
|
|
|
181
186
|
deleteReview: () => deleteReview,
|
|
182
187
|
deleteReviewFile: () => deleteReviewFile,
|
|
183
188
|
deleteStaleAnnotations: () => deleteStaleAnnotations,
|
|
189
|
+
getAnnotationCountsForReview: () => getAnnotationCountsForReview,
|
|
184
190
|
getAnnotationsForFile: () => getAnnotationsForFile,
|
|
185
191
|
getAnnotationsForReview: () => getAnnotationsForReview,
|
|
186
192
|
getLatestInProgressReview: () => getLatestInProgressReview,
|
|
@@ -199,9 +205,6 @@ __export(queries_exports, {
|
|
|
199
205
|
updateReviewHead: () => updateReviewHead,
|
|
200
206
|
updateReviewStatus: () => updateReviewStatus
|
|
201
207
|
});
|
|
202
|
-
function generateId() {
|
|
203
|
-
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
204
|
-
}
|
|
205
208
|
async function createReview(repoPath, repoName, mode, modeArgs, headCommit) {
|
|
206
209
|
const db2 = await getDb();
|
|
207
210
|
const id = generateId();
|
|
@@ -383,10 +386,26 @@ async function getStaleCountsForReview(reviewId) {
|
|
|
383
386
|
}
|
|
384
387
|
return counts;
|
|
385
388
|
}
|
|
389
|
+
async function getAnnotationCountsForReview(reviewId) {
|
|
390
|
+
const db2 = await getDb();
|
|
391
|
+
const result = await db2.query(
|
|
392
|
+
`SELECT a.review_file_id, COUNT(*)::text as count FROM annotations a
|
|
393
|
+
JOIN review_files rf ON a.review_file_id = rf.id
|
|
394
|
+
WHERE rf.review_id = $1
|
|
395
|
+
GROUP BY a.review_file_id`,
|
|
396
|
+
[reviewId]
|
|
397
|
+
);
|
|
398
|
+
const counts = {};
|
|
399
|
+
for (const row of result.rows) {
|
|
400
|
+
counts[row.review_file_id] = parseInt(row.count, 10);
|
|
401
|
+
}
|
|
402
|
+
return counts;
|
|
403
|
+
}
|
|
386
404
|
var init_queries = __esm({
|
|
387
405
|
"src/db/queries.ts"() {
|
|
388
406
|
"use strict";
|
|
389
407
|
init_connection();
|
|
408
|
+
init_ids();
|
|
390
409
|
}
|
|
391
410
|
});
|
|
392
411
|
|
|
@@ -395,7 +414,7 @@ init_connection();
|
|
|
395
414
|
init_queries();
|
|
396
415
|
import { mkdirSync as mkdirSync9, realpathSync } from "fs";
|
|
397
416
|
import { tmpdir } from "os";
|
|
398
|
-
import { join as join13, resolve as
|
|
417
|
+
import { join as join13, resolve as resolve8 } from "path";
|
|
399
418
|
|
|
400
419
|
// src/debug.ts
|
|
401
420
|
var debugEnabled = false;
|
|
@@ -691,12 +710,10 @@ function saveGuidedReviewConfig(settings) {
|
|
|
691
710
|
|
|
692
711
|
// src/db/ai-queries.ts
|
|
693
712
|
init_connection();
|
|
694
|
-
|
|
695
|
-
return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
|
|
696
|
-
}
|
|
713
|
+
init_ids();
|
|
697
714
|
async function createAnalysis(reviewId, analysisType) {
|
|
698
715
|
const db2 = await getDb();
|
|
699
|
-
const id =
|
|
716
|
+
const id = generateId();
|
|
700
717
|
const result = await db2.query(
|
|
701
718
|
`INSERT INTO ai_analyses (id, review_id, analysis_type, status)
|
|
702
719
|
VALUES ($1, $2, $3, 'running') RETURNING *`,
|
|
@@ -737,7 +754,7 @@ async function appendFileScores(analysisId, scores) {
|
|
|
737
754
|
const existingPaths = new Set(existing.rows.map((r) => r.file_path));
|
|
738
755
|
for (const score of scores) {
|
|
739
756
|
if (existingPaths.has(score.filePath)) continue;
|
|
740
|
-
const id =
|
|
757
|
+
const id = generateId();
|
|
741
758
|
await db2.query(
|
|
742
759
|
`INSERT INTO ai_file_scores (id, analysis_id, review_file_id, file_path, sort_order, aggregate_score, rationale, dimension_scores, notes)
|
|
743
760
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
|
@@ -1751,7 +1768,6 @@ async function updateReviewDiffs(reviewId, newDiffs, headCommit) {
|
|
|
1751
1768
|
|
|
1752
1769
|
// src/server.ts
|
|
1753
1770
|
import { serve } from "@hono/node-server";
|
|
1754
|
-
import { exec } from "child_process";
|
|
1755
1771
|
import { existsSync as existsSync8, readFileSync as readFileSync12 } from "fs";
|
|
1756
1772
|
import { Hono as Hono16 } from "hono";
|
|
1757
1773
|
import { dirname as dirname2, join as join10 } from "path";
|
|
@@ -2480,7 +2496,7 @@ async function runBatches(batches, totalFiles, processBatch, onBatchComplete, on
|
|
|
2480
2496
|
while (nextIndex < batches.length || running.size > 0) {
|
|
2481
2497
|
while (nextIndex < batches.length && running.size < concurrency) {
|
|
2482
2498
|
if (shouldCancel !== void 0 && shouldCancel()) {
|
|
2483
|
-
debugLog(`${tag}Batch runner
|
|
2499
|
+
debugLog(`${tag}Batch runner canceled \u2014 skipping batch ${String(nextIndex)} and ${String(batches.length - nextIndex - 1)} remaining`);
|
|
2484
2500
|
nextIndex = batches.length;
|
|
2485
2501
|
break;
|
|
2486
2502
|
}
|
|
@@ -2509,8 +2525,8 @@ function isRetriable(err) {
|
|
|
2509
2525
|
return msg.includes("429") || msg.includes("500") || msg.includes("502") || msg.includes("503") || msg.includes("504") || msg.includes("rate_limit");
|
|
2510
2526
|
}
|
|
2511
2527
|
function sleep(ms) {
|
|
2512
|
-
return new Promise((
|
|
2513
|
-
setTimeout(
|
|
2528
|
+
return new Promise((resolve9) => {
|
|
2529
|
+
setTimeout(resolve9, ms);
|
|
2514
2530
|
});
|
|
2515
2531
|
}
|
|
2516
2532
|
|
|
@@ -2554,8 +2570,8 @@ function randomLines(count) {
|
|
|
2554
2570
|
return lines.sort((a, b) => a.line - b.line);
|
|
2555
2571
|
}
|
|
2556
2572
|
function sleep2(ms) {
|
|
2557
|
-
return new Promise((
|
|
2558
|
-
setTimeout(
|
|
2573
|
+
return new Promise((resolve9) => {
|
|
2574
|
+
setTimeout(resolve9, ms);
|
|
2559
2575
|
});
|
|
2560
2576
|
}
|
|
2561
2577
|
async function mockRiskAnalysisBatch(files) {
|
|
@@ -2623,6 +2639,7 @@ async function mockGuidedAnalysisBatch(files) {
|
|
|
2623
2639
|
}
|
|
2624
2640
|
|
|
2625
2641
|
// src/routes/ai-analysis.ts
|
|
2642
|
+
init_connection();
|
|
2626
2643
|
init_queries();
|
|
2627
2644
|
|
|
2628
2645
|
// src/utils/resolveReviewId.ts
|
|
@@ -2648,7 +2665,7 @@ var VALID_RISK_DIMENSIONS = ["aggregate", "security", "correctness", "error-hand
|
|
|
2648
2665
|
var VALID_SVG_VIEW_MODES = ["code", "rendered"];
|
|
2649
2666
|
var VALID_IMAGE_MODES = ["metadata", "side-by-side", "difference", "slice"];
|
|
2650
2667
|
var VALID_ANALYSIS_TYPES = ["risk", "narrative", "guided"];
|
|
2651
|
-
var
|
|
2668
|
+
var canceledAnalyses = /* @__PURE__ */ new Set();
|
|
2652
2669
|
aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
2653
2670
|
const reviewId = resolveReviewId(c);
|
|
2654
2671
|
const repoRoot = c.get("repoRoot");
|
|
@@ -2671,21 +2688,21 @@ aiAnalysisRoutes.post("/analyze", async (c) => {
|
|
|
2671
2688
|
return c.json({ error: "No files in review" }, 400);
|
|
2672
2689
|
}
|
|
2673
2690
|
if (invalidateCache) {
|
|
2674
|
-
debugLog("POST /analyze: invalidateCache=true,
|
|
2691
|
+
debugLog("POST /analyze: invalidateCache=true, canceling all running analyses");
|
|
2675
2692
|
for (const type of ["risk", "narrative", "guided"]) {
|
|
2676
2693
|
const running = await getLatestAnalysis(reviewId, type);
|
|
2677
2694
|
if (running !== void 0 && running.status === "running") {
|
|
2678
|
-
debugLog(`POST /analyze:
|
|
2679
|
-
|
|
2680
|
-
await updateAnalysisStatus(running.id, "failed", "
|
|
2695
|
+
debugLog(`POST /analyze: canceling ${type} analysis id=${running.id}`);
|
|
2696
|
+
canceledAnalyses.add(running.id);
|
|
2697
|
+
await updateAnalysisStatus(running.id, "failed", "Canceled");
|
|
2681
2698
|
}
|
|
2682
2699
|
}
|
|
2683
2700
|
} else if (analysisType === "risk" || analysisType === "narrative") {
|
|
2684
2701
|
const otherType = analysisType === "risk" ? "narrative" : "risk";
|
|
2685
2702
|
const otherRunning = await getLatestAnalysis(reviewId, otherType);
|
|
2686
2703
|
if (otherRunning !== void 0 && otherRunning.status === "running") {
|
|
2687
|
-
debugLog(`POST /analyze:
|
|
2688
|
-
|
|
2704
|
+
debugLog(`POST /analyze: canceling ${otherType} analysis id=${otherRunning.id} (switching to ${analysisType})`);
|
|
2705
|
+
canceledAnalyses.add(otherRunning.id);
|
|
2689
2706
|
}
|
|
2690
2707
|
}
|
|
2691
2708
|
if (!invalidateCache) {
|
|
@@ -2727,76 +2744,46 @@ async function executeAnalysis(input) {
|
|
|
2727
2744
|
debugLog(`Context window: ${String(contextWindow)} tokens`);
|
|
2728
2745
|
const { batches, binaryFiles } = planBatches(files, contextWindow);
|
|
2729
2746
|
const fileIdMap = new Map(files.map((f) => [f.file_path, f.id]));
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
}
|
|
2740
|
-
return false;
|
|
2747
|
+
debugLog(`Analysis plan: ${String(batches.reduce((s, b) => s + b.files.length, 0))} analyzable + ${String(binaryFiles.length)} binary in ${String(batches.length)} batch(es)`);
|
|
2748
|
+
const { cachedCount, filteredBatches } = await applyCachedScores({
|
|
2749
|
+
analysisId,
|
|
2750
|
+
analysisType,
|
|
2751
|
+
reviewId,
|
|
2752
|
+
fileIdMap,
|
|
2753
|
+
batches,
|
|
2754
|
+
binaryFiles,
|
|
2755
|
+
invalidateCache
|
|
2741
2756
|
});
|
|
2742
|
-
debugLog(`Cache: ${String(cachedScores.length)} scores from previous analysis, ${String(totalAnalyzable - cachedScores.length)} files need processing`);
|
|
2743
|
-
if (cachedScores.length > 0) {
|
|
2744
|
-
const cachedForInsert = cachedScores.map((s) => ({
|
|
2745
|
-
reviewFileId: fileIdMap.get(s.file_path) ?? s.review_file_id,
|
|
2746
|
-
filePath: s.file_path,
|
|
2747
|
-
sortOrder: s.sort_order,
|
|
2748
|
-
aggregateScore: s.aggregate_score,
|
|
2749
|
-
rationale: s.rationale,
|
|
2750
|
-
dimensionScores: s.dimension_scores !== null ? JSON.parse(s.dimension_scores) : null,
|
|
2751
|
-
notes: s.notes !== null ? JSON.parse(s.notes) : null
|
|
2752
|
-
}));
|
|
2753
|
-
await appendFileScores(analysisId, cachedForInsert);
|
|
2754
|
-
}
|
|
2755
|
-
const filteredBatches = batches.map((batch) => {
|
|
2756
|
-
const remaining = batch.files.filter((f) => !unchangedPaths.has(f.file_path));
|
|
2757
|
-
return { files: remaining, estimatedTokens: batch.estimatedTokens };
|
|
2758
|
-
}).filter((batch) => batch.files.length > 0);
|
|
2759
2757
|
const filteredAnalyzable = filteredBatches.reduce((sum, b) => sum + b.files.length, 0);
|
|
2760
|
-
const totalForProgress = filteredAnalyzable + binaryFiles.length +
|
|
2758
|
+
const totalForProgress = filteredAnalyzable + binaryFiles.length + cachedCount;
|
|
2761
2759
|
debugLog(`After cache: ${String(filteredAnalyzable)} files to analyze in ${String(filteredBatches.length)} batch(es)`);
|
|
2762
|
-
await updateAnalysisProgress(analysisId,
|
|
2763
|
-
|
|
2764
|
-
debugLog(`Saving ${String(binaryFiles.length)} binary files with score 0`);
|
|
2765
|
-
const binaryScoreEntries = binaryFiles.map((f, idx) => ({
|
|
2766
|
-
reviewFileId: fileIdMap.get(f.file_path) ?? "",
|
|
2767
|
-
filePath: f.file_path,
|
|
2768
|
-
sortOrder: 99999 + idx,
|
|
2769
|
-
// Will be re-sorted later
|
|
2770
|
-
aggregateScore: analysisType === "risk" ? 0 : null,
|
|
2771
|
-
rationale: "Binary file \u2014 not analyzed",
|
|
2772
|
-
dimensionScores: analysisType === "risk" ? { security: 0, correctness: 0, "error-handling": 0, maintainability: 0, architecture: 0, performance: 0 } : null,
|
|
2773
|
-
notes: null
|
|
2774
|
-
}));
|
|
2775
|
-
await appendFileScores(analysisId, binaryScoreEntries);
|
|
2776
|
-
await updateAnalysisProgress(analysisId, cachedScores.length + binaryFiles.length, totalForProgress);
|
|
2777
|
-
}
|
|
2760
|
+
await updateAnalysisProgress(analysisId, cachedCount, totalForProgress);
|
|
2761
|
+
await saveBinaryFiles({ analysisId, analysisType, fileIdMap, binaryFiles, cachedCount, totalForProgress });
|
|
2778
2762
|
if (filteredBatches.length === 0) {
|
|
2779
2763
|
debugLog("No batches to process (all files cached or binary), marking completed");
|
|
2780
2764
|
await updateAnalysisStatus(analysisId, "completed");
|
|
2781
2765
|
return;
|
|
2782
2766
|
}
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2767
|
+
const progressOffset = cachedCount + binaryFiles.length;
|
|
2768
|
+
await dispatchByType({
|
|
2769
|
+
analysisType,
|
|
2770
|
+
analysisId,
|
|
2771
|
+
filteredBatches,
|
|
2772
|
+
files,
|
|
2773
|
+
totalForProgress,
|
|
2774
|
+
progressOffset,
|
|
2775
|
+
config,
|
|
2776
|
+
repoRoot,
|
|
2777
|
+
fileIdMap,
|
|
2778
|
+
guidedReview
|
|
2779
|
+
});
|
|
2780
|
+
if (canceledAnalyses.has(analysisId)) {
|
|
2781
|
+
canceledAnalyses.delete(analysisId);
|
|
2782
|
+
debugLog(`Analysis ${analysisId} was canceled (user switched modes)`);
|
|
2783
|
+
await updateAnalysisStatus(analysisId, "failed", "Canceled");
|
|
2797
2784
|
return;
|
|
2798
2785
|
}
|
|
2799
|
-
|
|
2786
|
+
canceledAnalyses.delete(analysisId);
|
|
2800
2787
|
debugLog(`Analysis ${analysisId} completed successfully`);
|
|
2801
2788
|
await updateAnalysisStatus(analysisId, "completed");
|
|
2802
2789
|
} catch (err) {
|
|
@@ -2806,6 +2793,66 @@ async function executeAnalysis(input) {
|
|
|
2806
2793
|
await updateAnalysisStatus(analysisId, "failed", message);
|
|
2807
2794
|
}
|
|
2808
2795
|
}
|
|
2796
|
+
async function applyCachedScores(args) {
|
|
2797
|
+
const { analysisId, analysisType, reviewId, fileIdMap, batches, binaryFiles, invalidateCache } = args;
|
|
2798
|
+
const prevScores = invalidateCache ? [] : await getPreviousScores(reviewId, analysisType, analysisId);
|
|
2799
|
+
const binaryPathSet = new Set(binaryFiles.map((f) => f.file_path));
|
|
2800
|
+
const unchangedPaths = /* @__PURE__ */ new Set();
|
|
2801
|
+
const cachedScores = prevScores.filter((s) => {
|
|
2802
|
+
if (fileIdMap.has(s.file_path) && !binaryPathSet.has(s.file_path)) {
|
|
2803
|
+
unchangedPaths.add(s.file_path);
|
|
2804
|
+
return true;
|
|
2805
|
+
}
|
|
2806
|
+
return false;
|
|
2807
|
+
});
|
|
2808
|
+
debugLog(`Cache: ${String(cachedScores.length)} scores carried forward from previous analysis`);
|
|
2809
|
+
if (cachedScores.length > 0) {
|
|
2810
|
+
const cachedForInsert = cachedScores.map((s) => ({
|
|
2811
|
+
reviewFileId: fileIdMap.get(s.file_path) ?? s.review_file_id,
|
|
2812
|
+
filePath: s.file_path,
|
|
2813
|
+
sortOrder: s.sort_order,
|
|
2814
|
+
aggregateScore: s.aggregate_score,
|
|
2815
|
+
rationale: s.rationale,
|
|
2816
|
+
dimensionScores: safeJsonParse(s.dimension_scores),
|
|
2817
|
+
notes: safeJsonParse(s.notes)
|
|
2818
|
+
}));
|
|
2819
|
+
await appendFileScores(analysisId, cachedForInsert);
|
|
2820
|
+
}
|
|
2821
|
+
const filteredBatches = batches.map((batch) => ({
|
|
2822
|
+
files: batch.files.filter((f) => !unchangedPaths.has(f.file_path)),
|
|
2823
|
+
estimatedTokens: batch.estimatedTokens
|
|
2824
|
+
})).filter((batch) => batch.files.length > 0);
|
|
2825
|
+
return { cachedCount: cachedScores.length, filteredBatches };
|
|
2826
|
+
}
|
|
2827
|
+
async function saveBinaryFiles(args) {
|
|
2828
|
+
const { analysisId, analysisType, fileIdMap, binaryFiles, cachedCount, totalForProgress } = args;
|
|
2829
|
+
if (binaryFiles.length === 0) return;
|
|
2830
|
+
debugLog(`Saving ${String(binaryFiles.length)} binary files with score 0`);
|
|
2831
|
+
const binaryScoreEntries = binaryFiles.map((f, idx) => ({
|
|
2832
|
+
reviewFileId: fileIdMap.get(f.file_path) ?? "",
|
|
2833
|
+
filePath: f.file_path,
|
|
2834
|
+
sortOrder: 99999 + idx,
|
|
2835
|
+
// re-sorted by `updateSortOrders` later
|
|
2836
|
+
aggregateScore: analysisType === "risk" ? 0 : null,
|
|
2837
|
+
rationale: "Binary file \u2014 not analyzed",
|
|
2838
|
+
dimensionScores: analysisType === "risk" ? { security: 0, correctness: 0, "error-handling": 0, maintainability: 0, architecture: 0, performance: 0 } : null,
|
|
2839
|
+
notes: null
|
|
2840
|
+
}));
|
|
2841
|
+
await appendFileScores(analysisId, binaryScoreEntries);
|
|
2842
|
+
await updateAnalysisProgress(analysisId, cachedCount + binaryFiles.length, totalForProgress);
|
|
2843
|
+
}
|
|
2844
|
+
async function dispatchByType(args) {
|
|
2845
|
+
const { analysisType, analysisId, filteredBatches, files, totalForProgress, progressOffset, config, repoRoot, fileIdMap, guidedReview } = args;
|
|
2846
|
+
const shouldCancel = () => canceledAnalyses.has(analysisId);
|
|
2847
|
+
const runArgs = [analysisId, filteredBatches, files.length, totalForProgress, progressOffset];
|
|
2848
|
+
if (analysisType === "risk") {
|
|
2849
|
+
await runBatchedAnalysis(...runArgs, riskAnalysisConfig(config, repoRoot, fileIdMap, guidedReview, analysisId), shouldCancel);
|
|
2850
|
+
} else if (analysisType === "narrative") {
|
|
2851
|
+
await runBatchedAnalysis(...runArgs, narrativeAnalysisConfig(config, repoRoot, fileIdMap, guidedReview, analysisId), shouldCancel);
|
|
2852
|
+
} else {
|
|
2853
|
+
await runBatchedAnalysis(...runArgs, guidedAnalysisConfig(config, repoRoot, fileIdMap, guidedReview), shouldCancel);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2809
2856
|
async function runBatchedAnalysis(analysisId, batches, totalFiles, progressTotal, progressOffset, cfg, shouldCancel) {
|
|
2810
2857
|
const allResults = await runBatches(
|
|
2811
2858
|
batches,
|
|
@@ -2828,8 +2875,7 @@ async function runBatchedAnalysis(analysisId, batches, totalFiles, progressTotal
|
|
|
2828
2875
|
if (cfg.finalize) await cfg.finalize(allResults, batches.length);
|
|
2829
2876
|
}
|
|
2830
2877
|
async function updateSortOrders(analysisId, entries) {
|
|
2831
|
-
const
|
|
2832
|
-
const db2 = await getDb2();
|
|
2878
|
+
const db2 = await getDb();
|
|
2833
2879
|
for (const [filePath, sortOrder] of entries) {
|
|
2834
2880
|
await db2.query(
|
|
2835
2881
|
"UPDATE ai_file_scores SET sort_order = $1 WHERE analysis_id = $2 AND file_path = $3",
|
|
@@ -2939,11 +2985,19 @@ aiAnalysisRoutes.get("/analysis/:type", async (c) => {
|
|
|
2939
2985
|
sortOrder: s.sort_order,
|
|
2940
2986
|
aggregateScore: s.aggregate_score,
|
|
2941
2987
|
rationale: s.rationale,
|
|
2942
|
-
dimensionScores:
|
|
2943
|
-
notes:
|
|
2988
|
+
dimensionScores: safeJsonParse(s.dimension_scores),
|
|
2989
|
+
notes: safeJsonParse(s.notes)
|
|
2944
2990
|
}))
|
|
2945
2991
|
});
|
|
2946
2992
|
});
|
|
2993
|
+
function safeJsonParse(raw) {
|
|
2994
|
+
if (raw === null || raw === void 0) return null;
|
|
2995
|
+
try {
|
|
2996
|
+
return JSON.parse(raw);
|
|
2997
|
+
} catch {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
2947
3001
|
aiAnalysisRoutes.get("/analysis/:type/status", async (c) => {
|
|
2948
3002
|
const reviewId = resolveReviewId(c);
|
|
2949
3003
|
const analysisType = c.req.param("type");
|
|
@@ -2987,7 +3041,11 @@ aiAnalysisRoutes.get("/preferences", async (c) => {
|
|
|
2987
3041
|
return c.json(prefs);
|
|
2988
3042
|
});
|
|
2989
3043
|
aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
2990
|
-
const
|
|
3044
|
+
const raw = await c.req.json();
|
|
3045
|
+
if (typeof raw !== "object" || raw === null) {
|
|
3046
|
+
return c.json({ error: "body must be a JSON object" }, 400);
|
|
3047
|
+
}
|
|
3048
|
+
const body = raw;
|
|
2991
3049
|
if (body.sort_mode !== void 0) {
|
|
2992
3050
|
const v = checkEnum(body.sort_mode, "sort_mode", VALID_SORT_MODES);
|
|
2993
3051
|
if ("error" in v) return c.json({ error: v.error }, 400);
|
|
@@ -3010,7 +3068,14 @@ aiAnalysisRoutes.post("/preferences", async (c) => {
|
|
|
3010
3068
|
const v = checkEnum(body.last_image_mode, "last_image_mode", VALID_IMAGE_MODES);
|
|
3011
3069
|
if ("error" in v) return c.json({ error: v.error }, 400);
|
|
3012
3070
|
}
|
|
3013
|
-
|
|
3071
|
+
const allowed = {};
|
|
3072
|
+
if (body.sort_mode !== void 0) allowed.sort_mode = body.sort_mode;
|
|
3073
|
+
if (body.risk_sort_dimension !== void 0) allowed.risk_sort_dimension = body.risk_sort_dimension;
|
|
3074
|
+
if (body.show_risk_scores !== void 0) allowed.show_risk_scores = body.show_risk_scores;
|
|
3075
|
+
if (body.ignore_whitespace !== void 0) allowed.ignore_whitespace = body.ignore_whitespace;
|
|
3076
|
+
if (body.svg_view_mode !== void 0) allowed.svg_view_mode = body.svg_view_mode;
|
|
3077
|
+
if (body.last_image_mode !== void 0) allowed.last_image_mode = body.last_image_mode;
|
|
3078
|
+
await saveUserPreferences(allowed);
|
|
3014
3079
|
return c.json({ ok: true });
|
|
3015
3080
|
});
|
|
3016
3081
|
|
|
@@ -3352,6 +3417,9 @@ contextRoutes.get("/context/:fileId", async (c) => {
|
|
|
3352
3417
|
if (!file) return c.json({ error: "Not found" }, 404);
|
|
3353
3418
|
const startLine = parseInt(c.req.query("start") ?? "1", 10);
|
|
3354
3419
|
const endLine = parseInt(c.req.query("end") ?? "20", 10);
|
|
3420
|
+
if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {
|
|
3421
|
+
return c.json({ error: "start and end must be integers" }, 400);
|
|
3422
|
+
}
|
|
3355
3423
|
const content = getFileContent(file.file_path, "working", repoRoot);
|
|
3356
3424
|
const allLines = content.split("\n");
|
|
3357
3425
|
const clampedStart = Math.max(1, startLine);
|
|
@@ -3365,20 +3433,42 @@ contextRoutes.get("/context/:fileId", async (c) => {
|
|
|
3365
3433
|
|
|
3366
3434
|
// src/routes/api/files.ts
|
|
3367
3435
|
init_queries();
|
|
3368
|
-
import { execFileSync } from "child_process";
|
|
3369
3436
|
import { Hono as Hono6 } from "hono";
|
|
3437
|
+
import { resolve as resolve4 } from "path";
|
|
3438
|
+
|
|
3439
|
+
// src/utils/openOS.ts
|
|
3440
|
+
import { execFileSync } from "child_process";
|
|
3370
3441
|
import { resolve as resolve3 } from "path";
|
|
3442
|
+
function openOS(target, mode) {
|
|
3443
|
+
if (mode === "reveal") {
|
|
3444
|
+
if (process.platform === "darwin") {
|
|
3445
|
+
execFileSync("open", ["-R", target]);
|
|
3446
|
+
} else if (process.platform === "win32") {
|
|
3447
|
+
execFileSync("explorer", ["/select," + target]);
|
|
3448
|
+
} else {
|
|
3449
|
+
execFileSync("xdg-open", [resolve3(target, "..")]);
|
|
3450
|
+
}
|
|
3451
|
+
return;
|
|
3452
|
+
}
|
|
3453
|
+
if (process.platform === "darwin") {
|
|
3454
|
+
execFileSync("open", [target]);
|
|
3455
|
+
} else if (process.platform === "win32") {
|
|
3456
|
+
execFileSync("cmd", ["/c", "start", "", target]);
|
|
3457
|
+
} else {
|
|
3458
|
+
execFileSync("xdg-open", [target]);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
// src/routes/api/files.ts
|
|
3371
3463
|
var filesRoutes = new Hono6();
|
|
3372
3464
|
var VALID_FILE_STATUSES = ["pending", "reviewed"];
|
|
3373
3465
|
filesRoutes.get("/files", async (c) => {
|
|
3374
3466
|
const reviewId = resolveReviewId(c);
|
|
3375
|
-
const files = await
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
}
|
|
3381
|
-
const staleCounts = await getStaleCountsForReview(reviewId);
|
|
3467
|
+
const [files, annotationCounts, staleCounts] = await Promise.all([
|
|
3468
|
+
getReviewFiles(reviewId),
|
|
3469
|
+
getAnnotationCountsForReview(reviewId),
|
|
3470
|
+
getStaleCountsForReview(reviewId)
|
|
3471
|
+
]);
|
|
3382
3472
|
return c.json({ files, annotationCounts, staleCounts });
|
|
3383
3473
|
});
|
|
3384
3474
|
filesRoutes.get("/files/:fileId", async (c) => {
|
|
@@ -3398,15 +3488,9 @@ filesRoutes.post("/files/:fileId/reveal", async (c) => {
|
|
|
3398
3488
|
const file = await getReviewFile(c.req.param("fileId"));
|
|
3399
3489
|
if (!file) return c.json({ error: "Not found" }, 404);
|
|
3400
3490
|
const repoRoot = c.get("repoRoot");
|
|
3401
|
-
const fullPath =
|
|
3491
|
+
const fullPath = resolve4(repoRoot, file.file_path);
|
|
3402
3492
|
try {
|
|
3403
|
-
|
|
3404
|
-
execFileSync("open", ["-R", fullPath]);
|
|
3405
|
-
} else if (process.platform === "win32") {
|
|
3406
|
-
execFileSync("explorer", ["/select," + fullPath]);
|
|
3407
|
-
} else {
|
|
3408
|
-
execFileSync("xdg-open", [resolve3(fullPath, "..")]);
|
|
3409
|
-
}
|
|
3493
|
+
openOS(fullPath, "reveal");
|
|
3410
3494
|
} catch {
|
|
3411
3495
|
}
|
|
3412
3496
|
return c.json({ ok: true });
|
|
@@ -3419,7 +3503,7 @@ import { Hono as Hono7 } from "hono";
|
|
|
3419
3503
|
// src/git/image.ts
|
|
3420
3504
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
3421
3505
|
import { readFileSync as readFileSync6 } from "fs";
|
|
3422
|
-
import { resolve as
|
|
3506
|
+
import { resolve as resolve5 } from "path";
|
|
3423
3507
|
|
|
3424
3508
|
// src/git/image-metadata.ts
|
|
3425
3509
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"]);
|
|
@@ -3689,7 +3773,7 @@ function gitShowFile(ref, filePath, repoRoot) {
|
|
|
3689
3773
|
}
|
|
3690
3774
|
function readWorkingFile(filePath, repoRoot) {
|
|
3691
3775
|
try {
|
|
3692
|
-
return readFileSync6(
|
|
3776
|
+
return readFileSync6(resolve5(repoRoot, filePath));
|
|
3693
3777
|
} catch {
|
|
3694
3778
|
return null;
|
|
3695
3779
|
}
|
|
@@ -3913,7 +3997,7 @@ init_queries();
|
|
|
3913
3997
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
3914
3998
|
import { readFileSync as readFileSync8 } from "fs";
|
|
3915
3999
|
import { Hono as Hono8 } from "hono";
|
|
3916
|
-
import { resolve as
|
|
4000
|
+
import { resolve as resolve6 } from "path";
|
|
3917
4001
|
|
|
3918
4002
|
// src/outline/parser.ts
|
|
3919
4003
|
var BRACE_LANGS = /* @__PURE__ */ new Set([
|
|
@@ -4282,7 +4366,7 @@ outlineRoutes.get("/symbol-definition", async (c) => {
|
|
|
4282
4366
|
if (!/\.(js|mjs|cjs|jsx|ts|tsx|mts|cts|java|go|rs|c|h|cpp|cc|cxx|hpp|cs|swift|php|kt|kts|scala|dart|groovy|py|rb)$/i.test(ext)) continue;
|
|
4283
4367
|
let content = "";
|
|
4284
4368
|
try {
|
|
4285
|
-
content = readFileSync8(
|
|
4369
|
+
content = readFileSync8(resolve6(repoRoot, filePath), "utf-8");
|
|
4286
4370
|
} catch {
|
|
4287
4371
|
continue;
|
|
4288
4372
|
}
|
|
@@ -4342,7 +4426,11 @@ projectSettingsRoutes.get("/project-settings", (c) => {
|
|
|
4342
4426
|
});
|
|
4343
4427
|
projectSettingsRoutes.patch("/project-settings", async (c) => {
|
|
4344
4428
|
const repoRoot = c.get("repoRoot");
|
|
4345
|
-
const
|
|
4429
|
+
const raw = await c.req.json();
|
|
4430
|
+
if (typeof raw !== "object" || raw === null) {
|
|
4431
|
+
return c.json({ error: "body must be a JSON object" }, 400);
|
|
4432
|
+
}
|
|
4433
|
+
const body = raw;
|
|
4346
4434
|
if (body.appName !== void 0 && typeof body.appName !== "string") {
|
|
4347
4435
|
return c.json({ error: "appName must be a string" }, 400);
|
|
4348
4436
|
}
|
|
@@ -4459,13 +4547,21 @@ sharePromptRoutes.post("/share-prompt/dismiss", (c) => {
|
|
|
4459
4547
|
return c.json({ ok: true });
|
|
4460
4548
|
});
|
|
4461
4549
|
sharePromptRoutes.post("/share-prompt/tick", async (c) => {
|
|
4462
|
-
const
|
|
4550
|
+
const raw = await c.req.json();
|
|
4551
|
+
if (typeof raw !== "object" || raw === null) {
|
|
4552
|
+
return c.json({ error: "body must be a JSON object" }, 400);
|
|
4553
|
+
}
|
|
4554
|
+
const body = raw;
|
|
4555
|
+
if (typeof body.sessionMs !== "number" || !Number.isFinite(body.sessionMs)) {
|
|
4556
|
+
return c.json({ error: "sessionMs must be a finite number" }, 400);
|
|
4557
|
+
}
|
|
4558
|
+
const sessionMs = body.sessionMs;
|
|
4463
4559
|
let totalOpenMs = 0;
|
|
4464
4560
|
updateGlobalConfig((config) => {
|
|
4465
4561
|
if (config.sharePrompt === void 0) config.sharePrompt = {};
|
|
4466
4562
|
const sp = config.sharePrompt;
|
|
4467
4563
|
const current = typeof sp.totalOpenMs === "number" ? sp.totalOpenMs : 0;
|
|
4468
|
-
const next = current + (
|
|
4564
|
+
const next = current + (sessionMs > 0 ? sessionMs : 0);
|
|
4469
4565
|
sp.totalOpenMs = next;
|
|
4470
4566
|
totalOpenMs = next;
|
|
4471
4567
|
});
|
|
@@ -4552,199 +4648,21 @@ channelApiRoutes.get("/claude-check", (c) => {
|
|
|
4552
4648
|
// src/routes/pages.tsx
|
|
4553
4649
|
import { readFileSync as readFileSync11 } from "fs";
|
|
4554
4650
|
import { Hono as Hono14 } from "hono";
|
|
4555
|
-
import { resolve as
|
|
4556
|
-
|
|
4557
|
-
// src/utils/escapeHtml.ts
|
|
4558
|
-
function escapeHtml(str) {
|
|
4559
|
-
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
4560
|
-
}
|
|
4561
|
-
function escapeAttr(str) {
|
|
4562
|
-
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
4563
|
-
}
|
|
4564
|
-
|
|
4565
|
-
// src/jsx-runtime.ts
|
|
4566
|
-
var SafeHtml = class {
|
|
4567
|
-
__html;
|
|
4568
|
-
constructor(html) {
|
|
4569
|
-
this.__html = html;
|
|
4570
|
-
}
|
|
4571
|
-
toString() {
|
|
4572
|
-
return this.__html;
|
|
4573
|
-
}
|
|
4574
|
-
};
|
|
4575
|
-
var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "track", "wbr"]);
|
|
4576
|
-
function renderChildren(children) {
|
|
4577
|
-
if (children == null || typeof children === "boolean") return "";
|
|
4578
|
-
if (children instanceof SafeHtml) return children.__html;
|
|
4579
|
-
if (typeof children === "string") return escapeHtml(children);
|
|
4580
|
-
if (typeof children === "number") return String(children);
|
|
4581
|
-
if (Array.isArray(children)) return children.map(renderChildren).join("");
|
|
4582
|
-
return "";
|
|
4583
|
-
}
|
|
4584
|
-
var ATTR_ALIASES = {
|
|
4585
|
-
// HTML attributes
|
|
4586
|
-
className: "class",
|
|
4587
|
-
htmlFor: "for",
|
|
4588
|
-
httpEquiv: "http-equiv",
|
|
4589
|
-
acceptCharset: "accept-charset",
|
|
4590
|
-
accessKey: "accesskey",
|
|
4591
|
-
autoCapitalize: "autocapitalize",
|
|
4592
|
-
autoComplete: "autocomplete",
|
|
4593
|
-
autoFocus: "autofocus",
|
|
4594
|
-
autoPlay: "autoplay",
|
|
4595
|
-
colSpan: "colspan",
|
|
4596
|
-
contentEditable: "contenteditable",
|
|
4597
|
-
crossOrigin: "crossorigin",
|
|
4598
|
-
dateTime: "datetime",
|
|
4599
|
-
defaultChecked: "checked",
|
|
4600
|
-
defaultValue: "value",
|
|
4601
|
-
encType: "enctype",
|
|
4602
|
-
formAction: "formaction",
|
|
4603
|
-
formEncType: "formenctype",
|
|
4604
|
-
formMethod: "formmethod",
|
|
4605
|
-
formNoValidate: "formnovalidate",
|
|
4606
|
-
formTarget: "formtarget",
|
|
4607
|
-
hrefLang: "hreflang",
|
|
4608
|
-
inputMode: "inputmode",
|
|
4609
|
-
maxLength: "maxlength",
|
|
4610
|
-
minLength: "minlength",
|
|
4611
|
-
noModule: "nomodule",
|
|
4612
|
-
noValidate: "novalidate",
|
|
4613
|
-
readOnly: "readonly",
|
|
4614
|
-
referrerPolicy: "referrerpolicy",
|
|
4615
|
-
rowSpan: "rowspan",
|
|
4616
|
-
spellCheck: "spellcheck",
|
|
4617
|
-
srcDoc: "srcdoc",
|
|
4618
|
-
srcLang: "srclang",
|
|
4619
|
-
srcSet: "srcset",
|
|
4620
|
-
tabIndex: "tabindex",
|
|
4621
|
-
useMap: "usemap",
|
|
4622
|
-
// SVG presentation attributes (camelCase → kebab-case)
|
|
4623
|
-
strokeWidth: "stroke-width",
|
|
4624
|
-
strokeLinecap: "stroke-linecap",
|
|
4625
|
-
strokeLinejoin: "stroke-linejoin",
|
|
4626
|
-
strokeDasharray: "stroke-dasharray",
|
|
4627
|
-
strokeDashoffset: "stroke-dashoffset",
|
|
4628
|
-
strokeMiterlimit: "stroke-miterlimit",
|
|
4629
|
-
strokeOpacity: "stroke-opacity",
|
|
4630
|
-
fillOpacity: "fill-opacity",
|
|
4631
|
-
fillRule: "fill-rule",
|
|
4632
|
-
clipPath: "clip-path",
|
|
4633
|
-
clipRule: "clip-rule",
|
|
4634
|
-
colorInterpolation: "color-interpolation",
|
|
4635
|
-
colorInterpolationFilters: "color-interpolation-filters",
|
|
4636
|
-
floodColor: "flood-color",
|
|
4637
|
-
floodOpacity: "flood-opacity",
|
|
4638
|
-
lightingColor: "lighting-color",
|
|
4639
|
-
stopColor: "stop-color",
|
|
4640
|
-
stopOpacity: "stop-opacity",
|
|
4641
|
-
shapeRendering: "shape-rendering",
|
|
4642
|
-
imageRendering: "image-rendering",
|
|
4643
|
-
textRendering: "text-rendering",
|
|
4644
|
-
pointerEvents: "pointer-events",
|
|
4645
|
-
vectorEffect: "vector-effect",
|
|
4646
|
-
paintOrder: "paint-order",
|
|
4647
|
-
// SVG text/font attributes
|
|
4648
|
-
fontFamily: "font-family",
|
|
4649
|
-
fontSize: "font-size",
|
|
4650
|
-
fontStyle: "font-style",
|
|
4651
|
-
fontVariant: "font-variant",
|
|
4652
|
-
fontWeight: "font-weight",
|
|
4653
|
-
fontStretch: "font-stretch",
|
|
4654
|
-
textAnchor: "text-anchor",
|
|
4655
|
-
textDecoration: "text-decoration",
|
|
4656
|
-
dominantBaseline: "dominant-baseline",
|
|
4657
|
-
alignmentBaseline: "alignment-baseline",
|
|
4658
|
-
baselineShift: "baseline-shift",
|
|
4659
|
-
letterSpacing: "letter-spacing",
|
|
4660
|
-
wordSpacing: "word-spacing",
|
|
4661
|
-
writingMode: "writing-mode",
|
|
4662
|
-
glyphOrientationHorizontal: "glyph-orientation-horizontal",
|
|
4663
|
-
glyphOrientationVertical: "glyph-orientation-vertical",
|
|
4664
|
-
// SVG marker/gradient/filter attributes
|
|
4665
|
-
markerStart: "marker-start",
|
|
4666
|
-
markerMid: "marker-mid",
|
|
4667
|
-
markerEnd: "marker-end",
|
|
4668
|
-
gradientUnits: "gradientUnits",
|
|
4669
|
-
gradientTransform: "gradientTransform",
|
|
4670
|
-
spreadMethod: "spreadMethod",
|
|
4671
|
-
patternUnits: "patternUnits",
|
|
4672
|
-
patternContentUnits: "patternContentUnits",
|
|
4673
|
-
patternTransform: "patternTransform",
|
|
4674
|
-
maskUnits: "maskUnits",
|
|
4675
|
-
maskContentUnits: "maskContentUnits",
|
|
4676
|
-
filterUnits: "filterUnits",
|
|
4677
|
-
primitiveUnits: "primitiveUnits",
|
|
4678
|
-
clipPathUnits: "clipPathUnits",
|
|
4679
|
-
// SVG xlink (legacy but still used)
|
|
4680
|
-
xlinkHref: "xlink:href",
|
|
4681
|
-
xlinkShow: "xlink:show",
|
|
4682
|
-
xlinkActuate: "xlink:actuate",
|
|
4683
|
-
xlinkType: "xlink:type",
|
|
4684
|
-
xlinkRole: "xlink:role",
|
|
4685
|
-
xlinkTitle: "xlink:title",
|
|
4686
|
-
xlinkArcrole: "xlink:arcrole",
|
|
4687
|
-
xmlBase: "xml:base",
|
|
4688
|
-
xmlLang: "xml:lang",
|
|
4689
|
-
xmlSpace: "xml:space",
|
|
4690
|
-
xmlns: "xmlns",
|
|
4691
|
-
xmlnsXlink: "xmlns:xlink",
|
|
4692
|
-
// SVG filter primitive attributes
|
|
4693
|
-
stdDeviation: "stdDeviation",
|
|
4694
|
-
baseFrequency: "baseFrequency",
|
|
4695
|
-
numOctaves: "numOctaves",
|
|
4696
|
-
kernelMatrix: "kernelMatrix",
|
|
4697
|
-
surfaceScale: "surfaceScale",
|
|
4698
|
-
specularConstant: "specularConstant",
|
|
4699
|
-
specularExponent: "specularExponent",
|
|
4700
|
-
diffuseConstant: "diffuseConstant",
|
|
4701
|
-
pointsAtX: "pointsAtX",
|
|
4702
|
-
pointsAtY: "pointsAtY",
|
|
4703
|
-
pointsAtZ: "pointsAtZ",
|
|
4704
|
-
limitingConeAngle: "limitingConeAngle",
|
|
4705
|
-
tableValues: "tableValues"
|
|
4706
|
-
// viewBox, preserveAspectRatio stay as-is (already correct casing)
|
|
4707
|
-
};
|
|
4708
|
-
function renderAttr(key, value) {
|
|
4709
|
-
const name = ATTR_ALIASES[key] ?? key;
|
|
4710
|
-
if (value == null || value === false) return "";
|
|
4711
|
-
if (value === true) return ` ${name}`;
|
|
4712
|
-
let strValue;
|
|
4713
|
-
if (value instanceof SafeHtml) {
|
|
4714
|
-
strValue = value.__html;
|
|
4715
|
-
} else if (typeof value === "number") {
|
|
4716
|
-
strValue = String(value);
|
|
4717
|
-
} else if (typeof value === "string") {
|
|
4718
|
-
strValue = escapeAttr(value);
|
|
4719
|
-
} else {
|
|
4720
|
-
strValue = "";
|
|
4721
|
-
}
|
|
4722
|
-
return ` ${name}="${strValue}"`;
|
|
4723
|
-
}
|
|
4724
|
-
function jsx(tag, props) {
|
|
4725
|
-
if (typeof tag === "function") return tag(props);
|
|
4726
|
-
const { children, ...attrs } = props;
|
|
4727
|
-
const attrStr = Object.entries(attrs).map(([k, v]) => renderAttr(k, v)).join("");
|
|
4728
|
-
if (VOID_TAGS.has(tag)) return new SafeHtml(`<${tag}${attrStr}>`);
|
|
4729
|
-
const childStr = children != null ? renderChildren(children) : "";
|
|
4730
|
-
return new SafeHtml(`<${tag}${attrStr}>${childStr}</${tag}>`);
|
|
4731
|
-
}
|
|
4732
|
-
function Fragment({ children }) {
|
|
4733
|
-
return new SafeHtml(children != null ? renderChildren(children) : "");
|
|
4734
|
-
}
|
|
4651
|
+
import { resolve as resolve7 } from "path";
|
|
4735
4652
|
|
|
4736
4653
|
// src/icons.tsx
|
|
4654
|
+
import { jsx, jsxs } from "kerfjs/jsx-runtime";
|
|
4737
4655
|
var S14 = { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" };
|
|
4738
4656
|
var S12 = { ...S14, width: "12", height: "12" };
|
|
4739
4657
|
var S16 = { ...S14, width: "16", height: "16" };
|
|
4740
4658
|
function IconEdit() {
|
|
4741
|
-
return /* @__PURE__ */
|
|
4659
|
+
return /* @__PURE__ */ jsxs("svg", { ...S14, children: [
|
|
4742
4660
|
/* @__PURE__ */ jsx("path", { d: "M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }),
|
|
4743
4661
|
/* @__PURE__ */ jsx("path", { d: "m15 5 4 4" })
|
|
4744
4662
|
] });
|
|
4745
4663
|
}
|
|
4746
4664
|
function IconTrash() {
|
|
4747
|
-
return /* @__PURE__ */
|
|
4665
|
+
return /* @__PURE__ */ jsxs("svg", { ...S14, children: [
|
|
4748
4666
|
/* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
|
|
4749
4667
|
/* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
4750
4668
|
/* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
|
|
@@ -4753,7 +4671,7 @@ function IconTrash() {
|
|
|
4753
4671
|
] });
|
|
4754
4672
|
}
|
|
4755
4673
|
function IconTrash16() {
|
|
4756
|
-
return /* @__PURE__ */
|
|
4674
|
+
return /* @__PURE__ */ jsxs("svg", { ...S16, children: [
|
|
4757
4675
|
/* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
|
|
4758
4676
|
/* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
4759
4677
|
/* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
|
|
@@ -4762,7 +4680,7 @@ function IconTrash16() {
|
|
|
4762
4680
|
] });
|
|
4763
4681
|
}
|
|
4764
4682
|
function IconReveal() {
|
|
4765
|
-
return /* @__PURE__ */
|
|
4683
|
+
return /* @__PURE__ */ jsxs("svg", { ...S12, children: [
|
|
4766
4684
|
/* @__PURE__ */ jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" }),
|
|
4767
4685
|
/* @__PURE__ */ jsx("line", { x1: "12", y1: "11", x2: "12", y2: "17" }),
|
|
4768
4686
|
/* @__PURE__ */ jsx("polyline", { points: "9 14 12 11 15 14" })
|
|
@@ -4772,13 +4690,13 @@ function IconZoomOut() {
|
|
|
4772
4690
|
return /* @__PURE__ */ jsx("svg", { ...S14, children: /* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) });
|
|
4773
4691
|
}
|
|
4774
4692
|
function IconZoomIn() {
|
|
4775
|
-
return /* @__PURE__ */
|
|
4693
|
+
return /* @__PURE__ */ jsxs("svg", { ...S14, children: [
|
|
4776
4694
|
/* @__PURE__ */ jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
|
|
4777
4695
|
/* @__PURE__ */ jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
|
|
4778
4696
|
] });
|
|
4779
4697
|
}
|
|
4780
4698
|
function IconFit() {
|
|
4781
|
-
return /* @__PURE__ */
|
|
4699
|
+
return /* @__PURE__ */ jsxs("svg", { ...S14, children: [
|
|
4782
4700
|
/* @__PURE__ */ jsx("path", { d: "M15 3h6v6" }),
|
|
4783
4701
|
/* @__PURE__ */ jsx("path", { d: "M9 21H3v-6" }),
|
|
4784
4702
|
/* @__PURE__ */ jsx("path", { d: "M21 3l-7 7" }),
|
|
@@ -4786,7 +4704,7 @@ function IconFit() {
|
|
|
4786
4704
|
] });
|
|
4787
4705
|
}
|
|
4788
4706
|
function IconActualSize() {
|
|
4789
|
-
return /* @__PURE__ */
|
|
4707
|
+
return /* @__PURE__ */ jsxs("svg", { ...S14, children: [
|
|
4790
4708
|
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
4791
4709
|
/* @__PURE__ */ jsx("text", { x: "12", y: "15.5", textAnchor: "middle", fontSize: "9", fontWeight: "bold", fill: "currentColor", stroke: "none", children: "1:1" })
|
|
4792
4710
|
] });
|
|
@@ -4851,6 +4769,7 @@ function buildSegments(str, commonPositions) {
|
|
|
4851
4769
|
}
|
|
4852
4770
|
|
|
4853
4771
|
// src/components/imageDiff.tsx
|
|
4772
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "kerfjs/jsx-runtime";
|
|
4854
4773
|
function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
4855
4774
|
const fileId = file.id;
|
|
4856
4775
|
const isAdded = diff.status === "added";
|
|
@@ -4858,7 +4777,7 @@ function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
|
4858
4777
|
const hasOld = !isAdded;
|
|
4859
4778
|
const hasNew = !isDeleted;
|
|
4860
4779
|
const hasComparison = hasOld && hasNew;
|
|
4861
|
-
return /* @__PURE__ */
|
|
4780
|
+
return /* @__PURE__ */ jsxs2(
|
|
4862
4781
|
"div",
|
|
4863
4782
|
{
|
|
4864
4783
|
className: "image-diff",
|
|
@@ -4869,22 +4788,22 @@ function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
|
4869
4788
|
...baseWidth !== void 0 ? { "data-base-width": String(baseWidth) } : {},
|
|
4870
4789
|
...baseHeight !== void 0 ? { "data-base-height": String(baseHeight) } : {},
|
|
4871
4790
|
children: [
|
|
4872
|
-
fontWarning === true && /* @__PURE__ */
|
|
4873
|
-
/* @__PURE__ */
|
|
4874
|
-
hasComparison && /* @__PURE__ */
|
|
4875
|
-
/* @__PURE__ */
|
|
4876
|
-
/* @__PURE__ */
|
|
4791
|
+
fontWarning === true && /* @__PURE__ */ jsx2("div", { className: "image-font-warning", children: "This SVG uses text that may render differently depending on locally installed fonts." }),
|
|
4792
|
+
/* @__PURE__ */ jsx2("div", { className: "image-diff-panel image-diff-metadata", "data-panel": "metadata", children: /* @__PURE__ */ jsx2("div", { className: "image-metadata-loading", children: "Loading metadata..." }) }),
|
|
4793
|
+
hasComparison && /* @__PURE__ */ jsx2("div", { className: "image-diff-panel image-diff-visual", "data-panel": "difference", children: /* @__PURE__ */ jsx2("div", { className: "image-visual-canvas", "data-zoomable": "true", children: /* @__PURE__ */ jsxs2("div", { className: "image-zoom-wrap", children: [
|
|
4794
|
+
/* @__PURE__ */ jsx2("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
4795
|
+
/* @__PURE__ */ jsx2("img", { className: "image-layer image-layer-new image-blend", src: `/api/image/${fileId}/new`, alt: "New version" })
|
|
4877
4796
|
] }) }) }),
|
|
4878
|
-
hasComparison && /* @__PURE__ */
|
|
4879
|
-
/* @__PURE__ */
|
|
4880
|
-
/* @__PURE__ */
|
|
4881
|
-
/* @__PURE__ */
|
|
4797
|
+
hasComparison && /* @__PURE__ */ jsx2("div", { className: "image-diff-panel image-diff-visual", "data-panel": "slice", children: /* @__PURE__ */ jsxs2("div", { className: "image-visual-canvas", "data-zoomable": "true", children: [
|
|
4798
|
+
/* @__PURE__ */ jsxs2("div", { className: "image-zoom-wrap", children: [
|
|
4799
|
+
/* @__PURE__ */ jsx2("img", { className: "image-layer image-layer-old", src: `/api/image/${fileId}/old`, alt: "Old version" }),
|
|
4800
|
+
/* @__PURE__ */ jsx2("img", { className: "image-layer image-layer-new image-slice-clipped", src: `/api/image/${fileId}/new`, alt: "New version" })
|
|
4882
4801
|
] }),
|
|
4883
|
-
/* @__PURE__ */
|
|
4884
|
-
/* @__PURE__ */
|
|
4885
|
-
/* @__PURE__ */
|
|
4802
|
+
/* @__PURE__ */ jsx2("div", { className: "slice-line" }),
|
|
4803
|
+
/* @__PURE__ */ jsx2("div", { className: "slice-handle slice-handle-a" }),
|
|
4804
|
+
/* @__PURE__ */ jsx2("div", { className: "slice-handle slice-handle-b" })
|
|
4886
4805
|
] }) }),
|
|
4887
|
-
!hasComparison && /* @__PURE__ */
|
|
4806
|
+
!hasComparison && /* @__PURE__ */ jsx2("div", { className: "image-diff-panel image-diff-visual", "data-panel": "image", children: /* @__PURE__ */ jsx2("div", { className: "image-visual-canvas", "data-zoomable": "true", children: /* @__PURE__ */ jsx2("div", { className: "image-zoom-wrap", children: /* @__PURE__ */ jsx2(
|
|
4888
4807
|
"img",
|
|
4889
4808
|
{
|
|
4890
4809
|
className: "image-layer image-layer-old",
|
|
@@ -4898,6 +4817,7 @@ function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
|
|
|
4898
4817
|
}
|
|
4899
4818
|
|
|
4900
4819
|
// src/components/diffView.tsx
|
|
4820
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "kerfjs/jsx-runtime";
|
|
4901
4821
|
function DiffView({ file, diff, annotations, mode }) {
|
|
4902
4822
|
const annotationsByLine = {};
|
|
4903
4823
|
for (const a of annotations) {
|
|
@@ -4905,7 +4825,7 @@ function DiffView({ file, diff, annotations, mode }) {
|
|
|
4905
4825
|
if (!(key in annotationsByLine)) annotationsByLine[key] = [];
|
|
4906
4826
|
annotationsByLine[key].push(a);
|
|
4907
4827
|
}
|
|
4908
|
-
return /* @__PURE__ */
|
|
4828
|
+
return /* @__PURE__ */ jsxs3(
|
|
4909
4829
|
"div",
|
|
4910
4830
|
{
|
|
4911
4831
|
className: "diff-view",
|
|
@@ -4913,14 +4833,14 @@ function DiffView({ file, diff, annotations, mode }) {
|
|
|
4913
4833
|
"data-file-path": file.file_path,
|
|
4914
4834
|
...isSvgFile(diff.filePath) ? { "data-is-svg": "true" } : {},
|
|
4915
4835
|
children: [
|
|
4916
|
-
/* @__PURE__ */
|
|
4917
|
-
/* @__PURE__ */
|
|
4918
|
-
/* @__PURE__ */
|
|
4919
|
-
/* @__PURE__ */
|
|
4836
|
+
/* @__PURE__ */ jsxs3("div", { className: "diff-header", children: [
|
|
4837
|
+
/* @__PURE__ */ jsxs3("div", { className: "diff-header-file", children: [
|
|
4838
|
+
/* @__PURE__ */ jsx3("span", { className: "file-path", children: diff.filePath }),
|
|
4839
|
+
/* @__PURE__ */ jsx3("button", { className: "reveal-btn", "data-file-id": file.id, title: "Reveal in file manager", children: /* @__PURE__ */ jsx3(IconReveal, {}) })
|
|
4920
4840
|
] }),
|
|
4921
|
-
/* @__PURE__ */
|
|
4841
|
+
/* @__PURE__ */ jsx3("div", { className: "diff-header-actions", children: /* @__PURE__ */ jsx3("span", { className: `file-status ${diff.status}`, children: diff.status }) })
|
|
4922
4842
|
] }),
|
|
4923
|
-
diff.isBinary && isImageFile(diff.filePath) ? /* @__PURE__ */
|
|
4843
|
+
diff.isBinary && isImageFile(diff.filePath) ? /* @__PURE__ */ jsx3(ImageDiff, { file, diff }) : diff.isBinary ? /* @__PURE__ */ jsx3("div", { className: "hunk-separator", children: "Binary file" }) : diff.status === "added" || diff.status === "deleted" || mode === "unified" ? /* @__PURE__ */ jsx3(UnifiedDiff, { hunks: diff.hunks, annotationsByLine }) : /* @__PURE__ */ jsx3(SplitDiff, { hunks: diff.hunks, annotationsByLine })
|
|
4924
4844
|
]
|
|
4925
4845
|
}
|
|
4926
4846
|
);
|
|
@@ -4964,11 +4884,11 @@ function SplitDiff({ hunks, annotationsByLine }) {
|
|
|
4964
4884
|
}
|
|
4965
4885
|
}
|
|
4966
4886
|
if (run.length > 0) groups.push({ type: "columns", items: run });
|
|
4967
|
-
return /* @__PURE__ */
|
|
4887
|
+
return /* @__PURE__ */ jsx3("div", { className: "diff-table-split", children: groups.map((group) => {
|
|
4968
4888
|
if (group.type === "annotated") {
|
|
4969
|
-
return /* @__PURE__ */
|
|
4970
|
-
/* @__PURE__ */
|
|
4971
|
-
/* @__PURE__ */
|
|
4889
|
+
return /* @__PURE__ */ jsxs3("div", { children: [
|
|
4890
|
+
/* @__PURE__ */ jsxs3("div", { className: "split-row", children: [
|
|
4891
|
+
/* @__PURE__ */ jsxs3(
|
|
4972
4892
|
"div",
|
|
4973
4893
|
{
|
|
4974
4894
|
className: `diff-line split-left ${group.pair.left?.type || "empty"}`,
|
|
@@ -4976,124 +4896,97 @@ function SplitDiff({ hunks, annotationsByLine }) {
|
|
|
4976
4896
|
"data-side": "old",
|
|
4977
4897
|
"data-new-line": group.pair.left?.newNum ?? group.pair.right?.newNum ?? "",
|
|
4978
4898
|
children: [
|
|
4979
|
-
/* @__PURE__ */
|
|
4980
|
-
/* @__PURE__ */
|
|
4899
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter", "data-line-number": group.pair.left?.oldNum ?? "" }),
|
|
4900
|
+
/* @__PURE__ */ jsx3("span", { className: "code", children: renderPairContent(group.pair, "left") })
|
|
4981
4901
|
]
|
|
4982
4902
|
}
|
|
4983
4903
|
),
|
|
4984
|
-
/* @__PURE__ */
|
|
4904
|
+
/* @__PURE__ */ jsxs3(
|
|
4985
4905
|
"div",
|
|
4986
4906
|
{
|
|
4987
4907
|
className: `diff-line split-right ${group.pair.right?.type || "empty"}`,
|
|
4988
4908
|
"data-line": group.pair.right?.newNum ?? "",
|
|
4989
4909
|
"data-side": "new",
|
|
4990
4910
|
children: [
|
|
4991
|
-
/* @__PURE__ */
|
|
4992
|
-
/* @__PURE__ */
|
|
4911
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter", "data-line-number": group.pair.right?.newNum ?? "" }),
|
|
4912
|
+
/* @__PURE__ */ jsx3("span", { className: "code", children: renderPairContent(group.pair, "right") })
|
|
4993
4913
|
]
|
|
4994
4914
|
}
|
|
4995
4915
|
)
|
|
4996
4916
|
] }),
|
|
4997
|
-
/* @__PURE__ */
|
|
4917
|
+
/* @__PURE__ */ jsx3(AnnotationRows, { annotations: group.annotations })
|
|
4998
4918
|
] });
|
|
4999
4919
|
}
|
|
5000
|
-
return /* @__PURE__ */
|
|
5001
|
-
/* @__PURE__ */
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
const { pair } = item;
|
|
5033
|
-
return /* @__PURE__ */ jsx(
|
|
5034
|
-
"div",
|
|
5035
|
-
{
|
|
5036
|
-
className: `diff-line split-left ${pair.left?.type || "empty"}`,
|
|
5037
|
-
"data-line": pair.left?.oldNum ?? "",
|
|
5038
|
-
"data-side": "old",
|
|
5039
|
-
"data-new-line": pair.left?.newNum ?? pair.right?.newNum ?? "",
|
|
5040
|
-
children: [
|
|
5041
|
-
/* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": pair.left?.oldNum ?? "" }),
|
|
5042
|
-
/* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(pair, "left") })
|
|
5043
|
-
]
|
|
5044
|
-
}
|
|
5045
|
-
);
|
|
5046
|
-
}) }),
|
|
5047
|
-
/* @__PURE__ */ jsx("div", { className: "split-col split-col-right", children: group.items.map((item) => {
|
|
5048
|
-
if (item.kind === "separator") {
|
|
5049
|
-
const { hunk, hunkIdx, gapStart, gapEnd } = item;
|
|
5050
|
-
return /* @__PURE__ */ jsx(
|
|
5051
|
-
"div",
|
|
5052
|
-
{
|
|
5053
|
-
className: "hunk-separator",
|
|
5054
|
-
"data-hunk-idx": hunkIdx,
|
|
5055
|
-
"data-old-start": hunk.oldStart,
|
|
5056
|
-
"data-old-count": hunk.oldCount,
|
|
5057
|
-
"data-new-start": hunk.newStart,
|
|
5058
|
-
"data-new-count": hunk.newCount,
|
|
5059
|
-
"data-gap-start": gapStart,
|
|
5060
|
-
"data-gap-end": gapEnd,
|
|
5061
|
-
children: [
|
|
5062
|
-
"@@ -",
|
|
5063
|
-
hunk.oldStart,
|
|
5064
|
-
",",
|
|
5065
|
-
hunk.oldCount,
|
|
5066
|
-
" +",
|
|
5067
|
-
hunk.newStart,
|
|
5068
|
-
",",
|
|
5069
|
-
hunk.newCount,
|
|
5070
|
-
" @@"
|
|
5071
|
-
]
|
|
5072
|
-
}
|
|
5073
|
-
);
|
|
4920
|
+
return /* @__PURE__ */ jsxs3("div", { className: "split-columns", children: [
|
|
4921
|
+
/* @__PURE__ */ jsx3("div", { className: "split-col split-col-left", children: renderSplitColumn(group.items, "left") }),
|
|
4922
|
+
/* @__PURE__ */ jsx3("div", { className: "split-col split-col-right", children: renderSplitColumn(group.items, "right") })
|
|
4923
|
+
] });
|
|
4924
|
+
}) });
|
|
4925
|
+
}
|
|
4926
|
+
function renderSplitColumn(items, side) {
|
|
4927
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: items.map((item) => {
|
|
4928
|
+
if (item.kind === "separator") {
|
|
4929
|
+
const { hunk, hunkIdx, gapStart, gapEnd } = item;
|
|
4930
|
+
return /* @__PURE__ */ jsxs3(
|
|
4931
|
+
"div",
|
|
4932
|
+
{
|
|
4933
|
+
className: "hunk-separator",
|
|
4934
|
+
"data-hunk-idx": hunkIdx,
|
|
4935
|
+
"data-old-start": hunk.oldStart,
|
|
4936
|
+
"data-old-count": hunk.oldCount,
|
|
4937
|
+
"data-new-start": hunk.newStart,
|
|
4938
|
+
"data-new-count": hunk.newCount,
|
|
4939
|
+
"data-gap-start": gapStart,
|
|
4940
|
+
"data-gap-end": gapEnd,
|
|
4941
|
+
children: [
|
|
4942
|
+
"@@ -",
|
|
4943
|
+
hunk.oldStart,
|
|
4944
|
+
",",
|
|
4945
|
+
hunk.oldCount,
|
|
4946
|
+
" +",
|
|
4947
|
+
hunk.newStart,
|
|
4948
|
+
",",
|
|
4949
|
+
hunk.newCount,
|
|
4950
|
+
" @@"
|
|
4951
|
+
]
|
|
5074
4952
|
}
|
|
5075
|
-
|
|
5076
|
-
|
|
4953
|
+
);
|
|
4954
|
+
}
|
|
4955
|
+
if (item.kind === "tail") {
|
|
4956
|
+
return /* @__PURE__ */ jsx3("div", { className: "hunk-separator hunk-expander-tail", "data-start": item.start, children: "\u2195 Show remaining lines" });
|
|
4957
|
+
}
|
|
4958
|
+
const { pair } = item;
|
|
4959
|
+
if (side === "left") {
|
|
4960
|
+
return /* @__PURE__ */ jsxs3(
|
|
4961
|
+
"div",
|
|
4962
|
+
{
|
|
4963
|
+
className: `diff-line split-left ${pair.left?.type || "empty"}`,
|
|
4964
|
+
"data-line": pair.left?.oldNum ?? "",
|
|
4965
|
+
"data-side": "old",
|
|
4966
|
+
"data-new-line": pair.left?.newNum ?? pair.right?.newNum ?? "",
|
|
4967
|
+
children: [
|
|
4968
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter", "data-line-number": pair.left?.oldNum ?? "" }),
|
|
4969
|
+
/* @__PURE__ */ jsx3("span", { className: "code", children: renderPairContent(pair, "left") })
|
|
4970
|
+
]
|
|
5077
4971
|
}
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
] });
|
|
4972
|
+
);
|
|
4973
|
+
}
|
|
4974
|
+
return /* @__PURE__ */ jsxs3(
|
|
4975
|
+
"div",
|
|
4976
|
+
{
|
|
4977
|
+
className: `diff-line split-right ${pair.right?.type || "empty"}`,
|
|
4978
|
+
"data-line": pair.right?.newNum ?? "",
|
|
4979
|
+
"data-side": "new",
|
|
4980
|
+
children: [
|
|
4981
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter", "data-line-number": pair.right?.newNum ?? "" }),
|
|
4982
|
+
/* @__PURE__ */ jsx3("span", { className: "code", children: renderPairContent(pair, "right") })
|
|
4983
|
+
]
|
|
4984
|
+
}
|
|
4985
|
+
);
|
|
5093
4986
|
}) });
|
|
5094
4987
|
}
|
|
5095
4988
|
function renderSegments(segments) {
|
|
5096
|
-
return /* @__PURE__ */
|
|
4989
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: segments.map((s) => s.changed ? /* @__PURE__ */ jsx3("span", { className: "char-change", children: s.text }) : /* @__PURE__ */ jsx3(Fragment, { children: s.text })) });
|
|
5097
4990
|
}
|
|
5098
4991
|
function renderPairContent(pair, side) {
|
|
5099
4992
|
const line = side === "left" ? pair.left : pair.right;
|
|
@@ -5171,11 +5064,11 @@ function buildUnifiedCharDiffs(lines) {
|
|
|
5171
5064
|
function UnifiedDiff({ hunks, annotationsByLine }) {
|
|
5172
5065
|
const lastHunk = hunks[hunks.length - 1];
|
|
5173
5066
|
const tailStart = lastHunk ? lastHunk.newStart + lastHunk.newCount : 1;
|
|
5174
|
-
return /* @__PURE__ */
|
|
5067
|
+
return /* @__PURE__ */ jsxs3("div", { className: "diff-table-unified", children: [
|
|
5175
5068
|
hunks.map((hunk, hunkIdx) => {
|
|
5176
5069
|
const charDiffs = buildUnifiedCharDiffs(hunk.lines);
|
|
5177
|
-
return /* @__PURE__ */
|
|
5178
|
-
/* @__PURE__ */
|
|
5070
|
+
return /* @__PURE__ */ jsxs3("div", { className: "hunk-block", children: [
|
|
5071
|
+
/* @__PURE__ */ jsxs3(
|
|
5179
5072
|
"div",
|
|
5180
5073
|
{
|
|
5181
5074
|
className: "hunk-separator",
|
|
@@ -5202,43 +5095,44 @@ function UnifiedDiff({ hunks, annotationsByLine }) {
|
|
|
5202
5095
|
const side = line.type === "remove" ? "old" : "new";
|
|
5203
5096
|
const anns = annotationsByLine[`${lineNum}:${side}`] ?? [];
|
|
5204
5097
|
const segments = charDiffs.get(line);
|
|
5205
|
-
return /* @__PURE__ */
|
|
5206
|
-
/* @__PURE__ */
|
|
5098
|
+
return /* @__PURE__ */ jsxs3("div", { children: [
|
|
5099
|
+
/* @__PURE__ */ jsxs3(
|
|
5207
5100
|
"div",
|
|
5208
5101
|
{
|
|
5209
5102
|
className: `diff-line ${line.type}${anns.length ? " has-annotation" : ""}`,
|
|
5210
5103
|
"data-line": lineNum,
|
|
5211
5104
|
"data-side": side,
|
|
5212
5105
|
children: [
|
|
5213
|
-
/* @__PURE__ */
|
|
5214
|
-
/* @__PURE__ */
|
|
5215
|
-
/* @__PURE__ */
|
|
5106
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter-old", "data-line-number": line.oldNum ?? "" }),
|
|
5107
|
+
/* @__PURE__ */ jsx3("span", { className: "gutter-new", "data-line-number": line.newNum ?? "" }),
|
|
5108
|
+
/* @__PURE__ */ jsx3("span", { className: "code", children: segments ? renderSegments(segments) : line.content })
|
|
5216
5109
|
]
|
|
5217
5110
|
}
|
|
5218
5111
|
),
|
|
5219
|
-
anns.length > 0 ? /* @__PURE__ */
|
|
5112
|
+
anns.length > 0 ? /* @__PURE__ */ jsx3(AnnotationRows, { annotations: anns }) : null
|
|
5220
5113
|
] });
|
|
5221
5114
|
})
|
|
5222
5115
|
] });
|
|
5223
5116
|
}),
|
|
5224
|
-
/* @__PURE__ */
|
|
5117
|
+
/* @__PURE__ */ jsx3("div", { className: "hunk-separator hunk-expander-tail", "data-start": tailStart, children: "\u2195 Show remaining lines" })
|
|
5225
5118
|
] });
|
|
5226
5119
|
}
|
|
5227
5120
|
function AnnotationRows({ annotations }) {
|
|
5228
|
-
return /* @__PURE__ */
|
|
5121
|
+
return /* @__PURE__ */ jsx3("div", { className: "annotation-row", children: annotations.map((a) => /* @__PURE__ */ jsxs3(
|
|
5229
5122
|
"div",
|
|
5230
5123
|
{
|
|
5231
5124
|
className: `annotation-item${a.is_stale ? " annotation-stale" : ""}`,
|
|
5125
|
+
"data-key": a.id,
|
|
5232
5126
|
"data-annotation-id": a.id,
|
|
5233
5127
|
"data-is-stale": a.is_stale ? "true" : void 0,
|
|
5234
5128
|
children: [
|
|
5235
|
-
/* @__PURE__ */
|
|
5236
|
-
/* @__PURE__ */
|
|
5237
|
-
/* @__PURE__ */
|
|
5238
|
-
/* @__PURE__ */
|
|
5239
|
-
a.is_stale ? /* @__PURE__ */
|
|
5240
|
-
/* @__PURE__ */
|
|
5241
|
-
/* @__PURE__ */
|
|
5129
|
+
/* @__PURE__ */ jsx3("span", { className: "annotation-drag-handle", draggable: true, title: "Drag to move", children: "\u283F" }),
|
|
5130
|
+
/* @__PURE__ */ jsx3("span", { className: `annotation-category category-${a.category}`, "data-action": "reclassify", children: a.category }),
|
|
5131
|
+
/* @__PURE__ */ jsx3("span", { className: "annotation-text", children: a.content }),
|
|
5132
|
+
/* @__PURE__ */ jsxs3("div", { className: "annotation-actions", children: [
|
|
5133
|
+
a.is_stale ? /* @__PURE__ */ jsx3("button", { className: "btn btn-xs btn-keep", "data-action": "keep", children: "Keep" }) : null,
|
|
5134
|
+
/* @__PURE__ */ jsx3("button", { className: "btn btn-xs btn-icon", "data-action": "edit", title: "Edit", children: /* @__PURE__ */ jsx3(IconEdit, {}) }),
|
|
5135
|
+
/* @__PURE__ */ jsx3("button", { className: "btn btn-xs btn-icon btn-danger", "data-action": "delete", title: "Delete", children: /* @__PURE__ */ jsx3(IconTrash, {}) })
|
|
5242
5136
|
] })
|
|
5243
5137
|
]
|
|
5244
5138
|
}
|
|
@@ -5788,25 +5682,27 @@ function generateThemeId() {
|
|
|
5788
5682
|
}
|
|
5789
5683
|
|
|
5790
5684
|
// src/components/layout.tsx
|
|
5685
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "kerfjs/jsx-runtime";
|
|
5791
5686
|
function Layout({ title, reviewId, children }) {
|
|
5792
5687
|
const themeId = getActiveThemeId();
|
|
5793
5688
|
const themeColors = getActiveThemeColors();
|
|
5794
5689
|
const themeStyle = themeToInlineStyle(themeColors);
|
|
5795
|
-
return /* @__PURE__ */
|
|
5796
|
-
/* @__PURE__ */
|
|
5797
|
-
/* @__PURE__ */
|
|
5798
|
-
/* @__PURE__ */
|
|
5799
|
-
/* @__PURE__ */
|
|
5800
|
-
/* @__PURE__ */
|
|
5690
|
+
return /* @__PURE__ */ jsxs4("html", { lang: "en", style: themeStyle, "data-theme": themeId, children: [
|
|
5691
|
+
/* @__PURE__ */ jsxs4("head", { children: [
|
|
5692
|
+
/* @__PURE__ */ jsx4("meta", { charSet: "utf-8" }),
|
|
5693
|
+
/* @__PURE__ */ jsx4("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
5694
|
+
/* @__PURE__ */ jsx4("title", { children: title }),
|
|
5695
|
+
/* @__PURE__ */ jsx4("link", { rel: "stylesheet", href: "/static/styles.css" })
|
|
5801
5696
|
] }),
|
|
5802
|
-
/* @__PURE__ */
|
|
5697
|
+
/* @__PURE__ */ jsxs4("body", { "data-review-id": reviewId, children: [
|
|
5803
5698
|
children,
|
|
5804
|
-
/* @__PURE__ */
|
|
5699
|
+
/* @__PURE__ */ jsx4("script", { src: "/static/app.js" })
|
|
5805
5700
|
] })
|
|
5806
5701
|
] });
|
|
5807
5702
|
}
|
|
5808
5703
|
|
|
5809
5704
|
// src/components/reviewHistory.tsx
|
|
5705
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "kerfjs/jsx-runtime";
|
|
5810
5706
|
function titleCase(s) {
|
|
5811
5707
|
return s.replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
5812
5708
|
}
|
|
@@ -5825,53 +5721,59 @@ function shortenArgs(args) {
|
|
|
5825
5721
|
function ReviewHistory({ reviews, currentReviewId }) {
|
|
5826
5722
|
const hasOtherReviews = reviews.some((r) => r.id !== currentReviewId);
|
|
5827
5723
|
const hasCompletedOthers = reviews.some((r) => r.id !== currentReviewId && r.status === "completed");
|
|
5828
|
-
return /* @__PURE__ */
|
|
5829
|
-
/* @__PURE__ */
|
|
5830
|
-
reviews.length === 0 ? /* @__PURE__ */
|
|
5724
|
+
return /* @__PURE__ */ jsxs5("div", { className: "history-page", children: [
|
|
5725
|
+
/* @__PURE__ */ jsx5("h1", { children: "Review History" }),
|
|
5726
|
+
reviews.length === 0 ? /* @__PURE__ */ jsx5("p", { style: "color:var(--text-dim)", children: "No previous reviews found." }) : /* @__PURE__ */ jsx5("div", { children: reviews.map((r) => {
|
|
5831
5727
|
const isCurrent = r.id === currentReviewId;
|
|
5832
5728
|
const href = isCurrent ? "/" : `/review/${r.id}`;
|
|
5833
5729
|
let argsDisplay = null;
|
|
5834
5730
|
if (r.mode_args !== null && r.mode_args !== "") {
|
|
5835
5731
|
const { short, full } = shortenArgs(r.mode_args);
|
|
5836
|
-
argsDisplay = full !== "" ? /* @__PURE__ */
|
|
5732
|
+
argsDisplay = full !== "" ? /* @__PURE__ */ jsxs5("span", { title: full, children: [
|
|
5837
5733
|
": ",
|
|
5838
5734
|
short
|
|
5839
|
-
] }) : /* @__PURE__ */
|
|
5735
|
+
] }) : /* @__PURE__ */ jsxs5("span", { children: [
|
|
5840
5736
|
": ",
|
|
5841
5737
|
short
|
|
5842
5738
|
] });
|
|
5843
5739
|
}
|
|
5844
|
-
return /* @__PURE__ */
|
|
5845
|
-
/* @__PURE__ */
|
|
5846
|
-
/* @__PURE__ */
|
|
5740
|
+
return /* @__PURE__ */ jsxs5("div", { children: [
|
|
5741
|
+
/* @__PURE__ */ jsx5("a", { href, className: "history-item-link", children: /* @__PURE__ */ jsxs5("div", { className: "history-item", "data-review-id": r.id, children: [
|
|
5742
|
+
/* @__PURE__ */ jsxs5("h3", { children: [
|
|
5847
5743
|
r.repo_name,
|
|
5848
5744
|
" - ",
|
|
5849
5745
|
titleCase(r.mode),
|
|
5850
5746
|
argsDisplay,
|
|
5851
|
-
isCurrent ? /* @__PURE__ */
|
|
5852
|
-
/* @__PURE__ */
|
|
5747
|
+
isCurrent ? /* @__PURE__ */ jsx5("span", { className: "status-badge in_progress", style: "margin-left:8px", children: "Current" }) : null,
|
|
5748
|
+
/* @__PURE__ */ jsx5("span", { className: `status-badge ${r.status}`, style: "margin-left:8px", children: titleCase(r.status) })
|
|
5853
5749
|
] }),
|
|
5854
|
-
/* @__PURE__ */
|
|
5750
|
+
/* @__PURE__ */ jsxs5("div", { className: "meta", children: [
|
|
5855
5751
|
"ID: ",
|
|
5856
5752
|
r.id,
|
|
5857
5753
|
" | Created: ",
|
|
5858
|
-
|
|
5754
|
+
/* The `Review` type says `created_at: string`, but PGLite
|
|
5755
|
+
actually returns a `Date` from `timestamp` columns at
|
|
5756
|
+
runtime, which the kerf JSX runtime rejects. The
|
|
5757
|
+
explicit String() coercion is intentional. */
|
|
5758
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion
|
|
5759
|
+
String(r.created_at)
|
|
5859
5760
|
] }),
|
|
5860
|
-
!isCurrent ? /* @__PURE__ */
|
|
5761
|
+
!isCurrent ? /* @__PURE__ */ jsx5("button", { className: "delete-review-btn", "data-delete-id": r.id, title: "Delete review", children: /* @__PURE__ */ jsx5(IconTrash16, {}) }) : null
|
|
5861
5762
|
] }) }),
|
|
5862
|
-
isCurrent && hasOtherReviews ? /* @__PURE__ */
|
|
5863
|
-
/* @__PURE__ */
|
|
5864
|
-
hasCompletedOthers ? /* @__PURE__ */
|
|
5865
|
-
/* @__PURE__ */
|
|
5763
|
+
isCurrent && hasOtherReviews ? /* @__PURE__ */ jsxs5("div", { className: "bulk-actions", children: [
|
|
5764
|
+
/* @__PURE__ */ jsx5("span", { children: "Bulk actions:" }),
|
|
5765
|
+
hasCompletedOthers ? /* @__PURE__ */ jsx5("button", { className: "btn btn-sm btn-danger", id: "delete-completed-btn", children: "Delete Completed" }) : null,
|
|
5766
|
+
/* @__PURE__ */ jsx5("button", { className: "btn btn-sm btn-danger", id: "delete-all-btn", children: "Delete All" })
|
|
5866
5767
|
] }) : null
|
|
5867
5768
|
] });
|
|
5868
5769
|
}) }),
|
|
5869
|
-
/* @__PURE__ */
|
|
5870
|
-
/* @__PURE__ */
|
|
5770
|
+
/* @__PURE__ */ jsx5("a", { href: "/", className: "btn btn-link", style: "margin-top:16px;display:inline-block", children: "Back to current review" }),
|
|
5771
|
+
/* @__PURE__ */ jsx5("script", { src: "/static/history.js" })
|
|
5871
5772
|
] });
|
|
5872
5773
|
}
|
|
5873
5774
|
|
|
5874
5775
|
// src/components/fileList.tsx
|
|
5776
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "kerfjs/jsx-runtime";
|
|
5875
5777
|
function buildFileTree(files) {
|
|
5876
5778
|
const root = { name: "", children: [], files: [] };
|
|
5877
5779
|
for (const f of files) {
|
|
@@ -5917,21 +5819,21 @@ function hasStale(node, staleCounts) {
|
|
|
5917
5819
|
}
|
|
5918
5820
|
function TreeView({ node, depth, annotationCounts, staleCounts }) {
|
|
5919
5821
|
const sortedChildren = [...node.children].sort((a, b) => a.name.localeCompare(b.name));
|
|
5920
|
-
return /* @__PURE__ */
|
|
5822
|
+
return /* @__PURE__ */ jsxs6("div", { children: [
|
|
5921
5823
|
sortedChildren.map((child) => {
|
|
5922
5824
|
const total = countFiles(child);
|
|
5923
5825
|
const isCollapsible = total > 1;
|
|
5924
5826
|
const stale = hasStale(child, staleCounts);
|
|
5925
|
-
return /* @__PURE__ */
|
|
5926
|
-
/* @__PURE__ */
|
|
5927
|
-
isCollapsible ? /* @__PURE__ */
|
|
5928
|
-
/* @__PURE__ */
|
|
5827
|
+
return /* @__PURE__ */ jsxs6("div", { className: "folder-group", children: [
|
|
5828
|
+
/* @__PURE__ */ jsxs6("div", { className: `folder-header${isCollapsible ? " collapsible" : ""}`, style: `padding-left:${16 + depth * 12}px`, children: [
|
|
5829
|
+
isCollapsible ? /* @__PURE__ */ jsx6("span", { className: "folder-arrow", children: "\u25BE" }) : /* @__PURE__ */ jsx6("span", { className: "folder-arrow-spacer" }),
|
|
5830
|
+
/* @__PURE__ */ jsxs6("span", { className: "folder-name", children: [
|
|
5929
5831
|
child.name,
|
|
5930
5832
|
"/"
|
|
5931
5833
|
] }),
|
|
5932
|
-
stale ? /* @__PURE__ */
|
|
5834
|
+
stale ? /* @__PURE__ */ jsx6("span", { className: "stale-dot" }) : null
|
|
5933
5835
|
] }),
|
|
5934
|
-
/* @__PURE__ */
|
|
5836
|
+
/* @__PURE__ */ jsx6("div", { className: "folder-content", children: /* @__PURE__ */ jsx6(TreeView, { node: child, depth: depth + 1, annotationCounts, staleCounts }) })
|
|
5935
5837
|
] });
|
|
5936
5838
|
}),
|
|
5937
5839
|
node.files.map((f) => {
|
|
@@ -5939,89 +5841,90 @@ function TreeView({ node, depth, annotationCounts, staleCounts }) {
|
|
|
5939
5841
|
const count = annotationCounts[f.id] || 0;
|
|
5940
5842
|
const stale = staleCounts[f.id] || 0;
|
|
5941
5843
|
const fileName = f.file_path.split("/").pop() ?? "";
|
|
5942
|
-
return /* @__PURE__ */
|
|
5943
|
-
/* @__PURE__ */
|
|
5944
|
-
/* @__PURE__ */
|
|
5945
|
-
/* @__PURE__ */
|
|
5946
|
-
stale > 0 ? /* @__PURE__ */
|
|
5947
|
-
count > 0 ? /* @__PURE__ */
|
|
5844
|
+
return /* @__PURE__ */ jsxs6("div", { className: "file-item", "data-file-id": f.id, style: `padding-left:${16 + depth * 12}px`, children: [
|
|
5845
|
+
/* @__PURE__ */ jsx6("span", { className: `status-dot ${f.status}` }),
|
|
5846
|
+
/* @__PURE__ */ jsx6("span", { className: "file-name", title: f.file_path, children: fileName }),
|
|
5847
|
+
/* @__PURE__ */ jsx6("span", { className: `file-status ${diff?.status ?? ""}`, children: diff?.status ?? "" }),
|
|
5848
|
+
stale > 0 ? /* @__PURE__ */ jsx6("span", { className: "stale-dot" }) : null,
|
|
5849
|
+
count > 0 ? /* @__PURE__ */ jsx6("span", { className: "annotation-count", children: count }) : null
|
|
5948
5850
|
] });
|
|
5949
5851
|
})
|
|
5950
5852
|
] });
|
|
5951
5853
|
}
|
|
5952
5854
|
function FileList({ files, annotationCounts, staleCounts }) {
|
|
5953
5855
|
const tree = buildFileTree(files);
|
|
5954
|
-
return /* @__PURE__ */
|
|
5856
|
+
return /* @__PURE__ */ jsx6("div", { className: "file-list", children: /* @__PURE__ */ jsx6("div", { className: "file-list-items", children: /* @__PURE__ */ jsx6(TreeView, { node: tree, depth: 0, annotationCounts, staleCounts }) }) });
|
|
5955
5857
|
}
|
|
5956
5858
|
|
|
5957
5859
|
// src/components/reviewShell.tsx
|
|
5860
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "kerfjs/jsx-runtime";
|
|
5958
5861
|
function ReviewShell({ reviewId, review, files, annotationCounts, staleCounts, footer }) {
|
|
5959
|
-
return /* @__PURE__ */
|
|
5960
|
-
/* @__PURE__ */
|
|
5961
|
-
/* @__PURE__ */
|
|
5962
|
-
/* @__PURE__ */
|
|
5963
|
-
/* @__PURE__ */
|
|
5964
|
-
/* @__PURE__ */
|
|
5862
|
+
return /* @__PURE__ */ jsxs7("div", { className: "review-app", "data-review-id": reviewId, children: [
|
|
5863
|
+
/* @__PURE__ */ jsxs7("div", { id: "update-banner", className: "update-banner", style: "display:none", children: [
|
|
5864
|
+
/* @__PURE__ */ jsx7("span", { id: "update-banner-label", children: "Update available" }),
|
|
5865
|
+
/* @__PURE__ */ jsxs7("div", { className: "update-banner-actions", children: [
|
|
5866
|
+
/* @__PURE__ */ jsx7("button", { id: "update-install-btn", className: "btn btn-sm btn-accent", children: "Install Update" }),
|
|
5867
|
+
/* @__PURE__ */ jsx7("button", { id: "update-banner-dismiss", className: "btn btn-sm", children: "Later" })
|
|
5965
5868
|
] })
|
|
5966
5869
|
] }),
|
|
5967
|
-
/* @__PURE__ */
|
|
5968
|
-
/* @__PURE__ */
|
|
5969
|
-
/* @__PURE__ */
|
|
5970
|
-
/* @__PURE__ */
|
|
5971
|
-
/* @__PURE__ */
|
|
5870
|
+
/* @__PURE__ */ jsxs7("div", { className: "review-body", children: [
|
|
5871
|
+
/* @__PURE__ */ jsxs7("aside", { className: "sidebar", children: [
|
|
5872
|
+
/* @__PURE__ */ jsxs7("div", { className: "sidebar-header", children: [
|
|
5873
|
+
/* @__PURE__ */ jsx7("h2", { children: review.repo_name }),
|
|
5874
|
+
/* @__PURE__ */ jsxs7("span", { className: "review-mode", children: [
|
|
5972
5875
|
review.mode,
|
|
5973
5876
|
review.mode_args !== null && review.mode_args !== "" ? `: ${review.mode_args}` : ""
|
|
5974
5877
|
] })
|
|
5975
5878
|
] }),
|
|
5976
|
-
/* @__PURE__ */
|
|
5977
|
-
/* @__PURE__ */
|
|
5978
|
-
/* @__PURE__ */
|
|
5979
|
-
/* @__PURE__ */
|
|
5879
|
+
/* @__PURE__ */ jsx7("div", { className: "file-filter", children: /* @__PURE__ */ jsx7("input", { type: "text", className: "file-filter-input", id: "file-filter", placeholder: "Filter files..." }) }),
|
|
5880
|
+
/* @__PURE__ */ jsx7(FileList, { files, annotationCounts, staleCounts }),
|
|
5881
|
+
/* @__PURE__ */ jsx7("div", { className: "sidebar-share", id: "sidebar-share" }),
|
|
5882
|
+
/* @__PURE__ */ jsx7("div", { className: "sidebar-footer", children: footer })
|
|
5980
5883
|
] }),
|
|
5981
|
-
/* @__PURE__ */
|
|
5982
|
-
/* @__PURE__ */
|
|
5983
|
-
/* @__PURE__ */
|
|
5984
|
-
/* @__PURE__ */
|
|
5985
|
-
/* @__PURE__ */
|
|
5884
|
+
/* @__PURE__ */ jsx7("div", { className: "sidebar-resize", id: "sidebar-resize" }),
|
|
5885
|
+
/* @__PURE__ */ jsxs7("main", { className: "main-content", children: [
|
|
5886
|
+
/* @__PURE__ */ jsxs7("div", { className: "welcome-message", children: [
|
|
5887
|
+
/* @__PURE__ */ jsx7("h3", { children: "Select a file to begin reviewing" }),
|
|
5888
|
+
/* @__PURE__ */ jsxs7("p", { children: [
|
|
5986
5889
|
files.length,
|
|
5987
5890
|
" file(s) to review"
|
|
5988
5891
|
] }),
|
|
5989
|
-
/* @__PURE__ */
|
|
5892
|
+
/* @__PURE__ */ jsx7("p", { className: "progress-summary", id: "progress-summary" })
|
|
5990
5893
|
] }),
|
|
5991
|
-
/* @__PURE__ */
|
|
5992
|
-
/* @__PURE__ */
|
|
5993
|
-
/* @__PURE__ */
|
|
5994
|
-
/* @__PURE__ */
|
|
5894
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-nav-bar", id: "diff-nav-bar", style: "display:none", children: [
|
|
5895
|
+
/* @__PURE__ */ jsx7("button", { className: "nav-btn disabled", id: "nav-back-btn", disabled: true, title: "Back", children: /* @__PURE__ */ jsx7("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "m15 18-6-6 6-6" }) }) }),
|
|
5896
|
+
/* @__PURE__ */ jsx7("button", { className: "nav-btn disabled", id: "nav-forward-btn", disabled: true, title: "Forward", children: /* @__PURE__ */ jsx7("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "m9 18 6-6-6-6" }) }) }),
|
|
5897
|
+
/* @__PURE__ */ jsx7("span", { className: "nav-file-path", id: "nav-file-path" })
|
|
5995
5898
|
] }),
|
|
5996
|
-
/* @__PURE__ */
|
|
5997
|
-
/* @__PURE__ */
|
|
5998
|
-
/* @__PURE__ */
|
|
5999
|
-
/* @__PURE__ */
|
|
6000
|
-
/* @__PURE__ */
|
|
5899
|
+
/* @__PURE__ */ jsx7("div", { className: "diff-container", id: "diff-container", style: "display:none" }),
|
|
5900
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-toolbar", id: "diff-toolbar", style: "display:none", children: [
|
|
5901
|
+
/* @__PURE__ */ jsx7("div", { className: "diff-toolbar-svg-toggle", style: "display:none", children: /* @__PURE__ */ jsxs7("div", { className: "segmented-control", children: [
|
|
5902
|
+
/* @__PURE__ */ jsx7("button", { className: "segment active", "data-svg-mode": "code", children: "Code" }),
|
|
5903
|
+
/* @__PURE__ */ jsx7("button", { className: "segment", "data-svg-mode": "rendered", children: "Rendered" })
|
|
6001
5904
|
] }) }),
|
|
6002
|
-
/* @__PURE__ */
|
|
6003
|
-
/* @__PURE__ */
|
|
6004
|
-
/* @__PURE__ */
|
|
6005
|
-
/* @__PURE__ */
|
|
6006
|
-
/* @__PURE__ */
|
|
5905
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-toolbar-text", children: [
|
|
5906
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-toolbar-left", children: [
|
|
5907
|
+
/* @__PURE__ */ jsxs7("div", { className: "segmented-control", children: [
|
|
5908
|
+
/* @__PURE__ */ jsx7("button", { className: "segment active", "data-diff-mode": "split", children: "Split" }),
|
|
5909
|
+
/* @__PURE__ */ jsx7("button", { className: "segment", "data-diff-mode": "unified", children: "Unified" })
|
|
6007
5910
|
] }),
|
|
6008
|
-
/* @__PURE__ */
|
|
6009
|
-
/* @__PURE__ */
|
|
5911
|
+
/* @__PURE__ */ jsx7("button", { className: "toolbar-btn", id: "wrap-toggle", children: "Wrap" }),
|
|
5912
|
+
/* @__PURE__ */ jsx7("button", { className: "toolbar-btn", id: "whitespace-toggle", children: "Ignore Whitespace" })
|
|
6010
5913
|
] }),
|
|
6011
|
-
/* @__PURE__ */
|
|
5914
|
+
/* @__PURE__ */ jsx7("div", { className: "diff-toolbar-right", children: /* @__PURE__ */ jsx7("button", { className: "toolbar-btn", id: "language-btn", children: "Plain Text" }) })
|
|
6012
5915
|
] }),
|
|
6013
|
-
/* @__PURE__ */
|
|
6014
|
-
/* @__PURE__ */
|
|
6015
|
-
/* @__PURE__ */
|
|
6016
|
-
/* @__PURE__ */
|
|
6017
|
-
/* @__PURE__ */
|
|
6018
|
-
/* @__PURE__ */
|
|
5916
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-toolbar-image", style: "display:none", children: [
|
|
5917
|
+
/* @__PURE__ */ jsx7("div", { className: "diff-toolbar-left", children: /* @__PURE__ */ jsxs7("div", { className: "segmented-control", children: [
|
|
5918
|
+
/* @__PURE__ */ jsx7("button", { className: "segment active", "data-image-mode": "metadata", children: "Metadata" }),
|
|
5919
|
+
/* @__PURE__ */ jsx7("button", { className: "segment", "data-image-mode": "difference", children: "Difference" }),
|
|
5920
|
+
/* @__PURE__ */ jsx7("button", { className: "segment", "data-image-mode": "slice", children: "Slice" }),
|
|
5921
|
+
/* @__PURE__ */ jsx7("button", { className: "segment", "data-image-mode": "image", style: "display:none", children: "Image" })
|
|
6019
5922
|
] }) }),
|
|
6020
|
-
/* @__PURE__ */
|
|
6021
|
-
/* @__PURE__ */
|
|
6022
|
-
/* @__PURE__ */
|
|
6023
|
-
/* @__PURE__ */
|
|
6024
|
-
/* @__PURE__ */
|
|
5923
|
+
/* @__PURE__ */ jsxs7("div", { className: "diff-toolbar-right", children: [
|
|
5924
|
+
/* @__PURE__ */ jsx7("button", { className: "image-zoom-btn", "data-zoom-action": "out", title: "Zoom out", children: /* @__PURE__ */ jsx7(IconZoomOut, {}) }),
|
|
5925
|
+
/* @__PURE__ */ jsx7("button", { className: "image-zoom-btn", "data-zoom-action": "fit", title: "Fit to view", children: /* @__PURE__ */ jsx7(IconFit, {}) }),
|
|
5926
|
+
/* @__PURE__ */ jsx7("button", { className: "image-zoom-btn", "data-zoom-action": "actual", title: "Actual size (1:1)", children: /* @__PURE__ */ jsx7(IconActualSize, {}) }),
|
|
5927
|
+
/* @__PURE__ */ jsx7("button", { className: "image-zoom-btn", "data-zoom-action": "in", title: "Zoom in", children: /* @__PURE__ */ jsx7(IconZoomIn, {}) })
|
|
6025
5928
|
] })
|
|
6026
5929
|
] })
|
|
6027
5930
|
] })
|
|
@@ -6032,22 +5935,21 @@ function ReviewShell({ reviewId, review, files, annotationCounts, staleCounts, f
|
|
|
6032
5935
|
|
|
6033
5936
|
// src/routes/pages.tsx
|
|
6034
5937
|
init_queries();
|
|
5938
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "kerfjs/jsx-runtime";
|
|
6035
5939
|
var pageRoutes = new Hono14();
|
|
6036
5940
|
pageRoutes.get("/", async (c) => {
|
|
6037
5941
|
const reviewId = c.get("reviewId");
|
|
6038
5942
|
const review = await getReview(reviewId);
|
|
6039
5943
|
if (!review) return c.text("Review not found", 404);
|
|
6040
|
-
const files = await
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
/* @__PURE__ */ jsx("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
|
|
6048
|
-
/* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" })
|
|
5944
|
+
const [files, annotationCounts] = await Promise.all([
|
|
5945
|
+
getReviewFiles(reviewId),
|
|
5946
|
+
getAnnotationCountsForReview(reviewId)
|
|
5947
|
+
]);
|
|
5948
|
+
const footer = /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
5949
|
+
/* @__PURE__ */ jsx8("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
|
|
5950
|
+
/* @__PURE__ */ jsx8("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" })
|
|
6049
5951
|
] });
|
|
6050
|
-
const html = /* @__PURE__ */
|
|
5952
|
+
const html = /* @__PURE__ */ jsx8(Layout, { title: `Glassbox - ${review.repo_name}`, reviewId, children: /* @__PURE__ */ jsx8(ReviewShell, { reviewId, review, files, annotationCounts, staleCounts: {}, footer }) });
|
|
6051
5953
|
return c.html(html.toString());
|
|
6052
5954
|
});
|
|
6053
5955
|
pageRoutes.get("/file/:fileId", async (c) => {
|
|
@@ -6078,15 +5980,15 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
6078
5980
|
fontWarning = true;
|
|
6079
5981
|
}
|
|
6080
5982
|
}
|
|
6081
|
-
const html2 = /* @__PURE__ */
|
|
6082
|
-
/* @__PURE__ */
|
|
6083
|
-
/* @__PURE__ */
|
|
6084
|
-
/* @__PURE__ */
|
|
6085
|
-
/* @__PURE__ */
|
|
5983
|
+
const html2 = /* @__PURE__ */ jsxs8("div", { className: "diff-view", "data-file-id": file.id, "data-file-path": file.file_path, "data-is-svg": "true", children: [
|
|
5984
|
+
/* @__PURE__ */ jsxs8("div", { className: "diff-header", children: [
|
|
5985
|
+
/* @__PURE__ */ jsxs8("div", { className: "diff-header-file", children: [
|
|
5986
|
+
/* @__PURE__ */ jsx8("span", { className: "file-path", children: diff.filePath }),
|
|
5987
|
+
/* @__PURE__ */ jsx8("button", { className: "reveal-btn", "data-file-id": file.id, title: "Reveal in file manager", children: /* @__PURE__ */ jsx8(IconReveal, {}) })
|
|
6086
5988
|
] }),
|
|
6087
|
-
/* @__PURE__ */
|
|
5989
|
+
/* @__PURE__ */ jsx8("div", { className: "diff-header-actions", children: /* @__PURE__ */ jsx8("span", { className: `file-status ${diff.status}`, children: diff.status }) })
|
|
6088
5990
|
] }),
|
|
6089
|
-
/* @__PURE__ */
|
|
5991
|
+
/* @__PURE__ */ jsx8(
|
|
6090
5992
|
ImageDiff,
|
|
6091
5993
|
{
|
|
6092
5994
|
file,
|
|
@@ -6112,7 +6014,7 @@ pageRoutes.get("/file/:fileId", async (c) => {
|
|
|
6112
6014
|
}
|
|
6113
6015
|
}
|
|
6114
6016
|
}
|
|
6115
|
-
const html = /* @__PURE__ */
|
|
6017
|
+
const html = /* @__PURE__ */ jsx8(DiffView, { file, diff: finalDiff, annotations, mode });
|
|
6116
6018
|
return c.html(html.toString());
|
|
6117
6019
|
});
|
|
6118
6020
|
pageRoutes.get("/file-raw", (c) => {
|
|
@@ -6121,7 +6023,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
6121
6023
|
const repoRoot = c.get("repoRoot");
|
|
6122
6024
|
let content;
|
|
6123
6025
|
try {
|
|
6124
|
-
content = readFileSync11(
|
|
6026
|
+
content = readFileSync11(resolve7(repoRoot, filePath), "utf-8");
|
|
6125
6027
|
} catch {
|
|
6126
6028
|
return c.text("File not found", 404);
|
|
6127
6029
|
}
|
|
@@ -6145,7 +6047,7 @@ pageRoutes.get("/file-raw", (c) => {
|
|
|
6145
6047
|
}]
|
|
6146
6048
|
};
|
|
6147
6049
|
const fakeFile = { id: "", review_id: "", file_path: filePath, status: "reviewed", diff_data: null, created_at: "" };
|
|
6148
|
-
const html = /* @__PURE__ */
|
|
6050
|
+
const html = /* @__PURE__ */ jsx8(DiffView, { file: fakeFile, diff, annotations: [], mode: "unified" });
|
|
6149
6051
|
return c.html(html.toString());
|
|
6150
6052
|
});
|
|
6151
6053
|
pageRoutes.get("/review/:reviewId", async (c) => {
|
|
@@ -6156,25 +6058,23 @@ pageRoutes.get("/review/:reviewId", async (c) => {
|
|
|
6156
6058
|
}
|
|
6157
6059
|
const review = await getReview(reviewId);
|
|
6158
6060
|
if (!review) return c.text("Review not found", 404);
|
|
6159
|
-
const files = await
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
/* @__PURE__ */ jsx("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" }),
|
|
6168
|
-
/* @__PURE__ */ jsx("a", { href: "/", className: "btn btn-sm btn-link", children: "Back to current review" })
|
|
6061
|
+
const [files, annotationCounts] = await Promise.all([
|
|
6062
|
+
getReviewFiles(reviewId),
|
|
6063
|
+
getAnnotationCountsForReview(reviewId)
|
|
6064
|
+
]);
|
|
6065
|
+
const footer = /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
6066
|
+
review.status === "completed" ? /* @__PURE__ */ jsx8("button", { className: "btn btn-primary", id: "reopen-review", children: "Reopen Review" }) : /* @__PURE__ */ jsx8("button", { className: "btn btn-primary btn-complete", id: "complete-review", children: "Complete Review" }),
|
|
6067
|
+
/* @__PURE__ */ jsx8("a", { href: "/history", className: "btn btn-sm btn-link", children: "Review History" }),
|
|
6068
|
+
/* @__PURE__ */ jsx8("a", { href: "/", className: "btn btn-sm btn-link", children: "Back to current review" })
|
|
6169
6069
|
] });
|
|
6170
|
-
const html = /* @__PURE__ */
|
|
6070
|
+
const html = /* @__PURE__ */ jsx8(Layout, { title: `Glassbox - ${review.repo_name}`, reviewId, children: /* @__PURE__ */ jsx8(ReviewShell, { reviewId, review, files, annotationCounts, staleCounts: {}, footer }) });
|
|
6171
6071
|
return c.html(html.toString());
|
|
6172
6072
|
});
|
|
6173
6073
|
pageRoutes.get("/history", async (c) => {
|
|
6174
6074
|
const repoRoot = c.get("repoRoot");
|
|
6175
6075
|
const currentReviewId = c.get("reviewId");
|
|
6176
6076
|
const reviews = await listReviews(repoRoot);
|
|
6177
|
-
const html = /* @__PURE__ */
|
|
6077
|
+
const html = /* @__PURE__ */ jsx8(Layout, { title: "Review History", reviewId: "", children: /* @__PURE__ */ jsx8(ReviewHistory, { reviews, currentReviewId }) });
|
|
6178
6078
|
return c.html(html.toString());
|
|
6179
6079
|
});
|
|
6180
6080
|
|
|
@@ -6314,10 +6214,10 @@ themeApiRoutes.delete("/:id", (c) => {
|
|
|
6314
6214
|
|
|
6315
6215
|
// src/server.ts
|
|
6316
6216
|
function tryServe(appFetch, port) {
|
|
6317
|
-
return new Promise((
|
|
6217
|
+
return new Promise((resolve9, reject) => {
|
|
6318
6218
|
const server = serve({ fetch: appFetch, port, hostname: "127.0.0.1" });
|
|
6319
6219
|
server.on("listening", () => {
|
|
6320
|
-
|
|
6220
|
+
resolve9(port);
|
|
6321
6221
|
});
|
|
6322
6222
|
server.on("error", (err) => {
|
|
6323
6223
|
if (err.code === "EADDRINUSE") {
|
|
@@ -6387,8 +6287,10 @@ async function startServer(port, reviewId, repoRoot, options) {
|
|
|
6387
6287
|
} catch {
|
|
6388
6288
|
}
|
|
6389
6289
|
if (options?.noOpen !== true) {
|
|
6390
|
-
|
|
6391
|
-
|
|
6290
|
+
try {
|
|
6291
|
+
openOS(url, "url");
|
|
6292
|
+
} catch {
|
|
6293
|
+
}
|
|
6392
6294
|
}
|
|
6393
6295
|
}
|
|
6394
6296
|
|
|
@@ -6547,10 +6449,10 @@ function isFirstUseToday() {
|
|
|
6547
6449
|
return last !== today;
|
|
6548
6450
|
}
|
|
6549
6451
|
function fetchLatestVersion() {
|
|
6550
|
-
return new Promise((
|
|
6452
|
+
return new Promise((resolve9) => {
|
|
6551
6453
|
const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
6552
6454
|
if (res.statusCode !== 200) {
|
|
6553
|
-
|
|
6455
|
+
resolve9(null);
|
|
6554
6456
|
return;
|
|
6555
6457
|
}
|
|
6556
6458
|
let data = "";
|
|
@@ -6559,18 +6461,18 @@ function fetchLatestVersion() {
|
|
|
6559
6461
|
});
|
|
6560
6462
|
res.on("end", () => {
|
|
6561
6463
|
try {
|
|
6562
|
-
|
|
6464
|
+
resolve9(JSON.parse(data).version);
|
|
6563
6465
|
} catch {
|
|
6564
|
-
|
|
6466
|
+
resolve9(null);
|
|
6565
6467
|
}
|
|
6566
6468
|
});
|
|
6567
6469
|
});
|
|
6568
6470
|
req.on("error", () => {
|
|
6569
|
-
|
|
6471
|
+
resolve9(null);
|
|
6570
6472
|
});
|
|
6571
6473
|
req.on("timeout", () => {
|
|
6572
6474
|
req.destroy();
|
|
6573
|
-
|
|
6475
|
+
resolve9(null);
|
|
6574
6476
|
});
|
|
6575
6477
|
});
|
|
6576
6478
|
}
|
|
@@ -6708,7 +6610,7 @@ function parseArgs(argv) {
|
|
|
6708
6610
|
port = parseInt(args[++i], 10);
|
|
6709
6611
|
break;
|
|
6710
6612
|
case "--data-dir":
|
|
6711
|
-
dataDir =
|
|
6613
|
+
dataDir = resolve8(args[++i]);
|
|
6712
6614
|
break;
|
|
6713
6615
|
case "--resume":
|
|
6714
6616
|
resume = true;
|
|
@@ -6764,7 +6666,7 @@ async function main() {
|
|
|
6764
6666
|
console.log("AI service test mode enabled \u2014 using mock AI responses");
|
|
6765
6667
|
}
|
|
6766
6668
|
if (debug) {
|
|
6767
|
-
console.log(`[debug] Build timestamp: ${"2026-05-
|
|
6669
|
+
console.log(`[debug] Build timestamp: ${"2026-05-13T05:27:19.184Z"}`);
|
|
6768
6670
|
}
|
|
6769
6671
|
if (projectDir !== null) {
|
|
6770
6672
|
process.chdir(projectDir);
|