U7: declare plugin_api 28, bind middle-click, translate the bar tooltip #16

Merged
yuris merged 1 commit from upgrade/u7-gestures into main 2026-09-01 19:50:37 +00:00
Owner

The bump buys one thing

plugin_api.h declares 26 feature constants; only seven are consulted anywhere in the shell,
and every one gates a manifest key, never a script binding. Of everything this upgrade
wanted, only kWidgetGestureActions (14) is enforced, at plugin_manifest.cpp:589-594.

Verified live earlier in this wave with a scratch plugin declaring plugin_api = 3:

P3|require ok=true val=MODULE-LOADED-AT-API-3
P3|argv exit=0 out=[ARGV-OK $notexpanded `nosub`]   -- literal $ and backticks
P3|getSetting ok=true
P3|formatTime ok=true val=2025-09-01 11:13
P3|openContextMenu callable=true ret=false          -- binding present; false is
                                                    -- the pointer-serial gate

So PRs #11-#15 -- argv, the scroll viewport, closures, the module split, the context menu --
all landed at API 3 and none needed this. The declaration goes to 28 here, for gestures.

Why only middle

PluginWidget creates its own input area accepting left/right/middle and dispatches them to
the onClick/onRightClick/onMiddleClick globals (plugin_widget.cpp:152-173). The bar
already claims left and right in Lua. Which wins when a gesture also claims a button is not
something I could establish: the probe showed a ui.button inside the plugin's tree
shadows the gesture, but that is a different mechanism from the widget-level globals, and
settling it needs real pointer events. The display was asleep, so that test could not run.

middle = "settings-open-widget" needs no such ruling -- nothing claims middle -- and it
matches what every built-in widget binds.

Rebinding without editing the manifest

[widget.actions] has no "call a Lua function" form: widget_action.cpp:26-56 parses a value
as none, exec <cmd>, or an IPC verb. A gesture reaches plugin code only by dispatching back
in, so onIpc is the rebinding surface and its event names are public API:

[widget.timew.actions]
right = "plugin yuris/timew:bar all toggle"   # tracking on right-click
left  = "plugin yuris/timew:bar all panel"    # panel on left-click

The suite pins onIpc's argv against onClick's own, not a literal -- a literal would be
a third spelling both could drift away from together.

The bar's first translation key

Deferred since PR #7: the panel translated the (untagged) sentinel and the bar rendered it
verbatim, so in a non-English locale the two surfaces disagreed about the same interval (panel
"sin etiqueta", bar "(untagged)"). The bar had no noctalia.tr call at all; now it has one,
using the panel's key.

setTooltip was a function() end stub that discarded its argument, so no test could have
caught this. It captures now.

Gates

viewModel: 12 states, 1739 field comparisons
ok
0 errors, 0 warnings

Mutations

# Mutation Result
M1 tooltip stops translating killed
M2 every tooltip becomes the key killed
M3 onIpc toggle event renamed killed
M4 onIpc toggle opens the panel instead killed
M5 unknown events fall through to the panel branch killed

M4 and M5 first died on a missing togglePanel stub rather than an assertion, which meant the
panel route was uncovered. Stubbed and asserted; M5 now fails at test.luau:858.

Not verified, and how to close it

The middle gesture has never been clicked. noctalia plugins lint does not validate gesture
keys or verbs -- the probe confirmed double_left lints clean and fails only at dispatch -- so
the manifest being accepted is not proof the binding fires. After merge the resolved set
appears in the journal as [DBG] [bar.actions] widget.timew: middle=settings-open-widget,
which proves resolution; firing needs a real middle-click.

