refac: overview
This commit is contained in:
parent
270ca2ddbe
commit
b4536a691a
|
@ -300,7 +300,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
const showMessage = async (message) => {
|
||||
const showMessage = async (message, ignoreSettings = false) => {
|
||||
await tick();
|
||||
|
||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||
|
@ -326,7 +326,7 @@
|
|||
await tick();
|
||||
await tick();
|
||||
|
||||
if ($settings?.scrollOnBranchChange ?? true) {
|
||||
if (($settings?.scrollOnBranchChange ?? true) || ignoreSettings) {
|
||||
const messageElement = document.getElementById(`message-${message.id}`);
|
||||
if (messageElement) {
|
||||
messageElement.scrollIntoView({ behavior: 'smooth' });
|
||||
|
|
|
@ -13,12 +13,9 @@
|
|||
showEmbeds
|
||||
} from '$lib/stores';
|
||||
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import Controls from './Controls/Controls.svelte';
|
||||
import CallOverlay from './MessageInput/CallOverlay.svelte';
|
||||
import Drawer from '../common/Drawer.svelte';
|
||||
import Overview from './Overview.svelte';
|
||||
import EllipsisVertical from '../icons/EllipsisVertical.svelte';
|
||||
import Artifacts from './Artifacts.svelte';
|
||||
import Embeds from './ChatControls/Embeds.svelte';
|
||||
|
||||
|
@ -154,24 +151,113 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<SvelteFlowProvider>
|
||||
{#if !largeScreen}
|
||||
{#if $showControls}
|
||||
<Drawer
|
||||
show={$showControls}
|
||||
onClose={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
{#if !largeScreen}
|
||||
{#if $showControls}
|
||||
<Drawer
|
||||
show={$showControls}
|
||||
onClose={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class=" {$showCallOverlay || $showOverview || $showArtifacts || $showEmbeds
|
||||
? ' h-screen w-full'
|
||||
: 'px-4 py-3'} h-full"
|
||||
>
|
||||
{#if $showCallOverlay}
|
||||
<div
|
||||
class=" h-full max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
|
||||
>
|
||||
<CallOverlay
|
||||
bind:files
|
||||
{submitPrompt}
|
||||
{stopResponse}
|
||||
{modelId}
|
||||
{chatId}
|
||||
{eventTarget}
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else if $showEmbeds}
|
||||
<Embeds />
|
||||
{:else if $showArtifacts}
|
||||
<Artifacts {history} />
|
||||
{:else if $showOverview}
|
||||
{#await import('./Overview.svelte') then { default: Overview }}
|
||||
<Overview
|
||||
{history}
|
||||
onNodeClick={(e) => {
|
||||
const node = e.node;
|
||||
showMessage(node.data.message, true);
|
||||
}}
|
||||
onClose={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
{:else}
|
||||
<Controls
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
{models}
|
||||
bind:chatFiles
|
||||
bind:params
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</Drawer>
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- if $showControls -->
|
||||
|
||||
{#if $showControls}
|
||||
<PaneResizer
|
||||
class="relative flex items-center justify-center group border-l border-gray-50 dark:border-gray-850 hover:border-gray-200 dark:hover:border-gray-800 transition z-20"
|
||||
id="controls-resizer"
|
||||
>
|
||||
<div
|
||||
class=" absolute -left-1.5 -right-1.5 -top-0 -bottom-0 z-20 cursor-col-resize bg-transparent"
|
||||
/>
|
||||
</PaneResizer>
|
||||
{/if}
|
||||
|
||||
<Pane
|
||||
bind:pane
|
||||
defaultSize={0}
|
||||
onResize={(size) => {
|
||||
if ($showControls && pane.isExpanded()) {
|
||||
if (size < minSize) {
|
||||
pane.resize(minSize);
|
||||
}
|
||||
|
||||
if (size < minSize) {
|
||||
localStorage.chatControlsSize = 0;
|
||||
} else {
|
||||
// save the size in pixels to localStorage
|
||||
const container = document.getElementById('chat-container');
|
||||
localStorage.chatControlsSize = Math.floor((size / 100) * container.clientWidth);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCollapse={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
collapsible={true}
|
||||
class=" z-10 bg-white dark:bg-gray-850"
|
||||
>
|
||||
{#if $showControls}
|
||||
<div class="flex max-h-full min-h-full">
|
||||
<div
|
||||
class=" {$showCallOverlay || $showOverview || $showArtifacts || $showEmbeds
|
||||
? ' h-screen w-full'
|
||||
: 'px-4 py-3'} h-full"
|
||||
class="w-full {($showOverview || $showArtifacts || $showEmbeds) && !$showCallOverlay
|
||||
? ' '
|
||||
: 'px-4 py-3 bg-white dark:shadow-lg dark:bg-gray-850 '} z-40 pointer-events-auto overflow-y-auto scrollbar-hidden"
|
||||
id="controls-container"
|
||||
>
|
||||
{#if $showCallOverlay}
|
||||
<div
|
||||
class=" h-full max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
|
||||
>
|
||||
<div class="w-full h-full flex justify-center">
|
||||
<CallOverlay
|
||||
bind:files
|
||||
{submitPrompt}
|
||||
|
@ -185,19 +271,28 @@
|
|||
/>
|
||||
</div>
|
||||
{:else if $showEmbeds}
|
||||
<Embeds />
|
||||
<Embeds overlay={dragged} />
|
||||
{:else if $showArtifacts}
|
||||
<Artifacts {history} />
|
||||
<Artifacts {history} overlay={dragged} />
|
||||
{:else if $showOverview}
|
||||
<Overview
|
||||
{history}
|
||||
on:nodeclick={(e) => {
|
||||
showMessage(e.detail.node.data.message);
|
||||
}}
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
{#await import('./Overview.svelte') then { default: Overview }}
|
||||
<Overview
|
||||
{history}
|
||||
onNodeClick={(e) => {
|
||||
const node = e.node;
|
||||
if (node?.data?.message?.favorite) {
|
||||
history.messages[node.data.message.id].favorite = true;
|
||||
} else {
|
||||
history.messages[node.data.message.id].favorite = null;
|
||||
}
|
||||
|
||||
showMessage(node.data.message, true);
|
||||
}}
|
||||
onClose={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
{/await}
|
||||
{:else}
|
||||
<Controls
|
||||
on:close={() => {
|
||||
|
@ -209,101 +304,7 @@
|
|||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</Drawer>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- if $showControls -->
|
||||
|
||||
{#if $showControls}
|
||||
<PaneResizer
|
||||
class="relative flex items-center justify-center group border-l border-gray-50 dark:border-gray-850 hover:border-gray-200 dark:hover:border-gray-800 transition z-20"
|
||||
id="controls-resizer"
|
||||
>
|
||||
<div
|
||||
class=" absolute -left-1.5 -right-1.5 -top-0 -bottom-0 z-20 cursor-col-resize bg-transparent"
|
||||
/>
|
||||
</PaneResizer>
|
||||
{/if}
|
||||
|
||||
<Pane
|
||||
bind:pane
|
||||
defaultSize={0}
|
||||
onResize={(size) => {
|
||||
if ($showControls && pane.isExpanded()) {
|
||||
if (size < minSize) {
|
||||
pane.resize(minSize);
|
||||
}
|
||||
|
||||
if (size < minSize) {
|
||||
localStorage.chatControlsSize = 0;
|
||||
} else {
|
||||
// save the size in pixels to localStorage
|
||||
const container = document.getElementById('chat-container');
|
||||
localStorage.chatControlsSize = Math.floor((size / 100) * container.clientWidth);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onCollapse={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
collapsible={true}
|
||||
class=" z-10 bg-white dark:bg-gray-850"
|
||||
>
|
||||
{#if $showControls}
|
||||
<div class="flex max-h-full min-h-full">
|
||||
<div
|
||||
class="w-full {($showOverview || $showArtifacts || $showEmbeds) && !$showCallOverlay
|
||||
? ' '
|
||||
: 'px-4 py-3 bg-white dark:shadow-lg dark:bg-gray-850 '} z-40 pointer-events-auto overflow-y-auto scrollbar-hidden"
|
||||
id="controls-container"
|
||||
>
|
||||
{#if $showCallOverlay}
|
||||
<div class="w-full h-full flex justify-center">
|
||||
<CallOverlay
|
||||
bind:files
|
||||
{submitPrompt}
|
||||
{stopResponse}
|
||||
{modelId}
|
||||
{chatId}
|
||||
{eventTarget}
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else if $showEmbeds}
|
||||
<Embeds overlay={dragged} />
|
||||
{:else if $showArtifacts}
|
||||
<Artifacts {history} overlay={dragged} />
|
||||
{:else if $showOverview}
|
||||
<Overview
|
||||
{history}
|
||||
on:nodeclick={(e) => {
|
||||
if (e.detail.node.data.message.favorite) {
|
||||
history.messages[e.detail.node.data.message.id].favorite = true;
|
||||
} else {
|
||||
history.messages[e.detail.node.data.message.id].favorite = null;
|
||||
}
|
||||
|
||||
showMessage(e.detail.node.data.message);
|
||||
}}
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Controls
|
||||
on:close={() => {
|
||||
showControls.set(false);
|
||||
}}
|
||||
{models}
|
||||
bind:chatFiles
|
||||
bind:params
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</Pane>
|
||||
{/if}
|
||||
</SvelteFlowProvider>
|
||||
</Pane>
|
||||
{/if}
|
||||
|
|
|
@ -1,206 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { getContext, createEventDispatcher, onDestroy } from 'svelte';
|
||||
import { useSvelteFlow, useNodesInitialized, useStore } from '@xyflow/svelte';
|
||||
import { useSvelteFlow, useNodesInitialized, useStore, SvelteFlowProvider } from '@xyflow/svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import { onMount, tick } from 'svelte';
|
||||
|
||||
import { writable } from 'svelte/store';
|
||||
import { models, showOverview, theme, user } from '$lib/stores';
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
|
||||
import CustomNode from './Overview/Node.svelte';
|
||||
import Flow from './Overview/Flow.svelte';
|
||||
import XMark from '../icons/XMark.svelte';
|
||||
import ArrowLeft from '../icons/ArrowLeft.svelte';
|
||||
|
||||
const { width, height } = useStore();
|
||||
|
||||
const { fitView, getViewport } = useSvelteFlow();
|
||||
const nodesInitialized = useNodesInitialized();
|
||||
import View from './Overview/View.svelte';
|
||||
|
||||
export let history;
|
||||
|
||||
let selectedMessageId = null;
|
||||
|
||||
const nodes = writable([]);
|
||||
const edges = writable([]);
|
||||
|
||||
let layoutDirection = 'vertical';
|
||||
|
||||
const nodeTypes = {
|
||||
custom: CustomNode
|
||||
};
|
||||
|
||||
$: if (history) {
|
||||
drawFlow(layoutDirection);
|
||||
}
|
||||
|
||||
$: if (history && history.currentId) {
|
||||
focusNode();
|
||||
}
|
||||
|
||||
const focusNode = async () => {
|
||||
if (selectedMessageId === null) {
|
||||
await fitView({ nodes: [{ id: history.currentId }] });
|
||||
} else {
|
||||
await fitView({ nodes: [{ id: selectedMessageId }] });
|
||||
}
|
||||
|
||||
selectedMessageId = null;
|
||||
};
|
||||
|
||||
const drawFlow = async (direction) => {
|
||||
const nodeList = [];
|
||||
const edgeList = [];
|
||||
const levelOffset = direction === 'vertical' ? 150 : 300;
|
||||
const siblingOffset = direction === 'vertical' ? 250 : 150;
|
||||
|
||||
// Map to keep track of node positions at each level
|
||||
let positionMap = new Map();
|
||||
|
||||
// Helper function to truncate labels
|
||||
function createLabel(content) {
|
||||
const maxLength = 100;
|
||||
return content.length > maxLength ? content.substr(0, maxLength) + '...' : content;
|
||||
}
|
||||
|
||||
// Create nodes and map children to ensure alignment in width
|
||||
let layerWidths = {}; // Track widths of each layer
|
||||
|
||||
Object.keys(history.messages).forEach((id) => {
|
||||
const message = history.messages[id];
|
||||
const level = message.parentId ? (positionMap.get(message.parentId)?.level ?? -1) + 1 : 0;
|
||||
if (!layerWidths[level]) layerWidths[level] = 0;
|
||||
|
||||
positionMap.set(id, {
|
||||
id: message.id,
|
||||
level,
|
||||
position: layerWidths[level]++
|
||||
});
|
||||
});
|
||||
|
||||
// Adjust positions based on siblings count to centralize vertical spacing
|
||||
Object.keys(history.messages).forEach((id) => {
|
||||
const pos = positionMap.get(id);
|
||||
const x = direction === 'vertical' ? pos.position * siblingOffset : pos.level * levelOffset;
|
||||
const y = direction === 'vertical' ? pos.level * levelOffset : pos.position * siblingOffset;
|
||||
|
||||
nodeList.push({
|
||||
id: pos.id,
|
||||
type: 'custom',
|
||||
data: {
|
||||
user: $user,
|
||||
message: history.messages[id],
|
||||
model: $models.find((model) => model.id === history.messages[id].model)
|
||||
},
|
||||
position: { x, y }
|
||||
});
|
||||
|
||||
// Create edges
|
||||
const parentId = history.messages[id].parentId;
|
||||
if (parentId) {
|
||||
edgeList.push({
|
||||
id: parentId + '-' + pos.id,
|
||||
source: parentId,
|
||||
target: pos.id,
|
||||
selectable: false,
|
||||
class: ' dark:fill-gray-300 fill-gray-300',
|
||||
type: 'smoothstep',
|
||||
animated: history.currentId === id || recurseCheckChild(id, history.currentId)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await edges.set([...edgeList]);
|
||||
await nodes.set([...nodeList]);
|
||||
};
|
||||
|
||||
const recurseCheckChild = (nodeId, currentId) => {
|
||||
const node = history.messages[nodeId];
|
||||
return (
|
||||
node.childrenIds &&
|
||||
node.childrenIds.some((id) => id === currentId || recurseCheckChild(id, currentId))
|
||||
);
|
||||
};
|
||||
|
||||
const setLayoutDirection = (direction) => {
|
||||
layoutDirection = direction;
|
||||
drawFlow(layoutDirection);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
drawFlow(layoutDirection);
|
||||
|
||||
nodesInitialized.subscribe(async (initialized) => {
|
||||
if (initialized) {
|
||||
await tick();
|
||||
const res = await fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
|
||||
width.subscribe((value) => {
|
||||
if (value) {
|
||||
// fitView();
|
||||
fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
|
||||
height.subscribe((value) => {
|
||||
if (value) {
|
||||
// fitView();
|
||||
fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
console.log('Overview destroyed');
|
||||
|
||||
nodes.set([]);
|
||||
edges.set([]);
|
||||
});
|
||||
export let onClose;
|
||||
export let onNodeClick;
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full relative">
|
||||
<div class=" absolute z-50 w-full flex justify-between dark:text-gray-100 px-4 py-3">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<button
|
||||
class="self-center p-0.5"
|
||||
on:click={() => {
|
||||
showOverview.set(false);
|
||||
}}
|
||||
>
|
||||
<ArrowLeft className="size-3.5" />
|
||||
</button>
|
||||
<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Overview')}</div>
|
||||
</div>
|
||||
<button
|
||||
class="self-center p-0.5"
|
||||
on:click={() => {
|
||||
dispatch('close');
|
||||
showOverview.set(false);
|
||||
}}
|
||||
>
|
||||
<XMark className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if $nodes.length > 0}
|
||||
<Flow
|
||||
{nodes}
|
||||
{nodeTypes}
|
||||
{edges}
|
||||
{setLayoutDirection}
|
||||
on:nodeclick={(e) => {
|
||||
console.log(e.detail.node.data);
|
||||
dispatch('nodeclick', e.detail);
|
||||
selectedMessageId = e.detail.node.data.message.id;
|
||||
fitView({ nodes: [{ id: selectedMessageId }] });
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<SvelteFlowProvider>
|
||||
<View {history} {onClose} {onNodeClick} />
|
||||
</SvelteFlowProvider>
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
<script lang="ts">
|
||||
import { getContext, createEventDispatcher, onDestroy } from 'svelte';
|
||||
import { useSvelteFlow, useNodesInitialized, useStore } from '@xyflow/svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import { onMount, tick } from 'svelte';
|
||||
|
||||
import { writable } from 'svelte/store';
|
||||
import { models, showOverview, theme, user } from '$lib/stores';
|
||||
|
||||
import '@xyflow/svelte/dist/style.css';
|
||||
|
||||
import CustomNode from './Node.svelte';
|
||||
import Flow from './Flow.svelte';
|
||||
import XMark from '../../icons/XMark.svelte';
|
||||
import ArrowLeft from '../../icons/ArrowLeft.svelte';
|
||||
|
||||
const { width, height } = useStore();
|
||||
|
||||
const { fitView, getViewport } = useSvelteFlow();
|
||||
const nodesInitialized = useNodesInitialized();
|
||||
|
||||
export let history;
|
||||
export let onClose;
|
||||
export let onNodeClick;
|
||||
|
||||
let selectedMessageId = null;
|
||||
|
||||
const nodes = writable([]);
|
||||
const edges = writable([]);
|
||||
|
||||
let layoutDirection = 'vertical';
|
||||
|
||||
const nodeTypes = {
|
||||
custom: CustomNode
|
||||
};
|
||||
|
||||
$: if (history) {
|
||||
drawFlow(layoutDirection);
|
||||
}
|
||||
|
||||
$: if (history && history.currentId) {
|
||||
focusNode();
|
||||
}
|
||||
|
||||
const focusNode = async () => {
|
||||
if (selectedMessageId === null) {
|
||||
await fitView({ nodes: [{ id: history.currentId }] });
|
||||
} else {
|
||||
await fitView({ nodes: [{ id: selectedMessageId }] });
|
||||
}
|
||||
|
||||
selectedMessageId = null;
|
||||
};
|
||||
|
||||
const drawFlow = async (direction) => {
|
||||
const nodeList = [];
|
||||
const edgeList = [];
|
||||
const levelOffset = direction === 'vertical' ? 150 : 300;
|
||||
const siblingOffset = direction === 'vertical' ? 250 : 150;
|
||||
|
||||
// Map to keep track of node positions at each level
|
||||
let positionMap = new Map();
|
||||
|
||||
// Helper function to truncate labels
|
||||
function createLabel(content) {
|
||||
const maxLength = 100;
|
||||
return content.length > maxLength ? content.substr(0, maxLength) + '...' : content;
|
||||
}
|
||||
|
||||
// Create nodes and map children to ensure alignment in width
|
||||
let layerWidths = {}; // Track widths of each layer
|
||||
|
||||
Object.keys(history.messages).forEach((id) => {
|
||||
const message = history.messages[id];
|
||||
const level = message.parentId ? (positionMap.get(message.parentId)?.level ?? -1) + 1 : 0;
|
||||
if (!layerWidths[level]) layerWidths[level] = 0;
|
||||
|
||||
positionMap.set(id, {
|
||||
id: message.id,
|
||||
level,
|
||||
position: layerWidths[level]++
|
||||
});
|
||||
});
|
||||
|
||||
// Adjust positions based on siblings count to centralize vertical spacing
|
||||
Object.keys(history.messages).forEach((id) => {
|
||||
const pos = positionMap.get(id);
|
||||
const x = direction === 'vertical' ? pos.position * siblingOffset : pos.level * levelOffset;
|
||||
const y = direction === 'vertical' ? pos.level * levelOffset : pos.position * siblingOffset;
|
||||
|
||||
nodeList.push({
|
||||
id: pos.id,
|
||||
type: 'custom',
|
||||
data: {
|
||||
user: $user,
|
||||
message: history.messages[id],
|
||||
model: $models.find((model) => model.id === history.messages[id].model)
|
||||
},
|
||||
position: { x, y }
|
||||
});
|
||||
|
||||
// Create edges
|
||||
const parentId = history.messages[id].parentId;
|
||||
if (parentId) {
|
||||
edgeList.push({
|
||||
id: parentId + '-' + pos.id,
|
||||
source: parentId,
|
||||
target: pos.id,
|
||||
selectable: false,
|
||||
class: ' dark:fill-gray-300 fill-gray-300',
|
||||
type: 'smoothstep',
|
||||
animated: history.currentId === id || recurseCheckChild(id, history.currentId)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await edges.set([...edgeList]);
|
||||
await nodes.set([...nodeList]);
|
||||
};
|
||||
|
||||
const recurseCheckChild = (nodeId, currentId) => {
|
||||
const node = history.messages[nodeId];
|
||||
return (
|
||||
node.childrenIds &&
|
||||
node.childrenIds.some((id) => id === currentId || recurseCheckChild(id, currentId))
|
||||
);
|
||||
};
|
||||
|
||||
const setLayoutDirection = (direction) => {
|
||||
layoutDirection = direction;
|
||||
drawFlow(layoutDirection);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
drawFlow(layoutDirection);
|
||||
|
||||
nodesInitialized.subscribe(async (initialized) => {
|
||||
if (initialized) {
|
||||
await tick();
|
||||
const res = await fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
|
||||
width.subscribe((value) => {
|
||||
if (value) {
|
||||
// fitView();
|
||||
fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
|
||||
height.subscribe((value) => {
|
||||
if (value) {
|
||||
// fitView();
|
||||
fitView({ nodes: [{ id: history.currentId }] });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
console.log('Overview destroyed');
|
||||
|
||||
nodes.set([]);
|
||||
edges.set([]);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full relative">
|
||||
<div class=" absolute z-50 w-full flex justify-between dark:text-gray-100 px-4 py-3">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<button
|
||||
class="self-center p-0.5"
|
||||
on:click={() => {
|
||||
showOverview.set(false);
|
||||
}}
|
||||
>
|
||||
<ArrowLeft className="size-3.5" />
|
||||
</button>
|
||||
<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Overview')}</div>
|
||||
</div>
|
||||
<button
|
||||
class="self-center p-0.5"
|
||||
on:click={() => {
|
||||
onClose();
|
||||
showOverview.set(false);
|
||||
}}
|
||||
>
|
||||
<XMark className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if $nodes.length > 0}
|
||||
<Flow
|
||||
{nodes}
|
||||
{nodeTypes}
|
||||
{edges}
|
||||
{setLayoutDirection}
|
||||
on:nodeclick={(e) => {
|
||||
onNodeClick(e.detail);
|
||||
selectedMessageId = e.detail.node.data.message.id;
|
||||
fitView({ nodes: [{ id: selectedMessageId }] });
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
Loading…
Reference in New Issue