@dopame/acde-linux-x64 0.0.0-stage → 0.0.10

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 (165) hide show
  1. package/LICENSE +8 -0
  2. package/bin/acde +66 -0
  3. package/bin/acde.cmd +45 -0
  4. package/bin/acdebin-dbg +4 -0
  5. package/bin/app/brand/art.mjs +53 -0
  6. package/bin/app/brand/brand.css +39 -0
  7. package/bin/app/brand/lockup.mjs +17 -0
  8. package/bin/app/brand/mark.mjs +124 -0
  9. package/bin/app/dist/broker.js +1 -0
  10. package/bin/app/dist/web.js +1 -0
  11. package/bin/app/greet/index.html +177 -0
  12. package/bin/app/greet/package.json +16 -0
  13. package/bin/app/package.json +35 -0
  14. package/lib/acde/LICENSE +8 -0
  15. package/lib/acde/dist/chunk-0n69bv5q.js +1 -0
  16. package/lib/acde/dist/chunk-1cw92x19.js +1 -0
  17. package/lib/acde/dist/chunk-458e14zb.js +1 -0
  18. package/lib/acde/dist/chunk-88mxgp54.js +1 -0
  19. package/lib/acde/dist/chunk-8dv09ven.js +1 -0
  20. package/lib/acde/dist/chunk-8fxsj5h6.js +1 -0
  21. package/lib/acde/dist/chunk-8pf3kk8v.js +1 -0
  22. package/lib/acde/dist/chunk-8r65bz1e.js +1 -0
  23. package/lib/acde/dist/chunk-9hpnkmxn.js +1 -0
  24. package/lib/acde/dist/chunk-a7w41xr9.js +1 -0
  25. package/lib/acde/dist/chunk-db7qqt8a.js +1 -0
  26. package/lib/acde/dist/chunk-e3rzr5fj.js +1 -0
  27. package/lib/acde/dist/chunk-ehms6xjp.js +1 -0
  28. package/lib/acde/dist/chunk-ggykhab6.js +1 -0
  29. package/lib/acde/dist/chunk-gt70e8h2.js +1 -0
  30. package/lib/acde/dist/chunk-hcf0xd5c.js +1 -0
  31. package/lib/acde/dist/chunk-hdrvz9sa.js +1 -0
  32. package/lib/acde/dist/chunk-jjq2mn94.js +1 -0
  33. package/lib/acde/dist/chunk-k8f4m94e.js +7 -0
  34. package/lib/acde/dist/chunk-mtgy1bm0.js +1 -0
  35. package/lib/acde/dist/chunk-mtw6p7kv.js +1 -0
  36. package/lib/acde/dist/chunk-nwkq7cpp.js +1 -0
  37. package/lib/acde/dist/chunk-nyr54266.js +1 -0
  38. package/lib/acde/dist/chunk-p0h5kspx.js +1 -0
  39. package/lib/acde/dist/chunk-pdj3h86e.js +1 -0
  40. package/lib/acde/dist/chunk-q9dbzac5.js +1 -0
  41. package/lib/acde/dist/chunk-qrhbrjzk.js +1 -0
  42. package/lib/acde/dist/chunk-s8p0y5ap.js +1 -0
  43. package/lib/acde/dist/chunk-t0xqxe00.js +1 -0
  44. package/lib/acde/dist/chunk-tgn5c2mw.js +1 -0
  45. package/lib/acde/dist/chunk-wmj6zwvn.js +1 -0
  46. package/lib/acde/dist/chunk-wnk81fkc.js +1 -0
  47. package/lib/acde/dist/chunk-x208z38w.js +1 -0
  48. package/lib/acde/dist/chunk-xq76vbg6.js +1 -0
  49. package/lib/acde/dist/chunk-xr9gh5x2.js +1 -0
  50. package/lib/acde/dist/chunk-yytnhz6e.js +1 -0
  51. package/lib/acde/dist/chunk-z0ts25s1.js +1 -0
  52. package/lib/acde/dist/chunk-zdyw1ckt.js +1 -0
  53. package/lib/acde/dist/cli.js +1 -0
  54. package/lib/acde/dist/driver/index.js +1 -0
  55. package/lib/acde/dist/e2e/index.js +1 -0
  56. package/lib/acde/dist/types/configs.d.ts +1 -0
  57. package/lib/acde/dist/types/driver/acde.d.ts +106 -0
  58. package/lib/acde/dist/types/driver/clock.d.ts +32 -0
  59. package/lib/acde/dist/types/driver/expect.d.ts +60 -0
  60. package/lib/acde/dist/types/driver/format.d.ts +16 -0
  61. package/lib/acde/dist/types/driver/index.d.ts +13 -0
  62. package/lib/acde/dist/types/driver/input.d.ts +75 -0
  63. package/lib/acde/dist/types/driver/locator.d.ts +151 -0
  64. package/lib/acde/dist/types/driver/logs.d.ts +26 -0
  65. package/lib/acde/dist/types/driver/png.d.ts +31 -0
  66. package/lib/acde/dist/types/driver/poll.d.ts +4 -0
  67. package/lib/acde/dist/types/driver/screenshot.d.ts +37 -0
  68. package/lib/acde/dist/types/driver/trace.d.ts +26 -0
  69. package/lib/acde/dist/types/driver/types.d.ts +213 -0
  70. package/lib/acde/dist/types/driver/video.d.ts +14 -0
  71. package/lib/acde/dist/types/driver/window.d.ts +77 -0
  72. package/lib/acde/dist/types/e2e/index.d.ts +78 -0
  73. package/lib/acde/dist/types/e2e/settings.d.ts +39 -0
  74. package/lib/acde/dist/types/ipc.d.ts +24 -0
  75. package/lib/acde/dist/types/paths.d.ts +5 -0
  76. package/lib/acde/dist/types/pm.d.ts +20 -0
  77. package/lib/acde/dist/types/runtime.d.ts +49 -0
  78. package/lib/acde/dist/types/sessions.d.ts +80 -0
  79. package/lib/acde/dist/types/sockets.d.ts +4 -0
  80. package/lib/acde/launcher.d.mts +25 -0
  81. package/lib/acde/launcher.mjs +1 -0
  82. package/lib/acde/package.json +31 -0
  83. package/lib/acde/skills/acde/SKILL.md +114 -0
  84. package/lib/acde/skills/acde/references/guide/agents.md +50 -0
  85. package/lib/acde/skills/acde/references/guide/assets.md +40 -0
  86. package/lib/acde/skills/acde/references/guide/backend.md +94 -0
  87. package/lib/acde/skills/acde/references/guide/capabilities.md +157 -0
  88. package/lib/acde/skills/acde/references/guide/config.md +73 -0
  89. package/lib/acde/skills/acde/references/guide/desktop.md +165 -0
  90. package/lib/acde/skills/acde/references/guide/dev.md +66 -0
  91. package/lib/acde/skills/acde/references/guide/doctor.md +144 -0
  92. package/lib/acde/skills/acde/references/guide/files.md +181 -0
  93. package/lib/acde/skills/acde/references/guide/frameworks.md +74 -0
  94. package/lib/acde/skills/acde/references/guide/install.md +34 -0
  95. package/lib/acde/skills/acde/references/guide/mcp.md +64 -0
  96. package/lib/acde/skills/acde/references/guide/open.md +107 -0
  97. package/lib/acde/skills/acde/references/guide/services.md +147 -0
  98. package/lib/acde/skills/acde/references/guide/testing.md +378 -0
  99. package/lib/acde/skills/acde/references/guide/tools.md +291 -0
  100. package/lib/acde/skills/acde/references/guide/window.md +167 -0
  101. package/lib/acde/skills/acde/references/reference/cli.md +221 -0
  102. package/lib/acde/skills/acde/references/reference/config.md +51 -0
  103. package/lib/acde/skills/acde/references/reference/env.md +44 -0
  104. package/lib/acde/skills/acde/references/reference/manifest.md +92 -0
  105. package/lib/acde/skills/acde/references/reference/modules.md +586 -0
  106. package/lib/acde/skills/acde/references/reference/platforms.md +23 -0
  107. package/lib/acde/skills/acde/references/reference/service.md +47 -0
  108. package/lib/acde/skills/acde/references/reference/testing.md +265 -0
  109. package/lib/acde/skills/acde/references/reference/web.md +94 -0
  110. package/lib/acde/skills/acde/references/start/first-app.md +87 -0
  111. package/lib/acde/skills/acde/references/start/install.md +232 -0
  112. package/lib/acde/skills/acde/references/start/project.md +77 -0
  113. package/lib/acde/templates/app/acde.config.jsonc +27 -0
  114. package/lib/acde/templates/app/gitignore +2 -0
  115. package/lib/acde/templates/app/index.html +22 -0
  116. package/lib/acde/templates/app/package.json +5 -0
  117. package/lib/acde/templates/app/src/i18n.js +26 -0
  118. package/lib/acde/templates/app/src/locales/en.js +4 -0
  119. package/lib/acde/templates/app/src/locales/zh.js +4 -0
  120. package/lib/acde/templates/app/src/main.js +5 -0
  121. package/lib/acde/templates/backend/server.ts +7 -0
  122. package/lib/acde/templates/backend/src/main.js +6 -0
  123. package/lib/acde/templates/desktop/acde.config.jsonc +17 -0
  124. package/lib/acde/templates/desktop/gitignore +2 -0
  125. package/lib/acde/templates/desktop/index.html +19 -0
  126. package/lib/acde/templates/desktop/package.json +5 -0
  127. package/lib/acde/templates/desktop/src/desktop.js +16 -0
  128. package/lib/acde/templates/desktop/src/i18n.js +26 -0
  129. package/lib/acde/templates/desktop/src/locales/en.js +4 -0
  130. package/lib/acde/templates/desktop/src/locales/zh.js +4 -0
  131. package/lib/acde/templates/framework/acde.config.jsonc +26 -0
  132. package/lib/acde-api/LICENSE +5 -0
  133. package/lib/acde-api/config.schema.json +563 -0
  134. package/lib/acde-api/package.json +51 -0
  135. package/lib/acde-api/src/app.d.ts +36 -0
  136. package/lib/acde-api/src/frame.d.ts +15 -0
  137. package/lib/acde-api/src/frame.js +1 -0
  138. package/lib/acde-api/src/index.d.ts +11 -0
  139. package/lib/acde-api/src/index.js +1 -0
  140. package/lib/acde-api/src/modules/app-modules.d.ts +81 -0
  141. package/lib/acde-api/src/modules/modules.d.ts +113 -0
  142. package/lib/acde-api/src/modules/shell-modules.d.ts +32 -0
  143. package/lib/acde-api/src/runtime/level.d.ts +1 -0
  144. package/lib/acde-api/src/runtime/level.js +1 -0
  145. package/lib/acde-api/src/runtime/load.d.ts +8 -0
  146. package/lib/acde-api/src/runtime/load.js +1 -0
  147. package/lib/acde-api/src/runtime/places.d.ts +3 -0
  148. package/lib/acde-api/src/runtime/places.js +1 -0
  149. package/lib/acde-api/src/runtime/platforms.d.ts +5 -0
  150. package/lib/acde-api/src/runtime/platforms.js +1 -0
  151. package/lib/acde-api/src/shell.d.ts +14 -0
  152. package/lib/acde-api/src/types/capabilities.d.ts +34 -0
  153. package/lib/acde-api/src/types/config.d.ts +55 -0
  154. package/lib/acde-api/src/types/events.d.ts +151 -0
  155. package/lib/acde-api/src/types/manifest.d.ts +83 -0
  156. package/lib/acde-api/src/types/office.d.ts +118 -0
  157. package/lib/acde-api/src/types/page.d.ts +1090 -0
  158. package/lib/acde-api/src/types/permissions.d.ts +50 -0
  159. package/lib/acde-api/src/types/service.d.ts +35 -0
  160. package/lib/acde-api/src/validate.d.ts +64 -0
  161. package/lib/acde-api/src/validate.js +1 -0
  162. package/lib/acde-api/src/vite.d.ts +3 -0
  163. package/lib/acde-api/src/vite.js +9 -0
  164. package/package.json +11 -4
  165. package/README.md +0 -3
