mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'mgn-dv' of https://github.com/appwrite/console into mgn-dv
This commit is contained in:
@@ -78,13 +78,9 @@
|
||||
) as Artifact[];
|
||||
$: selectedOrg = organizations.find((organization) => organization.isSelected);
|
||||
$: selectedProject = selectedOrg?.projects.find((project) => project.isSelected);
|
||||
$: selectedArtifact = displayArtifacts.find(
|
||||
(artifact) => $page.params.artifact === artifact.$id
|
||||
) as Artifact;
|
||||
|
||||
let organisationBottomSheetOpen = false;
|
||||
let projectsBottomSheetOpen = false;
|
||||
let artifactsBottomSheetOpen = false;
|
||||
|
||||
function createOrg() {
|
||||
trackEvent(Click.OrganizationClickCreate, { source: 'breadcrumbs' });
|
||||
@@ -192,54 +188,6 @@
|
||||
: undefined
|
||||
};
|
||||
|
||||
let artifactsBottomSheet: SheetMenu;
|
||||
$: artifactsBottomSheet = {
|
||||
top:
|
||||
displayArtifacts.length > 1
|
||||
? {
|
||||
title: 'Switch artifact',
|
||||
items: !selectedOrg
|
||||
? []
|
||||
: displayArtifacts
|
||||
.map((artifact, index) => {
|
||||
if (index < 4) {
|
||||
return {
|
||||
name: artifact.name,
|
||||
href: `${base}/project-${selectedProject.$id}/studio/artifact-${artifact.$id}`
|
||||
};
|
||||
} else if (index === 4) {
|
||||
return {
|
||||
name: 'All artifacts',
|
||||
href: `${base}/project-${selectedProject.$id}/studio`
|
||||
};
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((project) => project !== null)
|
||||
}
|
||||
: {
|
||||
items: [
|
||||
{
|
||||
name: 'Create project',
|
||||
trailingIcon: IconPlus,
|
||||
href: `${base}/organization-${selectedOrg?.$id}?create-project`
|
||||
}
|
||||
]
|
||||
},
|
||||
bottom:
|
||||
selectedOrg?.projects.length > 1
|
||||
? {
|
||||
items: [
|
||||
{
|
||||
name: 'Create project',
|
||||
trailingIcon: IconPlus,
|
||||
href: `${base}/organization-${selectedOrg?.$id}?create-project`
|
||||
}
|
||||
]
|
||||
}
|
||||
: undefined
|
||||
};
|
||||
|
||||
function onResize() {
|
||||
if ((organisationBottomSheetOpen || projectsBottomSheetOpen) && !$isSmallViewport) {
|
||||
organisationBottomSheetOpen = false;
|
||||
@@ -410,70 +358,11 @@
|
||||
</Card.Base>
|
||||
</div>
|
||||
{/if}
|
||||
{#if isStudio && displayArtifacts.length > 0 && selectedProject}
|
||||
<span class="breadcrumb-separator">/</span>
|
||||
{#if !$isSmallViewport}
|
||||
<button
|
||||
type="button"
|
||||
class="trigger"
|
||||
use:melt={$triggerArtifacts}
|
||||
aria-label="Open artifacts tab">
|
||||
<span class="projectName">{selectedArtifact?.name}</span>
|
||||
<Icon icon={IconChevronDown} size="s" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="trigger"
|
||||
on:click={() => (artifactsBottomSheetOpen = true)}
|
||||
aria-label="Open artifacts tab">
|
||||
<span class="projectName">{selectedArtifact.name}</span>
|
||||
<Icon icon={IconChevronDown} size="s" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<div class="menu" use:melt={$menuArtifacts}>
|
||||
<Card.Base padding="xxxs" shadow={true}>
|
||||
{#if displayArtifacts.length > 1}
|
||||
{#each displayArtifacts as artifact, index}
|
||||
{#if index < 4}
|
||||
<div use:melt={$itemArtifacts}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${selectedProject.$id}/studio/artifact-${artifact.$id}`}
|
||||
>{artifact.name}</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
{:else if index === 4}
|
||||
<div use:melt={$itemArtifacts}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
href={`${base}/project-${selectedProject.$id}/studio`}
|
||||
>All artifacts</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="separator" use:melt={$separatorArtifacts}></div>
|
||||
{/if}
|
||||
<div use:melt={$itemArtifacts}>
|
||||
<ActionMenu.Root>
|
||||
<ActionMenu.Item.Anchor
|
||||
leadingIcon={IconPlusSm}
|
||||
href={`${base}/project-${selectedProject.$id}/studio?create-project`}
|
||||
>Create artifact</ActionMenu.Item.Anchor
|
||||
></ActionMenu.Root>
|
||||
</div>
|
||||
</Card.Base>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<BottomSheet.Menu bind:isOpen={organisationBottomSheetOpen} menu={organizationsBottomSheet}
|
||||
></BottomSheet.Menu>
|
||||
<BottomSheet.Menu bind:isOpen={projectsBottomSheetOpen} menu={projectsBottomSheet}
|
||||
></BottomSheet.Menu>
|
||||
<BottomSheet.Menu bind:isOpen={artifactsBottomSheetOpen} menu={artifactsBottomSheet}
|
||||
></BottomSheet.Menu>
|
||||
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
IconArrowUp,
|
||||
IconChevronDown,
|
||||
IconChevronLeft,
|
||||
IconPaperClip,
|
||||
IconPaperClip
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import Conversation from './conversation.svelte';
|
||||
@@ -111,7 +111,9 @@
|
||||
color="--fgcolor-neutral-tertiary" /></Button.Button>
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<Divider />
|
||||
<div class="divider-wrapper">
|
||||
<Divider />
|
||||
</div>
|
||||
|
||||
<Conversation {parser} />
|
||||
{/if}
|
||||
@@ -206,20 +208,29 @@
|
||||
'.'
|
||||
'.';
|
||||
height: calc(100% - var(--space-4));
|
||||
background-color: var(--bgcolor-neutral-primary);
|
||||
width: 100%;
|
||||
|
||||
background-color: var(--bgcolor-neutral-primary);
|
||||
border: 1px solid var(--border-neutral);
|
||||
border-radius: var(--border-radius-m);
|
||||
|
||||
@media (min-width: 768px) {
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
padding: var(--base-8);
|
||||
}
|
||||
|
||||
.divider-wrapper {
|
||||
margin-inline-start: calc(-1 * var(--base-24));
|
||||
width: calc(100% + 30px);
|
||||
}
|
||||
|
||||
.is-visible .chat-content {
|
||||
@@ -228,8 +239,9 @@
|
||||
}
|
||||
|
||||
.input {
|
||||
border-top: 1px solid var(--border-neutral);
|
||||
border: 1px solid var(--border-neutral);
|
||||
border-radius: var(--border-radius-m);
|
||||
margin-block-end: var(--space-4);
|
||||
padding: var(--space-6);
|
||||
|
||||
textarea {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
import { base } from '$app/paths';
|
||||
import { isSmallViewport, isTabletViewport } from '$lib/stores/viewport';
|
||||
import type { ComponentType } from 'svelte';
|
||||
import { showChat } from '$lib/stores/chat';
|
||||
import { page } from '$app/state';
|
||||
|
||||
export let project;
|
||||
@@ -57,17 +56,6 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
{#if !$isSmallViewport}
|
||||
<ActionMenu.Item.Button
|
||||
on:click={() => {
|
||||
showChat.set(!$showChat);
|
||||
if ($isTabletViewport) {
|
||||
isOpen = false;
|
||||
}
|
||||
}}>
|
||||
<Icon icon={IconSparkles} />
|
||||
</ActionMenu.Item.Button>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
const { data } = message;
|
||||
term.write(data);
|
||||
});
|
||||
const observer = new ResizeObserver(([entry]) => {
|
||||
const observer = new ResizeObserver(() => {
|
||||
const { cols, rows } = fitAddon.proposeDimensions();
|
||||
if (term.cols === cols && term.rows === rows) return;
|
||||
term.resize(cols, rows);
|
||||
|
||||
@@ -23,7 +23,6 @@ export function getChatWidthFromPrefs(): number {
|
||||
}
|
||||
export function saveChatWidthToPrefs(position: number) {
|
||||
const currentPrefs = userPreferences();
|
||||
|
||||
const newPrefs = {
|
||||
...currentPrefs,
|
||||
studioChatWidth: position
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
let resizerLeftPosition = $state(
|
||||
page.data?.subNavigation ? getChatWidthFromPrefs() + 24 : getChatWidthFromPrefs()
|
||||
);
|
||||
let resizerLeftOffset = $state(page.data?.subNavigation ? 52 : 52);
|
||||
let resizerLeftOffset = $state(page.data?.subNavigation ? 52 : 60);
|
||||
let chatWidth = $derived(resizerLeftPosition - resizerLeftOffset);
|
||||
|
||||
$effect(() => {
|
||||
@@ -145,18 +145,27 @@
|
||||
: window.innerWidth - 460;
|
||||
|
||||
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);
|
||||
@@ -336,22 +345,23 @@
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
{:else}
|
||||
<Layout.Stack direction="row" gap="l">
|
||||
{#if hasProjectSidebar}
|
||||
<Chat {parser} width={chatWidth} hasSubNavigation={false} />
|
||||
{#if $showChat}
|
||||
<div
|
||||
role="presentation"
|
||||
class="resizer"
|
||||
style:left={`${resizerLeftPosition}px`}
|
||||
bind:this={resizer}
|
||||
onmousedown={startResize}
|
||||
ontouchmove={startResize}>
|
||||
</div>
|
||||
<Card.Base padding="xxs">
|
||||
<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}
|
||||
{/if}
|
||||
|
||||
<Card.Base>
|
||||
{@const Header = page.data.header}
|
||||
{#if page.data.subNavigation}
|
||||
{@const SubNavigation = page.data.subNavigation}
|
||||
@@ -372,8 +382,8 @@
|
||||
{@render children()}
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
</Card.Base>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card.Base>
|
||||
{/if}
|
||||
</div>
|
||||
{#if hasProjectSidebar}
|
||||
@@ -472,7 +482,7 @@
|
||||
|
||||
.studio-content {
|
||||
min-height: calc(100vh - 54px);
|
||||
margin-top: 54px;
|
||||
margin-top: 32px;
|
||||
width: 100vw;
|
||||
|
||||
padding-right: var(--space-4);
|
||||
@@ -481,6 +491,7 @@
|
||||
@media (min-width: 1024px) {
|
||||
width: calc(100vw - 200px);
|
||||
margin-left: 200px;
|
||||
margin-top: 54px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@@ -497,28 +508,31 @@
|
||||
cursor: col-resize;
|
||||
margin-inline: 10px;
|
||||
position: absolute;
|
||||
height: calc(100vh - 82px);
|
||||
margin-block-start: 6px;
|
||||
|
||||
margin-inline-start: 0px;
|
||||
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: 2px;
|
||||
width: 1px;
|
||||
margin-left: -1px;
|
||||
left: 8px;
|
||||
background-color: var(--border-neutral-strong);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
background-color: var(--border-neutral);
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
opacity: 1;
|
||||
width: 2px;
|
||||
background-color: var(--border-neutral-strong);
|
||||
}
|
||||
}
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.only-mobile-tablet {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Layout, Typography, Divider, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { Layout, Typography, Divider, Icon, Button } from '@appwrite.io/pink-svelte';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { page } from '$app/state';
|
||||
import { Tab, Tabs, Terminal } from '$lib/components';
|
||||
@@ -15,6 +15,8 @@
|
||||
saveTerminalOpenToPrefs
|
||||
} from '$lib/helpers/studioLayout';
|
||||
import { synapse } from '$lib/components/studio/synapse.svelte';
|
||||
import { showChat } from '$lib/stores/chat';
|
||||
import { default as IconChatLayout } from '../assets/chat-layout.svelte';
|
||||
|
||||
const { children } = $props();
|
||||
|
||||
@@ -91,20 +93,32 @@
|
||||
|
||||
<Layout.Stack
|
||||
direction="column"
|
||||
height={$isSmallViewport ? 'calc(100vh - 218px)' : 'calc(100vh - 120px)'}
|
||||
height={$isSmallViewport ? 'calc(100vh - 218px)' : 'calc(100vh - 88px)'}
|
||||
gap="none">
|
||||
<Layout.Stack direction="column">
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
<Divider />
|
||||
<Layout.Stack direction="column" gap="none">
|
||||
<Layout.Stack gap="xxs" direction="row" alignItems="center">
|
||||
{#if !$showChat}
|
||||
<Button.Button
|
||||
variant="compact"
|
||||
color="--fgcolor-neutral-secondary"
|
||||
on:click={() => {
|
||||
showChat.set(true);
|
||||
}}><Icon icon={IconChatLayout} size="l"></Icon></Button.Button>
|
||||
{/if}
|
||||
<Tabs>
|
||||
{#each tabs as tab}
|
||||
<Tab
|
||||
href={tab.href}
|
||||
selected={isTabSelected(tab, page.url.pathname, path, tabs)}
|
||||
event={tab.event}>
|
||||
{tab.title}
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</Layout.Stack>
|
||||
<div class="divider-wrapper">
|
||||
<Divider />
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
{@render children()}
|
||||
<aside bind:this={asideRef}>
|
||||
@@ -131,9 +145,8 @@
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
|
||||
margin-inline-start: -25px;
|
||||
margin-block-end: calc(-1 * var(--space-9));
|
||||
margin-block-end: calc(-1 * var(--base-8));
|
||||
padding: var(--space-3);
|
||||
border-bottom-left-radius: var(--border-radius-m);
|
||||
border-bottom-right-radius: var(--border-radius-m);
|
||||
|
||||
border: 1px solid var(--border-neutral);
|
||||
@@ -143,8 +156,8 @@
|
||||
bottom: 116px;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
width: calc(100% + 2 * var(--space-9));
|
||||
margin-inline-start: calc(-1 * var(--space-9));
|
||||
width: calc(100% + var(--base-16));
|
||||
margin-inline-start: calc(-1 * var(--space-4));
|
||||
position: static;
|
||||
border: 0;
|
||||
}
|
||||
@@ -156,4 +169,11 @@
|
||||
.terminal-slider {
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.divider-wrapper {
|
||||
margin-block-start: 7.5px;
|
||||
margin-inline-start: -9px;
|
||||
margin-block-end: 8px;
|
||||
width: calc(100% + 18px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
width: calc(100% + 2 * var(--space-9));
|
||||
width: calc(100% + 2 * var(--base-8));
|
||||
height: 100%;
|
||||
margin-inline-start: calc(-1 * var(--space-9));
|
||||
margin-inline-start: calc(-1 * var(--base-8));
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6.00488 16.2957C6.05278 16.7348 6.42703 17.0841 6.89746 17.1288L7 17.1333H17L17.1025 17.1288C17.573 17.0841 17.9472 16.7348 17.9951 16.2957L18 16.2V7.8C18 7.31683 17.6067 6.91913 17.1025 6.87122L17 6.86667V5C18.6569 5 20 6.2536 20 7.8V16.2C20 17.7464 18.6569 19 17 19H7C5.34315 19 4 17.7464 4 16.2V7.8C4 6.2536 5.34315 5 7 5V6.86667L6.89746 6.87122C6.39333 6.91913 6 7.31684 6 7.8V16.2L6.00488 16.2957ZM17 5V6.86667H7V5H17Z"
|
||||
fill="currentcolor" />
|
||||
<path d="M11 6V18H9V6H11Z" fill="currentcolor" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 635 B |
Reference in New Issue
Block a user