claude-in-mobile 3.5.0 → 3.7.0

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.
Files changed (206) hide show
  1. package/README.md +6 -5
  2. package/dist/a11y/formatter.d.ts +36 -0
  3. package/dist/a11y/formatter.d.ts.map +1 -0
  4. package/dist/a11y/formatter.js +134 -0
  5. package/dist/a11y/formatter.js.map +1 -0
  6. package/dist/a11y/rules/duplicate-descriptions.d.ts +3 -0
  7. package/dist/a11y/rules/duplicate-descriptions.d.ts.map +1 -0
  8. package/dist/a11y/rules/duplicate-descriptions.js +50 -0
  9. package/dist/a11y/rules/duplicate-descriptions.js.map +1 -0
  10. package/dist/a11y/rules/focus-order.d.ts +3 -0
  11. package/dist/a11y/rules/focus-order.d.ts.map +1 -0
  12. package/dist/a11y/rules/focus-order.js +35 -0
  13. package/dist/a11y/rules/focus-order.js.map +1 -0
  14. package/dist/a11y/rules/index.d.ts +4 -0
  15. package/dist/a11y/rules/index.d.ts.map +1 -0
  16. package/dist/a11y/rules/index.js +19 -0
  17. package/dist/a11y/rules/index.js.map +1 -0
  18. package/dist/a11y/rules/interactive-labels.d.ts +3 -0
  19. package/dist/a11y/rules/interactive-labels.d.ts.map +1 -0
  20. package/dist/a11y/rules/interactive-labels.js +39 -0
  21. package/dist/a11y/rules/interactive-labels.js.map +1 -0
  22. package/dist/a11y/rules/missing-label.d.ts +3 -0
  23. package/dist/a11y/rules/missing-label.d.ts.map +1 -0
  24. package/dist/a11y/rules/missing-label.js +64 -0
  25. package/dist/a11y/rules/missing-label.js.map +1 -0
  26. package/dist/a11y/rules/state-description.d.ts +3 -0
  27. package/dist/a11y/rules/state-description.d.ts.map +1 -0
  28. package/dist/a11y/rules/state-description.js +37 -0
  29. package/dist/a11y/rules/state-description.js.map +1 -0
  30. package/dist/a11y/rules/touch-target.d.ts +3 -0
  31. package/dist/a11y/rules/touch-target.d.ts.map +1 -0
  32. package/dist/a11y/rules/touch-target.js +37 -0
  33. package/dist/a11y/rules/touch-target.js.map +1 -0
  34. package/dist/a11y/score.d.ts +7 -0
  35. package/dist/a11y/score.d.ts.map +1 -0
  36. package/dist/a11y/score.js +10 -0
  37. package/dist/a11y/score.js.map +1 -0
  38. package/dist/a11y/severity.d.ts +4 -0
  39. package/dist/a11y/severity.d.ts.map +1 -0
  40. package/dist/a11y/severity.js +13 -0
  41. package/dist/a11y/severity.js.map +1 -0
  42. package/dist/a11y/types.d.ts +48 -0
  43. package/dist/a11y/types.d.ts.map +1 -0
  44. package/dist/a11y/types.js +2 -0
  45. package/dist/a11y/types.js.map +1 -0
  46. package/dist/adapters/desktop-adapter.d.ts +6 -1
  47. package/dist/adapters/desktop-adapter.d.ts.map +1 -1
  48. package/dist/adapters/desktop-adapter.js +24 -2
  49. package/dist/adapters/desktop-adapter.js.map +1 -1
  50. package/dist/adapters/ios-adapter.js +1 -1
  51. package/dist/adapters/ios-adapter.js.map +1 -1
  52. package/dist/adapters/platform-adapter.d.ts +1 -1
  53. package/dist/adapters/platform-adapter.d.ts.map +1 -1
  54. package/dist/adb/ui-parser.d.ts +2 -0
  55. package/dist/adb/ui-parser.d.ts.map +1 -1
  56. package/dist/adb/ui-parser.js +36 -0
  57. package/dist/adb/ui-parser.js.map +1 -1
  58. package/dist/autopilot/explorer.d.ts +15 -0
  59. package/dist/autopilot/explorer.d.ts.map +1 -0
  60. package/dist/autopilot/explorer.js +276 -0
  61. package/dist/autopilot/explorer.js.map +1 -0
  62. package/dist/autopilot/generator.d.ts +12 -0
  63. package/dist/autopilot/generator.d.ts.map +1 -0
  64. package/dist/autopilot/generator.js +101 -0
  65. package/dist/autopilot/generator.js.map +1 -0
  66. package/dist/autopilot/healer.d.ts +17 -0
  67. package/dist/autopilot/healer.d.ts.map +1 -0
  68. package/dist/autopilot/healer.js +142 -0
  69. package/dist/autopilot/healer.js.map +1 -0
  70. package/dist/autopilot/nav-graph.d.ts +26 -0
  71. package/dist/autopilot/nav-graph.d.ts.map +1 -0
  72. package/dist/autopilot/nav-graph.js +101 -0
  73. package/dist/autopilot/nav-graph.js.map +1 -0
  74. package/dist/autopilot/screen-fingerprint.d.ts +20 -0
  75. package/dist/autopilot/screen-fingerprint.d.ts.map +1 -0
  76. package/dist/autopilot/screen-fingerprint.js +36 -0
  77. package/dist/autopilot/screen-fingerprint.js.map +1 -0
  78. package/dist/autopilot/types.d.ts +109 -0
  79. package/dist/autopilot/types.d.ts.map +1 -0
  80. package/dist/autopilot/types.js +14 -0
  81. package/dist/autopilot/types.js.map +1 -0
  82. package/dist/browser/client.d.ts.map +1 -1
  83. package/dist/browser/client.js +25 -7
  84. package/dist/browser/client.js.map +1 -1
  85. package/dist/browser/types.d.ts +2 -0
  86. package/dist/browser/types.d.ts.map +1 -1
  87. package/dist/browser/types.js.map +1 -1
  88. package/dist/desktop/client.d.ts +26 -3
  89. package/dist/desktop/client.d.ts.map +1 -1
  90. package/dist/desktop/client.js +177 -11
  91. package/dist/desktop/client.js.map +1 -1
  92. package/dist/desktop/types.d.ts +3 -0
  93. package/dist/desktop/types.d.ts.map +1 -1
  94. package/dist/device-manager.d.ts +9 -7
  95. package/dist/device-manager.d.ts.map +1 -1
  96. package/dist/device-manager.js +98 -40
  97. package/dist/device-manager.js.map +1 -1
  98. package/dist/errors.d.ts +37 -0
  99. package/dist/errors.d.ts.map +1 -1
  100. package/dist/errors.js +74 -1
  101. package/dist/errors.js.map +1 -1
  102. package/dist/index.js +134 -36
  103. package/dist/index.js.map +1 -1
  104. package/dist/ios/client.d.ts +6 -2
  105. package/dist/ios/client.d.ts.map +1 -1
  106. package/dist/ios/client.js +25 -5
  107. package/dist/ios/client.js.map +1 -1
  108. package/dist/ios/wda/wda-client.d.ts +1 -0
  109. package/dist/ios/wda/wda-client.d.ts.map +1 -1
  110. package/dist/ios/wda/wda-client.js +9 -0
  111. package/dist/ios/wda/wda-client.js.map +1 -1
  112. package/dist/perf/collector.d.ts +45 -0
  113. package/dist/perf/collector.d.ts.map +1 -0
  114. package/dist/perf/collector.js +295 -0
  115. package/dist/perf/collector.js.map +1 -0
  116. package/dist/perf/formatter.d.ts +22 -0
  117. package/dist/perf/formatter.d.ts.map +1 -0
  118. package/dist/perf/formatter.js +116 -0
  119. package/dist/perf/formatter.js.map +1 -0
  120. package/dist/perf/types.d.ts +72 -0
  121. package/dist/perf/types.d.ts.map +1 -0
  122. package/dist/perf/types.js +5 -0
  123. package/dist/perf/types.js.map +1 -0
  124. package/dist/profiles.d.ts +33 -0
  125. package/dist/profiles.d.ts.map +1 -0
  126. package/dist/profiles.js +69 -0
  127. package/dist/profiles.js.map +1 -0
  128. package/dist/tools/accessibility-tools.d.ts +3 -0
  129. package/dist/tools/accessibility-tools.d.ts.map +1 -0
  130. package/dist/tools/accessibility-tools.js +256 -0
  131. package/dist/tools/accessibility-tools.js.map +1 -0
  132. package/dist/tools/app-tools.js +1 -1
  133. package/dist/tools/app-tools.js.map +1 -1
  134. package/dist/tools/autopilot-tools.d.ts +13 -0
  135. package/dist/tools/autopilot-tools.d.ts.map +1 -0
  136. package/dist/tools/autopilot-tools.js +353 -0
  137. package/dist/tools/autopilot-tools.js.map +1 -0
  138. package/dist/tools/context/shared-state.d.ts +1 -0
  139. package/dist/tools/context/shared-state.d.ts.map +1 -1
  140. package/dist/tools/context/shared-state.js +11 -0
  141. package/dist/tools/context/shared-state.js.map +1 -1
  142. package/dist/tools/context.d.ts +2 -1
  143. package/dist/tools/context.d.ts.map +1 -1
  144. package/dist/tools/context.js +5 -4
  145. package/dist/tools/context.js.map +1 -1
  146. package/dist/tools/desktop-tools.d.ts.map +1 -1
  147. package/dist/tools/desktop-tools.js +27 -3
  148. package/dist/tools/desktop-tools.js.map +1 -1
  149. package/dist/tools/flow-tools.d.ts.map +1 -1
  150. package/dist/tools/flow-tools.js +31 -5
  151. package/dist/tools/flow-tools.js.map +1 -1
  152. package/dist/tools/helpers/resolve-element.d.ts.map +1 -1
  153. package/dist/tools/helpers/resolve-element.js +12 -1
  154. package/dist/tools/helpers/resolve-element.js.map +1 -1
  155. package/dist/tools/interaction-tools.d.ts.map +1 -1
  156. package/dist/tools/interaction-tools.js +5 -0
  157. package/dist/tools/interaction-tools.js.map +1 -1
  158. package/dist/tools/meta/accessibility-meta.d.ts +6 -0
  159. package/dist/tools/meta/accessibility-meta.d.ts.map +1 -0
  160. package/dist/tools/meta/accessibility-meta.js +48 -0
  161. package/dist/tools/meta/accessibility-meta.js.map +1 -0
  162. package/dist/tools/meta/autopilot-meta.d.ts +6 -0
  163. package/dist/tools/meta/autopilot-meta.d.ts.map +1 -0
  164. package/dist/tools/meta/autopilot-meta.js +60 -0
  165. package/dist/tools/meta/autopilot-meta.js.map +1 -0
  166. package/dist/tools/meta/desktop-meta.d.ts.map +1 -1
  167. package/dist/tools/meta/desktop-meta.js +3 -0
  168. package/dist/tools/meta/desktop-meta.js.map +1 -1
  169. package/dist/tools/meta/device-meta.d.ts.map +1 -1
  170. package/dist/tools/meta/device-meta.js +94 -15
  171. package/dist/tools/meta/device-meta.js.map +1 -1
  172. package/dist/tools/meta/performance-meta.d.ts +6 -0
  173. package/dist/tools/meta/performance-meta.d.ts.map +1 -0
  174. package/dist/tools/meta/performance-meta.js +54 -0
  175. package/dist/tools/meta/performance-meta.js.map +1 -0
  176. package/dist/tools/meta/ui-meta.d.ts.map +1 -1
  177. package/dist/tools/meta/ui-meta.js +6 -0
  178. package/dist/tools/meta/ui-meta.js.map +1 -1
  179. package/dist/tools/performance-tools.d.ts +13 -0
  180. package/dist/tools/performance-tools.d.ts.map +1 -0
  181. package/dist/tools/performance-tools.js +397 -0
  182. package/dist/tools/performance-tools.js.map +1 -0
  183. package/dist/tools/registry.d.ts +18 -5
  184. package/dist/tools/registry.d.ts.map +1 -1
  185. package/dist/tools/registry.js +78 -7
  186. package/dist/tools/registry.js.map +1 -1
  187. package/dist/tools/ui-tools.d.ts.map +1 -1
  188. package/dist/tools/ui-tools.js +10 -7
  189. package/dist/tools/ui-tools.js.map +1 -1
  190. package/dist/utils/anti-patterns.d.ts +11 -0
  191. package/dist/utils/anti-patterns.d.ts.map +1 -0
  192. package/dist/utils/anti-patterns.js +82 -0
  193. package/dist/utils/anti-patterns.js.map +1 -0
  194. package/dist/utils/exploration-store.d.ts +24 -0
  195. package/dist/utils/exploration-store.d.ts.map +1 -0
  196. package/dist/utils/exploration-store.js +133 -0
  197. package/dist/utils/exploration-store.js.map +1 -0
  198. package/dist/utils/perf-baseline-store.d.ts +29 -0
  199. package/dist/utils/perf-baseline-store.d.ts.map +1 -0
  200. package/dist/utils/perf-baseline-store.js +149 -0
  201. package/dist/utils/perf-baseline-store.js.map +1 -0
  202. package/dist/utils/sanitize.d.ts +1 -0
  203. package/dist/utils/sanitize.d.ts.map +1 -1
  204. package/dist/utils/sanitize.js +7 -0
  205. package/dist/utils/sanitize.js.map +1 -1
  206. package/package.json +52 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Claude Mobile
