dotfiles
My PC Dotfiles & Websitegit clone https://github.com/maydayv7/dotfiles
files/noctalia/keyhelp/lib/model.luau (4541B)
1local M = {}
2local PAGE_SIZE = 8
3
4local function text(value)
5 return type(value) == "string" and value:find("%S") ~= nil
6end
7
8local function array(value)
9 if type(value) ~= "table" then
10 return false
11 end
12 local count = 0
13 for key in pairs(value) do
14 if type(key) ~= "number" or key < 1 or key % 1 ~= 0 then
15 return false
16 end
17 count = count + 1
18 end
19 return count == #value
20end
21
22-- Validate content and assign runtime IDs
23function M.prepare(data)
24 if type(data) ~= "table" or data.version ~= 1 or not array(data.pages) or #data.pages == 0 then
25 return nil, "Expected version 1 and a nonempty pages array"
26 end
27 local aliases, desktops = {}, {}
28 for pageIndex, page in ipairs(data.pages) do
29 if
30 type(page) ~= "table"
31 or not text(page.name)
32 or not array(page.groups)
33 or #page.groups == 0
34 or not array(page.aliases)
35 then
36 return nil, "Invalid page at position " .. pageIndex
37 end
38 page.id = tostring(pageIndex)
39 if page.compositor ~= nil then
40 if (page.compositor ~= "hyprland" and page.compositor ~= "niri") or desktops[page.compositor] then
41 return nil, "Invalid or duplicate Desktop compositor"
42 end
43 desktops[page.compositor] = true
44 end
45 for _, alias in ipairs(page.aliases) do
46 if not text(alias) or aliases[alias:lower()] then
47 return nil, "Invalid or duplicate app alias"
48 end
49 aliases[alias:lower()] = true
50 end
51 for groupIndex, group in ipairs(page.groups) do
52 if type(group) ~= "table" or not text(group.name) or not array(group.entries) or #group.entries == 0 then
53 return nil, "Invalid group at position " .. groupIndex .. " in " .. page.name
54 end
55 group.id = tostring(groupIndex)
56 for entryIndex, entry in ipairs(group.entries) do
57 if
58 type(entry) ~= "table"
59 or not text(entry.keys)
60 or not text(entry.description)
61 or (entry.gap_before ~= nil and type(entry.gap_before) ~= "boolean")
62 then
63 return nil, "Invalid entry at position " .. entryIndex .. " in " .. page.name .. "/" .. group.name
64 end
65 entry.id = tostring(entryIndex)
66 end
67 end
68 end
69 if not desktops.hyprland or not desktops.niri then
70 return nil, "Both Desktop pages are required"
71 end
72 return data
73end
74
75function M.pages(data, compositor)
76 local result = {}
77 for _, page in ipairs(data.pages) do
78 if page.compositor == nil or page.compositor == compositor then
79 table.insert(result, page)
80 end
81 end
82 return result
83end
84
85function M.selectPage(pages, appIds)
86 for _, appId in ipairs(appIds or {}) do
87 for _, page in ipairs(pages) do
88 for _, alias in ipairs(page.aliases) do
89 if appId:lower() == alias:lower() then
90 return page.id
91 end
92 end
93 end
94 end
95 for _, page in ipairs(pages) do
96 if page.compositor then
97 return page.id
98 end
99 end
100 return pages[1].id
101end
102
103function M.appIds(compositor, window)
104 if type(window) ~= "table" then
105 return {}
106 end
107 local ids = {}
108 local fields = compositor == "hyprland" and { "class", "initialClass" } or { "app_id" }
109 for _, field in ipairs(fields) do
110 if text(window[field]) then
111 table.insert(ids, window[field])
112 end
113 end
114 return ids
115end
116
117function M.search(pages, selected, query)
118 local terms, result = {}, {}
119 for term in query:lower():gmatch("%S+") do
120 table.insert(terms, term)
121 end
122 for _, page in ipairs(pages) do
123 if page.id == selected then
124 for _, group in ipairs(page.groups) do
125 for _, entry in ipairs(group.entries) do
126 local haystack = table.concat({ page.name, group.name, entry.keys, entry.description }, " "):lower()
127 local matches = true
128 for _, term in ipairs(terms) do
129 if not haystack:find(term, 1, true) then
130 matches = false
131 break
132 end
133 end
134 if matches then
135 table.insert(
136 result,
137 { page = page, group = group, entry = entry, key = page.id .. "/" .. group.id .. "/" .. entry.id }
138 )
139 end
140 end
141 end
142 end
143 end
144 return result
145end
146
147function M.paginate(results, requested)
148 local count = math.max(1, math.ceil(#results / PAGE_SIZE))
149 local current = math.max(1, math.min(requested, count))
150 local rows = {}
151 for i = (current - 1) * PAGE_SIZE + 1, math.min(current * PAGE_SIZE, #results) do
152 table.insert(rows, results[i])
153 end
154 return rows, current, count
155end
156
157return M