sign-off-axi 0.5.7 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/web/index.html +24 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sign-off-axi",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "One review board for evidence, quick yes/no decisions and retro changes.",
5
5
  "type": "module",
6
6
  "bin": { "sign-off-axi": "dist/cli.js" },
package/web/index.html CHANGED
@@ -1292,12 +1292,13 @@
1292
1292
  .split-bar .later { display: inline-flex; align-items: center; gap: 0.5rem; margin-left: auto; min-height: var(--ctl); padding: 0 0.75rem; border-radius: var(--r-ctl); background: none; color: var(--text); font-weight: 400; }
1293
1293
  .split-bar .later:hover, .dl-hover button:hover { background: var(--sunk); }
1294
1294
  .split-bar .later .glyph { width: 1.25rem; height: 1.25rem; stroke-width: 1.75; color: var(--muted); }
1295
- /* The ask fills the pane, so its answer bar stays at the pane's foot even under a short ask. */
1295
+ /* The ask fills the pane, so its answer bar stays at the pane's foot even under a short ask; under a tall one it comes after the last option, so it never covers one. */
1296
1296
  .split-detail > .decide { display: flex; flex: 1; flex-direction: column; width: 100%; max-width: 46rem; margin: 0 auto; padding: 0.5rem 2.5rem 0; }
1297
1297
  .split-detail > .decide .dhero, .split-detail > .decide .decide-preview { margin-top: 1.5rem; }
1298
1298
  .split-detail .decide > * { flex: none; } .split-detail .decide > :not(.decide-bar):nth-last-child(2) { margin-bottom: 1.5rem; }
1299
- .split-detail .decide-bar { position: sticky; bottom: 0; margin: auto -2.5rem 0; padding: 1rem 2.5rem 1.25rem; border-top: 1px solid var(--line); background: var(--bg); }
1299
+ .split-detail .decide-bar { margin: auto -2.5rem 0; padding: 1rem 2.5rem 1.25rem; border-top: 1px solid var(--line); background: var(--bg); }
1300
1300
  .split-detail .decide-bar .answer { min-width: 13rem; padding: 0 1.5rem; }
1301
+ .split-detail .decide-bar .open-link { display: inline-flex; align-items: center; justify-content: center; min-width: 13rem; min-height: 3.25rem; padding: 0 1.5rem; border-radius: 1rem; font-size: var(--t-m); }
1301
1302
  .split-detail [data-kbd]::after { content: attr(data-kbd); display: inline-grid; place-items: center; min-width: 1.375rem; height: 1.375rem; margin-left: 0.625rem; padding: 0 0.3125rem; border-radius: 0.375rem;
1302
1303
  background: rgb(255 255 255 / 0.22); color: inherit; font-size: 0.75rem; font-weight: 600; line-height: 1; vertical-align: 0.0625rem; }
1303
1304
  .split-bar [data-kbd]::after { background: var(--sunk); color: var(--muted); }
@@ -1651,20 +1652,25 @@
1651
1652
  };
1652
1653
  /** A sign-in: the service, what it unlocks, who waits, the time left and one Open for its link. Its poster closes it with signin done. */
1653
1654
  function signin(d, lead, saved, err) {
1654
- if (d.start) return started(d, lead, saved, err);
1655
+ const { open, line } = signinOpen(d, saved, err);
1656
+ return el('article', { class: 'row signin', id: `item-${d.key}` }, el('div', {}, lead,
1657
+ el('p', { class: 'line', dir: 'auto' }, `Unlocks: ${d.unlocks}`), el('p', { class: 'line', dir: 'auto' }, `Waits: ${d.waits}`), line), open, err);
1658
+ }
1659
+ /** A sign-in's one Open and the line under it, for its card and its desk page alike. */
1660
+ function signinOpen(d, saved, err) {
1661
+ if (d.start) return started(d, saved, err);
1655
1662
  const gone = !!d.expires && Date.parse(d.expires) <= Date.now();
1656
1663
  const open = saved ? '' : gone ? el('button', { class: 'open-link', disabled: '' }, 'Open')
1657
1664
  : el('a', { class: 'open-link', href: d.link, target: '_blank', rel: 'noopener noreferrer', 'aria-label': `Open the ${d.title} sign-in` }, 'Open');
1658
1665
  // The time left counts down on the page (data-expires); an expired link is drawn once more, with Open off.
1659
- const left = saved ? el('p', { class: 'meta' }, `Signed in at ${when(saved.at)}.`) : d.expires
1666
+ const line = saved ? el('p', { class: 'meta' }, `Signed in at ${when(saved.at)}.`) : d.expires
1660
1667
  ? el('p', { class: gone ? 'expiry gone' : 'expiry', ...(!gone && { 'data-expires': d.expires }) }, expiry(d.expires)) : '';
1661
- return el('article', { class: 'row signin', id: `item-${d.key}` }, el('div', {}, lead,
1662
- el('p', { class: 'line', dir: 'auto' }, `Unlocks: ${d.unlocks}`), el('p', { class: 'line', dir: 'auto' }, `Waits: ${d.waits}`), left), open, err);
1668
+ return { open, line };
1663
1669
  }
1664
1670
  /** What a tap-time sign-in did last, in plain words. */
1665
1671
  const RUNS = { none: 'Each tap gets a new link.', starting: 'Getting a new link…', live: 'The sign-in page is open. Finish it there.' };
