dsh-coding-sidebar 1.0.3 → 1.0.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.
Files changed (89) hide show
  1. package/README.md +27 -42
  2. package/lib/client-editor.js +297 -4179
  3. package/lib/client-registry.js +639 -1341
  4. package/lib/client-terminal.js +176 -266
  5. package/lib/client.js +640 -1342
  6. package/lib/index.js +4 -110
  7. package/lib/types/bundle-route.d.ts +1 -1
  8. package/lib/types/client/SideCardSection.d.ts +3 -3
  9. package/lib/types/client/TextEditor.d.ts +20 -10
  10. package/lib/types/client/add-plugin-modal.d.ts +0 -4
  11. package/lib/types/client/api.d.ts +8 -11
  12. package/lib/types/client/builtins/index.d.ts +10 -10
  13. package/lib/types/client/chunk-loader.d.ts +7 -8
  14. package/lib/types/client/locales.d.ts +0 -26
  15. package/lib/types/client/native-avoid.d.ts +2 -0
  16. package/lib/types/client/plugins-shared.d.ts +6 -6
  17. package/lib/types/client/service.d.ts +13 -83
  18. package/lib/types/index.d.ts +1 -1
  19. package/lib/types/prefs-shared.d.ts +1 -24
  20. package/package.json +26 -29
  21. package/src/bundle-route.ts +1 -1
  22. package/src/client/EditorHost.tsx +52 -122
  23. package/src/client/SandboxStatusBar.tsx +12 -9
  24. package/src/client/SideCardSection.tsx +28 -81
  25. package/src/client/TextEditor.tsx +89 -266
  26. package/src/client/add-plugin-modal.tsx +16 -24
  27. package/src/client/api.ts +1 -25
  28. package/src/client/builtins/index.ts +10 -14
  29. package/src/client/chunk-loader.ts +7 -8
  30. package/src/client/index.tsx +12 -3
  31. package/src/client/locales-ar.ts +0 -6
  32. package/src/client/locales-de.ts +0 -6
  33. package/src/client/locales-fr.ts +0 -6
  34. package/src/client/locales-hi.ts +0 -6
  35. package/src/client/locales-id.ts +0 -6
  36. package/src/client/locales-it.ts +0 -6
  37. package/src/client/locales-ja.ts +0 -6
  38. package/src/client/locales-ko.ts +0 -6
  39. package/src/client/locales-nl.ts +0 -6
  40. package/src/client/locales-pl.ts +0 -6
  41. package/src/client/locales-pt.ts +0 -6
  42. package/src/client/locales-ru.ts +0 -6
  43. package/src/client/locales-sv.ts +0 -6
  44. package/src/client/locales-th.ts +0 -6
  45. package/src/client/locales-tr.ts +0 -6
  46. package/src/client/locales-vi.ts +0 -6
  47. package/src/client/locales-zh-HK.ts +0 -6
  48. package/src/client/locales-zh-MO.ts +0 -6
  49. package/src/client/locales-zh-TW.ts +0 -6
  50. package/src/client/locales.ts +0 -52
  51. package/src/client/native-avoid.ts +155 -0
  52. package/src/client/plugins-shared.ts +6 -6
  53. package/src/client/prefs.ts +0 -7
  54. package/src/client/service.ts +13 -150
  55. package/src/client/sidebar.module.css +1 -425
  56. package/src/config.ts +1 -4
  57. package/src/index.ts +7 -74
  58. package/src/prefs-shared.ts +1 -27
  59. package/lib/client-mermaid.js +0 -200776
  60. package/lib/types/client/MarkdownHtml.d.ts +0 -32
  61. package/lib/types/client/PdfView.d.ts +0 -6
  62. package/lib/types/client/builtins/viewers.d.ts +0 -3
  63. package/lib/types/client/chunks/mermaid.d.ts +0 -10
  64. package/lib/types/client/editor-load.d.ts +0 -66
  65. package/lib/types/client/image-types.d.ts +0 -3
  66. package/lib/types/client/markdown-html.d.ts +0 -96
  67. package/lib/types/client/markdown-images.d.ts +0 -45
  68. package/lib/types/client/md-toc.d.ts +0 -4
  69. package/lib/types/client/mermaid-blocks.d.ts +0 -47
  70. package/lib/types/client/mermaid-sanitize.d.ts +0 -2
  71. package/lib/types/client/mermaid.d.ts +0 -12
  72. package/lib/types/client/pdf-types.d.ts +0 -2
  73. package/lib/types/client/plugins-viewers.d.ts +0 -3
  74. package/lib/types/html-route.d.ts +0 -59
  75. package/src/client/MarkdownHtml.tsx +0 -296
  76. package/src/client/PdfView.tsx +0 -110
  77. package/src/client/builtins/viewers.tsx +0 -125
  78. package/src/client/chunks/mermaid.tsx +0 -10
  79. package/src/client/editor-load.ts +0 -92
  80. package/src/client/image-types.ts +0 -8
  81. package/src/client/markdown-html.ts +0 -331
  82. package/src/client/markdown-images.ts +0 -137
  83. package/src/client/md-toc.tsx +0 -127
  84. package/src/client/mermaid-blocks.ts +0 -110
  85. package/src/client/mermaid-sanitize.ts +0 -93
  86. package/src/client/mermaid.tsx +0 -406
  87. package/src/client/pdf-types.ts +0 -4
  88. package/src/client/plugins-viewers.ts +0 -30
  89. package/src/html-route.ts +0 -106
@@ -1418,17 +1418,6 @@ body[data-dsh-sidebar-dragging] .panel {
1418
1418
  background: var(--dsw-alias-interactive-bg-hover);
1419
1419
  }
1420
1420
 
1421
- /* The HTML preview iframe: fills the editor body. Route-src (never srcdoc)
1422
- so the frame is cross-origin by construction even if the sandbox
1423
- attribute is ever dropped; sandboxed by default (see TextEditor). */
1424
- .editorHtml {
1425
- flex: 1;
1426
- min-height: 0;
1427
- width: 100%;
1428
- border: none;
1429
- background: var(--dsw-alias-bg-base);
1430
- }
1431
-
1432
1421
  /* ── Browser tab ──────────────────────────────────────────────────────── */
1433
1422
 
1434
1423
  .browser {
@@ -1545,10 +1534,6 @@ body[data-dsh-sidebar-dragging] .panel {
1545
1534
  background: transparent;
1546
1535
  }
1547
1536
 
1548
- .editorCmHidden {
1549
- display: none;
1550
- }
1551
-
1552
1537
  /* CodeMirror fills the pane; the theme supplies its own token palette
1553
1538
  (a designed dark theme, same policy as the xterm palette). */
1554
1539
  .editorCm :global(.cm-editor) {
@@ -1565,229 +1550,6 @@ body[data-dsh-sidebar-dragging] .panel {
1565
1550
  outline: none;
1566
1551
  }
1567
1552
 
1568
- /* Preview / edit segmented toggle (markdown and other previewable files). */
1569
- .editorModeToggle {
1570
- flex: none;
1571
- display: inline-flex;
1572
- align-items: center;
1573
- gap: 2px;
1574
- padding: 2px;
1575
- border: 1px solid var(--dsw-alias-border-l1);
1576
- border-radius: 6px;
1577
- background: var(--dsw-alias-bg-layer-1);
1578
- }
1579
-
1580
- .editorModeButton {
1581
- padding: 2px 8px;
1582
- border: none;
1583
- border-radius: 4px;
1584
- background: transparent;
1585
- color: var(--dsw-alias-label-tertiary);
1586
- font: var(--dsw-font-xxxs-11);
1587
- cursor: pointer;
1588
- }
1589
-
1590
- .editorModeButton:hover {
1591
- color: var(--dsw-alias-label-primary);
1592
- }
1593
-
1594
- .editorModeActive {
1595
- background: var(--dsw-alias-bg-base);
1596
- color: var(--dsw-alias-label-primary);
1597
- }
1598
-
1599
- .editorImageWrap {
1600
- flex: 1;
1601
- min-height: 0;
1602
- display: flex;
1603
- align-items: center;
1604
- justify-content: center;
1605
- overflow: auto;
1606
- padding: 12px;
1607
- }
1608
-
1609
- .editorImage {
1610
- max-width: 100%;
1611
- max-height: 100%;
1612
- object-fit: contain;
1613
- }
1614
-
1615
- .editorMd {
1616
- flex: 1;
1617
- min-height: 0;
1618
- overflow-y: auto;
1619
- padding: 12px 16px;
1620
- font: var(--dsw-font-xs-13);
1621
- }
1622
-
1623
- /* ── Mermaid diagram block (markdown preview, mermaid lazy chunk) ────────
1624
- Mirrors the md-code-block chrome the DSH CodeBlock renders, so a diagram
1625
- reads as a peer of the surrounding fences: hairline border, layer-2
1626
- header with the info string + copy action, layer-1 body. The SVG body is
1627
- static (no bindFunctions) and sanitized (no foreignObject/script/@attrs/
1628
- hrefs), so it carries no event surface beyond the copy button; the body
1629
- click opens the zoom modal on a sanitized clone. */
1630
-
1631
- .mermaidWrap {
1632
- margin: 6px 0;
1633
- border: 1px solid var(--dsw-alias-border-l1);
1634
- border-radius: 6px;
1635
- background: var(--dsw-alias-bg-layer-1);
1636
- overflow: hidden;
1637
- }
1638
-
1639
- .mermaidHeader {
1640
- display: flex;
1641
- align-items: center;
1642
- justify-content: space-between;
1643
- gap: 6px;
1644
- padding: 4px 8px;
1645
- border-bottom: 1px solid var(--dsw-alias-border-l1);
1646
- background: var(--dsw-alias-bg-layer-2);
1647
- }
1648
-
1649
- .mermaidInfo {
1650
- font: var(--dsw-font-xxxs-strong-11);
1651
- color: var(--dsw-alias-label-tertiary);
1652
- }
1653
-
1654
- .mermaidCopy {
1655
- display: inline-flex;
1656
- align-items: center;
1657
- gap: 4px;
1658
- padding: 0 6px;
1659
- height: 20px;
1660
- border: none;
1661
- border-radius: 4px;
1662
- background: transparent;
1663
- color: var(--dsw-alias-label-secondary);
1664
- font: var(--dsw-font-xxxs-11);
1665
- cursor: pointer;
1666
- }
1667
-
1668
- .mermaidCopy:hover {
1669
- background: var(--dsw-alias-interactive-bg-hover);
1670
- color: var(--dsw-alias-label-primary);
1671
- }
1672
-
1673
- .mermaidBody {
1674
- display: flex;
1675
- justify-content: center;
1676
- padding: 10px;
1677
- overflow: auto;
1678
- cursor: zoom-in;
1679
- }
1680
-
1681
- .mermaidBody :global(svg) {
1682
- max-width: 100%;
1683
- height: auto;
1684
- }
1685
-
1686
- .mermaidError {
1687
- padding: 6px 10px;
1688
- border-bottom: 1px solid var(--dsw-alias-border-l1);
1689
- color: var(--dsw-alias-state-error-primary);
1690
- font: var(--dsw-font-xxxs-11);
1691
- }
1692
-
1693
- /* The diagram source shown under a render error (helps fixing the fence). */
1694
- .mermaidCode {
1695
- margin: 0;
1696
- padding: 8px 10px;
1697
- overflow: auto;
1698
- font: var(--dsw-font-xxxs-11);
1699
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
1700
- }
1701
-
1702
- /* The mermaid markdown container (single MarkdownText pass): a swapped
1703
- code block keeps its React-managed .md-code-block wrapper in the tree
1704
- (reconciliation safety), but the wrapper's own box is suppressed so only
1705
- the diagram chrome renders; the swapped-in host div flows as a block. */
1706
- .mermaidMarkdown :global(.md-code-block[data-mermaid-processed]) {
1707
- display: contents;
1708
- }
1709
-
1710
- /* ── Mermaid zoom modal (click-to-enlarge; portalled to document.body) ──
1711
- Fixed overlay above the DSH overlay stack (1000+), token-driven surface.
1712
- The stage centers the (sanitized, cloned) svg and the modal owns the
1713
- transform; the drag/zoom listeners live in the component, nothing here
1714
- targets global class names. */
1715
-
1716
- .mermaidModal {
1717
- position: fixed;
1718
- inset: 0;
1719
- z-index: 1000;
1720
- display: flex;
1721
- flex-direction: column;
1722
- align-items: center;
1723
- justify-content: center;
1724
- background: var(--dsw-alias-bg-mask-1);
1725
- backdrop-filter: blur(2px);
1726
- }
1727
-
1728
- .mermaidModalToolbar {
1729
- position: absolute;
1730
- top: 16px;
1731
- right: 16px;
1732
- display: flex;
1733
- gap: 8px;
1734
- z-index: 10;
1735
- }
1736
-
1737
- .mermaidModalButton {
1738
- width: 36px;
1739
- height: 36px;
1740
- border-radius: 8px;
1741
- border: 1px solid var(--dsw-alias-border-l1);
1742
- background: var(--dsw-alias-bg-layer-2);
1743
- color: var(--dsw-alias-label-primary);
1744
- font: var(--dsw-font-xs-strong-13);
1745
- cursor: pointer;
1746
- display: inline-flex;
1747
- align-items: center;
1748
- justify-content: center;
1749
- }
1750
-
1751
- .mermaidModalButton:hover {
1752
- background: var(--dsw-alias-interactive-bg-hover);
1753
- }
1754
-
1755
- .mermaidModalStage {
1756
- width: 90vw;
1757
- height: 80vh;
1758
- overflow: hidden;
1759
- position: relative;
1760
- display: flex;
1761
- align-items: center;
1762
- justify-content: center;
1763
- }
1764
-
1765
- .mermaidModalStage :global(svg) {
1766
- max-width: none;
1767
- max-height: none;
1768
- cursor: grab;
1769
- transform-origin: center center;
1770
- user-select: none;
1771
- -webkit-user-drag: none;
1772
- background: var(--dsw-alias-bg-layer-1);
1773
- padding: 16px;
1774
- border-radius: 12px;
1775
- }
1776
-
1777
- .mermaidModalStage :global(svg):active {
1778
- cursor: grabbing;
1779
- }
1780
-
1781
- .mermaidModalHint {
1782
- position: absolute;
1783
- bottom: 16px;
1784
- left: 50%;
1785
- transform: translateX(-50%);
1786
- color: var(--dsw-alias-label-tertiary);
1787
- font: var(--dsw-font-xxxs-11);
1788
- pointer-events: none;
1789
- }
1790
-
1791
1553
  /* The floating "add to conversation" button anchored above a text
1792
1554
  selection in the preview/code viewers. Portalled to document.body and
1793
1555
  position:fixed, so the editor's overflow clips cannot crop it; flat
@@ -1814,64 +1576,6 @@ body[data-dsh-sidebar-dragging] .panel {
1814
1576
  background: var(--dsw-alias-interactive-bg-hover);
1815
1577
  }
1816
1578
 
1817
- .editorPdf {
1818
- flex: 1;
1819
- min-height: 0;
1820
- display: flex;
1821
- flex-direction: column;
1822
- background: var(--dsw-alias-bg-base);
1823
- }
1824
-
1825
- .editorPdfToolbar {
1826
- flex: none;
1827
- display: flex;
1828
- justify-content: flex-end;
1829
- padding: 6px 8px;
1830
- border-bottom: 1px solid var(--dsw-alias-border-l1);
1831
- }
1832
-
1833
- .editorPdfStage {
1834
- position: relative;
1835
- flex: 1;
1836
- min-height: 0;
1837
- display: flex;
1838
- }
1839
-
1840
- .editorPdfFrame {
1841
- flex: 1;
1842
- min-height: 0;
1843
- width: 100%;
1844
- border: none;
1845
- background: var(--dsw-alias-bg-base);
1846
- }
1847
-
1848
- .editorPdfFrameBlocked {
1849
- pointer-events: none;
1850
- }
1851
-
1852
- .editorPdfDragShield {
1853
- position: absolute;
1854
- inset: 0;
1855
- z-index: 4;
1856
- pointer-events: none;
1857
- background: transparent;
1858
- }
1859
-
1860
- .editorPdfDragShieldActive {
1861
- pointer-events: auto;
1862
- }
1863
-
1864
- /* Tab drag must switch iframe hit-testing before the pointer can enter the
1865
- Chromium PDF browsing context. The body flag is set synchronously by the
1866
- drag source, avoiding React render timing entirely. */
1867
- :global(body[data-dsh-tab-dragging]) .editorPdfFrame {
1868
- pointer-events: none !important;
1869
- }
1870
-
1871
- :global(body[data-dsh-tab-dragging]) .editorPdfDragShield {
1872
- pointer-events: auto !important;
1873
- }
1874
-
1875
1579
  /* ── Terminal ──────────────────────────────────────────────────────────── */
1876
1580
 
1877
1581
  .terminalWrap {
@@ -2552,10 +2256,7 @@ body[data-dsh-sidebar-dragging] .panel {
2552
2256
  .gitDiffFile:focus-visible,
2553
2257
  .gitDiffExpand:focus-visible,
2554
2258
  .terminalRetry:focus-visible,
2555
- .editorModeButton:focus-visible,
2556
- .editorDownloadLink:focus-visible,
2557
- .editorPptxButton:focus-visible,
2558
- .editorDocxZoomRange:focus-visible {
2259
+ .editorDownloadLink:focus-visible {
2559
2260
  outline: 2px solid var(--dsw-alias-interactive-bg-hover-accent);
2560
2261
  outline-offset: -1px;
2561
2262
  }
@@ -2655,134 +2356,9 @@ body[data-dsh-sidebar-dragging] .panel {
2655
2356
  color: var(--dsw-alias-state-business-primary);
2656
2357
  }
2657
2358
 
2658
- /* ── Markdown preview: raw-HTML runs + table of contents ─────────────────
2659
- The HTML runs lifted out of the markdown source (MarkdownHtml.tsx) render
2660
- as sanitized block leaves; the TOC is a zero-height sticky bar keeping the
2661
- outline button pinned at the preview's top-right without consuming layout.
2662
- All visuals ride the alias tokens (skin system follows automatically). */
2663
-
2664
- /* A sanitized HTML run leaf (block-level box; content is DOMPurify output). */
2665
- .editorHtmlBlock {
2666
- margin: 8px 0;
2667
- }
2668
-
2669
- .editorHtmlBlock img,
2670
- .editorHtmlBlock video {
2671
- max-width: 100%;
2672
- }
2673
-
2674
- .editorHtmlBlock details {
2675
- margin: 4px 0;
2676
- padding: 4px 0;
2677
- }
2678
-
2679
- .editorHtmlBlock summary {
2680
- cursor: pointer;
2681
- }
2682
-
2683
- /* The zero-height sticky bar: occupies no layout, keeps the button visible
2684
- while the preview scrolls, and lets pointer events pass through the bar
2685
- itself (only the interactive children re-enable them). */
2686
- .tocBar {
2687
- position: sticky;
2688
- top: 0;
2689
- z-index: 3;
2690
- display: flex;
2691
- justify-content: flex-end;
2692
- height: 0;
2693
- pointer-events: none;
2694
- }
2695
-
2696
- .tocButton {
2697
- pointer-events: auto;
2698
- display: inline-flex;
2699
- align-items: center;
2700
- justify-content: center;
2701
- width: 26px;
2702
- height: 26px;
2703
- margin: 4px 2px 0 0;
2704
- padding: 0;
2705
- border: 1px solid var(--dsw-alias-border-l1);
2706
- border-radius: 6px;
2707
- background: var(--dsw-alias-bg-layer-1);
2708
- color: var(--dsw-alias-label-secondary);
2709
- cursor: pointer;
2710
- }
2711
-
2712
- .tocButton:hover {
2713
- background: var(--dsw-alias-interactive-bg-hover);
2714
- color: var(--dsw-alias-label-primary);
2715
- }
2716
-
2717
- /* The outline popover, anchored under the button (the bar is the containing
2718
- block; its height is 0, so the panel drops below the pinned button row). */
2719
- .tocPanel {
2720
- position: absolute;
2721
- top: 32px;
2722
- right: 2px;
2723
- display: flex;
2724
- flex-direction: column;
2725
- width: min(300px, 82%);
2726
- max-height: 60vh;
2727
- overflow-y: auto;
2728
- padding: 4px;
2729
- border: 1px solid var(--dsw-alias-border-l1);
2730
- border-radius: 8px;
2731
- background: var(--dsw-alias-bg-layer-2);
2732
- box-shadow: var(--dsw-shadow-lv2);
2733
- pointer-events: auto;
2734
- }
2735
-
2736
- .tocItem {
2737
- display: flex;
2738
- align-items: baseline;
2739
- gap: 8px;
2740
- min-width: 0;
2741
- padding: 4px 8px;
2742
- border: none;
2743
- border-radius: 6px;
2744
- background: transparent;
2745
- color: var(--dsw-alias-label-secondary);
2746
- font: var(--dsw-font-xxs-12);
2747
- text-align: left;
2748
- cursor: pointer;
2749
- }
2750
-
2751
- .tocItem:hover {
2752
- background: var(--dsw-alias-interactive-bg-hover);
2753
- color: var(--dsw-alias-label-primary);
2754
- }
2755
-
2756
- /* Indent by heading level (h2 = one step, h3 = two steps…). */
2757
- .tocItem[data-level='2'] { padding-left: 18px; }
2758
- .tocItem[data-level='3'] { padding-left: 28px; }
2759
- .tocItem[data-level='4'] { padding-left: 38px; }
2760
- .tocItem[data-level='5'] { padding-left: 48px; }
2761
- .tocItem[data-level='6'] { padding-left: 58px; }
2762
-
2763
- /* The tiny level number keeps deep outlines scannable. */
2764
- .tocItemLevel {
2765
- flex: none;
2766
- font: var(--dsw-font-xxxs-11);
2767
- color: var(--dsw-alias-label-tertiary);
2768
- }
2769
-
2770
- .tocItemText {
2771
- flex: 1 1 auto;
2772
- min-width: 0;
2773
- overflow: hidden;
2774
- text-overflow: ellipsis;
2775
- white-space: nowrap;
2776
- }
2777
-
2778
2359
  /* The jumped-to heading flashes once (the class is added to the host-rendered
2779
2360
  heading element by md-toc.tsx; @keyframes rides the CSS module). */
2780
2361
  @keyframes dsh-toc-flash {
2781
2362
  0%, 60% { background: var(--dsw-alias-interactive-bg-hover); }
2782
2363
  100% { background: transparent; }
2783
2364
  }
2784
-
2785
- .tocFlash {
2786
- animation: dsh-toc-flash 1.2s ease-out;
2787
- border-radius: 4px;
2788
- }
package/src/config.ts CHANGED
@@ -134,18 +134,15 @@ export const PrefsSchema: z<SidebarPrefs> = z.object({
134
134
  customCss: z.string(),
135
135
  titleBarCompat: z.boolean().default(false),
136
136
  titleBarStripPx: z.number().step(1).min(TITLE_BAR_STRIP_MIN).max(TITLE_BAR_STRIP_MAX).default(TITLE_BAR_STRIP_DEFAULT),
137
- htmlViewerNoSandbox: z.boolean().default(false),
138
- htmlViewerDefaultUnsafe: z.boolean().default(false),
139
137
  browserNoSandbox: z.boolean().default(false),
140
138
  browserInterceptLinks: z.boolean().default(true),
141
139
  browserInterceptHttp: z.boolean().default(true),
142
140
  browserInterceptHttps: z.boolean().default(false),
143
141
  browserAllowedLoopback: z.string().default(''),
144
- // Per-feature enable switches are OPEN maps (any tab/viewer id, built-in or
142
+ // Per-feature enable switches are OPEN maps (any tab id, built-in or
145
143
  // external): an absent key means enabled, so old documents resolve to {}
146
144
  // (everything on) with no migration. Non-boolean values fail validation.
147
145
  tabsEnabled: z.dict(z.boolean()).default({}),
148
- viewersEnabled: z.dict(z.boolean()).default({}),
149
146
  // Plugin-owned settings blobs (v0.12.0+) are an OPEN nested map: any
150
147
  // descriptor id may carry any JSON-serializable values. This is the
151
148
  // "settings seam" opening — without it the seam would drop third-party
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dsh-coding-sidebar host half: the /sidebar JSON API (explorer listing, file
3
- * read/write, git), the /sidebar/file media route (images), the /sidebar/html
4
- * preview route, the /sidebar/bundle lazy-chunk route (client code splits),
3
+ * read/write, git), the /sidebar/file media route (file bytes / downloads),
4
+ * the /sidebar/bundle lazy-chunk route (client code splits),
5
5
  * and the terminal WebSocket upgrade. Every route passes the same
6
6
  * browser-trust fence as the /api gateway — Host-header loopback or the
7
7
  * web runtime's `trustedHosts` (LAN IP literals sampled at boot plus
@@ -33,7 +33,6 @@ import { parentOf, requireAbsolute, listDirectory, rootLabel } from './fs-tree.t
33
33
  import { writeWorkspaceUpload } from './fs-operations.ts'
34
34
  import { ensureWorkspacePath, ensureWorkspaceWritePath } from './path-security.ts'
35
35
  import { searchFiles } from './fs-search.ts'
36
- import { decodeHtmlUrl } from './html-route.ts'
37
36
  import { extractFrameAncestors } from './browser-probe.ts'
38
37
  import { isTrustedApiRequest, isLoopbackHostname } from './trust-fence.ts'
39
38
  import { registerBundleRoute } from './bundle-route.ts'
@@ -62,15 +61,14 @@ export type { SidebarConfig, ResolvedSidebarConfig }
62
61
  // `ctx.betterSidebar`; the Context re-export below is the vendored cordis
63
62
  // Context intersected with the structural service faces.
64
63
  // Also re-export the service descriptor types so consumers can type their
65
- // registerTab / registerFileViewer arguments without reaching into /client.
64
+ // registerTab arguments without reaching into /client. (v1.0.4: the file
65
+ // viewer types were retired with the file-viewer registry — file preview is
66
+ // the host's job now.)
66
67
  export type { Context } from './context-types.ts'
67
68
  export type {
68
69
  BetterSidebarService,
69
70
  TabDescriptor,
70
71
  TabComponentProps,
71
- FileViewerDescriptor,
72
- FileViewerProps,
73
- FileFetchStrategy,
74
72
  } from './client/service.ts'
75
73
 
76
74
  /** Plugin identity for cordis.yml rows. */
@@ -90,9 +88,6 @@ const MEDIA_TYPES: Record<string, string> = {
90
88
  '.bmp': 'image/bmp',
91
89
  '.ico': 'image/x-icon',
92
90
  '.avif': 'image/avif',
93
- '.pdf': 'application/pdf',
94
- '.html': 'text/html',
95
- '.htm': 'text/html',
96
91
  }
97
92
 
98
93
  /** Content type served by /sidebar/file (binary-safe fallback for unknowns). */
@@ -849,11 +844,11 @@ export function apply(ctx: Context, config?: SidebarConfig): void {
849
844
 
850
845
  // ── Lazy chunk route (client bundle splits) ─────────────────────────────
851
846
  // Serves the client half's split bundles (lib/client-<name>.js) so the
852
- // heavy preview/terminal libraries load on first use, not at page start
847
+ // heavy editor/terminal libraries load on first use, not at page start
853
848
  // (see bundle-route.ts / src/client/chunk-loader.ts).
854
849
  ctx.effect(() => registerBundleRoute(ctx, fence), 'dsh-coding-sidebar: /sidebar/bundle chunk route')
855
850
 
856
- // ── Media route (images for the editor) ─────────────────────────────────
851
+ // ── Media route (file bytes; the binary pane's download link) ───────────
857
852
  ctx.effect(() => ctx.webServer.register({
858
853
  kind: 'prefix',
859
854
  path: '/sidebar/file',
@@ -895,68 +890,6 @@ export function apply(ctx: Context, config?: SidebarConfig): void {
895
890
  },
896
891
  }), 'dsh-coding-sidebar: /sidebar/file media route')
897
892
 
898
- // ── HTML preview route (sandboxed HTML + its relative assets) ───────────
899
- // Serves files under the session cwd for the built-in HTML previewer. The
900
- // URL is path-encoded (see html-route.ts) so the previewed page's relative
901
- // assets (./style.css, img/x.png) resolve back into this route with the
902
- // session scope intact — a query-encoded URL would drop the scope when the
903
- // browser resolves relatives. Every response carries the CSP `sandbox`
904
- // directive: inside the editor's iframe the sandbox ATTRIBUTE is the
905
- // boundary, this header is defense-in-depth so even a top-level load of
906
- // the URL (e.g. a popup opened by a previewed page) stays in an opaque
907
- // origin with no same-origin access to the GUI.
908
- ctx.effect(() => ctx.webServer.register({
909
- kind: 'prefix',
910
- path: '/sidebar/html',
911
- handler: async (req, res) => {
912
- if (!fence(req)) {
913
- res.writeHead(403)
914
- res.end('forbidden')
915
- return
916
- }
917
- if (req.method !== 'GET') {
918
- res.writeHead(405)
919
- res.end()
920
- return
921
- }
922
- try {
923
- const url = new URL(req.url ?? '/', 'http://dsh.internal')
924
- const decoded = decodeHtmlUrl(url.pathname)
925
- if (!decoded.ok) {
926
- writeError(res, new SidebarError('bad-request', decoded.message, decoded.status))
927
- return
928
- }
929
- const { sessionId, path } = decoded.ref
930
- // The session's authoritative cwd (client cwd cannot ride in the URL
931
- // — the path encoding has no query; a detached first request falls
932
- // back to the process cwd and is normally refused by the workspace
933
- // real-path guard, with the same semantics as the media route's
934
- // fallback.
935
- const cwd = await sessionCwdOf(ctx, sessionId)
936
- const absolute = await ensureWorkspacePath(cwd, path)
937
- const info = await stat(absolute)
938
- if (!info.isFile() || info.size > resolved.mediaLimit) {
939
- throw new SidebarError('fs-error', 'not a file or too large', 400)
940
- }
941
- const type = mediaTypeForPath(absolute)
942
- const body = await readFile(absolute)
943
- res.writeHead(200, {
944
- 'content-type': type === 'text/html' ? 'text/html; charset=utf-8' : type,
945
- 'cache-control': 'no-cache',
946
- 'x-content-type-options': 'nosniff',
947
- 'referrer-policy': 'no-referrer',
948
- // The sandbox directive (no allow-same-origin → opaque origin) is
949
- // the previewer's security boundary even for top-level loads;
950
- // object-src 'none' blocks plugin embeds.
951
- 'content-security-policy': "sandbox allow-scripts allow-popups allow-downloads allow-modals; object-src 'none'",
952
- })
953
- res.end(body)
954
- } catch (error) {
955
- writeError(res, error)
956
- }
957
- },
958
- }), 'dsh-coding-sidebar: /sidebar/html preview route')
959
-
960
893
  // ── Terminal WebSocket ──────────────────────────────────────────────────
961
894
  // One upgrade endpoint serves both UI-tab terminals (?tab=...) and
962
895
  // agent-owned terminals (?uuid=...). The two paths attach to different
@@ -126,22 +126,6 @@ export interface SidebarPrefs {
126
126
  * starts `strip` px below its top edge.
127
127
  */
128
128
  titleBarStripPx: number
129
- /**
130
- * Whether the HTML previewer drops its sandboxed iframe. Sandbox ON (the
131
- * default) renders previewed HTML in an opaque-origin iframe that cannot
132
- * touch the GUI; turning it OFF runs the previewed page with the GUI's
133
- * own origin — full read/write access to session files and internal
134
- * APIs. Only for trusted local content; the setting copy warns.
135
- */
136
- htmlViewerNoSandbox: boolean
137
- /**
138
- * Whether a newly opened HTML preview starts UNSANDBOXED (the per-surface
139
- * temporary unlock pre-applied). Off by default: previews open sandboxed
140
- * and the status row offers the one-tap unlock; when on, previews open
141
- * in the red unsandboxed state and the status row offers a one-tap
142
- * restore for the current file.
143
- */
144
- htmlViewerDefaultUnsafe: boolean
145
129
  /**
146
130
  * Whether the browser tab drops its sandboxed iframe. Sandbox ON (the
147
131
  * default) keeps browsed sites in an opaque origin with no GUI access;
@@ -196,16 +180,9 @@ export interface SidebarPrefs {
196
180
  * own layouts" rule.
197
181
  */
198
182
  tabsEnabled: Record<string, boolean>
199
- /**
200
- * Per-viewer enable switches, keyed by file viewer descriptor id
201
- * (`'image'`, `'my-plugin:csv'`). An ABSENT key means enabled; a disabled
202
- * viewer is skipped by `matchFileViewer` so files fall through to the
203
- * next matching viewer (or the download button when none match).
204
- */
205
- viewersEnabled: Record<string, boolean>
206
183
  /**
207
184
  * Plugin-owned settings blobs (v0.12.0+), keyed by descriptor id: each
208
- * registered tab/viewer that declares `settings.pluginToggles` (or writes
185
+ * registered tab that declares `settings.pluginToggles` (or writes
209
186
  * through `settings.render`'s `updatePluginSetting`) persists its values
210
187
  * here — an open map, so third-party keys need no host PrefsSchema field.
211
188
  * Values are JSON-serializable (the row controls produce strings /
@@ -252,15 +229,12 @@ export const SIDEBAR_PREFS_DEFAULTS: SidebarPrefs = {
252
229
  customCss: '',
253
230
  titleBarCompat: false,
254
231
  titleBarStripPx: TITLE_BAR_STRIP_DEFAULT,
255
- htmlViewerNoSandbox: false,
256
- htmlViewerDefaultUnsafe: false,
257
232
  browserNoSandbox: false,
258
233
  browserInterceptLinks: true,
259
234
  browserInterceptHttp: true,
260
235
  browserInterceptHttps: false,
261
236
  browserAllowedLoopback: '',
262
237
  tabsEnabled: {},
263
- viewersEnabled: {},
264
238
  pluginSettings: {},
265
239
  }
266
240