@schlenkr/ragents 0.1.0 → 0.1.2
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 +10 -4
- package/apps/server/src/plugin-support/language-server/contract.ts +6 -1
- package/apps/server/src/plugin-support/language-server/tools.ts +29 -4
- package/apps/server/src/plugin-support/run-scripts.ts +4 -1
- package/apps/server/src/plugin-support/start-entries-contract.ts +4 -3
- package/apps/server/src/plugin-support/workspace-sandbox-host.ts +2 -0
- package/apps/server/src/profile-target.ts +25 -0
- package/apps/server/src/web-build.ts +82 -0
- package/apps/server/tsconfig.json +38 -0
- package/apps/web/column.html +12 -0
- package/apps/web/index.html +12 -0
- package/apps/web/src/App.tsx +0 -10
- package/apps/web/src/PluginChat.tsx +8 -8
- package/apps/web/src/PluginRegistry.tsx +0 -12
- package/apps/web/src/StartSurface.tsx +2 -2
- package/apps/web/src/ThemeSettings.tsx +1 -1
- package/apps/web/src/column/ColumnApp.tsx +83 -7
- package/apps/web/src/column/clipboard.ts +64 -0
- package/apps/web/src/column/host-contract.ts +10 -0
- package/apps/web/src/column.tsx +2 -0
- package/apps/web/src/panel/LoginForm.tsx +9 -3
- package/apps/web/src/panel/OverviewPage.tsx +187 -62
- package/apps/web/src/panel/PanelDialogs.tsx +115 -0
- package/apps/web/src/panel/PanelPage.tsx +1 -1
- package/apps/web/src/panel/SettingsPage.tsx +75 -79
- package/apps/web/src/panel/contract.ts +17 -9
- package/apps/web/src/panel/target-state.ts +27 -2
- package/apps/web/src/ui/theme.css +4 -0
- package/apps/web/tsconfig.json +41 -0
- package/apps/web/vite.config.ts +140 -0
- package/package.json +6 -2
- package/packages/ragents/src/plugin-host.ts +5 -2
- package/packages/ragents/src/plugin-types.ts +3 -2
- package/packages/workspace-executor/src/context.ts +4 -0
- package/packages/workspace-executor/src/executor.ts +9 -2
- package/packages/workspace-executor/src/index.ts +2 -0
- package/packages/workspace-executor/src/language-server/adapters/fsharp.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/roslyn.ts +1 -0
- package/packages/workspace-executor/src/language-server/adapters/typescript.ts +34 -6
- package/packages/workspace-executor/src/language-server/contract.ts +7 -3
- package/packages/workspace-executor/src/language-server/host.ts +275 -138
- package/plugins/ragents.actor-programs/contract.ts +1 -1
- package/plugins/ragents.actor-programs/prompt.hbs +4 -4
- package/plugins/ragents.actor-programs/server/app-project.ts +0 -2
- package/plugins/ragents.actor-programs/server/runtime.ts +1 -1
- package/plugins/ragents.actor-programs/web/AppsPanel.tsx +3 -5
- package/plugins/ragents.actor-programs/web/EmbeddedApps.tsx +7 -39
- package/plugins/ragents.actor-programs/web/api.ts +1 -10
- package/plugins/ragents.lsp-roslyn/web/language-server-plugin.tsx +73 -44
- package/plugins/ragents.orchestration/canvas.hbs +28 -44
- package/plugins/ragents.orchestration/contract.ts +17 -287
- package/plugins/ragents.orchestration/server/canvas-tool.ts +18 -123
- package/plugins/ragents.orchestration/tiled-layout.ts +12 -4
- package/plugins/ragents.orchestration/web/ActorCanvasControls.tsx +23 -47
- package/plugins/ragents.orchestration/web/ActorChatPreview.tsx +3 -9
- package/plugins/ragents.orchestration/web/ActorShortcut.tsx +3 -5
- package/plugins/ragents.orchestration/web/ActorTile.tsx +3 -2
- package/plugins/ragents.orchestration/web/CanvasTiles.tsx +3 -3
- package/plugins/ragents.orchestration/web/Center.tsx +28 -463
- package/plugins/ragents.orchestration/web/MaterialBody.tsx +6 -87
- package/plugins/ragents.orchestration/web/TiledCanvas.tsx +2 -3
- package/plugins/ragents.orchestration/web/canvas-view-settings.ts +7 -13
- package/plugins/ragents.orchestration/web/column/RunColumn.tsx +44 -19
- package/plugins/ragents.orchestration/web/column/column-state.ts +13 -4
- package/plugins/ragents.orchestration/web/constants.ts +0 -2
- package/plugins/ragents.orchestration/web/index.tsx +0 -6
- package/plugins/ragents.orchestration/web/tile-docking.ts +7 -1
- package/plugins/ragents.orchestration/web/tiled-view-settings.ts +7 -9
- package/plugins/ragents.overseer/server/coordinator-prompt.ts +1 -1
- package/plugins/ragents.profile-distribution/server/web.ts +3 -27
- 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/actors/balcony-app/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/src/server.ts +1 -1
- package/plugins/ragents.reference/run-scripts/balcony-wizard/tests/program.test.ts +2 -2
- package/plugins/ragents.reference/run-scripts/conversation-circle/RUN.md +3 -4
- package/plugins/ragents.reference/run-scripts/conversation-circle/src/server.ts +9 -15
- package/plugins/ragents.reference/run-scripts/conversation-circle/tests/program.test.ts +5 -3
- package/plugins/ragents.reference/run-scripts/learning-afternoon/package.json +1 -1
- package/plugins/ragents.reference/run-scripts/learning-afternoon/src/server.ts +14 -4
- package/plugins/ragents.reference/run-scripts/word-game/src/server.ts +1 -1
- package/plugins/ragents.reference/skills/210-moderator-handover/SKILL.md +1 -1
- package/plugins/ragents.reference/walkthroughs.ts +14 -14
- package/plugins/ragents.workspace/client/workspace-client.ts +4 -1
- package/scripts/agent/agent-cli.ts +9 -24
- package/scripts/package/build-package.ts +18 -5
- package/scripts/package/publish-package.ts +24 -10
- package/scripts/package/ragents.mjs +3 -2
- package/scripts/remote/connect.ts +16 -5
- package/scripts/remote/start.ts +33 -15
- package/scripts/workspace-client/run-workspace-client.ts +3 -1
- package/apps/web/src/MaterialSettings.tsx +0 -40
- package/apps/web/src/material-settings.ts +0 -104
- package/plugins/ragents.orchestration/web/ActorNet.tsx +0 -607
- package/plugins/ragents.orchestration/web/CardSizeSettings.tsx +0 -42
- package/plugins/ragents.orchestration/web/ScriptActorCard.tsx +0 -56
- package/plugins/ragents.orchestration/web/ZoomSettings.tsx +0 -38
- package/plugins/ragents.orchestration/web/canvas-layout-storage.ts +0 -39
- package/plugins/ragents.orchestration/web/canvas-layout.ts +0 -326
- package/plugins/ragents.orchestration/web/canvas-scene.ts +0 -311
- package/plugins/ragents.orchestration/web/card-size-settings.ts +0 -36
- package/plugins/ragents.orchestration/web/keyboard-navigation.ts +0 -55
- package/plugins/ragents.orchestration/web/material-layout.ts +0 -14
- package/plugins/ragents.orchestration/web/useCanvasMeasurements.ts +0 -78
- package/plugins/ragents.orchestration/web/zoom-settings.ts +0 -28
- package/plugins/ragents.reference/skills/120-canvas-conversation-circle/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/130-canvas-pipeline-with-approval/SKILL.md +0 -12
- package/plugins/ragents.reference/skills/140-canvas-team-tree-and-topic-grid/SKILL.md +0 -12
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
canvasEntityOf,
|
|
3
|
-
canvasShapeSize,
|
|
4
|
-
isCanvasGroupNode,
|
|
5
|
-
type CanvasArrow,
|
|
6
|
-
type CanvasGroupNode,
|
|
7
|
-
type CanvasLayout,
|
|
8
|
-
type CanvasLineStyle,
|
|
9
|
-
type CanvasShape,
|
|
10
|
-
} from "../contract";
|
|
11
|
-
import type { BoundaryShape, GroupLayout, SceneGroup, SceneLeaf, SceneNode } from "./canvas-layout";
|
|
12
|
-
import type { RunActor, RunView } from "./run-view";
|
|
13
|
-
import { ACTOR_PROGRAMS_STATE_ID, type ActorProgramState } from "@aicontainer/plugins/ragents.actor-programs/contract";
|
|
14
|
-
|
|
15
|
-
export const DEFAULT_GAP = 44;
|
|
16
|
-
export const RANK_GAP = 86;
|
|
17
|
-
export const TOP_LEVEL_GAP = 56;
|
|
18
|
-
export const ANCHORED_GAP = 20;
|
|
19
|
-
export const AUTO_APP_MAX_COLUMNS = 3;
|
|
20
|
-
export const UNPLACED_LABEL = "Automatisch angeordnet";
|
|
21
|
-
|
|
22
|
-
export interface SceneApp {
|
|
23
|
-
key: string;
|
|
24
|
-
id: string;
|
|
25
|
-
width: number;
|
|
26
|
-
height: number;
|
|
27
|
-
anchorActorId?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type SceneLeafInfo =
|
|
31
|
-
| { type: "actor"; actor: RunActor }
|
|
32
|
-
| { type: "app"; app: SceneApp }
|
|
33
|
-
| { type: "shape"; shape: CanvasShape }
|
|
34
|
-
| { type: "missing"; reference: string };
|
|
35
|
-
|
|
36
|
-
export interface SceneLine {
|
|
37
|
-
key: string;
|
|
38
|
-
fromKey: string;
|
|
39
|
-
toKey: string;
|
|
40
|
-
label?: string;
|
|
41
|
-
arrow: CanvasArrow;
|
|
42
|
-
style: CanvasLineStyle;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface Scene {
|
|
46
|
-
root: SceneGroup;
|
|
47
|
-
leaves: Map<string, SceneLeafInfo>;
|
|
48
|
-
lines: SceneLine[];
|
|
49
|
-
notices: string[];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface SceneInput {
|
|
53
|
-
view: RunView;
|
|
54
|
-
layout?: CanvasLayout;
|
|
55
|
-
layoutProblem?: string;
|
|
56
|
-
actorSize: (actor: RunActor) => { width: number; height: number };
|
|
57
|
-
apps: readonly SceneApp[];
|
|
58
|
-
hiddenAppIds?: readonly string[];
|
|
59
|
-
hiddenActorIds?: readonly string[];
|
|
60
|
-
showConnections?: boolean;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const ACTOR_KIND_ORDER: Record<string, number> = { human: 0, agent: 1, script: 2 };
|
|
64
|
-
|
|
65
|
-
export const actorSort = (left: RunActor, right: RunActor) =>
|
|
66
|
-
(ACTOR_KIND_ORDER[left.kind] ?? 3) - (ACTOR_KIND_ORDER[right.kind] ?? 3)
|
|
67
|
-
|| String(left.createdAt ?? "").localeCompare(String(right.createdAt ?? ""))
|
|
68
|
-
|| left.id.localeCompare(right.id);
|
|
69
|
-
|
|
70
|
-
export const boundaryOf = (info: SceneLeafInfo): BoundaryShape =>
|
|
71
|
-
info.type === "shape" ? (info.shape.kind === "circle" ? "circle" : "diamond") : "rect";
|
|
72
|
-
|
|
73
|
-
const missingSize = (reference: string) => ({
|
|
74
|
-
width: Math.min(360, Math.max(160, 60 + reference.length * 8)),
|
|
75
|
-
height: 36,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const isStopped = (actor: RunActor) => actor.lifecycle?.kind === "stopped";
|
|
79
|
-
|
|
80
|
-
/** Mirrors the server: an active actor wins the handle, otherwise the latest one. */
|
|
81
|
-
const actorsByHandle = (view: RunView): Map<string, RunActor> => {
|
|
82
|
-
const byHandle = new Map<string, RunActor>();
|
|
83
|
-
for (const actor of view.actors) {
|
|
84
|
-
if (actor.kind === "human") continue;
|
|
85
|
-
const handle = actor.handle.toLowerCase();
|
|
86
|
-
const current = byHandle.get(handle);
|
|
87
|
-
if (!current || isStopped(current)) byHandle.set(handle, actor);
|
|
88
|
-
}
|
|
89
|
-
return byHandle;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const groupLayoutOf = (node: CanvasGroupNode): GroupLayout => {
|
|
93
|
-
const gap = node.gap ?? DEFAULT_GAP;
|
|
94
|
-
switch (node.group) {
|
|
95
|
-
case "h": return { type: "stack", axis: "h", gap };
|
|
96
|
-
case "v": return { type: "stack", axis: "v", gap };
|
|
97
|
-
case "wrap-h": return { type: "wrap", axis: "h", limit: node.width, gap };
|
|
98
|
-
case "wrap-v": return { type: "wrap", axis: "v", limit: node.height, gap };
|
|
99
|
-
case "grid": return { type: "grid", columns: node.columns, gap };
|
|
100
|
-
case "circle": return { type: "circle", gap };
|
|
101
|
-
case "tree": return { type: "tree", direction: node.direction, rankGap: RANK_GAP, siblingGap: gap };
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const buildScene = ({ view, layout, layoutProblem, actorSize, apps, hiddenAppIds = [], hiddenActorIds = [], showConnections = true }: SceneInput): Scene => {
|
|
106
|
-
const hiddenActors = new Set(hiddenActorIds);
|
|
107
|
-
const hiddenAppReferences = new Set(hiddenAppIds.map((id) => `app:${id}`));
|
|
108
|
-
const availableAppIds = new Set(apps.map((app) => app.id));
|
|
109
|
-
const stoppedActorIds = new Set(view.actors.filter(isStopped).map((actor) => actor.id));
|
|
110
|
-
for (const entry of view.pluginStates) {
|
|
111
|
-
if (entry.pluginId !== ACTOR_PROGRAMS_STATE_ID || entry.scope.kind !== "actor" || !stoppedActorIds.has(entry.scope.actorId)) continue;
|
|
112
|
-
const { program } = entry.state as ActorProgramState;
|
|
113
|
-
for (const app of program?.views ?? []) {
|
|
114
|
-
if (!availableAppIds.has(app.id)) hiddenAppReferences.add(`app:${app.id}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const notices: string[] = layoutProblem ? [`Layout unlesbar: ${layoutProblem}`] : [];
|
|
118
|
-
const leaves = new Map<string, SceneLeafInfo>();
|
|
119
|
-
const cards = view.actors.filter((actor) => actor.kind !== "human").sort(actorSort);
|
|
120
|
-
const byHandle = actorsByHandle(view);
|
|
121
|
-
const shapesById = new Map((layout?.shapes ?? []).map((shape) => [shape.id, shape]));
|
|
122
|
-
const appsById = new Map(apps.map((app) => [app.id, app]));
|
|
123
|
-
const placedActors = new Set<string>();
|
|
124
|
-
const placedApps = new Set<string>();
|
|
125
|
-
const entityKeys = new Map<string, string>();
|
|
126
|
-
|
|
127
|
-
const actorLeaf = (actor: RunActor): SceneLeaf => {
|
|
128
|
-
const key = `actor:${actor.id}`;
|
|
129
|
-
placedActors.add(actor.id);
|
|
130
|
-
leaves.set(key, { type: "actor", actor });
|
|
131
|
-
return { kind: "leaf", key, ...actorSize(actor) };
|
|
132
|
-
};
|
|
133
|
-
const appLeaf = (app: SceneApp): SceneLeaf => {
|
|
134
|
-
placedApps.add(app.key);
|
|
135
|
-
leaves.set(app.key, { type: "app", app });
|
|
136
|
-
entityKeys.set(`app:${app.id}`, app.key);
|
|
137
|
-
return { kind: "leaf", key: app.key, width: app.width, height: app.height };
|
|
138
|
-
};
|
|
139
|
-
const shapeLeaf = (shape: CanvasShape): SceneLeaf => {
|
|
140
|
-
const key = `shape:${shape.id}`;
|
|
141
|
-
leaves.set(key, { type: "shape", shape });
|
|
142
|
-
entityKeys.set(key, key);
|
|
143
|
-
return { kind: "leaf", key, ...canvasShapeSize(shape) };
|
|
144
|
-
};
|
|
145
|
-
const missingLeaf = (reference: string, reason: string): SceneLeaf => {
|
|
146
|
-
const key = `missing:${reference}`;
|
|
147
|
-
leaves.set(key, { type: "missing", reference });
|
|
148
|
-
notices.push(reason);
|
|
149
|
-
return { kind: "leaf", key, ...missingSize(reference) };
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// Actors and apps named as single items are spoken for before any tree claims its lineage.
|
|
153
|
-
const claimed = new Set<string>();
|
|
154
|
-
const claimedApps = new Set<string>();
|
|
155
|
-
for (const node of layout?.nodes ?? []) {
|
|
156
|
-
if (isCanvasGroupNode(node)) continue;
|
|
157
|
-
const entity = canvasEntityOf(node.entity);
|
|
158
|
-
if (entity.kind === "actor") {
|
|
159
|
-
const actor = byHandle.get(entity.handle);
|
|
160
|
-
if (actor) claimed.add(actor.id);
|
|
161
|
-
} else if (entity.kind === "app") {
|
|
162
|
-
const app = appsById.get(entity.id);
|
|
163
|
-
if (app) claimedApps.add(app.key);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const appGroup = (key: string, members: readonly SceneApp[]): SceneGroup => ({
|
|
168
|
-
kind: "group", key,
|
|
169
|
-
layout: { type: "grid", columns: Math.min(AUTO_APP_MAX_COLUMNS, Math.ceil(Math.sqrt(members.length))), gap: DEFAULT_GAP },
|
|
170
|
-
children: members.map(appLeaf),
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Apps keep their owner's position even when the owner's card is hidden.
|
|
174
|
-
const actorWithApps = (actor: RunActor): SceneNode | undefined => {
|
|
175
|
-
placedActors.add(actor.id);
|
|
176
|
-
const leaf = hiddenActors.has(actor.id) ? undefined : actorLeaf(actor);
|
|
177
|
-
const anchored = apps.filter((app) =>
|
|
178
|
-
app.anchorActorId === actor.id && !claimedApps.has(app.key) && !placedApps.has(app.key));
|
|
179
|
-
if (anchored.length === 0) return leaf;
|
|
180
|
-
const group = appGroup(`anchored-apps:${actor.id}`, anchored);
|
|
181
|
-
return leaf
|
|
182
|
-
? { kind: "group", key: `anchored:${actor.id}`, layout: { type: "stack", axis: "v", gap: ANCHORED_GAP }, children: [leaf, group] }
|
|
183
|
-
: group;
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const lineageNode = (actor: RunActor, direction: "right" | "down", siblingGap: number): SceneGroup | undefined => {
|
|
187
|
-
const node = actorWithApps(actor);
|
|
188
|
-
const children = cards.filter((candidate) =>
|
|
189
|
-
candidate.createdBy === actor.id && !claimed.has(candidate.id) && !placedActors.has(candidate.id));
|
|
190
|
-
const descendants = children.flatMap((child) => {
|
|
191
|
-
const branch = lineageNode(child, direction, siblingGap);
|
|
192
|
-
return branch ? [branch] : [];
|
|
193
|
-
});
|
|
194
|
-
if (!node && descendants.length === 0) return undefined;
|
|
195
|
-
return {
|
|
196
|
-
kind: "group",
|
|
197
|
-
key: `lineage:${actor.id}`,
|
|
198
|
-
layout: node ? { type: "tree", direction, rankGap: RANK_GAP, siblingGap }
|
|
199
|
-
: { type: "stack", axis: direction === "right" ? "v" : "h", gap: siblingGap },
|
|
200
|
-
children: node ? [node, ...descendants] : descendants,
|
|
201
|
-
};
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const itemNode = (reference: string): SceneNode | undefined => {
|
|
205
|
-
const entity = canvasEntityOf(reference);
|
|
206
|
-
if (entity.kind === "actor") {
|
|
207
|
-
const actor = byHandle.get(entity.handle);
|
|
208
|
-
if (!actor) return missingLeaf(reference, `${reference} ist kein Actor dieses Laufs`);
|
|
209
|
-
if (placedActors.has(actor.id)) return missingLeaf(reference, `${reference} ist schon platziert`);
|
|
210
|
-
return actorWithApps(actor);
|
|
211
|
-
}
|
|
212
|
-
if (entity.kind === "shape") {
|
|
213
|
-
const shape = shapesById.get(entity.id);
|
|
214
|
-
return shape ? shapeLeaf(shape) : missingLeaf(reference, `${reference} ist keine definierte Form`);
|
|
215
|
-
}
|
|
216
|
-
const app = appsById.get(entity.id);
|
|
217
|
-
if (!app) return missingLeaf(reference, `${reference} ist keine App auf der Fläche`);
|
|
218
|
-
if (placedApps.has(app.key)) return missingLeaf(reference, `${reference} ist schon platziert`);
|
|
219
|
-
return appLeaf(app);
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const treeChildren = (node: CanvasGroupNode & { group: "tree" }, group: SceneGroup): SceneNode[] => {
|
|
223
|
-
const entity = canvasEntityOf(node.root);
|
|
224
|
-
const actor = entity.kind === "actor" ? byHandle.get(entity.handle) : undefined;
|
|
225
|
-
if (!actor) return [missingLeaf(node.root, `${node.root} ist kein Actor dieses Laufs; der Baum ${node.id} bleibt leer`)];
|
|
226
|
-
if (placedActors.has(actor.id)) return [missingLeaf(node.root, `${node.root} ist schon platziert; der Baum ${node.id} bleibt leer`)];
|
|
227
|
-
const tree = lineageNode(actor, node.direction, node.gap ?? DEFAULT_GAP);
|
|
228
|
-
if (tree) group.layout = tree.layout;
|
|
229
|
-
return tree?.children ?? [];
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const groups = new Map<string, SceneGroup>();
|
|
233
|
-
const topLevel: SceneNode[] = [];
|
|
234
|
-
for (const node of layout?.nodes ?? []) {
|
|
235
|
-
if (!isCanvasGroupNode(node)) continue;
|
|
236
|
-
groups.set(node.id, {
|
|
237
|
-
kind: "group",
|
|
238
|
-
key: `group:${node.id}`,
|
|
239
|
-
layout: groupLayoutOf(node),
|
|
240
|
-
children: [],
|
|
241
|
-
...(node.label || node.frame ? { frame: { label: node.label, outline: node.frame } } : {}),
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
for (const node of layout?.nodes ?? []) {
|
|
245
|
-
const target = node.parent ? groups.get(node.parent)?.children : topLevel;
|
|
246
|
-
if (!target) continue;
|
|
247
|
-
if (isCanvasGroupNode(node)) {
|
|
248
|
-
const group = groups.get(node.id)!;
|
|
249
|
-
if (node.group === "tree") group.children = treeChildren(node, group);
|
|
250
|
-
target.push(group);
|
|
251
|
-
} else {
|
|
252
|
-
if (!hiddenAppReferences.has(node.entity)) {
|
|
253
|
-
const item = itemNode(node.entity);
|
|
254
|
-
if (item) target.push(item);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const unplaced = cards.filter((actor) => !placedActors.has(actor.id));
|
|
260
|
-
const unplacedIds = new Set(unplaced.map((actor) => actor.id));
|
|
261
|
-
const roots = unplaced.filter((actor) => !actor.createdBy || !unplacedIds.has(actor.createdBy));
|
|
262
|
-
const autoChildren: SceneNode[] = roots
|
|
263
|
-
.filter((actor) => !placedActors.has(actor.id))
|
|
264
|
-
.flatMap((actor) => {
|
|
265
|
-
const branch = lineageNode(actor, "right", DEFAULT_GAP);
|
|
266
|
-
return branch ? [branch] : [];
|
|
267
|
-
});
|
|
268
|
-
const unplacedApps = apps.filter((app) => !placedApps.has(app.key));
|
|
269
|
-
if (unplacedApps.length > 0) {
|
|
270
|
-
autoChildren.push(appGroup("auto:apps", unplacedApps));
|
|
271
|
-
}
|
|
272
|
-
const removeEmpty = (nodes: SceneNode[]): SceneNode[] => nodes.flatMap<SceneNode>((node) => {
|
|
273
|
-
if (node.kind === "leaf") return [node];
|
|
274
|
-
const children = removeEmpty(node.children);
|
|
275
|
-
return children.length > 0 ? [{ ...node, children }] : [];
|
|
276
|
-
});
|
|
277
|
-
const children: SceneNode[] = hiddenActors.size > 0 || hiddenAppReferences.size > 0 ? removeEmpty(topLevel) : [...topLevel];
|
|
278
|
-
if (autoChildren.length > 0) {
|
|
279
|
-
children.push(children.length > 0
|
|
280
|
-
? { kind: "group", key: "auto", layout: { type: "stack", axis: "v", gap: DEFAULT_GAP }, frame: { label: UNPLACED_LABEL, outline: false }, children: autoChildren }
|
|
281
|
-
: { kind: "group", key: "auto", layout: { type: "stack", axis: "v", gap: DEFAULT_GAP }, children: autoChildren });
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
for (const [handle, actor] of byHandle) {
|
|
285
|
-
if (leaves.has(`actor:${actor.id}`)) entityKeys.set(`@${handle}`, `actor:${actor.id}`);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const lines = (layout?.lines ?? []).flatMap((line, index): SceneLine[] => {
|
|
289
|
-
if (!showConnections) return [];
|
|
290
|
-
if (hiddenAppReferences.has(line.from) || hiddenAppReferences.has(line.to)) return [];
|
|
291
|
-
if ([line.from, line.to].some((reference) => {
|
|
292
|
-
const entity = canvasEntityOf(reference);
|
|
293
|
-
const actor = entity.kind === "actor" ? byHandle.get(entity.handle) : undefined;
|
|
294
|
-
return actor && hiddenActors.has(actor.id);
|
|
295
|
-
})) return [];
|
|
296
|
-
const fromKey = entityKeys.get(line.from);
|
|
297
|
-
const toKey = entityKeys.get(line.to);
|
|
298
|
-
if (!fromKey || !toKey) {
|
|
299
|
-
notices.push(`Linie ${line.from} nach ${line.to}: ${fromKey ? line.to : line.from} ist nicht auf der Fläche`);
|
|
300
|
-
return [];
|
|
301
|
-
}
|
|
302
|
-
return [{ key: `line:${index}`, fromKey, toKey, label: line.label, arrow: line.arrow, style: line.style }];
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
return {
|
|
306
|
-
root: { kind: "group", key: "root", layout: { type: "stack", axis: "v", gap: TOP_LEVEL_GAP }, children },
|
|
307
|
-
leaves,
|
|
308
|
-
lines,
|
|
309
|
-
notices: [...new Set(notices)],
|
|
310
|
-
};
|
|
311
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { createLocalStorageSetting } from "@aicontainer/web/lib/local-storage-setting";
|
|
2
|
-
|
|
3
|
-
export interface ActorCardSize { width: number; height: number }
|
|
4
|
-
|
|
5
|
-
export const DEFAULT_ACTOR_CARD_SIZE: ActorCardSize = { width: 720, height: 520 };
|
|
6
|
-
export const ACTOR_CARD_SIZE_LIMITS = { minWidth: 240, maxWidth: 2880, minHeight: 260, maxHeight: 2700 };
|
|
7
|
-
export const ACTOR_CARD_SIZE_STORAGE_KEY = "ragents.orchestration.actor-card-size";
|
|
8
|
-
|
|
9
|
-
export function parseActorCardSize(raw: string | null): ActorCardSize {
|
|
10
|
-
if (raw === null) return DEFAULT_ACTOR_CARD_SIZE;
|
|
11
|
-
const value: unknown = JSON.parse(raw);
|
|
12
|
-
if (!value || typeof value !== "object" || !("width" in value) || !("height" in value)
|
|
13
|
-
|| !Number.isInteger(value.width) || !Number.isInteger(value.height)
|
|
14
|
-
|| typeof value.width !== "number" || typeof value.height !== "number"
|
|
15
|
-
|| value.width < ACTOR_CARD_SIZE_LIMITS.minWidth || value.width > ACTOR_CARD_SIZE_LIMITS.maxWidth
|
|
16
|
-
|| value.height < 180 || value.height > ACTOR_CARD_SIZE_LIMITS.maxHeight) {
|
|
17
|
-
throw new Error("Die Standardgröße der LLM-Karten ist ungültig. Breite: 240 bis 2880, Höhe: 260 bis 2700 Pixel.");
|
|
18
|
-
}
|
|
19
|
-
return { width: value.width, height: Math.max(ACTOR_CARD_SIZE_LIMITS.minHeight, value.height) };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const setting = createLocalStorageSetting({
|
|
23
|
-
changeEvent: "ragents-actor-card-size-change",
|
|
24
|
-
matchesKey: (key) => key === ACTOR_CARD_SIZE_STORAGE_KEY,
|
|
25
|
-
parse: parseActorCardSize,
|
|
26
|
-
serialize: JSON.stringify,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
export function useActorCardSize(): ActorCardSize {
|
|
30
|
-
return setting.useValue(ACTOR_CARD_SIZE_STORAGE_KEY);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function saveActorCardSize(size: ActorCardSize) {
|
|
34
|
-
if (size.height < ACTOR_CARD_SIZE_LIMITS.minHeight) throw new Error("LLM-Karten benötigen mindestens 260 Pixel Höhe für Verlauf und Eingabe.");
|
|
35
|
-
setting.save(ACTOR_CARD_SIZE_STORAGE_KEY, size);
|
|
36
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import type { CanvasCamera } from "./canvas-layout-storage";
|
|
2
|
-
|
|
3
|
-
const DURATION = 300;
|
|
4
|
-
|
|
5
|
-
export interface CanvasNavigationBox { id: string; left: number; top: number; right: number; bottom: number }
|
|
6
|
-
|
|
7
|
-
export const nextCanvasElement = (
|
|
8
|
-
elements: readonly CanvasNavigationBox[],
|
|
9
|
-
origin: Omit<CanvasNavigationBox, "id">,
|
|
10
|
-
key: string,
|
|
11
|
-
currentId?: string,
|
|
12
|
-
): CanvasNavigationBox | undefined => {
|
|
13
|
-
if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(key)) return undefined;
|
|
14
|
-
const horizontal = key === "ArrowLeft" || key === "ArrowRight";
|
|
15
|
-
const direction = key === "ArrowLeft" || key === "ArrowUp" ? -1 : 1;
|
|
16
|
-
const x = (origin.left + origin.right) / 2;
|
|
17
|
-
const y = (origin.top + origin.bottom) / 2;
|
|
18
|
-
return elements.filter((box) => box.id !== currentId).map((box) => {
|
|
19
|
-
const dx = (box.left + box.right) / 2 - x;
|
|
20
|
-
const dy = (box.top + box.bottom) / 2 - y;
|
|
21
|
-
const forward = (horizontal ? dx : dy) * direction;
|
|
22
|
-
const across = horizontal ? dy : dx;
|
|
23
|
-
const aligned = horizontal ? box.bottom >= origin.top && box.top <= origin.bottom
|
|
24
|
-
: box.right >= origin.left && box.left <= origin.right;
|
|
25
|
-
return { box, forward, aligned, distance: Math.hypot(forward, across * 2) };
|
|
26
|
-
}).filter((entry) => entry.forward > 1)
|
|
27
|
-
.sort((a, b) => Number(b.aligned) - Number(a.aligned) || a.distance - b.distance || a.box.id.localeCompare(b.box.id))[0]?.box;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const createKeyboardNavigation = (read: () => CanvasCamera, apply: (camera: CanvasCamera) => void) => {
|
|
31
|
-
let frame: number | undefined;
|
|
32
|
-
let startedAt = -Infinity;
|
|
33
|
-
const cancel = () => {
|
|
34
|
-
if (frame !== undefined) cancelAnimationFrame(frame);
|
|
35
|
-
frame = undefined;
|
|
36
|
-
};
|
|
37
|
-
const moveTo = (to: CanvasCamera, repeat: boolean, reducedMotion: boolean): boolean => {
|
|
38
|
-
const now = performance.now();
|
|
39
|
-
if (repeat && (frame !== undefined || now - startedAt < DURATION)) return false;
|
|
40
|
-
const from = read();
|
|
41
|
-
cancel();
|
|
42
|
-
startedAt = now;
|
|
43
|
-
if (reducedMotion) { apply(to); return true; }
|
|
44
|
-
const step = (time: number) => {
|
|
45
|
-
const progress = Math.min(1, Math.max(0, (time - startedAt) / DURATION));
|
|
46
|
-
const eased = progress * progress * (3 - 2 * progress);
|
|
47
|
-
apply({ x: from.x + (to.x - from.x) * eased, y: from.y + (to.y - from.y) * eased, zoom: from.zoom });
|
|
48
|
-
if (progress < 1) frame = requestAnimationFrame(step);
|
|
49
|
-
else frame = undefined;
|
|
50
|
-
};
|
|
51
|
-
frame = requestAnimationFrame(step);
|
|
52
|
-
return true;
|
|
53
|
-
};
|
|
54
|
-
return { moveTo, cancel };
|
|
55
|
-
};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { SceneNode } from "./canvas-layout";
|
|
2
|
-
|
|
3
|
-
export function withMaterialSpacing(node: SceneNode, depth: number): SceneNode {
|
|
4
|
-
if (node.kind === "leaf") return node;
|
|
5
|
-
const horizontal = depth * .5 + 32, vertical = depth * .7 + 32;
|
|
6
|
-
const layout = node.layout;
|
|
7
|
-
const next = layout.type === "tree" ? {
|
|
8
|
-
...layout,
|
|
9
|
-
rankGap: Math.max(layout.rankGap, layout.direction === "right" ? horizontal : vertical),
|
|
10
|
-
siblingGap: Math.max(layout.siblingGap, layout.direction === "right" ? vertical : horizontal),
|
|
11
|
-
} : { ...layout, gap: Math.max(layout.gap,
|
|
12
|
-
layout.type === "stack" ? layout.axis === "h" ? horizontal : vertical : Math.max(horizontal, vertical)) };
|
|
13
|
-
return { ...node, layout: next, children: node.children.map((child) => withMaterialSpacing(child, depth)) };
|
|
14
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { useCallback, useLayoutEffect, useRef, useState, type RefCallback } from "react";
|
|
2
|
-
|
|
3
|
-
export interface CanvasSize {
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
type MeasuredElement = HTMLElement | SVGGraphicsElement;
|
|
9
|
-
|
|
10
|
-
export function useCanvasMeasurements() {
|
|
11
|
-
const [sizes, setSizes] = useState<ReadonlyMap<string, CanvasSize>>(new Map());
|
|
12
|
-
const elements = useRef(new Map<string, MeasuredElement>());
|
|
13
|
-
const callbacks = useRef(new Map<string, RefCallback<MeasuredElement>>());
|
|
14
|
-
const observer = useRef<ResizeObserver | null>(null);
|
|
15
|
-
const frame = useRef<number | null>(null);
|
|
16
|
-
|
|
17
|
-
const measure = useCallback(() => {
|
|
18
|
-
frame.current = null;
|
|
19
|
-
const measured = new Map<string, CanvasSize>();
|
|
20
|
-
for (const [key, element] of elements.current) {
|
|
21
|
-
// Layout dimensions remain in world coordinates, independent of camera transforms.
|
|
22
|
-
const size = element instanceof SVGGraphicsElement ? element.getBBox() : { width: element.offsetWidth, height: element.offsetHeight };
|
|
23
|
-
const { width, height } = size;
|
|
24
|
-
if (element.isConnected && width > 0 && height > 0) measured.set(key, { width, height });
|
|
25
|
-
}
|
|
26
|
-
const keys = new Set(elements.current.keys());
|
|
27
|
-
for (const key of callbacks.current.keys()) if (!keys.has(key)) callbacks.current.delete(key);
|
|
28
|
-
setSizes((previous) => {
|
|
29
|
-
const next = new Map<string, CanvasSize>();
|
|
30
|
-
for (const key of keys) {
|
|
31
|
-
const size = measured.get(key) ?? previous.get(key);
|
|
32
|
-
if (size) next.set(key, size);
|
|
33
|
-
}
|
|
34
|
-
const changed = next.size !== previous.size || [...next].some(([key, size]) => {
|
|
35
|
-
const before = previous.get(key);
|
|
36
|
-
return !before || before.width !== size.width || before.height !== size.height;
|
|
37
|
-
});
|
|
38
|
-
return changed ? next : previous;
|
|
39
|
-
});
|
|
40
|
-
}, []);
|
|
41
|
-
|
|
42
|
-
const schedule = useCallback(() => {
|
|
43
|
-
if (observer.current && frame.current === null) frame.current = requestAnimationFrame(measure);
|
|
44
|
-
}, [measure]);
|
|
45
|
-
|
|
46
|
-
const refFor = useCallback((key: string): RefCallback<MeasuredElement> => {
|
|
47
|
-
let callback = callbacks.current.get(key);
|
|
48
|
-
if (!callback) {
|
|
49
|
-
callback = (element) => {
|
|
50
|
-
const previous = elements.current.get(key);
|
|
51
|
-
if (previous === element) return;
|
|
52
|
-
if (previous) observer.current?.unobserve(previous);
|
|
53
|
-
if (element) {
|
|
54
|
-
elements.current.set(key, element);
|
|
55
|
-
observer.current?.observe(element, { box: "border-box" });
|
|
56
|
-
} else elements.current.delete(key);
|
|
57
|
-
schedule();
|
|
58
|
-
};
|
|
59
|
-
callbacks.current.set(key, callback);
|
|
60
|
-
}
|
|
61
|
-
return callback;
|
|
62
|
-
}, [schedule]);
|
|
63
|
-
|
|
64
|
-
useLayoutEffect(() => {
|
|
65
|
-
const active = new ResizeObserver(schedule);
|
|
66
|
-
observer.current = active;
|
|
67
|
-
for (const element of elements.current.values()) active.observe(element, { box: "border-box" });
|
|
68
|
-
measure();
|
|
69
|
-
return () => {
|
|
70
|
-
active.disconnect();
|
|
71
|
-
observer.current = null;
|
|
72
|
-
if (frame.current !== null) cancelAnimationFrame(frame.current);
|
|
73
|
-
frame.current = null;
|
|
74
|
-
};
|
|
75
|
-
}, [measure, schedule]);
|
|
76
|
-
|
|
77
|
-
return { sizes, refFor };
|
|
78
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { createLocalStorageSetting } from "@aicontainer/web/lib/local-storage-setting";
|
|
2
|
-
|
|
3
|
-
export const DEFAULT_PINCH_ZOOM_SENSITIVITY = 2;
|
|
4
|
-
export const PINCH_ZOOM_STORAGE_KEY = "ragents.orchestration.pinch-zoom-sensitivity";
|
|
5
|
-
|
|
6
|
-
export function parsePinchZoomSensitivity(raw: string | null): number {
|
|
7
|
-
if (raw === null) return DEFAULT_PINCH_ZOOM_SENSITIVITY;
|
|
8
|
-
const value = Number(raw);
|
|
9
|
-
if (!Number.isFinite(value) || value < 0.1 || value > 10) {
|
|
10
|
-
throw new Error("Die Pinch-Zoom-Empfindlichkeit muss zwischen 0,1 und 10 liegen.");
|
|
11
|
-
}
|
|
12
|
-
return value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const setting = createLocalStorageSetting({
|
|
16
|
-
changeEvent: "ragents-pinch-zoom-change",
|
|
17
|
-
matchesKey: (key) => key === PINCH_ZOOM_STORAGE_KEY,
|
|
18
|
-
parse: parsePinchZoomSensitivity,
|
|
19
|
-
serialize: String,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export function usePinchZoomSensitivity(): number {
|
|
23
|
-
return setting.useValue(PINCH_ZOOM_STORAGE_KEY);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function savePinchZoomSensitivity(value: number) {
|
|
27
|
-
setting.save(PINCH_ZOOM_STORAGE_KEY, value);
|
|
28
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: 120-canvas-conversation-circle
|
|
3
|
-
start: true
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
title: "Eine Gesprächsrunde auf der Fläche anordnen"
|
|
6
|
-
description: "Zeigt beim Aufbau einer Gesprächsrunde, wie Actors, ein Thema und die Übergaben auf der Arbeitsfläche angeordnet werden."
|
|
7
|
-
category: "Zusammenarbeit"
|
|
8
|
-
order: 120
|
|
9
|
-
tags: "Anwendungsfall, Konzeptdemo, Agententeams, Canvas-Layout"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Ich hätte gern drei KIs mit eigenen Namen und Charakteren, die sich reihum zu einem Thema Deiner Wahl unterhalten, zwei Runden lang, jede mit einem kurzen Satz. Bevor es losgeht, will ich den Aufbau auf der Fläche neben dem Chat sehen: die drei im Kreis, das Thema als Raute darüber und Pfeile, die zeigen, wer an wen weitergibt. Am Ende fasst Du mir das Gespräch in drei Sätzen zusammen.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: 130-canvas-pipeline-with-approval
|
|
3
|
-
start: true
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
title: "Arbeitsschritte mit Freigabe verbinden"
|
|
6
|
-
description: "Zeigt drei Agenten als aufeinanderfolgende Arbeitsschritte mit beschrifteten Übergaben und einer grafisch dargestellten Freigabefrage."
|
|
7
|
-
category: "Ereignisse und Abläufe"
|
|
8
|
-
order: 130
|
|
9
|
-
tags: "Anwendungsfall, Konzeptdemo, Agententeams, Canvas-Layout"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Ich hätte gern drei KIs, die wie an einem Fließband arbeiten: die erste sammelt Stichpunkte zu einem Thema Deiner Wahl, die zweite schreibt daraus einen kurzen Absatz, die dritte poliert ihn. Auf der Fläche neben dem Chat will ich die drei in einer Reihe sehen, mit beschrifteten Pfeilen dazwischen, und am Ende eine Raute mit der Frage, ob der Text freigegeben wird. Zeig mir zum Schluss den fertigen Absatz.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: 140-canvas-team-tree-and-topic-grid
|
|
3
|
-
start: true
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
title: "Ein Team und seine Themen anordnen"
|
|
6
|
-
description: "Zeigt Hierarchien, Themengruppen und Zuständigkeiten auf der Arbeitsfläche sowie die automatische Platzierung eines zusätzlichen Beobachters."
|
|
7
|
-
category: "Zusammenarbeit"
|
|
8
|
-
order: 140
|
|
9
|
-
tags: "Konzeptdemo, Agententeams, Canvas-Layout"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Ich hätte gern einen Teamleiter, der sich drei Helfer holt und jedem ein Thema Deiner Wahl gibt; jeder Helfer liefert drei Stichpunkte. Dazu einen Beobachter, der am Ende alles in zwei Sätzen bewertet. Auf der Fläche neben dem Chat will ich das Team als Baum sehen, daneben vier Themen als Kreise in zwei Spalten und gestrichelte Linien von jedem Helfer zu seinem Thema; das vierte bleibt frei. Den Beobachter ordne nicht selbst ein.
|