diff --git a/package.json b/package.json index d8a0ffb4f..f5754d5d0 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd6aa9838..5b69bf151 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/src/lib/components/chat.svelte b/src/lib/components/chat.svelte index c8877ba23..758fd570f 100644 --- a/src/lib/components/chat.svelte +++ b/src/lib/components/chat.svelte @@ -3,9 +3,10 @@ import { IconArrowUp, IconChevronLeft, IconPaperClip } from '@appwrite.io/pink-icons-svelte'; export let showChat; + export let width; -
+
@@ -49,7 +50,8 @@
-
+
+
diff --git a/src/lib/layout/shellStudio.svelte b/src/lib/layout/shellStudio.svelte index ef92f4812..31586e16c 100644 --- a/src/lib/layout/shellStudio.svelte +++ b/src/lib/layout/shellStudio.svelte @@ -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 = ''; + }
@@ -39,9 +69,17 @@
- + {#if hasProjectSidebar} - + + {#if showChat} +
+
+ {/if} {/if} @@ -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; + } + } diff --git a/src/lib/profiles.ts b/src/lib/profiles.ts new file mode 100644 index 000000000..4fb91b0a9 --- /dev/null +++ b/src/lib/profiles.ts @@ -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 +}; diff --git a/src/lib/system.ts b/src/lib/system.ts index 8cd3a70f3..e3808b221 100644 --- a/src/lib/system.ts +++ b/src/lib/system.ts @@ -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(); diff --git a/src/routes/(console)/+layout.svelte b/src/routes/(console)/+layout.svelte index 21230e6ff..184e5473f 100644 --- a/src/routes/(console)/+layout.svelte +++ b/src/routes/(console)/+layout.svelte @@ -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 @@ -{#if isStudio} +{#if consoleProfile.hasChatLayout} {:else} - 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 = ''; + } -Iframe + + + + {#each tabs as tab} + + {tab.title} + + {/each} + + + + + + + + +
+
+
+ +
+
+ {#if terminalOpen}
+ {/if} + +
+ +