← back to Slack Idea Board
idea-board: per-card action buttons driven by next_step (Add new skill with repo / Build new project / Create agent)
433194cb9745cee235609fc1759e14d38dd9b9cf · 2026-07-13 14:51:18 -0700 · Steve Abrams
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Files touched
Diff
commit 433194cb9745cee235609fc1759e14d38dd9b9cf
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jul 13 14:51:18 2026 -0700
idea-board: per-card action buttons driven by next_step (Add new skill with repo / Build new project / Create agent)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
public/index.html | 45 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/public/index.html b/public/index.html
index 3739a53..632db85 100644
--- a/public/index.html
+++ b/public/index.html
@@ -82,9 +82,41 @@
</tr></thead>
<tbody id="rows"><tr><td colspan="3" class="empty">Loading your feed…</td></tr></tbody>
</table>
+<div id="toast" role="status" aria-live="polite"></div>
<script>
const stepClass = s => /build new project/i.test(s)?'build':/skill/i.test(s)?'skill':/agent/i.test(s)?'agent':'skip';
const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
+// Build a ready-to-run Claude Code kickoff command for an idea's next_step. SAFE — this is
+// only a STRING copied to the clipboard; nothing here shells out or executes anything.
+function kickoffCmd(i){
+ const from = i.title ? `${i.title} — ${i.url}` : i.url;
+ const d = (i.next_detail||i.about||i.title||'this idea').replace(/"/g,'\\"');
+ const s = i.next_step||'';
+ if(/build new project/i.test(s)) return `claude "Build a new project: ${d} (from idea: ${from})"`;
+ if(/skill/i.test(s)) return `claude "/skill-creator Create a new skill with its own git repo: ${d} (from idea: ${from})"`;
+ if(/agent/i.test(s)) return `claude "Create a new agent: ${d} (from idea: ${from})"`;
+ return '';
+}
+let TOAST_T;
+function toast(html){
+ const el=document.getElementById('toast'); el.innerHTML=html; el.classList.add('show');
+ clearTimeout(TOAST_T); TOAST_T=setTimeout(()=>el.classList.remove('show'),4200);
+}
+async function copyText(t){
+ try{ await navigator.clipboard.writeText(t); return true; }
+ catch{ try{ const ta=document.createElement('textarea'); ta.value=t; ta.style.position='fixed'; ta.style.opacity='0';
+ document.body.appendChild(ta); ta.select(); const ok=document.execCommand('copy'); ta.remove(); return ok; }catch{ return false; } }
+}
+async function doAction(btn){
+ const cmd=btn.dataset.cmd, label=btn.dataset.label;
+ const ok=await copyText(cmd);
+ if(ok){ btn.classList.add('copied'); const t=btn.textContent; btn.textContent='✓ Copied';
+ setTimeout(()=>{ btn.classList.remove('copied'); btn.textContent=t; },1400);
+ toast(`<b>${esc(label)}</b> command copied — paste into a Claude Code session:<br><code>${esc(cmd)}</code>`);
+ } else { toast('Could not access clipboard — command:<br><code>'+esc(cmd)+'</code>'); }
+}
+// Wire clicks via delegation so re-renders never lose handlers.
+document.addEventListener('click', e=>{ const b=e.target.closest('.act[data-cmd]'); if(b){ e.preventDefault(); doAction(b); } });
function fmt(iso){try{return new Date(iso).toLocaleString(undefined,{month:'short',day:'numeric',hour:'numeric',minute:'2-digit'})}catch{return iso}}
let LAST = { ideas:[], model:'' }, FILTER = 'actionable';
function passes(i){
@@ -116,7 +148,18 @@ function render(){
const c2 = i.pending ? '<span class="pending">analyzing…</span>'
: `<span class="fit ${i.fit}">${esc(i.fit)}</span><div class="reason">${esc(i.fit_reason)}</div>`;
const c3 = i.pending ? '<span class="pending">…</span>'
- : `<span class="step ${stepClass(i.next_step)}">${esc(i.next_step)}</span><div class="detail">${esc(i.next_detail)}</div>`;
+ : (()=>{
+ const step = esc(i.next_step||''), sc = stepClass(i.next_step);
+ const cmd = kickoffCmd(i);
+ const isSkip = sc==='skip' || !cmd;
+ const primary = isSkip
+ ? `<button class="act skip" disabled title="${esc(i.next_detail||'No action for this idea')}">Skip</button>`
+ : `<button class="act ${sc}" data-cmd="${esc(cmd)}" data-label="${step}" title="${esc(i.next_detail||step)}">${step}</button>`;
+ const open = i.url ? `<a class="act open" href="${esc(i.url)}" target="_blank" rel="noopener noreferrer">Open link ↗</a>` : '';
+ return `<span class="step ${sc}">${step}</span>`
+ + `<div class="detail">${esc(i.next_detail)}</div>`
+ + `<div class="actions">${primary}${open}</div>`;
+ })();
return `<tr>
<td class="col1">
<a class="link" href="${esc(i.url)}" target="_blank" rel="noopener noreferrer">${esc(i.title||host)} ↗</a>
← 372781e Add basic auth gate for public exposure at ideas.agentabrams
·
back to Slack Idea Board
·
idea-board: basic auth + per-card action buttons (copy claud 4309c45 →