feat: console component (#5685)

This commit is contained in:
Calum H.
2026-03-27 15:44:08 +00:00
committed by GitHub
parent fc87506745
commit 87122cf9bd
7 changed files with 572 additions and 108 deletions

View File

@@ -0,0 +1,101 @@
<template>
<div
class="flex size-full flex-col bg-surface-2 overflow-hidden rounded-[20px] border border-solid border-surface-4"
>
<div class="relative min-h-0 pb-1 flex-1 overflow-hidden">
<div ref="containerRef" class="size-full pl-2" />
<div v-if="!isAtBottom" class="absolute bottom-4 right-4">
<ButtonStyled circular type="highlight">
<button class="!shadow-none" aria-label="Scroll to bottom" @click="scrollToBottom">
<ChevronDownIcon />
</button>
</ButtonStyled>
</div>
</div>
<div
v-if="showInput"
class="border-t border-solid border-b-0 border-x-0 border-surface-5 bg-surface-3 p-4"
>
<StyledInput
v-model="commandInput"
:icon="TerminalSquareIcon"
placeholder="Send a command"
wrapper-class="w-full"
input-class="!h-10"
@keydown.enter="submitCommand"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ChevronDownIcon, TerminalSquareIcon } from '@modrinth/assets'
import type { Terminal } from '@xterm/xterm'
import { ref } from 'vue'
import ButtonStyled from '#ui/components/base/ButtonStyled.vue'
import StyledInput from '#ui/components/base/StyledInput.vue'
import { useTerminal } from '#ui/composables/terminal'
const props = withDefaults(
defineProps<{
scrollback?: number
showInput?: boolean
}>(),
{
scrollback: 10000,
showInput: false,
},
)
const emit = defineEmits<{
command: [command: string]
ready: [terminal: Terminal]
}>()
const containerRef = ref<HTMLElement | null>(null)
const commandInput = ref('')
const { terminal, searchAddon, isAtBottom, write, writeln, clear, reset, fit, scrollToBottom } =
useTerminal({
container: containerRef,
scrollback: props.scrollback,
onReady: (term) => emit('ready', term),
})
const submitCommand = () => {
const cmd = commandInput.value.trim()
if (!cmd) return
emit('command', cmd)
commandInput.value = ''
}
defineExpose({
write,
writeln,
clear,
reset,
fit,
scrollToBottom,
terminal,
searchAddon,
isAtBottom,
commandInput,
})
</script>
<style>
.xterm {
height: 100% !important;
}
.xterm .xterm-scrollable-element {
height: 100% !important;
}
.xterm .xterm-screen {
min-height: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
}
</style>

View File

@@ -5,6 +5,7 @@ export { default as AutoBrandIcon } from './AutoBrandIcon.vue'
export { default as AutoLink } from './AutoLink.vue'
export { default as Avatar } from './Avatar.vue'
export { default as Badge } from './Badge.vue'
export { default as BaseTerminal } from './BaseTerminal.vue'
export { default as BigOptionButton } from './BigOptionButton.vue'
export { default as BulletDivider } from './BulletDivider.vue'
export { default as Button } from './Button.vue'