@web-applets/sdk 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,93 +1,97 @@
1
1
  # Web Applets
2
2
 
3
- > An open SDK to create interoperable actions & views for agents _a web of capabilities!_
3
+ > An open spec & SDK for creating apps that agents can use.
4
4
 
5
- 🔗 [Community Applets Repo](https://github.com/unternet-co/community-applets) | 🔗 [Community Mailing List](https://groups.google.com/a/unternet.co/g/community)
5
+ 🔗 [Applets Repo](https://github.com/unternet-co/community-applets) | 🔗 [Mailing List](https://groups.google.com/a/unternet.co/g/community) | 🔗 [Applets Chat Demo](https://github.com/unternet-co/applets-chat)
6
6
 
7
7
  ## What is it?
8
8
 
9
- Web Applets is a specification for modular, local web software that can be read and used by both humans and machines. Web Applets aims to be an interoperabe application layer for agents instead of chatbots that can only interact in plain text, Web Applets allow them to actuate real software, read the results, and render rich, graphical views in response.
9
+ Web Applets is an open specification for building software that both humans and AI can understand and use together. Instead of forcing AI to operate traditional point-and-click apps built for humans, Web Applets creates a new kind of software designed for human-AI collaboration. Think of them a bit like artifacts, but they do stuff!
10
10
 
11
- In short, your model can use & respond with artibrary bits of web UI that a human can then interact with – a map, a rich text document, an item on Amazon with a purchase button, whatever you can build with the web you can turn it into an applet that an agent can use and display.
11
+ ![Demo of a web applets chatbot](./docs/assets/applets-chat-demo.gif)
12
12
 
13
- Did we mention it's _interoperable_? We think the future of software should be open & collaborative, not locked down to a single platform.
13
+ Web Applets are modular pieces of web software that:
14
14
 
15
- For more, see [why](./docs/why.md).
15
+ - **Can be used directly by humans with rich, graphical interfaces**
16
+ - **Can be understood and operated by AI through a clear protocol**
17
+ - **Run locally in your environment, not on distant servers**
18
+ - **Share context and state with their environment**
19
+ - **Can be freely combined and composed**
16
20
 
17
- ## Getting started
18
-
19
- Install the applets SDK & CLI:
20
-
21
- ```bash
22
- npm i --save @web-applets/sdk
23
- npm i --save-dev @web-applets/cli
24
- ```
21
+ Think of any web software you use today - maps, documents, shopping, calendars - and imagine if instead of visiting these as separate websites, you could pull them into your own environment where both you and AI could use them together seamlessly.
25
22
 
26
- Then, initialize the `applets.config.json` and create a new blank applet:
23
+ ## Key Features
27
24
 
28
- ```bash
29
- npx applets init
30
- npx applets create <your-applet-name>
31
- ```
32
-
33
- 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.
25
+ - **Built on Web Standards:** Create applets using familiar web technologies (HTML, CSS, JavaScript)
26
+ - **AI-Native Protocol:** Applets expose their state and actions in a way AI can understand and use
27
+ - **Rich Interfaces:** Full support for complex graphical UIs, not just text
28
+ - **Local-First:** Runs in your environment, keeping your data under your control
29
+ - **Composable:** Applets can work together, sharing context and state
30
+ - **Open Standard:** Designed for interoperability, not platform lock-in
34
31
 
35
- Inside your applet folder, you'll find a basic web app setup:
32
+ Web Applets aims to do for AI-enabled software what the web did for documents - create an open platform where anyone can build, share, and connect applications. We believe the future of software should be built on open collaboration, not tight integration with closed platforms.
36
33
 
37
- - `public/manifest.json`: This file describes the Applet, and tells the model what actions are available and what parameters each action takes
38
- - `index.html`: Much like a website, this holds the main page for your applet
39
- - `src/main.ts`: Declares functions that respond to each action, and a render function that updates the view based on state
34
+ ## Example
40
35
 
41
- > 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.
36
+ Let's say we have a simple website that says hello. It might look something like this:
42
37
 
43
- 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:
38
+ `index.html`:
44
39
 
45
- ```js
46
- {
47
- // ...
48
- "actions": [
49
- {
50
- "id": "set_name",
51
- "description": "Sets the name of the user to be greeted",
52
- "params": {
53
- "name": {
54
- "type": "string",
55
- "description": "The name of the user"
56
- }
57
- }
58
- }
59
- ]
60
- }
40
+ ```html
41
+ <!DOCTYPE html>
42
+ <html lang="en">
43
+ <script src="./main.js" type="module"></script>
44
+ <body>
45
+ Hello! <span id="name">whoever you are</span>.
46
+ </body>
47
+ </html>
61
48
  ```
62
49
 
63
- Now let's update `src/main.ts` to assign an action handler:
50
+ Let's add some Web Applets functionality, so this can respond to a `set_name` message:
51
+
52
+ `main.js`:
64
53
 
65
54
  ```js
66
- // First, import the SDK
67
- import { appletContext } from '../../sdk/src';
55
+ import { applets } from '@web-applets/sdk';
68
56
 
69
- // Now connect to the applet runtime
70
- const applet = appletContext.connect();
57
+ const context = applets.getContext();
71
58
 
72
- // Attach the action handler, and update the state
73
- applet.setActionHandler('set_name', ({ name }) => {
74
- 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 };
75
68
  });
69
+
70
+ // Whenever the data is updated, update the view
71
+ context.ondata = () => {
72
+ document.getElementById('name').innerText = applet.data.name;
73
+ };
76
74
  ```
77
75
 
78
- 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.
76
+ Done! If you load this up in the inspector and introduce yourself, it will respond by greeting you.
79
77
 
80
- Finally, we need to render the applet whenever a render signal is received. Again in `main.ts`:
78
+ ## Getting started
81
79
 
82
- ```js
83
- // ...
80
+ Create a new web app with the applets SDK installed. You can do this quickly using our CLI:
84
81
 
85
- applet.onrender = () => {
86
- document.body.innerText = `Hello, ${applet.state.name}!`;
87
- };
82
+ ```bash
83
+ npx @web-applets/create
88
84
  ```
89
85
 
90
- 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.
86
+ Inside the generated folder, you'll find a basic web app setup:
87
+
88
+ - `public/manifest.json`: A web app manifest, useful when publishing your applet, adding icons, etc.
89
+ - `index.html`: Much like a website, this holds the main page for your applet
90
+ - `src/main.ts`: Declares functions that respond to each action, and a render function that updates the view based on state
91
+
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.
93
+
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.
91
95
 
92
96
  ![A screenshot showing the 'playground' editing UI, with a web applets showing 'Hello, Web Applets'](docs/assets/web-applets-playground.png)
93
97
 
@@ -95,16 +99,12 @@ Now if you run `npx applets playground`, you should be able to test out your new
95
99
 
96
100
  Using Web Applets is just as easy as creating them!
97
101
 
98
- 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`.
99
-
100
- Then, run:
102
+ Install & import the applets client in your app:
101
103
 
102
104
  ```bash
103
- npx applets build
105
+ npm install @web-applets/sdk
104
106
  ```
105
107
 
106
- Now in your main app, you can import the applets client:
107
-
108
108
  ```js
109
109
  import { applets } from '@web-applets/sdk';
110
110
  ```
@@ -112,8 +112,8 @@ import { applets } from '@web-applets/sdk';
112
112
  Now you can import your applets from wherever they're being served from (note – you can also host them anywhere on the web):
113
113
 
114
114
  ```js
115
- const applet = await applets.load('/helloworld.applet'); // replace with a URL if hosted remotely
116
- 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);
117
117
  applet.dispatchAction('set_name', { name: 'Web Applets' });
118
118
  ```
119
119
 
@@ -125,43 +125,13 @@ document.body.appendChild(container);
125
125
  const applet = await applets.load(`/helloworld.applet`, container);
126
126
  ```
127
127
 
128
- 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:
129
129
 
130
130
  ```js
131
- applet.state = { name: 'Ada Lovelace' };
131
+ applet.data = { name: 'Ada Lovelace' };
132
132
  // console.log: { name: "Ada Lovelace" }
133
133
  ```
134
134
 
135
- 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.
136
-
137
- ```js
138
- const headers = await applets.getHeaders('/');
139
- ```
140
-
141
- This headers object looks like:
142
-
143
- ```js
144
- [
145
- {
146
- name: 'Hello World',
147
- description: 'Displays a greeting to the user.',
148
- url: '/applets/helloworld.applet',
149
- actions: [
150
- {
151
- id: 'set_name',
152
- description: 'Sets the name of the user to be greeted',
153
- params: {
154
- name: 'The name of the user',
155
- },
156
- },
157
- ],
158
- },
159
- // ...
160
- ];
161
- ```
162
-
163
- 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.
164
-
165
135
  ## Feedback & Community
166
136
 
167
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
+ }