@lobehub/lobehub 2.0.0-next.235 → 2.0.0-next.237

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 (30) hide show
  1. package/.devcontainer/devcontainer.json +4 -2
  2. package/CHANGELOG.md +58 -0
  3. package/changelog/v1.json +10 -0
  4. package/locales/zh-CN/subscription.json +1 -1
  5. package/package.json +1 -1
  6. package/packages/model-bank/src/aiModels/anthropic.ts +0 -30
  7. package/packages/model-bank/src/aiModels/volcengine.ts +2 -1
  8. package/packages/types/src/user/onboarding.ts +3 -1
  9. package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/Nav.tsx +19 -1
  10. package/src/app/[variants]/(main)/chat/_layout/Sidebar/Header/index.tsx +1 -2
  11. package/src/app/[variants]/(main)/settings/profile/features/KlavisAuthorizationList/index.tsx +6 -24
  12. package/src/app/[variants]/(main)/settings/profile/index.tsx +17 -3
  13. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +1 -7
  14. package/src/app/[variants]/onboarding/features/FullNameStep.tsx +18 -5
  15. package/src/app/[variants]/onboarding/features/InterestsStep.tsx +19 -7
  16. package/src/app/[variants]/onboarding/features/ProSettingsStep.tsx +30 -8
  17. package/src/app/[variants]/onboarding/features/ResponseLanguageStep.tsx +18 -4
  18. package/src/app/[variants]/onboarding/features/TelemetryStep.tsx +14 -5
  19. package/src/app/[variants]/onboarding/index.tsx +2 -1
  20. package/src/server/services/search/impls/exa/index.ts +1 -1
  21. package/src/server/services/search/impls/search1api/index.ts +1 -1
  22. package/src/server/services/search/impls/tavily/index.ts +1 -1
  23. package/src/store/tool/slices/klavisStore/action.test.ts +167 -2
  24. package/src/store/tool/slices/klavisStore/action.ts +9 -8
  25. package/src/store/tool/slices/klavisStore/initialState.ts +3 -0
  26. package/src/store/user/slices/auth/action.ts +1 -0
  27. package/src/store/user/slices/onboarding/action.test.ts +342 -0
  28. package/src/store/user/slices/onboarding/action.ts +4 -9
  29. package/src/store/user/slices/onboarding/selectors.test.ts +222 -0
  30. package/src/store/user/slices/onboarding/selectors.ts +6 -1
