@brftech/filex-core 0.2.0 → 0.4.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 (37) hide show
  1. package/dist/{ArchiveViewer-DCZ1FZYV.js → ArchiveViewer-Dgvl43gn.js} +2 -2
  2. package/dist/{ArchiveViewer-DCZ1FZYV.js.map → ArchiveViewer-Dgvl43gn.js.map} +1 -1
  3. package/dist/{CsvViewer-DMvnp82x.js → CsvViewer-gWO5pc8F.js} +2 -2
  4. package/dist/{CsvViewer-DMvnp82x.js.map → CsvViewer-gWO5pc8F.js.map} +1 -1
  5. package/dist/{DrawioViewer-B5Io0yTO.js → DrawioViewer-DUAlRIAb.js} +2 -2
  6. package/dist/{DrawioViewer-B5Io0yTO.js.map → DrawioViewer-DUAlRIAb.js.map} +1 -1
  7. package/dist/{EpubViewer-z6RZd3K8.js → EpubViewer-DIXzuQWK.js} +2 -2
  8. package/dist/{EpubViewer-z6RZd3K8.js.map → EpubViewer-DIXzuQWK.js.map} +1 -1
  9. package/dist/{IpynbViewer-DfL4EauH.js → IpynbViewer-CJ-6pU_Z.js} +2 -2
  10. package/dist/{IpynbViewer-DfL4EauH.js.map → IpynbViewer-CJ-6pU_Z.js.map} +1 -1
  11. package/dist/{MermaidViewer-QNl_o7V-.js → MermaidViewer-CyQQDyRb.js} +2 -2
  12. package/dist/{MermaidViewer-QNl_o7V-.js.map → MermaidViewer-CyQQDyRb.js.map} +1 -1
  13. package/dist/{PsdViewer-Btmpr0Fz.js → PsdViewer-ChfaXVgN.js} +2 -2
  14. package/dist/{PsdViewer-Btmpr0Fz.js.map → PsdViewer-ChfaXVgN.js.map} +1 -1
  15. package/dist/{TiffViewer-CMLpwds5.js → TiffViewer-CsfD-NG6.js} +2 -2
  16. package/dist/{TiffViewer-CMLpwds5.js.map → TiffViewer-CsfD-NG6.js.map} +1 -1
  17. package/dist/{Viewer3D-gf3cb2gv.js → Viewer3D-BQ2XYmkN.js} +2 -2
  18. package/dist/{Viewer3D-gf3cb2gv.js.map → Viewer3D-BQ2XYmkN.js.map} +1 -1
  19. package/dist/filex-core.js +1 -1
  20. package/dist/filex-core.umd.cjs +42 -42
  21. package/dist/filex-core.umd.cjs.map +1 -1
  22. package/dist/index-PnhGunSA.js +7972 -0
  23. package/dist/index-PnhGunSA.js.map +1 -0
  24. package/dist/index.d.ts +69 -2
  25. package/dist/style.css +1 -1
  26. package/package.json +1 -1
  27. package/src/FileExplorer.vue +146 -0
  28. package/src/components/ContextMenu.vue +101 -3
  29. package/src/components/InspectorPanel.vue +455 -0
  30. package/src/components/Toolbar.vue +250 -5
  31. package/src/composables/useFileApi.ts +59 -0
  32. package/src/composables/useKeyboardShortcuts.ts +11 -0
  33. package/src/locales/en.ts +45 -0
  34. package/src/locales/tr.ts +45 -0
  35. package/src/styles/base.css +458 -0
  36. package/dist/index-D49iw00E.js +0 -7119
  37. package/dist/index-D49iw00E.js.map +0 -1
@@ -11,10 +11,10 @@
11
11
  * Presentational only — all logic (rename, share, …) lives in
12
12
  * FileExplorer.vue, which listens for `action` emits.
13
13
  */
14
- import { computed, onMounted, ref, watch } from 'vue';
14
+ import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
15
15
  import type { ViewMode } from '../types/FileNode';
