@youtyan/code-viewer 0.8.2 → 0.8.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youtyan/code-viewer",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "Local browser-based code and git diff viewer",
5
5
  "type": "module",
6
6
  "bin": {
package/web/app.js CHANGED
@@ -13336,6 +13336,19 @@ ${frontmatter.yaml}
13336
13336
  return btn;
13337
13337
  }
13338
13338
 
13339
+ // web-src/views/database/query-value.ts
13340
+ function formatQueryValue(value) {
13341
+ if (value === null)
13342
+ return "NULL";
13343
+ if (value instanceof Uint8Array)
13344
+ return `<blob ${value.byteLength} bytes>`;
13345
+ if (typeof value === "boolean")
13346
+ return value ? "true" : "false";
13347
+ if (typeof value === "object")
13348
+ return JSON.stringify(value);
13349
+ return String(value);
13350
+ }
13351
+
13339
13352
  // web-src/views/database/shiki-sql.ts
13340
13353
  function highlightSqlToInnerHtml(code2, highlighter) {
13341
13354
  if (!highlighter || !code2)
@@ -13517,7 +13530,7 @@ ${frontmatter.yaml}
13517
13530
  tr.appendChild(tdNum);
13518
13531
  for (const value of row) {
13519
13532
  const td = document.createElement("td");
13520
- td.textContent = formatValue(value);
13533
+ td.textContent = formatQueryValue(value);
13521
13534
  if (value === null)
13522
13535
  td.classList.add("null");
13523
13536
  tr.appendChild(td);
@@ -13677,17 +13690,6 @@ ${frontmatter.yaml}
13677
13690
  localize
13678
13691
  };
13679
13692
  }
13680
- function formatValue(value) {
13681
- if (value === null)
13682
- return "NULL";
13683
- if (value instanceof Uint8Array)
13684
- return `<blob ${value.byteLength} bytes>`;
13685
- if (typeof value === "boolean")
13686
- return value ? "true" : "false";
13687
- if (typeof value === "object")
13688
- return JSON.stringify(value);
13689
- return String(value);
13690
- }
13691
13693
 
13692
13694
  // web-src/views/database/query-history-view.ts
13693
13695
  function createQueryHistoryView(callbacks) {
@@ -13996,7 +13998,7 @@ ${frontmatter.yaml}
13996
13998
  tr.appendChild(tdNum);
13997
13999
  for (const value of row) {
13998
14000
  const td = document.createElement("td");
13999
- td.textContent = formatValue2(value);
14001
+ td.textContent = formatQueryValue(value);
14000
14002
  if (value === null)
14001
14003
  td.classList.add("null");
14002
14004
  tr.appendChild(td);
@@ -14131,17 +14133,6 @@ ${frontmatter.yaml}
14131
14133
  return iso;
14132
14134
  }
14133
14135
  }
14134
- function formatValue2(value) {
14135
- if (value === null)
14136
- return "NULL";
14137
- if (value instanceof Uint8Array)
14138
- return `<blob ${value.byteLength} bytes>`;
14139
- if (typeof value === "boolean")
14140
- return value ? "true" : "false";
14141
- if (typeof value === "object")
14142
- return JSON.stringify(value);
14143
- return String(value);
14144
- }
14145
14136
 
14146
14137
  // web-src/views/database/redis-explorer.ts
14147
14138
  function isBinaryItem(item) {
@@ -19042,7 +19033,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19042
19033
  }
19043
19034
  for (let c2 = 0;c2 < columnNames.length; c2++) {
19044
19035
  const original = rowData[c2];
19045
- const originalShown = original === null ? null : original instanceof Uint8Array ? formatValue3(original) : String(original);
19036
+ const originalShown = original === null ? null : original instanceof Uint8Array ? formatValue(original) : String(original);
19046
19037
  const hasOverride = pendingForRow?.edits.has(c2) ?? false;
19047
19038
  const shown = hasOverride ? pendingForRow?.edits.get(c2) ?? null : originalShown;
19048
19039
  const ro = !editableRow || columns[c2]?.primaryKey === true || original instanceof Uint8Array;
@@ -19118,7 +19109,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19118
19109
  cell.className = "db-grid-cell";
19119
19110
  cell.style.width = `${getColWidth(columnNames[c2])}px`;
19120
19111
  const val = rowData[c2];
19121
- cell.textContent = formatValue3(val);
19112
+ cell.textContent = formatValue(val);
19122
19113
  if (val === null)
19123
19114
  cell.classList.add("null");
19124
19115
  else if (val instanceof Uint8Array)
@@ -19715,7 +19706,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
19715
19706
  onEditModeChange
19716
19707
  };
