@brftech/filex-core 0.39.1 → 0.41.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.
Files changed (125) hide show
  1. package/README.md +32 -25
  2. package/dist/ArchiveViewer-BNqukFg8.js +124 -0
  3. package/dist/ArchiveViewer-BNqukFg8.js.map +1 -0
  4. package/dist/CsvViewer-ii_-MgmG.js +141 -0
  5. package/dist/CsvViewer-ii_-MgmG.js.map +1 -0
  6. package/dist/{DrawioViewer-BNALOB04.js → DrawioViewer-B2tuu4rX.js} +48 -42
  7. package/dist/DrawioViewer-B2tuu4rX.js.map +1 -0
  8. package/dist/EpubViewer-p4B8iiWb.js +163 -0
  9. package/dist/EpubViewer-p4B8iiWb.js.map +1 -0
  10. package/dist/IpynbViewer-D_qYRJhu.js +184 -0
  11. package/dist/IpynbViewer-D_qYRJhu.js.map +1 -0
  12. package/dist/MermaidViewer-3ZkfNM8v.js +137 -0
  13. package/dist/MermaidViewer-3ZkfNM8v.js.map +1 -0
  14. package/dist/PsdViewer-CuV-OPSd.js +122 -0
  15. package/dist/PsdViewer-CuV-OPSd.js.map +1 -0
  16. package/dist/TiffViewer-BLeoF4Be.js +142 -0
  17. package/dist/TiffViewer-BLeoF4Be.js.map +1 -0
  18. package/dist/Viewer3D-DHhhU76E.js +75 -0
  19. package/dist/Viewer3D-DHhhU76E.js.map +1 -0
  20. package/dist/filex-core.js +217 -20077
  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-BraG7Cz4.js +26664 -0
  25. package/dist/index-BraG7Cz4.js.map +1 -0
  26. package/dist/index.d.ts +4276 -851
  27. package/dist/style.css +1 -1
  28. package/package.json +1 -1
  29. package/src/FileExplorer.vue +2170 -492
  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 +57 -2
  35. package/src/components/EncryptedFolderModal.vue +31 -25
  36. package/src/components/FilePane.vue +939 -0
  37. package/src/components/FilterBar.vue +568 -72
  38. package/src/components/GalleryView.vue +162 -30
  39. package/src/components/GridView.vue +322 -58
  40. package/src/components/HomeView.vue +331 -0
  41. package/src/components/InspectorPanel.vue +296 -53
  42. package/src/components/ListView.vue +1158 -167
  43. package/src/components/NFSExportsPanel.vue +7 -4
  44. package/src/components/OnboardingTour.vue +156 -33
  45. package/src/components/QuickLook.vue +34 -5
  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 +43 -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 +127 -0
  58. package/src/components/TimeZonePicker.vue +696 -0
  59. package/src/components/TokensPanel.vue +16 -4
  60. package/src/components/Toolbar.vue +965 -469
  61. package/src/components/ViewSwitcher.vue +9 -4
  62. package/src/composables/useConnections.ts +35 -4
  63. package/src/composables/useExplorerTimeZone.ts +85 -0
  64. package/src/composables/useFileApi.ts +53 -4
  65. package/src/composables/useKeyboardShortcuts.ts +122 -0
  66. package/src/composables/useLocale.ts +365 -25
  67. package/src/index.ts +182 -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 +11 -1
  78. package/src/lib/sortOrder.ts +459 -0
  79. package/src/lib/themes.ts +152 -23
  80. package/src/lib/timezone.ts +456 -0
  81. package/src/lib/uiProfile.ts +82 -0
  82. package/src/lib/viewPrefs.ts +1036 -0
  83. package/src/locales/en.ts +432 -25
  84. package/src/locales/tr.ts +425 -25
  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 +543 -473
  91. package/src/modals/PreviewModal.vue +516 -156
  92. package/src/styles/base.css +5435 -8
  93. package/src/styles/variables.css +140 -60
  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
@@ -8,14 +8,26 @@
8
8
  * square tiles: thumbnail (or SVG file-type icon fallback) with the name
9
9
  * below and size+date revealed on hover/focus. GridView itself is untouched.
10
10
  */
11
- import { ref } from 'vue';
11
+ import { computed, onBeforeUnmount, ref } from 'vue';
12
12
  import { hasInternalDrag } from '../lib/dragOut';
13
13
  import type { FileNode } from '../types/FileNode';
14
14
  import type { LocaleCode } from '../types/ExplorerConfig';
