You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/09/17 08:40:46 UTC

(maka) branch main updated: fix(desktop): keep the collapsed titlebar rail beside the traffic lights (#5430)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new 9f706efb9a fix(desktop): keep the collapsed titlebar rail beside the traffic lights (#5430)
9f706efb9a is described below

commit 9f706efb9adfa8a404a402188444d76a072ed851
Author: AstroHan <le...@outlook.com>
AuthorDate: Thu Sep 17 16:40:42 2026 +0800

    fix(desktop): keep the collapsed titlebar rail beside the traffic lights (#5430)
    
    #4829 made the appFrame publish --maka-sidenav-width unconditionally and wrote a unitless 0 for the collapsed state. A unitless 0 is a <number>, so calc(0 - 4px - …) in the titlebar's first track is a type error that drops the whole grid-template-columns; the strip falls back to implicit columns and the rail parks mid-window while the sidebar is gone.
    
    Publish '0px' instead, and move the two column-width vars into a shared writer (appShellFrameStyle) so the Storybook frame can no longer drift from the production write — the story had been mirroring the pre-#4829 conditional write, which is exactly why the regression shipped silent. The writer lives in src/renderer/shell/, the zone the architecture ledger sanctions for new AppShell dependencies, so the frame gains no legacy import debt. UpdateDownloadedCollapsed now asserts the grid  [...]
    
    The collapsed-state CSS rule that set --maka-sidenav-width is deleted: every real frame publishes the var inline, so the rule could never win — and its comment still documented it as the collapsed-width mechanism while an inline 0 was already shadowing it. That shadowing was the bug.
    
    Generated-by: Devin
---
 apps/desktop/renderer-architecture.json            |  3 +-
 .../main/__tests__/app-shell-frame-style.test.ts   | 39 ++++++++++++++++++++++
 apps/desktop/src/renderer/app-shell.tsx            | 13 ++++----
 .../features/session-navigation/testing.ts         |  1 +
 apps/desktop/src/renderer/shell/frame-style.ts     | 37 ++++++++++++++++++++
 apps/desktop/src/renderer/styles/shell-layout.css  | 18 ++++------
 apps/desktop/stories/app-shell.stories.tsx         | 30 +++++++++++------
 7 files changed, 111 insertions(+), 30 deletions(-)

diff --git a/apps/desktop/renderer-architecture.json b/apps/desktop/renderer-architecture.json
index a41ccae743..5bb7e985b3 100644
--- a/apps/desktop/renderer-architecture.json
+++ b/apps/desktop/renderer-architecture.json
@@ -832,6 +832,7 @@
           "./settings/provider-brand-marks": 1,
           "./settings/provider-display": 1,
           "./settings/runtime-host-ssh-terminal-dialog.js": 1,
+          "./shell/frame-style": 1,
           "./stale-sessions": 1,
           "./use-active-execution-boundary": 1,
           "./use-app-shell-composer-quotes": 1,
@@ -861,7 +862,7 @@
           "react": 1
         },
         "importSpecifiers": 100,
-        "nonTriviaTokens": 13152
+        "nonTriviaTokens": 13139
       },
       "src/renderer/use-app-shell-composer-quotes.ts": {
         "importDeclarations": 2,
diff --git a/apps/desktop/src/main/__tests__/app-shell-frame-style.test.ts b/apps/desktop/src/main/__tests__/app-shell-frame-style.test.ts
new file mode 100644
index 0000000000..b6db8bbb5f
--- /dev/null
+++ b/apps/desktop/src/main/__tests__/app-shell-frame-style.test.ts
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert from 'node:assert/strict';
+import { test } from 'node:test';
+import { appShellFrameStyle } from '../../renderer/shell/frame-style.js';
+
+test('every published width stays a <length>, collapsed included', () => {
+  const collapsed = appShellFrameStyle({
+    sessionListCollapsed: true,
+    sessionListWidth: 260,
+    workbarRightWidth: 480,
+  }) as Record<string, string>;
+  const expanded = appShellFrameStyle({
+    sessionListCollapsed: false,
+    sessionListWidth: 291,
+    workbarRightWidth: 480,
+  }) as Record<string, string>;
+
+  assert.equal(collapsed['--maka-sidenav-width'], '0px');
+  assert.equal(expanded['--maka-sidenav-width'], '291px');
+  assert.equal(collapsed['--maka-session-workbar-width'], '480px');
+});
diff --git a/apps/desktop/src/renderer/app-shell.tsx b/apps/desktop/src/renderer/app-shell.tsx
index 3740b12afa..c9b074a387 100644
--- a/apps/desktop/src/renderer/app-shell.tsx
+++ b/apps/desktop/src/renderer/app-shell.tsx
@@ -25,7 +25,6 @@ import {
   useMemo,
   useRef,
   useState,
-  type CSSProperties,
   type Dispatch,
   type SetStateAction,
 } from 'react';
@@ -140,6 +139,7 @@ import {
 } from './app-shell-context-compaction';
 import { AppShellTopbarActions } from './app-shell-chrome-actions';
 import { AppShellDetailPanel } from './app-shell-detail-panel';
+import { appShellFrameStyle } from './shell/frame-style';
 import { AppShellOverlays } from './app-shell-overlays';
 import type { ArchivedTasksBridge } from './settings/tasks-settings-page';
 import { CustomPetCompanion } from './custom-pet-companion';
@@ -2234,12 +2234,11 @@ function AppShellContent({
       /* The frame is the shared owner for dimensions consumed by both shell
          columns and titlebar chrome. CSS clears the titlebar reserve when the
          responsive layout moves the workbar below the conversation. */
-      style={
-        ({
-          '--maka-session-workbar-width': `${workbar.host.rightWidth}px`,
-          '--maka-sidenav-width': sessionListCollapsed ? 0 : `${sessionListWidth}px`,
-        } as CSSProperties)
-      }
+      style={appShellFrameStyle({
+        sessionListCollapsed,
+        sessionListWidth,
+        workbarRightWidth: workbar.host.rightWidth,
+      })}
     >
       <Conversation.TranscriptReadingPositionController
         commands={transcriptReadingCommands}
diff --git a/apps/desktop/src/renderer/features/session-navigation/testing.ts b/apps/desktop/src/renderer/features/session-navigation/testing.ts
index 59d17855bb..9626db5afc 100644
--- a/apps/desktop/src/renderer/features/session-navigation/testing.ts
+++ b/apps/desktop/src/renderer/features/session-navigation/testing.ts
@@ -51,6 +51,7 @@ export {
 } from './model/session-selection.js';
 export {
   readSessionListViewMode,
+  SESSION_LIST_EXPANDED_DEFAULT_WIDTH,
   writeSessionListViewMode,
 } from './model/session-list-layout.js';
 export { createSessionRailLayoutStore } from './model/session-rail-layout-store.js';
diff --git a/apps/desktop/src/renderer/shell/frame-style.ts b/apps/desktop/src/renderer/shell/frame-style.ts
new file mode 100644
index 0000000000..65aabb9d6f
--- /dev/null
+++ b/apps/desktop/src/renderer/shell/frame-style.ts
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import type { CSSProperties } from 'react';
+
+/* The appFrame publishes both column widths because the titlebar's first grid
+   track is a calc() on --maka-sidenav-width. Every value here must stay a
+   <length>: a unitless 0 makes that calc() invalid at computed-value time and
+   drops the whole grid-template-columns — the strip falls back to implicit
+   columns and the rail parks mid-window instead of beside the traffic lights.
+   Shared with the Storybook frame so the story cannot drift from this write. */
+export function appShellFrameStyle(input: {
+  sessionListCollapsed: boolean;
+  sessionListWidth: number;
+  workbarRightWidth: number;
+}): CSSProperties {
+  return {
+    '--maka-session-workbar-width': `${input.workbarRightWidth}px`,
+    '--maka-sidenav-width': `${input.sessionListCollapsed ? 0 : input.sessionListWidth}px`,
+  } as CSSProperties;
+}
diff --git a/apps/desktop/src/renderer/styles/shell-layout.css b/apps/desktop/src/renderer/styles/shell-layout.css
index 447e4779c0..084667bb92 100644
--- a/apps/desktop/src/renderer/styles/shell-layout.css
+++ b/apps/desktop/src/renderer/styles/shell-layout.css
@@ -187,22 +187,16 @@
    were too narrow to carry their meaning without tooltips. The titlebar keeps
    the explicit expand action, so the navigation column can close all the way.
 
-   Keyed off the shell's own collapsed flag — the wrapper carried a second copy
-   of it until this rule read the one `.appFrame` already publishes and the
-   hairlines already use.
-
-   It overrides the VARIABLE, not the wrapper's width, because the wrapper is no
-   longer the only reader: the titlebar starts its session breadcrumb at this
-   column's right edge. Setting the width here instead would leave the titlebar
-   reading the expanded value while the column sat collapsed. */
-.appFrame[data-sidebar-state='collapsed'] {
-  --maka-sidenav-width: 0px;
-}
+   The frame publishes `--maka-sidenav-width` itself; the titlebar reads it for
+   its first grid track, so nothing here may write it — an inline `0` was once
+   shadowed into `calc()` and dropped the whole track list. */
 
 /* `visibility`, rather than opacity, removes the hidden rail's buttons from
    focus navigation and accessibility hit testing. SideNav stays mounted so its
    imperative collapse handle can restore both the boolean and the resizable
-   width when the titlebar's expand button is pressed. */
+   width when the titlebar's expand button is pressed. Keyed off the collapsed
+   flag `.appFrame` already publishes — the same one the hairlines use — rather
+   than a second copy on the wrapper. */
 .appFrame[data-sidebar-state='collapsed'] .maka-sidenav-motion {
   visibility: hidden;
   pointer-events: none;
diff --git a/apps/desktop/stories/app-shell.stories.tsx b/apps/desktop/stories/app-shell.stories.tsx
index 54c5defc24..5d68e1ab1a 100644
--- a/apps/desktop/stories/app-shell.stories.tsx
+++ b/apps/desktop/stories/app-shell.stories.tsx
@@ -40,6 +40,7 @@ import {
 import type { ChatModelChoice, SessionViewMode, TurnViewModel, LiveTurnBuffer } from '@maka/ui';
 import { SessionRail, type SessionRailStoryProps } from '../../../packages/ui/stories/session-rail-harness.js';
 import { AppShellTopbarActions } from '../src/renderer/app-shell-chrome-actions';
+import { appShellFrameStyle } from '../src/renderer/shell/frame-style';
 import { SettingsOverlay } from '../src/renderer/app-shell-overlays';
 import {
   WorkbarServicesProvider,
@@ -60,6 +61,7 @@ import {
   deriveBranchBanner,
   deriveSessionRail,
   deriveSessionRevisionNavigation,
+  SESSION_LIST_EXPANDED_DEFAULT_WIDTH,
 } from '../src/renderer/features/session-navigation/testing';
 import { AppShell as AstryxAppShell } from '@astryxdesign/core/AppShell';
 import { Button } from '@astryxdesign/core';
@@ -310,17 +312,17 @@ function ShellFrame(props: {
       style={
         {
           minHeight: 640,
-          '--maka-session-workbar-width': `${props.workbarWidth ?? 480}px`,
           height: props.height,
-          /* Same publication point as production, for the same reason as
-             `data-sidebar-state` above: the titlebar's first grid track is a
-             `calc()` on this variable, and an unset variable makes the whole
-             track list invalid — the breadcrumb then parks against the icon
-             rail instead of the plate seam, so the seam and truncation stories
-             would be reviewing a layout the app never renders. Collapsed is
-             left to the CSS rule, exactly as in the app.
-             `SessionListPanel`'s own default width. */
-          ...(props.sidebarCollapsed ? null : { '--maka-sidenav-width': '260px' }),
+          /* Same writer as the production frame (app-shell.tsx) for the same
+             reason as `data-sidebar-state` above: the titlebar's first grid
+             track is a calc() on --maka-sidenav-width, so the story has to
+             publish the exact value the app writes or the seam and truncation
+             stories would review a layout the app never renders. */
+          ...appShellFrameStyle({
+            sessionListCollapsed: props.sidebarCollapsed ?? false,
+            sessionListWidth: SESSION_LIST_EXPANDED_DEFAULT_WIDTH,
+            workbarRightWidth: props.workbarWidth ?? SESSION_WORKBAR_DEFAULT_WIDTH,
+          }),
         } as CSSProperties
       }
     >
@@ -564,6 +566,14 @@ export const UpdateDownloadedCollapsed: Story = {
     if (!sidebar || !motion) throw new Error('Collapsed sidebar did not render');
     await expect(sidebar).not.toBeVisible();
     expect(getComputedStyle(motion).width).toBe('0px');
+    /* The rail must still hug the safe-area gutter: a --maka-sidenav-width
+       that is not a <length> (a unitless 0 was the regression) invalidates the
+       grid's calc(), the track list drops, and the rail parks mid-window. */
+    const titlebar = canvasElement.querySelector<HTMLElement>('.maka-window-titlebar');
+    const rail = canvasElement.querySelector<HTMLElement>('.maka-shell-topbar-rail');
+    if (!titlebar || !rail) throw new Error('Collapsed titlebar did not render');
+    expect(getComputedStyle(titlebar).gridTemplateColumns.trim().split(/\s+/)).toHaveLength(3);
+    expect(rail.getBoundingClientRect().left).toBeLessThan(160);
     const expand = canvas.getByRole('button', { name: '展开侧边栏' });
     await expect(expand).toBeVisible();
     expand.click();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org