@dolphy-app/extension-sdk 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,17 @@ generator makes (`--template blank`); the recipes next to this file build on the
6
6
  same layout:
7
7
 
8
8
  - [Exercise type](recipe-exercise-type.md): a new kind of task with its own answer input.
9
- - [Theme](recipe-theme.md): colors, no code.
10
- - [Command and panel](recipe-command-panel.md): palette commands and a screen in an isolated frame.
9
+ - [Theme](recipe-theme.md): colors, registered by the client part.
10
+ - [Command and panel](recipe-command-panel.md): palette commands and a panel, a Vue single-file component (`.vue`) in the app window.
11
+ - [A panel in React](recipe-react.md): the same panel drawn with React: `"frameworks": ["react"]`, `reactComponent`, hooks, tests with `mountForTest`.
12
+ - [A component of any framework](recipe-mountable.md): `defineMountable` on plain DOM, the base for Svelte, Solid or Lit, and what `ctx` gives it.
11
13
  - [Events and storage](recipe-event-storage.md): react to learning events and keep data.
14
+ - [Hooks](recipe-hooks.md): change or cancel a session start and the batch of exercises before the engine acts.
12
15
  - [Settings](recipe-settings.md): let the user configure the extension.
13
- - [Without a build](no-build.md): a hand-written `extension.json` and `main.mjs`, no TypeScript.
16
+ - [Visibility conditions and dependencies](recipe-when-dependencies.md): show a command only where it makes sense, a widget in the daily plan, require another extension.
17
+ - [Importer and exporter](recipe-import-export.md): bring a file in as a course, write a course out.
18
+ - [Calls, engine and window](recipe-rpc-and-app.md): `defineRpc` between the parts, `engine` access, `useApp`.
19
+ - [Without a build](no-build.md): a hand-written `extension.json`, `main.mjs` and `client.mjs`, no TypeScript.
14
20
  - [Debugging](debugging.md): where to look when something does not work.
15
21
 
16
22
  ## What you need
@@ -28,24 +34,26 @@ pnpm install
28
34
  ```
29
35
 
30
36
  `--id` is the extension id: lowercase letters, digits, dots and hyphens. Every
31
- id the extension declares (commands, panels, settings…) starts with it, so pick
32
- one that is yours (a publisher prefix, then a name). Without `--id` the id is the
33
- directory name in kebab-case. The `--template` values:
34
-
35
- | Template | What you get |
36
- | --------------- | ------------------------------------------------------------------------------ |
37
- | `exercise` | a task type with an answer input and a setting (the default) |
38
- | `theme` | a color theme, no code |
39
- | `command-panel` | palette commands and a panel |
40
- | `events` | a learning event handler, storage, commands and a panel |
41
- | `blank` | one palette command |
37
+ id the extension registers (commands, panels, settings…) is the extension id or
38
+ starts with it and a dot, so pick one that is yours (a publisher prefix, then a
39
+ name). Without `--id` the id is the directory name in kebab-case. The
40
+ `--template` values:
41
+
42
+ | Template | What you get |
43
+ | --------------- | ----------------------------------------------------------------- |
44
+ | `exercise` | a task type with an answer input and a setting (the default) |
45
+ | `theme` | a color theme |
46
+ | `command-panel` | palette commands and a panel as a Vue single-file component |
47
+ | `react-panel` | palette commands and a panel drawn with React |
48
+ | `events` | a learning event handler, storage, commands and a panel |
49
+ | `blank` | one palette command |
42
50
 
43
51
  An unknown name exits with code 2 and lists the available ones.
44
52
 
45
53
  ## 2. The files
46
54
 
47
- The project is two files that matter. The manifest declares what the extension
48
- adds, the code implements it.
55
+ The project is two files that matter. The manifest says who the extension is,
56
+ the code says what it adds.
49
57
 
50
58
  File `extension.json` (quick start):
51
59
 
@@ -58,61 +66,73 @@ File `extension.json` (quick start):
58
66
  "name": "Hello command",
59
67
  "description": "A command-palette command that shows a notification.",
60
68
  "author": "your-github-login",
61
- "tags": ["productivity"],
62
- "contributes": {
63
- "commands": [{ "id": "acme.hello.hello", "title": "Say hello" }]
64
- }
69
+ "tags": ["productivity"]
65
70
  }
66
71
  ```
