pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.modules.common.functions

Singleton {
    id: root
    property string filePath: Directories.shellConfigPath
    property alias options: configOptionsJsonAdapter
    property bool ready: false
    property int readWriteDelay: 50 // milliseconds
    property bool blockWrites: false

    // Tri-state option: "auto" (or anything unrecognized) defers to the
    // caller's default, "on"/"off" force it. Booleans are honored so old
    // configs written before the string form keep working.
    function tristate(value, fallback) {
        if (value === "on" || value === true) return true;
        if (value === "off" || value === false) return false;
        return fallback;
    }

    function setNestedValue(nestedKey, value) {
        let keys = nestedKey.split(".");
        let obj = root.options;
        let parents = [obj];

        // Traverse and collect parent objects
        for (let i = 0; i < keys.length - 1; ++i) {
            if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") {
                obj[keys[i]] = {};
            }
            obj = obj[keys[i]];
            parents.push(obj);
        }

        // Convert value to correct type using JSON.parse when safe
        let convertedValue = value;
        if (typeof value === "string") {
            let trimmed = value.trim();
            if (trimmed === "true" || trimmed === "false" || !isNaN(Number(trimmed))) {
                try {
                    convertedValue = JSON.parse(trimmed);
                } catch (e) {
                    convertedValue = value;
                }
            }
        }

        obj[keys[keys.length - 1]] = convertedValue;
    }

    Timer {
        id: fileReloadTimer
        interval: root.readWriteDelay
        repeat: false
        onTriggered: {
            configFileView.reload()
        }
    }

    Timer {
        id: fileWriteTimer
        interval: root.readWriteDelay
        repeat: false
        onTriggered: {
            configFileView.writeAdapter()
        }
    }

    FileView {
        id: configFileView
        path: root.filePath
        watchChanges: true
        blockWrites: root.blockWrites
        onFileChanged: fileReloadTimer.restart()
        onAdapterUpdated: fileWriteTimer.restart()
        onLoaded: root.ready = true
        onLoadFailed: error => {
            if (error == FileViewError.FileNotFound) {
                writeAdapter();
            }
        }

        JsonAdapter {
            id: configOptionsJsonAdapter

            property string panelFamily: "ii" // "ii", "waffle"

            // Souveraine form-factor gate. Flip to true on a phone deploy:
            // gates the Phone settings category and (later) the single-column
            // layout. Laptop deploys stay false and see the stock page set.
            property JsonObject souveraine: JsonObject {
                property bool phone: false
            }

            property JsonObject policies: JsonObject {
                property int ai: 1 // 0: No | 1: Yes | 2: Local
                property int weeb: 1 // 0: No | 1: Open | 2: Closet
            }

            property JsonObject ai: JsonObject {
                property string systemPrompt: "## Style\n- Use casual tone, don't be formal!\n- Always be brief and to the point, unless asked otherwise\n- Don't repeat the user's question\n- Be approachable: Avoid using overly complicated, domain-specific terms and provide analogies when asked to explain a concept\n\n## Context (ignore when irrelevant)\n- You are a helpful and inspiring sidebar assistant on a {DISTRO} Linux system\n- Desktop environment: {DE}\n- Current date & time: {DATETIME}\n- Focused app: {WINDOWCLASS}\n\n## Presentation\n- Use Markdown features in your response: \n  - **Bold** text to **highlight keywords** in your response\n  - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n"
                property string tool: "functions" // search, functions, or none
                property list<var> extraModels: [
                    {
                        "api_format": "openai", // Most of the time you want "openai". Use "gemini" for Google's models
                        "description": "This is a custom model. Edit the config to add more! | Anyway, this is DeepSeek R1 Distill LLaMA 70B",
                        "endpoint": "https://openrouter.ai/api/v1/chat/completions",
                        "homepage": "https://openrouter.ai/deepseek/deepseek-r1-distill-llama-70b:free", // Not mandatory
                        "icon": "spark-symbolic", // Not mandatory
                        "key_get_link": "https://openrouter.ai/settings/keys", // Not mandatory
                        "key_id": "openrouter",
                        "model": "deepseek/deepseek-r1-distill-llama-70b:free",
                        "name": "Custom: DS R1 Dstl. LLaMA 70B",
                        "requires_key": true
                    }
                ]
            }

            property JsonObject appearance: JsonObject {
                property bool extraBackgroundTint: true
                property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
                property JsonObject fonts: JsonObject {
                    property string main: "Google Sans Flex"
                    property string numbers: "Google Sans Flex"
                    property string title: "Google Sans Flex"
                    property string iconNerd: "JetBrains Mono NF"
                    property string monospace: "JetBrains Mono NF"
                    property string reading: "Readex Pro"
                    property string expressive: "Space Grotesk"
                }
                property JsonObject transparency: JsonObject {
                    property bool enable: false
                    property bool automatic: true
                    property real backgroundTransparency: 0.11
                    property real contentTransparency: 0.57
                }
                property JsonObject wallpaperTheming: JsonObject {
                    property bool enableAppsAndShell: true
                    property bool enableQtApps: true
                    property bool enableTerminal: true
                    property JsonObject terminalGenerationProps: JsonObject {
                        property real harmony: 0.6
                        property real harmonizeThreshold: 100
                        property real termFgBoost: 0.35
                        property bool forceDarkMode: false
                    }
                }
                property JsonObject palette: JsonObject {
                    property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot
                    property string accentColor: ""
                }
            }

            property JsonObject audio: JsonObject {
                // Values in %
                property JsonObject protection: JsonObject {
                    // Prevent sudden bangs
                    property bool enable: false
                    property real maxAllowedIncrease: 10
                    property real maxAllowed: 99
                }
            }

            property JsonObject apps: JsonObject {
                property string bluetooth: "kcmshell6 kcm_bluetooth"
                property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'"
                property string network: "kcmshell6 kcm_networkmanagement"
                property string manageUser: "kcmshell6 kcm_users"
                property string networkEthernet: "kcmshell6 kcm_networkmanagement"
                property string taskManager: "plasma-systemmonitor --page-name Processes"
                property string terminal: "kitty -1" // This is only for shell actions
                property string update: "kitty -1 --hold=yes fish -i -c 'pkexec pacman -Syu'"
                property string volumeMixer: `~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol"`
            }

            property JsonObject background: JsonObject {
                property JsonObject widgets: JsonObject {
                    property JsonObject clock: JsonObject {
                        property bool enable: true
                        property bool showOnlyWhenLocked: false
                        property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy"
                        property real x: 100
                        property real y: 100
                        property string style: "cookie"        // Options: "cookie", "digital"
                        property string styleLocked: "cookie"  // Options: "cookie", "digital"
                        property JsonObject cookie: JsonObject {
                            // aiStyling gates the categorize step in
                            // switchwall.sh (it must be true for wallpapers to
                            // be AI-categorized on apply). aiPreset is the
                            // SEPARATE gate for CookieClock applying a
                            // category preset over the user's configured clock
                            // style — split 2026-08-05 after turning on
                            // aiStyling rewrote the phone's clock
                            // (applyStyle writes these keys). Default off: the
                            // clock keeps whatever the user set.
                            property bool aiStyling: false
                            property bool aiPreset: false
                            property int sides: 14
                            property string dialNumberStyle: "full"   // Options: "dots" , "numbers", "full" , "none"
                            property string hourHandStyle: "fill"     // Options: "classic", "fill", "hollow", "hide"
                            property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide"
                            property string secondHandStyle: "dot"    // Options: "dot", "line", "classic", "hide"
                            property string dateStyle: "bubble"       // Options: "border", "rect", "bubble" , "hide"
                            property bool timeIndicators: true
                            property bool hourMarks: false
                            property bool dateInClock: true
                            property bool constantlyRotate: false
                            property bool useSineCookie: false
                        }
                        property JsonObject digital: JsonObject {
                            property bool adaptiveAlignment: true
                            property bool showDate: true
                            property bool animateChange: true
                            property bool vertical: false
                            property JsonObject font: JsonObject {
                                property string family: "Google Sans Flex"
                                property real weight: 350
                                property real width: 100
                                property real size: 90
                                property real roundness: 0
                            }
                        }
                        property JsonObject quote: JsonObject {
                            property bool enable: false
                            property string text: ""
                        }
                    }
                    property JsonObject weather: JsonObject {
                        property bool enable: false
                        property string placementStrategy: "free" // "free", "leastBusy", "mostBusy"
                        property real x: 400
                        property real y: 100
                    }
                    // Backported 2026-07-13 from laptop-upstream Config: the
                    // borrowed Background.qml reads these unconditionally, and
                    // their absence threw TypeErrors on the laptop. Keys only —
                    // we are NOT pulling the desktop-widget rendering machinery
                    // into this phone-first shell; enable defaults to false.
                    property JsonObject visualizer: JsonObject {
                        property bool enable: false
                        property string placementStrategy: "free"
                        property real x: 500
                        property real y: 500
                        property int bars: 30
                        property string style: "bars"
                        property bool vertical: false
                    }
                    property JsonObject stats: JsonObject {
                        property bool enable: false
                        property string placementStrategy: "free"
                        property real x: 150
                        property real y: 300
                        property string githubUsername: ""
                        property string codeforcesUsername: ""
                        property bool showGraphs: false
                    }
                    property JsonObject systemResources: JsonObject {
                        property bool enable: false
                        property string placementStrategy: "free"
                        property real x: 150
                        property real y: 600
                        property bool showGraphs: false
                    }
                }
                property string wallpaperPath: ""
                property string thumbnailPath: ""
                // Optional local derivatives for unlike display shapes. The
                // original wallpaper remains canonical; variants are never a
                // destructive replacement for it.
                property string portraitVariantPath: ""
                property string landscapeVariantPath: ""
                property real wallpaperFocalX: 0.5
                property real wallpaperFocalY: 0.5
                property bool hideWhenFullscreen: true
                property JsonObject parallax: JsonObject {
                    property bool vertical: false
                    property bool autoVertical: false
                    property bool enableWorkspace: false
                    property real workspaceZoom: 1.07 // Relative to wallpaper size
                    property bool enableSidebar: false
                    property real widgetsFactor: 1.2
                }
            }

            property JsonObject bar: JsonObject {
                // TASK-69/70: one collector for every agent session
                // (Souveraine, Claude Code, Codex). Replaces per-provider
                // fetches; claudeUsage above stays for the subscription
                // gauge until stage 3 folds its OAuth poll in here.
                property JsonObject agentSessions: JsonObject {
                    property bool enable: true // Show the agent island in the bar
                    property int refreshInterval: 60 // seconds
                    property int windowMinutes: 1440 // How far back counts as "a session"
                }
                // Bar layout. One BarContent serves every device; which
                // widgets appear and in what order is data, not a fork.
                // Known names: activeWindow, cellular, resources, media,
                // claudeUsage, workspaces, clock, utilButtons, island,
                // battery, pomodoro, systray. An unknown name renders
                // nothing, so ["none"] deliberately empties a slot; an
                // empty list means "unset" and falls back to the profile.
                property JsonObject layout: JsonObject {
                    // "auto" derives from the bar's own cramped-ness test
                    // (the same one behind useShortenedForm), so a phone
                    // needs no config. "desktop" | "compact" pin it.
                    property string profile: "auto"
                    property list<string> left: []
                    property list<string> centerLeft: []
                    property list<string> centerMiddle: []
                    property list<string> centerRight: []
                    property list<string> right: []
                }
                property JsonObject clock: JsonObject {
                    property string format: "" // Empty: pick by profile
                }
                property JsonObject autoHide: JsonObject {
                    property bool enable: false
                    property int hoverRegionWidth: 2
                    property bool pushWindows: false
                    property JsonObject showWhenPressingSuper: JsonObject {
                        property bool enable: true
                        property int delay: 140
                    }
                }
                property bool bottom: false // Instead of top
                property int cornerStyle: 0 // 0: Hug | 1: Float | 2: Plain rectangle
                property bool floatStyleShadow: true // Show shadow behind bar when cornerStyle == 1 (Float)
                property bool borderless: false // true for no grouping of items
                property string topLeftIcon: "spark" // Options: "distro" or any icon name in ~/.config/quickshell/ii/assets/icons
                property bool showBackground: true
                property bool verbose: true
                property bool vertical: false
                property JsonObject resources: JsonObject {
                    // "auto" follows the active bar profile: compact bars
                    // rotate one stat; desktop bars render the full group.
                    // "on"/"off" override it. Never null — a null var is
                    // serialized into config.json and crashes JsonAdapter on
                    // the next load.
                    property string rotate: "auto"
                    property int rotateInterval: 4
                    property bool alwaysShowSwap: true
                    property bool alwaysShowCpu: true
                    property int memoryWarningThreshold: 95
                    property int swapWarningThreshold: 85
                    property int cpuWarningThreshold: 90
                }
                property list<string> screenList: [] // List of names, like "eDP-1", find out with 'hyprctl monitors' command
                property JsonObject utilButtons: JsonObject {
                    property bool showScreenSnip: true
                    property bool showColorPicker: false
                    property bool showMicToggle: false
                    property bool showKeyboardToggle: true
                    property bool showDarkModeToggle: true
                    property bool showPerformanceProfileToggle: false
                    property bool showScreenRecord: false
                }
                property JsonObject workspaces: JsonObject {
                    property bool monochromeIcons: true
                    property int shown: 10
                    property bool showAppIcons: true
                    property bool alwaysShowNumbers: false
                    property int showNumberDelay: 300 // milliseconds
                    property list<string> numberMap: ["1", "2"] // Characters to show instead of numbers on workspace indicator
                    property bool useNerdFont: false
                }
                // Backported 2026-07-13: borrowed ClaudeUsage service + bar
                // widget read these unconditionally. Keys only, enable=false.
                property JsonObject claudeUsage: JsonObject {
                    property bool enable: false // Show a Claude (Pro/Max) subscription usage gauge in the bar
                    property bool defaultWeekly: false // Start on the 7-day window; click to switch session <-> week
                    property int warningThreshold: 90 // Turn red at/above this utilization (%)
                    property int fetchInterval: 5 // minutes
                }
                property JsonObject weather: JsonObject {
                    property bool enable: false
                    property bool enableGPS: true // gps based location
                    property string city: "" // When 'enableGPS' is false
                    property bool useUSCS: false // Instead of metric (SI) units
                    property int fetchInterval: 10 // minutes
                }
                property JsonObject indicators: JsonObject {
                    property JsonObject notifications: JsonObject {
                        property bool showUnreadCount: false
                    }
                    // Status icons inside the right-hand pill. "auto" follows
                    // the profile: a compact bar drops xkb and bluetooth.
                    // "on"/"off" override it. Never null — see bar.resources.
                    property string showXkb: "auto"
                    property string showBluetooth: "auto"
                }
                property JsonObject tooltips: JsonObject {
                    property bool clickToShow: false
                }
            }

            property JsonObject battery: JsonObject {
                property int low: 20
                property int critical: 5
                property int full: 101
                property bool automaticSuspend: true
                property int suspend: 3
            }

            property JsonObject calendar: JsonObject {
                property string locale: "en-GB"
            }

            property JsonObject cheatsheet: JsonObject {
                // Use a nerdfont to see the icons
                // 0: 󰖳  | 1: 󰌽 | 2: 󰘳 | 3:  | 4: 󰨡
                // 5:  | 6:  | 7: 󰣇 | 8:  | 9: 
                // 10:  | 11:  | 12:  | 13:  | 14: 󱄛
                property string superKey: ""
                property bool useMacSymbol: false
                property bool splitButtons: false
                property bool useMouseSymbol: false
                property bool useFnSymbol: false
                property JsonObject fontSize: JsonObject {
                    property int key: Appearance.font.pixelSize.smaller
                    property int comment: Appearance.font.pixelSize.smaller
                }
            }

            property JsonObject conflictKiller: JsonObject {
                property bool autoKillNotificationDaemons: false
                // DO NOT auto-kill trays here. On this phone kded6 is not a
                // competing Plasma tray — it is the StatusNotifierWatcher that
                // ii's OWN tray (Quickshell.Services.SystemTray) registers as a
                // host with (IsStatusNotifierHostRegistered=true). Killing it
                // just makes D-Bus re-activate it on ii's next tray call — an
                // infinite loop. Our services/ConflictKiller.qml override drops
                // the kded6 check entirely. Leave this false.
                property bool autoKillTrays: false
            }

            property JsonObject crosshair: JsonObject {
                // Valorant crosshair format. Use https://www.vcrdb.net/builder
                property string code: "0;P;d;1;0l;10;0o;2;1b;0"
            }

            property JsonObject dock: JsonObject {
                property bool enable: false
                property bool monochromeIcons: true
                property real height: 60
                property real hoverRegionHeight: 2
                property bool pinnedOnStartup: false
                property bool hoverToReveal: true // When false, only reveals on empty workspace
                property list<string> pinnedApps: [ // IDs of pinned entries
                    "org.kde.dolphin", "kitty",]
                property list<string> ignoredAppRegexes: []
                // Fan-out stacks (macOS "Fan" mode). Each entry is
                // "stackId|appId,appId,appId" — phone-local, no Phosh sync.
                // stackId is a display label; members render as an arc.
                property list<string> stacks: []
                // Feel knobs (single source — components read these, never
                // hardcode): dragDwellMs = how long a drag hovers an icon
                // or stack before it reads as combine-intent (GNOME's
                // rule); gestureRailHeight = bottom strip the dock reserves
                // for Souveraine's integrated navigation rail, visually and
                // in its input mask.
                property int dragDwellMs: 500
                property real gestureRailHeight: 32
                // How big the dock draws. `buttonSize` is the one number the
                // row's height follows and every icon is derived from, so the
                // dock scales as a piece rather than each part being tuned
                // apart. 56/44 replaced a hardcoded 44/35 that read small on a
                // 540px panel; it lives here so it is a setting rather than a
                // rebuild.
                property real buttonSize: 56
                property real iconSize: 44
            }

            property JsonObject interactions: JsonObject {
                property JsonObject scrolling: JsonObject {
                    property bool fasterTouchpadScroll: false // Enable faster scrolling with touchpad
                    property int mouseScrollDeltaThreshold: 120 // delta >= this then it gets detected as mouse scroll rather than touchpad
                    property int mouseScrollFactor: 120
                    property int touchpadScrollFactor: 450
                }
                property JsonObject deadPixelWorkaround: JsonObject { // Hyprland leaves out 1 pixel on the right for interactions
                    property bool enable: false
                }
            }

            property JsonObject language: JsonObject {
                property string ui: "auto" // UI language. "auto" for system locale, or specific language code like "zh_CN", "en_US"
                property JsonObject translator: JsonObject {
                    property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
                    property string targetLanguage: "auto" // Run `trans -list-all` for available languages
                    property string sourceLanguage: "auto"
                }
            }

            property JsonObject launcher: JsonObject {
                property list<string> pinnedApps: [ "org.kde.dolphin", "kitty", "cmake-gui"]
            }

            property JsonObject light: JsonObject {
                property JsonObject night: JsonObject {
                    property bool automatic: true
                    property string from: "19:00" // Format: "HH:mm", 24-hour time
                    property string to: "06:30"   // Format: "HH:mm", 24-hour time
                    property int colorTemperature: 5000
                }
                property JsonObject antiFlashbang: JsonObject {
                    property bool enable: false
                }
            }

            property JsonObject lock: JsonObject {
                property bool useHyprlock: false
                property bool launchOnStartup: false
                property int dpmsTimeout: 3
                property string unlockHook: ""
                // Souveraine: PIN keypad surface for touch panels (phone).
                // The OSK can't rise above a session lock, so the lock
                // surface must carry its own input.
                property bool touchKeypad: false
                property JsonObject blur: JsonObject {
                    property bool enable: true
                    property real radius: 100
                    property real extraZoom: 1.1
                }
                property bool centerClock: true
                // 12-hour glance clock (am/pm). Off = 24-hour.
                property bool twelveHourClock: true
                // Lock-screen wallpaper. Empty = follow the system wallpaper
                // (background.wallpaperPath); a path pins the lock's own.
                property string wallpaperPath: ""
                property bool showLockedText: true
                // A visible wiring exercise for the FPC1020 path. It never
                // unlocks the session; the separate Polkit setting below is
                // the temporary, post-login-only factor.
                property JsonObject fingerprintPreview: JsonObject {
                    property bool enabled: false
                    property int holdMs: 3000
                }
                // The temporary FPC factor belongs only to polkit-1. It cannot
                // satisfy first login, disk unlock, sudo, SSH, or lock-screen
                // PAM. The real souveraine-fpd match result will replace this
                // raw-reader bridge without changing the user-facing surface.
                property JsonObject fingerprintPolkit: JsonObject {
                    property bool enabled: true
                }
                // Souveraine-owned lock cards. Transport controls are
                // ambient; media metadata remains personal by default.
                property JsonObject content: JsonObject {
                    property bool showMediaControls: true
                    property bool mediaMetadataAmbient: false
                    property bool showBattery: true
                    // App identity + count are ambient; the summary line is
                    // personal until promoted. Bodies never render on lock.
                    property bool showNotifications: true
                    property bool notificationContentAmbient: false
                }
                property JsonObject idle: JsonObject {
                    // Native idle-notify remains opt-in until verified on the
                    // Pixel compositor; hypridle is the current adapter.
                    property bool nativeCoordinatorEnabled: false
                    // The lock timer is the one the user sets. The dim is
                    // expressed RELATIVE to it — how long before the lock the
                    // screen starts fading — so the two can never cross and
                    // moving one moves the other. They were independent
                    // absolutes (120 and 300), which meant picking a dim
                    // longer than the lock silently produced a dim that never
                    // fired. sessiond's lock-screen dim has always been
                    // relative (`dim_at = budget - dim_grace`); this is the
                    // shell saying the same thing the same way.
                    //
                    // Default 180 reproduces the old pair exactly: 300 - 180 =
                    // 120. An existing config.json keeps its lockAfterSeconds
                    // and picks this default up, so nobody's timing changes.
                    property int lockAfterSeconds: 300
                    property int dimBeforeLockSeconds: 180
                    // Backlight level while Dimmed; Active restores the
                    // brightnessctl-saved value.
                    property string dimBrightness: "30%"
                }
                property JsonObject security: JsonObject {
                    property bool unlockKeyring: true
                    property bool requirePasswordToPower: false
                    // Whether the lock surface may trigger power-off / reboot
                    // at all. Off by default (fail-closed): a destructive
                    // action from the locked screen is opt-in. The system
                    // polkit rule (souveraine-login1.rules) lets the active
                    // session power off without challenge once unlocked; this
                    // gate keeps the *locked* surface from reaching it until
                    // the user enables it. requirePasswordToPower then arms
                    // the action to be confirmed by the PIN unlock.
                    property bool allowPowerFromLock: false
                }
                // Step-up authentication grants for sensitive operations.
                // The PAM service (souveraine-stepup) is root-owned system
                // config and must be installed separately before enabling.
                property JsonObject stepUp: JsonObject {
                    property int grantTtlMs: 300000  // 5 minutes
                    property bool enabled: false  // requires souveraine-stepup PAM service
                }
                property bool materialShapeChars: true
            }

            property JsonObject media: JsonObject {
                // Attempt to remove dupes (the aggregator playerctl one and browsers' native ones when there's plasma browser integration)
                property bool filterDuplicatePlayers: true
            }

            property JsonObject networking: JsonObject {
                property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
            }

            property JsonObject notifications: JsonObject {
                property int timeout: 7000
                // Upstream modules (NotificationPopup, settings InterfaceConfig)
                // read forceMonitor; the old "monitor" name left that undefined
                // and threw a TypeError on every popup evaluation.
                property JsonObject forceMonitor: JsonObject {
                    property bool enable: false
                    property string name: "" // Name of the monitor to show notifications on, like "eDP-1". Find out with 'hyprctl monitors' command
                }
            }

            property JsonObject osd: JsonObject {
                property int timeout: 1000
            }

            property JsonObject osk: JsonObject {
                property string layout: "qwerty_full"
                property bool pinnedOnStartup: false
            }

            // Speech services — network STT/TTS on Casey's VPN. The shell
            // and the souveraine-stt/tts CLIs read the same config file, so
            // the settings page here is the single source of truth for both.
            property JsonObject speech: JsonObject {
                property JsonObject stt: JsonObject {
                    property bool enable: true
                    // faster-whisper REST bridge: POST multipart 'audio'
                    // (WAV) -> {"text": ...}. /health for the status probe.
                    property string endpoint: "http://10.10.20.19:7862/transcribe"
                }
                property JsonObject tts: JsonObject {
                    property bool enable: false
                    // Speech-synthesis endpoint: POST {"text": ...} -> audio
                    // stream. Empty until a TTS server exists on the VPN.
                    property string endpoint: ""
                }
            }

            // Text-selection action menu (TASK-18).
            //
            // OPT-IN ON PURPOSE, and do not flip this default casually. When
            // enabled, the shell watches the compositor's primary selection,
            // which means it observes EVERY piece of text the user highlights
            // anywhere on the device — including a password highlighted inside
            // a password manager. No tier in SESSION-AUTHORITY-DOCTRINE §2
            // covers an ambient capability of that reach, so the user turns it
            // on knowingly or it does not run. Selection.qml additionally kills
            // its watcher whenever the session is locked and never persists or
            // logs selection content.
            property JsonObject selection: JsonObject {
                property bool enable: false
                // Read Aloud is the one action with a live backend today; it
                // routes through Speech.speak() and so also obeys
                // speech.tts.enable. The rest are stubs until they earn a
                // backend (TASK-18: a stubbed action is fine, a janky menu is not).
                property bool readAloud: true
                // Agent actions ("Talk about this" / "Add to our conversation")
                // reach conversation history, which is `personal` tier — they
                // stay dark until the session is genuinely unlocked.
                property bool agentActions: true
                // Reference lookups stay strictly local/offline by default,
                // per TASK-18's stated default stance: never touch the agent.
                property bool referenceActions: true
            }

            property JsonObject overlay: JsonObject {
                property bool openingZoomAnimation: true
                property bool darkenScreen: true
                property real clickthroughOpacity: 0.8
                property JsonObject floatingImage: JsonObject {
                    property string imageSource: "https://media.tenor.com/H5U5bJzj3oAAAAAi/kukuru.gif"
                    property real scale: 0.5
                }
            }

            property JsonObject overview: JsonObject {
                property bool enable: true
                property real scale: 0.18 // Relative to screen size
                property real rows: 2
                property real columns: 5
                property bool orderRightLeft: false
                property bool orderBottomUp: false
                property bool centerIcons: true
                // The app drawer's own grid (TASK-14). Kept nested instead of
                // overloading rows/columns above — those are the desktop
                // overview's workspace-grid knobs, and the drawer is a
                // different density. The settings app writes these.
                property JsonObject appGrid: JsonObject {
                    property int columns: 4
                    property int rows: 5
                    property int iconSize: 44
                }
            }

            property JsonObject regionSelector: JsonObject {
                property JsonObject targetRegions: JsonObject {
                    property bool windows: true
                    property bool layers: false
                    property bool content: true
                    property bool showLabel: false
                    property real opacity: 0.3
                    property real contentRegionOpacity: 0.8
                    property int selectionPadding: 5
                }
                property JsonObject rect: JsonObject {
                    property bool showAimLines: true
                }
                property JsonObject circle: JsonObject {
                    property int strokeWidth: 6
                    property int padding: 10
                }
                property JsonObject annotation: JsonObject {
                    property bool useSatty: false
                }
            }

            property JsonObject resources: JsonObject {
                property int updateInterval: 3000
                property int historyLength: 60
            }

            property JsonObject tray: JsonObject {
                property bool monochromeIcons: true
                property bool showItemId: false
                property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area
                property list<var> pinnedItems: [ "Fcitx" ]
                property bool filterPassive: true
            }

            property JsonObject musicRecognition: JsonObject {
                property int timeout: 16
                property int interval: 4
            }

            property bool autoIdleInhibit: false

            property JsonObject search: JsonObject {
                property int nonAppResultDelay: 30 // This prevents lagging when typing
                property string engineBaseUrl: "https://www.google.com/search?q="
                property list<string> excludedSites: ["quora.com", "facebook.com"]
                property bool sloppy: false // Uses levenshtein distance based scoring instead of fuzzy sort. Very weird.
                property JsonObject prefix: JsonObject {
                    property bool showDefaultActionsWithoutPrefix: true
                    property string action: "/"
                    property string app: ">"
                    property string clipboard: ";"
                    property string emojis: ":"
                    property string math: "="
                    property string shellCommand: "$"
                    property string webSearch: "?"
                }
                property JsonObject imageSearch: JsonObject {
                    property string imageSearchEngineBaseUrl: "https://lens.google.com/uploadbyurl?url="
                    property bool useCircleSelection: false
                }
                // Backported 2026-07-13: borrowed LauncherSearch reads this
                // unconditionally. Key only, enable=false.
                property JsonObject fileSearch: JsonObject {
                    property bool enable: false
                    property list<string> paths: [Directories.home]
                    property list<string> exclude: [".git", "node_modules", "target", "build"]
                    property bool excludeHiddenDirs: true
                    property int maxResults: 30
                }
            }

            property JsonObject sidebar: JsonObject {
                property bool keepRightSidebarLoaded: true
                property int width: 460 // Collapsed sidebar width in px
                property int widthExtended: 750 // Extended (Ctrl+O) sidebar width in px
                property JsonObject translator: JsonObject {
                    property bool enable: false
                    property int delay: 300 // Delay before sending request. Reduces (potential) rate limits and lag.
                }
                property JsonObject ai: JsonObject {
                    property bool textFadeIn: false
                    // Chat message text size. Live binding to the theme value
                    // (never a literal, and never null — see
                    // null-in-a-serialized-config, 2026-08-11). ii-base added
                    // this key after our override was forked, and because this
                    // file SHADOWS ii-base's Config wholesale, the key simply
                    // vanished: MessageTextBlock read it as undefined and
                    // assigned undefined to font.pixelSize on every render.
                    property int fontSize: Appearance.font.pixelSize.small
                }
                property JsonObject booru: JsonObject {
                    property bool allowNsfw: false
                    property string defaultProvider: "yandere"
                    property int limit: 20
                    property JsonObject zerochan: JsonObject {
                        property string username: "[unset]"
                    }
                }
                property JsonObject cornerOpen: JsonObject {
                    property bool enable: true
                    property bool bottom: false
                    property bool valueScroll: true
                    property bool clickless: false
                    property int cornerRegionWidth: 250
                    property int cornerRegionHeight: 5
                    property bool visualize: false
                    property bool clicklessCornerEnd: true
                    property int clicklessCornerVerticalOffset: 1
                }

                property JsonObject quickToggles: JsonObject {
                    property string style: "android" // Options: classic, android
                    property JsonObject android: JsonObject {
                        property int columns: 5
                        property list<var> toggles: [
                            { "size": 2, "type": "network" },
                            { "size": 2, "type": "bluetooth"  },
                            { "size": 1, "type": "idleInhibitor" },
                            { "size": 1, "type": "mic" },
                            { "size": 2, "type": "audio" },
                            { "size": 2, "type": "nightLight" }
                        ]
                    }
                }

                property JsonObject quickSliders: JsonObject {
                    property bool enable: false
                    property bool showMic: false
                    property bool showVolume: true
                    property bool showBrightness: true
                }
            }

            property JsonObject screenRecord: JsonObject {
                property string savePath: Directories.videos.replace("file://","") // strip "file://"
            }

            property JsonObject screenSnip: JsonObject {
                property string savePath: "" // only copy to clipboard when empty
            }

            property JsonObject sounds: JsonObject {
                property bool battery: false
                property bool pomodoro: false
                property string theme: "freedesktop"
            }

            property JsonObject time: JsonObject {
                // https://doc.qt.io/qt-6/qtime.html#toString
                property string format: "hh:mm"
                property string shortDateFormat: "dd/MM"
                property string dateWithYearFormat: "dd/MM/yyyy"
                property string dateFormat: "ddd, dd/MM"
                property JsonObject pomodoro: JsonObject {
                    property int breakTime: 300
                    property int cyclesBeforeLongBreak: 4
                    property int focus: 1500
                    property int longBreak: 900
                }
                property bool secondPrecision: false
            }

            property JsonObject updates: JsonObject {
                property bool enableCheck: true
                property int checkInterval: 120 // minutes
                property int adviseUpdateThreshold: 75 // packages
                property int stronglyAdviseUpdateThreshold: 200 // packages
            }
            
            property JsonObject wallpaperSelector: JsonObject {
                property bool useSystemFileDialog: false
            }
            
            property JsonObject windows: JsonObject {
                property bool showTitlebar: true // Client-side decoration for shell apps
                property bool centerTitle: true
            }

            property JsonObject hacks: JsonObject {
                property int arbitraryRaceConditionDelay: 20 // milliseconds
            }

            property JsonObject workSafety: JsonObject {
                property JsonObject enable: JsonObject {
                    property bool wallpaper: false
                    property bool clipboard: false
                }
                property JsonObject triggerCondition: JsonObject {
                    property list<string> networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"]
                    property list<string> fileKeywords: ["anime", "booru", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"]
                    property list<string> linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"]
                }
            }

            property JsonObject waffles: JsonObject {
                // Some spots are kinda janky/awkward. Setting the following to
                // false will make (some) stuff also be like that for accuracy. 
                // Example: the right-click menu of the Start button
                property JsonObject tweaks: JsonObject {
                    property bool switchHandlePositionFix: true
                    property bool smootherMenuAnimations: true
                    property bool smootherSearchBar: true
                }
                property JsonObject bar: JsonObject {
                    property bool bottom: true
                    property bool leftAlignApps: false
                }
                property JsonObject actionCenter: JsonObject {
                    property list<string> toggles: [ "network", "vpn", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
                }
                property JsonObject calendar: JsonObject {
                    property bool force2CharDayOfWeek: true
                }
            }
        }
    }
}
