mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Don't load Chat on every project page
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
<script lang="ts">
|
||||
import Chat from '$lib/components/studio/chat/chat.svelte';
|
||||
import { page } from '$app/state';
|
||||
import { conversation, showChat } from '$lib/stores/chat.js';
|
||||
import {
|
||||
disableBodySelect,
|
||||
enabledBodySelect,
|
||||
getChatWidthFromPrefs,
|
||||
saveChatWidthToPrefs
|
||||
} from '$lib/helpers/studioLayout.js';
|
||||
import { createStreamParser } from '$lib/components/studio/chat/parser.js';
|
||||
import { sdk } from '$lib/stores/sdk.js';
|
||||
import { synapse } from '$lib/components/studio/synapse.svelte.js';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { filesystem } from '$lib/components/editor/filesystem';
|
||||
import { previewFrameRef } from '$routes/(console)/project-[project]/store';
|
||||
|
||||
$effect(() => {
|
||||
if ($isSmallViewport || page.params.artifact) {
|
||||
showChat.set(true);
|
||||
}
|
||||
if (!page.params.artifact) {
|
||||
showChat.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
let resizer = $state(null);
|
||||
let resizerLeftPosition = $state(
|
||||
page.data?.subNavigation ? getChatWidthFromPrefs() + 24 : getChatWidthFromPrefs()
|
||||
);
|
||||
let resizerLeftOffset = $state(page.data?.subNavigation ? 52 : 9);
|
||||
let chatWidth = $derived(resizerLeftPosition - resizerLeftOffset);
|
||||
|
||||
$effect(() => {
|
||||
chatWidth = resizerLeftPosition - resizerLeftOffset;
|
||||
});
|
||||
|
||||
let isResizing = false;
|
||||
|
||||
function startResize() {
|
||||
isResizing = true;
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
window.addEventListener('touchmove', resize);
|
||||
window.addEventListener('touchend', stopResize);
|
||||
disableBodySelect();
|
||||
|
||||
if ($previewFrameRef) {
|
||||
$previewFrameRef.style.pointerEvents = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function resize(event: TouchEvent | MouseEvent) {
|
||||
if (!isResizing) return;
|
||||
if (resizer) {
|
||||
resizerLeftPosition =
|
||||
('touches' in event ? event.touches[0].clientX : event.clientX) - 60;
|
||||
const maxSize = page.data?.subNavigation
|
||||
? window.innerWidth - 660
|
||||
: window.innerWidth - 540;
|
||||
|
||||
const minSize = 320;
|
||||
if (resizerLeftPosition > maxSize) {
|
||||
resizerLeftPosition = maxSize;
|
||||
} else if (resizerLeftPosition < minSize) {
|
||||
if (resizerLeftPosition < minSize - 100) {
|
||||
showChat.set(false);
|
||||
}
|
||||
|
||||
resizerLeftPosition = minSize;
|
||||
} else if (resizerLeftPosition > minSize && !$showChat) {
|
||||
showChat.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
if (!$showChat) {
|
||||
resizerLeftPosition = 500;
|
||||
}
|
||||
const saveWidth = page.data?.subNavigation ? resizerLeftPosition - 24 : resizerLeftPosition;
|
||||
saveChatWidthToPrefs(saveWidth);
|
||||
|
||||
isResizing = false;
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
window.removeEventListener('touchmove', resize);
|
||||
window.removeEventListener('touchend', stopResize);
|
||||
enabledBodySelect();
|
||||
|
||||
if ($previewFrameRef) {
|
||||
$previewFrameRef.style.pointerEvents = '';
|
||||
}
|
||||
}
|
||||
|
||||
const parser = createStreamParser();
|
||||
|
||||
async function getConversation(artifactId: string) {
|
||||
parser.reset();
|
||||
const { conversations } = await sdk.forProject.imagine.listConversations(artifactId);
|
||||
if (conversations.length === 0) {
|
||||
const convo = sdk.forProject.imagine.createConversation(
|
||||
artifactId,
|
||||
`Conversation ${new Date().getTime()}`
|
||||
);
|
||||
conversation.set(convo);
|
||||
} else {
|
||||
conversation.set(conversations[0]);
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (page.params.artifact && $conversation.data?.artifactId !== page.params.artifact) {
|
||||
getConversation(page.params.artifact);
|
||||
}
|
||||
});
|
||||
|
||||
conversation.subscribe(async (convo) => {
|
||||
if (!convo.data) return;
|
||||
const { messages } = await sdk.forProject.imagine.listMessages(
|
||||
convo.data.artifactId,
|
||||
convo.data.$id
|
||||
);
|
||||
const text = messages.reduce((curr, next) => {
|
||||
return curr + next.content;
|
||||
}, '');
|
||||
parser.init(text);
|
||||
});
|
||||
|
||||
parser.on('complete', async (action) => {
|
||||
switch (action.type) {
|
||||
case 'file':
|
||||
await synapse.dispatch('fs', {
|
||||
operation: 'updateFile',
|
||||
params: {
|
||||
filepath: action.src,
|
||||
content: action.content
|
||||
}
|
||||
});
|
||||
$filesystem = [...$filesystem, action.src];
|
||||
break;
|
||||
case 'shell':
|
||||
synapse.dispatch('terminal', {
|
||||
operation: 'createCommand',
|
||||
params: {
|
||||
command: action.content + '\n'
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Chat {parser} width={chatWidth} hasSubNavigation={false} />
|
||||
|
||||
{#if !$isSmallViewport && ($showChat || isResizing)}
|
||||
<div
|
||||
role="presentation"
|
||||
class="resizer"
|
||||
style:left={`${resizerLeftPosition}px`}
|
||||
class:hidden={!$showChat}
|
||||
bind:this={resizer}
|
||||
onmousedown={startResize}
|
||||
ontouchmove={startResize}>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.resizer {
|
||||
width: 16px;
|
||||
cursor: col-resize;
|
||||
margin-inline: 10px;
|
||||
position: absolute;
|
||||
height: calc(100vh - 72px);
|
||||
margin-block-start: calc(-1 * var(--base-8));
|
||||
margin-inline-start: 0;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
cursor: col-resize;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
margin-left: -1px;
|
||||
left: 8px;
|
||||
background-color: var(--border-neutral);
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
width: 2px;
|
||||
background-color: var(--border-neutral-strong);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -16,11 +16,11 @@
|
||||
import { user } from '$lib/stores/user';
|
||||
import SidebarProject from '$lib/components/studio/sidebarProject.svelte';
|
||||
import SidebarOrganization from '$lib/components/studio/sidebarOrganization.svelte';
|
||||
import ChatWrapper from '$lib/components/studio/chatWrapper.svelte';
|
||||
import { BillingPlan } from '$lib/constants.js';
|
||||
import { isCloud } from '$lib/system.js';
|
||||
import { tierToPlan } from '$lib/stores/billing.js';
|
||||
import type { NavbarProject } from '$lib/components/navbar.svelte';
|
||||
import Chat from '$lib/components/studio/chat/chat.svelte';
|
||||
import {
|
||||
IconChevronRight,
|
||||
IconLogoutRight,
|
||||
@@ -35,18 +35,7 @@
|
||||
import { logout } from '$lib/helpers/logout';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import { conversation, showChat } from '$lib/stores/chat';
|
||||
import { createStreamParser } from '$lib/components/studio/chat/parser';
|
||||
import { type Snippet } from 'svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { filesystem } from '$lib/components/editor/filesystem';
|
||||
import { previewFrameRef } from '$routes/(console)/project-[project]/store';
|
||||
import {
|
||||
disableBodySelect,
|
||||
enabledBodySelect,
|
||||
getChatWidthFromPrefs,
|
||||
saveChatWidthToPrefs
|
||||
} from '$lib/helpers/studioLayout';
|
||||
import { synapse } from '$lib/components/studio/synapse.svelte';
|
||||
|
||||
let hasProjectSidebar = $state(false);
|
||||
|
||||
@@ -114,141 +103,6 @@
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if ($isSmallViewport || page.params.artifact) {
|
||||
showChat.set(true);
|
||||
}
|
||||
if (!page.params.artifact) {
|
||||
showChat.set(false);
|
||||
}
|
||||
});
|
||||
|
||||
let resizer = $state(null);
|
||||
let resizerLeftPosition = $state(
|
||||
page.data?.subNavigation ? getChatWidthFromPrefs() + 24 : getChatWidthFromPrefs()
|
||||
);
|
||||
let resizerLeftOffset = $state(page.data?.subNavigation ? 52 : 9);
|
||||
let chatWidth = $derived(resizerLeftPosition - resizerLeftOffset);
|
||||
|
||||
$effect(() => {
|
||||
chatWidth = resizerLeftPosition - resizerLeftOffset;
|
||||
});
|
||||
|
||||
let isResizing = false;
|
||||
|
||||
function startResize() {
|
||||
isResizing = true;
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
window.addEventListener('touchmove', resize);
|
||||
window.addEventListener('touchend', stopResize);
|
||||
disableBodySelect();
|
||||
|
||||
if ($previewFrameRef) {
|
||||
$previewFrameRef.style.pointerEvents = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function resize(event: TouchEvent | MouseEvent) {
|
||||
if (!isResizing) return;
|
||||
if (resizer) {
|
||||
resizerLeftPosition =
|
||||
('touches' in event ? event.touches[0].clientX : event.clientX) - 60;
|
||||
const maxSize = page.data?.subNavigation
|
||||
? window.innerWidth - 660
|
||||
: window.innerWidth - 540;
|
||||
|
||||
const minSize = 320;
|
||||
if (resizerLeftPosition > maxSize) {
|
||||
resizerLeftPosition = maxSize;
|
||||
} else if (resizerLeftPosition < minSize) {
|
||||
if (resizerLeftPosition < minSize - 100) {
|
||||
showChat.set(false);
|
||||
}
|
||||
|
||||
resizerLeftPosition = minSize;
|
||||
} else if (resizerLeftPosition > minSize && !$showChat) {
|
||||
showChat.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
if (!$showChat) {
|
||||
resizerLeftPosition = 500;
|
||||
}
|
||||
const saveWidth = page.data?.subNavigation ? resizerLeftPosition - 24 : resizerLeftPosition;
|
||||
saveChatWidthToPrefs(saveWidth);
|
||||
|
||||
isResizing = false;
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
window.removeEventListener('touchmove', resize);
|
||||
window.removeEventListener('touchend', stopResize);
|
||||
enabledBodySelect();
|
||||
|
||||
if ($previewFrameRef) {
|
||||
$previewFrameRef.style.pointerEvents = '';
|
||||
}
|
||||
}
|
||||
|
||||
const parser = createStreamParser();
|
||||
|
||||
async function getConversation(artifactId: string) {
|
||||
parser.reset();
|
||||
const { conversations } = await sdk.forProject.imagine.listConversations(artifactId);
|
||||
if (conversations.length === 0) {
|
||||
const convo = sdk.forProject.imagine.createConversation(
|
||||
artifactId,
|
||||
`Conversation ${new Date().getTime()}`
|
||||
);
|
||||
conversation.set(convo);
|
||||
} else {
|
||||
conversation.set(conversations[0]);
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (page.params.artifact && $conversation.data?.artifactId !== page.params.artifact) {
|
||||
getConversation(page.params.artifact);
|
||||
}
|
||||
});
|
||||
|
||||
conversation.subscribe(async (convo) => {
|
||||
if (!convo.data) return;
|
||||
const { messages } = await sdk.forProject.imagine.listMessages(
|
||||
convo.data.artifactId,
|
||||
convo.data.$id
|
||||
);
|
||||
const text = messages.reduce((curr, next) => {
|
||||
return curr + next.content;
|
||||
}, '');
|
||||
parser.init(text);
|
||||
});
|
||||
|
||||
parser.on('complete', async (action) => {
|
||||
switch (action.type) {
|
||||
case 'file':
|
||||
await synapse.dispatch('fs', {
|
||||
operation: 'updateFile',
|
||||
params: {
|
||||
filepath: action.src,
|
||||
content: action.content
|
||||
}
|
||||
});
|
||||
$filesystem = [...$filesystem, action.src];
|
||||
break;
|
||||
case 'shell':
|
||||
synapse.dispatch('terminal', {
|
||||
operation: 'createCommand',
|
||||
params: {
|
||||
command: action.content + '\n'
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<main>
|
||||
@@ -370,9 +224,10 @@
|
||||
class:project-sidebar={hasProjectSidebar}
|
||||
class:sub-navigation={page.data.subNavigation}>
|
||||
{#if $isSmallViewport}
|
||||
{#if hasProjectSidebar}
|
||||
<Chat {parser} width={chatWidth} hasSubNavigation={page.data?.subNavigation} />
|
||||
{#if isOnEditorPage}
|
||||
<ChatWrapper />
|
||||
{/if}
|
||||
|
||||
<Card.Base padding="xxs" radius={$isSmallViewport ? 'none' : 'm'}>
|
||||
<Layout.Stack>
|
||||
{#if page.data?.subNavigation}
|
||||
@@ -386,19 +241,8 @@
|
||||
<div class="studio-wrapper">
|
||||
<Card.Base padding={isOnEditorPage ? 'xxs' : 's'}>
|
||||
<Layout.Stack direction="row" gap={$showChat ? 'l' : 'none'}>
|
||||
{#if hasProjectSidebar}
|
||||
<Chat {parser} width={chatWidth} hasSubNavigation={false} />
|
||||
{#if $showChat || isResizing}
|
||||
<div
|
||||
role="presentation"
|
||||
class="resizer"
|
||||
style:left={`${resizerLeftPosition}px`}
|
||||
class:hidden={!$showChat}
|
||||
bind:this={resizer}
|
||||
onmousedown={startResize}
|
||||
ontouchmove={startResize}>
|
||||
</div>
|
||||
{/if}
|
||||
{#if isOnEditorPage}
|
||||
<ChatWrapper />
|
||||
{/if}
|
||||
|
||||
{@const Header = page.data.header}
|
||||
@@ -551,33 +395,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.resizer {
|
||||
width: 16px;
|
||||
cursor: col-resize;
|
||||
margin-inline: 10px;
|
||||
position: absolute;
|
||||
height: calc(100vh - 72px);
|
||||
margin-block-start: calc(-1 * var(--base-8));
|
||||
margin-inline-start: 0;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
cursor: col-resize;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
margin-left: -1px;
|
||||
left: 8px;
|
||||
background-color: var(--border-neutral);
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
width: 2px;
|
||||
background-color: var(--border-neutral-strong);
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user