From 411e60709245441f1153f6486b828310404dd150 Mon Sep 17 00:00:00 2001 From: binarypie Date: Sat, 17 Jan 2026 09:26:41 -0800 Subject: [PATCH 1/6] WIP vimgreetn --- build_files/base/01-base-system.sh | 12 +- build_files/hypercube/03-hypercube-configs.sh | 4 - build_files/hypercube/99-tests.sh | 2 +- dot_files/foot/foot-kiosk.ini | 74 ++++++++++++ dot_files/hypr/hyprland-kiosk.conf | 107 ++++++++++++++++++ system_files/shared/etc/greetd/config.toml | 9 +- .../systemd/system/greetd.service.d/pty.conf | 4 + 7 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 dot_files/foot/foot-kiosk.ini create mode 100644 dot_files/hypr/hyprland-kiosk.conf create mode 100644 system_files/shared/etc/systemd/system/greetd.service.d/pty.conf diff --git a/build_files/base/01-base-system.sh b/build_files/base/01-base-system.sh index 2804f50..110e030 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 + vimgreet), and hardware support set -ouex pipefail @@ -12,14 +12,16 @@ 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 + vimgreet +# vimgreet from binarypie/hypercube COPR +# cage is a minimal Wayland compositor to host vimgreet and the first-boot wizard +# foot is a lightweight terminal for running vimgreet with proper colors/cursor dnf5 -y install \ greetd \ greetd-selinux \ cage \ - regreet + foot \ + vimgreet ### Desktop Portals & Integration dnf5 -y install \ 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..c92d60e 100755 --- a/build_files/hypercube/99-tests.sh +++ b/build_files/hypercube/99-tests.sh @@ -10,7 +10,7 @@ echo "Running Hypercube validation tests..." REQUIRED_PACKAGES=( # Display manager "greetd" - "regreet" + "vimgreet" "cage" # Hyprland stack "hyprland" diff --git a/dot_files/foot/foot-kiosk.ini b/dot_files/foot/foot-kiosk.ini new file mode 100644 index 0000000..691fffd --- /dev/null +++ b/dot_files/foot/foot-kiosk.ini @@ -0,0 +1,74 @@ +# Foot Kiosk Mode Configuration +# Used for launching TUI applications as standalone "apps" (e.g., vimgreet) +# - Minimal chrome +# - Tokyo Night Moon color scheme + +[main] +font=JetBrains Mono:size=13 +dpi-aware=yes +pad=0x0 + +[scrollback] +lines=0 + +[cursor] +color=222436 c8d3f5 +blink=no + +[colors] +background=222436 +foreground=c8d3f5 +selection-foreground=c8d3f5 +selection-background=2f334d + +# Black +regular0=1e2030 +bright0=444a73 + +# Red +regular1=ff757f +bright1=ff757f + +# Green +regular2=c3e88d +bright2=c3e88d + +# Yellow +regular3=ffc777 +bright3=ffc777 + +# Blue +regular4=82aaff +bright4=65bcff + +# Magenta/Purple +regular5=c099ff +bright5=fca7ea + +# Cyan +regular6=86e1fc +bright6=86e1fc + +# White +regular7=c8d3f5 +bright7=c8d3f5 + +[key-bindings] +# Disable all keybindings for kiosk mode +scrollback-up-page=none +scrollback-down-page=none +scrollback-up-half-page=none +scrollback-down-half-page=none +scrollback-up-line=none +scrollback-down-line=none +clipboard-copy=none +clipboard-paste=none +primary-paste=none +search-start=none +font-increase=none +font-decrease=none +font-reset=none +spawn-terminal=none +show-urls-launch=none +show-urls-copy=none +noop=none diff --git a/dot_files/hypr/hyprland-kiosk.conf b/dot_files/hypr/hyprland-kiosk.conf new file mode 100644 index 0000000..34246c2 --- /dev/null +++ b/dot_files/hypr/hyprland-kiosk.conf @@ -0,0 +1,107 @@ +# Hyprland Kiosk Mode Configuration +# Used for greetd login (vimgreet) and first-boot wizard (quickshell) +# - No keybindings (locked down) +# - Single fullscreen application +# - Exits when the application closes +# +# Usage: Set KIOSK_CMD environment variable before launching +# KIOSK_CMD="ghostty --config-file=... --command=vimgreet" 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/system_files/shared/etc/greetd/config.toml b/system_files/shared/etc/greetd/config.toml index 0996713..b42a5e7 100644 --- a/system_files/shared/etc/greetd/config.toml +++ b/system_files/shared/etc/greetd/config.toml @@ -3,13 +3,12 @@ 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" +# Run vimgreet in cage with foot (lightweight terminal for colors/cursor support) +command = "cage -s -- foot --config=/usr/share/hypercube/config/foot/foot-kiosk.ini vimgreet" user = "greeter" [initial_session] -# First boot wizard - runs once as root, then exits to show regreet +# First boot wizard - runs once as root, then exits to show vimgreet # 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 = "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; cage -s -- quickshell -p /usr/share/hypercube/config/quickshell/welcome-mode.qml'" user = "root" diff --git a/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf b/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf new file mode 100644 index 0000000..9c63b3d --- /dev/null +++ b/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf @@ -0,0 +1,4 @@ +[Service] +# Ensure pty devices are accessible for terminal emulators like ghostty +# PrivateDevices must be disabled to access the real /dev/pts +PrivateDevices=no From 49afa95d59fe1739150e5e4bfe97bb627d1c46db Mon Sep 17 00:00:00 2001 From: Charles Christolini Date: Sat, 17 Jan 2026 12:12:33 -0800 Subject: [PATCH 2/6] WIP vimgreet directly --- build_files/base/01-base-system.sh | 6 +- dot_files/foot/foot-kiosk.ini | 74 ------------------- system_files/shared/etc/greetd/config.toml | 4 +- .../systemd/system/greetd.service.d/pty.conf | 4 - .../lib/bootc/kargs.d/hypercube-kargs.json | 5 +- 5 files changed, 8 insertions(+), 85 deletions(-) delete mode 100644 dot_files/foot/foot-kiosk.ini delete mode 100644 system_files/shared/etc/systemd/system/greetd.service.d/pty.conf diff --git a/build_files/base/01-base-system.sh b/build_files/base/01-base-system.sh index 110e030..462553a 100755 --- a/build_files/base/01-base-system.sh +++ b/build_files/base/01-base-system.sh @@ -13,14 +13,12 @@ dnf5 -y clean all dnf5 -y copr enable binarypie/hypercube ### Display Manager: greetd + vimgreet -# vimgreet from binarypie/hypercube COPR -# cage is a minimal Wayland compositor to host vimgreet and the first-boot wizard -# foot is a lightweight terminal for running vimgreet with proper colors/cursor +# vimgreet from binarypie/hypercube COPR (runs directly on TTY) +# cage is a minimal Wayland compositor for the first-boot wizard dnf5 -y install \ greetd \ greetd-selinux \ cage \ - foot \ vimgreet ### Desktop Portals & Integration diff --git a/dot_files/foot/foot-kiosk.ini b/dot_files/foot/foot-kiosk.ini deleted file mode 100644 index 691fffd..0000000 --- a/dot_files/foot/foot-kiosk.ini +++ /dev/null @@ -1,74 +0,0 @@ -# Foot Kiosk Mode Configuration -# Used for launching TUI applications as standalone "apps" (e.g., vimgreet) -# - Minimal chrome -# - Tokyo Night Moon color scheme - -[main] -font=JetBrains Mono:size=13 -dpi-aware=yes -pad=0x0 - -[scrollback] -lines=0 - -[cursor] -color=222436 c8d3f5 -blink=no - -[colors] -background=222436 -foreground=c8d3f5 -selection-foreground=c8d3f5 -selection-background=2f334d - -# Black -regular0=1e2030 -bright0=444a73 - -# Red -regular1=ff757f -bright1=ff757f - -# Green -regular2=c3e88d -bright2=c3e88d - -# Yellow -regular3=ffc777 -bright3=ffc777 - -# Blue -regular4=82aaff -bright4=65bcff - -# Magenta/Purple -regular5=c099ff -bright5=fca7ea - -# Cyan -regular6=86e1fc -bright6=86e1fc - -# White -regular7=c8d3f5 -bright7=c8d3f5 - -[key-bindings] -# Disable all keybindings for kiosk mode -scrollback-up-page=none -scrollback-down-page=none -scrollback-up-half-page=none -scrollback-down-half-page=none -scrollback-up-line=none -scrollback-down-line=none -clipboard-copy=none -clipboard-paste=none -primary-paste=none -search-start=none -font-increase=none -font-decrease=none -font-reset=none -spawn-terminal=none -show-urls-launch=none -show-urls-copy=none -noop=none diff --git a/system_files/shared/etc/greetd/config.toml b/system_files/shared/etc/greetd/config.toml index b42a5e7..af4c05c 100644 --- a/system_files/shared/etc/greetd/config.toml +++ b/system_files/shared/etc/greetd/config.toml @@ -3,8 +3,8 @@ vt = 1 [default_session] -# Run vimgreet in cage with foot (lightweight terminal for colors/cursor support) -command = "cage -s -- foot --config=/usr/share/hypercube/config/foot/foot-kiosk.ini vimgreet" +# Run vimgreet directly on the TTY (no Wayland compositor needed) +command = "vimgreet" user = "greeter" [initial_session] diff --git a/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf b/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf deleted file mode 100644 index 9c63b3d..0000000 --- a/system_files/shared/etc/systemd/system/greetd.service.d/pty.conf +++ /dev/null @@ -1,4 +0,0 @@ -[Service] -# Ensure pty devices are accessible for terminal emulators like ghostty -# PrivateDevices must be disabled to access the real /dev/pts -PrivateDevices=no 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" ] } From 5681fb455b7fc7ae9e9a249631088b4ac4453e30 Mon Sep 17 00:00:00 2001 From: Charles Christolini Date: Fri, 23 Jan 2026 12:20:56 -0800 Subject: [PATCH 3/6] Ai dev (#54) --- .github/workflows/build-ai-dev.yml | 88 ++++++++++++++ .github/workflows/build-nvim-dev.yml | 6 +- .github/workflows/check-package-versions.yml | 9 +- dot_files/ai-dev/Containerfile | 37 ++++++ dot_files/ai-dev/Justfile | 113 ++++++++++++++++++ dot_files/ai-dev/ai-entrypoint.sh | 60 ++++++++++ dot_files/ai-dev/scripts/claude.sh | 18 +++ dot_files/ai-dev/scripts/enter.sh | 25 ++++ dot_files/ai-dev/scripts/gemini.sh | 22 ++++ .../shared/etc/distrobox/distrobox.ini | 2 +- .../shared/usr/share/ublue-os/just/62-ai.just | 37 ++++++ .../shared/usr/share/ublue-os/justfile | 1 + 12 files changed, 410 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/build-ai-dev.yml create mode 100644 dot_files/ai-dev/Containerfile create mode 100644 dot_files/ai-dev/Justfile create mode 100644 dot_files/ai-dev/ai-entrypoint.sh create mode 100755 dot_files/ai-dev/scripts/claude.sh create mode 100755 dot_files/ai-dev/scripts/enter.sh create mode 100755 dot_files/ai-dev/scripts/gemini.sh create mode 100644 system_files/shared/usr/share/ublue-os/just/62-ai.just diff --git a/.github/workflows/build-ai-dev.yml b/.github/workflows/build-ai-dev.yml new file mode 100644 index 0000000..9256c6b --- /dev/null +++ b/.github/workflows/build-ai-dev.yml @@ -0,0 +1,88 @@ +# Build and publish ai-dev container image +# Triggered on changes to dot_files/ai-dev/ or manual dispatch +# Image published to: ghcr.io/binarypie-dev/ai-dev:latest + +name: Build ai-dev Image + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: + - main + paths: + - 'dot_files/ai-dev/**' + - '.github/workflows/build-ai-dev.yml' + pull_request: + paths: + - 'dot_files/ai-dev/**' + - '.github/workflows/build-ai-dev.yml' + workflow_dispatch: + schedule: + # Rebuild daily to get updated packages + - cron: '0 6 * * *' + +env: + IMAGE_NAME: ai-dev + IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} + +jobs: + build: + runs-on: ubuntu-latest-m + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + type=sha,prefix= + type=ref,event=pr + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: dot_files/ai-dev + file: dot_files/ai-dev/Containerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Generate build summary + if: github.event_name != 'pull_request' + run: | + echo "## ai-dev Image Built" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Image:** \`${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Usage" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "# Run Claude Code" >> $GITHUB_STEP_SUMMARY + echo "podman run --rm -it --user root --security-opt label=disable \\" >> $GITHUB_STEP_SUMMARY + echo " -e HOST_UID=\$(id -u) -e HOST_GID=\$(id -g) -e HOME=\$HOME \\" >> $GITHUB_STEP_SUMMARY + echo " -v \"\$(pwd):\$(pwd):rw\" -w \"\$(pwd)\" \\" >> $GITHUB_STEP_SUMMARY + echo " ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}:latest claude" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/build-nvim-dev.yml b/.github/workflows/build-nvim-dev.yml index 6183b09..a14ef4f 100644 --- a/.github/workflows/build-nvim-dev.yml +++ b/.github/workflows/build-nvim-dev.yml @@ -1,6 +1,6 @@ # Build and publish nvim-dev container image # Triggered on changes to dot_files/nvim/ or manual dispatch -# Image published to: ghcr.io/binarypie/nvim-dev:latest +# Image published to: ghcr.io/binarypie-dev/nvim-dev:latest name: Build nvim-dev Image @@ -21,8 +21,8 @@ on: - '.github/workflows/build-nvim-dev.yml' workflow_dispatch: schedule: - # Rebuild weekly to get updated packages - - cron: '0 6 * * 0' + # Rebuild daily to get updated packages + - cron: '0 6 * * *' env: IMAGE_NAME: nvim-dev diff --git a/.github/workflows/check-package-versions.yml b/.github/workflows/check-package-versions.yml index 8ef3aa2..5ef449b 100644 --- a/.github/workflows/check-package-versions.yml +++ b/.github/workflows/check-package-versions.yml @@ -253,13 +253,14 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - BRANCH="${{ steps.info.outputs.branch }}" + TITLE="${{ steps.info.outputs.title }}" - # Check for PR with this branch - EXISTING=$(gh pr list --head "$BRANCH" --json number --jq '.[0].number' 2>/dev/null) || EXISTING="" + # Check for open PR with the same title + EXISTING=$(gh pr list --state open --search "in:title $TITLE" --json number,title \ + --jq ".[] | select(.title == \"$TITLE\") | .number" 2>/dev/null | head -1) || EXISTING="" if [[ -n "$EXISTING" ]]; then - echo "PR #$EXISTING already exists for this update group" + echo "PR #$EXISTING already exists with title: $TITLE" echo "exists=true" >> $GITHUB_OUTPUT else echo "exists=false" >> $GITHUB_OUTPUT diff --git a/dot_files/ai-dev/Containerfile b/dot_files/ai-dev/Containerfile new file mode 100644 index 0000000..689789d --- /dev/null +++ b/dot_files/ai-dev/Containerfile @@ -0,0 +1,37 @@ +# AI Development Environment (Sandboxed Podman Container) +# Pre-built image with Claude Code and Gemini CLI +# +# Build: podman build -t ai-dev . +# Run: podman run --rm -it --user 0:0 --security-opt label=disable \ +# -e HOME=$HOME -v "$(pwd):$(pwd):rw" -w "$(pwd)" localhost/ai-dev + +FROM ghcr.io/binarypie-dev/nvim-dev:latest + +# ============================================================================= +# LAYER 1: Claude Code (native install) +# ============================================================================= +USER linuxbrew +WORKDIR /home/linuxbrew + +RUN curl -fsSL https://claude.ai/install.sh | bash + +# ============================================================================= +# LAYER 2: Gemini CLI via npm +# ============================================================================= +RUN npm install -g @google/gemini-cli + +# ============================================================================= +# LAYER 3: Entrypoint script (sets PATH, execs command) +# ============================================================================= +USER root +COPY ai-entrypoint.sh /usr/local/bin/ai-entrypoint.sh +RUN chmod +x /usr/local/bin/ai-entrypoint.sh + +# In rootless podman, --user 0:0 maps to host UID (no privilege escalation) +ENTRYPOINT ["/usr/local/bin/ai-entrypoint.sh"] +CMD ["bash"] + +# Labels for GitHub Container Registry +LABEL org.opencontainers.image.source="https://github.com/binarypie/hypercube" +LABEL org.opencontainers.image.description="AI development environment with Claude Code and Gemini CLI" +LABEL org.opencontainers.image.licenses="MIT" diff --git a/dot_files/ai-dev/Justfile b/dot_files/ai-dev/Justfile new file mode 100644 index 0000000..bb0193a --- /dev/null +++ b/dot_files/ai-dev/Justfile @@ -0,0 +1,113 @@ +# AI Development Environment (Sandboxed Podman Container) +# Uses scripts/ for shared logic between local dev and system install + +local_image := "localhost/ai-dev" +remote_image := "ghcr.io/binarypie-dev/ai-dev:latest" + +# Default recipe - show help +default: + @just --list + +# ============================================================================= +# Image Building +# ============================================================================= + +# Build the container image locally +build: + @echo "Building ai-dev image locally..." + podman build -t {{local_image}} . + @echo "Done! Image: {{local_image}}" + +# Build without cache +build-no-cache: + @echo "Building ai-dev image (no cache)..." + podman build --no-cache -t {{local_image}} . + @echo "Done! Image: {{local_image}}" + +# Pull the remote image +pull: + podman pull {{remote_image}} + +# Push to GHCR (requires: podman login ghcr.io) +push: + podman tag {{local_image}} {{remote_image}} + podman push {{remote_image}} + +# ============================================================================= +# Usage (local image) +# ============================================================================= + +# Run Claude Code in the current directory +claude *args: + AI_DEV_IMAGE={{local_image}} ./scripts/claude.sh {{args}} + +# Run Gemini CLI in the current directory +gemini *args: + AI_DEV_IMAGE={{local_image}} ./scripts/gemini.sh {{args}} + +# Enter the container interactively +enter: + AI_DEV_IMAGE={{local_image}} ./scripts/enter.sh + +# ============================================================================= +# Installation (wrapper scripts to ~/.local/bin) +# ============================================================================= + +# Install wrapper scripts using remote image +install: + #!/usr/bin/bash + set -euo pipefail + mkdir -p "$HOME/.local/bin" + cp scripts/claude.sh "$HOME/.local/bin/claude" + cp scripts/gemini.sh "$HOME/.local/bin/gemini" + chmod +x "$HOME/.local/bin/claude" "$HOME/.local/bin/gemini" + echo "Installed ~/.local/bin/claude and ~/.local/bin/gemini (image: {{remote_image}})" + +# Install wrapper scripts using local image +install-local: + #!/usr/bin/bash + set -euo pipefail + mkdir -p "$HOME/.local/bin" + sed 's|ghcr.io/binarypie-dev/ai-dev:latest|localhost/ai-dev|' scripts/claude.sh > "$HOME/.local/bin/claude" + sed 's|ghcr.io/binarypie-dev/ai-dev:latest|localhost/ai-dev|' scripts/gemini.sh > "$HOME/.local/bin/gemini" + chmod +x "$HOME/.local/bin/claude" "$HOME/.local/bin/gemini" + echo "Installed ~/.local/bin/claude and ~/.local/bin/gemini (image: {{local_image}})" + +# Remove wrapper scripts from ~/.local/bin +uninstall: + #!/usr/bin/bash + set -euo pipefail + rm -f "$HOME/.local/bin/claude" + rm -f "$HOME/.local/bin/gemini" + echo "Removed wrapper scripts from ~/.local/bin" + +# ============================================================================= +# Setup & Cleanup +# ============================================================================= + +# Full setup: pull image + install wrappers +setup: pull install + @echo "" + @echo "Setup complete! Run 'claude' or 'gemini' to use the AI assistants." + +# Remove local image +clean: + #!/usr/bin/bash + set -euo pipefail + podman rmi {{local_image}} 2>/dev/null && echo "Removed {{local_image}}" || echo "No local image to remove" + +# ============================================================================= +# Testing +# ============================================================================= + +# Test the built image works correctly +test-build: build + @echo "Testing container..." + AI_DEV_IMAGE={{local_image}} ./scripts/claude.sh --version + AI_DEV_IMAGE={{local_image}} ./scripts/gemini.sh --version + @echo "" + @echo "All tests passed!" + +# Debug: show container environment, paths, and auth state +debug: + AI_DEV_IMAGE={{local_image}} ./scripts/enter.sh --ai-dev-debug diff --git a/dot_files/ai-dev/ai-entrypoint.sh b/dot_files/ai-dev/ai-entrypoint.sh new file mode 100644 index 0000000..06ca8fa --- /dev/null +++ b/dot_files/ai-dev/ai-entrypoint.sh @@ -0,0 +1,60 @@ +#!/usr/bin/bash +set -e + +# Fix root's home directory in /etc/passwd to match host $HOME +# This ensures os.homedir() in Node.js (used by claude/gemini) returns the correct path +if [ -n "$HOME" ] && [ "$HOME" != "/root" ]; then + sed -i "s|root:x:0:0:[^:]*:/root:|root:x:0:0:root:$HOME:|" /etc/passwd 2>/dev/null || true +fi + +# Symlink claude install paths to where the native installer expects them at runtime +# (installed under /home/linuxbrew at build time, but $HOME differs at runtime) +if [ -n "$HOME" ] && [ "$HOME" != "/home/linuxbrew" ]; then + mkdir -p "$HOME/.local/bin" "$HOME/.local/share" + ln -sf /home/linuxbrew/.local/bin/claude "$HOME/.local/bin/claude" 2>/dev/null || true + ln -sf /home/linuxbrew/.local/share/claude "$HOME/.local/share/claude" 2>/dev/null || true +fi + +# Ensure claude auth files are readable within the container +chmod -R a+rX "$HOME/.claude" 2>/dev/null || true +chmod a+rw "$HOME/.claude.json" 2>/dev/null || true + +export PATH="$HOME/.local/bin:/home/linuxbrew/.local/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/home/linuxbrew/.npm-global/bin:/home/linuxbrew/go/bin:/home/linuxbrew/.cargo/bin:$PATH" + +# Debug mode: print environment and auth state +if [ "$1" = "--ai-dev-debug" ]; then + echo "=== ai-dev debug ===" + echo "uid=$(id -u) gid=$(id -g) user=$(whoami 2>/dev/null || echo unknown)" + echo "HOME=$HOME" + echo "PATH=$PATH" + echo "" + echo "=== /etc/passwd root entry ===" + grep "^root:" /etc/passwd + echo "" + echo "=== TTY ===" + ls -la /dev/pts/ 2>/dev/null || echo "no /dev/pts" + echo "tty: $(tty 2>/dev/null || echo 'not a tty')" + echo "" + echo "=== env (GOOGLE_/GEMINI_/ANTHROPIC_) ===" + env | grep -E "^(GOOGLE_|GEMINI_|ANTHROPIC_)" || echo "(none set)" + echo "" + echo "=== $HOME/.gemini/ ===" + ls -la "$HOME/.gemini/" 2>/dev/null || echo "not found at $HOME/.gemini/" + echo "" + echo "=== $HOME/.claude/ ===" + ls -la "$HOME/.claude/" 2>/dev/null || echo "not found at $HOME/.claude/" + echo "" + echo "=== which claude/gemini ===" + which claude 2>/dev/null || echo "claude: not found" + which gemini 2>/dev/null || echo "gemini: not found" + echo "" + echo "=== node os.homedir() ===" + node -e "console.log(require(\"os\").homedir())" 2>/dev/null || echo "node not available" + exit 0 +fi + +if [ $# -eq 0 ]; then + exec bash +else + exec "$@" +fi diff --git a/dot_files/ai-dev/scripts/claude.sh b/dot_files/ai-dev/scripts/claude.sh new file mode 100755 index 0000000..55fcfad --- /dev/null +++ b/dot_files/ai-dev/scripts/claude.sh @@ -0,0 +1,18 @@ +#!/usr/bin/bash +set -euo pipefail + +IMAGE="${AI_DEV_IMAGE:-ghcr.io/binarypie-dev/ai-dev:latest}" + +mkdir -p "$HOME/.claude" +touch "$HOME/.claude.json" + +exec podman run --rm -it --init \ + --user 0:0 \ + --security-opt label=disable \ + -e HOME="$HOME" \ + -v "$(pwd):$(pwd):rw" \ + -v "$HOME/.claude:$HOME/.claude:rw" \ + -v "$HOME/.claude.json:$HOME/.claude.json:rw" \ + -w "$(pwd)" \ + "$IMAGE" \ + claude "$@" diff --git a/dot_files/ai-dev/scripts/enter.sh b/dot_files/ai-dev/scripts/enter.sh new file mode 100755 index 0000000..ebbbcdc --- /dev/null +++ b/dot_files/ai-dev/scripts/enter.sh @@ -0,0 +1,25 @@ +#!/usr/bin/bash +set -euo pipefail + +IMAGE="${AI_DEV_IMAGE:-ghcr.io/binarypie-dev/ai-dev:latest}" + +mkdir -p "$HOME/.claude" "$HOME/.gemini" +touch "$HOME/.claude.json" + +env_flags="" +for var in $(env | grep -E '^(GOOGLE_|GEMINI_|ANTHROPIC_)' | cut -d= -f1); do + env_flags="$env_flags -e $var" +done + +exec podman run --rm -it --init \ + --user 0:0 \ + --security-opt label=disable \ + -e HOME="$HOME" \ + $env_flags \ + -v "$(pwd):$(pwd):rw" \ + -v "$HOME/.claude:$HOME/.claude:rw" \ + -v "$HOME/.claude.json:$HOME/.claude.json:rw" \ + -v "$HOME/.gemini:$HOME/.gemini:rw" \ + -w "$(pwd)" \ + "$IMAGE" \ + "$@" diff --git a/dot_files/ai-dev/scripts/gemini.sh b/dot_files/ai-dev/scripts/gemini.sh new file mode 100755 index 0000000..4e7d36c --- /dev/null +++ b/dot_files/ai-dev/scripts/gemini.sh @@ -0,0 +1,22 @@ +#!/usr/bin/bash +set -euo pipefail + +IMAGE="${AI_DEV_IMAGE:-ghcr.io/binarypie-dev/ai-dev:latest}" + +mkdir -p "$HOME/.gemini" + +env_flags="" +for var in $(env | grep -E '^(GOOGLE_|GEMINI_)' | cut -d= -f1); do + env_flags="$env_flags -e $var" +done + +exec podman run --rm -it --init \ + --user 0:0 \ + --security-opt label=disable \ + -e HOME="$HOME" \ + $env_flags \ + -v "$(pwd):$(pwd):rw" \ + -v "$HOME/.gemini:$HOME/.gemini:rw" \ + -w "$(pwd)" \ + "$IMAGE" \ + gemini "$@" diff --git a/system_files/shared/etc/distrobox/distrobox.ini b/system_files/shared/etc/distrobox/distrobox.ini index 4741157..4ae2836 100644 --- a/system_files/shared/etc/distrobox/distrobox.ini +++ b/system_files/shared/etc/distrobox/distrobox.ini @@ -2,7 +2,7 @@ # Pre-configured containers for development [nvim-dev] -image=ghcr.io/binarypie/nvim-dev:latest +image=ghcr.io/binarypie-dev/nvim-dev:latest pull=true init=false start_now=false diff --git a/system_files/shared/usr/share/ublue-os/just/62-ai.just b/system_files/shared/usr/share/ublue-os/just/62-ai.just new file mode 100644 index 0000000..0d667a0 --- /dev/null +++ b/system_files/shared/usr/share/ublue-os/just/62-ai.just @@ -0,0 +1,37 @@ +# vim: set ft=make : +# Hypercube AI development environment commands + +ai_scripts := "/usr/share/hypercube/config/ai-dev/scripts" + +# Run Claude Code in the current directory +ai-claude *args: + {{ai_scripts}}/claude.sh {{args}} + +# Run Gemini CLI in the current directory +ai-gemini *args: + {{ai_scripts}}/gemini.sh {{args}} + +# Enter the ai-dev container interactively +ai-dev: + {{ai_scripts}}/enter.sh + +# Install claude and gemini wrapper scripts to ~/.local/bin +ai-setup: + #!/usr/bin/bash + set -euo pipefail + echo "Pulling ai-dev image..." + podman pull ghcr.io/binarypie-dev/ai-dev:latest + mkdir -p "$HOME/.local/bin" + cp {{ai_scripts}}/claude.sh "$HOME/.local/bin/claude" + cp {{ai_scripts}}/gemini.sh "$HOME/.local/bin/gemini" + chmod +x "$HOME/.local/bin/claude" "$HOME/.local/bin/gemini" + echo "" + echo "Setup complete! You can now run 'claude' and 'gemini' directly." + +# Upgrade ai-dev to latest image +ai-upgrade: + #!/usr/bin/bash + set -euo pipefail + echo "Pulling latest ai-dev image..." + podman pull ghcr.io/binarypie-dev/ai-dev:latest + echo "Done! Latest image pulled." diff --git a/system_files/shared/usr/share/ublue-os/justfile b/system_files/shared/usr/share/ublue-os/justfile index c3797a6..52cd2d1 100644 --- a/system_files/shared/usr/share/ublue-os/justfile +++ b/system_files/shared/usr/share/ublue-os/justfile @@ -26,3 +26,4 @@ import "/usr/share/ublue-os/just/50-akmods.just" # ============================================================================= import? "/usr/share/ublue-os/just/60-hypercube.just" import? "/usr/share/ublue-os/just/61-nvim.just" +import? "/usr/share/ublue-os/just/62-ai.just" From 4c9c5e34aa7a6bdfe97b0f61b3ffbd5c1a3974a4 Mon Sep 17 00:00:00 2001 From: binarypie Date: Sat, 24 Jan 2026 08:13:03 -0800 Subject: [PATCH 4/6] WIP integrate hyperland-utils (previously vimgreet) --- .gitignore | 1 + AGENTS.md | 99 +++ README.md | 3 +- build_files/apps/01-apps.sh | 14 + build_files/base/01-base-system.sh | 12 +- build_files/hypercube/99-tests.sh | 3 +- dot_files/hypercube-onboard/onboard.toml | 56 ++ dot_files/hypr/hyprland-kiosk.conf | 4 +- .../quickshell/modules/bar/StatusBar.qml | 19 +- .../quickshell/modules/common/Config.qml | 14 - .../modules/sidebars/UpdateView.qml | 207 +----- .../quickshell/modules/welcome/Welcome.qml | 632 ------------------ dot_files/quickshell/modules/welcome/qmldir | 3 - dot_files/quickshell/services/Updates.qml | 135 +--- dot_files/quickshell/welcome-mode.qml | 406 ----------- dot_files/regreet/regreet.css | 7 - dot_files/regreet/regreet.toml | 23 - iso_files/hook-post-rootfs.sh | 7 +- packages/regreet/regreet.spec | 58 -- scripts/packages/config.sh | 4 +- system_files/shared/etc/greetd/config.toml | 8 +- .../systemd/user-preset/50-hypercube.preset | 1 - .../systemd/user/flatpak-preinstall.service | 14 - .../shared/usr/libexec/flatpak-preinstall | 48 -- .../flatpak/preinstall.d/bazaar.preinstall | 3 - .../flatpak/preinstall.d/bottles.preinstall | 3 - .../flatpak/preinstall.d/boxbuddy.preinstall | 3 - .../flatpak/preinstall.d/boxes.preinstall | 3 - .../flatpak/preinstall.d/flatseal.preinstall | 3 - .../flatpak/preinstall.d/gradia.preinstall | 3 - .../flatpak/preinstall.d/kooha.preinstall | 3 - .../preinstall.d/podman-desktop.preinstall | 3 - .../preinstall.d/thunderbird.preinstall | 3 - .../flatpak/preinstall.d/warehouse.preinstall | 3 - 34 files changed, 193 insertions(+), 1615 deletions(-) create mode 100644 AGENTS.md create mode 100644 dot_files/hypercube-onboard/onboard.toml delete mode 100644 dot_files/quickshell/modules/welcome/Welcome.qml delete mode 100644 dot_files/quickshell/modules/welcome/qmldir delete mode 100644 dot_files/quickshell/welcome-mode.qml delete mode 100644 dot_files/regreet/regreet.css delete mode 100644 dot_files/regreet/regreet.toml delete mode 100644 packages/regreet/regreet.spec delete mode 100644 system_files/shared/usr/lib/systemd/user/flatpak-preinstall.service delete mode 100755 system_files/shared/usr/libexec/flatpak-preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/bazaar.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/bottles.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/boxbuddy.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/boxes.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/flatseal.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/gradia.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/kooha.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/podman-desktop.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/thunderbird.preinstall delete mode 100644 system_files/shared/usr/share/flatpak/preinstall.d/warehouse.preinstall 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 462553a..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 + vimgreet), 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 + vimgreet -# vimgreet from binarypie/hypercube COPR (runs directly on TTY) -# cage is a minimal Wayland compositor for 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 \ - vimgreet + 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/99-tests.sh b/build_files/hypercube/99-tests.sh index c92d60e..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" - "vimgreet" - "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..2077856 --- /dev/null +++ b/dot_files/hypercube-onboard/onboard.toml @@ -0,0 +1,56 @@ +# 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 = "dnf5 -y upgrade" + +[[updates.commands]] +description = "Flatpak update" +command = "flatpak update --system --noninteractive" + +[flatpaks] +# Optional user flatpak packages (user can select during onboard) + +[[flatpaks.optional]] +name = "Steam" +id = "com.valvesoftware.Steam" + +[[flatpaks.optional]] +name = "Discord" +id = "com.discordapp.Discord" + +[[flatpaks.optional]] +name = "Spotify" +id = "com.spotify.Client" + +[[flatpaks.optional]] +name = "VS Code" +id = "com.visualstudio.code" + +[[flatpaks.optional]] +name = "OBS Studio" +id = "com.obsproject.Studio" diff --git a/dot_files/hypr/hyprland-kiosk.conf b/dot_files/hypr/hyprland-kiosk.conf index 34246c2..1786b12 100644 --- a/dot_files/hypr/hyprland-kiosk.conf +++ b/dot_files/hypr/hyprland-kiosk.conf @@ -1,11 +1,11 @@ # Hyprland Kiosk Mode Configuration -# Used for greetd login (vimgreet) and first-boot wizard (quickshell) +# 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="ghostty --config-file=... --command=vimgreet" Hyprland -c /path/to/hyprland-kiosk.conf +# KIOSK_CMD="your-app" Hyprland -c /path/to/hyprland-kiosk.conf ################ ### MONITORS ### 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 af4c05c..4d964d7 100644 --- a/system_files/shared/etc/greetd/config.toml +++ b/system_files/shared/etc/greetd/config.toml @@ -1,14 +1,10 @@ [terminal] -# The VT to run the greeter on vt = 1 [default_session] -# Run vimgreet directly on the TTY (no Wayland compositor needed) -command = "vimgreet" +command = "hypercube-greeter" user = "greeter" [initial_session] -# First boot wizard - runs once as root, then exits to show vimgreet -# Check if regular users exist (UID >= 1000) - if so, skip wizard and remove this block -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; cage -s -- 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/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 From dae6db30f6997159c0798b54698466dd3481860e Mon Sep 17 00:00:00 2001 From: binarypie Date: Sun, 25 Jan 2026 17:22:35 -0800 Subject: [PATCH 5/6] Simplify system update --- dot_files/hypercube-onboard/onboard.toml | 29 +----------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/dot_files/hypercube-onboard/onboard.toml b/dot_files/hypercube-onboard/onboard.toml index 2077856..3fada6d 100644 --- a/dot_files/hypercube-onboard/onboard.toml +++ b/dot_files/hypercube-onboard/onboard.toml @@ -26,31 +26,4 @@ remove_initial_session = true [[updates.commands]] description = "System update" -command = "dnf5 -y upgrade" - -[[updates.commands]] -description = "Flatpak update" -command = "flatpak update --system --noninteractive" - -[flatpaks] -# Optional user flatpak packages (user can select during onboard) - -[[flatpaks.optional]] -name = "Steam" -id = "com.valvesoftware.Steam" - -[[flatpaks.optional]] -name = "Discord" -id = "com.discordapp.Discord" - -[[flatpaks.optional]] -name = "Spotify" -id = "com.spotify.Client" - -[[flatpaks.optional]] -name = "VS Code" -id = "com.visualstudio.code" - -[[flatpaks.optional]] -name = "OBS Studio" -id = "com.obsproject.Studio" +command = "ujust update" From 33709ce3dc1c95379ee718ef87bb4e67e1c7a4c4 Mon Sep 17 00:00:00 2001 From: Charles Christolini Date: Fri, 23 Jan 2026 16:04:44 -0800 Subject: [PATCH 6/6] Add just command runner to Neovim container (#55) ## Summary Added the `just` command runner to the Homebrew packages installed in the Neovim container. ## Changes - Added `just` to the list of Homebrew packages in the container build configuration - `just` is a handy way to save and run project-specific commands, similar to `make` but with a more modern syntax ## Details The `just` tool is being added alongside other utility tools like `eza`, `bat`, `yq`, and `jq` in the container's Layer 3 (Homebrew utilities). This provides developers with access to command runners within the containerized Neovim environment. Co-authored-by: Claude --- dot_files/nvim/Containerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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