Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ broke. The heading is parsed by `scripts/release-notes.ps1`, so keep its shape;
under it is ordinary Markdown, and the renderer handles paragraphs, lists, links, `code`
and **bold**.

## 1.7.8 - 31 October 2026

### Go to a LiveView's source window
A LiveView of a window has a **Go to source window** button that brings that window forward,
on whichever virtual desktop it is. Ctrl+Alt+Shift+W does the same from Whiteboard, and from
any other application returns to Whiteboard and resumes the LiveView.
**Preferences → Live View → Source window shortcut** offers Ctrl+Alt+. or Ctrl+Alt+J instead.

### Ink across overlapping containers
A stroke that touches more than one container now moves with the frontmost one. It used to
stay on the canvas, linked to none of them.

## 1.7.7 - 29 October 2026

### Clear strokes
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
scripts/build-installer.ps1 both read it from here, so releasing is a reviewed change
to this line rather than an edit in a pipeline variable group.
-->
<VersionPrefix>1.7.7</VersionPrefix>
<VersionPrefix>1.7.8</VersionPrefix>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,18 @@ Use **Copy settings** after finding a useful combination so the exact values can
| Ctrl + tap or drag | Add what is tapped, or what the area takes, to the selection instead of replacing it |
| Shift + drag a shape | Constrain the new shape to a square |
| Ctrl at release (connector) | Bind the endpoint to the nearest point anywhere on the object's border, rather than to one of its eight binding points. **Anchors → Auto** on the property bar moves a bound end to the side facing the other end instead |
| Help > Preferences | Searchable settings: mode, startup monitor, full screen at startup or when idle, no-pen warning, finger drawing, mouse drawing, pen button, snippet format order, area selection, background grid, laser trail, toolbar position and layout, Insert and Lasso on the toolbar, the Insert palette, after inserting an object, always show the Eraser, update checks. Each setting is one line; its chevron opens the reasoning behind it. Search marks what it matched, and marks the chevron when the match is in the text behind it |
| Help > Preferences | Searchable settings: mode, startup monitor, full screen at startup or when idle, no-pen warning, finger drawing, mouse drawing, pen button, snippet format order, area selection, background grid, laser trail, toolbar position and layout, Insert and Lasso on the toolbar, the Insert palette, after inserting an object, always show the Eraser, the LiveView source window shortcut, update checks. Each setting is one line; its chevron opens the reasoning behind it. Search marks what it matched, and marks the chevron when the match is in the text behind it |
| View > Bring to front / Bring forward / Send backward / Send to back | Reorder the selection (and its linked strokes) as one block. Letters B, W, K, and S while the View strip is open, and the same four behind the property bar's … |
| Help > About | Version, channel, license, the product site, and a download link when a newer release is known |
| View > LiveView | Capture, freeze, disconnect, or reconnect a window or display |
| View > LiveView | Capture, freeze, disconnect, or reconnect a window or display. **Go to source window** is available when the captured window can be identified uniquely; **Ctrl+Alt+Shift+W**, or the keys chosen in Preferences, switches between that window and Whiteboard across virtual desktops and resumes the feed on return |
| F11 | Fill the current monitor and hide title and tabs. Escape leaves it when a text container is not being edited. **Preferences → Startup → Go full screen when idle** does the same after 20 seconds without input in Whiteboard, even while another app has focus |
| Ctrl+F11 | Hide title and tabs but keep this window’s size and place |

With the mouse, selection is automatic: click a container to move it, or drag the circular bottom-right handle to resize it while preserving its aspect ratio. Double-click a container to center it and fit it to the canvas. Releasing the mouse returns to the previously selected drawing tool.

**Help → Preferences → Mouse drawing** changes what the left button means, and it defaults to on when Windows reports neither a pen tablet nor a touchscreen. With it on the left button uses the selected tool, the tool stays selected rather than being handed back, and Eraser and Pan join the toolbar as they do for finger drawing. Everything above then moves to Ctrl: Ctrl and the left button select, move, and resize a container and return to the previous drawing tool, and Ctrl with a double-click centers and fits one. Two plain clicks with an ink tool are two strokes, which is why framing moves out of the way. Shift still constrains a stroke to horizontal or vertical, and Alt+L is still the laser. A mouse reports no pressure, so ink is drawn at an even width; Calligraphy still varies, because its width comes from speed. The pen path is untouched, so Mouse drawing can be left on beside a pen.

