@brftech/filex-core 0.1.83 → 0.2.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 (45) hide show
  1. package/dist/{ArchiveViewer-BRASXNIS.js → ArchiveViewer-DCZ1FZYV.js} +2 -2
  2. package/dist/{ArchiveViewer-BRASXNIS.js.map → ArchiveViewer-DCZ1FZYV.js.map} +1 -1
  3. package/dist/{CsvViewer-CgiLiHWw.js → CsvViewer-DMvnp82x.js} +2 -2
  4. package/dist/{CsvViewer-CgiLiHWw.js.map → CsvViewer-DMvnp82x.js.map} +1 -1
  5. package/dist/{DrawioViewer-D2I_uEng.js → DrawioViewer-B5Io0yTO.js} +2 -2
  6. package/dist/{DrawioViewer-D2I_uEng.js.map → DrawioViewer-B5Io0yTO.js.map} +1 -1
  7. package/dist/{EpubViewer-6nVPRR6R.js → EpubViewer-z6RZd3K8.js} +2 -2
  8. package/dist/{EpubViewer-6nVPRR6R.js.map → EpubViewer-z6RZd3K8.js.map} +1 -1
  9. package/dist/{IpynbViewer-B7p9hxFx.js → IpynbViewer-DfL4EauH.js} +2 -2
  10. package/dist/{IpynbViewer-B7p9hxFx.js.map → IpynbViewer-DfL4EauH.js.map} +1 -1
  11. package/dist/{MermaidViewer-O9vGvp4y.js → MermaidViewer-QNl_o7V-.js} +2 -2
  12. package/dist/{MermaidViewer-O9vGvp4y.js.map → MermaidViewer-QNl_o7V-.js.map} +1 -1
  13. package/dist/{PsdViewer-C6EaEAxF.js → PsdViewer-Btmpr0Fz.js} +2 -2
  14. package/dist/{PsdViewer-C6EaEAxF.js.map → PsdViewer-Btmpr0Fz.js.map} +1 -1
  15. package/dist/{TiffViewer-Dod4uIXI.js → TiffViewer-CMLpwds5.js} +2 -2
  16. package/dist/{TiffViewer-Dod4uIXI.js.map → TiffViewer-CMLpwds5.js.map} +1 -1
  17. package/dist/{Viewer3D-B3kLZrwO.js → Viewer3D-gf3cb2gv.js} +2 -2
  18. package/dist/{Viewer3D-B3kLZrwO.js.map → Viewer3D-gf3cb2gv.js.map} +1 -1
  19. package/dist/filex-core.js +17 -15
  20. package/dist/filex-core.umd.cjs +38 -38
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/index-D49iw00E.js +7119 -0
  23. package/dist/index-D49iw00E.js.map +1 -0
  24. package/dist/index.d.ts +63 -0
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/src/FileExplorer.vue +440 -32
  28. package/src/components/Breadcrumb.vue +100 -5
  29. package/src/components/CommandPalette.vue +533 -0
  30. package/src/components/GridView.vue +8 -13
  31. package/src/components/ListView.vue +199 -21
  32. package/src/components/ShortcutsHelp.vue +59 -0
  33. package/src/components/Toolbar.vue +65 -1
  34. package/src/composables/useFileApi.ts +45 -0
  35. package/src/composables/useKeyboardShortcuts.ts +38 -0
  36. package/src/composables/useRealtime.ts +9 -1
  37. package/src/index.ts +4 -0
  38. package/src/lib/fileIcons.ts +127 -0
  39. package/src/lib/snippet.ts +43 -0
  40. package/src/locales/en.ts +62 -0
  41. package/src/locales/tr.ts +62 -0
  42. package/src/styles/base.css +650 -0
  43. package/src/styles/variables.css +70 -0
  44. package/dist/index-DL6_eaM3.js +0 -5888
  45. package/dist/index-DL6_eaM3.js.map +0 -1
