diff --git a/ui/app/pages/console.vue b/ui/app/pages/console.vue
index a7b16ee3..dcaaae5a 100644
--- a/ui/app/pages/console.vue
+++ b/ui/app/pages/console.vue
@@ -2,6 +2,15 @@
.terminal {
padding-left: 10px;
}
+
+.history-item {
+ cursor: pointer;
+ transition: background-color 0.2s;
+}
+
+.history-item:hover {
+ background-color: #f5f5f5;
+}
@@ -49,7 +58,7 @@
@@ -57,6 +66,48 @@
+
+
+
+
+
+
+ Commands history is empty.
+
+
+
+
+
+
+
+ {{ cmd.replace(/\n/g, ' ') }}
+
+ |
+
+
+ |
+
+
+
+
+
+
+
@@ -68,11 +119,13 @@ import { useStorage } from '@vueuse/core'
import { disableOpacity, enableOpacity } from '~/utils'
import InputAutocomplete from '~/components/InputAutocomplete.vue'
import TextareaAutocomplete from '~/components/TextareaAutocomplete.vue'
+import { useDialog } from '~/composables/useDialog'
import type { AutoCompleteOptions } from '~/types/autocomplete'
const config = useConfigStore()
const socket = useSocketStore()
const toast = useNotification()
+const dialog = useDialog()
const terminal = ref()
const terminalFit = ref()
@@ -82,6 +135,9 @@ const commandInput = ref | null>(null)
const commandTextarea = ref | null>(null)
const isLoading = ref(false)
const storedCommand = useStorage('console_command', '')
+const commandHistory = useStorage('console_command_history', [])
+const isHistoryCollapsed = useStorage('console_history_collapsed', false)
+const MAX_HISTORY_ITEMS = 50
const ytDlpOptions = computed(() => config.ytdlp_options.flatMap(opt => opt.flags
.map(flag => ({ value: flag, description: opt.description || '' }))
@@ -94,6 +150,9 @@ watch(() => isLoading.value, async value => {
if (value) {
return
}
+ if (command.value.trim()) {
+ addToHistory(command.value.trim())
+ }
command.value = ''
await nextTick();
focusInput()
@@ -256,6 +315,33 @@ const focusInput = async () => {
elm?.focus()
}
+const addToHistory = (cmd: string) => {
+ commandHistory.value = [cmd, ...commandHistory.value.filter(h => h !== cmd)].slice(0, MAX_HISTORY_ITEMS)
+}
+
+const loadCommand = async (cmd: string) => {
+ command.value = cmd
+ await nextTick()
+ focusInput()
+}
+
+const clearHistory = async () => {
+ if (commandHistory.value.length === 0) {
+ return
+ }
+ const { status } = await dialog.confirmDialog({
+ title: 'Confirm Action',
+ message: `Clear commands history?`,
+ confirmColor: 'is-danger',
+ })
+ if (!status) {
+ return
+ }
+ commandHistory.value = []
+}
+
+const removeFromHistory = (index: number) => commandHistory.value = commandHistory.value.filter((_, i) => i !== index)
+
const writer = (s: string) => {
if (!terminal.value) {
return