Connect the two tools: cross-nav links and Send to Scope Lock handoff

- Add a header link on each tool pointing to the other.
- Add a Send to Scope Lock button on Workshop clusters that carry a
  candidate solution tag. Writes the item to a shared localStorage
  queue (sde_scope_handoff_v1).
- Scope Lock drains that queue on load and adds each item to the
  Scope Boundary Board as unsorted, deduping by a deterministic id so
  a repeat send never creates a duplicate.
- No backend needed: both tools already share the same browser origin.
- Verified with a jsdom test covering send, drain, reload, and
  re-send-after-reset.
- Update README and CHANGELOG.
This commit is contained in:
2026-09-04 14:44:16 -07:00
parent 50424acad1
commit 6baf908cf9
4 changed files with 77 additions and 2 deletions

View File

@@ -36,7 +36,7 @@
header .meta{font-size:13px; color:#c6c6c6;}
.hdr-btn{
background:transparent; color:#fff; border:1px solid #6f6f6f;
padding:7px 14px; font-size:14px;
padding:7px 14px; font-size:14px; text-decoration:none; display:inline-block;
}
.hdr-btn:hover{background:#353535;}
.hdr-btn.primary{background:var(--interactive); border-color:var(--interactive);}
@@ -231,6 +231,7 @@
<div class="brand"><strong>Project SDE</strong> <span>| Scope Lock Meeting Suite</span></div>
<div class="spacer"></div>
<div class="meta">Micron Pilot | Innovation Team</div>
<a class="hdr-btn" href="/tools/field-problem-workshop.html">Field Problem Workshop &rarr;</a>
<button class="hdr-btn" id="btnReset">Reset all</button>
<button class="hdr-btn primary" id="btnExport">Export meeting summary</button>
</header>
@@ -783,6 +784,33 @@ function confirmModal(opts, onConfirm){
}
function renderAll(){ renderScope(); renderRank(); renderRegistry(); renderReady(); }
/* ================= HANDOFF FROM FIELD PROBLEM WORKSHOP ================= */
// Shared localStorage queue the Workshop's "Send to Scope Lock" button writes to.
// Both tools are served from the same origin, so this works with no backend.
const SCOPE_HANDOFF_KEY = 'sde_scope_handoff_v1';
function drainScopeHandoff(){
let queue = [];
try{
const raw = localStorage.getItem(SCOPE_HANDOFF_KEY);
if(raw) queue = JSON.parse(raw);
if(!Array.isArray(queue)) queue = [];
}catch(e){ queue = []; }
if(!queue.length) return;
let added = 0;
queue.forEach(item=>{
if(!item || !item.id || !item.t) return;
if(S.scope.some(s=>s.id===item.id)) return; // already added on a previous load
S.scope.push({id:item.id, t:item.t, b:'', lock:false});
added++;
});
try{ localStorage.removeItem(SCOPE_HANDOFF_KEY); }catch(e){}
if(added){
save();
toast(added===1 ? 'Added 1 item from Field Problem Workshop' : 'Added ' + added + ' items from Field Problem Workshop');
}
}
drainScopeHandoff();
renderAll();
</script>
</body>