67
72
 
68
73
  `$schema` gives editors completion and checking. `id` is the identity of the
69
74
  extension in the app and the catalog; it never changes after the first release.
70
75
  Replace `your-github-login` in `author` with your GitHub login before you
71
- publish. `contributes.commands` declares the command that appears in the palette.
76
+ publish. The manifest declares no commands, panels or settings: the code
77
+ registers them. The other keys are `platforms`, `minAppVersion`, `icon` and
78
+ `dependencies`. `main` and `client` are written by the build.
72
79
 
73
80
  File `src/index.ts` (quick start):
74
81
 
75
82
  ```ts
76
- import { defineExtension, notify } from '@dolphy-app/extension-sdk';
77
-
78
- // extension code: runs in the extension process of the app
79
- // the command id comes from extension.json: a misspelt id or a declared id
80
- // without a handler fails `pnpm typecheck`
81
- export const host = defineExtension({
82
- commands: {
83
- 'acme.hello.hello': () => notify('Hello from acme.hello!'),
84
- },
83
+ import { defineServer, notify } from '@dolphy-app/extension-sdk';
84
+
85
+ // runs in the extension host: every call registers a contribution
86
+ export const server = defineServer((s) => {
87
+ s.registerCommand({
88
+ id: 'acme.hello.hello',
89
+ title: { en: 'Say hello', ru: 'Поздороваться' },
90
+ run: () => notify('Hello from acme.hello!'),
91
+ });
85
92
  });
86
93
  ```
87
94
 
88
- `host` is the code that runs in the extension process of the app.
89
- `defineExtension` takes a handler for every command the manifest declares:
90
- `notify` returns a notification to the app. The ids are types: `pnpm typecheck`
91
- (and every build) writes `.dolphy/ids.d.ts` from `extension.json`, so a misspelt
92
- id, or a declared command without a handler, does not compile.
95
+ `src/index.ts` exports up to two entries. `server` runs in the extension host
96
+ (Node) and registers commands, exercise types, settings, event handlers,
97
+ schedules, importers and exporters; `client` runs in the app window and adds
98
+ panels, injected components, answer views, markdown renderers, themes and
99
+ client commands. This project has only `server`. The host calls it when it
100
+ loads the extension; if it throws, or does not finish in 10 seconds, the
101
+ extension shows `load-failed` in Settings → Extensions and registers nothing
102
+ (see [debugging](debugging.md)).
103
+
104
+ `notify` returns a notification to the app. A text the user sees is a
105
+ `LocalizedText`: a plain string, or `{ en, ru }` as here. An id is written in
106
+ the code and must be the extension id or start with `acme.hello.`; the host
107
+ refuses an id that is taken or does not carry the prefix.
108
+
109
+ When both entries exist, keep each in its own file and re-export them from
110
+ `src/index.ts` (the recipes do): `server` must not import `vue`, `vuetify` or a
111
+ component, `client` must not import `node:*` modules, and the build reports a
112
+ violation with the file and the rule.
93
113
 
94
114
  File `test/index.test.ts` (quick start):
95
115
 
96
116
  ```ts
97
- import { loadCommands } from '@dolphy-app/extension-sdk/testing';
117
+ import { createTestServer } from '@dolphy-app/extension-sdk/testing';
98
118
  import { expect, it } from 'vitest';
99
- import { host } from '../src/index.ts';
119
+ import { server } from '../src/index.ts';
100
120
 
101
121
  it('the hello command notifies', async () => {
102
- const commands = await loadCommands(host, {
103
- declaredCommands: ['acme.hello.hello'],
104
- });
105
- expect(await commands.run('acme.hello.hello')).toEqual({
122
+ const running = await createTestServer(server, { extensionId: 'acme.hello' });
123
+ expect(await running.commands.run('acme.hello.hello')).toEqual({
106
124
  kind: 'notify',
107
125
  text: 'Hello from acme.hello!',
108
126
  });
109
- await commands.dispose();
127
+ await running.dispose();
110
128
  });
111
129
  ```
