diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfdb8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf diff --git a/CLAUDE.md b/CLAUDE.md index cf87477..d917432 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,20 +10,32 @@ NexusOS is a local AI assistant platform. It runs a Python/FastAPI backend (Syna 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. +at runtime. Ollama is not started on backend process startup itself, but `ncp +start`/`start -b` bring it up right after via the `/ollama/start` endpoint; the +sidebar **Start/Stop AI** button and `ncp start --ai` remain for toggling it +independently once the backend is already up. **Windows (recommended):** ```powershell powershell -ExecutionPolicy Bypass -File .\install-windows.ps1 # one-time native install -ncp web # memory :8001 + backend :8000 + UI +ncp web # backend :8000 + UI +``` +`ncp` comes from `management\ncp.cmd`, which the installer puts on the machine +PATH — open a NEW shell after installing. `.\launch_nexus.ps1` is what the +desktop shortcut runs and still works directly. + +**Linux install / update:** +```bash +./install.sh # wrapper over `python3 bin/sync.py restore` +./install.sh --check # dry run +./install.sh --no-desktop # skip the XFCE wiring (test clones, non-XFCE boxes) ``` **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. +This activates the `Promethean` venv and starts 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 @@ -31,10 +43,7 @@ This activates the `Promethean` venv and starts the memory service on port 8001 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 +uvicorn synapse.main:sio_app --host 127.0.0.1 --port 8000 --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/. @@ -80,13 +89,15 @@ 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-windows.txt` (CPU-only). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, or CPU/Windows requirements from the host. +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 (nothing in it needs a GPU or imports torch), and a thin overlay per platform sets the right PyTorch package index — `requirements-amd.txt` (ROCm), `requirements-nvidia.txt` (CUDA, generated by `bin/gen-nvidia-reqs.py`), or `requirements-windows.txt` (CPU-only, standalone). `bin/sync.py` (`requirements()`) selects NVIDIA, AMD, or CPU/Windows requirements from the host and installs that alone by default — fast, no multi-GB downloads. + +`requirements-ml.txt` is a separate, **opt-in** overlay for local ML inference (transformers/accelerate/bitsandbytes + torch/torchaudio/torchvision) — nothing in `synapse/` imports any of it; Ollama does all inference over HTTP. Only pull it in for local model work outside Ollama: `pip install -r requirements-amd.txt -r requirements-ml.txt` (or `-nvidia`, or alone for CPU-only torch). Not installed by `bin/sync.py`/the installers. ### 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. +- `/chat/stream` — chat with Ollama; streaming uses SSE (the only chat endpoint — the non-stream `/chat` was removed). When a conversation goes idle the stream endpoint hands it to the in-process curator, which extracts 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). +- `/memory` — CRUD for persistent facts, backed by the same SQLite store the curator writes to. - `/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. @@ -94,8 +105,8 @@ FastAPI app at `synapse/main.py`. Key responsibilities: **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`). +### Memory (`synapse/memory/`) +Runs **in-process** — there is no separate service and no second model. `curator.py` reads the messages a conversation has added since its watermark, `extractor.py` asks the chat model which permanent facts they contain, and `store.py` merges the results into `memory.db`. The backend schedules it when a conversation goes idle (`_pending_extractions` in `main.py`), so a half-said fact is never persisted mid-thought. The old :8001 FastAPI app held a second, smaller model that could not share the GPU with the chat model; the chat model is already resident, so the extra hop bought nothing. ### 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. @@ -126,3 +137,5 @@ Most data lands in `synapse/memory/memory.db` (SQLite, WAL mode). Tables: memory | Python dependencies (AMD/ROCm) | `requirements-amd.txt` | | Python dependencies (NVIDIA/CUDA) | `requirements-nvidia.txt` (generated by `bin/gen-nvidia-reqs.py`) | | Python dependencies (Windows/CPU) | `requirements-windows.txt` | +| Python dependencies (optional local ML/torch) | `requirements-ml.txt` (opt-in, not installed by default) | + diff --git a/README.md b/README.md index a52fdea..3a674bf 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ # 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. +Python/FastAPI backend, a bundled Ollama instance for inference, persistent +memory, and a React frontend. No external AI provider is called. @@ -99,7 +99,7 @@ Linux distro, but I only support the Ubuntu/Linux Mint package base. # system packages, Ollama binary and the XFCE desktop wiring. ./install.sh -# 2. Launch (memory :8001, backend :8000 — backend also serves the built UI) +# 2. Launch (backend :8000 — it also serves the built UI) # The install symlinks ncp into /usr/local/bin (sudo); open a new shell first. ncp web ``` @@ -151,8 +151,7 @@ does all inference over HTTP). # Linux 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 +uvicorn synapse.main:sio_app --host 127.0.0.1 --port 8000 --reload # backend (serves the UI too) # Frontend dev server (hot-reload) — only needed when editing the UI; # production serves the built dist/ from the backend at :8000. @@ -163,8 +162,7 @@ cd interface/web && npm run dev # Windows — needs the RemoteSigned policy from the Quick start step above Promethean\Scripts\Activate.ps1 -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 +uvicorn synapse.main:sio_app --host 127.0.0.1 --port 8000 --reload # backend (serves the UI too) ``` ### Promethean @@ -216,7 +214,7 @@ ncp help # see complete help tree |---|---|---| | **Promethean** (venv) | `Promethean/` | The Python venv all backend code runs in — `source Promethean/bin/activate` (Linux) / `Promethean\Scripts\python.exe` (Windows). Keeps deps out of the system Python. | | **Synapse** (backend) | `synapse/` | FastAPI app: `/chat/stream` (+ `/chat/approve` for gated tool calls), `/playbooks`, `/memory`, `/models`, `/documents`, `/projects`, `/conversations`, `/stt`, `/logs`, `/settings`, `/ollama`, `/frontend`, `/icons`. Assembles the system prompt: active playbook → reference playbooks → memory facts → relevant past snippets → matching documents → web search results. | -| **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. | +| **Memory** | `synapse/memory/` | In-process, no second service or model: `curator.py` reads a conversation once it goes idle, `extractor.py` asks the chat model what is worth keeping, `store.py` merges it into the SQLite DB. | | **Documents / RAG** | `synapse/memory/store.py` | PDF/DOCX/TXT/MD ingest, chunked and embedded, retrieved via a sqlite-vec index; scoped per **Project** workspace. | | **Action tools** | `synapse/tools.py`, `synapse/search.py` | Read-only tools (search memory/history/documents, list models, get time) run automatically; `web_search`, `fetch_url`, and `remember` require per-call approval from the chat UI. | | **Playbooks** | `synapse/playbooks/` + `data/playbooks/` | Ordered `{id}.yaml` records managed by `PlaybookManager`; each can pin a chat model and a tool list. | @@ -233,7 +231,7 @@ are the exception (YAML files in `data/playbooks/`). All paths are defined in ## Layout -- `synapse/` — FastAPI backend + memory service + playbook/ollama managers +- `synapse/` — FastAPI backend + memory curator + playbook/ollama managers - `modules/` — auto-discovered feature plugins - `interface/web/` — React + Vite frontend - `management/` — nexus-cli.sh, ncp API client, control panel, desktop theme diff --git a/VERSION b/VERSION index 3eefcb9..26aaba0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 +1.2.0 diff --git a/assets/themes/KDE/aurorae/NexusOS/metadata.desktop b/assets/themes/KDE/aurorae/NexusOS/metadata.desktop index 92ed21f..8bbeb3b 100644 --- a/assets/themes/KDE/aurorae/NexusOS/metadata.desktop +++ b/assets/themes/KDE/aurorae/NexusOS/metadata.desktop @@ -7,7 +7,7 @@ X-KDE-ServiceTypes=org.kde.kwin.decoration [KWin] Type=Aurorae -X-KDE-PluginInfo-Author=Jon +X-KDE-PluginInfo-Author=NexusOS X-KDE-PluginInfo-Email= X-KDE-PluginInfo-Name=NexusOS X-KDE-PluginInfo-Version=1.0 diff --git a/assets/themes/KDE/install-plasma.sh b/assets/themes/KDE/install-plasma.sh index 454a959..d1b8837 100644 --- a/assets/themes/KDE/install-plasma.sh +++ b/assets/themes/KDE/install-plasma.sh @@ -12,8 +12,10 @@ KDE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO="$(cd "$KDE/../../.." && pwd)" NO_SDDM=0 +FORCE_PANEL=0 for arg in "$@"; do [[ "$arg" == "--no-sddm" ]] && NO_SDDM=1 + [[ "$arg" == "--panel" ]] && FORCE_PANEL=1 done echo "NexusOS KDE installer — repo: $REPO" @@ -24,13 +26,11 @@ mkdir -p \ ~/.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" @@ -80,10 +80,25 @@ else echo " [skip] plasma-apply-desktoptheme not found — apply desktop theme manually" fi -# ── 8. kscreenlocker ───────────────────────────────────────────────────── +# ── 8. Lock screen ─────────────────────────────────────────────────────── +# Plasma 5.27 draws the lock screen from a look-and-feel package's +# contents/lockscreen, and [Greeter]Theme names that PACKAGE. The old value here +# ("NexusOS") was not a look-and-feel id, so it silently fell back to Breeze. +# The standalone kscreenlocker theme that value referred to targeted an earlier +# kscreenlocker API that 5.27 no longer loads, and has been deleted. +# +# Breeze's lock UI is deliberately kept rather than replaced: a lock screen that +# fails to load is one you cannot get back through. Only the wallpaper is ours, +# which is what carries the brushed-metal look. if command -v kwriteconfig5 &>/dev/null; then - kwriteconfig5 --file kscreenlockerrc --group Greeter --key Theme NexusOS - echo " [ok] kscreenlocker theme: NexusOS" + kwriteconfig5 --file kscreenlockerrc --group Greeter --key Theme org.kde.breeze.desktop + LOCK_BG="$KDE/sddm/NexusOS-QML/assets/background.png" + if [[ -f "$LOCK_BG" ]]; then + kwriteconfig5 --file kscreenlockerrc \ + --group Greeter --group Wallpaper --group org.kde.image --group General \ + --key Image "$LOCK_BG" + echo " [ok] lock screen wallpaper: NexusOS brushed metal" + fi fi # ── 9. GTK apps under Plasma ───────────────────────────────────────────── @@ -106,7 +121,7 @@ if [[ $NO_SDDM -eq 0 ]]; then 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 + sudo kwriteconfig5 --file /etc/sddm.conf.d/nexusos.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' @@ -125,14 +140,158 @@ if [[ -f "$DESKTOP_SRC" ]]; then echo " [ok] Promethean Terminal launcher updated" fi +# ── 11b. Global Theme + wallpaper ──────────────────────────────────────── +# Copied, NOT symlinked, unlike the theme dirs above: KPackage silently skips a +# symlinked package directory, so a symlinked look-and-feel never appears in +# `lookandfeeltool -l` or System Settings at all. Aurorae/desktoptheme/Kvantum +# read their directories directly and are fine as symlinks. +LNF_SRC="$KDE/look-and-feel/com.nexusos.desktop" +WALL_SRC="$KDE/wallpaper/NexusOS" +if [[ -d "$LNF_SRC" ]]; then + mkdir -p ~/.local/share/plasma/look-and-feel ~/.local/share/wallpapers + rm -rf ~/.local/share/plasma/look-and-feel/com.nexusos.desktop + cp -rL "$LNF_SRC" ~/.local/share/plasma/look-and-feel/ + rm -rf ~/.local/share/wallpapers/NexusOS + cp -rL "$WALL_SRC" ~/.local/share/wallpapers/ + echo " [ok] Global Theme + wallpaper packages installed" + + if command -v lookandfeeltool &>/dev/null; then + # Verify registration before applying. KPackage reports nothing when it + # rejects a package, so without this check a botched install is silent and + # you only find out by opening System Settings. + if lookandfeeltool -l 2>/dev/null | grep -qx "com.nexusos.desktop"; then + lookandfeeltool -a com.nexusos.desktop 2>/dev/null \ + && echo " [ok] Global Theme applied (splash, colors, decoration)" + else + echo " [WARN] com.nexusos.desktop did not register with KPackage." + echo " Most likely it was symlinked instead of copied -- KPackage" + echo " skips symlinked package directories silently." + fi + fi + if command -v plasma-apply-wallpaperimage &>/dev/null; then + plasma-apply-wallpaperimage ~/.local/share/wallpapers/NexusOS &>/dev/null \ + && echo " [ok] wallpaper applied" + fi +fi + +# ── 11c. Panel layout + dock ───────────────────────────────────────────── +# The XFCE panel rebuilt with native Plasma widgets, plus Plank for the dock +# (its config and NexusOS theme already exist under assets/themes/restore-snapshot). +# +# Applied ONCE, guarded by a marker file. This rewrites the panel from scratch, +# so re-running it on every restore would wipe any widget you added since. Pass +# --panel to force it, e.g. after a fresh install or to undo panel experiments. +PANEL_MARKER="$HOME/.config/nexusos-panel-applied" +PANEL_JS="$KDE/panel-layout.js" +if [[ -f "$PANEL_JS" ]] && { [[ ! -f "$PANEL_MARKER" ]] || [[ $FORCE_PANEL -eq 1 ]]; }; then + if qdbus org.kde.plasmashell /PlasmaShell evaluateScript \ + "$(sed "s|__NEXUS_ROOT__|$REPO|g" "$PANEL_JS")" &>/dev/null; then + touch "$PANEL_MARKER" + echo " [ok] panel layout applied (top, 36px, Nexus launcher)" + else + echo " [skip] panel layout — plasmashell not running" + fi +elif [[ -f "$PANEL_MARKER" ]]; then + echo " [ok] panel layout already applied (--panel to redo)" +fi + +# Plank autostart. Plank is a GTK X11 app and runs fine under Plasma; this is +# the same dock, config and theme the XFCE session used. +if command -v plank &>/dev/null; then + mkdir -p ~/.config/autostart + # Symlink, matching bin/panel/install.sh. cp fails outright when the target is + # already a symlink back to this same file ("are the same file"), which made + # the step look like it had silently done nothing. + ln -sfn "$REPO/management/autostart/plank.desktop" ~/.config/autostart/plank.desktop \ + && echo " [ok] Plank autostart installed" + pgrep -x plank &>/dev/null || { setsid plank /dev/null & disown; } +fi + +# ── 11d. Custom shortcut: region screenshot to clipboard ───────────────── +# Meta+Shift+S -> `xfce4-screenshooter -rc`, the equivalent of the XFCE binding. +# Spectacle is not installed by kde-plasma-desktop, and xfce4-screenshooter +# works fine under Plasma, so this keeps the tool that is already here. +# +# Written as ONE atomic replace rather than a series of kwriteconfig5 calls, +# because khotkeys watches khotkeysrc and rewrites it from its own model on +# every change. +# +# The UUID below is FIXED, not generated. kglobalaccel records the key binding +# in kglobalshortcutsrc against the action's UUID, so a fresh UUID on each run +# cannot take a key that the previous run's UUID already holds: the entry gets +# registered with no shortcut at all, and every re-run leaves another dead +# registration behind. A stable UUID means a re-run re-claims its own binding. +if ! grep -q "^Key=Meta+Shift+S$" ~/.config/khotkeysrc 2>/dev/null; then + python3 - <<'PYEOF' +import pathlib, re + +# Fixed so re-runs reclaim the same kglobalaccel registration. +SHORTCUT_UUID = "87a4ba1e-4f01-4a79-9cd1-b73c17d459ea" +p = pathlib.Path.home() / ".config" / "khotkeysrc" +s = p.read_text() if p.exists() else "[Data]\nDataCount=0\n" + +# Drop any half-written entry from an earlier failed attempt. +for m in re.findall(r"\[Data_(\d+)\]\n(?:[^\[]*)", s): + blk = re.search(r"\[Data_%s\]\n((?:[^\[]*))" % m, s) + if blk and "Screenshot region to clipboard" in blk.group(1): + s = re.sub(r"\n\[Data_%s[^\]]*\]\n(?:[^\[]*)" % m, "\n", s) + +count = int(re.search(r"\[Data\]\nDataCount=(\d+)", s).group(1)) +n = count + 1 +s = re.sub(r"(\[Data\]\nDataCount=)\d+", r"\g<1>%d" % n, s, count=1) +s = s.rstrip("\n") + """ + +[Data_%d] +Comment=Select a region and copy it straight to the clipboard +Enabled=true +Name=Screenshot region to clipboard +Type=SIMPLE_ACTION_DATA + +[Data_%dActions] +ActionsCount=1 + +[Data_%dActions0] +CommandURL=xfce4-screenshooter -rc +Type=COMMAND_URL + +[Data_%dConditions] +Comment= +ConditionsCount=0 + +[Data_%dTriggers] +Comment=Simple_action +TriggersCount=1 + +[Data_%dTriggers0] +Key=Meta+Shift+S +Type=SHORTCUT +Uuid={%s} +""" % (n, n, n, n, n, n, SHORTCUT_UUID) + +tmp = p.with_suffix(".nexustmp") +tmp.write_text(s) +tmp.replace(p) +PYEOF + qdbus org.kde.kded5 /modules/khotkeys reread_configuration &>/dev/null \ + && echo " [ok] Meta+Shift+S -> region screenshot to clipboard" +else + echo " [ok] screenshot shortcut already bound" +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 + # Restart plasmashell to pick up new shell theme. + # setsid + closed stdio is load-bearing, not tidiness: the restarted shell + # outlives this script, and if it inherits our stdout it holds the write end + # of the caller's pipe open forever. `ncp restore` captures output, so a + # plain `kstart5 plasmashell &` hangs the entire restore after the theme is + # already applied -- which looks like a failure and isn't one. if command -v kquitapp5 &>/dev/null && command -v kstart5 &>/dev/null; then kquitapp5 plasmashell 2>/dev/null sleep 1 - kstart5 plasmashell & + setsid kstart5 plasmashell /dev/null 2>&1 & + disown 2>/dev/null || true echo " [ok] plasmashell restarted" fi fi diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg b/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg deleted file mode 100644 index 395f6f9..0000000 --- a/assets/themes/KDE/kscreenlocker/NexusOS/assets/background.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml b/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml deleted file mode 100644 index 6747792..0000000 --- a/assets/themes/KDE/kscreenlocker/NexusOS/contents/config/main.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml b/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml deleted file mode 100644 index 7d130a5..0000000 --- a/assets/themes/KDE/kscreenlocker/NexusOS/contents/ui/LockScreenUi.qml +++ /dev/null @@ -1,268 +0,0 @@ -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 deleted file mode 100644 index 1109447..0000000 --- a/assets/themes/KDE/kscreenlocker/NexusOS/metadata.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[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 index 8f44e00..8877867 100644 --- a/assets/themes/KDE/kvantum/NexusOS/NexusOS.kvconfig +++ b/assets/themes/KDE/kvantum/NexusOS/NexusOS.kvconfig @@ -45,6 +45,30 @@ large.icon.size=32 button.icon.size=16 toolbar.icon.size=16 +[GeneralColors] +# Palette. Without this section Kvantum falls back to its light default for +# view backgrounds, which rendered item views (e.g. the Add Bluetooth Device +# device list) as light-grey text on white. Values mirror ~/.config/kdeglobals. +window.color=#1e1526 +base.color=#1e1526 +alt.base.color=#1f2225 +button.color=#2a2e32 +light.color=#3a3d41 +mid.light.color=#2e3236 +dark.color=#140d1a +mid.color=#241a2d +highlight.color=#88008f +inactive.highlight.color=#5e0066 +text.color=#f2f2f2 +window.text.color=#f2f2f2 +button.text.color=#f2f2f2 +disabled.text.color=#6e7173 +tooltip.text.color=#f2f2f2 +highlight.text.color=#ffffff +link.color=#b8e373 +link.visited.color=#a232a8 +progress.indicator.text.color=#f2f2f2 + [PanelButtonCommand] inherits=PanelButtonTool frame=false diff --git a/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/defaults b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/defaults new file mode 100644 index 0000000..90ced66 --- /dev/null +++ b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/defaults @@ -0,0 +1,25 @@ +[kdeglobals][KDE] +widgetStyle=kvantum-dark + +[kdeglobals][General] +ColorScheme=NexusOS + +[kdeglobals][Icons] +Theme=NexusOS + +[plasmarc][Theme] +name=NexusOS + +[Wallpaper] +Image=NexusOS + +[kwinrc][org.kde.kdecoration2] +library=org.kde.kwin.aurorae +theme=__aurorae__svg__NexusOS + +[kscreenlockerrc][Greeter] +Theme=org.kde.breeze.desktop + +[KSplash] +Theme=com.nexusos.desktop +Engine=KSplashQML diff --git a/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/Splash.qml b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/Splash.qml new file mode 100644 index 0000000..29d7d93 --- /dev/null +++ b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/Splash.qml @@ -0,0 +1,95 @@ +/* + NexusOS Plasma startup splash. + + Deliberately plain QtQuick -- no PlasmaCore import. ksplashqml runs this + before the session is up, and the fewer modules it has to resolve at that + point the fewer ways it has to fail; sizes are proportional to the screen + instead of Units.gridUnit for the same reason. + + ksplashqml drives `stage` from 0 to 6 as the session comes up, which fills + the progress bar. The content is deliberately opaque from the start rather + than faded in on stage 2 the way Breeze's splash does: if that signal never + arrives -- as under `ksplashqml --test` -- a stage-gated splash shows nothing + but a blank coloured screen. Palette matches assets/themes/_palette.py. +*/ +import QtQuick 2.15 + +Rectangle { + id: root + color: "#1e1526" + + property int stage + + onStageChanged: { + if (stage == 6) { + outroAnimation.running = true; + } + } + + // Same brushed-metal ground as the Plymouth, SDDM and lock screens. Raster, + // not the source SVG: QtSvg is Tiny 1.2 and drops the textures. + Image { + anchors.fill: parent + source: "images/background.png" + fillMode: Image.PreserveAspectCrop + smooth: true + asynchronous: false + } + + Item { + id: content + anchors.fill: parent + + Image { + id: logo + anchors.horizontalCenter: parent.horizontalCenter + y: parent.height * 0.32 + source: "images/logo.png" + width: Math.round(parent.width * 0.15) + fillMode: Image.PreserveAspectFit + smooth: true + asynchronous: false + } + + Text { + id: brand + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: Math.round(parent.height * 0.03) + text: "NexusOS" + color: "#f2f2f2" + font.family: "Sans" + font.pixelSize: Math.round(parent.height * 0.026) + font.letterSpacing: 6 + font.weight: Font.Light + } + + Rectangle { + id: track + anchors.horizontalCenter: parent.horizontalCenter + y: Math.round(parent.height * 0.62) + width: Math.round(parent.width * 0.18) + height: 3 + radius: height / 2 + color: "#2e3236" + + Rectangle { + height: parent.height + radius: parent.radius + color: "#8cc63f" + width: parent.width * Math.min(root.stage / 6, 1) + Behavior on width { + NumberAnimation { duration: 250; easing.type: Easing.InOutQuad } + } + } + } + } + + OpacityAnimator { + id: outroAnimation + target: content + from: 1; to: 0 + duration: 400 + running: false + } +} diff --git a/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/images/background.png b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/images/background.png new file mode 120000 index 0000000..63391bb --- /dev/null +++ b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/images/background.png @@ -0,0 +1 @@ +../../../../../sddm/NexusOS-QML/assets/background.png \ No newline at end of file diff --git a/assets/themes/KDE/kscreenlocker/NexusOS/assets/logo.png b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/images/logo.png old mode 100644 new mode 100755 similarity index 100% rename from assets/themes/KDE/kscreenlocker/NexusOS/assets/logo.png rename to assets/themes/KDE/look-and-feel/com.nexusos.desktop/contents/splash/images/logo.png diff --git a/assets/themes/KDE/look-and-feel/com.nexusos.desktop/metadata.json b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/metadata.json new file mode 100644 index 0000000..c637d95 --- /dev/null +++ b/assets/themes/KDE/look-and-feel/com.nexusos.desktop/metadata.json @@ -0,0 +1,22 @@ +{ + "KPlugin": { + "Authors": [ + { + "Name": "NexusOS" + } + ], + "Category": "", + "Description": "Dark purple NexusOS desktop with lime-green accents", + "Id": "com.nexusos.desktop", + "License": "GPL", + "Name": "NexusOS", + "ServiceTypes": [ + "Plasma/LookAndFeel" + ], + "Version": "1.0", + "Website": "" + }, + "X-Plasma-APIVersion": "2", + "X-Plasma-MainScript": "defaults", + "KPackageStructure": "Plasma/LookAndFeel" +} diff --git a/assets/themes/KDE/panel-layout.js b/assets/themes/KDE/panel-layout.js new file mode 100644 index 0000000..c82b38b --- /dev/null +++ b/assets/themes/KDE/panel-layout.js @@ -0,0 +1,63 @@ +/* + NexusOS Plasma panel layout — the XFCE panel, rebuilt with native widgets. + + Applied through Plasma's scripting API rather than by editing + plasma-org.kde.plasma.desktop-appletsrc directly: the file is owned by a + running plasmashell, which rewrites it from memory on exit and would + clobber anything written underneath it. Run with: + + qdbus org.kde.plasmashell /PlasmaShell evaluateScript "$(cat panel-layout.js)" + + Source layout is assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml: + a 36px full-width top panel, Whisker menu at the far left with the Nexus N + as its button, everything else pushed to the right — tray, volume, + bluetooth, network, battery, clock. + + The XFCE panel carried no window list; Plank showed running apps. So this + adds no task manager either, and the dock keeps that job. + + Widget mapping from the XFCE plugins: + whiskermenu -> org.kde.plasma.kickoff (same N button icon) + separator (expand) -> org.kde.plasma.panelspacer + systray + notify -> org.kde.plasma.systemtray (notifications live in it) + genmon network -> networkmanagement, inside the tray + genmon bluetooth -> bluetooth, inside the tray + pulseaudio -> volume, inside the tray + power-manager -> battery, inside the tray + clock -> org.kde.plasma.digitalclock + genmon nexus -> not a panel widget; the dock keeps nexus-core +*/ + +var panel = panelById(panelIds[0]); +if (!panel) { + panel = new Panel; +} + +panel.location = "top"; +panel.height = 36; // xfce4-panel.xml: size=36 +panel.alignment = "left"; +panel.hiding = "none"; + +// Start from a known state so re-running is idempotent rather than additive. +var existing = panel.widgetIds; +for (var i = 0; i < existing.length; i++) { + var w = panel.widgetById(existing[i]); + if (w) { w.remove(); } +} + +var launcher = panel.addWidget("org.kde.plasma.kickoff"); +launcher.currentConfigGroup = ["General"]; +// __NEXUS_ROOT__ is substituted by install-plasma.sh so a clone in another +// directory, or another user, gets a working path rather than this box's. +launcher.writeConfig("icon", "__NEXUS_ROOT__/assets/n-small.png"); +launcher.writeConfig("favoritesPortedToKAstats", true); + +panel.addWidget("org.kde.plasma.panelspacer"); + +var tray = panel.addWidget("org.kde.plasma.systemtray"); + +var clock = panel.addWidget("org.kde.plasma.digitalclock"); +clock.currentConfigGroup = ["Appearance"]; +clock.writeConfig("showDate", true); + +panel.reloadConfig(); diff --git a/assets/themes/KDE/plasma/NexusOS/metadata.desktop b/assets/themes/KDE/plasma/NexusOS/metadata.desktop index e9d00b8..f583025 100644 --- a/assets/themes/KDE/plasma/NexusOS/metadata.desktop +++ b/assets/themes/KDE/plasma/NexusOS/metadata.desktop @@ -6,7 +6,7 @@ Type=Service [KPackageStructure] X-KDE-ServiceTypes=Plasma/Theme -X-KDE-PluginInfo-Author=Jon +X-KDE-PluginInfo-Author=NexusOS X-KDE-PluginInfo-Email= X-KDE-PluginInfo-Name=NexusOS X-KDE-PluginInfo-Version=1.0 diff --git a/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop b/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop index f4aa5a1..93ec12d 100644 --- a/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop +++ b/assets/themes/KDE/sddm/NexusOS-QML/metadata.desktop @@ -1,7 +1,7 @@ [SddmGreeterTheme] Name=NexusOS-QML Description=Dark brushed-metal lockscreen theme with flat N logo, upper-center branding, and centered login box. -Author=Jon +Author=NexusOS Version=1.0 Website= Screenshot= diff --git a/assets/themes/KDE/wallpaper/NexusOS/contents/images/1536x1024.png b/assets/themes/KDE/wallpaper/NexusOS/contents/images/1536x1024.png new file mode 100755 index 0000000..16b7e2f Binary files /dev/null and b/assets/themes/KDE/wallpaper/NexusOS/contents/images/1536x1024.png differ diff --git a/assets/themes/KDE/wallpaper/NexusOS/metadata.json b/assets/themes/KDE/wallpaper/NexusOS/metadata.json new file mode 100644 index 0000000..7e7fa50 --- /dev/null +++ b/assets/themes/KDE/wallpaper/NexusOS/metadata.json @@ -0,0 +1,10 @@ +{ + "KPlugin": { + "Authors": [{ "Name": "NexusOS" }], + "Id": "NexusOS", + "License": "GPL", + "Name": "NexusOS", + "Version": "1.0" + }, + "X-Plasma-APIVersion": "2" +} diff --git a/assets/themes/NexusOS-icons/index.theme b/assets/themes/NexusOS-icons/index.theme index 769fa0c..27bad47 100644 --- a/assets/themes/NexusOS-icons/index.theme +++ b/assets/themes/NexusOS-icons/index.theme @@ -1,702 +1,120 @@ [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 + +# Inherit breeze-dark, not Papirus-Dark. Breeze's icons carry +# ColorScheme-Text / ColorScheme-Highlight classes that Plasma recolours from +# the active colour scheme, so everything this theme does not override picks up +# the NexusOS palette on its own. Papirus hardcodes its blues (#5294e2), which +# no colour scheme can touch — inheriting it left the whole un-themed surface +# permanently blue. hicolor last, as the freedesktop fallback of last resort. +Inherits=breeze-dark,hicolor + +# Only directories that actually exist. This list previously declared 124 +# sections against 21 real directories, most of them copied from Papirus. +Directories=16x16/apps,16x16/places,16x16/actions,22x22/apps,22x22/places,22x22/actions,24x24/apps,24x24/places,24x24/actions,32x32/apps,32x32/places,32x32/actions,48x48/apps,48x48/places,48x48/actions,64x64/apps,64x64/places,64x64/actions,128x128/apps,128x128/places,128x128/actions [16x16/apps] Context=Applications Size=16 Type=Fixed +[16x16/places] +Context=Places +Size=16 +Type=Fixed + +[16x16/actions] +Context=Actions +Size=16 +Type=Fixed + [22x22/apps] Context=Applications Size=22 Type=Fixed +[22x22/places] +Context=Places +Size=22 +Type=Fixed + +[22x22/actions] +Context=Actions +Size=22 +Type=Fixed + [24x24/apps] Context=Applications Size=24 Type=Fixed +[24x24/places] +Context=Places +Size=24 +Type=Fixed + +[24x24/actions] +Context=Actions +Size=24 +Type=Fixed + [32x32/apps] Context=Applications Size=32 Type=Fixed +[32x32/places] +Context=Places +Size=32 +Type=Fixed + +[32x32/actions] +Context=Actions +Size=32 +Type=Fixed + [48x48/apps] Context=Applications Size=48 Type=Fixed +[48x48/places] +Context=Places +Size=48 +Type=Fixed + +[48x48/actions] +Context=Actions +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 +[64x64/actions] +Context=Actions +Size=64 +Type=Fixed + +[128x128/apps] +Context=Applications +Size=128 +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] +Context=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/README.md b/assets/themes/README.md index 46ce863..e067b75 100644 --- a/assets/themes/README.md +++ b/assets/themes/README.md @@ -112,8 +112,11 @@ KDE/ 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 + look-and-feel/com.nexusos.desktop/ # Global Theme: selects every component + contents/defaults # what System Settings applies in one click + contents/splash/Splash.qml # startup splash, matches Plymouth/SDDM + + wallpaper/NexusOS/ # desktop + lock wallpaper (brushed metal) konsole/ NexusOS.colorscheme # general terminal colors @@ -146,7 +149,12 @@ cp assets/themes/KDE/konsole/*.colorscheme assets/themes/KDE/konsole/Promethean. # SDDM palette (already live; installer will redeploy if needed) ``` -**Needs KDE session:** Aurorae decoration, Plasma shell theme, kscreenlocker. +**Needs KDE session:** Aurorae decoration, Plasma shell theme, Global Theme. + +The lock screen is Breeze's, with the NexusOS wallpaper set on it. Plasma 5.27 +draws the lock screen from a look-and-feel package's `contents/lockscreen`, and +replacing that QML risks a lock screen you cannot get back through, so only the +background is ours. ### First boot into Plasma diff --git a/assets/themes/install-theme.sh b/assets/themes/install-theme.sh index 5de40e1..d325cda 100644 --- a/assets/themes/install-theme.sh +++ b/assets/themes/install-theme.sh @@ -74,7 +74,12 @@ echo "NexusOS theme — restoring wiring from $REPO" echo "[1/4] Symlinks" link "$REPO/assets/themes/NexusOS" "$HOME/.themes/NexusOS" +# Icons go in BOTH places on purpose. ~/.icons is the GTK/XFCE legacy path and +# is all this used to install; Qt/KF5 searches XDG data dirs only, so under +# Plasma the theme was never found and every icon silently fell back to Breeze +# (`kiconfinder5 nexusos-logo` returned nothing at all). link "$REPO/assets/themes/NexusOS-icons" "$HOME/.icons/NexusOS" +link "$REPO/assets/themes/NexusOS-icons" "$HOME/.local/share/icons/NexusOS" link "$REPO/assets/themes/gtk3-user-overrides.css" "$HOME/.config/gtk-3.0/gtk.css" echo "[2/4] xfconf (xsettings + xfwm4)" @@ -98,7 +103,10 @@ 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)" +# Inherits is a comma-separated list, so check the first (primary) parent +# rather than the whole string — treating "breeze-dark,hicolor" as one theme +# name found no such directory and warned about a fallback that is installed. +INH="$(grep -i '^Inherits=' "$REPO/assets/themes/NexusOS-icons/index.theme" 2>/dev/null | cut -d= -f2 | cut -d, -f1)" if [[ -n "$INH" ]]; then INH_OK=0 for dir in /usr/share/icons ~/.icons ~/.local/share/icons; do diff --git a/assets/themes/plank/NexusOS/dock.theme b/assets/themes/plank/NexusOS/dock.theme new file mode 100644 index 0000000..0261aff --- /dev/null +++ b/assets/themes/plank/NexusOS/dock.theme @@ -0,0 +1,39 @@ +# NexusOS dock theme — matches the XFCE top panel. +# Colors are taken straight from xfce4-panel's background-rgba +# (0.588, 0.152, 0.773, 0.294) scaled to 0-255: 150;;39;;197;;75. +# Flat fill (start == end) because the panel has no gradient. +[PlankTheme] +TopRoundness=10 +BottomRoundness=10 +LineWidth=1 +OuterStrokeColor=150;;39;;197;;110 +FillStartColor=150;;39;;197;;75 +FillEndColor=150;;39;;197;;75 +InnerStrokeColor=150;;39;;197;;40 + +# Padding is NOT pixels: Plank renders it as value * icon_size / 10. These are +# tuned for the 48px icons set in dconf — 5px top/bottom, 10px left/right. +# Change icon-size and the borders scale with it; recompute as px * 10 / icon_size. +[PlankDockTheme] +HorizPadding=2.0833 +TopPadding=1.0417 +BottomPadding=1.0417 +ItemPadding=2.0 +IndicatorSize=6.0 +IconShadowSize=1.0 +UrgentBounceHeight=1.6667 +LaunchBounceHeight=0.625 +FadeOpacity=1.0 +ClickTime=300 +UrgentBounceTime=600 +LaunchBounceTime=600 +ActiveTime=300 +SlideTime=300 +FadeTime=250 +HideTime=250 +GlowSize=30 +GlowTime=10000 +GlowPulseTime=2000 +UrgentHueShift=150 +ItemMoveTime=450 +CascadeHide=true diff --git a/assets/themes/restore-snapshot/applications/nexus-core.desktop b/assets/themes/restore-snapshot/applications/nexus-core.desktop new file mode 100644 index 0000000..c0e4769 --- /dev/null +++ b/assets/themes/restore-snapshot/applications/nexus-core.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=NexusOS +Comment=Launch the NexusOS assistant (memory + backend + UI) +Exec=/home/jon/nexus-core/management/nexus-app.sh +Icon=/home/jon/nexus-core/assets/n-small.png +Terminal=false +Categories=Utility; +StartupNotify=false diff --git a/assets/themes/restore-snapshot/applications/promethean-terminal.desktop b/assets/themes/restore-snapshot/applications/promethean-terminal.desktop new file mode 100644 index 0000000..5656700 --- /dev/null +++ b/assets/themes/restore-snapshot/applications/promethean-terminal.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=Promethean Terminal +Comment=Launch a terminal with the Promethean environment +Exec=/home/jon/.local/kitty.app/bin/kitty --config /home/jon/nexus-core/bin/promethean/kitty.conf --class promethean-terminal -e bash --rcfile /home/jon/nexus-core/.promethean_bashrc -i +Icon=/home/jon/nexus-core/assets/promethean-terminal.png +Terminal=false +Categories=Utility;TerminalEmulator; +StartupNotify=false +StartupWMClass=promethean-terminal diff --git a/assets/themes/restore-snapshot/gtk-3.0-settings.ini b/assets/themes/restore-snapshot/gtk-3.0-settings.ini index 2864a2f..f10bb7d 100644 --- a/assets/themes/restore-snapshot/gtk-3.0-settings.ini +++ b/assets/themes/restore-snapshot/gtk-3.0-settings.ini @@ -1,15 +1,6 @@ [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-cursor-theme-size=24 +gtk-cursor-theme-name=DMZ-White 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 index 90b2453..f10bb7d 100644 --- a/assets/themes/restore-snapshot/gtk-4.0-settings.ini +++ b/assets/themes/restore-snapshot/gtk-4.0-settings.ini @@ -1,12 +1,6 @@ [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-cursor-theme-size=24 +gtk-cursor-theme-name=DMZ-White 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-dconf.ini b/assets/themes/restore-snapshot/plank-dconf.ini new file mode 100644 index 0000000..c321fc6 --- /dev/null +++ b/assets/themes/restore-snapshot/plank-dconf.ini @@ -0,0 +1,19 @@ +[docks/dock1] +alignment='center' +auto-pinning=true +current-workspace-only=false +dock-items=['microsoft-edge.dockitem', 'promethean-terminal.dockitem', 'nexus-core.dockitem', 'code.dockitem', 'thunar.dockitem'] +hide-delay=0 +hide-mode='intelligent' +icon-size=48 +items-alignment='center' +lock-items=false +offset=0 +pinned-only=false +pressure-reveal=false +show-dock-item=false +theme='NexusOS' +tooltips-enabled=true +unhide-delay=0 +zoom-enabled=false +zoom-percent=150 diff --git a/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem b/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem index 7a81d48..6c3be3d 100644 --- a/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem +++ b/assets/themes/restore-snapshot/plank/dock1/launchers/microsoft-edge.dockitem @@ -1,2 +1,2 @@ [PlankDockItemPreferences] -Launcher=file:///home/jon/.local/share/applications/microsoft-edge.desktop +Launcher=file:///usr/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 deleted file mode 100644 index 9bb8205..0000000 --- a/assets/themes/restore-snapshot/plank/dock1/launchers/nexus-activate.dockitem +++ /dev/null @@ -1,2 +0,0 @@ -[PlankDockItemPreferences] -Launcher=file:///home/jon/.local/share/applications/nexus-activate.desktop diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml index 2b402c5..bd6231d 100644 --- a/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-desktop.xml @@ -1,11 +1,11 @@ - + - + @@ -13,13 +13,13 @@ - + - + @@ -32,17 +32,17 @@ - + - + - + diff --git a/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml b/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml index 47d155c..15fad1d 100644 --- a/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml +++ b/assets/themes/restore-snapshot/xfconf-xml/xfce4-panel.xml @@ -1,4 +1,4 @@ - + @@ -38,7 +38,14 @@ + + + + + + + @@ -81,15 +88,11 @@ + - - - - - @@ -107,6 +110,7 @@ + @@ -118,8 +122,13 @@ + + + + + + - diff --git a/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml b/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml index 8bd4729..146c88c 100644 --- a/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml +++ b/assets/themes/restore-snapshot/xfconf-xml/xsettings.xml @@ -1,4 +1,4 @@ - + @@ -27,6 +27,8 @@ + + @@ -35,8 +37,6 @@ - - diff --git a/bin/backup-linux.sh b/bin/backup-linux.sh index efab4de..2a09b9f 100644 --- a/bin/backup-linux.sh +++ b/bin/backup-linux.sh @@ -51,6 +51,18 @@ if [ -d "$HOME/.config/plank" ]; then mkdir -p "$snap/plank" cp -rf "$HOME/.config/plank/." "$snap/plank/" 2>/dev/null || true fi +# The .dockitem files alone are NOT the dock. Everything else — item list, theme, +# icon size, position, hide mode — lives in dconf, and on startup Plank DELETES +# every .dockitem not in its list, so copying files alone restores nothing. +# `monitor` is stripped: it's per-machine, and plank-primary-watch.sh sets it. +dconf dump /net/launchpad/plank/ 2>/dev/null | grep -v '^monitor=' > "$snap/plank-dconf.ini" || true +# Dock launchers point at .desktop files in ~/.local/share/applications, which +# is outside the repo — Plank prunes any item whose target is missing. +mkdir -p "$snap/applications" +for d in $(sed -n 's#.*applications/\([^.]*\.desktop\).*#\1#p' \ + "$snap"/plank/dock1/launchers/*.dockitem 2>/dev/null); do + cp -f "$HOME/.local/share/applications/$d" "$snap/applications/$d" 2>/dev/null || true +done # 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.). diff --git a/bin/nexus_window.py b/bin/nexus_window.py index 9ad520f..2ccd288 100644 --- a/bin/nexus_window.py +++ b/bin/nexus_window.py @@ -10,7 +10,7 @@ The window opens IMMEDIATELY on a loading page and navigates to the app once the backend answers, rather than waiting up to 40s with nothing on screen and then appearing. One window the whole time: nothing pops up and vanishes, and there is no moment where the user is left wondering whether the command did anything. -The loading page's status line is updated live (memory -> backend -> ready) via +The loading page's status line is updated live (backend -> ready) via evaluate_js, instead of sitting on one static sentence for the whole wait - the point is a user watching the window can tell what stage it's stuck on, without needing to go read a terminal or a log file to find out. @@ -24,7 +24,6 @@ import time import urllib.request URL = "http://localhost:8000" -MEMORY_URL = "http://localhost:8001/" ICON_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets", "NexusOS.ico") # Inline so it renders with no server and no asset files - the whole point is @@ -47,7 +46,7 @@ LOADING_HTML = """ Starting NexusOS - Starting memory service... + Starting backend...
Starting memory service...
Starting backend...