tuffgal 0.2.0-alpha.5 → 0.2.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,20 @@ this project uses [Pride Versioning](https://pridever.org) → `PROUD.DEFAULT.SH
8
8
 
9
9
  _Nothing just yet_
10
10
 
11
+ ## [0.2.0-alpha.6] – 2026-08-09
12
+
13
+ ### Added
14
+
15
+ - Key glyph and abbreviation aliases for the `type` step: `Ctrl`,
16
+ `Cmd`/`⌘`, `Opt`/`⌥`, `Esc`, and `Return` resolve to their Playwright
17
+ key names, on their own and inside a combo
18
+
19
+ ### Changed
20
+
21
+ - An unrecognized key in a `type` step now fails with an error naming the
22
+ rejected key, the step value, and the aliases tuffgal accepts, in place
23
+ of Playwright's bare `Unknown key`
24
+
11
25
  ## [0.2.0-alpha.5] – 2026-07-28
12
26
 
13
27
  ### Fixed
@@ -305,7 +319,8 @@ styling or interactivity.
305
319
  Initial public alpha. Tuffgal extracted from [Linklater](https://github.com/nschneble/linklater)'s
306
320
  in-tree visual testing workspace.
307
321
 
308
- [Unreleased]: https://github.com/nschneble/tuffgal/compare/v0.2.0-alpha.5...HEAD
322
+ [Unreleased]: https://github.com/nschneble/tuffgal/compare/v0.2.0-alpha.6...HEAD
323
+ [0.2.0-alpha.6]: https://github.com/nschneble/tuffgal/releases/tag/v0.2.0-alpha.6
309
324
  [0.2.0-alpha.5]: https://github.com/nschneble/tuffgal/releases/tag/v0.2.0-alpha.5
310
325
  [0.2.0-alpha.4]: https://github.com/nschneble/tuffgal/releases/tag/v0.2.0-alpha.4
311
326
  [0.2.0-alpha.3]: https://github.com/nschneble/tuffgal/releases/tag/v0.2.0-alpha.3
package/README.md CHANGED
@@ -115,7 +115,7 @@ Precedence is `1` > `3` > `2` > `0`. Local mode only ever exits `1` or `0`.
115
115
 
116
116
  ## What's explicitly out of scope (v1)
117
117
 
118
- - AI fuzzy locator matching (deferred to v1.1, BYOLLM)
118
+ - AI fuzzy locator matching
119
119
  - Hosted SaaS / cloud runs
120
120
  - Native mobile (Playwright cannot drive it)
121
121
  - WebDriver / Puppeteer substrate
@@ -153,16 +153,15 @@ MIT. See [LICENSE](LICENSE).
153
153
 
154
154
  ## Roadmap
155
155
 
156
- | Milestone | Status |
157
- | -------------------------- | -------- |
158
- | Repo bootstrap | ✅ |
159
- | Core extraction | ✅ |
160
- | Bridges | ✅ |
161
- | Linklater migration | ✅ |
162
- | GitHub Action | ✅ |
163
- | `v0.1.0-alpha` npm publish | ✅ |
164
- | `v1.0.0` public launch | ⏳ |
165
- | `v1.1.0` AI fuzzy matching | Deferred |
156
+ | Milestone | Status |
157
+ | -------------------------- | ------ |
158
+ | Repo bootstrap | ✅ |
159
+ | Core extraction | ✅ |
160
+ | Bridges | ✅ |
161
+ | Linklater migration | ✅ |
162
+ | GitHub Action | ✅ |
163
+ | `v0.1.0-alpha` npm publish | ✅ |
164
+ | `v1.0.0` public launch | ⏳ |
166
165
 
167
166
  ## Acknowledgements
168
167
 
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Key-name handling for the `type` step. Playwright's key names stay the
3
+ * source of truth; this layer only rewrites a fixed set of glyphs and
4
+ * abbreviations story authors reach for by habit ("Cmd+K", "⌘+K", "Esc")
5
+ * into the names Playwright understands, and relabels its unknown-key
6
+ * failure. Everything else passes through untouched, so keys Playwright
7
+ * gains on upgrade need no change here.
8
+ */
9
+ /**
10
+ * Resolves each token of a key or combo through the alias table. Matching
11
+ * is exact-case, like Playwright's own key names: "Esc" resolves, "esc"
12
+ * does not and fails at press time.
13
+ */
14
+ export declare function normaliseKey(value: string): string;
15
+ /**
16
+ * The rewritten failure. Playwright quotes the token it rejected, so a
17
+ * trailing `+` in the value leaves that token empty; the key is named
18
+ * only when there is a name to give, and the value is always there to
19
+ * read. The value clause is dropped only when it would repeat a named
20
+ * key, which is the whole of a single-key step — an empty key equals an
21
+ * empty value without repeating anything, and the step is still what the
22
+ * author has to go and look at.
23
+ */
24
+ export declare class UnknownKeyError extends Error {
25
+ readonly cause?: unknown;
26
+ constructor(key: string, value: string, cause?: unknown);
27
+ }
28
+ /**
29
+ * Playwright's bare unknown-key failure says nothing about where the key
30
+ * came from or what tuffgal accepts. Only that failure is rewritten;
31
+ * timeouts, closed pages, and everything else pass through so their own
32
+ * diagnostics survive.
33
+ */
34
+ export declare function explainKeyPressFailure(value: string, cause: unknown): unknown;
35
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/runner/steps/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwEH;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAC;gBACtB,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAWxD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAQ7E"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Key-name handling for the `type` step. Playwright's key names stay the
3
+ * source of truth; this layer only rewrites a fixed set of glyphs and
4
+ * abbreviations story authors reach for by habit ("Cmd+K", "⌘+K", "Esc")
5
+ * into the names Playwright understands, and relabels its unknown-key
6
+ * failure. Everything else passes through untouched, so keys Playwright
7
+ * gains on upgrade need no change here.
8
+ */
9
+ /**
10
+ * Author-facing spellings mapped 1:1 onto Playwright key names. Every
11
+ * value must be a real Playwright key, or the token Playwright rejects
12
+ * could be one this layer wrote rather than one the author can find in
13
+ * their own step value. A Map, not an object literal, so a step value
14
+ * like "constructor" cannot read an inherited property and resolve to
15
+ * something that is not a key.
16
+ */
17
+ const KEY_ALIASES = new Map([
18
+ ['Ctrl', 'Control'],
19
+ ['Cmd', 'Meta'],
20
+ ['⌘', 'Meta'],
21
+ ['Opt', 'Alt'],
22
+ ['⌥', 'Alt'],
23
+ ['Esc', 'Escape'],
24
+ ['Return', 'Enter'],
25
+ ]);
26
+ const ALIAS_SUMMARY = [...KEY_ALIASES]
27
+ .map(([alias, key]) => `${alias}→${key}`)
28
+ .join(', ');
29
+ /**
30
+ * Playwright's own wording, from `_keyDescriptionForString` in
31
+ * playwright-core, which `press` reaches through `down`:
32
+ * `Unknown key: "${keyString}"`. Unanchored on purpose: the client
33
+ * prefixes the call, so what arrives is `keyboard.press: Unknown key:
34
+ * "esc"`. The quote and colon are part of the match so an unrelated
35
+ * future error that merely mentions an unknown key is not relabelled with
36
+ * an alias list, and the capture hands back the token Playwright
37
+ * rejected.
38
+ *
39
+ * The capture is greedy so a rejected token holding a quote of its own
40
+ * comes back whole. Playwright puts the failure on one line and ends it
41
+ * at the closing quote, and the client's prefix carries no quote, so the
42
+ * last quote on the line is the closing one: `Unknown key: "a""` yields
43
+ * `a"`. A lazy capture stops at the token's own quote and yields `a`,
44
+ * which is a real key and not the one that was rejected.
45
+ *
46
+ * This wording is the whole of the coupling to Playwright, and `npm test`
47
+ * cannot detect it changing: that suite mocks the keyboard, so every
48
+ * fixture restates the wording rather than observing it, and a reworded
49
+ * release leaves it green while this stops matching in production. What
50
+ * notices is `test/dom/type-keys.dom.test.ts`, which presses a real key
51
+ * and fails when the relabelling stops. Run `npm run test:dom` when
52
+ * upgrading Playwright.
53
+ */
54
+ const PLAYWRIGHT_UNKNOWN_KEY = /Unknown key: "(.*)"/;
55
+ /**
56
+ * Playwright's own `+` split, mirrored from `Keyboard.press` in
57
+ * playwright-core. A `+` separates tokens only when something precedes it,
58
+ * which is what keeps a bare "+" a pressable key instead of two empty
59
+ * tokens. Diverging here would invent a bug Playwright does not have.
60
+ */
61
+ function splitCombo(value) {
62
+ const tokens = [];
63
+ let building = '';
64
+ for (const char of value) {
65
+ if (char === '+' && building) {
66
+ tokens.push(building);
67
+ building = '';
68
+ }
69
+ else {
70
+ building += char;
71
+ }
72
+ }
73
+ tokens.push(building);
74
+ return tokens;
75
+ }
76
+ /**
77
+ * Resolves each token of a key or combo through the alias table. Matching
78
+ * is exact-case, like Playwright's own key names: "Esc" resolves, "esc"
79
+ * does not and fails at press time.
80
+ */
81
+ export function normaliseKey(value) {
82
+ return splitCombo(value)
83
+ .map((token) => KEY_ALIASES.get(token) ?? token)
84
+ .join('+');
85
+ }
86
+ /**
87
+ * The rewritten failure. Playwright quotes the token it rejected, so a
88
+ * trailing `+` in the value leaves that token empty; the key is named
89
+ * only when there is a name to give, and the value is always there to
90
+ * read. The value clause is dropped only when it would repeat a named
91
+ * key, which is the whole of a single-key step — an empty key equals an
92
+ * empty value without repeating anything, and the step is still what the
93
+ * author has to go and look at.
94
+ */
95
+ export class UnknownKeyError extends Error {
96
+ cause;
97
+ constructor(key, value, cause) {
98
+ const hasKey = key !== '';
99
+ const named = hasKey ? ` "${key}"` : '';
100
+ const where = hasKey && key === value ? '' : ` in the \`type\` step value "${value}"`;
101
+ super(`Unknown key${named}${where}. Key names are Playwright's and case-sensitive, so "Esc" resolves and "esc" does not.\nAliases: ${ALIAS_SUMMARY}`);
102
+ this.name = 'UnknownKeyError';
103
+ this.cause = cause;
104
+ }
105
+ }
106
+ /**
107
+ * Playwright's bare unknown-key failure says nothing about where the key
108
+ * came from or what tuffgal accepts. Only that failure is rewritten;
109
+ * timeouts, closed pages, and everything else pass through so their own
110
+ * diagnostics survive.
111
+ */
112
+ export function explainKeyPressFailure(value, cause) {
113
+ const rejected = cause instanceof Error
114
+ ? PLAYWRIGHT_UNKNOWN_KEY.exec(cause.message)?.[1]
115
+ : undefined;
116
+ return rejected === undefined
117
+ ? cause
118
+ : new UnknownKeyError(rejected, value, cause);
119
+ }
120
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/runner/steps/keys.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAiB;IAC1C,CAAC,MAAM,EAAE,SAAS,CAAC;IACnB,CAAC,KAAK,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,CAAC;IACb,CAAC,KAAK,EAAE,KAAK,CAAC;IACd,CAAC,GAAG,EAAE,KAAK,CAAC;IACZ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACjB,CAAC,QAAQ,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,GAAG,WAAW,CAAC;KACnC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC;KACxC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEd;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAErD;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAAa;IAC/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtB,QAAQ,GAAG,EAAE,CAAC;QAChB,CAAC;aAAM,CAAC;YACN,QAAQ,IAAI,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,UAAU,CAAC,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;SAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACtB,KAAK,CAAW;IAClC,YAAY,GAAW,EAAE,KAAa,EAAE,KAAe;QACrD,MAAM,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,MAAM,KAAK,GACT,MAAM,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gCAAgC,KAAK,GAAG,CAAC;QAC1E,KAAK,CACH,cAAc,KAAK,GAAG,KAAK,oGAAoG,aAAa,EAAE,CAC/I,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,KAAc;IAClE,MAAM,QAAQ,GACZ,KAAK,YAAY,KAAK;QACpB,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,SAAS,CAAC;IAChB,OAAO,QAAQ,KAAK,SAAS;QAC3B,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,IAAI,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC"}
@@ -1,9 +1,9 @@
1
1
  import type { Page } from 'playwright';
2
2
  /**
3
- * Page-level keyboard input. Delegates straight to Playwright's
4
- * `keyboard.press`, which understands single keys, named keys, and
5
- * combinations alike. Examples: "A", "Escape", "Shift+A",
6
- * "Control+Enter".
3
+ * Page-level keyboard input. Resolves the glyph and abbreviation aliases
4
+ * in keys.ts, then delegates to Playwright's `keyboard.press`, which
5
+ * understands single keys, named keys, and combinations alike. Examples:
6
+ * "A", "Esc", "Cmd+K", "Shift+A", "Control+Enter".
7
7
  */
8
8
  export declare function runType(page: Page, value: string): Promise<void>;
9
9
  //# sourceMappingURL=type.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/runner/steps/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE"}
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../src/runner/steps/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGvC;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMtE"}
@@ -1,10 +1,16 @@
1
+ import { explainKeyPressFailure, normaliseKey } from "./keys.js";
1
2
  /**
2
- * Page-level keyboard input. Delegates straight to Playwright's
3
- * `keyboard.press`, which understands single keys, named keys, and
4
- * combinations alike. Examples: "A", "Escape", "Shift+A",
5
- * "Control+Enter".
3
+ * Page-level keyboard input. Resolves the glyph and abbreviation aliases
4
+ * in keys.ts, then delegates to Playwright's `keyboard.press`, which
5
+ * understands single keys, named keys, and combinations alike. Examples:
6
+ * "A", "Esc", "Cmd+K", "Shift+A", "Control+Enter".
6
7
  */
7
8
  export async function runType(page, value) {
8
- await page.keyboard.press(value);
9
+ try {
10
+ await page.keyboard.press(normaliseKey(value));
11
+ }
12
+ catch (error) {
13
+ throw explainKeyPressFailure(value, error);
14
+ }
9
15
  }
10
16
  //# sourceMappingURL=type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/runner/steps/type.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,KAAa;IACrD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../src/runner/steps/type.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,KAAa;IACrD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/schema/action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkCrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAqB9C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAuGrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwDvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/schema/action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkCrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAqB9C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA0GrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwDvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
@@ -137,11 +137,14 @@ export const stepSchema = z.discriminatedUnion('kind', [
137
137
  hint: hintSchema,
138
138
  }),
139
139
  /**
140
- * Keyboard input directed at the page (not an input field). Passed
141
- * straight to Playwright's `keyboard.press`, so single keys ("A"),
140
+ * Keyboard input directed at the page (not an input field). Resolves a
141
+ * fixed set of glyphs and abbreviations ("Cmd", "⌘", "Esc") first, then
142
+ * hands the key to Playwright's `keyboard.press`, so single keys ("A"),
142
143
  * named keys ("Escape", "Tab"), and combinations ("Shift+A",
143
- * "Control+Enter") all work. Use for hotkeys, modal dismissal, focus
144
- * cycling.
144
+ * "Control+Enter") all work. Names are validated at press time, not by
145
+ * this schema, so Playwright stays the one list of keys. Use for
146
+ * hotkeys, modal dismissal, focus cycling. See runner/steps/keys.ts for
147
+ * the alias table.
145
148
  */
146
149
  z.object({
147
150
  kind: z.literal('type'),
@@ -1 +1 @@
1
- {"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/schema/action.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action.js","sourceRoot":"","sources":["../../src/schema/action.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tuffgal",
3
- "version": "0.2.0-alpha.5",
3
+ "version": "0.2.0-alpha.6",
4
4
  "description": "JSON-driven visual regression for web apps.",
5
5
  "license": "MIT",
6
6
  "author": "Nick Schneble",