587 lines
27 KiB
HTML
587 lines
27 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>StreamDeck</title>
|
|
<script src="/static/app.js"></script>
|
|
<script defer src="/static/alpine.min.js"></script>
|
|
<link rel="stylesheet" href="/static/fontawesome/css/all.min.css">
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
</head>
|
|
<body>
|
|
<div x-data="deckApp" x-init="init()" class="app" @keydown.escape="editing = null; subView = null">
|
|
|
|
<!-- ═══ Header ═══ -->
|
|
<header>
|
|
<button class="icon-btn" @click="view = 'grid'; subView = 'switcher'" title="Switch page">
|
|
<i class="fas fa-layer-group"></i>
|
|
</button>
|
|
|
|
<div class="page-nav" x-show="view === 'grid'">
|
|
<template x-for="(p, i) in pages" :key="p.name">
|
|
<span class="dot"
|
|
:class="{ active: p.name === activePage }"
|
|
@click="switchPage(p.name)"
|
|
:title="p.name"></span>
|
|
</template>
|
|
</div>
|
|
|
|
<span x-show="view !== 'grid'" x-text="view === 'settings' ? 'Settings' : 'Backups'"
|
|
style="font-size:14px;font-weight:600;"></span>
|
|
|
|
<div class="header-actions">
|
|
<button class="icon-btn" @click="view = 'grid'; subView = null" title="Grid">
|
|
<i class="fas fa-table-cells"></i>
|
|
</button>
|
|
<button class="icon-btn" @click="view = 'settings'; subView = null" title="Settings">
|
|
<i class="fas fa-gear"></i>
|
|
</button>
|
|
<button class="icon-btn" @click="view = 'backups'; loadBackups(); subView = null" title="Backups">
|
|
<i class="fas fa-floppy-disk"></i>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- ═══ Grid View ═══ -->
|
|
<main x-show="view === 'grid' && !subView">
|
|
<div class="deck-selector" x-show="decks.length > 1">
|
|
<template x-for="d in decks" :key="d.serial">
|
|
<button class="deck-btn"
|
|
:class="{ active: d.serial === activeDeckSerial }"
|
|
@click="activeDeckSerial = d.serial"
|
|
x-text="d.model + ' (' + d.keys_x + '\u00d7' + d.keys_y + ')'"></button>
|
|
</template>
|
|
</div>
|
|
<div class="deck-grid" :style="'grid-template-columns: repeat(' + activeDeck.keys_x + ', minmax(0, 1fr))'">
|
|
<template x-for="key in gridKeys" :key="key.index">
|
|
<button class="key-btn"
|
|
:style="key.displayBg ? { backgroundColor: key.displayBg } : {}"
|
|
:class="{ empty: !key.configured }"
|
|
@click="editKey(key.index)">
|
|
<img x-show="key.configured && (key.icon || key.label || key.displayText || key.displayBg || key.background)"
|
|
:src="key.previewUrl" alt="">
|
|
<span x-show="!key.configured" style="color:var(--text-muted);font-size:24px;opacity:0.5;line-height:1;">+</span>
|
|
<div class="action-icons" x-show="key.hasAction">
|
|
<template x-for="(t, ti) in key.actionTypes.slice(0,3)" :key="ti">
|
|
<i :class="'fas fa-' + actionIconMap[t] + ' action-icon type-' + t"
|
|
:title="actionTitleMap[t]"></i>
|
|
</template>
|
|
<span class="action-icon" x-show="key.actionTypes.length > 3"
|
|
style="background:var(--text-muted);font-size:7px;font-weight:700;padding:2px 3px;"
|
|
x-text="'+' + (key.actionTypes.length - 3)"></span>
|
|
</div>
|
|
<i x-show="key.hasDisplay" class="fas fa-arrows-rotate display-indicator" title="Periodic display"></i>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="toolbar">
|
|
<button class="btn btn-outline" @click="addPage">+ Add page</button>
|
|
<button class="btn btn-outline" @click="deletePage"
|
|
:disabled="pages.length <= 1"
|
|
style="margin-left:auto;">Delete page</button>
|
|
<button class="btn btn-outline" @click="renamePage">Rename</button>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- ═══ Page Switcher (sub-view) ═══ -->
|
|
<main x-show="view === 'grid' && subView === 'switcher'" style="justify-content:flex-start;padding-top:12px;">
|
|
<div class="panel" style="width:100%;">
|
|
<h3>Pages</h3>
|
|
<template x-for="p in pages" :key="p.name">
|
|
<button class="btn btn-outline" style="display:block;width:100%;margin-bottom:4px;text-align:left;"
|
|
:style="{ borderColor: p.name === activePage ? 'var(--accent)' : '' }"
|
|
@click="switchPage(p.name); subView = null">
|
|
<span x-text="p.name"></span>
|
|
<span style="float:right;color:var(--text-muted);font-size:11px;"
|
|
x-text="p.keys.length + ' keys'"></span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- ═══ Settings View ═══ -->
|
|
<main x-show="view === 'settings'" style="justify-content:flex-start;padding-top:12px;">
|
|
<div class="panel" style="width:100%;">
|
|
|
|
<!-- Device -->
|
|
<div class="panel-section">
|
|
<h4>Device</h4>
|
|
<div class="form-group">
|
|
<label>Brightness</label>
|
|
<div class="slider-group">
|
|
<input type="range" min="0" max="100" x-model.number="settingsForm.brightness">
|
|
<span class="value" x-text="settingsForm.brightness + '%'"></span>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Default Font</label>
|
|
<select x-model="settingsForm.font">
|
|
<option value="medium">Medium</option>
|
|
<option value="regular">Regular</option>
|
|
</select>
|
|
</div>
|
|
<div class="checkbox-row">
|
|
<input type="checkbox" id="show-label-bg" x-model="settingsForm.show_label_background">
|
|
<label for="show-label-bg">Show label background</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Screensaver -->
|
|
<div class="panel-section">
|
|
<h4>Screensaver</h4>
|
|
<div class="checkbox-row">
|
|
<input type="checkbox" id="ss-enabled" x-model="settingsForm.screensaver_enabled">
|
|
<label for="ss-enabled">Enable screensaver</label>
|
|
</div>
|
|
<div class="form-row" x-show="settingsForm.screensaver_enabled">
|
|
<div class="form-group">
|
|
<label>Idle (seconds)</label>
|
|
<input type="number" min="5" x-model.number="settingsForm.screensaver_idle">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Brightness on idle</label>
|
|
<input type="number" min="0" max="100" x-model.number="settingsForm.screensaver_brightness">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gesture Timing -->
|
|
<div class="panel-section">
|
|
<h4>Gesture Timing</h4>
|
|
<div class="form-group">
|
|
<label>Long press threshold</label>
|
|
<div class="slider-group">
|
|
<input type="range" min="200" max="1500" step="50" x-model.number="settingsForm.long_press_ms">
|
|
<span class="value" x-text="settingsForm.long_press_ms + 'ms'"></span>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Double tap threshold</label>
|
|
<div class="slider-group">
|
|
<input type="range" min="100" max="600" step="25" x-model.number="settingsForm.double_tap_ms">
|
|
<span class="value" x-text="settingsForm.double_tap_ms + 'ms'"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Auto-switch -->
|
|
<div class="panel-section">
|
|
<h4>Auto-switch rules</h4>
|
|
<template x-for="(rule, i) in autoSwitchRules" :key="i">
|
|
<div class="rule-item">
|
|
<div style="flex:1;">
|
|
<div class="form-row">
|
|
<div class="form-group" style="margin-bottom:4px;">
|
|
<label>WM Class</label>
|
|
<input type="text" x-model="rule.wm_class" placeholder="regex">
|
|
</div>
|
|
<div class="form-group" style="margin-bottom:4px;">
|
|
<label>Title</label>
|
|
<input type="text" x-model="rule.title" placeholder="regex">
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group" style="margin-bottom:0;">
|
|
<label>Page</label>
|
|
<select x-model="rule.page">
|
|
<option value="">--</option>
|
|
<template x-for="p in pages" :key="p.name">
|
|
<option :value="p.name" x-text="p.name"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
<div class="checkbox-row" style="margin-bottom:0;padding-top:16px;">
|
|
<input type="checkbox" x-model="rule.stay">
|
|
<label>Stay</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-danger" style="flex-shrink:0;" @click="removeAutoSwitchRule(i)">x</button>
|
|
</div>
|
|
</template>
|
|
<button class="btn btn-outline" @click="addAutoSwitchRule" style="width:100%;margin-top:8px;">
|
|
+ Add rule
|
|
</button>
|
|
</div>
|
|
|
|
<div class="modal-actions">
|
|
<button class="btn btn-primary" @click="saveSettings">Save settings</button>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- ═══ Backup View ═══ -->
|
|
<main x-show="view === 'backups'" style="justify-content:flex-start;padding-top:12px;">
|
|
<div class="panel" style="width:100%;">
|
|
|
|
<div class="panel-section">
|
|
<h4>Actions</h4>
|
|
<div style="display:flex;gap:8px;">
|
|
<button class="btn btn-primary" @click="downloadConfig">
|
|
<i class="fas fa-download" style="margin-right:4px;"></i> Export JSON
|
|
</button>
|
|
<button class="btn btn-outline" @click="restoreConfig">
|
|
<i class="fas fa-upload" style="margin-right:4px;"></i> Restore
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel-section">
|
|
<h4>Auto backups <span style="font-weight:400;color:var(--text-muted);"
|
|
x-text="backups.length ? '(' + backups.length + ')' : ''"></span></h4>
|
|
<template x-if="backups.length === 0">
|
|
<p style="color:var(--text-muted);font-size:12px;">No backups yet. They are created automatically when you save.</p>
|
|
</template>
|
|
<template x-for="b in backups" :key="b.name">
|
|
<div class="backup-item">
|
|
<div>
|
|
<div x-text="backupNameTime(b.name)"></div>
|
|
<div class="meta" x-text="formatSize(b.size)"></div>
|
|
</div>
|
|
<div style="display:flex;gap:4px;">
|
|
<button class="btn btn-outline" style="padding:4px 8px;font-size:11px;"
|
|
@click="downloadBackup(b.name)" title="Download">
|
|
<i class="fas fa-download"></i>
|
|
</button>
|
|
<button class="btn btn-outline" style="padding:4px 8px;font-size:11px;"
|
|
@click="restoreBackup(b.name)" title="Restore">
|
|
<i class="fas fa-rotate-left"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- ═══ Key Editor Modal ═══ -->
|
|
<div class="modal-overlay" x-show="editing !== null" @click.self="editing = null">
|
|
<div class="modal" x-show="editing !== null" x-transition>
|
|
<h3>Edit Key <span x-text="editing + 1"></span></h3>
|
|
|
|
<!-- Preview -->
|
|
<div class="preview-box" @click="pickImage" title="Click to upload image">
|
|
<img :src="editPreviewUrl" alt="">
|
|
<div class="preview-overlay">
|
|
<i class="fas fa-image"></i>
|
|
</div>
|
|
</div>
|
|
<input type="file" accept="image/*" id="image-picker" style="display:none"
|
|
x-ref="fileInput" @change="onImagePicked">
|
|
|
|
<!-- Basic fields -->
|
|
<div class="form-group">
|
|
<label>Icon</label>
|
|
<input type="text" x-model="editForm.icon"
|
|
placeholder="fa:terminal, @system-icon, /path/to/image.png">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Label</label>
|
|
<input type="text" x-model="editForm.label" placeholder="Optional text">
|
|
</div>
|
|
|
|
<!-- Actions section -->
|
|
<h4 style="font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;margin-top:12px;">Actions</h4>
|
|
|
|
<!-- Action tabs -->
|
|
<div class="action-tabs">
|
|
<template x-for="t in ['tap','long_press','double_tap','hold']" :key="t">
|
|
<button class="action-tab"
|
|
:class="{ active: editForm.activeTrigger === t, configured: (t === 'hold' ? (hasHoldAction('start') || hasHoldAction('end')) : hasCurrentAction(t)) }"
|
|
@click="editForm.activeTrigger = t">
|
|
<span x-text="triggerLabel(t)"></span>
|
|
<span class="tab-check" x-show="(t === 'hold' ? (hasHoldAction('start') || hasHoldAction('end')) : hasCurrentAction(t))">✓</span>
|
|
</button>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Non-hold trigger panes -->
|
|
<template x-for="t in ['tap','long_press','double_tap']" :key="t">
|
|
<div x-show="editForm.activeTrigger === t">
|
|
<div class="form-group">
|
|
<select x-model="editForm.actions[t].type">
|
|
<option value="none">None</option>
|
|
<option value="command">Command</option>
|
|
<option value="builtin">Builtin</option>
|
|
<option value="script">Script</option>
|
|
<option value="page">Switch page</option>
|
|
<option value="keyboard">Keyboard shortcut</option>
|
|
</select>
|
|
</div>
|
|
|
|
<template x-if="editForm.actions[t].type === 'command'">
|
|
<div>
|
|
<div class="form-group">
|
|
<input type="text" x-model="editForm.actions[t].command" placeholder="firefox">
|
|
</div>
|
|
<div class="checkbox-row">
|
|
<input type="checkbox" :id="'bg-cb-' + t" x-model="editForm.actions[t].background">
|
|
<label :for="'bg-cb-' + t">Run in background</label>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.actions[t].type === 'builtin'">
|
|
<div class="form-group">
|
|
<select x-model="editForm.actions[t].builtin">
|
|
<option value="">-- select --</option>
|
|
<option value="volume_up">Volume Up</option>
|
|
<option value="volume_down">Volume Down</option>
|
|
<option value="volume_mute">Volume Mute</option>
|
|
<option value="brightness_up">Brightness Up</option>
|
|
<option value="brightness_down">Brightness Down</option>
|
|
<option value="media_play_pause">Play/Pause</option>
|
|
<option value="media_next">Next Track</option>
|
|
<option value="media_prev">Previous Track</option>
|
|
<option value="media_stop">Stop</option>
|
|
</select>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.actions[t].type === 'script'">
|
|
<div class="form-group">
|
|
<input type="text" x-model="editForm.actions[t].script" placeholder="/path/to/script.sh">
|
|
</div>
|
|
</template>
|
|
|
|
<div class="form-group" x-show="editForm.actions[t].type === 'page'">
|
|
<select x-model="editForm.actions[t].page">
|
|
<option value="">-- select --</option>
|
|
<template x-for="p in pages" :key="p.name">
|
|
<option :value="p.name" x-text="p.name"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
|
|
<template x-if="editForm.actions[t].type === 'keyboard'">
|
|
<div class="form-group">
|
|
<label>Key combination</label>
|
|
<div class="mod-row">
|
|
<button class="mod-btn" :class="{ active: editForm.actions[t].modCtrl }" @click="toggleMod('ctrl')" type="button">Ctrl</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions[t].modAlt }" @click="toggleMod('alt')" type="button">Alt</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions[t].modShift }" @click="toggleMod('shift')" type="button">Shift</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions[t].modSuper }" @click="toggleMod('super')" type="button">Super</button>
|
|
</div>
|
|
<div class="key-capture"
|
|
x-ref="keyCapture"
|
|
:class="{ capturing: isCapturingKey }"
|
|
tabindex="0"
|
|
@keydown.prevent="onCaptureKey"
|
|
@blur="isCapturingKey = false"
|
|
@click="startKeyCapture">
|
|
<span x-show="!isCapturingKey && editForm.actions[t].mainKey" class="captured-key" x-text="displayKey(editForm.actions[t].mainKey)"></span>
|
|
<span x-show="isCapturingKey" class="capture-hint">Press any key...</span>
|
|
<span x-show="!isCapturingKey && !editForm.actions[t].mainKey" class="capture-placeholder">Click to capture</span>
|
|
</div>
|
|
<div class="key-chips" x-show="editForm.actions[t].keys" style="margin-top:8px;">
|
|
<template x-for="(chip, ki) in keyChips" :key="ki">
|
|
<span class="key-chip" x-text="chip"></span>
|
|
<span x-show="ki < keyChips.length - 1" class="key-plus">+</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Hold pane -->
|
|
<div x-show="editForm.activeTrigger === 'hold'">
|
|
<div class="hold-subtabs">
|
|
<button class="hold-stab" :class="{ active: editForm.holdPhase === 'start' }"
|
|
@click="editForm.holdPhase = 'start'">On press & hold</button>
|
|
<button class="hold-stab" :class="{ active: editForm.holdPhase === 'end' }"
|
|
@click="editForm.holdPhase = 'end'">On release</button>
|
|
</div>
|
|
|
|
<template x-for="phase in ['start','end']" :key="phase">
|
|
<div x-show="editForm.holdPhase === phase">
|
|
<div class="form-group">
|
|
<select x-model="editForm.actions.hold[phase].type">
|
|
<option value="none">None</option>
|
|
<option value="command">Command</option>
|
|
<option value="builtin">Builtin</option>
|
|
<option value="script">Script</option>
|
|
<option value="page">Switch page</option>
|
|
<option value="keyboard">Keyboard shortcut</option>
|
|
</select>
|
|
</div>
|
|
|
|
<template x-if="editForm.actions.hold[phase].type === 'command'">
|
|
<div>
|
|
<div class="form-group">
|
|
<input type="text" x-model="editForm.actions.hold[phase].command" placeholder="firefox">
|
|
</div>
|
|
<div class="checkbox-row">
|
|
<input type="checkbox" :id="'hold-bg-' + phase" x-model="editForm.actions.hold[phase].background">
|
|
<label :for="'hold-bg-' + phase">Run in background</label>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.actions.hold[phase].type === 'builtin'">
|
|
<div class="form-group">
|
|
<select x-model="editForm.actions.hold[phase].builtin">
|
|
<option value="">-- select --</option>
|
|
<option value="volume_up">Volume Up</option>
|
|
<option value="volume_down">Volume Down</option>
|
|
<option value="volume_mute">Volume Mute</option>
|
|
<option value="brightness_up">Brightness Up</option>
|
|
<option value="brightness_down">Brightness Down</option>
|
|
<option value="media_play_pause">Play/Pause</option>
|
|
<option value="media_next">Next Track</option>
|
|
<option value="media_prev">Previous Track</option>
|
|
<option value="media_stop">Stop</option>
|
|
</select>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.actions.hold[phase].type === 'script'">
|
|
<div class="form-group">
|
|
<input type="text" x-model="editForm.actions.hold[phase].script" placeholder="/path/to/script.sh">
|
|
</div>
|
|
</template>
|
|
|
|
<div class="form-group" x-show="editForm.actions.hold[phase].type === 'page'">
|
|
<select x-model="editForm.actions.hold[phase].page">
|
|
<option value="">-- select --</option>
|
|
<template x-for="p in pages" :key="p.name">
|
|
<option :value="p.name" x-text="p.name"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
|
|
<template x-if="editForm.actions.hold[phase].type === 'keyboard'">
|
|
<div class="form-group">
|
|
<label>Key combination</label>
|
|
<div class="mod-row">
|
|
<button class="mod-btn" :class="{ active: editForm.actions.hold[phase].modCtrl }" @click="toggleMod('ctrl')" type="button">Ctrl</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions.hold[phase].modAlt }" @click="toggleMod('alt')" type="button">Alt</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions.hold[phase].modShift }" @click="toggleMod('shift')" type="button">Shift</button>
|
|
<button class="mod-btn" :class="{ active: editForm.actions.hold[phase].modSuper }" @click="toggleMod('super')" type="button">Super</button>
|
|
</div>
|
|
<div class="key-capture"
|
|
x-ref="keyCapture"
|
|
:class="{ capturing: isCapturingKey }"
|
|
tabindex="0"
|
|
@keydown.prevent="onCaptureKey"
|
|
@blur="isCapturingKey = false"
|
|
@click="startKeyCapture">
|
|
<span x-show="!isCapturingKey && editForm.actions.hold[phase].mainKey" class="captured-key" x-text="displayKey(editForm.actions.hold[phase].mainKey)"></span>
|
|
<span x-show="isCapturingKey" class="capture-hint">Press any key...</span>
|
|
<span x-show="!isCapturingKey && !editForm.actions.hold[phase].mainKey" class="capture-placeholder">Click to capture</span>
|
|
</div>
|
|
<div class="key-chips" x-show="editForm.actions.hold[phase].keys" style="margin-top:8px;">
|
|
<template x-for="(chip, ki) in keyChips" :key="ki">
|
|
<span class="key-chip" x-text="chip"></span>
|
|
<span x-show="ki < keyChips.length - 1" class="key-plus">+</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Advanced toggle -->
|
|
<button class="collapse-toggle" :class="{ open: showAdvanced }" @click="showAdvanced = !showAdvanced" style="margin-top:8px;">
|
|
<i class="fas fa-chevron-right"></i> More
|
|
</button>
|
|
|
|
<!-- Advanced fields -->
|
|
<div x-show="showAdvanced" x-transition>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label>Icon Scale</label>
|
|
<input type="number" step="0.01" min="0.1" max="1.0" x-model.number="editForm.icon_scale">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Font Size</label>
|
|
<input type="number" step="0.5" min="8" max="32" x-model.number="editForm.font_size">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Background</label>
|
|
<div style="display:flex;gap:8px;align-items:center;">
|
|
<input type="color"
|
|
x-init="$el.value = editForm.bg_color || '#000000'; $el.addEventListener('input', () => editForm.bg_color = $el.value); $watch('editForm.bg_color', v => $el.value = v || '#000000')"
|
|
style="width:40px;height:40px;padding:2px;border-radius:var(--radius-xs);cursor:pointer;">
|
|
<input type="text" x-model="editForm.bg_color"
|
|
placeholder="#rrggbb or #rrggbbaa" maxlength="9"
|
|
style="flex:1;">
|
|
<button class="icon-btn" style="width:24px;height:24px;font-size:10px;flex-shrink:0;"
|
|
@click="editForm.bg_color = ''" title="Clear"
|
|
x-show="editForm.bg_color !== ''">
|
|
<i class="fas fa-xmark"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="border-top:1px solid var(--border);margin:8px 0;"></div>
|
|
|
|
<div class="form-group">
|
|
<label>Display mode</label>
|
|
<div class="tab-row">
|
|
<button class="tab-btn" :class="{ active: editForm.display_mode === 'none' }"
|
|
@click="editForm.display_mode = 'none'">Off</button>
|
|
<button class="tab-btn" :class="{ active: editForm.display_mode === 'command' }"
|
|
@click="editForm.display_mode = 'command'">Command</button>
|
|
<button class="tab-btn" :class="{ active: editForm.display_mode === 'script' }"
|
|
@click="editForm.display_mode = 'script'">Script</button>
|
|
</div>
|
|
</div>
|
|
|
|
<template x-if="editForm.display_mode === 'command'">
|
|
<div class="form-group">
|
|
<label>Command</label>
|
|
<input type="text" x-model="editForm.display_command"
|
|
placeholder="curl -s http://...">
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.display_mode === 'script'">
|
|
<div class="form-group">
|
|
<label>Script path</label>
|
|
<input type="text" x-model="editForm.display_script"
|
|
placeholder="/path/to/script.sh">
|
|
</div>
|
|
</template>
|
|
|
|
<template x-if="editForm.display_mode !== 'none'">
|
|
<div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label>Interval</label>
|
|
<input type="text" x-model="editForm.display_interval" placeholder="30s">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Timeout</label>
|
|
<input type="text" x-model="editForm.display_timeout" placeholder="30s">
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Max output length</label>
|
|
<input type="number" x-model.number="editForm.display_max_len" min="16" max="4096" placeholder="128">
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="modal-actions">
|
|
<button class="btn btn-primary" @click="saveKey">Save</button>
|
|
<button class="btn btn-danger" @click="deleteKey">Delete</button>
|
|
<button class="btn btn-outline" @click="editing = null">Cancel</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ Toast ═══ -->
|
|
<div class="toast" :class="toast?.type" x-show="toast" x-transition x-text="toast?.msg"></div>
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|