16
- import type { LocaleCode } from '../types/ExplorerConfig';
17
- import type { ContextAction } from './ContextMenu.vue';
16
+ import type { LocaleCode, ThemeMode } from '../types/ExplorerConfig';
17
+ import ContextMenu, { type ContextAction } from './ContextMenu.vue';
18
18
  import { useLocale } from '../composables/useLocale';
19
19
 
20
20
  export type SelectionMode = 'none' | 'single-file' | 'single-dir' | 'multi';
@@ -58,6 +58,21 @@ const props = defineProps<{
58
58
  */
59
59
  canWrite?: boolean;
60
60
  locale: LocaleCode;
61
+ /**
62
+ * bag:b4 — narrow/embed mini mode. When true the toolbar collapses to
63
+ * [↑?] [🔍] [⬆] [⋯]: secondary actions (New Folder, Refresh, density,
64
+ * view switcher, selection actions, Paste) move into the "⋯" overflow
65
+ * menu and search expands from an icon to a full-width input. When
66
+ * absent/false the classic wide layout renders unchanged.
67
+ */
68
+ narrow?: boolean;
69
+ /**
70
+ * bag:b4 — resolved theme, forwarded to the teleported overflow menu
71
+ * (which loses the `.fe` variable scope under <body>).
72
+ */
73
+ theme?: ThemeMode;
74
+ /* koru:k1 — inspector (details panel) toggle state, for the pressed style. */
75
+ inspectorOpen?: boolean;
61
76
  }>();
62
77
 