19717
19708
  }
19718
- function formatValue3(value) {
19709
+ function formatValue(value) {
19719
19710
  if (value === null)
19720
19711
  return "NULL";
19721
19712
  if (value instanceof Uint8Array)
@@ -26055,6 +26046,10 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26055
26046
  {
26056
26047
  kind: "paragraph",
26057
26048
  text: "In large repositories the sidebar loads folder children on demand. Folders you open are remembered and automatically re-expanded after a reload."
26049
+ },
26050
+ {
26051
+ kind: "paragraph",
26052
+ 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.'
26058
26053
  }
26059
26054
  ]
26060
26055
  },
@@ -26722,6 +26717,10 @@ code-viewer query agent-help`
26722
26717
  {
26723
26718
  kind: "paragraph",
26724
26719
  text: "大きいリポジトリではサイドバーがフォルダの中身を必要に応じて読み込みます。開いたフォルダは記憶され、次回のリロード時に同じ状態で展開し直されます。"
26720
+ },
26721
+ {
26722
+ kind: "paragraph",
26723
+ text: "シンボリックリンクは専用アイコンと「→ リンク先」ラベルで表示されるため通常のファイル/フォルダと区別でき、クリックするとリンク先に遷移します。リンク切れのシンボリックリンクは無効化されたことが分かる表示になります。未コミットの git 変更(新規・変更・リネーム・削除)があるファイルは、通常の種類アイコンの代わりにステータスバッジがツリーに表示されます。"
26725
26724
  }
26726
26725
  ]
26727
26726
  },
@@ -30719,6 +30718,11 @@ code-viewer query agent-help`
30719
30718
  node.children_omitted = true;
30720
30719
  node.children_omitted_reason = f2.children_omitted_reason;
30721
30720
  }
30721
+ if (f2.is_symlink) {
30722
+ node.is_symlink = true;
30723
+ node.symlink_target = f2.symlink_target;
30724
+ node.resolved_path = f2.resolved_path;
30725
+ }
30722
30726
  continue;
30723
30727
  }
30724
30728
  node.files.push(f2);
@@ -30768,6 +30772,8 @@ code-viewer query agent-help`
30768
30772
  li.dataset.childrenOmittedReason = dir.children_omitted_reason;
30769
30773
  if (dir.explicit)
30770
30774
  li.dataset.explicit = "true";
30775
+ if (dir.is_symlink)
30776
+ li.classList.add("symlink-row");
30771
30777
  if (dir.children_omitted) {
30772
30778
  li.classList.add("children-omitted");
30773
30779
  li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
@@ -30801,6 +30807,9 @@ code-viewer query agent-help`
30801
30807
  omitted.title = badge.title;
30802
30808
  label.appendChild(omitted);
30803
30809
  }
30810
+ const dirSymlinkLabel = symlinkTargetLabel(dir);
30811
+ if (dirSymlinkLabel)
30812
+ label.appendChild(dirSymlinkLabel);
30804
30813
  li.appendChild(label);
30805
30814
  li.appendChild(createOpenPathButton(dir.path, "directory", openDirectoryInOsTitle()));
30806
30815
  const collapsed = STATE.collapsedDirs.has(dir.path);
@@ -30839,7 +30848,8 @@ code-viewer query agent-help`
30839
30848
  display_path: dir.path,
30840
30849
  type: "tree",
30841
30850
  children_omitted: dir.children_omitted,
30842
- children_omitted_reason: dir.children_omitted_reason
30851
+ children_omitted_reason: dir.children_omitted_reason,
30852
+ resolved_path: dir.resolved_path
30843
30853
  });
30844
30854
  scheduleMainSurfaceFocus();
30845
30855
  });
@@ -30902,6 +30912,11 @@ code-viewer query agent-help`
30902
30912
  node.children_omitted = true;
