@youtyan/code-viewer 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/code-viewer.js +46 -12
- package/package.json +1 -1
- package/web/app.js +112 -10
package/dist/code-viewer.js
CHANGED
|
@@ -280,6 +280,8 @@ var DEFAULT_WORKTREE_OMIT_DIR_NAMES = [
|
|
|
280
280
|
"out",
|
|
281
281
|
"target",
|
|
282
282
|
".gradle",
|
|
283
|
+
".pnpm-store",
|
|
284
|
+
".turbo",
|
|
283
285
|
"__pycache__",
|
|
284
286
|
".pytest_cache",
|
|
285
287
|
".tox",
|
|
@@ -1279,6 +1281,9 @@ function startWorktreeUpdateWatch(options) {
|
|
|
1279
1281
|
const debounceMs = options.debounceMs ?? 250;
|
|
1280
1282
|
const watchers = new Map;
|
|
1281
1283
|
const signatures = new Map;
|
|
1284
|
+
const initialScanAsync = options.initialScanMode === "async" || (!options.watch || options.watch === nodeWatch) && !options.readdirSync;
|
|
1285
|
+
const initialScanQueue = [];
|
|
1286
|
+
let initialScanTimer = null;
|
|
1282
1287
|
let timer = null;
|
|
1283
1288
|
const ignored = (path) => isSkippableSearchPath(normalizeRelativePath(path), options.omitDirNames, options.excludeNames);
|
|
1284
1289
|
const scheduleUpdate = () => {
|
|
@@ -1301,6 +1306,11 @@ function startWorktreeUpdateWatch(options) {
|
|
|
1301
1306
|
}
|
|
1302
1307
|
};
|
|
1303
1308
|
const closeAll = () => {
|
|
1309
|
+
if (initialScanTimer) {
|
|
1310
|
+
clearTimer(initialScanTimer);
|
|
1311
|
+
initialScanTimer = null;
|
|
1312
|
+
}
|
|
1313
|
+
initialScanQueue.length = 0;
|
|
1304
1314
|
for (const watcher of [...watchers.values()]) {
|
|
1305
1315
|
try {
|
|
1306
1316
|
watcher.close?.();
|
|
@@ -1309,7 +1319,36 @@ function startWorktreeUpdateWatch(options) {
|
|
|
1309
1319
|
watchers.clear();
|
|
1310
1320
|
signatures.clear();
|
|
1311
1321
|
};
|
|
1312
|
-
const
|
|
1322
|
+
const readChildDirectories = (dir) => {
|
|
1323
|
+
let entries;
|
|
1324
|
+
try {
|
|
1325
|
+
entries = readDirs(dir);
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
options.onError?.(error);
|
|
1328
|
+
return [];
|
|
1329
|
+
}
|
|
1330
|
+
const children = [];
|
|
1331
|
+
for (const entry of entries) {
|
|
1332
|
+
if (!entry.isDirectory())
|
|
1333
|
+
continue;
|
|
1334
|
+
children.push(join4(dir, entry.name));
|
|
1335
|
+
}
|
|
1336
|
+
return children;
|
|
1337
|
+
};
|
|
1338
|
+
const processInitialScanQueue = () => {
|
|
1339
|
+
initialScanTimer = null;
|
|
1340
|
+
const next = initialScanQueue.shift();
|
|
1341
|
+
if (next)
|
|
1342
|
+
watchDirectory(next, true);
|
|
1343
|
+
if (initialScanQueue.length)
|
|
1344
|
+
initialScanTimer = setTimer(processInitialScanQueue, 50);
|
|
1345
|
+
};
|
|
1346
|
+
const queueInitialChildren = (dir) => {
|
|
1347
|
+
initialScanQueue.push(...readChildDirectories(dir));
|
|
1348
|
+
if (!initialScanTimer)
|
|
1349
|
+
initialScanTimer = setTimer(processInitialScanQueue, 5000);
|
|
1350
|
+
};
|
|
1351
|
+
const watchDirectory = (dir, initialScan = false) => {
|
|
1313
1352
|
if (watchers.has(dir))
|
|
1314
1353
|
return;
|
|
1315
1354
|
const rel = normalizeRelativePath(relative(options.root, dir));
|
|
@@ -1364,20 +1403,14 @@ function startWorktreeUpdateWatch(options) {
|
|
|
1364
1403
|
options.onError?.(error);
|
|
1365
1404
|
return;
|
|
1366
1405
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
entries = readDirs(dir);
|
|
1370
|
-
} catch (error) {
|
|
1371
|
-
options.onError?.(error);
|
|
1406
|
+
if (initialScanAsync && initialScan) {
|
|
1407
|
+
queueInitialChildren(dir);
|
|
1372
1408
|
return;
|
|
1373
1409
|
}
|
|
1374
|
-
for (const
|
|
1375
|
-
|
|
1376
|
-
continue;
|
|
1377
|
-
watchDirectory(join4(dir, entry.name));
|
|
1378
|
-
}
|
|
1410
|
+
for (const child of readChildDirectories(dir))
|
|
1411
|
+
watchDirectory(child);
|
|
1379
1412
|
};
|
|
1380
|
-
watchDirectory(options.root);
|
|
1413
|
+
watchDirectory(options.root, true);
|
|
1381
1414
|
return { started: watchers.size > 0, close: closeAll };
|
|
1382
1415
|
}
|
|
1383
1416
|
|
|
@@ -3147,6 +3180,7 @@ startWorktreeUpdateWatch({
|
|
|
3147
3180
|
omitDirNames: scopeOmitDirNames,
|
|
3148
3181
|
excludeNames: scopeExcludeNames,
|
|
3149
3182
|
watch,
|
|
3183
|
+
initialScanMode: "async",
|
|
3150
3184
|
onUpdate: triggerUpdate,
|
|
3151
3185
|
onError: (error) => {
|
|
3152
3186
|
const message = error instanceof Error ? error.message : String(error);
|
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -7002,7 +7002,9 @@ ${frontmatter.yaml}
|
|
|
7002
7002
|
let SIDEBAR_VISIBLE_ROWS = [];
|
|
7003
7003
|
let SIDEBAR_ROW_BY_PATH = new Map;
|
|
7004
7004
|
let SIDEBAR_VIRTUAL_ACTIVE_PATH = "";
|
|
7005
|
-
|
|
7005
|
+
let SIDEBAR_TREE_ITEMS_CACHE = new WeakMap;
|
|
7006
|
+
const SIDEBAR_LAZY_LOADED_DIRS = new Set;
|
|
7007
|
+
const SIDEBAR_LAZY_LOADING_DIRS = new Map;
|
|
7006
7008
|
let REPO_SORT = {
|
|
7007
7009
|
key: "name",
|
|
7008
7010
|
direction: "asc"
|
|
@@ -8058,6 +8060,86 @@ ${frontmatter.yaml}
|
|
|
8058
8060
|
SIDEBAR_TREE_ITEMS_CACHE.set(node, items);
|
|
8059
8061
|
return items;
|
|
8060
8062
|
}
|
|
8063
|
+
function sidebarTreeNodeHasChildren(node) {
|
|
8064
|
+
return Object.keys(node.dirs).length > 0 || node.files.length > 0;
|
|
8065
|
+
}
|
|
8066
|
+
function shouldLazyLoadSidebarDir(dir) {
|
|
8067
|
+
return isRepositorySidebarMode() && isVirtualSidebarActive() && !dir.children_omitted && !sidebarTreeNodeHasChildren(dir) && !SIDEBAR_LAZY_LOADED_DIRS.has(dir.path);
|
|
8068
|
+
}
|
|
8069
|
+
function upsertSidebarTreeEntry(entry, order) {
|
|
8070
|
+
if (!SIDEBAR_TREE_ROOT)
|
|
8071
|
+
return;
|
|
8072
|
+
const parts = entry.path.split("/").filter(Boolean);
|
|
8073
|
+
if (!parts.length)
|
|
8074
|
+
return;
|
|
8075
|
+
let node = SIDEBAR_TREE_ROOT;
|
|
8076
|
+
let acc = "";
|
|
8077
|
+
const dirPartCount = entry.type === "tree" ? parts.length : parts.length - 1;
|
|
8078
|
+
for (let i2 = 0;i2 < dirPartCount; i2++) {
|
|
8079
|
+
const part = parts[i2];
|
|
8080
|
+
acc = acc ? `${acc}/${part}` : part;
|
|
8081
|
+
if (!node.dirs[part]) {
|
|
8082
|
+
node.dirs[part] = {
|
|
8083
|
+
name: part,
|
|
8084
|
+
dirs: {},
|
|
8085
|
+
files: [],
|
|
8086
|
+
path: acc,
|
|
8087
|
+
minOrder: order
|
|
8088
|
+
};
|
|
8089
|
+
}
|
|
8090
|
+
node = node.dirs[part];
|
|
8091
|
+
node.minOrder = Math.min(node.minOrder, order);
|
|
8092
|
+
}
|
|
8093
|
+
if (entry.type === "tree") {
|
|
8094
|
+
node.explicit = true;
|
|
8095
|
+
if (entry.children_omitted === true) {
|
|
8096
|
+
node.children_omitted = true;
|
|
8097
|
+
node.children_omitted_reason = entry.children_omitted_reason;
|
|
8098
|
+
}
|
|
8099
|
+
return;
|
|
8100
|
+
}
|
|
8101
|
+
if (!node.files.some((file) => file.path === entry.path))
|
|
8102
|
+
node.files.push({ ...entry, order });
|
|
8103
|
+
}
|
|
8104
|
+
function mergeSidebarTreeEntries(entries) {
|
|
8105
|
+
entries.forEach((entry, index) => {
|
|
8106
|
+
upsertSidebarTreeEntry(entry, entry.order ?? index + 1);
|
|
8107
|
+
});
|
|
8108
|
+
SIDEBAR_TREE_ITEMS_CACHE = new WeakMap;
|
|
8109
|
+
if (SIDEBAR_TREE_ROOT)
|
|
8110
|
+
buildSidebarTreeRows(SIDEBAR_TREE_ROOT);
|
|
8111
|
+
}
|
|
8112
|
+
function ensureVirtualSidebarDirLoaded(dir) {
|
|
8113
|
+
if (!shouldLazyLoadSidebarDir(dir))
|
|
8114
|
+
return Promise.resolve();
|
|
8115
|
+
const existing = SIDEBAR_LAZY_LOADING_DIRS.get(dir.path);
|
|
8116
|
+
if (existing)
|
|
8117
|
+
return existing;
|
|
8118
|
+
const params = new URLSearchParams;
|
|
8119
|
+
params.set("ref", REPO_SIDEBAR_REF || "worktree");
|
|
8120
|
+
params.set("path", dir.path);
|
|
8121
|
+
appendScopeParams(params);
|
|
8122
|
+
const load2 = trackLoad(fetch(`/_tree?${params.toString()}`).then((response) => {
|
|
8123
|
+
if (!response.ok)
|
|
8124
|
+
throw new Error("failed to load repository tree");
|
|
8125
|
+
return response.json();
|
|
8126
|
+
})).then((meta) => {
|
|
8127
|
+
const entries = meta.entries.map((entry, index) => ({
|
|
8128
|
+
order: dir.minOrder + (index + 1) / 1e5,
|
|
8129
|
+
path: entry.path,
|
|
8130
|
+
display_path: entry.path,
|
|
8131
|
+
type: entry.type,
|
|
8132
|
+
children_omitted: entry.children_omitted,
|
|
8133
|
+
children_omitted_reason: entry.children_omitted_reason
|
|
8134
|
+
}));
|
|
8135
|
+
mergeSidebarTreeEntries(entries);
|
|
8136
|
+
SIDEBAR_LAZY_LOADED_DIRS.add(dir.path);
|
|
8137
|
+
}).finally(() => {
|
|
8138
|
+
SIDEBAR_LAZY_LOADING_DIRS.delete(dir.path);
|
|
8139
|
+
});
|
|
8140
|
+
SIDEBAR_LAZY_LOADING_DIRS.set(dir.path, load2);
|
|
8141
|
+
return load2;
|
|
8142
|
+
}
|
|
8061
8143
|
function createTreeDirRow(dir, depth, onFileClick) {
|
|
8062
8144
|
const li = document.createElement("li");
|
|
8063
8145
|
li.className = "tree-dir";
|
|
@@ -8105,16 +8187,26 @@ ${frontmatter.yaml}
|
|
|
8105
8187
|
const updateIcon = () => {
|
|
8106
8188
|
setFolderIcon(dirIcon, li.classList.contains("collapsed"));
|
|
8107
8189
|
};
|
|
8108
|
-
const toggleDir = (e2) => {
|
|
8190
|
+
const toggleDir = async (e2) => {
|
|
8109
8191
|
e2.stopPropagation();
|
|
8110
|
-
li.
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8192
|
+
if (li.dataset.toggling === "true")
|
|
8193
|
+
return;
|
|
8194
|
+
const expanding = li.classList.contains("collapsed");
|
|
8195
|
+
li.dataset.toggling = "true";
|
|
8196
|
+
try {
|
|
8197
|
+
if (expanding)
|
|
8198
|
+
await ensureVirtualSidebarDirLoaded(dir);
|
|
8199
|
+
li.classList.toggle("collapsed");
|
|
8200
|
+
updateIcon();
|
|
8201
|
+
if (li.classList.contains("collapsed"))
|
|
8202
|
+
STATE.collapsedDirs.add(dir.path);
|
|
8203
|
+
else
|
|
8204
|
+
STATE.collapsedDirs.delete(dir.path);
|
|
8205
|
+
localStorage.setItem("gdp:collapsed-dirs", JSON.stringify([...STATE.collapsedDirs]));
|
|
8206
|
+
rerenderVirtualSidebar();
|
|
8207
|
+
} finally {
|
|
8208
|
+
delete li.dataset.toggling;
|
|
8209
|
+
}
|
|
8118
8210
|
};
|
|
8119
8211
|
li.classList.toggle("collapsed", STATE.collapsedDirs.has(dir.path));
|
|
8120
8212
|
updateIcon();
|
|
@@ -8382,6 +8474,8 @@ ${frontmatter.yaml}
|
|
|
8382
8474
|
SIDEBAR_TREE_ROWS = [];
|
|
8383
8475
|
SIDEBAR_VISIBLE_ROWS = [];
|
|
8384
8476
|
SIDEBAR_ROW_BY_PATH = new Map;
|
|
8477
|
+
SIDEBAR_LAZY_LOADED_DIRS.clear();
|
|
8478
|
+
SIDEBAR_LAZY_LOADING_DIRS.clear();
|
|
8385
8479
|
STATE.files = files;
|
|
8386
8480
|
SIDEBAR_FILES = files;
|
|
8387
8481
|
SIDEBAR_ON_FILE_CLICK = onFileClick;
|
|
@@ -9851,6 +9945,14 @@ ${frontmatter.yaml}
|
|
|
9851
9945
|
function activateRepoSidebarPath(currentPath) {
|
|
9852
9946
|
markActive(currentPath, { reveal: true });
|
|
9853
9947
|
applyFilter();
|
|
9948
|
+
const row = SIDEBAR_ROW_BY_PATH.get(currentPath);
|
|
9949
|
+
if (row?.kind === "dir" && row.dir && shouldLazyLoadSidebarDir(row.dir))
|
|
9950
|
+
ensureVirtualSidebarDirLoaded(row.dir).then(() => {
|
|
9951
|
+
if (SIDEBAR_VIRTUAL_ACTIVE_PATH === currentPath) {
|
|
9952
|
+
rerenderVirtualSidebar();
|
|
9953
|
+
scrollVirtualSidebarPathIntoView(currentPath);
|
|
9954
|
+
}
|
|
9955
|
+
});
|
|
9854
9956
|
}
|
|
9855
9957
|
function createPlaceholder(f2) {
|
|
9856
9958
|
const card = document.createElement("div");
|