[object Object]

← 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=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[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 →