mirror of
https://gitlab.com/vyla-entertainment/home.git
synced 2026-11-14 14:17:25 -05:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fbb5a4437d | |||
| dbaab9a00e | |||
| fc49bb93a7 | |||
| 9229f0c2dd | |||
| 843abf7df4 | |||
| 27fda0430b | |||
| 8b49d679bd | |||
| 62883b3180 | |||
| 3c6ae0125b | |||
| be0810ea6d | |||
| dff55060e7 | |||
| df767b5ffc | |||
| 1b70c6784a | |||
| e25e1c93f9 | |||
| 4c708ad97c | |||
| b81a4e044b | |||
| f6aa2daa01 | |||
| e03eb38adc | |||
| 2b690b47c1 | |||
| dffbdb92a5 | |||
| 45d6515f2e | |||
| a3e6a278fe | |||
| 7ffc20ee48 | |||
| ddf3abb314 | |||
| ec50c4e07f |
1
DOCUEMENTATION/CLEAR.bash
Normal file
1
DOCUEMENTATION/CLEAR.bash
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bash -c 'pkill -9 -x node 2>/dev/null; pkill -9 -x "Vyla Home" 2>/dev/null; pkill -9 -x "vyla-home" 2>/dev/null; rm -rf "$HOME/Library/Application Support/com.vyla.home" "$HOME/Library/Application Support/vyla-home" "$HOME/Library/Application Support/Vyla Home" "$HOME/Library/Caches/com.vyla.home" "$HOME/Library/Caches/vyla-home" "$HOME/Library/Caches/Vyla Home" "$HOME/Library/WebKit/com.vyla.home" "$HOME/Library/WebKit/vyla-home" "$HOME/Library/WebKit/Vyla Home" "$HOME/Library/Logs/Vyla Home" "$HOME/Library/Preferences/com.vyla.home.plist" "$HOME/.vyla-home" "/Applications/Vyla Home.app" "/Applications/vyla-home.app" "$HOME/Applications/Vyla Home.app" "$HOME/Applications/vyla-home.app" "src-tauri/target" "dist-build" "temp-validation-extract" "dist" "frontend" "player" "auth-proxy" "stream-api" "logs" "*.log" "download-temp.zip" "update-payload.zip" "manifest.json" "validation-report.json" "config/encrypted-credentials.json" "config/credentials.json"; echo "Vyla Home cleanup complete."'
|
||||||
@@ -22,8 +22,8 @@ const clonedRepos = new Set();
|
|||||||
|
|
||||||
function clonePrivateRepo(repoConfig, targetDir) {
|
function clonePrivateRepo(repoConfig, targetDir) {
|
||||||
if (fs.existsSync(targetDir)) {
|
if (fs.existsSync(targetDir)) {
|
||||||
console.log(`[DEV] ${repoConfig.name} already exists locally. Skipping clone to use local changes.`);
|
console.log(`Removing stale ${repoConfig.name} before fresh clone...`);
|
||||||
return;
|
fs.rmSync(targetDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = process.env.GITLAB_TOKEN || process.env.VYLA_GITLAB_TOKEN;
|
const token = process.env.GITLAB_TOKEN || process.env.VYLA_GITLAB_TOKEN;
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ workflows:
|
|||||||
export PATH="$HOME/.cargo/bin:$PATH"
|
export PATH="$HOME/.cargo/bin:$PATH"
|
||||||
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
||||||
node release.js --installer --tag="v$PACKAGE_VERSION"
|
node release.js --installer --tag="v$PACKAGE_VERSION"
|
||||||
|
- name: Verify bundled node permissions
|
||||||
|
script: |
|
||||||
|
if [ -f "bin/node" ]; then
|
||||||
|
chmod +x bin/node
|
||||||
|
xattr -cr bin/node || true
|
||||||
|
ls -la bin/node
|
||||||
|
file bin/node
|
||||||
|
fi
|
||||||
|
APP_NODE=$(find src-tauri/target/release/bundle -name "node" -path "*bin*" 2>/dev/null | head -1)
|
||||||
|
if [ -n "$APP_NODE" ]; then
|
||||||
|
chmod +x "$APP_NODE"
|
||||||
|
xattr -cr "$APP_NODE" || true
|
||||||
|
ls -la "$APP_NODE"
|
||||||
|
fi
|
||||||
artifacts:
|
artifacts:
|
||||||
- src-tauri/target/release/bundle/dmg/*.dmg
|
- src-tauri/target/release/bundle/dmg/*.dmg
|
||||||
- src-tauri/target/release/bundle/macos/*.app
|
- src-tauri/target/release/bundle/macos/*.app
|
||||||
13
main.js
13
main.js
@@ -6,10 +6,19 @@ const ServiceManager = require('./service-manager');
|
|||||||
const Patcher = require('./patcher');
|
const Patcher = require('./patcher');
|
||||||
const UpdateClient = require('./update-client');
|
const UpdateClient = require('./update-client');
|
||||||
|
|
||||||
const defaultAppData = path.join(os.homedir(), 'AppData', 'Roaming', 'vyla-home', 'vyla-home-files');
|
function getDefaultAppData() {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
return path.join(os.homedir(), 'Library', 'Application Support', 'vyla-home', 'vyla-home-files');
|
||||||
|
}
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
return path.join(os.homedir(), '.local', 'share', 'vyla-home', 'vyla-home-files');
|
||||||
|
}
|
||||||
|
return path.join(os.homedir(), 'AppData', 'Roaming', 'vyla-home', 'vyla-home-files');
|
||||||
|
}
|
||||||
|
|
||||||
const APPDATA_DIR = (process.env.APPDATA_PATH && !process.env.APPDATA_PATH.includes('com.vyla.home'))
|
const APPDATA_DIR = (process.env.APPDATA_PATH && !process.env.APPDATA_PATH.includes('com.vyla.home'))
|
||||||
? process.env.APPDATA_PATH
|
? process.env.APPDATA_PATH
|
||||||
: defaultAppData;
|
: getDefaultAppData();
|
||||||
|
|
||||||
const LOGS_DIR = path.join(APPDATA_DIR, 'logs');
|
const LOGS_DIR = path.join(APPDATA_DIR, 'logs');
|
||||||
try { fs.mkdirSync(LOGS_DIR, { recursive: true }); } catch (e) { }
|
try { fs.mkdirSync(LOGS_DIR, { recursive: true }); } catch (e) { }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vyla-home",
|
"name": "vyla-home",
|
||||||
"version": "1.1.13",
|
"version": "1.1.25",
|
||||||
"description": "Vyla Home Desktop App Launcher",
|
"description": "Vyla Home Desktop App Launcher",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -54,4 +54,4 @@
|
|||||||
"installerHeaderIcon": "build/icon.ico"
|
"installerHeaderIcon": "build/icon.ico"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
release.js
11
release.js
@@ -295,11 +295,18 @@ function ensureBundledNode() {
|
|||||||
try {
|
try {
|
||||||
execSync(`tar -xzf node.tar.gz ${tarName}/bin/node`, { cwd: binDir });
|
execSync(`tar -xzf node.tar.gz ${tarName}/bin/node`, { cwd: binDir });
|
||||||
|
|
||||||
fs.renameSync(path.join(binDir, tarName, 'bin', 'node'), path.join(binDir, 'node'));
|
const extractedNode = path.join(binDir, tarName, 'bin', 'node');
|
||||||
|
const finalNode = path.join(binDir, 'node');
|
||||||
|
|
||||||
|
fs.renameSync(extractedNode, finalNode);
|
||||||
fs.rmSync(path.join(binDir, tarName), { recursive: true, force: true });
|
fs.rmSync(path.join(binDir, tarName), { recursive: true, force: true });
|
||||||
fs.unlinkSync(tarPath);
|
fs.unlinkSync(tarPath);
|
||||||
|
|
||||||
execSync(`chmod +x node`, { cwd: binDir });
|
fs.chmodSync(finalNode, 0o755);
|
||||||
|
|
||||||
|
try {
|
||||||
|
execSync(`xattr -d com.apple.quarantine "${finalNode}"`);
|
||||||
|
} catch (_) { }
|
||||||
|
|
||||||
fs.writeFileSync(versionMarker, nodeVersion, 'utf8');
|
fs.writeFileSync(versionMarker, nodeVersion, 'utf8');
|
||||||
resolve();
|
resolve();
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -4222,7 +4222,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vyla-home"
|
name = "vyla-home"
|
||||||
version = "1.1.13"
|
version = "1.1.25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "vyla-home"
|
name = "vyla-home"
|
||||||
version = "1.1.13"
|
version = "1.1.25"
|
||||||
description = "Vyla Home Desktop App Launcher"
|
description = "Vyla Home Desktop App Launcher"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
21
src-tauri/Info.plist
Normal file
21
src-tauri/Info.plist
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsLocalNetworking</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -58,11 +58,23 @@ fn kill_child_process(child: &mut Child) {
|
|||||||
|
|
||||||
fn find_node_binary(app: &tauri::App) -> Option<String> {
|
fn find_node_binary(app: &tauri::App) -> Option<String> {
|
||||||
if let Ok(resource_dir) = app.path().resource_dir() {
|
if let Ok(resource_dir) = app.path().resource_dir() {
|
||||||
|
#[cfg(windows)]
|
||||||
let candidates = [
|
let candidates = [
|
||||||
resource_dir.join("bin").join("node.exe"),
|
resource_dir.join("bin").join("node.exe"),
|
||||||
resource_dir.join("node.exe"),
|
resource_dir.join("node.exe"),
|
||||||
resource_dir.join("_up_").join("bin").join("node.exe"),
|
resource_dir.join("_up_").join("bin").join("node.exe"),
|
||||||
resource_dir.join("_up_").join("node.exe"),
|
resource_dir.join("_up_").join("node.exe"),
|
||||||
|
resource_dir.join("up").join("bin").join("node.exe"),
|
||||||
|
resource_dir.join("up").join("node.exe"),
|
||||||
|
];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let candidates = [
|
||||||
|
resource_dir.join("bin").join("node"),
|
||||||
|
resource_dir.join("node"),
|
||||||
|
resource_dir.join("_up_").join("bin").join("node"),
|
||||||
|
resource_dir.join("_up_").join("node"),
|
||||||
|
resource_dir.join("up").join("bin").join("node"),
|
||||||
|
resource_dir.join("up").join("node"),
|
||||||
];
|
];
|
||||||
for candidate in candidates {
|
for candidate in candidates {
|
||||||
if candidate.exists() {
|
if candidate.exists() {
|
||||||
@@ -75,9 +87,13 @@ fn find_node_binary(app: &tauri::App) -> Option<String> {
|
|||||||
if let Some(parent) = exe_path.parent() {
|
if let Some(parent) = exe_path.parent() {
|
||||||
let candidates = [
|
let candidates = [
|
||||||
parent.join("bin").join("node.exe"),
|
parent.join("bin").join("node.exe"),
|
||||||
|
parent.join("bin").join("node"),
|
||||||
parent.join("node.exe"),
|
parent.join("node.exe"),
|
||||||
|
parent.join("node"),
|
||||||
parent.join("resources").join("bin").join("node.exe"),
|
parent.join("resources").join("bin").join("node.exe"),
|
||||||
|
parent.join("resources").join("bin").join("node"),
|
||||||
parent.join("resources").join("node.exe"),
|
parent.join("resources").join("node.exe"),
|
||||||
|
parent.join("resources").join("node"),
|
||||||
];
|
];
|
||||||
for candidate in candidates {
|
for candidate in candidates {
|
||||||
if candidate.exists() {
|
if candidate.exists() {
|
||||||
@@ -89,9 +105,13 @@ fn find_node_binary(app: &tauri::App) -> Option<String> {
|
|||||||
|
|
||||||
if let Ok(path_var) = std::env::var("PATH") {
|
if let Ok(path_var) = std::env::var("PATH") {
|
||||||
for p in std::env::split_paths(&path_var) {
|
for p in std::env::split_paths(&path_var) {
|
||||||
let candidate = p.join("node.exe");
|
let node_exe = p.join("node.exe");
|
||||||
if candidate.exists() {
|
if node_exe.exists() {
|
||||||
return Some(clean_path(&candidate));
|
return Some(clean_path(&node_exe));
|
||||||
|
}
|
||||||
|
let node = p.join("node");
|
||||||
|
if node.exists() {
|
||||||
|
return Some(clean_path(&node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,6 +140,22 @@ fn find_node_binary(app: &tauri::App) -> Option<String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
let default_paths = [
|
||||||
|
"/usr/local/bin/node",
|
||||||
|
"/usr/bin/node",
|
||||||
|
"/opt/homebrew/bin/node",
|
||||||
|
"/opt/local/bin/node",
|
||||||
|
];
|
||||||
|
for path in default_paths {
|
||||||
|
let candidate = std::path::Path::new(path);
|
||||||
|
if candidate.exists() {
|
||||||
|
return Some(clean_path(candidate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +164,7 @@ fn resolve_script_and_root(app: &tauri::App) -> (String, String) {
|
|||||||
let candidates = vec![
|
let candidates = vec![
|
||||||
resource_dir.join("main.js"),
|
resource_dir.join("main.js"),
|
||||||
resource_dir.join("_up_").join("main.js"),
|
resource_dir.join("_up_").join("main.js"),
|
||||||
|
resource_dir.join("up").join("main.js"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for candidate in candidates {
|
for candidate in candidates {
|
||||||
@@ -561,10 +598,23 @@ fn main() {
|
|||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
debug_log("Tauri setup started");
|
debug_log("Tauri setup started");
|
||||||
let app_data = app.path().app_data_dir().unwrap_or_default();
|
let app_data = app.path().app_data_dir().unwrap_or_default();
|
||||||
let app_data_str = clean_path(&app_data);
|
|
||||||
|
|
||||||
let vyla_files = if let Ok(appdata_env) = std::env::var("APPDATA") {
|
let vyla_files = if let Ok(appdata_env) = std::env::var("APPDATA") {
|
||||||
std::path::PathBuf::from(appdata_env).join("vyla-home").join("vyla-home-files")
|
std::path::PathBuf::from(appdata_env)
|
||||||
|
.join("vyla-home")
|
||||||
|
.join("vyla-home-files")
|
||||||
|
} else if let Ok(home) = std::env::var("HOME") {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let base = std::path::PathBuf::from(&home)
|
||||||
|
.join("Library")
|
||||||
|
.join("Application Support");
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let base = std::path::PathBuf::from(&home)
|
||||||
|
.join(".local")
|
||||||
|
.join("share");
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
|
||||||
|
let base = app_data.clone();
|
||||||
|
base.join("vyla-home").join("vyla-home-files")
|
||||||
} else {
|
} else {
|
||||||
app_data.join("vyla-home-files")
|
app_data.join("vyla-home-files")
|
||||||
};
|
};
|
||||||
@@ -603,29 +653,35 @@ fn main() {
|
|||||||
.inner_size(1280.0, 800.0)
|
.inner_size(1280.0, 800.0)
|
||||||
.decorations(false)
|
.decorations(false)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.devtools(cfg!(debug_assertions))
|
.devtools(true)
|
||||||
.initialization_script("window.__VYLA_WINDOW_LABEL = 'main';")
|
.initialization_script("window.__VYLA_WINDOW_LABEL = 'main';")
|
||||||
.initialization_script(LINK_HANDLER_SCRIPT)
|
.initialization_script(LINK_HANDLER_SCRIPT)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
let (main_js, root_dir) = resolve_script_and_root(app);
|
let (main_js, root_dir) = resolve_script_and_root(app);
|
||||||
|
debug_log("Searching for node binary...");
|
||||||
let node_bin = match find_node_binary(app) {
|
let node_bin = match find_node_binary(app) {
|
||||||
Some(bin) => bin,
|
Some(bin) => {
|
||||||
|
debug_log(&format!("Found node binary: {}", bin));
|
||||||
|
bin
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
let _ = window.emit(
|
debug_log("FATAL: No node binary found in bundle or PATH.");
|
||||||
"error",
|
std::thread::sleep(std::time::Duration::from_millis(600));
|
||||||
serde_json::json!({
|
let _ = window.eval("document.getElementById('status-message').textContent='Error: Node runtime missing. Please reinstall.';");
|
||||||
"message": "Node.js runtime not found. Please ensure Node.js is installed or bundled correctly."
|
let _ = window.eval("var b=document.getElementById('debug-log-box');b.style.display='block';b.textContent='Node binary not found. bin/node missing from app bundle or not executable.';");
|
||||||
}),
|
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let vyla_files_str = clean_path(&vyla_files);
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(400));
|
||||||
|
|
||||||
let mut cmd = Command::new(&node_bin);
|
let mut cmd = Command::new(&node_bin);
|
||||||
cmd.arg(&main_js)
|
cmd.arg(&main_js)
|
||||||
.current_dir(&root_dir)
|
.current_dir(&root_dir)
|
||||||
.env("APPDATA_PATH", &app_data_str)
|
.env("APPDATA_PATH", &vyla_files_str)
|
||||||
.env("NODE_EXE", &node_bin)
|
.env("NODE_EXE", &node_bin)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
@@ -817,12 +873,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug_log(&format!("Failed to spawn node: {}", e));
|
debug_log(&format!("Failed to spawn node: {}", e));
|
||||||
let _ = window.emit(
|
let emsg = format!("Failed to spawn node: {}", e);
|
||||||
"error",
|
let js = format!(
|
||||||
serde_json::json!({
|
"document.getElementById('status-message').textContent='Spawn Error';var b=document.getElementById('debug-log-box');b.style.display='block';b.textContent={};",
|
||||||
"message": format!("Failed to launch backend runtime: {}", e)
|
serde_json::to_string(&emsg).unwrap_or_default()
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
let _ = window.eval(&js);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Vyla Home",
|
"productName": "Vyla Home",
|
||||||
"version": "1.1.13",
|
"version": "1.1.25",
|
||||||
"identifier": "com.vyla.home",
|
"identifier": "com.vyla.home",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../ui"
|
"frontendDist": "../ui"
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"withGlobalTauri": true,
|
"withGlobalTauri": true,
|
||||||
"security": {
|
"security": {
|
||||||
"csp": null
|
"csp": "default-src 'self' http://localhost:4890 http://127.0.0.1:4890; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:4890 http://127.0.0.1:4890 https://esm.sh; style-src 'self' 'unsafe-inline' http://localhost:4890 http://127.0.0.1:4890; img-src 'self' data: https: http://localhost:4890 http://127.0.0.1:4890; font-src 'self' data: http://localhost:4890 http://127.0.0.1:4890; connect-src 'self' http://localhost: http://127.0.0.1:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bundle": {
|
"bundle": {
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
"icons/icon.icns",
|
"icons/icon.icns",
|
||||||
"icons/icon.ico"
|
"icons/icon.ico"
|
||||||
],
|
],
|
||||||
|
"macOS": {
|
||||||
|
"exceptionDomain": "localhost",
|
||||||
|
"infoPlist": "../Info.plist"
|
||||||
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"webviewInstallMode": {
|
"webviewInstallMode": {
|
||||||
"type": "downloadBootstrapper"
|
"type": "downloadBootstrapper"
|
||||||
@@ -41,4 +45,4 @@
|
|||||||
"../node_modules/**/*"
|
"../node_modules/**/*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
275
ui/index.html
275
ui/index.html
@@ -271,61 +271,66 @@
|
|||||||
console.warn("OGL Shader background failed to load (offline):", err);
|
console.warn("OGL Shader background failed to load (offline):", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window.__OGL_LOADED__) {
|
window.dispatchEvent(new Event('ogl-ready'));
|
||||||
document.body.style.background = "radial-gradient(circle at 80% 20%, #1e1b4b 0%, #050505 70%)";
|
</script>
|
||||||
} else {
|
|
||||||
const { Renderer, Program, Triangle, Mesh } = window.__OGL_LOADED__;
|
|
||||||
const rayContainer = document.getElementById("side-rays");
|
|
||||||
const settings = {
|
|
||||||
speed: 2.5,
|
|
||||||
rayColor1: "#EAB308",
|
|
||||||
rayColor2: "#96c8ff",
|
|
||||||
intensity: 2,
|
|
||||||
spread: 2,
|
|
||||||
origin: "top-right",
|
|
||||||
tilt: 0,
|
|
||||||
saturation: 1.5,
|
|
||||||
blend: 0.75,
|
|
||||||
falloff: 1.6,
|
|
||||||
opacity: 1
|
|
||||||
};
|
|
||||||
|
|
||||||
function hexToRgb(hex) {
|
<script>
|
||||||
const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
window.addEventListener('ogl-ready', () => {
|
||||||
if (!match) return [1, 1, 1];
|
if (!window.__OGL_LOADED__) {
|
||||||
return [
|
document.body.style.background = "radial-gradient(circle at 80% 20%, #1e1b4b 0%, #050505 70%)";
|
||||||
parseInt(match[1], 16) / 255,
|
} else {
|
||||||
parseInt(match[2], 16) / 255,
|
const { Renderer, Program, Triangle, Mesh } = window.__OGL_LOADED__;
|
||||||
parseInt(match[3], 16) / 255
|
const rayContainer = document.getElementById("side-rays");
|
||||||
];
|
const settings = {
|
||||||
}
|
speed: 2.5,
|
||||||
|
rayColor1: "#EAB308",
|
||||||
|
rayColor2: "#96c8ff",
|
||||||
|
intensity: 2,
|
||||||
|
spread: 2,
|
||||||
|
origin: "top-right",
|
||||||
|
tilt: 0,
|
||||||
|
saturation: 1.5,
|
||||||
|
blend: 0.75,
|
||||||
|
falloff: 1.6,
|
||||||
|
opacity: 1
|
||||||
|
};
|
||||||
|
|
||||||
function originToFlip(origin) {
|
function hexToRgb(hex) {
|
||||||
switch (origin) {
|
const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
case "top-left": return [1, 0];
|
if (!match) return [1, 1, 1];
|
||||||
case "bottom-right": return [0, 1];
|
return [
|
||||||
case "bottom-left": return [1, 1];
|
parseInt(match[1], 16) / 255,
|
||||||
default: return [0, 0];
|
parseInt(match[2], 16) / 255,
|
||||||
|
parseInt(match[3], 16) / 255
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const renderer = new Renderer({
|
function originToFlip(origin) {
|
||||||
dpr: Math.min(window.devicePixelRatio, 2),
|
switch (origin) {
|
||||||
alpha: true
|
case "top-left": return [1, 0];
|
||||||
});
|
case "bottom-right": return [0, 1];
|
||||||
const gl = renderer.gl;
|
case "bottom-left": return [1, 1];
|
||||||
gl.canvas.style.width = "100%";
|
default: return [0, 0];
|
||||||
gl.canvas.style.height = "100%";
|
}
|
||||||
rayContainer.appendChild(gl.canvas);
|
}
|
||||||
|
|
||||||
const vertexShader = `
|
const renderer = new Renderer({
|
||||||
|
dpr: Math.min(window.devicePixelRatio, 2),
|
||||||
|
alpha: true
|
||||||
|
});
|
||||||
|
const gl = renderer.gl;
|
||||||
|
gl.canvas.style.width = "100%";
|
||||||
|
gl.canvas.style.height = "100%";
|
||||||
|
rayContainer.appendChild(gl.canvas);
|
||||||
|
|
||||||
|
const vertexShader = `
|
||||||
attribute vec2 position;
|
attribute vec2 position;
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = vec4(position, 0.0, 1.0);
|
gl_Position = vec4(position, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const fragmentShader = `
|
const fragmentShader = `
|
||||||
precision highp float;
|
precision highp float;
|
||||||
uniform float iTime;
|
uniform float iTime;
|
||||||
uniform vec2 iResolution;
|
uniform vec2 iResolution;
|
||||||
@@ -386,77 +391,77 @@ void main() {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const [flipX, flipY] = originToFlip(settings.origin);
|
const [flipX, flipY] = originToFlip(settings.origin);
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
iTime: { value: 0 },
|
iTime: { value: 0 },
|
||||||
iResolution: { value: [1, 1] },
|
iResolution: { value: [1, 1] },
|
||||||
iSpeed: { value: settings.speed },
|
iSpeed: { value: settings.speed },
|
||||||
iRayColor1: { value: hexToRgb(settings.rayColor1) },
|
iRayColor1: { value: hexToRgb(settings.rayColor1) },
|
||||||
iRayColor2: { value: hexToRgb(settings.rayColor2) },
|
iRayColor2: { value: hexToRgb(settings.rayColor2) },
|
||||||
iIntensity: { value: settings.intensity },
|
iIntensity: { value: settings.intensity },
|
||||||
iSpread: { value: settings.spread },
|
iSpread: { value: settings.spread },
|
||||||
iFlipX: { value: flipX },
|
iFlipX: { value: flipX },
|
||||||
iFlipY: { value: flipY },
|
iFlipY: { value: flipY },
|
||||||
iTilt: { value: settings.tilt },
|
iTilt: { value: settings.tilt },
|
||||||
iSaturation: { value: settings.saturation },
|
iSaturation: { value: settings.saturation },
|
||||||
iBlend: { value: settings.blend },
|
iBlend: { value: settings.blend },
|
||||||
iFalloff: { value: settings.falloff },
|
iFalloff: { value: settings.falloff },
|
||||||
iOpacity: { value: settings.opacity }
|
iOpacity: { value: settings.opacity }
|
||||||
};
|
};
|
||||||
|
|
||||||
const geometry = new Triangle(gl);
|
const geometry = new Triangle(gl);
|
||||||
const program = new Program(gl, { vertex: vertexShader, fragment: fragmentShader, uniforms });
|
const program = new Program(gl, { vertex: vertexShader, fragment: fragmentShader, uniforms });
|
||||||
const mesh = new Mesh(gl, { geometry, program });
|
const mesh = new Mesh(gl, { geometry, program });
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
const width = rayContainer.clientWidth;
|
const width = rayContainer.clientWidth;
|
||||||
const height = rayContainer.clientHeight;
|
const height = rayContainer.clientHeight;
|
||||||
renderer.dpr = Math.min(window.devicePixelRatio, 2);
|
renderer.dpr = Math.min(window.devicePixelRatio, 2);
|
||||||
renderer.setSize(width, height);
|
renderer.setSize(width, height);
|
||||||
uniforms.iResolution.value = [width * renderer.dpr, height * renderer.dpr];
|
uniforms.iResolution.value = [width * renderer.dpr, height * renderer.dpr];
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("resize", resize);
|
window.addEventListener("resize", resize);
|
||||||
resize();
|
resize();
|
||||||
|
|
||||||
function animate(time) {
|
function animate(time) {
|
||||||
uniforms.iTime.value = time * 0.001;
|
uniforms.iTime.value = time * 0.001;
|
||||||
renderer.render({ scene: mesh });
|
renderer.render({ scene: mesh });
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
requestAnimationFrame(animate);
|
requestAnimationFrame(animate);
|
||||||
}
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
|
|
||||||
function initLiquidButtons() {
|
function initLiquidButtons() {
|
||||||
const borderWidth = 0.16;
|
const borderWidth = 0.16;
|
||||||
const brightness = 35;
|
const brightness = 35;
|
||||||
const opacity = 0.55;
|
const opacity = 0.55;
|
||||||
const blur = 4;
|
const blur = 4;
|
||||||
const displace = 1.2;
|
const displace = 1.2;
|
||||||
const borderRadius = 9999;
|
const borderRadius = 9999;
|
||||||
const distortionScale = -380;
|
const distortionScale = -380;
|
||||||
const redOffset = -25;
|
const redOffset = -25;
|
||||||
const greenOffset = 0;
|
const greenOffset = 0;
|
||||||
const blueOffset = 25;
|
const blueOffset = 25;
|
||||||
const xChannel = "R";
|
const xChannel = "R";
|
||||||
const yChannel = "G";
|
const yChannel = "G";
|
||||||
const mixBlendMode = "screen";
|
const mixBlendMode = "screen";
|
||||||
|
|
||||||
document.querySelectorAll(".liquid-glass-btn").forEach((btn, index) => {
|
document.querySelectorAll(".liquid-glass-btn").forEach((btn, index) => {
|
||||||
const filterId = `glass-filter-${index + 1}`;
|
const filterId = `glass-filter-${index + 1}`;
|
||||||
btn.style.setProperty("--filter-id", `url(#${filterId})`);
|
btn.style.setProperty("--filter-id", `url(#${filterId})`);
|
||||||
|
|
||||||
const feImage = btn.querySelector(".fe-image");
|
const feImage = btn.querySelector(".fe-image");
|
||||||
const redChannel = btn.querySelector(".redchannel");
|
const redChannel = btn.querySelector(".redchannel");
|
||||||
const greenChannel = btn.querySelector(".greenchannel");
|
const greenChannel = btn.querySelector(".greenchannel");
|
||||||
const blueChannel = btn.querySelector(".bluechannel");
|
const blueChannel = btn.querySelector(".bluechannel");
|
||||||
const gaussianBlur = btn.querySelector(".gaussianBlur");
|
const gaussianBlur = btn.querySelector(".gaussianBlur");
|
||||||
|
|
||||||
function generateDisplacementMap() {
|
function generateDisplacementMap() {
|
||||||
const rect = btn.getBoundingClientRect();
|
const rect = btn.getBoundingClientRect();
|
||||||
const actualWidth = rect.width || 140;
|
const actualWidth = rect.width || 140;
|
||||||
const actualHeight = rect.height || 48;
|
const actualHeight = rect.height || 48;
|
||||||
|
|
||||||
const svgContent = `
|
const svgContent = `
|
||||||
<svg viewBox="0 0 ${actualWidth} ${actualHeight}" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 ${actualWidth} ${actualHeight}" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="red-grad-${index}" x1="100%" y1="0%" x2="0%" y2="0%">
|
<linearGradient id="red-grad-${index}" x1="100%" y1="0%" x2="0%" y2="0%">
|
||||||
@@ -473,32 +478,33 @@ void main() {
|
|||||||
<rect width="${actualWidth}" height="${actualHeight}" rx="${borderRadius}" fill="url(#blue-grad-${index})" style="mix-blend-mode:${mixBlendMode}"/>
|
<rect width="${actualWidth}" height="${actualHeight}" rx="${borderRadius}" fill="url(#blue-grad-${index})" style="mix-blend-mode:${mixBlendMode}"/>
|
||||||
</svg>
|
</svg>
|
||||||
`;
|
`;
|
||||||
return "data:image/svg+xml," + encodeURIComponent(svgContent);
|
return "data:image/svg+xml," + encodeURIComponent(svgContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
feImage.setAttribute("href", generateDisplacementMap());
|
feImage.setAttribute("href", generateDisplacementMap());
|
||||||
redChannel.setAttribute("scale", distortionScale + redOffset);
|
redChannel.setAttribute("scale", distortionScale + redOffset);
|
||||||
greenChannel.setAttribute("scale", distortionScale + greenOffset);
|
greenChannel.setAttribute("scale", distortionScale + greenOffset);
|
||||||
blueChannel.setAttribute("scale", distortionScale + blueOffset);
|
blueChannel.setAttribute("scale", distortionScale + blueOffset);
|
||||||
[redChannel, greenChannel, blueChannel].forEach((ch) => {
|
[redChannel, greenChannel, blueChannel].forEach((ch) => {
|
||||||
ch.setAttribute("xChannelSelector", xChannel);
|
ch.setAttribute("xChannelSelector", xChannel);
|
||||||
ch.setAttribute("yChannelSelector", yChannel);
|
ch.setAttribute("yChannelSelector", yChannel);
|
||||||
});
|
});
|
||||||
gaussianBlur.setAttribute("stdDeviation", displace);
|
gaussianBlur.setAttribute("stdDeviation", displace);
|
||||||
}
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
btn.classList.remove("glass-surface--fallback");
|
btn.classList.remove("glass-surface--fallback");
|
||||||
btn.classList.add("glass-surface--svg");
|
btn.classList.add("glass-surface--svg");
|
||||||
|
|
||||||
const observer = new ResizeObserver(() => setTimeout(update, 0));
|
const observer = new ResizeObserver(() => setTimeout(update, 0));
|
||||||
observer.observe(btn);
|
observer.observe(btn);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initLiquidButtons();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
initLiquidButtons();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -535,8 +541,17 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function initTauriEvents() {
|
async function initTauriEvents() {
|
||||||
const eventApi = window.__TAURI__?.event || window.__TAURI_INTERNALS__;
|
let eventApi = null;
|
||||||
if (!eventApi || typeof eventApi.listen !== 'function') {
|
|
||||||
|
if (window.__TAURI__?.event && typeof window.__TAURI__.event.listen === 'function') {
|
||||||
|
eventApi = window.__TAURI__.event;
|
||||||
|
} else if (window.__TAURI_INTERNALS__ && typeof window.__TAURI_INTERNALS__.listen === 'function') {
|
||||||
|
eventApi = window.__TAURI_INTERNALS__;
|
||||||
|
} else if (window.__TAURI__ && typeof window.__TAURI__.listen === 'function') {
|
||||||
|
eventApi = window.__TAURI__;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eventApi) {
|
||||||
setTimeout(initTauriEvents, 50);
|
setTimeout(initTauriEvents, 50);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -572,7 +587,7 @@ void main() {
|
|||||||
const data = e.payload || e;
|
const data = e.payload || e;
|
||||||
if (data && data.url) {
|
if (data && data.url) {
|
||||||
setStatus("Launching...");
|
setStatus("Launching...");
|
||||||
window.location.href = data.url;
|
window.location.replace(data.url);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user