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
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 45 additions & 36 deletions gc-openai/gcoai-stream-to-paragraph-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,46 +15,55 @@
*
* 2. Update the variables to match your own field IDs.
*/
var streamFieldId = 3;
var promptFieldId = 1;
var responseFieldId = 4;
var appendButtonFieldId = responseFieldId;
// 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).
];

var $streamFieldInput = $( `#input_GFFORMID_${streamFieldId}` );
var $streamButton = $streamFieldInput.closest( '.gfield' ).find( '.gcoai-trigger' );
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');
});
});
Comment on lines +33 to 69
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.

Loading