function createProgressiveCapture({ document, storage, getLogin = () => '', createCaptures = globalThis.createUnfiledCaptures, requestFullWorkspace = async () => { const lifecycle = await globalThis.stackchainWorkspaceLifecycle; return lifecycle?.optionalReady; }, createId, now, }) { const button = document.querySelector('[data-mobile-task="new"]'); const root = document.querySelector('#create-issue-sheet'); const title = document.querySelector('#create-issue-title'); const body = document.querySelector('#create-issue-body'); const heading = document.querySelector('#create-issue-heading'); const save = document.querySelector('#save-unfiled-issue'); const file = document.querySelector('#file-new-issue'); const cancel = document.querySelector('#cancel-new-issue'); const status = document.querySelector('#my-work-action-status'); const captures = createCaptures({ storage, getCaptureLogin:getLogin, getCurrentLogin:getLogin, ...(createId ? {createId} : {}), ...(now ? {now} : {}), }); const listeners = []; let started = false; let saving = null; let saved = false; let handedOff = false; const checkpointKey = 'stackchain.progressive-capture.v1'; function listen(element, name, callback) { element?.addEventListener?.(name, callback); listeners.push([element, name, callback]); } function close() { root?.classList.remove('open'); } function readCheckpoints() { try { const record = JSON.parse(storage?.getItem(checkpointKey) || 'null'); if (record?.version !== 1 || !Array.isArray(record.items)) return []; return record.items.filter(item => item && typeof item.ownerLogin === 'string' && typeof item.title === 'string' && typeof item.body === 'string'); } catch (_error) { return []; } } function readCheckpoint() { const login = String(getLogin() || '').trim(); if (!login) return null; return readCheckpoints().find(item => item.ownerLogin === login) || null; } function checkpoint() { const ownerLogin = String(getLogin() || '').trim(); if (!ownerLogin) return false; try { const items = readCheckpoints().filter(item => item.ownerLogin !== ownerLogin); items.push({ ownerLogin, title:String(title?.value || '').slice(0, 255), body:String(body?.value || '').slice(0, 10000), }); storage?.setItem(checkpointKey, JSON.stringify({ version:1, items, })); return true; } catch (_error) { return false; } } function clearCheckpoint(expected) { const current = readCheckpoint(); if (!current || current.title !== expected.title || current.body !== expected.body) return false; try { const ownerLogin = String(getLogin() || '').trim(); const items = readCheckpoints().filter(item => item.ownerLogin !== ownerLogin); if (items.length) storage?.setItem(checkpointKey, JSON.stringify({version:1, items})); else storage?.removeItem(checkpointKey); return true; } catch (_error) { return false; } } function open(event) { saved = false; const recovered = readCheckpoint(); if (recovered) { if (title) title.value = recovered.title; if (body) body.value = recovered.body; if (status) status.textContent = 'Unfinished capture restored from this phone.'; } if (heading) heading.textContent = 'Capture work'; root?.classList.add('open'); title?.focus?.(); } async function saveDraft() { if (saved || saving) return saving; save.disabled = true; const draft = {title:title?.value || '', body:body?.value || ''}; saving = Promise.resolve().then(() => captures.save(draft)).then(() => { saved = true; clearCheckpoint(draft); if (title) title.value = ''; if (body) body.value = ''; close(); if (status) status.textContent = 'Saved to Drafts. Choose a repository when you’re ready to file it.'; return true; }).catch(error => { if (status) status.textContent = error.message; title?.focus?.(); return false; }).finally(() => { save.disabled = false; saving = null; }); return saving; } async function fileNow() { if (file.disabled) return false; file.disabled = true; if (status) status.textContent = 'Loading filing tools…'; try { await requestFullWorkspace(); return true; } catch (_error) { file.disabled = false; if (status) status.textContent = 'Filing tools unavailable. Your capture is still editable; retry when connected.'; title?.focus?.(); return false; } } function start() { if (started) return false; started = true; root?.classList.add('progressive-capture'); listen(button, 'click', open); listen(save, 'click', saveDraft); listen(file, 'click', fileNow); listen(cancel, 'click', close); listen(title, 'input', checkpoint); listen(body, 'input', checkpoint); return true; } function handoff() { if (handedOff) return null; handedOff = true; const state = { open:Boolean(root?.classList.contains('open')), title:title?.value || '', body:body?.value || '', }; state.complete = () => clearCheckpoint(state); root?.classList.remove('progressive-capture'); file.disabled = false; listeners.forEach(([element, name, callback]) => element?.removeEventListener?.(name, callback)); listeners.length = 0; return state; } return {start, handoff}; } if (typeof window !== 'undefined' && typeof document !== 'undefined' && typeof createUnfiledCaptures === 'function' && window.matchMedia?.('(max-width: 600px)').matches === true) { window.stackchainProgressiveCapture = createProgressiveCapture({ document, storage:window.localStorage, getLogin:() => window.stackchainProgressiveMyWork?.login?.() || '', }); window.stackchainProgressiveCapture.start(); } if (typeof module !== 'undefined' && module.exports) module.exports = createProgressiveCapture;