@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 +8 -0
- package/client/workspace.js +78 -0
- package/docs/better-workspace.md +15 -0
- package/lib/client.js +79 -1
- package/package.json +1 -1
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
|
package/client/workspace.js
CHANGED
|
@@ -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,
|
package/docs/better-workspace.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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",
|