@lobehub/lobehub 2.0.0-next.211 → 2.0.0-next.213

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.
Files changed (83) hide show
  1. package/.github/workflows/auto-i18n.yml +1 -1
  2. package/.github/workflows/bundle-analyzer.yml +1 -1
  3. package/.github/workflows/claude-auto-testing.yml +1 -1
  4. package/.github/workflows/claude-dedupe-issues.yml +1 -1
  5. package/.github/workflows/claude-issue-triage.yml +1 -1
  6. package/.github/workflows/claude-translate-comments.yml +1 -1
  7. package/.github/workflows/claude-translator.yml +1 -1
  8. package/.github/workflows/claude.yml +1 -1
  9. package/.github/workflows/desktop-build-electron.yml +2 -2
  10. package/.github/workflows/e2e.yml +1 -1
  11. package/.github/workflows/issue-auto-close-duplicates.yml +1 -1
  12. package/.github/workflows/lighthouse.yml +2 -2
  13. package/.github/workflows/lock-closed-issues.yml +1 -1
  14. package/.github/workflows/manual-build-desktop.yml +6 -6
  15. package/.github/workflows/pr-build-desktop.yml +5 -5
  16. package/.github/workflows/pr-build-docker.yml +2 -2
  17. package/.github/workflows/release-desktop-beta.yml +4 -4
  18. package/.github/workflows/release-docker.yml +2 -2
  19. package/.github/workflows/release.yml +1 -1
  20. package/.github/workflows/sync-database-schema.yml +1 -1
  21. package/.github/workflows/sync.yml +1 -1
  22. package/.github/workflows/test.yml +5 -5
  23. package/.github/workflows/verify-desktop-patch.yml +1 -1
  24. package/CHANGELOG.md +58 -0
  25. package/changelog/v1.json +14 -0
  26. package/locales/ar/models.json +35 -4
  27. package/locales/ar/providers.json +1 -0
  28. package/locales/bg-BG/models.json +24 -1
  29. package/locales/bg-BG/providers.json +1 -0
  30. package/locales/de-DE/models.json +30 -1
  31. package/locales/de-DE/providers.json +1 -0
  32. package/locales/en-US/models.json +1 -0
  33. package/locales/en-US/providers.json +1 -0
  34. package/locales/es-ES/models.json +32 -1
  35. package/locales/es-ES/providers.json +1 -0
  36. package/locales/fa-IR/models.json +48 -1
  37. package/locales/fa-IR/providers.json +1 -0
  38. package/locales/fr-FR/models.json +47 -1
  39. package/locales/fr-FR/providers.json +1 -0
  40. package/locales/it-IT/models.json +32 -1
  41. package/locales/it-IT/providers.json +1 -0
  42. package/locales/ja-JP/models.json +2 -1
  43. package/locales/ja-JP/providers.json +1 -0
  44. package/locales/ko-KR/models.json +24 -1
  45. package/locales/ko-KR/providers.json +1 -0
  46. package/locales/nl-NL/models.json +46 -1
  47. package/locales/nl-NL/providers.json +1 -0
  48. package/locales/pl-PL/models.json +41 -1
  49. package/locales/pl-PL/providers.json +1 -0
  50. package/locales/pt-BR/models.json +32 -1
  51. package/locales/pt-BR/providers.json +1 -0
  52. package/locales/ru-RU/models.json +54 -2
  53. package/locales/ru-RU/providers.json +1 -0
  54. package/locales/tr-TR/models.json +32 -1
  55. package/locales/tr-TR/providers.json +1 -0
  56. package/locales/vi-VN/models.json +37 -1
  57. package/locales/vi-VN/providers.json +1 -0
  58. package/locales/zh-CN/models.json +24 -3
  59. package/locales/zh-CN/providers.json +1 -0
  60. package/locales/zh-TW/models.json +11 -1
  61. package/locales/zh-TW/providers.json +1 -0
  62. package/package.json +1 -1
  63. package/packages/context-engine/src/engine/messages/types.ts +1 -1
  64. package/packages/model-runtime/src/core/BaseAI.ts +1 -1
  65. package/packages/model-runtime/src/core/streams/qwen.test.ts +140 -0
  66. package/packages/model-runtime/src/core/streams/qwen.ts +17 -5
  67. package/packages/model-runtime/src/types/chat.ts +12 -12
  68. package/packages/model-runtime/src/types/error.ts +1 -1
  69. package/packages/model-runtime/src/types/image.ts +1 -1
  70. package/src/app/(backend)/f/[id]/route.ts +2 -2
  71. package/src/app/[variants]/(main)/chat/features/Conversation/Header/index.tsx +2 -1
  72. package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +28 -18
  73. package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +68 -3
  74. package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/MasonryItemWrapper.tsx +0 -2
  75. package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +114 -86
  76. package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +72 -32
  77. package/src/features/ResourceManager/components/Explorer/index.tsx +1 -14
  78. package/src/libs/better-auth/define-config.ts +1 -4
  79. package/src/libs/redis/upstash.ts +4 -1
  80. package/src/server/services/comfyui/config/constants.ts +7 -7
  81. package/src/server/services/comfyui/config/promptToolConst.ts +26 -26
  82. package/src/server/services/comfyui/utils/promptSplitter.ts +23 -23
  83. package/src/server/services/comfyui/utils/weightDType.ts +4 -5
