@aryacode/dsh-aryacode-polish 0.18.0 → 0.19.0

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.19.0] - 2026-10-28
4
+
5
+ ### Added
6
+ - **Move to top level** in a workspace menu drops the folder prefix, so filing is reversible.
7
+ - **Rename a folder** from its header: every workspace inside is refiled under the new
8
+ prefix through the Host rename, and the declared/empty list follows the folder.
9
+
10
+
3
11
  ## [0.18.0] - 2026-10-28
4
12
 
5
13
  ### Added
@@ -707,6 +707,14 @@ export function fileUnder(label, folder) {
707
707
  return bare ? target + '/' + bare : target
708
708
  }
709
709
 
710
+ /** The title a workspace needs to sit at the top level again: no folder prefix. */
711
+ export function unfiled(label) {
712
+ const name = String(label || '').trim()
713
+ if (!name) return name
714
+ const cut = name.lastIndexOf('/')
715
+ return cut >= 0 ? name.slice(cut + 1).trim() : name
716
+ }
717
+
710
718
  export function folderKey(workspace) {
711
719
  const label = workspaceLabel(workspace)
712
720
  const cut = label.lastIndexOf('/')
@@ -1189,6 +1197,22 @@ export function BetterWorkspace(props) {
1189
1197
  }
1190
1198
  },
1191
1199
  }, 'Restore saved order'),
1200
+ folderOf(menu.workspace) === undefined
1201
+ ? null
1202
+ : React.createElement('button', {
1203
+ type: 'button',
1204
+ 'data-polish-ws-unfile': menu.workspace.workspaceId,
1205
+ style: WS_MENU_ITEM,
1206
+ onClick: () => {
1207
+ const target = menu.workspace
1208
+ const title = unfiled(workspaceLabel(target))
1209
+ closeMenu()
1210
+ Promise.resolve()
1211
+ .then(() => services.renameWorkspace(target.workspaceId, title))
1212
+ .then(() => setNotice('Moved to the top level.'))
1213
+ .catch(() => setNotice('Could not move that workspace.'))
1214
+ },
1215
+ }, 'Move to top level'),
1192
1216
  React.createElement('button', {
1193
1217
  type: 'button',
1194
1218
  'data-polish-folder-new': 'open',
@@ -1291,6 +1315,46 @@ export function BetterWorkspace(props) {
1291
1315
  React.createElement('div', { style: WS_MENU_HINT }, 'Enter creates, Escape cancels'))
1292
1316
  : null
1293
1317
 
1318
+ const renameFolderMenu = menu && menu.kind === 'renameFolder'
1319
+ ? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
1320
+ React.createElement('input', {
1321
+ 'data-polish-folder-edit-input': 'true',
1322
+ type: 'text',
1323
+ value: folderDraft,
1324
+ placeholder: 'Folder name',
1325
+ autoFocus: true,
1326
+ style: WS_NOTE_INPUT,
1327
+ onChange: (event) => setFolderDraft(event.target.value),
1328
+ onKeyDown: (event) => {
1329
+ if (event.key === 'Escape') { event.stopPropagation(); closeMenu(); return }
1330
+ if (event.key !== 'Enter') return
1331
+ const target = cleanFolder(folderDraft)
1332
+ const moving = menu.members || []
1333
+ const from = menu.folder
1334
+ closeMenu()
1335
+ if (!target || target === from) return
1336
+ Promise.resolve()
1337
+ .then(async () => {
1338
+ for (const workspace of moving) {
1339
+ await services.renameWorkspace(workspace.workspaceId, fileUnder(workspaceLabel(workspace), target))
1340
+ }
1341
+ })
1342
+ .then(() => {
1343
+ setDeclared((prev) => addFolder(removeFolder(prev, from), target))
1344
+ setFolders((prev) => {
1345
+ const next = Object.assign({}, prev)
1346
+ if (next[from] !== undefined) next[target] = next[from]
1347
+ delete next[from]
1348
+ return next
1349
+ })
1350
+ setNotice('Folder renamed.')
1351
+ })
1352
+ .catch(() => setNotice('Could not rename that folder.'))
1353
+ },
1354
+ }),
1355
+ React.createElement('div', { style: WS_MENU_HINT }, 'Renames every workspace inside it'))
1356
+ : null
1357
+
1294
1358
  const glyphMenu = menu && menu.kind === 'glyph'
1295
1359
  ? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
1296
1360
  React.createElement('div', { style: WS_GLYPH_GRID },
@@ -1643,6 +1707,19 @@ export function BetterWorkspace(props) {
1643
1707
  },
1644
1708
  React.createElement('span', { 'aria-hidden': 'true' }, folded ? '›' : '⌄'),
1645
1709
  React.createElement('span', { style: WS_TITLE }, folder),
1710
+ React.createElement('button', {
1711
+ type: 'button',
1712
+ 'data-polish-folder-edit': folder,
1713
+ 'aria-label': 'Rename the folder ' + folder,
1714
+ title: 'Rename this folder',
1715
+ style: WS_ROW_ACTION,
1716
+ onClick: (event) => {
1717
+ event.stopPropagation()
1718
+ const box = event.currentTarget.getBoundingClientRect()
1719
+ setFolderDraft(folder)
1720
+ setMenu({ kind: 'renameFolder', folder, anchor: { top: box.bottom + 4, left: box.left }, members: inside.map((entry) => entry.workspace) })
1721
+ },
1722
+ }, '✎'),
1646
1723
  React.createElement('span', { style: WS_COUNT }, String(inside.reduce((sum, entry) => sum + entry.sessions.length, 0)))))
