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.
- package/README.md +27 -42
- package/lib/client-editor.js +297 -4179
- package/lib/client-registry.js +639 -1341
- package/lib/client-terminal.js +176 -266
- package/lib/client.js +640 -1342
- package/lib/index.js +4 -110
- package/lib/types/bundle-route.d.ts +1 -1
- 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 +8 -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 +0 -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/package.json +26 -29
- package/src/bundle-route.ts +1 -1
- package/src/client/EditorHost.tsx +52 -122
- 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 +1 -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 +0 -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 +1 -425
- package/src/config.ts +1 -4
- package/src/index.ts +7 -74
- package/src/prefs-shared.ts +1 -27
- 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,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
|
|
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). */
|
|
@@ -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
|
|
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 (
|
|
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
|
package/src/prefs-shared.ts
CHANGED
|
@@ -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
|
|
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
|
|