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:
@@ -29,6 +29,7 @@
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"ai": "^2.2.37",
|
||||
"analytics": "^0.8.16",
|
||||
"cron-parser": "^4.9.0",
|
||||
|
||||
Generated
+8
@@ -32,6 +32,9 @@ importers:
|
||||
'@stripe/stripe-js':
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0
|
||||
'@xterm/xterm':
|
||||
specifier: ^5.5.0
|
||||
version: 5.5.0
|
||||
ai:
|
||||
specifier: ^2.2.37
|
||||
version: 2.2.37(react@18.3.1)(solid-js@1.9.5)(svelte@5.25.3)(vue@3.5.13(typescript@5.8.2))
|
||||
@@ -1439,6 +1442,9 @@ packages:
|
||||
'@vue/shared@3.5.13':
|
||||
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
||||
|
||||
'@xterm/xterm@5.5.0':
|
||||
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
|
||||
|
||||
acorn-import-attributes@1.9.5:
|
||||
resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
|
||||
peerDependencies:
|
||||
@@ -5007,6 +5013,8 @@ snapshots:
|
||||
|
||||
'@vue/shared@3.5.13': {}
|
||||
|
||||
'@xterm/xterm@5.5.0': {}
|
||||
|
||||
acorn-import-attributes@1.9.5(acorn@8.14.1):
|
||||
dependencies:
|
||||
acorn: 8.14.1
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
import { IconArrowUp, IconChevronLeft, IconPaperClip } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
export let showChat;
|
||||
export let width;
|
||||
</script>
|
||||
|
||||
<section class="chat" class:is-visible={showChat}>
|
||||
<section class="chat" style:width={showChat ? `${width}px` : 0} class:is-visible={showChat}>
|
||||
<Card.Base>
|
||||
<div class="chat-content">
|
||||
<Layout.Stack height="calc(100vh - 120px)" justifyContent="space-between">
|
||||
@@ -49,7 +50,8 @@
|
||||
</div>
|
||||
</Card.Base>
|
||||
</section>
|
||||
<div class="chat-placeholder" class:is-visible={showChat}></div>
|
||||
<div class="chat-placeholder" class:is-visible={showChat} style:width={showChat ? `${width}px` : 0}>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.chat {
|
||||
@@ -57,19 +59,11 @@
|
||||
position: fixed;
|
||||
top: 48px;
|
||||
overflow: hidden;
|
||||
transition: width 0.3s ease-in-out;
|
||||
|
||||
&.is-visible {
|
||||
width: 400px;
|
||||
}
|
||||
//transition: width 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.chat-placeholder {
|
||||
width: 0;
|
||||
transition: width 0.3s ease-in-out;
|
||||
&.is-visible {
|
||||
width: 600px;
|
||||
}
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chat-content {
|
||||
|
||||
@@ -82,3 +82,4 @@ export { default as BottomSheet } from './bottom-sheet/index';
|
||||
export { default as Confirm } from './confirm.svelte';
|
||||
export { default as UsageCard } from './usageCard.svelte';
|
||||
export { default as Chat } from './chat.svelte';
|
||||
export { default as Terminal } from './studio/terminal.svelte';
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<script>
|
||||
// const xtermPkg = require('@xterm/xterm');
|
||||
// const { Terminal } = xtermPkg;
|
||||
// import { onMount } from 'svelte';
|
||||
// import '@xterm/xterm/css/xterm.css';
|
||||
//
|
||||
// let terminalContainer;
|
||||
export let height;
|
||||
//
|
||||
// onMount(() => {
|
||||
// const term = new Terminal();
|
||||
// term.open(terminalContainer);
|
||||
// term.write('Hello from xterm.js!\r\n');
|
||||
// });
|
||||
</script>
|
||||
|
||||
<div class="terminal-container" style:height={`${height}px`}>
|
||||
<!-- <div bind:this={terminalContainer}></div>-->
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.terminal-container {
|
||||
padding: var(--space-3);
|
||||
border-radius: var(--border-radius-xsmall);
|
||||
background-color: black;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
@@ -28,6 +28,36 @@
|
||||
});
|
||||
|
||||
$: showChat = !showChat ? $page.url.pathname.endsWith('builder') : showChat;
|
||||
|
||||
let resizer;
|
||||
let resizerLeftPosition = 500;
|
||||
let chatWidth = resizerLeftPosition - 52;
|
||||
let isResizing = false;
|
||||
|
||||
function startResize(event) {
|
||||
isResizing = true;
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = 'none';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
function resize(event) {
|
||||
if (!isResizing) return;
|
||||
|
||||
if (resizer) {
|
||||
resizerLeftPosition = event.clientX;
|
||||
chatWidth = event.clientX - 52;
|
||||
}
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
isResizing = false;
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = '';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
@@ -39,9 +69,17 @@
|
||||
</Layout.Stack>
|
||||
</header>
|
||||
<div class="studio-content" class:project-sidebar={hasProjectSidebar}>
|
||||
<Layout.Stack direction="row">
|
||||
<Layout.Stack direction="row" gap="l">
|
||||
{#if hasProjectSidebar}
|
||||
<Chat bind:showChat />
|
||||
<Chat bind:showChat bind:width={chatWidth} />
|
||||
{#if showChat}
|
||||
<div
|
||||
class="resizer"
|
||||
style:left={`${resizerLeftPosition}px`}
|
||||
bind:this={resizer}
|
||||
on:mousedown={startResize}>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<Card.Base>
|
||||
@@ -87,4 +125,37 @@
|
||||
margin-left: 52px;
|
||||
}
|
||||
}
|
||||
|
||||
.resizer {
|
||||
width: 7px;
|
||||
margin-inline: 10px;
|
||||
position: absolute;
|
||||
height: calc(100vh - 82px);
|
||||
margin-block-start: 6px;
|
||||
cursor: col-resize;
|
||||
margin-inline-start: 8px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background-color: var(--border-neutral);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
margin-left: -1px;
|
||||
background-color: var(--border-neutral-strong);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
type Profile = {
|
||||
hasChatLayout: boolean;
|
||||
hasAuth: boolean;
|
||||
hasDatabases: boolean;
|
||||
hasFunctions: boolean;
|
||||
hasMessages: boolean;
|
||||
hasStorage: boolean;
|
||||
hasSites: boolean;
|
||||
};
|
||||
|
||||
export const ConsoleCloudProfile: Profile = {
|
||||
hasChatLayout: false,
|
||||
hasAuth: true,
|
||||
hasDatabases: true,
|
||||
hasFunctions: true,
|
||||
hasMessages: true,
|
||||
hasStorage: true,
|
||||
hasSites: true
|
||||
};
|
||||
|
||||
export const ConsoleSelfhostedProfile: Profile = {
|
||||
hasChatLayout: false,
|
||||
hasAuth: true,
|
||||
hasDatabases: true,
|
||||
hasFunctions: true,
|
||||
hasMessages: true,
|
||||
hasStorage: true,
|
||||
hasSites: true
|
||||
};
|
||||
|
||||
export const StudioProfile: Profile = {
|
||||
hasChatLayout: true,
|
||||
hasAuth: true,
|
||||
hasDatabases: true,
|
||||
hasFunctions: false,
|
||||
hasMessages: true,
|
||||
hasStorage: true,
|
||||
hasSites: true
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { dev } from '$app/environment';
|
||||
import { ConsoleCloudProfile, ConsoleSelfhostedProfile, StudioProfile } from '$lib/profiles';
|
||||
|
||||
export const enum Mode {
|
||||
CLOUD = 'cloud',
|
||||
@@ -26,6 +27,16 @@ export const ENV = {
|
||||
TEST: !!import.meta.env?.VITEST
|
||||
};
|
||||
|
||||
function getProfile() {
|
||||
if (PROFILE === Profile.STUDIO) {
|
||||
return StudioProfile;
|
||||
} else if (MODE === Mode.CLOUD) {
|
||||
return ConsoleCloudProfile;
|
||||
} else {
|
||||
return ConsoleSelfhostedProfile;
|
||||
}
|
||||
}
|
||||
|
||||
export const MODE = VARS.CONSOLE_MODE === Mode.CLOUD ? Mode.CLOUD : Mode.SELF_HOSTED;
|
||||
export const PROFILE = VARS.PROJECT_PROFILE === Profile.CONSOLE ? Profile.CONSOLE : Profile.STUDIO;
|
||||
export const isCloud = MODE === Mode.CLOUD;
|
||||
@@ -35,3 +46,4 @@ export const isDev = ENV.DEV;
|
||||
export const isProd = ENV.PROD;
|
||||
export const hasStripePublicKey = !!VARS.PUBLIC_STRIPE_KEY;
|
||||
export const GRACE_PERIOD_OVERRIDE = false;
|
||||
export const consoleProfile = getProfile();
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
import { openMigrationWizard } from './(migration-wizard)';
|
||||
import { project } from './project-[project]/store';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import { hasStripePublicKey, isCloud, isStudio, VARS } from '$lib/system';
|
||||
import { consoleProfile, hasStripePublicKey, isCloud, isStudio, VARS } from '$lib/system';
|
||||
import { stripe } from '$lib/stores/stripe';
|
||||
import MobileSupportModal from './wizard/support/mobileSupportModal.svelte';
|
||||
import { showSupportModal } from './wizard/support/store';
|
||||
@@ -330,7 +330,7 @@
|
||||
</script>
|
||||
|
||||
<CommandCenter />
|
||||
{#if isStudio}
|
||||
{#if consoleProfile.hasChatLayout}
|
||||
<ShellStudio bind:loadedProjects><slot /></ShellStudio>
|
||||
{:else}
|
||||
<Shell
|
||||
|
||||
@@ -1,5 +1,140 @@
|
||||
<script lang="ts">
|
||||
import { Layout, Card, Typography, Divider } from '@appwrite.io/pink-svelte';
|
||||
import { Layout, Card, Typography, Divider, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { isTabSelected } from '$lib/helpers/load';
|
||||
import { page } from '$app/state';
|
||||
import { Tab, Tabs, Terminal } from '$lib/components';
|
||||
import { base } from '$app/paths';
|
||||
import {
|
||||
IconChevronDoubleUp,
|
||||
IconDeviceMobile,
|
||||
IconExternalLink,
|
||||
IconRefresh,
|
||||
IconTerminal
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import { InputText } from '$lib/elements/forms/index.js';
|
||||
|
||||
const frameUrl = 'https://app.posthog.com/embedded/fEjuk1e61iQjYiRjODPDjpbsyhIfpg';
|
||||
|
||||
const projectId = page.params.project;
|
||||
const path = `${base}/project-${projectId}/builder`;
|
||||
const tabs = [
|
||||
{
|
||||
href: path,
|
||||
title: 'Preview',
|
||||
event: 'preview',
|
||||
hasChildren: true
|
||||
},
|
||||
{
|
||||
href: `${path}/code`,
|
||||
title: 'Code',
|
||||
event: 'code',
|
||||
hasChildren: true
|
||||
}
|
||||
];
|
||||
|
||||
let terminalOpen;
|
||||
let asideRef;
|
||||
let isResizing = false;
|
||||
let terminalHeight = 300;
|
||||
|
||||
function startResize(event) {
|
||||
isResizing = true;
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = 'none';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
function resize(event) {
|
||||
if (!isResizing) return;
|
||||
|
||||
terminalHeight =
|
||||
asideRef.getBoundingClientRect().y -
|
||||
event.clientY +
|
||||
asideRef.getBoundingClientRect().height -
|
||||
32;
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
isResizing = false;
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = '';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row">Iframe</Layout.Stack>
|
||||
<Layout.Stack direction="column" height="calc(100vh - 120px)" 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="row" alignItems="center">
|
||||
<Icon icon={IconRefresh} color="--fgcolor-neutral-tertiary" />
|
||||
<InputText id="previewUrl" value={frameUrl} />
|
||||
<Icon icon={IconDeviceMobile} color="--fgcolor-neutral-tertiary" />
|
||||
<Icon icon={IconExternalLink} color="--fgcolor-neutral-tertiary" />
|
||||
</Layout.Stack>
|
||||
<div></div>
|
||||
</Layout.Stack>
|
||||
<div class="iframe-container">
|
||||
<iframe src={frameUrl} id="preview-iframe"></iframe>
|
||||
</div>
|
||||
<div></div>
|
||||
{#if terminalOpen}<div class="terminal-slider" on:mousedown={startResize}></div>
|
||||
{/if}
|
||||
<aside bind:this={asideRef}>
|
||||
<details bind:open={terminalOpen}>
|
||||
<summary>
|
||||
<Layout.Stack direction="row" justify="space-between">
|
||||
<Layout.Stack direction="row" alignItems="center" gap="xs">
|
||||
<Icon icon={IconTerminal} color="--fgcolor-neutral-tertiary" />
|
||||
<Typography.Text>Terminal</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Icon icon={IconChevronDoubleUp} color="--fgcolor-neutral-tertiary" />
|
||||
</Layout.Stack>
|
||||
</summary>
|
||||
<Terminal bind:height={terminalHeight}></Terminal>
|
||||
</details>
|
||||
</aside>
|
||||
</Layout.Stack>
|
||||
|
||||
<style>
|
||||
aside {
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
width: calc(100% + 2 * var(--space-9));
|
||||
margin-inline-start: calc(-1 * var(--space-9));
|
||||
margin-block-end: calc(-1 * var(--space-9));
|
||||
padding: var(--space-3);
|
||||
border-bottom-left-radius: var(--border-radius-m);
|
||||
border-bottom-right-radius: var(--border-radius-m);
|
||||
}
|
||||
summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
.iframe-container {
|
||||
flex-grow: 1;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
width: calc(100% + 2 * var(--space-9));
|
||||
height: 100%;
|
||||
margin-inline-start: calc(-1 * var(--space-9));
|
||||
}
|
||||
|
||||
.terminal-slider {
|
||||
height: 4px;
|
||||
background-color: var(--bgcolor-neutral-default);
|
||||
width: calc(100% + 2 * var(--space-9));
|
||||
margin-inline-start: calc(-1 * var(--space-9));
|
||||
cursor: row-resize;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user