@aryacode/dsh-aryacode-polish 0.14.0 → 0.15.2
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 +14 -0
- package/client/workspace.js +149 -4
- package/docs/better-workspace.md +24 -0
- package/lib/client.js +150 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.15.2] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Density modes.** A control in the tree header switches between comfortable and
|
|
7
|
+
compact rows, and the choice is remembered (`aryacode-polish:density.v1`). Density
|
|
8
|
+
changes row height only — the text size stays readable.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Expand all now expands folders too.** The header button only flipped workspace rows, so a
|
|
12
|
+
folded folder kept its workspaces hidden while the button claimed everything was open.
|
|
13
|
+
- **Archived sessions no longer sit in their workspace.** The tree now reads
|
|
14
|
+
`archivedSessionIds` (it never did) and shows those sessions in a collapsed
|
|
15
|
+
**Archived** section at the bottom, each one restorable from its own menu.
|
|
16
|
+
|
|
3
17
|
## [0.14.0] - 2026-10-28
|
|
4
18
|
|
|
5
19
|
### Added
|
package/client/workspace.js
CHANGED
|
@@ -202,7 +202,7 @@ const WS_ROW = {
|
|
|
202
202
|
gap: '6px',
|
|
203
203
|
boxSizing: 'border-box',
|
|
204
204
|
width: '100%',
|
|
205
|
-
height: '28px',
|
|
205
|
+
height: 'var(--polish-ws-row, 28px)',
|
|
206
206
|
padding: '0 6px',
|
|
207
207
|
border: 'none',
|
|
208
208
|
borderRadius: 'var(--dsw-radius-sm, 6px)',
|
|
@@ -385,6 +385,68 @@ export function moveBefore(list, movingId, beforeId) {
|
|
|
385
385
|
const LOOK_KEY = 'aryacode-polish:workspace-appearance.v1'
|
|
386
386
|
const ORDER_KEY = 'aryacode-polish:workspace-order.v1'
|
|
387
387
|
const FOLD_KEY = 'aryacode-polish:folded.v1'
|
|
388
|
+
const DENSITY_KEY = 'aryacode-polish:density.v1'
|
|
389
|
+
|
|
390
|
+
export const DENSITIES = ['comfortable', 'compact']
|
|
391
|
+
|
|
392
|
+
/** Row height per density: the tree fits more rows without shrinking the text. */
|
|
393
|
+
export const DENSITY_METRICS = {
|
|
394
|
+
comfortable: { row: '28px' },
|
|
395
|
+
compact: { row: '22px' },
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function densityOf(value) {
|
|
399
|
+
return value === 'compact' ? 'compact' : 'comfortable'
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function readDensity(storage) {
|
|
403
|
+
try {
|
|
404
|
+
const store = storage || (typeof localStorage === 'undefined' ? undefined : localStorage)
|
|
405
|
+
if (!store) return 'comfortable'
|
|
406
|
+
return densityOf(store.getItem(DENSITY_KEY))
|
|
407
|
+
} catch {
|
|
408
|
+
return 'comfortable'
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function writeDensity(value, storage) {
|
|
413
|
+
try {
|
|
414
|
+
const store = storage || (typeof localStorage === 'undefined' ? undefined : localStorage)
|
|
415
|
+
if (!store) return
|
|
416
|
+
store.setItem(DENSITY_KEY, densityOf(value))
|
|
417
|
+
} catch {
|
|
418
|
+
/* remembering this is a nicety, not a requirement */
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Split the flat session list into the rows a workspace shows and the ones it has
|
|
424
|
+
* archived. The shipped browser hides archived sessions; here they keep a section of
|
|
425
|
+
* their own at the bottom instead of disappearing without a trace.
|
|
426
|
+
*/
|
|
427
|
+
export function splitArchived(sessions, archivedIds) {
|
|
428
|
+
const archive = archivedIds instanceof Set ? archivedIds : new Set(archivedIds || [])
|
|
429
|
+
const active = []
|
|
430
|
+
const archived = []
|
|
431
|
+
for (const session of Array.isArray(sessions) ? sessions : []) {
|
|
432
|
+
if (archive.has(session.id)) archived.push(session)
|
|
433
|
+
else active.push(session)
|
|
434
|
+
}
|
|
435
|
+
return { active, archived }
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const WS_SECTION = {
|
|
439
|
+
display: 'flex',
|
|
440
|
+
alignItems: 'center',
|
|
441
|
+
gap: '5px',
|
|
442
|
+
padding: '8px 6px 2px',
|
|
443
|
+
textTransform: 'uppercase',
|
|
444
|
+
letterSpacing: '0.04em',
|
|
445
|
+
color: 'var(--dsw-alias-label-quaternary, #8a8a8a)',
|
|
446
|
+
fontSize: 'calc(' + 'var(--dsh-content-font-size-secondary, 13px)' + ' - 3px)',
|
|
447
|
+
cursor: 'pointer',
|
|
448
|
+
userSelect: 'none',
|
|
449
|
+
}
|
|
388
450
|
|
|
389
451
|
/** Which workspaces and folders are folded, kept so a restart looks like yesterday. */
|
|
390
452
|
export function readFolded(storage) {
|
|
@@ -715,6 +777,8 @@ export function BetterWorkspace(props) {
|
|
|
715
777
|
const folded = React.useMemo(() => readFolded(), [])
|
|
716
778
|
const [folders, setFolders] = React.useState(folded.folders)
|
|
717
779
|
const [switcher, setSwitcher] = React.useState(null)
|
|
780
|
+
const [density, setDensity] = React.useState(() => readDensity())
|
|
781
|
+
const [archiveOpen, setArchiveOpen] = React.useState(false)
|
|
718
782
|
React.useEffect(() => { writeFolded({ workspaces: expanded, folders }) }, [expanded, folders])
|
|
719
783
|
const [renaming, setRenaming] = React.useState(null)
|
|
720
784
|
const commitRename = async () => {
|
|
@@ -750,7 +814,11 @@ export function BetterWorkspace(props) {
|
|
|
750
814
|
() => new Set((workspaceState && workspaceState.pinnedSessionIds) || []),
|
|
751
815
|
[workspaceState],
|
|
752
816
|
)
|
|
753
|
-
const
|
|
817
|
+
const metrics = DENSITY_METRICS[densityOf(density)]
|
|
818
|
+
const archiveSet = new Set((workspaceState && workspaceState.archivedSessionIds) || [])
|
|
819
|
+
const split = splitArchived(sessionList, archiveSet)
|
|
820
|
+
const allRows = workspaceRows(workspaceList, split.active)
|
|
821
|
+
const archivedSessions = split.archived
|
|
754
822
|
const rows = filterTree(allRows, query, pinned)
|
|
755
823
|
const folderOf = (workspace) => folderKey(workspace)
|
|
756
824
|
const visibleRows = rows.filter((group) => {
|
|
@@ -763,7 +831,12 @@ export function BetterWorkspace(props) {
|
|
|
763
831
|
const entries = React.useMemo(() => rowMenuEntries(slots), [slots, workspaceList.length, sessionList.length])
|
|
764
832
|
const totalCount = allRows.reduce((sum, row) => sum + row.sessions.length, 0)
|
|
765
833
|
const shownCount = rows.reduce((sum, row) => sum + row.sessions.length, 0)
|
|
766
|
-
const
|
|
834
|
+
const folderNames = Array.from(new Set(rows.map(row => folderKey(row.workspace)).filter(Boolean)))
|
|
835
|
+
// One click should always do the useful thing: if anything is folded, the button
|
|
836
|
+
// expands everything (folders included); only when nothing is folded does it fold.
|
|
837
|
+
const anyFolded = rows.some(row => expanded[row.workspace.workspaceId] === false)
|
|
838
|
+
|| folderNames.some(name => folders[name] === true)
|
|
839
|
+
const allFolded = rows.length > 0 && !anyFolded
|
|
767
840
|
|
|
768
841
|
React.useEffect(() => {
|
|
769
842
|
if (cursor >= walk.length) setCursor(walk.length - 1)
|
|
@@ -833,10 +906,17 @@ export function BetterWorkspace(props) {
|
|
|
833
906
|
})
|
|
834
907
|
}
|
|
835
908
|
const foldAll = () => {
|
|
836
|
-
if (
|
|
909
|
+
if (anyFolded) {
|
|
910
|
+
setExpanded({})
|
|
911
|
+
setFolders({})
|
|
912
|
+
return
|
|
913
|
+
}
|
|
837
914
|
const next = {}
|
|
838
915
|
rows.forEach((row) => { next[row.workspace.workspaceId] = false })
|
|
916
|
+
const nextFolders = {}
|
|
917
|
+
folderNames.forEach((name) => { nextFolders[name] = true })
|
|
839
918
|
setExpanded(next)
|
|
919
|
+
setFolders(nextFolders)
|
|
840
920
|
}
|
|
841
921
|
|
|
842
922
|
const reorderWorkspace = async (movingId, beforeId) => {
|
|
@@ -980,6 +1060,25 @@ export function BetterWorkspace(props) {
|
|
|
980
1060
|
}, armed && armed === menu.workspace.workspaceId ? 'Remove workspace?' : 'Remove workspace'))
|
|
981
1061
|
: null
|
|
982
1062
|
|
|
1063
|
+
const archivedMenu = menu && menu.kind === 'archived'
|
|
1064
|
+
? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
|
|
1065
|
+
React.createElement('button', {
|
|
1066
|
+
type: 'button',
|
|
1067
|
+
'data-polish-ws-unarchive': menu.row.id,
|
|
1068
|
+
style: WS_MENU_ITEM,
|
|
1069
|
+
onClick: async () => {
|
|
1070
|
+
const row = menu.row
|
|
1071
|
+
closeMenu()
|
|
1072
|
+
try {
|
|
1073
|
+
await services.unarchiveSession(row.id)
|
|
1074
|
+
setNotice('Restored that session.')
|
|
1075
|
+
} catch {
|
|
1076
|
+
setNotice('Could not restore that session.')
|
|
1077
|
+
}
|
|
1078
|
+
},
|
|
1079
|
+
}, 'Unarchive session'))
|
|
1080
|
+
: null
|
|
1081
|
+
|
|
983
1082
|
const paintMenu = menu && menu.kind === 'paint'
|
|
984
1083
|
? (() => {
|
|
985
1084
|
const workspace = menu.workspace
|
|
@@ -1093,6 +1192,19 @@ export function BetterWorkspace(props) {
|
|
|
1093
1192
|
style: WS_ADD,
|
|
1094
1193
|
onClick: foldAll,
|
|
1095
1194
|
}, allFolded ? '⌄' : '⌃'),
|
|
1195
|
+
React.createElement('button', {
|
|
1196
|
+
key: 'density',
|
|
1197
|
+
type: 'button',
|
|
1198
|
+
'data-polish-ws-density': densityOf(density),
|
|
1199
|
+
title: densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
|
|
1200
|
+
'aria-label': densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
|
|
1201
|
+
style: WS_ADD,
|
|
1202
|
+
onClick: () => {
|
|
1203
|
+
const next = densityOf(density) === 'compact' ? 'comfortable' : 'compact'
|
|
1204
|
+
setDensity(next)
|
|
1205
|
+
writeDensity(next)
|
|
1206
|
+
},
|
|
1207
|
+
}, densityOf(density) === 'compact' ? '≡' : '≣'),
|
|
1096
1208
|
React.createElement('button', {
|
|
1097
1209
|
key: 'add',
|
|
1098
1210
|
type: 'button',
|
|
@@ -1301,6 +1413,36 @@ export function BetterWorkspace(props) {
|
|
|
1301
1413
|
}
|
|
1302
1414
|
})
|
|
1303
1415
|
|
|
1416
|
+
if (archivedSessions.length > 0) {
|
|
1417
|
+
children.push(React.createElement('div', {
|
|
1418
|
+
key: 'archived-head',
|
|
1419
|
+
'data-polish-ws-archived': 'head',
|
|
1420
|
+
style: WS_SECTION,
|
|
1421
|
+
onClick: () => setArchiveOpen((open) => !open),
|
|
1422
|
+
},
|
|
1423
|
+
React.createElement('span', { 'aria-hidden': 'true' }, archiveOpen ? '⌄' : '›'),
|
|
1424
|
+
React.createElement('span', { style: WS_TITLE }, 'Archived'),
|
|
1425
|
+
React.createElement('span', { style: WS_COUNT }, String(archivedSessions.length))))
|
|
1426
|
+
|
|
1427
|
+
if (archiveOpen) {
|
|
1428
|
+
for (const row of archivedSessions) {
|
|
1429
|
+
children.push(React.createElement('div', {
|
|
1430
|
+
key: 'archived:' + row.id,
|
|
1431
|
+
'data-polish-ws-archived-row': row.id,
|
|
1432
|
+
title: 'Archived session',
|
|
1433
|
+
style: Object.assign({}, WS_ROW, { opacity: 0.7, fontStyle: 'italic' }),
|
|
1434
|
+
onClick: () => services.openSession(row.id),
|
|
1435
|
+
onContextMenu: (event) => {
|
|
1436
|
+
event.preventDefault()
|
|
1437
|
+
event.stopPropagation()
|
|
1438
|
+
setMenu({ kind: 'archived', row, anchor: { top: event.clientY, left: event.clientX } })
|
|
1439
|
+
},
|
|
1440
|
+
},
|
|
1441
|
+
React.createElement('span', { style: WS_TITLE }, row.displayTitle || row.id)))
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1304
1446
|
return React.createElement('div', {
|
|
1305
1447
|
'data-polish-ws': 'tree',
|
|
1306
1448
|
tabIndex: 0,
|
|
@@ -1317,6 +1459,7 @@ export function BetterWorkspace(props) {
|
|
|
1317
1459
|
overflowX: 'hidden',
|
|
1318
1460
|
padding: '2px 6px 12px',
|
|
1319
1461
|
outline: 'none',
|
|
1462
|
+
'--polish-ws-row': metrics.row,
|
|
1320
1463
|
},
|
|
1321
1464
|
},
|
|
1322
1465
|
children,
|
|
@@ -1325,6 +1468,7 @@ export function BetterWorkspace(props) {
|
|
|
1325
1468
|
: null,
|
|
1326
1469
|
switcherCard,
|
|
1327
1470
|
sessionMenu,
|
|
1471
|
+
archivedMenu,
|
|
1328
1472
|
paintMenu,
|
|
1329
1473
|
workspaceMenu)
|
|
1330
1474
|
}
|
|
@@ -1451,6 +1595,7 @@ export function installWorkspaceSeat(ctx, configForms, extras = {}) {
|
|
|
1451
1595
|
reorderSession: (workspaceId, movingId, beforeId) => seated.workspaces.insertSessionBefore(workspaceId, movingId, beforeId),
|
|
1452
1596
|
removeWorkspace: (id) => seated.workspaces.delete(id),
|
|
1453
1597
|
renameWorkspace: (id, title) => seated.workspaces.rename(id, title),
|
|
1598
|
+
unarchiveSession: (id) => seated.workspaces.unarchiveSession(id),
|
|
1454
1599
|
openFolder: async (path) => {
|
|
1455
1600
|
const answer = await seated.remote.session.openWorkspacePath({ path })
|
|
1456
1601
|
if (answer && answer.ok === false) throw new Error('cannot open the folder')
|
package/docs/better-workspace.md
CHANGED
|
@@ -152,3 +152,27 @@ panel's stacking context can clip it.
|
|
|
152
152
|
like the way out. Verified across a real restart: folding `WORK` left
|
|
153
153
|
`{"workspaces":{},"folders":{"Work":true}}` in storage and the folder still read `› WORK 2`
|
|
154
154
|
with its workspace hidden.
|
|
155
|
+
|
|
156
|
+
## Density
|
|
157
|
+
|
|
158
|
+
The header carries a density control that flips between comfortable (28 px rows) and
|
|
159
|
+
compact (22 px rows), remembered under `aryacode-polish:density.v1`. Density changes row
|
|
160
|
+
height only: the text keeps its size, so a compact tree is still readable on a laptop.
|
|
161
|
+
The row height travels through the CSS variable `--polish-ws-row`, which the tree element
|
|
162
|
+
sets from `DENSITY_METRICS`; nothing else in the row styling depends on the mode.
|
|
163
|
+
|
|
164
|
+
## Archived sessions
|
|
165
|
+
|
|
166
|
+
Archiving a session removes it from its workspace — that is what the Host means by
|
|
167
|
+
archived, and the shipped browser simply stops rendering it. Here those sessions keep a
|
|
168
|
+
collapsed **Archived** section at the bottom of the tree instead, dimmed and italic, with
|
|
169
|
+
their count on the header. Clicking one opens it; right-clicking offers **Unarchive
|
|
170
|
+
session**, which goes through `workspaces.unarchiveSession`. This also fixed a real gap:
|
|
171
|
+
before this, `workspaceRows` never consulted `archivedSessionIds`, so archived sessions
|
|
172
|
+
were being rendered inline in their workspace as if they were live.
|
|
173
|
+
|
|
174
|
+
## Folding everything
|
|
175
|
+
|
|
176
|
+
The header's fold control expands when anything is folded (folders included) and folds
|
|
177
|
+
only when nothing is. An earlier version only looked at workspace rows, so a folded
|
|
178
|
+
folder left the button saying "Expand all" while its workspaces stayed hidden.
|
package/lib/client.js
CHANGED
|
@@ -53,7 +53,7 @@ window.__ModuleLoader__.load({
|
|
|
53
53
|
]
|
|
54
54
|
const ROW_ID = "polish"
|
|
55
55
|
const ROW_KEY = "@aryacode/dsh-aryacode-polish" + '#' + ROW_ID
|
|
56
|
-
const CLIENT_VERSION = "0.
|
|
56
|
+
const CLIENT_VERSION = "0.15.2"
|
|
57
57
|
|
|
58
58
|
/* html2canvas 1.4.1 — MIT, vendored from vendor/html2canvas.min.js.
|
|
59
59
|
* A UMD build, so it assigns itself to globalThis; face.js reads it lazily. */
|
|
@@ -403,7 +403,7 @@ window.__ModuleLoader__.load({
|
|
|
403
403
|
gap: '6px',
|
|
404
404
|
boxSizing: 'border-box',
|
|
405
405
|
width: '100%',
|
|
406
|
-
height: '28px',
|
|
406
|
+
height: 'var(--polish-ws-row, 28px)',
|
|
407
407
|
padding: '0 6px',
|
|
408
408
|
border: 'none',
|
|
409
409
|
borderRadius: 'var(--dsw-radius-sm, 6px)',
|
|
@@ -586,6 +586,68 @@ window.__ModuleLoader__.load({
|
|
|
586
586
|
const LOOK_KEY = 'aryacode-polish:workspace-appearance.v1'
|
|
587
587
|
const ORDER_KEY = 'aryacode-polish:workspace-order.v1'
|
|
588
588
|
const FOLD_KEY = 'aryacode-polish:folded.v1'
|
|
589
|
+
const DENSITY_KEY = 'aryacode-polish:density.v1'
|
|
590
|
+
|
|
591
|
+
const DENSITIES = ['comfortable', 'compact']
|
|
592
|
+
|
|
593
|
+
/** Row height per density: the tree fits more rows without shrinking the text. */
|
|
594
|
+
const DENSITY_METRICS = {
|
|
595
|
+
comfortable: { row: '28px' },
|
|
596
|
+
compact: { row: '22px' },
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function densityOf(value) {
|
|
600
|
+
return value === 'compact' ? 'compact' : 'comfortable'
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function readDensity(storage) {
|
|
604
|
+
try {
|
|
605
|
+
const store = storage || (typeof localStorage === 'undefined' ? undefined : localStorage)
|
|
606
|
+
if (!store) return 'comfortable'
|
|
607
|
+
return densityOf(store.getItem(DENSITY_KEY))
|
|
608
|
+
} catch {
|
|
609
|
+
return 'comfortable'
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function writeDensity(value, storage) {
|
|
614
|
+
try {
|
|
615
|
+
const store = storage || (typeof localStorage === 'undefined' ? undefined : localStorage)
|
|
616
|
+
if (!store) return
|
|
617
|
+
store.setItem(DENSITY_KEY, densityOf(value))
|
|
618
|
+
} catch {
|
|
619
|
+
/* remembering this is a nicety, not a requirement */
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Split the flat session list into the rows a workspace shows and the ones it has
|
|
625
|
+
* archived. The shipped browser hides archived sessions; here they keep a section of
|
|
626
|
+
* their own at the bottom instead of disappearing without a trace.
|
|
627
|
+
*/
|
|
628
|
+
function splitArchived(sessions, archivedIds) {
|
|
629
|
+
const archive = archivedIds instanceof Set ? archivedIds : new Set(archivedIds || [])
|
|
630
|
+
const active = []
|
|
631
|
+
const archived = []
|
|
632
|
+
for (const session of Array.isArray(sessions) ? sessions : []) {
|
|
633
|
+
if (archive.has(session.id)) archived.push(session)
|
|
634
|
+
else active.push(session)
|
|
635
|
+
}
|
|
636
|
+
return { active, archived }
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const WS_SECTION = {
|
|
640
|
+
display: 'flex',
|
|
641
|
+
alignItems: 'center',
|
|
642
|
+
gap: '5px',
|
|
643
|
+
padding: '8px 6px 2px',
|
|
644
|
+
textTransform: 'uppercase',
|
|
645
|
+
letterSpacing: '0.04em',
|
|
646
|
+
color: 'var(--dsw-alias-label-quaternary, #8a8a8a)',
|
|
647
|
+
fontSize: 'calc(' + 'var(--dsh-content-font-size-secondary, 13px)' + ' - 3px)',
|
|
648
|
+
cursor: 'pointer',
|
|
649
|
+
userSelect: 'none',
|
|
650
|
+
}
|
|
589
651
|
|
|
590
652
|
/** Which workspaces and folders are folded, kept so a restart looks like yesterday. */
|
|
591
653
|
function readFolded(storage) {
|
|
@@ -916,6 +978,8 @@ window.__ModuleLoader__.load({
|
|
|
916
978
|
const folded = React.useMemo(() => readFolded(), [])
|
|
917
979
|
const [folders, setFolders] = React.useState(folded.folders)
|
|
918
980
|
const [switcher, setSwitcher] = React.useState(null)
|
|
981
|
+
const [density, setDensity] = React.useState(() => readDensity())
|
|
982
|
+
const [archiveOpen, setArchiveOpen] = React.useState(false)
|
|
919
983
|
React.useEffect(() => { writeFolded({ workspaces: expanded, folders }) }, [expanded, folders])
|
|
920
984
|
const [renaming, setRenaming] = React.useState(null)
|
|
921
985
|
const commitRename = async () => {
|
|
@@ -951,7 +1015,11 @@ window.__ModuleLoader__.load({
|
|
|
951
1015
|
() => new Set((workspaceState && workspaceState.pinnedSessionIds) || []),
|
|
952
1016
|
[workspaceState],
|
|
953
1017
|
)
|
|
954
|
-
const
|
|
1018
|
+
const metrics = DENSITY_METRICS[densityOf(density)]
|
|
1019
|
+
const archiveSet = new Set((workspaceState && workspaceState.archivedSessionIds) || [])
|
|
1020
|
+
const split = splitArchived(sessionList, archiveSet)
|
|
1021
|
+
const allRows = workspaceRows(workspaceList, split.active)
|
|
1022
|
+
const archivedSessions = split.archived
|
|
955
1023
|
const rows = filterTree(allRows, query, pinned)
|
|
956
1024
|
const folderOf = (workspace) => folderKey(workspace)
|
|
957
1025
|
const visibleRows = rows.filter((group) => {
|
|
@@ -964,7 +1032,12 @@ window.__ModuleLoader__.load({
|
|
|
964
1032
|
const entries = React.useMemo(() => rowMenuEntries(slots), [slots, workspaceList.length, sessionList.length])
|
|
965
1033
|
const totalCount = allRows.reduce((sum, row) => sum + row.sessions.length, 0)
|
|
966
1034
|
const shownCount = rows.reduce((sum, row) => sum + row.sessions.length, 0)
|
|
967
|
-
const
|
|
1035
|
+
const folderNames = Array.from(new Set(rows.map(row => folderKey(row.workspace)).filter(Boolean)))
|
|
1036
|
+
// One click should always do the useful thing: if anything is folded, the button
|
|
1037
|
+
// expands everything (folders included); only when nothing is folded does it fold.
|
|
1038
|
+
const anyFolded = rows.some(row => expanded[row.workspace.workspaceId] === false)
|
|
1039
|
+
|| folderNames.some(name => folders[name] === true)
|
|
1040
|
+
const allFolded = rows.length > 0 && !anyFolded
|
|
968
1041
|
|
|
969
1042
|
React.useEffect(() => {
|
|
970
1043
|
if (cursor >= walk.length) setCursor(walk.length - 1)
|
|
@@ -1034,10 +1107,17 @@ window.__ModuleLoader__.load({
|
|
|
1034
1107
|
})
|
|
1035
1108
|
}
|
|
1036
1109
|
const foldAll = () => {
|
|
1037
|
-
if (
|
|
1110
|
+
if (anyFolded) {
|
|
1111
|
+
setExpanded({})
|
|
1112
|
+
setFolders({})
|
|
1113
|
+
return
|
|
1114
|
+
}
|
|
1038
1115
|
const next = {}
|
|
1039
1116
|
rows.forEach((row) => { next[row.workspace.workspaceId] = false })
|
|
1117
|
+
const nextFolders = {}
|
|
1118
|
+
folderNames.forEach((name) => { nextFolders[name] = true })
|
|
1040
1119
|
setExpanded(next)
|
|
1120
|
+
setFolders(nextFolders)
|
|
1041
1121
|
}
|
|
1042
1122
|
|
|
1043
1123
|
const reorderWorkspace = async (movingId, beforeId) => {
|
|
@@ -1181,6 +1261,25 @@ window.__ModuleLoader__.load({
|
|
|
1181
1261
|
}, armed && armed === menu.workspace.workspaceId ? 'Remove workspace?' : 'Remove workspace'))
|
|
1182
1262
|
: null
|
|
1183
1263
|
|
|
1264
|
+
const archivedMenu = menu && menu.kind === 'archived'
|
|
1265
|
+
? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
|
|
1266
|
+
React.createElement('button', {
|
|
1267
|
+
type: 'button',
|
|
1268
|
+
'data-polish-ws-unarchive': menu.row.id,
|
|
1269
|
+
style: WS_MENU_ITEM,
|
|
1270
|
+
onClick: async () => {
|
|
1271
|
+
const row = menu.row
|
|
1272
|
+
closeMenu()
|
|
1273
|
+
try {
|
|
1274
|
+
await services.unarchiveSession(row.id)
|
|
1275
|
+
setNotice('Restored that session.')
|
|
1276
|
+
} catch {
|
|
1277
|
+
setNotice('Could not restore that session.')
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
}, 'Unarchive session'))
|
|
1281
|
+
: null
|
|
1282
|
+
|
|
1184
1283
|
const paintMenu = menu && menu.kind === 'paint'
|
|
1185
1284
|
? (() => {
|
|
1186
1285
|
const workspace = menu.workspace
|
|
@@ -1294,6 +1393,19 @@ window.__ModuleLoader__.load({
|
|
|
1294
1393
|
style: WS_ADD,
|
|
1295
1394
|
onClick: foldAll,
|
|
1296
1395
|
}, allFolded ? '⌄' : '⌃'),
|
|
1396
|
+
React.createElement('button', {
|
|
1397
|
+
key: 'density',
|
|
1398
|
+
type: 'button',
|
|
1399
|
+
'data-polish-ws-density': densityOf(density),
|
|
1400
|
+
title: densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
|
|
1401
|
+
'aria-label': densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
|
|
1402
|
+
style: WS_ADD,
|
|
1403
|
+
onClick: () => {
|
|
1404
|
+
const next = densityOf(density) === 'compact' ? 'comfortable' : 'compact'
|
|
1405
|
+
setDensity(next)
|
|
1406
|
+
writeDensity(next)
|
|
1407
|
+
},
|
|
1408
|
+
}, densityOf(density) === 'compact' ? '≡' : '≣'),
|
|
1297
1409
|
React.createElement('button', {
|
|
1298
1410
|
key: 'add',
|
|
1299
1411
|
type: 'button',
|
|
@@ -1502,6 +1614,36 @@ window.__ModuleLoader__.load({
|
|
|
1502
1614
|
}
|
|
1503
1615
|
})
|
|
1504
1616
|
|
|
1617
|
+
if (archivedSessions.length > 0) {
|
|
1618
|
+
children.push(React.createElement('div', {
|
|
1619
|
+
key: 'archived-head',
|
|
1620
|
+
'data-polish-ws-archived': 'head',
|
|
1621
|
+
style: WS_SECTION,
|
|
1622
|
+
onClick: () => setArchiveOpen((open) => !open),
|
|
1623
|
+
},
|
|
1624
|
+
React.createElement('span', { 'aria-hidden': 'true' }, archiveOpen ? '⌄' : '›'),
|
|
1625
|
+
React.createElement('span', { style: WS_TITLE }, 'Archived'),
|
|
1626
|
+
React.createElement('span', { style: WS_COUNT }, String(archivedSessions.length))))
|
|
1627
|
+
|
|
1628
|
+
if (archiveOpen) {
|
|
1629
|
+
for (const row of archivedSessions) {
|
|
1630
|
+
children.push(React.createElement('div', {
|
|
1631
|
+
key: 'archived:' + row.id,
|
|
1632
|
+
'data-polish-ws-archived-row': row.id,
|
|
1633
|
+
title: 'Archived session',
|
|
1634
|
+
style: Object.assign({}, WS_ROW, { opacity: 0.7, fontStyle: 'italic' }),
|
|
1635
|
+
onClick: () => services.openSession(row.id),
|
|
1636
|
+
onContextMenu: (event) => {
|
|
1637
|
+
event.preventDefault()
|
|
1638
|
+
event.stopPropagation()
|
|
1639
|
+
setMenu({ kind: 'archived', row, anchor: { top: event.clientY, left: event.clientX } })
|
|
1640
|
+
},
|
|
1641
|
+
},
|
|
1642
|
+
React.createElement('span', { style: WS_TITLE }, row.displayTitle || row.id)))
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1505
1647
|
return React.createElement('div', {
|
|
1506
1648
|
'data-polish-ws': 'tree',
|
|
1507
1649
|
tabIndex: 0,
|
|
@@ -1518,6 +1660,7 @@ window.__ModuleLoader__.load({
|
|
|
1518
1660
|
overflowX: 'hidden',
|
|
1519
1661
|
padding: '2px 6px 12px',
|
|
1520
1662
|
outline: 'none',
|
|
1663
|
+
'--polish-ws-row': metrics.row,
|
|
1521
1664
|
},
|
|
1522
1665
|
},
|
|
1523
1666
|
children,
|
|
@@ -1526,6 +1669,7 @@ window.__ModuleLoader__.load({
|
|
|
1526
1669
|
: null,
|
|
1527
1670
|
switcherCard,
|
|
1528
1671
|
sessionMenu,
|
|
1672
|
+
archivedMenu,
|
|
1529
1673
|
paintMenu,
|
|
1530
1674
|
workspaceMenu)
|
|
1531
1675
|
}
|
|
@@ -1652,6 +1796,7 @@ window.__ModuleLoader__.load({
|
|
|
1652
1796
|
reorderSession: (workspaceId, movingId, beforeId) => seated.workspaces.insertSessionBefore(workspaceId, movingId, beforeId),
|
|
1653
1797
|
removeWorkspace: (id) => seated.workspaces.delete(id),
|
|
1654
1798
|
renameWorkspace: (id, title) => seated.workspaces.rename(id, title),
|
|
1799
|
+
unarchiveSession: (id) => seated.workspaces.unarchiveSession(id),
|
|
1655
1800
|
openFolder: async (path) => {
|
|
1656
1801
|
const answer = await seated.remote.session.openWorkspacePath({ path })
|
|
1657
1802
|
if (answer && answer.ok === false) throw new Error('cannot open the folder')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aryacode/dsh-aryacode-polish",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "AryaCode Polish: one install for the small things that make AryaCode feel finished. One Plugins row, one switch per quality-of-life feature.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|