@datalayer/agent-runtimes 1.3.15 → 1.3.16
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/README.md +1 -1
- package/lib/chat/base/ChatBase.js +3 -2
- package/lib/config/AgentConfiguration.d.ts +1 -1
- package/lib/loop/embed/LoopEmbed.d.ts +6 -1
- package/lib/loop/embed/LoopEmbed.js +3 -2
- package/lib/loop/plugins/chat/ChatView.js +9 -4
- package/lib/loop/plugins/chat/index.d.ts +10 -0
- package/lib/loop/plugins/chat/index.js +1 -0
- package/lib/loop/presets.d.ts +6 -0
- package/lib/loop/presets.js +2 -1
- package/lib/loop/shell/LoopWorkspace.d.ts +14 -0
- package/lib/loop/shell/LoopWorkspace.js +20 -6
- package/lib/loop/shell/index.d.ts +1 -1
- package/lib/loop/shell/index.js +1 -1
- package/lib/loop/shell/useWorkspaceFullScreen.d.ts +17 -4
- package/lib/loop/shell/useWorkspaceFullScreen.js +23 -6
- package/lib/specs/agents/agents.d.ts +1 -0
- package/lib/specs/agents/agents.js +338 -284
- package/lib/specs/events.d.ts +6 -1
- package/lib/specs/events.js +144 -1
- package/lib/specs/index.d.ts +2 -0
- package/lib/specs/index.js +2 -0
- package/lib/specs/mcpServers.js +2 -2
- package/lib/specs/memory.js +4 -0
- package/lib/specs/modelProviders.d.ts +22 -0
- package/lib/specs/modelProviders.js +102 -0
- package/lib/specs/models.d.ts +31 -0
- package/lib/specs/models.js +215 -0
- package/lib/specs/outputs.js +2 -2
- package/lib/specs/skills.js +1 -1
- package/lib/specs/teams/teams.js +18 -18
- package/lib/specs/uiPlugins.d.ts +16 -0
- package/lib/specs/uiPlugins.js +40 -0
- package/lib/types/agentspecs.d.ts +18 -2
- package/lib/types/memory.d.ts +2 -0
- package/lib/types/models.d.ts +52 -0
- package/package.json +1 -1
- package/scripts/check-cloudflare-models.py +122 -0
- package/scripts/codegen/generate_agents.py +6 -4
- package/scripts/codegen/generate_memory.py +2 -0
- package/scripts/codegen/generate_model_providers.py +246 -0
- package/scripts/codegen/generate_models.py +135 -4
- package/scripts/codegen/generate_sandbox_agents.py +1 -1
- package/scripts/codegen/generate_ui_plugins.py +226 -0
- package/scripts/gen_workers.py +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
~ BSD 3-Clause License
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
|
-
[](https://datalayer.ai)
|
|
8
8
|
|
|
9
9
|
[](https://github.com/sponsors/datalayer)
|
|
10
10
|
|
|
@@ -31,7 +31,7 @@ import { useChatStore } from '../../stores/chatStore';
|
|
|
31
31
|
import { useConversationStore } from '../../stores/conversationStore';
|
|
32
32
|
import { generateMessageId, createUserMessage, createAssistantMessage, } from '../../types/messages';
|
|
33
33
|
import { AgentDetails } from '../../agents/AgentDetails';
|
|
34
|
-
import { AI_MODEL_CATALOGUE, DEFAULT_MODEL } from '../../specs/models';
|
|
34
|
+
import { AI_MODEL_CATALOGUE, DEFAULT_MODEL, isChatModel, } from '../../specs/models';
|
|
35
35
|
import { internalQueryClient, isToolCallMessage, convertHistoryToDisplayItems, createProtocolAdapter, getApiBaseFromConfig, sanitizeAssistantContent, } from '../../utils';
|
|
36
36
|
import { useConfig, useSkills, useSkillActions, useContextSnapshot, useSandbox, } from '../../hooks';
|
|
37
37
|
import { useAgentRuntimeWebSocket } from '../../hooks/useAgentRuntimes';
|
|
@@ -1397,7 +1397,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
1397
1397
|
* four are current.
|
|
1398
1398
|
*/
|
|
1399
1399
|
const catalogued = Object.values(AI_MODEL_CATALOGUE)
|
|
1400
|
-
|
|
1400
|
+
// A typed-judgment model answers typed questions, not a chat.
|
|
1401
|
+
.filter(model => model.available && isChatModel(model))
|
|
1401
1402
|
.map(model => ({
|
|
1402
1403
|
id: model.id,
|
|
1403
1404
|
name: model.name,
|
|
@@ -28,7 +28,7 @@ export interface LibraryAgentspec {
|
|
|
28
28
|
welcomeDocument?: string | null;
|
|
29
29
|
sandboxVariant?: string | null;
|
|
30
30
|
protocol?: string | null;
|
|
31
|
-
|
|
31
|
+
uiPlugin?: string | null;
|
|
32
32
|
trigger?: Record<string, unknown> | null;
|
|
33
33
|
modelConfig?: Record<string, unknown> | null;
|
|
34
34
|
mcpServerTools?: Array<Record<string, unknown>> | null;
|
|
@@ -29,6 +29,11 @@ export type LoopEmbedProps = LoopPresetOptions & {
|
|
|
29
29
|
chatHeaderActions?: ReactNode;
|
|
30
30
|
/** The host's own controls for the workspace header row. */
|
|
31
31
|
headerActions?: ReactNode;
|
|
32
|
+
/**
|
|
33
|
+
* An element in the host's own bar to render the header's controls in,
|
|
34
|
+
* instead of a header row. See `LoopWorkspaceProps.headerContainer`.
|
|
35
|
+
*/
|
|
36
|
+
headerContainer?: HTMLElement | null;
|
|
32
37
|
/**
|
|
33
38
|
* Draw the workspace inside a window frame with this title bar.
|
|
34
39
|
*
|
|
@@ -53,5 +58,5 @@ export type LoopEmbedProps = LoopPresetOptions & {
|
|
|
53
58
|
*/
|
|
54
59
|
plugins?: PluginRef[];
|
|
55
60
|
};
|
|
56
|
-
export declare function LoopEmbed({ agentId, showHeader, chatHeaderActions, headerActions, frameTitle, frameHeight, plugins, ...preset }: LoopEmbedProps): React.JSX.Element;
|
|
61
|
+
export declare function LoopEmbed({ agentId, showHeader, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, plugins, ...preset }: LoopEmbedProps): React.JSX.Element;
|
|
57
62
|
export default LoopEmbed;
|
|
@@ -30,7 +30,7 @@ import { internalQueryClient } from '../../utils';
|
|
|
30
30
|
import { buildLoopReactor, LoopWorkspace } from '../shell';
|
|
31
31
|
import { loopPlugins } from '../presets';
|
|
32
32
|
import { WindowFrame } from '../plugins/window-frame';
|
|
33
|
-
export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions, headerActions, frameTitle, frameHeight, plugins = [], ...preset }) {
|
|
33
|
+
export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions, headerActions, headerContainer, frameTitle, frameHeight, plugins = [], ...preset }) {
|
|
34
34
|
/*
|
|
35
35
|
* The latest `firstPromptHook`, kept in a ref rather than closed over
|
|
36
36
|
* directly.
|
|
@@ -69,6 +69,7 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
69
69
|
preset.chatHeaderButtons,
|
|
70
70
|
preset.promptPlacement,
|
|
71
71
|
preset.autoFocusPrompt,
|
|
72
|
+
preset.suggestionLabels,
|
|
72
73
|
preset.fullScreenTopOffset,
|
|
73
74
|
preset.showAgentVariants,
|
|
74
75
|
preset.agentSummary,
|
|
@@ -119,7 +120,7 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
119
120
|
* view and the workspace draws "No view is available yet".
|
|
120
121
|
*/
|
|
121
122
|
useReactor(reactor);
|
|
122
|
-
const shell = (_jsx(LoopWorkspace, { serverUrl: preset.serverUrl ?? '', agentId: agentId, reactor: reactor, manageReactor: false, showViewSelector: preset.showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions }));
|
|
123
|
+
const shell = (_jsx(LoopWorkspace, { serverUrl: preset.serverUrl ?? '', agentId: agentId, reactor: reactor, manageReactor: false, showViewSelector: preset.showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions, headerContainer: headerContainer }));
|
|
123
124
|
return (_jsx(QueryClientProvider, { client: internalQueryClient, children: frameTitle !== undefined ? (_jsx(WindowFrame, { title: frameTitle, height: frameHeight, children: shell })) : (_jsx(Box, { sx: { height: '100%', minHeight: 0 }, children: shell })) }));
|
|
124
125
|
}
|
|
125
126
|
export default LoopEmbed;
|
|
@@ -37,7 +37,7 @@ import { agentRuntimeStore, useAgentRuntimeContextSnapshot, } from '../../../sto
|
|
|
37
37
|
import { useIAMStore } from '../../../state';
|
|
38
38
|
import { useConfig } from '../../../hooks/useConfig';
|
|
39
39
|
import { useSkills, useSkillActions } from '../../../hooks/useSkills';
|
|
40
|
-
import { AI_MODEL_CATALOGUE } from '../../../specs/models';
|
|
40
|
+
import { AI_MODEL_CATALOGUE, isChatModel } from '../../../specs/models';
|
|
41
41
|
import { LoopAgentBlueprint, LoopAgentGate, LoopChatComposer, LoopChatExtras, LoopChatHeader, LoopChatLayout, LoopChatSuggestion, LoopChatSurface, LoopChatTurn, LoopFrontendTool, LoopSlots, canOpenView, onPromptFocusRequest, onSurfaceRequest, useLoopPromptStore, } from '../../core';
|
|
42
42
|
import { turnWritersOf } from './turnState';
|
|
43
43
|
import { isInactiveSurfaceContribution, orderToolContributions, toolsForChatView, } from './chatViewTools';
|
|
@@ -1153,7 +1153,8 @@ export default function ChatView({ workspace }) {
|
|
|
1153
1153
|
const offeredModels = useMemo(() => catalogModels.length > 0
|
|
1154
1154
|
? catalogModels
|
|
1155
1155
|
: Object.values(AI_MODEL_CATALOGUE)
|
|
1156
|
-
|
|
1156
|
+
// A typed-judgment model answers typed questions, not a chat.
|
|
1157
|
+
.filter(model => model.available && isChatModel(model))
|
|
1157
1158
|
.map(model => ({
|
|
1158
1159
|
id: model.id,
|
|
1159
1160
|
name: model.name,
|
|
@@ -1475,7 +1476,9 @@ export default function ChatView({ workspace }) {
|
|
|
1475
1476
|
// With the prompt on top the openers are already chips under
|
|
1476
1477
|
// it; the empty state repeating them was the same three buttons
|
|
1477
1478
|
// twice on one screen.
|
|
1478
|
-
suggestions: topPrompt || layout
|
|
1479
|
+
suggestions: topPrompt || layout || config?.suggestionLabels === false
|
|
1480
|
+
? []
|
|
1481
|
+
: chatSuggestions,
|
|
1479
1482
|
/*
|
|
1480
1483
|
The title bar arrives as a plugin: the chat-header plugin
|
|
1481
1484
|
contributes the component, this view hands it the assembled
|
|
@@ -1598,7 +1601,9 @@ export default function ChatView({ workspace }) {
|
|
|
1598
1601
|
// through the extras channel — the A2UI examples draw their
|
|
1599
1602
|
// surface here. Wins over the notebook surfaces in ChatBase.
|
|
1600
1603
|
renderToolResult: chatExtras.renderToolResult, showTurnFooter: chatExtras.showTokenUsage ?? true, onContextSnapshot: handleContextSnapshot, onLoadingChange: handleLoadingChange, onItemsChange: handleMessagesChange, onDisplayItemsChange: handleDisplayItemsChange, enableStreaming: true }) }), besideChat && !promptHidden ? prompt : null] }) }));
|
|
1601
|
-
|
|
1604
|
+
// Off when the host asked for the menu alone: the composer's suggestions
|
|
1605
|
+
// control carries the same openers (`suggestions` above).
|
|
1606
|
+
const chipsEl = config?.suggestionLabels !== false && chatSuggestions.length > 0 ? (_jsx(Box, { "aria-label": "Suggested prompts", sx: { display: 'flex', flexWrap: 'wrap', gap: 2, px: 2, pb: '6px' }, children: chatSuggestions.map(item => (_jsx(Box, { as: "button", type: "button",
|
|
1602
1607
|
// The whole request, where the chip shows only its summary.
|
|
1603
1608
|
title: item.message, disabled: busy || chatDisabled, onClick: () => void handleSend(item.message), sx: {
|
|
1604
1609
|
appearance: 'none',
|
|
@@ -105,6 +105,16 @@ export type ChatPluginConfig = {
|
|
|
105
105
|
* them. Such a host passes `false` and lets the reader arrive at the prompt.
|
|
106
106
|
*/
|
|
107
107
|
autoFocusPrompt: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Whether the openers are drawn as a row of labels by the composer.
|
|
110
|
+
*
|
|
111
|
+
* On by default: a person who has just opened the workspace sees what it is
|
|
112
|
+
* worth asking without opening anything. The same openers are always behind
|
|
113
|
+
* the composer's own suggestions menu, so a host with little room — a frame
|
|
114
|
+
* on a landing page, where the labels would be a row of the first screen —
|
|
115
|
+
* turns the labels off and loses nothing.
|
|
116
|
+
*/
|
|
117
|
+
suggestionLabels: boolean;
|
|
108
118
|
/**
|
|
109
119
|
* Pixels to reserve at the top of the workspace when it goes full screen —
|
|
110
120
|
* room for a host's own fixed header, drawn outside the workspace
|
package/lib/loop/presets.d.ts
CHANGED
|
@@ -54,6 +54,12 @@ export type LoopPresetOptions = {
|
|
|
54
54
|
* does not scroll the reader into the middle of the page.
|
|
55
55
|
*/
|
|
56
56
|
autoFocusPrompt?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the openers show as labels by the composer. True by default; the
|
|
59
|
+
* composer's suggestions menu offers them either way. See
|
|
60
|
+
* `ChatPluginConfig.suggestionLabels`.
|
|
61
|
+
*/
|
|
62
|
+
suggestionLabels?: boolean;
|
|
57
63
|
/**
|
|
58
64
|
* Pixels to reserve at the top of the workspace when it goes full screen,
|
|
59
65
|
* for a host's own fixed header. Passed through to the chat plugin — see
|
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 = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
55
|
+
const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
56
56
|
return [
|
|
57
57
|
// The chat owns the editor beside it, so which one opens is its
|
|
58
58
|
// configuration rather than the workspace's.
|
|
@@ -66,6 +66,7 @@ export function loopPlugins(options = {}) {
|
|
|
66
66
|
headerButtons: { newChat: chatHeaderButtons, clear: chatHeaderButtons },
|
|
67
67
|
promptPlacement: floatingPrompt ? 'floating' : promptPlacement,
|
|
68
68
|
autoFocusPrompt,
|
|
69
|
+
suggestionLabels,
|
|
69
70
|
fullScreenTopOffset,
|
|
70
71
|
}),
|
|
71
72
|
// The composer and the title bar are plugins of their own: the chat
|
|
@@ -103,6 +103,20 @@ export type LoopWorkspaceProps = {
|
|
|
103
103
|
* through `LoopSlots.header`.
|
|
104
104
|
*/
|
|
105
105
|
headerActions?: ReactNode;
|
|
106
|
+
/**
|
|
107
|
+
* Where the header's controls go, when not in a row of their own.
|
|
108
|
+
*
|
|
109
|
+
* A host that already draws a bar above the workspace — a window's title
|
|
110
|
+
* bar, a frame with its own tabs — hands over an element in it, and the
|
|
111
|
+
* controls the header would have carried (the host's `headerActions`, every
|
|
112
|
+
* plugin's `LoopSlots.header` contribution, the view switcher) are rendered
|
|
113
|
+
* there instead, and the workspace draws no header row. They stay inside
|
|
114
|
+
* the workspace's React tree, so they keep their context and their state.
|
|
115
|
+
*
|
|
116
|
+
* `null` while the host's element is not mounted yet: nothing is rendered,
|
|
117
|
+
* and no row is drawn in its place.
|
|
118
|
+
*/
|
|
119
|
+
headerContainer?: HTMLElement | null;
|
|
106
120
|
};
|
|
107
121
|
/** Build the platform for a set of plugins. */
|
|
108
122
|
export declare function buildLoopReactor(extensions: PluginRef[]): ReactorPlatform;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
3
4
|
import { Box } from '@primer/react';
|
|
4
5
|
import { buildReactorFromPlugins, onView, } from '@datalayer/reactor';
|
|
5
6
|
import { ReactorSlot, ReactorViewHost, useContributions, useReactorEvent, useSlotComponents, useReactor, } from '@datalayer/reactor/react';
|
|
@@ -11,21 +12,21 @@ export function buildLoopReactor(extensions) {
|
|
|
11
12
|
return buildReactorFromPlugins(extensions);
|
|
12
13
|
}
|
|
13
14
|
export function LoopWorkspace(props) {
|
|
14
|
-
const { serverUrl, agentId, conversationId, model, sandbox = { state: 'idle' }, extensions = [], reactor: providedReactor, manageReactor = true, initialViewType, onSend, layout = 'page', showViewSelector = true, showHeader = true, chatHeaderActions, headerActions, } = props;
|
|
15
|
+
const { serverUrl, agentId, conversationId, model, sandbox = { state: 'idle' }, extensions = [], reactor: providedReactor, manageReactor = true, initialViewType, onSend, layout = 'page', showViewSelector = true, showHeader = true, chatHeaderActions, headerActions, headerContainer, } = props;
|
|
15
16
|
// Building the platform is a one-time act: rebuilding it on every render
|
|
16
17
|
// would restart every plugin.
|
|
17
18
|
const reactor = useMemo(() => providedReactor ?? buildLoopReactor(extensions),
|
|
18
19
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
19
20
|
[providedReactor]);
|
|
20
21
|
useReactor(reactor, { autoStart: manageReactor });
|
|
21
|
-
return (_jsx(WorkspaceBody, { serverUrl: serverUrl, agentId: agentId, conversationId: conversationId, model: model, initialSandbox: sandbox, initialViewType: initialViewType, onSend: onSend, layout: layout, showViewSelector: showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions }));
|
|
22
|
+
return (_jsx(WorkspaceBody, { serverUrl: serverUrl, agentId: agentId, conversationId: conversationId, model: model, initialSandbox: sandbox, initialViewType: initialViewType, onSend: onSend, layout: layout, showViewSelector: showViewSelector, showHeader: showHeader, chatHeaderActions: chatHeaderActions, headerActions: headerActions, headerContainer: headerContainer }));
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
25
|
* Split from `LoopWorkspace` so the hooks below run with the platform already
|
|
25
26
|
* registered — `useContributions` needs a reactor in the store, and `useReactor`
|
|
26
27
|
* registers it during the parent's render.
|
|
27
28
|
*/
|
|
28
|
-
function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, model, initialSandbox, initialViewType, onSend, layout = 'page', showViewSelector = true, showHeader = true, chatHeaderActions, headerActions, }) {
|
|
29
|
+
function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, model, initialSandbox, initialViewType, onSend, layout = 'page', showViewSelector = true, showHeader = true, chatHeaderActions, headerActions, headerContainer, }) {
|
|
29
30
|
/*
|
|
30
31
|
* Which agent the session is talking to.
|
|
31
32
|
*
|
|
@@ -146,6 +147,12 @@ function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, mod
|
|
|
146
147
|
// activates whatever was waiting for it. A plugin that declares neither is
|
|
147
148
|
// untouched, so this costs nothing until somebody uses it.
|
|
148
149
|
useReactorEvent(effectiveViewType ? onView(effectiveViewType) : undefined);
|
|
150
|
+
/*
|
|
151
|
+
* The header's controls: the host's first, then the plugins', then the
|
|
152
|
+
* view switcher on the trailing edge. Drawn in the header row, or in the
|
|
153
|
+
* host's own bar when it handed one over (`headerContainer`).
|
|
154
|
+
*/
|
|
155
|
+
const headerControls = (_jsxs(_Fragment, { children: [headerActions, _jsx(ReactorSlot, { slot: LoopSlots.header, props: { workspace } }), showViewSelector ? (_jsx(ViewSwitcher, { views: views, workspace: workspace, compact: compact })) : null] }));
|
|
149
156
|
return (_jsxs(Box
|
|
150
157
|
/*
|
|
151
158
|
Marked so a plugin inside can find the workspace it is part of.
|
|
@@ -169,7 +176,14 @@ function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, mod
|
|
|
169
176
|
position: 'relative',
|
|
170
177
|
bg: 'canvas.default',
|
|
171
178
|
color: 'fg.default',
|
|
172
|
-
}, children: [showHeader
|
|
179
|
+
}, children: [showHeader && headerContainer
|
|
180
|
+
? createPortal(_jsx(Box, { sx: {
|
|
181
|
+
display: 'flex',
|
|
182
|
+
alignItems: 'center',
|
|
183
|
+
flexWrap: 'wrap',
|
|
184
|
+
gap: 2,
|
|
185
|
+
}, children: headerControls }), headerContainer)
|
|
186
|
+
: null, showHeader && headerContainer === undefined ? (_jsx(Box, { as: "header", sx: {
|
|
173
187
|
display: 'flex',
|
|
174
188
|
alignItems: 'center',
|
|
175
189
|
// In a column there is no room for a row: the header wraps rather
|
|
@@ -191,7 +205,7 @@ function WorkspaceBody({ serverUrl, agentId: initialAgentId, conversationId, mod
|
|
|
191
205
|
borderBottom: '1px solid',
|
|
192
206
|
borderColor: 'border.default',
|
|
193
207
|
flex: '0 0 auto',
|
|
194
|
-
}, children:
|
|
208
|
+
}, children: headerControls })) : null, _jsxs(Box, { sx: { flex: '1 1 auto', minHeight: 0, display: 'flex' }, children: [_jsx(Box, { sx: {
|
|
195
209
|
flex: '1 1 auto',
|
|
196
210
|
minWidth: 0,
|
|
197
211
|
minHeight: 0,
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
export { LoopWorkspace, buildLoopReactor, type LoopWorkspaceProps, } from './LoopWorkspace';
|
|
7
7
|
export { ViewSwitcher, type ViewSwitcherProps } from './ViewSwitcher';
|
|
8
8
|
export { PluginToggles, type PluginTogglesProps } from './PluginToggles';
|
|
9
|
-
export { useWorkspaceFullScreen, type WorkspaceFullScreen, } from './useWorkspaceFullScreen';
|
|
9
|
+
export { fullScreenNode, useWorkspaceFullScreen, type WorkspaceFullScreen, } from './useWorkspaceFullScreen';
|
|
10
10
|
export { WorkspaceFullScreenAction } from './WorkspaceFullScreenAction';
|
package/lib/loop/shell/index.js
CHANGED
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
export { LoopWorkspace, buildLoopReactor, } from './LoopWorkspace';
|
|
11
11
|
export { ViewSwitcher } from './ViewSwitcher';
|
|
12
12
|
export { PluginToggles } from './PluginToggles';
|
|
13
|
-
export { useWorkspaceFullScreen, } from './useWorkspaceFullScreen';
|
|
13
|
+
export { fullScreenNode, useWorkspaceFullScreen, } from './useWorkspaceFullScreen';
|
|
14
14
|
export { WorkspaceFullScreenAction } from './WorkspaceFullScreenAction';
|
|
@@ -79,15 +79,28 @@ export type WorkspaceFullScreenOptions = {
|
|
|
79
79
|
*/
|
|
80
80
|
topOffset?: number | string;
|
|
81
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* What full screen promotes, found from the control that asked.
|
|
84
|
+
*
|
|
85
|
+
* The workspace (`[data-loop-workspace]`) — unless a host marked something
|
|
86
|
+
* around it with `data-loop-fullscreen-root`. A host that draws its own bar
|
|
87
|
+
* above the workspace and has the header's controls rendered in it
|
|
88
|
+
* (`LoopWorkspace`'s `headerContainer`) marks the element holding both: the
|
|
89
|
+
* controls are then outside the workspace, and promoting the workspace alone
|
|
90
|
+
* would leave them — the one that leaves full screen included — under the
|
|
91
|
+
* overlay. The anchor itself when there is neither.
|
|
92
|
+
*/
|
|
93
|
+
export declare function fullScreenNode(anchor: HTMLElement | null): HTMLElement | null;
|
|
82
94
|
/**
|
|
83
95
|
* Full-screen state and the toggle, anchored by any element inside the
|
|
84
96
|
* workspace.
|
|
85
97
|
*
|
|
86
98
|
* The element promoted is the whole workspace — the nearest
|
|
87
|
-
* `[data-loop-workspace]` above the anchor
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
99
|
+
* `[data-loop-workspace]` above the anchor, or the host's
|
|
100
|
+
* `[data-loop-fullscreen-root]` around it (see `fullScreenNode`) — so the
|
|
101
|
+
* header's controls come along instead of being left on the page underneath.
|
|
102
|
+
* With neither around it, the anchor's own element is promoted, which is what
|
|
103
|
+
* a view mounted without a shell would want.
|
|
91
104
|
*/
|
|
92
105
|
export declare function useWorkspaceFullScreen(anchorRef: RefObject<HTMLElement | null>, options?: WorkspaceFullScreenOptions): WorkspaceFullScreen;
|
|
93
106
|
export default useWorkspaceFullScreen;
|
|
@@ -21,15 +21,32 @@ export function resolveTopOffset(offset) {
|
|
|
21
21
|
? Math.max(0, Math.round(element.getBoundingClientRect().bottom))
|
|
22
22
|
: 0;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* What full screen promotes, found from the control that asked.
|
|
26
|
+
*
|
|
27
|
+
* The workspace (`[data-loop-workspace]`) — unless a host marked something
|
|
28
|
+
* around it with `data-loop-fullscreen-root`. A host that draws its own bar
|
|
29
|
+
* above the workspace and has the header's controls rendered in it
|
|
30
|
+
* (`LoopWorkspace`'s `headerContainer`) marks the element holding both: the
|
|
31
|
+
* controls are then outside the workspace, and promoting the workspace alone
|
|
32
|
+
* would leave them — the one that leaves full screen included — under the
|
|
33
|
+
* overlay. The anchor itself when there is neither.
|
|
34
|
+
*/
|
|
35
|
+
export function fullScreenNode(anchor) {
|
|
36
|
+
return (anchor?.closest('[data-loop-fullscreen-root]') ??
|
|
37
|
+
anchor?.closest('[data-loop-workspace]') ??
|
|
38
|
+
anchor);
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* Full-screen state and the toggle, anchored by any element inside the
|
|
26
42
|
* workspace.
|
|
27
43
|
*
|
|
28
44
|
* The element promoted is the whole workspace — the nearest
|
|
29
|
-
* `[data-loop-workspace]` above the anchor
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
45
|
+
* `[data-loop-workspace]` above the anchor, or the host's
|
|
46
|
+
* `[data-loop-fullscreen-root]` around it (see `fullScreenNode`) — so the
|
|
47
|
+
* header's controls come along instead of being left on the page underneath.
|
|
48
|
+
* With neither around it, the anchor's own element is promoted, which is what
|
|
49
|
+
* a view mounted without a shell would want.
|
|
33
50
|
*/
|
|
34
51
|
export function useWorkspaceFullScreen(anchorRef, options = {}) {
|
|
35
52
|
const { forceOverlay = false, paintOverlay = false, topOffset = 0 } = options;
|
|
@@ -66,7 +83,7 @@ export function useWorkspaceFullScreen(anchorRef, options = {}) {
|
|
|
66
83
|
* runs — so at full screen a reader would lose the two controls most
|
|
67
84
|
* worth having room for.
|
|
68
85
|
*/
|
|
69
|
-
const node = anchorRef.current
|
|
86
|
+
const node = fullScreenNode(anchorRef.current);
|
|
70
87
|
if (fullScreen) {
|
|
71
88
|
if (usingApi.current && document.fullscreenElement) {
|
|
72
89
|
void document.exitFullscreen();
|
|
@@ -116,7 +133,7 @@ export function useWorkspaceFullScreen(anchorRef, options = {}) {
|
|
|
116
133
|
if (!paintOverlay || !fullScreen || usingApi.current) {
|
|
117
134
|
return undefined;
|
|
118
135
|
}
|
|
119
|
-
const node = anchorRef.current
|
|
136
|
+
const node = fullScreenNode(anchorRef.current);
|
|
120
137
|
if (!node) {
|
|
121
138
|
return undefined;
|
|
122
139
|
}
|
|
@@ -22,6 +22,7 @@ export declare const EXAMPLE_ANALYZE_EXCEL_SPREADSHEET_AGENTSPEC_0_0_1: Agentspe
|
|
|
22
22
|
export declare const EXAMPLE_BACKEND_TOOL_RENDERING_AGENTSPEC_0_0_1: Agentspec;
|
|
23
23
|
export declare const EXAMPLE_BUILD_NOTEBOOK_WITH_ONE_PROMPT_AGENTSPEC_0_0_1: Agentspec;
|
|
24
24
|
export declare const EXAMPLE_CHECKPOINTS_AGENTSPEC_0_0_1: Agentspec;
|
|
25
|
+
export declare const EXAMPLE_CLOUDFLARE_AGENTSPEC_0_0_1: Agentspec;
|
|
25
26
|
export declare const EXAMPLE_CODE_SANDBOX_AGENTSPEC_0_0_1: Agentspec;
|
|
26
27
|
export declare const EXAMPLE_CODEMODE_AGENTSPEC_0_0_1: Agentspec;
|
|
27
28
|
export declare const EXAMPLE_COMPACTION_AGENTSPEC_0_0_1: Agentspec;
|