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.
- package/README.md +27 -42
- package/lib/client-editor.js +315 -4179
- package/lib/client-registry.js +913 -1303
- package/lib/client-terminal.js +189 -261
- package/lib/client.js +911 -1301
- package/lib/index.js +13 -110
- package/lib/types/bundle-route.d.ts +1 -1
- package/lib/types/client/LiveView.d.ts +1 -0
- package/lib/types/client/SideCardSection.d.ts +3 -3
- package/lib/types/client/TextEditor.d.ts +20 -10
- package/lib/types/client/add-plugin-modal.d.ts +0 -4
- package/lib/types/client/api.d.ts +16 -11
- package/lib/types/client/builtins/index.d.ts +10 -10
- package/lib/types/client/chunk-loader.d.ts +7 -8
- package/lib/types/client/locales.d.ts +6 -26
- package/lib/types/client/native-avoid.d.ts +2 -0
- package/lib/types/client/plugins-shared.d.ts +6 -6
- package/lib/types/client/service.d.ts +13 -83
- package/lib/types/index.d.ts +1 -1
- package/lib/types/prefs-shared.d.ts +1 -24
- package/lib/types/wire.d.ts +1 -1
- package/package.json +26 -29
- package/src/bundle-route.ts +1 -1
- package/src/client/BrowserView.tsx +18 -2
- package/src/client/EditorHost.tsx +52 -122
- package/src/client/LiveView.tsx +229 -0
- package/src/client/SandboxStatusBar.tsx +12 -9
- package/src/client/SideCardSection.tsx +28 -81
- package/src/client/TextEditor.tsx +89 -266
- package/src/client/add-plugin-modal.tsx +16 -24
- package/src/client/api.ts +4 -25
- package/src/client/builtins/index.ts +10 -14
- package/src/client/chunk-loader.ts +7 -8
- package/src/client/index.tsx +12 -3
- package/src/client/locales-ar.ts +0 -6
- package/src/client/locales-de.ts +0 -6
- package/src/client/locales-fr.ts +0 -6
- package/src/client/locales-hi.ts +0 -6
- package/src/client/locales-id.ts +0 -6
- package/src/client/locales-it.ts +0 -6
- package/src/client/locales-ja.ts +0 -6
- package/src/client/locales-ko.ts +0 -6
- package/src/client/locales-nl.ts +0 -6
- package/src/client/locales-pl.ts +0 -6
- package/src/client/locales-pt.ts +0 -6
- package/src/client/locales-ru.ts +0 -6
- package/src/client/locales-sv.ts +0 -6
- package/src/client/locales-th.ts +0 -6
- package/src/client/locales-tr.ts +0 -6
- package/src/client/locales-vi.ts +0 -6
- package/src/client/locales-zh-HK.ts +0 -6
- package/src/client/locales-zh-MO.ts +0 -6
- package/src/client/locales-zh-TW.ts +0 -6
- package/src/client/locales.ts +12 -52
- package/src/client/native-avoid.ts +155 -0
- package/src/client/plugins-shared.ts +6 -6
- package/src/client/prefs.ts +0 -7
- package/src/client/service.ts +13 -150
- package/src/client/sidebar.module.css +28 -420
- package/src/config.ts +1 -4
- package/src/index.ts +17 -74
- package/src/prefs-shared.ts +1 -27
- package/src/wire.ts +1 -0
- package/lib/client-mermaid.js +0 -200776
- package/lib/types/client/MarkdownHtml.d.ts +0 -32
- package/lib/types/client/PdfView.d.ts +0 -6
- package/lib/types/client/builtins/viewers.d.ts +0 -3
- package/lib/types/client/chunks/mermaid.d.ts +0 -10
- package/lib/types/client/editor-load.d.ts +0 -66
- package/lib/types/client/image-types.d.ts +0 -3
- package/lib/types/client/markdown-html.d.ts +0 -96
- package/lib/types/client/markdown-images.d.ts +0 -45
- package/lib/types/client/md-toc.d.ts +0 -4
- package/lib/types/client/mermaid-blocks.d.ts +0 -47
- package/lib/types/client/mermaid-sanitize.d.ts +0 -2
- package/lib/types/client/mermaid.d.ts +0 -12
- package/lib/types/client/pdf-types.d.ts +0 -2
- package/lib/types/client/plugins-viewers.d.ts +0 -3
- package/lib/types/html-route.d.ts +0 -59
- package/src/client/MarkdownHtml.tsx +0 -296
- package/src/client/PdfView.tsx +0 -110
- package/src/client/builtins/viewers.tsx +0 -125
- package/src/client/chunks/mermaid.tsx +0 -10
- package/src/client/editor-load.ts +0 -92
- package/src/client/image-types.ts +0 -8
- package/src/client/markdown-html.ts +0 -331
- package/src/client/markdown-images.ts +0 -137
- package/src/client/md-toc.tsx +0 -127
- package/src/client/mermaid-blocks.ts +0 -110
- package/src/client/mermaid-sanitize.ts +0 -93
- package/src/client/mermaid.tsx +0 -406
- package/src/client/pdf-types.ts +0 -4
- package/src/client/plugins-viewers.ts +0 -30
- 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
|
-
.
|
|
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
|
-
/*
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
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
|
-
/*
|
|
2718
|
-
|
|
2719
|
-
.tocPanel {
|
|
2366
|
+
/* ═══ Agent 浏览器实况(CDP screencast,2026-09-12 方案 2)═══ */
|
|
2367
|
+
.browserLive {
|
|
2720
2368
|
position: absolute;
|
|
2721
|
-
|
|
2722
|
-
right: 2px;
|
|
2369
|
+
inset: 0;
|
|
2723
2370
|
display: flex;
|
|
2724
2371
|
flex-direction: column;
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
overflow
|
|
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:
|
|
2378
|
+
align-items: center;
|
|
2739
2379
|
gap: 8px;
|
|
2740
|
-
|
|
2741
|
-
|
|
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
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
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
|
-
|
|
2786
|
-
|
|
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
|
|
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 (
|
|
4
|
-
*
|
|
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
|
|
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
|
|
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 (
|
|
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
|