sideby 0.12.0 → 0.12.1

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/dist/build.json CHANGED
@@ -1 +1 @@
1
- {"id":"20261016T071613-e5f0f1"}
1
+ {"id":"20261016T081651-7c664d"}
@@ -22,6 +22,10 @@ declare const en: {
22
22
  'upd.skill': string;
23
23
  'upd.checksOff': string;
24
24
  'upd.latest': string;
25
+ 'upd.latestNow': string;
26
+ 'upd.check': string;
27
+ 'upd.checking': string;
28
+ 'upd.verTitle': string;
25
29
  'upd.checked': string;
26
30
  'upd.notYet': string;
27
31
  'upd.auto': string;
@@ -23,7 +23,11 @@ const en = {
23
23
  'upd.cannot': 'sideby cannot update itself here: {why}. Run this yourself:',
24
24
  'upd.skill': 'If you use the sideby skill, update it too:',
25
25
  'upd.checksOff': 'Update checks are off ({why}).',
26
- 'upd.latest': 'This is the newest version.',
26
+ 'upd.latest': 'This is the newest version',
27
+ 'upd.latestNow': 'You have the newest version (checked just now).',
28
+ 'upd.check': 'Check for updates',
29
+ 'upd.checking': 'Checking…',
30
+ 'upd.verTitle': 'sideby {v} · check for updates',
27
31
  'upd.checked': 'checked {ago}',
28
32
  'upd.notYet': 'sideby has not checked for a new version yet. It checks once a day.',
29
33
  'upd.auto': 'Update by itself',
@@ -561,7 +565,11 @@ const zh = {
561
565
  'upd.cannot': '这种安装方式下 sideby 不能自己更新:{why}。请自己运行:',
562
566
  'upd.skill': '如果你装了 sideby skill,也更新一下:',
563
567
  'upd.checksOff': '检查更新已关闭({why})。',
564
- 'upd.latest': '已是最新版本。',
568
+ 'upd.latest': '已是最新版本',
569
+ 'upd.latestNow': '已是最新版本(刚刚检查)。',
570
+ 'upd.check': '检查更新',
571
+ 'upd.checking': '正在检查…',
572
+ 'upd.verTitle': '当前 sideby {v} · 检查更新',
565
573
  'upd.checked': '{ago}检查',
566
574
  'upd.notYet': 'sideby 还没检查过新版本,每天检查一次。',
567
575
  'upd.auto': '自动更新',
@@ -2687,6 +2687,20 @@ const MAIN = String.raw `
2687
2687
  function renderUpdateButton() {
2688
2688
  const b = $('update')
2689
2689
  const u = updState()
2690
+ // The version the page runs, a pill after the name; it opens the same dialog (with "Check for updates"), and
2691
+ // takes a dot while a newer version waits.
2692
+ const v = $('ver-top')
2693
+ v.hidden = !BOOT.version
2694
+ if (!v.hidden) {
2695
+ const newer = !!(u && u.available)
2696
+ v.textContent = 'v' + BOOT.version
2697
+ v.classList.toggle('has-update', newer)
2698
+ v.disabled = !BOOT.updates
2699
+ const tip = !BOOT.updates ? '' : newer ? t('upd.labelLong', { v: u.latest, cur: u.current }) : t('upd.verTitle', { v: BOOT.version })
2700
+ v.title = tip
2701
+ if (tip) v.setAttribute('aria-label', tip)
2702
+ else v.removeAttribute('aria-label')
2703
+ }
2690
2704
  b.hidden = !(u && u.available)
2691
2705
  if (!b.hidden) {
2692
2706
  setKids(b, [icon('arrowUp'), h('span', null, t('upd.label', { v: u.latest }))])
@@ -2697,7 +2711,7 @@ const MAIN = String.raw `
2697
2711
  }
2698
2712
  function openUpdate() {
2699
2713
  if (!BOOT.updates) return
2700
- up = { busy: false, error: null, done: null, changed: false, autoPlan: null, autoBusy: false, waitUntil: 0 }
2714
+ up = { busy: false, error: null, done: null, changed: false, autoPlan: null, autoBusy: false, waitUntil: 0, checking: false, checked: false }
2701
2715
  showDialog('update')
2702
2716
  renderUpdate()
2703
2717
  }
@@ -2724,6 +2738,19 @@ const MAIN = String.raw `
2724
2738
  dlg.busy = false
2725
2739
  renderUpdate()
2726
2740
  }
2741
+ // "Check for updates": asks the registry now (the daily check only runs once a day), then shows the answer.
2742
+ async function updCheck() {
2743
+ up.checking = true
2744
+ up.error = null
2745
+ renderUpdate()
2746
+ try {
2747
+ await api('/api/update', {})
2748
+ await load({ silent: true })
2749
+ up.checked = true
2750
+ } catch (e) { up.error = e }
2751
+ up.checking = false
2752
+ renderUpdate()
2753
+ }
2727
2754
  // After an Update the server (or the Portal's worker) comes back with a new build; load() then reloads the page.
2728
2755
  function updWait() {
2729
2756
  if (!up || !up.done || S.newBuild) { if (S.newBuild) location.reload(); return }
@@ -2780,11 +2807,17 @@ const MAIN = String.raw `
2780
2807
  body.push(codeCopy(u.command))
2781
2808
  }
2782
2809
  body.push(h('p', { class: 'small' }, t('upd.skill')), codeCopy(u.skill))
2810
+ } else if (up.checked && u.latest) {
2811
+ body.push(h('p', { class: 'note' }, t('upd.latestNow')))
2783
2812
  } else if (u.checksOff) {
2784
2813
  body.push(h('p', { class: 'muted small' }, t('upd.checksOff', { why: u.checksOff })))
2785
2814
  } else if (u.latest) {
2786
2815
  body.push(h('p', { class: 'muted small' }, t('upd.latest') + (u.checkedAt ? ' · ' + t('upd.checked', { ago: ago(u.checkedAt) }) : '')))
2787
2816
  } else body.push(h('p', { class: 'muted small' }, t('upd.notYet')))
