// Main window + sidebar (AGENTS.md §6, §7). // // Phase 3 C2 wires the live connection status (dot + status text under the // title bar) and accepts a `test-connection-requested` callback fired by // the Settings → Connection section. import { AppearanceMode } from "./theme.slint"; import { NavItem, SectionLabel } from "./components.slint"; import { MessageItem } from "./message-card.slint"; import { MessageList } from "./message-list.slint"; import { EmptyState } from "./empty-state.slint"; import { SettingsPage } from "./settings-page.slint"; import { AboutDialog } from "./dialogs.slint"; export enum SidebarSelection { All, Settings, About, } export component MainWindow inherits Window { title: "Gotify"; preferred-width: 960px; preferred-height: 680px; min-width: 760px; min-height: 520px; in-out property theme: AppearanceMode.Dark; out property is-dark: theme != AppearanceMode.Light; out property bg: is-dark ? #1f2024 : #f5f6f8; out property surface: is-dark ? #28292d : #ffffff; out property surface-elev: is-dark ? #2f3035 : #fafbfc; out property border: is-dark ? #3a3b40 : #e4e6ea; out property text-primary: is-dark ? #e6e6e6 : #1a1a1a; out property text-second: is-dark ? #a0a0a0 : #5c5c5c; out property text-muted: is-dark ? #6a6a6a : #9a9a9a; out property accent: is-dark ? #4ea1ff : #0a66c2; out property success: #4caf50; out property warning: #ff9800; out property danger: #f44336; out property priority-low: #6a6a6a; out property priority-normal: is-dark ? #4ea1ff : #0a66c2; out property priority-high: #ff9800; out property priority-max: #f44336; background: bg; in-out property selection: SidebarSelection.All; // ── Phase 2 C2: mock messages + selection ──────────────────── in-out property <[MessageItem]> messages; in-out property selected-message-index: -1; callback test-notification-clicked(); callback message-card-clicked(int); // ── Phase 2 C3: Settings / About / window state ────────────── in-out property appearance-index: 2; callback about-clicked(); // Phase 5 C2: fires whenever the user picks a new theme in Settings. // Rust persists it into `state.json::last_theme`. callback appearance-changed-persist(int); // ── Phase 6 C2: live AppConfig toggles (Settings page) ───────── // Forwarded from SettingsPage so the Rust runtime can: // 1. Persist into `config.json` // 2. For `start_with_windows`, also write HKCU Run. // 3. For `close_to_tray`, immediately affect the close-requested // handler below without a restart. in-out property desktop-notify: true; in-out property sound: true; in-out property notify-high: true; in-out property notify-max: true; in-out property start-with-windows: false; in-out property start-minimized: false; in-out property close-to-tray: true; callback setting-toggled(string, bool); // ── Phase 3 C2: live connection status ─────────────────────── in-out property is-connected: false; in-out property status-text: "Disconnected"; callback test-connection-requested(); HorizontalLayout { // §7 Sidebar width: 220-240px. Rectangle { width: 232px; background: surface; VerticalLayout { padding: 20px; spacing: 6px; Text { text: "Gotify"; color: text-primary; font-size: 18px; font-weight: 700; } Text { text: "Windows Client · Phase 3"; color: text-muted; font-size: 10px; } Rectangle { height: 12px; } // Live status row (Phase 3 C2): dot + text. HorizontalLayout { spacing: 6px; alignment: center; Rectangle { width: 8px; height: 8px; border-radius: 4px; background: is-connected ? success : text-muted; } Text { text: is-connected ? "Connected" : "Disconnected"; color: is-connected ? success : text-muted; font-size: 11px; font-weight: 600; } } Rectangle { height: 12px; } SectionLabel { label: "MESSAGES"; text-color: text-muted; } NavItem { label: "All"; selected: root.selection == SidebarSelection.All; clicked => { root.selection = SidebarSelection.All; } } Rectangle { height: 12px; } SectionLabel { label: "SERVERS"; text-color: text-muted; } Text { text: "(no servers configured)"; color: text-muted; font-size: 11px; } Rectangle { height: 12px; } SectionLabel { label: "FILTERS"; text-color: text-muted; } NavItem { label: "High"; selected: false; has-accent: false; clicked => { /* TODO Phase 3 C2: priority filter */ } } NavItem { label: "Normal"; selected: false; has-accent: false; clicked => { /* TODO Phase 3 C2: priority filter */ } } NavItem { label: "Low"; selected: false; has-accent: false; clicked => { /* TODO Phase 3 C2: priority filter */ } } Rectangle { height: 12px; } SectionLabel { label: "APP"; text-color: text-muted; } NavItem { label: "Settings"; selected: root.selection == SidebarSelection.Settings; clicked => { root.selection = SidebarSelection.Settings; } } NavItem { label: "About"; selected: root.selection == SidebarSelection.About; clicked => { root.selection = SidebarSelection.About; root.about-clicked(); } } } } // Right pane — driven by `selection`. Rectangle { background: bg; VerticalLayout { // Title bar with status text under it. padding: 24px; spacing: 4px; Text { text: messages.length == 0 ? "Messages" : "Messages · " + messages.length; color: text-primary; font-size: 18px; font-weight: 700; } Text { text: status-text; color: text-muted; font-size: 11px; } Rectangle { height: 12px; } if messages.length > 0: MessageList { messages: messages; selected-index: root.selected-message-index; vertical-stretch: 1; message-clicked(idx) => { root.selected-message-index = idx; root.message-card-clicked(idx); } } if messages.length == 0: EmptyState { vertical-stretch: 1; test-notification => { root.test-notification-clicked(); } } } if root.selection == SidebarSelection.Settings: SettingsPage { appearance-index: root.appearance-index; desktop-notify: root.desktop-notify; sound: root.sound; notify-high: root.notify-high; notify-max: root.notify-max; start-with-windows: root.start-with-windows; start-minimized: root.start-minimized; close-to-tray: root.close-to-tray; appearance-changed(idx) => { root.appearance-index = idx; root.theme = idx == 0 ? AppearanceMode.System : idx == 1 ? AppearanceMode.Light : AppearanceMode.Dark; root.appearance-changed-persist(idx); } test-connection-requested => { root.test-connection-requested(); } toggle-persist(label, value) => { root.setting-toggled(label, value); } } // About entry stays as a simple right-pane marker; the // modal AboutDialog overlay is wired through AboutDialog in // this component (visibility toggled via `about-clicked`). if root.selection == SidebarSelection.About: Rectangle { background: bg; Text { x: 24px; y: 24px; text: "About — see the dialog."; color: text-muted; font-size: 13px; } } } } AboutDialog {} }