@leftoff/cli 0.9.9-beta.2 → 0.9.9-beta.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leftoff",
3
- "version": "0.9.9-beta.2",
3
+ "version": "0.9.9-beta.3",
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.9-beta.2' // keep in step with .claude-plugin/plugin.json
31
+ const MOD_VERSION = '0.9.9-beta.3' // 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.9-beta.2",
3
+ "version": "0.9.9-beta.3",
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.js CHANGED
@@ -16,7 +16,7 @@ import { navHideStep, NAVHIDE_TYPING } from './navhide.js'
16
16
  import { openLightbox } from './lightbox.js' // 0.9.8: the image preview
17
17
  import { openPdfView, pdfThumb } from './pdfview.js' // 0.9.8: the PDF preview (pdf.js loads on the first PDF)
18
18
  import { isBlank, composerKey, isTouchUi, composerHeight, composerBoxHtml } from './composer.js' // 0.9.8 the Live composer
19
- import { startPageHtml, startPick, startAsksBanner, headlessBarHtml, startsSettingHtml } from './start.js' // 0.9.9 New session
19
+ import { startPageHtml, startPick, startAsksBanner, headlessBarHtml, startsSettingHtml, startViewerLocal as startViewerAt } from './start.js' // 0.9.9 New session
20
20
  import { attachPlan, skippedText, shortName, prepareFile, uploadFile, fetchAttachment, fmtSize, attachKind, pastedName, ATTACH_MAX_FILES } from './attach.js' // 0.9.8 attachments
21
21
  import { wireTips } from './tips.js'
22
22
  import { mountUpdateBar, takeCarry } from './update-bar.js' // 0.9.8 "Leftoff updated · Refresh"
@@ -841,7 +841,7 @@ async function viewBoard() {
841
841
  '<a class="btn primary" href="#/catchup/new">' + icon('coffee') + 'Catch me up</a>' +
842
842
  '<a class="btn" href="#/digest">' + icon('newspaper') + 'Today’s digest →</a></div></div>'
843
843
  html += syncDetails() + staleLine(sessions)
844
- html += restoreAsksBanner() + startAsksBanner(state.starts, { esc, icon, local: !!(state.starts && state.starts.viewer && state.starts.viewer.local), clock, busy: startUi.busy }) + restoreBanner()
844
+ html += restoreAsksBanner() + startAsksBanner(state.starts, { esc, icon, local: startViewerLocal(state.starts && state.starts.viewer), clock, busy: startUi.busy }) + restoreBanner()
845
845
  // Needs you (v0.4.4): every session waiting on you, whatever the filters, longest wait first.
846
846
  const needs = needsItems(sessions)
847
847
  html += needsStrip(needs, needOpts())
@@ -3397,7 +3397,7 @@ async function viewSettings() {
3397
3397
  phone: phonePane(relay, remote, unavailable),
3398
3398
  security: '<div class="section" id="answering">Other devices</div>' + (answeringSection(remote, relay) ||
3399
3399
  '<div class="full hint">Nothing reaches this board from elsewhere until <a href="#/settings/phone">Phone &amp; remote</a> is on.</div>') +
3400
- startsSettingHtml(starts, { esc, local: !!(starts && starts.viewer && starts.viewer.local) }) +
3400
+ startsSettingHtml(starts, { esc, local: startViewerLocal(starts && starts.viewer) }) +
3401
3401
  // a changed machine key needs a click here, so Other machines stays up top then
3402
3402
  (peers && (peers.peers || []).some((x) => x.trust === 'key_changed') ? machinesSection(peers) : '') +
3403
3403
  advanced('security', (peers && (peers.peers || []).some((x) => x.trust === 'key_changed') ? '' : machinesSection(peers)) +
@@ -4165,12 +4165,14 @@ async function render() {
4165
4165
 
4166
4166
  // ------------------------------------------------------------ New session (0.9.9, ui/start.js)
4167
4167
 
4168
+ // this Mac's own browser, or the hosted board open on it (the direct door)
4169
+ function startViewerLocal(v) { return startViewerAt(v, state.viewerLocal !== false) }
4168
4170
  const startUi = { data: null, machine: null, repo: null, where: 'repo', allRepos: false, ask: null, busy: new Set(), sending: false, poll: null }
4169
4171
  async function viewNewSession() {
4170
4172
  try { startUi.data = await api('GET', '/v1/start') } catch (e) { if (!startUi.data) throw e }
4171
4173
  const fa = document.activeElement
4172
4174
  const kf = fa && fa.classList && fa.classList.contains('start-text') ? { a: fa.selectionStart, b: fa.selectionEnd } : null
4173
- $app.innerHTML = startPageHtml(startUi.data, startUi, { esc, icon, emoji: mEmoji, local: state.viewerLocal !== false && !!(startUi.data.viewer && startUi.data.viewer.local), clock, draft: (state.drafts && state.drafts.start) || '', busy: startUi.sending })
4175
+ $app.innerHTML = startPageHtml(startUi.data, startUi, { esc, icon, emoji: mEmoji, local: startViewerLocal(startUi.data.viewer), clock, draft: (state.drafts && state.drafts.start) || '', busy: startUi.sending })
4174
4176
  const ta = $app.querySelector('.start-text')
4175
4177
  if (ta) { fitComposer(ta); if (kf && !ta.disabled) { ta.focus({ preventScroll: true }); try { ta.setSelectionRange(kf.a, kf.b) } catch {} } }
4176
4178
  }
package/ui/start.js CHANGED
@@ -8,6 +8,13 @@ import { composerBoxHtml } from './composer.js'
8
8
 
9
9
  const REPOS_SHOWN = 6
10
10
 
11
+ // At this Mac: its own browser (the daemon says local), or your hosted board
12
+ // open on it through the direct door (local and direct). boardLocal is the
13
+ // board's own read of where it runs; the direct door overrides it.
14
+ export function startViewerLocal(viewer, boardLocal = true) {
15
+ return !!(viewer && viewer.local) && (boardLocal || viewer.direct === true)
16
+ }
17
+
11
18
  // The choice the page shows: what you picked, else this Mac (or the first
12
19
  // online one), its newest repo, and "In the repo".
13
20
  export function startPick(data, ui = {}) {