├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── build.yml ├── .yml ├── static ├── media │ ├── file.png │ ├── text.png │ ├── audio.png │ ├── folder.png │ ├── image.png │ ├── splash.jpeg │ ├── upload.png │ ├── video.png │ ├── droppoint.ico │ ├── multifile.png │ ├── droppoint.icns │ ├── pngLogo │ │ ├── droppoint.png │ │ ├── droppoint@2.5x.png │ │ ├── droppoint@2x.png │ │ ├── droppoint@5x.png │ │ ├── droppointMacTemplate.png │ │ ├── droppointMacDarkTemplate.png │ │ ├── droppointMacTemplate@2x.png │ │ ├── droppointMacTemplate@4x.png │ │ ├── droppointMacDarkTemplate@2x.png │ │ ├── droppointMacDarkTemplate@4x.png │ │ └── DropPoint Symbolic-01.svg │ ├── droppoint-social-cover.jpg │ └── tick_icon.svg ├── tailwind │ └── settings-input.css ├── settings.html ├── index.html └── settings.css ├── .deepsource.toml ├── .gitignore ├── tailwind.config.js ├── .vscode └── launch.json ├── src ├── configOptions.js ├── Icons.js ├── Settings.js ├── preload.js ├── Shortcut.js ├── App.js ├── History.js ├── Tray.js ├── RequestHandlers.js └── Window.js ├── package.json ├── docs ├── PULL_REQUEST_TEMPLATE.md └── CONTRIBUTING.md ├── renderer ├── settings-renderer.js └── droppoint.js ├── README.md └── LICENSE /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: GameGodS3 2 | custom: ['https://www.buymeacoffee.com/sudev'] 3 | -------------------------------------------------------------------------------- /.yml: -------------------------------------------------------------------------------- 1 | - name: Electron Builder Action 2 | uses: samuelmeuli/action-electron-builder@v1.6.0 3 | -------------------------------------------------------------------------------- /static/media/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/file.png -------------------------------------------------------------------------------- /static/media/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/text.png -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "javascript" 5 | enabled = false 6 | -------------------------------------------------------------------------------- /static/media/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/audio.png -------------------------------------------------------------------------------- /static/media/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/folder.png -------------------------------------------------------------------------------- /static/media/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/image.png -------------------------------------------------------------------------------- /static/media/splash.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/splash.jpeg -------------------------------------------------------------------------------- /static/media/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/upload.png -------------------------------------------------------------------------------- /static/media/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/video.png -------------------------------------------------------------------------------- /static/media/droppoint.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/droppoint.ico -------------------------------------------------------------------------------- /static/media/multifile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/multifile.png -------------------------------------------------------------------------------- /static/media/droppoint.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/droppoint.icns -------------------------------------------------------------------------------- /static/media/pngLogo/droppoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppoint.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /node_modules/* 3 | /release_builds/ 4 | build_installer.js 5 | test.html 6 | instanceHistory.json -------------------------------------------------------------------------------- /static/media/droppoint-social-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/droppoint-social-cover.jpg -------------------------------------------------------------------------------- /static/media/pngLogo/droppoint@2.5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppoint@2.5x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppoint@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppoint@2x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppoint@5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppoint@5x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacTemplate.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacDarkTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacDarkTemplate.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacTemplate@2x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacTemplate@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacTemplate@4x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacDarkTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacDarkTemplate@2x.png -------------------------------------------------------------------------------- /static/media/pngLogo/droppointMacDarkTemplate@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GameGodS3/DropPoint/HEAD/static/media/pngLogo/droppointMacDarkTemplate@4x.png -------------------------------------------------------------------------------- /static/tailwind/settings-input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .settings-content div { 6 | @apply my-1; 7 | } -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./static/*.html", 5 | "./renderer/*.js", 6 | "node_modules/preline/dist/*.js", 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [require("@tailwindcss/forms"), require("preline/plugin")], 12 | darkMode: "class", 13 | }; 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Main Process", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceFolder}", 9 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", 10 | "windows": { 11 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" 12 | }, 13 | "args": [ 14 | "." 15 | ], 16 | "outputCapture": "std" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/configOptions.js: -------------------------------------------------------------------------------- 1 | const defaultAppConfig = { 2 | spawnOnLaunch: true, 3 | alwaysOnTop: true, 4 | openAtCursorPosition: false, 5 | shortcutAction: "toggle", 6 | debug: false, 7 | }; 8 | const appConfigSchema = { 9 | spawnOnLaunch: { 10 | type: "boolean", 11 | title: "Open a new instance on launch" 12 | }, 13 | alwaysOnTop: { 14 | type: "boolean", 15 | title: "Always on top", 16 | }, 17 | openAtCursorPosition: { 18 | type: "boolean", 19 | title: "Open at cursor position", 20 | }, 21 | shortcutAction: { 22 | enum: ["toggle", "spawn"], 23 | type: "string", 24 | title: "Shortcut behaviour", 25 | }, 26 | debug: { 27 | type: "boolean", 28 | title: "Enable debug mode", 29 | }, 30 | }; 31 | module.exports = { 32 | defaults: defaultAppConfig, 33 | schema: appConfigSchema, 34 | }; 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows] 28 | - OS Version/Build [e.g. Big Sur] 29 | - DropPoint Version [e.g. v1.0.0] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /src/Icons.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { nativeTheme } = require("electron"); 3 | 4 | /** 5 | * Adds static folder path to image name 6 | * 7 | * @param {String} imageName 8 | * @returns {String} 9 | */ 10 | const joinStaticPath = (imageName) => 11 | path.join(__dirname, "../static/media/", imageName); 12 | 13 | const icons = { 14 | droppointDefaultIcon: 15 | process.platform !== "win32" 16 | ? process.platform === "darwin" 17 | ? nativeTheme.shouldUseDarkColors 18 | ? joinStaticPath("pngLogo/droppointMacDarkTemplate.png") 19 | : joinStaticPath("pngLogo/droppointMacTemplate.png") 20 | : joinStaticPath("pngLogo/droppoint.png") 21 | : joinStaticPath("droppoint.ico"), 22 | audio: joinStaticPath("audio.png"), 23 | file: joinStaticPath("file.png"), 24 | folder: joinStaticPath("folder.png"), 25 | image: joinStaticPath("image.png"), 26 | multifile: joinStaticPath("multifile.png"), 27 | text: joinStaticPath("text.png"), 28 | video: joinStaticPath("video.png"), 29 | }; 30 | 31 | module.exports = icons; 32 | -------------------------------------------------------------------------------- /static/media/tick_icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Settings.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const { BrowserWindow, webContents, nativeImage } = require("electron"); 4 | const Store = require("electron-store"); 5 | const { droppointDefaultIcon } = require("./Icons"); 6 | const configOptions = require("./configOptions"); 7 | 8 | class Settings { 9 | constructor() { 10 | this.settings = null; 11 | this.settingsWindowConfig = { 12 | title: "Settings - DropPoint", 13 | }; 14 | } 15 | 16 | openSettings() { 17 | const allWindows = BrowserWindow.getAllWindows(); 18 | for (let i = 0; i < allWindows.length; i++) { 19 | const win = allWindows[i]; 20 | if (win.getTitle().split(" ")[0] === "Settings") { 21 | return; 22 | } 23 | } 24 | this.settings = new BrowserWindow({ 25 | title: "Settings - DropPoint", 26 | width: 600, 27 | height: 450, 28 | webPreferences: { 29 | nodeIntegration: true, 30 | preload: path.join(__dirname, "preload.js"), 31 | icon: nativeImage.createFromPath(droppointDefaultIcon), 32 | }, 33 | }); 34 | // this.settings.webContents.openDevTools(); // For Debugging purposes 35 | this.settings.removeMenu(); 36 | const html_path = path.join(__dirname, "../static/settings.html"); 37 | this.settings.loadURL(`file://${html_path}`).then(() => { }); 38 | } 39 | } 40 | 41 | module.exports = { 42 | Settings: Settings, 43 | }; 44 | -------------------------------------------------------------------------------- /src/preload.js: -------------------------------------------------------------------------------- 1 | const { contextBridge, ipcRenderer } = require("electron"); 2 | // const path = require("path"); 3 | 4 | contextBridge.exposeInMainWorld("electron", { 5 | getLatestInstanceId: () => { 6 | ipcRenderer.send("getLatestInstanceId"); 7 | }, 8 | dragOutListener: (params) => { 9 | ipcRenderer.send("ondragstart", params); 10 | }, 11 | minimise: () => { 12 | ipcRenderer.send("minimise"); 13 | }, 14 | debugPrint: (message) => { 15 | ipcRenderer.send("debugPrint", message); 16 | }, 17 | fetchConfig: () => { 18 | ipcRenderer.send("fetchConfig"); 19 | }, 20 | onConfigReceived: (callback) => { 21 | ipcRenderer.on("configObj", callback) 22 | }, 23 | applySettingsInConfig: (newConfig) => { 24 | ipcRenderer.send("applySettings", newConfig) 25 | }, 26 | }); 27 | 28 | // For settings renderer 29 | let configObj; 30 | const updateConfigObj = (config) => { 31 | configObj = config; 32 | console.log(configObj); 33 | }; 34 | 35 | ipcRenderer.on("configObj", (event, config) => { 36 | configObj = JSON.parse(config); 37 | const configFileContents = require(configObj.config.path); 38 | console.log(configFileContents); 39 | ipcRenderer.sendToHost(config); 40 | return configFileContents; 41 | }); 42 | 43 | ipcRenderer.on("close-signal", (event) => { 44 | window.close(); 45 | }); 46 | ipcRenderer.on("history-instance", (event, filelist) => { }); 47 | 48 | console.log("preload"); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "droppoint", 3 | "productName": "DropPoint", 4 | "version": "1.3.1", 5 | "description": "Drag-and-drop assist", 6 | "main": "./src/App.js", 7 | "scripts": { 8 | "start": "electron .", 9 | "build": "electron-builder", 10 | "release": "electron-builder --publish always" 11 | }, 12 | "keywords": [], 13 | "author": { 14 | "name": "Sudev Suresh Sreedevi", 15 | "email": "sudevssuresh@gmail.com" 16 | }, 17 | "license": "GPL-3.0-or-later", 18 | "build": { 19 | "appId": "com.gamegods3.droppoint", 20 | "productName": "DropPoint", 21 | "mac": { 22 | "category": "public.app-category.utilities" 23 | }, 24 | "dmg": { 25 | "icon": false 26 | }, 27 | "linux": { 28 | "target": [ 29 | "AppImage", 30 | "deb", 31 | "tar.gz", 32 | "rpm" 33 | ], 34 | "category": "Utility" 35 | }, 36 | "win": { 37 | "publisherName": "Sudev Suresh Sreedevi", 38 | "target": [ 39 | "zip", 40 | "nsis" 41 | ] 42 | } 43 | }, 44 | "dependencies": { 45 | "electron-store": "^8.1.0", 46 | "electron-updater": "^4.3.9", 47 | "preline": "^1.1.2" 48 | }, 49 | "devDependencies": { 50 | "@tailwindcss/forms": "^0.5.3", 51 | "electron": "^21.3.1", 52 | "electron-builder": "^23.0.2", 53 | "tailwindcss": "^3.1.8" 54 | }, 55 | "optionalDependencies": { 56 | "dmg-license": "^1.0.11" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Shortcut.js: -------------------------------------------------------------------------------- 1 | const { globalShortcut, app, webContents } = require("electron"); 2 | const { Instance } = require("./Window"); 3 | const Store = require("electron-store"); 4 | const configOptions = require("./configOptions"); 5 | 6 | /** 7 | * Sets Shift + Caps Lock as Shortcut. Change to convenience 8 | */ 9 | const setShortcut = () => { 10 | let shortcut = "Shift+Capslock"; 11 | 12 | if (process.platform === "darwin") { 13 | //caps lock is not a modifier in mac 14 | shortcut = "Shift+Tab"; 15 | 16 | //handle macos cmd q quitting 17 | globalShortcut.register("Cmd+Q", () => { 18 | app.exit(); 19 | }); 20 | } 21 | 22 | const ret = globalShortcut.register(shortcut, () => { 23 | const active_instances = webContents.getAllWebContents(); 24 | const config = new Store(configOptions); 25 | 26 | console.log("Active Instances: " + active_instances); 27 | if (config.get("shortcutAction") === "toggle") { 28 | if (active_instances.length === 1) { 29 | const instance = new Instance(); 30 | if (instance.createNewWindow() !== null) { 31 | console.log("New Window created"); 32 | } 33 | } else { 34 | active_instances[0].send("close-signal"); 35 | } 36 | } else { 37 | const instance = new Instance(); 38 | if (instance.createNewWindow() !== null) { 39 | console.log("New Window created"); 40 | } 41 | } 42 | }); 43 | 44 | if (!ret) { 45 | console.error("KeyboardShorcutError"); 46 | } 47 | }; 48 | 49 | module.exports = { 50 | setShortcut: setShortcut, 51 | }; 52 | -------------------------------------------------------------------------------- /static/media/pngLogo/DropPoint Symbolic-01.svg: -------------------------------------------------------------------------------- 1 | DropPoint Symbolic -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow, nativeImage } = require("electron"); 2 | const { autoUpdater } = require("electron-updater"); 3 | const Store = require("electron-store"); 4 | const configOptions = require("./configOptions"); 5 | 6 | const { Instance } = require("./Window"); 7 | const { setShortcut } = require("./Shortcut"); 8 | const { droppointDefaultIcon } = require("./Icons"); 9 | const { setTray } = require("./Tray"); 10 | 11 | const config = new Store(configOptions); 12 | let splashScreen; 13 | 14 | app 15 | .on("ready", () => { 16 | // Splash screen which also helps to run in background and keep app alive 17 | splashScreen = new BrowserWindow({ 18 | width: 400, 19 | height: 200, 20 | frame: false, 21 | titleBarStyle: "hidden", 22 | fullscreenable: false, 23 | transparent: true, 24 | icon: nativeImage.createFromPath(droppointDefaultIcon), 25 | show: false, 26 | }); 27 | // splashScreen.loadFile(path.join(__dirname, "../static/media/splash.jpeg")); 28 | // splashScreen.removeMenu(); 29 | // setTimeout(() => { 30 | // splashScreen.hide(); 31 | // }, 3000); 32 | 33 | setTray(); 34 | setShortcut(); 35 | 36 | if (BrowserWindow.getAllWindows.length === 0 && config.get("spawnOnLaunch")) { 37 | const instance = new Instance(); 38 | const instanceID = instance.createNewWindow(); 39 | if (instanceID !== null) { 40 | } 41 | } 42 | }) 43 | // .on("activate", () => { 44 | // autoUpdater.checkForUpdatesAndNotify(); 45 | // if (BrowserWindow.getAllWindows.length === 0) { 46 | // createMainWindow(); 47 | // } 48 | // }) 49 | .on("before-quit", () => { 50 | splashScreen.close(); 51 | }) 52 | .on("will-quit", () => { 53 | globalShortcut.unregisterAll(); 54 | }); 55 | module.exports = { 56 | whenReady: app.whenReady, 57 | }; 58 | -------------------------------------------------------------------------------- /src/History.js: -------------------------------------------------------------------------------- 1 | const { app } = require("electron"); 2 | const fs = require("fs"); 3 | 4 | const getHistory = () => { 5 | return new Promise((resolve, reject) => { 6 | if (!fs.existsSync("instanceHistory.json")) initHistoryFile(); 7 | 8 | fs.readFile("instanceHistory.json", (err, data) => { 9 | if (err) reject(err); 10 | 11 | try { 12 | let history = JSON.parse(data); 13 | resolve(history); 14 | } catch (e) { 15 | const historyObj = { 16 | history: [], 17 | }; 18 | setHistory(historyObj).then(resolve(historyObj)); 19 | } 20 | }); 21 | }); 22 | }; 23 | 24 | const setHistory = (historyObj) => { 25 | return new Promise((resolve, reject) => { 26 | const json = JSON.stringify(historyObj); 27 | fs.writeFile("instanceHistory.json", json, { flag: "w" }, (e) => { 28 | if (e) reject(e); 29 | }); 30 | resolve(); 31 | }); 32 | }; 33 | 34 | const initHistoryFile = async () => { 35 | const historyTemplate = { 36 | history: [], 37 | }; 38 | await setHistory(historyTemplate); 39 | }; 40 | 41 | const initHistory = (instanceId) => { 42 | getHistory() 43 | .then((history) => { 44 | let historyObj = history; 45 | historyObj.history.push({ 46 | instanceId: instanceId, 47 | files: [], 48 | }); 49 | 50 | setHistory(historyObj); 51 | }) 52 | .catch((err) => { 53 | console.error(err); 54 | app.quit(); 55 | }); 56 | }; 57 | 58 | const addToInstanceHistory = async (instanceId, filelist) => { 59 | let history = await getHistory(); 60 | 61 | history.history.forEach((instance, index) => { 62 | if (instance.instanceId === instanceId) { 63 | instance.files = filelist; 64 | } 65 | }); 66 | 67 | await setHistory(history); 68 | }; 69 | 70 | module.exports = { 71 | initHistory: initHistory, 72 | getHistory: getHistory, 73 | addToInstanceHistory: addToInstanceHistory, 74 | }; 75 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build_on_linux: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Check out Git repository 9 | uses: actions/checkout@v1 10 | 11 | - name: Install libarchive-tools for pacman 12 | run: | 13 | sudo apt-get update -y && sudo apt-get install -y libarchive-tools 14 | 15 | - name: Install Node.js, NPM and Yarn 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 18 19 | 20 | - name: Build/release Electron app 21 | uses: samuelmeuli/action-electron-builder@v1 22 | with: 23 | # GitHub token, automatically provided to the action 24 | # (No need to define this secret in the repo settings) 25 | github_token: ${{ secrets.github_token }} 26 | 27 | # If the commit is tagged with a version (e.g. "v1.0.0"), 28 | # release the app after building 29 | release: ${{ startsWith(github.ref, 'refs/tags/v') }} 30 | 31 | build_on_mac: 32 | runs-on: macos-latest 33 | steps: 34 | - name: Check out Git repository 35 | uses: actions/checkout@v1 36 | 37 | - name: Install Node.js, NPM and Yarn 38 | uses: actions/setup-node@v1 39 | with: 40 | node-version: 18 41 | 42 | - name: Build/release Electron app 43 | uses: samuelmeuli/action-electron-builder@v1 44 | with: 45 | # GitHub token, automatically provided to the action 46 | # (No need to define this secret in the repo settings) 47 | github_token: ${{ secrets.github_token }} 48 | 49 | # If the commit is tagged with a version (e.g. "v1.0.0"), 50 | # release the app after building 51 | release: ${{ startsWith(github.ref, 'refs/tags/v') }} 52 | 53 | build_on_win: 54 | runs-on: windows-latest 55 | steps: 56 | - name: Check out Git repository 57 | uses: actions/checkout@v1 58 | 59 | - name: Install Node.js, NPM and Yarn 60 | uses: actions/setup-node@v1 61 | with: 62 | node-version: 18 63 | 64 | - name: Build/release Electron app 65 | uses: samuelmeuli/action-electron-builder@v1 66 | with: 67 | # GitHub token, automatically provided to the action 68 | # (No need to define this secret in the repo settings) 69 | github_token: ${{ secrets.github_token }} 70 | 71 | # If the commit is tagged with a version (e.g. "v1.0.0"), 72 | # release the app after building 73 | release: ${{ startsWith(github.ref, 'refs/tags/v') }} 74 | -------------------------------------------------------------------------------- /docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ⚛👋 Hello there! Welcome. Please follow the steps below to tell us about your contribution. 2 | 3 | ### Requirements for Contributing 4 | 5 | * Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. 6 | * Include **one** of the following in the beginning of Pull Request title accordingly (square brackets included): 7 | * `[Bug Fix]` - for bug fixes 8 | * `[Feature]` - for feature implementations 9 | * `[Documentation]` - for documentation improvement (such as Readme, Contributing Guidelines, etc.) 10 | * `[Performance]` - for Performance improvements 11 | * After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. 12 | * Example of a [good Pull Request](https://github.com/GameGodS3/DropPoint/pull/14) 13 | 14 | ### Context 15 | 16 | 27 | 28 | ### Description of the Change 29 | 30 | 35 | 36 | ### Alternate Designs 37 | 38 | 39 | 40 | 41 | ### Possible Drawbacks 42 | 43 | 44 | 45 | 46 | ### Verification Process 47 | 48 | 53 | 54 | ### Release Notes 55 | 56 | 71 | 72 | -------------------------------------------------------------------------------- /src/Tray.js: -------------------------------------------------------------------------------- 1 | const { Tray, Menu, nativeImage, app } = require("electron"); 2 | const { droppointDefaultIcon } = require("./Icons"); 3 | const { Settings } = require("./Settings"); 4 | const { Instance } = require("./Window"); 5 | // const { getHistory } = require("./History"); 6 | 7 | let trayIcon = nativeImage 8 | .createFromPath(droppointDefaultIcon) 9 | .resize({ width: 16 }); 10 | 11 | let tray; 12 | 13 | /** 14 | * Sets system tray 15 | */ 16 | const setTray = () => { 17 | tray = new Tray(trayIcon); 18 | let trayMenu = [ 19 | { 20 | label: "New Instance", 21 | click: function () { 22 | const instance = new Instance(); 23 | const instanceID = instance.createNewWindow(); 24 | }, 25 | }, 26 | { 27 | label: "Settings", 28 | click: function () { 29 | const settings = new Settings(); 30 | settings.openSettings(); 31 | }, 32 | }, 33 | { 34 | label: "Quit", 35 | click: function () { 36 | app.exit(); 37 | }, 38 | }, 39 | // { 40 | // type: "separator", 41 | // }, 42 | // { 43 | // label: "History", 44 | // enabled: false, 45 | // }, 46 | ]; 47 | 48 | const setTrayHistory = async () => { 49 | const history = await getHistory(); 50 | history.history.slice(-5).forEach((instance, index) => { 51 | if (instance.files.length !== 0) { 52 | const d = new Date(instance.instanceId); 53 | trayMenu.push({ 54 | label: `${ 55 | d.getDate() + 56 | "/" + 57 | (d.getMonth() + 1) + 58 | "/" + 59 | d.getFullYear() + 60 | " " + 61 | d.getHours().toLocaleString("en", { 62 | minimumIntegerDigits: 2, 63 | useGrouping: false, 64 | }) + 65 | ":" + 66 | d.getMinutes().toLocaleString("en", { 67 | minimumIntegerDigits: 2, 68 | useGrouping: false, 69 | }) 70 | }`, 71 | submenu: [ 72 | { 73 | label: "Files", 74 | enabled: false, 75 | }, 76 | { 77 | label: 78 | instance.files.length < 2 79 | ? `${instance.files[0].filePath.toString().split("/").pop()}` 80 | : `${instance.files[0].filePath 81 | .toString() 82 | .split("/") 83 | .pop()} and others`, 84 | 85 | enabled: false, 86 | }, 87 | ], 88 | }); 89 | } 90 | }); 91 | tray.setContextMenu(Menu.buildFromTemplate(trayMenu)); 92 | }; 93 | // setTrayHistory(); 94 | 95 | tray.setContextMenu(Menu.buildFromTemplate(trayMenu)); 96 | tray.setToolTip("DropPoint"); 97 | 98 | tray.on("double-click", () => { 99 | const instance = new Instance(); 100 | instance.createNewWindow(); 101 | }); 102 | }; 103 | 104 | module.exports = { 105 | setTray: setTray, 106 | }; 107 | -------------------------------------------------------------------------------- /src/RequestHandlers.js: -------------------------------------------------------------------------------- 1 | const { ipcMain, nativeImage } = require("electron"); 2 | const configOptions = require("./configOptions"); 3 | const Store = require("electron-store"); 4 | 5 | global.share = { ipcMain }; 6 | 7 | // const { addToInstanceHistory } = require("./History"); 8 | const icons = require("./Icons"); 9 | 10 | /** 11 | * Assigns file icons according to type. If multiple files, use multifile icon. 12 | * @param {Array} fileList The collection of files currently dragged into the window 13 | * @return {String} Name of icon according to filetype 14 | */ 15 | let getFileTypeIcons = (fileList) => { 16 | let fileType; 17 | if (fileList.length <= 1) { 18 | fileType = fileList[0].fileType; 19 | if (fileType !== "application") { 20 | fileType = icons[fileType]; 21 | } else { 22 | fileType = icons.file; 23 | } 24 | } else { 25 | fileType = icons.multifile; 26 | } 27 | return fileType; 28 | }; 29 | 30 | /** 31 | * Returns list of file paths of all the files in filelist. 32 | * Necessary for startDrag API of electron 33 | * 34 | * @param {Array} fileList - List of files 35 | * @return {Array} List of paths of files in fileList 36 | */ 37 | let getFilePathList = (fileList) => { 38 | let filePathList = []; 39 | fileList.forEach((element) => { 40 | filePathList.push(element.filepath); 41 | }); 42 | return filePathList; 43 | }; 44 | 45 | /** 46 | * Activates Drag-and-Drop API of electron. Handles drag icon attributes. 47 | * Minimises instance after operation 48 | * 49 | * @param {Array} fileList - List of files 50 | */ 51 | let dragHandler = ipcMain.on("ondragstart", (event, params) => { 52 | console.log("Params - filelist: " + JSON.stringify(params)); 53 | let fileTypeIcons = getFileTypeIcons(params.filelist); 54 | let filePathList = getFilePathList(params.filelist); 55 | event.sender.startDrag({ 56 | files: filePathList, 57 | icon: nativeImage.createFromPath(fileTypeIcons).resize({ width: 64 }), 58 | }); 59 | // addToInstanceHistory(params.instanceId, params.filelist); 60 | event.sender.send("close-signal"); 61 | }); 62 | 63 | /** 64 | * For minimising instance on clicking the button 65 | */ 66 | let minimiseHandler = ipcMain.on("minimise", (event) => { 67 | event.sender.send("close-signal"); 68 | }); 69 | 70 | /** 71 | * For printing custom debug log in development console rather than browser 72 | */ 73 | let debugPrint = ipcMain.on("debugPrint", (event, message) => { 74 | console.log("[*] Debug Print: "); 75 | console.log(message); 76 | }); 77 | 78 | /** 79 | * Fetching config and schema 80 | */ 81 | let fetchConfig = ipcMain.on("fetchConfig", (event) => { 82 | const config = new Store(configOptions); 83 | const schema = configOptions.schema; 84 | event.sender.send( 85 | "configObj", 86 | JSON.stringify({ 87 | config: config, 88 | schema: schema, 89 | }) 90 | ); 91 | }); 92 | 93 | // For applying new settings 94 | ipcMain.on("applySettings", (event, newSettings) => { 95 | const config = new Store(configOptions); 96 | console.log("Received new settings in main process"); 97 | config.set(newSettings); 98 | console.log("Applied new settings in main process"); 99 | }) 100 | 101 | 102 | module.exports = { 103 | dragHandler: dragHandler, 104 | minimiseHandler: minimiseHandler, 105 | debugPrint: debugPrint, 106 | fetchConfig: fetchConfig, 107 | }; 108 | -------------------------------------------------------------------------------- /src/Window.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const { BrowserWindow, screen, nativeImage } = require("electron"); 4 | const Store = require("electron-store"); 5 | 6 | const { droppointDefaultIcon } = require("./Icons"); 7 | const configOptions = require("./configOptions"); 8 | // const { initHistory } = require("./History"); 9 | require("./RequestHandlers"); 10 | 11 | class Instance { 12 | /** 13 | * DropPoint Instance class. 14 | * Multiple instances of DropPoint can be maintained with this. 15 | * Each instance can be uniquely identified by id object 16 | * 17 | * @param {Boolean} devFlag - To open dev mode. Default = False 18 | * 19 | */ 20 | constructor(devFlag = false) { 21 | this.instance = null; 22 | this.id = +new Date(); 23 | 24 | this.config = new Store(configOptions); 25 | 26 | this.devFlag = this.config.get("debug"); 27 | this.windowConfig = { 28 | width: 200, 29 | height: 200, 30 | x: 0, 31 | y: 0, 32 | transparent: true, 33 | frame: devFlag ? true : false, 34 | titleBarStyle: devFlag ? "default" : "hidden", 35 | resizable: devFlag ? true : false, 36 | fullscreenable: devFlag ? true : false, 37 | alwaysOnTop: this.config.get("alwaysOnTop"), 38 | webPreferences: { 39 | nodeIntegration: true, 40 | preload: path.join(__dirname, "preload.js"), 41 | additionalArguments: [this.id], 42 | }, 43 | 44 | icon: nativeImage.createFromPath(droppointDefaultIcon), 45 | }; 46 | } 47 | 48 | /** 49 | * Creates a new DropPoint Instance 50 | * @returns {number} id - Unique ID of the instance | null if not created 51 | */ 52 | createNewWindow() { 53 | if (this.config.get("openAtCursorPosition")) { 54 | const cursorPosition = this.getCursorPos(); 55 | this.windowConfig.x = cursorPosition.x; 56 | this.windowConfig.y = cursorPosition.y; 57 | } 58 | else this.windowConfig.x = screen.getPrimaryDisplay().workArea.width / 2 - 100; 59 | 60 | this.instance = new BrowserWindow(this.windowConfig); 61 | 62 | const html_path = path.join(__dirname, "../static/index.html"); 63 | 64 | this.instance.loadURL(`file://${html_path}?id=${this.id}`); 65 | 66 | this.instance.setVisibleOnAllWorkspaces(true); 67 | 68 | if (this.devFlag) this.instance.webContents.openDevTools(); 69 | 70 | if (process.platform === "darwin") 71 | this.instance.setWindowButtonVisibility(false); 72 | 73 | this.instance.shadow = true; 74 | this.instance.removeMenu(); 75 | 76 | this.instance.on("closed", () => (this.instance = null)); 77 | 78 | console.log(`Instance ID: ${this.id}`); 79 | 80 | // Create a history for instance 81 | // initHistory(this.id); 82 | 83 | return this.instance ? this.id : null; 84 | } 85 | 86 | /** 87 | * Gets position at which DropPoint Instance must open 88 | * @returns {Object} - {x: number, y: number} Coordinates of the mouse position 89 | */ 90 | getCursorPos() { 91 | let point = screen.getCursorScreenPoint(); 92 | let screenWidth = screen.getPrimaryDisplay().bounds.width; 93 | 94 | // Position instance of window at Cursor position 95 | console.log(`Cursor position: ${point.x}, ${point.y}`); 96 | let xPoint = point.x; 97 | let yPoint = point.y - this.windowConfig.height; 98 | 99 | /* On Windows and Linux, app windows can go beyond screen in the top and right edges hence 100 | window needs to be repositioned to stay within screen */ 101 | if (xPoint + this.windowConfig.width > screenWidth) { 102 | xPoint = screenWidth - this.windowConfig.width; 103 | } 104 | if (yPoint < 0) { 105 | yPoint = 0; 106 | } 107 | return { x: xPoint, y: yPoint }; 108 | } 109 | } 110 | 111 | module.exports = { 112 | Instance: Instance, 113 | }; 114 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | variables, exposed ports, useful file locations and container parameters. 13 | 2. Increase the version numbers in any critical files (such as package.json) to the new version that this 14 | Pull Request would represent (if required). The versioning scheme we use is [SemVer](http://semver.org/). 15 | 3. Create Pull Requests according to the provided [Pull Request Template](https://github.com/GameGodS3/DropPoint/blob/main/docs/PULL_REQUEST_TEMPLATE.md) only. 16 | PRs otherwise might not be considered valid for merging. 17 | 18 | ## Code of Conduct 19 | 20 | ### Our Pledge 21 | 22 | In the interest of fostering an open and welcoming environment, we as 23 | contributors and maintainers pledge to making participation in our project and 24 | our community a harassment-free experience for everyone, regardless of age, body 25 | size, disability, ethnicity, gender identity and expression, level of experience, 26 | nationality, personal appearance, race, religion, or sexual identity and 27 | orientation. 28 | 29 | ### Our Standards 30 | 31 | Examples of behavior that contributes to creating a positive environment 32 | include: 33 | 34 | * Using welcoming and inclusive language 35 | * Being respectful of differing viewpoints and experiences 36 | * Gracefully accepting constructive criticism 37 | * Focusing on what is best for the community 38 | * Showing empathy towards other community members 39 | 40 | Examples of unacceptable behavior by participants include: 41 | 42 | * The use of sexualized language or imagery and unwelcome sexual attention or 43 | advances 44 | * Trolling, insulting/derogatory comments, and personal or political attacks 45 | * Public or private harassment 46 | * Publishing others' private information, such as a physical or electronic 47 | address, without explicit permission 48 | * Other conduct which could reasonably be considered inappropriate in a 49 | professional setting 50 | 51 | ### Our Responsibilities 52 | 53 | Project maintainers are responsible for clarifying the standards of acceptable 54 | behavior and are expected to take appropriate and fair corrective action in 55 | response to any instances of unacceptable behavior. 56 | 57 | Project maintainers have the right and responsibility to remove, edit, or 58 | reject comments, commits, code, wiki edits, issues, and other contributions 59 | that are not aligned to this Code of Conduct, or to ban temporarily or 60 | permanently any contributor for other behaviors that they deem inappropriate, 61 | threatening, offensive, or harmful. 62 | 63 | ### Scope 64 | 65 | This Code of Conduct applies both within project spaces and in public spaces 66 | when an individual is representing the project or its community. Examples of 67 | representing a project or community include using an official project e-mail 68 | address, posting via an official social media account, or acting as an appointed 69 | representative at an online or offline event. Representation of a project may be 70 | further defined and clarified by project maintainers. 71 | 72 | ### Enforcement 73 | 74 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 75 | reported by contacting the project maintainer at sudevssuresh@gmail.com. All 76 | complaints will be reviewed and investigated and will result in a response that 77 | is deemed necessary and appropriate to the circumstances. The project team is 78 | obligated to maintain confidentiality with regard to the reporter of an incident. 79 | Further details of specific enforcement policies may be posted separately. 80 | 81 | Project maintainers who do not follow or enforce the Code of Conduct in good 82 | faith may face temporary or permanent repercussions as determined by other 83 | members of the project's leadership. 84 | 85 | ### Attribution 86 | 87 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 88 | available at [http://contributor-covenant.org/version/1/4][version] 89 | 90 | [homepage]: http://contributor-covenant.org 91 | [version]: http://contributor-covenant.org/version/1/4/ 92 | -------------------------------------------------------------------------------- /renderer/settings-renderer.js: -------------------------------------------------------------------------------- 1 | const booleanInput = (switchID, labelText, isChecked) => { 2 | let switchInput = document.createElement("div"); 3 | switchInput.classList = "flex items-center justify-between w-full"; 4 | 5 | switchInput.innerHTML = ` 6 | 11 | 17 | `; 18 | switchInput.getElementsByTagName("input")[0].checked = isChecked; 19 | return switchInput; 20 | }; 21 | 22 | const enumInput = (enumID, labelText, enumList, selectedVal) => { 23 | let selectInput = document.createElement("div"); 24 | selectInput.classList = "flex items-center justify-between w-full"; 25 | 26 | selectInput.innerHTML = ` 27 | 32 | 33 | 43 | `; 44 | const options = selectInput.getElementsByTagName('option'); 45 | 46 | for (let i = 0; i < options.length; i++) { 47 | const element = options[i]; 48 | console.log(element); 49 | if (element.value === selectedVal) { 50 | element.selected = true; 51 | } 52 | } 53 | return selectInput; 54 | }; 55 | 56 | window.onload = async () => { 57 | electron.fetchConfig(); 58 | electron.onConfigReceived(async (_event, value) => { 59 | 60 | // Reading DropPoint config file from system 61 | const configResponse = JSON.parse(value); 62 | const configPath = configResponse.config.path; 63 | const response = await fetch(configPath); 64 | const configObj = await response.json(); 65 | console.log(Object.entries(configObj)); 66 | 67 | 68 | Object.entries(configObj).forEach(([key, value]) => { 69 | const configEntrySchema = configResponse.schema[key]; 70 | if (configEntrySchema.type === "boolean") { 71 | // console.log(`Boolean Pair: ${key}, ${value}`); 72 | document 73 | .querySelector(".settings-content") 74 | .appendChild(booleanInput(key, configEntrySchema.title, value)); 75 | } else if ( 76 | configEntrySchema.type === "string" && 77 | configEntrySchema.enum 78 | ) { 79 | console.log(`Enum Pair: ${key}, ${value}`); 80 | document 81 | .querySelector(".settings-content") 82 | .appendChild( 83 | enumInput( 84 | key, 85 | configEntrySchema.title, 86 | configEntrySchema.enum, 87 | value 88 | ) 89 | ); 90 | } 91 | }); 92 | }); 93 | }; 94 | 95 | const configResponse = { 96 | config: { 97 | events: { _events: {}, _eventsCount: 0 }, 98 | path: "C:\\Users\\sudev\\AppData\\Roaming\\DropPoint\\config.json", 99 | }, 100 | schema: { 101 | debug: { type: "boolean", title: "Enable debug mode" }, 102 | alwaysOnTop: { type: "boolean", title: "Always on Top" }, 103 | openAtCursorPosition: { type: "boolean", title: "Open at cursor position" }, 104 | shortcutAction: { 105 | enum: ["toggle", "spawn"], 106 | type: "string", 107 | title: "Shortcut Behaviour", 108 | }, 109 | }, 110 | }; 111 | 112 | const configObj = { 113 | debug: true, 114 | alwaysOnTop: true, 115 | openAtCursorPosition: false, 116 | shortcutAction: "toggle", 117 | }; 118 | 119 | const applySettings = () => { 120 | const currentSettings = document.querySelector(".settings-content").children; 121 | console.log("Settings Content: ", currentSettings); 122 | } -------------------------------------------------------------------------------- /static/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Settings - DropPoint 9 | 10 | 11 | 12 | 13 | 14 |

DropPoint Settings

15 |
16 |
17 |
18 | 19 |
20 | 24 |
25 | 33 |
34 |
35 | 38 | 63 |
64 | 65 | 66 | 67 | 113 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DropPoint - Make Drag 'n' Drop easier [![Twitter](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Fgithub.com%2FGameGodS3%2FDropPoint)](https://twitter.com/intent/tweet?text=Checkout%20this%20cool%20project!&url=https%3A%2F%2Fgithub.com%2FGameGodS3%2FDropPoint) 2 | 3 | ![DropPoint](https://github.com/GameGodS3/DropPoint/raw/main/static/media/droppoint-social-cover.jpg) 4 | 5 |

6 | 7 | 8 | 9 |

10 | 11 |

12 | 13 | 14 | 15 | 16 | 17 |

18 | 19 | 20 | Make drag-and-drop easier using DropPoint! 21 | 22 | DropPoint helps you drag content without having to open side-by-side windows 23 | 24 | Works on **Windows**, **Linux** and **MacOS**. 25 | 26 | ## Table of Contents 27 | - [:sparkles: Demo](#sparkles-demo) 28 | * [Across maximized windows in the same desktop](#across-maximized-windows-in-the-same-desktop) 29 | * [Across windows in different virtual desktops/workspaces](#across-windows-in-different-virtual-desktopsworkspaces) 30 | - [:package: How to Install (Normal Installation)](#package-how-to-install-normal-installation) 31 | - [:v: Usage](#v-usage) 32 | - [:gear: Developer Installation](#gear-developer-installation) 33 | - [:star: Contribution](#star-contribution) 34 | - [:heart: Credits](#heart-credits) 35 | - [Related](#related) 36 | 37 | ## :sparkles: Demo 38 | 39 | ### Across maximized windows in the same desktop 40 | 41 | ![Drag between windows](https://i.imgur.com/QkUPoOb.gif) 42 | 43 | ### Across windows in different virtual desktops/workspaces 44 | 45 | ![Drag between desktops](https://i.imgur.com/WElktc0.gif) 46 | 47 | ## :package: How to Install (Normal Installation) 48 | 49 | Go to the [Releases Page](https://github.com/GameGodS3/DropPoint/releases) to download the latest releases 50 | 51 | - Windows 52 | - Download `DropPoint-1.x.x-win.zip` from Releases and extract to a blank folder. Run `DropPoint.exe`. 53 | - To enable stickyness across Virtual Desktops (Windows) go to Task view while DropPoint is running, right click it and tick **"Show Window from this app on all Desktops"**. Stickyness in other Operating Systems works by default. 54 | - Mac 55 | - Download `DropPoint-1.x.x-intel.dmg` or `DropPoint-1.x.x-arm64-Apple-Silicon.dmg`(for Apple M1 Devices only) from Releases and install. Open "System Preferences > Security & Privacy > Open Anyway" 56 | - Linux 57 | - All Linux Distros: Download `DropPoint-1.x.x.AppImage` to run. 58 | - Debian-based Distros (Ubuntu, Mint, PopOs, etc): Download `droppoint_1.x.x_amd64.deb`. 59 | - Arch-based Distros (Manjaro, Arch, etc.): Download `droppoint-1.x.x.tar.gz` 60 | - RHEL-based Distros (Fedora, Red Hat, etc): Download `droppoint-1.x.x.x86_64.rpm`. 61 | 62 | ## :v: Usage 63 | 64 | - Drag and drop any file(s) or folder from the system into DropPoint, go to your desired location and drag it out. 65 | - App minimises to tray by default when you close the instance. To open instance, click on system tray. To quit, right click on tray icon > Quit. 66 | - While DropPoint is in the system tray, pressing Shift + Caps Lock anywhere in your PC would create a new instance of DropPoint, at mouse location. (as tested on Windows and Linux) (the shortcut is Shift + Tab on MacOs) 67 | 68 | ## :gear: Developer Installation 69 | 70 | **You must have NPM and Git installed in your PC** 71 | 72 | 1. Clone repo and change into directory 73 | ```bash 74 | git clone https://github.com/GameGodS3/DropPoint.git 75 | cd DropPoint 76 | ``` 77 | 2. Install dependencies and run 78 | ```bash 79 | npm install 80 | npm start 81 | ``` 82 | ## :star: Contribution 83 | Contributions are welcome 😃. 84 | 85 | Please do refer to our [Contributing Guidelines](https://github.com/GameGodS3/DropPoint/blob/main/docs/CONTRIBUTING.md) and [Pull Request Templates](https://github.com/GameGodS3/DropPoint/blob/main/docs/PULL_REQUEST_TEMPLATE.md) before making a Pull Request. 86 | 87 | ## :heart: Credits 88 | 89 | - Huge thanks to [Ajay Krishna KV](https://github.com/AJAYK-01) for CI/CD and Releases 90 | - Fluent icons from [Icons8](https://icons8.com) 91 | 92 | ## Related 93 | 94 | - Project inspired from [Dropover app](http://dropoverapp.com) in MacOS 95 | - For a Linux friendly and GTK-based alternative, checkout [PyDrop](https://github.com/Roshan-R/PyDrop) 96 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DropPoint 9 | 10 | 11 | 18 |
26 |
×
27 |
28 |
29 | Upload Here 30 | Drop Your File(s) Here 31 |
32 |
33 |
34 | 35 | 36 | 37 |
38 | Drag Your File(s) Out 39 |
40 |
41 |
42 | 43 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /renderer/droppoint.js: -------------------------------------------------------------------------------- 1 | // Instance ID from Main Process 2 | const instanceId = parseInt(window.location.search.slice(4)); 3 | console.log("Instance ID client: " + instanceId); 4 | 5 | // File List which will contain list of dicts in the format 6 | // {filePath:"file/Path", fileType:"filetype"} 7 | let filelist = []; 8 | 9 | // Custom event to trigger on push. 10 | // Will be used for creating UI elements. 11 | const filePushEvent = new Event("file-push"); 12 | Object.defineProperty(filelist, "push", { 13 | value: function () { 14 | for (var i = 0, n = this.length, l = arguments.length; i < l; i++, n++) { 15 | // Adding pushed element one by one into the array 16 | this[n] = arguments[i]; 17 | } 18 | // Trigger the event 19 | document.dispatchEvent(filePushEvent); 20 | return n; 21 | }, 22 | }); 23 | 24 | // Testing by growing the file list 25 | // let growfilelist = () => { 26 | // for (let i = 0; i < 8; i++) { 27 | // filelist.push({ 28 | // fileName: "TestFile", 29 | // fileType: filetypes[Math.floor(Math.random() * filetypes.length)], 30 | // }); 31 | // } 32 | // console.log(filelist); 33 | // }; 34 | 35 | // FileQueue to implement file icons animation 36 | class FileQueueUI { 37 | constructor() { 38 | this.queue = []; 39 | } 40 | enqueue(ele) { 41 | if (this.queue.length > 2) { 42 | this.dequeue(); 43 | } 44 | this.queue.push(ele); 45 | } 46 | dequeue() { 47 | if (this.isEmpty()) return null; 48 | return this.queue.shift(); 49 | } 50 | front() { 51 | if (this.isEmpty()) return null; 52 | return this.queue[0]; 53 | } 54 | isEmpty() { 55 | return this.queue.length === 0; 56 | } 57 | printQueue() { 58 | var str = ""; 59 | for (var i = 0; i < this.queue.length; i++) { 60 | str += this.queue[i] + " "; 61 | } 62 | return str; 63 | } 64 | length() { 65 | return this.queue.length; 66 | } 67 | } 68 | 69 | // UI image elements 70 | const imageHolder = document.querySelectorAll(".file-icon img"); 71 | 72 | let fq = new FileQueueUI(); 73 | 74 | document.addEventListener("file-push", () => { 75 | fq.enqueue(filelist[filelist.length - 1].fileType); 76 | for (let i = 0; i < fq.length(); i++) { 77 | let imageSource = "./media/" + fq.queue[fq.length() - 1 - i] + ".png"; 78 | imageHolder[i].src = imageSource; 79 | } 80 | 81 | // Reflow animation when new files are added 82 | imageHolder.forEach((e) => (e.style.animation = "none")); 83 | setTimeout(() => { 84 | imageHolder.forEach((e) => (e.style.animation = "")); 85 | }, 100); 86 | 87 | document.getElementsByTagName("num")[0].innerHTML = filelist.length; 88 | }); 89 | 90 | // Holder area where files will be dragged and dropped 91 | const holder = document.getElementById("droppoint"); 92 | 93 | // Adding "dragged" class to the holder when the file is dragged over it 94 | // "Dragged" class is used to do the border animation 95 | holder.ondragover = (e) => { 96 | e.preventDefault; 97 | e.stopPropagation; 98 | holder.setAttribute("class", "dragged"); 99 | return false; 100 | }; 101 | holder.ondragenter = (e) => { 102 | e.preventDefault; 103 | e.stopPropagation; 104 | holder.setAttribute("class", "dragged"); 105 | return false; 106 | }; 107 | // Removing the "dragged" class from the holder 108 | // when the file is dragged out of it 109 | holder.ondragleave = (e) => { 110 | e.preventDefault; 111 | e.stopPropagation; 112 | holder.removeAttribute("class"); 113 | return false; 114 | }; 115 | holder.ondragend = (e) => { 116 | e.preventDefault; 117 | e.stopPropagation; 118 | holder.removeAttribute("class"); 119 | return false; 120 | }; 121 | 122 | const uploadArea = document.querySelector(".upload"); 123 | const dragOutArea = document.querySelector("#drag"); 124 | holder.ondrop = (e) => { 125 | e.preventDefault(); 126 | // Remove animation borders on file drop inside DropPoint 127 | holder.removeAttribute("class"); 128 | 129 | // Get the files from the event 130 | for (let f of e.dataTransfer.files) { 131 | // Check if the file is already in the filelist 132 | 133 | let duplicateFile = false; 134 | 135 | for (let i = 0; i < filelist.length; i++) { 136 | if (filelist[i].filepath.includes(f.name)) { 137 | duplicateFile = true; 138 | break; 139 | } 140 | } 141 | 142 | if (duplicateFile) { 143 | alert("File already in the instance"); 144 | continue; 145 | } 146 | // Add the file to the filelist 147 | filelist.push({ 148 | filepath: f.path.toString(), 149 | fileType: 150 | f.type.split("/")[0] !== "application" ? f.type.split("/")[0] : "file", 151 | }); 152 | } 153 | 154 | uploadArea.style.display = "none"; 155 | dragOutArea.style.display = "flex"; 156 | }; 157 | 158 | const fileicons = document.querySelector(".file-icon"); 159 | const clearDrag = () => { 160 | filelist = []; 161 | if (dragicons[2]) { 162 | fileicons.removeChild(dragicons[2]); 163 | } 164 | if (dragicons[1]) { 165 | fileicons.removeChild(dragicons[1]); 166 | } 167 | }; 168 | 169 | // Drag out request to electron 170 | document.getElementById("drag").ondragstart = (event) => { 171 | event.preventDefault(); 172 | const params = { 173 | filelist: filelist, 174 | instanceId: instanceId, 175 | }; 176 | window.electron.dragOutListener(params); 177 | uploadArea.style.display = "flex"; 178 | dragOutArea.style.display = "none"; 179 | clearDrag(); 180 | }; 181 | 182 | // Close / Clear Button 183 | document.querySelector(".close").addEventListener("click", () => { 184 | window.close(); 185 | }); 186 | -------------------------------------------------------------------------------- /static/settings.css: -------------------------------------------------------------------------------- 1 | /* 2 | ! tailwindcss v3.1.8 | MIT License | https://tailwindcss.com 3 | */ 4 | 5 | /* 6 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 7 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 8 | */ 9 | 10 | *, 11 | ::before, 12 | ::after { 13 | box-sizing: border-box; 14 | /* 1 */ 15 | border-width: 0; 16 | /* 2 */ 17 | border-style: solid; 18 | /* 2 */ 19 | border-color: #e5e7eb; 20 | /* 2 */ 21 | } 22 | 23 | ::before, 24 | ::after { 25 | --tw-content: ''; 26 | } 27 | 28 | /* 29 | 1. Use a consistent sensible line-height in all browsers. 30 | 2. Prevent adjustments of font size after orientation changes in iOS. 31 | 3. Use a more readable tab size. 32 | 4. Use the user's configured `sans` font-family by default. 33 | */ 34 | 35 | html { 36 | line-height: 1.5; 37 | /* 1 */ 38 | -webkit-text-size-adjust: 100%; 39 | /* 2 */ 40 | -moz-tab-size: 4; 41 | /* 3 */ 42 | -o-tab-size: 4; 43 | tab-size: 4; 44 | /* 3 */ 45 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 46 | /* 4 */ 47 | } 48 | 49 | /* 50 | 1. Remove the margin in all browsers. 51 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 52 | */ 53 | 54 | body { 55 | margin: 0; 56 | /* 1 */ 57 | line-height: inherit; 58 | /* 2 */ 59 | } 60 | 61 | /* 62 | 1. Add the correct height in Firefox. 63 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 64 | 3. Ensure horizontal rules are visible by default. 65 | */ 66 | 67 | hr { 68 | height: 0; 69 | /* 1 */ 70 | color: inherit; 71 | /* 2 */ 72 | border-top-width: 1px; 73 | /* 3 */ 74 | } 75 | 76 | /* 77 | Add the correct text decoration in Chrome, Edge, and Safari. 78 | */ 79 | 80 | abbr:where([title]) { 81 | -webkit-text-decoration: underline dotted; 82 | text-decoration: underline dotted; 83 | } 84 | 85 | /* 86 | Remove the default font size and weight for headings. 87 | */ 88 | 89 | h1, 90 | h2, 91 | h3, 92 | h4, 93 | h5, 94 | h6 { 95 | font-size: inherit; 96 | font-weight: inherit; 97 | } 98 | 99 | /* 100 | Reset links to optimize for opt-in styling instead of opt-out. 101 | */ 102 | 103 | a { 104 | color: inherit; 105 | text-decoration: inherit; 106 | } 107 | 108 | /* 109 | Add the correct font weight in Edge and Safari. 110 | */ 111 | 112 | b, 113 | strong { 114 | font-weight: bolder; 115 | } 116 | 117 | /* 118 | 1. Use the user's configured `mono` font family by default. 119 | 2. Correct the odd `em` font sizing in all browsers. 120 | */ 121 | 122 | code, 123 | kbd, 124 | samp, 125 | pre { 126 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 127 | /* 1 */ 128 | font-size: 1em; 129 | /* 2 */ 130 | } 131 | 132 | /* 133 | Add the correct font size in all browsers. 134 | */ 135 | 136 | small { 137 | font-size: 80%; 138 | } 139 | 140 | /* 141 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 142 | */ 143 | 144 | sub, 145 | sup { 146 | font-size: 75%; 147 | line-height: 0; 148 | position: relative; 149 | vertical-align: baseline; 150 | } 151 | 152 | sub { 153 | bottom: -0.25em; 154 | } 155 | 156 | sup { 157 | top: -0.5em; 158 | } 159 | 160 | /* 161 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 162 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 163 | 3. Remove gaps between table borders by default. 164 | */ 165 | 166 | table { 167 | text-indent: 0; 168 | /* 1 */ 169 | border-color: inherit; 170 | /* 2 */ 171 | border-collapse: collapse; 172 | /* 3 */ 173 | } 174 | 175 | /* 176 | 1. Change the font styles in all browsers. 177 | 2. Remove the margin in Firefox and Safari. 178 | 3. Remove default padding in all browsers. 179 | */ 180 | 181 | button, 182 | input, 183 | optgroup, 184 | select, 185 | textarea { 186 | font-family: inherit; 187 | /* 1 */ 188 | font-size: 100%; 189 | /* 1 */ 190 | font-weight: inherit; 191 | /* 1 */ 192 | line-height: inherit; 193 | /* 1 */ 194 | color: inherit; 195 | /* 1 */ 196 | margin: 0; 197 | /* 2 */ 198 | padding: 0; 199 | /* 3 */ 200 | } 201 | 202 | /* 203 | Remove the inheritance of text transform in Edge and Firefox. 204 | */ 205 | 206 | button, 207 | select { 208 | text-transform: none; 209 | } 210 | 211 | /* 212 | 1. Correct the inability to style clickable types in iOS and Safari. 213 | 2. Remove default button styles. 214 | */ 215 | 216 | button, 217 | [type='button'], 218 | [type='reset'], 219 | [type='submit'] { 220 | -webkit-appearance: button; 221 | /* 1 */ 222 | background-color: transparent; 223 | /* 2 */ 224 | background-image: none; 225 | /* 2 */ 226 | } 227 | 228 | /* 229 | Use the modern Firefox focus style for all focusable elements. 230 | */ 231 | 232 | :-moz-focusring { 233 | outline: auto; 234 | } 235 | 236 | /* 237 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 238 | */ 239 | 240 | :-moz-ui-invalid { 241 | box-shadow: none; 242 | } 243 | 244 | /* 245 | Add the correct vertical alignment in Chrome and Firefox. 246 | */ 247 | 248 | progress { 249 | vertical-align: baseline; 250 | } 251 | 252 | /* 253 | Correct the cursor style of increment and decrement buttons in Safari. 254 | */ 255 | 256 | ::-webkit-inner-spin-button, 257 | ::-webkit-outer-spin-button { 258 | height: auto; 259 | } 260 | 261 | /* 262 | 1. Correct the odd appearance in Chrome and Safari. 263 | 2. Correct the outline style in Safari. 264 | */ 265 | 266 | [type='search'] { 267 | -webkit-appearance: textfield; 268 | /* 1 */ 269 | outline-offset: -2px; 270 | /* 2 */ 271 | } 272 | 273 | /* 274 | Remove the inner padding in Chrome and Safari on macOS. 275 | */ 276 | 277 | ::-webkit-search-decoration { 278 | -webkit-appearance: none; 279 | } 280 | 281 | /* 282 | 1. Correct the inability to style clickable types in iOS and Safari. 283 | 2. Change font properties to `inherit` in Safari. 284 | */ 285 | 286 | ::-webkit-file-upload-button { 287 | -webkit-appearance: button; 288 | /* 1 */ 289 | font: inherit; 290 | /* 2 */ 291 | } 292 | 293 | /* 294 | Add the correct display in Chrome and Safari. 295 | */ 296 | 297 | summary { 298 | display: list-item; 299 | } 300 | 301 | /* 302 | Removes the default spacing and border for appropriate elements. 303 | */ 304 | 305 | blockquote, 306 | dl, 307 | dd, 308 | h1, 309 | h2, 310 | h3, 311 | h4, 312 | h5, 313 | h6, 314 | hr, 315 | figure, 316 | p, 317 | pre { 318 | margin: 0; 319 | } 320 | 321 | fieldset { 322 | margin: 0; 323 | padding: 0; 324 | } 325 | 326 | legend { 327 | padding: 0; 328 | } 329 | 330 | ol, 331 | ul, 332 | menu { 333 | list-style: none; 334 | margin: 0; 335 | padding: 0; 336 | } 337 | 338 | /* 339 | Prevent resizing textareas horizontally by default. 340 | */ 341 | 342 | textarea { 343 | resize: vertical; 344 | } 345 | 346 | /* 347 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 348 | 2. Set the default placeholder color to the user's configured gray 400 color. 349 | */ 350 | 351 | input::-moz-placeholder, textarea::-moz-placeholder { 352 | opacity: 1; 353 | /* 1 */ 354 | color: #9ca3af; 355 | /* 2 */ 356 | } 357 | 358 | input::placeholder, 359 | textarea::placeholder { 360 | opacity: 1; 361 | /* 1 */ 362 | color: #9ca3af; 363 | /* 2 */ 364 | } 365 | 366 | /* 367 | Set the default cursor for buttons. 368 | */ 369 | 370 | button, 371 | [role="button"] { 372 | cursor: pointer; 373 | } 374 | 375 | /* 376 | Make sure disabled buttons don't get the pointer cursor. 377 | */ 378 | 379 | :disabled { 380 | cursor: default; 381 | } 382 | 383 | /* 384 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 385 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 386 | This can trigger a poorly considered lint error in some tools but is included by design. 387 | */ 388 | 389 | img, 390 | svg, 391 | video, 392 | canvas, 393 | audio, 394 | iframe, 395 | embed, 396 | object { 397 | display: block; 398 | /* 1 */ 399 | vertical-align: middle; 400 | /* 2 */ 401 | } 402 | 403 | /* 404 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 405 | */ 406 | 407 | img, 408 | video { 409 | max-width: 100%; 410 | height: auto; 411 | } 412 | 413 | [type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { 414 | -webkit-appearance: none; 415 | -moz-appearance: none; 416 | appearance: none; 417 | background-color: #fff; 418 | border-color: #6b7280; 419 | border-width: 1px; 420 | border-radius: 0px; 421 | padding-top: 0.5rem; 422 | padding-right: 0.75rem; 423 | padding-bottom: 0.5rem; 424 | padding-left: 0.75rem; 425 | font-size: 1rem; 426 | line-height: 1.5rem; 427 | --tw-shadow: 0 0 #0000; 428 | } 429 | 430 | [type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { 431 | outline: 2px solid transparent; 432 | outline-offset: 2px; 433 | --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); 434 | --tw-ring-offset-width: 0px; 435 | --tw-ring-offset-color: #fff; 436 | --tw-ring-color: #2563eb; 437 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 438 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 439 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); 440 | border-color: #2563eb; 441 | } 442 | 443 | input::-moz-placeholder, textarea::-moz-placeholder { 444 | color: #6b7280; 445 | opacity: 1; 446 | } 447 | 448 | input::placeholder,textarea::placeholder { 449 | color: #6b7280; 450 | opacity: 1; 451 | } 452 | 453 | ::-webkit-datetime-edit-fields-wrapper { 454 | padding: 0; 455 | } 456 | 457 | ::-webkit-date-and-time-value { 458 | min-height: 1.5em; 459 | } 460 | 461 | ::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { 462 | padding-top: 0; 463 | padding-bottom: 0; 464 | } 465 | 466 | select { 467 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); 468 | background-position: right 0.5rem center; 469 | background-repeat: no-repeat; 470 | background-size: 1.5em 1.5em; 471 | padding-right: 2.5rem; 472 | -webkit-print-color-adjust: exact; 473 | color-adjust: exact; 474 | print-color-adjust: exact; 475 | } 476 | 477 | [multiple] { 478 | background-image: initial; 479 | background-position: initial; 480 | background-repeat: unset; 481 | background-size: initial; 482 | padding-right: 0.75rem; 483 | -webkit-print-color-adjust: unset; 484 | color-adjust: unset; 485 | print-color-adjust: unset; 486 | } 487 | 488 | [type='checkbox'],[type='radio'] { 489 | -webkit-appearance: none; 490 | -moz-appearance: none; 491 | appearance: none; 492 | padding: 0; 493 | -webkit-print-color-adjust: exact; 494 | color-adjust: exact; 495 | print-color-adjust: exact; 496 | display: inline-block; 497 | vertical-align: middle; 498 | background-origin: border-box; 499 | -webkit-user-select: none; 500 | -moz-user-select: none; 501 | user-select: none; 502 | flex-shrink: 0; 503 | height: 1rem; 504 | width: 1rem; 505 | color: #2563eb; 506 | background-color: #fff; 507 | border-color: #6b7280; 508 | border-width: 1px; 509 | --tw-shadow: 0 0 #0000; 510 | } 511 | 512 | [type='checkbox'] { 513 | border-radius: 0px; 514 | } 515 | 516 | [type='radio'] { 517 | border-radius: 100%; 518 | } 519 | 520 | [type='checkbox']:focus,[type='radio']:focus { 521 | outline: 2px solid transparent; 522 | outline-offset: 2px; 523 | --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); 524 | --tw-ring-offset-width: 2px; 525 | --tw-ring-offset-color: #fff; 526 | --tw-ring-color: #2563eb; 527 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 528 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 529 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); 530 | } 531 | 532 | [type='checkbox']:checked,[type='radio']:checked { 533 | border-color: transparent; 534 | background-color: currentColor; 535 | background-size: 100% 100%; 536 | background-position: center; 537 | background-repeat: no-repeat; 538 | } 539 | 540 | [type='checkbox']:checked { 541 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); 542 | } 543 | 544 | [type='radio']:checked { 545 | background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); 546 | } 547 | 548 | [type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { 549 | border-color: transparent; 550 | background-color: currentColor; 551 | } 552 | 553 | [type='checkbox']:indeterminate { 554 | background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); 555 | border-color: transparent; 556 | background-color: currentColor; 557 | background-size: 100% 100%; 558 | background-position: center; 559 | background-repeat: no-repeat; 560 | } 561 | 562 | [type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { 563 | border-color: transparent; 564 | background-color: currentColor; 565 | } 566 | 567 | [type='file'] { 568 | background: unset; 569 | border-color: inherit; 570 | border-width: 0; 571 | border-radius: 0; 572 | padding: 0; 573 | font-size: unset; 574 | line-height: inherit; 575 | } 576 | 577 | [type='file']:focus { 578 | outline: 1px solid ButtonText; 579 | outline: 1px auto -webkit-focus-ring-color; 580 | } 581 | 582 | *, ::before, ::after { 583 | --tw-border-spacing-x: 0; 584 | --tw-border-spacing-y: 0; 585 | --tw-translate-x: 0; 586 | --tw-translate-y: 0; 587 | --tw-rotate: 0; 588 | --tw-skew-x: 0; 589 | --tw-skew-y: 0; 590 | --tw-scale-x: 1; 591 | --tw-scale-y: 1; 592 | --tw-pan-x: ; 593 | --tw-pan-y: ; 594 | --tw-pinch-zoom: ; 595 | --tw-scroll-snap-strictness: proximity; 596 | --tw-ordinal: ; 597 | --tw-slashed-zero: ; 598 | --tw-numeric-figure: ; 599 | --tw-numeric-spacing: ; 600 | --tw-numeric-fraction: ; 601 | --tw-ring-inset: ; 602 | --tw-ring-offset-width: 0px; 603 | --tw-ring-offset-color: #fff; 604 | --tw-ring-color: rgb(59 130 246 / 0.5); 605 | --tw-ring-offset-shadow: 0 0 #0000; 606 | --tw-ring-shadow: 0 0 #0000; 607 | --tw-shadow: 0 0 #0000; 608 | --tw-shadow-colored: 0 0 #0000; 609 | --tw-blur: ; 610 | --tw-brightness: ; 611 | --tw-contrast: ; 612 | --tw-grayscale: ; 613 | --tw-hue-rotate: ; 614 | --tw-invert: ; 615 | --tw-saturate: ; 616 | --tw-sepia: ; 617 | --tw-drop-shadow: ; 618 | --tw-backdrop-blur: ; 619 | --tw-backdrop-brightness: ; 620 | --tw-backdrop-contrast: ; 621 | --tw-backdrop-grayscale: ; 622 | --tw-backdrop-hue-rotate: ; 623 | --tw-backdrop-invert: ; 624 | --tw-backdrop-opacity: ; 625 | --tw-backdrop-saturate: ; 626 | --tw-backdrop-sepia: ; 627 | } 628 | 629 | ::-webkit-backdrop { 630 | --tw-border-spacing-x: 0; 631 | --tw-border-spacing-y: 0; 632 | --tw-translate-x: 0; 633 | --tw-translate-y: 0; 634 | --tw-rotate: 0; 635 | --tw-skew-x: 0; 636 | --tw-skew-y: 0; 637 | --tw-scale-x: 1; 638 | --tw-scale-y: 1; 639 | --tw-pan-x: ; 640 | --tw-pan-y: ; 641 | --tw-pinch-zoom: ; 642 | --tw-scroll-snap-strictness: proximity; 643 | --tw-ordinal: ; 644 | --tw-slashed-zero: ; 645 | --tw-numeric-figure: ; 646 | --tw-numeric-spacing: ; 647 | --tw-numeric-fraction: ; 648 | --tw-ring-inset: ; 649 | --tw-ring-offset-width: 0px; 650 | --tw-ring-offset-color: #fff; 651 | --tw-ring-color: rgb(59 130 246 / 0.5); 652 | --tw-ring-offset-shadow: 0 0 #0000; 653 | --tw-ring-shadow: 0 0 #0000; 654 | --tw-shadow: 0 0 #0000; 655 | --tw-shadow-colored: 0 0 #0000; 656 | --tw-blur: ; 657 | --tw-brightness: ; 658 | --tw-contrast: ; 659 | --tw-grayscale: ; 660 | --tw-hue-rotate: ; 661 | --tw-invert: ; 662 | --tw-saturate: ; 663 | --tw-sepia: ; 664 | --tw-drop-shadow: ; 665 | --tw-backdrop-blur: ; 666 | --tw-backdrop-brightness: ; 667 | --tw-backdrop-contrast: ; 668 | --tw-backdrop-grayscale: ; 669 | --tw-backdrop-hue-rotate: ; 670 | --tw-backdrop-invert: ; 671 | --tw-backdrop-opacity: ; 672 | --tw-backdrop-saturate: ; 673 | --tw-backdrop-sepia: ; 674 | } 675 | 676 | ::backdrop { 677 | --tw-border-spacing-x: 0; 678 | --tw-border-spacing-y: 0; 679 | --tw-translate-x: 0; 680 | --tw-translate-y: 0; 681 | --tw-rotate: 0; 682 | --tw-skew-x: 0; 683 | --tw-skew-y: 0; 684 | --tw-scale-x: 1; 685 | --tw-scale-y: 1; 686 | --tw-pan-x: ; 687 | --tw-pan-y: ; 688 | --tw-pinch-zoom: ; 689 | --tw-scroll-snap-strictness: proximity; 690 | --tw-ordinal: ; 691 | --tw-slashed-zero: ; 692 | --tw-numeric-figure: ; 693 | --tw-numeric-spacing: ; 694 | --tw-numeric-fraction: ; 695 | --tw-ring-inset: ; 696 | --tw-ring-offset-width: 0px; 697 | --tw-ring-offset-color: #fff; 698 | --tw-ring-color: rgb(59 130 246 / 0.5); 699 | --tw-ring-offset-shadow: 0 0 #0000; 700 | --tw-ring-shadow: 0 0 #0000; 701 | --tw-shadow: 0 0 #0000; 702 | --tw-shadow-colored: 0 0 #0000; 703 | --tw-blur: ; 704 | --tw-brightness: ; 705 | --tw-contrast: ; 706 | --tw-grayscale: ; 707 | --tw-hue-rotate: ; 708 | --tw-invert: ; 709 | --tw-saturate: ; 710 | --tw-sepia: ; 711 | --tw-drop-shadow: ; 712 | --tw-backdrop-blur: ; 713 | --tw-backdrop-brightness: ; 714 | --tw-backdrop-contrast: ; 715 | --tw-backdrop-grayscale: ; 716 | --tw-backdrop-hue-rotate: ; 717 | --tw-backdrop-invert: ; 718 | --tw-backdrop-opacity: ; 719 | --tw-backdrop-saturate: ; 720 | --tw-backdrop-sepia: ; 721 | } 722 | 723 | .static { 724 | position: static; 725 | } 726 | 727 | .fixed { 728 | position: fixed; 729 | } 730 | 731 | .absolute { 732 | position: absolute; 733 | } 734 | 735 | .relative { 736 | position: relative; 737 | } 738 | 739 | .inset-0 { 740 | top: 0px; 741 | right: 0px; 742 | bottom: 0px; 743 | left: 0px; 744 | } 745 | 746 | .z-50 { 747 | z-index: 50; 748 | } 749 | 750 | .my-4 { 751 | margin-top: 1rem; 752 | margin-bottom: 1rem; 753 | } 754 | 755 | .mx-3 { 756 | margin-left: 0.75rem; 757 | margin-right: 0.75rem; 758 | } 759 | 760 | .mb-1 { 761 | margin-bottom: 0.25rem; 762 | } 763 | 764 | .ml-3 { 765 | margin-left: 0.75rem; 766 | } 767 | 768 | .block { 769 | display: block; 770 | } 771 | 772 | .flex { 773 | display: flex; 774 | } 775 | 776 | .inline-flex { 777 | display: inline-flex; 778 | } 779 | 780 | .table { 781 | display: table; 782 | } 783 | 784 | .hidden { 785 | display: none; 786 | } 787 | 788 | .h-screen { 789 | height: 100vh; 790 | } 791 | 792 | .h-6 { 793 | height: 1.5rem; 794 | } 795 | 796 | .w-full { 797 | width: 100%; 798 | } 799 | 800 | .w-11 { 801 | width: 2.75rem; 802 | } 803 | 804 | .w-2 { 805 | width: 0.5rem; 806 | } 807 | 808 | .w-6 { 809 | width: 1.5rem; 810 | } 811 | 812 | .w-7 { 813 | width: 1.75rem; 814 | } 815 | 816 | .w-fit { 817 | width: -webkit-fit-content; 818 | width: -moz-fit-content; 819 | width: fit-content; 820 | } 821 | 822 | .flex-1 { 823 | flex: 1 1 0%; 824 | } 825 | 826 | .shrink-0 { 827 | flex-shrink: 0; 828 | } 829 | 830 | .transform { 831 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 832 | } 833 | 834 | .cursor-pointer { 835 | cursor: pointer; 836 | } 837 | 838 | .resize { 839 | resize: both; 840 | } 841 | 842 | .flex-col { 843 | flex-direction: column; 844 | } 845 | 846 | .items-center { 847 | align-items: center; 848 | } 849 | 850 | .justify-center { 851 | justify-content: center; 852 | } 853 | 854 | .justify-between { 855 | justify-content: space-between; 856 | } 857 | 858 | .gap-2 { 859 | gap: 0.5rem; 860 | } 861 | 862 | .rounded-full { 863 | border-radius: 9999px; 864 | } 865 | 866 | .rounded-md { 867 | border-radius: 0.375rem; 868 | } 869 | 870 | .border { 871 | border-width: 1px; 872 | } 873 | 874 | .border-2 { 875 | border-width: 2px; 876 | } 877 | 878 | .border-transparent { 879 | border-color: transparent; 880 | } 881 | 882 | .border-cyan-900 { 883 | --tw-border-opacity: 1; 884 | border-color: rgb(22 78 99 / var(--tw-border-opacity)); 885 | } 886 | 887 | .border-gray-200 { 888 | --tw-border-opacity: 1; 889 | border-color: rgb(229 231 235 / var(--tw-border-opacity)); 890 | } 891 | 892 | .bg-gray-100 { 893 | --tw-bg-opacity: 1; 894 | background-color: rgb(243 244 246 / var(--tw-bg-opacity)); 895 | } 896 | 897 | .bg-cyan-500 { 898 | --tw-bg-opacity: 1; 899 | background-color: rgb(6 182 212 / var(--tw-bg-opacity)); 900 | } 901 | 902 | .bg-gray-900 { 903 | --tw-bg-opacity: 1; 904 | background-color: rgb(17 24 39 / var(--tw-bg-opacity)); 905 | } 906 | 907 | .bg-opacity-50 { 908 | --tw-bg-opacity: 0.5; 909 | } 910 | 911 | .px-6 { 912 | padding-left: 1.5rem; 913 | padding-right: 1.5rem; 914 | } 915 | 916 | .py-4 { 917 | padding-top: 1rem; 918 | padding-bottom: 1rem; 919 | } 920 | 921 | .py-2 { 922 | padding-top: 0.5rem; 923 | padding-bottom: 0.5rem; 924 | } 925 | 926 | .px-3 { 927 | padding-left: 0.75rem; 928 | padding-right: 0.75rem; 929 | } 930 | 931 | .py-3 { 932 | padding-top: 0.75rem; 933 | padding-bottom: 0.75rem; 934 | } 935 | 936 | .px-4 { 937 | padding-left: 1rem; 938 | padding-right: 1rem; 939 | } 940 | 941 | .pr-9 { 942 | padding-right: 2.25rem; 943 | } 944 | 945 | .text-2xl { 946 | font-size: 1.5rem; 947 | line-height: 2rem; 948 | } 949 | 950 | .text-base { 951 | font-size: 1rem; 952 | line-height: 1.5rem; 953 | } 954 | 955 | .text-sm { 956 | font-size: 0.875rem; 957 | line-height: 1.25rem; 958 | } 959 | 960 | .font-bold { 961 | font-weight: 700; 962 | } 963 | 964 | .font-semibold { 965 | font-weight: 600; 966 | } 967 | 968 | .text-gray-600 { 969 | --tw-text-opacity: 1; 970 | color: rgb(75 85 99 / var(--tw-text-opacity)); 971 | } 972 | 973 | .text-cyan-800 { 974 | --tw-text-opacity: 1; 975 | color: rgb(21 94 117 / var(--tw-text-opacity)); 976 | } 977 | 978 | .text-white { 979 | --tw-text-opacity: 1; 980 | color: rgb(255 255 255 / var(--tw-text-opacity)); 981 | } 982 | 983 | .text-gray-800 { 984 | --tw-text-opacity: 1; 985 | color: rgb(31 41 55 / var(--tw-text-opacity)); 986 | } 987 | 988 | .antialiased { 989 | -webkit-font-smoothing: antialiased; 990 | -moz-osx-font-smoothing: grayscale; 991 | } 992 | 993 | .opacity-0 { 994 | opacity: 0; 995 | } 996 | 997 | .ring-1 { 998 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 999 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1000 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1001 | } 1002 | 1003 | .ring-transparent { 1004 | --tw-ring-color: transparent; 1005 | } 1006 | 1007 | .blur { 1008 | --tw-blur: blur(8px); 1009 | filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); 1010 | } 1011 | 1012 | .drop-shadow { 1013 | --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06)); 1014 | filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); 1015 | } 1016 | 1017 | .grayscale { 1018 | --tw-grayscale: grayscale(100%); 1019 | filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); 1020 | } 1021 | 1022 | .filter { 1023 | filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); 1024 | } 1025 | 1026 | .transition { 1027 | transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter; 1028 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 1029 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter; 1030 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1031 | transition-duration: 150ms; 1032 | } 1033 | 1034 | .transition-colors { 1035 | transition-property: color, background-color, border-color, fill, stroke, -webkit-text-decoration-color; 1036 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; 1037 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, -webkit-text-decoration-color; 1038 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1039 | transition-duration: 150ms; 1040 | } 1041 | 1042 | .transition-all { 1043 | transition-property: all; 1044 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1045 | transition-duration: 150ms; 1046 | } 1047 | 1048 | .duration-200 { 1049 | transition-duration: 200ms; 1050 | } 1051 | 1052 | .ease-in-out { 1053 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1054 | } 1055 | 1056 | .settings-content div { 1057 | margin-top: 0.25rem; 1058 | margin-bottom: 0.25rem; 1059 | } 1060 | 1061 | .before\:inline-block::before { 1062 | content: var(--tw-content); 1063 | display: inline-block; 1064 | } 1065 | 1066 | .before\:h-5::before { 1067 | content: var(--tw-content); 1068 | height: 1.25rem; 1069 | } 1070 | 1071 | .before\:w-5::before { 1072 | content: var(--tw-content); 1073 | width: 1.25rem; 1074 | } 1075 | 1076 | .before\:translate-x-0::before { 1077 | content: var(--tw-content); 1078 | --tw-translate-x: 0px; 1079 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 1080 | } 1081 | 1082 | .before\:transform::before { 1083 | content: var(--tw-content); 1084 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 1085 | } 1086 | 1087 | .before\:rounded-full::before { 1088 | content: var(--tw-content); 1089 | border-radius: 9999px; 1090 | } 1091 | 1092 | .before\:bg-white::before { 1093 | content: var(--tw-content); 1094 | --tw-bg-opacity: 1; 1095 | background-color: rgb(255 255 255 / var(--tw-bg-opacity)); 1096 | } 1097 | 1098 | .before\:shadow::before { 1099 | content: var(--tw-content); 1100 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); 1101 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); 1102 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 1103 | } 1104 | 1105 | .before\:ring-0::before { 1106 | content: var(--tw-content); 1107 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1108 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1109 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1110 | } 1111 | 1112 | .before\:transition::before { 1113 | content: var(--tw-content); 1114 | transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-text-decoration-color, -webkit-backdrop-filter; 1115 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 1116 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-text-decoration-color, -webkit-backdrop-filter; 1117 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1118 | transition-duration: 150ms; 1119 | } 1120 | 1121 | .before\:duration-200::before { 1122 | content: var(--tw-content); 1123 | transition-duration: 200ms; 1124 | } 1125 | 1126 | .before\:ease-in-out::before { 1127 | content: var(--tw-content); 1128 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 1129 | } 1130 | 1131 | .checked\:bg-cyan-600:checked { 1132 | --tw-bg-opacity: 1; 1133 | background-color: rgb(8 145 178 / var(--tw-bg-opacity)); 1134 | } 1135 | 1136 | .checked\:bg-none:checked { 1137 | background-image: none; 1138 | } 1139 | 1140 | .checked\:before\:translate-x-full:checked::before { 1141 | content: var(--tw-content); 1142 | --tw-translate-x: 100%; 1143 | transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); 1144 | } 1145 | 1146 | .checked\:before\:bg-cyan-200:checked::before { 1147 | content: var(--tw-content); 1148 | --tw-bg-opacity: 1; 1149 | background-color: rgb(165 243 252 / var(--tw-bg-opacity)); 1150 | } 1151 | 1152 | .hover\:border-cyan-800:hover { 1153 | --tw-border-opacity: 1; 1154 | border-color: rgb(21 94 117 / var(--tw-border-opacity)); 1155 | } 1156 | 1157 | .hover\:bg-cyan-800:hover { 1158 | --tw-bg-opacity: 1; 1159 | background-color: rgb(21 94 117 / var(--tw-bg-opacity)); 1160 | } 1161 | 1162 | .hover\:bg-cyan-600:hover { 1163 | --tw-bg-opacity: 1; 1164 | background-color: rgb(8 145 178 / var(--tw-bg-opacity)); 1165 | } 1166 | 1167 | .hover\:bg-gray-100:hover { 1168 | --tw-bg-opacity: 1; 1169 | background-color: rgb(243 244 246 / var(--tw-bg-opacity)); 1170 | } 1171 | 1172 | .hover\:text-white:hover { 1173 | --tw-text-opacity: 1; 1174 | color: rgb(255 255 255 / var(--tw-text-opacity)); 1175 | } 1176 | 1177 | .focus\:border-blue-500:focus { 1178 | --tw-border-opacity: 1; 1179 | border-color: rgb(59 130 246 / var(--tw-border-opacity)); 1180 | } 1181 | 1182 | .focus\:outline-none:focus { 1183 | outline: 2px solid transparent; 1184 | outline-offset: 2px; 1185 | } 1186 | 1187 | .focus\:ring-2:focus { 1188 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 1189 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 1190 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 1191 | } 1192 | 1193 | .focus\:ring-cyan-800:focus { 1194 | --tw-ring-opacity: 1; 1195 | --tw-ring-color: rgb(21 94 117 / var(--tw-ring-opacity)); 1196 | } 1197 | 1198 | .focus\:ring-cyan-500:focus { 1199 | --tw-ring-opacity: 1; 1200 | --tw-ring-color: rgb(6 182 212 / var(--tw-ring-opacity)); 1201 | } 1202 | 1203 | .focus\:ring-gray-800:focus { 1204 | --tw-ring-opacity: 1; 1205 | --tw-ring-color: rgb(31 41 55 / var(--tw-ring-opacity)); 1206 | } 1207 | 1208 | .focus\:ring-blue-500:focus { 1209 | --tw-ring-opacity: 1; 1210 | --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); 1211 | } 1212 | 1213 | .focus\:ring-offset-2:focus { 1214 | --tw-ring-offset-width: 2px; 1215 | } 1216 | 1217 | .dark .dark\:border-cyan-900 { 1218 | --tw-border-opacity: 1; 1219 | border-color: rgb(22 78 99 / var(--tw-border-opacity)); 1220 | } 1221 | 1222 | .dark .dark\:border-gray-700 { 1223 | --tw-border-opacity: 1; 1224 | border-color: rgb(55 65 81 / var(--tw-border-opacity)); 1225 | } 1226 | 1227 | .dark .dark\:bg-gray-700 { 1228 | --tw-bg-opacity: 1; 1229 | background-color: rgb(55 65 81 / var(--tw-bg-opacity)); 1230 | } 1231 | 1232 | .dark .dark\:bg-gray-800 { 1233 | --tw-bg-opacity: 1; 1234 | background-color: rgb(31 41 55 / var(--tw-bg-opacity)); 1235 | } 1236 | 1237 | .dark .dark\:bg-opacity-80 { 1238 | --tw-bg-opacity: 0.8; 1239 | } 1240 | 1241 | .dark .dark\:text-gray-400 { 1242 | --tw-text-opacity: 1; 1243 | color: rgb(156 163 175 / var(--tw-text-opacity)); 1244 | } 1245 | 1246 | .dark .dark\:text-white { 1247 | --tw-text-opacity: 1; 1248 | color: rgb(255 255 255 / var(--tw-text-opacity)); 1249 | } 1250 | 1251 | .dark .dark\:before\:bg-gray-400::before { 1252 | content: var(--tw-content); 1253 | --tw-bg-opacity: 1; 1254 | background-color: rgb(156 163 175 / var(--tw-bg-opacity)); 1255 | } 1256 | 1257 | .dark .dark\:checked\:bg-cyan-600:checked { 1258 | --tw-bg-opacity: 1; 1259 | background-color: rgb(8 145 178 / var(--tw-bg-opacity)); 1260 | } 1261 | 1262 | .dark .dark\:checked\:before\:bg-cyan-200:checked::before { 1263 | content: var(--tw-content); 1264 | --tw-bg-opacity: 1; 1265 | background-color: rgb(165 243 252 / var(--tw-bg-opacity)); 1266 | } 1267 | 1268 | .dark .dark\:hover\:border-cyan-900:hover { 1269 | --tw-border-opacity: 1; 1270 | border-color: rgb(22 78 99 / var(--tw-border-opacity)); 1271 | } 1272 | 1273 | .dark .dark\:hover\:border-gray-900:hover { 1274 | --tw-border-opacity: 1; 1275 | border-color: rgb(17 24 39 / var(--tw-border-opacity)); 1276 | } 1277 | 1278 | .dark .dark\:hover\:bg-cyan-900:hover { 1279 | --tw-bg-opacity: 1; 1280 | background-color: rgb(22 78 99 / var(--tw-bg-opacity)); 1281 | } 1282 | 1283 | .dark .dark\:hover\:bg-gray-900:hover { 1284 | --tw-bg-opacity: 1; 1285 | background-color: rgb(17 24 39 / var(--tw-bg-opacity)); 1286 | } 1287 | 1288 | .dark .dark\:focus\:ring-cyan-900:focus { 1289 | --tw-ring-opacity: 1; 1290 | --tw-ring-color: rgb(22 78 99 / var(--tw-ring-opacity)); 1291 | } 1292 | 1293 | .dark .dark\:focus\:ring-gray-900:focus { 1294 | --tw-ring-opacity: 1; 1295 | --tw-ring-color: rgb(17 24 39 / var(--tw-ring-opacity)); 1296 | } 1297 | 1298 | .dark .dark\:focus\:ring-offset-gray-800:focus { 1299 | --tw-ring-offset-color: #1f2937; 1300 | } 1301 | 1302 | .dark .dark\:focus\:ring-offset-cyan-800:focus { 1303 | --tw-ring-offset-color: #155e75; 1304 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------