@leftoff/cli 0.9.8-beta.14 → 0.9.8-beta.15

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,8 @@ What changed in Leftoff (called Checkpoints until 0.4.5), newest first. The boar
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ - Session page: a non-Live session's Live box shows the Live resume command with Copy; the Lists ? opens “How lists work” right there.
8
+
7
9
  - Session page: Copy Live resume, for a session that wasn't Live (exit it, run the command, it comes back Live).
8
10
 
9
11
  - Live: your messages render markdown (lists, code, bold), same as Claude's.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leftoff",
3
- "version": "0.9.8-beta.14",
3
+ "version": "0.9.8-beta.15",
4
4
  "description": "Leftoff (formerly Checkpoints): WTF was I working on? Tracks every Claude Code session on this machine, writes a 3-line checkpoint when one goes quiet, and adds /cp (checkpoint now), /cp-name and a /leftoff pane. Talks to the local Leftoff daemon on 127.0.0.1:7429 and falls back to writing markdown in ~/checkpoints.",
5
5
  "author": {
6
6
  "name": "Joel"
@@ -28,7 +28,7 @@ const END_TIMEOUT_MS = 1000
28
28
  const MODEL_TIMEOUT_MS = 20000
29
29
 
30
30
  // Module state. $.state is not used: the pane redraws with $.ui.invalidate.
31
- const MOD_VERSION = '0.9.8-beta.14' // keep in step with .claude-plugin/plugin.json
31
+ const MOD_VERSION = '0.9.8-beta.15' // keep in step with .claude-plugin/plugin.json
32
32
 
33
33
  const st = {
34
34
  internal: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leftoff/cli",
3
- "version": "0.9.8-beta.14",
3
+ "version": "0.9.8-beta.15",
4
4
  "description": "Leftoff: WTF was I working on? Tracks every Claude Code session, writes a 3-line checkpoint when one goes quiet, and gives you a daily digest and a board, on your Mac and (optionally) your phone.",
5
5
  "keywords": [
6
6
  "claude",
package/ui/app.css CHANGED
@@ -2610,3 +2610,8 @@ html.pv-open, html.pv-open body { overflow: hidden; }
2610
2610
  .updatebar .ub-go:focus-visible, .updatebar .ub-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2611
2611
  @media (min-width: 700px) { .updatebar { left: calc(50% + var(--rail-w, 0px) / 2); } }
2612
2612
  @media (prefers-reduced-motion: reduce) { .updatebar { transition: none; } }
2613
+
2614
+ /* 0.9.8: a non-Live session's Live box offers the Live resume command */
2615
+ .live-box.off .live-resume-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
2616
+ .live-box.off .live-resume-row code { flex: 1 1 220px; min-width: 0; overflow-wrap: anywhere; font-size: 12.5px; padding: 8px 10px; border-radius: 8px; background: var(--card2); border: 1px solid var(--line); }
2617
+ .live-box.off .live-resume-row .btn { min-height: 44px; gap: 7px; }
package/ui/app.js CHANGED
@@ -1483,7 +1483,8 @@ function liveBoxHtml(s, v) {
1483
1483
  const how = state.liveHow
1484
1484
  return '<section class="live-box off" aria-label="Live link"><div class="live-head"><h2>Live <span class="pill soon">Beta</span></h2></div>' +
1485
1485
  '<p class="faint">To send this session messages from here or your phone, and answer its permission prompts, start it with <code>leftoff live</code>' +
1486
- (how && how.line ? ' (or <code>' + esc(how.line) + '</code>)' : '') + '. Claude Code asks once to load the channel; channels are a research preview.</p></section>'
1486
+ (how && how.line ? ' (or <code>' + esc(how.line) + '</code>)' : '') + '. Claude Code asks once to load the channel; channels are a research preview.</p>' +
1487
+ (s.liveResumeCommand ? '<p class="faint">Already running? Exit it in its terminal, then paste this to bring it back Live:</p><div class="live-resume-row"><code class="mono">' + esc(s.liveResumeCommand) + '</code><button type="button" class="btn" data-action="copy-cmd" data-cmd="' + esc(s.liveResumeCommand) + '">' + icon('copy') + 'Copy Live resume</button></div>' : '') + '</section>'
1487
1488
  }
1488
1489
  const msgs = (v && v.messages) || []
1489
1490
  const may = state.mayAnswer !== false
@@ -3875,7 +3876,7 @@ function paintListsCount(lists) {
3875
3876
  // The session page's Lists section: saved lists, the hint, and "Find lists in this session".
3876
3877
  function sessionListsSection(s) {
3877
3878
  const f = listUi.found[s.id]
3878
- let h = '<h2>Lists <span class="count">' + ((s.lists || []).length) + '</span><button type="button" class="helpq" data-action="list-help-go" title="How lists work">?</button></h2>'
3879
+ let h = '<h2>Lists <span class="count">' + ((s.lists || []).length) + '</span><button type="button" class="helpq" data-action="list-help-inline" aria-expanded="' + (listUi.sHelp ? 'true' : 'false') + '" aria-controls="sListsHelp" title="How lists work">?</button></h2>' + (listUi.sHelp ? listsHelp().replace('id="listsHelp"', 'id="sListsHelp"') : '')
3879
3880
  if (s.lists && s.lists.length) h += '<div class="lists">' + s.lists.map((x) => (state.listFull && state.listFull[x.ref] ? listCard(state.listFull[x.ref], { session: false }) : '')).join('') + '</div>'
3880
3881
  if (s.listHint) h += '<div class="lfound hint"><div><strong>Claude wrote a ' + esc(s.listHint.count) + '-step list</strong>: ' + esc(s.listHint.name) + '</div><div class="row"><button type="button" class="btn primary" data-action="list-save-hint" data-id="' + esc(s.id) + '">Save</button><button type="button" class="btn" data-action="list-dismiss" data-id="' + esc(s.id) + '">Dismiss</button></div></div>'
3881
3882
  h += '<div class="lfind"><button type="button" class="btn" data-action="list-find" data-id="' + esc(s.id) + '"' + (listUi.finding[s.id] ? ' disabled' : '') + '>' + icon('list-checks') + (listUi.finding[s.id] ? 'Reading the transcript…' : 'Find lists in this session') + '</button>' +
@@ -4950,6 +4951,7 @@ document.addEventListener('click', async (ev) => {
4950
4951
  if (action === 'list-showall') { listUi.showAll = !listUi.showAll; return render() }
4951
4952
  if (action === 'list-help') { listUi.help = !listUi.help; return render() }
4952
4953
  if (action === 'list-help-go') { listUi.help = true; location.hash = '#/lists'; return }
4954
+ if (action === 'list-help-inline') { listUi.sHelp = !listUi.sHelp; render(); return }
4953
4955
  if (action === 'remote-link' || action === 'remote-code') {
4954
4956
  const l = await api('POST', '/v1/remote/link', action === 'remote-link' ? { full: true } : undefined)
4955
4957
  if (l.needed === false) { toast('No sign-in needed (remoteRequireToken is off)'); return }