@aryacode/dsh-aryacode-polish 0.8.1 → 0.9.6
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/CHANGELOG.md +71 -3
- package/README.md +2 -1
- package/client/face.js +128 -110
- package/client/switches.json +6 -0
- package/client/workspace.js +588 -0
- package/docs/better-workspace.md +51 -0
- package/entries/registry.js +6 -0
- package/lib/client.js +723 -111
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,80 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.6] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- A workspace whose Host title merely repeats its id is humanised, because that is
|
|
8
|
+
what the default workspace's title is.
|
|
9
|
+
|
|
10
|
+
## [0.9.5] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Better workspace carries the add control the stock browser had: a header with a
|
|
15
|
+
"+" that opens this bundle's own source menu (On this PC or a GitHub repository)
|
|
16
|
+
and adds the chosen folder as a workspace.
|
|
17
|
+
- The default workspace no longer shows its raw id: an empty Host `title` is
|
|
18
|
+
humanised ("default-workspace" reads "Default workspace").
|
|
19
|
+
|
|
20
|
+
## [0.9.4] - 2026-10-28
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- The seat's `inject()` is called with the standard props the runtime captured, so the
|
|
25
|
+
menu renders again after 0.9.3 referenced a name that only lived in the factory.
|
|
26
|
+
|
|
27
|
+
## [0.9.3] - 2026-10-28
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Each menu seat's own `inject()` runs before its occupant renders, so this bundle's
|
|
32
|
+
"Open workspace folder" row is back beside the shipped rows instead of silently
|
|
33
|
+
rendering nothing for want of the services it declared.
|
|
34
|
+
|
|
35
|
+
## [0.9.2] - 2026-10-28
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- The shipped session menu rows (Pin, Rename, Fork, Archive) now receive the standard
|
|
40
|
+
hooks they call -- `usePinned`, `useArchived`, `useSessionAccess` and `useShortcuts` --
|
|
41
|
+
derived from the snapshots this seat already subscribes to, instead of throwing
|
|
42
|
+
`TypeError: usePinned is not a function` inside `usePinState`.
|
|
43
|
+
|
|
44
|
+
## [0.9.1] - 2026-10-28
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Better workspace reads the session list as `{ ids, byId }` and keeps the Host order;
|
|
49
|
+
it no longer re-sorts on a timestamp `SessionSummary` does not carry.
|
|
50
|
+
|
|
51
|
+
## [0.9.0] - 2026-10-28
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Better workspace (`betterWorkspace`): our own sidebar browser registered into
|
|
56
|
+
`sidebar.workspaces` at `priority: -1`, with the shipped session menu rendered
|
|
57
|
+
inside it and a right-click menu on every workspace row (open, copy path, remove).
|
|
58
|
+
See `docs/better-workspace.md` for why this is a takeover rather than a menu item.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## [0.8.2] - 2026-10-28
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- The camera floated over the panel with metrics of its own, so it read as a
|
|
66
|
+
foreign control. It now mounts into the docking kit's chrome seat beside the
|
|
67
|
+
Shell's own presentation and collapse buttons, wearing that cluster's 28px box,
|
|
68
|
+
15px glyph, hover state and tooltip, and it follows the strip through docked,
|
|
69
|
+
split and fullscreen layouts.
|
|
70
|
+
|
|
3
71
|
## [0.8.1] - 2026-10-28
|
|
4
72
|
|
|
5
73
|
### Changed
|
|
6
74
|
|
|
7
|
-
- The camera moved out of the composer row and
|
|
8
|
-
|
|
9
|
-
|
|
75
|
+
- The camera moved out of the composer row and next to the right panel's own
|
|
76
|
+
controls. The composer dock entry stays, invisibly, to publish the session
|
|
77
|
+
context the button needs.
|
|
10
78
|
|
|
11
79
|
## [0.8.0] - 2026-10-28
|
|
12
80
|
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ installed into and patches nothing. It is one package, one Plugins row, and one
|
|
|
7
7
|
switch per feature, so a refinement that turns out to be wrong for a machine can
|
|
8
8
|
be turned off without uninstalling anything.
|
|
9
9
|
|
|
10
|
-
Status: **
|
|
10
|
+
Status: **seven features shipped.** The version is the package's, and the card
|
|
11
11
|
header already shows it.
|
|
12
12
|
|
|
13
13
|
## What it is
|
|
@@ -30,6 +30,7 @@ Configure page automatically.
|
|
|
30
30
|
|
|
31
31
|
| Switch | Default | What it does |
|
|
32
32
|
| --- | --- | --- |
|
|
33
|
+
| Better workspace | On | Replaces the sidebar workspace list with our own tree and a right-click menu on each workspace. |
|
|
33
34
|
| **Estimated session cost** (`sessionCost`) | On | A `≈$0.39` pill in the composer dock that opens a breakdown card — cache hit, uncached input, cached input, output, requests, and the estimate — priced from the session's recorded usage at the published DeepSeek rates. |
|
|
34
35
|
| **Account balance** (`accountBalance`) | On | The account's remaining wallet (`$17.33`) in the sidebar foot, above the account row. Renders nothing when the Host exposes no wallet, so it never shows a number nobody gave it. |
|
|
35
36
|
| **Open workspace folder** (`openWorkspaceFolder`) | On | An **Open workspace folder** row in a Session's "…" menu that opens the Session's workspace in Explorer/Finder. Renders nothing when the Session has no workspace or the Host cannot open a path. See [Open a workspace folder](docs/workspace-folder.md) for why the request had to land here instead of on the workspace row itself. |
|
package/client/face.js
CHANGED
|
@@ -1572,7 +1572,6 @@ function shotName() {
|
|
|
1572
1572
|
+ '-' + pad(now.getHours()) + pad(now.getMinutes()) + pad(now.getSeconds()) + '.png'
|
|
1573
1573
|
}
|
|
1574
1574
|
|
|
1575
|
-
const SHOT_GLYPH_STYLE = { width: '14px', height: '14px', flex: 'none', display: 'block' }
|
|
1576
1575
|
|
|
1577
1576
|
/**
|
|
1578
1577
|
* Camera glyph, drawn here so the face depends on no icon package.
|
|
@@ -1581,51 +1580,94 @@ const SHOT_GLYPH_STYLE = { width: '14px', height: '14px', flex: 'none', display:
|
|
|
1581
1580
|
function CameraGlyph() {
|
|
1582
1581
|
return React.createElement('svg', {
|
|
1583
1582
|
viewBox: '0 0 16 16',
|
|
1584
|
-
width:
|
|
1585
|
-
height:
|
|
1583
|
+
width: 15,
|
|
1584
|
+
height: 15,
|
|
1585
|
+
fill: 'none',
|
|
1586
|
+
stroke: 'currentColor',
|
|
1587
|
+
strokeWidth: 1,
|
|
1588
|
+
strokeLinecap: 'round',
|
|
1589
|
+
strokeLinejoin: 'round',
|
|
1590
|
+
'aria-hidden': 'true',
|
|
1591
|
+
},
|
|
1592
|
+
React.createElement('path', { d: 'M2.6 5.4h1.9l1-1.5h5l1 1.5h1.9a1 1 0 0 1 1 1v5.7a1 1 0 0 1-1 1H2.6a1 1 0 0 1-1-1V6.4a1 1 0 0 1 1-1Z' }),
|
|
1593
|
+
React.createElement('circle', { cx: 8, cy: 9.3, r: 2.5 }))
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/** Confirmation tick, drawn at the strip's own glyph size. */
|
|
1597
|
+
function CheckGlyph() {
|
|
1598
|
+
return React.createElement('svg', {
|
|
1599
|
+
viewBox: '0 0 16 16',
|
|
1600
|
+
width: 15,
|
|
1601
|
+
height: 15,
|
|
1602
|
+
fill: 'none',
|
|
1603
|
+
stroke: 'currentColor',
|
|
1604
|
+
strokeWidth: 1.4,
|
|
1605
|
+
strokeLinecap: 'round',
|
|
1606
|
+
strokeLinejoin: 'round',
|
|
1607
|
+
'aria-hidden': 'true',
|
|
1608
|
+
},
|
|
1609
|
+
React.createElement('path', { d: 'M3.4 8.6l3 3 6.2-6.6' }))
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
/** Refusal mark, drawn at the strip's own glyph size. */
|
|
1613
|
+
function BangGlyph() {
|
|
1614
|
+
return React.createElement('svg', {
|
|
1615
|
+
viewBox: '0 0 16 16',
|
|
1616
|
+
width: 15,
|
|
1617
|
+
height: 15,
|
|
1586
1618
|
fill: 'none',
|
|
1587
1619
|
stroke: 'currentColor',
|
|
1588
|
-
strokeWidth: 1.
|
|
1620
|
+
strokeWidth: 1.4,
|
|
1589
1621
|
strokeLinecap: 'round',
|
|
1590
1622
|
strokeLinejoin: 'round',
|
|
1591
1623
|
'aria-hidden': 'true',
|
|
1592
|
-
style: SHOT_GLYPH_STYLE,
|
|
1593
1624
|
},
|
|
1594
|
-
React.createElement('path', { d: '
|
|
1595
|
-
React.createElement('
|
|
1625
|
+
React.createElement('path', { d: 'M8 3.6v5.2' }),
|
|
1626
|
+
React.createElement('path', { d: 'M8 12v.4' }))
|
|
1596
1627
|
}
|
|
1597
1628
|
|
|
1598
1629
|
/**
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1630
|
+
* The Shell has no header slot for the panel, so the camera rides the docking
|
|
1631
|
+
* kit's chrome seat: the element at the end of the top-right pane's tab strip
|
|
1632
|
+
* that already holds the presentation and collapse controls. Portalling into
|
|
1633
|
+
* that element, instead of floating over the panel at fixed coordinates, is what
|
|
1634
|
+
* makes the button share the cluster's gap, its vertical centring and whatever
|
|
1635
|
+
* geometry the strip has docked, fullscreened or split.
|
|
1601
1636
|
*/
|
|
1602
|
-
const
|
|
1603
|
-
const SHOT_ANCHOR_BUTTON = 28
|
|
1604
|
-
const SHOT_ANCHOR_CONTROLS = 96
|
|
1637
|
+
const SHOT_CHROME_SELECTOR = '[data-dockkit-strip-chrome]'
|
|
1605
1638
|
|
|
1606
1639
|
/**
|
|
1607
|
-
*
|
|
1608
|
-
*
|
|
1640
|
+
* Mirrors the Shell's own strip control: a 28px box with 6px padding around a
|
|
1641
|
+
* 15px glyph, secondary label colour, transparent until hovered.
|
|
1609
1642
|
*/
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1643
|
+
const SHOT_BUTTON_STYLE = {
|
|
1644
|
+
display: 'inline-flex',
|
|
1645
|
+
flex: 'none',
|
|
1646
|
+
alignItems: 'center',
|
|
1647
|
+
justifyContent: 'center',
|
|
1648
|
+
width: '28px',
|
|
1649
|
+
height: '28px',
|
|
1650
|
+
padding: '6px',
|
|
1651
|
+
color: 'var(--dsw-alias-label-secondary, #61666b)',
|
|
1652
|
+
lineHeight: 1,
|
|
1653
|
+
background: 'transparent',
|
|
1654
|
+
border: 'none',
|
|
1655
|
+
borderRadius: 'var(--dsw-radius-sm, 6px)',
|
|
1656
|
+
cursor: 'pointer',
|
|
1657
|
+
// Ahead of the Shell's own two controls: the last thing you would reach for,
|
|
1658
|
+
// and never between them and the panel edge.
|
|
1659
|
+
order: -1,
|
|
1619
1660
|
}
|
|
1620
1661
|
|
|
1621
1662
|
/**
|
|
1622
|
-
* The composer context the
|
|
1663
|
+
* The composer context the chrome button attaches through.
|
|
1623
1664
|
*
|
|
1624
|
-
* The camera
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1665
|
+
* The camera renders inside the panel strip, which is root chrome, but attaching
|
|
1666
|
+
* an image needs the *session-scoped* composer props (`sessionId` and
|
|
1667
|
+
* `inputActions`), which only a session slot receives. The composer dock is
|
|
1627
1668
|
* therefore kept as an invisible publisher and this store carries its context up
|
|
1628
|
-
* to the
|
|
1669
|
+
* to the chrome button; with no open Session the store is empty and the button
|
|
1670
|
+
* does not render.
|
|
1629
1671
|
*/
|
|
1630
1672
|
const shotStore = (() => {
|
|
1631
1673
|
const listeners = new Set()
|
|
@@ -1643,24 +1685,8 @@ const shotStore = (() => {
|
|
|
1643
1685
|
}
|
|
1644
1686
|
})()
|
|
1645
1687
|
|
|
1646
|
-
const SHOT_BUTTON_STYLE = {
|
|
1647
|
-
position: 'fixed',
|
|
1648
|
-
display: 'inline-flex',
|
|
1649
|
-
alignItems: 'center',
|
|
1650
|
-
justifyContent: 'center',
|
|
1651
|
-
width: SHOT_ANCHOR_BUTTON + 'px',
|
|
1652
|
-
height: SHOT_ANCHOR_BUTTON + 'px',
|
|
1653
|
-
padding: '0',
|
|
1654
|
-
border: 'none',
|
|
1655
|
-
borderRadius: 'var(--dsw-radius-sm, 6px)',
|
|
1656
|
-
background: 'transparent',
|
|
1657
|
-
color: 'var(--dsw-alias-label-tertiary, #61666b)',
|
|
1658
|
-
cursor: 'pointer',
|
|
1659
|
-
zIndex: 40,
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
1688
|
/**
|
|
1663
|
-
* Invisible composer-dock entry that publishes the session context the
|
|
1689
|
+
* Invisible composer-dock entry that publishes the session context the chrome
|
|
1664
1690
|
* button needs, and nothing else.
|
|
1665
1691
|
* @param props - session slot props (`sessionId`, `inputActions`) plus the
|
|
1666
1692
|
* late `conversation` lookup.
|
|
@@ -1679,29 +1705,35 @@ function PanelShotContext(props) {
|
|
|
1679
1705
|
}
|
|
1680
1706
|
|
|
1681
1707
|
/**
|
|
1682
|
-
* Camera button
|
|
1683
|
-
*
|
|
1684
|
-
* @param props - slot props plus the late `conversation` lookup.
|
|
1685
|
-
* @returns the button, or null while there is
|
|
1686
|
-
* attach.
|
|
1708
|
+
* Camera button in the right panel's tab strip: photographs the open tab, copies
|
|
1709
|
+
* it, and attaches it to the message being written.
|
|
1710
|
+
* @param props - overlay slot props plus the late `conversation` lookup.
|
|
1711
|
+
* @returns the portalled button, or null while there is no panel or no Session.
|
|
1687
1712
|
*/
|
|
1688
1713
|
function PanelShotButton(props) {
|
|
1689
1714
|
const on = useSwitch(props.configForms, 'panelScreenshot', true)
|
|
1690
1715
|
const context = React.useSyncExternalStore(shotStore.subscribe, shotStore.get)
|
|
1691
|
-
const [
|
|
1716
|
+
const [host, setHost] = React.useState(undefined)
|
|
1692
1717
|
const [phase, setPhase] = React.useState('idle')
|
|
1693
1718
|
const [note, setNote] = React.useState('')
|
|
1694
1719
|
React.useEffect(() => {
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
//
|
|
1698
|
-
|
|
1699
|
-
const
|
|
1700
|
-
|
|
1701
|
-
|
|
1720
|
+
// The strip is created, destroyed and re-created as panes open, split and
|
|
1721
|
+
// close, so follow the document rather than measuring it once. Frames are
|
|
1722
|
+
// coalesced because React commits fire many mutations in a row.
|
|
1723
|
+
let frame = 0
|
|
1724
|
+
const find = () => {
|
|
1725
|
+
frame = 0
|
|
1726
|
+
setHost(document.querySelector(SHOT_CHROME_SELECTOR) ?? undefined)
|
|
1727
|
+
}
|
|
1728
|
+
const schedule = () => {
|
|
1729
|
+
if (frame === 0) frame = window.requestAnimationFrame(find)
|
|
1730
|
+
}
|
|
1731
|
+
find()
|
|
1732
|
+
const observer = new MutationObserver(schedule)
|
|
1733
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
1702
1734
|
return () => {
|
|
1735
|
+
if (frame !== 0) window.cancelAnimationFrame(frame)
|
|
1703
1736
|
observer.disconnect()
|
|
1704
|
-
window.removeEventListener('resize', measure)
|
|
1705
1737
|
}
|
|
1706
1738
|
}, [])
|
|
1707
1739
|
React.useEffect(() => {
|
|
@@ -1712,7 +1744,7 @@ function PanelShotButton(props) {
|
|
|
1712
1744
|
}, 4000)
|
|
1713
1745
|
return () => clearTimeout(timer)
|
|
1714
1746
|
}, [note])
|
|
1715
|
-
if (!on || context === undefined ||
|
|
1747
|
+
if (!on || context === undefined || host === undefined) return null
|
|
1716
1748
|
const take = async () => {
|
|
1717
1749
|
if (phase === 'busy') return
|
|
1718
1750
|
const node = panelTarget()
|
|
@@ -1748,63 +1780,46 @@ function PanelShotButton(props) {
|
|
|
1748
1780
|
throw new Error('the composer refused the image')
|
|
1749
1781
|
}
|
|
1750
1782
|
setPhase('done')
|
|
1751
|
-
setNote(copied ? '
|
|
1783
|
+
setNote(copied ? 'Right panel copied and attached' : 'Right panel attached, clipboard refused')
|
|
1752
1784
|
} catch (error) {
|
|
1753
1785
|
setPhase('failed')
|
|
1754
1786
|
setNote(error instanceof Error ? error.message : String(error))
|
|
1755
1787
|
console.warn('[aryacode-polish] panel screenshot failed:', error)
|
|
1756
1788
|
}
|
|
1757
1789
|
}
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
fontSize: '11px',
|
|
1783
|
-
},
|
|
1790
|
+
const tip = note !== ''
|
|
1791
|
+
? note
|
|
1792
|
+
: phase === 'busy' ? 'Capturing the right panel…' : 'Screenshot this panel into the chat (also copies it)'
|
|
1793
|
+
const glyph = phase === 'done'
|
|
1794
|
+
? React.createElement(CheckGlyph, null)
|
|
1795
|
+
: phase === 'failed'
|
|
1796
|
+
? React.createElement(BangGlyph, null)
|
|
1797
|
+
: React.createElement(CameraGlyph, null)
|
|
1798
|
+
const button = React.createElement('button', {
|
|
1799
|
+
type: 'button',
|
|
1800
|
+
'data-polish-shot': phase,
|
|
1801
|
+
'aria-label': 'Screenshot this panel',
|
|
1802
|
+
disabled: phase === 'busy',
|
|
1803
|
+
onClick: () => { void take() },
|
|
1804
|
+
onMouseEnter: (event) => { event.currentTarget.style.background = 'var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,0.06))' },
|
|
1805
|
+
onMouseLeave: (event) => { event.currentTarget.style.background = 'transparent' },
|
|
1806
|
+
style: {
|
|
1807
|
+
...SHOT_BUTTON_STYLE,
|
|
1808
|
+
color: phase === 'failed'
|
|
1809
|
+
? 'var(--dsw-alias-label-error, #b42318)'
|
|
1810
|
+
: phase === 'done'
|
|
1811
|
+
? 'var(--dsw-alias-label-primary, #111418)'
|
|
1812
|
+
: SHOT_BUTTON_STYLE.color,
|
|
1813
|
+
cursor: phase === 'busy' ? 'progress' : 'pointer',
|
|
1784
1814
|
},
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
?
|
|
1790
|
-
: React.createElement('
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
position: 'fixed',
|
|
1794
|
-
top: anchor.top + SHOT_ANCHOR_BUTTON + 6 + 'px',
|
|
1795
|
-
left: Math.max(12, anchor.left - 140) + 'px',
|
|
1796
|
-
maxWidth: '260px',
|
|
1797
|
-
padding: '3px 8px',
|
|
1798
|
-
borderRadius: 'var(--dsw-radius-sm, 6px)',
|
|
1799
|
-
background: 'Canvas',
|
|
1800
|
-
boxShadow: 'var(--dsw-elevation-prominent, 0 3px 8px rgba(0,0,0,0.12))',
|
|
1801
|
-
color: phase === 'failed' ? 'var(--dsw-alias-label-error, #b42318)' : 'var(--dsw-alias-label-secondary, #61666b)',
|
|
1802
|
-
fontSize: '12px',
|
|
1803
|
-
lineHeight: '18px',
|
|
1804
|
-
whiteSpace: 'nowrap',
|
|
1805
|
-
zIndex: 41,
|
|
1806
|
-
},
|
|
1807
|
-
}, note))
|
|
1815
|
+
}, glyph)
|
|
1816
|
+
const Tooltip = primitives === undefined ? undefined : primitives.Tooltip
|
|
1817
|
+
return ReactDOM.createPortal(
|
|
1818
|
+
Tooltip === undefined
|
|
1819
|
+
? button
|
|
1820
|
+
: React.createElement(Tooltip, { label: tip, side: 'bottom', delayMs: 500 }, button),
|
|
1821
|
+
host,
|
|
1822
|
+
)
|
|
1808
1823
|
}
|
|
1809
1824
|
|
|
1810
1825
|
/**
|
|
@@ -1834,6 +1849,9 @@ return {
|
|
|
1834
1849
|
return undefined
|
|
1835
1850
|
}
|
|
1836
1851
|
}
|
|
1852
|
+
// The Better workspace seat shadows the shipped browser at a different priority,
|
|
1853
|
+
// so it must be present only while its switch is on.
|
|
1854
|
+
installWorkspaceSeat(ctx, configForms, { sourceFlow: WorkspaceSourceFlow })
|
|
1837
1855
|
ctx.slots.inject('plugins.row.config', () => ctx.slots.register({
|
|
1838
1856
|
name: 'plugins.row.config',
|
|
1839
1857
|
key: ROW_KEY,
|
package/client/switches.json
CHANGED
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
"label": "Right-panel screenshot",
|
|
37
37
|
"description": "Copy the open right panel into the chat as an image, and onto the clipboard.",
|
|
38
38
|
"defaultOn": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"key": "betterWorkspace",
|
|
42
|
+
"label": "Better workspace",
|
|
43
|
+
"description": "Replace the sidebar workspace list with our own tree: workspaces with their sessions, a right-click menu on a workspace (open, copy path, remove), and the shipped session menu inside it.",
|
|
44
|
+
"defaultOn": true
|
|
39
45
|
}
|
|
40
46
|
]
|
|
41
47
|
}
|