@youtyan/code-viewer 0.8.5 → 0.8.6

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.5",
3
+ "version": "0.8.6",
4
4
  "description": "Local browser-based code and git diff viewer",
5
5
  "type": "module",
6
6
  "bin": {
package/web/app.js CHANGED
@@ -9672,6 +9672,10 @@ ${frontmatter.yaml}
9672
9672
  }
9673
9673
 
9674
9674
  // web-src/views/file-shell.ts
9675
+ function isMediaPreviewOnlySource(path) {
9676
+ const kind = sourceDisplayKind(path);
9677
+ return kind === "image" || kind === "video" || kind === "audio" || kind === "pdf";
9678
+ }
9675
9679
  function isBlobOrBlameFileRoute(route) {
9676
9680
  return route.screen === "file" && (route.view === "blob" || route.view === "blame");
9677
9681
  }
@@ -9724,14 +9728,18 @@ ${frontmatter.yaml}
9724
9728
  }
9725
9729
  function appendFileViewTabs(tabs, deps, target, activeTab, options = {}) {
9726
9730
  const includeFileTabs = options.includeFileTabs !== false;
9727
- const showPreview = options.previewable ?? (isPreviewableSource(target.path) || activeTab === "preview");
9731
+ const mediaOnly = options.previewable === undefined && isMediaPreviewOnlySource(target.path);
9732
+ const showPreview = mediaOnly || (options.previewable ?? (isPreviewableSource(target.path) || activeTab === "preview"));
9728
9733
  let previewButton = null;
9729
9734
  if (showPreview) {
9730
- previewButton = createBlobSourceTabButton(deps, target, "preview", activeTab === "preview", options);
9735
+ previewButton = createBlobSourceTabButton(deps, target, "preview", activeTab === "preview" || mediaOnly && activeTab === "code", options);
9731
9736
  tabs.appendChild(previewButton);
9732
9737
  }
9733
- const codeButton = createBlobSourceTabButton(deps, target, "code", activeTab === "code", options);
9734
- tabs.appendChild(codeButton);
9738
+ let codeButton = null;
9739
+ if (!mediaOnly) {
9740
+ codeButton = createBlobSourceTabButton(deps, target, "code", activeTab === "code", options);
9741
+ tabs.appendChild(codeButton);
9742
+ }
9735
9743
  if (includeFileTabs) {
9736
9744
  tabs.appendChild(createFileViewTabButton(deps, target, "blame", "Blame", activeTab === "blame"));
9737
9745
  tabs.appendChild(createFileViewTabButton(deps, target, "history", "History", activeTab === "history"));
@@ -26044,7 +26052,7 @@ ${entry.message}` : entry.detail ?? entry.message ?? "";
26044
26052
  },
26045
26053
  {
26046
26054
  kind: "paragraph",
26047
- text: "A file detail page exposes four tabs — Preview, Code, Blame, History. Code is the default and ?preview=1 opts in to the Markdown / media preview. Blame and History have their own canonical URLs (view=blame, view=history) so deep links and the browser back/forward stay in sync, and they keep the Repository sidebar visible."
26055
+ text: "A file detail page exposes up to four tabs — Preview, Code, Blame, History. For text files Code is the default and ?preview=1 opts in to the Markdown / HTML preview; media files (images, video, audio, PDF) show a Preview tab only, with no Code tab. Blame and History have their own canonical URLs (view=blame, view=history) so deep links and the browser back/forward stay in sync, and they keep the Repository sidebar visible."
26048
26056
  },
26049
26057
  {
26050
26058
  kind: "paragraph",
@@ -26715,7 +26723,7 @@ code-viewer query agent-help`
26715
26723
  },
26716
26724
  {
26717
26725
  kind: "paragraph",
26718
- text: "ファイル詳細ページには Preview / Code / Blame / History 4 つのタブがあります。Code がデフォルトで、?preview=1 を付けると Markdown / メディアプレビューに切り替わります。Blame と History はそれぞれ専用 URL (view=blame, view=history) を持つため、ディープリンクとブラウザの戻る / 進むが同期し、いずれも Repository サイドバーは表示されたままです。"
26726
+ text: "ファイル詳細ページには最大 4 つのタブ (Preview / Code / Blame / History) があります。テキストファイルは Code がデフォルトで、?preview=1 を付けると Markdown / HTML プレビューに切り替わります。画像・動画・音声・PDF などのメディアファイルは Preview タブのみ表示され、Code タブは表示されません。Blame と History はそれぞれ専用 URL (view=blame, view=history) を持つため、ディープリンクとブラウザの戻る / 進むが同期し、いずれも Repository サイドバーは表示されたままです。"
26719
26727
  },
26720
26728
  {
26721
26729
  kind: "paragraph",
@@ -34083,6 +34091,8 @@ code-viewer query agent-help`
34083
34091
  });
34084
34092
  }
34085
34093
  function applyRenderedSourceTab(tab, codeButton, previewButton, codePane, previewPane, updateRoute = true) {
34094
+ if (!codeButton)
34095
+ return false;
34086
34096
  if (tab === "preview" && (!previewButton || !previewPane))
34087
34097
  return false;
34088
34098
  codeButton.classList.toggle("active", tab === "code");
@@ -34332,6 +34342,7 @@ code-viewer query agent-help`
34332
34342
  body.replaceWith(view);
34333
34343
  else
34334
34344
  card.appendChild(view);
34345
+ setSourceCardState(card, "error");
34335
34346
  }
34336
34347
  function renderSourceCancelled(card, target) {
34337
34348
  const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
@@ -34356,6 +34367,7 @@ code-viewer query agent-help`
34356
34367
  body.replaceWith(view);
34357
34368
  else
34358
34369
  card.appendChild(view);
34370
+ setSourceCardState(card, "cancelled");
34359
34371
  }
34360
34372
  function renderSourceUnsupported(card, target) {
34361
34373
  const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
@@ -34374,6 +34386,7 @@ code-viewer query agent-help`
34374
34386
  body.replaceWith(view);
34375
34387
  else
34376
34388
  card.appendChild(view);
34389
+ setSourceCardState(card, "done");
34377
34390
  }
34378
34391
  function renderSourceInternalPath(card, target, kind) {
34379
34392
  const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
@@ -34398,6 +34411,7 @@ code-viewer query agent-help`
34398
34411
  body.replaceWith(view);
34399
34412
  else
34400
34413
  card.appendChild(view);
34414
+ setSourceCardState(card, "done");
34401
34415
  }
34402
34416
  function renderHtmlPreview(target, html) {
34403
34417
  return renderHtmlPreviewFrame(`${target.path} preview`, html);
@@ -34511,7 +34525,7 @@ code-viewer query agent-help`
34511
34525
  previewButton2?.addEventListener("click", () => {
34512
34526
  applyRenderedSourceTab("preview", codeButton2, previewButton2, virtualCode, preview);
34513
34527
  });
34514
- codeButton2.addEventListener("click", () => {
34528
+ codeButton2?.addEventListener("click", () => {
34515
34529
  applyRenderedSourceTab("code", codeButton2, previewButton2, virtualCode, preview);
34516
34530
  });
34517
34531
  if (header)
@@ -34606,7 +34620,7 @@ code-viewer query agent-help`
34606
34620
  previewButton?.addEventListener("click", () => {
34607
34621
  applyRenderedSourceTab("preview", codeButton, previewButton, table2, preview);
34608
34622
  });
34609
- codeButton.addEventListener("click", () => {
34623
+ codeButton?.addEventListener("click", () => {
34610
34624
  applyRenderedSourceTab("code", codeButton, previewButton, table2, preview);
34611
34625
  });
34612
34626
  if (header)
@@ -34926,7 +34940,7 @@ code-viewer query agent-help`
34926
34940
  e2.preventDefault();
34927
34941
  const url = new URL(full.href, window.location.origin);
34928
34942
  setRoute(parseRoute(url.pathname, url.search, currentRange()), true);
34929
- renderStandaloneSource(target);
34943
+ renderStandaloneSource(target, { refresh: true });
34930
34944
  });
34931
34945
  actions.append(copy, full);
34932
34946
  info.append(badge, summary, actions);
@@ -35042,7 +35056,7 @@ code-viewer query agent-help`
35042
35056
  e2.preventDefault();
35043
35057
  const url = new URL(full.href, window.location.origin);
35044
35058
  setRoute(parseRoute(url.pathname, url.search, currentRange()), true);
35045
- renderStandaloneSource(target);
35059
+ renderStandaloneSource(target, { refresh: true });
35046
35060
  });
35047
35061
  actions.append(raw, full);
35048
35062
  info.append(badge, summary, actions);
@@ -35308,6 +35322,7 @@ code-viewer query agent-help`
35308
35322
  body.replaceWith(view);
35309
35323
  else
35310
35324
  card.appendChild(view);
35325
+ setSourceCardState(card, "done");
35311
35326
  }
35312
35327
  function _renderSourceBinary(card, target) {
35313
35328
  const body = card.querySelector(".gdp-file-detail-body, .d2h-files-diff, .d2h-file-diff, .gdp-media, .gdp-source-viewer");
@@ -35331,7 +35346,30 @@ code-viewer query agent-help`
35331
35346
  else
35332
35347
  card.appendChild(view);
35333
35348
  }
35334
- async function renderStandaloneSource(target) {
35349
+ function mountedStandaloneSourceCard(target) {
35350
+ const card = document.querySelector(".gdp-standalone-source");
35351
+ if (!card)
35352
+ return null;
35353
+ if (card.dataset.path !== target.path)
35354
+ return null;
35355
+ if (card.dataset.sourceRef !== (target.ref || "worktree"))
35356
+ return null;
35357
+ return card;
35358
+ }
35359
+ function setSourceCardState(card, state) {
35360
+ card.dataset.sourceState = state;
35361
+ }
35362
+ async function renderStandaloneSource(target, options = {}) {
35363
+ if (!options.refresh) {
35364
+ const mounted = mountedStandaloneSourceCard(target);
35365
+ const state = mounted?.dataset.sourceState;
35366
+ if (mounted && state === "done") {
35367
+ scrollStandaloneSourceLine(mounted, lineTargetStart(STATE.route.screen === "file" ? STATE.route.line : undefined));
35368
+ return;
35369
+ }
35370
+ if (mounted && state === "loading")
35371
+ return;
35372
+ }
35335
35373
  cancelActiveSourceLoad("navigation");
35336
35374
  const req = ++SOURCE_REQ_SEQ;
35337
35375
  const repoTarget = repoFileTargetFromRoute();
@@ -35343,10 +35381,13 @@ code-viewer query agent-help`
35343
35381
  const card = document.createElement("article");
35344
35382
  card.className = "gdp-file-shell loaded gdp-standalone-source gdp-source-mode";
35345
35383
  card.dataset.path = target.path;
35384
+ card.dataset.sourceRef = target.ref || "worktree";
35385
+ setSourceCardState(card, "loading");
35346
35386
  const internalKind = sourceInternalPathKind(target.path);
35347
35387
  const wrapper = document.createElement("div");
35348
35388
  wrapper.className = "gdp-file-detail-wrapper";
35349
- const activeTab = !internalKind && STATE.route.screen === "file" && STATE.route.preview ? "preview" : "code";
35389
+ const mediaOnly = !internalKind && isMediaPreviewOnlySource(target.path);
35390
+ const activeTab = mediaOnly || !internalKind && STATE.route.screen === "file" && STATE.route.preview ? "preview" : "code";
35350
35391
  const { sticky, header } = createFileShellSticky({
35351
35392
  currentRange,
35352
35393
  setRoute,
@@ -35427,9 +35468,13 @@ code-viewer query agent-help`
35427
35468
  const rendered2 = await renderPagedSourceText(card, target, meta.size, controller.signal);
35428
35469
  if (req !== SOURCE_REQ_SEQ || !sourceTargetsEqual(sourceTargetFromRoute(), target))
35429
35470
  return;
35430
- if (!rendered2)
35471
+ if (!rendered2) {
35472
+ finishSourceLoad(req);
35473
+ renderSourceError(card, target, `Cannot load ${target.path} at ${target.ref}`);
35431
35474
  return;
35475
+ }
35432
35476
  scrollStandaloneSourceLine(card, lineTargetStart(STATE.route.screen === "file" ? STATE.route.line : undefined));
35477
+ setSourceCardState(card, "done");
35433
35478
  finishSourceLoad(req);
35434
35479
  return;
35435
35480
  }
@@ -35450,6 +35495,7 @@ code-viewer query agent-help`
35450
35495
  if (!rendered)
35451
35496
  return;
35452
35497
  scrollStandaloneSourceLine(card, lineTargetStart(STATE.route.screen === "file" ? STATE.route.line : undefined));
35498
+ setSourceCardState(card, "done");
35453
35499
  finishSourceLoad(req);
35454
35500
  }
35455
35501
  } catch (err) {
@@ -35477,7 +35523,7 @@ code-viewer query agent-help`
35477
35523
  if (row)
35478
35524
  row.scrollIntoView({ block: "center" });
35479
35525
  }
35480
- function applySourceRouteToShell() {
35526
+ function applySourceRouteToShell(options = {}) {
35481
35527
  const target = sourceTargetFromRoute();
35482
35528
  setPageMode();
35483
35529
  if (!target) {
@@ -35487,7 +35533,7 @@ code-viewer query agent-help`
35487
35533
  });
35488
35534
  return;
35489
35535
  }
