@volter-ai-dev/supercode-ui 0.1.45 → 0.1.47

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/README.md CHANGED
@@ -205,7 +205,10 @@ and can overlay a machine-wide session inventory, pagination state, attention, d
205
205
  errors, and owned/attached identities without reimplementing transcript or capability semantics.
206
206
  `projectSessionInventory` performs the corresponding title, latest-preview, activity, path, age,
207
207
  sorting, and row projection for raw machine-wide descriptors; the trusted host supplies only its
208
- opaque-key callback and retains the reversible locator map.
208
+ opaque-key callback and retains the reversible locator map. Its `isWritable` capability callback
209
+ is fail-closed: unread badges remain neutral until the host proves a real send or terminal-control
210
+ path. Runtime activity remains independent, so a read-only channel can still truthfully show that
211
+ its external agent is working or needs input.
209
212
 
210
213
  ## Modularity contract
211
214
 
package/activity.mjs CHANGED
@@ -554,6 +554,7 @@ function readSessions(value) {
554
554
  updatedAt: nullableNumber(row.updatedAt),
555
555
  messages: nullableNumber(row.messages),
556
556
  active: row.active === true,
557
+ writable: row.writable === true,
557
558
  live: row.live === true,
558
559
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
559
560
  }];
@@ -815,7 +816,9 @@ function activityDetail(activity, preview, pill) {
815
816
  function projectAgentActivity(value) {
816
817
  const state = normalizeUiState(value);
817
818
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
819
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
818
820
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
821
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
819
822
  const ranked = state.sessions.map((row, index) => {
820
823
  const activity2 = sessionActivity(state, row);
821
824
  return {
@@ -839,7 +842,8 @@ function projectAgentActivity(value) {
839
842
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
840
843
  detail: activityDetail(activity, preview, state.pill.label),
841
844
  activity,
842
- unreadCount
845
+ unreadCount,
846
+ unreadTone
843
847
  };
844
848
  }
845
849
  function toolTarget(argumentsText) {
@@ -926,7 +930,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
926
930
  const Logo = components.HarnessLogo ?? HarnessLogo;
927
931
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
928
932
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
929
- return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
933
+ return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
930
934
  /* @__PURE__ */ jsxs2("span", { className: "scui-launcher-logo", children: [
931
935
  /* @__PURE__ */ jsx2(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
932
936
  value.unreadCount ? /* @__PURE__ */ jsx2("b", { "aria-hidden": "true", children: badge }) : null
package/components.mjs CHANGED
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -827,7 +828,9 @@ function activityDetail(activity, preview, pill) {
827
828
  function projectAgentActivity(value) {
828
829
  const state = normalizeUiState(value);
829
830
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
831
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
830
832
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
833
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
831
834
  const ranked = state.sessions.map((row, index) => {
832
835
  const activity2 = sessionActivity(state, row);
833
836
  return {
@@ -851,7 +854,8 @@ function projectAgentActivity(value) {
851
854
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
852
855
  detail: activityDetail(activity, preview, state.pill.label),
853
856
  activity,
854
- unreadCount
857
+ unreadCount,
858
+ unreadTone
855
859
  };
856
860
  }
857
861
  function filterSessions(rows, query) {
@@ -1639,7 +1643,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
1639
1643
  const Logo = components.HarnessLogo ?? HarnessLogo;
1640
1644
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
1641
1645
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
1642
- return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1646
+ return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1643
1647
  /* @__PURE__ */ jsxs5("span", { className: "scui-launcher-logo", children: [
1644
1648
  /* @__PURE__ */ jsx5(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
1645
1649
  value.unreadCount ? /* @__PURE__ */ jsx5("b", { "aria-hidden": "true", children: badge }) : null
@@ -2299,7 +2303,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2299
2303
  const unreadCount = attention?.unreadCount ?? 0;
2300
2304
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2301
2305
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2302
- return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2306
+ return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2303
2307
  /* @__PURE__ */ jsx8(HarnessLogo, { id: row.harness, activity, size: 34 }),
2304
2308
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-copy", children: [
2305
2309
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-title", children: [
package/controller.d.ts CHANGED
@@ -65,6 +65,8 @@ export interface SessionInventoryProjectionOptions {
65
65
  maxSessions?: number;
66
66
  liveWindowMs?: number;
67
67
  preserveOrder?: boolean;
68
+ /** Capability check performed by the trusted host; omitted rows are read-only observations. */
69
+ isWritable?(descriptor: SessionDescriptor): boolean;
68
70
  }
69
71
 
70
72
  export function sessionConversationUpdatedAt(descriptor: SessionDescriptor): number | null;
package/controller.mjs CHANGED
@@ -72,7 +72,7 @@ function descriptorTitle(descriptor) {
72
72
  return compactTopic(nativeTitle);
73
73
  }
74
74
  if (descriptor?.locator?.harness !== 'claude-code' && descriptor?.locator?.harness !== 'codex') {
75
- return compactTopic(nativeTitle || workspace || descriptor?.locator?.session_id?.slice(0, 8)) || 'Untitled chat';
75
+ return compactTopic(nativeTitle || workspace) || 'Untitled chat';
76
76
  }
77
77
  const openingUserMessages = (descriptor?.preview_candidates ?? []).filter(
78
78
  (candidate) => candidate?.role === undefined || candidate.role === 'user',
@@ -125,6 +125,7 @@ export function projectSessionInventory(descriptors, options) {
125
125
  active: active?.sessionId != null
126
126
  && descriptor.locator.harness === active.harness
127
127
  && descriptor.locator.session_id === active.sessionId,
128
+ writable: options.isWritable?.(descriptor) === true,
128
129
  live: updatedAt !== null && now - updatedAt <= liveWindowMs,
129
130
  runtimeStatus: sessionDescriptorRuntimeStatus(descriptor),
130
131
  });
@@ -378,14 +379,14 @@ function attached(snapshot) {
378
379
  harness: snapshot.activeHarness,
379
380
  name: workspaceName(snapshot.workspace),
380
381
  cwd: snapshot.workspace,
381
- title: snapshot.activeSession?.model ?? snapshot.activeSessionId ?? 'Active session',
382
+ title: 'Untitled chat',
382
383
  } : null;
383
384
  return {
384
385
  key: session.key,
385
386
  harness: session.harness,
386
387
  name: workspaceName(session.cwd),
387
388
  cwd: session.cwd ?? '',
388
- title: session.title ?? session.model ?? session.sessionId,
389
+ title: session.title ?? 'Untitled chat',
389
390
  };
390
391
  }
391
392
 
@@ -442,11 +443,12 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
442
443
  harness: session.harness,
443
444
  name: workspaceName(session.cwd),
444
445
  cwd: session.cwd ?? '',
445
- title: session.title ?? session.model ?? session.sessionId,
446
+ title: session.title ?? 'Untitled chat',
446
447
  age: relativeAge(session.updatedAt, now),
447
448
  updatedAt: session.updatedAt ?? null,
448
449
  messages: session.messageCount ?? null,
449
450
  active: session.key === snapshot.activeSessionKey,
451
+ writable: session.key === snapshot.activeSessionKey && actions.send === true,
450
452
  live: typeof session.updatedAt === 'number' && now - session.updatedAt <= liveWindowMs,
451
453
  runtimeStatus: session.liveStatus ?? null,
452
454
  }));
package/core.mjs CHANGED
@@ -562,6 +562,7 @@ function readSessions(value) {
562
562
  updatedAt: nullableNumber(row.updatedAt),
563
563
  messages: nullableNumber(row.messages),
564
564
  active: row.active === true,
565
+ writable: row.writable === true,
565
566
  live: row.live === true,
566
567
  runtimeStatus: row.runtimeStatus === 'running' || row.runtimeStatus === 'busy' || row.runtimeStatus === 'idle' ? row.runtimeStatus : null,
567
568
  }];
@@ -896,7 +897,12 @@ function activityDetail(activity, preview, pill) {
896
897
  export function projectAgentActivity(value) {
897
898
  const state = normalizeUiState(value);
898
899
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
900
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
899
901
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
902
+ const unreadTone = state.attention.length > 0
903
+ && state.attention.every((item) => sessionsByKey.get(item.key)?.writable)
904
+ ? 'attention'
905
+ : 'neutral';
900
906
  const ranked = state.sessions.map((row, index) => {
901
907
  const activity = sessionActivity(state, row);
902
908
  return {
@@ -921,6 +927,7 @@ export function projectAgentActivity(value) {
921
927
  detail: activityDetail(activity, preview, state.pill.label),
922
928
  activity,
923
929
  unreadCount,
930
+ unreadTone,
924
931
  };
925
932
  }
926
933
 
package/embed.mjs CHANGED
@@ -569,6 +569,7 @@ function readSessions(value) {
569
569
  updatedAt: nullableNumber(row.updatedAt),
570
570
  messages: nullableNumber(row.messages),
571
571
  active: row.active === true,
572
+ writable: row.writable === true,
572
573
  live: row.live === true,
573
574
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
574
575
  }];
@@ -2219,7 +2220,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2219
2220
  const unreadCount = attention?.unreadCount ?? 0;
2220
2221
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2221
2222
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2222
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2223
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2223
2224
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2224
2225
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2225
2226
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", children: [
package/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface AgentActivityModel {
17
17
  detail: string;
18
18
  activity: SessionActivity;
19
19
  unreadCount: number;
20
+ unreadTone: 'attention' | 'neutral';
20
21
  }
21
22
 
22
23
  export interface HarnessOption {
@@ -58,6 +59,8 @@ export interface SessionRowModel {
58
59
  updatedAt: number | null;
59
60
  messages: number | null;
60
61
  active: boolean;
62
+ /** This surface currently has a real send or terminal-control path for the session. */
63
+ writable: boolean;
61
64
  live: boolean;
62
65
  runtimeStatus: 'running' | 'busy' | 'idle' | null;
63
66
  }
package/messenger.mjs CHANGED
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -2216,7 +2217,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2216
2217
  const unreadCount = attention?.unreadCount ?? 0;
2217
2218
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2218
2219
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2219
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2220
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2220
2221
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2221
2222
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2222
2223
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -554,6 +554,7 @@ function readSessions(value) {
554
554
  updatedAt: nullableNumber(row.updatedAt),
555
555
  messages: nullableNumber(row.messages),
556
556
  active: row.active === true,
557
+ writable: row.writable === true,
557
558
  live: row.live === true,
558
559
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
559
560
  }];
@@ -815,7 +816,9 @@ function activityDetail(activity, preview, pill) {
815
816
  function projectAgentActivity(value) {
816
817
  const state = normalizeUiState(value);
817
818
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
819
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
818
820
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
821
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
819
822
  const ranked = state.sessions.map((row, index) => {
820
823
  const activity2 = sessionActivity(state, row);
821
824
  return {
@@ -839,7 +842,8 @@ function projectAgentActivity(value) {
839
842
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
840
843
  detail: activityDetail(activity, preview, state.pill.label),
841
844
  activity,
842
- unreadCount
845
+ unreadCount,
846
+ unreadTone
843
847
  };
844
848
  }
845
849
  function toolTarget(argumentsText) {
@@ -926,7 +930,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
926
930
  const Logo = components.HarnessLogo ?? HarnessLogo;
927
931
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
928
932
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
929
- return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
933
+ return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
930
934
  /* @__PURE__ */ jsxs2("span", { className: "scui-launcher-logo", children: [
931
935
  /* @__PURE__ */ jsx2(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
932
936
  value.unreadCount ? /* @__PURE__ */ jsx2("b", { "aria-hidden": "true", children: badge }) : null
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -827,7 +828,9 @@ function activityDetail(activity, preview, pill) {
827
828
  function projectAgentActivity(value) {
828
829
  const state = normalizeUiState(value);
829
830
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
831
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
830
832
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
833
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
831
834
  const ranked = state.sessions.map((row, index) => {
832
835
  const activity2 = sessionActivity(state, row);
833
836
  return {
@@ -851,7 +854,8 @@ function projectAgentActivity(value) {
851
854
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
852
855
  detail: activityDetail(activity, preview, state.pill.label),
853
856
  activity,
854
- unreadCount
857
+ unreadCount,
858
+ unreadTone
855
859
  };
856
860
  }
857
861
  function filterSessions(rows, query) {
@@ -1639,7 +1643,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
1639
1643
  const Logo = components.HarnessLogo ?? HarnessLogo;
1640
1644
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
1641
1645
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
1642
- return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1646
+ return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1643
1647
  /* @__PURE__ */ jsxs5("span", { className: "scui-launcher-logo", children: [
1644
1648
  /* @__PURE__ */ jsx5(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
1645
1649
  value.unreadCount ? /* @__PURE__ */ jsx5("b", { "aria-hidden": "true", children: badge }) : null
@@ -2299,7 +2303,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2299
2303
  const unreadCount = attention?.unreadCount ?? 0;
2300
2304
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2301
2305
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2302
- return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2306
+ return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2303
2307
  /* @__PURE__ */ jsx8(HarnessLogo, { id: row.harness, activity, size: 34 }),
2304
2308
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-copy", children: [
2305
2309
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-title", children: [
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -2216,7 +2217,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2216
2217
  const unreadCount = attention?.unreadCount ?? 0;
2217
2218
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2218
2219
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2219
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2220
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2220
2221
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2221
2222
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2222
2223
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", children: [
@@ -332,7 +332,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
332
332
  const unreadCount = attention?.unreadCount ?? 0;
333
333
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
334
334
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
335
- return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
335
+ return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
336
336
  /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
337
337
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-copy", children: [
338
338
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-title", children: [
package/sessions.mjs CHANGED
@@ -332,7 +332,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
332
332
  const unreadCount = attention?.unreadCount ?? 0;
333
333
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
334
334
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
335
- return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
335
+ return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
336
336
  /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
337
337
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-copy", children: [
338
338
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-title", children: [
package/styles.css CHANGED
@@ -80,6 +80,7 @@
80
80
 
81
81
  .scui-launcher { --scui-bg:#fff; --scui-fg:#191918; --scui-muted:#5f5f5a; --scui-border:#deded9; --scui-positive:#22863a; --scui-warning:#b26a00; --scui-danger:#b4232e; display:inline-flex; min-width:44px; min-height:44px; align-items:center; gap:8px; padding:4px; overflow:visible; border:1px solid var(--scui-border); border-radius:999px; background:color-mix(in srgb,var(--scui-bg) 88%,transparent); color:var(--scui-fg); box-shadow:0 5px 18px rgba(0,0,0,.14); cursor:pointer; font:12px/1.25 var(--scui-font,Inter,ui-sans-serif,system-ui,sans-serif); backdrop-filter:blur(10px) }
82
82
  .scui-launcher-logo { position:relative; display:grid; flex:none; place-items:center }.scui-launcher-logo > b { position:absolute; z-index:1; top:-6px; right:-8px; display:grid; min-width:18px; height:18px; padding:0 4px; place-items:center; border:2px solid var(--scui-bg); border-radius:999px; background:var(--scui-danger); color:#fff; font-size:9px; line-height:1 }
83
+ .scui-launcher[data-unread-tone="neutral"] .scui-launcher-logo > b { background:var(--scui-muted) }
83
84
  .scui-launcher-copy { display:grid; min-width:0; max-width:220px; padding-right:8px; text-align:left }.scui-launcher-copy strong,.scui-launcher-copy small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-launcher-copy small { color:var(--scui-muted); font-size:10px }
84
85
  .scui-launcher:hover { border-color:color-mix(in srgb,var(--scui-fg) 28%,var(--scui-border)); box-shadow:0 7px 22px rgba(0,0,0,.18) }.scui-launcher:focus-visible { outline:2px solid currentColor; outline-offset:2px }
85
86
  @media (prefers-color-scheme:dark) { .scui-launcher { --scui-bg:#151515; --scui-fg:#f3f3ef; --scui-muted:#a3a39d; --scui-border:#353532; --scui-positive:#57c26e; --scui-warning:#e8a23a; --scui-danger:#ff747d } }
@@ -107,6 +108,7 @@
107
108
  .scui-session-path { display:flex; min-width:0; overflow:hidden; color:var(--scui-muted); font-size:9.5px; white-space:nowrap }
108
109
  .scui-session-path-leading { flex:0 1 auto; overflow:hidden; text-overflow:ellipsis }.scui-session-path-separator { flex:none }.scui-session-path-trailing { flex:none; max-width:58%; overflow:hidden; direction:rtl; text-align:left; text-overflow:ellipsis; unicode-bidi:plaintext }
109
110
  .scui-session-preview small { flex:1; min-width:0; color:var(--scui-muted); font-size:10.5px }.scui-session-preview b { display:grid; flex:none; min-width:16px; height:16px; padding:0 4px; place-items:center; border-radius:8px; background:var(--scui-accent); color:var(--scui-bg); font-size:9px; line-height:16px }
111
+ .scui-session[data-writable="false"] .scui-session-preview b { background:var(--scui-muted) }
110
112
  .scui-session-copy em { overflow:hidden; color:var(--scui-danger); font-size:10px; font-style:normal; text-overflow:ellipsis; white-space:nowrap }
111
113
  .scui-load { display:block; margin:7px auto; padding:5px 10px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted); cursor:pointer }.scui-load:disabled { opacity:.65; cursor:default }
112
114