@parlor/agent-ui 0.0.0-stage → 0.2.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.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/client/agent-ui-client.d.ts +60 -0
- package/dist/client/agent-ui-client.d.ts.map +1 -0
- package/dist/client/agent-ui-client.js +407 -0
- package/dist/client/agent-ui-client.js.map +1 -0
- package/dist/client/confirmations.d.ts +34 -0
- package/dist/client/confirmations.d.ts.map +1 -0
- package/dist/client/confirmations.js +95 -0
- package/dist/client/confirmations.js.map +1 -0
- package/dist/client/environment.d.ts +37 -0
- package/dist/client/environment.d.ts.map +1 -0
- package/dist/client/environment.js +79 -0
- package/dist/client/environment.js.map +1 -0
- package/dist/client/highlight.d.ts +12 -0
- package/dist/client/highlight.d.ts.map +1 -0
- package/dist/client/highlight.js +98 -0
- package/dist/client/highlight.js.map +1 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/json-equal.d.ts +2 -0
- package/dist/client/json-equal.d.ts.map +1 -0
- package/dist/client/json-equal.js +20 -0
- package/dist/client/json-equal.js.map +1 -0
- package/dist/client/registry.d.ts +54 -0
- package/dist/client/registry.d.ts.map +1 -0
- package/dist/client/registry.js +280 -0
- package/dist/client/registry.js.map +1 -0
- package/dist/client/screen.d.ts +20 -0
- package/dist/client/screen.d.ts.map +1 -0
- package/dist/client/screen.js +196 -0
- package/dist/client/screen.js.map +1 -0
- package/dist/client/write.d.ts +13 -0
- package/dist/client/write.d.ts.map +1 -0
- package/dist/client/write.js +101 -0
- package/dist/client/write.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +44 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +23 -0
- package/dist/manifest.js.map +1 -0
- package/dist/pages.d.ts +11 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +42 -0
- package/dist/pages.js.map +1 -0
- package/dist/protocol.d.ts +206 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +152 -0
- package/dist/protocol.js.map +1 -0
- package/dist/server/agent-ui.d.ts +36 -0
- package/dist/server/agent-ui.d.ts.map +1 -0
- package/dist/server/agent-ui.js +89 -0
- package/dist/server/agent-ui.js.map +1 -0
- package/dist/server/audience.d.ts +9 -0
- package/dist/server/audience.d.ts.map +1 -0
- package/dist/server/audience.js +15 -0
- package/dist/server/audience.js.map +1 -0
- package/dist/server/broker.d.ts +29 -0
- package/dist/server/broker.d.ts.map +1 -0
- package/dist/server/broker.js +152 -0
- package/dist/server/broker.js.map +1 -0
- package/dist/server/confirmations.d.ts +40 -0
- package/dist/server/confirmations.d.ts.map +1 -0
- package/dist/server/confirmations.js +230 -0
- package/dist/server/confirmations.js.map +1 -0
- package/dist/server/directory.d.ts +41 -0
- package/dist/server/directory.d.ts.map +1 -0
- package/dist/server/directory.js +193 -0
- package/dist/server/directory.js.map +1 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +10 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/manifest-source.d.ts +10 -0
- package/dist/server/manifest-source.d.ts.map +1 -0
- package/dist/server/manifest-source.js +36 -0
- package/dist/server/manifest-source.js.map +1 -0
- package/dist/server/routes.d.ts +11 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/routes.js +38 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/schemas.d.ts +69 -0
- package/dist/server/schemas.d.ts.map +1 -0
- package/dist/server/schemas.js +78 -0
- package/dist/server/schemas.js.map +1 -0
- package/dist/server/tools.d.ts +49 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +312 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/view.d.ts +54 -0
- package/dist/view.d.ts.map +1 -0
- package/dist/view.js +15 -0
- package/dist/view.js.map +1 -0
- package/dist/vite/index.d.ts +3 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +3 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/manifest.d.ts +5 -0
- package/dist/vite/manifest.d.ts.map +1 -0
- package/dist/vite/manifest.js +107 -0
- package/dist/vite/manifest.js.map +1 -0
- package/dist/vite/plugin.d.ts +16 -0
- package/dist/vite/plugin.d.ts.map +1 -0
- package/dist/vite/plugin.js +192 -0
- package/dist/vite/plugin.js.map +1 -0
- package/package.json +65 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const signatureOf = (prompt) => JSON.stringify([
|
|
2
|
+
prompt.action,
|
|
3
|
+
prompt.description,
|
|
4
|
+
prompt.input,
|
|
5
|
+
prompt.origin.via,
|
|
6
|
+
prompt.origin.name,
|
|
7
|
+
prompt.threadId,
|
|
8
|
+
]);
|
|
9
|
+
const sameItems = (a, b) => a.length === b.length && a.every((item, index) => item === b[index]);
|
|
10
|
+
export function createConfirmationStore(options) {
|
|
11
|
+
const { env } = options;
|
|
12
|
+
const listeners = new Set();
|
|
13
|
+
let entries = new Map();
|
|
14
|
+
const answered = new Set();
|
|
15
|
+
let visible = [];
|
|
16
|
+
let expiry;
|
|
17
|
+
const scheduleExpiry = () => {
|
|
18
|
+
env.clearTimeout(expiry);
|
|
19
|
+
expiry = undefined;
|
|
20
|
+
if (visible.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
const next = Math.min(...visible.map((item) => item.expiresAt));
|
|
23
|
+
expiry = env.setTimeout(expire, Math.max(0, next - env.now()));
|
|
24
|
+
};
|
|
25
|
+
const commit = () => {
|
|
26
|
+
const next = [...entries.values()]
|
|
27
|
+
.map((entry) => entry.item)
|
|
28
|
+
.filter((item) => !answered.has(item.id));
|
|
29
|
+
if (sameItems(next, visible))
|
|
30
|
+
return;
|
|
31
|
+
visible = next;
|
|
32
|
+
scheduleExpiry();
|
|
33
|
+
for (const listener of listeners)
|
|
34
|
+
listener();
|
|
35
|
+
options.onChange();
|
|
36
|
+
};
|
|
37
|
+
function expire() {
|
|
38
|
+
const now = env.now();
|
|
39
|
+
entries = new Map([...entries].filter(([, entry]) => entry.item.expiresAt > now));
|
|
40
|
+
commit();
|
|
41
|
+
scheduleExpiry();
|
|
42
|
+
}
|
|
43
|
+
const respond = (id, approve) => {
|
|
44
|
+
if (answered.has(id))
|
|
45
|
+
return;
|
|
46
|
+
answered.add(id);
|
|
47
|
+
options.answer(id, approve);
|
|
48
|
+
commit();
|
|
49
|
+
};
|
|
50
|
+
const itemOf = (prompt) => ({
|
|
51
|
+
id: prompt.id,
|
|
52
|
+
action: prompt.action,
|
|
53
|
+
description: prompt.description,
|
|
54
|
+
input: prompt.input,
|
|
55
|
+
origin: prompt.origin,
|
|
56
|
+
expiresAt: env.now() + prompt.expiresInMs,
|
|
57
|
+
threadId: prompt.threadId,
|
|
58
|
+
approve: () => respond(prompt.id, true),
|
|
59
|
+
decline: () => respond(prompt.id, false),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
subscribe(listener) {
|
|
63
|
+
listeners.add(listener);
|
|
64
|
+
if (listeners.size === 1)
|
|
65
|
+
options.onWatchedChange();
|
|
66
|
+
return () => {
|
|
67
|
+
if (!listeners.delete(listener))
|
|
68
|
+
return;
|
|
69
|
+
if (listeners.size === 0)
|
|
70
|
+
options.onWatchedChange();
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
get: () => visible,
|
|
74
|
+
replace(prompts) {
|
|
75
|
+
const next = new Map();
|
|
76
|
+
for (const prompt of prompts) {
|
|
77
|
+
const signature = signatureOf(prompt);
|
|
78
|
+
const known = entries.get(prompt.id);
|
|
79
|
+
next.set(prompt.id, known?.signature === signature ? known : { signature, item: itemOf(prompt) });
|
|
80
|
+
}
|
|
81
|
+
entries = next;
|
|
82
|
+
for (const id of answered)
|
|
83
|
+
if (!next.has(id))
|
|
84
|
+
answered.delete(id);
|
|
85
|
+
commit();
|
|
86
|
+
},
|
|
87
|
+
watched: () => listeners.size > 0,
|
|
88
|
+
dispose() {
|
|
89
|
+
env.clearTimeout(expiry);
|
|
90
|
+
expiry = undefined;
|
|
91
|
+
listeners.clear();
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=confirmations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmations.js","sourceRoot":"","sources":["../../src/client/confirmations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentOrigin, ConfirmationPrompt } from \"../protocol.js\";\nimport type { BrowserEnvironment } from \"./environment.js\";\n\n/** A pending agent request the user must approve, as an app renders it. */\nexport interface AgentConfirmation {\n readonly id: string;\n readonly action: string;\n readonly description: string;\n /** The validated input the action will run with, as JSON. */\n readonly input: unknown;\n readonly origin: AgentOrigin;\n /** Local `Date.now()` time after which the server stops waiting. */\n readonly expiresAt: number;\n /** The chat thread an inline agent asked from; the chat shows the card inline there. */\n readonly threadId?: string | undefined;\n readonly approve: () => void;\n readonly decline: () => void;\n}\n\nexport interface ConfirmationStore {\n subscribe(listener: () => void): () => void;\n get(): readonly AgentConfirmation[];\n}\n\nexport interface ConfirmationStoreRuntime extends ConfirmationStore {\n replace(items: readonly ConfirmationPrompt[]): void;\n watched(): boolean;\n dispose(): void;\n}\n\nexport interface ConfirmationStoreOptions {\n readonly env: Pick<BrowserEnvironment, \"now\" | \"setTimeout\" | \"clearTimeout\">;\n readonly answer: (id: string, approve: boolean) => void;\n readonly onWatchedChange: () => void;\n readonly onChange: () => void;\n}\n\ninterface Entry {\n readonly signature: string;\n readonly item: AgentConfirmation;\n}\n\nconst signatureOf = (prompt: ConfirmationPrompt) =>\n JSON.stringify([\n prompt.action,\n prompt.description,\n prompt.input,\n prompt.origin.via,\n prompt.origin.name,\n prompt.threadId,\n ]);\n\nconst sameItems = (a: readonly AgentConfirmation[], b: readonly AgentConfirmation[]) =>\n a.length === b.length && a.every((item, index) => item === b[index]);\n\nexport function createConfirmationStore(\n options: ConfirmationStoreOptions,\n): ConfirmationStoreRuntime {\n const { env } = options;\n const listeners = new Set<() => void>();\n let entries = new Map<string, Entry>();\n const answered = new Set<string>();\n let visible: readonly AgentConfirmation[] = [];\n let expiry: ReturnType<typeof setTimeout> | undefined;\n\n const scheduleExpiry = () => {\n env.clearTimeout(expiry);\n expiry = undefined;\n if (visible.length === 0) return;\n const next = Math.min(...visible.map((item) => item.expiresAt));\n expiry = env.setTimeout(expire, Math.max(0, next - env.now()));\n };\n\n const commit = () => {\n const next = [...entries.values()]\n .map((entry) => entry.item)\n .filter((item) => !answered.has(item.id));\n if (sameItems(next, visible)) return;\n visible = next;\n scheduleExpiry();\n for (const listener of listeners) listener();\n options.onChange();\n };\n\n function expire() {\n const now = env.now();\n entries = new Map([...entries].filter(([, entry]) => entry.item.expiresAt > now));\n commit();\n scheduleExpiry();\n }\n\n const respond = (id: string, approve: boolean) => {\n if (answered.has(id)) return;\n answered.add(id);\n options.answer(id, approve);\n commit();\n };\n\n const itemOf = (prompt: ConfirmationPrompt): AgentConfirmation => ({\n id: prompt.id,\n action: prompt.action,\n description: prompt.description,\n input: prompt.input,\n origin: prompt.origin,\n expiresAt: env.now() + prompt.expiresInMs,\n threadId: prompt.threadId,\n approve: () => respond(prompt.id, true),\n decline: () => respond(prompt.id, false),\n });\n\n return {\n subscribe(listener) {\n listeners.add(listener);\n if (listeners.size === 1) options.onWatchedChange();\n return () => {\n if (!listeners.delete(listener)) return;\n if (listeners.size === 0) options.onWatchedChange();\n };\n },\n get: () => visible,\n replace(prompts) {\n const next = new Map<string, Entry>();\n for (const prompt of prompts) {\n const signature = signatureOf(prompt);\n const known = entries.get(prompt.id);\n next.set(\n prompt.id,\n known?.signature === signature ? known : { signature, item: itemOf(prompt) },\n );\n }\n entries = next;\n for (const id of answered) if (!next.has(id)) answered.delete(id);\n commit();\n },\n watched: () => listeners.size > 0,\n dispose() {\n env.clearTimeout(expiry);\n expiry = undefined;\n listeners.clear();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface IntersectionObserverLike {
|
|
2
|
+
observe(element: Element): void;
|
|
3
|
+
unobserve(element: Element): void;
|
|
4
|
+
disconnect(): void;
|
|
5
|
+
}
|
|
6
|
+
export interface IntersectionEntryLike {
|
|
7
|
+
readonly target: Element;
|
|
8
|
+
readonly isIntersecting: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface RectLike {
|
|
11
|
+
readonly top: number;
|
|
12
|
+
readonly bottom: number;
|
|
13
|
+
readonly left: number;
|
|
14
|
+
readonly right: number;
|
|
15
|
+
readonly width: number;
|
|
16
|
+
readonly height: number;
|
|
17
|
+
}
|
|
18
|
+
export interface BrowserEnvironment {
|
|
19
|
+
readonly document: Document | undefined;
|
|
20
|
+
readonly window: (Window & typeof globalThis) | undefined;
|
|
21
|
+
readonly observeIntersections: (callback: (entries: readonly IntersectionEntryLike[]) => void) => IntersectionObserverLike | undefined;
|
|
22
|
+
readonly rect: (element: Element) => RectLike;
|
|
23
|
+
readonly viewport: () => {
|
|
24
|
+
readonly width: number;
|
|
25
|
+
readonly height: number;
|
|
26
|
+
};
|
|
27
|
+
readonly now: () => number;
|
|
28
|
+
readonly nextTask: () => Promise<void>;
|
|
29
|
+
readonly setTimeout: typeof globalThis.setTimeout;
|
|
30
|
+
readonly clearTimeout: typeof globalThis.clearTimeout;
|
|
31
|
+
readonly scrollIntoView: (element: Element) => Promise<void>;
|
|
32
|
+
readonly hasFinePointer: () => boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const SCROLL_SETTLE_CAP_MS = 900;
|
|
35
|
+
export declare function scrollIntoCenter(window: (Window & typeof globalThis) | undefined, element: Element): Promise<void>;
|
|
36
|
+
export declare function browserEnvironment(): BrowserEnvironment;
|
|
37
|
+
//# sourceMappingURL=environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/client/environment.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,UAAU,CAAC,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,oBAAoB,EAAE,CAC7B,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,KAAK,IAAI,KAC1D,wBAAwB,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,UAAU,CAAC,UAAU,CAAC;IAClD,QAAQ,CAAC,YAAY,EAAE,OAAO,UAAU,CAAC,YAAY,CAAC;IACtD,QAAQ,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,QAAQ,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC;CACxC;AAED,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAKxC,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,UAAU,CAAC,GAAG,SAAS,EAChD,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED,wBAAgB,kBAAkB,IAAI,kBAAkB,CA6BvD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export const SCROLL_SETTLE_CAP_MS = 900;
|
|
2
|
+
const NO_SCROLL_FRAMES = 6;
|
|
3
|
+
const STILL_FRAMES = 2;
|
|
4
|
+
export function scrollIntoCenter(window, element) {
|
|
5
|
+
if (!window)
|
|
6
|
+
return Promise.resolve();
|
|
7
|
+
const hidden = window.document?.visibilityState === "hidden";
|
|
8
|
+
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
9
|
+
const started = window.performance.now();
|
|
10
|
+
let last = element.getBoundingClientRect().top;
|
|
11
|
+
element.scrollIntoView({
|
|
12
|
+
block: "center",
|
|
13
|
+
inline: "nearest",
|
|
14
|
+
behavior: reduced || hidden ? "instant" : "smooth",
|
|
15
|
+
});
|
|
16
|
+
if (hidden)
|
|
17
|
+
return Promise.resolve();
|
|
18
|
+
return new Promise((resolve) => {
|
|
19
|
+
let moved = false;
|
|
20
|
+
let still = 0;
|
|
21
|
+
let frames = 0;
|
|
22
|
+
let done = false;
|
|
23
|
+
const finish = () => {
|
|
24
|
+
done = true;
|
|
25
|
+
globalThis.clearTimeout(cap);
|
|
26
|
+
resolve();
|
|
27
|
+
};
|
|
28
|
+
const cap = globalThis.setTimeout(finish, SCROLL_SETTLE_CAP_MS);
|
|
29
|
+
const step = () => {
|
|
30
|
+
if (done)
|
|
31
|
+
return;
|
|
32
|
+
const { top } = element.getBoundingClientRect();
|
|
33
|
+
frames++;
|
|
34
|
+
if (top === last)
|
|
35
|
+
still++;
|
|
36
|
+
else {
|
|
37
|
+
moved = true;
|
|
38
|
+
still = 0;
|
|
39
|
+
last = top;
|
|
40
|
+
}
|
|
41
|
+
const settled = moved ? still >= STILL_FRAMES : frames >= NO_SCROLL_FRAMES;
|
|
42
|
+
if (settled || window.performance.now() - started >= SCROLL_SETTLE_CAP_MS) {
|
|
43
|
+
finish();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
window.requestAnimationFrame(step);
|
|
47
|
+
};
|
|
48
|
+
window.requestAnimationFrame(step);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function browserEnvironment() {
|
|
52
|
+
const window = typeof globalThis.window === "undefined" ? undefined : globalThis.window;
|
|
53
|
+
return {
|
|
54
|
+
document: window?.document,
|
|
55
|
+
window,
|
|
56
|
+
observeIntersections: (callback) => typeof IntersectionObserver === "undefined"
|
|
57
|
+
? undefined
|
|
58
|
+
: new IntersectionObserver((entries) => callback(entries)),
|
|
59
|
+
rect: (element) => element.getBoundingClientRect(),
|
|
60
|
+
viewport: () => ({
|
|
61
|
+
width: window?.innerWidth ?? 0,
|
|
62
|
+
height: window?.innerHeight ?? 0,
|
|
63
|
+
}),
|
|
64
|
+
now: () => Date.now(),
|
|
65
|
+
nextTask: () => new Promise((resolve) => {
|
|
66
|
+
const channel = new MessageChannel();
|
|
67
|
+
channel.port1.onmessage = () => {
|
|
68
|
+
channel.port1.close();
|
|
69
|
+
resolve();
|
|
70
|
+
};
|
|
71
|
+
channel.port2.postMessage(undefined);
|
|
72
|
+
}),
|
|
73
|
+
setTimeout: globalThis.setTimeout.bind(globalThis),
|
|
74
|
+
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
|
75
|
+
scrollIntoView: (element) => scrollIntoCenter(window, element),
|
|
76
|
+
hasFinePointer: () => window?.matchMedia("(pointer: fine)").matches ?? false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/client/environment.ts"],"names":[],"mappings":"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","sourcesContent":["export interface IntersectionObserverLike {\n observe(element: Element): void;\n unobserve(element: Element): void;\n disconnect(): void;\n}\n\nexport interface IntersectionEntryLike {\n readonly target: Element;\n readonly isIntersecting: boolean;\n}\n\nexport interface RectLike {\n readonly top: number;\n readonly bottom: number;\n readonly left: number;\n readonly right: number;\n readonly width: number;\n readonly height: number;\n}\n\nexport interface BrowserEnvironment {\n readonly document: Document | undefined;\n readonly window: (Window & typeof globalThis) | undefined;\n readonly observeIntersections: (\n callback: (entries: readonly IntersectionEntryLike[]) => void,\n ) => IntersectionObserverLike | undefined;\n readonly rect: (element: Element) => RectLike;\n readonly viewport: () => { readonly width: number; readonly height: number };\n readonly now: () => number;\n readonly nextTask: () => Promise<void>;\n readonly setTimeout: typeof globalThis.setTimeout;\n readonly clearTimeout: typeof globalThis.clearTimeout;\n readonly scrollIntoView: (element: Element) => Promise<void>;\n readonly hasFinePointer: () => boolean;\n}\n\nexport const SCROLL_SETTLE_CAP_MS = 900;\n\nconst NO_SCROLL_FRAMES = 6;\nconst STILL_FRAMES = 2;\n\nexport function scrollIntoCenter(\n window: (Window & typeof globalThis) | undefined,\n element: Element,\n): Promise<void> {\n if (!window) return Promise.resolve();\n const hidden = window.document?.visibilityState === \"hidden\";\n const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n const started = window.performance.now();\n let last = element.getBoundingClientRect().top;\n element.scrollIntoView({\n block: \"center\",\n inline: \"nearest\",\n behavior: reduced || hidden ? \"instant\" : \"smooth\",\n });\n if (hidden) return Promise.resolve();\n return new Promise((resolve) => {\n let moved = false;\n let still = 0;\n let frames = 0;\n let done = false;\n const finish = () => {\n done = true;\n globalThis.clearTimeout(cap);\n resolve();\n };\n const cap = globalThis.setTimeout(finish, SCROLL_SETTLE_CAP_MS);\n const step = () => {\n if (done) return;\n const { top } = element.getBoundingClientRect();\n frames++;\n if (top === last) still++;\n else {\n moved = true;\n still = 0;\n last = top;\n }\n const settled = moved ? still >= STILL_FRAMES : frames >= NO_SCROLL_FRAMES;\n if (settled || window.performance.now() - started >= SCROLL_SETTLE_CAP_MS) {\n finish();\n return;\n }\n window.requestAnimationFrame(step);\n };\n window.requestAnimationFrame(step);\n });\n}\n\nexport function browserEnvironment(): BrowserEnvironment {\n const window = typeof globalThis.window === \"undefined\" ? undefined : globalThis.window;\n return {\n document: window?.document,\n window,\n observeIntersections: (callback) =>\n typeof IntersectionObserver === \"undefined\"\n ? undefined\n : new IntersectionObserver((entries) => callback(entries)),\n rect: (element) => element.getBoundingClientRect(),\n viewport: () => ({\n width: window?.innerWidth ?? 0,\n height: window?.innerHeight ?? 0,\n }),\n now: () => Date.now(),\n nextTask: () =>\n new Promise((resolve) => {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n channel.port1.close();\n resolve();\n };\n channel.port2.postMessage(undefined);\n }),\n setTimeout: globalThis.setTimeout.bind(globalThis),\n clearTimeout: globalThis.clearTimeout.bind(globalThis),\n scrollIntoView: (element) => scrollIntoCenter(window, element),\n hasFinePointer: () => window?.matchMedia(\"(pointer: fine)\").matches ?? false,\n };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UiError, UiOpInput, UiOpResults } from "../protocol.js";
|
|
2
|
+
import type { BrowserEnvironment } from "./environment.js";
|
|
3
|
+
import type { ViewRegistryRuntime } from "./registry.js";
|
|
4
|
+
export declare const HIGHLIGHT_ATTRIBUTE = "data-parlor-highlight";
|
|
5
|
+
export declare const HIGHLIGHT_HOLD_MS = 2000;
|
|
6
|
+
export interface HighlightMarks {
|
|
7
|
+
mark(element: Element): void;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createHighlightMarks(env: Pick<BrowserEnvironment, "setTimeout" | "clearTimeout">, holdMs?: number): HighlightMarks;
|
|
11
|
+
export declare function highlightView(registry: ViewRegistryRuntime, input: UiOpInput<"highlight">, env: BrowserEnvironment, marks: HighlightMarks): Promise<UiOpResults["highlight"] | UiError>;
|
|
12
|
+
//# sourceMappingURL=highlight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight.d.ts","sourceRoot":"","sources":["../../src/client/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAIzD,eAAO,MAAM,mBAAmB,0BAA0B,CAAC;AAC3D,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,cAAc,CAAC,EAC5D,MAAM,GAAE,MAA0B,GACjC,cAAc,CAoBhB;AAqDD,wBAAsB,aAAa,CACjC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,SAAS,CAAC,WAAW,CAAC,EAC7B,GAAG,EAAE,kBAAkB,EACvB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,CAgC7C"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { measure } from "./screen.js";
|
|
2
|
+
import { notMounted } from "./write.js";
|
|
3
|
+
export const HIGHLIGHT_ATTRIBUTE = "data-parlor-highlight";
|
|
4
|
+
export const HIGHLIGHT_HOLD_MS = 2000;
|
|
5
|
+
export function createHighlightMarks(env, holdMs = HIGHLIGHT_HOLD_MS) {
|
|
6
|
+
const timers = new Map();
|
|
7
|
+
const clear = (element) => {
|
|
8
|
+
env.clearTimeout(timers.get(element));
|
|
9
|
+
timers.delete(element);
|
|
10
|
+
element.removeAttribute(HIGHLIGHT_ATTRIBUTE);
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
mark(element) {
|
|
14
|
+
env.clearTimeout(timers.get(element));
|
|
15
|
+
element.setAttribute(HIGHLIGHT_ATTRIBUTE, "");
|
|
16
|
+
timers.set(element, env.setTimeout(() => clear(element), holdMs));
|
|
17
|
+
},
|
|
18
|
+
dispose() {
|
|
19
|
+
for (const element of [...timers.keys()])
|
|
20
|
+
clear(element);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const quote = (name) => name.replace(/["\\]/g, "\\$&");
|
|
25
|
+
const NON_TEXT_INPUTS = new Set([
|
|
26
|
+
"button",
|
|
27
|
+
"checkbox",
|
|
28
|
+
"color",
|
|
29
|
+
"file",
|
|
30
|
+
"image",
|
|
31
|
+
"radio",
|
|
32
|
+
"range",
|
|
33
|
+
"reset",
|
|
34
|
+
"submit",
|
|
35
|
+
]);
|
|
36
|
+
const CONTENT_EDITABLE = '[contenteditable=""],[contenteditable="true"],[contenteditable="plaintext-only"]';
|
|
37
|
+
function takesTyping(element) {
|
|
38
|
+
switch (element.tagName) {
|
|
39
|
+
case "TEXTAREA":
|
|
40
|
+
case "SELECT":
|
|
41
|
+
return true;
|
|
42
|
+
case "INPUT":
|
|
43
|
+
return !NON_TEXT_INPUTS.has(element.type);
|
|
44
|
+
default:
|
|
45
|
+
return element.closest(CONTENT_EDITABLE) !== null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function typingElsewhere(registry, env, target) {
|
|
49
|
+
const document = env.document;
|
|
50
|
+
if (!document?.hasFocus())
|
|
51
|
+
return undefined;
|
|
52
|
+
const active = document.activeElement;
|
|
53
|
+
return active &&
|
|
54
|
+
active !== target &&
|
|
55
|
+
active !== registry.agentFocus() &&
|
|
56
|
+
takesTyping(active)
|
|
57
|
+
? active
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
|
60
|
+
async function untilIntersectionsReported(env) {
|
|
61
|
+
// The observer's notification is queued after our first task, so wait for a second.
|
|
62
|
+
await env.nextTask();
|
|
63
|
+
await env.nextTask();
|
|
64
|
+
}
|
|
65
|
+
export async function highlightView(registry, input, env, marks) {
|
|
66
|
+
const root = registry.find(input)?.element;
|
|
67
|
+
if (!root)
|
|
68
|
+
return notMounted(input);
|
|
69
|
+
const element = input.field === undefined
|
|
70
|
+
? root
|
|
71
|
+
: root.querySelector(`[name="${quote(input.field)}"]`);
|
|
72
|
+
if (!element) {
|
|
73
|
+
return {
|
|
74
|
+
code: "not_mounted",
|
|
75
|
+
message: `${input.view} has no field ${input.field} on screen.`,
|
|
76
|
+
issues: [{ path: "field", message: input.field }],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const typing = typingElsewhere(registry, env, element);
|
|
80
|
+
if (typing) {
|
|
81
|
+
const name = typing.getAttribute("name") ?? typing.tagName.toLowerCase();
|
|
82
|
+
return {
|
|
83
|
+
code: "user_editing",
|
|
84
|
+
message: `The user is typing in ${name}. Wait, or ask them.`,
|
|
85
|
+
issues: [{ path: name, message: "The user is typing here." }],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
await registry.quietly(async () => {
|
|
89
|
+
if (input.field !== undefined && env.hasFinePointer()) {
|
|
90
|
+
registry.placeFocus(element);
|
|
91
|
+
}
|
|
92
|
+
await env.scrollIntoView(element);
|
|
93
|
+
await untilIntersectionsReported(env);
|
|
94
|
+
});
|
|
95
|
+
marks.mark(element);
|
|
96
|
+
return { shown: measure(element, env).shown };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=highlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight.js","sourceRoot":"","sources":["../../src/client/highlight.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UiError, UiOpInput, UiOpResults } from \"../protocol.js\";\nimport type { BrowserEnvironment } from \"./environment.js\";\nimport type { ViewRegistryRuntime } from \"./registry.js\";\nimport { measure } from \"./screen.js\";\nimport { notMounted } from \"./write.js\";\n\nexport const HIGHLIGHT_ATTRIBUTE = \"data-parlor-highlight\";\nexport const HIGHLIGHT_HOLD_MS = 2000;\n\nexport interface HighlightMarks {\n mark(element: Element): void;\n dispose(): void;\n}\n\nexport function createHighlightMarks(\n env: Pick<BrowserEnvironment, \"setTimeout\" | \"clearTimeout\">,\n holdMs: number = HIGHLIGHT_HOLD_MS,\n): HighlightMarks {\n const timers = new Map<Element, ReturnType<typeof setTimeout>>();\n const clear = (element: Element) => {\n env.clearTimeout(timers.get(element));\n timers.delete(element);\n element.removeAttribute(HIGHLIGHT_ATTRIBUTE);\n };\n return {\n mark(element) {\n env.clearTimeout(timers.get(element));\n element.setAttribute(HIGHLIGHT_ATTRIBUTE, \"\");\n timers.set(\n element,\n env.setTimeout(() => clear(element), holdMs),\n );\n },\n dispose() {\n for (const element of [...timers.keys()]) clear(element);\n },\n };\n}\n\nconst quote = (name: string) => name.replace(/[\"\\\\]/g, \"\\\\$&\");\n\nconst NON_TEXT_INPUTS: ReadonlySet<string> = new Set([\n \"button\",\n \"checkbox\",\n \"color\",\n \"file\",\n \"image\",\n \"radio\",\n \"range\",\n \"reset\",\n \"submit\",\n]);\n\nconst CONTENT_EDITABLE =\n '[contenteditable=\"\"],[contenteditable=\"true\"],[contenteditable=\"plaintext-only\"]';\n\nfunction takesTyping(element: Element): boolean {\n switch (element.tagName) {\n case \"TEXTAREA\":\n case \"SELECT\":\n return true;\n case \"INPUT\":\n return !NON_TEXT_INPUTS.has((element as HTMLInputElement).type);\n default:\n return element.closest(CONTENT_EDITABLE) !== null;\n }\n}\n\nfunction typingElsewhere(\n registry: Pick<ViewRegistryRuntime, \"agentFocus\">,\n env: Pick<BrowserEnvironment, \"document\">,\n target: Element,\n): Element | undefined {\n const document = env.document;\n if (!document?.hasFocus()) return undefined;\n const active = document.activeElement;\n return active &&\n active !== target &&\n active !== registry.agentFocus() &&\n takesTyping(active)\n ? active\n : undefined;\n}\n\nasync function untilIntersectionsReported(env: Pick<BrowserEnvironment, \"nextTask\">) {\n // The observer's notification is queued after our first task, so wait for a second.\n await env.nextTask();\n await env.nextTask();\n}\n\nexport async function highlightView(\n registry: ViewRegistryRuntime,\n input: UiOpInput<\"highlight\">,\n env: BrowserEnvironment,\n marks: HighlightMarks,\n): Promise<UiOpResults[\"highlight\"] | UiError> {\n const root = registry.find(input)?.element;\n if (!root) return notMounted(input);\n const element =\n input.field === undefined\n ? root\n : root.querySelector<HTMLElement>(`[name=\"${quote(input.field)}\"]`);\n if (!element) {\n return {\n code: \"not_mounted\",\n message: `${input.view} has no field ${input.field} on screen.`,\n issues: [{ path: \"field\", message: input.field! }],\n };\n }\n const typing = typingElsewhere(registry, env, element);\n if (typing) {\n const name = typing.getAttribute(\"name\") ?? typing.tagName.toLowerCase();\n return {\n code: \"user_editing\",\n message: `The user is typing in ${name}. Wait, or ask them.`,\n issues: [{ path: name, message: \"The user is typing here.\" }],\n };\n }\n await registry.quietly(async () => {\n if (input.field !== undefined && env.hasFinePointer()) {\n registry.placeFocus(element as HTMLElement);\n }\n await env.scrollIntoView(element);\n await untilIntersectionsReported(env);\n });\n marks.mark(element);\n return { shown: measure(element, env).shown };\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { type ActivityStore, type AgentActivity, type AgentActivityKind, type AgentOutcome, type AgentUiClient, type AgentUiClientOptions, createAgentUiClient, type QuerySource, routeSnapshot, } from "./agent-ui-client.js";
|
|
2
|
+
export { type BrowserEnvironment, browserEnvironment, type IntersectionEntryLike, type IntersectionObserverLike, type RectLike, } from "./environment.js";
|
|
3
|
+
export { type AgentConfirmation, type ConfirmationStore } from "./confirmations.js";
|
|
4
|
+
export { type AgentWrites, type AnyViewBinding, NO_AGENT_WRITES, type ViewRegistry, type ViewSlot, } from "./registry.js";
|
|
5
|
+
export { agentFieldsOf } from "./write.js";
|
|
6
|
+
export { DEFAULT_SCREEN_LIMITS, type ScreenLimits } from "./screen.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,KAAK,WAAW,EAChB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,QAAQ,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpF,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,eAAe,EACf,KAAK,YAAY,EACjB,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createAgentUiClient, routeSnapshot, } from "./agent-ui-client.js";
|
|
2
|
+
export { browserEnvironment, } from "./environment.js";
|
|
3
|
+
export {} from "./confirmations.js";
|
|
4
|
+
export { NO_AGENT_WRITES, } from "./registry.js";
|
|
5
|
+
export { agentFieldsOf } from "./write.js";
|
|
6
|
+
export { DEFAULT_SCREEN_LIMITS } from "./screen.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,mBAAmB,EAEnB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,kBAAkB,GAInB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAkD,MAAM,oBAAoB,CAAC;AACpF,OAAO,EAGL,eAAe,GAGhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAqB,MAAM,aAAa,CAAC","sourcesContent":["export {\n type ActivityStore,\n type AgentActivity,\n type AgentActivityKind,\n type AgentOutcome,\n type AgentUiClient,\n type AgentUiClientOptions,\n createAgentUiClient,\n type QuerySource,\n routeSnapshot,\n} from \"./agent-ui-client.js\";\nexport {\n type BrowserEnvironment,\n browserEnvironment,\n type IntersectionEntryLike,\n type IntersectionObserverLike,\n type RectLike,\n} from \"./environment.js\";\nexport { type AgentConfirmation, type ConfirmationStore } from \"./confirmations.js\";\nexport {\n type AgentWrites,\n type AnyViewBinding,\n NO_AGENT_WRITES,\n type ViewRegistry,\n type ViewSlot,\n} from \"./registry.js\";\nexport { agentFieldsOf } from \"./write.js\";\nexport { DEFAULT_SCREEN_LIMITS, type ScreenLimits } from \"./screen.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-equal.d.ts","sourceRoot":"","sources":["../../src/client/json-equal.ts"],"names":[],"mappings":"AAMA,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAazD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const isPlainObject = (value) => {
|
|
2
|
+
if (typeof value !== "object" || value === null)
|
|
3
|
+
return false;
|
|
4
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5
|
+
return prototype === Object.prototype || prototype === null;
|
|
6
|
+
};
|
|
7
|
+
export function jsonEqual(a, b) {
|
|
8
|
+
if (Object.is(a, b))
|
|
9
|
+
return true;
|
|
10
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
11
|
+
return a.length === b.length && a.every((item, index) => jsonEqual(item, b[index]));
|
|
12
|
+
}
|
|
13
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
14
|
+
const keys = Object.keys(a);
|
|
15
|
+
return (keys.length === Object.keys(b).length &&
|
|
16
|
+
keys.every((key) => Object.hasOwn(b, key) && jsonEqual(a[key], b[key])));
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=json-equal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-equal.js","sourceRoot":"","sources":["../../src/client/json-equal.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,CAAC,KAAc,EAAoC,EAAE;IACzE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC9D,CAAC,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,CAAU,EAAE,CAAU;IAC9C,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtF,CAAC;IACD,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,OAAO,CACL,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM;YACrC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["const isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== \"object\" || value === null) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nexport function jsonEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, index) => jsonEqual(item, b[index]));\n }\n if (isPlainObject(a) && isPlainObject(b)) {\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((key) => Object.hasOwn(b, key) && jsonEqual(a[key], b[key]))\n );\n }\n return false;\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AnyAgentView, ViewRef } from "../view.js";
|
|
2
|
+
import type { BrowserEnvironment } from "./environment.js";
|
|
3
|
+
export interface AnyViewBinding {
|
|
4
|
+
readonly key?: string | undefined;
|
|
5
|
+
get(): Readonly<Record<string, unknown>>;
|
|
6
|
+
set?(next: Readonly<Record<string, unknown>>, changed: readonly string[]): void | Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export type AgentWrites = ReadonlyMap<string, unknown>;
|
|
9
|
+
export declare const NO_AGENT_WRITES: AgentWrites;
|
|
10
|
+
export interface ViewSlot {
|
|
11
|
+
readonly ref: (element: Element | null) => void;
|
|
12
|
+
bind(binding: AnyViewBinding): void;
|
|
13
|
+
mount(key: string | undefined): () => void;
|
|
14
|
+
readonly subscribeWrites: (listener: () => void) => () => void;
|
|
15
|
+
readonly writes: () => AgentWrites;
|
|
16
|
+
}
|
|
17
|
+
export interface ViewRegistry {
|
|
18
|
+
track(view: AnyAgentView, binding: AnyViewBinding): ViewSlot;
|
|
19
|
+
}
|
|
20
|
+
export interface MountedView {
|
|
21
|
+
readonly view: AnyAgentView;
|
|
22
|
+
readonly key: string | undefined;
|
|
23
|
+
readonly binding: AnyViewBinding;
|
|
24
|
+
readonly element: Element | undefined;
|
|
25
|
+
readonly writes: AgentWrites;
|
|
26
|
+
}
|
|
27
|
+
export interface Moment {
|
|
28
|
+
readonly view: MountedView;
|
|
29
|
+
readonly at: number;
|
|
30
|
+
}
|
|
31
|
+
export interface RegistryReader {
|
|
32
|
+
find(ref: ViewRef): MountedView | undefined;
|
|
33
|
+
singles(): Iterable<MountedView>;
|
|
34
|
+
onScreenCandidates(): Iterable<MountedView>;
|
|
35
|
+
keyedCount(viewName: string): number;
|
|
36
|
+
isCurrent(mounted: MountedView): boolean;
|
|
37
|
+
owner(node: Node | null): MountedView | undefined;
|
|
38
|
+
lastEntered(): Moment | undefined;
|
|
39
|
+
lastInteracted(): Moment | undefined;
|
|
40
|
+
agentFocus(): Element | undefined;
|
|
41
|
+
}
|
|
42
|
+
export interface ViewRegistryRuntime extends ViewRegistry, RegistryReader {
|
|
43
|
+
remember(mounted: MountedView, values: Readonly<Record<string, unknown>>): void;
|
|
44
|
+
quietly<T>(act: () => Promise<T>): Promise<T>;
|
|
45
|
+
placeFocus(element: HTMLElement): void;
|
|
46
|
+
restore(mounted: MountedView, writes: AgentWrites): void;
|
|
47
|
+
awaitingObserver(): boolean;
|
|
48
|
+
observed(): Promise<void>;
|
|
49
|
+
resume(): void;
|
|
50
|
+
dispose(): void;
|
|
51
|
+
}
|
|
52
|
+
export declare function createViewRegistry(env: BrowserEnvironment): ViewRegistryRuntime;
|
|
53
|
+
export declare function refOf(mounted: MountedView): ViewRef;
|
|
54
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/client/registry.ts"],"names":[],"mappings":"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"}
|