30903
30913
  node.children_omitted_reason = entry.children_omitted_reason;
30904
30914
  }
30915
+ if (entry.is_symlink) {
30916
+ node.is_symlink = true;
30917
+ node.symlink_target = entry.symlink_target;
30918
+ node.resolved_path = entry.resolved_path;
30919
+ }
30905
30920
  return;
30906
30921
  }
30907
30922
  if (!node.files.some((file) => file.path === entry.path))
@@ -30937,7 +30952,12 @@ code-viewer query agent-help`
30937
30952
  type: meta.ref === "worktree" && entry.type === "commit" && !entry.submodule ? "tree" : entry.type,
30938
30953
  submodule: entry.submodule,
30939
30954
  children_omitted: entry.children_omitted,
30940
- children_omitted_reason: entry.children_omitted_reason
30955
+ children_omitted_reason: entry.children_omitted_reason,
30956
+ is_symlink: entry.is_symlink,
30957
+ symlink_target: entry.symlink_target,
30958
+ symlink_target_type: entry.symlink_target_type,
30959
+ resolved_path: entry.resolved_path,
30960
+ status: entry.status
30941
30961
  }));
30942
30962
  mergeSidebarTreeEntries(entries);
30943
30963
  SIDEBAR_LAZY_LOADED_DIRS.add(dir.path);
@@ -30957,6 +30977,8 @@ code-viewer query agent-help`
30957
30977
  li.dataset.childrenOmittedReason = dir.children_omitted_reason;
30958
30978
  if (dir.explicit)
30959
30979
  li.dataset.explicit = "true";
30980
+ if (dir.is_symlink)
30981
+ li.classList.add("symlink-row");
30960
30982
  if (dir.children_omitted) {
30961
30983
  li.classList.add("children-omitted");
30962
30984
  li.classList.add(dir.children_omitted_reason === "heavy" ? "children-omitted-heavy" : "children-omitted-internal");
@@ -30990,6 +31012,9 @@ code-viewer query agent-help`
30990
31012
  omitted.title = badge.title;
30991
31013
  label.appendChild(omitted);
30992
31014
  }
31015
+ const dirSymlinkLabel = symlinkTargetLabel(dir);
31016
+ if (dirSymlinkLabel)
31017
+ label.appendChild(dirSymlinkLabel);
30993
31018
  li.appendChild(label);
30994
31019
  li.appendChild(createOpenPathButton(dir.path, "directory", openDirectoryInOsTitle()));
30995
31020
  const updateIcon = () => {
@@ -31077,16 +31102,38 @@ code-viewer query agent-help`
31077
31102
  return tag;
31078
31103
  }
31079
31104
  function sidebarEntryIcon(f2) {
31105
+ if (f2.is_symlink)
31106
+ return iconSvg("octicon-link", LINK_16_PATH);
31080
31107
  return f2.type === "commit" ? iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH) : fileEntryIcon();
31081
31108
  }
