(function (root, factory) { const createSearchBatchPlan = factory(); if (typeof module === 'object' && module.exports) module.exports = createSearchBatchPlan; if (root) { root.createSearchBatchPlan = createSearchBatchPlan; root.mountSearchBatchPlanning = createSearchBatchPlan.mount; } })(typeof globalThis !== 'undefined' ? globalThis : this, function () { function createSearchBatchPlan({ limit = 50, resolve = item => Promise.resolve(item), claim = item => Promise.resolve(item), onChange = () => {}, } = {}) { const maximum = Number.isInteger(limit) && limit > 0 ? limit : 50; const selected = new Map(); let active = false; const identity = item => [item?.kind || '', item?.repository || '', item?.number || ''].join(':'); const eligible = item => item?.kind === 'issue' && item?.state === 'open' && Boolean(item.repository) && Number.isInteger(item.number); function snapshot() { return { active, count:selected.size, items:Array.from(selected.values()) }; } function changed() { const state = snapshot(); onChange(state); return state; } function start() { active = true; selected.clear(); return changed(); } function cancel() { active = false; selected.clear(); return changed(); } function toggle(item) { if (!active) return 'inactive'; if (!eligible(item)) return 'ineligible'; const key = identity(item); if (selected.has(key)) { selected.delete(key); changed(); return 'removed'; } if (selected.size >= maximum) return 'limit'; selected.set(key, { ...item }); changed(); return 'selected'; } async function prepare(item) { const detail = await resolve(item); if (detail?.assigned_to_me) return detail; if (detail?.claimable) return claim(detail); throw new Error('This issue is no longer available to assign.'); } return { identity, eligible, start, cancel, toggle, snapshot, prepare, limit:maximum }; } createSearchBatchPlan.mount = function mountSearchBatchPlanning( document, batchFactory, todayWork, getOwner, fetchJson, previewPath, queueToday, acceptIssue, lookup, render, escapeHtml, escapeAttribute ) { const get = selector => document.querySelector(selector); let processor; const plan = createSearchBatchPlan({ resolve:item => fetchJson(previewPath(item), {headers:{Accept:'application/json'}}), claim:detail => fetchJson( 'api/v1/repos/' + detail.repository.split('/').map(encodeURIComponent).join('/') + '/issues/' + encodeURIComponent(detail.number) + '/claim', {method:'PATCH', headers:{Accept:'application/json'}} ), onChange:state => { get('#search-batch-actions').hidden = !state.active; get('#select-search-results').hidden = state.active; get('#queue-selected-search-results').disabled = state.count === 0; get('#search-selection-status').textContent = state.count ? state.count + ' issue' + (state.count === 1 ? '' : 's') + ' selected.' : 'No issues selected.'; render(); }, }); processor = batchFactory({ capacity:() => Math.max(0, todayWork.limit - todayWork.read().length), owner:getOwner, journalName:'search-today-batch', autoMount:false, claim:item => plan.prepare(item), queue:confirmed => queueToday(acceptIssue(confirmed)), onProgress:progress => { get('#queue-selected-search-results').disabled = progress.status === 'running'; if (progress.status === 'running') { get('#search-selection-status').textContent = 'Planning ' + progress.processed + ' of ' + progress.selected + '…'; } else if (progress.status === 'full') { get('#search-selection-status').textContent = 'Today has ' + progress.available + ' remaining slot' + (progress.available === 1 ? '.' : 's.'); } else if (progress.status === 'complete') { get('#cmd-search-action-status').textContent = progress.failed.length ? progress.queued.length + ' queued · ' + progress.failed.length + ' need retry.' : progress.queued.length + ' added to Today.'; get('#resume-search-batch').hidden = processor.pending() === 0; if (!progress.failed.length) plan.cancel(); } }, }); get('#select-search-results').addEventListener('click', () => plan.start()); get('#cancel-search-selection').addEventListener('click', () => plan.cancel()); get('#queue-selected-search-results').addEventListener('click', () => processor.run(plan.snapshot().items)); get('#resume-search-batch').addEventListener('click', () => processor.resume()); get('#open-palette').addEventListener('click', () => restore()); get('#cmd-results').addEventListener('change', event => { const index = event.target?.dataset?.searchSelect; if (index === undefined) return; const result = lookup(Number(index)); if (result) plan.toggle(result); }); function restore() { const pending = processor.pending(); get('#resume-search-batch').hidden = pending === 0; get('#resume-search-batch').textContent = 'Resume ' + pending + ' interrupted'; } function resultHtml(result, index) { const allowed = result.kind === 'issue' && result.state === 'open'; const selected = plan.snapshot().items.some(item => plan.identity(item) === plan.identity(result)); return ''; } return { plan, processor, restore, resultHtml }; }; return createSearchBatchPlan; });