1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 12:52:06 -04:00

plugins: add plugin state helpers

This commit is contained in:
bbedward
2026-02-12 14:04:56 -05:00
parent ba5bf0cabc
commit 0e9b21d359
5 changed files with 592 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
import QtQuick
import Quickshell
import qs.Services
Item {
id: root
property var pluginService: null
property string trigger: "n"
signal itemsChanged
property var notes: []
property int maxNotes: 50
Component.onCompleted: {
if (!pluginService)
return;
trigger = pluginService.loadPluginData("quickNotesExample", "trigger", "n");
maxNotes = pluginService.loadPluginData("quickNotesExample", "maxNotes", 50);
// Load notes from plugin STATE (persistent across sessions, separate file)
notes = pluginService.loadPluginState("quickNotesExample", "notes", []);
}
function getItems(query) {
const items = [];
if (query && query.trim().length > 0) {
const text = query.trim();
items.push({
name: "Save note: " + text,
icon: "material:note_add",
comment: "Save as a new note",
action: "add:" + text,
categories: ["Quick Notes"]
});
items.push({
name: "Copy: " + text,
icon: "material:content_copy",
comment: "Copy text to clipboard",
action: "copy:" + text,
categories: ["Quick Notes"]
});
}
const filteredNotes = query ? notes.filter(n => n.text.toLowerCase().includes(query.toLowerCase())) : notes;
for (let i = 0; i < Math.min(20, filteredNotes.length); i++) {
const note = filteredNotes[i];
const age = _formatAge(note.timestamp);
items.push({
name: note.text,
icon: "material:sticky_note_2",
comment: age + " — select to copy, hold for options",
action: "copy:" + note.text,
categories: ["Quick Notes"]
});
}
if (notes.length > 0 && !query) {
items.push({
name: "Clear all notes (" + notes.length + ")",
icon: "material:delete_sweep",
comment: "Remove all saved notes",
action: "clear:",
categories: ["Quick Notes"]
});
}
return items;
}
function executeItem(item) {
if (!item?.action)
return;
const colonIdx = item.action.indexOf(":");
const actionType = item.action.substring(0, colonIdx);
const actionData = item.action.substring(colonIdx + 1);
switch (actionType) {
case "add":
addNote(actionData);
break;
case "copy":
copyToClipboard(actionData);
break;
case "remove":
removeNote(actionData);
break;
case "clear":
clearAllNotes();
break;
default:
showToast("Unknown action: " + actionType);
}
}
function addNote(text) {
if (!text)
return;
const existing = notes.findIndex(n => n.text === text);
if (existing !== -1)
notes.splice(existing, 1);
notes.unshift({
text: text,
timestamp: Date.now()
});
if (notes.length > maxNotes)
notes = notes.slice(0, maxNotes);
_saveNotes();
showToast("Note saved");
}
function removeNote(text) {
notes = notes.filter(n => n.text !== text);
_saveNotes();
showToast("Note removed");
}
function clearAllNotes() {
notes = [];
pluginService.clearPluginState("quickNotesExample");
showToast("All notes cleared");
itemsChanged();
}
function _saveNotes() {
if (!pluginService)
return;
// Save to plugin STATE — writes to quickNotesExample_state.json
// This is separate from plugin SETTINGS (plugin_settings.json)
pluginService.savePluginState("quickNotesExample", "notes", notes);
itemsChanged();
}
function copyToClipboard(text) {
Quickshell.execDetached(["dms", "cl", "copy", text]);
showToast("Copied to clipboard");
}
function showToast(message) {
if (typeof ToastService !== "undefined")
ToastService.showInfo("Quick Notes", message);
}
function _formatAge(timestamp) {
if (!timestamp)
return "";
const seconds = Math.floor((Date.now() - timestamp) / 1000);
if (seconds < 60)
return "just now";
const minutes = Math.floor(seconds / 60);
if (minutes < 60)
return minutes + "m ago";
const hours = Math.floor(minutes / 60);
if (hours < 24)
return hours + "h ago";
const days = Math.floor(hours / 24);
return days + "d ago";
}
onTriggerChanged: {
if (!pluginService)
return;
pluginService.savePluginData("quickNotesExample", "trigger", trigger);
}
}

View File