@@ -0,0 +1,265 @@
1
+ # Testing API
2
+
3
+ `acde/test` exports `test`, `expect`, `describe`, `beforeAll`, `beforeEach`, `afterAll`, `afterEach`, and everything from `acde/driver`, including the types (`Fixtures`, `AppWindow`, `Locator`, `Acde`, `E2eSettings` and the option and result types below; `import { type AppWindow } from 'acde/test'`). `acde/driver` depends only on `node:` modules, so you can use it on its own in Node.js / Bun scripts.
4
+
5
+ ## test
6
+
7
+ ```ts
8
+ test(name: string, body: (fixtures: Fixtures) => unknown, options?: TestOptions): void
9
+ test(name: string, options: TestOptions, body: (fixtures: Fixtures) => unknown): void
10
+ test.only / test.skip / test.todo(name) / test.if(condition)
11
+
12
+ type TestOptions = number | {
13
+ timeout?: number
14
+ size?: [width: number, height: number]
15
+ localStorage?: Record<string, string>
16
+ }
17
+
18
+ interface Fixtures {
19
+ acde: Acde
20
+ app: AppWindow | null
21
+ home: string
22
+ settings: E2eSettings
23
+ }
24
+ ```
25
+
26
+ A number is the timeout in milliseconds. `localStorage` is written before the page loads (`app.seedLocalStorage`), then `size` sets the page's size (`app.resize`); both need the `app` fixture.
27
+
28
+ ## expect
29
+
30
+ ```ts
31
+ expect(locator: Locator): LocatorAssertions
32
+ expect(window: AppWindow): WindowAssertions
33
+ expect(value): bun:test Matchers
34
+ expect.poll(read: () => T | Promise<T>, options?: { timeout?: number; message?: string }): bun:test Matchers, each returning a Promise
35
+ expect.soft(locator | window | value): the same assertions; a failure is recorded and the test fails at its end
36
+ ```
37
+
38
+ **LocatorAssertions** (all support `.not` and accept `{timeout}`):
39
+
40
+ `toBeVisible()` `toBeHidden()` `toHaveCount(n)` `toHaveText(text, {exact?})` `toContainText(text)` `toHaveValue(text)` `toHaveState(state)` `toBeChecked()` `toBeDisabled()` `toBeEnabled()` `toBeFocused()` `toHaveAttribute(name, value?)` `toHaveClass(text)` `toHaveCSS(property, text)` (the computed style) `toHaveJSProperty(name, value)` `toBeEmpty()` `toBeEditable()` `toBeInViewport({ratio?})` `toHaveScreenshot(name?, options?)`
41
+
42
+ **WindowAssertions**: `toBeOpen()` `toHaveTitle(text)` `toHaveScreenshot(name?, options?)`
43
+
44
+ `text` is a string or a RegExp. `toHaveText` and `toContainText` both compare the element's text content, not its accessible name. Screenshot options: `{maxDiffPixels, maxDiffPixelRatio, threshold, mask: Locator[], timeout}`.
45
+
46
+ ## Acde
47
+
48
+ ```ts
49
+ class Acde {
50
+ static launch(options?: {
51
+ dirs?: string[]; desktop?: string; app?: string; out?: string
52
+ cwd?: string; env?: Record<string, string>; headless?: boolean
53
+ timeout?: number; startTimeout?: number; force?: boolean
54
+ }): Promise<Acde>
55
+ static attach(options?: { session?: number; cwd?: string; env?: Record<string, string>; timeout?: number }): Promise<Acde>
56
+
57
+ readonly desktop: AppWindow
58
+ readonly trace: Trace
59
+ window(match: string | { ref?: string; id?: number; app?: string; title?: string | RegExp }): AppWindow
60
+ open(name: string, options?: { timeout?: number }): Promise<AppWindow>
61
+ webs(): Promise<Web[]>
62
+ windows(): Promise<InspectWindow[]>
63
+
64
+ evaluate<T>(web: string, code: string): Promise<T>
65
+ snapshot(options?): Promise<{ text; windows; trees }>
66
+ query(options: { selector: string; window?: string; ref?: string; limit?: number; attributes?: string[] }): Promise<QueryResult>
67
+ act(ref: string, action: InspectAction, arg?): Promise<ActResult>
68
+ run(steps): Promise<ActResult>
69
+
70
+ screenshot(web: string, options?: { path?: string }): Promise<Shot>
71
+ screen(options?: { path?: string }): Promise<Shot>
72
+ console(options?: { web?; since?; limit?; level? }): Promise<{ entries; next; dropped }>
73
+ network(options?: { web?; since?; limit?; failed? }): Promise<{ entries; next; dropped }>
74
+ waitForRequest(url: string | RegExp, options?: { method?; web?; timeout? }): Promise<RequestEntry>
75
+ waitForConsole(text: string | RegExp, options?: { level?; web?; timeout? }): Promise<ConsoleEntry>
76
+
77
+ emulate(options: { colorScheme?: 'light' | 'dark' | null }): Promise<...>
78
+ chooseFolder(folder: string | null): Promise<void>
79
+ chooseFiles(files: string | string[] | null): Promise<void>
80
+ chooseSaveFile(file: string | null): Promise<void>
81
+ route(url: string | RegExp, response: RouteResponse, options?: { method?; web?; times? }): Promise<number>
82
+ unroute(id?: number): Promise<{ removed: number; left: number }>
83
+ pointer(x: number, y: number): Promise<{ x: number; y: number }> // a real pointer move to screen coordinates
84
+ clipboard(): Promise<string>
85
+ setClipboard(text: string): Promise<void>
86
+
87
+ addInitScript(code: string, options?: { web?: string; once?: boolean }): Promise<number>
88
+ removeInitScript(id?: number): Promise<{ removed: number; left: number }>
89
+ reload(web: string): Promise<void>
90
+ resize(web: string, width: number, height: number): Promise<{ width: number; height: number }>
91
+ answerDialog(web: string, answer?: { accept?: boolean; text?: string }): Promise<void>
92
+ dialogs(options?: { web?: string }): Promise<Dialog[]>
93
+ waitForDialog(options?: { web?: string; type?: 'alert' | 'confirm' | 'prompt'; timeout?: number }): Promise<Dialog>
94
+ input(web: string, events: InputEvent[]): Promise<ActResult>
95
+
96
+ permissions(): Promise<{ pending: PermissionPrompt[]; decisions: PermissionDecision[] }>
97
+ waitForPrompt(match?: { app?: string; capability?: string; timeout?: number }): Promise<PermissionPrompt>
98
+ allow(match?: { app?: string; capability?: string; timeout?: number }): Promise<PermissionPrompt>
99
+ deny(match?: { app?: string; capability?: string; timeout?: number }): Promise<PermissionPrompt>
100
+ answer(prompt: PermissionPrompt | number, allow: boolean): Promise<void>
101
+ revoke(app: string, capability: string): Promise<boolean>
102
+ drop(files: string | string[], options?: { web?: string; x?: number; y?: number }): Promise<{ web; window; files }>
103
+
104
+ ready(): Promise<boolean>
105
+ readonly closed: boolean
106
+ close(): Promise<void>
107
+ }
108
+
109
+ interface ActResult {
110
+ ok: boolean
111
+ text: string
112
+ ended?: true
113
+ }
114
+ ```
115
+
116
+ - `launch` starts an `acde dev` session on the project's own runtime (Bun or Node.js) and waits until the windows of all pages have appeared, or an approval prompt waits (`startTimeout`, 120 seconds by default). With `force` (after 0.0.9), it starts even when another `acde dev` runs the project (`acde dev --force`); without it, that case is an error. When the session does not start, it ends that `acde dev` and throws a `LaunchError` (an `Error`, exported by `acde/driver`). Its message says how far the start got and ends with the last 40 lines of the output of `acde dev`; `logs` holds the whole output (`error.logs.tail(5000).text`), and `phase` is:
117
+ - `'exited'`: `acde dev exited (1) before it was ready:`; `code` is the exit code (`null` in the other phases).
118
+ - `'session'`: `acde dev (pid 4242) did not register a session within 120 s in <dir>`, then the line `no session yet: its build did not finish, or acdert did not start listening; last output:`.
119
+ - `'window'`: `acde dev did not start within 120 s:`, then the line `acdert runs and answers, but no window appeared (pages: 0, windows: 0) and no approval prompt waits; last output:`.
120
+ - `'failed'`: anything else that went wrong while it waited (the message is that error's, which is also the `cause`).
121
+ - `close()` ends the session that `launch` started: it asks acdert to quit, so acdert and `acde dev` end on their own. On macOS and Linux it also sends SIGTERM to `acde dev`, then SIGKILL after 10 seconds. On Windows it waits up to 10 seconds for them to exit, then ends `acde dev` and acdert together (the whole process tree). On a session from `attach` it only disconnects; requests still waiting fail at once.
122
+ - `act` answers `ok: false` when the action was not done. In a session that runs a single app, closing the app's only window ends the session, by design: the `close` that did it resolves with `ok: true`, `ended: true` and the text `closed w1; acdert exited with it, so acde dev <pid> ended`; `closed` becomes true, and every later request rejects with `acde dev <pid> is no longer connected`. Any other request during which the runtime goes away rejects with `acdert closed the connection` (also a `close` in a desktop session, and a `run` whose steps close the window).
123
+ - `choose*` answers, in advance, the **next** picker of that kind the page opens; `null` means the user cancelled; relative paths resolve against the current directory. A headless session has no picker dialog, so a picker opened without an answer fails and the page sees it as cancelled.
124
+ - Call `waitFor*` before the action that triggers it.
125
+ - `addInitScript` registers code that runs before a page's own scripts on every load (`once`: on the next load only); `web` is a page name or an app id, and without it every page runs the code. It returns an id for `removeInitScript`, which removes every script when no id is given.
126
+ - `answerDialog` answers the page's next `alert` / `confirm` / `prompt` in advance: `accept: false` cancels, `text` is what a `prompt` answers. Without an answer `confirm` gives false and `prompt` null. `Dialog` is `{ type, message, defaultValue, answered, accept, text }`.
127
+ - `input` sends raw events at page coordinates: `{ type: 'move' | 'down' | 'up' | 'wheel', x, y, button?, dx?, dy? }`, `{ type: 'keydown' | 'keyup', key }` and `{ type: 'text', text }`. `app.mouse` and `app.keyboard` are built on it.
128
+ - Camera and microphone prompts (`PermissionPrompt` is `{ id, app, label, capability, reason }`, `PermissionDecision` is `{ app, label, capability, decision: 'granted' | 'denied' }`): `allow` and `deny` wait for a prompt that matches (any prompt when nothing is given), answer it and return it; `waitForPrompt` only waits; `answer` answers a prompt you already have. Nothing answers a prompt by itself: after 0.0.9, `getUserMedia()` waits until the test answers; in 0.0.9, the prompt comes when an app that declares the capability starts, and its window waits. `revoke` takes a decision back and returns whether there was one. See [Approvals in tests](../guide/testing.md#approvals).
129
+ - `drop` drops files from the system onto a page (default: the middle of the desktop page; `web` picks a page, `x` and `y` a point in it). The page gets a standard `drop` event a moment after the call returns, so wait for what the page does with it.
130
+
131
+ ### route
132
+
133
+ ```ts
134
+ type RouteResponse =
135
+ | { status?: number; statusText?: string; headers?: Record<string, string>; contentType?: string; body?: string | Uint8Array; json?: unknown }
136
+ | { abort: true | string }
137
+ ```
138
+
139
+ `url` is a glob (`**` matches anything, `*` matches anything except `/`; a glob starting with `/` is matched against the path plus query, otherwise against the whole URL) or a RegExp. Rules added later match first; a rule is removed automatically once its `times` are used up. Only fetch / XHR are affected, not WebSocket / EventSource.
140
+
141
+ ## AppWindow
142
+
143
+ ```ts
144
+ class AppWindow {
145
+ getByRole(role: string, options?: RoleOptions): Locator
146
+ getByText(text: string | RegExp, options?: { exact?: boolean }): Locator
147
+ getByLabel / getByPlaceholder / getByTitle / getByAltText (text, options?): Locator
148
+ getByTestId(id: string): Locator
149
+ locator(selector: string): Locator
150
+
151
+ info(options?): Promise<InspectWindow>
152
+ isOpen(): Promise<boolean>
153
+ waitFor(options?: { state?: 'open' | 'closed'; timeout?: number }): Promise<void>
154
+ web(): Promise<string>
155
+ evaluate<T>(code: string): Promise<T>
156
+ snapshot(options?: { find?: string; diff?: boolean }): Promise<string>
157
+ screenshot(options?: { path?: string; mask?: Locator[] }): Promise<Shot>
158
+
159
+ press(keys: string): Promise<ActResult>
160
+ focus() / minimize() / restore() / fill() / fullscreen() / close(): Promise<ActResult>
161
+
162
+ readonly mouse: Mouse
163
+ readonly keyboard: Keyboard
164
+ readonly clock: Clock
165
+
166
+ addInitScript(code: string, options?: { once?: boolean }): Promise<number>
167
+ reload(options?: { timeout?: number }): Promise<void>
168
+ resize(width: number, height: number, options?: { timeout?: number }): Promise<{ width: number; height: number }>
169
+ seedLocalStorage(entries: Record<string, string>, options?: { timeout?: number }): Promise<void>
170
+ answerDialog(answer?: { accept?: boolean; text?: string }): Promise<void>
171
+ dialogs(): Promise<Dialog[]>
172
+ waitForDialog(options?: { type?: 'alert' | 'confirm' | 'prompt'; timeout?: number }): Promise<Dialog>
173
+
174
+ route / waitForRequest / waitForConsole / console / network / drop // as on Acde, scoped to this window
175
+ }
176
+
177
+ interface RoleOptions {
178
+ name?: string | RegExp; exact?: boolean
179
+ checked?: boolean; disabled?: boolean; selected?: boolean
180
+ expanded?: boolean; pressed?: boolean; focused?: boolean; level?: number
181
+ }
182
+ ```
183
+
184
+ `AppWindow` looks itself up in the window table again on every use, so it keeps working if the window is closed and reopened.
185
+
186
+ `info()` is the window's row in that table: `ref`, `id`, `app`, `title`, `state` (`normal`, `minimized`, `filled`, `fullscreen`), `focus`, `hidden` and more. While a window is minimized, `state` is `minimized`, `hidden` is `true`, and locators find no element in it: `toBeHidden()` passes, and an action waits until its timeout (`no element matches`). `restore()` brings the window and its elements back. `fill()` fills the desktop's work area with the window and `fullscreen()` covers the whole host window with it (state `filled` / `fullscreen`); `restore()` sets either back to `normal`. Both are refused for the desktop and for a window that already fills the host window (a single-app session).
187
+
188
+ `reload()` waits until the new page has loaded; `resize()` sets the size of the page without the title bar and waits until `innerWidth` and `innerHeight` have it (a window that fills the screen resizes the host window, which Windows cannot do yet); `seedLocalStorage()` writes the entries before the page's scripts run, by reloading the page once.
189
+
190
+ ```ts
191
+ class Mouse {
192
+ move(x: number, y: number, options?: { steps?: number }): Promise<ActResult>
193
+ down(options?: { button?: 'left' | 'right' | 'middle' }): Promise<ActResult>
194
+ up(options?: { button?: 'left' | 'right' | 'middle' }): Promise<ActResult>
195
+ click(x: number, y: number, options?: { button?; clickCount?: number; modifiers?: Modifier[] }): Promise<ActResult>
196
+ dblclick(x: number, y: number, options?: { button?; modifiers?: Modifier[] }): Promise<ActResult>
197
+ wheel(deltaX: number, deltaY: number): Promise<ActResult>
198
+ }
199
+
200
+ class Keyboard {
201
+ down(key: string) / up(key: string): Promise<ActResult>
202
+ press(keys: string): Promise<ActResult> // "Enter", "Control+a"
203
+ type(text: string): Promise<ActResult> // key presses; characters without a key are inserted as text
204
+ insertText(text: string): Promise<ActResult> // as the input method commits it, no key events
205
+ }
206
+
207
+ class Clock {
208
+ install(options?: { time?: number | string | Date }): Promise<ClockState>
209
+ fastForward(duration: number | string) / runFor(duration: number | string): Promise<ClockState>
210
+ pauseAt(time) / setFixedTime(time) / setSystemTime(time): Promise<ClockState>
211
+ resume() / state(): Promise<ClockState>
212
+ }
213
+
214
+ type Modifier = 'Shift' | 'Control' | 'Alt' | 'Meta' | 'ControlOrMeta'
215
+ ```
216
+
217
+ Coordinates are CSS pixels of the page, as `getBoundingClientRect()` gives them. `ControlOrMeta` is `Control` on every system. A time is milliseconds since 1970, a `Date` or a date string; a duration is milliseconds, `"mm:ss"` or `"hh:mm:ss"`. `ClockState` is `{ installed, paused, now, performanceNow, fixed, timers }`. `fastForward` fires each timer that is due once, `runFor` fires every timer at its own time; `setFixedTime` makes `Date` stand still while timers go on, `setSystemTime` moves `Date` only.
218
+
219
+ `close()` waits until the window is gone. Closing the only window of a session that runs a single app ends the session: the call resolves with `ended: true` and a text that says so (see `act` under [Acde](#acde)), and the session takes no more requests. A test has no need to close that window, since its session ends with the test.
220
+
221
+ ## Locator
222
+
223
+ ```ts
224
+ class Locator {
225
+ getByRole / getByText / getByLabel / … / locator(...) // search within this element
226
+ filter(options: { hasText?: string | RegExp; hasNotText?: string | RegExp; has?: Locator; hasNot?: Locator }): Locator
227
+ or(other: Locator) / and(other: Locator): Locator
228
+ nth(index: number) / first() / last(): Locator
229
+
230
+ count(): Promise<number>
231
+ all(): Promise<Locator[]>
232
+ isVisible(): Promise<boolean>
233
+ textContent(): Promise<string> // the text inside the element (what toHaveText compares)
234
+ text() / name() / value() / states() / ref() / node(): Promise<...>
235
+ waitFor(options?: { state?: 'visible' | 'hidden'; timeout?: number }): Promise<void>
236
+
237
+ click(options?: { button?: 'left' | 'right'; clickCount?: 1 | 2; modifiers?: Modifier[]; timeout?: number }): Promise<ActResult>
238
+ dblclick(options?: { modifiers?: Modifier[] }) / hover(options?: { modifiers?: Modifier[] }): Promise<ActResult>
239
+ focus() / clear(): Promise<ActResult>
240
+ evaluate<T>(fn: string | ((element, arg) => T), arg?: unknown): Promise<T>
241
+ fill(text: string) / type(text: string) / press(keys: string): Promise<ActResult>
242
+ scroll(dy?: number): Promise<ActResult>
243
+ selectOption(values: string | string[]): Promise<ActResult>
244
+ check() / uncheck() / setChecked(checked: boolean): Promise<void>
245
+ dragTo(target: Locator): Promise<ActResult>
246
+ screenshot(options?: { path?: string; mask?: Locator[] }): Promise<Shot>
247
+ }
248
+ ```
249
+
250
+ When an action fails (`ok: false`), it is retried until the timeout, then `ActError` is thrown.
251
+
252
+ `evaluate` calls the function in the page with the element and `arg` (JSON), and returns its JSON result; `fn` may also be the source of such a function.
253
+
254
+ ### Semantic tree limitations
255
+
256
+ `getByRole`, `getByText` and `getByLabel` search the runtime's semantic tree, not the DOM:
257
+
258
+ - Elements with `opacity: 0` are not in it, so these locators do not find them.
259
+ - `getByText` compares accessible names.
260
+
261
+ CSS locators (`locator`, `getByTestId`, `getByPlaceholder`, `getByTitle`, `getByAltText`) search the DOM and also match elements that are not rendered; `toBeVisible`, `toBeHidden`, `isVisible()` and `waitFor({ state })` tell the two apart, and actions wait until the element is rendered.
262
+
263
+ ## Png
264
+
265
+ `Png.decode(bytes)`, `Png.encode(image)`, `Png.diff(actual, expected, {threshold})`: PNG encoding/decoding using only `node:zlib`, plus a pixelmatch-style comparison. The screenshot assertions are built on it. `Png.animate(frames)` joins PNGs of one size (`{ png, ms }` each) into an animated PNG; `Video.start(acde)` records the screen with it (`stop()`, then `write(path)`), which is what `acde test --video` keeps.
@@ -0,0 +1,94 @@
1
+ # Web compatibility
2
+
3
+ acdert has its own renderer (it is not Chromium), and page JS runs in its embedded V8. The rule is that **standard APIs are fully browser-compatible**: behavior, signatures, events, and errors all follow the spec. Anything that can't be done that way is a gap; it is not filled with a look-alike proprietary version. Below are the things worth knowing.
4
+
5
+ ## Origin
6
+
7
+ Every app page has the origin `http://acde.local`. Each app's storage is a separate partition, invisible to other apps.
8
+
9
+ ## Storage
10
+
11
+ | API | Notes |
12
+ | --- | --- |
13
+ | `localStorage` | Shared by all windows of the same app; 5 MiB quota; other windows get a `storage` event on changes |
14
+ | `sessionStorage` | Per window, in memory only; **not** kept across `location.reload()` (unlike browsers) |
15
+ | IndexedDB | Fully implemented, stored in SQLite; promise-wrapper libraries (idb and others) behave as in browsers |
16
+ | Cache API | `caches` is supported |
17
+ | cookie | Stored and sent as usual for same-origin requests (your own backend); cross-origin requests neither send nor store cookies |
18
+ | `navigator.storage` | `estimate()`; `persist()` is always `true`; `getDirectory()` is the origin private file system (OPFS) |
19
+
20
+ Data lives in `$XDG_DATA_HOME/acde/storage/app-<id>/`.
21
+
22
+ ## Network
23
+
24
+ - `fetch`, `XMLHttpRequest`, `WebSocket`, and `EventSource` are all supported.
25
+ - Same-origin paths and `localhost` go to the app's own backend; see [Backend](../guide/backend.md).
26
+ - Requests to other hosts use the system's DNS and network, with normal certificate validation, and go through a proxy the way the system says: with `http_proxy`, `https_proxy` or `all_proxy` set in the session, only those variables (and `no_proxy`) count; otherwise the system's proxy settings (Windows: the user's Internet settings, including a configuration script and automatic detection; macOS: the network settings; Linux has no other source). `localhost`, loopback addresses and the app's own backend are always reached directly. Proxy authentication and `https://` proxies are not supported. Responses are not decompressed automatically (no `Accept-Encoding` is sent).
27
+ - Cross-origin requests follow browser rules: `Origin` header, CORS preflight, response header filtering, opaque responses for `no-cors`.
28
+ - Forbidden request headers (`Host`, `Cookie`, `Origin`, `Referer`, `Sec-*`, ...) are not sent even if you set them.
29
+ - Synchronous XHR can only read the app's own files.
30
+
31
+ ## External resources
32
+
33
+ Images (`<img>` and images in CSS), stylesheets (`<link rel="stylesheet">`) and fonts load from other hosts as they do in a browser: images and stylesheets without CORS, fonts only with an `Access-Control-Allow-Origin` that allows the page, a stylesheet only from a 2xx `text/css` response. They show up in `acde network` with the kinds `image`, `stylesheet` and `font`, and a failed one is a console error `Failed to load resource: <url>: <reason>`. A `<link rel="stylesheet">` fires `load` once its sheet is fetched and applied and `error` when it cannot be had (also when a sheet it `@import`s cannot); `link.sheet` and `document.styleSheets` have the sheet from then on. The rules of a sheet from another host (`sheet.cssRules`) throw `SecurityError` unless the link has `crossorigin` and the host allows the page with `Access-Control-Allow-Origin`; after 0.0.9, its `@font-face` faces are still in `document.fonts`. **ES modules can't be `import`ed from other hosts** (`module … is not a local file`): bundle the code you depend on into the app. After 0.0.9, `blob:` and `data:` modules can be imported when their type is a JavaScript MIME type (`text/javascript`). (Classic `<script src>` can load from other hosts.) An app that should work without a network bundles its images, styles and fonts too.
34
+
35
+ ## Scripts
36
+
37
+ Classic `<script>`, `type="module"`, and `type="importmap"` are supported.
38
+
39
+ ## File pickers
40
+
41
+ `showOpenFilePicker`, `showSaveFilePicker`, `showDirectoryPicker`, and `<input type="file">` open the system dialog; see [Files and folders](../guide/files.md).
42
+
43
+ Files and folders dropped into a window arrive the standard way: `dataTransfer.files`, and a read-only handle from `dataTransfer.items[i].getAsFileSystemHandle()`. Folders and files over 64 MiB are not in `files`; use the handle. See [Dropped files](../guide/files.md#dropped-files).
44
+
45
+ ## Media
46
+
47
+ `Audio` / `AudioContext` are supported. WebGPU is currently available only on Linux.
48
+
49
+ `<video>` plays what the system's own decoders can open, so formats differ by platform: on macOS, MP4 / QuickTime plays and WebM does not. Ask `canPlayType()` before choosing a source.
50
+
51
+ `navigator.mediaDevices.getUserMedia()` needs the `camera` / `microphone` capability in the config and the user's answer the first time (see [Capabilities](../guide/capabilities.md#camera-and-microphone)). Not available on Windows.
52
+
53
+ - In 0.0.9, a required capability is asked for when the app starts, and a denial keeps the app from starting; after 0.0.9, a required one is asked for on first use too, and a denial only fails the calls. An [optional](../guide/capabilities.md#optional-capabilities) one that was not asked for yet is asked for by `getUserMedia()` itself, with the prompt `request('camera')` raises; a call that wants both asks for the microphone first, then the camera. The call resolves once the user allows and rejects with `NotAllowedError` (`Permission denied`) when the user denies; later calls do not ask again.
54
+ - `navigator.permissions.query({ name: 'camera' })` (or `'microphone'`) answers `prompt` until the user has answered and `denied` after a denial or after the approval was taken back; its `change` event fires on the answer. Once the user has allowed it, the answer is the host system's own permission for the device (`granted` when the system allows it too).
55
+ - In an app that does not declare the capability, the query answers `denied` and `getUserMedia()` rejects with `NotAllowedError`, without a prompt.
56
+ - Constraints are judged first: a request that no device can meet rejects with `OverconstrainedError` and asks nobody.
57
+ - `enumerateDevices()` lists one entry without a label for a kind that is not allowed yet.
58
+ - To try a page without a real camera or microphone, start the session with `ACDE_CAPTURE=fake`: the devices are then `Fake camera` and `Fake microphone`.
59
+
60
+ ## Rendering and canvas
61
+
62
+ Styles are computed by Stylo, the style engine of Firefox; layout and painting are acdert's own. What does not work yet:
63
+
64
+ - Canvas 2D: `drawImage()` draws only an `<img>` or a `<canvas>`; any other source (a `<video>`, an `ImageBitmap`) draws nothing and throws nothing. `createConicGradient()` does not draw a conic gradient. In 0.0.9, `ctx.filter` is accepted but not applied, and `drawImage()` of an `<img>` showing an SVG draws nothing; after 0.0.9, `ctx.filter` applies `blur()`, `brightness()`, `contrast()`, `grayscale()`, `hue-rotate()`, `invert()`, `opacity()`, `saturate()`, `sepia()` and `drop-shadow()` (not `url()`), and SVG images are drawn.
65
+ - SVG geometry: `getTotalLength()` and `getPointAtLength()` are estimates from the element's box, not its path; `isPointInFill()` and `isPointInStroke()` are always `false`.
66
+ - In 0.0.9, elements inside an inline `<svg>` have empty boxes: `getBBox()` and `getBoundingClientRect()` give zeros. After 0.0.9, shapes, text and groups have their boxes, and `getComputedTextLength()` measures text.
67
+ - In 0.0.9, `<use href="#id">` and `url(#id)` that point into another `<svg>` of the page (a sprite sheet, a shared gradient) draw nothing. After 0.0.9 they work.
68
+ - In 0.0.9 on macOS, `font-family: monospace` is Courier; after 0.0.9 it is Menlo, as in Safari and Chrome. In 0.0.9, symbols that no font in the stack has (`✕` `✔` `❤`) draw as a box, and `font-weight: 700` of a font with only a regular face draws regular; after 0.0.9, the system supplies a font for such symbols, and the bold is synthesized.
69
+
70
+ Added after 0.0.9:
71
+
72
+ - `:has()` selectors (Tailwind's `has-*` variants).
73
+ - `animationstart`, `animationiteration`, `animationend`, `animationcancel`, `transitionrun`, `transitionstart`, `transitionend` and `transitioncancel` (dialogs and menus that wait for `animationend` before they unmount).
74
+ - A `<textarea>`'s `scrollHeight` covers its text, and `field-sizing: content` grows the box with it.
75
+ - `new Image()` with a `src` loads (and fires `load` / `error`) without being in the document.
76
+ - Inline SVG colors given as `oklch()`, `lab()` or `color-mix()` (Tailwind 4's palette) are painted.
77
+ - In SVG images and inline SVG, a `<mask>` made of one filled path clips to it (other masks clip to their region), and a group `filter` made of blurs, drop shadows and color matrices is applied (the glow of icons exported from Figma).
78
+ - An element with an `id` is reachable as a global (`window.wave`).
79
+ - `pointer-events: all` and the other SVG values (`visible`, `painted`, `fill`, `stroke` …) are understood and act as `auto`. In 0.0.9 they are dropped, so the element inherits `none`: every button in a Mantine `Modal` or `Drawer` cannot be clicked.
80
+ - An SVG image used as a `background-image` or `mask-image` is drawn. In 0.0.9 a simple one (shapes in plain colours: icons, the select arrows of `@tailwindcss/forms` and Bootstrap, CSS mask icons) draws nothing.
81
+ - An inline `<svg>` without a `viewBox` draws its content in CSS pixels from its top left, and clips it to its box, as browsers do. In 0.0.9 it is stretched to the size of the box, so an `<svg>` sized only by CSS draws too large and off centre (Mantine's `RingProgress`).
82
+ - `<input>` and `<textarea>` have undo and redo: `Ctrl+Z`, and `Ctrl+Shift+Z` or `Ctrl+Y`.
83
+
84
+ ## Not yet supported
85
+
86
+ `<datalist>`, `appearance: base-select` on `<select>`, Web Workers, `crypto.subtle`, WebGL (`getContext('webgl')` and `getContext('webgl2')` return `null`; use 2D canvas, or WebGPU on Linux), OffscreenCanvas / `createImageBitmap`, the Fullscreen API (`requestFullscreen()` rejects with `NotSupportedError`; an app window goes full screen with `requestState('fullscreen')` from [`acde:window`](../guide/window.md#window-state)) and pointer lock, `:target`, Storage Buckets, and Service Workers.
87
+
88
+ ## Differences from browsers
89
+
90
+ - `alert()` is drawn inside the window; `confirm()` returns `false` and `prompt()` returns `null`.
91
+ - There is no top layer: after 0.0.9, an open popover and a modal `<dialog>` get the highest `z-index` instead, so an ancestor's `transform`, `filter` or `contain` still clips them, and they have no `::backdrop`.
92
+ - `navigator.userAgent` and `navigator.platform` say Linux on every system (`Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) acdert/<version>` and `Linux x86_64`), and editing shortcuts use Ctrl everywhere, so an app behaves the same on every host. To know the real system, read `platform` from [`acde:capabilities`](../guide/capabilities.md#acdecapabilities).
93
+ - In 0.0.9 there are no developer tools: use the [debugging tools](../guide/tools.md) and [MCP](../guide/mcp.md). After 0.0.9, `acde dev` opens the DevTools of Chrome or Edge on a page (F12 in the window, or `acde devtools`); see [acde dev](../guide/dev.md#devtools).
94
+ - Pages have no security sandbox (there is no syscall-level isolation yet): don't run code you don't trust.
@@ -0,0 +1,87 @@
1
+ # First app
2
+
3
+ ## Create
4
+
5
+ ```sh
6
+ acde new my-app
7
+ ```
8
+
9
+ In a terminal, `acde new` asks for whatever is missing: the directory, app or desktop, template, display name, whether to add a backend, Bun or Node.js (when both are installed), whether to install dependencies, and whether to run `git init`. Press Enter at each prompt to accept the defaults.
10
+
11
+ To skip the prompts, pass everything as options and add `--yes` (it still installs the dependencies; add `--no-install` to skip that):
12
+
13
+ ```sh
14
+ acde new my-app --template html --label "My App" --yes
15
+ ```
16
+
17
+ Available templates:
18
+
19
+ | Template | What it is |
20
+ | --- | --- |
21
+ | `html` | A plain web page (default); add `--backend` to include a backend |
22
+ | `desktop` | A desktop (see [Desktops](../guide/desktop.md)) |
23
+ | `react` `vue` `svelte` `solid` `preact` `vanilla` | create-vite templates. In 0.0.9 they ignore `--backend`; after 0.0.9, `--backend` adds `server.ts` |
24
+ | `next` `nuxt` `sveltekit` `astro` | Each framework's official scaffolding tool |
25
+
26
+ Framework templates run the latest version of the framework's scaffolding tool through your package manager, then add `acde.config.jsonc`.
27
+
28
+ ## Run
29
+
30
+ `acde dev` runs your app in the runtime, acdert, which the installer set up next to acde (inside the Dopame virtual machine it is already on PATH). `acde doctor` checks that it is there and works on this machine; see [The runtime](install.md#the-runtime).
31
+
32
+ ```sh
33
+ cd my-app
34
+ acde dev
35
+ ```
36
+
37
+ `acde dev` builds the project, starts acdert, and opens a window on your computer with your app inside. Leave it running, then edit and save `index.html` or `src/main.js`; the window reloads automatically.
38
+
39
+ Press Ctrl+C or close the window to stop, or run `acde stop` from another terminal in the project directory.
40
+
41
+ > In the built-in templates, `package.json` has three scripts, `dev`, `build`, and `check`, which run `acde dev` / `acde build` / `acde check`. Framework templates leave the framework's own scripts alone and add `acde:dev`, `acde:build`, and `acde:check` instead.
42
+
43
+ ## Add a capability
44
+
45
+ Let's have the app keep a file of its own. This example uses the `html` template; in a framework template, add only the `capabilities` line to its `acde.config.jsonc` (it has no `entry`, and should not get one) and put the code in one of the framework's source files.
46
+
47
+ First, request the `fs` capability in `acde.config.jsonc`:
48
+
49
+ ```jsonc
50
+ {
51
+ "type": "app",
52
+ "id": "my-app",
53
+ "label": "My App",
54
+ "icon": "files",
55
+ "entry": "index.html",
56
+ "capabilities": { "fs": true }
57
+ }
58
+ ```
59
+
60
+ Then in `src/main.js`:
61
+
62
+ ```js
63
+ import { fs } from 'acde:fs'
64
+
65
+ const status = document.querySelector('#status')
66
+ await fs.writeFile('private', 'hello.txt', 'Hello from my app')
67
+ const { text } = await fs.readText('private', 'hello.txt')
68
+ const roots = await fs.roots()
69
+ status.textContent = `${text} (roots: ${roots.map((r) => r.id).join(', ')})`
70
+ ```
71
+
72
+ Save, and after the window reloads you'll see `Hello from my app (roots: private)`. With `"fs": true` an app gets a private folder of its own (the root `private`) and whatever folder the user picks for it. The user's shared folders, such as Desktop and Downloads, are named in the config and approved by the user before the app gets them; see [Files and folders](../guide/files.md). Importing a capability you haven't requested fails outright.
73
+
74
+ ## Build and check
75
+
76
+ ```sh
77
+ acde check # check the config and entry
78
+ acde build # build into .acde/
79
+ ```
80
+
81
+ `.acde/` is the **package** the runtime understands: a directory with a `package.json` whose `acde` field is the **manifest** (what the package contains), plus the build output for the web pages and backend. To install the app into the Dopame desktop on your machine, use `acde install`; see [Local install](../guide/install.md).
82
+
83
+ ## Next steps
84
+
85
+ - [Project layout](project.md)
86
+ - [Configuration](../guide/config.md)
87
+ - [Backend](../guide/backend.md)