├── .gitignore ├── LICENSE.txt ├── README.md ├── build ├── apple-touch-icon-120x120.png ├── apple-touch-icon.png ├── assets │ ├── fonts │ │ ├── Exo-VariableFont_wght.ttf │ │ └── dos-vga-9x16.ttf │ ├── icons │ │ ├── icon-192x192-mono.png │ │ ├── icon-192x192.png │ │ ├── icon-512x512-mono.png │ │ └── icon-512x512.png │ ├── index-CT-f2sUk.css │ ├── index-voi2eI38.js │ ├── lib │ │ ├── libxm.wasm │ │ └── zip-no-worker-inflate.min.js │ └── music │ │ └── tracks.zip ├── favicon.ico ├── index.html ├── manifest.webmanifest ├── registerSW.js ├── screenshots │ ├── screenshot-395x640.png │ ├── screenshot-535x760.png │ ├── screenshot-app-1135x809.png │ ├── screenshot-custom-395x640.png │ └── screenshot-options-700x800.png └── sw.js ├── eslint.config.mjs ├── index.html ├── package-lock.json ├── package.json ├── public ├── apple-touch-icon-120x120.png ├── apple-touch-icon.png ├── assets │ ├── fonts │ │ ├── Exo-VariableFont_wght.ttf │ │ └── dos-vga-9x16.ttf │ ├── icons │ │ ├── icon-192x192-mono.png │ │ ├── icon-192x192.png │ │ ├── icon-512x512-mono.png │ │ └── icon-512x512.png │ ├── lib │ │ ├── libxm.wasm │ │ └── zip-no-worker-inflate.min.js │ └── music │ │ └── tracks.zip ├── favicon.ico └── screenshots │ ├── screenshot-395x640.png │ ├── screenshot-535x760.png │ ├── screenshot-app-1135x809.png │ ├── screenshot-custom-395x640.png │ └── screenshot-options-700x800.png ├── src ├── index.css ├── index.jsx ├── sw.js └── zip-manager │ ├── ZipManager.jsx │ ├── business │ ├── constants.js │ ├── events.js │ ├── features │ │ ├── app.js │ │ ├── clipboard.js │ │ ├── common.js │ │ ├── downloads.js │ │ ├── entries.js │ │ ├── filesystem.js │ │ ├── folders.js │ │ ├── highlighted-entries.js │ │ ├── index.js │ │ ├── misc.js │ │ ├── options.js │ │ └── selected-folder.js │ ├── index.js │ └── ui-state.js │ ├── components │ ├── BottomButtonBar.jsx │ ├── Breadcrumb.jsx │ ├── Buttons.jsx │ ├── Downloads.jsx │ ├── Entries.jsx │ ├── History.jsx │ ├── InfoBar.jsx │ ├── NavigationBar.jsx │ ├── TopButtonBar.jsx │ ├── dialogs │ │ ├── ChooseActionDialog.jsx │ │ ├── CreateFolderDialog.jsx │ │ ├── DeleteEntriesDialog.jsx │ │ ├── Dialog.jsx │ │ ├── ErrorMessageDialog.jsx │ │ ├── ExportZipDialog.jsx │ │ ├── ExtractDialog.jsx │ │ ├── ImportPasswordDialog.jsx │ │ ├── OptionsDialog.jsx │ │ ├── RenameDialog.jsx │ │ ├── ResetDialog.jsx │ │ └── styles │ │ │ └── OptionsDialog.css │ ├── index.jsx │ └── styles │ │ ├── BottomButtonBar.css │ │ ├── Downloads.css │ │ ├── Entries.css │ │ ├── InfoBar.css │ │ ├── NavigationBar.css │ │ └── TopButtonBar.css │ ├── hooks │ └── hooks.js │ ├── messages │ ├── de-DE.js │ ├── en-US.js │ ├── es-ES.js │ ├── fr-FR.js │ ├── index.js │ ├── it-IT.js │ └── pt-PT.js │ ├── services │ ├── document-service.js │ ├── download-service.js │ ├── environment-service.js │ ├── file-handlers-service.js │ ├── filesystem-service.js │ ├── i18n-service.js │ ├── index.js │ ├── keyboard-service.js │ ├── lib │ │ ├── jsSID │ │ │ └── jsSID.js │ │ ├── libxm │ │ │ └── libxm-es6.js │ │ └── webaudio-tinysynth │ │ │ ├── LICENSE │ │ │ └── webaudio-tinysynth-core-es6.js │ ├── music-service-constants.js │ ├── music-service.js │ ├── share-target-service-constants.js │ ├── share-target-service.js │ ├── storage-service.js │ ├── stylesheet-service.js │ ├── theme-service.js │ ├── window-service.js │ └── zip-service.js │ └── styles │ ├── Base.css │ ├── ButtonBar.css │ ├── Dialog.css │ ├── ListItem.css │ ├── Skins.css │ ├── ZipManager.css │ └── index.css └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # misc 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Gildas Lormeau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a PWA using the filesystem API in [zip.js](https://github.com/gildas-lormeau/zip.js). 2 | 3 | It is based on React and built with Vite. 4 | 5 | See https://gildas-lormeau.github.io/zip-manager/ 6 | 7 | ## Features 8 | 9 | - installable for offline use 10 | - keyboard navigation 11 | - multi-core compression/decompression of zip entries 12 | - encrypted zip files 13 | - 4GB+ zip files (i.e. Zip64) 14 | - drag and drop files/directories from filesystem on desktop 15 | - integration in the "Open with..." menu on desktop (see https://developer.mozilla.org/en-US/docs/Web/Manifest/file_handlers) 16 | - integration with the "Share" feature on mobile (see https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target) 17 | - customizable accent color 18 | - DOS theme 19 | - integrated MIDI/XM/SID music player and visualizer 20 | 21 | ## Screenshots 22 | 23 | - Desktop 24 | 25 | ![screenshot-app-1135x809](https://user-images.githubusercontent.com/396787/233869327-c379f3dd-3ff7-426e-a2c0-c1eae8b7f5fb.png) 26 | 27 | - Mobile 28 | 29 | ![screenshot-395x640](https://user-images.githubusercontent.com/396787/233868744-56d47959-a99b-495d-bc3e-5b2e7c99c125.png) 30 | -------------------------------------------------------------------------------- /build/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /build/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/apple-touch-icon.png -------------------------------------------------------------------------------- /build/assets/fonts/Exo-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/fonts/Exo-VariableFont_wght.ttf -------------------------------------------------------------------------------- /build/assets/fonts/dos-vga-9x16.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/fonts/dos-vga-9x16.ttf -------------------------------------------------------------------------------- /build/assets/icons/icon-192x192-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/icons/icon-192x192-mono.png -------------------------------------------------------------------------------- /build/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /build/assets/icons/icon-512x512-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/icons/icon-512x512-mono.png -------------------------------------------------------------------------------- /build/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /build/assets/lib/libxm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/lib/libxm.wasm -------------------------------------------------------------------------------- /build/assets/music/tracks.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/assets/music/tracks.zip -------------------------------------------------------------------------------- /build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Zip Manager 16 | 33 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /build/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"Zip Manager","short_name":"Zip Manager","description":"Read, edit and write zip files.","start_url":"./index.html","display":"fullscreen","background_color":"#ffffff","theme_color":"#000000","lang":"en","scope":"./","orientation":"any","categories":["utilities"],"icons":[{"src":"assets/icons/icon-512x512.png","sizes":"512x512","type":"image/png","purpose":"any"},{"src":"assets/icons/icon-192x192.png","sizes":"192x192","type":"image/png","purpose":"any"},{"src":"assets/icons/icon-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"},{"src":"assets/icons/icon-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"assets/icons/icon-512x512-mono.png","sizes":"512x512","type":"image/png","purpose":"monochrome"},{"src":"./assets/icons/icon-192x192-mono.png","sizes":"192x192","type":"image/png","purpose":"monochrome"}],"file_handlers":[{"action":"./index.html","accept":{"application/zip":[".zip"],"application/vnd.openxmlformats-officedocument.wordprocessingml.document":[".docx"],"application/epub+zip":[".epub"],"application/java-archive":[".jar"],"application/vnd.oasis.opendocument.presentation":[".odp"],"application/vnd.oasis.opendocument.spreadsheet":[".ods"],"application/vnd.oasis.opendocument.text":[".odt"],"application/vnd.openxmlformats-officedocument.presentationml.presentation":[".pptx"],"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":[".xlsx"],"application/vnd.apple.keynote":[".key"],"application/vnd.apple.pages":[".pages"],"application/vnd.apple.numbers":[".numbers"],"application/vnd.android.package-archive":[".apk"],"application/x-ios-app":[".ipa"]},"launch_type":"single-client"}],"share_target":{"action":"./shared-files","enctype":"multipart/form-data","method":"POST","params":{"files":[{"name":"shared-files","accept":["*/*"]}]}},"screenshots":[{"src":"./screenshots/screenshot-395x640.png","sizes":"395x640","type":"image/png","form_factor":"narrow","label":"Main screen on mobile"},{"src":"./screenshots/screenshot-app-1135x809.png","sizes":"1135x809","type":"image/png","form_factor":"wide","label":"Main screen on desktop"},{"src":"./screenshots/screenshot-custom-395x640.png","sizes":"395x640","type":"image/png","form_factor":"narrow","label":"Custom user interface on mobile"}]} 2 | -------------------------------------------------------------------------------- /build/registerSW.js: -------------------------------------------------------------------------------- 1 | if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('./sw.js', { scope: './' })})} -------------------------------------------------------------------------------- /build/screenshots/screenshot-395x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/screenshots/screenshot-395x640.png -------------------------------------------------------------------------------- /build/screenshots/screenshot-535x760.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/screenshots/screenshot-535x760.png -------------------------------------------------------------------------------- /build/screenshots/screenshot-app-1135x809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/screenshots/screenshot-app-1135x809.png -------------------------------------------------------------------------------- /build/screenshots/screenshot-custom-395x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/screenshots/screenshot-custom-395x640.png -------------------------------------------------------------------------------- /build/screenshots/screenshot-options-700x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/build/screenshots/screenshot-options-700x800.png -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import js from "@eslint/js"; 2 | import react from "eslint-plugin-react"; 3 | import reactCompiler from "eslint-plugin-react-compiler"; 4 | import reactRefresh from "eslint-plugin-react-refresh"; 5 | 6 | export default [ 7 | js.configs.recommended, 8 | { 9 | files: ["**/*.{js,jsx}"], 10 | plugins: { 11 | react, 12 | "react-compiler": reactCompiler, 13 | "react-refresh": reactRefresh 14 | }, 15 | languageOptions: { 16 | ecmaVersion: 2020, 17 | sourceType: "module", 18 | globals: { 19 | console: "readonly" 20 | }, 21 | parserOptions: { 22 | ecmaFeatures: { 23 | jsx: true 24 | } 25 | } 26 | }, 27 | rules: { 28 | "react/jsx-uses-vars": "error", 29 | "react/jsx-uses-react": "error", 30 | "react-refresh/only-export-components": ["warn"], 31 | "indent": ["error", 2], 32 | "linebreak-style": ["error", "unix"], 33 | "quotes": ["error", "double"], 34 | "semi": ["warn", "always"], 35 | "no-console": "warn", 36 | "no-debugger": "warn", 37 | "no-unused-vars": "warn", 38 | "react-compiler/react-compiler": "error" 39 | }, 40 | ignores: [ 41 | "**/node_modules/", 42 | ".git/" 43 | ] 44 | } 45 | ]; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Zip Manager 16 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zip-manager", 3 | "version": "0.1.0", 4 | "type": "module", 5 | "dependencies": { 6 | "@zip.js/zip.js": "^2.7.59", 7 | "react": "^19.1.0", 8 | "react-dom": "^19.1.0", 9 | "workbox-core": "^7.3.0", 10 | "workbox-precaching": "^7.3.0", 11 | "workbox-routing": "^7.3.0" 12 | }, 13 | "scripts": { 14 | "start": "vite", 15 | "build": "vite build", 16 | "serve": "vite preview" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.2%", 21 | "not dead", 22 | "not op_mini all" 23 | ], 24 | "development": [ 25 | "last 1 chrome version", 26 | "last 1 firefox version", 27 | "last 1 safari version" 28 | ] 29 | }, 30 | "devDependencies": { 31 | "@vitejs/plugin-react": "^4.3.4", 32 | "babel-plugin-react-compiler": "^19.0.0-beta-aeaed83-20250323", 33 | "eslint": "^9.23.0", 34 | "eslint-plugin-react": "^7.37.4", 35 | "eslint-plugin-react-compiler": "^19.0.0-beta-aeaed83-20250323", 36 | "eslint-plugin-react-refresh": "^0.4.19", 37 | "vite": "^6.2.3", 38 | "vite-plugin-pwa": "^1.0.0" 39 | } 40 | } -------------------------------------------------------------------------------- /public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/assets/fonts/Exo-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/fonts/Exo-VariableFont_wght.ttf -------------------------------------------------------------------------------- /public/assets/fonts/dos-vga-9x16.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/fonts/dos-vga-9x16.ttf -------------------------------------------------------------------------------- /public/assets/icons/icon-192x192-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/icons/icon-192x192-mono.png -------------------------------------------------------------------------------- /public/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/assets/icons/icon-512x512-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/icons/icon-512x512-mono.png -------------------------------------------------------------------------------- /public/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/assets/lib/libxm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/lib/libxm.wasm -------------------------------------------------------------------------------- /public/assets/music/tracks.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/assets/music/tracks.zip -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/favicon.ico -------------------------------------------------------------------------------- /public/screenshots/screenshot-395x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/screenshots/screenshot-395x640.png -------------------------------------------------------------------------------- /public/screenshots/screenshot-535x760.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/screenshots/screenshot-535x760.png -------------------------------------------------------------------------------- /public/screenshots/screenshot-app-1135x809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/screenshots/screenshot-app-1135x809.png -------------------------------------------------------------------------------- /public/screenshots/screenshot-custom-395x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/screenshots/screenshot-custom-395x640.png -------------------------------------------------------------------------------- /public/screenshots/screenshot-options-700x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gildas-lormeau/zip-manager/8918543161e74e37fa92f799f8803caf0f7724f6/public/screenshots/screenshot-options-700x800.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: "Exo", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", 4 | "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | @font-face { 11 | font-family: "Exo"; 12 | src: url("/assets/fonts/Exo-VariableFont_wght.ttf"); 13 | font-display: swap; 14 | } -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | import React from "react"; 4 | import ReactDOM from "react-dom/client"; 5 | import "./index.css"; 6 | import App from "./zip-manager/ZipManager.jsx"; 7 | 8 | const root = ReactDOM.createRoot(document.body); 9 | root.render( 10 | 11 | 12 | 13 | ); -------------------------------------------------------------------------------- /src/sw.js: -------------------------------------------------------------------------------- 1 | /* global self, URL, Response, caches, importScripts, zip */ 2 | /* eslint-disable no-restricted-globals */ 3 | 4 | import { cacheNames, clientsClaim } from "workbox-core"; 5 | import { 6 | cleanupOutdatedCaches, 7 | precacheAndRoute, 8 | getCacheKeyForURL 9 | } from "workbox-precaching"; 10 | import { registerRoute } from "workbox-routing"; 11 | 12 | import { 13 | MAINPAGE_REDIRECT_PATH, 14 | SHARED_FILES_RELATIVE_PATH, 15 | SHARED_FILES_CACHE_ID, 16 | SHARED_FILES_FORM_PATH 17 | } from "./zip-manager/services/share-target-service-constants.js"; 18 | import { 19 | MUSIC_TRACKS_PATH, 20 | MUSIC_TRACK_PATH_REGEXP, 21 | MUSIC_TRACK_INDEX_REGEXP, 22 | MUSIC_FILE_CONTENT_TYPES 23 | } from "./zip-manager/services/music-service-constants.js"; 24 | 25 | const GET_REQUEST = "GET"; 26 | const POST_REQUEST = "POST"; 27 | 28 | importScripts("./assets/lib/zip-no-worker-inflate.min.js"); 29 | cleanupOutdatedCaches(); 30 | precacheAndRoute(self.__WB_MANIFEST); 31 | self.skipWaiting(); 32 | registerRoute(SHARED_FILES_RELATIVE_PATH, getSharedFiles, GET_REQUEST); 33 | registerRoute(SHARED_FILES_RELATIVE_PATH, setSharedFiles, POST_REQUEST); 34 | registerRoute(MUSIC_TRACK_PATH_REGEXP, getMusicTrack, GET_REQUEST); 35 | clientsClaim(); 36 | 37 | async function setSharedFiles({ event }) { 38 | const formData = await event.request.formData(); 39 | const cache = await caches.open(SHARED_FILES_CACHE_ID); 40 | await cache.put( 41 | new URL(SHARED_FILES_FORM_PATH, self.location).href, 42 | new Response(formData) 43 | ); 44 | return Response.redirect(MAINPAGE_REDIRECT_PATH, 303); 45 | } 46 | 47 | function getSharedFiles({ event }) { 48 | event.respondWith(getSharedFilesResponse()); 49 | } 50 | 51 | async function getSharedFilesResponse() { 52 | const cache = await caches.open(SHARED_FILES_CACHE_ID); 53 | const response = await cache.match(SHARED_FILES_FORM_PATH); 54 | if (response) { 55 | await cache.delete(SHARED_FILES_FORM_PATH); 56 | return response; 57 | } 58 | } 59 | 60 | async function getMusicTrack({ event }) { 61 | const cache = await caches.open(cacheNames.precache); 62 | const response = await cache.match(getCacheKeyForURL(MUSIC_TRACKS_PATH)); 63 | const zipReader = new zip.ZipReader(response.body); 64 | const entries = await zipReader.getEntries(); 65 | const fileEntryIndex = Number( 66 | event.request.url.match(MUSIC_TRACK_INDEX_REGEXP)[0] 67 | ); 68 | const fileEntry = entries[fileEntryIndex]; 69 | const contentType = MUSIC_FILE_CONTENT_TYPES.find((info) => 70 | fileEntry.filename.endsWith(info.extension) 71 | ).type; 72 | const data = await fileEntry.getData(new zip.BlobWriter(contentType)); 73 | await zipReader.close(); 74 | return new Response(data); 75 | } 76 | -------------------------------------------------------------------------------- /src/zip-manager/business/constants.js: -------------------------------------------------------------------------------- 1 | const ACTION_KEY = " "; 2 | const ENTER_KEY = "Enter"; 3 | const TAB_KEY = "Tab"; 4 | const CUT_KEY = "x"; 5 | const COPY_KEY = "c"; 6 | const EXTRACT_KEY = "Enter"; 7 | const RENAME_KEY = "r"; 8 | const PASTE_KEY = "v"; 9 | const CREATE_FOLDER_KEY = "d"; 10 | const ADD_FILES_KEY = "f"; 11 | const IMPORT_ZIP_KEY = "i"; 12 | const EXPORT_ZIP_KEY = "e"; 13 | const HIGHLIGHT_ALL_KEY = "a"; 14 | const DELETE_KEYS = ["Backspace", "Delete"]; 15 | const DOWN_KEY = "ArrowDown"; 16 | const UP_KEY = "ArrowUp"; 17 | const LEFT_KEY = "ArrowLeft"; 18 | const RIGHT_KEY = "ArrowRight"; 19 | const PAGE_UP_KEY = "PageUp"; 20 | const PAGE_DOWN_KEY = "PageDown"; 21 | const HOME_KEY = "Home"; 22 | const END_KEY = "End"; 23 | const BACK_KEY = "ArrowLeft"; 24 | const FORWARD_KEY = "ArrowRight"; 25 | const DEFAULT_MIME_TYPE = "application/octet-stream"; 26 | const OPTIONS_DEFAULT_SKIN = "skin-default"; 27 | const OPTIONS_DOS_SKIN = "skin-dos"; 28 | const DEFAULT_OPTIONS = { 29 | accentColor: "#FF672E", 30 | skin: OPTIONS_DEFAULT_SKIN, 31 | hideNavigationBar: false, 32 | hideDownloadManager: false, 33 | hideInfobar: false, 34 | bufferedWrite: true, 35 | checkSignature: false, 36 | keepOrder: true, 37 | maxWorkers: 2, 38 | chunkSize: 512 * 1024, 39 | promptForExportPassword: true, 40 | defaultExportPassword: "", 41 | zoomFactor: 100 42 | }; 43 | const ZIP_EXTENSION = ".zip"; 44 | const ZIP_EXTENSIONS = [ 45 | ZIP_EXTENSION, 46 | ".docx", 47 | ".epub", 48 | ".jar", 49 | ".odp", 50 | ".ods", 51 | ".odt", 52 | ".pptx", 53 | ".xlsx", 54 | ".key", 55 | ".pages", 56 | ".numbers", 57 | ".apk", 58 | ".ipa" 59 | ]; 60 | const ZIP_EXTENSIONS_ACCEPT = { 61 | "application/zip": [ZIP_EXTENSION], 62 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [ 63 | ".docx" 64 | ], 65 | "application/epub+zip": [".epub"], 66 | "application/java-archive": [".jar"], 67 | "application/vnd.oasis.opendocument.presentation": [".odp"], 68 | "application/vnd.oasis.opendocument.spreadsheet": [".ods"], 69 | "application/vnd.oasis.opendocument.text": [".odt"], 70 | "application/vnd.openxmlformats-officedocument.presentationml.presentation": [ 71 | ".pptx" 72 | ], 73 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [ 74 | ".xlsx" 75 | ], 76 | "application/vnd.apple.keynote": [".key"], 77 | "application/vnd.apple.pages": [".pages"], 78 | "application/vnd.apple.numbers": [".numbers"], 79 | "application/vnd.android.package-archive": [".apk"], 80 | "application/x-ios-app": [".ipa"] 81 | }; 82 | const ZIP_EXTENSIONS_ACCEPT_STRING = ZIP_EXTENSIONS.join(","); 83 | const LONG_TOUCH_DELAY = 750; 84 | const CREATE_FOLDER_BUTTON_NAME = "create-folder-button"; 85 | const ADD_FILES_BUTTON_NAME = "add-files-button"; 86 | const IMPORT_ZIP_BUTTON_NAME = "import-zip-button"; 87 | const EXPORT_ZIP_BUTTON_NAME = "export-zip-button"; 88 | const COPY_BUTTON_NAME = "copy-button"; 89 | const CUT_BUTTON_NAME = "cut-button"; 90 | const PASTE_BUTTON_NAME = "paste-button"; 91 | const EXTRACT_BUTTON_NAME = "extract-button"; 92 | const HIGHLIGHT_ALL_BUTTON_NAME = "select-all"; 93 | const RENAME_BUTTON_NAME = "rename-button"; 94 | const DELETE_BUTTON_NAME = "delete-entry-button"; 95 | const BACK_BUTTON_NAME = "back-button"; 96 | const FORWARD_BUTTON_NAME = "forward-button"; 97 | const OPTIONS_KEY_NAME = "options"; 98 | const FONT_SIZE_PROPERTY_NAME = "font-size"; 99 | const NO_ENTRIES_CUSTOM_PROPERTY_NAME = "--message-drag-and-drop-entries"; 100 | const FOLDER_SEPARATOR_CUSTOM_PROPERTY_NAME = "--folder-separator"; 101 | const FOLDER_SEPARATOR = "/"; 102 | const APP_CLASSNAME = "main-container"; 103 | const INFOBAR_HIDDEN_CLASSNAME = "hidden-footer"; 104 | const DOWNLOAD_MANAGER_HIDDEN_CLASSNAME = "hidden-downloads"; 105 | const APP_LOADING_ATTRIBUTE_NAME = "app-loading"; 106 | const LOW_RES_FFT = 32; 107 | const HIGH_RES_FFT = 128; 108 | const FFT_RESOLUTIONS = { 109 | [OPTIONS_DEFAULT_SKIN]: HIGH_RES_FFT, 110 | [OPTIONS_DOS_SKIN]: LOW_RES_FFT 111 | }; 112 | 113 | export { 114 | ACTION_KEY, 115 | ENTER_KEY, 116 | TAB_KEY, 117 | CUT_KEY, 118 | COPY_KEY, 119 | EXTRACT_KEY, 120 | RENAME_KEY, 121 | PASTE_KEY, 122 | CREATE_FOLDER_KEY, 123 | ADD_FILES_KEY, 124 | IMPORT_ZIP_KEY, 125 | EXPORT_ZIP_KEY, 126 | HIGHLIGHT_ALL_KEY, 127 | DELETE_KEYS, 128 | DOWN_KEY, 129 | UP_KEY, 130 | LEFT_KEY, 131 | RIGHT_KEY, 132 | PAGE_UP_KEY, 133 | PAGE_DOWN_KEY, 134 | HOME_KEY, 135 | END_KEY, 136 | BACK_KEY, 137 | FORWARD_KEY, 138 | DEFAULT_MIME_TYPE, 139 | DEFAULT_OPTIONS, 140 | ZIP_EXTENSION, 141 | ZIP_EXTENSIONS, 142 | ZIP_EXTENSIONS_ACCEPT, 143 | ZIP_EXTENSIONS_ACCEPT_STRING, 144 | LONG_TOUCH_DELAY, 145 | CREATE_FOLDER_BUTTON_NAME, 146 | ADD_FILES_BUTTON_NAME, 147 | IMPORT_ZIP_BUTTON_NAME, 148 | EXPORT_ZIP_BUTTON_NAME, 149 | COPY_BUTTON_NAME, 150 | CUT_BUTTON_NAME, 151 | PASTE_BUTTON_NAME, 152 | EXTRACT_BUTTON_NAME, 153 | HIGHLIGHT_ALL_BUTTON_NAME, 154 | RENAME_BUTTON_NAME, 155 | DELETE_BUTTON_NAME, 156 | BACK_BUTTON_NAME, 157 | FORWARD_BUTTON_NAME, 158 | OPTIONS_KEY_NAME, 159 | FONT_SIZE_PROPERTY_NAME, 160 | NO_ENTRIES_CUSTOM_PROPERTY_NAME, 161 | FOLDER_SEPARATOR_CUSTOM_PROPERTY_NAME, 162 | FOLDER_SEPARATOR, 163 | APP_CLASSNAME, 164 | INFOBAR_HIDDEN_CLASSNAME, 165 | DOWNLOAD_MANAGER_HIDDEN_CLASSNAME, 166 | APP_LOADING_ATTRIBUTE_NAME, 167 | OPTIONS_DEFAULT_SKIN, 168 | OPTIONS_DOS_SKIN, 169 | FFT_RESOLUTIONS 170 | }; 171 | -------------------------------------------------------------------------------- /src/zip-manager/business/events.js: -------------------------------------------------------------------------------- 1 | function getEventHandlers({ 2 | entries, 3 | downloads, 4 | dialogDisplayed, 5 | onEntriesKeyUp, 6 | onFoldersKeyUp, 7 | onHighlightedEntriesKeyUp, 8 | onAppKeyUp, 9 | onEntriesKeyDown, 10 | onHighlightedEntriesKeyDown, 11 | onSelectedFolderKeyDown 12 | }) { 13 | function handleKeyUp(event) { 14 | if (!dialogDisplayed) { 15 | onEntriesKeyUp(event); 16 | onFoldersKeyUp(event); 17 | onHighlightedEntriesKeyUp(event); 18 | onAppKeyUp(event); 19 | } 20 | } 21 | 22 | function handleKeyDown(event, resetHighlightedEntryElement) { 23 | if (!dialogDisplayed) { 24 | onEntriesKeyDown(event, resetHighlightedEntryElement); 25 | onHighlightedEntriesKeyDown(event); 26 | onSelectedFolderKeyDown(event); 27 | } 28 | } 29 | 30 | function handlePageUnload(event) { 31 | if (entries.length || downloads.length) { 32 | event.preventDefault(); 33 | event.returnValue = ""; 34 | } 35 | } 36 | 37 | return { 38 | handlePageUnload, 39 | handleKeyUp, 40 | handleKeyDown 41 | }; 42 | } 43 | 44 | export default getEventHandlers; 45 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/app.js: -------------------------------------------------------------------------------- 1 | function getAppFeatures({ 2 | disabledEnterEntry, 3 | zipFilesystem, 4 | highlightedEntry, 5 | selectedFolder, 6 | hiddenInfobar, 7 | hiddenDownloadManager, 8 | modifierKeyPressed, 9 | setNavigation, 10 | setSelectedFolder, 11 | setHighlightedIds, 12 | setHistory, 13 | setClickedButtonName, 14 | goIntoFolder, 15 | openPromptExtract, 16 | refreshSelectedFolder, 17 | stylesheetService, 18 | documentService, 19 | i18nService, 20 | constants, 21 | messages 22 | }) { 23 | const { 24 | NO_ENTRIES_CUSTOM_PROPERTY_NAME, 25 | FOLDER_SEPARATOR_CUSTOM_PROPERTY_NAME, 26 | FOLDER_SEPARATOR, 27 | APP_LOADING_ATTRIBUTE_NAME, 28 | APP_CLASSNAME, 29 | INFOBAR_HIDDEN_CLASSNAME, 30 | DOWNLOAD_MANAGER_HIDDEN_CLASSNAME, 31 | ACTION_KEY 32 | } = constants; 33 | 34 | function initAppFeatures() { 35 | stylesheetService.setStyle( 36 | NO_ENTRIES_CUSTOM_PROPERTY_NAME, 37 | JSON.stringify(messages.NO_ENTRIES_LABEL) 38 | ); 39 | stylesheetService.setStyle( 40 | FOLDER_SEPARATOR_CUSTOM_PROPERTY_NAME, 41 | JSON.stringify(FOLDER_SEPARATOR) 42 | ); 43 | documentService.setDocumentLanguage(i18nService.getLanguageId()); 44 | documentService.removeDocumentAttribute(APP_LOADING_ATTRIBUTE_NAME); 45 | } 46 | 47 | function updateZipFilesystem() { 48 | const { root } = zipFilesystem; 49 | setSelectedFolder(root); 50 | setHighlightedIds([]); 51 | setNavigation({ 52 | previousHighlight: null, 53 | direction: 0 54 | }); 55 | setHistory({ path: [root], index: 0 }); 56 | refreshSelectedFolder(root); 57 | } 58 | 59 | function enterEntry(entry) { 60 | if (entry.directory) { 61 | goIntoFolder(entry); 62 | } else { 63 | openPromptExtract(entry); 64 | } 65 | } 66 | 67 | function resetClickedButtonName() { 68 | setClickedButtonName(null); 69 | } 70 | 71 | function getAppClassName() { 72 | const classes = [APP_CLASSNAME]; 73 | if (hiddenInfobar) { 74 | classes.push(INFOBAR_HIDDEN_CLASSNAME); 75 | } 76 | if (hiddenDownloadManager) { 77 | classes.push(DOWNLOAD_MANAGER_HIDDEN_CLASSNAME); 78 | } 79 | return classes.join(" "); 80 | } 81 | 82 | function onAppKeyUp(event) { 83 | if (!event.altKey && !modifierKeyPressed(event) && !event.shiftKey) { 84 | if (event.key === ACTION_KEY && !disabledEnterEntry) { 85 | enterEntry(highlightedEntry || selectedFolder.parent); 86 | event.preventDefault(); 87 | } 88 | } 89 | } 90 | 91 | return { 92 | initAppFeatures, 93 | enterEntry, 94 | updateZipFilesystem, 95 | resetClickedButtonName, 96 | getAppClassName, 97 | onAppKeyUp 98 | }; 99 | } 100 | 101 | export default getAppFeatures; 102 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/clipboard.js: -------------------------------------------------------------------------------- 1 | function getClipboardFeatures({ setClipboardData }) { 2 | function resetClipboardData() { 3 | setClipboardData(null); 4 | } 5 | 6 | return { 7 | resetClipboardData 8 | }; 9 | } 10 | 11 | export default getClipboardFeatures; 12 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/common.js: -------------------------------------------------------------------------------- 1 | function getCommonFeatures({ 2 | dialogs, 3 | setDownloads, 4 | setDialogs, 5 | removeDownload, 6 | downloadService, 7 | filesystemService, 8 | environmentService 9 | }) { 10 | const isMacOSPlatform = environmentService.isMacOSPlatform(); 11 | 12 | async function saveEntries(entries, filename, options, parentHandle) { 13 | if (filesystemService.savePickersSupported()) { 14 | try { 15 | if (!parentHandle && (entries.length > 1 || entries[0].directory)) { 16 | parentHandle = await getParentHandle(); 17 | } 18 | } catch (error) { 19 | if (downloadService.downloadAborted(error)) { 20 | return; 21 | } else { 22 | throw error; 23 | } 24 | } 25 | } 26 | await Promise.all( 27 | entries.map(async (entry) => 28 | saveFile(entry, filename, options, parentHandle) 29 | ) 30 | ); 31 | } 32 | 33 | async function saveFile(entry, filename, options, parentHandle) { 34 | if (!parentHandle && entry.directory) { 35 | parentHandle = await getParentHandle(); 36 | } 37 | const name = filename || entry.name; 38 | let download; 39 | try { 40 | if (entry.directory) { 41 | await saveDirectoryEntry(name, entry, options, parentHandle); 42 | } else { 43 | download = { 44 | name, 45 | controller: downloadService.createAbortController(), 46 | progressValue: null, 47 | progressMax: null 48 | }; 49 | await saveFileEntry(name, entry, options, download, parentHandle); 50 | } 51 | } catch (error) { 52 | if (!downloadService.downloadAborted(error)) { 53 | throw error; 54 | } 55 | } finally { 56 | if (download) { 57 | removeDownload(download); 58 | } 59 | } 60 | } 61 | 62 | async function saveDirectoryEntry(name, entry, options, parentHandle) { 63 | const directoryHandle = await parentHandle.getDirectoryHandle(name, { 64 | create: true 65 | }); 66 | await saveEntries(entry.children, null, options, directoryHandle); 67 | } 68 | 69 | async function saveFileEntry(name, entry, options, download, parentHandle) { 70 | const { signal } = download.controller; 71 | const onprogress = (progressValue, progressMax) => 72 | onDownloadProgress(download.id, progressValue, progressMax); 73 | let fileHandle, writable, blob; 74 | if (filesystemService.savePickersSupported()) { 75 | if (parentHandle) { 76 | fileHandle = await parentHandle.getFileHandle(name, { 77 | create: true 78 | }); 79 | } else { 80 | fileHandle = await getFileHandle(name); 81 | download.name = fileHandle.name; 82 | } 83 | writable = await fileHandle.createWritable(); 84 | } else { 85 | ({ writable, blob } = getWritableBlob()); 86 | } 87 | setDownloads((downloads) => { 88 | let { nextId } = downloads; 89 | download.id = nextId; 90 | nextId = nextId + 1; 91 | return { 92 | nextId, 93 | queue: [download, ...downloads.queue] 94 | }; 95 | }); 96 | await entry.getWritable(writable, { signal, onprogress, ...options }); 97 | if (!filesystemService.savePickersSupported() && !signal.aborted) { 98 | filesystemService.saveBlob(await blob, download.name); 99 | } 100 | } 101 | 102 | function getWritableBlob() { 103 | // eslint-disable-next-line no-undef 104 | const { readable, writable } = new TransformStream({}); 105 | // eslint-disable-next-line no-undef 106 | const blob = new Response(readable).blob(); 107 | return { 108 | blob, 109 | writable 110 | }; 111 | } 112 | 113 | function getParentHandle() { 114 | return filesystemService.showDirectoryPicker({ 115 | mode: "readwrite", 116 | startIn: "downloads" 117 | }); 118 | } 119 | 120 | function getFileHandle(suggestedName) { 121 | return filesystemService.showSaveFilePicker({ 122 | suggestedName, 123 | mode: "readwrite", 124 | startIn: "downloads" 125 | }); 126 | } 127 | 128 | function onDownloadProgress(downloadId, progressValue, progressMax) { 129 | setDownloads((downloads) => ({ 130 | ...downloads, 131 | queue: downloads.queue.map((download) => { 132 | if (download.id === downloadId) { 133 | download = { 134 | ...download, 135 | progressValue, 136 | progressMax 137 | }; 138 | } 139 | return download; 140 | }) 141 | })); 142 | } 143 | 144 | function openDisplayError(message) { 145 | setDialogs({ 146 | ...dialogs, 147 | displayError: { message } 148 | }); 149 | } 150 | 151 | function closeDisplayError() { 152 | setDialogs({ 153 | ...dialogs, 154 | displayError: null 155 | }); 156 | } 157 | 158 | function modifierKeyPressed(event) { 159 | return isMacOSPlatform ? event.metaKey : event.ctrlKey; 160 | } 161 | 162 | return { 163 | modifierKeyPressed, 164 | saveZipFile: saveFile, 165 | saveEntries, 166 | openDisplayError, 167 | closeDisplayError 168 | }; 169 | } 170 | 171 | export default getCommonFeatures; 172 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/downloads.js: -------------------------------------------------------------------------------- 1 | function getDownloadsFeatures({ setDownloads, downloadService }) { 2 | function abortDownload(deletedDownload) { 3 | removeDownload(deletedDownload); 4 | downloadService.abortDownload(deletedDownload.controller); 5 | } 6 | 7 | function removeDownload(deletedDownload) { 8 | setDownloads((downloads) => ({ 9 | ...downloads, 10 | queue: downloads.queue.filter( 11 | (download) => download.id !== deletedDownload.id 12 | ) 13 | })); 14 | } 15 | 16 | return { 17 | removeDownload, 18 | abortDownload 19 | }; 20 | } 21 | 22 | export default getDownloadsFeatures; 23 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/filesystem.js: -------------------------------------------------------------------------------- 1 | function getFilesystemFeatures({ 2 | dialogs, 3 | setZipFilesystem, 4 | setDialogs, 5 | zipService 6 | }) { 7 | function openConfirmReset() { 8 | setDialogs({ 9 | ...dialogs, 10 | reset: {} 11 | }); 12 | } 13 | 14 | function reset() { 15 | setZipFilesystem(zipService.createZipFileSystem()); 16 | } 17 | 18 | function closeConfirmReset() { 19 | setDialogs({ 20 | ...dialogs, 21 | reset: null 22 | }); 23 | } 24 | 25 | return { 26 | openConfirmReset, 27 | reset, 28 | closeConfirmReset 29 | }; 30 | } 31 | 32 | export default getFilesystemFeatures; 33 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/folders.js: -------------------------------------------------------------------------------- 1 | function getFoldersFeatures({ 2 | disabledBack, 3 | disabledForward, 4 | history, 5 | highlightedEntry, 6 | highlightedEntries, 7 | modifierKeyPressed, 8 | selectedFolder, 9 | setSelectedFolder, 10 | setEntries, 11 | setHistory, 12 | setHighlightedIds, 13 | setClickedButtonName, 14 | constants 15 | }) { 16 | const { 17 | LEFT_KEY, 18 | RIGHT_KEY, 19 | BACK_KEY, 20 | FORWARD_KEY, 21 | BACK_BUTTON_NAME, 22 | FORWARD_BUTTON_NAME 23 | } = constants; 24 | 25 | function goIntoFolder(entry) { 26 | const path = [...history.path]; 27 | const index = history.index + 1; 28 | if (entry.isDescendantOf(selectedFolder)) { 29 | path.length = index + 1; 30 | } 31 | path[index] = entry; 32 | setHistory({ 33 | index, 34 | path 35 | }); 36 | highlightEntry(selectedFolder, entry); 37 | setSelectedFolder(entry); 38 | refreshSelectedFolder(entry); 39 | } 40 | 41 | function navigateBack() { 42 | navigateHistory(-1); 43 | } 44 | 45 | function navigateForward() { 46 | navigateHistory(1); 47 | } 48 | 49 | function navigateHistory(offset) { 50 | const index = history.index + offset; 51 | setHistory({ 52 | ...history, 53 | index 54 | }); 55 | const entry = history.path[index]; 56 | highlightEntry(selectedFolder, entry); 57 | setSelectedFolder(entry); 58 | refreshSelectedFolder(entry); 59 | } 60 | 61 | function highlightEntry(selectedFolder, entry) { 62 | if ( 63 | selectedFolder.children.includes(entry) || 64 | entry.children.includes(selectedFolder) 65 | ) { 66 | setHighlightedIds([selectedFolder.id]); 67 | } else { 68 | const highlightedEntry = entry.children.find((child) => 69 | selectedFolder.isDescendantOf(child) 70 | ); 71 | setHighlightedIds( 72 | highlightedEntry ? [highlightedEntry.id] : [entry.parent.id] 73 | ); 74 | } 75 | } 76 | 77 | function updateHistoryData() { 78 | let offsetIndex = 0; 79 | let previousEntry; 80 | const path = history.path.filter((entry, indexEntry) => { 81 | const entryRemoved = 82 | previousEntry === entry || 83 | highlightedEntries.includes(entry) || 84 | highlightedEntries.find((highlightedEntry) => 85 | entry.isDescendantOf(highlightedEntry) 86 | ); 87 | if (entryRemoved) { 88 | if (indexEntry <= history.index) { 89 | offsetIndex++; 90 | } 91 | } else { 92 | previousEntry = entry; 93 | } 94 | return !entryRemoved; 95 | }); 96 | const index = history.index - offsetIndex; 97 | setHistory({ 98 | path, 99 | index 100 | }); 101 | } 102 | 103 | function refreshSelectedFolder(folder = selectedFolder) { 104 | if (folder) { 105 | const { parent, children } = folder; 106 | const folders = filterChildren(children, true); 107 | const files = filterChildren(children, false); 108 | const ancestors = []; 109 | if (parent) { 110 | ancestors.push(parent); 111 | } 112 | setEntries([...ancestors, ...folders, ...files]); 113 | } 114 | } 115 | 116 | function filterChildren(children, isDirectory) { 117 | return children 118 | .filter((child) => Boolean(child.directory) === isDirectory) 119 | .sort((previousChild, nextChild) => 120 | previousChild.name.localeCompare(nextChild.name) 121 | ); 122 | } 123 | 124 | function onFoldersKeyUp(event) { 125 | if (event.altKey) { 126 | if (event.key === BACK_KEY && !disabledBack) { 127 | setClickedButtonName(BACK_BUTTON_NAME); 128 | } 129 | if (event.key === FORWARD_KEY && !disabledForward) { 130 | setClickedButtonName(FORWARD_BUTTON_NAME); 131 | } 132 | } 133 | if (!event.altKey && !modifierKeyPressed(event) && !event.shiftKey) { 134 | if (event.key === LEFT_KEY && selectedFolder.parent) { 135 | goIntoFolder(selectedFolder.parent); 136 | } 137 | if ( 138 | event.key === RIGHT_KEY && 139 | highlightedEntry && 140 | highlightedEntry.directory 141 | ) { 142 | goIntoFolder(highlightedEntry); 143 | } 144 | } 145 | } 146 | 147 | return { 148 | goIntoFolder, 149 | navigateBack, 150 | navigateForward, 151 | refreshSelectedFolder, 152 | updateHistoryData, 153 | onFoldersKeyUp 154 | }; 155 | } 156 | 157 | export default getFoldersFeatures; 158 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/highlighted-entries.js: -------------------------------------------------------------------------------- 1 | function getHighlightedEntriesFeatures({ 2 | disabledCopy, 3 | disabledCut, 4 | disabledExtract, 5 | disabledRename, 6 | disabledDelete, 7 | zipFilesystem, 8 | entries, 9 | highlightedIds, 10 | highlightedEntry, 11 | highlightedEntries, 12 | navigation, 13 | dialogs, 14 | modifierKeyPressed, 15 | setClipboardData, 16 | setHighlightedIds, 17 | setNavigation, 18 | setDialogs, 19 | setClickedButtonName, 20 | refreshSelectedFolder, 21 | updateHistoryData, 22 | saveEntries, 23 | getOptions, 24 | openDisplayError, 25 | filesystemService, 26 | constants 27 | }) { 28 | const { 29 | CUT_KEY, 30 | COPY_KEY, 31 | EXTRACT_KEY, 32 | RENAME_KEY, 33 | CUT_BUTTON_NAME, 34 | COPY_BUTTON_NAME, 35 | EXTRACT_BUTTON_NAME, 36 | RENAME_BUTTON_NAME, 37 | DELETE_KEYS, 38 | DELETE_BUTTON_NAME 39 | } = constants; 40 | 41 | function copy() { 42 | setClipboardData({ 43 | entries: highlightedEntries.map((entry) => entry.clone(true)) 44 | }); 45 | } 46 | 47 | function cut() { 48 | setClipboardData({ 49 | entries: highlightedEntries, 50 | cut: true 51 | }); 52 | } 53 | 54 | function openPromptRename() { 55 | setDialogs({ 56 | ...dialogs, 57 | rename: { 58 | filename: highlightedEntry.name 59 | } 60 | }); 61 | } 62 | 63 | function rename({ filename }) { 64 | try { 65 | if (filename !== highlightedEntry.name) { 66 | highlightedEntry.rename(filename); 67 | refreshSelectedFolder(); 68 | } 69 | } catch (error) { 70 | openDisplayError(error.message); 71 | } 72 | } 73 | function closePromptRename() { 74 | setDialogs({ 75 | ...dialogs, 76 | rename: null 77 | }); 78 | } 79 | 80 | function openConfirmDeleteEntries() { 81 | setDialogs({ 82 | ...dialogs, 83 | deleteEntries: {} 84 | }); 85 | } 86 | 87 | function deleteEntries() { 88 | highlightedEntries.forEach((entry) => zipFilesystem.remove(entry)); 89 | if (entries.length) { 90 | const indexEntry = Math.max( 91 | ...entries 92 | .map((entry, index) => ({ entry, index })) 93 | .filter(({ entry }) => highlightedIds.includes(entry.id)) 94 | .map(({ index }) => index) 95 | ); 96 | let indexNextEntry = indexEntry; 97 | while ( 98 | indexNextEntry < entries.length && 99 | highlightedIds.includes(entries[indexNextEntry].id) 100 | ) { 101 | indexNextEntry++; 102 | } 103 | if (indexNextEntry === entries.length) { 104 | indexNextEntry = indexEntry; 105 | while ( 106 | indexNextEntry >= 0 && 107 | highlightedIds.includes(entries[indexNextEntry].id) 108 | ) { 109 | indexNextEntry--; 110 | } 111 | } 112 | if (entries[indexNextEntry]) { 113 | setNavigation({ 114 | ...navigation, 115 | previousHighlight: entries[indexNextEntry] 116 | }); 117 | setHighlightedIds([entries[indexNextEntry].id]); 118 | } else { 119 | setNavigation({ 120 | ...navigation, 121 | previousHighlight: null 122 | }); 123 | setHighlightedIds([]); 124 | } 125 | } 126 | updateHistoryData(); 127 | refreshSelectedFolder(); 128 | } 129 | 130 | function closeConfirmDeleteEntries() { 131 | setDialogs({ 132 | ...dialogs, 133 | deleteEntries: null 134 | }); 135 | } 136 | 137 | function openPromptExtract(entry = highlightedEntry) { 138 | const options = { 139 | entries: [entry], 140 | filename: entry.name 141 | }; 142 | if (filesystemService.savePickersSupported()) { 143 | extract(options); 144 | } else { 145 | setDialogs({ 146 | ...dialogs, 147 | extract: options 148 | }); 149 | } 150 | } 151 | 152 | function extract({ entries = highlightedEntries, filename } = {}) { 153 | async function download() { 154 | try { 155 | const options = getOptions(); 156 | filename = entries.length === 1 ? filename : null; 157 | await saveEntries(entries, filename, options); 158 | } catch (error) { 159 | openDisplayError(error.message); 160 | } 161 | } 162 | 163 | download(); 164 | } 165 | 166 | function closePromptExtract() { 167 | setDialogs({ 168 | ...dialogs, 169 | extract: null 170 | }); 171 | } 172 | 173 | function onHighlightedEntriesKeyUp(event) { 174 | if (!event.altKey && !modifierKeyPressed(event) && !event.shiftKey) { 175 | if (DELETE_KEYS.includes(event.key) && !disabledDelete) { 176 | setClickedButtonName(DELETE_BUTTON_NAME); 177 | } 178 | } 179 | } 180 | 181 | function onHighlightedEntriesKeyDown(event) { 182 | if (modifierKeyPressed(event)) { 183 | if (event.key === COPY_KEY && !disabledCopy) { 184 | setClickedButtonName(COPY_BUTTON_NAME); 185 | event.preventDefault(); 186 | } 187 | if (event.key === CUT_KEY && !disabledCut) { 188 | setClickedButtonName(CUT_BUTTON_NAME); 189 | event.preventDefault(); 190 | } 191 | if (event.key === EXTRACT_KEY && !disabledExtract) { 192 | setClickedButtonName(EXTRACT_BUTTON_NAME); 193 | event.preventDefault(); 194 | } 195 | if (event.key === RENAME_KEY && !disabledRename) { 196 | setClickedButtonName(RENAME_BUTTON_NAME); 197 | event.preventDefault(); 198 | } 199 | } 200 | } 201 | 202 | return { 203 | copy, 204 | cut, 205 | openPromptRename, 206 | rename, 207 | closePromptRename, 208 | openConfirmDeleteEntries, 209 | deleteEntries, 210 | closeConfirmDeleteEntries, 211 | openPromptExtract, 212 | extract, 213 | closePromptExtract, 214 | onHighlightedEntriesKeyUp, 215 | onHighlightedEntriesKeyDown 216 | }; 217 | } 218 | 219 | export default getHighlightedEntriesFeatures; 220 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/index.js: -------------------------------------------------------------------------------- 1 | import getCommonFeatures from "./common.js"; 2 | import getEntriesFeatures from "./entries.js"; 3 | import getFoldersFeatures from "./folders.js"; 4 | import getSelectedFolderFeatures from "./selected-folder.js"; 5 | import getHighlightedEntriesFeatures from "./highlighted-entries.js"; 6 | import getAppFeatures from "./app.js"; 7 | import getFilesystemFeatures from "./filesystem.js"; 8 | import getDownloadsFeatures from "./downloads.js"; 9 | import getClipboardFeatures from "./clipboard.js"; 10 | import getOptionsFeatures from "./options.js"; 11 | import getMiscFeatures from "./misc.js"; 12 | 13 | export { 14 | getCommonFeatures, 15 | getEntriesFeatures, 16 | getFoldersFeatures, 17 | getSelectedFolderFeatures, 18 | getHighlightedEntriesFeatures, 19 | getAppFeatures, 20 | getFilesystemFeatures, 21 | getDownloadsFeatures, 22 | getClipboardFeatures, 23 | getOptionsFeatures, 24 | getMiscFeatures 25 | }; 26 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/misc.js: -------------------------------------------------------------------------------- 1 | function getMiscFeatures({ 2 | theme, 3 | setOptions, 4 | setTheme, 5 | setMusicData, 6 | setPlayerActive, 7 | getOptions, 8 | stylesheetService, 9 | themeService, 10 | musicService, 11 | constants 12 | }) { 13 | const { ACCENT_COLOR_CUSTOM_PROPERTY_NAME } = themeService; 14 | 15 | function initMiscFeatures() { 16 | const options = getOptions(); 17 | const { accentColor, skin } = options; 18 | setTheme({ accentColor, skin }); 19 | } 20 | 21 | function playMusic() { 22 | setPlayerActive(true); 23 | updateSkin(); 24 | musicService.play({ 25 | onSetFrequencyData: (frequencyData) => { 26 | setMusicData(() => ({ 27 | frequencyData 28 | })); 29 | } 30 | }); 31 | } 32 | 33 | function updateSkin() { 34 | if (theme.skin) { 35 | musicService.setFftSize(constants.FFT_RESOLUTIONS[theme.skin]); 36 | } 37 | } 38 | 39 | function stopMusic() { 40 | setMusicData({ frequencyData: [] }); 41 | setPlayerActive(false); 42 | musicService.stop(); 43 | } 44 | 45 | function updateAccentColor() { 46 | const { accentColor, skin } = theme; 47 | if (accentColor) { 48 | stylesheetService.setStyle( 49 | ACCENT_COLOR_CUSTOM_PROPERTY_NAME, 50 | theme.accentColor 51 | ); 52 | themeService.setTheme({ accentColor, skin }); 53 | const options = getOptions(); 54 | setOptions({ ...options, accentColor }); 55 | } 56 | } 57 | 58 | return { 59 | initMiscFeatures, 60 | playMusic, 61 | stopMusic, 62 | updateAccentColor, 63 | updateSkin 64 | }; 65 | } 66 | 67 | export default getMiscFeatures; 68 | -------------------------------------------------------------------------------- /src/zip-manager/business/features/options.js: -------------------------------------------------------------------------------- 1 | function getOptionsFeatures({ 2 | dialogs, 3 | setDialogs, 4 | setTheme, 5 | zipService, 6 | storageService, 7 | stylesheetService, 8 | environmentService, 9 | themeService, 10 | constants 11 | }) { 12 | const { DEFAULT_OPTIONS, OPTIONS_KEY_NAME, FONT_SIZE_PROPERTY_NAME } = 13 | constants; 14 | 15 | function initOptionsFeatures() { 16 | applyOptions(getOptions()); 17 | } 18 | 19 | function applyOptions(options) { 20 | configureZipService(options); 21 | configureZoomFactor(options); 22 | configureTheme(options); 23 | } 24 | 25 | function openOptions() { 26 | setDialogs({ 27 | ...dialogs, 28 | options: getOptions() 29 | }); 30 | } 31 | 32 | function closeOptions() { 33 | setDialogs({ 34 | ...dialogs, 35 | options: null 36 | }); 37 | } 38 | 39 | function resetOptions() { 40 | const options = { ...DEFAULT_OPTIONS }; 41 | options.maxWorkers = environmentService.getMaximumWorkers(); 42 | setDialogs({ 43 | ...dialogs, 44 | options 45 | }); 46 | } 47 | 48 | function setOptions(options) { 49 | const previousOptions = getOptions(); 50 | options = { ...previousOptions, ...options }; 51 | applyOptions(options); 52 | storageService.set(OPTIONS_KEY_NAME, options); 53 | } 54 | 55 | function getOptions() { 56 | let options = storageService.get(OPTIONS_KEY_NAME); 57 | if (!options) { 58 | options = { ...DEFAULT_OPTIONS }; 59 | options.maxWorkers = environmentService.getMaximumWorkers(); 60 | } 61 | if (options.hideNavigationBar === undefined) { 62 | options.hideNavigationBar = DEFAULT_OPTIONS.hideNavigationBar; 63 | } 64 | if (options.hideDownloadManager === undefined) { 65 | options.hideDownloadManager = DEFAULT_OPTIONS.hideDownloadManager; 66 | } 67 | if (options.hideInfobar === undefined) { 68 | options.hideInfobar = DEFAULT_OPTIONS.hideInfobar; 69 | } 70 | if (options.promptForExportPassword === undefined) { 71 | options.promptForExportPassword = DEFAULT_OPTIONS.promptForExportPassword; 72 | } 73 | if (options.defaultExportPassword === undefined) { 74 | options.defaultExportPassword = DEFAULT_OPTIONS.defaultExportPassword; 75 | } 76 | if (options.checkSignature === undefined) { 77 | options.checkSignature = DEFAULT_OPTIONS.checkSignature; 78 | } 79 | if (options.accentColor === undefined) { 80 | options.accentColor = DEFAULT_OPTIONS.accentColor; 81 | } 82 | if (options.zoomFactor === undefined) { 83 | options.zoomFactor = DEFAULT_OPTIONS.zoomFactor; 84 | } 85 | if (options.skin === undefined) { 86 | options.skin = DEFAULT_OPTIONS.skin; 87 | } 88 | return options; 89 | } 90 | 91 | function configureZipService(options) { 92 | const { maxWorkers, chunkSize } = options; 93 | zipService.configure({ 94 | maxWorkers, 95 | chunkSize 96 | }); 97 | } 98 | 99 | function configureZoomFactor({ zoomFactor }) { 100 | stylesheetService.setStyle( 101 | FONT_SIZE_PROPERTY_NAME, 102 | zoomFactor / 100 + "rem" 103 | ); 104 | } 105 | 106 | function configureTheme({ accentColor, skin }) { 107 | setTheme({ accentColor, skin }); 108 | themeService.setTheme({ accentColor, skin }); 109 | } 110 | 111 | return { 112 | initOptionsFeatures, 113 | getOptions, 114 | setOptions, 115 | openOptions, 116 | closeOptions, 117 | resetOptions 118 | }; 119 | } 120 | 121 | export default getOptionsFeatures; 122 | -------------------------------------------------------------------------------- /src/zip-manager/business/index.js: -------------------------------------------------------------------------------- 1 | import * as constants from "./constants.js"; 2 | import getUIState from "./ui-state.js"; 3 | import getEventHandlers from "./events.js"; 4 | import * as features from "./features/index.js"; 5 | 6 | export { constants, features, getUIState, getEventHandlers }; 7 | -------------------------------------------------------------------------------- /src/zip-manager/business/ui-state.js: -------------------------------------------------------------------------------- 1 | function getUIState({ 2 | entries, 3 | highlightedIds, 4 | selectedFolder, 5 | clipboardData, 6 | history, 7 | dialogs, 8 | getOptions, 9 | filesystemService 10 | }) { 11 | const entriesEmpty = !entries.length; 12 | const parentFolderHighlighted = 13 | !highlightedIds.length || 14 | (selectedFolder.parent && 15 | highlightedIds.includes(selectedFolder.parent.id)); 16 | const subFolderHighlighted = highlightedIds.find((id) => { 17 | const entry = selectedFolder.children.find((entry) => entry.id === id); 18 | if (entry) { 19 | return entry.directory; 20 | } 21 | return false; 22 | }); 23 | const clipboardDataEmpty = !clipboardData; 24 | const selectedFolderEntries = entries.filter( 25 | (entry) => entry !== selectedFolder.parent 26 | ); 27 | const disabledExportZip = entriesEmpty || !selectedFolderEntries.length; 28 | const disabledReset = entriesEmpty; 29 | const disabledNavigation = entriesEmpty; 30 | const disabledBack = !history.index; 31 | const disabledForward = history.index === history.path.length - 1; 32 | const disabledCopy = parentFolderHighlighted; 33 | const disabledCut = parentFolderHighlighted; 34 | const disabledPaste = 35 | clipboardDataEmpty || 36 | (clipboardData.cut && 37 | clipboardData.entries.find( 38 | (entry) => 39 | selectedFolder == entry || selectedFolder.isDescendantOf(entry) 40 | )); 41 | const disabledResetClipboardData = clipboardDataEmpty; 42 | const disabledExtract = 43 | parentFolderHighlighted || 44 | !highlightedIds.length || 45 | (!filesystemService.savePickersSupported() && subFolderHighlighted); 46 | const disabledHighlightAll = 47 | !selectedFolder || 48 | !selectedFolderEntries.length || 49 | (selectedFolderEntries.length === highlightedIds.length && 50 | selectedFolderEntries.every((entry) => 51 | highlightedIds.includes(entry.id) 52 | )); 53 | const disabledRename = highlightedIds.length !== 1 || parentFolderHighlighted; 54 | const disabledDelete = parentFolderHighlighted; 55 | const disabledEnterEntry = highlightedIds.length !== 1; 56 | const dialogDisplayed = 57 | dialogs.exportZip || 58 | dialogs.extract || 59 | dialogs.rename || 60 | dialogs.createFolder || 61 | dialogs.deleteEntries || 62 | dialogs.reset || 63 | dialogs.displayError || 64 | dialogs.enterImportPassword || 65 | dialogs.chooseAction || 66 | dialogs.options; 67 | const options = getOptions(); 68 | const hiddenNavigationBar = options.hideNavigationBar; 69 | const hiddenDownloadManager = options.hideDownloadManager; 70 | const hiddenInfobar = options.hideInfobar; 71 | const hiddenExportPassword = !options.promptForExportPassword; 72 | const highlightedEntries = 73 | (selectedFolder && 74 | selectedFolder.children.filter((entry) => 75 | highlightedIds.includes(entry.id) 76 | )) || 77 | []; 78 | const highlightedEntry = 79 | highlightedIds.length === 1 && 80 | selectedFolder && 81 | selectedFolder.children.find((entry) => entry.id === highlightedIds[0]); 82 | const ancestorFolders = getAncestors(selectedFolder); 83 | 84 | return { 85 | disabledExportZip, 86 | disabledReset, 87 | disabledNavigation, 88 | disabledBack, 89 | disabledForward, 90 | disabledCopy, 91 | disabledCut, 92 | disabledPaste, 93 | disabledResetClipboardData, 94 | disabledExtract, 95 | disabledHighlightAll, 96 | disabledRename, 97 | disabledDelete, 98 | disabledEnterEntry, 99 | dialogDisplayed, 100 | hiddenNavigationBar, 101 | hiddenDownloadManager, 102 | hiddenInfobar, 103 | hiddenExportPassword, 104 | highlightedEntries, 105 | highlightedEntry, 106 | selectedFolderEntries, 107 | ancestorFolders 108 | }; 109 | } 110 | 111 | function getAncestors(folder) { 112 | const ancestors = []; 113 | while (folder?.parent) { 114 | ancestors.unshift(folder); 115 | folder = folder.parent; 116 | } 117 | if (folder) { 118 | ancestors.unshift(folder); 119 | } 120 | return ancestors; 121 | } 122 | 123 | export default getUIState; 124 | -------------------------------------------------------------------------------- /src/zip-manager/components/BottomButtonBar.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/BottomButtonBar.css"; 2 | 3 | import { useRef } from "react"; 4 | 5 | import { 6 | CopyEntryButton, 7 | CutEntryButton, 8 | PasteEntryButton, 9 | ResetClipboardDataButton, 10 | ExtractEntryButton, 11 | HighlightAllButton, 12 | RenameEntryButton, 13 | DeleteEntriesButton 14 | } from "./Buttons.jsx"; 15 | 16 | function BottomButtonBar({ 17 | disabledCopyButton, 18 | disabledCutButton, 19 | disabledPasteButton, 20 | disabledResetClipboardDataButton, 21 | disabledExtractButton, 22 | disabledHighlightAllButton, 23 | disabledRenameButton, 24 | disabledDeleteButton, 25 | clickedButtonName, 26 | onCopy, 27 | onCut, 28 | onPaste, 29 | onResetClipboardData, 30 | onExtract, 31 | onHighlightAll, 32 | onRename, 33 | onRemove, 34 | onMove, 35 | onUpdateElementHeight, 36 | onClickedButton, 37 | constants, 38 | messages 39 | }) { 40 | const previousTouchClientY = useRef(0); 41 | 42 | function handleTouchMove(event) { 43 | const { clientY } = event.changedTouches[0]; 44 | if (previousTouchClientY.current) { 45 | const deltaY = clientY - previousTouchClientY.current; 46 | onMove(deltaY); 47 | } else { 48 | previousTouchClientY.current = clientY; 49 | } 50 | } 51 | 52 | function handleTouchEnd() { 53 | previousTouchClientY.current = 0; 54 | onUpdateElementHeight(); 55 | } 56 | 57 | return ( 58 |
event.preventDefault()} 65 | > 66 |
67 | 75 | 83 | 91 | 99 |
100 |
101 | 109 | 117 | 125 | 130 |
131 |
132 | ); 133 | } 134 | 135 | export default BottomButtonBar; 136 | -------------------------------------------------------------------------------- /src/zip-manager/components/Breadcrumb.jsx: -------------------------------------------------------------------------------- 1 | function Breadcrumb({ 2 | folder: selectedFolder, 3 | ancestorFolders, 4 | onGoIntoFolder, 5 | constants, 6 | messages 7 | }) { 8 | return ( 9 | 28 | ); 29 | } 30 | 31 | function BreadcrumbItem({ 32 | disabled, 33 | folder, 34 | isSelectedFolder, 35 | onGoIntoFolder, 36 | constants, 37 | messages 38 | }) { 39 | function getBreadcrumbItemClassName() { 40 | const classes = ["breadcrumb-item"]; 41 | if (!disabled) { 42 | classes.push("breadcrumb-item-active"); 43 | } 44 | return classes.join(" "); 45 | } 46 | 47 | function handleClick() { 48 | if (!disabled) { 49 | onGoIntoFolder(folder); 50 | } 51 | } 52 | 53 | function handleKeyUp({ event, folder }) { 54 | if (event.key === constants.ENTER_KEY) { 55 | handleClick(folder); 56 | } 57 | } 58 | 59 | return ( 60 | handleKeyUp({ event, folder })} 67 | tabIndex={disabled ? null : 0} 68 | > 69 | {folder.parent ? folder.name : messages.ROOT_FOLDER_LABEL} 70 | 71 | ); 72 | } 73 | 74 | export default Breadcrumb; 75 | -------------------------------------------------------------------------------- /src/zip-manager/components/Buttons.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | function CopyEntryButton({ 4 | disabled, 5 | clickedButtonName, 6 | onCopy, 7 | onClickedButton, 8 | constants, 9 | messages 10 | }) { 11 | return ( 12 | 344 | ); 345 | } 346 | 347 | export { 348 | CopyEntryButton, 349 | CutEntryButton, 350 | PasteEntryButton, 351 | ResetClipboardDataButton, 352 | HighlightAllButton, 353 | ExtractEntryButton, 354 | RenameEntryButton, 355 | DeleteEntriesButton, 356 | CreateFolderButton, 357 | AddFilesButton, 358 | ImportZipButton, 359 | ExportZipButton, 360 | ResetButton, 361 | OptionsButton, 362 | BackButton, 363 | ForwardButton 364 | }; 365 | -------------------------------------------------------------------------------- /src/zip-manager/components/Downloads.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/Downloads.css"; 2 | 3 | function Downloads({ 4 | hidden, 5 | downloads, 6 | onAbortDownload, 7 | i18n, 8 | constants, 9 | messages 10 | }) { 11 | if (hidden) { 12 | return; 13 | } else { 14 | return ( 15 |
20 |
    21 | {downloads.queue.map((download) => ( 22 |
  1. 23 | 30 |
  2. 31 | ))} 32 |
