diff --git a/.gitignore b/.gitignore index 11ff8f6..6037aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ podman_scp.* # VM testing _vm_build/ .vm/ +.cache # ISO files *.iso diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e289103 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,99 @@ +# AGENTS.md + +This file provides guidance to coding agents when working with code in this repository. + +## Project Overview + +Hypercube is a container-based Linux distribution built on Fedora Atomic (via Universal Blue's `base-main:43`). It's a keyboard-first Hyprland desktop with vim keybindings, Tokyo Night theming, and developer tooling. The image is built as an OCI container and deployed via bootc. + +## Build Commands + +```bash +just build # Build container image locally (rootless Podman) +just build-force # Build without cache +just build-ghcr # Build for GHCR push (rootful) +just run # Run container interactively for testing +just lint # Shellcheck all .sh files +just format # Format all .sh files with shfmt +just check # Validate Justfile syntax +just fix # Fix Justfile formatting +``` + +### VM/ISO Testing + +```bash +just build-qcow2-fast # Create disk image via bootc install +just run-qcow2 # Test qcow2 in VM with virt-manager +just build-iso-local # Build ISO from local image +just run-iso # Test ISO in QEMU VM +``` + +### Package Version Scripts + +```bash +./scripts/packages/check-upstream-versions.sh # Detect upstream updates +./scripts/packages/check-copr-versions.sh # Compare spec vs COPR builds +./scripts/packages/test-all.sh # Run package script tests +``` + +## Architecture + +### Build Pipeline + +The `Containerfile` defines a multi-stage build: + +1. **Stage `ctx`**: Aggregates `system_files/` and `build_files/` into `/ctx` +2. **Main stage**: Builds from `ghcr.io/ublue-os/base-main:43`, mounts `dot_files/` at `/dot_files`, runs `build.sh` + +`build_files/shared/build.sh` orchestrates the build: + +- Rsyncs `system_files/shared/` to the root filesystem +- Executes numbered scripts (`00-*.sh` through `99-*.sh`) sequentially from each phase directory + +### Build Phases (in order) + +| Directory | Purpose | +| ------------------------ | ----------------------------------------------------- | +| `build_files/base/` | Kernel, greetd, audio, networking, portals | +| `build_files/hyprland/` | Compositor, shell, terminal, editor, CLI tools | +| `build_files/dx/` | Language servers, containers (Distrobox/Podman) | +| `build_files/apps/` | Applications (Steam, etc.) | +| `build_files/hypercube/` | Branding, packages, theming, config deployment, tests | + +### Configuration System + +Configs follow XDG Base Directory specification: + +- `dot_files/` → deployed to `/usr/share/hypercube/config/` (system defaults) +- `/usr/lib/environment.d/60-hypercube-xdg.conf` adds this path to `XDG_CONFIG_DIRS` +- Users override via `~/.config/` +- Fish shell configs go to `/etc/fish/` (Fish doesn't use XDG_CONFIG_DIRS) +- System-level files in `system_files/shared/` mirror the target filesystem path (e.g., `system_files/shared/etc/greetd/config.toml` → `/etc/greetd/config.toml`) + +### Build Validation + +`build_files/hypercube/99-tests.sh` validates the build by checking: + +- Required packages are installed (greetd, hyprland, ghostty, neovim, etc.) +- Required files exist (branding, configs, themes, plymouth) +- os-release contains `ID=hypercube` +- Required services are enabled (greetd, NetworkManager) + +Failures exit with code 1, preventing the image from being published. + +### COPR Packages + +26 custom packages are maintained in `packages/` with RPM spec files. Package metadata, dependencies, and build ordering are defined in `scripts/packages/config.sh`. Packages are built in 5 dependency-ordered batches. + +## Shell Script Conventions + +- All build scripts use `set -ouex pipefail` +- Scripts are named with numeric prefixes for execution ordering +- Package installation uses `dnf5 -y install` +- COPR repos are enabled with `dnf5 -y copr enable owner/repo` + +## Key Integration Points + +- **Titanoboa** (`_titanoboa/`): External ISO builder, cloned on demand by `just build-iso-*` +- **Cosign** (`cosign.pub`): Image signature verification +- **GitHub Actions** (`.github/workflows/build.yml`): Builds on PR and daily, pushes to GHCR on merge to main diff --git a/README.md b/README.md index 7cce054..22c4dea 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,6 @@ | Package | Status | |---------|--------| | quickshell | [![quickshell](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/quickshell/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/quickshell/) | -| regreet | [![regreet](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/regreet/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/regreet/) | | livesys-scripts | [![livesys-scripts](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/livesys-scripts/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/binarypie/hypercube/package/livesys-scripts/) | @@ -90,7 +89,7 @@ Tokyo Night color scheme everywhere: - Terminal emulators - Neovim and all CLI tools - Plymouth boot animation -- ReGreet login screen +- Hypercube greeter - System-wide dark mode enforced ## Screenshots diff --git a/build_files/apps/01-apps.sh b/build_files/apps/01-apps.sh index ebe252a..8eb046b 100644 --- a/build_files/apps/01-apps.sh +++ b/build_files/apps/01-apps.sh @@ -9,4 +9,18 @@ echo "Installing applications..." ### Web Browser dnf5 -y install firefox +### Flatpak Applications (system-wide, baked into image) +flatpak remote-add --if-not-exists --system flathub https://flathub.org/repo/flathub.flatpakrepo +flatpak install --system --noninteractive flathub \ + io.github.kolunmi.Bazaar \ + com.usebottles.bottles \ + io.github.dvlv.boxbuddyrs \ + org.gnome.Boxes \ + com.github.tchx84.Flatseal \ + be.alexandervanhee.gradia \ + io.github.seadve.Kooha \ + io.podman_desktop.PodmanDesktop \ + org.mozilla.Thunderbird \ + io.github.flattool.Warehouse + echo "Applications installed successfully" diff --git a/build_files/base/01-base-system.sh b/build_files/base/01-base-system.sh index 2804f50..9535752 100755 --- a/build_files/base/01-base-system.sh +++ b/build_files/base/01-base-system.sh @@ -1,6 +1,6 @@ #!/bin/bash # Hypercube Base System -# Installs core system components, display manager (greetd + regreet), and hardware support +# Installs core system components, display manager (greetd + hypercube-utils), and hardware support set -ouex pipefail @@ -12,14 +12,12 @@ dnf5 -y clean all ### Enable Hypercube COPR for custom packages dnf5 -y copr enable binarypie/hypercube -### Display Manager: greetd + regreet -# regreet from binarypie/hypercube COPR -# cage is a minimal Wayland compositor to host regreet and the first-boot wizard +### Display Manager: greetd + hypercube-utils +# hypercube-utils provides hypercube-greeter and hypercube-onboard (run directly on TTY) dnf5 -y install \ greetd \ greetd-selinux \ - cage \ - regreet + hypercube-utils ### Desktop Portals & Integration dnf5 -y install \ @@ -84,7 +82,7 @@ dnf5 -y install \ gvfs-gphoto2 \ gvfs-smb -### Flatpak (ensure latest version for preinstall.d support) +### Flatpak dnf5 -y install flatpak ### Homebrew integration (from ublue COPR) diff --git a/build_files/hypercube/03-hypercube-configs.sh b/build_files/hypercube/03-hypercube-configs.sh index b94c841..03ad70f 100755 --- a/build_files/hypercube/03-hypercube-configs.sh +++ b/build_files/hypercube/03-hypercube-configs.sh @@ -78,10 +78,6 @@ install -Dm644 "${CONFIG_DIR}/gtk-4.0/settings.ini" /etc/xdg/gtk-4.0/settings.in install -Dm644 "${CONFIG_DIR}/qt6ct/qt6ct.conf" /etc/xdg/qt6ct/qt6ct.conf install -Dm644 "${CONFIG_DIR}/qt6ct/colors/TokyoNight.conf" /usr/share/qt6ct/colors/TokyoNight.conf -### ReGreet login greeter configuration (Tokyo Night themed) -install -Dm644 "${CONFIG_DIR}/regreet/regreet.toml" /etc/greetd/regreet.toml -install -Dm644 "${CONFIG_DIR}/regreet/regreet.css" /etc/greetd/regreet.css - ### Enable xdg-desktop-portal-gtk for dark mode detection (Firefox, etc.) # This portal provides the org.freedesktop.appearance.color-scheme setting systemctl --global enable xdg-desktop-portal-gtk.service diff --git a/build_files/hypercube/99-tests.sh b/build_files/hypercube/99-tests.sh index 63e69a2..ca64a37 100755 --- a/build_files/hypercube/99-tests.sh +++ b/build_files/hypercube/99-tests.sh @@ -10,8 +10,7 @@ echo "Running Hypercube validation tests..." REQUIRED_PACKAGES=( # Display manager "greetd" - "regreet" - "cage" + "hypercube-utils" # Hyprland stack "hyprland" "hyprlock" diff --git a/dot_files/hypercube-onboard/onboard.toml b/dot_files/hypercube-onboard/onboard.toml new file mode 100644 index 0000000..3fada6d --- /dev/null +++ b/dot_files/hypercube-onboard/onboard.toml @@ -0,0 +1,29 @@ +# Hypercube Onboarding Configuration +# Used by hypercube-onboard during first boot (greetd initial_session) + +[user] +# User creation settings +groups = ["wheel"] +shell = "/usr/bin/fish" +min_password_length = 8 + +[locale] +# Default locale settings +language = "en_US.UTF-8" +keyboard_layout = "us" + +[timezone] +# Timezone preferences +use_ntp = true + +[completion] +# What to do when onboarding finishes +action = "reboot" +remove_initial_session = true + +[updates] +# System updates to run during onboarding + +[[updates.commands]] +description = "System update" +command = "ujust update" diff --git a/dot_files/hypr/hyprland-kiosk.conf b/dot_files/hypr/hyprland-kiosk.conf new file mode 100644 index 0000000..1786b12 --- /dev/null +++ b/dot_files/hypr/hyprland-kiosk.conf @@ -0,0 +1,107 @@ +# Hyprland Kiosk Mode Configuration +# Used for desktop kiosk mode applications +# - No keybindings (locked down) +# - Single fullscreen application +# - Exits when the application closes +# +# Usage: Set KIOSK_CMD environment variable before launching +# KIOSK_CMD="your-app" Hyprland -c /path/to/hyprland-kiosk.conf + +################ +### MONITORS ### +################ + +# Enable all monitors - kiosk app shows on primary, others show wallpaper +monitor=,preferred,auto,auto + +################# +### AUTOSTART ### +################# + +# Wallpaper on all monitors +exec-once = hyprpaper -c /usr/share/hypercube/config/hypr/hyprpaper.conf + +# Launch the kiosk application from environment variable +exec-once = $KIOSK_CMD + +# Exit Hyprland when the last window closes +exec-once = handle=$(hyprctl -j clients | jq -r '.[0].address // empty'); while [ -n "$handle" ]; do sleep 0.5; handle=$(hyprctl -j clients | jq -r '.[0].address // empty'); done; hyprctl dispatch exit + +############################# +### ENVIRONMENT VARIABLES ### +############################# + +env = XCURSOR_SIZE,24 +env = HYPRCURSOR_SIZE,24 + +##################### +### LOOK AND FEEL ### +##################### + +general { + gaps_in = 0 + gaps_out = 0 + border_size = 0 + col.active_border = rgba(00000000) + col.inactive_border = rgba(00000000) + resize_on_border = false + allow_tearing = false + layout = dwindle +} + +decoration { + rounding = 0 + active_opacity = 1.0 + inactive_opacity = 1.0 + shadow { + enabled = false + } + blur { + enabled = false + } +} + +animations { + enabled = false +} + +dwindle { + pseudotile = false + preserve_split = true +} + +misc { + force_default_wallpaper = 0 + disable_hyprland_logo = true +} + +############# +### INPUT ### +############# + +input { + kb_layout = us + kb_variant = + kb_model = + kb_options = + kb_rules = + follow_mouse = 1 + sensitivity = 0 + touchpad { + natural_scroll = false + } +} + +################### +### KEYBINDINGS ### +################### + +# No keybindings - kiosk mode is locked down + +############################## +### WINDOWS AND WORKSPACES ### +############################## + +# Force all windows to fullscreen +windowrule = fullscreen, match:class .* +windowrule = nofocus, match:class ^$, match:title ^$ diff --git a/dot_files/nvim/Containerfile b/dot_files/nvim/Containerfile index 9812c10..0a1e6e8 100644 --- a/dot_files/nvim/Containerfile +++ b/dot_files/nvim/Containerfile @@ -92,7 +92,8 @@ RUN brew install \ jq \ yq \ bat \ - eza + eza \ + just # ============================================================================= # LAYER 4: Languages via Homebrew diff --git a/dot_files/quickshell/modules/bar/StatusBar.qml b/dot_files/quickshell/modules/bar/StatusBar.qml index 3fae25a..62a5dad 100644 --- a/dot_files/quickshell/modules/bar/StatusBar.qml +++ b/dot_files/quickshell/modules/bar/StatusBar.qml @@ -152,8 +152,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: Root.GlobalStates.toggleSidebarLeft(root.targetScreen, "updates") - property bool isRunning: Services.Updates.preinstallRunning - property bool needsAttention: Services.Updates.needsAttention + property bool needsAttention: Services.Updates.updateCount > 0 Rectangle { anchors.fill: parent @@ -169,23 +168,13 @@ PanelWindow { Common.Icon { anchors.centerIn: parent - name: updatesButton.isRunning - ? Common.Icons.icons.refresh - : (updatesButton.needsAttention - ? Common.Icons.icons.download - : Common.Icons.icons.checkCircle) + name: updatesButton.needsAttention + ? Common.Icons.icons.update + : Common.Icons.icons.checkCircle size: Common.Appearance.sizes.iconMedium color: updatesButton.needsAttention ? Common.Appearance.m3colors.primary : Common.Appearance.m3colors.onSurfaceVariant - - RotationAnimation on rotation { - running: updatesButton.isRunning - from: 0 - to: 360 - duration: 1000 - loops: Animation.Infinite - } } } diff --git a/dot_files/quickshell/modules/common/Config.qml b/dot_files/quickshell/modules/common/Config.qml index c98660a..7f3b9f7 100644 --- a/dot_files/quickshell/modules/common/Config.qml +++ b/dot_files/quickshell/modules/common/Config.qml @@ -62,9 +62,6 @@ Singleton { launcher: { maxResults: 50, showCategories: true - }, - system: { - preinstallCompleted: false } }) @@ -111,9 +108,6 @@ Singleton { property int launcherMaxResults: defaults.launcher.maxResults property bool launcherShowCategories: defaults.launcher.showCategories - // System settings - property bool preinstallCompleted: defaults.system.preinstallCompleted - // Load config synchronously at startup FileView { id: configFile @@ -204,11 +198,6 @@ Singleton { launcherShowCategories = config.launcher.showCategories ?? launcherShowCategories } - // System - if (config.system) { - preinstallCompleted = config.system.preinstallCompleted ?? preinstallCompleted - } - console.log("Config: Loaded from", root.configPath) } catch (e) { console.error("Config: Failed to parse:", e) @@ -277,9 +266,6 @@ Singleton { addIfChanged(config, "launcher", "maxResults", launcherMaxResults, defaults.launcher.maxResults) addIfChanged(config, "launcher", "showCategories", launcherShowCategories, defaults.launcher.showCategories) - // System - addIfChanged(config, "system", "preinstallCompleted", preinstallCompleted, defaults.system.preinstallCompleted) - // Only write if there are changes if (Object.keys(config).length === 0) { console.log("Config: No changes from defaults, skipping save") diff --git a/dot_files/quickshell/modules/sidebars/UpdateView.qml b/dot_files/quickshell/modules/sidebars/UpdateView.qml index 467f9e6..4ee1567 100644 --- a/dot_files/quickshell/modules/sidebars/UpdateView.qml +++ b/dot_files/quickshell/modules/sidebars/UpdateView.qml @@ -8,7 +8,7 @@ import "../common" as Common import "../../" as Root import "../../services" as Services -// System updates and setup view for the left sidebar +// System updates view for the left sidebar ColumnLayout { id: root spacing: Common.Appearance.spacing.large @@ -20,7 +20,7 @@ ColumnLayout { Text { Layout.fillWidth: true - text: "System Setup" + text: "System Updates" font.family: Common.Appearance.fonts.main font.pixelSize: Common.Appearance.fontSize.headline font.weight: Font.Medium @@ -50,209 +50,6 @@ ColumnLayout { } } - // Network warning (if no network and preinstall not done) - Rectangle { - visible: !Services.Network.connected && !Services.Updates.preinstallCompleted - Layout.fillWidth: true - Layout.preferredHeight: networkWarningContent.implicitHeight + Common.Appearance.spacing.medium * 2 - radius: Common.Appearance.rounding.large - color: Common.Appearance.m3colors.errorContainer - - RowLayout { - id: networkWarningContent - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.medium - spacing: Common.Appearance.spacing.medium - - Common.Icon { - name: Common.Icons.icons.wifiOff - size: Common.Appearance.sizes.iconLarge - color: Common.Appearance.m3colors.onErrorContainer - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - - Text { - text: "No Network Connection" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - font.weight: Font.Medium - color: Common.Appearance.m3colors.onErrorContainer - } - - Text { - Layout.fillWidth: true - text: "Connect to the internet to install default applications." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - color: Common.Appearance.m3colors.onErrorContainer - wrapMode: Text.WordWrap - opacity: 0.8 - } - } - } - } - - // Flatpak Preinstall Card - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: preinstallContent.implicitHeight + Common.Appearance.spacing.medium * 2 - radius: Common.Appearance.rounding.large - color: Common.Appearance.m3colors.surfaceVariant - - ColumnLayout { - id: preinstallContent - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.medium - spacing: Common.Appearance.spacing.medium - - // Header row - RowLayout { - Layout.fillWidth: true - spacing: Common.Appearance.spacing.medium - - Common.Icon { - name: Services.Updates.preinstallCompleted - ? Common.Icons.icons.checkCircle - : Common.Icons.icons.download - size: Common.Appearance.sizes.iconLarge - color: Services.Updates.preinstallCompleted - ? Common.Appearance.m3colors.primary - : Common.Appearance.m3colors.onSurfaceVariant - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - - Text { - text: "Default Applications" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - font.weight: Font.Medium - color: Common.Appearance.m3colors.onSurface - } - - Text { - Layout.fillWidth: true - text: Services.Updates.preinstallCompleted - ? "Installed" - : (Services.Updates.preinstallRunning - ? Services.Updates.preinstallStatus - : Services.Updates.preinstallTotal + " apps ready to install") - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - color: Common.Appearance.m3colors.onSurfaceVariant - elide: Text.ElideRight - } - } - } - - // Progress bar (when running) - Rectangle { - visible: Services.Updates.preinstallRunning - Layout.fillWidth: true - Layout.preferredHeight: 4 - radius: 2 - color: Common.Appearance.m3colors.surfaceVariant - border.width: 1 - border.color: Common.Appearance.m3colors.outline - - Rectangle { - width: parent.width * (Services.Updates.preinstallProgress() / 100) - height: parent.height - radius: 2 - color: Common.Appearance.m3colors.primary - - Behavior on width { - NumberAnimation { duration: 200 } - } - } - } - - // Action button - MouseArea { - visible: !Services.Updates.preinstallRunning - Layout.fillWidth: true - Layout.preferredHeight: 40 - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - enabled: Services.Network.connected || Services.Updates.preinstallCompleted - - onClicked: { - if (Services.Updates.preinstallCompleted) { - // Reset and allow re-running - Services.Updates.resetPreinstall() - } else { - Services.Updates.runPreinstall() - } - } - - Rectangle { - anchors.fill: parent - radius: Common.Appearance.rounding.medium - color: parent.containsMouse - ? Common.Appearance.m3colors.primary - : Common.Appearance.m3colors.primaryContainer - opacity: parent.enabled ? 1 : 0.5 - } - - Text { - anchors.centerIn: parent - text: Services.Updates.preinstallCompleted - ? "Reinstall Default Apps" - : "Install Now" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - font.weight: Font.Medium - color: parent.containsMouse - ? Common.Appearance.m3colors.onPrimary - : Common.Appearance.m3colors.onPrimaryContainer - } - } - - // Log output (when running or just completed) - Rectangle { - visible: Services.Updates.preinstallRunning || (Services.Updates.preinstallLog.length > 0 && !Services.Updates.preinstallCompleted) - Layout.fillWidth: true - Layout.preferredHeight: 120 - radius: Common.Appearance.rounding.small - color: Common.Appearance.m3colors.surface - clip: true - - ListView { - id: logView - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.small - anchors.rightMargin: Common.Appearance.spacing.small + 8 // Room for scrollbar - model: Services.Updates.preinstallLog - spacing: 2 - boundsBehavior: Flickable.StopAtBounds - - delegate: Text { - required property string modelData - width: logView.width - text: modelData - font.family: Common.Appearance.fonts.mono - font.pixelSize: Common.Appearance.fontSize.smallest - color: Common.Appearance.m3colors.onSurfaceVariant - wrapMode: Text.WrapAnywhere - } - - onCountChanged: { - positionViewAtEnd() - } - - ScrollBar.vertical: ScrollBar { - policy: ScrollBar.AsNeeded - } - } - } - } - } - // System Updates Card Rectangle { Layout.fillWidth: true diff --git a/dot_files/quickshell/modules/welcome/Welcome.qml b/dot_files/quickshell/modules/welcome/Welcome.qml deleted file mode 100644 index 3c4d8f1..0000000 --- a/dot_files/quickshell/modules/welcome/Welcome.qml +++ /dev/null @@ -1,632 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import Quickshell -import Quickshell.Wayland -import Quickshell.Io - -import "../common" as Common - -// First-boot welcome wizard for user creation and initial setup -PanelWindow { - id: root - - required property var screen - - // Full screen overlay - anchors.fill: true - - color: Common.Appearance.m3colors.background - - visible: false // Controlled externally - - property int currentStep: 0 - property int totalSteps: 4 - - // User creation data - property string username: "" - property string fullName: "" - property string password: "" - property string passwordConfirm: "" - property string errorMessage: "" - - // Theme preferences - property bool selectedDarkMode: true - property string selectedAccent: "blue" - - ColumnLayout { - anchors.centerIn: parent - width: Math.min(parent.width - 100, 500) - spacing: Common.Appearance.spacing.xlarge - - // Header - ColumnLayout { - Layout.fillWidth: true - spacing: Common.Appearance.spacing.small - - Text { - Layout.alignment: Qt.AlignHCenter - text: "Welcome to Hypercube" - font.family: Common.Appearance.fonts.title - font.pixelSize: Common.Appearance.fontSize.display - font.weight: Font.Medium - color: Common.Appearance.m3colors.primary - } - - Text { - Layout.alignment: Qt.AlignHCenter - text: getStepTitle() - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.large - color: Common.Appearance.m3colors.onSurfaceVariant - } - } - - // Progress indicator - RowLayout { - Layout.alignment: Qt.AlignHCenter - spacing: Common.Appearance.spacing.small - - Repeater { - model: totalSteps - - Rectangle { - width: 40 - height: 4 - radius: 2 - color: index <= currentStep - ? Common.Appearance.m3colors.primary - : Common.Appearance.m3colors.surfaceVariant - } - } - } - - // Content area - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 320 - radius: Common.Appearance.rounding.xlarge - color: Common.Appearance.m3colors.surfaceVariant - - Loader { - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.xlarge - sourceComponent: { - switch (currentStep) { - case 0: return usernameStep - case 1: return passwordStep - case 2: return themeStep - case 3: return confirmStep - default: return usernameStep - } - } - } - } - - // Error message - Text { - Layout.alignment: Qt.AlignHCenter - visible: errorMessage !== "" - text: errorMessage - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.error - } - - // Navigation buttons - RowLayout { - Layout.fillWidth: true - spacing: Common.Appearance.spacing.medium - - // Back button - MouseArea { - visible: currentStep > 0 - Layout.preferredWidth: 100 - Layout.preferredHeight: 44 - cursorShape: Qt.PointingHandCursor - onClicked: { - errorMessage = "" - currentStep-- - } - - Rectangle { - anchors.fill: parent - radius: Common.Appearance.rounding.large - color: parent.containsMouse - ? Common.Appearance.m3colors.surfaceVariant - : "transparent" - border.width: 1 - border.color: Common.Appearance.m3colors.outline - } - - Text { - anchors.centerIn: parent - text: "Back" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - } - } - - Item { Layout.fillWidth: true } - - // Next/Create button - MouseArea { - Layout.preferredWidth: currentStep === totalSteps - 1 ? 160 : 100 - Layout.preferredHeight: 44 - cursorShape: Qt.PointingHandCursor - onClicked: { - if (validateStep()) { - if (currentStep === totalSteps - 1) { - createUser() - } else { - currentStep++ - } - } - } - - Rectangle { - anchors.fill: parent - radius: Common.Appearance.rounding.large - color: parent.containsMouse - ? Qt.darker(Common.Appearance.m3colors.primary, 1.1) - : Common.Appearance.m3colors.primary - } - - Text { - anchors.centerIn: parent - text: currentStep === totalSteps - 1 ? "Create Account" : "Next" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - font.weight: Font.Medium - color: Common.Appearance.m3colors.onPrimary - } - } - } - } - - // Step 1: Username - Component { - id: usernameStep - - ColumnLayout { - spacing: Common.Appearance.spacing.large - - Text { - Layout.fillWidth: true - text: "Let's create your account. First, choose a username." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - wrapMode: Text.WordWrap - } - - WizardInput { - id: usernameInput - Layout.fillWidth: true - label: "Username" - placeholder: "Enter username" - text: username - onTextChanged: username = text - - Component.onCompleted: forceActiveFocus() - } - - WizardInput { - Layout.fillWidth: true - label: "Full Name (optional)" - placeholder: "Enter your full name" - text: fullName - onTextChanged: fullName = text - } - - Text { - Layout.fillWidth: true - text: "Username must be lowercase, start with a letter, and contain only letters, numbers, underscores, or hyphens." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - color: Common.Appearance.m3colors.onSurfaceVariant - wrapMode: Text.WordWrap - } - } - } - - // Step 2: Password - Component { - id: passwordStep - - ColumnLayout { - spacing: Common.Appearance.spacing.large - - Text { - Layout.fillWidth: true - text: "Create a secure password for your account." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - wrapMode: Text.WordWrap - } - - WizardInput { - id: passwordInput - Layout.fillWidth: true - label: "Password" - placeholder: "Enter password" - isPassword: true - text: password - onTextChanged: password = text - - Component.onCompleted: forceActiveFocus() - } - - WizardInput { - Layout.fillWidth: true - label: "Confirm Password" - placeholder: "Confirm password" - isPassword: true - text: passwordConfirm - onTextChanged: passwordConfirm = text - } - - Text { - Layout.fillWidth: true - text: "Password must be at least 8 characters." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - color: Common.Appearance.m3colors.onSurfaceVariant - wrapMode: Text.WordWrap - } - } - } - - // Step 3: Theme selection - Component { - id: themeStep - - ColumnLayout { - spacing: Common.Appearance.spacing.large - - Text { - Layout.fillWidth: true - text: "Choose your preferred appearance. You can change this later in Settings." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - wrapMode: Text.WordWrap - } - - // Dark/Light mode - RowLayout { - Layout.fillWidth: true - spacing: Common.Appearance.spacing.medium - - ThemeOption { - Layout.fillWidth: true - label: "Dark" - icon: Common.Icons.icons.night - selected: selectedDarkMode - onClicked: selectedDarkMode = true - } - - ThemeOption { - Layout.fillWidth: true - label: "Light" - icon: Common.Icons.icons.sunny - selected: !selectedDarkMode - onClicked: selectedDarkMode = false - } - } - - Text { - Layout.topMargin: Common.Appearance.spacing.medium - text: "Accent Color" - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - font.weight: Font.Medium - color: Common.Appearance.m3colors.onSurfaceVariant - } - - // Accent colors - RowLayout { - Layout.fillWidth: true - spacing: Common.Appearance.spacing.small - - Repeater { - model: [ - { id: "blue", color: "#7aa2f7" }, - { id: "green", color: "#9ece6a" }, - { id: "purple", color: "#bb9af7" }, - { id: "orange", color: "#ff9e64" }, - { id: "red", color: "#f7768e" }, - { id: "cyan", color: "#7dcfff" } - ] - - delegate: MouseArea { - Layout.preferredWidth: 48 - Layout.preferredHeight: 48 - cursorShape: Qt.PointingHandCursor - onClicked: selectedAccent = modelData.id - - Rectangle { - anchors.fill: parent - radius: Common.Appearance.rounding.full - color: modelData.color - - Rectangle { - anchors.centerIn: parent - width: 20 - height: 20 - radius: 10 - visible: selectedAccent === modelData.id - color: "white" - - Common.Icon { - anchors.centerIn: parent - name: Common.Icons.icons.check - size: 14 - color: modelData.color - } - } - } - } - } - } - } - } - - // Step 4: Confirmation - Component { - id: confirmStep - - ColumnLayout { - spacing: Common.Appearance.spacing.large - - Text { - Layout.fillWidth: true - text: "Review your settings and create your account." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - wrapMode: Text.WordWrap - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: summaryColumn.implicitHeight + Common.Appearance.spacing.large * 2 - radius: Common.Appearance.rounding.large - color: Common.Appearance.surfaceLayer(1) - - ColumnLayout { - id: summaryColumn - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.large - spacing: Common.Appearance.spacing.medium - - SummaryRow { label: "Username"; value: username } - SummaryRow { label: "Full Name"; value: fullName || "(not set)" } - SummaryRow { label: "Theme"; value: selectedDarkMode ? "Dark" : "Light" } - SummaryRow { label: "Accent"; value: selectedAccent.charAt(0).toUpperCase() + selectedAccent.slice(1) } - } - } - - Text { - Layout.fillWidth: true - text: "Your account will be added to the 'wheel' group for administrator access." - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - color: Common.Appearance.m3colors.onSurfaceVariant - wrapMode: Text.WordWrap - } - } - } - - // Helper components - component WizardInput: ColumnLayout { - property string label: "" - property string placeholder: "" - property bool isPassword: false - property alias text: inputField.text - - signal textChanged() - - spacing: Common.Appearance.spacing.tiny - - function forceActiveFocus() { - inputField.forceActiveFocus() - } - - Text { - text: label - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.small - font.weight: Font.Medium - color: Common.Appearance.m3colors.onSurfaceVariant - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: Common.Appearance.rounding.medium - color: Common.Appearance.surfaceLayer(1) - border.width: inputField.activeFocus ? 2 : 1 - border.color: inputField.activeFocus - ? Common.Appearance.m3colors.primary - : Common.Appearance.m3colors.outline - - TextInput { - id: inputField - anchors.fill: parent - anchors.margins: Common.Appearance.spacing.medium - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurface - echoMode: isPassword ? TextInput.Password : TextInput.Normal - clip: true - - onTextChanged: parent.parent.textChanged() - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - text: placeholder - font: inputField.font - color: Common.Appearance.m3colors.onSurfaceVariant - visible: !inputField.text && !inputField.activeFocus - } - } - } - } - - component ThemeOption: MouseArea { - property string label: "" - property string icon: "" - property bool selected: false - - Layout.preferredHeight: 80 - cursorShape: Qt.PointingHandCursor - - Rectangle { - anchors.fill: parent - radius: Common.Appearance.rounding.large - color: selected - ? Common.Appearance.m3colors.primaryContainer - : Common.Appearance.surfaceLayer(1) - border.width: selected ? 2 : 1 - border.color: selected - ? Common.Appearance.m3colors.primary - : Common.Appearance.m3colors.outline - - ColumnLayout { - anchors.centerIn: parent - spacing: Common.Appearance.spacing.small - - Common.Icon { - Layout.alignment: Qt.AlignHCenter - name: icon - size: Common.Appearance.sizes.iconXLarge - color: selected - ? Common.Appearance.m3colors.onPrimaryContainer - : Common.Appearance.m3colors.onSurface - } - - Text { - Layout.alignment: Qt.AlignHCenter - text: label - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: selected - ? Common.Appearance.m3colors.onPrimaryContainer - : Common.Appearance.m3colors.onSurface - } - } - } - } - - component SummaryRow: RowLayout { - property string label: "" - property string value: "" - - Layout.fillWidth: true - - Text { - text: label - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - color: Common.Appearance.m3colors.onSurfaceVariant - } - - Item { Layout.fillWidth: true } - - Text { - text: value - font.family: Common.Appearance.fonts.main - font.pixelSize: Common.Appearance.fontSize.normal - font.weight: Font.Medium - color: Common.Appearance.m3colors.onSurface - } - } - - function getStepTitle(): string { - switch (currentStep) { - case 0: return "Create Your Account" - case 1: return "Set Your Password" - case 2: return "Choose Your Theme" - case 3: return "Confirm Your Settings" - default: return "" - } - } - - function validateStep(): bool { - errorMessage = "" - - switch (currentStep) { - case 0: - if (!username || username.trim() === "") { - errorMessage = "Username is required" - return false - } - if (!/^[a-z_][a-z0-9_-]*$/.test(username)) { - errorMessage = "Invalid username format" - return false - } - if (username.length > 32) { - errorMessage = "Username too long (max 32 characters)" - return false - } - return true - - case 1: - if (!password || password.length < 8) { - errorMessage = "Password must be at least 8 characters" - return false - } - if (password !== passwordConfirm) { - errorMessage = "Passwords do not match" - return false - } - return true - - case 2: - return true - - case 3: - return true - - default: - return true - } - } - - function createUser() { - errorMessage = "" - - // Create user via pkexec (polkit) - userCreateProcess.running = true - } - - Process { - id: userCreateProcess - command: ["pkexec", "sh", "-c", - "useradd -m -G wheel -c '" + (fullName || username) + "' '" + username + "' && " + - "echo '" + username + ":" + password + "' | chpasswd" - ] - - running: false - onExited: { - if (exitCode === 0) { - // Save theme preferences - Common.Config.darkMode = selectedDarkMode - Common.Config.accentColor = selectedAccent - Common.Config.save() - - // Hide wizard and signal completion - root.visible = false - userCreated() - } else { - errorMessage = "Failed to create user. Please try again." - } - } - } - - signal userCreated() -} diff --git a/dot_files/quickshell/modules/welcome/qmldir b/dot_files/quickshell/modules/welcome/qmldir deleted file mode 100644 index 2a3f848..0000000 --- a/dot_files/quickshell/modules/welcome/qmldir +++ /dev/null @@ -1,3 +0,0 @@ -module welcome - -Welcome 1.0 Welcome.qml diff --git a/dot_files/quickshell/services/Updates.qml b/dot_files/quickshell/services/Updates.qml index 6d413ce..8f8b35b 100644 --- a/dot_files/quickshell/services/Updates.qml +++ b/dot_files/quickshell/services/Updates.qml @@ -4,9 +4,7 @@ import QtQuick import Quickshell import Quickshell.Io -import "../modules/common" as Common - -// System updates and flatpak preinstall management +// System updates management Singleton { id: root @@ -17,16 +15,8 @@ Singleton { property string lastChecked: "" property string error: "" - // Flatpak preinstall status - property bool preinstallCompleted: Common.Config.preinstallCompleted - property bool preinstallRunning: false - property string preinstallStatus: "" - property var preinstallLog: [] - property int preinstallTotal: 0 - property int preinstallCurrent: 0 - // Whether we need user attention - property bool needsAttention: !preinstallCompleted || updateCount > 0 + property bool needsAttention: updateCount > 0 // Update query script readonly property string updateQueryScript: " @@ -60,17 +50,6 @@ Singleton { echo \"lastChecked=$(date '+%Y-%m-%d %H:%M')\" " - // Count preinstall files script - readonly property string countPreinstallScript: ` - PREINSTALL_DIR="/usr/share/flatpak/preinstall.d" - if [[ -d "$PREINSTALL_DIR" ]]; then - count=$(ls -1 "$PREINSTALL_DIR"/*.preinstall 2>/dev/null | wc -l) - echo "total=$count" - else - echo "total=0" - fi - ` - Process { id: updateProcess command: ["sh", "-c", root.updateQueryScript] @@ -81,60 +60,6 @@ Singleton { } } - Process { - id: countProcess - command: ["sh", "-c", root.countPreinstallScript] - running: false - onExited: { - const output = stdout || "" - const match = output.match(/total=(\d+)/) - if (match) { - root.preinstallTotal = parseInt(match[1]) || 0 - } - } - } - - Process { - id: preinstallProcess - command: ["/usr/libexec/flatpak-preinstall"] - running: false - - stdout: SplitParser { - splitMarker: "\n" - onRead: data => { - if (data.trim() === "") return - root.preinstallLog = [...root.preinstallLog, data] - root.preinstallStatus = data - - // Track progress - if (data.startsWith("Installing:") || data.startsWith("Already installed:")) { - root.preinstallCurrent++ - } - } - } - - onRunningChanged: { - if (running) { - root.preinstallRunning = true - root.preinstallLog = [] - root.preinstallStatus = "Starting flatpak preinstall..." - root.preinstallCurrent = 0 - } - } - - onExited: { - root.preinstallRunning = false - if (exitCode === 0) { - root.preinstallStatus = "Preinstall complete" - root.preinstallCompleted = true - Common.Config.preinstallCompleted = true - Common.Config.save() - } else { - root.preinstallStatus = "Preinstall failed (exit code: " + exitCode + ")" - } - } - } - function parseUpdateOutput() { checking = false error = "" @@ -175,25 +100,6 @@ Singleton { } } - // Run flatpak preinstall - function runPreinstall() { - if (!preinstallRunning) { - preinstallProcess.running = true - } - } - - // Reset preinstall status (to allow re-running) - function resetPreinstall() { - preinstallCompleted = false - Common.Config.preinstallCompleted = false - Common.Config.save() - } - - // Count preinstall files - function countPreinstallApps() { - countProcess.running = true - } - // Auto-check timer (every 6 hours) Timer { interval: 6 * 60 * 60 * 1000 @@ -203,37 +109,6 @@ Singleton { onTriggered: checkUpdates() } - // On startup, count preinstall apps and check if we should auto-run - Component.onCompleted: { - countPreinstallApps() - // Try to auto-run preinstall if network is already connected - autoPreinstallTimer.start() - } - - // Auto-run preinstall when network becomes available - Connections { - target: Network - - function onConnectedChanged() { - if (Network.connected && !root.preinstallCompleted && !root.preinstallRunning) { - // Small delay to let network settle - autoPreinstallTimer.start() - } - } - } - - // Timer to auto-run preinstall (gives network time to settle) - Timer { - id: autoPreinstallTimer - interval: 3000 // 3 seconds - onTriggered: { - if (Network.connected && !root.preinstallCompleted && !root.preinstallRunning) { - console.log("Updates: Auto-starting preinstall...") - runPreinstall() - } - } - } - // Summary string function summary(): string { if (checking) return "Checking for updates..." @@ -242,10 +117,4 @@ Singleton { if (updateCount === 1) return "1 update available" return updateCount + " updates available" } - - // Preinstall progress (0-100) - function preinstallProgress(): real { - if (preinstallTotal === 0) return 0 - return (preinstallCurrent / preinstallTotal) * 100 - } } diff --git a/dot_files/quickshell/welcome-mode.qml b/dot_files/quickshell/welcome-mode.qml deleted file mode 100644 index ae55587..0000000 --- a/dot_files/quickshell/welcome-mode.qml +++ /dev/null @@ -1,406 +0,0 @@ -// Standalone welcome mode for first-boot wizard -// This runs independently of the main shell for user creation - -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtQuick.Window -import Quickshell -import Quickshell.Io - -ShellRoot { - id: root - - // Simple fullscreen window for cage compositor - Window { - id: welcomeWindow - visible: true - visibility: Window.FullScreen - color: "#1a1b26" // Tokyonight background - title: "Hypercube Setup" - - // Wizard content - WelcomeWizard { - anchors.fill: parent - } - } - - // Welcome Wizard Component - component WelcomeWizard: Item { - anchors.fill: parent - - // Theme colors (Tokyonight) - readonly property color bgColor: "#1a1b26" - readonly property color surfaceColor: "#24283b" - readonly property color primaryColor: "#7aa2f7" - readonly property color textColor: "#c0caf5" - readonly property color subtextColor: "#a9b1d6" - readonly property color errorColor: "#f7768e" - - // User data - property string username: "" - property string fullName: "" - property string password: "" - property string passwordConfirm: "" - property string errorMessage: "" - property bool isCreating: false - - ColumnLayout { - anchors.centerIn: parent - width: Math.min(parent.width - 100, 500) - spacing: 32 - - // Header - ColumnLayout { - Layout.fillWidth: true - spacing: 8 - - Text { - Layout.alignment: Qt.AlignHCenter - text: "Welcome to Hypercube" - font.pixelSize: 36 - font.weight: Font.Medium - color: primaryColor - } - - Text { - Layout.alignment: Qt.AlignHCenter - text: "Create Your Account" - font.pixelSize: 18 - color: subtextColor - } - } - - // Content area - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 380 - radius: 16 - color: surfaceColor - - ColumnLayout { - anchors.fill: parent - anchors.margins: 24 - spacing: 16 - - Text { - Layout.fillWidth: true - text: "Let's create your account." - font.pixelSize: 14 - color: textColor - wrapMode: Text.WordWrap - } - - // Username field - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - - Text { - text: "Username" - font.pixelSize: 12 - font.weight: Font.Medium - color: subtextColor - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 8 - color: bgColor - border.width: usernameInput.activeFocus ? 2 : 1 - border.color: usernameInput.activeFocus ? primaryColor : subtextColor - - TextInput { - id: usernameInput - anchors.fill: parent - anchors.margins: 12 - font.pixelSize: 14 - color: textColor - clip: true - text: username - onTextChanged: username = text - activeFocusOnTab: true - KeyNavigation.tab: fullNameInput - Component.onCompleted: forceActiveFocus() - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - text: "Enter username" - font: usernameInput.font - color: subtextColor - visible: !usernameInput.text && !usernameInput.activeFocus - } - } - } - } - - // Full Name field - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - - Text { - text: "Full Name (optional)" - font.pixelSize: 12 - font.weight: Font.Medium - color: subtextColor - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 8 - color: bgColor - border.width: fullNameInput.activeFocus ? 2 : 1 - border.color: fullNameInput.activeFocus ? primaryColor : subtextColor - - TextInput { - id: fullNameInput - anchors.fill: parent - anchors.margins: 12 - font.pixelSize: 14 - color: textColor - clip: true - text: fullName - onTextChanged: fullName = text - activeFocusOnTab: true - KeyNavigation.tab: passwordInput - KeyNavigation.backtab: usernameInput - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - text: "Enter your full name" - font: fullNameInput.font - color: subtextColor - visible: !fullNameInput.text && !fullNameInput.activeFocus - } - } - } - } - - // Password field - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - - Text { - text: "Password" - font.pixelSize: 12 - font.weight: Font.Medium - color: subtextColor - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 8 - color: bgColor - border.width: passwordInput.activeFocus ? 2 : 1 - border.color: passwordInput.activeFocus ? primaryColor : subtextColor - - TextInput { - id: passwordInput - anchors.fill: parent - anchors.margins: 12 - font.pixelSize: 14 - color: textColor - clip: true - echoMode: TextInput.Password - text: password - onTextChanged: password = text - activeFocusOnTab: true - KeyNavigation.tab: confirmInput - KeyNavigation.backtab: fullNameInput - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - text: "Enter password" - font: passwordInput.font - color: subtextColor - visible: !passwordInput.text && !passwordInput.activeFocus - } - } - } - } - - // Confirm Password field - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - - Text { - text: "Confirm Password" - font.pixelSize: 12 - font.weight: Font.Medium - color: subtextColor - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 8 - color: bgColor - border.width: confirmInput.activeFocus ? 2 : 1 - border.color: confirmInput.activeFocus ? primaryColor : subtextColor - - TextInput { - id: confirmInput - anchors.fill: parent - anchors.margins: 12 - font.pixelSize: 14 - color: textColor - clip: true - echoMode: TextInput.Password - text: passwordConfirm - onTextChanged: passwordConfirm = text - activeFocusOnTab: true - KeyNavigation.backtab: passwordInput - - Text { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - text: "Confirm password" - font: confirmInput.font - color: subtextColor - visible: !confirmInput.text && !confirmInput.activeFocus - } - } - } - } - - Text { - Layout.fillWidth: true - text: "Username: lowercase, starts with letter. Password: at least 8 characters." - font.pixelSize: 11 - color: subtextColor - wrapMode: Text.WordWrap - } - } - } - - // Error message - Text { - Layout.alignment: Qt.AlignHCenter - visible: errorMessage !== "" - text: errorMessage - font.pixelSize: 14 - color: errorColor - } - - // Create Account button - MouseArea { - Layout.alignment: Qt.AlignHCenter - Layout.preferredWidth: 180 - Layout.preferredHeight: 48 - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - enabled: !isCreating - onClicked: { - if (validateInput()) { - createUser() - } - } - - Rectangle { - anchors.fill: parent - radius: 12 - color: isCreating ? Qt.darker(primaryColor, 1.3) : parent.containsMouse ? Qt.darker(primaryColor, 1.1) : primaryColor - } - - Text { - anchors.centerIn: parent - text: isCreating ? "Creating..." : "Create Account" - font.pixelSize: 16 - font.weight: Font.Medium - color: bgColor - } - } - - // Info text - Text { - Layout.alignment: Qt.AlignHCenter - text: "Your account will have administrator access." - font.pixelSize: 12 - color: subtextColor - } - } - - function validateInput(): bool { - errorMessage = "" - - if (!username || username.trim() === "") { - errorMessage = "Username is required" - return false - } - if (!/^[a-z_][a-z0-9_-]*$/.test(username)) { - errorMessage = "Invalid username format" - return false - } - if (username.length > 32) { - errorMessage = "Username too long (max 32 characters)" - return false - } - if (!password || password.length < 8) { - errorMessage = "Password must be at least 8 characters" - return false - } - if (password !== passwordConfirm) { - errorMessage = "Passwords do not match" - return false - } - return true - } - - function createUser() { - if (isCreating) return - isCreating = true - errorMessage = "" - userCreateProcess.running = true - } - - // Escape single quotes in strings for shell safety - function shellEscape(str: string): string { - return str.replace(/'/g, "'\\''") - } - - Process { - id: userCreateProcess - // Runs as root via greetd initial_session - // After creating user, remove initial_session from greetd config to prevent wizard on next boot - command: ["sh", "-c", - "useradd -m -G wheel -c '" + shellEscape(fullName || username) + "' '" + shellEscape(username) + "' && " + - "echo '" + shellEscape(username) + ":" + shellEscape(password) + "' | chpasswd && " + - // Remove initial_session block from greetd config - "sed -i '/^\\[initial_session\\]/,/^\\[/{ /^\\[initial_session\\]/d; /^\\[/!d; }' /etc/greetd/config.toml && " + - // Also handle case where initial_session is at end of file (no following section) - "sed -i '/^\\[initial_session\\]/,$d' /etc/greetd/config.toml" - ] - - running: false - onExited: (code, status) => { - isCreating = false - if (code === 0) { - // Success - exit to greetd login screen - Qt.quit() - } else { - errorMessage = "Failed to create user (error " + code + "). Please try again." - } - } - } - - // Allow exiting with Escape key - Shortcut { - sequence: "Escape" - onActivated: { - if (!isCreating) { - // Exit without creating user - greetd will show regreet - Qt.quit() - } - } - } - } -} diff --git a/dot_files/regreet/regreet.css b/dot_files/regreet/regreet.css deleted file mode 100644 index fb5c7a6..0000000 --- a/dot_files/regreet/regreet.css +++ /dev/null @@ -1,7 +0,0 @@ -/* ReGreet CSS - Minimal overrides for Hypercube */ -/* Uses GTK Tokyo Night theme for base styling */ - -/* Transparent window to show wallpaper */ -window { - background-color: transparent; -} diff --git a/dot_files/regreet/regreet.toml b/dot_files/regreet/regreet.toml deleted file mode 100644 index ed031b2..0000000 --- a/dot_files/regreet/regreet.toml +++ /dev/null @@ -1,23 +0,0 @@ -# ReGreet configuration for Hypercube -# See https://github.com/rharish101/ReGreet for full options - -[background] -path = "/usr/share/backgrounds/hypercube/background.png" -fit = "Cover" - -[GTK] -application_prefer_dark_theme = true -cursor_theme_name = "Adwaita" -icon_theme_name = "Tokyonight-Dark" -theme_name = "Tokyonight-Dark" -font_name = "JetBrains Mono 11" - -[appearance] -# Greeting message -greeting = "Welcome to Hypercube" -# Default to Hyprland with uwsm -default_session = "hyprland-uwsm" - -[widget.clock] -format = "%H:%M" -resolution = "1000ms" diff --git a/iso_files/hook-post-rootfs.sh b/iso_files/hook-post-rootfs.sh index 477113e..fc1bbc0 100644 --- a/iso_files/hook-post-rootfs.sh +++ b/iso_files/hook-post-rootfs.sh @@ -118,14 +118,13 @@ grep -q '^livesys_session=' /etc/sysconfig/livesys || echo 'livesys_session=hypr systemctl disable rpm-ostree-countme.service || true systemctl disable bootloader-update.service || true systemctl disable rpm-ostreed-automatic.timer || true -# flatpak-preinstall is now a user service, no need to disable systemctl disable hypercube-first-boot.service || true -# Remove first-boot wizard from greetd config for live environment -# The initial_session runs the Quickshell wizard which is for installed systems only +# Remove onboarding wizard from greetd config for live environment +# The initial_session runs hypercube-onboard which is for installed systems only # On live ISO, we want livesys to handle the session (boots into Hyprland with installer) if [[ -f /etc/greetd/config.toml ]]; then - sed -i '/^\[initial_session\]/,/^$/d' /etc/greetd/config.toml + sed -i '/^\[initial_session\]/,$d' /etc/greetd/config.toml echo "Removed greetd initial_session for live environment" fi diff --git a/packages/regreet/regreet.spec b/packages/regreet/regreet.spec deleted file mode 100644 index 39cdf2a..0000000 --- a/packages/regreet/regreet.spec +++ /dev/null @@ -1,58 +0,0 @@ -# NOTE: This package requires "Enable internet access during builds" in COPR settings -# because cargo needs to download dependencies - -%global debug_package %{nil} - -Name: regreet -Version: 0.2.0 -Release: 1%{?dist} -Summary: Clean and customizable GTK4 greeter for greetd - -License: GPL-3.0-or-later -URL: https://github.com/rharish101/ReGreet -Source0: %{url}/archive/%{version}/%{name}-%{version}.tar.gz - -ExcludeArch: %{ix86} - -BuildRequires: rust >= 1.75.0 -BuildRequires: cargo -BuildRequires: pkgconfig(gtk4) >= 4.0 -BuildRequires: pkgconfig(glib-2.0) -BuildRequires: pkgconfig(pango) -BuildRequires: pkgconfig(cairo) - -Requires: greetd -Requires: gtk4 -Requires: cage - -%description -ReGreet is a clean and customizable GTK4-based greeter for greetd. -It provides a modern login screen experience for Wayland compositors. - -Features: -- Customizable appearance with CSS -- Background image support -- Session selection -- User management - -%prep -%autosetup -n ReGreet-%{version} - -%build -cargo build --release -F gtk4_8 - -%install -install -Dpm 0755 target/release/%{name} %{buildroot}%{_bindir}/%{name} - -# Create cache directory -install -dm 0755 %{buildroot}%{_localstatedir}/cache/%{name} - -%files -%license LICENSE -%doc README.md -%{_bindir}/%{name} -%dir %{_localstatedir}/cache/%{name} - -%changelog -* Tue Dec 31 2024 Hypercube - 0.2.0-1 -- Initial package for Hypercube diff --git a/scripts/packages/config.sh b/scripts/packages/config.sh index 3afe28c..2475973 100755 --- a/scripts/packages/config.sh +++ b/scripts/packages/config.sh @@ -33,7 +33,6 @@ declare -gA PACKAGE_REPOS=( [glaze]="stephenberry/glaze" [uwsm]="Vladimir-csp/uwsm" [quickshell]="quickshell-mirror/quickshell" - [regreet]="rharish101/ReGreet" ) # Version source (release or tag, default is release) @@ -57,7 +56,6 @@ declare -gA PACKAGE_DEPS=( [quickshell]="" [livesys-scripts]="" [wifitui]="" - [regreet]="" # Packages with dependencies [hyprlang]="hyprutils" @@ -77,7 +75,7 @@ declare -gA PACKAGE_DEPS=( # Build batches (packages in same batch can build in parallel) declare -gA BUILD_BATCHES=( - [1]="hyprutils hyprwayland-scanner hyprland-protocols hyprwire glaze uwsm eza starship lazygit quickshell livesys-scripts wifitui regreet" + [1]="hyprutils hyprwayland-scanner hyprland-protocols hyprwire glaze uwsm eza starship lazygit quickshell livesys-scripts wifitui" [2]="hyprlang hyprgraphics aquamarine" [3]="hyprcursor hyprland-qt-support" [4]="hyprland hyprlock hypridle xdg-desktop-portal-hyprland hyprpolkitagent hyprtoolkit" diff --git a/system_files/shared/etc/greetd/config.toml b/system_files/shared/etc/greetd/config.toml index 0996713..4d964d7 100644 --- a/system_files/shared/etc/greetd/config.toml +++ b/system_files/shared/etc/greetd/config.toml @@ -1,15 +1,10 @@ [terminal] -# The VT to run the greeter on vt = 1 [default_session] -# Run regreet in cage (minimal Wayland compositor) with custom CSS -command = "/usr/sbin/cage -s -- /usr/sbin/regreet --style /etc/greetd/regreet.css" +command = "hypercube-greeter" user = "greeter" [initial_session] -# First boot wizard - runs once as root, then exits to show regreet -# Check if regular users exist (UID >= 1000) - if so, skip wizard and remove this block -# Set XDG_RUNTIME_DIR for quickshell/Qt to work properly as root -command = "sh -c 'if getent passwd | awk -F: \"\\$3 >= 1000 && \\$3 < 65534\" | grep -q .; then sed -i \"/^\\[initial_session\\]/,\\$d\" /etc/greetd/config.toml; exit 0; fi; mkdir -p /run/user/0 && export XDG_RUNTIME_DIR=/run/user/0; /usr/sbin/cage -s -- /usr/sbin/quickshell -p /usr/share/hypercube/config/quickshell/welcome-mode.qml'" +command = "hypercube-onboard --config /usr/share/hypercube/config/hypercube-onboard/onboard.toml" user = "root" diff --git a/system_files/shared/usr/lib/bootc/kargs.d/hypercube-kargs.json b/system_files/shared/usr/lib/bootc/kargs.d/hypercube-kargs.json index 6bab70e..be6ccee 100644 --- a/system_files/shared/usr/lib/bootc/kargs.d/hypercube-kargs.json +++ b/system_files/shared/usr/lib/bootc/kargs.d/hypercube-kargs.json @@ -4,6 +4,9 @@ "quiet", "splash", "rd.plymouth=1", - "bgrt_disable" + "bgrt_disable", + "vt.default_red=0x1e,0xff,0xc3,0xff,0x82,0xc0,0x86,0xc8,0x44,0xff,0xc3,0xff,0x65,0xfc,0x86,0xc8", + "vt.default_grn=0x20,0x75,0xe8,0xc7,0xaa,0x99,0xe1,0xd3,0x4a,0x75,0xe8,0xc7,0xbc,0xa7,0xe1,0xd3", + "vt.default_blu=0x30,0x7f,0x8d,0x77,0xff,0xff,0xfc,0xf5,0x73,0x7f,0x8d,0x77,0xff,0xea,0xfc,0xf5" ] } diff --git a/system_files/shared/usr/lib/systemd/user-preset/50-hypercube.preset b/system_files/shared/usr/lib/systemd/user-preset/50-hypercube.preset index b642755..e69de29 100644 --- a/system_files/shared/usr/lib/systemd/user-preset/50-hypercube.preset +++ b/system_files/shared/usr/lib/systemd/user-preset/50-hypercube.preset @@ -1 +0,0 @@ -enable flatpak-preinstall.service diff --git a/system_files/shared/usr/lib/systemd/user/flatpak-preinstall.service b/system_files/shared/usr/lib/systemd/user/flatpak-preinstall.service deleted file mode 100644 index e97cede..0000000 --- a/system_files/shared/usr/lib/systemd/user/flatpak-preinstall.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Preinstall Flatpaks -ConditionPathExists=/usr/bin/flatpak -ConditionPathExists=/usr/libexec/flatpak-preinstall -StartLimitIntervalSec=600 -StartLimitBurst=3 - -[Service] -Type=oneshot -ExecStart=/usr/libexec/flatpak-preinstall -RemainAfterExit=true - -[Install] -WantedBy=default.target diff --git a/system_files/shared/usr/libexec/flatpak-preinstall b/system_files/shared/usr/libexec/flatpak-preinstall deleted file mode 100755 index 5b20e47..0000000 --- a/system_files/shared/usr/libexec/flatpak-preinstall +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/bash -# Flatpak preinstall script for systems without flatpak 1.17+ -# Reads .preinstall files from /usr/share/flatpak/preinstall.d/ -# and installs the specified flatpaks from flathub - -set -euo pipefail - -PREINSTALL_DIR="/usr/share/flatpak/preinstall.d" -REMOTE="flathub" - -if [[ ! -d "$PREINSTALL_DIR" ]]; then - echo "No preinstall directory found: $PREINSTALL_DIR" - exit 0 -fi - -# Check if flathub remote exists -if ! flatpak remote-list --system | grep -q "^${REMOTE}"; then - echo "Flathub remote not configured, skipping preinstall" - exit 0 -fi - -# Parse preinstall files and install flatpaks -for preinstall_file in "$PREINSTALL_DIR"/*.preinstall; do - [[ -f "$preinstall_file" ]] || continue - - # Extract app ID from section header: [Flatpak Preinstall org.example.App] - app_id=$(grep -oP '^\[Flatpak Preinstall \K[^\]]+' "$preinstall_file" 2>/dev/null || true) - - if [[ -z "$app_id" ]]; then - echo "Warning: Could not parse app ID from $preinstall_file" - continue - fi - - # Check if already installed - if flatpak list --system --app --columns=application | grep -q "^${app_id}$"; then - echo "Already installed: $app_id" - continue - fi - - echo "Installing: $app_id" - if flatpak install --system --noninteractive "$REMOTE" "$app_id"; then - echo "Successfully installed: $app_id" - else - echo "Failed to install: $app_id" - fi -done - -echo "Flatpak preinstall complete" diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/bazaar.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/bazaar.preinstall deleted file mode 100644 index ff69b07..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/bazaar.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall io.github.kolunmi.Bazaar] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/bottles.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/bottles.preinstall deleted file mode 100644 index b64863c..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/bottles.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall com.usebottles.bottles] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/boxbuddy.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/boxbuddy.preinstall deleted file mode 100644 index f862449..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/boxbuddy.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall io.github.dvlv.boxbuddyrs] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/boxes.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/boxes.preinstall deleted file mode 100644 index c3f255a..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/boxes.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall org.gnome.Boxes] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/flatseal.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/flatseal.preinstall deleted file mode 100644 index 131f263..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/flatseal.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall com.github.tchx84.Flatseal] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/gradia.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/gradia.preinstall deleted file mode 100644 index 6a06817..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/gradia.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall be.alexandervanhee.gradia] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/kooha.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/kooha.preinstall deleted file mode 100644 index 34b8d90..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/kooha.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall io.github.seadve.Kooha] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/podman-desktop.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/podman-desktop.preinstall deleted file mode 100644 index a6eb0fb..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/podman-desktop.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall io.podman_desktop.PodmanDesktop] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/thunderbird.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/thunderbird.preinstall deleted file mode 100644 index c21653b..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/thunderbird.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall org.mozilla.Thunderbird] -Branch=stable -IsRuntime=false diff --git a/system_files/shared/usr/share/flatpak/preinstall.d/warehouse.preinstall b/system_files/shared/usr/share/flatpak/preinstall.d/warehouse.preinstall deleted file mode 100644 index eec5691..0000000 --- a/system_files/shared/usr/share/flatpak/preinstall.d/warehouse.preinstall +++ /dev/null @@ -1,3 +0,0 @@ -[Flatpak Preinstall io.github.flattool.Warehouse] -Branch=stable -IsRuntime=false