35490
- renderStandaloneSource(target);
35536
+ renderStandaloneSource(target, options);
35491
35537
  }
35492
35538
  function inferLang(path) {
35493
35539
  const name = sourceFileName(path);
@@ -37636,12 +37682,12 @@ code-viewer query agent-help`
37636
37682
  history.replaceState(historyStateForRoute(STATE.route), "", url + window.location.hash);
37637
37683
  }
37638
37684
  }
37639
- function dispatchFileRoute(route) {
37685
+ function dispatchFileRoute(route, options = {}) {
37640
37686
  if (route.view === "blob") {
37641
37687
  setStatus("live");
37642
37688
  removeFileHistoryShell2();
37643
37689
  BLAME_VIEW.removeBlamePage();
37644
- applySourceRouteToShell();
37690
+ applySourceRouteToShell(options);
37645
37691
  return true;
37646
37692
  }
37647
37693
  if (route.view === "blame") {
@@ -38649,7 +38695,7 @@ code-viewer query agent-help`
38649
38695
  setStatus("live");
38650
38696
  return Promise.resolve(null);
38651
38697
  }
38652
- if (STATE.route.screen === "file" && !(isFileHistoryRoute(STATE.route) && activeHistoryPathFilter) && dispatchFileRoute(STATE.route)) {
38698
+ if (STATE.route.screen === "file" && !(isFileHistoryRoute(STATE.route) && activeHistoryPathFilter) && dispatchFileRoute(STATE.route, { refresh: true })) {
38653
38699
  return Promise.resolve({
38654
38700
  structureChanged: false,
38655
38701
  invalidatedCards: 0,
@@ -39342,7 +39388,7 @@ code-viewer query agent-help`
39342
39388
  if (!shouldAutoLoadCurrentRoute(route))
39343
39389
  return;
39344
39390
  if (isBlobOrBlameFileRoute(route)) {
39345
- dispatchFileRoute(route);
39391
+ dispatchFileRoute(route, { refresh: true });
39346
39392
  return;
39347
39393
  }
39348
39394
  doSseLoad(paths);
@@ -39382,7 +39428,7 @@ code-viewer query agent-help`
39382
39428
  const viewingPath = route.path;
39383
39429
  if (paths && viewingPath && !paths.has(viewingPath))
39384
39430
  return;
39385
- dispatchFileRoute(route);
39431
+ dispatchFileRoute(route, { refresh: true });
39386
39432
  return;
39387
39433
  }
39388
39434
  if (route.screen === "repo") {