Imported images, LiveViews, text objects, shapes, and text labels act as containers. A completed stroke is linked when it touches exactly one container, including crossing its edge; a stroke touching multiple containers remains independent. A shape is picked up by its outline rather than by its interior, so ink drawn inside one stays reachable, and a label is picked up anywhere on its rotated rectangle. Moving, resizing, or turning a container transforms its linked strokes with it. **View → Bring to front**, **Bring forward**, **Send backward**, and **Send to back** reorder the selection and those linked strokes as one block. Deleting a container also deletes all of its linked strokes. Undo/redo treats each complete container operation as one action.
Imported images, LiveViews, text objects, shapes, and text labels act as containers. A completed stroke links to the frontmost container it touches, including crossing its edge; a stroke touching no container remains independent. A shape is picked up by its outline rather than by its interior, so ink drawn inside one stays reachable, and a label is picked up anywhere on its rotated rectangle. Moving, resizing, or turning a container transforms its linked strokes with it. **View → Bring to front**, **Bring forward**, **Send backward**, and **Send to back** reorder the selection and those linked strokes as one block. Deleting a container also deletes all of its linked strokes. Undo/redo treats each complete container operation as one action.

Paste plain text to create a selected text container in display mode. **Help → Preferences** has Snippet format order: paste tries those languages from top to bottom and uses the first that accepts the text. Plain text always accepts and comes last by default, so DAX, SQL, KQL, and Markdown are recognized on paste; a language claims a snippet only when it carries an operator, function, or keyword, so a bare word stays a note. Put Plain text first to keep every paste plain. A language added by an update joins in front of Plain text unless Plain text is first. Recognized extensions (`.dax`, `.sql`, `.kql`, `.txt`) keep their language; other dropped text files use the same order. Choose **Plain text**, **Markdown**, **DAX**, **SQL Server**, or **KQL** from the title-bar chip afterward, or one of **Python**, **C**, **C++**, **Java**, **C#**, **JavaScript**, **TypeScript**, **Visual Basic .NET**, **R**, **Rust**, and **PHP**, which are highlighted but not formatted and are never chosen by paste; **F6** on one of them opens that language's issue on GitHub. Press **F6** to format DAX, SQL, or KQL on the selected container without entering edit. Press **F2** to edit the body; the same list is in the title bar while editing. In F2, **F6** formats in place; **Ctrl+Enter** commits that edit (including the format) and returns to display. **Escape** restores the previous text, language, and dimensions. Text reflows while its edit-mode resize grip changes the width, and the height grows automatically when necessary. Double-click still centers and fits the container. Syntax highlighting applies in both edit and display modes. A language-aware title identifies a defined DAX, SQL, or KQL object when possible. SQL Server mode targets SQL Server 2025 T-SQL, preserves `GO` batch separators, and leaves invalid scripts unchanged. KQL mode reads Kusto through Microsoft's own parser and likewise leaves an invalid query unchanged. In display mode, resizing preserves the aspect ratio and scales the complete text visual without reflowing it.

Expand Down
2 changes: 1 addition & 1 deletion docs/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ the machine).
The board is one unbounded plane. A `.wboard` is a flat list of objects in world
coordinates — ink strokes, images, text containers, and LiveViews — each with a bounding
rectangle and a z-index that is also its creation order, plus the assets the images refer
to. A stroke that touches exactly one container is linked to it and moves with it. Nothing
to. A newly drawn stroke links to the frontmost container it touches and moves with it. Nothing
in the file says where a page begins or ends. The first two exports have to decide it, and
the third does not need it.

Expand Down
98 changes: 98 additions & 0 deletions docs/liveview-navigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# LiveView window navigation

Implemented locally: the third on-container button activates the source window.
One system-wide shortcut, Ctrl+Alt+Shift+W by default, does the same from Whiteboard
and returns to the existing Whiteboard window from anywhere else, resuming the feed.

## Window identity and desktops

The original Windows capture picker is used for both windows and displays. After
selection, Whiteboard compares the capture title and pixel dimensions with open
windows. Candidate dimensions come from temporary capture items, so window borders
and DPI do not require approximate size comparisons. No additional capture session
is started. Windows on other virtual desktops are included in enumeration.

A single matching candidate enables navigation. Duplicate matches, changed titles,
resizing, or errors leave navigation disabled without interrupting capture. Display
capture descriptions are checked first to avoid linking a display to a window with
an identical title and size. This is a heuristic: a unique description is not proof
of identity, and a window changing while matching can make the result inconclusive.

