@datalayer/agent-runtimes 1.3.35 → 1.3.37
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/lib/chat/ChatFloating.d.ts +8 -2
- package/lib/chat/ChatFloating.js +2 -2
- package/lib/chat/base/ChatBase.js +10 -6
- package/lib/chat/display/EmptyState.js +8 -1
- package/lib/chat/presence/Presence.d.ts +15 -1
- package/lib/chat/presence/Presence.js +0 -13
- package/lib/components/a2ui/datalayer/Chart.d.ts +132 -0
- package/lib/components/a2ui/datalayer/Chart.js +173 -0
- package/lib/components/a2ui/datalayer/Chat.d.ts +21 -0
- package/lib/components/a2ui/datalayer/Chat.js +116 -0
- package/lib/components/a2ui/datalayer/Evidence.d.ts +22 -0
- package/lib/components/a2ui/datalayer/Evidence.js +73 -0
- package/lib/components/a2ui/datalayer/FileUpload.d.ts +22 -0
- package/lib/components/a2ui/datalayer/FileUpload.js +107 -0
- package/lib/components/a2ui/datalayer/Form.d.ts +12 -0
- package/lib/components/a2ui/datalayer/Form.js +46 -0
- package/lib/components/a2ui/datalayer/Table.d.ts +20 -0
- package/lib/components/a2ui/datalayer/Table.js +103 -0
- package/lib/components/a2ui/datalayer/drawn.d.ts +10 -0
- package/lib/components/a2ui/datalayer/drawn.js +20 -0
- package/lib/components/a2ui/datalayer/implementation.d.ts +17 -0
- package/lib/components/a2ui/datalayer/implementation.js +10 -0
- package/lib/components/a2ui/datalayer/index.d.ts +23 -0
- package/lib/components/a2ui/datalayer/index.js +44 -0
- package/lib/components/a2ui/datalayer/parts.d.ts +32 -0
- package/lib/components/a2ui/datalayer/parts.js +44 -0
- package/lib/components/a2ui/datalayer/schema.d.ts +34 -0
- package/lib/components/a2ui/datalayer/schema.js +108 -0
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +4 -2
- package/lib/components/a2ui/visibleWhen.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +8 -0
- package/lib/loop/apps/AppRenderer.js +21 -1
- package/lib/loop/embed/AppEmbed.d.ts +16 -1
- package/lib/loop/embed/AppEmbed.js +38 -9
- package/lib/loop/embed/LoopEmbed.js +2 -0
- package/lib/loop/embed/element.js +10 -1
- package/lib/loop/embed/embedTheme.d.ts +12 -0
- package/lib/loop/embed/embedTheme.js +15 -0
- package/lib/loop/plugins/assistant-characters/index.d.ts +10 -1
- package/lib/loop/plugins/assistant-characters/index.js +16 -1
- package/lib/loop/plugins/chat/ChatView.js +21 -9
- package/lib/loop/plugins/chat/index.d.ts +6 -0
- package/lib/loop/presets.d.ts +3 -0
- package/lib/loop/presets.js +2 -1
- package/lib/types/chat.d.ts +11 -0
- package/package.json +2 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The A2UI schema of a component of Datalayer's own, read from the catalog
|
|
7
|
+
* (LOOP C-13, C-18): each property its JSON Schema declares, typed as it
|
|
8
|
+
* says; each thing it shows or sends a binding into the data model — a path
|
|
9
|
+
* the application publishes, or a value given in place; its `action`, what
|
|
10
|
+
* it dispatches when a person acts on it, written as A2UI writes a Button's.
|
|
11
|
+
*
|
|
12
|
+
* A2UI's renderer reads a component's schema to tell its properties apart —
|
|
13
|
+
* a binding it resolves and writes back through, an action it dispatches, a
|
|
14
|
+
* plain value it hands over — so the schema is built with the zod A2UI reads
|
|
15
|
+
* (v3), from the catalog, and cannot drift from what the Canvas, the YAML
|
|
16
|
+
* and Python check a component against.
|
|
17
|
+
*
|
|
18
|
+
* @module components/a2ui/datalayer/schema
|
|
19
|
+
*/
|
|
20
|
+
import { z } from 'zod/v3';
|
|
21
|
+
import { ActionSchema, DataBindingSchema } from '@a2ui/web_core/v0_9';
|
|
22
|
+
import { COMPONENT_CATALOGUE } from '../../../specs/uiPlugins';
|
|
23
|
+
/** The components of Datalayer's own the catalog has, by id. */
|
|
24
|
+
export const OWN_COMPONENT_IDS = Object.values(COMPONENT_CATALOGUE)
|
|
25
|
+
.filter(component => !component.standard)
|
|
26
|
+
.map(component => component.id);
|
|
27
|
+
/** A component of Datalayer's own, from the catalog; it throws for any other. */
|
|
28
|
+
export function ownComponent(id) {
|
|
29
|
+
const component = COMPONENT_CATALOGUE[id];
|
|
30
|
+
if (!component) {
|
|
31
|
+
throw new Error(`The catalog has no component ${id}.`);
|
|
32
|
+
}
|
|
33
|
+
if (component.standard || !component.properties || !component.bindings) {
|
|
34
|
+
throw new Error(`${id} is A2UI's own: its schema is the basic catalog's, not Datalayer's.`);
|
|
35
|
+
}
|
|
36
|
+
return component;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* What a component shows or sends: a list, a record, words, a number, true
|
|
40
|
+
* or false given in place, or a path into the data model. A2UI resolves it
|
|
41
|
+
* — and gives the component a setter for it when it is a path.
|
|
42
|
+
*/
|
|
43
|
+
const BindingSchema = z
|
|
44
|
+
.union([
|
|
45
|
+
z.array(z.any()),
|
|
46
|
+
z.record(z.string(), z.unknown()),
|
|
47
|
+
z.string(),
|
|
48
|
+
z.number(),
|
|
49
|
+
z.boolean(),
|
|
50
|
+
DataBindingSchema,
|
|
51
|
+
])
|
|
52
|
+
.describe('REF:#/$defs/DynamicValue|Datalayer: what the component shows or sends — a path the application publishes or takes, or a value given in place.');
|
|
53
|
+
/** A property as its JSON Schema types it. */
|
|
54
|
+
function propertySchema(name, property) {
|
|
55
|
+
if (property.enum) {
|
|
56
|
+
return z.enum(property.enum);
|
|
57
|
+
}
|
|
58
|
+
switch (property.type) {
|
|
59
|
+
case 'string':
|
|
60
|
+
return z.string();
|
|
61
|
+
case 'integer':
|
|
62
|
+
case 'number':
|
|
63
|
+
return z.number();
|
|
64
|
+
case 'boolean':
|
|
65
|
+
return z.boolean();
|
|
66
|
+
case 'array':
|
|
67
|
+
return z.array(z.any());
|
|
68
|
+
case 'object':
|
|
69
|
+
return z.record(z.string(), z.unknown());
|
|
70
|
+
default:
|
|
71
|
+
throw new Error(`${name} is typed ${String(property.type)}, which a component's property cannot be.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** The names a component shows and sends through, each once. */
|
|
75
|
+
export function bindingNames(component) {
|
|
76
|
+
const { shows, sends } = component.bindings;
|
|
77
|
+
return [...new Set([...shows, ...sends])];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The A2UI schema of a component of Datalayer's own: its properties as its
|
|
81
|
+
* JSON Schema has them, its bindings, its `action`, and the `weight` every
|
|
82
|
+
* basic component takes inside a Row or a Column. Strict, as the basic
|
|
83
|
+
* catalog's are: a key it does not declare refuses the tree.
|
|
84
|
+
*/
|
|
85
|
+
export function ownComponentSchema(id) {
|
|
86
|
+
const component = ownComponent(id);
|
|
87
|
+
const json = component.properties;
|
|
88
|
+
const required = new Set(json.required ?? []);
|
|
89
|
+
const shape = {};
|
|
90
|
+
for (const [name, property] of Object.entries(json.properties ?? {})) {
|
|
91
|
+
const schema = propertySchema(name, property);
|
|
92
|
+
shape[name] = required.has(name) ? schema : schema.optional();
|
|
93
|
+
}
|
|
94
|
+
for (const name of bindingNames(component)) {
|
|
95
|
+
if (name in shape) {
|
|
96
|
+
throw new Error(`${id} both declares ${name} as a property and binds through it.`);
|
|
97
|
+
}
|
|
98
|
+
shape[name] = BindingSchema.optional();
|
|
99
|
+
}
|
|
100
|
+
shape.action = ActionSchema
|
|
101
|
+
.describe('REF:#/$defs/Action|What it dispatches when a person acts on it.')
|
|
102
|
+
.optional();
|
|
103
|
+
shape.weight = z
|
|
104
|
+
.number()
|
|
105
|
+
.describe('The relative weight of this component within a Row or Column.')
|
|
106
|
+
.optional();
|
|
107
|
+
return z.object(shape).strict();
|
|
108
|
+
}
|
|
@@ -11,7 +11,9 @@ export declare function nodeIsShown(props: Record<string, unknown>): boolean;
|
|
|
11
11
|
/** A component, its schema declaring `visible_when`, drawn behind the guard. */
|
|
12
12
|
export declare function withVisibleWhen(component: ReactComponentImplementation): ReactComponentImplementation;
|
|
13
13
|
/**
|
|
14
|
-
* The catalog Datalayer's renderer draws from: A2UI's basic catalog
|
|
15
|
-
*
|
|
14
|
+
* The catalog Datalayer's renderer draws from: A2UI's basic catalog and
|
|
15
|
+
* Datalayer's own components (Table, Chart, File upload, Chat, Evidence,
|
|
16
|
+
* Form — `./datalayer`), under the basic catalog's id — the payloads name
|
|
17
|
+
* it — with `visible_when` on every component.
|
|
16
18
|
*/
|
|
17
19
|
export declare const datalayerCatalog: Catalog<ReactComponentImplementation, import("@a2ui/web_core/v0_9").FunctionImplementation>;
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { basicCatalog, useSignalValue, } from '@a2ui/react/v0_9';
|
|
3
3
|
import { Catalog, DynamicBooleanSchema, ResolvedBinding, isWritable, } from '@a2ui/web_core/v0_9';
|
|
4
4
|
import { VISIBLE_WHEN, isShown } from './visibility';
|
|
5
|
+
import { OWN_COMPONENTS } from './datalayer';
|
|
5
6
|
/**
|
|
6
7
|
* Whether a node's resolved properties let it be drawn: no condition, or one
|
|
7
8
|
* that holds. A2UI resolves a dynamic property to a binding whether the block
|
|
@@ -48,7 +49,9 @@ export function withVisibleWhen(component) {
|
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
|
-
* The catalog Datalayer's renderer draws from: A2UI's basic catalog
|
|
52
|
-
*
|
|
52
|
+
* The catalog Datalayer's renderer draws from: A2UI's basic catalog and
|
|
53
|
+
* Datalayer's own components (Table, Chart, File upload, Chat, Evidence,
|
|
54
|
+
* Form — `./datalayer`), under the basic catalog's id — the payloads name
|
|
55
|
+
* it — with `visible_when` on every component.
|
|
53
56
|
*/
|
|
54
|
-
export const datalayerCatalog = new Catalog(basicCatalog.id, basicCatalog.protocolVersion, [...basicCatalog.components.values()].map(withVisibleWhen), [...basicCatalog.functions.values()], basicCatalog.themeSchema, basicCatalog.instructions);
|
|
57
|
+
export const datalayerCatalog = new Catalog(basicCatalog.id, basicCatalog.protocolVersion, [...basicCatalog.components.values(), ...OWN_COMPONENTS].map(withVisibleWhen), [...basicCatalog.functions.values()], basicCatalog.themeSchema, basicCatalog.instructions);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactorPlugin } from '@datalayer/reactor';
|
|
2
2
|
import type { AppSpec } from '../../types/agentspecs';
|
|
3
|
+
import type { ThemeOverrides } from '../../types/chat';
|
|
3
4
|
import { type LoopEmbedProps } from '../embed/LoopEmbed';
|
|
4
5
|
import type { LoopPresetOptions } from '../presets';
|
|
5
6
|
import type { PresenceState } from '../../chat/presence/presenceStatus';
|
|
@@ -79,5 +80,12 @@ export declare function appDatalayerCreatePayload(app: AppSpec, instance?: AppIn
|
|
|
79
80
|
/** No host plugins: one array, so that it never reads as a change. */
|
|
80
81
|
/** The agentspec a runtime is allocated with before an application's agent is created on it. */
|
|
81
82
|
export declare const DATALAYER_BOOTSTRAP_AGENTSPEC = "example-simple";
|
|
83
|
+
/**
|
|
84
|
+
* The application's accent over the theme its conversation wears, in both
|
|
85
|
+
* modes (LOOP T-05): its bubbles and its one button in its own colour, and
|
|
86
|
+
* not in the theme's default — the chat sets its theme again inside it, so
|
|
87
|
+
* what the page around it set does not reach in.
|
|
88
|
+
*/
|
|
89
|
+
export declare function appThemeOverrides(app: AppSpec): ThemeOverrides | undefined;
|
|
82
90
|
export declare function AppRenderer({ app, plugins, instance, onPresence, frame, ...embed }: AppRendererProps): React.JSX.Element;
|
|
83
91
|
export default AppRenderer;
|
|
@@ -34,6 +34,7 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
|
34
34
|
* @module loop/apps/AppRenderer
|
|
35
35
|
*/
|
|
36
36
|
import { useMemo } from 'react';
|
|
37
|
+
import { loopAccentStyles } from '@datalayer/primer-addons';
|
|
37
38
|
import { defineAgentCapacityPlugin } from '../plugins/agent-capacity';
|
|
38
39
|
import { APP_PAGE_SURFACE, defineAppPagePlugin, hasAppPage, } from '../plugins/app-page';
|
|
39
40
|
import { LoopEmbed } from '../embed/LoopEmbed';
|
|
@@ -158,6 +159,21 @@ export function appDatalayerCreatePayload(app, instance) {
|
|
|
158
159
|
/** The agentspec a runtime is allocated with before an application's agent is created on it. */
|
|
159
160
|
export const DATALAYER_BOOTSTRAP_AGENTSPEC = 'example-simple';
|
|
160
161
|
const NO_PLUGINS = [];
|
|
162
|
+
/**
|
|
163
|
+
* The application's accent over the theme its conversation wears, in both
|
|
164
|
+
* modes (LOOP T-05): its bubbles and its one button in its own colour, and
|
|
165
|
+
* not in the theme's default — the chat sets its theme again inside it, so
|
|
166
|
+
* what the page around it set does not reach in.
|
|
167
|
+
*/
|
|
168
|
+
export function appThemeOverrides(app) {
|
|
169
|
+
const accent = app.interface?.accent;
|
|
170
|
+
return accent
|
|
171
|
+
? {
|
|
172
|
+
light: loopAccentStyles(accent, 'light'),
|
|
173
|
+
dark: loopAccentStyles(accent, 'dark'),
|
|
174
|
+
}
|
|
175
|
+
: undefined;
|
|
176
|
+
}
|
|
161
177
|
export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, frame = false, ...embed }) {
|
|
162
178
|
/*
|
|
163
179
|
* The application plugin, made once per application. `LoopEmbed` rebuilds
|
|
@@ -198,6 +214,10 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, f
|
|
|
198
214
|
const datalayerCreatePayload = useMemo(() => (app.agent ? appDatalayerCreatePayload(app, instance) : undefined),
|
|
199
215
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
200
216
|
[source, instance?.appUid, instance?.deploymentUid, instance?.version]);
|
|
217
|
+
const accent = app.interface?.accent;
|
|
218
|
+
const themeOverrides = useMemo(() => appThemeOverrides(app),
|
|
219
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
220
|
+
[accent]);
|
|
201
221
|
if (!appPlugin) {
|
|
202
222
|
return (_jsxs("div", { role: "status", style: { padding: 16 }, children: ["\u201C", app.name, "\u201D is run by a team, which the workspace does not run as an application yet."] }));
|
|
203
223
|
}
|
|
@@ -217,7 +237,7 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, instance, onPresence, f
|
|
|
217
237
|
targetFixed: true,
|
|
218
238
|
// Applications first (LOOP T-12): an application's conversation wears
|
|
219
239
|
// the `loop` theme; a host may say otherwise.
|
|
220
|
-
themeVariant: "loop",
|
|
240
|
+
themeVariant: "loop", themeOverrides: themeOverrides,
|
|
221
241
|
// Its own face, name and welcome in the chat (T-08); no counters: a
|
|
222
242
|
// person using an application is not asking about tokens.
|
|
223
243
|
presence: {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CSSProperties, JSX, ReactNode } from 'react';
|
|
2
|
+
import type { PluginRef } from '@datalayer/reactor';
|
|
2
3
|
import type { AppAccent, AppEmbedMode, AppSpec } from '../../types/agentspecs';
|
|
3
4
|
import { type AppInstance } from '../apps/AppRenderer';
|
|
5
|
+
import { type AssistantCharacterChosen } from '../plugins/assistant-characters';
|
|
4
6
|
import { type EmbedColorMode } from './embedConfig';
|
|
5
7
|
export type AppEmbedProps = {
|
|
6
8
|
/** The application. */
|
|
@@ -29,7 +31,20 @@ export type AppEmbedProps = {
|
|
|
29
31
|
* shadow root's styles stop at its edge. A React host leaves it off.
|
|
30
32
|
*/
|
|
31
33
|
ownPortal?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The host's plugins, for the assistant's character: what they contribute
|
|
36
|
+
* to `loop.assistant.character` is there beside Datalayer's four. The
|
|
37
|
+
* element passes none.
|
|
38
|
+
*/
|
|
39
|
+
plugins?: PluginRef[];
|
|
32
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* The character the assistant mode draws (T-24, D-07): the application's own
|
|
43
|
+
* (`interface.assistant`), else the paper clip, from what Datalayer's
|
|
44
|
+
* characters and the host's plugins contribute — as a page decides it, with
|
|
45
|
+
* no person's choice, since a visitor to another product has none here.
|
|
46
|
+
*/
|
|
47
|
+
export declare function embedAssistantCharacter(app: AppSpec, plugins?: PluginRef[]): AssistantCharacterChosen;
|
|
33
48
|
/** The `loop` theme, with the application's (or the host's) accent and face. */
|
|
34
49
|
export declare function EmbedThemed({ accent, colorMode, font, children, style, }: {
|
|
35
50
|
accent: AppAccent;
|
|
@@ -38,5 +53,5 @@ export declare function EmbedThemed({ accent, colorMode, font, children, style,
|
|
|
38
53
|
children: ReactNode;
|
|
39
54
|
style?: CSSProperties;
|
|
40
55
|
}): JSX.Element;
|
|
41
|
-
export declare function AppEmbed({ app, mode, accent, colorMode, font, serverUrl, instance, height, ownPortal, }: AppEmbedProps): JSX.Element;
|
|
56
|
+
export declare function AppEmbed({ app, mode, accent, colorMode, font, serverUrl, instance, height, ownPortal, plugins, }: AppEmbedProps): JSX.Element;
|
|
42
57
|
export default AppEmbed;
|
|
@@ -17,7 +17,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
* - **panel** — the conversation at the right edge of the page, at full
|
|
18
18
|
* height;
|
|
19
19
|
* - **assistant** — the application's character on the page, speaking in a
|
|
20
|
-
* balloon (§6.9)
|
|
20
|
+
* balloon (§6.9): the one its Appspec names (`interface.assistant`), else
|
|
21
|
+
* the paper clip, looked up in what is contributed — Datalayer's four and
|
|
22
|
+
* the host's own plugins (T-24); an id nothing contributes is said, never
|
|
23
|
+
* replaced.
|
|
21
24
|
*
|
|
22
25
|
* The three floating modes are `ChatFloating`'s, given the application's
|
|
23
26
|
* name, welcome, starters and accent, and its agent's endpoint — created
|
|
@@ -25,7 +28,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
25
28
|
* rules there as it does inline.
|
|
26
29
|
*
|
|
27
30
|
* In the `loop` theme with the application's accent (T-12, T-05), which the
|
|
28
|
-
* host may override with the face and the mode (D-11)
|
|
31
|
+
* host may override with the face and the mode (D-11) — around the
|
|
32
|
+
* conversation and inside it, where the chat sets its theme again.
|
|
29
33
|
*
|
|
30
34
|
* @module loop/embed/AppEmbed
|
|
31
35
|
*/
|
|
@@ -38,8 +42,20 @@ import { useAgentRuntimes } from '../../hooks/useAgentRuntimes';
|
|
|
38
42
|
import { AppRenderer, DATALAYER_BOOTSTRAP_AGENTSPEC, appDatalayerCreatePayload, defineAppPlugin, } from '../apps/AppRenderer';
|
|
39
43
|
import { LoopAgentBlueprint } from '../core';
|
|
40
44
|
import { ensureServerAgent } from '../plugins/agents/switchable';
|
|
45
|
+
import { AssistantCharactersPlugin, assistantCharacterFor, assistantCharactersFrom, } from '../plugins/assistant-characters';
|
|
41
46
|
import { floatingViewOf } from './embedConfig';
|
|
42
|
-
import { embedThemeStyles } from './embedTheme';
|
|
47
|
+
import { embedThemeOverrides, embedThemeStyles } from './embedTheme';
|
|
48
|
+
/** No host plugins: one array, so that it never reads as a change. */
|
|
49
|
+
const NO_PLUGINS = [];
|
|
50
|
+
/**
|
|
51
|
+
* The character the assistant mode draws (T-24, D-07): the application's own
|
|
52
|
+
* (`interface.assistant`), else the paper clip, from what Datalayer's
|
|
53
|
+
* characters and the host's plugins contribute — as a page decides it, with
|
|
54
|
+
* no person's choice, since a visitor to another product has none here.
|
|
55
|
+
*/
|
|
56
|
+
export function embedAssistantCharacter(app, plugins = NO_PLUGINS) {
|
|
57
|
+
return assistantCharacterFor(assistantCharactersFrom([AssistantCharactersPlugin, ...plugins]), { app: app.interface.assistant });
|
|
58
|
+
}
|
|
43
59
|
/** What the application's agent is created with on an agent-runtimes server. */
|
|
44
60
|
function serverCreatePayload(app) {
|
|
45
61
|
const blueprint = (defineAppPlugin(app).contributes ?? []).find((item) => item.point === LoopAgentBlueprint);
|
|
@@ -132,14 +148,14 @@ function Face({ emoji }) {
|
|
|
132
148
|
return (_jsx("span", { "aria-hidden": true, style: { fontSize: 18, lineHeight: 1 }, children: emoji }));
|
|
133
149
|
}
|
|
134
150
|
/** The bubble, the panel and the assistant: `ChatFloating` in that mode. */
|
|
135
|
-
function FloatingChat({ app, view, colorMode, agent, }) {
|
|
151
|
+
function FloatingChat({ app, view, colorMode, themeOverrides, character, agent, }) {
|
|
136
152
|
const welcome = app.interface.welcome || app.description;
|
|
137
153
|
return (_jsx(ChatFloating
|
|
138
154
|
// A new agent is a new conversation.
|
|
139
|
-
, { defaultViewMode: view, assistantCharacter:
|
|
155
|
+
, { defaultViewMode: view, ...(character ? { assistantCharacter: character } : {}), protocol: agent.protocol, authToken: agent.protocol?.authToken, title: app.name, description: welcome, brandIcon: _jsx(Face, { emoji: app.emoji }), buttonIcon: _jsx(Face, { emoji: app.emoji }), buttonTooltip: `Talk to ${app.name}`, suggestions: app.interface.starters.map(starter => ({
|
|
140
156
|
title: starter.label,
|
|
141
157
|
message: starter.message,
|
|
142
|
-
})), themeVariant: "loop", colorMode: colorMode, showTokenUsage: false, showSettingsButton: false, showPoweredBy: false, useStore: false, launching: !agent.protocol && !agent.error, launchingMessage: agent.waiting, disabled: Boolean(agent.error), disableReason: agent.error }, agent.protocol?.endpoint ?? 'waiting'));
|
|
158
|
+
})), themeVariant: "loop", themeOverrides: themeOverrides, colorMode: colorMode, showTokenUsage: false, showSettingsButton: false, showPoweredBy: false, useStore: false, launching: !agent.protocol && !agent.error, launchingMessage: agent.waiting, disabled: Boolean(agent.error), disableReason: agent.error }, agent.protocol?.endpoint ?? 'waiting'));
|
|
143
159
|
}
|
|
144
160
|
/** Floating, its agent on the host's agent-runtimes server. */
|
|
145
161
|
function ServerFloating({ serverUrl, ...props }) {
|
|
@@ -192,14 +208,27 @@ export function EmbedThemed({ accent, colorMode, font, children, style, }) {
|
|
|
192
208
|
*/
|
|
193
209
|
_jsx("div", { "data-color-mode": colorMode, style: { display: 'contents' }, children: _jsx(DatalayerThemeProvider, { colorMode: colorMode, theme: loopTheme, themeStyles: themeStyles, baseStyles: style, children: children }) }));
|
|
194
210
|
}
|
|
195
|
-
export function AppEmbed({ app, mode, accent, colorMode = 'auto', font, serverUrl, instance, height = 640, ownPortal = false, }) {
|
|
211
|
+
export function AppEmbed({ app, mode, accent, colorMode = 'auto', font, serverUrl, instance, height = 640, ownPortal = false, plugins = NO_PLUGINS, }) {
|
|
196
212
|
const system = useSystemMode();
|
|
197
213
|
const resolvedMode = colorMode === 'auto' ? system : colorMode;
|
|
198
214
|
const shownAs = mode ?? app.deployment.embedded?.mode ?? 'inline';
|
|
199
215
|
const view = floatingViewOf(shownAs);
|
|
200
|
-
|
|
216
|
+
const worn = accent ?? app.interface.accent;
|
|
217
|
+
const themeOverrides = useMemo(() => embedThemeOverrides({ accent: worn, font }), [worn, font]);
|
|
218
|
+
const named = app.interface.assistant;
|
|
219
|
+
const assistant = useMemo(() => view === 'assistant' ? embedAssistantCharacter(app, plugins) : undefined,
|
|
220
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
221
|
+
[view, named, plugins]);
|
|
222
|
+
if (assistant && 'problem' in assistant) {
|
|
223
|
+
return (_jsx("p", { role: "status", className: "datalayer-app-said", children: assistant.problem }));
|
|
224
|
+
}
|
|
225
|
+
const character = assistant?.character;
|
|
226
|
+
return (_jsxs(EmbedThemed, { accent: worn, colorMode: resolvedMode, font: font, style: view
|
|
201
227
|
? // Floating: nothing in the page's flow, nothing painted behind.
|
|
202
228
|
{ backgroundColor: 'transparent' }
|
|
203
|
-
: { height, display: 'flex', flexDirection: 'column' }, children: [ownPortal ? _jsx(OwnPortalRoot, {}) : null, view && serverUrl ? (_jsx(ServerFloating, { app: app, view: view, serverUrl: serverUrl, colorMode: resolvedMode })) : view ? (_jsx(DatalayerFloating, { app: app, view: view, instance: instance, colorMode: resolvedMode })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), instance: instance
|
|
229
|
+
: { height, display: 'flex', flexDirection: 'column' }, children: [ownPortal ? _jsx(OwnPortalRoot, {}) : null, view && serverUrl ? (_jsx(ServerFloating, { app: app, view: view, serverUrl: serverUrl, colorMode: resolvedMode, themeOverrides: themeOverrides, character: character })) : view ? (_jsx(DatalayerFloating, { app: app, view: view, instance: instance, colorMode: resolvedMode, themeOverrides: themeOverrides, character: character })) : (_jsx(AppRenderer, { app: app, target: serverUrl ? 'local' : 'datalayer', ...(serverUrl ? { serverUrl } : {}), instance: instance,
|
|
230
|
+
// The host's accent and face over the application's own, inside
|
|
231
|
+
// its conversation too.
|
|
232
|
+
themeOverrides: themeOverrides }))] }));
|
|
204
233
|
}
|
|
205
234
|
export default AppEmbed;
|
|
@@ -66,6 +66,8 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
66
66
|
preset.datalayerAgentSpecId,
|
|
67
67
|
preset.targetFixed,
|
|
68
68
|
preset.themeVariant,
|
|
69
|
+
// By its content, as the payload below.
|
|
70
|
+
JSON.stringify(preset.themeOverrides ?? null),
|
|
69
71
|
preset.presence?.name,
|
|
70
72
|
preset.presence?.face,
|
|
71
73
|
preset.presence?.welcome,
|
|
@@ -37,7 +37,7 @@ import { loopThemeStyles } from '@datalayer/primer-addons';
|
|
|
37
37
|
import { coreStore } from '@datalayer/core/lib/state/substates/CoreState';
|
|
38
38
|
import { parseAppspec } from '../apps/appspec';
|
|
39
39
|
import { readAppspecYaml } from '../apps/yaml';
|
|
40
|
-
import { AppEmbed } from './AppEmbed';
|
|
40
|
+
import { AppEmbed, embedAssistantCharacter } from './AppEmbed';
|
|
41
41
|
import { EMBED_HOST_VARIABLES, EMBED_OBSERVED_ATTRIBUTES, EMBED_TAG, EmbedAttributeError, embedLookOf, inlineHeightOf, } from './embedConfig';
|
|
42
42
|
import { embedShadowCss } from './embedTheme';
|
|
43
43
|
/** What the framed run page and the element say to each other. */
|
|
@@ -362,6 +362,15 @@ export function defineDatalayerAppElement(options = {}) {
|
|
|
362
362
|
this.say(error.message);
|
|
363
363
|
return;
|
|
364
364
|
}
|
|
365
|
+
// A character nothing contributes is said in place, as an attribute
|
|
366
|
+
// that is not one is (D-07, T-24).
|
|
367
|
+
if (look.mode === 'assistant') {
|
|
368
|
+
const chosen = embedAssistantCharacter(source.app);
|
|
369
|
+
if ('problem' in chosen) {
|
|
370
|
+
this.say(chosen.problem);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
365
374
|
this.setAttribute('data-embed-mode', look.mode);
|
|
366
375
|
const shadow = this.skeleton(loopThemeStyles.css);
|
|
367
376
|
const mount = document.createElement('div');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ThemeStyles } from '@datalayer/primer-addons';
|
|
2
2
|
import type { AppAccent } from '../../types/agentspecs';
|
|
3
|
+
import type { ThemeOverrides } from '../../types/chat';
|
|
3
4
|
/**
|
|
4
5
|
* The `loop` theme as the embed wears it: the application's accent over it
|
|
5
6
|
* in both modes (T-05), and the host's face when it named one.
|
|
@@ -8,6 +9,17 @@ export declare function embedThemeStyles({ accent, font, }: {
|
|
|
8
9
|
accent: AppAccent;
|
|
9
10
|
font?: string;
|
|
10
11
|
}): ThemeStyles;
|
|
12
|
+
/**
|
|
13
|
+
* The same accent and face laid over the conversation's own theme (T-05,
|
|
14
|
+
* D-11): the chat sets the `loop` theme again inside it, which would put the
|
|
15
|
+
* theme's mint and face back over what the embed set around it. The
|
|
16
|
+
* accent's properties in each mode, and, when the host named a face, every
|
|
17
|
+
* property of the theme that names its own.
|
|
18
|
+
*/
|
|
19
|
+
export declare function embedThemeOverrides({ accent, font, }: {
|
|
20
|
+
accent: AppAccent;
|
|
21
|
+
font?: string;
|
|
22
|
+
}): ThemeOverrides;
|
|
11
23
|
/**
|
|
12
24
|
* The element's own stylesheet, written into its shadow root.
|
|
13
25
|
*
|
|
@@ -30,6 +30,21 @@ export function embedThemeStyles({ accent, font = '', }) {
|
|
|
30
30
|
dark: mode('dark'),
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The same accent and face laid over the conversation's own theme (T-05,
|
|
35
|
+
* D-11): the chat sets the `loop` theme again inside it, which would put the
|
|
36
|
+
* theme's mint and face back over what the embed set around it. The
|
|
37
|
+
* accent's properties in each mode, and, when the host named a face, every
|
|
38
|
+
* property of the theme that names its own.
|
|
39
|
+
*/
|
|
40
|
+
export function embedThemeOverrides({ accent, font = '', }) {
|
|
41
|
+
const mode = (which) => {
|
|
42
|
+
const theme = loopThemeStyles[which];
|
|
43
|
+
const faced = Object.fromEntries(Object.entries(withFace(theme, font)).filter(([name, value]) => typeof value === 'string' && value !== theme[name]));
|
|
44
|
+
return { ...faced, ...loopAccentStyles(accent, which) };
|
|
45
|
+
};
|
|
46
|
+
return { light: mode('light'), dark: mode('dark') };
|
|
47
|
+
}
|
|
33
48
|
/**
|
|
34
49
|
* The element's own stylesheet, written into its shadow root.
|
|
35
50
|
*
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module loop/plugins/assistant-characters
|
|
7
7
|
*/
|
|
8
|
-
import { type ReactorPlugin } from '@datalayer/reactor';
|
|
8
|
+
import { type PluginRef, type ReactorPlugin } from '@datalayer/reactor';
|
|
9
9
|
import { type AssistantCharacter } from '../../../chat/assistant/characters';
|
|
10
10
|
import type { AssistantCharacterData } from '../../../chat/assistant/formats/types';
|
|
11
11
|
import { LoopAssistantCharacter, type AssistantCharacterContribution } from '../../core';
|
|
@@ -22,6 +22,15 @@ export declare function assistantCharactersOf(reactor: ContributionsReader): {
|
|
|
22
22
|
id: string;
|
|
23
23
|
character: AssistantCharacter | AssistantCharacterData;
|
|
24
24
|
}[];
|
|
25
|
+
/**
|
|
26
|
+
* What these plugins contribute, enabled on their own: for a host with no
|
|
27
|
+
* workspace to read them from — the embed's floating assistant (D-07) —
|
|
28
|
+
* which enables Datalayer's characters and whatever plugins it is given.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assistantCharactersFrom(plugins: PluginRef[]): {
|
|
31
|
+
id: string;
|
|
32
|
+
character: AssistantCharacter | AssistantCharacterData;
|
|
33
|
+
}[];
|
|
25
34
|
/**
|
|
26
35
|
* The character an application names, from what is contributed; an id no
|
|
27
36
|
* enabled plugin contributes is an error that says which ids there are.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module loop/plugins/assistant-characters
|
|
11
11
|
*/
|
|
12
|
-
import { contribution, definePlugin, } from '@datalayer/reactor';
|
|
12
|
+
import { buildReactorFromPlugins, contribution, definePlugin, } from '@datalayer/reactor';
|
|
13
13
|
import { ASSISTANT_CHARACTERS, DEFAULT_ASSISTANT_CHARACTER, } from '../../../chat/assistant/characters';
|
|
14
14
|
import { LoopAssistantCharacter, } from '../../core';
|
|
15
15
|
export const ASSISTANT_CHARACTERS_PLUGIN_NAME = '@datalayer/loop-plugin-assistant-characters';
|
|
@@ -26,6 +26,21 @@ export function assistantCharactersOf(reactor) {
|
|
|
26
26
|
.getContributions(LoopAssistantCharacter)
|
|
27
27
|
.map(entry => entry.value);
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* What these plugins contribute, enabled on their own: for a host with no
|
|
31
|
+
* workspace to read them from — the embed's floating assistant (D-07) —
|
|
32
|
+
* which enables Datalayer's characters and whatever plugins it is given.
|
|
33
|
+
*/
|
|
34
|
+
export function assistantCharactersFrom(plugins) {
|
|
35
|
+
const reactor = buildReactorFromPlugins(plugins);
|
|
36
|
+
reactor.start();
|
|
37
|
+
try {
|
|
38
|
+
return assistantCharactersOf(reactor);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
reactor.stop();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
29
44
|
/**
|
|
30
45
|
* The character an application names, from what is contributed; an id no
|
|
31
46
|
* enabled plugin contributes is an error that says which ids there are.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Box, IconButton, SegmentedControl, Text, Truncate, } from '@primer/react';
|
|
4
|
-
import { useColorPalette } from '@datalayer/primer-addons';
|
|
4
|
+
import { loopShapeVars, useColorPalette } from '@datalayer/primer-addons';
|
|
5
5
|
import { ScreenFullIcon, ScreenNormalIcon } from '@primer/octicons-react';
|
|
6
6
|
import { computed, signal } from '@datalayer/reactor';
|
|
7
7
|
/* A signal to read when the layout publishes no live stance: the hook needs one. */
|
|
@@ -54,6 +54,11 @@ const NO_SURFACE = '';
|
|
|
54
54
|
* small control reads as an error state, and this one is an invitation.
|
|
55
55
|
*/
|
|
56
56
|
const FULLSCREEN_HINT_PERIOD_MS = 1400;
|
|
57
|
+
/**
|
|
58
|
+
* An application's face in the empty state: the page's size of the three a
|
|
59
|
+
* face is drawn at (LOOP T-19, `--loop-face-large`, 72px).
|
|
60
|
+
*/
|
|
61
|
+
const FACE_LARGE = parseInt(loopShapeVars['--loop-face-large'], 10);
|
|
57
62
|
export default function ChatView({ workspace }) {
|
|
58
63
|
/* The active theme's own colours. Read from the store rather than a
|
|
59
64
|
provider, so a workspace mounted without the addons theme still gets the
|
|
@@ -1326,7 +1331,14 @@ export default function ChatView({ workspace }) {
|
|
|
1326
1331
|
mentionableAgents: mentionable,
|
|
1327
1332
|
headerContent: inpromptMenu.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.inpromptMenu, props: { workspace } })) : undefined,
|
|
1328
1333
|
footerExtras: promptActions.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.promptAction, props: { workspace } })) : undefined,
|
|
1329
|
-
|
|
1334
|
+
/*
|
|
1335
|
+
An application's conversation keeps none of the session's controls
|
|
1336
|
+
under its composer — the agents, the model, the tools and their servers,
|
|
1337
|
+
the skills (LOOP T-17, T-18): a person using an application is not
|
|
1338
|
+
choosing them, and its builder chose them in its spec. An agent's chat
|
|
1339
|
+
keeps them.
|
|
1340
|
+
*/
|
|
1341
|
+
showAgentsMenu: !presence,
|
|
1330
1342
|
showInlineAgentsMenu: false,
|
|
1331
1343
|
agents: footerAgents,
|
|
1332
1344
|
selectedAgentId: selectedMemberId,
|
|
@@ -1336,18 +1348,18 @@ export default function ChatView({ workspace }) {
|
|
|
1336
1348
|
showTokenUsage: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true,
|
|
1337
1349
|
showContextRing: true,
|
|
1338
1350
|
agentUsage: contextUsage ?? undefined,
|
|
1339
|
-
showModelSelector:
|
|
1351
|
+
showModelSelector: !presence,
|
|
1340
1352
|
models: offeredModels,
|
|
1341
1353
|
decisions,
|
|
1342
1354
|
selectedModel: activeModel,
|
|
1343
1355
|
onModelSelect: model => void selectModel(model),
|
|
1344
|
-
showToolsMenu:
|
|
1356
|
+
showToolsMenu: !presence,
|
|
1345
1357
|
availableTools: offeredTools,
|
|
1346
1358
|
mcpServers: configQuery.data?.mcpServers ?? [],
|
|
1347
1359
|
// Live from the host example, when one feeds it: the footer's MCP status
|
|
1348
1360
|
// indicator. (Codemode surfaces through the toggle below, not a blob.)
|
|
1349
1361
|
mcpStatusData: chatExtras.mcpStatusData ?? undefined,
|
|
1350
|
-
showSkillsMenu:
|
|
1362
|
+
showSkillsMenu: !presence,
|
|
1351
1363
|
skills: skillsQuery.data?.skills ?? [],
|
|
1352
1364
|
skillsLoading: skillsQuery.isLoading,
|
|
1353
1365
|
enabledSkills,
|
|
@@ -1444,7 +1456,7 @@ export default function ChatView({ workspace }) {
|
|
|
1444
1456
|
// The theme it wears, when the host names one (an application's).
|
|
1445
1457
|
, {
|
|
1446
1458
|
// The theme it wears, when the host names one (an application's).
|
|
1447
|
-
themeVariant: config?.themeVariant,
|
|
1459
|
+
themeVariant: config?.themeVariant, themeOverrides: config?.themeOverrides,
|
|
1448
1460
|
// The header says why, beside the title, for the same reason the
|
|
1449
1461
|
// placeholder does: a dead control with no explanation is worse
|
|
1450
1462
|
// than an absent one.
|
|
@@ -1483,9 +1495,9 @@ export default function ChatView({ workspace }) {
|
|
|
1483
1495
|
// An application says what it is doing, in plain words, beside
|
|
1484
1496
|
// its name (T-08); an agent's chat keeps its header as it is.
|
|
1485
1497
|
headerContent: presence ? _jsx(PresenceLine, { state: presenceNow }) : undefined, emptyState: {
|
|
1486
|
-
// An application's own face,
|
|
1487
|
-
//
|
|
1488
|
-
icon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize:
|
|
1498
|
+
// An application's own face, at the page's size of the three
|
|
1499
|
+
// (LOOP T-08, T-19): the thing a person's eye lands on first.
|
|
1500
|
+
icon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize: FACE_LARGE, lineHeight: 1 }, children: presence.face })) : (_jsx(BrandIcon, { size: 48 })),
|
|
1489
1501
|
// `ChatEmptyState` reads its heading from here and nowhere
|
|
1490
1502
|
// else — the `title` above reaches the header only — so
|
|
1491
1503
|
// without this the agent introduced itself as "Start a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PresenceState } from '../../../chat/presence/presenceStatus';
|
|
2
|
+
import type { ThemeOverrides } from '../../../types/chat';
|
|
2
3
|
/** What a host may set on the chat. */
|
|
3
4
|
/** Who is answering, as the person meets them. */
|
|
4
5
|
export interface ChatPresence {
|
|
@@ -22,6 +23,11 @@ export type ChatPluginConfig = {
|
|
|
22
23
|
* the theme's bubbles, pills and colours whatever the page around it wears.
|
|
23
24
|
*/
|
|
24
25
|
themeVariant?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Laid over that theme, by mode: an application's accent (LOOP T-05), so
|
|
28
|
+
* that its bubbles and its one button wear its colour and not the theme's.
|
|
29
|
+
*/
|
|
30
|
+
themeOverrides?: ThemeOverrides;
|
|
25
31
|
/**
|
|
26
32
|
* Who is answering, as the person meets them (LOOP T-08): a name, a face
|
|
27
33
|
* and a welcome, in place of the agent's id and a generic icon. An
|
package/lib/loop/presets.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { type PluginRef } from '@datalayer/reactor';
|
|
|
23
23
|
import { type AgentsConfig, type SandboxTarget } from './plugins/agents';
|
|
24
24
|
import { type ChatPluginConfig, type ChatPresence } from './plugins/chat';
|
|
25
25
|
import { type PageSize } from './plugins/page-layout';
|
|
26
|
+
import type { ThemeOverrides } from '../types/chat';
|
|
26
27
|
export type LoopPresetOptions = {
|
|
27
28
|
/** Where the agent runtimes service is. */
|
|
28
29
|
serverUrl?: string;
|
|
@@ -140,6 +141,8 @@ export type LoopPresetOptions = {
|
|
|
140
141
|
targetFixed?: boolean;
|
|
141
142
|
/** The theme the conversation wears, by name; the person's unless said. */
|
|
142
143
|
themeVariant?: string;
|
|
144
|
+
/** Laid over that theme, by mode: an application's accent (LOOP T-05). */
|
|
145
|
+
themeOverrides?: ThemeOverrides;
|
|
143
146
|
/** Who is answering, as the person meets them (LOOP T-08). */
|
|
144
147
|
presence?: ChatPresence;
|
|
145
148
|
/** Whether the counters are shown under the prompt; on unless said. */
|
package/lib/loop/presets.js
CHANGED
|
@@ -52,7 +52,7 @@ import { LoopPageLayoutPlugin } from './plugins/page-layout';
|
|
|
52
52
|
* be installed or signed into, and the switches a demonstration wants are off.
|
|
53
53
|
*/
|
|
54
54
|
export function loopPlugins(options = {}) {
|
|
55
|
-
const { serverUrl, target = 'browser', defaultEditor: askedEditor, showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutArrangement = 'page', pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
55
|
+
const { serverUrl, target = 'browser', defaultEditor: askedEditor, showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutArrangement = 'page', pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, themeOverrides, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
56
56
|
const defaultEditor = askedEditor ?? (editors ? 'notebook' : 'none');
|
|
57
57
|
return [
|
|
58
58
|
// The chat owns the editor beside it, so which one opens is its
|
|
@@ -70,6 +70,7 @@ export function loopPlugins(options = {}) {
|
|
|
70
70
|
suggestionLabels,
|
|
71
71
|
fullScreenTopOffset,
|
|
72
72
|
themeVariant,
|
|
73
|
+
themeOverrides,
|
|
73
74
|
presence,
|
|
74
75
|
showTokenUsage,
|
|
75
76
|
}),
|