@@ -6,106 +6,134 @@ import { cssVar } from 'antd-style';
6
6
  import { type UIEvent, memo, useCallback, useMemo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
- import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
10
- import { type FileListItem } from '@/types/files';
9
+ import { useFolderPath } from '@/app/[variants]/(main)/resource/features/hooks/useFolderPath';
10
+ import {
11
+ useResourceManagerFetchKnowledgeItems,
12
+ useResourceManagerStore,
13
+ } from '@/app/[variants]/(main)/resource/features/store';
14
+ import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
11
15
 
12
16
  import { useMasonryColumnCount } from '../useMasonryColumnCount';
13
17
  import MasonryItemWrapper from './MasonryFileItem/MasonryItemWrapper';
14
18
 
15
- interface MasonryViewProps {
16
- data: FileListItem[] | undefined;
17
- hasMore: boolean;
18
- isMasonryReady: boolean;
19
- loadMore: () => Promise<void>;
20
- onOpenFile?: (id: string) => void;
21
- selectFileIds: string[];
22
- setSelectedFileIds: (ids: string[]) => void;
23
- }
19
+ const MasonryView = memo(() => {
20
+ // Access all state from Resource Manager store
21
+ const [
22
+ libraryId,
23
+ category,
24
+ searchQuery,
25
+ selectedFileIds,
26
+ setSelectedFileIds,
27
+ loadMoreKnowledgeItems,
28
+ fileListHasMore,
29
+ isMasonryReady,
30
+ sorter,
31
+ sortType,
32
+ ] = useResourceManagerStore((s) => [
33
+ s.libraryId,
34
+ s.category,
35
+ s.searchQuery,
36
+ s.selectedFileIds,
37
+ s.setSelectedFileIds,
38
+ s.loadMoreKnowledgeItems,
39
+ s.fileListHasMore,
40
+ s.isMasonryReady,
41
+ s.sorter,
42
+ s.sortType,
43
+ ]);
24
44
 
25
- const MasonryView = memo<MasonryViewProps>(
26
- ({ data, hasMore, isMasonryReady, loadMore, onOpenFile, selectFileIds, setSelectedFileIds }) => {
27
- const { t } = useTranslation('file');
28
- const columnCount = useMasonryColumnCount();
29
- const [isLoadingMore, setIsLoadingMore] = useState(false);
45
+ const { t } = useTranslation('file');
46
+ const columnCount = useMasonryColumnCount();
47
+ const [isLoadingMore, setIsLoadingMore] = useState(false);
30
48
 
31
- const libraryId = useResourceManagerStore((s) => s.libraryId);
49
+ const { currentFolderSlug } = useFolderPath();
32
50
 
33
- const masonryContext = useMemo(
34
- () => ({
35
- knowledgeBaseId: libraryId,
36
- openFile: onOpenFile,
37
- selectFileIds,
38
- setSelectedFileIds,
39
- }),
40
- [onOpenFile, libraryId, selectFileIds, setSelectedFileIds],
41
- );
51
+ // Fetch data with SWR
52
+ const { data: rawData } = useResourceManagerFetchKnowledgeItems({
53
+ category,
54
+ knowledgeBaseId: libraryId,
55
+ parentId: currentFolderSlug || null,
56
+ q: searchQuery ?? undefined,
57
+ showFilesInKnowledgeBase: false,
58
+ });
42
59
 
43
- // Handle automatic load more when scrolling to bottom
44
- const handleLoadMore = useCallback(async () => {
45
- if (!hasMore || isLoadingMore) return;
60
+ // Sort data using current sort settings
61
+ const data = sortFileList(rawData, sorter, sortType);
46
62
 
47
- setIsLoadingMore(true);
48
- try {
49
- await loadMore();
50
- } finally {
51
- setIsLoadingMore(false);
52
- }
53
- }, [hasMore, loadMore, isLoadingMore]);
63
+ const masonryContext = useMemo(
64
+ () => ({
65
+ knowledgeBaseId: libraryId,
66
+ selectFileIds: selectedFileIds,
67
+ setSelectedFileIds,
68
+ }),
69
+ [libraryId, selectedFileIds, setSelectedFileIds],
70
+ );
71
+
72
+ // Handle automatic load more when scrolling to bottom
73
+ const handleLoadMore = useCallback(async () => {
74
+ if (!fileListHasMore || isLoadingMore) return;
54
75
 
55
- // Handle scroll event to detect when near bottom
56
- const handleScroll = useCallback(
57
- (e: UIEvent<HTMLDivElement>) => {
58
- const target = e.currentTarget;
59
- const scrollTop = target.scrollTop;
60
- const scrollHeight = target.scrollHeight;
61
- const clientHeight = target.clientHeight;
76
+ setIsLoadingMore(true);
77
+ try {
78
+ await loadMoreKnowledgeItems();
79
+ } finally {
80
+ setIsLoadingMore(false);
81
+ }
82
+ }, [fileListHasMore, loadMoreKnowledgeItems, isLoadingMore]);
62
83
 
63
- // Trigger load when within 300px of bottom
64
- if (scrollHeight - scrollTop - clientHeight < 300) {
65
- handleLoadMore();
66
- }
67
- },
68
- [handleLoadMore],
69
- );
84
+ // Handle scroll event to detect when near bottom
85
+ const handleScroll = useCallback(
86
+ (e: UIEvent<HTMLDivElement>) => {
87
+ const target = e.currentTarget;
88
+ const scrollTop = target.scrollTop;
89
+ const scrollHeight = target.scrollHeight;
90
+ const clientHeight = target.clientHeight;
91
+
92
+ // Trigger load when within 300px of bottom
93
+ if (scrollHeight - scrollTop - clientHeight < 300) {
94
+ handleLoadMore();
95
+ }
96
+ },
97
+ [handleLoadMore],
98
+ );
70
99
 
71
- return (
72
- <div
73
- onScroll={handleScroll}
74
- style={{
75
- flex: 1,
76
- height: '100%',
77
- opacity: isMasonryReady ? 1 : 0,
78
- overflowY: 'auto',
79
- transition: 'opacity 0.2s ease-in-out',
80
- }}
81
- >
82
- <div style={{ paddingBlockEnd: 24, paddingBlockStart: 12, paddingInline: 24 }}>
83
- <VirtuosoMasonry
84
- ItemContent={MasonryItemWrapper}
85
- columnCount={columnCount}
86
- context={masonryContext}
87
- data={data || []}
100
+ return (
101
+ <div
102
+ onScroll={handleScroll}
103
+ style={{
104
+ flex: 1,
105
+ height: '100%',
106
+ opacity: isMasonryReady ? 1 : 0,
107
+ overflowY: 'auto',
108
+ transition: 'opacity 0.2s ease-in-out',
109
+ }}
110
+ >
111
+ <div style={{ paddingBlockEnd: 24, paddingBlockStart: 12, paddingInline: 24 }}>
112
+ <VirtuosoMasonry
113
+ ItemContent={MasonryItemWrapper}
114
+ columnCount={columnCount}
115
+ context={masonryContext}
116
+ data={data || []}
117
+ style={{
118
+ gap: '16px',
119
+ overflow: 'hidden',
120
+ }}
121
+ />
122
+ {isLoadingMore && (
123
+ <Center
88
124
  style={{
89
- gap: '16px',
90
- overflow: 'hidden',
125
+ color: cssVar.colorTextDescription,
126
+ fontSize: 14,
127
+ marginBlockStart: 16,
128
+ minHeight: 40,
91
129
  }}
92
- />
93
- {isLoadingMore && (
94
- <Center
95
- style={{
96
- color: cssVar.colorTextDescription,
97
- fontSize: 14,
98
- marginBlockStart: 16,
99
- minHeight: 40,
100
- }}
101
- >
102
- {t('loading', { defaultValue: 'Loading...' })}
103
- </Center>
104
- )}
105
- </div>
130
+ >
131
+ {t('loading', { defaultValue: 'Loading...' })}
132
+ </Center>
133
+ )}
106
134
  </div>
107
- );
108
- },
109
- );
135
+ </div>
136
+ );
137
+ });
110
138
 
111
139
  export default MasonryView;
@@ -11,6 +11,8 @@ import { memo, useMemo } from 'react';
11
11
  import { useTranslation } from 'react-i18next';
12
12
 
13
13
  import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
14
+ import RepoIcon from '@/components/LibIcon';
15
+ import { useKnowledgeBaseStore } from '@/store/knowledgeBase';
14
16
 
15
17
  import ActionIconWithChevron from './ActionIconWithChevron';
16
18
 
@@ -30,10 +32,18 @@ interface BatchActionsDropdownProps {
30
32
 
31
33
  const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
32
34
  ({ selectCount, onActionClick, disabled }) => {
33
- const { t } = useTranslation(['components', 'common', 'file']);
35
+ const { t } = useTranslation(['components', 'common', 'file', 'knowledgeBase']);
34
36
  const { modal, message } = App.useApp();
35
37
 
36
- const libraryId = useResourceManagerStore((s) => s.libraryId);
38
+ const [libraryId, selectedFileIds] = useResourceManagerStore((s) => [
39
+ s.libraryId,
40
+ s.selectedFileIds,
41
+ ]);
42
+ const [useFetchKnowledgeBaseList, addFilesToKnowledgeBase] = useKnowledgeBaseStore((s) => [
43
+ s.useFetchKnowledgeBaseList,
44
+ s.addFilesToKnowledgeBase,
45
+ ]);
46
+ const { data: knowledgeBases } = useFetchKnowledgeBaseList();
37
47
 
38
48
  const menuItems = useMemo<DropdownItem[]>(() => {
39
49
  const items: DropdownItem[] = [];
@@ -60,44 +70,64 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
60
70
  return items;
61
71
  }
62
72
 
73
+ // Filter out current knowledge base and create submenu items
74
+ const availableKnowledgeBases = (knowledgeBases || []).filter((kb) => kb.id !== libraryId);
75
+
76
+ const addToKnowledgeBaseSubmenu: DropdownItem[] = availableKnowledgeBases.map((kb) => ({
77
+ icon: <RepoIcon />,
78
+ key: `add-to-kb-${kb.id}`,
79
+ label: <span style={{ marginLeft: 8 }}>{kb.name}</span>,
80
+ onClick: async () => {
81
+ try {
82
+ await addFilesToKnowledgeBase(kb.id, selectedFileIds);
83
+ message.success(
84
+ t('addToKnowledgeBase.addSuccess', {
85
+ count: selectCount,
86
+ ns: 'knowledgeBase',
87
+ }),
88
+ );
89
+ } catch (e) {
90
+ console.error(e);
91
+ message.error(t('addToKnowledgeBase.error', { ns: 'knowledgeBase' }));
92
+ }
93
+ },
94
+ }));
95
+
63
96
  if (libraryId) {
64
- items.push(
65
- {
66
- icon: <Icon icon={BookMinusIcon} />,
67
- key: 'removeFromKnowledgeBase',
68
- label: t('FileManager.actions.removeFromKnowledgeBase'),
69
- onClick: () => {
70
- modal.confirm({
71
- okButtonProps: {
72
- danger: true,
73
- },
74
- onOk: async () => {
75
- await onActionClick('removeFromKnowledgeBase');
76
- message.success(t('FileManager.actions.removeFromKnowledgeBaseSuccess'));
77
- },
78
- title: t('FileManager.actions.confirmRemoveFromKnowledgeBase', {
79
- count: selectCount,
80
- }),
81
- });
82
- },
97
+ items.push({
98
+ icon: <Icon icon={BookMinusIcon} />,
99
+ key: 'removeFromKnowledgeBase',
100
+ label: t('FileManager.actions.removeFromKnowledgeBase'),
101
+ onClick: () => {
102
+ modal.confirm({
103
+ okButtonProps: {
104
+ danger: true,
105
+ },
106
+ onOk: async () => {
107
+ await onActionClick('removeFromKnowledgeBase');
108
+ message.success(t('FileManager.actions.removeFromKnowledgeBaseSuccess'));
109
+ },
110
+ title: t('FileManager.actions.confirmRemoveFromKnowledgeBase', {
111
+ count: selectCount,
112
+ }),
113
+ });
83
114
  },
84
- {
115
+ });
116
+
117
+ if (availableKnowledgeBases.length > 0) {
118
+ items.push({
119
+ children: addToKnowledgeBaseSubmenu as any,
85
120
  icon: <Icon icon={BookPlusIcon} />,
86
121
  key: 'addToOtherKnowledgeBase',
87
122
  label: t('FileManager.actions.addToOtherKnowledgeBase'),
88
- onClick: () => {
89
- onActionClick('addToOtherKnowledgeBase');
90
- },
91
- },
92
- );
93
- } else {
123
+ });
124
+ }
125
+ } else if (availableKnowledgeBases.length > 0) {
94
126
  items.push({
127
+ children: addToKnowledgeBaseSubmenu as any,
95
128
  icon: <Icon icon={BookPlusIcon} />,
96
129
  key: 'addToKnowledgeBase',
97
130
  label: t('FileManager.actions.addToKnowledgeBase'),
98
- onClick: () => {
99
- onActionClick('addToKnowledgeBase');
100
- },
101
131
  });
102
132
  }
103
133
 
@@ -134,7 +164,17 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
134
164
  );
135
165
 
136
166
  return items;
137
- }, [libraryId, selectCount, onActionClick, t, modal, message]);
167
+ }, [
168
+ libraryId,
169
+ selectCount,
170
+ selectedFileIds,
171
+ onActionClick,
172
+ addFilesToKnowledgeBase,
173
+ t,
174
+ modal,
175
+ message,
176
+ knowledgeBases,
177
+ ]);
138
178
 
