From 4fe12bbabfe2bb4c468c66348afedb34aee0b783 Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Sat, 16 Aug 2025 16:23:20 +0800 Subject: feat(Clip): metadatas(app it's taken from, timestamp) Persistent and Directories are partially copied from end4-dots which is GPL Licensed --- services/Clip.qml | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) (limited to 'services/Clip.qml') diff --git a/services/Clip.qml b/services/Clip.qml index d03976c..59c460b 100644 --- a/services/Clip.qml +++ b/services/Clip.qml @@ -2,8 +2,11 @@ pragma Singleton import Quickshell import Quickshell.Io -import QtQuick +import Quickshell.Wayland + import qs.components +import qs.utils +import qs.services Searchable { id: root @@ -15,6 +18,8 @@ Searchable { property list _clipImg: [] property list _clipImgIds: _clipImg.map(p => p.split("/").pop().split(".").shift()) + property var _clipMetadata: ({}) + list: { if (!_clipHist?.length) return []; @@ -41,16 +46,45 @@ Searchable { const idStr = idNum >= 0 ? "" + idNum : ""; const imgPath = idStr ? imgMap[idStr] || "" : ""; const isImg = !!imgPath; + const metadata = _clipMetadata[idStr] || {}; return { index: idNum, isImage: isImg, data: isImg ? imgPath : payload, - raw: raw + raw: raw, + timestamp: metadata.timestamp || "", + appId: metadata.appId || "", + appTitle: metadata.appTitle || "", + appIcon: Quickshell.iconPath(AppSearch.guessIcon(metadata.appId), "image-missing") }; }); } + Persistent { + id: clipMetaStore + filePath: Directories.clipHistMetaDataPath + adapter: JsonAdapter { + property var metadata: ({}) + } + fileView.onLoaded: { + const meta = adapter.metadata; + for (const k in meta) { + if (meta[k].timestamp && typeof meta[k].timestamp === "string") { + meta[k].timestamp = new Date(meta[k].timestamp); + } + } + root._clipMetadata = meta; + } + // fileView.onAdapterUpdated: { + // root._clipMetadata = adapter.metadata; + // } + } + + on_ClipMetadataChanged: { + clipMetaStore.adapter.metadata = _clipMetadata; + } + function transformSearch(search) { return search.toLowerCase(); } @@ -68,18 +102,57 @@ Searchable { imgProc.running = true; } + function _captureMetadata() { + const now = new Date(); + const activeToplevel = ToplevelManager.activeToplevel; + + Qt.callLater(() => { + clipProc.running = true; + clipProc.stdout.onceFinished = () => { + const lines = clipProc.stdout.text.split(/\r?\n/); + const firstLine = lines[0] || ""; + const tab = firstLine.indexOf("\t"); + + if (tab > 0) { + const idStr = firstLine.slice(0, tab); + const n = parseInt(idStr, 10); + if (!Number.isNaN(n)) { + var newMetadata = {}; + for (var k in _clipMetadata) { + if (_clipMetadata.hasOwnProperty(k)) + newMetadata[k] = _clipMetadata[k]; + } + + newMetadata[idStr] = { + timestamp: now, + appId: activeToplevel ? activeToplevel.appId : "", + appTitle: activeToplevel ? activeToplevel.title : "" + }; + _clipMetadata = newMetadata; + } + } + }; + }); + } + Process { id: clipProc running: true command: ["cliphist", "-preview-width", 9 ** 9, "-max-items", 9 ** 9, "list"] stdout: StdioCollector { property bool pending: false + property var onceFinished: null + onStreamFinished: () => { pending = true; Qt.callLater(() => { if (pending) { pending = false; _clipHist = this.text.split(/\r?\n/); + if (onceFinished) { + onceFinished(); + onceFinished = null; + } } }); } @@ -113,6 +186,7 @@ Searchable { stdout: SplitParser { onRead: line => { + _captureMetadata(); _update(); } } -- cgit v1.2.3