@web-applets/sdk 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -33,7 +33,7 @@ Web Applets aims to do for AI-enabled software what the web did for documents -
33
33
 
34
34
  ## Example
35
35
 
36
- This is a simple applet that prints "Hello, [your name]" when given the `set_name` action.
36
+ Let's say we have a simple website that says hello. It might look something like this:
37
37
 
38
38
  `index.html`:
39
39
 
@@ -47,125 +47,51 @@ This is a simple applet that prints "Hello, [your name]" when given the `set_nam
47
47
  </html>
48
48
  ```
49
49
 
50
+ Let's add some Web Applets functionality, so this can respond to a `set_name` message:
51
+
50
52
  `main.js`:
51
53
 
52
54
  ```js
53
- import { appletContext } from '@web-applets/sdk';
54
-
55
- // Get view element we want to manipulate
56
- const nameElem = document.getElementById('name');
55
+ import { applets } from '@web-applets/sdk';
57
56
 
58
- // Connect to the applet context
59
- const applet = appletContext.connect();
57
+ const context = applets.getContext();
60
58
 
61
- // When the set_name action is called, change the state
62
- applet.setActionHandler('set_name', ({ name }) => {
63
- applet.setState({ name });
59
+ // Define a 'set_name' action, and make it update the shared data object with the new name
60
+ context.defineAction('set_name', {
61
+ params: {
62
+ name: {
63
+ type: string,
64
+ description: 'The name of the person to be greeted.',
65
+ },
66
+ },
67
+ handler: ({ name }) => applet.data = { name };
64
68
  });
65
69
 
66
- // Whenever we get a request to render the view, update the name
67
- applet.onrender = () => {
68
- nameElem.innerText = applet.state?.name;
70
+ // Whenever the data is updated, update the view
71
+ context.ondata = () => {
72
+ document.getElementById('name').innerText = applet.data.name;
69
73
  };
70
74
  ```
71
75
 
72
- `manifest.json`:
73
-
74
- ```json
75
- {
76
- "type": "applet",
77
- "name": "Hello World",
78
- "description": "Displays a greeting to the user.",
79
- "entrypoint": "index.html",
80
- "actions": [
81
- {
82
- "id": "set_name",
83
- "description": "Sets the name of the user to be greeted",
84
- "params": {
85
- "name": {
86
- "type": "string",
87
- "description": "The name of the user"
88
- }
89
- }
90
- }
91
- ]
92
- }
93
- ```
76
+ Done! If you load this up in the inspector and introduce yourself, it will respond by greeting you.
94
77
 
95
78
  ## Getting started
96
79
 
97
- Install the applets SDK & CLI:
80
+ Create a new web app with the applets SDK installed. You can do this quickly using our CLI:
98
81
 
99
82
  ```bash
100
- npm i --save @web-applets/sdk
101
- npm i --save-dev @web-applets/cli
83
+ npx @web-applets/create
102
84
  ```
103
85
 
104
- Then, initialize the `applets.config.json` and create a new blank applet:
105
-
106
- ```bash
107
- npx applets init
108
- npx applets create <your-applet-name>
109
- ```
110
-
111
- This creates an applet folder, with a build system built-in using Vite. You can change this to anything you want. We recommend building at this stage, as the SDK currently needs to be bundled. We're working on adding a statically hosted script to import.
112
-
113
- Inside your applet folder, you'll find a basic web app setup:
86
+ Inside the generated folder, you'll find a basic web app setup:
114
87
 
115
- - `public/manifest.json`: This file describes the Applet, and tells the model what actions are available and what parameters each action takes
88
+ - `public/manifest.json`: A web app manifest, useful when publishing your applet, adding icons, etc.
116
89
  - `index.html`: Much like a website, this holds the main page for your applet
117
90
  - `src/main.ts`: Declares functions that respond to each action, and a render function that updates the view based on state
118
91
 
119
92
  > Want to use React? Svelte? Vue? – No problem, just install the dependencies and create an app the way you normally would in a website. So long as you're receiving the action events, it will all just work.
120
93
 
