@brftech/filex-core 0.40.0 → 0.41.1

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.
Files changed (125) hide show
  1. package/README.md +32 -25
  2. package/dist/ArchiveViewer-DxxjhYZD.js +124 -0
  3. package/dist/ArchiveViewer-DxxjhYZD.js.map +1 -0
  4. package/dist/CsvViewer-CyuwENI_.js +141 -0
  5. package/dist/CsvViewer-CyuwENI_.js.map +1 -0
  6. package/dist/{DrawioViewer-BNALOB04.js → DrawioViewer-BIYsTX97.js} +48 -42
  7. package/dist/DrawioViewer-BIYsTX97.js.map +1 -0
  8. package/dist/EpubViewer-K9PjCMmS.js +163 -0
  9. package/dist/EpubViewer-K9PjCMmS.js.map +1 -0
  10. package/dist/IpynbViewer-DYU6Zgz2.js +184 -0
  11. package/dist/IpynbViewer-DYU6Zgz2.js.map +1 -0
  12. package/dist/MermaidViewer-Bp73aId8.js +137 -0
  13. package/dist/MermaidViewer-Bp73aId8.js.map +1 -0
  14. package/dist/PsdViewer-C2eLivw1.js +122 -0
  15. package/dist/PsdViewer-C2eLivw1.js.map +1 -0
  16. package/dist/TiffViewer-DsHg5gcz.js +142 -0
  17. package/dist/TiffViewer-DsHg5gcz.js.map +1 -0
  18. package/dist/Viewer3D-C_dc2HN9.js +75 -0
  19. package/dist/Viewer3D-C_dc2HN9.js.map +1 -0
  20. package/dist/filex-core.js +218 -20361
  21. package/dist/filex-core.js.map +1 -1
  22. package/dist/filex-core.umd.cjs +61 -62
  23. package/dist/filex-core.umd.cjs.map +1 -1
  24. package/dist/index-vxMZpYb-.js +26700 -0
  25. package/dist/index-vxMZpYb-.js.map +1 -0
  26. package/dist/index.d.ts +4212 -862
  27. package/dist/style.css +1 -1
  28. package/package.json +1 -1
  29. package/src/FileExplorer.vue +2154 -506
  30. package/src/components/AdvancedSearch.vue +870 -0
  31. package/src/components/Breadcrumb.vue +191 -19
  32. package/src/components/CommandPalette.vue +66 -22
  33. package/src/components/ConnectionsPanel.vue +6 -2
  34. package/src/components/ContextMenu.vue +39 -2
  35. package/src/components/EncryptedFolderModal.vue +31 -25
  36. package/src/components/FilePane.vue +951 -0
  37. package/src/components/FilterBar.vue +568 -72
  38. package/src/components/GalleryView.vue +172 -56
  39. package/src/components/GridView.vue +331 -82
  40. package/src/components/HomeView.vue +331 -0
  41. package/src/components/InspectorPanel.vue +296 -53
  42. package/src/components/ListView.vue +1173 -194
  43. package/src/components/NFSExportsPanel.vue +7 -4
  44. package/src/components/OnboardingTour.vue +156 -33
  45. package/src/components/QuickLook.vue +26 -1
  46. package/src/components/RecentlyOpened.vue +17 -2
  47. package/src/components/S3KeysPanel.vue +7 -4
  48. package/src/components/SSHKeysPanel.vue +7 -4
  49. package/src/components/ShortcutSettings.vue +6 -2
  50. package/src/components/ShortcutsHelp.vue +4 -1
  51. package/src/components/SideNav.vue +206 -140
  52. package/src/components/StorageFields.vue +19 -1
  53. package/src/components/TabBar.vue +87 -1
  54. package/src/components/TagPicker.vue +77 -6
  55. package/src/components/ThemeGallery.vue +13 -198
  56. package/src/components/ThemePalette.vue +244 -0
  57. package/src/components/TimeZoneDialog.vue +135 -0
  58. package/src/components/TimeZonePicker.vue +696 -0
  59. package/src/components/TokensPanel.vue +35 -14
  60. package/src/components/Toolbar.vue +953 -465
  61. package/src/components/ViewSwitcher.vue +9 -4
  62. package/src/composables/useConnections.ts +35 -4
  63. package/src/composables/useExplorerTimeZone.ts +89 -0
  64. package/src/composables/useFileApi.ts +53 -4
  65. package/src/composables/useLocale.ts +382 -26
  66. package/src/composables/useRowTouch.ts +95 -0
  67. package/src/index.ts +177 -3
  68. package/src/lib/actionIcons.ts +330 -0
  69. package/src/lib/advSearch.ts +162 -0
  70. package/src/lib/dateGroups.ts +197 -0
  71. package/src/lib/destinationTree.ts +235 -0
  72. package/src/lib/downloadSelection.ts +154 -0
  73. package/src/lib/fileFilters.ts +319 -12
  74. package/src/lib/fileIcons.ts +299 -5
  75. package/src/lib/filePreview.ts +605 -0
  76. package/src/lib/listing.ts +72 -0
  77. package/src/lib/shareTtl.ts +31 -2
  78. package/src/lib/sortOrder.ts +459 -0
  79. package/src/lib/themes.ts +151 -116
  80. package/src/lib/timezone.ts +501 -0
  81. package/src/lib/uiProfile.ts +82 -0
  82. package/src/lib/viewPrefs.ts +1036 -0
  83. package/src/locales/en.ts +416 -26
  84. package/src/locales/tr.ts +409 -26
  85. package/src/modals/ConvertModal.vue +23 -3
  86. package/src/modals/DestinationPickerModal.vue +341 -0
  87. package/src/modals/Modal.vue +18 -4
  88. package/src/modals/NewDocumentModal.vue +525 -0
  89. package/src/modals/NewFolderModal.vue +15 -9
  90. package/src/modals/PermissionsModal.vue +557 -476
  91. package/src/modals/PreviewModal.vue +516 -156
  92. package/src/styles/base.css +5409 -8
  93. package/src/styles/variables.css +139 -65
  94. package/src/types/Connections.ts +9 -0
  95. package/src/types/ExplorerConfig.ts +144 -39
  96. package/src/types/FileNode.ts +28 -0
  97. package/src/viewers/ArchiveViewer.vue +46 -8
  98. package/src/viewers/CsvViewer.vue +19 -2
  99. package/src/viewers/DrawioViewer.vue +12 -1
  100. package/src/viewers/EpubViewer.vue +50 -6
  101. package/src/viewers/IpynbViewer.vue +19 -2
  102. package/src/viewers/MermaidViewer.vue +20 -3
  103. package/src/viewers/PdfViewer.vue +24 -4
  104. package/src/viewers/PsdViewer.vue +28 -4
  105. package/src/viewers/TiffViewer.vue +19 -2
  106. package/src/viewers/Viewer3D.vue +36 -2
  107. package/dist/ArchiveViewer-Cdfv9LFC.js +0 -99
  108. package/dist/ArchiveViewer-Cdfv9LFC.js.map +0 -1
  109. package/dist/CsvViewer-CqWeV8VO.js +0 -131
  110. package/dist/CsvViewer-CqWeV8VO.js.map +0 -1
  111. package/dist/DrawioViewer-BNALOB04.js.map +0 -1
  112. package/dist/EpubViewer-BPmlImG7.js +0 -145
  113. package/dist/EpubViewer-BPmlImG7.js.map +0 -1
  114. package/dist/IpynbViewer-CDlQmuCV.js +0 -175
  115. package/dist/IpynbViewer-CDlQmuCV.js.map +0 -1
  116. package/dist/MermaidViewer-sfZWdR-d.js +0 -127
  117. package/dist/MermaidViewer-sfZWdR-d.js.map +0 -1
  118. package/dist/PsdViewer-DwuySZsb.js +0 -112
  119. package/dist/PsdViewer-DwuySZsb.js.map +0 -1
  120. package/dist/TiffViewer-BDQ3d3CQ.js +0 -132
  121. package/dist/TiffViewer-BDQ3d3CQ.js.map +0 -1
  122. package/dist/Viewer3D-CJJJgkuv.js +0 -60
  123. package/dist/Viewer3D-CJJJgkuv.js.map +0 -1
  124. package/src/components/SecondaryPane.vue +0 -447
  125. package/src/modals/ShareModal.vue +0 -139
