@lobehub/chat 1.133.6 → 1.134.0

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 (24) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/apps/desktop/src/main/appBrowsers.ts +51 -0
  3. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +72 -1
  4. package/apps/desktop/src/main/core/browser/BrowserManager.ts +88 -18
  5. package/changelog/v1.json +9 -0
  6. package/package.json +1 -1
  7. package/packages/electron-client-ipc/src/events/windows.ts +39 -0
  8. package/src/app/[variants]/(main)/_layout/Desktop/DesktopLayoutContainer.tsx +4 -2
  9. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/index.tsx +3 -1
  10. package/src/app/[variants]/(main)/_layout/Desktop/index.tsx +3 -1
  11. package/src/app/[variants]/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicItem/TopicContent.tsx +25 -1
  12. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/Actions.tsx +19 -2
  13. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +27 -1
  14. package/src/app/[variants]/(main)/chat/_layout/Desktop/SessionPanel.tsx +11 -1
  15. package/src/app/[variants]/(main)/chat/features/TogglePanelButton.tsx +6 -0
  16. package/src/config/featureFlags/index.ts +2 -2
  17. package/src/config/featureFlags/schema.test.ts +165 -9
  18. package/src/config/featureFlags/schema.ts +68 -46
  19. package/src/features/ElectronTitlebar/Connection/index.tsx +0 -1
  20. package/src/hooks/useIsSingleMode.test.ts +66 -0
  21. package/src/hooks/useIsSingleMode.ts +29 -0
  22. package/src/server/featureFlags/index.ts +56 -0
  23. package/src/server/modules/EdgeConfig/index.ts +43 -4
  24. package/src/store/global/actions/general.ts +46 -0
@@ -0,0 +1,56 @@
1
+ import createDebug from 'debug';
2
+
3
+ import {
4
+ DEFAULT_FEATURE_FLAGS,
5
+ getServerFeatureFlagsValue,
6
+ mapFeatureFlagsEnvToState,
7
+ } from '@/config/featureFlags';
8
+ import { merge } from '@/utils/merge';
9
+
10
+ import { EdgeConfig } from '../modules/EdgeConfig';
11
+
12
+ const debug = createDebug('lobe:featureFlags');
13
+
14
+ /**
15
+ * Get feature flags from EdgeConfig with fallback to environment variables
16
+ * @param userId - Optional user ID for user-specific feature flag evaluation
17
+ */
18
+ export const getServerFeatureFlagsFromEdgeConfig = async (userId?: string) => {
19
+ // Try to get feature flags from EdgeConfig first
20
+ if (EdgeConfig.isEnabled()) {
21
+ try {
22
+ const edgeConfig = new EdgeConfig();
23
+ const edgeFeatureFlags = await edgeConfig.getFeatureFlags();
24
+
25
+ if (edgeFeatureFlags && Object.keys(edgeFeatureFlags).length > 0) {
26
+ // Merge EdgeConfig flags with defaults
27
+ const mergedFlags = merge(DEFAULT_FEATURE_FLAGS, edgeFeatureFlags);
28
+ debug('Using EdgeConfig flags for user: %s', userId || 'anonymous');
29
+ return mergedFlags;
30
+ } else {
31
+ debug('EdgeConfig returned empty/null/undefined, falling back to environment variables');
32
+ }
33
+ } catch (error) {
34
+ console.error(
35
+ '[FeatureFlags] Failed to fetch feature flags from EdgeConfig, falling back to environment variables:',
36
+ error,
37
+ );
38
+ }
39
+ } else {
40
+ debug('EdgeConfig not enabled, using environment variables');
41
+ }
42
+
43
+ // Fallback to environment variable-based feature flags
44
+ const envFlags = getServerFeatureFlagsValue();
45
+ debug('Using environment variable flags for user: %s', userId || 'anonymous');
46
+ return envFlags;
47
+ };
48
+
49
+ /**
50
+ * Get server feature flags from EdgeConfig and map them to state with user ID
51
+ * @param userId - Optional user ID for user-specific feature flag evaluation
52
+ */
53
+ export const getServerFeatureFlagsStateFromEdgeConfig = async (userId?: string) => {
54
+ const flags = await getServerFeatureFlagsFromEdgeConfig(userId);
55
+ return mapFeatureFlagsEnvToState(flags, userId);
56
+ };
@@ -1,8 +1,24 @@
1
1
  import { EdgeConfigClient, createClient } from '@vercel/edge-config';
2
+ import createDebug from 'debug';
2
3
 
3
4
  import { appEnv } from '@/envs/app';
4
5
 
