@aryacode/dsh-aryacode-polish 0.20.0 → 0.21.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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.21.2] - 2026-10-28
4
+
5
+ Fixed: the folders-only density showed a zero count because it read the hidden session list instead of the real one.
6
+
7
+ - 2026-10-28
8
+
9
+ Added: a folders-only density that hides session rows, and [ / ] to hop between workspaces.
10
+
3
11
  ## [0.20.0] - 2026-10-28
4
12
 
5
13
  Added: session flags and notes, shown on the row and on hover; the whole tree can be copied as indented text from the header.
@@ -371,6 +371,23 @@ export function flattenRows(rows, expanded, query) {
371
371
  return flat
372
372
  }
373
373
 
374
+ /** Where the cursor lands when hopping to the next or previous workspace head. */
375
+ export function jumpWorkspace(list, cursor, step) {
376
+ const flat = Array.isArray(list) ? list : []
377
+ const heads = []
378
+ flat.forEach((item, index) => {
379
+ if (item && item.kind === 'workspace') heads.push(index)
380
+ })
381
+ if (heads.length === 0) return -1
382
+ if (cursor === undefined || cursor < 0) return step > 0 ? heads[0] : heads[heads.length - 1]
383
+ if (step > 0) {
384
+ const after = heads.find(index => index > cursor)
385
+ return after === undefined ? heads[0] : after
386
+ }
387
+ const before = heads.filter(index => index < cursor)
388
+ return before.length === 0 ? heads[heads.length - 1] : before[before.length - 1]
389
+ }
390
+
374
391
  export function moveBefore(list, movingId, beforeId) {
375
392
  const from = list.findIndex(item => item.id === movingId)
376
393
  const to = list.findIndex(item => item.id === beforeId)
@@ -522,16 +539,34 @@ export function folderNamesOf(rows, declared) {
522
539
  return seen
523
540
  }
524
541
 
525
- export const DENSITIES = ['comfortable', 'compact']
542
+ export const DENSITIES = ['comfortable', 'compact', 'folders']
526
543
 
527
544
  /** Row height per density: the tree fits more rows without shrinking the text. */
528
545
  export const DENSITY_METRICS = {
529
- comfortable: { row: '28px' },
530
- compact: { row: '22px' },
546
+ comfortable: { row: '28px', sessions: true },
547
+ compact: { row: '22px', sessions: true },
548
+ folders: { row: '24px', sessions: false },
549
+ }
550
+
551
+ export const DENSITY_TITLES = {
552
+ comfortable: 'Use compact rows',
553
+ compact: 'Show folders only',
554
+ folders: 'Use comfortable rows',
555
+ }
556
+
557
+ export const DENSITY_GLYPHS = {
558
+ comfortable: '\u2263',
559
+ compact: '\u2261',
560
+ folders: '\u25a4',
531
561
  }
532
562
 
533
563
  export function densityOf(value) {
534
- return value === 'compact' ? 'compact' : 'comfortable'
564
+ return DENSITIES.indexOf(value) >= 0 ? value : 'comfortable'
565
+ }
566
+
567
+ export function nextDensity(value) {
568
+ const list = DENSITIES
569
+ return list[(list.indexOf(densityOf(value)) + 1) % list.length]
535
570
  }
536
571
 
537
572
  export function readDensity(storage) {
@@ -1193,7 +1228,17 @@ export function BetterWorkspace(props) {
1193
1228
  if (item.kind === 'workspace') toggleWorkspace(item.workspaceId)
1194
1229
  else services.openSession(item.id)
1195
1230
  }
1231
+ /** [ and ] hop between workspace heads: the fast way through a long tree. */
1232
+ const hopWorkspace = (step) => {
1233
+ const next = jumpWorkspace(walk, cursor, step)
1234
+ if (next >= 0) setCursor(next)
1235
+ }
1196
1236
  const onKeyDown = (event) => {
1237
+ if (event.key === '[' || event.key === ']') {
1238
+ event.preventDefault()
1239
+ hopWorkspace(event.key === ']' ? 1 : -1)
1240
+ return
1241
+ }
1197
1242
  if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
1198
1243
  if (walk.length === 0) return
1199
1244
  event.preventDefault()
@@ -1667,15 +1712,15 @@ export function BetterWorkspace(props) {
1667
1712
  key: 'density',
1668
1713
  type: 'button',
1669
1714
  'data-polish-ws-density': densityOf(density),
1670
- title: densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
1671
- 'aria-label': densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
1715
+ title: DENSITY_TITLES[densityOf(density)],
1716
+ 'aria-label': DENSITY_TITLES[densityOf(density)],
1672
1717
  style: WS_ADD,
1673
1718
  onClick: () => {
1674
- const next = densityOf(density) === 'compact' ? 'comfortable' : 'compact'
1719
+ const next = nextDensity(density)
1675
1720
  setDensity(next)
1676
1721
  writeDensity(next)
1677
1722
  },
1678
- }, densityOf(density) === 'compact' ? '≡' : '≣'),
1723
+ }, DENSITY_GLYPHS[densityOf(density)]),
1679
1724
  React.createElement('button', {
1680
1725
  key: 'add',
1681
1726
  type: 'button',
@@ -1936,7 +1981,7 @@ export function BetterWorkspace(props) {
1936
1981
  : null)))
