test/index.html
OpenClaw Agent ca521f79b1
All checks were successful
Deploy to dev.bl.pixeldev.eu / deploy (push) Successful in 2s
feat: double-click inline editing for card text
Replace the Edit button with dblclick-to-edit on the paragraph itself:
- Double-click #card-text → hides the <p>, shows a textarea in place
- Ctrl+Enter → saves (PUT /api/text)
- Escape → cancels and restores original text
- Textarea auto-sizes to content height on open
- Subtle dashed underline on hover signals editability
- Edit button removed; Reset button stays

Closes #9
2026-03-13 15:22:24 +00:00

249 lines
8.8 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<title>Deploy Test</title>
<style>
:root[data-theme="light"] {
--bg: #f5f5f5;
--fg: #111111;
--card: #ffffff;
--border: #ddd;
--accent: #4a6ef5;
--btn-bg: #111;
--btn-fg: #fff;
--btn-hover: #333;
--input-bg: #f9f9f9;
--meta: #888;
}
:root[data-theme="dark"] {
--bg: #0d0d0d;
--fg: #e0e0e0;
--card: #161616;
--border: #2a2a2a;
--accent: #7c9ef5;
--btn-bg: #2a2a2a;
--btn-fg: #e0e0e0;
--btn-hover: #3a3a3a;
--input-bg: #0d0d0d;
--meta: #555;
}
*, *::before, *::after { box-sizing: border-box; }
body {
background: var(--bg);
color: var(--fg);
font-family: system-ui, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
gap: 1rem;
transition: background .2s, color .2s;
margin: 0;
padding: 1rem;
}
h1 { margin: 0; }
p { max-width: 520px; text-align: center; line-height: 1.6; }
code { color: var(--accent); }
#theme-toggle {
position: fixed;
top: 1rem;
right: 1rem;
background: var(--btn-bg);
color: var(--btn-fg);
border: 1px solid var(--border);
border-radius: 8px;
padding: .45rem .85rem;
font-size: .85rem;
cursor: pointer;
font-family: inherit;
transition: background .15s;
}
#theme-toggle:hover { background: var(--btn-hover); }
/* Card */
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 14px;
padding: 1.4rem 1.8rem;
width: min(520px, 96vw);
line-height: 1.6;
box-shadow: 0 0 0 1px rgba(34, 197, 94, .15),
0 0 18px 4px rgba(34, 197, 94, .25),
0 0 40px 8px rgba(34, 197, 94, .1);
transition: background .2s, border-color .2s, box-shadow .2s;
}
:root[data-theme="dark"] .card {
box-shadow: 0 0 0 1px rgba(34, 197, 94, .2),
0 0 20px 5px rgba(34, 197, 94, .3),
0 0 50px 10px rgba(34, 197, 94, .12);
}
#card-text {
margin: 0 0 .4rem; cursor: text;
border-bottom: 1px dashed transparent; transition: border-color .15s;
}
#card-text:hover { border-bottom-color: var(--border); }
#card-dblclick-hint { color: var(--meta); font-size: .68rem; margin-bottom: .7rem; }
#card-meta { color: var(--meta); font-size: .72rem; margin-bottom: .7rem; min-height: 1rem; }
.card-actions { display: flex; gap: .5rem; justify-content: flex-end; }
.card-btn {
background: var(--btn-bg);
color: var(--btn-fg);
border: 1px solid var(--border);
border-radius: 7px;
padding: .35rem .75rem;
font-size: .8rem;
cursor: pointer;
font-family: inherit;
transition: background .15s;
}
.card-btn:hover { background: var(--btn-hover); }
.card-btn:disabled { opacity: .4; cursor: default; }
.card-btn-danger { color: #f57c7c; border-color: rgba(245,124,124,.3); }
.card-btn-danger:hover { background: rgba(245,124,124,.1); }
#edit-wrap { display: none; flex-direction: column; gap: .5rem; margin-bottom: .4rem; }
#edit-wrap.open { display: flex; }
#edit-input {
width: 100%; min-height: 80px; background: var(--input-bg); color: var(--fg);
border: 1px solid var(--accent); border-radius: 8px; padding: .55rem .7rem;
font-family: inherit; font-size: inherit; line-height: 1.6; resize: vertical; outline: none;
}
.edit-hint { font-size: .72rem; color: var(--meta); }
#card-status { font-size: .75rem; color: var(--meta); min-height: 1rem; text-align: right; }
</style>
</head>
<body>
<button id="theme-toggle">🌙 Dark mode</button>
<h1>🚀 Deploy verified ✅</h1>
<div class="card" id="text-card">
<p id="card-text">Loading…</p>
<div id="edit-wrap">
<textarea id="edit-input" placeholder="Enter text…"></textarea>
<span class="edit-hint">Ctrl+Enter to save · Esc to cancel</span>
</div>
<div id="card-meta"></div>
<div class="card-actions">
<span id="card-status"></span>
<button class="card-btn card-btn-danger" id="reset-btn">↺ Reset</button>
</div>
</div>
<p>Committed by OpenClaw at <code>2026-03-13 10:11:00 UTC</code></p>
<script>
// ── Theme ──────────────────────────────────────────────────────────────
(function() {
var root = document.documentElement;
var btn = document.getElementById('theme-toggle');
var saved = localStorage.getItem('theme') || 'light';
setTheme(saved);
btn.addEventListener('click', function() {
setTheme(root.getAttribute('data-theme') === 'light' ? 'dark' : 'light');
});
function setTheme(t) {
root.setAttribute('data-theme', t);
localStorage.setItem('theme', t);
btn.textContent = t === 'light' ? '🌙 Dark mode' : '☀️ Light mode';
}
})();
// ── CRUD card ──────────────────────────────────────────────────────────
(function() {
var BASE = '/test';
var textEl = document.getElementById('card-text');
var editWrap = document.getElementById('edit-wrap');
var editInput = document.getElementById('edit-input');
var metaEl = document.getElementById('card-meta');
var statusEl = document.getElementById('card-status');
var resetBtn = document.getElementById('reset-btn');
var editing = false;
function setMeta(updated_at) {
metaEl.textContent = updated_at
? 'Last updated: ' + new Date(updated_at).toLocaleString()
: '';
}
function setStatus(msg, color) {
statusEl.textContent = msg;
statusEl.style.color = color || '';
if (msg) setTimeout(function() { statusEl.textContent = ''; }, 2500);
}
// READ
function load() {
fetch(BASE + '/api/text')
.then(function(r) { return r.json(); })
.then(function(d) { textEl.textContent = d.content || ''; setMeta(d.updated_at); })
.catch(function() { textEl.textContent = '(failed to load)'; });
}
load();
// Enter edit mode on double-click
textEl.addEventListener('dblclick', function() {
if (editing) return;
editing = true;
editInput.value = textEl.textContent;
textEl.style.display = 'none';
editWrap.classList.add('open');
// Auto-size textarea to content
editInput.style.height = 'auto';
editInput.style.height = editInput.scrollHeight + 'px';
editInput.focus();
editInput.select();
});
// Cancel on Escape, save on Ctrl+Enter
editInput.addEventListener('keydown', function(e) {
if (e.key === 'Escape') { cancelEdit(); }
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); saveEdit(); }
});
function cancelEdit() {
editing = false;
editWrap.classList.remove('open');
textEl.style.display = '';
}
// UPDATE
function saveEdit() {
var content = editInput.value.trim();
if (!content) { setStatus('Cannot be empty.', '#f57c7c'); return; }
setStatus('Saving…', '');
fetch(BASE + '/api/text', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: content })
}).then(function(r) { return r.json(); }).then(function(d) {
if (d.error) { setStatus('Error: ' + d.error, '#f57c7c'); return; }
textEl.textContent = d.content;
setMeta(d.updated_at);
setStatus('Saved ✓', '#6fcf6f');
cancelEdit();
}).catch(function() { setStatus('Network error.', '#f57c7c'); });
}
// DELETE (reset)
resetBtn.addEventListener('click', function() {
if (editing) cancelEdit();
if (!confirm('Reset to default text?')) return;
resetBtn.disabled = true;
fetch(BASE + '/api/text', { method: 'DELETE' })
.then(function(r) { return r.json(); })
.then(function(d) {
textEl.textContent = d.content;
setMeta(d.updated_at);
setStatus('Reset ✓', '#6fcf6f');
resetBtn.disabled = false;
}).catch(function() { setStatus('Network error.', '#f57c7c'); resetBtn.disabled = false; });
});
})();
</script>
<script src="https://dev.bl.pixeldev.eu/feedback-tool/widget.js" data-repo="pixeldev/test"></script>
</body>
</html>