-
Notifications
You must be signed in to change notification settings - Fork 91
gcoai-stream-to-paragraph-field.js
: Added support for multiple OpenAI streams.
#1118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
gcoai-stream-to-paragraph-field.js
: Added support for multiple OpenAI streams.
#1118
Conversation
WalkthroughThe script was refactored to support multiple sets of field mappings by replacing single field ID variables with an array of mapping objects. All DOM selection and event binding logic was updated to iterate over each mapping, ensuring that the behaviors are applied independently for each configured set of fields. Changes
Sequence Diagram(s)sequenceDiagram
participant Script
participant Mapping[n]
participant StreamField
participant PromptField
participant ResponseField
participant StreamButton
participant ClonedButton
participant TinyMCE
Script->>Mapping[n]: Iterate over each mapping in streamFieldMappings
loop For each mapping
Script->>StreamField: Select stream input
Script->>StreamButton: Select trigger button
Script->>PromptField: Select prompt input
Script->>ResponseField: Select response input
StreamField-->>ResponseField: On change, update response (plain or via TinyMCE)
Script->>ClonedButton: Clone and style button, insert after response field
ClonedButton-->>StreamButton: On click, trigger original button click
alt If TinyMCE enabled
Script->>TinyMCE: Set content from .gcoai-output
end
PromptField-->>StreamButton: On blur, trigger original button click
end
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
gc-openai/gcoai-stream-to-paragraph-field.js (2)
18-31
: Well-structured configuration approach!The refactor to use an array of mapping objects is excellent for scalability and maintainability. The configuration is clear and self-documenting.
Consider adding basic validation to ensure field IDs are valid:
// Add one or more mappings for your Stream Field, Prompt Field, and Response Field. const streamFieldMappings = [ { streamFieldId: 3, // Stream Field. promptFieldId: 1, // Prompt Field. responseFieldId: 4 // Paragraph/Text Field where response content should go. }, { streamFieldId: 6, promptFieldId: 5, responseFieldId: 7 }, // Add more mappings here (as needed). -]; +].filter(mapping => + mapping.streamFieldId && + mapping.promptFieldId && + mapping.responseFieldId +);
33-33
: Consider performance implications for large numbers of mappings.While the current implementation works well for typical use cases, consider the following for scenarios with many mappings:
- Event delegation: Instead of binding individual event listeners, consider using event delegation:
// Example of event delegation approach $(document).on('change', '[id^="input_GFFORMID_"]', function() { const fieldId = this.id.match(/input_GFFORMID_(\d+)/)?.[1]; const mapping = streamFieldMappings.find(m => m.streamFieldId == fieldId); if (mapping) { // Handle the change event } });
- Cleanup mechanism: Add a way to remove event listeners if the form is dynamically updated.
streamFieldMappings.forEach(({ streamFieldId, promptFieldId, responseFieldId }) => { | ||
const $streamFieldInput = $(`#input_GFFORMID_${streamFieldId}`); | ||
const $streamButton = $streamFieldInput.closest('.gfield').find('.gcoai-trigger'); | ||
|
||
$streamFieldInput.on( 'change', function() { | ||
$input = $( `#input_GFFORMID_${responseFieldId}` ); | ||
var inputValue = this.value; | ||
// When the stream field changes (OpenAI output comes in) | ||
$streamFieldInput.on('change', function () { | ||
const $responseInput = $(`#input_GFFORMID_${responseFieldId}`); | ||
const value = this.value; | ||
const tiny = window.tinyMCE && tinyMCE.get($responseInput.attr('id')); | ||
|
||
// Check if the response field has TinyMCE enabled. | ||
var tiny = window.tinyMCE && tinyMCE.get( $input.attr( 'id' ) ); | ||
if (tiny) { | ||
const html = $streamFieldInput.closest('.gfield').find('.gcoai-output').html(); | ||
tiny.setContent(html); | ||
} else { | ||
$responseInput.val(value); | ||
} | ||
}); | ||
|
||
// Get HTML for the response field if TinyMCE is available. | ||
if (tiny) { | ||
var html = $streamFieldInput.closest( '.gfield' ).find('.gcoai-output').html(); | ||
|
||
// Set HTML content in TinyMCE. | ||
tiny.setContent( html ); | ||
} else { | ||
// If TinyMCE is not available, use plain text. | ||
$input.val( inputValue ); | ||
} | ||
}); | ||
// Add a secondary button that re-triggers OpenAI generation | ||
const $newButton = $streamButton | ||
.clone() | ||
.attr('style', 'margin-top: var(--gf-label-space-primary, 8px);') | ||
.on('click', function () { | ||
$streamButton.trigger('click'); | ||
}) | ||
.insertAfter($(`#input_GFFORMID_${responseFieldId}`)); | ||
|
||
let $newButton = $streamButton | ||
.clone() | ||
.attr( 'style', 'margin-top: var(--gf-label-space-primary, 8px);' ) | ||
.on( 'click', function() { | ||
$streamButton.trigger( 'click' ); | ||
} ) | ||
.insertAfter( $( `#input_GFFORMID_${appendButtonFieldId}` ) ); | ||
|
||
$wpEditor = $newButton.parents( '.wp-editor-container' ); | ||
if ( $wpEditor.length ) { | ||
$newButton.insertAfter( $wpEditor ); | ||
} | ||
const $wpEditor = $newButton.parents('.wp-editor-container'); | ||
if ($wpEditor.length) { | ||
$newButton.insertAfter($wpEditor); | ||
} | ||
|
||
$( `#input_GFFORMID_${promptFieldId}` ).on( 'blur', function() { | ||
$streamButton.trigger( 'click' ); | ||
// Optional: auto-trigger generation on prompt field blur | ||
$(`#input_GFFORMID_${promptFieldId}`).on('blur', function () { | ||
$streamButton.trigger('click'); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Core refactoring logic is solid, but add error handling.
The forEach implementation correctly applies the original functionality to each mapping. The destructuring and scoped variable approach is clean and maintainable.
However, add error handling to prevent silent failures when field IDs are incorrect:
streamFieldMappings.forEach(({ streamFieldId, promptFieldId, responseFieldId }) => {
const $streamFieldInput = $(`#input_GFFORMID_${streamFieldId}`);
const $streamButton = $streamFieldInput.closest('.gfield').find('.gcoai-trigger');
+
+ // Skip this mapping if required elements are not found
+ if (!$streamFieldInput.length || !$streamButton.length) {
+ console.warn(`Skipping mapping: Missing elements for streamFieldId ${streamFieldId}`);
+ return;
+ }
// When the stream field changes (OpenAI output comes in)
$streamFieldInput.on('change', function () {
const $responseInput = $(`#input_GFFORMID_${responseFieldId}`);
+ if (!$responseInput.length) {
+ console.warn(`Response field not found: ${responseFieldId}`);
+ return;
+ }
const value = this.value;
const tiny = window.tinyMCE && tinyMCE.get($responseInput.attr('id'));
if (tiny) {
const html = $streamFieldInput.closest('.gfield').find('.gcoai-output').html();
tiny.setContent(html);
} else {
$responseInput.val(value);
}
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
streamFieldMappings.forEach(({ streamFieldId, promptFieldId, responseFieldId }) => { | |
const $streamFieldInput = $(`#input_GFFORMID_${streamFieldId}`); | |
const $streamButton = $streamFieldInput.closest('.gfield').find('.gcoai-trigger'); | |
$streamFieldInput.on( 'change', function() { | |
$input = $( `#input_GFFORMID_${responseFieldId}` ); | |
var inputValue = this.value; | |
// When the stream field changes (OpenAI output comes in) | |
$streamFieldInput.on('change', function () { | |
const $responseInput = $(`#input_GFFORMID_${responseFieldId}`); | |
const value = this.value; | |
const tiny = window.tinyMCE && tinyMCE.get($responseInput.attr('id')); | |
// Check if the response field has TinyMCE enabled. | |
var tiny = window.tinyMCE && tinyMCE.get( $input.attr( 'id' ) ); | |
if (tiny) { | |
const html = $streamFieldInput.closest('.gfield').find('.gcoai-output').html(); | |
tiny.setContent(html); | |
} else { | |
$responseInput.val(value); | |
} | |
}); | |
// Get HTML for the response field if TinyMCE is available. | |
if (tiny) { | |
var html = $streamFieldInput.closest( '.gfield' ).find('.gcoai-output').html(); | |
// Set HTML content in TinyMCE. | |
tiny.setContent( html ); | |
} else { | |
// If TinyMCE is not available, use plain text. | |
$input.val( inputValue ); | |
} | |
}); | |
// Add a secondary button that re-triggers OpenAI generation | |
const $newButton = $streamButton | |
.clone() | |
.attr('style', 'margin-top: var(--gf-label-space-primary, 8px);') | |
.on('click', function () { | |
$streamButton.trigger('click'); | |
}) | |
.insertAfter($(`#input_GFFORMID_${responseFieldId}`)); | |
let $newButton = $streamButton | |
.clone() | |
.attr( 'style', 'margin-top: var(--gf-label-space-primary, 8px);' ) | |
.on( 'click', function() { | |
$streamButton.trigger( 'click' ); | |
} ) | |
.insertAfter( $( `#input_GFFORMID_${appendButtonFieldId}` ) ); | |
$wpEditor = $newButton.parents( '.wp-editor-container' ); | |
if ( $wpEditor.length ) { | |
$newButton.insertAfter( $wpEditor ); | |
} | |
const $wpEditor = $newButton.parents('.wp-editor-container'); | |
if ($wpEditor.length) { | |
$newButton.insertAfter($wpEditor); | |
} | |
$( `#input_GFFORMID_${promptFieldId}` ).on( 'blur', function() { | |
$streamButton.trigger( 'click' ); | |
// Optional: auto-trigger generation on prompt field blur | |
$(`#input_GFFORMID_${promptFieldId}`).on('blur', function () { | |
$streamButton.trigger('click'); | |
}); | |
}); | |
streamFieldMappings.forEach(({ streamFieldId, promptFieldId, responseFieldId }) => { | |
const $streamFieldInput = $(`#input_GFFORMID_${streamFieldId}`); | |
const $streamButton = $streamFieldInput.closest('.gfield').find('.gcoai-trigger'); | |
// Skip this mapping if required elements are not found | |
if (!$streamFieldInput.length || !$streamButton.length) { | |
console.warn(`Skipping mapping: Missing elements for streamFieldId ${streamFieldId}`); | |
return; | |
} | |
// When the stream field changes (OpenAI output comes in) | |
$streamFieldInput.on('change', function () { | |
const $responseInput = $(`#input_GFFORMID_${responseFieldId}`); | |
if (!$responseInput.length) { | |
console.warn(`Response field not found: ${responseFieldId}`); | |
return; | |
} | |
const value = this.value; | |
const tiny = window.tinyMCE && tinyMCE.get($responseInput.attr('id')); | |
if (tiny) { | |
const html = $streamFieldInput.closest('.gfield').find('.gcoai-output').html(); | |
tiny.setContent(html); | |
} else { | |
$responseInput.val(value); | |
} | |
}); | |
// Add a secondary button that re-triggers OpenAI generation | |
const $newButton = $streamButton | |
.clone() | |
.attr('style', 'margin-top: var(--gf-label-space-primary, 8px);') | |
.on('click', () => { | |
$streamButton.trigger('click'); | |
}) | |
.insertAfter($(`#input_GFFORMID_${responseFieldId}`)); | |
const $wpEditor = $newButton.parents('.wp-editor-container'); | |
if ($wpEditor.length) { | |
$newButton.insertAfter($wpEditor); | |
} | |
// Optional: auto-trigger generation on prompt field blur | |
$(`#input_GFFORMID_${promptFieldId}`).on('blur', () => { | |
$streamButton.trigger('click'); | |
}); | |
}); |
🤖 Prompt for AI Agents
In gc-openai/gcoai-stream-to-paragraph-field.js between lines 33 and 69, the
forEach loop processes multiple field mappings but lacks error handling for
invalid or missing field IDs. To fix this, add checks after selecting elements
like $streamFieldInput, $streamButton, and others to verify they exist before
proceeding. If any element is not found, log an error or warning and skip
further processing for that iteration to prevent silent failures.
Context
⛑️ Ticket(s): https://secure.helpscout.net/conversation/2957026305/84491
Summary
Update Stream to Paragraph Field (Multi-field Support)