@@ -25,6 +25,61 @@ document.addEventListener("DOMContentLoaded", function () {
25
25
document . getElementById ( "tab-metrics" ) . addEventListener ( "click" , ( ) => {
26
26
showTab ( "metrics" ) ;
27
27
} ) ;
28
+ document . getElementById ( "tab-version-info" ) . addEventListener ( "click" , ( ) => {
29
+ showTab ( "version-info" ) ;
30
+ } ) ;
31
+
32
+ /* ------------------------------------------------------------------
33
+ * Pre-load the "Version & Environment Info" partial once per page
34
+ * ------------------------------------------------------------------ */
35
+ /* Pre-load version-info once */
36
+ document . addEventListener ( "DOMContentLoaded" , ( ) => {
37
+ const panel = document . getElementById ( "version-info-panel" ) ;
38
+ if ( ! panel || panel . innerHTML . trim ( ) !== "" ) return ; // already loaded
39
+
40
+ fetch ( `${ window . ROOT_PATH } /version?partial=true` )
41
+ . then ( ( response ) => {
42
+ if ( ! response . ok ) throw new Error ( "Network response was not ok" ) ;
43
+ return response . text ( ) ;
44
+ } )
45
+ . then ( ( html ) => {
46
+ panel . innerHTML = html ;
47
+
48
+ // If the page was opened at #version-info, show that tab now
49
+ if ( window . location . hash === "#version-info" ) {
50
+ showTab ( "version-info" ) ;
51
+ }
52
+ } )
53
+ . catch ( ( error ) => {
54
+ console . error ( "Failed to preload version info:" , error ) ;
55
+ panel . innerHTML =
56
+ "<p class='text-red-600'>Failed to load version info.</p>" ;
57
+ } ) ;
58
+ } ) ;
59
+
60
+ /* ------------------------------------------------------------------
61
+ * HTMX debug hooks
62
+ * ------------------------------------------------------------------ */
63
+ document . body . addEventListener ( "htmx:afterSwap" , ( event ) => {
64
+ if ( event . detail . target . id === "version-info-panel" ) {
65
+ console . log ( "HTMX: Content swapped into version-info-panel" ) ;
66
+ }
67
+ } ) ;
68
+
69
+
70
+ // HTMX event listeners for debugging
71
+ document . body . addEventListener ( "htmx:beforeRequest" , ( event ) => {
72
+ if ( event . detail . elt . id === "tab-version-info" ) {
73
+ console . log ( "HTMX: Sending request for version info partial" ) ;
74
+ }
75
+ } ) ;
76
+
77
+ document . body . addEventListener ( "htmx:afterSwap" , ( event ) => {
78
+ if ( event . detail . target . id === "version-info-panel" ) {
79
+ console . log ( "HTMX: Content swapped into version-info-panel" ) ;
80
+ }
81
+ } ) ;
82
+
28
83
// Authentication toggle
29
84
document . getElementById ( "auth-type" ) . addEventListener ( "change" , function ( ) {
30
85
const basicFields = document . getElementById ( "auth-basic-fields" ) ;
@@ -110,6 +165,7 @@ document.addEventListener("DOMContentLoaded", function () {
110
165
status . classList . add ( "error-status" ) ;
111
166
} else {
112
167
location . reload ( ) ;
168
+ console . log ( response ) ;
113
169
}
114
170
} )
115
171
. catch ( ( error ) => {
@@ -266,6 +322,7 @@ document.addEventListener("DOMContentLoaded", function () {
266
322
REST : [ "GET" , "POST" , "PUT" , "DELETE" ] ,
267
323
} ;
268
324
325
+
269
326
// Optionally pass in a pre-selected method
270
327
function updateEditToolRequestTypes ( selectedMethod = null ) {
271
328
const selectedType = editToolTypeSelect . value ;
@@ -334,6 +391,27 @@ function showTab(tabName) {
334
391
if ( tabName === "metrics" ) {
335
392
loadAggregatedMetrics ( ) ;
336
393
}
394
+
395
+ if ( tabName === "version-info" ) {
396
+ const panel = document . getElementById ( "version-info-panel" ) ;
397
+ if ( panel && panel . innerHTML . trim ( ) === "" ) {
398
+ const url = `${ window . ROOT_PATH } /version?partial=true` ;
399
+ fetch ( url )
400
+ . then ( ( response ) => {
401
+ if ( ! response . ok ) {
402
+ throw new Error ( "Network response was not ok" ) ;
403
+ }
404
+ return response . text ( ) ;
405
+ } )
406
+ . then ( ( html ) => {
407
+ panel . innerHTML = html ;
408
+ } )
409
+ . catch ( ( error ) => {
410
+ console . error ( "Failed to load version info:" , error ) ;
411
+ panel . innerHTML = "<p class='text-red-600'>Failed to load version info.</p>" ;
412
+ } ) ;
413
+ }
414
+ }
337
415
}
338
416
339
417
// handle auth type selection
@@ -406,7 +484,9 @@ function updateSchemaPreview() {
406
484
}
407
485
}
408
486
409
- // Refresh CodeMirror every time Direct JSON Input is selected
487
+ /* ---------------------------------------------------------------
488
+ * Switch between "UI-builder" and "JSON input" modes
489
+ * ------------------------------------------------------------- */
410
490
Array . from ( schemaModeRadios ) . forEach ( ( radio ) => {
411
491
radio . addEventListener ( "change" , ( ) => {
412
492
if ( radio . value === "ui" && radio . checked ) {
@@ -415,25 +495,11 @@ Array.from(schemaModeRadios).forEach((radio) => {
415
495
} else if ( radio . value === "json" && radio . checked ) {
416
496
uiBuilderDiv . style . display = "none" ;
417
497
jsonInputContainer . style . display = "block" ;
418
- updateSchemaPreview ( ) ;
498
+ updateSchemaPreview ( ) ; // keep preview in sync
419
499
}
420
500
} ) ;
421
- } ) ;
501
+ } ) ; // closes addEventListener callback, forEach callback, and forEach call
422
502
423
- // Attach event listeners to dynamically added parameter inputs
424
- function attachListeners ( paramDiv ) {
425
- const inputs = paramDiv . querySelectorAll ( "input, select, textarea" ) ;
426
- inputs . forEach ( ( input ) => {
427
- input . addEventListener ( "input" , ( ) => {
428
- const mode = document . querySelector (
429
- 'input[name="schema_input_mode"]:checked' ,
430
- ) . value ;
431
- if ( mode === "json" ) {
432
- updateSchemaPreview ( ) ;
433
- }
434
- } ) ;
435
- } ) ;
436
- }
437
503
438
504
// On form submission, update CodeMirror with UI builder schema if needed
439
505
// document.getElementById('add-tool-form').addEventListener('submit', (e) => {
@@ -1630,6 +1696,45 @@ async function runToolTest() {
1630
1696
} ) ;
1631
1697
}
1632
1698
1699
+ /* ---------------------------------------------------------------
1700
+ * Utility: copy a JSON string (or any text) to the system clipboard
1701
+ * ------------------------------------------------------------- */
1702
+ function copyJsonToClipboard ( sourceId ) {
1703
+ // 1. Get the element that holds the JSON (can be a <pre>, <code>, <textarea>, etc.)
1704
+ const el = document . getElementById ( sourceId ) ;
1705
+ if ( ! el ) {
1706
+ console . warn ( `[copyJsonToClipboard] Source element "${ sourceId } " not found.` ) ;
1707
+ return ;
1708
+ }
1709
+
1710
+ // 2. Extract the text; fall back to textContent if value is undefined
1711
+ const text = "value" in el ? el . value : el . textContent ;
1712
+
1713
+ // 3. Copy to clipboard
1714
+ navigator . clipboard . writeText ( text ) . then (
1715
+ ( ) => {
1716
+ console . info ( "JSON copied to clipboard ✔️" ) ;
1717
+ // Optional: user feedback
1718
+ if ( el . dataset . toast !== "off" ) {
1719
+ const toast = document . createElement ( "div" ) ;
1720
+ toast . textContent = "Copied!" ;
1721
+ toast . className =
1722
+ "fixed bottom-4 right-4 bg-green-600 text-white px-3 py-1 rounded shadow" ;
1723
+ document . body . appendChild ( toast ) ;
1724
+ setTimeout ( ( ) => toast . remove ( ) , 1500 ) ;
1725
+ }
1726
+ } ,
1727
+ ( err ) => {
1728
+ console . error ( "Clipboard write failed:" , err ) ;
1729
+ alert ( "Unable to copy to clipboard - see console for details." ) ;
1730
+ }
1731
+ ) ;
1732
+ }
1733
+
1734
+ // Make it available to inline onclick handlers
1735
+ window . copyJsonToClipboard = copyJsonToClipboard ;
1736
+
1737
+
1633
1738
// Utility functions to open and close modals
1634
1739
function openModal ( modalId ) {
1635
1740
document . getElementById ( modalId ) . classList . remove ( "hidden" ) ;
0 commit comments