diff --git a/README.md b/README.md index 9d4d60f..240c5b5 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,11 @@ and durable delivery flow. A different or unconfirmed account can only copy or d the private content. Issue capture and authored mobile actions (issue comments, pull-request comments, notification replies, and reviews) persist per-draft idempotency keys, so retrying after a timeout, reload, process restart, or handoff to -another worker replays a confirmed result instead of posting duplicate content. The ordered, +another worker replays a confirmed result instead of posting duplicate content. Mobile Search previews +let operators assign an eligible issue and add it to Today without starting or replacing active work. +The queue action keeps the Search query, filters, results, and scroll position available for continued +planning, reports an existing Today item without duplicating it, and uses the same capacity, sync, and +offline-warm path as other Today admission flows. The ordered, five-item Today plan syncs across the operator's devices. **Plan Today** also stores available minutes and a per-item estimate with the account-scoped plan, continuously showing planned/free or over-capacity time. An over-capacity plan requires a second explicit save, legacy plans migrate with unestimated work, diff --git a/frontend/dashboard.css b/frontend/dashboard.css index 0f92001..9cf0ad0 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -549,6 +549,9 @@ textarea { resize: vertical; min-height: 120px; } .search-preview-actions { position:sticky; bottom:0; display:grid; gap:8px; padding:10px 0; padding-bottom:calc(10px + env(safe-area-inset-bottom)); background:#0b1526; } .search-preview-actions button, .search-preview-actions a { min-height:44px; box-sizing:border-box; display:flex; align-items:center; justify-content:center; } .search-preview-primary-actions { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; } +@media (max-width:420px) { + .search-preview-primary-actions { grid-template-columns:1fr; } +} .search-preview-actions a { display:flex; align-items:center; justify-content:center; border:1px solid #60a5fa; border-radius:10px; font-weight:700; } .markdown-content { min-width:0; max-width:100%; overflow-wrap:anywhere; white-space:normal; } .markdown-content > :first-child { margin-top:0; } diff --git a/frontend/dashboard.js b/frontend/dashboard.js index b14a8d3..db0fba2 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -4330,6 +4330,7 @@ const sheet = qs('#search-preview'); const status = qs('#search-preview-status'); const claimButton = qs('#claim-search-result'); + const queueButton = qs('#queue-search-result'); const startButton = qs('#start-search-result'); const shareButton = qs('#share-search-result'); @@ -4342,6 +4343,7 @@ sheet.classList.add('open'); claimButton.hidden = true; claimButton.disabled = false; + queueButton.hidden = true; startButton.hidden = true; startButton.disabled = false; shareButton.disabled = true; @@ -4376,6 +4378,9 @@ claimButton.hidden = !(detail.claimable || detail.assigned_to_me); claimButton.textContent = detail.assigned_to_me ? 'Open in My Work' : 'Assign to me'; claimButton.disabled = state.status === 'claiming'; + queueButton.hidden = !(detail.kind === 'issue' && detail.state === 'open' && + (detail.claimable || detail.assigned_to_me)); + queueButton.textContent = detail.assigned_to_me ? 'Add to Today' : 'Assign & add to Today'; startButton.hidden = !(detail.reviewable || (detail.kind === 'issue' && (detail.reopenable || (detail.state === 'open' && (detail.claimable || detail.assigned_to_me))))); startButton.textContent = detail.reviewable ? 'Review now' : detail.reopenable ? 'Reopen & resume' : @@ -4423,6 +4428,7 @@ refreshMyWorkView(); return createAndStart.complete(item); }, + queue: confirmed => queueToday(acceptClaimedIssue(confirmed)), recover: confirmed => { const item = acceptClaimedIssue(confirmed); taskOverlayHistory.leave(); @@ -4431,6 +4437,7 @@ }, announce: message => { qs('#search-preview-status').textContent = message; + qs('#cmd-search-action-status').textContent = message; qs('#my-work-action-status').textContent = message; }, }); @@ -4729,6 +4736,25 @@ qs('#search-preview-status').textContent = error.message + ' Retry assignment.'; } }); + qs('#queue-search-result').addEventListener('click', async () => { + const detail = searchPreviewDetail; + if (!detail || detail.kind !== 'issue' || detail.state !== 'open' || + (!detail.claimable && !detail.assigned_to_me)) return; + try { + if (todayWork.contains(detail)) { + qs('#cmd-search-action-status').textContent = 'Already in Today.'; + closeSearchPreview(); + return; + } + const outcome = await searchAssignAndStart.run(detail, { + alreadyOwned: detail.assigned_to_me, + destination: 'queue', + }); + if (outcome === 'queued') closeSearchPreview(); + } catch (error) { + qs('#search-preview-status').textContent = error.message + ' Retry.'; + } + }); qs('#start-search-result').addEventListener('click', async () => { const detail = searchPreviewDetail; if (detail?.reviewable) { diff --git a/frontend/index.html b/frontend/index.html index 06591d8..cb8445b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -448,6 +448,7 @@ +
@@ -467,6 +468,7 @@