Add per-adventure "Sync from library" for scripts
Adventure scripts are frozen snapshots taken at creation. Add an opt-in
way to pull the latest library code into a live adventure:
- AdventureScript.source_script_id links a copy to its library Script
(migration 24; set at adventure creation)
- list endpoint flags out_of_date by diffing copy vs library
- POST /adventures/{id}/scripts/{sid}/sync overwrites the copy's code,
preserving enabled/position/script_state
- legacy copies with no link fall back to a name match, then adopt the link
- Play page shows a Sync button only when a copy differs from its library
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEpWMjnfPqzZ13nPMoGhs5
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
94db6e055b
commit
328ae8942d
@@ -298,11 +298,24 @@ function ScriptsPanel({ advId }) {
|
||||
api.listAdventureScripts(advId).then(setScripts).catch(() => setScripts([]))
|
||||
}, [advId])
|
||||
|
||||
const [syncingId, setSyncingId] = useState(null)
|
||||
|
||||
const toggle = async (script) => {
|
||||
const updated = await api.updateAdventureScript(advId, script.id, { enabled: !script.enabled })
|
||||
setScripts((prev) => prev.map((s) => (s.id === script.id ? updated : s)))
|
||||
}
|
||||
|
||||
// Pull the latest code from the library script this copy was made from.
|
||||
const sync = async (script) => {
|
||||
setSyncingId(script.id)
|
||||
try {
|
||||
const updated = await api.syncAdventureScript(advId, script.id)
|
||||
setScripts((prev) => prev.map((s) => (s.id === script.id ? updated : s)))
|
||||
} finally {
|
||||
setSyncingId(null)
|
||||
}
|
||||
}
|
||||
|
||||
// Download as an import-compatible bundle (matches /scripts export), so demo
|
||||
// scripts can be forked into your own library via the Scripts page's Import.
|
||||
const download = (s) => {
|
||||
@@ -341,6 +354,16 @@ function ScriptsPanel({ advId }) {
|
||||
<span>{s.name}</span>
|
||||
</label>
|
||||
<div style={{ display: 'flex', gap: 12, flexShrink: 0 }}>
|
||||
{s.out_of_date && (
|
||||
<button
|
||||
className="linklike"
|
||||
title="Replace this adventure's copy with the latest from your script library"
|
||||
disabled={syncingId === s.id}
|
||||
onClick={() => sync(s)}
|
||||
>
|
||||
{syncingId === s.id ? 'Syncing…' : '⟳ Sync from library'}
|
||||
</button>
|
||||
)}
|
||||
<button className="linklike" onClick={() => setOpenId(open ? null : s.id)}>
|
||||
{open ? 'Hide code' : 'View code'}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user