@carlonicora/nextjs-jsonapi 1.108.0 → 1.110.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.
- package/dist/{AssistantInterface-BYgI5z1-.d.mts → AssistantInterface-B1c8FhGA.d.mts} +2 -0
- package/dist/{AssistantInterface-DfDcz0gJ.d.ts → AssistantInterface-BBUHxOCd.d.ts} +2 -0
- package/dist/{AssistantMessageInterface-BpEhx2pC.d.ts → AssistantMessageInterface-BNzf1etU.d.ts} +16 -1
- package/dist/{AssistantMessageInterface-DJ3Me16Y.d.mts → AssistantMessageInterface-DrgHDjZ5.d.mts} +16 -1
- package/dist/{AuthComponent-B6DIk8Vf.d.ts → AuthComponent-Cd7lcYif.d.ts} +1 -1
- package/dist/{AuthComponent-BKI0ZbtD.d.mts → AuthComponent-DdxCFgUZ.d.mts} +1 -1
- package/dist/{BlockNoteEditor-2AXSTGGG.js → BlockNoteEditor-7OKW6HHN.js} +19 -19
- package/dist/{BlockNoteEditor-2AXSTGGG.js.map → BlockNoteEditor-7OKW6HHN.js.map} +1 -1
- package/dist/{BlockNoteEditor-XVIBGXHF.mjs → BlockNoteEditor-K7IIWM3S.mjs} +4 -4
- package/dist/BlockNoteViewer-OPSCTHKJ.mjs +31 -0
- package/dist/BlockNoteViewer-OPSCTHKJ.mjs.map +1 -0
- package/dist/BlockNoteViewer-UW5VZ5UF.js +31 -0
- package/dist/BlockNoteViewer-UW5VZ5UF.js.map +1 -0
- package/dist/{auth.interface-BBUgMZzs.d.ts → auth.interface-8b601idJ.d.ts} +1 -1
- package/dist/{auth.interface-XYEREOD6.d.mts → auth.interface-CXBF8Mhi.d.mts} +1 -1
- package/dist/billing/index.d.mts +41 -41
- package/dist/billing/index.d.ts +41 -41
- package/dist/billing/index.js +346 -346
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-VLDLERJN.js → chunk-6XWW67F2.js} +29 -9
- package/dist/chunk-6XWW67F2.js.map +1 -0
- package/dist/{chunk-HC3JFN3C.js → chunk-AKXUNE3A.js} +1004 -862
- package/dist/chunk-AKXUNE3A.js.map +1 -0
- package/dist/{chunk-CFECWLHH.mjs → chunk-CWVYU4YZ.mjs} +23 -3
- package/dist/chunk-CWVYU4YZ.mjs.map +1 -0
- package/dist/{chunk-56XBGQGU.mjs → chunk-TXVVZLUQ.mjs} +1422 -1280
- package/dist/chunk-TXVVZLUQ.mjs.map +1 -0
- package/dist/{chunk-RXXZGPC3.js → chunk-U2HR57MY.js} +265 -113
- package/dist/chunk-U2HR57MY.js.map +1 -0
- package/dist/chunk-WPAQRB5Q.mjs +7 -0
- package/dist/chunk-WPAQRB5Q.mjs.map +1 -0
- package/dist/chunk-YBZVWLPH.js +7 -0
- package/dist/chunk-YBZVWLPH.js.map +1 -0
- package/dist/{chunk-N3NVIPSU.mjs → chunk-ZYPNVEHB.mjs} +235 -83
- package/dist/{chunk-N3NVIPSU.mjs.map → chunk-ZYPNVEHB.mjs.map} +1 -1
- package/dist/client/index.d.mts +9 -18
- package/dist/client/index.d.ts +9 -18
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +421 -417
- package/dist/components/index.d.ts +421 -417
- package/dist/components/index.js +6 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +5 -3
- package/dist/{config-CLQynoaa.d.ts → config-BrkU_Ynz.d.ts} +4 -1
- package/dist/{config-k61pe_o2.d.mts → config-snd2ToEB.d.mts} +4 -1
- package/dist/contexts/index.d.mts +31 -20
- package/dist/contexts/index.d.ts +31 -20
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +75 -11
- package/dist/core/index.d.ts +75 -11
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/features/help/index.css +29 -0
- package/dist/features/help/index.css.map +1 -0
- package/dist/features/help/index.d.mts +74 -0
- package/dist/features/help/index.d.ts +74 -0
- package/dist/features/help/index.js +522 -0
- package/dist/features/help/index.js.map +1 -0
- package/dist/features/help/index.mjs +522 -0
- package/dist/features/help/index.mjs.map +1 -0
- package/dist/features/help/server.d.mts +33 -0
- package/dist/features/help/server.d.ts +33 -0
- package/dist/features/help/server.js +8 -0
- package/dist/features/help/server.js.map +1 -0
- package/dist/features/help/server.mjs +8 -0
- package/dist/features/help/server.mjs.map +1 -0
- package/dist/help-content-config.interface-C5ESIod9.d.mts +13 -0
- package/dist/help-content-config.interface-C5ESIod9.d.ts +13 -0
- package/dist/index.d.mts +10 -8
- package/dist/index.d.ts +10 -8
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{notification.interface-aLEJbA_g.d.ts → notification.interface-C1T1C2ee.d.ts} +1 -100
- package/dist/{notification.interface-DLZGtV7Z.d.mts → notification.interface-DIxR23eS.d.mts} +1 -100
- package/dist/{s3.service-CVgLWaDc.d.mts → s3.service-0BTClOYO.d.mts} +2 -2
- package/dist/{s3.service-SLlX0Zbz.d.ts → s3.service-CT27Fm1s.d.ts} +2 -2
- package/dist/server/index.d.mts +4 -3
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/testing/index.d.mts +1 -2
- package/dist/testing/index.d.ts +1 -2
- package/dist/types-CQSjy7et.d.mts +101 -0
- package/dist/types-DHOxe8rc.d.ts +101 -0
- package/dist/usePageUrlGenerator-tjq2mlDV.d.ts +14 -0
- package/dist/usePageUrlGenerator-uOnyJ6j2.d.mts +14 -0
- package/dist/{useSocket-BkxHHujj.d.mts → useSocket-B1fMIr17.d.mts} +1 -1
- package/dist/{useSocket-CMDjWFYm.d.ts → useSocket-BdJTBXKv.d.ts} +1 -1
- package/package.json +15 -1
- package/src/client/config.ts +9 -1
- package/src/components/index.ts +1 -0
- package/src/components/viewers/BlockNoteViewer.tsx +27 -0
- package/src/components/viewers/BlockNoteViewerContainer.tsx +10 -0
- package/src/core/registry/helpStore.ts +28 -0
- package/src/features/assistant/contexts/AssistantContext.tsx +35 -19
- package/src/features/assistant/contexts/__tests__/AssistantContext.spec.tsx +66 -6
- package/src/features/assistant/data/Assistant.ts +2 -0
- package/src/features/assistant/data/AssistantInterface.ts +2 -0
- package/src/features/assistant/data/AssistantService.ts +18 -8
- package/src/features/assistant-message/components/parts/MessageSourcesContainer.tsx +4 -0
- package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +6 -4
- package/src/features/assistant-message/components/parts/tabs/ContentsTab.tsx +5 -1
- package/src/features/assistant-message/components/parts/tabs/ReferencesTab.tsx +3 -1
- package/src/features/assistant-message/data/AssistantMessage.ts +27 -1
- package/src/features/assistant-message/data/AssistantMessageInterface.ts +1 -0
- package/src/features/assistant-message/data/__tests__/AssistantMessage.spec.ts +7 -3
- package/src/features/help/components/HelpArticleBody.tsx +62 -0
- package/src/features/help/components/HelpAskAi.tsx +36 -0
- package/src/features/help/components/HelpAssistantSheet.tsx +53 -0
- package/src/features/help/components/HelpHeader.tsx +40 -0
- package/src/features/help/components/HelpHint.tsx +86 -0
- package/src/features/help/components/HelpSearchResultRow.tsx +61 -0
- package/src/features/help/components/HelpSideNav.tsx +119 -0
- package/src/features/help/components/HelpTOC.tsx +49 -0
- package/src/features/help/components/__tests__/HelpAskAi.spec.tsx +68 -0
- package/src/features/help/components/__tests__/HelpAssistantSheet.spec.tsx +36 -0
- package/src/features/help/components/__tests__/HelpHint.spec.tsx +50 -0
- package/src/features/help/components/__tests__/HelpSearchResultRow.spec.tsx +58 -0
- package/src/features/help/components/__tests__/HelpSideNav.spec.tsx +50 -0
- package/src/features/help/contexts/HelpContext.spec.tsx +26 -0
- package/src/features/help/contexts/HelpContext.tsx +24 -0
- package/src/features/help/hooks/useHelp.ts +1 -0
- package/src/features/help/hooks/useHelpFilter.ts +27 -0
- package/src/features/help/i18n-keys.ts +34 -0
- package/src/features/help/index.ts +24 -0
- package/src/features/help/interfaces/help-content-config.interface.ts +12 -0
- package/src/features/help/server-entry.ts +11 -0
- package/src/features/help/types/help-article.types.ts +35 -0
- package/src/features/help/utils/articleUrl.ts +13 -0
- package/src/features/how-to/HowToModule.ts +14 -1
- package/src/features/how-to/components/forms/HowToEditor.tsx +108 -4
- package/src/features/how-to/data/HowTo.spec.ts +43 -0
- package/src/features/how-to/data/HowTo.ts +64 -3
- package/src/features/how-to/data/HowToInterface.ts +14 -0
- package/src/features/how-to/data/HowToService.ts +61 -0
- package/src/index.ts +4 -0
- package/dist/chunk-56XBGQGU.mjs.map +0 -1
- package/dist/chunk-CFECWLHH.mjs.map +0 -1
- package/dist/chunk-HC3JFN3C.js.map +0 -1
- package/dist/chunk-RXXZGPC3.js.map +0 -1
- package/dist/chunk-VLDLERJN.js.map +0 -1
- /package/dist/{BlockNoteEditor-XVIBGXHF.mjs.map → BlockNoteEditor-K7IIWM3S.mjs.map} +0 -0
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import React__default, { ReactElement } from 'react';
|
|
3
2
|
import { J as JsonApiConfig } from '../JsonApiContext-Bsm_Q2oe.js';
|
|
4
3
|
import { A as ApiRequestDataTypeInterface } from '../ApiRequestDataTypeInterface-CYEcRUrh.js';
|
|
@@ -37,7 +36,7 @@ declare const defaultMockConfig: JsonApiConfig;
|
|
|
37
36
|
* );
|
|
38
37
|
* ```
|
|
39
38
|
*/
|
|
40
|
-
declare function MockJsonApiProvider({ children, config }: MockJsonApiProviderProps):
|
|
39
|
+
declare function MockJsonApiProvider({ children, config }: MockJsonApiProviderProps): React__default.JSX.Element;
|
|
41
40
|
|
|
42
41
|
interface CreateMockModuleOptions {
|
|
43
42
|
name: string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { LucideIcon } from 'lucide-react';
|
|
2
|
+
import { A as ApiRequestDataTypeInterface, F as FieldSelector } from './ApiRequestDataTypeInterface-CYEcRUrh.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Permission actions
|
|
6
|
+
*/
|
|
7
|
+
declare enum Action {
|
|
8
|
+
Read = "read",
|
|
9
|
+
Create = "create",
|
|
10
|
+
Update = "update",
|
|
11
|
+
Delete = "delete"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generic permission check type.
|
|
15
|
+
* Can be a boolean or a function that checks permissions dynamically.
|
|
16
|
+
* @template T - The data type being checked
|
|
17
|
+
* @template U - The user type (defaults to PermissionUser)
|
|
18
|
+
*/
|
|
19
|
+
type PermissionCheck<T, U = PermissionUser> = boolean | ((user?: U | string, data?: T) => boolean);
|
|
20
|
+
/**
|
|
21
|
+
* Page URL configuration for modules
|
|
22
|
+
*/
|
|
23
|
+
type PageUrl = {
|
|
24
|
+
pageUrl?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Module permission definition wrapper
|
|
28
|
+
*/
|
|
29
|
+
type ModulePermissionDefinition<T> = {
|
|
30
|
+
interface: T;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Base module definition
|
|
34
|
+
*/
|
|
35
|
+
type ModuleDefinition = {
|
|
36
|
+
pageUrl?: string;
|
|
37
|
+
name: string;
|
|
38
|
+
model: any;
|
|
39
|
+
feature?: string;
|
|
40
|
+
moduleId?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Permission configuration for a module.
|
|
44
|
+
* Can be a boolean (allow/deny all) or a string path for dynamic checks.
|
|
45
|
+
*/
|
|
46
|
+
interface PermissionConfig {
|
|
47
|
+
create: boolean | string;
|
|
48
|
+
read: boolean | string;
|
|
49
|
+
update: boolean | string;
|
|
50
|
+
delete: boolean | string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generic interface for a module that has permissions.
|
|
54
|
+
* Apps should ensure their Module class implements this.
|
|
55
|
+
*/
|
|
56
|
+
interface PermissionModule {
|
|
57
|
+
id: string;
|
|
58
|
+
permissions: PermissionConfig;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generic interface for a user that has modules with permissions.
|
|
62
|
+
* Apps should ensure their User class implements this.
|
|
63
|
+
*/
|
|
64
|
+
interface PermissionUser {
|
|
65
|
+
id: string;
|
|
66
|
+
modules: PermissionModule[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Module definition with permissions - extends ApiRequestDataTypeInterface
|
|
70
|
+
*/
|
|
71
|
+
type ModuleWithPermissions = ApiRequestDataTypeInterface & {
|
|
72
|
+
pageUrl?: string;
|
|
73
|
+
feature?: string;
|
|
74
|
+
moduleId?: string;
|
|
75
|
+
icon?: LucideIcon;
|
|
76
|
+
inclusions?: Record<string, {
|
|
77
|
+
types?: string[];
|
|
78
|
+
fields?: FieldSelector<any>[];
|
|
79
|
+
include?: string[];
|
|
80
|
+
}>;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Factory type for creating module definitions
|
|
84
|
+
*/
|
|
85
|
+
type ModuleFactory = (params: {
|
|
86
|
+
pageUrl?: string;
|
|
87
|
+
name: string;
|
|
88
|
+
cache?: string | "days" | "default" | "hours" | "max" | "minutes" | "seconds" | "weeks";
|
|
89
|
+
model: any;
|
|
90
|
+
feature?: string;
|
|
91
|
+
moduleId?: string;
|
|
92
|
+
icon?: LucideIcon;
|
|
93
|
+
identifier?: string[];
|
|
94
|
+
inclusions?: Record<string, {
|
|
95
|
+
types?: string[];
|
|
96
|
+
fields?: FieldSelector<any>[];
|
|
97
|
+
include?: string[];
|
|
98
|
+
}>;
|
|
99
|
+
}) => ModuleWithPermissions;
|
|
100
|
+
|
|
101
|
+
export { Action as A, type ModuleWithPermissions as M, type PageUrl as P, type PermissionCheck as a, type ModulePermissionDefinition as b, type ModuleDefinition as c, type PermissionConfig as d, type PermissionModule as e, type PermissionUser as f, type ModuleFactory as g };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { LucideIcon } from 'lucide-react';
|
|
2
|
+
import { A as ApiRequestDataTypeInterface, F as FieldSelector } from './ApiRequestDataTypeInterface-CYEcRUrh.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Permission actions
|
|
6
|
+
*/
|
|
7
|
+
declare enum Action {
|
|
8
|
+
Read = "read",
|
|
9
|
+
Create = "create",
|
|
10
|
+
Update = "update",
|
|
11
|
+
Delete = "delete"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generic permission check type.
|
|
15
|
+
* Can be a boolean or a function that checks permissions dynamically.
|
|
16
|
+
* @template T - The data type being checked
|
|
17
|
+
* @template U - The user type (defaults to PermissionUser)
|
|
18
|
+
*/
|
|
19
|
+
type PermissionCheck<T, U = PermissionUser> = boolean | ((user?: U | string, data?: T) => boolean);
|
|
20
|
+
/**
|
|
21
|
+
* Page URL configuration for modules
|
|
22
|
+
*/
|
|
23
|
+
type PageUrl = {
|
|
24
|
+
pageUrl?: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Module permission definition wrapper
|
|
28
|
+
*/
|
|
29
|
+
type ModulePermissionDefinition<T> = {
|
|
30
|
+
interface: T;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Base module definition
|
|
34
|
+
*/
|
|
35
|
+
type ModuleDefinition = {
|
|
36
|
+
pageUrl?: string;
|
|
37
|
+
name: string;
|
|
38
|
+
model: any;
|
|
39
|
+
feature?: string;
|
|
40
|
+
moduleId?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Permission configuration for a module.
|
|
44
|
+
* Can be a boolean (allow/deny all) or a string path for dynamic checks.
|
|
45
|
+
*/
|
|
46
|
+
interface PermissionConfig {
|
|
47
|
+
create: boolean | string;
|
|
48
|
+
read: boolean | string;
|
|
49
|
+
update: boolean | string;
|
|
50
|
+
delete: boolean | string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generic interface for a module that has permissions.
|
|
54
|
+
* Apps should ensure their Module class implements this.
|
|
55
|
+
*/
|
|
56
|
+
interface PermissionModule {
|
|
57
|
+
id: string;
|
|
58
|
+
permissions: PermissionConfig;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generic interface for a user that has modules with permissions.
|
|
62
|
+
* Apps should ensure their User class implements this.
|
|
63
|
+
*/
|
|
64
|
+
interface PermissionUser {
|
|
65
|
+
id: string;
|
|
66
|
+
modules: PermissionModule[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Module definition with permissions - extends ApiRequestDataTypeInterface
|
|
70
|
+
*/
|
|
71
|
+
type ModuleWithPermissions = ApiRequestDataTypeInterface & {
|
|
72
|
+
pageUrl?: string;
|
|
73
|
+
feature?: string;
|
|
74
|
+
moduleId?: string;
|
|
75
|
+
icon?: LucideIcon;
|
|
76
|
+
inclusions?: Record<string, {
|
|
77
|
+
types?: string[];
|
|
78
|
+
fields?: FieldSelector<any>[];
|
|
79
|
+
include?: string[];
|
|
80
|
+
}>;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Factory type for creating module definitions
|
|
84
|
+
*/
|
|
85
|
+
type ModuleFactory = (params: {
|
|
86
|
+
pageUrl?: string;
|
|
87
|
+
name: string;
|
|
88
|
+
cache?: string | "days" | "default" | "hours" | "max" | "minutes" | "seconds" | "weeks";
|
|
89
|
+
model: any;
|
|
90
|
+
feature?: string;
|
|
91
|
+
moduleId?: string;
|
|
92
|
+
icon?: LucideIcon;
|
|
93
|
+
identifier?: string[];
|
|
94
|
+
inclusions?: Record<string, {
|
|
95
|
+
types?: string[];
|
|
96
|
+
fields?: FieldSelector<any>[];
|
|
97
|
+
include?: string[];
|
|
98
|
+
}>;
|
|
99
|
+
}) => ModuleWithPermissions;
|
|
100
|
+
|
|
101
|
+
export { Action as A, type ModuleWithPermissions as M, type PageUrl as P, type PermissionCheck as a, type ModulePermissionDefinition as b, type ModuleDefinition as c, type PermissionConfig as d, type PermissionModule as e, type PermissionUser as f, type ModuleFactory as g };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { P as PageUrl } from './types-DHOxe8rc.js';
|
|
2
|
+
|
|
3
|
+
declare function usePageUrlGenerator(): (params: {
|
|
4
|
+
page?: PageUrl | string;
|
|
5
|
+
id?: string;
|
|
6
|
+
childPage?: PageUrl | string;
|
|
7
|
+
childId?: string;
|
|
8
|
+
additionalParameters?: {
|
|
9
|
+
[key: string]: string | string[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
language?: string;
|
|
12
|
+
}) => string;
|
|
13
|
+
|
|
14
|
+
export { usePageUrlGenerator as u };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { P as PageUrl } from './types-CQSjy7et.mjs';
|
|
2
|
+
|
|
3
|
+
declare function usePageUrlGenerator(): (params: {
|
|
4
|
+
page?: PageUrl | string;
|
|
5
|
+
id?: string;
|
|
6
|
+
childPage?: PageUrl | string;
|
|
7
|
+
childId?: string;
|
|
8
|
+
additionalParameters?: {
|
|
9
|
+
[key: string]: string | string[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
language?: string;
|
|
12
|
+
}) => string;
|
|
13
|
+
|
|
14
|
+
export { usePageUrlGenerator as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carlonicora/nextjs-jsonapi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.110.0",
|
|
4
4
|
"description": "Next.js JSON:API client with server/client support and caching",
|
|
5
5
|
"author": "Carlo Nicora",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -52,6 +52,16 @@
|
|
|
52
52
|
"types": "./dist/billing/index.d.ts",
|
|
53
53
|
"import": "./dist/billing/index.mjs",
|
|
54
54
|
"require": "./dist/billing/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./help": {
|
|
57
|
+
"types": "./dist/features/help/index.d.ts",
|
|
58
|
+
"import": "./dist/features/help/index.mjs",
|
|
59
|
+
"require": "./dist/features/help/index.js"
|
|
60
|
+
},
|
|
61
|
+
"./help/server": {
|
|
62
|
+
"types": "./dist/features/help/server.d.ts",
|
|
63
|
+
"import": "./dist/features/help/server.mjs",
|
|
64
|
+
"require": "./dist/features/help/server.js"
|
|
55
65
|
}
|
|
56
66
|
},
|
|
57
67
|
"scripts": {
|
|
@@ -139,6 +149,10 @@
|
|
|
139
149
|
"react-markdown": "^10.1.0",
|
|
140
150
|
"react-resizable-panels": "^4.11.1",
|
|
141
151
|
"recharts": "^3.8.1",
|
|
152
|
+
"fuse.js": "^7.0.0",
|
|
153
|
+
"next-mdx-remote": "^5.0.0",
|
|
154
|
+
"rehype-autolink-headings": "^7.1.0",
|
|
155
|
+
"rehype-slug": "^6.0.0",
|
|
142
156
|
"remark-gfm": "^4.0.1",
|
|
143
157
|
"shadcn": "^4.8.0",
|
|
144
158
|
"shepherd.js": "^15.2.2",
|
package/src/client/config.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { ModuleWithPermissions } from "../permissions/types";
|
|
4
4
|
import { setBootstrapper } from "../core/registry/bootstrapStore";
|
|
5
|
+
import { _setStaticHelpContent } from "../core/registry/helpStore";
|
|
6
|
+
import type { HelpContentConfig } from "../features/help/interfaces/help-content-config.interface";
|
|
5
7
|
|
|
6
8
|
// Config storage for client-side contexts
|
|
7
9
|
let _clientConfig: {
|
|
@@ -16,6 +18,7 @@ let _clientConfig: {
|
|
|
16
18
|
/**
|
|
17
19
|
* Configure the JSON:API client. This is the main configuration function.
|
|
18
20
|
* This is typically called during app initialization.
|
|
21
|
+
* @param config.helpContent - Optional help-content config (brand only). Forwarded to the help feature's globalThis-backed store; not stored on the client config.
|
|
19
22
|
*/
|
|
20
23
|
export function configureJsonApi(config: {
|
|
21
24
|
apiUrl: string;
|
|
@@ -24,8 +27,13 @@ export function configureJsonApi(config: {
|
|
|
24
27
|
bootstrapper?: () => void;
|
|
25
28
|
additionalHeaders?: Record<string, string>;
|
|
26
29
|
stripePublishableKey?: string;
|
|
30
|
+
helpContent?: HelpContentConfig;
|
|
27
31
|
}): void {
|
|
28
|
-
|
|
32
|
+
const { helpContent, ...rest } = config;
|
|
33
|
+
_clientConfig = rest;
|
|
34
|
+
if (helpContent) {
|
|
35
|
+
_setStaticHelpContent(helpContent);
|
|
36
|
+
}
|
|
29
37
|
// Register and call bootstrapper to register all modules
|
|
30
38
|
if (config.bootstrapper) {
|
|
31
39
|
setBootstrapper(config.bootstrapper);
|
package/src/components/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./containers";
|
|
|
6
6
|
export * from "./contents";
|
|
7
7
|
export * from "./details";
|
|
8
8
|
export * from "./editors";
|
|
9
|
+
export { BlockNoteViewerContainer } from "./viewers/BlockNoteViewerContainer";
|
|
9
10
|
export * from "./errors";
|
|
10
11
|
export * from "./forms";
|
|
11
12
|
export * from "./navigations";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCreateBlockNote } from "@blocknote/react";
|
|
4
|
+
import type { PartialBlock } from "@blocknote/core";
|
|
5
|
+
// Mirror BlockNoteEditor.tsx's BlockNoteView import + style.css. Default schema
|
|
6
|
+
// only — no useCurrentUserContext, uploadImage, AI, or custom inline specs, so
|
|
7
|
+
// this can render on auth-free public pages.
|
|
8
|
+
import { BlockNoteView } from "@blocknote/shadcn";
|
|
9
|
+
import "@blocknote/shadcn/style.css";
|
|
10
|
+
|
|
11
|
+
function normalize(content: unknown): PartialBlock[] | undefined {
|
|
12
|
+
if (Array.isArray(content) && content.length > 0) return content as PartialBlock[];
|
|
13
|
+
if (typeof content === "string" && content.trim()) {
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (Array.isArray(parsed) && parsed.length > 0) return parsed as PartialBlock[];
|
|
17
|
+
} catch {
|
|
18
|
+
/* plain string — fall through to undefined (rendered as empty) */
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function BlockNoteViewer({ content }: { content: unknown }) {
|
|
25
|
+
const editor = useCreateBlockNote({ initialContent: normalize(content) });
|
|
26
|
+
return <BlockNoteView editor={editor} editable={false} theme="light" />;
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import dynamic from "next/dynamic";
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
const BlockNoteViewer = dynamic(() => import("./BlockNoteViewer").then((m) => m.BlockNoteViewer), { ssr: false });
|
|
7
|
+
|
|
8
|
+
export const BlockNoteViewerContainer = React.memo(function ViewerContainer(props: { content: unknown }) {
|
|
9
|
+
return <BlockNoteViewer {...props} />;
|
|
10
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized help-content store accessible from client and server contexts.
|
|
3
|
+
* Uses a globalThis Symbol key to persist across HMR/Turbopack reloads and to
|
|
4
|
+
* bridge between the client-side `configureJsonApi` (in `client/config.ts`)
|
|
5
|
+
* and the HelpProvider context. Holds the brand-only HelpContentConfig.
|
|
6
|
+
*
|
|
7
|
+
* NO external dependencies to avoid circular imports.
|
|
8
|
+
*
|
|
9
|
+
* Pattern mirrors `bootstrapStore.ts`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const HELP_CONTENT_KEY = Symbol.for("nextjs-jsonapi:helpContent");
|
|
13
|
+
|
|
14
|
+
const globalStore = globalThis as unknown as {
|
|
15
|
+
[HELP_CONTENT_KEY]?: unknown | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (globalStore[HELP_CONTENT_KEY] === undefined) {
|
|
19
|
+
globalStore[HELP_CONTENT_KEY] = null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function _setStaticHelpContent(cfg: unknown | null): void {
|
|
23
|
+
globalStore[HELP_CONTENT_KEY] = cfg;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function _getStaticHelpContent<T = unknown>(): T | null {
|
|
27
|
+
return (globalStore[HELP_CONTENT_KEY] as T | null) ?? null;
|
|
28
|
+
}
|
|
@@ -20,7 +20,7 @@ interface AssistantContextValue {
|
|
|
20
20
|
sending: boolean;
|
|
21
21
|
status?: string;
|
|
22
22
|
failedMessageIds: Set<string>;
|
|
23
|
-
sendMessage(content: string): Promise<void>;
|
|
23
|
+
sendMessage(content: string, opts?: { howToMode?: boolean; limitToHowToId?: string }): Promise<void>;
|
|
24
24
|
retrySend(tempId: string): Promise<void>;
|
|
25
25
|
selectThread(id: string): Promise<void>;
|
|
26
26
|
startNew(): void;
|
|
@@ -34,10 +34,17 @@ interface Props {
|
|
|
34
34
|
children: React.ReactNode;
|
|
35
35
|
dehydratedAssistant?: JsonApiHydratedDataInterface;
|
|
36
36
|
dehydratedMessages?: JsonApiHydratedDataInterface[];
|
|
37
|
+
/**
|
|
38
|
+
* When `true` (default), the provider mutates the browser URL on
|
|
39
|
+
* create/selectThread/startNew (e.g. `/assistants/{id}`). Set to `false`
|
|
40
|
+
* when the assistant is hosted inside a sheet / overlay so the user's
|
|
41
|
+
* current route is preserved.
|
|
42
|
+
*/
|
|
43
|
+
manageUrl?: boolean;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
function stripOptimistic(list: AssistantMessageInterface[]): AssistantMessageInterface[] {
|
|
40
|
-
return list.filter((m) => !m.
|
|
47
|
+
return list.filter((m) => !m.isOptimistic);
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
function nextPosition(list: AssistantMessageInterface[]): number {
|
|
@@ -55,7 +62,7 @@ function withPatchedTitle(source: AssistantInterface, title: string): AssistantI
|
|
|
55
62
|
});
|
|
56
63
|
}
|
|
57
64
|
|
|
58
|
-
export function AssistantProvider({ children, dehydratedAssistant, dehydratedMessages }: Props) {
|
|
65
|
+
export function AssistantProvider({ children, dehydratedAssistant, dehydratedMessages, manageUrl = true }: Props) {
|
|
59
66
|
const t = useTranslations();
|
|
60
67
|
const generateUrl = usePageUrlGenerator();
|
|
61
68
|
|
|
@@ -73,7 +80,7 @@ export function AssistantProvider({ children, dehydratedAssistant, dehydratedMes
|
|
|
73
80
|
const { socket } = useSocketContext();
|
|
74
81
|
|
|
75
82
|
const sendMessage = useCallback(
|
|
76
|
-
async (content: string) => {
|
|
83
|
+
async (content: string, opts?: { howToMode?: boolean; limitToHowToId?: string }) => {
|
|
77
84
|
const trimmed = content.trim();
|
|
78
85
|
if (!trimmed) return;
|
|
79
86
|
|
|
@@ -94,18 +101,24 @@ export function AssistantProvider({ children, dehydratedAssistant, dehydratedMes
|
|
|
94
101
|
|
|
95
102
|
try {
|
|
96
103
|
if (!assistant) {
|
|
97
|
-
const created = await AssistantService.create({
|
|
104
|
+
const created = await AssistantService.create({
|
|
105
|
+
firstMessage: trimmed,
|
|
106
|
+
howToMode: opts?.howToMode,
|
|
107
|
+
limitToHowToId: opts?.limitToHowToId,
|
|
108
|
+
});
|
|
98
109
|
const msgs = await AssistantMessageService.findByAssistant({ assistantId: created.id });
|
|
99
110
|
setAssistant(created);
|
|
100
111
|
setMessages(msgs);
|
|
101
112
|
setThreads((prev) => [created, ...prev]);
|
|
102
|
-
if (typeof window !== "undefined") {
|
|
113
|
+
if (manageUrl && typeof window !== "undefined") {
|
|
103
114
|
window.history.replaceState(null, "", `/assistants/${created.id}`);
|
|
104
115
|
}
|
|
105
116
|
} else {
|
|
106
117
|
const result = await AssistantService.appendMessage({
|
|
107
118
|
assistantId: assistant.id,
|
|
108
119
|
content: trimmed,
|
|
120
|
+
howToMode: opts?.howToMode,
|
|
121
|
+
limitToHowToId: opts?.limitToHowToId,
|
|
109
122
|
});
|
|
110
123
|
setMessages((prev) => [...stripOptimistic(prev), ...result]);
|
|
111
124
|
}
|
|
@@ -142,17 +155,20 @@ export function AssistantProvider({ children, dehydratedAssistant, dehydratedMes
|
|
|
142
155
|
[messages, sendMessage],
|
|
143
156
|
);
|
|
144
157
|
|
|
145
|
-
const selectThread = useCallback(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
const selectThread = useCallback(
|
|
159
|
+
async (id: string) => {
|
|
160
|
+
const [target, msgs] = await Promise.all([
|
|
161
|
+
AssistantService.findOne({ id }),
|
|
162
|
+
AssistantMessageService.findByAssistant({ assistantId: id }),
|
|
163
|
+
]);
|
|
164
|
+
setAssistant(target);
|
|
165
|
+
setMessages(msgs);
|
|
166
|
+
if (manageUrl && typeof window !== "undefined") {
|
|
167
|
+
window.history.replaceState(null, "", `/assistants/${id}`);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
[manageUrl],
|
|
171
|
+
);
|
|
156
172
|
|
|
157
173
|
const renameThread = useCallback(async (id: string, title: string) => {
|
|
158
174
|
await AssistantService.rename({ id, title });
|
|
@@ -164,10 +180,10 @@ export function AssistantProvider({ children, dehydratedAssistant, dehydratedMes
|
|
|
164
180
|
setAssistant(undefined);
|
|
165
181
|
setMessages([]);
|
|
166
182
|
setFailedMessageIds(new Set());
|
|
167
|
-
if (typeof window !== "undefined") {
|
|
183
|
+
if (manageUrl && typeof window !== "undefined") {
|
|
168
184
|
window.history.replaceState(null, "", "/assistants");
|
|
169
185
|
}
|
|
170
|
-
}, []);
|
|
186
|
+
}, [manageUrl]);
|
|
171
187
|
|
|
172
188
|
const deleteThread = useCallback(async (id: string) => {
|
|
173
189
|
await AssistantService.delete({ id });
|
|
@@ -264,7 +264,7 @@ describe("AssistantContext", () => {
|
|
|
264
264
|
|
|
265
265
|
// Before the server responds, the optimistic user bubble must be visible.
|
|
266
266
|
expect(result.current.messages.map((m) => m.content)).toContain("follow-up");
|
|
267
|
-
expect(result.current.messages.some((m) => m.
|
|
267
|
+
expect(result.current.messages.some((m) => m.isOptimistic && m.role === "user")).toBe(true);
|
|
268
268
|
expect(result.current.sending).toBe(true);
|
|
269
269
|
|
|
270
270
|
await act(async () => {
|
|
@@ -276,7 +276,7 @@ describe("AssistantContext", () => {
|
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
// After reconciliation, no tmp-* remains, and server messages are appended.
|
|
279
|
-
expect(result.current.messages.some((m) => m.
|
|
279
|
+
expect(result.current.messages.some((m) => m.isOptimistic)).toBe(false);
|
|
280
280
|
expect(result.current.messages.map((m) => m.content)).toEqual(["follow-up", "reply"]);
|
|
281
281
|
expect(result.current.sending).toBe(false);
|
|
282
282
|
});
|
|
@@ -307,7 +307,7 @@ describe("AssistantContext", () => {
|
|
|
307
307
|
|
|
308
308
|
// Before the server responds: thread has exactly the optimistic user bubble.
|
|
309
309
|
expect(result.current.messages).toHaveLength(1);
|
|
310
|
-
expect(result.current.messages[0].
|
|
310
|
+
expect(result.current.messages[0].isOptimistic).toBe(true);
|
|
311
311
|
expect(result.current.messages[0].content).toBe("first question");
|
|
312
312
|
expect(result.current.assistant).toBeUndefined();
|
|
313
313
|
expect(result.current.sending).toBe(true);
|
|
@@ -319,11 +319,71 @@ describe("AssistantContext", () => {
|
|
|
319
319
|
|
|
320
320
|
// After reconciliation: assistant set, URL replaced, server messages only.
|
|
321
321
|
expect(result.current.assistant?.id).toBe("a-1");
|
|
322
|
-
expect(result.current.messages.some((m) => m.
|
|
322
|
+
expect(result.current.messages.some((m) => m.isOptimistic)).toBe(false);
|
|
323
323
|
expect(result.current.messages).toHaveLength(2);
|
|
324
324
|
expect(replaceState).toHaveBeenCalledWith(null, "", "/assistants/a-1");
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
it("forwards howToMode to AssistantService.create on first send", async () => {
|
|
328
|
+
const replaceState = vi.spyOn(window.history, "replaceState").mockImplementation(() => {});
|
|
329
|
+
const created = buildAssistantStub({ id: "a-1" });
|
|
330
|
+
AssistantService.create = vi.fn().mockResolvedValue(created);
|
|
331
|
+
AssistantMessageService.findByAssistant = vi.fn().mockResolvedValue([]);
|
|
332
|
+
|
|
333
|
+
const { result } = renderHook(() => useAssistantContext(), {
|
|
334
|
+
wrapper: ({ children }) => <AssistantProvider>{children}</AssistantProvider>,
|
|
335
|
+
});
|
|
336
|
+
await act(async () => {
|
|
337
|
+
await result.current.sendMessage("hi", { howToMode: true });
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
expect(AssistantService.create).toHaveBeenCalledWith(
|
|
341
|
+
expect.objectContaining({ firstMessage: "hi", howToMode: true }),
|
|
342
|
+
);
|
|
343
|
+
replaceState.mockRestore();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("forwards howToMode to AssistantService.appendMessage on follow-up send", async () => {
|
|
347
|
+
const existing = buildAssistantDehydrated({ id: "a-2", title: "Existing" });
|
|
348
|
+
AssistantService.appendMessage = vi.fn().mockResolvedValue([]);
|
|
349
|
+
|
|
350
|
+
const { result } = renderHook(() => useAssistantContext(), {
|
|
351
|
+
wrapper: ({ children }) => <AssistantProvider dehydratedAssistant={existing}>{children}</AssistantProvider>,
|
|
352
|
+
});
|
|
353
|
+
await act(async () => {
|
|
354
|
+
await result.current.sendMessage("follow up", { howToMode: true, limitToHowToId: "ht-1" });
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
expect(AssistantService.appendMessage).toHaveBeenCalledWith(
|
|
358
|
+
expect.objectContaining({
|
|
359
|
+
assistantId: "a-2",
|
|
360
|
+
content: "follow up",
|
|
361
|
+
howToMode: true,
|
|
362
|
+
limitToHowToId: "ht-1",
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("calls service without opts when called with content only (regression)", async () => {
|
|
368
|
+
const replaceState = vi.spyOn(window.history, "replaceState").mockImplementation(() => {});
|
|
369
|
+
const created = buildAssistantStub({ id: "a-1" });
|
|
370
|
+
AssistantService.create = vi.fn().mockResolvedValue(created);
|
|
371
|
+
AssistantMessageService.findByAssistant = vi.fn().mockResolvedValue([]);
|
|
372
|
+
|
|
373
|
+
const { result } = renderHook(() => useAssistantContext(), {
|
|
374
|
+
wrapper: ({ children }) => <AssistantProvider>{children}</AssistantProvider>,
|
|
375
|
+
});
|
|
376
|
+
await act(async () => {
|
|
377
|
+
await result.current.sendMessage("hi");
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
expect(AssistantService.create).toHaveBeenCalledWith(expect.objectContaining({ firstMessage: "hi" }));
|
|
381
|
+
const call = (AssistantService.create as ReturnType<typeof vi.fn>).mock.calls[0][0];
|
|
382
|
+
expect(call.howToMode).toBeUndefined();
|
|
383
|
+
expect(call.limitToHowToId).toBeUndefined();
|
|
384
|
+
replaceState.mockRestore();
|
|
385
|
+
});
|
|
386
|
+
|
|
327
387
|
it("sendMessage failure: optimistic message stays and its id lands in failedMessageIds", async () => {
|
|
328
388
|
const existing = buildAssistantDehydrated({ id: "a-x", title: "Ex" });
|
|
329
389
|
AssistantService.appendMessage = vi.fn().mockRejectedValue(new Error("boom"));
|
|
@@ -335,7 +395,7 @@ describe("AssistantContext", () => {
|
|
|
335
395
|
await result.current.sendMessage("oops").catch(() => {});
|
|
336
396
|
});
|
|
337
397
|
|
|
338
|
-
const optimistic = result.current.messages.find((m) => m.
|
|
398
|
+
const optimistic = result.current.messages.find((m) => m.isOptimistic);
|
|
339
399
|
expect(optimistic).toBeDefined();
|
|
340
400
|
expect(optimistic!.content).toBe("oops");
|
|
341
401
|
expect(result.current.failedMessageIds.has(optimistic!.id)).toBe(true);
|
|
@@ -368,7 +428,7 @@ describe("AssistantContext", () => {
|
|
|
368
428
|
});
|
|
369
429
|
|
|
370
430
|
expect(result.current.failedMessageIds.has(failedId!)).toBe(false);
|
|
371
|
-
expect(result.current.messages.some((m) => m.
|
|
431
|
+
expect(result.current.messages.some((m) => m.isOptimistic)).toBe(false);
|
|
372
432
|
expect(result.current.messages.map((m) => m.content)).toEqual(["retry-me", "ok"]);
|
|
373
433
|
expect(appendMock).toHaveBeenCalledTimes(2);
|
|
374
434
|
});
|
|
@@ -29,6 +29,8 @@ export class Assistant extends AbstractApiData implements AssistantInterface {
|
|
|
29
29
|
attributes: {
|
|
30
30
|
content: data.firstMessage,
|
|
31
31
|
...(data.title !== undefined ? { title: data.title } : {}),
|
|
32
|
+
...(data.howToMode !== undefined ? { howToMode: data.howToMode } : {}),
|
|
33
|
+
...(data.limitToHowToId !== undefined ? { limitToHowToId: data.limitToHowToId } : {}),
|
|
32
34
|
},
|
|
33
35
|
},
|
|
34
36
|
included: [],
|