1937
1982
 
1938
1983
  if (open) {
1939
- sessions.forEach((row) => {
1984
+ (metrics.sessions ? sessions : []).forEach((row) => {
1940
1985
  index += 1
1941
1986
  children.push(sessionRow(workspace, row, index))
1942
1987
  })
@@ -239,3 +239,21 @@ session that no longer exists is simply never drawn.
239
239
  The header's `⧉` copies the whole tree as indented text — folders, workspaces, sessions, with
240
240
  `⚑` before a flagged session — through `navigator.clipboard.writeText`. It is meant for pasting
241
241
  into a prompt or an issue: "here is what I am looking at", without screenshots.
242
+
243
+ ## Folders only
244
+
245
+ The density button walks three states, not two: comfortable, compact, and **folders only**. The
246
+ last one keeps every folder and workspace row (with its real session count) and drops the session
247
+ rows entirely — useful when you are navigating the shape of your work rather than a specific
248
+ session. Nothing is lost: one more click returns to comfortable, and the mode is remembered in
249
+ `aryacode-polish:density.v1`.
250
+
251
+ One trap it exposed: the workspace count must come from the unfiltered session list. Reading the
252
+ list the tree was about to draw made every workspace read `0` in this mode, which looked like the
253
+ feature had eaten the sessions.
254
+
255
+ ## Hopping between workspaces
256
+
257
+ With the tree focused, `[` and `]` move the keyboard cursor to the previous / next workspace head
258
+ and wrap at the ends. It is a second gear next to ArrowUp/ArrowDown: arrows walk rows, brackets
259
+ walk groups, so a long flat list of sessions stops being a scroll-and-squint exercise.
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.20.0"
56
+ const CLIENT_VERSION = "0.21.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. */
@@ -572,6 +572,23 @@ window.__ModuleLoader__.load({
572
572
  return flat
573
573
  }
574
574
 
575
+ /** Where the cursor lands when hopping to the next or previous workspace head. */
576
+ function jumpWorkspace(list, cursor, step) {
577
+ const flat = Array.isArray(list) ? list : []
578
+ const heads = []
579
+ flat.forEach((item, index) => {
580
+ if (item && item.kind === 'workspace') heads.push(index)
581
+ })
582
+ if (heads.length === 0) return -1
583
+ if (cursor === undefined || cursor < 0) return step > 0 ? heads[0] : heads[heads.length - 1]
584
+ if (step > 0) {
585
+ const after = heads.find(index => index > cursor)
586
+ return after === undefined ? heads[0] : after
587
+ }
588
+ const before = heads.filter(index => index < cursor)
589
+ return before.length === 0 ? heads[heads.length - 1] : before[before.length - 1]
590
+ }
591
+
575
592
  function moveBefore(list, movingId, beforeId) {
576
593
  const from = list.findIndex(item => item.id === movingId)
577
594
  const to = list.findIndex(item => item.id === beforeId)
@@ -723,16 +740,34 @@ window.__ModuleLoader__.load({
723
740
  return seen
724
741
  }
725
742
 
726
- const DENSITIES = ['comfortable', 'compact']
743
+ const DENSITIES = ['comfortable', 'compact', 'folders']
727
744
 
728
745
  /** Row height per density: the tree fits more rows without shrinking the text. */
729
746
  const DENSITY_METRICS = {
730
- comfortable: { row: '28px' },
731
- compact: { row: '22px' },
747
+ comfortable: { row: '28px', sessions: true },
748
+ compact: { row: '22px', sessions: true },
749
+ folders: { row: '24px', sessions: false },
750
+ }
751
+
752
+ const DENSITY_TITLES = {
753
+ comfortable: 'Use compact rows',
754
+ compact: 'Show folders only',
755
+ folders: 'Use comfortable rows',
756
+ }
757
+
758
+ const DENSITY_GLYPHS = {
759
+ comfortable: '\u2263',
760
+ compact: '\u2261',
761
+ folders: '\u25a4',
732
762
  }
733
763
 
734
764
  function densityOf(value) {
735
- return value === 'compact' ? 'compact' : 'comfortable'
765
+ return DENSITIES.indexOf(value) >= 0 ? value : 'comfortable'
766
+ }
767
+
768
+ function nextDensity(value) {
769
+ const list = DENSITIES
770
+ return list[(list.indexOf(densityOf(value)) + 1) % list.length]
736
771
  }
737
772
 
738
773
  function readDensity(storage) {
@@ -1394,7 +1429,17 @@ window.__ModuleLoader__.load({
1394
1429
  if (item.kind === 'workspace') toggleWorkspace(item.workspaceId)
1395
1430
  else services.openSession(item.id)
1396
1431
  }
1432
+ /** [ and ] hop between workspace heads: the fast way through a long tree. */
1433
+ const hopWorkspace = (step) => {
1434
+ const next = jumpWorkspace(walk, cursor, step)
1435
+ if (next >= 0) setCursor(next)
1436
+ }
1397
1437
  const onKeyDown = (event) => {
1438
+ if (event.key === '[' || event.key === ']') {
1439
+ event.preventDefault()
1440
+ hopWorkspace(event.key === ']' ? 1 : -1)
1441
+ return
1442
+ }
1398
1443
  if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
1399
1444
  if (walk.length === 0) return
1400
1445
  event.preventDefault()
@@ -1868,15 +1913,15 @@ window.__ModuleLoader__.load({
1868
1913
  key: 'density',
1869
1914
  type: 'button',
1870
1915
  'data-polish-ws-density': densityOf(density),
1871
- title: densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
1872
- 'aria-label': densityOf(density) === 'compact' ? 'Use comfortable rows' : 'Use compact rows',
1916
+ title: DENSITY_TITLES[densityOf(density)],
1917
+ 'aria-label': DENSITY_TITLES[densityOf(density)],
1873
1918
  style: WS_ADD,
1874
1919
  onClick: () => {
1875
- const next = densityOf(density) === 'compact' ? 'comfortable' : 'compact'
1920
+ const next = nextDensity(density)
1876
1921
  setDensity(next)
1877
1922
  writeDensity(next)
1878
1923
  },
1879
- }, densityOf(density) === 'compact' ? '≡' : '≣'),
1924
+ }, DENSITY_GLYPHS[densityOf(density)]),
1880
1925
  React.createElement('button', {
1881
1926
  key: 'add',
1882
1927
  type: 'button',
@@ -2137,7 +2182,7 @@ window.__ModuleLoader__.load({
2137
2182
  : null)))
2138
2183
 
2139
2184
  if (open) {
2140
- sessions.forEach((row) => {
2185
+ (metrics.sessions ? sessions : []).forEach((row) => {
2141
2186
  index += 1
2142
2187
  children.push(sessionRow(workspace, row, index))
2143
2188
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryacode/dsh-aryacode-polish",
3
- "version": "0.20.0",
3
+ "version": "0.21.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",