Once matched, the HWND and process ID are retained for the capture session; later
title changes do not trigger another search. The association is cleared on disconnect
or target closure. Reopening a board still requires Reconnect, which runs the same
picker and matching process again.

Navigation restores a minimized window and calls `SetForegroundWindow`. Windows
selects the virtual desktop containing that existing window. Neither source nor
Whiteboard is moved to another desktop. This follows the window's current desktop;
if the user moves it during the visit, the return follows its new location.

## Shortcut

`RegisterHotKey` delivers the shortcut to Whiteboard whichever window is in front,
so one key serves both directions. When Whiteboard's main window is active, it goes
to the selected LiveView's source, then the last one visited, then the only LiveView
with a source window. When another application is active, it returns to Whiteboard
and resumes the LiveView last visited. It does nothing while a Whiteboard dialog or
a label or shape text edit is active.

A registered hotkey takes the keys from every other application, including shortcuts
an application handles in its own window, which `RegisterHotKey` cannot detect. The
shortcut is therefore registered only while at least one LiveView has an available
source window, or a visit is under way, and released otherwise. A visit keeps it
registered when the source closes, so the return still works. A registration conflict
is silent when a LiveView connects, reported by **Go to source window** before leaving
the board, and reported when the shortcut is changed in Preferences.

**Preferences → Live View → Source window shortcut** offers a fixed list:
Ctrl+Alt+Shift+W, Ctrl+Alt+., and Ctrl+Alt+J. A list, rather than recording any
combination, avoids having to reject Whiteboard's own shortcuts, Windows-key
combinations reserved by the operating system, and Ctrl+Alt combinations that type a
character with AltGr on the current keyboard layout. Ctrl+Alt+Shift+W is the default
because three modifiers keep it out of AltGr typing and out of most applications'
shortcut tables. Ctrl+Alt+J, for example, is used by Visual Studio.

Jumping resumes a frozen source. During the visit, that feed is exempt from
**Pause when Whiteboard loses focus**, so edits can be captured before returning
to another desktop. Other feeds retain the preference. Activating Whiteboard ends
the exemption. Frame delivery still depends on the source application and Windows
Graphics Capture; activation does not guarantee that a new frame has already arrived.

## Validation

The default smoke suite checks matching with supplied capture descriptions, including
unique matches, duplicates, size mismatches, title changes, and inaccessible candidates.
It also checks native window identity, closed-window rejection, and hotkey registration
conflicts and cleanup. These checks do not require Windows Graphics Capture.

Real capture tests require an interactive Windows desktop and are explicitly enabled:

```powershell
dotnet run --project tests/SQLBI.Whiteboard.SmokeTests/SQLBI.Whiteboard.SmokeTests.csproj -c Release -- --liveview-navigation-capture
```

They create offscreen windows without activating them, verify capture and matching,
and check that failure leaves the selected capture item usable. GitHub's hosted runner
rejected capture-item creation with E_INVALIDARG, so this integration check is not part
of the default CI suite. Neither suite changes virtual desktops.

Interactive checks still required on Windows 11:

1. Capture a window with the Windows picker, jump with the shortcut, edit its contents,
and return with the same shortcut. Repeat with the button for the first jump.
2. Repeat with the source and Whiteboard on different virtual desktops. Confirm both
jumps follow the appropriate desktop and the edit appears in the feed.
3. Repeat with a minimized source, a frozen LiveView, and background capture pausing on.
4. Close the source during the visit. The shortcut must still return to Whiteboard.
5. Choose a display through the system picker. Freeze and Resume must still work;
the jump button must stay disabled. Repeat with two windows whose titles and dimensions match.
6. Change the shortcut in Preferences with a LiveView connected. The new keys must work
at once, the old ones must reach other applications again, and the button tooltip
must name the new keys.
7. Disconnect every LiveView, then close Whiteboard, and confirm another instance or
application can register the shortcut each time.

