fix(ui): target submenu input and expose the resize surface

Refs #8, #9. Keep settings/table/physical-resize assertions; verify pointer identity and dismiss the depth popup before dragging.

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
Signed-off-by: James Maes <jmaes@dmdbrands.com>
This commit is contained in:
James MaesandCodex committed 2026-10-10 21:23:09 -05:00
1 parent ce9afc6237
commit 443b4928ee
5 files changed
+49 -7

No files matched your search

+13 -3
View File
@@ -49,13 +49,23 @@ exact failure in InstalledUpdates.Tests.ps1: CommandInfo.get_Parameters ->
UseCorrectCasing.AnalyzeScript, matching upstream #1708. Locally verified workaround
checks casing alone, then every other rule; all 13 files and both negative fixtures
pass, plus a bounded 20-iteration offending-file probe. CI must verify this change;
no suppression, retry or analyzer-version change. Recent successes alone are not a fix.
no suppression, retry or analyzer-version change. Signed local ce9afc62378a1275dc723ee444066bd2c732cef5
contains the workaround; push with the input follow-up after all prior producers ended.
## Outline milestone being prepared
Pushed outline head: 5c3988e012b46aff2959eabd16c6de2b7381ab04. Authoritative CI
38104585881 and CodeQL 38104585849 are live; security/pr-gates passed, scripts
38104585530 failed with the captured race above. Do not restart or supersede live runs.
38104585881 is terminal FAILURE: 31/33 native/UI pass, including actual heading jumps,
independent nested collapse and retained theme/reload state. Depth choices pass; physical
resize fails. Frontmatter still fails because Markdown click never dispatched: settings
remain Metadata, only one reload logged, submenu remains open. CodeQL 38104585849,
security and PR gates passed; scripts 38104585530 captured the race above.
Follow-up adds an explicit Thumb hit-test template. Physical mouse helper moves directly
to the requested control, verifies that exact target or its descendant, then clicks;
an animated path across parent menu rows can close a submenu before the click. Depth
popup is explicitly collapsed before real divider drag and hit identity checked.
Settings/table/resize assertions remain; no local GUI input. Hosted verification needed.
Issue #9: sanitized heading labels, GFM fragment mapping, generated numeric identities,
stack-free section construction, duplicate/nested headings and passive markup.
+2 -1
View File
@@ -17,7 +17,8 @@
- [x] Implement persisted editable theme overrides/custom names, color/code-font/CSS controls and built-in restoration.
- [x] Complete hosted theme editor/validation/restoration acceptance (issue #7): CI 38102867556 passed; size 17 edit also passes in 29/30 later UI tests.
- [x] Implement persistent resizable TOC/depth, safe heading/local-fragment jumps and independent nested section collapse (issue #9).
- [ ] Verify hosted outline depth/resize/scroll/collapse/theme/reload acceptance.
- [x] Verify hosted heading jumps, independent nested collapse and theme/reload state on 5c3988e (31/33 UI pass).
- [ ] Verify physical sidebar resize and frontmatter input follow-up; keep exact assertions.
- [ ] Add source pane, synchronized navigation, find and vim keys.
- [x] Implement and core-test hidden/metadata/Markdown frontmatter modes, with persisted native controls and attribution.
- [ ] Verify actual hosted frontmatter mode switching and code-font size editing.
+7 -1
View File
@@ -86,7 +86,13 @@
<Button x:Name="ExpandSections" Content="Expand all" HorizontalAlignment="Stretch" Click="ExpandSections_Click" />
</StackPanel>
</Grid>
<primitives:Thumb x:Name="OutlineDivider" Grid.Column="1" Background="{ThemeResource DividerStrokeColorDefaultBrush}" DragDelta="OutlineDivider_DragDelta" DragCompleted="OutlineDivider_DragCompleted" AutomationProperties.Name="Resize table of contents" />
<primitives:Thumb x:Name="OutlineDivider" Grid.Column="1" Background="{ThemeResource DividerStrokeColorDefaultBrush}" DragDelta="OutlineDivider_DragDelta" DragCompleted="OutlineDivider_DragCompleted" AutomationProperties.Name="Resize table of contents">
<primitives:Thumb.Template>
<ControlTemplate TargetType="primitives:Thumb">
<Border Background="{TemplateBinding Background}" />
</ControlTemplate>
</primitives:Thumb.Template>
</primitives:Thumb>
<WebView2 x:Name="DocumentView" Grid.Column="2" Loaded="DocumentView_Loaded" AutomationProperties.Name="Markdown document" />
</Grid>
</Grid>
@@ -236,13 +236,36 @@ public sealed class AppSession : IDisposable
return hitProcess == App.ProcessId && element.IsEnabled && !element.IsOffscreen ? element : null;
},
$"an unobscured mouse target '{automationId}'");
element.Click();
// Move directly: an animated path across parent-menu rows can close the target submenu.
AssertMouseTarget(element);
Mouse.LeftClick();
Wait.UntilInputIsProcessed();
LastMouseInput += $" Mouse after={Mouse.Position}; hit PID={Automation.FromPoint(Mouse.Position).Properties.ProcessId.ValueOrDefault}.";
}
/// <summary>Moves once to the current target and verifies the pointer is over it or one of its children.</summary>
private void AssertMouseTarget(AutomationElement element)
{
if(!element.TryGetClickablePoint(out System.Drawing.Point point))
{
throw new InvalidOperationException("The native mouse target no longer has a clickable point.");
}
Mouse.Position = point;
Wait.UntilInputIsProcessed();
AutomationElement? hit = Automation.FromPoint(Mouse.Position);
for(int depth = 0; hit is not null && depth < 8; depth++, hit = hit.Parent)
{
if(hit.Equals(element))
{
return;
}
}
throw new InvalidOperationException("The pointer is not over the requested native control. " + LastMouseInput);
}
/// <summary>Invokes the element carrying <paramref name="automationId"/> (a button, for example).</summary>
public void ClickElement(string automationId) => Invoke(WaitForElement(automationId));
@@ -195,7 +195,7 @@ public class AppTests
}
catch(TimeoutException exception)
{
throw new TimeoutException($"{exception.Message} Settings: {session.ReadSettings()} Logs: {session.ReadLogs()} Native tree: {session.DescribeDocumentStatus()}", exception);
throw new TimeoutException($"{exception.Message} Input: {session.LastInvocation} {session.LastMouseInput} Settings: {session.ReadSettings()} Logs: {session.ReadLogs()} Native tree: {session.DescribeDocumentStatus()}", exception);
}
session.WaitFor(() => session.FindInApp(cf => cf.ByName("Visible metadata")), "the rendered frontmatter heading");
session.InvokeMenuItem("View", "FrontmatterMenu");
@@ -219,10 +219,12 @@ public class AppTests
session.WaitForElementGone("OutlineHeading:1");
session.WaitForElement("OutlineDepth").AsComboBox().Select(3);
session.WaitForElement("OutlineHeading:2");
session.WaitForElement("OutlineDepth").Patterns.ExpandCollapse.Pattern.Collapse();
AutomationElement divider = session.WaitForElement("OutlineDivider");
session.MainWindow.SetForeground();
Assert.True(divider.TryGetClickablePoint(out System.Drawing.Point start));
Assert.Equal(session.App.ProcessId, session.Automation.FromPoint(start).Properties.ProcessId.ValueOrDefault);
Assert.Equal("OutlineDivider", session.Automation.FromPoint(start).Properties.AutomationId.ValueOrDefault);
FlaUI.Core.Input.Mouse.DragHorizontally(start, 70);
session.WaitFor(
() =>