mirror of
https://github.com/MasterAcnolo/Freedom-Loader.git
synced 2026-09-28 04:21:15 +02:00
Compare commits
9 Commits
1.6.3
...
b319a1e445
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b319a1e445 | ||
|
|
4a64a40226 | ||
|
|
2c0f5b315b | ||
|
|
58b6637eeb | ||
|
|
8af5a4a383 | ||
|
|
23f04bafc3 | ||
|
|
35ffd7077e | ||
|
|
408df0e2f6 | ||
|
|
7dee94da6f |
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
BUG_REPORT_URL=
|
||||||
|
BUG_REPORT_API_KEY=
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,6 +6,9 @@
|
|||||||
/out
|
/out
|
||||||
/srpm-out
|
/srpm-out
|
||||||
|
|
||||||
|
# .env
|
||||||
|
*.env
|
||||||
|
|
||||||
# Config JSON File. Create it and fill it if you want to have dev only settings
|
# Config JSON File. Create it and fill it if you want to have dev only settings
|
||||||
config/config.dev.json
|
config/config.dev.json
|
||||||
|
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -250,7 +250,7 @@ Freedom Loader can be configured either through the settings panel in the UI or
|
|||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
|------------------------------|---------|----------|------------------------------------------------------------|
|
|------------------------------|---------|----------|------------------------------------------------------------|
|
||||||
| `autoUpdate` | boolean | `true` | Enable automatic application updates |
|
| `autoUpdate` | boolean | `true` | Enable automatic application updates |
|
||||||
| `systemTray` | boolean | `false` | Allow app to minimize on close [EXPERIMENTAL] |
|
| `systemTray` | boolean | `false` | Allow app to minimize on close [EXPERIMENTAL] |
|
||||||
| `discordRPC` | boolean | `true` | Enable Discord Rich Presence integration |
|
| `discordRPC` | boolean | `true` | Enable Discord Rich Presence integration |
|
||||||
| `customTopBar` | boolean | `true` | Use custom application top bar |
|
| `customTopBar` | boolean | `true` | Use custom application top bar |
|
||||||
| `autoCheckInfo` | boolean | `true` | Automatically fetch video information on URL paste |
|
| `autoCheckInfo` | boolean | `true` | Automatically fetch video information on URL paste |
|
||||||
@@ -367,13 +367,29 @@ Head over to the [Freedom Loader Workshop repository](https://github.com/MasterA
|
|||||||
|
|
||||||
### Bug Reports
|
### Bug Reports
|
||||||
|
|
||||||
Use the GitHub Issues system and include:
|
You can report bugs either using the built-in application system or via the GitHub Issues system.
|
||||||
|
|
||||||
|
#### Using the Built-in Report Tool
|
||||||
|
|
||||||
|
1. Open Freedom Loader.
|
||||||
|
2. Trigger the bug report interface.
|
||||||
|
3. Fill in the title and description of the issue.
|
||||||
|
4. Choose whether to include today's logs automatically.
|
||||||
|
5. Submit the report.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If you need to attach extra context like screenshots or screen recordings, it is recommended to use the GitHub Issues
|
||||||
|
system instead.
|
||||||
|
|
||||||
|
#### Using GitHub Issues
|
||||||
|
|
||||||
|
Open a new issue on the GitHub repository and include:
|
||||||
|
|
||||||
- Clear description of the issue
|
- Clear description of the issue
|
||||||
- Steps to reproduce
|
- Steps to reproduce
|
||||||
- Expected vs actual behavior
|
- Expected vs actual behavior
|
||||||
- Relevant logs from `AppData\Local\FreedomLoader\logs\` or `~/.local/share/FreedomLoader/logs/`
|
- Relevant logs from `AppData\Local\FreedomLoader\logs\` or `~/.local/share/FreedomLoader/logs/`
|
||||||
- Screenshots if applicable
|
- Screenshots or recordings if applicable
|
||||||
|
|
||||||
### Feature Requests
|
### Feature Requests
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ function startRPC() {
|
|||||||
details: `Open Source Download Tools - ${config.version}`,
|
details: `Open Source Download Tools - ${config.version}`,
|
||||||
state: "masteracnolo.github.io/FreedomLoader",
|
state: "masteracnolo.github.io/FreedomLoader",
|
||||||
};
|
};
|
||||||
rpc.clearActivity()
|
rpc.clearActivity();
|
||||||
rpc.setActivity(presence);
|
rpc.setActivity(presence);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const config = require("../config");
|
|||||||
const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator");
|
const { validateDownloadPath, getDefaultDownloadPath } = require("./pathValidator");
|
||||||
const { userThemesPath } = require("../server/helpers/path.helpers");
|
const { userThemesPath } = require("../server/helpers/path.helpers");
|
||||||
const { createSystemTray, destroyTray } = require("./tray");
|
const { createSystemTray, destroyTray } = require("./tray");
|
||||||
|
const {sendReport} = require("./sendReport");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security whitelist for feature flags that can be modified at runtime.
|
* Security whitelist for feature flags that can be modified at runtime.
|
||||||
@@ -117,11 +118,11 @@ function registerIpcHandlers(getMainWindow) {
|
|||||||
ipcMain.on("window-minimize", () => {
|
ipcMain.on("window-minimize", () => {
|
||||||
// Minimize to tray
|
// Minimize to tray
|
||||||
if (configFeatures.systemTray) {
|
if (configFeatures.systemTray) {
|
||||||
getMainWindow()?.hide()
|
getMainWindow()?.hide();
|
||||||
logger.info("Window Minimized in SystemTray (Using Minimize Button)");
|
logger.info("Window Minimized in SystemTray (Using Minimize Button)");
|
||||||
} else {
|
} else {
|
||||||
// Native minimize
|
// Native minimize
|
||||||
getMainWindow()?.minimize()
|
getMainWindow()?.minimize();
|
||||||
logger.info("Window minimized normally");
|
logger.info("Window minimized normally");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +181,12 @@ function registerIpcHandlers(getMainWindow) {
|
|||||||
*/
|
*/
|
||||||
ipcMain.on("open-config", () => shell.openPath(configFolderPath));
|
ipcMain.on("open-config", () => shell.openPath(configFolderPath));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Front end logger
|
||||||
|
*/
|
||||||
|
ipcMain.on("log-error", (_, message) => logger.error(`[Frontend] ${message}`));
|
||||||
|
ipcMain.on("log-info", (_, message) => logger.info(`[Frontend] ${message}`));
|
||||||
|
ipcMain.on("log-warn", (_, message) => logger.warn(`[Frontend] ${message}`));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves available themes from filesystem.
|
* Retrieves available themes from filesystem.
|
||||||
@@ -199,6 +205,13 @@ function registerIpcHandlers(getMainWindow) {
|
|||||||
return await reloadThemes();
|
return await reloadThemes();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send bug report
|
||||||
|
*/
|
||||||
|
ipcMain.handle("send-report", async (_, params) => {
|
||||||
|
return await sendReport(params);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a runtime feature flag and persists it to disk.
|
* Updates a runtime feature flag and persists it to disk.
|
||||||
*
|
*
|
||||||
|
|||||||
54
app/sendReport.js
Normal file
54
app/sendReport.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const {logger, logDir} = require("../server/logger");
|
||||||
|
|
||||||
|
async function sendReport(params) {
|
||||||
|
try {
|
||||||
|
const {title, description, includeLogs} = params;
|
||||||
|
|
||||||
|
if (!title || !description) {
|
||||||
|
throw new Error("Title and description are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("title", title);
|
||||||
|
formData.append("context", description);
|
||||||
|
|
||||||
|
if (includeLogs === "yes" && logDir) {
|
||||||
|
// YYYY-MM-DD
|
||||||
|
const today = new Date().toISOString().split("T")[0];
|
||||||
|
|
||||||
|
const logFilePath = path.join(logDir, `LOGS-${today}.log`);
|
||||||
|
|
||||||
|
if (fs.existsSync(logFilePath)) {
|
||||||
|
const logFileContent = fs.readFileSync(logFilePath);
|
||||||
|
const blob = new Blob([logFileContent], {type: "text/plain"});
|
||||||
|
formData.append("file", blob, `LOGS-${today}.log`);
|
||||||
|
logger.info("Logs attached to bug report.");
|
||||||
|
} else {
|
||||||
|
logger.warn(`Log file not found for today: ${logFilePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(process.env.BUG_REPORT_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-Api-Key": process.env.BUG_REPORT_API_KEY
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
logger.error(`Server returned an error: ${response.status}`);
|
||||||
|
throw new Error(`Server responded with status ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Bug report successfully sent.");
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to send bug report: ${err.message}`);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {sendReport};
|
||||||
@@ -95,7 +95,7 @@ function createSystemTray(devMode) {
|
|||||||
} else {
|
} else {
|
||||||
window.show();
|
window.show();
|
||||||
window.focus();
|
window.focus();
|
||||||
logger.info("Window Restored from SystemTray (Double Click on Tray Icon)")
|
logger.info("Window Restored from SystemTray (Double Click on Tray Icon)");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
40
eslint.config.js
Normal file
40
eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
const js = require("@eslint/js");
|
||||||
|
const globals = require("globals");
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
js.configs.recommended,
|
||||||
|
|
||||||
|
{ignores: ["node_modules/**", "srpm-out/**", "dist/**", "test/**"]},
|
||||||
|
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "commonjs",
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-unused-vars": ["error", {argsIgnorePattern: "^_"}],
|
||||||
|
"no-console": "off",
|
||||||
|
"semi": ["error", "always"]
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["app/**", "server/**", "main.js"],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {...globals.node}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["public/**"],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {...globals.browser}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["preload.js"],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {...globals.node, ...globals.browser}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
11
main.js
11
main.js
@@ -48,9 +48,9 @@ const { configFeatures, devMode} = require("./config");
|
|||||||
*/
|
*/
|
||||||
if (!configFeatures.enableHardwareAcceleration){
|
if (!configFeatures.enableHardwareAcceleration){
|
||||||
app.disableHardwareAcceleration();
|
app.disableHardwareAcceleration();
|
||||||
logger.info("Disabled Hardware Acceleration")
|
logger.info("Disabled Hardware Acceleration");
|
||||||
} else {
|
} else {
|
||||||
logger.info("Enable Hardware Acceleration")
|
logger.info("Enable Hardware Acceleration");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(devMode){
|
if(devMode){
|
||||||
@@ -59,7 +59,7 @@ if(devMode){
|
|||||||
*/
|
*/
|
||||||
const { devtron } = require('@electron/devtron');
|
const { devtron } = require('@electron/devtron');
|
||||||
devtron.install();
|
devtron.install();
|
||||||
logger.info("Loaded DevTron Extension")
|
logger.info("Loaded DevTron Extension");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +78,11 @@ const { userThemesPath, initUserThemes, validateBinaries, defaultDownloadFolder
|
|||||||
const {createSystemTray, destroyTray} = require("./app/tray");
|
const {createSystemTray, destroyTray} = require("./app/tray");
|
||||||
const { stopServer } = require("./server/server");
|
const { stopServer } = require("./server/server");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose .env in process.env
|
||||||
|
*/
|
||||||
|
require("dotenv").config();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global flag indicating if the application is intentionally shutting down.
|
* Global flag indicating if the application is intentionally shutting down.
|
||||||
* Used across the app to bypass the "minimize to tray on close" behavior.
|
* Used across the app to bypass the "minimize to tray on close" behavior.
|
||||||
|
|||||||
1078
package-lock.json
generated
1078
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
"name": "freedom-loader",
|
"name": "freedom-loader",
|
||||||
"desktopName": "com.masteracnolo.freedomloader",
|
"desktopName": "com.masteracnolo.freedomloader",
|
||||||
"productName": "Freedom Loader",
|
"productName": "Freedom Loader",
|
||||||
"version": "1.6.3",
|
"version": "1.6.4-preview",
|
||||||
"author": "MasterAcnolo <MasterAcnolo@users.noreply.github.com>",
|
"author": "MasterAcnolo <MasterAcnolo@users.noreply.github.com>",
|
||||||
"description": "Free and open-source GUI for yt-dlp — download video and audio from hundreds of platforms",
|
"description": "Free and open-source GUI for yt-dlp — download video and audio from hundreds of platforms",
|
||||||
"homepage": "https://masteracnolo.github.io/Freedom-Loader-Site/",
|
"homepage": "https://masteracnolo.github.io/Freedom-Loader-Site/",
|
||||||
@@ -32,12 +32,15 @@
|
|||||||
"release:dry-run": "bash scripts/release.sh --dry-run",
|
"release:dry-run": "bash scripts/release.sh --dry-run",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:unit": "jest test/unit",
|
"test:unit": "jest test/unit",
|
||||||
|
"lint": "eslint . --format pretty",
|
||||||
|
"lint:fix": "eslint . --fix --format pretty",
|
||||||
"update": "npm update"
|
"update": "npm update"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"debug": "^4.4.1",
|
"debug": "^4.4.1",
|
||||||
"discord-rpc": "^4.0.1",
|
"discord-rpc": "^4.0.1",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
"electron-updater": "^6.6.2",
|
"electron-updater": "^6.6.2",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-rate-limit": "^8.2.1",
|
"express-rate-limit": "^8.2.1",
|
||||||
@@ -48,10 +51,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@electron/devtron": "^2.1.1",
|
"@electron/devtron": "^2.1.1",
|
||||||
|
"@eslint/js": "^10.0.1",
|
||||||
"@jest/globals": "^30.4.1",
|
"@jest/globals": "^30.4.1",
|
||||||
"@playwright/test": "^1.62.1",
|
"@playwright/test": "^1.62.1",
|
||||||
"electron": "^44.3.0",
|
"electron": "^44.3.0",
|
||||||
"electron-builder": "^26.15.3",
|
"electron-builder": "^26.15.3",
|
||||||
|
"eslint": "^10.10.0",
|
||||||
|
"eslint-formatter-pretty": "^7.1.0",
|
||||||
|
"globals": "^17.12.0",
|
||||||
"jest": "^30.4.2",
|
"jest": "^30.4.2",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"playwright": "^1.62.1"
|
"playwright": "^1.62.1"
|
||||||
|
|||||||
29
preload.js
29
preload.js
@@ -9,6 +9,27 @@ const { contextBridge, ipcRenderer } = require("electron");
|
|||||||
*/
|
*/
|
||||||
contextBridge.exposeInMainWorld("electronAPI", {
|
contextBridge.exposeInMainWorld("electronAPI", {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends error log message.
|
||||||
|
*
|
||||||
|
* @param {...any} args - Values to log
|
||||||
|
*/
|
||||||
|
logError: (...args) => ipcRenderer.send("log-error", args.map(arg => typeof arg === "object" ? JSON.stringify(arg) : arg).join(" ")),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends info log message.
|
||||||
|
*
|
||||||
|
* @param {...any} args - Values to log
|
||||||
|
*/
|
||||||
|
logInfo: (...args) => ipcRenderer.send("log-info", args.map(arg => typeof arg === "object" ? JSON.stringify(arg) : arg).join(" ")),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends warning log message.
|
||||||
|
*
|
||||||
|
* @param {...any} args - Values to log
|
||||||
|
*/
|
||||||
|
logWarn: (...args) => ipcRenderer.send("log-warn", args.map(arg => typeof arg === "object" ? JSON.stringify(arg) : arg).join(" ")),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return process.platform to renderer
|
* Return process.platform to renderer
|
||||||
*/
|
*/
|
||||||
@@ -126,4 +147,12 @@ contextBridge.exposeInMainWorld("topbarAPI", {
|
|||||||
* Opens configuration/settings panel.
|
* Opens configuration/settings panel.
|
||||||
*/
|
*/
|
||||||
openConfig: () => ipcRenderer.send("open-config"),
|
openConfig: () => ipcRenderer.send("open-config"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send Bug Report
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
sendReport: (params) => ipcRenderer.invoke('send-report', params),
|
||||||
});
|
});
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<link rel="stylesheet" href="styles/styles.css">
|
<link rel="stylesheet" href="styles/styles.css">
|
||||||
<link rel="stylesheet" href="styles/layout/topbar.css">
|
<link rel="stylesheet" href="styles/layout/topbar.css">
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
<div class="custom-controls">
|
<div class="custom-controls">
|
||||||
<button id="devtools-btn" title="Dev Tools">Tools</button>
|
<button id="devtools-btn" title="Dev Tools">Tools</button>
|
||||||
<button id="logs-btn" title="Logs">Logs</button>
|
<button id="logs-btn" title="Logs">Logs</button>
|
||||||
|
<button id="report-bug-btn" title="Report a bug">Report a Bug</button> <!-- TODO: Find a better place -->
|
||||||
<button id="website-btn" title="Website">Website</button>
|
<button id="website-btn" title="Website">Website</button>
|
||||||
<button id="wiki-btn" title="Wiki">Wiki</button>
|
<button id="wiki-btn" title="Wiki">Wiki</button>
|
||||||
<button id="workshop-btn" title="Workshop">Theme Workshop</button>
|
<button id="workshop-btn" title="Workshop">Theme Workshop</button>
|
||||||
@@ -295,7 +297,6 @@
|
|||||||
|
|
||||||
<span id="version-badge" class="version-badge"></span>
|
<span id="version-badge" class="version-badge"></span>
|
||||||
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Toast Container -->
|
<!-- Toast Container -->
|
||||||
@@ -314,6 +315,7 @@
|
|||||||
<script src="script/fetchinfo.js"></script>
|
<script src="script/fetchinfo.js"></script>
|
||||||
<script src="script/progressBar.js"></script>
|
<script src="script/progressBar.js"></script>
|
||||||
<script src="script/customthemes.js"></script>
|
<script src="script/customthemes.js"></script>
|
||||||
|
<script src="script/reportBug.js"></script>
|
||||||
<script type="module" src="script/topbar.js"></script>
|
<script type="module" src="script/topbar.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -269,7 +269,7 @@ async function init() {
|
|||||||
infoDiv.classList.add("visible");
|
infoDiv.classList.add("visible");
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
121
public/script/reportBug.js
Normal file
121
public/script/reportBug.js
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
class BugReportModal {
|
||||||
|
constructor() {
|
||||||
|
this.modal = null;
|
||||||
|
this.form = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.modal = document.createElement("div");
|
||||||
|
this.modal.className = "bug-modal-overlay";
|
||||||
|
this.modal.style.cssText = "display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.7); justify-content:center; align-items:center; z-index:99999; font-family:sans-serif;";
|
||||||
|
|
||||||
|
this.modal.innerHTML = `
|
||||||
|
<div class="bug-modal-content" style="background:#14141491 !important; color:#ffffff !important; padding:24px !important; border-radius:10px !important; width:420px !important; box-shadow: 0 10px 25px rgba(0,0,0,0.8) !important; display:flex !important; flex-direction:column !important; gap:16px !important; border: 1px solid #333 !important; box-sizing: border-box !important;">
|
||||||
|
<h3 style="margin:0 !important; font-size:1.2rem !important; font-weight:600 !important; color:#ffffff !important;">Report a Bug</h3>
|
||||||
|
<form id="bugReportForm" style="display:flex !important; flex-direction:column !important; gap:12px !important; width:unset !important; margin:0 !important; background:transparent !important; box-shadow: unset !important;">
|
||||||
|
<div style="display:flex !important; flex-direction:column !important; gap:4px !important;">
|
||||||
|
<label style="font-size:0.85rem !important; color:#aaaaaa !important;">Title <span style="color: red; font-weight: bold" >*</span> </label>
|
||||||
|
<input type="text" id="bugTitle" required style="background:#2a2a2a !important; border:1px solid #444 !important; color:#ffffff !important; border-radius:6px !important; padding:8px 10px !important; outline:none !important; width:100% !important; box-sizing: border-box !important;" />
|
||||||
|
</div>
|
||||||
|
<div style="display:flex !important; flex-direction:column !important; gap:4px !important;">
|
||||||
|
<label style="font-size:0.85rem !important; color:#aaaaaa !important;">Description <span style="color: red; font-weight: bold" >*</span></label>
|
||||||
|
<textarea id="bugDescription" placeholder="Provide some details about the bug" required rows="4" style="background:#2a2a2a !important; border:1px solid #444 !important; color:#ffffff !important; border-radius:6px !important; padding:8px 10px !important; outline:none !important; resize:vertical !important; width:100% !important; box-sizing: border-box !important;"></textarea>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex !important; flex-direction:column !important; gap:4px !important;">
|
||||||
|
<label style="font-size:0.85rem !important; color:#aaaaaa !important;">Include Logs From Today</label>
|
||||||
|
<select id="bugIncludeLogs" style="background:#2a2a2a !important; border:1px solid #444 !important; color:#ffffff !important; border-radius:6px !important; padding:8px 10px !important; outline:none !important; width:100% !important; box-sizing: border-box !important;">
|
||||||
|
<option value="yes" style="background:#2a2a2a !important; color:#ffffff !important;">Yes</option>
|
||||||
|
<option value="no" style="background:#2a2a2a !important; color:#ffffff !important;">No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex !important; justify-content:flex-end !important; gap:8px !important; margin-top:8px !important;">
|
||||||
|
<button type="button" id="bugCancelBtn" style="background:#333333 !important; border:none !important; color:#cccccc !important; padding:8px 14px !important; border-radius:6px !important; cursor:pointer !important;">Cancel</button>
|
||||||
|
<button type="submit" style="background:#4f46e5 !important; border:none !important; color:#ffffff !important; padding:8px 16px !important; border-radius:6px !important; cursor:pointer !important; font-weight:500 !important;">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(this.modal);
|
||||||
|
this.form = this.modal.querySelector("#bugReportForm");
|
||||||
|
|
||||||
|
this.bindEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
const titleInput = this.modal.querySelector("#bugTitle");
|
||||||
|
const descInput = this.modal.querySelector("#bugDescription");
|
||||||
|
|
||||||
|
titleInput.addEventListener("input", () => {
|
||||||
|
localStorage.setItem("draft_bug_title", titleInput.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
descInput.addEventListener("input", () => {
|
||||||
|
localStorage.setItem("draft_bug_desc", descInput.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.modal.querySelector("#bugCancelBtn").addEventListener("click", () => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.modal.addEventListener("click", (e) => {
|
||||||
|
if (e.target === this.modal) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const data = {
|
||||||
|
title: titleInput.value,
|
||||||
|
description: descInput.value,
|
||||||
|
includeLogs: this.modal.querySelector("#bugIncludeLogs").value
|
||||||
|
};
|
||||||
|
|
||||||
|
window.electronAPI.logInfo("Submitting:", data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const success = await window.electronAPI.sendReport(data);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
localStorage.removeItem("draft_bug_title");
|
||||||
|
localStorage.removeItem("draft_bug_desc");
|
||||||
|
this.form.reset();
|
||||||
|
this.close();
|
||||||
|
window.showSuccess("Bug report sent successfully!");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
window.showError("Failed to send bug report.");
|
||||||
|
window.electronAPI.logError("Failed to send bug report:", err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadState() {
|
||||||
|
const savedTitle = localStorage.getItem("draft_bug_title");
|
||||||
|
const savedDesc = localStorage.getItem("draft_bug_desc");
|
||||||
|
|
||||||
|
if (savedTitle) {
|
||||||
|
this.modal.querySelector("#bugTitle").value = savedTitle;
|
||||||
|
}
|
||||||
|
if (savedDesc) {
|
||||||
|
this.modal.querySelector("#bugDescription").value = savedDesc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
this.loadState();
|
||||||
|
this.modal.style.display = "flex";
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.modal.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.bugModal = new BugReportModal();
|
||||||
|
|
||||||
|
document.getElementById("report-bug-btn").addEventListener("click", () => {
|
||||||
|
window.bugModal.open();
|
||||||
|
});
|
||||||
@@ -16,8 +16,7 @@ let isOpen = false;
|
|||||||
* Toggles visibility of the settings panel when clicking the settings button.
|
* Toggles visibility of the settings panel when clicking the settings button.
|
||||||
*/
|
*/
|
||||||
settingsButton.addEventListener("click", () => {
|
settingsButton.addEventListener("click", () => {
|
||||||
const currentlyOpen = settingsPanel.style.display === "block";
|
settingsPanel.classList.toggle("open");
|
||||||
settingsPanel.style.display = currentlyOpen ? "none" : "block";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +24,7 @@ settingsButton.addEventListener("click", () => {
|
|||||||
*/
|
*/
|
||||||
function closePanel() {
|
function closePanel() {
|
||||||
isOpen = false;
|
isOpen = false;
|
||||||
settingsPanel.style.display = "none";
|
settingsPanel.classList.remove("open");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,18 +17,20 @@ window.showToast = function (message, type = "info", duration = 4000) {
|
|||||||
toast.classList.add("toast", type);
|
toast.classList.add("toast", type);
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
success: "✓",
|
success: "check_circle",
|
||||||
error: "✕",
|
error: "error",
|
||||||
warning: "⚠",
|
warning: "warning",
|
||||||
info: "ℹ"
|
info: "info"
|
||||||
};
|
};
|
||||||
|
|
||||||
const icon = icons[type] || icons.info;
|
const icon = icons[type] || icons.info;
|
||||||
|
|
||||||
|
toast.style.setProperty("--toast-duration", `${duration}ms`);
|
||||||
toast.innerHTML = `
|
toast.innerHTML = `
|
||||||
<span class="toast-icon">${icon}</span>
|
<span class="toast-icon material-icons-round">${icon}</span>
|
||||||
<span class="toast-message">${message}</span>
|
<span class="toast-message">${message}</span>
|
||||||
<button class="toast-close" title="Close">×</button>
|
<button class="toast-close" title="Close">×</button>
|
||||||
|
<div class="toast-progress"></div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const closeBtn = toast.querySelector(".toast-close");
|
const closeBtn = toast.querySelector(".toast-close");
|
||||||
|
|||||||
@@ -5,73 +5,70 @@
|
|||||||
right: 20px;
|
right: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 12px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toast */
|
/* Toast */
|
||||||
.toast {
|
.toast {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
padding: 10px 14px;
|
padding: 14px 16px;
|
||||||
background-color: rgba(50, 50, 50, 0.9);
|
background-color: rgba(30, 30, 30, 0.9);
|
||||||
border-radius: 6px;
|
backdrop-filter: blur(12px);
|
||||||
color: #ffffff;
|
-webkit-backdrop-filter: blur(12px);
|
||||||
font-size: 0.9rem;
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
max-width: 220px;
|
border-radius: 10px;
|
||||||
min-width: 100px;
|
color: #f3f4f6;
|
||||||
animation: slideIn 0.25s ease-out;
|
font-family: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
max-width: 320px;
|
||||||
|
min-width: 220px;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.2);
|
||||||
|
animation: slideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
backdrop-filter: blur(8px);
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
.toast.success {
|
|
||||||
background-color: rgba(40, 167, 69, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.error {
|
|
||||||
background-color: rgba(220, 53, 69, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.warning {
|
|
||||||
background-color: rgba(255, 193, 7, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.info {
|
|
||||||
background-color: rgba(23, 162, 184, 0.15);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toast Icon */
|
/* Toast Icon */
|
||||||
.toast-icon {
|
.toast-icon {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast-icon svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.toast.success .toast-icon {
|
.toast.success .toast-icon {
|
||||||
color: #28a745;
|
color: #22c55e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.error .toast-icon {
|
.toast.error .toast-icon {
|
||||||
color: #dc3545;
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.warning .toast-icon {
|
.toast.warning .toast-icon {
|
||||||
color: #ffc107;
|
color: #f59e0b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.info .toast-icon {
|
.toast.info .toast-icon {
|
||||||
color: #17a2b8;
|
color: #0ea5e9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toast Message */
|
/* Toast Message */
|
||||||
.toast-message {
|
.toast-message {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-weight: 500;
|
font-weight: 400;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toast Close Button */
|
/* Toast Close Button */
|
||||||
@@ -79,54 +76,83 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: currentColor;
|
color: #9ca3af;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
padding: 0;
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
opacity: 0.5;
|
transition: background-color 0.15s ease, color 0.15s ease;
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-close:hover {
|
.toast-close:hover {
|
||||||
opacity: 1;
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-progress {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 3px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
animation: progress var(--toast-duration, 4000ms) linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.success .toast-progress {
|
||||||
|
background-color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.error .toast-progress {
|
||||||
|
background-color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes slideIn {
|
@keyframes slideIn {
|
||||||
from {
|
from {
|
||||||
transform: translateX(350px);
|
transform: translateY(-20px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
transform: translateX(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideOut {
|
@keyframes slideOut {
|
||||||
from {
|
from {
|
||||||
transform: translateX(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
transform: translateX(350px);
|
transform: translateY(-20px);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.removing {
|
@keyframes progress {
|
||||||
animation: slideOut 0.25s ease-out;
|
from {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.removing {
|
||||||
|
animation: slideOut 0.25s ease-in forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
#toast-container {
|
#toast-container {
|
||||||
bottom: 12px;
|
top: auto;
|
||||||
right: 12px;
|
bottom: 16px;
|
||||||
left: 12px;
|
right: 16px;
|
||||||
|
left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast {
|
.toast {
|
||||||
|
|||||||
@@ -45,11 +45,22 @@
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
box-shadow: 0 8px 24px rgba(0,0,0,0.4);
|
||||||
display: none;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
animation: slideIn 0.2s ease-out;
|
/*animation: slideIn 0.2s ease-out; */
|
||||||
color: var(--settings-text-color);
|
color: var(--settings-text-color);
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transform: translateX(20px);
|
||||||
|
transition: opacity 0.2s ease-out, transform 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-panel.open {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-panel::-webkit-scrollbar {
|
.settings-panel::-webkit-scrollbar {
|
||||||
|
|||||||
1
scripts/CHANGELOG.md
Normal file
1
scripts/CHANGELOG.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[//]: # (This file is used for the release CHANGELOG. Please use Markdown to describe changes.)
|
||||||
@@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
|
VERSION=$(node -p "require('$ROOT_DIR/package.json').version")
|
||||||
|
|
||||||
f [[ "$VERSION" == *"-preview"* ]]; then
|
if [[ "$VERSION" == *"-preview"* ]]; then
|
||||||
echo "Error: The current version ($VERSION) is a 'preview"
|
echo "Error: The current version ($VERSION) is a 'preview"
|
||||||
echo "Unable to launch release pipeline. Please update the package.json with a stable version.."
|
echo "Unable to launch release pipeline. Please update the package.json with a stable version.."
|
||||||
exit 1
|
exit 1
|
||||||
@@ -64,6 +64,16 @@ echo "[0/5] Cleaning workspace..."
|
|||||||
rm -rf "$ROOT_DIR/dist" "$ROOT_DIR/srpm-out"
|
rm -rf "$ROOT_DIR/dist" "$ROOT_DIR/srpm-out"
|
||||||
echo "Workspace cleaned (dist/ and srpm-out/ removed)"
|
echo "Workspace cleaned (dist/ and srpm-out/ removed)"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Step 0.3 - Retrieve Changelog
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
if [ ! -f "$ROOT_DIR/CHANGELOG.md" ]; then
|
||||||
|
echo "Warning: CHANGELOG.md not found, release notes will be empty."
|
||||||
|
CHANGELOG=""
|
||||||
|
else
|
||||||
|
CHANGELOG="$(cat "$ROOT_DIR/CHANGELOG.md")"
|
||||||
|
echo "Changelog retrieved successfully"
|
||||||
|
fi
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Step 1 - Build Linux packages (AppImage, deb, snap)
|
# Step 1 - Build Linux packages (AppImage, deb, snap)
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
@@ -105,6 +115,10 @@ PREV_TAG=$(git rev-list --tags --skip=1 --max-count=1 | xargs git describe --tag
|
|||||||
|
|
||||||
RELEASE_NOTES="# Freedom Loader - $VERSION
|
RELEASE_NOTES="# Freedom Loader - $VERSION
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
$CHANGELOG
|
||||||
|
|
||||||
## Found a bug or issue?
|
## Found a bug or issue?
|
||||||
Please report it in the [GitHub Issues](https://github.com/MasterAcnolo/Freedom-Loader/issues) section.
|
Please report it in the [GitHub Issues](https://github.com/MasterAcnolo/Freedom-Loader/issues) section.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user