1
- import { useKapaConfig } from "~/root" ;
2
1
import { useShortcuts } from "../components/primitives/ShortcutsProvider" ;
3
2
import { useFeatures } from "~/hooks/useFeatures" ;
4
3
import { useCallback , useEffect , useState } from "react" ;
4
+ import { useMatches } from "@remix-run/react" ;
5
+ import { useTypedMatchesData } from "./useTypedMatchData" ;
6
+ import { type loader } from "~/root" ;
7
+
8
+ type OpenOptions = { mode : string ; query : string ; submit : boolean } ;
5
9
6
10
declare global {
7
11
interface Window {
8
- Kapa : ( (
12
+ Kapa : (
9
13
command : string ,
10
- options ?: ( ) => void ,
14
+ options ?: ( ( ) => void ) | OpenOptions ,
11
15
remove ?: string | { onRender ?: ( ) => void }
12
- ) => void ) & {
13
- open : ( options ?: { mode : string ; query : string ; submit : boolean } ) => void ;
14
- } ;
16
+ ) => void ;
15
17
}
16
18
}
17
19
20
+ export function KapaScripts ( { websiteId } : { websiteId ?: string } ) {
21
+ if ( ! websiteId ) return null ;
22
+
23
+ return (
24
+ < >
25
+ < script
26
+ async
27
+ src = "https://widget.kapa.ai/kapa-widget.bundle.js"
28
+ data-website-id = { websiteId }
29
+ data-project-name = { "Trigger.dev" }
30
+ data-project-color = { "#C7D2FE" }
31
+ data-project-logo = { "https://content.trigger.dev/trigger-logo-circle.png" }
32
+ data-render-on-load = { "false" }
33
+ data-button-hide = { "true" }
34
+ data-modal-disclaimer-bg-color = { "#1A1B1F" }
35
+ data-modal-disclaimer-text-color = { "#878C99" }
36
+ data-modal-header-bg-color = { "#2C3034" }
37
+ data-modal-body-bg-color = { "#4D525B" }
38
+ data-query-input-text-color = { "#15171A" }
39
+ data-query-input-placeholder-text-color = { "#878C99" }
40
+ data-modal-title-color = { "#D7D9DD" }
41
+ data-button-text-color = { "#D7D9DD" }
42
+ > </ script >
43
+ < script
44
+ dangerouslySetInnerHTML = { {
45
+ __html : `
46
+ (function () {
47
+ let k = window.Kapa;
48
+ if (!k) {
49
+ let i = function () {
50
+ i.c(arguments);
51
+ };
52
+ i.q = [];
53
+ i.c = function (args) {
54
+ i.q.push(args);
55
+ };
56
+ window.Kapa = i;
57
+ }
58
+ })();
59
+ ` ,
60
+ } }
61
+ />
62
+ </ >
63
+ ) ;
64
+ }
65
+
66
+ export function useKapaConfig ( ) {
67
+ const matches = useMatches ( ) ;
68
+ const routeMatch = useTypedMatchesData < typeof loader > ( {
69
+ id : "root" ,
70
+ matches,
71
+ } ) ;
72
+ return routeMatch ?. kapa ;
73
+ }
74
+
18
75
export function useKapaWidget ( ) {
19
76
const kapa = useKapaConfig ( ) ;
20
77
const features = useFeatures ( ) ;
21
78
const { disableShortcuts, enableShortcuts, areShortcutsEnabled } = useShortcuts ( ) ;
22
79
const [ isKapaOpen , setIsKapaOpen ] = useState ( false ) ;
23
80
24
- useEffect ( ( ) => {
25
- if ( ! features . isManagedCloud || ! kapa ?. websiteId ) return ;
26
-
27
- loadScriptIfNotExists ( kapa . websiteId ) ;
28
-
29
- // Define the handler function
30
- const handleModalClose = ( ) => {
31
- setIsKapaOpen ( false ) ;
32
- enableShortcuts ( ) ;
33
- } ;
81
+ const handleModalClose = useCallback ( ( ) => {
82
+ setIsKapaOpen ( false ) ;
83
+ enableShortcuts ( ) ;
84
+ } , [ enableShortcuts ] ) ;
34
85
35
- const handleModalOpen = ( ) => {
36
- setIsKapaOpen ( true ) ;
37
- disableShortcuts ( ) ;
38
- } ;
86
+ const handleModalOpen = useCallback ( ( ) => {
87
+ setIsKapaOpen ( true ) ;
88
+ disableShortcuts ( ) ;
89
+ } , [ disableShortcuts ] ) ;
39
90
40
- const kapaInterval = setInterval ( ( ) => {
41
- if ( typeof window . Kapa === "function" ) {
42
- clearInterval ( kapaInterval ) ;
43
- window . Kapa ( "render" ) ;
44
- window . Kapa ( "onModalClose" , handleModalClose ) ;
91
+ useEffect ( ( ) => {
92
+ if ( ! features . isManagedCloud || ! kapa ?. websiteId ) return ;
45
93
46
- // Register onModalOpen handler
47
- window . Kapa ( "onModalOpen" , handleModalOpen ) ;
48
- }
49
- } , 100 ) ;
94
+ window . Kapa ( "render" ) ;
95
+ window . Kapa ( "onModalOpen" , handleModalOpen ) ;
96
+ window . Kapa ( "onModalClose" , handleModalClose ) ;
50
97
51
- // Clear interval on unmount to prevent memory leaks
52
98
return ( ) => {
53
- clearInterval ( kapaInterval ) ;
54
- if ( typeof window . Kapa === "function" ) {
55
- window . Kapa ( "unmount" ) ;
56
-
57
- window . Kapa ( "onModalOpen" , handleModalOpen , "remove" ) ;
58
- window . Kapa ( "onModalClose" , handleModalClose , "remove" ) ;
59
- }
99
+ window . Kapa ( "onModalOpen" , handleModalOpen , "remove" ) ;
100
+ window . Kapa ( "onModalClose" , handleModalClose , "remove" ) ;
60
101
} ;
61
- } , [ features . isManagedCloud , kapa ?. websiteId , disableShortcuts , enableShortcuts ] ) ;
102
+ } , [ features . isManagedCloud , kapa ?. websiteId ] ) ;
62
103
63
104
const openKapa = useCallback (
64
105
( query ?: string ) => {
65
106
if ( ! features . isManagedCloud || ! kapa ?. websiteId ) return ;
66
107
67
- if ( typeof window . Kapa === "function" ) {
68
- window . Kapa . open (
69
- query
70
- ? {
71
- mode : "ai" ,
72
- query,
73
- submit : true ,
74
- }
75
- : undefined
76
- ) ;
77
- setIsKapaOpen ( true ) ;
78
- disableShortcuts ( ) ;
79
- }
108
+ window . Kapa (
109
+ "open" ,
110
+ query
111
+ ? {
112
+ mode : "ai" ,
113
+ query,
114
+ submit : true ,
115
+ }
116
+ : undefined
117
+ ) ;
118
+ setIsKapaOpen ( true ) ;
119
+ disableShortcuts ( ) ;
80
120
} ,
81
121
[ disableShortcuts , features . isManagedCloud , kapa ?. websiteId ]
82
122
) ;
@@ -87,38 +127,3 @@ export function useKapaWidget() {
87
127
isKapaOpen,
88
128
} ;
89
129
}
90
-
91
- function loadScriptIfNotExists ( websiteId : string ) {
92
- const scriptSrc = "https://widget.kapa.ai/kapa-widget.bundle.js" ;
93
-
94
- if ( document . querySelector ( `script[src="${ scriptSrc } "]` ) ) {
95
- return ;
96
- }
97
-
98
- const script = document . createElement ( "script" ) ;
99
- script . async = true ;
100
- script . src = scriptSrc ;
101
-
102
- const attributes = {
103
- "data-website-id" : websiteId ,
104
- "data-project-name" : "Trigger.dev" ,
105
- "data-project-color" : "#C7D2FE" ,
106
- "data-project-logo" : "https://content.trigger.dev/trigger-logo-circle.png" ,
107
- "data-render-on-load" : "false" ,
108
- "data-button-hide" : "true" ,
109
- "data-modal-disclaimer-bg-color" : "#1A1B1F" ,
110
- "data-modal-disclaimer-text-color" : "#878C99" ,
111
- "data-modal-header-bg-color" : "#2C3034" ,
112
- "data-modal-body-bg-color" : "#4D525B" ,
113
- "data-query-input-text-color" : "#15171A" ,
114
- "data-query-input-placeholder-text-color" : "#878C99" ,
115
- "data-modal-title-color" : "#D7D9DD" ,
116
- "data-button-text-color" : "#D7D9DD" ,
117
- } ;
118
-
119
- Object . entries ( attributes ) . forEach ( ( [ key , value ] ) => {
120
- script . setAttribute ( key , value ) ;
121
- } ) ;
122
-
123
- document . head . appendChild ( script ) ;
124
- }
0 commit comments