mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Add terminal screen
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))
|
||||
@@ -1424,6 +1427,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:
|
||||
@@ -4816,6 +4822,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
|
||||
|
||||
@@ -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>
|
||||
@@ -39,6 +39,7 @@
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = 'none';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = 'none';
|
||||
}
|
||||
|
||||
function resize(event) {
|
||||
@@ -55,6 +56,7 @@
|
||||
window.removeEventListener('mousemove', resize);
|
||||
window.removeEventListener('mouseup', stopResize);
|
||||
document.body.style.userSelect = '';
|
||||
document.getElementById('preview-iframe').style.pointerEvents = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -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