1647
1724
  }
1648
1725
 
@@ -1829,6 +1906,7 @@ export function BetterWorkspace(props) {
1829
1906
  glyphMenu,
1830
1907
  noteMenu,
1831
1908
  folderMenu,
1909
+ renameFolderMenu,
1832
1910
  noteCard,
1833
1911
  archivedMenu,
1834
1912
  paintMenu,
@@ -210,3 +210,18 @@ drop like any other folder header, and carries a `×` that removes the declarati
210
210
  names live in `aryacode-polish:folders.v1`; the tree draws the names its workspaces imply
211
211
  first and the declared ones after, so an empty folder never jumps ahead of real content and
212
212
  disappears on its own the moment the first workspace is filed into it.
213
+
214
+ ## Renaming a folder
215
+
216
+ A folder is not an object in the Host — it is the prefix of the titles inside it. So renaming
217
+ a folder means refiling every member: the header's `✎` asks for a new name, and each
218
+ workspace inside is renamed to `NewName/` + its own name through `workspaces.rename`. The
219
+ declared/empty list follows the old name to the new one, and the fold state moves with it, so
220
+ a renamed folder does not suddenly spring open.
221
+
222
+ ## Filing is reversible
223
+
224
+ **Move to top level** appears in a workspace's menu only when it actually lives under a folder,
225
+ and does the inverse: `unfiled('Work/Alpha')` is `Alpha`, renamed through the Host like any
226
+ other title change. A folder left with nothing in it simply stops being drawn, unless it was
227
+ declared empty on purpose.
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.18.0"
56
+ const CLIENT_VERSION = "0.19.0"
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. */
@@ -908,6 +908,14 @@ window.__ModuleLoader__.load({
908
908
  return bare ? target + '/' + bare : target
909
909
  }
910
910
 
911
+ /** The title a workspace needs to sit at the top level again: no folder prefix. */
912
+ function unfiled(label) {
913
+ const name = String(label || '').trim()
914
+ if (!name) return name
915
+ const cut = name.lastIndexOf('/')
916
+ return cut >= 0 ? name.slice(cut + 1).trim() : name
917
+ }
918
+
911
919
  function folderKey(workspace) {
912
920
  const label = workspaceLabel(workspace)
913
921
  const cut = label.lastIndexOf('/')
@@ -1390,6 +1398,22 @@ window.__ModuleLoader__.load({
1390
1398
  }
1391
1399
  },
1392
1400
  }, 'Restore saved order'),
1401
+ folderOf(menu.workspace) === undefined
1402
+ ? null
1403
+ : React.createElement('button', {
1404
+ type: 'button',
1405
+ 'data-polish-ws-unfile': menu.workspace.workspaceId,
1406
+ style: WS_MENU_ITEM,
1407
+ onClick: () => {
1408
+ const target = menu.workspace
1409
+ const title = unfiled(workspaceLabel(target))
1410
+ closeMenu()
1411
+ Promise.resolve()
1412
+ .then(() => services.renameWorkspace(target.workspaceId, title))
1413
+ .then(() => setNotice('Moved to the top level.'))
1414
+ .catch(() => setNotice('Could not move that workspace.'))
1415
+ },
1416
+ }, 'Move to top level'),
1393
1417
  React.createElement('button', {
1394
1418
  type: 'button',
1395
1419
  'data-polish-folder-new': 'open',
@@ -1492,6 +1516,46 @@ window.__ModuleLoader__.load({
1492
1516
  React.createElement('div', { style: WS_MENU_HINT }, 'Enter creates, Escape cancels'))
1493
1517
  : null
1494
1518
 
1519
+ const renameFolderMenu = menu && menu.kind === 'renameFolder'
1520
+ ? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
1521
+ React.createElement('input', {
1522
+ 'data-polish-folder-edit-input': 'true',
1523
+ type: 'text',
1524
+ value: folderDraft,
1525
+ placeholder: 'Folder name',
1526
+ autoFocus: true,
1527
+ style: WS_NOTE_INPUT,
1528
+ onChange: (event) => setFolderDraft(event.target.value),
1529
+ onKeyDown: (event) => {
1530
+ if (event.key === 'Escape') { event.stopPropagation(); closeMenu(); return }
1531
+ if (event.key !== 'Enter') return
1532
+ const target = cleanFolder(folderDraft)
1533
+ const moving = menu.members || []
1534
+ const from = menu.folder
1535
+ closeMenu()
1536
+ if (!target || target === from) return
1537
+ Promise.resolve()
1538
+ .then(async () => {
1539
+ for (const workspace of moving) {
1540
+ await services.renameWorkspace(workspace.workspaceId, fileUnder(workspaceLabel(workspace), target))
1541
+ }
1542
+ })
1543
+ .then(() => {
1544
+ setDeclared((prev) => addFolder(removeFolder(prev, from), target))
1545
+ setFolders((prev) => {
1546
+ const next = Object.assign({}, prev)
1547
+ if (next[from] !== undefined) next[target] = next[from]
1548
+ delete next[from]
1549
+ return next
1550
+ })
1551
+ setNotice('Folder renamed.')
1552
+ })
1553
+ .catch(() => setNotice('Could not rename that folder.'))
1554
+ },
1555
+ }),
1556
+ React.createElement('div', { style: WS_MENU_HINT }, 'Renames every workspace inside it'))
1557
+ : null
1558
+
1495
1559
  const glyphMenu = menu && menu.kind === 'glyph'
1496
1560
  ? React.createElement(RowMenu, { anchor: menu.anchor, onClose: closeMenu },
1497
1561
  React.createElement('div', { style: WS_GLYPH_GRID },
@@ -1844,6 +1908,19 @@ window.__ModuleLoader__.load({
1844
1908
  },
1845
1909
  React.createElement('span', { 'aria-hidden': 'true' }, folded ? '›' : '⌄'),
1846
1910
  React.createElement('span', { style: WS_TITLE }, folder),
1911
+ React.createElement('button', {
1912
+ type: 'button',
1913
+ 'data-polish-folder-edit': folder,
1914
+ 'aria-label': 'Rename the folder ' + folder,
1915
+ title: 'Rename this folder',
1916
+ style: WS_ROW_ACTION,
1917
+ onClick: (event) => {
1918
+ event.stopPropagation()
1919
+ const box = event.currentTarget.getBoundingClientRect()
1920
+ setFolderDraft(folder)
1921
+ setMenu({ kind: 'renameFolder', folder, anchor: { top: box.bottom + 4, left: box.left }, members: inside.map((entry) => entry.workspace) })
1922
+ },
1923
+ }, '✎'),
1847
1924
  React.createElement('span', { style: WS_COUNT }, String(inside.reduce((sum, entry) => sum + entry.sessions.length, 0)))))
1848
1925
  }
1849
1926
 
@@ -2030,6 +2107,7 @@ window.__ModuleLoader__.load({
2030
2107
  glyphMenu,
2031
2108
  noteMenu,
2032
2109
  folderMenu,
2110
+ renameFolderMenu,
2033
2111
  noteCard,
2034
2112
  archivedMenu,
2035
2113
  paintMenu,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aryacode/dsh-aryacode-polish",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
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",