112
130
 
113
131
  The test runs the command the way the host does, without the app:
114
- `@dolphy-app/extension-sdk/testing` activates `host` with in-memory storage,
115
- settings and commands.
132
+ `createTestServer` from `@dolphy-app/extension-sdk/testing` starts `server` on
133
+ in-memory storage, settings and library, and `running.commands.run` applies the
134
+ rules the host applies to a command result. `createTestClient` does the same for
135
+ `client` (see the [command and panel recipe](recipe-command-panel.md)).
116
136
 
117
137
  ## 3. Build, check, test
118
138
 
@@ -120,13 +140,15 @@ settings and commands.
120
140
  pnpm build # dolphy-ext build: writes dist-ext/acme.hello
121
141
  pnpm validate # parses the built manifest the way the app does
122
142
  pnpm lint # metadata and bundle checks the catalog review also runs
123
- pnpm typecheck # dolphy-ext types, then tsc
143
+ pnpm typecheck # tsc
124
144
  pnpm test # vitest
125
145
  ```
126
146
 
127
147
  `pnpm build` turns `src/index.ts` into the files the app loads. With this
128
- template that is `dist-ext/acme.hello/extension.json` and `main.mjs`. Keep
129
- `dist-ext` and `.dolphy` out of git; the generated `.gitignore` does it.
148
+ template that is `dist-ext/acme.hello/extension.json` and `main.mjs`; the built
149
+ manifest names it in `main`. A project that exports `client` also gets
150
+ `client.mjs` and the `client` key. Keep `dist-ext` out of git; the generated
151
+ `.gitignore` does it.
130
152
 
131
153
  ## 4. Try it in the app
132
154
 
@@ -137,7 +159,7 @@ app on the result:
137
159
  pnpm exec dolphy-ext dev
138
160
  ```
139
161
 
140
- The extension appears in Settings → Extensions with the origin "development",
162
+ The extension appears in Settings → Extensions with the origin "Development",
141
163
  and the command appears in the command palette. Press Ctrl+C to stop the build
142
164
  and the app. Quit a running Dolphy first: the app has one instance, a second
143
165
  start does not pick up the extension directory. Where the app is looked up and
@@ -154,13 +176,13 @@ DOLPHY_DEV_EXTENSIONS=/path/to/my-extension/dist-ext pnpm dev
154
176
  ```
155
177
 
156
178
  Save a file: the build writes the new files and the app applies them without a
157
- restart. The window does not reload; answer inputs of extensions in development
158
- are recreated, so their state can be lost.
179
+ restart. The window does not reload; the components of extensions in
180
+ development are recreated, so their state can be lost.
159
181
 
160
- An extension that is not bundled with the app and not trusted runs in a
161
- restricted process: what the `permissions` of the manifest do not declare is
162
- unavailable. The test helpers do not reproduce that; try permission-dependent
163
- code in the app.
182
+ The extension code runs without restrictions: files, processes, threads and the
183
+ network are available. The test helpers run a handler in your process and do not
184
+ reproduce the host (time limits, the process boundary); try such code in the
185
+ app.
164
186
 
165
187
  ## 5. Next
166
188
 
@@ -168,5 +190,5 @@ code in the app.
168
190
  - Before a pull request to the extension catalog run `pnpm build`,
169
191
  `pnpm validate`, `pnpm lint`, `pnpm typecheck` and `pnpm test`: all must pass.
170
192
  The generated `.github/workflows/ci.yml` runs the same steps on every push.
171
- - Write in `README.md` what the extension does and what each permission is for;
172
- the catalog review reads it.
193
+ - Write in `README.md` what the extension does; the catalog
194
+ review reads it.