@youtyan/code-viewer 0.13.1 → 0.15.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/README.md +60 -6
- package/dist/code-viewer.js +526 -99
- package/package.json +1 -1
- package/web/app.js +2242 -541
- package/web/index.html +3 -0
- package/web/style.css +221 -4
package/web/app.js
CHANGED
|
@@ -835,6 +835,281 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
835
835
|
applyTrailingResult
|
|
836
836
|
};
|
|
837
837
|
|
|
838
|
+
// web-src/core/fuzzy-search.ts
|
|
839
|
+
function basenameStart(path) {
|
|
840
|
+
const slash = path.lastIndexOf("/");
|
|
841
|
+
return slash < 0 ? 0 : slash + 1;
|
|
842
|
+
}
|
|
843
|
+
function isBoundary(path, index) {
|
|
844
|
+
if (index <= 0) return true;
|
|
845
|
+
const prev = path[index - 1];
|
|
846
|
+
return prev === "/" || prev === "-" || prev === "_" || prev === "." || prev === " ";
|
|
847
|
+
}
|
|
848
|
+
function toRanges(indices) {
|
|
849
|
+
const ranges = [];
|
|
850
|
+
for (const index of indices) {
|
|
851
|
+
const last = ranges[ranges.length - 1];
|
|
852
|
+
if (last && last.end === index) {
|
|
853
|
+
last.end = index + 1;
|
|
854
|
+
} else {
|
|
855
|
+
ranges.push({ start: index, end: index + 1 });
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return ranges;
|
|
859
|
+
}
|
|
860
|
+
function basenameMatchTier(loweredQuery, loweredBasename) {
|
|
861
|
+
if (loweredBasename === loweredQuery) return 4;
|
|
862
|
+
if (loweredBasename.startsWith(`${loweredQuery}.`)) return 3;
|
|
863
|
+
if (loweredBasename.startsWith(loweredQuery)) return 2;
|
|
864
|
+
if (loweredBasename.includes(loweredQuery)) return 1;
|
|
865
|
+
return 0;
|
|
866
|
+
}
|
|
867
|
+
function pathMatchTier(loweredQuery, loweredPath, loweredBasename) {
|
|
868
|
+
if (loweredQuery.includes("/") && (loweredPath === loweredQuery || loweredPath.endsWith(`/${loweredQuery}`)))
|
|
869
|
+
return 4;
|
|
870
|
+
return basenameMatchTier(loweredQuery, loweredBasename);
|
|
871
|
+
}
|
|
872
|
+
function contiguousPathRange(loweredQuery, loweredPath, baseStart) {
|
|
873
|
+
const loweredBasename = loweredPath.slice(baseStart);
|
|
874
|
+
const basenameMatchStart = loweredBasename.indexOf(loweredQuery);
|
|
875
|
+
if (basenameMatchStart >= 0) {
|
|
876
|
+
const start = baseStart + basenameMatchStart;
|
|
877
|
+
return { start, end: start + loweredQuery.length };
|
|
878
|
+
}
|
|
879
|
+
if (loweredQuery.includes("/")) {
|
|
880
|
+
const pathMatchStart = loweredPath.endsWith(`/${loweredQuery}`) ? loweredPath.length - loweredQuery.length : loweredPath === loweredQuery ? 0 : -1;
|
|
881
|
+
if (pathMatchStart >= 0)
|
|
882
|
+
return {
|
|
883
|
+
start: pathMatchStart,
|
|
884
|
+
end: pathMatchStart + loweredQuery.length
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
function computeFuzzyMatch(query, path) {
|
|
890
|
+
const q = query.trim().toLowerCase();
|
|
891
|
+
if (!q) return { score: 0, ranges: [], tier: 0 };
|
|
892
|
+
const lowerPath = path.toLowerCase();
|
|
893
|
+
const baseStart = basenameStart(path);
|
|
894
|
+
const indices = [];
|
|
895
|
+
let from = 0;
|
|
896
|
+
let score = 0;
|
|
897
|
+
for (const ch of q) {
|
|
898
|
+
const index = lowerPath.indexOf(ch, from);
|
|
899
|
+
if (index < 0) return null;
|
|
900
|
+
indices.push(index);
|
|
901
|
+
score += 10;
|
|
902
|
+
if (index >= baseStart) score += 8;
|
|
903
|
+
if (isBoundary(path, index)) score += 6;
|
|
904
|
+
const prev = indices[indices.length - 2];
|
|
905
|
+
if (prev != null && prev + 1 === index) score += 12;
|
|
906
|
+
from = index + 1;
|
|
907
|
+
}
|
|
908
|
+
const first = indices[0];
|
|
909
|
+
score -= Math.min(first, 40);
|
|
910
|
+
if (indices[0] >= baseStart) score += 20;
|
|
911
|
+
const basename = lowerPath.slice(baseStart);
|
|
912
|
+
const tier = pathMatchTier(q, lowerPath, basename);
|
|
913
|
+
const contiguousRange = contiguousPathRange(q, lowerPath, baseStart);
|
|
914
|
+
return {
|
|
915
|
+
score,
|
|
916
|
+
ranges: contiguousRange ? [contiguousRange] : toRanges(indices),
|
|
917
|
+
tier
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
function fuzzyMatchPath(query, path) {
|
|
921
|
+
const match2 = computeFuzzyMatch(query, path);
|
|
922
|
+
return match2 ? { score: match2.score, ranges: match2.ranges } : null;
|
|
923
|
+
}
|
|
924
|
+
function rankFuzzyPaths(query, items, limit, stats) {
|
|
925
|
+
const bounded = Number.isInteger(limit) && limit !== void 0 && limit > 0 ? Math.floor(limit) : 0;
|
|
926
|
+
const compare = (a2, b2) => b2.tier - a2.tier || b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
|
|
927
|
+
if (!bounded) {
|
|
928
|
+
const ranked = items.map((item) => {
|
|
929
|
+
const match2 = computeFuzzyMatch(query, item.path);
|
|
930
|
+
return match2 ? { item, score: match2.score, ranges: match2.ranges, tier: match2.tier } : null;
|
|
931
|
+
}).filter(
|
|
932
|
+
(item) => item !== null
|
|
933
|
+
).sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
|
|
934
|
+
if (stats) stats.total = ranked.length;
|
|
935
|
+
return ranked;
|
|
936
|
+
}
|
|
937
|
+
const top = [];
|
|
938
|
+
let total = 0;
|
|
939
|
+
for (const item of items) {
|
|
940
|
+
const match2 = computeFuzzyMatch(query, item.path);
|
|
941
|
+
if (!match2) continue;
|
|
942
|
+
total++;
|
|
943
|
+
const ranked = {
|
|
944
|
+
item,
|
|
945
|
+
score: match2.score,
|
|
946
|
+
ranges: match2.ranges,
|
|
947
|
+
tier: match2.tier
|
|
948
|
+
};
|
|
949
|
+
pushBoundedTop(top, ranked, bounded, compare);
|
|
950
|
+
}
|
|
951
|
+
if (stats) stats.total = total;
|
|
952
|
+
return top.sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
|
|
953
|
+
}
|
|
954
|
+
function pushBoundedTop(heap, item, limit, compareBestFirst) {
|
|
955
|
+
const isWorse = (a2, b2) => compareBestFirst(a2, b2) > 0;
|
|
956
|
+
const siftUp = (index) => {
|
|
957
|
+
while (index > 0) {
|
|
958
|
+
const parent = Math.floor((index - 1) / 2);
|
|
959
|
+
if (!isWorse(heap[index], heap[parent])) break;
|
|
960
|
+
[heap[index], heap[parent]] = [heap[parent], heap[index]];
|
|
961
|
+
index = parent;
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
const siftDown = (index) => {
|
|
965
|
+
while (true) {
|
|
966
|
+
const left = index * 2 + 1;
|
|
967
|
+
const right = left + 1;
|
|
968
|
+
let worst = index;
|
|
969
|
+
if (left < heap.length && isWorse(heap[left], heap[worst])) worst = left;
|
|
970
|
+
if (right < heap.length && isWorse(heap[right], heap[worst]))
|
|
971
|
+
worst = right;
|
|
972
|
+
if (worst === index) break;
|
|
973
|
+
[heap[index], heap[worst]] = [heap[worst], heap[index]];
|
|
974
|
+
index = worst;
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
if (heap.length < limit) {
|
|
978
|
+
heap.push(item);
|
|
979
|
+
siftUp(heap.length - 1);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (compareBestFirst(item, heap[0]) >= 0) return;
|
|
983
|
+
heap[0] = item;
|
|
984
|
+
siftDown(0);
|
|
985
|
+
}
|
|
986
|
+
function rankGlobPathMatches(query, items, limit, stats) {
|
|
987
|
+
const matchPath = createGlobPathMatcher(query);
|
|
988
|
+
if (!matchPath) {
|
|
989
|
+
if (stats) stats.total = 0;
|
|
990
|
+
return [];
|
|
991
|
+
}
|
|
992
|
+
const bounded = Number.isInteger(limit) && limit !== void 0 && limit > 0 ? Math.floor(limit) : 0;
|
|
993
|
+
const compare = (a2, b2) => b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
|
|
994
|
+
if (!bounded) {
|
|
995
|
+
const ranked = items.map((item) => {
|
|
996
|
+
const match2 = matchPath(item.path);
|
|
997
|
+
return match2 ? {
|
|
998
|
+
item,
|
|
999
|
+
score: match2.score,
|
|
1000
|
+
ranges: match2.ranges,
|
|
1001
|
+
mode: "glob"
|
|
1002
|
+
} : null;
|
|
1003
|
+
}).filter((item) => item !== null).sort(compare);
|
|
1004
|
+
if (stats) stats.total = ranked.length;
|
|
1005
|
+
return ranked;
|
|
1006
|
+
}
|
|
1007
|
+
const top = [];
|
|
1008
|
+
let total = 0;
|
|
1009
|
+
for (const item of items) {
|
|
1010
|
+
const match2 = matchPath(item.path);
|
|
1011
|
+
if (!match2) continue;
|
|
1012
|
+
total++;
|
|
1013
|
+
const ranked = {
|
|
1014
|
+
item,
|
|
1015
|
+
score: match2.score,
|
|
1016
|
+
ranges: match2.ranges,
|
|
1017
|
+
mode: "glob"
|
|
1018
|
+
};
|
|
1019
|
+
pushBoundedTop(top, ranked, bounded, compare);
|
|
1020
|
+
}
|
|
1021
|
+
if (stats) stats.total = total;
|
|
1022
|
+
return top.sort(compare);
|
|
1023
|
+
}
|
|
1024
|
+
function rankPathMatches(query, items, limit, stats) {
|
|
1025
|
+
if (isGlobPathQuery(query)) {
|
|
1026
|
+
return rankGlobPathMatches(query, items, limit, stats);
|
|
1027
|
+
}
|
|
1028
|
+
return rankFuzzyPaths(query, items, limit, stats).map((item) => ({
|
|
1029
|
+
...item,
|
|
1030
|
+
mode: "fuzzy"
|
|
1031
|
+
}));
|
|
1032
|
+
}
|
|
1033
|
+
function isGlobPathQuery(query) {
|
|
1034
|
+
return /[*?]/.test(query.trim());
|
|
1035
|
+
}
|
|
1036
|
+
function escapeRegexChar(ch) {
|
|
1037
|
+
return /[\\^$+?.()|{}]/.test(ch) ? `\\${ch}` : ch;
|
|
1038
|
+
}
|
|
1039
|
+
function globToRegExp(query) {
|
|
1040
|
+
const pattern = query.trim();
|
|
1041
|
+
if (!pattern) return null;
|
|
1042
|
+
let source = "^";
|
|
1043
|
+
for (let i2 = 0; i2 < pattern.length; i2++) {
|
|
1044
|
+
const ch = pattern[i2];
|
|
1045
|
+
if (ch === "*") {
|
|
1046
|
+
if (pattern[i2 + 1] === "*") {
|
|
1047
|
+
if (pattern[i2 + 2] === "/") {
|
|
1048
|
+
source += "(?:.*/)?";
|
|
1049
|
+
i2 += 2;
|
|
1050
|
+
} else {
|
|
1051
|
+
source += ".*";
|
|
1052
|
+
i2++;
|
|
1053
|
+
}
|
|
1054
|
+
} else {
|
|
1055
|
+
source += "[^/]*";
|
|
1056
|
+
}
|
|
1057
|
+
} else if (ch === "?") {
|
|
1058
|
+
source += "[^/]";
|
|
1059
|
+
} else if (ch === "[") {
|
|
1060
|
+
const close = pattern.indexOf("]", i2 + 1);
|
|
1061
|
+
if (close < 0) {
|
|
1062
|
+
source += "\\[";
|
|
1063
|
+
} else {
|
|
1064
|
+
const body = pattern.slice(i2 + 1, close).replace(/\\/g, "\\\\");
|
|
1065
|
+
source += `[${body}]`;
|
|
1066
|
+
i2 = close;
|
|
1067
|
+
}
|
|
1068
|
+
} else {
|
|
1069
|
+
source += escapeRegexChar(ch);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
source += "$";
|
|
1073
|
+
try {
|
|
1074
|
+
return new RegExp(source, "i");
|
|
1075
|
+
} catch {
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function globMatchPath(query, path) {
|
|
1080
|
+
return createGlobPathMatcher(query)?.(path) ?? null;
|
|
1081
|
+
}
|
|
1082
|
+
function createGlobPathMatcher(query) {
|
|
1083
|
+
const regex = globToRegExp(query);
|
|
1084
|
+
if (!regex) return null;
|
|
1085
|
+
const literal = query.replace(/[*?[\]]+/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
1086
|
+
const suffix = query.replace(/^\*+/, "").toLowerCase();
|
|
1087
|
+
return (path) => {
|
|
1088
|
+
const baseStart = basenameStart(path);
|
|
1089
|
+
const basename = path.slice(baseStart);
|
|
1090
|
+
if (!regex.test(path) && (query.includes("/") || !regex.test(basename)))
|
|
1091
|
+
return null;
|
|
1092
|
+
const ranges = [];
|
|
1093
|
+
const lowerPath = path.toLowerCase();
|
|
1094
|
+
for (const part of literal) {
|
|
1095
|
+
const start = lowerPath.indexOf(part.toLowerCase());
|
|
1096
|
+
if (start >= 0) ranges.push({ start, end: start + part.length });
|
|
1097
|
+
}
|
|
1098
|
+
ranges.sort((a2, b2) => a2.start - b2.start || a2.end - b2.end);
|
|
1099
|
+
const mergedRanges = [];
|
|
1100
|
+
for (const range of ranges) {
|
|
1101
|
+
const last = mergedRanges[mergedRanges.length - 1];
|
|
1102
|
+
if (last && last.end >= range.start) {
|
|
1103
|
+
last.end = Math.max(last.end, range.end);
|
|
1104
|
+
} else {
|
|
1105
|
+
mergedRanges.push({ ...range });
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
const score = 1e3 - Math.min(path.length, 200) + (path.slice(baseStart).toLowerCase().endsWith(suffix) ? 50 : 0);
|
|
1109
|
+
return { score, ranges: mergedRanges };
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
|
|
838
1113
|
// web-src/core/file-filter.ts
|
|
839
1114
|
function normalizeFileFilterQuery(value) {
|
|
840
1115
|
return (value || "").toLowerCase().trim();
|
|
@@ -868,6 +1143,14 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
868
1143
|
};
|
|
869
1144
|
}
|
|
870
1145
|
}
|
|
1146
|
+
if (raw.startsWith("~")) {
|
|
1147
|
+
const needle = raw.slice(1).trim();
|
|
1148
|
+
if (!needle) return { kind: "empty", match: () => true };
|
|
1149
|
+
return { kind: "fuzzy", match: (path) => !!fuzzyMatchPath(needle, path) };
|
|
1150
|
+
}
|
|
1151
|
+
if (isGlobPathQuery(raw)) {
|
|
1152
|
+
return { kind: "glob", match: (path) => !!globMatchPath(raw, path) };
|
|
1153
|
+
}
|
|
871
1154
|
const q = normalizeFileFilterQuery(raw.startsWith("/") ? raw.slice(1) : raw);
|
|
872
1155
|
return {
|
|
873
1156
|
kind: "substring",
|
|
@@ -894,6 +1177,8 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
894
1177
|
}
|
|
895
1178
|
function keymapScope(target) {
|
|
896
1179
|
if (target?.closest("#app-panel")) return "panel";
|
|
1180
|
+
if (target?.closest("#history-panel, .gdp-file-history-panel"))
|
|
1181
|
+
return "history";
|
|
897
1182
|
if (target?.closest("#content")) return "main";
|
|
898
1183
|
if (target?.closest("#sidebar")) return "sidebar";
|
|
899
1184
|
return "global";
|
|
@@ -1158,7 +1443,13 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1158
1443
|
}
|
|
1159
1444
|
|
|
1160
1445
|
// web-src/core/keymap.ts
|
|
1161
|
-
var KEYMAP_SCOPES = [
|
|
1446
|
+
var KEYMAP_SCOPES = [
|
|
1447
|
+
"global",
|
|
1448
|
+
"sidebar",
|
|
1449
|
+
"main",
|
|
1450
|
+
"panel",
|
|
1451
|
+
"history"
|
|
1452
|
+
];
|
|
1162
1453
|
var KEYMAP_ACTIONS = [
|
|
1163
1454
|
"open-file-palette",
|
|
1164
1455
|
"open-grep-palette",
|
|
@@ -1198,6 +1489,8 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1198
1489
|
"previous-hunk",
|
|
1199
1490
|
"goto-diff",
|
|
1200
1491
|
"goto-history",
|
|
1492
|
+
"history-next-commit",
|
|
1493
|
+
"history-previous-commit",
|
|
1201
1494
|
"goto-repo",
|
|
1202
1495
|
"toggle-terminal-panel",
|
|
1203
1496
|
"toggle-sidebar",
|
|
@@ -1334,6 +1627,10 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1334
1627
|
{ action: "previous-hunk", key: "{", shift: true },
|
|
1335
1628
|
{ action: "goto-diff", key: "d", pendingG: true },
|
|
1336
1629
|
{ action: "goto-history", key: "h", pendingG: true },
|
|
1630
|
+
{ action: "history-next-commit", key: "arrowdown" },
|
|
1631
|
+
{ action: "history-previous-commit", key: "arrowup" },
|
|
1632
|
+
{ action: "history-next-commit", key: "j", scope: "history" },
|
|
1633
|
+
{ action: "history-previous-commit", key: "k", scope: "history" },
|
|
1337
1634
|
{ action: "goto-repo", key: "r", pendingG: true },
|
|
1338
1635
|
{ action: "toggle-sidebar", key: "b" },
|
|
1339
1636
|
{ action: "toggle-terminal-panel", key: "`", ctrl: true },
|
|
@@ -1638,6 +1935,14 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1638
1935
|
if (remote.hostname.toLowerCase() !== "github.com") {
|
|
1639
1936
|
return { url: base2, provider: "web" };
|
|
1640
1937
|
}
|
|
1938
|
+
if (options.kind === "commit") {
|
|
1939
|
+
const sha = (options.ref || "").trim();
|
|
1940
|
+
if (!sha || sha === "worktree") return { url: base2, provider: "github" };
|
|
1941
|
+
return {
|
|
1942
|
+
url: `${base2}/commit/${encodeURIComponent(sha)}`,
|
|
1943
|
+
provider: "github"
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1641
1946
|
const ref = encodePath(resolvedRef(options.ref, options.fallbackRef));
|
|
1642
1947
|
const path = encodePath(options.path || "");
|
|
1643
1948
|
if (!ref || options.kind === "blob" && !path) {
|
|
@@ -1660,6 +1965,61 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1660
1965
|
return { url: `${target}${lineHash}`, provider: "github" };
|
|
1661
1966
|
}
|
|
1662
1967
|
|
|
1968
|
+
// web-src/core/history.ts
|
|
1969
|
+
function parseHistoryLineRange(value) {
|
|
1970
|
+
const match2 = /^(\d+)-(\d+)$/.exec(value || "");
|
|
1971
|
+
if (!match2) return void 0;
|
|
1972
|
+
const a2 = Number(match2[1]);
|
|
1973
|
+
const b2 = Number(match2[2]);
|
|
1974
|
+
if (!(a2 > 0) || !(b2 > 0)) return void 0;
|
|
1975
|
+
return { start: Math.min(a2, b2), end: Math.max(a2, b2) };
|
|
1976
|
+
}
|
|
1977
|
+
function formatHistoryLineRange(range) {
|
|
1978
|
+
return `${range.start}-${range.end}`;
|
|
1979
|
+
}
|
|
1980
|
+
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
1981
|
+
var HISTORY_PAGE_SIZE = 50;
|
|
1982
|
+
var HISTORY_AUTO_LOAD_MAX_PAGES = 20;
|
|
1983
|
+
function commitDiffRange(commit) {
|
|
1984
|
+
return { from: commit.parents[0] || EMPTY_TREE_SHA, to: commit.sha };
|
|
1985
|
+
}
|
|
1986
|
+
function shouldContinueAutoLoad(state) {
|
|
1987
|
+
if (state.found) return false;
|
|
1988
|
+
if (!state.hasMore) return false;
|
|
1989
|
+
return state.pagesLoaded < HISTORY_AUTO_LOAD_MAX_PAGES;
|
|
1990
|
+
}
|
|
1991
|
+
var MONTH_NAMES = [
|
|
1992
|
+
"January",
|
|
1993
|
+
"February",
|
|
1994
|
+
"March",
|
|
1995
|
+
"April",
|
|
1996
|
+
"May",
|
|
1997
|
+
"June",
|
|
1998
|
+
"July",
|
|
1999
|
+
"August",
|
|
2000
|
+
"September",
|
|
2001
|
+
"October",
|
|
2002
|
+
"November",
|
|
2003
|
+
"December"
|
|
2004
|
+
];
|
|
2005
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2006
|
+
function historyGroupLabel(whenIso, now) {
|
|
2007
|
+
const t2 = Date.parse(whenIso);
|
|
2008
|
+
if (!Number.isFinite(t2)) return "Unknown date";
|
|
2009
|
+
const dayStart = new Date(
|
|
2010
|
+
now.getFullYear(),
|
|
2011
|
+
now.getMonth(),
|
|
2012
|
+
now.getDate()
|
|
2013
|
+
).getTime();
|
|
2014
|
+
if (t2 >= dayStart) return "Today";
|
|
2015
|
+
if (t2 >= dayStart - DAY_MS) return "Yesterday";
|
|
2016
|
+
if (t2 >= dayStart - 6 * DAY_MS) return "This week";
|
|
2017
|
+
const d2 = new Date(t2);
|
|
2018
|
+
if (d2.getFullYear() === now.getFullYear() && d2.getMonth() === now.getMonth())
|
|
2019
|
+
return "This month";
|
|
2020
|
+
return `${MONTH_NAMES[d2.getMonth()]} ${d2.getFullYear()}`;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
1663
2023
|
// web-src/core/shell.ts
|
|
1664
2024
|
var MIN_SHELL_COLS = 20;
|
|
1665
2025
|
var MAX_SHELL_COLS = 1e3;
|
|
@@ -1756,6 +2116,7 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1756
2116
|
};
|
|
1757
2117
|
const rawView = params.get("view");
|
|
1758
2118
|
const preview = params.get("preview") === "1";
|
|
2119
|
+
const hl = params.get("hl") || "";
|
|
1759
2120
|
if (rawView === "blob") {
|
|
1760
2121
|
return {
|
|
1761
2122
|
screen: "file",
|
|
@@ -1765,6 +2126,7 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1765
2126
|
view: "blob",
|
|
1766
2127
|
...preview ? { preview: true } : {},
|
|
1767
2128
|
...line ? { line } : {},
|
|
2129
|
+
...line && hl ? { hl } : {},
|
|
1768
2130
|
...params.get("virtual") === "off" ? { virtual: "off" } : {}
|
|
1769
2131
|
};
|
|
1770
2132
|
}
|
|
@@ -1786,6 +2148,9 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1786
2148
|
range,
|
|
1787
2149
|
view: "history",
|
|
1788
2150
|
...params.get("commit") ? { commit: params.get("commit") || "" } : {},
|
|
2151
|
+
...params.get("compare") ? { compare: params.get("compare") || "" } : {},
|
|
2152
|
+
...params.get("q") ? { q: params.get("q") || "" } : {},
|
|
2153
|
+
...parseHistoryLineRange(params.get("lines")) ? { lines: parseHistoryLineRange(params.get("lines")) } : {},
|
|
1789
2154
|
...line ? { line } : {}
|
|
1790
2155
|
};
|
|
1791
2156
|
}
|
|
@@ -1829,10 +2194,18 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1829
2194
|
};
|
|
1830
2195
|
case "/history": {
|
|
1831
2196
|
const commit = params.get("commit") || "";
|
|
2197
|
+
const compare = params.get("compare") || "";
|
|
2198
|
+
const q = params.get("q") || "";
|
|
2199
|
+
const path = params.get("path") || "";
|
|
2200
|
+
const lines = parseHistoryLineRange(params.get("lines"));
|
|
1832
2201
|
return {
|
|
1833
2202
|
screen: "history",
|
|
1834
2203
|
ref: params.get("ref") || "HEAD",
|
|
1835
2204
|
...commit ? { commit } : {},
|
|
2205
|
+
...compare ? { compare } : {},
|
|
2206
|
+
...q ? { q } : {},
|
|
2207
|
+
...path ? { path } : {},
|
|
2208
|
+
...path && lines ? { lines } : {},
|
|
1836
2209
|
range
|
|
1837
2210
|
};
|
|
1838
2211
|
}
|
|
@@ -1891,14 +2264,14 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1891
2264
|
}
|
|
1892
2265
|
case "file":
|
|
1893
2266
|
if (route.view === "blob") {
|
|
1894
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=blob" + (route.preview ? "&preview=1" : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
2267
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=blob" + (route.preview ? "&preview=1" : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.line && route.hl ? `&hl=${encodeURIComponent(route.hl)}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
1895
2268
|
}
|
|
1896
2269
|
if (route.view === "blame") {
|
|
1897
2270
|
const ref = route.ref || "worktree";
|
|
1898
2271
|
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(ref) + "&view=blame" + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
1899
2272
|
}
|
|
1900
2273
|
if (route.view === "history") {
|
|
1901
|
-
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=history" + (route.commit ? `&commit=${encodeURIComponent(route.commit)}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
2274
|
+
return "/file?path=" + encodeURIComponent(route.path) + "&target=" + encodeURIComponent(route.ref || "worktree") + "&view=history" + (route.commit ? `&commit=${encodeURIComponent(route.commit)}` : "") + (route.compare ? `&compare=${encodeURIComponent(route.compare)}` : "") + (route.q ? `&q=${encodeURIComponent(route.q)}` : "") + (route.lines ? `&lines=${encodeURIComponent(formatHistoryLineRange(route.lines))}` : "") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "");
|
|
1902
2275
|
}
|
|
1903
2276
|
return "/file?path=" + encodeURIComponent(route.path) + "&ref=" + encodeURIComponent(route.ref || "worktree") + "&from=" + encodeURIComponent(route.range.from || "") + "&to=" + encodeURIComponent(route.range.to || "worktree") + (route.line ? `&line=${encodeURIComponent(formatLineTarget(route.line))}` : "") + (route.virtual === "off" ? "&virtual=off" : "");
|
|
1904
2277
|
case "diff":
|
|
@@ -1922,7 +2295,12 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1922
2295
|
case "history": {
|
|
1923
2296
|
const params = new URLSearchParams();
|
|
1924
2297
|
if (route.ref && route.ref !== "HEAD") params.set("ref", route.ref);
|
|
2298
|
+
if (route.path) params.set("path", route.path);
|
|
2299
|
+
if (route.path && route.lines)
|
|
2300
|
+
params.set("lines", formatHistoryLineRange(route.lines));
|
|
1925
2301
|
if (route.commit) params.set("commit", route.commit);
|
|
2302
|
+
if (route.compare) params.set("compare", route.compare);
|
|
2303
|
+
if (route.q) params.set("q", route.q);
|
|
1926
2304
|
const qs = params.toString();
|
|
1927
2305
|
return `/history${qs ? `?${qs}` : ""}`;
|
|
1928
2306
|
}
|
|
@@ -1989,6 +2367,64 @@ Details: ${JSON.stringify(output)}` : "";
|
|
|
1989
2367
|
function withTerminalOverlay(url, state) {
|
|
1990
2368
|
return withQueryParam(url, "terminal", state);
|
|
1991
2369
|
}
|
|
2370
|
+
function parseSearchResultsOverlay(search) {
|
|
2371
|
+
return new URLSearchParams(search).get("results");
|
|
2372
|
+
}
|
|
2373
|
+
function withSearchResultsOverlay(url, query) {
|
|
2374
|
+
return withQueryParam(url, "results", query);
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// web-src/core/search-palette.ts
|
|
2378
|
+
var PALETTE_RESULT_LIMIT = 50;
|
|
2379
|
+
var GREP_SELECTION_HISTORY_LIMIT = 100;
|
|
2380
|
+
var MIN_GREP_PALETTE_WIDTH = 720;
|
|
2381
|
+
var MAX_GREP_PALETTE_WIDTH = 2400;
|
|
2382
|
+
var MIN_GREP_PALETTE_HEIGHT = 420;
|
|
2383
|
+
var MAX_GREP_PALETTE_HEIGHT = 1400;
|
|
2384
|
+
function limitPaletteResults(items) {
|
|
2385
|
+
return items.slice(0, PALETTE_RESULT_LIMIT);
|
|
2386
|
+
}
|
|
2387
|
+
function movePaletteSelection(index, count, direction) {
|
|
2388
|
+
if (count <= 0) return -1;
|
|
2389
|
+
if (index < 0) return direction > 0 ? 0 : count - 1;
|
|
2390
|
+
return Math.max(0, Math.min(count - 1, index + direction));
|
|
2391
|
+
}
|
|
2392
|
+
function rememberPaletteSelection(history2, path) {
|
|
2393
|
+
const next = history2.filter((entry) => entry !== path);
|
|
2394
|
+
next.push(path);
|
|
2395
|
+
return next.slice(-GREP_SELECTION_HISTORY_LIMIT);
|
|
2396
|
+
}
|
|
2397
|
+
function rankPaletteResultsByHistory(items, history2) {
|
|
2398
|
+
const priority = new Map(history2.map((path, index) => [path, index + 1]));
|
|
2399
|
+
return items.map((item, index) => ({ item, index })).sort(
|
|
2400
|
+
(a2, b2) => (priority.get(b2.item.path) ?? 0) - (priority.get(a2.item.path) ?? 0) || a2.index - b2.index
|
|
2401
|
+
).map(({ item }) => item);
|
|
2402
|
+
}
|
|
2403
|
+
function parseGrepQuery(raw) {
|
|
2404
|
+
const paths = [];
|
|
2405
|
+
const words = [];
|
|
2406
|
+
for (const token of raw.trim().split(/\s+/)) {
|
|
2407
|
+
if (!token) continue;
|
|
2408
|
+
const scoped = /^path:(.*)$/.exec(token);
|
|
2409
|
+
if (scoped) {
|
|
2410
|
+
if (scoped[1]) paths.push(scoped[1]);
|
|
2411
|
+
continue;
|
|
2412
|
+
}
|
|
2413
|
+
words.push(token);
|
|
2414
|
+
}
|
|
2415
|
+
return { term: words.join(" "), paths };
|
|
2416
|
+
}
|
|
2417
|
+
function buildGrepRequestParams(options) {
|
|
2418
|
+
const params = new URLSearchParams();
|
|
2419
|
+
params.set("ref", options.ref);
|
|
2420
|
+
params.set("q", options.term);
|
|
2421
|
+
params.set("max", String(options.max));
|
|
2422
|
+
if (options.regex) params.set("regex", "1");
|
|
2423
|
+
if (options.caseSensitive) params.set("case", "1");
|
|
2424
|
+
if (options.wholeWord) params.set("word", "1");
|
|
2425
|
+
if (options.hideTests) params.set("exclude_tests", "1");
|
|
2426
|
+
return params;
|
|
2427
|
+
}
|
|
1992
2428
|
|
|
1993
2429
|
// web-src/views/media-player.ts
|
|
1994
2430
|
var PLAYBACK_RATES = [1, 1.25, 1.5, 2];
|
|
@@ -11534,6 +11970,8 @@ ${frontmatter.yaml}
|
|
|
11534
11970
|
header.appendChild(name);
|
|
11535
11971
|
const repositoryWebLink = deps.createRepositoryWebLink?.(target);
|
|
11536
11972
|
if (repositoryWebLink) header.appendChild(repositoryWebLink);
|
|
11973
|
+
const revisionNav = deps.createRevisionNav?.(target, activeTab);
|
|
11974
|
+
if (revisionNav) header.appendChild(revisionNav);
|
|
11537
11975
|
sticky.appendChild(header);
|
|
11538
11976
|
const tabsHost = document.createElement("div");
|
|
11539
11977
|
tabsHost.className = "gdp-file-detail-tabs";
|
|
@@ -11753,7 +12191,8 @@ ${frontmatter.yaml}
|
|
|
11753
12191
|
setRoute: deps.setRoute,
|
|
11754
12192
|
setPreferredSourceTab: deps.setPreferredSourceTab,
|
|
11755
12193
|
createFileBreadcrumb: deps.createFileBreadcrumb,
|
|
11756
|
-
createRepositoryWebLink: deps.createRepositoryWebLink
|
|
12194
|
+
createRepositoryWebLink: deps.createRepositoryWebLink,
|
|
12195
|
+
createRevisionNav: deps.createRevisionNav
|
|
11757
12196
|
},
|
|
11758
12197
|
target,
|
|
11759
12198
|
"blame"
|
|
@@ -12478,6 +12917,7 @@ ${frontmatter.yaml}
|
|
|
12478
12917
|
exportAction: "Export",
|
|
12479
12918
|
foreignKeyHint: "Foreign key — click to view related rows",
|
|
12480
12919
|
relatedEmpty: "No matching row in the referenced table",
|
|
12920
|
+
relatedListResize: "Resize the related-reference list",
|
|
12481
12921
|
filteredEmptyTitle: (count) => `No rows match ${count} active filter${count === 1 ? "" : "s"}`,
|
|
12482
12922
|
filteredEmptyHint: "The table was loaded, but the current search or column filters hide every row.",
|
|
12483
12923
|
filteredEmptyAction: "Clear filters",
|
|
@@ -12835,6 +13275,7 @@ ${frontmatter.yaml}
|
|
|
12835
13275
|
exportAction: "エクスポート",
|
|
12836
13276
|
foreignKeyHint: "外部キー: クリックして関連データを表示",
|
|
12837
13277
|
relatedEmpty: "参照先に該当する行がありません",
|
|
13278
|
+
relatedListResize: "関連参照リストの幅を変える",
|
|
12838
13279
|
filteredEmptyTitle: (count) => `フィルタ ${count} 件に一致する行がありません`,
|
|
12839
13280
|
filteredEmptyHint: "表は読み込めていますが、現在の検索/列フィルタですべての行が隠れています。",
|
|
12840
13281
|
filteredEmptyAction: "フィルタ解除",
|
|
@@ -20087,17 +20528,29 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20087
20528
|
|
|
20088
20529
|
// web-src/views/database/table-grid.ts
|
|
20089
20530
|
var ROW_HEIGHT = 28;
|
|
20531
|
+
var ROWNUM_WIDTH = 50;
|
|
20090
20532
|
var OVERSCAN = 20;
|
|
20091
20533
|
var PAGE_SIZE = 200;
|
|
20092
20534
|
var MAX_PAGE_CACHE_PAGES = 32;
|
|
20093
20535
|
var FILTER_DEBOUNCE_MS = 300;
|
|
20094
20536
|
var DEFAULT_COL_WIDTH = 180;
|
|
20095
20537
|
var CELL_PREVIEW_MAX_CHARS = 4e3;
|
|
20538
|
+
var DETAIL_JSON_HIGHLIGHT_MAX_CHARS = 1e5;
|
|
20096
20539
|
var RELATED_PANEL_DEFAULT_HEIGHT = 320;
|
|
20097
20540
|
var RELATED_PANEL_MIN_HEIGHT = 60;
|
|
20098
20541
|
var DETAIL_PANEL_DEFAULT_HEIGHT = 200;
|
|
20099
20542
|
var DETAIL_PANEL_MIN_HEIGHT = 40;
|
|
20100
20543
|
var PANEL_MAX_RESERVE = 20;
|
|
20544
|
+
var RELATED_LIST_DEFAULT_WIDTH = 200;
|
|
20545
|
+
var RELATED_LIST_MIN_WIDTH = 120;
|
|
20546
|
+
var RELATED_LIST_MAX_WIDTH = 480;
|
|
20547
|
+
var RELATED_LIST_WIDTH_KEY = "code-viewer:db-related-list-width";
|
|
20548
|
+
var ARROW_STEP = {
|
|
20549
|
+
ArrowUp: { row: -1, col: 0 },
|
|
20550
|
+
ArrowDown: { row: 1, col: 0 },
|
|
20551
|
+
ArrowLeft: { row: 0, col: -1 },
|
|
20552
|
+
ArrowRight: { row: 0, col: 1 }
|
|
20553
|
+
};
|
|
20101
20554
|
function createTableGrid(callbacks, options = {}) {
|
|
20102
20555
|
const embedded = options.embedded === true;
|
|
20103
20556
|
const text3 = () => callbacks.getText?.() ?? dbText("en");
|
|
@@ -20222,6 +20675,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20222
20675
|
filterRowWrap.appendChild(filterRow);
|
|
20223
20676
|
const viewport = document.createElement("div");
|
|
20224
20677
|
viewport.className = "db-grid-viewport";
|
|
20678
|
+
viewport.tabIndex = 0;
|
|
20225
20679
|
const spacer = document.createElement("div");
|
|
20226
20680
|
spacer.className = "db-grid-spacer";
|
|
20227
20681
|
const body = document.createElement("div");
|
|
@@ -20325,6 +20779,156 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20325
20779
|
function clearActiveCell() {
|
|
20326
20780
|
setActiveCell(-1, -1);
|
|
20327
20781
|
}
|
|
20782
|
+
function syncHorizontalScroll() {
|
|
20783
|
+
headerWrap.scrollLeft = viewport.scrollLeft;
|
|
20784
|
+
filterRowWrap.scrollLeft = viewport.scrollLeft;
|
|
20785
|
+
}
|
|
20786
|
+
function focusCell(rowIndex, row, colIndex) {
|
|
20787
|
+
setSelectedRow(rowIndex, row);
|
|
20788
|
+
setActiveCell(rowIndex, colIndex);
|
|
20789
|
+
viewport.focus({ preventScroll: true });
|
|
20790
|
+
}
|
|
20791
|
+
function moveActiveCell(rowIndex, colIndex) {
|
|
20792
|
+
const rendered = body.children[rowIndex - renderStartRow];
|
|
20793
|
+
focusCell(rowIndex, rendered ?? null, colIndex);
|
|
20794
|
+
scrollCellIntoView(rowIndex, colIndex);
|
|
20795
|
+
showDetailForActiveCell();
|
|
20796
|
+
}
|
|
20797
|
+
function scrollCellIntoView(rowIndex, colIndex) {
|
|
20798
|
+
const viewHeight = viewport.clientHeight;
|
|
20799
|
+
if (viewHeight > 0) {
|
|
20800
|
+
const top = rowIndex * ROW_HEIGHT;
|
|
20801
|
+
const bottom = top + ROW_HEIGHT;
|
|
20802
|
+
if (top < viewport.scrollTop) viewport.scrollTop = top;
|
|
20803
|
+
else if (bottom > viewport.scrollTop + viewHeight) {
|
|
20804
|
+
viewport.scrollTop = bottom - viewHeight;
|
|
20805
|
+
}
|
|
20806
|
+
}
|
|
20807
|
+
const viewWidth = viewport.clientWidth;
|
|
20808
|
+
if (viewWidth <= 0) return;
|
|
20809
|
+
let left = ROWNUM_WIDTH;
|
|
20810
|
+
for (let c2 = 0; c2 < colIndex; c2++) left += getColWidth(columnNames[c2]);
|
|
20811
|
+
const right = left + getColWidth(columnNames[colIndex]);
|
|
20812
|
+
if (left < viewport.scrollLeft) {
|
|
20813
|
+
viewport.scrollLeft = colIndex === 0 ? 0 : left;
|
|
20814
|
+
} else if (right > viewport.scrollLeft + viewWidth) {
|
|
20815
|
+
viewport.scrollLeft = right - viewWidth;
|
|
20816
|
+
}
|
|
20817
|
+
}
|
|
20818
|
+
function cachedRow(rowIndex) {
|
|
20819
|
+
const pageStart = Math.floor(rowIndex / PAGE_SIZE) * PAGE_SIZE;
|
|
20820
|
+
return getCachedPage(pageStart)?.[rowIndex - pageStart];
|
|
20821
|
+
}
|
|
20822
|
+
function cachedCellValue(rowIndex, colIndex) {
|
|
20823
|
+
const row = cachedRow(rowIndex);
|
|
20824
|
+
return row ? row[colIndex] : void 0;
|
|
20825
|
+
}
|
|
20826
|
+
function showDetailForActiveCell() {
|
|
20827
|
+
const rowIndex = activeCellRowIndex;
|
|
20828
|
+
const colIndex = activeCellColIndex;
|
|
20829
|
+
if (rowIndex < 0 || colIndex < 0) return;
|
|
20830
|
+
const value = cachedCellValue(rowIndex, colIndex);
|
|
20831
|
+
if (value !== void 0) {
|
|
20832
|
+
showCellDetail(colIndex, value);
|
|
20833
|
+
return;
|
|
20834
|
+
}
|
|
20835
|
+
const pageStart = Math.floor(rowIndex / PAGE_SIZE) * PAGE_SIZE;
|
|
20836
|
+
void ensurePage(pageStart).then(() => {
|
|
20837
|
+
if (activeCellRowIndex !== rowIndex || activeCellColIndex !== colIndex) {
|
|
20838
|
+
return;
|
|
20839
|
+
}
|
|
20840
|
+
const loaded = cachedCellValue(rowIndex, colIndex);
|
|
20841
|
+
if (loaded !== void 0) showCellDetail(colIndex, loaded);
|
|
20842
|
+
});
|
|
20843
|
+
}
|
|
20844
|
+
function moveGridFocus(back) {
|
|
20845
|
+
if (back) {
|
|
20846
|
+
if (!embedded || !callbacks.onFocusParentGrid) return false;
|
|
20847
|
+
callbacks.onFocusParentGrid();
|
|
20848
|
+
return true;
|
|
20849
|
+
}
|
|
20850
|
+
if (embedded || !relatedPanel || relatedPanel.hidden || !embeddedGrid) {
|
|
20851
|
+
return false;
|
|
20852
|
+
}
|
|
20853
|
+
embeddedGrid.focusGrid();
|
|
20854
|
+
return true;
|
|
20855
|
+
}
|
|
20856
|
+
function closeOpenPanel() {
|
|
20857
|
+
if (relatedPanel && !relatedPanel.hidden) {
|
|
20858
|
+
hideRelatedPanel();
|
|
20859
|
+
clearActiveCell();
|
|
20860
|
+
return true;
|
|
20861
|
+
}
|
|
20862
|
+
if (!detailPanel.hidden) {
|
|
20863
|
+
detailPanel.hidden = true;
|
|
20864
|
+
clearDetailContent();
|
|
20865
|
+
clearActiveCell();
|
|
20866
|
+
return true;
|
|
20867
|
+
}
|
|
20868
|
+
return false;
|
|
20869
|
+
}
|
|
20870
|
+
function activateActiveCell() {
|
|
20871
|
+
const rowIndex = activeCellRowIndex;
|
|
20872
|
+
const colIndex = activeCellColIndex;
|
|
20873
|
+
if (rowIndex < 0 || colIndex < 0) return false;
|
|
20874
|
+
const rowValues = cachedRow(rowIndex);
|
|
20875
|
+
if (!rowValues) return false;
|
|
20876
|
+
const colName = columnNames[colIndex];
|
|
20877
|
+
const fkClickable = fkColumns.has(colName) && (!embedded || !!callbacks.onForeignKeyCellClick);
|
|
20878
|
+
if (!fkClickable) {
|
|
20879
|
+
showDetailForActiveCell();
|
|
20880
|
+
return true;
|
|
20881
|
+
}
|
|
20882
|
+
if (embedded) {
|
|
20883
|
+
callbacks.onForeignKeyCellClick?.(
|
|
20884
|
+
currentTable,
|
|
20885
|
+
columnNames,
|
|
20886
|
+
rowValues,
|
|
20887
|
+
colName
|
|
20888
|
+
);
|
|
20889
|
+
} else {
|
|
20890
|
+
openRelatedForRow(currentTable, columnNames, rowValues, colName);
|
|
20891
|
+
}
|
|
20892
|
+
return true;
|
|
20893
|
+
}
|
|
20894
|
+
function onViewportKeydown(e2) {
|
|
20895
|
+
if (isImeComposing(e2)) return;
|
|
20896
|
+
if (isEditableKeyTarget(e2.target)) return;
|
|
20897
|
+
if (e2.key === "Tab" && !e2.ctrlKey && !e2.metaKey && !e2.altKey) {
|
|
20898
|
+
if (moveGridFocus(e2.shiftKey)) e2.preventDefault();
|
|
20899
|
+
return;
|
|
20900
|
+
}
|
|
20901
|
+
if (e2.ctrlKey || e2.metaKey || e2.altKey || e2.shiftKey) return;
|
|
20902
|
+
if (e2.key === "Escape") {
|
|
20903
|
+
if (closeOpenPanel()) e2.preventDefault();
|
|
20904
|
+
return;
|
|
20905
|
+
}
|
|
20906
|
+
if (e2.key === "Enter") {
|
|
20907
|
+
if (activateActiveCell()) e2.preventDefault();
|
|
20908
|
+
return;
|
|
20909
|
+
}
|
|
20910
|
+
const step = ARROW_STEP[e2.key];
|
|
20911
|
+
if (!step) return;
|
|
20912
|
+
const lastRow = totalRows - 1;
|
|
20913
|
+
const lastCol = columnNames.length - 1;
|
|
20914
|
+
if (lastRow < 0 || lastCol < 0) return;
|
|
20915
|
+
e2.preventDefault();
|
|
20916
|
+
if (activeCellRowIndex < 0 || activeCellColIndex < 0) {
|
|
20917
|
+
moveActiveCell(Math.min(Math.max(selectedRowIndex, 0), lastRow), 0);
|
|
20918
|
+
return;
|
|
20919
|
+
}
|
|
20920
|
+
const nextRow = Math.min(
|
|
20921
|
+
Math.max(activeCellRowIndex + step.row, 0),
|
|
20922
|
+
lastRow
|
|
20923
|
+
);
|
|
20924
|
+
const nextCol = Math.min(
|
|
20925
|
+
Math.max(activeCellColIndex + step.col, 0),
|
|
20926
|
+
lastCol
|
|
20927
|
+
);
|
|
20928
|
+
if (nextRow === activeCellRowIndex && nextCol === activeCellColIndex)
|
|
20929
|
+
return;
|
|
20930
|
+
moveActiveCell(nextRow, nextCol);
|
|
20931
|
+
}
|
|
20328
20932
|
function clearDetailContent() {
|
|
20329
20933
|
for (const child of Array.from(detailPanel.children)) {
|
|
20330
20934
|
if (child !== detailResize) child.remove();
|
|
@@ -20399,6 +21003,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20399
21003
|
let editingCellCol = -1;
|
|
20400
21004
|
let relatedPanel = null;
|
|
20401
21005
|
let relatedListEl = null;
|
|
21006
|
+
let relatedListResizeEl = null;
|
|
20402
21007
|
let relatedGridHost = null;
|
|
20403
21008
|
let relatedEmptyEl = null;
|
|
20404
21009
|
let relatedCrumbEl = null;
|
|
@@ -20412,6 +21017,20 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20412
21017
|
RELATED_PANEL_MIN_HEIGHT,
|
|
20413
21018
|
Math.min(panelMaxHeight(), savedRelatedHeight)
|
|
20414
21019
|
) : RELATED_PANEL_DEFAULT_HEIGHT;
|
|
21020
|
+
let relatedListWidth = clampRelatedListWidth(
|
|
21021
|
+
readStoredSize(RELATED_LIST_WIDTH_KEY, RELATED_LIST_DEFAULT_WIDTH)
|
|
21022
|
+
);
|
|
21023
|
+
let relatedListResizeDetach = null;
|
|
21024
|
+
function clampRelatedListWidth(width) {
|
|
21025
|
+
return Math.max(
|
|
21026
|
+
RELATED_LIST_MIN_WIDTH,
|
|
21027
|
+
Math.min(RELATED_LIST_MAX_WIDTH, Math.round(width))
|
|
21028
|
+
);
|
|
21029
|
+
}
|
|
21030
|
+
function applyRelatedListWidth(width) {
|
|
21031
|
+
relatedListWidth = clampRelatedListWidth(width);
|
|
21032
|
+
el2.style.setProperty("--db-related-list-w", `${relatedListWidth}px`);
|
|
21033
|
+
}
|
|
20415
21034
|
if (!embedded) {
|
|
20416
21035
|
relatedPanel = document.createElement("div");
|
|
20417
21036
|
relatedPanel.className = "db-related-panel";
|
|
@@ -20591,6 +21210,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20591
21210
|
function resetSelectionAndDetail() {
|
|
20592
21211
|
selectedRowIndex = -1;
|
|
20593
21212
|
selectedRowElement = null;
|
|
21213
|
+
clearActiveCell();
|
|
20594
21214
|
detailPanel.hidden = true;
|
|
20595
21215
|
clearDetailContent();
|
|
20596
21216
|
}
|
|
@@ -20797,6 +21417,24 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20797
21417
|
bodyRow.className = "db-related-body";
|
|
20798
21418
|
relatedListEl = document.createElement("div");
|
|
20799
21419
|
relatedListEl.className = "db-related-list";
|
|
21420
|
+
const listResize = document.createElement("div");
|
|
21421
|
+
listResize.className = "db-related-list-resize";
|
|
21422
|
+
listResize.tabIndex = 0;
|
|
21423
|
+
listResize.setAttribute("role", "separator");
|
|
21424
|
+
listResize.setAttribute("aria-orientation", "vertical");
|
|
21425
|
+
listResize.setAttribute("aria-label", text3().grid.relatedListResize);
|
|
21426
|
+
relatedListResizeEl = listResize;
|
|
21427
|
+
applyRelatedListWidth(relatedListWidth);
|
|
21428
|
+
relatedListResizeDetach = attachDragResizer({
|
|
21429
|
+
handle: listResize,
|
|
21430
|
+
getSize: () => relatedListWidth,
|
|
21431
|
+
applySize: applyRelatedListWidth,
|
|
21432
|
+
direction: 1,
|
|
21433
|
+
axis: "x",
|
|
21434
|
+
onEnd: () => writeStoredSize(RELATED_LIST_WIDTH_KEY, relatedListWidth),
|
|
21435
|
+
activeClassTarget: relatedPanel,
|
|
21436
|
+
activeClassName: "db-related-list-resizing"
|
|
21437
|
+
});
|
|
20800
21438
|
relatedGridHost = document.createElement("div");
|
|
20801
21439
|
relatedGridHost.className = "db-related-grid-host";
|
|
20802
21440
|
embeddedGrid = createTableGrid(
|
|
@@ -20817,7 +21455,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20817
21455
|
getBaseEq: () => relatedEq,
|
|
20818
21456
|
getText: callbacks.getText,
|
|
20819
21457
|
// 埋め込みグリッドの FK クリックで 1 段潜る。
|
|
20820
|
-
onForeignKeyCellClick: (sourceTable, colNames, rowData, clicked) => drillIntoRelated(sourceTable, colNames, rowData, clicked)
|
|
21458
|
+
onForeignKeyCellClick: (sourceTable, colNames, rowData, clicked) => drillIntoRelated(sourceTable, colNames, rowData, clicked),
|
|
21459
|
+
// 埋め込み側で Shift+Tab を押したらメイングリッドへ戻す。
|
|
21460
|
+
onFocusParentGrid: () => viewport.focus({ preventScroll: true })
|
|
20821
21461
|
},
|
|
20822
21462
|
{ embedded: true }
|
|
20823
21463
|
);
|
|
@@ -20827,7 +21467,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20827
21467
|
relatedEmptyEl.textContent = text3().grid.relatedEmpty;
|
|
20828
21468
|
relatedEmptyEl.hidden = true;
|
|
20829
21469
|
relatedGridHost.appendChild(relatedEmptyEl);
|
|
20830
|
-
bodyRow.append(relatedListEl, relatedGridHost);
|
|
21470
|
+
bodyRow.append(relatedListEl, listResize, relatedGridHost);
|
|
20831
21471
|
relatedPanel.append(resizer, header, bodyRow);
|
|
20832
21472
|
}
|
|
20833
21473
|
function startRelatedResize(e2) {
|
|
@@ -20945,7 +21585,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20945
21585
|
if (i2 === level.selectedIndex) item.classList.add("active");
|
|
20946
21586
|
const name = document.createElement("span");
|
|
20947
21587
|
name.className = "db-related-list-name";
|
|
20948
|
-
|
|
21588
|
+
const tableName = document.createElement("span");
|
|
21589
|
+
tableName.className = "db-related-list-table";
|
|
21590
|
+
tableName.textContent = relatedDrillTable(target);
|
|
21591
|
+
tableName.title = relatedDrillTable(target);
|
|
21592
|
+
name.appendChild(tableName);
|
|
20949
21593
|
if (target.fk.inferred) {
|
|
20950
21594
|
const badge = document.createElement("span");
|
|
20951
21595
|
badge.className = "db-related-list-inferred-badge";
|
|
@@ -20955,7 +21599,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20955
21599
|
}
|
|
20956
21600
|
const via = document.createElement("span");
|
|
20957
21601
|
via.className = "db-related-list-via";
|
|
20958
|
-
|
|
21602
|
+
const condition = target.direction === "outgoing" ? `${target.fk.fromColumn} = ${target.value}` : `${target.fk.fromTable}.${target.fk.fromColumn} = ${target.value}`;
|
|
21603
|
+
via.textContent = condition;
|
|
21604
|
+
via.title = condition;
|
|
20959
21605
|
item.append(name, via);
|
|
20960
21606
|
item.addEventListener("click", () => selectRelatedTarget(i2));
|
|
20961
21607
|
relatedListEl?.appendChild(item);
|
|
@@ -20992,9 +21638,42 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
20992
21638
|
grid.showError(err instanceof Error ? err.message : String(err));
|
|
20993
21639
|
}
|
|
20994
21640
|
}
|
|
21641
|
+
let jsonHighlighter = null;
|
|
21642
|
+
let jsonHighlighterRequested = false;
|
|
21643
|
+
let detailJsonPre = null;
|
|
21644
|
+
let detailJsonText = "";
|
|
21645
|
+
function paintJsonHighlight(pre, json) {
|
|
21646
|
+
if (json.length > DETAIL_JSON_HIGHLIGHT_MAX_CHARS) return false;
|
|
21647
|
+
const inner = highlightToInnerHtml(json, "json", jsonHighlighter);
|
|
21648
|
+
if (!inner) return false;
|
|
21649
|
+
pre.innerHTML = inner;
|
|
21650
|
+
return true;
|
|
21651
|
+
}
|
|
21652
|
+
function showJsonDetail(pre, json) {
|
|
21653
|
+
detailJsonPre = pre;
|
|
21654
|
+
detailJsonText = json;
|
|
21655
|
+
if (paintJsonHighlight(pre, json)) return;
|
|
21656
|
+
pre.textContent = json;
|
|
21657
|
+
if (json.length > DETAIL_JSON_HIGHLIGHT_MAX_CHARS) return;
|
|
21658
|
+
ensureJsonHighlighter();
|
|
21659
|
+
}
|
|
21660
|
+
function ensureJsonHighlighter() {
|
|
21661
|
+
if (jsonHighlighterRequested) return;
|
|
21662
|
+
jsonHighlighterRequested = true;
|
|
21663
|
+
void loadShikiHighlighter({
|
|
21664
|
+
themes: ["github-light", "github-dark"],
|
|
21665
|
+
langs: ["json"]
|
|
21666
|
+
}).then((highlighter) => {
|
|
21667
|
+
jsonHighlighter = highlighter;
|
|
21668
|
+
if (!highlighter || !detailJsonPre?.isConnected) return;
|
|
21669
|
+
paintJsonHighlight(detailJsonPre, detailJsonText);
|
|
21670
|
+
});
|
|
21671
|
+
}
|
|
20995
21672
|
function showCellDetail(colIndex, value) {
|
|
20996
21673
|
const colName = columnNames[colIndex];
|
|
20997
21674
|
const colType = columns[colIndex]?.type || "";
|
|
21675
|
+
detailJsonPre = null;
|
|
21676
|
+
detailJsonText = "";
|
|
20998
21677
|
hideRelatedPanel();
|
|
20999
21678
|
detailPanel.hidden = false;
|
|
21000
21679
|
clearDetailContent();
|
|
@@ -21046,7 +21725,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21046
21725
|
const parsed = JSON.parse(str);
|
|
21047
21726
|
const pre = document.createElement("pre");
|
|
21048
21727
|
pre.className = "db-grid-detail-json";
|
|
21049
|
-
pre
|
|
21728
|
+
showJsonDetail(pre, JSON.stringify(parsed, null, 2));
|
|
21050
21729
|
content.appendChild(pre);
|
|
21051
21730
|
} catch {
|
|
21052
21731
|
content.textContent = str;
|
|
@@ -21111,6 +21790,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21111
21790
|
}
|
|
21112
21791
|
renderFilterRow();
|
|
21113
21792
|
syncContentWidth();
|
|
21793
|
+
syncHorizontalScroll();
|
|
21114
21794
|
}
|
|
21115
21795
|
function startResize(colIndex, startEvent) {
|
|
21116
21796
|
cleanupResize();
|
|
@@ -21191,9 +21871,27 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21191
21871
|
invalidateData();
|
|
21192
21872
|
}, FILTER_DEBOUNCE_MS);
|
|
21193
21873
|
}
|
|
21874
|
+
let scrollbarGutterPx = -1;
|
|
21875
|
+
function syncScrollbarGutter() {
|
|
21876
|
+
const outer = viewport.offsetWidth;
|
|
21877
|
+
const inner = viewport.clientWidth;
|
|
21878
|
+
if (!(outer > 0) || !(inner > 0)) return;
|
|
21879
|
+
const gutter = outer - inner;
|
|
21880
|
+
if (gutter === scrollbarGutterPx) return;
|
|
21881
|
+
scrollbarGutterPx = gutter;
|
|
21882
|
+
el2.style.setProperty("--db-grid-scrollbar-w", `${gutter}px`);
|
|
21883
|
+
}
|
|
21884
|
+
function measureContentWidth() {
|
|
21885
|
+
let total = 0;
|
|
21886
|
+
for (const cell of headerRow.children) {
|
|
21887
|
+
total += cell.getBoundingClientRect().width;
|
|
21888
|
+
}
|
|
21889
|
+
return total;
|
|
21890
|
+
}
|
|
21194
21891
|
function syncContentWidth() {
|
|
21195
21892
|
requestAnimationFrame(() => {
|
|
21196
|
-
|
|
21893
|
+
syncScrollbarGutter();
|
|
21894
|
+
const w = measureContentWidth();
|
|
21197
21895
|
if (w > 0) {
|
|
21198
21896
|
spacer.style.minWidth = `${w}px`;
|
|
21199
21897
|
body.style.minWidth = `${w}px`;
|
|
@@ -21393,8 +22091,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21393
22091
|
const fkClickable = fkColumns.has(cellColName) && (!embedded || !!callbacks.onForeignKeyCellClick);
|
|
21394
22092
|
const isEditingThisCell = !ro && i2 === editingCellRow && c2 === editingCellCol;
|
|
21395
22093
|
const activate = () => {
|
|
21396
|
-
|
|
21397
|
-
setActiveCell(rowIndex, cellColIndex);
|
|
22094
|
+
focusCell(rowIndex, row, cellColIndex);
|
|
21398
22095
|
if (fkClickable) {
|
|
21399
22096
|
if (embedded) {
|
|
21400
22097
|
callbacks.onForeignKeyCellClick?.(
|
|
@@ -21486,8 +22183,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21486
22183
|
}
|
|
21487
22184
|
cell.addEventListener("click", (e2) => {
|
|
21488
22185
|
e2.stopPropagation();
|
|
21489
|
-
|
|
21490
|
-
setActiveCell(rowIndex, cellColIndex);
|
|
22186
|
+
focusCell(rowIndex, row, cellColIndex);
|
|
21491
22187
|
if (fkClickable) {
|
|
21492
22188
|
if (embedded) {
|
|
21493
22189
|
callbacks.onForeignKeyCellClick?.(
|
|
@@ -21597,6 +22293,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21597
22293
|
);
|
|
21598
22294
|
}
|
|
21599
22295
|
syncFilteredEmptyState();
|
|
22296
|
+
syncHorizontalScroll();
|
|
21600
22297
|
if (focusRestore) {
|
|
21601
22298
|
const next = body.querySelector(
|
|
21602
22299
|
`.db-grid-cell-input[data-edit-row="${focusRestore.row}"][data-edit-col="${focusRestore.col}"]`
|
|
@@ -21762,11 +22459,22 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21762
22459
|
void refreshCurrentTable();
|
|
21763
22460
|
});
|
|
21764
22461
|
const onViewportScroll = () => {
|
|
21765
|
-
|
|
21766
|
-
filterRowWrap.scrollLeft = viewport.scrollLeft;
|
|
22462
|
+
syncHorizontalScroll();
|
|
21767
22463
|
renderViewport();
|
|
21768
22464
|
};
|
|
22465
|
+
const onWrapScroll = (wrap) => () => {
|
|
22466
|
+
if (wrap.scrollLeft === viewport.scrollLeft) return;
|
|
22467
|
+
viewport.scrollLeft = wrap.scrollLeft;
|
|
22468
|
+
syncHorizontalScroll();
|
|
22469
|
+
};
|
|
22470
|
+
const onHeaderWrapScroll = onWrapScroll(headerWrap);
|
|
22471
|
+
const onFilterWrapScroll = onWrapScroll(filterRowWrap);
|
|
21769
22472
|
viewport.addEventListener("scroll", onViewportScroll, { passive: true });
|
|
22473
|
+
viewport.addEventListener("keydown", onViewportKeydown);
|
|
22474
|
+
headerWrap.addEventListener("scroll", onHeaderWrapScroll, { passive: true });
|
|
22475
|
+
filterRowWrap.addEventListener("scroll", onFilterWrapScroll, {
|
|
22476
|
+
passive: true
|
|
22477
|
+
});
|
|
21770
22478
|
const onCompositionStart = () => {
|
|
21771
22479
|
isComposing = true;
|
|
21772
22480
|
};
|
|
@@ -21780,7 +22488,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21780
22488
|
clear();
|
|
21781
22489
|
embeddedGrid?.destroy();
|
|
21782
22490
|
embeddedGrid = null;
|
|
22491
|
+
relatedListResizeDetach?.();
|
|
22492
|
+
relatedListResizeDetach = null;
|
|
21783
22493
|
viewport.removeEventListener("scroll", onViewportScroll);
|
|
22494
|
+
viewport.removeEventListener("keydown", onViewportKeydown);
|
|
22495
|
+
headerWrap.removeEventListener("scroll", onHeaderWrapScroll);
|
|
22496
|
+
filterRowWrap.removeEventListener("scroll", onFilterWrapScroll);
|
|
21784
22497
|
body.removeEventListener("compositionstart", onCompositionStart);
|
|
21785
22498
|
body.removeEventListener("compositionend", onCompositionEnd);
|
|
21786
22499
|
detailResizeCleanup?.();
|
|
@@ -21804,6 +22517,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21804
22517
|
updateStatus();
|
|
21805
22518
|
}
|
|
21806
22519
|
renderRelatedCrumbs();
|
|
22520
|
+
renderRelatedList();
|
|
22521
|
+
if (relatedListResizeEl) {
|
|
22522
|
+
relatedListResizeEl.setAttribute("aria-label", t2.grid.relatedListResize);
|
|
22523
|
+
}
|
|
21807
22524
|
embeddedGrid?.localize();
|
|
21808
22525
|
}
|
|
21809
22526
|
function rebuildFkColumnsForCurrentTable() {
|
|
@@ -22053,6 +22770,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
22053
22770
|
}
|
|
22054
22771
|
return {
|
|
22055
22772
|
el: el2,
|
|
22773
|
+
focusGrid: () => viewport.focus({ preventScroll: true }),
|
|
22056
22774
|
load,
|
|
22057
22775
|
refresh: refreshCurrentTable,
|
|
22058
22776
|
showError,
|
|
@@ -27022,50 +27740,6 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27022
27740
|
deps.setStatus("live");
|
|
27023
27741
|
}
|
|
27024
27742
|
|
|
27025
|
-
// web-src/core/history.ts
|
|
27026
|
-
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
27027
|
-
var HISTORY_PAGE_SIZE = 50;
|
|
27028
|
-
var HISTORY_AUTO_LOAD_MAX_PAGES = 20;
|
|
27029
|
-
function commitDiffRange(commit) {
|
|
27030
|
-
return { from: commit.parents[0] || EMPTY_TREE_SHA, to: commit.sha };
|
|
27031
|
-
}
|
|
27032
|
-
function shouldContinueAutoLoad(state) {
|
|
27033
|
-
if (state.found) return false;
|
|
27034
|
-
if (!state.hasMore) return false;
|
|
27035
|
-
return state.pagesLoaded < HISTORY_AUTO_LOAD_MAX_PAGES;
|
|
27036
|
-
}
|
|
27037
|
-
var MONTH_NAMES = [
|
|
27038
|
-
"January",
|
|
27039
|
-
"February",
|
|
27040
|
-
"March",
|
|
27041
|
-
"April",
|
|
27042
|
-
"May",
|
|
27043
|
-
"June",
|
|
27044
|
-
"July",
|
|
27045
|
-
"August",
|
|
27046
|
-
"September",
|
|
27047
|
-
"October",
|
|
27048
|
-
"November",
|
|
27049
|
-
"December"
|
|
27050
|
-
];
|
|
27051
|
-
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
27052
|
-
function historyGroupLabel(whenIso, now) {
|
|
27053
|
-
const t2 = Date.parse(whenIso);
|
|
27054
|
-
if (!Number.isFinite(t2)) return "Unknown date";
|
|
27055
|
-
const dayStart = new Date(
|
|
27056
|
-
now.getFullYear(),
|
|
27057
|
-
now.getMonth(),
|
|
27058
|
-
now.getDate()
|
|
27059
|
-
).getTime();
|
|
27060
|
-
if (t2 >= dayStart) return "Today";
|
|
27061
|
-
if (t2 >= dayStart - DAY_MS) return "Yesterday";
|
|
27062
|
-
if (t2 >= dayStart - 6 * DAY_MS) return "This week";
|
|
27063
|
-
const d2 = new Date(t2);
|
|
27064
|
-
if (d2.getFullYear() === now.getFullYear() && d2.getMonth() === now.getMonth())
|
|
27065
|
-
return "This month";
|
|
27066
|
-
return `${MONTH_NAMES[d2.getMonth()]} ${d2.getFullYear()}`;
|
|
27067
|
-
}
|
|
27068
|
-
|
|
27069
27743
|
// web-src/views/history-view.ts
|
|
27070
27744
|
var HISTORY_BODY_COLLAPSE_LINES = 10;
|
|
27071
27745
|
var HISTORY_WORKTREE_COMMIT = "worktree";
|
|
@@ -27078,7 +27752,21 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27078
27752
|
refreshTitle: "Refresh commit history",
|
|
27079
27753
|
refreshTitlePending: "History may have changed. Refresh",
|
|
27080
27754
|
filterClearLabel: "Clear",
|
|
27081
|
-
filterClearTitle: "Clear commit filter"
|
|
27755
|
+
filterClearTitle: "Clear commit filter",
|
|
27756
|
+
filterPlaceholder: "filter… text author: path: since: code:",
|
|
27757
|
+
filterTitle: 'Filter commits. Words match the message ("quoted" keeps spaces), sha prefixes match commits, author:<name>, path:<part>, since:/after:<date>, until:/before:<date>, code:<text> (lines added or removed), merges:no / merges:only. Kinds combine with AND.',
|
|
27758
|
+
commitsTitle: "Commits",
|
|
27759
|
+
commitsIn: (path) => `Commits · ${path}`,
|
|
27760
|
+
commitsForLines: (start, end) => start === end ? `Commits · line ${start}` : `Commits · lines ${start}-${end}`,
|
|
27761
|
+
copyShaTitle: "Copy full commit sha",
|
|
27762
|
+
copiedTitle: "Copied",
|
|
27763
|
+
copyFailedTitle: "Copy failed",
|
|
27764
|
+
mergeBadge: "merge",
|
|
27765
|
+
compareWith: "Compare with",
|
|
27766
|
+
parentLabel: (index, shortSha) => `parent ${index} (${shortSha})`,
|
|
27767
|
+
comparing: (from, to) => `Comparing ${from}..${to}`,
|
|
27768
|
+
clearCompare: "Clear",
|
|
27769
|
+
refTitle: (kind, name) => kind === "tag" ? `Tag ${name}` : kind === "head" ? "HEAD" : `Branch ${name}`
|
|
27082
27770
|
},
|
|
27083
27771
|
ja: {
|
|
27084
27772
|
worktreeLabel: "未コミット変更 (Working tree)",
|
|
@@ -27088,7 +27776,21 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27088
27776
|
refreshTitle: "コミット履歴を更新",
|
|
27089
27777
|
refreshTitlePending: "新しい履歴がある可能性があります。更新",
|
|
27090
27778
|
filterClearLabel: "解除",
|
|
27091
|
-
filterClearTitle: "コミットフィルタを解除"
|
|
27779
|
+
filterClearTitle: "コミットフィルタを解除",
|
|
27780
|
+
filterPlaceholder: "絞り込み… 文字列 author: path: since: code:",
|
|
27781
|
+
filterTitle: 'コミットを絞り込みます。語はメッセージに一致("引用" で空白を含む句)、sha の前方一致、author:<名前>、path:<一部>、since:/after:<日付>、until:/before:<日付>、code:<文字列>(追加・削除された行)、merges:no / merges:only。種類が違う条件は AND。',
|
|
27782
|
+
commitsTitle: "コミット",
|
|
27783
|
+
commitsIn: (path) => `コミット · ${path}`,
|
|
27784
|
+
commitsForLines: (start, end) => start === end ? `コミット · ${start} 行目` : `コミット · ${start}-${end} 行`,
|
|
27785
|
+
copyShaTitle: "コミットの sha をコピー",
|
|
27786
|
+
copiedTitle: "コピーしました",
|
|
27787
|
+
copyFailedTitle: "コピーに失敗しました",
|
|
27788
|
+
mergeBadge: "merge",
|
|
27789
|
+
compareWith: "比較対象",
|
|
27790
|
+
parentLabel: (index, shortSha) => `親 ${index} (${shortSha})`,
|
|
27791
|
+
comparing: (from, to) => `${from}..${to} を比較中`,
|
|
27792
|
+
clearCompare: "解除",
|
|
27793
|
+
refTitle: (kind, name) => kind === "tag" ? `タグ ${name}` : kind === "head" ? "HEAD" : `ブランチ ${name}`
|
|
27092
27794
|
}
|
|
27093
27795
|
};
|
|
27094
27796
|
function historyText(lang) {
|
|
@@ -27108,6 +27810,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27108
27810
|
panel.className = "gdp-file-history-panel";
|
|
27109
27811
|
}
|
|
27110
27812
|
panel.setAttribute("aria-label", "Commit history");
|
|
27813
|
+
panel.tabIndex = -1;
|
|
27111
27814
|
const panelHead = document.createElement("div");
|
|
27112
27815
|
panelHead.className = "history-head";
|
|
27113
27816
|
const title = document.createElement("span");
|
|
@@ -27143,8 +27846,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27143
27846
|
if (page) filterInput.id = "history-filter";
|
|
27144
27847
|
else filterInput.className = "history-filter";
|
|
27145
27848
|
filterInput.type = "search";
|
|
27146
|
-
filterInput.placeholder =
|
|
27849
|
+
filterInput.placeholder = HISTORY_TEXT.en.filterPlaceholder;
|
|
27850
|
+
filterInput.title = HISTORY_TEXT.en.filterTitle;
|
|
27147
27851
|
filterInput.autocomplete = "off";
|
|
27852
|
+
const authorList = document.createElement("datalist");
|
|
27853
|
+
authorList.id = page ? "history-filter-authors" : `history-filter-authors-${Math.random().toString(36).slice(2, 8)}`;
|
|
27854
|
+
filterInput.setAttribute("list", authorList.id);
|
|
27148
27855
|
const filterClearButton = document.createElement("button");
|
|
27149
27856
|
filterClearButton.type = "button";
|
|
27150
27857
|
if (page) filterClearButton.id = "history-filter-clear";
|
|
@@ -27156,7 +27863,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27156
27863
|
"aria-label",
|
|
27157
27864
|
HISTORY_TEXT.en.filterClearTitle
|
|
27158
27865
|
);
|
|
27159
|
-
filterWrap.append(filterInput, filterClearButton);
|
|
27866
|
+
filterWrap.append(filterInput, filterClearButton, authorList);
|
|
27160
27867
|
const banner = document.createElement("div");
|
|
27161
27868
|
if (page) banner.id = "history-banner";
|
|
27162
27869
|
banner.className = "history-banner";
|
|
@@ -27183,6 +27890,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27183
27890
|
sentinel,
|
|
27184
27891
|
filterInput,
|
|
27185
27892
|
filterClearButton,
|
|
27893
|
+
authorList,
|
|
27186
27894
|
refreshButton,
|
|
27187
27895
|
refreshResult
|
|
27188
27896
|
};
|
|
@@ -27206,7 +27914,9 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27206
27914
|
const date = document.createElement("span");
|
|
27207
27915
|
if (page) date.id = "hci-date";
|
|
27208
27916
|
date.className = "hci-date";
|
|
27209
|
-
|
|
27917
|
+
const actions = document.createElement("span");
|
|
27918
|
+
actions.className = "hci-actions";
|
|
27919
|
+
head.append(sha, author, date, actions);
|
|
27210
27920
|
const subject = document.createElement("h2");
|
|
27211
27921
|
if (page) subject.id = "hci-subject";
|
|
27212
27922
|
subject.className = "hci-subject";
|
|
@@ -27308,6 +28018,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27308
28018
|
let pathFilter = "";
|
|
27309
28019
|
let refreshStatus = { type: "none" };
|
|
27310
28020
|
let freshSha = "";
|
|
28021
|
+
let compareSha = "";
|
|
28022
|
+
let authorsLoadedFor = "";
|
|
28023
|
+
let lineRange2;
|
|
28024
|
+
function lineRangeKey(range) {
|
|
28025
|
+
return range ? formatHistoryLineRange(range) : "";
|
|
28026
|
+
}
|
|
27311
28027
|
function historyScopeFromRoute(route = deps.getRoute()) {
|
|
27312
28028
|
if (route.screen === "history") {
|
|
27313
28029
|
const nextRef = route.ref || "HEAD";
|
|
@@ -27315,8 +28031,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27315
28031
|
mode: "history",
|
|
27316
28032
|
logRef: nextRef,
|
|
27317
28033
|
routeRef: nextRef,
|
|
27318
|
-
pathFilter: "",
|
|
27319
|
-
|
|
28034
|
+
pathFilter: route.path || "",
|
|
28035
|
+
query: route.q || "",
|
|
28036
|
+
lines: route.path ? route.lines : void 0,
|
|
28037
|
+
commit: route.commit,
|
|
28038
|
+
compare: route.compare
|
|
27320
28039
|
};
|
|
27321
28040
|
}
|
|
27322
28041
|
if (route.screen === "file" && route.view === "history") {
|
|
@@ -27326,11 +28045,51 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27326
28045
|
logRef: nextRouteRef === "worktree" ? "HEAD" : nextRouteRef,
|
|
27327
28046
|
routeRef: nextRouteRef,
|
|
27328
28047
|
pathFilter: route.path,
|
|
27329
|
-
|
|
28048
|
+
query: route.q || "",
|
|
28049
|
+
lines: route.lines,
|
|
28050
|
+
commit: route.commit,
|
|
28051
|
+
compare: route.compare
|
|
27330
28052
|
};
|
|
27331
28053
|
}
|
|
27332
28054
|
return null;
|
|
27333
28055
|
}
|
|
28056
|
+
function routeFor(options) {
|
|
28057
|
+
const commit = options.commit;
|
|
28058
|
+
const compare = options.compare || void 0;
|
|
28059
|
+
const q = (options.query ?? query) || void 0;
|
|
28060
|
+
const range = commit && commit !== HISTORY_WORKTREE_COMMIT ? diffRangeFor(commit, compare) : worktreeDiffRange();
|
|
28061
|
+
if (mode === "file") {
|
|
28062
|
+
return {
|
|
28063
|
+
screen: "file",
|
|
28064
|
+
path: pathFilter,
|
|
28065
|
+
ref: options.ref ?? routeRef,
|
|
28066
|
+
view: "history",
|
|
28067
|
+
...commit ? { commit } : {},
|
|
28068
|
+
...compare ? { compare } : {},
|
|
28069
|
+
...q ? { q } : {},
|
|
28070
|
+
...lineRange2 ? { lines: lineRange2 } : {},
|
|
28071
|
+
range
|
|
28072
|
+
};
|
|
28073
|
+
}
|
|
28074
|
+
return {
|
|
28075
|
+
screen: "history",
|
|
28076
|
+
ref: options.ref ?? ref,
|
|
28077
|
+
...pathFilter ? { path: pathFilter } : {},
|
|
28078
|
+
...pathFilter && lineRange2 ? { lines: lineRange2 } : {},
|
|
28079
|
+
...commit ? { commit } : {},
|
|
28080
|
+
...compare ? { compare } : {},
|
|
28081
|
+
...q ? { q } : {},
|
|
28082
|
+
range
|
|
28083
|
+
};
|
|
28084
|
+
}
|
|
28085
|
+
function diffRangeFor(sha, compare) {
|
|
28086
|
+
const commit = commits.find((item) => item.sha === sha);
|
|
28087
|
+
if (compare) return { from: compare, to: sha };
|
|
28088
|
+
return commit ? commitDiffRange(commit) : { from: `${sha}^`, to: sha };
|
|
28089
|
+
}
|
|
28090
|
+
function shortSha(sha) {
|
|
28091
|
+
return sha.slice(0, 7);
|
|
28092
|
+
}
|
|
27334
28093
|
function currentRefreshScopeKey() {
|
|
27335
28094
|
const scope = historyScopeFromRoute();
|
|
27336
28095
|
if (!scope) return "";
|
|
@@ -27339,6 +28098,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27339
28098
|
scope.logRef,
|
|
27340
28099
|
scope.routeRef,
|
|
27341
28100
|
scope.pathFilter,
|
|
28101
|
+
lineRangeKey(scope.lines),
|
|
27342
28102
|
query
|
|
27343
28103
|
].join("\0");
|
|
27344
28104
|
}
|
|
@@ -27420,6 +28180,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27420
28180
|
if (query) params.set("q", query);
|
|
27421
28181
|
if (pathFilter) {
|
|
27422
28182
|
params.set("path", pathFilter);
|
|
28183
|
+
if (lineRange2) params.set("lines", formatHistoryLineRange(lineRange2));
|
|
27423
28184
|
if (routeRef === "worktree") params.set("worktree", "1");
|
|
27424
28185
|
}
|
|
27425
28186
|
const url = `/_log?${params.toString()}`;
|
|
@@ -27436,10 +28197,43 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27436
28197
|
return null;
|
|
27437
28198
|
});
|
|
27438
28199
|
}
|
|
27439
|
-
function
|
|
28200
|
+
function refChipsHtml(commit) {
|
|
28201
|
+
const text3 = historyText(deps.getLanguage());
|
|
28202
|
+
const chips = [];
|
|
28203
|
+
for (const ref2 of commit.refs ?? []) {
|
|
28204
|
+
const classes = [
|
|
28205
|
+
"history-ref",
|
|
28206
|
+
`history-ref-${ref2.kind}`,
|
|
28207
|
+
ref2.head ? "history-ref-head" : ""
|
|
28208
|
+
].filter(Boolean).join(" ");
|
|
28209
|
+
chips.push(
|
|
28210
|
+
`<span class="${classes}" title="${deps.escapeHtml(text3.refTitle(ref2.kind, ref2.name))}">${deps.escapeHtml(ref2.name)}</span>`
|
|
28211
|
+
);
|
|
28212
|
+
}
|
|
28213
|
+
if (commit.parents.length > 1) {
|
|
28214
|
+
chips.push(
|
|
28215
|
+
`<span class="history-ref history-ref-merge" title="${deps.escapeHtml(text3.mergeBadge)}">${deps.escapeHtml(text3.mergeBadge)}</span>`
|
|
28216
|
+
);
|
|
28217
|
+
}
|
|
28218
|
+
return chips.length ? `<span class="history-refs">${chips.join("")}</span>` : "";
|
|
28219
|
+
}
|
|
28220
|
+
function commitRow(commit, inRange) {
|
|
27440
28221
|
const active = commit.sha === selectedSha ? " active" : "";
|
|
27441
28222
|
const fresh = commit.sha === freshSha ? " history-item-fresh" : "";
|
|
27442
|
-
|
|
28223
|
+
const ranged = inRange ? " history-item-in-range" : "";
|
|
28224
|
+
return `<li class="history-item${active}${fresh}${ranged}" data-sha="${deps.escapeHtml(commit.sha)}"><span class="subject" title="${deps.escapeHtml(commit.subject)}">${deps.escapeHtml(commit.subject)}</span><span class="meta2"><span class="sha">${deps.escapeHtml(shortSha(commit.sha))}</span><span class="author">${deps.escapeHtml(commit.author)}</span><span class="when">${deps.escapeHtml(displayWhen(commit.when))}</span>` + refChipsHtml(commit) + `</span></li>`;
|
|
28225
|
+
}
|
|
28226
|
+
function rangeShas() {
|
|
28227
|
+
const shas = /* @__PURE__ */ new Set();
|
|
28228
|
+
if (!compareSha || !selectedSha) return shas;
|
|
28229
|
+
const start = commits.findIndex((commit) => commit.sha === selectedSha);
|
|
28230
|
+
if (start < 0) return shas;
|
|
28231
|
+
const end = commits.findIndex(
|
|
28232
|
+
(commit, index) => index >= start && commit.parents[0] === compareSha
|
|
28233
|
+
);
|
|
28234
|
+
if (end < 0) return shas;
|
|
28235
|
+
for (let index = start; index <= end; index++) shas.add(commits[index].sha);
|
|
28236
|
+
return shas;
|
|
27443
28237
|
}
|
|
27444
28238
|
function worktreeRow() {
|
|
27445
28239
|
const active = selectedSha === HISTORY_WORKTREE_COMMIT ? " active" : "";
|
|
@@ -27450,6 +28244,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27450
28244
|
syncRefreshResult(activeMount.refreshResult);
|
|
27451
28245
|
const now = /* @__PURE__ */ new Date();
|
|
27452
28246
|
const html = mode === "history" ? [worktreeRow()] : [];
|
|
28247
|
+
const inRange = rangeShas();
|
|
27453
28248
|
let lastGroup = "";
|
|
27454
28249
|
for (const commit of commits) {
|
|
27455
28250
|
if (commit.sha === HISTORY_WORKTREE_COMMIT) {
|
|
@@ -27463,7 +28258,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27463
28258
|
);
|
|
27464
28259
|
lastGroup = group;
|
|
27465
28260
|
}
|
|
27466
|
-
html.push(commitRow(commit));
|
|
28261
|
+
html.push(commitRow(commit, inRange.has(commit.sha)));
|
|
27467
28262
|
}
|
|
27468
28263
|
list2.innerHTML = html.join("");
|
|
27469
28264
|
activeHistoryRow = list2.querySelector(".history-item.active");
|
|
@@ -27513,6 +28308,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27513
28308
|
info.querySelector(".hci-head")?.removeAttribute("hidden");
|
|
27514
28309
|
set2(".hci-sha", commit.sha);
|
|
27515
28310
|
set2(".hci-author", commit.author);
|
|
28311
|
+
renderCommitActions(info, commit);
|
|
27516
28312
|
const t2 = Date.parse(commit.when);
|
|
27517
28313
|
set2(
|
|
27518
28314
|
".hci-date",
|
|
@@ -27539,6 +28335,80 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27539
28335
|
}
|
|
27540
28336
|
info.hidden = false;
|
|
27541
28337
|
}
|
|
28338
|
+
function renderCommitActions(info, commit) {
|
|
28339
|
+
const actions = info.querySelector(".hci-actions");
|
|
28340
|
+
if (!actions) return;
|
|
28341
|
+
actions.replaceChildren();
|
|
28342
|
+
const text3 = historyText(deps.getLanguage());
|
|
28343
|
+
if (deps.copyText) {
|
|
28344
|
+
const copy = document.createElement("button");
|
|
28345
|
+
copy.type = "button";
|
|
28346
|
+
copy.className = "gdp-btn gdp-btn-sm hci-copy";
|
|
28347
|
+
copy.title = text3.copyShaTitle;
|
|
28348
|
+
copy.setAttribute("aria-label", text3.copyShaTitle);
|
|
28349
|
+
copy.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
|
|
28350
|
+
let resetTimer = null;
|
|
28351
|
+
const copyText = deps.copyText;
|
|
28352
|
+
copy.addEventListener("click", () => {
|
|
28353
|
+
void copyText(commit.sha).then(() => {
|
|
28354
|
+
copy.innerHTML = iconSvg("octicon-check", CHECK_16_PATHS);
|
|
28355
|
+
copy.title = text3.copiedTitle;
|
|
28356
|
+
copy.classList.add("copied");
|
|
28357
|
+
}).catch((err) => {
|
|
28358
|
+
console.error("Failed to copy commit sha", err);
|
|
28359
|
+
copy.title = text3.copyFailedTitle;
|
|
28360
|
+
copy.classList.add("failed");
|
|
28361
|
+
}).finally(() => {
|
|
28362
|
+
if (resetTimer) clearTimeout(resetTimer);
|
|
28363
|
+
resetTimer = setTimeout(() => {
|
|
28364
|
+
copy.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
|
|
28365
|
+
copy.title = text3.copyShaTitle;
|
|
28366
|
+
copy.classList.remove("copied", "failed");
|
|
28367
|
+
}, 1200);
|
|
28368
|
+
});
|
|
28369
|
+
});
|
|
28370
|
+
actions.appendChild(copy);
|
|
28371
|
+
}
|
|
28372
|
+
const link2 = deps.commitWebLink?.(commit.sha);
|
|
28373
|
+
if (link2) {
|
|
28374
|
+
link2.classList.add("hci-link");
|
|
28375
|
+
actions.appendChild(link2);
|
|
28376
|
+
}
|
|
28377
|
+
if (commit.parents.length > 1) {
|
|
28378
|
+
const label = document.createElement("span");
|
|
28379
|
+
label.className = "hci-compare-label";
|
|
28380
|
+
label.textContent = text3.compareWith;
|
|
28381
|
+
actions.appendChild(label);
|
|
28382
|
+
commit.parents.forEach((parent, index) => {
|
|
28383
|
+
const button = document.createElement("button");
|
|
28384
|
+
button.type = "button";
|
|
28385
|
+
button.className = "gdp-btn gdp-btn-sm hci-parent";
|
|
28386
|
+
button.textContent = text3.parentLabel(index + 1, shortSha(parent));
|
|
28387
|
+
const active = compareSha ? compareSha === parent : index === 0;
|
|
28388
|
+
button.setAttribute("aria-pressed", String(active));
|
|
28389
|
+
button.addEventListener("click", () => {
|
|
28390
|
+
void selectCommit(commit, { compare: index === 0 ? "" : parent });
|
|
28391
|
+
});
|
|
28392
|
+
actions.appendChild(button);
|
|
28393
|
+
});
|
|
28394
|
+
}
|
|
28395
|
+
if (compareSha && !commit.parents.includes(compareSha)) {
|
|
28396
|
+
const label = document.createElement("span");
|
|
28397
|
+
label.className = "hci-compare-label";
|
|
28398
|
+
label.textContent = text3.comparing(
|
|
28399
|
+
shortSha(compareSha),
|
|
28400
|
+
shortSha(commit.sha)
|
|
28401
|
+
);
|
|
28402
|
+
const clear = document.createElement("button");
|
|
28403
|
+
clear.type = "button";
|
|
28404
|
+
clear.className = "gdp-btn gdp-btn-sm hci-compare-clear";
|
|
28405
|
+
clear.textContent = text3.clearCompare;
|
|
28406
|
+
clear.addEventListener("click", () => {
|
|
28407
|
+
void selectCommit(commit, { compare: "" });
|
|
28408
|
+
});
|
|
28409
|
+
actions.append(label, clear);
|
|
28410
|
+
}
|
|
28411
|
+
}
|
|
27542
28412
|
function updateWorktreeInfo() {
|
|
27543
28413
|
const info = commitInfoElement();
|
|
27544
28414
|
if (!info) return;
|
|
@@ -27564,11 +28434,16 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27564
28434
|
}
|
|
27565
28435
|
activeHistoryRow = selectedSha ? list2.querySelector(historyItemSelector(selectedSha)) : null;
|
|
27566
28436
|
activeHistoryRow?.classList.add("active");
|
|
28437
|
+
updateRangeRows();
|
|
27567
28438
|
}
|
|
27568
|
-
function
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
28439
|
+
function updateRangeRows() {
|
|
28440
|
+
const inRange = rangeShas();
|
|
28441
|
+
list2.querySelectorAll(".history-item").forEach((row) => {
|
|
28442
|
+
row.classList.toggle(
|
|
28443
|
+
"history-item-in-range",
|
|
28444
|
+
inRange.has(row.dataset.sha || "")
|
|
28445
|
+
);
|
|
28446
|
+
});
|
|
27572
28447
|
}
|
|
27573
28448
|
function selectableShas() {
|
|
27574
28449
|
const shas = mode === "history" ? [HISTORY_WORKTREE_COMMIT] : [];
|
|
@@ -27630,63 +28505,46 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27630
28505
|
const selectionGen = ++selectionGeneration;
|
|
27631
28506
|
const gen = generation;
|
|
27632
28507
|
selectedSha = commit.sha;
|
|
28508
|
+
compareSha = options.compare ?? "";
|
|
28509
|
+
if (compareSha === commit.parents[0]) compareSha = "";
|
|
27633
28510
|
updateActiveRow();
|
|
27634
28511
|
await updateCommitInfo(commit);
|
|
27635
28512
|
if (selectionGen !== selectionGeneration || gen !== generation) return;
|
|
28513
|
+
const range = compareSha ? { from: compareSha, to: commit.sha } : commitDiffRange(commit);
|
|
27636
28514
|
if (options.updateUrl !== false) {
|
|
27637
|
-
|
|
27638
|
-
|
|
27639
|
-
|
|
27640
|
-
|
|
27641
|
-
screen: "file",
|
|
27642
|
-
path: pathFilter,
|
|
27643
|
-
ref: routeRef,
|
|
27644
|
-
view: "history",
|
|
27645
|
-
commit: commit.sha,
|
|
27646
|
-
range
|
|
27647
|
-
},
|
|
27648
|
-
true
|
|
27649
|
-
);
|
|
27650
|
-
} else {
|
|
27651
|
-
deps.setRoute(
|
|
27652
|
-
{ screen: "history", ref, commit: commit.sha, range },
|
|
27653
|
-
true
|
|
27654
|
-
);
|
|
27655
|
-
}
|
|
28515
|
+
deps.setRoute(
|
|
28516
|
+
routeFor({ commit: commit.sha, compare: compareSha }),
|
|
28517
|
+
true
|
|
28518
|
+
);
|
|
27656
28519
|
}
|
|
27657
28520
|
if (selectionGen !== selectionGeneration || gen !== generation) return;
|
|
27658
|
-
await deps.applyCommitRange(
|
|
27659
|
-
commitDiffRange(commit),
|
|
27660
|
-
pathFilter || void 0
|
|
27661
|
-
);
|
|
28521
|
+
await deps.applyCommitRange(range, pathFilter || void 0);
|
|
27662
28522
|
if (selectionGen !== selectionGeneration || gen !== generation) return;
|
|
27663
28523
|
}
|
|
28524
|
+
async function selectRange(clicked) {
|
|
28525
|
+
const anchor = commits.find((item) => item.sha === selectedSha);
|
|
28526
|
+
if (!anchor || anchor.sha === clicked.sha) {
|
|
28527
|
+
await selectCommit(clicked, { compare: "" });
|
|
28528
|
+
return;
|
|
28529
|
+
}
|
|
28530
|
+
const anchorIndex = commits.indexOf(anchor);
|
|
28531
|
+
const clickedIndex = commits.indexOf(clicked);
|
|
28532
|
+
const newer = anchorIndex < clickedIndex ? anchor : clicked;
|
|
28533
|
+
const older = newer === anchor ? clicked : anchor;
|
|
28534
|
+
await selectCommit(newer, {
|
|
28535
|
+
compare: older.parents[0] || EMPTY_TREE_SHA
|
|
28536
|
+
});
|
|
28537
|
+
}
|
|
27664
28538
|
async function selectWorktree(options = {}) {
|
|
27665
28539
|
const selectionGen = ++selectionGeneration;
|
|
27666
28540
|
const gen = generation;
|
|
27667
28541
|
selectedSha = HISTORY_WORKTREE_COMMIT;
|
|
28542
|
+
compareSha = "";
|
|
27668
28543
|
updateActiveRow();
|
|
27669
28544
|
updateWorktreeInfo();
|
|
27670
28545
|
const range = worktreeDiffRange();
|
|
27671
28546
|
if (options.updateUrl !== false) {
|
|
27672
|
-
|
|
27673
|
-
deps.setRoute(
|
|
27674
|
-
{
|
|
27675
|
-
screen: "file",
|
|
27676
|
-
path: pathFilter,
|
|
27677
|
-
ref: routeRef,
|
|
27678
|
-
view: "history",
|
|
27679
|
-
commit: selectedSha,
|
|
27680
|
-
range
|
|
27681
|
-
},
|
|
27682
|
-
true
|
|
27683
|
-
);
|
|
27684
|
-
} else {
|
|
27685
|
-
deps.setRoute(
|
|
27686
|
-
{ screen: "history", ref, commit: selectedSha, range },
|
|
27687
|
-
true
|
|
27688
|
-
);
|
|
27689
|
-
}
|
|
28547
|
+
deps.setRoute(routeFor({ commit: selectedSha }), true);
|
|
27690
28548
|
}
|
|
27691
28549
|
if (selectionGen !== selectionGeneration || gen !== generation) return;
|
|
27692
28550
|
await deps.applyCommitRange(range, pathFilter || void 0);
|
|
@@ -27724,10 +28582,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27724
28582
|
} else {
|
|
27725
28583
|
pagesLoaded = 1;
|
|
27726
28584
|
}
|
|
28585
|
+
const compare = historyScopeFromRoute()?.compare || "";
|
|
27727
28586
|
for (; ; ) {
|
|
27728
28587
|
const found = commits.find((c2) => c2.sha.startsWith(sha));
|
|
27729
28588
|
if (found) {
|
|
27730
|
-
await selectCommit(found, { updateUrl: false });
|
|
28589
|
+
await selectCommit(found, { updateUrl: false, compare });
|
|
27731
28590
|
scrollToSelected();
|
|
27732
28591
|
return;
|
|
27733
28592
|
}
|
|
@@ -27751,7 +28610,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27751
28610
|
setBanner(`showing commit outside the loaded ${ref} log`);
|
|
27752
28611
|
commits = [single, ...commits];
|
|
27753
28612
|
renderList();
|
|
27754
|
-
await selectCommit(single, { updateUrl: false });
|
|
28613
|
+
await selectCommit(single, { updateUrl: false, compare });
|
|
27755
28614
|
scrollToSelected();
|
|
27756
28615
|
}
|
|
27757
28616
|
function scrollToSelected() {
|
|
@@ -27768,14 +28627,19 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27768
28627
|
async function doEnterHistory(force = false) {
|
|
27769
28628
|
const scope = historyScopeFromRoute();
|
|
27770
28629
|
if (!scope) return;
|
|
27771
|
-
const scopeChanged = scope.logRef !== ref || scope.routeRef !== routeRef || scope.pathFilter !== pathFilter || scope.mode !== mode;
|
|
28630
|
+
const scopeChanged = scope.logRef !== ref || scope.routeRef !== routeRef || scope.pathFilter !== pathFilter || lineRangeKey(scope.lines) !== lineRangeKey(lineRange2) || scope.query !== query || scope.mode !== mode;
|
|
27772
28631
|
if (scopeChanged || force || commits.length === 0) {
|
|
27773
28632
|
generation++;
|
|
27774
28633
|
const gen = generation;
|
|
27775
28634
|
ref = scope.logRef;
|
|
27776
28635
|
routeRef = scope.routeRef;
|
|
27777
28636
|
pathFilter = scope.pathFilter;
|
|
28637
|
+
lineRange2 = scope.lines;
|
|
28638
|
+
query = scope.query;
|
|
27778
28639
|
mode = scope.mode;
|
|
28640
|
+
compareSha = "";
|
|
28641
|
+
syncFilterInputValue();
|
|
28642
|
+
syncPanelTitle();
|
|
27779
28643
|
commits = [];
|
|
27780
28644
|
hasMore = false;
|
|
27781
28645
|
loading = false;
|
|
@@ -27805,33 +28669,19 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27805
28669
|
}
|
|
27806
28670
|
function onRefPicked(nextRef) {
|
|
27807
28671
|
const value = nextRef && nextRef !== "worktree" ? nextRef : "HEAD";
|
|
27808
|
-
|
|
27809
|
-
|
|
27810
|
-
|
|
27811
|
-
|
|
27812
|
-
|
|
27813
|
-
|
|
27814
|
-
view: "history",
|
|
27815
|
-
range: { from: "HEAD", to: "worktree" }
|
|
27816
|
-
},
|
|
27817
|
-
false
|
|
27818
|
-
);
|
|
27819
|
-
} else {
|
|
27820
|
-
deps.setRoute(
|
|
27821
|
-
{
|
|
27822
|
-
screen: "history",
|
|
27823
|
-
ref: value,
|
|
27824
|
-
range: { from: "HEAD", to: "worktree" }
|
|
27825
|
-
},
|
|
27826
|
-
false
|
|
27827
|
-
);
|
|
27828
|
-
}
|
|
28672
|
+
deps.setRoute(
|
|
28673
|
+
routeFor({
|
|
28674
|
+
ref: mode === "file" ? nextRef || "worktree" : value
|
|
28675
|
+
}),
|
|
28676
|
+
false
|
|
28677
|
+
);
|
|
27829
28678
|
void enterHistory({ force: true });
|
|
27830
28679
|
}
|
|
27831
28680
|
function leaveHistory() {
|
|
27832
28681
|
generation++;
|
|
27833
28682
|
loading = false;
|
|
27834
28683
|
inFlight = null;
|
|
28684
|
+
compareSha = "";
|
|
27835
28685
|
if (filterTimer) {
|
|
27836
28686
|
clearTimeout(filterTimer);
|
|
27837
28687
|
filterTimer = void 0;
|
|
@@ -27846,17 +28696,33 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27846
28696
|
function handleListClick(e2) {
|
|
27847
28697
|
const row = e2.target.closest(".history-item");
|
|
27848
28698
|
if (!row?.dataset.sha) return;
|
|
28699
|
+
panel.focus?.();
|
|
27849
28700
|
if (row.dataset.sha === HISTORY_WORKTREE_COMMIT) {
|
|
27850
28701
|
void selectWorktree();
|
|
27851
28702
|
return;
|
|
27852
28703
|
}
|
|
27853
28704
|
const commit = commits.find((c2) => c2.sha === row.dataset.sha);
|
|
27854
|
-
if (commit)
|
|
28705
|
+
if (!commit) return;
|
|
28706
|
+
if (e2.shiftKey && selectedSha && selectedSha !== HISTORY_WORKTREE_COMMIT) {
|
|
28707
|
+
void selectRange(commit);
|
|
28708
|
+
return;
|
|
28709
|
+
}
|
|
28710
|
+
void selectCommit(commit, { compare: "" });
|
|
27855
28711
|
}
|
|
27856
28712
|
function applyFilter(next) {
|
|
27857
28713
|
const value = next.trim();
|
|
27858
28714
|
if (value === query) return;
|
|
27859
28715
|
query = value;
|
|
28716
|
+
if (historyScopeFromRoute()) {
|
|
28717
|
+
deps.setRoute(
|
|
28718
|
+
routeFor({
|
|
28719
|
+
commit: selectedSha || void 0,
|
|
28720
|
+
compare: compareSha,
|
|
28721
|
+
query: value
|
|
28722
|
+
}),
|
|
28723
|
+
true
|
|
28724
|
+
);
|
|
28725
|
+
}
|
|
27860
28726
|
generation++;
|
|
27861
28727
|
selectionGeneration++;
|
|
27862
28728
|
clearRefreshResult();
|
|
@@ -27945,6 +28811,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27945
28811
|
attachedFilterInput.removeEventListener("input", handleFilterInput);
|
|
27946
28812
|
attachedFilterInput.removeEventListener("change", handleFilterInput);
|
|
27947
28813
|
attachedFilterInput.removeEventListener("keydown", handleFilterKeydown);
|
|
28814
|
+
attachedFilterInput.removeEventListener("focus", handleFilterFocus);
|
|
27948
28815
|
attachedFilterInput = null;
|
|
27949
28816
|
}
|
|
27950
28817
|
if (attachedFilterClearButton) {
|
|
@@ -27977,8 +28844,11 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
27977
28844
|
input2.addEventListener("input", handleFilterInput);
|
|
27978
28845
|
input2.addEventListener("change", handleFilterInput);
|
|
27979
28846
|
input2.addEventListener("keydown", handleFilterKeydown);
|
|
28847
|
+
input2.addEventListener("focus", handleFilterFocus);
|
|
27980
28848
|
attachedFilterInput = input2;
|
|
27981
28849
|
}
|
|
28850
|
+
syncPanelTitle();
|
|
28851
|
+
syncFilterChrome();
|
|
27982
28852
|
const filterClearButton = mount.filterClearButton ?? null;
|
|
27983
28853
|
if (filterClearButton) {
|
|
27984
28854
|
syncFilterClearButton(filterClearButton);
|
|
@@ -28002,14 +28872,50 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28002
28872
|
);
|
|
28003
28873
|
observer.observe(sentinel);
|
|
28004
28874
|
}
|
|
28875
|
+
function syncFilterInputValue() {
|
|
28876
|
+
const input2 = activeMount.filterInput ?? null;
|
|
28877
|
+
if (input2 && input2.value !== query) input2.value = query;
|
|
28878
|
+
syncFilterClearButton();
|
|
28879
|
+
}
|
|
28880
|
+
function syncFilterChrome() {
|
|
28881
|
+
const input2 = activeMount.filterInput ?? null;
|
|
28882
|
+
if (!input2) return;
|
|
28883
|
+
const text3 = historyText(deps.getLanguage());
|
|
28884
|
+
input2.placeholder = text3.filterPlaceholder;
|
|
28885
|
+
input2.title = text3.filterTitle;
|
|
28886
|
+
}
|
|
28887
|
+
function syncPanelTitle() {
|
|
28888
|
+
const title = panel.querySelector?.(".history-title");
|
|
28889
|
+
if (!title) return;
|
|
28890
|
+
const text3 = historyText(deps.getLanguage());
|
|
28891
|
+
title.textContent = lineRange2 ? text3.commitsForLines(lineRange2.start, lineRange2.end) : mode === "history" && pathFilter ? text3.commitsIn(pathFilter) : text3.commitsTitle;
|
|
28892
|
+
}
|
|
28893
|
+
function handleFilterFocus() {
|
|
28894
|
+
const list3 = activeMount.authorList ?? null;
|
|
28895
|
+
if (!list3 || typeof fetch !== "function") return;
|
|
28896
|
+
const key = ref;
|
|
28897
|
+
if (authorsLoadedFor === key) return;
|
|
28898
|
+
authorsLoadedFor = key;
|
|
28899
|
+
void deps.trackLoad(
|
|
28900
|
+
fetch(`/_authors?ref=${encodeURIComponent(ref)}`).then(async (r2) => {
|
|
28901
|
+
if (!r2.ok) throw new Error(await r2.text());
|
|
28902
|
+
return await r2.json();
|
|
28903
|
+
})
|
|
28904
|
+
).then((res) => {
|
|
28905
|
+
if (activeMount.authorList !== list3 || key !== ref) return;
|
|
28906
|
+
list3.replaceChildren(
|
|
28907
|
+
...res.authors.map((author) => {
|
|
28908
|
+
const option = document.createElement("option");
|
|
28909
|
+
option.value = `author:${author.name}`;
|
|
28910
|
+
return option;
|
|
28911
|
+
})
|
|
28912
|
+
);
|
|
28913
|
+
}).catch((err) => {
|
|
28914
|
+
authorsLoadedFor = "";
|
|
28915
|
+
console.error("Failed to load history authors", err);
|
|
28916
|
+
});
|
|
28917
|
+
}
|
|
28005
28918
|
const docWithEvents = document;
|
|
28006
|
-
docWithEvents.addEventListener?.("keydown", (e2) => {
|
|
28007
|
-
if (isImeComposing(e2) || isEditableTarget(e2.target)) return;
|
|
28008
|
-
if (e2.key !== "ArrowDown" && e2.key !== "ArrowUp") return;
|
|
28009
|
-
if (!historyScopeFromRoute()) return;
|
|
28010
|
-
e2.preventDefault();
|
|
28011
|
-
void moveSelection(e2.key === "ArrowDown" ? 1 : -1);
|
|
28012
|
-
});
|
|
28013
28919
|
docWithEvents.addEventListener?.("input", (e2) => {
|
|
28014
28920
|
if (e2.target !== activeMount.filterInput) return;
|
|
28015
28921
|
handleFilterInput(e2);
|
|
@@ -28027,8 +28933,13 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28027
28933
|
syncRefreshButton(activeMount.refreshButton);
|
|
28028
28934
|
syncRefreshResult(activeMount.refreshResult);
|
|
28029
28935
|
syncFilterClearButton(activeMount.filterClearButton);
|
|
28936
|
+
syncFilterChrome();
|
|
28937
|
+
syncPanelTitle();
|
|
28030
28938
|
renderList();
|
|
28031
28939
|
},
|
|
28940
|
+
// Keyboard stepping through the commit list (keymap actions
|
|
28941
|
+
// history-next-commit / history-previous-commit).
|
|
28942
|
+
moveCommitSelection: (delta) => moveSelection(delta),
|
|
28032
28943
|
// Called from the SSE "update" listener when a history panel is on
|
|
28033
28944
|
// screen. Only updates existing local UI hints — no fetch, no list redraw,
|
|
28034
28945
|
// no generation bump, so it can't race with trackLoad/cancelInFlightRequests.
|
|
@@ -28284,8 +29195,28 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28284
29195
|
{
|
|
28285
29196
|
selectors: [{ action: "goto-history" }],
|
|
28286
29197
|
description: {
|
|
28287
|
-
en: "Go to the history screen",
|
|
28288
|
-
ja: "
|
|
29198
|
+
en: "Go to the history screen (keeps the ref you are viewing)",
|
|
29199
|
+
ja: "履歴画面へ移動(見ている ref を引き継ぐ)"
|
|
29200
|
+
}
|
|
29201
|
+
},
|
|
29202
|
+
{
|
|
29203
|
+
selectors: [
|
|
29204
|
+
{ action: "history-next-commit", key: "arrowdown" },
|
|
29205
|
+
{ action: "history-previous-commit", key: "arrowup" }
|
|
29206
|
+
],
|
|
29207
|
+
description: {
|
|
29208
|
+
en: "Select the next / previous commit in the history list",
|
|
29209
|
+
ja: "履歴一覧で次 / 前のコミットを選ぶ"
|
|
29210
|
+
}
|
|
29211
|
+
},
|
|
29212
|
+
{
|
|
29213
|
+
selectors: [
|
|
29214
|
+
{ action: "history-next-commit", scope: "history" },
|
|
29215
|
+
{ action: "history-previous-commit", scope: "history" }
|
|
29216
|
+
],
|
|
29217
|
+
description: {
|
|
29218
|
+
en: "Same, while the commit list has focus",
|
|
29219
|
+
ja: "同上(コミット一覧にフォーカスがあるとき)"
|
|
28289
29220
|
}
|
|
28290
29221
|
},
|
|
28291
29222
|
{
|
|
@@ -28968,6 +29899,23 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
28968
29899
|
}
|
|
28969
29900
|
]
|
|
28970
29901
|
},
|
|
29902
|
+
{
|
|
29903
|
+
title: "Search and history",
|
|
29904
|
+
blocks: [
|
|
29905
|
+
{
|
|
29906
|
+
kind: "paragraph",
|
|
29907
|
+
text: "Ctrl+K opens the file palette and Ctrl+G the text palette; the search button at the left of the header icons does the same (Shift+click for text). The two share one window: switching keeps what you typed, and reopening restores the last query, selected so typing replaces it. With an empty query the file palette lists the files you opened most recently, and the result line says when the ranking was cut at 50. The text palette has regex (Alt+R), match-case (Alt+C) and whole-word (Alt+W) toggles, and path:<dir or glob> tokens in the query narrow the search; matching is case-insensitive on every engine unless match-case is on. Opening a hit marks the matched text on the target line, and in a large virtualized file it pre-fills the in-file find bar. Pin (or Ctrl+Enter) moves the query into the bottom panel's Search tab, where the grouped result list stays open while you browse files; the query is part of the URL (?results=) so a reload re-runs it."
|
|
29908
|
+
},
|
|
29909
|
+
{
|
|
29910
|
+
kind: "paragraph",
|
|
29911
|
+
text: "The sidebar filter takes plain text (anywhere in the path), /pattern/ for a regex, ~text for a fuzzy match and *.ts or src/** for a glob, and shows matching / all file counts while active."
|
|
29912
|
+
},
|
|
29913
|
+
{
|
|
29914
|
+
kind: "paragraph",
|
|
29915
|
+
text: `The History screen filter understands words (message text, "quoted" keeps spaces), sha prefixes, author:<name>, path:<part>, since:/after:<date>, until:/before:<date>, code:<text> (lines added or removed) and merges:no / merges:only; different kinds combine with AND, author: offers the repository's authors as suggestions, and the filter is part of the URL (?q=) so a reload or shared link keeps it. Rows show branch and tag labels and a merge marker. The selected commit can be copied (sha) or opened on GitHub, Shift+click a second commit to diff the whole range between them, and a merge commit lets you pick which parent to compare against. ↑ / ↓ step commits anywhere on the screen, j / k do when the commit list has focus, and g h opens the history of the ref you are looking at. A folder page has a History button that opens the log restricted to that folder; selected source lines offer a Line history action (git log -L) in the line-reference pill, file pages have older / newer revision buttons that step through the commits that touched that file, and the ref picker keeps the refs you picked last as quick chips.`
|
|
29916
|
+
}
|
|
29917
|
+
]
|
|
29918
|
+
},
|
|
28971
29919
|
{
|
|
28972
29920
|
title: "Scratch on pasted text",
|
|
28973
29921
|
blocks: [
|
|
@@ -29342,7 +30290,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
29342
30290
|
],
|
|
29343
30291
|
[
|
|
29344
30292
|
"Detail footer & related panel",
|
|
29345
|
-
"Click any cell to open a resizable detail footer. Foreign-key cells open a related-rows panel with multi-step drill-down breadcrumbs, supporting both outgoing (FK → PK) and incoming (PK ← FK) navigation."
|
|
30293
|
+
"Click any cell to open a resizable detail footer; JSON values are pretty-printed and syntax-highlighted there. Once the grid has focus, arrow keys move the active cell from data cell to data cell and the footer follows the value under it, scrolling only as far as needed; Enter follows a foreign key (arrow keys alone never fire a related-table query), Escape closes whichever panel is open, and Tab / Shift+Tab move between the main grid and the related grid. Foreign-key cells open a related-rows panel with multi-step drill-down breadcrumbs, supporting both outgoing (FK → PK) and incoming (PK ← FK) navigation. Its reference list keeps each entry on one line with the full table name and condition in a tooltip, and the list width can be dragged and is remembered."
|
|
29346
30294
|
],
|
|
29347
30295
|
[
|
|
29348
30296
|
"Schema tab",
|
|
@@ -29735,6 +30683,23 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
29735
30683
|
}
|
|
29736
30684
|
]
|
|
29737
30685
|
},
|
|
30686
|
+
{
|
|
30687
|
+
title: "検索と履歴",
|
|
30688
|
+
blocks: [
|
|
30689
|
+
{
|
|
30690
|
+
kind: "paragraph",
|
|
30691
|
+
text: "Ctrl+K でファイルパレット、Ctrl+G でコード検索パレットが開きます。ヘッダのアイコン列の左端にある検索ボタンでも同じです(Shift+クリックでコード検索)。2 つは 1 つのウィンドウを共有し、切り替えても入力中の検索語は残り、閉じて開き直すと前回の検索語が選択状態で戻ります。ファイルパレットは空のとき最近開いたファイルを並べ、結果が 50 件で切られたときはその旨を表示します。コード検索には正規表現(Alt+R)・大文字小文字の区別(Alt+C)・単語単位(Alt+W)の切り替えがあり、検索語の中の path:<ディレクトリ or glob> で対象を絞れます。大文字小文字は「区別する」を押さない限りどのエンジンでも区別しません。ヒットを開くと該当行の一致箇所が強調され、大きな仮想表示のファイルではファイル内検索バーに検索語が入ります。「固定」(または Ctrl+Enter)を押すと検索語が下パネルの「検索」タブに移り、ファイルを開いて回る間も結果一覧が残ります。検索語は URL(?results=)に載るのでリロードしても同じ検索が走ります。"
|
|
30692
|
+
},
|
|
30693
|
+
{
|
|
30694
|
+
kind: "paragraph",
|
|
30695
|
+
text: "サイドバーの絞り込みは文字列(パスの部分一致)、/pattern/(正規表現)、~text(あいまい一致)、*.ts や src/**(glob)を受け付け、絞り込み中は「一致 / 全体」の件数を表示します。"
|
|
30696
|
+
},
|
|
30697
|
+
{
|
|
30698
|
+
kind: "paragraph",
|
|
30699
|
+
text: '履歴画面の絞り込みは、語(メッセージ本文。"引用" で空白を含む句)、sha の前方一致、author:<名前>、path:<一部>、since:/after:<日付>、until:/before:<日付>、code:<文字列>(追加・削除された行)、merges:no / merges:only を解釈し、種類の違う条件は AND で組み合わさります。author: にはリポジトリの著者名が候補として出て、絞り込みは URL(?q=)に載るのでリロードや共有でも残ります。各行にはブランチ / タグのラベルとマージ印が付きます。選択したコミットは sha をコピーしたり GitHub で開いたりでき、別のコミットを Shift+クリックするとその区間全体の差分、マージコミットでは比較する親を選べます。↑ / ↓ は画面のどこでも、j / k はコミット一覧にフォーカスがあるときにコミットを送り、g h は見ている ref の履歴を開きます。フォルダページの「履歴」ボタンでそのフォルダに絞った履歴が開きます。ソースの行を選択すると行参照ピルに「この行の履歴」(git log -L)が出て、ファイルページには前後のリビジョンへ移るボタン、ref ピッカーには最近使った ref のチップが並びます。'
|
|
30700
|
+
}
|
|
30701
|
+
]
|
|
30702
|
+
},
|
|
29738
30703
|
{
|
|
29739
30704
|
title: "貼り付けたテキストを扱う",
|
|
29740
30705
|
blocks: [
|
|
@@ -30109,7 +31074,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
30109
31074
|
],
|
|
30110
31075
|
[
|
|
30111
31076
|
"詳細フッタ・関連パネル",
|
|
30112
|
-
"
|
|
31077
|
+
"セルをクリックするとリサイズ可能な詳細フッタが開きます。JSON 値は整形してシンタックスハイライト付きで表示されます。グリッドにフォーカスがある間は矢印キーでデータセル間を移動でき、詳細フッタもその値に追従します(スクロールは見える位置まで必要なぶんだけ)。Enter で外部キーを辿り(矢印キーだけでは関連テーブルへのクエリは飛びません)、Escape で開いているパネルを閉じ、Tab / Shift+Tab でメイングリッドと関連グリッドを行き来できます。外部キー値からは関連行パネルが開き、ブレッドクラム付きで多段ドリルダウン可能。outgoing (FK→PK) と incoming (PK←FK) の両方向に対応。左の参照リストは各項目を1行に保ち、テーブル名と条件の全文は tooltip で確認できます。リスト幅はドラッグで変更でき、次回も保持されます。"
|
|
30113
31078
|
],
|
|
30114
31079
|
[
|
|
30115
31080
|
"Schema タブ",
|
|
@@ -33094,7 +34059,12 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33094
34059
|
githubCopy.type = "button";
|
|
33095
34060
|
githubCopy.innerHTML = `<span class="lrp-github-copy-icon">${iconSvg("octicon-copy", COPY_16_PATHS)}</span><span class="lrp-github-copy-label"></span>`;
|
|
33096
34061
|
githubActions.append(githubOpen, githubCopy);
|
|
33097
|
-
|
|
34062
|
+
const historyButton = document.createElement("button");
|
|
34063
|
+
historyButton.id = "line-ref-pill-history";
|
|
34064
|
+
historyButton.type = "button";
|
|
34065
|
+
historyButton.hidden = !deps.openLineHistory;
|
|
34066
|
+
historyButton.innerHTML = iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH) + '<span class="lrp-history-label"></span>';
|
|
34067
|
+
pill.append(copyButton, githubActions, historyButton, closeButton);
|
|
33098
34068
|
document.body.appendChild(pill);
|
|
33099
34069
|
let refText = "";
|
|
33100
34070
|
let currentPath = "";
|
|
@@ -33103,6 +34073,14 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33103
34073
|
let githubUrl = "";
|
|
33104
34074
|
let feedbackTimer = null;
|
|
33105
34075
|
let githubFeedbackTimer = null;
|
|
34076
|
+
function renderHistoryAction() {
|
|
34077
|
+
if (!deps.openLineHistory) return;
|
|
34078
|
+
const title = deps.lineHistoryTitle?.() ?? "Line history";
|
|
34079
|
+
historyButton.title = title;
|
|
34080
|
+
historyButton.setAttribute("aria-label", title);
|
|
34081
|
+
const label = historyButton.querySelector(".lrp-history-label");
|
|
34082
|
+
if (label) label.textContent = title;
|
|
34083
|
+
}
|
|
33106
34084
|
function renderGithubActions() {
|
|
33107
34085
|
const openTitle = deps.githubOpenTitle();
|
|
33108
34086
|
const copyTitle = deps.githubCopyTitle();
|
|
@@ -33194,6 +34172,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33194
34172
|
if (icon) icon.innerHTML = iconSvg("octicon-copy", COPY_16_PATHS);
|
|
33195
34173
|
}, 1200);
|
|
33196
34174
|
});
|
|
34175
|
+
historyButton.addEventListener("click", () => {
|
|
34176
|
+
if (!currentPath || !deps.openLineHistory) return;
|
|
34177
|
+
deps.openLineHistory(currentPath, currentStart, currentEnd);
|
|
34178
|
+
});
|
|
33197
34179
|
closeButton.addEventListener("click", () => {
|
|
33198
34180
|
deps.onClose();
|
|
33199
34181
|
});
|
|
@@ -33208,6 +34190,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33208
34190
|
currentEnd = Math.max(1, Math.floor(Math.max(start, end)));
|
|
33209
34191
|
githubUrl = deps.githubUrlForSelection(currentPath, currentStart, currentEnd) || "";
|
|
33210
34192
|
renderGithubActions();
|
|
34193
|
+
renderHistoryAction();
|
|
33211
34194
|
if (feedbackTimer) {
|
|
33212
34195
|
clearTimeout(feedbackTimer);
|
|
33213
34196
|
feedbackTimer = null;
|
|
@@ -33315,6 +34298,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33315
34298
|
}
|
|
33316
34299
|
|
|
33317
34300
|
// web-src/views/ref-picker.ts
|
|
34301
|
+
var QUICK_REF_VALUES = /* @__PURE__ */ new Set(["worktree", "HEAD", "--staged"]);
|
|
33318
34302
|
function createRefPicker(deps) {
|
|
33319
34303
|
function wireRefSelectorInput(input2, onPick) {
|
|
33320
34304
|
const wrap = input2.closest("[data-ref-selector]");
|
|
@@ -33334,6 +34318,37 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33334
34318
|
});
|
|
33335
34319
|
if (onPick)
|
|
33336
34320
|
input2.addEventListener("change", () => onPick(input2.value || "worktree"));
|
|
34321
|
+
input2.addEventListener("change", () => rememberRef(input2.value));
|
|
34322
|
+
}
|
|
34323
|
+
function rememberRef(value) {
|
|
34324
|
+
const ref = (value || "").trim();
|
|
34325
|
+
if (!ref || QUICK_REF_VALUES.has(ref)) return;
|
|
34326
|
+
deps.rememberRecentRef?.(ref);
|
|
34327
|
+
}
|
|
34328
|
+
function renderRecentChips(current) {
|
|
34329
|
+
let row = popover.querySelector(".rp-recent");
|
|
34330
|
+
const recent = (deps.getRecentRefs?.() ?? []).filter((ref) => ref && !QUICK_REF_VALUES.has(ref)).reverse();
|
|
34331
|
+
if (!row) {
|
|
34332
|
+
row = document.createElement("div");
|
|
34333
|
+
row.className = "rp-recent";
|
|
34334
|
+
const quick = popover.querySelector(".rp-quick");
|
|
34335
|
+
if (quick) quick.after(row);
|
|
34336
|
+
else popover.prepend(row);
|
|
34337
|
+
}
|
|
34338
|
+
row.replaceChildren();
|
|
34339
|
+
row.hidden = recent.length === 0;
|
|
34340
|
+
const title = deps.recentRefTitle?.() ?? "";
|
|
34341
|
+
for (const ref of recent) {
|
|
34342
|
+
const chip = document.createElement("button");
|
|
34343
|
+
chip.type = "button";
|
|
34344
|
+
chip.className = "rp-chip rp-chip-recent";
|
|
34345
|
+
chip.dataset.val = ref;
|
|
34346
|
+
chip.textContent = ref;
|
|
34347
|
+
if (title) chip.title = title;
|
|
34348
|
+
chip.classList.toggle("current", ref === current);
|
|
34349
|
+
chip.addEventListener("click", () => handlePicked(ref));
|
|
34350
|
+
row.appendChild(chip);
|
|
34351
|
+
}
|
|
33337
34352
|
}
|
|
33338
34353
|
const REFS = {
|
|
33339
34354
|
branches: [],
|
|
@@ -33558,6 +34573,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
33558
34573
|
popover.querySelectorAll(".rp-chip").forEach((c2) => {
|
|
33559
34574
|
c2.classList.toggle("current", c2.dataset.val === cur);
|
|
33560
34575
|
});
|
|
34576
|
+
renderRecentChips(cur);
|
|
33561
34577
|
popover.hidden = false;
|
|
33562
34578
|
const r2 = input2.getBoundingClientRect();
|
|
33563
34579
|
const popWidth = Math.min(560, Math.floor(window.innerWidth * 0.9));
|
|
@@ -34441,6 +35457,8 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
34441
35457
|
input2.title = invalid ? filter.error || "invalid regular expression" : "";
|
|
34442
35458
|
const filterActive = filter.kind !== "empty" && !invalid;
|
|
34443
35459
|
const matches2 = invalid ? () => true : filter.match;
|
|
35460
|
+
let totalFiles = 0;
|
|
35461
|
+
let visibleFiles = 0;
|
|
34444
35462
|
const walk = (node, depth) => {
|
|
34445
35463
|
let subtreeVisible = false;
|
|
34446
35464
|
const rows = [];
|
|
@@ -34465,7 +35483,9 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
34465
35483
|
} else {
|
|
34466
35484
|
const testHidden = STATE.hideTests && !isRepositorySidebarMode() && isTestPath(item.file.path || "");
|
|
34467
35485
|
const visible = !testHidden && matches2(item.file.path || "");
|
|
35486
|
+
if (!testHidden) totalFiles++;
|
|
34468
35487
|
if (visible) {
|
|
35488
|
+
visibleFiles++;
|
|
34469
35489
|
rows.push({
|
|
34470
35490
|
kind: "file",
|
|
34471
35491
|
path: item.file.path,
|
|
@@ -34480,6 +35500,31 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
34480
35500
|
return { visible: subtreeVisible, rows };
|
|
34481
35501
|
};
|
|
34482
35502
|
SIDEBAR_VISIBLE_ROWS = walk(SIDEBAR_TREE_ROOT, 0).rows;
|
|
35503
|
+
syncSidebarFilterCount(filterActive, visibleFiles, totalFiles);
|
|
35504
|
+
}
|
|
35505
|
+
let SIDEBAR_TOTALS_BASE = "";
|
|
35506
|
+
let SIDEBAR_TOTALS_FILTER_TEXT = "";
|
|
35507
|
+
function setSidebarTotals(text3) {
|
|
35508
|
+
SIDEBAR_TOTALS_BASE = text3;
|
|
35509
|
+
const el2 = $("#totals");
|
|
35510
|
+
if (el2.textContent === SIDEBAR_TOTALS_FILTER_TEXT) el2.textContent = text3;
|
|
35511
|
+
if (!SIDEBAR_TOTALS_FILTER_TEXT) el2.textContent = text3;
|
|
35512
|
+
}
|
|
35513
|
+
function syncSidebarFilterCount(active, visible, total) {
|
|
35514
|
+
const el2 = document.querySelector("#totals");
|
|
35515
|
+
if (!el2) return;
|
|
35516
|
+
if (!active) {
|
|
35517
|
+
if (SIDEBAR_TOTALS_FILTER_TEXT) {
|
|
35518
|
+
if (el2.textContent === SIDEBAR_TOTALS_FILTER_TEXT)
|
|
35519
|
+
el2.textContent = SIDEBAR_TOTALS_BASE;
|
|
35520
|
+
el2.removeAttribute("title");
|
|
35521
|
+
SIDEBAR_TOTALS_FILTER_TEXT = "";
|
|
35522
|
+
}
|
|
35523
|
+
return;
|
|
35524
|
+
}
|
|
35525
|
+
SIDEBAR_TOTALS_FILTER_TEXT = `${visible} / ${total}`;
|
|
35526
|
+
el2.textContent = SIDEBAR_TOTALS_FILTER_TEXT;
|
|
35527
|
+
el2.title = deps.filterCountTitle(visible, total);
|
|
34483
35528
|
}
|
|
34484
35529
|
function sidebarVirtualRange() {
|
|
34485
35530
|
const sidebar = document.querySelector("#sidebar");
|
|
@@ -34749,7 +35794,9 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
34749
35794
|
} else {
|
|
34750
35795
|
renderFlat(files, ul, onFileClick);
|
|
34751
35796
|
}
|
|
34752
|
-
|
|
35797
|
+
setSidebarTotals(
|
|
35798
|
+
!repoSidebar && files.length ? `${files.length} file${files.length === 1 ? "" : "s"}` : ""
|
|
35799
|
+
);
|
|
34753
35800
|
const effectiveView = treeMode ? "tree" : STATE.sbView;
|
|
34754
35801
|
$$(".sb-view-seg button").forEach((b2) => {
|
|
34755
35802
|
b2.classList.toggle("active", b2.dataset.view === effectiveView);
|
|
@@ -34864,10 +35911,16 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
34864
35911
|
input2.title = invalid ? filter.error || "invalid regular expression" : "";
|
|
34865
35912
|
const matches2 = invalid ? () => true : filter.match;
|
|
34866
35913
|
const filterActive = filter.kind !== "empty" && !invalid;
|
|
35914
|
+
let totalFiles = 0;
|
|
35915
|
+
let visibleFiles = 0;
|
|
34867
35916
|
$$("#filelist li[data-path]").forEach((li) => {
|
|
34868
35917
|
const match2 = matches2(li.dataset.path || "");
|
|
34869
35918
|
li.classList.toggle("hidden", !match2);
|
|
35919
|
+
if (li.classList.contains("hidden-by-tests")) return;
|
|
35920
|
+
totalFiles++;
|
|
35921
|
+
if (match2) visibleFiles++;
|
|
34870
35922
|
});
|
|
35923
|
+
syncSidebarFilterCount(filterActive, visibleFiles, totalFiles);
|
|
34871
35924
|
if (!isRepositorySidebarMode()) {
|
|
34872
35925
|
document.querySelectorAll(".gdp-file-shell").forEach((card) => {
|
|
34873
35926
|
const match2 = matches2(card.dataset.path || "");
|
|
@@ -35373,6 +36426,9 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
35373
36426
|
repositoryWebTarget,
|
|
35374
36427
|
openGithubLabel,
|
|
35375
36428
|
openRepositoryWebLabel,
|
|
36429
|
+
folderHistoryLabel,
|
|
36430
|
+
folderHistoryTitle,
|
|
36431
|
+
openFolderHistory,
|
|
35376
36432
|
fileBadge
|
|
35377
36433
|
} = deps;
|
|
35378
36434
|
let REPO_SORT = {
|
|
@@ -35789,6 +36845,15 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
35789
36845
|
meta.ref
|
|
35790
36846
|
);
|
|
35791
36847
|
if (repositoryWebLink) toolbar.appendChild(repositoryWebLink);
|
|
36848
|
+
const historyButton = document.createElement("button");
|
|
36849
|
+
historyButton.type = "button";
|
|
36850
|
+
historyButton.className = "gdp-btn gdp-btn-sm gdp-repo-history-btn";
|
|
36851
|
+
historyButton.textContent = folderHistoryLabel();
|
|
36852
|
+
historyButton.title = folderHistoryTitle();
|
|
36853
|
+
historyButton.addEventListener("click", () => {
|
|
36854
|
+
openFolderHistory(meta.ref, meta.path || "");
|
|
36855
|
+
});
|
|
36856
|
+
toolbar.appendChild(historyButton);
|
|
35792
36857
|
if (canTrashWorktreeRef(meta.ref)) {
|
|
35793
36858
|
toolbar.appendChild(
|
|
35794
36859
|
createNewFolderButton(meta.path || "", () => loadRepo())
|
|
@@ -36354,294 +37419,12 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36354
37419
|
};
|
|
36355
37420
|
}
|
|
36356
37421
|
|
|
36357
|
-
// web-src/core/fuzzy-search.ts
|
|
36358
|
-
function basenameStart(path) {
|
|
36359
|
-
const slash = path.lastIndexOf("/");
|
|
36360
|
-
return slash < 0 ? 0 : slash + 1;
|
|
36361
|
-
}
|
|
36362
|
-
function isBoundary(path, index) {
|
|
36363
|
-
if (index <= 0) return true;
|
|
36364
|
-
const prev = path[index - 1];
|
|
36365
|
-
return prev === "/" || prev === "-" || prev === "_" || prev === "." || prev === " ";
|
|
36366
|
-
}
|
|
36367
|
-
function toRanges(indices) {
|
|
36368
|
-
const ranges = [];
|
|
36369
|
-
for (const index of indices) {
|
|
36370
|
-
const last = ranges[ranges.length - 1];
|
|
36371
|
-
if (last && last.end === index) {
|
|
36372
|
-
last.end = index + 1;
|
|
36373
|
-
} else {
|
|
36374
|
-
ranges.push({ start: index, end: index + 1 });
|
|
36375
|
-
}
|
|
36376
|
-
}
|
|
36377
|
-
return ranges;
|
|
36378
|
-
}
|
|
36379
|
-
function basenameMatchTier(loweredQuery, loweredBasename) {
|
|
36380
|
-
if (loweredBasename === loweredQuery) return 4;
|
|
36381
|
-
if (loweredBasename.startsWith(`${loweredQuery}.`)) return 3;
|
|
36382
|
-
if (loweredBasename.startsWith(loweredQuery)) return 2;
|
|
36383
|
-
if (loweredBasename.includes(loweredQuery)) return 1;
|
|
36384
|
-
return 0;
|
|
36385
|
-
}
|
|
36386
|
-
function pathMatchTier(loweredQuery, loweredPath, loweredBasename) {
|
|
36387
|
-
if (loweredQuery.includes("/") && (loweredPath === loweredQuery || loweredPath.endsWith(`/${loweredQuery}`)))
|
|
36388
|
-
return 4;
|
|
36389
|
-
return basenameMatchTier(loweredQuery, loweredBasename);
|
|
36390
|
-
}
|
|
36391
|
-
function contiguousPathRange(loweredQuery, loweredPath, baseStart) {
|
|
36392
|
-
const loweredBasename = loweredPath.slice(baseStart);
|
|
36393
|
-
const basenameMatchStart = loweredBasename.indexOf(loweredQuery);
|
|
36394
|
-
if (basenameMatchStart >= 0) {
|
|
36395
|
-
const start = baseStart + basenameMatchStart;
|
|
36396
|
-
return { start, end: start + loweredQuery.length };
|
|
36397
|
-
}
|
|
36398
|
-
if (loweredQuery.includes("/")) {
|
|
36399
|
-
const pathMatchStart = loweredPath.endsWith(`/${loweredQuery}`) ? loweredPath.length - loweredQuery.length : loweredPath === loweredQuery ? 0 : -1;
|
|
36400
|
-
if (pathMatchStart >= 0)
|
|
36401
|
-
return {
|
|
36402
|
-
start: pathMatchStart,
|
|
36403
|
-
end: pathMatchStart + loweredQuery.length
|
|
36404
|
-
};
|
|
36405
|
-
}
|
|
36406
|
-
return null;
|
|
36407
|
-
}
|
|
36408
|
-
function computeFuzzyMatch(query, path) {
|
|
36409
|
-
const q = query.trim().toLowerCase();
|
|
36410
|
-
if (!q) return { score: 0, ranges: [], tier: 0 };
|
|
36411
|
-
const lowerPath = path.toLowerCase();
|
|
36412
|
-
const baseStart = basenameStart(path);
|
|
36413
|
-
const indices = [];
|
|
36414
|
-
let from = 0;
|
|
36415
|
-
let score = 0;
|
|
36416
|
-
for (const ch of q) {
|
|
36417
|
-
const index = lowerPath.indexOf(ch, from);
|
|
36418
|
-
if (index < 0) return null;
|
|
36419
|
-
indices.push(index);
|
|
36420
|
-
score += 10;
|
|
36421
|
-
if (index >= baseStart) score += 8;
|
|
36422
|
-
if (isBoundary(path, index)) score += 6;
|
|
36423
|
-
const prev = indices[indices.length - 2];
|
|
36424
|
-
if (prev != null && prev + 1 === index) score += 12;
|
|
36425
|
-
from = index + 1;
|
|
36426
|
-
}
|
|
36427
|
-
const first = indices[0];
|
|
36428
|
-
score -= Math.min(first, 40);
|
|
36429
|
-
if (indices[0] >= baseStart) score += 20;
|
|
36430
|
-
const basename = lowerPath.slice(baseStart);
|
|
36431
|
-
const tier = pathMatchTier(q, lowerPath, basename);
|
|
36432
|
-
const contiguousRange = contiguousPathRange(q, lowerPath, baseStart);
|
|
36433
|
-
return {
|
|
36434
|
-
score,
|
|
36435
|
-
ranges: contiguousRange ? [contiguousRange] : toRanges(indices),
|
|
36436
|
-
tier
|
|
36437
|
-
};
|
|
36438
|
-
}
|
|
36439
|
-
function fuzzyMatchPath(query, path) {
|
|
36440
|
-
const match2 = computeFuzzyMatch(query, path);
|
|
36441
|
-
return match2 ? { score: match2.score, ranges: match2.ranges } : null;
|
|
36442
|
-
}
|
|
36443
|
-
function rankFuzzyPaths(query, items, limit) {
|
|
36444
|
-
const bounded = Number.isInteger(limit) && limit !== void 0 && limit > 0 ? Math.floor(limit) : 0;
|
|
36445
|
-
const compare = (a2, b2) => b2.tier - a2.tier || b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
|
|
36446
|
-
if (!bounded) {
|
|
36447
|
-
return items.map((item) => {
|
|
36448
|
-
const match2 = computeFuzzyMatch(query, item.path);
|
|
36449
|
-
return match2 ? { item, score: match2.score, ranges: match2.ranges, tier: match2.tier } : null;
|
|
36450
|
-
}).filter(
|
|
36451
|
-
(item) => item !== null
|
|
36452
|
-
).sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
|
|
36453
|
-
}
|
|
36454
|
-
const top = [];
|
|
36455
|
-
for (const item of items) {
|
|
36456
|
-
const match2 = computeFuzzyMatch(query, item.path);
|
|
36457
|
-
if (!match2) continue;
|
|
36458
|
-
const ranked = {
|
|
36459
|
-
item,
|
|
36460
|
-
score: match2.score,
|
|
36461
|
-
ranges: match2.ranges,
|
|
36462
|
-
tier: match2.tier
|
|
36463
|
-
};
|
|
36464
|
-
pushBoundedTop(top, ranked, bounded, compare);
|
|
36465
|
-
}
|
|
36466
|
-
return top.sort(compare).map(({ item, score, ranges }) => ({ item, score, ranges }));
|
|
36467
|
-
}
|
|
36468
|
-
function pushBoundedTop(heap, item, limit, compareBestFirst) {
|
|
36469
|
-
const isWorse = (a2, b2) => compareBestFirst(a2, b2) > 0;
|
|
36470
|
-
const siftUp = (index) => {
|
|
36471
|
-
while (index > 0) {
|
|
36472
|
-
const parent = Math.floor((index - 1) / 2);
|
|
36473
|
-
if (!isWorse(heap[index], heap[parent])) break;
|
|
36474
|
-
[heap[index], heap[parent]] = [heap[parent], heap[index]];
|
|
36475
|
-
index = parent;
|
|
36476
|
-
}
|
|
36477
|
-
};
|
|
36478
|
-
const siftDown = (index) => {
|
|
36479
|
-
while (true) {
|
|
36480
|
-
const left = index * 2 + 1;
|
|
36481
|
-
const right = left + 1;
|
|
36482
|
-
let worst = index;
|
|
36483
|
-
if (left < heap.length && isWorse(heap[left], heap[worst])) worst = left;
|
|
36484
|
-
if (right < heap.length && isWorse(heap[right], heap[worst]))
|
|
36485
|
-
worst = right;
|
|
36486
|
-
if (worst === index) break;
|
|
36487
|
-
[heap[index], heap[worst]] = [heap[worst], heap[index]];
|
|
36488
|
-
index = worst;
|
|
36489
|
-
}
|
|
36490
|
-
};
|
|
36491
|
-
if (heap.length < limit) {
|
|
36492
|
-
heap.push(item);
|
|
36493
|
-
siftUp(heap.length - 1);
|
|
36494
|
-
return;
|
|
36495
|
-
}
|
|
36496
|
-
if (compareBestFirst(item, heap[0]) >= 0) return;
|
|
36497
|
-
heap[0] = item;
|
|
36498
|
-
siftDown(0);
|
|
36499
|
-
}
|
|
36500
|
-
function rankGlobPathMatches(query, items, limit) {
|
|
36501
|
-
const matchPath = createGlobPathMatcher(query);
|
|
36502
|
-
if (!matchPath) return [];
|
|
36503
|
-
const bounded = Number.isInteger(limit) && limit !== void 0 && limit > 0 ? Math.floor(limit) : 0;
|
|
36504
|
-
const compare = (a2, b2) => b2.score - a2.score || a2.item.path.localeCompare(b2.item.path);
|
|
36505
|
-
if (!bounded) {
|
|
36506
|
-
return items.map((item) => {
|
|
36507
|
-
const match2 = matchPath(item.path);
|
|
36508
|
-
return match2 ? {
|
|
36509
|
-
item,
|
|
36510
|
-
score: match2.score,
|
|
36511
|
-
ranges: match2.ranges,
|
|
36512
|
-
mode: "glob"
|
|
36513
|
-
} : null;
|
|
36514
|
-
}).filter((item) => item !== null).sort(compare);
|
|
36515
|
-
}
|
|
36516
|
-
const top = [];
|
|
36517
|
-
for (const item of items) {
|
|
36518
|
-
const match2 = matchPath(item.path);
|
|
36519
|
-
if (!match2) continue;
|
|
36520
|
-
const ranked = {
|
|
36521
|
-
item,
|
|
36522
|
-
score: match2.score,
|
|
36523
|
-
ranges: match2.ranges,
|
|
36524
|
-
mode: "glob"
|
|
36525
|
-
};
|
|
36526
|
-
pushBoundedTop(top, ranked, bounded, compare);
|
|
36527
|
-
}
|
|
36528
|
-
return top.sort(compare);
|
|
36529
|
-
}
|
|
36530
|
-
function rankPathMatches(query, items, limit) {
|
|
36531
|
-
if (isGlobPathQuery(query)) {
|
|
36532
|
-
return rankGlobPathMatches(query, items, limit);
|
|
36533
|
-
}
|
|
36534
|
-
return rankFuzzyPaths(query, items, limit).map((item) => ({
|
|
36535
|
-
...item,
|
|
36536
|
-
mode: "fuzzy"
|
|
36537
|
-
}));
|
|
36538
|
-
}
|
|
36539
|
-
function isGlobPathQuery(query) {
|
|
36540
|
-
return /[*?]/.test(query.trim());
|
|
36541
|
-
}
|
|
36542
|
-
function escapeRegexChar(ch) {
|
|
36543
|
-
return /[\\^$+?.()|{}]/.test(ch) ? `\\${ch}` : ch;
|
|
36544
|
-
}
|
|
36545
|
-
function globToRegExp(query) {
|
|
36546
|
-
const pattern = query.trim();
|
|
36547
|
-
if (!pattern) return null;
|
|
36548
|
-
let source = "^";
|
|
36549
|
-
for (let i2 = 0; i2 < pattern.length; i2++) {
|
|
36550
|
-
const ch = pattern[i2];
|
|
36551
|
-
if (ch === "*") {
|
|
36552
|
-
if (pattern[i2 + 1] === "*") {
|
|
36553
|
-
source += ".*";
|
|
36554
|
-
i2++;
|
|
36555
|
-
} else {
|
|
36556
|
-
source += "[^/]*";
|
|
36557
|
-
}
|
|
36558
|
-
} else if (ch === "?") {
|
|
36559
|
-
source += "[^/]";
|
|
36560
|
-
} else if (ch === "[") {
|
|
36561
|
-
const close = pattern.indexOf("]", i2 + 1);
|
|
36562
|
-
if (close < 0) {
|
|
36563
|
-
source += "\\[";
|
|
36564
|
-
} else {
|
|
36565
|
-
const body = pattern.slice(i2 + 1, close).replace(/\\/g, "\\\\");
|
|
36566
|
-
source += `[${body}]`;
|
|
36567
|
-
i2 = close;
|
|
36568
|
-
}
|
|
36569
|
-
} else {
|
|
36570
|
-
source += escapeRegexChar(ch);
|
|
36571
|
-
}
|
|
36572
|
-
}
|
|
36573
|
-
source += "$";
|
|
36574
|
-
try {
|
|
36575
|
-
return new RegExp(source, "i");
|
|
36576
|
-
} catch {
|
|
36577
|
-
return null;
|
|
36578
|
-
}
|
|
36579
|
-
}
|
|
36580
|
-
function globMatchPath(query, path) {
|
|
36581
|
-
return createGlobPathMatcher(query)?.(path) ?? null;
|
|
36582
|
-
}
|
|
36583
|
-
function createGlobPathMatcher(query) {
|
|
36584
|
-
const regex = globToRegExp(query);
|
|
36585
|
-
if (!regex) return null;
|
|
36586
|
-
const literal = query.replace(/[*?[\]]+/g, " ").trim().split(/\s+/).filter(Boolean);
|
|
36587
|
-
const suffix = query.replace(/^\*+/, "").toLowerCase();
|
|
36588
|
-
return (path) => {
|
|
36589
|
-
const baseStart = basenameStart(path);
|
|
36590
|
-
const basename = path.slice(baseStart);
|
|
36591
|
-
if (!regex.test(path) && (query.includes("/") || !regex.test(basename)))
|
|
36592
|
-
return null;
|
|
36593
|
-
const ranges = [];
|
|
36594
|
-
const lowerPath = path.toLowerCase();
|
|
36595
|
-
for (const part of literal) {
|
|
36596
|
-
const start = lowerPath.indexOf(part.toLowerCase());
|
|
36597
|
-
if (start >= 0) ranges.push({ start, end: start + part.length });
|
|
36598
|
-
}
|
|
36599
|
-
ranges.sort((a2, b2) => a2.start - b2.start || a2.end - b2.end);
|
|
36600
|
-
const mergedRanges = [];
|
|
36601
|
-
for (const range of ranges) {
|
|
36602
|
-
const last = mergedRanges[mergedRanges.length - 1];
|
|
36603
|
-
if (last && last.end >= range.start) {
|
|
36604
|
-
last.end = Math.max(last.end, range.end);
|
|
36605
|
-
} else {
|
|
36606
|
-
mergedRanges.push({ ...range });
|
|
36607
|
-
}
|
|
36608
|
-
}
|
|
36609
|
-
const score = 1e3 - Math.min(path.length, 200) + (path.slice(baseStart).toLowerCase().endsWith(suffix) ? 50 : 0);
|
|
36610
|
-
return { score, ranges: mergedRanges };
|
|
36611
|
-
};
|
|
36612
|
-
}
|
|
36613
|
-
|
|
36614
|
-
// web-src/core/search-palette.ts
|
|
36615
|
-
var PALETTE_RESULT_LIMIT = 50;
|
|
36616
|
-
var GREP_SELECTION_HISTORY_LIMIT = 100;
|
|
36617
|
-
var MIN_GREP_PALETTE_WIDTH = 720;
|
|
36618
|
-
var MAX_GREP_PALETTE_WIDTH = 2400;
|
|
36619
|
-
var MIN_GREP_PALETTE_HEIGHT = 420;
|
|
36620
|
-
var MAX_GREP_PALETTE_HEIGHT = 1400;
|
|
36621
|
-
function limitPaletteResults(items) {
|
|
36622
|
-
return items.slice(0, PALETTE_RESULT_LIMIT);
|
|
36623
|
-
}
|
|
36624
|
-
function movePaletteSelection(index, count, direction) {
|
|
36625
|
-
if (count <= 0) return -1;
|
|
36626
|
-
if (index < 0) return direction > 0 ? 0 : count - 1;
|
|
36627
|
-
return Math.max(0, Math.min(count - 1, index + direction));
|
|
36628
|
-
}
|
|
36629
|
-
function rememberPaletteSelection(history2, path) {
|
|
36630
|
-
const next = history2.filter((entry) => entry !== path);
|
|
36631
|
-
next.push(path);
|
|
36632
|
-
return next.slice(-GREP_SELECTION_HISTORY_LIMIT);
|
|
36633
|
-
}
|
|
36634
|
-
function rankPaletteResultsByHistory(items, history2) {
|
|
36635
|
-
const priority = new Map(history2.map((path, index) => [path, index + 1]));
|
|
36636
|
-
return items.map((item, index) => ({ item, index })).sort(
|
|
36637
|
-
(a2, b2) => (priority.get(b2.item.path) ?? 0) - (priority.get(a2.item.path) ?? 0) || a2.index - b2.index
|
|
36638
|
-
).map(({ item }) => item);
|
|
36639
|
-
}
|
|
36640
|
-
|
|
36641
37422
|
// web-src/views/search-palette-i18n.ts
|
|
36642
37423
|
var EN2 = {
|
|
36643
37424
|
files: "Files",
|
|
36644
37425
|
grep: "Grep",
|
|
37426
|
+
switchToFiles: "Switch to file search (Ctrl+K)",
|
|
37427
|
+
switchToGrep: "Switch to text search (Ctrl+G)",
|
|
36645
37428
|
searchFiles: "Search files",
|
|
36646
37429
|
searchText: "Search text",
|
|
36647
37430
|
fileCodePreview: "File code preview",
|
|
@@ -36652,14 +37435,20 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36652
37435
|
fuzzyHint: "Fuzzy path search",
|
|
36653
37436
|
plain: "Plain",
|
|
36654
37437
|
regex: ".* Regex",
|
|
37438
|
+
matchCase: "Aa",
|
|
37439
|
+
matchCaseTitle: "Match case (Alt+C)",
|
|
37440
|
+
wholeWord: "Word",
|
|
37441
|
+
wholeWordTitle: "Match whole words only (Alt+W)",
|
|
36655
37442
|
excludeTests: "No test",
|
|
36656
37443
|
excludeTestsTitle: "Exclude test/spec files",
|
|
36657
37444
|
groupFiles: "Group files",
|
|
36658
37445
|
groupFilesTitle: "Group matching lines by file",
|
|
36659
|
-
|
|
37446
|
+
grepHint: "path:<dir or glob> narrows · Alt+R regex",
|
|
36660
37447
|
windowWidth: "window width",
|
|
36661
37448
|
windowHeight: "window height",
|
|
36662
37449
|
regexMode: "regex mode",
|
|
37450
|
+
caseSensitivity: "case sensitivity",
|
|
37451
|
+
wordMatching: "whole-word matching",
|
|
36663
37452
|
fileGrouping: "file grouping",
|
|
36664
37453
|
testExclusion: "test exclusion",
|
|
36665
37454
|
saveFailed: (label, error2) => `Failed to save ${label}: ${error2}`,
|
|
@@ -36673,22 +37462,42 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36673
37462
|
line: (line, column) => `Line ${line}:${column}`,
|
|
36674
37463
|
diffFiles: (count) => `${count} diff files`,
|
|
36675
37464
|
typeToSearchFiles: "Type to search repository files",
|
|
37465
|
+
recentFiles: (count) => `Recent files - ${count}`,
|
|
36676
37466
|
loadingFiles: "Loading files...",
|
|
36677
|
-
results: (count) => `${count} results
|
|
37467
|
+
results: (count, total, candidatesTruncated) => (total === void 0 ? `${count} results` : `${count} of ${total} results`) + (candidatesTruncated ? " · file list truncated" : ""),
|
|
36678
37468
|
noResults: "No results",
|
|
36679
37469
|
typeToGrep: "Type to grep",
|
|
36680
37470
|
invalidRegex: "Invalid regular expression",
|
|
36681
37471
|
searching: "Searching...",
|
|
36682
37472
|
repositoryChanged: "Repository changed; search again",
|
|
36683
|
-
grepSummary: ({
|
|
37473
|
+
grepSummary: ({
|
|
37474
|
+
engine,
|
|
37475
|
+
regex,
|
|
37476
|
+
caseSensitive,
|
|
37477
|
+
wholeWord,
|
|
37478
|
+
testsExcluded,
|
|
37479
|
+
truncated,
|
|
37480
|
+
count,
|
|
37481
|
+
paths
|
|
37482
|
+
}) => engine + (regex ? " regex" : " plain") + (caseSensitive ? " · match case" : "") + (wholeWord ? " · whole word" : "") + (testsExcluded ? " · tests excluded" : "") + (paths.length ? ` · in ${paths.join(" ")}` : "") + (truncated ? " truncated" : "") + ` - ${count} results`,
|
|
36684
37483
|
searchFailed: (error2) => `Search failed: ${error2}`,
|
|
36685
37484
|
savingSelection: "Saving selection...",
|
|
36686
37485
|
selectionSaveFailed: (error2) => `Failed to save selection: ${error2}`,
|
|
36687
|
-
unknownError: "unknown error"
|
|
37486
|
+
unknownError: "unknown error",
|
|
37487
|
+
pinResults: "Pin",
|
|
37488
|
+
pinResultsTitle: "Keep these results open in the bottom panel while you browse (Ctrl+Enter)",
|
|
37489
|
+
resultsTitle: "Search",
|
|
37490
|
+
resultsOpen: "Open the search results panel",
|
|
37491
|
+
resultsRun: "Search",
|
|
37492
|
+
resultsPlaceholder: "Search text (path:<dir or glob> narrows)",
|
|
37493
|
+
resultsIdle: "Type a search and press Enter",
|
|
37494
|
+
resultsScope: (ref) => `in ${ref}`
|
|
36688
37495
|
};
|
|
36689
37496
|
var JA2 = {
|
|
36690
37497
|
files: "ファイル",
|
|
36691
37498
|
grep: "GREP",
|
|
37499
|
+
switchToFiles: "ファイル名検索に切り替え (Ctrl+K)",
|
|
37500
|
+
switchToGrep: "コード検索に切り替え (Ctrl+G)",
|
|
36692
37501
|
searchFiles: "ファイルを検索",
|
|
36693
37502
|
searchText: "コードを検索",
|
|
36694
37503
|
fileCodePreview: "ファイルのコード表示",
|
|
@@ -36699,14 +37508,20 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36699
37508
|
fuzzyHint: "パスのあいまい検索",
|
|
36700
37509
|
plain: "通常",
|
|
36701
37510
|
regex: ".* 正規表現",
|
|
37511
|
+
matchCase: "Aa",
|
|
37512
|
+
matchCaseTitle: "大文字と小文字を区別 (Alt+C)",
|
|
37513
|
+
wholeWord: "単語",
|
|
37514
|
+
wholeWordTitle: "単語単位で一致 (Alt+W)",
|
|
36702
37515
|
excludeTests: "テスト除外",
|
|
36703
37516
|
excludeTestsTitle: "test/spec ファイルを除外",
|
|
36704
37517
|
groupFiles: "ファイル別",
|
|
36705
37518
|
groupFilesTitle: "一致した行をファイル別に表示",
|
|
36706
|
-
|
|
37519
|
+
grepHint: "path:<ディレクトリ or glob> で絞り込み · Alt+R 正規表現",
|
|
36707
37520
|
windowWidth: "ウィンドウの幅",
|
|
36708
37521
|
windowHeight: "ウィンドウの高さ",
|
|
36709
37522
|
regexMode: "正規表現モード",
|
|
37523
|
+
caseSensitivity: "大文字小文字の区別",
|
|
37524
|
+
wordMatching: "単語単位の一致",
|
|
36710
37525
|
fileGrouping: "ファイル別表示",
|
|
36711
37526
|
testExclusion: "テスト除外",
|
|
36712
37527
|
saveFailed: (label, error2) => `${label}を保存できませんでした: ${error2}`,
|
|
@@ -36720,23 +37535,92 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36720
37535
|
line: (line, column) => `${line} 行:${column}`,
|
|
36721
37536
|
diffFiles: (count) => `差分ファイル ${count} 件`,
|
|
36722
37537
|
typeToSearchFiles: "リポジトリ内のファイル名を入力してください",
|
|
37538
|
+
recentFiles: (count) => `最近開いたファイル - ${count} 件`,
|
|
36723
37539
|
loadingFiles: "ファイルを読み込み中...",
|
|
36724
|
-
results: (count) => `${count}
|
|
37540
|
+
results: (count, total, candidatesTruncated) => (total === void 0 ? `${count} 件` : `${total} 件中 ${count} 件`) + (candidatesTruncated ? "・ファイル一覧は上限で打ち切り" : ""),
|
|
36725
37541
|
noResults: "該当なし",
|
|
36726
37542
|
typeToGrep: "検索するコードを入力してください",
|
|
36727
37543
|
invalidRegex: "正規表現が正しくありません",
|
|
36728
37544
|
searching: "検索中...",
|
|
36729
37545
|
repositoryChanged: "リポジトリが変更されました。もう一度検索してください",
|
|
36730
|
-
grepSummary: ({
|
|
37546
|
+
grepSummary: ({
|
|
37547
|
+
engine,
|
|
37548
|
+
regex,
|
|
37549
|
+
caseSensitive,
|
|
37550
|
+
wholeWord,
|
|
37551
|
+
testsExcluded,
|
|
37552
|
+
truncated,
|
|
37553
|
+
count,
|
|
37554
|
+
paths
|
|
37555
|
+
}) => engine + (regex ? "・正規表現" : "・通常") + (caseSensitive ? "・大文字小文字を区別" : "") + (wholeWord ? "・単語単位" : "") + (testsExcluded ? "・テスト除外" : "") + (paths.length ? `・${paths.join(" ")} 内` : "") + (truncated ? "・一部表示" : "") + ` - ${count} 件`,
|
|
36731
37556
|
searchFailed: (error2) => `検索に失敗しました: ${error2}`,
|
|
36732
37557
|
savingSelection: "選択履歴を保存中...",
|
|
36733
37558
|
selectionSaveFailed: (error2) => `選択履歴を保存できませんでした: ${error2}`,
|
|
36734
|
-
unknownError: "不明なエラー"
|
|
37559
|
+
unknownError: "不明なエラー",
|
|
37560
|
+
pinResults: "固定",
|
|
37561
|
+
pinResultsTitle: "この結果を下パネルに出したまま閲覧を続ける (Ctrl+Enter)",
|
|
37562
|
+
resultsTitle: "検索",
|
|
37563
|
+
resultsOpen: "検索結果パネルを開く",
|
|
37564
|
+
resultsRun: "検索",
|
|
37565
|
+
resultsPlaceholder: "検索するコード(path:<ディレクトリ or glob> で絞り込み)",
|
|
37566
|
+
resultsIdle: "検索語を入力して Enter",
|
|
37567
|
+
resultsScope: (ref) => `${ref} 内`
|
|
36735
37568
|
};
|
|
36736
37569
|
function searchPaletteText(language) {
|
|
36737
37570
|
return language === "ja" ? JA2 : EN2;
|
|
36738
37571
|
}
|
|
36739
37572
|
|
|
37573
|
+
// web-src/views/text-mark.ts
|
|
37574
|
+
function findTextRange(lineText, needle, options = {}) {
|
|
37575
|
+
if (!needle) return null;
|
|
37576
|
+
const haystack = options.caseSensitive ? lineText : lineText.toLowerCase();
|
|
37577
|
+
const target = options.caseSensitive ? needle : needle.toLowerCase();
|
|
37578
|
+
const expectedStart = Math.max(0, (options.nearColumn ?? 1) - 1);
|
|
37579
|
+
let bestStart = -1;
|
|
37580
|
+
let cursor = haystack.indexOf(target);
|
|
37581
|
+
while (cursor >= 0) {
|
|
37582
|
+
if (bestStart < 0 || Math.abs(cursor - expectedStart) < Math.abs(bestStart - expectedStart))
|
|
37583
|
+
bestStart = cursor;
|
|
37584
|
+
cursor = haystack.indexOf(target, cursor + Math.max(1, target.length));
|
|
37585
|
+
}
|
|
37586
|
+
return bestStart < 0 ? null : { start: bestStart, end: bestStart + needle.length };
|
|
37587
|
+
}
|
|
37588
|
+
function markTextRange(cell, start, end, className) {
|
|
37589
|
+
if (end <= start) return;
|
|
37590
|
+
const walker = document.createTreeWalker(cell, NodeFilter.SHOW_TEXT);
|
|
37591
|
+
const parts = [];
|
|
37592
|
+
let offset = 0;
|
|
37593
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
37594
|
+
const textNode = node;
|
|
37595
|
+
const nextOffset = offset + textNode.data.length;
|
|
37596
|
+
const partStart = Math.max(start, offset);
|
|
37597
|
+
const partEnd = Math.min(end, nextOffset);
|
|
37598
|
+
if (partStart < partEnd)
|
|
37599
|
+
parts.push({
|
|
37600
|
+
node: textNode,
|
|
37601
|
+
start: partStart - offset,
|
|
37602
|
+
end: partEnd - offset
|
|
37603
|
+
});
|
|
37604
|
+
offset = nextOffset;
|
|
37605
|
+
}
|
|
37606
|
+
for (const part of parts.reverse()) {
|
|
37607
|
+
const selected = part.node.splitText(part.start);
|
|
37608
|
+
selected.splitText(part.end - part.start);
|
|
37609
|
+
const mark = document.createElement("mark");
|
|
37610
|
+
mark.className = className;
|
|
37611
|
+
const parent = selected.parentNode;
|
|
37612
|
+
if (!parent) throw new Error("marked text is detached");
|
|
37613
|
+
parent.insertBefore(mark, selected);
|
|
37614
|
+
mark.appendChild(selected);
|
|
37615
|
+
}
|
|
37616
|
+
}
|
|
37617
|
+
function markNeedleInCell(cell, lineText, needle, className, options = {}) {
|
|
37618
|
+
const range = findTextRange(lineText, needle, options);
|
|
37619
|
+
if (!range) return false;
|
|
37620
|
+
markTextRange(cell, range.start, range.end, className);
|
|
37621
|
+
return true;
|
|
37622
|
+
}
|
|
37623
|
+
|
|
36740
37624
|
// web-src/views/search-palette-ui.ts
|
|
36741
37625
|
function createSearchPalette(deps) {
|
|
36742
37626
|
const {
|
|
@@ -36760,14 +37644,18 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36760
37644
|
getFileSelectionHistory,
|
|
36761
37645
|
getGrepSelectionHistory,
|
|
36762
37646
|
getGrepRegex,
|
|
37647
|
+
getGrepCaseSensitive,
|
|
37648
|
+
getGrepWholeWord,
|
|
36763
37649
|
getGrepHideTests,
|
|
36764
37650
|
getGrepGroupByFile,
|
|
36765
37651
|
getGrepPaletteWidth,
|
|
36766
37652
|
getGrepPaletteHeight,
|
|
36767
37653
|
persistGrepSettings,
|
|
36768
|
-
applyGrepHideTests
|
|
37654
|
+
applyGrepHideTests,
|
|
37655
|
+
openSearchResults
|
|
36769
37656
|
} = deps;
|
|
36770
37657
|
let PALETTE = null;
|
|
37658
|
+
const LAST_QUERY = { file: "", grep: "" };
|
|
36771
37659
|
let repoFileRequestGeneration = 0;
|
|
36772
37660
|
const GREP_CONTEXT_RADIUS = 5;
|
|
36773
37661
|
const FILE_PREVIEW_LINE_LIMIT = 200;
|
|
@@ -36804,6 +37692,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36804
37692
|
}
|
|
36805
37693
|
function closeSearchPalette() {
|
|
36806
37694
|
if (!PALETTE) return;
|
|
37695
|
+
LAST_QUERY[PALETTE.mode] = PALETTE.input.value;
|
|
36807
37696
|
const previousFocusScope = PALETTE.previousFocusScope;
|
|
36808
37697
|
PALETTE.controller?.abort();
|
|
36809
37698
|
PALETTE.previewController?.abort();
|
|
@@ -36815,6 +37704,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36815
37704
|
}
|
|
36816
37705
|
function createPalette(mode) {
|
|
36817
37706
|
const previousFocusScope = PALETTE ? PALETTE.previousFocusScope : getPanelFocusScope();
|
|
37707
|
+
const initialQuery = PALETTE ? PALETTE.input.value : LAST_QUERY[mode];
|
|
36818
37708
|
closeSearchPalette();
|
|
36819
37709
|
const root = document.createElement("div");
|
|
36820
37710
|
root.className = "gdp-palette-backdrop";
|
|
@@ -36833,7 +37723,19 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36833
37723
|
dialog.style.height = `${clampGrepPaletteHeight(savedHeight)}px`;
|
|
36834
37724
|
const label = document.createElement("div");
|
|
36835
37725
|
label.className = "gdp-palette-label";
|
|
36836
|
-
|
|
37726
|
+
for (const switchMode of ["file", "grep"]) {
|
|
37727
|
+
const button = document.createElement("button");
|
|
37728
|
+
button.type = "button";
|
|
37729
|
+
button.className = "gdp-palette-mode-button gdp-palette-mode-switch";
|
|
37730
|
+
button.setAttribute("aria-pressed", String(switchMode === mode));
|
|
37731
|
+
button.textContent = switchMode === "file" ? text3().files : text3().grep;
|
|
37732
|
+
button.title = switchMode === "file" ? text3().switchToFiles : text3().switchToGrep;
|
|
37733
|
+
button.addEventListener("mousedown", (event) => {
|
|
37734
|
+
event.preventDefault();
|
|
37735
|
+
if (switchMode !== mode) createPalette(switchMode);
|
|
37736
|
+
});
|
|
37737
|
+
label.appendChild(button);
|
|
37738
|
+
}
|
|
36837
37739
|
const input2 = document.createElement("input");
|
|
36838
37740
|
input2.className = "gdp-palette-input";
|
|
36839
37741
|
input2.type = "search";
|
|
@@ -36843,6 +37745,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36843
37745
|
input2.setAttribute("role", "combobox");
|
|
36844
37746
|
input2.setAttribute("aria-expanded", "true");
|
|
36845
37747
|
input2.setAttribute("aria-controls", "gdp-palette-list");
|
|
37748
|
+
input2.value = initialQuery;
|
|
36846
37749
|
const status = document.createElement("div");
|
|
36847
37750
|
status.className = "gdp-palette-status";
|
|
36848
37751
|
const controls = document.createElement("div");
|
|
@@ -36886,6 +37789,8 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36886
37789
|
preview,
|
|
36887
37790
|
mode,
|
|
36888
37791
|
grepRegex: getGrepRegex(),
|
|
37792
|
+
grepCaseSensitive: getGrepCaseSensitive(),
|
|
37793
|
+
grepWholeWord: getGrepWholeWord(),
|
|
36889
37794
|
grepHideTests: getGrepHideTests(),
|
|
36890
37795
|
grepGroupByFile: getGrepGroupByFile(),
|
|
36891
37796
|
grepPaletteWidth: clampGrepPaletteWidth(savedWidth ?? dialogRect.width),
|
|
@@ -36957,6 +37862,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
36957
37862
|
input2.addEventListener("input", () => updatePaletteResults(state));
|
|
36958
37863
|
input2.addEventListener("keydown", (e2) => handlePaletteKeydown(e2, state));
|
|
36959
37864
|
input2.focus();
|
|
37865
|
+
if (input2.value) input2.select();
|
|
36960
37866
|
updatePaletteResults(state);
|
|
36961
37867
|
return state;
|
|
36962
37868
|
}
|
|
@@ -37004,6 +37910,38 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37004
37910
|
e2.preventDefault();
|
|
37005
37911
|
void updateGrepRegex(state, true);
|
|
37006
37912
|
});
|
|
37913
|
+
const matchCase = document.createElement("button");
|
|
37914
|
+
matchCase.type = "button";
|
|
37915
|
+
matchCase.className = "gdp-palette-mode-button";
|
|
37916
|
+
matchCase.setAttribute("aria-pressed", String(state.grepCaseSensitive));
|
|
37917
|
+
matchCase.textContent = text3().matchCase;
|
|
37918
|
+
matchCase.title = text3().matchCaseTitle;
|
|
37919
|
+
matchCase.disabled = state.settingsPending;
|
|
37920
|
+
matchCase.addEventListener("mousedown", (e2) => {
|
|
37921
|
+
e2.preventDefault();
|
|
37922
|
+
void updateGrepFlag(
|
|
37923
|
+
state,
|
|
37924
|
+
"grepCaseSensitive",
|
|
37925
|
+
!state.grepCaseSensitive,
|
|
37926
|
+
text3().caseSensitivity
|
|
37927
|
+
);
|
|
37928
|
+
});
|
|
37929
|
+
const wholeWord = document.createElement("button");
|
|
37930
|
+
wholeWord.type = "button";
|
|
37931
|
+
wholeWord.className = "gdp-palette-mode-button";
|
|
37932
|
+
wholeWord.setAttribute("aria-pressed", String(state.grepWholeWord));
|
|
37933
|
+
wholeWord.textContent = text3().wholeWord;
|
|
37934
|
+
wholeWord.title = text3().wholeWordTitle;
|
|
37935
|
+
wholeWord.disabled = state.settingsPending;
|
|
37936
|
+
wholeWord.addEventListener("mousedown", (e2) => {
|
|
37937
|
+
e2.preventDefault();
|
|
37938
|
+
void updateGrepFlag(
|
|
37939
|
+
state,
|
|
37940
|
+
"grepWholeWord",
|
|
37941
|
+
!state.grepWholeWord,
|
|
37942
|
+
text3().wordMatching
|
|
37943
|
+
);
|
|
37944
|
+
});
|
|
37007
37945
|
const excludeTests = createExcludeTestsButton(state);
|
|
37008
37946
|
const groupFiles = document.createElement("button");
|
|
37009
37947
|
groupFiles.type = "button";
|
|
@@ -37018,8 +37956,62 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37018
37956
|
});
|
|
37019
37957
|
const hint = document.createElement("span");
|
|
37020
37958
|
hint.className = "gdp-palette-mode-hint";
|
|
37021
|
-
hint.textContent = text3().
|
|
37022
|
-
state.controls.append(
|
|
37959
|
+
hint.textContent = text3().grepHint;
|
|
37960
|
+
state.controls.append(
|
|
37961
|
+
plain,
|
|
37962
|
+
regex,
|
|
37963
|
+
matchCase,
|
|
37964
|
+
wholeWord,
|
|
37965
|
+
excludeTests,
|
|
37966
|
+
groupFiles
|
|
37967
|
+
);
|
|
37968
|
+
if (openSearchResults) {
|
|
37969
|
+
const pin = document.createElement("button");
|
|
37970
|
+
pin.type = "button";
|
|
37971
|
+
pin.className = "gdp-palette-mode-button gdp-palette-pin";
|
|
37972
|
+
pin.textContent = text3().pinResults;
|
|
37973
|
+
pin.title = text3().pinResultsTitle;
|
|
37974
|
+
pin.addEventListener("mousedown", (e2) => {
|
|
37975
|
+
e2.preventDefault();
|
|
37976
|
+
pinResults(state);
|
|
37977
|
+
});
|
|
37978
|
+
state.controls.append(pin);
|
|
37979
|
+
}
|
|
37980
|
+
state.controls.append(hint);
|
|
37981
|
+
}
|
|
37982
|
+
function pinResults(state) {
|
|
37983
|
+
if (!openSearchResults) return;
|
|
37984
|
+
const query = state.input.value;
|
|
37985
|
+
closeSearchPalette();
|
|
37986
|
+
openSearchResults(query);
|
|
37987
|
+
}
|
|
37988
|
+
async function updateGrepFlag(state, flag, value, label) {
|
|
37989
|
+
if (state.settingsPending || value === state[flag]) return;
|
|
37990
|
+
const previous = state[flag];
|
|
37991
|
+
state[flag] = value;
|
|
37992
|
+
state.settingsPending = true;
|
|
37993
|
+
renderPaletteControls(state);
|
|
37994
|
+
try {
|
|
37995
|
+
await persistGrepSettings(
|
|
37996
|
+
flag === "grepCaseSensitive" ? { grepCaseSensitive: value } : { grepWholeWord: value }
|
|
37997
|
+
);
|
|
37998
|
+
if (PALETTE === state) updatePaletteResults(state);
|
|
37999
|
+
} catch (err) {
|
|
38000
|
+
console.error(`Failed to save grep ${label}`, err);
|
|
38001
|
+
state[flag] = previous;
|
|
38002
|
+
if (PALETTE === state) {
|
|
38003
|
+
state.status.textContent = text3().saveFailed(
|
|
38004
|
+
label,
|
|
38005
|
+
errorMessage2(err, text3().unknownError)
|
|
38006
|
+
);
|
|
38007
|
+
}
|
|
38008
|
+
} finally {
|
|
38009
|
+
state.settingsPending = false;
|
|
38010
|
+
if (PALETTE === state) {
|
|
38011
|
+
renderPaletteControls(state);
|
|
38012
|
+
state.input.focus();
|
|
38013
|
+
}
|
|
38014
|
+
}
|
|
37023
38015
|
}
|
|
37024
38016
|
function errorMessage2(err, fallback) {
|
|
37025
38017
|
return err instanceof Error && err.message ? err.message : fallback;
|
|
@@ -37144,51 +38136,16 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37144
38136
|
if (cursor < path.length)
|
|
37145
38137
|
parent.appendChild(document.createTextNode(path.slice(cursor)));
|
|
37146
38138
|
}
|
|
37147
|
-
function
|
|
37148
|
-
|
|
37149
|
-
if (!matchText) return null;
|
|
37150
|
-
const caseSensitive = item.regex || /[A-Z]/.test(item.query);
|
|
37151
|
-
const haystack = caseSensitive ? lineText : lineText.toLowerCase();
|
|
37152
|
-
const needle = caseSensitive ? matchText : matchText.toLowerCase();
|
|
37153
|
-
const expectedStart = Math.max(0, item.column - 1);
|
|
37154
|
-
let bestStart = -1;
|
|
37155
|
-
let cursor = haystack.indexOf(needle);
|
|
37156
|
-
while (cursor >= 0) {
|
|
37157
|
-
if (bestStart < 0 || Math.abs(cursor - expectedStart) < Math.abs(bestStart - expectedStart))
|
|
37158
|
-
bestStart = cursor;
|
|
37159
|
-
cursor = haystack.indexOf(needle, cursor + Math.max(1, needle.length));
|
|
37160
|
-
}
|
|
37161
|
-
return bestStart < 0 ? null : { start: bestStart, end: bestStart + matchText.length };
|
|
38139
|
+
function grepHighlightText(item) {
|
|
38140
|
+
return item.matchText || (item.regex ? "" : item.term);
|
|
37162
38141
|
}
|
|
37163
38142
|
function highlightGrepMatch(cell, lineText, item) {
|
|
37164
|
-
const
|
|
37165
|
-
if (!
|
|
37166
|
-
|
|
37167
|
-
|
|
37168
|
-
|
|
37169
|
-
|
|
37170
|
-
const textNode = node;
|
|
37171
|
-
const nextOffset = offset + textNode.data.length;
|
|
37172
|
-
const start = Math.max(match2.start, offset);
|
|
37173
|
-
const end = Math.min(match2.end, nextOffset);
|
|
37174
|
-
if (start < end)
|
|
37175
|
-
parts.push({
|
|
37176
|
-
node: textNode,
|
|
37177
|
-
start: start - offset,
|
|
37178
|
-
end: end - offset
|
|
37179
|
-
});
|
|
37180
|
-
offset = nextOffset;
|
|
37181
|
-
}
|
|
37182
|
-
for (const part of parts.reverse()) {
|
|
37183
|
-
const selected = part.node.splitText(part.start);
|
|
37184
|
-
selected.splitText(part.end - part.start);
|
|
37185
|
-
const mark = document.createElement("mark");
|
|
37186
|
-
mark.className = "gdp-grep-match";
|
|
37187
|
-
const parent = selected.parentNode;
|
|
37188
|
-
if (!parent) throw new Error("grep match text is detached");
|
|
37189
|
-
parent.insertBefore(mark, selected);
|
|
37190
|
-
mark.appendChild(selected);
|
|
37191
|
-
}
|
|
38143
|
+
const needle = grepHighlightText(item);
|
|
38144
|
+
if (!needle) return;
|
|
38145
|
+
markNeedleInCell(cell, lineText, needle, "gdp-grep-match", {
|
|
38146
|
+
caseSensitive: item.caseSensitive,
|
|
38147
|
+
nearColumn: item.column
|
|
38148
|
+
});
|
|
37192
38149
|
}
|
|
37193
38150
|
function palettePreviewTarget(item) {
|
|
37194
38151
|
return item.kind === "file" ? {
|
|
@@ -37490,27 +38447,73 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37490
38447
|
).sort(
|
|
37491
38448
|
(a2, b2) => b2.match.score - a2.match.score || a2.file.path.localeCompare(b2.file.path)
|
|
37492
38449
|
);
|
|
37493
|
-
return
|
|
37494
|
-
|
|
37495
|
-
|
|
37496
|
-
|
|
37497
|
-
|
|
37498
|
-
|
|
37499
|
-
|
|
37500
|
-
|
|
37501
|
-
|
|
37502
|
-
|
|
37503
|
-
|
|
37504
|
-
|
|
37505
|
-
|
|
37506
|
-
|
|
38450
|
+
return {
|
|
38451
|
+
total: candidates.length,
|
|
38452
|
+
items: limitPaletteResults(
|
|
38453
|
+
rankPaletteResultsByHistory(
|
|
38454
|
+
candidates.map((candidate) => ({
|
|
38455
|
+
kind: "file",
|
|
38456
|
+
path: candidate.file.path,
|
|
38457
|
+
old_path: candidate.file.old_path,
|
|
38458
|
+
displayPath: candidate.displayPath,
|
|
38459
|
+
ref: paletteRef("diff"),
|
|
38460
|
+
targetPath: fileSourceTarget(candidate.file).path,
|
|
38461
|
+
targetRef: fileSourceTarget(candidate.file).ref,
|
|
38462
|
+
source: "diff",
|
|
38463
|
+
ranges: candidate.match.ranges
|
|
38464
|
+
})),
|
|
38465
|
+
state.fileHistory
|
|
38466
|
+
)
|
|
37507
38467
|
)
|
|
38468
|
+
};
|
|
38469
|
+
}
|
|
38470
|
+
async function updateRecentFilePalette(state, source) {
|
|
38471
|
+
const recent = [...state.fileHistory].reverse();
|
|
38472
|
+
if (recent.length === 0) {
|
|
38473
|
+
state.items = [];
|
|
38474
|
+
state.selected = -1;
|
|
38475
|
+
state.status.textContent = text3().typeToSearchFiles;
|
|
38476
|
+
renderPalette(state);
|
|
38477
|
+
return;
|
|
38478
|
+
}
|
|
38479
|
+
state.status.textContent = text3().loadingFiles;
|
|
38480
|
+
const ref = paletteRef(source);
|
|
38481
|
+
const requestGeneration = ++repoFileRequestGeneration;
|
|
38482
|
+
let response;
|
|
38483
|
+
try {
|
|
38484
|
+
response = await repoPaletteFiles(ref);
|
|
38485
|
+
} catch (err) {
|
|
38486
|
+
if (requestGeneration !== repoFileRequestGeneration) return;
|
|
38487
|
+
throw err;
|
|
38488
|
+
}
|
|
38489
|
+
if (PALETTE !== state || state.input.value.trim() !== "" || requestGeneration !== repoFileRequestGeneration)
|
|
38490
|
+
return;
|
|
38491
|
+
REPO_FILE_CACHE.set(repoFileCacheKey(ref), response);
|
|
38492
|
+
const existing = new Set(response.files.map((file) => file.path));
|
|
38493
|
+
state.items = limitPaletteResults(
|
|
38494
|
+
recent.filter(
|
|
38495
|
+
(path) => existing.has(path) && (!state.grepHideTests || !isTestFilePath(path))
|
|
38496
|
+
).map((path) => ({
|
|
38497
|
+
kind: "file",
|
|
38498
|
+
path,
|
|
38499
|
+
displayPath: path,
|
|
38500
|
+
ref,
|
|
38501
|
+
source,
|
|
38502
|
+
ranges: []
|
|
38503
|
+
}))
|
|
37508
38504
|
);
|
|
38505
|
+
state.selected = state.items.length ? 0 : -1;
|
|
38506
|
+
state.status.textContent = state.items.length ? text3().recentFiles(state.items.length) : text3().typeToSearchFiles;
|
|
38507
|
+
renderPalette(state);
|
|
37509
38508
|
}
|
|
37510
38509
|
async function updateFilePalette(state, query) {
|
|
37511
38510
|
renderPaletteControls(state);
|
|
37512
38511
|
const source = paletteSource();
|
|
37513
38512
|
if (!query.trim()) {
|
|
38513
|
+
if (source === "repo") {
|
|
38514
|
+
await updateRecentFilePalette(state, source);
|
|
38515
|
+
return;
|
|
38516
|
+
}
|
|
37514
38517
|
const base2 = source === "diff" ? state.diffSnapshot.filter(
|
|
37515
38518
|
(file) => !state.grepHideTests || !isTestFilePath(file.path)
|
|
37516
38519
|
).map((file) => {
|
|
@@ -37535,8 +38538,12 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37535
38538
|
renderPalette(state);
|
|
37536
38539
|
return;
|
|
37537
38540
|
}
|
|
38541
|
+
let totalMatches = 0;
|
|
38542
|
+
let candidatesTruncated = false;
|
|
37538
38543
|
if (source === "diff") {
|
|
37539
|
-
|
|
38544
|
+
const ranked = diffFilePaletteItems(state, query);
|
|
38545
|
+
state.items = ranked.items;
|
|
38546
|
+
totalMatches = ranked.total;
|
|
37540
38547
|
} else {
|
|
37541
38548
|
state.status.textContent = text3().loadingFiles;
|
|
37542
38549
|
const ref = paletteRef(source);
|
|
@@ -37554,9 +38561,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37554
38561
|
const visibleFiles = response.files.filter(
|
|
37555
38562
|
(file) => !state.grepHideTests || !isTestFilePath(file.path)
|
|
37556
38563
|
);
|
|
38564
|
+
const stats = { total: 0 };
|
|
37557
38565
|
state.items = limitPaletteResults(
|
|
37558
38566
|
rankPaletteResultsByHistory(
|
|
37559
|
-
rankPathMatches(query, visibleFiles, PALETTE_RESULT_LIMIT).map(
|
|
38567
|
+
rankPathMatches(query, visibleFiles, PALETTE_RESULT_LIMIT, stats).map(
|
|
37560
38568
|
(match2) => ({
|
|
37561
38569
|
kind: "file",
|
|
37562
38570
|
path: match2.item.path,
|
|
@@ -37569,23 +38577,37 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37569
38577
|
state.fileHistory
|
|
37570
38578
|
)
|
|
37571
38579
|
);
|
|
38580
|
+
totalMatches = stats.total;
|
|
38581
|
+
candidatesTruncated = response.truncated;
|
|
37572
38582
|
}
|
|
37573
38583
|
state.selected = state.items.length ? 0 : -1;
|
|
37574
|
-
state.status.textContent = state.items.length ? text3().results(
|
|
38584
|
+
state.status.textContent = state.items.length ? text3().results(
|
|
38585
|
+
state.items.length,
|
|
38586
|
+
totalMatches > state.items.length ? totalMatches : void 0,
|
|
38587
|
+
candidatesTruncated
|
|
38588
|
+
) : text3().noResults;
|
|
37575
38589
|
renderPalette(state);
|
|
37576
38590
|
}
|
|
38591
|
+
function diffPathInScope(path, scopes) {
|
|
38592
|
+
if (scopes.length === 0) return true;
|
|
38593
|
+
return scopes.some(
|
|
38594
|
+
(scope) => isGlobPathQuery(scope) ? !!globMatchPath(scope, path) : path === scope || path.startsWith(`${scope.replace(/\/+$/, "")}/`)
|
|
38595
|
+
);
|
|
38596
|
+
}
|
|
37577
38597
|
function updateGrepPalette(state, query) {
|
|
37578
38598
|
renderPaletteControls(state);
|
|
37579
38599
|
state.controller?.abort();
|
|
37580
38600
|
if (state.debounce) window.clearTimeout(state.debounce);
|
|
37581
|
-
|
|
38601
|
+
const parsed = parseGrepQuery(query);
|
|
38602
|
+
const term = parsed.term;
|
|
38603
|
+
if (!term) {
|
|
37582
38604
|
state.items = [];
|
|
37583
38605
|
state.selected = -1;
|
|
37584
38606
|
state.status.textContent = text3().typeToGrep;
|
|
37585
38607
|
renderPalette(state);
|
|
37586
38608
|
return;
|
|
37587
38609
|
}
|
|
37588
|
-
if (state.grepRegex && !regexQueryIsValid(
|
|
38610
|
+
if (state.grepRegex && !regexQueryIsValid(term)) {
|
|
37589
38611
|
state.controller?.abort();
|
|
37590
38612
|
state.items = [];
|
|
37591
38613
|
state.selected = -1;
|
|
@@ -37601,16 +38623,30 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37601
38623
|
const source = paletteSource();
|
|
37602
38624
|
const ref = paletteRef(source);
|
|
37603
38625
|
const regex = state.grepRegex;
|
|
38626
|
+
const caseSensitive = state.grepCaseSensitive;
|
|
38627
|
+
const wholeWord = state.grepWholeWord;
|
|
37604
38628
|
const hideTests = state.grepHideTests;
|
|
37605
|
-
const params =
|
|
37606
|
-
|
|
37607
|
-
|
|
37608
|
-
|
|
37609
|
-
|
|
37610
|
-
|
|
38629
|
+
const params = buildGrepRequestParams({
|
|
38630
|
+
term,
|
|
38631
|
+
ref,
|
|
38632
|
+
regex,
|
|
38633
|
+
caseSensitive,
|
|
38634
|
+
wholeWord,
|
|
38635
|
+
hideTests,
|
|
38636
|
+
max: 200
|
|
38637
|
+
});
|
|
37611
38638
|
appendScopeParams(params);
|
|
37612
38639
|
if (source === "diff") {
|
|
37613
|
-
|
|
38640
|
+
const scoped = state.diffSnapshot.filter(
|
|
38641
|
+
(file) => diffPathInScope(file.path, parsed.paths)
|
|
38642
|
+
);
|
|
38643
|
+
if (scoped.length === 0) {
|
|
38644
|
+
state.status.textContent = text3().noResults;
|
|
38645
|
+
return;
|
|
38646
|
+
}
|
|
38647
|
+
for (const file of scoped) params.append("path", file.path);
|
|
38648
|
+
} else {
|
|
38649
|
+
for (const scope of parsed.paths) params.append("path", scope);
|
|
37614
38650
|
}
|
|
37615
38651
|
const controller = new AbortController();
|
|
37616
38652
|
state.controller = controller;
|
|
@@ -37625,7 +38661,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37625
38661
|
return r2.json();
|
|
37626
38662
|
})
|
|
37627
38663
|
).then((response) => {
|
|
37628
|
-
if (PALETTE !== state || controller.signal.aborted || state.input.value !== query || state.grepRegex !== regex || state.grepHideTests !== hideTests)
|
|
38664
|
+
if (PALETTE !== state || controller.signal.aborted || state.input.value !== query || state.grepRegex !== regex || state.grepCaseSensitive !== caseSensitive || state.grepWholeWord !== wholeWord || state.grepHideTests !== hideTests)
|
|
37629
38665
|
return;
|
|
37630
38666
|
if (responseGenerationIsStale(response.generation)) {
|
|
37631
38667
|
state.items = [];
|
|
@@ -37646,7 +38682,9 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37646
38682
|
preview: match2.preview,
|
|
37647
38683
|
matchText: match2.matchText,
|
|
37648
38684
|
query,
|
|
38685
|
+
term,
|
|
37649
38686
|
regex,
|
|
38687
|
+
caseSensitive,
|
|
37650
38688
|
ref,
|
|
37651
38689
|
source
|
|
37652
38690
|
}))
|
|
@@ -37655,9 +38693,12 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37655
38693
|
state.status.textContent = text3().grepSummary({
|
|
37656
38694
|
engine: response.engine,
|
|
37657
38695
|
regex,
|
|
38696
|
+
caseSensitive,
|
|
38697
|
+
wholeWord,
|
|
37658
38698
|
testsExcluded: hideTests,
|
|
37659
38699
|
truncated: response.truncated,
|
|
37660
|
-
count: state.items.length
|
|
38700
|
+
count: state.items.length,
|
|
38701
|
+
paths: parsed.paths
|
|
37661
38702
|
});
|
|
37662
38703
|
renderPalette(state);
|
|
37663
38704
|
}).catch((err) => {
|
|
@@ -37748,12 +38789,14 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37748
38789
|
});
|
|
37749
38790
|
scrollToFile(item.path, item.line);
|
|
37750
38791
|
} else {
|
|
38792
|
+
const hl = grepHighlightText(item);
|
|
37751
38793
|
setRoute({
|
|
37752
38794
|
screen: "file",
|
|
37753
38795
|
path: item.path,
|
|
37754
38796
|
ref: item.ref,
|
|
37755
38797
|
view: "blob",
|
|
37756
38798
|
line: item.line,
|
|
38799
|
+
...hl ? { hl } : {},
|
|
37757
38800
|
range: currentRange()
|
|
37758
38801
|
});
|
|
37759
38802
|
void renderStandaloneSource({ path: item.path, ref: item.ref });
|
|
@@ -37769,6 +38812,10 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37769
38812
|
if (e2.key === "Enter") {
|
|
37770
38813
|
if (state.composing) return;
|
|
37771
38814
|
e2.preventDefault();
|
|
38815
|
+
if (state.mode === "grep" && (e2.ctrlKey || e2.metaKey) && openSearchResults) {
|
|
38816
|
+
pinResults(state);
|
|
38817
|
+
return;
|
|
38818
|
+
}
|
|
37772
38819
|
void selectPaletteItem(state);
|
|
37773
38820
|
return;
|
|
37774
38821
|
}
|
|
@@ -37777,6 +38824,26 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37777
38824
|
void updateGrepRegex(state, !state.grepRegex);
|
|
37778
38825
|
return;
|
|
37779
38826
|
}
|
|
38827
|
+
if (state.mode === "grep" && e2.altKey && e2.key.toLowerCase() === "c") {
|
|
38828
|
+
e2.preventDefault();
|
|
38829
|
+
void updateGrepFlag(
|
|
38830
|
+
state,
|
|
38831
|
+
"grepCaseSensitive",
|
|
38832
|
+
!state.grepCaseSensitive,
|
|
38833
|
+
text3().caseSensitivity
|
|
38834
|
+
);
|
|
38835
|
+
return;
|
|
38836
|
+
}
|
|
38837
|
+
if (state.mode === "grep" && e2.altKey && e2.key.toLowerCase() === "w") {
|
|
38838
|
+
e2.preventDefault();
|
|
38839
|
+
void updateGrepFlag(
|
|
38840
|
+
state,
|
|
38841
|
+
"grepWholeWord",
|
|
38842
|
+
!state.grepWholeWord,
|
|
38843
|
+
text3().wordMatching
|
|
38844
|
+
);
|
|
38845
|
+
return;
|
|
38846
|
+
}
|
|
37780
38847
|
const direction = e2.key === "ArrowDown" || e2.ctrlKey && e2.key.toLowerCase() === "n" ? 1 : e2.key === "ArrowUp" || e2.ctrlKey && e2.key.toLowerCase() === "p" ? -1 : 0;
|
|
37781
38848
|
if (direction) {
|
|
37782
38849
|
e2.preventDefault();
|
|
@@ -37809,6 +38876,317 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37809
38876
|
};
|
|
37810
38877
|
}
|
|
37811
38878
|
|
|
38879
|
+
// web-src/views/search-results-view.ts
|
|
38880
|
+
var RESULTS_MAX = 500;
|
|
38881
|
+
function createSearchResultsView(deps) {
|
|
38882
|
+
let mounted = false;
|
|
38883
|
+
let input2 = null;
|
|
38884
|
+
let runButton = null;
|
|
38885
|
+
let controls = null;
|
|
38886
|
+
let status = null;
|
|
38887
|
+
let list2 = null;
|
|
38888
|
+
let query = "";
|
|
38889
|
+
let controller = null;
|
|
38890
|
+
let generation = 0;
|
|
38891
|
+
let settingsPending = false;
|
|
38892
|
+
let lastResponse = null;
|
|
38893
|
+
let activeKey = "";
|
|
38894
|
+
const text3 = () => searchPaletteText(deps.getLanguage());
|
|
38895
|
+
function host() {
|
|
38896
|
+
return deps.$("#search-sheet");
|
|
38897
|
+
}
|
|
38898
|
+
function toggleButton(label, title, pressed, onToggle) {
|
|
38899
|
+
const button = document.createElement("button");
|
|
38900
|
+
button.type = "button";
|
|
38901
|
+
button.className = "gdp-palette-mode-button";
|
|
38902
|
+
button.textContent = label;
|
|
38903
|
+
button.title = title;
|
|
38904
|
+
button.setAttribute("aria-pressed", String(pressed));
|
|
38905
|
+
button.disabled = settingsPending;
|
|
38906
|
+
button.addEventListener("click", (event) => {
|
|
38907
|
+
event.preventDefault();
|
|
38908
|
+
onToggle();
|
|
38909
|
+
});
|
|
38910
|
+
return button;
|
|
38911
|
+
}
|
|
38912
|
+
async function updateFlag(patch) {
|
|
38913
|
+
if (settingsPending) return;
|
|
38914
|
+
settingsPending = true;
|
|
38915
|
+
renderControls();
|
|
38916
|
+
try {
|
|
38917
|
+
await deps.persistGrepSettings(patch);
|
|
38918
|
+
} catch (err) {
|
|
38919
|
+
console.error("Failed to save search option", err);
|
|
38920
|
+
if (status)
|
|
38921
|
+
status.textContent = text3().saveFailed(
|
|
38922
|
+
text3().regexMode,
|
|
38923
|
+
err instanceof Error && err.message ? err.message : text3().unknownError
|
|
38924
|
+
);
|
|
38925
|
+
} finally {
|
|
38926
|
+
settingsPending = false;
|
|
38927
|
+
renderControls();
|
|
38928
|
+
if (query) run();
|
|
38929
|
+
}
|
|
38930
|
+
}
|
|
38931
|
+
function renderControls() {
|
|
38932
|
+
if (!controls) return;
|
|
38933
|
+
const current = text3();
|
|
38934
|
+
controls.replaceChildren(
|
|
38935
|
+
toggleButton(
|
|
38936
|
+
current.plain,
|
|
38937
|
+
current.plain,
|
|
38938
|
+
!deps.getGrepRegex(),
|
|
38939
|
+
() => void updateFlag({ grepRegex: false })
|
|
38940
|
+
),
|
|
38941
|
+
toggleButton(
|
|
38942
|
+
current.regex,
|
|
38943
|
+
"Alt+R",
|
|
38944
|
+
deps.getGrepRegex(),
|
|
38945
|
+
() => void updateFlag({ grepRegex: true })
|
|
38946
|
+
),
|
|
38947
|
+
toggleButton(
|
|
38948
|
+
current.matchCase,
|
|
38949
|
+
current.matchCaseTitle,
|
|
38950
|
+
deps.getGrepCaseSensitive(),
|
|
38951
|
+
() => void updateFlag({ grepCaseSensitive: !deps.getGrepCaseSensitive() })
|
|
38952
|
+
),
|
|
38953
|
+
toggleButton(
|
|
38954
|
+
current.wholeWord,
|
|
38955
|
+
current.wholeWordTitle,
|
|
38956
|
+
deps.getGrepWholeWord(),
|
|
38957
|
+
() => void updateFlag({ grepWholeWord: !deps.getGrepWholeWord() })
|
|
38958
|
+
),
|
|
38959
|
+
toggleButton(
|
|
38960
|
+
current.excludeTests,
|
|
38961
|
+
current.excludeTestsTitle,
|
|
38962
|
+
deps.getGrepHideTests(),
|
|
38963
|
+
() => void updateFlag({ hideTests: !deps.getGrepHideTests() })
|
|
38964
|
+
)
|
|
38965
|
+
);
|
|
38966
|
+
}
|
|
38967
|
+
function mount() {
|
|
38968
|
+
const el2 = host();
|
|
38969
|
+
if (!el2 || mounted) return;
|
|
38970
|
+
mounted = true;
|
|
38971
|
+
el2.replaceChildren();
|
|
38972
|
+
const head = document.createElement("div");
|
|
38973
|
+
head.className = "search-results-head";
|
|
38974
|
+
input2 = document.createElement("input");
|
|
38975
|
+
input2.type = "search";
|
|
38976
|
+
input2.className = "search-results-input";
|
|
38977
|
+
input2.autocomplete = "off";
|
|
38978
|
+
input2.spellcheck = false;
|
|
38979
|
+
input2.addEventListener("keydown", (event) => {
|
|
38980
|
+
if (event.key === "Enter") {
|
|
38981
|
+
event.preventDefault();
|
|
38982
|
+
run(input2?.value ?? "");
|
|
38983
|
+
}
|
|
38984
|
+
});
|
|
38985
|
+
runButton = document.createElement("button");
|
|
38986
|
+
runButton.type = "button";
|
|
38987
|
+
runButton.className = "gdp-btn gdp-btn-sm search-results-run";
|
|
38988
|
+
runButton.addEventListener("click", () => run(input2?.value ?? ""));
|
|
38989
|
+
controls = document.createElement("div");
|
|
38990
|
+
controls.className = "gdp-palette-controls search-results-controls";
|
|
38991
|
+
status = document.createElement("div");
|
|
38992
|
+
status.className = "gdp-palette-status search-results-status";
|
|
38993
|
+
head.append(input2, runButton);
|
|
38994
|
+
list2 = document.createElement("div");
|
|
38995
|
+
list2.className = "gdp-palette-list search-results-list";
|
|
38996
|
+
list2.setAttribute("role", "listbox");
|
|
38997
|
+
el2.append(head, controls, status, list2);
|
|
38998
|
+
localize();
|
|
38999
|
+
}
|
|
39000
|
+
function renderResults() {
|
|
39001
|
+
if (!list2 || !status) return;
|
|
39002
|
+
list2.replaceChildren();
|
|
39003
|
+
if (!lastResponse) {
|
|
39004
|
+
status.textContent = text3().resultsIdle;
|
|
39005
|
+
return;
|
|
39006
|
+
}
|
|
39007
|
+
const { response, term } = lastResponse;
|
|
39008
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
39009
|
+
for (const match2 of response.matches) {
|
|
39010
|
+
const bucket = grouped.get(match2.path) ?? [];
|
|
39011
|
+
bucket.push(match2);
|
|
39012
|
+
grouped.set(match2.path, bucket);
|
|
39013
|
+
}
|
|
39014
|
+
for (const [path, matches2] of grouped) {
|
|
39015
|
+
const group = document.createElement("section");
|
|
39016
|
+
group.className = "gdp-palette-file-group";
|
|
39017
|
+
group.setAttribute("role", "group");
|
|
39018
|
+
group.setAttribute("aria-label", path);
|
|
39019
|
+
const heading2 = document.createElement("div");
|
|
39020
|
+
heading2.className = "gdp-palette-file-heading";
|
|
39021
|
+
const name = document.createElement("span");
|
|
39022
|
+
name.className = "gdp-palette-file-heading-name";
|
|
39023
|
+
name.textContent = path;
|
|
39024
|
+
name.title = path;
|
|
39025
|
+
const count = document.createElement("span");
|
|
39026
|
+
count.className = "gdp-palette-file-heading-count";
|
|
39027
|
+
count.textContent = String(matches2.length);
|
|
39028
|
+
heading2.append(name, count);
|
|
39029
|
+
const rows = document.createElement("div");
|
|
39030
|
+
rows.className = "gdp-palette-file-matches";
|
|
39031
|
+
for (const match2 of matches2) {
|
|
39032
|
+
const key = `${match2.path}\0${match2.line}`;
|
|
39033
|
+
const row = document.createElement("button");
|
|
39034
|
+
row.type = "button";
|
|
39035
|
+
row.className = "gdp-palette-row";
|
|
39036
|
+
row.setAttribute("role", "option");
|
|
39037
|
+
row.dataset.resultKey = key;
|
|
39038
|
+
row.setAttribute("aria-selected", String(key === activeKey));
|
|
39039
|
+
const title = document.createElement("span");
|
|
39040
|
+
title.className = "gdp-palette-row-title";
|
|
39041
|
+
title.textContent = text3().line(match2.line, match2.column);
|
|
39042
|
+
const detail = document.createElement("span");
|
|
39043
|
+
detail.className = "gdp-palette-row-detail";
|
|
39044
|
+
detail.textContent = match2.preview;
|
|
39045
|
+
row.append(title, detail);
|
|
39046
|
+
row.addEventListener("click", () => {
|
|
39047
|
+
setActiveRow(key);
|
|
39048
|
+
const hl = match2.matchText || (deps.getGrepRegex() ? "" : term);
|
|
39049
|
+
deps.openMatch({
|
|
39050
|
+
path: match2.path,
|
|
39051
|
+
line: match2.line,
|
|
39052
|
+
...hl ? { hl } : {}
|
|
39053
|
+
});
|
|
39054
|
+
});
|
|
39055
|
+
rows.appendChild(row);
|
|
39056
|
+
}
|
|
39057
|
+
group.append(heading2, rows);
|
|
39058
|
+
list2.appendChild(group);
|
|
39059
|
+
}
|
|
39060
|
+
}
|
|
39061
|
+
function setActiveRow(key) {
|
|
39062
|
+
activeKey = key;
|
|
39063
|
+
list2?.querySelectorAll(".gdp-palette-row").forEach((row) => {
|
|
39064
|
+
row.setAttribute("aria-selected", String(row.dataset.resultKey === key));
|
|
39065
|
+
});
|
|
39066
|
+
}
|
|
39067
|
+
function run(nextQuery) {
|
|
39068
|
+
if (!mounted) return;
|
|
39069
|
+
if (nextQuery !== void 0) query = nextQuery.trim();
|
|
39070
|
+
if (input2 && input2.value !== query) input2.value = query;
|
|
39071
|
+
controller?.abort();
|
|
39072
|
+
controller = null;
|
|
39073
|
+
const parsed = parseGrepQuery(query);
|
|
39074
|
+
deps.onQueryChange?.(query);
|
|
39075
|
+
if (!parsed.term) {
|
|
39076
|
+
lastResponse = null;
|
|
39077
|
+
renderResults();
|
|
39078
|
+
return;
|
|
39079
|
+
}
|
|
39080
|
+
const myGeneration = ++generation;
|
|
39081
|
+
if (status) status.textContent = text3().searching;
|
|
39082
|
+
const ref = deps.getRef();
|
|
39083
|
+
const regex = deps.getGrepRegex();
|
|
39084
|
+
const caseSensitive = deps.getGrepCaseSensitive();
|
|
39085
|
+
const wholeWord = deps.getGrepWholeWord();
|
|
39086
|
+
const hideTests = deps.getGrepHideTests();
|
|
39087
|
+
const params = buildGrepRequestParams({
|
|
39088
|
+
term: parsed.term,
|
|
39089
|
+
ref,
|
|
39090
|
+
regex,
|
|
39091
|
+
caseSensitive,
|
|
39092
|
+
wholeWord,
|
|
39093
|
+
hideTests,
|
|
39094
|
+
max: RESULTS_MAX
|
|
39095
|
+
});
|
|
39096
|
+
deps.appendScopeParams(params);
|
|
39097
|
+
for (const scope of parsed.paths) params.append("path", scope);
|
|
39098
|
+
const abort = new AbortController();
|
|
39099
|
+
controller = abort;
|
|
39100
|
+
void deps.trackLoad(
|
|
39101
|
+
fetch(`/_grep?${params.toString()}`, { signal: abort.signal }).then(
|
|
39102
|
+
async (r2) => {
|
|
39103
|
+
if (!r2.ok)
|
|
39104
|
+
throw new Error(
|
|
39105
|
+
`grep request failed (${r2.status}): ${await r2.text()}`
|
|
39106
|
+
);
|
|
39107
|
+
return r2.json();
|
|
39108
|
+
}
|
|
39109
|
+
)
|
|
39110
|
+
).then((response) => {
|
|
39111
|
+
if (myGeneration !== generation || abort.signal.aborted) return;
|
|
39112
|
+
const current = deps.getServerGeneration();
|
|
39113
|
+
if (response.generation !== void 0 && current > 0 && response.generation < current) {
|
|
39114
|
+
lastResponse = null;
|
|
39115
|
+
renderResults();
|
|
39116
|
+
if (status) status.textContent = text3().repositoryChanged;
|
|
39117
|
+
return;
|
|
39118
|
+
}
|
|
39119
|
+
lastResponse = { response, term: parsed.term };
|
|
39120
|
+
renderResults();
|
|
39121
|
+
if (status)
|
|
39122
|
+
status.textContent = `${text3().grepSummary({
|
|
39123
|
+
engine: response.engine,
|
|
39124
|
+
regex,
|
|
39125
|
+
caseSensitive,
|
|
39126
|
+
wholeWord,
|
|
39127
|
+
testsExcluded: hideTests,
|
|
39128
|
+
truncated: response.truncated,
|
|
39129
|
+
count: response.matches.length,
|
|
39130
|
+
paths: parsed.paths
|
|
39131
|
+
})} · ${text3().resultsScope(ref)}`;
|
|
39132
|
+
}).catch((err) => {
|
|
39133
|
+
if (deps.isAbortError(err) || abort.signal.aborted) return;
|
|
39134
|
+
if (myGeneration !== generation) return;
|
|
39135
|
+
console.error("Search results request failed", err);
|
|
39136
|
+
lastResponse = null;
|
|
39137
|
+
renderResults();
|
|
39138
|
+
if (status)
|
|
39139
|
+
status.textContent = text3().searchFailed(
|
|
39140
|
+
err instanceof Error && err.message ? err.message : text3().unknownError
|
|
39141
|
+
);
|
|
39142
|
+
});
|
|
39143
|
+
}
|
|
39144
|
+
function localize() {
|
|
39145
|
+
if (!mounted) return;
|
|
39146
|
+
const current = text3();
|
|
39147
|
+
if (input2) input2.placeholder = current.resultsPlaceholder;
|
|
39148
|
+
if (runButton) {
|
|
39149
|
+
runButton.textContent = current.resultsRun;
|
|
39150
|
+
runButton.title = current.resultsRun;
|
|
39151
|
+
}
|
|
39152
|
+
host()?.setAttribute("aria-label", current.resultsTitle);
|
|
39153
|
+
renderControls();
|
|
39154
|
+
renderResults();
|
|
39155
|
+
}
|
|
39156
|
+
function isOpen() {
|
|
39157
|
+
const el2 = host();
|
|
39158
|
+
return !!el2 && !el2.hidden;
|
|
39159
|
+
}
|
|
39160
|
+
return {
|
|
39161
|
+
open(nextQuery) {
|
|
39162
|
+
const el2 = host();
|
|
39163
|
+
if (!el2) return;
|
|
39164
|
+
mount();
|
|
39165
|
+
el2.hidden = false;
|
|
39166
|
+
el2.setAttribute("aria-hidden", "false");
|
|
39167
|
+
el2.removeAttribute("inert");
|
|
39168
|
+
document.body.classList.add("search-sheet-open");
|
|
39169
|
+
if (nextQuery !== void 0 && nextQuery !== query) run(nextQuery);
|
|
39170
|
+
else if (nextQuery === void 0 && !lastResponse && query) run(query);
|
|
39171
|
+
else renderResults();
|
|
39172
|
+
input2?.focus();
|
|
39173
|
+
},
|
|
39174
|
+
close() {
|
|
39175
|
+
const el2 = host();
|
|
39176
|
+
if (!el2) return;
|
|
39177
|
+
el2.hidden = true;
|
|
39178
|
+
el2.setAttribute("aria-hidden", "true");
|
|
39179
|
+
el2.setAttribute("inert", "");
|
|
39180
|
+
document.body.classList.remove("search-sheet-open");
|
|
39181
|
+
controller?.abort();
|
|
39182
|
+
controller = null;
|
|
39183
|
+
},
|
|
39184
|
+
isOpen,
|
|
39185
|
+
getQuery: () => query,
|
|
39186
|
+
localize
|
|
39187
|
+
};
|
|
39188
|
+
}
|
|
39189
|
+
|
|
37812
39190
|
// web-src/views/source-view.ts
|
|
37813
39191
|
function createSourceView(deps) {
|
|
37814
39192
|
const {
|
|
@@ -37827,6 +39205,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
37827
39205
|
placeSidebarToggle,
|
|
37828
39206
|
createFileBreadcrumb,
|
|
37829
39207
|
createRepositoryWebLink: createRepositoryWebLink2,
|
|
39208
|
+
createRevisionNav,
|
|
37830
39209
|
createFileDetailMeta,
|
|
37831
39210
|
createOpenPathButton,
|
|
37832
39211
|
createMoveToTrashButton,
|
|
@@ -38117,10 +39496,17 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
38117
39496
|
const cells = table2.querySelectorAll(
|
|
38118
39497
|
".gdp-source-line-code"
|
|
38119
39498
|
);
|
|
39499
|
+
const sourceLines = textValue.split("\n");
|
|
38120
39500
|
cells.forEach((cell, index) => {
|
|
38121
39501
|
if (highlightedLines[index] == null) return;
|
|
38122
39502
|
cell.innerHTML = highlightedLines[index] || " ";
|
|
38123
39503
|
cell.classList.add("shiki");
|
|
39504
|
+
markSourceHighlightTerm(
|
|
39505
|
+
cell,
|
|
39506
|
+
sourceLines[index] ?? "",
|
|
39507
|
+
index + 1,
|
|
39508
|
+
target
|
|
39509
|
+
);
|
|
38124
39510
|
});
|
|
38125
39511
|
}).catch((err) => {
|
|
38126
39512
|
console.error("Failed to apply source syntax highlighting", err);
|
|
@@ -38535,6 +39921,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
38535
39921
|
const code2 = document.createElement("td");
|
|
38536
39922
|
code2.className = "gdp-source-line-code";
|
|
38537
39923
|
code2.textContent = line || " ";
|
|
39924
|
+
markSourceHighlightTerm(code2, line, index + 1, target);
|
|
38538
39925
|
tr.appendChild(num);
|
|
38539
39926
|
tr.appendChild(code2);
|
|
38540
39927
|
tbody.appendChild(tr);
|
|
@@ -38680,6 +40067,45 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
38680
40067
|
const routeTarget = sourceTargetFromRoute();
|
|
38681
40068
|
return sourceTargetsEqual(routeTarget, target) && STATE.route.screen === "file" ? STATE.route.line : void 0;
|
|
38682
40069
|
}
|
|
40070
|
+
function currentSourceHighlightTerm(target) {
|
|
40071
|
+
const routeTarget = sourceTargetFromRoute();
|
|
40072
|
+
return sourceTargetsEqual(routeTarget, target) && STATE.route.screen === "file" && STATE.route.line && STATE.route.hl ? STATE.route.hl : void 0;
|
|
40073
|
+
}
|
|
40074
|
+
function syncSourceHighlightMarks(card, target) {
|
|
40075
|
+
card.querySelectorAll("mark.gdp-grep-match").forEach((mark) => {
|
|
40076
|
+
const parent = mark.parentElement;
|
|
40077
|
+
if (!parent) return;
|
|
40078
|
+
while (mark.firstChild) parent.insertBefore(mark.firstChild, mark);
|
|
40079
|
+
parent.removeChild(mark);
|
|
40080
|
+
parent.normalize();
|
|
40081
|
+
});
|
|
40082
|
+
const term = currentSourceHighlightTerm(target);
|
|
40083
|
+
const virtual = card.querySelector(
|
|
40084
|
+
".gdp-source-virtual"
|
|
40085
|
+
);
|
|
40086
|
+
if (virtual?.__gdpVirtualSourceSearch) {
|
|
40087
|
+
if (term) virtual.__gdpVirtualSourceSearch.open(term);
|
|
40088
|
+
return;
|
|
40089
|
+
}
|
|
40090
|
+
if (!term) return;
|
|
40091
|
+
card.querySelectorAll(".gdp-source-table tr[data-line]").forEach((row) => {
|
|
40092
|
+
const cell = row.querySelector(".gdp-source-line-code");
|
|
40093
|
+
if (!cell) return;
|
|
40094
|
+
markSourceHighlightTerm(
|
|
40095
|
+
cell,
|
|
40096
|
+
cell.textContent ?? "",
|
|
40097
|
+
Number(row.dataset.line || "0"),
|
|
40098
|
+
target
|
|
40099
|
+
);
|
|
40100
|
+
});
|
|
40101
|
+
}
|
|
40102
|
+
function markSourceHighlightTerm(cell, lineText, lineNumber, target) {
|
|
40103
|
+
const term = currentSourceHighlightTerm(target);
|
|
40104
|
+
if (!term) return;
|
|
40105
|
+
if (!lineInSourceTarget(lineNumber, currentSourceLineTarget(target)))
|
|
40106
|
+
return;
|
|
40107
|
+
markNeedleInCell(cell, lineText, term, "gdp-grep-match");
|
|
40108
|
+
}
|
|
38683
40109
|
function lineTargetStart(line) {
|
|
38684
40110
|
if (!line) return void 0;
|
|
38685
40111
|
return typeof line === "number" ? line : line.start;
|
|
@@ -38887,8 +40313,9 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
38887
40313
|
next.addEventListener("click", () => move(1));
|
|
38888
40314
|
close.addEventListener("click", hide);
|
|
38889
40315
|
return {
|
|
38890
|
-
open: () => {
|
|
40316
|
+
open: (query) => {
|
|
38891
40317
|
bar.hidden = false;
|
|
40318
|
+
if (query !== void 0) input2.value = query;
|
|
38892
40319
|
input2.focus();
|
|
38893
40320
|
input2.select();
|
|
38894
40321
|
sync();
|
|
@@ -39056,6 +40483,13 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39056
40483
|
render
|
|
39057
40484
|
);
|
|
39058
40485
|
wrap.__gdpVirtualSourceSearch = search;
|
|
40486
|
+
const initialHighlightTerm = currentSourceHighlightTerm(target);
|
|
40487
|
+
if (initialHighlightTerm) {
|
|
40488
|
+
const searchHandle = search;
|
|
40489
|
+
setTimeout(() => {
|
|
40490
|
+
if (wrap.isConnected) searchHandle.open(initialHighlightTerm);
|
|
40491
|
+
}, 0);
|
|
40492
|
+
}
|
|
39059
40493
|
let resizeObserver = null;
|
|
39060
40494
|
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
39061
40495
|
if (!scroller.isConnected) {
|
|
@@ -39309,6 +40743,13 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39309
40743
|
render
|
|
39310
40744
|
);
|
|
39311
40745
|
wrap.__gdpVirtualSourceSearch = search;
|
|
40746
|
+
const initialHighlightTerm = currentSourceHighlightTerm(target);
|
|
40747
|
+
if (initialHighlightTerm) {
|
|
40748
|
+
const searchHandle = search;
|
|
40749
|
+
setTimeout(() => {
|
|
40750
|
+
if (wrap.isConnected) searchHandle.open(initialHighlightTerm);
|
|
40751
|
+
}, 0);
|
|
40752
|
+
}
|
|
39312
40753
|
let resizeObserver = null;
|
|
39313
40754
|
resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
|
|
39314
40755
|
if (!scroller.isConnected) {
|
|
@@ -39458,6 +40899,8 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39458
40899
|
const mounted = mountedStandaloneSourceCard(target);
|
|
39459
40900
|
const state = mounted?.dataset.sourceState;
|
|
39460
40901
|
if (mounted && state === "done") {
|
|
40902
|
+
syncRenderedSourceLineHighlights(mounted, target);
|
|
40903
|
+
syncSourceHighlightMarks(mounted, target);
|
|
39461
40904
|
scrollStandaloneSourceLine(
|
|
39462
40905
|
mounted,
|
|
39463
40906
|
lineTargetStart(
|
|
@@ -39493,7 +40936,8 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
39493
40936
|
setRoute,
|
|
39494
40937
|
setPreferredSourceTab,
|
|
39495
40938
|
createFileBreadcrumb,
|
|
39496
|
-
createRepositoryWebLink: createRepositoryWebLink2
|
|
40939
|
+
createRepositoryWebLink: createRepositoryWebLink2,
|
|
40940
|
+
createRevisionNav
|
|
39497
40941
|
},
|
|
39498
40942
|
target,
|
|
39499
40943
|
activeTab,
|
|
@@ -45176,6 +46620,7 @@ ${t2.files.overlapTitle(others)}`;
|
|
|
45176
46620
|
if (name) name.textContent = branch;
|
|
45177
46621
|
el2.title = branch ? `Current branch: ${branch}` : "";
|
|
45178
46622
|
}
|
|
46623
|
+
const MAX_RECENT_REFS = 8;
|
|
45179
46624
|
function mergeLocalSettings(patch) {
|
|
45180
46625
|
const next = { ...APP_SETTINGS };
|
|
45181
46626
|
for (const [key, value] of Object.entries(patch)) {
|
|
@@ -45742,7 +47187,20 @@ ${error2.stack}` : ""}`
|
|
|
45742
47187
|
copyReferenceLabel: () => uiText().global.copyLineReference,
|
|
45743
47188
|
lineCountLabel: (count) => uiText().global.selectedLineCount(count),
|
|
45744
47189
|
githubOpenTitle: () => uiText().global.githubSelectionOpen,
|
|
45745
|
-
githubCopyTitle: () => uiText().global.githubSelectionCopy
|
|
47190
|
+
githubCopyTitle: () => uiText().global.githubSelectionCopy,
|
|
47191
|
+
lineHistoryTitle: () => uiText().global.lineHistory,
|
|
47192
|
+
openLineHistory: (path, start, end) => {
|
|
47193
|
+
const route = STATE.route;
|
|
47194
|
+
const ref = route.screen === "file" ? route.commit || route.ref || "worktree" : route.screen === "diff" && route.range.to ? route.range.to : "worktree";
|
|
47195
|
+
navigateToRoute({
|
|
47196
|
+
screen: "file",
|
|
47197
|
+
path,
|
|
47198
|
+
ref,
|
|
47199
|
+
view: "history",
|
|
47200
|
+
lines: { start, end },
|
|
47201
|
+
range: currentRange()
|
|
47202
|
+
});
|
|
47203
|
+
}
|
|
45746
47204
|
});
|
|
45747
47205
|
const DIFF_LINE_SELECT = createDiffLineSelect({ pill: LINE_REF_PILL });
|
|
45748
47206
|
function clearRenderedSourceLineTargets() {
|
|
@@ -45812,6 +47270,7 @@ ${error2.stack}` : ""}`
|
|
|
45812
47270
|
REPO_SIDEBAR_REF = ref;
|
|
45813
47271
|
},
|
|
45814
47272
|
isTestPath: isTestFilePath,
|
|
47273
|
+
filterCountTitle: (visible, total) => uiText().sidebar.filterCountTitle(visible, total),
|
|
45815
47274
|
sidebarToggleTitle: (hidden) => hidden ? uiText().sidebar.show : uiText().sidebar.hide,
|
|
45816
47275
|
openDirectoryInOsTitle: () => uiText().sidebar.openDirectoryInOs,
|
|
45817
47276
|
omittedDirectoryBadge: (reason) => {
|
|
@@ -45890,6 +47349,7 @@ ${error2.stack}` : ""}`
|
|
|
45890
47349
|
placeSidebarToggle,
|
|
45891
47350
|
createFileBreadcrumb: (path, ref) => DIFF_VIEW.createFileBreadcrumb(path, ref),
|
|
45892
47351
|
createRepositoryWebLink: createFileRepositoryWebLink,
|
|
47352
|
+
createRevisionNav: createFileRevisionNav,
|
|
45893
47353
|
createFileDetailMeta: (target, meta) => REPO_VIEW.createFileDetailMeta(target, meta),
|
|
45894
47354
|
createOpenPathButton,
|
|
45895
47355
|
createMoveToTrashButton: (path, onDeleted) => REPO_VIEW.createMoveToTrashButton(path, onDeleted),
|
|
@@ -45933,6 +47393,7 @@ ${error2.stack}` : ""}`
|
|
|
45933
47393
|
setPreferredSourceTab: (tab) => SOURCE_VIEW.setPreferredSourceTab(tab),
|
|
45934
47394
|
createFileBreadcrumb: (path, ref) => DIFF_VIEW.createFileBreadcrumb(path, ref),
|
|
45935
47395
|
createRepositoryWebLink: createFileRepositoryWebLink,
|
|
47396
|
+
createRevisionNav: createFileRevisionNav,
|
|
45936
47397
|
removeStandaloneSource,
|
|
45937
47398
|
placeSidebarToggle,
|
|
45938
47399
|
escapeHtml: escapeHtml3,
|
|
@@ -46016,6 +47477,17 @@ ${error2.stack}` : ""}`
|
|
|
46016
47477
|
}),
|
|
46017
47478
|
openGithubLabel: () => uiText().repo.openGithub,
|
|
46018
47479
|
openRepositoryWebLabel: () => uiText().repo.openRepositoryWeb,
|
|
47480
|
+
folderHistoryLabel: () => uiText().repo.folderHistory,
|
|
47481
|
+
folderHistoryTitle: () => uiText().repo.folderHistoryTitle,
|
|
47482
|
+
openFolderHistory: (ref, path) => {
|
|
47483
|
+
const dir = path.replace(/\/+$/, "");
|
|
47484
|
+
navigateToRoute({
|
|
47485
|
+
screen: "history",
|
|
47486
|
+
ref: ref && ref !== "worktree" ? ref : "HEAD",
|
|
47487
|
+
...dir ? { path: `${dir}/` } : {},
|
|
47488
|
+
range: currentRange()
|
|
47489
|
+
});
|
|
47490
|
+
},
|
|
46019
47491
|
fileBadge: (status) => DIFF_VIEW.fileBadge(status)
|
|
46020
47492
|
});
|
|
46021
47493
|
const {
|
|
@@ -46049,6 +47521,8 @@ ${error2.stack}` : ""}`
|
|
|
46049
47521
|
getFileSelectionHistory: () => APP_SETTINGS.fileSelectionHistory || [],
|
|
46050
47522
|
getGrepSelectionHistory: () => APP_SETTINGS.grepSelectionHistory || [],
|
|
46051
47523
|
getGrepRegex: () => APP_SETTINGS.grepRegex === true,
|
|
47524
|
+
getGrepCaseSensitive: () => APP_SETTINGS.grepCaseSensitive === true,
|
|
47525
|
+
getGrepWholeWord: () => APP_SETTINGS.grepWholeWord === true,
|
|
46052
47526
|
getGrepHideTests: () => STATE.hideTests,
|
|
46053
47527
|
getGrepGroupByFile: () => APP_SETTINGS.grepGroupByFile === true,
|
|
46054
47528
|
getGrepPaletteWidth: () => APP_SETTINGS.grepPaletteWidth,
|
|
@@ -46057,7 +47531,8 @@ ${error2.stack}` : ""}`
|
|
|
46057
47531
|
applyGrepHideTests: (hidden) => {
|
|
46058
47532
|
STATE.hideTests = hidden;
|
|
46059
47533
|
applyHideTests();
|
|
46060
|
-
}
|
|
47534
|
+
},
|
|
47535
|
+
openSearchResults: (query) => openSearchSheet(query)
|
|
46061
47536
|
});
|
|
46062
47537
|
const { openSearchPalette, isPaletteOpen, paletteMode, clearRepoFileCache } = SEARCH_PALETTE;
|
|
46063
47538
|
const UI_TEXT = {
|
|
@@ -46087,6 +47562,11 @@ ${error2.stack}` : ""}`
|
|
|
46087
47562
|
queryHistory: "query history",
|
|
46088
47563
|
settings: "viewer settings",
|
|
46089
47564
|
theme: "toggle theme",
|
|
47565
|
+
search: "Search files (Ctrl+K) · Shift+click: grep (Ctrl+G)",
|
|
47566
|
+
lineHistory: "Line history",
|
|
47567
|
+
recentRef: "Recently used ref",
|
|
47568
|
+
olderRevision: "Older revision of this file",
|
|
47569
|
+
newerRevision: "Newer revision of this file",
|
|
46090
47570
|
copyAiContext: "Copy AI context (Shift+Click to include code)",
|
|
46091
47571
|
copyAiContextCopied: "Copied AI context",
|
|
46092
47572
|
copyAiContextCopiedWithCode: (lines) => `Copied AI context + code (${lines} line${lines === 1 ? "" : "s"})`,
|
|
@@ -46175,7 +47655,8 @@ ${error2.stack}` : ""}`
|
|
|
46175
47655
|
treeTitle: "tree view",
|
|
46176
47656
|
flatTitle: "flat list",
|
|
46177
47657
|
filter: "Filter files… / ⌘K",
|
|
46178
|
-
filterTitle: "Filter files.
|
|
47658
|
+
filterTitle: "Filter files. Plain text matches anywhere in the path; /pattern/ is a regex, ~text is a fuzzy match, *.ts or src/** is a glob. Press / to focus this field, Cmd/Ctrl+K for the full-file palette, Ctrl+G for grep, ? for help.",
|
|
47659
|
+
filterCountTitle: (visible, total) => `${visible} of ${total} files match the filter`,
|
|
46179
47660
|
filterClear: "Clear",
|
|
46180
47661
|
filterClearTitle: "Clear file filter",
|
|
46181
47662
|
hide: "hide sidebar",
|
|
@@ -46211,7 +47692,9 @@ ${error2.stack}` : ""}`
|
|
|
46211
47692
|
submoduleLabel: "submodule",
|
|
46212
47693
|
submoduleTitle: "Git submodule pinned to a commit",
|
|
46213
47694
|
openGithub: "Open on GitHub",
|
|
46214
|
-
openRepositoryWeb: "Open repository web page"
|
|
47695
|
+
openRepositoryWeb: "Open repository web page",
|
|
47696
|
+
folderHistory: "History",
|
|
47697
|
+
folderHistoryTitle: "Commits that touched this folder"
|
|
46215
47698
|
},
|
|
46216
47699
|
history: {
|
|
46217
47700
|
title: "Commits",
|
|
@@ -46423,6 +47906,11 @@ ${error2.stack}` : ""}`
|
|
|
46423
47906
|
queryHistory: "クエリ履歴",
|
|
46424
47907
|
settings: "ビューア設定",
|
|
46425
47908
|
theme: "テーマ切り替え",
|
|
47909
|
+
search: "ファイルを検索 (Ctrl+K)・Shift+クリックで grep (Ctrl+G)",
|
|
47910
|
+
lineHistory: "この行の履歴",
|
|
47911
|
+
recentRef: "最近使った ref",
|
|
47912
|
+
olderRevision: "このファイルの 1 つ前のリビジョン",
|
|
47913
|
+
newerRevision: "このファイルの 1 つ後のリビジョン",
|
|
46426
47914
|
copyAiContext: "AI 用コンテキストをコピー(Shift+Click でコードも添付)",
|
|
46427
47915
|
copyAiContextCopied: "コピーしました",
|
|
46428
47916
|
copyAiContextCopiedWithCode: (lines) => `コピーしました(コード付き・${lines}行)`,
|
|
@@ -46511,7 +47999,8 @@ ${error2.stack}` : ""}`
|
|
|
46511
47999
|
treeTitle: "ツリー表示",
|
|
46512
48000
|
flatTitle: "一覧表示",
|
|
46513
48001
|
filter: "ファイル絞り込み… / ⌘K",
|
|
46514
|
-
filterTitle: "
|
|
48002
|
+
filterTitle: "ファイルを絞り込みます。文字列はパスの部分一致、/pattern/ は正規表現、~text はあいまい一致、*.ts や src/** は glob。/ でこの欄にフォーカス、Cmd/Ctrl+K で全ファイルパレット、Ctrl+G で grep、? でヘルプ。",
|
|
48003
|
+
filterCountTitle: (visible, total) => `${total} ファイル中 ${visible} 件が一致`,
|
|
46515
48004
|
filterClear: "解除",
|
|
46516
48005
|
filterClearTitle: "ファイル絞り込みを解除",
|
|
46517
48006
|
hide: "サイドバーを隠す",
|
|
@@ -46547,7 +48036,9 @@ ${error2.stack}` : ""}`
|
|
|
46547
48036
|
submoduleLabel: "サブモジュール",
|
|
46548
48037
|
submoduleTitle: "Git サブモジュール: 特定のコミットに固定されています。直接は開けません。",
|
|
46549
48038
|
openGithub: "GitHubで開く",
|
|
46550
|
-
openRepositoryWeb: "リポジトリのウェブページを開く"
|
|
48039
|
+
openRepositoryWeb: "リポジトリのウェブページを開く",
|
|
48040
|
+
folderHistory: "履歴",
|
|
48041
|
+
folderHistoryTitle: "このフォルダを変更したコミット"
|
|
46551
48042
|
},
|
|
46552
48043
|
history: {
|
|
46553
48044
|
title: "コミット",
|
|
@@ -46775,6 +48266,11 @@ ${error2.stack}` : ""}`
|
|
|
46775
48266
|
quickHelpBtn.title = text3.quickHelp.buttonTitle;
|
|
46776
48267
|
quickHelpBtn.setAttribute("aria-label", text3.quickHelp.buttonTitle);
|
|
46777
48268
|
}
|
|
48269
|
+
const searchBtn = document.querySelector("#search-btn");
|
|
48270
|
+
if (searchBtn) {
|
|
48271
|
+
searchBtn.title = text3.global.search;
|
|
48272
|
+
searchBtn.setAttribute("aria-label", text3.global.search);
|
|
48273
|
+
}
|
|
46778
48274
|
QUICK_HELP?.localize();
|
|
46779
48275
|
const doctorTitle = doctorText(STATE.language).title;
|
|
46780
48276
|
const doctorBtn = document.querySelector("#doctor-btn");
|
|
@@ -46801,6 +48297,13 @@ ${error2.stack}` : ""}`
|
|
|
46801
48297
|
}
|
|
46802
48298
|
document.querySelector("#terminal-sheet")?.setAttribute("aria-label", terminalChrome.title);
|
|
46803
48299
|
relocalizeTerminal?.();
|
|
48300
|
+
const searchChrome = searchPaletteText(STATE.language);
|
|
48301
|
+
const searchTabBtn = document.querySelector("#panel-tab-search");
|
|
48302
|
+
if (searchTabBtn) {
|
|
48303
|
+
searchTabBtn.textContent = searchChrome.resultsTitle;
|
|
48304
|
+
searchTabBtn.title = searchChrome.resultsOpen;
|
|
48305
|
+
}
|
|
48306
|
+
relocalizeSearchResults?.();
|
|
46804
48307
|
document.querySelector(".app-panel-tabs")?.setAttribute("aria-label", text3.appPanel.tabs);
|
|
46805
48308
|
const panelLayout = document.querySelector(
|
|
46806
48309
|
".app-panel-layout-switch"
|
|
@@ -46963,6 +48466,7 @@ ${error2.stack}` : ""}`
|
|
|
46963
48466
|
let relocalizeTools = null;
|
|
46964
48467
|
let relocalizeViewerSettings = null;
|
|
46965
48468
|
let relocalizeTerminal = null;
|
|
48469
|
+
let relocalizeSearchResults = null;
|
|
46966
48470
|
let relocalizeDatabase = null;
|
|
46967
48471
|
let QUICK_HELP = null;
|
|
46968
48472
|
function setViewerLanguage(language, persist = true) {
|
|
@@ -47313,6 +48817,18 @@ ${error2.stack}` : ""}`
|
|
|
47313
48817
|
function isHistoryPanelRoute(route) {
|
|
47314
48818
|
return route.screen === "history" || isFileHistoryRoute(route);
|
|
47315
48819
|
}
|
|
48820
|
+
function historyRefForCurrentView() {
|
|
48821
|
+
const route = STATE.route;
|
|
48822
|
+
if (route.screen === "history") return route.ref || "HEAD";
|
|
48823
|
+
if (route.screen === "repo" || route.screen === "file") {
|
|
48824
|
+
return route.ref && route.ref !== "worktree" ? route.ref : "HEAD";
|
|
48825
|
+
}
|
|
48826
|
+
if (route.screen === "diff") {
|
|
48827
|
+
const to = route.range.to;
|
|
48828
|
+
return to && to !== "worktree" ? to : "HEAD";
|
|
48829
|
+
}
|
|
48830
|
+
return "HEAD";
|
|
48831
|
+
}
|
|
47316
48832
|
function normalizeInternalFileRoute(route) {
|
|
47317
48833
|
if (route.screen !== "file") return route;
|
|
47318
48834
|
if (sourceInternalPathKind(route.path) === null) return route;
|
|
@@ -47361,6 +48877,7 @@ ${error2.stack}` : ""}`
|
|
|
47361
48877
|
setPreferredSourceTab: (tab) => SOURCE_VIEW.setPreferredSourceTab(tab),
|
|
47362
48878
|
createFileBreadcrumb: (path, ref) => DIFF_VIEW.createFileBreadcrumb(path, ref),
|
|
47363
48879
|
createRepositoryWebLink: createFileRepositoryWebLink,
|
|
48880
|
+
createRevisionNav: createFileRevisionNav,
|
|
47364
48881
|
emptyText: () => uiText().diff
|
|
47365
48882
|
},
|
|
47366
48883
|
historyRoute,
|
|
@@ -47380,15 +48897,21 @@ ${error2.stack}` : ""}`
|
|
|
47380
48897
|
return ANNOTATIONS_UI ? ANNOTATIONS_UI.withSessionParam(rawUrl) : rawUrl;
|
|
47381
48898
|
}
|
|
47382
48899
|
function withOverlayState(url) {
|
|
47383
|
-
return
|
|
47384
|
-
|
|
47385
|
-
|
|
47386
|
-
|
|
47387
|
-
|
|
48900
|
+
return withSearchResultsOverlay(
|
|
48901
|
+
withTerminalOverlay(
|
|
48902
|
+
withToolsOverlay(
|
|
48903
|
+
withDoctorOverlay(
|
|
48904
|
+
url,
|
|
48905
|
+
parseDoctorOverlay(
|
|
48906
|
+
window.location.pathname,
|
|
48907
|
+
window.location.search
|
|
48908
|
+
)
|
|
48909
|
+
),
|
|
48910
|
+
parseToolsOverlay(window.location.search)
|
|
47388
48911
|
),
|
|
47389
|
-
|
|
48912
|
+
parseTerminalOverlay(window.location.search)
|
|
47390
48913
|
),
|
|
47391
|
-
|
|
48914
|
+
parseSearchResultsOverlay(window.location.search)
|
|
47392
48915
|
);
|
|
47393
48916
|
}
|
|
47394
48917
|
function urlForRoute(route) {
|
|
@@ -47779,6 +49302,61 @@ ${error2.stack}` : ""}`
|
|
|
47779
49302
|
});
|
|
47780
49303
|
return button;
|
|
47781
49304
|
}
|
|
49305
|
+
function createFileRevisionNav(target, activeTab) {
|
|
49306
|
+
if (activeTab === "history") return null;
|
|
49307
|
+
const nav = document.createElement("span");
|
|
49308
|
+
nav.className = "gdp-file-revision-nav";
|
|
49309
|
+
const text3 = uiText().global;
|
|
49310
|
+
const make = (title, paths) => {
|
|
49311
|
+
const button = document.createElement("button");
|
|
49312
|
+
button.type = "button";
|
|
49313
|
+
button.className = "gdp-file-header-icon gdp-file-revision-btn";
|
|
49314
|
+
button.title = title;
|
|
49315
|
+
button.setAttribute("aria-label", title);
|
|
49316
|
+
button.disabled = true;
|
|
49317
|
+
button.innerHTML = iconSvg("octicon-revision", paths);
|
|
49318
|
+
return button;
|
|
49319
|
+
};
|
|
49320
|
+
const older = make(text3.olderRevision, PREVIOUS_16_PATHS);
|
|
49321
|
+
const newer = make(text3.newerRevision, NEXT_16_PATHS);
|
|
49322
|
+
nav.append(older, newer);
|
|
49323
|
+
const goTo = (sha) => {
|
|
49324
|
+
const view = STATE.route.screen === "file" && STATE.route.view === "blame" ? "blame" : "blob";
|
|
49325
|
+
navigateToRoute({
|
|
49326
|
+
screen: "file",
|
|
49327
|
+
path: target.path,
|
|
49328
|
+
ref: sha,
|
|
49329
|
+
view,
|
|
49330
|
+
range: currentRange()
|
|
49331
|
+
});
|
|
49332
|
+
};
|
|
49333
|
+
const params = new URLSearchParams({
|
|
49334
|
+
path: target.path,
|
|
49335
|
+
ref: target.ref || "worktree"
|
|
49336
|
+
});
|
|
49337
|
+
void trackLoad(
|
|
49338
|
+
fetch(`/_file_revisions?${params.toString()}`).then(async (r2) => {
|
|
49339
|
+
if (!r2.ok) throw new Error(await r2.text());
|
|
49340
|
+
return r2.json();
|
|
49341
|
+
})
|
|
49342
|
+
).then((neighbors) => {
|
|
49343
|
+
if (!nav.isConnected) return;
|
|
49344
|
+
if (neighbors.previous) {
|
|
49345
|
+
const sha = neighbors.previous;
|
|
49346
|
+
older.disabled = false;
|
|
49347
|
+
older.addEventListener("click", () => goTo(sha));
|
|
49348
|
+
}
|
|
49349
|
+
if (neighbors.next) {
|
|
49350
|
+
const sha = neighbors.next;
|
|
49351
|
+
newer.disabled = false;
|
|
49352
|
+
newer.addEventListener("click", () => goTo(sha));
|
|
49353
|
+
}
|
|
49354
|
+
}).catch((err) => {
|
|
49355
|
+
if (isAbortError3(err)) return;
|
|
49356
|
+
console.error("Failed to load file revision neighbours", err);
|
|
49357
|
+
});
|
|
49358
|
+
return nav;
|
|
49359
|
+
}
|
|
47782
49360
|
function createFileRepositoryWebLink(target) {
|
|
47783
49361
|
const webTarget = buildRepositoryWebTarget(REPO_WEB_URL, {
|
|
47784
49362
|
ref: target.ref,
|
|
@@ -47980,6 +49558,12 @@ ${error2.stack}` : ""}`
|
|
|
47980
49558
|
if (quickHelpIcon) {
|
|
47981
49559
|
quickHelpIcon.innerHTML = iconSvg("octicon-question", QUESTION_16_PATH);
|
|
47982
49560
|
}
|
|
49561
|
+
const searchIcon = document.querySelector(
|
|
49562
|
+
"#search-btn .goi-icon"
|
|
49563
|
+
);
|
|
49564
|
+
if (searchIcon) {
|
|
49565
|
+
searchIcon.innerHTML = iconSvg("octicon-search", SEARCH_16_PATH);
|
|
49566
|
+
}
|
|
47983
49567
|
const branchIcon = document.querySelector(
|
|
47984
49568
|
"#project-branch .goi-icon"
|
|
47985
49569
|
);
|
|
@@ -48034,6 +49618,10 @@ ${error2.stack}` : ""}`
|
|
|
48034
49618
|
event.preventDefault();
|
|
48035
49619
|
openTerminalSheet();
|
|
48036
49620
|
});
|
|
49621
|
+
$("#panel-tab-search")?.addEventListener("click", (event) => {
|
|
49622
|
+
event.preventDefault();
|
|
49623
|
+
openSearchSheet();
|
|
49624
|
+
});
|
|
48037
49625
|
$("#app-panel-close")?.addEventListener("click", (event) => {
|
|
48038
49626
|
event.preventDefault();
|
|
48039
49627
|
closeAppPanel();
|
|
@@ -48269,6 +49857,9 @@ ${error2.stack}` : ""}`
|
|
|
48269
49857
|
syncSidebarFilterClearButton();
|
|
48270
49858
|
sbFilterClear.addEventListener("click", clearSidebarFilter);
|
|
48271
49859
|
}
|
|
49860
|
+
document.querySelector("#search-btn")?.addEventListener("click", (event) => {
|
|
49861
|
+
openSearchPalette(event.shiftKey ? "grep" : "file");
|
|
49862
|
+
});
|
|
48272
49863
|
function focusFileFilter() {
|
|
48273
49864
|
const input2 = $("#sb-filter");
|
|
48274
49865
|
input2.focus();
|
|
@@ -48459,11 +50050,18 @@ ${error2.stack}` : ""}`
|
|
|
48459
50050
|
if (action === "goto-history") {
|
|
48460
50051
|
navigateToRoute({
|
|
48461
50052
|
screen: "history",
|
|
48462
|
-
ref:
|
|
50053
|
+
ref: historyRefForCurrentView(),
|
|
48463
50054
|
range: currentRange()
|
|
48464
50055
|
});
|
|
48465
50056
|
return true;
|
|
48466
50057
|
}
|
|
50058
|
+
if (action === "history-next-commit" || action === "history-previous-commit") {
|
|
50059
|
+
if (!isHistoryPanelRoute(STATE.route)) return false;
|
|
50060
|
+
void HISTORY_VIEW.moveCommitSelection(
|
|
50061
|
+
action === "history-next-commit" ? 1 : -1
|
|
50062
|
+
);
|
|
50063
|
+
return true;
|
|
50064
|
+
}
|
|
48467
50065
|
if (action === "goto-repo") {
|
|
48468
50066
|
navigateToRoute({
|
|
48469
50067
|
screen: "repo",
|
|
@@ -48781,6 +50379,7 @@ ${error2.stack}` : ""}`
|
|
|
48781
50379
|
syncDoctorSheetFromUrl();
|
|
48782
50380
|
syncToolsSheetFromUrl();
|
|
48783
50381
|
syncTerminalSheetFromUrl();
|
|
50382
|
+
syncSearchSheetFromUrl();
|
|
48784
50383
|
});
|
|
48785
50384
|
function syncRefInputs() {
|
|
48786
50385
|
const fi = $("#ref-from"), ti = $("#ref-to");
|
|
@@ -48872,7 +50471,19 @@ ${error2.stack}` : ""}`
|
|
|
48872
50471
|
},
|
|
48873
50472
|
getSyntaxHighlight: () => STATE.syntaxHighlight,
|
|
48874
50473
|
getLanguage: () => STATE.language,
|
|
48875
|
-
trackLoad
|
|
50474
|
+
trackLoad,
|
|
50475
|
+
commitWebLink: (sha) => {
|
|
50476
|
+
const target = buildRepositoryWebTarget(REPO_WEB_URL, {
|
|
50477
|
+
ref: sha,
|
|
50478
|
+
kind: "commit"
|
|
50479
|
+
});
|
|
50480
|
+
if (!target) return null;
|
|
50481
|
+
return createRepositoryWebLink(
|
|
50482
|
+
target,
|
|
50483
|
+
target.provider === "github" ? uiText().repo.openGithub : uiText().repo.openRepositoryWeb
|
|
50484
|
+
);
|
|
50485
|
+
},
|
|
50486
|
+
copyText: (text3) => navigator.clipboard.writeText(text3)
|
|
48876
50487
|
});
|
|
48877
50488
|
relocalizeHistory = () => HISTORY_VIEW.localize();
|
|
48878
50489
|
function helpSectionDeps() {
|
|
@@ -48973,14 +50584,16 @@ ${error2.stack}` : ""}`
|
|
|
48973
50584
|
function syncAppPanel() {
|
|
48974
50585
|
const tools = parseToolsOverlay(window.location.search) !== null;
|
|
48975
50586
|
const terminal = parseTerminalOverlay(window.location.search) !== null;
|
|
48976
|
-
const
|
|
50587
|
+
const search = parseSearchResultsOverlay(window.location.search) !== null;
|
|
50588
|
+
const open2 = tools || terminal || search;
|
|
48977
50589
|
const panel = document.getElementById("app-panel");
|
|
48978
50590
|
if (panel) {
|
|
48979
50591
|
panel.classList.toggle("app-panel-open", open2);
|
|
48980
50592
|
}
|
|
48981
50593
|
for (const [id, selected] of [
|
|
48982
50594
|
["#panel-tab-tools", tools],
|
|
48983
|
-
["#panel-tab-terminal", terminal]
|
|
50595
|
+
["#panel-tab-terminal", terminal],
|
|
50596
|
+
["#panel-tab-search", search]
|
|
48984
50597
|
]) {
|
|
48985
50598
|
const tab = document.querySelector(id);
|
|
48986
50599
|
if (!tab) continue;
|
|
@@ -49005,6 +50618,8 @@ ${error2.stack}` : ""}`
|
|
|
49005
50618
|
syncAppPanelLayout();
|
|
49006
50619
|
}
|
|
49007
50620
|
function closeAppPanel() {
|
|
50621
|
+
if (parseSearchResultsOverlay(window.location.search) !== null || SEARCH_RESULTS_VIEW.isOpen())
|
|
50622
|
+
closeSearchSheet();
|
|
49008
50623
|
if (parseToolsOverlay(window.location.search) !== null || TOOLS_VIEW.isOpen())
|
|
49009
50624
|
closeToolsSheet();
|
|
49010
50625
|
if (parseTerminalOverlay(window.location.search) !== null || TERMINAL_VIEW.isOpen())
|
|
@@ -49053,6 +50668,8 @@ ${error2.stack}` : ""}`
|
|
|
49053
50668
|
function openToolsSheet(tool) {
|
|
49054
50669
|
if (parseTerminalOverlay(window.location.search) !== null || TERMINAL_VIEW.isOpen())
|
|
49055
50670
|
closeTerminalSheet();
|
|
50671
|
+
if (parseSearchResultsOverlay(window.location.search) !== null || SEARCH_RESULTS_VIEW.isOpen())
|
|
50672
|
+
closeSearchSheet();
|
|
49056
50673
|
updateUrlForToolsOverlay(tool ?? TOOLS_VIEW.getActiveTool());
|
|
49057
50674
|
void TOOLS_VIEW.open(tool);
|
|
49058
50675
|
syncAppPanel();
|
|
@@ -49070,6 +50687,77 @@ ${error2.stack}` : ""}`
|
|
|
49070
50687
|
else if (!tool && open2) TOOLS_VIEW.close();
|
|
49071
50688
|
syncAppPanel();
|
|
49072
50689
|
}
|
|
50690
|
+
const SEARCH_RESULTS_VIEW = createSearchResultsView({
|
|
50691
|
+
$: (sel) => document.querySelector(sel),
|
|
50692
|
+
trackLoad,
|
|
50693
|
+
getLanguage: () => STATE.language,
|
|
50694
|
+
appendScopeParams,
|
|
50695
|
+
getRef: () => {
|
|
50696
|
+
const route = STATE.route;
|
|
50697
|
+
if (route.screen === "repo" || route.screen === "file")
|
|
50698
|
+
return route.ref || "worktree";
|
|
50699
|
+
return STATE.repoRef || "worktree";
|
|
50700
|
+
},
|
|
50701
|
+
getServerGeneration: () => SERVER_GENERATION,
|
|
50702
|
+
isAbortError: isAbortError3,
|
|
50703
|
+
getGrepRegex: () => APP_SETTINGS.grepRegex === true,
|
|
50704
|
+
getGrepCaseSensitive: () => APP_SETTINGS.grepCaseSensitive === true,
|
|
50705
|
+
getGrepWholeWord: () => APP_SETTINGS.grepWholeWord === true,
|
|
50706
|
+
getGrepHideTests: () => STATE.hideTests,
|
|
50707
|
+
persistGrepSettings: async (patch) => {
|
|
50708
|
+
await persistSettingsPatch(patch);
|
|
50709
|
+
if (patch.hideTests !== void 0) {
|
|
50710
|
+
STATE.hideTests = patch.hideTests;
|
|
50711
|
+
applyHideTests();
|
|
50712
|
+
}
|
|
50713
|
+
},
|
|
50714
|
+
openMatch: ({ path, line, hl }) => {
|
|
50715
|
+
const route = STATE.route;
|
|
50716
|
+
const ref = route.screen === "repo" || route.screen === "file" ? route.ref || "worktree" : STATE.repoRef || "worktree";
|
|
50717
|
+
setRoute({
|
|
50718
|
+
screen: "file",
|
|
50719
|
+
path,
|
|
50720
|
+
ref,
|
|
50721
|
+
view: "blob",
|
|
50722
|
+
line,
|
|
50723
|
+
...hl ? { hl } : {},
|
|
50724
|
+
range: currentRange()
|
|
50725
|
+
});
|
|
50726
|
+
void renderStandaloneSource({ path, ref });
|
|
50727
|
+
},
|
|
50728
|
+
onQueryChange: (query) => updateUrlForSearchResultsOverlay(query)
|
|
50729
|
+
});
|
|
50730
|
+
relocalizeSearchResults = () => SEARCH_RESULTS_VIEW.localize();
|
|
50731
|
+
function updateUrlForSearchResultsOverlay(query) {
|
|
50732
|
+
const current = window.location.pathname + window.location.search;
|
|
50733
|
+
const next = withSearchResultsOverlay(current, query);
|
|
50734
|
+
if (next !== current) {
|
|
50735
|
+
history.replaceState(history.state, "", next + window.location.hash);
|
|
50736
|
+
}
|
|
50737
|
+
syncHeaderMenu();
|
|
50738
|
+
}
|
|
50739
|
+
function openSearchSheet(query) {
|
|
50740
|
+
if (parseTerminalOverlay(window.location.search) !== null || TERMINAL_VIEW.isOpen())
|
|
50741
|
+
closeTerminalSheet();
|
|
50742
|
+
if (parseToolsOverlay(window.location.search) !== null || TOOLS_VIEW.isOpen())
|
|
50743
|
+
closeToolsSheet();
|
|
50744
|
+
updateUrlForSearchResultsOverlay(query ?? SEARCH_RESULTS_VIEW.getQuery());
|
|
50745
|
+
SEARCH_RESULTS_VIEW.open(query);
|
|
50746
|
+
syncAppPanel();
|
|
50747
|
+
}
|
|
50748
|
+
function closeSearchSheet() {
|
|
50749
|
+
SEARCH_RESULTS_VIEW.close();
|
|
50750
|
+
updateUrlForSearchResultsOverlay(null);
|
|
50751
|
+
syncAppPanel();
|
|
50752
|
+
}
|
|
50753
|
+
function syncSearchSheetFromUrl() {
|
|
50754
|
+
const query = parseSearchResultsOverlay(window.location.search);
|
|
50755
|
+
const open2 = SEARCH_RESULTS_VIEW.isOpen();
|
|
50756
|
+
if (query !== null && (!open2 || SEARCH_RESULTS_VIEW.getQuery() !== query))
|
|
50757
|
+
SEARCH_RESULTS_VIEW.open(query);
|
|
50758
|
+
else if (query === null && open2) SEARCH_RESULTS_VIEW.close();
|
|
50759
|
+
syncAppPanel();
|
|
50760
|
+
}
|
|
49073
50761
|
const TERMINAL_VIEW = createTerminalView({
|
|
49074
50762
|
$: (sel) => document.querySelector(sel),
|
|
49075
50763
|
trackLoad,
|
|
@@ -49098,6 +50786,8 @@ ${error2.stack}` : ""}`
|
|
|
49098
50786
|
function openTerminalSheet(id) {
|
|
49099
50787
|
if (parseToolsOverlay(window.location.search) !== null || TOOLS_VIEW.isOpen())
|
|
49100
50788
|
closeToolsSheet();
|
|
50789
|
+
if (parseSearchResultsOverlay(window.location.search) !== null || SEARCH_RESULTS_VIEW.isOpen())
|
|
50790
|
+
closeSearchSheet();
|
|
49101
50791
|
const target = id ?? TERMINAL_VIEW.getActiveTarget();
|
|
49102
50792
|
updateUrlForTerminalOverlay(target ?? "open");
|
|
49103
50793
|
void TERMINAL_VIEW.open(target);
|
|
@@ -49175,7 +50865,17 @@ ${error2.stack}` : ""}`
|
|
|
49175
50865
|
getFrom: () => STATE.from,
|
|
49176
50866
|
getTo: () => STATE.to,
|
|
49177
50867
|
getRepoRef: () => STATE.repoRef,
|
|
49178
|
-
getRoute: () => STATE.route
|
|
50868
|
+
getRoute: () => STATE.route,
|
|
50869
|
+
getRecentRefs: () => APP_SETTINGS.recentRefs || [],
|
|
50870
|
+
rememberRecentRef: (ref) => {
|
|
50871
|
+
const current = APP_SETTINGS.recentRefs || [];
|
|
50872
|
+
const next = rememberPaletteSelection(current, ref).slice(
|
|
50873
|
+
-MAX_RECENT_REFS
|
|
50874
|
+
);
|
|
50875
|
+
if (next.join("\0") === current.join("\0")) return;
|
|
50876
|
+
patchSettings({ recentRefs: next });
|
|
50877
|
+
},
|
|
50878
|
+
recentRefTitle: () => uiText().global.recentRef
|
|
49179
50879
|
});
|
|
49180
50880
|
if (REF_PICKER) {
|
|
49181
50881
|
const historyRefInput = document.querySelector("#history-ref");
|
|
@@ -49232,6 +50932,7 @@ ${error2.stack}` : ""}`
|
|
|
49232
50932
|
syncDoctorSheetFromUrl();
|
|
49233
50933
|
syncToolsSheetFromUrl();
|
|
49234
50934
|
syncTerminalSheetFromUrl();
|
|
50935
|
+
syncSearchSheetFromUrl();
|
|
49235
50936
|
if (isSameBlobFileRoute(previousRoute, STATE.route) && routeBlobPreview(previousRoute) !== routeBlobPreview(STATE.route) && switchSourceTab(routeBlobPreview(STATE.route) ? "preview" : "code", {
|
|
49236
50937
|
updateRoute: false
|
|
49237
50938
|
})) {
|