@sybilion/uilib 1.3.59 → 1.3.60

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.
@@ -123,29 +123,27 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
123
123
  if (embedAsPage) {
124
124
  return;
125
125
  }
126
- const run = () => {
127
- setIsOpen(open);
128
- setChatPanelOpen(open);
129
- };
130
- if (!isMobile &&
131
- 'startViewTransition' in document &&
132
- document.startViewTransition) {
133
- document.startViewTransition(run);
134
- }
135
- else {
136
- run();
137
- }
138
126
  if (open) {
139
- // Avoid a second setSearchParams when `chat` is already set (e.g. Reports merges
140
- // reportId + chat in one update). A redundant add can merge from a stale `prev`
141
- // and drop other query keys.
127
+ setIsOpen(true);
128
+ setChatPanelOpen(true);
142
129
  if (!searchParams.has(CHAT_QUERY_PARAM)) {
143
130
  addSearchParams({ [CHAT_QUERY_PARAM]: CHAT_OPEN_VALUE });
144
131
  }
132
+ return;
133
+ }
134
+ const applyClose = () => {
135
+ setIsOpen(false);
136
+ setChatPanelOpen(false);
137
+ };
138
+ if (!isMobile &&
139
+ 'startViewTransition' in document &&
140
+ document.startViewTransition) {
141
+ document.startViewTransition(applyClose);
145
142
  }
146
143
  else {
147
- removeSearchParams(CHAT_QUERY_PARAM);
144
+ applyClose();
148
145
  }
146
+ removeSearchParams(CHAT_QUERY_PARAM);
149
147
  };
150
148
  const isEmpty = isChatEmpty(chat) && !isLoading;
151
149
  const openNewChatWithPrefill = useCallback((prompt) => {
@@ -630,9 +628,25 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
630
628
  if (embedAsPage) {
631
629
  return;
632
630
  }
633
- setIsOpen(chatOpen);
634
- setChatPanelOpen(chatOpen);
635
- }, [embedAsPage, chatOpen, setChatPanelOpen]);
631
+ if (!chatOpen) {
632
+ setIsOpen(false);
633
+ setChatPanelOpen(false);
634
+ return;
635
+ }
636
+ if (!shellChatPanelOpen && sidebarNavOpen) {
637
+ removeSearchParams(CHAT_QUERY_PARAM);
638
+ return;
639
+ }
640
+ setIsOpen(true);
641
+ setChatPanelOpen(true);
642
+ }, [
643
+ embedAsPage,
644
+ chatOpen,
645
+ setChatPanelOpen,
646
+ sidebarNavOpen,
647
+ shellChatPanelOpen,
648
+ removeSearchParams,
649
+ ]);
636
650
  /** Shell closed chat for space (e.g. nav opened); keep UI and URL in sync. */
