Commit 87f7b79
fix(tui): polish sprint - cursor, hotkeys, auth flow, menus (#407)
* fix(tui): improve cursor visibility in text fields
Add high-contrast TextInput color scheme to TuiColorPalette with
BrightCyan background on focus for better cursor visibility. Apply
this scheme to all TextField instances across dialogs and screens.
Files updated:
- TuiColorPalette.cs: Add TextInput color scheme
- ProfileCreationDialog.cs: 7 TextFields
- EnvironmentSelectorDialog.cs: 2 TextFields
- ClearAllProfilesDialog.cs: 1 TextField
- QueryHistoryDialog.cs: 1 TextField
- SqlQueryScreen.cs: 1 TextField
- DataTableView.cs: 1 TextField
- ProfileSelectorDialog.cs: 1 TextField (rename dialog)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): replace auth method combobox with radio group
Replace ComboBox with RadioGroup in ProfileCreationDialog for
reliable rendering. ComboBox had known z-order and redraw issues
in Terminal.Gui 1.x that caused visual artifacts.
Changes:
- Replace _authMethodCombo with _authMethodRadio (RadioGroup)
- Adjust dialog height from 24 to 27 to accommodate RadioGroup
- Adjust Y positions of subsequent elements
- Update event handler signature to SelectedItemChangedArgs
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): show environment selector after auth success
Replace success message with immediate environment selector dialog
after authentication completes. This provides a seamless flow where
users authenticate, pick their environment, and the profile is ready.
Changes to ProfileCreationDialog:
- Remove success MessageBox
- Show EnvironmentSelectorDialog immediately after auth
- Add SelectedEnvironmentUrl/Name properties for post-auth selection
Changes to MainWindow:
- Add SetActiveProfileWithEnvironmentAsync method
- Use post-auth selected environment if available
Flow: Auth → Environment Selection → Close (no success message)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): document menu keyboard shortcuts
Update help dialog to document the menu navigation pattern:
- Alt+Letter opens menus globally (Alt+F, Alt+E, Alt+T, Alt+H)
- Single letter selects item when menu is open (underscore convention)
- Arrow keys, Enter, and Esc for menu navigation
Terminal.Gui handles single-letter hotkeys when menus are open via
the underscore prefix convention (_File, _SQL Query, etc.) which is
already in place.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): add menu click debounce to prevent flicker
Add 150ms debounce on MenuBar mouse click events to prevent the
intermittent double-fire issue where menus immediately close after
opening. Terminal.Gui 1.x can fire multiple events for a single
click in some terminal emulators.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): allow deletion of active/last profile
Previously the TUI blocked deletion of the active profile with
"Switch to a different profile first." This made it impossible to
delete the last remaining profile.
The app already handles null authentication gracefully - TUI starts
with no profile, CLI commands fail with helpful messages. Now the TUI
allows deleting any profile with appropriate warning if it's active.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use block cursor for text field visibility
Replace high-contrast BrightCyan background workaround with proper
block cursor via ANSI escape sequence (DECSCUSR). This addresses the
root cause - thin cursor visibility - rather than masking it with
jarring background colors.
- Add \x1b[2 q (steady block cursor) before Application.Init()
- Restore default cursor \x1b[0 q on shutdown
- Tone down TextInput focus from BrightCyan to Cyan
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): remove text field highlight and fix cursor position
- Remove cyan background from TextInput focus state - block cursor
provides sufficient visibility without jarring color changes
- Add SetFocus() to ProfileCreationDialog to position cursor in
name field on load instead of top-left corner
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): defer SetFocus to Ready event for cursor positioning
Move SetFocus() call from constructor to Ready event handler to ensure
layout is complete before positioning cursor. Constructor-time focus
was causing cursor to appear at dialog origin instead of text field.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): add keyboard handlers to dialogs
Add Enter and Escape key handlers to improve keyboard UX:
- ProfileCreationDialog: Enter on auth method RadioGroup advances to URL field,
Escape closes dialog (if not authenticating)
- ExportDialog: Enter on format RadioGroup triggers export, Escape closes
- EnvironmentSelectorDialog: Enter on URL field triggers select, Escape closes
- ProfileSelectorDialog: Escape closes dialog (added to existing OnKeyPress)
Terminal.Gui RadioGroup uses Space for selection by default; Enter was not
handled, breaking user expectations. These handlers make dialogs more
keyboard-friendly.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use GetByNameOrIndex for profile lookup
Profile lookup was using GetByName which fails when profile has no name
and DisplayIdentifier returns "[1]" format. Changed to GetByNameOrIndex
which handles both named profiles and index references.
Fixed in:
- InteractiveSession.InitializeAsync - for TUI session initialization
- ProfileServiceFactory.CreateFromProfileAsync - for service provider creation
- DaemonConnectionPoolManager - for daemon connection pool creation
This fixes "Profile '[1]' not found" error in Environment Details dialog
when using unnamed profiles.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent CTS disposed error in EnvironmentDetailsDialog
Add _disposed flag to track dialog disposal state and check it before
accessing cancellation token in MainLoop.Invoke callbacks.
The issue occurred because:
1. Dialog closes, triggering Dispose()
2. Dispose() cancels and disposes CancellationTokenSource
3. Async callback still running, tries to check token.IsCancellationRequested
4. Accessing disposed CTS token throws ObjectDisposedException
Now the callbacks check _disposed first, avoiding the token access after
CTS disposal.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): remove Enter handler from auth RadioGroup
The Enter handler was causing a flicker without providing useful behavior.
RadioGroup selection happens via arrow keys which select immediately as
you navigate. Removed the handler to restore default Terminal.Gui behavior.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): handle bracket notation in GetByNameOrIndex
GetByNameOrIndex("[1]") was failing for unnamed profiles because
int.TryParse("[1]") returns false. The TUI uses DisplayIdentifier
(which returns "[N]" for unnamed profiles) when deleting/renaming,
causing these operations to silently fail.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): make Enter select RadioGroup items
Terminal.Gui RadioGroup only binds Space to selection by default.
Arrow keys move the cursor but don't change SelectedItem until
Space is pressed. Added Enter key handler that simulates Space
to match user expectations.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add AboutDialog with official branding
- Create dedicated AboutDialog replacing MessageBox
- Apply official tagline from branding guidelines
- Add docs site link alongside GitHub repo
- Include copyright notice
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): adjust AboutDialog layout to prevent overlap
- Widen dialog to fit full GitHub URL
- Increase height for proper button spacing
- Move copyright above close button
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use semantic version and fix URL truncation in AboutDialog
- Use ErrorOutput.Version for full semantic version with beta suffix
- Move URLs to separate lines to prevent truncation
- Adjust dialog dimensions for new layout
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): increase AboutDialog height for button spacing
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): consolidate TUI entry points and fix device code dialog
- Remove redundant Discover button from ProfileCreationDialog
- Fix device code rendering: use MessageBox instead of nested dialog
- Consolidate ppds and ppds interactive to use same LaunchTui method
- Delete unused DeviceCodeAuthDialog.cs
- Auto-copy device code to clipboard for convenience
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): improve error dialog readability and clean up menu labels
Add ReadOnlyText color scheme to prevent cyan background on focused
read-only TextViews (was making error details unreadable). Remove
trailing ellipsis from menu item labels for cleaner appearance.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): resolve hotkey conflicts and improve status bar readability
- Fix duplicate Alt+C hotkey: change Close to Clos_e (Alt+E)
- Fix duplicate Alt+L hotkey: change C_lear All to Clear _All (Alt+A)
- Improve StatusBar_Default contrast: black on gray for readability
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): remove trailing slash from docs URL for consistency
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add interactive status bar with profile/environment switching
- Create TuiStatusBar component with clickable profile/environment sections
- Show environment-aware coloring (red=Production, yellow=Sandbox, green=Dev)
- Remove Environment menu - switching now via status bar clicks
- Simplify File menu - profile actions moved to ProfileSelectorDialog
- Add Details and Clear All buttons to ProfileSelectorDialog (two-row layout)
- Add Details button to EnvironmentSelectorDialog
- Add CurrentProfileName property and ProfileChanged event to InteractiveSession
- Update SqlQueryScreen to use shared TuiStatusBar component
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): polish profile selector dialog and status bar display
- Show identity (email/app ID) in status bar profile display
- Remove grey background from hint label in profile selector
- Remove asterisk from profile list (confusing with session-only switching)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add context-aware HotkeyRegistry for global shortcuts
- Add HotkeyRegistry with Global/Screen/Dialog scope layers
- Alt+P and Alt+E now work everywhere (close dialogs first)
- Screen hotkeys (Ctrl+E, Ctrl+H) blocked when dialog is open
- Fix button label conflicts (_Export → E_xport, etc.)
- Update F1 help with new shortcuts
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): remove pool warming on startup to prevent login prompt
TUI was triggering auth dialog immediately on startup due to pool
warming in InitializeAsync(). This was introduced in cb41e2e to
improve first-query latency, but became problematic after 6051698
added PreAuthenticationDialog which made the auth visible as a dialog.
Changes:
- Remove WarmPoolAsync() call from InitializeAsync()
- Delete unused WarmPoolAsync() method
- Connection/auth now happens lazily on first query
This returns to the pre-cb41e2e behavior where TUI starts instantly
without prompting for authentication.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): always show profile index in DisplayIdentifier
Change DisplayIdentifier to always include the index prefix:
- Named profile: "[1] MyProfile" (was just "MyProfile")
- Unnamed profile: "[1]" (unchanged)
This makes profile identification consistent across CLI and TUI,
allowing users to always reference profiles by index.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent double key processing causing F1+Escape crash
- Remove duplicate shortcut: params from F1/F2/F12 menu items (already
registered as global hotkeys in HotkeyRegistry)
- Add using pattern to all dialog creation methods for proper disposal
- Clear active dialog state immediately when handling global hotkeys
The crash occurred because F1/F2/F12 were registered in both the menu
system AND HotkeyRegistry, causing Terminal.Gui to process these keys
through multiple paths and corrupt internal state.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent nested SqlQueryScreen creation on repeated F2
F2 global hotkey was creating stacked Application.Run() calls when
pressed repeatedly or from within SqlQueryScreen. This corrupted
Terminal.Gui internal state and caused Border.SetBorderBrush crash.
- Add ActiveScreen property to IHotkeyRegistry
- Check if already in SqlQueryScreen before creating new one
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use custom dialog for keyboard shortcuts instead of MessageBox
Terminal.Gui's MessageBox.Query has a bug where Border.SetBorderBrush
crashes with null reference after rapid screen/dialog transitions.
Replace with custom KeyboardShortcutsDialog that we control.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): defer global hotkey handlers to prevent Terminal.Gui state corruption
Root cause: Starting Application.Run() from within a key event handler
(HotkeyRegistry.TryHandle) corrupts Terminal.Gui's internal state,
causing Border.SetBorderBrush to crash with NullReferenceException.
The fix defers ALL global hotkey handlers to the next main loop iteration
via MainLoop.Invoke(). This ensures the current key event fully completes
before any new Application.Run() calls start.
Also adds proper disposal to SqlQueryScreen.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent duplicate global hotkey handlers from stacking up
When F1/F2 keys are pressed rapidly, multiple handlers were queued via
MainLoop.Invoke(). These would all execute sequentially, creating
overlapping dialogs/screens that corrupt Terminal.Gui state.
Fix: Track pending global handlers in a HashSet. Ignore subsequent
key presses if a handler for that key is already pending. Clear
the pending state after handler completes.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent cross-key global hotkey race condition causing crash
The per-key HashSet only prevented same-key duplicates (F1+F1) but not
cross-key stacking (F1+F2). When F2 was pressed while F1's dialog was
open, RequestStop() was called but the new Application.Run() started
before cleanup finished, corrupting Terminal.Gui's ConsoleDriver state.
Solution: Replace HashSet<Key> with single boolean gate. When a global
hotkey is pressed while dialog is open, just close the dialog - don't
execute the handler. User must press key again after dialog closes.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* refactor(tui): add consistent 2-line footer across all screens
- Add TuiStatusLine component for contextual status messages and spinner
- Remove status message functionality from TuiStatusBar (now profile/env only)
- Update MainWindow and SqlQueryScreen to use new 2-line footer pattern
- Profile/environment bar stays at bottom, status line above it
- Spinner no longer hides profile/env buttons during operations
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add autonomous testing feedback loop with state capture and tui-test
Enable Claude to autonomously iterate on TUI code without human visual inspection.
Phase 1 - State Capture Infrastructure:
- Add ITuiStateCapture<T> interface for testable state exposure
- Add 15 state record types for screens, dialogs, and components
- Implement CaptureState() on MainWindow, TuiStatusBar, SqlQueryScreen,
ProfileSelectorDialog, and ErrorDetailsDialog
- Fix 2 failing TuiUnit tests (lazy-loading behavior after commit 9a14526)
Phase 2 - Visual Snapshot Testing:
- Add tui-e2e npm project using @microsoft/tui-test
- Add startup flow tests with snapshot verification
- Add CI workflow for TUI E2E tests on TUI file changes
- Update /test command to include tui-e2e in TUI testing flow
Claude's new feedback loop:
- Fast logic: dotnet test --filter "Category=TuiUnit"
- Visual: npm test --prefix tests/tui-e2e
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* chore(tui-e2e): pin Node 20 LTS and add setup docs
- Add .nvmrc pinning Node 20 (required by @microsoft/tui-test)
- Add README with setup instructions and usage
- Fix package.json version (0.0.1-rc.5 is latest available)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* docs(tui-e2e): add nvm-windows installation instructions
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui-e2e): configure @microsoft/tui-test correctly for ESM
- Add ESM support: "type": "module" in package.json, ESNext in tsconfig
- Switch test runner from Playwright to @microsoft/tui-test CLI
- Use test.use({ program }) API instead of terminal.spawn()
- Fix path resolution using process.cwd() for correct repo root
- Add tui-test.config.ts with trace enabled
- Add initial snapshot for main menu visual regression
- Update .gitignore for tui-traces and .tui-test cache
Tests now pass: launches main menu, status bar, Ctrl+Q quit
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* chore(tui-e2e): remove unused playwright.config.ts
Now using tui-test.config.ts with @microsoft/tui-test runner.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(commands): add /tui-review for UX evaluation
Enables structured TUI screen evaluation with:
- Setup and launch instructions
- Interaction checklist
- Structured report format (What works, Issues, Improvements)
- Severity levels (P1/P2/P3)
- Screen file location reference
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): resolve SQL Query screen UX issues
- Fix ClearData() call on environment switch (was Clear() which only cleared visually)
- Change query history hotkey to Ctrl+Shift+H (Ctrl+H now exclusively for GUID toggle)
- Remove status bar buttons from Tab order with CanFocus=false
- Add F6 hotkey to toggle focus between query input and results table
- Fix Escape to return to query input when in results (not close screen)
- Fix filter frame layout by adjusting results table Y position on show/hide
- Add visual focus indicators using TuiColorPalette.Focused on Enter/Leave
- Update query frame title hint to include F6 shortcut
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* test(tui-e2e): add SQL Query screen E2E tests
Add automated tests for SQL Query screen using @microsoft/tui-test:
- F2 opens SQL Query screen
- Default query pre-populated
- Escape returns to main menu
- Ctrl+E shows error when no data
- Ctrl+Shift+H behavior without environment
- Status bar displays profile info
- Tab navigation between controls
- Visual regression snapshots
12 tests now pass covering startup and SQL Query flows.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): resolve UX review issues round 2
P2 fixes:
- Fix status line not rendering during construction (MainLoop null)
- Add text truncation with ellipsis to status bar buttons
P3 fixes:
- Add ▶ prefix to focused frame titles for better visibility
- Center "No data" message in results table
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* test(tui-e2e): update snapshots for status bar and focus fixes
Updated snapshots reflect UX improvements:
- Status bar now truncates profile with ellipsis, shows full env name
- Focus indicator (▶) now appears in focused frame title
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): properly fix status line and No data centering
Status line fix:
- Use Label child view instead of manual Driver drawing
- Driver/Bounds are null during construction, Label handles rendering
No data centering fix:
- Use TextAlignment.Centered instead of Pos.Center()
- Pos.Center() with Dim.Fill() doesn't center text content
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* test(tui-e2e): update snapshots for status line and centered empty state
All 4 UX issues now verified fixed:
- Status line renders ("Connecting to environment...")
- "No data" text centered in Results frame
- Status bar truncates profile, shows full environment
- Focus indicator (▶) visible in active frame
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): set environment URL synchronously to fix status bar connection bug
The status bar showed the environment correctly but SQL queries failed with
"No environment selected" because _currentEnvironmentUrl was never set in
InitializeAsync(), SetEnvironmentAsync(), and SetActiveProfileAsync().
- InitializeAsync: now sets _currentEnvironmentUrl along with display name
- SetEnvironmentAsync: now sets _currentEnvironmentUrl after invalidation
- SetActiveProfileAsync: now sets URL and fires EnvironmentChanged synchronously
instead of inside async continuation (matching SetEnvironmentAsync behavior)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* refactor(tui): use hotkey registry for Ctrl+Enter in SQL query screen
Replace direct KeyPress handler with registered hotkey for consistency
with other keyboard shortcuts in the application.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): restore status bar click handling with custom MouseEvent
Terminal.Gui buttons with CanFocus=false don't receive click events.
Added MouseEvent override to handle clicks at the View level, preserving
clean tab order (query ↔ results only) while restoring mouse interaction.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): rewrite status bar with direct rendering for reliable mouse handling
Terminal.Gui child views consume mouse events before parents can handle them,
even with CanFocus=false. Rewrote TuiStatusBar to:
- Remove all child views (Labels/Buttons)
- Draw content directly in Redraw() override
- Set WantMousePositionReports=true to receive mouse events
- Handle clicks in MouseEvent without child view interference
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* refactor(tui): implement shell architecture with persistent menu/status bar
Replace MainWindow with TuiShell that provides:
- Persistent menu bar visible on all screens
- Persistent status bar (no duplication)
- Content area where screens swap via NavigateTo/NavigateBack
- Context-aware menus (screen-specific items inserted between File and Help)
Refactor SqlQueryScreen to implement ITuiScreen interface:
- No longer inherits from Window
- Provides Content view, Title, and ScreenMenuItems
- Uses CloseRequested event for navigation instead of RequestStop
- Hotkeys registered in OnActivated, unregistered in OnDeactivating
This fixes the issue where menu bar was not visible on SqlQueryScreen
and eliminates duplicated status bar code.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): polish UI colors and column sizing
- Fix results table highlight: only change title, not entire table color
- Auto-size columns based on content with padding and breathing room
- Fix query history dialog: proper color schemes for ListView and TextView
- Remove Ctrl+Q quit shortcut (conflicts with Query menu, Alt+F4 is standard)
- Fix export dialog: dark background instead of grey
- Dark menu bar with cyan accents to match overall theme
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): fix filter functionality and add status auto-restore
Filter:
- Filter now actually filters visible rows (was only updating DefaultView)
- Creates filtered DataTable from DefaultView.ToTable() and assigns to TableView
- Stores unfiltered data for restore when filter is cleared
- Shows "(filtered: X of Y)" in persistent status line
Status line:
- Temporary messages (copy, errors) auto-restore after 2.5s
- Uses token-based cancellation to handle rapid successive messages
- Filter info persists in main status, not as temporary message
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): apply color scheme recursively to SaveDialog subviews
The SaveDialog's internal file list wasn't inheriting the color scheme.
Now recursively applies TuiColorPalette.Default to all subviews.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add JSON export, fix profile state, improve UX
- Fix profile state bug: update _profileName in InitializeAsync when
using active profile so CurrentProfileName returns actual name
- Fix SaveDialog colors: apply color scheme in Loaded event to prevent
Terminal.Gui's default blue background from leaking through
- Add Ctrl+W and Q shortcuts for direct window close (hybrid escape UX)
- Add JSON export with type preservation using column metadata
JSON export converts string values back to proper types (guid, int,
decimal, boolean, datetime) using the QueryColumnType metadata stored
during query result conversion.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* docs: document TUI testing philosophy and workflow
Codify the principle that CLI tests verify service logic while TUI tests
verify presentation. This avoids duplicating test coverage across layers.
Changes:
- CLAUDE.md: Add NEVER rule for re-testing services in TUI E2E
- ADR-0028: Add Test Responsibility Matrix and development workflow
- /test command: Add TUI philosophy and snapshot diff guidance
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add TuiDialog base class for standardized dialog behavior
Adds TuiDialog abstract base class that all dialogs should inherit from.
Features:
- Automatic HotkeyRegistry integration (blocks screen-scope hotkeys while dialog is open)
- Consistent ColorScheme (TuiColorPalette.Default)
- Standard Escape key handling (override OnEscapePressed for custom behavior)
- Proper cleanup on disposal
This addresses the issue where only 1 of 12 dialogs properly registered
with the HotkeyRegistry, causing screen-scope hotkeys like F2 to fire
while dialogs were open.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): add Application.Refresh() before PreAuthenticationDialog to fix border rendering
When PreAuthenticationDialog is shown from a MainLoop.Invoke() callback
triggered by background authentication, Terminal.Gui's ConsoleDriver
state may be unstable. This caused the dialog content to render but the
border/frame to be missing.
Adding Application.Refresh() before showing the dialog forces a full
screen redraw, stabilizing the ConsoleDriver state before
Application.Run() starts the nested event loop.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* refactor(tui): migrate all dialogs to TuiDialog base class
Migrates all 12 dialogs to inherit from TuiDialog, providing:
- Automatic HotkeyRegistry integration (SetActiveDialog/clear on dispose)
- Consistent ColorScheme (TuiColorPalette.Default)
- Standard Escape key handling (override OnEscapePressed for custom behavior)
- Proper cleanup on disposal
Dialogs migrated:
- AboutDialog, ClearAllProfilesDialog, EnvironmentDetailsDialog
- EnvironmentSelectorDialog, ErrorDetailsDialog, ExportDialog
- KeyboardShortcutsDialog, PreAuthenticationDialog, ProfileCreationDialog
- ProfileDetailsDialog, ProfileSelectorDialog, QueryHistoryDialog
Special cases:
- PreAuthenticationDialog: Override OnEscapePressed to set Result before close
- ProfileCreationDialog: Override OnEscapePressed to block during auth
- EnvironmentDetailsDialog: Keeps existing Dispose for CancellationTokenSource
This fixes the issue where only 1 of 12 dialogs properly registered with
the HotkeyRegistry, causing screen-scope hotkeys like F2 to fire while
dialogs were open.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): remove global menu item hotkeys, keep menu bar hotkeys only
Terminal.Gui's MenuItem underscore convention (_Quit) creates global
Alt+letter hotkeys that work even when menus aren't open. This caused
Alt+Q to quit the app from anywhere, conflicting with the Query menu.
Solution:
- Keep underscores on MenuBarItem names (_File, _Tools, _Help, _Query)
so Alt+F/T/H/Q open the respective menus
- Remove underscores from MenuItem names (Quit, About, Execute, etc.)
so they no longer create global hotkeys
Result:
- Alt+F opens File menu, Alt+Q opens Query menu (when on SQL screen)
- Once menu is open, first-letter navigation still works (Q for Quit)
- No more accidental Alt+Q closing the app
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): implement ITuiStateCapture on all dialogs
Adds ITuiStateCapture implementation to all 12 dialogs, enabling
autonomous testing via state capture pattern. Tests can now verify
dialog state without requiring Terminal.Gui runtime.
Dialogs with state capture:
- AboutDialog: ProductName, Version, Description, GitHubUrl
- ClearAllProfilesDialog: WarningMessage, ProfileCount, button state
- EnvironmentDetailsDialog: DisplayName, Url, Type, OrgId, Version
- EnvironmentSelectorDialog: Environment list, selection, loading state
- ErrorDetailsDialog: Error list, selected error, details
- ExportDialog: Format selection, row count, headers option
- KeyboardShortcutsDialog: Shortcut list with key/description/scope
- PreAuthenticationDialog: Available options, selected option
- ProfileCreationDialog: Auth method, profile name, creation state
- ProfileDetailsDialog: Profile info, identity, environment details
- ProfileSelectorDialog: Profile list, selection, loading/error state
- QueryHistoryDialog: History items, selection, empty state
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* test(tui): add dialog state capture unit tests
Adds unit tests demonstrating the ITuiStateCapture pattern for dialogs.
Tests can verify dialog state without Terminal.Gui runtime, enabling
autonomous testing in CI/CD pipelines.
Test coverage includes:
- AboutDialog: Title, ProductName, Version, GitHubUrl
- KeyboardShortcutsDialog: Shortcuts list with global and screen-specific
- PreAuthenticationDialog: Available options, device code presence
- State record equality verification
Total test count: 35 → 46 (+11 dialog tests)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): fix spinner rendering and improve status bar layout
TuiSpinner:
- Fix rendering by using proper Terminal.Gui pattern (Redraw override)
- Was incorrectly trying to draw directly in Invoke() callback
- Now stores display text and lets Terminal.Gui call Redraw()
TuiStatusBar:
- Pin profile to left, environment to right (better use of space)
- Remove hard-coded truncation limits (18/28 chars)
- Dynamic truncation based on actual terminal width
SqlQueryScreen:
- Add spinner + status label for query execution feedback
- Shows "Executing query..." during execution
Dialogs:
- Defer spinner start to Loaded event (ensures dialog is visible)
- EnvironmentDetailsDialog: responsive width (Dim.Percent(80))
F2 key conflict:
- Remove F2 global shortcut for SQL Query (was conflicting with rename)
- F-keys now reserved for actions, menus for navigation
Other:
- Remove Quit button from main menu (use File > Quit or Ctrl+Q)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): persist HomeAccountId after environment discovery auth
Environment Selector was prompting for login every time because
GlobalDiscoveryService did not expose the HomeAccountId captured
during interactive authentication. EnvironmentService now persists
this value to the profile, enabling silent token acquisition on
subsequent discovery calls.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): polish punch list - cursor, rename, dropdown, focus
- Set cursor color to black via OSC 12 escape (terminal-dependent)
- Fix profile rename error: use DisplayIdentifier instead of Name
- Add FileDialog color scheme with white text on cyan for readability
- Auto-focus filename field when SaveDialog opens
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): set Colors.Menu for ComboBox dropdown readability
Terminal.Gui ComboBox popups use the global Colors.Menu scheme,
not the parent view's ColorScheme. Now setting Colors.Menu to
FileDialog scheme before showing SaveDialog, then restoring after.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add Ctrl+A select all in SQL query editor
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): fix Ctrl+A/C/X keyboard shortcuts in SQL query editor
- Ctrl+A: Use ProcessKey to properly select all text (SelectAll() just moves cursor)
- Ctrl+C/X: Add HasFocus guard to prevent table handler firing when query input focused
Fixes issue where Ctrl+A moved cursor instead of selecting, and Ctrl+C
showed "failed to copy cell" error when copying from query input.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): add VerifyPersistence() to ensure MSAL token cache persists
The MSAL token cache file was never created because CreateAndRegisterCacheAsync()
didn't call VerifyPersistence() after creating the MsalCacheHelper. Without this
call, MSAL.Extensions silently fails to persist tokens - the cache registration
"succeeds" but no file is written.
Added VerifyPersistence() call which performs a write/read/clear test and throws
MsalCachePersistenceException if persistence is unavailable. Also improved
diagnostic logging to show verification status and file existence.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use selection properties directly for Ctrl+A select all
Previous approach using ProcessKey didn't work. Now directly set
SelectionStartColumn/Row to (0,0) and CursorPosition to document end.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): handle Ctrl+A directly on TextView to intercept before default
Terminal.Gui's TextView handles Ctrl+A internally (move to start).
By attaching handler directly to _queryInput.KeyPress, we intercept
the key before Terminal.Gui's default handling takes over.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): handle Shift+Space/Delete/Backspace in SQL query editor
Terminal.Gui doesn't process these key combinations by default.
Convert them to their non-shifted equivalents so they work normally.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add Alt/Ctrl+Backspace/Delete for word deletion
- Alt+Backspace and Ctrl+Backspace: delete word before cursor
- Alt+Delete and Ctrl+Delete: delete word after cursor
Standard text editing shortcuts for faster query editing.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent Alt key from focusing menu after Alt+modifier combos
When using Alt+Backspace or Alt+Delete for word deletion, the Alt key
release was causing Terminal.Gui to focus the menu bar. Added
SuppressNextAltMenuFocus() to HotkeyRegistry to intercept the bare
Alt key event after it was used as a modifier.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): use reflection to reset MenuBar's openedByAltKey field
The MenuBar uses OnKeyDown/OnKeyUp (not KeyPress) for Alt handling.
When Alt is pressed, it sets openedByAltKey=true internally. We now
use reflection to reset this field when handling Alt+modifier combos,
preventing the menu from stealing focus on Alt release.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): explicitly handle Ctrl+Z/Y for undo/redo in query editor
Terminal.Gui has built-in undo/redo but something may be blocking
the key events. Explicitly intercept and pass through to ProcessKey.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent accidental menu item selection and crash on quit
- Block single letter keys when menu dropdown is open to prevent
first-letter navigation (e.g., Q selecting Quit in File menu)
- Close menu before calling RequestStop() to prevent Terminal.Gui
state corruption that caused NullReferenceException on mouse events
- Rename "Quit" to "Exit" as defensive measure (E less likely accidental)
- Remove undocumented Ctrl+Q from keyboard shortcuts help
- Fix test that incorrectly asserted F2 as global shortcut
The crash was in Menu.SetParentSetNeedsDisplay() when mouse events
arrived after menu state corruption from quitting while menu was open.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): also block Alt+letter when menu is open
Alt+S was still selecting "SQL Query" when File menu was open.
Now blocks both plain letters and Alt+letter combinations.
Ctrl+letter is still allowed for potential shortcuts.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): ensure Alt+F/T/H opens menus even when TextView has focus
Terminal.Gui's TextView intercepts Alt+F for emacs-style word navigation,
preventing the MenuBar from receiving it. Now we intercept Alt+F/T/H in
RootKeyEvent and set focus to MenuBar first, ensuring menus open correctly.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* revert: remove SetFocus approach that broke Alt+arrows
The SetFocus() call to redirect Alt+F/T/H to MenuBar was causing
inconsistent behavior and breaking Alt+arrow word navigation.
Alt+F not opening menu when in SQL text box is a Terminal.Gui limitation -
TextView's emacs-style bindings consume Alt+F for word-forward navigation.
Users can use F9 or mouse to access menus instead.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add Alt+Left/Right for word navigation in SQL editor
Alt+Left and Alt+Right now work the same as Ctrl+Left/Right for
word-by-word cursor navigation in the SQL query editor.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add token expiry handling with re-authentication dialog
Auth error detection (benefits all UIs):
- Add AuthenticationErrorDetector for 401/403 detection
- Add DataverseAuthenticationException with RequiresReauthentication flag
- Update ThrottleDetector to detect and wrap auth errors
- Update ExceptionMapper to handle DataverseAuthenticationException
TUI-specific handling:
- Add ReAuthenticationDialog for session expiry prompt
- Add InvalidateAndReauthenticateAsync to InteractiveSession
- Handle auth errors in SqlQueryScreen with retry on re-auth
Quick fixes:
- Fix LastUsedAt persistence in ProfileServiceFactory
- Add Object Id and PUID to ProfileDetailsDialog (align with CLI)
- Remove Type/Region from EnvironmentDetailsDialog (shown in header badge)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): always update LastUsedAt on profile use
Previously, LastUsedAt only updated when HomeAccountId changed (first
auth or re-auth). Now the callback fires on every successful connection,
so LastUsedAt tracks actual usage.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* refactor(tui): reorganize menu structure for scalability
Menu structure now follows consistent UX pattern:
- File: Export (context-aware), Exit
- [Context Menu]: Screen-specific actions (Query: Execute, History, Filter)
- Tools: SQL Query, Data Migration, Solutions, Plugin Traces
- Help: Keyboard Shortcuts, About, Error Log
This pattern scales as new tools are added (Metadata Explorer,
Data Explorer, Import Jobs, etc.) - all go under Tools menu.
Export moved from Query menu to File menu (universal convention).
Added ExportAction to ITuiScreen interface for context-aware enabling.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): fix profile update and environment persistence
Bug 1: LastUsedAt never updated for unnamed profiles
- ProfileStore.UpdateProfileAsync used GetByName() which doesn't
support bracket notation like "[1]"
- Changed to GetByNameOrIndex() to support DisplayIdentifier format
Bug 2: Environment not saved when creating new profile
- SetActiveProfileAsync() only updated in-memory state
- Added call to profileService.SetEnvironmentAsync() to persist
environment selection to profile file
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): handle full DisplayIdentifier format in profile lookups
GetByNameOrIndex() now parses "[N] Name" format (not just "[N]"), fixing
profile switching in TUI and enabling bracket notation in CLI commands.
- Enhanced GetByNameOrIndex() to extract index from "[N] ..." patterns
- Fixed ProfileSelectorDialog rename re-selection (compare Name not DisplayIdentifier)
- Changed GetByName() to GetByNameOrIndex() in CLI select/delete commands
- Changed GetByName() to GetByNameOrIndex() in RPC auth/select handler
- Changed GetByName() to GetByNameOrIndex() in ProfileServiceFactory
- Added unit tests for DisplayIdentifier parsing
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(tui): add FetchXML preview and Cache Type to profile details
- Add FetchXmlPreviewDialog for viewing transpiled SQL queries
- Add Ctrl+Shift+F hotkey and Query menu item for FetchXML preview
- Add Cache Type field to ProfileDetailsDialog (aligns with CLI ppds auth who)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): fix FetchXML preview compile error and update E2E tests
- Fix CS8604 error in ShowFetchXmlDialogAsync by using _environmentUrl
instead of _deviceCodeCallback (caller already guards for null)
- Update E2E tests to use Tools menu navigation instead of removed F2 shortcut
- Update test snapshots to match current UI (removed Quit button, etc.)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): enable File > Export menu after query execution
The File > Export menu item stayed disabled even after query returned
data because ExportAction was evaluated once at screen load time.
Fix: Add MenuStateChanged event to ITuiScreen interface. SqlQueryScreen
raises this event after LoadResults(), and TuiShell rebuilds the menu
bar in response, enabling the Export item.
Includes interface contract tests to prevent regression.
Closes #405
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): enforce black text on blue/cyan backgrounds
- Fix FetchXmlPreviewDialog: add ReadOnlyText ColorScheme to prevent grey text
- Fix FileDialog: change Focus/HotFocus from White to Black on Cyan
- Fix StatusBar_Trial: change all attributes to Black on Cyan
- Fix Selected: change HotNormal/HotFocus/Disabled to Black on Cyan
- Add ValidateBlueBackgroundRule() helper for automated validation
- Add TuiColorPaletteTests to fail build on future violations
- Add NEVER rule to CLAUDE.md for blue background text color
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): make read-only text white instead of grey
Terminal.Gui treats ReadOnly TextViews as Disabled, so the Disabled
attribute colors are used. Changed ReadOnlyText.Disabled to White
so FetchXML preview and other read-only content is fully visible.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): prevent Query History from triggering unnecessary authentication
QueryHistoryService is a local file-based service that doesn't need a
Dataverse connection, but GetQueryHistoryServiceAsync() was going through
GetServiceProviderAsync() which triggers authentication. This caused the
Query History dialog to hang on "Loading..." when auth tokens weren't cached.
- Add GetQueryHistoryService() sync method to InteractiveSession (like GetProfileService)
- Simplify ShowHistoryDialog() to use the sync method directly
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): show loading spinner when navigating to SQL Query screen
Add loading indicator when user clicks SQL Query from main menu.
Uses TuiStatusLine's built-in spinner with "Loading SQL Query..." message.
Defers screen creation via AddIdle() so UI refreshes before blocking work.
The delay is expected (Terminal.Gui component creation + JIT compilation)
but users perceive a frozen UI as broken. This provides immediate feedback.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): center 'No data' label in query results
Move empty state label to center of results pane instead of bottom.
Separate from status label which stays at bottom for row count/shortcuts.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(auth): prevent tests from deleting global MSAL token cache
ProfileService.ClearAllAsync() was calling TokenCacheManager.ClearAllCachesAsync()
which always deleted the global token cache at %LOCALAPPDATA%\PPDS\msal_token_cache.bin.
This meant running unit tests would nuke the user's real auth tokens.
Fix: TokenCacheManager now accepts an optional path parameter. ProfileService
derives the token cache path from the ProfileStore's directory, so tests using
isolated temp directories delete their own cache, not the global one.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): catch MainLoop.Invoke exceptions and log to error service
Previously, exceptions in MainLoop.Invoke callbacks would crash the TUI
because they execute outside the scope of surrounding try/catch blocks.
This defeated the purpose of the error logging infrastructure.
Changes:
- Add global errorHandler to Application.Run() that catches unhandled
exceptions and reports them to TuiErrorService
- Add MainLoopExtensions.SafeInvoke helper for wrapped callback execution
- Fix DuplicateNameException crash by handling duplicate column names
in QueryResultConverter (appends _1, _2, etc. to duplicates)
- Wrap SqlQueryScreen.ExecuteQueryAsync callback in try/catch/finally
to ensure spinner stops and state resets even on error
- Add tests for duplicate column name handling
Now exceptions are caught, logged to ~/.ppds/tui-debug.log, and shown
via F12 (Error Details dialog) instead of crashing the application.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* feat(sql): auto-expand lookup, optionset, and boolean columns with *name variants
SQL queries now automatically expand lookup, optionset, and boolean columns
to show both raw values and formatted display names, matching the behavior
of the old VS Code extension.
- SELECT ownerid → shows ownerid (GUID) + owneridname (display name)
- SELECT statuscode → shows statuscode (int) + statuscodename (label)
- SELECT ismanaged → shows ismanaged (bool) + ismanagedname (Yes/No)
Virtual column support allows querying *name columns directly:
- SELECT owneridname → shows only owneridname (hides base column)
- SELECT ownerid, owneridname → shows both (no duplicates)
Changes:
- QueryValue: Add IsLookup, IsOptionSet, IsBoolean detection properties
- SqlToFetchXmlTranspiler: Detect virtual *name columns, transpile base column
- SqlQueryResultExpander: Handle auto-expansion and virtual columns
- SqlQueryService: Integrate transpiler and expander
- Both columns preserve lookup metadata for TUI navigation (Ctrl+O)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix: address CodeQL and Copilot code review findings
- Remove unused addedVirtualColumns HashSet in SqlQueryResultExpander
- Add CancellationToken propagation to ProfileCreationDialog
- Wrap screen disposal in try/catch for exception safety in TuiShell
- Remove redundant Color casts in TuiColorPalette
- Use TryGetValue instead of ContainsKey+indexer in tests
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tui): wrap Pop() in try-catch to satisfy CodeQL dispose check
Addresses CodeQL alert about potential missed Dispose if Pop() throws.
Both Pop() and Dispose() are now wrapped in nested try-catch blocks
to ensure the disposal loop continues even if either operation fails.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
---------
Co-authored-by: Claude Opus 4.5 <[email protected]>1 parent a026385 commit 87f7b79
File tree
106 files changed
+9915
-1380
lines changed- .claude/commands
- .github/workflows
- docs/adr
- src
- PPDS.Auth
- Credentials
- Discovery
- Pooling
- Profiles
- PPDS.Cli
- Commands
- Auth
- Serve/Handlers
- Infrastructure
- Errors
- Services
- Environment
- Export
- Profile
- Query
- Tui
- Dialogs
- Infrastructure
- Screens
- Testing
- States
- Views
- PPDS.Dataverse
- Query
- Resilience
- Sql/Transpilation
- tests
- PPDS.Auth.Tests/Profiles
- PPDS.Cli.Tests
- Mocks
- Services
- Profile
- Query
- Tui
- Dialogs
- Infrastructure
- Screens
- Views
- PPDS.Dataverse.Tests/Sql/Transpilation
- tui-e2e
- tests
- __snapshots__
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
106 files changed
+9915
-1380
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
37 | 42 | | |
38 | 43 | | |
39 | 44 | | |
40 | 45 | | |
41 | | - | |
| 46 | + | |
42 | 47 | | |
43 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
44 | 55 | | |
45 | 56 | | |
46 | 57 | | |
47 | 58 | | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
48 | 95 | | |
49 | 96 | | |
50 | 97 | | |
| |||
177 | 224 | | |
178 | 225 | | |
179 | 226 | | |
| 227 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
| 38 | + | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
62 | 102 | | |
63 | 103 | | |
64 | 104 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
| 77 | + | |
| 78 | + | |
77 | 79 | | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
81 | | - | |
82 | 83 | | |
83 | 84 | | |
84 | 85 | | |
| |||
91 | 92 | | |
92 | 93 | | |
93 | 94 | | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
94 | 101 | | |
95 | 102 | | |
96 | | - | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
97 | 106 | | |
98 | 107 | | |
99 | 108 | | |
100 | 109 | | |
101 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
102 | 113 | | |
103 | 114 | | |
104 | 115 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
15 | 19 | | |
16 | 20 | | |
17 | 21 | | |
18 | 22 | | |
19 | | - | |
| 23 | + | |
20 | 24 | | |
21 | | - | |
| 25 | + | |
| 26 | + | |
22 | 27 | | |
23 | | - | |
| 28 | + | |
24 | 29 | | |
25 | 30 | | |
26 | 31 | | |
| |||
0 commit comments