diff --git a/assets/js/editor.js b/assets/js/editor.js index 4fbd603..03a0d0a 100644 --- a/assets/js/editor.js +++ b/assets/js/editor.js @@ -5,6 +5,10 @@ * Classic editor metabox. Both implementations share the same REST endpoint * and language list. * + * Content translation is performed in parallel chunks so that long posts do not + * hit server-side timeout limits. Title and excerpt are short fields and are + * translated in a single request using the standard /translate endpoint. + * * @package ROBOTSTXT\AI_Translator * @since 1.0.0 */ @@ -27,12 +31,201 @@ var apiFetch = window.wp && window.wp.apiFetch ? window.wp.apiFetch : null; var labels = data.i18n || {}; + // ── Chunking helpers ────────────────────────────────────────────────────── + /** - * Calls the REST endpoint to translate the selected fields of the saved post. + * Splits Gutenberg block HTML into chunks at block boundaries. * - * @param {Array} fields Field names to translate ('title', 'content', 'excerpt'). + * Each chunk ends on a complete Gutenberg block so the AI never receives + * partial markup. Chunks are kept below maxChars where possible; a single + * block larger than maxChars is sent as its own chunk. + * + * @param {string} content Serialised Gutenberg block HTML. + * @param {number} maxChars Maximum characters per chunk. + * @returns {Array} + */ + function chunkByBlocks( content, maxChars ) { + if ( ! content || content.length <= maxChars ) { + return [ content ]; + } + + // Split just before each block comment opener so every part starts at a + // block boundary. The split delimiter is a zero-width lookahead so each + // part keeps the opening comment intact. + var parts = content.split( /(?=