Port artifact renderers from cog frontend into Display/Konsole panes

- Rename DashboardPane → DisplayPane, ArtifactsPane → KonsolePane
- Remove WorkspacePane (superseded by ContentLayout)
- DisplayPane: renders data_table, entity_detail, document_page artifacts
- KonsolePane: renders machine, action_bar, status artifacts
- chat store: add artifacts ref, displayArtifacts/konsoleArtifacts computed,
  setArtifacts(), clearArtifacts(), sendAction()
- useAgentSocket: wire artifacts event → chatStore.setArtifacts()
- AppToolbar: update labels to Chat/Display/Files/Konsole

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nico 2026-04-02 23:44:02 +02:00
parent 60029f1863
commit 8996e553e1
9 changed files with 519 additions and 213 deletions

View File

@ -21,7 +21,7 @@
class="toolbar-pill toolbar-pill-icon" class="toolbar-pill toolbar-pill-icon"
:class="{ active: isPaneOpen('dashboard') }" :class="{ active: isPaneOpen('dashboard') }"
@click="togglePane('dashboard')" @click="togglePane('dashboard')"
title="Dashboard" title="Display"
> >
<RectangleGroupIcon class="w-4 h-4" /> <RectangleGroupIcon class="w-4 h-4" />
</button> </button>
@ -37,7 +37,7 @@
class="toolbar-pill toolbar-pill-icon" class="toolbar-pill toolbar-pill-icon"
:class="{ active: isPaneOpen('artifacts') }" :class="{ active: isPaneOpen('artifacts') }"
@click="togglePane('artifacts')" @click="togglePane('artifacts')"
title="Artifacts" title="Konsole"
> >
<SparklesIcon class="w-4 h-4" /> <SparklesIcon class="w-4 h-4" />
</button> </button>

View File

