@weniatall/dsh-file-viewer 0.3.6 → 0.3.8

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.
Files changed (2) hide show
  1. package/lib/client.js +128 -104
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -387,7 +387,7 @@ window.__ModuleLoader__.load({
387
387
  body>div[role="presentation"]:has(.fv-scrim)>[aria-hidden="true"]{display:none!important}
388
388
  .fv-scrim[role="dialog"]{position:absolute!important;inset:0!important;width:auto!important;max-width:none!important;max-height:none!important;min-height:0!important;margin:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;background:transparent!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important;overflow:visible!important}
389
389
  .fv-scrim{position:absolute;inset:0;z-index:40;display:flex;align-items:stretch;justify-content:flex-end;background:transparent;pointer-events:auto;animation:fv-fade .16s ease-out}
390
- .fv-shell{display:flex;flex-direction:column;width:min(64vw,1100px);min-width:0;min-height:0;overflow:hidden;border-left:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));box-shadow:var(--dsw-shadow-lv3);font-family:var(--dsw-font-family);color:var(--dsw-alias-label-primary);animation:fv-slide .2s cubic-bezier(.2,.8,.2,1);transition:width .2s cubic-bezier(.2,.8,.2,1)}
390
+ .fv-shell{display:flex;flex-direction:column;width:min(64vw,1100px);min-width:0;min-height:0;overflow:hidden;border-left:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));box-shadow:var(--dsw-shadow-lv3);font-family:var(--dsw-font-family);color:var(--dsw-alias-label-primary);-webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);animation:fv-slide .2s cubic-bezier(.2,.8,.2,1);transition:width .2s cubic-bezier(.2,.8,.2,1)}
391
391
  .fv-shell[data-fullscreen="true"]{width:100vw;border-left:none}
392
392
  /* The leading edge is the drawer's own resize handle. It is positioned
393
393
  over the border rather than beside it so the panel does not shift when
@@ -483,14 +483,14 @@ window.__ModuleLoader__.load({
483
483
  .fv-lang-select{box-sizing:border-box;max-width:132px;height:24px;padding:0 4px;border:1px solid var(--dsw-alias-border-l2);border-radius:6px;background:var(--dsw-alias-bg-layer-1,transparent);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:22px;cursor:pointer}
484
484
  .fv-lang-select:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3,var(--dsw-alias-border-l2))}
485
485
  .fv-lang-select:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary,#4d6bfe);outline-offset:1px}
486
- .fv-content{flex:1 1 auto;min-height:0;overflow:auto;padding:12px;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
486
+ .fv-content{flex:1 1 auto;min-height:0;overflow:auto;padding:12px;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;isolation:isolate}
487
487
  .fv-content>*{max-width:100%}
488
488
  /* The Markdown outline: a rail pinned to the reading area's trailing edge.
489
489
  The wrapper is the positioning context, so the rail stays put while the
490
490
  text inside .fv-content scrolls. */
491
491
  .fv-content-wrap{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
492
492
  .fv-content-wrap>.fv-content{flex:1 1 auto}
493
- .fv-outline{position:absolute;top:8px;right:8px;z-index:5;display:flex;flex-direction:column;align-items:flex-end;gap:6px;max-height:calc(100% - 16px);pointer-events:none}
493
+ .fv-outline{position:absolute;top:8px;right:8px;z-index:10;display:flex;flex-direction:column;align-items:flex-end;gap:6px;max-height:calc(100% - 16px);pointer-events:none}
494
494
  .fv-outline>*{pointer-events:auto}
495
495
  .fv-outline-toggle{display:inline-grid;place-items:center;width:28px;height:28px;padding:0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-base,#fff));box-shadow:var(--dsw-shadow-lv2,0 2px 8px rgba(0,0,0,.08));color:var(--dsw-alias-label-secondary);cursor:pointer;flex:none;transition:color .12s,background-color .12s}
496
496
  .fv-outline-toggle:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover-solid,var(--dsw-alias-interactive-bg-hover))}
