Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide/components/terminal')
-rw-r--r--app/assets/javascripts/ide/components/terminal/empty_state.vue71
-rw-r--r--app/assets/javascripts/ide/components/terminal/session.vue53
-rw-r--r--app/assets/javascripts/ide/components/terminal/terminal.vue117
-rw-r--r--app/assets/javascripts/ide/components/terminal/terminal_controls.vue27
-rw-r--r--app/assets/javascripts/ide/components/terminal/view.vue41
5 files changed, 309 insertions, 0 deletions
diff --git a/app/assets/javascripts/ide/components/terminal/empty_state.vue b/app/assets/javascripts/ide/components/terminal/empty_state.vue
new file mode 100644
index 00000000000..9841f1ece48
--- /dev/null
+++ b/app/assets/javascripts/ide/components/terminal/empty_state.vue
@@ -0,0 +1,71 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ },
+ props: {
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ isValid: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ message: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ helpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ illustrationPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ methods: {
+ onStart() {
+ this.$emit('start');
+ },
+ },
+};
+</script>
+<template>
+ <div class="text-center p-3">
+ <div v-if="illustrationPath" class="svg-content svg-130"><img :src="illustrationPath" /></div>
+ <h4>{{ __('Web Terminal') }}</h4>
+ <gl-loading-icon v-if="isLoading" size="lg" class="prepend-top-default" />
+ <template v-else>
+ <p>{{ __('Run tests against your code live using the Web Terminal') }}</p>
+ <p>
+ <button
+ :disabled="!isValid"
+ class="btn btn-info"
+ type="button"
+ data-qa-selector="start_web_terminal_button"
+ @click="onStart"
+ >
+ {{ __('Start Web Terminal') }}
+ </button>
+ </p>
+ <div v-if="!isValid && message" class="bs-callout text-left" v-html="message"></div>
+ <p v-else>
+ <a
+ v-if="helpPath"
+ :href="helpPath"
+ target="_blank"
+ v-text="__('Learn more about Web Terminal')"
+ ></a>
+ </p>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/terminal/session.vue b/app/assets/javascripts/ide/components/terminal/session.vue
new file mode 100644
index 00000000000..a8fe9ea6866
--- /dev/null
+++ b/app/assets/javascripts/ide/components/terminal/session.vue
@@ -0,0 +1,53 @@
+<script>
+import { mapActions, mapState } from 'vuex';
+import { __ } from '~/locale';
+import Terminal from './terminal.vue';
+import { isEndingStatus } from '../../stores/modules/terminal/utils';
+
+export default {
+ components: {
+ Terminal,
+ },
+ computed: {
+ ...mapState('terminal', ['session']),
+ actionButton() {
+ if (isEndingStatus(this.session.status)) {
+ return {
+ action: () => this.restartSession(),
+ text: __('Restart Terminal'),
+ class: 'btn-primary',
+ };
+ }
+
+ return {
+ action: () => this.stopSession(),
+ text: __('Stop Terminal'),
+ class: 'btn-inverted btn-remove',
+ };
+ },
+ },
+ methods: {
+ ...mapActions('terminal', ['restartSession', 'stopSession']),
+ },
+};
+</script>
+
+<template>
+ <div v-if="session" class="ide-terminal d-flex flex-column">
+ <header class="ide-job-header d-flex align-items-center">
+ <h5>{{ __('Web Terminal') }}</h5>
+ <div class="ml-auto align-self-center">
+ <button
+ v-if="actionButton"
+ type="button"
+ class="btn btn-sm"
+ :class="actionButton.class"
+ @click="actionButton.action"
+ >
+ {{ actionButton.text }}
+ </button>
+ </div>
+ </header>
+ <terminal :terminal-path="session.terminalPath" :status="session.status" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/terminal/terminal.vue b/app/assets/javascripts/ide/components/terminal/terminal.vue
new file mode 100644
index 00000000000..0ee4107f9ab
--- /dev/null
+++ b/app/assets/javascripts/ide/components/terminal/terminal.vue
@@ -0,0 +1,117 @@
+<script>
+import { mapState } from 'vuex';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { __ } from '~/locale';
+import GLTerminal from '~/terminal/terminal';
+import TerminalControls from './terminal_controls.vue';
+import { RUNNING, STOPPING } from '../../stores/modules/terminal/constants';
+import { isStartingStatus } from '../../stores/modules/terminal/utils';
+
+export default {
+ components: {
+ GlLoadingIcon,
+ TerminalControls,
+ },
+ props: {
+ terminalPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ status: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ glterminal: null,
+ canScrollUp: false,
+ canScrollDown: false,
+ };
+ },
+ computed: {
+ ...mapState(['panelResizing']),
+ loadingText() {
+ if (isStartingStatus(this.status)) {
+ return __('Starting...');
+ } else if (this.status === STOPPING) {
+ return __('Stopping...');
+ }
+
+ return '';
+ },
+ },
+ watch: {
+ panelResizing() {
+ if (!this.panelResizing && this.glterminal) {
+ this.glterminal.fit();
+ }
+ },
+ status() {
+ this.refresh();
+ },
+ terminalPath() {
+ this.refresh();
+ },
+ },
+ beforeDestroy() {
+ this.destroyTerminal();
+ },
+ methods: {
+ refresh() {
+ if (this.status === RUNNING && this.terminalPath) {
+ this.createTerminal();
+ } else if (this.status === STOPPING) {
+ this.stopTerminal();
+ }
+ },
+ createTerminal() {
+ this.destroyTerminal();
+ this.glterminal = new GLTerminal(this.$refs.terminal);
+ this.glterminal.addScrollListener(({ canScrollUp, canScrollDown }) => {
+ this.canScrollUp = canScrollUp;
+ this.canScrollDown = canScrollDown;
+ });
+ },
+ destroyTerminal() {
+ if (this.glterminal) {
+ this.glterminal.dispose();
+ this.glterminal = null;
+ }
+ },
+ stopTerminal() {
+ if (this.glterminal) {
+ this.glterminal.disable();
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="d-flex flex-column flex-fill min-height-0 pr-3">
+ <div class="top-bar d-flex border-left-0 align-items-center">
+ <div v-if="loadingText" data-qa-selector="loading_container">
+ <gl-loading-icon :inline="true" />
+ <span>{{ loadingText }}</span>
+ </div>
+ <terminal-controls
+ v-if="glterminal"
+ class="ml-auto"
+ :can-scroll-up="canScrollUp"
+ :can-scroll-down="canScrollDown"
+ @scroll-up="glterminal.scrollToTop()"
+ @scroll-down="glterminal.scrollToBottom()"
+ />
+ </div>
+ <div class="terminal-wrapper d-flex flex-fill min-height-0">
+ <div
+ ref="terminal"
+ class="ide-terminal-trace flex-fill min-height-0 w-100"
+ :data-project-path="terminalPath"
+ data-qa-selector="terminal_screen"
+ ></div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/terminal/terminal_controls.vue b/app/assets/javascripts/ide/components/terminal/terminal_controls.vue
new file mode 100644
index 00000000000..4c13b4ef103
--- /dev/null
+++ b/app/assets/javascripts/ide/components/terminal/terminal_controls.vue
@@ -0,0 +1,27 @@
+<script>
+import ScrollButton from '~/ide/components/jobs/detail/scroll_button.vue';
+
+export default {
+ components: {
+ ScrollButton,
+ },
+ props: {
+ canScrollUp: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ canScrollDown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+<template>
+ <div class="controllers">
+ <scroll-button :disabled="!canScrollUp" direction="up" @click="$emit('scroll-up')" />
+ <scroll-button :disabled="!canScrollDown" direction="down" @click="$emit('scroll-down')" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/ide/components/terminal/view.vue b/app/assets/javascripts/ide/components/terminal/view.vue
new file mode 100644
index 00000000000..db97e95eed9
--- /dev/null
+++ b/app/assets/javascripts/ide/components/terminal/view.vue
@@ -0,0 +1,41 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import EmptyState from './empty_state.vue';
+import TerminalSession from './session.vue';
+
+export default {
+ components: {
+ EmptyState,
+ TerminalSession,
+ },
+ computed: {
+ ...mapState('terminal', ['isShowSplash', 'paths']),
+ ...mapGetters('terminal', ['allCheck']),
+ },
+ methods: {
+ ...mapActions('terminal', ['startSession', 'hideSplash']),
+ start() {
+ this.startSession();
+ this.hideSplash();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="h-100">
+ <div v-if="isShowSplash" class="h-100 d-flex flex-column justify-content-center">
+ <empty-state
+ :is-loading="allCheck.isLoading"
+ :is-valid="allCheck.isValid"
+ :message="allCheck.message"
+ :help-path="paths.webTerminalHelpPath"
+ :illustration-path="paths.webTerminalSvgPath"
+ @start="start()"
+ />
+ </div>
+ <template v-else>
+ <terminal-session />
+ </template>
+ </div>
+</template>