windhamdavid 3 days ago
parent
commit
f59a5abe65
2 changed files with 57 additions and 0 deletions
  1. 14 0
      functions.php
  2. 43 0
      js/ask-widget.js

+ 14 - 0
functions.php

@@ -173,6 +173,20 @@ function dw_scripts() {
 
 }
 
+// davo-bot 2000 โ€” AI ask widget, loaded site-wide on the front end. The loader
+// pulls /ask/widget.js (proxied same-origin to the ralph RAG server). No deps.
+function dw_ask_widget() {
+	if ( is_admin() ) return;
+	wp_enqueue_script(
+		'dw-ask-widget',
+		get_template_directory_uri() . '/js/ask-widget.js',
+		array(),
+		filemtime( get_template_directory() . '/js/ask-widget.js' ),
+		true
+	);
+}
+add_action( 'wp_enqueue_scripts', 'dw_ask_widget' );
+
 add_action('wp_footer', 'dw_analytics', 22);
 function dw_analytics() { ?>
 <script>

+ 43 - 0
js/ask-widget.js

@@ -0,0 +1,43 @@
+/**
+ * Loads "davo-bot 2000" (the floating AI launcher) site-wide.
+ *
+ * The widget bundle (/ask/widget.js) and its API (/ask/api/ask) are served by
+ * the ralph RAG server and proxied SAME-ORIGIN under /ask/ by Apache โ€” in
+ * production on davidawindham.com and locally on daw.stu (see the vhost
+ * ProxyPass directives). Same-origin means no CORS to worry about.
+ *
+ * We probe the widget URL first and only inject it if the response is really
+ * JavaScript: when ralph is down, the request falls through to WordPress and
+ * returns HTML, and injecting that would throw. The probe makes it a silent
+ * no-op until the backend is actually serving the widget. The widget sets
+ * window.__dawaskLoaded to guard against a double instance.
+ */
+( function () {
+	if ( typeof document === 'undefined' || typeof fetch === 'undefined' ) {
+		return;
+	}
+
+	var WIDGET_URL = '/ask/widget.js';
+	var API_URL = '/ask/api/ask';
+
+	function inject() {
+		if ( document.getElementById( 'dawask-script' ) ) {
+			return; // already injected
+		}
+		var s = document.createElement( 'script' );
+		s.id = 'dawask-script';
+		s.src = WIDGET_URL;
+		s.setAttribute( 'data-api-url', API_URL );
+		s.setAttribute( 'data-mode', 'launcher' );
+		document.body.appendChild( s );
+	}
+
+	fetch( WIDGET_URL, { method: 'GET', cache: 'no-store' } )
+		.then( function ( r ) {
+			var ct = ( r.headers.get( 'content-type' ) || '' ).toLowerCase();
+			if ( r.ok && ct.indexOf( 'javascript' ) !== -1 ) {
+				inject();
+			}
+		} )
+		.catch( function () {} ); // backend unreachable โ†’ no widget, no error
+} )();