@ -1,115 +0,0 @@
<template>
<div class="artifacts-pane" :class="orientation">
<div class="artifacts-bar">
<span class="artifacts-label">Artifacts</span>
<button class="orient-btn" @click="toggleOrientation" :title="orientation === 'vertical' ? 'Switch to horizontal' : 'Switch to vertical'">
<component :is="orientation === 'vertical' ? Bars3Icon : Bars3BottomLeftIcon" class="w-3.5 h-3.5" />
</button>
</div>
<div class="artifacts-body">
<span class="artifacts-hint">saved artifacts</span>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'ArtifactsPane' });
import { ref } from 'vue';
import { Bars3Icon, Bars3BottomLeftIcon } from '@heroicons/vue/20/solid';
const orientation = ref<'vertical' | 'horizontal'>('vertical');
function toggleOrientation() {
orientation.value = orientation.value === 'vertical' ? 'horizontal' : 'vertical';
}
</script>
<style scoped>
.artifacts-pane {
display: flex;
height: 100%;
min-height: 0;
min-width: 0;
background: var(--bg);
}
/* Vertical: bar on top, body below */
.artifacts-pane.vertical {
flex-direction: column;
}
.artifacts-pane.vertical .artifacts-bar {
flex-direction: row;
border-bottom: 1px solid var(--border);
padding: 0 8px;
height: 32px;
}
.artifacts-pane.vertical .artifacts-body {
flex: 1;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* Horizontal: bar on left, body to the right */
.artifacts-pane.horizontal {
flex-direction: row;
}
.artifacts-pane.horizontal .artifacts-bar {
flex-direction: column;
border-right: 1px solid var(--border);
padding: 8px 0;
width: 32px;
}
.artifacts-pane.horizontal .artifacts-body {
flex: 1;
flex-direction: row;
align-items: center;
justify-content: center;
}
.artifacts-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
flex-shrink: 0;
background: var(--bg-dim, var(--bg));
}
.artifacts-label {
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-dim);
opacity: 0.5;
writing-mode: initial;
}
.artifacts-pane.horizontal .artifacts-label {
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.orient-btn {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border: none;
background: none;
color: var(--text-dim);
cursor: pointer;
border-radius: 4px;
opacity: 0.5;
transition: opacity 0.12s;
flex-shrink: 0;
}
.orient-btn:hover { opacity: 1; }
.artifacts-body {
display: flex;
opacity: 0.25;
color: var(--text-dim);
font-size: 0.65rem;
}
</style>

View File

@ -1,44 +0,0 @@
<template>
<div class="dashboard-pane">
<div class="dashboard-empty">
<span class="dashboard-label">Dashboard</span>
<span class="dashboard-hint">Artifacts and controls from the agent appear here</span>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'DashboardPane' });
</script>
<style scoped>
.dashboard-pane {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
overflow-y: auto;
background: var(--bg);
}
.dashboard-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
color: var(--text-dim);
opacity: 0.4;
}
.dashboard-label {
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.dashboard-hint {
font-size: 0.72rem;
text-align: center;
max-width: 160px;
}
</style>

View File

@ -0,0 +1,212 @@
<template>
<div class="display-pane">
<div v-if="!displayArtifacts.length" class="display-empty">
<span class="display-label">Anzeige</span>
<span class="display-hint">Tables, pages and cards appear here</span>
</div>
<div v-else class="display-scroll">
<template v-for="art in displayArtifacts" :key="art.id">
<!-- data_table -->
<div v-if="art.type === 'data_table'" class="ws-artifact ws-data-table">
<div v-if="art.data.title" class="ws-artifact-header">{{ art.data.title }}</div>
<div class="ws-table-wrap">
<table class="ws-table">
<thead>
<tr>
<th v-for="col in art.data.columns" :key="col">{{ col }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, i) in (art.data.rows || art.data.data || [])" :key="i">
<td v-for="col in art.data.columns" :key="col">{{ row[col] ?? '' }}</td>
</tr>
</tbody>
</table>
</div>
<div v-if="art.meta?.source" class="ws-artifact-meta">{{ art.meta.source }}</div>
</div>
<!-- entity_detail: single card or list -->
<div v-else-if="art.type === 'entity_detail'" class="ws-artifact ws-entity-detail">
<div v-if="art.data.title" class="ws-card-title">{{ art.data.title }}</div>
<div v-if="art.data.subtitle" class="ws-card-subtitle">{{ art.data.subtitle }}</div>
<!-- list mode -->
<div v-if="art.data.items?.length" class="ws-list">
<div v-for="(item, i) in art.data.items" :key="i" class="ws-card-nested">
<div v-if="item.title" class="ws-card-title">{{ item.title }}</div>
<div v-if="item.fields?.length" class="ws-card-fields">
<div v-for="f in item.fields" :key="f.label" class="ws-card-field">
<span class="ws-card-key">{{ f.label }}</span>
<span class="ws-card-val">{{ f.value ?? '' }}</span>
</div>
</div>
</div>
</div>
<!-- single entity fields -->
<div v-if="art.data.fields?.length" class="ws-card-fields">
<div v-for="f in art.data.fields" :key="f.label" class="ws-card-field">
<span class="ws-card-key">{{ f.label }}</span>
<span v-if="f.action" class="ws-card-link" @click="sendAction(f.action)">{{ f.value ?? '' }}</span>
<span v-else class="ws-card-val">{{ f.value ?? '' }}</span>
</div>
</div>
<div v-if="art.actions?.length" class="ws-card-actions">
<button v-for="a in art.actions" :key="a.action" class="ws-btn" @click="sendAction(a.action, a.payload)">{{ a.label }}</button>
</div>
</div>
<!-- document_page -->
<div v-else-if="art.type === 'document_page'" class="ws-artifact ws-document-page">
<div v-if="art.data.title" class="ws-doc-title">{{ art.data.title }}</div>
<div v-for="(section, i) in (art.data.sections || [])" :key="i" class="ws-doc-section">
<div v-if="section.heading" class="ws-doc-heading">{{ section.heading }}</div>
<div v-if="section.content" class="ws-doc-content" v-html="renderMd(section.content)" />
</div>
<div v-if="art.actions?.length" class="ws-card-actions">
<button v-for="a in art.actions" :key="a.action" class="ws-btn" @click="sendAction(a.action, a.payload)">{{ a.label }}</button>
</div>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'DisplayPane' });
import { useChatStore } from '../store/chat';
const chatStore = useChatStore();
const { displayArtifacts, sendAction } = chatStore;
function renderMd(text: string): string {
return text
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.+?)\*/g, '<em>$1</em>')
.replace(/`(.+?)`/g, '<code>$1</code>')
.replace(/\n/g, '<br>');
}
</script>
<style scoped>
.display-pane {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
background: var(--bg);
}
.display-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
color: var(--text-dim);
opacity: 0.4;
}
.display-label {
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.display-hint { font-size: 0.72rem; text-align: center; max-width: 160px; }
.display-scroll {
flex: 1;
overflow-y: auto;
padding: 12px;
display: flex;
flex-direction: column;
gap: 12px;
}
.ws-artifact {
background: var(--panel-bg, var(--bg-dim));
border-radius: 8px;
overflow: hidden;
}
.ws-artifact-header {
padding: 8px 12px 4px;
font-size: 0.75rem;
font-weight: 600;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.ws-artifact-meta {
padding: 4px 12px 8px;
font-size: 0.68rem;
color: var(--text-dim);
opacity: 0.5;
}
/* Table */
.ws-table-wrap { overflow-x: auto; }
.ws-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
.ws-table th {
text-align: left; padding: 6px 10px; font-weight: 600;
color: var(--text-dim); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.ws-table td {
padding: 5px 10px;
border-bottom: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
color: var(--text);
}
.ws-table tr:last-child td { border-bottom: none; }
.ws-table tr:hover td { background: color-mix(in srgb, var(--accent) 5%, transparent); }
/* Entity */
.ws-entity-detail { padding: 12px; }
.ws-card-title { font-size: 0.9rem; font-weight: 600; color: var(--text); margin-bottom: 2px; }
.ws-card-subtitle { font-size: 0.78rem; color: var(--text-dim); margin-bottom: 8px; }
.ws-list { display: flex; flex-direction: column; gap: 8px; }
.ws-card-nested {
background: color-mix(in srgb, var(--border) 30%, transparent);
border-radius: 6px; padding: 8px 10px;
}
.ws-card-fields { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.ws-card-field { display: flex; gap: 8px; font-size: 0.8rem; }
.ws-card-key { color: var(--text-dim); flex: 0 0 auto; min-width: 80px; }
.ws-card-val { color: var(--text); }
.ws-card-link { color: var(--accent); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.ws-card-link:hover { opacity: 0.8; }
.ws-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
/* Document */
.ws-document-page { padding: 16px; }
.ws-doc-title { font-size: 1rem; font-weight: 700; color: var(--text); margin-bottom: 12px; }
.ws-doc-section { margin-bottom: 12px; }
.ws-doc-heading {
font-size: 0.85rem; font-weight: 600; color: var(--text-dim);
margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.04em;
}
.ws-doc-content { font-size: 0.85rem; color: var(--text); line-height: 1.6; }
.ws-doc-content :deep(code) {
background: color-mix(in srgb, var(--border) 60%, transparent);
border-radius: 3px; padding: 1px 4px; font-size: 0.8em;
}
/* Shared button */
.ws-btn {
padding: 5px 12px;
background: var(--bg-dim);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 0.8rem;
color: var(--text);
cursor: pointer;
transition: border-color 0.12s, color 0.12s;
}
.ws-btn:hover { border-color: var(--accent); color: var(--accent); }
</style>

View File

@ -0,0 +1,268 @@
<template>
<div class="konsole-pane" :class="orientation">
<div class="konsole-bar">
<span class="konsole-label">Konsole</span>
<button class="orient-btn" @click="toggleOrientation" :title="orientation === 'vertical' ? 'Switch to horizontal' : 'Switch to vertical'">
<component :is="orientation === 'vertical' ? Bars3Icon : Bars3BottomLeftIcon" class="w-3.5 h-3.5" />
</button>
</div>
<div class="konsole-body">
<div v-if="!konsoleArtifacts.length" class="konsole-empty">
<span class="konsole-hint">Konsole</span>
</div>
<div v-else class="konsole-scroll">
<template v-for="art in konsoleArtifacts" :key="art.id">
<!-- machine -->
<div v-if="art.type === 'machine'" class="kw-artifact kw-machine">
<div class="kw-machine-header">
<span class="kw-machine-name">{{ art.data.machine_id }}</span>
<span class="kw-machine-state">{{ art.data.current }}</span>
</div>
<div v-for="(text, i) in (art.data.content || [])" :key="i" class="kw-machine-content">{{ text }}</div>
<div v-if="Object.keys(art.data.stored_data || {}).length" class="kw-machine-data">
<span v-for="(v, k) in art.data.stored_data" :key="k" class="kw-machine-datum">{{ k }}={{ v }}</span>
</div>
<div v-if="art.actions?.length" class="kw-actions">
<button
v-for="a in art.actions"
:key="a.action"
class="kw-btn"
@click="sendAction(a.action)"
>{{ a.label }}</button>
</div>
</div>
<!-- action_bar -->
<div v-else-if="art.type === 'action_bar'" class="kw-artifact kw-action-bar">
<button
v-for="a in art.actions"
:key="a.action"
class="kw-btn"
@click="sendAction(a.action, a.payload)"
>{{ a.label }}</button>
</div>
<!-- status -->
<div v-else-if="art.type === 'status'" class="kw-artifact kw-status" :class="'kw-dt-' + (art.data.display_type || 'text')">
<!-- progress bar -->
<template v-if="art.data.display_type === 'progress'">
<span class="kw-label">{{ art.data.label }}</span>
<div class="kw-bar"><div class="kw-fill" :style="{ width: clamp(art.data.value) + '%' }" /></div>
<span class="kw-pct">{{ clamp(art.data.value) }}%</span>
</template>
<!-- info -->
<template v-else-if="art.data.display_type === 'info'">
<span class="kw-icon"></span>
<span class="kw-label">{{ art.data.label }}</span>
</template>
<!-- text / default -->
<template v-else>
<span class="kw-label">{{ art.data.label }}</span>
<span v-if="art.data.value" class="kw-value">{{ art.data.value }}</span>
</template>
</div>
</template>
</div>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'KonsolePane' });
import { ref } from 'vue';
import { Bars3Icon, Bars3BottomLeftIcon } from '@heroicons/vue/20/solid';
import { useChatStore } from '../store/chat';
const chatStore = useChatStore();
const { konsoleArtifacts, sendAction } = chatStore;
const orientation = ref<'vertical' | 'horizontal'>('vertical');
function toggleOrientation() {
orientation.value = orientation.value === 'vertical' ? 'horizontal' : 'vertical';
}
function clamp(v: any): number {
return Math.min(100, Math.max(0, Number(v) || 0));
}
</script>
<style scoped>
.konsole-pane {
display: flex;
height: 100%;
min-height: 0;
min-width: 0;
background: var(--bg);
}
/* Vertical: bar on top */
.konsole-pane.vertical { flex-direction: column; }
.konsole-pane.vertical .konsole-bar {
flex-direction: row;
border-bottom: 1px solid var(--border);
padding: 0 8px;
height: 32px;
}
.konsole-pane.vertical .konsole-body { flex: 1; overflow-y: auto; }
/* Horizontal: bar on left */
.konsole-pane.horizontal { flex-direction: row; }
.konsole-pane.horizontal .konsole-bar {
flex-direction: column;
border-right: 1px solid var(--border);
padding: 8px 0;
width: 32px;
}
.konsole-pane.horizontal .konsole-body { flex: 1; overflow-y: auto; }
.konsole-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px;
flex-shrink: 0;
background: var(--bg-dim, var(--bg));
}
.konsole-label {
font-size: 0.65rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-dim);
opacity: 0.5;
}
.konsole-pane.horizontal .konsole-label {
writing-mode: vertical-rl;
transform: rotate(180deg);
}
.orient-btn {
display: flex; align-items: center; justify-content: center;
width: 22px; height: 22px;
border: none; background: none; color: var(--text-dim);
cursor: pointer; border-radius: 4px;
opacity: 0.5; transition: opacity 0.12s; flex-shrink: 0;
}
.orient-btn:hover { opacity: 1; }
.konsole-body { display: flex; flex-direction: column; }
.konsole-empty {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
opacity: 0.25;
font-size: 0.7rem;
}
.konsole-scroll {
padding: 8px;
display: flex;
flex-direction: column;
gap: 8px;
}
/* Artifact base */
.kw-artifact {
background: var(--panel-bg, var(--bg-dim));
border-radius: 6px;
overflow: hidden;
}
/* Machine */
.kw-machine { padding: 10px; }
.kw-machine-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.kw-machine-name {
font-size: 0.75rem;
font-weight: 600;
color: var(--text-dim);
text-transform: uppercase;
letter-spacing: 0.04em;
}
.kw-machine-state {
font-size: 0.72rem;
color: var(--accent);
font-weight: 600;
}
.kw-machine-content {
font-size: 0.82rem;
color: var(--text);
padding: 2px 0;
}
.kw-machine-data {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 6px;
}
.kw-machine-datum {
font-size: 0.72rem;
color: var(--text-dim);
background: color-mix(in srgb, var(--border) 40%, transparent);
border-radius: 4px;
padding: 2px 6px;
}
/* Action bar */
.kw-action-bar {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 8px;
}
/* Status */
.kw-status {
display: flex;
align-items: center;
gap: 8px;
padding: 7px 10px;
font-size: 0.8rem;
}
.kw-label { color: var(--text-dim); flex: 0 0 auto; }
.kw-value { color: var(--text); font-weight: 500; }
.kw-icon { font-size: 0.85rem; }
.kw-bar {
flex: 1;
height: 6px;
background: color-mix(in srgb, var(--border) 60%, transparent);
border-radius: 3px;
overflow: hidden;
}
.kw-fill {
height: 100%;
background: var(--accent);
border-radius: 3px;
transition: width 0.3s ease;
}
.kw-pct { color: var(--text-dim); font-size: 0.75rem; flex: 0 0 auto; }
/* Actions */
.kw-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.kw-btn {
padding: 5px 12px;
background: var(--bg-dim);
border: 1px solid var(--border);
border-radius: 6px;
font-size: 0.8rem;
color: var(--text);
cursor: pointer;
transition: border-color 0.12s, color 0.12s;
}
.kw-btn:hover { border-color: var(--accent); color: var(--accent); }
</style>

View File

@ -1,41 +0,0 @@
<template>
<div class="workspace-pane">
<div class="workspace-empty">
<span class="workspace-label">Workspace</span>
<span class="workspace-hint">Dashboard, files, artifacts</span>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'WorkspacePane' });
</script>
<style scoped>
.workspace-pane {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
background: var(--bg);
}
.workspace-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
color: var(--text-dim);
opacity: 0.4;
}
.workspace-label {
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.workspace-hint {
font-size: 0.72rem;
}
</style>

View File

@ -56,14 +56,12 @@ export function useAgentSocket(
if (data.session_id) chatStore.sessionKey = data.session_id; if (data.session_id) chatStore.sessionKey = data.session_id;
}, },
// assay UI controls — store for later rendering // assay UI controls (legacy flat list) — kept for compat, ignored for now
controls(_data) { controls(_data) {},
// TODO: render controls in workspace panel
},
// assay artifacts — store for later rendering // assay artifacts — split into Display (pages/tables) and Konsole (machines/controls)
artifacts(_data) { artifacts(data) {
// TODO: render artifacts in workspace panel chatStore.setArtifacts(data.artifacts || []);
}, },
// assay session cleared // assay session cleared

