@hmcs/sdk 1.0.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.
@@ -0,0 +1,310 @@
1
+ 'use strict';
2
+
3
+ var host = require('./host.cjs');
4
+ var vrm = require('./vrm.cjs');
5
+
6
+ /**
7
+ * Returns whether webview source is local.
8
+ */
9
+ function isWebviewSourceLocal(source) {
10
+ return source.type === "local";
11
+ }
12
+ /**
13
+ * Returns whether webview source is url.
14
+ */
15
+ function isWebviewSourceUrl(source) {
16
+ return source.type === "url";
17
+ }
18
+ /**
19
+ * Returns whether webview source is inline-html.
20
+ */
21
+ function isWebviewSourceHtml(source) {
22
+ return source.type === "html";
23
+ }
24
+ /**
25
+ * Factory functions for creating {@link WebviewSource} objects.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { Webview, webviewSource } from "@hmcs/sdk";
30
+ *
31
+ * await Webview.open({ source: webviewSource.local("menu:ui") });
32
+ * await Webview.open({ source: webviewSource.url("https://example.com") });
33
+ * await wv.navigate(webviewSource.html("<h1>Hello</h1>"));
34
+ * ```
35
+ */
36
+ exports.webviewSource = void 0;
37
+ (function (webviewSource) {
38
+ /**
39
+ * Create a local asset source.
40
+ *
41
+ * @param id - Asset ID (e.g., `"menu:ui"`, `"settings:ui"`)
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const source = webviewSource.local("menu:ui");
46
+ * // { type: "local", id: "menu:ui" }
47
+ * ```
48
+ */
49
+ function local(id) {
50
+ return { type: "local", id };
51
+ }
52
+ webviewSource.local = local;
53
+ /**
54
+ * Create a URL source.
55
+ *
56
+ * @param url - URL string
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const source = webviewSource.url("https://example.com");
61
+ * // { type: "url", url: "https://example.com" }
62
+ * ```
63
+ */
64
+ function url(url) {
65
+ return { type: "url", url };
66
+ }
67
+ webviewSource.url = url;
68
+ /**
69
+ * Create an inline HTML source.
70
+ *
71
+ * @param content - HTML string
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * const source = webviewSource.html("<h1>Hello</h1>");
76
+ * // { type: "html", content: "<h1>Hello</h1>" }
77
+ * ```
78
+ */
79
+ function html(content) {
80
+ return { type: "html", content };
81
+ }
82
+ webviewSource.html = html;
83
+ })(exports.webviewSource || (exports.webviewSource = {}));
84
+ function isWebviewSourceInfoLocal(source) {
85
+ return source.type === "local";
86
+ }
87
+ function isWebviewSourceInfoUrl(source) {
88
+ return source.type === "url";
89
+ }
90
+ function isWebviewSourceInfoHtml(source) {
91
+ return source.type === "html";
92
+ }
93
+ /**
94
+ * Webview management for creating and controlling embedded web interfaces.
95
+ *
96
+ * Desktop Homunculus uses webviews to provide rich UI experiences that can be
97
+ * positioned anywhere in 3D space or attached to VRM characters.
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const webview = await Webview.open({
102
+ * source: webviewSource.url("my-mod::ui.html"),
103
+ * size: [0.7, 0.7],
104
+ * viewportSize: [800, 600],
105
+ * offset: [0, 0.5],
106
+ * });
107
+ *
108
+ * if (!(await webview.isClosed())) {
109
+ * await webview.close();
110
+ * }
111
+ * ```
112
+ */
113
+ /**
114
+ * Represents a webview instance that can display HTML content in 3D space.
115
+ */
116
+ class Webview {
117
+ entity;
118
+ constructor(entity) {
119
+ this.entity = entity;
120
+ this.entity = entity;
121
+ }
122
+ /**
123
+ * Closes the webview.
124
+ */
125
+ async close() {
126
+ await host.host.deleteMethod(host.host.createUrl(`webviews/${this.entity}`));
127
+ }
128
+ /**
129
+ * Checks whether this webview has been closed.
130
+ *
131
+ * @returns A promise that resolves to true if the webview is closed
132
+ */
133
+ async isClosed() {
134
+ const response = await host.host.get(host.host.createUrl(`webviews/${this.entity}/is-closed`));
135
+ return await response.json();
136
+ }
137
+ /**
138
+ * Gets information about this webview.
139
+ *
140
+ * @returns A promise that resolves to the webview info
141
+ */
142
+ async info() {
143
+ const response = await host.host.get(host.host.createUrl(`webviews/${this.entity}`));
144
+ return await response.json();
145
+ }
146
+ /**
147
+ * Patches webview properties (offset, size, viewportSize).
148
+ *
149
+ * @param options - The properties to update
150
+ */
151
+ async patch(options) {
152
+ await host.host.patch(host.host.createUrl(`webviews/${this.entity}`), options);
153
+ }
154
+ /**
155
+ * Sets the offset of the webview.
156
+ *
157
+ * @param offset - The new offset
158
+ */
159
+ async setOffset(offset) {
160
+ await this.patch({ offset });
161
+ }
162
+ /**
163
+ * Sets the size of the webview.
164
+ *
165
+ * @param size - The new size
166
+ */
167
+ async setSize(size) {
168
+ await this.patch({ size });
169
+ }
170
+ /**
171
+ * Sets the viewport size of the webview.
172
+ *
173
+ * @param size - The new viewport size
174
+ */
175
+ async setViewportSize(size) {
176
+ await this.patch({ viewportSize: size });
177
+ }
178
+ /**
179
+ * Navigates the webview to a new source.
180
+ *
181
+ * @param source - The new source (URL/path, inline HTML, or local asset ID)
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const wv = new Webview(entity);
186
+ * // Navigate to a mod asset
187
+ * await wv.navigate(webviewSource.url("my-mod::page.html"));
188
+ * // Navigate to inline HTML
189
+ * await wv.navigate(webviewSource.html("<h1>Hello</h1>"));
190
+ * // Navigate to a local asset by ID
191
+ * await wv.navigate(webviewSource.local("my-mod::panel.html"));
192
+ * ```
193
+ */
194
+ async navigate(source) {
195
+ await host.host.post(host.host.createUrl(`webviews/${this.entity}/navigate`), { source });
196
+ }
197
+ /**
198
+ * Reloads the webview content.
199
+ */
200
+ async reload() {
201
+ await host.host.post(host.host.createUrl(`webviews/${this.entity}/reload`));
202
+ }
203
+ /**
204
+ * Navigates the webview back in history.
205
+ *
206
+ * @example
207
+ * ```typescript
208
+ * const wv = (await Webview.list())[0];
209
+ * await new Webview(wv.entity).navigateBack();
210
+ * ```
211
+ */
212
+ async navigateBack() {
213
+ await host.host.post(host.host.createUrl(`webviews/${this.entity}/navigate/back`));
214
+ }
215
+ /**
216
+ * Navigates the webview forward in history.
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * const wv = (await Webview.list())[0];
221
+ * await new Webview(wv.entity).navigateForward();
222
+ * ```
223
+ */
224
+ async navigateForward() {
225
+ await host.host.post(host.host.createUrl(`webviews/${this.entity}/navigate/forward`));
226
+ }
227
+ /**
228
+ * Gets the VRM linked to this webview.
229
+ *
230
+ * @returns The linked VRM instance, or undefined if no VRM is linked
231
+ */
232
+ async linkedVrm() {
233
+ const response = await host.host.get(host.host.createUrl(`webviews/${this.entity}/linked-vrm`));
234
+ const entity = await response.json();
235
+ return entity !== null ? new vrm.Vrm(entity) : undefined;
236
+ }
237
+ /**
238
+ * Links this webview to a VRM entity.
239
+ *
240
+ * @param vrm - The VRM to link to this webview
241
+ */
242
+ async setLinkedVrm(vrm) {
243
+ await host.host.put(host.host.createUrl(`webviews/${this.entity}/linked-vrm`), { vrm: vrm.entity });
244
+ }
245
+ /**
246
+ * Removes the VRM link from this webview.
247
+ */
248
+ async unlinkVrm() {
249
+ await host.host.deleteMethod(host.host.createUrl(`webviews/${this.entity}/linked-vrm`));
250
+ }
251
+ /**
252
+ * Gets all open webviews.
253
+ *
254
+ * @returns A promise that resolves to an array of webview info
255
+ */
256
+ static async list() {
257
+ const response = await host.host.get(host.host.createUrl("webviews"));
258
+ return await response.json();
259
+ }
260
+ /**
261
+ * Creates and opens a webview positioned in world space.
262
+ *
263
+ * @param options - Configuration for the webview
264
+ * @returns A promise that resolves to a new Webview instance
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * // Open with a mod asset URL
269
+ * const panel = await Webview.open({
270
+ * source: webviewSource.url("my-mod::settings.html"),
271
+ * size: [0.7, 0.5],
272
+ * viewportSize: [800, 600],
273
+ * offset: [0, 1.0],
274
+ * });
275
+ *
276
+ * // Open with inline HTML
277
+ * const inline = await Webview.open({
278
+ * source: webviewSource.html("<h1>Hello World</h1>"),
279
+ * });
280
+ *
281
+ * // Open with a local asset
282
+ * const local = await Webview.open({
283
+ * source: webviewSource.local("my-mod::panel.html"),
284
+ * offset: [0.5, 0],
285
+ * });
286
+ * ```
287
+ */
288
+ static async open(options) {
289
+ const response = await host.host.post(host.host.createUrl(`webviews`), options);
290
+ return new Webview(Number(await response.json()));
291
+ }
292
+ /**
293
+ * Gets the current webview instance if called from within a webview context.
294
+ *
295
+ * @returns The current Webview instance, or undefined if not in a webview context
296
+ */
297
+ static current() {
298
+ // @ts-expect-error -- CEF injects WEBVIEW_ENTITY on the window object
299
+ const entity = window.WEBVIEW_ENTITY;
300
+ return entity !== undefined ? new Webview(entity) : undefined;
301
+ }
302
+ }
303
+
304
+ exports.Webview = Webview;
305
+ exports.isWebviewSourceHtml = isWebviewSourceHtml;
306
+ exports.isWebviewSourceInfoHtml = isWebviewSourceInfoHtml;
307
+ exports.isWebviewSourceInfoLocal = isWebviewSourceInfoLocal;
308
+ exports.isWebviewSourceInfoUrl = isWebviewSourceInfoUrl;
309
+ exports.isWebviewSourceLocal = isWebviewSourceLocal;
310
+ exports.isWebviewSourceUrl = isWebviewSourceUrl;
@@ -0,0 +1,302 @@
1
+ import { host } from './host.js';
2
+ import { Vrm } from './vrm.js';
3
+
4
+ /**
5
+ * Returns whether webview source is local.
6
+ */
7
+ function isWebviewSourceLocal(source) {
8
+ return source.type === "local";
9
+ }
10
+ /**
11
+ * Returns whether webview source is url.
12
+ */
13
+ function isWebviewSourceUrl(source) {
14
+ return source.type === "url";
15
+ }
16
+ /**
17
+ * Returns whether webview source is inline-html.
18
+ */
19
+ function isWebviewSourceHtml(source) {
20
+ return source.type === "html";
21
+ }
22
+ /**
23
+ * Factory functions for creating {@link WebviewSource} objects.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { Webview, webviewSource } from "@hmcs/sdk";
28
+ *
29
+ * await Webview.open({ source: webviewSource.local("menu:ui") });
30
+ * await Webview.open({ source: webviewSource.url("https://example.com") });
31
+ * await wv.navigate(webviewSource.html("<h1>Hello</h1>"));
32
+ * ```
33
+ */
34
+ var webviewSource;
35
+ (function (webviewSource) {
36
+ /**
37
+ * Create a local asset source.
38
+ *
39
+ * @param id - Asset ID (e.g., `"menu:ui"`, `"settings:ui"`)
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const source = webviewSource.local("menu:ui");
44
+ * // { type: "local", id: "menu:ui" }
45
+ * ```
46
+ */
47
+ function local(id) {
48
+ return { type: "local", id };
49
+ }
50
+ webviewSource.local = local;
51
+ /**
52
+ * Create a URL source.
53
+ *
54
+ * @param url - URL string
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const source = webviewSource.url("https://example.com");
59
+ * // { type: "url", url: "https://example.com" }
60
+ * ```
61
+ */
62
+ function url(url) {
63
+ return { type: "url", url };
64
+ }
65
+ webviewSource.url = url;
66
+ /**
67
+ * Create an inline HTML source.
68
+ *
69
+ * @param content - HTML string
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * const source = webviewSource.html("<h1>Hello</h1>");
74
+ * // { type: "html", content: "<h1>Hello</h1>" }
75
+ * ```
76
+ */
77
+ function html(content) {
78
+ return { type: "html", content };
79
+ }
80
+ webviewSource.html = html;
81
+ })(webviewSource || (webviewSource = {}));
82
+ function isWebviewSourceInfoLocal(source) {
83
+ return source.type === "local";
84
+ }
85
+ function isWebviewSourceInfoUrl(source) {
86
+ return source.type === "url";
87
+ }
88
+ function isWebviewSourceInfoHtml(source) {
89
+ return source.type === "html";
90
+ }
91
+ /**
92
+ * Webview management for creating and controlling embedded web interfaces.
93
+ *
94
+ * Desktop Homunculus uses webviews to provide rich UI experiences that can be
95
+ * positioned anywhere in 3D space or attached to VRM characters.
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const webview = await Webview.open({
100
+ * source: webviewSource.url("my-mod::ui.html"),
101
+ * size: [0.7, 0.7],
102
+ * viewportSize: [800, 600],
103
+ * offset: [0, 0.5],
104
+ * });
105
+ *
106
+ * if (!(await webview.isClosed())) {
107
+ * await webview.close();
108
+ * }
109
+ * ```
110
+ */
111
+ /**
112
+ * Represents a webview instance that can display HTML content in 3D space.
113
+ */
114
+ class Webview {
115
+ entity;
116
+ constructor(entity) {
117
+ this.entity = entity;
118
+ this.entity = entity;
119
+ }
120
+ /**
121
+ * Closes the webview.
122
+ */
123
+ async close() {
124
+ await host.deleteMethod(host.createUrl(`webviews/${this.entity}`));
125
+ }
126
+ /**
127
+ * Checks whether this webview has been closed.
128
+ *
129
+ * @returns A promise that resolves to true if the webview is closed
130
+ */
131
+ async isClosed() {
132
+ const response = await host.get(host.createUrl(`webviews/${this.entity}/is-closed`));
133
+ return await response.json();
134
+ }
135
+ /**
136
+ * Gets information about this webview.
137
+ *
138
+ * @returns A promise that resolves to the webview info
139
+ */
140
+ async info() {
141
+ const response = await host.get(host.createUrl(`webviews/${this.entity}`));
142
+ return await response.json();
143
+ }
144
+ /**
145
+ * Patches webview properties (offset, size, viewportSize).
146
+ *
147
+ * @param options - The properties to update
148
+ */
149
+ async patch(options) {
150
+ await host.patch(host.createUrl(`webviews/${this.entity}`), options);
151
+ }
152
+ /**
153
+ * Sets the offset of the webview.
154
+ *
155
+ * @param offset - The new offset
156
+ */
157
+ async setOffset(offset) {
158
+ await this.patch({ offset });
159
+ }
160
+ /**
161
+ * Sets the size of the webview.
162
+ *
163
+ * @param size - The new size
164
+ */
165
+ async setSize(size) {
166
+ await this.patch({ size });
167
+ }
168
+ /**
169
+ * Sets the viewport size of the webview.
170
+ *
171
+ * @param size - The new viewport size
172
+ */
173
+ async setViewportSize(size) {
174
+ await this.patch({ viewportSize: size });
175
+ }
176
+ /**
177
+ * Navigates the webview to a new source.
178
+ *
179
+ * @param source - The new source (URL/path, inline HTML, or local asset ID)
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * const wv = new Webview(entity);
184
+ * // Navigate to a mod asset
185
+ * await wv.navigate(webviewSource.url("my-mod::page.html"));
186
+ * // Navigate to inline HTML
187
+ * await wv.navigate(webviewSource.html("<h1>Hello</h1>"));
188
+ * // Navigate to a local asset by ID
189
+ * await wv.navigate(webviewSource.local("my-mod::panel.html"));
190
+ * ```
191
+ */
192
+ async navigate(source) {
193
+ await host.post(host.createUrl(`webviews/${this.entity}/navigate`), { source });
194
+ }
195
+ /**
196
+ * Reloads the webview content.
197
+ */
198
+ async reload() {
199
+ await host.post(host.createUrl(`webviews/${this.entity}/reload`));
200
+ }
201
+ /**
202
+ * Navigates the webview back in history.
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * const wv = (await Webview.list())[0];
207
+ * await new Webview(wv.entity).navigateBack();
208
+ * ```
209
+ */
210
+ async navigateBack() {
211
+ await host.post(host.createUrl(`webviews/${this.entity}/navigate/back`));
212
+ }
213
+ /**
214
+ * Navigates the webview forward in history.
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * const wv = (await Webview.list())[0];
219
+ * await new Webview(wv.entity).navigateForward();
220
+ * ```
221
+ */
222
+ async navigateForward() {
223
+ await host.post(host.createUrl(`webviews/${this.entity}/navigate/forward`));
224
+ }
225
+ /**
226
+ * Gets the VRM linked to this webview.
227
+ *
228
+ * @returns The linked VRM instance, or undefined if no VRM is linked
229
+ */
230
+ async linkedVrm() {
231
+ const response = await host.get(host.createUrl(`webviews/${this.entity}/linked-vrm`));
232
+ const entity = await response.json();
233
+ return entity !== null ? new Vrm(entity) : undefined;
234
+ }
235
+ /**
236
+ * Links this webview to a VRM entity.
237
+ *
238
+ * @param vrm - The VRM to link to this webview
239
+ */
240
+ async setLinkedVrm(vrm) {
241
+ await host.put(host.createUrl(`webviews/${this.entity}/linked-vrm`), { vrm: vrm.entity });
242
+ }
243
+ /**
244
+ * Removes the VRM link from this webview.
245
+ */
246
+ async unlinkVrm() {
247
+ await host.deleteMethod(host.createUrl(`webviews/${this.entity}/linked-vrm`));
248
+ }
249
+ /**
250
+ * Gets all open webviews.
251
+ *
252
+ * @returns A promise that resolves to an array of webview info
253
+ */
254
+ static async list() {
255
+ const response = await host.get(host.createUrl("webviews"));
256
+ return await response.json();
257
+ }
258
+ /**
259
+ * Creates and opens a webview positioned in world space.
260
+ *
261
+ * @param options - Configuration for the webview
262
+ * @returns A promise that resolves to a new Webview instance
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * // Open with a mod asset URL
267
+ * const panel = await Webview.open({
268
+ * source: webviewSource.url("my-mod::settings.html"),
269
+ * size: [0.7, 0.5],
270
+ * viewportSize: [800, 600],
271
+ * offset: [0, 1.0],
272
+ * });
273
+ *
274
+ * // Open with inline HTML
275
+ * const inline = await Webview.open({
276
+ * source: webviewSource.html("<h1>Hello World</h1>"),
277
+ * });
278
+ *
279
+ * // Open with a local asset
280
+ * const local = await Webview.open({
281
+ * source: webviewSource.local("my-mod::panel.html"),
282
+ * offset: [0.5, 0],
283
+ * });
284
+ * ```
285
+ */
286
+ static async open(options) {
287
+ const response = await host.post(host.createUrl(`webviews`), options);
288
+ return new Webview(Number(await response.json()));
289
+ }
290
+ /**
291
+ * Gets the current webview instance if called from within a webview context.
292
+ *
293
+ * @returns The current Webview instance, or undefined if not in a webview context
294
+ */
295
+ static current() {
296
+ // @ts-expect-error -- CEF injects WEBVIEW_ENTITY on the window object
297
+ const entity = window.WEBVIEW_ENTITY;
298
+ return entity !== undefined ? new Webview(entity) : undefined;
299
+ }
300
+ }
301
+
302
+ export { Webview, isWebviewSourceHtml, isWebviewSourceInfoHtml, isWebviewSourceInfoLocal, isWebviewSourceInfoUrl, isWebviewSourceLocal, isWebviewSourceUrl, webviewSource };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@hmcs/sdk",
3
+ "version": "1.0.0",
4
+ "description": "TypeScript SDK for building mods and extensions for Desktop Homunculus",
5
+ "author": "notelm",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "sideEffects": false,
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./commands": {
19
+ "types": "./dist/commands.d.ts",
20
+ "import": "./dist/commands.js",
21
+ "require": "./dist/commands.cjs",
22
+ "default": "./dist/commands.js"
23
+ }
24
+ },
25
+ "engines": {
26
+ "node": ">=22.0.0"
27
+ },
28
+ "keywords": [],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/desktop-homunculus/typescript-sdk.git",
32
+ "directory": "packages/sdk"
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "dependencies": {
38
+ "eventsource": "^4.1.0",
39
+ "zod": "^3.25.0"
40
+ },
41
+ "devDependencies": {
42
+ "@eslint/js": "^9.39.2",
43
+ "@rollup/plugin-typescript": "^11.1.6",
44
+ "@types/node": "^20.19.33",
45
+ "eslint": "^9.39.2",
46
+ "rimraf": "^6.1.2",
47
+ "rollup": "^4.57.1",
48
+ "rollup-plugin-dts": "^6.3.0",
49
+ "tslib": "^2.8.1",
50
+ "typescript": "^5.9.3",
51
+ "typescript-eslint": "^8.56.0",
52
+ "vitest": "^3.0.0"
53
+ },
54
+ "scripts": {
55
+ "dev": "rollup -c --configPlugin typescript --watch",
56
+ "build": "rollup -c --configPlugin typescript",
57
+ "check-types": "tsc --noEmit",
58
+ "lint": "eslint .",
59
+ "test": "vitest run"
60
+ }
61
+ }