@@ -8,6 +8,50 @@ external runtime: runtime = "runtime"
88@send
99external sendMessage : (runtime , Js .Json .t ) => Js .Promise .t <Js .Json .t > = "sendMessage"
1010
11+ let ensureOverlayMountPoint : unit => unit = %raw (` () => {
12+ if (document .querySelector (" #blocky-writer-overlay" ) !== null ) return ;
13+ const root = document .createElement (" div" );
14+ root .id = " blocky-writer-overlay" ;
15+ root .style .position = " fixed" ;
16+ root .style .right = " 16px" ;
17+ root .style .bottom = " 16px" ;
18+ root .style .zIndex = " 2147483647" ;
19+ root .style .width = " 320px" ;
20+ root .style .maxWidth = " 90vw" ;
21+ root .style .fontFamily = " ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif" ;
22+ document .body .appendChild (root);
23+ }` )
24+
25+ let escapeHtml : string => string = %raw (` (value ) =>
26+ value
27+ .replace (/ &/ g , " &" )
28+ .replace (/ </ g , " <" )
29+ .replace (/ >/ g , " >" )
30+ .replace (/ "/ g , " "" )
31+ .replace (/ '/ g , " '" )
32+ ` )
33+
34+ let renderPanel = (~title : string , ~detail : string ): unit => {
35+ let safeTitle = escapeHtml (title )
36+ let safeDetail = escapeHtml (detail )
37+ let html =
38+ "<section style='background:#111827;color:#fff;border-radius:10px;padding:12px;box-shadow:0 10px 30px rgba(0,0,0,.3);'>"
39+ ++ "<div style='font-size:13px;font-weight:700;margin-bottom:6px;'>Blocky Writer</div>"
40+ ++ "<div style='font-size:12px;line-height:1.4;'><strong>"
41+ ++ safeTitle
42+ ++ "</strong><br />"
43+ ++ safeDetail
44+ ++ "</div>"
45+ ++ "</section>"
46+
47+ SafeDOM .mountWhenReady (
48+ "#blocky-writer-overlay" ,
49+ html ,
50+ ~onSuccess = {_ => ()},
51+ ~onError = {error => Js .log2 ("Blocky Writer overlay mount error" , error )},
52+ )
53+ }
54+
1155let unsafeJson = (value : 'a ): Js .Json .t => Obj .magic (value )
1256
1357let makeDetectMessage = (url : string ): Js .Json .t => {
@@ -17,7 +61,12 @@ let makeDetectMessage = (url: string): Js.Json.t => {
1761 unsafeJson (payload )
1862}
1963
20- let isPdfUrl = (url : string ): bool => url -> Js .String2 .toLowerCase -> Js .String2 .endsWith (".pdf" )
64+ let isPdfUrl = (url : string ): bool => {
65+ let lower = url -> Js .String2 .toLowerCase
66+ lower -> Js .String2 .endsWith (".pdf" )
67+ || lower -> Js .String2 .includes (".pdf?" )
68+ || lower -> Js .String2 .includes (".pdf#" )
69+ }
2170
2271let findPdfTarget = (): option <string > => {
2372 let currentUrl = Webapi .Dom .location -> Webapi .Dom .Location .href
@@ -41,7 +90,7 @@ let findPdfTarget = (): option<string> => {
4190 }
4291}
4392
44- let logDetectionResponse = (response : Js .Json .t ): unit => {
93+ let handleDetectionResponse = (response : Js .Json .t ): unit => {
4594 switch Js .Json .decodeObject (response ) {
4695 | Some (payload ) =>
4796 let ok =
@@ -53,29 +102,37 @@ let logDetectionResponse = (response: Js.Json.t): unit => {
53102 -> Belt .Option .flatMap (Js .Json .decodeArray )
54103 -> Belt .Option .map (Belt .Array .length )
55104 -> Belt .Option .getWithDefault (0 )
56- Js .log2 ("Blocky Writer detected blocks" , count )
105+ if count == 0 {
106+ renderPanel (~title = "No widgets detected" , ~detail = "This PDF did not expose form widgets." )
107+ } else {
108+ renderPanel (
109+ ~title = "Detection successful" ,
110+ ~detail = "Detected " ++ Belt .Int .toString (count ) ++ " PDF widgets ready for filling." ,
111+ )
112+ }
57113 } else {
58114 let message =
59115 payload
60116 -> Js .Dict .get ("error" )
61117 -> Belt .Option .flatMap (Js .Json .decodeString )
62118 -> Belt .Option .getWithDefault ("unknown error" )
63- Js . log2 ( "Blocky Writer detection failed" , message )
119+ renderPanel (~ title = "Detection failed" , ~ detail = message )
64120 }
65- | None => Js . log ( "Blocky Writer received non-object response " )
121+ | None => renderPanel (~ title = "Detection failed" , ~ detail = "Background returned an invalid payload. " )
66122 }
67123}
68124
69125let requestBlockDetection = (targetUrl : string ): unit => {
70- Js . log2 ( "Blocky Writer detectBlocks target " , targetUrl )
126+ renderPanel (~ title = "Scanning PDF " , ~ detail = "Requesting block detection..." )
71127 let request = makeDetectMessage (targetUrl )
72128 let sendPromise = sendMessage (runtime , request )
73129 let loggedPromise = Js .Promise2 .then (sendPromise , response => {
74- logDetectionResponse (response )
130+ handleDetectionResponse (response )
75131 Js .Promise .resolve (response )
76132 })
77133 let _ = Js .Promise2 .catch (loggedPromise , err => {
78134 Js .log2 ("Blocky Writer message failed" , err )
135+ renderPanel (~title = "Detection failed" , ~detail = "Unable to contact extension background worker." )
79136 Js .Promise .resolve (Js .Json .null )
80137 })
81138 ()
@@ -84,8 +141,9 @@ let requestBlockDetection = (targetUrl: string): unit => {
84141let currentUrl = Webapi .Dom .location -> Webapi .Dom .Location .href
85142
86143if currentUrl -> Js .String2 .includes ("gov.uk" ) {
144+ ensureOverlayMountPoint ()
87145 switch findPdfTarget () {
88146 | Some (targetUrl ) => requestBlockDetection (targetUrl )
89- | None => Js . log ( "Blocky Writer found no PDF target on page" )
147+ | None => renderPanel (~ title = "No PDF target found" , ~ detail = "Open a .pdf URL or include a direct PDF link on this page. " )
90148 }
91149}
0 commit comments