add directory state to console (#416)

This commit is contained in:
Saeed Vaziry
2025-01-01 14:52:27 -08:00
committed by GitHub
parent ba069a2db0
commit f5c9d6701b
7 changed files with 149 additions and 50 deletions

View File

@ -1,5 +1,5 @@
<div
{{ $attributes->merge(["class" => "font-mono whitespace-pre relative h-[500px] w-full overflow-auto whitespace-pre-line rounded-xl border border-gray-200 bg-black p-5 text-gray-50 dark:border-gray-800"]) }}
{{ $attributes->merge(["class" => "font-mono relative h-[500px] w-full overflow-auto whitespace-pre-line rounded-xl border border-gray-200 bg-black p-5 text-gray-50 dark:border-gray-800"]) }}
>
{{ $slot }}
</div>

View File

@ -2,13 +2,48 @@
x-data="{
user: '{{ $server->ssh_user }}',
running: false,
dir: '{{ cache()->get("console.$server->id.dir", "~") }}',
command: '',
output: '',
serverName: '{{ $server->name }}',
shellPrefix: '',
clearAfterCommand: false,
runUrl: '{{ route("servers.console.run", ["server" => $server]) }}',
init() {
this.setShellPrefix()
$watch('user', async (value) => {
await this.getWorkingDir()
})
const consoleOutput = document.getElementById('console-output')
consoleOutput.addEventListener('mouseup', (event) => {
if (window.getSelection()?.toString()) {
return
}
this.focusCommand()
})
this.focusCommand()
document.addEventListener('keydown', (event) => {
if (event.ctrlKey && event.key === 'l') {
event.preventDefault()
if (this.running) return
this.output = ''
}
})
},
async run() {
if (! this.command) return
this.running = true
this.output = this.command + '\n'
let output = this.shellPrefix + ' ' + this.command + '\n'
if (this.clearAfterCommand) {
this.output = output
} else {
this.output += output
}
setTimeout(() => {
document.getElementById('console-output').scrollTop =
document.getElementById('console-output').scrollHeight
}, 100)
const fetchOptions = {
method: 'POST',
headers: {
@ -25,6 +60,7 @@
const response = await fetch(this.runUrl, fetchOptions)
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
this.setShellPrefix()
while (true) {
if (! this.running) {
@ -39,65 +75,91 @@
this.output += textChunk
document.getElementById('console-output').scrollTop =
document.getElementById('console-output').scrollHeight
setTimeout(() => {
document.getElementById('console-output').scrollTop =
document.getElementById('console-output').scrollHeight
}, 100)
}
this.output += '\nDone!'
this.output += '\n'
await this.getWorkingDir()
this.running = false
setTimeout(() => {
document.getElementById('command').focus()
}, 100)
},
stop() {
this.running = false
},
setShellPrefix() {
this.shellPrefix = `${this.user}@${this.serverName}:${this.dir}$`
},
focusCommand() {
document.getElementById('command').focus()
},
async getWorkingDir() {
const response = await fetch(
'{{ route("servers.console.working-dir", ["server" => $server]) }}',
)
if (response.ok) {
const data = await response.json()
this.dir = data.dir
this.setShellPrefix()
}
},
}"
>
<div>
<x-console-view id="console-output">
<div class="w-full" x-text="output"></div>
</x-console-view>
<form onsubmit="return false" id="console-form" class="mt-5 flex items-center justify-between">
<div class="grid w-full grid-cols-8 gap-2">
<x-filament::input.wrapper class="col-span-1 w-full">
<x-filament::input.select
id="user"
name="user"
x-model="user"
class="w-full"
x-bind:disabled="running"
>
<option value="root">root</option>
<option value="{{ $server->ssh_user }}">{{ $server->ssh_user }}</option>
</x-filament::input.select>
</x-filament::input.wrapper>
<x-filament::input.wrapper class="col-span-6 w-full">
<x-filament::input
id="command"
name="command"
x-model="command"
type="text"
placeholder="Type your command here..."
class="mx-1 flex-grow"
autocomplete="off"
/>
</x-filament::input.wrapper>
<div class="relative">
<form class="flex items-center justify-between">
<x-filament::input.wrapper>
<x-filament::input.select id="user" name="user" x-model="user" class="w-full" x-bind:disabled="running">
<option value="root">root</option>
<option value="{{ $server->ssh_user }}">{{ $server->ssh_user }}</option>
</x-filament::input.select>
</x-filament::input.wrapper>
<div class="flex items-center">
<x-filament::button
color="gray"
icon="heroicon-o-play"
type="submit"
id="btn-run"
x-on:click="run"
class="col-span-1"
type="button"
x-on:click="output = ''"
icon="heroicon-o-trash"
tooltip="Clear"
x-show="!running"
/>
>
Clear
</x-filament::button>
<x-filament::button
color="gray"
type="button"
id="btn-stop"
x-on:click="stop"
class="col-span-1"
x-show="running"
icon="heroicon-o-stop"
/>
tooltip="Stop"
class="ml-2"
>
Stop
</x-filament::button>
</div>
</form>
<x-console-view id="console-output" class="mt-5">
<div class="w-full" x-text="output"></div>
</x-console-view>
<div
class="relative -mt-5 flex h-[50px] w-full items-center rounded-b-xl border-b border-l border-r border-gray-200 bg-black px-5 font-mono text-gray-50 dark:border-gray-800"
>
<form class="flex w-full items-center" x-show="!running" onsubmit="return false" id="console-form">
<div x-text="shellPrefix"></div>
<input
type="text"
class="h-5 flex-grow border-0 bg-transparent p-0 px-1 outline-none ring-0 focus:outline-none focus:ring-0"
autofocus
id="command"
name="command"
x-model="command"
autocomplete="off"
/>
<button type="submit" id="btn-run" x-on:click="run" class="hidden"></button>
</form>
</div>
</div>
</div>