1666
1672
  /** A sign-in whose link this host makes at each tap: Open starts it and sends a new tab to the new link, so its link never expires on the card. */
1667
- function started(d, lead, saved, err) {
1673
+ function started(d, saved, err) {
1668
1674
  const failed = d.run?.startsWith('failed: ') && d.run.slice(8);
1669
1675
  const open = saved ? '' : FROZEN || state.readonly ? el('button', { class: 'open-link', disabled: '' }, 'Open')
1670
1676
  : el('button', { class: 'open-link', 'aria-label': `Open the ${d.title} sign-in` }, 'Open');
@@ -1690,9 +1696,7 @@
1690
1696
  }
1691
1697
  };
1692
1698
  const line = saved ? `Signed in at ${when(saved.at)}.` : failed ? `The last try failed: ${failed} Tap Open to try again.` : RUNS[d.run] ?? RUNS.none;
1693
- return el('article', { class: 'row signin', id: `item-${d.key}` }, el('div', {}, lead,
1694
- el('p', { class: 'line', dir: 'auto' }, `Unlocks: ${d.unlocks}`), el('p', { class: 'line', dir: 'auto' }, `Waits: ${d.waits}`),
1695
- el('p', { class: failed ? 'expiry gone' : saved ? 'meta' : 'expiry' }, line)), open, err);
1699
+ return { open, line: el('p', { class: failed ? 'expiry gone' : saved ? 'meta' : 'expiry' }, line) };
1696
1700
  }
1697
1701
  setInterval(() => {
1698
1702
  // While a sign-in runs on this host, the page checks often, so the card closes soon after the sign-in is done.
@@ -3443,7 +3447,7 @@
3443
3447
  let skipTo = '';
3444
3448
  /** Asks the person chose to answer in their own words, kept across redraws. */
3445
3449
  const own = new Set();
3446
- function decidePage(d, next) {
3450
+ function decidePage(d, next, desk = false) {
3447
3451
  if (!d) return el('p', { class: 'empty' }, EMPTY.waiting);
3448
3452
  const to = next ? `#decide/${next.key}` : '#waiting';
3449
3453
  skipTo = next && !state.readonly ? to : '';
@@ -3462,6 +3466,14 @@
3462
3466
  const gone = xBtn(d, 'Dismiss');
3463
3467
  if (gone) { gone.setAttribute('role', 'menuitem'); gone.addEventListener('click', () => (location.hash = to)); }
3464
3468
  const more = (...items) => (state.readonly ? '' : moreSheet('More', [['Ask me later', () => { location.hash = '#waiting'; toLater([d.key], true); }, 'later', 'clock'], ...items, gone]));
3469
+ if (d.type === 'signin' && desk) {
3470
+ // On a desk a sign-in reads like any ask: who waits, the headline, what it unlocks, and Open as the bar's one button. Its row beside it counts the link's time down, so only a line that is not that countdown shows here.
3471
+ const err = el('div', { class: 'err', role: 'alert', id: `err-${d.key}` }), { open, line } = signinOpen(d, undefined, err);
3472
+ if (open) open.textContent = 'Open sign-in', open.dataset.kbd = '↵';
3473
+ const kicker = el('p', { class: 'kicker' }, el('span', { class: 'k' }, TYPE_NAMES.signin), ` · ${d.waits}${d.filed ? ` · ${when(d.filed)}` : ''}`);
3474
+ return el('section', { class: 'decide signin-ask' }, el('div', { class: 'lead' }, kicker, el('h2', { dir: 'auto' }, `Sign in to ${d.title}`)),
3475
+ el('p', { class: 'why', dir: 'auto' }, `Unlocks: ${d.unlocks}`), line?.dataset?.expires ? '' : line, err, el('div', { class: 'decide-bar' }, open, more()));
3476
+ }
3465
3477
  if (d.type === 'signin') {
3466
3478
  const signinPage = el('div', { class: 'decide' }, row(d, { x: false }), el('div', { class: 'decide-bar' }, more()));
3467
3479
  if (!state.readonly) swipe(signinPage);
@@ -3543,7 +3555,7 @@
3543
3555
  if (!d) return split([top, list], [items.length ? el('p', { class: 'empty' }, 'Open a review from the list.') : allClear()], [[['?'], 'All keys']]);
3544
3556
  const g = groupOf(d), later = el('button', { class: 'later', 'data-kbd': 'L', 'aria-keyshortcuts': 'L' }, glyph('clock'), g === 'later' ? 'Back from Later' : 'Later');
3545
3557
  later.onclick = () => { toLater([d.key], g !== 'later'); if (g !== 'later' && next) location.hash = `#decide/${next.key}`; };
3546
- return split([top, list], [el('div', { class: 'split-bar' }, queue.length > 1 ? pager(queue, i) : '', state.readonly ? '' : later), decidePage(d, next)],
3558
+ return split([top, list], [el('div', { class: 'split-bar' }, queue.length > 1 ? pager(queue, i) : '', state.readonly ? '' : later), decidePage(d, next, true)],
3547
3559
  [[['J', 'K'], 'Move'], [['↵'], 'Answer'], [['?'], 'All keys']]);
3548
3560
  }
3549
3561
  /** Evidence on a desk: the list of updates beside the one selected. */