Skip to content

Commit 9c7161f

Browse files
Fix API keys (remove button) and scroll (prevent checked radio autoscroll)
Per user feedback - simplified both fixes: 1. API Key Fields - Simple and Clean (AISettings.php) - Removed "Change Key" button completely - Removed readonly attribute - fields are now editable - Removed all JavaScript for button handling - Fields show masked values (first 6 chars + *** + last 4 chars) - Users can click and type directly to change keys - Save button validates key is not masked version before saving Simple UX: See masked key, click field, type new key, save. 2. Scroll Fix - Root Cause Solution (Types.php) - IDENTIFIED ROOT CAUSE: Checked radio button causes browser autoscroll - Script finds checked radio buttons BEFORE browser can scroll - Temporarily removes "checked" attribute during page load - Forces scroll to top immediately and continuously - On DOMContentLoaded, restores checked state WITHOUT scrolling - Multiple setTimeout intervals as backup (0-2000ms) - Scroll lock for 2 seconds then releases for user control This directly prevents the browser's native scroll-to-checked behavior which was causing the view to scroll down to the chart library section. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e901194 commit 9c7161f

2 files changed

Lines changed: 37 additions & 66 deletions

File tree

classes/Visualizer/Render/Page/AISettings.php

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ protected function _renderContent() {
116116
echo '<tr>';
117117
echo '<th scope="row"><label for="visualizer_openai_api_key">' . esc_html__( 'OpenAI API Key (ChatGPT)', 'visualizer' ) . '</label></th>';
118118
echo '<td>';
119-
echo '<input type="text" id="visualizer_openai_api_key" name="visualizer_openai_api_key" value="' . esc_attr( $openai_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" readonly />';
120-
echo '<button type="button" class="button visualizer-change-key" data-target="visualizer_openai_api_key" style="margin-left: 5px;">' . esc_html__( 'Change Key', 'visualizer' ) . '</button>';
119+
echo '<input type="text" id="visualizer_openai_api_key" name="visualizer_openai_api_key" value="' . esc_attr( $openai_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" />';
121120
echo '<p class="description">' . esc_html__( 'Enter your OpenAI API key to enable ChatGPT integration.', 'visualizer' ) . ' <a href="https://platform.openai.com/api-keys" target="_blank">' . esc_html__( 'Get API Key', 'visualizer' ) . '</a></p>';
122121
echo '</td>';
123122
echo '</tr>';
@@ -126,8 +125,7 @@ protected function _renderContent() {
126125
echo '<tr>';
127126
echo '<th scope="row"><label for="visualizer_gemini_api_key">' . esc_html__( 'Google Gemini API Key', 'visualizer' ) . '</label></th>';
128127
echo '<td>';
129-
echo '<input type="text" id="visualizer_gemini_api_key" name="visualizer_gemini_api_key" value="' . esc_attr( $gemini_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" readonly />';
130-
echo '<button type="button" class="button visualizer-change-key" data-target="visualizer_gemini_api_key" style="margin-left: 5px;">' . esc_html__( 'Change Key', 'visualizer' ) . '</button>';
128+
echo '<input type="text" id="visualizer_gemini_api_key" name="visualizer_gemini_api_key" value="' . esc_attr( $gemini_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" />';
131129
echo '<p class="description">' . esc_html__( 'Enter your Google Gemini API key.', 'visualizer' ) . ' <a href="https://makersuite.google.com/app/apikey" target="_blank">' . esc_html__( 'Get API Key', 'visualizer' ) . '</a></p>';
132130
echo '</td>';
133131
echo '</tr>';
@@ -136,8 +134,7 @@ protected function _renderContent() {
136134
echo '<tr>';
137135
echo '<th scope="row"><label for="visualizer_claude_api_key">' . esc_html__( 'Anthropic Claude API Key', 'visualizer' ) . '</label></th>';
138136
echo '<td>';
139-
echo '<input type="text" id="visualizer_claude_api_key" name="visualizer_claude_api_key" value="' . esc_attr( $claude_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" readonly />';
140-
echo '<button type="button" class="button visualizer-change-key" data-target="visualizer_claude_api_key" style="margin-left: 5px;">' . esc_html__( 'Change Key', 'visualizer' ) . '</button>';
137+
echo '<input type="text" id="visualizer_claude_api_key" name="visualizer_claude_api_key" value="' . esc_attr( $claude_key_display ) . '" class="regular-text" placeholder="' . esc_attr__( 'Enter API key', 'visualizer' ) . '" autocomplete="off" />';
141138
echo '<p class="description">' . esc_html__( 'Enter your Anthropic Claude API key.', 'visualizer' ) . ' <a href="https://console.anthropic.com/account/keys" target="_blank">' . esc_html__( 'Get API Key', 'visualizer' ) . '</a></p>';
142139
echo '</td>';
143140
echo '</tr>';
@@ -150,45 +147,6 @@ protected function _renderContent() {
150147

151148
echo '</form>';
152149

153-
// Add JavaScript to handle Change Key button
154-
?>
155-
<script type="text/javascript">
156-
jQuery(document).ready(function($) {
157-
$('.visualizer-change-key').on('click', function() {
158-
var $button = $(this);
159-
var targetId = $button.attr('data-target');
160-
var $input = $('#' + targetId);
161-
162-
// Make field editable and clear it
163-
$input.prop('readonly', false).val('').focus();
164-
165-
// Change button text
166-
$button.text('<?php echo esc_js( __( 'Cancel', 'visualizer' ) ); ?>');
167-
$button.removeClass('visualizer-change-key').addClass('visualizer-cancel-change');
168-
});
169-
170-
$(document).on('click', '.visualizer-cancel-change', function() {
171-
var $button = $(this);
172-
var targetId = $button.attr('data-target');
173-
var $input = $('#' + targetId);
174-
var originalValue = $input.attr('data-original');
175-
176-
// Restore readonly and original masked value
177-
$input.prop('readonly', true).val(originalValue || '');
178-
179-
// Change button text back
180-
$button.text('<?php echo esc_js( __( 'Change Key', 'visualizer' ) ); ?>');
181-
$button.removeClass('visualizer-cancel-change').addClass('visualizer-change-key');
182-
});
183-
184-
// Store original masked values
185-
$('input[type="text"][id^="visualizer_"]').each(function() {
186-
$(this).attr('data-original', $(this).val());
187-
});
188-
});
189-
</script>
190-
<?php
191-
192150
echo '</div>'; // End opacity wrapper
193151

194152
if ( $is_locked ) {

classes/Visualizer/Render/Page/Types.php

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ protected function _toHTML() {
5252
* @access protected
5353
*/
5454
protected function _renderContent() {
55-
echo '<div id="type-picker">';
55+
echo '<div id="type-picker" style="scroll-margin-top: 0; scroll-padding-top: 0;">';
5656

5757
// AI Image Upload Section
5858
$has_ai_keys = ! empty( get_option( 'visualizer_openai_api_key', '' ) ) ||
@@ -170,42 +170,55 @@ protected function _renderContent() {
170170
}
171171
echo '</div>';
172172

173-
// Ensure the view scrolls to top when loaded (keep AI image upload section visible)
174-
// Aggressive scroll prevention to override any auto-scroll behavior
173+
// Prevent browser from auto-scrolling to checked radio buttons
175174
echo '<script type="text/javascript">';
176175
echo '(function() {';
176+
echo ' // Prevent autoscroll to checked radio by temporarily unchecking all radios during page load';
177+
echo ' var checkedRadio = null;';
178+
echo ' var radios = document.querySelectorAll("input.type-radio[checked]");';
179+
echo ' if (radios.length > 0) {';
180+
echo ' checkedRadio = radios[0];';
181+
echo ' checkedRadio.removeAttribute("checked");';
182+
echo ' }';
183+
echo ' ';
184+
echo ' // Force scroll to top immediately and continuously';
177185
echo ' var scrollLocked = true;';
178-
echo ' var scrollAttempts = 0;';
179186
echo ' function forceScrollToTop() {';
180187
echo ' if (scrollLocked) {';
181-
echo ' scrollAttempts++;';
182188
echo ' window.scrollTo(0, 0);';
183189
echo ' document.documentElement.scrollTop = 0;';
184190
echo ' document.body.scrollTop = 0;';
185-
echo ' if (window.parent !== window && window.parent.scrollTo) {';
186-
echo ' try { ';
191+
echo ' try { ';
192+
echo ' if (window.parent !== window) {';
187193
echo ' window.parent.scrollTo(0, 0); ';
188194
echo ' window.parent.document.documentElement.scrollTop = 0;';
189195
echo ' window.parent.document.body.scrollTop = 0;';
190-
echo ' } catch(e) {}';
191-
echo ' }';
196+
echo ' }';
197+
echo ' } catch(e) {}';
192198
echo ' }';
193199
echo ' }';
200+
echo ' ';
201+
echo ' // Immediate execution';
202+
echo ' forceScrollToTop();';
203+
echo ' ';
204+
echo ' // Listen to all scroll events';
194205
echo ' document.addEventListener("scroll", forceScrollToTop, true);';
195206
echo ' window.addEventListener("scroll", forceScrollToTop, true);';
196-
echo ' document.addEventListener("DOMContentLoaded", forceScrollToTop);';
197-
echo ' window.addEventListener("load", forceScrollToTop);';
198-
echo ' if (document.readyState === "loading") {';
199-
echo ' document.addEventListener("readystatechange", forceScrollToTop);';
200-
echo ' }';
201-
echo ' var intervals = [0, 10, 50, 100, 150, 200, 250, 300, 400, 500, 600, 800, 1000, 1200, 1500, 2000, 2500];';
202-
echo ' intervals.forEach(function(delay) {';
203-
echo ' setTimeout(forceScrollToTop, delay);';
207+
echo ' ';
208+
echo ' // On DOM ready, restore the checked radio without scrolling';
209+
echo ' document.addEventListener("DOMContentLoaded", function() {';
210+
echo ' forceScrollToTop();';
211+
echo ' if (checkedRadio) {';
212+
echo ' checkedRadio.checked = true;';
213+
echo ' }';
204214
echo ' });';
205-
echo ' setTimeout(function() { ';
206-
echo ' scrollLocked = false; ';
207-
echo ' console.log("Visualizer: Scroll lock released after " + scrollAttempts + " forced resets");';
208-
echo ' }, 3000);';
215+
echo ' ';
216+
echo ' // Keep forcing scroll to top at intervals';
217+
echo ' var intervals = [0, 10, 50, 100, 150, 200, 300, 500, 800, 1000, 1500, 2000];';
218+
echo ' intervals.forEach(function(delay) { setTimeout(forceScrollToTop, delay); });';
219+
echo ' ';
220+
echo ' // Release lock after 2 seconds';
221+
echo ' setTimeout(function() { scrollLocked = false; }, 2000);';
209222
echo '})();';
210223
echo '</script>';
211224
}

0 commit comments

Comments
 (0)