@bakapiano/ccsm 0.10.1 → 0.10.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakapiano/ccsm",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Claude Code Session Manager — Windows web UI to manage many concurrent claude sessions: live list, snapshot/restore, focus existing window, new session in an isolated workspace with repo clones",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
@@ -28,9 +28,9 @@
28
28
  --ink-muted: #8a8475;
29
29
  --ink-faint: #b5af9d;
30
30
 
31
- /* Accent — Claude warm copper. Slightly desaturated from raw #c45f3f so
32
- solid-fill buttons feel calm against the cream surfaces, while still
33
- reading as the same brand hue in star icons, focus rings, etc. */
31
+ /* Accent — Ocean blue. Calm and content-first. Users can override via
32
+ the Theme accent picker in Settings; that flow rewrites these vars
33
+ at runtime (state.js · applyAccentCssVars). */
34
34
  --accent: #2f6fa3;
35
35
  --accent-deep: #25577f;
36
36
  --accent-soft: rgba(47, 111, 163, 0.10);
@@ -13,13 +13,48 @@
13
13
  import { html } from '../html.js';
14
14
  import { useEffect, useState } from 'preact/hooks';
15
15
  import { serverHealth } from '../state.js';
16
- import { refreshAll } from '../api.js';
16
+ import { refreshAll, pollHealth } from '../api.js';
17
17
  import { BrandMark } from '../icons.js';
18
18
 
19
+ // Silent ccsm:// launch via hidden iframe. Same trick as the router.
20
+ // If the protocol is registered AND the user has already OK'd the
21
+ // Windows confirmation prompt, ccsm wakes up within ~2s and the
22
+ // banner auto-dismisses on the next health poll. On a cold first
23
+ // visit (protocol not registered, or "Always allow" not yet ticked),
24
+ // the iframe noops silently and the manual "Start ccsm" button is
25
+ // still there as fallback.
26
+ function silentProtocolLaunch() {
27
+ try {
28
+ const f = document.createElement('iframe');
29
+ f.style.display = 'none';
30
+ f.src = 'ccsm://start';
31
+ document.body.appendChild(f);
32
+ setTimeout(() => { try { f.remove(); } catch {} }, 1500);
33
+ } catch {}
34
+ }
35
+
19
36
  export function OfflineBanner() {
20
37
  const h = serverHealth.value;
21
38
  const offline = h.state === 'offline';
22
39
  const [clicked, setClicked] = useState(false);
40
+ const [autoTried, setAutoTried] = useState(false);
41
+
42
+ // First time we see offline state, try a silent ccsm:// launch and
43
+ // tighten the health-poll cadence for a few seconds so the redirect
44
+ // happens within ~2-3s without any visible UI flash.
45
+ useEffect(() => {
46
+ if (!offline || autoTried) return;
47
+ setAutoTried(true);
48
+ silentProtocolLaunch();
49
+ let n = 0;
50
+ const tick = async () => {
51
+ if (n++ > 12) return; // ~6s of tight polling
52
+ await pollHealth();
53
+ if (serverHealth.value.state === 'online') return;
54
+ setTimeout(tick, 500);
55
+ };
56
+ setTimeout(tick, 500);
57
+ }, [offline]);
23
58
 
24
59
  useEffect(() => {
25
60
  if (h.state === 'online' && clicked) {
package/public/js/main.js CHANGED
@@ -3,24 +3,32 @@
3
3
  // the mount root.
4
4
 
5
5
  import { render } from 'preact';
6
+ import { effect } from '@preact/signals';
6
7
  import { html } from './html.js';
7
- import { loadPersisted, clockTick, lastRefreshAt, installPrompt, isInstalledPwa, sidebarForcedCollapsed } from './state.js';
8
+ import { loadPersisted, clockTick, lastRefreshAt, installPrompt, isInstalledPwa, sidebarForcedCollapsed, serverHealth } from './state.js';
8
9
  import { httpBase } from './backend.js';
9
10
  import { loadConfig, refreshAll, loadSessions, loadFolders, loadWorkspaces, pollHealth } from './api.js';
10
11
  import { setToast } from './toast.js';
11
12
  import { App } from './components/App.js';
12
13
 
13
14
  loadPersisted();
14
- // Pin the document title to "CCSM" some Chromium builds will inject the
15
- // current URL or path into the standalone window title bar if the page
16
- // title is empty / changes; locking it here keeps the OS title bar text
17
- // stable across navigation, tab switches, and PWA-install refresh.
18
- const lockTitle = () => { if (document.title !== 'CCSM') document.title = 'CCSM'; };
15
+ // Window/tab title tracks the live backend version "CCSM v0.10.1" once
16
+ // /api/health responds, "CCSM" before then. A MutationObserver guards
17
+ // against Chromium standalone builds that occasionally try to inject the
18
+ // URL into the title bar; it accepts any "CCSM..." string we set and
19
+ // resets anything else.
20
+ function desiredTitle() {
21
+ const v = serverHealth.value.version;
22
+ return v ? `CCSM v${v}` : 'CCSM';
23
+ }
24
+ let expected = desiredTitle();
25
+ function lockTitle() { if (document.title !== expected) document.title = expected; }
19
26
  lockTitle();
20
27
  new MutationObserver(lockTitle).observe(
21
28
  document.querySelector('title') || document.head,
22
29
  { childList: true, subtree: true, characterData: true }
23
30
  );
31
+ effect(() => { expected = desiredTitle(); lockTitle(); });
24
32
  render(html`<${App} />`, document.getElementById('app'));
25
33
 
26
34
  // PWA install affordance — Chromium fires `beforeinstallprompt` when the
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "CCSM",
3
3
  "short_name": "CCSM",
4
+ "version": "0.0.0-dev",
4
5
  "description": "Single pane over every live claude session on this machine.",
5
6
  "start_url": "./",
6
7
  "scope": "./",