@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.
- package/LICENSE +8 -0
- package/bin/acde +66 -0
- package/bin/acde.cmd +45 -0
- package/bin/acdebin-dbg +4 -0
- package/bin/app/brand/art.mjs +53 -0
- package/bin/app/brand/brand.css +39 -0
- package/bin/app/brand/lockup.mjs +17 -0
- package/bin/app/brand/mark.mjs +124 -0
- package/bin/app/dist/broker.js +1 -0
- package/bin/app/dist/web.js +1 -0
- package/bin/app/greet/index.html +177 -0
- package/bin/app/greet/package.json +16 -0
- package/bin/app/package.json +35 -0
- package/lib/acde/LICENSE +8 -0
- package/lib/acde/dist/chunk-0n69bv5q.js +1 -0
- package/lib/acde/dist/chunk-1cw92x19.js +1 -0
- package/lib/acde/dist/chunk-458e14zb.js +1 -0
- package/lib/acde/dist/chunk-88mxgp54.js +1 -0
- package/lib/acde/dist/chunk-8dv09ven.js +1 -0
- package/lib/acde/dist/chunk-8fxsj5h6.js +1 -0
- package/lib/acde/dist/chunk-8pf3kk8v.js +1 -0
- package/lib/acde/dist/chunk-8r65bz1e.js +1 -0
- package/lib/acde/dist/chunk-9hpnkmxn.js +1 -0
- package/lib/acde/dist/chunk-a7w41xr9.js +1 -0
- package/lib/acde/dist/chunk-db7qqt8a.js +1 -0
- package/lib/acde/dist/chunk-e3rzr5fj.js +1 -0
- package/lib/acde/dist/chunk-ehms6xjp.js +1 -0
- package/lib/acde/dist/chunk-ggykhab6.js +1 -0
- package/lib/acde/dist/chunk-gt70e8h2.js +1 -0
- package/lib/acde/dist/chunk-hcf0xd5c.js +1 -0
- package/lib/acde/dist/chunk-hdrvz9sa.js +1 -0
- package/lib/acde/dist/chunk-jjq2mn94.js +1 -0
- package/lib/acde/dist/chunk-k8f4m94e.js +7 -0
- package/lib/acde/dist/chunk-mtgy1bm0.js +1 -0
- package/lib/acde/dist/chunk-mtw6p7kv.js +1 -0
- package/lib/acde/dist/chunk-nwkq7cpp.js +1 -0
- package/lib/acde/dist/chunk-nyr54266.js +1 -0
- package/lib/acde/dist/chunk-p0h5kspx.js +1 -0
- package/lib/acde/dist/chunk-pdj3h86e.js +1 -0
- package/lib/acde/dist/chunk-q9dbzac5.js +1 -0
- package/lib/acde/dist/chunk-qrhbrjzk.js +1 -0
- package/lib/acde/dist/chunk-s8p0y5ap.js +1 -0
- package/lib/acde/dist/chunk-t0xqxe00.js +1 -0
- package/lib/acde/dist/chunk-tgn5c2mw.js +1 -0
- package/lib/acde/dist/chunk-wmj6zwvn.js +1 -0
- package/lib/acde/dist/chunk-wnk81fkc.js +1 -0
- package/lib/acde/dist/chunk-x208z38w.js +1 -0
- package/lib/acde/dist/chunk-xq76vbg6.js +1 -0
- package/lib/acde/dist/chunk-xr9gh5x2.js +1 -0
- package/lib/acde/dist/chunk-yytnhz6e.js +1 -0
- package/lib/acde/dist/chunk-z0ts25s1.js +1 -0
- package/lib/acde/dist/chunk-zdyw1ckt.js +1 -0
- package/lib/acde/dist/cli.js +1 -0
- package/lib/acde/dist/driver/index.js +1 -0
- package/lib/acde/dist/e2e/index.js +1 -0
- package/lib/acde/dist/types/configs.d.ts +1 -0
- package/lib/acde/dist/types/driver/acde.d.ts +106 -0
- package/lib/acde/dist/types/driver/clock.d.ts +32 -0
- package/lib/acde/dist/types/driver/expect.d.ts +60 -0
- package/lib/acde/dist/types/driver/format.d.ts +16 -0
- package/lib/acde/dist/types/driver/index.d.ts +13 -0
- package/lib/acde/dist/types/driver/input.d.ts +75 -0
- package/lib/acde/dist/types/driver/locator.d.ts +151 -0
- package/lib/acde/dist/types/driver/logs.d.ts +26 -0
- package/lib/acde/dist/types/driver/png.d.ts +31 -0
- package/lib/acde/dist/types/driver/poll.d.ts +4 -0
- package/lib/acde/dist/types/driver/screenshot.d.ts +37 -0
- package/lib/acde/dist/types/driver/trace.d.ts +26 -0
- package/lib/acde/dist/types/driver/types.d.ts +213 -0
- package/lib/acde/dist/types/driver/video.d.ts +14 -0
- package/lib/acde/dist/types/driver/window.d.ts +77 -0
- package/lib/acde/dist/types/e2e/index.d.ts +78 -0
- package/lib/acde/dist/types/e2e/settings.d.ts +39 -0
- package/lib/acde/dist/types/ipc.d.ts +24 -0
- package/lib/acde/dist/types/paths.d.ts +5 -0
- package/lib/acde/dist/types/pm.d.ts +20 -0
- package/lib/acde/dist/types/runtime.d.ts +49 -0
- package/lib/acde/dist/types/sessions.d.ts +80 -0
- package/lib/acde/dist/types/sockets.d.ts +4 -0
- package/lib/acde/launcher.d.mts +25 -0
- package/lib/acde/launcher.mjs +1 -0
- package/lib/acde/package.json +31 -0
- package/lib/acde/skills/acde/SKILL.md +114 -0
- package/lib/acde/skills/acde/references/guide/agents.md +50 -0
- package/lib/acde/skills/acde/references/guide/assets.md +40 -0
- package/lib/acde/skills/acde/references/guide/backend.md +94 -0
- package/lib/acde/skills/acde/references/guide/capabilities.md +157 -0
- package/lib/acde/skills/acde/references/guide/config.md +73 -0
- package/lib/acde/skills/acde/references/guide/desktop.md +165 -0
- package/lib/acde/skills/acde/references/guide/dev.md +66 -0
- package/lib/acde/skills/acde/references/guide/doctor.md +144 -0
- package/lib/acde/skills/acde/references/guide/files.md +181 -0
- package/lib/acde/skills/acde/references/guide/frameworks.md +74 -0
- package/lib/acde/skills/acde/references/guide/install.md +34 -0
- package/lib/acde/skills/acde/references/guide/mcp.md +64 -0
- package/lib/acde/skills/acde/references/guide/open.md +107 -0
- package/lib/acde/skills/acde/references/guide/services.md +147 -0
- package/lib/acde/skills/acde/references/guide/testing.md +378 -0
- package/lib/acde/skills/acde/references/guide/tools.md +291 -0
- package/lib/acde/skills/acde/references/guide/window.md +167 -0
- package/lib/acde/skills/acde/references/reference/cli.md +221 -0
- package/lib/acde/skills/acde/references/reference/config.md +51 -0
- package/lib/acde/skills/acde/references/reference/env.md +44 -0
- package/lib/acde/skills/acde/references/reference/manifest.md +92 -0
- package/lib/acde/skills/acde/references/reference/modules.md +586 -0
- package/lib/acde/skills/acde/references/reference/platforms.md +23 -0
- package/lib/acde/skills/acde/references/reference/service.md +47 -0
- package/lib/acde/skills/acde/references/reference/testing.md +265 -0
- package/lib/acde/skills/acde/references/reference/web.md +94 -0
- package/lib/acde/skills/acde/references/start/first-app.md +87 -0
- package/lib/acde/skills/acde/references/start/install.md +232 -0
- package/lib/acde/skills/acde/references/start/project.md +77 -0
- package/lib/acde/templates/app/acde.config.jsonc +27 -0
- package/lib/acde/templates/app/gitignore +2 -0
- package/lib/acde/templates/app/index.html +22 -0
- package/lib/acde/templates/app/package.json +5 -0
- package/lib/acde/templates/app/src/i18n.js +26 -0
- package/lib/acde/templates/app/src/locales/en.js +4 -0
- package/lib/acde/templates/app/src/locales/zh.js +4 -0
- package/lib/acde/templates/app/src/main.js +5 -0
- package/lib/acde/templates/backend/server.ts +7 -0
- package/lib/acde/templates/backend/src/main.js +6 -0
- package/lib/acde/templates/desktop/acde.config.jsonc +17 -0
- package/lib/acde/templates/desktop/gitignore +2 -0
- package/lib/acde/templates/desktop/index.html +19 -0
- package/lib/acde/templates/desktop/package.json +5 -0
- package/lib/acde/templates/desktop/src/desktop.js +16 -0
- package/lib/acde/templates/desktop/src/i18n.js +26 -0
- package/lib/acde/templates/desktop/src/locales/en.js +4 -0
- package/lib/acde/templates/desktop/src/locales/zh.js +4 -0
- package/lib/acde/templates/framework/acde.config.jsonc +26 -0
- package/lib/acde-api/LICENSE +5 -0
- package/lib/acde-api/config.schema.json +563 -0
- package/lib/acde-api/package.json +51 -0
- package/lib/acde-api/src/app.d.ts +36 -0
- package/lib/acde-api/src/frame.d.ts +15 -0
- package/lib/acde-api/src/frame.js +1 -0
- package/lib/acde-api/src/index.d.ts +11 -0
- package/lib/acde-api/src/index.js +1 -0
- package/lib/acde-api/src/modules/app-modules.d.ts +81 -0
- package/lib/acde-api/src/modules/modules.d.ts +113 -0
- package/lib/acde-api/src/modules/shell-modules.d.ts +32 -0
- package/lib/acde-api/src/runtime/level.d.ts +1 -0
- package/lib/acde-api/src/runtime/level.js +1 -0
- package/lib/acde-api/src/runtime/load.d.ts +8 -0
- package/lib/acde-api/src/runtime/load.js +1 -0
- package/lib/acde-api/src/runtime/places.d.ts +3 -0
- package/lib/acde-api/src/runtime/places.js +1 -0
- package/lib/acde-api/src/runtime/platforms.d.ts +5 -0
- package/lib/acde-api/src/runtime/platforms.js +1 -0
- package/lib/acde-api/src/shell.d.ts +14 -0
- package/lib/acde-api/src/types/capabilities.d.ts +34 -0
- package/lib/acde-api/src/types/config.d.ts +55 -0
- package/lib/acde-api/src/types/events.d.ts +151 -0
- package/lib/acde-api/src/types/manifest.d.ts +83 -0
- package/lib/acde-api/src/types/office.d.ts +118 -0
- package/lib/acde-api/src/types/page.d.ts +1090 -0
- package/lib/acde-api/src/types/permissions.d.ts +50 -0
- package/lib/acde-api/src/types/service.d.ts +35 -0
- package/lib/acde-api/src/validate.d.ts +64 -0
- package/lib/acde-api/src/validate.js +1 -0
- package/lib/acde-api/src/vite.d.ts +3 -0
- package/lib/acde-api/src/vite.js +9 -0
- package/package.json +11 -4
- package/README.md +0 -3
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Debugging
|
|
2
|
+
|
|
3
|
+
A set of commands connects to a running `acde dev` session and lets you look at pages, run code, take screenshots, and click buttons. After 0.0.9, `acde devtools` also opens the DevTools of Chrome or Edge on a page; see [DevTools](#devtools). Run them in another terminal:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
acde webs
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
NAME KIND APP WINDOW TITLE
|
|
11
|
+
desktop desktop - 0 My Desktop
|
|
12
|
+
notes#1048576 app notes 1048576 Notes
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Sessions
|
|
16
|
+
|
|
17
|
+
These commands find an `acde dev` session in this order: `--session <pid>`; otherwise the session whose project directory contains the current directory; otherwise, outside any project, the only session, if there is just one (the command then names it on stderr: `acde: using acde dev <pid> (...), the only one running`); otherwise they fail and list all sessions. After 0.0.9, sessions that `acde test` started for its tests are left out of this choice (they are listed with `acde test` after what they run); only `--session <pid>` reaches them.
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
| Command | What it does |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `acde webs` | Lists the running pages (one per window) |
|
|
24
|
+
| `acde eval <web> <code>` | Runs code in a page and prints the result as JSON; Promises are awaited. With `-` as the code, reads it from stdin |
|
|
25
|
+
| `acde shot <web> [-o FILE]` | Screenshot of one window (PNG) |
|
|
26
|
+
| `acde screen [-o FILE]` | Screenshot of the whole screen, all windows together |
|
|
27
|
+
| `acde pointer <x> <y>` | Moves the pointer to a point on the screen, as a real move: hover effects and screen edge events (a desktop showing its Dock at an edge) follow. In a host window the screen is its content area |
|
|
28
|
+
| `acde inspect [--window W] [--find TEXT] [--all] [--offscreen]` | Turns the screen into text: the window list and the element tree of the focused window (`--all`: every window that is not marked `[minimized]`, and elements hidden by `opacity: 0` or `visibility`) |
|
|
29
|
+
| `acde query <selector> [--window W] [--ref REF] [--limit N]` | Finds elements by CSS selector, including ones `inspect` leaves out, and prints a ref for each |
|
|
30
|
+
| `acde act <ref> <action> [arg]` | Clicks, types, presses keys, and more on a ref from `inspect` or `query` |
|
|
31
|
+
| `acde console [--web W] [--level L] [--since S] [--follow]` | The pages' `console.*` output and uncaught errors |
|
|
32
|
+
| `acde network [--web W] [--failed] [--since S] [--follow]` | The pages' fetch / XHR / WebSocket / EventSource requests and their image, stylesheet and font loads; the kind column says which |
|
|
33
|
+
| `acde permissions` | Lists the camera and microphone prompts that wait and the decisions made; see [Camera and microphone](#camera-and-microphone) |
|
|
34
|
+
| `acde allow` / `acde deny` `[<prompt id> \| <app> <capability>] [--all]` | Answers a waiting camera or microphone prompt |
|
|
35
|
+
| `acde revoke <app> <capability>` | Takes back a decision |
|
|
36
|
+
| `acde stop` | Ends the session; see [Stopping](dev.md#stopping) |
|
|
37
|
+
| `acde devtools [web]` | Opens the DevTools of Chrome or Edge on a page; see [DevTools](#devtools) (after 0.0.9) |
|
|
38
|
+
|
|
39
|
+
A **web** is one page: the desktop page, or the page in one app window (each window has one). `<web>` is a web name from `acde webs` (`notes#1048576`), an app id (that app's first window), or `desktop`.
|
|
40
|
+
|
|
41
|
+
### eval
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
acde eval notes 'document.title'
|
|
45
|
+
acde eval notes 'import("acde:fs").then(m => m.fs.roots())'
|
|
46
|
+
echo 'localStorage.length' | acde eval notes -
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
On Windows, `acde` is `acde.cmd`, so what reaches the CLI is what cmd makes of the arguments. What a shell passes differs:
|
|
50
|
+
|
|
51
|
+
- PowerShell (5.1 and 7) drops double quotes inside arguments it passes to a `.cmd`; write them as `\"` (the first line below).
|
|
52
|
+
- cmd has no single-quoted strings: put the argument in double quotes and write the double quotes inside it as `\"` (the second line below).
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
acde eval notes 'document.querySelector(\"#x\").textContent'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bat
|
|
59
|
+
acde eval notes "document.querySelector(\"#x\").textContent"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Code that has only single quotes inside it is written the same way in all three, and passing the code on stdin avoids the question (shown for PowerShell):
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
acde eval notes "document.querySelector('#x').textContent"
|
|
66
|
+
'document.querySelector("#x").textContent' | acde eval notes -
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The same applies to any argument with double quotes, such as `act ... type`.
|
|
70
|
+
|
|
71
|
+
### inspect and act
|
|
72
|
+
|
|
73
|
+
`inspect` gives every element a **ref**, such as `w1.18` (element 18 in window `w1`); pass it to `act`.
|
|
74
|
+
|
|
75
|
+
```sh
|
|
76
|
+
$ acde inspect
|
|
77
|
+
- window "Notes" [app=notes] [focused] [fullscreen] [w1]
|
|
78
|
+
- heading "Notes" [level=1] [w1.16]
|
|
79
|
+
- form [w1.6]
|
|
80
|
+
- text "Title" [w1.17]
|
|
81
|
+
- textbox "Title": "" [w1.18]
|
|
82
|
+
- text "Email" [w1.19]
|
|
83
|
+
- textbox "Email": "" [w1.20]
|
|
84
|
+
- button "Save" [w1.21]
|
|
85
|
+
- list "Saved notes" [w1.7]
|
|
86
|
+
- listitem [w1.22]
|
|
87
|
+
- text "Buy milk" [w1.22]
|
|
88
|
+
- text "1 note" [w1.8]
|
|
89
|
+
(1 more outside the visible area; scroll, or add offscreen)
|
|
90
|
+
|
|
91
|
+
$ acde act w1.18 type "Weekly report"
|
|
92
|
+
typed, the value is now "Weekly report"
|
|
93
|
+
|
|
94
|
+
(windows unchanged)
|
|
95
|
+
- window "Notes" [app=notes] [focused] [fullscreen] [w1]
|
|
96
|
+
~ - textbox "Title": "Weekly report" [focused] [w1.18]
|
|
97
|
+
(1 more outside the visible area; scroll, or add offscreen)
|
|
98
|
+
|
|
99
|
+
$ acde act w1.21 click
|
|
100
|
+
clicked 55,202
|
|
101
|
+
|
|
102
|
+
(windows unchanged)
|
|
103
|
+
- window "Notes" [app=notes] [focused] [fullscreen] [w1]
|
|
104
|
+
~ - textbox "Title": "" [w1.18]
|
|
105
|
+
~ - button "Save" [focused] [w1.21]
|
|
106
|
+
+ - listitem [w1.23]
|
|
107
|
+
+ - text "Weekly report" [w1.23]
|
|
108
|
+
~ - text "2 notes" [w1.8]
|
|
109
|
+
(1 more outside the visible area; scroll, or add offscreen)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Without `--find`, the text is always in this English format: one line per window, top first (`- window "Notes" [app=notes] [focused] [w1]`, the desktop is `- desktop`), and under a listed window its elements, indented two spaces per level, as `- role "name": "value" [states] [ref]` (the value only where the element has one). Notes in parentheses say what is left out or unchanged, such as `(empty)`, `(no changes)` and `(windows unchanged)`.
|
|
113
|
+
|
|
114
|
+
`act` prints what it did on the first line (`clicked 55,202` is the point it clicked, in window coordinates), waits for the page to settle, and then prints the windows and the content of the window again. Where it has listed that window before, it prints only what changed since: `~` for a changed line, `+` for a new one, `-` for one that is gone; `(windows unchanged)` stands for an unchanged window list. So the output of `act` is usually all you need to read before the next step. An element that is scrolled out of view is scrolled into view first, so a ref from `inspect --offscreen` or `query` works wherever the element is on the page.
|
|
115
|
+
|
|
116
|
+
`act` exits with code 0 when the action was done. When it was not, it prints one line on stderr, `acde: act <ref>: <reason>`, and exits with code 1. The reasons: `the element is not displayed` (it is `display: none` or hidden by `visibility`), `covered by another element: <div> (27)`, `cannot type into this element`, `there is no such element (any more); acde inspect --find TEXT gives the current refs` (the element is gone, see below), `w1 is minimized; act restore on it first` (a click or another pointer action on a minimized window or on an element in it).
|
|
117
|
+
|
|
118
|
+
A ref stays the same as long as its element lives: moving it, changing its text or attributes, or reordering a list keeps it. When the page replaces the element with a new one (for example by setting `innerHTML`), the new element gets a new ref, and acting on the old one fails (`acde: act w1.23: there is no such element (any more); …`); take a new `inspect` then.
|
|
119
|
+
|
|
120
|
+
After 0.0.9, an app run on its own shows its window menus on the title bar (see [Windows](window.md#menus)), and `inspect` lists them first: `menubar [w1.m]`, one `menuitem "File" [collapsed] [w1.m1]` per menu, and under the open one its items with their shortcuts, `menuitem "New": "ctrl+n" [w1.m1.1]` (a separator takes a number too). `acde act w1.m1 click` opens a menu, `acde act w1.m1.1 click` opens it if needed and chooses the item (`chose File › New`), and `acde act w1 key escape` closes it. A disabled item is not chosen (`acde: act w1.m1.4: w1.m1.4 (Print) is disabled`).
|
|
121
|
+
|
|
122
|
+
By default the tree leaves out elements that can't be seen: `display: none`, `aria-hidden`, `inert`, `opacity: 0` and hidden `visibility`. `--all` lists every window that is not marked `[minimized]` in the window list and also keeps the elements hidden by `opacity: 0` (marked `transparent`; they can still be clicked) or by `visibility` (marked `invisible`; they can't); `display: none`, `aria-hidden` and `inert` are always left out.
|
|
123
|
+
|
|
124
|
+
A window that is off the screen is marked `[minimized]` in the window list: one whose state is `minimized` (after `act w1 minimize`, the app's own `minimize()`, or the desktop's `windows.requestState(id, 'minimized')`), and one the desktop hid with `windows.hide(id)`. Its elements are not listed: `--all` and `--find` skip it, and `--window w1` prints `(empty)` and `(minimized; act restore on it to see its elements)` under its line. Pointer actions on it (`click`, `double`, `right`, `hover`, `scroll`, and `drag` from or onto it) are refused, and `act w1 restore` brings it back. In a desktop session:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
$ acde act w1 minimize
|
|
128
|
+
w1 is minimized (the windows changed)
|
|
129
|
+
|
|
130
|
+
- window "Tools" [app=tools] [minimized] [w1]
|
|
131
|
+
- desktop [focused] [fullscreen] [w0]
|
|
132
|
+
- navigation "Apps" [w0.6]
|
|
133
|
+
- button "Tools" [w0.10]
|
|
134
|
+
|
|
135
|
+
$ acde act w1.10 click
|
|
136
|
+
acde: act w1.10: w1 is minimized; act restore on it first
|
|
137
|
+
|
|
138
|
+
$ acde act w1 restore
|
|
139
|
+
w1 is in front (the windows changed)
|
|
140
|
+
|
|
141
|
+
- window "Tools" [app=tools] [focused] [w1]
|
|
142
|
+
+ - heading "Tools" [level=1] [w1.10]
|
|
143
|
+
- desktop [fullscreen] [w0]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`--find TEXT` looks in every window that is not marked `[minimized]` (or in `--window W`) and prints only the elements whose name or value contains the text (case does not matter). Its output has a format of its own: a line `<window ref> <app id> "<title>":` for each window that has a match (`w0 desktop:` for the desktop), and under it one line per element. An element inside an ancestor that has a name is followed by `in` and the nearest such ancestor; one that has none is followed by nothing:
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
$ acde inspect --find milk
|
|
150
|
+
w1 notes "Notes":
|
|
151
|
+
- text "Buy milk" [w1.22] in list "Saved notes" [w1.7]
|
|
152
|
+
|
|
153
|
+
$ acde inspect --find save
|
|
154
|
+
w1 notes "Notes":
|
|
155
|
+
- button "Save" [w1.21]
|
|
156
|
+
- list "Saved notes" [w1.7]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
When nothing matches it says so on stderr (`acde: no element's name or value contains "milk"; elements scrolled out of view need --offscreen`) and exits with code 2. Elements scrolled out of view are found only with `--offscreen`; without it, a note under the tree says how many were left out (`(1 more outside the visible area; scroll, or add offscreen)`).
|
|
160
|
+
|
|
161
|
+
Actions: `click` `double` `right` `hover` `scroll` `type` `settext` `select` `key` `drag` `focus`; a window ref (`w1`) also accepts `close` `minimize` `restore` `fill` `fullscreen`. `fill` fills the desktop's work area with the window and `fullscreen` covers the whole host window with it; `restore` sets either back to normal. Both refuse the desktop (`the desktop cannot be made filled`, `... fullscreen`) and a window that already fills the host window (`w1 already fills the host window`), and need an acdert from 0.0.9 on. `minimize` on a window that cannot be minimized changes nothing, says why and exits with code 1; in a session that runs a single app, on macOS and headless, that is the app's own window: `w1 cannot be minimized: it fills the host window, which only the host window's own controls minimize here`. The argument to `key` looks like `"ctrl+a backspace"`; the argument to `drag` is the target ref. When closing a window ends acdert (the app's window when `acde dev` runs one app), `acde dev` ends with it; `act w1 close` then says so and exits with code 0.
|
|
162
|
+
|
|
163
|
+
#### Key names
|
|
164
|
+
|
|
165
|
+
The argument to `key` (and `press()` in tests) is one or more chords separated by spaces; a chord is keys joined by `+`, pressed in order and released in reverse. Keys are physical keys on a US layout. Case does not matter, except that a single capital letter (`A`) adds `shift`:
|
|
166
|
+
|
|
167
|
+
- modifiers: `ctrl` (`control`), `shift`, `alt`, `super` (`meta`, `win`)
|
|
168
|
+
- letters `a` … `z`, digits `0` … `9`, `f1` … `f12`
|
|
169
|
+
- `enter` (`return`), `escape` (`esc`), `tab`, `space`, `backspace`, `delete` (`del`), `insert`, `home`, `end`, `pageup`, `pagedown`, `up`, `down`, `left`, `right`, `menu`
|
|
170
|
+
- printable characters as they are: `-` `=` `[` `]` `;` `'` `` ` `` `\` `,` `.` `/`, and the shifted ones `!` `@` `#` `$` `%` `^` `&` `*` `(` `)` `_` `+` `{` `}` `|` `:` `"` `<` `>` `?` `~`, which add `shift` themselves (`*` is `shift+8`). `+` alone, or last in a chord (`ctrl++`), is the `+` key
|
|
171
|
+
- names for some characters: `plus` (`+`), `multiply`, `asterisk`, `star` (`*`), `minus` (`-`), `divide` (`/`), `period` (`.`), `comma` (`,`); these are the main keyboard keys, not the keypad
|
|
172
|
+
- the keypad: `Numpad0` … `Numpad9`, `NumpadAdd`, `NumpadSubtract`, `NumpadMultiply`, `NumpadDivide`, `NumpadDecimal`, `NumpadEnter`
|
|
173
|
+
- the DOM `KeyboardEvent.code` names of the other keys, e.g. `KeyA`, `Digit8`, `Minus`, `ArrowUp`, `CapsLock`, `ShiftRight`
|
|
174
|
+
|
|
175
|
+
The whole argument is checked before anything is pressed: an unknown key fails with `unknown key: …` and presses nothing. The space character is `space` (spaces separate chords). To enter text, use `type` instead.
|
|
176
|
+
|
|
177
|
+
### query
|
|
178
|
+
|
|
179
|
+
`inspect` lists what a user can see and use. `query` finds elements by CSS selector whether they are shown or not, and gives each a ref, so it reaches what `inspect` leaves out: an element that is `display: none`, one that is scrolled out of view, and one that has no role, name or value of its own (a plain `<div>`; `inspect` lists what is inside it in its place).
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
$ acde query 'form input[type=email]'
|
|
183
|
+
1 in w1:
|
|
184
|
+
- textbox "Email": "" [w1.20] <input>
|
|
185
|
+
|
|
186
|
+
$ acde query '[data-testid="menu"] li'
|
|
187
|
+
2 in w1:
|
|
188
|
+
- generic "Rename" [w1.25] <li> [hidden]
|
|
189
|
+
- generic "Delete" [w1.26] <li> [hidden]
|
|
190
|
+
|
|
191
|
+
$ acde query button
|
|
192
|
+
4 in w1:
|
|
193
|
+
- button "Save" [focused] [w1.21] <button>
|
|
194
|
+
- button "Undo" [transparent] [w1.10] <button> [hidden]
|
|
195
|
+
- button [invisible] [w1.24] <button> [hidden]
|
|
196
|
+
- button "Load more" [w1.11] <button>
|
|
197
|
+
|
|
198
|
+
$ acde query nav
|
|
199
|
+
acde: nothing in w1 matches "nav"
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Each line is the element's role, name, value, a heading's level, states and ref, exactly as `inspect` prints them (`- heading "Notes" [level=1] [w1.16] <h1>`), then its tag. `[hidden]` after the tag marks an element that is not visible: it has no size, or is `display: none`, hidden by `visibility`, or transparent, or its window is minimized. The `transparent` and `invisible` marks are the ones `inspect --all` gives: a transparent element still takes clicks, as it does for a user; for an `invisible` or `display: none` one, `act` answers `the element is not displayed` until the page shows it. An element whose role cannot be told is listed as `generic`; the tag says what it is.
|
|
203
|
+
|
|
204
|
+
The refs work with `act`, like the ones from `inspect`: `acde act w1.20 type "me@example.com"`. `query` searches the focused window; `--window w2` picks another, `--ref w1.6` searches only under that element, and `--limit N` lists at most N elements (1 to 3000, default 600; the first line still counts them all). When nothing matches, it says so on stderr (`acde: nothing in w1 matches "nav"`; in 0.0.9 on stdout, without `acde: `) and the exit code is 2; otherwise it is 0.
|
|
205
|
+
|
|
206
|
+
### Styles and layout
|
|
207
|
+
|
|
208
|
+
To look at styles and boxes, ask the page with `eval` (after 0.0.9, also the Elements panel of [DevTools](#devtools)). `getComputedStyle` gives the values the page uses after the CSS cascade, and `getBoundingClientRect` gives an element's box in CSS pixels of the page:
|
|
209
|
+
|
|
210
|
+
```sh
|
|
211
|
+
acde eval notes "getComputedStyle(document.querySelector('h1')).color"
|
|
212
|
+
acde eval notes "(({ display, fontSize, margin }) => ({ display, fontSize, margin }))(getComputedStyle(document.querySelector('.card')))"
|
|
213
|
+
acde eval notes "(({ x, y, width, height }) => ({ x, y, width, height }))(document.querySelector('.card').getBoundingClientRect())"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Copy the properties you want into a plain object, as above, so the JSON result has them. The code has only single quotes inside, so it is written the same way in sh, PowerShell and cmd (see [eval](#eval)). To see whether something is drawn where you expect, take a screenshot with `acde shot`; to find an element that `inspect` leaves out (no size, `display: none`), use `acde query`.
|
|
217
|
+
|
|
218
|
+
### console and network
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
acde console --level errors --follow
|
|
222
|
+
acde network --failed
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`--level` is `log`, `info`, `warn`, `error`, `debug`, or `errors` (`console.error` plus uncaught errors). `--follow` keeps printing new entries.
|
|
226
|
+
|
|
227
|
+
When a `fetch` in the page rejects with `TypeError: Failed to fetch`, the line of that request in `acde network --failed` says why (a CORS rule, a name that does not resolve, a refused connection):
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
$ acde network --failed
|
|
231
|
+
1 GET ERR https://nonexistent.invalid/ (fetch 158 ms; Failed to connect to host: Failed resolving hostname: failed to lookup address information: nodename nor servname provided, or not known) notes#1048576
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Entries stay after a page reloads, so errors from before a change are still listed. The first column is the entry's number. `--since reload` lists only what came after `acde dev` last rebuilt and reloaded (everything, if it has not reloaded yet); `--since N` lists entry `N` and later. A reload the page does by itself (`location.reload()`) does not count.
|
|
235
|
+
|
|
236
|
+
```sh
|
|
237
|
+
acde console --level errors --since reload
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The most recent 5000 entries of each kind are kept.
|
|
241
|
+
|
|
242
|
+
## DevTools
|
|
243
|
+
|
|
244
|
+
After 0.0.9, `acde devtools [web]` opens the DevTools of Chrome or Edge (or Chromium on Linux) on a page of the session: Elements, Console, Network, and Sources with breakpoints.
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
acde devtools # the focused page
|
|
248
|
+
acde devtools notes # a web name, an app id, desktop, or a window title
|
|
249
|
+
acde devtools services # the desktop's services (or one service, by its name)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
It prints the DevTools address on stdout, and on stderr `acde: opened DevTools for <page> in <browser>`. Asked again for a page whose DevTools are open, it brings them to the front instead (`acde: brought DevTools for <page> to the front in <browser>`). F12 in the host window of `acde dev` does the same for the page in that window; see [acde dev](dev.md#devtools).
|
|
253
|
+
|
|
254
|
+
The browser runs with a profile of acde's own, not your everyday one. `ACDE_BROWSER` names another browser (a path to it). Without Chrome or Edge, or with `ACDE_BROWSER=none`, nothing opens: paste the printed address into the address bar of Chrome or Edge on this machine.
|
|
255
|
+
|
|
256
|
+
Elements shows the element tree, the computed styles and the box model, read only. It does not list the CSS rules that matched an element (only its inline style); change styles from the page code or the Console.
|
|
257
|
+
|
|
258
|
+
## Without a display
|
|
259
|
+
|
|
260
|
+
On a machine without a display, such as a server over ssh, start the session with `acde dev --headless`. On Linux, `acde dev` without it stops at once when neither `WAYLAND_DISPLAY` nor `DISPLAY` is set and says to add `--headless`; see [acde dev](dev.md). Every command on this page works the same in a headless session:
|
|
261
|
+
|
|
262
|
+
```sh
|
|
263
|
+
acde dev --headless &
|
|
264
|
+
acde inspect # the windows and the focused window's elements, as text
|
|
265
|
+
acde shot notes # ./notes.png
|
|
266
|
+
acde screen # ./screen.png, every window together
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
A headless session has no picker dialogs: a folder, file or save picker the page opens fails unless it was answered beforehand, and the page sees it as cancelled. Answer it from a test with `chooseFolder`, `chooseFiles` or `chooseSaveFile` ([Testing](testing.md#network-file-pickers-clipboard)), from an agent with the MCP tools `choose_folder`, `choose_files` and `choose_save_file` ([MCP](mcp.md)), or (after 0.0.9) from the shell with `acde choose folder ~/Notes`, `acde choose open a.md b.md` or `acde choose save out.md` (`--cancel` to cancel). `acde drop <web> <file...>` (after 0.0.9) drops files onto a page, as a drag from the file manager would. The session output names the missing answer: `no folder chooser without a host window (ACDE_HEADLESS): answer it beforehand with the MCP tool choose_folder or acde.chooseFolder() in a test`. A headless session also has its own clipboard and no camera or microphone dialog (see below).
|
|
270
|
+
|
|
271
|
+
## Camera and microphone
|
|
272
|
+
|
|
273
|
+
A capability an app declares is granted without asking, except the camera and the microphone. In 0.0.9, an app that declares one is asked when it starts, and its window opens once the prompt is answered; after 0.0.9, the user is asked the first time a page asks for one (`getUserMedia`), and the call waits for the answer. In a session that runs a single app, the host window asks in a dialog on Windows, and after 0.0.9 also on macOS (a sheet with `Don't Allow` and `Allow`) and on Linux (a notification with the same two buttons, where the desktop's notification service shows buttons). Elsewhere, and in a headless session, there is no dialog, so answer from the command line:
|
|
274
|
+
|
|
275
|
+
```sh
|
|
276
|
+
$ acde permissions
|
|
277
|
+
pending:
|
|
278
|
+
1 recorder microphone (request)
|
|
279
|
+
decisions: none
|
|
280
|
+
|
|
281
|
+
$ acde allow
|
|
282
|
+
allowed recorder microphone (prompt 1)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Without arguments, `acde allow` and `acde deny` answer the only waiting prompt. When several wait, they answer none and list them; name one by its id (`acde allow 2`) or by app and capability (`acde allow recorder microphone`), or answer them all with `--all`.
|
|
286
|
+
|
|
287
|
+
Decisions persist per app (in `$XDG_CONFIG_HOME/acde/permissions.json`), so the user is not asked again. Answering is the user's decision: when you run these commands for someone, allow only what they asked for or agreed to, and tell them what you allowed. `acde revoke <app> <capability>` removes a decision, so the app is asked again the next time it asks. A denied or revoked capability makes `getUserMedia` reject with `NotAllowedError`, as in a browser.
|
|
288
|
+
|
|
289
|
+
## Errors
|
|
290
|
+
|
|
291
|
+
When a command fails, it prints `acde: <reason>` and exits with code 1. If the session has already exited, it says `acde dev <pid> is gone`. `inspect --find` and `query` that find nothing are not failures; they exit with code 2.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Windows
|
|
2
|
+
|
|
3
|
+
Each app window is a separate web page with its own DOM and JS heap; one window crashing does not affect the others. All windows of the same app share storage such as `localStorage`, IndexedDB, and cookies.
|
|
4
|
+
|
|
5
|
+
## Size and title bar
|
|
6
|
+
|
|
7
|
+
- In a desktop, a new window's content area is the configured `size` (800 × 600 by default) and opens centered; next time it opens where it was last closed.
|
|
8
|
+
- In an `acde dev` session that runs a single app (an app project without `--desktop`), the app **fills the host window**, and `size` sets how large that window opens: with `"size": [400, 300]` the host window's content area is 400 × 300. The title bar is drawn inside it, so the page is 400 × 268. Without `size` the host window takes most of the screen (90% of its width and 85% of its height, at most 1440 × 900).
|
|
9
|
+
- `acde dev --headless` has no host window, and `size` is not applied: the screen is 1440 × 900 (1280 × 800 in `acde test`) and the app fills it, so the page is 1440 × 868. To see the app in a window of its own `size`, run it in a desktop: `acde dev --desktop ./my-desktop`, or `--desktop dopame` once that desktop is installed (see [acde dev](dev.md#what-runs)).
|
|
10
|
+
- The title bar drawn by the system is **not part of the page's document**: `innerHeight`, `100vh`, and `resize` events only cover the content area. To drop the title bar, set `"frame": false` in the config, draw your own, and use `setGrabHint` to tell the system where the window can be dragged.
|
|
11
|
+
- The title comes from `document.title` or `window.setTitle()`, whichever was set last (a `<title>` in the page replaces a title set before the document loaded); if neither is set, `label` is used.
|
|
12
|
+
- Declare the colour schemes your page supports, as in a browser: `color-scheme: light dark` on the root (`:root { color-scheme: light dark }`) or `<meta name="color-scheme" content="light dark">` when it follows the system with `prefers-color-scheme`, `dark` when it is always dark. The system draws a dark title bar only for pages that name `dark`; a page that declares nothing is taken as light only and gets a light title bar even in a dark theme. The `html` template follows the system and declares `light dark`. In 0.0.9, an app run on its own (an `acde dev` of the app, `acde test`) always gets a light title bar, because only a desktop sets the title bar's colours. After 0.0.9, there the title bar is dark while the page is: a page that names only `dark` always, a page that names `light dark` while `prefers-color-scheme` is `dark` (the system's scheme, or `emulate({ colorScheme })` in a test). As in browsers, the root's `color-scheme` counts before the meta: an app with its own light / dark switch sets `color-scheme: dark` or `light` on the root, and the title bar follows it.
|
|
13
|
+
|
|
14
|
+
## acde:window
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { window as self } from 'acde:window'
|
|
18
|
+
|
|
19
|
+
self.setTitle('Untitled — Notes')
|
|
20
|
+
|
|
21
|
+
self.on('beforeclose', async () => {
|
|
22
|
+
if (!dirty) return true
|
|
23
|
+
return confirmDiscard() // return false to keep the window open
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> The module is named `window`, the same as the global `window`, so renaming it on import keeps things clear.
|
|
28
|
+
|
|
29
|
+
| Member | Does |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `id` | This window's id (a number) |
|
|
32
|
+
| `setTitle(title)` | Sets the title |
|
|
33
|
+
| `setMenus(menus)` | App menus (shown in the desktop's menu bar) |
|
|
34
|
+
| `close()` | Closes the window at once; `beforeclose` is not asked |
|
|
35
|
+
| `minimize()` | Minimizes the window, as `requestState('minimized')` does; the promise rejects with `status: 409`, `code: 'unavailable'` when the window cannot be minimized |
|
|
36
|
+
| `state` / `requestState(state)` | The window's state: `normal`, `minimized`, `filled` (fills the work area), `fullscreen`; see [Window state](#window-state) |
|
|
37
|
+
| `requestMode(mode)` | Whether the user can move and resize the window: `free` (both), `fixed` (move only), `pinned` (neither) |
|
|
38
|
+
| `output` | The screen the window is on: `{width, height, scale, workArea: {x, y, width, height}}` in logical pixels; `workArea` leaves out what the desktop's panels reserve. In a session that runs a single app the screen is the host window: with `"size": [400, 300]`, `output` and its `workArea` are both 400 × 300 |
|
|
39
|
+
| `requestAttention()` | Requests attention (for example a bouncing Dock icon; up to the desktop) |
|
|
40
|
+
| `requestActivate()` | Brings the window to the front and requests focus; the system may turn this into a request for attention |
|
|
41
|
+
| `setGrabHint(rect, edges?)` | Pressing in this area makes the system move the window (no `edges`) or resize it (with edges) |
|
|
42
|
+
| `setBackdrop(rect, effect?)` | Frosted glass over whatever is behind this area: `{radius, blur, saturate, brightness}`; `null` removes it |
|
|
43
|
+
| `requestGrab(kind)` / `releaseGrab(kind)` | Takes exclusive pointer or keyboard input (`'pointer'` / `'keyboard'`) |
|
|
44
|
+
| `shortcut(combo)` / `unshortcut(combo)` | Registers an app-level global shortcut, such as `'ctrl+shift+k'` |
|
|
45
|
+
| `hostControls` | `true` when the host system draws the window's minimize / maximize / close buttons, so a frameless app should hide its own |
|
|
46
|
+
| `minimizable` | `false` when this window cannot be minimized. In a session that runs a single app, on macOS and headless, the app's window is such a window: it fills the host window |
|
|
47
|
+
|
|
48
|
+
### Window state
|
|
49
|
+
|
|
50
|
+
Full screen is a **state** of the window, not a mode: `requestMode('fullscreen')` throws a `TypeError` (`requestMode: full screen is a window state, use requestState('fullscreen')`).
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
self.on('state', ({ state, previous }) => {
|
|
54
|
+
document.body.dataset.windowState = state
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
self.requestState('fullscreen') // 'normal' | 'minimized' | 'filled' | 'fullscreen'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- `requestState()` returns nothing. The system decides: when it refuses, nothing changes and no event arrives. A `fixed` window can only be minimized, and a `pinned` one cannot change state. A window whose `minimizable` is `false` cannot be minimized: `requestState('minimized')` changes nothing, and `minimize()` rejects.
|
|
61
|
+
- The state also changes without the page asking: the title bar's buttons, the desktop minimizing the window, the user bringing a minimized window back. Keep the UI in step with the `state` event (or `self.state`), not with what the page last requested.
|
|
62
|
+
- A minimized window that gets the focus again returns to the state it had before (`fullscreen`, for example).
|
|
63
|
+
- In a single-app `acde dev` session the app already fills the host window, and state requests may change nothing (headless on macOS, `minimized`, `filled` and `fullscreen` all leave the state `normal`, and no event arrives). Try states in a desktop session.
|
|
64
|
+
- The standard Fullscreen API (`element.requestFullscreen()`) is not supported; it rejects with `NotSupportedError`.
|
|
65
|
+
|
|
66
|
+
### Events
|
|
67
|
+
|
|
68
|
+
Subscribe with `on(name, handler)`, which returns an unsubscribe function. A name the module does not have throws a `TypeError` (`acde:window has no nosuch event (activate, deactivate, beforeclose, request, suspend, resume, grab, grablost, state, output, hostcontrols, minimizable, shortcut)`). Events are not dispatched on the global `window`.
|
|
69
|
+
|
|
70
|
+
| Event | When |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `activate` / `deactivate` | The window gains / loses focus |
|
|
73
|
+
| `state` | The window's state changed, `{state, previous}` |
|
|
74
|
+
| `output` | The screen or its work area changed; the handler gets the new `output` |
|
|
75
|
+
| `beforeclose` | The user or the desktop wants to close the window. Return `false` (or resolve to `false`) to keep it; if there is no answer within 3 seconds, it closes |
|
|
76
|
+
| `request` | Another app or the system asks this window to open something (see [Opening apps](open.md)) |
|
|
77
|
+
| `shortcut` | A registered shortcut was pressed, `{key}` |
|
|
78
|
+
| `grab` / `grablost` | Exclusive input was answered, `{kind, granted}` / taken back, `{kind: 'lost', reason}` |
|
|
79
|
+
| `hostcontrols` / `minimizable` | `hostControls` / `minimizable` changed; the handler gets the new value |
|
|
80
|
+
|
|
81
|
+
### Menus
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
self.setMenus([
|
|
85
|
+
{
|
|
86
|
+
id: 'file',
|
|
87
|
+
label: 'File',
|
|
88
|
+
items: [
|
|
89
|
+
{ id: 'new', label: 'New', shortcut: 'ctrl+n', onSelect: () => newDoc() },
|
|
90
|
+
{ type: 'separator' },
|
|
91
|
+
{ id: 'close', label: 'Close', onSelect: () => self.close() },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Call `setMenus` again with the whole list to change anything (a `checked` mark, a `disabled` item). Where the menus show:
|
|
98
|
+
|
|
99
|
+
- In a desktop, in its menu bar (the Dopame desktop shows the focused window's menus).
|
|
100
|
+
- In 0.0.9, an app run on its own (an `acde dev` of the app, `acde test`) shows them nowhere. After 0.0.9, it shows them on the title bar the system draws: the menu names after the window buttons, the window's title moved past them. A name opens its menu on a press; the arrow keys, Return and Escape work the open menu, and choosing an item runs its `onSelect`. An item without `onSelect` is shown disabled. `acde inspect` lists the menus first (`menuitem "File" [collapsed] [w1.m1]`, and the items of the open one, `w1.m1.2`), `acde act w1.m1.2 click` opens the menu and chooses the item, and in a test `getByRole('menuitem', { name: 'New' })` finds them. With `"frame": false` there is no title bar, so no menus either.
|
|
101
|
+
|
|
102
|
+
An item's `shortcut` (`'ctrl+n'`, the same names as `shortcut()`; after 0.0.9 also `mod`, which is ⌘ on macOS and Ctrl elsewhere) is shown next to it. In a desktop, and in 0.0.9 everywhere, it is only shown: to have the key work, register the combo with `self.shortcut()` as well and handle the `shortcut` event. After 0.0.9, an app run on its own has it bound too, while its window is focused: pressing it runs the item's `onSelect`, as long as the item has one and is not `disabled`. The page then gets no `keydown` for that combo (an `Edit` item with `ctrl+c` takes copy from text fields as well), and if the app also registered it with `self.shortcut()`, the item runs and no `shortcut` event comes, so it does not run twice.
|
|
103
|
+
|
|
104
|
+
## A second window of your app
|
|
105
|
+
|
|
106
|
+
An app opens another window of itself with `open` from `acde:open`, like any other app. It needs two things in its config: `multiple`, so that a second window is allowed, and its own id in the `open` capability:
|
|
107
|
+
|
|
108
|
+
```jsonc
|
|
109
|
+
{
|
|
110
|
+
"id": "notes",
|
|
111
|
+
"multiple": true,
|
|
112
|
+
"requests": ["edit-note"],
|
|
113
|
+
"capabilities": { "open": ["notes"] }
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
import { open } from 'acde:open'
|
|
119
|
+
import { window as self } from 'acde:window'
|
|
120
|
+
|
|
121
|
+
const { window } = await open('notes', { kind: 'edit-note', note: 'todo' })
|
|
122
|
+
|
|
123
|
+
self.on('request', (request) => {
|
|
124
|
+
if (request.kind === 'edit-note') load(request.note)
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- The new window runs the same `entry`; a request tells it what to show. Like any request it needs a `kind` that the app lists in its own `requests`; `open('notes')` with no request opens a plain window.
|
|
129
|
+
- Without `multiple`, `open('notes', …)` opens nothing new: it resolves with the window that already runs, and that window gets the request.
|
|
130
|
+
- Without `notes` in `open`, the call is rejected with 403 `forbidden` (`Notes may not use open notes (not in its manifest's capabilities)`), even though it is the app's own id.
|
|
131
|
+
- Pass `{ parent: self.id }` as the third argument to make the new window a child of this one (see [Opening apps](open.md#apps)).
|
|
132
|
+
- The windows share storage (`localStorage`, IndexedDB, cookies) but not memory: each is its own page.
|
|
133
|
+
|
|
134
|
+
## Browser APIs
|
|
135
|
+
|
|
136
|
+
| API | In acde |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| `window.close()` | Closes this window |
|
|
139
|
+
| `location.reload()` | Reloads this window |
|
|
140
|
+
| `alert()` | Drawn inside the window; `confirm()` / `prompt()` return `false` / `null` |
|
|
141
|
+
| External links | Handed to the browser app |
|
|
142
|
+
| Downloads | Saved to the Downloads folder |
|
|
143
|
+
| `Notification` | Shown by the desktop (a system notification when there is no desktop, except on Windows, where a single app shows none; `acde:capabilities` reports `notifications` `unsupported` there) |
|
|
144
|
+
| `visibilitychange` | The window is hidden / shown |
|
|
145
|
+
| `screen.*` | The screen's logical size |
|
|
146
|
+
|
|
147
|
+
## Module entry
|
|
148
|
+
|
|
149
|
+
`entry` can also be a module whose default export is a `WindowApp`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import type { WindowApp } from '@dopame/acde-api'
|
|
153
|
+
|
|
154
|
+
const app: WindowApp = {
|
|
155
|
+
mount(element, host) {
|
|
156
|
+
host.setTitle('Hello')
|
|
157
|
+
element.textContent = 'Hello from a module'
|
|
158
|
+
return () => { element.textContent = '' } // unmount
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
export default app
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Memory and hangs
|
|
165
|
+
|
|
166
|
+
- Each window's JS heap has a limit (`heapMb`, 256 MB by default). Exceeding it ends only that window.
|
|
167
|
+
- When a single task runs longer than 5 seconds, the desktop is told the window is "not responding" and can end it.
|