dsh-coding-sidebar 1.0.3 → 1.0.5

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 (94) hide show
  1. package/README.md +27 -42
  2. package/lib/client-editor.js +315 -4179
  3. package/lib/client-registry.js +913 -1303
  4. package/lib/client-terminal.js +189 -261
  5. package/lib/client.js +911 -1301
  6. package/lib/index.js +13 -110
  7. package/lib/types/bundle-route.d.ts +1 -1
  8. package/lib/types/client/LiveView.d.ts +1 -0
  9. package/lib/types/client/SideCardSection.d.ts +3 -3
  10. package/lib/types/client/TextEditor.d.ts +20 -10
  11. package/lib/types/client/add-plugin-modal.d.ts +0 -4
  12. package/lib/types/client/api.d.ts +16 -11
  13. package/lib/types/client/builtins/index.d.ts +10 -10
  14. package/lib/types/client/chunk-loader.d.ts +7 -8
  15. package/lib/types/client/locales.d.ts +6 -26
  16. package/lib/types/client/native-avoid.d.ts +2 -0
  17. package/lib/types/client/plugins-shared.d.ts +6 -6
  18. package/lib/types/client/service.d.ts +13 -83
  19. package/lib/types/index.d.ts +1 -1
  20. package/lib/types/prefs-shared.d.ts +1 -24
  21. package/lib/types/wire.d.ts +1 -1
  22. package/package.json +26 -29
  23. package/src/bundle-route.ts +1 -1
  24. package/src/client/BrowserView.tsx +18 -2
  25. package/src/client/EditorHost.tsx +52 -122
  26. package/src/client/LiveView.tsx +229 -0
  27. package/src/client/SandboxStatusBar.tsx +12 -9
  28. package/src/client/SideCardSection.tsx +28 -81
  29. package/src/client/TextEditor.tsx +89 -266
  30. package/src/client/add-plugin-modal.tsx +16 -24
  31. package/src/client/api.ts +4 -25
  32. package/src/client/builtins/index.ts +10 -14
  33. package/src/client/chunk-loader.ts +7 -8
  34. package/src/client/index.tsx +12 -3
  35. package/src/client/locales-ar.ts +0 -6
  36. package/src/client/locales-de.ts +0 -6
  37. package/src/client/locales-fr.ts +0 -6
  38. package/src/client/locales-hi.ts +0 -6
  39. package/src/client/locales-id.ts +0 -6
  40. package/src/client/locales-it.ts +0 -6
  41. package/src/client/locales-ja.ts +0 -6
  42. package/src/client/locales-ko.ts +0 -6
  43. package/src/client/locales-nl.ts +0 -6
  44. package/src/client/locales-pl.ts +0 -6
  45. package/src/client/locales-pt.ts +0 -6
  46. package/src/client/locales-ru.ts +0 -6
  47. package/src/client/locales-sv.ts +0 -6
  48. package/src/client/locales-th.ts +0 -6
  49. package/src/client/locales-tr.ts +0 -6
  50. package/src/client/locales-vi.ts +0 -6
  51. package/src/client/locales-zh-HK.ts +0 -6
  52. package/src/client/locales-zh-MO.ts +0 -6
  53. package/src/client/locales-zh-TW.ts +0 -6
  54. package/src/client/locales.ts +12 -52
  55. package/src/client/native-avoid.ts +155 -0
  56. package/src/client/plugins-shared.ts +6 -6
  57. package/src/client/prefs.ts +0 -7
  58. package/src/client/service.ts +13 -150
  59. package/src/client/sidebar.module.css +28 -420
  60. package/src/config.ts +1 -4
  61. package/src/index.ts +17 -74
  62. package/src/prefs-shared.ts +1 -27
  63. package/src/wire.ts +1 -0
  64. package/lib/client-mermaid.js +0 -200776
  65. package/lib/types/client/MarkdownHtml.d.ts +0 -32
  66. package/lib/types/client/PdfView.d.ts +0 -6
  67. package/lib/types/client/builtins/viewers.d.ts +0 -3
  68. package/lib/types/client/chunks/mermaid.d.ts +0 -10
  69. package/lib/types/client/editor-load.d.ts +0 -66
  70. package/lib/types/client/image-types.d.ts +0 -3
  71. package/lib/types/client/markdown-html.d.ts +0 -96
  72. package/lib/types/client/markdown-images.d.ts +0 -45
  73. package/lib/types/client/md-toc.d.ts +0 -4
  74. package/lib/types/client/mermaid-blocks.d.ts +0 -47
  75. package/lib/types/client/mermaid-sanitize.d.ts +0 -2
  76. package/lib/types/client/mermaid.d.ts +0 -12
  77. package/lib/types/client/pdf-types.d.ts +0 -2
  78. package/lib/types/client/plugins-viewers.d.ts +0 -3
  79. package/lib/types/html-route.d.ts +0 -59
  80. package/src/client/MarkdownHtml.tsx +0 -296
  81. package/src/client/PdfView.tsx +0 -110
  82. package/src/client/builtins/viewers.tsx +0 -125
  83. package/src/client/chunks/mermaid.tsx +0 -10
  84. package/src/client/editor-load.ts +0 -92
  85. package/src/client/image-types.ts +0 -8
  86. package/src/client/markdown-html.ts +0 -331
  87. package/src/client/markdown-images.ts +0 -137
  88. package/src/client/md-toc.tsx +0 -127
  89. package/src/client/mermaid-blocks.ts +0 -110
  90. package/src/client/mermaid-sanitize.ts +0 -93
  91. package/src/client/mermaid.tsx +0 -406
  92. package/src/client/pdf-types.ts +0 -4
  93. package/src/client/plugins-viewers.ts +0 -30
  94. 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,41 @@ 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);
