mirror of
https://github.com/xlmnxp/extractify.zip.git
synced 2024-11-23 17:13:12 +03:00
support text editor (now it possible to view text files)
This commit is contained in:
parent
8b19f257ed
commit
dc764433ae
14
app.vue
14
app.vue
@ -17,8 +17,6 @@ let filesGridList = ref<any>([])
|
|||||||
let selectedList = ref<any>([]);
|
let selectedList = ref<any>([]);
|
||||||
let filesManager = new FilesManager(filesList);
|
let filesManager = new FilesManager(filesList);
|
||||||
let history = new HistoryManager(filesManager);
|
let history = new HistoryManager(filesManager);
|
||||||
|
|
||||||
const videoExtensions = ['mp4', 'avi', 'mov', 'mkv'];
|
|
||||||
let mediaBlobUrl = ref('');
|
let mediaBlobUrl = ref('');
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
@ -27,7 +25,6 @@ watchEffect(async () => {
|
|||||||
filesList.value = [];
|
filesList.value = [];
|
||||||
|
|
||||||
await filesManager.loadArchive(files.value?.[0]);
|
await filesManager.loadArchive(files.value?.[0]);
|
||||||
|
|
||||||
loadingModel.value = false;
|
loadingModel.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -68,7 +65,6 @@ watchEffect(async () => {
|
|||||||
// Experimental feature
|
// Experimental feature
|
||||||
if (videoExtensions.includes(filesManager.getFile(selectedPath.value)?.extension?.toLowerCase())) {
|
if (videoExtensions.includes(filesManager.getFile(selectedPath.value)?.extension?.toLowerCase())) {
|
||||||
mediaBlobUrl.value = await filesManager.getFileBlobUrl(selectedPath.value) as string;
|
mediaBlobUrl.value = await filesManager.getFileBlobUrl(selectedPath.value) as string;
|
||||||
console.log(mediaBlobUrl.value)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -162,15 +158,13 @@ function stepUp(path: string) {
|
|||||||
</v-row>
|
</v-row>
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<v-container>
|
<v-container>
|
||||||
<template v-if="filesManager.getFile(selectedPath)?.isFolder || false">
|
<template v-if="filesManager.getFile(selectedPath)?.isFolder">
|
||||||
<v-list :selected="[selectedPath]">
|
<v-list :selected="[selectedPath]">
|
||||||
<v-row no-gutters>
|
<v-row no-gutters>
|
||||||
<v-col cols="6" lg="2" md="3" sm="6" v-for="file of filesGridList" style="text-align: center;">
|
<v-col cols="6" lg="2" md="3" sm="6" v-for="file of filesGridList" style="text-align: center;">
|
||||||
<v-list-item class="ma-2 pa-5 selectable" active-color="light-blue-darken-4" :value="file.path" rounded
|
<v-list-item class="ma-2 pa-5 selectable" active-color="light-blue-darken-4" :value="file.path" rounded
|
||||||
@click="selectedPath = file.path">
|
@click="selectedPath = file.path">
|
||||||
<v-avatar class="mb-2" :color="file.isFolder ? 'light-blue-accent-4' : 'blue-grey-darken-1'">
|
<file-logo class="mb-2" :file="file" :key="file.path" />
|
||||||
<v-icon color="white">{{ file.isFolder ? 'mdi-folder' : 'mdi-file' }}</v-icon>
|
|
||||||
</v-avatar>
|
|
||||||
<p>{{ file.name }}</p>
|
<p>{{ file.name }}</p>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-col>
|
</v-col>
|
||||||
@ -181,6 +175,10 @@ function stepUp(path: string) {
|
|||||||
v-if="!filesManager.getFile(selectedPath)?.isFolder && videoExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
|
v-if="!filesManager.getFile(selectedPath)?.isFolder && videoExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
|
||||||
<MediaVideoPlayer :src="mediaBlobUrl"></MediaVideoPlayer>
|
<MediaVideoPlayer :src="mediaBlobUrl"></MediaVideoPlayer>
|
||||||
</template>
|
</template>
|
||||||
|
<template
|
||||||
|
v-if="!filesManager.getFile(selectedPath)?.isFolder && files.length && !videoExtensions.includes(filesManager.getFile(selectedPath)?.extension)">
|
||||||
|
<TextEditor :file="filesManager.getFile(selectedPath)" :filesManager="filesManager"></TextEditor>
|
||||||
|
</template>
|
||||||
<template v-if="!files.length">
|
<template v-if="!files.length">
|
||||||
<!-- tutorial drag and drop zipped file here and review it securely -->
|
<!-- tutorial drag and drop zipped file here and review it securely -->
|
||||||
<v-row align="center" justify="center">
|
<v-row align="center" justify="center">
|
||||||
|
27
components/file-logo.vue
Normal file
27
components/file-logo.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { iFile } from '~/composables/worker/7zip-manager';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
file: iFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
let { file } = defineProps<Props>()
|
||||||
|
|
||||||
|
let icon = computed(() => {
|
||||||
|
if (file.isFolder) return 'mdi-folder';
|
||||||
|
|
||||||
|
if (file.extension) {
|
||||||
|
if (videoExtensions.includes(file.extension!)) return 'mdi-video';
|
||||||
|
if (audioExtensions.includes(file.extension!)) return 'mdi-music';
|
||||||
|
if (imageExtensions.includes(file.extension!)) return 'mdi-image';
|
||||||
|
if (textExtensions.includes(file.extension!)) return 'mdi-file-document';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'mdi-file';
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<v-avatar :color="file.isFolder ? 'light-blue-accent-4' : 'blue-grey-darken-1'">
|
||||||
|
<v-icon color="white">{{ icon }}</v-icon>
|
||||||
|
</v-avatar>
|
||||||
|
</template>
|
40
components/text-editor.vue
Normal file
40
components/text-editor.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { FilesManager } from '~/composables/files-manager';
|
||||||
|
// @ts-ignore
|
||||||
|
import { iFile } from '~/composables/worker/7zip-manager';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
file: iFile,
|
||||||
|
filesManager: FilesManager
|
||||||
|
}
|
||||||
|
|
||||||
|
const editor = ref()
|
||||||
|
|
||||||
|
let { file, filesManager } = defineProps<Props>()
|
||||||
|
|
||||||
|
// files manager
|
||||||
|
onMounted(async () => {
|
||||||
|
const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
|
||||||
|
let fileContent = await filesManager.getFileContent(file.path);
|
||||||
|
monaco.editor.create(editor.value, {
|
||||||
|
value: fileContent?.toString()!,
|
||||||
|
language: file.extension,
|
||||||
|
readOnly: true,
|
||||||
|
theme: darkMode ? 'vs-dark' : 'vs-light'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="editor" ref="editor"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#editor {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,15 +14,13 @@ let selectedPath = useSelectedPath();
|
|||||||
<template>
|
<template>
|
||||||
<v-list :selected="[selectedPath]" density="compact" :nav="nav">
|
<v-list :selected="[selectedPath]" density="compact" :nav="nav">
|
||||||
<template v-for="file in filesList" :key="file.path">
|
<template v-for="file in filesList" :key="file.path">
|
||||||
<v-list-item active-color="light-blue-darken-4" :active="selectedPath == file.path" :title="file.name" :subtitle="file.path"
|
<v-list-item active-color="light-blue-darken-4" :active="selectedPath == file.path" :title="file.name"
|
||||||
:value="file.path" @click="() => {
|
:subtitle="file.path" :value="file.path" @click="() => {
|
||||||
selectedPath = file.path;
|
selectedPath = file.path;
|
||||||
file.toggle = !file.toggle;
|
file.toggle = !file.toggle;
|
||||||
}">
|
}">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<v-avatar :color="file.isFolder ? 'light-blue-accent-4' : 'blue-grey-darken-1'">
|
<file-logo :file="file" :key="file.path" />
|
||||||
<v-icon color="white">{{ file.isFolder ? 'mdi-folder' : 'mdi-file' }}</v-icon>
|
|
||||||
</v-avatar>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-if="file.isFolder" v-slot:append>
|
<template v-if="file.isFolder" v-slot:append>
|
||||||
<v-icon>{{ `mdi-chevron-${file.toggle ? 'up' : 'down'}` }}</v-icon>
|
<v-icon>{{ `mdi-chevron-${file.toggle ? 'up' : 'down'}` }}</v-icon>
|
||||||
|
@ -6,6 +6,11 @@ import * as Comlink from "comlink";
|
|||||||
import SevenZipWorker from "./worker/7zip-manager?worker";
|
import SevenZipWorker from "./worker/7zip-manager?worker";
|
||||||
import { SevenZipManager, iFile } from "./worker/7zip-manager";
|
import { SevenZipManager, iFile } from "./worker/7zip-manager";
|
||||||
|
|
||||||
|
export const videoExtensions = ['mp4', 'avi', 'mov', 'mkv'];
|
||||||
|
export const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
export const audioExtensions = ['mp3', 'wav', 'ogg', 'flac'];
|
||||||
|
export const textExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'js', 'ts', 'php', 'c', 'cpp', 'py', 'html', 'css', 'scss', 'sass', 'less', 'json', 'xml', 'sql', 'java', 'go', 'rb', 'sh', 'bat', 'ps1', 'cmd', 'yml', 'yaml', 'ini', 'toml', 'csv', 'tsv', 'gitignore', 'lock', 'htaccess', 'htpasswd', 'env', 'dockerfile', 'gitattributes', 'gitmodules', 'editorconfig', 'babelrc', 'eslintrc', 'eslintignore', 'prettierrc', 'prettierignore', 'stylelintrc', 'stylelintignore', 'postcssrc', 'postcss.config', 'jsx', 'tsx', 'license']
|
||||||
|
|
||||||
export class FilesManager {
|
export class FilesManager {
|
||||||
consoleOutputBuffer: string[] = [];
|
consoleOutputBuffer: string[] = [];
|
||||||
path: Ref<string> = useSelectedPath();
|
path: Ref<string> = useSelectedPath();
|
||||||
@ -68,4 +73,9 @@ export class FilesManager {
|
|||||||
if (!this.remoteSevenZipManager) return;
|
if (!this.remoteSevenZipManager) return;
|
||||||
return await this.remoteSevenZipManager.generateBlobUrl(JSON.stringify(this.getFile(path)) as any);
|
return await this.remoteSevenZipManager.generateBlobUrl(JSON.stringify(this.getFile(path)) as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFileContent(path: string) {
|
||||||
|
if (!this.remoteSevenZipManager) return;
|
||||||
|
return await this.remoteSevenZipManager.getFileContent(JSON.stringify(this.getFile(path)) as any);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,2 +1,4 @@
|
|||||||
|
import { FilesManager } from "./files-manager";
|
||||||
|
|
||||||
let selectedPath = ref("/")
|
let selectedPath = ref("/")
|
||||||
export const useSelectedPath = () => useState("selected-path", () => selectedPath)
|
export const useSelectedPath = () => useState("selected-path", () => selectedPath)
|
@ -66,7 +66,7 @@ export class SevenZipManager {
|
|||||||
name: file.groups!.path.lastIndexOf('/') > -1 ? file.groups!.path.substring(file.groups!.path.lastIndexOf('/') + 1) : file.groups!.path,
|
name: file.groups!.path.lastIndexOf('/') > -1 ? file.groups!.path.substring(file.groups!.path.lastIndexOf('/') + 1) : file.groups!.path,
|
||||||
path: `/${file.groups!.path}`,
|
path: `/${file.groups!.path}`,
|
||||||
isFolder: isFolder ? true : false,
|
isFolder: isFolder ? true : false,
|
||||||
extension: isFolder ? "" : file.groups!.path.substring(file.groups!.path.lastIndexOf('.') + 1),
|
extension: isFolder ? "" : file.groups!.path.substring(file.groups!.path.lastIndexOf('.') + 1).toLowerCase(),
|
||||||
content: isFolder ? [] as any[] : undefined,
|
content: isFolder ? [] as any[] : undefined,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -161,6 +161,23 @@ export class SevenZipManager {
|
|||||||
|
|
||||||
return blobUrl;
|
return blobUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get content from buffer (Experimental)
|
||||||
|
async getFileContent(file: iFile) {
|
||||||
|
if (!this.sevenZip) return;
|
||||||
|
file = typeof file === "string" ? JSON.parse(file) : file;
|
||||||
|
|
||||||
|
// extract file from archive
|
||||||
|
this.execute(['x', '-y', this.archiveName, file.path.substring(1)]);
|
||||||
|
this.sevenZip.FS.chmod(file.path, 0o777);
|
||||||
|
|
||||||
|
// get file buffer
|
||||||
|
const buffer = this.sevenZip.FS.readFile(file.path, { encoding: "utf8" });
|
||||||
|
// remove the file after extract local blob url
|
||||||
|
this.sevenZip.FS.unlink(file.path);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Comlink.expose(SevenZipManager);
|
Comlink.expose(SevenZipManager);
|
16
package-lock.json
generated
16
package-lock.json
generated
@ -15,13 +15,14 @@
|
|||||||
"7z-wasm": "^1.0.2",
|
"7z-wasm": "^1.0.2",
|
||||||
"comlink": "^4.4.1",
|
"comlink": "^4.4.1",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
|
"monaco-editor": "^0.45.0",
|
||||||
"moveable": "^0.52.0",
|
"moveable": "^0.52.0",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.62.1",
|
||||||
"selecto": "^1.26.2",
|
"selecto": "^1.26.2",
|
||||||
"stateshot": "^1.3.5",
|
"stateshot": "^1.3.5",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"vue-plyr": "^7.0.0",
|
"vue-plyr": "^7.0.0",
|
||||||
"vue3-selecto": "^1.12.2",
|
"vue3-selecto": "^1.12.3",
|
||||||
"vuetify": "^3.2.4"
|
"vuetify": "^3.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -5254,6 +5255,11 @@
|
|||||||
"ufo": "^1.1.2"
|
"ufo": "^1.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/monaco-editor": {
|
||||||
|
"version": "0.45.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.45.0.tgz",
|
||||||
|
"integrity": "sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA=="
|
||||||
|
},
|
||||||
"node_modules/moveable": {
|
"node_modules/moveable": {
|
||||||
"version": "0.52.0",
|
"version": "0.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/moveable/-/moveable-0.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/moveable/-/moveable-0.52.0.tgz",
|
||||||
@ -8499,11 +8505,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue3-selecto": {
|
"node_modules/vue3-selecto": {
|
||||||
"version": "1.12.2",
|
"version": "1.12.3",
|
||||||
"resolved": "https://registry.npmjs.org/vue3-selecto/-/vue3-selecto-1.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/vue3-selecto/-/vue3-selecto-1.12.3.tgz",
|
||||||
"integrity": "sha512-GamgTxYLMnQ8/N1/lFeoyjcfzG7h7J9UUI1f+DH4CXBLq2EVOZ83CgG0TuOm1juxgNXbbmXcDIN72FRvUmTqKQ==",
|
"integrity": "sha512-zIQLwuvjTNaivITLbBAnm6Sh8BFRG8QocNZLzKvqRIqnpRa2lvlIw9+l1wdI/84msh8cSNIdiWsgote62cgtHA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"selecto": "~1.26.2"
|
"selecto": "~1.26.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vuetify": {
|
"node_modules/vuetify": {
|
||||||
|
@ -25,13 +25,14 @@
|
|||||||
"7z-wasm": "^1.0.2",
|
"7z-wasm": "^1.0.2",
|
||||||
"comlink": "^4.4.1",
|
"comlink": "^4.4.1",
|
||||||
"mime": "^3.0.0",
|
"mime": "^3.0.0",
|
||||||
|
"monaco-editor": "^0.45.0",
|
||||||
"moveable": "^0.52.0",
|
"moveable": "^0.52.0",
|
||||||
"sass": "^1.62.1",
|
"sass": "^1.62.1",
|
||||||
"selecto": "^1.26.2",
|
"selecto": "^1.26.2",
|
||||||
"stateshot": "^1.3.5",
|
"stateshot": "^1.3.5",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"vue-plyr": "^7.0.0",
|
"vue-plyr": "^7.0.0",
|
||||||
"vue3-selecto": "^1.12.2",
|
"vue3-selecto": "^1.12.3",
|
||||||
"vuetify": "^3.2.4"
|
"vuetify": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user