2817
+ // A read-only Panel sends no request and writes nothing, so it cannot check (spec §3.23).
2818
+ if (!BOOT.readOnly && !up.done && !u.available)
2819
+ body.push(h('p', null, h('button', { class: 'btn btn-sm', type: 'button', disabled: up.checking || up.busy, 'aria-busy': up.checking ? 'true' : null, onclick: updCheck },
2820
+ up.checking ? spinner() : icon('refresh'), up.checking ? t('upd.checking') : t('upd.check'))))
2788
2821
  if (!BOOT.readOnly && !up.done) {
2789
2822
  const box = h('input', { type: 'checkbox', disabled: up.autoBusy || !u.method.canUpdate })
2790
2823
  box.checked = up.autoPlan ? up.autoPlan.auto : u.auto
@@ -2807,6 +2840,7 @@ const MAIN = String.raw `
2807
2840
  $('ver').disabled = !BOOT.updates
2808
2841
  $('ver').addEventListener('click', openUpdate)
2809
2842
  $('update').addEventListener('click', openUpdate)
2843
+ $('ver-top').addEventListener('click', openUpdate)
2810
2844
  $('refresh').addEventListener('click', () => load({ check: true, announce: true }))
2811
2845
  $('handoff').addEventListener('click', () => setPage('handoff'))
2812
2846
  $('lang').addEventListener('click', () => {
@@ -63,6 +63,9 @@ body { margin: 0; background: var(--bg); color: var(--text); font: var(--font-si
63
63
  [hidden] { display: none !important; }
64
64
  .top-in, .wrap { max-width: var(--content-width); margin: 0 auto; }
65
65
  .top-in { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 28px var(--gutter) 0; }
66
+ /* When the name, its version pill and the buttons do not fit one row, the buttons wrap below, on the right. */
67
+ .top-in { flex-wrap: wrap; row-gap: 10px; }
68
+ .top-in > .top-actions { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; }
66
69
  .wrap { padding: 20px var(--gutter) 40px; }
67
70
  .header-bar .top { background: var(--surface); border-bottom: 1px solid var(--border); }
68
71
  .header-bar .top-in { min-height: 84px; padding: 14px var(--gutter); }
@@ -82,6 +85,14 @@ code, .mono { font-family: var(--mono); font-size: 0.92em; }
82
85
  .brand { display: flex; align-items: center; gap: 12px; min-width: 0; }
83
86
  .brand h1 { font-size: var(--title-size); font-weight: var(--title-weight); line-height: 1.3; letter-spacing: -0.02em; }
84
87
  .tagline { color: var(--muted); font-size: 13px; }
88
+ /* The running version, a quiet pill after the name; with a newer version it takes the accent and a dot (spec §3.23). */
89
+ .title-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
90
+ .ver-pill { display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--muted); font: 500 11.5px/18px var(--mono); cursor: pointer; white-space: nowrap; }
91
+ .ver-pill:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
92
+ .ver-pill:disabled { cursor: default; }
93
+ .ver-pill:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }
94
+ .ver-pill.has-update { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
95
+ .ver-pill.has-update::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
85
96
  .logo { display: inline-flex; flex: none; width: 40px; height: 40px; border-radius: 10px; background: var(--logo-bg); box-shadow: inset 0 0 0 1px var(--logo-line); }
86
97
  /* desktop/icon.ts draws the same two half discs for the app icon. */
87
98
  .logo svg { display: block; width: 100%; height: 100%; fill: var(--logo-fg); }
@@ -559,6 +570,7 @@ select:is(:hover, :focus) { --chevron: var(--text); }
559
570
  :root { --gutter: 14px; }
560
571
  .top-in { padding: 16px var(--gutter) 0; }
561
572
  .header-bar .top-in { min-height: 0; padding: 12px var(--gutter); }
573
+ .update-btn span { display: none; }
562
574
  .wrap { padding: 14px var(--gutter) 32px; }
563
575
  .hide-sm { display: none; }
564
576
  .tagline { display: none; }
@@ -61,7 +61,7 @@ export function renderPage(opts) {
61
61
  <div class="brand">
62
62
  <span class="logo" aria-hidden="true">${MARK_SVG}</span>
63
63
  <div>
64
- <h1>sideby</h1>
64
+ <div class="title-row"><h1>sideby</h1><button id="ver-top" class="ver-pill" type="button" hidden></button></div>
65
65
  <p class="tagline" data-i18n="top.tagline">Every AI coding account, side by side.</p>
66
66
  </div>
67
67
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sideby",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Run work, personal and API-key accounts at the same time, without their configs drifting apart.",
5
5
  "license": "MIT",
6
6
  "author": "Adonis0123 (https://github.com/Adonis0123)",
@@ -2,7 +2,7 @@
2
2
  name: sideby
3
3
  description: Use when the user runs, or wants to run, several AI coding accounts (Claude Code, Codex, Grok Build, pi) side by side. Covers installing sideby and setting up a new account ("install sideby", "set up another Claude account", "add cc008", "配置一个 cc00x", "再加一个 Codex 账号"), explaining what accounts share and what stays separate ("what do my accounts share?", "这些账号共享了什么"), showing which account a session is ("show [cc001] in the status line", "加上账号标识"), seeing accounts, quota and usage, moving on when one hits its 5-hour or weekly limit, moving the current task to another account ("用 cc008 继续", "交回 cc008"), adding a short command (alias), and repairing drifted account directories (skills, hooks, rules or settings not shared, wrong symlinks, credential file permissions). Drives the `sideby` CLI with `--json`; the agent does every step except browser sign-in, filling in secrets and starting interactive sessions, which it hands to the user as one command (inside an Auto Handoff session, `sideby handoff ready --to` moves the task itself).
4
4
  metadata:
5
- version: "0.12.0"
5
+ version: "0.12.1"
6
6
  requires:
7
7
  bins: [sideby, node]
8
8
  cliHelp: sideby --help