pragma Singleton

// ─────────────────────────────────────────────────────────────────────────────
// Tokyo Night Moon — the single source of truth for every color, radius,
// duration and font in the shell. Nothing below this file should hardcode a
// color; if you find yourself writing "#" outside this file, add a token here.
//
// The bar keeps GNOME's 36px information density but sits directly on the
// desktop, edge to edge. The dock retains the former glass vocabulary: bottom,
// 48px icons and dot indicators.
// ─────────────────────────────────────────────────────────────────────────────

import Quickshell
// QtQuick is required even though nothing visual is declared here: `color` is a
// QtQuick value type, and Qt.rgba() lives in its JS namespace.
import QtQuick
import qs.services

Singleton {
    id: root

    // ── Color scheme ───────────────────────────────────────────────────────
    // Tokyo Night ships an official light variant (Day), so light mode is that
    // rather than a palette invented to merely not be dark. The two share the
    // same hues at different lightness, which is what lets the Prism identity
    // survive the switch: blue still leads into orchid, it is simply a darker
    // blue on a lighter ground.
    //
    // Every token below is a binding on this, so flipping it repaints the whole
    // shell without anything needing to know it happened.
    readonly property bool dark: DesktopPreferences.get("colorScheme") !== "light"

    // ── Palette ─────────────────────────────────────────────────────────────
    // Canonical Tokyo Night Moon. `accent` matches the GNOME accent exactly.
    readonly property color bg: root.dark ? "#222436" : "#e1e2e7"
    readonly property color bgDark: root.dark ? "#1e2030" : "#d3d5de"
    readonly property color bgHighlight: root.dark ? "#2f334d" : "#c4c8da"
    readonly property color bgPanel: root.dark ? "#2e2f3d" : "#d9dae3" // dock glass surface
    readonly property color bgPopover: root.dark ? "#21212f" : "#eaeaee" // Openbar submenu background

    readonly property color fg: root.dark ? "#c8d3f5" : "#3760bf"
    readonly property color fgDim: root.dark ? "#828bb8" : "#6172b0"
    readonly property color fgMuted: root.dark ? "#636da6" : "#848cb5"
    readonly property color gutter: root.dark ? "#3b4261" : "#a8aecb"

    // ── The accent ──────────────────────────────────────────────────────────
    //
    // `accent` is the primary and carries every state meaning (focused, active,
    // on). `accentSecondary` never appears alone, only as the far end of a
    // gradient. That restraint is the whole point: the two colors meeting is
    // the signature, so the second color stops being special the moment it is
    // used as a flat fill.
    //
    // NAMED accents rather than a free color. Each name carries a curated
    // triple per scheme, because an arbitrary hex cannot work in both: a color
    // legible on the Moon background is usually illegible on the Day one, and a
    // picker that lets someone choose an unreadable desktop is not a feature.
    // Every pair below measures at least 3:1 against the ground it sits on.
    // This is also GNOME's model, which is the parity being chased.
    //
    // Blue is the shipped Prism -- blue leading, orchid following -- and stays
    // the default.
    //
    // The table itself lives in services/ThemeProfileModel.js so a curated
    // accent and a custom profile are the same kind of record. Its `gnome`
    // member is the nearest name in GNOME's own accent-color enum, which is a
    // fixed list of nine we do not get to extend; it is what libadwaita
    // applications -- Files, Papers, Loupe -- are told to use, so choosing an
    // accent here recolors them too instead of leaving them in GNOME blue.
    readonly property var accents: ThemeProfiles.curatedAccents
    readonly property var activeProfile: ThemeProfiles.activeProfile

    // ThemeProfiles validates every persisted record before it can become
    // active, so these bindings are both reactive and safe to expose as the
    // shell-wide colour roles.
    readonly property color accent: root.activeProfile.accent
    readonly property color accentSecondary: root.activeProfile.secondary
    readonly property color accentAlt: root.dark ? "#65bcff" : "#007197" // blue1, a lighter blue
    readonly property color cyan: root.dark ? "#86e1fc" : "#007197"
    readonly property color teal: root.dark ? "#4fd6be" : "#118c74"
    readonly property color green: root.dark ? "#c3e88d" : "#587539"
    readonly property color yellow: root.dark ? "#ffc777" : "#8c6c3e"
    readonly property color orange: root.dark ? "#ff966c" : "#b15c00"
    readonly property color red: root.dark ? "#ff757f" : "#f52a65"
    readonly property color redDeep: root.dark ? "#c53b53" : "#c64343"
    readonly property color magenta: root.dark ? "#c099ff" : "#9854f1"
    readonly property color pink: root.dark ? "#fca7ea" : "#d20065"

    // Semantic aliases — prefer these in widgets so intent survives a repaint.
    readonly property color ok: green
    readonly property color warn: yellow
    readonly property color danger: red
    readonly property color urgent: orange

    // ── Surface alphas ──────────────────────────────────────────────────────
    // The dock sits on compositor blur, so its fill is deliberately very
    // translucent. Popovers are near-opaque because they carry text that must
    // stay legible. The bar itself has no surface or alpha token.
    // Light surfaces need more opacity for the same sense of a solid panel: the
    // same 0.34 that reads as glass over a dark desktop reads as haze over a
    // light one, and text on it stops being legible.
    readonly property real dockAlpha: root.dark ? 0.34 : 0.62
    readonly property real popoverAlpha: root.dark ? 0.92 : 0.97

    // Toasts sit a little lighter than a popover you opened on purpose. A
    // notification arrives unbidden over whatever you were doing, and at full
    // popover weight it reads as a dialog demanding an answer -- but the 6%
    // foreground tint it used to have left the text competing with the desktop
    // behind it. This is the point between the two.
    readonly property real toastAlpha: root.dark ? 0.86 : 0.94
    readonly property real overlayAlpha: root.dark ? 0.55 : 0.40
    readonly property real hoverAlpha: root.dark ? 0.14 : 0.10
    readonly property real activeAlpha: root.dark ? 0.24 : 0.18

    // ── Geometry ────────────────────────────────────────────────────────────
    readonly property int barHeight: 36
    readonly property int barGap: 6 // breathing room below the bar for popovers
    readonly property int barSideMargin: 10 // inset for floating popovers

    readonly property int dockIconSize: DesktopPreferences.get("dockIconSize")
    readonly property int dockPadding: 8
    readonly property int dockGap: 8
    readonly property int dockRadius: 20

    readonly property int popoverRadius: 18
    readonly property int popoverPadding: 14
    readonly property int popoverWidth: 380
    readonly property int controlCenterWidth: 430

    readonly property int cardRadius: 12
    readonly property int pillRadius: 999
    readonly property int itemSpacing: 8
    readonly property int sectionSpacing: 14

    // ── Type ────────────────────────────────────────────────────────────────
    // Adwaita Sans for everything the user reads as text. No exceptions: a
    // monospaced clock or percentage reads as a terminal readout pasted into a
    // UI, which is the opposite of the intent here.
    readonly property string fontFamily: DesktopPreferences.get("interfaceFont")

    // Nerd Font, used ONLY to draw icon glyphs — never for text. It is the
    // pragmatic alternative to freedesktop symbolic icons, which ship with a
    // hardcoded dark fill Qt will not recolor (see widgets/ThemedIcon.qml).
    readonly property string fontMono: DesktopPreferences.get("iconFont")

    // Apply to any text whose digits change in place — clocks, percentages,
    // elapsed timers, dimension readouts. Tabular figures all share one
    // advance width, so the text stops twitching as the numbers tick without
    // needing a monospaced face.
    //
    //   font.family: Theme.fontFamily
    //   font.features: Theme.tabularFigures
    readonly property var tabularFigures: ({
            "tnum": 1
        })
    // The four sizes are a scale, not four independent numbers: they move
    // together so the relationship between body, caption, heading and title
    // survives a change to the base rather than drifting apart.
    readonly property int fontSize: DesktopPreferences.get("interfaceFontSize")
    readonly property int fontSizeSmall: Math.max(9, root.fontSize - 2)
    readonly property int fontSizeLarge: root.fontSize + 3
    readonly property int fontSizeTitle: root.fontSize + 7

    // ── Motion ──────────────────────────────────────────────────────────────
    // Event-driven only. Nothing in this shell animates while idle — no pulse,
    // no shimmer, no spinners. These durations are used for open/close/hover.
    readonly property int durFast: 120
    readonly property int durNormal: 200
    readonly property int durSlow: 320

    // The dock revealing is the one animation that answers a live pointer
    // movement, so it gets its own (much shorter) duration. Anything slower
    // reads as the desktop lagging behind the cursor rather than as motion.
    readonly property int durDockReveal: 90

    // Matches the "snappy" spring curve defined in hypr/looks.lua.
    readonly property list<real> easeStandard: [0.05, 0.9, 0.1, 1.0]
    readonly property list<real> easeOvershoot: [0.34, 1.56, 0.64, 1.0]

    // ── Helpers ─────────────────────────────────────────────────────────────
    // Qt.alpha() does not exist; this is the idiomatic way to re-alpha a token.
    function alpha(c: color, a: real): color {
        return Qt.rgba(c.r, c.g, c.b, a);
    }

    // Blend two tokens — used for hover/pressed states so we never invent a
    // color that isn't derived from the palette.
    function mix(a: color, b: color, ratio: real): color {
        return Qt.rgba(a.r + (b.r - a.r) * ratio, a.g + (b.g - a.g) * ratio, a.b + (b.b - a.b) * ratio, a.a + (b.a - a.a) * ratio);
    }

    // ── The prism ───────────────────────────────────────────────────────────
    // Where the two accents meet. Used for the hairline along the top edge of
    // every glass surface (widgets/PrismEdge.qml), the focused window border,
    // the active workspace pill and slider fills.
    //
    // Where the gradient runs left-to-right the blue leads, because the eye
    // reads left first and blue is the color that carries meaning.
    readonly property real prismStart: 0.22
    readonly property real prismEnd: 0.78

    // Opacity of the hairline itself. Deliberately below 1: at full strength it
    // reads as a drawn line rather than as light catching an edge.
    readonly property real prismEdgeAlpha: 0.85
}
