Skip to content

fix(ui): stop showing healthy legacy sessions as 已阻塞; compact sidebar timestamps; localize bypass label - #452

Merged
jackwener merged 1 commit into
mainfrom
ui/blocked-session-display-semantics
Jul 3, 2026
Merged

fix(ui): stop showing healthy legacy sessions as 已阻塞; compact sidebar timestamps; localize bypass label#452
jackwener merged 1 commit into
mainfrom
ui/blocked-session-display-semantics

Conversation

@jackwener

Copy link
Copy Markdown
Member

Driven by a real-app screenshot report (2026-07-03): three healthy chat sessions piled under an 已阻塞 sidebar group with shield icons, titles crushed to ?..., and an English 'Bypass permissions' pill in an otherwise Chinese composer.

1. Blocked display semantics (the headline bug)

The #410 terminal-ledger invariant marks legacy sessions missing a terminal fact as blocked/unknown. The sidebar groups by raw status → intact, resumable conversations showed as 已阻塞 (header badge too), reading like data loss when nothing needed the user's attention.

First-principles split: session-level 已阻塞 is only worth the interruption when the user can ACT — configure a connection, re-login, confirm a permission. tool_failed/unknown mean "the last run's bookkeeping didn't close cleanly"; the failure detail already lives on the failed turn in the chat.

2. Sidebar timestamps crushed titles

>7-day sessions rendered 2026年6月20日 16:33 (~150px) next to the title. New formatCompactTimestamp in @maka/core: relative within 7 days, date-only beyond (6月20日 same year, 2025年6月20日 across years). Sidebar-only; wide surfaces keep the existing formatter (artifact-pane dedup contract still satisfied — one shared implementation in core).

3. Bypass permissions → 跳过确认

询问权限 / 自动执行 / Bypass permissions 跳过确认 — consistent zh across composer, command palette, session settings, and account settings copy.

Verification

  • npm --workspace @maka/desktop test: 1685/1685
  • npm run typecheck: clean
  • behavior spot-check: unknown/tool_failed → active (reason dropped); auth/NO_REAL_CONNECTION/permission_required → blocked kept; non-blocked statuses identity

