@schlenkr/ragents 0.1.18 → 0.1.20
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/apps/server/src/host-api.json +6 -2
- package/apps/server/src/host-api.ts +3 -2
- package/apps/server/src/host-web.ts +3 -0
- package/apps/server/src/plugin-support/actor-programs/contract.ts +0 -3
- package/apps/server/src/plugin-support/removed-layout.ts +94 -0
- package/apps/server/src/ragents/engine.ts +2 -1
- package/apps/server/src/title-compactor.ts +5 -7
- package/apps/web/dist/assets/highlighted-body-KPVGNVTW-k67VKdbw.js +1 -0
- package/apps/web/dist/assets/main-CVjIz7BS.js +674 -0
- package/apps/web/dist/help/guide-clients.html +22 -42
- package/apps/web/dist/help/guide-clients.md +24 -45
- package/apps/web/dist/help/guide-functions.html +1 -1
- package/apps/web/dist/help/guide-functions.md +1 -1
- package/apps/web/dist/help/guide-getting-started.html +14 -22
- package/apps/web/dist/help/guide-getting-started.md +17 -24
- package/apps/web/dist/help/guide-ideas.html +1 -1
- package/apps/web/dist/help/guide-ideas.md +1 -1
- package/apps/web/dist/help/guide-plugins.html +1 -1
- package/apps/web/dist/help/guide-plugins.md +1 -1
- package/apps/web/dist/help/guide-programs.html +1 -1
- package/apps/web/dist/help/guide-programs.md +1 -1
- package/apps/web/dist/help/index.html +12 -79
- package/apps/web/dist/help/mini-app.css +8 -502
- package/apps/web/dist/host-web.json +28 -23
- package/apps/web/dist/index.html +2 -3
- package/apps/web/dist/run-panel.html +3 -4
- package/apps/web/src/Overview.tsx +4 -143
- package/apps/web/src/PluginChat.tsx +22 -116
- package/apps/web/src/PluginRegistry.tsx +4 -9
- package/apps/web/src/RunPreparationChat.tsx +4 -1
- package/apps/web/src/host-modules.ts +2 -0
- package/apps/web/src/main.tsx +23 -3
- package/apps/web/src/panel/RunLine.tsx +5 -2
- package/apps/web/src/panel/RunsPage.tsx +6 -6
- package/apps/web/src/panel/StartPage.tsx +6 -5
- package/apps/web/src/panel/contract.ts +3 -0
- package/apps/web/src/panel/page-props.ts +2 -0
- package/apps/web/src/run-apps.ts +34 -0
- package/apps/web/src/run-overview.ts +0 -27
- package/apps/web/src/run-panel/PanelContributions.tsx +41 -0
- package/apps/web/src/run-panel/RunPanelApp.tsx +172 -91
- package/apps/web/src/run-panel/RunPanelMenu.tsx +4 -3
- package/apps/web/src/run-panel/RunPanelWorkspace.tsx +8 -2
- package/apps/web/src/run-panel/clipboard.ts +17 -14
- package/apps/web/src/run-panel/host-contract.ts +8 -9
- package/apps/web/src/run-panel/host.ts +5 -31
- package/apps/web/src/run-panel/input-bridge.ts +15 -7
- package/bundles/ragents.activity/ragents-bundle.json +2 -2
- package/bundles/ragents.actor-programs/prompt.hbs +4 -5
- package/bundles/ragents.actor-programs/ragents-bundle.json +4 -14
- package/bundles/ragents.actor-programs/server/index.js +2 -14
- package/bundles/ragents.actor-programs/server/index.js.map +2 -2
- package/bundles/ragents.actor-programs/web/classes.json +1 -1
- package/bundles/ragents.actor-programs/web/index.js +1 -3
- package/bundles/ragents.actor-programs/web/index.js.map +4 -4
- package/bundles/ragents.ask/ragents-bundle.json +2 -2
- package/bundles/ragents.browser/ragents-bundle.json +2 -2
- package/bundles/ragents.documents/ragents-bundle.json +2 -2
- package/bundles/ragents.lsp-fsharp/ragents-bundle.json +2 -2
- package/bundles/ragents.lsp-roslyn/ragents-bundle.json +2 -2
- package/bundles/ragents.lsp-typescript/ragents-bundle.json +2 -2
- package/bundles/ragents.model-relay/ragents-bundle.json +2 -2
- package/bundles/ragents.orchestration/ragents-bundle.json +15 -41
- package/bundles/ragents.orchestration/server/index.js +10 -249
- package/bundles/ragents.orchestration/server/index.js.map +4 -4
- package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js +2 -0
- package/bundles/ragents.orchestration/web/chunks/chunk-KU7O6MEX.js.map +7 -0
- package/bundles/ragents.orchestration/web/classes.json +1 -1
- package/bundles/ragents.orchestration/web/exports/web/program-slot.js +1 -1
- package/bundles/ragents.orchestration/web/index.js +10 -18
- package/bundles/ragents.orchestration/web/index.js.map +4 -4
- package/bundles/ragents.overseer/ragents-bundle.json +3 -3
- package/bundles/ragents.overseer/server/index.js +0 -1
- package/bundles/ragents.overseer/server/index.js.map +2 -2
- package/bundles/ragents.processes/ragents-bundle.json +2 -2
- package/bundles/ragents.product/ragents-bundle.json +2 -2
- package/bundles/ragents.profile-distribution/ragents-bundle.json +2 -2
- package/bundles/ragents.reference/ragents-bundle.json +3 -3
- package/bundles/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
- package/bundles/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
- package/bundles/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
- package/bundles/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
- package/bundles/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
- package/bundles/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
- package/bundles/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
- package/bundles/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
- package/bundles/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/bundles/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
- package/bundles/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
- package/bundles/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
- package/bundles/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
- package/bundles/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
- package/bundles/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
- package/bundles/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
- package/bundles/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
- package/bundles/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
- package/bundles/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
- package/bundles/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
- package/bundles/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
- package/bundles/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
- package/bundles/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
- package/bundles/ragents.todo/ragents-bundle.json +2 -2
- package/bundles/ragents.transcript/ragents-bundle.json +2 -2
- package/bundles/ragents.watch/ragents-bundle.json +2 -2
- package/bundles/ragents.workspace/ragents-bundle.json +2 -2
- package/package.json +8 -8
- package/packages/agent/dist/core/agent-settings.d.ts +1 -1
- package/packages/agent/src/core/agent-settings.ts +2 -2
- package/packages/ai/dist/types.d.ts +2 -1
- package/packages/ai/src/api/ai-sdk.ts +15 -2
- package/packages/ai/src/types.ts +2 -1
- package/packages/ragents/src/runtime/journal.ts +4 -0
- package/plugins/ragents.actor-programs/prompt.hbs +4 -5
- package/plugins/ragents.actor-programs/server/access-projections.ts +1 -1
- package/plugins/ragents.actor-programs/server/index.ts +0 -2
- package/plugins/ragents.actor-programs/server/runtime.ts +2 -10
- package/plugins/ragents.actor-programs/web/ActorViewFrame.tsx +5 -20
- package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -79
- package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +0 -9
- package/plugins/ragents.actor-programs/web/api.ts +0 -20
- package/plugins/ragents.actor-programs/web/context.ts +0 -3
- package/plugins/ragents.actor-programs/web/index.tsx +0 -9
- package/plugins/ragents.orchestration/ragents-plugin.json +1 -2
- package/plugins/ragents.orchestration/server/index.ts +1 -11
- package/plugins/ragents.orchestration/web/ActorChat.tsx +1 -1
- package/plugins/ragents.orchestration/web/ActorChatControls.tsx +1 -1
- package/plugins/ragents.orchestration/web/FlowInspector.tsx +2 -4
- package/plugins/ragents.orchestration/web/InspectionPanel.tsx +21 -0
- package/plugins/ragents.orchestration/web/index.tsx +13 -9
- package/plugins/ragents.orchestration/web/program-slot.ts +0 -3
- package/plugins/ragents.orchestration/web/run-panel/AddresseeControl.tsx +15 -36
- package/plugins/ragents.orchestration/web/run-panel/RunPanel.tsx +76 -451
- package/plugins/ragents.orchestration/web/run-panel/run-panel-actors.ts +0 -27
- package/plugins/ragents.orchestration/web/run-panel/run-panel-state.ts +7 -40
- package/plugins/ragents.overseer/server/coordinator-prompt.ts +0 -1
- package/plugins/ragents.reference/examples.ts +1 -2
- package/plugins/ragents.reference/run-scripts/balcony-wizard/RUN.md +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -2
- package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +6 -7
- package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -3
- package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +3 -13
- package/plugins/ragents.reference/run-scripts/conversation-circle/tests/helpers.ts +1 -1
- package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +1 -6
- package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +1 -15
- package/plugins/ragents.reference/run-scripts/learning-afternoon/tests/program.test.ts +1 -1
- package/plugins/ragents.reference/run-scripts/moderated-round/tests/helpers.ts +1 -1
- package/plugins/ragents.reference/run-scripts/quick-note/RUN.md +1 -1
- package/plugins/ragents.reference/run-scripts/quick-note/src/server.ts +1 -2
- package/plugins/ragents.reference/run-scripts/quick-note/tests/program.test.ts +0 -2
- package/plugins/ragents.reference/run-scripts/run-roster/RUN.md +2 -2
- package/plugins/ragents.reference/run-scripts/run-roster/src/server.ts +1 -2
- package/plugins/ragents.reference/run-scripts/run-roster/tests/program.test.ts +0 -2
- package/plugins/ragents.reference/run-scripts/shared-actor-list/src/server.ts +1 -1
- package/plugins/ragents.reference/run-scripts/shared-actor-list/tests/helpers.ts +1 -1
- package/plugins/ragents.reference/run-scripts/word-game/src/contract.ts +1 -1
- package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +0 -1
- package/plugins/ragents.reference/run-scripts/word-game/tests/program.test.ts +1 -2
- package/plugins/ragents.reference/skills/80-actor-text-analysis/SKILL.md +1 -1
- package/plugins/ragents.reference/skills/90-actor-notes/SKILL.md +1 -1
- package/plugins/ragents.reference/walkthroughs.ts +13 -14
- package/apps/web/dist/assets/highlighted-body-KPVGNVTW-BwD4uYEe.js +0 -1
- package/apps/web/dist/assets/main-KBrc5Q77.js +0 -11
- package/apps/web/dist/assets/mermaid-HWGCJPDP-CSCkaArr.js +0 -608
- package/apps/web/dist/assets/runPanel-BjG5NBKm.js +0 -26
- package/apps/web/src/App.tsx +0 -318
- package/apps/web/src/SessionList.tsx +0 -157
- package/apps/web/src/SurfaceShortcuts.tsx +0 -60
- package/apps/web/src/WorkspacePanel.tsx +0 -309
- package/apps/web/src/run-panel.tsx +0 -52
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js +0 -11
- package/bundles/ragents.orchestration/server/chunks/chunk-QS3WJ3D7.js.map +0 -7
- package/bundles/ragents.orchestration/server/exports/server/contract.js +0 -9
- package/bundles/ragents.orchestration/server/exports/server/contract.js.map +0 -7
- package/bundles/ragents.orchestration/surface.hbs +0 -39
- package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js +0 -2
- package/bundles/ragents.orchestration/web/chunks/chunk-CFMJD5ZI.js.map +0 -7
- package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js +0 -2
- package/bundles/ragents.orchestration/web/chunks/chunk-HOBLI364.js.map +0 -7
- package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js +0 -2
- package/bundles/ragents.orchestration/web/chunks/chunk-UCIMT2R4.js.map +0 -7
- package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js +0 -2
- package/bundles/ragents.orchestration/web/chunks/chunk-XGPPQCZR.js.map +0 -7
- package/bundles/ragents.orchestration/web/exports/web/surface-entities.js +0 -2
- package/bundles/ragents.orchestration/web/exports/web/surface-entities.js.map +0 -7
- package/bundles/ragents.orchestration/web/exports/web/tile-docking.js +0 -2
- package/bundles/ragents.orchestration/web/exports/web/tile-docking.js.map +0 -7
- package/plugins/ragents.actor-programs/web/ExpandIcon.tsx +0 -5
- package/plugins/ragents.actor-programs/web/FullscreenHost.tsx +0 -31
- package/plugins/ragents.orchestration/contract.ts +0 -51
- package/plugins/ragents.orchestration/server/contract.ts +0 -14
- package/plugins/ragents.orchestration/server/surface-tool.ts +0 -198
- package/plugins/ragents.orchestration/surface.hbs +0 -39
- package/plugins/ragents.orchestration/tiled-layout.ts +0 -55
- package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +0 -29
- package/plugins/ragents.orchestration/web/ActorShortcut.tsx +0 -74
- package/plugins/ragents.orchestration/web/ActorSurfaceControls.tsx +0 -130
- package/plugins/ragents.orchestration/web/ActorTile.tsx +0 -23
- package/plugins/ragents.orchestration/web/Center.tsx +0 -133
- package/plugins/ragents.orchestration/web/SurfaceTiles.tsx +0 -47
- package/plugins/ragents.orchestration/web/TiledSurface.tsx +0 -280
- package/plugins/ragents.orchestration/web/actor-header-settings.ts +0 -41
- package/plugins/ragents.orchestration/web/run-panel/RunPanelSettings.tsx +0 -50
- package/plugins/ragents.orchestration/web/run-panel/run-panel-settings.ts +0 -47
- package/plugins/ragents.orchestration/web/run-panel/sheet-status.ts +0 -30
- package/plugins/ragents.orchestration/web/surface-entities.ts +0 -21
- package/plugins/ragents.orchestration/web/surface-view-settings.ts +0 -42
- package/plugins/ragents.orchestration/web/tile-docking.ts +0 -105
- package/plugins/ragents.orchestration/web/tiled-view-settings.ts +0 -74
- /package/apps/web/dist/assets/{mermaid-HWGCJPDP-BPn4aV2r.css → main-BPn4aV2r.css} +0 -0
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { useAccess } from "@ragents/web/AccessContext";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { SurfaceTiles } from "./SurfaceTiles";
|
|
5
|
-
import type { SurfaceTileNode } from "../tiled-layout";
|
|
6
|
-
import { SurfaceStartup } from "./SurfaceStartup";
|
|
7
|
-
import { surfaceStartupState } from "./surface-startup";
|
|
8
|
-
import { tileEntities } from "./tile-docking";
|
|
9
|
-
import { publishSurfaceEntities } from "./surface-entities";
|
|
10
|
-
import { initialTileLayout, saveSurfacePresentation, useSurfacePresentation } from "./tiled-view-settings";
|
|
11
|
-
import { StatusGroup } from "@ragents/web/StatusGroup";
|
|
12
|
-
import { statusControlClass } from "./constants";
|
|
13
|
-
import { runViewFrom } from "@ragents/web/run-view";
|
|
14
|
-
import { ActorSurfaceControls, ActorHeaderModeControl } from "./ActorSurfaceControls";
|
|
15
|
-
import { actorVisibleOnSurface, saveSurfaceViewPreferences, useSurfaceViewPreferences } from "./surface-view-settings";
|
|
16
|
-
import { useSurfaceController, type SurfaceCenterContext, type EntityReference } from "@ragents/web/PluginRegistry";
|
|
17
|
-
|
|
18
|
-
export function OrchestrationCenter(props: SurfaceCenterContext) {
|
|
19
|
-
return <RunOrchestrationCenter key={props.session.session.id} {...props} />;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function RunOrchestrationCenter({
|
|
23
|
-
cardSections,
|
|
24
|
-
surfaceElements,
|
|
25
|
-
navigation,
|
|
26
|
-
session,
|
|
27
|
-
statusContainer,
|
|
28
|
-
runToolbarContainer,
|
|
29
|
-
}: SurfaceCenterContext) {
|
|
30
|
-
const inspect = useAccess().can("runs.inspect");
|
|
31
|
-
const surface = useSurfaceController();
|
|
32
|
-
if (!surface) throw new Error("The surface needs the surface controller of the orchestration plugin.");
|
|
33
|
-
const lastRunView = useRef<ReturnType<typeof runViewFrom>>(undefined);
|
|
34
|
-
const parsedRunView = runViewFrom(session.runView);
|
|
35
|
-
if (parsedRunView !== undefined) lastRunView.current = parsedRunView;
|
|
36
|
-
const runView = parsedRunView ?? lastRunView.current;
|
|
37
|
-
const { programLayout, personalLayout, presentation, programBasis } = useSurfacePresentation(session.session.id, runView);
|
|
38
|
-
const [presentationError, setPresentationError] = useState<string>();
|
|
39
|
-
const preferences = useSurfaceViewPreferences(session.session.id);
|
|
40
|
-
const appActorIds = useMemo(() => new Set(surfaceElements.flatMap((contribution) => contribution.select(session)
|
|
41
|
-
.flatMap((element) => element.anchorActorId ? [element.anchorActorId] : []))), [surfaceElements, session]);
|
|
42
|
-
const hiddenActorIds = useMemo(() => runView?.actors
|
|
43
|
-
.filter((actor) => actor.kind !== "human" && (inspect ? !actorVisibleOnSurface(actor, appActorIds, preferences, runView.primaryActorId) : actor.kind === "script")).map((actor) => actor.id) ?? [],
|
|
44
|
-
[inspect, runView, appActorIds, preferences]);
|
|
45
|
-
const visibleApps = useMemo(() => surfaceElements.flatMap((contribution) => contribution.select(session)
|
|
46
|
-
.filter((element) => element.visible !== false).map((element) => `app:${element.id}`)), [surfaceElements, session]);
|
|
47
|
-
const visibleEntities = new Set([
|
|
48
|
-
...visibleApps,
|
|
49
|
-
...(runView?.actors.filter((actor) => actor.kind !== "human" && actor.lifecycle?.kind !== "stopped"
|
|
50
|
-
&& (inspect || actor.kind === "agent")).map((actor) => `@${actor.handle}`) ?? []),
|
|
51
|
-
]);
|
|
52
|
-
/** Without any stored tree the visible participants are split once; only a real change is saved. */
|
|
53
|
-
const fallbackRoot = useMemo(() => personalLayout === null && programLayout.layout?.root === undefined
|
|
54
|
-
? initialTileLayout([
|
|
55
|
-
...visibleApps,
|
|
56
|
-
...(runView?.actors.filter((actor) => actor.kind !== "human" && actor.lifecycle?.kind !== "stopped" && !hiddenActorIds.includes(actor.id)).map((actor) => `@${actor.handle}`) ?? []),
|
|
57
|
-
])
|
|
58
|
-
: null, [personalLayout, programLayout, visibleApps, runView, hiddenActorIds]);
|
|
59
|
-
const root = presentation.root ?? fallbackRoot;
|
|
60
|
-
const hasVisibleContent = tileEntities(root).some((entity) => visibleEntities.has(entity));
|
|
61
|
-
const stageEntities = useMemo(() => new Set(tileEntities(root)), [root]);
|
|
62
|
-
useEffect(() => publishSurfaceEntities(session.session.id, stageEntities), [session.session.id, stageEntities]);
|
|
63
|
-
useEffect(() => () => publishSurfaceEntities(session.session.id, undefined), [session.session.id]);
|
|
64
|
-
const startupState = surfaceStartupState({ view: runView, startup: session.startup, connected: session.connected,
|
|
65
|
-
running: session.running, error: session.conversationError });
|
|
66
|
-
const stageRef = useRef<HTMLDivElement | null>(null);
|
|
67
|
-
const showStartup = !hasVisibleContent && startupState !== undefined;
|
|
68
|
-
|
|
69
|
-
const visibleStageSize = useCallback((): { width: number; height: number } | undefined => {
|
|
70
|
-
const element = stageRef.current;
|
|
71
|
-
if (!element) return undefined;
|
|
72
|
-
const stage = element.getBoundingClientRect();
|
|
73
|
-
const panel = document.getElementById("workspace-panel")?.getBoundingClientRect();
|
|
74
|
-
const overlap = panel ? Math.min(stage.right, panel.right) - Math.max(stage.left, panel.left) : 0;
|
|
75
|
-
return { width: stage.width - Math.max(0, overlap), height: stage.height };
|
|
76
|
-
}, []);
|
|
77
|
-
|
|
78
|
-
useEffect(() => {
|
|
79
|
-
const element = stageRef.current;
|
|
80
|
-
if (!element) return;
|
|
81
|
-
const observer = new ResizeObserver(() => {
|
|
82
|
-
const size = visibleStageSize();
|
|
83
|
-
if (size) element.style.setProperty("--surface-visible-width", `${size.width}px`);
|
|
84
|
-
});
|
|
85
|
-
observer.observe(element);
|
|
86
|
-
const panel = document.getElementById("workspace-panel");
|
|
87
|
-
if (panel) observer.observe(panel);
|
|
88
|
-
return () => observer.disconnect();
|
|
89
|
-
}, [visibleStageSize]);
|
|
90
|
-
|
|
91
|
-
const handleSelect = useCallback((selection: EntityReference) => {
|
|
92
|
-
surface.acceptSelection(selection);
|
|
93
|
-
}, [surface]);
|
|
94
|
-
|
|
95
|
-
const changeTiles = (next: SurfaceTileNode | null) => {
|
|
96
|
-
try {
|
|
97
|
-
saveSurfacePresentation(session.session.id, { root: next, programBasis });
|
|
98
|
-
setPresentationError(undefined);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
setPresentationError(error instanceof Error ? error.message : String(error));
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div className="relative flex h-full min-h-0 min-w-0 flex-1 overflow-clip" data-view="tiled">
|
|
106
|
-
{personalLayout && <StatusGroup container={statusContainer} label="Surface" order={20}>
|
|
107
|
-
<button className={statusControlClass} onClick={() => saveSurfacePresentation(session.session.id, null)}
|
|
108
|
-
title="Reset your own arrangement and follow the program arrangement" type="button">Apply program default</button>
|
|
109
|
-
</StatusGroup>}
|
|
110
|
-
{runToolbarContainer && createPortal(<><ActorHeaderModeControl runId={session.session.id} />{inspect && <ActorSurfaceControls session={session}
|
|
111
|
-
appActorIds={appActorIds} preferences={preferences}
|
|
112
|
-
onPreferencesChange={(next) => saveSurfaceViewPreferences(session.session.id, next)} />}</>, runToolbarContainer)}
|
|
113
|
-
<div aria-label="Surface" className="relative min-h-0 min-w-0 flex-[1_1_360px] overflow-clip bg-[image:var(--surface-backdrop)]" ref={stageRef}>
|
|
114
|
-
<SurfaceGrain />
|
|
115
|
-
{(programLayout.error || presentationError) && <p className="absolute inset-x-2 top-2 z-10 bg-secondary p-3 text-destructive" role="alert">{programLayout.error || presentationError}</p>}
|
|
116
|
-
{runView && !showStartup && <div className="absolute inset-y-0 left-0 w-[var(--surface-visible-width,100%)] overflow-hidden">
|
|
117
|
-
<SurfaceTiles root={root} onChange={changeTiles}
|
|
118
|
-
surfaceElements={surfaceElements} cardSections={cardSections} session={session} view={runView} navigation={navigation} onSelect={handleSelect}
|
|
119
|
-
selected={surface.selection} />
|
|
120
|
-
</div>}
|
|
121
|
-
{showStartup && <SurfaceStartup state={startupState} />}
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Fine noise over the surface so the material does not look smooth. */
|
|
128
|
-
function SurfaceGrain() {
|
|
129
|
-
return <svg aria-hidden="true" className="pointer-events-none absolute inset-0 size-full opacity-10" preserveAspectRatio="none" viewBox="0 0 180 180">
|
|
130
|
-
<filter id="surface-grain"><feTurbulence baseFrequency=".76" numOctaves="3" stitchTiles="stitch" type="fractalNoise" /></filter>
|
|
131
|
-
<path d="M0 0h180v180H0z" fill="#887890" filter="url(#surface-grain)" opacity=".32" />
|
|
132
|
-
</svg>;
|
|
133
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { useAccess } from "@ragents/web/AccessContext";
|
|
2
|
-
import type { SurfaceElementContribution, CardSectionContribution, EntityReference, SessionContext, SessionNavigation } from "@ragents/web/PluginRegistry";
|
|
3
|
-
import { surfaceTileChatInput, type SurfaceTileNode } from "../tiled-layout";
|
|
4
|
-
import { ActorTile } from "./ActorTile";
|
|
5
|
-
import { FlowInspector } from "./FlowInspector";
|
|
6
|
-
import { TiledSurface, type SurfaceTileItem } from "./TiledSurface";
|
|
7
|
-
import { actorTone, type RunView } from "@ragents/web/run-view";
|
|
8
|
-
|
|
9
|
-
export function SurfaceTiles({ root, onChange, surfaceElements, cardSections, session, view, navigation, onSelect, selected }: {
|
|
10
|
-
root: SurfaceTileNode | null;
|
|
11
|
-
onChange: (root: SurfaceTileNode | null) => void;
|
|
12
|
-
surfaceElements: readonly SurfaceElementContribution[];
|
|
13
|
-
cardSections: readonly CardSectionContribution[];
|
|
14
|
-
session: SessionContext;
|
|
15
|
-
view: RunView;
|
|
16
|
-
navigation: SessionNavigation;
|
|
17
|
-
onSelect: (selection: EntityReference) => void;
|
|
18
|
-
selected?: EntityReference;
|
|
19
|
-
}) {
|
|
20
|
-
const inspect = useAccess().can("runs.inspect");
|
|
21
|
-
const actors = view.actors.filter((actor) => actor.kind !== "human" && actor.lifecycle?.kind !== "stopped"
|
|
22
|
-
&& (inspect || actor.kind === "agent"));
|
|
23
|
-
const items: SurfaceTileItem[] = [
|
|
24
|
-
...actors.map((actor): SurfaceTileItem => ({
|
|
25
|
-
entity: `@${actor.handle}`,
|
|
26
|
-
title: `${actor.displayName || actor.handle} (@${actor.handle})`,
|
|
27
|
-
tone: actorTone(view, actor),
|
|
28
|
-
content: actor.kind === "agent"
|
|
29
|
-
? <ActorTile actor={actor} view={view} cardSections={cardSections} chatInput={surfaceTileChatInput(root, `@${actor.handle}`)} session={session} navigation={navigation} onSelect={onSelect} />
|
|
30
|
-
: <FlowInspector view={view} selection={{ type: "actor", id: actor.id }} canGoBack={false} chatDisplay="surface"
|
|
31
|
-
composerVisible={false} onNavigate={onSelect} onBack={() => {}} primaryMessages={session.messages}
|
|
32
|
-
actorConversations={session.actorConversations} conversationError={session.conversationError} primaryRunning={session.running} />,
|
|
33
|
-
})),
|
|
34
|
-
...surfaceElements.flatMap(({ Element, select }) => select(session)
|
|
35
|
-
.filter((definition) => definition.visible !== false)
|
|
36
|
-
.map((definition): SurfaceTileItem => ({
|
|
37
|
-
entity: `app:${definition.id}`,
|
|
38
|
-
title: definition.title ?? definition.id,
|
|
39
|
-
tone: "app",
|
|
40
|
-
content: <Element definition={definition} navigation={navigation} session={session} />,
|
|
41
|
-
}))),
|
|
42
|
-
];
|
|
43
|
-
const selectedEntity = selected?.type === "run-app" ? `app:${selected.id}`
|
|
44
|
-
: selected?.type === "actor" ? actors.find((actor) => actor.id === selected.id)?.handle : undefined;
|
|
45
|
-
return <TiledSurface root={root} onChange={onChange} items={items} canArrange={inspect}
|
|
46
|
-
selectedEntity={selected?.type === "actor" && selectedEntity ? `@${selectedEntity}` : selectedEntity} />;
|
|
47
|
-
}
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
|
2
|
-
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, CodeXml, LayoutGrid, Sparkles, GripVertical, X } from "lucide-react";
|
|
3
|
-
import { surfaceTileNodeOf, type SurfaceTileNode } from "../tiled-layout";
|
|
4
|
-
import { SURFACE_TILE_DRAG_TYPE, dockTile, removeTile, resizeTile, tileEntities, tileGeometry, tileMinimum, type TileDivider, type TileDockSide, type TileRect } from "./tile-docking";
|
|
5
|
-
import { materialCardClass, materialHeadClass, materialIconClass, materialTitleClass } from "./MaterialBody";
|
|
6
|
-
|
|
7
|
-
export interface SurfaceTileItem { entity: string; title: string; content: ReactNode; tone?: "agent" | "primary" | "script" | "app" }
|
|
8
|
-
interface TiledSurfaceProps {
|
|
9
|
-
root: SurfaceTileNode | null;
|
|
10
|
-
onChange: (root: SurfaceTileNode | null) => void;
|
|
11
|
-
items: readonly SurfaceTileItem[];
|
|
12
|
-
canArrange: boolean;
|
|
13
|
-
selectedEntity?: string;
|
|
14
|
-
onSelect?: (entity: string) => void;
|
|
15
|
-
}
|
|
16
|
-
interface DockTarget { entity: string | null; side: TileDockSide }
|
|
17
|
-
const sides: readonly TileDockSide[] = ["left", "right", "top", "bottom"];
|
|
18
|
-
const sideNames = { left: "Dock left", right: "Dock right", top: "Dock top", bottom: "Dock bottom" };
|
|
19
|
-
const sideIcons = { left: ArrowLeft, right: ArrowRight, top: ArrowUp, bottom: ArrowDown };
|
|
20
|
-
const materialFaceRect = (slot: TileRect): TileRect => ({
|
|
21
|
-
left: slot.left + 2,
|
|
22
|
-
top: slot.top + 2,
|
|
23
|
-
width: Math.max(0, slot.width - 4),
|
|
24
|
-
height: Math.max(0, slot.height - 4),
|
|
25
|
-
});
|
|
26
|
-
const positioned = (rect: TileRect): CSSProperties => ({ left: rect.left, top: rect.top, width: rect.width, height: rect.height });
|
|
27
|
-
const sidePlacement = {
|
|
28
|
-
left: { inner: "left-0 top-[40px]", outer: "left-0 top-[calc(50%_-_18px)]" },
|
|
29
|
-
right: { inner: "right-0 top-[40px]", outer: "right-0 top-[calc(50%_-_18px)]" },
|
|
30
|
-
top: { inner: "top-0 left-[40px]", outer: "top-0 left-[calc(50%_-_18px)]" },
|
|
31
|
-
bottom: { inner: "bottom-0 left-[40px]", outer: "bottom-0 left-[calc(50%_-_18px)]" },
|
|
32
|
-
};
|
|
33
|
-
const dockTargetClass = "absolute grid size-[36px] place-items-center rounded-[5px] border border-primary bg-surface text-primary shadow-[0_2px_8px_#0002] pointer-events-auto data-[active]:bg-primary data-[active]:text-surface [&>svg]:pointer-events-none";
|
|
34
|
-
const dividerClass = "absolute z-3 grid touch-none place-items-center rounded-[4px] focus-visible:outline-2 focus-visible:outline-primary focus-visible:-outline-offset-2 data-[direction=horizontal]:cursor-col-resize data-[direction=vertical]:cursor-row-resize [&>span]:pointer-events-none [&>span]:rounded-[4px] [&>span]:bg-border-strong data-[direction=horizontal]:[&>span]:h-8 data-[direction=horizontal]:[&>span]:w-[3px] data-[direction=vertical]:[&>span]:h-[3px] data-[direction=vertical]:[&>span]:w-8 hover:[&>span]:bg-primary focus-visible:[&>span]:bg-primary";
|
|
35
|
-
|
|
36
|
-
export function TiledSurface({ root, onChange, items, canArrange, selectedEntity, onSelect }: TiledSurfaceProps) {
|
|
37
|
-
const stageRef = useRef<HTMLDivElement>(null);
|
|
38
|
-
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
39
|
-
const [available, setAvailable] = useState({ width: 0, height: 0 });
|
|
40
|
-
const [dragEntity, setDragEntity] = useState<string | null>(null);
|
|
41
|
-
const [target, setTarget] = useState<DockTarget | null>(null);
|
|
42
|
-
const [resizing, setResizing] = useState(false);
|
|
43
|
-
const [error, setError] = useState<string | null>(null);
|
|
44
|
-
const [selected, setSelected] = useState<string>();
|
|
45
|
-
const [resizePreview, setResizePreview] = useState<SurfaceTileNode | null>(null);
|
|
46
|
-
const resizeFrame = useRef<number | null>(null);
|
|
47
|
-
const resizeRef = useRef<{ divider: TileDivider; start: number; root: SurfaceTileNode; next: SurfaceTileNode; pointerId: number; handle: HTMLDivElement } | null>(null);
|
|
48
|
-
const visibleRoot = resizePreview ?? root;
|
|
49
|
-
const entities = useMemo(() => tileEntities(root), [root]);
|
|
50
|
-
const mountedEntities = useMemo(() => [...entities].sort(), [entities]);
|
|
51
|
-
const itemMap = useMemo(() => new Map(items.map((item) => [item.entity, item])), [items]);
|
|
52
|
-
const minimum = root ? tileMinimum(root) : { width: 0, height: 0 };
|
|
53
|
-
const compact = available.width < minimum.width || available.height < minimum.height;
|
|
54
|
-
const active = selected && entities.includes(selected) ? selected : entities[0];
|
|
55
|
-
const rect = { left: 0, top: 0, ...size };
|
|
56
|
-
const geometry = useMemo(() => visibleRoot ? tileGeometry(visibleRoot, { left: 0, top: 0, ...size }) : { leaves: new Map<string, TileRect>(), dividers: [] }, [visibleRoot, size]);
|
|
57
|
-
|
|
58
|
-
useLayoutEffect(() => {
|
|
59
|
-
const stage = stageRef.current;
|
|
60
|
-
if (!stage) return;
|
|
61
|
-
const container = stage.parentElement!;
|
|
62
|
-
const measure = () => {
|
|
63
|
-
const width = stage.clientWidth;
|
|
64
|
-
const height = stage.clientHeight;
|
|
65
|
-
setSize((previous) => previous.width === width && previous.height === height ? previous : { width, height });
|
|
66
|
-
const style = getComputedStyle(container);
|
|
67
|
-
const availableWidth = container.clientWidth - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight);
|
|
68
|
-
const availableHeight = container.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);
|
|
69
|
-
setAvailable((previous) => previous.width === availableWidth && previous.height === availableHeight
|
|
70
|
-
? previous : { width: availableWidth, height: availableHeight });
|
|
71
|
-
};
|
|
72
|
-
const observer = new ResizeObserver(measure);
|
|
73
|
-
observer.observe(stage);
|
|
74
|
-
observer.observe(container);
|
|
75
|
-
measure();
|
|
76
|
-
return () => observer.disconnect();
|
|
77
|
-
}, []);
|
|
78
|
-
|
|
79
|
-
useEffect(() => { if (selectedEntity) setSelected(selectedEntity); }, [selectedEntity]);
|
|
80
|
-
useLayoutEffect(() => {
|
|
81
|
-
if (!canArrange) { setDragEntity(null); setTarget(null); return; }
|
|
82
|
-
let pendingFrame: number | null = null;
|
|
83
|
-
const cancelActivation = () => {
|
|
84
|
-
if (pendingFrame !== null) cancelAnimationFrame(pendingFrame);
|
|
85
|
-
pendingFrame = null;
|
|
86
|
-
};
|
|
87
|
-
const start = (event: DragEvent) => {
|
|
88
|
-
if (!event.dataTransfer?.types.includes(SURFACE_TILE_DRAG_TYPE)) return;
|
|
89
|
-
const entity = event.dataTransfer.getData(SURFACE_TILE_DRAG_TYPE);
|
|
90
|
-
if (!entity) return;
|
|
91
|
-
cancelActivation();
|
|
92
|
-
pendingFrame = requestAnimationFrame(() => {
|
|
93
|
-
pendingFrame = null;
|
|
94
|
-
if (!event.defaultPrevented) setDragEntity(entity);
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
const end = () => { cancelActivation(); setDragEntity(null); setTarget(null); };
|
|
98
|
-
const key = (event: KeyboardEvent) => { if (event.key === "Escape") end(); };
|
|
99
|
-
window.addEventListener("dragstart", start);
|
|
100
|
-
window.addEventListener("dragend", end);
|
|
101
|
-
window.addEventListener("drop", end);
|
|
102
|
-
window.addEventListener("keydown", key);
|
|
103
|
-
return () => {
|
|
104
|
-
cancelActivation();
|
|
105
|
-
window.removeEventListener("dragstart", start);
|
|
106
|
-
window.removeEventListener("dragend", end);
|
|
107
|
-
window.removeEventListener("drop", end);
|
|
108
|
-
window.removeEventListener("keydown", key);
|
|
109
|
-
};
|
|
110
|
-
}, [canArrange]);
|
|
111
|
-
|
|
112
|
-
const cancelResize = useCallback(() => {
|
|
113
|
-
if (resizeFrame.current !== null) cancelAnimationFrame(resizeFrame.current);
|
|
114
|
-
resizeFrame.current = null;
|
|
115
|
-
const state = resizeRef.current;
|
|
116
|
-
resizeRef.current = null;
|
|
117
|
-
if (state?.handle.hasPointerCapture(state.pointerId)) state.handle.releasePointerCapture(state.pointerId);
|
|
118
|
-
setResizePreview(null);
|
|
119
|
-
setResizing(false);
|
|
120
|
-
}, []);
|
|
121
|
-
|
|
122
|
-
useLayoutEffect(() => {
|
|
123
|
-
cancelResize();
|
|
124
|
-
return cancelResize;
|
|
125
|
-
}, [root, size, compact, cancelResize]);
|
|
126
|
-
|
|
127
|
-
useEffect(() => {
|
|
128
|
-
if (!resizing) return;
|
|
129
|
-
const key = (event: KeyboardEvent) => { if (event.key === "Escape") cancelResize(); };
|
|
130
|
-
window.addEventListener("keydown", key);
|
|
131
|
-
window.addEventListener("blur", cancelResize);
|
|
132
|
-
return () => {
|
|
133
|
-
window.removeEventListener("keydown", key);
|
|
134
|
-
window.removeEventListener("blur", cancelResize);
|
|
135
|
-
};
|
|
136
|
-
}, [resizing, cancelResize]);
|
|
137
|
-
|
|
138
|
-
const updateResize = (event: { pointerId: number; clientX: number; clientY: number }) => {
|
|
139
|
-
const state = resizeRef.current;
|
|
140
|
-
if (!state || state.pointerId !== event.pointerId) return;
|
|
141
|
-
const coordinate = state.divider.direction === "horizontal" ? event.clientX : event.clientY;
|
|
142
|
-
const ratio = Math.max(state.divider.minimum, Math.min(state.divider.maximum,
|
|
143
|
-
state.divider.ratio + (coordinate - state.start) / state.divider.span));
|
|
144
|
-
state.next = coordinate === state.start ? state.root : resizeTile(state.root, state.divider.path, ratio);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const select = (entity: string) => { setSelected(entity); onSelect?.(entity); };
|
|
148
|
-
const previewRoot = canArrange && target && dragEntity ? dockTile(root, dragEntity, target.entity, target.side) : null;
|
|
149
|
-
const previewMinimum = previewRoot ? tileMinimum(previewRoot) : null;
|
|
150
|
-
const preview = previewRoot && previewRoot !== root && previewMinimum && dragEntity
|
|
151
|
-
? available.width < previewMinimum.width || available.height < previewMinimum.height
|
|
152
|
-
? rect
|
|
153
|
-
: tileGeometry(previewRoot, rect).leaves.get(dragEntity)
|
|
154
|
-
: null;
|
|
155
|
-
|
|
156
|
-
const targets = (entity: string | null) => sides.map((side) => {
|
|
157
|
-
const Icon = sideIcons[side];
|
|
158
|
-
return <div key={side} className={`${dockTargetClass} ${sidePlacement[side][entity === null ? "outer" : "inner"]}`}
|
|
159
|
-
data-dock-side={side} data-dock-entity={entity ?? ""} title={sideNames[side]}
|
|
160
|
-
data-active={target?.entity === entity && target.side === side || undefined}>
|
|
161
|
-
<Icon size={18} aria-hidden="true" />
|
|
162
|
-
</div>;
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
return <div className="flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden p-2 data-[dragging]:[&_iframe]:pointer-events-none data-[resizing]:[&_iframe]:pointer-events-none"
|
|
166
|
-
data-dragging={canArrange && dragEntity !== null || undefined} data-resizing={resizing || undefined}>
|
|
167
|
-
{compact && entities.length > 1 && <label className="flex items-center gap-2 pb-2 text-[0.8rem]">Tile
|
|
168
|
-
<select className="max-w-[60%] min-w-0" aria-label="Visible tile" value={active} onChange={(event) => select(event.target.value)}>
|
|
169
|
-
{entities.map((entity) => <option key={entity} value={entity}>{itemMap.get(entity)?.title ?? entity}</option>)}
|
|
170
|
-
</select>
|
|
171
|
-
<span className="truncate text-muted-foreground">More space needed for the split view</span>
|
|
172
|
-
</label>}
|
|
173
|
-
<div ref={stageRef} className="relative min-h-0 min-w-0 flex-1 overflow-hidden"
|
|
174
|
-
onDragOver={(event) => {
|
|
175
|
-
if (!canArrange || dragEntity === null || !event.dataTransfer.types.includes(SURFACE_TILE_DRAG_TYPE)) return;
|
|
176
|
-
event.preventDefault();
|
|
177
|
-
event.dataTransfer.dropEffect = "move";
|
|
178
|
-
const element = event.target instanceof Element ? event.target.closest<HTMLElement>("[data-dock-side]") : null;
|
|
179
|
-
const next = element && stageRef.current?.contains(element)
|
|
180
|
-
? { entity: element.dataset.dockEntity || null, side: element.dataset.dockSide as TileDockSide } : null;
|
|
181
|
-
setTarget((previous) => previous?.entity === next?.entity && previous?.side === next?.side ? previous : next);
|
|
182
|
-
}}
|
|
183
|
-
onDragLeave={(event) => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setTarget(null); }}
|
|
184
|
-
onDrop={(event) => {
|
|
185
|
-
if (!canArrange || dragEntity === null) return;
|
|
186
|
-
const entity = event.dataTransfer.getData(SURFACE_TILE_DRAG_TYPE);
|
|
187
|
-
if (!entity || entity !== dragEntity) return;
|
|
188
|
-
event.preventDefault();
|
|
189
|
-
if (!itemMap.has(entity) && !entities.includes(entity)) return;
|
|
190
|
-
const element = event.target instanceof Element ? event.target.closest<HTMLElement>("[data-dock-side]") : null;
|
|
191
|
-
try {
|
|
192
|
-
if (!root || element) {
|
|
193
|
-
const next = dockTile(root, entity, element?.dataset.dockEntity || null, (element?.dataset.dockSide ?? "right") as TileDockSide);
|
|
194
|
-
if (next !== root) onChange(next === null ? null : surfaceTileNodeOf(next));
|
|
195
|
-
select(entity);
|
|
196
|
-
setError(null);
|
|
197
|
-
}
|
|
198
|
-
} catch (cause) {
|
|
199
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
200
|
-
} finally {
|
|
201
|
-
setDragEntity(null);
|
|
202
|
-
setTarget(null);
|
|
203
|
-
}
|
|
204
|
-
}}>
|
|
205
|
-
{error && <div className="absolute inset-x-2 bottom-2 z-7 flex items-center gap-2 rounded-lg border border-primary bg-surface p-3 text-foreground shadow-[0_2px_8px_#0002]" role="alert"><span className="flex-1">{error}</span><button className="grid cursor-pointer place-items-center bg-transparent p-1" type="button" aria-label="Close message" onClick={() => setError(null)}><X size={16} /></button></div>}
|
|
206
|
-
{!root && <div className="grid h-full place-items-center rounded-lg border border-dashed border-border-strong p-6 text-center text-muted-foreground">{canArrange ? "Drag an actor or a mini-app from the header here." : "No content has been arranged for this tile view yet."}</div>}
|
|
207
|
-
{mountedEntities.map((entity) => {
|
|
208
|
-
const item = itemMap.get(entity);
|
|
209
|
-
const bounds = materialFaceRect(compact ? rect : geometry.leaves.get(entity)!);
|
|
210
|
-
return <section key={entity} className={materialCardClass(item?.tone ?? (entity.startsWith("app:") ? "app" : "agent"),
|
|
211
|
-
"absolute z-0 flex min-h-0 min-w-0 flex-col overflow-visible")} data-tile-entity={entity} data-tone="material"
|
|
212
|
-
style={{ ...positioned(bounds), display: compact && entity !== active ? "none" : undefined }}
|
|
213
|
-
onFocusCapture={() => { if (active !== entity) setSelected(entity); }}>
|
|
214
|
-
<div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-[15.5px]">
|
|
215
|
-
<header className={`${materialHeadClass} flex-[0_0_49px] min-w-0 select-none border-b border-[#51465738] text-foreground [&[draggable=true]]:cursor-grab`}
|
|
216
|
-
draggable={canArrange} onDragStart={(event) => {
|
|
217
|
-
if (!canArrange) { event.preventDefault(); return; }
|
|
218
|
-
event.dataTransfer.setData(SURFACE_TILE_DRAG_TYPE, entity);
|
|
219
|
-
event.dataTransfer.effectAllowed = "move";
|
|
220
|
-
}}>
|
|
221
|
-
{canArrange && <GripVertical size={16} aria-hidden="true" />}
|
|
222
|
-
<span className={materialIconClass}>{item?.tone === "app" || entity.startsWith("app:") ? <LayoutGrid size={20} aria-hidden="true" /> : item?.tone === "script" ? <CodeXml size={20} aria-hidden="true" /> : <Sparkles size={20} aria-hidden="true" />}</span>
|
|
223
|
-
<span className={`${materialTitleClass} flex-1`} title={item?.title ?? entity}>{item?.title ?? entity}</span>
|
|
224
|
-
{canArrange && <button className="grid size-[26px] cursor-pointer place-items-center rounded-[4px] bg-transparent p-0 text-muted-foreground hover:bg-glass-group hover:text-foreground"
|
|
225
|
-
type="button" draggable={false} aria-label={`Remove tile ${item?.title ?? entity}`}
|
|
226
|
-
onClick={() => onChange(removeTile(root, entity))}><X size={16} /></button>}
|
|
227
|
-
</header>
|
|
228
|
-
<div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-auto [&>*]:min-h-0 [&>*]:min-w-0 [&>*]:flex-1">{item?.content ?? <div className="p-4.5 text-muted-foreground">{canArrange ? "This content is no longer available. You can remove the tile or dock other content here." : "This content is no longer available."}</div>}</div>
|
|
229
|
-
</div>
|
|
230
|
-
{canArrange && dragEntity !== null && dragEntity !== entity && <div className="absolute inset-0 z-4"><div className="pointer-events-none absolute top-1/2 left-1/2 size-[116px] -translate-x-1/2 -translate-y-1/2">{targets(entity)}</div></div>}
|
|
231
|
-
</section>;
|
|
232
|
-
})}
|
|
233
|
-
{!compact && geometry.dividers.map((divider) => <div key={divider.path.join(".")} className={dividerClass}
|
|
234
|
-
role="separator" tabIndex={0} aria-label="Resize tile"
|
|
235
|
-
aria-orientation={divider.direction === "horizontal" ? "vertical" : "horizontal"}
|
|
236
|
-
aria-valuemin={Math.round(divider.minimum * 100)} aria-valuemax={Math.round(divider.maximum * 100)} aria-valuenow={Math.round(divider.ratio * 100)}
|
|
237
|
-
data-direction={divider.direction} style={positioned(divider)}
|
|
238
|
-
onPointerDown={(event) => {
|
|
239
|
-
if (event.button !== 0 || !event.isPrimary || !root || resizeRef.current) return;
|
|
240
|
-
event.preventDefault();
|
|
241
|
-
event.currentTarget.setPointerCapture(event.pointerId);
|
|
242
|
-
resizeRef.current = { divider, start: divider.direction === "horizontal" ? event.clientX : event.clientY, root, next: root, pointerId: event.pointerId, handle: event.currentTarget };
|
|
243
|
-
setResizing(true);
|
|
244
|
-
}}
|
|
245
|
-
onPointerMove={(event) => {
|
|
246
|
-
if (resizeRef.current?.pointerId !== event.pointerId) return;
|
|
247
|
-
updateResize(event);
|
|
248
|
-
if (resizeFrame.current !== null) return;
|
|
249
|
-
resizeFrame.current = requestAnimationFrame(() => {
|
|
250
|
-
resizeFrame.current = null;
|
|
251
|
-
if (resizeRef.current) setResizePreview(resizeRef.current.next);
|
|
252
|
-
});
|
|
253
|
-
}}
|
|
254
|
-
onPointerUp={(event) => {
|
|
255
|
-
const state = resizeRef.current;
|
|
256
|
-
if (!state || state.pointerId !== event.pointerId) return;
|
|
257
|
-
updateResize(event);
|
|
258
|
-
cancelResize();
|
|
259
|
-
if (state.next !== state.root) onChange(state.next);
|
|
260
|
-
}}
|
|
261
|
-
onLostPointerCapture={(event) => { if (resizeRef.current?.pointerId === event.pointerId) cancelResize(); }}
|
|
262
|
-
onPointerCancel={(event) => { if (resizeRef.current?.pointerId === event.pointerId) cancelResize(); }}
|
|
263
|
-
onKeyDown={(event) => {
|
|
264
|
-
if (event.key === "Escape" && resizeRef.current) { event.preventDefault(); cancelResize(); return; }
|
|
265
|
-
if (!root || resizeRef.current) return;
|
|
266
|
-
const negative = divider.direction === "horizontal" ? "ArrowLeft" : "ArrowUp";
|
|
267
|
-
const positive = divider.direction === "horizontal" ? "ArrowRight" : "ArrowDown";
|
|
268
|
-
if (event.key !== negative && event.key !== positive && event.key !== "Home" && event.key !== "End") return;
|
|
269
|
-
event.preventDefault();
|
|
270
|
-
const ratio = event.key === "Home" ? divider.minimum : event.key === "End" ? divider.maximum : divider.ratio + (event.key === positive ? 0.02 : -0.02);
|
|
271
|
-
onChange(resizeTile(root, divider.path, Math.max(divider.minimum, Math.min(divider.maximum, ratio))));
|
|
272
|
-
}}><span /></div>)}
|
|
273
|
-
{resizing && <div className="absolute inset-0 z-2" />}
|
|
274
|
-
{canArrange && dragEntity !== null && root && <div className="pointer-events-none absolute inset-2 z-6">{targets(null)}</div>}
|
|
275
|
-
{canArrange && dragEntity !== null && preview && <div className={materialCardClass(itemMap.get(dragEntity)?.tone ?? (dragEntity.startsWith("app:") ? "app" : "agent"),
|
|
276
|
-
"pointer-events-none absolute z-5 opacity-80")}
|
|
277
|
-
style={positioned(materialFaceRect(preview))}><div className="absolute inset-0 rounded-[15.5px] border-2 border-primary bg-primary/22" /></div>}
|
|
278
|
-
</div>
|
|
279
|
-
</div>;
|
|
280
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { createLocalStorageSetting } from "@ragents/web/lib/local-storage-setting";
|
|
2
|
-
import type { RunActor } from "@ragents/web/run-view";
|
|
3
|
-
|
|
4
|
-
export const ACTOR_HEADER_MODES = ["all", "active", "visible", "agent", "script"] as const;
|
|
5
|
-
export type ActorHeaderMode = typeof ACTOR_HEADER_MODES[number];
|
|
6
|
-
export const ACTOR_HEADER_MODE_LABELS: Record<ActorHeaderMode, string> = {
|
|
7
|
-
all: "All",
|
|
8
|
-
active: "Active",
|
|
9
|
-
visible: "Visible",
|
|
10
|
-
agent: "LLM agents",
|
|
11
|
-
script: "TypeScript",
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export const actorHeaderStorageKey = (runId: string) => `ragents.orchestration.actor-header:v2:${runId}`;
|
|
15
|
-
|
|
16
|
-
export function parseActorHeaderMode(raw: string | null): ActorHeaderMode {
|
|
17
|
-
if (raw === null) return "visible";
|
|
18
|
-
if (ACTOR_HEADER_MODES.some((mode) => mode === raw)) return raw as ActorHeaderMode;
|
|
19
|
-
throw new Error("The stored actor display is invalid. Allowed are all, active, visible, agent and script.");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const actorOnStage = (actor: RunActor, primaryActorId: string | null | undefined, stage: ReadonlySet<string>) =>
|
|
23
|
-
actor.id === primaryActorId || stage.has(`@${actor.handle}`);
|
|
24
|
-
|
|
25
|
-
export const actorVisibleInHeader = (actor: RunActor, mode: ActorHeaderMode, onStage: boolean) =>
|
|
26
|
-
actor.kind !== "human" && (mode === "all" || (mode === "active" ? actor.lifecycle?.kind !== "stopped" : mode === "visible" ? onStage : actor.kind === mode));
|
|
27
|
-
|
|
28
|
-
const setting = createLocalStorageSetting({
|
|
29
|
-
changeEvent: "ragents-actor-header-change",
|
|
30
|
-
matchesKey: (key) => key.startsWith("ragents.orchestration.actor-header:"),
|
|
31
|
-
parse: parseActorHeaderMode,
|
|
32
|
-
serialize: (mode: ActorHeaderMode) => mode,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export function useActorHeaderMode(runId: string): ActorHeaderMode {
|
|
36
|
-
return setting.useValue(actorHeaderStorageKey(runId));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function saveActorHeaderMode(runId: string, mode: ActorHeaderMode) {
|
|
40
|
-
setting.save(actorHeaderStorageKey(runId), mode);
|
|
41
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import { useAccess } from "@ragents/web/AccessContext";
|
|
3
|
-
import { Button, Input } from "@ragents/web/ui";
|
|
4
|
-
import { RUN_PANEL_SETTINGS_LIMITS, DEFAULT_RUN_PANEL_SETTINGS, saveRunPanelSettings, useRunPanelSettings, type RunPanelSettings as RunPanelSettingsValue } from "./run-panel-settings";
|
|
5
|
-
|
|
6
|
-
type Draft = Record<keyof RunPanelSettingsValue, string>;
|
|
7
|
-
|
|
8
|
-
const toDraft = (value: RunPanelSettingsValue): Draft => ({ sideWidth: String(value.sideWidth), openDelay: String(value.openDelay), closeDelay: String(value.closeDelay) });
|
|
9
|
-
|
|
10
|
-
const fields: readonly { key: keyof RunPanelSettingsValue; label: string; unit: string }[] = [
|
|
11
|
-
{ key: "sideWidth", label: "Chat next to the mini-app from", unit: "px" },
|
|
12
|
-
{ key: "openDelay", label: "Slide up after", unit: "ms" },
|
|
13
|
-
{ key: "closeDelay", label: "Slide back after leaving after", unit: "ms" },
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
export function RunPanelSettings() {
|
|
17
|
-
const access = useAccess();
|
|
18
|
-
const stored = useRunPanelSettings();
|
|
19
|
-
const [draft, setDraft] = useState(() => toDraft(stored));
|
|
20
|
-
const [message, setMessage] = useState<string>();
|
|
21
|
-
const [error, setError] = useState<string>();
|
|
22
|
-
useEffect(() => { setDraft(toDraft(stored)); }, [stored]);
|
|
23
|
-
const writable = access.can("settings.write");
|
|
24
|
-
const save = (next: RunPanelSettingsValue) => {
|
|
25
|
-
if (!writable) return;
|
|
26
|
-
setError(undefined);
|
|
27
|
-
setMessage(undefined);
|
|
28
|
-
try {
|
|
29
|
-
saveRunPanelSettings(next);
|
|
30
|
-
setMessage("Run panel settings saved.");
|
|
31
|
-
} catch (cause) {
|
|
32
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
return <form className="grid gap-3" onSubmit={(event) => {
|
|
36
|
-
event.preventDefault();
|
|
37
|
-
save({ sideWidth: Number(draft.sideWidth), openDelay: Number(draft.openDelay), closeDelay: Number(draft.closeDelay) });
|
|
38
|
-
}}>
|
|
39
|
-
<p className="text-muted-foreground">From this panel width on, the chat sits to the right of the mini-app instead of as a sheet below it. The delays determine how long the mouse must rest over the sheet until it slides up, and how long it stays up after leaving. Applies immediately to all runs in this browser.</p>
|
|
40
|
-
<div className="flex flex-wrap items-end gap-3">
|
|
41
|
-
{fields.map(({ key, label, unit }) => <label className="grid gap-1.5" key={key}>
|
|
42
|
-
<span>{label} ({unit})</span>
|
|
43
|
-
<Input className="w-[130px]" disabled={!writable} max={RUN_PANEL_SETTINGS_LIMITS[key].max} min={RUN_PANEL_SETTINGS_LIMITS[key].min} onChange={(event) => { setDraft({ ...draft, [key]: event.target.value }); setMessage(undefined); }} required step="1" type="number" value={draft[key]} />
|
|
44
|
-
</label>)}
|
|
45
|
-
<Button disabled={!writable} type="submit">Save</Button>
|
|
46
|
-
<Button disabled={!writable} onClick={() => save(DEFAULT_RUN_PANEL_SETTINGS)} variant="outline">Reset</Button>
|
|
47
|
-
</div>
|
|
48
|
-
{error ? <p className="text-muted-foreground" role="alert">{error}</p> : message && <p className="text-muted-foreground" role="status">{message}</p>}
|
|
49
|
-
</form>;
|
|
50
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { createLocalStorageSetting } from "@ragents/web/lib/local-storage-setting";
|
|
2
|
-
|
|
3
|
-
/** Behavior of the run panel: from which width the chat sits next to the mini-app and how the sheet reacts to the mouse. */
|
|
4
|
-
export interface RunPanelSettings {
|
|
5
|
-
sideWidth: number;
|
|
6
|
-
openDelay: number;
|
|
7
|
-
closeDelay: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const DEFAULT_RUN_PANEL_SETTINGS: RunPanelSettings = Object.freeze({ sideWidth: 900, openDelay: 160, closeDelay: 150 });
|
|
11
|
-
export const RUN_PANEL_SETTINGS_STORAGE_KEY = "ragents.orchestration.run-panel-settings";
|
|
12
|
-
export const RUN_PANEL_SETTINGS_LIMITS = Object.freeze({
|
|
13
|
-
sideWidth: { min: 400, max: 4000 },
|
|
14
|
-
openDelay: { min: 0, max: 5000 },
|
|
15
|
-
closeDelay: { min: 0, max: 10000 },
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
const inRange = (value: unknown, range: { min: number; max: number }): value is number =>
|
|
19
|
-
typeof value === "number" && Number.isInteger(value) && value >= range.min && value <= range.max;
|
|
20
|
-
|
|
21
|
-
export function parseRunPanelSettings(raw: string | null): RunPanelSettings {
|
|
22
|
-
if (raw === null) return DEFAULT_RUN_PANEL_SETTINGS;
|
|
23
|
-
const value: unknown = JSON.parse(raw);
|
|
24
|
-
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
25
|
-
|| Object.keys(value).some((key) => !["sideWidth", "openDelay", "closeDelay"].includes(key))
|
|
26
|
-
|| !("sideWidth" in value) || !inRange(value.sideWidth, RUN_PANEL_SETTINGS_LIMITS.sideWidth)
|
|
27
|
-
|| !("openDelay" in value) || !inRange(value.openDelay, RUN_PANEL_SETTINGS_LIMITS.openDelay)
|
|
28
|
-
|| !("closeDelay" in value) || !inRange(value.closeDelay, RUN_PANEL_SETTINGS_LIMITS.closeDelay)) {
|
|
29
|
-
throw new Error("The run panel settings are invalid: width 400 to 4000 pixels, delays 0 to 5000 and 10000 milliseconds respectively, integers.");
|
|
30
|
-
}
|
|
31
|
-
return value as RunPanelSettings;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const setting = createLocalStorageSetting({
|
|
35
|
-
changeEvent: "ragents-run-panel-settings-change",
|
|
36
|
-
matchesKey: (key) => key === RUN_PANEL_SETTINGS_STORAGE_KEY,
|
|
37
|
-
parse: parseRunPanelSettings,
|
|
38
|
-
serialize: JSON.stringify,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export function useRunPanelSettings(): RunPanelSettings {
|
|
42
|
-
return setting.useValue(RUN_PANEL_SETTINGS_STORAGE_KEY);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function saveRunPanelSettings(value: RunPanelSettings) {
|
|
46
|
-
setting.save(RUN_PANEL_SETTINGS_STORAGE_KEY, value);
|
|
47
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { Message } from "quassel/events";
|
|
2
|
-
|
|
3
|
-
/** The single line of the collapsed sheet: what the chat is doing right now or said last; open actions themselves are in the dock below. */
|
|
4
|
-
export interface SheetStatus {
|
|
5
|
-
text: string;
|
|
6
|
-
kind: "idle" | "working" | "waiting";
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const firstLine = (text: string, limit = 160): string => {
|
|
10
|
-
const line = text.trim().split(/\r?\n/).find((entry) => entry.trim() !== "") ?? "";
|
|
11
|
-
return line.length > limit ? `${line.slice(0, limit - 3)}...` : line;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const senderOf = (message: Message, handle: string): string =>
|
|
15
|
-
message.role === "user" ? "You" : message.sender ?? `@${handle}`;
|
|
16
|
-
|
|
17
|
-
export function sheetStatus(messages: readonly Message[], working: boolean, handle: string): SheetStatus {
|
|
18
|
-
const last = messages[messages.length - 1];
|
|
19
|
-
const waiting = messages.filter((message) => message.role === "action" && message.action?.status === undefined).length;
|
|
20
|
-
if (waiting > 0) return { kind: "waiting", text: waiting === 1 ? "Waiting for input" : `Waiting for ${waiting} inputs` };
|
|
21
|
-
if (working) {
|
|
22
|
-
if (last?.role === "thinking" && !last.closed) return { kind: "working", text: `@${handle} is thinking ...` };
|
|
23
|
-
if (last?.role === "tool" && last.tool && last.tool.result === undefined) return { kind: "working", text: `@${handle} is using ${last.tool.name} ...` };
|
|
24
|
-
if (last?.role === "assistant" && !last.closed && last.text.trim() !== "") return { kind: "working", text: `${senderOf(last, handle)} is answering: ${firstLine(last.text)}` };
|
|
25
|
-
return { kind: "working", text: `@${handle} is working ...` };
|
|
26
|
-
}
|
|
27
|
-
const spoken = [...messages].reverse().find((message) => (message.role === "user" || message.role === "assistant") && message.text.trim() !== "");
|
|
28
|
-
if (!spoken) return { kind: "idle", text: "No messages yet." };
|
|
29
|
-
return { kind: "idle", text: `${senderOf(spoken, handle)}: ${firstLine(spoken.text)}` };
|
|
30
|
-
}
|