Skip to content

Commit 4e52b89

Browse files
Make API keys non-retrievable and fix scroll locking
Two critical fixes per user requirements: 1. API Keys Fully Secured (AISettings.php) - Removed toggle button - keys are now completely non-retrievable - Changed all input fields to always show empty value (never display stored key) - Added green checkmark indicator when key is configured - Only update database if new non-empty value is entered - If user forgets key, they must enter new one (security by design) Security model: - Once saved, API keys cannot be viewed in dashboard - No visibility toggle, no masked display, completely hidden - Placeholder shows "API key is set (enter new key to replace)" - Maximum security - keys only retrievable from database, not UI 2. Aggressive Scroll Lock (Types.php) - Added scroll event listener that prevents ANY scrolling for 2.5 seconds - Multiple setTimeout intervals: 0, 50, 100, 200, 300, 500, 800, 1000, 1500, 2000ms - Forces scroll to 0,0 on both window and parent window (iframe) - Scroll lock automatically releases after 2.5 seconds for user control - Overrides any lazy-loaded JavaScript causing auto-scroll This ensures AI image upload section stays visible when modal opens, regardless of any async content loading or cached JavaScript. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7d7b35b commit 4e52b89

2 files changed

Lines changed: 39 additions & 50 deletions

File tree

classes/Visualizer/Render/Page/AISettings.php

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,11 @@ 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 '<div style="position: relative; display: inline-block; width: 100%;">';
120-
echo '<input type="password" id="visualizer_openai_api_key" name="visualizer_openai_api_key" value="' . esc_attr( $openai_key ) . '" class="regular-text visualizer-api-key-input" placeholder="' . ( $has_openai_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : '' ) . '" autocomplete="off" />';
121-
echo '<button type="button" class="button visualizer-toggle-key" data-target="visualizer_openai_api_key" style="margin-left: 5px; vertical-align: top;">';
122-
echo '<span class="dashicons dashicons-visibility" style="margin-top: 3px;"></span>';
123-
echo '</button>';
124-
echo '</div>';
119+
echo '<input type="password" id="visualizer_openai_api_key" name="visualizer_openai_api_key" value="" class="regular-text" placeholder="' . ( $has_openai_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : esc_attr__( 'Enter API key', 'visualizer' ) ) . '" autocomplete="off" />';
120+
if ( $has_openai_key ) {
121+
echo '<input type="hidden" name="visualizer_openai_api_key_exists" value="1" />';
122+
echo '<p class="description" style="color: #46b450; font-weight: 500;"><span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> ' . esc_html__( 'API key is configured', 'visualizer' ) . '</p>';
123+
}
125124
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>';
126125
echo '</td>';
127126
echo '</tr>';
@@ -130,12 +129,11 @@ protected function _renderContent() {
130129
echo '<tr>';
131130
echo '<th scope="row"><label for="visualizer_gemini_api_key">' . esc_html__( 'Google Gemini API Key', 'visualizer' ) . '</label></th>';
132131
echo '<td>';
133-
echo '<div style="position: relative; display: inline-block; width: 100%;">';
134-
echo '<input type="password" id="visualizer_gemini_api_key" name="visualizer_gemini_api_key" value="' . esc_attr( $gemini_key ) . '" class="regular-text visualizer-api-key-input" placeholder="' . ( $has_gemini_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : '' ) . '" autocomplete="off" />';
135-
echo '<button type="button" class="button visualizer-toggle-key" data-target="visualizer_gemini_api_key" style="margin-left: 5px; vertical-align: top;">';
136-
echo '<span class="dashicons dashicons-visibility" style="margin-top: 3px;"></span>';
137-
echo '</button>';
138-
echo '</div>';
132+
echo '<input type="password" id="visualizer_gemini_api_key" name="visualizer_gemini_api_key" value="" class="regular-text" placeholder="' . ( $has_gemini_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : esc_attr__( 'Enter API key', 'visualizer' ) ) . '" autocomplete="off" />';
133+
if ( $has_gemini_key ) {
134+
echo '<input type="hidden" name="visualizer_gemini_api_key_exists" value="1" />';
135+
echo '<p class="description" style="color: #46b450; font-weight: 500;"><span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> ' . esc_html__( 'API key is configured', 'visualizer' ) . '</p>';
136+
}
139137
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>';
140138
echo '</td>';
141139
echo '</tr>';
@@ -144,12 +142,11 @@ protected function _renderContent() {
144142
echo '<tr>';
145143
echo '<th scope="row"><label for="visualizer_claude_api_key">' . esc_html__( 'Anthropic Claude API Key', 'visualizer' ) . '</label></th>';
146144
echo '<td>';
147-
echo '<div style="position: relative; display: inline-block; width: 100%;">';
148-
echo '<input type="password" id="visualizer_claude_api_key" name="visualizer_claude_api_key" value="' . esc_attr( $claude_key ) . '" class="regular-text visualizer-api-key-input" placeholder="' . ( $has_claude_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : '' ) . '" autocomplete="off" />';
149-
echo '<button type="button" class="button visualizer-toggle-key" data-target="visualizer_claude_api_key" style="margin-left: 5px; vertical-align: top;">';
150-
echo '<span class="dashicons dashicons-visibility" style="margin-top: 3px;"></span>';
151-
echo '</button>';
152-
echo '</div>';
145+
echo '<input type="password" id="visualizer_claude_api_key" name="visualizer_claude_api_key" value="" class="regular-text" placeholder="' . ( $has_claude_key ? esc_attr__( 'API key is set (enter new key to replace)', 'visualizer' ) : esc_attr__( 'Enter API key', 'visualizer' ) ) . '" autocomplete="off" />';
146+
if ( $has_claude_key ) {
147+
echo '<input type="hidden" name="visualizer_claude_api_key_exists" value="1" />';
148+
echo '<p class="description" style="color: #46b450; font-weight: 500;"><span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span> ' . esc_html__( 'API key is configured', 'visualizer' ) . '</p>';
149+
}
153150
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>';
154151
echo '</td>';
155152
echo '</tr>';
@@ -162,28 +159,6 @@ protected function _renderContent() {
162159

163160
echo '</form>';
164161

165-
// Add JavaScript to handle show/hide toggle
166-
?>
167-
<script type="text/javascript">
168-
jQuery(document).ready(function($) {
169-
$('.visualizer-toggle-key').on('click', function() {
170-
var $button = $(this);
171-
var targetId = $button.attr('data-target');
172-
var $input = $('#' + targetId);
173-
var $icon = $button.find('.dashicons');
174-
175-
if ($input.attr('type') === 'password') {
176-
$input.attr('type', 'text');
177-
$icon.removeClass('dashicons-visibility').addClass('dashicons-hidden');
178-
} else {
179-
$input.attr('type', 'password');
180-
$icon.removeClass('dashicons-hidden').addClass('dashicons-visibility');
181-
}
182-
});
183-
});
184-
</script>
185-
<?php
186-
187162
echo '</div>'; // End opacity wrapper
188163

189164
if ( $is_locked ) {
@@ -202,15 +177,18 @@ protected function _renderContent() {
202177
* @return void
203178
*/
204179
private function _saveSettings() {
205-
if ( isset( $_POST['visualizer_openai_api_key'] ) ) {
180+
// Only update OpenAI key if a new value is provided
181+
if ( isset( $_POST['visualizer_openai_api_key'] ) && ! empty( $_POST['visualizer_openai_api_key'] ) ) {
206182
update_option( 'visualizer_openai_api_key', sanitize_text_field( $_POST['visualizer_openai_api_key'] ) );
207183
}
208184

209-
if ( isset( $_POST['visualizer_gemini_api_key'] ) ) {
185+
// Only update Gemini key if a new value is provided
186+
if ( isset( $_POST['visualizer_gemini_api_key'] ) && ! empty( $_POST['visualizer_gemini_api_key'] ) ) {
210187
update_option( 'visualizer_gemini_api_key', sanitize_text_field( $_POST['visualizer_gemini_api_key'] ) );
211188
}
212189

213-
if ( isset( $_POST['visualizer_claude_api_key'] ) ) {
190+
// Only update Claude key if a new value is provided
191+
if ( isset( $_POST['visualizer_claude_api_key'] ) && ! empty( $_POST['visualizer_claude_api_key'] ) ) {
214192
update_option( 'visualizer_claude_api_key', sanitize_text_field( $_POST['visualizer_claude_api_key'] ) );
215193
}
216194
}

classes/Visualizer/Render/Page/Types.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,26 @@ protected function _renderContent() {
171171
echo '</div>';
172172

173173
// Ensure the view scrolls to top when loaded (keep AI image upload section visible)
174+
// Aggressive scroll prevention to override any auto-scroll behavior
174175
echo '<script type="text/javascript">';
175176
echo '(function() {';
176-
echo ' function scrollToTop() { window.scrollTo(0, 0); if (window.parent !== window) { window.parent.scrollTo(0, 0); } }';
177-
echo ' window.addEventListener("DOMContentLoaded", scrollToTop);';
178-
echo ' window.addEventListener("load", scrollToTop);';
179-
echo ' setTimeout(scrollToTop, 100);';
180-
echo ' setTimeout(scrollToTop, 300);';
181-
echo ' setTimeout(scrollToTop, 500);';
182-
echo ' setTimeout(scrollToTop, 1000);';
177+
echo ' var scrollLocked = true;';
178+
echo ' function forceScrollToTop() {';
179+
echo ' if (scrollLocked) {';
180+
echo ' window.scrollTo(0, 0);';
181+
echo ' if (window.parent !== window && window.parent.scrollTo) {';
182+
echo ' try { window.parent.scrollTo(0, 0); } catch(e) {}';
183+
echo ' }';
184+
echo ' }';
185+
echo ' }';
186+
echo ' window.addEventListener("scroll", forceScrollToTop, true);';
187+
echo ' window.addEventListener("DOMContentLoaded", forceScrollToTop);';
188+
echo ' window.addEventListener("load", forceScrollToTop);';
189+
echo ' var intervals = [0, 50, 100, 200, 300, 500, 800, 1000, 1500, 2000];';
190+
echo ' intervals.forEach(function(delay) {';
191+
echo ' setTimeout(forceScrollToTop, delay);';
192+
echo ' });';
193+
echo ' setTimeout(function() { scrollLocked = false; }, 2500);';
183194
echo '})();';
184195
echo '</script>';
185196
}

0 commit comments

Comments
 (0)