@@ -544,7 +544,11 @@ window.__ModuleLoader__.load({
544
544
  .fv-image-canvas.fv-image-dragging{cursor:grabbing}
545
545
  .fv-image-element{max-width:calc(100% - 32px);max-height:calc(100% - 32px);object-fit:contain;transition:transform .05s ease-out;pointer-events:none;box-shadow:var(--dsw-shadow-lv2,0 4px 12px rgba(0,0,0,.08))}
546
546
  .fv-frame{width:100%;height:100%;min-height:420px;border:none;background:var(--dsw-alias-bg-layer-1,#fff)}
547
- .fv-sheet-tabs{display:flex;flex-wrap:wrap;flex:none;align-items:center;gap:6px;padding:8px 12px;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,#fff));border-bottom:1px solid var(--dsw-alias-border-l1);z-index:2}
547
+ .fv-sheet-tabs{display:flex;flex-wrap:wrap;flex:none;align-items:center;justify-content:space-between;gap:6px;padding:6px 12px;background:var(--dsw-alias-bg-layer-1,var(--dsw-alias-bg-base,#fff));border-bottom:1px solid var(--dsw-alias-border-l1);z-index:2;min-height:38px}
548
+ .fv-sheet-tabs-start{display:inline-flex;align-items:center;gap:6px;min-width:0;flex:1 1 auto}
549
+ .fv-sheet-tabs-end{display:inline-flex;align-items:center;gap:8px;margin-left:auto;flex:none}
550
+ .fv-sheet-tabs .fv-btn-back{width:26px;height:26px;font-size:16px;line-height:24px;border-radius:6px;margin-right:2px}
551
+ .fv-main-size{font-size:11px;color:var(--dsw-alias-label-caption);white-space:nowrap}
548
552
  .fv-content .fv-sheet-tabs{position:sticky;top:0;margin:-12px -12px 10px;padding:12px 12px 8px}
549
553
  .fv-tab{height:28px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:none;color:var(--dsw-alias-label-secondary);font-size:12px;cursor:pointer}
550
554
  .fv-tab[aria-selected="true"]{border-color:transparent;background:var(--dsw-alias-brand-primary,#4d6bfe);color:var(--dsw-alias-label-primary-foreground,#fff)}
@@ -677,7 +681,7 @@ window.__ModuleLoader__.load({
677
681
  .fv-content[data-wrap="on"] div[class*="_line_"]{align-items:flex-start}
678
682
  .fv-content[data-wrap="on"] span[class*="_gutter_"]{flex:none;white-space:pre}
679
683
  .fv-content[data-wrap="on"] span[class*="_content_"]{min-width:0;flex:1 1 auto;white-space:pre-wrap;overflow-wrap:anywhere}
680
- .fv-context-backdrop{position:fixed;inset:0;z-index:99;background:transparent;touch-action:none}
684
+ .fv-context-backdrop{position:fixed;inset:0;z-index:99;background:transparent!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important;touch-action:none}
681
685
  /* 菜单是实底卡片,不吃主题的毛玻璃。
682
686
  玻璃主题(glass.css)里有一条规则,给 [role="menu"] 套上
683
687
  background:var(--color-menu) 和 backdrop-filter:blur(...);我们的菜单正好
@@ -2608,7 +2612,7 @@ window.__ModuleLoader__.load({
2608
2612
  *
2609
2613
  * @param {{state: object, path: string}} props - panel snapshot and the file to diff.
2610
2614
  */
2611
- function WorkspaceChangeDetail({ state, path: filePath, request, repo }) {
2615
+ function WorkspaceChangeDetail({ state, path: filePath, request, repo, backControl }) {
2612
2616
  const [diff, setDiff] = react.useState({ status: "idle", data: null, error: null });
2613
2617
  // A change can be staged, unstaged, or both. The reader picks which side
2614
2618
  // to look at instead of the pane guessing.
@@ -2660,6 +2664,7 @@ window.__ModuleLoader__.load({
2660
2664
 
2661
2665
  return react.createElement("div", { className: "fv-commit" },
2662
2666
  react.createElement("div", { className: "fv-commit-head" },
2667
+ backControl ? react.createElement("div", { style: { marginBottom: "6px" } }, backControl) : null,
2663
2668
  react.createElement("span", { className: "fv-commit-subject" }, filePath || "工作区改动"),
2664
2669
  react.createElement("span", { className: "fv-commit-meta" },
2665
2670
  row !== null
@@ -2687,7 +2692,7 @@ window.__ModuleLoader__.load({
2687
2692
  * second viewer; a row that lands outside the current root stays visible
2688
2693
  * but disabled rather than silently disappearing.
2689
2694
  */
2690
- function CommitDetail({ state, selectedPath, onSelectFile, view, onSwitchView, onLoadDiff }) {
2695
+ function CommitDetail({ state, selectedPath, onSelectFile, view, onSwitchView, onLoadDiff, backControl }) {
2691
2696
  if (state.detailStatus === "loading" || state.detailStatus === "idle") {
2692
2697
  return react.createElement("div", { className: "fv-note" }, "正在读取提交详情…");
2693
2698
  }
@@ -2733,27 +2738,29 @@ window.__ModuleLoader__.load({
2733
2738
  // Two views of one commit: which files changed, and what changed inside
2734
2739
  // them. Both come from the same revision pair, so they cannot disagree.
2735
2740
  const tabs = react.createElement("div", { className: "fv-sheet-tabs", role: "tablist" },
2736
- react.createElement("button", {
2737
- type: "button", className: "fv-tab", "aria-selected": view !== "diff" ? "true" : "false",
2738
- onClick: () => onSwitchView("files"),
2739
- }, "变更文件", react.createElement("span", { className: "fv-git-count" }, String(files.length))),
2740
- react.createElement("button", {
2741
- type: "button", className: "fv-tab fv-tab-diff", "aria-selected": view === "diff" ? "true" : "false",
2742
- onClick: () => {
2743
- onSwitchView("diff");
2744
- if (onLoadDiff !== undefined) onLoadDiff();
2745
- },
2746
- }, "改动内容",
2747
- state.commitDiff !== null && state.commitDiff !== undefined && state.commitDiff.summary !== undefined
2748
- ? react.createElement("span", { className: "fv-diff-stat" },
2749
- state.commitDiff.summary.added > 0
2750
- ? react.createElement("span", { className: "fv-diff-add" }, "+" + state.commitDiff.summary.added)
2751
- : null,
2752
- state.commitDiff.summary.added > 0 && state.commitDiff.summary.removed > 0 ? " " : "",
2753
- state.commitDiff.summary.removed > 0
2754
- ? react.createElement("span", { className: "fv-diff-del" }, "-" + state.commitDiff.summary.removed)
2755
- : null)
2756
- : null));
2741
+ react.createElement("div", { className: "fv-sheet-tabs-start" },
2742
+ backControl,
2743
+ react.createElement("button", {
2744
+ type: "button", className: "fv-tab", "aria-selected": view !== "diff" ? "true" : "false",
2745
+ onClick: () => onSwitchView("files"),
2746
+ }, "变更文件", react.createElement("span", { className: "fv-git-count" }, String(files.length))),
2747
+ react.createElement("button", {
2748
+ type: "button", className: "fv-tab fv-tab-diff", "aria-selected": view === "diff" ? "true" : "false",
2749
+ onClick: () => {
2750
+ onSwitchView("diff");
2751
+ if (onLoadDiff !== undefined) onLoadDiff();
2752
+ },
2753
+ }, "改动内容",
2754
+ state.commitDiff !== null && state.commitDiff !== undefined && state.commitDiff.summary !== undefined
2755
+ ? react.createElement("span", { className: "fv-diff-stat" },
2756
+ state.commitDiff.summary.added > 0
2757
+ ? react.createElement("span", { className: "fv-diff-add" }, "+" + state.commitDiff.summary.added)
2758
+ : null,
2759
+ state.commitDiff.summary.added > 0 && state.commitDiff.summary.removed > 0 ? " " : "",
2760
+ state.commitDiff.summary.removed > 0
2761
+ ? react.createElement("span", { className: "fv-diff-del" }, "-" + state.commitDiff.summary.removed)
2762
+ : null)
2763
+ : null)));
2757
2764
 
2758
2765
  let patchBody;
2759
2766
  if (state.commitDiffStatus === "loading" || state.commitDiffStatus === "idle") {
@@ -3938,7 +3945,7 @@ window.__ModuleLoader__.load({
3938
3945
 
3939
3946
  /** Line-numbered diff viewer with dual line number gutters. */
3940
3947
  /** Text, Markdown and JSON: one windowed read, three renderings. */
3941
- function TextView({ meta, root, wrap, lang, commit, repo }) {
3948
+ function TextView({ meta, root, wrap, lang, commit, repo, backControl, sizeControl, langControl }) {
3942
3949
  // Callers mount this with a per-file key, so paging state starts fresh
3943
3950
  // for every file instead of leaking the previous file's window into the
3944
3951
  // first request.
@@ -4218,11 +4225,22 @@ window.__ModuleLoader__.load({
4218
4225
  }, "源码")
4219
4226
  : null;
4220
4227
 
4221
- const tabs = showTabs
4228
+ const tabButtons = showTabs
4229
+ ? [...(fromCommit && diffTab !== null
4230
+ ? [diffTab, contentTab, sourceTab]
4231
+ : [contentTab, sourceTab, diffTab])].filter((tab) => tab !== null)
4232
+ : [];
4233
+
4234
+ const tabs = showTabs || Boolean(backControl || langControl || sizeControl)
4222
4235
  ? react.createElement("div", { className: "fv-sheet-tabs", role: "tablist" },
4223
- ...(fromCommit && diffTab !== null
4224
- ? [diffTab, contentTab, sourceTab]
4225
- : [contentTab, sourceTab, diffTab]).filter((tab) => tab !== null))
4236
+ react.createElement("div", { className: "fv-sheet-tabs-start" },
4237
+ backControl,
4238
+ ...tabButtons),
4239
+ (sizeControl || langControl)
4240
+ ? react.createElement("div", { className: "fv-sheet-tabs-end" },
4241
+ sizeControl,
4242
+ langControl)
4243
+ : null)
4226
4244
  : null;
4227
4245
 
4228
4246
 
@@ -4289,32 +4307,38 @@ window.__ModuleLoader__.load({
4289
4307
  }
4290
4308
 
4291
4309
  /** Pick the viewer for a file and keep unsupported formats useful. */
4292
- function FileView({ meta, root, api, wrap, lang, onDownload, commit, repo, loading }) {
4310
+ function FileView({ meta, root, api, wrap, lang, onDownload, commit, repo, loading, backControl, sizeControl, langControl }) {
4293
4311
  if (meta === null) {
4294
4312
  // A tab exists but its metadata is still in flight. Saying "pick a
4295
4313
  // file" there would be a lie, and the reader would click again.
4296
- return react.createElement("div", { className: "fv-note" },
4297
- loading === true ? "正在读取文件…" : "从左侧选择一个文件。");
4314
+ return react.createElement("div", { className: "fv-empty-pane" },
4315
+ backControl ? react.createElement("div", { className: "fv-sheet-tabs", role: "toolbar" }, backControl) : null,
4316
+ react.createElement("div", { className: "fv-note" },
4317
+ loading === true ? "正在读取文件…" : "从左侧选择一个文件。"));
4298
4318
  }
4299
4319
  if (meta.tooLarge || meta.kind === "binary") {
4300
- return react.createElement("div", { className: "fv-note" },
4301
- react.createElement("strong", null, meta.name),
4302
- react.createElement("span", null, meta.tooLarge
4303
- ? `文件大小 ${formatBytes(meta.size)},超过查看器上限 ${formatBytes(meta.limit)}。`
4304
- : "这是二进制文件,查看器无法显示其内容。"),
4305
- react.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px", marginTop: "4px" } },
4306
- api && api.host
4307
- ? react.createElement("button", {
4320
+ return react.createElement("div", { className: "fv-binary-pane" },
4321
+ backControl ? react.createElement("div", { className: "fv-sheet-tabs", role: "toolbar" },
4322
+ react.createElement("div", { className: "fv-sheet-tabs-start" }, backControl),
4323
+ sizeControl ? react.createElement("div", { className: "fv-sheet-tabs-end" }, sizeControl) : null) : null,
4324
+ react.createElement("div", { className: "fv-note" },
4325
+ react.createElement("strong", null, meta.name),
4326
+ react.createElement("span", null, meta.tooLarge
4327
+ ? `文件大小 ${formatBytes(meta.size)},超过查看器上限 ${formatBytes(meta.limit)}。`
4328
+ : "这是二进制文件,查看器无法显示其内容。"),
4329
+ react.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px", marginTop: "4px" } },
4330
+ api && api.host
4331
+ ? react.createElement("button", {
4332
+ type: "button",
4333
+ className: "fv-button",
4334
+ onClick: () => api.host.openPath({ path: meta.displayPath }),
4335
+ }, "用本地程序打开")
4336
+ : null,
4337
+ react.createElement("button", {
4308
4338
  type: "button",
4309
- className: "fv-button",
4310
- onClick: () => api.host.openPath({ path: meta.displayPath }),
4311
- }, "用本地程序打开")
4312
- : null,
4313
- react.createElement("button", {
4314
- type: "button",
4315
- className: "fv-button fv-btn-download-note",
4316
- onClick: () => onDownload && onDownload(meta),
4317
- }, "下载到本地")));
4339
+ className: "fv-button fv-btn-download-note",
4340
+ onClick: () => onDownload && onDownload(meta),
4341
+ }, "下载到本地"))));
4318
4342
  }
4319
4343
  // The key remounts each viewer per file so no per-file state (paging,
4320
4344
  // active sheet, blob URL) survives a switch.
@@ -4329,21 +4353,28 @@ window.__ModuleLoader__.load({
4329
4353
  "以下内容来自提交 " + commit.slice(0, 7) + (meta.change ? ",该文件本次为「" + meta.change + "」" : "")),
4330
4354
  react.createElement("span", { className: "fv-history-hint" }, "这是历史版本,不是工作区当前内容。"))
4331
4355
  : null;
4356
+
4357
+ const headBar = (backControl || sizeControl)
4358
+ ? react.createElement("div", { className: "fv-sheet-tabs", role: "toolbar" },
4359
+ react.createElement("div", { className: "fv-sheet-tabs-start" }, backControl),
4360
+ sizeControl ? react.createElement("div", { className: "fv-sheet-tabs-end" }, sizeControl) : null)
4361
+ : null;
4362
+
4332
4363
  if (fromHistory && meta.kind !== "text" && meta.kind !== "markdown" && meta.kind !== "json") {
4333
- return react.createElement(react.Fragment, null, banner,
4364
+ return react.createElement(react.Fragment, null, headBar, banner,
4334
4365
  react.createElement("div", { className: "fv-note" },
4335
4366
  "该提交中的文件是 " + meta.kind + " 格式,历史版本暂不支持预览。"));
4336
4367
  }
4337
- if (meta.kind === "image") return react.createElement(ImageView, { key, meta, root });
4338
- if (meta.kind === "pdf") return react.createElement(BinaryView, { key, meta, root });
4368
+ if (meta.kind === "image") return react.createElement(react.Fragment, null, headBar, react.createElement(ImageView, { key, meta, root }));
4369
+ if (meta.kind === "pdf") return react.createElement(react.Fragment, null, headBar, react.createElement(BinaryView, { key, meta, root }));
4339
4370
  if (meta.kind === "sheet") {
4340
- return react.createElement("div", { className: "fv-content" }, react.createElement(SheetView, { key, meta, root }));
4371
+ return react.createElement("div", { className: "fv-content" }, headBar, react.createElement(SheetView, { key, meta, root }));
4341
4372
  }
4342
4373
  if (meta.kind === "doc") {
4343
- return react.createElement("div", { className: "fv-content" }, react.createElement(DocView, { key, meta, root }));
4374
+ return react.createElement("div", { className: "fv-content" }, headBar, react.createElement(DocView, { key, meta, root }));
4344
4375
  }
4345
4376
  return react.createElement(react.Fragment, null, banner,
4346
- react.createElement(TextView, { key, meta, root, wrap, lang, commit, repo }));
4377
+ react.createElement(TextView, { key, meta, root, wrap, lang, commit, repo, backControl, sizeControl, langControl }));
4347
4378
  }
4348
4379
 
4349
4380
 
@@ -5747,6 +5778,36 @@ window.__ModuleLoader__.load({
5747
5778
  performDownload(request, root, targetPath, targetName, showToast);
5748
5779
  };
5749
5780
 
5781
+ const originKind = previewOrigin === null || previewOrigin === undefined ? null : previewOrigin.kind;
5782
+ const backTarget = originKind === "commit"
5783
+ ? "返回变更文件列表"
5784
+ : (originKind === "changes" ? "返回工作区改动列表" : "返回文件列表");
5785
+ const canGoBack = isMobile || pane === "content"
5786
+ ? (previewOrigin !== null || meta !== null || showCommit)
5787
+ : false;
5788
+ const backControl = react.createElement("button", {
5789
+ type: "button",
5790
+ className: "fv-icon-button fv-btn-back",
5791
+ "aria-label": backTarget,
5792
+ title: backTarget,
5793
+ disabled: !canGoBack,
5794
+ onClick: goBack,
5795
+ }, react.createElement("span", { "aria-hidden": "true" }, "‹"));
5796
+
5797
+ const sizeControl = meta !== null && !showCommit && typeof meta.size === "number"
5798
+ ? react.createElement("span", { className: "fv-main-size" }, formatBytes(meta.size))
5799
+ : null;
5800
+
5801
+ const langControl = meta !== null && !showCommit && !meta.tooLarge && (meta.kind === "text" || meta.kind === "markdown" || meta.kind === "json")
5802
+ ? react.createElement(LanguagePicker, {
5803
+ choice: localeTagOfActive,
5804
+ catalog: languageCatalog,
5805
+ source: meta.langSource,
5806
+ resolved: meta.lang,
5807
+ onChange: chooseLang,
5808
+ })
5809
+ : null;
5810
+
5750
5811
  return react.createElement("div", {
5751
5812
  className: "fv-scrim",
5752
5813
  role: "dialog",
@@ -5934,51 +5995,6 @@ window.__ModuleLoader__.load({
5934
5995
  onContextMenu: (menu) => setTabMenu(menu),
5935
5996
  menuFor: tabMenu === null || tabMenu === undefined ? "" : tabMenu.tabId,
5936
5997
  }),
5937
- react.createElement("div", { className: "fv-main-head" },
5938
- (() => {
5939
- // The control returns one step, so it names that step and
5940
- // is disabled when there is nowhere to go — a button that
5941
- // silently does nothing reads as broken.
5942
- const originKind = previewOrigin === null || previewOrigin === undefined ? null : previewOrigin.kind;
5943
- const backTarget = originKind === "commit"
5944
- ? "返回变更文件列表"
5945
- : (originKind === "changes" ? "返回工作区改动列表" : "返回文件列表");
5946
- // On a phone the panes are exclusive, so leaving the content
5947
- // pane is always meaningful; on desktop the sidebar is
5948
- // already visible and only an origin is worth returning to.
5949
- const canGoBack = isMobile || pane === "content"
5950
- ? (previewOrigin !== null || meta !== null || showCommit)
5951
- : false;
5952
- return react.createElement("button", {
5953
- type: "button",
5954
- className: "fv-icon-button fv-btn-back",
5955
- "aria-label": backTarget,
5956
- title: backTarget,
5957
- disabled: !canGoBack,
5958
- onClick: goBack,
5959
- }, react.createElement("span", { "aria-hidden": "true" }, "‹"));
5960
- })(),
5961
- react.createElement("span", { className: "fv-main-name" },
5962
- commitHeadline !== ""
5963
- ? commitHeadline
5964
- : (activeTab === null
5965
- ? "未选择文件"
5966
- : (meta === null ? activeTab.title : meta.name))),
5967
- meta !== null && !showCommit && typeof meta.size === "number"
5968
- ? react.createElement("span", null, formatBytes(meta.size))
5969
- : null,
5970
- // The language picker sits with the file it applies to, and
5971
- // is offered only for source views: an image, sheet or
5972
- // document has no grammar to choose.
5973
- meta !== null && !showCommit && !meta.tooLarge && (meta.kind === "text" || meta.kind === "markdown" || meta.kind === "json")
5974
- ? react.createElement(LanguagePicker, {
5975
- choice: localeTagOfActive,
5976
- catalog: languageCatalog,
5977
- source: meta.langSource,
5978
- resolved: meta.lang,
5979
- onChange: chooseLang,
5980
- })
5981
- : null),
5982
5998
  error !== null && meta === null && !showCommit && activeTab === null
5983
5999
  ? react.createElement("div", { className: "fv-note fv-error" },
5984
6000
  react.createElement("span", null, messageOf(error)),
@@ -6021,6 +6037,7 @@ window.__ModuleLoader__.load({
6021
6037
  path: previewChangePath,
6022
6038
  request,
6023
6039
  repo: gitState.repoId,
6040
+ backControl,
6024
6041
  })
6025
6042
  : react.createElement(CommitDetail, {
6026
6043
  state: gitState,
@@ -6036,6 +6053,7 @@ window.__ModuleLoader__.load({
6036
6053
  const sha = commitSha;
6037
6054
  if (sha !== "") git.loadCommitDiff(sha);
6038
6055
  },
6056
+ backControl,
6039
6057
  }))
6040
6058
  : react.createElement(ErrorBoundary, {
6041
6059
  // One viewer per tab (and per refresh epoch), so no
@@ -6053,6 +6071,9 @@ window.__ModuleLoader__.load({
6053
6071
  onDownload: handleDownloadItem,
6054
6072
  commit: "",
6055
6073
  repo: gitState.repoId,
6074
+ backControl,
6075
+ sizeControl,
6076
+ langControl,
6056
6077
  })
6057
6078
  : react.createElement(FileView, {
6058
6079
  meta,
@@ -6068,6 +6089,9 @@ window.__ModuleLoader__.load({
6068
6089
  // A tab is created before its metadata arrives, so the
6069
6090
  // pane has to say "reading" instead of "pick a file".
6070
6091
  loading: activeTab.status === "loading",
6092
+ backControl,
6093
+ sizeControl,
6094
+ langControl,
6071
6095
  })))),
6072
6096
  contextMenu !== null
6073
6097
  ? react.createElement(ContextMenu, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weniatall/dsh-file-viewer",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Workspace file viewer and Git panel for DeepSeek Harness: file tree, syntax-highlighted code, images, PDF, Excel and Word, plus local/remote branches, paged commit history, commit diffs, working-tree changes and changed-file navigation",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",