@openchamber/sdk 2.1.0 → 2.1.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 +33 -6
- package/DOCUMENTATION.md +14 -4
- package/README.md +13 -1
- package/dist/contract.d.ts +41 -3
- package/dist/contract.js +1 -0
- package/dist/host.d.ts +15 -5
- package/dist/host.js +144 -6
- package/dist/index.d.ts +7 -3
- package/dist/index.js +3 -1
- package/dist/manifest.d.ts +2 -0
- package/dist/parse.d.ts +8 -4
- package/dist/parse.js +2 -0
- package/dist/popover-schemas.d.ts +33 -0
- package/dist/popover-schemas.js +74 -0
- package/dist/popover.d.ts +37 -0
- package/dist/popover.js +57 -0
- package/dist/protocol.d.ts +125 -0
- package/dist/protocol.js +32 -1
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +2 -0
- package/dist/status-control-schemas.d.ts +21 -0
- package/dist/status-control-schemas.js +29 -0
- package/dist/status-controls.d.ts +31 -0
- package/dist/status-controls.js +48 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/popover-anchor.d.ts +16 -0
- package/dist/ui/popover-anchor.js +198 -0
- package/dist/ui/theme.js +15 -0
- package/dist/workspace-schemas.d.ts +16 -0
- package/dist/workspace-schemas.js +4 -4
- package/dist/workspace.d.ts +10 -4
- package/dist/workspace.js +2 -0
- package/package.json +1 -1
package/dist/protocol.d.ts
CHANGED
|
@@ -128,6 +128,14 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
128
128
|
font: z.ZodString;
|
|
129
129
|
mono: z.ZodString;
|
|
130
130
|
radius: z.ZodString;
|
|
131
|
+
syntaxKeyword: z.ZodOptional<z.ZodString>;
|
|
132
|
+
syntaxString: z.ZodOptional<z.ZodString>;
|
|
133
|
+
syntaxNumber: z.ZodOptional<z.ZodString>;
|
|
134
|
+
syntaxFunction: z.ZodOptional<z.ZodString>;
|
|
135
|
+
syntaxType: z.ZodOptional<z.ZodString>;
|
|
136
|
+
syntaxComment: z.ZodOptional<z.ZodString>;
|
|
137
|
+
syntaxVariable: z.ZodOptional<z.ZodString>;
|
|
138
|
+
syntaxOperator: z.ZodOptional<z.ZodString>;
|
|
131
139
|
}, z.core.$strip>;
|
|
132
140
|
}, z.core.$strip>;
|
|
133
141
|
locale: z.ZodString;
|
|
@@ -146,6 +154,7 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
146
154
|
background: "background";
|
|
147
155
|
status: "status";
|
|
148
156
|
file: "file";
|
|
157
|
+
popover: "popover";
|
|
149
158
|
}>;
|
|
150
159
|
connection: z.ZodObject<{
|
|
151
160
|
connected: z.ZodBoolean;
|
|
@@ -197,6 +206,15 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
197
206
|
}, z.core.$strip>>;
|
|
198
207
|
data: z.ZodOptional<z.ZodJSONSchema>;
|
|
199
208
|
}, z.core.$strip>]>>;
|
|
209
|
+
popover: z.ZodOptional<z.ZodObject<{
|
|
210
|
+
id: z.ZodString;
|
|
211
|
+
data: z.ZodCustom<import("./contract.ts").JsonValue, import("./contract.ts").JsonValue>;
|
|
212
|
+
}, z.core.$strict>>;
|
|
213
|
+
features: z.ZodOptional<z.ZodObject<{
|
|
214
|
+
deviceStorage: z.ZodOptional<z.ZodLiteral<true>>;
|
|
215
|
+
statusControls: z.ZodOptional<z.ZodLiteral<true>>;
|
|
216
|
+
popovers: z.ZodOptional<z.ZodLiteral<true>>;
|
|
217
|
+
}, z.core.$strip>>;
|
|
200
218
|
}, z.core.$strip>;
|
|
201
219
|
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
202
220
|
v: z.ZodLiteral<1>;
|
|
@@ -300,6 +318,29 @@ export declare const hostMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
300
318
|
}, z.core.$strip>;
|
|
301
319
|
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
302
320
|
v: z.ZodLiteral<1>;
|
|
321
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
322
|
+
type: z.ZodLiteral<"status-control-event">;
|
|
323
|
+
payload: z.ZodObject<{
|
|
324
|
+
id: z.ZodString;
|
|
325
|
+
value: z.ZodOptional<z.ZodString>;
|
|
326
|
+
}, z.core.$strict>;
|
|
327
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
328
|
+
v: z.ZodLiteral<1>;
|
|
329
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
330
|
+
type: z.ZodLiteral<"popover-closed">;
|
|
331
|
+
payload: z.ZodObject<{
|
|
332
|
+
id: z.ZodString;
|
|
333
|
+
reason: z.ZodEnum<{
|
|
334
|
+
escape: "escape";
|
|
335
|
+
outside: "outside";
|
|
336
|
+
anchor: "anchor";
|
|
337
|
+
owner: "owner";
|
|
338
|
+
closed: "closed";
|
|
339
|
+
replaced: "replaced";
|
|
340
|
+
}>;
|
|
341
|
+
}, z.core.$strict>;
|
|
342
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
343
|
+
v: z.ZodLiteral<1>;
|
|
303
344
|
}, z.core.$strip>, z.ZodObject<{
|
|
304
345
|
type: z.ZodLiteral<"resolve">;
|
|
305
346
|
id: z.ZodString;
|
|
@@ -880,18 +921,102 @@ export declare const guestMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
880
921
|
payload: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
881
922
|
op: z.ZodLiteral<"get">;
|
|
882
923
|
key: z.ZodString;
|
|
924
|
+
scope: z.ZodOptional<z.ZodEnum<{
|
|
925
|
+
instance: "instance";
|
|
926
|
+
device: "device";
|
|
927
|
+
}>>;
|
|
883
928
|
}, z.core.$strict>, z.ZodObject<{
|
|
884
929
|
op: z.ZodLiteral<"delete">;
|
|
885
930
|
key: z.ZodString;
|
|
931
|
+
scope: z.ZodOptional<z.ZodEnum<{
|
|
932
|
+
instance: "instance";
|
|
933
|
+
device: "device";
|
|
934
|
+
}>>;
|
|
886
935
|
}, z.core.$strict>, z.ZodObject<{
|
|
887
936
|
op: z.ZodLiteral<"set">;
|
|
888
937
|
key: z.ZodString;
|
|
889
938
|
value: z.ZodJSONSchema;
|
|
939
|
+
scope: z.ZodOptional<z.ZodEnum<{
|
|
940
|
+
instance: "instance";
|
|
941
|
+
device: "device";
|
|
942
|
+
}>>;
|
|
890
943
|
}, z.core.$strict>, z.ZodObject<{
|
|
891
944
|
op: z.ZodLiteral<"keys">;
|
|
945
|
+
scope: z.ZodOptional<z.ZodEnum<{
|
|
946
|
+
instance: "instance";
|
|
947
|
+
device: "device";
|
|
948
|
+
}>>;
|
|
892
949
|
}, z.core.$strict>], "op">;
|
|
893
950
|
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
894
951
|
v: z.ZodLiteral<1>;
|
|
952
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
953
|
+
type: z.ZodLiteral<"status-controls">;
|
|
954
|
+
id: z.ZodString;
|
|
955
|
+
payload: z.ZodObject<{
|
|
956
|
+
controls: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
957
|
+
kind: z.ZodLiteral<"button">;
|
|
958
|
+
id: z.ZodString;
|
|
959
|
+
label: z.ZodString;
|
|
960
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
961
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
962
|
+
kind: z.ZodLiteral<"select">;
|
|
963
|
+
id: z.ZodString;
|
|
964
|
+
label: z.ZodString;
|
|
965
|
+
value: z.ZodString;
|
|
966
|
+
options: z.ZodArray<z.ZodObject<{
|
|
967
|
+
value: z.ZodString;
|
|
968
|
+
label: z.ZodString;
|
|
969
|
+
}, z.core.$strict>>;
|
|
970
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
971
|
+
}, z.core.$strict>], "kind">>;
|
|
972
|
+
}, z.core.$strict>;
|
|
973
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
974
|
+
v: z.ZodLiteral<1>;
|
|
975
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
976
|
+
type: z.ZodLiteral<"popover-open">;
|
|
977
|
+
id: z.ZodString;
|
|
978
|
+
payload: z.ZodObject<{
|
|
979
|
+
id: z.ZodString;
|
|
980
|
+
anchor: z.ZodObject<{
|
|
981
|
+
x: z.ZodNumber;
|
|
982
|
+
y: z.ZodNumber;
|
|
983
|
+
width: z.ZodNumber;
|
|
984
|
+
height: z.ZodNumber;
|
|
985
|
+
}, z.core.$strict>;
|
|
986
|
+
width: z.ZodNumber;
|
|
987
|
+
height: z.ZodNumber;
|
|
988
|
+
side: z.ZodOptional<z.ZodEnum<{
|
|
989
|
+
left: "left";
|
|
990
|
+
right: "right";
|
|
991
|
+
top: "top";
|
|
992
|
+
bottom: "bottom";
|
|
993
|
+
}>>;
|
|
994
|
+
focus: z.ZodOptional<z.ZodBoolean>;
|
|
995
|
+
data: z.ZodCustom<import("./contract.ts").JsonValue, import("./contract.ts").JsonValue>;
|
|
996
|
+
}, z.core.$strict>;
|
|
997
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
998
|
+
v: z.ZodLiteral<1>;
|
|
999
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1000
|
+
type: z.ZodLiteral<"popover-close">;
|
|
1001
|
+
id: z.ZodString;
|
|
1002
|
+
payload: z.ZodObject<{
|
|
1003
|
+
id: z.ZodString;
|
|
1004
|
+
reason: z.ZodOptional<z.ZodEnum<{
|
|
1005
|
+
escape: "escape";
|
|
1006
|
+
closed: "closed";
|
|
1007
|
+
}>>;
|
|
1008
|
+
}, z.core.$strict>;
|
|
1009
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
1010
|
+
v: z.ZodLiteral<1>;
|
|
1011
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
1012
|
+
type: z.ZodLiteral<"popover-anchor">;
|
|
1013
|
+
id: z.ZodString;
|
|
1014
|
+
payload: z.ZodObject<{
|
|
1015
|
+
id: z.ZodString;
|
|
1016
|
+
active: z.ZodBoolean;
|
|
1017
|
+
}, z.core.$strict>;
|
|
1018
|
+
channel: z.ZodLiteral<"openchamber.sdk">;
|
|
1019
|
+
v: z.ZodLiteral<1>;
|
|
895
1020
|
}, z.core.$strip>, z.ZodObject<{
|
|
896
1021
|
type: z.ZodLiteral<"open-session">;
|
|
897
1022
|
id: z.ZodString;
|
package/dist/protocol.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { guestSessionWorktreeSchema, guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceQuerySchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
|
|
3
|
+
import { guestStatusControlEventSchema, guestStatusControlsSchema } from "./status-control-schemas.js";
|
|
4
|
+
import { guestPopoverClosedEventSchema, guestPopoverDataSchema, guestPopoverRequestSchema } from "./popover-schemas.js";
|
|
3
5
|
import { OPENCHAMBER_SDK_API_VERSION, OPENCHAMBER_SDK_CHANNEL } from "./api-version.js";
|
|
4
6
|
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
7
|
import { GUEST_FILE_EDITOR_CONTENT_MAX, GUEST_FILE_EDITOR_VERSION_MAX } from "./file-editor.js";
|
|
@@ -39,6 +41,14 @@ const themeTokensSchema = z.object({
|
|
|
39
41
|
font: z.string().min(1),
|
|
40
42
|
mono: z.string().min(1),
|
|
41
43
|
radius: z.string().min(1),
|
|
44
|
+
syntaxKeyword: z.string().min(1).optional(),
|
|
45
|
+
syntaxString: z.string().min(1).optional(),
|
|
46
|
+
syntaxNumber: z.string().min(1).optional(),
|
|
47
|
+
syntaxFunction: z.string().min(1).optional(),
|
|
48
|
+
syntaxType: z.string().min(1).optional(),
|
|
49
|
+
syntaxComment: z.string().min(1).optional(),
|
|
50
|
+
syntaxVariable: z.string().min(1).optional(),
|
|
51
|
+
syntaxOperator: z.string().min(1).optional(),
|
|
42
52
|
});
|
|
43
53
|
const sessionSnapshotSchema = z.object({
|
|
44
54
|
id: z.string().min(1),
|
|
@@ -151,10 +161,16 @@ const readyPayloadSchema = z.object({
|
|
|
151
161
|
locale: z.string().min(1),
|
|
152
162
|
directory: z.string().nullable(),
|
|
153
163
|
session: sessionSnapshotSchema,
|
|
154
|
-
surface: z.enum(['panel', 'dialog', 'page', 'background', 'status', 'file']),
|
|
164
|
+
surface: z.enum(['panel', 'dialog', 'page', 'background', 'status', 'file', 'popover']),
|
|
155
165
|
connection: guestConnectionSchema,
|
|
156
166
|
settings: guestSettingsSchema,
|
|
157
167
|
item: guestItemSchema,
|
|
168
|
+
popover: z.object({ id: z.string().min(1).max(80), data: guestPopoverDataSchema }).strict().optional(),
|
|
169
|
+
features: z.object({
|
|
170
|
+
deviceStorage: z.literal(true).optional(),
|
|
171
|
+
statusControls: z.literal(true).optional(),
|
|
172
|
+
popovers: z.literal(true).optional(),
|
|
173
|
+
}).optional(),
|
|
158
174
|
});
|
|
159
175
|
const hostResultSchema = z.object({
|
|
160
176
|
...envelope,
|
|
@@ -248,6 +264,12 @@ export const hostMessageSchema = z.union([
|
|
|
248
264
|
item: guestItemSchema,
|
|
249
265
|
}),
|
|
250
266
|
}),
|
|
267
|
+
z.object({
|
|
268
|
+
...envelope,
|
|
269
|
+
type: z.literal('status-control-event'),
|
|
270
|
+
payload: guestStatusControlEventSchema,
|
|
271
|
+
}),
|
|
272
|
+
z.object({ ...envelope, type: z.literal('popover-closed'), payload: guestPopoverClosedEventSchema }),
|
|
251
273
|
z.object({
|
|
252
274
|
...envelope,
|
|
253
275
|
type: z.literal('resolve'),
|
|
@@ -311,6 +333,15 @@ export const guestMessageSchema = z.discriminatedUnion('type', [
|
|
|
311
333
|
z.object({ ...envelope, type: z.literal('workspace-subscribe'), id: z.string().min(1), payload: z.object({ subscriptionId: z.string().min(1).max(128), query: guestWorkspaceQuerySchema }) }),
|
|
312
334
|
z.object({ ...envelope, type: z.literal('workspace-unsubscribe'), id: z.string().min(1), payload: z.object({ subscriptionId: z.string().min(1).max(128) }) }),
|
|
313
335
|
z.object({ ...envelope, type: z.literal('storage'), id: z.string().min(1), payload: guestStorageRequestSchema }),
|
|
336
|
+
z.object({
|
|
337
|
+
...envelope,
|
|
338
|
+
type: z.literal('status-controls'),
|
|
339
|
+
id: z.string().min(1),
|
|
340
|
+
payload: z.object({ controls: guestStatusControlsSchema }).strict(),
|
|
341
|
+
}),
|
|
342
|
+
z.object({ ...envelope, type: z.literal('popover-open'), id: z.string().min(1), payload: guestPopoverRequestSchema }),
|
|
343
|
+
z.object({ ...envelope, type: z.literal('popover-close'), id: z.string().min(1), payload: z.object({ id: z.string().min(1).max(80), reason: z.enum(['closed', 'escape']).optional() }).strict() }),
|
|
344
|
+
z.object({ ...envelope, type: z.literal('popover-anchor'), id: z.string().min(1), payload: z.object({ id: z.string().min(1).max(80), active: z.boolean() }).strict() }),
|
|
314
345
|
z.object({ ...envelope, type: z.literal('open-session'), id: z.string().min(1), payload: z.object({ sessionId: z.string().min(1).max(1024) }) }),
|
|
315
346
|
z.object({
|
|
316
347
|
...envelope,
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from './parse.ts';
|
|
2
2
|
export type { ManifestDocument } from './parse.ts';
|
|
3
3
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from './workspace-schemas.ts';
|
|
4
|
+
export { guestStatusControlsSchema, guestStatusControlEventSchema } from './status-control-schemas.ts';
|
|
5
|
+
export { guestPopoverClosedEventSchema, guestPopoverRequestSchema } from './popover-schemas.ts';
|
|
4
6
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from './protocol.ts';
|
|
5
7
|
export { browserControlActionSchema, browserProviderResultSchema } from './service-provider-schemas.ts';
|
|
6
8
|
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from './service-surface-schemas.ts';
|
package/dist/schemas.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// `@openchamber/sdk`, which carries no schema library.
|
|
4
4
|
export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from "./parse.js";
|
|
5
5
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
|
|
6
|
+
export { guestStatusControlsSchema, guestStatusControlEventSchema } from "./status-control-schemas.js";
|
|
7
|
+
export { guestPopoverClosedEventSchema, guestPopoverRequestSchema } from "./popover-schemas.js";
|
|
6
8
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from "./protocol.js";
|
|
7
9
|
export { browserControlActionSchema, browserProviderResultSchema } from "./service-provider-schemas.js";
|
|
8
10
|
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from "./service-surface-schemas.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const guestStatusControlsSchema: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
3
|
+
kind: z.ZodLiteral<"button">;
|
|
4
|
+
id: z.ZodString;
|
|
5
|
+
label: z.ZodString;
|
|
6
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
7
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
8
|
+
kind: z.ZodLiteral<"select">;
|
|
9
|
+
id: z.ZodString;
|
|
10
|
+
label: z.ZodString;
|
|
11
|
+
value: z.ZodString;
|
|
12
|
+
options: z.ZodArray<z.ZodObject<{
|
|
13
|
+
value: z.ZodString;
|
|
14
|
+
label: z.ZodString;
|
|
15
|
+
}, z.core.$strict>>;
|
|
16
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
}, z.core.$strict>], "kind">>;
|
|
18
|
+
export declare const guestStatusControlEventSchema: z.ZodObject<{
|
|
19
|
+
id: z.ZodString;
|
|
20
|
+
value: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$strict>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { GUEST_STATUS_CONTROL_ID, GUEST_STATUS_CONTROL_LABEL_MAX, GUEST_STATUS_CONTROL_OPTIONS_MAX, GUEST_STATUS_CONTROL_VALUE_MAX, GUEST_STATUS_CONTROLS_MAX, } from "./status-controls.js";
|
|
3
|
+
const idSchema = z.string().regex(GUEST_STATUS_CONTROL_ID);
|
|
4
|
+
const labelSchema = z.string().min(1).max(GUEST_STATUS_CONTROL_LABEL_MAX);
|
|
5
|
+
const optionSchema = z.object({
|
|
6
|
+
value: z.string().min(1).max(GUEST_STATUS_CONTROL_VALUE_MAX),
|
|
7
|
+
label: labelSchema,
|
|
8
|
+
}).strict();
|
|
9
|
+
const buttonSchema = z.object({
|
|
10
|
+
kind: z.literal('button'),
|
|
11
|
+
id: idSchema,
|
|
12
|
+
label: labelSchema,
|
|
13
|
+
disabled: z.boolean().optional(),
|
|
14
|
+
}).strict();
|
|
15
|
+
const selectSchema = z.object({
|
|
16
|
+
kind: z.literal('select'),
|
|
17
|
+
id: idSchema,
|
|
18
|
+
label: labelSchema,
|
|
19
|
+
value: z.string().min(1).max(GUEST_STATUS_CONTROL_VALUE_MAX),
|
|
20
|
+
options: z.array(optionSchema).min(1).max(GUEST_STATUS_CONTROL_OPTIONS_MAX),
|
|
21
|
+
disabled: z.boolean().optional(),
|
|
22
|
+
}).strict().refine((control) => new Set(control.options.map((option) => option.value)).size === control.options.length, { message: 'status control option values must be unique', path: ['options'] }).refine((control) => control.options.some((option) => option.value === control.value), { message: 'status control value must be one of its options', path: ['value'] });
|
|
23
|
+
export const guestStatusControlsSchema = z.array(z.discriminatedUnion('kind', [buttonSchema, selectSchema]))
|
|
24
|
+
.max(GUEST_STATUS_CONTROLS_MAX)
|
|
25
|
+
.refine((controls) => new Set(controls.map((control) => control.id)).size === controls.length, { message: 'status control ids must be unique' });
|
|
26
|
+
export const guestStatusControlEventSchema = z.object({
|
|
27
|
+
id: idSchema,
|
|
28
|
+
value: z.string().min(1).max(GUEST_STATUS_CONTROL_VALUE_MAX).optional(),
|
|
29
|
+
}).strict();
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const GUEST_STATUS_CONTROLS_MAX = 4;
|
|
2
|
+
export declare const GUEST_STATUS_CONTROL_ID: RegExp;
|
|
3
|
+
export declare const GUEST_STATUS_CONTROL_LABEL_MAX = 60;
|
|
4
|
+
export declare const GUEST_STATUS_CONTROL_OPTIONS_MAX = 16;
|
|
5
|
+
export declare const GUEST_STATUS_CONTROL_VALUE_MAX = 80;
|
|
6
|
+
export type GuestStatusControlOption = {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
};
|
|
10
|
+
export type GuestStatusControl = {
|
|
11
|
+
kind: 'button';
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'select';
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
value: string;
|
|
20
|
+
options: GuestStatusControlOption[];
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type GuestStatusControlEvent = {
|
|
24
|
+
id: string;
|
|
25
|
+
value?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Guest-side fast-fail guard for `setStatusControls`. The host zod schema is
|
|
29
|
+
* still authoritative because JavaScript callers can bypass this SDK method.
|
|
30
|
+
*/
|
|
31
|
+
export declare const isGuestStatusControls: (controls: GuestStatusControl[]) => boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const GUEST_STATUS_CONTROLS_MAX = 4;
|
|
2
|
+
export const GUEST_STATUS_CONTROL_ID = /^[a-z][a-z0-9-]{0,39}$/;
|
|
3
|
+
export const GUEST_STATUS_CONTROL_LABEL_MAX = 60;
|
|
4
|
+
export const GUEST_STATUS_CONTROL_OPTIONS_MAX = 16;
|
|
5
|
+
export const GUEST_STATUS_CONTROL_VALUE_MAX = 80;
|
|
6
|
+
const isControlObject = (value) => (Object(value) === value && !Array.isArray(value));
|
|
7
|
+
const hasOnlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
8
|
+
const isLabel = (value) => (String(value) === value && value.length >= 1 && value.length <= GUEST_STATUS_CONTROL_LABEL_MAX);
|
|
9
|
+
const isValue = (value) => (String(value) === value && value.length >= 1 && value.length <= GUEST_STATUS_CONTROL_VALUE_MAX);
|
|
10
|
+
/**
|
|
11
|
+
* Guest-side fast-fail guard for `setStatusControls`. The host zod schema is
|
|
12
|
+
* still authoritative because JavaScript callers can bypass this SDK method.
|
|
13
|
+
*/
|
|
14
|
+
export const isGuestStatusControls = (controls) => {
|
|
15
|
+
// The public type is precise, but this boundary also receives untyped JavaScript callers.
|
|
16
|
+
if (!Array.isArray(controls) || controls.length > GUEST_STATUS_CONTROLS_MAX)
|
|
17
|
+
return false;
|
|
18
|
+
const ids = new Set();
|
|
19
|
+
for (const control of controls) {
|
|
20
|
+
if (!isControlObject(control) || !GUEST_STATUS_CONTROL_ID.test(control.id) || !isLabel(control.label))
|
|
21
|
+
return false;
|
|
22
|
+
if (control.disabled !== undefined && control.disabled !== true && control.disabled !== false)
|
|
23
|
+
return false;
|
|
24
|
+
if (ids.has(control.id))
|
|
25
|
+
return false;
|
|
26
|
+
ids.add(control.id);
|
|
27
|
+
if (control.kind === 'button') {
|
|
28
|
+
if (!hasOnlyKeys(control, ['kind', 'id', 'label', 'disabled']))
|
|
29
|
+
return false;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (control.kind !== 'select' || !hasOnlyKeys(control, ['kind', 'id', 'label', 'value', 'options', 'disabled']))
|
|
33
|
+
return false;
|
|
34
|
+
if (!isValue(control.value) || !Array.isArray(control.options)
|
|
35
|
+
|| control.options.length < 1 || control.options.length > GUEST_STATUS_CONTROL_OPTIONS_MAX)
|
|
36
|
+
return false;
|
|
37
|
+
const values = new Set();
|
|
38
|
+
for (const option of control.options) {
|
|
39
|
+
if (!isControlObject(option) || !hasOnlyKeys(option, ['value', 'label'])
|
|
40
|
+
|| !isValue(option.value) || !isLabel(option.label) || values.has(option.value))
|
|
41
|
+
return false;
|
|
42
|
+
values.add(option.value);
|
|
43
|
+
}
|
|
44
|
+
if (!values.has(control.value))
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
};
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -33,3 +33,5 @@ export { mountMenu } from './menu.ts';
|
|
|
33
33
|
export type { MenuHandle, MenuItem, MenuProps } from './menu.ts';
|
|
34
34
|
export { mountText, splitTextMedia } from './text.ts';
|
|
35
35
|
export type { TextHandle, TextPart, TextProps } from './text.ts';
|
|
36
|
+
export { mountPopoverAnchor } from './popover-anchor.ts';
|
|
37
|
+
export type { PopoverAnchorHandle, PopoverAnchorOptions } from './popover-anchor.ts';
|
package/dist/ui/index.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HostClient } from '../host.ts';
|
|
2
|
+
import type { GuestPopoverSide } from '../popover.ts';
|
|
3
|
+
import type { JsonValue } from '../contract.ts';
|
|
4
|
+
export type PopoverAnchorOptions = {
|
|
5
|
+
host: Pick<HostClient, 'openPopover' | 'closePopover' | 'setPopoverAnchorActive' | 'onPopoverClosed'>;
|
|
6
|
+
getData: () => JsonValue;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
side?: GuestPopoverSide;
|
|
10
|
+
label?: string;
|
|
11
|
+
};
|
|
12
|
+
export type PopoverAnchorHandle = {
|
|
13
|
+
dispose: () => void;
|
|
14
|
+
};
|
|
15
|
+
/** Adds a bounded host popover preview to an extension-owned element. */
|
|
16
|
+
export declare const mountPopoverAnchor: (element: HTMLElement, options: PopoverAnchorOptions) => PopoverAnchorHandle;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
let nextPopoverAnchorId = 0;
|
|
2
|
+
const isKeyboardEvent = (event) => 'key' in event;
|
|
3
|
+
/** Adds a bounded host popover preview to an extension-owned element. */
|
|
4
|
+
export const mountPopoverAnchor = (element, options) => {
|
|
5
|
+
const document = element.ownerDocument;
|
|
6
|
+
const window = document.defaultView;
|
|
7
|
+
let timer = null;
|
|
8
|
+
let activeId = null;
|
|
9
|
+
let disposed = false;
|
|
10
|
+
let ignoreNextFocus = false;
|
|
11
|
+
let pointerActive = false;
|
|
12
|
+
let focusActive = false;
|
|
13
|
+
let observer = null;
|
|
14
|
+
const previousExpanded = element.getAttribute('aria-expanded');
|
|
15
|
+
const previousPopup = element.getAttribute('aria-haspopup');
|
|
16
|
+
const previousLabel = element.getAttribute('aria-label');
|
|
17
|
+
const ownsExpanded = previousExpanded === null;
|
|
18
|
+
const clearTimer = () => {
|
|
19
|
+
if (timer !== null)
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
timer = null;
|
|
22
|
+
};
|
|
23
|
+
const removeActiveListeners = () => {
|
|
24
|
+
document.removeEventListener('scroll', closeForAnchor, true);
|
|
25
|
+
document.removeEventListener('pointerdown', closeForOutside, true);
|
|
26
|
+
window?.removeEventListener('resize', closeForAnchor);
|
|
27
|
+
observer?.disconnect();
|
|
28
|
+
observer = null;
|
|
29
|
+
};
|
|
30
|
+
const close = (reason = 'closed', restoreFocus = false) => {
|
|
31
|
+
const id = activeId;
|
|
32
|
+
clearTimer();
|
|
33
|
+
activeId = null;
|
|
34
|
+
removeActiveListeners();
|
|
35
|
+
element.removeAttribute('data-oc-popover-open');
|
|
36
|
+
if (ownsExpanded)
|
|
37
|
+
element.setAttribute('aria-expanded', 'false');
|
|
38
|
+
if (id)
|
|
39
|
+
void options.host.closePopover(id, reason).catch(() => undefined);
|
|
40
|
+
if (restoreFocus) {
|
|
41
|
+
ignoreNextFocus = true;
|
|
42
|
+
element.focus();
|
|
43
|
+
queueMicrotask(() => { ignoreNextFocus = false; });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const closeForAnchor = () => close();
|
|
47
|
+
const closeForOutside = (event) => {
|
|
48
|
+
const target = event.target;
|
|
49
|
+
// SAFETY: DOM pointer events target a Node; this avoids cross-realm constructors.
|
|
50
|
+
if (target !== element && !element.contains(target))
|
|
51
|
+
close();
|
|
52
|
+
};
|
|
53
|
+
const watchActive = () => {
|
|
54
|
+
document.addEventListener('scroll', closeForAnchor, true);
|
|
55
|
+
document.addEventListener('pointerdown', closeForOutside, true);
|
|
56
|
+
window?.addEventListener('resize', closeForAnchor);
|
|
57
|
+
observer = new MutationObserver(() => { if (!document.contains(element))
|
|
58
|
+
close(); });
|
|
59
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
60
|
+
};
|
|
61
|
+
const reportAnchorActivity = () => {
|
|
62
|
+
if (activeId)
|
|
63
|
+
void options.host.setPopoverAnchorActive(activeId, pointerActive || focusActive).catch(() => undefined);
|
|
64
|
+
};
|
|
65
|
+
const open = (focus) => {
|
|
66
|
+
clearTimer();
|
|
67
|
+
if (disposed || activeId || !element.isConnected)
|
|
68
|
+
return;
|
|
69
|
+
const rect = element.getBoundingClientRect();
|
|
70
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
71
|
+
return;
|
|
72
|
+
const id = `oc-popover-${++nextPopoverAnchorId}`;
|
|
73
|
+
let data;
|
|
74
|
+
try {
|
|
75
|
+
data = options.getData();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
activeId = id;
|
|
81
|
+
element.setAttribute('data-oc-popover-open', 'true');
|
|
82
|
+
if (ownsExpanded)
|
|
83
|
+
element.setAttribute('aria-expanded', 'true');
|
|
84
|
+
watchActive();
|
|
85
|
+
void options.host.openPopover({
|
|
86
|
+
id,
|
|
87
|
+
anchor: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
88
|
+
width: options.width,
|
|
89
|
+
height: options.height,
|
|
90
|
+
side: options.side,
|
|
91
|
+
focus,
|
|
92
|
+
data,
|
|
93
|
+
}).catch(() => {
|
|
94
|
+
if (activeId === id)
|
|
95
|
+
close();
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const scheduleOpen = () => {
|
|
99
|
+
if (!activeId && !disposed && timer === null)
|
|
100
|
+
timer = setTimeout(() => open(false), 250);
|
|
101
|
+
};
|
|
102
|
+
const onPointerEnter = (event) => {
|
|
103
|
+
if ('pointerType' in event && event.pointerType && event.pointerType !== 'mouse')
|
|
104
|
+
return;
|
|
105
|
+
pointerActive = true;
|
|
106
|
+
if (activeId)
|
|
107
|
+
reportAnchorActivity();
|
|
108
|
+
else if (!ignoreNextFocus)
|
|
109
|
+
scheduleOpen();
|
|
110
|
+
};
|
|
111
|
+
const onPointerLeave = (event) => {
|
|
112
|
+
if ('pointerType' in event && event.pointerType && event.pointerType !== 'mouse')
|
|
113
|
+
return;
|
|
114
|
+
pointerActive = false;
|
|
115
|
+
clearTimer();
|
|
116
|
+
reportAnchorActivity();
|
|
117
|
+
};
|
|
118
|
+
const onFocus = () => {
|
|
119
|
+
focusActive = true;
|
|
120
|
+
if (activeId)
|
|
121
|
+
reportAnchorActivity();
|
|
122
|
+
else if (!ignoreNextFocus && element.matches(':focus-visible'))
|
|
123
|
+
open(false);
|
|
124
|
+
};
|
|
125
|
+
const onFocusOut = () => {
|
|
126
|
+
focusActive = false;
|
|
127
|
+
reportAnchorActivity();
|
|
128
|
+
};
|
|
129
|
+
const onKeyDown = (event) => {
|
|
130
|
+
if (!isKeyboardEvent(event))
|
|
131
|
+
return;
|
|
132
|
+
if (event.key === 'ArrowRight') {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
if (activeId)
|
|
135
|
+
close();
|
|
136
|
+
open(true);
|
|
137
|
+
}
|
|
138
|
+
else if (event.key === 'Escape' && activeId) {
|
|
139
|
+
event.preventDefault();
|
|
140
|
+
close('escape', true);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const unsubscribe = options.host.onPopoverClosed((event) => {
|
|
144
|
+
if (event.id !== activeId)
|
|
145
|
+
return;
|
|
146
|
+
activeId = null;
|
|
147
|
+
clearTimer();
|
|
148
|
+
removeActiveListeners();
|
|
149
|
+
element.removeAttribute('data-oc-popover-open');
|
|
150
|
+
if (ownsExpanded)
|
|
151
|
+
element.setAttribute('aria-expanded', 'false');
|
|
152
|
+
if (event.reason === 'escape') {
|
|
153
|
+
ignoreNextFocus = true;
|
|
154
|
+
element.focus();
|
|
155
|
+
queueMicrotask(() => { ignoreNextFocus = false; });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
element.setAttribute('aria-haspopup', 'dialog');
|
|
159
|
+
if (ownsExpanded)
|
|
160
|
+
element.setAttribute('aria-expanded', 'false');
|
|
161
|
+
if (options.label)
|
|
162
|
+
element.setAttribute('aria-label', options.label);
|
|
163
|
+
element.addEventListener('pointerenter', onPointerEnter);
|
|
164
|
+
// Crossing opaque iframe documents can omit an enter transition. A real
|
|
165
|
+
// pointer move over this element still establishes a new hover intent.
|
|
166
|
+
element.addEventListener('pointermove', onPointerEnter);
|
|
167
|
+
element.addEventListener('pointerleave', onPointerLeave);
|
|
168
|
+
element.addEventListener('focus', onFocus);
|
|
169
|
+
element.addEventListener('focusout', onFocusOut);
|
|
170
|
+
element.addEventListener('keydown', onKeyDown);
|
|
171
|
+
return { dispose: () => {
|
|
172
|
+
if (disposed)
|
|
173
|
+
return;
|
|
174
|
+
disposed = true;
|
|
175
|
+
clearTimer();
|
|
176
|
+
close();
|
|
177
|
+
unsubscribe();
|
|
178
|
+
element.removeAttribute('data-oc-popover-open');
|
|
179
|
+
if (previousExpanded === null)
|
|
180
|
+
element.removeAttribute('aria-expanded');
|
|
181
|
+
if (previousPopup === null)
|
|
182
|
+
element.removeAttribute('aria-haspopup');
|
|
183
|
+
else
|
|
184
|
+
element.setAttribute('aria-haspopup', previousPopup);
|
|
185
|
+
if (options.label) {
|
|
186
|
+
if (previousLabel === null)
|
|
187
|
+
element.removeAttribute('aria-label');
|
|
188
|
+
else
|
|
189
|
+
element.setAttribute('aria-label', previousLabel);
|
|
190
|
+
}
|
|
191
|
+
element.removeEventListener('pointerenter', onPointerEnter);
|
|
192
|
+
element.removeEventListener('pointermove', onPointerEnter);
|
|
193
|
+
element.removeEventListener('pointerleave', onPointerLeave);
|
|
194
|
+
element.removeEventListener('focus', onFocus);
|
|
195
|
+
element.removeEventListener('focusout', onFocusOut);
|
|
196
|
+
element.removeEventListener('keydown', onKeyDown);
|
|
197
|
+
} };
|
|
198
|
+
};
|
package/dist/ui/theme.js
CHANGED
|
@@ -54,6 +54,16 @@ const TOKEN_VARS = [
|
|
|
54
54
|
['--font-mono', 'mono'],
|
|
55
55
|
['--radius', 'radius'],
|
|
56
56
|
];
|
|
57
|
+
const SYNTAX_TOKEN_VARS = [
|
|
58
|
+
['--oc-syntax-keyword', '--syntax-keyword', 'syntaxKeyword'],
|
|
59
|
+
['--oc-syntax-string', '--syntax-string', 'syntaxString'],
|
|
60
|
+
['--oc-syntax-number', '--syntax-number', 'syntaxNumber'],
|
|
61
|
+
['--oc-syntax-function', '--syntax-function', 'syntaxFunction'],
|
|
62
|
+
['--oc-syntax-type', '--syntax-type', 'syntaxType'],
|
|
63
|
+
['--oc-syntax-comment', '--syntax-comment', 'syntaxComment'],
|
|
64
|
+
['--oc-syntax-variable', '--syntax-variable', 'syntaxVariable'],
|
|
65
|
+
['--oc-syntax-operator', '--syntax-operator', 'syntaxOperator'],
|
|
66
|
+
];
|
|
57
67
|
/**
|
|
58
68
|
* Paint the host theme onto the iframe root. Guest chrome reads these
|
|
59
69
|
* variables, and the root itself gets the host font and text colour so plain
|
|
@@ -65,6 +75,11 @@ export const applyHostTheme = (theme, root) => {
|
|
|
65
75
|
for (const [name, key] of TOKEN_VARS) {
|
|
66
76
|
root.style.setProperty(name, theme.tokens[key]);
|
|
67
77
|
}
|
|
78
|
+
for (const [alias, hostName, key] of SYNTAX_TOKEN_VARS) {
|
|
79
|
+
const value = theme.tokens[key] ?? '';
|
|
80
|
+
root.style.setProperty(alias, value);
|
|
81
|
+
root.style.setProperty(hostName, value);
|
|
82
|
+
}
|
|
68
83
|
root.style.setProperty('font-family', theme.tokens.font);
|
|
69
84
|
root.style.setProperty('font-size', '0.875rem');
|
|
70
85
|
root.style.setProperty('line-height', '1.45');
|