## The bump buys one thing `plugin_api.h` declares 26 feature constants; only seven are consulted anywhere in the shell, and every one gates a **manifest key**, never a script binding. Of everything this upgrade wanted, only `kWidgetGestureActions` (14) is enforced, at `plugin_manifest.cpp:589-594`. Verified live earlier in this wave with a scratch plugin declaring `plugin_api = 3`: ``` P3|require ok=true val=MODULE-LOADED-AT-API-3 P3|argv exit=0 out=[ARGV-OK $notexpanded `nosub`] -- literal $ and backticks P3|getSetting ok=true P3|formatTime ok=true val=2025-09-01 11:13 P3|openContextMenu callable=true ret=false -- binding present; false is -- the pointer-serial gate ``` So PRs #11-#15 -- argv, the scroll viewport, closures, the module split, the context menu -- all landed at API 3 and none needed this. The declaration goes to 28 here, for gestures. ## Why only `middle` `PluginWidget` creates its own input area accepting left/right/middle and dispatches them to the `onClick`/`onRightClick`/`onMiddleClick` globals (`plugin_widget.cpp:152-173`). The bar already claims left and right in Lua. Which wins when a gesture *also* claims a button is not something I could establish: the probe showed a `ui.button` **inside** the plugin's tree shadows the gesture, but that is a different mechanism from the widget-level globals, and settling it needs real pointer events. The display was asleep, so that test could not run. `middle = "settings-open-widget"` needs no such ruling -- nothing claims middle -- and it matches what every built-in widget binds. ## Rebinding without editing the manifest `[widget.actions]` has no "call a Lua function" form: `widget_action.cpp:26-56` parses a value as `none`, `exec <cmd>`, or an IPC verb. A gesture reaches plugin code only by dispatching back in, so `onIpc` is the rebinding surface and its event names are public API: ```toml [widget.timew.actions] right = "plugin yuris/timew:bar all toggle" # tracking on right-click left = "plugin yuris/timew:bar all panel" # panel on left-click ``` The suite pins `onIpc`'s argv against **`onClick`'s own**, not a literal -- a literal would be a third spelling both could drift away from together. ## The bar's first translation key Deferred since PR #7: the panel translated the `(untagged)` sentinel and the bar rendered it verbatim, so in a non-English locale the two surfaces disagreed about the same interval (panel "sin etiqueta", bar "(untagged)"). The bar had no `noctalia.tr` call at all; now it has one, using the panel's key. `setTooltip` was a `function() end` stub that discarded its argument, so no test could have caught this. It captures now. ## Gates ``` viewModel: 12 states, 1739 field comparisons ok 0 errors, 0 warnings ``` ## Mutations | # | Mutation | Result | |---|----------|--------| | M1 | tooltip stops translating | killed | | M2 | every tooltip becomes the key | killed | | M3 | `onIpc` toggle event renamed | killed | | M4 | `onIpc` toggle opens the panel instead | killed | | M5 | unknown events fall through to the panel branch | killed | M4 and M5 first died on a missing `togglePanel` stub rather than an assertion, which meant the `panel` route was uncovered. Stubbed and asserted; M5 now fails at `test.luau:858`. ## Not verified, and how to close it The `middle` gesture has never been clicked. `noctalia plugins lint` does not validate gesture keys or verbs -- the probe confirmed `double_left` lints clean and fails only at dispatch -- so the manifest being accepted is not proof the binding fires. After merge the resolved set appears in the journal as `[DBG] [bar.actions] widget.timew: middle=settings-open-widget`, which proves resolution; firing needs a real middle-click.
The bump exists for exactly one feature: [widget.actions] is the only
capability in this upgrade the shell actually gates (plugin_manifest.cpp:589).
require, argv runAsync, UI closures, getSetting, formatTime and
panel.openContextMenu were all verified working at plugin_api = 3 and needed
no bump.

Left and right stay in Lua. The shell dispatches those to onClick/onRightClick
through PluginWidget's own input area, and which wins when a gesture also
claims the button is unverified -- so this binds middle only, matching what
every built-in widget binds. Every gesture remains user-rebindable without
editing the manifest: onIpc answers 'toggle' and 'panel', so a gesture pointed
at 'plugin yuris/timew:bar all toggle' reaches the same code the click does.

Also closes the bar's untranslated (untagged) tooltip, deferred since PR #7:
the panel translated the sentinel and the bar rendered it raw, so the two
surfaces disagreed about one interval in any non-English locale.
yuris merged commit 9e9cb35e3b into main 2026-09-01 19:50:37 +00:00
yuris deleted branch upgrade/u7-gestures 2026-09-01 19:50:37 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/noctalia-timew!16
No description provided.