Skip to content

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

saifsultanc
Copy link
Contributor

Context

⛑️ Ticket(s): https://secure.helpscout.net/conversation/2957026305/84491

Summary

Update Stream to Paragraph Field (Multi-field Support)

Copy link

coderabbitai bot commented Jun 5, 2025

Walkthrough

The 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

File(s) Change Summary
gc-openai/gcoai-stream-to-paragraph-field.js Refactored to use an array of field mappings; updated all logic to process each mapping in a loop accordingly.

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
Loading
✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Jun 5, 2025

Warnings
⚠️ When ready, don't forget to request reviews on this pull request from your fellow wizards.

Generated by 🚫 dangerJS against 688c8dc

Copy link

@coderabbitai coderabbitai bot left a 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:

  1. 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
    }
});
  1. Cleanup mechanism: Add a way to remove event listeners if the form is dynamically updated.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 072a5c4 and 688c8dc.

📒 Files selected for processing (1)
  • gc-openai/gcoai-stream-to-paragraph-field.js (1 hunks)

Comment on lines +33 to 69
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');
});
});
Copy link

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.

Suggested change
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

1 participant