144 lines
3.8 KiB
Luau
144 lines
3.8 KiB
Luau
--!nonstrict
|
|
--!nocheck
|
|
--!nolint UnknownGlobal
|
|
local tr = noctalia.tr("ds4_color")
|
|
local SERVICE = "hy4ri/ds4-color:service"
|
|
|
|
-- Validate a color is exactly 6 hex chars (no metachars / quote-breakers can
|
|
-- survive this, so it is safe to interpolate into the runAsync shell string).
|
|
local function isHex6(s)
|
|
return type(s) == "string" and s:match("^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$") ~= nil
|
|
end
|
|
|
|
local function sendToService(event, payload)
|
|
local cmd = "noctalia msg plugin " .. SERVICE .. " all " .. event
|
|
if payload ~= nil then cmd = cmd .. " '" .. payload .. "'" end
|
|
noctalia.runAsync(cmd, function() end)
|
|
end
|
|
|
|
local function currentColor()
|
|
return noctalia.state.get("lastColor") or "990000"
|
|
end
|
|
|
|
local applyPreset
|
|
|
|
local function render()
|
|
local color = currentColor()
|
|
local presets = noctalia.state.get("presets") or {}
|
|
|
|
local presetRow = {}
|
|
for i, p in ipairs(presets) do
|
|
local presetIndex = i
|
|
local selected = (p.hex:lower() == color:lower())
|
|
table.insert(presetRow, ui.box({
|
|
width = 34,
|
|
height = 34,
|
|
fill = "#" .. p.hex,
|
|
radius = 8,
|
|
border = "outline",
|
|
borderWidth = selected and 3 or 1,
|
|
onClick = function() applyPreset(presetIndex) end,
|
|
}))
|
|
end
|
|
|
|
panel.render(ui.column({ gap = 10, padding = 12 }, {
|
|
ui.row({ align = "center", justify = "space_between" }, {
|
|
ui.label({ text = tr, fontSize = 18, fontWeight = "bold", color = "primary", flexGrow = 1 }),
|
|
ui.button({ glyph = "close", onClick = "onCloseClicked" }),
|
|
}),
|
|
|
|
-- Live swatch preview
|
|
ui.box({
|
|
width = 200,
|
|
height = 72,
|
|
fill = "#" .. color,
|
|
radius = 12,
|
|
border = "outline",
|
|
borderWidth = 2,
|
|
onClick = "onNativePicker",
|
|
}),
|
|
|
|
-- Hex field + native picker button
|
|
ui.row({ gap = 8, align = "center" }, {
|
|
ui.input({
|
|
key = "hex-input",
|
|
value = "#" .. color,
|
|
flexGrow = 1,
|
|
onSubmit = "onSubmitHex",
|
|
}),
|
|
ui.button({ glyph = "color-picker", variant = "ghost", onClick = "onNativePicker" }),
|
|
}),
|
|
|
|
ui.label({ text = noctalia.tr("presets"), fontSize = 13, color = "on_surface_variant" }),
|
|
ui.row({ gap = 8, align = "center" }, presetRow),
|
|
|
|
ui.row({ gap = 8 }, {
|
|
ui.button({
|
|
text = noctalia.tr("save"),
|
|
variant = "ghost",
|
|
onClick = "onSave",
|
|
flexGrow = 1,
|
|
}),
|
|
ui.button({
|
|
text = noctalia.tr("apply"),
|
|
variant = "primary",
|
|
onClick = "onApply",
|
|
flexGrow = 1,
|
|
}),
|
|
}),
|
|
}))
|
|
end
|
|
|
|
--==== callbacks ====
|
|
function onApply()
|
|
local c = currentColor()
|
|
if isHex6(c) then sendToService("apply", c) end
|
|
end
|
|
|
|
function onSave()
|
|
local c = currentColor()
|
|
if isHex6(c) then sendToService("save", c) end
|
|
end
|
|
|
|
function onSubmitHex(text)
|
|
local hex = text:match("^#?([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])$")
|
|
or text:match("^0x([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])$")
|
|
if hex == nil or not isHex6(hex) then
|
|
noctalia.notifyError(tr, noctalia.tr("bad_color"))
|
|
return
|
|
end
|
|
noctalia.state.set("lastColor", hex:upper())
|
|
render()
|
|
end
|
|
|
|
function onNativePicker()
|
|
noctalia.openColorPicker("#" .. currentColor(), function(color)
|
|
if color == nil then return end
|
|
local hex = color:match("#?(%x%x%x%x%x%x)")
|
|
if hex ~= nil then
|
|
noctalia.state.set("lastColor", hex:upper())
|
|
render()
|
|
end
|
|
end)
|
|
end
|
|
|
|
function onCloseClicked()
|
|
panel.close()
|
|
end
|
|
|
|
function onClose()
|
|
end
|
|
|
|
applyPreset = function(i)
|
|
local presets = noctalia.state.get("presets") or {}
|
|
local p = presets[i]
|
|
if p == nil then return end
|
|
noctalia.state.set("lastColor", p.hex:upper())
|
|
render()
|
|
end
|
|
|
|
function onOpen(context)
|
|
noctalia.state.watch("lastColor", function() render() end)
|
|
render()
|
|
end
|