Skip to content

Commit

Permalink
[MS] Added file viewers
Browse files Browse the repository at this point in the history
  • Loading branch information
Max-7 committed Nov 7, 2024
1 parent 2453c6e commit baf3a31
Show file tree
Hide file tree
Showing 46 changed files with 3,952 additions and 62 deletions.
1 change: 1 addition & 0 deletions client/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ electron/node_modules
src/views/testing/TestPage.vue
bindings
playwright.config.ts
src/parsec/mock_files
1 change: 1 addition & 0 deletions client/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ package-lock.json
# Auto-generated
src/plugins/libparsec/definitions.ts
src/components.d.ts
src/parsec/mock_files
470 changes: 454 additions & 16 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@
"axios": "^1.7.4",
"file-type": "^19.6.0",
"luxon": "^3.4.4",
"mammoth": "^1.8.0",
"megashark-lib": "git+https://github.com/Scille/megashark-lib.git#a9430d2cd6d0f481ff4fef3c53682553a1391822",
"monaco-editor": "^0.52.0",
"pdfjs-dist": "^4.8.69",
"qrcode-vue3": "^1.6.8",
"uuid": "^9.0.1",
"vue": "^3.3.8",
"vue-i18n": "^9.6.5",
"vue-router": "^4.2.5"
"vue-router": "^4.2.5",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
},
"devDependencies": {
"@capacitor/cli": "^5.6.0",
Expand Down
5 changes: 2 additions & 3 deletions client/scripts/file_to_uint8array.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
parser.add_argument("-o", "--output", default=sys.stdout, help="Where to put the result")
args = parser.parse_args()
content = args.input.read_bytes()
array = ",".join([str(c) for c in content])
result = f"""
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS
array = ", ".join([str(c) for c in content])
result = f"""// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS
/*
Generated automatically with {sys.argv[0]}
Expand Down
70 changes: 68 additions & 2 deletions client/src/common/fileTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { closeFile, FileDescriptor, FsPath, openFile, Path, readFile, WorkspaceHandle } from '@/parsec';
import { fileTypeFromBuffer } from 'file-type';

enum FileContentType {
Expand All @@ -8,6 +9,8 @@ enum FileContentType {
Audio = 'audio',
Spreadsheet = 'spreadsheet',
Document = 'document',
Text = 'text',
PdfDocument = 'pdf-document',
Unknown = 'unknown',
}

Expand All @@ -19,8 +22,25 @@ interface DetectedFileType {

const IMAGES = ['image/png', 'image/webp', 'image/jpeg', 'image/svg+xml', 'image/bmp', 'image/gif'];
const SPREADSHEETS = ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
const DOCUMENTS = ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'];

Check warning on line 25 in client/src/common/fileTypes.ts

View workflow job for this annotation

GitHub Actions / spelling / cspell

Unknown word (wordprocessingml)
const PDF_DOCUMENTS = ['application/pdf'];
const AUDIOS = ['audio/x-wav', 'audio/mpeg'];
const VIDEOS = ['video/mp4', 'video/mpeg'];

async function detectFileContentType(buffer: Uint8Array): Promise<DetectedFileType> {
const TEXTS = new Map<string, string>([
['xml', 'application/xml'],
['json', 'application/json'],
['js', 'text/javascript'],
['html', 'text/html'],
['htm', 'text/html'],
['txt', 'text/plain'],
['sh', 'application/x-sh'],
['csv', 'text/csv'],
['css', 'text/css'],
['py', 'text/x-python'],
]);

async function detectFileContentTypeFromBuffer(buffer: Uint8Array): Promise<DetectedFileType> {
const result = await fileTypeFromBuffer(buffer);

if (!result) {
Expand All @@ -33,9 +53,55 @@ async function detectFileContentType(buffer: Uint8Array): Promise<DetectedFileTy
if (SPREADSHEETS.includes(result.mime)) {
return { type: FileContentType.Spreadsheet, extension: result.ext, mimeType: result.mime };
}
if (DOCUMENTS.includes(result.mime)) {
return { type: FileContentType.Document, extension: result.ext, mimeType: result.mime };
}
if (PDF_DOCUMENTS.includes(result.mime)) {
return { type: FileContentType.PdfDocument, extension: result.ext, mimeType: result.mime };
}
if (AUDIOS.includes(result.mime)) {
return { type: FileContentType.Audio, extension: result.ext, mimeType: result.mime };
}
if (VIDEOS.includes(result.mime)) {
return { type: FileContentType.Video, extension: result.ext, mimeType: result.mime };
}
console.log(`Unhandled mimetype ${result.mime}`);

return { type: FileContentType.Unknown, extension: result.ext, mimeType: result.mime };
}

export { DetectedFileType, FileContentType, detectFileContentType };
async function detectFileContentType(workspaceHandle: WorkspaceHandle, path: FsPath): Promise<DetectedFileType | undefined> {
const fileName = await Path.filename(path);

if (!fileName) {
return;
}
const ext = Path.getFileExtension(fileName).toLocaleLowerCase();

if (TEXTS.has(ext)) {
return { type: FileContentType.Text, extension: ext, mimeType: TEXTS.get(ext) as string };
}

const READ_CHUNK_SIZE = 512;
let fd: FileDescriptor | null = null;
try {
const openResult = await openFile(workspaceHandle, path, { read: true });
if (!openResult.ok) {
return;
}
fd = openResult.value;
const readResult = await readFile(workspaceHandle, fd, 0, READ_CHUNK_SIZE);
if (!readResult.ok) {
return;
}
const buffer = new Uint8Array(readResult.value);
const detectedFileType = await detectFileContentTypeFromBuffer(buffer);
return detectedFileType;
} finally {
if (fd) {
closeFile(workspaceHandle, fd);
}
}
}

export { DetectedFileType, detectFileContentType, detectFileContentTypeFromBuffer, FileContentType };
3 changes: 2 additions & 1 deletion client/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@
},
"myProfile": "My profile",
"recoveryExport": "Recovery files",
"history": "History"
"history": "History",
"viewer": "File viewer"
},
"previous": "Go back",
"invitations": {
Expand Down
3 changes: 2 additions & 1 deletion client/src/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@
},
"myProfile": "Mon profil",
"recoveryExport": "Fichiers de récupération",
"history": "Historique"
"history": "Historique",
"viewer": "Visualiseur de fichier"
},
"previous": "Précédent",
"invitations": {
Expand Down
15 changes: 15 additions & 0 deletions client/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import { Information, InformationDataType, InformationLevel, InformationManager,
import { InjectionProvider, InjectionProviderKey } from '@/services/injectionProvider';
import { Sentry } from '@/services/sentry';
import { Answer, Base64, I18n, Locale, MegaSharkPlugin, ThemeManager, Validity, askQuestion } from 'megashark-lib';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import * as pdfjs from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker?worker&url';

enum AppState {
Ready = 'ready',
Expand All @@ -51,6 +54,16 @@ function preventRightClick(): void {
});
}

async function initViewers(): Promise<void> {
pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker;

self.MonacoEnvironment = {
getWorker: function (_workerId, _label): Worker {
return new editorWorker();
},
};
}

async function setupApp(): Promise<void> {
await storageManagerInstance.init();
const storageManager = storageManagerInstance.get();
Expand Down Expand Up @@ -94,6 +107,8 @@ async function setupApp(): Promise<void> {
app.provide(InjectionProviderKey, injectionProvider);
app.provide(HotkeyManagerKey, hotkeyManager);

await initViewers();

// We get the app element
const appElem = document.getElementById('app');
if (!appElem) {
Expand Down
22 changes: 20 additions & 2 deletions client/src/parsec/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@ export async function entryStat(workspaceHandle: WorkspaceHandle, path: FsPath):
let entry: MockEntry;

if (path !== '/' && fileName.startsWith('File_')) {
entry = await generateFile(path, `${MOCK_FILE_ID}`);
entry = await generateFile(path, { parentId: `${MOCK_FILE_ID}`, fileName: fileName });
} else {
entry = await generateFolder(path, `${MOCK_FILE_ID}`);
entry = await generateFolder(path, { parentId: `${MOCK_FILE_ID}`, fileName: fileName });
}
(entry as any as EntryStat).baseVersion = entry.version;
(entry as any as EntryStat).confinementPoint = null;
Expand Down Expand Up @@ -326,6 +326,24 @@ export async function readFile(
case 'png':
console.log('Using PNG content');
return { ok: true, value: MockFiles.PNG };
case 'docx':
console.log('Using DOCX content');
return { ok: true, value: MockFiles.DOCX };
case 'txt':
console.log('Using TXT content');
return { ok: true, value: MockFiles.TXT };
case 'py':
console.log('Using PY content');
return { ok: true, value: MockFiles.PY };
case 'pdf':
console.log('Using PDF content');
return { ok: true, value: MockFiles.PDF };
case 'mp3':
console.log('Using MP3 content');
return { ok: true, value: MockFiles.MP3 };
case 'mp4':
console.log('Using MP4 content');
return { ok: true, value: MockFiles.MP4 };
}
console.log('Using default file content');
return {
Expand Down
2 changes: 1 addition & 1 deletion client/src/parsec/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export async function entryStatAt(
}
return result;
}
const entry = fileName.startsWith('File_') ? await generateFile(path, 'MOCK_ID') : await generateFolder(path, 'MOCK_ID');
const entry = fileName.startsWith('File_') ? await generateFile(path) : await generateFolder(path);
return { ok: true, value: entry as any as WorkspaceHistoryEntryStat };
}

Expand Down
Loading

0 comments on commit baf3a31

Please sign in to comment.