* init * feat: notify options; all, minimal and none * update: plugin.toml * update: add README * feat: add button toggle * feat: add configurable glyph * fix: valid image radius * fix: shell escape entry * feat: add settings * update: add thumbnail.webp * feat: add titlebar and titlebar toggle * feat: add custom image widget setting, reorder keys * fix: auto-size image with titlebar * update: README (new settings) * fix: lowercase correction_level label keys * fix: file type and move shellEscape to the cmd
227 lines
5.5 KiB
Luau
227 lines
5.5 KiB
Luau
local currentText = ""
|
|
local genCount = 0
|
|
local errorMessage = nil
|
|
local imagePath = nil
|
|
local status = "idle"
|
|
|
|
local imageSize = 400
|
|
|
|
local notifyConf = noctalia.getConfig("notify")
|
|
|
|
local function notify(text, error)
|
|
local cmd = error and noctalia.notifyError or noctalia.notify
|
|
cmd("QR Code encoder", text)
|
|
end
|
|
|
|
local function statusText()
|
|
if status == "generating" then
|
|
return noctalia.tr("panel.status.generating")
|
|
elseif status == "error" then
|
|
return noctalia.tr("panel.status.error", { message = errorMessage })
|
|
elseif status == "ready" then
|
|
return noctalia.tr("panel.status.ready")
|
|
elseif status == "copied" then
|
|
return noctalia.tr("panel.status.copied")
|
|
else
|
|
return noctalia.tr("panel.status.idle")
|
|
end
|
|
end
|
|
|
|
local function render()
|
|
local rowContent = {
|
|
ui.input({
|
|
flexGrow = 1,
|
|
focus = true,
|
|
placeholder = noctalia.tr("panel.placeholder"),
|
|
onChange = "onTextChange",
|
|
onSubmit = "onTextSubmit",
|
|
})
|
|
}
|
|
|
|
if noctalia.getConfig("generate_button") then
|
|
table.insert(
|
|
rowContent,
|
|
ui.button({
|
|
text = "Generate",
|
|
variant = "primary",
|
|
enabled = status ~= "generating",
|
|
onClick = "onGenerateClick",
|
|
})
|
|
)
|
|
end
|
|
|
|
local content = {
|
|
ui.row({ gap = 8 }, rowContent),
|
|
ui.label({
|
|
flexGrow = 1,
|
|
textAlign = "center",
|
|
text = statusText(),
|
|
color = status == "error" and "error" or "on_surface/0.75",
|
|
})
|
|
}
|
|
|
|
if (status == "ready"
|
|
or status == "copied"
|
|
or (status == "error" and errorMessage == noctalia.tr("panel.error.copy_failed")))
|
|
and imagePath ~= nil then
|
|
table.insert(
|
|
content,
|
|
ui.row({ justify = "center" }, {
|
|
ui.image({
|
|
path = imagePath,
|
|
width = imageSize,
|
|
height = imageSize,
|
|
radius = 24,
|
|
onClick = "onImageClick",
|
|
})
|
|
})
|
|
)
|
|
end
|
|
|
|
if noctalia.getConfig("titlebar") then
|
|
imageSize = 350
|
|
table.insert(
|
|
content, 1,
|
|
ui.row({ align = "center", justify = "space_between", gap = 8 }, {
|
|
ui.label({ text = "QR Code Encoder", fontSize = 16, fontWeight = "bold", flexGrow = 1 }),
|
|
ui.button({ glyph = "settings", onClick = noctalia.openSettings }),
|
|
ui.button({ glyph = "close", onClick = "onCloseClicked" }),
|
|
})
|
|
)
|
|
end
|
|
|
|
panel.render(ui.column({ flexGrow = 1, gap = 16 }, content))
|
|
end
|
|
|
|
local function shellEscape(raw)
|
|
return "'" .. raw:gsub("'", "'\\''") .. "'"
|
|
end
|
|
|
|
local function generate(text)
|
|
if status == "generating" then
|
|
return
|
|
end
|
|
|
|
text = noctalia.string.trim(text or "")
|
|
|
|
if text == "" then
|
|
if imagePath ~= nil then
|
|
noctalia.removeFile(imagePath)
|
|
end
|
|
status = "idle"
|
|
errorMessage = nil
|
|
imagePath = nil
|
|
render()
|
|
return
|
|
end
|
|
|
|
if not noctalia.commandExists("qrencode") then
|
|
status = "error"
|
|
errorMessage = noctalia.tr("panel.error.missing_qrencode")
|
|
|
|
if notifyConf == "all" then
|
|
notify(statusText(), status == "error")
|
|
end
|
|
|
|
render()
|
|
return
|
|
end
|
|
|
|
status = "generating"
|
|
render()
|
|
|
|
local dir = noctalia.pluginDir()
|
|
local outputPath = string.format("%s/qr-%s.png", dir, genCount)
|
|
local previousPath = imagePath
|
|
genCount += 1
|
|
|
|
local size = noctalia.getConfig("size") or 8
|
|
local correctionLevel = noctalia.getConfig("correction_level") or "M"
|
|
|
|
local cmd = string.format(
|
|
'qrencode %s -o %s -s %s -l %s',
|
|
shellEscape(text),
|
|
outputPath,
|
|
size,
|
|
correctionLevel
|
|
)
|
|
|
|
noctalia.runAsync(
|
|
cmd,
|
|
function(result)
|
|
if result.exitCode == 0 and noctalia.fileExists(outputPath) then
|
|
imagePath = outputPath
|
|
status = "ready"
|
|
if previousPath ~= nil then
|
|
noctalia.removeFile(previousPath)
|
|
end
|
|
else
|
|
status = "error"
|
|
errorMessage = noctalia.tr("panel.error.generate_failed")
|
|
end
|
|
|
|
if notifyConf == "all" then
|
|
notify(statusText(), status == "error")
|
|
end
|
|
|
|
render()
|
|
end
|
|
)
|
|
end
|
|
|
|
function onOpen(_context)
|
|
status = "idle"
|
|
render()
|
|
end
|
|
|
|
function onTextChange(value)
|
|
currentText = value
|
|
end
|
|
|
|
function onTextSubmit(value)
|
|
currentText = value
|
|
generate(currentText)
|
|
end
|
|
|
|
function onGenerateClick()
|
|
generate(currentText)
|
|
end
|
|
|
|
function onImageClick()
|
|
local bytes, err = noctalia.readFile(imagePath)
|
|
errorMessage = noctalia.tr("panel.error.copy_failed")
|
|
|
|
if bytes == nil then
|
|
status = "error"
|
|
else
|
|
local ok = noctalia.copyToClipboard(bytes, "image/png")
|
|
|
|
status = ok and "copied" or "error"
|
|
end
|
|
|
|
if notifyConf == "all" then
|
|
notify(statusText(), status == "error")
|
|
end
|
|
|
|
if noctalia.getConfig("close_on_copy") then
|
|
if notifyConf == "minimal" then
|
|
notify(statusText(), status == "error")
|
|
end
|
|
|
|
panel.close()
|
|
return
|
|
end
|
|
|
|
render()
|
|
end
|
|
|
|
function onCloseClicked()
|
|
panel.close()
|
|
end
|
|
|
|
function onClose()
|
|
if imagePath ~= nil then
|
|
noctalia.removeFile(imagePath)
|
|
end
|
|
end
|