From 80c3cf4780ca8adf083b0f3c0374bdacd003dec0 Mon Sep 17 00:00:00 2001 From: Derek Miller <1340081+derek-miller@users.noreply.github.com> Date: Tue, 10 Nov 2026 21:47:37 -0600 Subject: [PATCH 1/4] feat(ui): the properties card follows the Support Code's window by itself --- template/CONTRIBUTING.md.jinja | 6 ++++-- template/src/lib/support-code.lua | 19 +++++++++++++++++-- template/src/lib/ui/tab-properties.lua | 12 +++++++++++- template/test/test_support_code.lua | 23 +++++++++++++++++++++++ template/test/test_ui_tab_properties.lua | 12 ++++++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) diff --git a/template/CONTRIBUTING.md.jinja b/template/CONTRIBUTING.md.jinja index 42096a8..50984bb 100644 --- a/template/CONTRIBUTING.md.jinja +++ b/template/CONTRIBUTING.md.jinja @@ -672,8 +672,10 @@ end The library clears the property at once, keeps its value out of the dispatcher's Trace output, and prints its answer to the Lua output whatever Log Mode is. While the window is open, Log Level also offers Trace and Ultra (see -[Log Level](#log-level)). A driver that shows more while the window is open -(extra properties) passes `supportCode:onChange(function(open) ... end)`. +[Log Level](#log-level)). A web tab's properties card follows the window by +itself, once the driver calls `lib.ui.tab-properties`' `serve()`. A driver that +shows more elsewhere while the window is open passes +`supportCode:onChange(function(open) ... end)`. ### Log Level diff --git a/template/src/lib/support-code.lua b/template/src/lib/support-code.lua index c17a306..54481e1 100644 --- a/template/src/lib/support-code.lua +++ b/template/src/lib/support-code.lua @@ -15,6 +15,7 @@ OPC.suppressDebug["Support Code"] = true --- @field _secret string The build secret the day's code comes from. --- @field _open boolean Whether this code opened the Lua window. --- @field _onChange? fun(open: boolean) Called as the code opens and closes the window. +--- @field _followers fun(open: boolean)[] The libraries told the same, after the driver. local SupportCode = {} SupportCode.__index = SupportCode @@ -40,6 +41,7 @@ function SupportCode:new(secret) local instance = setmetatable({}, self) instance._secret = secret or SECRET instance._open = false + instance._followers = {} return instance end @@ -81,8 +83,8 @@ function SupportCode:accepts(value) return false end ---- Tells the driver's `onChange` callback, if any. A raise there would reach the dispatcher's error print, which shows ---- the typed value, so it is caught and reported without it. +--- Tells the driver's `onChange` callback, if any, then each follower. A raise there would reach the dispatcher's +--- error print, which shows the typed value, so it is caught and reported without it. --- @param open boolean Whether the window is now open. function SupportCode:_tell(open) if self._onChange then @@ -91,6 +93,12 @@ function SupportCode:_tell(open) print("The Support Code's onChange failed: " .. tostring(err)) end end + for _, follower in ipairs(self._followers) do + local ok, err = pcall(follower, open) + if not ok then + print("A Support Code follower failed: " .. tostring(err)) + end + end end --- Lets Log Level reach Trace and Ultra, or stops it at Debug. Caught as `_tell` is, since a raise would print the @@ -110,6 +118,13 @@ function SupportCode:onChange(callback) self._onChange = callback end +--- Calls `callback(open)` as the window opens and closes, alongside the driver's `onChange`, for a library whose view +--- changes with it: lib.ui.tab-properties' card, whose Log Level list does. +--- @param callback fun(open: boolean) +function SupportCode:follow(callback) + table.insert(self._followers, callback) +end + --- Takes what was typed into the Support Code property. The property is cleared at once, so the code is never left --- in the project. Today's code opens the Lua window and Log Level's Trace and Ultra for an hour, or closes them while --- open. diff --git a/template/src/lib/ui/tab-properties.lua b/template/src/lib/ui/tab-properties.lua index 514097b..5176f2c 100644 --- a/template/src/lib/ui/tab-properties.lua +++ b/template/src/lib/ui/tab-properties.lua @@ -11,6 +11,9 @@ require("drivers-common-public.global.lib") local log = require("lib.logging") local page = require("lib.ui.page") local pageParams = require("lib.ui.page-params") +--#ifdef DRIVERCENTRAL +local supportCode = require("lib.support-code") +--#endif local SHOWN_TYPES = { LIST = true, STRING = true, RANGED_INTEGER = true } @@ -138,7 +141,8 @@ function TabProperties:set(name, value) return "The tab cannot set " .. tostring(name) .. "." end ---- Answers the properties card's two commands. Call it once, as the driver loads. +--- Answers the properties card's two commands, and in a DriverCentral build sends the card its list again as the +--- Support Code opens and closes the Lua window. Call it once, as the driver loads. function TabProperties:serve() UIR._GET_PROPERTIES = function() page.send("PROPERTIES", { properties = self:list() }) @@ -151,6 +155,12 @@ function TabProperties:serve() page.send("PROPERTY_ERROR", { property = tostring(params.property), error = err }) end end + --#ifdef DRIVERCENTRAL + -- The Support Code widens Log Level's list and its hour's end narrows it, and no property handler runs for either. + supportCode:follow(function() + UIR._GET_PROPERTIES() + end) + --#endif end --- Calls `push` after each change to a property the tab shows, wherever it was made. Call it once the load's own diff --git a/template/test/test_support_code.lua b/template/test/test_support_code.lua index 1d3a51f..94d61e6 100644 --- a/template/test/test_support_code.lua +++ b/template/test/test_support_code.lua @@ -140,6 +140,29 @@ T.eq("and when the hour does", told, { true, false, true, false }) enter(sc, "nonsense") T.eq("but not for a wrong code", told, { true, false, true, false }) +T.section("A library can follow it too, alongside the driver") +do + local lib = getmetatable(supportCode):new(SECRET) + local driverTold, libTold = {}, {} + lib:onChange(function(open) + driverTold[#driverTold + 1] = open + end) + lib:follow(function(open) + libTold[#libTold + 1] = open + end) + lib:follow(function() + error("the library's hook broke") + end) + local traced = enter(lib, "VEAFA-RSBWM") + T.eq("a follower is told when the code opens it", libTold, { true }) + T.eq("and so is the driver", driverTold, { true }) + T.contains("a follower that raises is reported", traced, "the library's hook broke") + T.excludes("without the typed value", traced, "VEAFA") + enter(lib, "VEAFA-RSBWM") + T.eq("and the follower is told when it closes", libTold, { true, false }) + lib:close() +end + T.section("The code never reaches the Lua output") -- The driver's handler, as CONTRIBUTING.md shows it. function OPC.Support_Code(propertyValue) diff --git a/template/test/test_ui_tab_properties.lua b/template/test/test_ui_tab_properties.lua index c0d73cb..a32c39e 100644 --- a/template/test/test_ui_tab_properties.lua +++ b/template/test/test_ui_tab_properties.lua @@ -143,4 +143,16 @@ sent = {} UIR._SET_PROPERTY({ DATA = "not base64 json" }) T.eq("a body that does not decode is refused as no property", sent[1].data.error, "The tab cannot set nil.") +T.section("The card follows the Support Code's window") +do + -- No secret in a test build, so the window's change is told directly. + local code = require("lib.support-code") + sent = {} + code:_tell(true) + T.eq("opening it sends the card its list", sent[1] and sent[1].command, "PROPERTIES") + sent = {} + code:_tell(false) + T.eq("and so does the hour's end", sent[1] and sent[1].command, "PROPERTIES") +end + T.finish() From ee1416786449dc511be749950bc409a39634f718 Mon Sep 17 00:00:00 2001 From: Derek Miller <1340081+derek-miller@users.noreply.github.com> Date: Tue, 10 Nov 2026 21:53:54 -0600 Subject: [PATCH 2/4] feat(ui): style.css offers the widgets the tabs repeat: chips, states, labels, rows, switches, popovers, forms and layout helpers --- template/CONTRIBUTING.md.jinja | 4 +- template/src/lib/ui/style.css | 142 +++++++++++++++++++++++++++++++- template/test/test_ui_pages.lua | 9 ++ 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/template/CONTRIBUTING.md.jinja b/template/CONTRIBUTING.md.jinja index 50984bb..f0e7176 100644 --- a/template/CONTRIBUTING.md.jinja +++ b/template/CONTRIBUTING.md.jinja @@ -838,7 +838,9 @@ such as `drivers/tplink_light/www/documentation/index.md` in others, so a page keeps only its own views and its driver only its own commands: - `style.css`: the house look, from the tab bar and cards to form fields, - tables, chips, the segmented switch and a list beside its editor + tables, chips, the segmented switch and a list beside its editor, and a set of + `.kit-*` widgets and layout helpers to use as needed, as with a CSS framework: + states, tags, counts, a form grid, a switch, popovers and more - `page.js`: the page's link to its driver, the live-link and demo banners, the tabs, the properties card and demo mode - `variables.js` and `variables.css`: the variable browser, with diff --git a/template/src/lib/ui/style.css b/template/src/lib/ui/style.css index 8ac585b..712fc23 100644 --- a/template/src/lib/ui/style.css +++ b/template/src/lib/ui/style.css @@ -1,8 +1,9 @@ /* The look of a driver's custom Composer tab. A page embeds it first in its