package/src/locales/en.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** English UI strings. */
2
2
  export const en: Record<string, string> = {
3
- 'toolbar.new_folder': 'New Folder',
3
+ 'toolbar.new_folder': 'New folder',
4
4
  'toolbar.upload': 'Upload',
5
5
  'toolbar.search': 'Search',
6
6
  'toolbar.search.placeholder': 'Search by name or tag:…',
@@ -39,10 +39,11 @@ export const en: Record<string, string> = {
39
39
  'ctx.paste': 'Paste',
40
40
  'ctx.info': 'Info',
41
41
  'ctx.duplicate': 'Duplicate',
42
- 'ctx.new_file': 'New File',
42
+ 'ctx.new_file': 'New file',
43
43
 
44
44
  'modal.delete.title': 'Delete?',
45
- 'modal.delete.message': '{count} item(s) will be moved to trash.',
45
+ 'modal.delete.message': '{count} items will be moved to trash.',
46
+ 'modal.delete.message_one': '1 item will be moved to trash.',
46
47
  'modal.delete.confirm': 'Move to Trash',
47
48
  'modal.delete.cancel': 'Cancel',
48
49
 
@@ -50,7 +51,7 @@ export const en: Record<string, string> = {
50
51
  'modal.rename.save': 'Save',
51
52
  'modal.rename.cancel': 'Cancel',
52
53
 
53
- 'modal.newfolder.title': 'New Folder',
54
+ 'modal.newfolder.title': 'New folder',
54
55
  'modal.newfolder.placeholder': 'Folder name',
55
56
  'modal.newfolder.create': 'Create',
56
57
  'modal.newfolder.cancel': 'Cancel',
@@ -96,10 +97,11 @@ export const en: Record<string, string> = {
96
97
  'unit.mb': 'MB',
97
98
  'unit.gb': 'GB',
98
99
  'unit.tb': 'TB',
100
+ 'unit.pb': 'PB',
99
101
 
100
102
  'breadcrumb.root': 'Root',
101
- 'breadcrumb.copy_path': 'Copy Path',
102
- 'breadcrumb.go_to_path': 'Go To Path',
103
+ 'breadcrumb.copy_path': 'Copy path',
104
+ 'breadcrumb.go_to_path': 'Go to path',
103
105
  'breadcrumb.path_placeholder': 'fileman/foo/bar',
104
106
 
105
107
  'ops.copy': 'Copying',
@@ -110,9 +112,13 @@ export const en: Record<string, string> = {
110
112
  'ops.error': 'error',
111
113
 
112
114
  'node.trash': 'Trash',
115
+ /* gorunum:v1 — the singular kind, printed under a folder's name on a card
116
+ * and beside it in the list. The plural section heading is a different
117
+ * string on purpose. */
118
+ 'node.folder': 'Folder',
113
119
 
114
120
  // ——— Rich viewer chrome ———
115
- 'viewer.open_in_new_tab': 'Open in New Tab',
121
+ 'viewer.open_in_new_tab': 'Open in new tab',
116
122
  'viewer.loading': 'Loading…',
117
123
  'viewer.failed_to_load': 'Failed to load file',
118
124
  'viewer.peer_not_installed': 'Optional viewer library is not installed. Install the peer dependency or use the download button.',
@@ -150,6 +156,7 @@ export const en: Record<string, string> = {
150
156
  'viewer.epub_next': 'Next',
151
157
  'viewer.drawio.disabled': 'diagrams.net embed is not configured for this filex instance — operator must wire FILEX_DRAWIO_URL to enable the editor.',
152
158
  'viewer.archive.entries': '{n} files',
159
+ 'viewer.archive.entries_one': '1 file',
153
160
  'viewer.archive.empty': 'Archive is empty.',
154
161
  'viewer.archive.error': 'Could not read archive contents.',
155
162
  'viewer.name': 'Name',
@@ -217,7 +224,10 @@ export const en: Record<string, string> = {
217
224
  'toast.cut_ready': 'Cut — ready to paste',
218
225
  'toast.copy_ready': 'Copied — ready to paste',
219
226
  'toast.same_folder_cut': 'Cannot cut into the same folder',
220
- 'toast.restored': '{n} item(s) restored',
227
+ 'toast.restored': '{n} items restored',
228
+ 'toast.restored_one': '1 item restored',
229
+ 'toast.restore_taken': '{n} items were not restored: something already has their names. Rename what is there, then restore again.',
230
+ 'toast.restore_taken_one': '“{name}” was not restored: something already has that name. Rename what is there, then restore again.',
221
231
  'toast.delete_queued': 'Delete queued',
222
232
  'toast.trash_retention': 'Items in the trash are removed automatically once the retention window ends. Permanent deletion is done from the admin panel.',
223
233
  'dropzone.hint': 'Drop files here',
@@ -226,7 +236,9 @@ export const en: Record<string, string> = {
226
236
  'viewer.pdf_inline_failed': 'Your browser could not display this PDF inline.',
227
237
  'viewer.too_large': 'File is too large (>1 MB).',
228
238
  'viewer.csv_rows': '{n} rows',
239
+ 'viewer.csv_rows_one': '1 row',
229
240
  'presence.others': '{n} people',
241
+ 'presence.others_one': '1 person',
230
242
  'conn.offline': 'No live connection — changes may be delayed',
231
243
  'conn.tooltip': 'Live connection to the server is unavailable; the list refreshes periodically. This notice disappears once the connection is back.',
232
244
 
@@ -253,8 +265,25 @@ export const en: Record<string, string> = {
253
265
  'star.failed': 'Could not change the star',
254
266
  'shortcuts.inspector': 'Toggle details panel',
255
267
  'inspector.title': 'Details',
268
+ 'inspector.copy_name': 'Copy name',
269
+ 'inspector.items': '{n} items',
270
+ 'inspector.items_one': '1 item',
271
+ 'inspector.type': 'Type',
272
+ 'inspector.select_hint': 'Select an item to see its details.',
273
+ /* pane:p1 — the panel HOLDS the last selected thing (owner's ruling,
274
+ 2026-09-13), so it can be describing an item in the half of a split
275
+ window nobody is looking at. This line says which one and where. */
276
+ 'inspector.held': 'Last selected — in {where}',
256
277
  'inspector.close': 'Close',
257
278
  'inspector.section.general': 'General',
279
+ 'inspector.section.tags': 'Tags',
280
+ /* etiket:t1 — TagPicker's own chrome. It printed these three in English
281
+ regardless of the catalogue until 2026-09-13; `tags.open` is the chip's new
282
+ "show me everything with this tag". */
283
+ 'tags.open': 'Open tag: {tag}',
284
+ 'tags.add': '+ Add tag',
285
+ 'tags.name': 'Tag name',
286
+ 'tags.remove': 'Remove tag',
258
287
  'inspector.section.versions': 'Versions',
259
288
  'inspector.section.permissions': 'Permissions',
260
289
  'inspector.section.shares': 'Shares',
@@ -263,11 +292,14 @@ export const en: Record<string, string> = {
263
292
  'inspector.modified': 'Modified',
264
293
  'inspector.mime': 'MIME type',
265
294
  'inspector.etag': 'ETag',
295
+ 'inspector.nodeId': 'Node ID',
266
296
  'inspector.copy': 'Copy',
267
297
  'inspector.copied': 'Copied',
268
298
  'inspector.error': 'Could not load',
269
299
  'inspector.items_summary': '{n} items, {size} total',
300
+ 'inspector.items_summary_one': '1 item, {size} total',
270
301
  'inspector.folder_items': '{n} items',
302
+ 'inspector.folder_items_one': '1 item',
271
303
  'inspector.versions.empty': 'No version history',
272
304
  'inspector.versions.v': 'Version {n}',
273
305
  'inspector.versions.restore': 'Restore',
@@ -306,7 +338,18 @@ export const en: Record<string, string> = {
306
338
  'theme.name.contrast': 'High Contrast',
307
339
  'theme.name.gray': 'Soft Gray',
308
340
  'theme.name.terminal': 'Terminal Green',
309
- 'theme.name.drive': 'Drive',
341
+ /* === zaman:z3 — the time-zone picker (embed dialog + web settings) === */
342
+ 'tz.menu': 'Time zone',
343
+ 'tz.title': 'Time zone',
344
+ 'tz.label': 'Show dates in',
345
+ 'tz.search': 'Search a city, country or offset — try "Istanbul" or "GMT+3"',
346
+ 'tz.default.device': "Use this device's zone ({zone})",
347
+ 'tz.default.account': "Use the account's zone ({zone})",
348
+ 'tz.default.host': "Use this site's default ({zone})",
349
+ 'tz.no_match': 'No zone matches “{query}”. Try a city, a country, or an offset like GMT+3.',
350
+ 'tz.more': '{n} more — keep typing to narrow the list.',
351
+ 'tz.hint': 'Saved in this browser only. Every date is stored as one moment in time and shown on the clock you pick here.',
352
+ 'tz.now': 'Right now in {zone}: {time}',
310
353
  /* === wiring:c2 — customizable shortcuts + quick look === */
311
354
  'shortcuts.quicklook': 'Quick look',
312
355
  'shortcuts.customize': 'Customize',
@@ -331,6 +374,7 @@ export const en: Record<string, string> = {
331
374
  /* === wiring:c3 — operations center === */
332
375
  'opc.title': 'Operations',
333
376
  'opc.aria_badge': '{n} operations — toggle operations center',
377
+ 'opc.aria_badge_one': '1 operation — toggle operations center',
334
378
  'opc.close': 'Close',
335
379
  'opc.active': 'In progress',
336
380
  'opc.history': 'History',
@@ -359,24 +403,34 @@ export const en: Record<string, string> = {
359
403
  'tour.next': 'Next',
360
404
  'tour.done': 'Done',
361
405
  'tour.progress': '{n} / {m}',
362
- 'tour.step.nav.title': 'Storages & folders',
406
+ 'tour.step.nav.title': 'Storages & views',
363
407
  'tour.step.nav.desc':
364
- 'Your current location lives here. Click any crumb to jump back to parent folders or the storage root.',
365
- 'tour.step.upload.title': 'Upload files',
366
- 'tour.step.upload.desc':
367
- 'Pick files with this button — or simply drag & drop them anywhere in the window.',
408
+ 'Home, Shared with me, Recent, Starred and Trash are always in this panel, with your storages and connections underneath. The button in the top-left corner collapses it to a rail and brings it back.',
409
+ 'tour.step.nav.desc_closed':
410
+ 'This button opens the navigation panel: Home, Shared with me, Recent, Starred and Trash, with your storages and connections underneath.',
411
+ 'tour.step.new.title': 'Add something',
412
+ 'tour.step.new.desc':
413
+ 'Everything new starts here — upload from this device, create a folder, or ask somebody else for a file. Dropping files onto the window uploads them as well.',
414
+ 'tour.step.new.desc_fab':
415
+ 'This button uploads files from this device. Dropping files onto the window works as well.',
368
416
  'tour.step.search.title': 'Search',
369
417
  'tour.step.search.desc':
370
- 'This box searches the whole storage by name — separators and one typo are forgiven, so "invoice 2026" finds invoice_2026.pdf. Add tag:invoice to filter by tag. The {palette} command palette also runs commands.',
418
+ 'This field searches the whole storage by name — separators and one typo are forgiven, so "invoice 2026" finds invoice_2026.pdf. Add tag:invoice to filter by tag. The sliders button opens advanced search, and {palette} opens the command palette, which runs commands too.',
419
+ 'tour.step.crumb.title': 'Where you are',
420
+ 'tour.step.crumb.desc':
421
+ 'This trail is the address of the folder on screen. Click any step to go back up, and the chevron beside the last one lists the folders inside it.',
422
+ 'tour.step.filters.title': 'Narrow the list',
423
+ 'tour.step.filters.desc':
424
+ 'Type, People, Modified and Size filter the folder you are in, the box beside them matches names as you type, and the control at the end of the row sorts.',
371
425
  'tour.step.view.title': 'Switch views',
372
426
  'tour.step.view.desc':
373
- 'Toggle between the list and grid layout; your choice is remembered.',
374
- 'tour.step.share.title': 'Sharing',
375
- 'tour.step.share.desc':
376
- 'Right-click any file and use "Share / Permissions" to create a link with an optional PIN and expiry.',
427
+ 'Change how this folder is drawn; your choice is remembered.',
428
+ 'tour.step.details.title': 'Details & sharing',
429
+ 'tour.step.details.desc':
430
+ 'Select a file and open this panel for its details, its activity and its share links. For the full menu, right-click the file or use the button at the end of its row — "Share / Permissions" there creates a link with an optional PIN and expiry.',
377
431
  'tour.step.help.title': 'Shortcuts',
378
432
  'tour.step.help.desc':
379
- 'Press {help} for the shortcut cheat-sheet and {palette} for the command palette. Replay this tour any time via "Restart the tour" in the menu.',
433
+ 'Press {help} for the shortcut cheat-sheet and {palette} for the command palette. Theme and "Restart the tour" are in the "More actions" menu at the top right; your account and sign-out are behind the avatar beside it.',
380
434
  'error.hint': 'This may be a temporary network or server problem. Please try again.',
381
435
  'error.details': 'Technical details',
382
436
  'col.star': 'Star',
@@ -422,6 +476,7 @@ export const en: Record<string, string> = {
422
476
  'split.retry': 'Retry',
423
477
  'split.copy_queued': 'Copy queued',
424
478
  'split.move_queued': 'Move queued',
479
+ 'toast.move_kept_both': 'Move queued. A name was already taken there, so that item will be kept beside the existing one with “-copy” added. This move cannot be undone.',
425
480
  'split.cross_copy': 'Different storages — copy queued instead',
426
481
  'dragout.downloading': 'Downloading into the folder you dropped on…',
427
482
  'dragout.not_found': 'Could not find where it was dropped — a drop onto an application (rather than a folder) cannot be filled in',
@@ -443,8 +498,9 @@ export const en: Record<string, string> = {
443
498
  /* wiring:e2 — end-to-end encrypted folders */
444
499
  'e2e.create.title': 'Create encrypted folder',
445
500
  'e2e.create.option': 'Create encrypted folder…',
446
- 'e2e.create.pw_placeholder': 'Folder password (min 8 characters)',
447
- 'e2e.create.pw2_placeholder': 'Repeat the password',
501
+ 'e2e.create.pw_label': 'Folder password',
502
+ 'e2e.create.pw_placeholder': 'At least 8 characters',
503
+ 'e2e.create.pw2_label': 'Repeat the password',
448
504
  'e2e.create.warn_title': 'NO WAY BACK',
449
505
  'e2e.create.warn_body':
450
506
  'Files in this folder can only be opened with this password or the recovery key shown once when the folder is created. The password is NEVER stored on the server, and filex keeps no copy of the recovery key either. Lose both and the contents are gone for good.',
@@ -465,6 +521,10 @@ export const en: Record<string, string> = {
465
521
  'e2e.locked.busy': 'Verifying…',
466
522
  'e2e.unlock.wrong': 'Wrong password.',
467
523
  'e2e.unlock.marker_missing': 'Could not read the key file (.filex-e2e.json).',
524
+ /* ikon:emoji — the listing marker's accessible name. The row used to print
525
+ a 🔒 and nothing else; the padlock that replaced it is aria-hidden inside
526
+ the tile, so the span that carries it says this instead. */
527
+ 'e2e.badge': 'Encrypted folder',
468
528
  'e2e.strip.label': 'Encrypted folder — unlocked',
469
529
  'e2e.strip.lock': 'Lock',
470
530
  'e2e.locked_toast': 'Folder locked',
@@ -575,7 +635,8 @@ export const en: Record<string, string> = {
575
635
  'conn.denied.unreachable': 'The server did not answer: {error}',
576
636
  'conn.denied.guideHint': 'You do not need administrator rights to connect your own computer to the storages you can already see.',
577
637
  'conn.denied.guideCta': 'Show me how to connect',
578
- 'conn.list.count': '{n} storage(s)',
638
+ 'conn.list.count': '{n} storages',
639
+ 'conn.list.count_one': '1 storage',
579
640
  'conn.list.add': 'Add a storage',
580
641
  'conn.list.empty': 'No storage is configured yet. Add one and the files show up in the explorer.',
581
642
  'conn.list.edit': 'Edit',
@@ -594,7 +655,8 @@ export const en: Record<string, string> = {
594
655
  'conn.form.enabled': 'Enabled',
595
656
  'conn.form.test': 'Test connection',
596
657
  'conn.form.testing': 'Testing…',
597
- 'conn.form.testOk': 'Connected. {count} item(s) at the root.',
658
+ 'conn.form.testOk': 'Connected. {count} items at the root.',
659
+ 'conn.form.testOk_one': 'Connected. 1 item at the root.',
598
660
  'conn.form.testFail': 'Could not connect: {error}',
599
661
  'conn.form.save': 'Save',
600
662
  'conn.form.saving': 'Saving…',
@@ -969,6 +1031,8 @@ export const en: Record<string, string> = {
969
1031
  'sidenav.title': 'Navigation',
970
1032
  'sidenav.views': 'Views',
971
1033
  'sidenav.storages': 'Storages',
1034
+ 'sidenav.home': 'Home',
1035
+ 'sidenav.myfiles': 'My files',
972
1036
  'sidenav.recent': 'Recent',
973
1037
  'sidenav.starred': 'Starred',
974
1038
  'sidenav.shared': 'Shared with me',
@@ -976,8 +1040,22 @@ export const en: Record<string, string> = {
976
1040
  'sidenav.collapse': 'Collapse navigation',
977
1041
  'sidenav.expand': 'Expand navigation',
978
1042
  'sidenav.close': 'Close navigation',
1043
+ // ⚠ `open`/`close` are the DRAWER's pair, `expand`/`collapse` the docked
1044
+ // panel's, and they are not interchangeable — see the note on
1045
+ // `navToggleLabel` in Toolbar.vue. Below 560px the panel is a drawer over
1046
+ // the listing: it is not narrowed to a rail, it is not on screen at all, so
1047
+ // "expand" would promise a width change to something that has no width.
1048
+ 'sidenav.open': 'Open navigation',
979
1049
  'sidenav.storage.shared': 'Shared',
980
- 'toolbar.nav': 'Navigation',
1050
+ // ⚠ `toolbar.nav` is GONE, and nothing replaced it. It was the one label on
1051
+ // the panel's collapse control, first as the noun "Navigation" — which names
1052
+ // the panel and never says the button does anything to it, so the owner
1053
+ // could not find how to collapse the panel (2026-09-13: "yan menüyü kısıp
1054
+ // açabilir olalım ya bence") — and then as the static stopgap "Show or hide
1055
+ // navigation". The four `sidenav.*` verbs above are the real fix: the label
1056
+ // is bound to `navOpen`, so it says *collapse* when it will collapse and
1057
+ // *expand* when it will expand.
1058
+ 'node.home': 'Home',
981
1059
  'node.recent': 'Recent',
982
1060
  'node.starred': 'Starred',
983
1061
  'node.shared': 'Shared with me',
@@ -1003,20 +1081,62 @@ export const en: Record<string, string> = {
1003
1081
  'conn.tokens.rootPlaceholder': 'storage://folder',
1004
1082
  'conn.tokens.expiry': 'Expires in (days)',
1005
1083
  'conn.tokens.expiryNever': 'never',
1084
+ 'conn.tokens.defaultName': 'API key {date}',
1085
+ 'conn.tokens.namePlaceholder': 'Name — e.g. backup script',
1006
1086
  'conn.tokens.capNote': 'Your account and its permissions are the ceiling — asking for more than you have is refused, not granted.',
1007
1087
  /* === /gezinti:g1 === */
1008
1088
 
1009
- /* === surucu:d1 — the Drive shell (uiProfile: 'drive', GitHub #14) ===== */
1089
+ /* === surucu:d1 — the shell (GitHub #14); every profile, no longer a
1090
+ `uiProfile: 'drive'` opt-in === */
1010
1091
  'drive.new': 'New',
1011
1092
  'drive.new.upload': 'Upload files',
1012
1093
  'drive.new.folder': 'New folder',
1013
1094
  'drive.new.request': 'Request files',
1095
+ 'drive.new.document': 'New document',
1096
+
1097
+ /* === belge:n1 — "New document": the picker under + New =================
1098
+ Type names are NOT listed here. A tile reads its words from
1099
+ lib/fileIcons `typeLabelFor`, the same table the listing's Type column
1100
+ uses, so ".docx" is called the same thing in both places and a new type
1101
+ needs no string at all. Only this dialog's own chrome lives below. */
1102
+ 'newdoc.title': 'New document',
1103
+ 'newdoc.type.label': 'Document type',
1104
+ 'newdoc.group.document': 'Documents',
1105
+ 'newdoc.group.text': 'Text and code',
1106
+ 'newdoc.group.diagram': 'Diagrams',
1107
+ 'newdoc.withheld.onlyoffice': 'Office documents need a document server (OnlyOffice), which is not configured here.',
1108
+ 'newdoc.withheld.drawio': 'Diagrams need a drawio service, which is not configured here.',
1109
+ 'newdoc.empty.title': 'No document types are available',
1110
+ 'newdoc.empty.body': 'This server did not offer any type this app can create.',
1111
+ 'newdoc.empty.blocked': 'Every type this server can create needs an editor service that is not configured here.',
1112
+ 'newdoc.name': 'Name',
1113
+ 'newdoc.name.placeholder': 'Document name',
1114
+ 'newdoc.untitled': 'Untitled',
1115
+ 'newdoc.err.slash': 'A name cannot contain a slash.',
1116
+ 'newdoc.err.exists': '{name} is already here. Choose another name.',
1117
+ 'newdoc.location': 'Location',
1118
+ 'newdoc.location.none': 'Choose a folder',
1119
+ 'newdoc.location.change': 'Change',
1120
+ 'newdoc.location.noaccess': 'You cannot save into this folder.',
1121
+ 'newdoc.create': 'Create',
1122
+ 'newdoc.creating': 'Creating…',
1123
+ 'newdoc.cancel': 'Cancel',
1014
1124
  'drive.search.placeholder': 'Search in {scope}',
1015
1125
  'drive.search.placeholder_all': 'Search your files',
1016
1126
  'drive.search.hint_title': 'Search everywhere and run commands ({combo})',
1017
1127
  'drive.storage.used': '{used} of {total} used',
1018
1128
  'drive.storage.used_unlimited': '{used} used',
1019
1129
  'drive.storage.label': 'Storage',
1130
+ /* === gorunum:v3-shell — the Home view (the overview, inside the shell) ==
1131
+ * The section headings are `sidenav.storages` / `sidenav.recent` /
1132
+ * `sidenav.starred` and the two empty states are `empty.recent.*` /
1133
+ * `empty.starred.*`, on purpose: the block headed "Recent" on Home and the
1134
+ * panel row labelled "Recent" beside it are the same thing, and a second
1135
+ * string for it is how the two drift apart one translation at a time. Only
1136
+ * what exists nowhere else is below. */
1137
+ 'home.title': 'Home',
1138
+ 'home.storages.empty': 'No storages yet',
1139
+ 'home.storages.hint': 'A storage is a drive filex reads and writes — an administrator adds them.',
1020
1140
  'drive.section.folders': 'Folders',
1021
1141
  'drive.section.files': 'Files',
1022
1142
  'filter.aria': 'Filters',
@@ -1044,6 +1164,12 @@ export const en: Record<string, string> = {
1044
1164
  'filter.size.1to10': '1 - 10 MB',
1045
1165
  'filter.size.10to100': '10 - 100 MB',
1046
1166
  'filter.size.gt100': 'Over 100 MB',
1167
+ /* surucu:d1-actions — the ⋮ menu at the end of the filter row. Only its own
1168
+ NAME is new: every row inside it reuses the string that verb already has
1169
+ (`toolbar.new_folder`, `toolbar.upload`, `ctx.paste`, `shortcuts.select_all`,
1170
+ `selection.clear`), because a menu that renames a command is a menu the
1171
+ reader has to learn twice. */
1172
+ 'filter.actions': 'Listing actions',
1047
1173
  'filter.clear': 'Clear filters',
1048
1174
  'filter.empty.title': 'Nothing here matches those filters',
1049
1175
  'filter.empty.hint': 'Clear a filter, or search everywhere with the field above.',
@@ -1077,4 +1203,268 @@ export const en: Record<string, string> = {
1077
1203
  'convert.unsupported_input': 'This file type has no source format the converter can read.',
1078
1204
  'convert.converting': 'Converting…',
1079
1205
  'convert.convert': 'Convert',
1206
+ /* === gorunum:v1-chrome — the filter row's name box === */
1207
+ 'filter.find': 'Filter in this folder…',
1208
+ /* surucu:d1-scope — the same box where the rows are not a folder's contents.
1209
+ It has to say what it narrows: at the drive list "this folder" names
1210
+ nothing, and on Home it would name three blocks at once. */
1211
+ 'filter.find.storages': 'Filter storages…',
1212
+ 'filter.find.home': 'Filter Home…',
1213
+ /* === gorunum:v1-viewer — the full-bleed overlay's own chrome === */
1214
+ 'viewer.share': 'Share',
1215
+ 'viewer.nav_prev': 'Previous file',
1216
+ 'viewer.nav_next': 'Next file',
1217
+ /* The counter in the top bar's meta line: "246.3 KB • Sep 9, 2026 • 1 of 9". */
1218
+ 'viewer.counter': '{i} of {n}',
1219
+ 'viewer.zoom_reset': 'Reset zoom',
1220
+ 'viewer.fullscreen': 'Full screen',
1221
+ 'viewer.exit_fullscreen': 'Exit full screen',
1222
+ 'viewer.md_placeholder': '# Markdown here…',
1223
+ /* === gorunum:v1-selbar — the selection bar that replaces the filter row === */
1224
+ 'selection.count': '{n} selected',
1225
+ 'selection.clear': 'Clear selection',
1226
+ /* === gorunum:v1-preview — the kind, said in words ======================
1227
+ The Type column used to print the extension in caps. These are what it
1228
+ prints instead; the table that picks one is in `lib/fileIcons.ts`
1229
+ (`typeLabelKey`), so this list and that one are the only two places a
1230
+ kind is named. Anything unmapped still falls back to the uppercased
1231
+ extension — these names improve on that fallback, they do not replace
1232
+ it. `Folder` is NOT here: it is `node.folder`, which the grid card's
1233
+ caption already prints. */
1234
+ 'ftype.typescript': 'TypeScript',
1235
+ 'ftype.javascript': 'JavaScript',
1236
+ 'ftype.vue': 'Vue component',
1237
+ 'ftype.python': 'Python',
1238
+ 'ftype.go': 'Go',
1239
+ 'ftype.rust': 'Rust',
1240
+ 'ftype.php': 'PHP',
1241
+ 'ftype.ruby': 'Ruby',
1242
+ 'ftype.java': 'Java',
1243
+ 'ftype.kotlin': 'Kotlin',
1244
+ 'ftype.swift': 'Swift',
1245
+ 'ftype.c': 'C source',
1246
+ 'ftype.cpp': 'C++ source',
1247
+ 'ftype.csharp': 'C# source',
1248
+ 'ftype.stylesheet': 'Stylesheet',
1249
+ 'ftype.html': 'HTML document',
1250
+ 'ftype.json': 'JSON data',
1251
+ 'ftype.yaml': 'YAML',
1252
+ 'ftype.xml': 'XML',
1253
+ 'ftype.toml': 'TOML',
1254
+ 'ftype.sql': 'SQL',
1255
+ 'ftype.shell': 'Shell script',
1256
+ 'ftype.powershell': 'PowerShell script',
1257
+ 'ftype.markdown': 'Markdown',
1258
+ 'ftype.plaintext': 'Plain text',
1259
+ 'ftype.log': 'Log file',
1260
+ 'ftype.config': 'Configuration',
1261
+ 'ftype.sheet': 'Spreadsheet',
1262
+ 'ftype.figma': 'Figma design',
1263
+ 'ftype.design': 'Design file',
1264
+ 'ftype.image': 'Image',
1265
+ 'ftype.video': 'Video',
1266
+ 'ftype.audio': 'Audio',
1267
+ 'ftype.pdf': 'PDF document',
1268
+ 'ftype.document': 'Document',
1269
+ 'ftype.slides': 'Presentation',
1270
+ 'ftype.archive': 'Archive',
1271
+ 'ftype.code': 'Code',
1272
+ /* === gorunum:v1-advsearch — the Advanced search dialog ==================
1273
+ * Two `filter.*` keys are added here rather than beside their siblings
1274
+ * because they are new members of shared unions the dialog introduced
1275
+ * (`modified: 'around'`, `size: 'range'`); the filter row never selects
1276
+ * them, but `chipLabel` would print the raw key if one ever reached it. */
1277
+ 'filter.modified.around': 'Around a date',
1278
+ 'filter.size.range': 'Custom range',
1279
+
1280
+ 'advsearch.open': 'Advanced search',
1281
+ 'advsearch.title': 'Advanced search',
1282
+ 'advsearch.subtitle': 'Find files by name, content, path, or tags.',
1283
+ 'advsearch.close': 'Close',
1284
+ 'advsearch.content.heading': 'Content search',
1285
+ 'advsearch.viewall': 'View all results',
1286
+ 'advsearch.where.any_short': 'Anywhere',
1287
+ 'advsearch.where.here_short': 'Only here',
1288
+ 'advsearch.where.skip_short': 'Skip this',
1289
+ 'advsearch.server.legend': 'What the server searches',
1290
+ 'advsearch.client.legend': 'Narrow the results that come back',
1291
+ 'advsearch.query.label': 'Words',
1292
+ 'advsearch.query.placeholder': 'Part of a name, or words inside a file',
1293
+ 'advsearch.scope.label': 'Look in',
1294
+ 'advsearch.scope.name': 'Names and paths',
1295
+ 'advsearch.scope.content': 'File contents',
1296
+ 'advsearch.scope.all': 'Both',
1297
+ 'advsearch.scope.hint':
1298
+ 'Contents are searched from the index, which covers text-like files up to 200 KB.',
1299
+ 'advsearch.people.name_only':
1300
+ 'Owner is read off the rows that come back, and only a name search returns rows that carry one.',
1301
+ 'advsearch.scope.name_only':
1302
+ 'Only names and paths here: a content hit reaches the explorer without the drive it came from, so content search is offered on single-storage deployments only.',
1303
+ 'advsearch.tags.label': 'Tags',
1304
+ 'advsearch.tags.placeholder': 'invoice, quarterly report',
1305
+ 'advsearch.tags.exclude': 'Without tags',
1306
+ 'advsearch.tags.exclude_placeholder': 'archive',
1307
+ 'advsearch.tags.hint':
1308
+ 'Separate with commas. Tags narrow: several tags means files carrying all of them, and a tag nobody has applied returns nothing.',
1309
+ 'advsearch.where.label': 'Folder',
1310
+ 'advsearch.where.any': 'Anywhere in this storage',
1311
+ 'advsearch.where.here': 'Only in {folder}',
1312
+ 'advsearch.where.skip': 'Everywhere except {folder}',
1313
+ 'advsearch.around.label': 'Within',
1314
+ 'advsearch.around.date': 'Date and time',
1315
+ 'advsearch.around.h1': '1 hour',
1316
+ 'advsearch.around.d1': '1 day',
1317
+ 'advsearch.around.w1': '1 week',
1318
+ 'advsearch.size.range_label': 'Between',
1319
+ 'advsearch.size.from': 'From',
1320
+ 'advsearch.size.to': 'To',
1321
+ 'advsearch.count.idle': 'Type something, or pick a tag.',
1322
+ 'advsearch.count.counting': 'Counting…',
1323
+ 'advsearch.count.result': '{n} matching items',
1324
+ 'advsearch.count.capped': '{n} matching items in the first page of hits',
1325
+ 'advsearch.count.result_one': '1 matching item',
1326
+ 'advsearch.count.capped_one': '1 matching item in the first page of hits',
1327
+ 'advsearch.count.error': 'The count could not be run.',
1328
+ 'advsearch.count.cost':
1329
+ 'The count runs this search. Type, owner, date, size and folder are applied to the rows that come back, not by the server.',
1330
+ 'advsearch.wire': 'Sent as',
1331
+ 'advsearch.reset': 'Reset',
1332
+ 'advsearch.cancel': 'Cancel',
1333
+ 'advsearch.submit': 'Search',
1334
+
1335
+ /* === gorunum:v2-topbar — the page bar is gone; the explorer's own header
1336
+ and breadcrumb row carry what it used to. === */
1337
+ 'breadcrumb.subfolders': 'Subfolders',
1338
+ 'breadcrumb.subfolders.empty': 'No subfolders here',
1339
+ 'header.admin': 'Admin panel',
1340
+ 'header.settings': 'Settings',
1341
+ 'header.signout': 'Sign out',
1342
+ 'ai.assistant': 'AI assistant',
1343
+ 'ai.soon': 'Coming soon',
1344
+ 'ai.assistant.soon': 'AI assistant — coming soon',
1345
+
1346
+ /* gorunum:v2-share — the Share / Permissions dialog's own chrome. The
1347
+ controls inside it keep the strings they already had; these are the new
1348
+ shell: the title, the one switch, the sentence that says who can open the
1349
+ item right now, and the three named sections it all folds into. */
1350
+ 'access.title': 'Share "{name}"',
1351
+ 'access.close': 'Close',
1352
+ 'access.done': 'Done',
1353
+ 'access.copy': 'Copy',
1354
+ 'access.copied': 'Copied',
1355
+ 'access.link.switch': 'Link sharing',
1356
+ 'access.who.private': 'Only people with access can open it.',
1357
+ 'access.who.link': 'Anyone with the link can open it.',
1358
+ 'access.who.pin': 'Anyone with the link and the PIN can open it.',
1359
+ 'access.who.existing': 'Link sharing is on — {n} links already exist.',
1360
+ 'access.who.existing_one': 'Link sharing is on — 1 link already exists.',
1361
+ 'access.section.link': 'Link options',
1362
+ 'access.section.people': 'People with access',
1363
+ 'access.section.drop': 'Request files',
1364
+ 'access.sum.pin_on': 'PIN on',
1365
+ 'access.sum.pin_off': 'No PIN',
1366
+ 'access.sum.people': '{n} people',
1367
+ 'access.sum.people_one': '1 person',
1368
+ 'access.sum.people_none': 'No one yet',
1369
+ 'access.sum.drop': '{n} upload links',
1370
+ 'access.sum.drop_one': '1 upload link',
1371
+ 'access.sum.drop_none': 'No upload link',
1372
+ /* Ownership — the Owner column and the People chip. "System" is the honest
1373
+ word for a row nobody put here through filex (the scanner found it, or it
1374
+ was written straight into the bucket); it is a real answer, not a blank. */
1375
+ 'col.owner': 'Owner',
1376
+ 'owner.you': 'You',
1377
+ 'owner.system': 'System',
1378
+ 'owner.unknown': 'Unknown',
1379
+ 'owner.external': 'Uploaded through an upload link',
1380
+ 'owner.last_actor': 'Last changed by {who}',
1381
+ 'filter.people': 'People',
1382
+ 'filter.people.any': 'Anyone',
1383
+ 'filter.people.me': 'You',
1384
+ 'filter.people.system': 'System',
1385
+ 'filter.people.someone': 'Someone else',
1386
+ /* surucu:d1-sort — the listing sort control at the trailing edge of the
1387
+ filter row. The four key names are `col.*`: the menu offers exactly the
1388
+ words the list view's own column headers carry, so the two controls that
1389
+ drive one sort cannot name it differently. */
1390
+ 'sort.by': 'Sort by',
1391
+ 'sort.asc': 'Ascending',
1392
+ 'sort.desc': 'Descending',
1393
+ /* The order a SEARCH RESULT is in. It is not a fifth sort key — it is what
1394
+ the control prints instead of a key while the rows on screen were ranked
1395
+ by the server, so the button stops claiming a sort it is not performing.
1396
+ `_why` is the reason the control is closed, and the owner asked for the
1397
+ reason to be written rather than for the control to go quietly inert. */
1398
+ 'sort.relevance': 'Relevance',
1399
+ 'sort.relevance_why':
1400
+ 'Search results are listed by how well they match. Your sort comes back when you leave the search.',
1401
+ /* === tablo:t1 — the table's own configuration ==========================
1402
+ Column widths and visibility are a preference about YOUR SCREEN; the view
1403
+ a folder opens in is a fact about the FOLDER. Both are set from here, and
1404
+ `folder_hint` is the rule in one sentence, written where the person who is
1405
+ about to be surprised by it can read it. */
1406
+ 'col.location': 'Location',
1407
+ 'cols.menu': 'Column options',
1408
+ 'cols.title': 'Columns',
1409
+ 'cols.resize': 'Resize {col}',
1410
+ 'cols.move_left': 'Move {col} left',
1411
+ 'cols.move_right': 'Move {col} right',
1412
+ 'cols.reset': 'Reset columns',
1413
+ 'cols.folder_title': 'Folder view',
1414
+ 'cols.folder_hint':
1415
+ 'Folders you set up keep their own view. Your last choice is the default everywhere else.',
1416
+ 'cols.forget_folder': "Forget this folder's view",
1417
+ 'cols.apply_all': 'Apply to all folders ({count} remembered)',
1418
+ /* === tablo:t1 — the trash banner ======================================
1419
+ ⚠ `retention` is only used when the deployment has TOLD us the number
1420
+ (GET /api/admin/protection). Everywhere else `retention_unknown` says the
1421
+ same thing without naming a period we cannot verify: a banner that states
1422
+ the wrong retention is worse than no banner, because people act on it. */
1423
+ 'trash.retention': 'Items in the trash are permanently deleted after {days} days.',
1424
+ 'trash.retention_one': 'Items in the trash are permanently deleted after 1 day.',
1425
+ 'trash.retention_unknown':
1426
+ 'Items in the trash are permanently deleted after a retention period set by your administrator.',
1427
+ 'trash.empty_action': 'Empty trash',
1428
+ 'trash.empty_confirm_title': 'Empty the trash?',
1429
+ 'trash.empty_confirm_body_one': 'This permanently deletes 1 item ({size}). It cannot be undone.',
1430
+ 'trash.empty_confirm_body':
1431
+ 'This permanently deletes {count} items ({size}). It cannot be undone.',
1432
+ 'trash.empty_confirm_body_nosize_one': 'This permanently deletes 1 item. It cannot be undone.',
1433
+ 'trash.empty_confirm_body_nosize': 'This permanently deletes {count} items. It cannot be undone.',
1434
+ 'trash.emptied': 'Trash emptied',
1435
+ 'empty.trash.hint': 'Files you delete land here first, so you can put them back.',
1436
+
1437
+ /* tasi:m1 — the destination picker (modals/DestinationPickerModal.vue) and
1438
+ the two selection verbs built on it. */
1439
+ 'destpicker.title.move': 'Move to',
1440
+ 'destpicker.title.copy': 'Copy to',
1441
+ 'destpicker.title.choose': 'Choose a folder',
1442
+ 'destpicker.confirm.move': 'Move here',
1443
+ 'destpicker.confirm.copy': 'Copy here',
1444
+ 'destpicker.confirm.choose': 'Choose this folder',
1445
+ 'destpicker.cancel': 'Cancel',
1446
+ 'destpicker.up': 'Up one level',
1447
+ 'destpicker.crumbs': 'Location',
1448
+ 'destpicker.drives': 'Drives',
1449
+ 'destpicker.loading': 'Loading…',
1450
+ 'destpicker.empty': 'No subfolders here',
1451
+ 'destpicker.unreadable': 'This folder cannot be opened.',
1452
+ 'destpicker.readonly': 'Read-only',
1453
+ 'destpicker.readonly_here': 'You cannot write into this folder.',
1454
+ 'destpicker.pick_a_drive': 'Open a storage to choose a folder inside it.',
1455
+ 'destpicker.target': 'Destination: {name}',
1456
+ 'destpicker.tag.blocked': 'Not allowed',
1457
+ 'destpicker.blocked.self': 'A folder cannot be moved into itself.',
1458
+ 'destpicker.blocked.descendant': 'A folder cannot be moved into one of its own subfolders.',
1459
+
1460
+ 'ctx.move_to': 'Move to…',
1461
+ 'ctx.copy_to': 'Copy to…',
1462
+ 'ctx.access.one_only': 'Sharing works on one item at a time.',
1463
+
1464
+ 'toast.archive.preparing': 'Preparing the archive…',
1465
+ 'toast.archive.started': 'Downloading {name} ({count} files)',
1466
+ 'toast.archive.started_one': 'Downloading {name} (1 file)',
1467
+ 'toast.archive.empty': 'Nothing to download — the selection contains no readable file.',
1468
+ 'toast.moved_to': 'Moved to {name}',
1469
+ 'toast.copied_to': 'Copy to {name} queued',
1080
1470
  };