@lobehub/lobehub 2.0.0-next.270 → 2.0.0-next.271

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/.eslintrc.js CHANGED
@@ -22,6 +22,7 @@ config.rules['unicorn/prefer-query-selector'] = 0;
22
22
  config.rules['unicorn/no-array-callback-reference'] = 0;
23
23
  // FIXME: Linting error in src/app/[variants]/(main)/chat/features/Migration/DBReader.ts, the fundamental solution should be upgrading typescript-eslint
24
24
  config.rules['@typescript-eslint/no-useless-constructor'] = 0;
25
+ config.rules['@next/next/no-img-element'] = 0;
25
26
 
26
27
  config.overrides = [
27
28
  {
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.271](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.270...v2.0.0-next.271)
6
+
7
+ <sup>Released on **2026-01-12**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Improve baseline alignment for tool items.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Improve baseline alignment for tool items, closes [#11447](https://github.com/lobehub/lobe-chat/issues/11447) ([be8dddd](https://github.com/lobehub/lobe-chat/commit/be8dddd))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.270](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.269...v2.0.0-next.270)
6
31
 
7
32
  <sup>Released on **2026-01-12**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Improve baseline alignment for tool items."
6
+ ]
7
+ },
8
+ "date": "2026-01-12",
9
+ "version": "2.0.0-next.271"
10
+ },
2
11
  {
3
12
  "children": {},
4
13
  "date": "2026-01-12",
@@ -55,12 +55,13 @@ When('I click on a category in the category menu', async function (this: CustomW
55
55
  // Wait for categories to be visible
56
56
  await categoryItems.first().waitFor({ state: 'visible', timeout: 30_000 });
57
57
 
58
- // Click the second category (skip "All" which is usually first)
59
- const secondCategory = categoryItems.nth(1);
60
- await secondCategory.click();
58
+ // Click the third category (skip "Discover" at index 0 and "All" at index 1)
59
+ // This should select the first actual category filter like "Academic"
60
+ const targetCategory = categoryItems.nth(2);
61
+ await targetCategory.click();
61
62
 
62
63
  // Store the category for later verification
63
- const categoryText = await secondCategory.textContent();
64
+ const categoryText = await targetCategory.textContent();
64
65
  this.testContext.selectedCategory = categoryText?.trim();
65
66
  });
66
67
 
@@ -94,12 +95,13 @@ When('I click on a category in the category filter', async function (this: Custo
94
95
  // Wait for categories to be visible
95
96
  await categoryItems.first().waitFor({ state: 'visible', timeout: 30_000 });
96
97
 
97
- // Click the second category (skip "All" which is usually first)
98
- const secondCategory = categoryItems.nth(1);
99
- await secondCategory.click();
98
+ // Click the third category (skip "Discover" at index 0 and "All" at index 1)
99
+ // This should select the first actual category filter
100
+ const targetCategory = categoryItems.nth(2);
101
+ await targetCategory.click();
100
102
 
101
103
  // Store the category for later verification
102
- const categoryText = await secondCategory.textContent();
104
+ const categoryText = await targetCategory.textContent();
103
105
  this.testContext.selectedCategory = categoryText?.trim();
104
106
  });
105
107
 
@@ -285,10 +287,15 @@ When(
285
287
 
286
288
  // Try to find "more" link near MCP-related content
287
289
  const mcpSection = this.page.locator('section:has-text("MCP"), div:has-text("MCP Tools")');
288
- const mcpSectionVisible = await mcpSection.first().isVisible().catch(() => false);
290
+ const mcpSectionVisible = await mcpSection
291
+ .first()
292
+ .isVisible()
293
+ .catch(() => false);
289
294
 
290
295
  if (mcpSectionVisible) {
291
- const moreLinkInSection = mcpSection.locator(`a:has-text("${linkText}"), button:has-text("${linkText}")`);
296
+ const moreLinkInSection = mcpSection.locator(
297
+ `a:has-text("${linkText}"), button:has-text("${linkText}")`,
298
+ );
292
299
  if ((await moreLinkInSection.count()) > 0) {
293
300
  await moreLinkInSection.first().click();
294
301
  return;
@@ -297,7 +304,9 @@ When(
297
304
 
298
305
  // Fallback: click on MCP in the sidebar navigation
299
306
  console.log(' 📍 Fallback: clicking MCP in sidebar');
300
- const mcpNavItem = this.page.locator('nav a:has-text("MCP"), [class*="nav"] a:has-text("MCP")').first();
307
+ const mcpNavItem = this.page
308
+ .locator('nav a:has-text("MCP"), [class*="nav"] a:has-text("MCP")')
309
+ .first();
301
310
  if (await mcpNavItem.isVisible().catch(() => false)) {
302
311
  await mcpNavItem.click();
303
312
  return;
@@ -363,9 +372,19 @@ Then(
363
372
 
364
373
  Then('the URL should contain the category parameter', async function (this: CustomWorld) {
365
374
  const currentUrl = this.page.url();
375
+ console.log(` 📍 Current URL: ${currentUrl}`);
376
+ console.log(` 📍 Selected category: ${this.testContext.selectedCategory}`);
377
+
366
378
  // Check if URL contains a category-related parameter
379
+ // The URL format is: /community/assistant?category=xxx
380
+ const hasCategory =
381
+ currentUrl.includes('category=') ||
382
+ currentUrl.includes('tag=') ||
383
+ // For path-based routing like /community/assistant/category-name
384
+ /\/community\/assistant\/[^/?]+/.test(currentUrl);
385
+
367
386
  expect(
368
- currentUrl.includes('category=') || currentUrl.includes('tag='),
387
+ hasCategory,
369
388
  `Expected URL to contain category parameter, but got: ${currentUrl}`,
370
389
  ).toBeTruthy();
371
390
  });
@@ -383,7 +402,9 @@ Then('I should see different assistant cards', async function (this: CustomWorld
383
402
 
384
403
  // If we used infinite scroll, check that we have cards (might be same or more)
385
404
  if (this.testContext.usedInfiniteScroll) {
386
- console.log(` 📍 Used infinite scroll, initial count was: ${this.testContext.initialCardCount}`);
405
+ console.log(
406
+ ` 📍 Used infinite scroll, initial count was: ${this.testContext.initialCardCount}`,
407
+ );
387
408
  expect(currentCount).toBeGreaterThan(0);
388
409
  } else {
389
410
  expect(currentCount).toBeGreaterThan(0);
@@ -463,7 +484,9 @@ Then('I should see the model detail content', async function (this: CustomWorld)
463
484
 
464
485
  // Model detail page should have tabs like "Overview", "Model Parameters"
465
486
  // Wait for these specific elements to appear
466
- const modelTabs = this.page.locator('text=/Overview|Model Parameters|Related Recommendations|Configuration Guide/');
487
+ const modelTabs = this.page.locator(
488
+ 'text=/Overview|Model Parameters|Related Recommendations|Configuration Guide/',
489
+ );
467
490
 
468
491
  console.log(' 📍 Waiting for model detail content to load...');
469
492
  await expect(modelTabs.first()).toBeVisible({ timeout: 30_000 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.270",
3
+ "version": "2.0.0-next.271",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -1,14 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { Flexbox } from '@lobehub/ui';
4
- import { Switch } from 'antd';
3
+ import { type DropdownMenuCheckboxItem } from '@lobehub/ui';
5
4
  import { useMemo } from 'react';
6
5
  import { useTranslation } from 'react-i18next';
7
6
 
8
7
  import { useGlobalStore } from '@/store/global';
9
8
  import { systemStatusSelectors } from '@/store/global/selectors';
10
9
 
11
- export const useMenu = (): { menuItems: any[] } => {
10
+ export const useMenu = (): { menuItems: DropdownMenuCheckboxItem[] } => {
12
11
  const { t } = useTranslation('chat');
13
12
 
14
13
  const [wideScreen, toggleWideScreen] = useGlobalStore((s) => [
@@ -16,23 +15,14 @@ export const useMenu = (): { menuItems: any[] } => {
16
15
  s.toggleWideScreen,
17
16
  ]);
18
17
 
19
- const menuItems = useMemo(
18
+ const menuItems = useMemo<DropdownMenuCheckboxItem[]>(
20
19
  () => [
21
20
  {
21
+ checked: wideScreen,
22
22
  key: 'full-width',
23
- label: (
24
- <Flexbox align="center" horizontal justify="space-between">
25
- <span>{t('viewMode.fullWidth')}</span>
26
- <Switch
27
- checked={wideScreen}
28
- onChange={toggleWideScreen}
29
- onClick={(checked, event) => {
30
- event.stopPropagation();
31
- }}
32
- size="small"
33
- />
34
- </Flexbox>
35
- ),
23
+ label: t('viewMode.fullWidth'),
24
+ onCheckedChange: toggleWideScreen,
25
+ type: 'checkbox',
36
26
  },
37
27
  ],
38
28
  [t, wideScreen, toggleWideScreen],