Build the Panama Hyprland desktop

This commit is contained in:
Gabriel Brown
2026-08-17 10:32:55 -04:00
parent 67033f2a31
commit 5248883e4b
190 changed files with 18554 additions and 34 deletions
@@ -0,0 +1,230 @@
// Clipboard history, backed by Vicinae's existing database. Opening the panel
// performs one read-only query; nothing polls or repaints while it is closed.
import QtQuick
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import qs.config
import qs.services
import qs.widgets
import qs.modules.quicksettings
PanelWindow {
id: root
visible: ShellState.clipboardOpen
color: "transparent"
anchors {
top: true
right: true
}
margins {
top: Theme.barHeight + Theme.barGap * 2
right: Theme.barSideMargin
}
exclusiveZone: 0
implicitWidth: 430
implicitHeight: 550
WlrLayershell.namespace: "qs-popover-clipboard"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
property string filter: ""
readonly property var filteredEntries: Clipboard.entries.filter(entry => {
const needle = root.filter.trim().toLowerCase();
if (!needle)
return true;
return [entry.preview, entry.source, entry.host, entry.typeLabel]
.join(" ").toLowerCase().includes(needle);
})
function close(): void {
ShellState.close();
}
function activateFirst(): void {
if (root.filteredEntries.length === 0)
return;
Clipboard.copyEntry(root.filteredEntries[0].id);
root.close();
}
onVisibleChanged: {
if (root.visible) {
root.filter = "";
search.clear();
Clipboard.refresh();
openAnim.restart();
Qt.callLater(search.grab);
}
}
HyprlandFocusGrab {
windows: [root]
active: root.visible
onCleared: root.close()
}
Rectangle {
id: surface
anchors.fill: parent
radius: Theme.popoverRadius
color: Theme.alpha(Theme.bgPopover, Theme.popoverAlpha)
border.width: 1
border.color: Theme.alpha(Theme.fg, 0.08)
PrismEdge {
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: 1
}
inset: parent.radius
}
transform: Scale {
id: openScale
origin.x: surface.width
origin.y: 0
xScale: 1
yScale: 1
}
Column {
anchors.fill: parent
anchors.margins: Theme.popoverPadding
spacing: Theme.sectionSpacing
Item {
width: parent.width
height: 30
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 9
ThemedIcon {
anchors.verticalCenter: parent.verticalCenter
size: 17
icon: "edit-paste-symbolic"
iconFallback: "edit-copy-symbolic"
tint: Theme.accent
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: "Clipboard"
color: Theme.fg
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeTitle
font.weight: Font.DemiBold
}
}
Text {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: Clipboard.entries.length === 1
? "1 item"
: Clipboard.entries.length + " items"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSizeSmall
}
}
SearchField {
id: search
width: parent.width
placeholder: "Search clipboard history"
onTextChanged: root.filter = text
onAccepted: root.activateFirst()
onDismissed: root.close()
}
Rectangle {
width: parent.width
height: 1
border.width: 0
color: Theme.alpha(Theme.fg, 0.1)
}
Item {
width: parent.width
height: parent.height - y
Text {
anchors.centerIn: parent
visible: Clipboard.loading
text: "Loading clipboard history…"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
Text {
anchors.centerIn: parent
width: parent.width - 40
visible: !Clipboard.loading && !Clipboard.available
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: "Clipboard history is unavailable. Start Vicinae to restore it."
color: Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
Text {
anchors.centerIn: parent
visible: !Clipboard.loading && Clipboard.available
&& root.filteredEntries.length === 0
text: root.filter ? "No matching clipboard items" : "Nothing copied yet"
color: Theme.fgMuted
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
}
ScrollColumn {
anchors.fill: parent
maxHeight: parent.height
spacing: 4
visible: !Clipboard.loading && Clipboard.available
&& root.filteredEntries.length > 0
Repeater {
model: root.filteredEntries
ClipboardEntry {
required property var modelData
width: parent.width
entry: modelData
onActivated: {
Clipboard.copyEntry(modelData.id);
root.close();
}
}
}
}
}
}
}
NumberAnimation {
id: openAnim
target: openScale
property: "yScale"
from: 0.96
to: 1
duration: Theme.durFast
easing.type: Easing.OutCubic
}
}