tuispec 0.3.1.0 → 0.3.1.1
raw patch · 6 files changed
+49/−8 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- SKILL.md +6/−0
- src/TuiSpec/Internal.hs +32/−2
- src/TuiSpec/Runner.hs +5/−4
- src/TuiSpec/Types.hs +1/−1
- tuispec.cabal +1/−1
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Changelog +## 0.3.1.1 — 2026-06-06++* Fix selector clicks landing on the wrong column. A regex/substring selector resolved to its matching line's first non-blank column instead of the actual match position, so clicking centred text (e.g. a label behind a box border) hit the line's left edge and missed. `regexOrigins` now reports the match's start column via new origin-returning matchers `regexLikeMatchOrigin`/`wildcardContainsOrigin`, in terms of which the boolean `regexLikeMatch`/`wildcardContains` are now defined (so match and origin can't diverge).+ ## 0.3.1.0 — 2026-06-06 * Add mouse click simulation. DSL: `click`/`clickWith` (0-based coordinate) and `clickSelector`/`clickSelectorWith` (Playwright-style, resolves a selector to its match origin and honors `ambiguityMode`). New `MouseButton`, `MouseEncoding`, and `ClickOptions`/`defaultClickOptions` types. JSON-RPC: a `click` method accepting either `{col,row}` (1-based) or `{selector}`, with optional `button` and `encoding`. Clicks emit a press + release; SGR encoding is the default (what `vty`/Brick enable), with an `x10` override.
SKILL.md view
@@ -112,6 +112,12 @@ ignored. Throws `ChoiceSelectionError` on failure. - `artifactRoot tui`, `artifactFile tui rel`, `writeArtifactFile tui rel txt` — locate and write into the per-test artifact directory.+- `click tui col row` / `clickSelector tui selector` — simulate a mouse click+ at a 0-based coordinate or on the element a selector matches (Playwright+ style; honors `ambiguityMode`). Use `clickWith` / `clickSelectorWith` with+ `ClickOptions` to choose the button or the legacy X10 encoding. The target+ app must have mouse tracking enabled (`vty`/Brick apps do once mouse mode is+ on); the default SGR encoding suits any modern TUI. - `currentViewRect tui rect` — return the viewport text cropped to a region. - `dumpFailureBundle` / `withFailureBundle` — capture diagnostic state on failure (snapshot + viewport + action log + warnings + exit status).
src/TuiSpec/Internal.hs view
@@ -19,8 +19,10 @@ -- * Pattern matching regexLikeMatch,+ regexLikeMatchOrigin, cleanPattern, wildcardContains,+ wildcardContainsOrigin, -- * Theme helpers resolveAutoSnapshotTheme,@@ -33,6 +35,7 @@ import Control.Exception (SomeException, catch) import Data.Char (isAlphaNum, toLower) import Data.List (isSuffixOf)+import Data.Maybe (isJust) import Data.Text (Text) import Data.Text qualified as T import Text.Read (readMaybe)@@ -79,7 +82,18 @@ -} regexLikeMatch :: Text -> Text -> Bool regexLikeMatch patternText haystack =- any (`wildcardContains` haystack) alternatives+ isJust (regexLikeMatchOrigin patternText haystack)++{- | Like 'regexLikeMatch', but returns the 0-based column where the match+begins — the start of the first literal segment of the earliest-matching+alternative. This is the position a user sees and would click, so selector+clicks must target it rather than, say, the line's first non-blank column.+-}+regexLikeMatchOrigin :: Text -> Text -> Maybe Int+regexLikeMatchOrigin patternText haystack =+ case [col | alt <- alternatives, Just col <- [wildcardContainsOrigin alt haystack]] of+ [] -> Nothing+ cols -> Just (minimum cols) where alternatives = filter (not . T.null) $@@ -92,7 +106,23 @@ -- | Check whether a @.*@-delimited pattern matches within a haystack. wildcardContains :: Text -> Text -> Bool wildcardContains patternText haystack =- checkSegments 0 segments+ isJust (wildcardContainsOrigin patternText haystack)++{- | Like 'wildcardContains', but returns the 0-based start column of the match+— the position of the first literal (non-@.*@) segment — or 'Nothing'. A+pattern that is empty or all wildcards matches at column 0.+-}+wildcardContainsOrigin :: Text -> Text -> Maybe Int+wildcardContainsOrigin patternText haystack =+ case segments of+ [] -> Just 0+ (firstSeg : rest) ->+ let (prefix, suffix) = T.breakOn firstSeg haystack+ firstCol = T.length prefix+ in if not (T.null suffix)+ && checkSegments (firstCol + T.length firstSeg) rest+ then Just firstCol+ else Nothing where segments = filter (not . T.null) (T.splitOn ".*" patternText)
src/TuiSpec/Runner.hs view
@@ -91,7 +91,7 @@ import Test.Tasty (TestTree) import Test.Tasty.HUnit (assertFailure, testCase) import Text.Read (readMaybe)-import TuiSpec.Internal (cleanPattern, ignoreIOError, regexLikeMatch, resolveAutoSnapshotTheme, safeFileStem, safeIndex, snapshotMetadataPath, wildcardContains)+import TuiSpec.Internal (cleanPattern, ignoreIOError, regexLikeMatch, regexLikeMatchOrigin, resolveAutoSnapshotTheme, safeFileStem, safeIndex, snapshotMetadataPath, wildcardContains) import TuiSpec.ProjectRoot (resolveProjectRoot) import TuiSpec.Replay (RecordingDirection (DirectionFrame), RecordingEvent (..)) import TuiSpec.Types@@ -1972,13 +1972,14 @@ listToMaybe (regexOrigins patternText (viewportText viewport)) {- | All 0-based @(col, row)@ origins of a lightweight regex pattern, one per-matching line, taken at the line's first non-blank column.+matching line, at the column where the pattern's first literal segment matches+(so a selector click lands on the matched text, not the line's left edge). -} regexOrigins :: Text -> Text -> [(Int, Int)] regexOrigins patternText textValue =- [ (T.length (T.takeWhile (== ' ') line), rowIdx)+ [ (col, rowIdx) | (rowIdx, line) <- zip [0 ..] (T.lines textValue)- , regexLikeMatch patternText line+ , Just col <- [regexLikeMatchOrigin patternText line] ] -- | All 0-based @(col, row)@ origins of a literal substring within viewport text.
src/TuiSpec/Types.hs view
@@ -168,7 +168,7 @@ -- | The tuispec library/protocol version. tuispecVersion :: String-tuispecVersion = "0.3.1.0"+tuispecVersion = "0.3.1.1" -- | Key modifiers for combo key presses. data Modifier
tuispec.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.8 name: tuispec-version: 0.3.1.0+version: 0.3.1.1 build-type: Simple license: MIT license-file: LICENSE