… timestamps; localize bypass label
Three fixes from a real-app screenshot report (main window, 2026-07-03):
1. Blocked display semantics. The #410 terminal-ledger invariant marks
legacy sessions missing a terminal fact as blocked/unknown. The
sidebar groups by raw status, so intact, resumable conversations
piled up under an 已阻塞 group with shield icons and the chat header
badge read 已阻塞 — for sessions where the model had answered and
nothing needed the user's attention. Session-level blocked is now a
display-layer distinction: only actionable reasons
(NO_REAL_CONNECTION / auth / permission_required) keep the blocked
presentation; tool_failed / unknown normalize to active at the
renderer state boundary (commitSessions / upsertSessionSummary), so
grouping, row icon, and header badge agree. Runtime status writes
are untouched. Regression: session-status-display-normalization.test.ts.
2. Sidebar timestamps. Sessions older than 7 days rendered the full
medium date + time (2026年6月20日 16:33, ~150px) which crushed the
title next to it to ~2 characters. New formatCompactTimestamp in
@maka/core: relative inside 7 days, date-only beyond (6月20日 within
the year, 2025年6月20日 across years). Sidebar-only; wide surfaces
keep formatRelativeTimestamp.
3. i18n consistency. The composer permission-mode pill showed English
'Bypass permissions' between two Chinese modes; now 跳过确认 in the
composer, command palette, session settings actions, and the
account settings copy.
Desktop suite: 1685/1685; typecheck clean.
@jackwener
jackwener merged commit cdaa1ac into mainJul 3, 2026
@jackwener
jackwener deleted the ui/blocked-session-display-semantics branch July 3, 2026 06:05
jackwener added a commit to sunheyi6/maka-agent that referenced this pull request Jul 3, 2026
Maintainer pass on apache#456 (cold-start reordering + lazy splits). The
perf direction is right; this closes the races the reordering opened:
- recovery vs live send (review P0): recoverInterruptedSessions now
re-checks runtimeKernel.hasActiveRuns immediately before BOTH
updateStatus('active') sites, so a message sent mid-recovery owns
the session status. Recovery-order contract rewritten to pin the
new invariant (recovery inside runBackgroundStartup + the kernel
guard) instead of the old recovery-before-window ordering.
- snapshot seeding bypassed display normalization (apache#452 regression):
the onboarding-snapshot seed is a third renderer state entry —
without normalizeSessionSummaryForDisplay legacy blocked/unknown
sessions flash an 已阻塞 group on first paint. Now normalized, and
a snapshot-IPC-failure fallback calls bootstrapSessions() +
refreshConnections() so the sidebar cannot stay empty forever.
- env bootstrap connections never reached the renderer (review P1):
ensureBootstrapConnection creates connections in background startup
but never emitted connections:event — on a fresh env-var cold start
the model picker stayed empty until an unrelated refresh. Both
creation paths now emitConnectionListChanged().
- bundled-skills race (review P1): the fire-and-forget copy moved from
main-window into runBackgroundStartup with a deferred handle;
skills:list awaits it so an early Skills-page open cannot observe a
half-bundled list (settled promise = zero steady-state cost).
- scope: dropped .github/workflows/sync-fork.yml (author's fork-sync
cron; on upstream it would be a zombie hourly self-merge job).
Kept as-is after verification: dev:hmr -> dev.mjs (the launcher self-
contains incremental tsc --build for all workspaces, so the alias is
semantically equivalent and faster); lazy markdown split (fallback
renders plain text, no unstyled flash).
typecheck clean; runtime 806/806; desktop 1696/1696.
jackwener added a commit that referenced this pull request Jul 3, 2026
* Add fork sync GitHub Actions workflow
* Change fork sync to hourly
* perf(desktop): cut first-paint latency — lazy heavy modules, bundle onboarding data, defer non-critical startup
A coordinated cold-start pass so the window shows an interactive shell
within milliseconds of launch.
Main process:
- main.ts: createWindow() now runs before runBackgroundStartup();
credential migration, connection bootstrap, telemetry/pricing,
interrupted-session recovery, bot bridges, gateway and schedulers
all run concurrently in the background and never block first paint.
- main-window.ts: ensureBundledOfficeSkills moved off the createWindow
critical path to a fire-and-forget task after loadFile.
Renderer — shrink the initial chunk via React.lazy + Suspense:
- markdown.tsx + new markdown-body.tsx: split the react-markdown +
remark-* + rehype-highlight (highlight.js) pipeline behind lazy so
it's only parsed when a message actually renders.
- chat-view.tsx: SkillsModuleMain / DailyReviewPanel / PlanReminderPanel.
- app-shell.tsx: ArtifactPane / BrowserPanel.
- app-shell-overlays.tsx: SettingsModal.
- OnboardingHero.tsx / command-palette.tsx: import the fine-grained
provider-display / settings-nav leaves instead of the full
ProvidersPanel / SettingsModal modules.
First-paint data — bundle into the onboarding snapshot to skip
redundant IPC round-trips:
- onboarding-service.ts / global.d.ts / preload.ts: OnboardingSnapshot
now carries sessions / connections / defaultSlug.
- use-onboarding-snapshot.ts: expose getSessions/getConnections/
getDefaultSlug.
- app-shell.tsx: seed the sidebar + model picker from the snapshot on
first load; hoist onboardingComposerHidden to suppress the composer
skeleton flash.
- app-shell-effects.ts: only refreshShellSettings stays on the critical
path (theme + locale); appInfo / memory / skills / planReminders /
visual-smoke defer to requestAnimationFrame.
Dev:
- apps/desktop/package.json: dev:hmr simplified to .
Tests follow the refactor:
- modal-lifecycle-contract: tolerate the optional <Suspense> boundary
around the lazy SettingsModal.
- use-onboarding-snapshot: fixture gains the new snapshot fields.
- visible-copy-hygiene-contract: read CodeBlock from markdown-body.tsx.
* fix(ui): pin home-surface hero to top and fill window width
Two layout fixes for the first-run / ready_empty home surface.
- onboarding.css: PR-FIRSTRUN-FOLD-0 (PR #436) only flattened the
padding stack for .maka-firstrun and intentionally kept the other
onboarding states' centered rhythm. ready_empty ("今天想让 Maka
帮你做什么?") is the actual landing page once a model is
configured, so mirror the same fold-flattening for
:has(.maka-onboarding-ready) — chatContent padding-top
clamp(72px,10vh,116px) -> clamp(24px,3.5vh,44px), align-content
center -> start, and onboarding-stack min-height -> 0 /
padding-top clamp(40px,7vh,76px) -> clamp(12px,2vh,24px). The hero
+ quickchat + first-run checklist now sit near the top instead of
floating mid-window.
- sidebar.css: #root (index.html) is display:flex + justify-content:
center so the preload skeleton centers before React mounts. Once
mounted, .appFrame is that flex container's only child, and without
an explicit main-axis size it shrank to content max-content and got
centered — so on the narrow-content first-run hero the whole app
frame floated mid-window with large L/R gutters, while long-message
sessions filled the width. Pin .appFrame width:100% so it always
spans the window regardless of inner content. Preload centering is
unaffected (it's still the only child at that point).
* review: harden background-startup races, seed normalization, and scope
Maintainer pass on #456 (cold-start reordering + lazy splits). The
perf direction is right; this closes the races the reordering opened:
- recovery vs live send (review P0): recoverInterruptedSessions now
re-checks runtimeKernel.hasActiveRuns immediately before BOTH
updateStatus('active') sites, so a message sent mid-recovery owns
the session status. Recovery-order contract rewritten to pin the
new invariant (recovery inside runBackgroundStartup + the kernel
guard) instead of the old recovery-before-window ordering.
- snapshot seeding bypassed display normalization (#452 regression):
the onboarding-snapshot seed is a third renderer state entry —
without normalizeSessionSummaryForDisplay legacy blocked/unknown
sessions flash an 已阻塞 group on first paint. Now normalized, and
a snapshot-IPC-failure fallback calls bootstrapSessions() +
refreshConnections() so the sidebar cannot stay empty forever.
- env bootstrap connections never reached the renderer (review P1):
ensureBootstrapConnection creates connections in background startup
but never emitted connections:event — on a fresh env-var cold start
the model picker stayed empty until an unrelated refresh. Both
creation paths now emitConnectionListChanged().
- bundled-skills race (review P1): the fire-and-forget copy moved from
main-window into runBackgroundStartup with a deferred handle;
skills:list awaits it so an early Skills-page open cannot observe a
half-bundled list (settled promise = zero steady-state cost).
- scope: dropped .github/workflows/sync-fork.yml (author's fork-sync
cron; on upstream it would be a zombie hourly self-merge job).
Kept as-is after verification: dev:hmr -> dev.mjs (the launcher self-
contains incremental tsc --build for all workspaces, so the alias is
semantically equivalent and faster); lazy markdown split (fallback
renders plain text, no unstyled flash).
typecheck clean; runtime 806/806; desktop 1696/1696.
---------
Co-authored-by: jackwener <jakevingoo@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jackwener