Refactor AppLauncher, fix clipboardhistory, add persistent history (max 50 entries), add advanced math stuff
This commit is contained in:
parent
7ce8e51fa3
commit
044da17763
5 changed files with 690 additions and 263 deletions
|
|
@ -15,30 +15,125 @@ import "../../Helpers/FuzzySort.js" as Fuzzysort
|
|||
NLoader {
|
||||
id: appLauncher
|
||||
isLoaded: false
|
||||
// Clipboard state is persisted in Services/ClipboardService.qml
|
||||
|
||||
content: Component {
|
||||
NPanel {
|
||||
id: appLauncherPanel
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
// No local timer/processes; use persistent Clipboard service
|
||||
|
||||
// Removed local clipboard processes; handled by Clipboard service
|
||||
|
||||
// Copy helpers via simple exec; avoid keeping processes alive locally
|
||||
function copyImageBase64(mime, base64) {
|
||||
Quickshell.execDetached(["sh", "-lc", `printf %s ${base64} | base64 -d | wl-copy -t '${mime}'`])
|
||||
// Import modular components
|
||||
Calculator {
|
||||
id: calculator
|
||||
}
|
||||
|
||||
function copyText(text) {
|
||||
Quickshell.execDetached(["sh", "-lc", `printf %s ${text} | wl-copy -t text/plain;charset=utf-8`])
|
||||
ClipboardHistory {
|
||||
id: clipboardHistory
|
||||
}
|
||||
|
||||
function updateClipboardHistory() {
|
||||
ClipboardService.refresh()
|
||||
// Properties
|
||||
property var desktopEntries: DesktopEntries.applications.values
|
||||
property string searchText: ""
|
||||
property int selectedIndex: 0
|
||||
|
||||
// Refresh clipboard when user starts typing clipboard commands
|
||||
onSearchTextChanged: {
|
||||
if (searchText.startsWith(">clip")) {
|
||||
clipboardHistory.refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// Main filtering logic
|
||||
property var filteredEntries: {
|
||||
Logger.log("AppLauncher", "Total desktop entries:", desktopEntries ? desktopEntries.length : 0)
|
||||
if (!desktopEntries || desktopEntries.length === 0) {
|
||||
Logger.log("AppLauncher", "No desktop entries available")
|
||||
return []
|
||||
}
|
||||
|
||||
// Filter out entries that shouldn't be displayed
|
||||
var visibleEntries = desktopEntries.filter(entry => {
|
||||
if (!entry || entry.noDisplay) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
Logger.log("AppLauncher", "Visible entries:", visibleEntries.length)
|
||||
|
||||
var query = searchText ? searchText.toLowerCase() : ""
|
||||
var results = []
|
||||
|
||||
// Handle special commands
|
||||
if (query === ">") {
|
||||
results.push({
|
||||
"isCommand": true,
|
||||
"name": ">calc",
|
||||
"content": "Calculator - evaluate mathematical expressions",
|
||||
"icon": "calculate",
|
||||
"execute": executeCalcCommand
|
||||
})
|
||||
|
||||
results.push({
|
||||
"isCommand": true,
|
||||
"name": ">clip",
|
||||
"content": "Clipboard history - browse and restore clipboard items",
|
||||
"icon": "content_paste",
|
||||
"execute": executeClipCommand
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Handle clipboard history
|
||||
if (query.startsWith(">clip")) {
|
||||
return clipboardHistory.processQuery(query)
|
||||
}
|
||||
|
||||
// Handle calculator
|
||||
if (query.startsWith(">calc")) {
|
||||
return calculator.processQuery(query, "calc")
|
||||
}
|
||||
|
||||
// Handle direct math expressions after ">"
|
||||
if (query.startsWith(">") && query.length > 1 && !query.startsWith(">clip") && !query.startsWith(">calc")) {
|
||||
const mathResults = calculator.processQuery(query, "direct")
|
||||
if (mathResults.length > 0) {
|
||||
return mathResults
|
||||
}
|
||||
// If math evaluation fails, fall through to regular search
|
||||
}
|
||||
|
||||
// Regular app search
|
||||
if (!query) {
|
||||
results = results.concat(visibleEntries.sort(function (a, b) {
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
}))
|
||||
} else {
|
||||
var fuzzyResults = Fuzzysort.go(query, visibleEntries, {
|
||||
"keys": ["name", "comment", "genericName"]
|
||||
})
|
||||
results = results.concat(fuzzyResults.map(function (r) {
|
||||
return r.obj
|
||||
}))
|
||||
}
|
||||
|
||||
Logger.log("AppLauncher", "Filtered entries:", results.length)
|
||||
return results
|
||||
}
|
||||
|
||||
// Command execution functions
|
||||
function executeCalcCommand() {
|
||||
searchText = ">calc "
|
||||
searchInput.cursorPosition = searchText.length
|
||||
}
|
||||
|
||||
function executeClipCommand() {
|
||||
searchText = ">clip "
|
||||
searchInput.cursorPosition = searchText.length
|
||||
}
|
||||
|
||||
// Navigation functions
|
||||
function selectNext() {
|
||||
if (filteredEntries.length > 0) {
|
||||
selectedIndex = Math.min(selectedIndex + 1, filteredEntries.length - 1)
|
||||
|
|
@ -67,233 +162,6 @@ NLoader {
|
|||
}
|
||||
}
|
||||
|
||||
property var desktopEntries: DesktopEntries.applications.values
|
||||
property string searchText: ""
|
||||
property int selectedIndex: 0
|
||||
|
||||
// Refresh clipboard when user starts typing clipboard commands
|
||||
onSearchTextChanged: {
|
||||
if (searchText.startsWith(">clip")) {
|
||||
ClipboardService.refresh()
|
||||
}
|
||||
}
|
||||
property var filteredEntries: {
|
||||
Logger.log("AppLauncher", "Total desktop entries:", desktopEntries ? desktopEntries.length : 0)
|
||||
if (!desktopEntries || desktopEntries.length === 0) {
|
||||
Logger.log("AppLauncher", "No desktop entries available")
|
||||
return []
|
||||
}
|
||||
|
||||
// Filter out entries that shouldn't be displayed
|
||||
var visibleEntries = desktopEntries.filter(entry => {
|
||||
if (!entry || entry.noDisplay) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
Logger.log("AppLauncher", "Visible entries:", visibleEntries.length)
|
||||
|
||||
var query = searchText ? searchText.toLowerCase() : ""
|
||||
var results = []
|
||||
|
||||
// Handle special commands
|
||||
if (query === ">") {
|
||||
results.push({
|
||||
"isCommand": true,
|
||||
"name": ">calc",
|
||||
"content": "Calculator - evaluate mathematical expressions",
|
||||
"icon": "tag",
|
||||
"execute": function () {
|
||||
searchText = ">calc "
|
||||
searchInput.cursorPosition = searchText.length
|
||||
}
|
||||
})
|
||||
|
||||
results.push({
|
||||
"isCommand": true,
|
||||
"name": ">clip",
|
||||
"content": "Clipboard history - browse and restore clipboard items",
|
||||
"icon": "content_paste",
|
||||
"execute": function () {
|
||||
searchText = ">clip "
|
||||
searchInput.cursorPosition = searchText.length
|
||||
}
|
||||
})
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Handle clipboard history
|
||||
if (query.startsWith(">clip")) {
|
||||
const searchTerm = query.slice(5).trim()
|
||||
|
||||
ClipboardService.history.forEach(function (clip, index) {
|
||||
let searchContent = clip.type === 'image' ? clip.mimeType : clip.content || clip
|
||||
|
||||
if (!searchTerm || searchContent.toLowerCase().includes(searchTerm)) {
|
||||
let entry
|
||||
if (clip.type === 'image') {
|
||||
entry = {
|
||||
"isClipboard": true,
|
||||
"name": "Image from " + new Date(clip.timestamp).toLocaleTimeString(),
|
||||
"content": "Image: " + clip.mimeType,
|
||||
"icon": "image",
|
||||
"type": 'image',
|
||||
"data": clip.data,
|
||||
"execute": function () {
|
||||
const base64Data = clip.data.split(',')[1]
|
||||
copyImageBase64(clip.mimeType, base64Data)
|
||||
Quickshell.execDetached(["notify-send", "Clipboard", "Image copied: " + clip.mimeType])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const textContent = clip.content || clip
|
||||
let displayContent = textContent
|
||||
let previewContent = ""
|
||||
|
||||
displayContent = displayContent.replace(/\s+/g, ' ').trim()
|
||||
|
||||
if (displayContent.length > 50) {
|
||||
previewContent = displayContent
|
||||
displayContent = displayContent.split('\n')[0].substring(0, 50) + "..."
|
||||
}
|
||||
|
||||
entry = {
|
||||
"isClipboard": true,
|
||||
"name": displayContent,
|
||||
"content": previewContent || textContent,
|
||||
"icon": "content_paste",
|
||||
"execute": function () {
|
||||
Quickshell.clipboardText = String(textContent)
|
||||
copyText(String(textContent))
|
||||
var preview = (textContent.length > 50) ? textContent.slice(0, 50) + "…" : textContent
|
||||
Quickshell.execDetached(["notify-send", "Clipboard", "Text copied: " + preview])
|
||||
}
|
||||
}
|
||||
}
|
||||
results.push(entry)
|
||||
}
|
||||
})
|
||||
|
||||
if (results.length === 0) {
|
||||
results.push({
|
||||
"isClipboard": true,
|
||||
"name": "No clipboard history",
|
||||
"content": "No matching clipboard entries found",
|
||||
"icon": "content_paste_off"
|
||||
})
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Handle direct math expressions after ">"
|
||||
if (query.startsWith(">") && query.length > 1 && !query.startsWith(">clip") && !query.startsWith(">calc")) {
|
||||
var mathExpr = query.slice(1).trim()
|
||||
// Check if it looks like a math expression (contains numbers and math operators)
|
||||
if (mathExpr && /[0-9+\-*/().]/.test(mathExpr)) {
|
||||
try {
|
||||
var sanitizedExpr = mathExpr.replace(/[^0-9+\-*/().\s]/g, '')
|
||||
var result = eval(sanitizedExpr)
|
||||
|
||||
if (isFinite(result) && !isNaN(result)) {
|
||||
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, '')
|
||||
results.push({
|
||||
"isCalculator": true,
|
||||
"name": `${mathExpr} = ${displayResult}`,
|
||||
"result": result,
|
||||
"expr": mathExpr,
|
||||
"icon": "tag",
|
||||
"execute": function () {
|
||||
Quickshell.clipboardText = displayResult
|
||||
copyText(displayResult)
|
||||
Quickshell.execDetached(
|
||||
["notify-send", "Calculator", `${mathExpr} = ${displayResult} (copied to clipboard)`])
|
||||
}
|
||||
})
|
||||
return results
|
||||
}
|
||||
} catch (error) {
|
||||
// If math evaluation fails, fall through to regular search
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle calculator
|
||||
if (query.startsWith(">calc")) {
|
||||
var expr = searchText.slice(5).trim()
|
||||
if (expr && expr !== "") {
|
||||
try {
|
||||
// Simple evaluation - only allow basic math operations
|
||||
var sanitizedExpr = expr.replace(/[^0-9+\-*/().\s]/g, '')
|
||||
var result = eval(sanitizedExpr)
|
||||
|
||||
if (isFinite(result) && !isNaN(result)) {
|
||||
var displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/,
|
||||
'')
|
||||
results.push({
|
||||
"isCalculator": true,
|
||||
"name": `${expr} = ${displayResult}`,
|
||||
"result": result,
|
||||
"expr": expr,
|
||||
"icon": "tag",
|
||||
"execute": function () {
|
||||
Quickshell.clipboardText = displayResult
|
||||
copyText(displayResult)
|
||||
Quickshell.execDetached(
|
||||
["notify-send", "Calculator", `${expr} = ${displayResult} (copied to clipboard)`])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
results.push({
|
||||
"isCalculator": true,
|
||||
"name": "Invalid expression",
|
||||
"content": "Please enter a valid mathematical expression",
|
||||
"icon": "tag",
|
||||
"execute": function () {}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
results.push({
|
||||
"isCalculator": true,
|
||||
"name": "Invalid expression",
|
||||
"content": "Please enter a valid mathematical expression",
|
||||
"icon": "tag",
|
||||
"execute": function () {}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Show placeholder when just ">calc" is entered
|
||||
results.push({
|
||||
"isCalculator": true,
|
||||
"name": "Calculator",
|
||||
"content": "Enter a mathematical expression (e.g., 5+5, 2*3, 10/2)",
|
||||
"icon": "tag",
|
||||
"execute": function () {}
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// Regular app search
|
||||
if (!query) {
|
||||
results = results.concat(visibleEntries.sort(function (a, b) {
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
}))
|
||||
} else {
|
||||
var fuzzyResults = Fuzzysort.go(query, visibleEntries, {
|
||||
"keys": ["name", "comment", "genericName"]
|
||||
})
|
||||
results = results.concat(fuzzyResults.map(function (r) {
|
||||
return r.obj
|
||||
}))
|
||||
}
|
||||
|
||||
Logger.log("AppLauncher", "Filtered entries:", results.length)
|
||||
return results
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Logger.log("AppLauncher", "Component completed")
|
||||
Logger.log("AppLauncher", "DesktopEntries available:", typeof DesktopEntries !== 'undefined')
|
||||
|
|
@ -302,7 +170,7 @@ NLoader {
|
|||
DesktopEntries.entries ? DesktopEntries.entries.length : 'undefined')
|
||||
}
|
||||
// Start clipboard refresh immediately on open
|
||||
updateClipboardHistory()
|
||||
clipboardHistory.refresh()
|
||||
}
|
||||
|
||||
// Main content container
|
||||
|
|
@ -368,7 +236,8 @@ NLoader {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
onTextChanged: {
|
||||
searchText = text
|
||||
selectedIndex = 0 // Reset selection when search changes
|
||||
// Defer selectedIndex reset to avoid binding loops
|
||||
Qt.callLater(() => selectedIndex = 0)
|
||||
}
|
||||
selectedTextColor: Color.mOnSurface
|
||||
selectionColor: Color.mPrimary
|
||||
|
|
|
|||
161
Modules/AppLauncher/Calculator.qml
Normal file
161
Modules/AppLauncher/Calculator.qml
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
|
||||
import "../../Helpers/AdvancedMath.js" as AdvancedMath
|
||||
|
||||
QtObject {
|
||||
id: calculator
|
||||
|
||||
// Function to evaluate mathematical expressions
|
||||
function evaluate(expression) {
|
||||
if (!expression || expression.trim() === "") {
|
||||
return {
|
||||
"isValid": false,
|
||||
"result": "",
|
||||
"displayResult": "",
|
||||
"error": "Empty expression"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Try advanced math first
|
||||
if (typeof AdvancedMath !== 'undefined') {
|
||||
const result = AdvancedMath.evaluate(expression.trim())
|
||||
const displayResult = AdvancedMath.formatResult(result)
|
||||
|
||||
return {
|
||||
"isValid": true,
|
||||
"result": result,
|
||||
"displayResult": displayResult,
|
||||
"expression": expression,
|
||||
"error": ""
|
||||
}
|
||||
} else {
|
||||
// Fallback to basic evaluation
|
||||
console.log("AdvancedMath not available, using basic eval")
|
||||
|
||||
// Basic preprocessing for common functions
|
||||
var processed = expression.trim()
|
||||
.replace(/\bpi\b/gi, Math.PI)
|
||||
.replace(/\be\b/gi, Math.E)
|
||||
.replace(/\bsqrt\s*\(/g, 'Math.sqrt(')
|
||||
.replace(/\bsin\s*\(/g, 'Math.sin(')
|
||||
.replace(/\bcos\s*\(/g, 'Math.cos(')
|
||||
.replace(/\btan\s*\(/g, 'Math.tan(')
|
||||
.replace(/\blog\s*\(/g, 'Math.log10(')
|
||||
.replace(/\bln\s*\(/g, 'Math.log(')
|
||||
.replace(/\bexp\s*\(/g, 'Math.exp(')
|
||||
.replace(/\bpow\s*\(/g, 'Math.pow(')
|
||||
.replace(/\babs\s*\(/g, 'Math.abs(')
|
||||
|
||||
// Sanitize and evaluate
|
||||
if (!/^[0-9+\-*/().\s\w,]+$/.test(processed)) {
|
||||
throw new Error("Invalid characters in expression")
|
||||
}
|
||||
|
||||
const result = eval(processed)
|
||||
|
||||
if (!isFinite(result) || isNaN(result)) {
|
||||
throw new Error("Invalid result")
|
||||
}
|
||||
|
||||
const displayResult = Number.isInteger(result) ? result.toString() : result.toFixed(6).replace(/\.?0+$/, '')
|
||||
|
||||
return {
|
||||
"isValid": true,
|
||||
"result": result,
|
||||
"displayResult": displayResult,
|
||||
"expression": expression,
|
||||
"error": ""
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
"isValid": false,
|
||||
"result": "",
|
||||
"displayResult": "",
|
||||
"error": error.message || error.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate calculator entry for display
|
||||
function createEntry(expression, searchContext = "") {
|
||||
const evaluation = evaluate(expression)
|
||||
|
||||
if (!evaluation.isValid) {
|
||||
return {
|
||||
"isCalculator": true,
|
||||
"name": "Invalid expression",
|
||||
"content": evaluation.error,
|
||||
"icon": "error",
|
||||
"execute": function () {
|
||||
// Do nothing for invalid expressions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const displayName = searchContext === "calc"
|
||||
? `${expression} = ${evaluation.displayResult}`
|
||||
: `${expression} = ${evaluation.displayResult}`
|
||||
|
||||
return {
|
||||
"isCalculator": true,
|
||||
"name": displayName,
|
||||
"result": evaluation.result,
|
||||
"expr": expression,
|
||||
"displayResult": evaluation.displayResult,
|
||||
"icon": "calculate",
|
||||
"execute": function () {
|
||||
Quickshell.clipboardText = evaluation.displayResult
|
||||
// Also copy using shell command for better compatibility
|
||||
Quickshell.execDetached(["sh", "-lc", `printf %s ${evaluation.displayResult} | wl-copy -t text/plain;charset=utf-8`])
|
||||
Quickshell.execDetached([
|
||||
"notify-send",
|
||||
"Calculator",
|
||||
`${expression} = ${evaluation.displayResult} (copied to clipboard)`
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create placeholder entry for empty calculator mode
|
||||
function createPlaceholderEntry() {
|
||||
return {
|
||||
"isCalculator": true,
|
||||
"name": "Calculator",
|
||||
"content": "Try: sqrt(16), sin(1), cos(0), pi*2, exp(1), pow(2,8), abs(-5)",
|
||||
"icon": "calculate",
|
||||
"execute": function () {
|
||||
// Do nothing for placeholder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process calculator queries
|
||||
function processQuery(query, searchContext = "") {
|
||||
const results = []
|
||||
|
||||
if (searchContext === "calc") {
|
||||
// Handle ">calc" mode
|
||||
const expr = query.slice(5).trim()
|
||||
if (expr && expr !== "") {
|
||||
results.push(createEntry(expr, "calc"))
|
||||
} else {
|
||||
results.push(createPlaceholderEntry())
|
||||
}
|
||||
} else if (query.startsWith(">") && query.length > 1 && !query.startsWith(">clip") && !query.startsWith(">calc")) {
|
||||
// Handle direct math expressions after ">"
|
||||
const mathExpr = query.slice(1).trim()
|
||||
const evaluation = evaluate(mathExpr)
|
||||
|
||||
if (evaluation.isValid) {
|
||||
results.push(createEntry(mathExpr, "direct"))
|
||||
}
|
||||
// If invalid, don't add anything - let it fall through to regular search
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
158
Modules/AppLauncher/ClipboardHistory.qml
Normal file
158
Modules/AppLauncher/ClipboardHistory.qml
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
|
||||
QtObject {
|
||||
id: clipboardHistory
|
||||
|
||||
// Copy helpers for different content types
|
||||
function copyImageBase64(mime, base64) {
|
||||
Quickshell.execDetached(["sh", "-lc", `printf %s ${base64} | base64 -d | wl-copy -t '${mime}'`])
|
||||
}
|
||||
|
||||
function copyText(text) {
|
||||
// Use printf with proper quoting to handle special characters
|
||||
Quickshell.execDetached(["sh", "-c", `printf '%s' ${JSON.stringify(text)} | wl-copy -t text/plain`])
|
||||
}
|
||||
|
||||
// Create clipboard entry for display
|
||||
function createClipboardEntry(clip, index) {
|
||||
if (clip.type === 'image') {
|
||||
return {
|
||||
"isClipboard": true,
|
||||
"name": "Image from " + new Date(clip.timestamp).toLocaleTimeString(),
|
||||
"content": "Image: " + clip.mimeType,
|
||||
"icon": "image",
|
||||
"type": 'image',
|
||||
"data": clip.data,
|
||||
"timestamp": clip.timestamp,
|
||||
"index": index,
|
||||
"execute": function () {
|
||||
const dataParts = clip.data.split(',')
|
||||
const base64Data = dataParts.length > 1 ? dataParts[1] : clip.data
|
||||
copyImageBase64(clip.mimeType, base64Data)
|
||||
Quickshell.execDetached(["notify-send", "Clipboard", "Image copied: " + clip.mimeType])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle text content
|
||||
const textContent = clip.content || clip
|
||||
let displayContent = textContent
|
||||
let previewContent = ""
|
||||
|
||||
// Normalize whitespace for display
|
||||
displayContent = displayContent.replace(/\s+/g, ' ').trim()
|
||||
|
||||
// Create preview for long content
|
||||
if (displayContent.length > 50) {
|
||||
previewContent = displayContent
|
||||
displayContent = displayContent.split('\n')[0].substring(0, 50) + "..."
|
||||
}
|
||||
|
||||
return {
|
||||
"isClipboard": true,
|
||||
"name": displayContent,
|
||||
"content": previewContent || textContent,
|
||||
"icon": "content_paste",
|
||||
"type": 'text',
|
||||
"timestamp": clip.timestamp,
|
||||
"index": index,
|
||||
"textData": textContent, // Store the text data for the execute function
|
||||
"execute": function () {
|
||||
const text = this.textData || clip.content || clip
|
||||
Quickshell.clipboardText = String(text)
|
||||
copyText(String(text))
|
||||
var preview = (text.length > 50) ? text.slice(0, 50) + "…" : text
|
||||
Quickshell.execDetached(["notify-send", "Clipboard", "Text copied: " + preview])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create empty state entry
|
||||
function createEmptyEntry() {
|
||||
return {
|
||||
"isClipboard": true,
|
||||
"name": "No clipboard history",
|
||||
"content": "No matching clipboard entries found",
|
||||
"icon": "content_paste_off",
|
||||
"execute": function () {
|
||||
// Do nothing for empty state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process clipboard queries
|
||||
function processQuery(query) {
|
||||
const results = []
|
||||
|
||||
if (!query.startsWith(">clip")) {
|
||||
return results
|
||||
}
|
||||
|
||||
// Extract search term after ">clip "
|
||||
const searchTerm = query.slice(5).trim()
|
||||
|
||||
// Note: Clipboard refresh should be handled externally to avoid binding loops
|
||||
|
||||
// Process each clipboard item
|
||||
ClipboardService.history.forEach(function (clip, index) {
|
||||
let searchContent = clip.type === 'image' ? clip.mimeType : clip.content || clip
|
||||
|
||||
// Apply search filter if provided
|
||||
if (!searchTerm || searchContent.toLowerCase().includes(searchTerm.toLowerCase())) {
|
||||
const entry = createClipboardEntry(clip, index)
|
||||
results.push(entry)
|
||||
}
|
||||
})
|
||||
|
||||
// Show empty state if no results
|
||||
if (results.length === 0) {
|
||||
results.push(createEmptyEntry())
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Create command entry for clipboard mode (deprecated - use direct creation in parent)
|
||||
function createCommandEntry() {
|
||||
return {
|
||||
"isCommand": true,
|
||||
"name": ">clip",
|
||||
"content": "Clipboard history - browse and restore clipboard items",
|
||||
"icon": "content_paste",
|
||||
"execute": function () {
|
||||
// This should be handled by the parent component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to refresh clipboard
|
||||
function refresh() {
|
||||
ClipboardService.refresh()
|
||||
}
|
||||
|
||||
// Get clipboard history count
|
||||
function getHistoryCount() {
|
||||
return ClipboardService.history ? ClipboardService.history.length : 0
|
||||
}
|
||||
|
||||
// Get formatted timestamp for display
|
||||
function formatTimestamp(timestamp) {
|
||||
return new Date(timestamp).toLocaleTimeString()
|
||||
}
|
||||
|
||||
// Get clipboard entry by index
|
||||
function getEntryByIndex(index) {
|
||||
if (ClipboardService.history && index >= 0 && index < ClipboardService.history.length) {
|
||||
return ClipboardService.history[index]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Clear all clipboard history
|
||||
function clearAll() {
|
||||
ClipboardService.clearHistory()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue