Skip to content

Commit 0a114e3

Browse files
Kasper Jungeclaude
authored andcommitted
feat: add Cmd+S / Ctrl+S keyboard shortcut to primitive editor forms
Users can now press Cmd+S (Mac) or Ctrl+S (Windows/Linux) to save or create primitives without scrolling to the action bar. The Save/Create buttons show a subtle keyboard hint badge for discoverability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c9c18e3 commit 0a114e3

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

src/ralphify/ui/static/dashboard.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,20 @@ body {
317317
box-shadow: 0 2px 6px rgba(109, 74, 232, 0.35);
318318
}
319319

320+
.btn-kbd {
321+
display: inline-block;
322+
margin-left: 6px;
323+
padding: 1px 5px;
324+
font-size: 0.7rem;
325+
font-family: inherit;
326+
border: 1px solid rgba(255, 255, 255, 0.25);
327+
border-radius: 3px;
328+
background: rgba(255, 255, 255, 0.1);
329+
vertical-align: baseline;
330+
line-height: 1.3;
331+
opacity: 0.7;
332+
}
333+
320334
.btn-danger {
321335
border-color: rgba(248, 113, 113, 0.4);
322336
color: var(--red);

src/ralphify/ui/static/dashboard.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,19 @@ function PrimEditForm({ primitive, kind, meta, onBack, onSaved }) {
12621262
(hasCommand && command !== (primitive.frontmatter?.command || '')) ||
12631263
(hasCommand && timeoutVal !== (primitive.frontmatter?.timeout != null ? String(primitive.frontmatter.timeout) : ''));
12641264

1265+
// Cmd+S / Ctrl+S to save
1266+
const saveRef = useRef(null);
1267+
useEffect(() => {
1268+
const onKey = (e) => {
1269+
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
1270+
e.preventDefault();
1271+
saveRef.current?.();
1272+
}
1273+
};
1274+
document.addEventListener('keydown', onKey);
1275+
return () => document.removeEventListener('keydown', onKey);
1276+
}, []);
1277+
12651278
async function handleSave() {
12661279
setSaving(true);
12671280
try {
@@ -1282,6 +1295,8 @@ function PrimEditForm({ primitive, kind, meta, onBack, onSaved }) {
12821295
}
12831296
setSaving(false);
12841297
}
1298+
// Keep ref in sync so Cmd+S always calls the latest handleSave (with current form state)
1299+
saveRef.current = hasChanges && !saving ? handleSave : null;
12851300

12861301
async function handleDelete() {
12871302
if (!confirm(`Delete "${primitive.name}"? This cannot be undone.`)) return;
@@ -1436,7 +1451,7 @@ function PrimEditForm({ primitive, kind, meta, onBack, onSaved }) {
14361451
<div style="flex: 1"></div>
14371452
<button class="btn" onClick=${onBack}>Cancel</button>
14381453
<button class="btn btn-primary" onClick=${handleSave} disabled=${!hasChanges || saving}>
1439-
${saving ? 'Saving...' : 'Save Changes'}
1454+
${saving ? 'Saving...' : html`Save Changes <kbd class="btn-kbd">${navigator.platform?.includes('Mac') ? '\u2318' : 'Ctrl'}S</kbd>`}
14401455
</button>
14411456
</div>
14421457
</div>
@@ -1455,6 +1470,19 @@ function PrimCreateForm({ kind, meta, onBack, onCreated }) {
14551470
const hasCommand = kind === 'checks' || kind === 'contexts';
14561471
const canCreate = name.trim().length > 0 && (!hasCommand || command.trim().length > 0);
14571472

1473+
// Cmd+S / Ctrl+S to create
1474+
const createRef = useRef(null);
1475+
useEffect(() => {
1476+
const onKey = (e) => {
1477+
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
1478+
e.preventDefault();
1479+
createRef.current?.();
1480+
}
1481+
};
1482+
document.addEventListener('keydown', onKey);
1483+
return () => document.removeEventListener('keydown', onKey);
1484+
}, []);
1485+
14581486
async function handleCreate() {
14591487
setCreating(true);
14601488
setError(null);
@@ -1474,6 +1502,7 @@ function PrimCreateForm({ kind, meta, onBack, onCreated }) {
14741502
}
14751503
setCreating(false);
14761504
}
1505+
createRef.current = canCreate && !creating ? handleCreate : null;
14771506

14781507
return html`
14791508
<div class="prim-editor">
@@ -1541,7 +1570,7 @@ function PrimCreateForm({ kind, meta, onBack, onCreated }) {
15411570
<div style="flex: 1"></div>
15421571
<button class="btn" onClick=${onBack}>Cancel</button>
15431572
<button class="btn btn-primary" onClick=${handleCreate} disabled=${!canCreate || creating}>
1544-
${creating ? 'Creating...' : `Create ${meta.label.replace(/s$/, '')}`}
1573+
${creating ? 'Creating...' : html`Create ${meta.label.replace(/s$/, '')} <kbd class="btn-kbd">${navigator.platform?.includes('Mac') ? '\u2318' : 'Ctrl'}S</kbd>`}
15451574
</button>
15461575
</div>
15471576
</div>

0 commit comments

Comments
 (0)