31109
+ function symlinkTargetLabel(f2) {
31110
+ if (!f2.is_symlink)
31111
+ return null;
31112
+ const broken = f2.symlink_target_type === "missing";
31113
+ const label = document.createElement("span");
31114
+ label.className = broken ? "symlink-target broken" : "symlink-target";
31115
+ label.textContent = `→ ${f2.symlink_target || "?"}`;
31116
+ label.title = broken ? `Broken symlink → ${f2.symlink_target || ""}` : `Symlink → ${f2.symlink_target || ""}`;
31117
+ return label;
31118
+ }
31082
31119
  function createTreeFileRow(f2, depth, onFileClick) {
31083
31120
  const li = document.createElement("li");
31084
31121
  li.className = "tree-file";
31085
31122
  li.tabIndex = -1;
31086
31123
  li.dataset.path = f2.path;
31087
31124
  li.dataset.type = f2.type || "blob";
31125
+ const brokenSymlink = f2.is_symlink && f2.symlink_target_type === "missing";
31126
+ const deletedEntry = !!onFileClick && f2.status === "D";
31127
+ if (f2.is_symlink)
31128
+ li.classList.add("symlink-row");
31088
31129
  if (f2.type === "commit") {
31089
31130
  li.title = commitEntryBadge(f2.submodule).title;
31131
+ } else if (brokenSymlink) {
31132
+ li.classList.add("symlink-broken-row", "gdp-row-disabled");
31133
+ li.setAttribute("aria-disabled", "true");
31134
+ } else if (deletedEntry) {
31135
+ li.classList.add("gdp-row-disabled");
31136
+ li.setAttribute("aria-disabled", "true");
31090
31137
  }
31091
31138
  li.classList.toggle("viewed", !onFileClick && STATE.viewedFiles.has(f2.path));
31092
31139
  li.classList.toggle("hidden-by-tests", STATE.hideTests && !isRepositorySidebarMode() && isTestPath(f2.path || ""));
@@ -31107,17 +31154,22 @@ code-viewer query agent-help`
31107
31154
  name.textContent = f2.path.split("/").pop();
31108
31155
  name.title = f2.path;
31109
31156
  li.appendChild(name);
31157
+ const symlinkLabel = symlinkTargetLabel(f2);
31158
+ if (symlinkLabel)
31159
+ li.appendChild(symlinkLabel);
31110
31160
  const kindTag = fileKindTag(f2);
31111
31161
  if (kindTag)
31112
31162
  li.appendChild(kindTag);
31113
31163
  li.addEventListener("click", () => {
31164
+ if (brokenSymlink || deletedEntry)
31165
+ return;
31114
31166
  if (onFileClick)
31115
31167
  onFileClick(f2);
31116
31168
  else
31117
31169
  scrollToFile(f2.path);
31118
31170
  scheduleMainSurfaceFocus();
31119
31171
  });
31120
- if (!onFileClick)
31172
+ if (!onFileClick && !brokenSymlink)
31121
31173
  li.addEventListener("mouseenter", () => prefetchByPath(f2.path), {
31122
31174
  passive: true
31123
31175
  });
@@ -32022,7 +32074,8 @@ code-viewer query agent-help`
32022
32074
  sortColumnLabels,
32023
32075
  repositoryFallback,
32024
32076
  repositoryRootFallback,
32025
- commitEntryMeta
32077
+ commitEntryMeta,
32078
+ fileBadge
32026
32079
  } = deps;
