add noctalia and fuzzel

This commit is contained in:
2026-05-23 21:20:58 +02:00
parent 364801f1a3
commit 9a3eceb3ab
599 changed files with 204318 additions and 0 deletions
@@ -0,0 +1,195 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services.Keyboard
import qs.Widgets
Item {
id: previewPanel
property var currentItem: null
property string fullContent: ""
property string imageDataUrl: ""
property bool loadingFullContent: false
property bool isImageContent: false
implicitHeight: contentArea.implicitHeight + Style.margin2L
function loadContent() {
if (!currentItem || !currentItem.clipboardId)
return;
if (isImageContent) {
// For images, check cache first then decode
imageDataUrl = ClipboardService.getImageData(currentItem.clipboardId) || "";
loadingFullContent = !imageDataUrl;
if (!imageDataUrl && currentItem.mime) {
ClipboardService.decodeToDataUrl(currentItem.clipboardId, currentItem.mime, null);
}
} else {
// For text, check sync cache first
const cached = ClipboardService.getContent(currentItem.clipboardId);
if (cached) {
fullContent = cached;
loadingFullContent = false;
} else {
// Show preview while loading full content
fullContent = currentItem.preview || "";
loadingFullContent = true;
// Async decode as fallback
var requestedId = currentItem.clipboardId;
ClipboardService.decode(requestedId, function (content) {
if (!previewPanel || !previewPanel.currentItem) {
return;
}
if (previewPanel.currentItem.clipboardId === requestedId) {
var trimmed = content ? content.trim() : "";
if (trimmed !== "") {
previewPanel.fullContent = trimmed;
}
previewPanel.loadingFullContent = false;
}
});
}
}
}
Connections {
target: previewPanel
function onCurrentItemChanged() {
fullContent = "";
imageDataUrl = "";
loadingFullContent = false;
isImageContent = currentItem && currentItem.isImage;
if (currentItem && currentItem.clipboardId) {
loadContent();
}
}
}
readonly property int _rev: ClipboardService.revision
on_RevChanged: {
// When cache updates, try to load content if we're still showing loading or preview
if (currentItem && currentItem.clipboardId && !isImageContent && loadingFullContent) {
const cached = ClipboardService.getContent(currentItem.clipboardId);
if (cached) {
fullContent = cached;
loadingFullContent = false;
}
}
}
Timer {
id: imageUpdateTimer
interval: 200
running: currentItem && currentItem.isImage && imageDataUrl === ""
repeat: currentItem && currentItem.isImage && imageDataUrl === ""
onTriggered: {
if (currentItem && currentItem.clipboardId) {
const newData = ClipboardService.getImageData(currentItem.clipboardId) || "";
if (newData !== imageDataUrl) {
imageDataUrl = newData;
if (newData) {
loadingFullContent = false;
}
}
}
}
}
Item {
id: contentArea
anchors.fill: parent
anchors.margins: Style.marginS
BusyIndicator {
anchors.centerIn: parent
running: loadingFullContent
visible: loadingFullContent
width: Style.baseWidgetSize
height: width
}
// Preview for image entry
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
spacing: Style.marginS
visible: isImageContent && !loadingFullContent && imageDataUrl !== ""
NImageRounded {
id: previewImage
Layout.fillWidth: true
Layout.fillHeight: true
radius: Style.marginS
imagePath: imageDataUrl
imageFillMode: Image.PreserveAspectFit
}
NDivider {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginS
}
NText {
Layout.fillWidth: true
text: {
const meta = ClipboardService.parseImageMeta(currentItem?.preview);
if (meta)
return `${meta.fmt} ${meta.w}×${meta.h} ${meta.size}`;
// Fallback to basic info
const format = (currentItem?.mime || "image").split("/")[1]?.toUpperCase() || "Image";
return `${format} ${previewImage.implicitWidth}×${previewImage.implicitHeight}`;
}
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
}
}
// Preview for text entry
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
spacing: Style.marginS
visible: !isImageContent && !loadingFullContent
NScrollView {
id: clipboardScrollView
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPolicy: Settings.data.appLauncher.clipboardWrapText ? ScrollBar.AlwaysOff : ScrollBar.AsNeeded
NText {
text: fullContent
width: Settings.data.appLauncher.clipboardWrapText ? clipboardScrollView.availableWidth : implicitWidth
wrapMode: Settings.data.appLauncher.clipboardWrapText ? Text.Wrap : Text.NoWrap
textFormat: Text.PlainText
font.pointSize: Style.fontSizeM
font.family: Settings.data.ui.fontFixed
color: Color.mOnSurface
}
}
NDivider {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginS
}
NText {
Layout.fillWidth: true
visible: fullContent.length > 0
text: {
const chars = fullContent.length;
const words = fullContent.split(/\s+/).filter(w => w.length > 0).length;
const lines = fullContent.split('\n').length;
return `${chars} chars, ${words} words, ${lines} lines`;
}
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
}
}
}
}