diff options
Diffstat (limited to 'services')
| -rw-r--r-- | services/Clip.qml | 78 | ||||
| -rw-r--r-- | services/Persistent.qml | 27 | ||||
| -rw-r--r-- | services/TimeDate.qml | 48 |
3 files changed, 151 insertions, 2 deletions
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<string> _clipImg: [] property list<string> _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(); } } diff --git a/services/Persistent.qml b/services/Persistent.qml new file mode 100644 index 0000000..3a911bf --- /dev/null +++ b/services/Persistent.qml @@ -0,0 +1,27 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.utils + +Scope { + id: root + property alias adapter: fileView.adapter + property alias fileView: fileView + property string fileDir: Directories.state + property string fileName: "states.json" + property string filePath: `${root.fileDir}/${root.fileName}` + + FileView { + id: fileView + path: root.filePath + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: writeAdapter() + onLoadFailed: error => { + console.log("Failed to load persistent states file:", error); + if (error == FileViewError.FileNotFound) { + writeAdapter(); + } + } + } +} diff --git a/services/TimeDate.qml b/services/TimeDate.qml index fab2beb..f066f46 100644 --- a/services/TimeDate.qml +++ b/services/TimeDate.qml @@ -27,6 +27,54 @@ Singleton { readonly property string date: Qt.formatDateTime(clock.date, "dddd dd/MM") + function sinceWhen(d) { + if (typeof d === "string") + return; + + var now = clock.date; + + function startOfDay(x) { + return new Date(x.getFullYear(), x.getMonth(), x.getDate(), 0, 0, 0, 0); + } + + function startOfWeek(x) { + var dow = x.getDay(); + var mondayOffset = (dow + 6) % 7; + var sod = startOfDay(x); + sod.setDate(sod.getDate() - mondayOffset); + return sod; + } + + function fmt12h(dateObj) { + var h24 = dateObj.getHours(); + var m = dateObj.getMinutes(); + var h12 = h24 % 12; + if (h12 === 0) + h12 = 12; + var ampm = h24 < 12 ? "AM" : "PM"; + return ((h12 < 10 ? "0" : "") + h12 + ":" + (m < 10 ? "0" : "") + m + " " + ampm); + } + + function weekdayName(dateObj) { + return Qt.formatDateTime(dateObj, "dddd"); + } + + var nowSOD = startOfDay(now).getTime(); + var dSOD = startOfDay(d).getTime(); + if (nowSOD === dSOD) { + return fmt12h(d); + } + + var nowSOW = startOfWeek(now).getTime(); + var dSOW = startOfWeek(d).getTime(); + if (nowSOW === dSOW) { + return weekdayName(d) + " " + fmt12h(d); + } + + var ymd = Qt.formatDateTime(d, "yyyy-MM-dd"); + return ymd + " at " + fmt12h(d); + } + SystemClock { id: clock precision: SystemClock.Seconds |