@@ -0,0 +1,127 @@
1
+ /**
2
+ * fileIcons — extension/type → inline SVG icon mapping.
3
+ *
4
+ * Replaces the old emoji maps in GridView/ListView with a minimalist
5
+ * line-icon set. Icons are hand-drawn 24×24 strokes on `currentColor`;
6
+ * each family gets an accent via `--fe-icon-<family>` (variables.css),
7
+ * applied through the `fe-ficon--<family>` class in base.css.
8
+ *
9
+ * Scope: file-TYPE icons only. Action emojis (trash/star/…) and the
10
+ * special `.trash` / storage rows stay emoji — see the views.
11
+ */
12
+
13
+ export type IconFamily =
14
+ | 'folder'
15
+ | 'image'
16
+ | 'video'
17
+ | 'audio'
18
+ | 'pdf'
19
+ | 'doc'
20
+ | 'sheet'
21
+ | 'slides'
22
+ | 'archive'
23
+ | 'code'
24
+ | 'text'
25
+ | 'unknown';
26
+
27
+ const EXT_FAMILIES: Record<string, IconFamily> = {};
28
+
29
+ function reg(family: IconFamily, exts: string[]) {
30
+ for (const e of exts) EXT_FAMILIES[e] = family;
31
+ }
32
+
33
+ reg('image', ['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'avif', 'heic', 'svg', 'ico', 'tiff', 'tif']);
34
+ reg('video', ['mp4', 'webm', 'mov', 'mkv', 'avi', 'ogv', 'm4v']);
35
+ reg('audio', ['mp3', 'wav', 'flac', 'ogg', 'm4a', 'aac', 'opus']);
36
+ reg('pdf', ['pdf']);
37
+ reg('doc', ['doc', 'docx', 'odt', 'rtf']);
38
+ reg('sheet', ['xls', 'xlsx', 'ods', 'csv']);
39
+ reg('slides', ['ppt', 'pptx', 'odp']);
40
+ reg('archive', ['zip', 'tar', 'gz', 'bz2', '7z', 'rar', 'xz', 'zst']);
41
+ reg('code', [
42
+ 'js', 'ts', 'jsx', 'tsx', 'mjs', 'cjs', 'vue', 'py', 'go', 'rs', 'php', 'rb',
43
+ 'java', 'kt', 'swift', 'c', 'cpp', 'h', 'hpp', 'cs', 'css', 'scss', 'less',
44
+ 'html', 'htm', 'json', 'yml', 'yaml', 'xml', 'sh', 'bash', 'ps1', 'sql', 'toml',
45
+ ]);
46
+ reg('text', ['txt', 'md', 'markdown', 'log', 'ini', 'conf', 'cfg', 'env']);
47
+
48
+ /** Pick the icon family for a listing node. */
49
+ export function iconFamilyFor(node: {
50
+ type?: string;
51
+ extension?: string | null;
52
+ }): IconFamily {
53
+ if (node.type === 'dir') return 'folder';
54
+ const ext = (node.extension || '').toLowerCase();
55
+ return EXT_FAMILIES[ext] ?? 'unknown';
56
+ }
57
+
58
+ // Shared fragments. The document families reuse one sheet-with-fold base so
59
+ // the set reads as one system; media families get standalone shapes.
60
+ const FILE_BASE =
61
+ '<path d="M6.5 3h7L18.5 8v11.5a1.5 1.5 0 0 1-1.5 1.5H6.5A1.5 1.5 0 0 1 5 19.5v-15A1.5 1.5 0 0 1 6.5 3z"/>' +
62
+ '<path d="M13.5 3v5h5"/>';
63
+
64
+ const GLYPHS: Record<IconFamily, string> = {
65
+ // Folder is the one "filled" member — a soft currentColor wash keeps it
66
+ // visually anchored without breaking the line style.
67
+ folder:
68
+ '<path d="M3 7a2 2 0 0 1 2-2h4.2l2 2.4H19a2 2 0 0 1 2 2V17a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" fill="currentColor" fill-opacity="0.14"/>',
69
+ image:
70
+ '<rect x="3" y="5" width="18" height="14" rx="2"/>' +
71
+ '<circle cx="8.6" cy="10" r="1.6"/>' +
72
+ '<path d="M5.2 16.6l4.3-4.3 3 3 2.4-2.4 3.9 3.7"/>',
73
+ video:
74
+ '<rect x="3" y="5" width="18" height="14" rx="2"/>' +
75
+ '<path d="M10.2 9.3v5.4l4.8-2.7z" fill="currentColor" stroke="none"/>',
76
+ audio:
77
+ '<path d="M9 17.5V7.2l9-2.2v10.4"/>' +
78
+ '<circle cx="6.8" cy="17.6" r="2.2"/>' +
79
+ '<circle cx="15.8" cy="15.5" r="2.2"/>',
80
+ pdf:
81
+ FILE_BASE +
82
+ '<rect x="7.5" y="12" width="9" height="5" rx="1" fill="currentColor" fill-opacity="0.14"/>',
83
+ doc:
84
+ FILE_BASE +
85
+ '<path d="M8.5 13h7M8.5 16h7"/>',
86
+ sheet:
87
+ FILE_BASE +
88
+ '<path d="M8 12.5h8M8 15.5h8M8 18h8M12 12.5V18"/>',
89
+ slides:
90
+ FILE_BASE +
91
+ '<rect x="8" y="12.5" width="8" height="5" rx="0.8"/>',
92
+ archive:
93
+ FILE_BASE +
94
+ '<path d="M10 3v1.6M10 6.6v1.6M10 10.2v1.6"/>' +
95
+ '<rect x="8.6" y="14" width="2.8" height="3.4" rx="0.8"/>',
96
+ code:
97
+ FILE_BASE +
98
+ '<path d="M10.4 12.5L8.3 15l2.1 2.5M13.6 12.5l2.1 2.5-2.1 2.5"/>',
99
+ text:
100
+ FILE_BASE +
101
+ '<path d="M8.5 12h7M8.5 15h7M8.5 18h4.5"/>',
102
+ unknown:
103
+ FILE_BASE +
104
+ '<path d="M10.3 13.1a1.8 1.8 0 1 1 2.5 1.9c-.6.3-.8.7-.8 1.4"/>' +
105
+ '<circle cx="12" cy="18.4" r="0.9" fill="currentColor" stroke="none"/>',
106
+ };
107
+
108
+ const SVG_CACHE = new Map<IconFamily, string>();
109
+
110
+ /** Inline SVG markup for a family (safe static strings — v-html friendly). */
111
+ export function iconSvg(family: IconFamily): string {
112
+ let svg = SVG_CACHE.get(family);
113
+ if (!svg) {
114
+ svg =
115
+ `<svg class="fe-ficon fe-ficon--${family}" viewBox="0 0 24 24" fill="none" ` +
116
+ 'stroke="currentColor" stroke-width="1.6" stroke-linecap="round" ' +
117
+ 'stroke-linejoin="round" aria-hidden="true" focusable="false">' +
118
+ `${GLYPHS[family]}</svg>`;
119
+ SVG_CACHE.set(family, svg);
120
+ }
121
+ return svg;
122
+ }
123
+
124
+ /** Convenience: node → SVG markup in one call. */
125
+ export function fileIconSvg(node: { type?: string; extension?: string | null }): string {
126
+ return iconSvg(iconFamilyFor(node));
127
+ }
@@ -0,0 +1,43 @@
1
+ /* bul:s3 */
2
+ /**
3
+ * Snippet segment parser — the search backend returns content snippets as
4
+ * PLAIN TEXT with matched words wrapped in «guillemets» (contract: no HTML
5
+ * ever crosses the wire). The UI renders highlights by splitting the string
6
+ * into segments and emitting each one as a TEXT node (a <mark> wrapper for
7
+ * matches) — never via innerHTML, so a file whose content contains markup
8
+ * can't inject anything into the page.
9
+ */
10
+
11
+ export interface SnippetSegment {
12
+ /** Literal text of this segment. Always rendered as a text node. */
13
+ text: string;
14
+ /** True when the segment was «wrapped» — render inside <mark>. */
15
+ match: boolean;
16
+ }
17
+
18
+ /**
19
+ * Split a `«»`-annotated snippet into render segments. An unpaired `«`
20
+ * degrades gracefully: the rest of the string is treated as plain text.
21
+ */
22
+ export function snippetSegments(snippet: string): SnippetSegment[] {
23
+ const out: SnippetSegment[] = [];
24
+ if (!snippet) return out;
25
+ const re = /«([^«»]*)»/g;
26
+ let last = 0;
27
+ let m: RegExpExecArray | null;
28
+ while ((m = re.exec(snippet)) !== null) {
29
+ if (m.index > last) out.push({ text: snippet.slice(last, m.index), match: false });
30
+ if (m[1]) out.push({ text: m[1], match: true });
31
+ last = m.index + m[0].length;
32
+ }
33
+ if (last < snippet.length) out.push({ text: snippet.slice(last), match: false });
34
+ return out;
35
+ }
36
+
37
+ /** Matched-in values the search contract allows on a hit. */
38
+ export type SearchMatched = 'name' | 'content' | 'both';
39
+
40
+ /** True when the hit matched (at least partly) inside file CONTENT. */
41
+ export function matchedInContent(matched: unknown): boolean {
42
+ return matched === 'content' || matched === 'both';
43
+ }
package/src/locales/en.ts CHANGED
@@ -133,4 +133,66 @@ export const en: Record<string, string> = {
133
133
  'viewer.archive.error': 'Could not read archive contents.',
134
134
  'viewer.name': 'Name',
135
135
  'viewer.size': 'Size',
136
+
137
+ // ——— cila:a — sorting / date groups / density ———
138
+ 'col.sort': 'Sort',
139
+ 'group.today': 'Today',
140
+ 'group.yesterday': 'Yesterday',
141
+ 'group.this_week': 'This Week',
142
+ 'group.this_month': 'This Month',
143
+ 'group.no_date': 'No date',
144
+ 'toolbar.density.compact': 'Compact view',
145
+ 'toolbar.density.comfortable': 'Comfortable view',
146
+
147
+ // ——— cila:c — command palette + shortcuts help + breadcrumb ———
148
+ 'palette.placeholder': 'Search files, folders or commands…',
149
+ 'palette.files': 'Files',
150
+ 'palette.commands': 'Commands',
151
+ 'palette.goto': 'Go to path',
152
+ 'palette.empty': 'No results',
153
+ 'palette.hint': '↑↓ navigate · Enter select · Esc close',
154
+ 'cmd.view_toggle': 'Toggle view (list ⇄ grid)',
155
+ 'cmd.trash': 'Open trash',
156
+ 'shortcuts.title': 'Keyboard Shortcuts',
157
+ 'shortcuts.group.nav': 'Navigation',
158
+ 'shortcuts.group.selection': 'Selection',
159
+ 'shortcuts.group.file': 'File operations',
160
+ 'shortcuts.palette': 'Command palette',
161
+ 'shortcuts.search': 'Focus search',
162
+ 'shortcuts.go_up': 'Go up one level',
163
+ 'shortcuts.open': 'Open selected item',
164
+ 'shortcuts.close': 'Close / cancel',
165
+ 'shortcuts.help': 'This help dialog',
166
+ 'shortcuts.select_all': 'Select all',
167
+ 'shortcuts.rename': 'Rename',
168
+ 'shortcuts.delete': 'Delete',
169
+ 'shortcuts.cut': 'Cut',
170
+ 'shortcuts.copy': 'Copy',
171
+ 'shortcuts.paste': 'Paste',
172
+ 'breadcrumb.more': 'Hidden folders',
173
+
174
+ // ——— State screens + undo snackbar + connection badge (cila:b) ———
175
+ 'empty.hint': 'Drag and drop files here',
176
+ 'empty.upload': 'Upload files',
177
+ 'empty.search.title': 'No files match your search',
178
+ 'empty.search.hint': 'Try a different search term',
179
+ 'empty.trash.title': 'Trash is empty',
180
+ 'error.title': 'Could not load this folder',
181
+ 'error.retry': 'Try again',
182
+ 'toast.undo': 'Undo',
183
+ 'toast.undone': 'Undone',
184
+ 'toast.undo_failed': 'Could not undo',
185
+ 'toast.renamed': 'Renamed',
186
+ 'toast.moved': 'Moved',
187
+ 'toast.trashed': 'Moved to trash',
188
+ 'conn.offline': 'No live connection — changes may be delayed',
189
+ 'conn.tooltip': 'Live connection to the server is unavailable; the list refreshes periodically. This notice disappears once the connection is back.',
190
+
191
+ /* === bul:s3 === */
192
+ 'palette.everywhere': 'Everywhere',
193
+ 'palette.searching': 'Searching…',
194
+ 'palette.saved': 'Saved searches',
195
+ 'palette.save': 'Save search',
196
+ 'palette.saved.delete': 'Delete saved search',
197
+ 'search.in_content': 'In content',
136
198
  };
package/src/locales/tr.ts CHANGED
@@ -133,4 +133,66 @@ export const tr: Record<string, string> = {
133
133
  'viewer.archive.error': 'Arşiv okunamadı.',
134
134
  'viewer.name': 'Ad',
135
135
  'viewer.size': 'Boyut',
136
+
137
+ // ——— cila:a — sıralama / tarih grupları / yoğunluk ———
138
+ 'col.sort': 'Sırala',
139
+ 'group.today': 'Bugün',
140
+ 'group.yesterday': 'Dün',
141
+ 'group.this_week': 'Bu Hafta',
142
+ 'group.this_month': 'Bu Ay',
143
+ 'group.no_date': 'Tarihsiz',
144
+ 'toolbar.density.compact': 'Sıkışık görünüm',
145
+ 'toolbar.density.comfortable': 'Rahat görünüm',
146
+
147
+ // ——— cila:c — komut paleti + kısayol yardımı + breadcrumb ———
148
+ 'palette.placeholder': 'Dosya, klasör veya komut ara…',
149
+ 'palette.files': 'Dosyalar',
150
+ 'palette.commands': 'Komutlar',
151
+ 'palette.goto': 'Yola git',
152
+ 'palette.empty': 'Sonuç bulunamadı',
153
+ 'palette.hint': '↑↓ gezin · Enter seç · Esc kapat',
154
+ 'cmd.view_toggle': 'Görünümü değiştir (liste ⇄ ızgara)',
155
+ 'cmd.trash': 'Çöp kutusunu aç',
156
+ 'shortcuts.title': 'Klavye Kısayolları',
157
+ 'shortcuts.group.nav': 'Gezinme',
158
+ 'shortcuts.group.selection': 'Seçim',
159
+ 'shortcuts.group.file': 'Dosya işlemleri',
160
+ 'shortcuts.palette': 'Komut paleti',
161
+ 'shortcuts.search': 'Aramaya odaklan',
162
+ 'shortcuts.go_up': 'Üst klasöre çık',
163
+ 'shortcuts.open': 'Seçili öğeyi aç',
164
+ 'shortcuts.close': 'Kapat / vazgeç',
165
+ 'shortcuts.help': 'Bu yardım penceresi',
166
+ 'shortcuts.select_all': 'Tümünü seç',
167
+ 'shortcuts.rename': 'Yeniden adlandır',
168
+ 'shortcuts.delete': 'Sil',
169
+ 'shortcuts.cut': 'Kes',
170
+ 'shortcuts.copy': 'Kopyala',
171
+ 'shortcuts.paste': 'Yapıştır',
172
+ 'breadcrumb.more': 'Aradaki klasörler',
173
+
174
+ // ——— State screens + undo snackbar + connection badge (cila:b) ———
175
+ 'empty.hint': 'Dosyaları buraya sürükleyip bırakın',
176
+ 'empty.upload': 'Dosya yükle',
177
+ 'empty.search.title': 'Aramanla eşleşen dosya yok',
178
+ 'empty.search.hint': 'Farklı bir arama terimi deneyin',
179
+ 'empty.trash.title': 'Çöp kutusu boş',
180
+ 'error.title': 'Liste yüklenemedi',
181
+ 'error.retry': 'Yeniden dene',
182
+ 'toast.undo': 'Geri Al',
183
+ 'toast.undone': 'Geri alındı',
184
+ 'toast.undo_failed': 'Geri alınamadı',
185
+ 'toast.renamed': 'Yeniden adlandırıldı',
186
+ 'toast.moved': 'Taşındı',
187
+ 'toast.trashed': 'Çöpe taşındı',
188
+ 'conn.offline': 'Canlı bağlantı yok — değişiklikler gecikebilir',
189
+ 'conn.tooltip': 'Sunucuyla canlı bağlantı kurulamadı; liste belirli aralıklarla otomatik yenilenir. Bağlantı geri gelince bu uyarı kaybolur.',
190
+
191
+ /* === bul:s3 === */
192
+ 'palette.everywhere': 'Her yerde',
193
+ 'palette.searching': 'Aranıyor…',
194
+ 'palette.saved': 'Kayıtlı aramalar',
195
+ 'palette.save': 'Aramayı kaydet',
196
+ 'palette.saved.delete': 'Kayıtlı aramayı sil',
197
+ 'search.in_content': 'İçerikte',
136
198
  };