121
- Let's say we want our applet to respond to a "set_name" action and render the user's name. In our `manifest.json` file we can write:
122
-
123
- ```js
124
- {
125
- // ...
126
- "actions": [
127
- {
128
- "id": "set_name",
129
- "description": "Sets the name of the user to be greeted",
130
- "params": {
131
- "name": {
132
- "type": "string",
133
- "description": "The name of the user"
134
- }
135
- }
136
- }
137
- ]
138
- }
139
- ```
140
-
141
- Now let's update `src/main.ts` to assign an action handler:
142
-
143
- ```js
144
- // First, import the SDK
145
- import { appletContext } from '../../sdk/src';
146
-
147
- // Now connect to the applet runtime
148
- const applet = appletContext.connect();
149
-
150
- // Attach the action handler, and update the state
151
- applet.setActionHandler('set_name', ({ name }) => {
152
- applet.setState({ name });
153
- });
154
- ```
155
-
156
- When this state updates, it will inform the client which can then store the state somewhere, for example in a database so the applet will persist between uses.
157
-
158
- Finally, we need to render the applet whenever a render signal is received. Again in `main.ts`:
159
-
160
- ```js
161
- // ...
162
-
163
- applet.onrender = () => {
164
- document.body.innerText = `Hello, ${applet.state.name}!`;
165
- };
166
- ```
167
-
168
- Now if you run `npx applets playground`, you should be able to test out your new applet action directly. This applet will now work in any environment where the SDK is installed.
94
+ Now if you run `npx @web-applets/inspector`, you should be able to test out your new applet directly. This applet will now work in any environment where the SDK is installed.
169
95
 
170
96
  ![A screenshot showing the 'playground' editing UI, with a web applets showing 'Hello, Web Applets'](docs/assets/web-applets-playground.png)
171
97
 
@@ -173,16 +99,12 @@ Now if you run `npx applets playground`, you should be able to test out your new
173
99
 
174
100
  Using Web Applets is just as easy as creating them!
175
101
 
176
- First, build your applets. By default, this goes into a folder called `dist/`, but you'll likely want to change this in `applets.config.json` to point to wherever you're serving public files from. For example, in a Vite project, edit this to be `./public`.
177
-
178
- Then, run:
102
+ Install & import the applets client in your app:
179
103
 
180
104
  ```bash
181
- npx applets build
105
+ npm install @web-applets/sdk
182
106
  ```
183
107
 
184
- Now in your main app, you can import the applets client:
185
-
186
108
  ```js
187
109
  import { applets } from '@web-applets/sdk';
188
110
  ```
@@ -190,8 +112,8 @@ import { applets } from '@web-applets/sdk';
190
112
  Now you can import your applets from wherever they're being served from (note – you can also host them anywhere on the web):
191
113
 
192
114
  ```js
193
- const applet = await applets.load('/helloworld.applet'); // replace with a URL if hosted remotely
194
- applet.onstateupdated = (state) => console.log(state);
115
+ const applet = await applets.load('/helloworld.applet'); // replace with an https URL if hosted remotely
116
+ applet.ondata = (e) => console.log(e.data);
195
117
  applet.dispatchAction('set_name', { name: 'Web Applets' });
196
118
  ```
197
119
 
@@ -203,44 +125,13 @@ document.body.appendChild(container);
203
125
  const applet = await applets.load(`/helloworld.applet`, container);
204
126
  ```
205
127
 
206
- To load pre-existing saved state into an applet, simply set the state property:
128
+ To load pre-existing saved data into an applet, simply set the data property:
207
129
 
208
130
  ```js
209
- applet.state = { name: 'Ada Lovelace' };
131
+ applet.data = { name: 'Ada Lovelace' };
210
132
  // console.log: { name: "Ada Lovelace" }
