Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 37 additions & 17 deletions src-tauri/src/auto_fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,11 @@ fn generate_random_voice_subset(target_os: &str) -> Vec<String> {
// ── Main generation function ────────────────────────────────────────────────

/// Build a complete CAMOU_CONFIG JSON from a random preset.
pub fn generate_auto_config() -> serde_json::Value {
pub fn generate_auto_config(
change_window_size: bool,
default_outer_width: Option<u32>,
default_outer_height: Option<u32>,
) -> serde_json::Value {
let presets = fingerprint_presets::get_presets();
let mut rng = rand::thread_rng();

Expand Down Expand Up @@ -311,16 +315,26 @@ pub fn generate_auto_config() -> serde_json::Value {
// (any value other than 1.0 is suspicious)

// ── Derive window dimensions from screen ────────────────────────
// Browsers don't usually run maximized — pick a random fraction of
// the available screen area to feel realistic.
// Browsers don't usually run maximized. When enabled, vary window
// geometry per launch; otherwise keep deterministic geometry.
let avail_w = screen.avail_width.or(screen.width).unwrap_or(1920);
let avail_h = screen.avail_height.or(screen.height).unwrap_or(1080);

// Random outer size: 75-100% of available dimensions
let pct_w = rng.gen_range(75..=100) as f64 / 100.0;
let pct_h = rng.gen_range(75..=100) as f64 / 100.0;
let outer_w = ((avail_w as f64 * pct_w) as u32).max(800);
let outer_h = ((avail_h as f64 * pct_h) as u32).max(600);
let (outer_w, outer_h) = if change_window_size {
let pct_w = rng.gen_range(75..=100) as f64 / 100.0;
let pct_h = rng.gen_range(75..=100) as f64 / 100.0;
(
((avail_w as f64 * pct_w) as u32).max(800),
((avail_h as f64 * pct_h) as u32).max(600),
)
} else if let (Some(w), Some(h)) = (default_outer_width, default_outer_height) {
(w.min(avail_w), h.min(avail_h))
} else {
(
((avail_w as f64 * 0.9) as u32).max(800),
((avail_h as f64 * 0.9) as u32).max(600),
)
};
Comment on lines +323 to +337
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block uses several magic numbers (75..=100, 800, 600, 0.9). To improve readability and maintainability, consider defining them as named constants at the top of the module. This makes the code self-documenting and easier to modify in the future.

For example:

const MIN_WINDOW_DIM_PCT: u8 = 75;
const MAX_WINDOW_DIM_PCT: u8 = 100;
const MIN_OUTER_WIDTH: u32 = 800;
const MIN_OUTER_HEIGHT: u32 = 600;
const DEFAULT_WINDOW_SCALE: f64 = 0.9;


config.insert("window.outerWidth".into(), serde_json::json!(outer_w));
config.insert("window.outerHeight".into(), serde_json::json!(outer_h));
Expand All @@ -331,18 +345,24 @@ pub fn generate_auto_config() -> serde_json::Value {
config.insert("window.innerWidth".into(), serde_json::json!(inner_w));
config.insert("window.innerHeight".into(), serde_json::json!(inner_h));

// screenX/screenY: random position within remaining space
// screenX/screenY: random position or centered fallback
let max_x = avail_w.saturating_sub(outer_w);
let max_y = avail_h.saturating_sub(outer_h);
let screen_x = if max_x > 0 {
rng.gen_range(0..=max_x) as i32
} else {
0
};
let screen_y = if max_y > 0 {
rng.gen_range(0..=max_y) as i32
let (screen_x, screen_y) = if change_window_size {
(
if max_x > 0 {
rng.gen_range(0..=max_x) as i32
} else {
0
},
if max_y > 0 {
rng.gen_range(0..=max_y) as i32
} else {
0
},
)
} else {
0
((max_x / 2) as i32, (max_y / 2) as i32)
};
config.insert("window.screenX".into(), serde_json::json!(screen_x));
config.insert("window.screenY".into(), serde_json::json!(screen_y));
Expand Down
8 changes: 7 additions & 1 deletion src-tauri/src/instances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ pub struct FingerprintConfig {

// AUTO mode: let camoufox's built-in browserforge handle all fingerprinting
pub auto_fingerprint: Option<bool>,
pub auto_change_window_size: Option<bool>,
}

/// Convert a FingerprintConfig into a JSON object that camoufox understands
Expand Down Expand Up @@ -482,7 +483,12 @@ pub async fn launch_instance(app: &AppHandle, id: String) -> Result<(), String>
// Build the CAMOU_CONFIG JSON from fingerprint settings
let camou_config_json = if let Some(fp) = config.as_ref().and_then(|c| c.fingerprint.as_ref()) {
if fp.auto_fingerprint == Some(true) {
crate::auto_fingerprint::generate_auto_config().to_string()
crate::auto_fingerprint::generate_auto_config(
fp.auto_change_window_size.unwrap_or(true),
fp.outer_width,
fp.outer_height,
)
.to_string()
} else {
build_camou_config(fp).to_string()
}
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"windows": [
{
"title": "Anon",
"width": 800,
"width": 1100,
"height": 600
}
],
Expand Down
59 changes: 59 additions & 0 deletions src/lib/components/instance/InstanceSettingsModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import MediaDevicesSection from "./settings/MediaDevicesSection.svelte";
import BehaviorSection from "./settings/BehaviorSection.svelte";
import AdvancedSection from "./settings/AdvancedSection.svelte";
import { WINDOW_PRESETS } from "./settings/constants";
import { fingerprintPresets } from "$lib/store";
import type { Preset } from "$lib/store";

Expand Down Expand Up @@ -47,6 +48,8 @@

// AUTO mode state
let autoMode = false;
let autoChangeWindowSize = true;
let autoWindowPresetIndex = -1;
const accentGreen = '#10b981';
const accentGreenBg = 'rgba(16, 185, 129, 0.05)';

Expand All @@ -62,6 +65,12 @@
globalCategory = instance.fingerprint?.global_category ?? "";
globalPresetIndex = instance.fingerprint?.global_preset_index ?? -1;
autoMode = instance.fingerprint?.auto_fingerprint === true;
autoChangeWindowSize = instance.fingerprint?.auto_change_window_size !== false;
autoWindowPresetIndex = WINDOW_PRESETS.findIndex(
(p) =>
p.w === instance.fingerprint?.outer_width &&
p.h === instance.fingerprint?.outer_height
);
}

// ── Save / Reset ───────────────────────────────────────────────────────────
Expand Down Expand Up @@ -118,6 +127,19 @@
fp.global_preset_index = globalPresetIndex >= 0 ? globalPresetIndex : null;
// Persist AUTO mode settings
fp.auto_fingerprint = autoMode || null;
fp.auto_change_window_size = autoMode ? autoChangeWindowSize : null;
if (autoMode) {
const preset = !autoChangeWindowSize && autoWindowPresetIndex >= 0 ? WINDOW_PRESETS[autoWindowPresetIndex] : null;
if (preset) {
fp.outer_width = preset.w;
fp.outer_height = preset.h;
} else {
fp.outer_width = null;
fp.outer_height = null;
}
fp.inner_width = null;
fp.inner_height = null;
}
Comment on lines +130 to +142
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for setting window dimensions in AUTO mode can be refactored to be more concise and avoid repetition. The fp.inner_width = null and fp.inner_height = null assignments are made in both branches of the conditional. You can simplify this by checking for autoMode once and then handling the conditional logic for outer_width and outer_height inside that block, setting the inner dimensions just once at the end.

      fp.auto_change_window_size = autoMode ? autoChangeWindowSize : null;
      if (autoMode) {
        const preset = !autoChangeWindowSize && autoWindowPresetIndex >= 0 ? WINDOW_PRESETS[autoWindowPresetIndex] : null;
        if (preset) {
          fp.outer_width = preset.w;
          fp.outer_height = preset.h;
        } else {
          fp.outer_width = null;
          fp.outer_height = null;
        }
        fp.inner_width = null;
        fp.inner_height = null;
      }

await updateInstanceSettings(instance.id, fp);
dispatch("close");
} catch (e) {
Expand All @@ -135,6 +157,8 @@
globalCategory = "";
globalPresetIndex = -1;
autoMode = false;
autoChangeWindowSize = true;
autoWindowPresetIndex = -1;
}
function handleClose() {
dispatch("close");
Expand Down Expand Up @@ -190,6 +214,41 @@
<p style="margin: 12px 0 0; font-size: 0.6rem; color: var(--text-secondary); letter-spacing: 0.03em; line-height: 1.5;">
Camoufox's built-in browserforge will automatically generate a unique fingerprint on each launch. All fields — navigator, screen, WebGL, fonts, audio, canvas, and more — are handled automatically.
</p>
<div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid rgba(16, 185, 129, 0.2); display: flex; align-items: center; justify-content: space-between; gap: 12px;">
<div style="display: flex; flex-direction: column; gap: 4px;">
<span style="font-size: 0.58rem; letter-spacing: 0.08em; color: var(--text-primary);">CHANGE WINDOW SIZE EACH LAUNCH</span>
<span style="font-size: 0.55rem; letter-spacing: 0.03em; color: var(--text-secondary);">
{autoChangeWindowSize
? 'Randomizes window size and position every launch.'
: 'Keeps window size and position deterministic for the selected profile.'}
</span>
</div>
<button
class="btn {autoChangeWindowSize ? 'btn-active' : ''}"
style="font-size: 0.58rem; padding: 4px 10px; min-width: 50px; letter-spacing: 0.05em; {autoChangeWindowSize ? `background: ${accentGreen}; color: #000; border-color: ${accentGreen};` : ''}"
on:click={() => { autoChangeWindowSize = !autoChangeWindowSize; }}
>
{autoChangeWindowSize ? 'ON' : 'OFF'}
</button>
</div>
<div style="margin-top: 10px; display: flex; flex-direction: column; gap: 6px; opacity: {autoChangeWindowSize ? 0.6 : 1};">
<label for="auto-window-size-preset" style="font-size: 0.58rem; letter-spacing: 0.08em; color: var(--text-primary);">DEFAULT WINDOW SIZE PRESET</label>
<select
id="auto-window-size-preset"
class="input-field"
style="font-size: 0.65rem;"
bind:value={autoWindowPresetIndex}
disabled={autoChangeWindowSize}
>
<option value={-1}>AUTO DEFAULT</option>
{#each WINDOW_PRESETS as p, i}
<option value={i}>{p.label}</option>
{/each}
</select>
<span style="font-size: 0.54rem; letter-spacing: 0.03em; color: var(--text-secondary);">
Applies when window-size changing is OFF.
</span>
</div>
Comment on lines +217 to +251
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This section contains a lot of inline styles. For better maintainability, readability, and separation of concerns, it's recommended to move these styles into the InstanceSettingsModal.css file and use CSS classes instead. This also allows for easier reuse of styles and keeps the HTML markup cleaner. For example, you could create classes like .auto-mode-option and .auto-mode-description for the styled elements.

{/if}
</div>

Expand Down
1 change: 1 addition & 0 deletions src/lib/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export interface FingerprintConfig {

// AUTO mode: let camoufox's browserforge handle all fingerprinting
auto_fingerprint?: boolean | null;
auto_change_window_size?: boolean | null;
}

export interface InstanceConfig {
Expand Down
Loading