diff --git a/src/app/layouts/tasks-executor/tasks-executor.css b/src/app/layouts/tasks-executor/tasks-executor.css index 510d046..bace9dd 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.css +++ b/src/app/layouts/tasks-executor/tasks-executor.css @@ -16,13 +16,58 @@ } .tasks-executor-rail-list { - flex: 1 1 auto; + flex: 1 1 0; min-height: 0; } -.tasks-executor-rail-tree { +.tasks-executor-tree-panel { + display: flex; + flex-direction: column; + gap: 8px; + flex: 1 1 0; + min-height: 0; +} + +.tasks-executor-tree-panel-collapsed { flex: 0 0 auto; - max-height: 45%; + gap: 0; +} + +.tasks-executor-tree-toggle { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 10px 12px; + border: 1px solid #dbe2ea; + border-radius: 10px; + background: #ffffff; + color: #334155; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06); +} + +.tasks-executor-tree-toggle-icon { + display: inline-flex; + align-items: center; + justify-content: center; + color: #64748b; + font-size: 14px; + line-height: 1; + transform: rotate(180deg); + transition: transform 0.15s ease; +} + +.tasks-executor-tree-toggle-icon-open { + transform: rotate(0deg); +} + +.tasks-executor-rail-tree { + flex: 1 1 auto; + min-height: 0; } .interactive-subflow-switcher { diff --git a/src/app/layouts/tasks-executor/tasks-executor.html b/src/app/layouts/tasks-executor/tasks-executor.html index c315233..061021a 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.html +++ b/src/app/layouts/tasks-executor/tasks-executor.html @@ -10,12 +10,25 @@ @if (selectedExecution(); as run) { - - +
+ + + @if (executionTreeOpen()) { + + + } +
} diff --git a/src/app/layouts/tasks-executor/tasks-executor.spec.ts b/src/app/layouts/tasks-executor/tasks-executor.spec.ts index 706828b..bacd94d 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.spec.ts +++ b/src/app/layouts/tasks-executor/tasks-executor.spec.ts @@ -105,6 +105,16 @@ describe('TasksExecutor', () => { expect(component).toBeTruthy(); }); + it('toggles the execution tree panel', () => { + expect(component.executionTreeOpen()).toBe(true); + + component.toggleExecutionTree(); + expect(component.executionTreeOpen()).toBe(false); + + component.toggleExecutionTree(); + expect(component.executionTreeOpen()).toBe(true); + }); + it('follows only container steps waiting for a child subflow', () => { const execution = { id: 'parent-1', diff --git a/src/app/layouts/tasks-executor/tasks-executor.ts b/src/app/layouts/tasks-executor/tasks-executor.ts index 3236b0c..c33c36f 100644 --- a/src/app/layouts/tasks-executor/tasks-executor.ts +++ b/src/app/layouts/tasks-executor/tasks-executor.ts @@ -91,6 +91,7 @@ export class TasksExecutor { readonly selectedExecutionId = signal(null); readonly requestedExecutionId = signal(null); + readonly executionTreeOpen = signal(true); readonly selectedExecution = computed(() => { const selectedId = this.selectedExecutionId(); @@ -240,6 +241,10 @@ export class TasksExecutor { this.manualTreeSelectionId.set(selection.executionId); } + toggleExecutionTree() { + this.executionTreeOpen.update((open) => !open); + } + private resolveExecutionById(executionId: string | null): TaskExecution | null { if (!executionId) return null; return this.executionDetails().find((execution) => execution.id === executionId) diff --git a/src/app/shared/execution-tree/execution-tree.css b/src/app/shared/execution-tree/execution-tree.css index 9892f20..e205170 100644 --- a/src/app/shared/execution-tree/execution-tree.css +++ b/src/app/shared/execution-tree/execution-tree.css @@ -1,5 +1,7 @@ :host { - display: block; + display: flex; + flex: 1 1 auto; + min-height: 0; } :host(.execution-tree-empty) { @@ -8,7 +10,9 @@ .execution-tree-shell { display: flex; + flex: 1 1 auto; flex-direction: column; + min-height: 0; max-height: 100%; overflow: hidden; background: #ffffff; @@ -16,16 +20,6 @@ border-radius: 8px; } -.execution-tree-title { - padding: 10px 12px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: #64748b; - border-bottom: 1px solid #e2e8f0; -} - .execution-tree-body { flex: 1; overflow-y: auto; diff --git a/src/app/shared/execution-tree/execution-tree.html b/src/app/shared/execution-tree/execution-tree.html index c83c470..c6a3b41 100644 --- a/src/app/shared/execution-tree/execution-tree.html +++ b/src/app/shared/execution-tree/execution-tree.html @@ -1,7 +1,6 @@ @if (rootExecution(); as root) { @if (hasContainerContent()) {
-
Execution tree
diff --git a/src/app/shared/nodes/task-step-node/task-step-node.css b/src/app/shared/nodes/task-step-node/task-step-node.css index 4d7a1f0..e02aa7a 100644 --- a/src/app/shared/nodes/task-step-node/task-step-node.css +++ b/src/app/shared/nodes/task-step-node/task-step-node.css @@ -460,16 +460,17 @@ button.llm-node-bias-summary:focus-visible { .llm-pill-name { min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + white-space: normal; + overflow-wrap: anywhere; + word-break: break-word; + line-height: 1.15; } .llm-pill-context { min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + white-space: normal; + overflow-wrap: anywhere; + word-break: break-word; font-size: 9px; line-height: 1; font-weight: 700; diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.css b/src/app/shared/task-execution-viewer/task-execution-viewer.css index 1bfadca..63d5d93 100644 --- a/src/app/shared/task-execution-viewer/task-execution-viewer.css +++ b/src/app/shared/task-execution-viewer/task-execution-viewer.css @@ -150,17 +150,22 @@ } .execution-aside-tab { + flex: 0 0 auto; border: 0; border-bottom: 2px solid transparent; background: transparent; color: #64748b; - font-size: 12px; + font-size: 11px; font-weight: 700; - padding: 9px 9px; + padding: 9px 6px; cursor: pointer; white-space: nowrap; } +.execution-aside-tabs { + flex-wrap: nowrap; +} + .execution-aside-tab-active { border-bottom-color: #2563eb; color: #1d4ed8; diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.html b/src/app/shared/task-execution-viewer/task-execution-viewer.html index 08b80ed..8cfad55 100644 --- a/src/app/shared/task-execution-viewer/task-execution-viewer.html +++ b/src/app/shared/task-execution-viewer/task-execution-viewer.html @@ -160,8 +160,8 @@ @if (contextAsideOpen()) { -
-@if (flow()) { +@if (flow() && !editingSubflow()) {
- @if (editingSubflow()) { - Main flow: {{ flow()!.name }} - } Author: {{ flow()!.author }} Created: {{ flow()!.createdAt | date:'dd/MM/yyyy HH:mm' }} Updated: {{ flow()!.updatedAt | date:'dd/MM/yyyy HH:mm' }} diff --git a/src/app/shared/title-toolbar/title-toolbar.spec.ts b/src/app/shared/title-toolbar/title-toolbar.spec.ts index 793a013..b55d736 100644 --- a/src/app/shared/title-toolbar/title-toolbar.spec.ts +++ b/src/app/shared/title-toolbar/title-toolbar.spec.ts @@ -38,6 +38,50 @@ describe('TitleToolbar', () => { expect(component).toBeTruthy(); }); + it('hides parent metadata and global inputs while editing a subflow', async () => { + editorState.currentFlow.set({ + ...makeFlow(), + description: 'Main flow description', + data: { + blocks: [], + containers: [{ + id: 'container-1', + name: 'Container', + typeName: 'LoopContainer', + nodeFamily: 'container', + inputs: [], + outputs: [], + specificConfiguration: { + subFlow: { + blocks: [], + containers: [], + connections: [], + dependencies: [], + globalInputs: [{ name: 'topic', type: 'TEXT', multiple: false }], + lanes: [] + } + } + }], + connections: [], + dependencies: [], + globalInputs: [{ name: 'rootInput', type: 'TEXT', multiple: false }], + lanes: [] + } + }); + editorState.openSubflow([{ containerId: 'container-1', configurationPath: 'subFlow' }]); + + fixture.detectChanges(); + await fixture.whenStable(); + + const host = fixture.nativeElement as HTMLElement; + expect(host.textContent).not.toContain('Description:'); + expect(host.textContent).not.toContain('Global Inputs'); + expect(host.textContent).not.toContain('Author:'); + expect(host.textContent).not.toContain('Created:'); + expect(host.textContent).not.toContain('Updated:'); + expect(host.textContent).not.toContain('Main flow:'); + }); + describe('lanes', () => { beforeEach(() => { editorState.currentFlow.set(makeFlow());