References: [foreground activation and virtual desktops](https://devblogs.microsoft.com/oldnewthing/20171002-00/?p=97116),
[capture by HWND](https://learn.microsoft.com/en-us/windows/win32/api/windows.graphics.capture.interop/nf-windows-graphics-capture-interop-igraphicscaptureiteminterop-createforwindow),
[global hotkey registration](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey).
4 changes: 2 additions & 2 deletions docs/microsoft-whiteboard-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ Windows client offers, from `paleYellowGradient` (#FEE15A) to `grayGradient` (#C

Coordinates stay in canvas pixels, one to one with board units, so a 4-pixel Microsoft pen
is as wide as the 4-unit pen here. A new board puts the export's top-left corner at the
origin. A stroke that touches exactly one imported container travels with it, which is the
rule for a stroke drawn by hand.
origin. A stroke that touches exactly one imported container travels with it. This import rule differs from the
rule for newly drawn strokes, which link to the frontmost container they touch.

**Shape kinds** are read from the outline, because the shape's name on the page is
written in the language of whoever made the board. Curves with straight sides are a tube
Expand Down
8 changes: 4 additions & 4 deletions site/guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ <h3>Pin a live demo without leaving the board</h3>

<section class="story">
<h3>Keep ink on the slide you are discussing</h3>
<p>A finished stroke that touches only one container (an image, a text block, or a LiveView) travels with that container when you move or resize it. A stroke that crosses two containers stays on the canvas. Delete the container and its linked ink goes with it. Undo treats the whole operation as one step.</p>
<p>A finished stroke travels with the frontmost container it touches when you move or resize that container. Containers underneath do not take the stroke. Delete the container and its linked ink goes with it. Undo treats the whole operation as one step.</p>

<figure class="fig">
<div class="figbox">
<svg viewBox="0 0 700 290" role="img" aria-label="A stroke drawn inside one container moves with it, while a stroke crossing two containers stays behind on the canvas.">
<svg viewBox="0 0 700 290" role="img" aria-label="A stroke drawn inside the chart moves with it. A stroke crossing both containers links to the notes container, which is in front.">
<title>Ink linked to a container</title>
<defs>
<radialGradient id="f1fade" cx="0.42" cy="0.4" r="0.8"><stop offset="0.25" stop-color="#fff"/><stop offset="1" stop-color="#000"/></radialGradient>
Expand Down Expand Up @@ -245,7 +245,7 @@ <h3>Keep ink on the slide you are discussing</h3>
</div>
<figcaption>
<p><span class="swatch" style="background:var(--brand)"></span><b>1</b> sits wholly inside the chart container, so it travels with it. The dashed outline is where the container was.</p>
<p><span class="swatch" style="background:var(--fig-muted)"></span><b>2</b> crosses into the notes container, so it stays on the canvas.</p>
<p><span class="swatch" style="background:var(--fig-muted)"></span><b>2</b> touches both containers and links to the notes container, which is in front.</p>
</figcaption>
</figure>
</section>
Expand Down Expand Up @@ -969,7 +969,7 @@ <h3>Shapes, connectors, and text</h3>
<p><span class="ui">Select</span> comes back once one object is drawn, and <kbd>Escape</kbd> returns to <span class="ui">Select</span> at any time. <span class="ui">Preferences → Toolbar</span> keeps it for the next one instead, adds <span class="ui">Insert</span> to the floating palette, or pins the Insert row into a second palette you can drag anywhere. Shapes, connectors, and labels are saved in the board, undone with everything else, and exported to a deck or a PDF as objects you can select and change — in PowerPoint the straight arrows are still attached to their shapes.</p>

<h3 id="liveview">LiveView</h3>
<p><span class="ui">View → LiveView</span> captures a window or a display. The container behaves like an image: move, resize, frame, delete, undo.</p>
<p><span class="ui">View → LiveView</span> captures a window or a display. When the captured window can be identified uniquely, <span class="ui">Go to source window</span> opens it, and <kbd>Ctrl</kbd><span class="plus">+</span><kbd>Alt</kbd><span class="plus">+</span><kbd>Shift</kbd><span class="plus">+</span><kbd>W</kbd> switches between it and Whiteboard, resuming the feed each time you return. Each jump follows the window to its virtual desktop, and <span class="ui">Preferences → Live View</span> offers two other keys. The container behaves like an image: move, resize, frame, delete, undo.</p>
<p>Turn on <span class="ui">Preferences → Live View → Pause when Whiteboard loses focus</span> to stop capture while you use another application and keep the last frame visible. Returning to Whiteboard resumes the views that were playing, leaving manually paused views paused. Whiteboard's own dialogs do not interrupt capture.</p>

<figure class="fig">
Expand Down
Loading
Loading