* screen-toolkit: add Noctalia v5 plugin * screen-toolkit: credit original plugin * screen-toolkit: fix recording, OCR capture, and result panel UX * screen-toolkit: rename record tool labels to Record / Rec FS * screen-toolkit: update plugin thumbnail * screen-toolkit: fix README validation (backtick deps, result panel toggle) * screen-toolkit: fix OCR results and dependencies * screen-toolkit: backtick recording and annotator deps in README --------- Co-authored-by: Ahmed5Emad <ahmed5emad@users.noreply.github.com>
163 lines
5.4 KiB
Luau
163 lines
5.4 KiB
Luau
--!nonstrict
|
|
-- Screen Toolkit — main [[panel]].
|
|
--
|
|
-- Thin client for the toolkit: a header plus the tool grid grouped into
|
|
-- Capture / Annotate / Record sections. It only dispatches actions to the
|
|
-- [[service]] through the "command" state channel. Capture tools close the
|
|
-- panel first so the capture never includes it.
|
|
|
|
local panelOpen = false
|
|
local recording = noctalia.state.get("recording") == true
|
|
|
|
-- ── Helpers ────────────────────────────────────────────────────────────────
|
|
|
|
local function tr(key, subst)
|
|
if subst then return noctalia.tr(key, subst) end
|
|
return noctalia.tr(key)
|
|
end
|
|
|
|
local function send(action, payload)
|
|
noctalia.state.set("command", { action = action, payload = payload })
|
|
end
|
|
|
|
-- Capture tools must not see the panel in the frame, so close it first.
|
|
local CAPTURE_ACTIONS = {
|
|
colorPicker = true,
|
|
ocr = true,
|
|
qr = true,
|
|
palette = true,
|
|
lens = true,
|
|
measure = true,
|
|
annotate = true,
|
|
annotateFullscreen = true,
|
|
annotateWindow = true,
|
|
record = true,
|
|
recordMp4 = true,
|
|
recordFullscreen = true,
|
|
recordFullscreenMp4 = true,
|
|
}
|
|
|
|
local function runTool(action)
|
|
if CAPTURE_ACTIONS[action] then
|
|
panel.close()
|
|
end
|
|
send(action)
|
|
end
|
|
|
|
-- ── Header ─────────────────────────────────────────────────────────────────
|
|
|
|
local function header()
|
|
local actions = {
|
|
ui.button({ glyph = "close", onClick = function() panel.close() end }),
|
|
}
|
|
if recording then
|
|
table.insert(actions, 1, ui.button({
|
|
glyph = "square",
|
|
variant = "destructive",
|
|
controlSize = "sm",
|
|
tooltip = tr("panel.stop"),
|
|
onClick = function() send("recordStop") end,
|
|
}))
|
|
end
|
|
return ui.row({ align = "center", gap = 8 }, {
|
|
ui.label({ text = tr("panel.title"), flexGrow = 1, fontSize = 16, fontWeight = "bold", color = "on_surface" }),
|
|
ui.row({ gap = 4 }, actions),
|
|
})
|
|
end
|
|
|
|
-- ── Tool grid ──────────────────────────────────────────────────────────────
|
|
|
|
local TOOLS = {
|
|
{ action = "colorPicker", label_key = "tools.colorpicker", glyph = "palette" },
|
|
{ action = "ocr", label_key = "tools.ocr", glyph = "scan" },
|
|
{ action = "qr", label_key = "tools.qr", glyph = "qrcode" },
|
|
{ action = "palette", label_key = "tools.palette", glyph = "color-swatch" },
|
|
{ action = "lens", label_key = "tools.lens", glyph = "search" },
|
|
{ action = "measure", label_key = "tools.measure", glyph = "ruler" },
|
|
}
|
|
|
|
local ANNOTATE_TOOLS = {
|
|
{ action = "annotate", label_key = "tools.annotate", glyph = "pencil" },
|
|
{ action = "annotateFullscreen", label_key = "tools.annotate_fullscreen", glyph = "maximize" },
|
|
{ action = "annotateWindow", label_key = "tools.annotate_window", glyph = "window" },
|
|
}
|
|
|
|
local RECORD_TOOLS = {
|
|
{ action = "record", label_key = "tools.record_gif", glyph = "gif" },
|
|
{ action = "recordMp4", label_key = "tools.record_mp4", glyph = "movie" },
|
|
{ action = "recordFullscreen", label_key = "tools.record_fullscreen_gif", glyph = "video" },
|
|
{ action = "recordFullscreenMp4", label_key = "tools.record_fullscreen_mp4", glyph = "video" },
|
|
}
|
|
|
|
local function toolTile(t)
|
|
return ui.column({
|
|
key = "tile-" .. t.action,
|
|
flexGrow = 1,
|
|
gap = 6,
|
|
align = "center",
|
|
paddingV = 6,
|
|
radius = 10,
|
|
fill = "surface_variant/0.28",
|
|
borderWidth = 1,
|
|
border = "outline",
|
|
onClick = function() runTool(t.action) end,
|
|
}, {
|
|
ui.column({ width = 30, height = 30, radius = 9, fill = "primary/0.12", align = "center", justify = "center" }, {
|
|
ui.glyph({ name = t.glyph, size = 16, color = "primary" }),
|
|
}),
|
|
ui.label({ text = tr(t.label_key), maxWidth = 100, maxLines = 1, textAlign = "center", fontSize = 11, color = "on_surface_variant" }),
|
|
})
|
|
end
|
|
|
|
local function toolsRow(tools)
|
|
local row = {}
|
|
for _, t in ipairs(tools) do
|
|
row[#row + 1] = toolTile(t)
|
|
end
|
|
return ui.row({ flexGrow = 1, gap = 6 }, row)
|
|
end
|
|
|
|
local function sectionLabel(text)
|
|
return ui.label({ text = text, fontSize = 11, fontWeight = "bold", color = "on_surface_variant" })
|
|
end
|
|
|
|
local function toolGrid()
|
|
return ui.column({ flexGrow = 1, gap = 4, justify = "space_between" }, {
|
|
sectionLabel(tr("panel.section_capture")),
|
|
toolsRow(TOOLS),
|
|
sectionLabel(tr("panel.section_annotate")),
|
|
toolsRow(ANNOTATE_TOOLS),
|
|
sectionLabel(tr("panel.section_record")),
|
|
toolsRow(RECORD_TOOLS),
|
|
})
|
|
end
|
|
|
|
-- ── Main render ────────────────────────────────────────────────────────────
|
|
|
|
local function render()
|
|
panel.render(ui.column({ flexGrow = 1, gap = 10 }, {
|
|
header(),
|
|
toolGrid(),
|
|
}))
|
|
end
|
|
|
|
-- ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
|
|
function onOpen(_context)
|
|
panelOpen = true
|
|
render()
|
|
end
|
|
|
|
function onClose()
|
|
panelOpen = false
|
|
end
|
|
|
|
noctalia.state.watch("recording", function(v)
|
|
recording = v == true
|
|
if panelOpen then render() end
|
|
end)
|
|
|
|
function update()
|
|
noctalia.setUpdateInterval(500)
|
|
end
|