@brftech/filex-core 0.1.56 → 0.1.60
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.
- package/LICENSE +21 -21
- package/README.md +108 -108
- package/dist/{ArchiveViewer-CJdaw5x3.js → ArchiveViewer-CbUUEREI.js} +20 -20
- package/dist/ArchiveViewer-CbUUEREI.js.map +1 -0
- package/dist/{CsvViewer-QYx_tePQ.js → CsvViewer-72rDsQJX.js} +3 -3
- package/dist/CsvViewer-72rDsQJX.js.map +1 -0
- package/dist/{DrawioViewer-GWt0TyB7.js → DrawioViewer-DSyMwGvg.js} +7 -7
- package/dist/DrawioViewer-DSyMwGvg.js.map +1 -0
- package/dist/{EpubViewer-Je0ywklQ.js → EpubViewer-DvDa17d7.js} +3 -3
- package/dist/EpubViewer-DvDa17d7.js.map +1 -0
- package/dist/{IpynbViewer-TsB4gqP-.js → IpynbViewer-DibIkdsz.js} +3 -3
- package/dist/IpynbViewer-DibIkdsz.js.map +1 -0
- package/dist/{MermaidViewer-CXfeDS-7.js → MermaidViewer-BoFLtGvl.js} +3 -3
- package/dist/MermaidViewer-BoFLtGvl.js.map +1 -0
- package/dist/{PsdViewer-VtPpxKVe.js → PsdViewer-K2iZkwax.js} +3 -3
- package/dist/PsdViewer-K2iZkwax.js.map +1 -0
- package/dist/{TiffViewer-OINn9bbb.js → TiffViewer-JzDzbqpO.js} +3 -3
- package/dist/TiffViewer-JzDzbqpO.js.map +1 -0
- package/dist/{Viewer3D-xvvyiWHd.js → Viewer3D-WEMYCP8l.js} +7 -7
- package/dist/Viewer3D-WEMYCP8l.js.map +1 -0
- package/dist/filex-core.js +1 -1
- package/dist/filex-core.umd.cjs +4 -4
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/{index-CmSYzt6L.js → index-B5YT-m5Q.js} +209 -209
- package/dist/index-B5YT-m5Q.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/useViewerFetch-czqbd2Lj.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Breadcrumb.vue +282 -282
- package/src/components/ContextMenu.vue +152 -152
- package/src/components/GridView.vue +166 -166
- package/src/components/ListView.vue +212 -212
- package/src/components/PendingOpsTray.vue +91 -91
- package/src/components/RecentlyOpened.vue +155 -155
- package/src/components/StarButton.vue +101 -101
- package/src/components/TagPicker.vue +184 -184
- package/src/components/Toolbar.vue +220 -220
- package/src/components/UploadProgress.vue +78 -78
- package/src/composables/useFileApi.ts +692 -692
- package/src/composables/useKeyboardShortcuts.ts +144 -144
- package/src/composables/useLocale.ts +60 -60
- package/src/composables/useMonacoLoader.ts +98 -98
- package/src/composables/usePendingOps.ts +200 -200
- package/src/composables/useSelection.ts +81 -81
- package/src/composables/useUploadChunked.ts +250 -250
- package/src/composables/useViewerFetch.ts +58 -58
- package/src/index.ts +80 -80
- package/src/locales/en.ts +122 -122
- package/src/locales/index.ts +12 -12
- package/src/locales/tr.ts +122 -122
- package/src/modals/DeleteConfirmModal.vue +32 -32
- package/src/modals/Modal.vue +104 -104
- package/src/modals/NewFolderModal.vue +64 -64
- package/src/modals/PermissionsModal.vue +815 -815
- package/src/modals/PreviewModal.vue +1070 -1070
- package/src/modals/RenameModal.vue +58 -58
- package/src/modals/ShareModal.vue +135 -135
- package/src/styles/base.css +1224 -1224
- package/src/styles/variables.css +90 -90
- package/src/types/FileNode.ts +136 -136
- package/src/types/index.ts +27 -27
- package/src/types/peers.d.ts +45 -45
- package/src/viewers/ArchiveViewer.vue +186 -186
- package/src/viewers/CsvViewer.vue +319 -319
- package/src/viewers/DrawioViewer.vue +243 -243
- package/src/viewers/EpubViewer.vue +307 -307
- package/src/viewers/IpynbViewer.vue +365 -365
- package/src/viewers/MermaidViewer.vue +259 -259
- package/src/viewers/PdfViewer.vue +562 -562
- package/src/viewers/PsdViewer.vue +264 -264
- package/src/viewers/TiffViewer.vue +273 -273
- package/src/viewers/Viewer3D.vue +120 -120
- package/dist/ArchiveViewer-CJdaw5x3.js.map +0 -1
- package/dist/CsvViewer-QYx_tePQ.js.map +0 -1
- package/dist/DrawioViewer-GWt0TyB7.js.map +0 -1
- package/dist/EpubViewer-Je0ywklQ.js.map +0 -1
- package/dist/IpynbViewer-TsB4gqP-.js.map +0 -1
- package/dist/MermaidViewer-CXfeDS-7.js.map +0 -1
- package/dist/PsdViewer-VtPpxKVe.js.map +0 -1
- package/dist/TiffViewer-OINn9bbb.js.map +0 -1
- package/dist/Viewer3D-xvvyiWHd.js.map +0 -1
- package/dist/index-CmSYzt6L.js.map +0 -1
|
@@ -1,184 +1,184 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* TagPicker — small chip list with add/remove for a node.
|
|
4
|
-
*
|
|
5
|
-
* Reads `GET /api/files/manager/tags?node_id=…` on mount, writes the
|
|
6
|
-
* full new array via `POST /api/files/manager/tags`.
|
|
7
|
-
*
|
|
8
|
-
* Tag colors are deterministic: hash(name) → palette index. Consumers
|
|
9
|
-
* can override with the `palette` prop.
|
|
10
|
-
*/
|
|
11
|
-
import { ref, watch, onMounted, computed } from 'vue';
|
|
12
|
-
|
|
13
|
-
const props = defineProps<{
|
|
14
|
-
nodeId: number;
|
|
15
|
-
apiBase?: string;
|
|
16
|
-
authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
17
|
-
/** Optional list of palette swatches; one is picked per tag deterministically. */
|
|
18
|
-
palette?: string[];
|
|
19
|
-
}>();
|
|
20
|
-
|
|
21
|
-
const emit = defineEmits<{
|
|
22
|
-
(e: 'change', tags: string[]): void;
|
|
23
|
-
(e: 'error', message: string): void;
|
|
24
|
-
}>();
|
|
25
|
-
|
|
26
|
-
const tags = ref<string[]>([]);
|
|
27
|
-
const loading = ref(false);
|
|
28
|
-
const adding = ref(false);
|
|
29
|
-
const newTag = ref('');
|
|
30
|
-
|
|
31
|
-
const palette = computed(() => props.palette ?? [
|
|
32
|
-
'#ef4444', '#f59e0b', '#10b981', '#3b82f6',
|
|
33
|
-
'#8b5cf6', '#ec4899', '#14b8a6', '#f97316',
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
function colorFor(tag: string): string {
|
|
37
|
-
let h = 0;
|
|
38
|
-
for (let i = 0; i < tag.length; i++) h = (h * 31 + tag.charCodeAt(i)) | 0;
|
|
39
|
-
const arr = palette.value;
|
|
40
|
-
return arr[Math.abs(h) % arr.length];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function buildHeaders(extra: Record<string, string> = {}): Promise<Record<string, string>> {
|
|
44
|
-
return { ...(await (props.authHeaders ?? (() => ({})))()), ...extra };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function load() {
|
|
48
|
-
loading.value = true;
|
|
49
|
-
try {
|
|
50
|
-
const base = props.apiBase ?? '';
|
|
51
|
-
const res = await fetch(`${base}/api/files/manager/tags?node_id=${props.nodeId}`, {
|
|
52
|
-
headers: await buildHeaders(),
|
|
53
|
-
credentials: 'include',
|
|
54
|
-
});
|
|
55
|
-
if (res.ok) {
|
|
56
|
-
const body = await res.json();
|
|
57
|
-
tags.value = Array.isArray(body.tags) ? body.tags : [];
|
|
58
|
-
}
|
|
59
|
-
} catch (err) {
|
|
60
|
-
emit('error', err instanceof Error ? err.message : String(err));
|
|
61
|
-
} finally {
|
|
62
|
-
loading.value = false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function persist(next: string[]) {
|
|
67
|
-
const previous = [...tags.value];
|
|
68
|
-
tags.value = next;
|
|
69
|
-
try {
|
|
70
|
-
const base = props.apiBase ?? '';
|
|
71
|
-
const res = await fetch(`${base}/api/files/manager/tags`, {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
74
|
-
credentials: 'include',
|
|
75
|
-
body: JSON.stringify({ node_id: props.nodeId, tags: next }),
|
|
76
|
-
});
|
|
77
|
-
if (!res.ok) throw new Error(`tag save failed: ${res.status}`);
|
|
78
|
-
emit('change', next);
|
|
79
|
-
} catch (err) {
|
|
80
|
-
tags.value = previous;
|
|
81
|
-
emit('error', err instanceof Error ? err.message : String(err));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function add() {
|
|
86
|
-
const v = newTag.value.trim();
|
|
87
|
-
if (!v || tags.value.includes(v)) {
|
|
88
|
-
newTag.value = '';
|
|
89
|
-
adding.value = false;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
persist([...tags.value, v]);
|
|
93
|
-
newTag.value = '';
|
|
94
|
-
adding.value = false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function remove(tag: string) {
|
|
98
|
-
persist(tags.value.filter((t) => t !== tag));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
onMounted(load);
|
|
102
|
-
watch(() => props.nodeId, load);
|
|
103
|
-
</script>
|
|
104
|
-
|
|
105
|
-
<template>
|
|
106
|
-
<div class="filex-tag-picker" :class="{ 'is-loading': loading }">
|
|
107
|
-
<span
|
|
108
|
-
v-for="tag in tags"
|
|
109
|
-
:key="tag"
|
|
110
|
-
class="filex-tag"
|
|
111
|
-
:style="{ '--filex-tag-color': colorFor(tag) }"
|
|
112
|
-
>
|
|
113
|
-
{{ tag }}
|
|
114
|
-
<button class="filex-tag-x" type="button" @click="remove(tag)" aria-label="Remove tag">×</button>
|
|
115
|
-
</span>
|
|
116
|
-
|
|
117
|
-
<form v-if="adding" class="filex-tag-add" @submit.prevent="add">
|
|
118
|
-
<input
|
|
119
|
-
v-model="newTag"
|
|
120
|
-
autofocus
|
|
121
|
-
placeholder="tag name"
|
|
122
|
-
@blur="add"
|
|
123
|
-
@keydown.escape="adding = false; newTag = ''"
|
|
124
|
-
/>
|
|
125
|
-
</form>
|
|
126
|
-
<button v-else class="filex-tag-add-btn" type="button" @click="adding = true">+ Add tag</button>
|
|
127
|
-
</div>
|
|
128
|
-
</template>
|
|
129
|
-
|
|
130
|
-
<style scoped>
|
|
131
|
-
.filex-tag-picker {
|
|
132
|
-
display: flex;
|
|
133
|
-
flex-wrap: wrap;
|
|
134
|
-
gap: 6px;
|
|
135
|
-
align-items: center;
|
|
136
|
-
}
|
|
137
|
-
.filex-tag-picker.is-loading {
|
|
138
|
-
opacity: 0.6;
|
|
139
|
-
pointer-events: none;
|
|
140
|
-
}
|
|
141
|
-
.filex-tag {
|
|
142
|
-
display: inline-flex;
|
|
143
|
-
align-items: center;
|
|
144
|
-
gap: 4px;
|
|
145
|
-
border-radius: 999px;
|
|
146
|
-
padding: 2px 10px;
|
|
147
|
-
font-size: 12px;
|
|
148
|
-
font-weight: 500;
|
|
149
|
-
color: #fff;
|
|
150
|
-
background: var(--filex-tag-color);
|
|
151
|
-
}
|
|
152
|
-
.filex-tag-x {
|
|
153
|
-
background: transparent;
|
|
154
|
-
border: none;
|
|
155
|
-
color: rgba(255, 255, 255, 0.85);
|
|
156
|
-
cursor: pointer;
|
|
157
|
-
padding: 0 2px;
|
|
158
|
-
font-size: 14px;
|
|
159
|
-
line-height: 1;
|
|
160
|
-
}
|
|
161
|
-
.filex-tag-x:hover {
|
|
162
|
-
color: #fff;
|
|
163
|
-
}
|
|
164
|
-
.filex-tag-add input {
|
|
165
|
-
border: 1px solid var(--filex-border, #d1d5db);
|
|
166
|
-
border-radius: 12px;
|
|
167
|
-
padding: 2px 10px;
|
|
168
|
-
font-size: 12px;
|
|
169
|
-
width: 110px;
|
|
170
|
-
}
|
|
171
|
-
.filex-tag-add-btn {
|
|
172
|
-
border: 1px dashed var(--filex-border, #d1d5db);
|
|
173
|
-
background: transparent;
|
|
174
|
-
border-radius: 999px;
|
|
175
|
-
padding: 2px 10px;
|
|
176
|
-
font-size: 12px;
|
|
177
|
-
color: var(--filex-text-muted, #6b7280);
|
|
178
|
-
cursor: pointer;
|
|
179
|
-
}
|
|
180
|
-
.filex-tag-add-btn:hover {
|
|
181
|
-
border-color: var(--filex-accent, #6366f1);
|
|
182
|
-
color: var(--filex-accent, #6366f1);
|
|
183
|
-
}
|
|
184
|
-
</style>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* TagPicker — small chip list with add/remove for a node.
|
|
4
|
+
*
|
|
5
|
+
* Reads `GET /api/files/manager/tags?node_id=…` on mount, writes the
|
|
6
|
+
* full new array via `POST /api/files/manager/tags`.
|
|
7
|
+
*
|
|
8
|
+
* Tag colors are deterministic: hash(name) → palette index. Consumers
|
|
9
|
+
* can override with the `palette` prop.
|
|
10
|
+
*/
|
|
11
|
+
import { ref, watch, onMounted, computed } from 'vue';
|
|
12
|
+
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
nodeId: number;
|
|
15
|
+
apiBase?: string;
|
|
16
|
+
authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
17
|
+
/** Optional list of palette swatches; one is picked per tag deterministically. */
|
|
18
|
+
palette?: string[];
|
|
19
|
+
}>();
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits<{
|
|
22
|
+
(e: 'change', tags: string[]): void;
|
|
23
|
+
(e: 'error', message: string): void;
|
|
24
|
+
}>();
|
|
25
|
+
|
|
26
|
+
const tags = ref<string[]>([]);
|
|
27
|
+
const loading = ref(false);
|
|
28
|
+
const adding = ref(false);
|
|
29
|
+
const newTag = ref('');
|
|
30
|
+
|
|
31
|
+
const palette = computed(() => props.palette ?? [
|
|
32
|
+
'#ef4444', '#f59e0b', '#10b981', '#3b82f6',
|
|
33
|
+
'#8b5cf6', '#ec4899', '#14b8a6', '#f97316',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function colorFor(tag: string): string {
|
|
37
|
+
let h = 0;
|
|
38
|
+
for (let i = 0; i < tag.length; i++) h = (h * 31 + tag.charCodeAt(i)) | 0;
|
|
39
|
+
const arr = palette.value;
|
|
40
|
+
return arr[Math.abs(h) % arr.length];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function buildHeaders(extra: Record<string, string> = {}): Promise<Record<string, string>> {
|
|
44
|
+
return { ...(await (props.authHeaders ?? (() => ({})))()), ...extra };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function load() {
|
|
48
|
+
loading.value = true;
|
|
49
|
+
try {
|
|
50
|
+
const base = props.apiBase ?? '';
|
|
51
|
+
const res = await fetch(`${base}/api/files/manager/tags?node_id=${props.nodeId}`, {
|
|
52
|
+
headers: await buildHeaders(),
|
|
53
|
+
credentials: 'include',
|
|
54
|
+
});
|
|
55
|
+
if (res.ok) {
|
|
56
|
+
const body = await res.json();
|
|
57
|
+
tags.value = Array.isArray(body.tags) ? body.tags : [];
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
emit('error', err instanceof Error ? err.message : String(err));
|
|
61
|
+
} finally {
|
|
62
|
+
loading.value = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function persist(next: string[]) {
|
|
67
|
+
const previous = [...tags.value];
|
|
68
|
+
tags.value = next;
|
|
69
|
+
try {
|
|
70
|
+
const base = props.apiBase ?? '';
|
|
71
|
+
const res = await fetch(`${base}/api/files/manager/tags`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: await buildHeaders({ 'Content-Type': 'application/json' }),
|
|
74
|
+
credentials: 'include',
|
|
75
|
+
body: JSON.stringify({ node_id: props.nodeId, tags: next }),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) throw new Error(`tag save failed: ${res.status}`);
|
|
78
|
+
emit('change', next);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
tags.value = previous;
|
|
81
|
+
emit('error', err instanceof Error ? err.message : String(err));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function add() {
|
|
86
|
+
const v = newTag.value.trim();
|
|
87
|
+
if (!v || tags.value.includes(v)) {
|
|
88
|
+
newTag.value = '';
|
|
89
|
+
adding.value = false;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
persist([...tags.value, v]);
|
|
93
|
+
newTag.value = '';
|
|
94
|
+
adding.value = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function remove(tag: string) {
|
|
98
|
+
persist(tags.value.filter((t) => t !== tag));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
onMounted(load);
|
|
102
|
+
watch(() => props.nodeId, load);
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<template>
|
|
106
|
+
<div class="filex-tag-picker" :class="{ 'is-loading': loading }">
|
|
107
|
+
<span
|
|
108
|
+
v-for="tag in tags"
|
|
109
|
+
:key="tag"
|
|
110
|
+
class="filex-tag"
|
|
111
|
+
:style="{ '--filex-tag-color': colorFor(tag) }"
|
|
112
|
+
>
|
|
113
|
+
{{ tag }}
|
|
114
|
+
<button class="filex-tag-x" type="button" @click="remove(tag)" aria-label="Remove tag">×</button>
|
|
115
|
+
</span>
|
|
116
|
+
|
|
117
|
+
<form v-if="adding" class="filex-tag-add" @submit.prevent="add">
|
|
118
|
+
<input
|
|
119
|
+
v-model="newTag"
|
|
120
|
+
autofocus
|
|
121
|
+
placeholder="tag name"
|
|
122
|
+
@blur="add"
|
|
123
|
+
@keydown.escape="adding = false; newTag = ''"
|
|
124
|
+
/>
|
|
125
|
+
</form>
|
|
126
|
+
<button v-else class="filex-tag-add-btn" type="button" @click="adding = true">+ Add tag</button>
|
|
127
|
+
</div>
|
|
128
|
+
</template>
|
|
129
|
+
|
|
130
|
+
<style scoped>
|
|
131
|
+
.filex-tag-picker {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-wrap: wrap;
|
|
134
|
+
gap: 6px;
|
|
135
|
+
align-items: center;
|
|
136
|
+
}
|
|
137
|
+
.filex-tag-picker.is-loading {
|
|
138
|
+
opacity: 0.6;
|
|
139
|
+
pointer-events: none;
|
|
140
|
+
}
|
|
141
|
+
.filex-tag {
|
|
142
|
+
display: inline-flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 4px;
|
|
145
|
+
border-radius: 999px;
|
|
146
|
+
padding: 2px 10px;
|
|
147
|
+
font-size: 12px;
|
|
148
|
+
font-weight: 500;
|
|
149
|
+
color: #fff;
|
|
150
|
+
background: var(--filex-tag-color);
|
|
151
|
+
}
|
|
152
|
+
.filex-tag-x {
|
|
153
|
+
background: transparent;
|
|
154
|
+
border: none;
|
|
155
|
+
color: rgba(255, 255, 255, 0.85);
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
padding: 0 2px;
|
|
158
|
+
font-size: 14px;
|
|
159
|
+
line-height: 1;
|
|
160
|
+
}
|
|
161
|
+
.filex-tag-x:hover {
|
|
162
|
+
color: #fff;
|
|
163
|
+
}
|
|
164
|
+
.filex-tag-add input {
|
|
165
|
+
border: 1px solid var(--filex-border, #d1d5db);
|
|
166
|
+
border-radius: 12px;
|
|
167
|
+
padding: 2px 10px;
|
|
168
|
+
font-size: 12px;
|
|
169
|
+
width: 110px;
|
|
170
|
+
}
|
|
171
|
+
.filex-tag-add-btn {
|
|
172
|
+
border: 1px dashed var(--filex-border, #d1d5db);
|
|
173
|
+
background: transparent;
|
|
174
|
+
border-radius: 999px;
|
|
175
|
+
padding: 2px 10px;
|
|
176
|
+
font-size: 12px;
|
|
177
|
+
color: var(--filex-text-muted, #6b7280);
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
}
|
|
180
|
+
.filex-tag-add-btn:hover {
|
|
181
|
+
border-color: var(--filex-accent, #6366f1);
|
|
182
|
+
color: var(--filex-accent, #6366f1);
|
|
183
|
+
}
|
|
184
|
+
</style>
|