@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
@@ -0,0 +1,244 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * ThemePalette — the grid of palette cards, and nothing else.
4
+ *
5
+ * Each card is a mini live preview (surface + toolbar chips + sample rows)
6
+ * painted with that palette's own tokens for the currently resolved light/dark
7
+ * mode, so the card shows exactly what you would get. Click emits `select`;
8
+ * the selected card carries a ✓.
9
+ *
10
+ * ⚠ Split out of `ThemeGallery.vue` rather than copied: the appearance
11
+ * controls now have TWO hosts — the explorer's own gallery modal, and the web
12
+ * app's user-settings modal, which cannot mount the gallery because the
13
+ * gallery IS a modal (`modals/Modal.vue`, a fixed full-viewport backdrop) and
14
+ * a dialog inside a dialog is not a settings pane. Duplicating thirty lines of
15
+ * card markup into `web/` would have meant the two pickers drift apart the
16
+ * first time a token is added to a preview — the same split this package
17
+ * exists to prevent. One component, two hosts.
18
+ *
19
+ * Presentational only. Selection state + persistence live in lib/themes.ts,
20
+ * shared across every instance on the page; the host forwards `select` to it.
21
+ *
22
+ * `default` is the first card and IS the reset: applying it clears every
23
+ * override. A host therefore needs no separate "reset" control (the gallery
24
+ * keeps one in its footer because a modal has a footer to keep it in).
25
+ */
26
+ import type { LocaleCode } from '../types/ExplorerConfig';
27
+ import { useLocale } from '../composables/useLocale';
28
+ import { THEMES, type ThemeDef } from '../lib/themes';
29
+
30
+ const props = defineProps<{
31
+ locale: LocaleCode;
32
+ /** Resolved mode — decides which variant the preview cards paint. */
33
+ dark: boolean;
34
+ /** Currently selected theme id. */
35
+ current: string;
36
+ /** Draw the explanatory line above the grid. Default true. */
37
+ hint?: boolean;
38
+ }>();
39
+
40
+ const emit = defineEmits<{ (e: 'select', id: string): void }>();
41
+
42
+ const { t } = useLocale(() => props.locale);
43
+
44
+ /** Inline variables for one card — a self-contained mini `--fe-*` scope,
45
+ * so the preview markup can use the exact same tokens the real UI uses. */
46
+ function cardVars(th: ThemeDef): Record<string, string> {
47
+ return props.dark ? th.dark : th.light;
48
+ }
49
+ </script>
50
+
51
+ <template>
52
+ <p v-if="hint !== false" class="fe-themes__hint">{{ t('theme.hint') }}</p>
53
+ <div class="fe-themes" role="listbox" :aria-label="t('theme.title')">
54
+ <button
55
+ v-for="th in THEMES"
56
+ :key="th.id"
57
+ type="button"
58
+ class="fe-themecard"
59
+ :class="{ 'is-selected': th.id === current }"
60
+ role="option"
61
+ :aria-selected="th.id === current"
62
+ :data-theme-id="th.id"
63
+ :style="cardVars(th)"
64
+ @click="emit('select', th.id)"
65
+ >
66
+ <span class="fe-themecard__preview" aria-hidden="true">
67
+ <span class="fe-themecard__topbar">
68
+ <span class="fe-themecard__chip fe-themecard__chip--primary"></span>
69
+ <span class="fe-themecard__chip"></span>
70
+ <span class="fe-themecard__chip fe-themecard__chip--ghost"></span>
71
+ </span>
72
+ <span class="fe-themecard__row">
73
+ <span class="fe-themecard__dot"></span>
74
+ <span class="fe-themecard__line" style="width: 62%"></span>
75
+ </span>
76
+ <span class="fe-themecard__row fe-themecard__row--selected">
77
+ <span class="fe-themecard__dot fe-themecard__dot--primary"></span>
78
+ <span class="fe-themecard__line" style="width: 44%"></span>
79
+ </span>
80
+ <span class="fe-themecard__row">
81
+ <span class="fe-themecard__dot fe-themecard__dot--muted"></span>
82
+ <span class="fe-themecard__line fe-themecard__line--muted" style="width: 74%"></span>
83
+ </span>
84
+ </span>
85
+ <span class="fe-themecard__name">
86
+ {{ t(th.nameKey) }}
87
+ <span v-if="th.id === current" class="fe-themecard__check" :title="t('theme.selected')">✓</span>
88
+ </span>
89
+ </button>
90
+ </div>
91
+ </template>
92
+
93
+ <!-- Deliberately NOT scoped: the webcomponent injects the CORE package's
94
+ built style.css, whose data-v scope ids don't match the ids the
95
+ webcomponent's own compile assigns (verified: fe-themecard got
96
+ data-v-99a95ed7 in core vs data-v-5d4cc1fd in the wc bundle) — scoped
97
+ rules would silently not apply in embeds. Class names are fe-*
98
+ namespaced, so global is collision-safe (same approach as base.css). -->
99
+ <style>
100
+ .fe-themes__hint {
101
+ margin: 0 0 12px;
102
+ font-size: 12.5px;
103
+ color: var(--fe-text-muted);
104
+ }
105
+
106
+ .fe-themes {
107
+ display: grid;
108
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
109
+ gap: 12px;
110
+ }
111
+
112
+ /* Each card is its own mini token scope (inline --fe-* vars from the theme
113
+ definition), so every color below refers to the CARD's theme — not the
114
+ active page theme. */
115
+ .fe-themecard {
116
+ display: flex;
117
+ flex-direction: column;
118
+ padding: 0;
119
+ border: 2px solid var(--fe-border-strong);
120
+ border-radius: var(--fe-radius, 8px);
121
+ background: var(--fe-bg);
122
+ cursor: pointer;
123
+ overflow: hidden;
124
+ text-align: left;
125
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
126
+ font: inherit;
127
+ }
128
+
129
+ .fe-themecard:hover {
130
+ transform: translateY(-2px);
131
+ box-shadow: var(--fe-shadow-sm, 0 2px 6px rgba(15, 23, 42, 0.08));
132
+ }
133
+
134
+ .fe-themecard:focus-visible {
135
+ outline: 2px solid var(--fe-primary);
136
+ outline-offset: 2px;
137
+ }
138
+
139
+ .fe-themecard.is-selected {
140
+ border-color: var(--fe-primary);
141
+ box-shadow: 0 0 0 2px var(--fe-primary);
142
+ }
143
+
144
+ .fe-themecard__preview {
145
+ display: flex;
146
+ flex-direction: column;
147
+ gap: 6px;
148
+ padding: 10px;
149
+ background: var(--fe-bg);
150
+ border-bottom: 1px solid var(--fe-border);
151
+ min-height: 84px;
152
+ }
153
+
154
+ .fe-themecard__topbar {
155
+ display: flex;
156
+ gap: 4px;
157
+ margin-bottom: 2px;
158
+ }
159
+
160
+ .fe-themecard__chip {
161
+ height: 8px;
162
+ width: 22px;
163
+ border-radius: 4px;
164
+ background: var(--fe-bg-hover);
165
+ border: 1px solid var(--fe-border);
166
+ }
167
+
168
+ .fe-themecard__chip--primary {
169
+ background: var(--fe-primary);
170
+ border-color: var(--fe-primary);
171
+ }
172
+
173
+ .fe-themecard__chip--ghost {
174
+ background: var(--fe-bg-elev);
175
+ }
176
+
177
+ .fe-themecard__row {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 6px;
181
+ padding: 3px 4px;
182
+ border-radius: 4px;
183
+ }
184
+
185
+ .fe-themecard__row--selected {
186
+ background: var(--fe-bg-selected);
187
+ }
188
+
189
+ .fe-themecard__dot {
190
+ width: 8px;
191
+ height: 8px;
192
+ border-radius: 50%;
193
+ background: var(--fe-text);
194
+ opacity: 0.75;
195
+ flex: none;
196
+ }
197
+
198
+ .fe-themecard__dot--primary {
199
+ background: var(--fe-primary);
200
+ opacity: 1;
201
+ }
202
+
203
+ .fe-themecard__dot--muted {
204
+ background: var(--fe-text-muted);
205
+ }
206
+
207
+ .fe-themecard__line {
208
+ height: 6px;
209
+ border-radius: 3px;
210
+ background: var(--fe-text);
211
+ opacity: 0.55;
212
+ }
213
+
214
+ .fe-themecard__line--muted {
215
+ background: var(--fe-text-muted);
216
+ }
217
+
218
+ .fe-themecard__name {
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: space-between;
222
+ gap: 8px;
223
+ padding: 8px 10px;
224
+ font-size: 12.5px;
225
+ font-weight: 600;
226
+ color: var(--fe-text);
227
+ background: var(--fe-bg-elev);
228
+ font-family: var(--fe-font, inherit);
229
+ }
230
+
231
+ .fe-themecard__check {
232
+ flex: none;
233
+ width: 18px;
234
+ height: 18px;
235
+ display: inline-flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ border-radius: 50%;
239
+ background: var(--fe-primary);
240
+ color: var(--fe-text-on-primary);
241
+ font-size: 11px;
242
+ line-height: 1;
243
+ }
244
+ </style>
@@ -0,0 +1,135 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * TimeZoneDialog — zaman:z3, the embed's own time-zone setting.
4
+ *
5
+ * The owner, 2026-09-14: *"gömme için ayar bırakmamız lazım; basitçe tarayıcıda
6
+ * tutulan bir ayar olması lazım."* An embed has no user-settings dialog — its
7
+ * "⋯" menu (theme, density, shortcuts) is where its settings live — so the menu
8
+ * opens this, and a pick is stored in THIS browser (`lib/timezone`, tier
9
+ * `viewer`). Nothing is sent anywhere: an embed's credential may be shared by
10
+ * every visitor, and there is no account of the visitor's to write to.
11
+ *
12
+ * ⚠ The picker is `TimeZonePicker`, the same component the web app's settings
13
+ * modal mounts. Not a second one.
14
+ *
15
+ * ⚠ The first row is "use the default", and it names what the default IS on
16
+ * this page — the zone the embedder configured, the account's zone, or this
17
+ * device's — by asking the resolver with the viewer tier blanked. A reset
18
+ * whose outcome you have to guess is not a reset.
19
+ */
20
+ import { computed, inject, onBeforeUnmount, ref, watch } from 'vue';
21
+ import type { LocaleCode, ThemeMode } from '../types/ExplorerConfig';
22
+ import { formatInstant, useLocale } from '../composables/useLocale';
23
+ import {
24
+ EXPLORER_CLOCK,
25
+ deviceTimeZone,
26
+ resolveTimeZone,
27
+ resolvedTimeZoneFor,
28
+ setViewerTimeZone,
29
+ timeZoneSourcesFor,
30
+ viewerTimeZone,
31
+ } from '../lib/timezone';
32
+ import Modal from '../modals/Modal.vue';
33
+ import TimeZonePicker from './TimeZonePicker.vue';
34
+
35
+ const props = defineProps<{
36
+ open: boolean;
37
+ locale: LocaleCode;
38
+ theme?: ThemeMode;
39
+ }>();
40
+
41
+ const emit = defineEmits<{ (e: 'close'): void }>();
42
+
43
+ const { t } = useLocale(() => props.locale);
44
+
45
+ const inputId = `fe-tzdialog-${Math.random().toString(36).slice(2, 9)}`;
46
+
47
+ // This explorer's clock, not the page's newest (lib/timezone, timeZoneSourcesFor).
48
+ const clock = inject(EXPLORER_CLOCK, undefined);
49
+ const fallback = computed(() => resolveTimeZone({ ...timeZoneSourcesFor(clock), viewer: '' }));
50
+ const inForce = computed(() => resolvedTimeZoneFor(clock));
51
+ const inForceZone = computed(() => inForce.value.zone ?? deviceTimeZone());
52
+
53
+ /** "Right now in Asia/Tokyo: 14:05:09" — the fastest way to see a wrong pick. */
54
+ const now = ref('');
55
+ let timer: ReturnType<typeof setInterval> | undefined;
56
+ function refresh(): void {
57
+ // Through the shared formatter, which reads the RESOLVED zone — the readout
58
+ // must say what every date on the page is actually drawn in, not what this
59
+ // dialog thinks it chose.
60
+ now.value = formatInstant(
61
+ new Date(),
62
+ props.locale,
63
+ {
64
+ hour: '2-digit',
65
+ minute: '2-digit',
66
+ second: '2-digit',
67
+ },
68
+ clock,
69
+ );
70
+ }
71
+ watch(
72
+ () => props.open,
73
+ (open) => {
74
+ if (timer) clearInterval(timer);
75
+ timer = undefined;
76
+ if (!open) return;
77
+ refresh();
78
+ timer = setInterval(refresh, 1000);
79
+ },
80
+ { immediate: true },
81
+ );
82
+ watch(inForce, refresh);
83
+ onBeforeUnmount(() => {
84
+ if (timer) clearInterval(timer);
85
+ });
86
+ </script>
87
+
88
+ <template>
89
+ <Modal :open="open" :title="t('tz.title')" size="md" :theme="theme" @close="emit('close')">
90
+ <div class="fe-tzdialog" data-testid="fe-tz-dialog">
91
+ <label class="fe-tzdialog__label" :for="inputId">{{ t('tz.label') }}</label>
92
+ <TimeZonePicker
93
+ :input-id="inputId"
94
+ :locale="locale"
95
+ :model-value="viewerTimeZone()"
96
+ :fallback="fallback"
97
+ :label="t('tz.label')"
98
+ testid="fe-tz"
99
+ @update:model-value="setViewerTimeZone"
100
+ />
101
+ <p class="fe-tzdialog__hint">{{ t('tz.hint') }}</p>
102
+ <p class="fe-tzdialog__now" data-testid="fe-tz-now" :data-tier="inForce.tier">
103
+ {{ t('tz.now', { zone: inForceZone, time: now }) }}
104
+ </p>
105
+ </div>
106
+ </Modal>
107
+ </template>
108
+
109
+ <!-- Not scoped — see TimeZonePicker.vue / ThemePalette.vue. -->
110
+ <style>
111
+ /* Tall enough that the zone list opens downward inside the dialog instead of
112
+ being clipped by the body's own scroll box. */
113
+ .fe-tzdialog {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: var(--fe-gap-sm);
117
+ min-height: 340px;
118
+ }
119
+ .fe-tzdialog__label {
120
+ font-size: var(--fe-text-sm);
121
+ font-weight: 500;
122
+ color: var(--fe-text);
123
+ }
124
+ .fe-tzdialog__hint,
125
+ .fe-tzdialog__now {
126
+ margin: 0;
127
+ font-size: var(--fe-text-xs);
128
+ line-height: 1.45;
129
+ color: var(--fe-text-muted);
130
+ }
131
+ .fe-tzdialog__now {
132
+ font-size: var(--fe-text-sm);
133
+ font-variant-numeric: tabular-nums;
134
+ }
135
+ </style>