Repository navigation
UX: fix-now batch, layout model, Client registry, trigger builder, Light settings - #96
Draft
HarryCordewener wants to merge 79 commits into
Draft
HarryCordewener wants to merge 79 commits into
HarryCordewener wants to merge 79 commits into
Conversation
The status row wrapped to a second row at 80x24, the meters vanished below 120 columns, and F2/F4 drew their editors off the right-hand edge at 80. The status row now drops its encoding and palette hints before wrapping, meters fall back to a compact percentage form, two-column screens too narrow for both columns show the one the keyboard is in, and header/footer bars elide text rather than clipping their keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
--plain had no way to add a world on a first run: the settings screens are not usable there. /connect <host> [port] [tls] saves a new world and dials it; /connect <name> opens a configured one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
A machine with no Nerd Font installed drew every chrome icon in the window as a box, and text fell back to whatever monospace face the system had. The window now carries JetBrains Mono (OFL 1.1) and Symbols Nerd Font Mono (MIT) as embedded resources, used after any preferred installed face, and ships their licences beside the executable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
A header too narrow for its hints now shortens them from the middle, so the key that closes the screen stays on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
|
… availability Core only, no UI wiring. A layout (Layouts/LayoutDefinition) places panels by id in regions with an order, a relative size, a collapsed flag and an optional tab host. Roleplay and Combat are built in and read-only; LayoutLibrary saves, updates, renames and deletes the player's layouts in AppConfiguration.Layouts, turning an update of a built-in into a saved copy. LayoutResolver picks the character's layout, then the world's, then the world's accepted suggestion, then Roleplay, passing over ids that name nothing. LayoutSuggester suggests Combat on vitals and Roleplay otherwise, saying whether the MSSP codebase was a MUSH-family one. PanelAvailability says which panels have data for a session, and LayoutFold folds a layout into the workspace's own split tree. New config fields are optional with old-behaviour defaults, so no migration and no schema version change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…mands from it A new UI-agnostic project, SharpMUTerm.Client (Core only), holds the shared client shell's first piece: ContributionRegistry, with six kinds of contribution (commands, panels, settings sections, status cells, overlays, output decorators), each with a stable id unique within its kind and an owner. The built-in ⌃P catalog moves out of Core's CommandCatalog.Build into BuiltInCommands, registered as contributions; the Tui supplies the work (RunBuiltInCommand). The command surface, the pointer menu and DispatchCommand all read the registry, so a package or host can add commands. The catalog is unchanged entry for entry. New suite: SharpMUTerm.Client.Tests (in CI). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…match kinds, and a live test on F2 Ctrl+P > "Make a trigger from the selection" (and the output right-click menu) makes a rule from the selected line, with numbers, quoted speech and known GMCP player names turned into * wildcards, and opens F2 on it with the pattern being typed and the regex shown behind it. Triggers gain a match type (contains the text, starts with, * wildcards, regular expression; regex stays the default), and F2's pattern editor shows how many of the last 2,000 held lines match, with the newest three highlighted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…ind readers Settings screens (D6): ScreenPalette's constants become roles spelt as the Dark theme's values. A new immutable ScreenTheme, built once from the app's theme, resolves renderer markup and control colours at the view boundary (ScreenChrome.Panel/Band/VerticalRule, ScreenTheme.Colour), so renderers and their tests are untouched and there is no mutable global. Dark resolves to the constants byte for byte; Light mirrors L*, Solarized takes its background's hue, and every ink is held to Contrast.Floor on its planes. Threaded through every *ScreenView.Build (required), SettingsOverlay, ComposeOverlay, ListOverlay and EditReviewOverlay. FrameContrastTests gains 13 settings views on all three themes; ScreenThemeTests checks that no Light settings frame paints a dark-palette plane. Tints: ColourVision (Core) simulates protanopia/deuteranopia/tritanopia (Machado 2009) and measures CIEDE2000. The ten anchors were re-picked within 40 degrees of each name: the worst dichromat pair goes from 0.28 to 1.99, and the worst pair for typical vision from 3.71 to 3.96. TintColourVisionTests holds 1.9 / 3.5 on all themes. Names kept. A tinted character's rail and tab tooltips name the colour, at no cell cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md
…rnings The palette's ranking and grouping move into SharpMUTerm.Client (CommandPaletteModel, FuzzyMatch, RecentCommands, KeyGlyphs) so the window's palette can list the same rows; Core's CommandMatcher is removed. Matching is word-by-word subsequence with word-start/consecutive/prefix scoring and highlighted matches. An empty query lists RECENT (in memory, eight) and SUGGESTED (from CommandContext) before the groups. Keys sit in a right-aligned column in one glyph vocabulary and are never elided. F4 marks a binding that wins over a client key (MacroKeys.YieldedBy) with an amber triangle and a shadowed duplicate as dead; ⌃P lists "Key clashes (N)" while N > 0. New snapshot views menu-fuzzy and menu-recent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…uggestion A character's resolved layout is realised as one pane per panel that has data (LayoutFold -> LayoutRealiser -> WorkspaceLayout.ReplaceTree); output, chat and map are the existing windows, every other panel a panel:<id>:<key> window whose control comes from a factory registered in TerminalPanels. Re-folds are rate limited and rebuild only when the drawn panels change, and stop once the player rearranges the panes. A restored workspace wins at startup; only the live entry point applies layouts on its own. Alt+M and Ctrl+P > Layouts open the list (apply, save changes, save as new, use for world/character); F5 gains a layout row on worlds and characters; a world's first connect accepts a suggestion and says so once. LayoutCapture reads an arrangement back into placements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md
…, meter table F7 and F8 are drawn from settings sections declared in SharpMUTerm.Client (BuiltInSettings): each field has a group heading and a SettingBinding over the configuration, whose default is read off a fresh AppConfiguration. F7 is now "Text & display", grouped under Colour & legibility, Text, Links & pictures, Status row & meters, Activity and Sound, and windowed to the screen height so nothing runs off a 24-row terminal. - `/` on any settings screen opens a search over every declared field and every screen title, with a per-section match count; Enter goes to the field on its own screen, Esc puts the screen back. - A field away from its default is marked "● changed"; `r` resets it through the same save funnel. The header names `r reset` only there. - F7's meters are a table: label, value path, maximum path and a live `now` column from the focused session's game data, [+ meter], Delete to remove, a box of received paths while the cursor is in the table, and the path fields offer those paths with their values. Snapshot views settings-search, settings-meters, settings-meters-pick and settings-changed, in the parity list and FrameContrastTests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md # src/SharpMUTerm.Tui/SharpMUTermApp.cs
Four panels plug into the layouts seam (TerminalPanels.Register from RegisterTerminalPanels): who is here from GMCP Room.Players and the room's item list (a click puts look <name> on the command line), a one-row vitals strip of reserved fields with bars, values, a low mark and the combat target, a quick bar of function-key macros run through the key's own path, and the F1 editor docked as a pane sharing F1's draft. While the composer's pane is in front it is armed: the focus pin targets its editor, Esc hands the keyboard back to the command line, and arriving at the pane arms it again. Each is on Ctrl+P and has a snapshot view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md
A headless app runs OnUiThread inline, so the picture host's deliveries ran on the thread pool while the test or snapshot appended the next line. The reserved-picture draw and the append that reads ReservedPicture.Drawn raced over the pane buffer, and a row could be left blank: the intermittent APictureStopsAtTheFirstLineWithoutItsCells. Headless picture work is now queued and run at the next frame or while InlineImagesSettled is awaited. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
… cells The window host draws native views in a layer above the cell view, fed through IPointerPresenter (the scrollbars' channel): a Layouts button over the header's three trailing blank cells, whose menu is built by the client from the layout library's registry commands; native vitals gauges drawn from the VitalsModel the cell strip now renders from; and a native map canvas (zoom, pan, click to walk through the client's speedwalk). The cells underneath are unchanged, so no pane rectangle moves and host parity is untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
Scrolling a pane back now splits it inside its own rectangle: the history
being read on top, a band ("scrolled back N rows · End returns · ⌥F
freezes"), and the newest lines still arriving below. It reuses freeze's
two-viewport layout with the window's own control and viewport kept on top,
so the scroll keys, wheel, search landing and status row are unchanged; the
tail is a re-fed copy of the buffer's last lines and indexes nothing. A
split pane is not "scrolled back" for unread badging. F7 ▸ SCROLLBACK turns
it off.
F5 ▸ join wraps (per world, off by default) joins rows a server broke at
its own column, at ingest and before the triggers, through a conservative
ServerWrapJoiner: full row, unpunctuated end, lower-case continuation, same
style, no prompts, structure or column layouts. A full row is held until
the next line or 250 ms of quiet.
New snapshot views: scrollback-split, reflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
The vitals strip and the quick bar are exactly one row, with no tab strip over a one-tab strip, and the docked composer has an editor's floor. A pane's height claim (PaneNode.FixedRows/MinRows) is derived from what it holds on every rebuild (PanelSizing) and honoured by the grid and by LayoutSolver (PaneExtent.DistributeRows); a divider beside a fixed strip neither hovers nor drags, and the resize keys take rows from a pane beyond it or refuse out loud. The status row leaves its meters out while the vitals strip is showing them. Layout panels are WindowKind.Panel: no rail row, no Alt+number, no Go to entry, so map and capture windows keep their digits. Alt+M renames (r) and deletes (d, then y) saved layouts. New snapshot view: layout-roleplay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md
…onnect A client with no worlds opens on a welcome: connect to a host (a form that goes through /connect's own path, adding the character it names), browse games from the package index (still fetched only when asked; says so in place when no index is set), or open F5, with the /connect line spelt out and a starting layout to pick (suggested by default, so the client's suggestion still applies). Esc closes it for the run; /welcome and Ctrl+P > Welcome reopen it. Under --plain (ClientHost.ScreenIsSeen false) the choices are announced as commands and /welcome layout picks the layout. The first connect of a configuration that started empty puts one chrome row naming Ctrl+P, Alt+M, F1 and F5, owed through AppConfiguration.KeyTourPending so it is shown once and survives a restart. Ctrl+P > Key tour hides and shows it. Snapshot views welcome, welcome-connect and tour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md # src/SharpMUTerm.Tui/ListOverlay.cs # tests/SharpMUTerm.Tui.Tests/FrameContrastTests.cs
In a stacked split no row is spent on a divider above a pane that draws its own tab strip: the strip is the boundary, and its empty run past the last tab is the divider's handle (hover cue, drag, double-click to even, tooltip). Fixed strip panels sit flush with no divider on either side and, unfocused, are recessed a step so they never share an unfocused pane's plane. Side by side is unchanged. The freed rows go to the panes (a NAWS change). PaneExtent.DividerRows is the one rule, read by LayoutSolver, PaneExtent, PaneResize and the terminal's grid. Adds the split-stacked snapshot view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…window F10 is a declared list section (BuiltInSettings.Packages, PackageCollection): an enabled switch whose binding replaces DisabledPackages, read-only fields for name, version, games, author, description, sets, scripts, media, update and characters, and the Space/c/Del/b actions. The cell modal and the window's PackagePageView draw the same fields in the same order. The window gets a package manager page: an Installed tab (switches, details, copy sets, remove) and a Browse tab that fetches the index only when asked and lists the focused world's packages first. Install opens a native review drawn from the same PackageReviewModel as the cell review, leading with what can act as the player; both end in AcceptPackage, which refuses a stale review, so nothing is written until the player installs. Every gesture goes to the client through IPackageHost. A settings --view opens the native window in the window host; the parity harness still compares cells. The demo scene serves a fake index from memory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…w, expose output to screen readers - telnet:// and telnets:// links connect through the same reader as /connect (ConnectEndpoint, TelnetLink), matched on the endpoint, never sending anything; credentials, paths, queries, control characters and bad ports are refused. --open-link drops every other argument. A second launch hands its link to the running client over a Unix domain socket (LinkHandoff). The .desktop file declares the schemes and the Inno Setup script registers them as an unchecked per-user task. - Character passwords go to Windows Credential Manager, the Secret Service (secret-tool) or the macOS Keychain (CredentialStores, in Hosting/), with secrets.json behind them (CredentialVault). Existing entries migrate at load; an unreachable store keeps every reference through a save. Only the live entry points make a vault. - The window and the terminal are titled Character@World — SharpMUTerm from the header's model (WindowTitle, ClientHost.Title: Window.Title, or OSC 2 with the title stack pushed and popped). - The window's output view has an automation peer: a read-only document holding the front window's newest lines (IAnnouncer.Reading, TerminalViewAutomationPeer). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
In the desktop window the quit prompt, Ctrl+F, Ctrl+R, the Alt+M layouts list and the first-run welcome are native modals over the window, drawn from the Client model the cell modal draws and ending every gesture in the cell key's method. One host member, ClientHost.NativeModals (INativeModals), covers all five: the presenter owns stacking, focus return and theme. The quit prompt is never refused and always ends in front, so closing the window while F1 is open no longer hides the prompt under the composer. The terminal is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…619a1f4ede77 # Conflicts: # CHANGELOG.md
…hread-izbu0c # Conflicts: # CHANGELOG.md
…619a1f4ede77 # Conflicts: # CLAUDE.md # src/SharpMUTerm.Avalonia/AvaloniaConsoleDriver.cs # src/SharpMUTerm.Avalonia/WindowHost.cs # src/SharpMUTerm.Tui/Hosting/ClientHost.cs
The map, vitals, who-here and the quick bar can be popped out of their pane into a window of their own in the desktop host: from a button on the docked native view, the pane's right-click menu, or Ctrl+P (panel:popout:<window>), and docked back from the floating window's Dock button, by closing it, or panel:dock:<window>. - Floating is host-neutral: WorkspaceWindow.Floating (FloatingBounds, screen pixels) is saved with the workspace and PanelPlacement.Floating with a layout (LayoutCapture). The fold never reads it, so the terminal keeps the panel in its region and the flag survives for the next host that can float. - Host fact: ClientHost.FloatingPanels (IFloatingPanels?, required; null in the terminal and headless). Ctrl+P families, the pane menu entry and the docked overlay's PopOut callback exist only where it is set; the surface catalog declares both actions OnlyOn: Window with the Ctrl+P id as the cell route. - The pane a panel leaves goes back through the workspace operations: with the live layout owning the panes it is re-applied without the panel, so pane rectangles and NAWS equal a layout that never placed it. - The floating window draws the panel's own native view from the same overlay and callbacks (OverlayFor), follows the character's plane and theme ink, never takes focus on opening, forwards keys and text to the command line, and docks when closed. Remembered places are clamped to the screens present. - PaneOutputRects no longer throws when called before the main window exists, so resuming a workspace with a panel or map pane no longer fails at startup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
- Startup runs once: the window's welcome demo no longer raises its notice twice. - The window's history-search-filter view opens native Ctrl+R with its query typed. - search-landed lands through the native search in the window. - A hovered toast freezes the stack so it keeps its place; toasts stack clear of the map legend. - F10 fits 80x24: ListOverlay sizes from measured wrapped rows, confirm rows always emitted. - Every native surface spells keys in words (NativeKeys.Words, Enter for the return glyph), with a runtime and source scan (NativeKeySpellingTests). - The settings window takes the keyboard on open and returns it on close. - The composer face is saved per character through PersistConfiguration. - List editors remember their selection per section for the run. - The command line goes idle while Alt+C sidebar navigation holds the keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…hread-izbu0c # Conflicts: # CHANGELOG.md
NativeTokens (Avalonia) derives the window's planes, inks, radii and type ramp from the client's theme the way ScreenTheme does, with every ink held to 4.5:1. Settings, list editor, package page, theme cards, menus, palette and modals read it; Fluent's blue is gone. A test forbids literal font sizes and colours outside the token file. The settings window gets the palette's modal frame (title bar, close, Esc, scrim) and docks to the right half when the trigger builder opens it. Light themes: header fills deepened under light ink (4.5:1), muted header text and the low-vitals cue held to the floor, in both hosts. The terminal popups (Ctrl+P, quit, search, history, prefix panel, game data, message log) follow ScreenTheme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
… welcome - Away/activity/search/restore bars are sized to the pane (BarRule), elide the label first, and redraw in place on resize (SyncChromeBarWidths). - PaneLine.Plain comes from the line's spans, so anchored patterns match highlighted lines in Ctrl+F and F2's live test. - CharacterNames / DisplayName: "Corvid · Aetherfall" on every surface. - Ctrl+F footer drop ladder; the surface fits 80 columns. - Command palette naming, OUTPUT group, strict short-word fuzzy matching. - F4 and quick bar keys in glyphs; F4 footer ladder. - Window delivers numpad keys (HostKeys.DeliversNumpad, KeyMapper). - Collapsed rail world marks and two-letter initials. - Welcome copy in WelcomeModel, shared by both hosts; editable route combo. - Reconnect hints name the key; F1 names an offline window's owner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…ngth - Notices: a notice repeated while still on the row is said once; the status row is elided to its width in both hosts so it never wraps. - Window toasts avoid native quick bars and docked composers and are hidden while settings, Ctrl+P, a native modal or the quit prompt is open. - Join wraps: a pane narrower than the server's learnt wrap column gets a once-per-world notice with a "Join wraps" action (world:join-wraps:<w>); never switched on by default. - Tab overflow chip "+N ▾" painted over a strip's clipped last cells, listing hidden tabs: native menu in the window, cell list in a terminal. Declared as SurfaceCatalog.TabOverflow. - InputSettings.Rows defaults to 1. - Composer: "N chars" (as sent) in F1 and the docked panel in both hosts; Alt+P toggles an "as sent" preview in F1, cells and native. New compose-preview view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
- Settings window: edge resize bands (WindowChrome.WithResizeBands, as the
borderless main window does) under its own frame; the centred size is kept
for the run (SettingsPresenter.RememberedSize), docked-right is not.
- Opening or closing the settings window re-syncs the desktop layer, so
toasts hide and return at once rather than on the next frame.
- One spelling of a name: prompt "Corvid · Aetherfall ›", window title
"Corvid · Aetherfall — SharpMUTerm", tab owner prefix "Corvid · Chat"
(CharacterNames.Window; StatusFormatter.NameSeparator).
- Native palette placeholder starts with "Command palette"
(CommandPalette.Title shared with the header search box).
- A press on a tab strip's +N chip is struck from the driver's frame before
the framework sees it, so the tab drawn under the chip is not selected or
closed; pinned in both hosts.
- Notices from packages, map and sound carry a Source label ("Sound:" on the
row, a caption on a toast), not a word in the key chip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…wave 12) - F2 and F4 at 80 columns draw their one column through ScreenChrome.Panel, so Light resolves it (was Dark's wells and inks, 1.05-1.34:1). - Status row: notice inks and every bare [dim] held to 4.5:1 on the backdrop; Light's backdrop (rail, status row) lifted from #b7b7b9 to #e6e6e9. - Command palette's selected row is Ink on Accent (both roles), 5.8:1 on Light. - Window chrome's secondary ink is opaque and held to the 4.5:1 text floor. - Spell check skips command tokens (+ @ & /) and every known name. - Demo: F1/docked composer can send where the header says Corvid is connected, and no "nothing connects at start" notice over that. - Who-here drops idle words before cutting names; the status room cell grows into spare width; the as-sent preview wraps at spaces; the window's docked composer stops saying "click to write" twice. - FrameContrastTests walks 80x24 too and holds the status row and the palette's selected row to 4.5:1; ScreenThemeTests covers 80-column Light settings; NativeTokenTests covers the chrome's secondary ink. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
- F5 reconnect (per world): a dropped connection is dialled again after 5/15/30/60/60 s with a status-row countdown and a notice carrying Stop; the player's own disconnect, quit, or a server close within 10 s of a command (a logout) never reconnects. Alt+R dials at once; Disconnect and Ctrl+P > Stop reconnecting cancel. Core AutoReconnect + WorldSession.LastClose. - Attention: a trigger's "alert" flag, or F5 alert on = name & pages, raises WorldSession.AttentionRequested; the client asks the host (new required ClientHost.Attention / IAttention) only while away, with F7 "alert when away" on, one per 10 s. Window: taskbar flash / osascript / notify-send. Terminal: OSC 99 (kitty, o=unfocused), OSC 777 (WezTerm, Ghostty, foot), else BEL after 30 s idle. The window title counts lines that arrived while behind. - F5 command stacking (off by default): TypedCommands splits a typed line on the world's separator, with \ escapes and #N repeats (bounded at 100), before the alias engine; composer posts, grabs, scripts and macros are never split. - Tests in Core and Tui; reconnect snapshot view; CLAUDE.md and CHANGELOG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…stion - /help and Ctrl+P > Help: one Client model (HelpModel) drawn as a cell ListOverlay and as a native modal in the window. It lists how-to pages, the slash commands from SlashCommands.All (the table --help now prints too), every registry key for this host and the host's own chords. It is searchable with one View in both hosts. Enter places a slash command unsent, runs a key's command, or opens a page. Declared in SurfaceCatalog with parity probes. - F1's Alt+W with no dictionary, and Ctrl+P > Get a spelling dictionary, offer LibreOffice's en_US/en_GB. Nothing is fetched until the player says yes. It is https only, capped while streaming and checked against a pinned SHA-256, then written into dictionaries/ beside config.json. The dictionaries root is null by default, so tests and snapshots fetch nothing. Native and cell offers share DictionaryOfferModel. - While a pane is narrower than a world's wrap column, Ctrl+P suggests "Join server wraps for <world>" and F5 marks join wraps as recommended. It is still off by default. - New views: help, help-topic, dictionary-offer, join-wraps, join-wraps-f5. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…t and TinTin++ Core/Import reads a MUSHclient .mcl, a Mudlet XML package or profile save, a Mudlet profile folder or .mpackage (read in memory, never extracted) and a TinTin++ script, bounded and tolerant: a rule that cannot be carried over exactly is skipped and listed with its reason, and script code is listed as left out and never run. ImportPlan decides what is new without touching the configuration; the rules go into a new "Imported from <client>" set and nothing existing is overwritten. Passwords are stored only when the player asks. The review (ImportReviewModel) leads with what can act as the player and is drawn by a cell overlay and a native card in the window; nothing is written before Enter. Entry points: /import <path>, Ctrl+P > Import from another client..., and the welcome. The window opens a native file picker (ClientHost.FilePicker, required, null elsewhere). Declared in SurfaceCatalog with parity probes; import-review snapshot view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
…oth hosts The cell map's title is the current room's name, and MapViewModel.Nearby (each exit and the room it leads to) fills a side column beside a wide grid or the rows under a tall one, so both hosts name the same rooms. Map editing, a Mudlet subset: rename, area, lock, weight, add/remove exit (to a room id or the next room walked into), nudge, delete, note and symbol, with undo of the last ten edits. Every edit is a Client MapEdit through ApplyMapEdit -> MapEditing.Apply -> MapRecorder.Edit, on the UI thread under SyncRoot, marked dirty and saved like a recorded room. Cells: Ctrl+P > Edit map, arrow keys and an honest key footer, /map verbs for the edits that need words. Window: a right-click room menu and a native inspector. Declared on the map surface; SurfaceParityTests compares MapDigest after native and cell edits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
- Ctrl+P > Script console: per character, output and errors with file:line, a time- and instruction-limited Lua prompt run through the host's gate, history, a read-only store viewer with delete, and Reload. One Client model drawn as cells and as a native modal; surface script-console with parity probes. - F5 > channels (off/copy/move): Comm.Channel.Text into a spawn window per channel through SpawnLine; move gags the main-window echo. - F5 > names: whole-word highlighted names with HighlightColours, at ingest. - Ctrl+P > Start/Stop scene log: the focused window's poses (or every line) to an HTML or plain file under the log root with a who-was-present header; refused with no log root. F7 > Scene log holds the format and content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Grave · project thread
The fix-now batch from the UX audit, plus the first wave of the UX plan.
What changed
Fix-now batch
82/100does not fit they show82%instead.ScreenChrome.TooNarrowForTwoandSingle) instead of drawing their editors off the right edge.SpreadTitle) shorten from the middle, so the close key stays./connect <host> [port] [tls]saves a world and dials it./connect <name>opens a world that is already configured.sharpmuterm-window, with their licences copied tolicenses/.Wave 1
Core/Layouts):config.jsonlayouts).WorldDefinition.LayoutandCharacterDefinition.Layout.LayoutResolvertries the character's layout, then the world's, then the accepted suggestion, then Roleplay.LayoutSuggestersuggests Combat when vitals arrive, and Roleplay when MSSP reports a MUSH-family codebase.PanelAvailabilitysays which panels have data yet.LayoutFoldturns a layout into the existingPaneNode/SplitNodetree.SharpMUTerm.Client, a new project with no UI dependencies:ContributionRegistrywith six kinds: commands, panels, settings sections, status cells, overlays and output decorators.DispatchCommandnow read their commands from the registry. The catalog was checked against the old one across 12,288 states before the old one was deleted.SharpMUTerm.Client.Tests, also added toci.yml.Trigger.MatchType: contains, starts with,*wildcards, or regex. Regex is the default, so old configs load unchanged.trigger-builder.ScreenThemeis passed through every view; there is no global state. Dark is byte-identical.FrameContrastTests.Testing
dotnet build -c Release SharpMUTerm.slnx: 0 errors.AReadOnlyCacheDirectory_DegradesToMemoryOnlyfails only because this sandbox runs as root.🤖 Generated with Claude Code
https://claude.ai/code/session_01Rqqt7SPYixp6Cg4JDKRTwx