@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,40 @@
|
|
|
1
|
+
# Assets and icons
|
|
2
|
+
|
|
3
|
+
## public/
|
|
4
|
+
|
|
5
|
+
The project's `public/` directory (rename it with the `public` config field) is copied as is into the app's `web/`.
|
|
6
|
+
|
|
7
|
+
References in your pages that start with `/` and point to a file in public:
|
|
8
|
+
|
|
9
|
+
- In HTML: bundled as usual.
|
|
10
|
+
- `url(/x)` in CSS: kept as is when the project is an app whose entry is HTML, because the runtime also serves the app's directory at `/`. Elsewhere (a desktop, or the apps it bundles) it is bundled as usual.
|
|
11
|
+
- If the file can't be found anywhere, the build fails and tells you which URL in which file, and what's missing from public.
|
|
12
|
+
|
|
13
|
+
## Files your scripts refer to
|
|
14
|
+
|
|
15
|
+
A file that only your JavaScript names is bundled one of these ways:
|
|
16
|
+
|
|
17
|
+
- `import logo from './logo.png'`: `logo` is its URL in the built app.
|
|
18
|
+
- `new URL('./logo.png', import.meta.url)`: the file is copied and the URL points at it (with Node.js, Vite does this; with Bun, acde does it after 0.0.9). The path must be a literal that starts with `./` or `../` and names a file that exists; a module (`.js`, `.ts`, …) is left alone.
|
|
19
|
+
- A path in a string (`img.src = 'img/logo.png'`) is not seen by any bundler: put the file in `public/` and refer to it as `/img/logo.png`.
|
|
20
|
+
|
|
21
|
+
## URLs
|
|
22
|
+
|
|
23
|
+
App pages have the origin `http://acde.local`. An app's files are served both under `/app/<id>/` and at `/`, so `/logo.png` and relative paths both work.
|
|
24
|
+
|
|
25
|
+
## Icons
|
|
26
|
+
|
|
27
|
+
Set `icon` to an icon name or to an image in the project:
|
|
28
|
+
|
|
29
|
+
```jsonc
|
|
30
|
+
{ "icon": "chart" } // icon name
|
|
31
|
+
{ "icon": "assets/logo.svg" } // image in the project
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Icon names** refer to Dopame's built-in icon set, which matches the desktop's visual style:
|
|
35
|
+
|
|
36
|
+
`files` `browser` `editor` `office` `term` `projects` `dash` `team` `dopa` `settings` `store` `app` `timer` `cards` `list` `chart` `calc` `music` `video` `photo` `paint` `game` `chat` `mail` `calendar` `code` `book` `map` `weather`
|
|
37
|
+
|
|
38
|
+
If omitted, it's `app`.
|
|
39
|
+
|
|
40
|
+
**Images**: a value that contains `/` or `\`, or has an extension, is treated as a path relative to the project directory, and must be a `.svg` or `.png`. At build time it's copied to the app's `web/icon.<ext>`, and the manifest gets `/app/<id>/icon.<ext>`. `acde check` warns about any other value.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Backend
|
|
2
|
+
|
|
3
|
+
An app can have a backend: a module that exports `fetch`, or any Node.js / Bun server that calls `listen()` (Express, Hono, Fastify, ...). Pages reach it with a plain `fetch('/api/…')`.
|
|
4
|
+
|
|
5
|
+
App backends run on macOS, Linux and Windows. On Windows the runtime reaches the backend in another way; see [On Windows](#on-windows).
|
|
6
|
+
|
|
7
|
+
## Write a backend
|
|
8
|
+
|
|
9
|
+
The `server.ts` generated by `acde new my-app --backend`:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
export default {
|
|
13
|
+
fetch(request: Request): Response {
|
|
14
|
+
const url = new URL(request.url)
|
|
15
|
+
if (url.pathname === '/api/hello') return Response.json({ hello: 'world', at: new Date().toISOString() })
|
|
16
|
+
return new Response('not found', { status: 404 })
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
In the page:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const answer = await (await fetch('/api/hello')).json()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`export default { fetch }` works on both Bun and Node.js. On Node.js, acde's preload runs it with `node:http`; requests and responses are WHATWG `Request` / `Response` objects with streaming bodies. (There's no WebSocket upgrade on Node.js.)
|
|
28
|
+
|
|
29
|
+
You can also listen yourself:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { createServer } from 'node:http'
|
|
33
|
+
|
|
34
|
+
createServer((req, res) => res.end('hi')).listen(3000)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Discovery
|
|
38
|
+
|
|
39
|
+
Without a `backend` field, acde uses the first of these that exists:
|
|
40
|
+
|
|
41
|
+
`server.ts` `server.js` `src/server.ts` `src/server.js` `server/index.ts` `server/index.js` `backend/index.ts` `backend/index.js`
|
|
42
|
+
|
|
43
|
+
To set it explicitly, or turn it off:
|
|
44
|
+
|
|
45
|
+
```jsonc
|
|
46
|
+
{ "backend": { "entry": "api/main.ts", "port": 8080 } }
|
|
47
|
+
{ "backend": false }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`port` defaults to 3000; it's the port the backend thinks it's listening on.
|
|
51
|
+
|
|
52
|
+
## No TCP ports
|
|
53
|
+
|
|
54
|
+
An app's backend doesn't open TCP ports on the host. At build time, acde puts a preload (`acde-listen.js`) next to the backend that replaces `listen(P)` in `Bun.serve`, `Bun.listen`, and `net` / `http` / `https` / `tls` / `http2` with a unix socket at `<app's socket directory>/P.sock`. If no port is given, it uses `PORT` / `BUN_PORT` / `NODE_PORT`, and otherwise 3000.
|
|
55
|
+
|
|
56
|
+
As a result:
|
|
57
|
+
|
|
58
|
+
- Two apps can both `listen(3000)` without conflict.
|
|
59
|
+
- Requests from the page to same-origin paths (`/api/x`) or to `http://localhost:<port>/…` (and `127.0.0.1`, `[::1]`, `0.0.0.0`) all go to the app's own backend.
|
|
60
|
+
- UDP `bind` to a specific port is rejected (`EACCES`); ports opened by native extensions on their own aren't covered.
|
|
61
|
+
|
|
62
|
+
## On Windows
|
|
63
|
+
|
|
64
|
+
The same build runs on Windows, and there too the backend opens no TCP port and no named pipe. How the runtime reaches it depends on what runs it:
|
|
65
|
+
|
|
66
|
+
- **Bun**: as on macOS and Linux, `listen(P)` becomes a socket file `P.sock`. Its directory (`ACDE_APP_SOCKETS`) is made anew each time the backend starts, and only the current user and the system can open it.
|
|
67
|
+
- **Node.js**: Node.js cannot listen on a socket file on Windows, so nothing listens at all. The preload keeps the server that called `listen(P)`, and the runtime hands it every connection (HTTP requests and WebSocket upgrades) through a private pipe to the backend process. The backend gets `ACDE_APP_CHANNEL=fd` and no `ACDE_APP_SOCKETS`. A backend built by an acde from before this is not started on Windows; the log says `not started: acde-listen.js does not support the Windows channel (built by an older acde: acde build it again)`.
|
|
68
|
+
|
|
69
|
+
To stop a backend on Windows the runtime asks it to shut down: it takes no new connections, finishes the requests in progress and exits; after 3 seconds the runtime ends it together with the processes it started.
|
|
70
|
+
|
|
71
|
+
## Bundling
|
|
72
|
+
|
|
73
|
+
`acde build` bundles the backend into a single ES module, `backend/server.js` (target node): npm packages are bundled in, while Node built-in modules and `bun` / `bun:*` stay as imports. The same output runs on both Bun and Node.js.
|
|
74
|
+
|
|
75
|
+
## Running
|
|
76
|
+
|
|
77
|
+
- The backend starts **when the app's first window opens** and stops **when its last window closes** (on macOS and Linux with SIGTERM, then SIGKILL after 3 seconds; for Windows see [above](#on-windows)). After the 4th crash within a minute, it's no longer restarted automatically.
|
|
78
|
+
- It runs on Bun if `bun` is on PATH, otherwise on Node.js (≥ 22.3). `acde dev` picks based on the project's JavaScript runtime.
|
|
79
|
+
- The environment is built from scratch: `ACDE_APP_ID`, `ACDE_APP_DATA`, `ACDE_APP_SOCKETS`, `PORT`, `NODE_ENV=production`, plus the session's `HOME`, `PATH`, `LANG`, and so on, and the proxy variables below. Nothing else is inherited from your terminal.
|
|
80
|
+
- Proxy settings are passed on. If the session sets any of `http_proxy`, `https_proxy` or `all_proxy`, the backend gets `http_proxy`, `https_proxy`, `all_proxy` and `no_proxy` as the session has them (on macOS and Linux under both the lower-case and the upper-case names, as set; on Windows, where variable names ignore case, under the upper-case names). If none is set, the backend gets the system's manual proxy, the same settings pages use. Only Windows and macOS have one: on Windows the HTTP proxy from the user's Internet settings (a `socks=` entry becomes `all_proxy=socks4a://host:port`), set under the upper-case names; on macOS the HTTP, HTTPS and SOCKS proxies from the network settings (SOCKS becomes `socks5h://host:port`), set under both the lower-case and the upper-case names. `no_proxy` then holds the session's `no_proxy`, the system's exceptions in `no_proxy` form (`*.example.com` becomes `.example.com`, `192.168.*` becomes `192.168.0.0/16`; other wildcards, Windows' `<local>` and macOS's "exclude simple hostnames" have no `no_proxy` form and are left out), and `localhost,127.0.0.1,::1`. A proxy auto-config script or automatic proxy discovery answers per URL and can't be expressed as variables, so with those the backend gets no system proxy (on Windows, "Automatically detect settings" alone still passes the manual proxy). The settings are read each time a backend starts. Linux has no system proxy setting to read: only the session's variables count there. Your HTTP client still has to honour these variables (Bun's `fetch` and many npm clients do; Node.js's built-in `fetch` does not by default).
|
|
81
|
+
- The working directory is `$XDG_DATA_HOME/acde/apps/<id>/`; the app can keep its own data here.
|
|
82
|
+
- On macOS and Linux the sockets are in a directory of the session's runtime (`ACDE_APP_SOCKETS`). In 0.0.9 it is one directory per app id, `<runtime dir>/acde/apps/<id>`, shared by every session: running `acde test` while `acde dev` runs the same app leaves the dev session's backend unreachable (`cannot connect to …/3000.sock: Connection refused`) until `acde dev` is restarted. After 0.0.9, each session has its own, `<runtime pid>/<id>` in acde's runtime directory, removed when the backend stops: `$TMPDIR/acde-<uid>` on macOS, and `acde/` in `XDG_RUNTIME_DIR` (`/run/user/<uid>` on Linux when it is not set). A socket path longer than the system allows (104 bytes on macOS, 108 on Linux) is not started; the log says so.
|
|
83
|
+
- Backend output goes to the runtime's log, prefixed with `[<id>#backend]`. `acde dev` prints it in your terminal.
|
|
84
|
+
|
|
85
|
+
## Running it directly
|
|
86
|
+
|
|
87
|
+
To debug the built backend on its own, without the runtime, give it a socket directory and load the preload that `acde build` put next to it (the commands are for macOS and Linux):
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
acde build
|
|
91
|
+
cd .acde/backend
|
|
92
|
+
ACDE_APP_SOCKETS=/tmp/my-app-sockets bun --preload ./acde-listen.js server.js
|
|
93
|
+
curl --unix-socket /tmp/my-app-sockets/3000.sock http://localhost/api/hello
|
|
94
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Capabilities
|
|
2
|
+
|
|
3
|
+
Pages only get the standard Web APIs. There is exactly one way into system capabilities: the built-in `acde:` modules, imported the same way as `node:fs`:
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import { fs } from 'acde:fs'
|
|
7
|
+
import { window } from 'acde:window'
|
|
8
|
+
import fs2 from 'acde:fs' // the default export is the same object
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
There is no `acde` global, and the runtime never attaches anything non-standard to `window`.
|
|
12
|
+
|
|
13
|
+
Talking to other hosts needs no capability: `fetch`, `WebSocket`, and the other standard APIs work as usual, through the system's proxy when one is set. See [Web compatibility](../reference/web.md#network).
|
|
14
|
+
|
|
15
|
+
## Requesting capabilities
|
|
16
|
+
|
|
17
|
+
The `capabilities` in your config decide which modules an app can import:
|
|
18
|
+
|
|
19
|
+
```jsonc
|
|
20
|
+
{
|
|
21
|
+
"capabilities": {
|
|
22
|
+
"fs": true,
|
|
23
|
+
"openFiles": true,
|
|
24
|
+
"open": ["notes", "viewer"]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Capability | Grants |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `fs` | `acde:fs`. `true`: the app's private folder and the folders the user picks for it. `{ "roots": ["downloads", … ] }` adds shared places; see [Files and folders](files.md) |
|
|
32
|
+
| `terminal` | `acde:terminal`: pseudo-terminals |
|
|
33
|
+
| `network` | `acde:network`: network settings, Wi-Fi (Linux only, including Dopame OS) |
|
|
34
|
+
| `office` | `acde:office`: office document service (Linux only) |
|
|
35
|
+
| `open` | Opening other apps with `acde:open`. `true` for any app, or a list of app ids |
|
|
36
|
+
| `openFiles` | `acde:openPath` / `acde:handlers`, and `open(request)` without a target app: a file or a link goes to its default app |
|
|
37
|
+
| `launch` | `start` in `acde:launch`: start native programs |
|
|
38
|
+
| `slots` | `embed` in `acde:launch`: native programs embedded in your own window (Dopame OS only) |
|
|
39
|
+
| `camera` / `microphone` | `navigator.mediaDevices.getUserMedia()` video / audio (not on Windows); the user is asked, see [below](#camera-and-microphone) |
|
|
40
|
+
|
|
41
|
+
Every app can import these without requesting anything: `acde:window` (its own window), `acde:capabilities` (what it has, see [below](#acdecapabilities)), `acde:apps` (`list()` and `open()`), and `acde:open` (opening another app still needs `open`).
|
|
42
|
+
|
|
43
|
+
A name that is not in the table is an error in `acde check`:
|
|
44
|
+
|
|
45
|
+
- **Desktop services** are listed under `services`, not under `capabilities`: `"services": ["clock"]`, then `import { clock } from 'acde:clock'`. The app then starts only in a desktop that runs the service. See [Services](services.md).
|
|
46
|
+
- **Shell modules** (`acde:windows`, `acde:packages`, `acde:session`, `acde:desktops`, `acde:inspect`, `acde:desktop`, `acde:setDefault`, `acde:permissions`) belong to the desktop. Only the desktop page, its services, and the apps bundled with a desktop (system apps) get them; see [Desktops](desktop.md#system-apps).
|
|
47
|
+
|
|
48
|
+
Importing a module the app has no right to fails with a `TypeError` that names the missing declaration, and `acde check` and `acde build` report the same thing for every `import` in your source before anything runs:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
error: app "notes": capability "clock" is not a system capability; list desktop services under services
|
|
52
|
+
error: src/main.js:1: app "notes": acde:terminal needs "terminal" in the capabilities of Notes
|
|
53
|
+
error: src/main.js:2: app "notes": acde:windows is a shell module: only the desktop, its services and system apps may import it, and Notes is not a system app
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Calling a method the app has no right to is rejected with `status: 403` (`code: 'forbidden'`), for example `open('mail')` when `open` lists only other apps.
|
|
57
|
+
|
|
58
|
+
## Camera and microphone
|
|
59
|
+
|
|
60
|
+
Declaring a capability is what gives the app access to it: nothing is asked, also for shared places in `fs.roots`, `terminal`, `network`, `launch` and `slots`. Only the camera and the microphone ask the user. The apps bundled with a desktop (system apps) are not asked.
|
|
61
|
+
|
|
62
|
+
- **When**: the first time a page calls `navigator.mediaDevices.getUserMedia()` (or `request()`, below); the call waits for the answer. In 0.0.9, a camera or microphone the app requires (not in `optional`) is asked for when the app starts, and the window opens after the answer; after 0.0.9, it is asked for on first use too.
|
|
63
|
+
- **Allowed**: the decision is stored per app (`$XDG_CONFIG_HOME/acde/permissions.json`) and the user is not asked again.
|
|
64
|
+
- **Denied**: `getUserMedia()` rejects with `NotAllowedError`, as in a browser. In 0.0.9, denying a required one keeps the app from starting (`Notes: "camera" is denied by the user`, 403 `denied`) and takes it off the app list; after 0.0.9, the app keeps running and stays in the list.
|
|
65
|
+
- **Who asks**: in a desktop, the desktop shows the prompt (see [Desktops](desktop.md#camera-and-microphone-prompts)). In the host window of a single app, Windows asks in a dialog; in 0.0.9, macOS and Linux show nothing. After 0.0.9, a macOS host window asks in a sheet (`Allow` / `Don't Allow`) and a Linux host window in a desktop notification with Allow and Deny (without a notification service, nothing shows).
|
|
66
|
+
- **In a dev session**: answer with `acde allow` or `acde deny` (also when nothing shows, and headless), take a decision back with `acde revoke`; see [Debugging](tools.md#camera-and-microphone). Tests answer with `acde.allow()` (see [Testing](testing.md#approvals)).
|
|
67
|
+
|
|
68
|
+
## Optional capabilities
|
|
69
|
+
|
|
70
|
+
Everything in `capabilities` is required by default: if the platform lacks one, the app does not start. List the ones the app can run without in `optional`:
|
|
71
|
+
|
|
72
|
+
```jsonc
|
|
73
|
+
{
|
|
74
|
+
"capabilities": { "terminal": true },
|
|
75
|
+
"optional": ["terminal"]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- The app also starts on a platform that lacks the capability.
|
|
80
|
+
- An optional module must not be imported statically (`acde check`: `acde:terminal is optional ("terminal" is in optional): reach it with load('terminal') from @dopame/acde-api instead of importing it`). Get it with `load()`, which resolves to `null` while the module is unavailable.
|
|
81
|
+
- `camera` and `microphone` have no module to load: the page calls `navigator.mediaDevices.getUserMedia()`, which asks the user the first time. See [Web compatibility](../reference/web.md#media).
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
import { load } from '@dopame/acde-api'
|
|
85
|
+
|
|
86
|
+
const terminal = await load('terminal') // null where this platform lacks it
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Making `fs` optional makes its shared places optional too.
|
|
90
|
+
|
|
91
|
+
## acde:capabilities
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
import { capabilities } from 'acde:capabilities'
|
|
95
|
+
|
|
96
|
+
capabilities.level // 1: the contract level of the runtime
|
|
97
|
+
capabilities.platform // 'linux' | 'darwin' | 'win32'
|
|
98
|
+
capabilities.mode // 'session' (Dopame OS) | 'desktop' (a desktop in the host window) | 'standalone' (a single app in the host window)
|
|
99
|
+
capabilities.get('terminal') // { name, kind: 'system' | 'service', state, reason, optional }
|
|
100
|
+
capabilities.list() // the same for every system capability and shell module, notifications, and each fs.<place> and service the app declares
|
|
101
|
+
await capabilities.request('camera') // asks the user for the camera or microphone; resolves with the status
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| `state` | Meaning |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `available` | Declared and this platform has it (camera, microphone: and the user allowed it) |
|
|
107
|
+
| `not-asked` | Camera or microphone, and the user was not asked yet: `request()` asks |
|
|
108
|
+
| `asking` | The camera or microphone prompt waits for the user's answer |
|
|
109
|
+
| `not-declared` | Not in the app's `capabilities` / `services` |
|
|
110
|
+
| `unsupported` | This platform, or this way of running, lacks it |
|
|
111
|
+
| `service-not-running` | The desktop service is not running |
|
|
112
|
+
| `shell-only` | A shell module, and the app is not a system app |
|
|
113
|
+
| `denied` | Camera or microphone: the user denied it, or the decision was taken back |
|
|
114
|
+
|
|
115
|
+
`request()` of any other capability, or of one the user already answered, resolves at once with the current status.
|
|
116
|
+
|
|
117
|
+
`notifications` is the standard `Notification` API, which needs no declaration: it is `available`, or `unsupported` where this way of running shows no notifications (a single app on Windows).
|
|
118
|
+
|
|
119
|
+
## Types
|
|
120
|
+
|
|
121
|
+
Types for every module ship in the SDK, `@dopame/acde-api`. Reference the entry point that fits the project and your editor understands the `acde:*` modules it can import:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
/// <reference types="@dopame/acde-api/app" />
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- `@dopame/acde-api/app`: the modules of an app (`acde:fs`, `acde:window`, `acde:open`, ...).
|
|
128
|
+
- `@dopame/acde-api/shell`: those plus the shell modules, for a desktop and its bundled apps.
|
|
129
|
+
- `@dopame/acde-api`: all of them.
|
|
130
|
+
|
|
131
|
+
## In a browser
|
|
132
|
+
|
|
133
|
+
To preview the same code in an ordinary browser:
|
|
134
|
+
|
|
135
|
+
- In a Vite project, add the `@dopame/acde-api/vite` plugin; the dev server then stubs `acde:*`. See [Frameworks](frameworks.md#vite-plugin).
|
|
136
|
+
- Or load modules on demand with `load()`, which returns `null` when the module is unavailable:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
import { load } from '@dopame/acde-api'
|
|
140
|
+
|
|
141
|
+
const fs = await load('fs')
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Modules
|
|
145
|
+
|
|
146
|
+
| Module | Purpose | Docs |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| `acde:window` | Your own window: title, menus, closing, state, shortcuts, events | [Windows](window.md) |
|
|
149
|
+
| `acde:capabilities` | What the app has, asking for optional capabilities | [Above](#acdecapabilities) |
|
|
150
|
+
| `acde:fs` | Files | [Files and folders](files.md) |
|
|
151
|
+
| `acde:apps` `acde:open` `acde:openPath` `acde:handlers` | Other apps, opening files and links | [Opening apps](open.md) |
|
|
152
|
+
| `acde:launch` | Native programs | [Reference](../reference/modules.md#acdelaunch) |
|
|
153
|
+
| `acde:terminal` | Pseudo-terminals | [Reference](../reference/modules.md#acdeterminal) |
|
|
154
|
+
| `acde:network` | Networking | [Reference](../reference/modules.md#acdenetwork) |
|
|
155
|
+
| `acde:windows` `acde:packages` `acde:desktop` `acde:inspect` `acde:permissions` `acde:session` `acde:desktops` `acde:setDefault` | Shell modules: the desktop and system apps | [Desktops](desktop.md) |
|
|
156
|
+
|
|
157
|
+
For full signatures, see [Reference: acde: modules](../reference/modules.md).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
A project's config lives in `acde.config.jsonc` at its root (`acde.config.json` also works). JSONC allows comments and trailing commas. A `$schema` at the top gives your editor completion and validation:
|
|
4
|
+
|
|
5
|
+
```jsonc
|
|
6
|
+
{
|
|
7
|
+
"$schema": "./node_modules/@dopame/acde-api/config.schema.json",
|
|
8
|
+
"type": "app",
|
|
9
|
+
"id": "notes",
|
|
10
|
+
"label": "Notes",
|
|
11
|
+
"icon": "editor",
|
|
12
|
+
"entry": "index.html",
|
|
13
|
+
"size": [960, 640],
|
|
14
|
+
"capabilities": { "fs": true }
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Apps (`"type": "app"`)
|
|
19
|
+
|
|
20
|
+
| Field | Description |
|
|
21
|
+
| --- | --- |
|
|
22
|
+
| `id` | App id: lowercase letters, digits, and `-`. Defaults to one derived from `name` in `package.json` |
|
|
23
|
+
| `label` | Display name, shown in the title bar and on the desktop |
|
|
24
|
+
| `icon` | An icon name, or a `.svg` / `.png` in the project; see [Assets and icons](assets.md) |
|
|
25
|
+
| `entry` | Entry point: an HTML page (default `index.html`), or a module whose default export is a `WindowApp`. Omit it when building with a framework |
|
|
26
|
+
| `framework` | How to build; defaults to `auto`. See [Frameworks](frameworks.md) |
|
|
27
|
+
| `capabilities` | System capabilities the app requests, such as `{ "fs": true }`; see [Capabilities](capabilities.md). Declaring one grants it; the camera and the microphone also ask the user (see [Camera and microphone](capabilities.md#camera-and-microphone)) |
|
|
28
|
+
| `optional` | Names from `capabilities` that the app can run without; the app starts where this platform lacks them, and reaches them with `load()`. See [Optional capabilities](capabilities.md#optional-capabilities) |
|
|
29
|
+
| `services` | Names of the [desktop services](services.md) the app uses, e.g. `["clock"]`. The app starts only where they run |
|
|
30
|
+
| `size` | Default size of the window's content area, `[width, height]` in logical pixels; defaults to `[800, 600]`. The title bar is not included. A desktop opens the window at this size; a single app run by `acde dev` fills the host window, which opens at this size (see [Windows](window.md#size-and-title-bar)) |
|
|
31
|
+
| `frame` | Whether the window has a system title bar; defaults to `true` |
|
|
32
|
+
| `multiple` | Whether multiple windows can be open at once |
|
|
33
|
+
| `mimeTypes` | File types the app can open, e.g. `["text/markdown", "image/*"]`; see [Opening apps](open.md#files-and-links) |
|
|
34
|
+
| `schemes` | URL schemes of the links the app can open, lowercase and without the colon, e.g. `["mailto"]` |
|
|
35
|
+
| `requests` | Kinds of request the app accepts from other apps' `open()`, e.g. `["show-image"]`; see [Opening apps](open.md#apps) |
|
|
36
|
+
| `heapMb` | JS heap limit per window, in MB; defaults to 256, minimum 32 |
|
|
37
|
+
| `backend` | The backend: `false` for none, or `{ "entry": "server.ts", "port": 3000 }`. Detected automatically by default; see [Backend](backend.md) |
|
|
38
|
+
| `public` | Directory of static files copied as is; defaults to `public` |
|
|
39
|
+
| `name` / `version` | Package name and version; taken from `package.json` by default |
|
|
40
|
+
| `apiLevel` | The contract level the package is built for. Leave it out: `acde build` writes the level of the installed SDK |
|
|
41
|
+
|
|
42
|
+
## Desktops (`"type": "desktop"`)
|
|
43
|
+
|
|
44
|
+
| Field | Description |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `id` / `label` / `description` | The desktop's id, name, and description |
|
|
47
|
+
| `entry` | The desktop page; defaults to `index.html` |
|
|
48
|
+
| `heapMb` | JS heap limit for the desktop page; defaults to 1024 |
|
|
49
|
+
| `services` | Desktop services: name → module path, or name → `{ "module": path, "modules": [shell modules] }` for a service that uses shell modules; see [Services](services.md) |
|
|
50
|
+
| `apps` | Apps bundled with this desktop (system apps). Each entry has the same fields as an app, but `id` and `entry` are required; its `capabilities` may also name shell modules, such as `"windows": true` |
|
|
51
|
+
|
|
52
|
+
See [Desktops](desktop.md).
|
|
53
|
+
|
|
54
|
+
## Check
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
acde check
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This checks that the id is valid, the entry file exists, the icon is a known name or an existing image, service modules exist, and the framework is recognized. It also checks the declarations against the rules of the runtime: capability names, the shared places in `fs.roots`, `optional`, `services`, and every `acde:` import in the source against what the app declares. Errors make the command exit with 1; warnings and notes are only printed.
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
error: app "notes": fs.roots "work" is not a shared place (desktop, documents, downloads, music, pictures, videos); any other folder is one the user picks (showDirectoryPicker() with "fs": true)
|
|
64
|
+
error: app "notes": capability "clock" is not a system capability; list desktop services under services
|
|
65
|
+
error: src/main.js:1: app "notes": acde:terminal needs "terminal" in the capabilities of Notes
|
|
66
|
+
error: src/main.js:2: app "notes": acde:windows is a shell module: only the desktop, its services and system apps may import it, and Notes is not a system app
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A path in `fs.roots` is answered with the name to write instead: `fs.roots "~/Documents" is not a shared place: write "documents", the runtime finds that folder through the system (also when it was moved)`. An `optional` name that is not in `capabilities` is reported first, on its own: `error: app notes: optional "launch" is not in capabilities (optional lists declared capabilities that the app can run without)`. That line really names the app without quotes, unlike the ones above; match either form when you read these lines by program.
|
|
70
|
+
|
|
71
|
+
`acde check --platform windows` (or `macos`, `linux`) adds what that platform lacks: a required capability it does not have is an error, an optional one a note.
|
|
72
|
+
|
|
73
|
+
For the full field definitions, see [Reference: acde.config.jsonc](../reference/config.md).
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Desktops
|
|
2
|
+
|
|
3
|
+
The Dopame OS desktop is itself an acde project: a web page that fills the screen behind every window. A desktop can import every `acde:` module this platform has except `acde:window` (which is for app windows), including the shell modules that apps do not get, and is responsible for the "system UI": the Dock, the menu bar, window switching, notifications, camera and microphone prompts, and so on. The desktop that ships with Dopame is called `dopame`; you can build your own.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
acde new my-desktop --template desktop
|
|
7
|
+
cd my-desktop
|
|
8
|
+
acde dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
```jsonc
|
|
14
|
+
{
|
|
15
|
+
"$schema": "./node_modules/@dopame/acde-api/config.schema.json",
|
|
16
|
+
"type": "desktop",
|
|
17
|
+
"id": "my-desktop",
|
|
18
|
+
"label": "My Desktop",
|
|
19
|
+
"entry": "index.html",
|
|
20
|
+
"services": { "clock": "services/clock.ts" },
|
|
21
|
+
"apps": [
|
|
22
|
+
{ "id": "hello", "label": "Hello", "icon": "app", "entry": "apps/hello/index.html" }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `services`: [desktop services](services.md) running in the background, name → module.
|
|
28
|
+
- `apps`: apps that ship with this desktop, with the same fields as an app project (`id` and `entry` are required). They are [system apps](#system-apps).
|
|
29
|
+
|
|
30
|
+
For editor types in a desktop project, reference the shell entry point of the SDK: `/// <reference types="@dopame/acde-api/shell" />`.
|
|
31
|
+
|
|
32
|
+
## Apps
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { apps } from 'acde:apps'
|
|
36
|
+
|
|
37
|
+
for (const app of await apps.list()) {
|
|
38
|
+
const button = document.createElement('button')
|
|
39
|
+
button.textContent = app.label
|
|
40
|
+
button.onclick = () => apps.open(app.id)
|
|
41
|
+
dock.append(button)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
apps.on('changed', ({ apps }) => render(apps))
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`app.icon` is an icon name or an image URL; the desktop decides how to draw it. An app that cannot run here is not in the list: one that requires a capability this platform lacks. `changed` fires with the new list when it changes.
|
|
48
|
+
|
|
49
|
+
`apps.open(id)` rejects when the app cannot start: with `status: 404`, `code: 'unsupported_app'` for an app that requires what this platform lacks (`Viewer needs "network" (not available on macOS host windows); list it in "optional" to run without it`), and with `status: 503`, `code: 'service_not_running'` for one whose service is not running (`Viewer needs "clock" (service not running)`). In 0.0.9, an app whose required camera or microphone the user denied also rejects, with `status: 403`, `code: 'denied'` (`Viewer: "camera" is denied by the user`); after 0.0.9 it starts. The desktop also imports `acde:openPath`, `acde:handlers`, and `acde:setDefault` (see [Opening apps](open.md#files-and-links)). Installing packages and listing native programs are in `acde:packages` (`refresh`, `install`, `natives`, `runNative`).
|
|
50
|
+
|
|
51
|
+
## Windows
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { windows } from 'acde:windows'
|
|
55
|
+
|
|
56
|
+
windows.on('open', (e) => addToTaskbar(e.window))
|
|
57
|
+
windows.on('close', (e) => removeFromTaskbar(e.window))
|
|
58
|
+
windows.on('state', (e) => markMinimized(e.window, e.state === 'minimized'))
|
|
59
|
+
|
|
60
|
+
await windows.focus(id)
|
|
61
|
+
await windows.requestState(id, 'minimized')
|
|
62
|
+
await windows.animate(id, { opacity: 0, scale: 0.9, duration: 180, easing: 'ease-out' })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Window events: `open` `close` `title` `state` `activate` `minsize` `parent` `menus` `crash` `hang` `responsive` `attention` `ready` `show` `animated`. Any other name throws a `TypeError`. Each event is `{type: 'window<name>', window, …}`.
|
|
66
|
+
|
|
67
|
+
A window's state is `normal`, `minimized`, `filled`, or `fullscreen`. The runtime carries out state changes itself, whether the app asked (`minimize()`, `requestState()`), the user pressed a title bar button, or the desktop called `windows.requestState(id, state)`; the desktop learns of every change from the `state` event (`{window, state, previous}`). `windows.list()` gives every window with its current `state`. `windows.focus(id)` or `windows.show(id)` on a minimized window brings it back in the state it had before. Hiding (`hide` / `show`) is separate from the state and only the desktop can do it. A window that hangs (`hang`) can be ended with `kill`; `close(id)` asks the app first (its `beforeclose`).
|
|
68
|
+
|
|
69
|
+
## Panels
|
|
70
|
+
|
|
71
|
+
For things that must float **above** app windows, such as the Dock, the menu bar, and popovers, use `pin` to turn an element of the desktop page into its own always-on-top window:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
import { desktop } from 'acde:desktop'
|
|
75
|
+
|
|
76
|
+
const panel = desktop.pin(document.querySelector('#dock'), { margin: 8, reserve: { edge: 'bottom' } })
|
|
77
|
+
// The system re-measures automatically when the element changes; you can also do it by hand:
|
|
78
|
+
panel.update()
|
|
79
|
+
panel.reserve({ edge: 'bottom', thickness: 64 }) // or null to reserve nothing
|
|
80
|
+
panel.unpin()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The element stays in the same document, so its state, React tree, and events work as usual. The element must be a stacking context (for example, `position` plus `z-index`, or `isolation: isolate`); its `backdrop-filter` becomes real frosted glass across windows.
|
|
84
|
+
|
|
85
|
+
`reserve` keeps a strip along one edge of the screen (`top`, `bottom`, `left`, `right`) free of filled windows: the work area shrinks by it, `filled` windows follow, and apps see the new `workArea` in `acde:window`'s `output`. Without `thickness` the strip reaches from the edge to the far side of the panel. A panel without `reserve` takes no space.
|
|
86
|
+
|
|
87
|
+
## Title bars
|
|
88
|
+
|
|
89
|
+
The runtime draws the title bar of every app window; the desktop sets how it looks and behaves:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
desktop.setDoubleClick('fill') // double-clicking a title bar: 'fill' | 'minimize' | 'none'
|
|
93
|
+
desktop.setFrameTheme({
|
|
94
|
+
active: { background: '#202030', title: '#ffffff' },
|
|
95
|
+
inactive: { background: '#2a2a35', title: '#a0a0b0' },
|
|
96
|
+
buttonStyle: 'glyphs', // or 'dots'
|
|
97
|
+
buttonSide: 'right', // or 'left'
|
|
98
|
+
titleAlign: 'start', // or 'center'
|
|
99
|
+
})
|
|
100
|
+
desktop.setFrameTheme(null) // back to the default look
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Colors are `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()` or `rgba()`; each of `active` and `inactive` takes `background`, `title`, `line`, and `buttons`. Add `light: { active, inactive }` for the windows whose page does not declare dark support (no `dark` in its `color-scheme`, see [Windows](window.md#size-and-title-bar)); `active` and `inactive` are then for pages that do. Every call gives the whole theme. A bad value throws a `TypeError`; an unknown key is ignored, with a warning in the log.
|
|
104
|
+
|
|
105
|
+
## Notifications, downloads, and shortcuts
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
desktop.on('notification', (n) => toast(n.title, n.body, n.icon))
|
|
109
|
+
desktop.on('download', (d) => showDownload(d.name, d.path))
|
|
110
|
+
desktop.on('edge', ({ edge, entered }) => { if (edge === 'top') menuBar.hidden = !entered })
|
|
111
|
+
|
|
112
|
+
await windows.shortcut('ctrl+alt+t', 'desktop')
|
|
113
|
+
desktop.on('shortcut', ({ key }) => { if (key === 'ctrl+alt+t') apps.open('term') })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Shortcut scopes: `session` (highest, cannot be overridden) → app-level shortcuts of the focused window → `desktop`. On conflicts, the first registration wins.
|
|
117
|
+
|
|
118
|
+
`edge` fires when the pointer reaches (`entered: true`) or leaves an edge of the screen (`top`, `right`, `bottom`, `left`), without taking focus: a desktop that hides its menu bar or Dock over a full-screen window shows them again with it.
|
|
119
|
+
|
|
120
|
+
## Camera and microphone prompts
|
|
121
|
+
|
|
122
|
+
The camera and the microphone ask the user the first time an app uses them (see [Capabilities](capabilities.md#camera-and-microphone)); no other capability asks. In 0.0.9, one that an app requires is asked for when the app starts, with `reason: 'launch'`; after 0.0.9, `reason` is always `'request'`. The desktop shows these prompts and passes the answers on with `acde:permissions`:
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { permissions } from 'acde:permissions'
|
|
126
|
+
|
|
127
|
+
const ask = async (prompt) => {
|
|
128
|
+
// prompt: { id, app, label, capability: 'camera' | 'microphone', reason: 'request' }
|
|
129
|
+
const allow = await showPrompt(`Allow ${prompt.label} to use ${prompt.capability}?`)
|
|
130
|
+
await permissions.answer(prompt.id, allow)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
permissions.on('prompt', ask)
|
|
134
|
+
for (const prompt of await permissions.pending()) ask(prompt) // prompts raised before the page subscribed
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`permissions.list(app?)` returns the stored decisions (`{app, label, capability, decision: 'granted' | 'denied'}`; a folder the user picked for an app is listed as `fs.folder-…` with `folder: {label, exists}`), and `permissions.revoke(app, capability)` takes one back, resolving to `false` when there was none. The `answered` event (`{id, app, capability, allowed}`) reports each answer.
|
|
138
|
+
|
|
139
|
+
## System apps
|
|
140
|
+
|
|
141
|
+
The apps in a desktop's `apps` ship in the same package as the desktop, which makes them **system apps**. They differ from other apps in three ways:
|
|
142
|
+
|
|
143
|
+
- They may declare shell modules as capabilities and import them: `"capabilities": { "windows": true, "permissions": true }`. An app outside a desktop package that declares one fails `acde check` (`acde:windows is a shell module: only apps of a desktop package (system apps) may declare it`).
|
|
144
|
+
- The user is not asked about their camera and microphone.
|
|
145
|
+
- With shared places in `fs.roots`, their `acde:fs` roots carry the folder's `path`, and `fs.locate()` / `fs.openPath()` work.
|
|
146
|
+
|
|
147
|
+
A system app imports only what it declares: `import 'acde:session'` without `"session": true` fails with `acde:session is a shell module: declare "session" in the capabilities of <label>`.
|
|
148
|
+
|
|
149
|
+
## Other modules
|
|
150
|
+
|
|
151
|
+
- `acde:session`: `logout()`, `reboot()`, `poweroff()`, `setLanguage(tag)`. Only the desktop page and its system apps that declare `"session": true` can use it; an ordinary app cannot.
|
|
152
|
+
- `acde:desktops`: `list()`, `choose(id, remember?)` to switch desktops.
|
|
153
|
+
- `acde:launch`: `start(argv)` runs a native program, `embed(argv)` embeds one in a window (Dopame OS only).
|
|
154
|
+
- `acde:inspect`: turns the screen into a text semantic tree and acts on it by ref (for AI agents). See the [reference](../reference/modules.md#acdeinspect).
|
|
155
|
+
|
|
156
|
+
## Developing in a desktop
|
|
157
|
+
|
|
158
|
+
While developing an app, you can also run it inside a desktop:
|
|
159
|
+
|
|
160
|
+
```sh
|
|
161
|
+
acde dev ./my-app --desktop ./my-desktop
|
|
162
|
+
acde dev ./my-app --desktop dopame
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
See [acde dev](dev.md).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# acde dev
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
acde dev
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
This does three things:
|
|
8
|
+
|
|
9
|
+
1. Builds the project (into `.acde/`).
|
|
10
|
+
2. Starts acdert, which opens **one** window on your computer: the **host window**, standing in for the screen of a Dopame OS machine.
|
|
11
|
+
3. Watches the project's files, and on every change rebuilds and reloads the affected windows.
|
|
12
|
+
|
|
13
|
+
A running `acde dev` is a **dev session**: the [debugging commands](tools.md), [MCP](mcp.md), and `acde test` all work with one.
|
|
14
|
+
|
|
15
|
+
## What runs
|
|
16
|
+
|
|
17
|
+
| Command | What you get |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `acde dev` (app project) | Just this app, filling the host window; closing the app ends the session |
|
|
20
|
+
| `acde dev` (desktop project) | This desktop |
|
|
21
|
+
| `acde dev --app ID` | Picks one app when the project has several |
|
|
22
|
+
| `acde dev ./a ./b` | Several projects together, in one desktop |
|
|
23
|
+
| `acde dev ./app --desktop ./my-desktop` | The app runs in the given desktop project |
|
|
24
|
+
| `acde dev ./app --desktop dopame` | The app runs in the `dopame` desktop, once it is [installed](../start/install.md#install-the-dopame-desktop) |
|
|
25
|
+
|
|
26
|
+
With several projects or `--desktop`, the desktop is chosen like this: the value of `--desktop`; otherwise the only desktop project among them; otherwise `dopame`. A `--desktop` value that looks like a path (contains `/`, or is `.` / `..`), or that names a project in the current directory, is treated as one more project.
|
|
27
|
+
|
|
28
|
+
The session is ready when `acde webs`, run from the project directory, lists the app's page. Declaring a capability grants it, so the app opens its window right away. The camera and the microphone wait for an answer, which `acde allow` or `acde deny` gives from another terminal: in 0.0.9, an app that declares one opens its window only once answered; after 0.0.9, only a page that asks for one waits. See [Camera and microphone](tools.md#camera-and-microphone).
|
|
29
|
+
|
|
30
|
+
`--headless` doesn't open a window (a **headless session**), which suits scripts and CI. Pages still render and screenshots still work. It still needs a GPU, so it doesn't run on Linux CI machines without one (macOS CI runners work); see [Platform support](../reference/platforms.md).
|
|
31
|
+
|
|
32
|
+
On a machine without a display, such as a Linux server over ssh, use `--headless` and look at the session with `acde shot`, `acde screen` and `acde inspect` (see [Without a display](tools.md#without-a-display)). On Linux, `acde dev` without `--headless` (and `acde test --headed`) stops at once when neither `WAYLAND_DISPLAY` nor `DISPLAY` is set, and says so. If acdert exits with an error anyway, `acde dev` adds the same `--headless` hint after acdert's own messages.
|
|
33
|
+
|
|
34
|
+
## Reloading
|
|
35
|
+
|
|
36
|
+
- Changes within 150 ms are merged into one build, and only the project that changed is rebuilt.
|
|
37
|
+
- Apps: the window is closed and reopened at its previous position and size (or the new `size`, if that changed in the config). Desktops: the desktop page reloads; services are not restarted.
|
|
38
|
+
- State survives a reload where possible: form values, scroll position, focus, and selection are restored. The page itself reloads: there is no hot module replacement, so component state (a React counter, Vue state, values in memory) starts over; see [Frameworks](frameworks.md#saving-in-acde-dev).
|
|
39
|
+
- A failed build prints a single error line, and windows keep showing the last good build.
|
|
40
|
+
- Changes in `node_modules`, `.acde`, `.git`, framework output directories such as `dist` / `build` / `.next`, `*.e2e.*` test files, and `__screenshots__` do not trigger a build.
|
|
41
|
+
|
|
42
|
+
## Build output
|
|
43
|
+
|
|
44
|
+
By default the build goes into the project's `.acde/`. `--out DIR` changes that: an absolute path is used as is, and a relative path is resolved against **the project being built**. Dev session builds reach acdert only through `ACDE_PATH`; they are **not** installed into the local package directory.
|
|
45
|
+
|
|
46
|
+
## Stopping
|
|
47
|
+
|
|
48
|
+
`acde dev` ends on Ctrl+C, when you close the window, or when acdert exits. Its exit code is the same as acdert's. To end one running in the background, run `acde stop` from the project directory (or `acde stop --session <pid>`): it asks acdert to quit, waits for `acde dev` to exit, and prints `acde: stopped acde dev <pid>`.
|
|
49
|
+
|
|
50
|
+
## Logs
|
|
51
|
+
|
|
52
|
+
The terminal shows builds and reloads, the pages' `console.*` output (lines such as `6975.356 I acde js: …`) and uncaught errors (`[<app>#<window>] page error: …`), backend output (`[<app>#backend] …`), what happens to an app (its backend started or stopped, its state restored after a reload, an `alert()`, a permission prompt waiting for an answer), and the runtime's warnings and errors (lines such as `14.383 W main …`). After 0.0.9, the runtime's own info log (startup timings, GPU and the like: `14.382 I main …`) is left out and `acde dev --verbose` shows it too; in 0.0.9 it is always shown.
|
|
53
|
+
|
|
54
|
+
To watch them from another terminal, or to read them from a script, use `acde console` / `acde network` from the [debugging tools](tools.md).
|
|
55
|
+
|
|
56
|
+
## DevTools
|
|
57
|
+
|
|
58
|
+
After 0.0.9, press **F12** in the host window (fn+F12 on a Mac keyboard) to open the DevTools of Chrome or Edge on the page in that window: Elements, Console, Network, and Sources with breakpoints. The terminal says `acde: opened DevTools for <page> in <browser>`; F12 again brings that DevTools window to the front. `acde devtools [web]` does the same from another terminal (see [DevTools](tools.md#devtools)). The page never sees that F12; F12 with Ctrl, Shift, Alt or Cmd still reaches it.
|
|
59
|
+
|
|
60
|
+
DevTools open in a browser profile of acde's own, not your everyday one. `ACDE_BROWSER` names another browser; without Chrome or Edge, or with `ACDE_BROWSER=none`, the terminal prints the address to paste into one instead.
|
|
61
|
+
|
|
62
|
+
## Runtime
|
|
63
|
+
|
|
64
|
+
`acde dev` runs the acdert named by `ACDERT` (a path, or a name on PATH), otherwise the acdert the installer put next to acde, otherwise the `acdert` on PATH. `acde --version` shows which one it finds, and `acde doctor` checks that it runs here; see [The runtime](../start/install.md#the-runtime) and [Check the setup](doctor.md).
|
|
65
|
+
|
|
66
|
+
When acdert exits before the session is ready (for example because it cannot use the GPU), `acde dev` prints its log line, then `acde: acdert exited (1) before it was ready; acde doctor checks the runtime, the GPU and this setup`, and exits with that code.
|