@youtyan/code-viewer 0.8.1 → 0.8.3
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 +13 -4
- package/dist/code-viewer.js +433 -180
- package/package.json +1 -1
- package/web/app.js +418 -133
- package/web/style.css +92 -24
package/web/app.js
CHANGED
|
@@ -10480,6 +10480,80 @@ ${frontmatter.yaml}
|
|
|
10480
10480
|
return true;
|
|
10481
10481
|
}
|
|
10482
10482
|
|
|
10483
|
+
// web-src/views/database/detail-table.ts
|
|
10484
|
+
function createDetailTable(headers, rows, emptyText) {
|
|
10485
|
+
const table2 = document.createElement("table");
|
|
10486
|
+
table2.className = "db-detail-table";
|
|
10487
|
+
const thead = document.createElement("thead");
|
|
10488
|
+
const headRow = document.createElement("tr");
|
|
10489
|
+
for (const label of headers) {
|
|
10490
|
+
const th = document.createElement("th");
|
|
10491
|
+
th.textContent = label;
|
|
10492
|
+
headRow.appendChild(th);
|
|
10493
|
+
}
|
|
10494
|
+
thead.appendChild(headRow);
|
|
10495
|
+
table2.appendChild(thead);
|
|
10496
|
+
const tbody = document.createElement("tbody");
|
|
10497
|
+
if (rows.length === 0) {
|
|
10498
|
+
const row = document.createElement("tr");
|
|
10499
|
+
const td = document.createElement("td");
|
|
10500
|
+
td.colSpan = headers.length;
|
|
10501
|
+
td.className = "db-value-empty";
|
|
10502
|
+
td.textContent = emptyText;
|
|
10503
|
+
row.appendChild(td);
|
|
10504
|
+
tbody.appendChild(row);
|
|
10505
|
+
}
|
|
10506
|
+
for (const cells of rows) {
|
|
10507
|
+
const row = document.createElement("tr");
|
|
10508
|
+
cells.forEach((cellText, index) => {
|
|
10509
|
+
const td = document.createElement("td");
|
|
10510
|
+
td.textContent = cellText;
|
|
10511
|
+
td.className = index === 0 ? "db-detail-table-primary" : "db-detail-table-muted";
|
|
10512
|
+
row.appendChild(td);
|
|
10513
|
+
});
|
|
10514
|
+
tbody.appendChild(row);
|
|
10515
|
+
}
|
|
10516
|
+
table2.appendChild(tbody);
|
|
10517
|
+
return table2;
|
|
10518
|
+
}
|
|
10519
|
+
|
|
10520
|
+
// web-src/views/database/detail-tabs.ts
|
|
10521
|
+
function createDetailTabs(specs, initial, onSelect) {
|
|
10522
|
+
const tabsEl = document.createElement("div");
|
|
10523
|
+
tabsEl.className = "db-detail-tabs";
|
|
10524
|
+
const buttons = {};
|
|
10525
|
+
const bodies = {};
|
|
10526
|
+
let active = initial;
|
|
10527
|
+
function setActive(tab) {
|
|
10528
|
+
active = tab;
|
|
10529
|
+
for (const spec of specs) {
|
|
10530
|
+
buttons[spec.id].classList.toggle("active", spec.id === tab);
|
|
10531
|
+
bodies[spec.id].hidden = spec.id !== tab;
|
|
10532
|
+
}
|
|
10533
|
+
}
|
|
10534
|
+
for (const spec of specs) {
|
|
10535
|
+
const btn = document.createElement("button");
|
|
10536
|
+
btn.type = "button";
|
|
10537
|
+
btn.className = "db-detail-tab";
|
|
10538
|
+
btn.textContent = spec.label;
|
|
10539
|
+
btn.addEventListener("click", () => {
|
|
10540
|
+
setActive(spec.id);
|
|
10541
|
+
onSelect(spec.id);
|
|
10542
|
+
});
|
|
10543
|
+
buttons[spec.id] = btn;
|
|
10544
|
+
tabsEl.appendChild(btn);
|
|
10545
|
+
const body = document.createElement("div");
|
|
10546
|
+
body.className = "db-detail-tab-body";
|
|
10547
|
+
bodies[spec.id] = body;
|
|
10548
|
+
}
|
|
10549
|
+
setActive(initial);
|
|
10550
|
+
function setLabels(labels) {
|
|
10551
|
+
for (const spec of specs)
|
|
10552
|
+
buttons[spec.id].textContent = labels[spec.id];
|
|
10553
|
+
}
|
|
10554
|
+
return { tabsEl, bodies, getActive: () => active, setActive, setLabels };
|
|
10555
|
+
}
|
|
10556
|
+
|
|
10483
10557
|
// web-src/views/database/i18n.ts
|
|
10484
10558
|
var EN = {
|
|
10485
10559
|
nav: {
|
|
@@ -10820,7 +10894,21 @@ ${frontmatter.yaml}
|
|
|
10820
10894
|
copied: "Copied",
|
|
10821
10895
|
copyFailed: "Copy failed",
|
|
10822
10896
|
invalidAttributeValues: "Invalid attribute values JSON",
|
|
10823
|
-
runQuery: "Run"
|
|
10897
|
+
runQuery: "Run",
|
|
10898
|
+
structureTab: "Structure",
|
|
10899
|
+
itemTab: "Item",
|
|
10900
|
+
selectTable: "Select a table to view its structure.",
|
|
10901
|
+
attributeHeader: "Attribute",
|
|
10902
|
+
typeHeader: "Type",
|
|
10903
|
+
keyRoleHeader: "Key",
|
|
10904
|
+
noAttributes: "(no attributes)",
|
|
10905
|
+
globalSecondaryIndexes: "Global secondary indexes",
|
|
10906
|
+
localSecondaryIndexes: "Local secondary indexes",
|
|
10907
|
+
projectionAll: "ALL",
|
|
10908
|
+
projectionKeysOnly: "KEYS_ONLY",
|
|
10909
|
+
projectionInclude: (attrs) => `INCLUDE (${attrs})`,
|
|
10910
|
+
keySchemaOnlyHint: "DynamoDB only enforces types for key attributes. Additional attributes will appear here once items are loaded.",
|
|
10911
|
+
inferredAttributesNote: (count) => `Attributes beyond the key schema are inferred from ${count.toLocaleString()} loaded item${count === 1 ? "" : "s"} and may not reflect every item.`
|
|
10824
10912
|
}
|
|
10825
10913
|
}
|
|
10826
10914
|
};
|
|
@@ -11163,7 +11251,21 @@ ${frontmatter.yaml}
|
|
|
11163
11251
|
copied: "コピーしました",
|
|
11164
11252
|
copyFailed: "コピーに失敗しました",
|
|
11165
11253
|
invalidAttributeValues: "属性値の JSON が不正です",
|
|
11166
|
-
runQuery: "実行"
|
|
11254
|
+
runQuery: "実行",
|
|
11255
|
+
structureTab: "構造",
|
|
11256
|
+
itemTab: "アイテム",
|
|
11257
|
+
selectTable: "テーブルを選択すると構造が表示されます。",
|
|
11258
|
+
attributeHeader: "属性",
|
|
11259
|
+
typeHeader: "型",
|
|
11260
|
+
keyRoleHeader: "キー",
|
|
11261
|
+
noAttributes: "(属性がありません)",
|
|
11262
|
+
globalSecondaryIndexes: "グローバルセカンダリインデックス",
|
|
11263
|
+
localSecondaryIndexes: "ローカルセカンダリインデックス",
|
|
11264
|
+
projectionAll: "ALL",
|
|
11265
|
+
projectionKeysOnly: "KEYS_ONLY",
|
|
11266
|
+
projectionInclude: (attrs) => `INCLUDE (${attrs})`,
|
|
11267
|
+
keySchemaOnlyHint: "DynamoDBがスキーマとして強制するのはキー属性のみです。アイテムを読み込むと追加の属性がここに表示されます。",
|
|
11268
|
+
inferredAttributesNote: (count) => `キー以外の属性は、読み込み済みの${count.toLocaleString()}件のアイテムから検出したものです (全アイテムを網羅するとは限りません)。`
|
|
11167
11269
|
}
|
|
11168
11270
|
}
|
|
11169
11271
|
};
|
|
@@ -11237,6 +11339,25 @@ ${frontmatter.yaml}
|
|
|
11237
11339
|
out[k] = unwrapAttributeValue(v);
|
|
11238
11340
|
return out;
|
|
11239
11341
|
}
|
|
11342
|
+
var ATTRIBUTE_VALUE_TAG_ORDER = [
|
|
11343
|
+
"S",
|
|
11344
|
+
"N",
|
|
11345
|
+
"B",
|
|
11346
|
+
"BOOL",
|
|
11347
|
+
"NULL",
|
|
11348
|
+
"M",
|
|
11349
|
+
"L",
|
|
11350
|
+
"SS",
|
|
11351
|
+
"NS",
|
|
11352
|
+
"BS"
|
|
11353
|
+
];
|
|
11354
|
+
function attributeValueTag(av) {
|
|
11355
|
+
for (const tag of ATTRIBUTE_VALUE_TAG_ORDER) {
|
|
11356
|
+
if (tag in av)
|
|
11357
|
+
return tag;
|
|
11358
|
+
}
|
|
11359
|
+
return "?";
|
|
11360
|
+
}
|
|
11240
11361
|
function previewItem(item) {
|
|
11241
11362
|
const entries = Object.entries(item).slice(0, 6);
|
|
11242
11363
|
const parts = entries.map(([key, rawValue]) => {
|
|
@@ -11347,8 +11468,17 @@ ${frontmatter.yaml}
|
|
|
11347
11468
|
moreBtn.hidden = true;
|
|
11348
11469
|
itemListPane.appendChild(moreBtn);
|
|
11349
11470
|
const detailPane = document.createElement("div");
|
|
11350
|
-
detailPane.className = "
|
|
11351
|
-
|
|
11471
|
+
detailPane.className = "db-detail-pane";
|
|
11472
|
+
const detailTabs = createDetailTabs([
|
|
11473
|
+
{ id: "structure", label: text3().dynamodb.structureTab },
|
|
11474
|
+
{ id: "item", label: text3().dynamodb.itemTab }
|
|
11475
|
+
], "structure", () => notifySelectionChange());
|
|
11476
|
+
detailPane.appendChild(detailTabs.tabsEl);
|
|
11477
|
+
const structureBody = detailTabs.bodies.structure;
|
|
11478
|
+
setPaneEmpty(structureBody, text3().dynamodb.selectTable);
|
|
11479
|
+
const itemBody = detailTabs.bodies.item;
|
|
11480
|
+
setPaneEmpty(itemBody, text3().dynamodb.selectItem);
|
|
11481
|
+
detailPane.append(structureBody, itemBody);
|
|
11352
11482
|
container.append(itemListPane, detailPane);
|
|
11353
11483
|
let currentDbId = null;
|
|
11354
11484
|
let currentTable = null;
|
|
@@ -11360,6 +11490,7 @@ ${frontmatter.yaml}
|
|
|
11360
11490
|
let currentItemKeyToken = null;
|
|
11361
11491
|
let cumulativeShownCount = 0;
|
|
11362
11492
|
let cumulativeScannedCount = 0;
|
|
11493
|
+
let lastRenderedItem = null;
|
|
11363
11494
|
let disposed = false;
|
|
11364
11495
|
let loadRunId = 0;
|
|
11365
11496
|
let itemRunId = 0;
|
|
@@ -11384,6 +11515,9 @@ ${frontmatter.yaml}
|
|
|
11384
11515
|
queryModeBtn.classList.toggle("active", mode === "query");
|
|
11385
11516
|
keyConditionInput.hidden = mode !== "query";
|
|
11386
11517
|
}
|
|
11518
|
+
function setDetailTab(tab) {
|
|
11519
|
+
detailTabs.setActive(tab);
|
|
11520
|
+
}
|
|
11387
11521
|
function renderTables(tableNames, append = false) {
|
|
11388
11522
|
if (!append) {
|
|
11389
11523
|
tableList.innerHTML = "";
|
|
@@ -11436,10 +11570,51 @@ ${frontmatter.yaml}
|
|
|
11436
11570
|
}
|
|
11437
11571
|
itemList.appendChild(fragment);
|
|
11438
11572
|
}
|
|
11439
|
-
function
|
|
11440
|
-
|
|
11441
|
-
if (!
|
|
11573
|
+
function projectionText(projection) {
|
|
11574
|
+
const t2 = text3().dynamodb;
|
|
11575
|
+
if (!projection?.ProjectionType)
|
|
11576
|
+
return "";
|
|
11577
|
+
if (projection.ProjectionType === "ALL")
|
|
11578
|
+
return t2.projectionAll;
|
|
11579
|
+
if (projection.ProjectionType === "KEYS_ONLY")
|
|
11580
|
+
return t2.projectionKeysOnly;
|
|
11581
|
+
return t2.projectionInclude((projection.NonKeyAttributes ?? []).join(", "));
|
|
11582
|
+
}
|
|
11583
|
+
function renderSecondaryIndexes(label, indexes) {
|
|
11584
|
+
if (!indexes || indexes.length === 0)
|
|
11442
11585
|
return;
|
|
11586
|
+
const section = document.createElement("div");
|
|
11587
|
+
section.className = "dynamodb-index-section";
|
|
11588
|
+
const heading2 = document.createElement("div");
|
|
11589
|
+
heading2.className = "dynamodb-index-heading";
|
|
11590
|
+
heading2.textContent = label;
|
|
11591
|
+
section.appendChild(heading2);
|
|
11592
|
+
for (const ix of indexes) {
|
|
11593
|
+
const row = document.createElement("div");
|
|
11594
|
+
row.className = "dynamodb-index-row";
|
|
11595
|
+
const name = document.createElement("div");
|
|
11596
|
+
name.className = "dynamodb-index-name";
|
|
11597
|
+
name.textContent = ix.IndexName ?? "";
|
|
11598
|
+
const detail = document.createElement("div");
|
|
11599
|
+
detail.className = "dynamodb-index-detail";
|
|
11600
|
+
const keyPart = (ix.KeySchema ?? []).map((k) => `${k.AttributeName} (${k.KeyType})`).join(", ");
|
|
11601
|
+
detail.textContent = [
|
|
11602
|
+
keyPart,
|
|
11603
|
+
projectionText(ix.Projection),
|
|
11604
|
+
ix.ItemCount !== undefined ? `${ix.ItemCount.toLocaleString()} items` : undefined
|
|
11605
|
+
].filter(Boolean).join(" / ");
|
|
11606
|
+
row.append(name, detail);
|
|
11607
|
+
section.appendChild(row);
|
|
11608
|
+
}
|
|
11609
|
+
structureBody.appendChild(section);
|
|
11610
|
+
}
|
|
11611
|
+
function renderTableStructure() {
|
|
11612
|
+
structureBody.innerHTML = "";
|
|
11613
|
+
if (!currentTableInfo) {
|
|
11614
|
+
setPaneEmpty(structureBody, text3().dynamodb.selectTable);
|
|
11615
|
+
return;
|
|
11616
|
+
}
|
|
11617
|
+
const t2 = text3().dynamodb;
|
|
11443
11618
|
const header = document.createElement("div");
|
|
11444
11619
|
header.className = "dynamodb-table-info-header";
|
|
11445
11620
|
header.textContent = currentTableInfo.TableName ?? currentTable ?? "";
|
|
@@ -11447,26 +11622,54 @@ ${frontmatter.yaml}
|
|
|
11447
11622
|
meta.className = "dynamodb-table-info-meta";
|
|
11448
11623
|
meta.textContent = [
|
|
11449
11624
|
currentTableInfo.TableStatus,
|
|
11450
|
-
currentTableInfo.ItemCount !== undefined ? `${currentTableInfo.ItemCount.toLocaleString()} items` : undefined
|
|
11625
|
+
currentTableInfo.ItemCount !== undefined ? `${currentTableInfo.ItemCount.toLocaleString()} items` : undefined,
|
|
11626
|
+
currentTableInfo.TableSizeBytes !== undefined ? formatBytes(currentTableInfo.TableSizeBytes) : undefined,
|
|
11627
|
+
currentTableInfo.BillingModeSummary?.BillingMode
|
|
11451
11628
|
].filter(Boolean).join(" / ");
|
|
11452
|
-
|
|
11453
|
-
const
|
|
11454
|
-
|
|
11455
|
-
|
|
11456
|
-
|
|
11457
|
-
|
|
11458
|
-
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
|
|
11462
|
-
const
|
|
11463
|
-
|
|
11464
|
-
|
|
11465
|
-
|
|
11466
|
-
|
|
11629
|
+
structureBody.append(header, meta);
|
|
11630
|
+
const attributeTypeByName = new Map((currentTableInfo.AttributeDefinitions ?? []).map((a2) => [
|
|
11631
|
+
a2.AttributeName,
|
|
11632
|
+
a2.AttributeType
|
|
11633
|
+
]));
|
|
11634
|
+
const keyRoleByName = new Map((currentTableInfo.KeySchema ?? []).map((k) => [
|
|
11635
|
+
k.AttributeName,
|
|
11636
|
+
k.KeyType
|
|
11637
|
+
]));
|
|
11638
|
+
const attrNames = [...attributeTypeByName.keys()];
|
|
11639
|
+
const inferredTypesByName = new Map;
|
|
11640
|
+
for (const item of itemsByKeyToken.values()) {
|
|
11641
|
+
for (const [attrName, av] of Object.entries(item)) {
|
|
11642
|
+
if (attributeTypeByName.has(attrName))
|
|
11643
|
+
continue;
|
|
11644
|
+
const set2 = inferredTypesByName.get(attrName) ?? new Set;
|
|
11645
|
+
set2.add(attributeValueTag(av));
|
|
11646
|
+
inferredTypesByName.set(attrName, set2);
|
|
11647
|
+
}
|
|
11648
|
+
}
|
|
11649
|
+
const inferredNames = [...inferredTypesByName.keys()].sort();
|
|
11650
|
+
const rows = [
|
|
11651
|
+
...attrNames.map((name) => [
|
|
11652
|
+
name,
|
|
11653
|
+
attributeTypeByName.get(name) ?? "",
|
|
11654
|
+
keyRoleByName.get(name) ?? ""
|
|
11655
|
+
]),
|
|
11656
|
+
...inferredNames.map((name) => [
|
|
11657
|
+
name,
|
|
11658
|
+
[...inferredTypesByName.get(name) ?? []].join(", "),
|
|
11659
|
+
""
|
|
11660
|
+
])
|
|
11661
|
+
];
|
|
11662
|
+
structureBody.appendChild(createDetailTable([t2.attributeHeader, t2.typeHeader, t2.keyRoleHeader], rows, t2.noAttributes));
|
|
11663
|
+
const note = document.createElement("div");
|
|
11664
|
+
note.className = "dynamodb-attr-note";
|
|
11665
|
+
note.textContent = inferredNames.length === 0 ? t2.keySchemaOnlyHint : t2.inferredAttributesNote(itemsByKeyToken.size);
|
|
11666
|
+
structureBody.appendChild(note);
|
|
11667
|
+
renderSecondaryIndexes(t2.globalSecondaryIndexes, currentTableInfo.GlobalSecondaryIndexes);
|
|
11668
|
+
renderSecondaryIndexes(t2.localSecondaryIndexes, currentTableInfo.LocalSecondaryIndexes);
|
|
11467
11669
|
}
|
|
11468
11670
|
function renderItemDetail(item) {
|
|
11469
|
-
|
|
11671
|
+
lastRenderedItem = item;
|
|
11672
|
+
itemBody.innerHTML = "";
|
|
11470
11673
|
const header = document.createElement("div");
|
|
11471
11674
|
header.className = "dynamodb-item-detail-header";
|
|
11472
11675
|
const title = document.createElement("span");
|
|
@@ -11489,7 +11692,7 @@ ${frontmatter.yaml}
|
|
|
11489
11692
|
}
|
|
11490
11693
|
});
|
|
11491
11694
|
header.append(title, copyBtn, copyStatus);
|
|
11492
|
-
|
|
11695
|
+
itemBody.appendChild(header);
|
|
11493
11696
|
const pre = document.createElement("pre");
|
|
11494
11697
|
pre.className = "dynamodb-item-source";
|
|
11495
11698
|
try {
|
|
@@ -11497,12 +11700,13 @@ ${frontmatter.yaml}
|
|
|
11497
11700
|
} catch {
|
|
11498
11701
|
pre.textContent = String(item);
|
|
11499
11702
|
}
|
|
11500
|
-
|
|
11703
|
+
itemBody.appendChild(pre);
|
|
11501
11704
|
}
|
|
11502
11705
|
function selectItem(item, token) {
|
|
11503
11706
|
currentItemKeyToken = token ?? itemKeyToken(extractItemKey(item, currentTableInfo?.KeySchema));
|
|
11504
11707
|
highlightActiveItem(currentItemKeyToken);
|
|
11505
11708
|
renderItemDetail(item);
|
|
11709
|
+
setDetailTab("item");
|
|
11506
11710
|
notifySelectionChange();
|
|
11507
11711
|
}
|
|
11508
11712
|
async function loadItems(append) {
|
|
@@ -11538,8 +11742,6 @@ ${frontmatter.yaml}
|
|
|
11538
11742
|
cumulativeShownCount = 0;
|
|
11539
11743
|
cumulativeScannedCount = 0;
|
|
11540
11744
|
setPaneStatus(itemList, "Loading items...");
|
|
11541
|
-
if (!currentTableInfo)
|
|
11542
|
-
setPaneEmpty(detailPane, text3().dynamodb.selectItem);
|
|
11543
11745
|
}
|
|
11544
11746
|
try {
|
|
11545
11747
|
const params = new URLSearchParams({
|
|
@@ -11581,6 +11783,8 @@ ${frontmatter.yaml}
|
|
|
11581
11783
|
} else {
|
|
11582
11784
|
appendItems(data.items);
|
|
11583
11785
|
}
|
|
11786
|
+
if (currentTableInfo)
|
|
11787
|
+
renderTableStructure();
|
|
11584
11788
|
currentNextToken = data.lastEvaluatedKey;
|
|
11585
11789
|
moreBtn.hidden = !data.lastEvaluatedKey;
|
|
11586
11790
|
cumulativeShownCount += data.items.length;
|
|
@@ -11602,21 +11806,29 @@ ${frontmatter.yaml}
|
|
|
11602
11806
|
return;
|
|
11603
11807
|
const slot = tableInfoGuard.start();
|
|
11604
11808
|
const requestDbId = currentDbId;
|
|
11809
|
+
const isStaleRequest = () => disposed || slot.isStale() || requestDbId !== currentDbId || currentTable !== table2;
|
|
11605
11810
|
try {
|
|
11606
11811
|
const params = new URLSearchParams({ db: requestDbId, table: table2 });
|
|
11607
11812
|
const res = await trackLoad(fetch(`/_db/dynamodb/table?${params}`, { signal: slot.signal }));
|
|
11608
|
-
if (
|
|
11813
|
+
if (isStaleRequest())
|
|
11609
11814
|
return;
|
|
11610
|
-
if (!res.ok)
|
|
11815
|
+
if (!res.ok) {
|
|
11816
|
+
const errText = await res.text();
|
|
11817
|
+
setPaneStatus(structureBody, `Error: ${errText || res.statusText}`, {
|
|
11818
|
+
error: true
|
|
11819
|
+
});
|
|
11611
11820
|
return;
|
|
11821
|
+
}
|
|
11612
11822
|
const data = await res.json();
|
|
11613
|
-
if (
|
|
11823
|
+
if (isStaleRequest())
|
|
11614
11824
|
return;
|
|
11615
|
-
}
|
|
11616
11825
|
currentTableInfo = data.table;
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11826
|
+
renderTableStructure();
|
|
11827
|
+
} catch (err) {
|
|
11828
|
+
if (isStaleRequest())
|
|
11829
|
+
return;
|
|
11830
|
+
setPaneStatus(structureBody, `Error: ${err instanceof Error ? err.message : String(err)}`, { error: true });
|
|
11831
|
+
} finally {
|
|
11620
11832
|
slot.finish();
|
|
11621
11833
|
}
|
|
11622
11834
|
}
|
|
@@ -11656,7 +11868,14 @@ ${frontmatter.yaml}
|
|
|
11656
11868
|
currentNextToken = undefined;
|
|
11657
11869
|
highlightActiveTable(name);
|
|
11658
11870
|
notifySelectionChange();
|
|
11659
|
-
|
|
11871
|
+
lastRenderedItem = null;
|
|
11872
|
+
itemsByKeyToken.clear();
|
|
11873
|
+
itemRowsByKeyToken.clear();
|
|
11874
|
+
structureBody.innerHTML = "";
|
|
11875
|
+
setPaneStatus(structureBody, "Loading table...");
|
|
11876
|
+
itemBody.innerHTML = "";
|
|
11877
|
+
setPaneEmpty(itemBody, text3().dynamodb.selectItem);
|
|
11878
|
+
setDetailTab("structure");
|
|
11660
11879
|
await fetchTableInfo(name);
|
|
11661
11880
|
await loadItems(false);
|
|
11662
11881
|
}
|
|
@@ -11773,7 +11992,12 @@ ${frontmatter.yaml}
|
|
|
11773
11992
|
activeItemRow = null;
|
|
11774
11993
|
moreBtn.hidden = true;
|
|
11775
11994
|
tableMoreBtn.hidden = true;
|
|
11776
|
-
|
|
11995
|
+
lastRenderedItem = null;
|
|
11996
|
+
structureBody.innerHTML = "";
|
|
11997
|
+
setPaneEmpty(structureBody, text3().dynamodb.selectTable);
|
|
11998
|
+
itemBody.innerHTML = "";
|
|
11999
|
+
setPaneEmpty(itemBody, text3().dynamodb.selectItem);
|
|
12000
|
+
setDetailTab("structure");
|
|
11777
12001
|
setPaneStatus(tableList, "Loading tables...");
|
|
11778
12002
|
try {
|
|
11779
12003
|
const res = await trackLoad(fetch(`/_db/dynamodb/tables?db=${encodeURIComponent(dbId)}`, {
|
|
@@ -11809,6 +12033,8 @@ ${frontmatter.yaml}
|
|
|
11809
12033
|
await selectItemByKey(key);
|
|
11810
12034
|
} catch {}
|
|
11811
12035
|
}
|
|
12036
|
+
if (initial?.detailTab)
|
|
12037
|
+
setDetailTab(initial.detailTab);
|
|
11812
12038
|
} finally {
|
|
11813
12039
|
suppressNotify = false;
|
|
11814
12040
|
}
|
|
@@ -11854,7 +12080,12 @@ ${frontmatter.yaml}
|
|
|
11854
12080
|
itemRowsByKeyToken.clear();
|
|
11855
12081
|
activeItemRow = null;
|
|
11856
12082
|
moreBtn.hidden = true;
|
|
11857
|
-
|
|
12083
|
+
lastRenderedItem = null;
|
|
12084
|
+
structureBody.innerHTML = "";
|
|
12085
|
+
setPaneEmpty(structureBody, text3().dynamodb.selectTable);
|
|
12086
|
+
itemBody.innerHTML = "";
|
|
12087
|
+
setPaneEmpty(itemBody, text3().dynamodb.selectItem);
|
|
12088
|
+
setDetailTab("structure");
|
|
11858
12089
|
}
|
|
11859
12090
|
setMode("scan");
|
|
11860
12091
|
function getSelection() {
|
|
@@ -11865,7 +12096,8 @@ ${frontmatter.yaml}
|
|
|
11865
12096
|
filterExpression: filterInput.value.trim() || undefined,
|
|
11866
12097
|
expressionAttributeValues: attributeValuesInput.value.trim() || undefined,
|
|
11867
12098
|
scanIndexForward: currentMode === "query" ? currentScanIndexForward : undefined,
|
|
11868
|
-
itemKey: currentItemKeyToken ?? undefined
|
|
12099
|
+
itemKey: currentItemKeyToken ?? undefined,
|
|
12100
|
+
detailTab: detailTabs.getActive()
|
|
11869
12101
|
};
|
|
11870
12102
|
}
|
|
11871
12103
|
function dispose() {
|
|
@@ -11883,12 +12115,18 @@ ${frontmatter.yaml}
|
|
|
11883
12115
|
runBtn.textContent = t2.dynamodb.runQuery;
|
|
11884
12116
|
tableMoreBtn.textContent = t2.common.loadMore;
|
|
11885
12117
|
moreBtn.textContent = t2.common.loadMore;
|
|
11886
|
-
|
|
11887
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
12118
|
+
detailTabs.setLabels({
|
|
12119
|
+
structure: t2.dynamodb.structureTab,
|
|
12120
|
+
item: t2.dynamodb.itemTab
|
|
12121
|
+
});
|
|
12122
|
+
if (currentTableInfo)
|
|
12123
|
+
renderTableStructure();
|
|
12124
|
+
else
|
|
12125
|
+
setPaneEmpty(structureBody, t2.dynamodb.selectTable);
|
|
12126
|
+
if (lastRenderedItem)
|
|
12127
|
+
renderItemDetail(lastRenderedItem);
|
|
12128
|
+
else
|
|
12129
|
+
setPaneEmpty(itemBody, t2.dynamodb.selectItem);
|
|
11892
12130
|
}
|
|
11893
12131
|
return {
|
|
11894
12132
|
el: container,
|
|
@@ -11951,25 +12189,15 @@ ${frontmatter.yaml}
|
|
|
11951
12189
|
docMoreBtn.hidden = true;
|
|
11952
12190
|
docListPane.appendChild(docMoreBtn);
|
|
11953
12191
|
const detailPane = document.createElement("div");
|
|
11954
|
-
detailPane.className = "
|
|
11955
|
-
const detailTabs =
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
const tabDoc = document.createElement("button");
|
|
11962
|
-
tabDoc.type = "button";
|
|
11963
|
-
tabDoc.className = "es-detail-tab";
|
|
11964
|
-
tabDoc.textContent = text3().es.doc;
|
|
11965
|
-
detailTabs.append(tabMapping, tabDoc);
|
|
11966
|
-
detailPane.appendChild(detailTabs);
|
|
11967
|
-
const mappingBody = document.createElement("div");
|
|
11968
|
-
mappingBody.className = "es-mapping-body";
|
|
12192
|
+
detailPane.className = "db-detail-pane";
|
|
12193
|
+
const detailTabs = createDetailTabs([
|
|
12194
|
+
{ id: "mapping", label: text3().es.mapping },
|
|
12195
|
+
{ id: "doc", label: text3().es.doc }
|
|
12196
|
+
], "mapping", () => {});
|
|
12197
|
+
detailPane.appendChild(detailTabs.tabsEl);
|
|
12198
|
+
const mappingBody = detailTabs.bodies.mapping;
|
|
11969
12199
|
setPaneEmpty(mappingBody, text3().es.selectIndex);
|
|
11970
|
-
const docBody =
|
|
11971
|
-
docBody.className = "es-doc-body";
|
|
11972
|
-
docBody.hidden = true;
|
|
12200
|
+
const docBody = detailTabs.bodies.doc;
|
|
11973
12201
|
setPaneEmpty(docBody, text3().es.selectDoc);
|
|
11974
12202
|
detailPane.append(mappingBody, docBody);
|
|
11975
12203
|
container.append(docListPane, detailPane);
|
|
@@ -12094,13 +12322,8 @@ ${frontmatter.yaml}
|
|
|
12094
12322
|
}
|
|
12095
12323
|
function setDetailTab(tab) {
|
|
12096
12324
|
detailTab = tab;
|
|
12097
|
-
|
|
12098
|
-
tabDoc.classList.toggle("active", tab === "doc");
|
|
12099
|
-
mappingBody.hidden = tab !== "mapping";
|
|
12100
|
-
docBody.hidden = tab !== "doc";
|
|
12325
|
+
detailTabs.setActive(tab);
|
|
12101
12326
|
}
|
|
12102
|
-
tabMapping.addEventListener("click", () => setDetailTab("mapping"));
|
|
12103
|
-
tabDoc.addEventListener("click", () => setDetailTab("doc"));
|
|
12104
12327
|
function renderMapping(resp) {
|
|
12105
12328
|
lastMapping = resp;
|
|
12106
12329
|
mappingBody.innerHTML = "";
|
|
@@ -12108,43 +12331,13 @@ ${frontmatter.yaml}
|
|
|
12108
12331
|
header.className = "es-mapping-header";
|
|
12109
12332
|
header.textContent = resp.mapping.index;
|
|
12110
12333
|
mappingBody.appendChild(header);
|
|
12111
|
-
const table2 = document.createElement("table");
|
|
12112
|
-
table2.className = "es-mapping-table";
|
|
12113
|
-
const thead = document.createElement("thead");
|
|
12114
|
-
const headRow = document.createElement("tr");
|
|
12115
|
-
for (const label of [text3().es.fieldHeader, text3().es.typeHeader]) {
|
|
12116
|
-
const th = document.createElement("th");
|
|
12117
|
-
th.textContent = label;
|
|
12118
|
-
headRow.appendChild(th);
|
|
12119
|
-
}
|
|
12120
|
-
thead.appendChild(headRow);
|
|
12121
|
-
table2.appendChild(thead);
|
|
12122
|
-
const tbody = document.createElement("tbody");
|
|
12123
12334
|
const props = resp.mapping.properties;
|
|
12124
12335
|
const keys = Object.keys(props).sort();
|
|
12125
|
-
|
|
12126
|
-
const row = document.createElement("tr");
|
|
12127
|
-
const td = document.createElement("td");
|
|
12128
|
-
td.colSpan = 2;
|
|
12129
|
-
td.className = "es-value-empty";
|
|
12130
|
-
td.textContent = text3().es.noMappedFields;
|
|
12131
|
-
row.appendChild(td);
|
|
12132
|
-
tbody.appendChild(row);
|
|
12133
|
-
}
|
|
12134
|
-
for (const key of keys) {
|
|
12135
|
-
const row = document.createElement("tr");
|
|
12136
|
-
const fieldTd = document.createElement("td");
|
|
12137
|
-
fieldTd.className = "es-mapping-field";
|
|
12138
|
-
fieldTd.textContent = key;
|
|
12139
|
-
const typeTd = document.createElement("td");
|
|
12140
|
-
typeTd.className = "es-mapping-type";
|
|
12336
|
+
const rows = keys.map((key) => {
|
|
12141
12337
|
const p2 = props[key];
|
|
12142
|
-
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
}
|
|
12146
|
-
table2.appendChild(tbody);
|
|
12147
|
-
mappingBody.appendChild(table2);
|
|
12338
|
+
return [key, p2.type ?? (p2.properties ? "object" : "(unknown)")];
|
|
12339
|
+
});
|
|
12340
|
+
mappingBody.appendChild(createDetailTable([text3().es.fieldHeader, text3().es.typeHeader], rows, text3().es.noMappedFields));
|
|
12148
12341
|
}
|
|
12149
12342
|
function mkBtn(label, cls) {
|
|
12150
12343
|
const b2 = document.createElement("button");
|
|
@@ -12649,11 +12842,10 @@ ${frontmatter.yaml}
|
|
|
12649
12842
|
searchInput.placeholder = t2.es.queryPlaceholder;
|
|
12650
12843
|
searchBtn.textContent = t2.common.search;
|
|
12651
12844
|
docMoreBtn.textContent = t2.common.loadMore;
|
|
12652
|
-
|
|
12653
|
-
tabDoc.textContent = t2.es.doc;
|
|
12845
|
+
detailTabs.setLabels({ mapping: t2.es.mapping, doc: t2.es.doc });
|
|
12654
12846
|
if (!currentIndex) {
|
|
12655
12847
|
setPaneEmpty(mappingBody, t2.es.selectIndex);
|
|
12656
|
-
} else if (lastMapping && mappingBody.querySelector(".
|
|
12848
|
+
} else if (lastMapping && mappingBody.querySelector(".db-detail-table")) {
|
|
12657
12849
|
renderMapping(lastMapping);
|
|
12658
12850
|
}
|
|
12659
12851
|
if (!activeDocRow) {
|
|
@@ -21430,11 +21622,12 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21430
21622
|
dbSelect.value = target;
|
|
21431
21623
|
currentDbInfo = files.find((f2) => f2.id === target) || null;
|
|
21432
21624
|
syncConnectionActions();
|
|
21625
|
+
const forceReload = options.forceReload ?? false;
|
|
21433
21626
|
const explorerInitial = {
|
|
21434
|
-
redis: pendingRedisInitial,
|
|
21435
|
-
es: pendingEsInitial,
|
|
21436
|
-
s3: pendingS3Initial,
|
|
21437
|
-
dynamodb: pendingDynamodbInitial
|
|
21627
|
+
redis: pendingRedisInitial ?? (forceReload && currentDbInfo?.kind === "redis" ? redisExplorer.getSelection() : undefined),
|
|
21628
|
+
es: pendingEsInitial ?? (forceReload && currentDbInfo?.kind === "elasticsearch" ? esExplorer.getSelection() : undefined),
|
|
21629
|
+
s3: pendingS3Initial ?? (forceReload && currentDbInfo?.kind === "s3" ? s3Explorer.getSelection() : undefined),
|
|
21630
|
+
dynamodb: pendingDynamodbInitial ?? (forceReload && currentDbInfo?.kind === "dynamodb" ? dynamodbExplorer.getSelection() : undefined)
|
|
21438
21631
|
};
|
|
21439
21632
|
pendingRedisInitial = undefined;
|
|
21440
21633
|
pendingEsInitial = undefined;
|
|
@@ -21567,7 +21760,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
21567
21760
|
}
|
|
21568
21761
|
} else if (currentDbInfo?.kind === "dynamodb") {
|
|
21569
21762
|
const sel = dynamodbExplorer.getSelection();
|
|
21570
|
-
if (sel.table !== undefined || sel.mode !== "scan" || sel.keyConditionExpression !== undefined || sel.filterExpression !== undefined || sel.expressionAttributeValues !== undefined || sel.itemKey !== undefined) {
|
|
21763
|
+
if (sel.table !== undefined || sel.mode !== "scan" || sel.keyConditionExpression !== undefined || sel.filterExpression !== undefined || sel.expressionAttributeValues !== undefined || sel.itemKey !== undefined || sel.detailTab === "item") {
|
|
21571
21764
|
state.dynamodb = sel;
|
|
21572
21765
|
}
|
|
21573
21766
|
}
|
|
@@ -22632,7 +22825,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
22632
22825
|
if (!mounted || activeTabId !== id)
|
|
22633
22826
|
return;
|
|
22634
22827
|
const state = entry.pane.getState();
|
|
22635
|
-
await entry.pane.enter(state.dbId ?? undefined, state.schema ?? undefined, state.table ?? undefined, state.view, { autoSelectFirst: state.dbId !== null });
|
|
22828
|
+
await entry.pane.enter(state.dbId ?? undefined, state.schema ?? undefined, state.table ?? undefined, state.view, { autoSelectFirst: state.dbId !== null, forceReload: true });
|
|
22636
22829
|
if (!mounted || activeTabId !== id)
|
|
22637
22830
|
return;
|
|
22638
22831
|
refreshChipLabel(id);
|
|
@@ -25862,6 +26055,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
|
|
|
25862
26055
|
{
|
|
25863
26056
|
kind: "paragraph",
|
|
25864
26057
|
text: "In large repositories the sidebar loads folder children on demand. Folders you open are remembered and automatically re-expanded after a reload."
|
|
26058
|
+
},
|
|
26059
|
+
{
|
|
26060
|
+
kind: "paragraph",
|
|
26061
|
+
text: 'Symlinks show a distinct icon and a "→ target" label so they are never mistaken for a regular file or folder, and clicking one navigates to its resolved target. A broken symlink is visually flagged and disabled. Files with pending git changes (new, modified, renamed, deleted) show a status badge in the tree instead of the regular type icon.'
|
|
25865
26062
|
}
|
|
25866
26063
|
]
|
|
25867
26064
|
},
|
|
@@ -26128,7 +26325,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
26128
26325
|
],
|
|
26129
26326
|
[
|
|
26130
26327
|
"DynamoDB / LocalStack",
|
|
26131
|
-
"Detected when DynamoDB is enabled on a LocalStack compose service. List tables,
|
|
26328
|
+
"Detected when DynamoDB is enabled on a LocalStack compose service. List tables, browse a Structure tab (key schema, GSI/LSI, and non-key attribute types inferred from loaded items), scan or query items, follow pagination tokens, and open item details with a copyable key. Browsing is read-only."
|
|
26132
26329
|
],
|
|
26133
26330
|
[
|
|
26134
26331
|
"S3 / MinIO / LocalStack",
|
|
@@ -26529,6 +26726,10 @@ code-viewer query agent-help`
|
|
|
26529
26726
|
{
|
|
26530
26727
|
kind: "paragraph",
|
|
26531
26728
|
text: "大きいリポジトリではサイドバーがフォルダの中身を必要に応じて読み込みます。開いたフォルダは記憶され、次回のリロード時に同じ状態で展開し直されます。"
|
|
26729
|
+
},
|
|
26730
|
+
{
|
|
26731
|
+
kind: "paragraph",
|
|
26732
|
+
text: "シンボリックリンクは専用アイコンと「→ リンク先」ラベルで表示されるため通常のファイル/フォルダと区別でき、クリックするとリンク先に遷移します。リンク切れのシンボリックリンクは無効化されたことが分かる表示になります。未コミットの git 変更(新規・変更・リネーム・削除)があるファイルは、通常の種類アイコンの代わりにステータスバッジがツリーに表示されます。"
|
|
26532
26733
|
}
|
|
26533
26734
|
]
|
|
26534
26735
|
},
|
|
@@ -26795,7 +26996,7 @@ code-viewer annotate add-db --db app.db --tab query \\
|
|
|
26795
26996
|
],
|
|
26796
26997
|
[
|
|
26797
26998
|
"DynamoDB / LocalStack",
|
|
26798
|
-
"LocalStack の compose サービスで DynamoDB
|
|
26999
|
+
"LocalStack の compose サービスで DynamoDB が有効な場合に検出。テーブル一覧、構造タブ(キースキーマ・GSI/LSI・読み込み済みアイテムから推測した非キー属性の型)、scan / query、継続トークンによるページング、コピー可能なキー付きのアイテム詳細を表示します。閲覧専用です。"
|
|
26799
27000
|
],
|
|
26800
27001
|
[
|
|
26801
27002
|
"S3 / MinIO / LocalStack",
|
|
@@ -30526,6 +30727,11 @@ code-viewer query agent-help`
|
|
|
30526
30727
|
node.children_omitted = true;
|
|
30527
30728
|
node.children_omitted_reason = f2.children_omitted_reason;
|
|
30528
30729
|
}
|
|
30730
|
+
if (f2.is_symlink) {
|
|
30731
|
+
node.is_symlink = true;
|
|
30732
|
+
node.symlink_target = f2.symlink_target;
|
|
30733
|
+
node.resolved_path = f2.resolved_path;
|
|
30734
|
+
}
|
|
30529
30735
|
continue;
|
|
30530
30736
|
}
|
|
30531
30737
|
node.files.push(f2);
|
|
@@ -30575,6 +30781,8 @@ code-viewer query agent-help`
|
|
|
30575
30781
|
li.dataset.childrenOmittedReason = dir.children_omitted_reason;
|
|
30576
30782
|
if (dir.explicit)
|
|
30577
30783
|
li.dataset.explicit = "true";
|
|
30784
|
+
if (dir.is_symlink)
|
|
30785
|
+
li.classList.add("symlink-row");
|
|
30578
30786
|
if (dir.children_omitted) {
|
|
30579
30787
|
li.classList.add("children-omitted");
|
|
30580
30788
|
li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
|
|
@@ -30608,6 +30816,9 @@ code-viewer query agent-help`
|
|
|
30608
30816
|
omitted.title = badge.title;
|
|
30609
30817
|
label.appendChild(omitted);
|
|
30610
30818
|
}
|
|
30819
|
+
const dirSymlinkLabel = symlinkTargetLabel(dir);
|
|
30820
|
+
if (dirSymlinkLabel)
|
|
30821
|
+
label.appendChild(dirSymlinkLabel);
|
|
30611
30822
|
li.appendChild(label);
|
|
30612
30823
|
li.appendChild(createOpenPathButton(dir.path, "directory", openDirectoryInOsTitle()));
|
|
30613
30824
|
const collapsed = STATE.collapsedDirs.has(dir.path);
|
|
@@ -30646,7 +30857,8 @@ code-viewer query agent-help`
|
|
|
30646
30857
|
display_path: dir.path,
|
|
30647
30858
|
type: "tree",
|
|
30648
30859
|
children_omitted: dir.children_omitted,
|
|
30649
|
-
children_omitted_reason: dir.children_omitted_reason
|
|
30860
|
+
children_omitted_reason: dir.children_omitted_reason,
|
|
30861
|
+
resolved_path: dir.resolved_path
|
|
30650
30862
|
});
|
|
30651
30863
|
scheduleMainSurfaceFocus();
|
|
30652
30864
|
});
|
|
@@ -30709,6 +30921,11 @@ code-viewer query agent-help`
|
|
|
30709
30921
|
node.children_omitted = true;
|
|
30710
30922
|
node.children_omitted_reason = entry.children_omitted_reason;
|
|
30711
30923
|
}
|
|
30924
|
+
if (entry.is_symlink) {
|
|
30925
|
+
node.is_symlink = true;
|
|
30926
|
+
node.symlink_target = entry.symlink_target;
|
|
30927
|
+
node.resolved_path = entry.resolved_path;
|
|
30928
|
+
}
|
|
30712
30929
|
return;
|
|
30713
30930
|
}
|
|
30714
30931
|
if (!node.files.some((file) => file.path === entry.path))
|
|
@@ -30744,7 +30961,12 @@ code-viewer query agent-help`
|
|
|
30744
30961
|
type: meta.ref === "worktree" && entry.type === "commit" && !entry.submodule ? "tree" : entry.type,
|
|
30745
30962
|
submodule: entry.submodule,
|
|
30746
30963
|
children_omitted: entry.children_omitted,
|
|
30747
|
-
children_omitted_reason: entry.children_omitted_reason
|
|
30964
|
+
children_omitted_reason: entry.children_omitted_reason,
|
|
30965
|
+
is_symlink: entry.is_symlink,
|
|
30966
|
+
symlink_target: entry.symlink_target,
|
|
30967
|
+
symlink_target_type: entry.symlink_target_type,
|
|
30968
|
+
resolved_path: entry.resolved_path,
|
|
30969
|
+
status: entry.status
|
|
30748
30970
|
}));
|
|
30749
30971
|
mergeSidebarTreeEntries(entries);
|
|
30750
30972
|
SIDEBAR_LAZY_LOADED_DIRS.add(dir.path);
|
|
@@ -30764,6 +30986,8 @@ code-viewer query agent-help`
|
|
|
30764
30986
|
li.dataset.childrenOmittedReason = dir.children_omitted_reason;
|
|
30765
30987
|
if (dir.explicit)
|
|
30766
30988
|
li.dataset.explicit = "true";
|
|
30989
|
+
if (dir.is_symlink)
|
|
30990
|
+
li.classList.add("symlink-row");
|
|
30767
30991
|
if (dir.children_omitted) {
|
|
30768
30992
|
li.classList.add("children-omitted");
|
|
30769
30993
|
li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
|
|
@@ -30797,6 +31021,9 @@ code-viewer query agent-help`
|
|
|
30797
31021
|
omitted.title = badge.title;
|
|
30798
31022
|
label.appendChild(omitted);
|
|
30799
31023
|
}
|
|
31024
|
+
const dirSymlinkLabel = symlinkTargetLabel(dir);
|
|
31025
|
+
if (dirSymlinkLabel)
|
|
31026
|
+
label.appendChild(dirSymlinkLabel);
|
|
30800
31027
|
li.appendChild(label);
|
|
30801
31028
|
li.appendChild(createOpenPathButton(dir.path, "directory", openDirectoryInOsTitle()));
|
|
30802
31029
|
const updateIcon = () => {
|
|
@@ -30884,16 +31111,38 @@ code-viewer query agent-help`
|
|
|
30884
31111
|
return tag;
|
|
30885
31112
|
}
|
|
30886
31113
|
function sidebarEntryIcon(f2) {
|
|
31114
|
+
if (f2.is_symlink)
|
|
31115
|
+
return iconSvg("octicon-link", LINK_16_PATH);
|
|
30887
31116
|
return f2.type === "commit" ? iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH) : fileEntryIcon();
|
|
30888
31117
|
}
|
|
31118
|
+
function symlinkTargetLabel(f2) {
|
|
31119
|
+
if (!f2.is_symlink)
|
|
31120
|
+
return null;
|
|
31121
|
+
const broken = f2.symlink_target_type === "missing";
|
|
31122
|
+
const label = document.createElement("span");
|
|
31123
|
+
label.className = broken ? "symlink-target broken" : "symlink-target";
|
|
31124
|
+
label.textContent = `→ ${f2.symlink_target || "?"}`;
|
|
31125
|
+
label.title = broken ? `Broken symlink → ${f2.symlink_target || ""}` : `Symlink → ${f2.symlink_target || ""}`;
|
|
31126
|
+
return label;
|
|
31127
|
+
}
|
|
30889
31128
|
function createTreeFileRow(f2, depth, onFileClick) {
|
|
30890
31129
|
const li = document.createElement("li");
|
|
30891
31130
|
li.className = "tree-file";
|
|
30892
31131
|
li.tabIndex = -1;
|
|
30893
31132
|
li.dataset.path = f2.path;
|
|
30894
31133
|
li.dataset.type = f2.type || "blob";
|
|
31134
|
+
const brokenSymlink = f2.is_symlink && f2.symlink_target_type === "missing";
|
|
31135
|
+
const deletedEntry = !!onFileClick && f2.status === "D";
|
|
31136
|
+
if (f2.is_symlink)
|
|
31137
|
+
li.classList.add("symlink-row");
|
|
30895
31138
|
if (f2.type === "commit") {
|
|
30896
31139
|
li.title = commitEntryBadge(f2.submodule).title;
|
|
31140
|
+
} else if (brokenSymlink) {
|
|
31141
|
+
li.classList.add("symlink-broken-row", "gdp-row-disabled");
|
|
31142
|
+
li.setAttribute("aria-disabled", "true");
|
|
31143
|
+
} else if (deletedEntry) {
|
|
31144
|
+
li.classList.add("gdp-row-disabled");
|
|
31145
|
+
li.setAttribute("aria-disabled", "true");
|
|
30897
31146
|
}
|
|
30898
31147
|
li.classList.toggle("viewed", !onFileClick && STATE.viewedFiles.has(f2.path));
|
|
30899
31148
|
li.classList.toggle("hidden-by-tests", STATE.hideTests && !isRepositorySidebarMode() && isTestPath(f2.path || ""));
|
|
@@ -30914,17 +31163,22 @@ code-viewer query agent-help`
|
|
|
30914
31163
|
name.textContent = f2.path.split("/").pop();
|
|
30915
31164
|
name.title = f2.path;
|
|
30916
31165
|
li.appendChild(name);
|
|
31166
|
+
const symlinkLabel = symlinkTargetLabel(f2);
|
|
31167
|
+
if (symlinkLabel)
|
|
31168
|
+
li.appendChild(symlinkLabel);
|
|
30917
31169
|
const kindTag = fileKindTag(f2);
|
|
30918
31170
|
if (kindTag)
|
|
30919
31171
|
li.appendChild(kindTag);
|
|
30920
31172
|
li.addEventListener("click", () => {
|
|
31173
|
+
if (brokenSymlink || deletedEntry)
|
|
31174
|
+
return;
|
|
30921
31175
|
if (onFileClick)
|
|
30922
31176
|
onFileClick(f2);
|
|
30923
31177
|
else
|
|
30924
31178
|
scrollToFile(f2.path);
|
|
30925
31179
|
scheduleMainSurfaceFocus();
|
|
30926
31180
|
});
|
|
30927
|
-
if (!onFileClick)
|
|
31181
|
+
if (!onFileClick && !brokenSymlink)
|
|
30928
31182
|
li.addEventListener("mouseenter", () => prefetchByPath(f2.path), {
|
|
30929
31183
|
passive: true
|
|
30930
31184
|
});
|
|
@@ -31829,7 +32083,8 @@ code-viewer query agent-help`
|
|
|
31829
32083
|
sortColumnLabels,
|
|
31830
32084
|
repositoryFallback,
|
|
31831
32085
|
repositoryRootFallback,
|
|
31832
|
-
commitEntryMeta
|
|
32086
|
+
commitEntryMeta,
|
|
32087
|
+
fileBadge
|
|
31833
32088
|
} = deps;
|
|
31834
32089
|
let REPO_SORT = {
|
|
31835
32090
|
key: "name",
|
|
@@ -31875,9 +32130,21 @@ code-viewer query agent-help`
|
|
|
31875
32130
|
function commitEntryIcon() {
|
|
31876
32131
|
return iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
|
|
31877
32132
|
}
|
|
32133
|
+
function symlinkEntryIcon() {
|
|
32134
|
+
return iconSvg("octicon-link", LINK_16_PATH);
|
|
32135
|
+
}
|
|
31878
32136
|
function isWorktreeRef(ref) {
|
|
31879
32137
|
return canTrashWorktreeRef(ref);
|
|
31880
32138
|
}
|
|
32139
|
+
function repoEntryTypeIcon(entry, browsable, nonBrowsableCommit) {
|
|
32140
|
+
const icon = document.createElement("span");
|
|
32141
|
+
icon.className = browsable ? "dir-icon" : nonBrowsableCommit ? "d2h-icon-wrapper gdp-repo-row-gitlink-icon" : "d2h-icon-wrapper";
|
|
32142
|
+
if (browsable)
|
|
32143
|
+
setFolderIcon(icon, true);
|
|
32144
|
+
else
|
|
32145
|
+
icon.innerHTML = entry.is_symlink ? symlinkEntryIcon() : entry.type === "commit" ? commitEntryIcon() : fileEntryIcon();
|
|
32146
|
+
return icon;
|
|
32147
|
+
}
|
|
31881
32148
|
function canBrowseRepoEntry(entry, ref) {
|
|
31882
32149
|
return entry.type === "tree" || entry.type === "commit" && isWorktreeRef(ref) && !entry.submodule;
|
|
31883
32150
|
}
|
|
@@ -32255,28 +32522,31 @@ code-viewer query agent-help`
|
|
|
32255
32522
|
sortedRepoEntries(meta.entries, meta.ref).forEach((entry) => {
|
|
32256
32523
|
const browsable = canBrowseRepoEntry(entry, meta.ref);
|
|
32257
32524
|
const nonBrowsableCommit = entry.type === "commit" && !browsable;
|
|
32525
|
+
const brokenSymlink = !!entry.is_symlink && entry.symlink_target_type === "missing";
|
|
32526
|
+
const deletedEntry = entry.status === "D";
|
|
32258
32527
|
const row = document.createElement("button");
|
|
32259
32528
|
row.type = "button";
|
|
32260
|
-
row.className = nonBrowsableCommit ? `gdp-repo-row ${entry.type} gdp-repo-row-gitlink` : `gdp-repo-row ${entry.type}`;
|
|
32261
|
-
|
|
32262
|
-
|
|
32263
|
-
|
|
32264
|
-
setFolderIcon(icon, true);
|
|
32265
|
-
else
|
|
32266
|
-
icon.innerHTML = entry.type === "commit" ? commitEntryIcon() : fileEntryIcon();
|
|
32529
|
+
row.className = nonBrowsableCommit ? `gdp-repo-row ${entry.type} gdp-repo-row-gitlink` : entry.is_symlink ? `gdp-repo-row ${entry.type} symlink-row${brokenSymlink ? " symlink-broken-row gdp-row-disabled" : ""}` : `gdp-repo-row ${entry.type}`;
|
|
32530
|
+
if (deletedEntry)
|
|
32531
|
+
row.classList.add("gdp-row-disabled");
|
|
32532
|
+
const icon = entry.status ? fileBadge(entry.status) : repoEntryTypeIcon(entry, browsable, nonBrowsableCommit);
|
|
32267
32533
|
const name = document.createElement("span");
|
|
32268
32534
|
name.className = "name";
|
|
32269
32535
|
name.textContent = entry.name;
|
|
32270
32536
|
if (nonBrowsableCommit) {
|
|
32271
32537
|
row.title = commitEntryMeta(entry.submodule).title;
|
|
32272
32538
|
row.setAttribute("aria-disabled", "true");
|
|
32539
|
+
} else if (brokenSymlink || deletedEntry) {
|
|
32540
|
+
row.setAttribute("aria-disabled", "true");
|
|
32273
32541
|
}
|
|
32274
32542
|
const metaBlock = createRepoEntryMeta(entry, browsable);
|
|
32275
32543
|
const size = createRepoEntrySize(entry);
|
|
32276
32544
|
row.append(icon, name, metaBlock, size);
|
|
32277
32545
|
row.addEventListener("click", () => {
|
|
32546
|
+
if (brokenSymlink || deletedEntry)
|
|
32547
|
+
return;
|
|
32278
32548
|
if (browsable) {
|
|
32279
|
-
setRoute(repoRoute(meta.ref, entry.path));
|
|
32549
|
+
setRoute(repoRoute(meta.ref, entry.resolved_path ?? entry.path));
|
|
32280
32550
|
loadRepo();
|
|
32281
32551
|
} else if (entry.type === "blob") {
|
|
32282
32552
|
setRoute({
|
|
@@ -32385,12 +32655,17 @@ code-viewer query agent-help`
|
|
|
32385
32655
|
type: canBrowseRepoEntry(entry, normalizedRef) ? "tree" : entry.type,
|
|
32386
32656
|
submodule: entry.submodule,
|
|
32387
32657
|
children_omitted: entry.children_omitted,
|
|
32388
|
-
children_omitted_reason: entry.children_omitted_reason
|
|
32658
|
+
children_omitted_reason: entry.children_omitted_reason,
|
|
32659
|
+
is_symlink: entry.is_symlink,
|
|
32660
|
+
symlink_target: entry.symlink_target,
|
|
32661
|
+
symlink_target_type: entry.symlink_target_type,
|
|
32662
|
+
resolved_path: entry.resolved_path,
|
|
32663
|
+
status: entry.status
|
|
32389
32664
|
}));
|
|
32390
32665
|
setRepoSidebarRef(normalizedRef);
|
|
32391
32666
|
renderSidebar(files, (file) => {
|
|
32392
32667
|
if (file.type === "tree") {
|
|
32393
|
-
setRoute(repoRoute(normalizedRef, file.path));
|
|
32668
|
+
setRoute(repoRoute(normalizedRef, file.resolved_path ?? file.path));
|
|
32394
32669
|
loadRepo();
|
|
32395
32670
|
return;
|
|
32396
32671
|
}
|
|
@@ -32456,6 +32731,15 @@ code-viewer query agent-help`
|
|
|
32456
32731
|
meta.title = badge.title;
|
|
32457
32732
|
return meta;
|
|
32458
32733
|
}
|
|
32734
|
+
if (entry.is_symlink) {
|
|
32735
|
+
const broken = entry.symlink_target_type === "missing";
|
|
32736
|
+
meta.classList.add("symlink-target");
|
|
32737
|
+
if (broken)
|
|
32738
|
+
meta.classList.add("broken");
|
|
32739
|
+
meta.textContent = `→ ${entry.symlink_target || "?"}`;
|
|
32740
|
+
meta.title = broken ? `Broken symlink → ${entry.symlink_target || ""}` : `Symlink → ${entry.symlink_target || ""}`;
|
|
32741
|
+
return meta;
|
|
32742
|
+
}
|
|
32459
32743
|
const updated = formatFileDate(entry.updated_at || entry.commit_updated_at);
|
|
32460
32744
|
const created = formatFileDate(entry.created_at);
|
|
32461
32745
|
if (browsable && updated) {
|
|
@@ -35884,7 +36168,8 @@ code-viewer query agent-help`
|
|
|
35884
36168
|
commitEntryMeta: (submodule) => {
|
|
35885
36169
|
const text3 = uiText().repo;
|
|
35886
36170
|
return submodule ? { label: text3.submoduleLabel, title: text3.submoduleTitle } : { label: text3.gitlinkLabel, title: text3.gitlinkTitle };
|
|
35887
|
-
}
|
|
36171
|
+
},
|
|
36172
|
+
fileBadge: (status) => DIFF_VIEW.fileBadge(status)
|
|
35888
36173
|
});
|
|
35889
36174
|
const {
|
|
35890
36175
|
loadRepo,
|
|
@@ -36172,9 +36457,9 @@ code-viewer query agent-help`
|
|
|
36172
36457
|
displaySource: "Applies to all projects in this browser.",
|
|
36173
36458
|
excludedDirectories: "Excluded directories",
|
|
36174
36459
|
omitDirs: "Skip these directory names while browsing and searching",
|
|
36175
|
-
omitDirsHelp: "Reads no contents inside these directories. Applies to the sidebar (Files), Ctrl+K (file search), Ctrl+G (grep), Datastores, and the file change watcher.",
|
|
36460
|
+
omitDirsHelp: "Reads no contents inside these directories. Applies to the sidebar (Files), Ctrl+K (file search), Ctrl+G (grep), Datastores, and the file change watcher. Supports gitignore-style wildcards (*, ?, [abc], [!abc]).",
|
|
36176
36461
|
excludeNames: "Hide these file or directory names completely",
|
|
36177
|
-
excludeNamesHelp: "Removes matching files or directories from the sidebar, search, and grep results entirely. Unlike Skip, the names themselves disappear from the UI.",
|
|
36462
|
+
excludeNamesHelp: "Removes matching files or directories from the sidebar, search, and grep results entirely. Unlike Skip, the names themselves disappear from the UI. Supports gitignore-style wildcards (*, ?, [abc], [!abc]).",
|
|
36178
36463
|
reset: "Restore defaults",
|
|
36179
36464
|
autosaveNote: "Changes save automatically.",
|
|
36180
36465
|
scopeSource: (project, source) => `Saved for project "${project}" in this browser. Source: ${source}. Used by the sidebar, Ctrl+K, Ctrl+G, Datastores, and the file change watcher. Restore defaults removes the browser override.`,
|
|
@@ -36462,9 +36747,9 @@ code-viewer query agent-help`
|
|
|
36462
36747
|
displaySource: "このブラウザのすべてのプロジェクトに適用されます。",
|
|
36463
36748
|
excludedDirectories: "除外ディレクトリ",
|
|
36464
36749
|
omitDirs: "閲覧と検索でスキップするディレクトリ名",
|
|
36465
|
-
omitDirsHelp: "これらのディレクトリの中身は読み込みません。サイドバー(Files)・Ctrl+K(ファイル検索)・Ctrl+G(grep)・Datastores・File change watcher の5機能すべてに適用されます。",
|
|
36750
|
+
omitDirsHelp: "これらのディレクトリの中身は読み込みません。サイドバー(Files)・Ctrl+K(ファイル検索)・Ctrl+G(grep)・Datastores・File change watcher の5機能すべてに適用されます。gitignore方式のワイルドカード(*, ?, [abc], [!abc])に対応しています。",
|
|
36466
36751
|
excludeNames: "完全に非表示にするファイル名またはディレクトリ名",
|
|
36467
|
-
excludeNamesHelp: "リスト中の名前に一致するファイル/ディレクトリを、サイドバー・検索結果・grep 結果から完全に消します。Skip と違い、名前自体が UI に出なくなります。",
|
|
36752
|
+
excludeNamesHelp: "リスト中の名前に一致するファイル/ディレクトリを、サイドバー・検索結果・grep 結果から完全に消します。Skip と違い、名前自体が UI に出なくなります。gitignore方式のワイルドカード(*, ?, [abc], [!abc])に対応しています。",
|
|
36468
36753
|
reset: "デフォルトに戻す",
|
|
36469
36754
|
autosaveNote: "変更は自動で保存されます。",
|
|
36470
36755
|
scopeSource: (project, source) => `このブラウザのプロジェクト "${project}" に保存されます。ソース: ${source}。サイドバー、Ctrl+K、Ctrl+G、Datastores、File change watcher で使われます。「デフォルトに戻す」でブラウザ側の上書きを削除します。`,
|