637
651
  useEffect(() => {
638
652
  if (embedAsPage || shellChatPanelOpen || !isOpen) {
@@ -46,13 +46,24 @@ function clampChatWidthPx(px, shellWidth, effectiveSidebarWidthPx) {
46
46
  function defaultChatWidthPx(shellWidth) {
47
47
  return clampChatWidthPx(CHAT_WIDTH_DEFAULT_PX, shellWidth, SIDEBAR_WIDTH_DEFAULT_PX);
48
48
  }
49
+ /**
50
+ * Stored width can fall below panel min when the shell was narrow (clamp cap < min).
51
+ * For dual-panel fit checks, treat open panels as at least their min width.
52
+ */
53
+ function effectiveOpenPanelWidthPx(px, minPx, bothPanelsOpen) {
54
+ if (bothPanelsOpen) {
55
+ return Math.max(px, minPx);
56
+ }
57
+ return px > 0 ? px : minPx;
58
+ }
49
59
  /** Minimum shell width when nav and/or chat panels are open (main column at least `CENTRAL_AREA_MIN_PX`). */
50
60
  function requiredShellWidthForSidebars(widths) {
61
+ const bothPanelsOpen = widths.mainSidebarOpen && widths.chatPanelOpen;
51
62
  const sidebarW = widths.mainSidebarOpen
52
- ? widths.sidebarWidthPx || SIDEBAR_WIDTH_MIN_PX
63
+ ? effectiveOpenPanelWidthPx(widths.sidebarWidthPx, SIDEBAR_WIDTH_MIN_PX, bothPanelsOpen)
53
64
  : 0;
54
65
  const chatW = widths.chatPanelOpen
55
- ? widths.chatWidthPx || CHAT_WIDTH_MIN_PX
66
+ ? effectiveOpenPanelWidthPx(widths.chatWidthPx, CHAT_WIDTH_MIN_PX, bothPanelsOpen)
56
67
  : 0;
57
68
  return sidebarW + CENTRAL_AREA_MIN_PX + chatW;
58
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.59",
3
+ "version": "1.3.60",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -287,30 +287,29 @@ export function useChatPanelChromeModel({
287
287
  if (embedAsPage) {
288
288
  return;
289
289
  }
290
- const run = () => {
291
- setIsOpen(open);
292
- setChatPanelOpen(open);
290
+ if (open) {
291
+ setIsOpen(true);
292
+ setChatPanelOpen(true);
293
+ if (!searchParams.has(CHAT_QUERY_PARAM)) {
294
+ addSearchParams({ [CHAT_QUERY_PARAM]: CHAT_OPEN_VALUE });
295
+ }
296
+ return;
297
+ }
298
+
299
+ const applyClose = () => {
300
+ setIsOpen(false);
301
+ setChatPanelOpen(false);
293
302
  };
294
303
  if (
295
304
  !isMobile &&
296
305
  'startViewTransition' in document &&
297
306
  document.startViewTransition
298
307
  ) {
299
- document.startViewTransition(run);
308
+ document.startViewTransition(applyClose);
300
309
  } else {
301
- run();
302
- }
303
-
304
- if (open) {
305
- // Avoid a second setSearchParams when `chat` is already set (e.g. Reports merges
306
- // reportId + chat in one update). A redundant add can merge from a stale `prev`
307
- // and drop other query keys.
308
- if (!searchParams.has(CHAT_QUERY_PARAM)) {
309
- addSearchParams({ [CHAT_QUERY_PARAM]: CHAT_OPEN_VALUE });
310
- }
311
- } else {
312
- removeSearchParams(CHAT_QUERY_PARAM);
310
+ applyClose();
313
311
  }
312
+ removeSearchParams(CHAT_QUERY_PARAM);
314
313
  };
315
314
 
316
315
  const isEmpty = isChatEmpty(chat) && !isLoading;
@@ -856,9 +855,25 @@ export function useChatPanelChromeModel({
856
855
  if (embedAsPage) {
857
856
  return;
858
857
  }
859
- setIsOpen(chatOpen);
860
- setChatPanelOpen(chatOpen);
861
- }, [embedAsPage, chatOpen, setChatPanelOpen]);
858
+ if (!chatOpen) {
859
+ setIsOpen(false);
860
+ setChatPanelOpen(false);
861
+ return;
862
+ }
863
+ if (!shellChatPanelOpen && sidebarNavOpen) {
864
+ removeSearchParams(CHAT_QUERY_PARAM);
865
+ return;
866
+ }
867
+ setIsOpen(true);
868
+ setChatPanelOpen(true);
869
+ }, [
870
+ embedAsPage,
871
+ chatOpen,
872
+ setChatPanelOpen,
873
+ sidebarNavOpen,
874
+ shellChatPanelOpen,
875
+ removeSearchParams,
876
+ ]);
862
877
 
863
878
  /** Shell closed chat for space (e.g. nav opened); keep UI and URL in sync. */
864
879
  useEffect(() => {
@@ -37,6 +37,28 @@ describe('requiredShellWidthForSidebars', () => {
37
37
  }),
38
38
  ).toBe(SIDEBAR_WIDTH_MIN_PX + CENTRAL_AREA_MIN_PX + CHAT_WIDTH_MIN_PX);
39
39
  });
40
+
41
+ it('uses panel mins for sub-min stored widths when both panels are open', () => {
42
+ expect(
43
+ requiredShellWidthForSidebars({
44
+ mainSidebarOpen: true,
45
+ chatPanelOpen: true,
46
+ sidebarWidthPx: 300,
47
+ chatWidthPx: 37,
48
+ }),
49
+ ).toBe(300 + CENTRAL_AREA_MIN_PX + CHAT_WIDTH_MIN_PX);
50
+ });
51
+
52
+ it('keeps actual chat width when only chat is open', () => {
53
+ expect(
54
+ requiredShellWidthForSidebars({
55
+ mainSidebarOpen: false,
56
+ chatPanelOpen: true,
57
+ sidebarWidthPx: 300,
58
+ chatWidthPx: 337,
59
+ }),
60
+ ).toBe(CENTRAL_AREA_MIN_PX + 337);
61
+ });
40
62
  });
41
63
 
42
64
  describe('shellFitsSidebarsLayout', () => {
@@ -62,6 +84,17 @@ describe('shellFitsSidebarsLayout', () => {
62
84
  ).toBe(false);
63
85
  });
64
86
 
87
+ it('returns false when sub-min chat width would falsely fit at shell edge', () => {
88
+ expect(
89
+ shellFitsSidebarsLayout(1137, {
90
+ mainSidebarOpen: true,
91
+ chatPanelOpen: true,
92
+ sidebarWidthPx: 300,
93
+ chatWidthPx: 37,
94
+ }),
95
+ ).toBe(false);
96
+ });
97
+
65
98
  it('returns true for unknown shell width', () => {
66
99
  expect(
67
100
  shellFitsSidebarsLayout(0, {
@@ -86,15 +86,40 @@ export type SidebarsLayoutWidths = {
86
86
  chatWidthPx: number;
87
87
  };
88
88
 
89
+ /**
90
+ * Stored width can fall below panel min when the shell was narrow (clamp cap < min).
91
+ * For dual-panel fit checks, treat open panels as at least their min width.
92
+ */
93
+ function effectiveOpenPanelWidthPx(
94
+ px: number,
95
+ minPx: number,
96
+ bothPanelsOpen: boolean,
97
+ ): number {
98
+ if (bothPanelsOpen) {
99
+ return Math.max(px, minPx);
100
+ }
101
+ return px > 0 ? px : minPx;
102
+ }
103
+
89
104
  /** Minimum shell width when nav and/or chat panels are open (main column at least `CENTRAL_AREA_MIN_PX`). */
90
105
  export function requiredShellWidthForSidebars(
91
106
  widths: SidebarsLayoutWidths,
92
107
  ): number {
108
+ const bothPanelsOpen =
109
+ widths.mainSidebarOpen && widths.chatPanelOpen;
93
110
  const sidebarW = widths.mainSidebarOpen
94
- ? widths.sidebarWidthPx || SIDEBAR_WIDTH_MIN_PX
111
+ ? effectiveOpenPanelWidthPx(
112
+ widths.sidebarWidthPx,
113
+ SIDEBAR_WIDTH_MIN_PX,
114
+ bothPanelsOpen,
115
+ )
95
116
  : 0;
96
117
  const chatW = widths.chatPanelOpen
97
- ? widths.chatWidthPx || CHAT_WIDTH_MIN_PX
118
+ ? effectiveOpenPanelWidthPx(
119
+ widths.chatWidthPx,
120
+ CHAT_WIDTH_MIN_PX,
121
+ bothPanelsOpen,
122
+ )
98
123
  : 0;
99
124
  return sidebarW + CENTRAL_AREA_MIN_PX + chatW;
100
125
  }