@openchamber/sdk 1.24.2 → 2.0.1
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/API.md +14 -3
- package/DOCUMENTATION.md +7 -2
- package/GUEST_SERVICES.md +88 -1
- package/README.md +6 -2
- package/dist/contract.d.ts +24 -4
- package/dist/contract.js +12 -0
- package/dist/host.d.ts +15 -0
- package/dist/host.js +15 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.js +6 -4
- package/dist/manifest.d.ts +67 -1
- package/dist/manifest.js +45 -0
- package/dist/parse.d.ts +32 -0
- package/dist/parse.js +65 -3
- package/dist/protocol.d.ts +18 -1
- package/dist/protocol.js +18 -2
- package/dist/schemas.d.ts +3 -0
- package/dist/schemas.js +2 -0
- package/dist/scrollbar-style.d.ts +10 -2
- package/dist/scrollbar-style.js +50 -5
- package/dist/service-provider-schemas.d.ts +25 -0
- package/dist/service-provider-schemas.js +12 -0
- package/dist/service-providers.d.ts +237 -0
- package/dist/service-providers.js +73 -0
- package/dist/service-surface-schemas.d.ts +173 -0
- package/dist/service-surface-schemas.js +59 -0
- package/dist/service-surface.d.ts +134 -0
- package/dist/service-surface.js +173 -0
- package/dist/ui/dom.js +3 -0
- package/dist/workspace.d.ts +1 -0
- package/package.json +1 -1
package/dist/manifest.d.ts
CHANGED
|
@@ -10,7 +10,24 @@ export type PanelContribution = {
|
|
|
10
10
|
name: string;
|
|
11
11
|
icon: string;
|
|
12
12
|
entry?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Beside a shared surface (`service.surface`), the panel page is docked to
|
|
15
|
+
* one edge of the host-drawn picture: a toolbar above it, an inspector
|
|
16
|
+
* below, a tool column beside it. `dock` is the edge (default
|
|
17
|
+
* `GUEST_SURFACE_DOCK_DEFAULT`) and `size` the page's thickness in CSS
|
|
18
|
+
* pixels across that edge (default `GUEST_SURFACE_DOCK_SIZE_DEFAULT`).
|
|
19
|
+
* Both only make sense with `entry` and `service.surface` together.
|
|
20
|
+
*/
|
|
21
|
+
dock?: GuestSurfaceDock;
|
|
22
|
+
size?: number;
|
|
13
23
|
};
|
|
24
|
+
export declare const GUEST_SURFACE_DOCKS: readonly ["top", "bottom", "left", "right"];
|
|
25
|
+
export type GuestSurfaceDock = (typeof GUEST_SURFACE_DOCKS)[number];
|
|
26
|
+
export declare const GUEST_SURFACE_DOCK_DEFAULT: GuestSurfaceDock;
|
|
27
|
+
/** Thickness in CSS px of a docked `panel.entry` when the manifest names none. */
|
|
28
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_DEFAULT = 40;
|
|
29
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_MIN = 24;
|
|
30
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_MAX = 480;
|
|
14
31
|
/** Sandboxed HTML loaded on demand for background actions and slash commands. */
|
|
15
32
|
export type BackgroundContribution = {
|
|
16
33
|
entry: string;
|
|
@@ -31,6 +48,26 @@ export type PageContribution = true | {
|
|
|
31
48
|
entry: string;
|
|
32
49
|
title?: string;
|
|
33
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* A section inside the chat's Work Status panel. `true` reuses `panel.entry`;
|
|
53
|
+
* the object form names its own package HTML, so an extension can ship only
|
|
54
|
+
* this section and no rail panel. `title` replaces `panel.name` on the section
|
|
55
|
+
* header; `height` is the starting frame height in CSS px before the guest
|
|
56
|
+
* reports its own through `setHeight`.
|
|
57
|
+
*/
|
|
58
|
+
export type StatusSectionContribution = true | {
|
|
59
|
+
entry: string;
|
|
60
|
+
title?: string;
|
|
61
|
+
height?: number;
|
|
62
|
+
};
|
|
63
|
+
/** Characters in a status section title. */
|
|
64
|
+
export declare const GUEST_STATUS_SECTION_TITLE_MAX = 60;
|
|
65
|
+
/** Smallest frame height the host gives a status section, in CSS px. */
|
|
66
|
+
export declare const GUEST_STATUS_SECTION_HEIGHT_MIN = 24;
|
|
67
|
+
/** Tallest frame height the host gives a status section; taller content scrolls inside the frame. */
|
|
68
|
+
export declare const GUEST_STATUS_SECTION_HEIGHT_MAX = 320;
|
|
69
|
+
/** Frame height before the manifest or the guest says otherwise. */
|
|
70
|
+
export declare const GUEST_STATUS_SECTION_HEIGHT_DEFAULT = 120;
|
|
34
71
|
export type GuestActionWhere = 'message' | 'session';
|
|
35
72
|
export type GuestActionRole = 'user' | 'assistant';
|
|
36
73
|
/** What a session action wants alongside the session id and title. */
|
|
@@ -199,18 +236,37 @@ export type PublicSocketBinding = {
|
|
|
199
236
|
resolved: string | null;
|
|
200
237
|
override: string | null;
|
|
201
238
|
};
|
|
239
|
+
/**
|
|
240
|
+
* Host roles a service can stand in for. `browser` answers the agent's
|
|
241
|
+
* `browser.*` actions in place of the in-app browser view; the contract is in
|
|
242
|
+
* `service-providers.ts`. A service that provides a role needs no panel or
|
|
243
|
+
* background entry: the host starts it on the first action.
|
|
244
|
+
*/
|
|
245
|
+
export declare const GUEST_SERVICE_PROVIDES: readonly ["browser"];
|
|
246
|
+
export type GuestServiceProvides = (typeof GUEST_SERVICE_PROVIDES)[number];
|
|
202
247
|
export type ServiceContribution = {
|
|
203
248
|
entry: string;
|
|
204
249
|
runtime: 'host';
|
|
205
250
|
permissions?: ServicePermissions;
|
|
251
|
+
provides?: GuestServiceProvides[];
|
|
252
|
+
/**
|
|
253
|
+
* The service shows a live surface (frames out, input in) that the host
|
|
254
|
+
* draws in this extension's rail panel; see `service-surface.ts`. With
|
|
255
|
+
* `panel.entry` too, that page is docked to one edge of the picture (see
|
|
256
|
+
* `PanelContribution.dock`); without it, the panel is the surface alone.
|
|
257
|
+
*/
|
|
258
|
+
surface?: true;
|
|
206
259
|
};
|
|
207
260
|
/** Catalog card for a local service. Drops nothing secret; grant is host state. */
|
|
208
261
|
export type PublicService = {
|
|
209
262
|
runtime: 'host';
|
|
210
263
|
permissions?: PublicServicePermissions;
|
|
211
264
|
socketBindings?: PublicSocketBinding[];
|
|
265
|
+
provides?: GuestServiceProvides[];
|
|
266
|
+
surface?: true;
|
|
212
267
|
granted: boolean;
|
|
213
268
|
};
|
|
269
|
+
export declare const serviceProvides: (service: Pick<ServiceContribution, "provides"> | undefined, role: GuestServiceProvides) => boolean;
|
|
214
270
|
/**
|
|
215
271
|
* What a guest may do beyond drawing its own panel. The user approves the
|
|
216
272
|
* full list once, when the package is installed; a later package that asks
|
|
@@ -240,6 +296,8 @@ export type OpenChamberContributes = {
|
|
|
240
296
|
background?: BackgroundContribution;
|
|
241
297
|
attach?: AttachContribution;
|
|
242
298
|
page?: PageContribution;
|
|
299
|
+
/** A section in the chat's Work Status panel. */
|
|
300
|
+
statusSection?: StatusSectionContribution;
|
|
243
301
|
capabilities?: DeclaredGuestCapability[];
|
|
244
302
|
integration?: IntegrationContribution;
|
|
245
303
|
service?: ServiceContribution;
|
|
@@ -274,6 +332,14 @@ export declare const resolveAttachMode: (attach: AttachContribution | undefined)
|
|
|
274
332
|
*/
|
|
275
333
|
export declare const resolveAttachEntry: (contributes: Pick<OpenChamberContributes, "attach">) => string | null;
|
|
276
334
|
export declare const resolvePageEntry: (contributes: Pick<OpenChamberContributes, "panel" | "page">) => string | null;
|
|
335
|
+
/**
|
|
336
|
+
* The HTML the Work Status section loads: `panel.entry` for `true`, the
|
|
337
|
+
* object's own `entry` otherwise, `null` when nothing is declared or `true`
|
|
338
|
+
* has no panel page to reuse.
|
|
339
|
+
*/
|
|
340
|
+
export declare const resolveStatusSectionEntry: (contributes: Pick<OpenChamberContributes, "panel" | "statusSection">) => string | null;
|
|
341
|
+
/** Clamp a requested status section height to what the host allows. */
|
|
342
|
+
export declare const clampStatusSectionHeight: (height: number) => number;
|
|
277
343
|
export type OpenChamberEngines = {
|
|
278
344
|
openchamber: string;
|
|
279
345
|
};
|
|
@@ -282,7 +348,7 @@ export type OpenChamberManifest = {
|
|
|
282
348
|
engines?: OpenChamberEngines;
|
|
283
349
|
contributes: OpenChamberContributes;
|
|
284
350
|
};
|
|
285
|
-
export type ParseManifestErrorCode = 'not-object' | 'missing-openchamber' | 'unsupported-api-version' | 'invalid-engines' | 'invalid-version' | 'missing-panel' | 'invalid-panel' | 'invalid-panel-id' | 'invalid-panel-name' | 'invalid-panel-icon' | 'invalid-panel-entry' | 'invalid-background' | 'invalid-attach' | 'invalid-page' | 'invalid-capabilities' | 'invalid-integration' | 'invalid-service' | 'invalid-filesystem' | 'invalid-actions' | 'invalid-commands' | 'invalid-tools';
|
|
351
|
+
export type ParseManifestErrorCode = 'not-object' | 'missing-openchamber' | 'unsupported-api-version' | 'invalid-engines' | 'invalid-version' | 'missing-panel' | 'invalid-panel' | 'invalid-panel-id' | 'invalid-panel-name' | 'invalid-panel-icon' | 'invalid-panel-entry' | 'invalid-background' | 'invalid-attach' | 'invalid-page' | 'invalid-status-section' | 'invalid-capabilities' | 'invalid-integration' | 'invalid-service' | 'invalid-filesystem' | 'invalid-actions' | 'invalid-commands' | 'invalid-tools';
|
|
286
352
|
export type ParseManifestFailure = {
|
|
287
353
|
ok: false;
|
|
288
354
|
code: ParseManifestErrorCode;
|
package/dist/manifest.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
export const PANEL_ID = /^[a-z][a-z0-9-]*$/;
|
|
2
|
+
export const GUEST_SURFACE_DOCKS = ['top', 'bottom', 'left', 'right'];
|
|
3
|
+
export const GUEST_SURFACE_DOCK_DEFAULT = 'top';
|
|
4
|
+
/** Thickness in CSS px of a docked `panel.entry` when the manifest names none. */
|
|
5
|
+
export const GUEST_SURFACE_DOCK_SIZE_DEFAULT = 40;
|
|
6
|
+
export const GUEST_SURFACE_DOCK_SIZE_MIN = 24;
|
|
7
|
+
export const GUEST_SURFACE_DOCK_SIZE_MAX = 480;
|
|
8
|
+
/** Characters in a status section title. */
|
|
9
|
+
export const GUEST_STATUS_SECTION_TITLE_MAX = 60;
|
|
10
|
+
/** Smallest frame height the host gives a status section, in CSS px. */
|
|
11
|
+
export const GUEST_STATUS_SECTION_HEIGHT_MIN = 24;
|
|
12
|
+
/** Tallest frame height the host gives a status section; taller content scrolls inside the frame. */
|
|
13
|
+
export const GUEST_STATUS_SECTION_HEIGHT_MAX = 320;
|
|
14
|
+
/** Frame height before the manifest or the guest says otherwise. */
|
|
15
|
+
export const GUEST_STATUS_SECTION_HEIGHT_DEFAULT = 120;
|
|
2
16
|
/** How many `contributes.actions` entries a package may declare. */
|
|
3
17
|
export const GUEST_ACTIONS_MAX = 8;
|
|
4
18
|
/** Characters in an action label. */
|
|
@@ -71,6 +85,14 @@ export const resolveIntegrationApi = (integration) => {
|
|
|
71
85
|
}
|
|
72
86
|
return null;
|
|
73
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* Host roles a service can stand in for. `browser` answers the agent's
|
|
90
|
+
* `browser.*` actions in place of the in-app browser view; the contract is in
|
|
91
|
+
* `service-providers.ts`. A service that provides a role needs no panel or
|
|
92
|
+
* background entry: the host starts it on the first action.
|
|
93
|
+
*/
|
|
94
|
+
export const GUEST_SERVICE_PROVIDES = ['browser'];
|
|
95
|
+
export const serviceProvides = (service, role) => Boolean(service?.provides?.includes(role));
|
|
74
96
|
/**
|
|
75
97
|
* What a guest may do beyond drawing its own panel. The user approves the
|
|
76
98
|
* full list once, when the package is installed; a later package that asks
|
|
@@ -152,6 +174,23 @@ export const resolvePageEntry = (contributes) => {
|
|
|
152
174
|
return null;
|
|
153
175
|
return contributes.page === true ? contributes.panel.entry : contributes.page.entry;
|
|
154
176
|
};
|
|
177
|
+
/**
|
|
178
|
+
* The HTML the Work Status section loads: `panel.entry` for `true`, the
|
|
179
|
+
* object's own `entry` otherwise, `null` when nothing is declared or `true`
|
|
180
|
+
* has no panel page to reuse.
|
|
181
|
+
*/
|
|
182
|
+
export const resolveStatusSectionEntry = (contributes) => {
|
|
183
|
+
const section = contributes.statusSection;
|
|
184
|
+
if (!section)
|
|
185
|
+
return null;
|
|
186
|
+
return section === true ? contributes.panel.entry ?? null : section.entry;
|
|
187
|
+
};
|
|
188
|
+
/** Clamp a requested status section height to what the host allows. */
|
|
189
|
+
export const clampStatusSectionHeight = (height) => {
|
|
190
|
+
if (!Number.isFinite(height))
|
|
191
|
+
return GUEST_STATUS_SECTION_HEIGHT_DEFAULT;
|
|
192
|
+
return Math.min(GUEST_STATUS_SECTION_HEIGHT_MAX, Math.max(GUEST_STATUS_SECTION_HEIGHT_MIN, Math.round(height)));
|
|
193
|
+
};
|
|
155
194
|
export const isSafeAssetPath = (value) => {
|
|
156
195
|
if (value.includes('\0') || value.includes('\\') || value.startsWith('/') || value.includes('://')) {
|
|
157
196
|
return false;
|
|
@@ -217,6 +256,12 @@ export const toPublicService = (service, granted, socketBindings) => {
|
|
|
217
256
|
override: binding.override,
|
|
218
257
|
}));
|
|
219
258
|
}
|
|
259
|
+
if (service.provides && service.provides.length > 0) {
|
|
260
|
+
next.provides = [...service.provides];
|
|
261
|
+
}
|
|
262
|
+
if (service.surface) {
|
|
263
|
+
next.surface = true;
|
|
264
|
+
}
|
|
220
265
|
return next;
|
|
221
266
|
};
|
|
222
267
|
/** Guest package version: `1.2.3`, optional prerelease / build. */
|
package/dist/parse.d.ts
CHANGED
|
@@ -11,6 +11,13 @@ export declare const openChamberManifestSchema: z.ZodObject<{
|
|
|
11
11
|
name: z.ZodString;
|
|
12
12
|
icon: z.ZodString;
|
|
13
13
|
entry: z.ZodOptional<z.ZodString>;
|
|
14
|
+
dock: z.ZodOptional<z.ZodEnum<{
|
|
15
|
+
top: "top";
|
|
16
|
+
bottom: "bottom";
|
|
17
|
+
left: "left";
|
|
18
|
+
right: "right";
|
|
19
|
+
}>>;
|
|
20
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
14
21
|
}, z.core.$strip>;
|
|
15
22
|
background: z.ZodOptional<z.ZodObject<{
|
|
16
23
|
entry: z.ZodString;
|
|
@@ -29,6 +36,11 @@ export declare const openChamberManifestSchema: z.ZodObject<{
|
|
|
29
36
|
entry: z.ZodString;
|
|
30
37
|
title: z.ZodOptional<z.ZodString>;
|
|
31
38
|
}, z.core.$strip>]>>;
|
|
39
|
+
statusSection: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
|
|
40
|
+
entry: z.ZodString;
|
|
41
|
+
title: z.ZodOptional<z.ZodString>;
|
|
42
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
43
|
+
}, z.core.$strip>]>>;
|
|
32
44
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
33
45
|
sessions: "sessions";
|
|
34
46
|
prompt: "prompt";
|
|
@@ -97,6 +109,10 @@ export declare const openChamberManifestSchema: z.ZodObject<{
|
|
|
97
109
|
sockets?: SocketBinding[] | undefined;
|
|
98
110
|
exec?: string[] | undefined;
|
|
99
111
|
}>>>;
|
|
112
|
+
provides: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
113
|
+
browser: "browser";
|
|
114
|
+
}>>>;
|
|
115
|
+
surface: z.ZodOptional<z.ZodLiteral<true>>;
|
|
100
116
|
}, z.core.$strip>>;
|
|
101
117
|
filesystem: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
102
118
|
actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -156,6 +172,13 @@ export declare const packageManifestSchema: z.ZodObject<{
|
|
|
156
172
|
name: z.ZodString;
|
|
157
173
|
icon: z.ZodString;
|
|
158
174
|
entry: z.ZodOptional<z.ZodString>;
|
|
175
|
+
dock: z.ZodOptional<z.ZodEnum<{
|
|
176
|
+
top: "top";
|
|
177
|
+
bottom: "bottom";
|
|
178
|
+
left: "left";
|
|
179
|
+
right: "right";
|
|
180
|
+
}>>;
|
|
181
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
159
182
|
}, z.core.$strip>;
|
|
160
183
|
background: z.ZodOptional<z.ZodObject<{
|
|
161
184
|
entry: z.ZodString;
|
|
@@ -174,6 +197,11 @@ export declare const packageManifestSchema: z.ZodObject<{
|
|
|
174
197
|
entry: z.ZodString;
|
|
175
198
|
title: z.ZodOptional<z.ZodString>;
|
|
176
199
|
}, z.core.$strip>]>>;
|
|
200
|
+
statusSection: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodObject<{
|
|
201
|
+
entry: z.ZodString;
|
|
202
|
+
title: z.ZodOptional<z.ZodString>;
|
|
203
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
204
|
+
}, z.core.$strip>]>>;
|
|
177
205
|
capabilities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
178
206
|
sessions: "sessions";
|
|
179
207
|
prompt: "prompt";
|
|
@@ -242,6 +270,10 @@ export declare const packageManifestSchema: z.ZodObject<{
|
|
|
242
270
|
sockets?: SocketBinding[] | undefined;
|
|
243
271
|
exec?: string[] | undefined;
|
|
244
272
|
}>>>;
|
|
273
|
+
provides: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
274
|
+
browser: "browser";
|
|
275
|
+
}>>>;
|
|
276
|
+
surface: z.ZodOptional<z.ZodLiteral<true>>;
|
|
245
277
|
}, z.core.$strip>>;
|
|
246
278
|
filesystem: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
247
279
|
actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
package/dist/parse.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { OPENCHAMBER_SDK_MANIFEST_API_VERSIONS } from "./api-version.js";
|
|
3
3
|
import { OPENCHAMBER_ENGINE_PATTERN } from "./host-version.js";
|
|
4
|
-
import { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, PANEL_ID, hasGuestPage, isGuestFilesystemPattern, isGuestPackageSvgIcon, isSafeAssetPath, resolveIntegrationAuth, } from "./manifest.js";
|
|
4
|
+
import { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_SERVICE_PROVIDES, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, PANEL_ID, hasGuestPage, isGuestFilesystemPattern, isGuestPackageSvgIcon, isSafeAssetPath, resolveIntegrationAuth, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, GUEST_STATUS_SECTION_HEIGHT_MAX, GUEST_STATUS_SECTION_HEIGHT_MIN, GUEST_STATUS_SECTION_TITLE_MAX, } from "./manifest.js";
|
|
5
5
|
const isPanelIcon = (value) => (PANEL_ID.test(value) || isGuestPackageSvgIcon(value));
|
|
6
6
|
const isHttpsUrl = (value) => {
|
|
7
7
|
try {
|
|
@@ -48,6 +48,8 @@ const panelSchema = z.object({
|
|
|
48
48
|
name: z.string().trim().min(1),
|
|
49
49
|
icon: z.string().trim().refine(isPanelIcon),
|
|
50
50
|
entry: z.string().trim().refine(isSafeAssetPath).optional(),
|
|
51
|
+
dock: z.enum(GUEST_SURFACE_DOCKS).optional(),
|
|
52
|
+
size: z.number().int().min(GUEST_SURFACE_DOCK_SIZE_MIN).max(GUEST_SURFACE_DOCK_SIZE_MAX).optional(),
|
|
51
53
|
});
|
|
52
54
|
const integrationSettingSchema = z.object({
|
|
53
55
|
id: z.string().trim().regex(PANEL_ID),
|
|
@@ -140,6 +142,10 @@ const serviceSchema = z.object({
|
|
|
140
142
|
entry: z.string().trim().refine(isSafeAssetPath),
|
|
141
143
|
runtime: z.literal('host'),
|
|
142
144
|
permissions: servicePermissionsSchema.optional(),
|
|
145
|
+
provides: z.array(z.enum(GUEST_SERVICE_PROVIDES)).min(1).max(GUEST_SERVICE_PROVIDES.length)
|
|
146
|
+
.refine((roles) => new Set(roles).size === roles.length, { message: 'provides entries must be unique' })
|
|
147
|
+
.optional(),
|
|
148
|
+
surface: z.literal(true).optional(),
|
|
143
149
|
});
|
|
144
150
|
const uniqueBy = (items, key) => (new Set(items.map(key)).size === items.length);
|
|
145
151
|
// `roles` only means something on a message action and `payload` only on a
|
|
@@ -187,6 +193,11 @@ const contributesSchema = z.object({
|
|
|
187
193
|
entry: z.string().trim().refine(isSafeAssetPath),
|
|
188
194
|
title: z.string().trim().min(1).max(200).optional(),
|
|
189
195
|
})]).optional(),
|
|
196
|
+
statusSection: z.union([z.literal(true), z.object({
|
|
197
|
+
entry: z.string().trim().refine((value) => isSafeAssetPath(value) && value.toLowerCase().endsWith('.html')),
|
|
198
|
+
title: z.string().trim().min(1).max(GUEST_STATUS_SECTION_TITLE_MAX).optional(),
|
|
199
|
+
height: z.number().int().min(GUEST_STATUS_SECTION_HEIGHT_MIN).max(GUEST_STATUS_SECTION_HEIGHT_MAX).optional(),
|
|
200
|
+
})]).optional(),
|
|
190
201
|
capabilities: z.array(z.enum(DECLARED_GUEST_CAPABILITIES)).max(8).optional(),
|
|
191
202
|
integration: integrationSchema.optional(),
|
|
192
203
|
service: serviceSchema.optional(),
|
|
@@ -208,7 +219,10 @@ const runtimeContributions = (contributes) => {
|
|
|
208
219
|
declared.push('capabilities');
|
|
209
220
|
if (contributes.integration !== undefined)
|
|
210
221
|
declared.push('integration');
|
|
211
|
-
|
|
222
|
+
// A service the host starts itself (it provides a role or a surface) needs
|
|
223
|
+
// no frame; one that only answers a panel's `serviceRequest` has no caller
|
|
224
|
+
// without one.
|
|
225
|
+
if (contributes.service !== undefined && !contributes.service.provides?.length && !contributes.service.surface)
|
|
212
226
|
declared.push('service');
|
|
213
227
|
if (contributes.filesystem !== undefined)
|
|
214
228
|
declared.push('filesystem');
|
|
@@ -224,6 +238,23 @@ export const openChamberManifestSchema = z.object({
|
|
|
224
238
|
openchamber: z.string().trim().regex(OPENCHAMBER_ENGINE_PATTERN),
|
|
225
239
|
}).strict().optional(),
|
|
226
240
|
contributes: contributesSchema.superRefine((contributes, ctx) => {
|
|
241
|
+
// Docking only means something for a page beside a shared surface.
|
|
242
|
+
const docked = contributes.panel.dock !== undefined || contributes.panel.size !== undefined;
|
|
243
|
+
if (docked && !(contributes.service?.surface && hasGuestPage(contributes))) {
|
|
244
|
+
ctx.addIssue({
|
|
245
|
+
code: 'custom', path: ['panel', 'dock'],
|
|
246
|
+
message: 'panel.dock and panel.size place panel.entry beside service.surface; they need both.',
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// `true` reuses the panel page; without one the section has nothing to load.
|
|
251
|
+
if (contributes.statusSection === true && !hasGuestPage(contributes)) {
|
|
252
|
+
ctx.addIssue({
|
|
253
|
+
code: 'custom', path: ['statusSection'],
|
|
254
|
+
message: 'contributes.statusSection: true reuses panel.entry; without a panel page, name its own { "entry": "<package .html>" }.',
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
227
258
|
if (hasGuestPage(contributes))
|
|
228
259
|
return;
|
|
229
260
|
if (contributes.background) {
|
|
@@ -241,6 +272,26 @@ export const openChamberManifestSchema = z.object({
|
|
|
241
272
|
});
|
|
242
273
|
return;
|
|
243
274
|
}
|
|
275
|
+
if (contributes.statusSection) {
|
|
276
|
+
// The section frame runs code and may use what the package is granted,
|
|
277
|
+
// but it only hosts itself: things the host opens or invokes elsewhere
|
|
278
|
+
// need a panel or background entry.
|
|
279
|
+
const needsFrame = [];
|
|
280
|
+
if (contributes.page !== undefined)
|
|
281
|
+
needsFrame.push('page');
|
|
282
|
+
if (contributes.attach !== undefined && contributes.attach !== false)
|
|
283
|
+
needsFrame.push('attach');
|
|
284
|
+
if (contributes.actions !== undefined)
|
|
285
|
+
needsFrame.push('actions');
|
|
286
|
+
if (contributes.commands !== undefined)
|
|
287
|
+
needsFrame.push('commands');
|
|
288
|
+
if (needsFrame.length > 0)
|
|
289
|
+
ctx.addIssue({
|
|
290
|
+
code: 'custom', path: ['panel'],
|
|
291
|
+
message: `${needsFrame.map((key) => `contributes.${key}`).join(', ')} needs panel.entry or background.entry; a status section only hosts itself.`,
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
244
295
|
const needsRuntime = runtimeContributions(contributes);
|
|
245
296
|
if (needsRuntime.length === 0)
|
|
246
297
|
return;
|
|
@@ -301,6 +352,12 @@ const failureFromIssue = (issue) => {
|
|
|
301
352
|
return fail('invalid-panel-icon', 'panel.icon must be a Remixicon name or a package .svg path.');
|
|
302
353
|
case 'contributes.panel.entry':
|
|
303
354
|
return fail('invalid-panel-entry', 'panel.entry must be a relative path inside the package.');
|
|
355
|
+
case 'contributes.panel.dock':
|
|
356
|
+
return fail('invalid-panel', issue.code === 'custom'
|
|
357
|
+
? issue.message
|
|
358
|
+
: `panel.dock is one of ${GUEST_SURFACE_DOCKS.join(', ')}.`);
|
|
359
|
+
case 'contributes.panel.size':
|
|
360
|
+
return fail('invalid-panel', `panel.size is a whole number of CSS pixels from ${GUEST_SURFACE_DOCK_SIZE_MIN} to ${GUEST_SURFACE_DOCK_SIZE_MAX}.`);
|
|
304
361
|
case 'contributes.capabilities':
|
|
305
362
|
return fail('invalid-capabilities', 'contributes.capabilities may list "prompt", "sessions", and "files".');
|
|
306
363
|
default:
|
|
@@ -309,6 +366,11 @@ const failureFromIssue = (issue) => {
|
|
|
309
366
|
if (path === 'contributes.page' || path.startsWith('contributes.page.')) {
|
|
310
367
|
return fail('invalid-page', 'contributes.page must be true or { entry: "<package HTML>", title?: "Page title" }.');
|
|
311
368
|
}
|
|
369
|
+
if (path === 'contributes.statusSection' || path.startsWith('contributes.statusSection.')) {
|
|
370
|
+
return fail('invalid-status-section', issue.code === 'custom'
|
|
371
|
+
? issue.message
|
|
372
|
+
: `contributes.statusSection must be true or { entry: "<package .html>", title?: "1 to ${GUEST_STATUS_SECTION_TITLE_MAX} characters", height?: ${GUEST_STATUS_SECTION_HEIGHT_MIN} to ${GUEST_STATUS_SECTION_HEIGHT_MAX} }.`);
|
|
373
|
+
}
|
|
312
374
|
if (path === 'contributes.background' || path.startsWith('contributes.background.')) {
|
|
313
375
|
return fail('invalid-background', 'contributes.background needs an entry ending in .html inside the package.');
|
|
314
376
|
}
|
|
@@ -334,7 +396,7 @@ const failureFromIssue = (issue) => {
|
|
|
334
396
|
return fail('invalid-integration', 'contributes.integration needs a name, description, and oauth, token, or host.');
|
|
335
397
|
}
|
|
336
398
|
if (path.startsWith('contributes.service')) {
|
|
337
|
-
return fail('invalid-service', 'contributes.service needs entry, runtime "host", and optional
|
|
399
|
+
return fail('invalid-service', 'contributes.service needs entry, runtime "host", optional permissions, optional provides ("browser"), and optional surface (true).');
|
|
338
400
|
}
|
|
339
401
|
return fail('missing-panel', 'contributes.panel is required.');
|
|
340
402
|
};
|
package/dist/protocol.d.ts
CHANGED
|
@@ -144,6 +144,7 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
144
144
|
dialog: "dialog";
|
|
145
145
|
page: "page";
|
|
146
146
|
background: "background";
|
|
147
|
+
status: "status";
|
|
147
148
|
}>;
|
|
148
149
|
connection: z.ZodObject<{
|
|
149
150
|
connected: z.ZodBoolean;
|
|
@@ -633,7 +634,7 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
633
634
|
id: string;
|
|
634
635
|
ok: false;
|
|
635
636
|
error: string;
|
|
636
|
-
code: "HOST_UNAVAILABLE" | "HOST_TIMEOUT" | "HOST_REJECTED" | "DISCONNECTED" | "DISABLED" | "BAD_PATH" | "NO_INTEGRATION" | "NO_SERVICE" | "SERVICE_FAILED" | "NO_SESSION" | "SESSION_BUSY" | "NOT_GRANTED" | "NO_DIRECTORY" | "NOT_FOUND" | "FILE_TOO_LARGE" | "DENIED" | "NO_MODEL" | "MODEL_FAILED";
|
|
637
|
+
code: "HOST_UNAVAILABLE" | "HOST_TIMEOUT" | "HOST_REJECTED" | "DISCONNECTED" | "DISABLED" | "BAD_PATH" | "NO_INTEGRATION" | "NO_SERVICE" | "SERVICE_FAILED" | "NO_SESSION" | "SESSION_BUSY" | "NOT_GRANTED" | "NO_DIRECTORY" | "NOT_FOUND" | "FILE_TOO_LARGE" | "DENIED" | "NO_MODEL" | "MODEL_FAILED" | "UNSUPPORTED";
|
|
637
638
|
payload?: undefined;
|
|
638
639
|
}, {
|
|
639
640
|
type: "result";
|
|
@@ -1114,6 +1115,22 @@ export declare const guestMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
1114
1115
|
}, z.core.$strip>;
|
|
1115
1116
|
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
1116
1117
|
v: z.ZodLiteral<1>;
|
|
1118
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1119
|
+
type: z.ZodLiteral<"open-commit">;
|
|
1120
|
+
id: z.ZodString;
|
|
1121
|
+
payload: z.ZodObject<{
|
|
1122
|
+
sha: z.ZodString;
|
|
1123
|
+
}, z.core.$strip>;
|
|
1124
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
1125
|
+
v: z.ZodLiteral<1>;
|
|
1126
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1127
|
+
type: z.ZodLiteral<"resize">;
|
|
1128
|
+
id: z.ZodString;
|
|
1129
|
+
payload: z.ZodObject<{
|
|
1130
|
+
height: z.ZodNumber;
|
|
1131
|
+
}, z.core.$strip>;
|
|
1132
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
1133
|
+
v: z.ZodLiteral<1>;
|
|
1117
1134
|
}, z.core.$strip>, z.ZodObject<{
|
|
1118
1135
|
type: z.ZodLiteral<"resolve-result">;
|
|
1119
1136
|
id: z.ZodString;
|
package/dist/protocol.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { guestSessionWorktreeSchema, guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceQuerySchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
|
|
3
3
|
import { OPENCHAMBER_SDK_API_VERSION, OPENCHAMBER_SDK_CHANNEL } from "./api-version.js";
|
|
4
|
-
import { SERVICE_STATUS_VALUES, ATTACH_PROVIDER_ID, GUEST_ACCOUNT_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_BADGE_MAX, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_FILE_PATH_MAX, GUEST_FILE_STAT_KINDS, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_SETTING_VALUE_MAX, GUEST_TOAST_MAX, SESSION_LIFECYCLE_PHASES, SETTING_KEY, START_SESSION_SENT, isGuestFilePath, isGuestRequestPath, resolveHostRequestErrorCode, } from "./contract.js";
|
|
4
|
+
import { SERVICE_STATUS_VALUES, ATTACH_PROVIDER_ID, GUEST_ACCOUNT_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_BADGE_MAX, GUEST_FRAME_HEIGHT_MAX, GUEST_COMMIT_SHA, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_FILE_PATH_MAX, GUEST_FILE_STAT_KINDS, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_SETTING_VALUE_MAX, GUEST_TOAST_MAX, SESSION_LIFECYCLE_PHASES, SETTING_KEY, START_SESSION_SENT, isGuestFilePath, isGuestRequestPath, resolveHostRequestErrorCode, } from "./contract.js";
|
|
5
5
|
const envelope = {
|
|
6
6
|
channel: z.literal(OPENCHAMBER_SDK_CHANNEL),
|
|
7
7
|
v: z.literal(OPENCHAMBER_SDK_API_VERSION),
|
|
@@ -146,7 +146,7 @@ const readyPayloadSchema = z.object({
|
|
|
146
146
|
locale: z.string().min(1),
|
|
147
147
|
directory: z.string().nullable(),
|
|
148
148
|
session: sessionSnapshotSchema,
|
|
149
|
-
surface: z.enum(['panel', 'dialog', 'page', 'background']),
|
|
149
|
+
surface: z.enum(['panel', 'dialog', 'page', 'background', 'status']),
|
|
150
150
|
connection: guestConnectionSchema,
|
|
151
151
|
settings: guestSettingsSchema,
|
|
152
152
|
item: guestItemSchema,
|
|
@@ -443,6 +443,22 @@ export const guestMessageSchema = z.discriminatedUnion('type', [
|
|
|
443
443
|
count: z.number().int().min(0).max(GUEST_BADGE_MAX).nullable(),
|
|
444
444
|
}),
|
|
445
445
|
}),
|
|
446
|
+
z.object({
|
|
447
|
+
...envelope,
|
|
448
|
+
type: z.literal('open-commit'),
|
|
449
|
+
id: z.string().min(1),
|
|
450
|
+
payload: z.object({
|
|
451
|
+
sha: z.string().regex(GUEST_COMMIT_SHA),
|
|
452
|
+
}),
|
|
453
|
+
}),
|
|
454
|
+
z.object({
|
|
455
|
+
...envelope,
|
|
456
|
+
type: z.literal('resize'),
|
|
457
|
+
id: z.string().min(1),
|
|
458
|
+
payload: z.object({
|
|
459
|
+
height: z.number().int().min(0).max(GUEST_FRAME_HEIGHT_MAX),
|
|
460
|
+
}),
|
|
461
|
+
}),
|
|
446
462
|
z.object({
|
|
447
463
|
...envelope,
|
|
448
464
|
type: z.literal('resolve-result'),
|
package/dist/schemas.d.ts
CHANGED
|
@@ -2,3 +2,6 @@ export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSche
|
|
|
2
2
|
export type { ManifestDocument } from './parse.ts';
|
|
3
3
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from './workspace-schemas.ts';
|
|
4
4
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from './protocol.ts';
|
|
5
|
+
export { browserControlActionSchema, browserProviderResultSchema } from './service-provider-schemas.ts';
|
|
6
|
+
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from './service-surface-schemas.ts';
|
|
7
|
+
export type { SurfaceHostMessage, SurfaceViewerMessage } from './service-surface-schemas.ts';
|
package/dist/schemas.js
CHANGED
|
@@ -4,3 +4,5 @@
|
|
|
4
4
|
export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from "./parse.js";
|
|
5
5
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
|
|
6
6
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from "./protocol.js";
|
|
7
|
+
export { browserControlActionSchema, browserProviderResultSchema } from "./service-provider-schemas.js";
|
|
8
|
+
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from "./service-surface-schemas.js";
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export declare const GUEST_SCROLLBAR_CSS = "\n:root {\n --oc-scrollbar-thumb: color-mix(in srgb, var(--oc-muted, currentColor) 40%, transparent);\n --oc-scrollbar-thumb-hover: color-mix(in srgb, var(--oc-muted, currentColor) 65%, transparent);\n scrollbar-gutter: stable;\n}\n* {\n scrollbar-width: thin;\n scrollbar-color: transparent transparent;\n}\n:hover, [data-oc-scrolling] {\n scrollbar-color: var(--oc-scrollbar-thumb) transparent;\n}\n/* Chromium's standard scrollbar properties otherwise override its pseudo-elements. */\n@supports selector(::-webkit-scrollbar) {\n *, :hover, [data-oc-scrolling] { scrollbar-width: auto; scrollbar-color: auto; }\n ::-webkit-scrollbar { width: 6px; height: 6px; background: transparent; }\n ::-webkit-scrollbar-track { background: transparent; }\n ::-webkit-scrollbar-thumb {\n background: transparent;\n border-radius: 999px;\n min-width: 24px;\n min-height: 24px;\n }\n :hover::-webkit-scrollbar-thumb, [data-oc-scrolling]::-webkit-scrollbar-thumb { background: var(--oc-scrollbar-thumb); }\n ::-webkit-scrollbar-thumb:hover { background: var(--oc-scrollbar-thumb-hover); }\n ::-webkit-scrollbar-corner { background: transparent; }\n ::-webkit-scrollbar-button { display: none; width: 0; height: 0; }\n}\n@media (forced-colors: active) {\n *, :hover, [data-oc-scrolling] { scrollbar-color: auto; }\n ::-webkit-scrollbar-thumb, ::-webkit-scrollbar-thumb:hover { background: CanvasText; }\n}\n";
|
|
2
|
+
/**
|
|
3
|
+
* Marks whichever element scrolls (the root for document scrolling) with
|
|
4
|
+
* `GUEST_SCROLLING_ATTRIBUTE` and clears it once scrolling has been quiet for
|
|
5
|
+
* the hide delay. One capturing, passive listener per document; installing
|
|
6
|
+
* twice is a no-op. Self-contained on purpose: the server inlines its source.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installGuestScrollbarActivity(doc: Document): void;
|
|
9
|
+
/** Inline script form of `installGuestScrollbarActivity`, appended by the host to served guest HTML. */
|
|
10
|
+
export declare const GUEST_SCROLLBAR_SCRIPT: string;
|
package/dist/scrollbar-style.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Scrollbars in extension documents behave like the app's own overlay
|
|
3
|
+
* scrollbars: invisible at rest, shown while the pointer is over the
|
|
4
|
+
* scrolling element or while it scrolls, then hidden again after
|
|
5
|
+
* one second (the app's `OverlayScrollbar` default).
|
|
6
|
+
*
|
|
7
|
+
* Hover works from CSS alone. "While it scrolls" needs the attribute set by
|
|
8
|
+
* `installGuestScrollbarActivity`; without it (an authored CSP that blocks the
|
|
9
|
+
* inline script), scrollbars still appear on hover.
|
|
10
|
+
*
|
|
11
|
+
* Shared by served extension documents and the UI kit, including older guest bundles.
|
|
12
|
+
*/
|
|
13
|
+
/** Attribute present on an element for a moment after it scrolled. */
|
|
14
|
+
const GUEST_SCROLLING_ATTRIBUTE = 'data-oc-scrolling';
|
|
2
15
|
export const GUEST_SCROLLBAR_CSS = `
|
|
3
16
|
:root {
|
|
4
17
|
--oc-scrollbar-thumb: color-mix(in srgb, var(--oc-muted, currentColor) 40%, transparent);
|
|
@@ -7,26 +20,58 @@ export const GUEST_SCROLLBAR_CSS = `
|
|
|
7
20
|
}
|
|
8
21
|
* {
|
|
9
22
|
scrollbar-width: thin;
|
|
23
|
+
scrollbar-color: transparent transparent;
|
|
24
|
+
}
|
|
25
|
+
:hover, [${GUEST_SCROLLING_ATTRIBUTE}] {
|
|
10
26
|
scrollbar-color: var(--oc-scrollbar-thumb) transparent;
|
|
11
27
|
}
|
|
12
28
|
/* Chromium's standard scrollbar properties otherwise override its pseudo-elements. */
|
|
13
29
|
@supports selector(::-webkit-scrollbar) {
|
|
14
|
-
|
|
30
|
+
*, :hover, [${GUEST_SCROLLING_ATTRIBUTE}] { scrollbar-width: auto; scrollbar-color: auto; }
|
|
15
31
|
::-webkit-scrollbar { width: 6px; height: 6px; background: transparent; }
|
|
16
|
-
:root::-webkit-scrollbar, body::-webkit-scrollbar { background: var(--oc-bg, inherit); }
|
|
17
32
|
::-webkit-scrollbar-track { background: transparent; }
|
|
18
33
|
::-webkit-scrollbar-thumb {
|
|
19
|
-
background:
|
|
34
|
+
background: transparent;
|
|
20
35
|
border-radius: 999px;
|
|
21
36
|
min-width: 24px;
|
|
22
37
|
min-height: 24px;
|
|
23
38
|
}
|
|
39
|
+
:hover::-webkit-scrollbar-thumb, [${GUEST_SCROLLING_ATTRIBUTE}]::-webkit-scrollbar-thumb { background: var(--oc-scrollbar-thumb); }
|
|
24
40
|
::-webkit-scrollbar-thumb:hover { background: var(--oc-scrollbar-thumb-hover); }
|
|
25
41
|
::-webkit-scrollbar-corner { background: transparent; }
|
|
26
42
|
::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
|
|
27
43
|
}
|
|
28
44
|
@media (forced-colors: active) {
|
|
29
|
-
|
|
45
|
+
*, :hover, [${GUEST_SCROLLING_ATTRIBUTE}] { scrollbar-color: auto; }
|
|
30
46
|
::-webkit-scrollbar-thumb, ::-webkit-scrollbar-thumb:hover { background: CanvasText; }
|
|
31
47
|
}
|
|
32
48
|
`;
|
|
49
|
+
/**
|
|
50
|
+
* Marks whichever element scrolls (the root for document scrolling) with
|
|
51
|
+
* `GUEST_SCROLLING_ATTRIBUTE` and clears it once scrolling has been quiet for
|
|
52
|
+
* the hide delay. One capturing, passive listener per document; installing
|
|
53
|
+
* twice is a no-op. Self-contained on purpose: the server inlines its source.
|
|
54
|
+
*/
|
|
55
|
+
export function installGuestScrollbarActivity(doc) {
|
|
56
|
+
const root = doc.documentElement;
|
|
57
|
+
if (root.hasAttribute('data-oc-scrollbar-activity'))
|
|
58
|
+
return;
|
|
59
|
+
root.setAttribute('data-oc-scrollbar-activity', '');
|
|
60
|
+
const timers = new WeakMap();
|
|
61
|
+
doc.addEventListener('scroll', (event) => {
|
|
62
|
+
const target = event.target === doc ? root : event.target;
|
|
63
|
+
if (!(target instanceof Element))
|
|
64
|
+
return;
|
|
65
|
+
if (!target.hasAttribute('data-oc-scrolling'))
|
|
66
|
+
target.setAttribute('data-oc-scrolling', '');
|
|
67
|
+
const pending = timers.get(target);
|
|
68
|
+
if (pending !== undefined)
|
|
69
|
+
clearTimeout(pending);
|
|
70
|
+
timers.set(target, setTimeout(() => {
|
|
71
|
+
timers.delete(target);
|
|
72
|
+
target.removeAttribute('data-oc-scrolling');
|
|
73
|
+
}, 1000));
|
|
74
|
+
}, { capture: true, passive: true });
|
|
75
|
+
}
|
|
76
|
+
/** Inline script form of `installGuestScrollbarActivity`, appended by the host to served guest HTML. */
|
|
77
|
+
export const GUEST_SCROLLBAR_SCRIPT = `(${installGuestScrollbarActivity.toString()})(document);`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Host-side parse of what a browser provider answers. `data` stays whatever
|
|
4
|
+
* the service produced: the host hands it to the agent as is, and only
|
|
5
|
+
* `browser.capture` is read further (the image the host writes to disk).
|
|
6
|
+
*/
|
|
7
|
+
export declare const browserProviderResultSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
8
|
+
ok: z.ZodLiteral<true>;
|
|
9
|
+
data: z.ZodObject<{}, z.core.$loose>;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
ok: z.ZodLiteral<false>;
|
|
12
|
+
error: z.ZodString;
|
|
13
|
+
}, z.core.$strip>]>;
|
|
14
|
+
export declare const browserControlActionSchema: z.ZodEnum<{
|
|
15
|
+
"browser.open": "browser.open";
|
|
16
|
+
"browser.snapshot": "browser.snapshot";
|
|
17
|
+
"browser.click": "browser.click";
|
|
18
|
+
"browser.type": "browser.type";
|
|
19
|
+
"browser.scroll": "browser.scroll";
|
|
20
|
+
"browser.back": "browser.back";
|
|
21
|
+
"browser.forward": "browser.forward";
|
|
22
|
+
"browser.inspect": "browser.inspect";
|
|
23
|
+
"browser.capture": "browser.capture";
|
|
24
|
+
"browser.resize": "browser.resize";
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BROWSER_CONTROL_ACTIONS } from "./service-providers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Host-side parse of what a browser provider answers. `data` stays whatever
|
|
5
|
+
* the service produced: the host hands it to the agent as is, and only
|
|
6
|
+
* `browser.capture` is read further (the image the host writes to disk).
|
|
7
|
+
*/
|
|
8
|
+
export const browserProviderResultSchema = z.union([
|
|
9
|
+
z.object({ ok: z.literal(true), data: z.object({}).passthrough() }),
|
|
10
|
+
z.object({ ok: z.literal(false), error: z.string().min(1).max(2_000) }),
|
|
11
|
+
]);
|
|
12
|
+
export const browserControlActionSchema = z.enum(BROWSER_CONTROL_ACTIONS);
|