5
- import type { EdgeConfigData } from './types';
6
+ const debug = createDebug('lobe-server:edge-config');
7
+
8
+ const EdgeConfigKeys = {
9
+ /**
10
+ * Assistant whitelist
11
+ */
12
+ AssistantBlacklist: 'assistant_blacklist',
13
+ /**
14
+ * Assistant whitelist
15
+ */
16
+ AssistantWhitelist: 'assistant_whitelist',
17
+ /**
18
+ * Feature flags configuration
19
+ */
20
+ FeatureFlags: 'feature_flags',
21
+ };
6
22
 
7
23
  export class EdgeConfig {
8
24
  get client(): EdgeConfigClient {
@@ -16,12 +32,35 @@ export class EdgeConfig {
16
32
  * Check if Edge Config is enabled
17
33
  */
18
34
  static isEnabled() {
19
- return !!appEnv.VERCEL_EDGE_CONFIG;
35
+ const isEnabled = !!appEnv.VERCEL_EDGE_CONFIG;
36
+ debug('VERCEL_EDGE_CONFIG env var: %s', appEnv.VERCEL_EDGE_CONFIG ? 'SET' : 'NOT SET');
37
+ debug('EdgeConfig enabled: %s', isEnabled);
38
+ return isEnabled;
20
39
  }
21
40
 
22
41
  getAgentRestrictions = async () => {
23
42
  const { assistant_blacklist: blacklist, assistant_whitelist: whitelist } =
24
- await this.client.getAll<EdgeConfigData>(['assistant_whitelist', 'assistant_blacklist']);
25
- return { blacklist, whitelist };
43
+ await this.client.getAll([
44
+ EdgeConfigKeys.AssistantWhitelist,
45
+ EdgeConfigKeys.AssistantBlacklist,
46
+ ]);
47
+
48
+ return { blacklist, whitelist } as {
49
+ blacklist: string[] | undefined;
50
+ whitelist: string[] | undefined;
51
+ };
52
+ };
53
+
54
+ getFlagByKey = async (key: string) => {
55
+ const value = await this.client.get(key);
56
+ return value;
57
+ };
58
+
59
+ getFeatureFlags = async () => {
60
+ const featureFlags = await this.client.get(EdgeConfigKeys.FeatureFlags);
61
+ debug('Feature flags retrieved: %O', featureFlags);
62
+ return featureFlags as Record<string, boolean | string[]> | undefined;
26
63
  };
27
64
  }
65
+
66
+ export { EdgeConfigKeys };
@@ -20,6 +20,8 @@ import type { GlobalStore } from '../store';
20
20
  const n = setNamespace('g');
21
21
 
22
22
  export interface GlobalGeneralAction {
23
+ openSessionInNewWindow: (sessionId: string) => Promise<void>;
24
+ openTopicInNewWindow: (sessionId: string, topicId: string) => Promise<void>;
23
25
  switchLocale: (locale: LocaleMode) => void;
24
26
  switchThemeMode: (themeMode: ThemeMode, params?: { skipBroadcast?: boolean }) => void;
25
27
  updateSystemStatus: (status: Partial<SystemStatus>, action?: any) => void;
@@ -33,6 +35,50 @@ export const generalActionSlice: StateCreator<
33
35
  [],
34
36
  GlobalGeneralAction
35
37
  > = (set, get) => ({
38
+ openSessionInNewWindow: async (sessionId: string) => {
39
+ if (!isDesktop) return;
40
+
41
+ try {
42
+ const { dispatch } = await import('@lobechat/electron-client-ipc');
43
+
44
+ const url = `/chat?session=${sessionId}&mode=single`;
45
+
46
+ const result = await dispatch('createMultiInstanceWindow', {
47
+ path: url,
48
+ templateId: 'chatSingle',
49
+ uniqueId: `chat_${sessionId}`,
50
+ });
51
+
52
+ if (!result.success) {
53
+ console.error('Failed to open session in new window:', result.error);
54
+ }
55
+ } catch (error) {
56
+ console.error('Error opening session in new window:', error);
57
+ }
58
+ },
59
+
60
+ openTopicInNewWindow: async (sessionId: string, topicId: string) => {
61
+ if (!isDesktop) return;
62
+
63
+ try {
64
+ const { dispatch } = await import('@lobechat/electron-client-ipc');
65
+
66
+ const url = `/chat?session=${sessionId}&topic=${topicId}&mode=single`;
67
+
68
+ const result = await dispatch('createMultiInstanceWindow', {
69
+ path: url,
70
+ templateId: 'chatSingle',
71
+ uniqueId: `chat_${sessionId}_${topicId}`,
72
+ });
73
+
74
+ if (!result.success) {
75
+ console.error('Failed to open topic in new window:', result.error);
76
+ }
77
+ } catch (error) {
78
+ console.error('Error opening topic in new window:', error);
79
+ }
80
+ },
81
+
36
82
  switchLocale: (locale) => {
37
83
  get().updateSystemStatus({ language: locale });
38
84