63
78
  const emit = defineEmits<{
@@ -70,6 +85,7 @@ const emit = defineEmits<{
70
85
  (e: 'action', key: string): void;
71
86
  (e: 'open-recents'): void;
72
87
  (e: 'update:density', v: Density): void;
88
+ (e: 'toggle-inspector'): void /* koru:k1 */;
73
89
  }>();
74
90
 
75
91
  // Density toggle — the toolbar owns the persisted preference; the parent
@@ -114,7 +130,12 @@ function onSearchInput(ev: Event) {
114
130
  debounce = setTimeout(() => emit('update:searchQuery', v), 200);
115
131
  }
116
132
 
117
- function focusSearch() {
133
+ async function focusSearch() {
134
+ /* bag:b4 — in narrow mode the input is collapsed behind the icon. */
135
+ if (props.narrow && !searchOpen.value) {
136
+ searchOpen.value = true;
137
+ await nextTick();
138
+ }
118
139
  searchEl.value?.focus();
119
140
  searchEl.value?.select();
120
141
  }
@@ -132,10 +153,125 @@ const toolbarItems = computed(() => props.actions.filter((a) => !a.divider && !a
132
153
  function fire(key: string) {
133
154
  emit('action', key);
134
155
  }
156
+
157
+ /* === bag:b4 — narrow-mode state: expandable search + "⋯" overflow menu === */
158
+
159
+ const searchOpen = ref(false);
160
+ // Leaving narrow mode discards the transient expanded-search state so the
161
+ // wide layout always comes back exactly as it was.
162
+ watch(
163
+ () => props.narrow,
164
+ (n) => {
165
+ if (!n) searchOpen.value = false;
166
+ },
167
+ );
168
+
169
+ async function openSearch() {
170
+ searchOpen.value = true;
171
+ await nextTick();
172
+ searchEl.value?.focus();
173
+ }
174
+ function closeSearch() {
175
+ // Keep the query (it still filters the listing); the icon shows an
176
+ // active state while a query is set.
177
+ searchOpen.value = false;
178
+ }
179
+
180
+ // Coarse-pointer detection — the overflow menu renders as a bottom sheet on
181
+ // touch devices, matching the file context menu.
182
+ const coarse = ref(false);
183
+ let coarseMq: MediaQueryList | undefined;
184
+ function syncCoarse(e?: MediaQueryListEvent | MediaQueryList) {
185
+ coarse.value = !!(e && 'matches' in e && e.matches);
186
+ }
187
+ onMounted(() => {
188
+ if (typeof window === 'undefined' || !window.matchMedia) return;
189
+ coarseMq = window.matchMedia('(pointer: coarse)');
190
+ syncCoarse(coarseMq);
191
+ coarseMq.addEventListener?.('change', syncCoarse);
192
+ });
193
+ onBeforeUnmount(() => {
194
+ coarseMq?.removeEventListener?.('change', syncCoarse);
195
+ });
196
+
197
+ const moreBtnEl = ref<HTMLElement | null>(null);
198
+ const moreRef = ref<InstanceType<typeof ContextMenu> | null>(null);
199
+
200
+ // Everything the wide toolbar renders as standalone buttons, folded into one
201
+ // action list: folder-level writes (New Folder / Paste), the shared
202
+ // selection actions, then the view utilities (Refresh / density / view mode).
203
+ const moreActions = computed<ContextAction[]>(() => {
204
+ const list: ContextAction[] = [];
205
+ const writable =
206
+ !props.trashActive && !props.atVirtualRoot && props.canWrite !== false;
207
+ if (mode.value === 'none' && writable) {
208
+ list.push({ key: 'new-folder', label: t('toolbar.new_folder'), icon: '📁' });
209
+ if (props.pasteEnabled) list.push({ key: 'paste', label: t('ctx.paste'), icon: '📋' });
210
+ }
211
+ list.push(...toolbarItems.value);
212
+ if (list.length) list.push({ divider: true, key: 'bag-sep', label: '' });
213
+ list.push({ key: 'refresh', label: t('toolbar.refresh'), icon: '↻' });
214
+ list.push({
215
+ key: 'density',
216
+ label:
217
+ density.value === 'compact'
218
+ ? t('toolbar.density.comfortable')
219
+ : t('toolbar.density.compact'),
220
+ icon: '⇅',
221
+ });
222
+ list.push(
223
+ props.viewMode === 'list'
224
+ ? { key: 'view-grid', label: t('toolbar.view.grid'), icon: '▦' }
225
+ : { key: 'view-list', label: t('toolbar.view.list'), icon: '☰' },
226
+ );
227
+ /* koru:k1 — inspector toggle also reachable from the narrow overflow menu */
228
+ list.push({ key: 'inspector', label: t('toolbar.inspector'), icon: 'ℹ' });
229
+ return list;
230
+ });
231
+
232
+ function openMore() {
233
+ const r = moreBtnEl.value?.getBoundingClientRect();
234
+ moreRef.value?.show({ clientX: r ? r.right : 0, clientY: r ? r.bottom + 4 : 0 }, []);
235
+ }
236
+
237
+ function onMoreSelect(a: ContextAction) {
238
+ switch (a.key) {
239
+ case 'new-folder':
240
+ emit('new-folder');
241
+ break;
242
+ case 'refresh':
243
+ emit('refresh');
244
+ break;
245
+ case 'density':
246
+ toggleDensity();
247
+ break;
248
+ case 'view-list':
249
+ emit('update:viewMode', 'list');
250
+ break;
251
+ case 'view-grid':
252
+ emit('update:viewMode', 'grid');
253
+ break;
254
+ case 'inspector' /* koru:k1 */:
255
+ emit('toggle-inspector');
256
+ break;
257
+ default:
258
+ fire(a.key);
259
+ }
260
+ }
261
+
262
+ /* === /bag:b4 === */
135
263
  </script>
136
264
 
137
265
  <template>
138
- <div class="fe-toolbar">
266
+ <div
267
+ class="fe-toolbar"
268
+ :class="{
269
+ 'fe-toolbar--narrow': narrow /* bag:b4 */,
270
+ 'fe-toolbar--searching': narrow && searchOpen /* bag:b4 */,
271
+ }"
272
+ >
273
+ <!-- bag:b4 — wide layout, untouched; renders exactly as before when not narrow -->
274
+ <template v-if="!narrow">
139
275
  <div class="fe-toolbar__primary">
140
276
  <button
141
277
  v-if="canGoUp"
@@ -256,6 +392,32 @@ function fire(key: string) {
256
392
  </svg>
257
393
  </button>
258
394
 
395
+ <!-- koru:k1 — inspector (details panel) toggle -->
396
+ <button
397
+ type="button"
398
+ class="fe-btn fe-btn--icon-only fe-toolbar__inspector"
399
+ :class="{ 'is-active': inspectorOpen }"
400
+ :aria-pressed="!!inspectorOpen"
401
+ :title="t('toolbar.inspector')"
402
+ :aria-label="t('toolbar.inspector')"
403
+ @click="emit('toggle-inspector')"
404
+ >
405
+ <svg
406
+ class="fe-ficon"
407
+ viewBox="0 0 24 24"
408
+ fill="none"
409
+ stroke="currentColor"
410
+ stroke-width="1.8"
411
+ stroke-linecap="round"
412
+ aria-hidden="true"
413
+ focusable="false"
414
+ >
415
+ <circle cx="12" cy="12" r="9" />
416
+ <path d="M12 11v5" />
417
+ <circle cx="12" cy="7.6" r="1" fill="currentColor" stroke="none" />
418
+ </svg>
419
+ </button>
420
+
259
421
  <div class="fe-toolbar__view" role="tablist" aria-label="View">
260
422
  <button
261
423
  type="button"
@@ -280,5 +442,88 @@ function fire(key: string) {
280
442
  <span class="fe-icon">▦</span>
281
443
  </button>
282
444
  </div>
445
+ </template>
446
+
447
+ <!-- bag:b4 — narrow layout: [↑?] ... [🔍] [⬆] [⋯], search expands full-width -->
448
+ <template v-else>
449
+ <template v-if="searchOpen">
450
+ <div class="fe-search fe-search--full">
451
+ <input
452
+ ref="searchEl"
453
+ type="search"
454
+ class="fe-search__input"
455
+ :placeholder="t('toolbar.search.placeholder')"
456
+ :value="localSearch"
457
+ :aria-label="t('toolbar.search')"
458
+ @input="onSearchInput"
459
+ @keydown.esc.stop.prevent="closeSearch"
460
+ />
461
+ </div>
462
+ <button
463
+ type="button"
464
+ class="fe-btn fe-btn--icon-only"
465
+ :title="t('toolbar.search.close')"
466
+ :aria-label="t('toolbar.search.close')"
467
+ @click="closeSearch"
468
+ >
469
+ <span class="fe-icon">✕</span>
470
+ </button>
471
+ </template>
472
+ <template v-else>
473
+ <button
474
+ v-if="canGoUp"
475
+ type="button"
476
+ class="fe-btn fe-btn--icon-only"
477
+ :title="t('toolbar.go_up')"
478
+ :aria-label="t('toolbar.go_up')"
479
+ @click="emit('go-up')"
480
+ >
481
+ <span class="fe-icon">↑</span>
482
+ </button>
483
+
484
+ <div class="fe-toolbar__spacer" />
485
+
486
+ <button
487
+ type="button"
488
+ class="fe-btn fe-btn--icon-only fe-toolbar__search-toggle"
489
+ :class="{ 'is-active': !!localSearch }"
490
+ :title="t('toolbar.search')"
491
+ :aria-label="t('toolbar.search')"
492
+ @click="openSearch"
493
+ >
494
+ <span class="fe-icon">🔍</span>
495
+ </button>
496
+ <button
497
+ v-if="!atVirtualRoot && canWrite !== false"
498
+ type="button"
499
+ class="fe-btn fe-btn--icon-only"
500
+ :title="t('toolbar.upload')"
501
+ :aria-label="t('toolbar.upload')"
502
+ @click="emit('upload')"
503
+ >
504
+ <span class="fe-icon">⬆</span>
505
+ </button>
506
+ <button
507
+ ref="moreBtnEl"
508
+ type="button"
509
+ class="fe-btn fe-btn--icon-only fe-toolbar__more"
510
+ :title="t('toolbar.more')"
511
+ :aria-label="t('toolbar.more')"
512
+ aria-haspopup="menu"
513
+ @click="openMore"
514
+ >
515
+ <span class="fe-icon">⋯</span>
516
+ </button>
517
+ </template>
518
+
519
+ <ContextMenu
520
+ ref="moreRef"
521
+ :locale="locale"
522
+ :theme="theme || 'auto'"
523
+ :sheet="coarse"
524
+ :actions="moreActions"
525
+ @select="onMoreSelect"
526
+ />
527
+ </template>
283
528
  </div>
284
529
  </template>
@@ -89,6 +89,19 @@ export interface UserSuggestion {
89
89
  display_name: string;
90
90
  role: string;
91
91
  }
92
+
93
+ /* === koru:k1 — version history (inspector panel) === */
94
+
95
+ /** Mirrors backend `model.NodeVersion` (GET /api/files/versions?node_id=…). */
96
+ export interface NodeVersion {
97
+ id: number;
98
+ node_id: number;
99
+ version_n: number;
100
+ storage_key?: string;
101
+ size: number;
102
+ etag?: string;
103
+ created_at: string;
104
+ }
92
105
  export interface UserSearchResponse {
93
106
  users: UserSuggestion[];
94
107
  }
@@ -686,6 +699,48 @@ export function useFileApi(config: ExplorerConfig) {
686
699
  });
687
700
  }
688
701
 
702
+ /* === koru:k1 — version history (inspector panel) ===
703
+ * Derived from the manager endpoint by swapping `/manager` for `/versions`
704
+ * (same trick permissionsUrl/globalSearch use) so embedded proxies that
705
+ * forward the whole /api/files/* subtree keep working.
706
+ * GET /api/files/versions?node_id=N → {versions, node_id}
707
+ * POST /api/files/versions/restore → {node_id, version_id, snapshot_current}
708
+ * POST /api/files/versions/snapshot → {node_id} (may not exist on older backends)
709
+ */
710
+ function versionsUrl(sub = ''): string {
711
+ const base = endpoints.manager.replace(/\/manager(\?.*)?$/, '/versions');
712
+ return base + sub;
713
+ }
714
+ async function listVersions(nodeId: number): Promise<NodeVersion[]> {
715
+ const data = await jsonFetch<{ versions?: NodeVersion[] | null }>(
716
+ versionsUrl() + '?node_id=' + encodeURIComponent(String(nodeId)),
717
+ );
718
+ return Array.isArray(data?.versions) ? data.versions : [];
719
+ }
720
+ async function restoreVersion(
721
+ nodeId: number,
722
+ versionId: number,
723
+ snapshotCurrent = true,
724
+ ): Promise<void> {
725
+ await jsonFetch(versionsUrl('/restore'), {
726
+ method: 'POST',
727
+ headers: { 'Content-Type': 'application/json' },
728
+ body: JSON.stringify({
729
+ node_id: nodeId,
730
+ version_id: versionId,
731
+ snapshot_current: snapshotCurrent,
732
+ }),
733
+ });
734
+ }
735
+ async function snapshotVersion(nodeId: number): Promise<void> {
736
+ await jsonFetch(versionsUrl('/snapshot'), {
737
+ method: 'POST',
738
+ headers: { 'Content-Type': 'application/json' },
739
+ body: JSON.stringify({ node_id: nodeId }),
740
+ });
741
+ }
742
+ /* === /koru:k1 === */
743
+
689
744
  // Mint a short-lived WebSocket auth ticket for the realtime layer. Derived
690
745
  // from the manager URL (so it flows through the same host proxy) and uses the
691
746
  // same auth/creds as every other call. Returns null on any failure (a backend
@@ -731,6 +786,10 @@ export function useFileApi(config: ExplorerConfig) {
731
786
  archiveList,
732
787
  archiveExtract,
733
788
  archiveAdd,
789
+ // Version history (koru:k1 inspector)
790
+ listVersions,
791
+ restoreVersion,
792
+ snapshotVersion,
734
793
  // Permissions (RBAC panel)
735
794
  listPermissions,
736
795
  resolveEmail,
@@ -22,6 +22,7 @@ export interface ShortcutHandlers {
22
22
  onPathJump?: () => void; // Cmd+K / Ctrl+K
23
23
  onGoUp?: () => void; // Alt+Up / Backspace (when nothing selected)
24
24
  onShowHelp?: () => void; // ? (Shift+/ on most layouts)
25
+ onToggleInspector?: () => void; // i (koru:k1 details panel)
25
26
  hasSelection?: () => boolean; // disambiguates Backspace
26
27
  }
27
28
 
@@ -45,6 +46,7 @@ export const SHORTCUTS: ShortcutDef[] = [
45
46
  { keys: ['Alt+↑', 'Backspace'], labelKey: 'shortcuts.go_up', groupKey: 'shortcuts.group.nav' },
46
47
  { keys: ['Enter'], labelKey: 'shortcuts.open', groupKey: 'shortcuts.group.nav' },
47
48
  { keys: ['Esc'], labelKey: 'shortcuts.close', groupKey: 'shortcuts.group.nav' },
49
+ { keys: ['I'], labelKey: 'shortcuts.inspector', groupKey: 'shortcuts.group.nav' } /* koru:k1 */,
48
50
  { keys: ['?'], labelKey: 'shortcuts.help', groupKey: 'shortcuts.group.nav' },
49
51
  // Selection
50
52
  { keys: ['Ctrl+A'], labelKey: 'shortcuts.select_all', groupKey: 'shortcuts.group.selection' },
@@ -160,6 +162,15 @@ export function useKeyboardShortcuts(rootEl: Ref<HTMLElement | null>, handlers:
160
162
  handlers.onDuplicate();
161
163
  }
162
164
  break;
165
+ /* koru:k1 — plain `i` toggles the inspector (details) panel. Ctrl+I /
166
+ Alt+I stay untouched so browser/OS combos keep working. */
167
+ case 'i':
168
+ case 'I':
169
+ if (!ctrl && !e.altKey && handlers.onToggleInspector) {
170
+ e.preventDefault();
171
+ handlers.onToggleInspector();
172
+ }
173
+ break;
163
174
  case 'k':
164
175
  case 'K':
165
176
  if (ctrl && handlers.onPathJump) {
package/src/locales/en.ts CHANGED
@@ -195,4 +195,49 @@ export const en: Record<string, string> = {
195
195
  'palette.save': 'Save search',
196
196
  'palette.saved.delete': 'Delete saved search',
197
197
  'search.in_content': 'In content',
198
+
199
+ /* === bag:b4 === */
200
+ 'toolbar.more': 'More actions',
201
+ 'toolbar.search.close': 'Close search',
202
+ 'sheet.menu': 'Actions',
203
+ 'sheet.close': 'Close',
204
+
205
+ /* === koru:k1 === */
206
+ 'toolbar.inspector': 'Details',
207
+ 'ctx.details': 'Details',
208
+ 'shortcuts.inspector': 'Toggle details panel',
209
+ 'inspector.title': 'Details',
210
+ 'inspector.close': 'Close',
211
+ 'inspector.section.general': 'General',
212
+ 'inspector.section.versions': 'Versions',
213
+ 'inspector.section.permissions': 'Permissions',
214
+ 'inspector.section.shares': 'Shares',
215
+ 'inspector.path': 'Path',
216
+ 'inspector.size': 'Size',
217
+ 'inspector.modified': 'Modified',
218
+ 'inspector.mime': 'MIME type',
219
+ 'inspector.etag': 'ETag',
220
+ 'inspector.copy': 'Copy',
221
+ 'inspector.copied': 'Copied',
222
+ 'inspector.error': 'Could not load',
223
+ 'inspector.items_summary': '{n} items, {size} total',
224
+ 'inspector.folder_items': '{n} items',
225
+ 'inspector.versions.empty': 'No version history',
226
+ 'inspector.versions.v': 'Version {n}',
227
+ 'inspector.versions.restore': 'Restore',
228
+ 'inspector.versions.restore_confirm': 'Restore this version?',
229
+ 'inspector.versions.snapshot_current': 'Snapshot the current state first',
230
+ 'inspector.versions.confirm': 'Confirm',
231
+ 'inspector.versions.cancel': 'Cancel',
232
+ 'inspector.versions.take_snapshot': 'Snapshot current state',
233
+ 'inspector.versions.restored': 'Version restored',
234
+ 'inspector.versions.snapshotted': 'Snapshot taken',
235
+ 'inspector.versions.unsupported': 'The server does not support this operation',
236
+ 'inspector.perm.none': 'No access',
237
+ 'inspector.perm.viewer': 'Viewer',
238
+ 'inspector.perm.editor': 'Editor',
239
+ 'inspector.perm.owner': 'Owner',
240
+ 'inspector.perm.manage': 'Manage permissions',
241
+ 'inspector.shares.empty': 'No share links',
242
+ 'inspector.shares.copy': 'Copy link',
198
243
  };
package/src/locales/tr.ts CHANGED
@@ -195,4 +195,49 @@ export const tr: Record<string, string> = {
195
195
  'palette.save': 'Aramayı kaydet',
196
196
  'palette.saved.delete': 'Kayıtlı aramayı sil',
197
197
  'search.in_content': 'İçerikte',
198
+
199
+ /* === bag:b4 === */
200
+ 'toolbar.more': 'Diğer işlemler',
201
+ 'toolbar.search.close': 'Aramayı kapat',
202
+ 'sheet.menu': 'İşlemler',
203
+ 'sheet.close': 'Kapat',
204
+
205
+ /* === koru:k1 === */
206
+ 'toolbar.inspector': 'Ayrıntılar',
207
+ 'ctx.details': 'Ayrıntılar',
208
+ 'shortcuts.inspector': 'Ayrıntılar panelini aç/kapat',
209
+ 'inspector.title': 'Ayrıntılar',
210
+ 'inspector.close': 'Kapat',
211
+ 'inspector.section.general': 'Genel',
212
+ 'inspector.section.versions': 'Sürümler',
213
+ 'inspector.section.permissions': 'İzinler',
214
+ 'inspector.section.shares': 'Paylaşımlar',
215
+ 'inspector.path': 'Yol',
216
+ 'inspector.size': 'Boyut',
217
+ 'inspector.modified': 'Değiştirilme',
218
+ 'inspector.mime': 'MIME türü',
219
+ 'inspector.etag': 'ETag',
220
+ 'inspector.copy': 'Kopyala',
221
+ 'inspector.copied': 'Kopyalandı',
222
+ 'inspector.error': 'Yüklenemedi',
223
+ 'inspector.items_summary': '{n} öğe, toplam {size}',
224
+ 'inspector.folder_items': '{n} öğe',
225
+ 'inspector.versions.empty': 'Sürüm geçmişi yok',
226
+ 'inspector.versions.v': 'Sürüm {n}',
227
+ 'inspector.versions.restore': 'Geri yükle',
228
+ 'inspector.versions.restore_confirm': 'Bu sürüm geri yüklensin mi?',
229
+ 'inspector.versions.snapshot_current': 'Önce şimdiki halin sürümünü al',
230
+ 'inspector.versions.confirm': 'Onayla',
231
+ 'inspector.versions.cancel': 'Vazgeç',
232
+ 'inspector.versions.take_snapshot': 'Şimdiki halin sürümünü al',
233
+ 'inspector.versions.restored': 'Sürüm geri yüklendi',
234
+ 'inspector.versions.snapshotted': 'Sürüm alındı',
235
+ 'inspector.versions.unsupported': 'Sunucu bu işlemi desteklemiyor',
236
+ 'inspector.perm.none': 'Erişim yok',
237
+ 'inspector.perm.viewer': 'Görüntüleyen',
238
+ 'inspector.perm.editor': 'Düzenleyen',
239
+ 'inspector.perm.owner': 'Sahip',
240
+ 'inspector.perm.manage': 'İzinleri yönet',
241
+ 'inspector.shares.empty': 'Paylaşım linki yok',
242
+ 'inspector.shares.copy': 'Linki kopyala',
198
243
  };