139
179
  return (
140
180
  <DropdownMenu items={menuItems} placement="bottomLeft" triggerProps={{ disabled }}>
@@ -41,10 +41,7 @@ const ResourceExplorer = memo(() => {
41
41
  isTransitioning,
42
42
  isMasonryReady,
43
43
  searchQuery,
44
- selectedFileIds,
45
44
  setSelectedFileIds,
46
- loadMoreKnowledgeItems,
47
- fileListHasMore,
48
45
  sorter,
49
46
  sortType,
50
47
  ] = useResourceManagerStore((s) => [
@@ -54,10 +51,7 @@ const ResourceExplorer = memo(() => {
54
51
  s.isTransitioning,
55
52
  s.isMasonryReady,
56
53
  s.searchQuery,
57
- s.selectedFileIds,
58
54
  s.setSelectedFileIds,
59
- s.loadMoreKnowledgeItems,
60
- s.fileListHasMore,
61
55
  s.sorter,
62
56
  s.sortType,
63
57
  ]);
@@ -114,14 +108,7 @@ const ResourceExplorer = memo(() => {
114
108
  ) : viewMode === 'list' ? (
115
109
  <ListView />
116
110
  ) : (
117
- <MasonryView
118
- data={data}
119
- hasMore={fileListHasMore}
120
- isMasonryReady={isMasonryReady}
121
- loadMore={loadMoreKnowledgeItems}
122
- selectFileIds={selectedFileIds}
123
- setSelectedFileIds={setSelectedFileIds}
124
- />
111
+ <MasonryView />
125
112
  )}
126
113
  </Flexbox>
127
114
  );
@@ -64,10 +64,7 @@ const enabledSSOProviders = parseSSOProviders(authEnv.AUTH_SSO_PROVIDERS);
64
64
  const { socialProviders, genericOAuthProviders } = initBetterAuthSSOProviders();
65
65
 
66
66
  async function customEmailValidator(email: string): Promise<boolean> {
67
- if (ENABLE_BUSINESS_FEATURES && !(await businessEmailValidator(email))) {
68
- return false;
69
- }
70
- return validateEmail(email);
67
+ return ENABLE_BUSINESS_FEATURES ? businessEmailValidator(email) : validateEmail(email);
71
68
  }
72
69
 
73
70
  interface CustomBetterAuthOptions {
@@ -25,7 +25,10 @@ export class UpstashRedisProvider implements BaseRedisProvider {
25
25
  constructor(options: UpstashConfig | RedisConfigNodejs) {
26
26
  const { prefix, ...clientOptions } = options as UpstashConfig & RedisConfigNodejs;
27
27
  this.prefix = prefix ? `${prefix}:` : '';
28
- this.client = new Redis(clientOptions as RedisConfigNodejs);
28
+ this.client = new Redis({
29
+ ...clientOptions,
30
+ automaticDeserialization: false,
31
+ } as RedisConfigNodejs);
29
32
  }
30
33
 
31
34
  /**
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * ComfyUI framework constants configuration
3
- * Unified management of hardcoded values with environment variable overrides / 统一管理硬编码值,支持环境变量覆盖
3
+ * Unified management of hardcoded values with environment variable overrides
4
4
  */
5
5
 
6
6
  /**
7
- * Default configuration / 默认配置
8
- * 注意:BASE_URL不再处理环境变量,由构造函数统一处理优先级
7
+ * Default configuration
8
+ * Note: BASE_URL no longer handles environment variables, priority is uniformly handled by constructor
9
9
  */
10
10
  export const COMFYUI_DEFAULTS = {
11
11
  BASE_URL: 'http://localhost:8000',
@@ -14,8 +14,8 @@ export const COMFYUI_DEFAULTS = {
14
14
  } as const;
15
15
 
16
16
  /**
17
- * FLUX model configuration / FLUX 模型配置
18
- * Removed over-engineered dynamic T5 selection, maintain simple fixed configuration / 移除过度工程化的动态T5选择,保持简单固定配置
17
+ * FLUX model configuration
18
+ * Removed over-engineered dynamic T5 selection, maintain simple fixed configuration
19
19
  */
20
20
  export const FLUX_MODEL_CONFIG = {
21
21
  FILENAME_PREFIXES: {
@@ -40,8 +40,8 @@ export const SD_MODEL_CONFIG = {
40
40
  } as const;
41
41
 
42
42
  /**
43
- * Default workflow node parameters / 工作流节点默认参数
44
- * Based on 2024 community best practices configuration / 基于 2024 年社区最佳实践配置
43
+ * Default workflow node parameters
44
+ * Based on 2024 community best practices configuration
45
45
  */
46
46
 
47
47
  /**
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * Prompt Optimizer Configuration
3
- * 提示词优化器配置 - 用于智能分割和优化提示词
3
+ * Prompt optimizer configuration - for intelligent splitting and optimization of prompts
4
4
  */
5
5
 
6
6
  /**
7
7
  * Style keywords configuration - organized by category
8
- * 风格关键词配置 - 按类别组织便于维护和扩展
8
+ * Style keyword configuration - organized by category for easy maintenance and extension
9
9
  */
10
10
 
11
11
  /* eslint-disable sort-keys-fix/sort-keys-fix */
12
12
  export const STYLE_KEYWORDS = {
13
- // Artists and platforms / 艺术家和平台
13
+ // Artists and platforms
14
14
  ARTISTS: [
15
15
  'by greg rutkowski',
16
16
  'by artgerm',
@@ -34,7 +34,7 @@ export const STYLE_KEYWORDS = {
34
34
  'digital painting',
35
35
  ],
36
36
 
37
- // Art styles / 艺术风格
37
+ // Art styles
38
38
  ART_STYLES: [
39
39
  'photorealistic',
40
40
  'photo realistic',
@@ -90,7 +90,7 @@ export const STYLE_KEYWORDS = {
90
90
  'retrowave',
91
91
  ],
92
92
 
93
- // Lighting effects / 光照效果
93
+ // Lighting effects
94
94
  LIGHTING: [
95
95
  'dramatic lighting',
96
96
  'soft lighting',
@@ -131,7 +131,7 @@ export const STYLE_KEYWORDS = {
131
131
  'incandescent',
132
132
  ],
133
133
 
134
- // Photography terms / 摄影术语
134
+ // Photography terms
135
135
  PHOTOGRAPHY: [
136
136
  'depth of field',
137
137
  'shallow depth of field',
@@ -184,7 +184,7 @@ export const STYLE_KEYWORDS = {
184
184
  'instant photo',
185
185
  ],
186
186
 
187
- // Quality descriptions / 质量描述
187
+ // Quality descriptions
188
188
  QUALITY: [
189
189
  'high quality',
190
190
  'best quality',
@@ -227,7 +227,7 @@ export const STYLE_KEYWORDS = {
227
227
  'exquisite',
228
228
  ],
229
229
 
230
- // Rendering and effects / 渲染和效果
230
+ // Rendering and effects
231
231
  RENDERING: [
232
232
  'octane render',
233
233
  'octane',
@@ -270,7 +270,7 @@ export const STYLE_KEYWORDS = {
270
270
  'high dynamic range',
271
271
  ],
272
272
 
273
- // Color and mood / 颜色和氛围
273
+ // Color and mood
274
274
  COLOR_MOOD: [
275
275
  'vibrant',
276
276
  'vibrant colors',
@@ -330,7 +330,7 @@ export const STYLE_KEYWORDS = {
330
330
  'gothic atmosphere',
331
331
  ],
332
332
 
333
- // Texture and materials / 纹理和材质
333
+ // Texture and materials
334
334
  TEXTURE_MATERIAL: [
335
335
  'glossy',
336
336
  'matte',
@@ -397,7 +397,7 @@ export const STYLE_KEYWORDS = {
397
397
 
398
398
  /**
399
399
  * Style synonyms mapping for better recognition
400
- * 同义词映射,提高识别准确率
400
+ * Synonym mapping to improve recognition accuracy
401
401
  */
402
402
  export const STYLE_SYNONYMS: Record<string, string[]> = {
403
403
  // Photography variations
@@ -443,7 +443,7 @@ export const STYLE_SYNONYMS: Record<string, string[]> = {
443
443
 
444
444
  /**
445
445
  * Compound styles that should be recognized as a whole
446
- * 组合风格,应该作为整体识别
446
+ * Compound styles that should be recognized as a complete unit
447
447
  */
448
448
  export const COMPOUND_STYLES = [
449
449
  // Studio and brand styles
@@ -536,37 +536,37 @@ export const COMPOUND_STYLES = [
536
536
 
537
537
  /**
538
538
  * Precise adjective patterns for style extraction
539
- * 精确的形容词模式,用于风格提取
539
+ * Precise adjective patterns for style extraction
540
540
  */
541
541
  export const STYLE_ADJECTIVE_PATTERNS = {
542
- // Visual quality related / 视觉质量相关
542
+ // Visual quality related
543
543
  quality:
544
544
  /^(sharp|blur(ry)?|clear|crisp|clean|smooth|rough|grainy|noisy|pristine|flawless|perfect|polished)$/i,
545
545
 
546
- // Artistic style related / 艺术风格相关
546
+ // Artistic style related
547
547
  artistic:
548
548
  /^(abstract|surreal|minimal(ist)?|ornate|baroque|gothic|modern|contemporary|traditional|classical|vintage|retro|antique|futuristic|avant-garde)$/i,
549
549
 
550
- // Color and lighting / 颜色和光照
550
+ // Color and lighting
551
551
  visual:
552
552
  /^(bright|dark|dim|vibrant|vivid|muted|saturated|desaturated|warm|cool|cold|hot|soft|hard|harsh|gentle|subtle|bold|pale|rich|deep)$/i,
553
553
 
554
- // Mood and atmosphere / 情绪和氛围
554
+ // Mood and atmosphere
555
555
  mood: /^(dramatic|peaceful|chaotic|serene|calm|mysterious|mystical|magical|epic|legendary|heroic|romantic|melancholic|nostalgic|whimsical|playful|serious|solemn|cheerful|gloomy|ominous|eerie|creepy|scary|dreamy|ethereal|fantastical|moody|atmospheric)$/i,
556
556
 
557
- // Texture and material / 纹理和材质
557
+ // Texture and material
558
558
  texture:
559
559
  /^(metallic|wooden|glass(y)?|crystalline|fabric|leather|plastic|rubber|organic|synthetic|liquid|solid|transparent|translucent|opaque|reflective|matte|glossy|satin|rough|smooth|wet|dry|dusty|rusty|weathered|aged|new|fresh|worn)$/i,
560
560
 
561
- // Size and scale / 尺寸和规模
561
+ // Size and scale
562
562
  scale:
563
563
  /^(tiny|small|medium|large|huge|massive|gigantic|colossal|enormous|microscopic|miniature|oversized|epic-scale|human-scale|intimate|vast|infinite)$/i,
564
564
 
565
- // Complexity and detail / 复杂度和细节
565
+ // Complexity and detail
566
566
  detail:
567
567
  /^(simple|complex|intricate|elaborate|detailed|minimal|advanced|sophisticated|primitive|refined|crude|delicate|robust)$/i,
568
568
 
569
- // Professional quality / 专业质量
569
+ // Professional quality
570
570
  professional:
571
571
  /^(professional|amateur|masterful|skilled|expert|novice|polished|raw|finished|unfinished|complete|incomplete|refined|rough)$/i,
572
572
  } as const;
@@ -575,7 +575,7 @@ export const STYLE_ADJECTIVE_PATTERNS = {
575
575
 
576
576
  /**
577
577
  * Get all style keywords as a flattened array
578
- * 获取所有风格关键词的扁平数组
578
+ * Get all style keywords as a flattened array
579
579
  */
580
580
  export function getAllStyleKeywords(): readonly string[] {
581
581
  return Object.values(STYLE_KEYWORDS).flat();
@@ -583,7 +583,7 @@ export function getAllStyleKeywords(): readonly string[] {
583
583
 
584
584
  /**
585
585
  * Get all compound styles
586
- * 获取所有组合风格
586
+ * Get all compound styles
587
587
  */
588
588
  export function getCompoundStyles(): readonly string[] {
589
589
  return COMPOUND_STYLES;
@@ -591,7 +591,7 @@ export function getCompoundStyles(): readonly string[] {
591
591
 
592
592
  /**
593
593
  * Normalize a style term using synonyms
594
- * 使用同义词标准化风格术语
594
+ * Normalize a style term using synonyms
595
595
  */
596
596
  export function normalizeStyleTerm(term: string): string {
597
597
  const lowerTerm = term.toLowerCase();
@@ -608,7 +608,7 @@ export function normalizeStyleTerm(term: string): string {
608
608
 
609
609
  /**
610
610
  * Check if a word matches any style adjective pattern
611
- * 检查词语是否匹配任何风格形容词模式
611
+ * Check if a word matches any style adjective pattern
612
612
  */
613
613
  export function isStyleAdjective(word: string): boolean {
614
614
  const lowerWord = word.toLowerCase();
@@ -617,7 +617,7 @@ export function isStyleAdjective(word: string): boolean {
617
617
 
618
618
  /**
619
619
  * Extract style adjectives from words based on precise patterns
620
- * 基于精确模式从词语中提取风格形容词
620
+ * Extract style adjectives from words based on precise patterns
621
621
  */
622
622
  export function extractStyleAdjectives(words: string[]): string[] {
623
623
  return words.filter((word) => isStyleAdjective(word));