2
2
 
3
- MCP server for mobile and desktop automation — Android (via ADB), iOS Simulator (via simctl), Desktop (Compose Multiplatform), and Aurora OS (via audb). Like [Claude in Chrome](https://www.anthropic.com/news/claude-for-chrome) but for mobile devices and desktop apps.
3
+ MCP server for mobile and desktop automation — Android (via ADB), iOS Simulator (via simctl), Desktop (any macOS app), and Aurora OS (via audb). Like [Claude in Chrome](https://www.anthropic.com/news/claude-for-chrome) but for mobile devices and desktop apps.
4
4
 
5
5
  Control your Android phone, emulator, iOS Simulator, Desktop applications, or Aurora OS device with natural language through Claude.
6
6
 
@@ -19,7 +19,7 @@ Control your Android phone, emulator, iOS Simulator, Desktop applications, or Au
19
19
  - **Flow engine** — `flow_batch` for sequential commands, `flow_run` for conditional loops, `flow_parallel` for fan-out
20
20
  - **Permission management** — Grant, revoke, and reset app permissions (Android runtime, iOS privacy services)
21
21
  - **Store management** — Upload builds to Google Play, Huawei AppGallery, and RuStore (optional module)
22
- - **Desktop support** — Test Compose Multiplatform desktop apps with window management, clipboard, and performance metrics
22
+ - **Desktop support** — Test any macOS app (SwiftUI, AppKit, Electron, Compose) with window management, clipboard, and performance metrics
23
23
 
24
24
  ## Installation
25
25
 
@@ -200,7 +200,8 @@ claude mcp add --transport stdio mobile -- cmd /c npx claude-in-mobile@latest
200
200
  ### Desktop
201
201
  - macOS (Windows/Linux support planned)
202
202
  - JDK 17+ for building the Desktop companion
203
- - Compose Multiplatform desktop application to test
203
+ - Any macOS application (SwiftUI, AppKit, Electron, Compose) launch by `bundleId`, `.app` path, or attach by PID
204
+ - Accessibility permissions required: System Settings → Privacy & Security → Accessibility
204
205
 
205
206
  ### Aurora OS
206
207
  - audb CLI installed and in PATH (`cargo install audb-client`)
@@ -209,7 +210,7 @@ claude mcp add --transport stdio mobile -- cmd /c npx claude-in-mobile@latest
209
210
 
210
211
  ## Available Tools
211
212
 
212
- v3.4.0 consolidates tools into **8 core meta-tools** + **3 optional modules**. Each meta-tool uses an `action` parameter to select the operation. All v3.0/v3.1 tool names still work as backward-compatible aliases.
213
+ v3.7.0 provides **8 core meta-tools** + **3 optional modules**. Each meta-tool uses an `action` parameter to select the operation. All v3.0/v3.1 tool names still work as backward-compatible aliases.
213
214
 
214
215
  ### Core Meta-Tools (always loaded)
215
216
 
@@ -231,7 +232,7 @@ These modules are hidden by default to save tokens. They auto-enable when you ca
231
232
  | Module | Actions | Description |
232
233
  |--------|---------|-------------|
233
234
  | `browser` | `open`, `close`, `list_sessions`, `navigate`, `click`, `fill`, `fill_form`, `press_key`, `snapshot`, `screenshot`, `evaluate`, `wait_for_selector`, `clear_session` | Chrome/Chromium automation via CDP |
234
- | `desktop` | `launch`, `stop`, `windows`, `focus`, `resize`, `clipboard_get`, `clipboard_set`, `performance`, `monitors` | Compose Desktop app testing |
235
+ | `desktop` | `launch`, `stop`, `windows`, `focus`, `resize`, `clipboard_get`, `clipboard_set`, `performance`, `monitors` | Desktop app testing (any macOS app: SwiftUI, AppKit, Electron, Compose) |
235
236
  | `store` | `upload`, `set_notes`, `submit`, `get_releases`, `discard`, `promote`, `halt_rollout`, `get_versions` | Google Play, Huawei AppGallery, RuStore publishing |
236
237
 
237
238
  ### Flow Tools
@@ -0,0 +1,36 @@
1
+ import type { A11yReport, A11ySeverity } from "./types.js";
2
+ /**
3
+ * Format an accessibility report as text for LLM consumption.
4
+ * Follows the PASS/FAIL pattern from visual-tools.
5
+ */
6
+ export declare function formatAuditReport(report: A11yReport, options?: {
7
+ compact?: boolean;
8
+ passwordIndices?: Set<number>;
9
+ }): string;
10
+ /**
11
+ * Format a summary-only report (score + counts, no individual issues).
12
+ */
13
+ export declare function formatAuditSummary(report: A11yReport): string;
14
+ /**
15
+ * Format a list of all rules.
16
+ */
17
+ export declare function formatRuleList(rules: Array<{
18
+ id: string;
19
+ name: string;
20
+ wcag: string;
21
+ severity: A11ySeverity;
22
+ description: string;
23
+ platforms: string[];
24
+ }>): string;
25
+ /**
26
+ * Format details of a single rule.
27
+ */
28
+ export declare function formatRuleDetail(rule: {
29
+ id: string;
30
+ name: string;
31
+ wcag: string;
32
+ severity: A11ySeverity;
33
+ description: string;
34
+ platforms: string[];
35
+ }): string;
36
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../src/a11y/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAa,YAAY,EAAE,MAAM,YAAY,CAAC;AAoDtE;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GAC7D,MAAM,CAgER;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAW7D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,GACzH,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,GAAG,MAAM,CAST"}
@@ -0,0 +1,134 @@
1
+ import { SEVERITY_ORDER } from "./severity.js";
2
+ function shortClassName(className) {
3
+ return className.split(".").pop() ?? className;
4
+ }
5
+ function shortResourceId(resourceId) {
6
+ if (!resourceId)
7
+ return "";
8
+ return resourceId.split(":id/").pop() ?? resourceId;
9
+ }
10
+ function formatElementRef(issue, redactPasswords) {
11
+ const el = issue.element;
12
+ const shortClass = shortClassName(el.className);
13
+ const parts = [`<${shortClass}>`];
14
+ const shortId = shortResourceId(el.resourceId);
15
+ if (shortId) {
16
+ parts.push(`id="${shortId}"`);
17
+ }
18
+ parts.push(`@ (${el.centerX}, ${el.centerY})`);
19
+ if (redactPasswords.has(el.index)) {
20
+ parts.push("text=[REDACTED]");
21
+ }
22
+ return parts.join(" ");
23
+ }
24
+ function formatIssueFull(issue, num, redactPasswords) {
25
+ const ref = formatElementRef(issue, redactPasswords);
26
+ return [
27
+ ` [${num}] ${issue.ruleId} (WCAG ${issue.wcag}) — ${ref}`,
28
+ ` ${issue.message}`,
29
+ ].join("\n");
30
+ }
31
+ function formatIssueCompact(issue, num, redactPasswords) {
32
+ const ref = formatElementRef(issue, redactPasswords);
33
+ return ` [${num}] ${issue.ruleId} (${issue.wcag}) — ${ref}`;
34
+ }
35
+ /**
36
+ * Format an accessibility report as text for LLM consumption.
37
+ * Follows the PASS/FAIL pattern from visual-tools.
38
+ */
39
+ export function formatAuditReport(report, options) {
40
+ const compact = options?.compact === true;
41
+ const passwordIndices = options?.passwordIndices ?? new Set();
42
+ const hasCritical = report.issueCount.critical > 0;
43
+ const isPassing = report.score >= 100 && !hasCritical;
44
+ const status = isPassing ? "PASS" : "FAIL";
45
+ const header = `A11Y AUDIT: ${status} (score: ${report.score}/100)`;
46
+ const meta = `Platform: ${report.platform} | Elements: ${report.totalElements} | Standard: ${report.standard}`;
47
+ const counts = `Issues: ${report.issueCount.critical} critical, ${report.issueCount.serious} serious, ` +
48
+ `${report.issueCount.moderate} moderate, ${report.issueCount.minor} minor (${report.issueCount.total} total)`;
49
+ const lines = [header, meta, counts];
50
+ if (report.issues.length > 0) {
51
+ // Sort issues by severity
52
+ const sorted = [...report.issues].sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
53
+ // Group by severity
54
+ const groups = new Map();
55
+ for (const issue of sorted) {
56
+ const existing = groups.get(issue.severity);
57
+ if (existing) {
58
+ existing.push(issue);
59
+ }
60
+ else {
61
+ groups.set(issue.severity, [issue]);
62
+ }
63
+ }
64
+ let issueNum = 1;
65
+ const severityLabels = [
66
+ ["critical", "CRITICAL"],
67
+ ["serious", "SERIOUS"],
68
+ ["moderate", "MODERATE"],
69
+ ["minor", "MINOR"],
70
+ ];
71
+ for (const [sev, label] of severityLabels) {
72
+ const groupIssues = groups.get(sev);
73
+ if (!groupIssues || groupIssues.length === 0)
74
+ continue;
75
+ lines.push("");
76
+ lines.push(`${label}:`);
77
+ for (const issue of groupIssues) {
78
+ if (compact) {
79
+ lines.push(formatIssueCompact(issue, issueNum, passwordIndices));
80
+ }
81
+ else {
82
+ lines.push(formatIssueFull(issue, issueNum, passwordIndices));
83
+ }
84
+ issueNum++;
85
+ }
86
+ }
87
+ }
88
+ if (report.passedRules.length > 0) {
89
+ lines.push("");
90
+ lines.push(`Passed rules: ${report.passedRules.join(", ")}`);
91
+ }
92
+ return lines.join("\n");
93
+ }
94
+ /**
95
+ * Format a summary-only report (score + counts, no individual issues).
96
+ */
97
+ export function formatAuditSummary(report) {
98
+ const hasCritical = report.issueCount.critical > 0;
99
+ const isPassing = report.score >= 100 && !hasCritical;
100
+ const status = isPassing ? "PASS" : "FAIL";
101
+ return [
102
+ `A11Y: ${status} (score: ${report.score}/100)`,
103
+ `Platform: ${report.platform} | Elements: ${report.totalElements} | Standard: ${report.standard}`,
104
+ `Issues: ${report.issueCount.critical} critical, ${report.issueCount.serious} serious, ${report.issueCount.moderate} moderate, ${report.issueCount.minor} minor (${report.issueCount.total} total)`,
105
+ `Passed: ${report.passedRules.length} rules`,
106
+ ].join("\n");
107
+ }
108
+ /**
109
+ * Format a list of all rules.
110
+ */
111
+ export function formatRuleList(rules) {
112
+ const lines = [`Accessibility rules: ${rules.length} total`, ""];
113
+ for (const rule of rules) {
114
+ lines.push(` ${rule.id} (WCAG ${rule.wcag}, ${rule.severity})`);
115
+ lines.push(` ${rule.description}`);
116
+ lines.push(` Platforms: ${rule.platforms.join(", ")}`);
117
+ lines.push("");
118
+ }
119
+ return lines.join("\n").trimEnd();
120
+ }
121
+ /**
122
+ * Format details of a single rule.
123
+ */
124
+ export function formatRuleDetail(rule) {
125
+ return [
126
+ `Rule: ${rule.id}`,
127
+ `Name: ${rule.name}`,
128
+ `WCAG: ${rule.wcag}`,
129
+ `Severity: ${rule.severity}`,
130
+ `Description: ${rule.description}`,
131
+ `Platforms: ${rule.platforms.join(", ")}`,
132
+ ].join("\n");
133
+ }
134
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/a11y/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const duplicateDescriptionsRule: A11yRule;
3
+ //# sourceMappingURL=duplicate-descriptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-descriptions.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/duplicate-descriptions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,yBAAyB,EAAE,QAmDvC,CAAC"}
@@ -0,0 +1,50 @@
1
+ export const duplicateDescriptionsRule = {
2
+ id: "duplicate-descriptions",
3
+ name: "Duplicate Descriptions",
4
+ wcag: "1.3.1",
5
+ severity: "moderate",
6
+ description: "Multiple elements with identical content descriptions confuse screen reader users.",
7
+ platforms: ["android", "ios", "desktop"],
8
+ run(elements) {
9
+ // Group by non-empty contentDesc, exclude short (<=1 char) descriptions
10
+ const groups = new Map();
11
+ for (const el of elements) {
12
+ const desc = el.contentDesc.trim();
13
+ if (desc.length <= 1)
14
+ continue;
15
+ if (el.width <= 0 || el.height <= 0)
16
+ continue;
17
+ const existing = groups.get(desc);
18
+ if (existing) {
19
+ existing.push(el);
20
+ }
21
+ else {
22
+ groups.set(desc, [el]);
23
+ }
24
+ }
25
+ const issues = [];
26
+ for (const [desc, els] of groups) {
27
+ if (els.length <= 1)
28
+ continue;
29
+ // Report each duplicate element
30
+ for (const el of els) {
31
+ issues.push({
32
+ ruleId: "duplicate-descriptions",
33
+ wcag: "1.3.1",
34
+ severity: "moderate",
35
+ message: `Duplicate content description "${desc}" shared by ${els.length} elements`,
36
+ element: {
37
+ index: el.index,
38
+ className: el.className,
39
+ resourceId: el.resourceId,
40
+ bounds: el.bounds,
41
+ centerX: el.centerX,
42
+ centerY: el.centerY,
43
+ },
44
+ });
45
+ }
46
+ }
47
+ return issues;
48
+ },
49
+ };
50
+ //# sourceMappingURL=duplicate-descriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-descriptions.js","sourceRoot":"","sources":["../../../src/a11y/rules/duplicate-descriptions.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,yBAAyB,GAAa;IACjD,EAAE,EAAE,wBAAwB;IAC5B,IAAI,EAAE,wBAAwB;IAC9B,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,oFAAoF;IACtF,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,wEAAwE;QACxE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;QAE9C,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAC/B,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClC,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;YACjC,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9B,gCAAgC;YAChC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,wBAAwB;oBAChC,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,UAAU;oBACpB,OAAO,EAAE,kCAAkC,IAAI,eAAe,GAAG,CAAC,MAAM,WAAW;oBACnF,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const focusOrderRule: A11yRule;
3
+ //# sourceMappingURL=focus-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-order.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/focus-order.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,cAAc,EAAE,QAmC5B,CAAC"}
@@ -0,0 +1,35 @@
1
+ export const focusOrderRule = {
2
+ id: "focus-order",
3
+ name: "Focus Order",
4
+ wcag: "2.1.1",
5
+ severity: "serious",
6
+ description: "Clickable elements should be focusable so they can be reached via keyboard or switch navigation.",
7
+ platforms: ["android", "ios", "desktop"],
8
+ run(elements) {
9
+ const issues = [];
10
+ for (const el of elements) {
11
+ if (!el.clickable)
12
+ continue;
13
+ if (el.width <= 0 || el.height <= 0)
14
+ continue;
15
+ if (!el.focusable) {
16
+ issues.push({
17
+ ruleId: "focus-order",
18
+ wcag: "2.1.1",
19
+ severity: "serious",
20
+ message: "Clickable element is not focusable for keyboard/switch navigation",
21
+ element: {
22
+ index: el.index,
23
+ className: el.className,
24
+ resourceId: el.resourceId,
25
+ bounds: el.bounds,
26
+ centerX: el.centerX,
27
+ centerY: el.centerY,
28
+ },
29
+ });
30
+ }
31
+ }
32
+ return issues;
33
+ },
34
+ };
35
+ //# sourceMappingURL=focus-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"focus-order.js","sourceRoot":"","sources":["../../../src/a11y/rules/focus-order.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,cAAc,GAAa;IACtC,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,SAAS;IACnB,WAAW,EACT,kGAAkG;IACpG,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YAC5B,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,aAAa;oBACrB,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,mEAAmE;oBAC5E,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const ALL_RULES: A11yRule[];
3
+ export declare function getRuleById(id: string): A11yRule | undefined;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C,eAAO,MAAM,SAAS,EAAE,QAAQ,EAO/B,CAAC;AAMF,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE5D"}
@@ -0,0 +1,19 @@
1
+ import { missingLabelRule } from "./missing-label.js";
2
+ import { touchTargetRule } from "./touch-target.js";
3
+ import { interactiveLabelsRule } from "./interactive-labels.js";
4
+ import { focusOrderRule } from "./focus-order.js";
5
+ import { duplicateDescriptionsRule } from "./duplicate-descriptions.js";
6
+ import { stateDescriptionRule } from "./state-description.js";
7
+ export const ALL_RULES = [
8
+ missingLabelRule,
9
+ touchTargetRule,
10
+ interactiveLabelsRule,
11
+ focusOrderRule,
12
+ duplicateDescriptionsRule,
13
+ stateDescriptionRule,
14
+ ];
15
+ const ruleMap = new Map(ALL_RULES.map((r) => [r.id, r]));
16
+ export function getRuleById(id) {
17
+ return ruleMap.get(id);
18
+ }
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/a11y/rules/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAAe;IACnC,gBAAgB;IAChB,eAAe;IACf,qBAAqB;IACrB,cAAc;IACd,yBAAyB;IACzB,oBAAoB;CACrB,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAChC,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const interactiveLabelsRule: A11yRule;
3
+ //# sourceMappingURL=interactive-labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-labels.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/interactive-labels.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,qBAAqB,EAAE,QAyCnC,CAAC"}
@@ -0,0 +1,39 @@
1
+ export const interactiveLabelsRule = {
2
+ id: "interactive-labels",
3
+ name: "Interactive Image Labels",
4
+ wcag: "4.1.2",
5
+ severity: "critical",
6
+ description: "Clickable ImageView and ImageButton elements must have a content description.",
7
+ platforms: ["android", "ios", "desktop"],
8
+ run(elements) {
9
+ const issues = [];
10
+ for (const el of elements) {
11
+ if (!el.clickable)
12
+ continue;
13
+ if (el.width <= 0 || el.height <= 0)
14
+ continue;
15
+ const isImage = el.className.includes("ImageView") ||
16
+ el.className.includes("ImageButton");
17
+ if (!isImage)
18
+ continue;
19
+ if (el.contentDesc.trim().length === 0) {
20
+ issues.push({
21
+ ruleId: "interactive-labels",
22
+ wcag: "4.1.2",
23
+ severity: "critical",
24
+ message: "Clickable image element has no content description for screen readers",
25
+ element: {
26
+ index: el.index,
27
+ className: el.className,
28
+ resourceId: el.resourceId,
29
+ bounds: el.bounds,
30
+ centerX: el.centerX,
31
+ centerY: el.centerY,
32
+ },
33
+ });
34
+ }
35
+ }
36
+ return issues;
37
+ },
38
+ };
39
+ //# sourceMappingURL=interactive-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive-labels.js","sourceRoot":"","sources":["../../../src/a11y/rules/interactive-labels.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,qBAAqB,GAAa;IAC7C,EAAE,EAAE,oBAAoB;IACxB,IAAI,EAAE,0BAA0B;IAChC,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,+EAA+E;IACjF,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YAC5B,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,MAAM,OAAO,GACX,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAClC,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvC,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,oBAAoB;oBAC5B,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,UAAU;oBACpB,OAAO,EACL,uEAAuE;oBACzE,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const missingLabelRule: A11yRule;
3
+ //# sourceMappingURL=missing-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-label.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/missing-label.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAQvD,eAAO,MAAM,gBAAgB,EAAE,QA8D9B,CAAC"}
@@ -0,0 +1,64 @@
1
+ const CONTAINER_PATTERNS = ["Layout", "ViewGroup", "ScrollView"];
2
+ function isContainer(className) {
3
+ return CONTAINER_PATTERNS.some((p) => className.includes(p));
4
+ }
5
+ export const missingLabelRule = {
6
+ id: "missing-label",
7
+ name: "Missing Label",
8
+ wcag: "1.1.1",
9
+ severity: "critical",
10
+ description: "Clickable elements must have a text label or content description for screen readers.",
11
+ platforms: ["android", "ios", "desktop"],
12
+ run(elements) {
13
+ const issues = [];
14
+ for (const el of elements) {
15
+ if (!el.clickable)
16
+ continue;
17
+ if (isContainer(el.className))
18
+ continue;
19
+ // Invisible elements are skipped
20
+ if (el.width <= 0 || el.height <= 0)
21
+ continue;
22
+ const hasContentDesc = el.contentDesc.trim().length > 0;
23
+ const hasText = el.text.trim().length > 0;
24
+ // Password elements: contentDesc still required, but missing text is ok
25
+ if (el.password) {
26
+ if (!hasContentDesc) {
27
+ issues.push({
28
+ ruleId: "missing-label",
29
+ wcag: "1.1.1",
30
+ severity: "critical",
31
+ message: "Password field has no content description for screen readers",
32
+ element: {
33
+ index: el.index,
34
+ className: el.className,
35
+ resourceId: el.resourceId,
36
+ bounds: el.bounds,
37
+ centerX: el.centerX,
38
+ centerY: el.centerY,
39
+ },
40
+ });
41
+ }
42
+ continue;
43
+ }
44
+ if (!hasText && !hasContentDesc) {
45
+ issues.push({
46
+ ruleId: "missing-label",
47
+ wcag: "1.1.1",
48
+ severity: "critical",
49
+ message: "Clickable element has no text or content description",
50
+ element: {
51
+ index: el.index,
52
+ className: el.className,
53
+ resourceId: el.resourceId,
54
+ bounds: el.bounds,
55
+ centerX: el.centerX,
56
+ centerY: el.centerY,
57
+ },
58
+ });
59
+ }
60
+ }
61
+ return issues;
62
+ },
63
+ };
64
+ //# sourceMappingURL=missing-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-label.js","sourceRoot":"","sources":["../../../src/a11y/rules/missing-label.ts"],"names":[],"mappings":"AAGA,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;AAEjE,SAAS,WAAW,CAAC,SAAiB;IACpC,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,sFAAsF;IACxF,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YAC5B,IAAI,WAAW,CAAC,EAAE,CAAC,SAAS,CAAC;gBAAE,SAAS;YACxC,iCAAiC;YACjC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,MAAM,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YACxD,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YAE1C,wEAAwE;YACxE,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;gBAChB,IAAI,CAAC,cAAc,EAAE,CAAC;oBACpB,MAAM,CAAC,IAAI,CAAC;wBACV,MAAM,EAAE,eAAe;wBACvB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,UAAU;wBACpB,OAAO,EACL,8DAA8D;wBAChE,OAAO,EAAE;4BACP,KAAK,EAAE,EAAE,CAAC,KAAK;4BACf,SAAS,EAAE,EAAE,CAAC,SAAS;4BACvB,UAAU,EAAE,EAAE,CAAC,UAAU;4BACzB,MAAM,EAAE,EAAE,CAAC,MAAM;4BACjB,OAAO,EAAE,EAAE,CAAC,OAAO;4BACnB,OAAO,EAAE,EAAE,CAAC,OAAO;yBACpB;qBACF,CAAC,CAAC;gBACL,CAAC;gBACD,SAAS;YACX,CAAC;YAED,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,eAAe;oBACvB,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,UAAU;oBACpB,OAAO,EAAE,sDAAsD;oBAC/D,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const stateDescriptionRule: A11yRule;
3
+ //# sourceMappingURL=state-description.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-description.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/state-description.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAEvD,eAAO,MAAM,oBAAoB,EAAE,QAuClC,CAAC"}
@@ -0,0 +1,37 @@
1
+ export const stateDescriptionRule = {
2
+ id: "state-description",
3
+ name: "State Description",
4
+ wcag: "4.1.2",
5
+ severity: "moderate",
6
+ description: "Checkable elements (checkboxes, switches, toggles) must have a text label or content description.",
7
+ platforms: ["android", "ios", "desktop"],
8
+ run(elements) {
9
+ const issues = [];
10
+ for (const el of elements) {
11
+ if (!el.checkable)
12
+ continue;
13
+ if (el.width <= 0 || el.height <= 0)
14
+ continue;
15
+ const hasText = el.text.trim().length > 0;
16
+ const hasContentDesc = el.contentDesc.trim().length > 0;
17
+ if (!hasText && !hasContentDesc) {
18
+ issues.push({
19
+ ruleId: "state-description",
20
+ wcag: "4.1.2",
21
+ severity: "moderate",
22
+ message: "Checkable element has no text or content description to convey its purpose",
23
+ element: {
24
+ index: el.index,
25
+ className: el.className,
26
+ resourceId: el.resourceId,
27
+ bounds: el.bounds,
28
+ centerX: el.centerX,
29
+ centerY: el.centerY,
30
+ },
31
+ });
32
+ }
33
+ }
34
+ return issues;
35
+ },
36
+ };
37
+ //# sourceMappingURL=state-description.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-description.js","sourceRoot":"","sources":["../../../src/a11y/rules/state-description.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,oBAAoB,GAAa;IAC5C,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,mGAAmG;IACrG,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YAC5B,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YAC1C,MAAM,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;YAExD,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,mBAAmB;oBAC3B,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,UAAU;oBACpB,OAAO,EACL,4EAA4E;oBAC9E,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { A11yRule } from "../types.js";
2
+ export declare const touchTargetRule: A11yRule;
3
+ //# sourceMappingURL=touch-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"touch-target.d.ts","sourceRoot":"","sources":["../../../src/a11y/rules/touch-target.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAIvD,eAAO,MAAM,eAAe,EAAE,QAmC7B,CAAC"}
@@ -0,0 +1,37 @@
1
+ const MIN_TARGET_SIZE = 48;
2
+ export const touchTargetRule = {
3
+ id: "touch-target",
4
+ name: "Touch Target Size",
5
+ wcag: "2.5.8",
6
+ severity: "serious",
7
+ description: `Interactive elements must have a minimum touch target size of ${MIN_TARGET_SIZE}x${MIN_TARGET_SIZE}dp.`,
8
+ platforms: ["android", "ios", "desktop"],
9
+ run(elements) {
10
+ const issues = [];
11
+ for (const el of elements) {
12
+ if (!el.clickable)
13
+ continue;
14
+ // Skip invisible elements
15
+ if (el.width <= 0 || el.height <= 0)
16
+ continue;
17
+ if (el.width < MIN_TARGET_SIZE || el.height < MIN_TARGET_SIZE) {
18
+ issues.push({
19
+ ruleId: "touch-target",
20
+ wcag: "2.5.8",
21
+ severity: "serious",
22
+ message: `Touch target ${el.width}x${el.height} is smaller than minimum ${MIN_TARGET_SIZE}x${MIN_TARGET_SIZE}`,
23
+ element: {
24
+ index: el.index,
25
+ className: el.className,
26
+ resourceId: el.resourceId,
27
+ bounds: el.bounds,
28
+ centerX: el.centerX,
29
+ centerY: el.centerY,
30
+ },
31
+ });
32
+ }
33
+ }
34
+ return issues;
35
+ },
36
+ };
37
+ //# sourceMappingURL=touch-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"touch-target.js","sourceRoot":"","sources":["../../../src/a11y/rules/touch-target.ts"],"names":[],"mappings":"AAGA,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,SAAS;IACnB,WAAW,EAAE,iEAAiE,eAAe,IAAI,eAAe,KAAK;IACrH,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC;IACxC,GAAG,CAAC,QAAqB;QACvB,MAAM,MAAM,GAAgB,EAAE,CAAC;QAE/B,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,IAAI,CAAC,EAAE,CAAC,SAAS;gBAAE,SAAS;YAC5B,0BAA0B;YAC1B,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAS;YAE9C,IAAI,EAAE,CAAC,KAAK,GAAG,eAAe,IAAI,EAAE,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;gBAC9D,MAAM,CAAC,IAAI,CAAC;oBACV,MAAM,EAAE,cAAc;oBACtB,IAAI,EAAE,OAAO;oBACb,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,gBAAgB,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,4BAA4B,eAAe,IAAI,eAAe,EAAE;oBAC9G,OAAO,EAAE;wBACP,KAAK,EAAE,EAAE,CAAC,KAAK;wBACf,SAAS,EAAE,EAAE,CAAC,SAAS;wBACvB,UAAU,EAAE,EAAE,CAAC,UAAU;wBACzB,MAAM,EAAE,EAAE,CAAC,MAAM;wBACjB,OAAO,EAAE,EAAE,CAAC,OAAO;wBACnB,OAAO,EAAE,EAAE,CAAC,OAAO;qBACpB;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { A11yIssue } from "./types.js";
2
+ /**
3
+ * Calculate accessibility score from issues.
4
+ * Score = 100 - sum(severity weights), clamped to [0, 100].
5
+ */
6
+ export declare function calculateScore(issues: A11yIssue[]): number;
7
+ //# sourceMappingURL=score.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/a11y/score.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAM1D"}