15
15
  import { useLocale } from '../composables/useLocale';
16
- import { fileIconSvg } from '../lib/fileIcons';
16
+ import { encryptedFolderTile, fileIconTile, isEncryptedFolder } from '../lib/fileIcons';
17
+ import {
18
+ createFilePreviews,
19
+ drawsAsPage,
20
+ drawsAsVideo,
21
+ type FilePreview,
22
+ } from '../lib/filePreview'; /* gorunum:v1-preview */
17
23
  import StarButton from './StarButton.vue';
18
24
  import { applyDragGhost } from '../lib/dragGhost';
25
+ import { parentDirOf } from '../lib/listing'; /* the folder a row sits in — one split rule for every view */
26
+ import {
27
+ groupByDate,
28
+ groupingActive,
29
+ } from '../lib/dateGroups'; /* gruplama */
30
+ import { useSortStore, type ListingOrder } from '../lib/sortOrder'; /* gruplama */
19
31
 
20
32
  const props = defineProps<{
21
33
  files: FileNode[];
@@ -50,6 +62,17 @@ const props = defineProps<{
50
62
  apiBase?: string;
51
63
  authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
52
64
  authCredentials?: RequestCredentials;
65
+ /** gorunum:v1-preview — inside an E2E-encrypted folder every body is
66
+ * ciphertext, so the text preview stays off. Same prop, same meaning and
67
+ * same default as GridView's. */
68
+ e2eActive?: boolean;
69
+ /**
70
+ * gruplama — where these rows got their order, the same prop the list and
71
+ * the grid take (`lib/sortOrder.ListingOrder`). Read for one thing only: a
72
+ * date heading over the server's RANKED answer would be a claim about an
73
+ * order the tiles are not in. Omitted = an ordinary folder listing.
74
+ */
75
+ order?: ListingOrder;
53
76
  }>();
54
77
 
55
78
  const emit = defineEmits<{
@@ -61,12 +84,68 @@ const emit = defineEmits<{
61
84
  (e: 'star-change', node: FileNode, value: boolean): void;
62
85
  }>();
63
86
 
64
- const { t, formatSize, nodeDisplayName } = useLocale(() => props.locale);
87
+ const { t, formatSize, nodeDisplayName, formatDate, formatMonthYear, zonedYearMonth, toDate } =
88
+ useLocale(() => props.locale);
89
+
90
+ /* gruplama — this pane's sort, by injection, so the gallery cannot disagree
91
+ * with the list beside it about whether a date heading is honest right now. */
92
+ const sort = useSortStore();
93
+
94
+ /**
95
+ * gruplama — THE DATE HEADINGS, from the one module that owns the ladder
96
+ * (`lib/dateGroups`), read identically by ListView and GridView.
97
+ *
98
+ * ⚠ The gallery has no Folders / Files sections to collide with, so nothing
99
+ * here replaces anything: folders keep a run of their own at the top — with no
100
+ * heading, because the gallery has never named one — and each day of files
101
+ * gets its label. Any sort key but `modified` draws nothing at all, exactly as
102
+ * before this existed.
103
+ *
104
+ * ⚠ The tiles are handed to us ALREADY sorted (`FilePane.displayFiles` runs
105
+ * the pane's comparator, folders first, then the key), which is why this
106
+ * groups `props.files` in place rather than re-sorting: a second sort here is
107
+ * a second opinion about the order, and the grid learned that lesson once.
108
+ */
109
+ const grouped = computed(() =>
110
+ groupByDate(props.files, {
111
+ active: groupingActive(sort.key.value, props.order),
112
+ dateOf: (n) => toDate(n.last_modified),
113
+ aside: (n) => (n.type === 'dir' ? { id: 'dirs' } : null),
114
+ labels: { t, formatMonthYear, zonedYearMonth },
115
+ }),
116
+ );
65
117
 
66
118
  function thumbOf(n: FileNode): string | null {
67
119
  return props.thumbSrc ? props.thumbSrc(n) : (n.thumb_url ?? null);
68
120
  }
69
121
 
122
+ /* gorunum:v1-preview — the SAME preview the grid card draws, from the same
123
+ * module and with the same bounds. Not a gallery variant: a text file in a
124
+ * gallery is the identical hole (the backend's generic extension-card), and
125
+ * "open it on the desktop, leave the embeds on the old behaviour" is how one
126
+ * product becomes two. Only the type scale differs, and that is CSS. */
127
+ const previews = createFilePreviews({
128
+ apiBase: props.apiBase,
129
+ authHeaders: props.authHeaders ? () => props.authHeaders!() : undefined,
130
+ credentials: props.authCredentials,
131
+ disabled: () => props.e2eActive === true,
132
+ });
133
+ onBeforeUnmount(() => previews.dispose());
134
+
135
+ function previewKind(n: FileNode) {
136
+ return previews.kindFor(n);
137
+ }
138
+
139
+ /** At most one element — a typed local for the template, no `!` per loop. */
140
+ function previewFor(n: FileNode): FilePreview[] {
141
+ const p = previews.get(n);
142
+ return p ? [p] : [];
143
+ }
144
+
145
+ function bindPreview(el: unknown, n: FileNode) {
146
+ previews.bind(el instanceof Element ? el : null, n);
147
+ }
148
+
70
149
  /** A card carries a star when the host wired the API and the node is a file
71
150
  * with a server id — the same rule the list row uses. */
72
151
  function canStar(n: FileNode): boolean {
@@ -148,25 +227,15 @@ function cancelPress() {
148
227
  pressTarget = null;
149
228
  }
150
229
 
151
- function parentDir(path: string): string {
152
- const stripped = path.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
153
- const idx = stripped.lastIndexOf('/');
154
- if (idx === -1) return '';
155
- return stripped.slice(0, idx);
156
- }
157
-
158
- // Special rows keep their emoji (trash/storage are not file-TYPE icons).
159
- function specialEmojiFor(n: FileNode): string | null {
160
- if (n.basename === '.trash') return '🗑';
161
- if (n.mime_type === 'inode/storage') return '💾';
162
- if (n.type === 'dir' && n.e2e === true) return '🔒'; /* wiring:e2 — encrypted-folder badge */
163
- return null;
164
- }
165
-
230
+ /**
231
+ * zaman:z1 — routed through useLocale.formatDate, the package's one date
232
+ * formatter. It was a bare `toLocaleString()`, i.e. the BROWSER's locale and
233
+ * the BROWSER's zone: the same file's timestamp came out in a different
234
+ * language here than in the list two clicks away, and in a different clock
235
+ * than the viewer had chosen. Neither of those is a gallery decision.
236
+ */
166
237
  function displayDate(ms: number | undefined): string {
167
- if (!ms) return '';
168
- const d = new Date(ms * (ms < 1e12 ? 1000 : 1));
169
- return d.toLocaleString();
238
+ return formatDate(ms, { time: true });
170
239
  }
171
240
 
172
241
  // Hover meta line: size for files, entry count (when known) for dirs.
@@ -186,9 +255,13 @@ function metaFor(n: FileNode): string {
186
255
  :aria-label="t('gallery.aria')"
187
256
  :aria-busy="loading ? 'true' : undefined"
188
257
  >
258
+ <template v-for="n in files" :key="n.path">
259
+ <!-- gruplama — the date label: a grid item spanning every column, so the
260
+ tiles keep flowing under it. The cards keep `role="option"`; the
261
+ heading is aria-hidden and the day it names is already in each
262
+ card's own hover text. -->
263
+ <p v-if="grouped.headingBefore(n)" class="fe-gal__heading" aria-hidden="true">{{ grouped.headingBefore(n) }}</p>
189
264
  <div
190
- v-for="n in files"
191
- :key="n.path"
192
265
  class="fe-gal__card"
193
266
  :class="{
194
267
  'is-selected': isSelected(n),
@@ -214,23 +287,81 @@ function metaFor(n: FileNode): string {
214
287
  @touchend="cancelPress"
215
288
  @touchmove="cancelPress"
216
289
  >
217
- <div class="fe-gal__thumb">
290
+ <div class="fe-gal__thumb" :ref="(el) => bindPreview(el, n)">
291
+ <!-- gorunum:v1-preview — a text-readable file shows its own first
292
+ lines; it never reaches the <img> branch, so no generic
293
+ placeholder thumbnail is fetched for it either. -->
294
+ <template v-if="previewKind(n)">
295
+ <!-- ⚠ Interpolated, never v-html: this is somebody's file content. -->
296
+ <div
297
+ v-for="p in previewFor(n)"
298
+ :key="'fprev'"
299
+ class="fe-fprev"
300
+ :class="'fe-fprev--' + p.kind"
301
+ :style="p.kind === 'table' ? { '--fprev-cols': String(p.cols) } : undefined"
302
+ aria-hidden="true"
303
+ >
304
+ <!-- One grid, cells emitted flat: <template> makes no DOM, so a
305
+ short row's padding cells keep every column aligned. -->
306
+ <template v-if="p.kind === 'table'">
307
+ <template v-for="(row, ri) in p.rows" :key="ri">
308
+ <span
309
+ v-for="(cell, ci) in row"
310
+ :key="ci"
311
+ class="fe-fprev__cell"
312
+ :class="{ 'is-head': ri === 0 }"
313
+ >{{ cell }}</span>
314
+ </template>
315
+ </template>
316
+ <template v-else>
317
+ <div v-for="(ln, li) in p.lines" :key="li" class="fe-fprev__line"><span
318
+ v-if="ln.tint"
319
+ class="fe-fprev__tint"
320
+ >{{ ln.indent }}{{ ln.tint }}</span>{{ ln.text }}</div>
321
+ </template>
322
+ </div>
323
+ <!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
324
+ <span
325
+ v-if="previewFor(n).length === 0"
326
+ class="fe-gal__icon fe-gal__icon--svg"
327
+ v-html="fileIconTile(n)"
328
+ ></span>
329
+ </template>
218
330
  <!-- draggable="false" for the same reason as GridView: HTML5 image
219
331
  drag adds a 'Files' MIME to dataTransfer, which would trip the
220
332
  parent's upload handler on internal drags. -->
221
333
  <img
222
- v-if="thumbOf(n)"
334
+ v-else-if="thumbOf(n)"
223
335
  :src="thumbOf(n)!"
224
336
  :alt="n.basename"
337
+ :class="{ 'fe-thumb--page': drawsAsPage(n) /* gorunum:v1-preview — crop a page from its TOP */ }"
225
338
  loading="lazy"
226
339
  draggable="false"
227
340
  />
228
- <span v-else-if="specialEmojiFor(n)" class="fe-gal__icon">{{ specialEmojiFor(n) }}</span>
341
+ <!-- ikon:emoji see GridView: the folder shape survives, the padlock
342
+ is cut out of it, and the name the 🔒 never had is on the span. -->
343
+ <!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
344
+ <span
345
+ v-else-if="isEncryptedFolder(n)"
346
+ class="fe-gal__icon fe-gal__icon--svg"
347
+ role="img"
348
+ :aria-label="t('e2e.badge')"
349
+ v-html="encryptedFolderTile()"
350
+ ></span>
229
351
  <!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
230
- <span v-else class="fe-gal__icon fe-gal__icon--svg" v-html="fileIconSvg(n)"></span>
352
+ <span v-else class="fe-gal__icon fe-gal__icon--svg" v-html="fileIconTile(n)"></span>
231
353
  <!-- Same star chip as the grid, same component, same rule: painted
232
354
  when starred, on hover/focus otherwise. It sits above .fe-gal__meta
233
355
  (which is aria-hidden and covers the tile's foot on hover). -->
356
+ <!-- gorunum:v1-preview — a frame lifted out of a video is, on a card,
357
+ indistinguishable from a photograph. The badge is the difference,
358
+ and it is drawn only over a real frame: a video that fell back to
359
+ its type tile already says what it is. -->
360
+ <span
361
+ v-if="!previewKind(n) && thumbOf(n) && drawsAsVideo(n)"
362
+ class="fe-thumb__play"
363
+ aria-hidden="true"
364
+ ></span>
234
365
  <div v-if="canStar(n)" class="fe-gal__star" @click.stop @dblclick.stop>
235
366
  <StarButton
236
367
  :starred="!!starredIds?.has(n.id!)"
@@ -247,16 +378,17 @@ function metaFor(n: FileNode): string {
247
378
  <div class="fe-gal__meta" aria-hidden="true">
248
379
  <span v-if="metaFor(n)" class="fe-gal__meta-line">{{ metaFor(n) }}</span>
249
380
  <span
250
- v-if="showParentPath && parentDir(n.path)"
381
+ v-if="showParentPath && parentDirOf(n.path)"
251
382
  class="fe-gal__meta-line fe-gal__meta-line--path"
252
- :title="parentDir(n.path)"
253
- >{{ parentDir(n.path) }}</span>
383
+ :title="parentDirOf(n.path)"
384
+ >{{ parentDirOf(n.path) }}</span>
254
385
  </div>
255
386
  </div>
256
387
  <div class="fe-gal__label" :title="n.basename">
257
388
  {{ nodeDisplayName(n) }}
258
389
  </div>
259
390
  </div>
391
+ </template>
260
392
  <div v-if="!loading && files.length === 0" class="fe-gal__empty">
261
393
  {{ t('empty.folder') }}
262
394
  </div>