mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Write half of new Lua API
This commit is contained in:
parent
3b88b3ff11
commit
97f9cf82d6
7 changed files with 605 additions and 34 deletions
8
api/lua_grpc/.luarc.json
Normal file
8
api/lua_grpc/.luarc.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"workspace.library": ["./"],
|
||||
"runtime.version": "Lua 5.4",
|
||||
|
||||
"--comment": "Format using Stylua instead",
|
||||
"format.enable": false,
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
local cqueues = require("cqueues")
|
||||
|
||||
---@type ClientModule
|
||||
local client = require("pinnacle.grpc.client")
|
||||
|
||||
---@class PinnacleModule
|
||||
local pinnacle = {}
|
||||
local pinnacle = {
|
||||
version = "v0alpha1",
|
||||
}
|
||||
|
||||
---@class Pinnacle
|
||||
---@field private config_client Client
|
||||
---@field private loop CqueuesLoop
|
||||
---@field input Input
|
||||
local Pinnacle = {}
|
||||
|
||||
|
@ -25,21 +25,21 @@ end
|
|||
---@param config_fn fun(pinnacle: Pinnacle)
|
||||
function pinnacle.setup(config_fn)
|
||||
require("pinnacle.grpc.protobuf").build_protos()
|
||||
|
||||
local loop = cqueues.new()
|
||||
---@type Client
|
||||
|
||||
local config_client = client.new(loop)
|
||||
|
||||
---@type Pinnacle
|
||||
local self = {
|
||||
config_client = config_client,
|
||||
loop = loop,
|
||||
input = require("pinnacle.input").new(config_client),
|
||||
}
|
||||
setmetatable(self, { __index = Pinnacle })
|
||||
|
||||
config_fn(self)
|
||||
|
||||
self.loop:loop()
|
||||
loop:loop()
|
||||
end
|
||||
|
||||
return pinnacle
|
||||
|
|
|
@ -26,20 +26,18 @@ local client = {}
|
|||
---@field loop CqueuesLoop
|
||||
local Client = {}
|
||||
|
||||
---@return H2Stream stream An http2 stream
|
||||
function Client:new_stream()
|
||||
return self.conn:new_stream()
|
||||
end
|
||||
|
||||
---@class GrpcRequestParams
|
||||
---@field service string
|
||||
---@field method string
|
||||
---@field request_type string
|
||||
---@field request_type string?
|
||||
---@field response_type string?
|
||||
---@field data table
|
||||
|
||||
---Send a synchronous unary request to the compositor.
|
||||
---
|
||||
---If `request_type` is not specified then it will default to
|
||||
---`method` .. "Request".
|
||||
---
|
||||
---If `response_type` is not specified then it will default to
|
||||
---`google.protobuf.Empty`.
|
||||
---@param grpc_request_params GrpcRequestParams
|
||||
|
@ -49,7 +47,7 @@ function Client:unary_request(grpc_request_params)
|
|||
|
||||
local service = grpc_request_params.service
|
||||
local method = grpc_request_params.method
|
||||
local request_type = grpc_request_params.request_type
|
||||
local request_type = grpc_request_params.request_type or method .. "Request"
|
||||
local response_type = grpc_request_params.response_type or "google.protobuf.Empty"
|
||||
local data = grpc_request_params.data
|
||||
|
||||
|
@ -129,6 +127,7 @@ function client.new(loop)
|
|||
loop = loop,
|
||||
}
|
||||
setmetatable(self, { __index = Client })
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +1,38 @@
|
|||
---@class InputModule
|
||||
local input = {}
|
||||
---The protobuf absolute path prefix
|
||||
local prefix = "pinnacle.input." .. require("pinnacle").version .. "."
|
||||
local service = prefix .. "InputService"
|
||||
|
||||
---@class Input
|
||||
---@field private config_client Client
|
||||
local Input = {}
|
||||
---@type table<string, { request_type: string?, response_type: string? }>
|
||||
---@enum (key) InputServiceMethod
|
||||
local rpc_types = {
|
||||
SetKeybind = {
|
||||
response_type = "SetKeybindResponse",
|
||||
},
|
||||
SetMousebind = {
|
||||
response_type = "SetMousebindResponse",
|
||||
},
|
||||
SetXkbConfig = {},
|
||||
SetRepeatRate = {},
|
||||
SetLibinputSetting = {},
|
||||
}
|
||||
|
||||
---Build GrpcRequestParams
|
||||
---@param method InputServiceMethod
|
||||
---@param data table
|
||||
---@return GrpcRequestParams
|
||||
local function build_grpc_request_params(method, data)
|
||||
local req_type = rpc_types[method].request_type
|
||||
local resp_type = rpc_types[method].response_type
|
||||
|
||||
---@type GrpcRequestParams
|
||||
return {
|
||||
service = service,
|
||||
method = method,
|
||||
request_type = req_type and prefix .. req_type,
|
||||
response_type = resp_type and prefix .. resp_type,
|
||||
data = data,
|
||||
}
|
||||
end
|
||||
|
||||
---@enum Modifier
|
||||
local modifier = {
|
||||
|
@ -13,7 +42,49 @@ local modifier = {
|
|||
SUPER = 4,
|
||||
}
|
||||
|
||||
---@param mods Modifier[]
|
||||
---@enum MouseButton
|
||||
local mouse_button = {
|
||||
--- Left
|
||||
[1] = 0x110,
|
||||
--- Right
|
||||
[2] = 0x111,
|
||||
--- Middle
|
||||
[3] = 0x112,
|
||||
--- Side
|
||||
[4] = 0x113,
|
||||
--- Extra
|
||||
[5] = 0x114,
|
||||
--- Forward
|
||||
[6] = 0x115,
|
||||
--- Back
|
||||
[7] = 0x116,
|
||||
left = 0x110,
|
||||
right = 0x111,
|
||||
middle = 0x112,
|
||||
side = 0x113,
|
||||
extra = 0x114,
|
||||
forward = 0x115,
|
||||
back = 0x116,
|
||||
}
|
||||
|
||||
---@enum MouseEdge
|
||||
local mouse_edge = {
|
||||
PRESS = 1,
|
||||
RELEASE = 2,
|
||||
}
|
||||
|
||||
---@class InputModule
|
||||
local input = {}
|
||||
|
||||
---@class Input
|
||||
---@field private config_client Client
|
||||
local Input = {
|
||||
mod = modifier,
|
||||
btn = mouse_button,
|
||||
edge = mouse_edge,
|
||||
}
|
||||
|
||||
---@param mods Modifier[] TODO: accept strings of mods
|
||||
---@param key integer | string
|
||||
---@param action fun()
|
||||
function Input:set_keybind(mods, key, action)
|
||||
|
@ -26,17 +97,63 @@ function Input:set_keybind(mods, key, action)
|
|||
xkb_name = key
|
||||
end
|
||||
|
||||
self.config_client:server_streaming_request({
|
||||
service = "pinnacle.input.v0alpha1.InputService",
|
||||
method = "SetKeybind",
|
||||
request_type = "pinnacle.input.v0alpha1.SetKeybindRequest",
|
||||
data = {
|
||||
self.config_client:server_streaming_request(
|
||||
build_grpc_request_params("SetKeybind", {
|
||||
modifiers = mods,
|
||||
-- oneof not violated because `key` can't be both an int and string
|
||||
raw_code = raw_code,
|
||||
xkb_name = xkb_name,
|
||||
},
|
||||
}, action)
|
||||
}),
|
||||
action
|
||||
)
|
||||
end
|
||||
|
||||
---Set a mousebind.
|
||||
---
|
||||
---@param mods Modifier[]
|
||||
---@param button MouseButton
|
||||
---@param edge MouseEdge|"press"|"release"
|
||||
---@param action fun()
|
||||
function Input:set_mousebind(mods, button, edge, action)
|
||||
local edge = edge
|
||||
if edge == "press" then
|
||||
edge = mouse_edge.PRESS
|
||||
elseif edge == "release" then
|
||||
edge = mouse_edge.RELEASE
|
||||
end
|
||||
|
||||
self.config_client:server_streaming_request(
|
||||
build_grpc_request_params("SetMousebind", {
|
||||
modifiers = mods,
|
||||
button = button,
|
||||
edge = edge,
|
||||
}),
|
||||
action
|
||||
)
|
||||
end
|
||||
|
||||
---@class XkbConfig
|
||||
---@field rules string?
|
||||
---@field model string?
|
||||
---@field layout string?
|
||||
---@field variant string?
|
||||
---@field options string?
|
||||
|
||||
---Set the xkbconfig for your keyboard.
|
||||
---
|
||||
---@param xkb_config XkbConfig
|
||||
function Input:set_xkb_config(xkb_config)
|
||||
self.config_client:unary_request(build_grpc_request_params("SetXkbConfig", xkb_config))
|
||||
end
|
||||
|
||||
---Set the keyboard's repeat rate and delay.
|
||||
---
|
||||
---@param rate integer The time between repeats, in milliseconds
|
||||
---@param delay integer The duration a key needs to be held down before repeating starts, in milliseconds
|
||||
function Input:set_repeat_rate(rate, delay)
|
||||
self.config_client:unary_request(build_grpc_request_params("SetRepeatRate", {
|
||||
rate = rate,
|
||||
delay = delay,
|
||||
}))
|
||||
end
|
||||
|
||||
function input.new(config_client)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
local cqueues = require("cqueues")
|
||||
|
||||
local loop = {
|
||||
loop = cqueues.new(),
|
||||
}
|
||||
|
||||
return loop
|
165
api/lua_grpc/pinnacle/output.lua
Normal file
165
api/lua_grpc/pinnacle/output.lua
Normal file
|
@ -0,0 +1,165 @@
|
|||
---The protobuf absolute path prefix
|
||||
local prefix = "pinnacle.output." .. require("pinnacle").version .. "."
|
||||
local service = prefix .. "OutputService"
|
||||
|
||||
---@type table<string, { request_type: string?, response_type: string? }>
|
||||
---@enum (key) OutputServiceMethod
|
||||
local rpc_types = {
|
||||
SetLocation = {},
|
||||
ConnectForAll = {
|
||||
response_type = "ConnectForAllResponse",
|
||||
},
|
||||
Get = {
|
||||
response_type = "GetResponse",
|
||||
},
|
||||
GetProperties = {
|
||||
response_type = "GetPropertiesResponse",
|
||||
},
|
||||
}
|
||||
|
||||
---Build GrpcRequestParams
|
||||
---@param method OutputServiceMethod
|
||||
---@param data table
|
||||
---@return GrpcRequestParams
|
||||
local function build_grpc_request_params(method, data)
|
||||
local req_type = rpc_types[method].request_type
|
||||
local resp_type = rpc_types[method].response_type
|
||||
|
||||
---@type GrpcRequestParams
|
||||
return {
|
||||
service = service,
|
||||
method = method,
|
||||
request_type = req_type and prefix .. req_type,
|
||||
response_type = resp_type and prefix .. resp_type,
|
||||
data = data,
|
||||
}
|
||||
end
|
||||
|
||||
---@class OutputHandleModule
|
||||
local output_handle = {}
|
||||
|
||||
---@class OutputHandle
|
||||
---@field private config_client Client
|
||||
---@field name string The unique name of this output
|
||||
local OutputHandle = {}
|
||||
|
||||
---@class OutputModule
|
||||
---@field private handle OutputHandleModule
|
||||
local output = {}
|
||||
output.handle = output_handle
|
||||
|
||||
---@class Output
|
||||
---@field private config_client Client
|
||||
local Output = {}
|
||||
|
||||
---Get all outputs.
|
||||
---
|
||||
---@return OutputHandle[]
|
||||
function Output:get_all()
|
||||
local response = self.config_client:unary_request(build_grpc_request_params("Get", {}))
|
||||
|
||||
---@type OutputHandle[]
|
||||
local handles = {}
|
||||
|
||||
for _, output_name in pairs(response.output_names) do
|
||||
table.insert(handles, output_handle.new(self.config_client, output_name))
|
||||
end
|
||||
|
||||
return handles
|
||||
end
|
||||
|
||||
---@param name string The name of the port the output is connected to
|
||||
---@return OutputHandle | nil
|
||||
function Output:get_by_name(name)
|
||||
local handles = self:get_all()
|
||||
|
||||
for _, handle in pairs(handles) do
|
||||
if handle.name == name then
|
||||
return handle
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
---@return OutputHandle | nil
|
||||
function Output:get_focused()
|
||||
local handles = self:get_all()
|
||||
|
||||
for _, handle in pairs(handles) do
|
||||
if handle:props().focused then
|
||||
return handle
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param callback fun(output: OutputHandle)
|
||||
function Output:connect_for_all(callback)
|
||||
self.config_client:server_streaming_request(build_grpc_request_params("ConnectForAll", {}), function(response)
|
||||
local output_name = response.output_name
|
||||
local handle = output_handle.new(self.config_client, output_name)
|
||||
callback(handle)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param loc { x: integer?, y: integer? }
|
||||
function OutputHandle:set_location(loc)
|
||||
self.config_client:unary_request(build_grpc_request_params("SetLocation", {
|
||||
output_name = self.name,
|
||||
x = loc.x,
|
||||
y = loc.y,
|
||||
}))
|
||||
end
|
||||
|
||||
---@class OutputProperties
|
||||
---@field make string?
|
||||
---@field model string?
|
||||
---@field x integer?
|
||||
---@field y integer?
|
||||
---@field pixel_width integer?
|
||||
---@field pixel_height integer?
|
||||
---@field refresh_rate integer?
|
||||
---@field physical_width integer?
|
||||
---@field physical_height integer?
|
||||
---@field focused boolean?
|
||||
---@field tags TagHandle[]
|
||||
|
||||
---Get all properties of this output.
|
||||
---@return OutputProperties
|
||||
function OutputHandle:props()
|
||||
local response =
|
||||
self.config_client:unary_request(build_grpc_request_params("GetProperties", { output_name = self.name }))
|
||||
|
||||
local handles = require("pinnacle.tag").handle.new_from_table(self.config_client, response.tag_ids)
|
||||
|
||||
response.tags = handles
|
||||
response.tag_ids = nil
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
---@return Output
|
||||
function output.new(config_client)
|
||||
---@type Output
|
||||
local self = {
|
||||
config_client = config_client,
|
||||
}
|
||||
setmetatable(self, { __index = Output })
|
||||
return self
|
||||
end
|
||||
|
||||
---Create a new `OutputHandle` from its raw name.
|
||||
---@param output_name string
|
||||
function output_handle.new(config_client, output_name)
|
||||
---@type OutputHandle
|
||||
local self = {
|
||||
config_client = config_client,
|
||||
name = output_name,
|
||||
}
|
||||
setmetatable(self, { __index = OutputHandle })
|
||||
return self
|
||||
end
|
||||
|
||||
return output
|
289
api/lua_grpc/pinnacle/tag.lua
Normal file
289
api/lua_grpc/pinnacle/tag.lua
Normal file
|
@ -0,0 +1,289 @@
|
|||
---The protobuf absolute path prefix
|
||||
local prefix = "pinnacle.tag." .. require("pinnacle").version .. "."
|
||||
local service = prefix .. "TagService"
|
||||
|
||||
---@type table<string, { request_type: string?, response_type: string? }>
|
||||
---@enum (key) TagServiceMethod
|
||||
local rpc_types = {
|
||||
SetActive = {},
|
||||
SwitchTo = {},
|
||||
Add = {
|
||||
response_type = "AddResponse",
|
||||
},
|
||||
Remove = {},
|
||||
SetLayout = {},
|
||||
Get = {
|
||||
response_type = "GetResponse",
|
||||
},
|
||||
GetProperties = {
|
||||
response_type = "GetPropertiesResponse",
|
||||
},
|
||||
}
|
||||
|
||||
---Build GrpcRequestParams
|
||||
---@param method TagServiceMethod
|
||||
---@param data table
|
||||
---@return GrpcRequestParams
|
||||
local function build_grpc_request_params(method, data)
|
||||
local req_type = rpc_types[method].request_type
|
||||
local resp_type = rpc_types[method].response_type
|
||||
|
||||
---@type GrpcRequestParams
|
||||
return {
|
||||
service = service,
|
||||
method = method,
|
||||
request_type = req_type and prefix .. req_type,
|
||||
response_type = resp_type and prefix .. resp_type,
|
||||
data = data,
|
||||
}
|
||||
end
|
||||
|
||||
---@class TagHandleModule
|
||||
local tag_handle = {}
|
||||
|
||||
---@class TagHandle
|
||||
---@field private config_client Client
|
||||
---@field id integer
|
||||
local TagHandle = {}
|
||||
|
||||
---@class TagModule
|
||||
---@field private handle TagHandleModule
|
||||
local tag = {}
|
||||
tag.handle = tag_handle
|
||||
|
||||
---@class Tag
|
||||
---@field private config_client Client
|
||||
local Tag = {}
|
||||
|
||||
---Get all tags across all outputs.
|
||||
---
|
||||
---@return TagHandle[]
|
||||
function Tag:get_all()
|
||||
local response = self.config_client:unary_request(build_grpc_request_params("Get", {}))
|
||||
|
||||
---@type TagHandle[]
|
||||
local handles = {}
|
||||
|
||||
for _, id in pairs(response.tag_ids) do
|
||||
table.insert(handles, tag_handle.new(self.config_client, id))
|
||||
end
|
||||
|
||||
return handles
|
||||
end
|
||||
|
||||
---Add tags with the given names to the specified output.
|
||||
---
|
||||
---Returns handles to the created tags.
|
||||
---
|
||||
---@param output OutputHandle
|
||||
---@param ... string
|
||||
---
|
||||
---@return TagHandle[]
|
||||
---
|
||||
---@overload fun(output: OutputHandle, tag_names: string[])
|
||||
function Tag:add(output, ...)
|
||||
local tag_names = { ... }
|
||||
if type(tag_names[1]) == "table" then
|
||||
tag_names = tag_names[1] --[=[@as string[]]=]
|
||||
end
|
||||
|
||||
local response = self.config_client:unary_request(build_grpc_request_params("Add", {
|
||||
output_name = output.name,
|
||||
tag_names = tag_names,
|
||||
}))
|
||||
|
||||
---@type TagHandle[]
|
||||
local handles = {}
|
||||
|
||||
for _, id in pairs(response.tag_ids) do
|
||||
table.insert(handles, tag_handle.new(self.config_client, id))
|
||||
end
|
||||
|
||||
return handles
|
||||
end
|
||||
|
||||
---Remove the given tags.
|
||||
---
|
||||
---@param tags TagHandle[]
|
||||
function Tag:remove(tags)
|
||||
---@type integer[]
|
||||
local ids = {}
|
||||
|
||||
for _, tg in pairs(tags) do
|
||||
table.insert(ids, tg.id)
|
||||
end
|
||||
|
||||
self.config_client:unary_request(build_grpc_request_params("Remove", { tag_ids = ids }))
|
||||
end
|
||||
|
||||
---@class LayoutCycler
|
||||
---@field next fun(output: OutputHandle)
|
||||
---@field prev fun(output: OutputHandle)
|
||||
|
||||
--- TODO: docs
|
||||
---@param layouts Layout[]
|
||||
---
|
||||
---@return LayoutCycler
|
||||
function Tag:new_layout_cycler(layouts)
|
||||
local indices = {}
|
||||
|
||||
if #layouts == 0 then
|
||||
return {
|
||||
next = function(_) end,
|
||||
prev = function(_) end,
|
||||
}
|
||||
end
|
||||
|
||||
---@type LayoutCycler
|
||||
return {
|
||||
next = function(output)
|
||||
local tags = output:props().tags
|
||||
|
||||
for _, tg in pairs(tags) do
|
||||
if tg:props().active then
|
||||
local id = tg.id
|
||||
if #layouts == 1 then
|
||||
indices[id] = 1
|
||||
elseif indices[id] == nil then
|
||||
indices[id] = 2
|
||||
else
|
||||
if indices[id] + 1 > #layouts then
|
||||
indices[id] = 1
|
||||
else
|
||||
indices[id] = indices[id] + 1
|
||||
end
|
||||
end
|
||||
|
||||
tg:set_layout(layouts[indices[id]])
|
||||
break
|
||||
end
|
||||
end
|
||||
end,
|
||||
prev = function(output)
|
||||
local tags = output:props().tags
|
||||
|
||||
for _, tg in pairs(tags) do
|
||||
if tg:props().active then
|
||||
local id = tg.id
|
||||
|
||||
if #layouts == 1 then
|
||||
indices[id] = 1
|
||||
elseif indices[id] == nil then
|
||||
indices[id] = #layouts - 1
|
||||
else
|
||||
if indices[id] - 1 < 1 then
|
||||
indices[id] = #layouts
|
||||
else
|
||||
indices[id] = indices[id] - 1
|
||||
end
|
||||
end
|
||||
|
||||
tg:set_layout(layouts[indices[id]])
|
||||
break
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
---Remove this tag.
|
||||
function TagHandle:remove()
|
||||
self.config_client:unary_request(build_grpc_request_params("Remove", { tag_ids = { self.id } }))
|
||||
end
|
||||
|
||||
---@enum (key) Layout
|
||||
local _layouts = {
|
||||
master_stack = 1,
|
||||
dwindle = 2,
|
||||
spiral = 3,
|
||||
corner_top_left = 4,
|
||||
corner_top_right = 5,
|
||||
corner_bottom_left = 6,
|
||||
corner_bottom_right = 7,
|
||||
}
|
||||
|
||||
---@param layout Layout
|
||||
function TagHandle:set_layout(layout)
|
||||
local layout = _layouts[layout]
|
||||
|
||||
self.config_client:unary_request(build_grpc_request_params("SetLayout", {
|
||||
tag_id = self.id,
|
||||
layout = layout,
|
||||
}))
|
||||
end
|
||||
|
||||
---Activate this tag and deactivate all other ones on the same output.
|
||||
function TagHandle:switch_to()
|
||||
self.config_client:unary_request(build_grpc_request_params("SwitchTo", { tag_id = self.id }))
|
||||
end
|
||||
|
||||
---Set whether or not this tag is active.
|
||||
---
|
||||
---@param active boolean
|
||||
function TagHandle:set_active(active)
|
||||
self.config_client:unary_request(build_grpc_request_params("SetActive", { tag_id = self.id, set = active }))
|
||||
end
|
||||
|
||||
---Toggle this tag's active state.
|
||||
function TagHandle:toggle_active()
|
||||
self.config_client:unary_request(build_grpc_request_params("SetActive", { tag_id = self.id, toggle = {} }))
|
||||
end
|
||||
|
||||
---@class TagProperties
|
||||
---@field active boolean?
|
||||
---@field name string?
|
||||
---@field output OutputHandle?
|
||||
|
||||
---Get all properties of this tag.
|
||||
---
|
||||
---@return TagProperties
|
||||
function TagHandle:props()
|
||||
local response = self.config_client:unary_request(build_grpc_request_params("GetProperties", { tag_id = self.id }))
|
||||
|
||||
return {
|
||||
active = response.active,
|
||||
name = response.name,
|
||||
output = response.output_name
|
||||
and require("pinnacle.output").handle.new(self.config_client, response.output_name),
|
||||
}
|
||||
end
|
||||
|
||||
---@return Tag
|
||||
function tag.new(config_client)
|
||||
---@type Tag
|
||||
local self = {
|
||||
config_client = config_client,
|
||||
}
|
||||
setmetatable(self, { __index = Tag })
|
||||
return self
|
||||
end
|
||||
|
||||
---Create a new `TagHandle` from an id.
|
||||
---@param config_client Client
|
||||
---@param tag_id integer
|
||||
---@return TagHandle
|
||||
function tag_handle.new(config_client, tag_id)
|
||||
---@type TagHandle
|
||||
local self = {
|
||||
config_client = config_client,
|
||||
id = tag_id,
|
||||
}
|
||||
setmetatable(self, { __index = TagHandle })
|
||||
return self
|
||||
end
|
||||
|
||||
---@param config_client Client
|
||||
---@param tag_ids integer[]
|
||||
---@return TagHandle[]
|
||||
function tag_handle.new_from_table(config_client, tag_ids)
|
||||
---@type TagHandle[]
|
||||
local handles = {}
|
||||
|
||||
for _, id in pairs(tag_ids) do
|
||||
table.insert(handles, tag_handle.new(config_client, id))
|
||||
end
|
||||
|
||||
return handles
|
||||
end
|
||||
|
||||
return tag
|
Loading…
Reference in a new issue