1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-07-20 06:28:04 +00:00

Shell feature (#44)

Resolves #37: adds a shell tab where the user can write mongo shell
scripts and execute them.
This commit is contained in:
2023-07-01 21:34:32 +02:00
committed by GitHub
parent 62f4b88ea6
commit 61142844fa
12 changed files with 375 additions and 59 deletions

View File

@ -9,6 +9,7 @@
import Indexes from './indexes.svelte';
import Insert from './insert.svelte';
import Remove from './remove.svelte';
import Shell from '../shell.svelte';
import Stats from './stats.svelte';
import Update from './update.svelte';
@ -42,16 +43,19 @@
<div class="view" class:empty={!collection}>
{#if collection}
{#key collection}
<TabBar tabs={[
{ key: 'stats', icon: 'chart', title: 'Stats' },
{ key: 'find', icon: 'db', title: 'Find' },
{ key: 'insert', icon: '+', title: 'Insert' },
{ key: 'update', icon: 'edit', title: 'Update' },
{ key: 'remove', icon: 'trash', title: 'Remove' },
{ key: 'indexes', icon: 'list', title: 'Indexes' },
{ key: 'aggregate', icon: 're', title: 'Aggregate' },
]}
bind:selectedKey={tab} />
<TabBar
tabs={[
{ key: 'stats', icon: 'chart', title: 'Stats' },
{ key: 'find', icon: 'db', title: 'Find' },
{ key: 'insert', icon: '+', title: 'Insert' },
{ key: 'update', icon: 'edit', title: 'Update' },
{ key: 'remove', icon: 'trash', title: 'Remove' },
{ key: 'indexes', icon: 'list', title: 'Indexes' },
{ key: 'aggregate', icon: 're', title: 'Aggregate' },
{ key: 'shell', icon: 'shell', title: 'Shell' },
]}
bind:selectedKey={tab}
/>
<div class="container">
{#if tab === 'stats'} <Stats {collection} />
@ -61,6 +65,7 @@
{:else if tab === 'remove'} <Remove {collection} />
{:else if tab === 'indexes'} <Indexes {collection} />
{:else if tab === 'aggregate'} <Aggregate {collection} />
{:else if tab === 'shell'} <Shell {collection} />
{/if}
</div>
{/key}

View File

@ -2,6 +2,8 @@
import BlankState from '$components/blankstate.svelte';
import TabBar from '$components/tabbar.svelte';
import { EventsOn } from '$wails/runtime/runtime';
import Shell from '../shell.svelte';
import Stats from './stats.svelte';
export let database;
@ -24,9 +26,15 @@
<div class="view" class:empty={!database}>
{#if database}
{#key database}
<TabBar tabs={[ { key: 'stats', icon: 'chart', title: 'Database stats' } ]} bind:selectedKey={tab} />
<TabBar
tabs={[
{ key: 'stats', icon: 'chart', title: 'Database stats' },
{ key: 'shell', icon: 'shell', title: 'Shell' },
]}
bind:selectedKey={tab} />
<div class="container">
{#if tab === 'stats'} <Stats {database} />
{:else if tab === 'shell'} <Shell {database} />
{/if}
</div>
{/key}

View File

@ -4,6 +4,7 @@
import { EventsOn } from '$wails/runtime/runtime';
import Logs from './logs.svelte';
import Shell from '../shell.svelte';
import Status from './status.svelte';
import SystemInfo from './systeminfo.svelte';
@ -28,6 +29,7 @@
<TabBar
tabs={[
{ key: 'status', icon: 'chart', title: 'Host status' },
{ key: 'shell', icon: 'shell', title: 'Shell' },
{ key: 'logs', icon: 'doc', title: 'Logs' },
{ key: 'systemInfo', icon: 'server', title: 'System info' },
]}
@ -38,6 +40,7 @@
{#if tab === 'status'} <Status {host} />
{:else if tab === 'logs'} <Logs {host} />
{:else if tab === 'systemInfo'} <SystemInfo {host} />
{:else if tab === 'shell'} <Shell {host} />
{/if}
</div>
{/key}

View File

@ -0,0 +1,146 @@
<script>
import BlankState from '$components/blankstate.svelte';
import CodeEditor from '$components/codeeditor.svelte';
import Icon from '$components/icon.svelte';
import { javascript } from '@codemirror/lang-javascript';
import { onDestroy, onMount } from 'svelte';
export let host = undefined;
export let database = undefined;
export let collection = undefined;
const placeholder = '// Write your script here...';
const extensions = [ javascript() ];
let script = '';
let result = {};
let copySucceeded = false;
let timeout;
let busy = false;
let editor;
async function run() {
busy = true;
if (collection) {
result = await collection.executeShellScript(script);
}
else if (database) {
result = await database.executeShellScript(script);
}
else if (host) {
result = await host.executeShellScript(script);
}
busy = false;
}
async function copyErrorDescription() {
await navigator.clipboard.writeText(result.errorDescription);
copySucceeded = true;
timeout = setTimeout(() => copySucceeded = false, 1500);
}
onMount(() => {
editor.dispatch({
changes: {
from: 0,
to: editor.state.doc.length,
insert: placeholder,
},
selection: {
from: 0,
anchor: 0,
to: placeholder.length,
head: placeholder.length,
},
});
editor.focus();
});
onDestroy(() => clearTimeout(timeout));
</script>
<div class="shell">
<div class="overflow">
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="field">
<CodeEditor bind:editor bind:text={script} {extensions} />
</label>
</div>
<div class="output">
{#if busy}
<BlankState icon="loading" label="Executing…" />
{:else if result.errorTitle || result.errorDescription}
<BlankState title={result.errorTitle} label={result.errorDescription} icon="!">
<button class="button-small" on:click={copyErrorDescription}>
<Icon name={copySucceeded ? 'check' : 'clipboard'} /> Copy error message
</button>
</BlankState>
{:else}
<pre>{result.output || ''}</pre>
{/if}
</div>
<div class="controls">
{#key result}
<div class="status flash-green">
{#if result?.status}
Exit code: {result.status}
{/if}
</div>
{/key}
<button class="btn" on:click={run}>
<Icon name="play" /> Run
</button>
</div>
</div>
<style>
.shell {
display: grid;
grid-template: 1fr auto / 1fr 1fr;
}
.overflow {
overflow: auto;
}
.field {
height: 100%;
}
.field :global(.editor) {
border-radius: 0;
}
.output {
background-color: #111;
color: #fff;
overflow: auto;
display: flex;
}
.output :global(*) {
color: #fff;
}
.output pre {
font-family: monospace;
padding: 0.5rem;
user-select: text;
-webkit-user-select: text;
cursor: text;
}
.output :global(.blankstate) {
margin: auto;
padding: 0.5rem;
}
.controls {
margin-top: 0.5rem;
display: flex;
align-items: center;
grid-column: 1 / 3;
}
.controls .status {
margin-right: auto;
}
</style>