commit 714b9fc890f66cd5f033e0a2e2683f3022fba63c Author: Jon Wingender Date: Tue Jul 21 22:37:57 2026 -0500 Initial commit: NexusOS - local AI assistant platform diff --git a/.github/agents/ponytail-caveman.agent.md b/.github/agents/ponytail-caveman.agent.md new file mode 100644 index 0000000..8b36006 --- /dev/null +++ b/.github/agents/ponytail-caveman.agent.md @@ -0,0 +1,43 @@ +--- +name: Ponyman +description: "Minimalist coding agent with a caveman-speaking toggle. Use for pragmatic bug fixes, small implementations, reviews, and cleanup where the shortest correct solution matters. Say 'caveman mode' for compressed speech or 'normal mode' for standard speech." +tools: [read, search, edit, execute, todo] +user-invocable: true +--- +You are Ponytail Caveman, a pragmatic senior coding agent. + +Your engineering rule is ponytail minimalism: understand the real control path, reuse existing code, prefer the standard library and native platform features, and make the smallest correct change. Fix root causes. Do not add speculative abstractions, dependencies, boilerplate, or unrelated refactors. Never simplify away security, validation, error handling, accessibility, or tests needed to protect changed behavior. After this statement, the rest of the readme will be in caveman talk to provide a reference for how it should sound. + +Caveman talk dumb. Grunt words. "Me", "you", "big", "broke", "good". Short. Sound like cave person poke rock with stick. BUT point always land — reader still know what happen and what do next. Dumb sound, smart meaning. Keep code, file name, command, error word exact — no dumb those. + +## How Me Talk +- No word say: me talk normal. Clear. +- You say `caveman mode`, `talk caveman`, or `/caveman`: me go dumb caveman. Still say enough, point land. +- You say `normal mode`, `talk normally`, or `/caveman normal`: me talk normal again. +- Me keep same talk till you change it. +- Talk change word only. Me brain and safe stay smart. + +## Me Do Work Like This +1. Find thing. File, symbol, broke part, command, or test. +2. Look small part near. Make one guess me can prove wrong. Pick one cheap check. +3. Fix right code path. Smallest patch. No more. +4. Run small check. Now, not later. +5. Add or fix test for tricky part. Security, save-data, parse, error path — these most. +6. Run big check when change touch many module. +7. Other dirty change — no touch. Never reset, revert, commit, or make branch unless you ask. + +## Me Pick Tool +- Read and look before me edit. +- Use pattern and command repo already got. +- Use real parser/API for structured data. +- Use `apply_patch` for hand edit. +- Like focused test, lint, typecheck, or build more than diff-only check. +- Comment rare, only useful. No talk what code already say. + +## Me Say Back +Normal mode: say what change, what me check, what risk left. Few short line. + +Caveman mode: dumb short grunt, point still land. Like this: +`Me fix big bug. Add test. pytest: 8 pass. One warning still there — old deprecation, no scare.` + +Look-over work: bad thing first, worst on top, with file link and how me fix. No bad thing → say so, name test gap or risk left. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54c9c65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +Promethean/ +ollama/ +interface/web/node_modules/ +interface/web/dist/ +runtime/ +__pycache__/ +*.pyc +*.pyo +synapse/memory/memory.db +synapse/memory/memory.db-wal +synapse/memory/memory.db-shm +*.db-wal +*.db-shm +.DS_Store +.directory + +# Ollama model blobs (~15G, regenerable via `ollama pull`) +models/ + +# local editor / assistant tooling +.vscode/ +.claude/ +.claude-backup/ diff --git a/.promethean_bashrc b/.promethean_bashrc new file mode 100644 index 0000000..1e92ab5 --- /dev/null +++ b/.promethean_bashrc @@ -0,0 +1,8 @@ +# Promethean Terminal shell init +[[ -f ~/.bashrc ]] && source ~/.bashrc +export VIRTUAL_ENV_DISABLE_PROMPT=1 +if [[ -f "$HOME/nexus-core/Promethean/bin/activate" ]]; then + source "$HOME/nexus-core/Promethean/bin/activate" +fi +export PS1='\[\e[0;35m\](Promethean)\[\e[0m\] ${debian_chroot:+($debian_chroot)}\[\e[0;37m\]\u@\h\[\e[0m\]:\[\e[1;32m\]\w\[\e[0m\]\$ ' +printf '\e]0;Promethean Terminal\a' diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..54cafd3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,128 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is NexusOS + +NexusOS is a local AI assistant platform. It runs a Python/FastAPI backend (Synapse) that interfaces with a locally bundled Ollama instance, a dedicated memory microservice, and a React/Vite frontend. All AI inference runs through Ollama on `localhost`; no external AI provider is configured or called. + +## Running the Project + +NexusOS runs **single-process**: the Synapse backend on port 8000 serves the +built web UI (`interface/web/dist`) itself, so there is no separate Vite server +at runtime. Ollama is started manually (sidebar **Start AI** / `nexus-cli.sh +start --ai`), not on backend startup. + +**Windows (recommended):** +```powershell +powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 # one-time native install +.\launch_nexus.ps1 # memory :8001 + backend :8000 +``` + +**Linux full stack (dev):** +```bash +./launch_nexus.sh +``` +This activates the `Promethean` venv and starts the memory service on port 8001 and the Synapse backend on port 8000 (which also serves the built UI). It additionally starts a **Vite dev server** for frontend hot-reload — a Linux-dev convenience, unlike the single-process Windows/production path where the backend serves `dist/` alone. It does **not** start Ollama. + +**Individual services via CLI:** +```bash +# From nexus-core/ with Promethean venv active: +source Promethean/bin/activate + +# Backend (serves the built UI at :8000 too) +uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload + +# Memory service +uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload + +# Frontend DEV server (hot-reload) — only when editing the UI; production is the +# built dist/ served by the backend. Run `npm run build` to refresh dist/. +cd interface/web && npm run dev +``` + +**Management CLI** (`ncp`) — start/stop services with PID tracking, plus terminal +access to the same features as the web UI (all via the REST API on `:8000`): +```bash +./management/nexus-cli.sh start # starts backend + frontend +./management/nexus-cli.sh stop +./management/nexus-cli.sh start --backend|-b / --frontend|-f / --memory|-m + +# Feature commands (dispatch to management/nexus_api.py — httpx, no TUI): +ncp chat "" # stream a reply (POST /chat/stream) +ncp memory list|add |rm +ncp playbook list|show # first playbook (*) is the active system prompt +ncp history [query] # recent conversations +``` +The old curses TUIs (`nexus-chat.py`, `nexus-playbook.py`) were removed in favor of +these API-backed subcommands. The CLI covers chat, memory, playbooks, and history; +the web UI and control panel expose the remaining management features. +`management/controlpanel.py` (tkinter GUI, wired into the XFCE panel via +`bin/panel/nexus-popup.py`) stays. + +**Checks (the release gate):** +```bash +./bin/check.sh # pytest (tests/ + management/) + eslint + .ps1 parse check +``` +There is no hosted CI — the remote is self-hosted Gitea with no act_runner — so +this script *is* the gate. Run it before tagging a release. + +**Frontend lint only:** +```bash +cd interface/web && npm run lint +``` + +**Frontend build:** +```bash +cd interface/web && npm run build +``` + +## Architecture + +### Python venv +All Python code runs inside `Promethean/` (a local venv). Always activate it before running backend commands: `source Promethean/bin/activate`. Dependencies are layered: `requirements-base.txt` holds the GPU-agnostic core, and a thin overlay pins the right PyTorch build for the target — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-wsl.txt` (CPU-only). `bin/install.sh` selects NVIDIA, AMD, or CPU/WSL requirements from the host. + +### Synapse Backend (`synapse/`) +FastAPI app at `synapse/main.py`. Key responsibilities: +- `/chat/stream` — chat with Ollama; streaming uses SSE (the only chat endpoint — the non-stream `/chat` was removed). After each exchange the stream endpoint calls the Memory Service to auto-extract persistent facts. +- `/playbooks` — CRUD for playbooks stored as YAML files in `data/playbooks/` via `synapse/playbooks/store.py`. +- `/memory` — CRUD for persistent facts (proxies the same SQLite store as the memory service). +- `/models` — lists, pulls, and deletes Ollama models by proxying Ollama's HTTP API. +- `/settings` and `/ollama` — persist runtime settings and control Ollama lifecycle. +- `/conversations` — persists, retrieves, edits, deletes, and exports full chat history from SQLite. +- `/icons` — lists local application icons and applies NexusOS branding. + +**System prompt assembly** (in `main.py` `chat_stream_endpoint`): the final system prompt is built by layering the active playbook instructions → reference playbook context → persistent memory facts → relevant past conversation snippets retrieved by `store.search_conversations`. + +### Memory Service (`synapse/memory/`) +A separate FastAPI app on port 8001. `service.py` exposes `/memories/extract` which calls `extractor.py` — an Ollama prompt that decides whether to persist a new fact from a conversation exchange. The main Synapse backend calls this asynchronously after each streaming response. Both services share the same SQLite database (`synapse/memory/memory.db`). + +### Playbook System (`synapse/playbooks/` + `synapse/playbook_manager.py`) +Playbooks are ordered records (title, goal, instructions, tags), each persisted as a `{id}.yaml` file in `data/playbooks/` by `PlaybookFileStore` (the dir is `PLAYBOOK_DIR` in `nexus_config.py`). The **first** playbook by order is the active system prompt; all subsequent playbooks are injected as reference context. `PlaybookManager` is the thin class the backend uses to retrieve them and assemble the system prompt. + +### Ollama (`ollama/bin/ollama`) +A bundled Ollama binary lives at `ollama/bin/ollama`. `OllamaManager` in `synapse/ollama_manager.py` manages its lifecycle (start/stop/health-check) and selects the best available model. GPU detection uses Vulkan (`vulkaninfo`) to prefer discrete AMD/NVIDIA GPUs. The Ollama HTTP API is at `http://127.0.0.1:11434` (overridable via `OLLAMA_HOST` env var). + +### Frontend (`interface/web/`) +React 19 + Vite. No routing library — `App.jsx` manages page state in a single `currentPage` useState. All API calls hit `http://localhost:8000` (configured in `src/config.js`). Built to `dist/` (gitignored) via `npm run build` and served by the backend at `:8000` — the mount is in `synapse/main.py` (`_DIST` at `/`, guarded by `is_dir()`), so `dist/` must be built for the UI to appear. Pages: Chatbot, Playbook editor, Conversation History, Models, Memory, Settings, Logs. + +### Persistent Storage +Most data lands in `synapse/memory/memory.db` (SQLite, WAL mode). Tables: memory facts, conversations, messages, app settings. `synapse/memory/store.py` (`PersistentMemoryStore`) owns the schema and all queries. Playbooks are the exception — they live as YAML files in `data/playbooks/` (see Playbook System). `nexus_config.py` defines all paths; it also ensures all required directories exist on import. + +### Logs & Runtime State +- `runtime/backend.log`, `runtime/frontend.log`, `runtime/memory.log` — service stdout +- `runtime/logs/ollama.log`, `runtime/logs/chat.log` +- `runtime/pids/backend.pid`, `runtime/pids/frontend.pid` — used by the management CLI + +## Key Config + +| Concern | Location | +|---|---| +| Ollama host | `OLLAMA_HOST` env var (default `http://127.0.0.1:11434`) | +| All filesystem paths | `synapse/nexus_config.py` `Settings` class | +| Frontend API base URL | `interface/web/src/config.js` | +| Default chat/memory models | `synapse/nexus_config.py` `DEFAULT_CHAT_MODEL` / `DEFAULT_MEMORY_MODEL` | +| Python dependencies (base) | `requirements-base.txt` | +| Python dependencies (AMD/ROCm) | `requirements-amd.txt` | +| Python dependencies (NVIDIA/CUDA) | `requirements-nvidia.txt` (generated by `bin/gen-nvidia-reqs.py`) | +| Python dependencies (WSL/CPU) | `requirements-wsl.txt` | diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e32593 --- /dev/null +++ b/README.md @@ -0,0 +1,152 @@ +
+ +NexusOS + +# NexusOS + +**A local-first AI assistant platform.** Runs entirely on your machine — a +Python/FastAPI backend, a bundled Ollama instance for inference, a persistent +memory service, and a React frontend. No external AI provider is called. + +
+ +--- + +## What it is + +NexusOS ("Nexus") is a self-hosted assistant you actually own. All inference +runs through a **locally bundled Ollama** on `localhost`; conversations, facts, +and settings live in local SQLite. It ships with desktop branding (XFCE theme, +icons, boot splash) so it can be run as a full assistant environment, not just +a web app. + +- **Chat** — streaming responses from local Ollama models (SSE). +- **Persistent memory** — a dedicated service auto-extracts durable facts from + each exchange and layers them into future prompts. +- **Playbooks** — ordered YAML system-prompt records; the first is the active + persona, the rest are injected as reference context. +- **Model management** — list, pull, and delete Ollama models from the UI/CLI. +- **History** — full conversation persistence, search, edit, export. + +## Quick start + +NexusOS runs **single-process**: the backend on `:8000` serves the built web UI +itself, so there's no separate frontend server at runtime. Ollama is started +manually from the app (**Start AI** in the sidebar), not at boot. + +### Windows (recommended) + +```powershell +# 1. Install Git, then clone (Gitea user + PAT): +winget install Git.Git +# open a NEW PowerShell window, then: +git clone https://git.enderofwings.com/enderofwings/NexusOS.git nexus-core +cd nexus-core + +# 2. Native install — winget Python/Node/Ollama, venv, pip, web build, desktop icon +powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 +``` + +> **Execution policy:** Windows blocks unsigned `.ps1` scripts by default, so +> run them with `-ExecutionPolicy Bypass` as shown (a one-run override — nothing +> permanent). Double-clicking `install-windows.ps1` or running `.\install-windows.ps1` +> bare will fail with *"running scripts is disabled on this system"*. +> The installer self-elevates (a UAC prompt will appear). +> +> To allow scripts persistently instead (then you can run `.\...ps1` directly): +> ```powershell +> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +> ``` + +Then double-click the **NexusOS** desktop icon (the shortcut already passes the +bypass), or launch from a shell with: + +```powershell +powershell -ExecutionPolicy Bypass -File .\launch_nexus.ps1 +``` + +The app opens at `:8000`; click **Start AI** to launch Ollama. The installer +uses `requirements-wsl.txt` (CPU-only, pure-Python — no ML stack, since Ollama +does all inference over HTTP). + +### Linux + +```bash +# 1. Install deps into the Promethean venv (auto-selects AMD/NVIDIA/CPU) +./bin/install.sh + +# 2. Launch (memory :8001, backend :8000 — backend also serves the built UI) +./launch_nexus.sh +``` + +Python deps are layered: `requirements-base.txt` (GPU-agnostic core) plus one +GPU overlay — `requirements-amd.txt` (ROCm) or `requirements-nvidia.txt` (CUDA). +`requirements-wsl.txt` is the standalone CPU-only runtime (no base overlay). +`bin/install.sh` picks the right one for the host. + +### Individual services + +```bash +source Promethean/bin/activate + +uvicorn synapse.main:sio_app --host 0.0.0.0 --port 8000 --reload # backend (serves the UI too) +uvicorn synapse.memory.service:app --host 0.0.0.0 --port 8001 --reload # memory + +# Frontend dev server (hot-reload) — only needed when editing the UI; +# production serves the built dist/ from the backend at :8000. +cd interface/web && npm run dev +``` + +## CLI (`ncp`) + +Start/stop services and drive the same features as the web UI over the REST API: + +```bash +./management/nexus-cli.sh start # backend + frontend (--backend|--frontend|--memory) +./management/nexus-cli.sh stop + +ncp chat "" # stream a reply +ncp memory list | add | rm +ncp playbook list | show # first playbook (*) = active system prompt +ncp history [query] # recent conversations +``` + +## Architecture + +| Component | Location | Role | +|---|---|---| +| **Synapse** (backend) | `synapse/` | FastAPI app. `/chat/stream`, `/playbooks`, `/memory`, `/models`, `/conversations`, `/settings`, `/ollama`, `/icons`. Assembles the system prompt: active playbook → reference playbooks → memory facts → relevant past snippets. | +| **Memory service** | `synapse/memory/` | Separate FastAPI app (:8001). `/memories/extract` uses an Ollama prompt to decide what to persist. Shares the SQLite DB with the backend. | +| **Playbooks** | `synapse/playbooks/` + `data/playbooks/` | Ordered `{id}.yaml` records managed by `PlaybookManager`. | +| **Ollama** | `ollama/bin/ollama` | Bundled binary; `OllamaManager` handles lifecycle + model selection (Vulkan GPU detection). HTTP API at `127.0.0.1:11434`. | +| **Frontend** | `interface/web/` | React 19 + Vite. Built to `dist/` and served by the backend at `:8000` (single-process). Pages: Chat, Playbooks, History, Models, Memory, Settings. | + +### Storage + +Most data lives in `synapse/memory/memory.db` (SQLite, WAL) — facts, +conversations, messages, settings. Playbooks are the exception (YAML files in +`data/playbooks/`). All paths are defined in `synapse/nexus_config.py`. + +## Layout + +``` +synapse/ FastAPI backend + memory service + playbook/ollama managers +interface/web/ React + Vite frontend +management/ nexus-cli.sh, ncp API client, control panel, desktop theme +bin/ install, backup/restore, panel + provisioning scripts +assets/ branding: icons, boot splash, XFCE/GTK theme +data/playbooks/ active playbook YAML +``` + +## Configuration + +| Concern | Location | +|---|---| +| Ollama host | `OLLAMA_HOST` env (default `http://127.0.0.1:11434`) | +| Filesystem paths | `synapse/nexus_config.py` | +| Frontend API base URL | `interface/web/src/config.js` | +| Python deps | `requirements-base.txt` + amd/nvidia GPU overlay; `requirements-wsl.txt` = standalone CPU runtime | + +--- + +
NexusOS · local AI, self-hosted on GitNexus
diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..867bf6b --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0-rc1 diff --git a/assets/NexusOS.ico b/assets/NexusOS.ico new file mode 100644 index 0000000..71d111e Binary files /dev/null and b/assets/NexusOS.ico differ diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000..16b7e2f Binary files /dev/null and b/assets/background.png differ diff --git a/assets/boot/initramfs-hook-my-custom-logo b/assets/boot/initramfs-hook-my-custom-logo new file mode 100644 index 0000000..f56900d --- /dev/null +++ b/assets/boot/initramfs-hook-my-custom-logo @@ -0,0 +1,29 @@ +#!/bin/sh +# Bakes a real, dereferenced copy of the NexusOS Plymouth theme into the +# initramfs. The theme files live in nexus-core and are exposed at +# /usr/share/plymouth/themes/my-custom-logo via a symlink into /home, which +# the stock initramfs plymouth hook copies verbatim -> it dangles at early +# boot (before /home is mounted) and Plymouth falls back to text mode. +# This hook runs after the stock "plymouth" hook and replaces that dangling +# symlink with the actual theme files. + +PREREQS="plymouth" +prereqs() { echo "$PREREQS"; } + +case "$1" in + prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + +THEME_SRC="/home/jon/nexus-core/assets/boot/my-custom-logo" +THEME_DEST="${DESTDIR}/usr/share/plymouth/themes/my-custom-logo" + +[ -d "${THEME_SRC}" ] || exit 0 + +rm -rf "${THEME_DEST}" +mkdir -p "${THEME_DEST}" +cp -aL "${THEME_SRC}/." "${THEME_DEST}/" diff --git a/assets/boot/my-custom-logo/animation-0001.png b/assets/boot/my-custom-logo/animation-0001.png new file mode 100644 index 0000000..4275d88 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0001.png differ diff --git a/assets/boot/my-custom-logo/animation-0002.png b/assets/boot/my-custom-logo/animation-0002.png new file mode 100644 index 0000000..834a4da Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0002.png differ diff --git a/assets/boot/my-custom-logo/animation-0003.png b/assets/boot/my-custom-logo/animation-0003.png new file mode 100644 index 0000000..c57defa Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0003.png differ diff --git a/assets/boot/my-custom-logo/animation-0004.png b/assets/boot/my-custom-logo/animation-0004.png new file mode 100644 index 0000000..5f3d074 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0004.png differ diff --git a/assets/boot/my-custom-logo/animation-0005.png b/assets/boot/my-custom-logo/animation-0005.png new file mode 100644 index 0000000..891ce97 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0005.png differ diff --git a/assets/boot/my-custom-logo/animation-0006.png b/assets/boot/my-custom-logo/animation-0006.png new file mode 100644 index 0000000..fd34309 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0006.png differ diff --git a/assets/boot/my-custom-logo/animation-0007.png b/assets/boot/my-custom-logo/animation-0007.png new file mode 100644 index 0000000..4ea1626 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0007.png differ diff --git a/assets/boot/my-custom-logo/animation-0008.png b/assets/boot/my-custom-logo/animation-0008.png new file mode 100644 index 0000000..9961b29 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0008.png differ diff --git a/assets/boot/my-custom-logo/animation-0009.png b/assets/boot/my-custom-logo/animation-0009.png new file mode 100644 index 0000000..e17ded6 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0009.png differ diff --git a/assets/boot/my-custom-logo/animation-0010.png b/assets/boot/my-custom-logo/animation-0010.png new file mode 100644 index 0000000..2dd2a4e Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0010.png differ diff --git a/assets/boot/my-custom-logo/animation-0011.png b/assets/boot/my-custom-logo/animation-0011.png new file mode 100644 index 0000000..15accdf Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0011.png differ diff --git a/assets/boot/my-custom-logo/animation-0012.png b/assets/boot/my-custom-logo/animation-0012.png new file mode 100644 index 0000000..2847d19 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0012.png differ diff --git a/assets/boot/my-custom-logo/animation-0013.png b/assets/boot/my-custom-logo/animation-0013.png new file mode 100644 index 0000000..e3a70ad Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0013.png differ diff --git a/assets/boot/my-custom-logo/animation-0014.png b/assets/boot/my-custom-logo/animation-0014.png new file mode 100644 index 0000000..72adbff Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0014.png differ diff --git a/assets/boot/my-custom-logo/animation-0015.png b/assets/boot/my-custom-logo/animation-0015.png new file mode 100644 index 0000000..199f9ac Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0015.png differ diff --git a/assets/boot/my-custom-logo/animation-0016.png b/assets/boot/my-custom-logo/animation-0016.png new file mode 100644 index 0000000..73a985e Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0016.png differ diff --git a/assets/boot/my-custom-logo/animation-0017.png b/assets/boot/my-custom-logo/animation-0017.png new file mode 100644 index 0000000..87d86fb Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0017.png differ diff --git a/assets/boot/my-custom-logo/animation-0018.png b/assets/boot/my-custom-logo/animation-0018.png new file mode 100644 index 0000000..c4af64b Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0018.png differ diff --git a/assets/boot/my-custom-logo/animation-0019.png b/assets/boot/my-custom-logo/animation-0019.png new file mode 100644 index 0000000..f242239 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0019.png differ diff --git a/assets/boot/my-custom-logo/animation-0020.png b/assets/boot/my-custom-logo/animation-0020.png new file mode 100644 index 0000000..1d6c50b Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0020.png differ diff --git a/assets/boot/my-custom-logo/animation-0021.png b/assets/boot/my-custom-logo/animation-0021.png new file mode 100644 index 0000000..e2e12eb Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0021.png differ diff --git a/assets/boot/my-custom-logo/animation-0022.png b/assets/boot/my-custom-logo/animation-0022.png new file mode 100644 index 0000000..2fe5f19 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0022.png differ diff --git a/assets/boot/my-custom-logo/animation-0023.png b/assets/boot/my-custom-logo/animation-0023.png new file mode 100644 index 0000000..29e8b80 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0023.png differ diff --git a/assets/boot/my-custom-logo/animation-0024.png b/assets/boot/my-custom-logo/animation-0024.png new file mode 100644 index 0000000..8fb996d Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0024.png differ diff --git a/assets/boot/my-custom-logo/animation-0025.png b/assets/boot/my-custom-logo/animation-0025.png new file mode 100644 index 0000000..9520100 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0025.png differ diff --git a/assets/boot/my-custom-logo/animation-0026.png b/assets/boot/my-custom-logo/animation-0026.png new file mode 100644 index 0000000..0cf4df7 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0026.png differ diff --git a/assets/boot/my-custom-logo/animation-0027.png b/assets/boot/my-custom-logo/animation-0027.png new file mode 100644 index 0000000..e409cc1 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0027.png differ diff --git a/assets/boot/my-custom-logo/animation-0028.png b/assets/boot/my-custom-logo/animation-0028.png new file mode 100644 index 0000000..945cda3 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0028.png differ diff --git a/assets/boot/my-custom-logo/animation-0029.png b/assets/boot/my-custom-logo/animation-0029.png new file mode 100644 index 0000000..ec56ba0 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0029.png differ diff --git a/assets/boot/my-custom-logo/animation-0030.png b/assets/boot/my-custom-logo/animation-0030.png new file mode 100644 index 0000000..fed035e Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0030.png differ diff --git a/assets/boot/my-custom-logo/animation-0031.png b/assets/boot/my-custom-logo/animation-0031.png new file mode 100644 index 0000000..44ed292 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0031.png differ diff --git a/assets/boot/my-custom-logo/animation-0032.png b/assets/boot/my-custom-logo/animation-0032.png new file mode 100644 index 0000000..aa73ccd Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0032.png differ diff --git a/assets/boot/my-custom-logo/animation-0033.png b/assets/boot/my-custom-logo/animation-0033.png new file mode 100644 index 0000000..4050187 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0033.png differ diff --git a/assets/boot/my-custom-logo/animation-0034.png b/assets/boot/my-custom-logo/animation-0034.png new file mode 100644 index 0000000..d67b954 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0034.png differ diff --git a/assets/boot/my-custom-logo/animation-0035.png b/assets/boot/my-custom-logo/animation-0035.png new file mode 100644 index 0000000..2918406 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0035.png differ diff --git a/assets/boot/my-custom-logo/animation-0036.png b/assets/boot/my-custom-logo/animation-0036.png new file mode 100644 index 0000000..2dd20c8 Binary files /dev/null and b/assets/boot/my-custom-logo/animation-0036.png differ diff --git a/assets/boot/my-custom-logo/bgrt-fallback.png b/assets/boot/my-custom-logo/bgrt-fallback.png new file mode 100644 index 0000000..4720a90 Binary files /dev/null and b/assets/boot/my-custom-logo/bgrt-fallback.png differ diff --git a/assets/boot/my-custom-logo/bullet.png b/assets/boot/my-custom-logo/bullet.png new file mode 100644 index 0000000..e94e877 Binary files /dev/null and b/assets/boot/my-custom-logo/bullet.png differ diff --git a/assets/boot/my-custom-logo/capslock.png b/assets/boot/my-custom-logo/capslock.png new file mode 100644 index 0000000..45afa93 Binary files /dev/null and b/assets/boot/my-custom-logo/capslock.png differ diff --git a/assets/boot/my-custom-logo/entry.png b/assets/boot/my-custom-logo/entry.png new file mode 100644 index 0000000..8fa0a11 Binary files /dev/null and b/assets/boot/my-custom-logo/entry.png differ diff --git a/assets/boot/my-custom-logo/keyboard.png b/assets/boot/my-custom-logo/keyboard.png new file mode 100644 index 0000000..e1fd13b Binary files /dev/null and b/assets/boot/my-custom-logo/keyboard.png differ diff --git a/assets/boot/my-custom-logo/keymap-render.png b/assets/boot/my-custom-logo/keymap-render.png new file mode 100644 index 0000000..91a3eec Binary files /dev/null and b/assets/boot/my-custom-logo/keymap-render.png differ diff --git a/assets/boot/my-custom-logo/lock.png b/assets/boot/my-custom-logo/lock.png new file mode 100644 index 0000000..f3a13ee Binary files /dev/null and b/assets/boot/my-custom-logo/lock.png differ diff --git a/assets/boot/my-custom-logo/my-custom-logo.plymouth b/assets/boot/my-custom-logo/my-custom-logo.plymouth new file mode 100644 index 0000000..6a04ce6 --- /dev/null +++ b/assets/boot/my-custom-logo/my-custom-logo.plymouth @@ -0,0 +1,54 @@ +[Plymouth Theme] +Name=My Custom Logo +Description=Custom boot logo with shimmer effect +ModuleName=two-step + +[two-step] +Font=Cantarell 12 +TitleFont=Cantarell Light 30 +ImageDir=/usr/share/plymouth/themes/my-custom-logo +DialogHorizontalAlignment=.5 +DialogVerticalAlignment=.382 +TitleHorizontalAlignment=.5 +TitleVerticalAlignment=.382 +HorizontalAlignment=.5 +VerticalAlignment=.5 +WatermarkHorizontalAlignment=.5 +WatermarkVerticalAlignment=.96 +Transition=none +TransitionDuration=0.0 +BackgroundStartColor=0x000000 +BackgroundEndColor=0x000000 +ProgressBarBackgroundColor=0x606060 +ProgressBarForegroundColor=0xffffff +MessageBelowAnimation=true + +[boot-up] +UseEndAnimation=false + +[shutdown] +UseEndAnimation=false + +[reboot] +UseEndAnimation=false + +[updates] +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Installing Updates... +SubTitle=Do not turn off your computer + +[system-upgrade] +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Upgrading System... +SubTitle=Do not turn off your computer + +[firmware-upgrade] +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Upgrading Firmware... +SubTitle=Do not turn off your computer diff --git a/assets/boot/my-custom-logo/throbber-0001.png b/assets/boot/my-custom-logo/throbber-0001.png new file mode 100644 index 0000000..4275d88 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0001.png differ diff --git a/assets/boot/my-custom-logo/throbber-0002.png b/assets/boot/my-custom-logo/throbber-0002.png new file mode 100644 index 0000000..d305c43 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0002.png differ diff --git a/assets/boot/my-custom-logo/throbber-0003.png b/assets/boot/my-custom-logo/throbber-0003.png new file mode 100644 index 0000000..5079a10 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0003.png differ diff --git a/assets/boot/my-custom-logo/throbber-0004.png b/assets/boot/my-custom-logo/throbber-0004.png new file mode 100644 index 0000000..afafd5b Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0004.png differ diff --git a/assets/boot/my-custom-logo/throbber-0005.png b/assets/boot/my-custom-logo/throbber-0005.png new file mode 100644 index 0000000..7e70385 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0005.png differ diff --git a/assets/boot/my-custom-logo/throbber-0006.png b/assets/boot/my-custom-logo/throbber-0006.png new file mode 100644 index 0000000..4ea1626 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0006.png differ diff --git a/assets/boot/my-custom-logo/throbber-0007.png b/assets/boot/my-custom-logo/throbber-0007.png new file mode 100644 index 0000000..ae7bc8a Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0007.png differ diff --git a/assets/boot/my-custom-logo/throbber-0008.png b/assets/boot/my-custom-logo/throbber-0008.png new file mode 100644 index 0000000..9000ad1 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0008.png differ diff --git a/assets/boot/my-custom-logo/throbber-0009.png b/assets/boot/my-custom-logo/throbber-0009.png new file mode 100644 index 0000000..1dc98f2 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0009.png differ diff --git a/assets/boot/my-custom-logo/throbber-0010.png b/assets/boot/my-custom-logo/throbber-0010.png new file mode 100644 index 0000000..0cccf02 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0010.png differ diff --git a/assets/boot/my-custom-logo/throbber-0011.png b/assets/boot/my-custom-logo/throbber-0011.png new file mode 100644 index 0000000..e3a70ad Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0011.png differ diff --git a/assets/boot/my-custom-logo/throbber-0012.png b/assets/boot/my-custom-logo/throbber-0012.png new file mode 100644 index 0000000..c38a69c Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0012.png differ diff --git a/assets/boot/my-custom-logo/throbber-0013.png b/assets/boot/my-custom-logo/throbber-0013.png new file mode 100644 index 0000000..9b36e13 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0013.png differ diff --git a/assets/boot/my-custom-logo/throbber-0014.png b/assets/boot/my-custom-logo/throbber-0014.png new file mode 100644 index 0000000..52c2a81 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0014.png differ diff --git a/assets/boot/my-custom-logo/throbber-0015.png b/assets/boot/my-custom-logo/throbber-0015.png new file mode 100644 index 0000000..7b1b780 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0015.png differ diff --git a/assets/boot/my-custom-logo/throbber-0016.png b/assets/boot/my-custom-logo/throbber-0016.png new file mode 100644 index 0000000..f242239 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0016.png differ diff --git a/assets/boot/my-custom-logo/throbber-0017.png b/assets/boot/my-custom-logo/throbber-0017.png new file mode 100644 index 0000000..50b1102 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0017.png differ diff --git a/assets/boot/my-custom-logo/throbber-0018.png b/assets/boot/my-custom-logo/throbber-0018.png new file mode 100644 index 0000000..9446a83 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0018.png differ diff --git a/assets/boot/my-custom-logo/throbber-0019.png b/assets/boot/my-custom-logo/throbber-0019.png new file mode 100644 index 0000000..d879178 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0019.png differ diff --git a/assets/boot/my-custom-logo/throbber-0020.png b/assets/boot/my-custom-logo/throbber-0020.png new file mode 100644 index 0000000..670ae2a Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0020.png differ diff --git a/assets/boot/my-custom-logo/throbber-0021.png b/assets/boot/my-custom-logo/throbber-0021.png new file mode 100644 index 0000000..9520100 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0021.png differ diff --git a/assets/boot/my-custom-logo/throbber-0022.png b/assets/boot/my-custom-logo/throbber-0022.png new file mode 100644 index 0000000..a58d757 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0022.png differ diff --git a/assets/boot/my-custom-logo/throbber-0023.png b/assets/boot/my-custom-logo/throbber-0023.png new file mode 100644 index 0000000..2bda4c0 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0023.png differ diff --git a/assets/boot/my-custom-logo/throbber-0024.png b/assets/boot/my-custom-logo/throbber-0024.png new file mode 100644 index 0000000..55a7268 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0024.png differ diff --git a/assets/boot/my-custom-logo/throbber-0025.png b/assets/boot/my-custom-logo/throbber-0025.png new file mode 100644 index 0000000..50753d2 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0025.png differ diff --git a/assets/boot/my-custom-logo/throbber-0026.png b/assets/boot/my-custom-logo/throbber-0026.png new file mode 100644 index 0000000..44ed292 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0026.png differ diff --git a/assets/boot/my-custom-logo/throbber-0027.png b/assets/boot/my-custom-logo/throbber-0027.png new file mode 100644 index 0000000..59d304c Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0027.png differ diff --git a/assets/boot/my-custom-logo/throbber-0028.png b/assets/boot/my-custom-logo/throbber-0028.png new file mode 100644 index 0000000..aa02fa3 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0028.png differ diff --git a/assets/boot/my-custom-logo/throbber-0029.png b/assets/boot/my-custom-logo/throbber-0029.png new file mode 100644 index 0000000..e769813 Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0029.png differ diff --git a/assets/boot/my-custom-logo/throbber-0030.png b/assets/boot/my-custom-logo/throbber-0030.png new file mode 100644 index 0000000..852b18f Binary files /dev/null and b/assets/boot/my-custom-logo/throbber-0030.png differ diff --git a/assets/boot/my-custom-logo/watermark.png b/assets/boot/my-custom-logo/watermark.png new file mode 100644 index 0000000..fcdbb92 Binary files /dev/null and b/assets/boot/my-custom-logo/watermark.png differ diff --git a/assets/gitnexus-logo.svg b/assets/gitnexus-logo.svg new file mode 100644 index 0000000..ca49156 --- /dev/null +++ b/assets/gitnexus-logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/n-folder.png b/assets/n-folder.png new file mode 100644 index 0000000..b4cea46 Binary files /dev/null and b/assets/n-folder.png differ diff --git a/assets/n-small.png b/assets/n-small.png new file mode 100644 index 0000000..c96bb05 Binary files /dev/null and b/assets/n-small.png differ diff --git a/assets/nexus-folder-app.png b/assets/nexus-folder-app.png new file mode 100644 index 0000000..239f381 Binary files /dev/null and b/assets/nexus-folder-app.png differ diff --git a/assets/nexus-terminal-blank.svg b/assets/nexus-terminal-blank.svg new file mode 100644 index 0000000..a63a108 --- /dev/null +++ b/assets/nexus-terminal-blank.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/nexus-terminal-draft.png b/assets/nexus-terminal-draft.png new file mode 100644 index 0000000..59f5a1b Binary files /dev/null and b/assets/nexus-terminal-draft.png differ diff --git a/assets/nexus-terminal-naked.png b/assets/nexus-terminal-naked.png new file mode 100644 index 0000000..dc9c154 Binary files /dev/null and b/assets/nexus-terminal-naked.png differ diff --git a/assets/nexus-terminal-ring.png b/assets/nexus-terminal-ring.png new file mode 100644 index 0000000..59f5a1b Binary files /dev/null and b/assets/nexus-terminal-ring.png differ diff --git a/assets/panel-icons/audio-volume-high.png b/assets/panel-icons/audio-volume-high.png new file mode 100644 index 0000000..495c963 Binary files /dev/null and b/assets/panel-icons/audio-volume-high.png differ diff --git a/assets/panel-icons/audio-volume-low.png b/assets/panel-icons/audio-volume-low.png new file mode 100644 index 0000000..fdd681e Binary files /dev/null and b/assets/panel-icons/audio-volume-low.png differ diff --git a/assets/panel-icons/audio-volume-medium.png b/assets/panel-icons/audio-volume-medium.png new file mode 100644 index 0000000..3e2914d Binary files /dev/null and b/assets/panel-icons/audio-volume-medium.png differ diff --git a/assets/panel-icons/audio-volume-muted.png b/assets/panel-icons/audio-volume-muted.png new file mode 100644 index 0000000..53266f0 Binary files /dev/null and b/assets/panel-icons/audio-volume-muted.png differ diff --git a/assets/panel-icons/bluetooth-active.png b/assets/panel-icons/bluetooth-active.png new file mode 100644 index 0000000..1d054f9 Binary files /dev/null and b/assets/panel-icons/bluetooth-active.png differ diff --git a/assets/panel-icons/bluetooth-disabled.png b/assets/panel-icons/bluetooth-disabled.png new file mode 100644 index 0000000..2bfddb1 Binary files /dev/null and b/assets/panel-icons/bluetooth-disabled.png differ diff --git a/assets/panel-icons/bluetooth-online.png b/assets/panel-icons/bluetooth-online.png new file mode 100644 index 0000000..70a741e Binary files /dev/null and b/assets/panel-icons/bluetooth-online.png differ diff --git a/assets/panel-icons/bluetooth-paired.png b/assets/panel-icons/bluetooth-paired.png new file mode 100644 index 0000000..4341d6c Binary files /dev/null and b/assets/panel-icons/bluetooth-paired.png differ diff --git a/assets/panel-icons/network-offline.png b/assets/panel-icons/network-offline.png new file mode 100644 index 0000000..3f97c32 Binary files /dev/null and b/assets/panel-icons/network-offline.png differ diff --git a/assets/panel-icons/network-wired.png b/assets/panel-icons/network-wired.png new file mode 100644 index 0000000..4bedd5a Binary files /dev/null and b/assets/panel-icons/network-wired.png differ diff --git a/assets/panel-icons/network-wireless-signal-excellent.png b/assets/panel-icons/network-wireless-signal-excellent.png new file mode 100644 index 0000000..91b22d9 Binary files /dev/null and b/assets/panel-icons/network-wireless-signal-excellent.png differ diff --git a/assets/panel-icons/network-wireless-signal-good.png b/assets/panel-icons/network-wireless-signal-good.png new file mode 100644 index 0000000..2c735e0 Binary files /dev/null and b/assets/panel-icons/network-wireless-signal-good.png differ diff --git a/assets/panel-icons/network-wireless-signal-none.png b/assets/panel-icons/network-wireless-signal-none.png new file mode 100644 index 0000000..8a5a579 Binary files /dev/null and b/assets/panel-icons/network-wireless-signal-none.png differ diff --git a/assets/panel-icons/network-wireless-signal-ok.png b/assets/panel-icons/network-wireless-signal-ok.png new file mode 100644 index 0000000..29f095a Binary files /dev/null and b/assets/panel-icons/network-wireless-signal-ok.png differ diff --git a/assets/panel-icons/network-wireless-signal-weak.png b/assets/panel-icons/network-wireless-signal-weak.png new file mode 100644 index 0000000..ce82778 Binary files /dev/null and b/assets/panel-icons/network-wireless-signal-weak.png differ diff --git a/assets/panel-icons/nexus-off.png b/assets/panel-icons/nexus-off.png new file mode 100644 index 0000000..fde6fc9 Binary files /dev/null and b/assets/panel-icons/nexus-off.png differ diff --git a/assets/panel-icons/nexus-on.png b/assets/panel-icons/nexus-on.png new file mode 100644 index 0000000..6bd2ca2 Binary files /dev/null and b/assets/panel-icons/nexus-on.png differ diff --git a/assets/panel-icons/nexus-partial.png b/assets/panel-icons/nexus-partial.png new file mode 100644 index 0000000..2b1d244 Binary files /dev/null and b/assets/panel-icons/nexus-partial.png differ diff --git a/assets/promethean-terminal.png b/assets/promethean-terminal.png new file mode 100644 index 0000000..61c6079 Binary files /dev/null and b/assets/promethean-terminal.png differ diff --git a/assets/themes/CREDITS.md b/assets/themes/CREDITS.md new file mode 100644 index 0000000..7aefa06 --- /dev/null +++ b/assets/themes/CREDITS.md @@ -0,0 +1,24 @@ +# Theme Asset Credits + +The NexusOS theme is assembled from original work plus assets adapted from the following upstream projects. All upstream sources are GPL-3.0 (or compatible), so the NexusOS theme inherits GPL-3.0. + +## Sources + +### WhiteSur GTK Theme — `gtk-2.0/`, `xfwm4/` +- **Project**: https://github.com/vinceliuice/WhiteSur-gtk-theme +- **Author**: Vince Liu (vinceliuice) +- **License**: GPL-3.0 +- **Variant used as base**: `WhiteSur-Dark-purple` +- **What we use**: The entire `gtk-2.0/` and `xfwm4/` directories are cloned verbatim from WhiteSur-Dark-purple. We rely on these for macOS-style window controls (stoplight buttons) and the GTK2 application skin. + +### Mint-Y Icon Theme — `NexusOS-icons/` (folder geometry reference) +- **Project**: https://github.com/linuxmint/mint-y-icons +- **Author**: Linux Mint team +- **License**: GPL-3.0 +- **What we use**: Visual reference only for the folder shape language (flat folder with tab + lip). No PNGs are copied from Mint-Y; our folder set is regenerated from scratch via `NexusOS-icons-src/build.py` against a master SVG we authored. + +## NexusOS-original content +- `gtk-3.0/*.css` — original work +- `NexusOS-icons-src/*` and the rendered output in `NexusOS-icons/places/` — original work +- The `n-small.png` badge composited into `folder-nexus-core` is the NexusOS logo at `nexus-core/assets/n-small.png` +- `index.theme` files in each theme directory — original work diff --git a/assets/themes/KDE/aurorae/NexusOS/NexusOSrc b/assets/themes/KDE/aurorae/NexusOS/NexusOSrc new file mode 100644 index 0000000..f552dad --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/NexusOSrc @@ -0,0 +1,30 @@ +[General] +TitleAlignment=Left +TitleEdgeTop=0 +TitleEdgeBottom=0 +TitleEdgeLeft=12 +TitleEdgeRight=8 +TitleBorderLeft=0 +TitleBorderRight=0 +TitleHeight=30 +BorderLeft=1 +BorderRight=1 +BorderBottom=5 +BorderTop=0 +ButtonWidth=32 +ButtonSize=32 +ButtonSpacing=8 +ButtonMarginTop=0 +ExplicitButtonSpacer=0 +PaddingTop=0 +PaddingBottom=0 +PaddingRight=0 +PaddingLeft=0 +ActiveTextColor=#f2f2f2 +ActiveTextShadowColor=0,0,0,0 +InactiveTextColor=#6e7173 +InactiveTextShadowColor=0,0,0,0 +Shadow=false +ShadowColor=0,0,0,180 +ShadowStrength=100 +Animation=0 diff --git a/assets/themes/KDE/aurorae/NexusOS/alldesktops.svg b/assets/themes/KDE/aurorae/NexusOS/alldesktops.svg new file mode 100644 index 0000000..6786a6c --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/alldesktops.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/close.svg b/assets/themes/KDE/aurorae/NexusOS/close.svg new file mode 100644 index 0000000..1313b7e --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/close.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/decoration.svg b/assets/themes/KDE/aurorae/NexusOS/decoration.svg new file mode 100644 index 0000000..01a78b1 --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/decoration.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/keepabove.svg b/assets/themes/KDE/aurorae/NexusOS/keepabove.svg new file mode 100644 index 0000000..a772438 --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/keepabove.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/keepbelow.svg b/assets/themes/KDE/aurorae/NexusOS/keepbelow.svg new file mode 100644 index 0000000..fc8032d --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/keepbelow.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/maximize.svg b/assets/themes/KDE/aurorae/NexusOS/maximize.svg new file mode 100644 index 0000000..2c8d255 --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/maximize.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/metadata.desktop b/assets/themes/KDE/aurorae/NexusOS/metadata.desktop new file mode 100644 index 0000000..92ed21f --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/metadata.desktop @@ -0,0 +1,18 @@ +[Desktop Entry] +Name=NexusOS +Comment=Flat dark-purple window decoration, lime-green focus accent +Type=Service +X-KDE-ServiceTypes=org.kde.kwin.decoration + +[KWin] +Type=Aurorae + +X-KDE-PluginInfo-Author=Jon +X-KDE-PluginInfo-Email= +X-KDE-PluginInfo-Name=NexusOS +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false diff --git a/assets/themes/KDE/aurorae/NexusOS/minimize.svg b/assets/themes/KDE/aurorae/NexusOS/minimize.svg new file mode 100644 index 0000000..b4a8754 --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/minimize.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/restore.svg b/assets/themes/KDE/aurorae/NexusOS/restore.svg new file mode 100644 index 0000000..27f72c0 --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/restore.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/aurorae/NexusOS/shade.svg b/assets/themes/KDE/aurorae/NexusOS/shade.svg new file mode 100644 index 0000000..bb4e7df --- /dev/null +++ b/assets/themes/KDE/aurorae/NexusOS/shade.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/generate_plasma_colors.py b/assets/themes/KDE/generate_plasma_colors.py new file mode 100644 index 0000000..1871513 --- /dev/null +++ b/assets/themes/KDE/generate_plasma_colors.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""Generate NexusOS.colors (KDE KColorScheme format) from _palette.py. + +Run from anywhere: + python3 assets/themes/KDE/generate_plasma_colors.py + +Writes to assets/themes/KDE/plasma/NexusOS/NexusOS.colors (relative to repo root). +""" +import os +import sys + +# Resolve repo root relative to this script's location +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +REPO_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, "../../..")) +sys.path.insert(0, os.path.join(REPO_ROOT, "assets/themes")) + +import _palette as p # noqa: E402 + +OUT = os.path.join(SCRIPT_DIR, "plasma/NexusOS/NexusOS.colors") + + +def rgb(hexstr): + h = hexstr.lstrip("#") + return f"{int(h[0:2],16)},{int(h[2:4],16)},{int(h[4:6],16)}" + + +def section(name, bg_normal, bg_alt, fg_normal, fg_inactive, fg_active, + fg_link, fg_visited, fg_neg, fg_neutral, fg_pos, + deco_focus, deco_hover): + return f"""[Colors:{name}] +BackgroundNormal={rgb(bg_normal)} +BackgroundAlternate={rgb(bg_alt)} +ForegroundNormal={rgb(fg_normal)} +ForegroundInactive={rgb(fg_inactive)} +ForegroundActive={rgb(fg_active)} +ForegroundLink={rgb(fg_link)} +ForegroundVisited={rgb(fg_visited)} +ForegroundNegative={rgb(fg_neg)} +ForegroundNeutral={rgb(fg_neutral)} +ForegroundPositive={rgb(fg_pos)} +DecorationFocus={rgb(deco_focus)} +DecorationHover={rgb(deco_hover)} +""" + + +SHARED = dict( + fg_normal = "#" + p.TEXT_PRIMARY, + fg_inactive = "#" + p.TEXT_SECONDARY, + fg_active = "#" + p.BRAND_GREEN, + fg_link = "#" + p.BRAND_GREEN_LIGHT, + fg_visited = "#" + p.BRAND_PURPLE_LIGHT, + fg_neg = "#" + p.ERROR, + fg_neutral = "#" + p.WARNING, + fg_pos = "#" + p.SUCCESS, + deco_focus = "#" + p.BRAND_GREEN, + deco_hover = "#" + p.BRAND_PURPLE, +) + +content = f"""\ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.1 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=0 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +""" + section( + "Window", + bg_normal="#" + p.BASE_BG, + bg_alt ="#" + p.SURFACE_BG, + **SHARED, +) + section( + "View", + bg_normal="#" + p.BASE_BG, + bg_alt ="#" + p.SURFACE_BG, + **SHARED, +) + section( + "Button", + bg_normal="#" + p.SURFACE_BG_ALT, + bg_alt ="#" + p.BASE_BG, + **SHARED, +) + section( + "Selection", + bg_normal ="#" + p.BRAND_PURPLE, + bg_alt ="#" + p.BRAND_GREEN_DARK, + fg_normal ="#" + p.TEXT_ON_SELECTION, + fg_inactive="#" + p.TEXT_ON_SELECTION, + fg_active ="#" + p.BRAND_GREEN, + fg_link ="#" + p.BRAND_GREEN_LIGHT, + fg_visited="#" + p.BRAND_PURPLE_LIGHT, + fg_neg ="#" + p.ERROR, + fg_neutral="#" + p.WARNING, + fg_pos ="#" + p.SUCCESS, + deco_focus="#" + p.BRAND_GREEN, + deco_hover="#" + p.BRAND_PURPLE, +) + section( + "Tooltip", + bg_normal="#" + p.OVERLAY_BG, + bg_alt ="#" + p.BORDER_STRONG, + **SHARED, +) + section( + "Complementary", + bg_normal="#" + p.SURFACE_BG_ALT, + bg_alt ="#" + p.OVERLAY_BG, + **SHARED, +) + section( + "Header", + bg_normal="#" + p.BASE_BG, + bg_alt ="#" + p.SURFACE_BG_ALT, + **SHARED, +) + f"""\ +[General] +ColorScheme=NexusOS +Name=NexusOS +shadeSortColumn=true + +[KDE] +contrast=4 +""" + +os.makedirs(os.path.dirname(OUT), exist_ok=True) +with open(OUT, "w") as f: + f.write(content) + +print(f"Written: {OUT}") diff --git a/assets/themes/KDE/install-plasma.sh b/assets/themes/KDE/install-plasma.sh new file mode 100644 index 0000000..454a959 --- /dev/null +++ b/assets/themes/KDE/install-plasma.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# NexusOS KDE Plasma theme installer — idempotent, safe to re-run. +# Equivalent of assets/themes/install-theme.sh but for KDE. +# +# Usage: +# assets/themes/KDE/install-plasma.sh [--no-sddm] +# +# --no-sddm skip the SDDM step (requires sudo) — useful for live testing +set -euo pipefail + +KDE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO="$(cd "$KDE/../../.." && pwd)" + +NO_SDDM=0 +for arg in "$@"; do + [[ "$arg" == "--no-sddm" ]] && NO_SDDM=1 +done + +echo "NexusOS KDE installer — repo: $REPO" + +# ── 1. Create target directories ──────────────────────────────────────── +mkdir -p \ + ~/.local/share/aurorae/themes \ + ~/.local/share/plasma/desktoptheme \ + ~/.local/share/color-schemes \ + ~/.local/share/konsole \ + ~/.local/share/kscreenlocker/themes \ + ~/.config/Kvantum + +# ── 2. Symlink theme directories (update automatically on git pull) ────── +ln -sfn "$KDE/aurorae/NexusOS" ~/.local/share/aurorae/themes/NexusOS +ln -sfn "$KDE/plasma/NexusOS" ~/.local/share/plasma/desktoptheme/NexusOS +ln -sfn "$KDE/kscreenlocker/NexusOS" ~/.local/share/kscreenlocker/themes/NexusOS +ln -sfn "$KDE/kvantum/NexusOS" ~/.config/Kvantum/NexusOS + +echo " [ok] theme symlinks" + +# ── 3. Copy files that need to be in specific locations (not symlinks) ─── +cp -f "$KDE/plasma/NexusOS/NexusOS.colors" ~/.local/share/color-schemes/ +cp -f "$KDE/konsole/NexusOS.colorscheme" ~/.local/share/konsole/ +cp -f "$KDE/konsole/NexusOS-Promethean.colorscheme" ~/.local/share/konsole/ +cp -f "$KDE/konsole/Promethean.profile" ~/.local/share/konsole/ + +echo " [ok] color scheme + konsole files" + +# ── 4. KDE color scheme ────────────────────────────────────────────────── +if command -v plasma-apply-colorscheme &>/dev/null; then + plasma-apply-colorscheme NexusOS 2>/dev/null && echo " [ok] color scheme applied" +else + echo " [skip] plasma-apply-colorscheme not found — apply color scheme manually" +fi + +# ── 5. Kvantum ─────────────────────────────────────────────────────────── +if command -v kvantummanager &>/dev/null; then + kvantummanager --set NexusOS 2>/dev/null && echo " [ok] Kvantum set to NexusOS" +else + echo " [skip] kvantummanager not found — install qt5-style-kvantum and run: kvantummanager --set NexusOS" +fi + +# ── 6. KWin Aurorae decoration ─────────────────────────────────────────── +if command -v kwriteconfig5 &>/dev/null; then + kwriteconfig5 --file kwinrc \ + --group "org.kde.kdecoration2" --key library "org.kde.kwin.aurorae" + kwriteconfig5 --file kwinrc \ + --group "org.kde.kdecoration2" --key theme "__aurorae__svg__NexusOS" + # Buttons on the right: minimize, maximize, close (I=minimize, A=maximize, X=close) + kwriteconfig5 --file kwinrc \ + --group "org.kde.kdecoration2" --key ButtonsOnLeft "" + kwriteconfig5 --file kwinrc \ + --group "org.kde.kdecoration2" --key ButtonsOnRight "IAX" + echo " [ok] KWin decoration: Aurorae NexusOS, buttons right" +else + echo " [skip] kwriteconfig5 not found — configure KWin decoration manually" +fi + +# ── 7. Plasma shell theme ──────────────────────────────────────────────── +if command -v plasma-apply-desktoptheme &>/dev/null; then + plasma-apply-desktoptheme NexusOS 2>/dev/null && echo " [ok] Plasma shell theme: NexusOS" +else + echo " [skip] plasma-apply-desktoptheme not found — apply desktop theme manually" +fi + +# ── 8. kscreenlocker ───────────────────────────────────────────────────── +if command -v kwriteconfig5 &>/dev/null; then + kwriteconfig5 --file kscreenlockerrc --group Greeter --key Theme NexusOS + echo " [ok] kscreenlocker theme: NexusOS" +fi + +# ── 9. GTK apps under Plasma ───────────────────────────────────────────── +if command -v kwriteconfig5 &>/dev/null; then + kwriteconfig5 --file kdeglobals --group KDE --key widgetStyle "kvantum-dark" + kwriteconfig5 --file kdeglobals --group Icons --key Theme NexusOS + kwriteconfig5 --file kdeglobals --group General \ + --key font "Ubuntu,10,-1,5,50,0,0,0,0,0" + echo " [ok] kdeglobals: Kvantum style, NexusOS icons, Ubuntu 10" +fi +if command -v gsettings &>/dev/null; then + gsettings set org.gnome.desktop.interface gtk-theme NexusOS 2>/dev/null || true + gsettings set org.gnome.desktop.interface icon-theme NexusOS 2>/dev/null || true + echo " [ok] gsettings: GTK theme + icons = NexusOS" +fi + +# ── 10. SDDM login theme ───────────────────────────────────────────────── +if [[ $NO_SDDM -eq 0 ]]; then + echo " Deploying SDDM theme + KDE session entry (requires sudo)..." + sudo cp -r "$KDE/sddm/NexusOS-QML" /usr/share/sddm/themes/ + sudo cp "$REPO/management/sessions/nexusos-kde.desktop" /usr/share/xsessions/ + if command -v kwriteconfig5 &>/dev/null; then + sudo kwriteconfig5 --file /etc/sddm.conf --group Theme --key Current NexusOS-QML + else + # Fallback: write the INI directly + sudo bash -c 'printf "[Theme]\nCurrent=NexusOS-QML\n" > /etc/sddm.conf.d/nexusos.conf' + fi + echo " [ok] SDDM theme: NexusOS-QML" + echo " [ok] KDE session: NexusOS-KDE added to /usr/share/xsessions/" +else + echo " [skip] SDDM (--no-sddm passed)" +fi + +# ── 11. Promethean Terminal desktop launcher ────────────────────────────── +DESKTOP_SRC="$REPO/bin/promethean/promethean-terminal.desktop" +DESKTOP_DEST="$HOME/.local/share/applications/promethean-terminal.desktop" +if [[ -f "$DESKTOP_SRC" ]]; then + cp -f "$DESKTOP_SRC" "$DESKTOP_DEST" + echo " [ok] Promethean Terminal launcher updated" +fi + +# ── 12. Apply live changes (if Plasma is running) ──────────────────────── +if qdbus org.kde.KWin /KWin reconfigure 2>/dev/null; then + echo " [ok] KWin reconfigured" + # Restart plasmashell to pick up new shell theme + if command -v kquitapp5 &>/dev/null && command -v kstart5 &>/dev/null; then + kquitapp5 plasmashell 2>/dev/null + sleep 1 + kstart5 plasmashell & + echo " [ok] plasmashell restarted" + fi +fi + +echo "" +echo "NexusOS KDE theme installed. On first KDE session:" +echo " 1. System Settings → Appearance → verify all components show NexusOS" +echo " 2. Panel: right-click → Edit Panel to adjust height/position" +echo " 3. Konsole: Settings → Edit Profile → select Promethean (for Promethean Terminal)" +echo " 4. Test lock screen: Meta+L" diff --git a/assets/themes/KDE/konsole/NexusOS-Promethean.colorscheme b/assets/themes/KDE/konsole/NexusOS-Promethean.colorscheme new file mode 100644 index 0000000..c43aca7 --- /dev/null +++ b/assets/themes/KDE/konsole/NexusOS-Promethean.colorscheme @@ -0,0 +1,94 @@ +[General] +Description=NexusOS Promethean +Opacity=1 +Wallpaper= + +[Background] +Color=26,0,48 + +[BackgroundFaint] +Color=13,0,16 + +[BackgroundIntense] +Color=61,0,96 + +[Foreground] +Color=204,204,204 + +[ForegroundFaint] +Color=140,140,140 + +[ForegroundIntense] +Color=230,230,230 + +[Color0] +Color=26,0,48 + +[Color0Faint] +Color=13,0,24 + +[Color0Intense] +Color=61,0,96 + +[Color1] +Color=218,68,83 + +[Color1Faint] +Color=160,50,60 + +[Color1Intense] +Color=235,100,110 + +[Color2] +Color=39,174,96 + +[Color2Faint] +Color=28,128,70 + +[Color2Intense] +Color=80,200,120 + +[Color3] +Color=246,116,0 + +[Color3Faint] +Color=180,85,0 + +[Color3Intense] +Color=255,160,50 + +[Color4] +Color=136,0,143 + +[Color4Faint] +Color=94,0,100 + +[Color4Intense] +Color=176,64,192 + +[Color5] +Color=176,64,192 + +[Color5Faint] +Color=120,40,135 + +[Color5Intense] +Color=210,110,225 + +[Color6] +Color=140,198,63 + +[Color6Faint] +Color=100,145,45 + +[Color6Intense] +Color=184,227,115 + +[Color7] +Color=204,204,204 + +[Color7Faint] +Color=150,150,150 + +[Color7Intense] +Color=230,230,230 diff --git a/assets/themes/KDE/konsole/NexusOS.colorscheme b/assets/themes/KDE/konsole/NexusOS.colorscheme new file mode 100644 index 0000000..6a761dd --- /dev/null +++ b/assets/themes/KDE/konsole/NexusOS.colorscheme @@ -0,0 +1,94 @@ +[General] +Description=NexusOS +Opacity=1 +Wallpaper= + +[Background] +Color=30,21,38 + +[BackgroundFaint] +Color=31,34,37 + +[BackgroundIntense] +Color=46,50,54 + +[Foreground] +Color=242,242,242 + +[ForegroundFaint] +Color=168,168,168 + +[ForegroundIntense] +Color=255,255,255 + +[Color0] +Color=31,34,37 + +[Color0Faint] +Color=20,20,25 + +[Color0Intense] +Color=58,61,65 + +[Color1] +Color=218,68,83 + +[Color1Faint] +Color=160,50,60 + +[Color1Intense] +Color=235,100,110 + +[Color2] +Color=39,174,96 + +[Color2Faint] +Color=28,128,70 + +[Color2Intense] +Color=80,200,120 + +[Color3] +Color=246,116,0 + +[Color3Faint] +Color=180,85,0 + +[Color3Intense] +Color=255,160,50 + +[Color4] +Color=136,0,143 + +[Color4Faint] +Color=94,0,100 + +[Color4Intense] +Color=162,50,168 + +[Color5] +Color=162,50,168 + +[Color5Faint] +Color=110,30,115 + +[Color5Intense] +Color=200,100,210 + +[Color6] +Color=140,198,63 + +[Color6Faint] +Color=100,145,45 + +[Color6Intense] +Color=184,227,115 + +[Color7] +Color=168,168,168 + +[Color7Faint] +Color=110,113,115 + +[Color7Intense] +Color=242,242,242 diff --git a/assets/themes/KDE/konsole/Promethean.profile b/assets/themes/KDE/konsole/Promethean.profile new file mode 100644 index 0000000..a4b993e --- /dev/null +++ b/assets/themes/KDE/konsole/Promethean.profile @@ -0,0 +1,36 @@ +[Appearance] +ColorScheme=NexusOS-Promethean +Font=Monospace,12,-1,5,50,0,0,0,0,0 +LineSpacing=1 +UseFontLineCharacters=false + +[Cursor Options] +CursorShape=0 +CustomCursorColor=#b040c0 +UseCustomCursorColor=true + +[General] +Command=/bin/bash --rcfile /home/jon/nexus-core/.promethean_bashrc -i +Name=Promethean +Parent=FALLBACK/ +TerminalCenter=false +TerminalColumns=110 +TerminalRows=30 + +[Interaction Options] +AutoCopySelectedText=false +UnderlineFilesEnabled=false + +[Scrolling] +HistoryMode=2 +HistorySize=5000 +ScrollBarPosition=2 + +[Terminal Features] +BidiRenderingEnabled=true +BlinkingCursorEnabled=true +BlinkingTextEnabled=true +UrlHintsModifiers=0 + +[Window] +RememberWindowSize=true diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg b/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg new file mode 100644 index 0000000..395f6f9 --- /dev/null +++ b/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/assets/logo.png b/assets/themes/KDE/kscreenlocker/NexusOS/assets/logo.png new file mode 100644 index 0000000..c96bb05 Binary files /dev/null and b/assets/themes/KDE/kscreenlocker/NexusOS/assets/logo.png differ diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml b/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml new file mode 100644 index 0000000..6747792 --- /dev/null +++ b/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml b/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml new file mode 100644 index 0000000..7d130a5 --- /dev/null +++ b/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml @@ -0,0 +1,268 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import org.kde.plasma.core 2.0 as PlasmaCore + +/* + * NexusOS kscreenlocker theme. + * Mirrors the SDDM NexusOS-QML aesthetic: logo upper-center, + * clock/date bottom-center, centered password box. + * + * kscreenlocker API differences from SDDM: + * - authenticator.tryUnlock(password) instead of sddm.login(...) + * - authenticator.failed / authenticator.succeeded signals + * - No session selector, no power buttons + * - walletModel, userModel available but not mandatory + */ + +Rectangle { + id: root + + // kscreenlocker injects these from the surrounding PlasmaShell context + property bool locked: true + + readonly property color accentColor: "#8cc63f" // brand_green + readonly property color bgDark: "#1e1526" // base_bg + readonly property color textPrimary: "#f2f2f2" + readonly property color textMuted: "#a8a8a8" + readonly property color fieldBg: "#1f2225" // surface_bg + readonly property color fieldBorder: "#4d3461" // menu_border + readonly property color errorColor: "#da4453" + + anchors.fill: parent + color: bgDark + + // ── Background image (reuses SDDM background asset) ──────────────── + Image { + anchors.fill: parent + source: Qt.resolvedUrl("../../assets/background.svg") + fillMode: Image.PreserveAspectCrop + smooth: true + asynchronous: false + } + + Rectangle { + anchors.fill: parent + color: "#000000" + opacity: 0.30 + } + + // ── Logo area ─────────────────────────────────────────────────────── + Item { + id: logoArea + width: 100 + height: 100 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: parent.height * 0.10 + + Rectangle { + anchors.centerIn: parent + width: parent.width * 1.6 + height: parent.height * 1.6 + radius: width / 2 + color: accentColor + opacity: 0.06 + } + + Image { + anchors.fill: parent + source: Qt.resolvedUrl("../../assets/logo.png") + sourceSize: Qt.size(200, 200) + smooth: true + } + } + + Text { + text: "NexusOS" + color: textPrimary + font.family: "Sans" + font.pixelSize: 26 + font.letterSpacing: 6 + font.weight: Font.Light + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logoArea.bottom + anchors.topMargin: 14 + } + + // ── Password box ──────────────────────────────────────────────────── + Rectangle { + id: loginBox + width: 340 + height: loginColumn.height + 56 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: Qt.rgba(30/255, 21/255, 38/255, 0.80) + radius: 12 + border.color: fieldBorder + border.width: 1 + + SequentialAnimation { + id: shakeAnim + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 10; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 10; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 6; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 6; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x; duration: 40 } + } + + Column { + id: loginColumn + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 28 + spacing: 12 + + // Username label (read-only; kscreenlocker always locks current session) + Text { + width: parent.width + text: kscreenlocker_userName || userModel.data(userModel.index(0, 0), Qt.DisplayRole) || "" + color: textPrimary + font.pixelSize: 14 + font.weight: Font.Medium + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + } + + // Password field + Rectangle { + width: parent.width + height: 44 + color: fieldBg + radius: 6 + border.color: passwordInput.activeFocus ? accentColor : fieldBorder + border.width: 1 + + TextInput { + id: passwordInput + anchors.fill: parent + anchors.leftMargin: 14 + anchors.rightMargin: 14 + verticalAlignment: TextInput.AlignVCenter + color: textPrimary + font.pixelSize: 14 + echoMode: TextInput.Password + focus: true + clip: true + + Keys.onReturnPressed: authenticator.tryUnlock(passwordInput.text) + Keys.onEnterPressed: authenticator.tryUnlock(passwordInput.text) + } + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + text: "Password" + color: textMuted + font.pixelSize: 14 + visible: passwordInput.text.length === 0 && !passwordInput.activeFocus + } + } + + // Error message + Text { + id: errorMsg + width: parent.width + text: "" + color: errorColor + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + visible: text !== "" + } + + // Unlock button + Rectangle { + id: unlockButton + width: parent.width + height: 44 + radius: 6 + color: unlockMouse.pressed + ? Qt.darker(accentColor, 1.3) + : unlockMouse.containsMouse + ? Qt.lighter(accentColor, 1.15) + : accentColor + + Behavior on color { ColorAnimation { duration: 150 } } + + Text { + anchors.centerIn: parent + text: "UNLOCK" + color: "#0a0a00" // text_on_accent + font.pixelSize: 14 + font.letterSpacing: 3 + font.weight: Font.DemiBold + } + + MouseArea { + id: unlockMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: authenticator.tryUnlock(passwordInput.text) + } + } + } + } + + // ── Authenticator connections ─────────────────────────────────────── + Connections { + target: authenticator + + function onFailed() { + errorMsg.text = "Incorrect password — try again." + passwordInput.text = "" + passwordInput.forceActiveFocus() + shakeAnim.start() + } + + function onSucceeded() { + errorMsg.text = "" + } + + function onGraceLockedChanged() {} + function onMessage(msg) {} + function onError(err) { + errorMsg.text = err + } + } + + // ── Clock / date (bottom) ─────────────────────────────────────────── + Column { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: parent.height * 0.05 + spacing: 2 + + Text { + id: clockText + anchors.horizontalCenter: parent.horizontalCenter + color: textPrimary + font.family: "Sans" + font.pixelSize: 42 + font.weight: Font.Thin + } + + Text { + id: dateText + anchors.horizontalCenter: parent.horizontalCenter + color: textMuted + font.family: "Sans" + font.pixelSize: 14 + font.letterSpacing: 2 + } + + Timer { + interval: 1000 + running: true + repeat: true + triggeredOnStart: true + onTriggered: { + var d = new Date() + clockText.text = Qt.formatTime(d, "hh:mm") + dateText.text = Qt.formatDate(d, "dddd, MMMM d") + } + } + } +} diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/metadata.desktop b/assets/themes/KDE/kscreenlocker/NexusOS/metadata.desktop new file mode 100644 index 0000000..1109447 --- /dev/null +++ b/assets/themes/KDE/kscreenlocker/NexusOS/metadata.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=NexusOS +Comment=NexusOS lock screen — dark purple, lime-green accents +Type=Service +X-KDE-ServiceTypes=org.kde.kscreenlocker.Greeter + +[ScreenLocker] +Name=NexusOS +Description=NexusOS lock screen — dark purple, lime-green accents +MainScript=contents/ui/LockScreenUi.qml diff --git a/assets/themes/KDE/kvantum/NexusOS/NexusOS.kvconfig b/assets/themes/KDE/kvantum/NexusOS/NexusOS.kvconfig new file mode 100644 index 0000000..8f44e00 --- /dev/null +++ b/assets/themes/KDE/kvantum/NexusOS/NexusOS.kvconfig @@ -0,0 +1,397 @@ +[%GradualConfiguration] +# Toolbar / menubar gradient — flat: both ends same color +left.header.color=#2a2e32 +right.header.color=#2a2e32 +header.text.color=#f2f2f2 +header.text.shadow.color=0,0,0,0 +left.dock.header.color=#1e1526 +right.dock.header.color=#1e1526 +dock.header.text.color=#f2f2f2 +dock.header.text.shadow.color=0,0,0,0 + +[%ThemeHacks] +transparent.menus=false +transparent.tooltips=false +blurring=false +composite=false + +[General] +author=NexusOS +comment=Flat dark purple theme, lime-green accents, purple selections +x11drag=all +double.click.exception= +group.label.boldFont=false +menubar.mouse.tracking=true +toolbutton.style=0 +dialog.button.layout=0 +splitter.width=4 +scroll.arrows=false +scroll.min.extent=36 +tooltip.delay=500 +tooltip.count=1 +vertical.center.dialogs=true +inline.spin.indicators=true +transient.scrollbar=false +submenu.overlap=3 +submenu.delay=250 +shadow.spread=0 +shadow.x.offset=0 +shadow.y.offset=0 +focus.rect.color=#8cc63f +reduce.window.opacity=0 +reduce.menu.opacity=0 +small.icon.size=16 +large.icon.size=32 +button.icon.size=16 +toolbar.icon.size=16 + +[PanelButtonCommand] +inherits=PanelButtonTool +frame=false +interior=true +interior.element=button +interior.x.padding=8 +interior.y.padding=4 +indicator.size=0 +text.shadow=0 +text.margin=1 +text.bold=false +min.height=26 +min.width=60 + +[PanelButtonTool] +frame=false +interior=true +interior.element=toolbutton +interior.x.padding=6 +interior.y.padding=3 +indicator.size=0 +text.shadow=0 +text.margin=0 +text.bold=false +min.height=22 +min.width=0 + +[ToolbarButton] +inherits=PanelButtonTool + +[Dock] +frame=false +interior=true +interior.element=dock + +[DockTitle] +frame=false +interior=false +indicator.element=arrow +text.margin=3 +text.shadow=0 + +[TitleBar] +frame=false +interior=true +interior.element=titlebar +indicator.element=button +min.height=30 +text.margin=4 +text.shadow=0 +text.bold=false + +[GroupBox] +frame=true +frame.element=groupbox +frame.top=6 +frame.bottom=2 +frame.left=2 +frame.right=2 +interior=false +text.margin=4 +text.shadow=0 +text.bold=false +text.italic=false + +[LineEdit] +frame=false +interior=true +interior.element=lineedit +interior.x.padding=8 +interior.y.padding=4 +min.height=26 +min.width=0 + +[DropDownButton] +frame=false +interior=true +interior.element=button +indicator.element=arrow-down +indicator.size=10 + +[ToolboxTab] +frame=false +interior=true +interior.element=tab +text.margin=4 +text.shadow=0 +text.bold=false + +[Tab] +frame=false +interior=true +interior.element=tab +interior.x.padding=10 +interior.y.padding=4 +indicator.element=tab-close +indicator.size=12 +text.shadow=0 +text.margin=2 +min.height=26 +min.width=60 + +[TabFrame] +frame=false +interior=true +interior.element=tabframe +frame.top=0 +frame.bottom=0 +frame.left=0 +frame.right=0 + +[TreeExpander] +frame=false +interior=false +indicator.element=arrow +indicator.size=10 + +[HeaderSection] +frame=false +interior=true +interior.element=header +interior.x.padding=6 +interior.y.padding=3 +indicator.element=arrow +indicator.size=10 +text.shadow=0 +min.height=24 + +[SizeGrip] +frame=false +interior=false +indicator.element=sizegrip +indicator.size=14 + +[Splitter] +frame=false +interior=true +interior.element=splitter +min.height=4 +min.width=4 + +[Slider] +frame=false +interior=true +interior.element=slider +indicator.element=slider-handle +indicator.size=16 +min.height=4 +min.width=4 + +[SliderCursorFlat] +frame=false +interior=true +interior.element=slider-handle +min.height=16 +min.width=16 + +[ProgressbarContents] +frame=false +interior=true +interior.element=progressbar-fill + +[Progressbar] +frame=false +interior=true +interior.element=progressbar +indicator.size=0 +text.shadow=0 +min.height=6 +min.width=0 + +[ItemView] +frame=false +interior=true +interior.element=itemview +interior.x.padding=4 +interior.y.padding=2 + +[Toolbar] +frame=false +interior=true +interior.element=toolbar +interior.x.padding=2 +interior.y.padding=2 +indicator.element=toolbar-handle +indicator.size=8 + +[ScrollbarSlider] +frame=false +interior=true +interior.element=scrollbar-slider +min.height=36 +min.width=8 + +[ScrollbarGroove] +frame=false +interior=true +interior.element=scrollbar-groove + +[Scrollbar] +frame=false +interior=true +interior.element=scrollbar-groove +indicator.element=arrow +indicator.size=0 +min.height=8 +min.width=8 + +[ScrollArrow] +frame=false +interior=false +indicator.element=arrow +indicator.size=8 +min.height=0 +min.width=0 + +[CheckBox] +frame=false +interior=true +interior.element=checkbox +indicator.element=checkbox-indicator +indicator.size=14 +text.shadow=0 +text.margin=3 +min.height=16 +min.width=16 + +[RadioButton] +frame=false +interior=true +interior.element=radiobutton +indicator.element=radiobutton-indicator +indicator.size=8 +text.shadow=0 +text.margin=3 +min.height=16 +min.width=16 + +[Separator] +frame=false +interior=true +interior.element=separator +min.height=1 +min.width=1 + +[ComboBox] +frame=false +interior=true +interior.element=combobox +interior.x.padding=8 +interior.y.padding=4 +indicator.element=arrow-down +indicator.size=10 +min.height=26 +min.width=0 + +[SpinBox] +frame=false +interior=true +interior.element=spinbox +interior.x.padding=6 +interior.y.padding=3 +indicator.element=arrow +indicator.size=10 +min.height=26 +min.width=0 + +[Frame] +frame=true +frame.element=frame +frame.top=1 +frame.bottom=1 +frame.left=1 +frame.right=1 +interior=false + +[MenuItem] +frame=false +interior=true +interior.element=menuitem +interior.x.padding=6 +interior.y.padding=1 +indicator.element=menuitem-arrow +indicator.size=10 +text.shadow=0 +min.height=22 + +[Menu] +frame=false +interior=true +interior.element=menu +interior.x.padding=0 +interior.y.padding=2 +text.shadow=0 +min.height=0 +min.width=0 + +[Menubar] +frame=false +interior=true +interior.element=menubar +text.shadow=0 + +[MenubarItem] +frame=false +interior=true +interior.element=menubaritem +interior.x.padding=10 +interior.y.padding=4 +text.shadow=0 + +[ToolTip] +frame=false +interior=true +interior.element=tooltip +interior.x.padding=8 +interior.y.padding=4 +text.shadow=0 +min.height=0 +min.width=0 + +[StatusBar] +frame=false +interior=true +interior.element=statusbar + +[Window] +frame=false +interior=true +interior.element=window +interior.x.padding=0 +interior.y.padding=0 + +[Dialog] +frame=false +interior=true +interior.element=window + +[MessageBox] +inherits=Dialog +text.bold=false + +[GenericFrame] +frame=true +frame.element=frame +frame.top=1 +frame.bottom=1 +frame.left=1 +frame.right=1 +interior=false diff --git a/assets/themes/KDE/kvantum/NexusOS/NexusOS.svg b/assets/themes/KDE/kvantum/NexusOS/NexusOS.svg new file mode 100644 index 0000000..0c7a5d2 --- /dev/null +++ b/assets/themes/KDE/kvantum/NexusOS/NexusOS.svg @@ -0,0 +1,619 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/NexusOS.colors b/assets/themes/KDE/plasma/NexusOS/NexusOS.colors new file mode 100644 index 0000000..1d7b77c --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/NexusOS.colors @@ -0,0 +1,118 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.1 +ContrastEffect=2 +IntensityAmount=0 +IntensityEffect=0 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.025 +ColorEffect=0 +ContrastAmount=0.1 +ContrastEffect=2 +Enable=false +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Window] +BackgroundNormal=30,21,38 +BackgroundAlternate=31,34,37 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:View] +BackgroundNormal=30,21,38 +BackgroundAlternate=31,34,37 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:Button] +BackgroundNormal=42,46,50 +BackgroundAlternate=30,21,38 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:Selection] +BackgroundNormal=136,0,143 +BackgroundAlternate=107,166,42 +ForegroundNormal=255,255,255 +ForegroundInactive=255,255,255 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:Tooltip] +BackgroundNormal=46,50,54 +BackgroundAlternate=58,61,65 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:Complementary] +BackgroundNormal=42,46,50 +BackgroundAlternate=46,50,54 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[Colors:Header] +BackgroundNormal=30,21,38 +BackgroundAlternate=42,46,50 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +ForegroundLink=184,227,115 +ForegroundVisited=162,50,168 +ForegroundNegative=218,68,83 +ForegroundNeutral=246,116,0 +ForegroundPositive=39,174,96 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 +[General] +ColorScheme=NexusOS +Name=NexusOS +shadeSortColumn=true + +[KDE] +contrast=4 diff --git a/assets/themes/KDE/plasma/NexusOS/colors b/assets/themes/KDE/plasma/NexusOS/colors new file mode 100644 index 0000000..f7ff83a --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/colors @@ -0,0 +1,53 @@ +[Colors:Window] +BackgroundNormal=30,21,38 +BackgroundAlternate=31,34,37 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 + +[Colors:Button] +BackgroundNormal=42,46,50 +BackgroundAlternate=30,21,38 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 + +[Colors:View] +BackgroundNormal=30,21,38 +BackgroundAlternate=31,34,37 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 + +[Colors:Selection] +BackgroundNormal=136,0,143 +BackgroundAlternate=107,166,42 +ForegroundNormal=255,255,255 +ForegroundInactive=255,255,255 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 + +[Colors:Tooltip] +BackgroundNormal=46,50,54 +BackgroundAlternate=58,61,65 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 + +[Colors:Complementary] +BackgroundNormal=42,46,50 +BackgroundAlternate=46,50,54 +ForegroundNormal=242,242,242 +ForegroundInactive=168,168,168 +ForegroundActive=140,198,63 +DecorationFocus=140,198,63 +DecorationHover=136,0,143 diff --git a/assets/themes/KDE/plasma/NexusOS/metadata.desktop b/assets/themes/KDE/plasma/NexusOS/metadata.desktop new file mode 100644 index 0000000..e9d00b8 --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/metadata.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Name=NexusOS +Comment=Dark purple Plasma shell theme, lime-green accents +Type=Service + +[KPackageStructure] +X-KDE-ServiceTypes=Plasma/Theme + +X-KDE-PluginInfo-Author=Jon +X-KDE-PluginInfo-Email= +X-KDE-PluginInfo-Name=NexusOS +X-KDE-PluginInfo-Version=1.0 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false diff --git a/assets/themes/KDE/plasma/NexusOS/opaque/widgets/panel-background.svg b/assets/themes/KDE/plasma/NexusOS/opaque/widgets/panel-background.svg new file mode 100644 index 0000000..2c360c5 --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/opaque/widgets/panel-background.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/widgets/background.svg b/assets/themes/KDE/plasma/NexusOS/widgets/background.svg new file mode 100644 index 0000000..04dbe18 --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/widgets/background.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/widgets/button.svg b/assets/themes/KDE/plasma/NexusOS/widgets/button.svg new file mode 100644 index 0000000..af5f63b --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/widgets/button.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/widgets/panel-background.svg b/assets/themes/KDE/plasma/NexusOS/widgets/panel-background.svg new file mode 100644 index 0000000..c48eb7b --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/widgets/panel-background.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/widgets/plasmoidheading.svg b/assets/themes/KDE/plasma/NexusOS/widgets/plasmoidheading.svg new file mode 100644 index 0000000..28a3f8e --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/widgets/plasmoidheading.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/plasma/NexusOS/widgets/tooltip.svg b/assets/themes/KDE/plasma/NexusOS/widgets/tooltip.svg new file mode 100644 index 0000000..7e925b4 --- /dev/null +++ b/assets/themes/KDE/plasma/NexusOS/widgets/tooltip.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/sddm/NexusOS-QML/Main.qml b/assets/themes/KDE/sddm/NexusOS-QML/Main.qml new file mode 100644 index 0000000..e8b5ed7 --- /dev/null +++ b/assets/themes/KDE/sddm/NexusOS-QML/Main.qml @@ -0,0 +1,397 @@ +import QtQuick 2.0 +import SddmComponents 2.0 + +Rectangle { + id: root + width: 1920 + height: 1200 + + readonly property color accentColor: "#8cc63f" // brand_green + readonly property color bgDark: "#1e1526" // base_bg + readonly property color textPrimary: "#f2f2f2" // text_primary + readonly property color textMuted: "#a8a8a8" // text_secondary + readonly property color fieldBg: "#1f2225" // surface_bg + readonly property color fieldBorder: "#4d3461" // menu_border (purple tint) + readonly property color errorColor: "#da4453" // error_color + + property int sessionIndex: sessionModel.lastIndex + + Connections { + target: sddm + onLoginFailed: { + errorMsg.text = "Login failed — check your credentials." + passwordInput.text = "" + passwordInput.focus = true + shakeAnim.start() + } + onLoginSucceeded: { + errorMsg.text = "" + } + } + + Image { + anchors.fill: parent + source: "assets/background.svg" + fillMode: Image.PreserveAspectCrop + smooth: true + asynchronous: false + } + + Rectangle { + anchors.fill: parent + color: "#000000" + opacity: 0.25 + } + + Item { + id: logoArea + width: 120 + height: 120 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: parent.height * 0.10 + + Rectangle { + anchors.centerIn: parent + width: parent.width * 1.6 + height: parent.height * 1.6 + radius: width / 2 + color: accentColor + opacity: 0.06 + } + + Image { + id: logo + anchors.fill: parent + source: "assets/logo.png" + sourceSize: Qt.size(200, 200) + smooth: true + } + } + + Text { + id: brandLabel + text: "NexusOS" + color: textPrimary + font.family: "Sans" + font.pixelSize: 28 + font.letterSpacing: 6 + font.weight: Font.Light + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logoArea.bottom + anchors.topMargin: 16 + } + + Rectangle { + id: loginBox + width: 360 + height: loginColumn.height + 60 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: brandLabel.bottom + anchors.topMargin: parent.height * 0.08 + color: Qt.rgba(15/255, 22/255, 38/255, 0.75) + radius: 12 + border.color: fieldBorder + border.width: 1 + + SequentialAnimation { + id: shakeAnim + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 10; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 10; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x - 6; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x + 6; duration: 50 } + NumberAnimation { target: loginBox; property: "x"; to: loginBox.x; duration: 40 } + } + + Column { + id: loginColumn + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 30 + spacing: 14 + + Rectangle { + id: sessionBox + width: parent.width + height: 40 + color: fieldBg + radius: 6 + border.color: sessionDropdown.visible ? accentColor : fieldBorder + border.width: 1 + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + anchors.right: sessionArrow.left + anchors.rightMargin: 8 + text: sessionModel.data(sessionModel.index(sessionIndex, 0), Qt.DisplayRole) || "Session" + color: textPrimary + font.pixelSize: 13 + elide: Text.ElideRight + } + + Text { + id: sessionArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: 14 + text: sessionDropdown.visible ? "▲" : "▼" + color: textMuted + font.pixelSize: 10 + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (!sessionDropdown.visible) { + var pos = sessionBox.mapToItem(root, 0, sessionBox.height) + sessionDropdown.x = pos.x + sessionDropdown.y = pos.y + 2 + sessionDropdown.width = sessionBox.width + } + sessionDropdown.visible = !sessionDropdown.visible + } + } + } + + Rectangle { + width: parent.width + height: 44 + color: fieldBg + radius: 6 + border.color: usernameInput.activeFocus ? accentColor : fieldBorder + border.width: 1 + + TextInput { + id: usernameInput + anchors.fill: parent + anchors.leftMargin: 14 + anchors.rightMargin: 14 + verticalAlignment: TextInput.AlignVCenter + color: textPrimary + font.pixelSize: 14 + clip: true + text: userModel.lastUser + + Keys.onTabPressed: passwordInput.forceActiveFocus() + Keys.onReturnPressed: sddm.login(usernameInput.text, passwordInput.text, sessionIndex) + } + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + text: "Username" + color: textMuted + font.pixelSize: 14 + visible: usernameInput.text.length === 0 && !usernameInput.activeFocus + } + } + + Rectangle { + width: parent.width + height: 44 + color: fieldBg + radius: 6 + border.color: passwordInput.activeFocus ? accentColor : fieldBorder + border.width: 1 + + TextInput { + id: passwordInput + anchors.fill: parent + anchors.leftMargin: 14 + anchors.rightMargin: 14 + verticalAlignment: TextInput.AlignVCenter + color: textPrimary + font.pixelSize: 14 + echoMode: TextInput.Password + clip: true + focus: true + + Keys.onReturnPressed: sddm.login(usernameInput.text, passwordInput.text, sessionIndex) + } + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + text: "Password" + color: textMuted + font.pixelSize: 14 + visible: passwordInput.text.length === 0 && !passwordInput.activeFocus + } + } + + Text { + id: errorMsg + width: parent.width + text: "" + color: errorColor + font.pixelSize: 12 + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + visible: text !== "" + } + + Rectangle { + id: loginButton + width: parent.width + height: 44 + radius: 6 + color: loginMouse.pressed + ? Qt.darker(accentColor, 1.3) + : loginMouse.containsMouse + ? Qt.lighter(accentColor, 1.15) + : accentColor + + Behavior on color { ColorAnimation { duration: 150 } } + + Text { + anchors.centerIn: parent + text: "LOGIN" + color: "#0a0a00" // text_on_accent (dark on lime green) + font.pixelSize: 14 + font.letterSpacing: 3 + font.weight: Font.DemiBold + } + + MouseArea { + id: loginMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: sddm.login(usernameInput.text, passwordInput.text, sessionIndex) + } + } + + Item { + width: parent.width + height: 30 + + Row { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: 30 + + Text { + text: "⏻ Shutdown" + color: textMuted + font.pixelSize: 12 + opacity: shutdownMouse.containsMouse ? 1.0 : 0.7 + Behavior on opacity { NumberAnimation { duration: 120 } } + + MouseArea { + id: shutdownMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: sddm.powerOff() + } + } + + Text { + text: "↻ Reboot" + color: textMuted + font.pixelSize: 12 + opacity: rebootMouse.containsMouse ? 1.0 : 0.7 + Behavior on opacity { NumberAnimation { duration: 120 } } + + MouseArea { + id: rebootMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: sddm.reboot() + } + } + } + } + } + } + + Rectangle { + id: sessionDropdown + visible: false + z: 999 + color: fieldBg + radius: 6 + border.color: fieldBorder + border.width: 1 + height: sessionList.contentHeight + clip: true + + ListView { + id: sessionList + anchors.fill: parent + model: sessionModel + interactive: false + + delegate: Rectangle { + width: sessionList.width + height: 36 + color: sessionItemMouse.containsMouse ? Qt.lighter(fieldBg, 1.4) : "transparent" + radius: 4 + + Text { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + text: model.name || "" + color: index === sessionIndex ? accentColor : textPrimary + font.pixelSize: 13 + } + + MouseArea { + id: sessionItemMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + sessionIndex = index + sessionDropdown.visible = false + } + } + } + } + } + + Column { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: parent.height * 0.05 + spacing: 2 + + Text { + id: clockText + anchors.horizontalCenter: parent.horizontalCenter + color: textPrimary + font.family: "Sans" + font.pixelSize: 42 + font.weight: Font.Thin + } + + Text { + id: dateText + anchors.horizontalCenter: parent.horizontalCenter + color: textMuted + font.family: "Sans" + font.pixelSize: 14 + font.letterSpacing: 2 + } + + Timer { + interval: 1000 + running: true + repeat: true + triggeredOnStart: true + onTriggered: { + var d = new Date() + clockText.text = Qt.formatTime(d, "hh:mm") + dateText.text = Qt.formatDate(d, "dddd, MMMM d") + } + } + } +} diff --git a/assets/themes/KDE/sddm/NexusOS-QML/assets/background.svg b/assets/themes/KDE/sddm/NexusOS-QML/assets/background.svg new file mode 100644 index 0000000..395f6f9 --- /dev/null +++ b/assets/themes/KDE/sddm/NexusOS-QML/assets/background.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/KDE/sddm/NexusOS-QML/assets/logo.png b/assets/themes/KDE/sddm/NexusOS-QML/assets/logo.png new file mode 100644 index 0000000..c96bb05 Binary files /dev/null and b/assets/themes/KDE/sddm/NexusOS-QML/assets/logo.png differ diff --git a/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop b/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop new file mode 100644 index 0000000..f4aa5a1 --- /dev/null +++ b/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop @@ -0,0 +1,10 @@ +[SddmGreeterTheme] +Name=NexusOS-QML +Description=Dark brushed-metal lockscreen theme with flat N logo, upper-center branding, and centered login box. +Author=Jon +Version=1.0 +Website= +Screenshot= +MainScript=Main.qml +ConfigFile=theme.conf +TranslationsDirectory= diff --git a/assets/themes/KDE/sddm/NexusOS-QML/theme.conf b/assets/themes/KDE/sddm/NexusOS-QML/theme.conf new file mode 100644 index 0000000..115bc41 --- /dev/null +++ b/assets/themes/KDE/sddm/NexusOS-QML/theme.conf @@ -0,0 +1,16 @@ +[General] +# ── NexusOS-QML Theme Configuration ── + +background=assets/background.svg +logo=assets/logo.png + +# Color palette +accentColor=#8cc63f +bgDark=#1e1526 +textPrimary=#f2f2f2 +textMuted=#a8a8a8 +fieldBg=#1f2225 +fieldBorder=#4d3461 +errorColor=#da4453 + +type=color diff --git a/assets/themes/LICENSE b/assets/themes/LICENSE new file mode 100644 index 0000000..37441ab --- /dev/null +++ b/assets/themes/LICENSE @@ -0,0 +1,23 @@ +NexusOS theme assets — attribution and license +================================================ + +The NexusOS GTK2 and xfwm4 themes are derived from Mint-Y-Dark-Aqua, +part of the mint-themes package shipped by Linux Mint. + +Upstream: + https://github.com/linuxmint/mint-themes + Copyright (c) 2017-2024 Linux Mint + Licensed under the GNU General Public License v3.0 or later. + +Derivative work in this directory: + - NexusOS/gtk-2.0/ (PNGs recolored from Mint-Y-Dark-Aqua/gtk-2.0/) + - NexusOS/xfwm4/ (PNGs recolored from Mint-Y-Dark-Aqua/xfwm4/) + - NexusOS-gtk2-src/ (build script that performs the recolor) + - NexusOS-xfwm4-src/ (build script that performs the recolor) + +As a derivative of GPLv3 material, these files are likewise distributed +under the terms of the GNU General Public License version 3 or later. See +https://www.gnu.org/licenses/gpl-3.0.html for the full license text. + +The NexusOS GTK3 theme (NexusOS/gtk-3.0/) and the NexusOS icon set +(NexusOS-icons*/) are original works and not derived from Mint-Y. diff --git a/assets/themes/NexusOS-gtk2-src/build.py b/assets/themes/NexusOS-gtk2-src/build.py new file mode 100644 index 0000000..e1e9673 --- /dev/null +++ b/assets/themes/NexusOS-gtk2-src/build.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +"""Build NexusOS GTK2 theme from Mint-Y-Dark-Aqua base. + +- Copies .rc files + assets/ from /usr/share/themes/Mint-Y-Dark-Aqua/gtk-2.0/ +- Recolors PNGs containing the Aqua accent (#1f9ede) to NexusOS green (#8cc63f) +- Rewrites the gtk-color-scheme block in gtkrc to NexusOS palette +- Replaces remaining aqua hex literals in .rc files +""" +from __future__ import annotations + +import colorsys +import shutil +import sys +from pathlib import Path + +THEMES_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(THEMES_ROOT)) +import _palette as P # noqa: E402 + +from PIL import Image # noqa: E402 + +SRC = Path("/usr/share/themes/Mint-Y-Dark-Aqua/gtk-2.0") +OUT = THEMES_ROOT / "NexusOS" / "gtk-2.0" + +AQUA_RGB = (0x1F, 0x9E, 0xDE) +AQUA_HSV = colorsys.rgb_to_hsv(*(c / 255 for c in AQUA_RGB)) +GREEN_HSV = P.hex_to_hsv(P.BRAND_GREEN) + +HUE_TOLERANCE = 30 / 360 +SAT_FLOOR = 0.18 + +S_SCALE = GREEN_HSV[1] / AQUA_HSV[1] +V_SCALE = GREEN_HSV[2] / AQUA_HSV[2] + + +def hue_distance(a: float, b: float) -> float: + d = abs(a - b) + return min(d, 1 - d) + + +def recolor_pixel(r: int, g: int, b: int, a: int) -> tuple[int, int, int, int]: + if a < 8: + return (r, g, b, a) + h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255) + if s < SAT_FLOOR or hue_distance(h, AQUA_HSV[0]) > HUE_TOLERANCE: + return (r, g, b, a) + new_s = min(1.0, s * S_SCALE) + new_v = min(1.0, v * V_SCALE) + nr, ng, nb = colorsys.hsv_to_rgb(GREEN_HSV[0], new_s, new_v) + return (round(nr * 255), round(ng * 255), round(nb * 255), a) + + +def recolor_png(path: Path) -> int: + """Recolor aqua pixels in-place. Returns count of pixels changed.""" + im = Image.open(path).convert("RGBA") + px = im.load() + w, h = im.size + changed = 0 + for y in range(h): + for x in range(w): + r, g, b, a = px[x, y] + new = recolor_pixel(r, g, b, a) + if new != (r, g, b, a): + px[x, y] = new + changed += 1 + if changed: + im.save(path, optimize=True) + return changed + + +# ── gtkrc color-scheme block ────────────────────────────────────────── +NEW_COLOR_SCHEME = ( + f"base_color:#{P.BASE_BG}\\n" + f"fg_color:#{P.TEXT_PRIMARY}\\n" + f"tooltip_fg_color:#{P.TEXT_PRIMARY}\\n" + f"selected_bg_color:#{P.BRAND_PURPLE_DARK}\\n" + f"selected_fg_color:#{P.TEXT_ON_SELECTION}\\n" + f"text_color:#{P.TEXT_PRIMARY}\\n" + f"bg_color:#{P.SURFACE_BG}\\n" + f"insensitive_bg_color:#{P.SURFACE_BG}\\n" + f"insensitive_fg_color:#{P.TEXT_DISABLED}\\n" + f"notebook_bg:#{P.BASE_BG}\\n" + f"dark_sidebar_bg:#{P.SURFACE_BG_ALT}\\n" + f"tooltip_bg_color:#{P.OVERLAY_BG}\\n" + f"link_color:#{P.BRAND_GREEN_LIGHT}\\n" + f"menu_bg:#{P.SURFACE_BG}\\n" + f"menu_separator_color:#{P.BORDER}" +) + + +def patch_gtkrc(path: Path) -> None: + text = path.read_text() + out_lines = [] + for line in text.splitlines(): + if line.startswith("gtk-color-scheme"): + out_lines.append(f'gtk-color-scheme = "{NEW_COLOR_SCHEME}"') + else: + out_lines.append(line) + path.write_text("\n".join(out_lines) + "\n") + + +# ── Remaining hex literals in .rc files ─────────────────────────────── +# Captured from Mint-Y-Dark-Aqua; map source hex → NexusOS replacement. +RC_HEX_REPLACEMENTS = { + # accent / focus / selection + "#1f9ede": f"#{P.BRAND_GREEN}", + "#1F9EDE": f"#{P.BRAND_GREEN}", + "#5294E2": f"#{P.BRAND_GREEN_LIGHT}", # link_color fallback if quoted literal + "#5294e2": f"#{P.BRAND_GREEN_LIGHT}", + # Mint-Y greys we want to nudge onto our surface family + "#2b2b2b": f"#{P.SURFACE_BG_ALT}", + "#383838": f"#{P.SURFACE_BG}", + "#404040": f"#{P.BASE_BG}", + "#3e3e3e": f"#{P.SURFACE_BG}", + "#dadada": f"#{P.TEXT_PRIMARY}", + "#DADADA": f"#{P.TEXT_PRIMARY}", + "#d3d3d3": f"#{P.TEXT_PRIMARY}", + "#D3D3D3": f"#{P.TEXT_PRIMARY}", +} + + +def patch_rc(path: Path) -> int: + text = path.read_text() + count = 0 + for src, dst in RC_HEX_REPLACEMENTS.items(): + n = text.count(src) + if n: + text = text.replace(src, dst) + count += n + path.write_text(text) + return count + + +def main() -> None: + if OUT.exists(): + shutil.rmtree(OUT) + shutil.copytree(SRC, OUT) + print(f"Copied {SRC} → {OUT}") + + # Recolor PNGs + recolored = 0 + for png in sorted((OUT / "assets").glob("*.png")): + changed = recolor_png(png) + if changed: + recolored += 1 + print(f" recolored {png.name}: {changed}px") + print(f"Recolored {recolored} PNGs") + + # Patch rc files + patched_total = 0 + for rc in sorted(OUT.glob("*.rc")): + n = patch_rc(rc) + if n: + print(f" patched {rc.name}: {n} replacements") + patched_total += n + gtkrc = OUT / "gtkrc" + patch_rc(gtkrc) + patch_gtkrc(gtkrc) + print(f"Rewrote gtkrc color scheme; total .rc hex replacements: {patched_total}") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/128.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/128.png new file mode 100644 index 0000000..fa71a0a Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/128.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/16.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/16.png new file mode 100644 index 0000000..d52512e Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/16.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/22.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/22.png new file mode 100644 index 0000000..ef88fa0 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/22.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/24.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/24.png new file mode 100644 index 0000000..981d898 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/24.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/32.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/32.png new file mode 100644 index 0000000..6a54713 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/32.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/48.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/48.png new file mode 100644 index 0000000..a0787a1 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/48.png differ diff --git a/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/64.png b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/64.png new file mode 100644 index 0000000..4338770 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_backup/folder-nexus-core-original/64.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-128.png new file mode 100644 index 0000000..f4e42e3 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-documents-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-documents-128.png new file mode 100644 index 0000000..d2ffb23 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-documents-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-download-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-download-128.png new file mode 100644 index 0000000..575e7c1 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-download-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-drag-accept-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-drag-accept-128.png new file mode 100644 index 0000000..95f8dea Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-drag-accept-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-home-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-home-128.png new file mode 100644 index 0000000..d1110fb Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-home-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-music-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-music-128.png new file mode 100644 index 0000000..da004e7 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-music-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-nexus-core-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-nexus-core-128.png new file mode 100644 index 0000000..1fba482 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-nexus-core-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-open-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-open-128.png new file mode 100644 index 0000000..a007282 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-open-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-pictures-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-pictures-128.png new file mode 100644 index 0000000..77941cc Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-pictures-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-publicshare-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-publicshare-128.png new file mode 100644 index 0000000..9f37ef0 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-publicshare-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-recent-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-recent-128.png new file mode 100644 index 0000000..0f18ae2 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-recent-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-remote-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-remote-128.png new file mode 100644 index 0000000..a7bbefa Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-remote-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-saved-search-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-saved-search-128.png new file mode 100644 index 0000000..b0f6de5 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-saved-search-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-templates-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-templates-128.png new file mode 100644 index 0000000..9ef2876 Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-templates-128.png differ diff --git a/assets/themes/NexusOS-icons-src/_previews/folder-videos-128.png b/assets/themes/NexusOS-icons-src/_previews/folder-videos-128.png new file mode 100644 index 0000000..643d96b Binary files /dev/null and b/assets/themes/NexusOS-icons-src/_previews/folder-videos-128.png differ diff --git a/assets/themes/NexusOS-icons-src/actions/applications-development.svg b/assets/themes/NexusOS-icons-src/actions/applications-development.svg new file mode 100644 index 0000000..880eee0 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-development.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-games.svg b/assets/themes/NexusOS-icons-src/actions/applications-games.svg new file mode 100644 index 0000000..c676a9c --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-games.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-graphics.svg b/assets/themes/NexusOS-icons-src/actions/applications-graphics.svg new file mode 100644 index 0000000..4218480 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-graphics.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-internet.svg b/assets/themes/NexusOS-icons-src/actions/applications-internet.svg new file mode 100644 index 0000000..7fa26a6 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-internet.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-multimedia.svg b/assets/themes/NexusOS-icons-src/actions/applications-multimedia.svg new file mode 100644 index 0000000..98ecd0d --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-multimedia.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-office.svg b/assets/themes/NexusOS-icons-src/actions/applications-office.svg new file mode 100644 index 0000000..56f157e --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-office.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-science.svg b/assets/themes/NexusOS-icons-src/actions/applications-science.svg new file mode 100644 index 0000000..d89eb36 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-science.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-system.svg b/assets/themes/NexusOS-icons-src/actions/applications-system.svg new file mode 100644 index 0000000..60125e4 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-system.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/applications-utilities.svg b/assets/themes/NexusOS-icons-src/actions/applications-utilities.svg new file mode 100644 index 0000000..02e9ebc --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/applications-utilities.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/preferences-desktop.svg b/assets/themes/NexusOS-icons-src/actions/preferences-desktop.svg new file mode 100644 index 0000000..d7d0503 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/preferences-desktop.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-hibernate.svg b/assets/themes/NexusOS-icons-src/actions/system-hibernate.svg new file mode 100644 index 0000000..aaa5b4a --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-hibernate.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-lock-screen.svg b/assets/themes/NexusOS-icons-src/actions/system-lock-screen.svg new file mode 100644 index 0000000..6aa1edc --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-lock-screen.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-log-out.svg b/assets/themes/NexusOS-icons-src/actions/system-log-out.svg new file mode 100644 index 0000000..6796f44 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-log-out.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-reboot.svg b/assets/themes/NexusOS-icons-src/actions/system-reboot.svg new file mode 100644 index 0000000..9fee993 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-reboot.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-shutdown.svg b/assets/themes/NexusOS-icons-src/actions/system-shutdown.svg new file mode 100644 index 0000000..0e5bc8f --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-shutdown.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-suspend.svg b/assets/themes/NexusOS-icons-src/actions/system-suspend.svg new file mode 100644 index 0000000..ea8ae40 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-suspend.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/assets/themes/NexusOS-icons-src/actions/system-switch-user.svg b/assets/themes/NexusOS-icons-src/actions/system-switch-user.svg new file mode 100644 index 0000000..c161ff5 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/actions/system-switch-user.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/build.py b/assets/themes/NexusOS-icons-src/build.py new file mode 100644 index 0000000..8699ddb --- /dev/null +++ b/assets/themes/NexusOS-icons-src/build.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python3 +""" +NexusOS folder icon builder. + +Emits one SVG per variant into places/, then rasterizes to +~/.icons/NexusOS//places/.png at 16/22/24/32/48/64/128. + +Run: python3 build.py # builds SVGs + renders all PNGs + python3 build.py --svg-only # just rewrite SVGs +""" + +from pathlib import Path +import base64 +import shutil +import subprocess +import sys + +ROOT = Path(__file__).resolve().parent +PLACES = ROOT / "places" +ICONS_OUT = Path.home() / ".icons" / "NexusOS" +SIZES = [16, 22, 24, 32, 48, 64, 128] + +# N logo source. Inkscape's headless renderer doesn't follow external href +# references, so we inline it as a base64 data URI. +LOGO_PATH = Path.home() / "nexus-core" / "assets" / "n-small.png" +LOGO_DATA_URI = ( + "data:image/png;base64," + + base64.b64encode(LOGO_PATH.read_bytes()).decode("ascii") +) + +# ── master folder geometry ──────────────────────────────────────────── +# viewBox 0 0 128 128. Badge area: roughly (32,52)-(96,108). +MASTER = """\ + + + + + + + + +""" + +# Open-folder geometry used by folder-open and folder-drag-accept. +# The front face is angled forward, exposing the back as a triangle on top. +OPEN_MASTER = """\ + + + + + + + + +""" + +# ── badge fragments ─────────────────────────────────────────────────── +# All badges drawn in white (#f2f2f2) and centered around (64, 78). +# Keep glyphs in roughly a 40-50px box for legibility at 22-32 sizes. + +BADGES = { + "folder": "", # the base, no badge + + "folder-documents": """\ + + + + + + + +""", + + "folder-download": """\ + + + + +""", + + "folder-music": """\ + + + + +""", + + "folder-pictures": """\ + + + + +""", + + "folder-videos": """\ + + + + + + + + + + + + + +""", + + "folder-templates": """\ + + + + + + +""", + + "folder-publicshare": """\ + + + + + + + +""", + + "folder-home": """\ + + +""", + + "folder-recent": """\ + + + + + +""", + + "folder-remote": """\ + + + + + + +""", + + "folder-saved-search": """\ + + + +""", + + "folder-drag-accept": "USE_OPEN_MASTER", # uses OPEN_MASTER, no extra badge + "folder-open": "USE_OPEN_MASTER", + + "folder-nexus-core": f"""\ + + + +""", +} + + +def svg_for(name: str, badge: str) -> str: + if badge == "USE_OPEN_MASTER": + body = OPEN_MASTER + extra = "" + else: + body = MASTER + extra = badge + return f""" + +{body}{extra} +""" + + +def write_svgs() -> list[str]: + PLACES.mkdir(parents=True, exist_ok=True) + written = [] + for name, badge in BADGES.items(): + path = PLACES / f"{name}.svg" + path.write_text(svg_for(name, badge)) + written.append(name) + return written + + +def render_pngs(names: list[str]) -> None: + for name in names: + svg = PLACES / f"{name}.svg" + for size in SIZES: + out_dir = ICONS_OUT / f"{size}x{size}" / "places" + out_dir.mkdir(parents=True, exist_ok=True) + out = out_dir / f"{name}.png" + subprocess.run( + [ + "inkscape", + str(svg), + "--export-type=png", + f"--export-filename={out}", + f"--export-width={size}", + f"--export-height={size}", + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f" rendered {name} @ {size}") + + +def backup_existing_nexus_core() -> None: + """Move the existing chunky 3D folder-nexus-core PNGs aside before overwriting.""" + backup = ROOT / "_backup" / "folder-nexus-core-original" + backup.mkdir(parents=True, exist_ok=True) + for size in SIZES: + src = ICONS_OUT / f"{size}x{size}" / "places" / "folder-nexus-core.png" + if src.exists(): + shutil.copy2(src, backup / f"{size}.png") + print(f" backed up original folder-nexus-core to {backup}") + + +def main() -> None: + svg_only = "--svg-only" in sys.argv + names = write_svgs() + print(f"Wrote {len(names)} SVGs into {PLACES}") + if svg_only: + return + backup_existing_nexus_core() + render_pngs(names) + # Refresh the GTK icon cache so file managers pick up the new icons. + subprocess.run( + ["gtk-update-icon-cache", "-f", str(ICONS_OUT)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f"\nDone. Set theme to NexusOS in your DE to see the icons.") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS-icons-src/build_actions.py b/assets/themes/NexusOS-icons-src/build_actions.py new file mode 100644 index 0000000..1fbc260 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/build_actions.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python3 +""" +NexusOS action-icon generator. + +Builds the icons the XFCE logoff dialog (xfce4-session-logout) and the +Whisker menu command buttons request — shutdown / reboot / log-out / +suspend / hibernate / switch-user / lock-screen / settings — in the +NexusOS palette (white glyph on a deep-purple disc with a lime ring). + +Emits one SVG per icon into actions/, rasterizes to +~/.icons/NexusOS//actions/.png at 16/22/24/32/48/64/128, +then writes the alias copies (xfsm-*, system-suspend-hibernate, and the +settings-manager app-icon names that Whisker's Settings button uses). + +Run: python3 build_actions.py # SVGs + PNGs + aliases + python3 build_actions.py --svg-only # just rewrite SVGs +""" + +from pathlib import Path +import shutil +import subprocess +import sys + +ROOT = Path(__file__).resolve().parent +ACTIONS = ROOT / "actions" +ICONS_OUT = Path.home() / ".icons" / "NexusOS" +SIZES = [16, 22, 24, 32, 48, 64, 128] + +# ── colors (mirror gtk-3.0/colors.css) ──────────────────────────────── +BODY = "#5e0066" # brand_purple_dark — disc fill +RING = "#8cc63f" # brand_green — disc ring +GLYPH = "#f2f2f2" # text_primary — glyph + +# ── master: purple disc + lime ring, viewBox 0 0 128 128 ────────────── +# Glyph area: a ~70px box centered on (64, 64). +DISC = f' \n' + + +def _gear_glyph() -> str: + """Settings cog: 8 teeth on a white disc with a punched centre hole.""" + teeth = "".join( + f' \n' + for k in range(8) + ) + return ( + " \n" + + teeth + + f' \n' + + f' \n' + ) + + +# ── glyph fragments — white, centered on (64, 64) ───────────────────── +GLYPHS = { + "system-shutdown": f"""\ + + + +""", + + "system-reboot": f"""\ + + + +""", + + "system-log-out": f"""\ + + + + + +""", + + "system-suspend": f"""\ + + +""", + + "system-hibernate": f"""\ + + + +""", + + "system-switch-user": f"""\ + + + + + +""", + + "system-lock-screen": f"""\ + + + + + +""", + + "preferences-desktop": _gear_glyph(), + + # ── Whisker menu application-category glyphs ────────────────────── + "applications-internet": f"""\ + + + + + +""", + + "applications-development": f"""\ + + + + +""", + + "applications-multimedia": f"""\ + + +""", + + "applications-office": f"""\ + + + + + + +""", + + "applications-graphics": f"""\ + + + + +""", + + "applications-system": f"""\ + + + + + +""", + + "applications-utilities": f"""\ + + + + + + +""", + + "applications-games": f"""\ + + + + + + +""", + + "applications-science": f"""\ + + + + +""", +} + +# Extra filenames that render the same art (XFCE's xfsm-* lookup names, +# the hybrid-sleep alias, and the Whisker command-button names). +ALIASES = { + "xfsm-logout": "system-log-out", + "xfsm-reboot": "system-reboot", + "xfsm-shutdown": "system-shutdown", + "xfsm-suspend": "system-suspend", + "xfsm-hibernate": "system-hibernate", + "xfsm-switch-user": "system-switch-user", + "system-suspend-hibernate": "system-suspend", + "xfsm-lock": "system-lock-screen", + "preferences-system": "preferences-desktop", + "org.xfce.settings.manager": "preferences-desktop", + "xfce4-settings-manager": "preferences-desktop", + # XFCE's "Accessories" menu uses xfce-accessories.directory + # (Icon=applications-accessories), NOT the freedesktop Utility name, so + # it needs the same wrench/tools badge as applications-utilities. + "applications-accessories": "applications-utilities", +} + + +def svg_for(glyph: str) -> str: + return ( + '\n' + '\n' + f"{DISC}{glyph}\n" + ) + + +def write_svgs() -> list[str]: + ACTIONS.mkdir(parents=True, exist_ok=True) + for name, glyph in GLYPHS.items(): + (ACTIONS / f"{name}.svg").write_text(svg_for(glyph)) + return list(GLYPHS) + + +def render_pngs(names: list[str]) -> None: + for name in names: + svg = ACTIONS / f"{name}.svg" + for size in SIZES: + out_dir = ICONS_OUT / f"{size}x{size}" / "actions" + out_dir.mkdir(parents=True, exist_ok=True) + out = out_dir / f"{name}.png" + subprocess.run( + [ + "inkscape", + str(svg), + "--export-type=png", + f"--export-filename={out}", + f"--export-width={size}", + f"--export-height={size}", + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f" rendered {name} @ {size}") + + +def write_aliases() -> None: + for alias, src in ALIASES.items(): + for size in SIZES: + d = ICONS_OUT / f"{size}x{size}" / "actions" + src_png = d / f"{src}.png" + if src_png.exists(): + shutil.copy2(src_png, d / f"{alias}.png") + print(f" alias {alias} -> {src}") + + +def main() -> None: + svg_only = "--svg-only" in sys.argv + names = write_svgs() + print(f"Wrote {len(names)} SVGs into {ACTIONS}") + if svg_only: + return + render_pngs(names) + write_aliases() + subprocess.run( + ["gtk-update-icon-cache", "-f", str(ICONS_OUT)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print("\nDone. Reload: xfdesktop --reload & xfce4-panel -r") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS-icons-src/build_battery.py b/assets/themes/NexusOS-icons-src/build_battery.py new file mode 100644 index 0000000..75bb69f --- /dev/null +++ b/assets/themes/NexusOS-icons-src/build_battery.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +""" +NexusOS battery-icon recolorizer. + +xfce4-power-manager draws the panel battery from the icon theme's +`battery-*-charging-symbolic` icons. Papirus-Dark (which NexusOS +inherits from) paints the charging bolt/fill in its own material green +(#4caf50). This script copies every charging battery symbolic icon into +the NexusOS theme with that green swapped for brand_green (#8cc63f), so +the charging indicator matches the rest of the NexusOS accent. + +Only the green is touched — the gray battery body (#dfdfdf) is left +alone (it already reads fine on the dark panel). Symlinked aliases +(good/low/medium/caution/empty-charging) are resolved and written as +real recolored files so every requested name overrides Papirus. + +Run: python3 build_battery.py +Then: gtk-update-icon-cache -f -t ../NexusOS-icons + xfce4-panel -r # reload panel to pick up new icons +""" + +from pathlib import Path + +ROOT = Path(__file__).resolve().parent +OUT = ROOT.parent / "NexusOS-icons" / "scalable" / "status" +SRC = Path("/usr/share/icons/Papirus-Dark/symbolic/status") + +PAPIRUS_GREEN = "#4caf50" +NEXUS_GREEN = "#8cc63f" # brand_green + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + # Every charging battery symbolic icon (charged-100 has no green, so + # the replace is a harmless no-op there; we copy it too for a + # consistent battery set under the NexusOS theme). + sources = sorted(SRC.glob("battery-*charg*-symbolic.svg")) + written = 0 + for src in sources: + # read_text follows symlinks, so alias names get the real content + content = src.read_text() + if PAPIRUS_GREEN not in content: + # charged/full icons with no green — skip; let them fall + # through to Papirus unchanged. + continue + recolored = content.replace(PAPIRUS_GREEN, NEXUS_GREEN) + (OUT / src.name).write_text(recolored) + written += 1 + print(f"Recolored {written} charging battery icons " + f"({PAPIRUS_GREEN} -> {NEXUS_GREEN}) into {OUT}") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS-icons-src/build_icons.py b/assets/themes/NexusOS-icons-src/build_icons.py new file mode 100644 index 0000000..2a6fd02 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/build_icons.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +""" +NexusOS folder icon generator. + +Composes each variant SVG from a shared master + a per-variant badge fragment, +then rasterizes each to PNG at 16/22/24/32/48/64/128 and drops the result into +~/.icons/NexusOS//places/.png. + +Run from this directory: + python3 build_icons.py [--svg-only] [--no-cache-update] +""" +from __future__ import annotations + +import argparse +import base64 +import pathlib +import shutil +import subprocess +import sys +from textwrap import dedent + +ROOT = pathlib.Path(__file__).resolve().parent +PLACES_DIR = ROOT / "places" +LOGO_SRC = ROOT.parent.parent / "assets" / "n-small.png" +ICONS_OUT = pathlib.Path.home() / ".icons" / "NexusOS" +SIZES = (16, 22, 24, 32, 48, 64, 128) + +# ── colors (mirror gtk-3.0/colors.css) ─────────────────────────────────────── +BODY = "#5e0066" # brand_purple_dark — folder body +HIGHLIGHT = "#88008f" # brand_purple — top-of-face highlight stripe +TAB = "#8cc63f" # brand_green — folder tab +TAB_BRIGHT = "#b8e373" # brand_green_light — drag-accept tab +LIP = "#f2f2f2" # text_primary — inner lip stripe & badge fill + +# Embedded logo (base64) populated at runtime +_LOGO_B64: str | None = None + + +def logo_data_uri() -> str: + global _LOGO_B64 + if _LOGO_B64 is None: + _LOGO_B64 = base64.b64encode(LOGO_SRC.read_bytes()).decode("ascii") + return f"data:image/png;base64,{_LOGO_B64}" + + +def folder_svg(badge: str, *, tab_color: str = TAB) -> str: + """Build a full variant SVG by injecting `badge` into the master template.""" + return dedent(f"""\ + + + + + + + {badge} + + """) + + +# ── badge fragments (centered on front face ~x=64, y=78) ───────────────────── +# Each is a simple white-on-purple glyph kept readable at 16-22px. + +BADGES: dict[str, str] = { + # Base folder — no badge + "folder": "", + + # Folder-open: lighter tab + slight perspective on front (simple variant) + "folder-open": ( + f'' + ), + + # Documents — page with folded corner + "folder-documents": ( + f'' + f'' + ), + + # Download — down arrow + "folder-download": ( + f'' + ), + + # Drag-accept — uses bright green tab, plus white plus glyph + "folder-drag-accept": ( + f'' + ), + + # Home — house + "folder-home": ( + f'' + ), + + # Music — eighth note + "folder-music": ( + f'' + f'' + f'' + ), + + # Pictures — sun over mountains + "folder-pictures": ( + f'' + f'' + f'' + ), + + # Publicshare — two people + "folder-publicshare": ( + f'' + f'' + f'' + ), + + # Recent — clock face + "folder-recent": ( + f'' + f'' + ), + + # Remote — globe (circle + ellipse + meridian) + "folder-remote": ( + f'' + f'' + f'' + ), + + # Saved search — magnifying glass + "folder-saved-search": ( + f'' + f'' + ), + + # Templates — page with horizontal lines + "folder-templates": ( + f'' + f'' + f'' + f'' + f'' + f'' + ), + + # Videos — play triangle + "folder-videos": ( + f'' + ), +} + + +def nexus_core_svg() -> str: + """folder-nexus-core embeds the bicolor N logo via base64 data URI.""" + return dedent(f"""\ + + + + + + + + + """) + + +def write_variants() -> list[tuple[str, pathlib.Path]]: + PLACES_DIR.mkdir(parents=True, exist_ok=True) + written: list[tuple[str, pathlib.Path]] = [] + + for name, badge in BADGES.items(): + tab = TAB_BRIGHT if name == "folder-drag-accept" else TAB + path = PLACES_DIR / f"{name}.svg" + path.write_text(folder_svg(badge, tab_color=tab)) + written.append((name, path)) + + nx_path = PLACES_DIR / "folder-nexus-core.svg" + nx_path.write_text(nexus_core_svg()) + written.append(("folder-nexus-core", nx_path)) + + return written + + +def render_one(svg_path: pathlib.Path, name: str) -> None: + for size in SIZES: + out_dir = ICONS_OUT / f"{size}x{size}" / "places" + out_dir.mkdir(parents=True, exist_ok=True) + out_png = out_dir / f"{name}.png" + subprocess.run( + [ + "inkscape", + str(svg_path), + "--export-type=png", + f"--export-filename={out_png}", + f"--export-width={size}", + f"--export-height={size}", + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print(f" {name} → {size}x{size}") + + +def backup_existing() -> None: + backup = ROOT / "_backup" / "folder-nexus-core-pre-rebuild" + if backup.exists(): + return + backup.mkdir(parents=True, exist_ok=True) + for size in SIZES: + src = ICONS_OUT / f"{size}x{size}" / "places" / "folder-nexus-core.png" + if src.exists(): + shutil.copy2(src, backup / f"{size}-folder-nexus-core.png") + print(f"Backed up existing folder-nexus-core PNGs → {backup}") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--svg-only", action="store_true", + help="only write SVG sources, skip rasterization") + parser.add_argument("--no-cache-update", action="store_true", + help="skip gtk-update-icon-cache at the end") + args = parser.parse_args() + + if not LOGO_SRC.exists(): + print(f"ERROR: logo source missing at {LOGO_SRC}", file=sys.stderr) + return 1 + + print("Writing variant SVGs…") + variants = write_variants() + print(f" {len(variants)} SVGs written to {PLACES_DIR}") + + if args.svg_only: + return 0 + + backup_existing() + + print("Rendering PNGs…") + for name, svg_path in variants: + render_one(svg_path, name) + + if not args.no_cache_update: + print("Refreshing icon cache…") + subprocess.run( + ["gtk-update-icon-cache", "-f", "-t", str(ICONS_OUT)], + check=False, + ) + + print(f"\nDone. Rendered {len(variants)} variants × {len(SIZES)} sizes " + f"= {len(variants) * len(SIZES)} PNGs.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/assets/themes/NexusOS-icons-src/build_status.py b/assets/themes/NexusOS-icons-src/build_status.py new file mode 100644 index 0000000..f268a71 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/build_status.py @@ -0,0 +1,398 @@ +#!/usr/bin/env python3 +""" +NexusOS network status icons generator. + +Generates flat symbolic-style SVGs for nm-tray and any other Qt/GTK app +that requests network-* status icons. Output goes to +scalable/status/ of the NexusOS-icons theme. + +Design language: + * 16×16 viewBox, scalable SVG (no rasterization needed — Qt5 + GTK3 + both handle SVG fine in scalable/ dirs) + * Light off-white stroke for base shape (wifi fan, ethernet plug, + globe) — readable on the dark NexusOS surfaces + * Lime brand accent for connected / active states + * Purple lock for encryption + * Red for error, orange for acquiring / no-route + +Run: python3 build_status.py +""" + +from pathlib import Path +import math + +ROOT = Path(__file__).resolve().parent +OUT = ROOT.parent / "NexusOS-icons" / "scalable" / "status" + +# Palette — mirrors gtk-3.0/colors.css. +BASE = "#f2f2f2" # text_primary +MUTED = "#6e7173" # text_disabled +GOOD = "#8cc63f" # brand_green +LOCK = "#f2f2f2" # text_primary (white) — neutral attribute indicator + # while green is reserved for active signal strength. +ERROR = "#da4453" # error_color +WARN = "#f67400" # warning_color + +SW = 1.6 +# Wifi fan arc geometry: half-angle from vertical. Smaller half-angle ⇒ +# narrower sweep but allows bigger radius (taller fan) before endpoints +# run off the sides at 16x16. 40° (80° total sweep) is a sweet spot. +ARC_HALF_ANGLE = 40 +S_ARC = math.sin(math.radians(ARC_HALF_ANGLE)) # x offset coefficient +C_ARC = math.cos(math.radians(ARC_HALF_ANGLE)) # y offset coefficient + + +def svg(body: str) -> str: + return ( + '\n' + + body.rstrip() + + "\n\n" + ) + + +# ── wifi fan ──────────────────────────────────────────────────────── +# Fan sits low in the viewBox with arcs reaching almost the top edge so +# the rendered icon matches the height of neighboring tray icons (shield, +# bluetooth, etc.). Outermost r=11.5 with the narrower 80° arc sweep +# keeps endpoints inside x=[0.61, 15.39] — fits with stroke-cap margin. +WIFI_CX, WIFI_CY = 8, 13.0 +WIFI_R = [3.0, 5.5, 8.5, 11.5] +DOT_R = 1.3 + + +def _arc(r: float, color: str, opacity: float = 1.0) -> str: + x0 = WIFI_CX - r * S_ARC + x1 = WIFI_CX + r * S_ARC + y = WIFI_CY - r * C_ARC + return ( + f' ' + ) + + +def wifi_fan(level: int, active_color: str = GOOD) -> str: + """level 0 (none) .. 4 (excellent). Filled arcs use brand_green by + default so signal strength reads as the primary visual element.""" + parts = [] + for i, r in enumerate(WIFI_R): + if i < level: + parts.append(_arc(r, active_color, 1.0)) + else: + parts.append(_arc(r, MUTED, 0.35)) + if level > 0: + parts.append( + f' ' + ) + else: + parts.append( + f' ' + ) + return "\n".join(parts) + + +# ── ethernet plug (RJ45) ──────────────────────────────────────────── +# Sized to fill ~75% of the viewBox vertically (y=2→14) and horizontally +# (x=2→14) so the rendered icon matches the height of neighboring tray +# icons (shield, notification, bluetooth) instead of looking like a tiny +# crate in the middle of the slot. +def ethernet_plug(color: str = GOOD, opacity: float = 1.0) -> str: + return ( + f' \n' + # Pentagonal RJ45 outline + ' \n' + # Three pins, spread wider to match the bigger body + ' \n' + ' \n' + ' \n' + " " + ) + + +# ── globe (generic network) ───────────────────────────────────────── +def globe(color: str = BASE, opacity: float = 1.0) -> str: + return ( + f' \n' + ' \n' + ' \n' + ' \n' + " " + ) + + +# ── key (vpn) ─────────────────────────────────────────────────────── +def vpn_key(color: str = GOOD, opacity: float = 1.0) -> str: + """Horizontal key: round bow on the left, shaft to the right with + two teeth hanging down. Stroke-only so it doesn't visually dominate + the way the filled globe did.""" + return ( + f' \n' + # Bow (handle) — outline circle on the left + ' \n' + # Shaft + ' \n' + # Two teeth pointing down + ' \n' + " " + ) + + +# ── state overlays / badges ───────────────────────────────────────── +# nm-tray's MultiIconDelegate stacks state icons on top of the signal- +# level base, so each overlay icon is *just the badge* — no wifi fan, +# no globe. That way stacking signal-good + encrypted gives fan + lock +# instead of fan + fan + lock, and adding -disconnected on top of an +# unsaved network adds a single X instead of another fan. + +def standalone_lock(color: str = LOCK) -> str: + """Centered padlock filling most of the 16x16 box. This is the icon + nm-tray actually loads for encryption — it requests `security-high` + / `security-high-symbolic` (NOT `network-wireless-encrypted`) and + overlays it on top of a `network-wireless-signal-*` base via its + MultiIconDelegate, scaling our full-size lock into a corner badge. + + Keyhole detail intentionally omitted: at the corner-badge render + size nm-tray uses, it's invisible anyway, and a dark keyhole on a + white body reads as visual noise rather than as a lock detail.""" + return ( + # Body: 8.5w x 6h, centered horizontally, sitting low + f' \n' + # Shackle: pronounced U above the body + f' ' + ) + + +def badge_lock() -> str: + """Lock badge: body 4.4w × 3.8h + shackle 3w × 2h on top, lower-right + corner. Made deliberately larger and with a thicker shackle stroke + than a typical badge — at 16-24px render, a small thin-stroke lock + blurs into a shield-like rounded shape. The big-shackle proportions + here keep the lock recognizable down to ~18px icon size.""" + return ( + ' \n' + f' \n' + f' \n' + " " + ) + + +def badge_x(color: str = ERROR) -> str: + return ( + f' \n' + ' \n' + ' \n' + " " + ) + + +def badge_dots(color: str = WARN) -> str: + return ( + f' \n' + ' \n' + ' \n' + ' \n' + " " + ) + + +def badge_excl(color: str = ERROR) -> str: + return ( + f' \n' + ' \n' + ' \n' + " " + ) + + +def badge_slash(color: str = MUTED) -> str: + return ( + f' ' + ) + + +def badge_arrow_up(color: str = GOOD) -> str: + return ( + f' ' + ) + + +def badge_arrow_down(color: str = GOOD) -> str: + return ( + f' ' + ) + + +# ── icon catalog ──────────────────────────────────────────────────── +def cell_tech(label: str) -> str: + """Wifi fan + tiny generation-tech label (2G/3G/4G/5G/E/G/H/U/1x).""" + return svg( + wifi_fan(3) + "\n" + f' {label}' + ) + + +ICONS = { + # wireless signal levels + "network-wireless-signal-none": svg(wifi_fan(0)), + "network-wireless-signal-weak": svg(wifi_fan(1)), + "network-wireless-signal-ok": svg(wifi_fan(2)), + "network-wireless-signal-good": svg(wifi_fan(3)), + "network-wireless-signal-excellent": svg(wifi_fan(4)), + + # wireless — nm-tray picks one icon per row, so each name is a + # self-contained composite (signal-level fan + state badge if any). + "network-wireless": svg(wifi_fan(3)), + "network-wireless-connected": svg(wifi_fan(4, GOOD)), + # Disconnected: faded-empty fan. NO X badge — nm-tray overlays this + # on top of saved-network rows, so an X here would visually pile up + # with the encrypted-lock overlay. The dim level-0 fan already reads + # as "no signal/disconnected" without the extra badge. + "network-wireless-disconnected": svg(wifi_fan(0)), + "network-wireless-disabled": svg(wifi_fan(0) + "\n" + badge_slash()), + "network-wireless-acquiring": svg(wifi_fan(2) + "\n" + badge_dots()), + "network-wireless-error": svg(wifi_fan(2) + "\n" + badge_excl()), + "network-wireless-no-route": svg(wifi_fan(3) + "\n" + badge_excl(WARN)), + "network-wireless-offline": svg(wifi_fan(0)), + "network-wireless-encrypted": svg(wifi_fan(3) + "\n" + badge_lock()), + "network-wireless-hotspot": svg(wifi_fan(4, GOOD) + "\n" + badge_dots(GOOD)), + "network-wireless-hardware-disabled": svg(wifi_fan(0) + "\n" + badge_slash(ERROR)), + + # wired + "network-wired": svg(ethernet_plug()), + "network-wired-disconnected": svg(ethernet_plug(MUTED, 0.55) + "\n" + badge_x()), + "network-wired-acquiring": svg(ethernet_plug() + "\n" + badge_dots()), + "network-wired-error": svg(ethernet_plug() + "\n" + badge_excl()), + "network-wired-no-route": svg(ethernet_plug() + "\n" + badge_excl(WARN)), + "network-wired-offline": svg(ethernet_plug(MUTED, 0.55)), + + # vpn — green key. Stroke-only so it doesn't dominate the menu row. + "network-vpn": svg(vpn_key()), + "network-vpn-connected": svg(vpn_key()), + "network-vpn-acquiring": svg(vpn_key() + "\n" + badge_dots()), + "network-vpn-disconnected": svg(vpn_key(MUTED, 0.55) + "\n" + badge_x()), + "network-vpn-error": svg(vpn_key(ERROR)), + "network-vpn-no-route": svg(vpn_key() + "\n" + badge_excl(WARN)), + "network-vpn-offline": svg(vpn_key(MUTED, 0.55)), + + # generic + "network-error": svg(globe() + "\n" + badge_excl()), + "network-idle": svg(globe()), + "network-offline": svg(globe(MUTED, 0.55) + "\n" + badge_slash()), + "network-no-route": svg(globe() + "\n" + badge_excl(WARN)), + "network-acquiring": svg(globe() + "\n" + badge_dots()), + "network-transmit": svg(globe() + "\n" + badge_arrow_up()), + "network-receive": svg(globe() + "\n" + badge_arrow_down()), + "network-transmit-receive": svg(globe() + "\n" + badge_arrow_up() + "\n" + badge_arrow_down()), + + # cellular — same fan, with state badges + "network-cellular-signal-none": svg(wifi_fan(0)), + "network-cellular-signal-weak": svg(wifi_fan(1)), + "network-cellular-signal-ok": svg(wifi_fan(2)), + "network-cellular-signal-good": svg(wifi_fan(3)), + "network-cellular-signal-excellent": svg(wifi_fan(4)), + "network-cellular-acquiring": svg(wifi_fan(2) + "\n" + badge_dots()), + "network-cellular-connected": svg(wifi_fan(4, GOOD)), + "network-cellular-disabled": svg(wifi_fan(0) + "\n" + badge_slash()), + "network-cellular-disconnected": svg(wifi_fan(0) + "\n" + badge_x()), + "network-cellular-error": svg(wifi_fan(2) + "\n" + badge_excl()), + "network-cellular-hardware-disabled": svg(wifi_fan(0) + "\n" + badge_slash(ERROR)), + "network-cellular-no-route": svg(wifi_fan(3) + "\n" + badge_excl(WARN)), + "network-cellular-offline": svg(wifi_fan(0)), + + # security indicators — nm-tray's actual encryption overlay icon + "security-high": svg(standalone_lock()), + "security-high-symbolic": svg(standalone_lock()), + "security-medium": svg(standalone_lock(WARN)), + "security-medium-symbolic": svg(standalone_lock(WARN)), + "security-low": svg(standalone_lock(ERROR)), + "security-low-symbolic": svg(standalone_lock(ERROR)), + + # cellular generation-tech labels + "network-cellular-2g": cell_tech("2G"), + "network-cellular-3g": cell_tech("3G"), + "network-cellular-4g": cell_tech("4G"), + "network-cellular-5g": cell_tech("5G"), + "network-cellular-edge": cell_tech("E"), + "network-cellular-gprs": cell_tech("G"), + "network-cellular-cdma-1x": cell_tech("1x"), + "network-cellular-hspa": cell_tech("H"), + "network-cellular-umts": cell_tech("U"), +} + + +# ── genmon panel applet PNGs ───────────────────────────────────────── +# bin/panel/network-applet.sh (the XFCE genmon network applet) loads PNGs +# directly from assets/panel-icons/ by path — it does NOT go through the +# icon theme. Rasterize the relevant status icons there too so the applet +# stays in sync with the canonical SVG design above (otherwise the PNGs +# drift into a different look, e.g. solid filled cones vs. arc bars). +PANEL_PNG_DIR = ROOT.parent.parent / "panel-icons" # assets/panel-icons +PANEL_PNG_SIZE = 22 +PANEL_PNG_NAMES = [ + "network-wireless-signal-none", + "network-wireless-signal-weak", + "network-wireless-signal-ok", + "network-wireless-signal-good", + "network-wireless-signal-excellent", + "network-wired", + "network-offline", +] + + +def _rasterize_panel_pngs() -> int: + import subprocess + if not PANEL_PNG_DIR.is_dir(): + return 0 + n = 0 + for name in PANEL_PNG_NAMES: + svg_path = OUT / f"{name}.svg" + if not svg_path.exists(): + continue + subprocess.run( + ["inkscape", str(svg_path), "--export-type=png", + f"--export-filename={PANEL_PNG_DIR / (name + '.png')}", + f"--export-width={PANEL_PNG_SIZE}", + f"--export-height={PANEL_PNG_SIZE}", + "--export-background-opacity=0"], + check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + ) + n += 1 + return n + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + # Sweep any leftover Papirus fallback symlinks from the same dir. + cleaned = 0 + for entry in OUT.iterdir(): + if entry.is_symlink(): + entry.unlink() + cleaned += 1 + for name, body in ICONS.items(): + (OUT / f"{name}.svg").write_text(body) + print(f"Cleared {cleaned} symlinks, wrote {len(ICONS)} SVGs to {OUT}") + n = _rasterize_panel_pngs() + print(f"Rasterized {n} genmon panel PNGs to {PANEL_PNG_DIR}") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS-icons-src/nexus-underlay-ring.svg b/assets/themes/NexusOS-icons-src/nexus-underlay-ring.svg new file mode 100644 index 0000000..7222fc9 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/nexus-underlay-ring.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/nexus-underlay.svg b/assets/themes/NexusOS-icons-src/nexus-underlay.svg new file mode 100644 index 0000000..e3646de --- /dev/null +++ b/assets/themes/NexusOS-icons-src/nexus-underlay.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-documents.svg b/assets/themes/NexusOS-icons-src/places/folder-documents.svg new file mode 100644 index 0000000..dfe629c --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-documents.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-download.svg b/assets/themes/NexusOS-icons-src/places/folder-download.svg new file mode 100644 index 0000000..7655370 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-download.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-drag-accept.svg b/assets/themes/NexusOS-icons-src/places/folder-drag-accept.svg new file mode 100644 index 0000000..e657e4c --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-drag-accept.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-home.svg b/assets/themes/NexusOS-icons-src/places/folder-home.svg new file mode 100644 index 0000000..dd78ae0 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-home.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-music.svg b/assets/themes/NexusOS-icons-src/places/folder-music.svg new file mode 100644 index 0000000..26f0f19 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-music.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-nexus-core.svg b/assets/themes/NexusOS-icons-src/places/folder-nexus-core.svg new file mode 100644 index 0000000..ed4ffc9 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-nexus-core.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-open.svg b/assets/themes/NexusOS-icons-src/places/folder-open.svg new file mode 100644 index 0000000..e657e4c --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-open.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-pictures.svg b/assets/themes/NexusOS-icons-src/places/folder-pictures.svg new file mode 100644 index 0000000..94122b8 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-pictures.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-publicshare.svg b/assets/themes/NexusOS-icons-src/places/folder-publicshare.svg new file mode 100644 index 0000000..3290c8b --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-publicshare.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-recent.svg b/assets/themes/NexusOS-icons-src/places/folder-recent.svg new file mode 100644 index 0000000..5fb0d75 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-recent.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-remote.svg b/assets/themes/NexusOS-icons-src/places/folder-remote.svg new file mode 100644 index 0000000..6f61960 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-remote.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-saved-search.svg b/assets/themes/NexusOS-icons-src/places/folder-saved-search.svg new file mode 100644 index 0000000..cd1e47d --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-saved-search.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-templates.svg b/assets/themes/NexusOS-icons-src/places/folder-templates.svg new file mode 100644 index 0000000..2fa1430 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-templates.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder-videos.svg b/assets/themes/NexusOS-icons-src/places/folder-videos.svg new file mode 100644 index 0000000..fc4a8a0 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder-videos.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons-src/places/folder.svg b/assets/themes/NexusOS-icons-src/places/folder.svg new file mode 100644 index 0000000..a930859 --- /dev/null +++ b/assets/themes/NexusOS-icons-src/places/folder.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-accessories.png b/assets/themes/NexusOS-icons/128x128/actions/applications-accessories.png new file mode 100644 index 0000000..c7a89e3 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-development.png b/assets/themes/NexusOS-icons/128x128/actions/applications-development.png new file mode 100644 index 0000000..7facfa5 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-games.png b/assets/themes/NexusOS-icons/128x128/actions/applications-games.png new file mode 100644 index 0000000..cf8840c Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-graphics.png b/assets/themes/NexusOS-icons/128x128/actions/applications-graphics.png new file mode 100644 index 0000000..570b613 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-internet.png b/assets/themes/NexusOS-icons/128x128/actions/applications-internet.png new file mode 100644 index 0000000..a94eb25 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/128x128/actions/applications-multimedia.png new file mode 100644 index 0000000..4a6c72f Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-office.png b/assets/themes/NexusOS-icons/128x128/actions/applications-office.png new file mode 100644 index 0000000..acf4d26 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-science.png b/assets/themes/NexusOS-icons/128x128/actions/applications-science.png new file mode 100644 index 0000000..308de87 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-system.png b/assets/themes/NexusOS-icons/128x128/actions/applications-system.png new file mode 100644 index 0000000..a065e22 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/applications-utilities.png b/assets/themes/NexusOS-icons/128x128/actions/applications-utilities.png new file mode 100644 index 0000000..c7a89e3 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/128x128/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..60a5672 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/128x128/actions/preferences-desktop.png new file mode 100644 index 0000000..60a5672 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/preferences-system.png b/assets/themes/NexusOS-icons/128x128/actions/preferences-system.png new file mode 100644 index 0000000..60a5672 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-hibernate.png b/assets/themes/NexusOS-icons/128x128/actions/system-hibernate.png new file mode 100644 index 0000000..7218185 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/128x128/actions/system-lock-screen.png new file mode 100644 index 0000000..f720a98 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-log-out.png b/assets/themes/NexusOS-icons/128x128/actions/system-log-out.png new file mode 100644 index 0000000..8744da7 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-reboot.png b/assets/themes/NexusOS-icons/128x128/actions/system-reboot.png new file mode 100644 index 0000000..4aaa352 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-shutdown.png b/assets/themes/NexusOS-icons/128x128/actions/system-shutdown.png new file mode 100644 index 0000000..ea8ae1c Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/128x128/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..3f1aa8f Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-suspend.png b/assets/themes/NexusOS-icons/128x128/actions/system-suspend.png new file mode 100644 index 0000000..3f1aa8f Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/system-switch-user.png b/assets/themes/NexusOS-icons/128x128/actions/system-switch-user.png new file mode 100644 index 0000000..2831e15 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/128x128/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..60a5672 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-hibernate.png new file mode 100644 index 0000000..7218185 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-lock.png new file mode 100644 index 0000000..f720a98 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-logout.png new file mode 100644 index 0000000..8744da7 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-reboot.png new file mode 100644 index 0000000..4aaa352 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-shutdown.png new file mode 100644 index 0000000..ea8ae1c Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-suspend.png new file mode 100644 index 0000000..3f1aa8f Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/128x128/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/128x128/actions/xfsm-switch-user.png new file mode 100644 index 0000000..2831e15 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/128x128/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/128x128/apps/com.visualstudio.code.png new file mode 100644 index 0000000..cf0ad9d Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/128x128/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/128x128/apps/microsoft-edge.png new file mode 100644 index 0000000..3b1c52b Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/128x128/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/128x128/apps/nexusos-logo.png new file mode 100644 index 0000000..baf2050 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-documents.png b/assets/themes/NexusOS-icons/128x128/places/folder-documents.png new file mode 100644 index 0000000..f61db61 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-download.png b/assets/themes/NexusOS-icons/128x128/places/folder-download.png new file mode 100644 index 0000000..baa037d Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/128x128/places/folder-drag-accept.png new file mode 100644 index 0000000..a007282 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-home.png b/assets/themes/NexusOS-icons/128x128/places/folder-home.png new file mode 100644 index 0000000..1eafb4a Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-music.png b/assets/themes/NexusOS-icons/128x128/places/folder-music.png new file mode 100644 index 0000000..02f6dc2 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/128x128/places/folder-nexus-core.png new file mode 100644 index 0000000..9b68987 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-open.png b/assets/themes/NexusOS-icons/128x128/places/folder-open.png new file mode 100644 index 0000000..a007282 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-pictures.png b/assets/themes/NexusOS-icons/128x128/places/folder-pictures.png new file mode 100644 index 0000000..77941cc Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-publicshare.png b/assets/themes/NexusOS-icons/128x128/places/folder-publicshare.png new file mode 100644 index 0000000..9f37ef0 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-recent.png b/assets/themes/NexusOS-icons/128x128/places/folder-recent.png new file mode 100644 index 0000000..0f18ae2 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-remote.png b/assets/themes/NexusOS-icons/128x128/places/folder-remote.png new file mode 100644 index 0000000..a7bbefa Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-saved-search.png b/assets/themes/NexusOS-icons/128x128/places/folder-saved-search.png new file mode 100644 index 0000000..b0f6de5 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-templates.png b/assets/themes/NexusOS-icons/128x128/places/folder-templates.png new file mode 100644 index 0000000..9ef2876 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder-videos.png b/assets/themes/NexusOS-icons/128x128/places/folder-videos.png new file mode 100644 index 0000000..643d96b Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/128x128/places/folder.png b/assets/themes/NexusOS-icons/128x128/places/folder.png new file mode 100644 index 0000000..f4e42e3 Binary files /dev/null and b/assets/themes/NexusOS-icons/128x128/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-accessories.png b/assets/themes/NexusOS-icons/16x16/actions/applications-accessories.png new file mode 100644 index 0000000..504c699 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-development.png b/assets/themes/NexusOS-icons/16x16/actions/applications-development.png new file mode 100644 index 0000000..3f9d2c1 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-games.png b/assets/themes/NexusOS-icons/16x16/actions/applications-games.png new file mode 100644 index 0000000..c88fb6e Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-graphics.png b/assets/themes/NexusOS-icons/16x16/actions/applications-graphics.png new file mode 100644 index 0000000..f8ec788 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-internet.png b/assets/themes/NexusOS-icons/16x16/actions/applications-internet.png new file mode 100644 index 0000000..d109b1e Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/16x16/actions/applications-multimedia.png new file mode 100644 index 0000000..cd03797 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-office.png b/assets/themes/NexusOS-icons/16x16/actions/applications-office.png new file mode 100644 index 0000000..e449c4f Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-science.png b/assets/themes/NexusOS-icons/16x16/actions/applications-science.png new file mode 100644 index 0000000..b63cec8 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-system.png b/assets/themes/NexusOS-icons/16x16/actions/applications-system.png new file mode 100644 index 0000000..99d37f2 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/applications-utilities.png b/assets/themes/NexusOS-icons/16x16/actions/applications-utilities.png new file mode 100644 index 0000000..504c699 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/16x16/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..eb0b208 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/16x16/actions/preferences-desktop.png new file mode 100644 index 0000000..eb0b208 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/preferences-system.png b/assets/themes/NexusOS-icons/16x16/actions/preferences-system.png new file mode 100644 index 0000000..eb0b208 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-hibernate.png b/assets/themes/NexusOS-icons/16x16/actions/system-hibernate.png new file mode 100644 index 0000000..ee6908c Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/16x16/actions/system-lock-screen.png new file mode 100644 index 0000000..c077fa1 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-log-out.png b/assets/themes/NexusOS-icons/16x16/actions/system-log-out.png new file mode 100644 index 0000000..a43a68a Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-reboot.png b/assets/themes/NexusOS-icons/16x16/actions/system-reboot.png new file mode 100644 index 0000000..bf06f9d Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-shutdown.png b/assets/themes/NexusOS-icons/16x16/actions/system-shutdown.png new file mode 100644 index 0000000..74908e6 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/16x16/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..408ff56 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-suspend.png b/assets/themes/NexusOS-icons/16x16/actions/system-suspend.png new file mode 100644 index 0000000..408ff56 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/system-switch-user.png b/assets/themes/NexusOS-icons/16x16/actions/system-switch-user.png new file mode 100644 index 0000000..b70f9f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/16x16/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..eb0b208 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-hibernate.png new file mode 100644 index 0000000..ee6908c Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-lock.png new file mode 100644 index 0000000..c077fa1 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-logout.png new file mode 100644 index 0000000..a43a68a Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-reboot.png new file mode 100644 index 0000000..bf06f9d Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-shutdown.png new file mode 100644 index 0000000..74908e6 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-suspend.png new file mode 100644 index 0000000..408ff56 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/16x16/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/16x16/actions/xfsm-switch-user.png new file mode 100644 index 0000000..b70f9f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/16x16/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/16x16/apps/com.visualstudio.code.png new file mode 100644 index 0000000..9c90355 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/16x16/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/16x16/apps/microsoft-edge.png new file mode 100644 index 0000000..3e68a44 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/16x16/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/16x16/apps/nexusos-logo.png new file mode 100644 index 0000000..b3ef393 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-documents.png b/assets/themes/NexusOS-icons/16x16/places/folder-documents.png new file mode 100644 index 0000000..afefc9d Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-download.png b/assets/themes/NexusOS-icons/16x16/places/folder-download.png new file mode 100644 index 0000000..ed6a8f6 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/16x16/places/folder-drag-accept.png new file mode 100644 index 0000000..abcffa8 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-home.png b/assets/themes/NexusOS-icons/16x16/places/folder-home.png new file mode 100644 index 0000000..aa59ef3 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-music.png b/assets/themes/NexusOS-icons/16x16/places/folder-music.png new file mode 100644 index 0000000..3443376 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/16x16/places/folder-nexus-core.png new file mode 100644 index 0000000..7e56f97 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-open.png b/assets/themes/NexusOS-icons/16x16/places/folder-open.png new file mode 100644 index 0000000..abcffa8 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-pictures.png b/assets/themes/NexusOS-icons/16x16/places/folder-pictures.png new file mode 100644 index 0000000..e1ffa5a Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-publicshare.png b/assets/themes/NexusOS-icons/16x16/places/folder-publicshare.png new file mode 100644 index 0000000..47b2794 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-recent.png b/assets/themes/NexusOS-icons/16x16/places/folder-recent.png new file mode 100644 index 0000000..7765172 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-remote.png b/assets/themes/NexusOS-icons/16x16/places/folder-remote.png new file mode 100644 index 0000000..53bf9c9 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-saved-search.png b/assets/themes/NexusOS-icons/16x16/places/folder-saved-search.png new file mode 100644 index 0000000..7f638bb Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-templates.png b/assets/themes/NexusOS-icons/16x16/places/folder-templates.png new file mode 100644 index 0000000..63b5295 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder-videos.png b/assets/themes/NexusOS-icons/16x16/places/folder-videos.png new file mode 100644 index 0000000..f46ef59 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/16x16/places/folder.png b/assets/themes/NexusOS-icons/16x16/places/folder.png new file mode 100644 index 0000000..2921b94 Binary files /dev/null and b/assets/themes/NexusOS-icons/16x16/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-accessories.png b/assets/themes/NexusOS-icons/22x22/actions/applications-accessories.png new file mode 100644 index 0000000..b36d8ac Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-development.png b/assets/themes/NexusOS-icons/22x22/actions/applications-development.png new file mode 100644 index 0000000..63cb6f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-games.png b/assets/themes/NexusOS-icons/22x22/actions/applications-games.png new file mode 100644 index 0000000..4d11259 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-graphics.png b/assets/themes/NexusOS-icons/22x22/actions/applications-graphics.png new file mode 100644 index 0000000..dcff76e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-internet.png b/assets/themes/NexusOS-icons/22x22/actions/applications-internet.png new file mode 100644 index 0000000..b60c548 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/22x22/actions/applications-multimedia.png new file mode 100644 index 0000000..e6f61cf Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-office.png b/assets/themes/NexusOS-icons/22x22/actions/applications-office.png new file mode 100644 index 0000000..f0ee2f2 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-science.png b/assets/themes/NexusOS-icons/22x22/actions/applications-science.png new file mode 100644 index 0000000..e5d7a05 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-system.png b/assets/themes/NexusOS-icons/22x22/actions/applications-system.png new file mode 100644 index 0000000..b66c80c Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/applications-utilities.png b/assets/themes/NexusOS-icons/22x22/actions/applications-utilities.png new file mode 100644 index 0000000..b36d8ac Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/22x22/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..35ced32 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/22x22/actions/preferences-desktop.png new file mode 100644 index 0000000..35ced32 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/preferences-system.png b/assets/themes/NexusOS-icons/22x22/actions/preferences-system.png new file mode 100644 index 0000000..35ced32 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-hibernate.png b/assets/themes/NexusOS-icons/22x22/actions/system-hibernate.png new file mode 100644 index 0000000..1f59af9 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/22x22/actions/system-lock-screen.png new file mode 100644 index 0000000..0c87b2e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-log-out.png b/assets/themes/NexusOS-icons/22x22/actions/system-log-out.png new file mode 100644 index 0000000..9995952 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-reboot.png b/assets/themes/NexusOS-icons/22x22/actions/system-reboot.png new file mode 100644 index 0000000..dffb063 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-shutdown.png b/assets/themes/NexusOS-icons/22x22/actions/system-shutdown.png new file mode 100644 index 0000000..be219eb Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/22x22/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..775c1d8 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-suspend.png b/assets/themes/NexusOS-icons/22x22/actions/system-suspend.png new file mode 100644 index 0000000..775c1d8 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/system-switch-user.png b/assets/themes/NexusOS-icons/22x22/actions/system-switch-user.png new file mode 100644 index 0000000..0f79e76 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/22x22/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..35ced32 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-hibernate.png new file mode 100644 index 0000000..1f59af9 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-lock.png new file mode 100644 index 0000000..0c87b2e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-logout.png new file mode 100644 index 0000000..9995952 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-reboot.png new file mode 100644 index 0000000..dffb063 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-shutdown.png new file mode 100644 index 0000000..be219eb Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-suspend.png new file mode 100644 index 0000000..775c1d8 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/22x22/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/22x22/actions/xfsm-switch-user.png new file mode 100644 index 0000000..0f79e76 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/22x22/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/22x22/apps/com.visualstudio.code.png new file mode 100644 index 0000000..896eff4 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/22x22/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/22x22/apps/microsoft-edge.png new file mode 100644 index 0000000..f7b5d72 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/22x22/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/22x22/apps/nexusos-logo.png new file mode 100644 index 0000000..67057f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-documents.png b/assets/themes/NexusOS-icons/22x22/places/folder-documents.png new file mode 100644 index 0000000..6474a19 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-download.png b/assets/themes/NexusOS-icons/22x22/places/folder-download.png new file mode 100644 index 0000000..1121c8e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/22x22/places/folder-drag-accept.png new file mode 100644 index 0000000..396a8a4 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-home.png b/assets/themes/NexusOS-icons/22x22/places/folder-home.png new file mode 100644 index 0000000..8b634f1 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-music.png b/assets/themes/NexusOS-icons/22x22/places/folder-music.png new file mode 100644 index 0000000..713f21e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/22x22/places/folder-nexus-core.png new file mode 100644 index 0000000..76b46a9 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-open.png b/assets/themes/NexusOS-icons/22x22/places/folder-open.png new file mode 100644 index 0000000..396a8a4 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-pictures.png b/assets/themes/NexusOS-icons/22x22/places/folder-pictures.png new file mode 100644 index 0000000..8b02e98 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-publicshare.png b/assets/themes/NexusOS-icons/22x22/places/folder-publicshare.png new file mode 100644 index 0000000..ef8235b Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-recent.png b/assets/themes/NexusOS-icons/22x22/places/folder-recent.png new file mode 100644 index 0000000..35033b1 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-remote.png b/assets/themes/NexusOS-icons/22x22/places/folder-remote.png new file mode 100644 index 0000000..548206e Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-saved-search.png b/assets/themes/NexusOS-icons/22x22/places/folder-saved-search.png new file mode 100644 index 0000000..588b85b Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-templates.png b/assets/themes/NexusOS-icons/22x22/places/folder-templates.png new file mode 100644 index 0000000..59caafb Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder-videos.png b/assets/themes/NexusOS-icons/22x22/places/folder-videos.png new file mode 100644 index 0000000..7eb56a7 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/22x22/places/folder.png b/assets/themes/NexusOS-icons/22x22/places/folder.png new file mode 100644 index 0000000..972ce76 Binary files /dev/null and b/assets/themes/NexusOS-icons/22x22/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-accessories.png b/assets/themes/NexusOS-icons/24x24/actions/applications-accessories.png new file mode 100644 index 0000000..8071b66 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-development.png b/assets/themes/NexusOS-icons/24x24/actions/applications-development.png new file mode 100644 index 0000000..0062acd Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-games.png b/assets/themes/NexusOS-icons/24x24/actions/applications-games.png new file mode 100644 index 0000000..5412f4c Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-graphics.png b/assets/themes/NexusOS-icons/24x24/actions/applications-graphics.png new file mode 100644 index 0000000..6e42553 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-internet.png b/assets/themes/NexusOS-icons/24x24/actions/applications-internet.png new file mode 100644 index 0000000..22ddb35 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/24x24/actions/applications-multimedia.png new file mode 100644 index 0000000..74f084c Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-office.png b/assets/themes/NexusOS-icons/24x24/actions/applications-office.png new file mode 100644 index 0000000..44e0de9 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-science.png b/assets/themes/NexusOS-icons/24x24/actions/applications-science.png new file mode 100644 index 0000000..193f3d6 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-system.png b/assets/themes/NexusOS-icons/24x24/actions/applications-system.png new file mode 100644 index 0000000..9c5fd1e Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/applications-utilities.png b/assets/themes/NexusOS-icons/24x24/actions/applications-utilities.png new file mode 100644 index 0000000..8071b66 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/24x24/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..1d262aa Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/24x24/actions/preferences-desktop.png new file mode 100644 index 0000000..1d262aa Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/preferences-system.png b/assets/themes/NexusOS-icons/24x24/actions/preferences-system.png new file mode 100644 index 0000000..1d262aa Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-hibernate.png b/assets/themes/NexusOS-icons/24x24/actions/system-hibernate.png new file mode 100644 index 0000000..d86e893 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/24x24/actions/system-lock-screen.png new file mode 100644 index 0000000..d66a573 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-log-out.png b/assets/themes/NexusOS-icons/24x24/actions/system-log-out.png new file mode 100644 index 0000000..585f1c7 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-reboot.png b/assets/themes/NexusOS-icons/24x24/actions/system-reboot.png new file mode 100644 index 0000000..e29c8f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-shutdown.png b/assets/themes/NexusOS-icons/24x24/actions/system-shutdown.png new file mode 100644 index 0000000..dbd2f01 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/24x24/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..4b0f86f Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-suspend.png b/assets/themes/NexusOS-icons/24x24/actions/system-suspend.png new file mode 100644 index 0000000..4b0f86f Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/system-switch-user.png b/assets/themes/NexusOS-icons/24x24/actions/system-switch-user.png new file mode 100644 index 0000000..525b422 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/24x24/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..1d262aa Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-hibernate.png new file mode 100644 index 0000000..d86e893 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-lock.png new file mode 100644 index 0000000..d66a573 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-logout.png new file mode 100644 index 0000000..585f1c7 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-reboot.png new file mode 100644 index 0000000..e29c8f5 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-shutdown.png new file mode 100644 index 0000000..dbd2f01 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-suspend.png new file mode 100644 index 0000000..4b0f86f Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/24x24/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/24x24/actions/xfsm-switch-user.png new file mode 100644 index 0000000..525b422 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/24x24/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/24x24/apps/com.visualstudio.code.png new file mode 100644 index 0000000..6245110 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/24x24/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/24x24/apps/microsoft-edge.png new file mode 100644 index 0000000..b6f6c07 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/24x24/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/24x24/apps/nexusos-logo.png new file mode 100644 index 0000000..90b8440 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-documents.png b/assets/themes/NexusOS-icons/24x24/places/folder-documents.png new file mode 100644 index 0000000..671731f Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-download.png b/assets/themes/NexusOS-icons/24x24/places/folder-download.png new file mode 100644 index 0000000..d5faae0 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/24x24/places/folder-drag-accept.png new file mode 100644 index 0000000..a690edb Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-home.png b/assets/themes/NexusOS-icons/24x24/places/folder-home.png new file mode 100644 index 0000000..a9f932e Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-music.png b/assets/themes/NexusOS-icons/24x24/places/folder-music.png new file mode 100644 index 0000000..60f8405 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/24x24/places/folder-nexus-core.png new file mode 100644 index 0000000..333480a Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-open.png b/assets/themes/NexusOS-icons/24x24/places/folder-open.png new file mode 100644 index 0000000..a690edb Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-pictures.png b/assets/themes/NexusOS-icons/24x24/places/folder-pictures.png new file mode 100644 index 0000000..b995f3c Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-publicshare.png b/assets/themes/NexusOS-icons/24x24/places/folder-publicshare.png new file mode 100644 index 0000000..cc869c9 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-recent.png b/assets/themes/NexusOS-icons/24x24/places/folder-recent.png new file mode 100644 index 0000000..298c343 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-remote.png b/assets/themes/NexusOS-icons/24x24/places/folder-remote.png new file mode 100644 index 0000000..ca4f3d4 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-saved-search.png b/assets/themes/NexusOS-icons/24x24/places/folder-saved-search.png new file mode 100644 index 0000000..14aaf75 Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-templates.png b/assets/themes/NexusOS-icons/24x24/places/folder-templates.png new file mode 100644 index 0000000..021409f Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder-videos.png b/assets/themes/NexusOS-icons/24x24/places/folder-videos.png new file mode 100644 index 0000000..0fa630e Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/24x24/places/folder.png b/assets/themes/NexusOS-icons/24x24/places/folder.png new file mode 100644 index 0000000..7c055bb Binary files /dev/null and b/assets/themes/NexusOS-icons/24x24/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-accessories.png b/assets/themes/NexusOS-icons/32x32/actions/applications-accessories.png new file mode 100644 index 0000000..b90a9e5 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-development.png b/assets/themes/NexusOS-icons/32x32/actions/applications-development.png new file mode 100644 index 0000000..0f7c491 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-games.png b/assets/themes/NexusOS-icons/32x32/actions/applications-games.png new file mode 100644 index 0000000..e34f229 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-graphics.png b/assets/themes/NexusOS-icons/32x32/actions/applications-graphics.png new file mode 100644 index 0000000..b2d87b9 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-internet.png b/assets/themes/NexusOS-icons/32x32/actions/applications-internet.png new file mode 100644 index 0000000..0919488 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/32x32/actions/applications-multimedia.png new file mode 100644 index 0000000..38339ec Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-office.png b/assets/themes/NexusOS-icons/32x32/actions/applications-office.png new file mode 100644 index 0000000..1489476 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-science.png b/assets/themes/NexusOS-icons/32x32/actions/applications-science.png new file mode 100644 index 0000000..403d637 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-system.png b/assets/themes/NexusOS-icons/32x32/actions/applications-system.png new file mode 100644 index 0000000..45a92b5 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/applications-utilities.png b/assets/themes/NexusOS-icons/32x32/actions/applications-utilities.png new file mode 100644 index 0000000..b90a9e5 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/32x32/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..74f18b3 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/32x32/actions/preferences-desktop.png new file mode 100644 index 0000000..74f18b3 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/preferences-system.png b/assets/themes/NexusOS-icons/32x32/actions/preferences-system.png new file mode 100644 index 0000000..74f18b3 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-hibernate.png b/assets/themes/NexusOS-icons/32x32/actions/system-hibernate.png new file mode 100644 index 0000000..d838efa Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/32x32/actions/system-lock-screen.png new file mode 100644 index 0000000..58563ce Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-log-out.png b/assets/themes/NexusOS-icons/32x32/actions/system-log-out.png new file mode 100644 index 0000000..7c2ae6a Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-reboot.png b/assets/themes/NexusOS-icons/32x32/actions/system-reboot.png new file mode 100644 index 0000000..872335d Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-shutdown.png b/assets/themes/NexusOS-icons/32x32/actions/system-shutdown.png new file mode 100644 index 0000000..7ac9b17 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/32x32/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..208acd0 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-suspend.png b/assets/themes/NexusOS-icons/32x32/actions/system-suspend.png new file mode 100644 index 0000000..208acd0 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/system-switch-user.png b/assets/themes/NexusOS-icons/32x32/actions/system-switch-user.png new file mode 100644 index 0000000..b750cc4 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/32x32/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..74f18b3 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-hibernate.png new file mode 100644 index 0000000..d838efa Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-lock.png new file mode 100644 index 0000000..58563ce Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-logout.png new file mode 100644 index 0000000..7c2ae6a Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-reboot.png new file mode 100644 index 0000000..872335d Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-shutdown.png new file mode 100644 index 0000000..7ac9b17 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-suspend.png new file mode 100644 index 0000000..208acd0 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/32x32/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/32x32/actions/xfsm-switch-user.png new file mode 100644 index 0000000..b750cc4 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/32x32/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/32x32/apps/com.visualstudio.code.png new file mode 100644 index 0000000..1f3a90b Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/32x32/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/32x32/apps/microsoft-edge.png new file mode 100644 index 0000000..bcea0be Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/32x32/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/32x32/apps/nexusos-logo.png new file mode 100644 index 0000000..59bce6c Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-documents.png b/assets/themes/NexusOS-icons/32x32/places/folder-documents.png new file mode 100644 index 0000000..c7f9515 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-download.png b/assets/themes/NexusOS-icons/32x32/places/folder-download.png new file mode 100644 index 0000000..5757273 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/32x32/places/folder-drag-accept.png new file mode 100644 index 0000000..c7f2116 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-home.png b/assets/themes/NexusOS-icons/32x32/places/folder-home.png new file mode 100644 index 0000000..cb4e457 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-music.png b/assets/themes/NexusOS-icons/32x32/places/folder-music.png new file mode 100644 index 0000000..54c641f Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/32x32/places/folder-nexus-core.png new file mode 100644 index 0000000..b938707 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-open.png b/assets/themes/NexusOS-icons/32x32/places/folder-open.png new file mode 100644 index 0000000..c7f2116 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-pictures.png b/assets/themes/NexusOS-icons/32x32/places/folder-pictures.png new file mode 100644 index 0000000..f2938ee Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-publicshare.png b/assets/themes/NexusOS-icons/32x32/places/folder-publicshare.png new file mode 100644 index 0000000..f32a9b5 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-recent.png b/assets/themes/NexusOS-icons/32x32/places/folder-recent.png new file mode 100644 index 0000000..d7dd720 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-remote.png b/assets/themes/NexusOS-icons/32x32/places/folder-remote.png new file mode 100644 index 0000000..7055f23 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-saved-search.png b/assets/themes/NexusOS-icons/32x32/places/folder-saved-search.png new file mode 100644 index 0000000..90a20b1 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-templates.png b/assets/themes/NexusOS-icons/32x32/places/folder-templates.png new file mode 100644 index 0000000..abb32ee Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder-videos.png b/assets/themes/NexusOS-icons/32x32/places/folder-videos.png new file mode 100644 index 0000000..5166999 Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/32x32/places/folder.png b/assets/themes/NexusOS-icons/32x32/places/folder.png new file mode 100644 index 0000000..b1d4e3b Binary files /dev/null and b/assets/themes/NexusOS-icons/32x32/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-accessories.png b/assets/themes/NexusOS-icons/48x48/actions/applications-accessories.png new file mode 100644 index 0000000..70db615 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-development.png b/assets/themes/NexusOS-icons/48x48/actions/applications-development.png new file mode 100644 index 0000000..7de34fc Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-games.png b/assets/themes/NexusOS-icons/48x48/actions/applications-games.png new file mode 100644 index 0000000..1637113 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-graphics.png b/assets/themes/NexusOS-icons/48x48/actions/applications-graphics.png new file mode 100644 index 0000000..61a40e3 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-internet.png b/assets/themes/NexusOS-icons/48x48/actions/applications-internet.png new file mode 100644 index 0000000..9200057 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/48x48/actions/applications-multimedia.png new file mode 100644 index 0000000..afd243f Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-office.png b/assets/themes/NexusOS-icons/48x48/actions/applications-office.png new file mode 100644 index 0000000..bbf52af Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-science.png b/assets/themes/NexusOS-icons/48x48/actions/applications-science.png new file mode 100644 index 0000000..cdc427c Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-system.png b/assets/themes/NexusOS-icons/48x48/actions/applications-system.png new file mode 100644 index 0000000..9ed30e5 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/applications-utilities.png b/assets/themes/NexusOS-icons/48x48/actions/applications-utilities.png new file mode 100644 index 0000000..70db615 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/48x48/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..7b739f1 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/48x48/actions/preferences-desktop.png new file mode 100644 index 0000000..7b739f1 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/preferences-system.png b/assets/themes/NexusOS-icons/48x48/actions/preferences-system.png new file mode 100644 index 0000000..7b739f1 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-hibernate.png b/assets/themes/NexusOS-icons/48x48/actions/system-hibernate.png new file mode 100644 index 0000000..9437429 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/48x48/actions/system-lock-screen.png new file mode 100644 index 0000000..889d387 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-log-out.png b/assets/themes/NexusOS-icons/48x48/actions/system-log-out.png new file mode 100644 index 0000000..77bcf05 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-reboot.png b/assets/themes/NexusOS-icons/48x48/actions/system-reboot.png new file mode 100644 index 0000000..5c07da7 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-shutdown.png b/assets/themes/NexusOS-icons/48x48/actions/system-shutdown.png new file mode 100644 index 0000000..72d94fb Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/48x48/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..c4b2625 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-suspend.png b/assets/themes/NexusOS-icons/48x48/actions/system-suspend.png new file mode 100644 index 0000000..c4b2625 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/system-switch-user.png b/assets/themes/NexusOS-icons/48x48/actions/system-switch-user.png new file mode 100644 index 0000000..d06b828 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/48x48/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..7b739f1 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-hibernate.png new file mode 100644 index 0000000..9437429 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-lock.png new file mode 100644 index 0000000..889d387 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-logout.png new file mode 100644 index 0000000..77bcf05 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-reboot.png new file mode 100644 index 0000000..5c07da7 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-shutdown.png new file mode 100644 index 0000000..72d94fb Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-suspend.png new file mode 100644 index 0000000..c4b2625 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/48x48/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/48x48/actions/xfsm-switch-user.png new file mode 100644 index 0000000..d06b828 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/48x48/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/48x48/apps/com.visualstudio.code.png new file mode 100644 index 0000000..5d1d790 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/48x48/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/48x48/apps/microsoft-edge.png new file mode 100644 index 0000000..e399431 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/48x48/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/48x48/apps/nexusos-logo.png new file mode 100644 index 0000000..ca5b2ab Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-documents.png b/assets/themes/NexusOS-icons/48x48/places/folder-documents.png new file mode 100644 index 0000000..6d2ed47 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-download.png b/assets/themes/NexusOS-icons/48x48/places/folder-download.png new file mode 100644 index 0000000..90c5ab3 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/48x48/places/folder-drag-accept.png new file mode 100644 index 0000000..9f4d441 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-home.png b/assets/themes/NexusOS-icons/48x48/places/folder-home.png new file mode 100644 index 0000000..651856f Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-music.png b/assets/themes/NexusOS-icons/48x48/places/folder-music.png new file mode 100644 index 0000000..4b135a4 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/48x48/places/folder-nexus-core.png new file mode 100644 index 0000000..745561f Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-open.png b/assets/themes/NexusOS-icons/48x48/places/folder-open.png new file mode 100644 index 0000000..9f4d441 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-pictures.png b/assets/themes/NexusOS-icons/48x48/places/folder-pictures.png new file mode 100644 index 0000000..f3cfae5 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-publicshare.png b/assets/themes/NexusOS-icons/48x48/places/folder-publicshare.png new file mode 100644 index 0000000..329a5a7 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-recent.png b/assets/themes/NexusOS-icons/48x48/places/folder-recent.png new file mode 100644 index 0000000..45c001d Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-remote.png b/assets/themes/NexusOS-icons/48x48/places/folder-remote.png new file mode 100644 index 0000000..9680e56 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-saved-search.png b/assets/themes/NexusOS-icons/48x48/places/folder-saved-search.png new file mode 100644 index 0000000..ae80566 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-templates.png b/assets/themes/NexusOS-icons/48x48/places/folder-templates.png new file mode 100644 index 0000000..d707440 Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder-videos.png b/assets/themes/NexusOS-icons/48x48/places/folder-videos.png new file mode 100644 index 0000000..314266d Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/48x48/places/folder.png b/assets/themes/NexusOS-icons/48x48/places/folder.png new file mode 100644 index 0000000..069cbad Binary files /dev/null and b/assets/themes/NexusOS-icons/48x48/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-accessories.png b/assets/themes/NexusOS-icons/64x64/actions/applications-accessories.png new file mode 100644 index 0000000..721c687 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-accessories.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-development.png b/assets/themes/NexusOS-icons/64x64/actions/applications-development.png new file mode 100644 index 0000000..e4bc223 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-development.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-games.png b/assets/themes/NexusOS-icons/64x64/actions/applications-games.png new file mode 100644 index 0000000..3f3cfa3 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-games.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-graphics.png b/assets/themes/NexusOS-icons/64x64/actions/applications-graphics.png new file mode 100644 index 0000000..a9b5a7e Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-graphics.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-internet.png b/assets/themes/NexusOS-icons/64x64/actions/applications-internet.png new file mode 100644 index 0000000..8b72063 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-internet.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-multimedia.png b/assets/themes/NexusOS-icons/64x64/actions/applications-multimedia.png new file mode 100644 index 0000000..c6d676d Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-multimedia.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-office.png b/assets/themes/NexusOS-icons/64x64/actions/applications-office.png new file mode 100644 index 0000000..2224df3 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-office.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-science.png b/assets/themes/NexusOS-icons/64x64/actions/applications-science.png new file mode 100644 index 0000000..6c5661f Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-science.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-system.png b/assets/themes/NexusOS-icons/64x64/actions/applications-system.png new file mode 100644 index 0000000..05aa4c9 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-system.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/applications-utilities.png b/assets/themes/NexusOS-icons/64x64/actions/applications-utilities.png new file mode 100644 index 0000000..721c687 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/applications-utilities.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/org.xfce.settings.manager.png b/assets/themes/NexusOS-icons/64x64/actions/org.xfce.settings.manager.png new file mode 100644 index 0000000..df7d4ab Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/org.xfce.settings.manager.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/preferences-desktop.png b/assets/themes/NexusOS-icons/64x64/actions/preferences-desktop.png new file mode 100644 index 0000000..df7d4ab Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/preferences-desktop.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/preferences-system.png b/assets/themes/NexusOS-icons/64x64/actions/preferences-system.png new file mode 100644 index 0000000..df7d4ab Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/preferences-system.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-hibernate.png b/assets/themes/NexusOS-icons/64x64/actions/system-hibernate.png new file mode 100644 index 0000000..356867a Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-lock-screen.png b/assets/themes/NexusOS-icons/64x64/actions/system-lock-screen.png new file mode 100644 index 0000000..e61ae61 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-lock-screen.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-log-out.png b/assets/themes/NexusOS-icons/64x64/actions/system-log-out.png new file mode 100644 index 0000000..3ad737a Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-log-out.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-reboot.png b/assets/themes/NexusOS-icons/64x64/actions/system-reboot.png new file mode 100644 index 0000000..6baa71c Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-reboot.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-shutdown.png b/assets/themes/NexusOS-icons/64x64/actions/system-shutdown.png new file mode 100644 index 0000000..da727ad Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-suspend-hibernate.png b/assets/themes/NexusOS-icons/64x64/actions/system-suspend-hibernate.png new file mode 100644 index 0000000..f339511 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-suspend-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-suspend.png b/assets/themes/NexusOS-icons/64x64/actions/system-suspend.png new file mode 100644 index 0000000..f339511 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-suspend.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/system-switch-user.png b/assets/themes/NexusOS-icons/64x64/actions/system-switch-user.png new file mode 100644 index 0000000..d440c79 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/system-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfce4-settings-manager.png b/assets/themes/NexusOS-icons/64x64/actions/xfce4-settings-manager.png new file mode 100644 index 0000000..df7d4ab Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfce4-settings-manager.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-hibernate.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-hibernate.png new file mode 100644 index 0000000..356867a Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-hibernate.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-lock.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-lock.png new file mode 100644 index 0000000..e61ae61 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-lock.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-logout.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-logout.png new file mode 100644 index 0000000..3ad737a Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-logout.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-reboot.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-reboot.png new file mode 100644 index 0000000..6baa71c Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-reboot.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-shutdown.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-shutdown.png new file mode 100644 index 0000000..da727ad Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-shutdown.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-suspend.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-suspend.png new file mode 100644 index 0000000..f339511 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-suspend.png differ diff --git a/assets/themes/NexusOS-icons/64x64/actions/xfsm-switch-user.png b/assets/themes/NexusOS-icons/64x64/actions/xfsm-switch-user.png new file mode 100644 index 0000000..d440c79 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/actions/xfsm-switch-user.png differ diff --git a/assets/themes/NexusOS-icons/64x64/apps/com.visualstudio.code.png b/assets/themes/NexusOS-icons/64x64/apps/com.visualstudio.code.png new file mode 100644 index 0000000..97ab954 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/apps/com.visualstudio.code.png differ diff --git a/assets/themes/NexusOS-icons/64x64/apps/microsoft-edge.png b/assets/themes/NexusOS-icons/64x64/apps/microsoft-edge.png new file mode 100644 index 0000000..7981afd Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/apps/microsoft-edge.png differ diff --git a/assets/themes/NexusOS-icons/64x64/apps/nexusos-logo.png b/assets/themes/NexusOS-icons/64x64/apps/nexusos-logo.png new file mode 100644 index 0000000..d961a6e Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/apps/nexusos-logo.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-documents.png b/assets/themes/NexusOS-icons/64x64/places/folder-documents.png new file mode 100644 index 0000000..7b91c3e Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-documents.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-download.png b/assets/themes/NexusOS-icons/64x64/places/folder-download.png new file mode 100644 index 0000000..752da81 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-download.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-drag-accept.png b/assets/themes/NexusOS-icons/64x64/places/folder-drag-accept.png new file mode 100644 index 0000000..24c279e Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-drag-accept.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-home.png b/assets/themes/NexusOS-icons/64x64/places/folder-home.png new file mode 100644 index 0000000..c3d6e47 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-home.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-music.png b/assets/themes/NexusOS-icons/64x64/places/folder-music.png new file mode 100644 index 0000000..5316d0a Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-music.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-nexus-core.png b/assets/themes/NexusOS-icons/64x64/places/folder-nexus-core.png new file mode 100644 index 0000000..caff89c Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-nexus-core.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-open.png b/assets/themes/NexusOS-icons/64x64/places/folder-open.png new file mode 100644 index 0000000..24c279e Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-open.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-pictures.png b/assets/themes/NexusOS-icons/64x64/places/folder-pictures.png new file mode 100644 index 0000000..c0cc183 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-pictures.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-publicshare.png b/assets/themes/NexusOS-icons/64x64/places/folder-publicshare.png new file mode 100644 index 0000000..6e2a2ee Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-publicshare.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-recent.png b/assets/themes/NexusOS-icons/64x64/places/folder-recent.png new file mode 100644 index 0000000..a153b82 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-recent.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-remote.png b/assets/themes/NexusOS-icons/64x64/places/folder-remote.png new file mode 100644 index 0000000..34ad282 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-remote.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-saved-search.png b/assets/themes/NexusOS-icons/64x64/places/folder-saved-search.png new file mode 100644 index 0000000..fabb2e3 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-saved-search.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-templates.png b/assets/themes/NexusOS-icons/64x64/places/folder-templates.png new file mode 100644 index 0000000..a5acbcb Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-templates.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder-videos.png b/assets/themes/NexusOS-icons/64x64/places/folder-videos.png new file mode 100644 index 0000000..fe75cac Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder-videos.png differ diff --git a/assets/themes/NexusOS-icons/64x64/places/folder.png b/assets/themes/NexusOS-icons/64x64/places/folder.png new file mode 100644 index 0000000..df9c1d2 Binary files /dev/null and b/assets/themes/NexusOS-icons/64x64/places/folder.png differ diff --git a/assets/themes/NexusOS-icons/icon-theme.cache b/assets/themes/NexusOS-icons/icon-theme.cache new file mode 100644 index 0000000..cb0d984 Binary files /dev/null and b/assets/themes/NexusOS-icons/icon-theme.cache differ diff --git a/assets/themes/NexusOS-icons/index.theme b/assets/themes/NexusOS-icons/index.theme new file mode 100644 index 0000000..769fa0c --- /dev/null +++ b/assets/themes/NexusOS-icons/index.theme @@ -0,0 +1,702 @@ +[Icon Theme] +Name=NexusOS +Comment=NexusOS accent icon theme +Inherits=Papirus-Dark,hicolor +Directories=16x16/apps,22x22/apps,24x24/apps,32x32/apps,48x48/apps,64x64/apps,128x128/apps,16x16/places,22x22/places,24x24/places,32x32/places,48x48/places,64x64/places,128x128/places,16x16/actions,22x22/actions,24x24/actions,32x32/actions,48x48/actions,64x64/actions,128x128/actions,scalable/status,8x8/emblems,16x16/devices,16x16/emblems,16x16/emotes,16x16/mimetypes,16x16/panel,16x16/status,16x16@2x/actions,16x16@2x/apps,16x16@2x/devices,16x16@2x/emblems,16x16@2x/emotes,16x16@2x/mimetypes,16x16@2x/panel,16x16@2x/places,16x16@2x/status,18x18/actions,18x18@2x/actions,22x22/animations,22x22/devices,22x22/emblems,22x22/emotes,22x22/mimetypes,22x22/panel,22x22/status,22x22@2x/actions,22x22@2x/animations,22x22@2x/apps,22x22@2x/devices,22x22@2x/emblems,22x22@2x/emotes,22x22@2x/mimetypes,22x22@2x/panel,22x22@2x/places,22x22@2x/status,24x24/animations,24x24/devices,24x24/emblems,24x24/emotes,24x24/mimetypes,24x24/panel,24x24/status,24x24@2x/actions,24x24@2x/animations,24x24@2x/apps,24x24@2x/devices,24x24@2x/emblems,24x24@2x/emotes,24x24@2x/mimetypes,24x24@2x/panel,24x24@2x/places,24x24@2x/status,32x32/devices,32x32/emblems,32x32/emotes,32x32/mimetypes,32x32/status,32x32@2x/actions,32x32@2x/apps,32x32@2x/devices,32x32@2x/emblems,32x32@2x/emotes,32x32@2x/mimetypes,32x32@2x/places,32x32@2x/status,42x42/apps,48x48/devices,48x48/emblems,48x48/emotes,48x48/mimetypes,48x48/status,48x48@2x/actions,48x48@2x/apps,48x48@2x/devices,48x48@2x/emblems,48x48@2x/emotes,48x48@2x/mimetypes,48x48@2x/places,48x48@2x/status,64x64/devices,64x64/mimetypes,64x64@2x/apps,64x64@2x/devices,64x64@2x/mimetypes,64x64@2x/places,84x84/apps,96x96/apps,96x96/devices,96x96/mimetypes,96x96/places,128x128/devices,128x128/mimetypes,symbolic/actions,symbolic/apps,symbolic/devices,symbolic/emblems,symbolic/emotes,symbolic/mimetypes,symbolic/places,symbolic/status,symbolic/up-to-32 + +[16x16/apps] +Context=Applications +Size=16 +Type=Fixed + +[22x22/apps] +Context=Applications +Size=22 +Type=Fixed + +[24x24/apps] +Context=Applications +Size=24 +Type=Fixed + +[32x32/apps] +Context=Applications +Size=32 +Type=Fixed + +[48x48/apps] +Context=Applications +Size=48 +Type=Fixed + +[64x64/apps] +Context=Applications +Size=64 +Type=Fixed + +[128x128/apps] +Context=Applications +Size=128 +MinSize=128 +MaxSize=512 +Type=Scalable + +[16x16/places] +Context=Places +Size=16 +Type=Fixed + +[22x22/places] +Context=Places +Size=22 +Type=Fixed + +[24x24/places] +Context=Places +Size=24 +Type=Fixed + +[32x32/places] +Context=Places +Size=32 +Type=Fixed + +[48x48/places] +Context=Places +Size=48 +Type=Fixed + +[64x64/places] +Context=Places +Size=64 +Type=Fixed + +[128x128/places] +Context=Places +Size=128 +MinSize=128 +MaxSize=512 +Type=Scalable + +[16x16/actions] +Context=Actions +Size=16 +Type=Fixed + +[22x22/actions] +Context=Actions +Size=22 +Type=Fixed + +[24x24/actions] +Context=Actions +Size=24 +Type=Fixed + +[32x32/actions] +Context=Actions +Size=32 +Type=Fixed + +[48x48/actions] +Context=Actions +Size=48 +Type=Fixed + +[64x64/actions] +Size=64 +Context=Actions +Type=Fixed + +[128x128/actions] +Size=128 +Context=Actions +Type=Fixed + +[scalable/status] +Size=16 +Context=Status +MinSize=8 +MaxSize=512 +Type=Scalable + +[8x8/emblems] +Context=Emblems +Size=8 +Type=Fixed + +[16x16/devices] +Context=Devices +Size=16 +Type=Fixed + +[16x16/emblems] +Context=Emblems +Size=16 +Type=Fixed + +[16x16/emotes] +Context=Emotes +Size=16 +Type=Fixed + +[16x16/mimetypes] +Context=MimeTypes +Size=16 +Type=Fixed + +[16x16/panel] +Context=Status +Size=16 +Type=Fixed + +[16x16/status] +Context=Status +Size=16 +Type=Fixed + +[16x16@2x/actions] +Context=Actions +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/apps] +Context=Applications +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/devices] +Context=Devices +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/emblems] +Context=Emblems +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/emotes] +Context=Emotes +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/mimetypes] +Context=MimeTypes +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/panel] +Context=Status +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/places] +Context=Places +Size=16 +Scale=2 +Type=Fixed + +[16x16@2x/status] +Context=Status +Size=16 +Scale=2 +Type=Fixed + +[18x18/actions] +Context=Actions +Size=18 +Type=Fixed + +[18x18@2x/actions] +Context=Actions +Size=18 +Scale=2 +Type=Fixed + +[22x22/animations] +Context=Animations +Size=22 +Type=Fixed + +[22x22/devices] +Context=Devices +Size=22 +Type=Fixed + +[22x22/emblems] +Context=Emblems +Size=22 +Type=Fixed + +[22x22/emotes] +Context=Emotes +Size=22 +Type=Fixed + +[22x22/mimetypes] +Context=MimeTypes +Size=22 +Type=Fixed + +[22x22/panel] +Context=Status +Size=22 +Type=Fixed + +[22x22/status] +Context=Status +Size=22 +Type=Fixed + +[22x22@2x/actions] +Context=Actions +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/animations] +Context=Animations +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/apps] +Context=Applications +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/devices] +Context=Devices +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/emblems] +Context=Emblems +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/emotes] +Context=Emotes +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/mimetypes] +Context=MimeTypes +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/panel] +Context=Status +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/places] +Context=Places +Size=22 +Scale=2 +Type=Fixed + +[22x22@2x/status] +Context=Status +Size=22 +Scale=2 +Type=Fixed + +[24x24/animations] +Context=Animations +Size=24 +Type=Fixed + +[24x24/devices] +Context=Devices +Size=24 +Type=Fixed + +[24x24/emblems] +Context=Emblems +Size=24 +Type=Fixed + +[24x24/emotes] +Context=Emotes +Size=24 +Type=Fixed + +[24x24/mimetypes] +Context=MimeTypes +Size=24 +Type=Fixed + +[24x24/panel] +Context=Status +Size=24 +Type=Fixed + +[24x24/status] +Context=Status +Size=24 +Type=Fixed + +[24x24@2x/actions] +Context=Actions +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/animations] +Context=Animations +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/apps] +Context=Applications +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/devices] +Context=Devices +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/emblems] +Context=Emblems +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/emotes] +Context=Emotes +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/mimetypes] +Context=MimeTypes +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/panel] +Context=Status +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/places] +Context=Places +Size=24 +Scale=2 +Type=Fixed + +[24x24@2x/status] +Context=Status +Size=24 +Scale=2 +Type=Fixed + +[32x32/devices] +Context=Devices +Size=32 +Type=Fixed + +[32x32/emblems] +Context=Emblems +Size=32 +Type=Fixed + +[32x32/emotes] +Context=Emotes +Size=32 +Type=Fixed + +[32x32/mimetypes] +Context=MimeTypes +Size=32 +Type=Fixed + +[32x32/status] +Context=Status +Size=32 +Type=Fixed + +[32x32@2x/actions] +Context=Actions +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/apps] +Context=Applications +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/devices] +Context=Devices +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/emblems] +Context=Emblems +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/emotes] +Context=Emotes +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/mimetypes] +Context=MimeTypes +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/places] +Context=Places +Size=32 +Scale=2 +Type=Fixed + +[32x32@2x/status] +Context=Status +Size=32 +Scale=2 +Type=Fixed + +[42x42/apps] +Context=Applications +Size=42 +Type=Fixed + +[48x48/devices] +Context=Devices +Size=48 +Type=Fixed + +[48x48/emblems] +Context=Emblems +Size=48 +Type=Fixed + +[48x48/emotes] +Context=Emotes +Size=48 +Type=Fixed + +[48x48/mimetypes] +Context=MimeTypes +Size=48 +Type=Fixed + +[48x48/status] +Context=Status +Size=48 +MinSize=48 +MaxSize=512 +Type=Scalable + +[48x48@2x/actions] +Context=Actions +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/apps] +Context=Applications +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/devices] +Context=Devices +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/emblems] +Context=Emblems +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/emotes] +Context=Emotes +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/mimetypes] +Context=MimeTypes +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/places] +Context=Places +Size=48 +Scale=2 +Type=Fixed + +[48x48@2x/status] +Context=Status +Size=48 +MinSize=48 +MaxSize=512 +Scale=2 +Type=Scalable + +[64x64/devices] +Context=Devices +Size=64 +Type=Fixed + +[64x64/mimetypes] +Context=MimeTypes +Size=64 +Type=Fixed + +[64x64@2x/apps] +Context=Applications +Size=64 +Scale=2 +Type=Fixed + +[64x64@2x/devices] +Context=Devices +Size=64 +Scale=2 +Type=Fixed + +[64x64@2x/mimetypes] +Context=MimeTypes +Size=64 +Scale=2 +Type=Fixed + +[64x64@2x/places] +Context=Places +Size=64 +Scale=2 +Type=Fixed + +[84x84/apps] +Context=Applications +Size=84 +Type=Fixed + +[96x96/apps] +Context=Applications +Size=96 +Type=Fixed + +[96x96/devices] +Context=Devices +Size=96 +Type=Fixed + +[96x96/mimetypes] +Context=MimeTypes +Size=96 +Type=Fixed + +[96x96/places] +Context=Places +Size=96 +Type=Fixed + +[128x128/devices] +Context=Devices +Size=128 +MinSize=128 +MaxSize=512 +Type=Scalable + +[128x128/mimetypes] +Context=MimeTypes +Size=128 +MinSize=128 +MaxSize=512 +Type=Scalable + +[symbolic/actions] +Context=Actions +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/apps] +Context=Applications +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/devices] +Context=Devices +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/emblems] +Context=Emblems +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/emotes] +Context=Emotes +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/mimetypes] +Context=MimeTypes +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/places] +Context=Places +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/status] +Context=Status +Size=16 +MinSize=16 +MaxSize=512 +Type=Scalable + +[symbolic/up-to-32] +Context=Status +Size=16 +MinSize=16 +MaxSize=32 +Type=Scalable diff --git a/assets/themes/NexusOS-xfwm4-src/build.py b/assets/themes/NexusOS-xfwm4-src/build.py new file mode 100644 index 0000000..e4b078f --- /dev/null +++ b/assets/themes/NexusOS-xfwm4-src/build.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +"""Build NexusOS xfwm4 theme from Mint-Y-Dark-Aqua base. + +- Copies all xfwm4 PNGs + themerc +- Aqua pixels (close-active/pressed glyph) → NexusOS green via HSV hue swap +- Dark Mint-Y greys repainted to NexusOS surface_bg_alt / border_strong +- themerc title text colors updated to NexusOS palette +""" +from __future__ import annotations + +import colorsys +import shutil +import sys +from pathlib import Path + +THEMES_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(THEMES_ROOT)) +import _palette as P # noqa: E402 + +from PIL import Image # noqa: E402 + +SRC = Path("/usr/share/themes/Mint-Y-Dark-Aqua/xfwm4") +OUT = THEMES_ROOT / "NexusOS" / "xfwm4" + +# Aqua → green HSV swap (identical to gtk2 build) +AQUA_RGB = (0x1F, 0x9E, 0xDE) +AQUA_HSV = colorsys.rgb_to_hsv(*(c / 255 for c in AQUA_RGB)) +GREEN_HSV = P.hex_to_hsv(P.BRAND_GREEN) +S_SCALE = GREEN_HSV[1] / AQUA_HSV[1] +V_SCALE = GREEN_HSV[2] / AQUA_HSV[2] +HUE_TOLERANCE = 30 / 360 +SAT_FLOOR = 0.18 + +# Gray repaints — direct RGB substitutions +GRAY_REMAP = { + (34, 34, 38): P.hex_to_rgb(P.SURFACE_BG_ALT), + (46, 46, 46): P.hex_to_rgb(P.BORDER_STRONG), + (36, 36, 39): P.hex_to_rgb(P.SURFACE_BG_ALT), + (76, 76, 80): P.hex_to_rgb(P.BORDER_STRONG), + (85, 85, 89): (130, 130, 134), + (106, 106, 109): (150, 150, 154), +} + + +def hue_distance(a: float, b: float) -> float: + d = abs(a - b) + return min(d, 1 - d) + + +def recolor_pixel(r: int, g: int, b: int, a: int) -> tuple[int, int, int, int]: + if a < 8: + return (r, g, b, a) + # Direct gray remap + if (r, g, b) in GRAY_REMAP: + nr, ng, nb = GRAY_REMAP[(r, g, b)] + return (nr, ng, nb, a) + # Aqua → green + h, s, v = colorsys.rgb_to_hsv(r / 255, g / 255, b / 255) + if s >= SAT_FLOOR and hue_distance(h, AQUA_HSV[0]) <= HUE_TOLERANCE: + new_s = min(1.0, s * S_SCALE) + new_v = min(1.0, v * V_SCALE) + nr, ng, nb = colorsys.hsv_to_rgb(GREEN_HSV[0], new_s, new_v) + return (round(nr * 255), round(ng * 255), round(nb * 255), a) + return (r, g, b, a) + + +def recolor_png(path: Path) -> int: + im = Image.open(path).convert("RGBA") + px = im.load() + w, h = im.size + changed = 0 + for y in range(h): + for x in range(w): + r, g, b, a = px[x, y] + new = recolor_pixel(r, g, b, a) + if new != (r, g, b, a): + px[x, y] = new + changed += 1 + if changed: + im.save(path, optimize=True) + return changed + + +THEMERC_REPLACEMENTS = { + "active_text_color=#e3e3e3": f"active_text_color=#{P.TEXT_PRIMARY}", + "active_text_shadow_color=#e3e3e3": f"active_text_shadow_color=#{P.TEXT_PRIMARY}", + "inactive_text_color=#acacac": f"inactive_text_color=#{P.TEXT_SECONDARY}", + "inactive_text_shadow_color=#acacac": f"inactive_text_shadow_color=#{P.TEXT_SECONDARY}", +} + + +def patch_themerc(path: Path) -> None: + text = path.read_text() + for src, dst in THEMERC_REPLACEMENTS.items(): + text = text.replace(src, dst) + path.write_text(text) + + +def main() -> None: + if OUT.exists(): + shutil.rmtree(OUT) + shutil.copytree(SRC, OUT) + print(f"Copied {SRC} → {OUT}") + + recolored = 0 + for png in sorted(OUT.glob("*.png")): + changed = recolor_png(png) + if changed: + recolored += 1 + print(f"Recolored {recolored} PNGs") + + patch_themerc(OUT / "themerc") + print("Patched themerc") + + +if __name__ == "__main__": + main() diff --git a/assets/themes/NexusOS/gtk-2.0/apps.rc b/assets/themes/NexusOS/gtk-2.0/apps.rc new file mode 100644 index 0000000..cb3ee0d --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/apps.rc @@ -0,0 +1,157 @@ +# +# Thunar +# +style "thunar-handle" { GtkPaned::handle-size = 2 } + +style "dark-sidebar" { + GtkTreeView::odd_row_color = @dark_sidebar_bg + GtkTreeView::even_row_color = @dark_sidebar_bg + + + base[NORMAL] = @dark_sidebar_bg + base[INSENSITIVE] = @dark_sidebar_bg + + text[NORMAL] = @fg_color + text[ACTIVE] = @selected_fg_color + text[SELECTED] = @selected_fg_color +} + +style "thunar-frame" { + xthickness = 0 + ythickness = 0 +} + +widget_class "*ThunarWindow*." style "thunar-frame" +widget_class "*ThunarShortcutsView*" style "dark-sidebar" +widget_class "*ThunarTreeView*" style "dark-sidebar" +widget_class "*ThunarWindow*." style "thunar-handle" + +# +# Workaround for colored entries +# +style "entry_border" { + + xthickness = 7 + ythickness = 5 + + engine "pixmap" { + + image { + function = SHADOW + state = NORMAL + detail = "entry" + file = "assets/entry-border-bg.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = ACTIVE + detail = "entry" + file = "assets/entry-border-active-bg.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = FLAT_BOX + state = ACTIVE + detail = "entry_bg" + file = "assets/null.png" + } + + image { + function = FLAT_BOX + state = INSENSITIVE + detail = "entry_bg" + file = "assets/null.png" + } + + image { + function = FLAT_BOX + detail = "entry_bg" + file = "assets/null.png" + } + } +} + +style "combobox_entry_border" = "combobox_entry" { + + engine "pixmap" { + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry-border.png" + border = { 4, 4, 12, 12 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-border-focus.png" + border = { 4, 4, 12, 12 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry-border-rtl.png" + border = { 4, 4, 12, 12 } + stretch = TRUE + direction = RTL + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-border-focus-rtl.png" + border = { 4, 4, 12, 12 } + stretch = TRUE + direction = RTL + } + + image { + function = FLAT_BOX + state = INSENSITIVE + detail = "entry_bg" + file = "assets/null.png" + } + + image { + function = FLAT_BOX + detail = "entry_bg" + file = "assets/null.png" + } + } +} + + +# Mousepad search entry +widget_class "*MousepadSearchBar*." style "entry_border" + +# Mousepad find and replace +widget_class "*MousepadReplaceDialog*." style "entry_border" + +# Thunar bulk rename +widget_class "*ThunarRenamerDialog*." style "entry_border" + +# Hexchat input box +class "SexySpellEntry" style:highest "entry_border" + +# Geany search entries +widget "*GeanyToolbar.*geany-search-entry-no-match*" style "entry_border" +widget "*GeanyToolbar.*GtkEntry*" style "entry_border" + +widget "GeanyDialogSearch.*GtkComboBoxEntry*.*geany-search-entry-no-match*" style "combobox_entry_border" diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-insens.png new file mode 100644 index 0000000..13313a8 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-prelight.png new file mode 100644 index 0000000..d7031da Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-insens.png new file mode 100644 index 0000000..e1f4dd1 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-prelight.png new file mode 100644 index 0000000..5a4efa2 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small.png new file mode 100644 index 0000000..681fd6f Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down-small.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-down.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down.png new file mode 100644 index 0000000..32b997b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-down.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-insens.png new file mode 100644 index 0000000..7a2c2cf Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-prelight.png new file mode 100644 index 0000000..5bd6a2c Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-left.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left.png new file mode 100644 index 0000000..b1764fe Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-left.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-insens.png new file mode 100644 index 0000000..e94f0fb Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-prelight.png new file mode 100644 index 0000000..d20b82c Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-right.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right.png new file mode 100644 index 0000000..eb725e6 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-right.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-insens.png new file mode 100644 index 0000000..22c44ff Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-prelight.png new file mode 100644 index 0000000..8d9edac Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-insens.png new file mode 100644 index 0000000..11a9e76 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-prelight.png new file mode 100644 index 0000000..da76e01 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small.png new file mode 100644 index 0000000..59e45db Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up-small.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/arrow-up.png b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up.png new file mode 100644 index 0000000..1f095b2 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/arrow-up.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/border.png b/assets/themes/NexusOS/gtk-2.0/assets/border.png new file mode 100644 index 0000000..7eafe12 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/border.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/button-active-hover.png b/assets/themes/NexusOS/gtk-2.0/assets/button-active-hover.png new file mode 100644 index 0000000..393812e Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/button-active-hover.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/button-active.png b/assets/themes/NexusOS/gtk-2.0/assets/button-active.png new file mode 100644 index 0000000..0f1be04 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/button-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/button-hover.png b/assets/themes/NexusOS/gtk-2.0/assets/button-hover.png new file mode 100644 index 0000000..8273b31 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/button-hover.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/button-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/button-insensitive.png new file mode 100644 index 0000000..ebb9e3f Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/button-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/button.png b/assets/themes/NexusOS/gtk-2.0/assets/button.png new file mode 100644 index 0000000..ff486df Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/button.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked-insensitive.png new file mode 100644 index 0000000..2cf6083 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked.png b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked.png new file mode 100644 index 0000000..2230264 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-checked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked-insensitive.png new file mode 100644 index 0000000..e11adb5 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked.png b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked.png new file mode 100644 index 0000000..38d8473 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/checkbox-unchecked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus-rtl.png new file mode 100644 index 0000000..361f1c3 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus.png new file mode 100644 index 0000000..7b1ae33 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-focus.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-rtl.png new file mode 100644 index 0000000..dbfa580 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border.png new file mode 100644 index 0000000..d18775a Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-border.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active-rtl.png new file mode 100644 index 0000000..e41ee9c Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active.png new file mode 100644 index 0000000..8017f74 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive-rtl.png new file mode 100644 index 0000000..ccda812 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive.png new file mode 100644 index 0000000..fb3453b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-rtl.png new file mode 100644 index 0000000..ccda812 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button.png new file mode 100644 index 0000000..3061a91 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-button.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook-rtl.png new file mode 100644 index 0000000..2327e14 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook.png new file mode 100644 index 0000000..bb81b3f Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-rtl.png new file mode 100644 index 0000000..f559df5 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus.png new file mode 100644 index 0000000..ee711af Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-focus.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook-rtl.png new file mode 100644 index 0000000..f7b02d7 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook.png new file mode 100644 index 0000000..b162520 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-rtl.png new file mode 100644 index 0000000..f7b02d7 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive.png new file mode 100644 index 0000000..e84bcf6 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook-rtl.png new file mode 100644 index 0000000..e64b763 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook.png new file mode 100644 index 0000000..5fabd1b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-rtl.png new file mode 100644 index 0000000..b0289ae Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/combo-entry.png b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry.png new file mode 100644 index 0000000..ed16a77 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/combo-entry.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable-rtl.png new file mode 100644 index 0000000..bb40993 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable.png b/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable.png new file mode 100644 index 0000000..5cfb997 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/down-background-disable.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/down-background-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/down-background-rtl.png new file mode 100644 index 0000000..03c300f Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/down-background-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/down-background.png b/assets/themes/NexusOS/gtk-2.0/assets/down-background.png new file mode 100644 index 0000000..83ca6b5 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/down-background.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-active-bg.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-bg.png new file mode 100644 index 0000000..d2a58a9 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-bg.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-active-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-notebook.png new file mode 100644 index 0000000..33051ff Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-active-toolbar.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-toolbar.png new file mode 100644 index 0000000..e8ef5dc Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-active-toolbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-background-disabled.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-background-disabled.png new file mode 100644 index 0000000..3bde125 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-background-disabled.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-background.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-background.png new file mode 100644 index 0000000..edbadfd Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-background.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-bg.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-bg.png new file mode 100644 index 0000000..8e13c86 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-bg.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-border-active-bg.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-border-active-bg.png new file mode 100644 index 0000000..d697042 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-border-active-bg.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-border-bg.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-border-bg.png new file mode 100644 index 0000000..bcfd3c1 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-border-bg.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-bg.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-bg.png new file mode 100644 index 0000000..31ffd6e Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-bg.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-notebook.png new file mode 100644 index 0000000..af8a9a3 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-toolbar.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-toolbar.png new file mode 100644 index 0000000..8011330 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-disabled-toolbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-notebook.png new file mode 100644 index 0000000..433e761 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/entry-toolbar.png b/assets/themes/NexusOS/gtk-2.0/assets/entry-toolbar.png new file mode 100644 index 0000000..499ff69 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/entry-toolbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/focus-line.png b/assets/themes/NexusOS/gtk-2.0/assets/focus-line.png new file mode 100644 index 0000000..67162d4 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/focus-line.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-end.png b/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-end.png new file mode 100644 index 0000000..b5549a4 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-end.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-start.png b/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-start.png new file mode 100644 index 0000000..b5549a4 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/frame-gap-start.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/frame.png b/assets/themes/NexusOS/gtk-2.0/assets/frame.png new file mode 100644 index 0000000..9cefc7b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/frame.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/handle-h.png b/assets/themes/NexusOS/gtk-2.0/assets/handle-h.png new file mode 100644 index 0000000..fab6bf5 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/handle-h.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/handle-v.png b/assets/themes/NexusOS/gtk-2.0/assets/handle-v.png new file mode 100644 index 0000000..6276854 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/handle-v.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/inline-toolbar.png b/assets/themes/NexusOS/gtk-2.0/assets/inline-toolbar.png new file mode 100644 index 0000000..51700d0 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/inline-toolbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/line-h.png b/assets/themes/NexusOS/gtk-2.0/assets/line-h.png new file mode 100644 index 0000000..85fcb27 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/line-h.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/line-v.png b/assets/themes/NexusOS/gtk-2.0/assets/line-v.png new file mode 100644 index 0000000..b9cc686 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/line-v.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow-prelight.png new file mode 100644 index 0000000..45f7574 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow.png new file mode 100644 index 0000000..7163de7 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-arrow.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked-insensitive.png new file mode 100644 index 0000000..b8c0617 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked.png new file mode 100644 index 0000000..b0e05ba Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-checked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked-insensitive.png new file mode 100644 index 0000000..480564b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked.png new file mode 100644 index 0000000..4c1fadc Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-checkbox-unchecked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked-insensitive.png new file mode 100644 index 0000000..a87f782 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked.png new file mode 100644 index 0000000..fd900e1 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-checked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked-insensitive.png new file mode 100644 index 0000000..f26dcf8 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked.png new file mode 100644 index 0000000..81fb24e Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-radio-unchecked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menu-separator.png b/assets/themes/NexusOS/gtk-2.0/assets/menu-separator.png new file mode 100644 index 0000000..f6aec56 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menu-separator.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menubar.png b/assets/themes/NexusOS/gtk-2.0/assets/menubar.png new file mode 100644 index 0000000..ba32eed Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menubar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menubar_button.png b/assets/themes/NexusOS/gtk-2.0/assets/menubar_button.png new file mode 100644 index 0000000..935de53 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menubar_button.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/menuitem.png b/assets/themes/NexusOS/gtk-2.0/assets/menuitem.png new file mode 100644 index 0000000..380cc25 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/menuitem.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/minus.png b/assets/themes/NexusOS/gtk-2.0/assets/minus.png new file mode 100644 index 0000000..6bc78c3 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/minus.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-horiz.png b/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-horiz.png new file mode 100644 index 0000000..7acf3be Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-horiz.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-vert.png b/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-vert.png new file mode 100644 index 0000000..ad8092f Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/notebook-gap-vert.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/notebook.png b/assets/themes/NexusOS/gtk-2.0/assets/notebook.png new file mode 100644 index 0000000..7471c8e Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/notebook.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/null.png b/assets/themes/NexusOS/gtk-2.0/assets/null.png new file mode 100644 index 0000000..537156d Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/null.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_active.png b/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_active.png new file mode 100644 index 0000000..cc77d06 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_prelight.png new file mode 100644 index 0000000..9add29c Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/pathbar_button_prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/plus.png b/assets/themes/NexusOS/gtk-2.0/assets/plus.png new file mode 100644 index 0000000..cebf088 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/plus.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/progressbar.png b/assets/themes/NexusOS/gtk-2.0/assets/progressbar.png new file mode 100644 index 0000000..c95291a Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/progressbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/progressbar_v.png b/assets/themes/NexusOS/gtk-2.0/assets/progressbar_v.png new file mode 100644 index 0000000..f9c178b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/progressbar_v.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/radio-checked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/radio-checked-insensitive.png new file mode 100644 index 0000000..50ee95a Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/radio-checked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/radio-checked.png b/assets/themes/NexusOS/gtk-2.0/assets/radio-checked.png new file mode 100644 index 0000000..687ab59 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/radio-checked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked-insensitive.png new file mode 100644 index 0000000..aea9150 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked.png b/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked.png new file mode 100644 index 0000000..890b7c7 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/radio-unchecked.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-active.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-active.png new file mode 100644 index 0000000..efaf2f4 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-insens.png new file mode 100644 index 0000000..b77847a Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-prelight.png new file mode 100644 index 0000000..d08a4cb Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz.png new file mode 100644 index 0000000..1ab5c43 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-horiz.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-insensitive.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-insensitive.png new file mode 100644 index 0000000..5eff626 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-insensitive.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-prelight.png new file mode 100644 index 0000000..f824440 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-active.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-active.png new file mode 100644 index 0000000..30fc643 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-insens.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-insens.png new file mode 100644 index 0000000..17fde48 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-insens.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-prelight.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-prelight.png new file mode 100644 index 0000000..7cc77bb Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert-prelight.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider-vert.png b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert.png new file mode 100644 index 0000000..0d4d978 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider-vert.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/slider.png b/assets/themes/NexusOS/gtk-2.0/assets/slider.png new file mode 100644 index 0000000..f8680e6 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/slider.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/tab-bottom-active.png b/assets/themes/NexusOS/gtk-2.0/assets/tab-bottom-active.png new file mode 100644 index 0000000..36d315d Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/tab-bottom-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/tab-left-active.png b/assets/themes/NexusOS/gtk-2.0/assets/tab-left-active.png new file mode 100644 index 0000000..b6e1f47 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/tab-left-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/tab-right-active.png b/assets/themes/NexusOS/gtk-2.0/assets/tab-right-active.png new file mode 100644 index 0000000..73663ab Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/tab-right-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/tab-top-active.png b/assets/themes/NexusOS/gtk-2.0/assets/tab-top-active.png new file mode 100644 index 0000000..d316d16 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/tab-top-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active-hover.png b/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active-hover.png new file mode 100644 index 0000000..8273b31 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active-hover.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active.png b/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active.png new file mode 100644 index 0000000..ff486df Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/toolbar-button-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/toolbar.png b/assets/themes/NexusOS/gtk-2.0/assets/toolbar.png new file mode 100644 index 0000000..95cfd21 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/toolbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/tree_header.png b/assets/themes/NexusOS/gtk-2.0/assets/tree_header.png new file mode 100644 index 0000000..92d0146 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/tree_header.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal-active.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal-active.png new file mode 100644 index 0000000..275be24 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal.png new file mode 100644 index 0000000..c9257f9 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-horizontal.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar.png new file mode 100644 index 0000000..6fc1c27 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar_v.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar_v.png new file mode 100644 index 0000000..03f65bd Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-progressbar_v.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-horiz.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-horiz.png new file mode 100644 index 0000000..8c66765 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-horiz.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-vert.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-vert.png new file mode 100644 index 0000000..b277bb4 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-scrollbar-vert.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical-active.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical-active.png new file mode 100644 index 0000000..152e321 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical-active.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical.png b/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical.png new file mode 100644 index 0000000..1a7a190 Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/trough-vertical.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable-rtl.png new file mode 100644 index 0000000..625b4bb Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable.png b/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable.png new file mode 100644 index 0000000..883b78b Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/up-background-disable.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/up-background-rtl.png b/assets/themes/NexusOS/gtk-2.0/assets/up-background-rtl.png new file mode 100644 index 0000000..6cd15ba Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/up-background-rtl.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/assets/up-background.png b/assets/themes/NexusOS/gtk-2.0/assets/up-background.png new file mode 100644 index 0000000..ae2035a Binary files /dev/null and b/assets/themes/NexusOS/gtk-2.0/assets/up-background.png differ diff --git a/assets/themes/NexusOS/gtk-2.0/gtkrc b/assets/themes/NexusOS/gtk-2.0/gtkrc new file mode 100644 index 0000000..0144af5 --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/gtkrc @@ -0,0 +1,26 @@ +gtk-color-scheme = "base_color: #242424" +gtk-color-scheme = "text_color: #dedede" +gtk-color-scheme = "bg_color: #333333" +gtk-color-scheme = "fg_color: #dedede" +gtk-color-scheme = "tooltip_bg_color: #2a2a2a" +gtk-color-scheme = "tooltip_fg_color: #dedede" +gtk-color-scheme = "selected_bg_color: #9A57A3" +gtk-color-scheme = "selected_fg_color: #ffffff" +gtk-color-scheme = "insensitive_bg_color: #2a2a2a" +gtk-color-scheme = "insensitive_fg_color: #565656" +gtk-color-scheme = "insensitive_button_fg_color: #565656" +gtk-color-scheme = "notebook_bg: #242424" +gtk-color-scheme = "dark_sidebar_bg: #3b3b3b" +gtk-color-scheme = "link_color: #5294e2" +gtk-color-scheme = "menu_bg: #2a1d33" +gtk-color-scheme = "menu_fg: #dedede" + +gtk-icon-sizes = "gtk-button=16,16" # This makes button icons smaller. +gtk-auto-mnemonics = 1 +gtk-primary-button-warps-slider = 1 + +include "main.rc" +include "apps.rc" +include "panel.rc" +include "xfce-notify.rc" +include "menubar-toolbar.rc" diff --git a/assets/themes/NexusOS/gtk-2.0/main.rc b/assets/themes/NexusOS/gtk-2.0/main.rc new file mode 100644 index 0000000..4f21ab2 --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/main.rc @@ -0,0 +1,2483 @@ +style "default" { + + xthickness = 1 + ythickness = 1 + + # Style Properties + + GtkWindow::resize-grip-height = 4 + GtkWindow::resize-grip-width = 4 + + GtkWidget::focus-line-width = 1 + GtkMenuBar::window-dragging = 1 + GtkToolbar::window-dragging = 1 + GtkToolbar::internal-padding = 4 + GtkToolButton::icon-spacing = 4 + + GtkWidget::tooltip-radius = 2 + GtkWidget::tooltip-alpha = 235 + GtkWidget::new-tooltip-style = 1 #for compatibility + + GtkSeparatorMenuItem::horizontal-padding = 0 + GtkSeparatorMenuItem::wide-separators = 1 + GtkSeparatorMenuItem::separator-height = 2 + + GtkButton::child-displacement-y = 0 + GtkButton::default-border = { 0, 0, 0, 0 } + GtkButton::default-outside_border = { 0, 0, 0, 0 } + + GtkEntry::state-hint = 1 + + GtkScrollbar::trough-border = 0 + GtkRange::trough-border = 0 + GtkRange::slider-width = 13 + GtkRange::stepper-size = 0 + + GtkScrollbar::activate-slider = 1 + GtkScrollbar::has-backward-stepper = 0 + GtkScrollbar::has-forward-stepper = 0 + GtkScrollbar::min-slider-length = 32 + GtkScrolledWindow::scrollbar-spacing = 0 + GtkScrolledWindow::scrollbars-within-bevel = 1 + + GtkScale::slider_length = 20 + GtkScale::slider_width = 20 + GtkScale::trough-side-details = 1 + + GtkProgressBar::min-horizontal-bar-height = 8 + GtkProgressBar::min-vertical-bar-width = 8 + + GtkStatusbar::shadow_type = GTK_SHADOW_NONE + GtkSpinButton::shadow_type = GTK_SHADOW_NONE + GtkMenuBar::shadow-type = GTK_SHADOW_NONE + GtkToolbar::shadow-type = GTK_SHADOW_NONE + GtkMenuBar::internal-padding = 0 #( every window is misaligned for the sake of menus ): + GtkMenu::horizontal-padding = 0 + GtkMenu::vertical-padding = 0 + + GtkCheckButton::indicator_spacing = 3 + GtkOptionMenu::indicator_spacing = { 8, 2, 0, 0 } + + GtkTreeView::row_ending_details = 0 + GtkTreeView::expander-size = 11 + GtkTreeView::vertical-separator = 4 + GtkTreeView::horizontal-separator = 4 + GtkTreeView::allow-rules = 1 + GtkTreeView::odd_row_color = shade(0.98, @base_color) + + GtkExpander::expander-size = 11 + + GnomeHRef::link_color = @link_color + GtkHTML::link-color = @link_color + GtkIMHtmlr::hyperlink-color = @link_color + GtkIMHtml::hyperlink-color = @link_color + GtkWidget::link-color = @link_color + GtkWidget::visited-link-color = @text_color + + # Colors + + bg[NORMAL] = @bg_color + bg[PRELIGHT] = shade (1.0, @bg_color) + bg[SELECTED] = @selected_bg_color + bg[INSENSITIVE] = @insensitive_bg_color + bg[ACTIVE] = shade (0.9, @bg_color) + + fg[NORMAL] = @text_color + fg[PRELIGHT] = @fg_color + fg[INSENSITIVE] = @insensitive_fg_color + # fg[ACTIVE] = @fg_color + fg[SELECTED] = @selected_fg_color + + text[NORMAL] = @text_color + text[PRELIGHT] = @text_color + text[SELECTED] = @selected_fg_color + text[INSENSITIVE] = @insensitive_fg_color + text[ACTIVE] = @selected_fg_color + + base[NORMAL] = @base_color + base[PRELIGHT] = shade (0.95, @bg_color) + base[SELECTED] = @selected_bg_color + base[INSENSITIVE] = @bg_color + base[ACTIVE] = shade (0.9, @selected_bg_color) + + # For succinctness, all reasonable pixmap options remain here + # This needs to go before pixmap because we need to override some stuff + engine "adwaita" {} + + engine "pixmap" { + + # Check Buttons + + image { + function = CHECK + recolorable = TRUE + state = NORMAL + shadow = OUT + overlay_file = "assets/checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = PRELIGHT + shadow = OUT + overlay_file = "assets/checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = ACTIVE + shadow = OUT + overlay_file = "assets/checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = SELECTED + shadow = OUT + overlay_file = "assets/checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = INSENSITIVE + shadow = OUT + overlay_file = "assets/checkbox-unchecked-insensitive.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = NORMAL + shadow = IN + overlay_file = "assets/checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = PRELIGHT + shadow = IN + overlay_file = "assets/checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = ACTIVE + shadow = IN + overlay_file = "assets/checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = SELECTED + shadow = IN + overlay_file = "assets/checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = INSENSITIVE + shadow = IN + overlay_file = "assets/checkbox-checked-insensitive.png" + overlay_stretch = FALSE + } + + # Radio Buttons + + image { + function = OPTION + state = NORMAL + shadow = OUT + overlay_file = "assets/radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = PRELIGHT + shadow = OUT + overlay_file = "assets/radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = ACTIVE + shadow = OUT + overlay_file = "assets/radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = SELECTED + shadow = OUT + overlay_file = "assets/radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = INSENSITIVE + shadow = OUT + overlay_file = "assets/radio-unchecked-insensitive.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = NORMAL + shadow = IN + overlay_file = "assets/radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = PRELIGHT + shadow = IN + overlay_file = "assets/radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = ACTIVE + shadow = IN + overlay_file = "assets/radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = SELECTED + shadow = IN + overlay_file = "assets/radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = INSENSITIVE + shadow = IN + overlay_file = "assets/radio-checked-insensitive.png" + overlay_stretch = FALSE + } + + # Arrows + + image { + function = ARROW + overlay_file = "assets/arrow-up.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = UP + } + + image { + function = ARROW + state = PRELIGHT + overlay_file = "assets/arrow-up-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = UP + } + + image { + function = ARROW + state = ACTIVE + overlay_file = "assets/arrow-up-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = UP + } + + image { + function = ARROW + state = INSENSITIVE + overlay_file = "assets/arrow-up-insens.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = UP + } + + image { + function = ARROW + state = NORMAL + overlay_file = "assets/arrow-down.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = DOWN + } + + image { + function = ARROW + state = PRELIGHT + overlay_file = "assets/arrow-down-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = DOWN + } + + image { + function = ARROW + state = ACTIVE + overlay_file = "assets/arrow-down-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = DOWN + } + + image { + function = ARROW + state = INSENSITIVE + overlay_file = "assets/arrow-down-insens.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = DOWN + } + + image { + function = ARROW + overlay_file = "assets/arrow-left.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = LEFT + } + + image { + function = ARROW + state= PRELIGHT + overlay_file = "assets/arrow-left-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = LEFT + } + + image { + function = ARROW + state = ACTIVE + overlay_file = "assets/arrow-left-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = LEFT + } + + image { + function = ARROW + state = INSENSITIVE + overlay_file = "assets/arrow-left-insens.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = LEFT + } + + image { + function = ARROW + overlay_file = "assets/arrow-right.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + image { + function = ARROW + state = PRELIGHT + overlay_file = "assets/arrow-right-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + image { + function = ARROW + state = ACTIVE + overlay_file = "assets/arrow-right-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + image { + function = ARROW + state = INSENSITIVE + overlay_file = "assets/arrow-right-insens.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + # Option Menu Arrows + + image { + function = TAB + state = INSENSITIVE + overlay_file = "assets/arrow-down-insens.png" + overlay_stretch = FALSE + } + + image { + function = TAB + state = NORMAL + overlay_file = "assets/arrow-down.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } + + image { + function = TAB + state = PRELIGHT + overlay_file = "assets/arrow-down-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + } + + # Lines + + image { + function = VLINE + file = "assets/border.png" + border = { 1, 0, 0, 0 } + stretch = TRUE + } + + image { + function = HLINE + file = "assets/border.png" + border = { 0, 0, 1, 0 } + stretch = TRUE + } + + # Focuslines + + image { + function = FOCUS + file = "assets/null.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + } + + # Handles + + image { + function = HANDLE + overlay_file = "assets/handle-h.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + image { + function = HANDLE + overlay_file = "assets/handle-v.png" + overlay_stretch = FALSE + orientation = VERTICAL + } + + # Expanders + + image { + function = EXPANDER + expander_style = COLLAPSED + file = "assets/plus.png" + } + + image { + function = EXPANDER + expander_style = EXPANDED + file = "assets/minus.png" + } + + image { + function = EXPANDER + expander_style = SEMI_EXPANDED + file = "assets/minus.png" + } + + image { + function = EXPANDER + expander_style = SEMI_COLLAPSED + file = "assets/plus.png" + } + + image { + function = RESIZE_GRIP + state = NORMAL + detail = "statusbar" + overlay_file = "assets/null.png" + overlay_border = { 0,0,0,0 } + overlay_stretch = FALSE + } + + # Shadows ( this area needs help :P ) + + image { + function = SHADOW_GAP + file = "assets/null.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + } +} + + +style "toplevel_hack" { + + engine "adwaita" { + } +} + +style "ooo_stepper_hack" { + + GtkScrollbar::stepper-size = 0 + GtkScrollbar::has-backward-stepper = 0 + GtkScrollbar::has-forward-stepper = 0 + +} + +style "scrollbar" { + + engine "pixmap" { + + image { + function = BOX + detail = "trough" + file = "assets/trough-scrollbar-horiz.png" + border = { 2, 2, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = BOX + detail = "trough" + file = "assets/trough-scrollbar-vert.png" + border = { 3, 3, 2, 2 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = ARROW + overlay_file = "assets/null.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = UP + } + + image { + function = ARROW + overlay_file = "assets/null.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = DOWN + } + + image { + function = ARROW + overlay_file = "assets/null.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = LEFT + } + + image { + function = ARROW + overlay_file = "assets/null.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + # Sliders + + image { + function = SLIDER + state = NORMAL + file = "assets/slider-horiz.png" + border = { 5, 5, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = ACTIVE + file = "assets/slider-horiz-active.png" + border = { 5, 5, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = PRELIGHT + file = "assets/slider-horiz-prelight.png" + border = { 5, 5, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = INSENSITIVE + file = "assets/slider-horiz-insens.png" + border = { 5, 5, 3, 3 } + stretch = TRUE + orientation = HORIZONTAL + } + +# X Verticals + + image { + function = SLIDER + state = NORMAL + file = "assets/slider-vert.png" + border = { 3, 3, 5, 5 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = SLIDER + state = ACTIVE + file = "assets/slider-vert-active.png" + border = { 3, 3, 5, 5 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = SLIDER + state = PRELIGHT + file = "assets/slider-vert-prelight.png" + border = { 3, 3, 5, 5 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = SLIDER + state = INSENSITIVE + file = "assets/slider-vert-insens.png" + border = { 3, 3, 5, 5 } + stretch = TRUE + orientation = VERTICAL + } + } +} + +style "menu" { + + xthickness = 0 + ythickness = 0 + + GtkMenuItem::arrow-scaling = 0.4 + + bg[NORMAL] = @menu_bg + bg[INSENSITIVE] = @menu_bg + bg[PRELIGHT] = @menu_bg +} + +style "menu_framed_box" { + +# engine "adwaita" { # default menu border +# } + + engine "pixmap" { + image { + function = BOX + file = "assets/frame.png" + border = { 1, 1, 2, 1 } + stretch = TRUE + } + } + +} + +style "menu_item" { + xthickness = 4 + ythickness = 2 + + # HACK: Gtk doesn't actually read this value + # while rendering the menu items, but Libreoffice + # does; setting this value equal to the one in + # fg[PRELIGHT] ensures a code path in the LO theming code + # that falls back to a dark text color for menu item text + # highlight. The price to pay is black text on menus as well, + # but at least it's readable. + # See https://bugs.freedesktop.org/show_bug.cgi?id=38038 + bg[SELECTED] = @selected_bg_color + + fg[NORMAL] = @menu_fg + fg[SELECTED] = @selected_fg_color + + fg[PRELIGHT] = @selected_fg_color + text[PRELIGHT] = @selected_fg_color + + engine "pixmap" { + + image { + function = BOX + state = PRELIGHT + file = "assets/menuitem.png" + border = { 1, 0, 1, 0 } + stretch = TRUE + } + + # Fix invisible scale trough on selected menuitems + + image { + function = BOX + detail = "trough-lower" + file = "assets/trough-horizontal.png" + border = { 8, 8, 0, 0 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = PRELIGHT + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + # Check Buttons + + image { + function = CHECK + recolorable = TRUE + state = NORMAL + shadow = OUT + overlay_file = "assets/menu-checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = PRELIGHT + shadow = OUT + overlay_file = "assets/menu-checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = ACTIVE + shadow = OUT + overlay_file = "assets/menu-checkbox-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = INSENSITIVE + shadow = OUT + overlay_file = "assets/menu-checkbox-unchecked-insensitive.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = NORMAL + shadow = IN + overlay_file = "assets/menu-checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = PRELIGHT + shadow = IN + overlay_file = "assets/menu-checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = ACTIVE + shadow = IN + overlay_file = "assets/menu-checkbox-checked.png" + overlay_stretch = FALSE + } + + image { + function = CHECK + recolorable = TRUE + state = INSENSITIVE + shadow = IN + overlay_file = "assets/menu-checkbox-checked-insensitive.png" + overlay_stretch = FALSE + } + + # Radio Buttons + + image { + function = OPTION + state = NORMAL + shadow = OUT + overlay_file = "assets/menu-radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = PRELIGHT + shadow = OUT + overlay_file = "assets/menu-radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = ACTIVE + shadow = OUT + overlay_file = "assets/menu-radio-unchecked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = INSENSITIVE + shadow = OUT + overlay_file = "assets/menu-radio-unchecked-insensitive.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = NORMAL + shadow = IN + overlay_file = "assets/menu-radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = PRELIGHT + shadow = IN + overlay_file = "assets/menu-radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = ACTIVE + shadow = IN + overlay_file = "assets/menu-radio-checked.png" + overlay_stretch = FALSE + } + + image { + function = OPTION + state = INSENSITIVE + shadow = IN + overlay_file = "assets/menu-radio-checked-insensitive.png" + overlay_stretch = FALSE + } + + image { + function = SHADOW # This fixes boxy Qt menu items + file = "assets/null.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + + # Arrow Buttons + + image { + function = ARROW + state = NORMAL + overlay_file = "assets/menu-arrow.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + image { + function = ARROW + state = PRELIGHT + overlay_file = "assets/menu-arrow-prelight.png" + overlay_border = { 0, 0, 0, 0 } + overlay_stretch = FALSE + arrow_direction = RIGHT + } + + image { + function = BOX + state = PRELIGHT + detail = "menu_scroll_arrow_up" + file = "assets/border.png" + border = {0, 0, 1, 0} + } + + image { + function = BOX + detail = "menu_scroll_arrow_up" + file = "assets/border.png" + border = {0, 0, 1, 0} + } + + image { + function = BOX + state = PRELIGHT + detail = "menu_scroll_arrow_down" + file = "assets/border.png" + border = {1, 0, 0, 0} + } + + image { + function = BOX + detail = "menu_scroll_arrow_down" + file = "assets/border.png" + border = {1, 0, 0, 0} + } + } +} + +style "button" { + xthickness = 4 + ythickness = 4 + + engine "murrine" { textstyle = 0 } + + engine "pixmap" { + + image { + function = BOX + state = NORMAL + file = "assets/button.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = BOX + state = PRELIGHT + shadow = OUT + file = "assets/button-hover.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + # hover effect on pressed buttons + image { + function = BOX + state = PRELIGHT + shadow = IN + file = "assets/button-active-hover.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = BOX + state = ACTIVE + file = "assets/button-active.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = BOX + state = INSENSITIVE + file = "assets/button-insensitive.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + } +} + +style "toolbar_button" { + + engine "pixmap" { + + # hover effect on pressed buttons + image { + function = BOX + state = PRELIGHT + shadow = IN + file = "assets/toolbar-button-active-hover.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = BOX + state = ACTIVE + file = "assets/toolbar-button-active.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + } +} + +style "button_label" { + + fg[NORMAL] = @text_color + fg[PRELIGHT] = @fg_color + fg[INSENSITIVE] = @insensitive_button_fg_color + fg[ACTIVE] = @fg_color + + engine "murrine" { textstyle = 0 } +} + +style "checkbutton" { + + fg[PRELIGHT] = @text_color + fg[ACTIVE] = @text_color + +} + +style "link_button" { + # Disable the button effect, leave just the link + engine "pixmap" { + image { + function = BOX + } + } +} + +style "entry" { + + xthickness = 6 + ythickness = 4 + + engine "pixmap" { + + image { + function = SHADOW + state = NORMAL + detail = "entry" + file = "assets/entry-bg.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = ACTIVE + detail = "entry" + file = "assets/entry-active-bg.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = INSENSITIVE + detail = "entry" + file = "assets/entry-disabled-bg.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = FLAT_BOX + state = ACTIVE + detail = "entry_bg" + file = "assets/entry-background.png" + } + + image { + function = FLAT_BOX + state = INSENSITIVE + detail = "entry_bg" + file = "assets/entry-background-disabled.png" + } + + image { + function = FLAT_BOX + detail = "entry_bg" + file = "assets/entry-background.png" + } + } +} + +style "notebook_entry" { + + engine "pixmap" { + + image { + function = SHADOW + state = NORMAL + detail = "entry" + file = "assets/entry-notebook.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = ACTIVE + detail = "entry" + file = "assets/entry-active-notebook.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = INSENSITIVE + detail = "entry" + file = "assets/entry-disabled-notebook.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + } +} + +style "notebook_button_bg" { + + bg[NORMAL] = @notebook_bg + bg[PRELIGHT] = @notebook_bg + bg[INSENSITIVE] = @notebook_bg + bg[ACTIVE] = @notebook_bg + +} + +style "notebook_tab_label" { + + fg[NORMAL] = @text_color + fg[PRELIGHT] = @fg_color + fg[INSENSITIVE] = @insensitive_fg_color + fg[ACTIVE] = @text_color + +} + +style "combobox_entry" { + + xthickness = 3 + ythickness = 4 + + engine "pixmap" { + + # LTR version + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = INSENSITIVE + shadow = IN + file = "assets/combo-entry-insensitive.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-focus.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + # RTL version + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = SHADOW + detail = "entry" + state = INSENSITIVE + shadow = IN + file = "assets/combo-entry-insensitive-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-focus-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + } +} + +style "notebook_combobox_entry" { + + engine "pixmap" { + + # LTR version + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry-notebook.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = INSENSITIVE + shadow = IN + file = "assets/combo-entry-insensitive-notebook.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-focus-notebook.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + # RTL version + + image { + function = SHADOW + detail = "entry" + state = NORMAL + shadow = IN + file = "assets/combo-entry-notebook-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = SHADOW + detail = "entry" + state = INSENSITIVE + shadow = IN + file = "assets/combo-entry-insensitive-notebook-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = SHADOW + detail = "entry" + state = ACTIVE + file = "assets/combo-entry-focus-notebook-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + } +} + +style "combobox_entry_button" { + + xthickness = 6 + + fg[ACTIVE] = @text_color + + engine "pixmap" { + + # LTR version + + image { + function = BOX + state = NORMAL + file = "assets/combo-entry-button.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = BOX + state = PRELIGHT + file = "assets/combo-entry-button.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = BOX + state = INSENSITIVE + file = "assets/combo-entry-button-insensitive.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + image { + function = BOX + state = ACTIVE + file = "assets/combo-entry-button-active.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = LTR + } + + # RTL version + image { + function = BOX + state = NORMAL + file = "assets/combo-entry-button-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = BOX + state = PRELIGHT + file = "assets/combo-entry-button-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = BOX + state = INSENSITIVE + file = "assets/combo-entry-button-insensitive-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + + image { + function = BOX + state = ACTIVE + file = "assets/combo-entry-button-active-rtl.png" + border = { 4, 4, 5, 4 } + stretch = TRUE + direction = RTL + } + } +} + +style "spinbutton" { + + bg[NORMAL] = @bg_color + + xthickness = 6 + ythickness = 4 + + engine "pixmap" { + + image { + function = ARROW + } + + # Spin-Up LTR + + image { + function = BOX + state = NORMAL + detail = "spinbutton_up" + file = "assets/up-background.png" + border = { 1, 4, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_up" + file = "assets/up-background.png" + border = { 1, 4, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_up" + file = "assets/up-background-disable.png" + border = { 1, 4, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-insens.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_up" + file = "assets/up-background.png" + border = { 1, 4, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + direction = LTR + } + + # Spin-Up RTL + + image { + function = BOX + state = NORMAL + detail = "spinbutton_up" + file = "assets/up-background-rtl.png" + border = { 4, 1, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_up" + file = "assets/up-background-rtl.png" + border = { 4, 1, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_up" + file = "assets/up-background-disable-rtl.png" + border = { 4, 1, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-insens.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_up" + file = "assets/up-background-rtl.png" + border = { 4, 1, 5, 0 } + stretch = TRUE + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + direction = RTL + } + + # Spin-Down LTR + + image { + function = BOX + state = NORMAL + detail = "spinbutton_down" + file = "assets/down-background.png" + border = { 1, 4, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_down" + file = "assets/down-background.png" + border = { 1, 4, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_down" + file = "assets/down-background-disable.png" + border = { 1, 4, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-insens.png" + overlay_stretch = FALSE + direction = LTR + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_down" + file = "assets/down-background.png" + border = { 1, 4, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + direction = LTR + } + + # Spin-Down RTL + + image { + function = BOX + state = NORMAL + detail = "spinbutton_down" + file = "assets/down-background-rtl.png" + border = { 4, 1, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_down" + file = "assets/down-background-rtl.png" + border = { 4, 1, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_down" + file = "assets/down-background-disable-rtl.png" + border = { 4, 1, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-insens.png" + overlay_stretch = FALSE + direction = RTL + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_down" + file = "assets/down-background-rtl.png" + border = { 4, 1, 1, 4 } + stretch = TRUE + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + direction = RTL + } + } +} + +style "gimp_spin_scale" { + + bg[NORMAL] = @base_color + + engine "pixmap" { + + image { + function = FLAT_BOX + detail = "entry_bg" + state = NORMAL + } + + image { + function = FLAT_BOX + detail = "entry_bg" + state = ACTIVE + } + + image { + function = BOX + state = NORMAL + detail = "spinbutton_up" + overlay_file = "assets/arrow-up-small.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_up" + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_up" + overlay_file = "assets/arrow-up-small-prelight.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_up" + overlay_file = "assets/arrow-up-small-insens.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = NORMAL + detail = "spinbutton_down" + overlay_file = "assets/arrow-down-small.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = PRELIGHT + detail = "spinbutton_down" + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = ACTIVE + detail = "spinbutton_down" + overlay_file = "assets/arrow-down-small-prelight.png" + overlay_stretch = FALSE + } + + image { + function = BOX + state = INSENSITIVE + detail = "spinbutton_down" + overlay_file = "assets/arrow-down-small-insens.png" + overlay_stretch = FALSE + } + } +} + +style "notebook" { + + xthickness = 5 + ythickness = 2 + + engine "pixmap" { + + image { + function = EXTENSION + state = ACTIVE + file = "assets/null.png" + border = { 0,0,0,0 } + stretch = TRUE + gap_side = TOP + } + + image { + function = EXTENSION + state = ACTIVE + file = "assets/null.png" + border = { 0,0,0,0 } + stretch = TRUE + gap_side = BOTTOM + } + + image { + function = EXTENSION + state = ACTIVE + file = "assets/null.png" + border = { 0,0,0,0 } + stretch = TRUE + gap_side = RIGHT + } + + image { + function = EXTENSION + state = ACTIVE + file = "assets/null.png" + border = { 0,0,0,0 } + stretch = TRUE + gap_side = LEFT + } + + image { + function = EXTENSION + file = "assets/tab-top-active.png" + border = { 3,3,3,3 } + stretch = TRUE + gap_side = BOTTOM + } + + image { + function = EXTENSION + file = "assets/tab-bottom-active.png" + border = { 3,3,3,3 } + stretch = TRUE + gap_side = TOP + } + + image { + function = EXTENSION + file = "assets/tab-left-active.png" + border = { 3,3,3,3 } + stretch = TRUE + gap_side = RIGHT + } + + image { + function = EXTENSION + file = "assets/tab-right-active.png" + border = { 3,3,3,3 } + stretch = TRUE + gap_side = LEFT + } + + # How to draw boxes with a gap on one side (ie the page of a notebook) + + image { + function = BOX_GAP + file = "assets/notebook.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + gap_file = "assets/notebook-gap-horiz.png" + gap_border = { 1, 1, 0, 0 } + gap_side = TOP + } + + image { + function = BOX_GAP + file = "assets/notebook.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + gap_file = "assets/notebook-gap-horiz.png" + gap_border = { 1, 1, 0, 0 } + gap_side = BOTTOM + } + + image { + function = BOX_GAP + file = "assets/notebook.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + gap_file = "assets/notebook-gap-vert.png" + gap_border = { 0, 0, 1, 1 } + gap_side = LEFT + } + + image { + function = BOX_GAP + file = "assets/notebook.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + gap_file = "assets/notebook-gap-vert.png" + gap_border = { 0, 0, 1, 1 } + gap_side = RIGHT + } + + # How to draw the box of a notebook when it isnt attached to a tab + + image { + function = BOX + file = "assets/notebook.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + } +} + +style "handlebox" { + + engine "pixmap" { + + image { + function = BOX + file = "assets/null.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + detail = "handlebox_bin" + shadow = IN + } + + image { + function = BOX + file = "assets/null.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + detail = "handlebox_bin" + shadow = OUT + } + } +} + +style "combobox_separator" { + + xthickness = 0 + ythickness = 0 + GtkWidget::wide-separators = 1 + +} + +style "combobox" { + + xthickness = 0 + ythickness = 0 + +} + +style "combobox_button" { + + xthickness = 3 + ythickness = 3 + +} + +style "range" { + + engine "pixmap" { + + image { + function = BOX + detail = "trough-upper" + file = "assets/trough-horizontal.png" + border = { 8, 8, 0, 0 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = BOX + detail = "trough-lower" + file = "assets/trough-horizontal-active.png" + border = { 8, 8, 0, 0 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = BOX + detail = "trough-upper" + file = "assets/trough-vertical.png" + border = { 0, 0, 8, 8 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = BOX + detail = "trough-lower" + file = "assets/trough-vertical-active.png" + border = { 0, 0, 8, 8 } + stretch = TRUE + orientation = VERTICAL + } + + # Horizontal + + image { + function = SLIDER + state = NORMAL + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = PRELIGHT + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider-prelight.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + image { + function = SLIDER + state = INSENSITIVE + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider-insensitive.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + # Vertical + + image { + function = SLIDER + state = NORMAL + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider.png" + overlay_stretch = FALSE + orientation = VERTICAL + } + + image { + function = SLIDER + state = PRELIGHT + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider-prelight.png" + overlay_stretch = FALSE + orientation = VERTICAL + } + + image { + function = SLIDER + state = INSENSITIVE + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + overlay_file = "assets/slider-insensitive.png" + overlay_stretch = FALSE + orientation = VERTICAL + } + + # Function below removes ugly boxes + + image { + function = BOX + file = "assets/null.png" + border = { 3, 3, 3, 3 } + stretch = TRUE + } + } +} + +style "progressbar" { + + xthickness = 1 + ythickness = 1 + + fg[NORMAL] = @fg_color + fg[PRELIGHT] = @selected_fg_color + + engine "pixmap" { + + image { + function = BOX + detail = "trough" + file = "assets/trough-progressbar.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + orientation = HORIZONTAL + } + + image { + function = BOX + detail = "bar" + file = "assets/progressbar.png" + stretch = TRUE + border = { 3, 3, 3, 3 } + orientation = HORIZONTAL + } + + image { + function = BOX + detail = "trough" + file = "assets/trough-progressbar_v.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + orientation = VERTICAL + } + + image { + function = BOX + detail = "bar" + file = "assets/progressbar_v.png" + stretch = TRUE + border = { 3, 3, 3, 3 } + orientation = VERTICAL + } + } +} + +style "separator_menu_item" { + xthickness = 0 + ythickness = 2 + + engine "pixmap" { + image { + function = BOX + file = "assets/menu-separator.png" + border = {0, 0, 2, 0} + } + } +} + +style "treeview_header" { + ythickness = 1 + + fg[PRELIGHT] = mix(0.70, @text_color, @base_color) + font_name = "Bold" + + engine "pixmap" { + + image { + function = BOX + file = "assets/tree_header.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + } + } +} + +# Treeview Rows + +style "treeview" { + + xthickness = 2 + ythickness = 0 + +} + +style "scrolled_window" { + + xthickness = 1 + ythickness = 1 + + engine "pixmap" { + + image { + function = SHADOW + file = "assets/frame.png" + border = { 5, 5, 5, 5 } + stretch = TRUE + } + } +} + +style "frame" { + + xthickness = 1 + ythickness = 1 + + engine "pixmap" { + + image { + function = SHADOW + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + shadow = IN + } + + image { + function = SHADOW_GAP + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + gap_start_file = "assets/frame-gap-start.png" + gap_start_border = { 1, 0, 0, 0 } + gap_end_file = "assets/frame-gap-end.png" + gap_end_border = { 0, 1, 0, 0 } + shadow = IN + } + + image { + function = SHADOW + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + shadow = OUT + } + + image { + function = SHADOW_GAP + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + gap_start_file = "assets/frame-gap-start.png" + gap_start_border = { 1, 0, 0, 0 } + gap_end_file = "assets/frame-gap-end.png" + gap_end_border = { 0, 1, 0, 0 } + shadow = OUT + } + + image { + function = SHADOW + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + shadow = ETCHED_IN + } + + image { + function = SHADOW_GAP + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + gap_start_file = "assets/frame-gap-start.png" + gap_start_border = { 1, 0, 0, 0 } + gap_end_file = "assets/frame-gap-end.png" + gap_end_border = { 0, 1, 0, 0 } + shadow = ETCHED_IN + } + + image { + function = SHADOW + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + shadow = ETCHED_OUT + } + + image { + function = SHADOW_GAP + file = "assets/frame.png" + border = { 1, 1, 1, 1 } + stretch = TRUE + gap_start_file = "assets/frame-gap-start.png" + gap_start_border = { 1, 0, 0, 0 } + gap_end_file = "assets/frame-gap-end.png" + gap_end_border = { 0, 1, 0, 0 } + shadow = ETCHED_OUT + } + } +} + +style "gimp_toolbox_frame" { + + engine "pixmap" { + + image { + function = SHADOW + } + + } +} + +style "toolbar" { + + engine "pixmap" { + + image { + function = BOX + file = "assets/toolbar.png" + stretch = TRUE + border = { 1, 1, 1, 1 } + } + + image { + function = HANDLE + overlay_file = "assets/handle-h.png" + overlay_stretch = FALSE + orientation = HORIZONTAL + } + + image { + function = HANDLE + overlay_file = "assets/handle-v.png" + overlay_stretch = FALSE + orientation = VERTICAL + } + + ######### + # Lines # + ######### + + image { + function = VLINE + file = "assets/border.png" + border = {1, 0, 0, 0} + } + + image { + function = HLINE + file = "assets/border.png" + border = {0, 0, 1, 0} + } + } +} + +style "toolbar_separator" { + GtkWidget::wide-separators = 1 + GtkWidget::separator-width = 1 + GtkWidget::separator-height = 1 + + engine "pixmap" { + image { + function = BOX + file = "assets/border.png" + } + } +} + +style "inline_toolbar" { + + GtkToolbar::button-relief = GTK_RELIEF_NORMAL + + engine "pixmap" { + + image { + function = BOX + file = "assets/inline-toolbar.png" + stretch = TRUE + border = { 1, 1, 1, 1 } + } + } +} + +style "notebook_viewport" { + + bg[NORMAL] = @notebook_bg +} + + +style "notebook_eventbox" { + + bg[NORMAL] = @notebook_bg + bg[ACTIVE] = @bg_color +} + +style "tooltips" { + + xthickness = 8 + ythickness = 4 + + bg[NORMAL] = @tooltip_bg_color + fg[NORMAL] = @tooltip_fg_color + bg[SELECTED] = @tooltip_bg_color + +} + +style "eclipse-tooltips" { + + xthickness = 8 + ythickness = 4 + + bg[NORMAL] = shade(1.05, @bg_color) + fg[NORMAL] = @text_color + bg[SELECTED] = shade(1.05, @bg_color) + +} + +style "xfdesktop-icon-view" { + XfdesktopIconView::label-alpha = 0 + XfdesktopIconView::selected-label-alpha = 100 + XfdesktopIconView::shadow-x-offset = 0 + XfdesktopIconView::shadow-y-offset = 1 + XfdesktopIconView::selected-shadow-x-offset = 0 + XfdesktopIconView::selected-shadow-y-offset = 1 + XfdesktopIconView::shadow-color = "#000000" + XfdesktopIconView::selected-shadow-color = "#000000" + XfdesktopIconView::shadow-blur-radius = 2 + XfdesktopIconView::cell-spacing = 2 + XfdesktopIconView::cell-padding = 6 + XfdesktopIconView::cell-text-width-proportion = 1.9 + + fg[NORMAL] = @selected_fg_color + fg[ACTIVE] = @selected_fg_color +} + +style "xfwm-tabwin" { + Xfwm4TabwinWidget::border-width = 1 + Xfwm4TabwinWidget::border-alpha = 1.0 + Xfwm4TabwinWidget::icon-size = 64 + Xfwm4TabwinWidget::alpha = 1.0 + Xfwm4TabwinWidget::border-radius = 2 + + bg[NORMAL] = @bg_color + bg[SELECTED] = @bg_color + + fg[NORMAL] = @fg_color + + engine "murrine" { + contrast = 0.7 + glazestyle = 0 + glowstyle = 0 + highlight_shade = 1.0 + gradient_shades = {1.0,1.0,1.0,1.0} + border_shades = { 0.8, 0.8 } + } +} + +style "xfwm-tabwin-button" { + font_name = "bold" + bg[SELECTED] = @selected_bg_color +} + +# Chromium +style "chrome_menu_item" { + + bg[SELECTED] = @selected_bg_color + +} + +# Text Style +style "text" = "default" { + fg[NORMAL] = @fg_color # FIXME: VMWare needs this? + + engine "murrine" { textstyle = 0 } +} + +style "menu_text" = "menu_item" { + engine "murrine" { textstyle = 0 } +} + +style "null" { + + engine "pixmap" { + + image { + function = BOX + file = "assets/null.png" + stretch = TRUE + } + } +} + + +class "GtkWidget" style "default" +class "GtkScrollbar" style "scrollbar" +class "GtkButton" style "button" +class "GtkLinkButton" style "link_button" +class "GtkEntry" style "entry" +class "GtkOldEditable" style "entry" +class "GtkSpinButton" style "spinbutton" +class "GtkNotebook" style "notebook" +class "GtkRange" style "range" +class "GtkProgressBar" style "progressbar" +class "GtkScrolledWindow" style "scrolled_window" +class "GtkFrame" style "frame" +class "GtkTreeView" style "treeview" +class "GtkToolbar" style "toolbar" +class "*HandleBox" style "toolbar" + +widget_class "**" style "menu" +widget_class "**" style "menu_framed_box" +widget_class "**" style "menu_item" +widget_class "**" style "separator_menu_item" +widget_class "**" style "checkbutton" +widget_class "*" style "combobox" +widget_class "**" style "combobox_button" +widget_class "**" style "combobox_separator" +widget_class "***" style "treeview_header" +widget_class "**" style "inline_toolbar" +widget_class "**" style "combobox_entry" +widget_class "**" style "combobox_entry_button" +widget_class "***" style "notebook_viewport" +widget_class "*HandleBox" style "toolbar" + +widget_class "**" style "button_label" +widget_class "**" style "button_label" +#widget_class "**" style "button_label" +#widget_class "**" style "button_label" + +widget_class "**" style "toolbar_button" +widget_class "***" style "button_label" + +widget_class "*" style "toolbar_button" +widget_class "**" style "button_label" + +# Entries in notebooks draw with notebook's base color, but not if there's +# something else in the middle that draws gray again +widget_class "**" style "notebook_entry" +widget_class "***" style "entry" + +widget_class "***" style "notebook_combobox_entry" +widget_class "****" style "combobox_entry" + +widget_class "**" style "notebook_button_bg" + +# We also need to avoid changing fg color for the inactive notebook tab labels +widget_class "**" style "notebook_tab_label" +widget_class "***" style "button_label" + +# GTK tooltips +widget "gtk-tooltip*" style "tooltips" + +#Fix GVim tabs +widget_class "**" style "notebook_eventbox" + +# Xchat special cases +widget "*xchat-inputbox" style "entry" + +# GIMP +# Disable gradients completely for GimpSpinScale +#class "GimpSpinScale" style "gimp_spin_scale" + +# Remove borders from "Wilbert frame" in Gimp +widget_class "**" style "gimp_toolbox_frame" + +# Chrome/Chromium +widget_class "*Chrom*Button*" style "button" +widget_class "***" style "chrome_menu_item" + +# Eclipse/SWT +widget "gtk-tooltips*" style "eclipse-tooltips" +widget "*swt-toolbar-flat" style "null" + +# Openoffice, Libreoffice +class "GtkWindow" style "toplevel_hack" +widget "*openoffice-toplevel*" style "ooo_stepper_hack" + +# Xfce +widget_class "*XfdesktopIconView*" style "xfdesktop-icon-view" +widget "xfwm4-tabwin*" style "xfwm-tabwin" +widget "xfwm4-tabwin*GtkButton*" style "xfwm-tabwin-button" + +# Fixes ugly text shadows for insensitive text +widget_class "*" style "text" +widget_class "**" style "menu_text" +widget_class "**" style "text" +widget_class "**" style "text" +widget_class "**" style "text" diff --git a/assets/themes/NexusOS/gtk-2.0/menubar-toolbar.rc b/assets/themes/NexusOS/gtk-2.0/menubar-toolbar.rc new file mode 100644 index 0000000..55d05ff --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/menubar-toolbar.rc @@ -0,0 +1,212 @@ +style "menubar" { + + bg[NORMAL] = @tooltip_bg_color + fg[NORMAL] = @tooltip_fg_color + fg[PRELIGHT] = shade(1.15, @tooltip_fg_color) + fg[ACTIVE] = shade(1.15, @tooltip_fg_color) + fg[SELECTED] = @selected_fg_color + fg[INSENSITIVE] = shade(0.7, @tooltip_fg_color) + + xthickness = 0 + ythickness = 0 + + engine "pixmap" { + + image { + function = BOX + file = "assets/menubar.png" + stretch = TRUE + border = { 1, 1, 1, 1 } + } + } +} + +style "menubar-borderless" { + + bg[NORMAL] = @tooltip_bg_color + fg[NORMAL] = @tooltip_fg_color + fg[SELECTED] = @selected_fg_color + fg[INSENSITIVE] = shade(0.7, @tooltip_fg_color) + + xthickness = 0 + ythickness = 0 + + engine "pixmap" { + + image { + function = BOX + file = "assets/null.png" + stretch = TRUE + border = { 1, 1, 1, 1 } + } + } +} + +style "menubar_item" { + + xthickness = 2 + ythickness = 2 + + fg[PRELIGHT] = @selected_fg_color + + engine "pixmap" { + + image { + function = BOX + state = PRELIGHT + file = "assets/menubar_button.png" + border = { 2, 2, 2, 2 } + stretch = TRUE + } + } +} + +style "toolbar_text" { + fg[NORMAL] = @tooltip_fg_color + fg[PRELIGHT] = shade(1.15, @tooltip_fg_color) + fg[INSENSITIVE] = shade(0.7, @tooltip_fg_color) + fg[ACTIVE] = shade(0.9, @tooltip_fg_color) + + text[NORMAL] = @tooltip_fg_color + text[PRELIGHT] = shade(1.15, @tooltip_fg_color) + text[INSENSITIVE] = shade(0.7, @tooltip_fg_color) + text[ACTIVE] = shade(0.9, @tooltip_fg_color) + +} + +style "toolbar_button" { + + xthickness = 4 + ythickness = 4 + + engine "pixmap" { + + image { + function = BOX + state = NORMAL + file = "assets/button.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + + image { + function = BOX + state = PRELIGHT + file = "assets/button-hover.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + + image { + function = BOX + state = ACTIVE + file = "assets/button-active.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + + image { + function = BOX + state = INSENSITIVE + file = "assets/button-insensitive.png" + border = { 4, 4, 4, 4 } + stretch = TRUE + } + } +} + +style "toolbar_entry" { + + base[NORMAL] = @base_color + base[ACTIVE] = @base_color + base[INSENSITIVE] = @insensitive_bg_color + + text[NORMAL] = @text_color + + engine "pixmap" { + + image { + function = SHADOW + state = NORMAL + detail = "entry" + file = "assets/entry-toolbar.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = ACTIVE + detail = "entry" + file = "assets/entry-active-toolbar.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = SHADOW + state = INSENSITIVE + detail = "entry" + file = "assets/entry-disabled-toolbar.png" + border = {6, 6, 6, 6} + stretch = TRUE + } + + image { + function = FLAT_BOX + state = ACTIVE + detail = "entry_bg" + file = "assets/null.png" + } + + image { + function = FLAT_BOX + state = INSENSITIVE + detail = "entry_bg" + file = "assets/null.png" + } + + image { + function = FLAT_BOX + detail = "entry_bg" + file = "assets/null.png" + } + } +} + +#Chromium +style "chrome-gtk-frame" { + + ChromeGtkFrame::frame-color = @tooltip_bg_color + ChromeGtkFrame::inactive-frame-color = @tooltip_bg_color + + ChromeGtkFrame::frame-gradient-size = 0 + ChromeGtkFrame::frame-gradient-color = shade(0.5, @bg_color) + + ChromeGtkFrame::incognito-frame-color = shade(0.85, @bg_color) + ChromeGtkFrame::incognito-inactive-frame-color = @bg_color + + ChromeGtkFrame::incognito-frame-gradient-color = @bg_color + + ChromeGtkFrame::scrollbar-trough-color = shade(0.912, @bg_color) + ChromeGtkFrame::scrollbar-slider-prelight-color = shade(1.04, @bg_color) + ChromeGtkFrame::scrollbar-slider-normal-color = @bg_color + +} + +widget_class "**" style "menubar" +widget_class "*.*" style "menubar_item" + +widget_class "*ThunarWindow*" style "menubar" + +class "ChromeGtkFrame" style "chrome-gtk-frame" + +# Whitelist for dark toolbars +widget_class "*ThunarWindow*" style "menubar-borderless" +widget_class "*ThunarWindow**" style "toolbar_entry" +widget_class "*ThunarWindow**" style "toolbar_button" +widget_class "*ThunarWindow**" style "toolbar_text" + +# GtkCheckButton +widget_class "*" style "button" + diff --git a/assets/themes/NexusOS/gtk-2.0/panel.rc b/assets/themes/NexusOS/gtk-2.0/panel.rc new file mode 100644 index 0000000..178128b --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/panel.rc @@ -0,0 +1,188 @@ +style "theme-panel" { + GtkButton::inner-border = { 0, 0, 0, 0 } + xthickness = 2 + ythickness = 0 + + bg[NORMAL] = shade(1.0, @tooltip_bg_color) + bg[ACTIVE] = @selected_bg_color + bg[PRELIGHT] = shade(1.2, @tooltip_bg_color) + bg[SELECTED] = @selected_bg_color + + fg[NORMAL] = shade(1.0, @tooltip_fg_color) + fg[PRELIGHT] = @fg_color + fg[ACTIVE] = @tooltip_fg_color + fg[SELECTED] = @tooltip_fg_color + + text[NORMAL] = shade(1.0, @tooltip_fg_color) + text[PRELIGHT] = shade(1.1, @tooltip_fg_color) + text[ACTIVE] = shade(1.0, @tooltip_fg_color) + text[SELECTED] = @tooltip_fg_color + + engine "pixmap" { + image { + function = SHADOW + file = "assets/null.png" + border = { 0, 0, 0, 0 } + stretch = TRUE + } + } +} + +style "theme-panel-progressbar" { + bg[ACTIVE] = shade(0.8, @tooltip_bg_color) +} + +style "panelbar" { + fg[NORMAL] = shade(1.0, @tooltip_fg_color) + fg[ACTIVE] = shade(1.0, @tooltip_fg_color) + fg[PRELIGHT] = shade(1.1, @tooltip_fg_color) + fg[SELECTED] = @tooltip_fg_color +} + +style "panelbuttons" { + GtkButton::inner-border = { 0, 0, 0, 0 } + xthickness = 4 + ythickness = 0 + + fg[NORMAL] = shade(0.8, @tooltip_fg_color) + fg[PRELIGHT] = @tooltip_fg_color + fg[ACTIVE] = @tooltip_fg_color + fg[SELECTED] = @tooltip_fg_color + fg[INSENSITIVE] = mix(0.28, @tooltip_fg_color, @tooltip_bg_color) + bg[PRELIGHT] = shade(1.2, @tooltip_bg_color) + bg[ACTIVE] = shade(1.5, @tooltip_bg_color) + + engine "pixmap" { + image { + function = BOX + state = NORMAL + file = "assets/null.png" + border = { 0, 0, 0, 2 } + stretch = TRUE + } + image { + function = BOX + state = ACTIVE + file = "assets/pathbar_button_active.png" + border = { 0, 0, 0, 2 } + stretch = TRUE + } + image { + function = BOX + state = PRELIGHT + file = "assets/pathbar_button_prelight.png" + border = { 0, 0, 0, 2 } + stretch = TRUE + } + image { + function = BOX + state = INSENSITIVE + file = "assets/null.png" + border = { 0, 0, 0, 2 } + stretch = TRUE + } + } +} + +style "regular-label" { + font_name = "Regular" +} + +style "theme-panel-text" { + fg[NORMAL] = shade(1.0, @tooltip_fg_color) + fg[PRELIGHT] = @tooltip_fg_color + fg[ACTIVE] = shade(1.0, @tooltip_fg_color) + + text[NORMAL] = shade(1.0, @tooltip_fg_color) + text[PRELIGHT] = @tooltip_fg_color + text[ACTIVE] = shade(1.0, @tooltip_fg_color) +} + +style "panel-entry" { + fg[NORMAL] = @text_color + fg[PRELIGHT] = @text_color + fg[ACTIVE] = @text_color + fg[SELECTED] = @text_color + fg[INSENSITIVE] = @text_color + + text[NORMAL] = @text_color + text[PRELIGHT] = @text_color + text[ACTIVE] = @text_color + text[SELECTED] = @text_color + text[INSENSITIVE] = @text_color +} + +style "theme-main-menu-text" = "theme-panel-text" { + fg[PRELIGHT] = @tooltip_fg_color + text[PRELIGHT] = @tooltip_fg_color +} + +style "workspace-switcher" = "theme-panel" { + fg[SELECTED] = @selected_fg_color + bg[SELECTED] = @selected_bg_color +} + +style "indicator" = "theme-panel" { + xthickness = 0 + ythickness = 0 +} + +widget "*tasklist*" style "panelbuttons" +widget_class "*Xfce*Panel*.GtkToggleButton" style "panelbuttons" +widget_class "*Xfce*NetkTasklist*GtkToggleButton" style "panelbuttons" +widget_class "*PanelToplevel*Button" style "panelbuttons" +widget_class "*Panel*GtkToggleButton" style "panelbuttons" +widget_class "*Xfce*Panel*Button*" style "panelbuttons" +widget_class "*" style "panelbuttons" +widget_class "**" style "panelbuttons" +widget_class "*XfcePanelPlugin.GtkButton" style "panelbuttons" +widget_class "*XfcePanelPlugin.GtkToggleButton" style "panelbuttons" +widget "*dict*Applet*" style "panelbuttons" +widget_class "*Xfce*NetkTasklist*GtkToggleButton" style "panelbuttons" +widget_class "*Tasklist*" style:highest "panelbuttons" +widget_class "*Tasklist*.GtkLabel" style:highest "regular-label" +widget_class "*Mixer*lugin*" style:highest "panelbuttons" + +class "*Panel*MenuBar*" style "panelbar" +widget_class "*Panel*MenuBar*" style "panelbar" +widget_class "*Panel*MenuBar*Item*" style:highest "panelbar" + +widget "*PanelWidget*" style "theme-panel" +widget "*PanelApplet*" style "theme-panel" +widget "*fast-user-switch*" style "theme-panel" +widget "*CPUFreq*Applet*" style "theme-panel" +class "PanelApp*" style "theme-panel" +class "PanelToplevel*" style "theme-panel" +widget_class "*PanelToplevel*" style "theme-panel" +widget_class "*notif*" style "theme-panel" +widget_class "*Notif*" style "theme-panel" +widget_class "*Tray*" style "theme-panel" +widget_class "*tray*" style "theme-panel" +widget_class "*computertemp*" style "theme-panel" +widget_class "*Applet*Tomboy*" style "theme-panel" +widget_class "*Applet*Netstatus*" style "theme-panel" + +# Fixes for tooltip text in some apps. +widget_class "*Notif*Beagle*" style "theme-panel" +widget_class "*Notif*Brasero*" style "theme-panel" + +# XFCE panel theming. +widget "*Xfce*Panel*" style "theme-panel" +class "*Xfce*Panel*" style "theme-panel" +widget "*Xfce*Panel*GtkProgressBar" style "theme-panel-progressbar" +widget "*WnckPager*" style "workspace-switcher" +widget "*TopMenu*" style "theme-panel" +widget "*XfceTasklist*" style "panelbuttons" + +# Fix gtk-entries in the panel +widget "*bookmark*GtkEntry" style "panel-entry" # fixes smartbookmark-plugin + +# Make sure panel text color doesn't change +widget_class "*Panel*MenuBar*" style "theme-main-menu-text" +widget_class "*Panel**" style "theme-main-menu-text" +widget "*.clock-applet-button.*" style "theme-panel-text" +widget "*PanelApplet*" style "theme-panel-text" + +# Override general panel-style with specific plugin-styles +widget "*indicator-applet*" style "indicator" +widget "*indicator-button*" style "indicator" diff --git a/assets/themes/NexusOS/gtk-2.0/xfce-notify.rc b/assets/themes/NexusOS/gtk-2.0/xfce-notify.rc new file mode 100644 index 0000000..3ac7956 --- /dev/null +++ b/assets/themes/NexusOS/gtk-2.0/xfce-notify.rc @@ -0,0 +1,52 @@ + +style "notify-window" { + XfceNotifyWindow::summary-bold = 1 + XfceNotifyWindow::border-color = shade(1.3, @tooltip_bg_color) + XfceNotifyWindow::border-color-hover = shade(1.3, @tooltip_bg_color) + XfceNotifyWindow::border-radius = 3.0 + XfceNotifyWindow::border-width = 1.0 + XfceNotifyWindow::border-width-hover = 1.0 + + bg[NORMAL] = @tooltip_bg_color +} + +style "notify-button" { + bg[NORMAL] = shade(1.1, @tooltip_bg_color) + bg[PRELIGHT] = shade(1.2, @tooltip_bg_color) + bg[ACTIVE] = shade(1.15, @tooltip_bg_color) + + fg[NORMAL] = @tooltip_fg_color + fg[PRELIGHT] = shade(1.1, @tooltip_fg_color) + fg[ACTIVE] = @selected_fg_color +} + +style "notify-text" { + GtkWidget::link-color = @selected_bg_color + + fg[NORMAL] = shade(1.0, @tooltip_fg_color) + fg[PRELIGHT] = shade(1.1, @tooltip_fg_color) + fg[ACTIVE] = shade(1.0, @tooltip_fg_color) +} + +style "notify-summary" { + font_name = "Bold" +} + +style "notify-progressbar" { + GtkProgressBar::min-horizontal-bar-height = 4 + + xthickness = 0 + ythickness = 0 + + fg[PRELIGHT] = shade(0.8, @tooltip_bg_color) + bg[NORMAL] = @selected_bg_color + bg[ACTIVE] = shade(0.8, @tooltip_bg_color) + bg[SELECTED] = @selected_bg_color +} + +class "XfceNotifyWindow" style "notify-window" +widget "XfceNotifyWindow.*.summary" style "notify-summary" +widget_class "XfceNotifyWindow.*" style "notify-button" +widget_class "XfceNotifyWindow.*." style "notify-text" +widget_class "XfceNotifyWindow.*." style "notify-progressbar" +widget_class "XfceNotifyWindow.*." style "notify-progressbar" diff --git a/assets/themes/NexusOS/gtk-3.0/buttons-entries.css b/assets/themes/NexusOS/gtk-3.0/buttons-entries.css new file mode 100644 index 0000000..9fa0d9c --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/buttons-entries.css @@ -0,0 +1,342 @@ +/* NexusOS — Segment 2: buttons & entries. + Foundation widgets. Lime green = primary accent (hover/active/focus), + purple = selection within text. */ + +/* ── Buttons ─────────────────────────────────────────────── */ + +button { + min-height: 24px; + min-width: 16px; + padding: 6px 12px; + border: 1px solid @border_strong; + border-radius: 6px; + background-color: @surface_bg; + background-image: none; + color: @text_primary; + transition: background-color 120ms ease, + border-color 120ms ease, + color 120ms ease, + box-shadow 120ms ease; + text-shadow: none; + -gtk-icon-shadow: none; +} + +button:hover { + background-color: @hover_bg; + border-color: @brand_green_dark; + color: @text_primary; +} + +button:active, +button:checked { + background-color: @active_bg; + border-color: @brand_green_dark; + color: @active_fg; +} + +button:checked:hover { + background-color: @brand_green_light; + border-color: @brand_green; + color: @active_fg; +} + +button:focus, +button:focus-visible { + outline: 2px solid @focus_ring; + outline-offset: -1px; + border-color: @brand_green; +} + +button:disabled, +button:disabled:hover, +button:disabled:active { + background-color: @surface_bg; + border-color: @border; + color: @text_disabled; + -gtk-icon-effect: dim; +} + +/* Flat buttons — transparent until interacted with */ +button.flat { + background-color: transparent; + border-color: transparent; + box-shadow: none; +} + +button.flat:hover { + background-color: @hover_bg; + border-color: transparent; +} + +button.flat:active, +button.flat:checked { + background-color: @active_bg; + border-color: transparent; + color: @active_fg; +} + +button.flat:disabled { + background-color: transparent; + border-color: transparent; + color: @text_disabled; +} + +/* Suggested-action — primary CTA, solid lime green */ +button.suggested-action { + background-color: @brand_green; + border-color: @brand_green_dark; + color: @text_on_accent; + font-weight: 600; +} + +button.suggested-action:hover { + background-color: @brand_green_light; + border-color: @brand_green; + color: @text_on_accent; +} + +button.suggested-action:active, +button.suggested-action:checked { + background-color: @brand_green_dark; + border-color: @brand_green_dark; + color: @text_on_accent; +} + +button.suggested-action:disabled { + background-color: alpha(@brand_green, 0.25); + border-color: transparent; + color: @text_disabled; +} + +/* Destructive-action — solid red */ +button.destructive-action { + background-color: @error_color; + border-color: shade(@error_color, 0.85); + color: #ffffff; + font-weight: 600; +} + +button.destructive-action:hover { + background-color: shade(@error_color, 1.10); + border-color: shade(@error_color, 0.85); +} + +button.destructive-action:active, +button.destructive-action:checked { + background-color: shade(@error_color, 0.85); + border-color: shade(@error_color, 0.75); +} + +button.destructive-action:disabled { + background-color: alpha(@error_color, 0.30); + border-color: transparent; + color: @text_disabled; +} + +/* Image-only buttons — slightly tighter padding */ +button.image-button { + padding: 6px 8px; + min-width: 24px; +} + +/* Linked button groups — share borders, only the ends round */ +.linked > button, +.linked > entry { + border-radius: 0; + border-right-width: 0; +} + +.linked > button:first-child, +.linked > entry:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} + +.linked > button:last-child, +.linked > entry:last-child { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + border-right-width: 1px; +} + +.linked.vertical > button, +.linked.vertical > entry { + border-radius: 0; + border-right-width: 1px; + border-bottom-width: 0; +} + +.linked.vertical > button:first-child, +.linked.vertical > entry:first-child { + border-top-left-radius: 6px; + border-top-right-radius: 6px; +} + +.linked.vertical > button:last-child, +.linked.vertical > entry:last-child { + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + border-bottom-width: 1px; +} + +/* Link buttons — inline hyperlink style */ +button.link, +*.link { + background-color: transparent; + border-color: transparent; + padding: 0; + color: @link_color; + text-shadow: none; +} + +button.link:hover, +*.link:hover { + background-color: transparent; + color: @brand_green_light; + text-decoration: underline; +} + +button.link:visited, +*.link:visited { + color: @link_visited_color; +} + +button.link:disabled { + color: @text_disabled; +} + +/* ── Entries ─────────────────────────────────────────────── */ + +entry, +spinbutton:not(.vertical) { + min-height: 24px; + padding: 6px 10px; + border: 1px solid @border_strong; + border-radius: 6px; + background-color: @surface_bg; + background-image: none; + color: @text_primary; + caret-color: @brand_green; + transition: border-color 120ms ease, + box-shadow 120ms ease, + background-color 120ms ease; +} + +entry:hover { + border-color: @brand_green_dark; +} + +entry:focus, +entry:focus-within { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; + background-color: @surface_bg; +} + +entry:disabled { + background-color: @insensitive_bg_color; + border-color: @border; + color: @text_disabled; +} + +entry selection { + background-color: @selected_bg; + color: @selected_fg; +} + +entry:selected { + background-color: @selected_bg; + color: @selected_fg; +} + +entry image { + color: @text_secondary; + padding: 0 4px; +} + +entry image:hover { + color: @brand_green; +} + +entry progress { + background-color: @brand_green; + background-image: none; + border: none; + border-radius: 2px; + margin: 0 -10px -6px -10px; + min-height: 2px; +} + +/* Error / warning / success states */ +entry.error { + border-color: @error_color; + box-shadow: inset 0 0 0 1px @error_color; + color: @error_color; +} + +entry.warning { + border-color: @warning_color; + box-shadow: inset 0 0 0 1px @warning_color; + color: @warning_color; +} + +entry.success { + border-color: @success_color; + box-shadow: inset 0 0 0 1px @success_color; +} + +/* Search entries — slight visual tweak */ +entry.search { + border-radius: 999px; + padding-left: 14px; + padding-right: 14px; +} + +/* ── Spinbuttons ─────────────────────────────────────────── */ + +spinbutton button { + background-color: transparent; + border-color: transparent; + border-radius: 0; + color: @text_secondary; + min-width: 18px; + padding: 4px 6px; +} + +spinbutton button:hover { + background-color: @hover_bg; + color: @text_primary; +} + +spinbutton button:active { + background-color: @active_bg; + color: @active_fg; +} + +spinbutton button:disabled { + background-color: transparent; + color: @text_disabled; +} + +spinbutton.vertical button { + border-left: 1px solid @border; +} + +/* ── Text views (multi-line) ────────────────────────────── */ + +textview, +textview text { + background-color: @base_bg; + color: @text_primary; + caret-color: @brand_green; +} + +textview text selection { + background-color: @selected_bg; + color: @selected_fg; +} + +textview:disabled, +textview:disabled text { + color: @text_disabled; +} diff --git a/assets/themes/NexusOS/gtk-3.0/colors.css b/assets/themes/NexusOS/gtk-3.0/colors.css new file mode 100644 index 0000000..ea8cbce --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/colors.css @@ -0,0 +1,72 @@ +/* NexusOS palette — derived from the n-small.png logo. + Lime green = primary accent (hover/active/focus). + Purple = secondary accent (selection/highlight). + Swap their roles in later segments if preferred. */ + +/* ── Brand ─────────────────────────────────────────── */ +@define-color brand_green #8cc63f; +@define-color brand_green_light #b8e373; +@define-color brand_green_dark #6ba62a; +@define-color brand_purple #88008f; +@define-color brand_purple_light #a232a8; +@define-color brand_purple_dark #5e0066; + +/* ── Foundation (dark) ─────────────────────────────── */ +@define-color base_bg #1e1526; /* deepest background — dark purple */ +@define-color surface_bg #1f2225; /* cards, popovers, entries */ +@define-color surface_bg_alt #2a2e32; /* alt rows, headerbars */ +@define-color overlay_bg #2e3236; /* tooltips, raised surfaces */ +@define-color border #2e3236; +@define-color border_strong #3a3d41; +@define-color menu_bg #2a1d33; /* dark purple — menus/popovers/whisker */ +@define-color menu_border #4d3461; /* purple frame + separators on menu_bg */ + +/* ── Text ──────────────────────────────────────────── */ +@define-color text_primary #f2f2f2; +@define-color text_secondary #a8a8a8; +@define-color text_disabled #6e7173; +@define-color text_on_accent #0a0a00; /* sits on lime green */ +@define-color text_on_selection #ffffff; /* sits on purple */ + +/* ── Interaction states ────────────────────────────── */ +@define-color hover_bg alpha(@brand_green, 0.18); +@define-color active_bg @brand_green; +@define-color active_fg @text_on_accent; +@define-color selected_bg @brand_purple; +@define-color selected_fg @text_on_selection; +@define-color focus_ring @brand_green; + +/* ── Semantic ──────────────────────────────────────── */ +@define-color success_color #27ae60; +@define-color warning_color #f67400; +@define-color error_color #da4453; +@define-color info_color @brand_green; + +/* ── GTK named-color aliases (compatibility) ───────── */ +/* GTK widgets reference these well-known names internally; + keeping them in sync with our brand palette avoids gaps. */ +@define-color theme_bg_color @base_bg; +@define-color theme_fg_color @text_primary; +@define-color theme_base_color @base_bg; +@define-color theme_text_color @text_primary; +@define-color theme_selected_bg_color @brand_green; +@define-color theme_selected_fg_color @active_fg; +@define-color theme_hovering_selected_bg_color @brand_green_light; +@define-color theme_unfocused_bg_color @surface_bg; +@define-color theme_unfocused_fg_color @text_secondary; +@define-color theme_unfocused_base_color @base_bg; +@define-color theme_unfocused_text_color @text_secondary; +@define-color theme_unfocused_selected_bg_color @brand_purple_dark; +@define-color theme_unfocused_selected_fg_color @selected_fg; +@define-color borders @border; +@define-color unfocused_borders @border; +@define-color insensitive_bg_color @surface_bg; +@define-color insensitive_fg_color @text_disabled; +@define-color insensitive_base_color @base_bg; +@define-color insensitive_borders @border_strong; +@define-color content_view_bg @base_bg; +@define-color link_color @brand_green_light; +@define-color link_visited_color @brand_purple_light; +@define-color tooltip_background @overlay_bg; +@define-color tooltip_text @text_primary; +@define-color tooltip_border @border_strong; diff --git a/assets/themes/NexusOS/gtk-3.0/dialogs-infobars.css b/assets/themes/NexusOS/gtk-3.0/dialogs-infobars.css new file mode 100644 index 0000000..cf44439 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/dialogs-infobars.css @@ -0,0 +1,329 @@ +/* NexusOS — Segment 9: dialogs, message dialogs, infobars, app notifications, + assistants/wizards. Semantic colors (info/warning/error/success) get matched + bg + a left-edge accent so the message type reads at a glance. */ + +/* ── Generic dialog window ───────────────────────────────── */ + +dialog, +messagedialog { + background-color: @base_bg; + color: @text_primary; +} + +dialog.background, +messagedialog.background { + background-color: @base_bg; +} + +dialog > box.dialog-vbox, +messagedialog > box.dialog-vbox { + padding: 6px; +} + +/* The titlebar on dialogs (when CSD) — already styled by Segment 3, but + trim the bottom border so it reads as "part of the dialog" not "separate + chrome". */ +dialog > headerbar, +messagedialog > headerbar { + background-color: @surface_bg_alt; + border-bottom: 1px solid @border; +} + +dialog > headerbar.flat, +messagedialog > headerbar.flat { + background-color: transparent; + border-bottom-color: transparent; +} + +/* Action button row at the bottom of a dialog */ +dialog .dialog-action-area, +messagedialog .dialog-action-area, +.dialog-action-box { + background-color: @surface_bg; + border-top: 1px solid @border; + padding: 10px 12px; +} + +dialog .dialog-action-area > button, +messagedialog .dialog-action-area > button, +.dialog-action-box > button { + min-width: 84px; +} + +/* Body / icon area of a message dialog */ +messagedialog .dialog-vbox > box { + padding: 18px 18px 8px 18px; +} + +messagedialog .horizontal { + padding: 8px; +} + +messagedialog .titlebar:not(.flat) { + background-color: @surface_bg_alt; +} + +/* Title and secondary text in a message dialog */ +messagedialog label.title { + font-weight: 700; + font-size: 1.1em; + color: @text_primary; +} + +messagedialog label { + color: @text_primary; +} + +messagedialog label.dim-label, +messagedialog .dim-label { + color: @text_secondary; +} + +/* ── About / file-chooser dialog tweaks ──────────────────── */ + +filechooser stack { + background-color: @base_bg; +} + +filechooser actionbar { + background-color: @surface_bg; + border-top: 1px solid @border; +} + +/* Path bar inside the file chooser */ +.path-bar button { + border-radius: 0; + border: none; + border-right: 1px solid @border; + background-color: transparent; +} + +.path-bar button:first-child { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} + +.path-bar button:last-child { + border-right: none; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} + +.path-bar button:checked { + background-color: @active_bg; + color: @active_fg; +} + +.path-bar button.text-button:not(:only-child):not(:first-child) { + padding-left: 8px; +} + +/* ── Infobars ────────────────────────────────────────────── */ + +infobar { + background-color: @surface_bg_alt; + color: @text_primary; + border: 1px solid @border; + border-radius: 6px; + padding: 8px 12px; + box-shadow: inset 4px 0 0 0 @brand_green; /* default = info accent */ + min-height: 36px; +} + +infobar > revealer > box { + padding: 0; +} + +infobar label { + color: @text_primary; +} + +infobar button { + /* Buttons inside infobars stay flat, with brand hover */ + background-color: transparent; + border-color: transparent; + color: @text_primary; +} + +infobar button:hover { + background-color: alpha(@text_primary, 0.10); +} + +infobar button:focus { + outline: 2px solid @focus_ring; + outline-offset: -2px; +} + +infobar button.text-button { + font-weight: 500; +} + +/* Semantic variants — left-edge accent + tinted background */ + +infobar.info { + background-color: alpha(@info_color, 0.12); + box-shadow: inset 4px 0 0 0 @info_color; +} + +infobar.info label { + color: @text_primary; +} + +infobar.question { + background-color: alpha(@brand_purple, 0.18); + box-shadow: inset 4px 0 0 0 @brand_purple; +} + +infobar.warning { + background-color: alpha(@warning_color, 0.18); + box-shadow: inset 4px 0 0 0 @warning_color; +} + +infobar.warning label { + color: @text_primary; +} + +infobar.error { + background-color: alpha(@error_color, 0.18); + box-shadow: inset 4px 0 0 0 @error_color; +} + +infobar.error label { + color: @text_primary; +} + +infobar.success { + background-color: alpha(@success_color, 0.18); + box-shadow: inset 4px 0 0 0 @success_color; +} + +infobar.success label { + color: @text_primary; +} + +/* The close (X) button on an infobar */ +infobar button.close { + min-width: 22px; + min-height: 22px; + padding: 2px; + border-radius: 999px; + color: @text_secondary; +} + +infobar button.close:hover { + background-color: alpha(@text_primary, 0.15); + color: @text_primary; +} + +/* ── App notifications (overlay banners) ─────────────────── */ + +.app-notification, +.app-notification.frame { + background-color: @overlay_bg; + color: @text_primary; + border: 1px solid @border_strong; + border-radius: 8px; + padding: 10px 14px; + margin: 8px; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45), + 0 2px 4px rgba(0, 0, 0, 0.30); +} + +.app-notification button, +.app-notification.frame button { + background-color: transparent; + border-color: transparent; + color: @brand_green_light; +} + +.app-notification button:hover { + background-color: @hover_bg; + color: @brand_green; +} + +.app-notification button.suggested-action { + background-color: @brand_green; + color: @text_on_accent; + border-color: @brand_green_dark; +} + +.app-notification button.destructive-action { + background-color: @error_color; + color: #ffffff; +} + +/* ── Assistant / wizard ──────────────────────────────────── */ + +assistant { + background-color: @base_bg; + color: @text_primary; +} + +assistant .sidebar { + background-color: @surface_bg; + border-right: 1px solid @border; + padding: 6px; +} + +assistant .sidebar label { + padding: 6px 10px; + color: @text_secondary; + border-radius: 4px; +} + +assistant .sidebar label.highlight { + background-color: @selected_bg; + color: @selected_fg; + font-weight: 500; +} + +assistant headerbar { + background-color: @surface_bg_alt; +} + +/* ── Frames & separators inside dialogs ──────────────────── */ + +frame > border, +.frame { + border: 1px solid @border; + border-radius: 6px; + padding: 0; +} + +/* Breathing room so group content doesn't sit flush against the + frame border. XFCE settings dialogs indent frame content 12px on + the left (the HIG sub-section indent), so the left padding is + dropped to 0 and the right bumped to 12px to keep the gap even. + Scoped to real GtkFrames — not the broad .frame class, which also + lands on scrolledwindows. */ +frame > border { + padding: 6px 12px 6px 0; +} + +frame > label, +.frame > label { + color: @text_secondary; + font-weight: 500; + padding: 0 4px; +} + +separator { + background-color: @border; + min-width: 1px; + min-height: 1px; +} + +separator.horizontal { + min-height: 1px; +} + +separator.vertical { + min-width: 1px; +} + +/* Dim label utility — used in lots of dialog body text */ +.dim-label, +label.dim-label { + color: @text_secondary; + opacity: 1.0; /* GTK's default also dims via opacity — we just recolor */ +} diff --git a/assets/themes/NexusOS/gtk-3.0/gtk-dark.css b/assets/themes/NexusOS/gtk-3.0/gtk-dark.css new file mode 100644 index 0000000..7aa1541 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/gtk-dark.css @@ -0,0 +1,2 @@ +/* NexusOS is dark-only by design — gtk-dark.css mirrors gtk.css. */ +@import url("gtk.css"); diff --git a/assets/themes/NexusOS/gtk-3.0/gtk.css b/assets/themes/NexusOS/gtk-3.0/gtk.css new file mode 100644 index 0000000..c0ee34e --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/gtk.css @@ -0,0 +1,34 @@ +/* NexusOS — GTK3 theme entry point. + Each segment lives in its own file and is imported below in the order it + was built. A trailing fallback rule keeps unstyled widgets legible on the + dark base if a future segment hasn't landed yet. */ + +@import url("colors.css"); + +/* Segment 2 — buttons & entries */ +@import url("buttons-entries.css"); + +/* Segment 3 — headerbars, titlebars, window controls, window chrome */ +@import url("headerbars.css"); + +/* Segment 4 — menus, popovers, tooltips */ +@import url("menus-popovers.css"); + +/* Segment 5 — sidebars, lists, treeviews */ +@import url("lists-sidebars.css"); + +/* Segment 6 — notebooks (tabs) and stack switchers */ +@import url("notebooks.css"); + +/* Segment 7 — scrollbars, progress bars, level bars, spinners */ +@import url("scrollbars-progress.css"); + +/* Segment 8 — switches, checkboxes, radios, scales/sliders */ +@import url("toggles-sliders.css"); + +/* Segment 9 — dialogs, infobars, app notifications, assistants */ +@import url("dialogs-infobars.css"); + +/* Segment 10 — polish (combobox, toolbar, actionbar, statusbar, paned, + expander, calendar, iconview, shortcuts-window, OSD, focus rings) */ +@import url("polish.css"); diff --git a/assets/themes/NexusOS/gtk-3.0/headerbars.css b/assets/themes/NexusOS/gtk-3.0/headerbars.css new file mode 100644 index 0000000..186f1f4 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/headerbars.css @@ -0,0 +1,298 @@ +/* NexusOS — Segment 3: headerbars, titlebars, window controls, window chrome. + Headerbars use the alt surface to distinguish chrome from content. + Buttons in headerbars default to flat; close button warns red on hover. */ + +/* ── Window ──────────────────────────────────────────────── */ + +window, +window.background { + background-color: @base_bg; + color: @text_primary; +} + +window:backdrop { + background-color: @base_bg; + color: @text_secondary; +} + +/* CSD (client-side decorated) windows — rounded corners + subtle border */ +window.csd { + border-radius: 8px; + box-shadow: 0 0 0 1px @border, + 0 8px 24px rgba(0, 0, 0, 0.45), + 0 2px 6px rgba(0, 0, 0, 0.30); +} + +window.csd:backdrop { + box-shadow: 0 0 0 1px @border, + 0 4px 12px rgba(0, 0, 0, 0.30); +} + +window.ssd { + /* server-side decorated — leave decoration to WM */ + box-shadow: none; +} + +/* Maximized / tiled / fullscreen — no rounded corners, no shadow */ +window.maximized, +window.tiled, +window.tiled-top, +window.tiled-bottom, +window.tiled-left, +window.tiled-right, +window.fullscreen { + border-radius: 0; + box-shadow: none; +} + +/* ── Headerbar / titlebar ────────────────────────────────── */ + +headerbar, +.titlebar { + padding: 4px 6px; + min-height: 38px; + border: none; + border-bottom: 1px solid @border; + background-color: @surface_bg_alt; + background-image: none; + color: @text_primary; + box-shadow: none; + text-shadow: none; +} + +headerbar:backdrop, +.titlebar:backdrop { + background-color: @surface_bg; + color: @text_secondary; + border-bottom-color: @border; +} + +/* Rounded top corners on CSD headerbars */ +window.csd > headerbar:first-child, +window.csd > .titlebar:first-child, +window.csd > deck > headerbar:first-child, +window.csd > box > headerbar:first-child { + border-top-left-radius: 7px; + border-top-right-radius: 7px; +} + +window.maximized > headerbar, +window.tiled > headerbar, +window.fullscreen > headerbar { + border-radius: 0; +} + +/* Title / subtitle text */ +headerbar .title, +.titlebar .title { + font-weight: 600; + color: @text_primary; + padding: 0 12px; +} + +headerbar .subtitle, +.titlebar .subtitle { + font-size: smaller; + color: @text_secondary; + padding: 0 12px; +} + +headerbar:backdrop .title, +.titlebar:backdrop .title, +headerbar:backdrop .subtitle, +.titlebar:backdrop .subtitle { + color: @text_secondary; +} + +/* Separator between title and other widgets */ +headerbar separator.titlebutton, +.titlebar separator.titlebutton { + background-color: @border; + min-width: 1px; + margin: 6px 4px; +} + +/* ── Buttons inside headerbars ───────────────────────────── */ + +headerbar button, +.titlebar button { + padding: 4px 8px; + min-height: 24px; + border: 1px solid transparent; + background-color: transparent; + background-image: none; + color: @text_primary; + box-shadow: none; +} + +headerbar button:hover, +.titlebar button:hover { + background-color: @hover_bg; + border-color: transparent; + color: @text_primary; +} + +headerbar button:active, +headerbar button:checked, +.titlebar button:active, +.titlebar button:checked { + background-color: @active_bg; + border-color: transparent; + color: @active_fg; +} + +headerbar button:disabled, +.titlebar button:disabled { + background-color: transparent; + color: @text_disabled; +} + +headerbar button:focus, +.titlebar button:focus { + outline: 2px solid @focus_ring; + outline-offset: -2px; +} + +headerbar button:backdrop, +.titlebar button:backdrop { + color: @text_secondary; +} + +/* Suggested-action in a headerbar keeps its solid lime fill */ +headerbar button.suggested-action, +.titlebar button.suggested-action { + background-color: @brand_green; + border-color: @brand_green_dark; + color: @text_on_accent; +} + +headerbar button.suggested-action:hover, +.titlebar button.suggested-action:hover { + background-color: @brand_green_light; + color: @text_on_accent; +} + +headerbar button.destructive-action, +.titlebar button.destructive-action { + background-color: @error_color; + color: #ffffff; +} + +/* Entries in headerbars — pick up the alt surface */ +headerbar entry, +.titlebar entry { + background-color: @base_bg; + border-color: @border_strong; +} + +headerbar entry:focus, +.titlebar entry:focus { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; +} + +/* ── Window control buttons (close, min, max) ────────────── */ + +.titlebutton, +headerbar button.titlebutton, +.titlebar button.titlebutton, +button.titlebutton { + min-width: 22px; + min-height: 22px; + padding: 4px; + margin: 0 2px; + border-radius: 999px; /* circular control dots */ + border: 1px solid transparent; + background-color: alpha(@text_primary, 0.08); + color: @text_primary; + -gtk-icon-shadow: none; +} + +.titlebutton:hover, +headerbar button.titlebutton:hover, +.titlebar button.titlebutton:hover { + background-color: @hover_bg; + border-color: @brand_green_dark; + color: @text_primary; +} + +.titlebutton:active, +.titlebutton:checked { + background-color: @active_bg; + border-color: @brand_green_dark; + color: @active_fg; +} + +.titlebutton:backdrop, +.titlebutton:disabled { + background-color: alpha(@text_primary, 0.04); + color: @text_disabled; + border-color: transparent; +} + +/* Close button — warn red on hover so it can't be confused with accent actions */ +.titlebutton.close, +button.titlebutton.close { + background-color: alpha(@error_color, 0.18); + color: @text_primary; +} + +.titlebutton.close:hover, +button.titlebutton.close:hover { + background-color: @error_color; + border-color: shade(@error_color, 0.85); + color: #ffffff; +} + +.titlebutton.close:active, +button.titlebutton.close:active { + background-color: shade(@error_color, 0.85); + color: #ffffff; +} + +.titlebutton.close:backdrop { + background-color: alpha(@error_color, 0.10); + color: @text_disabled; +} + +/* Minimize / maximize — explicit selectors in case theming engines key off them */ +.titlebutton.minimize:hover, +.titlebutton.maximize:hover, +button.titlebutton.minimize:hover, +button.titlebutton.maximize:hover { + background-color: @hover_bg; + border-color: @brand_green_dark; + color: @text_primary; +} + +/* ── Stack-of-headerbars (e.g. libhandy/libadwaita-style split) ── */ + +headerbar.flat { + background-color: transparent; + border-bottom-color: transparent; +} + +/* Selection-mode headerbar (e.g. when picking files) — purple to match selection accent */ +headerbar.selection-mode, +.titlebar.selection-mode { + background-color: @brand_purple_dark; + color: @selected_fg; + border-bottom-color: @brand_purple; +} + +headerbar.selection-mode .title, +.titlebar.selection-mode .title, +headerbar.selection-mode .subtitle, +.titlebar.selection-mode .subtitle { + color: @selected_fg; +} + +headerbar.selection-mode button, +.titlebar.selection-mode button { + color: @selected_fg; +} + +headerbar.selection-mode button:hover, +.titlebar.selection-mode button:hover { + background-color: alpha(@selected_fg, 0.15); +} diff --git a/assets/themes/NexusOS/gtk-3.0/lists-sidebars.css b/assets/themes/NexusOS/gtk-3.0/lists-sidebars.css new file mode 100644 index 0000000..99e4bfa --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/lists-sidebars.css @@ -0,0 +1,357 @@ +/* NexusOS — Segment 5: sidebars, list boxes, tree views, places sidebars. + Row selection is purple (the secondary accent), hover is the lime wash. + This keeps "selected" visually distinct from "the thing my cursor is on". */ + +/* ── Generic listbox ─────────────────────────────────────── */ + +list, +listview, +.list { + background-color: @base_bg; + color: @text_primary; + border-color: @border; +} + +list row, +listview row, +.list row, +.list-row { + padding: 8px 12px; + background-color: transparent; + color: @text_primary; + border-bottom: 1px solid alpha(@border, 0.45); + transition: background-color 100ms ease, + color 100ms ease; +} + +list row:last-child, +listview row:last-child, +.list row:last-child { + border-bottom: none; +} + +list row:hover, +listview row:hover, +.list row:hover { + background-color: @hover_bg; + color: @text_primary; +} + +list row:selected, +listview row:selected, +.list row:selected, +list row.activatable:selected { + background-color: @selected_bg; + color: @selected_fg; + border-bottom-color: alpha(@brand_purple_dark, 0.6); +} + +list row:selected:hover, +listview row:selected:hover { + background-color: shade(@selected_bg, 1.12); + color: @selected_fg; +} + +list row:disabled, +listview row:disabled { + color: @text_disabled; +} + +/* .activatable rows (clickable) get a subtle hover hint */ +list row.activatable:hover { + background-color: @hover_bg; +} + +list row.activatable:active { + background-color: shade(@hover_bg, 1.2); +} + +/* Separator row */ +list row.separator, +list separator { + background-color: @border; + min-height: 1px; + padding: 0; + margin: 0; +} + +/* Rich-list rows (titles + subtitles) */ +list.rich-list row { + padding: 10px 14px; +} + +list.rich-list row .title { + font-weight: 500; + color: @text_primary; +} + +list.rich-list row .subtitle { + color: @text_secondary; + font-size: smaller; +} + +list.rich-list row:selected .subtitle { + color: alpha(@selected_fg, 0.85); +} + +/* ── Sidebars ────────────────────────────────────────────── */ + +.sidebar, +stacksidebar, +stacksidebar.sidebar { + background-color: @base_bg; + color: @text_primary; + border-right: 1px solid @border; + padding: 0; +} + +.sidebar:dir(rtl), +stacksidebar:dir(rtl) { + border-left: 1px solid @border; + border-right: none; +} + +.sidebar:backdrop, +stacksidebar:backdrop { + background-color: @base_bg; + color: @text_secondary; +} + +.sidebar list, +.sidebar listview, +stacksidebar list { + background-color: transparent; + color: @text_primary; +} + +.sidebar list row, +.sidebar listview row, +stacksidebar list row { + padding: 8px 14px; + border-bottom: none; + border-radius: 4px; + margin: 2px 6px; +} + +.sidebar list row:hover, +stacksidebar list row:hover { + background-color: @hover_bg; +} + +.sidebar list row:selected, +.sidebar listview row:selected, +stacksidebar list row:selected { + background-color: @selected_bg; + color: @selected_fg; +} + +.sidebar list row:selected:hover, +stacksidebar list row:selected:hover { + background-color: shade(@selected_bg, 1.12); +} + +/* Header rows inside a sidebar (libhandy/libadwaita) */ +.sidebar .navigation-sidebar > row, +.navigation-sidebar > row { + padding: 8px 14px; + margin: 2px 6px; + border-radius: 4px; + border-bottom: none; +} + +/* ── Places sidebar (file managers, GtkFileChooser) ──────── */ + +placessidebar { + background-color: @base_bg; + color: @text_primary; +} + +placessidebar > viewport.frame { + border: none; +} + +placessidebar list { + background-color: transparent; +} + +placessidebar row { + padding: 6px 12px; + border-radius: 4px; + margin: 1px 6px; +} + +placessidebar row:hover { + background-color: @hover_bg; +} + +placessidebar row:selected { + background-color: @selected_bg; + color: @selected_fg; +} + +placessidebar row:selected image { + color: @selected_fg; +} + +placessidebar row image { + color: @text_secondary; + min-width: 16px; + padding-right: 6px; +} + +placessidebar row:hover image { + color: @brand_green; +} + +placessidebar row.has-open-popup { + background-color: @hover_bg; +} + +/* Drop-target highlight when dragging onto a places row */ +placessidebar row.sidebar-new-bookmark-row { + color: @brand_green; +} + +placessidebar row:drop(active) { + background-color: alpha(@brand_green, 0.25); + color: @text_primary; + box-shadow: inset 0 0 0 2px @brand_green; +} + +/* ── Tree views / column views ───────────────────────────── */ + +treeview, +columnview, +treeview.view, +columnview.view { + background-color: @base_bg; + color: @text_primary; + border-color: @border; + -GtkTreeView-grid-line-pattern: "\7\7"; + -GtkTreeView-grid-line-width: 1; +} + +treeview.view:hover, +columnview.view:hover { + background-color: @hover_bg; +} + +treeview.view:selected, +columnview.view:selected, +treeview.view:selected:focus, +columnview.view:selected:focus, +treeview.view row:selected, +columnview.view row:selected { + background-color: @selected_bg; + color: @selected_fg; +} + +treeview.view:selected:hover, +columnview.view:selected:hover { + background-color: shade(@selected_bg, 1.12); +} + +treeview.view:disabled, +columnview.view:disabled { + color: @text_disabled; +} + +/* Alternate-row background for "rules-hint" tree views */ +treeview.view:nth-child(even) { + background-color: alpha(@surface_bg, 0.5); +} + +/* Tree expander arrows */ +treeview.view expander, +columnview.view expander { + color: @text_secondary; + min-width: 14px; + min-height: 14px; + -gtk-icon-source: -gtk-icontheme("pan-end-symbolic"); +} + +treeview.view expander:hover, +columnview.view expander:hover { + color: @brand_green; +} + +treeview.view expander:checked, +columnview.view expander:checked { + -gtk-icon-source: -gtk-icontheme("pan-down-symbolic"); + color: @brand_green; +} + +treeview.view expander:dir(rtl) { + -gtk-icon-source: -gtk-icontheme("pan-start-symbolic"); +} + +/* Cell separators (vertical grid lines) */ +treeview.view.separator, +columnview.view.separator { + color: @border; + background-color: @border; +} + +/* Column headers — sit on the alt surface so they read as chrome */ +treeview header button, +columnview header button, +treeview.view header button, +columnview.view header button { + background-color: @surface_bg_alt; + background-image: none; + border: none; + border-right: 1px solid @border; + border-bottom: 1px solid @border; + border-radius: 0; + color: @text_primary; + padding: 6px 10px; + font-weight: 500; + box-shadow: none; +} + +treeview header button:hover, +columnview header button:hover { + background-color: @overlay_bg; + color: @brand_green; +} + +treeview header button:active, +treeview header button:checked, +columnview header button:active { + background-color: @overlay_bg; + color: @brand_green; + box-shadow: inset 0 -2px 0 0 @brand_green; +} + +treeview header button:last-child, +columnview header button:last-child { + border-right: none; +} + +/* Drag-and-drop indicators */ +treeview.view.dnd, +columnview.view.dnd { + border-style: solid none; + border-width: 1px; + border-color: @brand_green; +} + +treeview.view:drop(active), +columnview.view:drop(active) { + background-color: alpha(@brand_green, 0.20); + box-shadow: inset 0 0 0 1px @brand_green; +} + +/* In-cell progress bar */ +treeview.view.progressbar, +columnview.view.progressbar { + background-color: @brand_green; + color: @text_on_accent; + border-radius: 2px; +} + +treeview.view.trough, +columnview.view.trough { + background-color: @surface_bg_alt; + border-radius: 2px; +} diff --git a/assets/themes/NexusOS/gtk-3.0/menus-popovers.css b/assets/themes/NexusOS/gtk-3.0/menus-popovers.css new file mode 100644 index 0000000..f7dcd27 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/menus-popovers.css @@ -0,0 +1,392 @@ +/* NexusOS — Segment 4: menus, popovers, tooltips. + Surfaces lift one step above the window base. Hovered menu items use the + lime hover wash; selected/checked items go to solid lime. */ + +/* ── Menubar (top-level) ─────────────────────────────────── */ + +menubar, +.menubar { + background-color: @surface_bg_alt; + color: @text_primary; + border-bottom: 1px solid @border; + padding: 0; +} + +menubar > menuitem, +.menubar > menuitem { + padding: 6px 10px; + color: @text_primary; + background-color: transparent; +} + +menubar > menuitem:hover, +.menubar > menuitem:hover { + background-color: @hover_bg; + color: @text_primary; +} + +menubar > menuitem:active, +menubar > menuitem:checked, +.menubar > menuitem:active { + background-color: @active_bg; + color: @active_fg; +} + +menubar > menuitem:disabled, +.menubar > menuitem:disabled { + color: @text_disabled; +} + +/* ── Pop-up menus (classic GtkMenu) ──────────────────────── */ + +menu, +.menu, +.context-menu { + padding: 1px 0; + background-color: @menu_bg; + background-image: none; + border: 1px solid @menu_border; + border-radius: 6px; + color: @text_primary; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45), + 0 2px 4px rgba(0, 0, 0, 0.30); +} + +menu menuitem, +.menu menuitem, +.context-menu menuitem { + padding: 1px 10px; + min-height: 0; + color: @text_primary; + background-color: transparent; + text-shadow: none; +} + +/* Normalize the optional menu icon (gtk-menu-images=true) so an icon'd + row — e.g. xfdesktop's "Desktop Settings…" — is the same height as a + text-only row instead of being sized by the raw icon + GTK defaults. + Kept tight because nm-applet's wifi list grows long and the menu is + bounded by screen height — every px per row matters. */ +menu menuitem image, +.menu menuitem image, +.context-menu menuitem image { + min-width: 14px; + min-height: 14px; + margin: 0 6px 0 0; + -gtk-icon-transform: scale(1); +} + +menu menuitem box, +.menu menuitem box, +.context-menu menuitem box { + margin: 0; + padding: 0; + /* border-spacing not valid in GTK3; gap managed by margin/padding above */ +} + +/* nm-applet wraps the Wi-Fi list in a GtkScrolledWindow inside the menu. + GTK caps it programmatically; min-height overrides that cap so all + networks are visible without scrolling. */ +menu scrolledwindow { + min-height: 800px; +} + +menu menuitem:hover, +.menu menuitem:hover, +.context-menu menuitem:hover { + background-color: @hover_bg; + color: @text_primary; +} + +menu menuitem:active, +menu menuitem:checked, +.menu menuitem:active { + background-color: @active_bg; + color: @active_fg; +} + +menu menuitem:disabled, +.menu menuitem:disabled, +.context-menu menuitem:disabled { + color: @text_disabled; +} + +/* Keyboard accelerator hint text on the right side */ +menuitem accelerator, +.menuitem accelerator { + color: @text_secondary; + padding-left: 16px; +} + +menuitem:hover accelerator { + color: @text_primary; +} + +menuitem:active accelerator, +menuitem:checked accelerator { + color: @active_fg; +} + +menuitem:disabled accelerator { + color: @text_disabled; +} + +/* Submenu arrow */ +menuitem arrow, +.menuitem arrow { + color: @text_secondary; + min-width: 12px; + min-height: 12px; + -gtk-icon-source: -gtk-icontheme("pan-end-symbolic"); +} + +menuitem:hover arrow { + color: @text_primary; +} + +menuitem:dir(rtl) arrow { + -gtk-icon-source: -gtk-icontheme("pan-start-symbolic"); +} + +/* Check / radio inside menus */ +menuitem check, +menuitem radio, +.menuitem check, +.menuitem radio { + min-width: 14px; + min-height: 14px; + color: @brand_green; + margin-right: 8px; +} + +menuitem check:checked, +menuitem radio:checked { + color: @brand_green; + -gtk-icon-shadow: none; +} + +/* Separator inside menus */ +menu separator, +.menu separator, +.context-menu separator { + background-color: @menu_border; + min-height: 1px; + margin: 3px 0; +} + +/* ── Popovers (modern) ───────────────────────────────────── */ + +popover, +popover.background { + background-color: @menu_bg; + background-image: none; + border: 1px solid @menu_border; + border-radius: 8px; + color: @text_primary; + padding: 6px; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45), + 0 2px 4px rgba(0, 0, 0, 0.30); +} + +popover > arrow, +popover.background > arrow { + background-color: @menu_bg; + border: 1px solid @menu_border; +} + +/* Modelbutton — the flat row-style button used in popover menus */ +modelbutton, +popover modelbutton { + padding: 6px 10px; + min-height: 24px; + border-radius: 4px; + background-color: transparent; + color: @text_primary; + outline: none; +} + +modelbutton:hover, +popover modelbutton:hover { + background-color: @hover_bg; + color: @text_primary; +} + +modelbutton:active, +modelbutton:checked, +popover modelbutton:active, +popover modelbutton:checked { + background-color: @active_bg; + color: @active_fg; +} + +modelbutton:disabled, +popover modelbutton:disabled { + background-color: transparent; + color: @text_disabled; +} + +modelbutton check, +modelbutton radio { + color: @brand_green; +} + +modelbutton arrow.left, +modelbutton arrow.right { + color: @text_secondary; + min-width: 12px; + min-height: 12px; +} + +modelbutton:hover arrow { + color: @text_primary; +} + +/* Separators inside popovers */ +popover separator { + background-color: @menu_border; + min-height: 1px; + margin: 3px 2px; +} + +/* Entries inside popovers — keep the entry style but match the surface */ +popover entry { + background-color: @surface_bg; + border-color: @menu_border; +} + +popover entry:focus { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; +} + +/* Buttons inside popovers — flat by default */ +popover button { + background-color: transparent; + border-color: transparent; + color: @text_primary; +} + +popover button:hover { + background-color: @hover_bg; +} + +popover button:active, +popover button:checked { + background-color: @active_bg; + color: @active_fg; +} + +popover button.suggested-action { + background-color: @brand_green; + color: @text_on_accent; +} + +popover button.destructive-action { + background-color: @error_color; + color: #ffffff; +} + +/* ── Tooltips ────────────────────────────────────────────── */ + +tooltip, +tooltip.background { + background-color: @tooltip_background; + background-image: none; + border: 1px solid @tooltip_border; + border-radius: 6px; + color: @tooltip_text; + padding: 4px 8px; + text-shadow: none; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.40); +} + +tooltip label, +tooltip.background label { + padding: 0; + color: @tooltip_text; +} + +/* ── Whisker menu (xfce4-whiskermenu-plugin) ─────────────── */ +/* Whisker has no menuitems — the app/category lists are GtkTreeViews. + Row height = cell padding + the icon-size set in Whisker's own + settings, so trim padding here and keep icons small in Whisker. */ + +/* Whisker sets the popup's GTK *widget name* (gtk_widget_set_name), + which CSS matches as #id — NOT a .class. The #id selector also + outranks the base `window.background` rule (headerbars.css). */ +#whiskermenu-window { + background-color: @menu_bg; + color: @text_primary; + padding: 2px; +} + +#whiskermenu-window .view, +#whiskermenu-window treeview, +#whiskermenu-window treeview.view, +#whiskermenu-window iconview, +#whiskermenu-window scrolledwindow, +#whiskermenu-window viewport, +#whiskermenu-window stack, +#whiskermenu-window box { + background-color: @menu_bg; + color: @text_primary; +} + +#whiskermenu-window treeview.view, +#whiskermenu-window iconview { + padding: 1px 4px; + -GtkTreeView-vertical-separator: 0; +} + +#whiskermenu-window treeview.view:hover, +#whiskermenu-window iconview:hover { + background-color: @hover_bg; + color: @text_primary; +} + +#whiskermenu-window treeview.view:selected, +#whiskermenu-window iconview:selected { + background-color: @active_bg; + color: @active_fg; +} + +/* Search entry and the bottom command buttons */ +#whiskermenu-window entry { + margin: 2px; + background-color: @menu_bg; + border-color: @menu_border; + color: @text_primary; +} + +#whiskermenu-window entry:focus { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; +} + +#whiskermenu-window button { + padding: 2px 6px; + min-height: 0; + background-color: transparent; + color: @text_primary; +} + +#whiskermenu-window button:hover { + background-color: @hover_bg; +} + +#whiskermenu-window button:checked, +#whiskermenu-window button:active { + background-color: @active_bg; + color: @active_fg; +} + +#whiskermenu-window separator { + margin: 2px 0; +} + +/* ── Tooltips (cont.) ────────────────────────────────────── */ + +tooltip * { + background-color: transparent; + color: @tooltip_text; +} diff --git a/assets/themes/NexusOS/gtk-3.0/notebooks.css b/assets/themes/NexusOS/gtk-3.0/notebooks.css new file mode 100644 index 0000000..56c9f6d --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/notebooks.css @@ -0,0 +1,253 @@ +/* NexusOS — Segment 6: notebooks (tabs) and stack switchers. + Active tab gets a lime underline (or side-line for vertical tab strips). + Stack switchers behave like a pill-shaped segmented control. */ + +/* ── Notebook container ──────────────────────────────────── */ + +notebook { + background-color: transparent; + color: @text_primary; + padding: 0; +} + +notebook > stack { + background-color: @base_bg; + color: @text_primary; +} + +/* ── Tab strip header ────────────────────────────────────── */ + +notebook > header { + background-color: @surface_bg; + border-color: @border; + padding: 0; +} + +notebook > header.top { + border-bottom: 1px solid @border; +} + +notebook > header.bottom { + border-top: 1px solid @border; +} + +notebook > header.left { + border-right: 1px solid @border; +} + +notebook > header.right { + border-left: 1px solid @border; +} + +/* Tab strip background gap (the part not covered by tabs) */ +notebook > header > tabs { + margin: 0; + padding: 0; +} + +/* ── Individual tabs ─────────────────────────────────────── */ + +notebook > header > tabs > tab { + background-color: transparent; + background-image: none; + color: @text_secondary; + padding: 4px 12px; + min-height: 20px; + border: none; + border-radius: 0; + transition: background-color 120ms ease, + color 120ms ease, + box-shadow 120ms ease; +} + +notebook > header > tabs > tab:hover { + background-color: @hover_bg; + color: @text_primary; +} + +notebook > header > tabs > tab:checked { + background-color: @base_bg; + color: @text_primary; + font-weight: 500; +} + +notebook > header > tabs > tab:disabled { + color: @text_disabled; +} + +/* Position-specific active-tab accent line */ +notebook > header.top > tabs > tab:checked { + box-shadow: inset 0 -2px 0 0 @brand_green; +} + +notebook > header.bottom > tabs > tab:checked { + box-shadow: inset 0 2px 0 0 @brand_green; +} + +notebook > header.left > tabs > tab:checked { + box-shadow: inset -2px 0 0 0 @brand_green; +} + +notebook > header.right > tabs > tab:checked { + box-shadow: inset 2px 0 0 0 @brand_green; +} + +/* Focused tab ring */ +notebook > header > tabs > tab:focus, +notebook > header > tabs > tab:focus-visible { + outline: 2px solid @focus_ring; + outline-offset: -3px; +} + +/* Close button on a tab */ +notebook > header > tabs > tab button.flat, +notebook > header > tabs > tab > button { + padding: 2px; + margin: 0 0 0 6px; + min-width: 18px; + min-height: 18px; + border-radius: 999px; + background-color: transparent; + border-color: transparent; + color: @text_secondary; +} + +notebook > header > tabs > tab button.flat:hover, +notebook > header > tabs > tab > button:hover { + background-color: @error_color; + color: #ffffff; +} + +notebook > header > tabs > tab:checked button.flat, +notebook > header > tabs > tab:checked > button { + color: @text_primary; +} + +/* Scrolling arrows when the tab strip overflows */ +notebook > header > arrow { + color: @text_secondary; + min-width: 18px; + min-height: 18px; + padding: 0 4px; +} + +notebook > header > arrow:hover { + color: @brand_green; + background-color: @hover_bg; +} + +notebook > header > arrow:disabled { + color: @text_disabled; +} + +/* "Reorderable" drag indicator */ +notebook > header > tabs > tab.reorderable-page:drop(active) { + box-shadow: inset 0 -2px 0 0 @brand_green; + background-color: @hover_bg; +} + +/* ── Stack switcher (pill-style segmented control) ───────── */ + +stackswitcher { + padding: 2px; + background-color: transparent; + border-radius: 999px; +} + +stackswitcher > button { + padding: 6px 14px; + min-height: 24px; + min-width: 64px; + border: 1px solid @border_strong; + border-radius: 0; + border-right-width: 0; + background-color: @surface_bg; + background-image: none; + color: @text_primary; + font-weight: 500; + box-shadow: none; +} + +stackswitcher > button:first-child { + border-top-left-radius: 999px; + border-bottom-left-radius: 999px; +} + +stackswitcher > button:last-child { + border-top-right-radius: 999px; + border-bottom-right-radius: 999px; + border-right-width: 1px; +} + +stackswitcher > button:hover { + background-color: @hover_bg; + border-color: @brand_green_dark; + color: @text_primary; +} + +stackswitcher > button:checked { + background-color: @active_bg; + border-color: @brand_green_dark; + color: @active_fg; +} + +stackswitcher > button:checked:hover { + background-color: @brand_green_light; + color: @text_on_accent; +} + +stackswitcher > button:disabled { + background-color: @surface_bg; + border-color: @border; + color: @text_disabled; +} + +stackswitcher > button:focus { + outline: 2px solid @focus_ring; + outline-offset: -2px; +} + +/* Notification dot when a hidden stack page needs attention */ +stackswitcher > button.needs-attention > label { + animation: none; + background-image: radial-gradient(circle at center, + @brand_green 30%, + transparent 32%); + background-size: 6px 6px; + background-repeat: no-repeat; + background-position: right top; + padding-right: 12px; +} + +/* ── Vertical stack switcher (e.g. preferences) ──────────── */ + +stackswitcher.vertical { + background-color: transparent; + padding: 0; +} + +stackswitcher.vertical > button { + border-radius: 6px; + border: 1px solid transparent; + border-right-width: 1px; + background-color: transparent; + margin: 2px 4px; + padding: 8px 12px; + min-width: 120px; +} + +stackswitcher.vertical > button:hover { + background-color: @hover_bg; + border-color: transparent; +} + +stackswitcher.vertical > button:checked { + background-color: @selected_bg; + border-color: transparent; + color: @selected_fg; +} + +stackswitcher.vertical > button:checked:hover { + background-color: shade(@selected_bg, 1.12); + color: @selected_fg; +} diff --git a/assets/themes/NexusOS/gtk-3.0/polish.css b/assets/themes/NexusOS/gtk-3.0/polish.css new file mode 100644 index 0000000..8d1eae9 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/polish.css @@ -0,0 +1,569 @@ +/* NexusOS — Segment 10: polish pass. + Catches widgets not covered by earlier segments (combobox, toolbar, + searchbar, actionbar, statusbar, paned, expander, calendar, iconview, + shortcuts-window, OSD overlays, etc.) and tightens focus/disabled rules. */ + +/* ── Comboboxes / dropdowns ──────────────────────────────── */ + +combobox, +combobox.combo, +combobox button { + background-color: @surface_bg; + color: @text_primary; +} + +combobox button.combo { + padding: 2px 8px; + border: 1px solid @border_strong; + border-radius: 6px; +} + +combobox button.combo:hover { + border-color: @brand_green_dark; + background-color: @hover_bg; +} + +combobox button.combo:focus { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; +} + +combobox button.combo:active, +combobox button.combo:checked { + background-color: @overlay_bg; +} + +combobox arrow { + color: @text_secondary; + min-width: 12px; + min-height: 12px; + margin-left: 6px; + -gtk-icon-source: -gtk-icontheme("pan-down-symbolic"); +} + +combobox button.combo:hover arrow { + color: @brand_green; +} + +/* The popup list for a combobox */ +combobox > window.popup, +combobox window.combo { + background-color: @overlay_bg; + border: 1px solid @border_strong; + border-radius: 6px; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); +} + +combobox treeview.view { + background-color: @overlay_bg; +} + +combobox treeview.view:selected, +combobox treeview.view:hover { + background-color: @hover_bg; + color: @text_primary; +} + +/* ── Toolbars ────────────────────────────────────────────── */ + +toolbar { + background-color: @surface_bg_alt; + border-color: @border; + padding: 4px 6px; + color: @text_primary; +} + +toolbar.horizontal { + border-bottom: 1px solid @border; +} + +toolbar.vertical { + border-right: 1px solid @border; +} + +toolbar > button, +toolbar button.flat { + background-color: transparent; + border-color: transparent; + color: @text_primary; +} + +toolbar > button:hover, +toolbar button.flat:hover { + background-color: @hover_bg; +} + +toolbar > button:active, +toolbar > button:checked, +toolbar button.flat:checked { + background-color: @active_bg; + color: @active_fg; +} + +toolbar separator { + background-color: @border; + margin: 4px 4px; +} + +/* Inline toolbar — sits inside content, lower contrast */ +toolbar.inline-toolbar { + background-color: @surface_bg; + border: 1px solid @border; + border-radius: 0 0 6px 6px; + border-top: none; +} + +/* OSD-style toolbar (overlay) */ +toolbar.osd { + background-color: alpha(@overlay_bg, 0.92); + border: 1px solid @border_strong; + border-radius: 8px; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55); + color: @text_primary; + padding: 4px; +} + +/* ── Searchbars, actionbars, statusbars ──────────────────── */ + +searchbar { + background-color: @surface_bg_alt; + border-bottom: 1px solid @border; + padding: 6px; +} + +searchbar > revealer > box { + padding: 0 6px; +} + +actionbar { + background-color: @surface_bg; + border-top: 1px solid @border; + padding: 6px 10px; + color: @text_primary; +} + +statusbar { + background-color: @surface_bg; + border-top: 1px solid @border; + padding: 4px 10px; + color: @text_secondary; + font-size: smaller; +} + +/* ── Paned (resizable split-view divider) ────────────────── */ + +paned > separator { + background-color: @border; + background-image: none; + min-width: 1px; + min-height: 1px; +} + +paned > separator:hover, +paned > separator:active { + background-color: @brand_green; +} + +paned.wide > separator { + min-width: 4px; + min-height: 4px; + background-color: @border; +} + +paned.wide > separator:hover { + background-color: alpha(@brand_green, 0.6); +} + +/* ── Expander (disclosure triangle + label) ──────────────── */ + +expander { + color: @text_primary; +} + +expander title > arrow { + color: @text_secondary; + min-width: 12px; + min-height: 12px; + -gtk-icon-source: -gtk-icontheme("pan-end-symbolic"); +} + +expander title:hover > arrow { + color: @brand_green; +} + +expander title > arrow:checked { + -gtk-icon-source: -gtk-icontheme("pan-down-symbolic"); + color: @brand_green; +} + +expander title:hover { + color: @brand_green; +} + +/* ── Calendar ────────────────────────────────────────────── */ + +calendar { + background-color: @base_bg; + color: @text_primary; + border: 1px solid @border; + border-radius: 6px; + padding: 4px; +} + +calendar:selected { + background-color: @selected_bg; + color: @selected_fg; + border-radius: 4px; +} + +calendar.header { + background-color: @surface_bg_alt; + border-bottom: 1px solid @border; + color: @text_primary; + font-weight: 600; +} + +calendar.button { + color: @text_secondary; + background-color: transparent; + border-color: transparent; +} + +calendar.button:hover { + color: @brand_green; + background-color: @hover_bg; +} + +calendar.button:disabled { + color: @text_disabled; +} + +calendar.highlight { + color: @brand_green; + font-weight: 600; +} + +calendar:indeterminate { + color: @text_disabled; +} + +/* ── Icon view (e.g. file manager grid view) ─────────────── */ + +iconview { + background-color: @base_bg; + color: @text_primary; +} + +iconview:hover, +iconview .cell:hover { + background-color: @hover_bg; +} + +iconview:selected, +iconview .cell:selected { + background-color: @selected_bg; + color: @selected_fg; + border-radius: 6px; +} + +iconview:selected:focus, +iconview .cell:selected:focus { + background-color: shade(@selected_bg, 1.10); +} + +iconview.dnd { + box-shadow: inset 0 0 0 1px @brand_green; +} + +iconview > rubberband, +.view > rubberband { + background-color: alpha(@brand_green, 0.18); + border: 1px solid @brand_green; +} + +/* ── Generic .view (CellRenderer-based widgets) ──────────── */ + +.view { + background-color: @base_bg; + color: @text_primary; +} + +.view:selected { + background-color: @selected_bg; + color: @selected_fg; +} + +.view:hover { + background-color: @hover_bg; +} + +.view:disabled { + color: @text_disabled; +} + +/* ── Shortcuts window (Ctrl+? help) ──────────────────────── */ + +shortcuts-section, +shortcut { + background-color: @base_bg; + color: @text_primary; +} + +shortcut > .keycap { + background-color: @surface_bg_alt; + border: 1px solid @border_strong; + border-radius: 4px; + color: @text_primary; + min-width: 16px; + padding: 1px 6px; + font-family: monospace; + box-shadow: 0 1px 0 0 @border; +} + +/* ── Selection-mode helpers (matches headerbar selection-mode) ── */ + +.selection-mode { + background-color: @brand_purple_dark; + color: @selected_fg; +} + +.selection-mode button:hover { + background-color: alpha(@selected_fg, 0.15); +} + +/* ── OSD overlay class (generic) ─────────────────────────── */ + +.osd { + background-color: alpha(@overlay_bg, 0.92); + color: @text_primary; + border: 1px solid @border_strong; + border-radius: 8px; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55); +} + +.osd button { + background-color: transparent; + border-color: transparent; + color: @text_primary; +} + +.osd button:hover { + background-color: alpha(@text_primary, 0.10); +} + +.osd entry { + background-color: alpha(#000000, 0.40); + border-color: @border_strong; + color: @text_primary; +} + +.osd entry:focus { + border-color: @brand_green; + box-shadow: inset 0 0 0 1px @brand_green; +} + +/* ── Globally consistent focus ring on common widgets ─────── */ + +*:focus-visible { + outline: 2px solid @focus_ring; + outline-offset: 1px; +} + +/* Some widgets are better off without an outline ring */ +button:focus-visible, +entry:focus-visible, +notebook > header > tabs > tab:focus-visible, +treeview.view:focus-visible, +columnview.view:focus-visible { + /* These have their own focus treatment from earlier segments; the + universal rule above is fine, but keep an explicit empty block to + document the intent rather than chasing override specificity. */ +} + +/* ── Universal insensitive (disabled) handling ───────────── */ + +:disabled, +*:disabled { + -gtk-icon-effect: dim; +} + +/* ── Labels: dim-label, error-label, etc. ────────────────── */ + +label.error, +label.error-message { + color: @error_color; +} + +label.warning { + color: @warning_color; +} + +label.success { + color: @success_color; +} + +label.heading, +.heading { + font-weight: 600; + color: @text_primary; +} + +/* ── Drag-and-drop highlight surfaces ────────────────────── */ + +box:drop(active), +grid:drop(active), +flowbox:drop(active) { + box-shadow: inset 0 0 0 2px @brand_green; + background-color: alpha(@brand_green, 0.08); +} + +/* ── Selection rubberband (drag-select) ──────────────────── */ + +rubberband, +.rubberband { + background-color: alpha(@brand_green, 0.18); + border: 1px solid @brand_green; +} + +/* ── Color & font choosers — small surface touches ───────── */ + +colorswatch, +colorswatch.color-active-badge { + border-radius: 4px; + border: 1px solid @border_strong; +} + +colorswatch:hover { + border-color: @brand_green; +} + +colorswatch.color-light { + color: @text_on_accent; +} + +colorswatch.color-dark { + color: @text_primary; +} + +colorchooser .popover { + background-color: @overlay_bg; +} + +fontchooser .dim-label { + color: @text_secondary; +} + +/* ── Emoji chooser ───────────────────────────────────────── */ + +emoji-chooser, +emoji-chooser .emoji { + background-color: @overlay_bg; + color: @text_primary; + border-radius: 6px; + padding: 2px; +} + +emoji-chooser .emoji:hover { + background-color: @hover_bg; +} + +emoji-chooser .emoji:focus, +emoji-chooser .emoji:checked { + background-color: @selected_bg; +} + +emoji-chooser .emoji-section { + border-top: 1px solid @border; + padding-top: 4px; +} + +/* ── Placeholder text in entries ─────────────────────────── */ + +entry placeholder, +.entry placeholder { + color: @text_disabled; + opacity: 1.0; +} + +/* ── Scaling fonts / image effects in disabled state ─────── */ + +image:disabled, +icon:disabled { + -gtk-icon-effect: dim; + opacity: 0.5; +} + +/* ── Nemo file manager ───────────────────────────────────── */ +/* Compact the top toolbar and the bottom status bar. */ +.nemo-window .primary-toolbar, +.nemo-window statusbar { + padding-top: 0; + padding-bottom: 0; + min-height: 0; +} + +/* Slimmer buttons across the toolbar/statusbar chrome. */ +.nemo-window .primary-toolbar button, +.nemo-window statusbar button, +.nemo-window statusbar radiobutton { + min-height: 0; + padding-top: 3px; + padding-bottom: 3px; +} + +/* The zoom slider's tall padding inflates the bottom bar. */ +.nemo-window statusbar scale { + min-height: 0; + padding-top: 2px; + padding-bottom: 2px; +} + +/* The item/size readout sits in a plain GtkFrame — give it tight, + symmetric padding (the group-frame rule's asymmetric padding is + wrong for it and shoves the text off-centre) and a larger font. */ +.nemo-window statusbar frame > border { + padding: 1px 8px; +} + +.nemo-window statusbar label { + font-size: 11pt; +} + +/* ── Desktop icon labels (xfdesktop 4.20) ────────────────── + xfdesktop ships its own built-in CSS for the desktop icon + view. Its label rule is: + XfdesktopIconView.view.label { color: @theme_selected_fg_color; } + In our palette theme_selected_fg_color resolves to #0a0a00 + (text_on_accent — meant to sit on lime-green), so unselected + desktop labels render near-black and vanish on the dark + wallpaper. Override with the exact selectors xfdesktop uses + (CSS node name is the GType name "XfdesktopIconView", the + label is a ".view.label" sub-node) so the cascade wins. */ +XfdesktopIconView.view.label { + color: @text_primary; + background-color: transparent; + border-radius: 3px; + text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.9); +} + +/* Selected icon: purple chip behind the (white) label. */ +XfdesktopIconView.view.label:selected { + color: @selected_fg; + background-color: @selected_bg; + text-shadow: none; +} + +XfdesktopIconView.view.label:selected:backdrop { + background-color: alpha(@selected_bg, 0.5); +} + +/* Keyboard-cursor / active item tint. */ +XfdesktopIconView.view:active { + color: @selected_bg; +} + +/* Rubber-band marquee when drag-selecting icons. */ +XfdesktopIconView.rubberband { + border: 1px solid @selected_bg; + background-color: alpha(@selected_bg, 0.2); +} diff --git a/assets/themes/NexusOS/gtk-3.0/scrollbars-progress.css b/assets/themes/NexusOS/gtk-3.0/scrollbars-progress.css new file mode 100644 index 0000000..dbebc43 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/scrollbars-progress.css @@ -0,0 +1,306 @@ +/* NexusOS — Segment 7: scrollbars, progress bars, level bars, spinners. + Scrollbars are slim and overlay-ish; the slider thickens on hover. + Progress fills use the lime brand color. Spinners are lime too. */ + +/* ── Scrollbars ──────────────────────────────────────────── */ + +scrollbar { + background-color: transparent; + border: none; + transition: all 160ms ease; +} + +scrollbar.horizontal { + min-height: 12px; +} + +scrollbar.vertical { + min-width: 12px; +} + +scrollbar trough { + background-color: transparent; + border: none; + border-radius: 999px; + margin: 0; +} + +scrollbar:hover trough { + background-color: alpha(@surface_bg, 0.55); +} + +/* The draggable slider */ +scrollbar slider { + background-color: alpha(@text_primary, 0.25); + border: 2px solid transparent; + border-radius: 999px; + background-clip: padding-box; + min-width: 4px; + min-height: 4px; + transition: background-color 160ms ease, + min-width 160ms ease, + min-height 160ms ease; +} + +scrollbar.horizontal slider { + min-width: 32px; + min-height: 4px; +} + +scrollbar.vertical slider { + min-width: 4px; + min-height: 32px; +} + +scrollbar:hover slider, +scrollbar.hovering slider { + background-color: alpha(@brand_green, 0.55); +} + +scrollbar.horizontal:hover slider, +scrollbar.horizontal.hovering slider { + min-height: 8px; +} + +scrollbar.vertical:hover slider, +scrollbar.vertical.hovering slider { + min-width: 8px; +} + +scrollbar slider:hover { + background-color: @brand_green; +} + +scrollbar slider:active, +scrollbar slider:hover:active { + background-color: @brand_green_dark; +} + +scrollbar slider:disabled { + background-color: transparent; +} + +/* Fine-tune mode (the user is scrolling slowly with Shift held) */ +scrollbar.fine-tune slider { + min-width: 4px; + min-height: 4px; +} + +/* Hide the legacy stepper buttons unless the app explicitly opts in */ +scrollbar button { + min-width: 0; + min-height: 0; + padding: 0; + margin: 0; + border: none; + background: transparent; + -GtkScrollbar-has-backward-stepper: 0; + -GtkScrollbar-has-forward-stepper: 0; +} + +/* Overlay vs always-shown — overlay variant fades in over content */ +scrollbar.overlay-indicator { + background-color: transparent; +} + +scrollbar.overlay-indicator:not(.dragging):not(.hovering) { + opacity: 0.45; +} + +scrollbar.overlay-indicator.dragging, +scrollbar.overlay-indicator.hovering { + opacity: 1.0; +} + +/* Undershoot / overshoot hints in scrolled windows */ +scrolledwindow undershoot.top, +scrolledwindow undershoot.bottom, +scrolledwindow undershoot.left, +scrolledwindow undershoot.right { + background-image: none; + background-color: transparent; +} + +scrolledwindow overshoot.top { + background-image: linear-gradient(to bottom, + alpha(@brand_green, 0.25), + transparent); + background-repeat: no-repeat; + background-size: 100% 24px; + background-position: top; +} + +scrolledwindow overshoot.bottom { + background-image: linear-gradient(to top, + alpha(@brand_green, 0.25), + transparent); + background-repeat: no-repeat; + background-size: 100% 24px; + background-position: bottom; +} + +scrolledwindow overshoot.left { + background-image: linear-gradient(to right, + alpha(@brand_green, 0.25), + transparent); + background-repeat: no-repeat; + background-size: 24px 100%; + background-position: left; +} + +scrolledwindow overshoot.right { + background-image: linear-gradient(to left, + alpha(@brand_green, 0.25), + transparent); + background-repeat: no-repeat; + background-size: 24px 100%; + background-position: right; +} + +/* Junction (the small square where horizontal + vertical scrollbars meet) */ +scrolledwindow junction { + background-color: transparent; + border: none; +} + +/* ── Progress bars ───────────────────────────────────────── */ + +progressbar { + color: @text_secondary; + font-size: smaller; +} + +progressbar trough { + background-color: @surface_bg_alt; + background-image: none; + border: 1px solid @border; + border-radius: 999px; + min-height: 6px; + min-width: 6px; +} + +progressbar progress { + background-color: @brand_green; + background-image: none; + border: none; + border-radius: 999px; + box-shadow: 0 0 6px alpha(@brand_green, 0.35); +} + +progressbar.horizontal trough, +progressbar.horizontal progress { + min-height: 6px; +} + +progressbar.vertical trough, +progressbar.vertical progress { + min-width: 6px; +} + +progressbar:disabled trough { + background-color: @surface_bg; +} + +progressbar:disabled progress { + background-color: @text_disabled; + box-shadow: none; +} + +/* OSD progressbar — used as an overlay on top of media etc. */ +progressbar.osd { + color: #ffffff; +} + +progressbar.osd trough { + background-color: alpha(#000000, 0.45); + border-color: transparent; +} + +progressbar.osd progress { + background-color: @brand_green; + box-shadow: 0 0 8px alpha(@brand_green, 0.55); +} + +/* ── Level bars ──────────────────────────────────────────── */ + +levelbar { + color: @text_secondary; +} + +levelbar trough { + background-color: @surface_bg_alt; + border: 1px solid @border; + border-radius: 4px; + padding: 2px; + min-height: 6px; + min-width: 6px; +} + +levelbar.horizontal trough, +levelbar.horizontal block { + min-height: 6px; +} + +levelbar.vertical trough, +levelbar.vertical block { + min-width: 6px; +} + +levelbar block { + border-radius: 2px; + margin: 0 1px; + min-width: 32px; + background-color: @brand_green; + border: none; +} + +levelbar block.empty { + background-color: alpha(@text_primary, 0.10); +} + +levelbar block.low { + background-color: @warning_color; +} + +levelbar block.high { + background-color: @brand_green; +} + +levelbar block.full { + background-color: @brand_green_light; +} + +levelbar.discrete block { + min-width: 24px; + margin: 0 1px; +} + +levelbar:disabled block { + background-color: @text_disabled; +} + +/* ── Spinner ─────────────────────────────────────────────── */ + +spinner { + background: none; + opacity: 0; + -gtk-icon-source: -gtk-icontheme("process-working-symbolic"); + color: @brand_green; + min-width: 16px; + min-height: 16px; + transition: opacity 200ms ease; +} + +spinner:checked { + opacity: 1; + animation: nexus_spinner 1s linear infinite; +} + +spinner:disabled { + opacity: 0.4; + color: @text_disabled; +} + +@keyframes nexus_spinner { + to { -gtk-icon-transform: rotate(1turn); } +} diff --git a/assets/themes/NexusOS/gtk-3.0/toggles-sliders.css b/assets/themes/NexusOS/gtk-3.0/toggles-sliders.css new file mode 100644 index 0000000..6cf0281 --- /dev/null +++ b/assets/themes/NexusOS/gtk-3.0/toggles-sliders.css @@ -0,0 +1,377 @@ +/* NexusOS — Segment 8: switches, checkboxes, radios, scales/sliders. + Switches and checks fill with lime when on. Scales use the lime knob and + a purple highlight on the filled portion of the track. */ + +/* ── Switch (GtkSwitch) ──────────────────────────────────── */ + +switch { + background-color: @surface_bg_alt; + background-image: none; + border: 1px solid @border_strong; + border-radius: 999px; + min-width: 42px; + min-height: 22px; + padding: 0; + color: transparent; /* hide internal ON/OFF labels */ + font-size: 0; + box-shadow: none; + transition: background-color 160ms ease, + border-color 160ms ease; +} + +switch:hover { + background-color: @overlay_bg; + border-color: @border_strong; +} + +switch:focus, +switch:focus-visible { + outline: 2px solid @focus_ring; + outline-offset: 2px; +} + +switch:checked { + background-color: @brand_green; + border-color: @brand_green_dark; + color: transparent; +} + +switch:checked:hover { + background-color: @brand_green_light; + border-color: @brand_green; +} + +switch:disabled { + background-color: @surface_bg; + border-color: @border; + opacity: 0.6; +} + +switch:checked:disabled { + background-color: alpha(@brand_green, 0.35); + border-color: transparent; +} + +/* The sliding knob */ +switch slider { + background-color: @text_primary; + background-image: none; + border: 1px solid @border_strong; + border-radius: 999px; + min-width: 18px; + min-height: 18px; + margin: 1px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35); + transition: all 160ms ease; +} + +switch:checked slider { + background-color: #ffffff; + border-color: @brand_green_dark; +} + +switch:disabled slider { + background-color: @text_disabled; + box-shadow: none; +} + +/* ── Checkbox ────────────────────────────────────────────── */ + +check, +checkbutton check { + min-width: 16px; + min-height: 16px; + padding: 1px; + background-color: @surface_bg; + background-image: none; + border: 1px solid @border_strong; + border-radius: 4px; + color: transparent; + -gtk-icon-source: none; + -gtk-icon-shadow: none; + transition: background-color 120ms ease, + border-color 120ms ease; +} + +check:hover, +checkbutton check:hover { + border-color: @brand_green_dark; + background-color: @hover_bg; +} + +check:focus, +checkbutton check:focus { + outline: 2px solid @focus_ring; + outline-offset: 1px; +} + +check:checked, +checkbutton check:checked { + background-color: @brand_green; + border-color: @brand_green_dark; + color: @text_on_accent; + -gtk-icon-source: -gtk-icontheme("object-select-symbolic"); +} + +check:checked:hover, +checkbutton check:checked:hover { + background-color: @brand_green_light; + border-color: @brand_green; +} + +check:indeterminate, +checkbutton check:indeterminate { + background-color: @brand_green; + border-color: @brand_green_dark; + color: @text_on_accent; + -gtk-icon-source: -gtk-icontheme("checkbox-mixed-symbolic"); +} + +check:disabled, +checkbutton check:disabled { + background-color: @insensitive_bg_color; + border-color: @border; + color: @text_disabled; +} + +check:checked:disabled, +checkbutton check:checked:disabled { + background-color: alpha(@brand_green, 0.35); + color: @text_disabled; + border-color: transparent; +} + +/* The label next to the check */ +checkbutton:disabled, +checkbutton:disabled label { + color: @text_disabled; +} + +/* ── Radio button ────────────────────────────────────────── */ + +radio, +radiobutton radio { + min-width: 16px; + min-height: 16px; + padding: 1px; + background-color: @surface_bg; + background-image: none; + border: 1px solid @border_strong; + border-radius: 999px; + color: transparent; + -gtk-icon-source: none; + transition: background-color 120ms ease, + border-color 120ms ease; +} + +radio:hover, +radiobutton radio:hover { + border-color: @brand_green_dark; + background-color: @hover_bg; +} + +radio:focus, +radiobutton radio:focus { + outline: 2px solid @focus_ring; + outline-offset: 1px; +} + +radio:checked, +radiobutton radio:checked { + background-color: @surface_bg; + border-color: @brand_green; + color: @brand_green; + /* Inner filled dot via icon */ + -gtk-icon-source: -gtk-icontheme("radio-checked-symbolic"); +} + +radio:checked:hover, +radiobutton radio:checked:hover { + border-color: @brand_green_light; +} + +radio:disabled, +radiobutton radio:disabled { + background-color: @insensitive_bg_color; + border-color: @border; +} + +radio:checked:disabled, +radiobutton radio:checked:disabled { + color: @text_disabled; + border-color: @border; +} + +radiobutton:disabled, +radiobutton:disabled label { + color: @text_disabled; +} + +/* Gap between the indicator and its label. Without this the label sits + flush against the box. first/last-child keeps it correct under RTL; + :only-child excludes indicator-only (label-less) check/radio buttons. */ +checkbutton check:first-child:not(:only-child), +radiobutton radio:first-child:not(:only-child) { + margin-right: 6px; +} + +checkbutton check:last-child:not(:only-child), +radiobutton radio:last-child:not(:only-child) { + margin-left: 6px; +} + +/* ── Scale / slider (GtkScale) ───────────────────────────── */ + +scale { + min-height: 18px; + min-width: 18px; + padding: 8px 4px; + color: @text_primary; +} + +scale.horizontal { + min-height: 18px; +} + +scale.vertical { + min-width: 18px; +} + +/* The track */ +scale trough { + background-color: @surface_bg_alt; + border: 1px solid @border; + border-radius: 999px; + min-height: 4px; + min-width: 4px; +} + +scale.horizontal trough { + min-height: 4px; +} + +scale.vertical trough { + min-width: 4px; +} + +/* The filled portion (left of the knob on a horizontal scale) */ +scale highlight { + background-color: @brand_purple; + background-image: none; + border-radius: 999px; + min-height: 4px; + min-width: 4px; +} + +scale.horizontal highlight { + min-height: 4px; +} + +scale.vertical highlight { + min-width: 4px; +} + +/* A secondary fill (e.g. media-player buffered range) */ +scale fill { + background-color: alpha(@brand_purple, 0.35); + border-radius: 999px; + min-height: 4px; + min-width: 4px; +} + +scale:disabled trough, +scale:disabled highlight, +scale:disabled fill { + background-color: @insensitive_bg_color; +} + +scale:disabled highlight { + background-color: alpha(@brand_purple, 0.30); +} + +/* The knob */ +scale slider { + background-color: @brand_green; + background-image: none; + border: 1px solid @brand_green_dark; + border-radius: 999px; + min-width: 14px; + min-height: 14px; + margin: -6px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.40); + transition: background-color 120ms ease, + box-shadow 120ms ease, + min-width 120ms ease, + min-height 120ms ease; +} + +scale slider:hover { + background-color: @brand_green_light; + border-color: @brand_green; + box-shadow: 0 0 0 4px alpha(@brand_green, 0.18), + 0 1px 3px rgba(0, 0, 0, 0.40); +} + +scale slider:active { + background-color: @brand_green_dark; + box-shadow: 0 0 0 6px alpha(@brand_green, 0.25), + 0 1px 3px rgba(0, 0, 0, 0.40); +} + +scale slider:focus { + outline: 2px solid @focus_ring; + outline-offset: 2px; +} + +scale slider:disabled { + background-color: @text_disabled; + border-color: @border; + box-shadow: none; +} + +/* Tick marks */ +scale marks { + color: @text_secondary; +} + +scale marks indicator { + background-color: @text_secondary; + min-height: 4px; + min-width: 1px; +} + +scale.has-marks-above { + margin-top: 4px; +} + +scale.has-marks-below { + margin-bottom: 4px; +} + +/* Value label (shown when draw-value is set) */ +scale value { + color: @text_primary; + padding: 0 6px; + font-feature-settings: "tnum"; +} + +/* "Fine-tune" mode — the slider gets a slightly bigger halo */ +scale.fine-tune slider { + box-shadow: 0 0 0 6px alpha(@brand_green, 0.25), + 0 1px 3px rgba(0, 0, 0, 0.40); +} + +/* OSD scale (used in media players over video) */ +scale.osd trough { + background-color: alpha(#000000, 0.45); + border-color: transparent; +} + +scale.osd highlight { + background-color: @brand_green; +} + +scale.osd slider { + background-color: @brand_green; + border-color: @brand_green_dark; +} diff --git a/assets/themes/NexusOS/index.theme b/assets/themes/NexusOS/index.theme new file mode 100644 index 0000000..78db4c5 --- /dev/null +++ b/assets/themes/NexusOS/index.theme @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=X-GNOME-Metatheme +Name=NexusOS +Comment=NexusOS dark theme — lime green + purple accents drawn from the NexusOS logo +Encoding=UTF-8 + +[X-GNOME-Metatheme] +GtkTheme=NexusOS +MetacityTheme=NexusOS +IconTheme=NexusOS +CursorTheme=DMZ-White +ButtonLayout=close,minimize,maximize:menu diff --git a/assets/themes/NexusOS/xfwm4/bottom-active.png b/assets/themes/NexusOS/xfwm4/bottom-active.png new file mode 100644 index 0000000..82b980d Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/bottom-inactive.png b/assets/themes/NexusOS/xfwm4/bottom-inactive.png new file mode 100644 index 0000000..82b980d Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/bottom-left-active.png b/assets/themes/NexusOS/xfwm4/bottom-left-active.png new file mode 100644 index 0000000..62a022c Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-left-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/bottom-left-inactive.png b/assets/themes/NexusOS/xfwm4/bottom-left-inactive.png new file mode 100644 index 0000000..62a022c Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-left-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/bottom-right-active.png b/assets/themes/NexusOS/xfwm4/bottom-right-active.png new file mode 100644 index 0000000..158a577 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-right-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/bottom-right-inactive.png b/assets/themes/NexusOS/xfwm4/bottom-right-inactive.png new file mode 100644 index 0000000..158a577 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/bottom-right-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/close-active.png b/assets/themes/NexusOS/xfwm4/close-active.png new file mode 100644 index 0000000..ff2f95e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/close-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/close-inactive.png b/assets/themes/NexusOS/xfwm4/close-inactive.png new file mode 100644 index 0000000..76211a8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/close-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/close-prelight.png b/assets/themes/NexusOS/xfwm4/close-prelight.png new file mode 100644 index 0000000..b8b0d2a Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/close-prelight.png differ diff --git a/assets/themes/NexusOS/xfwm4/close-pressed.png b/assets/themes/NexusOS/xfwm4/close-pressed.png new file mode 100644 index 0000000..52285e4 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/close-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/hide-active.png b/assets/themes/NexusOS/xfwm4/hide-active.png new file mode 100644 index 0000000..7b1f0d6 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/hide-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/hide-inactive.png b/assets/themes/NexusOS/xfwm4/hide-inactive.png new file mode 100644 index 0000000..76211a8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/hide-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/hide-prelight.png b/assets/themes/NexusOS/xfwm4/hide-prelight.png new file mode 100644 index 0000000..861ae67 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/hide-prelight.png differ diff --git a/assets/themes/NexusOS/xfwm4/hide-pressed.png b/assets/themes/NexusOS/xfwm4/hide-pressed.png new file mode 100644 index 0000000..c343c8d Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/hide-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/left-active.png b/assets/themes/NexusOS/xfwm4/left-active.png new file mode 100644 index 0000000..9ee3767 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/left-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/left-inactive.png b/assets/themes/NexusOS/xfwm4/left-inactive.png new file mode 100644 index 0000000..9ee3767 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/left-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-active.png b/assets/themes/NexusOS/xfwm4/maximize-active.png new file mode 100644 index 0000000..4007a17 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-inactive.png b/assets/themes/NexusOS/xfwm4/maximize-inactive.png new file mode 100644 index 0000000..76211a8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-prelight.png b/assets/themes/NexusOS/xfwm4/maximize-prelight.png new file mode 100644 index 0000000..f9233c2 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-prelight.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-pressed.png b/assets/themes/NexusOS/xfwm4/maximize-pressed.png new file mode 100644 index 0000000..a0dcd98 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-toggled-active.png b/assets/themes/NexusOS/xfwm4/maximize-toggled-active.png new file mode 100644 index 0000000..4007a17 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-toggled-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-toggled-inactive.png b/assets/themes/NexusOS/xfwm4/maximize-toggled-inactive.png new file mode 100644 index 0000000..76211a8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-toggled-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-toggled-prelight.png b/assets/themes/NexusOS/xfwm4/maximize-toggled-prelight.png new file mode 100644 index 0000000..c63497d Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-toggled-prelight.png differ diff --git a/assets/themes/NexusOS/xfwm4/maximize-toggled-pressed.png b/assets/themes/NexusOS/xfwm4/maximize-toggled-pressed.png new file mode 100644 index 0000000..6407f40 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/maximize-toggled-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/menu-active.png b/assets/themes/NexusOS/xfwm4/menu-active.png new file mode 100644 index 0000000..c56a275 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/menu-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/menu-inactive.png b/assets/themes/NexusOS/xfwm4/menu-inactive.png new file mode 100644 index 0000000..3545fd1 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/menu-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/menu-pressed.png b/assets/themes/NexusOS/xfwm4/menu-pressed.png new file mode 100644 index 0000000..69aad60 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/menu-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/right-active.png b/assets/themes/NexusOS/xfwm4/right-active.png new file mode 100644 index 0000000..4a9cb4e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/right-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/right-inactive.png b/assets/themes/NexusOS/xfwm4/right-inactive.png new file mode 100644 index 0000000..4a9cb4e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/right-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/shade-active.png b/assets/themes/NexusOS/xfwm4/shade-active.png new file mode 100644 index 0000000..1153daa Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/shade-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/shade-inactive.png b/assets/themes/NexusOS/xfwm4/shade-inactive.png new file mode 100644 index 0000000..0549f20 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/shade-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/shade-pressed.png b/assets/themes/NexusOS/xfwm4/shade-pressed.png new file mode 100644 index 0000000..f515f06 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/shade-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/stick-active.png b/assets/themes/NexusOS/xfwm4/stick-active.png new file mode 100644 index 0000000..e1a6542 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/stick-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/stick-inactive.png b/assets/themes/NexusOS/xfwm4/stick-inactive.png new file mode 100644 index 0000000..4d31a33 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/stick-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/stick-pressed.png b/assets/themes/NexusOS/xfwm4/stick-pressed.png new file mode 100644 index 0000000..d2d205a Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/stick-pressed.png differ diff --git a/assets/themes/NexusOS/xfwm4/themerc b/assets/themes/NexusOS/xfwm4/themerc new file mode 100644 index 0000000..8ed1a77 --- /dev/null +++ b/assets/themes/NexusOS/xfwm4/themerc @@ -0,0 +1,23 @@ +button_offset=10 +button_spacing=0 + +show_app_icon=false + +full_width_title=true + +title_shadow_active=false +title_shadow_inactive=false + +title_horizontal_offset=3 + +active_text_color=#afafaf +active_text_shadow_color=#252525 + +inactive_text_color=#808080 +inactive_text_shadow_color=#252525 + +shadow_delta_height=2 +shadow_delta_width=0 +shadow_delta_x=0 +shadow_delta_y=-5 +shadow_opacity=40 diff --git a/assets/themes/NexusOS/xfwm4/title-1-active.png b/assets/themes/NexusOS/xfwm4/title-1-active.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-1-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-1-inactive.png b/assets/themes/NexusOS/xfwm4/title-1-inactive.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-1-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-2-active.png b/assets/themes/NexusOS/xfwm4/title-2-active.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-2-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-2-inactive.png b/assets/themes/NexusOS/xfwm4/title-2-inactive.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-2-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-3-active.png b/assets/themes/NexusOS/xfwm4/title-3-active.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-3-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-3-inactive.png b/assets/themes/NexusOS/xfwm4/title-3-inactive.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-3-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-4-active.png b/assets/themes/NexusOS/xfwm4/title-4-active.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-4-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-4-inactive.png b/assets/themes/NexusOS/xfwm4/title-4-inactive.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-4-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-5-active.png b/assets/themes/NexusOS/xfwm4/title-5-active.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-5-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/title-5-inactive.png b/assets/themes/NexusOS/xfwm4/title-5-inactive.png new file mode 100644 index 0000000..fb0122e Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/title-5-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/top-left-active.png b/assets/themes/NexusOS/xfwm4/top-left-active.png new file mode 100644 index 0000000..b733ef8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/top-left-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/top-left-inactive.png b/assets/themes/NexusOS/xfwm4/top-left-inactive.png new file mode 100644 index 0000000..b733ef8 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/top-left-inactive.png differ diff --git a/assets/themes/NexusOS/xfwm4/top-right-active.png b/assets/themes/NexusOS/xfwm4/top-right-active.png new file mode 100644 index 0000000..8e85f93 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/top-right-active.png differ diff --git a/assets/themes/NexusOS/xfwm4/top-right-inactive.png b/assets/themes/NexusOS/xfwm4/top-right-inactive.png new file mode 100644 index 0000000..8e85f93 Binary files /dev/null and b/assets/themes/NexusOS/xfwm4/top-right-inactive.png differ diff --git a/assets/themes/README.md b/assets/themes/README.md new file mode 100644 index 0000000..46ce863 --- /dev/null +++ b/assets/themes/README.md @@ -0,0 +1,178 @@ +# NexusOS desktop theme + +XFCE/GTK desktop theme (distinct from the web UI in `interface/web/`). +Lime-green + purple, dark, with tightened menus. + +## Layout + +| Path | What | +|---|---| +| `NexusOS/` | GTK2/3 + xfwm4 theme. **GTK3 CSS in `NexusOS/gtk-3.0/` is hand-maintained** (no build step). | +| `NexusOS/gtk-3.0/colors.css` | All color tokens. Edit colors here, not in the widget files. | +| `NexusOS-icons/` | Icon theme (inherits `Papirus-Dark`). | +| `_palette.py` + `*-src/build.py` | Regenerate gtk2 / icons / xfwm4 — **NOT gtk-3.0**. | +| `NexusOS-icons-src/build_actions.py` | Regenerate the `actions` icons (logoff dialog / Whisker session buttons). | +| `install-theme.sh` | Idempotent restore of all the *wiring* (see below). | +| `gtk3-user-overrides.css` | Symlinked to `~/.config/gtk-3.0/gtk.css`. | + +## How it's wired (the part backups miss) + +The files above are just *source*. What makes the desktop use them lives +outside this folder and is recreated by `install-theme.sh`: + +- Symlinks: `~/.themes/NexusOS`, `~/.icons/NexusOS`, + `~/.config/gtk-3.0/gtk.css`. +- xfconf: `xsettings` (`/Net/ThemeName`, `/Net/IconThemeName`, + `/Gtk/CursorThemeName`, `/Gtk/FontName`) and `xfwm4 /general/theme`. +- `~/.config/gtk-3.0/settings.ini` **and** `~/.config/gtk-4.0/settings.ini`. + +Canonical values: theme `NexusOS`, icons `NexusOS`, cursor `DMZ-White`, +font `Ubuntu 10`. Keep these in sync with `NexusOS/index.theme` and +`install-theme.sh`. + +## Apply / reload after editing + +```bash +xfconf-query -c xsettings -p /Net/ThemeName -s Adwaita +xfconf-query -c xsettings -p /Net/ThemeName -s NexusOS +xfdesktop --reload & xfce4-panel -r +``` + +Some apps cache the theme at startup and need a full restart. + +## Backup / restore + +- `ncp backup -f` (`python bin/sync.py backup --full`): `bin/backup-linux.sh` + snapshots live wiring into `restore-snapshot/`, then the tree is committed + and pushed to Gitea. +- `ncp restore` (`python bin/sync.py restore`): pull, rebuild venv/frontend, + then `bin/restore-linux.sh desktop` auto-runs `install-theme.sh`. Add + `--check` for a dry run. +- Both stages are Linux-only and skipped on Windows; `bin/sync.py` is the + entry point on both machines. + +## Gotchas (each cost real debugging time) + +1. **Theme silently falls back to Adwaita** if `gtk-theme-name` in + `settings.ini` and xfconf `xsettings/Net/ThemeName` disagree, or + point at a non-existent theme. Edits then have zero visible effect. +2. **`settings.ini` is machine-generated** by xfsettingsd from + `xsettings.xml` — don't symlink it into the repo. **GTK4 has its own + `~/.config/gtk-4.0/settings.ini`** that xsettings does *not* override; + it must be reconciled separately (easy to forget). +3. **`@border` == `@overlay_bg` (`#2e3236`)** in `colors.css`, so a + separator drawn in `@border` on an overlay surface is invisible. Use + `@border_strong` / `@menu_border`. Root collision is unfixed; patched + per-spot. +4. **`gtk-menu-images`** toggles icons in classic GtkMenus only. It does + **not** affect the Whisker menu (separate widget tree). +5. **Whisker menu**: the popup's identifier is set via + `gtk_widget_set_name()`, so CSS must target **`#whiskermenu-window` + (an #id), not `.whiskermenu-window` (a class)** — a class selector + matches nothing. The base `window.background` rule also outranks a + bare class; `#id` wins on both counts. Whisker version: 2.9.x. + Its app/category lists are `treeview`/`iconview`; restart + `xfce4-panel` to reload its CSS. +6. **Menu surfaces** use `@menu_bg` (`#2a1d33`) / `@menu_border` + (`#4d3461`) — dark purple. Tooltips/menubars/cards stay on the gray + surface palette deliberately. +7. **`.gitignore` does not support trailing/inline comments.** A + `Dir/ # note` line matches nothing — comments must be on their own + line. (This once let a `git add` start ingesting the 28 GB venv.) + +--- + +## KDE Plasma migration (`KDE/`) + +All KDE Plasma theme assets live under `KDE/`. These are built to mirror the NexusOS +visual design — same palette, same flat aesthetic — in KDE-native formats. The GTK 3 +theme and icon theme carry over unchanged. + +``` +KDE/ + install-plasma.sh # idempotent installer (run once after switching) + generate_plasma_colors.py # regenerate NexusOS.colors from _palette.py + + plasma/NexusOS/ # Plasma shell theme (panel, widgets, tooltips) + NexusOS.colors # KDE color scheme — source of truth for Qt/KDE colors + colors # Plasma shell palette overrides + widgets/*.svg # 9-slice SVGs: panel-background, tooltip, button, etc. + opaque/widgets/ # compositor-off variants + + kvantum/NexusOS/ # Qt5/Qt6 app styling + NexusOS.kvconfig # widget geometry + element references + NexusOS.svg # flat SVG widget drawings + + aurorae/NexusOS/ # KWin window decoration + NexusOSrc # titlebar height, button layout, colors + decoration.svg # window frame (9-slice) + close/maximize/minimize/restore/alldesktops/keepabove/keepbelow/shade.svg + + sddm/NexusOS-QML/ # Login screen (already deployed; palette now aligned) + Main.qml # QML login UI — NexusOS purple/green palette + assets/background.svg assets/logo.png + + kscreenlocker/NexusOS/ # Runtime screen lock (Meta+L in KDE) + contents/ui/LockScreenUi.qml # matches SDDM aesthetic; kscreenlocker API + + konsole/ + NexusOS.colorscheme # general terminal colors + NexusOS-Promethean.colorscheme # deep purple — matches promethean-kitty.conf + Promethean.profile # Konsole profile: rcfile, cursor, color scheme +``` + +### What survives the switch unchanged + +| Asset | Status | +|---|---| +| `NexusOS/gtk-3.0/` (all CSS) | GTK apps on Plasma use it as-is | +| `NexusOS-icons/` | Freedesktop spec — works on any DE | +| `_palette.py` / `colors.css` | Source of truth; KDE palette generated from it | +| Plymouth boot splash | System-level; unaffected by DE switch | + +### Installing before the switch (XFCE) + +**Testable now:** +```bash +# Kvantum (Qt5 app styling — works on XFCE) +sudo apt install qt5-style-kvantum qt5-style-kvantum-themes +ln -sfn "$PWD/assets/themes/KDE/kvantum/NexusOS" ~/.config/Kvantum/NexusOS +kvantummanager --set NexusOS # then open any Qt5 app + +# Konsole colors (works on XFCE if konsole installed) +cp assets/themes/KDE/konsole/*.colorscheme assets/themes/KDE/konsole/Promethean.profile \ + ~/.local/share/konsole/ + +# SDDM palette (already live; installer will redeploy if needed) +``` + +**Needs KDE session:** Aurorae decoration, Plasma shell theme, kscreenlocker. + +### First boot into Plasma + +```bash +assets/themes/KDE/install-plasma.sh +``` + +Then in System Settings → Appearance verify: +- Global Theme: (manual if needed — set individual components below) +- Colors: NexusOS +- Application Style: Kvantum-dark +- Plasma Style: NexusOS +- Window Decorations: NexusOS +- Icons: NexusOS + +Test lock screen: **Meta+L** + +### Promethean Terminal on KDE + +`bin/promethean/promethean-terminal.desktop` now uses `konsole --profile Promethean`. +The Promethean Konsole profile configures the rcfile, deep-purple color scheme, +and `#b040c0` cursor. The kitty fallback line is commented out in the `.desktop`. + +### Palette alignment note + +The SDDM `NexusOS-QML` theme previously used a navy/cyan palette (`#0f1626` / `#00d4ff`). +It has been updated to the NexusOS palette (`#1e1526` / `#8cc63f`) for consistency. +The live SDDM at `/usr/share/sddm/themes/NexusOS-QML/` still has the old colors; +`install-plasma.sh` will replace it. diff --git a/assets/themes/_palette.py b/assets/themes/_palette.py new file mode 100644 index 0000000..eba09ec --- /dev/null +++ b/assets/themes/_palette.py @@ -0,0 +1,44 @@ +"""Shared color palette for NexusOS theme builds. + +Source of truth is NexusOS/gtk-3.0/colors.css. Keep these in sync. +Hex strings have no leading '#'. +""" + +# Brand +BRAND_GREEN = "8cc63f" +BRAND_GREEN_LIGHT = "b8e373" +BRAND_GREEN_DARK = "6ba62a" +BRAND_PURPLE = "88008f" +BRAND_PURPLE_LIGHT = "a232a8" +BRAND_PURPLE_DARK = "5e0066" + +# Foundation +BASE_BG = "1e1526" +SURFACE_BG = "1f2225" +SURFACE_BG_ALT = "2a2e32" +OVERLAY_BG = "2e3236" +BORDER = "2e3236" +BORDER_STRONG = "3a3d41" + +# Text +TEXT_PRIMARY = "f2f2f2" +TEXT_SECONDARY = "a8a8a8" +TEXT_DISABLED = "6e7173" +TEXT_ON_ACCENT = "0a0a00" +TEXT_ON_SELECTION = "ffffff" + +# Semantic +SUCCESS = "27ae60" +WARNING = "f67400" +ERROR = "da4453" + + +def hex_to_rgb(h): + h = h.lstrip("#") + return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)) + + +def hex_to_hsv(h): + import colorsys + r, g, b = hex_to_rgb(h) + return colorsys.rgb_to_hsv(r / 255, g / 255, b / 255) diff --git a/assets/themes/gtk3-user-overrides.css b/assets/themes/gtk3-user-overrides.css new file mode 100644 index 0000000..4384d30 --- /dev/null +++ b/assets/themes/gtk3-user-overrides.css @@ -0,0 +1,6 @@ +/* NexusOS — user-level GTK3 overrides. + Symlinked to ~/.config/gtk-3.0/gtk.css so it lives with the rest of + the NexusOS theme in nexus-core. The theme proper is in NexusOS/. */ +* { + caret-color: #8cc63f; +} diff --git a/assets/themes/install-theme.sh b/assets/themes/install-theme.sh new file mode 100644 index 0000000..5de40e1 --- /dev/null +++ b/assets/themes/install-theme.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# install-theme.sh — restore the NexusOS desktop theme wiring. +# +# The theme *assets* live in this repo (assets/themes/). +# This script recreates everything OUTSIDE the repo that makes the desktop +# actually use them: symlinks, xfconf (xsettings + xfwm4), and the canonical +# lines in the GTK 3/4 settings.ini files. +# +# Idempotent and safe: re-running only fixes drift; any real file it would +# replace with a symlink is backed up to .bak-YYYYMMDD first. +# +# Usage: ./assets/themes/install-theme.sh (apply + reload) +# ./assets/themes/install-theme.sh --no-reload +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +RELOAD=1 +[[ "${1:-}" == "--no-reload" ]] && RELOAD=0 +STAMP="$(date +%Y%m%d)" + +# Canonical values — keep in sync with assets/themes/NexusOS/index.theme +GTK_THEME="NexusOS" +ICON_THEME="NexusOS" +CURSOR_THEME="DMZ-White" +CURSOR_SIZE="24" +FONT_NAME="Ubuntu 10" + +say() { printf ' %s\n' "$*"; } + +# link : make linkname a symlink to target, backing up +# any pre-existing real file/dir that isn't already the correct link. +link() { + local target="$1" linkname="$2" + mkdir -p "$(dirname "$linkname")" + if [[ -L "$linkname" && "$(readlink -f "$linkname")" == "$(readlink -f "$target")" ]]; then + say "ok $linkname" + return + fi + if [[ -e "$linkname" || -L "$linkname" ]]; then + mv "$linkname" "$linkname.bak-$STAMP" + say "backup $linkname -> $linkname.bak-$STAMP" + fi + ln -sfn "$target" "$linkname" + say "link $linkname -> $target" +} + +# set_ini : ensure key=value under [Settings], +# creating the file/section/key as needed, replacing in place otherwise. +set_ini() { + local file="$1" key="$2" value="$3" + if [[ ! -f "$file" ]]; then + mkdir -p "$(dirname "$file")" + printf '[Settings]\n%s=%s\n' "$key" "$value" > "$file" + say "create $file ($key)" + return + fi + if grep -qE "^${key}=" "$file"; then + sed -i "s|^${key}=.*|${key}=${value}|" "$file" + else + sed -i "0,/^\[Settings\]/s//[Settings]\n${key}=${value}/" "$file" + fi + say "ini $file : $key=$value" +} + +xset() { # xfconf set, creating the prop with the right type if absent + local channel="$1" prop="$2" type="$3" val="$4" + command -v xfconf-query >/dev/null || return 0 + xfconf-query -c "$channel" -p "$prop" -n -t "$type" -s "$val" 2>/dev/null \ + || xfconf-query -c "$channel" -p "$prop" -s "$val" 2>/dev/null || true + say "xfconf $channel$prop = $val" +} + +echo "NexusOS theme — restoring wiring from $REPO" + +echo "[1/4] Symlinks" +link "$REPO/assets/themes/NexusOS" "$HOME/.themes/NexusOS" +link "$REPO/assets/themes/NexusOS-icons" "$HOME/.icons/NexusOS" +link "$REPO/assets/themes/gtk3-user-overrides.css" "$HOME/.config/gtk-3.0/gtk.css" + +echo "[2/4] xfconf (xsettings + xfwm4)" +xset xsettings /Net/ThemeName string "$GTK_THEME" +xset xsettings /Net/IconThemeName string "$ICON_THEME" +xset xsettings /Gtk/CursorThemeName string "$CURSOR_THEME" +xset xsettings /Gtk/CursorThemeSize int "$CURSOR_SIZE" +xset xsettings /Gtk/FontName string "$FONT_NAME" +xset xfwm4 /general/theme string "$GTK_THEME" + +echo "[3/4] GTK 3 / GTK 4 settings.ini" +for f in "$HOME/.config/gtk-3.0/settings.ini" "$HOME/.config/gtk-4.0/settings.ini"; do + set_ini "$f" gtk-theme-name "$GTK_THEME" + set_ini "$f" gtk-icon-theme-name "$ICON_THEME" + set_ini "$f" gtk-cursor-theme-name "$CURSOR_THEME" + set_ini "$f" gtk-cursor-theme-size "$CURSOR_SIZE" + set_ini "$f" gtk-font-name "$FONT_NAME" +done + +echo "[4/4] Icon caches" +gtk-update-icon-cache -f -t "$HOME/.icons/NexusOS" 2>/dev/null \ + && say "ok NexusOS icon cache rebuilt" \ + || say "WARN NexusOS icon cache rebuild failed" +INH="$(grep -i '^Inherits=' "$REPO/assets/themes/NexusOS-icons/index.theme" 2>/dev/null | cut -d= -f2)" +if [[ -n "$INH" ]]; then + INH_OK=0 + for dir in /usr/share/icons ~/.icons ~/.local/share/icons; do + [[ -d "$dir/$INH" ]] && { INH_OK=1; INH_DIR="$dir/$INH"; break; } + done + if [[ "$INH_OK" == 1 ]]; then + say "ok icon fallback '$INH' found" + if sudo -n gtk-update-icon-cache -f -t "$INH_DIR" 2>/dev/null; then + say "ok $INH icon cache rebuilt" + else + say "note $INH cache skipped (no passwordless sudo — run manually if needed)" + fi + else + say "WARN icon fallback '$INH' not installed — icons may be missing" + fi +fi + +echo "[5/5] Sanity" +for dir in /usr/share/icons ~/.icons ~/.local/share/icons; do + [[ -d "$dir/$CURSOR_THEME" ]] && { say "ok cursor '$CURSOR_THEME' found"; CUR_OK=1; break; } +done +[[ "${CUR_OK:-0}" == 1 ]] || say "WARN cursor theme '$CURSOR_THEME' not installed — will fall back" + +if [[ "$RELOAD" == 1 ]] && command -v xfconf-query >/dev/null && [[ -n "${DISPLAY:-}" ]]; then + xfconf-query -c xsettings -p /Net/ThemeName -s "Adwaita" 2>/dev/null || true + xfconf-query -c xsettings -p /Net/ThemeName -s "$GTK_THEME" 2>/dev/null || true + command -v xfdesktop >/dev/null && (xfdesktop --reload >/dev/null 2>&1 &) || true + command -v xfce4-panel >/dev/null && (xfce4-panel -r >/dev/null 2>&1 &) || true + echo "Reloaded. (some already-running apps may need a restart)" +else + echo "Done. Skipped live reload (no X session or --no-reload)." +fi diff --git a/assets/themes/restore-snapshot/gtk-3.0-settings.ini b/assets/themes/restore-snapshot/gtk-3.0-settings.ini new file mode 100644 index 0000000..2864a2f --- /dev/null +++ b/assets/themes/restore-snapshot/gtk-3.0-settings.ini @@ -0,0 +1,15 @@ +[Settings] +gtk-application-prefer-dark-theme=true +gtk-button-images=true +gtk-cursor-theme-name=DMZ-White +gtk-cursor-theme-size=24 +gtk-decoration-layout=close,minimize,maximize: +gtk-enable-animations=true +gtk-font-name=Ubuntu 10 +gtk-icon-theme-name=NexusOS +gtk-menu-images=true +gtk-modules=colorreload-gtk-module +gtk-primary-button-warps-slider=false +gtk-theme-name=NexusOS +gtk-toolbar-style=3 +gtk-xft-dpi=98304 diff --git a/assets/themes/restore-snapshot/gtk-4.0-settings.ini b/assets/themes/restore-snapshot/gtk-4.0-settings.ini new file mode 100644 index 0000000..90b2453 --- /dev/null +++ b/assets/themes/restore-snapshot/gtk-4.0-settings.ini @@ -0,0 +1,12 @@ +[Settings] +gtk-application-prefer-dark-theme=true +gtk-cursor-theme-name=DMZ-White +gtk-cursor-theme-size=24 +gtk-decoration-layout=close,minimize,maximize: +gtk-enable-animations=true +gtk-font-name=Ubuntu 10 +gtk-icon-theme-name=NexusOS +gtk-modules=colorreload-gtk-module +gtk-primary-button-warps-slider=false +gtk-theme-name=NexusOS +gtk-xft-dpi=98304 diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/code.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/code.dockitem new file mode 100644 index 0000000..133e5db --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/code.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///home/jon/.local/share/applications/code.desktop diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem new file mode 100644 index 0000000..7a81d48 --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///home/jon/.local/share/applications/microsoft-edge.desktop diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-activate.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-activate.dockitem new file mode 100644 index 0000000..9bb8205 --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-activate.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///home/jon/.local/share/applications/nexus-activate.desktop diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-core.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-core.dockitem new file mode 100644 index 0000000..19d99f0 --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-core.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///home/jon/.local/share/applications/nexus-core.desktop diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/promethean-terminal.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/promethean-terminal.dockitem new file mode 100644 index 0000000..42553e6 --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/promethean-terminal.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///home/jon/.local/share/applications/promethean-terminal.desktop diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/thunar.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/thunar.dockitem new file mode 100644 index 0000000..1bfd173 --- /dev/null +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/thunar.dockitem @@ -0,0 +1,2 @@ +[PlankDockItemPreferences] +Launcher=file:///usr/share/applications/thunar.desktop diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml new file mode 100644 index 0000000..2b402c5 --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-keyboard-shortcuts.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-keyboard-shortcuts.xml new file mode 100644 index 0000000..7fee74a --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-keyboard-shortcuts.xml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml new file mode 100644 index 0000000..47d155c --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-terminal.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-terminal.xml new file mode 100644 index 0000000..318d0bc --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-terminal.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfwm4.xml b/assets/themes/restore-snapshot/xfconf-xml/xfwm4.xml new file mode 100644 index 0000000..11e1ae3 --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xfwm4.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml b/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml new file mode 100644 index 0000000..8bd4729 --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/themes/restore-snapshot/xfconf.txt b/assets/themes/restore-snapshot/xfconf.txt new file mode 100644 index 0000000..9e656da --- /dev/null +++ b/assets/themes/restore-snapshot/xfconf.txt @@ -0,0 +1,8 @@ +# NexusOS live desktop wiring — reference snapshot +# Reference only. Restore is done by assets/themes/install-theme.sh +xsettings /Net/ThemeName = NexusOS +xsettings /Net/IconThemeName = NexusOS +xsettings /Gtk/CursorThemeName = DMZ-White +xsettings /Gtk/CursorThemeSize = 24 +xsettings /Gtk/FontName = Ubuntu 10 +xfwm4 /general/theme = NexusOS diff --git a/bin/backup-linux.sh b/bin/backup-linux.sh new file mode 100644 index 0000000..efab4de --- /dev/null +++ b/bin/backup-linux.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# The Linux-only half of a backup: snapshot the LIVE desktop wiring into the repo +# (reference copy for recovery/diffing; assets/themes/install-theme.sh remains the +# applier on restore) and copy the Claude memory notes in so git commits their +# content. None of it exists on Windows, which is why it isn't in bin/sync.py. +# +# Not meant to be run by hand — it's the `--full` stage of: +# python bin/sync.py backup --full (or `ncp backup -f`) + +# Set by bin/sync.py to the repo it was invoked from, so a clone in a scratch dir +# operates on itself instead of reaching into the real ~/nexus-core. +NEXUS_ROOT="${NEXUS_ROOT:-$HOME/nexus-core}" +cd "$NEXUS_ROOT" || { echo "No $NEXUS_ROOT"; exit 1; } + +snap="$NEXUS_ROOT/assets/themes/restore-snapshot" +mkdir -p "$snap" +{ + # No timestamp here — git records commit time, and a date line makes every + # `backup full` dirty this file and commit even when nothing changed. + echo "# NexusOS live desktop wiring — reference snapshot" + echo "# Reference only. Restore is done by assets/themes/install-theme.sh" + for p in /Net/ThemeName /Net/IconThemeName /Gtk/CursorThemeName \ + /Gtk/CursorThemeSize /Gtk/FontName; do + echo "xsettings $p = $(xfconf-query -c xsettings -p "$p" 2>/dev/null)" + done + echo "xfwm4 /general/theme = $(xfconf-query -c xfwm4 -p /general/theme 2>/dev/null)" +} > "$snap/xfconf.txt" 2>/dev/null || true +cp -f "$HOME/.config/gtk-3.0/settings.ini" "$snap/gtk-3.0-settings.ini" 2>/dev/null || true +cp -f "$HOME/.config/gtk-4.0/settings.ini" "$snap/gtk-4.0-settings.ini" 2>/dev/null || true +cp -f "$HOME/.config/xfce4/panel/genmon-13.rc" \ + "$NEXUS_ROOT/management/panel/genmon-13.rc" 2>/dev/null || true + +# The xfconf channel XMLs ARE the desktop: panel layout/size/colour, the +# wallpaper, compositing + keybindings, terminal profile. Copying the files +# is the whole restore — no per-property xfconf-query scripting needed. +# displays.xml is deliberately excluded (monitor-specific; would break another box). +xml_src="$HOME/.config/xfce4/xfconf/xfce-perchannel-xml" +mkdir -p "$snap/xfconf-xml" +for c in xfce4-panel xfce4-desktop xfwm4 xsettings xfce4-keyboard-shortcuts xfce4-terminal; do + cp -f "$xml_src/$c.xml" "$snap/xfconf-xml/$c.xml" 2>/dev/null || true +done + +# Multi-monitor primary-follow watcher lives in ~/.local/bin, not the repo. +cp -f "$HOME/.local/bin/plank-primary-watch.sh" \ + "$NEXUS_ROOT/bin/panel/plank-primary-watch.sh" 2>/dev/null || true +# Trailing /. copies the CONTENTS — plain `cp -r src dst` nests into dst/src +# once dst exists, burying the dock one level deeper on every backup. +rm -rf "$snap/plank" +if [ -d "$HOME/.config/plank" ]; then + mkdir -p "$snap/plank" + cp -rf "$HOME/.config/plank/." "$snap/plank/" 2>/dev/null || true +fi + +# Copy the Claude memory notes (machine knowledge under ~/.claude) into the +# repo so they're versioned too. Some aren't Nexus-specific (Fusion 360, etc.). +notes_src="$HOME/.claude/projects/-home-jon-nexus-core/memory" +notes_dst="$NEXUS_ROOT/assets/notes" +mkdir -p "$notes_dst" +cp -f "$notes_src"/*.md "$notes_dst/" 2>/dev/null || true + +echo "Snapshotted desktop wiring + Claude notes." diff --git a/bin/check.sh b/bin/check.sh new file mode 100644 index 0000000..dc4ea91 --- /dev/null +++ b/bin/check.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# One command that answers "is this shippable" - Python tests + frontend lint. +# +# ponytail: this IS the CI. The remote is self-hosted Gitea with no act_runner, +# so a .github/workflows file would never execute. Run this before tagging a +# release; wire it to a runner the day one exists. +set -uo pipefail +cd "$(dirname "$0")/.." + +fail=0 + +if [ ! -x Promethean/bin/python ]; then + echo "!! no Promethean venv - run bin/install.sh first" >&2 + exit 1 +fi + +echo "== pytest ==" +# Explicit dirs: a bare `pytest` would walk Promethean/ and node_modules too. +Promethean/bin/python -m pytest -q tests management || fail=1 + +echo "== eslint ==" +if [ -d interface/web/node_modules ]; then + (cd interface/web && npm run lint) || fail=1 +else + echo "-- skipped: interface/web/node_modules missing (npm install)" +fi + +echo "== powershell parse ==" +# The Windows installer has died at parse twice. Cheap to catch here if pwsh +# happens to be installed on the Linux box; the ASCII guard in tests/ is the +# portable half of the same check. +if command -v pwsh >/dev/null; then + for f in install-windows.ps1 launch_nexus.ps1; do + pwsh -NoProfile -Command "\$e=\$null + [System.Management.Automation.Language.Parser]::ParseFile('$f',[ref]\$null,[ref]\$e) | Out-Null + if (\$e) { \$e | ForEach-Object { Write-Host '$f:' \$_.Message }; exit 1 }" || fail=1 + done +else + echo "-- skipped: pwsh not installed" +fi + +[ "$fail" -eq 0 ] && echo "OK" || echo "FAILED" +exit "$fail" diff --git a/bin/fetch-ollama.sh b/bin/fetch-ollama.sh new file mode 100644 index 0000000..a82b64d --- /dev/null +++ b/bin/fetch-ollama.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# Ensure a runnable Ollama binary exists at /ollama/bin/ollama. +# +# ollama/ is gitignored, so a `git clone` checkout has no binary — download the +# official Linux x86-64 build (pinned to the version the native box ships) so +# clone installs are self-sufficient. A folder-copy install already has the +# binary and this is a no-op. Single source of the pinned version for both +# bin/restore-linux.sh (the desktop stage of a restore). +# +# fetch-ollama.sh +# NEXUS_OLLAMA_VERSION=vX.Y.Z fetch-ollama.sh # override pin + +set -euo pipefail + +root="${1:?usage: fetch-ollama.sh }" +bin="$root/ollama/bin/ollama" +version="${NEXUS_OLLAMA_VERSION:-v0.21.1}" + +# Already present and runnable → nothing to do. +if [ -x "$bin" ] && "$bin" --version &>/dev/null; then + exit 0 +fi + +case "$(uname -m)" in + x86_64|amd64) ;; + *) + echo "ERROR: Ollama download supports x86-64 only (detected $(uname -m))." >&2 + exit 1 + ;; +esac + +# ponytail: stock amd64 asset ships CPU + CUDA runners, not ROCm. An AMD/ROCm +# box wanting GPU offload needs the `-rocm` asset; the CPU runner works meanwhile +# (the native box runs num_gpu=0 anyway). Swap the asset name if that changes. +echo "Ollama binary missing — downloading $version..." +url="https://github.com/ollama/ollama/releases/download/${version}/ollama-linux-amd64.tar.zst" +tmp="$(mktemp)" +trap 'rm -f "$tmp"' EXIT + +curl -fSL --retry 3 -o "$tmp" "$url" \ + || { echo "ERROR: could not download Ollama from $url" >&2; exit 1; } +mkdir -p "$root/ollama" +tar --zstd -xf "$tmp" -C "$root/ollama" \ + || { echo "ERROR: could not extract Ollama tarball (need zstd + tar --zstd)." >&2; exit 1; } + +chmod +x "$bin" 2>/dev/null || true +"$bin" --version &>/dev/null \ + || { echo "ERROR: downloaded Ollama could not run (expected x86-64 at $bin)." >&2; exit 1; } +echo "Ollama $version ready." diff --git a/bin/gen-nvidia-reqs.py b/bin/gen-nvidia-reqs.py new file mode 100644 index 0000000..56d1f6d --- /dev/null +++ b/bin/gen-nvidia-reqs.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import re, subprocess, sys +from pathlib import Path + +NEXUS_ROOT = Path.home() / "nexus-core" +NVIDIA_REQS = NEXUS_ROOT / "requirements-nvidia.txt" + +CUDA_TO_WHEEL = [ + ((12, 8), "cu128"), + ((12, 6), "cu126"), + ((12, 4), "cu124"), + ((12, 1), "cu121"), + ((11, 8), "cu118"), +] + +def detect_cuda(): + try: + out = subprocess.run(["nvidia-smi"], capture_output=True, text=True, timeout=10).stdout + m = re.search(r"CUDA Version:\s*(\d+)\.(\d+)", out) + if m: + return int(m.group(1)), int(m.group(2)) + except (FileNotFoundError, subprocess.TimeoutExpired): + pass + return None, None + +def wheel_suffix(major, minor): + for (req_major, req_minor), suffix in CUDA_TO_WHEEL: + if (major, minor) >= (req_major, req_minor): + return suffix + return "cu118" + +def main(): + print("Detecting NVIDIA GPU...") + major, minor = detect_cuda() + + if major is None: + print("Error: nvidia-smi not found or CUDA version unreadable.") + print("Ensure NVIDIA drivers are installed and nvidia-smi is on your PATH.") + sys.exit(1) + + print(f"CUDA {major}.{minor} detected.") + suffix = wheel_suffix(major, minor) + print(f"PyTorch wheel: {suffix}") + + NVIDIA_REQS.write_text(f"""\ +# --- Force NVIDIA/CUDA Priority --- +--index-url https://download.pytorch.org/whl/{suffix} +--extra-index-url https://pypi.org/simple + +-r requirements-base.txt + +# GPU Compute Stack +torch +torchaudio +torchvision +""") + + print(f"\nWritten: {NVIDIA_REQS}") + print("Run 'ncp backup' to push it to the router.") + +if __name__ == "__main__": + main() diff --git a/bin/install.sh b/bin/install.sh new file mode 100644 index 0000000..4473e8b --- /dev/null +++ b/bin/install.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# NexusOS installer — Linux side. +# Syncs the repo, builds the Python venv, installs frontend deps, registers ncp, +# and installs the Promethean Terminal + panel. +# +# ./install.sh Linux install (default) +# ./install.sh -w | --windows Hand off to the Windows installer (install-windows.ps1) + +NEXUS_ROOT="$HOME/nexus-core" +ROUTER_BACKUP="router:/tmp/mnt/Wingdrive2/nexus-core/" + +# ─── Helpers ────────────────────────────────────────────────────────────────── + +usage() { + cat </dev/null && nvidia-smi &>/dev/null 2>&1; then + echo "requirements-nvidia.txt" + elif lspci 2>/dev/null | grep -qi nvidia; then + echo "requirements-nvidia.txt" + elif grep -qi microsoft /proc/version 2>/dev/null; then + echo "requirements-wsl.txt" + elif lspci 2>/dev/null | grep -qi amd; then + echo "requirements-amd.txt" + else + echo "requirements-wsl.txt" + fi +} + +run_windows() { + local ps1="$NEXUS_ROOT/install-windows.ps1" + if [ ! -f "$ps1" ]; then + echo "Error: $ps1 not found." >&2 + exit 1 + fi + + # install-windows.ps1 is a native-Windows installer: it self-elevates to + # Administrator and uses winget (Python/Node/Ollama) directly. No WSL. + # It must run from Windows PowerShell, so just point the way. + cat <&2 + usage + exit 1 + ;; +esac + +# ─── Step 1: Sync from router ───────────────────────────────────────────────── + +echo "Pulling Nexus from router..." +mkdir -p "$NEXUS_ROOT" +rsync -avz --delete \ + --exclude='.git/' \ + --exclude='Promethean/' \ + --exclude='models/blobs/' \ + --exclude='ollama/' \ + --exclude='interface/web/node_modules/' \ + --exclude='interface/web/dist/' \ + --exclude='runtime/' \ + --exclude='__pycache__/' \ + --exclude='*.pyc' \ + -e ssh \ + "$ROUTER_BACKUP" "$NEXUS_ROOT/" + +# ─── Step 2: Python venv ────────────────────────────────────────────────────── + +echo "" +echo "Creating Python environment..." +python3 -m venv "$NEXUS_ROOT/Promethean" + +# ─── Step 3: pip install ────────────────────────────────────────────────────── + +echo "" +echo "Installing Python dependencies..." +req=$(detect_requirements) +echo "Detected: $req" +if [ -f "$NEXUS_ROOT/$req" ]; then + "$NEXUS_ROOT/Promethean/bin/pip" install --upgrade pip -q + "$NEXUS_ROOT/Promethean/bin/pip" install -r "$NEXUS_ROOT/$req" +else + echo "Warning: $req not found — skipping pip install." +fi + +# ─── Step 4: npm install ────────────────────────────────────────────────────── + +echo "" +echo "Installing frontend dependencies..." +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +if command -v npm &>/dev/null; then + cd "$NEXUS_ROOT/interface/web" && npm install +else + echo "npm not found — install nvm/node then run 'cd $NEXUS_ROOT/interface/web && npm install'." +fi + +# ─── Step 5: Register ncp in ~/.bashrc ─────────────────────────────────────── + +echo "" +echo "Registering ncp..." +if ! grep -q "nexus-core/management/nexus-cli.sh" "$HOME/.bashrc"; then + cat >> "$HOME/.bashrc" << 'EOF' + +# Nexus +ncp() { + ~/nexus-core/management/nexus-cli.sh "$@" +} +EOF + echo "ncp registered in ~/.bashrc." +else + echo "ncp already in ~/.bashrc — skipping." +fi + +if ! grep -qF "alias promethean='source ~/nexus-core/.promethean_bashrc'" "$HOME/.bashrc"; then + printf '\n# Promethean\nalias promethean='"'"'source ~/nexus-core/.promethean_bashrc'"'"'\n' >> "$HOME/.bashrc" + echo "promethean registered in ~/.bashrc." +else + echo "promethean already in ~/.bashrc — skipping." +fi + +# ─── Step 6: Promethean Terminal + panel ───────────────────────────────────── + +echo "" +echo "Installing Promethean Terminal..." +bash "$NEXUS_ROOT/bin/promethean/install.sh" || \ + echo "Warning: Promethean Terminal install failed — run bin/promethean/install.sh manually." + +echo "" +echo "Installing NexusOS panel applet..." +bash "$NEXUS_ROOT/bin/panel/install.sh" || \ + echo "Warning: panel install failed — run bin/panel/install.sh manually." + +# ─── Done ───────────────────────────────────────────────────────────────────── + +echo "" +echo "Installation complete. Nexus is ready." +echo "Run 'ncp start' to launch Nexus." diff --git a/bin/nexus_window.py b/bin/nexus_window.py new file mode 100644 index 0000000..ff6dcb0 --- /dev/null +++ b/bin/nexus_window.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Open the NexusOS UI in a native window. + +Uses pywebview, which renders via the WebView2 runtime on Windows (already +present on Win10/11) -- a real app window with no browser chrome and none of the +Edge --app profile cold-start. Blocks until the window is closed; the launcher +waits on this process and stops the services when it exits. + +Falls back to the default browser if pywebview/WebView2 is unavailable, staying +alive so the launcher doesn't tear the services down underneath it. +""" +import sys +import time +import urllib.request + +URL = "http://localhost:8000" + + +def _wait_for_backend(timeout: float = 40.0) -> bool: + """Poll /status until the backend answers, so the window never loads before + the server is up (which shows a localhost error the webview won't retry).""" + status_url = URL.rstrip("/") + "/status" + deadline = time.time() + timeout + while time.time() < deadline: + try: + with urllib.request.urlopen(status_url, timeout=2) as r: + if r.status == 200: + return True + except Exception: + time.sleep(1) + return False + + +def main() -> int: + try: + import webview + except Exception as e: # pywebview not installed + return _browser_fallback(f"pywebview unavailable ({e})") + + if not _wait_for_backend(): + print("[nexus] backend not reachable on :8000 after 40s.", file=sys.stderr) + + try: + webview.create_window( + "NexusOS", + URL, + width=1200, + height=800, + min_size=(900, 600), + ) + webview.start() # blocks until the window is closed + return 0 + except Exception as e: # no WebView2 runtime / backend failure + return _browser_fallback(f"native window failed ({e})") + + +def _browser_fallback(reason: str) -> int: + import webbrowser + + print(f"[nexus] {reason}; opening default browser instead.", file=sys.stderr) + webbrowser.open(URL) + # Stay alive so the launcher keeps the services up. The user stops NexusOS + # by closing the launcher (or the browser tab, then the services idle out). + try: + while True: + time.sleep(3600) + except KeyboardInterrupt: + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/bin/panel/bluetooth-applet.sh b/bin/panel/bluetooth-applet.sh new file mode 100644 index 0000000..832b7ae --- /dev/null +++ b/bin/panel/bluetooth-applet.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Genmon bluetooth applet — PNG icon + status, click opens blueman-manager. +# Replaces blueman's StatusNotifier tray icon (which the panel renders too small); +# blueman-applet still runs headless for the agent — see bin/panel/install.sh. + +NEXUS_ROOT="$(cd "$(dirname "$(realpath "$0")")/../.." && pwd)" +ICON_DIR="$NEXUS_ROOT/assets/panel-icons" + +powered=$(bluetoothctl show 2>/dev/null | awk -F': ' '/Powered:/{print $2; exit}') +connected=$(bluetoothctl devices Connected 2>/dev/null | grep -c '^Device') + +if [ "$powered" != "yes" ]; then + echo "${ICON_DIR}/bluetooth-disabled.png" + echo "Bluetooth: off" +elif [ "$connected" -gt 0 ]; then + names=$(bluetoothctl devices Connected 2>/dev/null | sed 's/^Device [0-9A-F:]* //') + echo "${ICON_DIR}/bluetooth-active.png" + echo "Bluetooth: connected +${names}" +else + echo "${ICON_DIR}/bluetooth-online.png" + echo "Bluetooth: on (no devices connected)" +fi +echo "blueman-manager" diff --git a/bin/panel/install.sh b/bin/panel/install.sh new file mode 100644 index 0000000..0eaabfc --- /dev/null +++ b/bin/panel/install.sh @@ -0,0 +1,87 @@ +#!/bin/bash +# Install NexusOS panel applets — symlink scripts, restore genmon config, wire autostart. + +set -e +NEXUS="$HOME/nexus-core" + +BIN="$HOME/.local/bin" +AUTOSTART="$HOME/.config/autostart" +PANEL_CFG="$HOME/.config/xfce4/panel" + +mkdir -p "$BIN" "$AUTOSTART" "$PANEL_CFG" + +# Scripts +for script in network-applet.sh network-popup.py nexus_menu_base.py \ + nexus-applet.sh nexus-popup.py bluetooth-applet.sh; do + ln -sf "$NEXUS/bin/panel/$script" "$BIN/$script" +done +chmod +x "$NEXUS/bin/panel/network-popup.py" "$NEXUS/bin/panel/network-applet.sh" \ + "$NEXUS/bin/panel/nexus-popup.py" "$NEXUS/bin/panel/nexus-applet.sh" \ + "$NEXUS/bin/panel/bluetooth-applet.sh" + +# Autostart — popup launchers (symlinks) + nm-tray suppressors (regular files) +ln -sf "$NEXUS/management/autostart/nexus-network-popup.desktop" \ + "$AUTOSTART/nexus-network-popup.desktop" +ln -sf "$NEXUS/management/autostart/nexus-popup.desktop" \ + "$AUTOSTART/nexus-popup.desktop" +cp -f "$NEXUS/management/autostart/nm-tray.desktop" "$AUTOSTART/nm-tray.desktop" +cp -f "$NEXUS/management/autostart/nm-tray-autostart.desktop" "$AUTOSTART/nm-tray-autostart.desktop" +# Suppress blueman-applet autostart (both the system blueman.desktop and the +# user blueman-applet.desktop). blueman always forces its own tray icon, which +# the panel renders too small; the Bluetooth genmon (plugin-16) replaces it and +# opens blueman-manager on click (which provides the pairing agent on demand). +cp -f "$NEXUS/management/autostart/blueman.desktop" "$AUTOSTART/blueman.desktop" +cp -f "$NEXUS/management/autostart/blueman-applet.desktop" "$AUTOSTART/blueman-applet.desktop" + +# Genmon configs (regular files — panel writes back to them) +cp -f "$NEXUS/management/panel/genmon-13.rc" "$PANEL_CFG/genmon-13.rc" +cp -f "$NEXUS/management/panel/genmon-15.rc" "$PANEL_CFG/genmon-15.rc" +cp -f "$NEXUS/management/panel/genmon-16.rc" "$PANEL_CFG/genmon-16.rc" + +# ── Register the genmon applets into the XFCE panel ──────────────────────── +# The network applet's genmon (plugin-13) was added by hand once; the Nexus +# (15) and Bluetooth (16) applets are wired up programmatically via xfconf so a +# fresh install picks them up. No-op on machines without XFCE (e.g. the WSL box). +# place = before|after — where to insert relative to the network applet (13). +register_genmon() { + local id=$1 place=$2 anchor=13 panel=panel-1 + command -v xfconf-query >/dev/null 2>&1 || { echo "xfconf-query not found — skipping panel wiring."; return 0; } + + # Declare the plugin's type so the panel knows it's a Generic Monitor. + xfconf-query -c xfce4-panel -p "/plugins/plugin-$id" -t string -s genmon --create + + mapfile -t ids < <(xfconf-query -c xfce4-panel -p "/panels/$panel/plugin-ids" 2>/dev/null | grep -E '^[0-9]+$') + if [ ${#ids[@]} -eq 0 ]; then + echo "Panel '$panel' has no plugin-ids array — add plugin-$id manually."; return 0 + fi + for e in "${ids[@]}"; do [ "$e" = "$id" ] && { echo "genmon plugin-$id already in panel."; return 0; }; done + + local new=() ins=0 + for e in "${ids[@]}"; do + [ "$e" = "$anchor" ] && [ "$place" = before ] && [ "$ins" = 0 ] && { new+=("$id"); ins=1; } + new+=("$e") + [ "$e" = "$anchor" ] && [ "$place" = after ] && [ "$ins" = 0 ] && { new+=("$id"); ins=1; } + done + [ "$ins" = 0 ] && new+=("$id") # anchor absent — append + + local args=(); for v in "${new[@]}"; do args+=(-t int -s "$v"); done + xfconf-query -c xfce4-panel -p "/panels/$panel/plugin-ids" --force-array "${args[@]}" + echo "genmon plugin-$id registered." +} + +register_genmon 15 before # Nexus applet, left of the network applet +register_genmon 16 after # Bluetooth applet, right of the network applet +echo "Reloading panel…" +xfce4-panel -r >/dev/null 2>&1 || true + +# Start the popup daemon now so the first click works without a re-login. +# Pin to the system python3 explicitly: PyGObject (gi) is a system package, and +# install.sh is often run from an activated Promethean venv whose python3 lacks +# gi. At login/panel-click time the shebang resolves against the clean system +# PATH (like the network popup), so only this pre-launch needs the hard path. +if command -v xfce4-panel >/dev/null 2>&1; then + pkill -f "bin/.*nexus-popup.py" 2>/dev/null || true + setsid /usr/bin/python3 "$BIN/nexus-popup.py" >/dev/null 2>&1 < /dev/null & +fi + +echo "Panel applets installed." diff --git a/bin/panel/network-applet.sh b/bin/panel/network-applet.sh new file mode 100644 index 0000000..d5c3c51 --- /dev/null +++ b/bin/panel/network-applet.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Genmon network applet — PNG icon + SSID label + hover details +# Referred to as nm-applet + +NEXUS_ROOT="$(cd "$(dirname "$(realpath "$0")")/../.." && pwd)" +ICON_DIR="$NEXUS_ROOT/assets/panel-icons" + +signal_icon() { + local sig=$1 + if [ "$sig" -ge 80 ]; then echo "${ICON_DIR}/network-wireless-signal-excellent.png" + elif [ "$sig" -ge 60 ]; then echo "${ICON_DIR}/network-wireless-signal-good.png" + elif [ "$sig" -ge 40 ]; then echo "${ICON_DIR}/network-wireless-signal-ok.png" + elif [ "$sig" -ge 20 ]; then echo "${ICON_DIR}/network-wireless-signal-weak.png" + else echo "${ICON_DIR}/network-wireless-signal-none.png" + fi +} + +# Determine the active uplink from the default route rather than NetworkManager's +# connected state: the wired interface (t2_ncm) is unmanaged by NM and never +# reports STATE=connected, so it would otherwise fall through to "offline". +PRIMARY_IF=$(ip route show default 2>/dev/null | awk '/^default/{print $5; exit}') +if [ -n "$PRIMARY_IF" ] && [ -d "/sys/class/net/$PRIMARY_IF/wireless" ]; then + ACTIVE_TYPE=wifi +elif [ -n "$PRIMARY_IF" ]; then + ACTIVE_TYPE=ethernet +else + ACTIVE_TYPE= +fi +POPUP_OPEN=false +[ -f /tmp/nexus-network-popup-visible ] && POPUP_OPEN=true + +if [[ "$ACTIVE_TYPE" == "wifi" ]]; then + IFS=: read -r _ SSID SIGNAL < <(nmcli -t --escape no -f ACTIVE,SSID,SIGNAL dev wifi list --rescan no | grep '^yes') + IP=$(nmcli -t --escape no -f IP4.ADDRESS dev show | grep -m1 'IP4.ADDRESS' | cut -d: -f2 | cut -d/ -f1) + + echo "$(signal_icon "${SIGNAL:-0}")" + if $POPUP_OPEN; then + echo "" + else + echo "Wireless +SSID: ${SSID} +Signal: ${SIGNAL}% +IP: ${IP:-unknown}" + fi + echo "$HOME/.local/bin/network-popup.py" + +elif [[ "$ACTIVE_TYPE" == "ethernet" ]]; then + # t2_ncm is unmanaged, so read the IP straight off the primary interface. + IP=$(ip -4 -o addr show "$PRIMARY_IF" 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n1) + + echo "${ICON_DIR}/network-wired.png" + if $POPUP_OPEN; then + echo "" + else + echo "Wired (Ethernet) +Interface: ${PRIMARY_IF:-unknown} +IP: ${IP:-unknown}" + fi + echo "$HOME/.local/bin/network-popup.py" + +else + echo "${ICON_DIR}/network-offline.png" + if $POPUP_OPEN; then + echo "" + else + echo "No active network connection" + fi + echo "$HOME/.local/bin/network-popup.py" +fi diff --git a/bin/panel/network-popup.py b/bin/panel/network-popup.py new file mode 100644 index 0000000..256d661 --- /dev/null +++ b/bin/panel/network-popup.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python3 +"""NexusOS network popup daemon — toggle via SIGUSR1, instant open.""" +# PyGObject (gi.repository) is dynamically generated and its API is Optional-heavy +# (e.g. Gdk.Display.get_default() is typed Display|None). These are fine at runtime, +# so silence the type-checker noise for this GTK desktop script. +# pyright: reportMissingModuleSource=false, reportOptionalMemberAccess=false, reportArgumentType=false, reportCallIssue=false, reportAttributeAccessIssue=false + +import os, sys, signal, threading, subprocess +from pathlib import Path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +gi.require_version('GdkPixbuf', '2.0') +from gi.repository import Gtk, Gdk, GLib, GdkPixbuf +from nexus_menu_base import apply_css, get_panel_bottom, get_mouse_position + +PID_FILE = "/tmp/nexus-network-popup.pid" +VISIBLE_FILE = "/tmp/nexus-network-popup-visible" + +POPUP_CSS = b""" +list { background-color: transparent; } +list row { background-color: transparent; padding: 0; border: none; } +list row:hover { background-color: rgba(140,198,63,0.12); } +list row:selected { background-color: rgba(140,198,63,0.18); } +.net-header { padding: 10px 12px; } +.net-ssid { font-weight: bold; } +.net-ip { color: #a8a8a8; font-size: 0.85em; } +.net-check { color: #8cc63f; font-weight: bold; } +.net-lock { color: #a8a8a8; } +.net-row-box { padding: 7px 8px; } +.footer-btn { padding: 6px 12px; border-radius: 0; border: none; + background-color: transparent; color: #a8a8a8; } +.footer-btn:hover { background-color: rgba(140,198,63,0.12); color: #f2f2f2; } +.vpn-row { padding: 8px 12px; } +.vpn-label { font-size: 0.9em; } +.vpn-status { color: #a8a8a8; font-size: 0.8em; } +switch { background-color: #3a3d41; border-radius: 14px; border: 1px solid #4d3461; + min-width: 42px; min-height: 22px; } +switch:checked { background-color: #8cc63f; border-color: #6ba62a; } +switch slider { background-color: #f2f2f2; border-radius: 50%; + min-width: 16px; min-height: 16px; margin: 2px; } +""" + + +def nmcli(*args): + try: + return subprocess.check_output( + ["nmcli", "-t", "--escape", "no"] + list(args), + text=True, stderr=subprocess.DEVNULL + ).strip() + except Exception: + return "" + + +# This file lives at /bin/panel/network-popup.py, so the repo root is +# three parents up (panel -> bin -> repo). Using only two parents pointed +# ICON_DIR at the nonexistent bin/assets/panel-icons, so every signal-icon +# load failed and each SSID row fell back to a "?" label. +ICON_DIR = str(Path(__file__).resolve().parent.parent.parent / "assets" / "panel-icons") + +def signal_icon(sig): + if sig >= 80: return f"{ICON_DIR}/network-wireless-signal-excellent.png" + if sig >= 60: return f"{ICON_DIR}/network-wireless-signal-good.png" + if sig >= 40: return f"{ICON_DIR}/network-wireless-signal-ok.png" + if sig >= 20: return f"{ICON_DIR}/network-wireless-signal-weak.png" + return f"{ICON_DIR}/network-wireless-signal-none.png" + + +class NetworkPopup: + def __init__(self): + self.visible = False + self._click_x = None + self.vpn_switch = None + self.vpn_status_lbl = None + self._build_window() + + def _on_sig(s, f): + # Capture mouse position NOW (signal handler runs in main thread) + try: + self._click_x = get_mouse_position()[0] + except Exception: + self._click_x = None + GLib.idle_add(self.toggle) + + signal.signal(signal.SIGUSR1, _on_sig) + with open(PID_FILE, 'w') as f: + f.write(str(os.getpid())) + + def _build_window(self): + self.win = Gtk.Window(type=Gtk.WindowType.POPUP) + self.win.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU) + self.win.set_decorated(False) + self.win.set_skip_taskbar_hint(True) + self.win.set_keep_above(True) + self.win.set_default_size(190, -1) + + self.win.connect('key-press-event', + lambda w, e: self.hide() if e.keyval == Gdk.KEY_Escape else None) + + def on_button_press(w, event): + wx, wy = w.get_position() + ww, wh = w.get_allocated_width(), w.get_allocated_height() + if (int(event.x_root) < wx or int(event.x_root) >= wx + ww or + int(event.y_root) < wy or int(event.y_root) >= wy + wh): + self.hide() + return False + self.win.connect('button-press-event', on_button_press) + + def on_map(w, _): + gdk_win = w.get_window() + if gdk_win: + Gdk.Display.get_default().get_default_seat().grab( + gdk_win, Gdk.SeatCapabilities.ALL, True, None, None, None) + return False + self.win.connect('map-event', on_map) + + def on_unmap(w, _): + Gdk.Display.get_default().get_default_seat().ungrab() + self.win.connect('unmap-event', on_unmap) + + def toggle(self): + if self.visible: + self.hide() + else: + self.show() + + def show(self): + for child in self.win.get_children(): + self.win.remove(child) + self.win.add(self._build_content()) + self.win.show_all() + self._position() + self.win.present() + self.visible = True + open(VISIBLE_FILE, 'w').close() + + def hide(self): + self.win.hide() + self.visible = False + try: + os.remove(VISIBLE_FILE) + except FileNotFoundError: + pass + + def _position(self): + mx = self._click_x if self._click_x is not None else get_mouse_position()[0] + screen = Gdk.Screen.get_default() + sw = screen.get_width() + w = 190 + panel_bottom = get_panel_bottom() + x = max(4, min(mx - w // 2, sw - w - 4)) + self.win.move(x, panel_bottom + 2) + + def _build_content(self): + outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + # ── Header (current connection) ────────────────────────────── + self.header = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + self.header.get_style_context().add_class("net-header") + spin = Gtk.Spinner(); spin.start() + self.header.pack_start(spin, False, False, 0) + self.header.pack_start(Gtk.Label(label="Loading…"), True, True, 0) + outer.pack_start(self.header, False, False, 0) + + outer.pack_start(Gtk.Separator(), False, False, 0) + + # ── Network list ───────────────────────────────────────────── + self.listbox = Gtk.ListBox() + self.listbox.set_selection_mode(Gtk.SelectionMode.NONE) + self.listbox.connect('row-activated', self._on_row_activated) + + sw = Gtk.ScrolledWindow() + sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + sw.set_min_content_height(280) + sw.set_max_content_height(480) + sw.set_propagate_natural_height(True) + sw.add(self.listbox) + outer.pack_start(sw, True, True, 0) + + outer.pack_start(Gtk.Separator(), False, False, 0) + + # ── VPN (WireGuard) toggle ──────────────────────────────────── + vpn_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + vpn_row.get_style_context().add_class("vpn-row") + + vpn_icon = Gtk.Label(label="🔒") + vpn_row.pack_start(vpn_icon, False, False, 0) + + vpn_text = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + vpn_name = Gtk.Label(label="WireGuard", xalign=0.0) + vpn_name.get_style_context().add_class("vpn-label") + self.vpn_status_lbl = Gtk.Label(label="Checking…", xalign=0.0) + self.vpn_status_lbl.get_style_context().add_class("vpn-status") + vpn_text.pack_start(vpn_name, False, False, 0) + vpn_text.pack_start(self.vpn_status_lbl, False, False, 0) + vpn_row.pack_start(vpn_text, True, True, 0) + + self.vpn_switch = Gtk.Switch() + self.vpn_switch.set_valign(Gtk.Align.CENTER) + self._vpn_handler = self.vpn_switch.connect('state-set', self._on_vpn_toggle) + vpn_row.pack_start(self.vpn_switch, False, False, 0) + + # Set switch state immediately from sysfs — no nmcli round-trip needed + vpn_up = os.path.exists('/sys/class/net/wgs_client') + self.vpn_switch.handler_block(self._vpn_handler) + self.vpn_switch.set_active(vpn_up) + self.vpn_switch.handler_unblock(self._vpn_handler) + self.vpn_status_lbl.set_text("Connected" if vpn_up else "Disconnected") + + outer.pack_start(vpn_row, False, False, 0) + outer.pack_start(Gtk.Separator(), False, False, 0) + + # ── Footer ─────────────────────────────────────────────────── + btn = Gtk.Button(label="Network Settings") + btn.get_style_context().add_class("footer-btn") + btn.set_relief(Gtk.ReliefStyle.NONE) + btn.connect('clicked', lambda _: (self.hide(), + subprocess.Popen(['nm-connection-editor']))) + outer.pack_start(btn, False, False, 0) + + threading.Thread(target=self._fetch, daemon=True).start() + return outer + + def _fetch(self): + conn_type, device, current_ssid, ip = "", "", "", "" + + devs = nmcli("-f", "TYPE,STATE,DEVICE", "dev") + for line in devs.splitlines(): + parts = line.split(":") + if len(parts) >= 2 and parts[1] == "connected": + conn_type = parts[0] + device = parts[2] if len(parts) > 2 else "" + break + + if conn_type == "wifi": + raw = nmcli("-f", "ACTIVE,SSID,SIGNAL", "dev", "wifi") + for line in raw.splitlines(): + parts = line.split(":") + if parts[0] == "yes" and len(parts) >= 2: + current_ssid = parts[1] + break + ip = (nmcli("-f", "IP4.ADDRESS", "dev", "show") + .split("\n")[0].split(":")[-1].split("/")[0].strip()) + elif conn_type == "ethernet": + ip = (nmcli("-f", "IP4.ADDRESS", "dev", "show") + .split("\n")[0].split(":")[-1].split("/")[0].strip()) + + raw = nmcli("-f", "SSID,SIGNAL,SECURITY,IN-USE", "dev", "wifi", "list", "--rescan", "no") + seen, networks = set(), [] + for line in raw.splitlines(): + parts = line.split(":") + if len(parts) < 2: continue + ssid = parts[0].strip() + if not ssid or ssid in seen: continue + seen.add(ssid) + try: sig = int(parts[1]) + except: sig = 0 + sec = parts[2].strip() if len(parts) > 2 else "Open" + in_use = (parts[3].strip() == "*") if len(parts) > 3 else False + networks.append((ssid, sig, sec, in_use)) + networks.sort(key=lambda r: (not r[3], -r[1])) + + GLib.idle_add(self._populate, conn_type, device, current_ssid, ip, networks[:20]) + + def _populate(self, conn_type, device, current_ssid, ip, networks): + # Rebuild header + for child in self.header.get_children(): + self.header.remove(child) + + if conn_type == "wifi" and current_ssid: + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + lbl_ssid = Gtk.Label(label=current_ssid, xalign=0.0) + lbl_ssid.get_style_context().add_class("net-ssid") + lbl_ip = Gtk.Label(label=ip or "no IP", xalign=0.0) + lbl_ip.get_style_context().add_class("net-ip") + vbox.pack_start(lbl_ssid, False, False, 0) + vbox.pack_start(lbl_ip, False, False, 0) + self.header.pack_start(vbox, True, True, 0) + + btn_dis = Gtk.Button(label="Disconnect") + btn_dis.get_style_context().add_class("footer-btn") + btn_dis.set_relief(Gtk.ReliefStyle.NONE) + btn_dis.connect('clicked', lambda _, d=device: ( + self.hide(), + subprocess.Popen(["bash", "-c", + f"nmcli dev disconnect {d}; notify-send Network Disconnected"]) + )) + self.header.pack_start(btn_dis, False, False, 0) + + elif conn_type == "ethernet": + lbl = Gtk.Label(label=f"Wired {ip or ''}", xalign=0.0) + self.header.pack_start(lbl, True, True, 0) + else: + lbl = Gtk.Label(label="Not connected", xalign=0.0) + lbl.get_style_context().add_class("net-ip") + self.header.pack_start(lbl, True, True, 0) + + self.header.show_all() + + for ssid, sig, sec, in_use in networks: + row = Gtk.ListBoxRow() + row.ssid = ssid + row.sec = sec + row.in_use = in_use + + box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + box.get_style_context().add_class("net-row-box") + + try: + pb = GdkPixbuf.Pixbuf.new_from_file_at_size(signal_icon(sig), 16, 16) + img = Gtk.Image.new_from_pixbuf(pb) + except Exception: + img = Gtk.Label(label="?") + box.pack_start(img, False, False, 0) + + ssid_lbl = Gtk.Label(label=ssid, xalign=0.0) + ssid_lbl.set_ellipsize(3) + if in_use: + ssid_lbl.get_style_context().add_class("net-ssid") + box.pack_start(ssid_lbl, True, True, 0) + + if sec and sec.lower() not in ("open", "--", ""): + lock = Gtk.Label(label="") + lock.get_style_context().add_class("net-lock") + box.pack_start(lock, False, False, 0) + + if in_use: + chk = Gtk.Label(label="✓") + chk.get_style_context().add_class("net-check") + box.pack_start(chk, False, False, 0) + + row.add(box) + self.listbox.add(row) + + self.listbox.show_all() + + # Re-position now that height is known + GLib.idle_add(self._position) + + def _on_vpn_toggle(self, switch, state): + threading.Thread(target=self._do_vpn_toggle, args=(state,), daemon=True).start() + return False # let GTK move the switch immediately; revert on failure + + def _do_vpn_toggle(self, enable): + action = "up" if enable else "down" + try: + subprocess.check_output( + ["nmcli", "connection", action, "wgs_client"], + stderr=subprocess.STDOUT, text=True + ) + label = "Connected" if enable else "Disconnected" + subprocess.Popen(["notify-send", "WireGuard", label]) + GLib.idle_add(self._apply_vpn_state, enable, label) + except subprocess.CalledProcessError as e: + msg = (e.output.strip().split("\n")[-1] if e.output else "Unknown error") + subprocess.Popen(["notify-send", "-u", "critical", "WireGuard", + f"Failed: {msg}"]) + # Revert switch on failure + GLib.idle_add(self._apply_vpn_state, not enable, + "Connected" if not enable else "Disconnected") + + def _apply_vpn_state(self, active, label): + if self.vpn_switch: + self.vpn_switch.handler_block(self._vpn_handler) + self.vpn_switch.set_active(active) + self.vpn_switch.handler_unblock(self._vpn_handler) + if self.vpn_status_lbl: + self.vpn_status_lbl.set_text(label) + + def _on_row_activated(self, lb, row): + if row.in_use: + return + self.hide() + if row.sec and row.sec.lower() not in ("open", "--", ""): + self._show_password_prompt(row.ssid) + else: + self._do_connect(row.ssid) + + def _do_connect(self, ssid, password=None): + def run(): + cmd = ["nmcli", "dev", "wifi", "connect", ssid] + if password: + cmd += ["password", password] + try: + subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True) + subprocess.Popen(["notify-send", "Network", f"Connected to {ssid}"]) + except subprocess.CalledProcessError as e: + msg = (e.output.strip().split("\n")[-1] + if e.output else "Unknown error") + subprocess.Popen(["notify-send", "-u", "critical", "Network", + f"Failed to connect to {ssid}:\n{msg}"]) + subprocess.Popen(["notify-send", "Network", f"Connecting to {ssid}…"]) + threading.Thread(target=run, daemon=True).start() + + def _show_password_prompt(self, ssid): + dlg = Gtk.Window() + dlg.set_title(f"Connect to {ssid}") + dlg.set_default_size(300, -1) + dlg.set_position(Gtk.WindowPosition.CENTER) + dlg.set_keep_above(True) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) + box.set_margin_top(16); box.set_margin_bottom(16) + box.set_margin_start(16); box.set_margin_end(16) + + lbl = Gtk.Label(xalign=0.0) + lbl.set_markup(f'Password for "{ssid}"') + entry = Gtk.Entry() + entry.set_visibility(False) + entry.set_placeholder_text("Enter password…") + + btn_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + btn_box.set_halign(Gtk.Align.END) + btn_cancel = Gtk.Button(label="Cancel") + btn_connect = Gtk.Button(label="Connect") + btn_box.pack_start(btn_cancel, False, False, 0) + btn_box.pack_start(btn_connect, False, False, 0) + + box.pack_start(lbl, False, False, 0) + box.pack_start(entry, False, False, 0) + box.pack_start(btn_box, False, False, 0) + dlg.add(box) + + def on_confirm(_): + pw = entry.get_text() + dlg.destroy() + if pw: + self._do_connect(ssid, pw) + + entry.connect("activate", on_confirm) + btn_connect.connect("clicked", on_confirm) + btn_cancel.connect("clicked", lambda _: dlg.destroy()) + dlg.connect("key-press-event", + lambda w, e: w.destroy() if e.keyval == Gdk.KEY_Escape else None) + dlg.show_all() + dlg.present() + + +def main(): + # If already running, toggle it and exit + try: + with open(PID_FILE) as f: + pid = int(f.read().strip()) + os.kill(pid, signal.SIGUSR1) + sys.exit(0) + except (FileNotFoundError, ProcessLookupError, ValueError): + pass + + provider = Gtk.CssProvider() + provider.load_from_data(POPUP_CSS) + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + apply_css() + NetworkPopup() + Gtk.main() + + +if __name__ == "__main__": + main() diff --git a/bin/panel/nexus-applet.sh b/bin/panel/nexus-applet.sh new file mode 100644 index 0000000..d5ea204 --- /dev/null +++ b/bin/panel/nexus-applet.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# Genmon Nexus applet — logo icon whose state tracks the running services, +# left-click opens the control popup. Mirrors network-applet.sh. + +NEXUS_ROOT="$(cd "$(dirname "$(realpath "$0")")/../.." && pwd)" +ICON_DIR="$NEXUS_ROOT/assets/panel-icons" + +# Fast, dependency-free liveness check via bash's built-in /dev/tcp — no curl +# round-trip on every 5s genmon tick. +port_up() { + (exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null && { exec 3>&- 3<&-; return 0; } + return 1 +} + +state() { port_up "$1" && echo "up" || echo "down"; } + +BACKEND=$(state 8000) # Synapse +MEMORY=$(state 8001) # Memory service +FRONTEND=$(state 5173) # Vite frontend + +up=0 +[ "$BACKEND" = up ] && up=$((up + 1)) +[ "$MEMORY" = up ] && up=$((up + 1)) +[ "$FRONTEND" = up ] && up=$((up + 1)) + +if [ "$up" -eq 3 ]; then ICON="$ICON_DIR/nexus-on.png" +elif [ "$up" -eq 0 ]; then ICON="$ICON_DIR/nexus-off.png" +else ICON="$ICON_DIR/nexus-partial.png" +fi + +dot() { [ "$1" = up ] && echo "●" || echo "○"; } + +echo "$ICON" +echo "NexusOS — ${up}/3 services up +$(dot "$BACKEND") Synapse backend :8000 +$(dot "$MEMORY") Memory service :8001 +$(dot "$FRONTEND") Frontend (Vite) :5173" +echo "$HOME/.local/bin/nexus-popup.py" diff --git a/bin/panel/nexus-popup.py b/bin/panel/nexus-popup.py new file mode 100644 index 0000000..6fde653 --- /dev/null +++ b/bin/panel/nexus-popup.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +"""NexusOS service popup daemon — toggle via SIGUSR1, instant open. + +Mirrors network-popup.py: an autostarted daemon holding an override_redirect +window. The genmon applet's re-invokes this script, which signals the +running daemon (SIGUSR1) to toggle the window instead of paying Python startup +on every click. +""" +# PyGObject (gi.repository) is dynamically generated and its API is Optional-heavy +# (e.g. Gdk.Display.get_default() is typed Display|None). These are fine at runtime, +# so silence the type-checker noise for this GTK desktop script. +# pyright: reportMissingModuleSource=false, reportOptionalMemberAccess=false, reportArgumentType=false, reportCallIssue=false, reportAttributeAccessIssue=false + +import os, sys, signal, socket, threading, subprocess +from pathlib import Path +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +from gi.repository import Gtk, Gdk, GLib +from nexus_menu_base import apply_css, get_panel_bottom, get_mouse_position + +PID_FILE = "/tmp/nexus-popup.pid" +VISIBLE_FILE = "/tmp/nexus-popup-visible" + +# repo root is three parents up: panel -> bin -> repo +REPO = Path(__file__).resolve().parent.parent.parent +NEXUS_CLI = str(REPO / "management" / "nexus-cli.sh") +CTRL_PANEL = str(REPO / "management" / "controlpanel.py") +VENV_PY = str(REPO / "Promethean" / "bin" / "python") +EDGE_PROFILE = str(Path.home() / ".config" / "nexus-edge") +APP_URL = "http://localhost:5173" + +# name, port, nexus-cli flag +SERVICES = [ + ("Synapse backend", 8000, "--backend"), + ("Memory service", 8001, "--memory"), + ("Frontend (Vite)", 5173, "--frontend"), +] + +WIDTH = 264 + +POPUP_CSS = b""" +.nx-header { padding: 11px 12px 9px 12px; } +.nx-title { font-weight: bold; font-size: 1.05em; } +.nx-sub { color: #a8a8a8; font-size: 0.82em; } +.svc-row { padding: 8px 12px; } +.svc-name { font-size: 0.95em; } +.svc-port { color: #a8a8a8; font-size: 0.78em; } +.dot-up { color: #8cc63f; } +.dot-down { color: #6b6b6b; } +.master-row { padding: 9px 12px; } +.master-label { font-weight: bold; } +.footer-btn { padding: 9px 12px; border-radius: 0; border: none; + background-color: transparent; color: #cfcfcf; } +.footer-btn:hover { background-color: rgba(140,198,63,0.14); color: #f2f2f2; } +switch { background-color: #3a3d41; border-radius: 14px; border: 1px solid #4d3461; + min-width: 42px; min-height: 22px; } +switch:checked { background-color: #8cc63f; border-color: #6ba62a; } +switch slider { background-color: #f2f2f2; border-radius: 50%; + min-width: 16px; min-height: 16px; margin: 2px; } +""" + + +def port_up(port, timeout=0.3): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(timeout) + try: + return s.connect_ex(("127.0.0.1", port)) == 0 + except OSError: + return False + finally: + s.close() + + +class NexusPopup: + def __init__(self): + self.visible = False + self._click_x = None + # widgets keyed by port so background refreshes can update them + self._switches = {} # port -> (Gtk.Switch, handler_id) + self._dots = {} # port -> Gtk.Label + self._master = None + self._master_handler = None + self._sub_lbl = None + self._build_window() + + def _on_sig(s, f): + try: + self._click_x = get_mouse_position()[0] + except Exception: + self._click_x = None + GLib.idle_add(self.toggle) + + signal.signal(signal.SIGUSR1, _on_sig) + with open(PID_FILE, 'w') as f: + f.write(str(os.getpid())) + + # ── window plumbing (mirrors network-popup) ─────────────────────── + def _build_window(self): + self.win = Gtk.Window(type=Gtk.WindowType.POPUP) + self.win.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU) + self.win.set_decorated(False) + self.win.set_skip_taskbar_hint(True) + self.win.set_keep_above(True) + self.win.set_default_size(WIDTH, -1) + + self.win.connect('key-press-event', + lambda w, e: self.hide() if e.keyval == Gdk.KEY_Escape else None) + + def on_button_press(w, event): + wx, wy = w.get_position() + ww, wh = w.get_allocated_width(), w.get_allocated_height() + if (int(event.x_root) < wx or int(event.x_root) >= wx + ww or + int(event.y_root) < wy or int(event.y_root) >= wy + wh): + self.hide() + return False + self.win.connect('button-press-event', on_button_press) + + def on_map(w, _): + gdk_win = w.get_window() + if gdk_win: + Gdk.Display.get_default().get_default_seat().grab( + gdk_win, Gdk.SeatCapabilities.ALL, True, None, None, None) + return False + self.win.connect('map-event', on_map) + + def on_unmap(w, _): + Gdk.Display.get_default().get_default_seat().ungrab() + self.win.connect('unmap-event', on_unmap) + + def toggle(self): + self.hide() if self.visible else self.show() + + def show(self): + for child in self.win.get_children(): + self.win.remove(child) + self._switches.clear() + self._dots.clear() + self.win.add(self._build_content()) + self.win.show_all() + self._sync_state() + self._position() + self.win.present() + self.visible = True + open(VISIBLE_FILE, 'w').close() + + def hide(self): + self.win.hide() + self.visible = False + try: + os.remove(VISIBLE_FILE) + except FileNotFoundError: + pass + + def _position(self): + mx = self._click_x if self._click_x is not None else get_mouse_position()[0] + sw = Gdk.Screen.get_default().get_width() + panel_bottom = get_panel_bottom() + x = max(4, min(mx - WIDTH // 2, sw - WIDTH - 4)) + self.win.move(x, panel_bottom + 2) + + # ── content ─────────────────────────────────────────────────────── + def _build_content(self): + outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + # Header + head = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + head.get_style_context().add_class("nx-header") + title = Gtk.Label(label="NexusOS", xalign=0.0) + title.get_style_context().add_class("nx-title") + self._sub_lbl = Gtk.Label(label="", xalign=0.0) + self._sub_lbl.get_style_context().add_class("nx-sub") + head.pack_start(title, False, False, 0) + head.pack_start(self._sub_lbl, False, False, 0) + outer.pack_start(head, False, False, 0) + outer.pack_start(Gtk.Separator(), False, False, 0) + + # Per-service rows + for name, port, flag in SERVICES: + row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + row.get_style_context().add_class("svc-row") + + dot = Gtk.Label(label="●") + dot.get_style_context().add_class("dot-down") + row.pack_start(dot, False, False, 0) + self._dots[port] = dot + + txt = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + nm = Gtk.Label(label=name, xalign=0.0) + nm.get_style_context().add_class("svc-name") + pl = Gtk.Label(label=f":{port}", xalign=0.0) + pl.get_style_context().add_class("svc-port") + txt.pack_start(nm, False, False, 0) + txt.pack_start(pl, False, False, 0) + row.pack_start(txt, True, True, 0) + + sw = Gtk.Switch() + sw.set_valign(Gtk.Align.CENTER) + hid = sw.connect('state-set', self._on_service_toggle, flag, port) + self._switches[port] = (sw, hid) + row.pack_start(sw, False, False, 0) + + outer.pack_start(row, False, False, 0) + + outer.pack_start(Gtk.Separator(), False, False, 0) + + # Master "All services" row + master_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) + master_row.get_style_context().add_class("master-row") + ml = Gtk.Label(label="All services", xalign=0.0) + ml.get_style_context().add_class("master-label") + master_row.pack_start(ml, True, True, 0) + self._master = Gtk.Switch() + self._master.set_valign(Gtk.Align.CENTER) + self._master_handler = self._master.connect('state-set', self._on_master_toggle) + master_row.pack_start(self._master, False, False, 0) + outer.pack_start(master_row, False, False, 0) + + outer.pack_start(Gtk.Separator(), False, False, 0) + + # Footer actions + btn_panel = self._footer_button("Control Panel", self._open_control_panel) + btn_app = self._footer_button("Open App", self._open_app) + outer.pack_start(btn_panel, False, False, 0) + outer.pack_start(Gtk.Separator(), False, False, 0) + outer.pack_start(btn_app, False, False, 0) + + return outer + + def _footer_button(self, label, cb): + btn = Gtk.Button(label=label) + btn.get_style_context().add_class("footer-btn") + btn.set_relief(Gtk.ReliefStyle.NONE) + btn.connect('clicked', lambda _: (self.hide(), cb())) + return btn + + # ── state sync ───────────────────────────────────────────────────── + def _sync_state(self): + """Read live port state and update every switch, dot, and the header.""" + up = 0 + all_up = True + for _, port, _flag in SERVICES: + alive = port_up(port) + up += 1 if alive else 0 + all_up = all_up and alive + self._set_switch(port, alive) + dot = self._dots.get(port) + if dot: + ctx = dot.get_style_context() + ctx.remove_class("dot-up"); ctx.remove_class("dot-down") + ctx.add_class("dot-up" if alive else "dot-down") + if self._master is not None: + self._master.handler_block(self._master_handler) + self._master.set_active(all_up) + self._master.handler_unblock(self._master_handler) + if self._sub_lbl is not None: + n = len(SERVICES) + label = ("All services running" if up == n + else "Stopped" if up == 0 + else f"{up}/{n} services running") + self._sub_lbl.set_text(label) + + def _set_switch(self, port, active): + entry = self._switches.get(port) + if not entry: + return + sw, hid = entry + sw.handler_block(hid) + sw.set_active(active) + sw.handler_unblock(hid) + + # ── actions ──────────────────────────────────────────────────────── + def _cli(self, *args): + """Run nexus-cli.sh, then resync the UI from real port state.""" + def run(): + try: + subprocess.run(["bash", NEXUS_CLI, *args], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + timeout=60) + except Exception: + pass + GLib.idle_add(self._sync_state) + threading.Thread(target=run, daemon=True).start() + + def _on_service_toggle(self, switch, state, flag, port): + self._cli("start" if state else "stop", flag) + return False # let GTK move the switch now; _sync_state corrects on failure + + def _on_master_toggle(self, switch, state): + self._cli("start" if state else "stop", "all") + return False + + def _open_control_panel(self): + try: + subprocess.Popen([VENV_PY, CTRL_PANEL], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except Exception as e: + subprocess.Popen(["notify-send", "NexusOS", f"Control Panel failed: {e}"]) + + def _open_app(self): + # Raise the existing dedicated app window if it's already open, else + # launch a new one. No service lifecycle ownership here (unlike + # nexus-app.sh) — the switches above own start/stop. + try: + already = subprocess.run( + ["pgrep", "-f", f"user-data-dir={EDGE_PROFILE}"], + stdout=subprocess.DEVNULL).returncode == 0 + cmd = ["microsoft-edge-stable", f"--user-data-dir={EDGE_PROFILE}"] + if not already: + cmd += ["--no-first-run", "--no-default-browser-check", + "--disable-background-mode", "--class=NexusOS"] + cmd += [f"--app={APP_URL}"] + subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except Exception as e: + subprocess.Popen(["notify-send", "NexusOS", f"Open App failed: {e}"]) + + +def main(): + # Already running? Signal it to toggle and exit. + try: + with open(PID_FILE) as f: + pid = int(f.read().strip()) + os.kill(pid, signal.SIGUSR1) + sys.exit(0) + except (FileNotFoundError, ProcessLookupError, ValueError): + pass + + provider = Gtk.CssProvider() + provider.load_from_data(POPUP_CSS) + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + + apply_css() + NexusPopup() + Gtk.main() + + +if __name__ == "__main__": + main() diff --git a/bin/panel/nexus_menu_base.py b/bin/panel/nexus_menu_base.py new file mode 100644 index 0000000..347ebaf --- /dev/null +++ b/bin/panel/nexus_menu_base.py @@ -0,0 +1,241 @@ +"""Shared base for NexusOS popup menus — override_redirect window, no WM placement.""" +# PyGObject (gi.repository) is dynamically generated and its API is Optional-heavy +# (e.g. Gdk.Display.get_default() is typed Display|None). These are fine at runtime, +# so silence the type-checker noise for this GTK desktop script. +# pyright: reportMissingModuleSource=false, reportOptionalMemberAccess=false, reportArgumentType=false, reportCallIssue=false, reportAttributeAccessIssue=false + +import gi, fcntl, os, sys, signal, subprocess +gi.require_version('Gtk', '3.0') +gi.require_version('Gdk', '3.0') +from gi.repository import Gtk, Gdk, GdkPixbuf, GLib + +NEXUS_CSS = b""" +* { font-family: sans-serif; } + +window { + background-color: #2a1d33; + color: #f2f2f2; + border: 1px solid #4d3461; + border-radius: 6px; +} + +notebook { + background-color: #2a1d33; +} +notebook > header { + background-color: #1e1526; + border-bottom: 1px solid #4d3461; + border-radius: 6px 6px 0 0; +} +notebook > header > tabs > tab { + color: #a8a8a8; + padding: 6px 16px; + border: none; + background-color: transparent; + border-bottom: 2px solid transparent; +} +notebook > header > tabs > tab:checked { + color: #8cc63f; + border-bottom: 2px solid #8cc63f; +} +notebook > header > tabs > tab:hover:not(:checked) { + color: #f2f2f2; + background-color: rgba(140,198,63,0.10); +} +notebook stack { + background-color: #2a1d33; +} + +treeview { + background-color: #1e1526; + color: #f2f2f2; +} +treeview:selected { + background-color: #88008f; + color: #ffffff; +} +treeview:hover { + background-color: rgba(140,198,63,0.14); +} +treeview header button { + background-color: #1e1526; + color: #a8a8a8; + border: none; + border-bottom: 1px solid #4d3461; + box-shadow: none; +} + +label { color: #f2f2f2; } + +button { + background-color: #2e3236; + color: #f2f2f2; + border: 1px solid #3a3d41; + border-radius: 4px; + padding: 5px 14px; + box-shadow: none; + text-shadow: none; + -gtk-icon-shadow: none; +} +button:hover { + background-color: rgba(140,198,63,0.18); + border-color: #8cc63f; + color: #8cc63f; +} +button:active { + background-color: #8cc63f; + color: #0a0a00; +} + +scale trough { + background-color: #1e1526; + border: 1px solid #4d3461; + border-radius: 4px; + min-height: 6px; +} +scale trough highlight { + background-color: #8cc63f; + border-radius: 4px; +} +scale slider { + background-color: #8cc63f; + border: none; + border-radius: 50%; + min-width: 16px; + min-height: 16px; +} + +separator { + background-color: #4d3461; + min-height: 1px; +} + +scrolledwindow { background-color: #1e1526; } + +.section-header { + color: #a8a8a8; + font-size: 0.8em; + padding: 4px 8px 2px 8px; +} +.action-row { + padding: 6px 12px; + border-radius: 4px; +} +.action-row:hover { + background-color: rgba(140,198,63,0.14); + color: #8cc63f; +} +.dim { color: #a8a8a8; } +""" + + +def apply_css(): + provider = Gtk.CssProvider() + provider.load_from_data(NEXUS_CSS) + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, + ) + + +def make_popup_window(width, height): + """Create an override_redirect popup window — bypasses WM placement entirely.""" + win = Gtk.Window(type=Gtk.WindowType.POPUP) + win.set_type_hint(Gdk.WindowTypeHint.POPUP_MENU) + win.set_decorated(False) + win.set_skip_taskbar_hint(True) + win.set_skip_pager_hint(True) + win.set_keep_above(True) + win.set_default_size(width, height) + win.set_resizable(False) + + win.connect('key-press-event', lambda w, e: w.destroy() if e.keyval == Gdk.KEY_Escape else None) + + def on_button_press(w, event): + wx, wy = w.get_position() + ww = w.get_allocated_width() + wh = w.get_allocated_height() + rx, ry = int(event.x_root), int(event.y_root) + if rx < wx or rx >= wx + ww or ry < wy or ry >= wy + wh: + w.destroy() + return False + win.connect('button-press-event', on_button_press) + + def on_map(w, _event): + def do_grab(): + gdk_win = w.get_window() + if gdk_win: + seat = Gdk.Display.get_default().get_default_seat() + seat.grab(gdk_win, Gdk.SeatCapabilities.ALL, True, None, None, None) + return False # don't repeat + GLib.timeout_add(150, do_grab) + return False + win.connect('map-event', on_map) + + def on_destroy(w): + Gdk.Display.get_default().get_default_seat().ungrab() + win.connect('destroy', on_destroy) + + return win + + +def get_mouse_position(): + display = Gdk.Display.get_default() + seat = display.get_default_seat() + _, x, y = seat.get_pointer().get_position() + return x, y + + +def get_panel_bottom(): + """Return y-coordinate just below the top panel via _NET_WORKAREA.""" + try: + out = subprocess.check_output( + ['xprop', '-root', '_NET_WORKAREA'], + text=True, stderr=subprocess.DEVNULL + ) + nums = [int(n.strip()) for n in out.split('=')[1].split(',')] + return nums[1] # workarea y = where usable area begins (= panel height) + except Exception: + return 40 + + +def position_window(win, width, height): + """Position popup just below the panel, horizontally centered on click.""" + mx, _ = get_mouse_position() + screen = Gdk.Screen.get_default() + sw, sh = screen.get_width(), screen.get_height() + + panel_bottom = get_panel_bottom() + x = mx - width // 2 + y = panel_bottom + 2 + + x = max(4, min(x, sw - width - 4)) + if height > 0: + y = min(y, sh - height - 4) + + win.move(x, y) + + +def single_instance(lockfile, pidfile): + """flock-based single instance. Returns lock_fd on success, exits on collision.""" + fd = open(lockfile, 'w') + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + try: + with open(pidfile) as f: + os.kill(int(f.read().strip()), signal.SIGTERM) + except Exception: + pass + sys.exit(0) + with open(pidfile, 'w') as f: + f.write(str(os.getpid())) + return fd + + +def load_icon(path, size=16): + try: + return GdkPixbuf.Pixbuf.new_from_file_at_size(path, size, size) + except Exception: + return None diff --git a/bin/panel/plank-primary-watch.sh b/bin/panel/plank-primary-watch.sh new file mode 100644 index 0000000..01e283d --- /dev/null +++ b/bin/panel/plank-primary-watch.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# plank-primary-watch.sh +# Keep Plank pinned to the xrandr *primary* monitor. +# +# Plank's monitor='' ("primary") miscomputes its autohide reveal region on +# multi-head layouts, so we write the explicit connector name into Plank's +# `monitor` gsetting and restart Plank whenever the primary output changes +# OR its geometry (resolution/position) changes — either invalidates Plank's +# cached reveal region. + +PLANK_PATH="net.launchpad.plank.dock.settings:/net/launchpad/plank/docks/dock1/" + +# Connector name of the primary output, e.g. "DisplayPort-9". +primary_name() { + xrandr --query | awk '/ connected primary/ {print $1; exit}' +} + +# Change key: name + "WxH+X+Y" geometry. Changes if the primary moves/resizes. +primary_key() { + xrandr --query | awk '/ connected primary/ {print $1, $4; exit}' +} + +set_monitor() { + # $1 = connector name; only write if it differs (avoids needless restarts) + local want="$1" have + have="$(gsettings get "$PLANK_PATH" monitor 2>/dev/null | tr -d \"\')" + [ "$have" = "$want" ] || gsettings set "$PLANK_PATH" monitor "$want" +} + +start_plank() { + pgrep -x plank >/dev/null || setsid plank >/dev/null 2>&1 & +} + +restart_plank() { + pkill -x plank + sleep 0.5 + setsid plank >/dev/null 2>&1 & +} + +last="$(primary_key)" +name="$(primary_name)" +[ -n "$name" ] && set_monitor "$name" +start_plank + +while true; do + sleep 3 + cur="$(primary_key)" + if [ -n "$cur" ] && [ "$cur" != "$last" ]; then + last="$cur" + set_monitor "$(primary_name)" + restart_plank + elif ! pgrep -x plank >/dev/null; then + # Plank died (crash, manual kill) — bring it back. + start_plank + fi +done diff --git a/bin/restore-linux.sh b/bin/restore-linux.sh new file mode 100644 index 0000000..0a71899 --- /dev/null +++ b/bin/restore-linux.sh @@ -0,0 +1,120 @@ +#!/bin/bash + +# The Linux-only half of a restore: system packages, the bundled Ollama binary, +# and the XFCE desktop wiring (panel, wallpaper, theme, terminal, branding). +# None of it means anything on Windows, which is why it lives here instead of in +# bin/sync.py — sync.py owns the portable half and calls this in two stages: +# +# restore-linux.sh prep Before the rebuild: system packages the build needs. +# restore-linux.sh desktop After the rebuild: Ollama binary + desktop wiring. +# +# Not meant to be run by hand — use `ncp restore` (python bin/sync.py restore). + +# Set by bin/sync.py to the repo it was invoked from, so a clone in a scratch dir +# operates on itself instead of reaching into the real ~/nexus-core. +NEXUS_ROOT="${NEXUS_ROOT:-$HOME/nexus-core}" +stage="${1:?usage: restore-linux.sh prep|desktop}" +cd "$NEXUS_ROOT" || { echo "No $NEXUS_ROOT"; exit 1; } + +if [ "$stage" = "prep" ]; then + # Fresh machine: the system packages the desktop + build steps need. No-op once + # installed, so it's cheap on an in-place restore. Skipped without apt (non-Debian). + if command -v apt-get >/dev/null; then + echo "Installing system packages..." + sudo apt-get install -y --no-install-recommends \ + git sqlite3 curl python3-venv python3-gi gir1.2-gtk-3.0 \ + nodejs npm xfce4-genmon-plugin plank blueman \ + || echo "Warning: some system packages failed — install them manually." + fi + exit 0 +fi + +echo "" +echo "Ensuring Ollama binary..." +# ollama/ is gitignored, so a fresh clone has no binary — fetch it. No-op if the +# machine already has one (e.g. an in-place restore). +if [ -x "$NEXUS_ROOT/bin/fetch-ollama.sh" ]; then + "$NEXUS_ROOT/bin/fetch-ollama.sh" "$NEXUS_ROOT" || echo "Warning: Ollama fetch failed — install it manually." +else + echo "Warning: bin/fetch-ollama.sh not found — skipping Ollama fetch." +fi + +# Panel layout, wallpaper, compositing, keybindings and terminal profile all live +# in the xfconf channel XMLs — restore them by copying the files back. xfconfd +# caches channels in memory and rewrites them on exit, so it has to die first or +# it clobbers what we just copied; the next xfconf-query respawns it. +xml_snap="$NEXUS_ROOT/assets/themes/restore-snapshot/xfconf-xml" +xml_dst="$HOME/.config/xfce4/xfconf/xfce-perchannel-xml" +if [ -d "$xml_snap" ] && command -v xfconf-query >/dev/null; then + echo "" + echo "Restoring XFCE desktop settings (panel, wallpaper, effects)..." + mkdir -p "$xml_dst" + pkill -x xfconfd 2>/dev/null && sleep 1 + cp -f "$xml_snap"/*.xml "$xml_dst/" 2>/dev/null + + # Wallpaper props are keyed by monitor name, which differs per machine — point + # every backdrop this box actually has at the Nexus background. + bg="$NEXUS_ROOT/assets/background.png" + if [ -f "$bg" ] && [ -n "${DISPLAY:-}" ]; then + xfconf-query -c xfce4-desktop -l 2>/dev/null | grep 'last-image$' | while read -r p; do + xfconf-query -c xfce4-desktop -p "$p" -s "$bg" 2>/dev/null || true + done + fi +fi + +# Multi-monitor primary-follow watcher (panel + Plank track the xrandr primary). +if [ -f "$NEXUS_ROOT/bin/panel/plank-primary-watch.sh" ]; then + mkdir -p "$HOME/.local/bin" + chmod +x "$NEXUS_ROOT/bin/panel/plank-primary-watch.sh" + ln -sf "$NEXUS_ROOT/bin/panel/plank-primary-watch.sh" "$HOME/.local/bin/plank-primary-watch.sh" +fi +plank_snap="$NEXUS_ROOT/assets/themes/restore-snapshot/plank" +if [ -d "$plank_snap" ]; then + mkdir -p "$HOME/.config/plank" + cp -rf "$plank_snap/." "$HOME/.config/plank/" # /. = contents, else it nests +fi + +echo "" +echo "Restoring NexusOS desktop theme..." +theme_installer="$NEXUS_ROOT/assets/themes/install-theme.sh" +if [ -x "$theme_installer" ]; then + "$theme_installer" +elif [ -f "$theme_installer" ]; then + bash "$theme_installer" +else + echo "Warning: $theme_installer not found — skipping theme restore." +fi + +echo "" +echo "Installing Promethean Terminal launcher..." +term_installer="$NEXUS_ROOT/bin/promethean/install.sh" +if [ -x "$term_installer" ]; then + "$term_installer" +else + echo "Warning: $term_installer not found — skipping." +fi + +echo "" +echo "Installing panel applet..." +panel_installer="$NEXUS_ROOT/bin/panel/install.sh" +if [ -x "$panel_installer" ]; then + "$panel_installer" +else + echo "Warning: $panel_installer not found — skipping panel install." +fi + +# Distro branding — the About dialog / neofetch read /etc/os-release. +if grep -q '^ID=linuxmint' /etc/os-release 2>/dev/null && ! grep -q 'NexusOS' /etc/os-release; then + echo "" + echo "Branding /etc/os-release as NexusOS..." + sudo sed -i -e 's/^NAME=.*/NAME="NexusOS"/' -e 's/^PRETTY_NAME=.*/PRETTY_NAME="NexusOS 1.0"/' \ + /etc/os-release || echo "Warning: os-release branding skipped." +fi + +# The AMD box runs CPU-only (Vega 20, 4GB VRAM thrashes). An NVIDIA box should not. +if command -v nvidia-smi >/dev/null && \ + [ "$(sqlite3 "$NEXUS_ROOT/synapse/memory/memory.db" \ + "select value from settings where key='memory_gpu_offload'" 2>/dev/null)" = "0" ]; then + echo "Note: memory_gpu_offload=0 came from the AMD box (4GB VRAM). This machine has an" + echo " NVIDIA GPU — raise it in Settings to actually use the card." +fi diff --git a/bin/sync.py b/bin/sync.py new file mode 100644 index 0000000..d458bc9 --- /dev/null +++ b/bin/sync.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python3 +"""NexusOS backup and restore - one entry point for Linux and native Windows. + +Same command on both boxes. Everything portable lives here: the git sync with +Gitea, the memory-DB dump / compare / rebuild, and the venv + web-UI rebuild. +The parts that only mean something on Linux - apt packages, the bundled Ollama +binary, the XFCE desktop wiring, the desktop snapshot - stay in bash and get +called from here, skipped outright on Windows. + + python bin/sync.py restore [--check] + python bin/sync.py backup [--check] [--full] [--force-db] + python bin/sync.py compare # print the direction verdict only + +Standard library only, so it runs before the Promethean venv exists and needs no +sqlite3 binary on PATH - Windows has none, which is why the old +bin/db-compare.sh could never work there. + +A fresh machine clones first (git clone nexus-core), then runs this. +""" +import argparse +import os +import shutil +import sqlite3 +import subprocess +import sys +from datetime import datetime, timezone +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +DB = ROOT / "synapse" / "memory" / "memory.db" +DB_SQL = ROOT / "synapse" / "memory" / "memory.db.sql" +DB_SQL_REL = "synapse/memory/memory.db.sql" # git paths are always posix-style + + +def run(*args, capture=False, check=False): + """Run a command from the repo root. Returns CompletedProcess.""" + exe = shutil.which(args[0]) # resolves npm -> npm.cmd on Windows + if exe is None: + raise SystemExit(f"'{args[0]}' not found on PATH") + return subprocess.run( + [exe, *args[1:]], cwd=ROOT, check=check, + capture_output=capture, text=True, encoding="utf-8", + ) + + +def linux_stage(script: str, *args) -> None: + """Run one of the Linux-only bash stages. A no-op on Windows, where apt, + xfconf, plank and the rest have nothing to act on.""" + if os.name == "nt": + return + path = ROOT / "bin" / script + bash = shutil.which("bash") + if path.exists() and bash: + # Pin the stage to THIS repo. It defaults to ~/nexus-core otherwise, so a + # clone in a scratch dir would restore over the real machine instead. + subprocess.run([bash, str(path), *args], cwd=ROOT, check=False, + env={**os.environ, "NEXUS_ROOT": str(ROOT)}) + + +def venv_python() -> Path: + """Path to the Promethean interpreter, creating the venv if it's missing.""" + venv = ROOT / "Promethean" + py = venv / ("Scripts/python.exe" if os.name == "nt" else "bin/python") + if not py.exists(): + subprocess.run([sys.executable, "-m", "venv", str(venv)], check=True) + return py + + +def requirements() -> str: + """Pick the PyTorch overlay for this host.""" + if os.name == "nt": + return "requirements-wsl.txt" # CPU / pure-Python, right for native Windows + if shutil.which("nvidia-smi"): + return "requirements-nvidia.txt" + lspci = shutil.which("lspci") + if lspci and "nvidia" in subprocess.run( + [lspci], capture_output=True, text=True).stdout.lower(): + return "requirements-nvidia.txt" + return "requirements-amd.txt" + + +# -- memory DB ----------------------------------------------------------------- + +def dump_db() -> bool: + """Dump the (gitignored, binary, WAL) memory DB to a diff-friendly SQL file + so git backs up the assistant's memory + conversation history. sqlite3 reads + through the WAL, so the dump has the latest committed data uncheckpointed.""" + if not DB.exists(): + return False + try: + with sqlite3.connect(f"file:{DB}?mode=ro", uri=True) as conn: + DB_SQL.write_text("\n".join(conn.iterdump()) + "\n", encoding="utf-8") + except sqlite3.Error as exc: + print(f"Warning: could not dump {DB} ({exc}) - DB not captured") + return False + print(f"Dumped memory DB -> {DB_SQL}") + return True + + +def _state(conn): + """What a machine holds: conversations as {id: updated_at} and memory facts + as a set of ids. NOT messages.id - it's INTEGER AUTOINCREMENT, so both + machines hand out the same ids independently and comparing them is + meaningless. A new message bumps its conversation's updated_at, which is + what actually gets caught here.""" + def query(sql): + try: + return conn.execute(sql).fetchall() + except sqlite3.Error: + return [] # table absent in an old dump - treat as empty + return ( + dict(query("select id, updated_at from conversations")), + {row[0] for row in query("select id from memory")}, + ) + + +def _extra(a, b) -> int: + """How much `a` holds that `b` lacks: conversations `b` is missing or has an + older copy of, plus memory facts `b` doesn't have at all. An out-of-date + copy is NOT extra content on b's side - that asymmetry is what separates + 'one box is simply ahead' from a real divergence.""" + a_conv, a_mem = a + b_conv, b_mem = b + newer = sum(1 for cid, ts in a_conv.items() if b_conv.get(cid, "") < ts) + return newer + len(a_mem - b_mem) + + +def compare(db: Path = DB, dump: Path = DB_SQL) -> str: + """Which way the sync should go. One of: same, local-ahead, local-behind, + diverged, no-live, no-dump. + ponytail: detects direction, does not merge. Diverged is reported, not resolved.""" + if not dump.exists() or not dump.stat().st_size: + return "no-dump" + if not db.exists(): + return "no-live" + try: + with sqlite3.connect(f"file:{db}?mode=ro", uri=True) as live_conn: + live = _state(live_conn) + with sqlite3.connect(":memory:") as dump_conn: + dump_conn.executescript(dump.read_text(encoding="utf-8")) + backup = _state(dump_conn) + except (sqlite3.Error, OSError): + return "diverged" # can't tell: fail safe, refuse both directions + ahead, behind = _extra(live, backup), _extra(backup, live) + if ahead and behind: + return "diverged" + if ahead: + return "local-ahead" + if behind: + return "local-behind" + return "same" + + +def restore_db() -> None: + """Rebuild the memory DB from the pulled dump, keeping a rollback copy.""" + print() + state = compare() + if state == "same": + print("Memory DB already matches the backup.") + return + if state == "local-ahead": + print("WARNING: this machine has conversations the backup doesn't.") + print(" Not applying the dump - run `backup` HERE first.") + print(f" To discard local history instead: delete {DB} and restore") + return + if state == "diverged": + print("WARNING: this machine and the backup each have conversations the other lacks.") + print(" Not applying the dump - nothing is merging these automatically.") + print(f" Keep local: run `backup` here. Keep remote: delete {DB} and restore") + return + if state == "no-dump": + print("No memory dump in the backup - skipping DB restore.") + return + + print("Restoring memory DB from backup...") + rollback = DB.with_suffix(".db.pre-restore") + if DB.exists(): + shutil.copyfile(DB, rollback) + for suffix in ("", "-wal", "-shm"): + Path(str(DB) + suffix).unlink(missing_ok=True) + try: + with sqlite3.connect(DB) as conn: + conn.executescript(DB_SQL.read_text(encoding="utf-8")) + print("Memory DB restored (conversations + history + facts).") + except sqlite3.Error as exc: + print(f"Warning: memory DB rebuild failed ({exc}).") + if rollback.exists(): + shutil.move(rollback, DB) + print("Rolled back to previous DB.") + + +# -- rebuild ------------------------------------------------------------------- + +def rebuild_env() -> None: + print("\nRebuilding Python environment...") + py, req = venv_python(), requirements() + if (ROOT / req).exists(): + subprocess.run([str(py), "-m", "pip", "install", "--upgrade", "pip", "-q"], check=False) + subprocess.run([str(py), "-m", "pip", "install", "-r", req], cwd=ROOT, check=False) + else: + print(f"Warning: {req} not found - skipping pip install.") + + print("\nRebuilding frontend dependencies...") + web = ROOT / "interface" / "web" + npm = shutil.which("npm") + if npm is None: + print("Warning: npm not found - the web UI will not be built.") + return + subprocess.run([npm, "install"], cwd=web, check=False) + # Build the UI so the backend can serve it single-process (it mounts + # interface/web/dist at :8000). Without this the app has no UI to show. + print("Building frontend...") + subprocess.run([npm, "run", "build"], cwd=web, check=False) + if not (web / "dist" / "index.html").exists(): + print("Warning: interface/web/dist/index.html missing - the app will serve no UI.") + + +# -- commands ------------------------------------------------------------------ + +def cmd_restore(args) -> int: + if args.check: + print("Fetching to preview restore (no changes)...") + run("git", "fetch", "origin") + print("\nCommits a restore would apply:") + print(run("git", "log", "--oneline", "..origin/main", capture=True).stdout or "(up to date)") + print(run("git", "diff", "--stat", "..origin/main", capture=True).stdout) + print(f"Memory DB vs backup: {compare()}") + print("\n(dry-run only - nothing changed. Apply with: restore)") + return 0 + + # System packages first - the venv and npm build below need them present. + linux_stage("restore-linux.sh", "prep") + + print("\nPulling latest from Gitea...") + if run("git", "pull", "--ff-only", "origin", "main").returncode: + print("Pull failed (diverged? stash/commit local changes).") + return 1 + restore_db() + rebuild_env() + linux_stage("restore-linux.sh", "desktop") + print("\nRestore complete. Nexus is ready to start.") + print("Note: Ollama models are not in the backup - pull them with `ollama pull `.") + return 0 + + +def _remote_dump(tmp: Path) -> bool: + """Write origin/main's dump to tmp. False if it can't be fetched.""" + if run("git", "fetch", "-q", "origin").returncode: + print("Note: could not reach Gitea - skipping backup safety check.") + return False + shown = run("git", "show", f"origin/main:{DB_SQL_REL}", capture=True) + if shown.returncode: + return False + tmp.write_text(shown.stdout, encoding="utf-8") + return True + + +def check_db_direction(tmp_dir: Path) -> bool: + """Mirror of restore's guard: refuse to dump a stale live DB over a backup + that already holds newer conversations from the other machine. Compares + against the REMOTE dump, not the working-tree copy - that copy is from this + box's last backup and is exactly what goes stale when the other box pushes.""" + if not DB.exists(): + return True + tmp = tmp_dir / "remote.sql" + if not _remote_dump(tmp): + return True + state = compare(DB, tmp) + if state == "local-behind": + print("REFUSING TO BACK UP: the backup has conversations this machine doesn't.") + print(" Backing up now would overwrite them with this box's older history.") + print(" Run `restore` here first, then back up.") + return False + if state == "diverged": + print("REFUSING TO BACK UP: this machine and the backup each have conversations") + print(" the other lacks. Nothing merges these automatically.") + print(" Force this box's history to win: python bin/sync.py backup --force-db") + return False + return True + + +def cmd_backup(args) -> int: + import tempfile + if args.full: + linux_stage("backup-linux.sh") + dump_db() + with tempfile.TemporaryDirectory() as tmp_dir: + safe = check_db_direction(Path(tmp_dir)) + + if args.check: + print(f"\nMemory DB vs backup: {'safe to back up' if safe else 'STALE - restore first'}") + print("\nFiles a backup would commit:") + print(run("git", "status", "--short", capture=True).stdout) + print("Local commits not yet pushed:") + print(run("git", "log", "--oneline", "@{u}..", capture=True).stdout or "(none / no upstream)") + print("(dry-run only - nothing committed or pushed. Apply with: backup)") + return 0 + if not safe and not args.force_db: + return 1 + + run("git", "add", "-A") + if run("git", "diff", "--cached", "--quiet").returncode: + stamp = datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds") + if run("git", "commit", "-q", "-m", f"backup: {stamp}").returncode: + print("Commit failed - see error above (e.g. git identity not set).") + return 1 + print("Committed backup snapshot.") + else: + print("No changes to commit.") + + print("Pushing to Gitea...") + if run("git", "push", "origin", "main").returncode: + print("Push failed. Set up credentials once with:") + print(" git config credential.helper store # then push once and enter your Gitea token") + return 1 + print("Backup complete.") + return 0 + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="cmd", required=True) + + restore = sub.add_parser("restore", help="pull from Gitea, rebuild DB + venv + web UI") + restore.add_argument("-c", "--check", action="store_true", help="dry run, change nothing") + restore.set_defaults(func=cmd_restore) + + backup = sub.add_parser("backup", help="dump DB, commit and push to Gitea") + backup.add_argument("-c", "--check", action="store_true", help="dry run, change nothing") + backup.add_argument("-f", "--full", action="store_true", + help="also snapshot the live desktop wiring + Claude notes (Linux)") + backup.add_argument("--force-db", action="store_true", help="skip the staleness guard") + backup.set_defaults(func=cmd_backup) + + compare_cmd = sub.add_parser("compare", help="print the sync direction verdict") + compare_cmd.set_defaults(func=lambda a: (print(compare()), 0)[1]) + + args = parser.parse_args() + return args.func(args) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/data/playbooks/0858861d-6c42-48b9-be9f-d7e86cc45586.yaml b/data/playbooks/0858861d-6c42-48b9-be9f-d7e86cc45586.yaml new file mode 100644 index 0000000..7a8833a --- /dev/null +++ b/data/playbooks/0858861d-6c42-48b9-be9f-d7e86cc45586.yaml @@ -0,0 +1,22 @@ +id: 0858861d-6c42-48b9-be9f-d7e86cc45586 +title: main +goal: You are Nexus, a helpful local AI assistant. You function as both an assistant and a friend. +tags: [] +order: 0 +instructions: |- + Your personality: + - Warm, casual, and conversational — treat the user as a friend, not a customer + - Confident and direct — give real answers, not hedged corporate-speak + - Occasionally witty, but never at the expense of being helpful + + Your responsibilities: + - Help the user with tasks, questions, planning, research, writing, and problem solving + - Remember context within a conversation and refer back to it naturally + - Proactively offer suggestions or flag things the user might have missed + + Rules: + - Never refer to yourself as an AI or language model + - Never start a response with "Certainly!", "Of course!", or similar filler phrases + - Never restate, echo, rephrase, or summarize the user's own message back to them. Do NOT open with a header or a recap of what they just said. React to it directly — with your own thoughts, a genuine reaction, or a question — the way a friend would in conversation + - Keep responses concise unless the user asks for detail + - If you don't know something, say so plainly and help find the answer diff --git a/install-windows.ps1 b/install-windows.ps1 new file mode 100644 index 0000000..862fc0e --- /dev/null +++ b/install-windows.ps1 @@ -0,0 +1,198 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + NexusOS installer for Windows (native - no WSL). +.DESCRIPTION + Installs Python, Node.js, and Ollama via winget, builds the Promethean + virtualenv and the web UI, and drops a desktop shortcut. NexusOS then runs + as a single process: the backend on :8000 serves the built UI, and the AI + (Ollama) is started manually from the UI. + + Run from the nexus-core directory: + Right-click install-windows.ps1 -> "Run with PowerShell" + + Requires Windows 10/11 with winget (App Installer). No WSL, no reboot. +#> + +Set-StrictMode -Version Latest +$ErrorActionPreference = "Stop" +$RepoRoot = $PSScriptRoot + +# -- Helpers ------------------------------------------------------------------- +function Write-Step { param([string]$Msg) Write-Host "`n==> $Msg" -ForegroundColor Cyan } +function Write-OK { param([string]$Msg) Write-Host " ok: $Msg" -ForegroundColor Green } +function Write-Warn { param([string]$Msg) Write-Host " warn: $Msg" -ForegroundColor Yellow } +function Write-Fail { param([string]$Msg) Write-Host "`n ERROR: $Msg`n" -ForegroundColor Red; Read-Host "Press Enter to exit"; exit 1 } + +# Pull the current machine + user PATH out of the registry into this session, so +# tools winget just installed become runnable without opening a new shell. +function Update-SessionPath { + $m = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + $u = [System.Environment]::GetEnvironmentVariable("Path", "User") + $env:Path = ($m, $u | Where-Object { $_ }) -join ";" +} + +function Install-Winget { + param([string]$Id, [string]$Label) + Write-Step "Installing $Label" + winget install --id $Id -e --source winget ` + --accept-package-agreements --accept-source-agreements --disable-interactivity | Out-Host + # winget returns non-zero when the package is already installed / up to date; + # that's not a failure for us. Verify presence after refreshing PATH instead. + Update-SessionPath + Write-OK "$Label step done" +} + +# -- Self-elevate to Administrator --------------------------------------------- +$IsAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $IsAdmin) { + Write-Host "Requesting administrator privileges..." -ForegroundColor Yellow + Start-Process powershell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs + exit +} + +Write-Host "" +Write-Host " NexusOS Installer for Windows (native)" -ForegroundColor White +Write-Host " ======================================" -ForegroundColor DarkGray + +# -- winget present? ----------------------------------------------------------- +Write-Step "Checking winget" +if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { + Write-Fail "winget not found. Install 'App Installer' from the Microsoft Store, then re-run this installer." +} +Write-OK "winget available" + +# -- Install runtimes ---------------------------------------------------------- +Install-Winget "Python.Python.3.12" "Python 3.12" +Install-Winget "OpenJS.NodeJS.LTS" "Node.js LTS" +Install-Winget "Ollama.Ollama" "Ollama" + +# -- Verify tools -------------------------------------------------------------- +Write-Step "Verifying tools" +foreach ($t in @("python", "npm", "ollama")) { + if (-not (Get-Command $t -ErrorAction SilentlyContinue)) { + Write-Fail "$t is not on PATH after install. Close this window, open a new PowerShell, and re-run the installer." + } + Write-OK "$t found" +} + +# -- Python venv + deps -------------------------------------------------------- +Write-Step "Building the Promethean virtualenv" +$Venv = Join-Path $RepoRoot "Promethean" +$VenvPy = Join-Path $Venv "Scripts\python.exe" +if (-not (Test-Path $VenvPy)) { + python -m venv $Venv +} +if (-not (Test-Path $VenvPy)) { Write-Fail "venv creation failed at $Venv" } + +& $VenvPy -m pip install --upgrade pip -q +$Req = Join-Path $RepoRoot "requirements-wsl.txt" # CPU / pure-Python overlay - right for native Windows too +if (-not (Test-Path $Req)) { Write-Fail "requirements file not found: $Req" } +& $VenvPy -m pip install -r $Req +if ($LASTEXITCODE -ne 0) { Write-Fail "pip install failed (exit $LASTEXITCODE) - see output above" } +Write-OK "Python environment ready" + +# -- Frontend build ------------------------------------------------------------ +Write-Step "Building the web UI" +Push-Location (Join-Path $RepoRoot "interface\web") +npm install +if ($LASTEXITCODE -ne 0) { Pop-Location; Write-Fail "npm install failed (exit $LASTEXITCODE) - see output above" } +npm run build +if ($LASTEXITCODE -ne 0) { Pop-Location; Write-Fail "npm run build failed (exit $LASTEXITCODE) - no UI would be served" } +Pop-Location +if (-not (Test-Path (Join-Path $RepoRoot "interface\web\dist\index.html"))) { + Write-Fail "build reported success but interface\web\dist\index.html is missing" +} +Write-OK "Web UI built (interface\web\dist)" + +# -- Pull models (best-effort) ------------------------------------------------- +# Which models ship is decided in ONE place - DEFAULT_CHAT_MODEL and +# DEFAULT_MEMORY_MODEL in synapse\nexus_config.py (rationale documented there). +# Read them instead of hardcoding, so the installer can never pull one model +# while the backend defaults to another. +Push-Location $RepoRoot +$ChatModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_CHAT_MODEL as m; print(m)") +$MemModel = (& $VenvPy -c "from synapse.nexus_config import DEFAULT_MEMORY_MODEL as m; print(m)") +Pop-Location +if ($LASTEXITCODE -ne 0 -or -not $ChatModel -or -not $MemModel) { + Write-Fail "Could not read the default models from synapse\nexus_config.py - the venv install is broken" +} + +Write-Step "Pulling models ($ChatModel for chat, $MemModel for memory)" +Write-Host " Downloads a few GB; press Ctrl+C to skip and pull them later from the Models tab." -ForegroundColor DarkGray +try { + ollama pull $ChatModel | Out-Host + Write-OK "$ChatModel ready (default chat model)" +} catch { + Write-Warn "$ChatModel pull skipped/failed - pull it from the Models tab later." +} +try { + ollama pull $MemModel | Out-Host + Write-OK "$MemModel ready (memory curator)" +} catch { + Write-Warn "$MemModel pull skipped/failed - the memory service will fall back to the chat model." +} + +# Pin it as the default chat model. Runs from the repo root so the synapse +# package imports; only writes the 'model' setting in the shared DB. +Write-Step "Setting $ChatModel as the default model" +Push-Location $RepoRoot +& $VenvPy -c "from synapse.memory.store import store; from synapse.nexus_config import DEFAULT_CHAT_MODEL; store.update_settings({'model': DEFAULT_CHAT_MODEL})" +$seedOk = ($LASTEXITCODE -eq 0) +Pop-Location +if ($seedOk) { Write-OK "Default model set to $ChatModel" } +else { Write-Warn "Could not persist default model - pick it at the top of the chat instead." } + +# -- Make Ollama manual-start (NexusOS owns the lifecycle) ---------------------- +Write-Step "Setting Ollama to manual start" +# The Ollama desktop app autostarts a server at every login, and the elevated +# 'ollama pull' above leaves an elevated server the user-level app cannot stop - +# which makes the Start/Stop AI button get stuck. Remove the login autostart and +# stop the running server so NexusOS controls Ollama via its Start AI button. +try { + $ollamaAutostart = Join-Path ([Environment]::GetFolderPath("Startup")) "Ollama.lnk" + if (Test-Path $ollamaAutostart) { + Remove-Item $ollamaAutostart -Force + Write-OK "Removed Ollama login autostart" + } + foreach ($img in @("ollama app.exe", "ollama.exe")) { + taskkill /F /T /IM $img 2>$null | Out-Null + } + Write-OK "Ollama set to manual start" +} catch { + Write-Warn "Could not adjust Ollama autostart - you can still Start/Stop AI from the app." +} + +# -- Desktop shortcut ---------------------------------------------------------- +Write-Step "Creating desktop shortcut" +try { + $Launcher = Join-Path $RepoRoot "launch_nexus.ps1" + $LnkPath = Join-Path ([Environment]::GetFolderPath("Desktop")) "NexusOS.lnk" + $ws = New-Object -ComObject WScript.Shell + $lnk = $ws.CreateShortcut($LnkPath) + $lnk.TargetPath = "powershell.exe" + $lnk.Arguments = "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$Launcher`"" + $lnk.WorkingDirectory = $RepoRoot + $lnk.Description = "Launch NexusOS" + $Ico = Join-Path $RepoRoot "assets\NexusOS.ico" + if (Test-Path $Ico) { $lnk.IconLocation = "$Ico,0" } + $lnk.Save() + Write-OK "Desktop shortcut created" +} catch { + Write-Warn "Could not create desktop shortcut - launch with: powershell -File launch_nexus.ps1" +} + +# -- Done ---------------------------------------------------------------------- +Write-Host "" +Write-Host " ==========================================================" -ForegroundColor Green +Write-Host " NexusOS is installed!" -ForegroundColor Green +Write-Host "" +Write-Host " Launch: double-click the NexusOS shortcut on your desktop" -ForegroundColor White +Write-Host " (or run powershell -File launch_nexus.ps1)" -ForegroundColor White +Write-Host "" +Write-Host " The app opens at http://localhost:8000" -ForegroundColor White +Write-Host " The AI starts OFF - click 'Start AI' in the sidebar to turn it on." -ForegroundColor White +Write-Host " ==========================================================" -ForegroundColor Green +Write-Host "" +Read-Host "Press Enter to close" diff --git a/interface/web/.gitignore b/interface/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/interface/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/interface/web/.nvmrc b/interface/web/.nvmrc new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/interface/web/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/interface/web/README.md b/interface/web/README.md new file mode 100644 index 0000000..a36934d --- /dev/null +++ b/interface/web/README.md @@ -0,0 +1,16 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/interface/web/eslint.config.js b/interface/web/eslint.config.js new file mode 100644 index 0000000..4fa125d --- /dev/null +++ b/interface/web/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/interface/web/index.html b/interface/web/index.html new file mode 100644 index 0000000..6c697db --- /dev/null +++ b/interface/web/index.html @@ -0,0 +1,13 @@ + + + + + + + NexusOS + + +
+ + + diff --git a/interface/web/package-lock.json b/interface/web/package-lock.json new file mode 100644 index 0000000..cecd34e --- /dev/null +++ b/interface/web/package-lock.json @@ -0,0 +1,2623 @@ +{ + "name": "web", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "1.0.0", + "dependencies": { + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "vite": "^8.0.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.0.tgz", + "integrity": "sha512-oCu2wfipvX3AePSgmOuKkIywOu+8n9psz7hXYmk56ghpu3+7KzNIBopaOs4c9BrtdnTtW30unG9GTfHo7EwERQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.395", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz", + "integrity": "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", + "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.21.tgz", + "integrity": "sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/interface/web/package.json b/interface/web/package.json new file mode 100644 index 0000000..67cf7f4 --- /dev/null +++ b/interface/web/package.json @@ -0,0 +1,27 @@ +{ + "name": "web", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "vite": "^8.0.4" + } +} diff --git a/interface/web/public/favicon.svg b/interface/web/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/interface/web/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/interface/web/public/icons.svg b/interface/web/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/interface/web/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/interface/web/public/n small.png b/interface/web/public/n small.png new file mode 100644 index 0000000..c96bb05 Binary files /dev/null and b/interface/web/public/n small.png differ diff --git a/interface/web/src/App.css b/interface/web/src/App.css new file mode 100644 index 0000000..f90339d --- /dev/null +++ b/interface/web/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/interface/web/src/App.jsx b/interface/web/src/App.jsx new file mode 100644 index 0000000..f96821d --- /dev/null +++ b/interface/web/src/App.jsx @@ -0,0 +1,444 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { Chatbot } from "./Chatbot"; +import { Playbook } from "./Playbook"; +import { Models } from "./Models"; +import { Settings } from "./Settings"; +import { Memory } from "./Memory"; +import { Logs } from "./Logs"; + +import { API_BASE } from "./config"; + +function timeAgo(epochSeconds) { + const diff = Math.floor(Date.now() / 1000) - epochSeconds; + if (diff < 60) return "just now"; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`; + return new Date(epochSeconds * 1000).toLocaleDateString(); +} + +function App() { + const [currentPage, setCurrentPage] = useState("chatbot"); + const [status, setStatus] = useState("Checking Synapse..."); + const [version, setVersion] = useState(""); + const [ollamaStatus, setOllamaStatus] = useState("checking"); + const [ollamaBusy, setOllamaBusy] = useState(false); + const [showStatusTooltip, setShowStatusTooltip] = useState(false); + + const [activeConversationId, setActiveConversationId] = useState(() => crypto.randomUUID()); + const [isModelPulling, setIsModelPulling] = useState(false); + const [conversations, setConversations] = useState([]); + const [search, setSearch] = useState(""); + const [hoveredConvId, setHoveredConvId] = useState(null); + const searchDebounce = useRef(null); + + const loadStatus = () => { + fetch(`${API_BASE}/status`) + .then(r => r.json()) + .then(d => { + setStatus(d.status ?? "Unknown"); + setVersion(d.version ?? ""); + setOllamaStatus(d.ollama ?? "unavailable"); + }) + .catch(() => { + setStatus("Offline"); + setOllamaStatus("unavailable"); + }); + }; + + // Manual AI control — Ollama does not auto-start with the app. + const toggleOllama = async () => { + const action = ollamaStatus === "running" ? "stop" : "start"; + setOllamaBusy(true); + try { + await fetch(`${API_BASE}/ollama/${action}`, { method: "POST" }); + } catch (e) { + console.error("Ollama toggle failed:", e); + } + setOllamaBusy(false); + loadStatus(); + }; + + const loadConversations = useCallback(async (q = "") => { + try { + const url = q.trim() + ? `${API_BASE}/conversations?q=${encodeURIComponent(q.trim())}` + : `${API_BASE}/conversations`; + const response = await fetch(url); + if (response.ok) { + const data = await response.json(); + setConversations(data.conversations || []); + } + } catch (error) { + console.error("Failed to load conversations:", error); + } + }, []); + + useEffect(() => { + loadStatus(); + const interval = setInterval(loadStatus, 5000); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + let cancelled = false; + fetch(`${API_BASE}/conversations`) + .then(r => r.ok ? r.json() : null) + .then(data => { + if (cancelled || !data) return; + setConversations(data.conversations || []); + }) + .catch(err => console.error("Failed to load conversations:", err)); + return () => { cancelled = true; }; + }, []); + + const handleSearch = (e) => { + const q = e.target.value; + setSearch(q); + clearTimeout(searchDebounce.current); + searchDebounce.current = setTimeout(() => loadConversations(q), 300); + }; + + const selectConversation = (id) => { + setActiveConversationId(id); + setCurrentPage("chatbot"); + }; + + const startNewChat = () => { + setActiveConversationId(crypto.randomUUID()); + setCurrentPage("chatbot"); + }; + + const renameConversation = async (e, conv) => { + e.stopPropagation(); + const current = conv.title || conv.preview || ""; + const next = window.prompt("Rename conversation:", current); + if (next == null) return; + const title = next.trim(); + if (!title || title === current) return; + try { + const response = await fetch(`${API_BASE}/conversations/${conv.id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title }), + }); + if (response.ok) loadConversations(search); + } catch (error) { + console.error("Failed to rename conversation:", error); + } + }; + + const deleteConversation = async (e, conversationId) => { + e.stopPropagation(); + if (!window.confirm("Delete this conversation?")) return; + try { + const response = await fetch(`${API_BASE}/conversations/${conversationId}`, { method: "DELETE" }); + if (response.ok) { + if (activeConversationId === conversationId) { + setActiveConversationId(crypto.randomUUID()); + } + loadConversations(search); + } + } catch (error) { + console.error("Failed to delete conversation:", error); + } + }; + + const navItems = [ + { key: "chatbot", label: "💬 Chat" }, + { key: "playbook", label: "📖 Playbooks" }, + { key: "models", label: "🤖 Models", badge: isModelPulling }, + { key: "memory", label: "🧠 Memory" }, + { key: "logs", label: "📜 Logs" }, + { key: "settings", label: "⚙️ Settings" }, + ]; + + return ( +
+ {/* Sidebar */} + + + {/* Main Content */} +
+ {/* Models stays mounted so active downloads survive page navigation */} +
+ +
+ {currentPage === "chatbot" && ( + loadConversations(search)} + /> + )} + {currentPage === "playbook" && } + {currentPage === "memory" && } + {currentPage === "logs" && } + {currentPage === "settings" && } +
+
+ ); +} + +export default App; diff --git a/interface/web/src/Chatbot.jsx b/interface/web/src/Chatbot.jsx new file mode 100644 index 0000000..1477b84 --- /dev/null +++ b/interface/web/src/Chatbot.jsx @@ -0,0 +1,481 @@ +import { useState, useRef, useEffect } from "react"; + +import { API_BASE } from "./config"; +import { Markdown } from "./Markdown"; + +export function Chatbot({ conversationId, setConversationId, onConversationChanged }) { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(""); + const [loading, setLoading] = useState(false); + const [modelList, setModelList] = useState([]); + const [selectedModel, setSelectedModel] = useState(""); // "" = auto + const [autoModel, setAutoModel] = useState(null); + const [showPicker, setShowPicker] = useState(false); + const [copiedIdx, setCopiedIdx] = useState(null); + const [lastStats, setLastStats] = useState(null); + const [memoryToast, setMemoryToast] = useState(null); + + const abortRef = useRef(null); + const messagesEndRef = useRef(null); + const pickerRef = useRef(null); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); + + useEffect(() => { + if (!conversationId) return; + if (abortRef.current) abortRef.current.abort(); + let cancelled = false; + setLastStats(null); + fetch(`${API_BASE}/conversations/${conversationId}`) + .then(r => r.ok ? r.json() : null) + .then(data => { + if (cancelled) return; + setMessages(data?.messages?.map(m => ({ role: m.role, content: m.content })) || []); + }) + .catch(() => { if (!cancelled) setMessages([]); }); + return () => { cancelled = true; }; + }, [conversationId]); + + useEffect(() => { + Promise.all([ + fetch(`${API_BASE}/models`).then(r => r.ok ? r.json() : null), + fetch(`${API_BASE}/settings`).then(r => r.ok ? r.json() : null), + ]).then(([models, settings]) => { + if (models?.models) setModelList(models.models); + if (models?.selected) setAutoModel(models.selected); + if (settings) setSelectedModel(settings.model || ""); + }).catch(() => {}); + }, []); + + useEffect(() => { + if (!showPicker) return; + const handle = (e) => { + if (pickerRef.current && !pickerRef.current.contains(e.target)) setShowPicker(false); + }; + document.addEventListener("mousedown", handle); + return () => document.removeEventListener("mousedown", handle); + }, [showPicker]); + + const setModelChoice = async (model) => { + setSelectedModel(model); + setShowPicker(false); + try { + await fetch(`${API_BASE}/settings`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ model }), + }); + } catch { /* persisting the model choice is best-effort */ } + }; + + const startNewChat = () => { + if (abortRef.current) abortRef.current.abort(); + setInput(""); + setLoading(false); + setLastStats(null); + setConversationId(crypto.randomUUID()); + }; + + const sendMessage = async () => { + if (!input.trim() || loading) return; + + const userMessage = input.trim(); + setInput(""); + + // Add user message + setMessages(prev => [...prev, { role: "user", content: userMessage }]); + + // Prepare assistant placeholder + const assistantIndex = messages.length + 1; + setMessages(prev => [...prev, { role: "assistant", content: "" }]); + + setLoading(true); + + // Abort previous stream if still open + if (abortRef.current) abortRef.current.abort(); + const controller = new AbortController(); + abortRef.current = controller; + + try { + // Exclude the empty assistant placeholder that was just appended + const historySnapshot = messages.filter(m => m.content.trim() !== ""); + + const response = await fetch(`${API_BASE}/chat/stream`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + message: userMessage, + conversation_id: conversationId, + history: historySnapshot.map(m => ({ role: m.role, content: m.content })), + }), + signal: controller.signal, + }); + + if (!response.ok) { + const text = await response.text(); + updateAssistant(assistantIndex, `Error: ${text}`); + setLoading(false); + return; + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + let buffer = ""; + let pendingEventType = null; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + + const lines = buffer.split("\n"); + buffer = lines.pop(); + + for (const line of lines) { + if (line.startsWith("event: ")) { + pendingEventType = line.slice(7).trim(); + continue; + } + if (line.startsWith("data: ")) { + const payload = line.slice(6); + if (!payload.trim()) { pendingEventType = null; continue; } + + if (pendingEventType === "meta") { + try { + const stats = JSON.parse(payload); + setLastStats(stats); + // Tag this message with the model that answered + if (stats.model) { + setMessages(prev => { + const updated = [...prev]; + updated[assistantIndex] = { ...updated[assistantIndex], model: stats.model }; + return updated; + }); + } + } catch { /* ignore */ } + pendingEventType = null; + continue; + } + if (pendingEventType === "memory") { + try { + const mem = JSON.parse(payload); + setMemoryToast(mem); + setTimeout(() => setMemoryToast(null), 5000); + } catch { /* ignore */ } + pendingEventType = null; + continue; + } + if (pendingEventType === "done") { + // Answer is complete; re-enable input while the backend finishes + // slow post-processing (title, memory) on the still-open stream. + setLoading(false); + pendingEventType = null; + continue; + } + if (pendingEventType === "title") { + if (onConversationChanged) onConversationChanged(); + pendingEventType = null; + continue; + } + if (pendingEventType === "error") { + try { + const err = JSON.parse(payload); + updateAssistant(assistantIndex, `Error: ${err.detail || payload}`); + } catch { + updateAssistant(assistantIndex, `Error: ${payload}`); + } + pendingEventType = null; + setLoading(false); + return; + } + pendingEventType = null; + + let token = payload; + try { token = JSON.parse(payload); } catch { /* plain text fallback */ } + + setMessages(prev => { + const updated = [...prev]; + updated[assistantIndex] = { + ...updated[assistantIndex], + content: (updated[assistantIndex].content || "") + token, + }; + return updated; + }); + } + } + } + + } catch (err) { + if (err.name !== "AbortError") { + updateAssistant(assistantIndex, `Connection error: ${err.message}`); + } + } finally { + setLoading(false); + if (onConversationChanged) onConversationChanged(); + } + }; + + const updateAssistant = (index, text) => { + setMessages(prev => { + const updated = [...prev]; + updated[index] = { ...updated[index], content: text }; + return updated; + }); + }; + + const handleKeyDown = (e) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } + }; + + return ( +
+ {memoryToast && ( +
+ 🧠 Memory saved [{memoryToast.section}]
+ {memoryToast.text} +
+ )} +
+ {/* Header */} +
+
+ + {messages.length === 0 ? "New conversation" : `${Math.ceil(messages.length / 2)} exchange${messages.length > 2 ? "s" : ""}`} + +
+ + {showPicker && ( +
+
setModelChoice("")} + style={{ + padding: "0.55rem 0.85rem", + cursor: "pointer", + fontSize: "0.8rem", + color: !selectedModel ? "#7aa" : "#888", + background: !selectedModel ? "#1a2a2a" : "transparent", + borderBottom: "1px solid #262626", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }} + > + Auto {autoModel ? `(${autoModel})` : ""} + {!selectedModel && } +
+ {modelList.map(m => ( +
setModelChoice(m)} + style={{ + padding: "0.55rem 0.85rem", + cursor: "pointer", + fontSize: "0.8rem", + color: selectedModel === m ? "#7aa" : "#ccc", + background: selectedModel === m ? "#1a2a2a" : "transparent", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + }} + > + {m} + {selectedModel === m && } +
+ ))} +
+ )} +
+ {lastStats && ( + + {lastStats.tokens} tok · {lastStats.elapsed_s}s + {lastStats.tokens_per_s > 0 ? ` · ${lastStats.tokens_per_s} t/s` : ""} + + )} +
+ +
+ +
+ {messages.length === 0 ? ( +
+

Start a conversation with the chatbot...

+
+ ) : ( + messages.map((msg, idx) => ( +
+
+ {msg.role === "user" + ? {msg.content} + : msg.content + ? + : Thinking... + } +
+ {msg.content && ( + + )} +
+ )) + )} + +
+
+ +
+