@@ -0,0 +1,222 @@
1
+ import { CURRENT_ONBOARDING_VERSION } from '@lobechat/const';
2
+ import { MAX_ONBOARDING_STEPS } from '@lobechat/types';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ import type { UserStore } from '@/store/user';
6
+
7
+ import { initialOnboardingState } from './initialState';
8
+ import { onboardingSelectors } from './selectors';
9
+
10
+ describe('onboardingSelectors', () => {
11
+ describe('currentStep', () => {
12
+ it('should return localOnboardingStep when set', () => {
13
+ const store = {
14
+ ...initialOnboardingState,
15
+ localOnboardingStep: 3,
16
+ onboarding: { currentStep: 1, version: CURRENT_ONBOARDING_VERSION },
17
+ } as unknown as UserStore;
18
+
19
+ expect(onboardingSelectors.currentStep(store)).toBe(3);
20
+ });
21
+
22
+ it('should return onboarding.currentStep when localOnboardingStep is undefined', () => {
23
+ const store = {
24
+ ...initialOnboardingState,
25
+ localOnboardingStep: undefined,
26
+ onboarding: { currentStep: 4, version: CURRENT_ONBOARDING_VERSION },
27
+ } as unknown as UserStore;
28
+
29
+ expect(onboardingSelectors.currentStep(store)).toBe(4);
30
+ });
31
+
32
+ it('should return 1 when both localOnboardingStep and onboarding.currentStep are undefined', () => {
33
+ const store = {
34
+ ...initialOnboardingState,
35
+ localOnboardingStep: undefined,
36
+ onboarding: undefined,
37
+ } as unknown as UserStore;
38
+
39
+ expect(onboardingSelectors.currentStep(store)).toBe(1);
40
+ });
41
+
42
+ it('should clamp step to minimum of 1 when step is less than 1', () => {
43
+ const store = {
44
+ ...initialOnboardingState,
45
+ localOnboardingStep: 0,
46
+ onboarding: undefined,
47
+ } as unknown as UserStore;
48
+
49
+ expect(onboardingSelectors.currentStep(store)).toBe(1);
50
+ });
51
+
52
+ it('should clamp step to minimum of 1 when step is negative', () => {
53
+ const store = {
54
+ ...initialOnboardingState,
55
+ localOnboardingStep: -5,
56
+ onboarding: undefined,
57
+ } as unknown as UserStore;
58
+
59
+ expect(onboardingSelectors.currentStep(store)).toBe(1);
60
+ });
61
+
62
+ it('should clamp step to MAX_ONBOARDING_STEPS when step exceeds maximum', () => {
63
+ const store = {
64
+ ...initialOnboardingState,
65
+ localOnboardingStep: 10,
66
+ onboarding: undefined,
67
+ } as unknown as UserStore;
68
+
69
+ expect(onboardingSelectors.currentStep(store)).toBe(MAX_ONBOARDING_STEPS);
70
+ });
71
+
72
+ it('should clamp server state step to MAX_ONBOARDING_STEPS when it exceeds maximum', () => {
73
+ const store = {
74
+ ...initialOnboardingState,
75
+ localOnboardingStep: undefined,
76
+ onboarding: { currentStep: 29, version: CURRENT_ONBOARDING_VERSION },
77
+ } as unknown as UserStore;
78
+
79
+ expect(onboardingSelectors.currentStep(store)).toBe(MAX_ONBOARDING_STEPS);
80
+ });
81
+
82
+ it('should return exact step when within valid range', () => {
83
+ for (let step = 1; step <= MAX_ONBOARDING_STEPS; step++) {
84
+ const store = {
85
+ ...initialOnboardingState,
86
+ localOnboardingStep: step,
87
+ onboarding: undefined,
88
+ } as unknown as UserStore;
89
+
90
+ expect(onboardingSelectors.currentStep(store)).toBe(step);
91
+ }
92
+ });
93
+ });
94
+
95
+ describe('version', () => {
96
+ it('should return onboarding version', () => {
97
+ const store = {
98
+ ...initialOnboardingState,
99
+ onboarding: { version: 2 },
100
+ } as unknown as UserStore;
101
+
102
+ expect(onboardingSelectors.version(store)).toBe(2);
103
+ });
104
+
105
+ it('should return CURRENT_ONBOARDING_VERSION when onboarding is undefined', () => {
106
+ const store = {
107
+ ...initialOnboardingState,
108
+ onboarding: undefined,
109
+ } as unknown as UserStore;
110
+
111
+ expect(onboardingSelectors.version(store)).toBe(CURRENT_ONBOARDING_VERSION);
112
+ });
113
+ });
114
+
115
+ describe('finishedAt', () => {
116
+ it('should return finishedAt when set', () => {
117
+ const finishedAt = '2024-01-01T00:00:00Z';
118
+ const store = {
119
+ ...initialOnboardingState,
120
+ onboarding: { finishedAt, version: CURRENT_ONBOARDING_VERSION },
121
+ } as unknown as UserStore;
122
+
123
+ expect(onboardingSelectors.finishedAt(store)).toBe(finishedAt);
124
+ });
125
+
126
+ it('should return undefined when onboarding is undefined', () => {
127
+ const store = {
128
+ ...initialOnboardingState,
129
+ onboarding: undefined,
130
+ } as unknown as UserStore;
131
+
132
+ expect(onboardingSelectors.finishedAt(store)).toBeUndefined();
133
+ });
134
+ });
135
+
136
+ describe('isFinished', () => {
137
+ it('should return true when finishedAt is set', () => {
138
+ const store = {
139
+ ...initialOnboardingState,
140
+ onboarding: { finishedAt: '2024-01-01T00:00:00Z', version: CURRENT_ONBOARDING_VERSION },
141
+ } as unknown as UserStore;
142
+
143
+ expect(onboardingSelectors.isFinished(store)).toBe(true);
144
+ });
145
+
146
+ it('should return false when finishedAt is undefined', () => {
147
+ const store = {
148
+ ...initialOnboardingState,
149
+ onboarding: { version: CURRENT_ONBOARDING_VERSION },
150
+ } as unknown as UserStore;
151
+
152
+ expect(onboardingSelectors.isFinished(store)).toBe(false);
153
+ });
154
+
155
+ it('should return false when onboarding is undefined', () => {
156
+ const store = {
157
+ ...initialOnboardingState,
158
+ onboarding: undefined,
159
+ } as unknown as UserStore;
160
+
161
+ expect(onboardingSelectors.isFinished(store)).toBe(false);
162
+ });
163
+ });
164
+
165
+ describe('needsOnboarding', () => {
166
+ it('should return true when finishedAt is not set', () => {
167
+ const store = {
168
+ onboarding: { version: CURRENT_ONBOARDING_VERSION },
169
+ } as Pick<UserStore, 'onboarding'>;
170
+
171
+ expect(onboardingSelectors.needsOnboarding(store)).toBe(true);
172
+ });
173
+
174
+ it('should return true when version is older than current', () => {
175
+ // If CURRENT_ONBOARDING_VERSION > 1, test with version 1
176
+ // Otherwise, this test is not applicable since there's no valid older version
177
+ if (CURRENT_ONBOARDING_VERSION > 1) {
178
+ const store = {
179
+ onboarding: {
180
+ finishedAt: '2024-01-01T00:00:00Z',
181
+ version: 1,
182
+ },
183
+ } as Pick<UserStore, 'onboarding'>;
184
+
185
+ expect(onboardingSelectors.needsOnboarding(store)).toBe(true);
186
+ } else {
187
+ // When CURRENT_ONBOARDING_VERSION is 1, there's no valid older version (0 is falsy)
188
+ // Test that version 0 is treated as NOT needing onboarding due to falsy check
189
+ const store = {
190
+ onboarding: {
191
+ finishedAt: '2024-01-01T00:00:00Z',
192
+ version: 0,
193
+ },
194
+ } as Pick<UserStore, 'onboarding'>;
195
+
196
+ // version 0 is falsy, so the condition (version && version < CURRENT) short-circuits to 0 (falsy)
197
+ // finishedAt is set, so the first condition is false
198
+ // The result is falsy (0), not strictly false
199
+ expect(onboardingSelectors.needsOnboarding(store)).toBeFalsy();
200
+ }
201
+ });
202
+
203
+ it('should return false when finishedAt is set and version is current', () => {
204
+ const store = {
205
+ onboarding: {
206
+ finishedAt: '2024-01-01T00:00:00Z',
207
+ version: CURRENT_ONBOARDING_VERSION,
208
+ },
209
+ } as Pick<UserStore, 'onboarding'>;
210
+
211
+ expect(onboardingSelectors.needsOnboarding(store)).toBe(false);
212
+ });
213
+
214
+ it('should return true when onboarding is undefined', () => {
215
+ const store = {
216
+ onboarding: undefined,
217
+ } as Pick<UserStore, 'onboarding'>;
218
+
219
+ expect(onboardingSelectors.needsOnboarding(store)).toBe(true);
220
+ });
221
+ });
222
+ });
@@ -1,12 +1,17 @@
1
1
  import { CURRENT_ONBOARDING_VERSION } from '@lobechat/const';
2
+ import { MAX_ONBOARDING_STEPS } from '@lobechat/types';
2
3
 
3
4
  import type { UserStore } from '../../store';
4
5
 
5
6
  /**
6
7
  * Returns the current step for UI display.
7
8
  * Prioritizes local optimistic state over server state for immediate feedback.
9
+ * Clamps the value to valid range [1, MAX_ONBOARDING_STEPS].
8
10
  */
9
- const currentStep = (s: UserStore) => s.localOnboardingStep ?? s.onboarding?.currentStep ?? 1;
11
+ const currentStep = (s: UserStore) => {
12
+ const step = s.localOnboardingStep ?? s.onboarding?.currentStep ?? 1;
13
+ return Math.max(1, Math.min(step, MAX_ONBOARDING_STEPS));
14
+ };
10
15
 
11
16
  const version = (s: UserStore) => s.onboarding?.version ?? CURRENT_ONBOARDING_VERSION;
12
17