105 lines
3.1 KiB
QML
105 lines
3.1 KiB
QML
import QtQuick
|
|
import qs.Services
|
|
import "../../../Helpers/AdvancedMath.js" as AdvancedMath
|
|
|
|
Item {
|
|
property var launcher: null
|
|
property string name: "Calculator"
|
|
|
|
function handleCommand(query) {
|
|
// Handle >calc command or direct math expressions after >
|
|
return query.startsWith(">calc") || (query.startsWith(">") && query.length > 1 && isMathExpression(query.substring(1)))
|
|
}
|
|
|
|
function commands() {
|
|
return [{
|
|
"name": ">calc",
|
|
"description": "Calculator - evaluate mathematical expressions",
|
|
"icon": "accessories-calculator",
|
|
"isImage": false,
|
|
"onActivate": function () {
|
|
launcher.setSearchText(">calc ")
|
|
}
|
|
}]
|
|
}
|
|
|
|
function getResults(query) {
|
|
let expression = ""
|
|
|
|
if (query.startsWith(">calc")) {
|
|
expression = query.substring(5).trim()
|
|
} else if (query.startsWith(">")) {
|
|
expression = query.substring(1).trim()
|
|
} else {
|
|
return []
|
|
}
|
|
|
|
if (!expression) {
|
|
return [{
|
|
"name": "Calculator",
|
|
"description": "Enter a mathematical expression",
|
|
"icon": "accessories-calculator",
|
|
"isImage": false,
|
|
"onActivate": function () {}
|
|
}]
|
|
}
|
|
|
|
try {
|
|
let result = AdvancedMath.evaluate(expression.trim())
|
|
|
|
return [{
|
|
"name": AdvancedMath.formatResult(result),
|
|
"description": `${expression} = ${result}`,
|
|
"icon": "accessories-calculator",
|
|
"isImage": false,
|
|
"onActivate": function () {
|
|
// TODO: copy entry to clipboard via ClipHist
|
|
launcher.close()
|
|
}
|
|
}]
|
|
} catch (error) {
|
|
return [{
|
|
"name": "Error",
|
|
"description": error.message || "Invalid expression",
|
|
"icon": "dialog-error",
|
|
"isImage": false,
|
|
"onActivate": function () {}
|
|
}]
|
|
}
|
|
}
|
|
|
|
function evaluateExpression(expr) {
|
|
// Sanitize input - only allow safe characters
|
|
const sanitized = expr.replace(/[^0-9\+\-\*\/\(\)\.\s\%]/g, '')
|
|
if (sanitized !== expr) {
|
|
throw new Error("Invalid characters in expression")
|
|
}
|
|
|
|
// Don't allow empty expressions
|
|
if (!sanitized.trim()) {
|
|
throw new Error("Empty expression")
|
|
}
|
|
|
|
try {
|
|
// Use Function constructor for safe evaluation
|
|
// This is safer than eval() but still evaluate math
|
|
const result = Function('"use strict"; return (' + sanitized + ')')()
|
|
|
|
// Check for valid result
|
|
if (!isFinite(result)) {
|
|
throw new Error("Result is not a finite number")
|
|
}
|
|
|
|
// Round to reasonable precision to avoid floating point issues
|
|
return Math.round(result * 1000000000) / 1000000000
|
|
} catch (e) {
|
|
throw new Error("Invalid mathematical expression")
|
|
}
|
|
}
|
|
|
|
function isMathExpression(expr) {
|
|
// Check if string looks like a math expression
|
|
// Allow digits, operators, parentheses, decimal points, and whitespace
|
|
return /^[\d\s\+\-\*\/\(\)\.\%]+$/.test(expr)
|
|
}
|
|
}
|