2359
+ /* The jumped-to heading flashes once (the class is added to the host-rendered
2360
+ heading element by md-toc.tsx; @keyframes rides the CSS module). */
2361
+ @keyframes dsh-toc-flash {
2362
+ 0%, 60% { background: var(--dsw-alias-interactive-bg-hover); }
2363
+ 100% { background: transparent; }
2715
2364
  }
2716
2365
 
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 {
2366
+ /* ═══ Agent 浏览器实况(CDP screencast,2026-09-12 方案 2)═══ */
2367
+ .browserLive {
2720
2368
  position: absolute;
2721
- top: 32px;
2722
- right: 2px;
2369
+ inset: 0;
2723
2370
  display: flex;
2724
2371
  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;
2372
+ gap: 6px;
2373
+ padding: 8px 10px;
2374
+ overflow: hidden;
2734
2375
  }
2735
-
2736
- .tocItem {
2376
+ .browserLiveStatus {
2737
2377
  display: flex;
2738
- align-items: baseline;
2378
+ align-items: center;
2739
2379
  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;
2380
+ font-size: 12px;
2381
+ opacity: 0.75;
2776
2382
  }
2777
-
2778
- /* The jumped-to heading flashes once (the class is added to the host-rendered
2779
- heading element by md-toc.tsx; @keyframes rides the CSS module). */
2780
- @keyframes dsh-toc-flash {
2781
- 0%, 60% { background: var(--dsw-alias-interactive-bg-hover); }
2782
- 100% { background: transparent; }
2383
+ .browserLiveCanvas {
2384
+ flex: 1;
2385
+ min-height: 0;
2386
+ width: 100%;
2387
+ object-fit: contain;
2388
+ border: 1px solid var(--dsw-alias-border-l, rgba(128, 128, 128, 0.25));
2389
+ border-radius: 8px;
2390
+ background: #fff;
2391
+ cursor: crosshair;
2783
2392
  }
2784
-
2785
- .tocFlash {
2786
- animation: dsh-toc-flash 1.2s ease-out;
2787
- border-radius: 4px;
2393
+ .browserLiveTarget {
2394
+ max-width: 260px;
2395
+ font-size: 12px;
2788
2396
  }
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). */
@@ -522,6 +517,16 @@ function buildApi(
522
517
  // probe is display-only (headers back to the caller), restricted to
523
518
  // http(s) non-loopback URLs with a hard timeout, and gated by the same
524
519
  // trust fence as every other route — a cross-site page cannot reach it.
520
+ 'cdp.targets': async () => {
521
+ // Agent 浏览器宿主(KCoder 桌面端维护,固定 127.0.0.1:9223)的
522
+ // page target 列表。host 侧代理:CDP 端点无 CORS 头,renderer 直连
523
+ // /json/list 会被拦,WS(screencast/输入)则由 renderer 直连。
524
+ // 端口/地址是编译期常量,无用户输入,无 SSRF 面。
525
+ const res = await fetch('http://127.0.0.1:9223/json/list', { signal: AbortSignal.timeout(3000) })
526
+ if (!res.ok) throw new SidebarError('cdp-down', `browser host unreachable (${res.status})`, 502)
527
+ const list = (await res.json()) as Array<{ id: string; type: string; title: string; url: string }>
528
+ return { targets: list.filter((t) => t.type === 'page').map((t) => ({ id: t.id, url: t.url, title: t.title })) }
529
+ },
525
530
  'browser.probe': async (payload) => {
526
531
  const raw = requireString(payload, 'url')
527
532
  let parsed: URL
@@ -849,11 +854,11 @@ export function apply(ctx: Context, config?: SidebarConfig): void {
849
854
 
850
855
  // ── Lazy chunk route (client bundle splits) ─────────────────────────────
851
856
  // 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
857
+ // heavy editor/terminal libraries load on first use, not at page start
853
858
  // (see bundle-route.ts / src/client/chunk-loader.ts).
854
859
  ctx.effect(() => registerBundleRoute(ctx, fence), 'dsh-coding-sidebar: /sidebar/bundle chunk route')
855
860
 
856
- // ── Media route (images for the editor) ─────────────────────────────────
861
+ // ── Media route (file bytes; the binary pane's download link) ───────────
857
862
  ctx.effect(() => ctx.webServer.register({
858
863
  kind: 'prefix',
859
864
  path: '/sidebar/file',
@@ -895,68 +900,6 @@ export function apply(ctx: Context, config?: SidebarConfig): void {
895
900
  },
896
901
  }), 'dsh-coding-sidebar: /sidebar/file media route')
897
902
 
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
903
  // ── Terminal WebSocket ──────────────────────────────────────────────────
961
904
  // One upgrade endpoint serves both UI-tab terminals (?tab=...) and
962
905
  // agent-owned terminals (?uuid=...). The two paths attach to different