@@ -0,0 +1,263 @@
import QtQuick
import qs.Widgets
FocusScope {
id: root
property var pluginService: null
implicitHeight: settingsColumn.implicitHeight
height: implicitHeight
Column {
id: settingsColumn
anchors.fill: parent
anchors.margins: 16
spacing: 16
Text {
text: "Quick Notes Settings"
font.pixelSize: 18
font.weight: Font.Bold
color: "#FFFFFF"
}
Text {
text: "Demonstrates the plugin state API — notes are stored in a separate state file (quickNotesExample_state.json) rather than plugin_settings.json."
font.pixelSize: 14
color: "#CCFFFFFF"
wrapMode: Text.WordWrap
width: parent.width - 32
}
Rectangle {
width: parent.width - 32
height: 1
color: "#30FFFFFF"
}
Column {
spacing: 12
width: parent.width - 32
Text {
text: "Trigger Configuration"
font.pixelSize: 16
font.weight: Font.Medium
color: "#FFFFFF"
}
Row {
spacing: 12
anchors.left: parent.left
anchors.right: parent.right
Text {
text: "Trigger:"
font.pixelSize: 14
color: "#FFFFFF"
anchors.verticalCenter: parent.verticalCenter
}
DankTextField {
id: triggerField
width: 100
height: 40
text: loadSettings("trigger", "n")
placeholderText: "n"
backgroundColor: "#30FFFFFF"
textColor: "#FFFFFF"
onTextEdited: {
saveSettings("trigger", text.trim() || "n");
}
}
}
}
Rectangle {
width: parent.width - 32
height: 1
color: "#30FFFFFF"
}
Column {
spacing: 12
width: parent.width - 32
Text {
text: "Storage"
font.pixelSize: 16
font.weight: Font.Medium
color: "#FFFFFF"
}
Row {
spacing: 12
Text {
text: "Max notes:"
font.pixelSize: 14
color: "#FFFFFF"
anchors.verticalCenter: parent.verticalCenter
}
DankTextField {
id: maxNotesField
width: 80
height: 40
text: loadSettings("maxNotes", 50).toString()
placeholderText: "50"
backgroundColor: "#30FFFFFF"
textColor: "#FFFFFF"
onTextEdited: {
const val = parseInt(text);
if (!isNaN(val) && val > 0)
saveSettings("maxNotes", val);
}
}
}
Text {
text: {
const count = loadState("notes", []).length;
return "Currently storing " + count + " note(s)";
}
font.pixelSize: 12
color: "#AAFFFFFF"
}
Rectangle {
width: clearRow.implicitWidth + 24
height: clearRow.implicitHeight + 16
radius: 8
color: clearMouseArea.containsMouse ? "#40FF5252" : "#30FF5252"
Row {
id: clearRow
anchors.centerIn: parent
spacing: 8
Text {
text: "🗑"
font.pixelSize: 14
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Clear all notes"
font.pixelSize: 14
color: "#FF5252"
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: clearMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (pluginService) {
pluginService.clearPluginState("quickNotesExample");
}
}
}
}
}
Rectangle {
width: parent.width - 32
height: 1
color: "#30FFFFFF"
}
Column {
spacing: 8
width: parent.width - 32
Text {
text: "API Usage (for plugin developers):"
font.pixelSize: 14
font.weight: Font.Medium
color: "#FFFFFF"
}
Column {
spacing: 4
leftPadding: 16
bottomPadding: 24
Text {
text: "• pluginService.savePluginState(id, key, value)"
font.pixelSize: 12
color: "#CCFFFFFF"
font.family: "monospace"
}
Text {
text: " Writes to ~/.local/state/.../id_state.json"
font.pixelSize: 11
color: "#AAFFFFFF"
}
Text {
text: "• pluginService.loadPluginState(id, key, default)"
font.pixelSize: 12
color: "#CCFFFFFF"
font.family: "monospace"
}
Text {
text: " Reads from the per-plugin state file"
font.pixelSize: 11
color: "#AAFFFFFF"
}
Text {
text: "• pluginService.clearPluginState(id)"
font.pixelSize: 12
color: "#CCFFFFFF"
font.family: "monospace"
}
Text {
text: " Clears all state for a plugin"
font.pixelSize: 11
color: "#AAFFFFFF"
}
Text {
text: "• pluginService.removePluginStateKey(id, key)"
font.pixelSize: 12
color: "#CCFFFFFF"
font.family: "monospace"
}
Text {
text: " Removes a single key from plugin state"
font.pixelSize: 11
color: "#AAFFFFFF"
}
}
}
}
function saveSettings(key, value) {
if (pluginService)
pluginService.savePluginData("quickNotesExample", key, value);
}
function loadSettings(key, defaultValue) {
if (pluginService)
return pluginService.loadPluginData("quickNotesExample", key, defaultValue);
return defaultValue;
}
function loadState(key, defaultValue) {
if (pluginService)
return pluginService.loadPluginState("quickNotesExample", key, defaultValue);
return defaultValue;
}
}

View File

@@ -0,0 +1,17 @@
{
"id": "quickNotesExample",
"name": "Quick Notes",
"description": "Example launcher plugin demonstrating the plugin state API for persistent data like history and notes",
"version": "1.0.0",
"author": "DankMaterialShell",
"icon": "sticky_note_2",
"type": "launcher",
"capabilities": ["clipboard"],
"component": "./QuickNotesLauncher.qml",
"settings": "./QuickNotesSettings.qml",
"trigger": "n",
"permissions": [
"settings_read",
"settings_write"
]
}