@youtyan/code-viewer 0.6.1 → 0.6.2
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 +7 -4
- package/dist/code-viewer.js +57 -6
- package/package.json +1 -1
- package/web/app.js +2040 -261
- package/web/index.html +40 -13
- package/web/style.css +786 -41
package/README.md
CHANGED
|
@@ -251,8 +251,10 @@ Open Datastores in the global navigation to access:
|
|
|
251
251
|
the database.
|
|
252
252
|
- **Table browser** — paginated data grid with column sort, text filter, cell
|
|
253
253
|
copy, and CSV/JSON export (capped at 100,000 rows; export respects current
|
|
254
|
-
filter/sort).
|
|
255
|
-
|
|
254
|
+
filter/sort). Reload just the current table from the search bar while
|
|
255
|
+
keeping global search and column filters, with a result chip that calls out
|
|
256
|
+
row-count changes. Double-click a cell to edit inline when Edit mode is on;
|
|
257
|
+
the whole pending edit batch commits atomically.
|
|
256
258
|
- **Detail footer and related panel** — click any cell to open a resizable
|
|
257
259
|
detail footer; foreign-key cells open a related panel showing the
|
|
258
260
|
referenced or referencing rows, with multi-step drill-down breadcrumbs.
|
|
@@ -276,8 +278,9 @@ Open Datastores in the global navigation to access:
|
|
|
276
278
|
- **ER diagram** — auto-generated entity-relationship diagram showing
|
|
277
279
|
foreign-key relationships between tables.
|
|
278
280
|
- **Schema view** — table columns (with column comments when the database
|
|
279
|
-
defines them), indexes, foreign keys, triggers, and DDL
|
|
280
|
-
surface a comment column on the
|
|
281
|
+
defines them), indexes, foreign keys, triggers, and DDL, with an in-tab
|
|
282
|
+
schema refresh action. Tables themselves surface a comment column on the
|
|
283
|
+
database table list.
|
|
281
284
|
- **Global search** — full-text search across all tables and text columns of
|
|
282
285
|
a database.
|
|
283
286
|
- **Snapshots and diffs** — take point-in-time snapshots of selected tables
|
package/dist/code-viewer.js
CHANGED
|
@@ -1438,7 +1438,19 @@ function omittedWorktreeDirectoryReason(name, omitDirNames) {
|
|
|
1438
1438
|
return "internal";
|
|
1439
1439
|
return omitDirNames.has(name) ? "heavy" : undefined;
|
|
1440
1440
|
}
|
|
1441
|
-
function
|
|
1441
|
+
function worktreeSubmodulePaths(cwd) {
|
|
1442
|
+
if (!existsSync(join2(cwd, ".gitmodules")))
|
|
1443
|
+
return new Set;
|
|
1444
|
+
const res = run(["git", "config", "--file", ".gitmodules", "--get-regexp", "\\.path$"], cwd);
|
|
1445
|
+
if (res.code !== 0)
|
|
1446
|
+
return new Set;
|
|
1447
|
+
return new Set(res.stdout.split(`
|
|
1448
|
+
`).map((line) => {
|
|
1449
|
+
const split = line.indexOf(" ");
|
|
1450
|
+
return split >= 0 ? normalizeTreePath(line.slice(split + 1)) : "";
|
|
1451
|
+
}).filter(Boolean));
|
|
1452
|
+
}
|
|
1453
|
+
function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, excludeNames, submodulePaths) {
|
|
1442
1454
|
if (excludeNames.has(name.toLowerCase()))
|
|
1443
1455
|
return {
|
|
1444
1456
|
name,
|
|
@@ -1448,23 +1460,24 @@ function worktreeEntryFromDirent(base, dir, name, isDirectory, omitDirNames, exc
|
|
|
1448
1460
|
const entryPath = base ? `${base}/${name}` : name;
|
|
1449
1461
|
const type = isDirectory ? hasDotGitEntry(join2(dir, name)) ? "commit" : "tree" : "blob";
|
|
1450
1462
|
const omittedReason = type === "tree" ? omittedWorktreeDirectoryReason(name, omitDirNames) : undefined;
|
|
1463
|
+
const submodule = type === "commit" && submodulePaths.has(entryPath) ? true : undefined;
|
|
1464
|
+
const baseEntry = submodule ? { name, path: entryPath, type, submodule } : { name, path: entryPath, type };
|
|
1451
1465
|
return omittedReason ? {
|
|
1452
|
-
|
|
1453
|
-
path: entryPath,
|
|
1454
|
-
type,
|
|
1466
|
+
...baseEntry,
|
|
1455
1467
|
children_omitted: true,
|
|
1456
1468
|
children_omitted_reason: omittedReason
|
|
1457
|
-
} :
|
|
1469
|
+
} : baseEntry;
|
|
1458
1470
|
}
|
|
1459
1471
|
function worktreeFilesystemEntries(cwd, path, recursive, omitDirNames = DEFAULT_WORKTREE_OMIT_DIR_NAMES, excludeNames = []) {
|
|
1460
1472
|
const base = normalizeTreePath(path);
|
|
1461
1473
|
const root = join2(cwd, base);
|
|
1462
1474
|
const omitDirNameSet = new Set(omitDirNames);
|
|
1463
1475
|
const excludeNameSet = new Set(excludeNames.map((name) => name.toLowerCase()));
|
|
1476
|
+
const submodulePaths = worktreeSubmodulePaths(cwd);
|
|
1464
1477
|
let directEntries;
|
|
1465
1478
|
try {
|
|
1466
1479
|
const dirents = readdirSync(root, { withFileTypes: true });
|
|
1467
|
-
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet)).filter((entry) => entry.path));
|
|
1480
|
+
directEntries = sortTreeEntries(dirents.map((entry) => worktreeEntryFromDirent(base, root, entry.name, entry.isDirectory(), omitDirNameSet, excludeNameSet, submodulePaths)).filter((entry) => entry.path));
|
|
1468
1481
|
} catch {
|
|
1469
1482
|
return [];
|
|
1470
1483
|
}
|
|
@@ -15437,6 +15450,38 @@ async function handleTable(cwd, url, omitDirNames, signal) {
|
|
|
15437
15450
|
return handleError("database", "read table", err, signal);
|
|
15438
15451
|
}
|
|
15439
15452
|
}
|
|
15453
|
+
async function handleTableCount(cwd, url, omitDirNames, signal) {
|
|
15454
|
+
const r = await resolveDb(cwd, url.searchParams.get("db"), omitDirNames, url.searchParams.get("schema"), signal);
|
|
15455
|
+
if (r instanceof Response)
|
|
15456
|
+
return r;
|
|
15457
|
+
const table = url.searchParams.get("table");
|
|
15458
|
+
if (!table)
|
|
15459
|
+
return textError("missing table parameter", 400);
|
|
15460
|
+
try {
|
|
15461
|
+
const adapter = await getAdapter(r, cwd, signal);
|
|
15462
|
+
const { result, executedSql } = await captureSql(async () => {
|
|
15463
|
+
const db = asAsync(adapter);
|
|
15464
|
+
const tables = await db.tables(signal);
|
|
15465
|
+
const entry = tables.find((candidate) => candidate.name === table);
|
|
15466
|
+
if (!entry)
|
|
15467
|
+
throw new Error(`unknown table: ${table}`);
|
|
15468
|
+
if (entry.type !== "table")
|
|
15469
|
+
return { rowCount: null };
|
|
15470
|
+
const counts = await db.tableRowCounts([table], signal);
|
|
15471
|
+
return { rowCount: counts.get(table) ?? null };
|
|
15472
|
+
});
|
|
15473
|
+
const body = {
|
|
15474
|
+
dbId: r.dbId,
|
|
15475
|
+
...r.schema ? { schema: r.schema } : {},
|
|
15476
|
+
table,
|
|
15477
|
+
rowCount: result.rowCount,
|
|
15478
|
+
executedSql
|
|
15479
|
+
};
|
|
15480
|
+
return json(body);
|
|
15481
|
+
} catch (err) {
|
|
15482
|
+
return handleError("database", "read table count", err, signal);
|
|
15483
|
+
}
|
|
15484
|
+
}
|
|
15440
15485
|
function makeHistoryId() {
|
|
15441
15486
|
return makeId("qh");
|
|
15442
15487
|
}
|
|
@@ -16365,6 +16410,10 @@ async function handleDatabaseRoute(req, url, cwd, omitDirNames, sideEffectAllowe
|
|
|
16365
16410
|
methods: ["GET"],
|
|
16366
16411
|
handler: () => handleTable(cwd, url, omitDirNames, req.signal)
|
|
16367
16412
|
},
|
|
16413
|
+
"/_db/table-count": {
|
|
16414
|
+
methods: ["GET"],
|
|
16415
|
+
handler: () => handleTableCount(cwd, url, omitDirNames, req.signal)
|
|
16416
|
+
},
|
|
16368
16417
|
"/_db/columns": {
|
|
16369
16418
|
methods: ["GET"],
|
|
16370
16419
|
handler: () => handleColumns(cwd, url, omitDirNames, req.signal)
|
|
@@ -19983,6 +20032,8 @@ function fileMetadataForTarget(target, path) {
|
|
|
19983
20032
|
function attachTreeEntryMetadata(target, entry) {
|
|
19984
20033
|
if (entry.type === "tree")
|
|
19985
20034
|
return { ...entry, ...directoryMetadata(target, entry.path) };
|
|
20035
|
+
if (entry.type === "commit" && !entry.submodule && (target === "worktree" || target === ""))
|
|
20036
|
+
return { ...entry, ...directoryMetadata(target, entry.path) };
|
|
19986
20037
|
if (entry.type !== "blob")
|
|
19987
20038
|
return entry;
|
|
19988
20039
|
return { ...entry, ...fileMetadataForTarget(target, entry.path) };
|