33 |
34 | ); 35 | } 36 | } 37 | 38 | function DownloadEntry({ 39 | download, 40 | onAbortDownload, 41 | i18n, 42 | constants, 43 | messages 44 | }) { 45 | return ( 46 | <> 47 | 53 | 54 | 55 | ); 56 | } 57 | 58 | function DownloadEntryInfo({ download, onAbortDownload, constants, messages }) { 59 | return ( 60 |
61 | 62 | {download.name} 63 | 64 | 70 |
71 | ); 72 | } 73 | 74 | function DeleteDownloadEntryButton({ 75 | download, 76 | onAbortDownload, 77 | constants, 78 | messages 79 | }) { 80 | function handleClick() { 81 | onAbortDownload(download); 82 | } 83 | 84 | function handleKeyUp(event) { 85 | if (event.key === constants.ENTER_KEY) { 86 | handleClick(); 87 | } 88 | } 89 | 90 | return ( 91 | 98 | {messages.ABORT_DOWNLOAD_BUTTON_LABEL} 99 | 100 | ); 101 | } 102 | 103 | function DownloadEntryProgress({ download, i18n }) { 104 | return ( 105 | 117 | ); 118 | } 119 | 120 | export default Downloads; 121 | -------------------------------------------------------------------------------- /src/zip-manager/components/History.jsx: -------------------------------------------------------------------------------- 1 | import { BackButton, ForwardButton } from "./Buttons.jsx"; 2 | 3 | function History({ 4 | disabledBackButton, 5 | disabledForwardButton, 6 | clickedButtonName, 7 | onNavigateBack, 8 | onNavigateForward, 9 | onClickedButton, 10 | constants, 11 | messages 12 | }) { 13 | return ( 14 | 15 | 23 | 31 | 32 | ); 33 | } 34 | 35 | export default History; 36 | -------------------------------------------------------------------------------- /src/zip-manager/components/InfoBar.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/InfoBar.css"; 2 | 3 | import { useEffect, useRef, useState } from "react"; 4 | 5 | function InfoBar({ 6 | hidden, 7 | theme, 8 | musicData, 9 | playerActive, 10 | onPlayMusic, 11 | onStopMusic, 12 | onSetTheme, 13 | constants, 14 | messages 15 | }) { 16 | const PLAYER_ICON_CLASSNAME = "icon icon-music-player"; 17 | const PLAYER_PAUSED_CLASSNAME = " paused"; 18 | const PLAYER_PAUSED = { 19 | label: messages.PAUSED_MUSIC_ICON, 20 | className: PLAYER_ICON_CLASSNAME + PLAYER_PAUSED_CLASSNAME 21 | }; 22 | const PLAYER_ACTIVE = { 23 | label: messages.PLAYING_MUSIC_ICON, 24 | className: PLAYER_ICON_CLASSNAME 25 | }; 26 | 27 | const [iconPlayer, setIconPlayer] = useState(PLAYER_PAUSED); 28 | 29 | function handleChangeAccentColor(accentColor) { 30 | onSetTheme({ accentColor }); 31 | } 32 | 33 | function handleChangeIconPlayer(paused) { 34 | const iconData = paused ? PLAYER_PAUSED : PLAYER_ACTIVE; 35 | setIconPlayer(iconData); 36 | } 37 | 38 | if (hidden) { 39 | return; 40 | } else { 41 | return ( 42 | 112 | ); 113 | } 114 | } 115 | 116 | function AccentColorPickerButton({ 117 | accentColor, 118 | onSetAccentColor, 119 | children, 120 | messages 121 | }) { 122 | const colorInputRef = useRef(null); 123 | 124 | function handleChange() { 125 | onSetAccentColor(colorInputRef.current.value); 126 | } 127 | 128 | useEffect(() => { 129 | if (accentColor) { 130 | colorInputRef.current.value = accentColor; 131 | } 132 | }, [accentColor]); 133 | return ( 134 | <> 135 | {children} 136 | 143 | 144 | ); 145 | } 146 | 147 | function MusicPlayerButton({ 148 | playerActive, 149 | iconPlayer, 150 | onPlayMusic, 151 | onStopMusic, 152 | onSetIconPlayer 153 | }) { 154 | function handlePlayButtonClick() { 155 | if (playerActive) { 156 | onStopMusic(); 157 | onSetIconPlayer(true); 158 | } else { 159 | onPlayMusic(); 160 | onSetIconPlayer(); 161 | } 162 | } 163 | 164 | return ( 165 | <> 166 | 171 | {iconPlayer.label} 172 | 173 | 174 | ); 175 | } 176 | 177 | function MusicVisualizer({ 178 | theme, 179 | musicData, 180 | playerActive, 181 | constants 182 | }) { 183 | const CANVAS_WIDTH = 128; 184 | const CANVAS_HEIGTH = 64; 185 | const CANVAS_BLOCK_OFFSET = CANVAS_HEIGTH / 8; 186 | const MAX_FFT_VALUE = 256; 187 | 188 | const [barWidth, setBarWidth] = useState(0); 189 | const canvasRef = useRef(null); 190 | const audioContextRef = useRef(null); 191 | 192 | function updateColor() { 193 | if (theme.accentColor) { 194 | const context = audioContextRef.current; 195 | const gradient = context.createLinearGradient(0, 0, 0, CANVAS_HEIGTH); 196 | gradient.addColorStop(0, theme.accentColor); 197 | gradient.addColorStop(0.7, theme.accentColor); 198 | gradient.addColorStop(1, "transparent"); 199 | context.fillStyle = gradient; 200 | } 201 | } 202 | 203 | function updateBarWidth() { 204 | if (theme.skin) { 205 | setBarWidth(CANVAS_WIDTH / (constants.FFT_RESOLUTIONS[theme.skin] / 2)); 206 | } 207 | } 208 | 209 | useEffect(updateBarWidth, [theme.skin]); 210 | useEffect(updateColor, [theme.accentColor]); 211 | useEffect(() => { 212 | if (canvasRef.current) { 213 | if (!audioContextRef.current) { 214 | audioContextRef.current = canvasRef.current.getContext("2d"); 215 | } 216 | const context = audioContextRef.current; 217 | context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGTH); 218 | if (playerActive) { 219 | musicData.frequencyData.forEach((byteTimeDomain, index) => { 220 | const barHeight = 221 | (CANVAS_BLOCK_OFFSET - byteTimeDomain) / 222 | (MAX_FFT_VALUE / CANVAS_HEIGTH); 223 | context.fillRect(index * barWidth, CANVAS_HEIGTH, barWidth, barHeight); 224 | context.fillRect( 225 | CANVAS_WIDTH - index * barWidth - barWidth, 226 | CANVAS_HEIGTH, 227 | barWidth, 228 | barHeight 229 | ); 230 | }); 231 | } 232 | } 233 | }, [playerActive, musicData]); 234 | return ( 235 | 240 | ); 241 | } 242 | 243 | export default InfoBar; 244 | -------------------------------------------------------------------------------- /src/zip-manager/components/NavigationBar.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/NavigationBar.css"; 2 | 3 | import History from "./History.jsx"; 4 | import Breadcrumb from "./Breadcrumb.jsx"; 5 | 6 | function NavigationBar({ 7 | hidden, 8 | selectedFolder, 9 | ancestorFolders, 10 | disabledBackButton, 11 | disabledForwardButton, 12 | clickedButtonName, 13 | onNavigateBack, 14 | onNavigateForward, 15 | onGoIntoFolder, 16 | onClickedButton, 17 | constants, 18 | messages 19 | }) { 20 | if (hidden) { 21 | return; 22 | } else { 23 | return ( 24 |
29 | 39 | 46 |
47 | ); 48 | } 49 | } 50 | 51 | export default NavigationBar; 52 | -------------------------------------------------------------------------------- /src/zip-manager/components/TopButtonBar.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/TopButtonBar.css"; 2 | 3 | import { 4 | AddFilesButton, 5 | CreateFolderButton, 6 | ImportZipButton, 7 | ExportZipButton, 8 | ResetButton, 9 | OptionsButton 10 | } from "./Buttons.jsx"; 11 | 12 | function TopButtonBar({ 13 | disabledExportZipButton, 14 | disabledResetButton, 15 | clickedButtonName, 16 | onCreateFolder, 17 | onImportZipFile, 18 | onExportZip, 19 | onReset, 20 | onOpenOptions, 21 | onShowImportZipFilePicker, 22 | onShowAddFilesPicker, 23 | onClickedButton, 24 | constants, 25 | messages 26 | }) { 27 | function handleDragOver(event) { 28 | event.preventDefault(); 29 | } 30 | 31 | function handleDrop(event) { 32 | const file = event.dataTransfer?.files[0]; 33 | if (file) { 34 | event.preventDefault(); 35 | onImportZipFile(file); 36 | } 37 | } 38 | 39 | return ( 40 |
47 |
48 | 55 | 62 |
63 |
64 | 71 | 79 |
80 |
81 | 86 | 87 |
88 |
89 | ); 90 | } 91 | 92 | export default TopButtonBar; 93 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ChooseActionDialog.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | function ChooseActionDialog({ 4 | data, 5 | onImportZipFile, 6 | onAddFiles, 7 | onClose, 8 | messages 9 | }) { 10 | const dialogRef = useRef(null); 11 | 12 | function handleClose() { 13 | onClose(); 14 | } 15 | 16 | function handleImportZipClick() { 17 | onImportZipFile(data?.files[0]); 18 | } 19 | 20 | function handleAddFileClick() { 21 | onAddFiles(data?.files); 22 | } 23 | 24 | function handleReset() { 25 | dialogRef.current.close(); 26 | } 27 | 28 | useEffect(() => { 29 | if (!dialogRef.current.open && data) { 30 | dialogRef.current.showModal(); 31 | } 32 | }, [data]); 33 | return ( 34 | 35 |
36 |
37 |
38 | 39 |
40 |

{messages.CHOOSE_ACTION_LABEL}

41 |
42 |
43 |
44 | 47 | 50 | 53 |
54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | 61 | export default ChooseActionDialog; 62 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/CreateFolderDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | import { useState } from "react"; 4 | 5 | function CreateFolderDialog({ data, onCreateFolder, onClose, messages }) { 6 | const [folderName, setFolderName] = useState(""); 7 | 8 | function handleChangeFilename(event) { 9 | setFolderName(event.target.value); 10 | } 11 | 12 | function handleSubmit() { 13 | onCreateFolder({ folderName }); 14 | } 15 | 16 | function handleClose() { 17 | setFolderName(""); 18 | onClose(); 19 | } 20 | 21 | return ( 22 | 30 | 40 | 41 | ); 42 | } 43 | 44 | export default CreateFolderDialog; 45 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/DeleteEntriesDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | function DeleteEntriesDialog({ data, onDeleteEntries, onClose, messages }) { 4 | return ( 5 | 13 | {messages.DELETE_ENTRIES_MESSAGE} 14 | 15 | ); 16 | } 17 | 18 | export default DeleteEntriesDialog; 19 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/Dialog.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | function Dialog({ 4 | className, 5 | data, 6 | title, 7 | resetLabel, 8 | cancelLabel, 9 | submitLabel, 10 | children, 11 | onOpen, 12 | onSubmit, 13 | onReset, 14 | onClose 15 | }) { 16 | const dialogRef = useRef(null); 17 | 18 | function handleButtonReset(event) { 19 | event.preventDefault(); 20 | onReset(); 21 | } 22 | 23 | function handleReset() { 24 | dialogRef.current.close(); 25 | } 26 | 27 | useEffect(() => { 28 | if (!dialogRef.current.open && data) { 29 | if (onOpen) { 30 | onOpen(); 31 | } 32 | dialogRef.current.showModal(); 33 | } 34 | }, [data]); 35 | return ( 36 | 37 |
38 |
{title}
39 |

{children}

40 |
41 |
42 | {resetLabel && ( 43 | 46 | )} 47 |
48 |
49 | {cancelLabel && } 50 | 51 |
52 |
53 |
54 |
55 | ); 56 | } 57 | 58 | export default Dialog; 59 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ErrorMessageDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | function ErrorMessageDialog({ data, onClose, messages }) { 4 | return ( 5 | 11 | {data?.message} 12 | 13 | ); 14 | } 15 | 16 | export default ErrorMessageDialog; 17 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ExportZipDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | import { useEffect, useRef, useState } from "react"; 4 | 5 | function ExportZipDialog({ 6 | data, 7 | hiddenPassword, 8 | onExportZip, 9 | onClose, 10 | messages 11 | }) { 12 | const filenameInputRef = useRef(null); 13 | const filenameTextSelected = useRef(false); 14 | const [filename, setFilename] = useState(""); 15 | const [password, setPassword] = useState(""); 16 | const filenameHidden = data?.filenameHidden; 17 | 18 | function handleChangeFilename(event) { 19 | setFilename(event.target.value); 20 | } 21 | 22 | function handleChangePassword(event) { 23 | setPassword(event.target.value); 24 | } 25 | 26 | function onOpen() { 27 | const { filename, password } = data; 28 | setFilename(filename); 29 | setPassword(password); 30 | } 31 | 32 | function handleSubmit() { 33 | onExportZip({ filename, password }); 34 | } 35 | 36 | function handleClose() { 37 | filenameTextSelected.current = false; 38 | onClose(); 39 | } 40 | 41 | useEffect(() => { 42 | if ( 43 | !filenameTextSelected.current && 44 | filename && 45 | filenameInputRef && 46 | filenameInputRef.current 47 | ) { 48 | filenameTextSelected.current = true; 49 | let selectionEnd = filename.lastIndexOf("."); 50 | filenameInputRef.current.setSelectionRange( 51 | 0, 52 | selectionEnd === -1 ? filename.length : selectionEnd 53 | ); 54 | } 55 | }, [filename]); 56 | return ( 57 | 66 | 79 | {hiddenPassword || ( 80 | 89 | )} 90 | 91 | ); 92 | } 93 | 94 | export default ExportZipDialog; 95 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ExtractDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | import { useEffect, useRef, useState } from "react"; 4 | 5 | function ExtractDialog({ data, onExtract, onClose, messages }) { 6 | const filenameInputRef = useRef(null); 7 | const filenameTextSelected = useRef(false); 8 | const [filename, setFilename] = useState(""); 9 | 10 | function handleChangeFilename(event) { 11 | setFilename(event.target.value); 12 | } 13 | 14 | function onOpen() { 15 | setFilename(data?.filename); 16 | } 17 | 18 | function handleSubmit() { 19 | onExtract({ filename, entries: data?.entries }); 20 | } 21 | 22 | function handleClose() { 23 | setFilename(""); 24 | filenameTextSelected.current = false; 25 | onClose(); 26 | } 27 | 28 | useEffect(() => { 29 | if (!filenameTextSelected.current && filename) { 30 | filenameTextSelected.current = true; 31 | filenameInputRef.current.select(); 32 | } 33 | }, [filename]); 34 | return ( 35 | 44 | 55 | 56 | ); 57 | } 58 | 59 | export default ExtractDialog; 60 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ImportPasswordDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | import { useState } from "react"; 4 | 5 | function PasswordDialog({ data, onClose, messages }) { 6 | const [password, setPassword] = useState(""); 7 | 8 | function handleChangePassword(event) { 9 | setPassword(event.target.value); 10 | } 11 | 12 | function handleClose() { 13 | setPassword(""); 14 | data.onSetImportPassword({ password }); 15 | onClose(); 16 | } 17 | 18 | return ( 19 | 26 | 36 | 37 | ); 38 | } 39 | 40 | export default PasswordDialog; 41 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/OptionsDialog.jsx: -------------------------------------------------------------------------------- 1 | import "./styles/OptionsDialog.css"; 2 | import Dialog from "./Dialog.jsx"; 3 | 4 | import { useEffect, useRef, useState } from "react"; 5 | import { constants } from "../../business"; 6 | 7 | function OptionsDialog({ 8 | data, 9 | onSetOptions, 10 | onResetOptions, 11 | onClose, 12 | messages 13 | }) { 14 | const [zoomFactor, setZoomFactor] = useState(""); 15 | const [hideNavigationBar, setHideNavigationBar] = useState(false); 16 | const [hideDownloadManager, setHideDownloadManager] = useState(false); 17 | const [hideInfobar, setHideInfobar] = useState(false); 18 | const [skin, setSkin] = useState("default"); 19 | const [defaultExportPassword, setDefaultExportPassword] = useState(""); 20 | const [promptForExportPassword, setPromptForExportPassword] = useState(false); 21 | const [keepOrder, setKeepOrder] = useState(false); 22 | const [checkSignature, setCheckSignature] = useState(false); 23 | const [bufferedWrite, setBufferedWrite] = useState(false); 24 | const [maxWorkers, setMaxWorkers] = useState("0"); 25 | const [chunkSize, setChunkSize] = useState("0"); 26 | const defaultPasswordInputRef = useRef(null); 27 | 28 | function handleChangeZoomFactor(event) { 29 | setZoomFactor(event.target.value); 30 | } 31 | 32 | function handleChangeHideNavigationBar(event) { 33 | setHideNavigationBar(event.target.checked); 34 | } 35 | 36 | function handleChangeHideDownloadManager(event) { 37 | setHideDownloadManager(event.target.checked); 38 | } 39 | 40 | function handleChangeHideInfobar(event) { 41 | setHideInfobar(event.target.checked); 42 | } 43 | 44 | function handleChangeSkin(event) { 45 | setSkin(event.target.value); 46 | } 47 | 48 | function handleChangePromptForExportPassword(event) { 49 | setPromptForExportPassword(event.target.checked); 50 | } 51 | 52 | function handleFocusDefaultExportPassword() { 53 | defaultPasswordInputRef.current.select(); 54 | } 55 | 56 | function handleChangeDefaultExportPassword(event) { 57 | setDefaultExportPassword(event.target.value); 58 | } 59 | 60 | function handleChangeKeepOrder(event) { 61 | setKeepOrder(event.target.checked); 62 | } 63 | 64 | function handleChangeBufferedWrite(event) { 65 | setBufferedWrite(event.target.checked); 66 | } 67 | 68 | function handleChangeCheckSignature(event) { 69 | setCheckSignature(event.target.checked); 70 | } 71 | 72 | function handleChangeMaxWorkers(event) { 73 | setMaxWorkers(event.target.value); 74 | } 75 | 76 | function handleChangeChunkSize(event) { 77 | setChunkSize(event.target.value); 78 | } 79 | 80 | function handleSubmit() { 81 | onSetOptions({ 82 | zoomFactor: Number(zoomFactor), 83 | hideNavigationBar, 84 | hideDownloadManager, 85 | hideInfobar, 86 | skin, 87 | promptForExportPassword, 88 | defaultExportPassword, 89 | keepOrder, 90 | checkSignature, 91 | bufferedWrite, 92 | maxWorkers: Number(maxWorkers), 93 | chunkSize: Number(chunkSize) * 1024 94 | }); 95 | } 96 | 97 | function updateData() { 98 | if (data) { 99 | const { 100 | zoomFactor, 101 | hideNavigationBar, 102 | hideDownloadManager, 103 | hideInfobar, 104 | skin, 105 | promptForExportPassword, 106 | defaultExportPassword, 107 | keepOrder, 108 | checkSignature, 109 | bufferedWrite, 110 | maxWorkers, 111 | chunkSize 112 | } = data; 113 | setZoomFactor(zoomFactor); 114 | setHideNavigationBar(hideNavigationBar); 115 | setHideDownloadManager(hideDownloadManager); 116 | setHideInfobar(hideInfobar); 117 | setSkin(skin); 118 | setPromptForExportPassword(promptForExportPassword); 119 | setDefaultExportPassword(defaultExportPassword); 120 | setKeepOrder(keepOrder); 121 | setCheckSignature(checkSignature); 122 | setBufferedWrite(bufferedWrite); 123 | setMaxWorkers(maxWorkers); 124 | setChunkSize(chunkSize / 1024); 125 | } 126 | } 127 | 128 | useEffect(updateData, [data]); 129 | return ( 130 | 142 | 154 | 162 | 170 | 178 | 189 | 197 | 208 | 216 | 224 | 235 | 243 | 253 | 254 | ); 255 | } 256 | 257 | export default OptionsDialog; 258 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/RenameDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | import { useEffect, useRef, useState } from "react"; 4 | 5 | function RenameDialog({ data, onRename, onClose, messages }) { 6 | const filenameInputRef = useRef(null); 7 | const filenameTextSelected = useRef(false); 8 | const [filename, setFilename] = useState(""); 9 | 10 | function handleChangeFilename(event) { 11 | setFilename(event.target.value); 12 | } 13 | 14 | function onOpen() { 15 | setFilename(data?.filename); 16 | } 17 | 18 | function handleSubmit() { 19 | onRename({ filename }); 20 | } 21 | 22 | function handleClose() { 23 | setFilename(""); 24 | filenameTextSelected.current = false; 25 | onClose(); 26 | } 27 | 28 | useEffect(() => { 29 | if (!filenameTextSelected.current && filename) { 30 | filenameTextSelected.current = true; 31 | filenameInputRef.current.select(); 32 | } 33 | }, [filename]); 34 | return ( 35 | 44 | 55 | 56 | ); 57 | } 58 | 59 | export default RenameDialog; 60 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/ResetDialog.jsx: -------------------------------------------------------------------------------- 1 | import Dialog from "./Dialog.jsx"; 2 | 3 | function ResetDialog({ data, onReset, onClose, messages }) { 4 | return ( 5 | 13 | {messages.RESET_MESSAGE} 14 | 15 | ); 16 | } 17 | 18 | export default ResetDialog; 19 | -------------------------------------------------------------------------------- /src/zip-manager/components/dialogs/styles/OptionsDialog.css: -------------------------------------------------------------------------------- 1 | .options-dialog form p { 2 | padding-inline: var(--small-gap-inline-size); 3 | padding-block-start: var(--large-gap-block-size); 4 | } 5 | 6 | .options-dialog label { 7 | flex-direction: row; 8 | align-items: center; 9 | justify-content: space-between; 10 | min-block-size: 1.5em; 11 | } 12 | 13 | .options-dialog label span { 14 | flex: 1; 15 | } 16 | 17 | .options-dialog input, 18 | .options-dialog select { 19 | margin-inline-start: var(--gap-inline-size); 20 | } 21 | 22 | .options-dialog input[disabled] { 23 | color: var(--disabled-button-color); 24 | } 25 | 26 | .options-dialog input[type="checkbox"] { 27 | inline-size: 2ch; 28 | block-size: 1em; 29 | accent-color: var(--accent-color); 30 | margin-inline: calc(var(--outline-width)); 31 | } 32 | 33 | .options-dialog input[type="password"], 34 | .options-dialog input[type="number"], 35 | .options-dialog select { 36 | inline-size: min(10ch, 100%); 37 | } -------------------------------------------------------------------------------- /src/zip-manager/components/index.jsx: -------------------------------------------------------------------------------- 1 | import TopButtonBar from "./TopButtonBar.jsx"; 2 | import NavigationBar from "./NavigationBar.jsx"; 3 | import Entries from "./Entries.jsx"; 4 | import BottomButtonBar from "./BottomButtonBar.jsx"; 5 | import Downloads from "./Downloads.jsx"; 6 | import InfoBar from "./InfoBar.jsx"; 7 | import ExportZipDialog from "./dialogs/ExportZipDialog.jsx"; 8 | import ExtractDialog from "./dialogs/ExtractDialog.jsx"; 9 | import RenameDialog from "./dialogs/RenameDialog.jsx"; 10 | import CreateFolderDialog from "./dialogs/CreateFolderDialog.jsx"; 11 | import ResetDialog from "./dialogs/ResetDialog.jsx"; 12 | import DeleteEntriesDialog from "./dialogs/DeleteEntriesDialog.jsx"; 13 | import ErrorMessageDialog from "./dialogs/ErrorMessageDialog.jsx"; 14 | import ImportPasswordDialog from "./dialogs/ImportPasswordDialog.jsx"; 15 | import OptionsDialog from "./dialogs/OptionsDialog.jsx"; 16 | import ChooseActionDialog from "./dialogs/ChooseActionDialog.jsx"; 17 | 18 | export { 19 | TopButtonBar, 20 | NavigationBar, 21 | Entries, 22 | BottomButtonBar, 23 | Downloads, 24 | InfoBar, 25 | ExportZipDialog, 26 | ExtractDialog, 27 | RenameDialog, 28 | CreateFolderDialog, 29 | ResetDialog, 30 | DeleteEntriesDialog, 31 | ErrorMessageDialog, 32 | ImportPasswordDialog, 33 | OptionsDialog, 34 | ChooseActionDialog 35 | }; 36 | -------------------------------------------------------------------------------- /src/zip-manager/components/styles/BottomButtonBar.css: -------------------------------------------------------------------------------- 1 | .button-bar-bottom { 2 | justify-content: flex-end; 3 | touch-action: none; 4 | } -------------------------------------------------------------------------------- /src/zip-manager/components/styles/Downloads.css: -------------------------------------------------------------------------------- 1 | .downloads { 2 | min-block-size: 5dvh; 3 | overflow-block: auto; 4 | flex: 1; 5 | } 6 | 7 | .downloads ol, 8 | .downloads li { 9 | flex-direction: column; 10 | } 11 | 12 | .downloads ol { 13 | row-gap: var(--gap-block-size); 14 | } 15 | 16 | .downloads li { 17 | row-gap: var(--small-gap-block-size); 18 | align-items: normal; 19 | } 20 | 21 | .downloads .download-entry { 22 | display: flex; 23 | } 24 | 25 | .downloads .download-entry-name { 26 | flex: 1; 27 | } 28 | 29 | .downloads li progress { 30 | inline-size: 100%; 31 | block-size: 1em; 32 | accent-color: var(--accent-color); 33 | } -------------------------------------------------------------------------------- /src/zip-manager/components/styles/Entries.css: -------------------------------------------------------------------------------- 1 | .entries { 2 | block-size: 55dvh; 3 | min-block-size: 10dvh; 4 | resize: vertical; 5 | box-sizing: border-box; 6 | padding-block-start: 0; 7 | margin-block-start: var(--gap-block-size); 8 | } 9 | 10 | .entries ol { 11 | flex-direction: column; 12 | row-gap: var(--list-item-gap-block-size); 13 | } 14 | 15 | .entries li { 16 | padding-block: var(--list-item-padding-block-size); 17 | padding-inline: var(--list-item-padding-inline-size); 18 | outline: none; 19 | column-gap: var(--gap-inline-size); 20 | cursor: pointer; 21 | } 22 | 23 | @supports (grid-template-columns: subgrid) { 24 | .entries ol { 25 | display: grid; 26 | grid-template-columns: auto min-content min-content; 27 | grid-template-rows: repeat(auto); 28 | } 29 | 30 | .entries li { 31 | display: grid; 32 | grid-template-columns: subgrid; 33 | grid-column: 1 / 4; 34 | } 35 | } 36 | 37 | .entries .entry-select { 38 | accent-color: var(--dark-accent-color); 39 | margin: 0; 40 | padding: 0; 41 | max-block-size: 1em; 42 | margin-inline-start: var(--small-gap-inline-size); 43 | } 44 | 45 | .entries .entry-name { 46 | display: flex; 47 | user-select: none; 48 | overflow-x: hidden; 49 | overflow-inline: hidden; 50 | column-gap: var(--small-gap-inline-size); 51 | flex: 1; 52 | -webkit-tap-highlight-color: transparent; 53 | } 54 | 55 | .entries .entry-size { 56 | text-align: right; 57 | white-space: nowrap; 58 | } 59 | 60 | .entries .entry-highlighted { 61 | background-color: var(--accent-color); 62 | color: var(--highlighted-text-color); 63 | } 64 | 65 | .entries .entry-highlighted ::selection { 66 | background-color: var(--text-color); 67 | color: var(--bg-color); 68 | } 69 | 70 | .entries .directory .entry-name::after { 71 | content: var(--folder-separator); 72 | } 73 | 74 | @media (hover: hover) and (pointer: fine) { 75 | .entries ol { 76 | --no-entries-outline-width: 1px; 77 | --no-entries-outline-width-negative: calc(var(--no-entries-outline-width) * -1); 78 | } 79 | 80 | .entries ol:empty { 81 | block-size: 100%; 82 | } 83 | 84 | .entries ol:empty::after { 85 | display: flex; 86 | block-size: 100%; 87 | justify-content: center; 88 | align-items: center; 89 | text-align: center; 90 | font-weight: bold; 91 | font-size: 2em; 92 | line-height: 1.5em; 93 | text-shadow: var(--no-entries-outline-width-negative) var(--no-entries-outline-width-negative) 0 var(--accent-color), 94 | var(--no-entries-outline-width-negative) 0 0 var(--accent-color), 95 | var(--no-entries-outline-width-negative) var(--no-entries-outline-width) 0 var(--accent-color), 96 | 0 var(--no-entries-outline-width-negative) 0 var(--accent-color), 97 | 0 0 0 var(--accent-color), 98 | 0 var(--no-entries-outline-width) 0 var(--accent-color), 99 | var(--no-entries-outline-width) var(--no-entries-outline-width-negative) 0 var(--accent-color), 100 | var(--no-entries-outline-width) 0 0 var(--accent-color), 101 | var(--no-entries-outline-width) var(--no-entries-outline-width) 0 var(--accent-color); 102 | color: var(--bg-color); 103 | opacity: 0.5; 104 | } 105 | 106 | .entries.dragging-items ol:empty::after { 107 | color: var(--accent-color); 108 | text-shadow: none; 109 | } 110 | 111 | .entries ol:empty::after { 112 | content: var(--message-drag-and-drop-entries); 113 | } 114 | } -------------------------------------------------------------------------------- /src/zip-manager/components/styles/InfoBar.css: -------------------------------------------------------------------------------- 1 | .info-bar { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | padding-block: var(--gap-block-size); 6 | margin-inline: var(--gap-inline-size); 7 | inline-size: 100%; 8 | overflow: hidden; 9 | } 10 | 11 | .info-bar .source-link { 12 | display: inline; 13 | color: var(--basic-text-color); 14 | text-align: center; 15 | position: relative; 16 | overflow: visible; 17 | } 18 | 19 | .info-bar .source-link * { 20 | font-size: 0.8rem; 21 | } 22 | 23 | .info-bar .source-link a { 24 | text-decoration: none; 25 | } 26 | 27 | .info-bar .source-link a svg { 28 | width: 1em; 29 | height: 1em; 30 | margin-bottom: -2px; 31 | } 32 | 33 | .info-bar .player-active :not(canvas) { 34 | opacity: 0.5; 35 | color: var(--basic-text-color); 36 | transition: all 0.2s; 37 | } 38 | 39 | .info-bar .player-active .label { 40 | opacity: 0; 41 | pointer-events: none; 42 | } 43 | 44 | .info-bar:hover .source-link.player-active canvas { 45 | opacity: 0.2; 46 | } 47 | 48 | .info-bar:hover .player-active * { 49 | opacity: 1; 50 | pointer-events: inherit; 51 | } 52 | 53 | .info-bar:hover .source-link.player-active .icon { 54 | color: var(--accent-color); 55 | opacity: 1; 56 | } 57 | 58 | .info-bar .icon { 59 | display: inline-block; 60 | color: var(--accent-color); 61 | inline-size: 1.75ch; 62 | user-select: none; 63 | } 64 | 65 | .info-bar .icon-music-player { 66 | inline-size: 2.25ch; 67 | } 68 | 69 | .info-bar input[type="color"], 70 | .info-bar .icon-music-player { 71 | cursor: pointer; 72 | outline-width: 0; 73 | } 74 | 75 | .icon-music-player.paused { 76 | color: inherit; 77 | } 78 | 79 | .info-bar .icon:hover { 80 | filter: brightness(var(--brightness-hover)); 81 | } 82 | 83 | .info-bar input[type="color"] { 84 | position: relative; 85 | left: -1.75ch; 86 | margin-right: -1.8ch; 87 | inline-size: 1.8ch; 88 | block-size: 1.25em; 89 | padding: 0; 90 | filter: opacity(0); 91 | } 92 | 93 | .info-bar canvas { 94 | position: absolute; 95 | bottom: calc(-1 * var(--gap-block-size)); 96 | left: -7.5%; 97 | z-index: -1; 98 | inline-size: 115%; 99 | block-size: calc(100% + var(--gap-block-size)); 100 | opacity: 0.4; 101 | } -------------------------------------------------------------------------------- /src/zip-manager/components/styles/NavigationBar.css: -------------------------------------------------------------------------------- 1 | .navigation-bar, 2 | .history-buttons, 3 | .breadcrumb { 4 | display: flex; 5 | } 6 | 7 | .navigation-bar { 8 | column-gap: var(--gap-inline-size); 9 | } 10 | 11 | .history-buttons, 12 | .breadcrumb li { 13 | column-gap: var(--small-gap-inline-size); 14 | } 15 | 16 | .history-buttons button { 17 | font-family: monospace; 18 | align-self: start; 19 | padding-block: var(--small-button-padding-block-size); 20 | padding-inline: var(--small-button-padding-inline-size); 21 | } 22 | 23 | .history-buttons button:not([disabled]), 24 | .breadcrumb .breadcrumb-item-active { 25 | cursor: pointer; 26 | } 27 | 28 | .breadcrumb, 29 | .breadcrumb ol, 30 | .breadcrumb li, 31 | .breadcrumb .breadcrumb-item { 32 | overflow-x: hidden; 33 | overflow-inline: hidden; 34 | } 35 | 36 | .navigation-bar, 37 | .breadcrumb ol { 38 | flex-wrap: wrap; 39 | column-gap: calc(var(--small-gap-inline-size) - var(--outline-width)); 40 | row-gap: var(--small-gap-block-size); 41 | } 42 | 43 | .breadcrumb li::after { 44 | content: var(--folder-separator); 45 | } 46 | 47 | .breadcrumb .breadcrumb-item { 48 | text-decoration: underline dotted transparent var(--large-border-width); 49 | text-underline-offset: 0.25em; 50 | white-space: nowrap; 51 | text-overflow: ellipsis; 52 | -webkit-tap-highlight-color: transparent; 53 | margin: var(--outline-width); 54 | } 55 | 56 | .breadcrumb .breadcrumb-item:focus-visible { 57 | text-decoration: none; 58 | } 59 | 60 | .breadcrumb li:last-of-type .breadcrumb-item { 61 | font-weight: var(--font-weight-bolder); 62 | } 63 | 64 | .breadcrumb .breadcrumb-item-active { 65 | text-decoration-color: var(--accent-color); 66 | } 67 | 68 | .breadcrumb .breadcrumb-item-active:hover { 69 | filter: brightness(var(--brightness-hover)); 70 | } -------------------------------------------------------------------------------- /src/zip-manager/components/styles/TopButtonBar.css: -------------------------------------------------------------------------------- 1 | .button-bar-top { 2 | justify-content: flex-start; 3 | } 4 | 5 | .button-bar-top .button-group:last-of-type { 6 | flex: 1; 7 | justify-content: end; 8 | } -------------------------------------------------------------------------------- /src/zip-manager/hooks/hooks.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | function getHooks({ keyboardService, windowService }) { 4 | function useKeyUp(handleKeyUp) { 5 | useEffect(registerKeyUpHandler); 6 | 7 | function registerKeyUpHandler() { 8 | keyboardService.addKeyUpListener(handleKeyUp); 9 | return () => keyboardService.removeKeyUpListener(handleKeyUp); 10 | } 11 | } 12 | 13 | function useKeyDown(handleKeyDown) { 14 | useEffect(registerKeyDownHandler); 15 | 16 | function registerKeyDownHandler() { 17 | keyboardService.addKeyDownListener(handleKeyDown); 18 | return () => keyboardService.removeKeyDownListener(handleKeyDown); 19 | } 20 | } 21 | 22 | function usePageUnload(handlePageUnload) { 23 | useEffect(registerPageUnloadHandler); 24 | 25 | function registerPageUnloadHandler() { 26 | windowService.addUnloadListener(handlePageUnload); 27 | return () => windowService.removeUnloadListener(handlePageUnload); 28 | } 29 | } 30 | 31 | return { useKeyUp, useKeyDown, usePageUnload }; 32 | } 33 | 34 | export { getHooks }; 35 | -------------------------------------------------------------------------------- /src/zip-manager/messages/de-DE.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Download.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Übergeordneter Ordner"; 6 | 7 | const SHORTCUT_LABEL = "Verknüpfung: "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "Leertaste"; 11 | const ARROW_LEFT_KEY_LABEL = "links"; 12 | const ARROW_RIGHT_KEY_LABEL = "rechts"; 13 | const SIZE_LABEL = "Größe:"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Unkomprimierte Größe:"; 15 | const COMPRESSED_SIZE_LABEL = "Komprimierte Größe:"; 16 | const LAST_MOD_DATE_LABEL = "Letzte Änderung:"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Enter"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["Rücktaste", "Entf"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Verzeichnis erstellen"; 36 | const ADD_FILES_BUTTON_LABEL = "Dateien hinzufügen"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Zip importieren"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Zip exportieren"; 39 | const RESET_BUTTON_LABEL = "Zurücksetzen"; 40 | const OPTIONS_BUTTON_LABEL = "Optionen"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Kopieren"; 46 | const CUT_BUTTON_LABEL = "Ausschneiden"; 47 | const PASTE_BUTTON_LABEL = "Einfügen"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Zwischenablage zurücksetzen"; 49 | const EXTRACT_BUTTON_LABEL = "Extrahieren"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Alles auswählen"; 51 | const RENAME_BUTTON_LABEL = "Umbenennen"; 52 | const DELETE_BUTTON_LABEL = "Löschen"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Zurücksetzen"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Abbrechen"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Fehler"; 58 | 59 | const EXPORT_ZIP_TITLE = "Zip-Datei exportieren"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Dateiname:"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Passwort:"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Exportieren"; 63 | const EXTRACT_TITLE = "Datei extrahieren"; 64 | const EXTRACT_FILENAME_LABEL = "Dateiname:"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Extrahieren"; 66 | const RENAME_TITLE = "Eintrag umbenennen"; 67 | const RENAME_FILENAME_LABEL = "Neuer Eintragsname:"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Umbenennen"; 69 | const CREATE_FOLDER_TITLE = "Verzeichnis erstellen"; 70 | const CREATE_FOLDER_NAME_LABEL = "Verzeichnisname:"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Erstellen"; 72 | const RESET_TITLE = "Dateisystem zurücksetzen"; 73 | const RESET_MESSAGE = "Bitte bestätigen Sie das Zurücksetzen des Dateisystems."; 74 | const RESET_DIALOG_BUTTON_LABEL = "Zurücksetzen"; 75 | const DELETE_ENTRIES_TITLE = "Einträge löschen"; 76 | const DELETE_ENTRIES_MESSAGE = 77 | "Bitte bestätigen Sie das Löschen der markierten Einträge."; 78 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Löschen"; 79 | const ZIP_FILE_DESCRIPTION_LABEL = "Zip-Datei"; 80 | const IMPORT_PASSWORD_TITLE = "Passwort eingeben"; 81 | const IMPORT_PASSWORD_LABEL = "Passwort:"; 82 | const OPTIONS_DIALOG_BUTTON_LABEL = "Speichern"; 83 | const OPTIONS_TITLE = "Optionen"; 84 | const OPTIONS_ZOOM_FACTOR_LABEL = "Zoomfaktor (%):"; 85 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Navigationsleiste ausblenden:"; 86 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = "Download-Manager ausblenden:"; 87 | const OPTIONS_HIDE_INFOBAR_LABEL = "Untere Leiste ausblenden:"; 88 | const OPTIONS_SELECT_SKIN_LABEL = "Thema:"; 89 | const OPTIONS_DEFAULT_SKIN_LABEL = "Standard"; 90 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 91 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Nach Passwort fragen:"; 92 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Standardpasswort:"; 93 | const OPTIONS_KEEP_ORDER_LABEL = "Einträge sortieren:"; 94 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Daten-Signatur prüfen:"; 95 | const OPTIONS_BUFFERED_WRITE_LABEL = "Mehrere Kerne verwenden:"; 96 | const OPTIONS_MAX_WORKERS_LABEL = "Anzahl der Kerne:"; 97 | const OPTIONS_CHUNK_SIZE_LABEL = "Chunk-Größe (kB):"; 98 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Datei hinzufügen"; 99 | const CHOOSE_ACTION_TITLE = "Aktion auswählen"; 100 | const CHOOSE_ACTION_LABEL = 101 | "Eine Zip-Datei wurde ausgewählt. Bitte wählen Sie die entsprechende Aktion."; 102 | const NO_ENTRIES_LABEL = "Dateien und Verzeichnisse hierher ziehen"; 103 | const INFO_LABEL = [ 104 | "Quellcode auf ", 105 | " GitHub ", 106 | " Mit ", 107 | "♡", 108 | " gemacht in ", 109 | "Rennes" 110 | ]; 111 | const CITY_URL = "https://de.wikipedia.org/wiki/Rennes"; 112 | const HIGHLIGHTED_ENTRIES_LABEL = "Befehle für markierte Einträge"; 113 | const DOWNLOADS_LABEL = "Download-Manager"; 114 | const ENTRIES_LABEL = "Verzeichniseinträge"; 115 | const FOLDERS_LABEL = "Navigationsverlauf"; 116 | const SELECTED_FOLDER_LABEL = "Befehle für ausgewähltes Verzeichnis"; 117 | const GO_INTO_FOLDER_LABEL = "In Verzeichnis wechseln"; 118 | const GO_BACK_LABEL = "Zurück"; 119 | const GO_FORWARD_LABEL = "Vorwärts"; 120 | const ACCENT_COLOR_LABEL = "Akzentfarbe"; 121 | const PLAYING_MUSIC_ICON = "▶"; 122 | const PAUSED_MUSIC_ICON = "II"; 123 | 124 | export { 125 | ROOT_ZIP_FILENAME, 126 | SHORTCUT_LABEL, 127 | CTRL_KEY_LABEL, 128 | SPACE_KEY_LABEL, 129 | ARROW_LEFT_KEY_LABEL, 130 | ARROW_RIGHT_KEY_LABEL, 131 | PARENT_FOLDER_TOOLTIP, 132 | CREATE_FOLDER_BUTTON_TOOLTIP, 133 | ADD_FILES_BUTTON_TOOLTIP, 134 | IMPORT_ZIP_BUTTON_TOOLTIP, 135 | EXPORT_ZIP_BUTTON_TOOLTIP, 136 | BACK_BUTTON_TOOLTIP, 137 | FORWARD_BUTTON_TOOLTIP, 138 | COPY_BUTTON_TOOLTIP, 139 | CUT_BUTTON_TOOLTIP, 140 | PASTE_BUTTON_TOOLTIP, 141 | EXTRACT_BUTTON_TOOLTIP, 142 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 143 | RENAME_BUTTON_TOOLTIP, 144 | DELETE_BUTTON_TOOLTIP, 145 | ROOT_FOLDER_LABEL, 146 | PARENT_FOLDER_LABEL, 147 | COPY_BUTTON_LABEL, 148 | CUT_BUTTON_LABEL, 149 | PASTE_BUTTON_LABEL, 150 | RESET_CLIPBOARD_BUTTON_LABEL, 151 | EXTRACT_BUTTON_LABEL, 152 | HIGHLIGHT_ALL_BUTTON_LABEL, 153 | RENAME_BUTTON_LABEL, 154 | DELETE_BUTTON_LABEL, 155 | ABORT_DOWNLOAD_BUTTON_LABEL, 156 | DOWNLOAD_BUTTON_LABEL, 157 | ENTER_FOLDER_BUTTON_LABEL, 158 | BACK_BUTTON_LABEL, 159 | FORWARD_BUTTON_LABEL, 160 | CREATE_FOLDER_BUTTON_LABEL, 161 | ADD_FILES_BUTTON_LABEL, 162 | IMPORT_ZIP_BUTTON_LABEL, 163 | EXPORT_ZIP_BUTTON_LABEL, 164 | RESET_BUTTON_LABEL, 165 | OPTIONS_BUTTON_LABEL, 166 | KEYS_SEPARATOR_LABEL, 167 | ZIP_FILE_DESCRIPTION_LABEL, 168 | SIZE_LABEL, 169 | UNCOMPRESSED_SIZE_LABEL, 170 | COMPRESSED_SIZE_LABEL, 171 | LAST_MOD_DATE_LABEL, 172 | EXPORT_ZIP_TITLE, 173 | EXPORT_ZIP_FILENAME_LABEL, 174 | EXPORT_ZIP_PASSWORD_LABEL, 175 | DIALOG_OK_BUTTON_LABEL, 176 | DIALOG_CANCEL_BUTTON_LABEL, 177 | EXTRACT_TITLE, 178 | EXTRACT_FILENAME_LABEL, 179 | RENAME_TITLE, 180 | RENAME_FILENAME_LABEL, 181 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 182 | RENAME_DIALOG_BUTTON_LABEL, 183 | EXTRACT_DIALOG_BUTTON_LABEL, 184 | CREATE_FOLDER_TITLE, 185 | CREATE_FOLDER_NAME_LABEL, 186 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 187 | RESET_TITLE, 188 | RESET_MESSAGE, 189 | RESET_DIALOG_BUTTON_LABEL, 190 | DELETE_ENTRIES_TITLE, 191 | DELETE_ENTRIES_MESSAGE, 192 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 193 | ERROR_TITLE, 194 | IMPORT_PASSWORD_TITLE, 195 | IMPORT_PASSWORD_LABEL, 196 | DIALOG_RESET_BUTTON_LABEL, 197 | OPTIONS_TITLE, 198 | OPTIONS_DIALOG_BUTTON_LABEL, 199 | OPTIONS_ZOOM_FACTOR_LABEL, 200 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 201 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 202 | OPTIONS_HIDE_INFOBAR_LABEL, 203 | OPTIONS_DEFAULT_SKIN_LABEL, 204 | OPTIONS_DOS_SKIN_LABEL, 205 | OPTIONS_SELECT_SKIN_LABEL, 206 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 207 | OPTIONS_DEFAULT_PASSWORD_LABEL, 208 | OPTIONS_KEEP_ORDER_LABEL, 209 | OPTIONS_CHECK_SIGNATURE_LABEL, 210 | OPTIONS_BUFFERED_WRITE_LABEL, 211 | OPTIONS_MAX_WORKERS_LABEL, 212 | OPTIONS_CHUNK_SIZE_LABEL, 213 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 214 | CHOOSE_ACTION_TITLE, 215 | CHOOSE_ACTION_LABEL, 216 | NO_ENTRIES_LABEL, 217 | INFO_LABEL, 218 | CITY_URL, 219 | HIGHLIGHTED_ENTRIES_LABEL, 220 | DOWNLOADS_LABEL, 221 | ENTRIES_LABEL, 222 | FOLDERS_LABEL, 223 | SELECTED_FOLDER_LABEL, 224 | GO_INTO_FOLDER_LABEL, 225 | GO_BACK_LABEL, 226 | GO_FORWARD_LABEL, 227 | ACCENT_COLOR_LABEL, 228 | PLAYING_MUSIC_ICON, 229 | PAUSED_MUSIC_ICON 230 | }; 231 | -------------------------------------------------------------------------------- /src/zip-manager/messages/en-US.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Download.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Parent directory"; 6 | 7 | const SHORTCUT_LABEL = "Shortcut: "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "space"; 11 | const ARROW_LEFT_KEY_LABEL = "left"; 12 | const ARROW_RIGHT_KEY_LABEL = "right"; 13 | const SIZE_LABEL = "Size:"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Uncompressed size:"; 15 | const COMPRESSED_SIZE_LABEL = "Compressed size:"; 16 | const LAST_MOD_DATE_LABEL = "Last mod. date:"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Enter"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["backspace", "delete"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Create directory"; 36 | const ADD_FILES_BUTTON_LABEL = "Add files"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Import zip"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Export zip"; 39 | const RESET_BUTTON_LABEL = "Reset"; 40 | const OPTIONS_BUTTON_LABEL = "Options"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Copy"; 46 | const CUT_BUTTON_LABEL = "Cut"; 47 | const PASTE_BUTTON_LABEL = "Paste"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Reset clipboard"; 49 | const EXTRACT_BUTTON_LABEL = "Extract"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Select all"; 51 | const RENAME_BUTTON_LABEL = "Rename"; 52 | const DELETE_BUTTON_LABEL = "Delete"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Reset"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Cancel"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Error"; 58 | 59 | const EXPORT_ZIP_TITLE = "Export zip file"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Filename:"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Password:"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Export"; 63 | const EXTRACT_TITLE = "Extract file"; 64 | const EXTRACT_FILENAME_LABEL = "Filename:"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Extract"; 66 | const RENAME_TITLE = "Rename entry"; 67 | const RENAME_FILENAME_LABEL = "New entry name:"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Rename"; 69 | const CREATE_FOLDER_TITLE = "Create directory"; 70 | const CREATE_FOLDER_NAME_LABEL = "Directory name:"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Create"; 72 | const RESET_TITLE = "Reset filesystem"; 73 | const RESET_MESSAGE = "Please confirm the reset of the filesystem."; 74 | const RESET_DIALOG_BUTTON_LABEL = "Reset"; 75 | const DELETE_ENTRIES_TITLE = "Delete entries"; 76 | const DELETE_ENTRIES_MESSAGE = 77 | "Please confirm the deletion of the highlighted entries."; 78 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Delete"; 79 | const ZIP_FILE_DESCRIPTION_LABEL = "Zip file"; 80 | const IMPORT_PASSWORD_TITLE = "Enter password"; 81 | const IMPORT_PASSWORD_LABEL = "Password:"; 82 | const OPTIONS_DIALOG_BUTTON_LABEL = "Save"; 83 | const OPTIONS_TITLE = "Options"; 84 | const OPTIONS_ZOOM_FACTOR_LABEL = "Zoom factor (%):"; 85 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Hide navigation bar:"; 86 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = "Hide downloads panel:"; 87 | const OPTIONS_HIDE_INFOBAR_LABEL = "Hide bottom bar:"; 88 | const OPTIONS_SELECT_SKIN_LABEL = "Theme:"; 89 | const OPTIONS_DEFAULT_SKIN_LABEL = "Default"; 90 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 91 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Prompt for password:"; 92 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Default password:"; 93 | const OPTIONS_KEEP_ORDER_LABEL = "Keep entries order:"; 94 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Check data signature:"; 95 | const OPTIONS_BUFFERED_WRITE_LABEL = "Use multiple cores:"; 96 | const OPTIONS_MAX_WORKERS_LABEL = "Number of cores:"; 97 | const OPTIONS_CHUNK_SIZE_LABEL = "Chunk size (kB):"; 98 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Add file"; 99 | const CHOOSE_ACTION_TITLE = "Choose action"; 100 | const CHOOSE_ACTION_LABEL = 101 | "A zip file has been selected, please choose the proper action."; 102 | const NO_ENTRIES_LABEL = "Drag and drop files and directories here"; 103 | const INFO_LABEL = [ 104 | "Source code on ", 105 | " GitHub ", 106 | " Made with ", 107 | "♡", 108 | " in ", 109 | "Rennes" 110 | ]; 111 | const CITY_URL = "https://en.wikipedia.org/wiki/Rennes"; 112 | const HIGHLIGHTED_ENTRIES_LABEL = "Highlighted entries commands"; 113 | const DOWNLOADS_LABEL = "Downloads panel"; 114 | const ENTRIES_LABEL = "Directory entries"; 115 | const FOLDERS_LABEL = "Navigation history"; 116 | const SELECTED_FOLDER_LABEL = "Selected directory commands"; 117 | const GO_INTO_FOLDER_LABEL = "Go into folder"; 118 | const GO_BACK_LABEL = "Go back"; 119 | const GO_FORWARD_LABEL = "Go forward"; 120 | const ACCENT_COLOR_LABEL = "Accent color"; 121 | const PLAYING_MUSIC_ICON = "▶"; 122 | const PAUSED_MUSIC_ICON = "II"; 123 | 124 | export { 125 | ROOT_ZIP_FILENAME, 126 | SHORTCUT_LABEL, 127 | CTRL_KEY_LABEL, 128 | SPACE_KEY_LABEL, 129 | ARROW_LEFT_KEY_LABEL, 130 | ARROW_RIGHT_KEY_LABEL, 131 | PARENT_FOLDER_TOOLTIP, 132 | CREATE_FOLDER_BUTTON_TOOLTIP, 133 | ADD_FILES_BUTTON_TOOLTIP, 134 | IMPORT_ZIP_BUTTON_TOOLTIP, 135 | EXPORT_ZIP_BUTTON_TOOLTIP, 136 | BACK_BUTTON_TOOLTIP, 137 | FORWARD_BUTTON_TOOLTIP, 138 | COPY_BUTTON_TOOLTIP, 139 | CUT_BUTTON_TOOLTIP, 140 | PASTE_BUTTON_TOOLTIP, 141 | EXTRACT_BUTTON_TOOLTIP, 142 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 143 | RENAME_BUTTON_TOOLTIP, 144 | DELETE_BUTTON_TOOLTIP, 145 | ROOT_FOLDER_LABEL, 146 | PARENT_FOLDER_LABEL, 147 | COPY_BUTTON_LABEL, 148 | CUT_BUTTON_LABEL, 149 | PASTE_BUTTON_LABEL, 150 | RESET_CLIPBOARD_BUTTON_LABEL, 151 | EXTRACT_BUTTON_LABEL, 152 | HIGHLIGHT_ALL_BUTTON_LABEL, 153 | RENAME_BUTTON_LABEL, 154 | DELETE_BUTTON_LABEL, 155 | ABORT_DOWNLOAD_BUTTON_LABEL, 156 | DOWNLOAD_BUTTON_LABEL, 157 | ENTER_FOLDER_BUTTON_LABEL, 158 | BACK_BUTTON_LABEL, 159 | FORWARD_BUTTON_LABEL, 160 | CREATE_FOLDER_BUTTON_LABEL, 161 | ADD_FILES_BUTTON_LABEL, 162 | IMPORT_ZIP_BUTTON_LABEL, 163 | EXPORT_ZIP_BUTTON_LABEL, 164 | RESET_BUTTON_LABEL, 165 | OPTIONS_BUTTON_LABEL, 166 | KEYS_SEPARATOR_LABEL, 167 | ZIP_FILE_DESCRIPTION_LABEL, 168 | SIZE_LABEL, 169 | UNCOMPRESSED_SIZE_LABEL, 170 | COMPRESSED_SIZE_LABEL, 171 | LAST_MOD_DATE_LABEL, 172 | EXPORT_ZIP_TITLE, 173 | EXPORT_ZIP_FILENAME_LABEL, 174 | EXPORT_ZIP_PASSWORD_LABEL, 175 | DIALOG_OK_BUTTON_LABEL, 176 | DIALOG_CANCEL_BUTTON_LABEL, 177 | EXTRACT_TITLE, 178 | EXTRACT_FILENAME_LABEL, 179 | RENAME_TITLE, 180 | RENAME_FILENAME_LABEL, 181 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 182 | RENAME_DIALOG_BUTTON_LABEL, 183 | EXTRACT_DIALOG_BUTTON_LABEL, 184 | CREATE_FOLDER_TITLE, 185 | CREATE_FOLDER_NAME_LABEL, 186 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 187 | RESET_TITLE, 188 | RESET_MESSAGE, 189 | RESET_DIALOG_BUTTON_LABEL, 190 | DELETE_ENTRIES_TITLE, 191 | DELETE_ENTRIES_MESSAGE, 192 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 193 | ERROR_TITLE, 194 | IMPORT_PASSWORD_TITLE, 195 | IMPORT_PASSWORD_LABEL, 196 | DIALOG_RESET_BUTTON_LABEL, 197 | OPTIONS_TITLE, 198 | OPTIONS_DIALOG_BUTTON_LABEL, 199 | OPTIONS_ZOOM_FACTOR_LABEL, 200 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 201 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 202 | OPTIONS_HIDE_INFOBAR_LABEL, 203 | OPTIONS_DEFAULT_SKIN_LABEL, 204 | OPTIONS_DOS_SKIN_LABEL, 205 | OPTIONS_SELECT_SKIN_LABEL, 206 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 207 | OPTIONS_DEFAULT_PASSWORD_LABEL, 208 | OPTIONS_KEEP_ORDER_LABEL, 209 | OPTIONS_CHECK_SIGNATURE_LABEL, 210 | OPTIONS_BUFFERED_WRITE_LABEL, 211 | OPTIONS_MAX_WORKERS_LABEL, 212 | OPTIONS_CHUNK_SIZE_LABEL, 213 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 214 | CHOOSE_ACTION_TITLE, 215 | CHOOSE_ACTION_LABEL, 216 | NO_ENTRIES_LABEL, 217 | INFO_LABEL, 218 | CITY_URL, 219 | HIGHLIGHTED_ENTRIES_LABEL, 220 | DOWNLOADS_LABEL, 221 | ENTRIES_LABEL, 222 | FOLDERS_LABEL, 223 | SELECTED_FOLDER_LABEL, 224 | GO_INTO_FOLDER_LABEL, 225 | GO_BACK_LABEL, 226 | GO_FORWARD_LABEL, 227 | ACCENT_COLOR_LABEL, 228 | PLAYING_MUSIC_ICON, 229 | PAUSED_MUSIC_ICON 230 | }; 231 | -------------------------------------------------------------------------------- /src/zip-manager/messages/es-ES.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Descarga.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Directorio padre"; 6 | 7 | const SHORTCUT_LABEL = "Atajo: "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "espacio"; 11 | const ARROW_LEFT_KEY_LABEL = "izquierda"; 12 | const ARROW_RIGHT_KEY_LABEL = "derecha"; 13 | const SIZE_LABEL = "Tamaño:"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Tamaño sin comprimir:"; 15 | const COMPRESSED_SIZE_LABEL = "Tamaño comprimido:"; 16 | const LAST_MOD_DATE_LABEL = "Fecha de última modificación:"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Enter"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["backspace", "delete"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Crear directorio"; 36 | const ADD_FILES_BUTTON_LABEL = "Agregar archivos"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Importar zip"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Exportar zip"; 39 | const RESET_BUTTON_LABEL = "Reiniciar"; 40 | const OPTIONS_BUTTON_LABEL = "Opciones"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Copiar"; 46 | const CUT_BUTTON_LABEL = "Cortar"; 47 | const PASTE_BUTTON_LABEL = "Pegar"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Reiniciar portapapeles"; 49 | const EXTRACT_BUTTON_LABEL = "Extraer"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Seleccionar todo"; 51 | const RENAME_BUTTON_LABEL = "Renombrar"; 52 | const DELETE_BUTTON_LABEL = "Eliminar"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Reiniciar"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Cancelar"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Error"; 58 | 59 | const EXPORT_ZIP_TITLE = "Exportar archivo zip"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Nombre de archivo:"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Contraseña:"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Exportar"; 63 | const EXTRACT_TITLE = "Extraer archivo"; 64 | const EXTRACT_FILENAME_LABEL = "Nombre de archivo:"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Extraer"; 66 | const RENAME_TITLE = "Renombrar entrada"; 67 | const RENAME_FILENAME_LABEL = "Nuevo nombre de entrada:"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Renombrar"; 69 | const CREATE_FOLDER_TITLE = "Crear directorio"; 70 | const CREATE_FOLDER_NAME_LABEL = "Nombre del directorio:"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Crear"; 72 | const RESET_TITLE = "Reiniciar sistema de archivos"; 73 | const RESET_MESSAGE = "Por favor, confirme el reinicio del sistema de archivos."; 74 | const RESET_DIALOG_BUTTON_LABEL = "Reiniciar"; 75 | const DELETE_ENTRIES_TITLE = "Eliminar entradas"; 76 | const DELETE_ENTRIES_MESSAGE = 77 | "Por favor, confirme la eliminación de las entradas resaltadas."; 78 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Eliminar"; 79 | const ZIP_FILE_DESCRIPTION_LABEL = "Archivo zip"; 80 | const IMPORT_PASSWORD_TITLE = "Introducir contraseña"; 81 | const IMPORT_PASSWORD_LABEL = "Contraseña:"; 82 | const OPTIONS_DIALOG_BUTTON_LABEL = "Guardar"; 83 | const OPTIONS_TITLE = "Opciones"; 84 | const OPTIONS_ZOOM_FACTOR_LABEL = "Factor de zoom (%):"; 85 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Ocultar barra de navegación:"; 86 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = "Ocultar panel de descargas:"; 87 | const OPTIONS_HIDE_INFOBAR_LABEL = "Ocultar barra inferior:"; 88 | const OPTIONS_SELECT_SKIN_LABEL = "Tema:"; 89 | const OPTIONS_DEFAULT_SKIN_LABEL = "Predeterminado"; 90 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 91 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Solicitar contraseña:"; 92 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Contraseña predeterminada:"; 93 | const OPTIONS_KEEP_ORDER_LABEL = "Mantener orden de entradas:"; 94 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Verificar firma de datos:"; 95 | const OPTIONS_BUFFERED_WRITE_LABEL = "Usar múltiples núcleos:"; 96 | const OPTIONS_MAX_WORKERS_LABEL = "Número de núcleos:"; 97 | const OPTIONS_CHUNK_SIZE_LABEL = "Tamaño de fragmento (kB):"; 98 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Agregar archivo"; 99 | const CHOOSE_ACTION_TITLE = "Elegir acción"; 100 | const CHOOSE_ACTION_LABEL = 101 | "Se ha seleccionado un archivo zip, por favor elija la acción adecuada."; 102 | const NO_ENTRIES_LABEL = "Arrastre y suelte archivos y directorios aquí"; 103 | const INFO_LABEL = [ 104 | "Código fuente en ", 105 | " GitHub ", 106 | " Hecho con ", 107 | "♡", 108 | " en ", 109 | "Rennes" 110 | ]; 111 | const CITY_URL = "https://es.wikipedia.org/wiki/Rennes"; 112 | const HIGHLIGHTED_ENTRIES_LABEL = "Comandos de entradas resaltadas"; 113 | const DOWNLOADS_LABEL = "Panel de descargas"; 114 | const ENTRIES_LABEL = "Entradas de directorio"; 115 | const FOLDERS_LABEL = "Historial de navegación"; 116 | const SELECTED_FOLDER_LABEL = "Comandos de directorio seleccionado"; 117 | const GO_INTO_FOLDER_LABEL = "Ir al directorio"; 118 | const GO_BACK_LABEL = "Retroceder"; 119 | const GO_FORWARD_LABEL = "Avanzar"; 120 | const ACCENT_COLOR_LABEL = "Color de acento"; 121 | const PLAYING_MUSIC_ICON = "▶"; 122 | const PAUSED_MUSIC_ICON = "II"; 123 | 124 | export { 125 | ROOT_ZIP_FILENAME, 126 | SHORTCUT_LABEL, 127 | CTRL_KEY_LABEL, 128 | SPACE_KEY_LABEL, 129 | ARROW_LEFT_KEY_LABEL, 130 | ARROW_RIGHT_KEY_LABEL, 131 | PARENT_FOLDER_TOOLTIP, 132 | CREATE_FOLDER_BUTTON_TOOLTIP, 133 | ADD_FILES_BUTTON_TOOLTIP, 134 | IMPORT_ZIP_BUTTON_TOOLTIP, 135 | EXPORT_ZIP_BUTTON_TOOLTIP, 136 | BACK_BUTTON_TOOLTIP, 137 | FORWARD_BUTTON_TOOLTIP, 138 | COPY_BUTTON_TOOLTIP, 139 | CUT_BUTTON_TOOLTIP, 140 | PASTE_BUTTON_TOOLTIP, 141 | EXTRACT_BUTTON_TOOLTIP, 142 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 143 | RENAME_BUTTON_TOOLTIP, 144 | DELETE_BUTTON_TOOLTIP, 145 | ROOT_FOLDER_LABEL, 146 | PARENT_FOLDER_LABEL, 147 | COPY_BUTTON_LABEL, 148 | CUT_BUTTON_LABEL, 149 | PASTE_BUTTON_LABEL, 150 | RESET_CLIPBOARD_BUTTON_LABEL, 151 | EXTRACT_BUTTON_LABEL, 152 | HIGHLIGHT_ALL_BUTTON_LABEL, 153 | RENAME_BUTTON_LABEL, 154 | DELETE_BUTTON_LABEL, 155 | ABORT_DOWNLOAD_BUTTON_LABEL, 156 | DOWNLOAD_BUTTON_LABEL, 157 | ENTER_FOLDER_BUTTON_LABEL, 158 | BACK_BUTTON_LABEL, 159 | FORWARD_BUTTON_LABEL, 160 | CREATE_FOLDER_BUTTON_LABEL, 161 | ADD_FILES_BUTTON_LABEL, 162 | IMPORT_ZIP_BUTTON_LABEL, 163 | EXPORT_ZIP_BUTTON_LABEL, 164 | RESET_BUTTON_LABEL, 165 | OPTIONS_BUTTON_LABEL, 166 | KEYS_SEPARATOR_LABEL, 167 | ZIP_FILE_DESCRIPTION_LABEL, 168 | SIZE_LABEL, 169 | UNCOMPRESSED_SIZE_LABEL, 170 | COMPRESSED_SIZE_LABEL, 171 | LAST_MOD_DATE_LABEL, 172 | EXPORT_ZIP_TITLE, 173 | EXPORT_ZIP_FILENAME_LABEL, 174 | EXPORT_ZIP_PASSWORD_LABEL, 175 | DIALOG_OK_BUTTON_LABEL, 176 | DIALOG_CANCEL_BUTTON_LABEL, 177 | EXTRACT_TITLE, 178 | EXTRACT_FILENAME_LABEL, 179 | RENAME_TITLE, 180 | RENAME_FILENAME_LABEL, 181 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 182 | RENAME_DIALOG_BUTTON_LABEL, 183 | EXTRACT_DIALOG_BUTTON_LABEL, 184 | CREATE_FOLDER_TITLE, 185 | CREATE_FOLDER_NAME_LABEL, 186 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 187 | RESET_TITLE, 188 | RESET_MESSAGE, 189 | RESET_DIALOG_BUTTON_LABEL, 190 | DELETE_ENTRIES_TITLE, 191 | DELETE_ENTRIES_MESSAGE, 192 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 193 | ERROR_TITLE, 194 | IMPORT_PASSWORD_TITLE, 195 | IMPORT_PASSWORD_LABEL, 196 | DIALOG_RESET_BUTTON_LABEL, 197 | OPTIONS_TITLE, 198 | OPTIONS_DIALOG_BUTTON_LABEL, 199 | OPTIONS_ZOOM_FACTOR_LABEL, 200 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 201 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 202 | OPTIONS_HIDE_INFOBAR_LABEL, 203 | OPTIONS_DEFAULT_SKIN_LABEL, 204 | OPTIONS_DOS_SKIN_LABEL, 205 | OPTIONS_SELECT_SKIN_LABEL, 206 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 207 | OPTIONS_DEFAULT_PASSWORD_LABEL, 208 | OPTIONS_KEEP_ORDER_LABEL, 209 | OPTIONS_CHECK_SIGNATURE_LABEL, 210 | OPTIONS_BUFFERED_WRITE_LABEL, 211 | OPTIONS_MAX_WORKERS_LABEL, 212 | OPTIONS_CHUNK_SIZE_LABEL, 213 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 214 | CHOOSE_ACTION_TITLE, 215 | CHOOSE_ACTION_LABEL, 216 | NO_ENTRIES_LABEL, 217 | INFO_LABEL, 218 | CITY_URL, 219 | HIGHLIGHTED_ENTRIES_LABEL, 220 | DOWNLOADS_LABEL, 221 | ENTRIES_LABEL, 222 | FOLDERS_LABEL, 223 | SELECTED_FOLDER_LABEL, 224 | GO_INTO_FOLDER_LABEL, 225 | GO_BACK_LABEL, 226 | GO_FORWARD_LABEL, 227 | ACCENT_COLOR_LABEL, 228 | PLAYING_MUSIC_ICON, 229 | PAUSED_MUSIC_ICON 230 | }; 231 | -------------------------------------------------------------------------------- /src/zip-manager/messages/fr-FR.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Téléchargement.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Répertoire parent"; 6 | 7 | const SHORTCUT_LABEL = "Raccourci : "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "espace"; 11 | const ARROW_LEFT_KEY_LABEL = "gauche"; 12 | const ARROW_RIGHT_KEY_LABEL = "droite"; 13 | const SIZE_LABEL = "Taille :"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Taille non compressée :"; 15 | const COMPRESSED_SIZE_LABEL = "Taille compressée :"; 16 | const LAST_MOD_DATE_LABEL = "Date de dernière modification :"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Entrée"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["retour arrière", "suppr"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Créer répertoire"; 36 | const ADD_FILES_BUTTON_LABEL = "Ajouter fichiers"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Importer zip"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Exporter zip"; 39 | const RESET_BUTTON_LABEL = "Réinitialiser"; 40 | const OPTIONS_BUTTON_LABEL = "Options"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Copier"; 46 | const CUT_BUTTON_LABEL = "Couper"; 47 | const PASTE_BUTTON_LABEL = "Coller"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Réinitialiser presse-papier"; 49 | const EXTRACT_BUTTON_LABEL = "Extraire"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Selectionner tout"; 51 | const RENAME_BUTTON_LABEL = "Renommer"; 52 | const DELETE_BUTTON_LABEL = "Supprimer"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Réinitialiser"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Annuler"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Erreur"; 58 | 59 | const EXPORT_ZIP_TITLE = "Exporter fichier zip"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Nom du fichier :"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Mot de passe :"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Export"; 63 | const EXTRACT_TITLE = "Extraire fichier"; 64 | const EXTRACT_FILENAME_LABEL = "Nom du fichier :"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Extraire"; 66 | const RENAME_TITLE = "Renommer entrée"; 67 | const RENAME_FILENAME_LABEL = "Nouveau nom de l'entrée :"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Renommer"; 69 | const CREATE_FOLDER_TITLE = "Créer répertoire"; 70 | const CREATE_FOLDER_NAME_LABEL = "Nom du repertoire :"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Créer"; 72 | const RESET_TITLE = "Réinitialiser système de fichiers"; 73 | const RESET_MESSAGE = 74 | "Veuillez confirmer la réinitialisation du système de fichiers."; 75 | const RESET_DIALOG_BUTTON_LABEL = "Réinitialiser"; 76 | const DELETE_ENTRIES_TITLE = "Supprimer les entrées"; 77 | const DELETE_ENTRIES_MESSAGE = 78 | "Veuillez confirmer la suppression des entrées en surbrillance."; 79 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Supprimer"; 80 | const ZIP_FILE_DESCRIPTION_LABEL = "Zipper fichier"; 81 | const IMPORT_PASSWORD_TITLE = "Entrer le mot de passse"; 82 | const IMPORT_PASSWORD_LABEL = "Mot de passe :"; 83 | const OPTIONS_DIALOG_BUTTON_LABEL = "Sauver"; 84 | const OPTIONS_TITLE = "Options"; 85 | const OPTIONS_ZOOM_FACTOR_LABEL = "Facteur de zoom (%) :"; 86 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Cacher la barre de navigation :"; 87 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = 88 | "Cacher le panneau de téléchargements :"; 89 | const OPTIONS_HIDE_INFOBAR_LABEL = "Cacher la barre du bas :"; 90 | const OPTIONS_SELECT_SKIN_LABEL = "Thème :"; 91 | const OPTIONS_DEFAULT_SKIN_LABEL = "Défaut"; 92 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 93 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Demander un mot de passe :"; 94 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Mot de passe par défaut :"; 95 | const OPTIONS_KEEP_ORDER_LABEL = "Préserver l'ordre des entrées :"; 96 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Vérifier la signature des données :"; 97 | const OPTIONS_BUFFERED_WRITE_LABEL = "Utiliser plusieurs cœurs :"; 98 | const OPTIONS_MAX_WORKERS_LABEL = "Nombre de cœurs :"; 99 | const OPTIONS_CHUNK_SIZE_LABEL = "Taille des paquets (ko) :"; 100 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Ajouter fichier"; 101 | const CHOOSE_ACTION_TITLE = "Choisir l'action"; 102 | const CHOOSE_ACTION_LABEL = 103 | "Un fichier zip a été selectionné, veuillez choisir l'action appropriée."; 104 | const NO_ENTRIES_LABEL = 105 | "Glissez et déposez des fichiers et des répertoires ici"; 106 | const INFO_LABEL = [ 107 | "Code source sur ", 108 | " GitHub ", 109 | " Fait avec ", 110 | "♡", 111 | " à ", 112 | "Rennes" 113 | ]; 114 | const CITY_URL = "https://fr.wikipedia.org/wiki/Rennes"; 115 | const HIGHLIGHTED_ENTRIES_LABEL = "Commandes des entrées en surbrillance"; 116 | const DOWNLOADS_LABEL = "Panneau de téléchargements"; 117 | const ENTRIES_LABEL = "Entrées du répertoire"; 118 | const FOLDERS_LABEL = "Historique de navigation"; 119 | const SELECTED_FOLDER_LABEL = "Commandes du répertoire selectionné"; 120 | const GO_INTO_FOLDER_LABEL = "Naviguer dans le répertoire"; 121 | const GO_BACK_LABEL = "Naviguer en arrière"; 122 | const GO_FORWARD_LABEL = "Naviguer en avant"; 123 | const ACCENT_COLOR_LABEL = "Couleur d'accentuation"; 124 | const PLAYING_MUSIC_ICON = "▶"; 125 | const PAUSED_MUSIC_ICON = "II"; 126 | 127 | export { 128 | ROOT_ZIP_FILENAME, 129 | SHORTCUT_LABEL, 130 | CTRL_KEY_LABEL, 131 | SPACE_KEY_LABEL, 132 | ARROW_LEFT_KEY_LABEL, 133 | ARROW_RIGHT_KEY_LABEL, 134 | PARENT_FOLDER_TOOLTIP, 135 | CREATE_FOLDER_BUTTON_TOOLTIP, 136 | ADD_FILES_BUTTON_TOOLTIP, 137 | IMPORT_ZIP_BUTTON_TOOLTIP, 138 | EXPORT_ZIP_BUTTON_TOOLTIP, 139 | BACK_BUTTON_TOOLTIP, 140 | FORWARD_BUTTON_TOOLTIP, 141 | COPY_BUTTON_TOOLTIP, 142 | CUT_BUTTON_TOOLTIP, 143 | PASTE_BUTTON_TOOLTIP, 144 | EXTRACT_BUTTON_TOOLTIP, 145 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 146 | RENAME_BUTTON_TOOLTIP, 147 | DELETE_BUTTON_TOOLTIP, 148 | ROOT_FOLDER_LABEL, 149 | PARENT_FOLDER_LABEL, 150 | COPY_BUTTON_LABEL, 151 | CUT_BUTTON_LABEL, 152 | PASTE_BUTTON_LABEL, 153 | RESET_CLIPBOARD_BUTTON_LABEL, 154 | EXTRACT_BUTTON_LABEL, 155 | HIGHLIGHT_ALL_BUTTON_LABEL, 156 | RENAME_BUTTON_LABEL, 157 | DELETE_BUTTON_LABEL, 158 | ABORT_DOWNLOAD_BUTTON_LABEL, 159 | DOWNLOAD_BUTTON_LABEL, 160 | ENTER_FOLDER_BUTTON_LABEL, 161 | BACK_BUTTON_LABEL, 162 | FORWARD_BUTTON_LABEL, 163 | CREATE_FOLDER_BUTTON_LABEL, 164 | ADD_FILES_BUTTON_LABEL, 165 | IMPORT_ZIP_BUTTON_LABEL, 166 | EXPORT_ZIP_BUTTON_LABEL, 167 | RESET_BUTTON_LABEL, 168 | OPTIONS_BUTTON_LABEL, 169 | KEYS_SEPARATOR_LABEL, 170 | ZIP_FILE_DESCRIPTION_LABEL, 171 | SIZE_LABEL, 172 | UNCOMPRESSED_SIZE_LABEL, 173 | COMPRESSED_SIZE_LABEL, 174 | LAST_MOD_DATE_LABEL, 175 | EXPORT_ZIP_TITLE, 176 | EXPORT_ZIP_FILENAME_LABEL, 177 | EXPORT_ZIP_PASSWORD_LABEL, 178 | DIALOG_OK_BUTTON_LABEL, 179 | DIALOG_CANCEL_BUTTON_LABEL, 180 | EXTRACT_TITLE, 181 | EXTRACT_FILENAME_LABEL, 182 | RENAME_TITLE, 183 | RENAME_FILENAME_LABEL, 184 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 185 | RENAME_DIALOG_BUTTON_LABEL, 186 | EXTRACT_DIALOG_BUTTON_LABEL, 187 | CREATE_FOLDER_TITLE, 188 | CREATE_FOLDER_NAME_LABEL, 189 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 190 | RESET_TITLE, 191 | RESET_MESSAGE, 192 | RESET_DIALOG_BUTTON_LABEL, 193 | DELETE_ENTRIES_TITLE, 194 | DELETE_ENTRIES_MESSAGE, 195 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 196 | ERROR_TITLE, 197 | IMPORT_PASSWORD_TITLE, 198 | IMPORT_PASSWORD_LABEL, 199 | DIALOG_RESET_BUTTON_LABEL, 200 | OPTIONS_TITLE, 201 | OPTIONS_DIALOG_BUTTON_LABEL, 202 | OPTIONS_ZOOM_FACTOR_LABEL, 203 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 204 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 205 | OPTIONS_HIDE_INFOBAR_LABEL, 206 | OPTIONS_SELECT_SKIN_LABEL, 207 | OPTIONS_DEFAULT_SKIN_LABEL, 208 | OPTIONS_DOS_SKIN_LABEL, 209 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 210 | OPTIONS_DEFAULT_PASSWORD_LABEL, 211 | OPTIONS_KEEP_ORDER_LABEL, 212 | OPTIONS_CHECK_SIGNATURE_LABEL, 213 | OPTIONS_BUFFERED_WRITE_LABEL, 214 | OPTIONS_MAX_WORKERS_LABEL, 215 | OPTIONS_CHUNK_SIZE_LABEL, 216 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 217 | CHOOSE_ACTION_TITLE, 218 | CHOOSE_ACTION_LABEL, 219 | NO_ENTRIES_LABEL, 220 | INFO_LABEL, 221 | CITY_URL, 222 | HIGHLIGHTED_ENTRIES_LABEL, 223 | DOWNLOADS_LABEL, 224 | ENTRIES_LABEL, 225 | FOLDERS_LABEL, 226 | SELECTED_FOLDER_LABEL, 227 | GO_INTO_FOLDER_LABEL, 228 | GO_BACK_LABEL, 229 | GO_FORWARD_LABEL, 230 | ACCENT_COLOR_LABEL, 231 | PLAYING_MUSIC_ICON, 232 | PAUSED_MUSIC_ICON 233 | }; 234 | -------------------------------------------------------------------------------- /src/zip-manager/messages/index.js: -------------------------------------------------------------------------------- 1 | import * as fr_FR from "./fr-FR.js"; 2 | import * as en_US from "./en-US.js"; 3 | import * as de_DE from "./de-DE.js"; 4 | import * as es_ES from "./es-ES.js"; 5 | import * as it_IT from "./it-IT.js"; 6 | import * as pt_PT from "./pt-PT.js"; 7 | 8 | const LANGUAGES = { 9 | "fr-FR": fr_FR, 10 | "en-US": en_US, 11 | "de-DE": de_DE, 12 | "es-ES": es_ES, 13 | "it-IT": it_IT, 14 | "pt-PT": pt_PT 15 | }; 16 | 17 | function getMessages({ i18nService }) { 18 | return LANGUAGES[i18nService.getLanguageId()]; 19 | } 20 | 21 | export { getMessages }; 22 | -------------------------------------------------------------------------------- /src/zip-manager/messages/it-IT.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Download.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Cartella padre"; 6 | 7 | const SHORTCUT_LABEL = "Scorciatoia: "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "spazio"; 11 | const ARROW_LEFT_KEY_LABEL = "sinistra"; 12 | const ARROW_RIGHT_KEY_LABEL = "destra"; 13 | const SIZE_LABEL = "Dimensioni:"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Dimensioni non compressa:"; 15 | const COMPRESSED_SIZE_LABEL = "Dimensioni compressa:"; 16 | const LAST_MOD_DATE_LABEL = "Data ultima modifica:"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Invio"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["backspace", "delete"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Crea cartella"; 36 | const ADD_FILES_BUTTON_LABEL = "Aggiungi file"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Importa zip"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Esporta zip"; 39 | const RESET_BUTTON_LABEL = "Resetta"; 40 | const OPTIONS_BUTTON_LABEL = "Opzioni"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Copia"; 46 | const CUT_BUTTON_LABEL = "Taglia"; 47 | const PASTE_BUTTON_LABEL = "Incolla"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Resetta clipboard"; 49 | const EXTRACT_BUTTON_LABEL = "Estrai"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Seleziona tutto"; 51 | const RENAME_BUTTON_LABEL = "Rinomina"; 52 | const DELETE_BUTTON_LABEL = "Elimina"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Resetta"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Annulla"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Errore"; 58 | 59 | const EXPORT_ZIP_TITLE = "Esporta file zip"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Nome file:"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Password:"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Esporta"; 63 | const EXTRACT_TITLE = "Estrai file"; 64 | const EXTRACT_FILENAME_LABEL = "Nome file:"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Estrai"; 66 | const RENAME_TITLE = "Rinomina voce"; 67 | const RENAME_FILENAME_LABEL = "Nuovo nome voce:"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Rinomina"; 69 | const CREATE_FOLDER_TITLE = "Crea cartella"; 70 | const CREATE_FOLDER_NAME_LABEL = "Nome cartella:"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Crea"; 72 | const RESET_TITLE = "Resetta file system"; 73 | const RESET_MESSAGE = "Confermi il reset del file system?"; 74 | const RESET_DIALOG_BUTTON_LABEL = "Resetta"; 75 | const DELETE_ENTRIES_TITLE = "Elimina voci"; 76 | const DELETE_ENTRIES_MESSAGE = 77 | "Confermi l'eliminazione delle voci evidenziate?"; 78 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Elimina"; 79 | const ZIP_FILE_DESCRIPTION_LABEL = "File zip"; 80 | const IMPORT_PASSWORD_TITLE = "Inserisci password"; 81 | const IMPORT_PASSWORD_LABEL = "Password:"; 82 | const OPTIONS_DIALOG_BUTTON_LABEL = "Salva"; 83 | const OPTIONS_TITLE = "Opzioni"; 84 | const OPTIONS_ZOOM_FACTOR_LABEL = "Fattore di zoom (%):"; 85 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Nascondi barra di navigazione:"; 86 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = "Nascondi pannello download:"; 87 | const OPTIONS_HIDE_INFOBAR_LABEL = "Nascondi barra inferiore:"; 88 | const OPTIONS_SELECT_SKIN_LABEL = "Tema:"; 89 | const OPTIONS_DEFAULT_SKIN_LABEL = "Predefinito"; 90 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 91 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Richiedi password:"; 92 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Password predefinita:"; 93 | const OPTIONS_KEEP_ORDER_LABEL = "Mantieni ordine voci:"; 94 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Verifica firma dati:"; 95 | const OPTIONS_BUFFERED_WRITE_LABEL = "Usa multi-core:"; 96 | const OPTIONS_MAX_WORKERS_LABEL = "Numero core:"; 97 | const OPTIONS_CHUNK_SIZE_LABEL = "Dimensione frammento (kB):"; 98 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Aggiungi file"; 99 | const CHOOSE_ACTION_TITLE = "Scegli azione"; 100 | const CHOOSE_ACTION_LABEL = 101 | "È stato selezionato un file zip, scegli l'azione appropriata."; 102 | const NO_ENTRIES_LABEL = "Trascina qui file e cartelle"; 103 | const INFO_LABEL = [ 104 | "Codice sorgente su ", 105 | " GitHub ", 106 | " Creato con ", 107 | "♡", 108 | " a ", 109 | "Rennes" 110 | ]; 111 | const CITY_URL = "https://it.wikipedia.org/wiki/Rennes"; 112 | const HIGHLIGHTED_ENTRIES_LABEL = "Comandi voci evidenziate"; 113 | const DOWNLOADS_LABEL = "Pannello download"; 114 | const ENTRIES_LABEL = "Voci directory"; 115 | const FOLDERS_LABEL = "Cronologia navigazione"; 116 | const SELECTED_FOLDER_LABEL = "Comandi cartella selezionata"; 117 | const GO_INTO_FOLDER_LABEL = "Entra nella cartella"; 118 | const GO_BACK_LABEL = "Indietro"; 119 | const GO_FORWARD_LABEL = "Avanti"; 120 | const ACCENT_COLOR_LABEL = "Colore accentuazione"; 121 | const PLAYING_MUSIC_ICON = "▶"; 122 | const PAUSED_MUSIC_ICON = "II"; 123 | 124 | export { 125 | ROOT_ZIP_FILENAME, 126 | SHORTCUT_LABEL, 127 | CTRL_KEY_LABEL, 128 | SPACE_KEY_LABEL, 129 | ARROW_LEFT_KEY_LABEL, 130 | ARROW_RIGHT_KEY_LABEL, 131 | PARENT_FOLDER_TOOLTIP, 132 | CREATE_FOLDER_BUTTON_TOOLTIP, 133 | ADD_FILES_BUTTON_TOOLTIP, 134 | IMPORT_ZIP_BUTTON_TOOLTIP, 135 | EXPORT_ZIP_BUTTON_TOOLTIP, 136 | BACK_BUTTON_TOOLTIP, 137 | FORWARD_BUTTON_TOOLTIP, 138 | COPY_BUTTON_TOOLTIP, 139 | CUT_BUTTON_TOOLTIP, 140 | PASTE_BUTTON_TOOLTIP, 141 | EXTRACT_BUTTON_TOOLTIP, 142 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 143 | RENAME_BUTTON_TOOLTIP, 144 | DELETE_BUTTON_TOOLTIP, 145 | ROOT_FOLDER_LABEL, 146 | PARENT_FOLDER_LABEL, 147 | COPY_BUTTON_LABEL, 148 | CUT_BUTTON_LABEL, 149 | PASTE_BUTTON_LABEL, 150 | RESET_CLIPBOARD_BUTTON_LABEL, 151 | EXTRACT_BUTTON_LABEL, 152 | HIGHLIGHT_ALL_BUTTON_LABEL, 153 | RENAME_BUTTON_LABEL, 154 | DELETE_BUTTON_LABEL, 155 | ABORT_DOWNLOAD_BUTTON_LABEL, 156 | DOWNLOAD_BUTTON_LABEL, 157 | ENTER_FOLDER_BUTTON_LABEL, 158 | BACK_BUTTON_LABEL, 159 | FORWARD_BUTTON_LABEL, 160 | CREATE_FOLDER_BUTTON_LABEL, 161 | ADD_FILES_BUTTON_LABEL, 162 | IMPORT_ZIP_BUTTON_LABEL, 163 | EXPORT_ZIP_BUTTON_LABEL, 164 | RESET_BUTTON_LABEL, 165 | OPTIONS_BUTTON_LABEL, 166 | KEYS_SEPARATOR_LABEL, 167 | ZIP_FILE_DESCRIPTION_LABEL, 168 | SIZE_LABEL, 169 | UNCOMPRESSED_SIZE_LABEL, 170 | COMPRESSED_SIZE_LABEL, 171 | LAST_MOD_DATE_LABEL, 172 | EXPORT_ZIP_TITLE, 173 | EXPORT_ZIP_FILENAME_LABEL, 174 | EXPORT_ZIP_PASSWORD_LABEL, 175 | DIALOG_OK_BUTTON_LABEL, 176 | DIALOG_CANCEL_BUTTON_LABEL, 177 | EXTRACT_TITLE, 178 | EXTRACT_FILENAME_LABEL, 179 | RENAME_TITLE, 180 | RENAME_FILENAME_LABEL, 181 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 182 | RENAME_DIALOG_BUTTON_LABEL, 183 | EXTRACT_DIALOG_BUTTON_LABEL, 184 | CREATE_FOLDER_TITLE, 185 | CREATE_FOLDER_NAME_LABEL, 186 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 187 | RESET_TITLE, 188 | RESET_MESSAGE, 189 | RESET_DIALOG_BUTTON_LABEL, 190 | DELETE_ENTRIES_TITLE, 191 | DELETE_ENTRIES_MESSAGE, 192 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 193 | ERROR_TITLE, 194 | IMPORT_PASSWORD_TITLE, 195 | IMPORT_PASSWORD_LABEL, 196 | DIALOG_RESET_BUTTON_LABEL, 197 | OPTIONS_TITLE, 198 | OPTIONS_DIALOG_BUTTON_LABEL, 199 | OPTIONS_ZOOM_FACTOR_LABEL, 200 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 201 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 202 | OPTIONS_HIDE_INFOBAR_LABEL, 203 | OPTIONS_DEFAULT_SKIN_LABEL, 204 | OPTIONS_DOS_SKIN_LABEL, 205 | OPTIONS_SELECT_SKIN_LABEL, 206 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 207 | OPTIONS_DEFAULT_PASSWORD_LABEL, 208 | OPTIONS_KEEP_ORDER_LABEL, 209 | OPTIONS_CHECK_SIGNATURE_LABEL, 210 | OPTIONS_BUFFERED_WRITE_LABEL, 211 | OPTIONS_MAX_WORKERS_LABEL, 212 | OPTIONS_CHUNK_SIZE_LABEL, 213 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 214 | CHOOSE_ACTION_TITLE, 215 | CHOOSE_ACTION_LABEL, 216 | NO_ENTRIES_LABEL, 217 | INFO_LABEL, 218 | CITY_URL, 219 | HIGHLIGHTED_ENTRIES_LABEL, 220 | DOWNLOADS_LABEL, 221 | ENTRIES_LABEL, 222 | FOLDERS_LABEL, 223 | SELECTED_FOLDER_LABEL, 224 | GO_INTO_FOLDER_LABEL, 225 | GO_BACK_LABEL, 226 | GO_FORWARD_LABEL, 227 | ACCENT_COLOR_LABEL, 228 | PLAYING_MUSIC_ICON, 229 | PAUSED_MUSIC_ICON 230 | }; 231 | -------------------------------------------------------------------------------- /src/zip-manager/messages/pt-PT.js: -------------------------------------------------------------------------------- 1 | const PARENT_FOLDER_LABEL = ".."; 2 | const ROOT_FOLDER_LABEL = ""; 3 | const ROOT_ZIP_FILENAME = "Download.zip"; 4 | const KEYS_SEPARATOR_LABEL = ", "; 5 | const PARENT_FOLDER_TOOLTIP = "Diretório pai"; 6 | 7 | const SHORTCUT_LABEL = "Atalho: "; 8 | const CTRL_KEY_LABEL = "⌘/ctrl-"; 9 | const ALT_KEY_LABEL = "alt-"; 10 | const SPACE_KEY_LABEL = "espaço"; 11 | const ARROW_LEFT_KEY_LABEL = "esquerda"; 12 | const ARROW_RIGHT_KEY_LABEL = "direita"; 13 | const SIZE_LABEL = "Tamanho:"; 14 | const UNCOMPRESSED_SIZE_LABEL = "Tamanho descompactado:"; 15 | const COMPRESSED_SIZE_LABEL = "Tamanho compactado:"; 16 | const LAST_MOD_DATE_LABEL = "Data da última modificação:"; 17 | 18 | const COPY_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "c"; 19 | const CUT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "x"; 20 | const PASTE_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "v"; 21 | const EXTRACT_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "Enter"; 22 | const HIGHLIGHT_ALL_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "a"; 23 | const RENAME_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "r"; 24 | const DELETE_BUTTON_TOOLTIP = 25 | SHORTCUT_LABEL + ["backspace", "delete"].join(KEYS_SEPARATOR_LABEL); 26 | const CREATE_FOLDER_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "d"; 27 | const ADD_FILES_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "f"; 28 | const IMPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "i"; 29 | const EXPORT_ZIP_BUTTON_TOOLTIP = SHORTCUT_LABEL + CTRL_KEY_LABEL + "e"; 30 | const BACK_BUTTON_TOOLTIP = 31 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_LEFT_KEY_LABEL; 32 | const FORWARD_BUTTON_TOOLTIP = 33 | SHORTCUT_LABEL + ALT_KEY_LABEL + ARROW_RIGHT_KEY_LABEL; 34 | 35 | const CREATE_FOLDER_BUTTON_LABEL = "Criar diretório"; 36 | const ADD_FILES_BUTTON_LABEL = "Adicionar ficheiros"; 37 | const IMPORT_ZIP_BUTTON_LABEL = "Importar zip"; 38 | const EXPORT_ZIP_BUTTON_LABEL = "Exportar zip"; 39 | const RESET_BUTTON_LABEL = "Repor"; 40 | const OPTIONS_BUTTON_LABEL = "Opções"; 41 | const BACK_BUTTON_LABEL = "<"; 42 | const FORWARD_BUTTON_LABEL = ">"; 43 | const DOWNLOAD_BUTTON_LABEL = "↵"; 44 | const ENTER_FOLDER_BUTTON_LABEL = "↓"; 45 | const COPY_BUTTON_LABEL = "Copiar"; 46 | const CUT_BUTTON_LABEL = "Cortar"; 47 | const PASTE_BUTTON_LABEL = "Colar"; 48 | const RESET_CLIPBOARD_BUTTON_LABEL = "Repor área de transferência"; 49 | const EXTRACT_BUTTON_LABEL = "Extrair"; 50 | const HIGHLIGHT_ALL_BUTTON_LABEL = "Selecionar tudo"; 51 | const RENAME_BUTTON_LABEL = "Renomear"; 52 | const DELETE_BUTTON_LABEL = "Eliminar"; 53 | const ABORT_DOWNLOAD_BUTTON_LABEL = "✕"; 54 | const DIALOG_RESET_BUTTON_LABEL = "Repor"; 55 | const DIALOG_CANCEL_BUTTON_LABEL = "Cancelar"; 56 | const DIALOG_OK_BUTTON_LABEL = "OK"; 57 | const ERROR_TITLE = "Erro"; 58 | 59 | const EXPORT_ZIP_TITLE = "Exportar ficheiro zip"; 60 | const EXPORT_ZIP_FILENAME_LABEL = "Nome do ficheiro:"; 61 | const EXPORT_ZIP_PASSWORD_LABEL = "Palavra-passe:"; 62 | const EXPORT_ZIP_DIALOG_BUTTON_LABEL = "Exportar"; 63 | const EXTRACT_TITLE = "Extrair ficheiro"; 64 | const EXTRACT_FILENAME_LABEL = "Nome do ficheiro:"; 65 | const EXTRACT_DIALOG_BUTTON_LABEL = "Extrair"; 66 | const RENAME_TITLE = "Renomear entrada"; 67 | const RENAME_FILENAME_LABEL = "Novo nome da entrada:"; 68 | const RENAME_DIALOG_BUTTON_LABEL = "Renomear"; 69 | const CREATE_FOLDER_TITLE = "Criar diretório"; 70 | const CREATE_FOLDER_NAME_LABEL = "Nome do diretório:"; 71 | const CREATE_FOLDER_DIALOG_BUTTON_LABEL = "Criar"; 72 | const RESET_TITLE = "Repor sistema de ficheiros"; 73 | const RESET_MESSAGE = "Por favor confirme a reposição do sistema de ficheiros."; 74 | const RESET_DIALOG_BUTTON_LABEL = "Repor"; 75 | const DELETE_ENTRIES_TITLE = "Eliminar entradas"; 76 | const DELETE_ENTRIES_MESSAGE = 77 | "Por favor confirme a eliminação das entradas selecionadas."; 78 | const DELETE_ENTRIES_DIALOG_BUTTON_LABEL = "Eliminar"; 79 | const ZIP_FILE_DESCRIPTION_LABEL = "Ficheiro zip"; 80 | const IMPORT_PASSWORD_TITLE = "Introduzir palavra-passe"; 81 | const IMPORT_PASSWORD_LABEL = "Palavra-passe:"; 82 | const OPTIONS_DIALOG_BUTTON_LABEL = "Guardar"; 83 | const OPTIONS_TITLE = "Opções"; 84 | const OPTIONS_ZOOM_FACTOR_LABEL = "Fator de zoom (%):"; 85 | const OPTIONS_HIDE_NAVIGATION_BAR_LABEL = "Esconder barra de navegação:"; 86 | const OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL = "Esconder painel de downloads:"; 87 | const OPTIONS_HIDE_INFOBAR_LABEL = "Esconder barra inferior:"; 88 | const OPTIONS_SELECT_SKIN_LABEL = "Tema:"; 89 | const OPTIONS_DEFAULT_SKIN_LABEL = "Padrão"; 90 | const OPTIONS_DOS_SKIN_LABEL = "DOS"; 91 | const OPTIONS_EXPORT_ZIP_PASSWORD_LABEL = "Pedir palavra-passe:"; 92 | const OPTIONS_DEFAULT_PASSWORD_LABEL = "Palavra-passe padrão:"; 93 | const OPTIONS_KEEP_ORDER_LABEL = "Manter ordem das entradas:"; 94 | const OPTIONS_CHECK_SIGNATURE_LABEL = "Verificar assinatura dos dados:"; 95 | const OPTIONS_BUFFERED_WRITE_LABEL = "Usar múltiplos núcleos:"; 96 | const OPTIONS_MAX_WORKERS_LABEL = "Número de núcleos:"; 97 | const OPTIONS_CHUNK_SIZE_LABEL = "Tamanho do bloco (kB):"; 98 | const CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL = "Adicionar ficheiro"; 99 | const CHOOSE_ACTION_TITLE = "Escolher ação"; 100 | const CHOOSE_ACTION_LABEL = 101 | "Foi selecionado um ficheiro zip, por favor escolha a ação apropriada."; 102 | const NO_ENTRIES_LABEL = "Arraste e solte ficheiros e diretórios aqui"; 103 | const INFO_LABEL = [ 104 | "Código-fonte em ", 105 | " GitHub", 106 | " Criado com ", 107 | "♡", 108 | " em ", 109 | "Rennes" 110 | ]; 111 | const CITY_URL = "https://pt.wikipedia.org/wiki/Rennes"; 112 | const HIGHLIGHTED_ENTRIES_LABEL = "Comandos para entradas selecionadas"; 113 | const DOWNLOADS_LABEL = "Painel de downloads"; 114 | const ENTRIES_LABEL = "Entradas do diretório"; 115 | const FOLDERS_LABEL = "Histórico de navegação"; 116 | const SELECTED_FOLDER_LABEL = "Comandos para diretório selecionado"; 117 | const GO_INTO_FOLDER_LABEL = "Entrar no diretório"; 118 | const GO_BACK_LABEL = "Voltar"; 119 | const GO_FORWARD_LABEL = "Avançar"; 120 | const ACCENT_COLOR_LABEL = "Cor de destaque"; 121 | const PLAYING_MUSIC_ICON = "▶"; 122 | const PAUSED_MUSIC_ICON = "II"; 123 | 124 | export { 125 | ROOT_ZIP_FILENAME, 126 | SHORTCUT_LABEL, 127 | CTRL_KEY_LABEL, 128 | SPACE_KEY_LABEL, 129 | ARROW_LEFT_KEY_LABEL, 130 | ARROW_RIGHT_KEY_LABEL, 131 | PARENT_FOLDER_TOOLTIP, 132 | CREATE_FOLDER_BUTTON_TOOLTIP, 133 | ADD_FILES_BUTTON_TOOLTIP, 134 | IMPORT_ZIP_BUTTON_TOOLTIP, 135 | EXPORT_ZIP_BUTTON_TOOLTIP, 136 | BACK_BUTTON_TOOLTIP, 137 | FORWARD_BUTTON_TOOLTIP, 138 | COPY_BUTTON_TOOLTIP, 139 | CUT_BUTTON_TOOLTIP, 140 | PASTE_BUTTON_TOOLTIP, 141 | EXTRACT_BUTTON_TOOLTIP, 142 | HIGHLIGHT_ALL_BUTTON_TOOLTIP, 143 | RENAME_BUTTON_TOOLTIP, 144 | DELETE_BUTTON_TOOLTIP, 145 | ROOT_FOLDER_LABEL, 146 | PARENT_FOLDER_LABEL, 147 | COPY_BUTTON_LABEL, 148 | CUT_BUTTON_LABEL, 149 | PASTE_BUTTON_LABEL, 150 | RESET_CLIPBOARD_BUTTON_LABEL, 151 | EXTRACT_BUTTON_LABEL, 152 | HIGHLIGHT_ALL_BUTTON_LABEL, 153 | RENAME_BUTTON_LABEL, 154 | DELETE_BUTTON_LABEL, 155 | ABORT_DOWNLOAD_BUTTON_LABEL, 156 | DOWNLOAD_BUTTON_LABEL, 157 | ENTER_FOLDER_BUTTON_LABEL, 158 | BACK_BUTTON_LABEL, 159 | FORWARD_BUTTON_LABEL, 160 | CREATE_FOLDER_BUTTON_LABEL, 161 | ADD_FILES_BUTTON_LABEL, 162 | IMPORT_ZIP_BUTTON_LABEL, 163 | EXPORT_ZIP_BUTTON_LABEL, 164 | RESET_BUTTON_LABEL, 165 | OPTIONS_BUTTON_LABEL, 166 | KEYS_SEPARATOR_LABEL, 167 | ZIP_FILE_DESCRIPTION_LABEL, 168 | SIZE_LABEL, 169 | UNCOMPRESSED_SIZE_LABEL, 170 | COMPRESSED_SIZE_LABEL, 171 | LAST_MOD_DATE_LABEL, 172 | EXPORT_ZIP_TITLE, 173 | EXPORT_ZIP_FILENAME_LABEL, 174 | EXPORT_ZIP_PASSWORD_LABEL, 175 | DIALOG_OK_BUTTON_LABEL, 176 | DIALOG_CANCEL_BUTTON_LABEL, 177 | EXTRACT_TITLE, 178 | EXTRACT_FILENAME_LABEL, 179 | RENAME_TITLE, 180 | RENAME_FILENAME_LABEL, 181 | EXPORT_ZIP_DIALOG_BUTTON_LABEL, 182 | RENAME_DIALOG_BUTTON_LABEL, 183 | EXTRACT_DIALOG_BUTTON_LABEL, 184 | CREATE_FOLDER_TITLE, 185 | CREATE_FOLDER_NAME_LABEL, 186 | CREATE_FOLDER_DIALOG_BUTTON_LABEL, 187 | RESET_TITLE, 188 | RESET_MESSAGE, 189 | RESET_DIALOG_BUTTON_LABEL, 190 | DELETE_ENTRIES_TITLE, 191 | DELETE_ENTRIES_MESSAGE, 192 | DELETE_ENTRIES_DIALOG_BUTTON_LABEL, 193 | ERROR_TITLE, 194 | IMPORT_PASSWORD_TITLE, 195 | IMPORT_PASSWORD_LABEL, 196 | DIALOG_RESET_BUTTON_LABEL, 197 | OPTIONS_TITLE, 198 | OPTIONS_DIALOG_BUTTON_LABEL, 199 | OPTIONS_ZOOM_FACTOR_LABEL, 200 | OPTIONS_HIDE_NAVIGATION_BAR_LABEL, 201 | OPTIONS_HIDE_DOWNLOAD_MANAGER_LABEL, 202 | OPTIONS_HIDE_INFOBAR_LABEL, 203 | OPTIONS_DEFAULT_SKIN_LABEL, 204 | OPTIONS_DOS_SKIN_LABEL, 205 | OPTIONS_SELECT_SKIN_LABEL, 206 | OPTIONS_EXPORT_ZIP_PASSWORD_LABEL, 207 | OPTIONS_DEFAULT_PASSWORD_LABEL, 208 | OPTIONS_KEEP_ORDER_LABEL, 209 | OPTIONS_CHECK_SIGNATURE_LABEL, 210 | OPTIONS_BUFFERED_WRITE_LABEL, 211 | OPTIONS_MAX_WORKERS_LABEL, 212 | OPTIONS_CHUNK_SIZE_LABEL, 213 | CHOOSE_ACTION_DIALOG_ADD_FILE_BUTTON_LABEL, 214 | CHOOSE_ACTION_TITLE, 215 | CHOOSE_ACTION_LABEL, 216 | NO_ENTRIES_LABEL, 217 | INFO_LABEL, 218 | CITY_URL, 219 | HIGHLIGHTED_ENTRIES_LABEL, 220 | DOWNLOADS_LABEL, 221 | ENTRIES_LABEL, 222 | FOLDERS_LABEL, 223 | SELECTED_FOLDER_LABEL, 224 | GO_INTO_FOLDER_LABEL, 225 | GO_BACK_LABEL, 226 | GO_FORWARD_LABEL, 227 | ACCENT_COLOR_LABEL, 228 | PLAYING_MUSIC_ICON, 229 | PAUSED_MUSIC_ICON 230 | }; 231 | -------------------------------------------------------------------------------- /src/zip-manager/services/document-service.js: -------------------------------------------------------------------------------- 1 | /* global document, getComputedStyle, ResizeObserver */ 2 | 3 | function scrollIntoView(element) { 4 | element.scrollIntoView({ block: "nearest" }); 5 | } 6 | 7 | function getHeight(element) { 8 | return element.offsetHeight; 9 | } 10 | 11 | function getRowHeight(element) { 12 | const rowGap = parseInt(getComputedStyle(element.parentElement).rowGap, 10); 13 | return getHeight(element) + (Number.isNaN(rowGap) ? 0 : rowGap); 14 | } 15 | 16 | function removeDocumentAttribute(name) { 17 | document.documentElement.removeAttribute(name); 18 | } 19 | 20 | function addResizeObserver(element, listener) { 21 | const observer = new ResizeObserver(listener); 22 | observer.observe(element, { attributes: true }); 23 | return observer; 24 | } 25 | 26 | function setDocumentLanguage(language) { 27 | document.documentElement.lang = language; 28 | } 29 | 30 | export { 31 | getHeight, 32 | getRowHeight, 33 | scrollIntoView, 34 | removeDocumentAttribute, 35 | addResizeObserver, 36 | setDocumentLanguage 37 | }; 38 | -------------------------------------------------------------------------------- /src/zip-manager/services/download-service.js: -------------------------------------------------------------------------------- 1 | /* global AbortController */ 2 | 3 | const ABORT_ERROR_NAME = "AbortError"; 4 | const CANCELLED_DOWNLOAD_MESSAGE = "download cancelled"; 5 | 6 | function createAbortController() { 7 | return new AbortController(); 8 | } 9 | 10 | function abortDownload(controller) { 11 | controller.abort(new Error(CANCELLED_DOWNLOAD_MESSAGE)); 12 | } 13 | 14 | function downloadAborted(error) { 15 | const message = error.message || error; 16 | return ( 17 | message === CANCELLED_DOWNLOAD_MESSAGE || error.name === ABORT_ERROR_NAME 18 | ); 19 | } 20 | 21 | export { createAbortController, abortDownload, downloadAborted }; 22 | -------------------------------------------------------------------------------- /src/zip-manager/services/environment-service.js: -------------------------------------------------------------------------------- 1 | /* global navigator */ 2 | 3 | const MACOS_PLATFORMS = ["Macintosh", "MacIntel", "MacPPC", "Mac68K"]; 4 | 5 | function getMaximumWorkers() { 6 | return navigator.hardwareConcurrency; 7 | } 8 | 9 | function isMacOSPlatform() { 10 | const { platform } = navigator; 11 | return platform !== undefined && MACOS_PLATFORMS.includes(platform); 12 | } 13 | 14 | export { getMaximumWorkers, isMacOSPlatform }; 15 | -------------------------------------------------------------------------------- /src/zip-manager/services/file-handlers-service.js: -------------------------------------------------------------------------------- 1 | /* global window, launchQueue */ 2 | 3 | function onOpenWith(callback) { 4 | if ("launchQueue" in window) { 5 | launchQueue.setConsumer((launchParams) => { 6 | getLaunchQueueConsumer(launchParams, callback); 7 | }); 8 | } 9 | } 10 | 11 | async function getLaunchQueueConsumer(launchParams, callback) { 12 | if (launchParams.files.length) { 13 | await Promise.all( 14 | launchParams.files.map(async (handle) => callback(await handle.getFile())) 15 | ); 16 | } 17 | } 18 | 19 | export { onOpenWith }; 20 | -------------------------------------------------------------------------------- /src/zip-manager/services/filesystem-service.js: -------------------------------------------------------------------------------- 1 | /* global window, document, URL */ 2 | 3 | const FILESYSTEM_FILE_KIND = "file"; 4 | const ABORT_ERROR_NAME = "AbortError"; 5 | 6 | async function showOpenFilePicker({ multiple, description, accept } = {}) { 7 | const excludeAcceptAllOption = Boolean(accept); 8 | if ("showOpenFilePicker" in window) { 9 | try { 10 | const options = { 11 | excludeAcceptAllOption, 12 | multiple 13 | }; 14 | if (excludeAcceptAllOption) { 15 | Object.assign(options, { 16 | types: [ 17 | { 18 | description, 19 | accept 20 | } 21 | ] 22 | }); 23 | } 24 | const fileHandles = await window.showOpenFilePicker(options); 25 | return Promise.all(fileHandles.map((fileHandle) => fileHandle.getFile())); 26 | } catch (error) { 27 | if (error.name === ABORT_ERROR_NAME) { 28 | return []; 29 | } else { 30 | throw error; 31 | } 32 | } 33 | } else { 34 | const fileInputElement = document.createElement("input"); 35 | Object.assign(fileInputElement, { 36 | type: "file", 37 | accept: accept ? Object.keys(accept).join(",") : "", 38 | multiple 39 | }); 40 | return new Promise((resolve) => { 41 | fileInputElement.onchange = ({ target }) => { 42 | if (target.files.length) { 43 | resolve(Array.from(target.files)); 44 | } 45 | }; 46 | fileInputElement.click(); 47 | }); 48 | } 49 | } 50 | 51 | function showDirectoryPicker(options) { 52 | return window.showDirectoryPicker(options); 53 | } 54 | 55 | function showSaveFilePicker(options) { 56 | return window.showSaveFilePicker(options); 57 | } 58 | 59 | function savePickersSupported() { 60 | return "showSaveFilePicker" in window && "showDirectoryPicker" in window; 61 | } 62 | 63 | function saveBlob(blob, filename) { 64 | const href = URL.createObjectURL(blob); 65 | const anchorElement = document.createElement("a"); 66 | Object.assign(anchorElement, { href, download: filename }); 67 | anchorElement.click(); 68 | URL.revokeObjectURL(href); 69 | } 70 | 71 | export { 72 | FILESYSTEM_FILE_KIND, 73 | showOpenFilePicker, 74 | showDirectoryPicker, 75 | savePickersSupported, 76 | showSaveFilePicker, 77 | saveBlob 78 | }; 79 | -------------------------------------------------------------------------------- /src/zip-manager/services/i18n-service.js: -------------------------------------------------------------------------------- 1 | /* global navigator */ 2 | 3 | const DEFAULT_LANGUAGE_ID = "en-US"; 4 | const LANGUAGE_IDS = [DEFAULT_LANGUAGE_ID, "de-DE", "es-ES", "fr-FR", "it-IT", "pt-PT"]; 5 | 6 | const SIZE_NUMBER_FORMATS = [ 7 | "kilobyte", 8 | "megabyte", 9 | "gigabyte", 10 | "terabyte", 11 | "petabyte" 12 | ].map( 13 | (unit) => 14 | new Intl.NumberFormat(getLanguageId(), { 15 | style: "unit", 16 | maximumFractionDigits: 1, 17 | unit 18 | }) 19 | ); 20 | const DATE_TIME_FORMAT = new Intl.DateTimeFormat(getLanguageId(), { 21 | dateStyle: "short", 22 | timeStyle: "short" 23 | }); 24 | const PERCENT_VALUE_FORMAT = new Intl.NumberFormat(getLanguageId(), { 25 | style: "unit", 26 | maximumFractionDigits: 0, 27 | unit: "percent" 28 | }); 29 | 30 | function formatSize(number) { 31 | let indexNumberFormat = 0; 32 | number = number / 1000; 33 | while (number > 1000 && indexNumberFormat < SIZE_NUMBER_FORMATS.length - 1) { 34 | number = number / 1000; 35 | indexNumberFormat++; 36 | } 37 | return SIZE_NUMBER_FORMATS[indexNumberFormat].format(number); 38 | } 39 | 40 | function formatDate(date) { 41 | return DATE_TIME_FORMAT.format(date); 42 | } 43 | 44 | function formatPercentValue(value) { 45 | return PERCENT_VALUE_FORMAT.format(value); 46 | } 47 | 48 | function getLanguageId() { 49 | return LANGUAGE_IDS.includes(navigator.language) 50 | ? navigator.language 51 | : DEFAULT_LANGUAGE_ID; 52 | } 53 | 54 | export { formatSize, formatDate, formatPercentValue, getLanguageId }; 55 | -------------------------------------------------------------------------------- /src/zip-manager/services/index.js: -------------------------------------------------------------------------------- 1 | import * as filesystemService from "./filesystem-service.js"; 2 | import * as downloadService from "./download-service.js"; 3 | import * as i18nService from "./i18n-service.js"; 4 | import * as storageService from "./storage-service.js"; 5 | import * as zipService from "./zip-service.js"; 6 | import * as shareTargetService from "./share-target-service.js"; 7 | import * as fileHandlersService from "./file-handlers-service.js"; 8 | import * as stylesheetService from "./stylesheet-service.js"; 9 | import * as environmentService from "./environment-service.js"; 10 | import * as keyboardService from "./keyboard-service.js"; 11 | import * as themeService from "./theme-service.js"; 12 | import * as documentService from "./document-service.js"; 13 | import * as windowService from "./window-service.js"; 14 | import * as musicService from "./music-service.js"; 15 | 16 | export { 17 | filesystemService, 18 | downloadService, 19 | i18nService, 20 | storageService, 21 | zipService, 22 | shareTargetService, 23 | fileHandlersService, 24 | stylesheetService, 25 | environmentService, 26 | keyboardService, 27 | themeService, 28 | documentService, 29 | windowService, 30 | musicService 31 | }; 32 | -------------------------------------------------------------------------------- /src/zip-manager/services/keyboard-service.js: -------------------------------------------------------------------------------- 1 | /* global addEventListener, removeEventListener */ 2 | 3 | const KEYUP_EVENT_NAME = "keyup"; 4 | const KEYDOWN_EVENT_NAME = "keydown"; 5 | 6 | function addKeyUpListener(listener) { 7 | addEventListener(KEYUP_EVENT_NAME, listener); 8 | } 9 | 10 | function removeKeyUpListener(listener) { 11 | removeEventListener(KEYUP_EVENT_NAME, listener); 12 | } 13 | 14 | function addKeyDownListener(listener) { 15 | addEventListener(KEYDOWN_EVENT_NAME, listener); 16 | } 17 | 18 | function removeKeyDownListener(listener) { 19 | removeEventListener(KEYDOWN_EVENT_NAME, listener); 20 | } 21 | 22 | export { 23 | addKeyUpListener, 24 | removeKeyUpListener, 25 | addKeyDownListener, 26 | removeKeyDownListener 27 | }; 28 | -------------------------------------------------------------------------------- /src/zip-manager/services/music-service-constants.js: -------------------------------------------------------------------------------- 1 | const MIDI_CONTENT_TYPE = "audio/midi"; 2 | const XM_CONTENT_TYPE = "audio/xm"; 3 | const SID_CONTENT_TYPE = "audio/prs.sid"; 4 | const MUSIC_TRACK_PATH_PREFIX = "music-track-"; 5 | const MUSIC_TRACK_PATH_REGEXP = new RegExp(MUSIC_TRACK_PATH_PREFIX + ".*"); 6 | const MUSIC_TRACK_INDEX_REGEXP = /\d+$/; 7 | const MUSIC_TRACKS_PATH = "./assets/music/tracks.zip"; 8 | const MIDI_FILE_EXTENSION = ".mid"; 9 | const XM_FILE_EXTENSION = ".xm"; 10 | const SID_FILE_EXTENSION = ".sid"; 11 | const MUSIC_FILE_CONTENT_TYPES = [ 12 | { extension: MIDI_FILE_EXTENSION, type: MIDI_CONTENT_TYPE }, 13 | { extension: XM_FILE_EXTENSION, type: XM_CONTENT_TYPE }, 14 | { extension: SID_FILE_EXTENSION, type: SID_CONTENT_TYPE } 15 | ]; 16 | 17 | export { 18 | MIDI_CONTENT_TYPE, 19 | XM_CONTENT_TYPE, 20 | SID_CONTENT_TYPE, 21 | MUSIC_TRACK_PATH_PREFIX, 22 | MUSIC_TRACK_PATH_REGEXP, 23 | MUSIC_TRACK_INDEX_REGEXP, 24 | MUSIC_TRACKS_PATH, 25 | MUSIC_FILE_CONTENT_TYPES 26 | }; 27 | -------------------------------------------------------------------------------- /src/zip-manager/services/music-service.js: -------------------------------------------------------------------------------- 1 | /* global document, requestAnimationFrame, fetch */ 2 | 3 | import WebAudioTinySynth from "./lib/webaudio-tinysynth/webaudio-tinysynth-core-es6.js"; 4 | import * as libxm from "./lib/libxm/libxm-es6.js"; 5 | import * as jsSID from "./lib/jsSID/jsSID.js"; 6 | 7 | import { 8 | MUSIC_TRACK_PATH_PREFIX, 9 | MIDI_CONTENT_TYPE, 10 | XM_CONTENT_TYPE, 11 | SID_CONTENT_TYPE 12 | } from "./music-service-constants.js"; 13 | 14 | const MUSIC_TRACK_RELATIVE_PATH_PREFIX = "./" + MUSIC_TRACK_PATH_PREFIX; 15 | const MUSIC_TRACKS_INFO = [ 16 | { masterVolume: 0.1 }, 17 | { masterVolume: 0.7 }, 18 | { masterVolume: 0.4 }, 19 | { masterVolume: 0.1 }, 20 | { masterVolume: 1.2, track: 1 }, 21 | { masterVolume: 1.8 }, 22 | { masterVolume: 0.6 }, 23 | { masterVolume: 0.8 }, 24 | { masterVolume: 1.1 }, 25 | { masterVolume: 0.5 }, 26 | { masterVolume: 0.7 }, 27 | { masterVolume: 0.7 } 28 | ]; 29 | 30 | let trackIndex = Math.floor(Math.random() * MUSIC_TRACKS_INFO.length); 31 | let midiLibrary, 32 | xmLibrary, 33 | sidLibrary, 34 | musicLibrary, 35 | playing, 36 | analyser, 37 | byteFrequencyData, 38 | callbackFrequencyData, 39 | fftSize = 128; 40 | 41 | document.onvisibilitychange = () => { 42 | if (musicLibrary && playing) { 43 | if (document.hidden) { 44 | musicLibrary.pause(); 45 | } else { 46 | musicLibrary.resume(); 47 | requestAnimationFrame(getByteFrequencyData); 48 | } 49 | } 50 | }; 51 | 52 | async function init({ data, contentType, masterVolume, track }) { 53 | musicLibrary = null; 54 | if (contentType === MIDI_CONTENT_TYPE) { 55 | initMIDI(); 56 | } else if (contentType === XM_CONTENT_TYPE) { 57 | await initXM(); 58 | } else if (contentType === SID_CONTENT_TYPE) { 59 | initSID(); 60 | } 61 | if (musicLibrary) { 62 | musicLibrary.play({ data, masterVolume, track }); 63 | initAnalyser(fftSize); 64 | } 65 | } 66 | 67 | function initMIDI() { 68 | if (!midiLibrary) { 69 | midiLibrary = new WebAudioTinySynth({ quality: 1, useReverb: 1 }); 70 | midiLibrary.setLoop(true); 71 | midiLibrary.setTimbre(1, 49, [ 72 | { w: "n0", f: 150, v: 0.2, d: 0.1, r: 0.1, h: 0.05, t: 0, p: 0.1 } 73 | ]); 74 | midiLibrary.setTimbre(1, 53, [ 75 | { w: "n0", f: 440, v: 0.3, d: 0.1, p: 0.9, t: 0, r: 0.1 } 76 | ]); 77 | midiLibrary.setTimbre(0, 28, [ 78 | { w: "sine", v: 0.8, d: 1, f: -1 }, 79 | { w: "triangle", v: 4, f: 1, d: 0.4, s: 0.5, g: 1, t: 3 } 80 | ]); 81 | midiLibrary.setTimbre(0, 38, [ 82 | { w: "sine", v: 0.6, d: 0.3 }, 83 | { w: "triangle", v: 4, f: 1, d: 0.4, s: 0.5, g: 1, t: 3 }, 84 | { w: "sawtooth", v: 0.1, a: 0.03, d: 0.5, s: 0.3, r: 0.2 } 85 | ]); 86 | midiLibrary.setTimbre(0, 39, [ 87 | { w: "sine", v: 0.5, d: 0.3 }, 88 | { w: "square", v: 3, f: -1, d: 1, s: 0.7, g: 1 }, 89 | { w: "sawtooth", v: 0.1, a: 0.03, d: 0.6, s: 0.3, r: 0.2 } 90 | ]); 91 | } 92 | musicLibrary = midiLibrary; 93 | } 94 | 95 | async function initXM() { 96 | if (!xmLibrary) { 97 | await libxm.init(); 98 | xmLibrary = libxm; 99 | } 100 | musicLibrary = xmLibrary; 101 | } 102 | 103 | function initSID() { 104 | if (!sidLibrary) { 105 | jsSID.init(); 106 | } 107 | musicLibrary = jsSID; 108 | } 109 | 110 | function initAnalyser() { 111 | analyser = musicLibrary.audioContext.createAnalyser(); 112 | musicLibrary.out.connect(analyser); 113 | analyser.fftSize = fftSize; 114 | byteFrequencyData = new Uint8Array(analyser.frequencyBinCount); 115 | } 116 | 117 | async function play({ onSetFrequencyData }) { 118 | const response = await fetch( 119 | MUSIC_TRACK_RELATIVE_PATH_PREFIX + (trackIndex + 1) 120 | ); 121 | const blob = await response.blob(); 122 | const contentType = blob.type; 123 | const data = await blob.arrayBuffer(); 124 | const { masterVolume, track } = MUSIC_TRACKS_INFO[trackIndex]; 125 | trackIndex = (trackIndex + 1) % MUSIC_TRACKS_INFO.length; 126 | await init({ data, contentType, masterVolume, track }); 127 | playing = true; 128 | callbackFrequencyData = onSetFrequencyData; 129 | if (musicLibrary) { 130 | requestAnimationFrame(getByteFrequencyData); 131 | } 132 | return {}; 133 | } 134 | 135 | function getByteFrequencyData() { 136 | analyser.getByteFrequencyData(byteFrequencyData); 137 | if (callbackFrequencyData) { 138 | callbackFrequencyData(Array.from(byteFrequencyData)); 139 | } 140 | if (playing && !document.hidden) { 141 | requestAnimationFrame(getByteFrequencyData); 142 | } 143 | } 144 | 145 | function stop() { 146 | playing = false; 147 | if (musicLibrary) { 148 | musicLibrary.pause(); 149 | } 150 | } 151 | 152 | function setFftSize(value) { 153 | fftSize = value; 154 | if (analyser) { 155 | initAnalyser(); 156 | } 157 | } 158 | 159 | export { play, stop, setFftSize }; 160 | -------------------------------------------------------------------------------- /src/zip-manager/services/share-target-service-constants.js: -------------------------------------------------------------------------------- 1 | const CURRENT_PATH = "."; 2 | const MAINPAGE_PATH = "/index.html"; 3 | const MAINPAGE_RELATIVE_PATH = CURRENT_PATH + MAINPAGE_PATH; 4 | const SHARED_FILES_PARAMETER = "?shared-files"; 5 | const MAINPAGE_REDIRECT_PATH = MAINPAGE_RELATIVE_PATH + SHARED_FILES_PARAMETER; 6 | const SHARED_FILES_PATH = "/shared-files"; 7 | const SHARED_FILES_RELATIVE_PATH = CURRENT_PATH + SHARED_FILES_PATH; 8 | const SHARED_FILES_FORM_PATH = SHARED_FILES_PATH + "/form"; 9 | const SHARED_FILES_CACHE_ID = "shared-files"; 10 | const SHARED_FILES_FIELD_NAME = "shared-files"; 11 | 12 | export { 13 | SHARED_FILES_PARAMETER, 14 | MAINPAGE_REDIRECT_PATH, 15 | SHARED_FILES_RELATIVE_PATH, 16 | SHARED_FILES_FORM_PATH, 17 | SHARED_FILES_CACHE_ID, 18 | SHARED_FILES_FIELD_NAME 19 | }; 20 | -------------------------------------------------------------------------------- /src/zip-manager/services/share-target-service.js: -------------------------------------------------------------------------------- 1 | /* global fetch, location, history */ 2 | 3 | import { 4 | SHARED_FILES_PARAMETER, 5 | SHARED_FILES_RELATIVE_PATH, 6 | SHARED_FILES_FIELD_NAME 7 | } from "./share-target-service-constants.js"; 8 | 9 | async function onShareFiles(callback) { 10 | const locationSearch = getLocationSearch(); 11 | if (locationSearch) { 12 | resetLocationSearch(); 13 | if (locationSearch === SHARED_FILES_PARAMETER) { 14 | const sharedFilesPath = SHARED_FILES_RELATIVE_PATH; 15 | const response = await fetch(sharedFilesPath); 16 | const formData = await response.formData(); 17 | callback(formData.getAll(SHARED_FILES_FIELD_NAME)); 18 | } 19 | } 20 | } 21 | 22 | function getLocationSearch() { 23 | return location.search; 24 | } 25 | 26 | function resetLocationSearch() { 27 | return history.replaceState(null, null, location.pathname); 28 | } 29 | 30 | export { onShareFiles }; 31 | -------------------------------------------------------------------------------- /src/zip-manager/services/storage-service.js: -------------------------------------------------------------------------------- 1 | /* global localStorage */ 2 | 3 | function setValue(key, value) { 4 | localStorage.setItem(key, JSON.stringify(value)); 5 | } 6 | 7 | function getValue(key) { 8 | const value = localStorage.getItem(key); 9 | try { 10 | return JSON.parse(value); 11 | // eslint-disable-next-line no-unused-vars 12 | } catch (error) { 13 | return value; 14 | } 15 | } 16 | 17 | export { setValue as set, getValue as get }; 18 | -------------------------------------------------------------------------------- /src/zip-manager/services/stylesheet-service.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | const styleElement = document.createElement("style"); 4 | document.head.appendChild(styleElement); 5 | 6 | function setStyle(name, value) { 7 | const rule = Array.from(styleElement.sheet.rules).find((rule) => 8 | rule.style.getPropertyValue(name) 9 | ); 10 | if (rule) { 11 | rule.style.setProperty(name, value); 12 | } else { 13 | styleElement.sheet.insertRule(":root { " + name + ": " + value + "}"); 14 | } 15 | } 16 | 17 | export { setStyle }; 18 | -------------------------------------------------------------------------------- /src/zip-manager/services/theme-service.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | const ACCENT_COLOR_CUSTOM_PROPERTY_NAME = "--accent-color"; 4 | 5 | function setTheme({ accentColor, skin }) { 6 | const documentClasses = [skin]; 7 | const brightNessAccentColor = getBrightNess(accentColor); 8 | if (brightNessAccentColor > 224) { 9 | documentClasses.push("dark"); 10 | } else if (brightNessAccentColor < 32) { 11 | documentClasses.push("light"); 12 | } 13 | document.documentElement.className = documentClasses.join(" "); 14 | } 15 | 16 | function getBrightNess(color) { 17 | const red = parseInt(color.substring(1, 3), 16); 18 | const green = parseInt(color.substring(3, 5), 16); 19 | const blue = parseInt(color.substring(5, 7), 16); 20 | // cf. https://www.w3.org/TR/AERT/#color-contrast 21 | return Math.round((red * 299 + green * 587 + blue * 114) / 1000); 22 | } 23 | 24 | export { ACCENT_COLOR_CUSTOM_PROPERTY_NAME, setTheme }; 25 | -------------------------------------------------------------------------------- /src/zip-manager/services/window-service.js: -------------------------------------------------------------------------------- 1 | /* global addEventListener, removeEventListener */ 2 | 3 | const RESIZE_EVENT_NAME = "resize"; 4 | const BEFORE_UNLOAD_EVENT_NAME = "beforeunload"; 5 | 6 | function addUnloadListener(listener) { 7 | addEventListener(BEFORE_UNLOAD_EVENT_NAME, listener); 8 | } 9 | 10 | function removeUnloadListener(listener) { 11 | removeEventListener(BEFORE_UNLOAD_EVENT_NAME, listener); 12 | } 13 | 14 | function addResizeListener(listener) { 15 | addEventListener(RESIZE_EVENT_NAME, listener); 16 | } 17 | 18 | function removeResizeListener(listener) { 19 | removeEventListener(RESIZE_EVENT_NAME, listener); 20 | } 21 | 22 | export { 23 | addUnloadListener, 24 | removeUnloadListener, 25 | addResizeListener, 26 | removeResizeListener 27 | }; 28 | -------------------------------------------------------------------------------- /src/zip-manager/services/zip-service.js: -------------------------------------------------------------------------------- 1 | import { fs, configure } from "@zip.js/zip.js"; 2 | 3 | const { FS } = fs; 4 | 5 | function createZipFileSystem() { 6 | return new FS(); 7 | } 8 | 9 | export { createZipFileSystem, configure }; 10 | -------------------------------------------------------------------------------- /src/zip-manager/styles/ButtonBar.css: -------------------------------------------------------------------------------- 1 | .button-bar, 2 | .button-bar .button-group { 3 | display: flex; 4 | flex-wrap: wrap; 5 | row-gap: var(--gap-block-size); 6 | } 7 | 8 | .button-bar { 9 | column-gap: var(--gap-inline-size); 10 | } 11 | 12 | .button-bar .button-group { 13 | column-gap: var(--small-gap-inline-size); 14 | } 15 | 16 | .button-bar button:not([disabled]) { 17 | cursor: pointer; 18 | } -------------------------------------------------------------------------------- /src/zip-manager/styles/Dialog.css: -------------------------------------------------------------------------------- 1 | dialog::backdrop { 2 | background-color: var(--basic-bg-color); 3 | opacity: 0.85; 4 | } 5 | 6 | dialog { 7 | margin-block-start: var(--dialog-margin-block-start); 8 | inline-size: var(--dialog-width); 9 | background-color: var(--page-color); 10 | color: var(--text-color); 11 | border: solid var(--accent-color) var(--large-border-width); 12 | border-radius: var(--border-radius-width); 13 | padding-inline: var(--large-gap-inline-size); 14 | padding-block: var(--large-gap-block-size); 15 | } 16 | 17 | dialog:focus { 18 | outline-width: 0; 19 | } 20 | 21 | dialog input, 22 | dialog select { 23 | background-color: var(--bg-color); 24 | color: var(--text-color); 25 | border: solid var(--accent-color) var(--border-width); 26 | block-size: 1.5em; 27 | padding-inline: var(--gap-inline-size); 28 | font-family: inherit; 29 | margin: var(--outline-width); 30 | border-radius: var(--border-radius-width); 31 | box-sizing: content-box; 32 | caret-color: var(--accent-color); 33 | align-self: baseline; 34 | inline-size: calc(100% - 2 * (var(--gap-inline-size) + var(--border-width) + var(--outline-width))); 35 | align-self: end; 36 | } 37 | 38 | dialog form, 39 | dialog form p, 40 | dialog label { 41 | display: flex; 42 | flex-direction: column; 43 | } 44 | 45 | dialog form, 46 | dialog form p { 47 | row-gap: var(--large-gap-block-size); 48 | } 49 | 50 | dialog form { 51 | max-block-size: var(--dialog-form-max-block-size); 52 | } 53 | 54 | dialog form .dialog-title { 55 | font-weight: var(--font-weight-bolder); 56 | border-block-end: solid var(--accent-color) var(--border-width); 57 | padding-block-end: var(--large-gap-block-size); 58 | } 59 | 60 | dialog form p { 61 | margin: 0; 62 | padding-block-start: var(--small-gap-block-size); 63 | padding-block-end: var(--large-gap-block-size); 64 | overflow: visible auto; 65 | } 66 | 67 | dialog label { 68 | row-gap: var(--gap-block-size); 69 | overflow: visible; 70 | } 71 | 72 | dialog .button-bar { 73 | margin-block-start: var(--gap-block-size); 74 | } 75 | 76 | dialog .button-group:first-of-type { 77 | flex: 1; 78 | } -------------------------------------------------------------------------------- /src/zip-manager/styles/ListItem.css: -------------------------------------------------------------------------------- 1 | .list-item-button { 2 | font-family: monospace; 3 | cursor: pointer; 4 | user-select: none; 5 | } 6 | 7 | .list-item-button:hover { 8 | filter: brightness(var(--brightness-hover)); 9 | } 10 | 11 | .list-item-name { 12 | overflow-x: hidden; 13 | overflow-inline: hidden; 14 | text-overflow: ellipsis; 15 | white-space: nowrap; 16 | } -------------------------------------------------------------------------------- /src/zip-manager/styles/Skins.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "DOS-VGA-437"; 3 | src: url("/assets/fonts/dos-vga-9x16.ttf"); 4 | font-display: swap; 5 | } 6 | 7 | .skin-dos { 8 | --gap-inline-size: 2ch; 9 | --gap-block-size: calc(1em * 1.25); 10 | --border-width: 0; 11 | --button-padding-inline-size: var(--gap-inline-size); 12 | --button-padding-block-size: 0; 13 | --border-radius-width: 0; 14 | --list-item-padding-inline-size: 0; 15 | --list-item-padding-block-size: 0; 16 | --outline-color: var(--accent-color); 17 | --outline-width: 0; 18 | --dialog-margin-block-start: calc(var(--gap-block-size) + var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 19 | } 20 | 21 | .skin-dos * { 22 | line-height: 1.25; 23 | } 24 | 25 | .skin-dos body { 26 | font-family: "DOS-VGA-437", monospace; 27 | } 28 | 29 | .skin-dos body::after { 30 | top: 0; 31 | left: 0; 32 | width: 100%; 33 | height: 100%; 34 | } 35 | 36 | .skin-dos dialog[open] form::after { 37 | top: calc(-1 * var(--smaller-gap-inline-size)); 38 | left: calc(-1 * var(--smaller-gap-block-size)); 39 | width: calc(100% + 2 * var(--small-gap-inline-size)); 40 | height: calc(100% + 2 * var(--small-gap-block-size)); 41 | } 42 | 43 | .skin-dos main, 44 | .skin-dos dialog { 45 | position: relative; 46 | overflow: visible; 47 | border-width: var(--smaller-gap-block-size) var(--smaller-gap-inline-size); 48 | } 49 | 50 | .skin-dos main::before, 51 | .skin-dos dialog::before, 52 | .skin-dos main:after, 53 | .skin-dos dialog::after { 54 | position: absolute; 55 | content: ""; 56 | background-color: var(--darker-accent-color); 57 | } 58 | 59 | .skin-dos main::before { 60 | top: var(--smaller-gap-block-size); 61 | right: calc(-1 * var(--smaller-gap-inline-size) - var(--small-gap-inline-size)); 62 | inline-size: var(--small-gap-inline-size); 63 | block-size: calc(100% + var(--small-gap-block-size) - var(--smaller-gap-block-size) + var(--smaller-gap-block-size) / 2); 64 | } 65 | 66 | .skin-dos main:after { 67 | left: var(--smaller-gap-inline-size); 68 | bottom: calc(-1 * var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 69 | inline-size: calc(100% + var(--small-gap-inline-size)); 70 | block-size: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 71 | } 72 | 73 | .skin-dos dialog::before { 74 | right: calc(-1 * var(--smaller-gap-inline-size) - var(--small-gap-inline-size)); 75 | inline-size: var(--small-gap-inline-size); 76 | block-size: calc(100% + var(--small-gap-block-size) - var(--smaller-gap-block-size)); 77 | } 78 | 79 | .skin-dos dialog::after { 80 | left: var(--smaller-gap-inline-size); 81 | bottom: calc(-1 * var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 82 | inline-size: calc(100% + var(--small-gap-inline-size)); 83 | block-size: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 84 | } 85 | 86 | .skin-dos main { 87 | margin-block-start: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 88 | min-inline-size: min(85dvw, 132ch); 89 | inline-size: min(85dvw, 132ch); 90 | inline-size: min(round(up, 85dvw, 1ch), 132ch); 91 | } 92 | 93 | .skin-dos main, 94 | .skin-dos main>*, 95 | .skin-dos dialog { 96 | border-style: double; 97 | } 98 | 99 | .skin-dos main>* { 100 | padding-inline: var(--smaller-gap-inline-size); 101 | padding-block: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 102 | border-block-end-width: var(--smaller-gap-block-size); 103 | } 104 | 105 | .skin-dos button { 106 | margin: 0; 107 | } 108 | 109 | .skin-dos button:focus { 110 | filter: brightness(var(--brightness-hover)); 111 | outline-width: 0; 112 | } 113 | 114 | .skin-dos .scrollable::-webkit-scrollbar { 115 | inline-size: var(--small-gap-inline-size); 116 | background-color: var(--bg-disabled-button-color); 117 | } 118 | 119 | .skin-dos .scrollable::-webkit-scrollbar-thumb, 120 | .skin-dos .scrollable::-webkit-scrollbar-track { 121 | border-radius: 0; 122 | } 123 | 124 | .skin-dos input[type="checkbox"] { 125 | font-family: "DOS-VGA-437", monospace; 126 | accent-color: var(--text-color); 127 | appearance: none; 128 | cursor: pointer; 129 | } 130 | 131 | .skin-dos input[type="checkbox"]:after { 132 | content: "[ ]"; 133 | } 134 | 135 | .skin-dos input[type="checkbox"]:focus:after { 136 | background-color: var(--text-color); 137 | color: var(--bg-color); 138 | } 139 | 140 | .skin-dos input[type="checkbox"]:checked::after { 141 | content: "[■]"; 142 | } 143 | 144 | .skin-dos .button-bar, 145 | .skin-dos .button-bar .button-group { 146 | row-gap: var(--small-gap-block-size); 147 | } 148 | 149 | .skin-dos .history-buttons button { 150 | font-family: "DOS-VGA-437", monospace; 151 | padding-inline: var(--button-padding-inline-size); 152 | } 153 | 154 | .skin-dos .navigation-bar { 155 | column-gap: var(--small-gap-inline-size); 156 | } 157 | 158 | .skin-dos .breadcrumb ol, 159 | .skin-dos .breadcrumb li, 160 | .skin-dos .entries .entry-name { 161 | column-gap: var(--small-gap-inline-size); 162 | } 163 | 164 | .skin-dos .breadcrumb .breadcrumb-item-active, 165 | .skin-dos .breadcrumb .breadcrumb-item-active:focus { 166 | text-decoration: underline var(--accent-color) var(--large-border-width); 167 | } 168 | 169 | .skin-dos .breadcrumb .breadcrumb-item { 170 | margin: 0; 171 | } 172 | 173 | .skin-dos .breadcrumb .breadcrumb-item-active:focus { 174 | outline: 0; 175 | filter: brightness(var(--brightness-hover)); 176 | } 177 | 178 | .skin-dos .entries { 179 | padding-block-start: 0; 180 | margin-block-start: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 181 | } 182 | 183 | .skin-dos .entries li { 184 | column-gap: var(--small-gap-inline-size); 185 | } 186 | 187 | .skin-dos .entries .entry-select { 188 | color: var(--text-color); 189 | } 190 | 191 | .skin-dos .entries .entry-select:checked { 192 | color: var(--bg-color); 193 | } 194 | 195 | .skin-dos .entries .entry-select:focus:after { 196 | background-color: var(--bg-color); 197 | color: var(--text-color); 198 | } 199 | 200 | .skin-dos .entries .entry-select:checked:focus:after { 201 | background-color: var(--accent-color); 202 | color: var(--bg-color); 203 | } 204 | 205 | .skin-dos .info-bar { 206 | margin-block-end: 0; 207 | padding-block: var(--gap-block-size); 208 | min-block-size: var(--gap-block-size); 209 | font-size: 0.8em; 210 | } 211 | 212 | .skin-dos .info-bar canvas { 213 | bottom: calc(-1.25 * var(--gap-block-size)); 214 | } 215 | 216 | .skin-dos dialog { 217 | min-inline-size: min(75dvw, 50ch); 218 | inline-size: min(75dvw, 50ch); 219 | inline-size: min(round(up, 75dvw, 1ch), 50ch); 220 | padding-inline: var(--smaller-gap-inline-size); 221 | padding-block: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 2); 222 | } 223 | 224 | .skin-dos dialog form, 225 | .skin-dos dialog form p { 226 | row-gap: 0; 227 | } 228 | 229 | .skin-dos dialog form p { 230 | margin: 0; 231 | padding-inline: var(--small-gap-inline-size); 232 | padding-block-start: var(--large-gap-block-size); 233 | } 234 | 235 | .skin-dos dialog form .dialog-title { 236 | padding-inline-start: var(--small-gap-inline-size); 237 | padding-block-end: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 4); 238 | border-block-end: solid var(--accent-color) calc(var(--smaller-gap-block-size) / 2); 239 | } 240 | 241 | .skin-dos dialog form p { 242 | row-gap: var(--gap-block-size); 243 | padding-block-start: calc(var(--small-gap-block-size) - var(--smaller-gap-block-size) / 4); 244 | padding-block-end: 0; 245 | } 246 | 247 | .skin-dos dialog label { 248 | row-gap: 0; 249 | } 250 | 251 | .skin-dos dialog input { 252 | appearance: textfield; 253 | caret-shape: underscore; 254 | } 255 | 256 | .skin-dos dialog select { 257 | appearance: none; 258 | } 259 | 260 | .skin-dos dialog input::-webkit-inner-spin-button { 261 | appearance: none; 262 | } 263 | 264 | .skin-dos dialog input, 265 | .skin-dos dialog select { 266 | padding: 0; 267 | block-size: var(--gap-block-size); 268 | inline-size: 100%; 269 | } 270 | 271 | .skin-dos dialog select:focus { 272 | background-color: var(--text-color); 273 | color: var(--bg-color); 274 | } 275 | 276 | .skin-dos .options-dialog label { 277 | min-block-size: auto; 278 | } 279 | 280 | .skin-dos .options-dialog form p { 281 | row-gap: 0; 282 | } 283 | 284 | .skin-dos .options-dialog input[type="checkbox"] { 285 | inline-size: inherit; 286 | block-size: inherit; 287 | accent-color: inherit; 288 | } 289 | 290 | .skin-dos .options-dialog input, 291 | .skin-dos .options-dialog select { 292 | inline-size: min(10ch, 100%); 293 | text-align: right; 294 | } 295 | 296 | .skin-dos .downloads, 297 | .main-container.hidden-downloads .button-bar-bottom { 298 | border-block-end-width: 0; 299 | } 300 | 301 | @media (display-mode: standalone) or (orientation: portrait) or (max-aspect-ratio: 7/5) { 302 | 303 | .skin-dos .main-container:not(.hidden-footer) .downloads, 304 | .main-container.hidden-downloads:not(.hidden-footer) .button-bar-bottom { 305 | border-block-end-width: var(--smaller-gap-block-size); 306 | } 307 | 308 | .skin-dos main { 309 | inline-size: 100%; 310 | inline-size: round(down, 100%, 1ch); 311 | margin-block-start: 0; 312 | border-width: 0; 313 | } 314 | 315 | .skin-dos main::before, 316 | .skin-dos main:after { 317 | content: none; 318 | } 319 | 320 | .skin-dos main> :first-child { 321 | padding-block-start: var(--small-gap-block-size); 322 | padding-inline: var(--small-gap-inline-size); 323 | } 324 | 325 | .skin-dos main>* { 326 | padding-inline: var(--small-gap-inline-size); 327 | } 328 | 329 | .skin-dos .info-bar { 330 | margin-block-start: 0; 331 | padding-block: var(--small-gap-inline-size); 332 | } 333 | 334 | .skin-dos .info-bar canvas { 335 | bottom: calc(-0.75 * var(--gap-block-size)); 336 | } 337 | 338 | .skin-dos dialog { 339 | margin-block-start: calc(var(--gap-block-size) - var(--smaller-gap-block-size) / 2); 340 | } 341 | } -------------------------------------------------------------------------------- /src/zip-manager/styles/ZipManager.css: -------------------------------------------------------------------------------- 1 | .main-container, 2 | main { 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | .main-container { 8 | block-size: 100dvh; 9 | align-items: center; 10 | } 11 | 12 | main, 13 | .hidden-downloads .entries { 14 | flex: 1; 15 | } 16 | 17 | main, 18 | main>* { 19 | border-style: solid; 20 | border-color: var(--accent-color); 21 | border-width: 0; 22 | } 23 | 24 | main { 25 | background-color: var(--bg-color); 26 | color: var(--text-color); 27 | margin-inline: var(--gap-inline-size); 28 | margin-block: var(--gap-block-size); 29 | border-width: var(--border-width); 30 | inline-size: var(--app-width); 31 | min-block-size: 20em; 32 | border-radius: var(--border-radius-width); 33 | } 34 | 35 | .main-container:not(.hidden-footer) main { 36 | margin-block-end: 0; 37 | } 38 | 39 | main>* { 40 | padding-inline: var(--gap-inline-size); 41 | padding-block: var(--gap-block-size); 42 | border-block-end-width: var(--border-width); 43 | } 44 | 45 | .hidden-downloads .entries { 46 | resize: none; 47 | } 48 | 49 | .downloads, 50 | .main-container.hidden-downloads .button-bar-bottom { 51 | border-block-end-width: 0; 52 | } 53 | 54 | @media (display-mode: standalone) or (orientation: portrait) or (max-aspect-ratio: 7/5) { 55 | main { 56 | inline-size: 100dvw; 57 | margin: 0; 58 | border-width: 0; 59 | margin-block-end: 0; 60 | } 61 | 62 | .main-container:not(.hidden-footer) .downloads, 63 | .main-container.hidden-downloads:not(.hidden-footer) .button-bar-bottom { 64 | border-block-end-width: var(--border-width); 65 | } 66 | } -------------------------------------------------------------------------------- /src/zip-manager/styles/index.css: -------------------------------------------------------------------------------- 1 | @import url(./Base.css); 2 | @import url(./ListItem.css); 3 | @import url(./ButtonBar.css); 4 | @import url(./Dialog.css); 5 | @import url(./Skins.css); 6 | @import url(./ZipManager.css); -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import { VitePWA } from "vite-plugin-pwa"; 4 | 5 | export default defineConfig(() => { 6 | return { 7 | base: "./", 8 | build: { 9 | outDir: "build", 10 | target: "es2020", 11 | chunkSizeWarningLimit: 768 12 | }, 13 | plugins: [ 14 | react({ 15 | babel: { 16 | plugins: [ 17 | ["babel-plugin-react-compiler"] 18 | ] 19 | } 20 | }), 21 | VitePWA({ 22 | registerType: "autoUpdate", 23 | strategies: "injectManifest", 24 | srcDir: "src", 25 | filename: "sw.js", 26 | injectManifest: { 27 | rollupFormat: "iife", 28 | globPatterns: [ 29 | "./**/*.{js,css,png,ttf,wasm,zip}", 30 | "./*.{html,ico,png,js,json}" 31 | ] 32 | }, 33 | includeManifestIcons: false, 34 | manifest: { 35 | "short_name": "Zip Manager", 36 | "name": "Zip Manager", 37 | "description": "Read, edit and write zip files.", 38 | "start_url": "./index.html", 39 | "display": "fullscreen", 40 | "theme_color": "#000000", 41 | "background_color": "#ffffff", 42 | "orientation": "any", 43 | "categories": [ 44 | "utilities" 45 | ], 46 | "icons": [ 47 | { 48 | "src": "assets/icons/icon-512x512.png", 49 | "sizes": "512x512", 50 | "type": "image/png", 51 | "purpose": "any" 52 | }, 53 | { 54 | "src": "assets/icons/icon-192x192.png", 55 | "sizes": "192x192", 56 | "type": "image/png", 57 | "purpose": "any" 58 | }, 59 | { 60 | "src": "assets/icons/icon-512x512.png", 61 | "sizes": "512x512", 62 | "type": "image/png", 63 | "purpose": "maskable" 64 | }, 65 | { 66 | "src": "assets/icons/icon-192x192.png", 67 | "sizes": "192x192", 68 | "type": "image/png", 69 | "purpose": "maskable" 70 | }, 71 | { 72 | "src": "assets/icons/icon-512x512-mono.png", 73 | "sizes": "512x512", 74 | "type": "image/png", 75 | "purpose": "monochrome" 76 | }, 77 | { 78 | "src": "./assets/icons/icon-192x192-mono.png", 79 | "sizes": "192x192", 80 | "type": "image/png", 81 | "purpose": "monochrome" 82 | } 83 | ], 84 | "file_handlers": [ 85 | { 86 | "action": "./index.html", 87 | "accept": { 88 | "application/zip": [ 89 | ".zip" 90 | ], 91 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [ 92 | ".docx" 93 | ], 94 | "application/epub+zip": [ 95 | ".epub" 96 | ], 97 | "application/java-archive": [ 98 | ".jar" 99 | ], 100 | "application/vnd.oasis.opendocument.presentation": [ 101 | ".odp" 102 | ], 103 | "application/vnd.oasis.opendocument.spreadsheet": [ 104 | ".ods" 105 | ], 106 | "application/vnd.oasis.opendocument.text": [ 107 | ".odt" 108 | ], 109 | "application/vnd.openxmlformats-officedocument.presentationml.presentation": [ 110 | ".pptx" 111 | ], 112 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [ 113 | ".xlsx" 114 | ], 115 | "application/vnd.apple.keynote": [ 116 | ".key" 117 | ], 118 | "application/vnd.apple.pages": [ 119 | ".pages" 120 | ], 121 | "application/vnd.apple.numbers": [ 122 | ".numbers" 123 | ], 124 | "application/vnd.android.package-archive": [ 125 | ".apk" 126 | ], 127 | "application/x-ios-app": [ 128 | ".ipa" 129 | ] 130 | }, 131 | "launch_type": "single-client" 132 | } 133 | ], 134 | "share_target": { 135 | "action": "./shared-files", 136 | "enctype": "multipart/form-data", 137 | "method": "POST", 138 | "params": { 139 | "files": [ 140 | { 141 | "name": "shared-files", 142 | "accept": [ 143 | "*/*" 144 | ] 145 | } 146 | ] 147 | } 148 | }, 149 | "screenshots": [ 150 | { 151 | "src": "./screenshots/screenshot-395x640.png", 152 | "sizes": "395x640", 153 | "type": "image/png", 154 | "form_factor": "narrow", 155 | "label": "Main screen on mobile" 156 | }, 157 | { 158 | "src": "./screenshots/screenshot-app-1135x809.png", 159 | "sizes": "1135x809", 160 | "type": "image/png", 161 | "form_factor": "wide", 162 | "label": "Main screen on desktop" 163 | }, 164 | { 165 | "src": "./screenshots/screenshot-custom-395x640.png", 166 | "sizes": "395x640", 167 | "type": "image/png", 168 | "form_factor": "narrow", 169 | "label": "Custom user interface on mobile" 170 | } 171 | ] 172 | } 173 | }) 174 | ] 175 | }; 176 | }); 177 | --------------------------------------------------------------------------------