211
133
  ```
212
134
 
213
- It may also be helpful to check available applets at a domain, or in your public folder. For that you can extract the applet headers from the App Manifest at the public root (`/manifest.json`), and see the available applets and a shorthand for the actions you can take in them. This is automatically created when you build your applets.
214
-
215
- ```js
216
- const applets = await applets.list('/');
217
- ```
218
-
219
- This applets object looks like:
220
-
221
- ```js
222
- {
223
- '/helloworld.applet': {
224
- name: 'Hello World',
225
- description: 'Displays a greeting to the user.',
226
- url: '/applets/helloworld.applet',
227
- actions: {
228
- set_name: {
229
- description: 'Sets the name of the user to be greeted',
230
- params: {
231
- name: {
232
- type: 'string',
233
- description: 'The name of the user'
234
- }
235
- },
236
- },
237
- },
238
- // ...
239
- };
240
- ```
241
-
242
- You can use it to present a quick summary of available tools to your model, and then decide on an applet and action to use.
243
-
244
135
  ## Feedback & Community
245
136
 
246
137
  This is a community project, and we're open to community members discussing the project direction, and submitting code!
@@ -0,0 +1,19 @@
1
+ import { Applet } from '../index';
2
+ export declare class AppletFrame extends HTMLElement {
3
+ #private;
4
+ container?: HTMLIFrameElement;
5
+ applet?: Applet;
6
+ loaded?: boolean;
7
+ static observedAttributes: string[];
8
+ connectedCallback(): void;
9
+ set src(value: string);
10
+ get src(): string;
11
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
12
+ loadApplet(url: string): Promise<void>;
13
+ set data(data: any);
14
+ resizeContainer(dimensions: {
15
+ height: number;
16
+ width: number;
17
+ }): void;
18
+ get styles(): string;
19
+ }
@@ -0,0 +1,93 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _AppletFrame_root, _AppletFrame_src;
13
+ import { applets, } from '../index';
14
+ // TODO: Add resize event handler, and resize DOM element
15
+ export class AppletFrame extends HTMLElement {
16
+ constructor() {
17
+ super(...arguments);
18
+ _AppletFrame_root.set(this, void 0);
19
+ _AppletFrame_src.set(this, void 0);
20
+ }
21
+ connectedCallback() {
22
+ __classPrivateFieldSet(this, _AppletFrame_root, this.attachShadow({ mode: 'open' }), "f");
23
+ this.container = document.createElement('iframe');
24
+ __classPrivateFieldGet(this, _AppletFrame_root, "f").appendChild(this.container);
25
+ const styles = document.createElement('style');
26
+ styles.textContent = this.styles;
27
+ __classPrivateFieldGet(this, _AppletFrame_root, "f").appendChild(styles);
28
+ const url = this.getAttribute('src');
29
+ if (url)
30
+ this.loadApplet(url);
31
+ }
32
+ set src(value) {
33
+ __classPrivateFieldSet(this, _AppletFrame_src, value, "f");
34
+ this.loadApplet(value);
35
+ }
36
+ get src() {
37
+ return __classPrivateFieldGet(this, _AppletFrame_src, "f");
38
+ }
39
+ attributeChangedCallback(name, oldValue, newValue) {
40
+ if (name === 'src') {
41
+ this.src = newValue;
42
+ }
43
+ }
44
+ async loadApplet(url) {
45
+ if (!this.container)
46
+ return;
47
+ this.applet = await applets.load(url, this.container);
48
+ // When data received, bubble the event up
49
+ this.applet.ondata = (dataEvent) => {
50
+ this.dispatchEvent(dataEvent);
51
+ };
52
+ // Resize
53
+ this.applet.onresize = (resizeEvent) => {
54
+ this.resizeContainer(resizeEvent.dimensions);
55
+ };
56
+ this.applet.onactions = (e) => { };
57
+ // Emit a load event when loading complete
58
+ this.dispatchEvent(new Event('load'));
59
+ this.loaded = true;
60
+ }
61
+ set data(data) {
62
+ if (this.applet && this.loaded) {
63
+ this.applet.data = data;
64
+ }
65
+ else {
66
+ const loadListener = () => {
67
+ this.applet.data = data;
68
+ this.removeEventListener('load', loadListener);
69
+ };
70
+ this.addEventListener('load', loadListener);
71
+ }
72
+ }
73
+ resizeContainer(dimensions) {
74
+ this.container.style.height = `${dimensions.height + 2}px`;
75
+ }
76
+ get styles() {
77
+ return /*css*/ `
78
+ :host {
79
+ display: flex;
80
+ flex-direction: column;
81
+ }
82
+
83
+ iframe {
84
+ border: none;
85
+ height: 100%;
86
+ width: 100%;
87
+ }
88
+ `;
89
+ }
90
+ }
91
+ _AppletFrame_root = new WeakMap(), _AppletFrame_src = new WeakMap();
92
+ AppletFrame.observedAttributes = ['src'];
93
+ customElements.define('applet-frame', AppletFrame);
@@ -0,0 +1,21 @@
1
+ import { AppletAction, AppletMessage, ActionParams, AppletManifest, AppletDataEvent, AppletResizeEvent, AppletActionsEvent, AppletMessageRelay, AppletReadyEvent } from './shared';
2
+ export declare function load(url: string, container?: HTMLIFrameElement): Promise<Applet>;
3
+ export interface AppletOptions {
4
+ manifest: AppletManifest;
5
+ container: HTMLIFrameElement;
6
+ }
7
+ export declare class Applet<T = any> extends EventTarget {
8
+ #private;
9
+ messageRelay: AppletMessageRelay;
10
+ actions: AppletAction[];
11
+ container: HTMLIFrameElement;
12
+ constructor(manifest: AppletManifest, targetWindow: Window);
13
+ dispatchAction(actionId: string, params?: ActionParams): Promise<AppletMessage>;
14
+ get data(): T;
15
+ set data(data: T);
16
+ get manifest(): AppletManifest;
17
+ onready(event: AppletReadyEvent): void;
18
+ onresize(event: AppletResizeEvent): void;
19
+ onactions(event: AppletActionsEvent): void;
20
+ ondata(event: AppletDataEvent): void;
21
+ }
@@ -0,0 +1,117 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _Applet_instances, _Applet_manifest, _Applet_data, _Applet_addListeners;
13
+ import { parseUrl } from '../utils';
14
+ import { AppletDataMessage, AppletInitMessage, AppletDataEvent, AppletResizeEvent, AppletActionsEvent, AppletMessageRelay, loadManifest, AppletReadyEvent, AppletActionMessage, } from './shared';
15
+ // Container for initializing applets without an explicit container
16
+ const hiddenContainer = document.createElement('iframe');
17
+ hiddenContainer.style.display = 'none';
18
+ document.body.appendChild(hiddenContainer);
19
+ // Options for loading an applet
20
+ // interface LoadOpts {
21
+ // unsafe?: boolean;
22
+ // }
23
+ // const defaultOpts: LoadOpts = {
24
+ // unsafe: false,
25
+ // };
26
+ // Load an applet object from a URL
27
+ export async function load(url, container
28
+ // opts?: LoadOpts
29
+ ) {
30
+ if (!container)
31
+ container = hiddenContainer;
32
+ url = parseUrl(url);
33
+ const manifest = await loadManifest(url);
34
+ // If unsafe enabled, allow same origin sandbox
35
+ // This is required for e.g. YouTube embeds
36
+ // const _opts = Object.assign(defaultOpts, opts ?? {});
37
+ // if (_opts.unsafe && manifest.unsafe) {
38
+ // container.setAttribute(
39
+ // 'sandbox',
40
+ // 'allow-scripts allow-forms allow-same-origin'
41
+ // );
42
+ // } else {
43
+ // container.setAttribute('sandbox', 'allow-scripts allow-forms');
44
+ // }
45
+ container.setAttribute('sandbox', 'allow-scripts allow-forms');
46
+ container.src = url;
47
+ const applet = new Applet(manifest, container.contentWindow);
48
+ return new Promise((resolve) => {
49
+ applet.onready = () => resolve(applet);
50
+ });
51
+ }
52
+ export class Applet extends EventTarget {
53
+ constructor(manifest, targetWindow) {
54
+ super();
55
+ _Applet_instances.add(this);
56
+ this.actions = [];
57
+ _Applet_manifest.set(this, void 0);
58
+ _Applet_data.set(this, void 0);
59
+ this.messageRelay = new AppletMessageRelay(targetWindow);
60
+ __classPrivateFieldSet(this, _Applet_manifest, manifest, "f");
61
+ __classPrivateFieldGet(this, _Applet_instances, "m", _Applet_addListeners).call(this);
62
+ this.messageRelay.on('ready', () => {
63
+ this.messageRelay.send(new AppletInitMessage());
64
+ });
65
+ }
66
+ async dispatchAction(actionId, params) {
67
+ const actionMessage = new AppletActionMessage({
68
+ actionId,
69
+ params,
70
+ });
71
+ return await this.messageRelay.send(actionMessage);
72
+ }
73
+ get data() {
74
+ return __classPrivateFieldGet(this, _Applet_data, "f");
75
+ }
76
+ set data(data) {
77
+ __classPrivateFieldSet(this, _Applet_data, data, "f");
78
+ this.messageRelay.send(new AppletDataMessage({ data }));
79
+ }
80
+ get manifest() {
81
+ return __classPrivateFieldGet(this, _Applet_manifest, "f");
82
+ }
83
+ onready(event) { }
84
+ onresize(event) { }
85
+ onactions(event) { }
86
+ ondata(event) { }
87
+ }
88
+ _Applet_manifest = new WeakMap(), _Applet_data = new WeakMap(), _Applet_instances = new WeakSet(), _Applet_addListeners = function _Applet_addListeners() {
89
+ this.messageRelay.on('ready', (message) => {
90
+ const readyEvent = new AppletReadyEvent();
91
+ if (typeof this.onready === 'function')
92
+ this.onready(readyEvent);
93
+ this.dispatchEvent(readyEvent);
94
+ });
95
+ this.messageRelay.on('data', (message) => {
96
+ __classPrivateFieldSet(this, _Applet_data, message.data, "f");
97
+ const dataEvent = new AppletDataEvent({ data: message.data });
98
+ if (typeof this.ondata === 'function')
99
+ this.ondata(dataEvent);
100
+ this.dispatchEvent(dataEvent);
101
+ });
102
+ this.messageRelay.on('resize', (message) => {
103
+ const resizeEvent = new AppletResizeEvent({
104
+ dimensions: message.dimensions,
105
+ });
106
+ if (typeof this.onresize === 'function')
107
+ this.onresize(resizeEvent);
108
+ this.dispatchEvent(resizeEvent);
109
+ });
110
+ this.messageRelay.on('actions', (message) => {
111
+ this.actions = message.actions;
112
+ const actionsEvent = new AppletActionsEvent({ actions: message.actions });
113
+ if (typeof this.onactions === 'function')
114
+ this.onactions(actionsEvent);
115
+ this.dispatchEvent(actionsEvent);
116
+ });
117
+ };
@@ -0,0 +1,32 @@
1
+ import { ActionParams, AppletDataEvent, AppletLoadEvent, AppletReadyEvent, JSONSchemaProperties, AppletManifest, AppletAction, AppletMessageRelay } from './shared';
2
+ export type ActionHandler<T extends ActionParams> = (params: T) => void | Promise<void>;
3
+ export type ActionHandlerDict = {
4
+ [key: string]: ActionHandler<any>;
5
+ };
6
+ export declare class AppletContext extends EventTarget {
7
+ #private;
8
+ messageRelay: AppletMessageRelay;
9
+ actionHandlers: ActionHandlerDict;
10
+ manifest: AppletManifest;
11
+ constructor();
12
+ connect(): void;
13
+ initialize(): Promise<void>;
14
+ createResizeObserver(): void;
15
+ attachListeners(): void;
16
+ setActionHandler<T = ActionParams>(actionId: string, handler: ActionHandler<T>): void;
17
+ defineAction<T = ActionParams>(actionId: string, definition: ActionDefinition<T>): void;
18
+ set actions(actions: AppletAction[]);
19
+ get actions(): AppletAction[];
20
+ set data(data: any);
21
+ get data(): any;
22
+ setData(data: any): Promise<void>;
23
+ onload(event: AppletLoadEvent): Promise<void> | void;
24
+ onready(event: AppletReadyEvent): void;
25
+ ondata(event: AppletDataEvent): void;
26
+ }
27
+ interface ActionDefinition<T> extends Omit<AppletAction, 'id'> {
28
+ params?: JSONSchemaProperties;
29
+ handler?: ActionHandler<T>;
30
+ }
31
+ export declare function getContext(): AppletContext;
32
+ export {};
@@ -0,0 +1,138 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _AppletContext_actions, _AppletContext_data;
13
+ import { AppletMessage, AppletDataEvent, AppletLoadEvent, AppletReadyEvent, AppletActionsMessage, AppletReadyMessage, AppletMessageRelay, } from './shared';
14
+ export class AppletContext extends EventTarget {
15
+ constructor() {
16
+ super();
17
+ this.actionHandlers = {};
18
+ _AppletContext_actions.set(this, {});
19
+ _AppletContext_data.set(this, void 0);
20
+ this.connect();
21
+ }
22
+ connect() {
23
+ this.messageRelay = new AppletMessageRelay(window.parent);
24
+ // When document loads/if it's loaded, call the initialize function
25
+ if (document.readyState === 'complete' ||
26
+ document.readyState === 'interactive') {
27
+ // Document has loaded already.
28
+ // Timeout added so if the caller defines the onload function, it will exist by now
29
+ setTimeout(this.initialize.bind(this), 1);
30
+ }
31
+ else {
32
+ // Document not yet loaded, we'll add an event listener to call when it does
33
+ window.addEventListener('DOMContentLoaded', this.initialize.bind(this));
34
+ }
35
+ this.createResizeObserver();
36
+ this.attachListeners();
37
+ }
38
+ async initialize() {
39
+ const manifestLinkElem = document.querySelector('link[rel="manifest"]');
40
+ if (!manifestLinkElem)
41
+ return;
42
+ try {
43
+ const manifestRequest = await fetch(manifestLinkElem.href);
44
+ const manifest = await manifestRequest.json();
45
+ this.manifest = manifest;
46
+ this.actions = manifest.actions ?? [];
47
+ }
48
+ catch (e) {
49
+ return;
50
+ }
51
+ // Call the onload function
52
+ const loadEvent = new AppletLoadEvent();
53
+ this.dispatchEvent(loadEvent);
54
+ if (typeof this.onload === 'function')
55
+ await this.onload(loadEvent);
56
+ // Tell the host we're ready
57
+ this.messageRelay.send(new AppletReadyMessage());
58
+ // Emit a local ready event
59
+ const readyEvent = new AppletReadyEvent();
60
+ this.dispatchEvent(readyEvent);
61
+ if (typeof this.onready === 'function')
62
+ this.onready(readyEvent);
63
+ }
64
+ createResizeObserver() {
65
+ const resizeObserver = new ResizeObserver((entries) => {
66
+ for (let entry of entries) {
67
+ const message = new AppletMessage('resize', {
68
+ dimensions: {
69
+ width: entry.contentRect.width,
70
+ height: entry.contentRect.height,
71
+ },
72
+ });
73
+ this.messageRelay.send(message);
74
+ }
75
+ });
76
+ resizeObserver.observe(document.querySelector('html'));
77
+ }
78
+ attachListeners() {
79
+ this.messageRelay.on('init', (message) => {
80
+ this.manifest = message.manifest;
81
+ this.actions = this.manifest?.actions || [];
82
+ });
83
+ this.messageRelay.on('data', (message) => {
84
+ this.setData(message.data);
85
+ });
86
+ this.messageRelay.on('action', async (message) => {
87
+ if (Object.keys(this.actionHandlers).includes(message.actionId)) {
88
+ await this.actionHandlers[message.actionId](message.params);
89
+ }
90
+ });
91
+ }
92
+ setActionHandler(actionId, handler) {
93
+ this.actionHandlers[actionId] = handler;
94
+ }
95
+ defineAction(actionId, definition) {
96
+ const { handler, ...properties } = definition;
97
+ this.actions = [
98
+ ...this.actions,
99
+ {
100
+ id: actionId,
101
+ ...properties,
102
+ },
103
+ ];
104
+ this.setActionHandler(actionId, handler);
105
+ }
106
+ set actions(actions) {
107
+ if (!actions)
108
+ return;
109
+ for (let action of actions) {
110
+ __classPrivateFieldGet(this, _AppletContext_actions, "f")[action.id] = action;
111
+ }
112
+ this.messageRelay.send(new AppletActionsMessage({ actions: this.actions }));
113
+ }
114
+ get actions() {
115
+ return Object.values(__classPrivateFieldGet(this, _AppletContext_actions, "f"));
116
+ }
117
+ set data(data) {
118
+ this.setData(data);
119
+ }
120
+ get data() {
121
+ return __classPrivateFieldGet(this, _AppletContext_data, "f");
122
+ }
123
+ async setData(data) {
124
+ const dataMessage = new AppletMessage('data', { data });
125
+ await this.messageRelay.send(dataMessage);
126
+ __classPrivateFieldSet(this, _AppletContext_data, data, "f");
127
+ const dataEvent = new AppletDataEvent({ data });
128
+ this.dispatchEvent(dataEvent);
129
+ this.ondata(dataEvent);
130
+ }
131
+ onload(event) { }
132
+ onready(event) { }
133
+ ondata(event) { }
134
+ }
135
+ _AppletContext_actions = new WeakMap(), _AppletContext_data = new WeakMap();
136
+ export function getContext() {
137
+ return new AppletContext();
138
+ }