ui: version-display via JS, undo-toast centering, ctrl+enter save, test progress polish
* version: every span that shows v<APP_VERSION> in the page chrome now carries data-app-version, and checkAuth() patches them in from the /api/auth/status response so the GUI version doesn't go stale when PHP is cached behind anything that revalidates lazily. * undo: tailwind v4 compiles `-translate-x-1/2` to the *new* CSS `translate` property, which then stacks with the inline `transform` the toast set — the box ended up shifted ~ -100% and looked left- anchored. Drop the tailwind utility, set `translate` directly inline so there's one source of truth for the offset. * editor: ctrl/cmd+enter while the editor modal is open now invokes saveCard() (= save + close), pairing with the existing ctrl+s (save only) and ctrl+shift+s (save+close) shortcuts. * collab buckets: drop the inset left edge from .bucket-tab-collab and the matching pulse-keyframe shadow — the bar tab now reads as a normal bucket tab, just with the unread dot + 3-cycle pulse on update. * collab create dialog: drop the box border / background from .collab-toggle-row and .collab-members so the toggle and the user list sit directly on the modal background instead of looking like nested input boxes. * test progress strip: ensureProgressBar() now also pushes the body down by 30px AND shrinks it via height: calc(100vh - 30px) so the fixed-top bar doesn't clip the footer (body is h-screen + overflow-hidden in the core layout). hideProgress() clears both inline styles when the run finishes. chore: bump version to 0.9.2 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
fe3399a44b
commit
2059e50ce3
7 files changed
+39
-26
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
define('APP_NAME', 'hyprcrd');
|
||||
define('APP_VERSION', '0.9.1');
|
||||
define('APP_VERSION', '0.9.2');
|
||||
|
||||
// Database path — writable directory, outside webroot
|
||||
define('DB_PATH', __DIR__ . '/data/hyprcrd.db');
|
||||
|
||||
+3
-3
@@ -1185,7 +1185,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
<div class="flex items-center gap-3">
|
||||
<i class="ph ph-terminal-window text-t-accent text-xl"></i>
|
||||
<h1 class="brand font-pixel text-t-accent text-lg tracking-wider">hyprcrd</h1>
|
||||
<span class="text-[10px] text-t-dim font-pixel ml-1">v<?= APP_VERSION ?></span>
|
||||
<span class="text-[10px] text-t-dim font-pixel ml-1" data-app-version>v<?= APP_VERSION ?></span>
|
||||
<button id="search-btn" class="flex items-center gap-2 px-2.5 py-1.5 text-xs border border-t-border text-t-muted hover:border-t-accent hover:text-t-accent transition-colors ml-3">
|
||||
<i class="ph ph-magnifying-glass text-sm"></i><span class="font-pixel text-[10px]">Search</span>
|
||||
<kbd class="text-[9px] text-t-dim border border-t-border rounded px-1 py-0.5 ml-1 font-pixel">ctrl+k</kbd>
|
||||
@@ -1478,7 +1478,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
</section>
|
||||
|
||||
<div class="pt-4 border-t border-t-border text-center">
|
||||
<span class="font-pixel text-[10px] text-t-dim">© <?= date('Y') ?> hyprcrd · v<?= APP_VERSION ?></span>
|
||||
<span class="font-pixel text-[10px] text-t-dim">© <?= date('Y') ?> hyprcrd · <span data-app-version>v<?= APP_VERSION ?></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1509,7 +1509,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<i class="ph ph-terminal-window text-t-accent text-5xl mb-4"></i>
|
||||
<h1 class="brand font-pixel text-2xl text-t-accent tracking-wider mb-2">hyprcrd</h1>
|
||||
<p class="font-pixel text-[10px] text-t-dim mb-6">v<?= APP_VERSION ?></p>
|
||||
<p class="font-pixel text-[10px] text-t-dim mb-6" data-app-version>v<?= APP_VERSION ?></p>
|
||||
<p class="text-xs text-t-muted leading-relaxed max-w-sm">Self-hosted card manager with markdown, tags, calendar, CalDAV sync, encrypted WebDAV notes, and Obsidian-compatible export.</p>
|
||||
<div class="flex items-center gap-4 mt-8 text-t-dim">
|
||||
<div class="text-center"><div id="stats-cards" class="font-pixel text-lg text-t-bright">—</div><div class="font-pixel text-[8px] uppercase">cards</div></div>
|
||||
|
||||
@@ -158,6 +158,13 @@ async function checkAuth() {
|
||||
const res = await fetch('api/auth/status');
|
||||
const data = await res.json();
|
||||
if (data.csrf) { setCsrf(data.csrf); window.hyprcrd?.util?.setCsrf?.(data.csrf); }
|
||||
// Refresh version-displaying spans from the live API response so the
|
||||
// GUI doesn't show a stale APP_VERSION when the PHP page is cached.
|
||||
if (data.version) {
|
||||
for (const el of document.querySelectorAll('[data-app-version]')) {
|
||||
el.textContent = 'v' + data.version;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.setup) {
|
||||
let pw = null;
|
||||
@@ -223,6 +230,7 @@ document.addEventListener('keydown', e => {
|
||||
return;
|
||||
}
|
||||
if (e.key === 's' && (e.ctrlKey || e.metaKey) && modal.classList.contains('open')) { e.preventDefault(); e.shiftKey ? saveCard() : saveOnly(); return; }
|
||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey) && modal.classList.contains('open')) { e.preventDefault(); saveCard(); return; }
|
||||
if (e.key === 'z' && (e.ctrlKey || e.metaKey) && !modal.classList.contains('open') && canUndo()) { e.preventDefault(); undo(); return; }
|
||||
if (e.key === '/' && ![modal, detail, search, tasks].some(el => el?.classList.contains('open')) && document.activeElement === document.body) { e.preventDefault(); openSearch(); }
|
||||
|
||||
|
||||
@@ -66,21 +66,29 @@ export function pushBatch(label, patches) {
|
||||
}
|
||||
|
||||
// Toast hint
|
||||
//
|
||||
// Centering note: tailwind v4 compiles `-translate-x-1/2` to the *new*
|
||||
// CSS `translate` property, which then stacks with any inline `transform`
|
||||
// we set — the box ended up shifted ~ -100% (utility translate + JS
|
||||
// transform translate both applied) and looked left-anchored. Fix: drop
|
||||
// the tailwind translate utility and centre purely via the modern
|
||||
// `translate` property in inline styles, which is one source of truth.
|
||||
let hintTimer = null;
|
||||
function showHint(text) {
|
||||
let el = document.getElementById('undo-hint');
|
||||
if (!el) {
|
||||
el = document.createElement('div');
|
||||
el.id = 'undo-hint';
|
||||
el.className = 'fixed bottom-16 left-1/2 -translate-x-1/2 z-[95] bg-t-surface border border-t-border px-4 py-2 text-xs font-pixel text-t-muted shadow-lg transition-all';
|
||||
el.className = 'fixed bottom-16 left-1/2 z-[95] bg-t-surface border border-t-border px-4 py-2 text-xs font-pixel text-t-muted shadow-lg transition-all';
|
||||
el.style.translate = '-50% 0';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
el.textContent = text + ' (ctrl+z undo)';
|
||||
el.style.opacity = '1';
|
||||
el.style.transform = 'translateX(-50%) translateY(0)';
|
||||
el.style.translate = '-50% 0';
|
||||
clearTimeout(hintTimer);
|
||||
hintTimer = setTimeout(() => {
|
||||
el.style.opacity = '0';
|
||||
el.style.transform = 'translateX(-50%) translateY(8px)';
|
||||
el.style.translate = '-50% 8px';
|
||||
}, 2500);
|
||||
}
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
.bucket-tab.bucket-tab-collab {
|
||||
position: relative;
|
||||
/* Subtle left emphasis so collab tabs read differently at a glance */
|
||||
box-shadow: inset 2px 0 0 0 color-mix(in srgb, var(--t-accent2) 70%, transparent);
|
||||
}
|
||||
|
||||
.bucket-tab.bucket-tab-collab .collab-tab-dot {
|
||||
@@ -35,14 +33,8 @@ body.anim-slow .bucket-tab.bucket-tab-collab.pulsing {
|
||||
animation-duration: 1.2s !important;
|
||||
}
|
||||
@keyframes collab-bucket-pulse {
|
||||
0%, 100% {
|
||||
box-shadow: inset 2px 0 0 0 color-mix(in srgb, var(--t-accent2) 70%, transparent),
|
||||
0 0 0 0 color-mix(in srgb, var(--t-danger) 60%, transparent);
|
||||
}
|
||||
50% {
|
||||
box-shadow: inset 2px 0 0 0 color-mix(in srgb, var(--t-accent2) 70%, transparent),
|
||||
0 0 0 4px color-mix(in srgb, var(--t-danger) 0%, transparent);
|
||||
}
|
||||
0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--t-danger) 60%, transparent); }
|
||||
50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--t-danger) 0%, transparent); }
|
||||
}
|
||||
|
||||
/* ── Create / edit modal (used by the + Bucket button when the plugin
|
||||
@@ -136,26 +128,20 @@ body.anim-slow .bucket-tab.bucket-tab-collab.pulsing {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
background: var(--t-bg);
|
||||
border: 1px solid var(--t-border);
|
||||
border-radius: var(--ui-radius-input);
|
||||
padding: 6px 0;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: var(--t-text);
|
||||
user-select: none;
|
||||
}
|
||||
.collab-toggle-row input { accent-color: var(--t-accent); cursor: pointer; }
|
||||
.collab-toggle-row:hover { border-color: var(--t-accent); color: var(--t-bright); }
|
||||
.collab-toggle-row:hover { color: var(--t-bright); }
|
||||
|
||||
.collab-members-wrap { margin-top: 10px; }
|
||||
.collab-members {
|
||||
border: 1px solid var(--t-border);
|
||||
border-radius: var(--ui-radius-input);
|
||||
background: var(--t-bg);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
padding: 0;
|
||||
}
|
||||
.collab-members-empty {
|
||||
font-size: 11px;
|
||||
|
||||
@@ -43,6 +43,12 @@ const RUNNER_V = '?v=2';
|
||||
|
||||
function ensureProgressBar() {
|
||||
if (document.getElementById('test-progress')) return;
|
||||
// Push the whole document down by 30px AND shrink the body by the
|
||||
// same amount so the footer doesn't get clipped (body is h-screen +
|
||||
// overflow-hidden, so without the height shrink the bottom 30px get
|
||||
// pushed off-viewport). hideProgress() reverts both.
|
||||
document.body.style.marginTop = '30px';
|
||||
document.body.style.height = 'calc(100vh - 30px)';
|
||||
const bar = document.createElement('div');
|
||||
bar.id = 'test-progress';
|
||||
const s = bar.style;
|
||||
|
||||
@@ -234,6 +234,11 @@ function updateProgress(current, total, name, failed) {
|
||||
}
|
||||
function hideProgress() {
|
||||
getProgressBar()?.remove();
|
||||
// ensureProgressBar() in loader.js pushed the document down by 30px and
|
||||
// shrunk it by the same amount so the footer wouldn't get clipped.
|
||||
// Restore both inline overrides here.
|
||||
document.body.style.marginTop = '';
|
||||
document.body.style.height = '';
|
||||
}
|
||||
|
||||
async function runAll() {
|
||||
|
||||
Reference in new issue
Block a user