From 00a3a039cfb54231199274237154a7c29fe5e6ff Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 12 May 2025 12:04:31 +0200 Subject: [PATCH] Add terminal tabs with read-only main terminal --- src/lib/components/studio/synapse.svelte.ts | 3 +- src/lib/components/studio/terminal.svelte | 10 ++++- .../studio/artifact-[artifact]/+layout.svelte | 41 ++++++++++++++++++- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/lib/components/studio/synapse.svelte.ts b/src/lib/components/studio/synapse.svelte.ts index bba3130af..164df82ae 100644 --- a/src/lib/components/studio/synapse.svelte.ts +++ b/src/lib/components/studio/synapse.svelte.ts @@ -170,4 +170,5 @@ export class Synapse { } } -export const synapse = new Synapse('wss://terminal.appwrite.torsten.work'); +export const endpoint = 'wss://terminal.appwrite.torsten.work'; +export const synapse = new Synapse(endpoint); diff --git a/src/lib/components/studio/terminal.svelte b/src/lib/components/studio/terminal.svelte index a4db94a5b..882f444ae 100644 --- a/src/lib/components/studio/terminal.svelte +++ b/src/lib/components/studio/terminal.svelte @@ -7,8 +7,9 @@ type Props = { height: number; synapse: Synapse; + readonly?: boolean; }; - let { height, synapse }: Props = $props(); + let { height, synapse, readonly = false }: Props = $props(); const terminal: Action = (node) => { const init = async () => { @@ -26,7 +27,12 @@ const fitAddon = new FitAddon(); term.loadAddon(fitAddon); term.open(node); - term.onKey(({ key }) => { + term.onKey(({ key, domEvent }) => { + if (readonly) { + domEvent.preventDefault(); + domEvent.stopPropagation(); + return; + } synapse.dispatch('terminal', { operation: 'createCommand', params: { diff --git a/src/routes/(console)/project-[project]/studio/artifact-[artifact]/+layout.svelte b/src/routes/(console)/project-[project]/studio/artifact-[artifact]/+layout.svelte index 12831495c..2b8c8010b 100644 --- a/src/routes/(console)/project-[project]/studio/artifact-[artifact]/+layout.svelte +++ b/src/routes/(console)/project-[project]/studio/artifact-[artifact]/+layout.svelte @@ -25,11 +25,12 @@ saveTerminalHeightToPrefs, saveTerminalOpenToPrefs } from '$lib/helpers/studioLayout'; - import { synapse } from '$lib/components/studio/synapse.svelte'; + import { endpoint, Synapse, synapse } from '$lib/components/studio/synapse.svelte'; import { showChat } from '$lib/stores/chat'; import { default as IconChatLayout } from '../assets/chat-layout.svelte'; import { filesystem } from '$lib/components/editor/filesystem'; import { InputSelect } from '$lib/elements/forms/index.js'; + import { SvelteMap } from 'svelte/reactivity'; const { children } = $props(); @@ -51,6 +52,10 @@ hasChildren: true } ]; + const mainTerminalId = Symbol(); + const terminals = new SvelteMap(); + let currentTerminal: symbol = $state(mainTerminalId); + synapse.dispatch('synapse', { operation: 'updateWorkDir', params: { @@ -132,6 +137,12 @@ $previewFrameRef.style.pointerEvents = ''; } } + + function createTerminal() { + const symbol = Symbol(); + terminals.set(symbol, new Synapse(endpoint)); + currentTerminal = symbol; + } - + + + (currentTerminal = mainTerminalId)}> + Imagine + + {#each terminals as [symbol] (symbol)} + (currentTerminal = symbol)} + selected={currentTerminal === symbol}> + Terminal + + {/each} + + + + +
+ +
+ {#each terminals as [symbol, synapse] (symbol)} +
+ +
+ {/each}