View File

@ -16,9 +16,25 @@ export interface FinanceData {
pricing: { prompt: number, completion: number }; pricing: { prompt: number, completion: number };
} }
// Artifact types that belong to each pane
const DISPLAY_TYPES = new Set(['data_table', 'document_page', 'entity_detail']);
const KONSOLE_TYPES = new Set(['machine', 'action_bar', 'status']);
export const useChatStore = defineStore('chat', () => { export const useChatStore = defineStore('chat', () => {
// --- State --- // --- State ---
const messages = ref<any[]>([]); const messages = ref<any[]>([]);
// --- Artifacts ---
const artifacts = ref<any[]>([]);
const displayArtifacts = computed(() => artifacts.value.filter(a => DISPLAY_TYPES.has(a.type)));
const konsoleArtifacts = computed(() => artifacts.value.filter(a => KONSOLE_TYPES.has(a.type)));
function setArtifacts(arts: any[]) {
artifacts.value = arts;
}
function clearArtifacts() {
artifacts.value = [];
}
// Two-SM architecture: channel (shared) + connection (per-user) // Two-SM architecture: channel (shared) + connection (per-user)
const channelState = ref<ChannelState>('NO_SESSION'); const channelState = ref<ChannelState>('NO_SESSION');
const connectionState = ref<ConnectionState>('CONNECTING'); const connectionState = ref<ConnectionState>('CONNECTING');
@ -66,6 +82,11 @@ export const useChatStore = defineStore('chat', () => {
let _wsSend: ((payload: any) => void) | null = null; let _wsSend: ((payload: any) => void) | null = null;
function setWsSend(fn: (payload: any) => void) { _wsSend = fn; } function setWsSend(fn: (payload: any) => void) { _wsSend = fn; }
// Artifact action dispatch — sends {type:'action', action, data} over WS
function sendAction(action: string, data: any = {}) {
_wsSend?.({ type: 'action', action, data });
}
// Session actions (called from HudControls / HudRow) // Session actions (called from HudControls / HudRow)
function newSession() { function newSession() {
stashMessages(); stashMessages();
@ -201,6 +222,7 @@ export const useChatStore = defineStore('chat', () => {
messages.value = []; messages.value = [];
sessionTotalTokens.value = null; sessionTotalTokens.value = null;
finance.value = null; finance.value = null;
artifacts.value = [];
resetStreamingState(); resetStreamingState();
} }
@ -434,10 +456,16 @@ export const useChatStore = defineStore('chat', () => {
handoverPending, handoverPending,
isRunning, isRunning,
setWsSend, setWsSend,
sendAction,
newSession, newSession,
handover, handover,
stop, stop,
confirmNew, confirmNew,
stay, stay,
artifacts,
displayArtifacts,
konsoleArtifacts,
setArtifacts,
clearArtifacts,
}; };
}); });

View File

@ -43,13 +43,13 @@
/> />
</template> </template>
<template #dashboard> <template #dashboard>
<DashboardPane /> <DisplayPane />
</template> </template>
<template #files> <template #files>
<FilesPane /> <FilesPane />
</template> </template>
<template #artifacts> <template #artifacts>
<ArtifactsPane /> <KonsolePane />
</template> </template>
</ContentLayout> </ContentLayout>
@ -79,9 +79,9 @@ import { usePanels } from '../composables/usePanels';
import { LockClosedIcon, UserGroupIcon } from '@heroicons/vue/20/solid'; import { LockClosedIcon, UserGroupIcon } from '@heroicons/vue/20/solid';
import ChatPane from '../components/ChatPane.vue'; import ChatPane from '../components/ChatPane.vue';
import ContentLayout from '../components/ContentLayout.vue'; import ContentLayout from '../components/ContentLayout.vue';
import DashboardPane from '../components/DashboardPane.vue'; import DisplayPane from '../components/DisplayPane.vue';
import FilesPane from '../components/FilesPane.vue'; import FilesPane from '../components/FilesPane.vue';
import ArtifactsPane from '../components/ArtifactsPane.vue'; import KonsolePane from '../components/KonsolePane.vue';
import DebugColumn from '../components/DebugColumn.vue'; import DebugColumn from '../components/DebugColumn.vue';
import router from '../router'; import router from '../router';