Skip to content

Commit 6f1695f

Browse files
committed
fix(interactive): bridge Register-StrataCommand keymaps into the interaction CommandRegistry
Register-StrataCommand handlers (cmd:*) are now registered into the host's CommandRegistry so CSS command: keymaps actually dispatch to the author's scriptblock. Also corrects a stale README note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ed63860 commit 6f1695f

4 files changed

Lines changed: 53 additions & 6 deletions

File tree

src/Strata.Dsl.TerminalGui/StrataInteractiveHost.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ public sealed class StrataInteractiveHost
2020
private readonly StrataElement _root;
2121
private readonly StrataStore _store;
2222
private readonly Action<string, StrataUiEvent> _invokeHandler;
23+
private readonly string[] _commandNames;
2324

24-
private StrataInteractiveHost(StrataElement root, StrataStore store, Action<string, StrataUiEvent> invokeHandler)
25+
private StrataInteractiveHost(
26+
StrataElement root,
27+
StrataStore store,
28+
Action<string, StrataUiEvent> invokeHandler,
29+
IEnumerable<string>? commandNames)
2530
{
2631
_root = root;
2732
_store = store;
2833
_invokeHandler = invokeHandler;
34+
_commandNames = commandNames?.ToArray() ?? Array.Empty<string>();
2935
}
3036

3137
/// <summary>Copy each bound list's resolved array into its <c>items</c> attribute for the projection.</summary>
@@ -62,6 +68,21 @@ public static void WriteFieldValue(StrataElement field, StrataStore store, strin
6268
}
6369
}
6470

71+
/// <summary>
72+
/// Build the <see cref="CommandHandler"/> that bridges a CSS <c>command:</c> keymap named
73+
/// <paramref name="name"/> to the author's dispatcher: when the command fires, invoke the
74+
/// PowerShell handler registered under <c>cmd:&lt;name&gt;</c> with the focused element.
75+
/// </summary>
76+
public static CommandHandler MakeCommandHandler(
77+
string name, StrataStore store, StrataElement fallback, Action<string, StrataUiEvent> invoke)
78+
{
79+
ArgumentNullException.ThrowIfNull(name);
80+
ArgumentNullException.ThrowIfNull(store);
81+
ArgumentNullException.ThrowIfNull(fallback);
82+
ArgumentNullException.ThrowIfNull(invoke);
83+
return ctx => invoke($"cmd:{name}", new StrataUiEvent(store, ctx.Node as StrataElement ?? fallback, null));
84+
}
85+
6586
private static JsonNode? ResolveFirst(string jsonPath, JsonObject state)
6687
{
6788
var result = JsonPath.Parse(jsonPath).Evaluate(state);
@@ -81,14 +102,15 @@ public static string Run(
81102
StrataElement root,
82103
string cssPath,
83104
StrataStore store,
84-
Action<string, StrataUiEvent> invokeHandler)
105+
Action<string, StrataUiEvent> invokeHandler,
106+
IEnumerable<string>? commandNames = null)
85107
{
86108
ArgumentNullException.ThrowIfNull(root);
87109
ArgumentNullException.ThrowIfNull(cssPath);
88110
ArgumentNullException.ThrowIfNull(store);
89111
ArgumentNullException.ThrowIfNull(invokeHandler);
90112

91-
var host = new StrataInteractiveHost(root, store, invokeHandler);
113+
var host = new StrataInteractiveHost(root, store, invokeHandler, commandNames);
92114

93115
var registry = StylingProperties.CreateRegistry();
94116
InteractionProperties.RegisterAll(registry);
@@ -130,6 +152,10 @@ private string RunLoop(
130152

131153
using var input = new TerminalGuiInputSource();
132154
var commands = new CommandRegistry();
155+
foreach (var name in _commandNames)
156+
{
157+
commands.Register(name, MakeCommandHandler(name, _store, _root, _invokeHandler));
158+
}
133159
using var interaction = new InteractionHost(input, commands);
134160

135161
void Render()

src/Strata.PowerShell/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ A reusable layout is a function returning a parameterized subtree — see
6464
`samples/Strata.Demo.PowerShell/StrataTemplates.psm1` (`HostCard`), reused across the ping and
6565
uptime monitor samples.
6666

67-
Rendering uses the Spectre live-redraw loop (ideal for monitoring dashboards). A Terminal.Gui
68-
interactive projection (focus/keyboard) over the same store + widgets is an additive future option.
67+
Rendering uses the Spectre live-redraw loop (ideal for monitoring dashboards). For interactive
68+
full-screen apps with focus and keyboard input, use `Show-StrataApp` — see the **Interactive apps**
69+
section above and `samples/Strata.Demo.PowerShell/db-query.ps1`.

src/Strata.PowerShell/Strata.PowerShell.psm1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ function Show-StrataApp {
187187
}
188188
}.GetNewClosure()
189189
$action = [System.Action[string, Strata.Dsl.TerminalGui.StrataUiEvent]]$dispatch
190-
[Strata.Dsl.TerminalGui.StrataInteractiveHost]::Run($Layout, $path, $Store, $action)
190+
$commandNames = [string[]]@($script:StrataHandlers.Keys | Where-Object { $_ -like 'cmd:*' } | ForEach-Object { $_.Substring(4) })
191+
[Strata.Dsl.TerminalGui.StrataInteractiveHost]::Run($Layout, $path, $Store, $action, $commandNames)
191192
}
192193

193194
Export-ModuleMember -Function Element, Stack, Card, Text, Graph, Button, TextField, List, Show-Styled, New-StrataStore, Update-StrataStore, Start-StrataApp, Register-StrataCommand, Show-StrataApp

tests/Strata.Dsl.TerminalGui.Tests/HostWiringTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using FluentAssertions;
22
using Strata.Dsl;
33
using Strata.Dsl.TerminalGui;
4+
using Strata.Interaction;
45
using Xunit;
56

67
namespace Strata.Dsl.TerminalGui.Tests;
@@ -43,4 +44,22 @@ public void WriteFieldValue_noop_without_bind_value()
4344
act.Should().NotThrow();
4445
store.State.Should().BeEmpty();
4546
}
47+
48+
[Fact]
49+
public void MakeCommandHandler_invokes_dispatcher_with_cmd_prefixed_id_and_focused_element()
50+
{
51+
var store = StrataStore.FromJson("{}");
52+
var root = new StrataElement("Stack");
53+
var focused = new StrataElement("Button");
54+
string? gotId = null;
55+
StrataUiEvent? gotEvent = null;
56+
Action<string, StrataUiEvent> invoke = (id, ev) => { gotId = id; gotEvent = ev; };
57+
58+
var handler = StrataInteractiveHost.MakeCommandHandler("run-query", store, root, invoke);
59+
handler(new CommandContext(focused, new HostEvent.Custom("custom.x", null), null!));
60+
61+
gotId.Should().Be("cmd:run-query");
62+
gotEvent!.Element.Should().BeSameAs(focused);
63+
gotEvent.Store.Should().BeSameAs(store);
64+
}
4665
}

0 commit comments

Comments
 (0)