feat: sync with upstream — v1.2.0, in-app updates, Projects, modules
Brings the public tree back in line with the development repo after several weeks of drift caused by a stale publish include list. New: - In-app update path: GET /update/check compares the checkout against origin/main and POST /update/apply runs `ncp upgrade` detached (pull, rebuild, restart). The sidebar shows the version, checks on click, and offers an "update available" pill. - Projects: a project workspace groups chats and RAG documents, with per-project instructions and document retrieval scoped to the active project. Replaces the standalone Documents page. - modules/: auto-discovered feature plugins (mail, network) with their frontend counterparts and tests. - Memory curation runs in-process (synapse/memory/curator.py) on the chat model when a conversation goes idle. The separate memory service on :8001 is gone, along with the launcher lines that started it. Also: the KDE theme, panel and Promethean terminal assets, the full test suite, and VERSION 1.2.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 &>/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 >/dev/null 2>&1 &
|
||||
disown 2>/dev/null || true
|
||||
echo " [ok] plasmashell restarted"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1920 1200" width="1920" height="1200">
|
||||
<defs>
|
||||
<linearGradient id="metalBase" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#1a1a2e;stop-opacity:1"/>
|
||||
<stop offset="35%" style="stop-color:#16213e;stop-opacity:1"/>
|
||||
<stop offset="65%" style="stop-color:#0f1626;stop-opacity:1"/>
|
||||
<stop offset="100%" style="stop-color:#0a0e1a;stop-opacity:1"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="vignette" cx="50%" cy="50%" r="70%">
|
||||
<stop offset="0%" style="stop-color:transparent;stop-opacity:0"/>
|
||||
<stop offset="100%" style="stop-color:#000000;stop-opacity:0.6"/>
|
||||
</radialGradient>
|
||||
<pattern id="brushLines" x="0" y="0" width="4" height="4" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="4" x2="4" y2="0" stroke="#ffffff" stroke-width="0.3" opacity="0.03"/>
|
||||
</pattern>
|
||||
<pattern id="machineLines" x="0" y="0" width="100" height="3" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="1.5" x2="100" y2="1.5" stroke="#ffffff" stroke-width="0.2" opacity="0.025"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="1920" height="1200" fill="url(#metalBase)"/>
|
||||
<rect width="1920" height="1200" fill="url(#brushLines)"/>
|
||||
<rect width="1920" height="1200" fill="url(#machineLines)"/>
|
||||
<ellipse cx="960" cy="340" rx="1200" ry="280" fill="#ffffff" opacity="0.012"/>
|
||||
<ellipse cx="960" cy="220" rx="400" ry="170" fill="#00d4ff" opacity="0.015"/>
|
||||
<rect width="1920" height="1200" fill="url(#vignette)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
|
||||
<kcfgfile name=""/>
|
||||
<group name="General">
|
||||
</group>
|
||||
</kcfg>
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 <pattern> 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
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../../sddm/NexusOS-QML/assets/background.png
|
||||
Regular → Executable
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 185 KiB |
@@ -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"
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Authors": [{ "Name": "NexusOS" }],
|
||||
"Id": "NexusOS",
|
||||
"License": "GPL",
|
||||
"Name": "NexusOS",
|
||||
"Version": "1.0"
|
||||
},
|
||||
"X-Plasma-APIVersion": "2"
|
||||
}
|
||||
Reference in New Issue
Block a user