@web-applets/sdk 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  > An open SDK to create interoperable actions & views for agents – _a web of capabilities!_
4
4
 
5
- 🔗 [Community Applets Repo](https://github.com/unternet-co/community-applets)
5
+ 🔗 [Community Applets Repo](https://github.com/unternet-co/community-applets) | 🔗 [Community Mailing List](https://groups.google.com/a/unternet.co/g/community)
6
6
 
7
7
  ## What is it?
8
8
 
9
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.
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.
12
+
11
13
  Did we mention it's _interoperable_? We think the future of software should be open & collaborative, not locked down to a single platform.
12
14
 
15
+ For more, see [why](./docs/why.md).
16
+
13
17
  ## Getting started
14
18
 
15
19
  Install the applets SDK & CLI:
package/dist/client.d.ts CHANGED
@@ -1,7 +1,10 @@
1
- import { AppletAction, AppletHeader, AppletMessage, ActionParams, AppletManifest } from './types';
2
- export declare function getHeaders(url: string): Promise<AppletHeader[]>;
3
- export declare function getManifests(url: string): Promise<any[]>;
4
- export declare function load(url: string, container: HTMLIFrameElement): Promise<Applet>;
1
+ import { AppletAction, AppletHeader, AppletMessage, ActionParams, AppletManifest, AppletMessageType, AppletMessageCallback, AppletManifestDict } from './types';
2
+ export declare function list(url: string): Promise<AppletManifestDict>;
3
+ interface AppletOpts {
4
+ headless?: boolean;
5
+ unsafe?: boolean;
6
+ }
7
+ export declare function load(url: string, container?: HTMLIFrameElement, opts?: AppletOpts): Promise<Applet>;
5
8
  export declare class Applet<T = unknown> extends EventTarget {
6
9
  #private;
7
10
  actions: AppletAction[];
@@ -20,5 +23,10 @@ export declare class Applet<T = unknown> extends EventTarget {
20
23
  onstateupdated(event: CustomEvent): void;
21
24
  disconnect(): void;
22
25
  dispatchAction(actionId: string, params: ActionParams): Promise<AppletMessage<any>>;
26
+ send(message: AppletMessage): Promise<AppletMessage<any>>;
27
+ on(messageType: AppletMessageType, callback: AppletMessageCallback): Promise<void>;
23
28
  }
24
29
  export declare function loadManifest(url: string): Promise<AppletManifest>;
30
+ export declare function getHeaders(url: string): Promise<AppletHeader[]>;
31
+ export declare function getManifests(url: string): Promise<any[]>;
32
+ export {};
package/dist/client.js CHANGED
@@ -9,73 +9,72 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
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
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _Applet_state;
12
+ var _Applet_instances, _Applet_state, _Applet_dispatchEvent;
13
13
  import { AppletMessage, } from './types';
14
- const hiddenRoot = document.createElement('div');
15
- hiddenRoot.style.display = 'none';
16
- document.body.appendChild(hiddenRoot);
17
- export async function getHeaders(url) {
14
+ const hiddenContainer = document.createElement('iframe');
15
+ hiddenContainer.style.display = 'none';
16
+ document.body.appendChild(hiddenContainer);
17
+ export async function list(url) {
18
18
  url = parseUrl(url);
19
19
  try {
20
20
  const request = await fetch(`${url}/manifest.json`);
21
21
  const appManifest = await request.json();
22
- const appletHeaders = appManifest.applets;
23
- return appletHeaders ?? [];
22
+ const appletUrls = appManifest.applets;
23
+ const manifests = {};
24
+ const manifestRequests = appletUrls.map(async (appletUrl) => {
25
+ appletUrl = parseUrl(appletUrl, url);
26
+ const request = await fetch(`${appletUrl}/manifest.json`);
27
+ const manifest = await request.json();
28
+ manifests[appletUrl] = manifest;
29
+ });
30
+ await Promise.all(manifestRequests);
31
+ return manifests;
24
32
  }
25
33
  catch {
26
- return [];
34
+ return {};
27
35
  }
28
36
  }
29
- export async function getManifests(url) {
30
- url = parseUrl(url);
31
- const request = await fetch(`${url}/manifest.json`);
32
- const headers = (await request.json()).applets;
33
- const manifests = await Promise.all(headers.map(async (header) => {
34
- const appletUrl = parseUrl(header.url);
35
- const request = await fetch(`${appletUrl}/manifest.json`);
36
- return await request.json();
37
- }));
38
- return manifests ?? [];
39
- }
40
- export async function load(url, container) {
37
+ const defaultOpts = {
38
+ headless: false,
39
+ unsafe: false,
40
+ };
41
+ export async function load(url, container, opts) {
42
+ const _opts = Object.assign(defaultOpts, opts ?? {});
43
+ if (!container) {
44
+ container = hiddenContainer;
45
+ _opts.headless = true;
46
+ }
47
+ if (!_opts.unsafe)
48
+ container.setAttribute('sandbox', 'allow-scripts allow-forms');
41
49
  url = parseUrl(url);
42
50
  const manifest = await loadManifest(`${url}`);
43
51
  const applet = new Applet();
44
52
  applet.manifest = manifest;
45
- applet.actions = manifest.actions; // let the events set this later
53
+ applet.actions = manifest.actions;
46
54
  applet.container = container;
47
55
  container.src = applet.manifest.entrypoint;
48
- if (!container.isConnected)
49
- hiddenRoot.appendChild(container);
50
56
  return new Promise((resolve) => {
51
- window.addEventListener('message', (message) => {
52
- if (message.source !== container.contentWindow)
53
- return;
54
- if (message.data.type === 'ready')
55
- resolve(applet);
57
+ applet.on('ready', () => {
58
+ const initMessage = new AppletMessage('init', {
59
+ headless: _opts.headless,
60
+ });
61
+ applet.send(initMessage);
62
+ resolve(applet);
56
63
  });
57
64
  });
58
65
  }
59
66
  export class Applet extends EventTarget {
60
67
  constructor() {
61
68
  super();
69
+ _Applet_instances.add(this);
62
70
  this.actions = [];
63
71
  _Applet_state.set(this, void 0);
64
- window.addEventListener('message', (message) => {
65
- if (message.source !== this.container.contentWindow)
66
- return;
67
- if (message.data.type === 'state') {
68
- __classPrivateFieldSet(this, _Applet_state, message.data.state, "f");
69
- this.dispatchEvent(new CustomEvent('stateupdated', { detail: message.data.detail }));
70
- this.onstateupdated(message.data.state);
71
- }
72
- if (message.data.type === 'resize') {
73
- this.resizeContainer(message.data.dimensions);
74
- }
75
- this.container.contentWindow?.postMessage({
76
- type: 'resolve',
77
- id: message.data.id,
78
- }, '*');
72
+ this.on('state', (message) => {
73
+ __classPrivateFieldSet(this, _Applet_state, message.state, "f");
74
+ __classPrivateFieldGet(this, _Applet_instances, "m", _Applet_dispatchEvent).call(this, 'stateupdated', message.state);
75
+ });
76
+ this.on('resize', (message) => {
77
+ this.resizeContainer(message.dimensions);
79
78
  });
80
79
  }
81
80
  get state() {
@@ -83,8 +82,7 @@ export class Applet extends EventTarget {
83
82
  }
84
83
  set state(state) {
85
84
  __classPrivateFieldSet(this, _Applet_state, state, "f");
86
- const stateMessage = new AppletMessage('state', { state });
87
- this.container.contentWindow?.postMessage(stateMessage.toJson(), '*');
85
+ this.send(new AppletMessage('state', { state }));
88
86
  }
89
87
  toJson() {
90
88
  return Object.fromEntries(Object.entries(this).filter(([_, value]) => {
@@ -98,7 +96,7 @@ export class Applet extends EventTarget {
98
96
  }));
99
97
  }
100
98
  resizeContainer(dimensions) {
101
- this.container.style.height = `${dimensions.height}px`;
99
+ this.container.style.height = `${dimensions.height + 2}px`;
102
100
  // if (!this.#styleOverrides) {
103
101
  // this.#container.style.height = `${dimensions.height}px`;
104
102
  // }
@@ -110,14 +108,15 @@ export class Applet extends EventTarget {
110
108
  actionId,
111
109
  params,
112
110
  });
113
- this.container.contentWindow?.postMessage(requestMessage.toJson(), '*');
111
+ return await this.send(requestMessage);
112
+ }
113
+ async send(message) {
114
+ this.container.contentWindow?.postMessage(message.toJson(), '*');
114
115
  return new Promise((resolve) => {
115
116
  const listener = (messageEvent) => {
116
- if (messageEvent.source !== this.container.contentWindow)
117
- return;
118
117
  const responseMessage = new AppletMessage(messageEvent.data.type, messageEvent.data);
119
118
  if (responseMessage.type === 'resolve' &&
120
- responseMessage.id === requestMessage.id) {
119
+ responseMessage.id === message.id) {
121
120
  window.removeEventListener('message', listener);
122
121
  resolve(responseMessage);
123
122
  }
@@ -125,20 +124,37 @@ export class Applet extends EventTarget {
125
124
  window.addEventListener('message', listener);
126
125
  });
127
126
  }
127
+ async on(messageType, callback) {
128
+ const listener = async (messageEvent) => {
129
+ if (messageEvent.source !== this.container.contentWindow)
130
+ return;
131
+ if (messageEvent.data.type !== messageType)
132
+ return;
133
+ const message = new AppletMessage(messageEvent.data.type, messageEvent.data);
134
+ await callback(message);
135
+ this.container.contentWindow?.postMessage(new AppletMessage('resolve', { id: message.id }), '*');
136
+ };
137
+ window.addEventListener('message', listener);
138
+ }
128
139
  }
129
- _Applet_state = new WeakMap();
140
+ _Applet_state = new WeakMap(), _Applet_instances = new WeakSet(), _Applet_dispatchEvent = function _Applet_dispatchEvent(id, detail) {
141
+ if (typeof this[`on${id}`] === 'function') {
142
+ this[`on${id}`](detail);
143
+ }
144
+ this.dispatchEvent(new CustomEvent(id, { detail }));
145
+ };
146
+ /* Helpers */
130
147
  function parseUrl(url, base) {
131
148
  if (['http', 'https'].includes(url.split('://')[0])) {
132
149
  return url;
133
150
  }
134
- let path = url;
135
- if (path.startsWith('/'))
136
- path = path.slice(1);
137
- if (path.endsWith('/'))
138
- path = path.slice(0, -1);
151
+ let path = trimSlashes(url);
139
152
  url = `${base || window.location.origin}/${path}`;
140
153
  return url;
141
154
  }
155
+ function trimSlashes(str) {
156
+ return str.replace(/^\/+|\/+$/g, '');
157
+ }
142
158
  export async function loadManifest(url) {
143
159
  url = parseUrl(url);
144
160
  const request = await fetch(`${url}/manifest.json`);
@@ -149,3 +165,26 @@ export async function loadManifest(url) {
149
165
  appletManifest.entrypoint = parseUrl(appletManifest.entrypoint, url);
150
166
  return appletManifest;
151
167
  }
168
+ export async function getHeaders(url) {
169
+ url = parseUrl(url);
170
+ try {
171
+ const request = await fetch(`${url}/manifest.json`);
172
+ const appManifest = await request.json();
173
+ const appletHeaders = appManifest.applets;
174
+ return appletHeaders ?? [];
175
+ }
176
+ catch {
177
+ return [];
178
+ }
179
+ }
180
+ export async function getManifests(url) {
181
+ url = parseUrl(url);
182
+ const request = await fetch(`${url}/manifest.json`);
183
+ const headers = (await request.json()).applets;
184
+ const manifests = await Promise.all(headers.map(async (header) => {
185
+ const appletUrl = parseUrl(header.url);
186
+ const request = await fetch(`${appletUrl}/manifest.json`);
187
+ return await request.json();
188
+ }));
189
+ return manifests ?? [];
190
+ }
package/dist/context.d.ts CHANGED
@@ -3,12 +3,15 @@ import { ActionHandlerDict, AppletMessage, AppletMessageType, AppletMessageCallb
3
3
  * Context
4
4
  */
5
5
  export declare class AppletContext<StateType = any> extends EventTarget {
6
+ #private;
6
7
  client: AppletClient;
7
8
  actionHandlers: ActionHandlerDict;
8
- state: StateType;
9
+ headless: boolean;
9
10
  connect(): this;
10
11
  setActionHandler<T extends ActionParams>(actionId: string, handler: ActionHandler<T>): void;
11
- setState(state: StateType): Promise<void>;
12
+ set state(state: StateType);
13
+ get state(): StateType;
14
+ setState(state: StateType, shouldRender?: boolean): Promise<void>;
12
15
  onload(): Promise<void> | void;
13
16
  onready(): Promise<void> | void;
14
17
  onrender(): void;
package/dist/context.js CHANGED
@@ -1,3 +1,15 @@
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_state;
1
13
  import { AppletMessage, } from './types';
2
14
  /**
3
15
  * Context
@@ -6,6 +18,8 @@ export class AppletContext extends EventTarget {
6
18
  constructor() {
7
19
  super(...arguments);
8
20
  this.actionHandlers = {};
21
+ _AppletContext_state.set(this, void 0);
22
+ this.headless = false;
9
23
  }
10
24
  connect() {
11
25
  this.client = new AppletClient();
@@ -34,11 +48,24 @@ export class AppletContext extends EventTarget {
34
48
  }
35
49
  });
36
50
  resizeObserver.observe(document.querySelector('html'));
51
+ this.client.on('init', (message) => {
52
+ const initMessage = message;
53
+ this.headless = initMessage.headless;
54
+ });
37
55
  this.client.on('state', (message) => {
38
56
  if (!isStateMessage(message)) {
39
57
  throw new TypeError("Message doesn't match type StateMessage");
40
58
  }
41
- this.setState(message.state);
59
+ // Don't render when state updates match the current state
60
+ // this retains cursor positions in text fields, for example
61
+ if (JSON.stringify(message.state) === JSON.stringify(__classPrivateFieldGet(this, _AppletContext_state, "f")))
62
+ return;
63
+ __classPrivateFieldSet(this, _AppletContext_state, message.state, "f");
64
+ // BUG: For some reason regular applets were loading headless, when instantiated not on a page reload
65
+ // if (!this.headless) {
66
+ this.onrender();
67
+ this.dispatchEvent(new CustomEvent('render'));
68
+ // }
42
69
  });
43
70
  this.client.on('action', async (message) => {
44
71
  if (!isActionMessage(message)) {
@@ -47,24 +74,32 @@ export class AppletContext extends EventTarget {
47
74
  if (Object.keys(this.actionHandlers).includes(message.actionId)) {
48
75
  await this.actionHandlers[message.actionId](message.params);
49
76
  }
50
- message.resolve();
51
77
  });
52
78
  return this;
53
79
  }
54
80
  setActionHandler(actionId, handler) {
55
81
  this.actionHandlers[actionId] = handler;
56
82
  }
57
- async setState(state) {
83
+ set state(state) {
84
+ this.setState(state);
85
+ }
86
+ get state() {
87
+ return __classPrivateFieldGet(this, _AppletContext_state, "f");
88
+ }
89
+ async setState(state, shouldRender) {
58
90
  const message = new AppletMessage('state', { state });
59
91
  await this.client.send(message);
60
- this.state = state;
61
- this.dispatchEvent(new CustomEvent('render'));
62
- this.onrender(); // TODO: Should come from client? Or stay here, and only activate if mounted? Need a control for mounting.
92
+ __classPrivateFieldSet(this, _AppletContext_state, state, "f");
93
+ if (shouldRender !== false && !this.headless) {
94
+ this.onrender();
95
+ this.dispatchEvent(new CustomEvent('render'));
96
+ }
63
97
  }
64
98
  onload() { }
65
99
  onready() { }
66
100
  onrender() { }
67
101
  }
102
+ _AppletContext_state = new WeakMap();
68
103
  function isActionMessage(message) {
69
104
  return message.type === 'action';
70
105
  }
@@ -76,14 +111,12 @@ function isStateMessage(message) {
76
111
  */
77
112
  class AppletClient {
78
113
  on(messageType, callback) {
79
- window.addEventListener('message', (messageEvent) => {
114
+ window.addEventListener('message', async (messageEvent) => {
80
115
  if (messageEvent.data.type !== messageType)
81
116
  return;
82
117
  const message = new AppletMessage(messageEvent.data.type, messageEvent.data);
83
- message.resolve = () => {
84
- window.parent.postMessage(new AppletMessage('resolve', { id: message.id }), '*');
85
- };
86
- callback(message);
118
+ await callback(message);
119
+ window.parent.postMessage(new AppletMessage('resolve', { id: message.id }), '*');
87
120
  });
88
121
  }
89
122
  send(message) {
package/dist/types.d.ts CHANGED
@@ -1,14 +1,19 @@
1
1
  export interface AppletManifest {
2
2
  type: 'applet';
3
3
  name: string;
4
- description: string;
4
+ description?: string;
5
5
  icon?: string;
6
- entrypoint: string;
7
- actions: AppletAction[];
6
+ frameless?: boolean;
7
+ entrypoint?: string;
8
+ actions?: AppletAction[];
9
+ }
10
+ export interface AppletManifestDict {
11
+ [url: string]: AppletManifest;
8
12
  }
9
13
  export interface AppletAction {
10
14
  id: string;
11
- description: string;
15
+ name?: string;
16
+ description?: string;
12
17
  params?: ActionParamSchema;
13
18
  }
14
19
  export interface AppletHeader {
@@ -23,12 +28,12 @@ export interface AppletHeader {
23
28
  };
24
29
  }[];
25
30
  }
26
- export type AppletState = Record<string, Serializable>;
31
+ export type AppletState = any;
27
32
  export type ActionParamSchema = Record<string, {
28
33
  description: string;
29
34
  type: 'string';
30
35
  }>;
31
- export type ActionParams = Record<string, unknown>;
36
+ export type ActionParams<T = any> = Record<string, T>;
32
37
  export type ActionHandlerDict = {
33
38
  [key: string]: ActionHandler<any>;
34
39
  };
@@ -38,11 +43,22 @@ export interface AppletStateMessage<T = any> extends AppletMessage {
38
43
  type: 'state';
39
44
  state: T;
40
45
  }
46
+ export interface AppletResizeMessage extends AppletMessage {
47
+ type: 'resize';
48
+ dimensions: {
49
+ height: number;
50
+ width: number;
51
+ };
52
+ }
41
53
  export interface AppletActionMessage<T = any> extends AppletMessage {
42
54
  type: 'action';
43
55
  actionId: string;
44
56
  params: T;
45
57
  }
58
+ export interface AppletInitMessage extends AppletMessage {
59
+ type: 'init';
60
+ headless: boolean;
61
+ }
46
62
  export declare class AppletMessage<T = any> {
47
63
  type: AppletMessageType;
48
64
  id: string;
@@ -53,9 +69,5 @@ export declare class AppletMessage<T = any> {
53
69
  };
54
70
  resolve(): void;
55
71
  }
56
- export type AppletMessageType = 'action' | 'render' | 'state' | 'ready' | 'resolve' | 'resize';
57
- export type AppletMessageCallback = (message: AnyAppletMessage) => void;
58
- type Serializable = string | number | boolean | null | Serializable[] | {
59
- [key: string]: Serializable;
60
- };
61
- export {};
72
+ export type AppletMessageType = 'action' | 'actions' | 'render' | 'state' | 'init' | 'ready' | 'resolve' | 'resize';
73
+ export type AppletMessageCallback = (message: AnyAppletMessage) => Promise<void> | void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { applets } from '../index';
2
+ class AppletView extends HTMLElement {
3
+ connectedCallback() {
4
+ const styles = document.createElement('style');
5
+ styles.textContent = this.styles;
6
+ this.appendChild(styles);
7
+ this.container = document.createElement('iframe');
8
+ this.appendChild(this.container);
9
+ }
10
+ get styles() {
11
+ return /*css*/ `
12
+ applet-frame {
13
+ display: flex;
14
+ flex-direction: column;
15
+ }
16
+
17
+ applet-frame iframe {
18
+ border: none;
19
+ }
20
+
21
+ applet-frame:not(.frameless) {
22
+ border: 1px solid #ddd;
23
+ }
24
+
25
+ applet-frame.frameless {
26
+ padding: 0 7px;
27
+ }
28
+ `;
29
+ }
30
+ set url(url) {
31
+ setTimeout(() => this.loadApplet(url), 1);
32
+ }
33
+ async loadApplet(url) {
34
+ if (!this.container)
35
+ return;
36
+ this.applet = await applets.load(url, this.container);
37
+ if (this.applet.manifest.frameless)
38
+ this.classList.add('frameless');
39
+ this.applet.onstateupdated = () => {
40
+ this.dispatchEvent(new CustomEvent('stateupdated', { detail: this.applet.state }));
41
+ };
42
+ this.dispatchEvent(new CustomEvent('load'));
43
+ }
44
+ set state(state) {
45
+ if (this.applet)
46
+ this.applet.state = state;
47
+ this.addEventListener('load', () => {
48
+ this.applet.state = state;
49
+ });
50
+ }
51
+ }
52
+ customElements.define('applet-frame', AppletView);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { applets } from '../index.js';
2
+ class AppletFrame extends HTMLElement {
3
+ connectedCallback() {
4
+ const styles = document.createElement('style');
5
+ styles.textContent = this.styles;
6
+ this.appendChild(styles);
7
+ this.container = document.createElement('iframe');
8
+ this.appendChild(this.container);
9
+ }
10
+ get styles() {
11
+ return /*css*/ `
12
+ applet-frame {
13
+ display: flex;
14
+ flex-direction: column;
15
+ }
16
+
17
+ applet-frame iframe {
18
+ border: none;
19
+ }
20
+
21
+ applet-frame:not(.frameless) {
22
+ border: 1px solid #ddd;
23
+ }
24
+
25
+ applet-frame.frameless {
26
+ padding: 0 7px;
27
+ }
28
+ `;
29
+ }
30
+ set url(url) {
31
+ setTimeout(() => this.loadApplet(url), 1);
32
+ }
33
+ async loadApplet(url) {
34
+ if (!this.container)
35
+ return;
36
+ this.applet = await applets.load(url, this.container);
37
+ if (this.applet.manifest.frameless)
38
+ this.classList.add('frameless');
39
+ this.applet.onstateupdated = () => {
40
+ this.dispatchEvent(new CustomEvent('stateupdated', { detail: this.applet.state }));
41
+ };
42
+ this.dispatchEvent(new CustomEvent('load'));
43
+ }
44
+ set state(state) {
45
+ if (this.applet)
46
+ this.applet.state = state;
47
+ this.addEventListener('load', () => {
48
+ this.applet.state = state;
49
+ });
50
+ }
51
+ }
52
+ customElements.define('applet-frame', AppletFrame);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-applets/sdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "The Web Applets SDK, for creating & hosting Web Applets.",
5
5
  "author": "Rupert Manfredi <rupert@unternet.co>",
6
6
  "license": "MIT",
@@ -25,6 +25,7 @@
25
25
  "typescript": "^5.6.2"
26
26
  },
27
27
  "dependencies": {
28
+ "marked": "^14.1.3",
28
29
  "vite": "^5.4.7"
29
30
  }
30
31
  }