32027
32080
  let REPO_SORT = {
32028
32081
  key: "name",
@@ -32068,9 +32121,21 @@ code-viewer query agent-help`
32068
32121
  function commitEntryIcon() {
32069
32122
  return iconSvg("octicon-git-branch", GIT_BRANCH_16_PATH);
32070
32123
  }
32124
+ function symlinkEntryIcon() {
32125
+ return iconSvg("octicon-link", LINK_16_PATH);
32126
+ }
32071
32127
  function isWorktreeRef(ref) {
32072
32128
  return canTrashWorktreeRef(ref);
32073
32129
  }
32130
+ function repoEntryTypeIcon(entry, browsable, nonBrowsableCommit) {
32131
+ const icon = document.createElement("span");
32132
+ icon.className = browsable ? "dir-icon" : nonBrowsableCommit ? "d2h-icon-wrapper gdp-repo-row-gitlink-icon" : "d2h-icon-wrapper";
32133
+ if (browsable)
32134
+ setFolderIcon(icon, true);
32135
+ else
32136
+ icon.innerHTML = entry.is_symlink ? symlinkEntryIcon() : entry.type === "commit" ? commitEntryIcon() : fileEntryIcon();
32137
+ return icon;
32138
+ }
32074
32139
  function canBrowseRepoEntry(entry, ref) {
32075
32140
  return entry.type === "tree" || entry.type === "commit" && isWorktreeRef(ref) && !entry.submodule;
32076
32141
  }
@@ -32448,28 +32513,31 @@ code-viewer query agent-help`
32448
32513
  sortedRepoEntries(meta.entries, meta.ref).forEach((entry) => {
32449
32514
  const browsable = canBrowseRepoEntry(entry, meta.ref);
32450
32515
  const nonBrowsableCommit = entry.type === "commit" && !browsable;
32516
+ const brokenSymlink = !!entry.is_symlink && entry.symlink_target_type === "missing";
32517
+ const deletedEntry = entry.status === "D";
32451
32518
  const row = document.createElement("button");
32452
32519
  row.type = "button";
32453
- row.className = nonBrowsableCommit ? `gdp-repo-row ${entry.type} gdp-repo-row-gitlink` : `gdp-repo-row ${entry.type}`;
32454
- const icon = document.createElement("span");
32455
- icon.className = browsable ? "dir-icon" : nonBrowsableCommit ? "d2h-icon-wrapper gdp-repo-row-gitlink-icon" : "d2h-icon-wrapper";
32456
- if (browsable)
32457
- setFolderIcon(icon, true);
32458
- else
32459
- icon.innerHTML = entry.type === "commit" ? commitEntryIcon() : fileEntryIcon();
32520
+ 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}`;
32521
+ if (deletedEntry)
32522
+ row.classList.add("gdp-row-disabled");
32523
+ const icon = entry.status ? fileBadge(entry.status) : repoEntryTypeIcon(entry, browsable, nonBrowsableCommit);
32460
32524
  const name = document.createElement("span");
32461
32525
  name.className = "name";
32462
32526
  name.textContent = entry.name;
32463
32527
  if (nonBrowsableCommit) {
32464
32528
  row.title = commitEntryMeta(entry.submodule).title;
32465
32529
  row.setAttribute("aria-disabled", "true");
32530
+ } else if (brokenSymlink || deletedEntry) {
32531
+ row.setAttribute("aria-disabled", "true");
32466
32532
  }
32467
32533
  const metaBlock = createRepoEntryMeta(entry, browsable);
32468
32534
  const size = createRepoEntrySize(entry);
32469
32535
  row.append(icon, name, metaBlock, size);
32470
32536
  row.addEventListener("click", () => {
32537
+ if (brokenSymlink || deletedEntry)
32538
+ return;
32471
32539
  if (browsable) {
32472
- setRoute(repoRoute(meta.ref, entry.path));
32540
+ setRoute(repoRoute(meta.ref, entry.resolved_path ?? entry.path));
32473
32541
  loadRepo();
32474
32542
  } else if (entry.type === "blob") {
32475
32543
  setRoute({
@@ -32578,12 +32646,17 @@ code-viewer query agent-help`
32578
32646
  type: canBrowseRepoEntry(entry, normalizedRef) ? "tree" : entry.type,
32579
32647
  submodule: entry.submodule,
32580
32648
  children_omitted: entry.children_omitted,
32581
- children_omitted_reason: entry.children_omitted_reason
32649
+ children_omitted_reason: entry.children_omitted_reason,
32650
+ is_symlink: entry.is_symlink,
32651
+ symlink_target: entry.symlink_target,
32652
+ symlink_target_type: entry.symlink_target_type,
32653
+ resolved_path: entry.resolved_path,
32654
+ status: entry.status
32582
32655
  }));
32583
32656
  setRepoSidebarRef(normalizedRef);
32584
32657
  renderSidebar(files, (file) => {
32585
32658
  if (file.type === "tree") {
32586
- setRoute(repoRoute(normalizedRef, file.path));
32659
+ setRoute(repoRoute(normalizedRef, file.resolved_path ?? file.path));
32587
32660
  loadRepo();
32588
32661
  return;
32589
32662
  }
@@ -32649,6 +32722,15 @@ code-viewer query agent-help`
32649
32722
  meta.title = badge.title;
32650
32723
  return meta;
32651
32724
  }
32725
+ if (entry.is_symlink) {
32726
+ const broken = entry.symlink_target_type === "missing";
32727
+ meta.classList.add("symlink-target");
32728
+ if (broken)
32729
+ meta.classList.add("broken");
32730
+ meta.textContent = `→ ${entry.symlink_target || "?"}`;
32731
+ meta.title = broken ? `Broken symlink → ${entry.symlink_target || ""}` : `Symlink → ${entry.symlink_target || ""}`;
32732
+ return meta;
32733
+ }
32652
32734
  const updated = formatFileDate(entry.updated_at || entry.commit_updated_at);
32653
32735
  const created = formatFileDate(entry.created_at);
32654
32736
  if (browsable && updated) {
@@ -36077,7 +36159,8 @@ code-viewer query agent-help`
36077
36159
  commitEntryMeta: (submodule) => {
36078
36160
  const text3 = uiText().repo;
36079
36161
  return submodule ? { label: text3.submoduleLabel, title: text3.submoduleTitle } : { label: text3.gitlinkLabel, title: text3.gitlinkTitle };
36080
- }
36162
+ },
36163
+ fileBadge: (status) => DIFF_VIEW.fileBadge(status)
36081
36164
  });
