add crypto

This commit is contained in:
zack 2025-07-22 20:21:21 -04:00
parent 90cbe489f6
commit af6a3bce3e
Signed by: zoey
GPG key ID: 81FB9FECDD6A33E2
120 changed files with 24616 additions and 462 deletions

View file

@ -0,0 +1,442 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Shapes
import Qt5Compat.GraphicalEffects
import Quickshell.Io
import "root:/Data" as Data
import "root:/Core" as Core
// Niri workspace indicator
Rectangle {
id: root
property ListModel workspaces: ListModel {}
property int currentWorkspace: -1
property bool isDestroying: false
// Signal for workspace change bursts
signal workspaceChanged(int workspaceId, color accentColor)
// MASTER ANIMATION CONTROLLER - drives Desktop overlay burst effect
property real masterProgress: 0.0
property bool effectsActive: false
property color effectColor: Data.ThemeManager.accent
// Single master animation that controls Desktop overlay burst
function triggerUnifiedWave() {
effectColor = Data.ThemeManager.accent
masterAnimation.restart()
}
SequentialAnimation {
id: masterAnimation
PropertyAction {
target: root
property: "effectsActive"
value: true
}
NumberAnimation {
target: root
property: "masterProgress"
from: 0.0
to: 1.0
duration: 1000
easing.type: Easing.OutQuint
}
PropertyAction {
target: root
property: "effectsActive"
value: false
}
PropertyAction {
target: root
property: "masterProgress"
value: 0.0
}
}
color: Data.ThemeManager.bgColor
width: 32
height: workspaceColumn.implicitHeight + 24
// Smooth height animation
Behavior on height {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
// Right-side rounded corners
topRightRadius: width / 2
bottomRightRadius: width / 2
topLeftRadius: 0
bottomLeftRadius: 0
// Wave effects overlay - unified animation system (DISABLED - using Desktop overlay)
Item {
id: waveEffects
anchors.fill: parent
visible: false // Disabled in favor of unified overlay
z: 2
}
// Niri event stream listener
Process {
id: niriProcess
command: ["niri", "msg", "event-stream"]
running: true
stdout: SplitParser {
onRead: data => {
const lines = data.split('\n');
for (const line of lines) {
if (line.trim()) {
parseNiriEvent(line.trim());
}
}
}
}
onExited: {
// Auto-restart on failure to maintain workspace sync (but not during destruction)
if (exitCode !== 0 && !root.isDestroying) {
Qt.callLater(() => running = true);
}
}
}
// Parse Niri event stream messages
function parseNiriEvent(line) {
try {
// Handle workspace focus changes
if (line.startsWith("Workspace focused: ")) {
const workspaceId = parseInt(line.replace("Workspace focused: ", ""));
if (!isNaN(workspaceId)) {
const previousWorkspace = root.currentWorkspace;
root.currentWorkspace = workspaceId;
updateWorkspaceFocus(workspaceId);
// Trigger burst effect if workspace actually changed
if (previousWorkspace !== workspaceId && previousWorkspace !== -1) {
root.triggerUnifiedWave();
root.workspaceChanged(workspaceId, Data.ThemeManager.accent);
}
}
}
// Handle workspace list updates
else if (line.startsWith("Workspaces changed: ")) {
const workspaceData = line.replace("Workspaces changed: ", "");
parseWorkspaceList(workspaceData);
}
} catch (e) {
console.log("Error parsing niri event:", e);
}
}
// Update workspace focus states
function updateWorkspaceFocus(focusedWorkspaceId) {
for (let i = 0; i < root.workspaces.count; i++) {
const workspace = root.workspaces.get(i);
const wasFocused = workspace.isFocused;
const isFocused = workspace.id === focusedWorkspaceId;
const isActive = workspace.id === focusedWorkspaceId;
// Only update changed properties to trigger animations
if (wasFocused !== isFocused) {
root.workspaces.setProperty(i, "isFocused", isFocused);
root.workspaces.setProperty(i, "isActive", isActive);
}
}
}
// Parse workspace data from Niri's Rust-style output format
function parseWorkspaceList(data) {
try {
const workspaceMatches = data.match(/Workspace \{[^}]+\}/g);
if (!workspaceMatches) {
return;
}
const newWorkspaces = [];
for (const match of workspaceMatches) {
const idMatch = match.match(/id: (\d+)/);
const idxMatch = match.match(/idx: (\d+)/);
const nameMatch = match.match(/name: Some\("([^"]+)"\)|name: None/);
const outputMatch = match.match(/output: Some\("([^"]+)"\)/);
const isActiveMatch = match.match(/is_active: (true|false)/);
const isFocusedMatch = match.match(/is_focused: (true|false)/);
const isUrgentMatch = match.match(/is_urgent: (true|false)/);
if (idMatch && idxMatch && outputMatch) {
const workspace = {
id: parseInt(idMatch[1]),
idx: parseInt(idxMatch[1]),
name: nameMatch && nameMatch[1] ? nameMatch[1] : "",
output: outputMatch[1],
isActive: isActiveMatch ? isActiveMatch[1] === "true" : false,
isFocused: isFocusedMatch ? isFocusedMatch[1] === "true" : false,
isUrgent: isUrgentMatch ? isUrgentMatch[1] === "true" : false
};
newWorkspaces.push(workspace);
if (workspace.isFocused) {
root.currentWorkspace = workspace.id;
}
}
}
// Sort by index and update model
newWorkspaces.sort((a, b) => a.idx - b.idx);
root.workspaces.clear();
root.workspaces.append(newWorkspaces);
} catch (e) {
console.log("Error parsing workspace list:", e);
}
}
// Vertical workspace indicator pills
Column {
id: workspaceColumn
anchors.centerIn: parent
spacing: 6
Repeater {
model: root.workspaces
Rectangle {
id: workspacePill
// Dynamic sizing based on focus state
width: model.isFocused ? 18 : 16
height: model.isFocused ? 36 : 22
radius: width / 2
scale: model.isFocused ? 1.0 : 0.9
// Material Design 3 inspired colors
color: {
if (model.isFocused) {
return Data.ThemeManager.accent;
}
if (model.isActive) {
return Qt.rgba(Data.ThemeManager.accent.r, Data.ThemeManager.accent.g, Data.ThemeManager.accent.b, 0.5);
}
if (model.isUrgent) {
return Data.ThemeManager.error;
}
return Qt.rgba(Data.ThemeManager.primaryText.r, Data.ThemeManager.primaryText.g, Data.ThemeManager.primaryText.b, 0.4);
}
// Workspace pill burst overlay (DISABLED - using unified overlay)
Rectangle {
id: pillBurst
anchors.centerIn: parent
width: parent.width + 8
height: parent.height + 8
radius: width / 2
color: Data.ThemeManager.accent
opacity: 0 // Disabled in favor of unified overlay
visible: false
z: -1
}
// Subtle pulse for inactive pills during workspace changes
Rectangle {
id: inactivePillPulse
anchors.fill: parent
radius: parent.radius
color: Data.ThemeManager.accent
opacity: {
// Only pulse inactive pills during effects
if (model.isFocused || !root.effectsActive) return 0
// More subtle pulse that peaks mid-animation
if (root.masterProgress < 0.3) {
return (root.masterProgress / 0.3) * 0.15
} else if (root.masterProgress < 0.7) {
return 0.15
} else {
return 0.15 * (1.0 - (root.masterProgress - 0.7) / 0.3)
}
}
z: -0.5 // Behind the pill content but visible
}
// Enhanced corner shadows for burst effect (DISABLED - using unified overlay)
Rectangle {
id: cornerBurst
anchors.centerIn: parent
width: parent.width + 4
height: parent.height + 4
radius: width / 2
color: "transparent"
border.color: Data.ThemeManager.accent
border.width: 0 // Disabled
opacity: 0 // Disabled in favor of unified overlay
visible: false
z: 1
}
// Elevation shadow
Rectangle {
anchors.fill: parent
anchors.topMargin: model.isFocused ? 1 : 0
anchors.leftMargin: model.isFocused ? 0.5 : 0
anchors.rightMargin: model.isFocused ? -0.5 : 0
anchors.bottomMargin: model.isFocused ? -1 : 0
radius: parent.radius
color: Qt.rgba(0, 0, 0, model.isFocused ? 0.15 : 0)
z: -1
visible: model.isFocused
Behavior on color { ColorAnimation { duration: 200 } }
}
// Smooth Material Design transitions
Behavior on width {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
Behavior on height {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
Behavior on scale {
NumberAnimation {
duration: 250
easing.type: Easing.OutCubic
}
}
Behavior on color {
ColorAnimation {
duration: 200
}
}
// Workspace number text
Text {
anchors.centerIn: parent
text: model.idx.toString()
color: model.isFocused ? Data.ThemeManager.background : Data.ThemeManager.primaryText
font.pixelSize: model.isFocused ? 10 : 8
font.bold: model.isFocused
font.family: "Roboto, sans-serif"
visible: model.isFocused || model.isActive
Behavior on font.pixelSize { NumberAnimation { duration: 200 } }
Behavior on color { ColorAnimation { duration: 200 } }
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onClicked: {
// Switch workspace via Niri command
switchProcess.command = ["niri", "msg", "action", "focus-workspace", model.idx.toString()];
switchProcess.running = true;
}
// Hover feedback
onEntered: {
if (!model.isFocused) {
workspacePill.color = Qt.rgba(Data.ThemeManager.primaryText.r, Data.ThemeManager.primaryText.g, Data.ThemeManager.primaryText.b, 0.6);
}
}
onExited: {
// Reset to normal color
if (!model.isFocused) {
if (model.isActive) {
workspacePill.color = Qt.rgba(Data.ThemeManager.accent.r, Data.ThemeManager.accent.g, Data.ThemeManager.accent.b, 0.5);
} else if (model.isUrgent) {
workspacePill.color = Data.ThemeManager.error;
} else {
workspacePill.color = Qt.rgba(Data.ThemeManager.primaryText.r, Data.ThemeManager.primaryText.g, Data.ThemeManager.primaryText.b, 0.4);
}
}
}
}
}
}
}
// Workspace switching command process
Process {
id: switchProcess
running: false
onExited: {
running = false
if (exitCode !== 0) {
console.log("Failed to switch workspace:", exitCode);
}
}
}
// Border integration corners
Core.Corners {
id: topLeftCorner
position: "topleft"
size: 1.3
fillColor: Data.ThemeManager.bgColor
offsetX: -41
offsetY: -25
}
// Top-left corner wave overlay (DISABLED - using unified overlay)
Shape {
id: topLeftWave
width: topLeftCorner.width
height: topLeftCorner.height
x: topLeftCorner.x
y: topLeftCorner.y
visible: false // Disabled in favor of unified overlay
preferredRendererType: Shape.CurveRenderer
layer.enabled: true
layer.samples: 4
}
Core.Corners {
id: bottomLeftCorner
position: "bottomleft"
size: 1.3
fillColor: Data.ThemeManager.bgColor
offsetX: -41
offsetY: 78
}
// Bottom-left corner wave overlay (DISABLED - using unified overlay)
Shape {
id: bottomLeftWave
width: bottomLeftCorner.width
height: bottomLeftCorner.height
x: bottomLeftCorner.x
y: bottomLeftCorner.y
visible: false // Disabled in favor of unified overlay
preferredRendererType: Shape.CurveRenderer
layer.enabled: true
layer.samples: 4
}
// Clean up processes on destruction
Component.onDestruction: {
root.isDestroying = true
if (niriProcess.running) {
niriProcess.running = false
}
if (switchProcess.running) {
switchProcess.running = false
}
}
}