@web-applets/sdk 0.0.9 → 0.1.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.
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
+ }