36082
36165
  const {
36083
36166
  loadRepo,
package/web/style.css CHANGED
@@ -2646,6 +2646,45 @@ body.gdp-help-page #content {
2646
2646
  color: var(--accent-emph);
2647
2647
  }
2648
2648
 
2649
+ /* "→ target" sublabel next to a symlink name in the sidebar tree, and the
2650
+ same marker's meta-column form in the repo listing (.gdp-repo-row
2651
+ .meta.symlink-target below). Uses --done (violet) rather than the usual
2652
+ muted meta color so a symlink row reads as distinct at a glance, not
2653
+ just on hover/title. Broken links (target missing/outside the repo)
2654
+ switch to the danger color instead. */
2655
+ .symlink-target {
2656
+ overflow: hidden;
2657
+ text-overflow: ellipsis;
2658
+ white-space: nowrap;
2659
+ color: var(--done);
2660
+ font-weight: 500;
2661
+ font-size: 12px;
2662
+ margin-left: 6px;
2663
+ }
2664
+ .symlink-target.broken { color: var(--danger); }
2665
+ /* Recolor the row's own type icon (file icon, or the shared blue dir-icon)
2666
+ to match the target label, so the symlink signal isn't limited to the
2667
+ sublabel text. */
2668
+ #filelist.tree .tree-file.symlink-row .d2h-icon-wrapper,
2669
+ #filelist.tree .tree-dir.symlink-row .dir-icon,
2670
+ .gdp-repo-row.symlink-row .d2h-icon-wrapper,
2671
+ .gdp-repo-row.symlink-row .dir-icon {
2672
+ color: var(--done);
2673
+ }
2674
+ .tree-file.symlink-broken-row .d2h-icon-wrapper,
2675
+ .gdp-repo-row.symlink-broken-row .d2h-icon-wrapper {
2676
+ color: var(--danger);
2677
+ }
2678
+ /* Shared "disabled row" treatment: a broken symlink or a synthesized
2679
+ deleted-file row (see deletedTreeEntriesForPath in preview.ts) - both are
2680
+ badged but cannot actually be opened. */
2681
+ .tree-file.gdp-row-disabled,
2682
+ .tree-dir.gdp-row-disabled,
2683
+ .gdp-repo-row.gdp-row-disabled {
2684
+ cursor: default;
2685
+ opacity: 0.7;
2686
+ }
2687
+
2649
2688
  /* ===== Main content area ===== */
2650
2689
  #content {
2651
2690
  margin-left: var(--sidebar-w);
@@ -4595,6 +4634,13 @@ body.gdp-file-detail-page #empty {
4595
4634
  font-size: 11px;
4596
4635
  line-height: 1.4;
4597
4636
  }
4637
+ .gdp-repo-row .meta.symlink-target.broken { color: var(--danger); }
4638
+ /* Disabled row (broken symlink or synthesized deleted-file entry): opening
4639
+ it would just 404, so no hover affordance - same treatment as
4640
+ .gdp-repo-row-gitlink above. */
4641
+ .gdp-repo-row.gdp-row-disabled:hover {
4642
+ background: var(--bg);
4643
+ }
4598
4644
  .gdp-context-menu {
4599
4645
  position: fixed;
4600
4646
  z-index: 1000;