Plugin Directory

Changeset 3401075


Ignore:
Timestamp:
11/22/2025 08:03:55 PM (4 months ago)
Author:
primetimejas
Message:

Update to version v0.2.4 from GitHub

Location:
kaigen
Files:
16 edited
1 copied

Legend:

Unmodified
Added
Removed
  • kaigen/tags/v0.2.4/build/index.asset.php

    r3361350 r3401075  
    1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-rich-text'), 'version' => 'fdcac31f2e1ee5e68851');
     1<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-rich-text'), 'version' => 'fd9194ab12765b897e21');
  • kaigen/tags/v0.2.4/build/index.js

    r3361350 r3401075  
    1 (()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var a=e.g.document;if(!t&&a&&(a.currentScript&&(t=a.currentScript.src),!t)){var r=a.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=r[n--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})();const t=window.React,a=window.wp.element,r=window.wp.components,n=e.p+"images/KaiGen-logo-128x128.44b1814b.png",o=async(e,t,a={})=>{try{const r=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!r)throw new Error("No provider configured. Please check your plugin settings.");const n=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key;if(!n)throw new Error("No API key configured for the selected provider. Please add one in the KaiGen settings.");const o={prompt:e,provider:r};a.sourceImageUrl&&(o.source_image_url=a.sourceImageUrl),a.additionalImageUrls&&Array.isArray(a.additionalImageUrls)&&(o.additional_image_urls=a.additionalImageUrls),a.maskUrl&&(o.mask_url=a.maskUrl),a.moderation&&["auto","low"].includes(a.moderation)&&(o.moderation=a.moderation),a.style&&["natural","vivid"].includes(a.style)&&(o.style=a.style),a.aspectRatio&&["1:1","16:9","9:16","4:3","3:4"].includes(a.aspectRatio)&&(o.aspect_ratio=a.aspectRatio);const i=await wp.apiFetch({path:"/kaigen/v1/generate-image",method:"POST",data:o});if(i.code&&i.message){if("content_moderation"===i.code)throw new Error(i.message);if("replicate_error"===i.code)throw new Error("Image generation failed: "+i.message);throw new Error(i.message)}if(!i||!i.url)throw new Error("Invalid response from server: "+JSON.stringify(i));i.id&&"number"==typeof i.id&&i.id>0?t({url:i.url,alt:e,id:i.id,caption:""}):t({url:i.url,alt:e,caption:""})}catch(e){console.error("Image generation failed:",e),e.message&&console.error("Error message:",e.message),e.stack&&console.error("Error stack:",e.stack),t({error:e.message||"An unknown error occurred while generating the image"})}},i=({onSelect:e,shouldDisplay:i})=>{const[l,c]=(0,a.useState)(!1),[s,d]=(0,a.useState)(""),[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(null),[k,b]=(0,a.useState)([]),[w,E]=(0,a.useState)(null),[f,h]=(0,a.useState)("1:1"),y=window.kaiGen?.supportsImageToImage||!1;(0,a.useEffect)((()=>{l&&y&&(async()=>{try{const e=await wp.apiFetch({path:"/kaigen/v1/reference-images",method:"GET"});return Array.isArray(e)?e:[]}catch(e){return console.error("Failed to fetch reference images:",e),[]}})().then(b)}),[l]);const v=()=>{if(!s.trim())return void p("Please enter a prompt for image generation.");g(!0),p(null);const t={};w&&(t.sourceImageUrl=w.url),f&&(t.aspectRatio=f),o(s.trim(),(t=>{t.error?(p(t.error),g(!1)):(e(t),g(!1),c(!1))}),t)};return i?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Button,{onClick:()=>c(!0),className:"kaigen-placeholder-button","aria-label":"KaiGen",role:"button",title:"KaiGen",style:{order:10}},(0,t.createElement)("img",{src:n,alt:"KaiGen","aria-label":"KaiGen logo",role:"button",title:"KaiGen logo",style:{width:"64px",height:"64px"}})),l&&(0,t.createElement)(r.Modal,{className:"kaigen-modal",title:(0,t.createElement)("div",{className:"kaigen-modal__logo-container"},(0,t.createElement)("img",{src:n,alt:"KaiGen logo",className:"kaigen-modal__logo"})),"aria-label":"KaiGen",onRequestClose:()=>c(!1)},u&&(0,t.createElement)("p",{className:"kaigen-error-text"},u),(0,t.createElement)("div",{className:"kaigen-modal__input-container"},y&&k.length>0&&(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-start",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__ref-button "+(w?"kaigen-ref-button-selected":""),onClick:a,"aria-expanded":e,"aria-label":"Reference Images"},(0,t.createElement)(r.Dashicon,{icon:"format-image",className:w?"kaigen-ref-button-icon-selected":""})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Reference Images"),(0,t.createElement)("div",{className:"kaigen-modal-reference-images-container"},k.map((e=>(0,t.createElement)("img",{key:e.id,src:e.url,alt:e.alt||"",onClick:()=>w&&w.id===e.id?E(null):E(e),className:"kaigen-modal-reference-image "+(w&&w.id===e.id?"kaigen-modal-reference-image-selected":"")})))))}),(0,t.createElement)("div",{className:"kaigen-modal__textarea-container"},(0,t.createElement)(r.TextareaControl,{className:"kaigen-modal__textarea",placeholder:"Image prompt...",value:s,onChange:d,onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),v())},rows:2})),s.trim()&&(0,t.createElement)(r.Button,{className:"kaigen-modal__submit-button",variant:"primary",onClick:v,disabled:m||!s.trim(),"aria-label":"Generate Image"},m?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null)):(0,t.createElement)(r.Dashicon,{icon:"admin-appearance"})),(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-end",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__settings-button",onClick:a,"aria-expanded":e,"aria-label":"Settings"},(0,t.createElement)(r.Dashicon,{icon:"admin-generic"})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Aspect Ratio"),(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-container"},[{value:"1:1",label:"1:1",title:"Square"},{value:"16:9",label:"16:9",title:"Landscape"},{value:"9:16",label:"9:16",title:"Portrait"}].map((e=>(0,t.createElement)("div",{key:e.value,onClick:()=>h((t=>t===e.value?null:e.value)),"aria-pressed":f===e.value,"aria-label":`${e.title} (${e.label})`,className:"kaigen-modal__aspect-ratio-button "+(f===e.value?"kaigen-modal__aspect-ratio-button-selected":"")},(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-icon-container"},(0,t.createElement)("div",{className:`kaigen-modal-aspect-ratio-icon ${f===e.value?"kaigen-modal-aspect-ratio-icon-selected":""} kaigen-aspect-ratio-${e.value.replace(":","-")}`})),(0,t.createElement)("span",{className:"kaigen-modal-aspect-ratio-label"},e.label))))))})))):null},l=e.p+"images/KaiGen-logo-64x64.a6e617bd.png",c=({isGenerating:e,onGenerateImage:n,isRegenerating:o,onRegenerateImage:i,isImageBlock:c,isTextSelected:s,supportsImageToImage:d})=>{const[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(""),[k,b]=(0,a.useState)(null);return c&&d?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:o?(0,t.createElement)(r.Spinner,null):(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-toolbar-icon"}),label:o?"KaiGen is generating...":"KaiGen",onClick:()=>g(!0),disabled:o})),m&&(0,t.createElement)(r.Modal,{title:(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-modal-logo"}),onRequestClose:()=>{g(!1),p(""),b(null)}},k&&(0,t.createElement)("p",{className:"kaigen-error-text"},k),(0,t.createElement)(r.TextareaControl,{label:"Editing Instructions (optional)",value:u,onChange:p,rows:4}),(0,t.createElement)(r.Button,{variant:"primary",onClick:()=>{i(u.trim()),g(!1),p(""),b(null)},disabled:o},o?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null),"Regenerating..."):"Regenerate Image"))):s?(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:e?(0,t.createElement)(r.Spinner,null):"format-image",label:e?"KaiGen is generating...":"KaiGen",onClick:n,disabled:e})):null},s=window.wp.blockEditor,d=window.wp.data;(0,window.wp.richText.registerFormatType)("kaigen/custom-format",{title:"AI Image Gen",tagName:"span",className:"kaigen-format",edit:({isActive:e,value:r,onChange:n})=>{const[i,l]=(0,a.useState)(!1),m=(0,d.useSelect)((e=>e("core/block-editor").getSelectedBlock()),[]),{replaceBlocks:g}=(0,d.useDispatch)("core/block-editor"),u=(0,a.useCallback)((()=>{if(m&&"core/paragraph"===m.name){const e=r.text.slice(r.start,r.end).trim();if(!e)return void wp.data.dispatch("core/notices").createErrorNotice("Please select some text to use as the image generation prompt.",{type:"snackbar"});const t=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!t)return void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please set one in the plugin settings.",{type:"snackbar"});const a=wp.blocks.createBlock("core/heading",{content:"Generating AI image...",level:2,className:"kaigen-text-center"});g(m.clientId,[a,m]),l(!0),o(e,(e=>{if(l(!1),e.error)console.error("Image generation failed:",e.error),wp.data.dispatch("core/notices").createErrorNotice("Failed to generate image: "+e.error,{type:"snackbar"}),g(a.clientId,[]);else{let t={url:e.url,alt:e.alt,caption:""};e.id&&"number"==typeof e.id&&e.id>0&&(t.id=e.id);const r=wp.blocks.createBlock("core/image",t);g(a.clientId,[r])}}))}}),[m,r.text,r.start,r.end,g]),p=""!==r.text.slice(r.start,r.end).trim();return(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isGenerating:i,onGenerateImage:u,isTextSelected:p}))}});const m=window.wp.hooks;(0,m.addFilter)("editor.MediaUpload","kaigen/add-ai-tab",(e=>a=>{const r=a.allowedTypes&&a.allowedTypes.includes("image")&&!a.multiple,n=wp.data.select("core/block-editor").getSelectedBlock(),o=n&&"core/image"===n.name,l=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key,c=r&&o&&!(n&&n.attributes&&n.attributes.url)&&l;return(0,t.createElement)(e,{...a,render:e=>(0,t.createElement)(t.Fragment,null,a.render(e),(0,t.createElement)(i,{onSelect:a.onSelect,shouldDisplay:c}))})}));const g=window.wp.apiFetch;var u=e.n(g);(0,m.addFilter)("editor.BlockEdit","kaigen/add-regenerate-button",(e=>n=>{if("core/image"!==n.name)return(0,t.createElement)(e,{...n});const i=n.attributes.id&&"number"==typeof n.attributes.id&&n.attributes.id>0,[l,d]=(0,a.useState)(!1),[m,g]=(0,a.useState)(null),[p,k]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{(async()=>{try{const e=window.kaiGen?.provider,t=window.kaiGen?.supportsImageToImage||!1;if(!e)return;k(t)}catch(e){console.error("Failed to initialize provider:",e)}})()}),[]),(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...n}),(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isRegenerating:l,onRegenerateImage:async e=>{g(null);const t=e||n.attributes.alt||"no alt text or prompt, please just enhance",a=window.kaiGen?.provider;if(!a)return console.error("No provider configured"),void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please check your plugin settings.",{type:"snackbar"});d(!0);try{const e=n.attributes.url,a={};p&&e?a.sourceImageUrl=e:p&&!e&&(console.warn("Image-to-image requested but no source image URL available"),wp.data.dispatch("core/notices").createWarningNotice("Image-to-image generation requires a source image. Please ensure the image is properly loaded.",{type:"snackbar"}));const r=await new Promise(((e,r)=>{o(t,(t=>{t.error?r(new Error(t.error)):e(t)}),a)}));r.id&&"number"==typeof r.id&&r.id>0?n.setAttributes({url:r.url,id:r.id}):n.setAttributes({url:r.url,id:void 0}),wp.data.dispatch("core/notices").createSuccessNotice("Image regenerated successfully!",{type:"snackbar"})}catch(e){console.error("Image regeneration failed:",e);let t=e.message||"Unknown error",a="";t.includes("organization verification")?a=" Please verify your organization in the OpenAI dashboard.":t.includes("parameter")?t="API configuration error. Please contact the plugin developer.":t.includes("content policy")&&(a=" Try a different prompt."),wp.data.dispatch("core/notices").createErrorNotice("Failed to regenerate image: "+t+a,{type:"snackbar"})}finally{d(!1)}},isImageBlock:!0,supportsImageToImage:p})),i&&(0,t.createElement)(s.InspectorControls,null,(0,t.createElement)(r.PanelBody,{title:"KaiGen Settings",initialOpen:!1},(0,t.createElement)(r.CheckboxControl,{label:"Reference image",checked:n.attributes.kaigen_reference_image||!1,onChange:async e=>{n.setAttributes({kaigen_reference_image:e});try{await u()({path:`/wp/v2/media/${n.attributes.id}`,method:"POST",data:{meta:{kaigen_reference_image:e?1:0}}})}catch(e){console.error("Failed to update reference image meta:",e),wp.data.dispatch("core/notices").createErrorNotice("Failed to update reference image meta",{type:"snackbar"})}},help:"Add to the list of reference images."}))))})),(0,m.addFilter)("blocks.registerBlockType","kaigen/add-reference-image-attribute",((e,t)=>"core/image"!==t?e:{...e,attributes:{...e.attributes,kaigen_reference_image:{type:"boolean",default:!1}}}))})();
     1(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var a=e.g.document;if(!t&&a&&(a.currentScript&&(t=a.currentScript.src),!t)){var r=a.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=r[n--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})();const t=window.React,a=window.wp.element,r=window.wp.components,n=e.p+"images/KaiGen-logo-128x128.44b1814b.png",o=async(e,t,a={})=>{try{const r=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!r)throw new Error("No provider configured. Please check your plugin settings.");const n=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key;if(!n)throw new Error("No API key configured for the selected provider. Please add one in the KaiGen settings.");const o={prompt:e,provider:r};a.sourceImageUrls&&Array.isArray(a.sourceImageUrls)?o.source_image_urls=a.sourceImageUrls:a.sourceImageUrl&&(o.source_image_url=a.sourceImageUrl),a.additionalImageUrls&&Array.isArray(a.additionalImageUrls)&&(o.additional_image_urls=a.additionalImageUrls),a.maskUrl&&(o.mask_url=a.maskUrl),a.moderation&&["auto","low"].includes(a.moderation)&&(o.moderation=a.moderation),a.style&&["natural","vivid"].includes(a.style)&&(o.style=a.style),a.aspectRatio&&["1:1","16:9","9:16","4:3","3:4"].includes(a.aspectRatio)&&(o.aspect_ratio=a.aspectRatio);const i=await wp.apiFetch({path:"/kaigen/v1/generate-image",method:"POST",data:o});if(i.code&&i.message){if("content_moderation"===i.code)throw new Error(i.message);if("replicate_error"===i.code)throw new Error("Image generation failed: "+i.message);throw new Error(i.message)}if(!i||!i.url)throw new Error("Invalid response from server: "+JSON.stringify(i));i.id&&"number"==typeof i.id&&i.id>0?t({url:i.url,alt:e,id:i.id,caption:""}):t({url:i.url,alt:e,caption:""})}catch(e){console.error("Image generation failed:",e),e.message&&console.error("Error message:",e.message),e.stack&&console.error("Error stack:",e.stack),t({error:e.message||"An unknown error occurred while generating the image"})}},i=({onSelect:e,shouldDisplay:i})=>{const[l,c]=(0,a.useState)(!1),[s,d]=(0,a.useState)(""),[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(null),[k,w]=(0,a.useState)([]),[b,h]=(0,a.useState)([]),[E,f]=(0,a.useState)("1:1"),y=window.kaiGen?.supportsImageToImage||!1,v="replicate"===(wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider||"replicate")?10:16;(0,a.useEffect)((()=>{l&&y&&(async()=>{try{const e=await wp.apiFetch({path:"/kaigen/v1/reference-images",method:"GET"});return Array.isArray(e)?e:[]}catch(e){return console.error("Failed to fetch reference images:",e),[]}})().then(w)}),[l]);const I=()=>{if(!s.trim())return void p("Please enter a prompt for image generation.");g(!0),p(null);const t={};b.length>0&&(t.sourceImageUrls=b.map((e=>e.url))),E&&(t.aspectRatio=E),o(s.trim(),(t=>{t.error?(p(t.error),g(!1)):(e(t),g(!1),c(!1))}),t)};return i?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Button,{onClick:()=>c(!0),className:"kaigen-placeholder-button","aria-label":"KaiGen",role:"button",title:"KaiGen",style:{order:10}},(0,t.createElement)("img",{src:n,alt:"KaiGen","aria-label":"KaiGen logo",role:"button",title:"KaiGen logo",style:{width:"64px",height:"64px"}})),l&&(0,t.createElement)(r.Modal,{className:"kaigen-modal",title:(0,t.createElement)("div",{className:"kaigen-modal__logo-container"},(0,t.createElement)("img",{src:n,alt:"KaiGen logo",className:"kaigen-modal__logo"})),"aria-label":"KaiGen",onRequestClose:()=>c(!1)},u&&(0,t.createElement)("p",{className:"kaigen-error-text"},u),(0,t.createElement)("div",{className:"kaigen-modal__input-container"},y&&k.length>0&&(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-start",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__ref-button "+(b.length>0?"kaigen-ref-button-selected":""),onClick:a,"aria-expanded":e,"aria-label":"Reference Images"},(0,t.createElement)(r.Dashicon,{icon:"format-image",className:b.length>0?"kaigen-ref-button-icon-selected":""})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Reference Images (up to ",v,")"),(0,t.createElement)("div",{className:"kaigen-modal-reference-images-container"},k.map((e=>(0,t.createElement)("img",{key:e.id,src:e.url,alt:e.alt||"",onClick:()=>{h((t=>t.some((t=>t.id===e.id))?t.filter((t=>t.id!==e.id)):t.length<v?[...t,e]:t))},className:"kaigen-modal-reference-image "+(b.some((t=>t.id===e.id))?"kaigen-modal-reference-image-selected":"")})))))}),(0,t.createElement)("div",{className:"kaigen-modal__textarea-container"},(0,t.createElement)(r.TextareaControl,{className:"kaigen-modal__textarea",placeholder:"Image prompt...",value:s,onChange:d,onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),I())},rows:2})),s.trim()&&(0,t.createElement)(r.Button,{className:"kaigen-modal__submit-button",variant:"primary",onClick:I,disabled:m||!s.trim(),"aria-label":"Generate Image"},m?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null)):(0,t.createElement)(r.Dashicon,{icon:"admin-appearance"})),(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-end",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__settings-button",onClick:a,"aria-expanded":e,"aria-label":"Settings"},(0,t.createElement)(r.Dashicon,{icon:"admin-generic"})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Aspect Ratio"),(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-container"},[{value:"1:1",label:"1:1",title:"Square"},{value:"16:9",label:"16:9",title:"Landscape"},{value:"9:16",label:"9:16",title:"Portrait"}].map((e=>(0,t.createElement)("div",{key:e.value,onClick:()=>f((t=>t===e.value?null:e.value)),"aria-pressed":E===e.value,"aria-label":`${e.title} (${e.label})`,className:"kaigen-modal__aspect-ratio-button "+(E===e.value?"kaigen-modal__aspect-ratio-button-selected":"")},(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-icon-container"},(0,t.createElement)("div",{className:`kaigen-modal-aspect-ratio-icon ${E===e.value?"kaigen-modal-aspect-ratio-icon-selected":""} kaigen-aspect-ratio-${e.value.replace(":","-")}`})),(0,t.createElement)("span",{className:"kaigen-modal-aspect-ratio-label"},e.label))))))})))):null},l=e.p+"images/KaiGen-logo-64x64.a6e617bd.png",c=({isGenerating:e,onGenerateImage:n,isRegenerating:o,onRegenerateImage:i,isImageBlock:c,isTextSelected:s,supportsImageToImage:d})=>{const[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(""),[k,w]=(0,a.useState)(null);return c&&d?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:o?(0,t.createElement)(r.Spinner,null):(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-toolbar-icon"}),label:o?"KaiGen is generating...":"KaiGen",onClick:()=>g(!0),disabled:o})),m&&(0,t.createElement)(r.Modal,{title:(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-modal-logo"}),onRequestClose:()=>{g(!1),p(""),w(null)}},k&&(0,t.createElement)("p",{className:"kaigen-error-text"},k),(0,t.createElement)(r.TextareaControl,{label:"Editing Instructions (optional)",value:u,onChange:p,rows:4}),(0,t.createElement)(r.Button,{variant:"primary",onClick:()=>{i(u.trim()),g(!1),p(""),w(null)},disabled:o},o?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null),"Regenerating..."):"Regenerate Image"))):s?(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:e?(0,t.createElement)(r.Spinner,null):"format-image",label:e?"KaiGen is generating...":"KaiGen",onClick:n,disabled:e})):null},s=window.wp.blockEditor,d=window.wp.data;(0,window.wp.richText.registerFormatType)("kaigen/custom-format",{title:"AI Image Gen",tagName:"span",className:"kaigen-format",edit:({isActive:e,value:r,onChange:n})=>{const[i,l]=(0,a.useState)(!1),m=(0,d.useSelect)((e=>e("core/block-editor").getSelectedBlock()),[]),{replaceBlocks:g}=(0,d.useDispatch)("core/block-editor"),u=(0,a.useCallback)((()=>{if(m&&"core/paragraph"===m.name){const e=r.text.slice(r.start,r.end).trim();if(!e)return void wp.data.dispatch("core/notices").createErrorNotice("Please select some text to use as the image generation prompt.",{type:"snackbar"});const t=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!t)return void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please set one in the plugin settings.",{type:"snackbar"});const a=wp.blocks.createBlock("core/heading",{content:"Generating AI image...",level:2,className:"kaigen-text-center"});g(m.clientId,[a,m]),l(!0),o(e,(e=>{if(l(!1),e.error)console.error("Image generation failed:",e.error),wp.data.dispatch("core/notices").createErrorNotice("Failed to generate image: "+e.error,{type:"snackbar"}),g(a.clientId,[]);else{let t={url:e.url,alt:e.alt,caption:""};e.id&&"number"==typeof e.id&&e.id>0&&(t.id=e.id);const r=wp.blocks.createBlock("core/image",t);g(a.clientId,[r])}}))}}),[m,r.text,r.start,r.end,g]),p=""!==r.text.slice(r.start,r.end).trim();return(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isGenerating:i,onGenerateImage:u,isTextSelected:p}))}});const m=window.wp.hooks;(0,m.addFilter)("editor.MediaUpload","kaigen/add-ai-tab",(e=>a=>{const r=a.allowedTypes&&a.allowedTypes.includes("image")&&!a.multiple,n=wp.data.select("core/block-editor").getSelectedBlock(),o=n&&"core/image"===n.name,l=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key,c=r&&o&&!(n&&n.attributes&&n.attributes.url)&&l;return(0,t.createElement)(e,{...a,render:e=>(0,t.createElement)(t.Fragment,null,a.render(e),(0,t.createElement)(i,{onSelect:a.onSelect,shouldDisplay:c}))})}));const g=window.wp.apiFetch;var u=e.n(g);(0,m.addFilter)("editor.BlockEdit","kaigen/add-regenerate-button",(e=>n=>{if("core/image"!==n.name)return(0,t.createElement)(e,{...n});const i=n.attributes.id&&"number"==typeof n.attributes.id&&n.attributes.id>0,[l,d]=(0,a.useState)(!1),[m,g]=(0,a.useState)(null),[p,k]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{(async()=>{try{const e=window.kaiGen?.provider,t=window.kaiGen?.supportsImageToImage||!1;if(!e)return;k(t)}catch(e){console.error("Failed to initialize provider:",e)}})()}),[]),(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...n}),(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isRegenerating:l,onRegenerateImage:async e=>{g(null);const t=e||n.attributes.alt||"no alt text or prompt, please just enhance",a=window.kaiGen?.provider;if(!a)return console.error("No provider configured"),void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please check your plugin settings.",{type:"snackbar"});d(!0);try{const e=n.attributes.url,a={};p&&e?a.sourceImageUrl=e:p&&!e&&(console.warn("Image-to-image requested but no source image URL available"),wp.data.dispatch("core/notices").createWarningNotice("Image-to-image generation requires a source image. Please ensure the image is properly loaded.",{type:"snackbar"}));const r=await new Promise(((e,r)=>{o(t,(t=>{t.error?r(new Error(t.error)):e(t)}),a)}));r.id&&"number"==typeof r.id&&r.id>0?n.setAttributes({url:r.url,id:r.id}):n.setAttributes({url:r.url,id:void 0}),wp.data.dispatch("core/notices").createSuccessNotice("Image regenerated successfully!",{type:"snackbar"})}catch(e){console.error("Image regeneration failed:",e);let t=e.message||"Unknown error",a="";t.includes("organization verification")?a=" Please verify your organization in the OpenAI dashboard.":t.includes("parameter")?t="API configuration error. Please contact the plugin developer.":t.includes("content policy")&&(a=" Try a different prompt."),wp.data.dispatch("core/notices").createErrorNotice("Failed to regenerate image: "+t+a,{type:"snackbar"})}finally{d(!1)}},isImageBlock:!0,supportsImageToImage:p})),i&&(0,t.createElement)(s.InspectorControls,null,(0,t.createElement)(r.PanelBody,{title:"KaiGen Settings",initialOpen:!1},(0,t.createElement)(r.CheckboxControl,{label:"Reference image",checked:n.attributes.kaigen_reference_image||!1,onChange:async e=>{n.setAttributes({kaigen_reference_image:e});try{await u()({path:`/wp/v2/media/${n.attributes.id}`,method:"POST",data:{meta:{kaigen_reference_image:e?1:0}}})}catch(e){console.error("Failed to update reference image meta:",e),wp.data.dispatch("core/notices").createErrorNotice("Failed to update reference image meta",{type:"snackbar"})}},help:"Add to the list of reference images."}))))})),(0,m.addFilter)("blocks.registerBlockType","kaigen/add-reference-image-attribute",((e,t)=>"core/image"!==t?e:{...e,attributes:{...e.attributes,kaigen_reference_image:{type:"boolean",default:!1}}}))})();
  • kaigen/tags/v0.2.4/inc/class-admin.php

    r3361350 r3401075  
    200200        // Add quality field
    201201        add_settings_field(
    202             'kaigen_quality_setting',
     202            'kaigen_quality_settings',
    203203            'Image Quality',
    204204            [$this, 'render_quality_field'],
     
    352352     */
    353353    public function render_quality_field() {
    354         $quality_settings = get_option('kaigen_quality_settings', []);
    355         $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     354        $quality = KaiGen_Image_Provider::get_quality_setting();
    356355        ?>
    357356        <select name="kaigen_quality_settings[quality]">
  • kaigen/tags/v0.2.4/inc/class-image-provider.php

    r3315977 r3401075  
    171171     */
    172172    abstract public function process_api_response($response);
     173
     174    /**
     175     * Gets the quality setting from the options.
     176     *
     177     * @return string The quality setting.
     178     */
     179    public static function get_quality_setting() {
     180        $quality_settings = get_option('kaigen_quality_settings', []);
     181
     182        // Ensure we have an array to work with.
     183        if ( ! is_array( $quality_settings ) ) {
     184            $quality_settings = [];
     185        }
     186
     187        // If the quality setting is not set, check for the old option and migrate it.
     188        if ( ! isset( $quality_settings['quality'] ) ) {
     189            $legacy_quality_setting = get_option('kaigen_quality_setting');
     190           
     191            if ( ! empty( $legacy_quality_setting ) && in_array( $legacy_quality_setting, ['low', 'medium', 'high'], true ) ) {
     192                $quality_settings['quality'] = $legacy_quality_setting;
     193                update_option('kaigen_quality_settings', $quality_settings);
     194                delete_option('kaigen_quality_setting');
     195            } else {
     196                // Default to medium quality if neither exists.
     197                $quality_settings['quality'] = 'medium'; 
     198                update_option('kaigen_quality_settings', $quality_settings);
     199            }
     200        }
     201
     202        return $quality_settings['quality'];
     203    }
    173204}
  • kaigen/tags/v0.2.4/inc/class-rest-api.php

    r3361350 r3401075  
    124124        // For Replicate, get the model based on quality setting
    125125        if ($provider_id === 'replicate') {
    126             $quality_settings = get_option('kaigen_quality_settings', []);
    127             $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     126            $quality = KaiGen_Image_Provider::get_quality_setting();
    128127           
    129128            $provider = kaigen_provider_manager()->get_provider($provider_id);
     
    161160    private function get_additional_params($request) {
    162161        // Get saved quality settings
     162        $quality = KaiGen_Image_Provider::get_quality_setting();
     163        $quality_value = $quality === 'hd' ? 100 : 80;
    163164        $quality_settings = get_option('kaigen_quality_settings', []);
    164         $quality_value = isset($quality_settings['quality']) && $quality_settings['quality'] === 'hd' ? 100 : 80;
    165165        $style_value = isset($quality_settings['style']) ? $quality_settings['style'] : 'natural';
    166166       
     
    183183
    184184        // Add source image URL if provided (single or array)
    185         $source_image_url = $request->get_param('source_image_url');
    186         if (!empty($source_image_url)) {
    187             $params['source_image_url'] = $source_image_url;
     185        $source_image_urls = $request->get_param('source_image_urls');
     186        if (!empty($source_image_urls)) {
     187            $params['source_image_urls'] = $source_image_urls;
     188        } else {
     189            $source_image_url = $request->get_param('source_image_url');
     190            if (!empty($source_image_url)) {
     191                $params['source_image_url'] = $source_image_url;
     192            }
    188193        }
    189194       
     
    274279                    'empty_image_data',
    275280                    'replicate_validation_error',
    276                     'content_moderation'
     281                    'content_moderation',
     282                    'api_error',
     283                    'openai_error',
     284                    'max_retries_exceeded',
     285                    'invalid_api_key_format'
    277286                ])) {
    278287                    return $result;
  • kaigen/tags/v0.2.4/inc/providers/class-image-provider-openai.php

    r3315977 r3401075  
    4848        }
    4949
     50        // Handle source image URLs
     51        $source_image_urls = $additional_params['source_image_urls'] ?? [];
    5052        $source_image_url = $additional_params['source_image_url'] ?? null;
    51         $max_retries = 3; // Reduce max retries to fail faster
    52         $timeout = 60; // Set request timeout to 60 seconds
     53        if ($source_image_url) {
     54            array_unshift($source_image_urls, $source_image_url);
     55        }
     56
     57        // Limit the number of source image URLs to 16.
     58        $source_image_urls = array_slice(array_unique($source_image_urls), 0, 16);
     59
     60        $max_retries = 3;
     61        $timeout = 150; // Increased timeout for image generation (docs say up to 2 mins)
    5362        $retry_delay = 2; // Seconds to wait between retries
    5463
     
    6170        add_filter('http_request_args', function($args) use ($timeout) {
    6271            $args['timeout'] = $timeout;
    63             $args['httpversion'] = '1.1';
    6472            $args['sslverify'] = true;
    6573            $args['blocking'] = true;
     
    7078            }
    7179            $args['curl'][CURLOPT_TIMEOUT] = $timeout;
    72             $args['curl'][CURLOPT_CONNECTTIMEOUT] = 10;
    73             $args['curl'][CURLOPT_LOW_SPEED_TIME] = 30; // Increased low speed time
    74             $args['curl'][CURLOPT_LOW_SPEED_LIMIT] = 1024; // 1KB/s minimum speed
     80            $args['curl'][CURLOPT_CONNECTTIMEOUT] = 30; // Increased connect timeout
     81            $args['curl'][CURLOPT_TCP_KEEPALIVE] = 1; // Enable TCP keepalive
     82
     83            // Adjust low speed settings to prevent timeouts on slow generation
     84            $args['curl'][CURLOPT_LOW_SPEED_TIME] = 600; // Wait 10 minutes before timing out due to low speed
     85            $args['curl'][CURLOPT_LOW_SPEED_LIMIT] = 1; // Only timeout if speed is effectively 0
    7586           
    7687            return $args;
     
    8192
    8293        // Log if we're using image-to-image
    83         if ( ! empty( $source_image_url ) ) {
     94        if (!empty($source_image_urls)) {
    8495            $endpoint = self::IMAGE_EDIT_API_BASE_URL;
    8596        }
    8697       
    8798        // Get quality setting from admin options
    88         $quality_settings = get_option('kaigen_quality_settings', []);
    89         $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     99        $quality = self::get_quality_setting();
    90100       
    91101        // Map quality settings to supported values
     
    100110       
    101111        // Prepare the request based on the type of request
    102         if ( ! empty( $source_image_url ) ) {
     112        if (!empty($source_image_urls)) {
    103113            // For image edit requests, we need to use multipart/form-data
    104114            $boundary = wp_generate_password(24, false);
     
    125135            $body .= 'Content-Disposition: form-data; name="quality"' . "\r\n\r\n";
    126136            $body .= $quality . "\r\n";
     137
     138            // Add format parameter (jpeg is faster than png)
     139            $body .= "--{$boundary}\r\n";
     140            $body .= 'Content-Disposition: form-data; name="output_format"' . "\r\n\r\n";
     141            $body .= "jpeg\r\n";
    127142           
    128143            // Add image files
    129             if (is_array($source_image_url)) {
    130                 foreach ($source_image_url as $index => $image_url) {
    131                     $image_data = $this->get_image_data($image_url);
    132                     if (is_wp_error($image_data)) {
    133                         return $image_data;
    134                     }
    135                    
    136                     $body .= "--{$boundary}\r\n";
    137                     $body .= 'Content-Disposition: form-data; name="image[]"; filename="' . basename($image_url) . '"' . "\r\n";
    138                     $body .= 'Content-Type: ' . $this->get_image_mime_type($image_url) . "\r\n\r\n";
    139                     $body .= $image_data . "\r\n";
    140                 }
    141             } else {
    142                 $image_data = $this->get_image_data($source_image_url);
     144            foreach ($source_image_urls as $index => $image_url) {
     145                $image_data = $this->get_image_data($image_url);
    143146                if (is_wp_error($image_data)) {
    144147                    return $image_data;
     
    146149               
    147150                $body .= "--{$boundary}\r\n";
    148                 $body .= 'Content-Disposition: form-data; name="image"; filename="' . basename($source_image_url) . '"' . "\r\n";
    149                 $body .= 'Content-Type: ' . $this->get_image_mime_type($source_image_url) . "\r\n\r\n";
     151                $body .= 'Content-Disposition: form-data; name="image[]"; filename="' . basename($image_url) . '"' . "\r\n";
     152                $body .= 'Content-Type: ' . $this->get_image_mime_type($image_url) . "\r\n\r\n";
    150153                $body .= $image_data . "\r\n";
    151154            }
     
    158161            $headers = $this->get_request_headers();
    159162            $body = [
    160                 'model'   => 'gpt-image-1',
    161                 'prompt'  => $prompt,
    162                 'quality' => $quality,
     163                'model'          => 'gpt-image-1',
     164                'prompt'         => $prompt,
     165                'quality'        => $quality,
     166                'output_format'  => 'jpeg',
    163167            ];
    164            
     168
    165169            // Add size parameter if aspect ratio is specified
    166170            if (isset($additional_params['aspect_ratio'])) {
     
    168172                $body['size'] = "{$width}x{$height}";
    169173            }
    170            
     174
    171175            $body = wp_json_encode($body);
    172176        }
  • kaigen/tags/v0.2.4/inc/providers/class-image-provider-replicate.php

    r3361350 r3401075  
    5252     */
    5353    public function get_current_model() {
    54         // Get all quality-related options to debug
    55         $quality_settings = get_option('kaigen_quality_settings');
    56         $quality_setting = get_option('kaigen_quality_setting');
    57 
    58         // Use the correct option name
    59         $quality = 'medium'; // Default
    60         if (is_array($quality_settings) && isset($quality_settings['quality'])) {
    61             $quality = $quality_settings['quality'];
    62         }
    63        
     54        $quality = self::get_quality_setting();
    6455        $model = $this->get_model_from_quality_setting($quality);
    6556        return $model;
     
    8576        $model_to_use = $this->model;
    8677       
    87         // Handle source_image_url parameter (convert to image_input for Replicate seedream-4)
    88         $source_image_url = $additional_params['source_image_url'] ?? $additional_params['input_image'] ?? null;
    89        
    90         // If source image is provided, use the hardcoded image-to-image model
    91         if (!empty($source_image_url)) {
    92             $model_to_use = $this->get_image_to_image_model();
    93 
    94             // Process image URL (converts to base64 only if local)
    95             $processed_image = $this->process_image_url($source_image_url);
    96             if (is_wp_error($processed_image)) {
    97                 // Return image processing errors immediately without retry
    98                 return $processed_image;
    99             }
    100            
    101             // Use 'image_input' parameter as array for seedream-4 model (confirmed by schema)
    102             $input_data['image_input'] = [$processed_image];
    103         }
    104        
    105         // Remove these parameters to prevent duplication
     78        // Handle source image URLs (can be single string or array)
     79        $source_image_urls = $additional_params['source_image_urls'] ?? $additional_params['source_image_url'] ?? null;
     80        if (!empty($source_image_urls)) {
     81            if (!is_array($source_image_urls)) {
     82                $source_image_urls = [$source_image_urls];
     83            }
     84           
     85            $image_inputs = [];
     86            foreach ($source_image_urls as $url) {
     87                if (count($image_inputs) >= 10) break;
     88                $processed = $this->process_image_url($url);
     89                if (!is_wp_error($processed)) {
     90                    $image_inputs[] = $processed;
     91                } else {
     92                    // Log error but continue with other images
     93                    error_log('Failed to process image: ' . $processed->get_error_message());
     94                }
     95            }
     96           
     97            if (!empty($image_inputs)) {
     98                $model_to_use = $this->get_image_to_image_model();
     99                $input_data['image_input'] = $image_inputs;
     100
     101                // Set size to 1k for low quality image edits, assumes we are using seedream-4 model so if that changes, this will need to be updated.
     102                $quality = self::get_quality_setting();
     103
     104                if ($quality === 'low') {
     105                    $additional_params['size'] = '1K';
     106                }
     107            }
     108        }
     109       
     110        // Remove source image parameters to prevent duplication
     111        unset($additional_params['source_image_urls']);
    106112        unset($additional_params['source_image_url']);
    107113        unset($additional_params['input_image']);
    108114
    109115        // Filter parameters based on the model being used
    110         if (!empty($source_image_url)) {
     116        if (!empty($source_image_urls)) {
    111117            // For seedream-4, only keep valid parameters according to schema
    112118            $valid_params = ['size', 'width', 'height', 'max_images', 'aspect_ratio', 'sequential_image_generation'];
     
    358364            'black-forest-labs/flux-schnell' => 'Flux Schnell by Black Forest Labs (low quality)',
    359365            'bytedance/seedream-4'           => 'Seedream 4 by Bytedance (high quality)',
    360             'google/imagen-4-ultra'          => 'Imagen 4 Ultra by Google (highest quality)',
     366            'google/nano-banana-pro'         => 'Nano Banana Pro by Google (highest quality)',
    361367        ];
    362368    }
    363369
    364370    /**
    365      * Gets the hardcoded image-to-image model for Replicate.
     371     * Gets the image-to-image model for Replicate based on quality setting.
    366372     *
    367373     * @return string The image-to-image model.
    368374     */
    369375    private function get_image_to_image_model() {
    370         return 'bytedance/seedream-4';
     376        $model = 'bytedance/seedream-4';
     377        $quality = self::get_quality_setting();
     378
     379        if ($quality === 'high') {
     380            $model = 'google/nano-banana-pro';
     381        }
     382
     383        return $model;
    371384    }
    372385
     
    395408                break;
    396409            case 'high':
    397                 $model = 'google/imagen-4-ultra';
     410                $model = 'google/nano-banana-pro';
    398411                break;
    399412            default:
  • kaigen/tags/v0.2.4/kaigen.php

    r3361350 r3401075  
    55 * Requires at least: 6.1
    66 * Requires PHP:      7.0
    7  * Version:           0.2.3
     7 * Version:           0.2.4
    88 * Author:            Jacob Schweitzer
    99 * License:           GPL-2.0-or-later
  • kaigen/trunk/build/index.asset.php

    r3361350 r3401075  
    1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-rich-text'), 'version' => 'fdcac31f2e1ee5e68851');
     1<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-rich-text'), 'version' => 'fd9194ab12765b897e21');
  • kaigen/trunk/build/index.js

    r3361350 r3401075  
    1 (()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var a=e.g.document;if(!t&&a&&(a.currentScript&&(t=a.currentScript.src),!t)){var r=a.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=r[n--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})();const t=window.React,a=window.wp.element,r=window.wp.components,n=e.p+"images/KaiGen-logo-128x128.44b1814b.png",o=async(e,t,a={})=>{try{const r=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!r)throw new Error("No provider configured. Please check your plugin settings.");const n=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key;if(!n)throw new Error("No API key configured for the selected provider. Please add one in the KaiGen settings.");const o={prompt:e,provider:r};a.sourceImageUrl&&(o.source_image_url=a.sourceImageUrl),a.additionalImageUrls&&Array.isArray(a.additionalImageUrls)&&(o.additional_image_urls=a.additionalImageUrls),a.maskUrl&&(o.mask_url=a.maskUrl),a.moderation&&["auto","low"].includes(a.moderation)&&(o.moderation=a.moderation),a.style&&["natural","vivid"].includes(a.style)&&(o.style=a.style),a.aspectRatio&&["1:1","16:9","9:16","4:3","3:4"].includes(a.aspectRatio)&&(o.aspect_ratio=a.aspectRatio);const i=await wp.apiFetch({path:"/kaigen/v1/generate-image",method:"POST",data:o});if(i.code&&i.message){if("content_moderation"===i.code)throw new Error(i.message);if("replicate_error"===i.code)throw new Error("Image generation failed: "+i.message);throw new Error(i.message)}if(!i||!i.url)throw new Error("Invalid response from server: "+JSON.stringify(i));i.id&&"number"==typeof i.id&&i.id>0?t({url:i.url,alt:e,id:i.id,caption:""}):t({url:i.url,alt:e,caption:""})}catch(e){console.error("Image generation failed:",e),e.message&&console.error("Error message:",e.message),e.stack&&console.error("Error stack:",e.stack),t({error:e.message||"An unknown error occurred while generating the image"})}},i=({onSelect:e,shouldDisplay:i})=>{const[l,c]=(0,a.useState)(!1),[s,d]=(0,a.useState)(""),[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(null),[k,b]=(0,a.useState)([]),[w,E]=(0,a.useState)(null),[f,h]=(0,a.useState)("1:1"),y=window.kaiGen?.supportsImageToImage||!1;(0,a.useEffect)((()=>{l&&y&&(async()=>{try{const e=await wp.apiFetch({path:"/kaigen/v1/reference-images",method:"GET"});return Array.isArray(e)?e:[]}catch(e){return console.error("Failed to fetch reference images:",e),[]}})().then(b)}),[l]);const v=()=>{if(!s.trim())return void p("Please enter a prompt for image generation.");g(!0),p(null);const t={};w&&(t.sourceImageUrl=w.url),f&&(t.aspectRatio=f),o(s.trim(),(t=>{t.error?(p(t.error),g(!1)):(e(t),g(!1),c(!1))}),t)};return i?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Button,{onClick:()=>c(!0),className:"kaigen-placeholder-button","aria-label":"KaiGen",role:"button",title:"KaiGen",style:{order:10}},(0,t.createElement)("img",{src:n,alt:"KaiGen","aria-label":"KaiGen logo",role:"button",title:"KaiGen logo",style:{width:"64px",height:"64px"}})),l&&(0,t.createElement)(r.Modal,{className:"kaigen-modal",title:(0,t.createElement)("div",{className:"kaigen-modal__logo-container"},(0,t.createElement)("img",{src:n,alt:"KaiGen logo",className:"kaigen-modal__logo"})),"aria-label":"KaiGen",onRequestClose:()=>c(!1)},u&&(0,t.createElement)("p",{className:"kaigen-error-text"},u),(0,t.createElement)("div",{className:"kaigen-modal__input-container"},y&&k.length>0&&(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-start",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__ref-button "+(w?"kaigen-ref-button-selected":""),onClick:a,"aria-expanded":e,"aria-label":"Reference Images"},(0,t.createElement)(r.Dashicon,{icon:"format-image",className:w?"kaigen-ref-button-icon-selected":""})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Reference Images"),(0,t.createElement)("div",{className:"kaigen-modal-reference-images-container"},k.map((e=>(0,t.createElement)("img",{key:e.id,src:e.url,alt:e.alt||"",onClick:()=>w&&w.id===e.id?E(null):E(e),className:"kaigen-modal-reference-image "+(w&&w.id===e.id?"kaigen-modal-reference-image-selected":"")})))))}),(0,t.createElement)("div",{className:"kaigen-modal__textarea-container"},(0,t.createElement)(r.TextareaControl,{className:"kaigen-modal__textarea",placeholder:"Image prompt...",value:s,onChange:d,onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),v())},rows:2})),s.trim()&&(0,t.createElement)(r.Button,{className:"kaigen-modal__submit-button",variant:"primary",onClick:v,disabled:m||!s.trim(),"aria-label":"Generate Image"},m?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null)):(0,t.createElement)(r.Dashicon,{icon:"admin-appearance"})),(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-end",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__settings-button",onClick:a,"aria-expanded":e,"aria-label":"Settings"},(0,t.createElement)(r.Dashicon,{icon:"admin-generic"})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Aspect Ratio"),(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-container"},[{value:"1:1",label:"1:1",title:"Square"},{value:"16:9",label:"16:9",title:"Landscape"},{value:"9:16",label:"9:16",title:"Portrait"}].map((e=>(0,t.createElement)("div",{key:e.value,onClick:()=>h((t=>t===e.value?null:e.value)),"aria-pressed":f===e.value,"aria-label":`${e.title} (${e.label})`,className:"kaigen-modal__aspect-ratio-button "+(f===e.value?"kaigen-modal__aspect-ratio-button-selected":"")},(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-icon-container"},(0,t.createElement)("div",{className:`kaigen-modal-aspect-ratio-icon ${f===e.value?"kaigen-modal-aspect-ratio-icon-selected":""} kaigen-aspect-ratio-${e.value.replace(":","-")}`})),(0,t.createElement)("span",{className:"kaigen-modal-aspect-ratio-label"},e.label))))))})))):null},l=e.p+"images/KaiGen-logo-64x64.a6e617bd.png",c=({isGenerating:e,onGenerateImage:n,isRegenerating:o,onRegenerateImage:i,isImageBlock:c,isTextSelected:s,supportsImageToImage:d})=>{const[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(""),[k,b]=(0,a.useState)(null);return c&&d?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:o?(0,t.createElement)(r.Spinner,null):(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-toolbar-icon"}),label:o?"KaiGen is generating...":"KaiGen",onClick:()=>g(!0),disabled:o})),m&&(0,t.createElement)(r.Modal,{title:(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-modal-logo"}),onRequestClose:()=>{g(!1),p(""),b(null)}},k&&(0,t.createElement)("p",{className:"kaigen-error-text"},k),(0,t.createElement)(r.TextareaControl,{label:"Editing Instructions (optional)",value:u,onChange:p,rows:4}),(0,t.createElement)(r.Button,{variant:"primary",onClick:()=>{i(u.trim()),g(!1),p(""),b(null)},disabled:o},o?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null),"Regenerating..."):"Regenerate Image"))):s?(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:e?(0,t.createElement)(r.Spinner,null):"format-image",label:e?"KaiGen is generating...":"KaiGen",onClick:n,disabled:e})):null},s=window.wp.blockEditor,d=window.wp.data;(0,window.wp.richText.registerFormatType)("kaigen/custom-format",{title:"AI Image Gen",tagName:"span",className:"kaigen-format",edit:({isActive:e,value:r,onChange:n})=>{const[i,l]=(0,a.useState)(!1),m=(0,d.useSelect)((e=>e("core/block-editor").getSelectedBlock()),[]),{replaceBlocks:g}=(0,d.useDispatch)("core/block-editor"),u=(0,a.useCallback)((()=>{if(m&&"core/paragraph"===m.name){const e=r.text.slice(r.start,r.end).trim();if(!e)return void wp.data.dispatch("core/notices").createErrorNotice("Please select some text to use as the image generation prompt.",{type:"snackbar"});const t=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!t)return void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please set one in the plugin settings.",{type:"snackbar"});const a=wp.blocks.createBlock("core/heading",{content:"Generating AI image...",level:2,className:"kaigen-text-center"});g(m.clientId,[a,m]),l(!0),o(e,(e=>{if(l(!1),e.error)console.error("Image generation failed:",e.error),wp.data.dispatch("core/notices").createErrorNotice("Failed to generate image: "+e.error,{type:"snackbar"}),g(a.clientId,[]);else{let t={url:e.url,alt:e.alt,caption:""};e.id&&"number"==typeof e.id&&e.id>0&&(t.id=e.id);const r=wp.blocks.createBlock("core/image",t);g(a.clientId,[r])}}))}}),[m,r.text,r.start,r.end,g]),p=""!==r.text.slice(r.start,r.end).trim();return(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isGenerating:i,onGenerateImage:u,isTextSelected:p}))}});const m=window.wp.hooks;(0,m.addFilter)("editor.MediaUpload","kaigen/add-ai-tab",(e=>a=>{const r=a.allowedTypes&&a.allowedTypes.includes("image")&&!a.multiple,n=wp.data.select("core/block-editor").getSelectedBlock(),o=n&&"core/image"===n.name,l=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key,c=r&&o&&!(n&&n.attributes&&n.attributes.url)&&l;return(0,t.createElement)(e,{...a,render:e=>(0,t.createElement)(t.Fragment,null,a.render(e),(0,t.createElement)(i,{onSelect:a.onSelect,shouldDisplay:c}))})}));const g=window.wp.apiFetch;var u=e.n(g);(0,m.addFilter)("editor.BlockEdit","kaigen/add-regenerate-button",(e=>n=>{if("core/image"!==n.name)return(0,t.createElement)(e,{...n});const i=n.attributes.id&&"number"==typeof n.attributes.id&&n.attributes.id>0,[l,d]=(0,a.useState)(!1),[m,g]=(0,a.useState)(null),[p,k]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{(async()=>{try{const e=window.kaiGen?.provider,t=window.kaiGen?.supportsImageToImage||!1;if(!e)return;k(t)}catch(e){console.error("Failed to initialize provider:",e)}})()}),[]),(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...n}),(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isRegenerating:l,onRegenerateImage:async e=>{g(null);const t=e||n.attributes.alt||"no alt text or prompt, please just enhance",a=window.kaiGen?.provider;if(!a)return console.error("No provider configured"),void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please check your plugin settings.",{type:"snackbar"});d(!0);try{const e=n.attributes.url,a={};p&&e?a.sourceImageUrl=e:p&&!e&&(console.warn("Image-to-image requested but no source image URL available"),wp.data.dispatch("core/notices").createWarningNotice("Image-to-image generation requires a source image. Please ensure the image is properly loaded.",{type:"snackbar"}));const r=await new Promise(((e,r)=>{o(t,(t=>{t.error?r(new Error(t.error)):e(t)}),a)}));r.id&&"number"==typeof r.id&&r.id>0?n.setAttributes({url:r.url,id:r.id}):n.setAttributes({url:r.url,id:void 0}),wp.data.dispatch("core/notices").createSuccessNotice("Image regenerated successfully!",{type:"snackbar"})}catch(e){console.error("Image regeneration failed:",e);let t=e.message||"Unknown error",a="";t.includes("organization verification")?a=" Please verify your organization in the OpenAI dashboard.":t.includes("parameter")?t="API configuration error. Please contact the plugin developer.":t.includes("content policy")&&(a=" Try a different prompt."),wp.data.dispatch("core/notices").createErrorNotice("Failed to regenerate image: "+t+a,{type:"snackbar"})}finally{d(!1)}},isImageBlock:!0,supportsImageToImage:p})),i&&(0,t.createElement)(s.InspectorControls,null,(0,t.createElement)(r.PanelBody,{title:"KaiGen Settings",initialOpen:!1},(0,t.createElement)(r.CheckboxControl,{label:"Reference image",checked:n.attributes.kaigen_reference_image||!1,onChange:async e=>{n.setAttributes({kaigen_reference_image:e});try{await u()({path:`/wp/v2/media/${n.attributes.id}`,method:"POST",data:{meta:{kaigen_reference_image:e?1:0}}})}catch(e){console.error("Failed to update reference image meta:",e),wp.data.dispatch("core/notices").createErrorNotice("Failed to update reference image meta",{type:"snackbar"})}},help:"Add to the list of reference images."}))))})),(0,m.addFilter)("blocks.registerBlockType","kaigen/add-reference-image-attribute",((e,t)=>"core/image"!==t?e:{...e,attributes:{...e.attributes,kaigen_reference_image:{type:"boolean",default:!1}}}))})();
     1(()=>{"use strict";var e={n:t=>{var a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var t;e.g.importScripts&&(t=e.g.location+"");var a=e.g.document;if(!t&&a&&(a.currentScript&&(t=a.currentScript.src),!t)){var r=a.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!t||!/^http(s?):/.test(t));)t=r[n--].src}if(!t)throw new Error("Automatic publicPath is not supported in this browser");t=t.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),e.p=t})();const t=window.React,a=window.wp.element,r=window.wp.components,n=e.p+"images/KaiGen-logo-128x128.44b1814b.png",o=async(e,t,a={})=>{try{const r=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!r)throw new Error("No provider configured. Please check your plugin settings.");const n=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key;if(!n)throw new Error("No API key configured for the selected provider. Please add one in the KaiGen settings.");const o={prompt:e,provider:r};a.sourceImageUrls&&Array.isArray(a.sourceImageUrls)?o.source_image_urls=a.sourceImageUrls:a.sourceImageUrl&&(o.source_image_url=a.sourceImageUrl),a.additionalImageUrls&&Array.isArray(a.additionalImageUrls)&&(o.additional_image_urls=a.additionalImageUrls),a.maskUrl&&(o.mask_url=a.maskUrl),a.moderation&&["auto","low"].includes(a.moderation)&&(o.moderation=a.moderation),a.style&&["natural","vivid"].includes(a.style)&&(o.style=a.style),a.aspectRatio&&["1:1","16:9","9:16","4:3","3:4"].includes(a.aspectRatio)&&(o.aspect_ratio=a.aspectRatio);const i=await wp.apiFetch({path:"/kaigen/v1/generate-image",method:"POST",data:o});if(i.code&&i.message){if("content_moderation"===i.code)throw new Error(i.message);if("replicate_error"===i.code)throw new Error("Image generation failed: "+i.message);throw new Error(i.message)}if(!i||!i.url)throw new Error("Invalid response from server: "+JSON.stringify(i));i.id&&"number"==typeof i.id&&i.id>0?t({url:i.url,alt:e,id:i.id,caption:""}):t({url:i.url,alt:e,caption:""})}catch(e){console.error("Image generation failed:",e),e.message&&console.error("Error message:",e.message),e.stack&&console.error("Error stack:",e.stack),t({error:e.message||"An unknown error occurred while generating the image"})}},i=({onSelect:e,shouldDisplay:i})=>{const[l,c]=(0,a.useState)(!1),[s,d]=(0,a.useState)(""),[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(null),[k,w]=(0,a.useState)([]),[b,h]=(0,a.useState)([]),[E,f]=(0,a.useState)("1:1"),y=window.kaiGen?.supportsImageToImage||!1,v="replicate"===(wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider||"replicate")?10:16;(0,a.useEffect)((()=>{l&&y&&(async()=>{try{const e=await wp.apiFetch({path:"/kaigen/v1/reference-images",method:"GET"});return Array.isArray(e)?e:[]}catch(e){return console.error("Failed to fetch reference images:",e),[]}})().then(w)}),[l]);const I=()=>{if(!s.trim())return void p("Please enter a prompt for image generation.");g(!0),p(null);const t={};b.length>0&&(t.sourceImageUrls=b.map((e=>e.url))),E&&(t.aspectRatio=E),o(s.trim(),(t=>{t.error?(p(t.error),g(!1)):(e(t),g(!1),c(!1))}),t)};return i?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Button,{onClick:()=>c(!0),className:"kaigen-placeholder-button","aria-label":"KaiGen",role:"button",title:"KaiGen",style:{order:10}},(0,t.createElement)("img",{src:n,alt:"KaiGen","aria-label":"KaiGen logo",role:"button",title:"KaiGen logo",style:{width:"64px",height:"64px"}})),l&&(0,t.createElement)(r.Modal,{className:"kaigen-modal",title:(0,t.createElement)("div",{className:"kaigen-modal__logo-container"},(0,t.createElement)("img",{src:n,alt:"KaiGen logo",className:"kaigen-modal__logo"})),"aria-label":"KaiGen",onRequestClose:()=>c(!1)},u&&(0,t.createElement)("p",{className:"kaigen-error-text"},u),(0,t.createElement)("div",{className:"kaigen-modal__input-container"},y&&k.length>0&&(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-start",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__ref-button "+(b.length>0?"kaigen-ref-button-selected":""),onClick:a,"aria-expanded":e,"aria-label":"Reference Images"},(0,t.createElement)(r.Dashicon,{icon:"format-image",className:b.length>0?"kaigen-ref-button-icon-selected":""})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Reference Images (up to ",v,")"),(0,t.createElement)("div",{className:"kaigen-modal-reference-images-container"},k.map((e=>(0,t.createElement)("img",{key:e.id,src:e.url,alt:e.alt||"",onClick:()=>{h((t=>t.some((t=>t.id===e.id))?t.filter((t=>t.id!==e.id)):t.length<v?[...t,e]:t))},className:"kaigen-modal-reference-image "+(b.some((t=>t.id===e.id))?"kaigen-modal-reference-image-selected":"")})))))}),(0,t.createElement)("div",{className:"kaigen-modal__textarea-container"},(0,t.createElement)(r.TextareaControl,{className:"kaigen-modal__textarea",placeholder:"Image prompt...",value:s,onChange:d,onKeyDown:e=>{"Enter"!==e.key||e.shiftKey||(e.preventDefault(),I())},rows:2})),s.trim()&&(0,t.createElement)(r.Button,{className:"kaigen-modal__submit-button",variant:"primary",onClick:I,disabled:m||!s.trim(),"aria-label":"Generate Image"},m?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null)):(0,t.createElement)(r.Dashicon,{icon:"admin-appearance"})),(0,t.createElement)(r.Dropdown,{onFocusOutside:()=>setIsOpen(!1),popoverProps:{placement:"bottom-end",focusOnMount:!0},renderToggle:({isOpen:e,onToggle:a})=>(0,t.createElement)(r.Button,{className:"kaigen-modal__settings-button",onClick:a,"aria-expanded":e,"aria-label":"Settings"},(0,t.createElement)(r.Dashicon,{icon:"admin-generic"})),renderContent:()=>(0,t.createElement)("div",{className:"kaigen-modal-dropdown-content-container"},(0,t.createElement)("h4",{className:"kaigen-modal-dropdown-content-title"},"Aspect Ratio"),(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-container"},[{value:"1:1",label:"1:1",title:"Square"},{value:"16:9",label:"16:9",title:"Landscape"},{value:"9:16",label:"9:16",title:"Portrait"}].map((e=>(0,t.createElement)("div",{key:e.value,onClick:()=>f((t=>t===e.value?null:e.value)),"aria-pressed":E===e.value,"aria-label":`${e.title} (${e.label})`,className:"kaigen-modal__aspect-ratio-button "+(E===e.value?"kaigen-modal__aspect-ratio-button-selected":"")},(0,t.createElement)("div",{className:"kaigen-modal-aspect-ratio-icon-container"},(0,t.createElement)("div",{className:`kaigen-modal-aspect-ratio-icon ${E===e.value?"kaigen-modal-aspect-ratio-icon-selected":""} kaigen-aspect-ratio-${e.value.replace(":","-")}`})),(0,t.createElement)("span",{className:"kaigen-modal-aspect-ratio-label"},e.label))))))})))):null},l=e.p+"images/KaiGen-logo-64x64.a6e617bd.png",c=({isGenerating:e,onGenerateImage:n,isRegenerating:o,onRegenerateImage:i,isImageBlock:c,isTextSelected:s,supportsImageToImage:d})=>{const[m,g]=(0,a.useState)(!1),[u,p]=(0,a.useState)(""),[k,w]=(0,a.useState)(null);return c&&d?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:o?(0,t.createElement)(r.Spinner,null):(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-toolbar-icon"}),label:o?"KaiGen is generating...":"KaiGen",onClick:()=>g(!0),disabled:o})),m&&(0,t.createElement)(r.Modal,{title:(0,t.createElement)("img",{src:l,alt:"KaiGen logo",className:"kaigen-modal-logo"}),onRequestClose:()=>{g(!1),p(""),w(null)}},k&&(0,t.createElement)("p",{className:"kaigen-error-text"},k),(0,t.createElement)(r.TextareaControl,{label:"Editing Instructions (optional)",value:u,onChange:p,rows:4}),(0,t.createElement)(r.Button,{variant:"primary",onClick:()=>{i(u.trim()),g(!1),p(""),w(null)},disabled:o},o?(0,t.createElement)(t.Fragment,null,(0,t.createElement)(r.Spinner,null),"Regenerating..."):"Regenerate Image"))):s?(0,t.createElement)(r.ToolbarGroup,null,(0,t.createElement)(r.ToolbarButton,{icon:e?(0,t.createElement)(r.Spinner,null):"format-image",label:e?"KaiGen is generating...":"KaiGen",onClick:n,disabled:e})):null},s=window.wp.blockEditor,d=window.wp.data;(0,window.wp.richText.registerFormatType)("kaigen/custom-format",{title:"AI Image Gen",tagName:"span",className:"kaigen-format",edit:({isActive:e,value:r,onChange:n})=>{const[i,l]=(0,a.useState)(!1),m=(0,d.useSelect)((e=>e("core/block-editor").getSelectedBlock()),[]),{replaceBlocks:g}=(0,d.useDispatch)("core/block-editor"),u=(0,a.useCallback)((()=>{if(m&&"core/paragraph"===m.name){const e=r.text.slice(r.start,r.end).trim();if(!e)return void wp.data.dispatch("core/notices").createErrorNotice("Please select some text to use as the image generation prompt.",{type:"snackbar"});const t=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_provider;if(!t)return void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please set one in the plugin settings.",{type:"snackbar"});const a=wp.blocks.createBlock("core/heading",{content:"Generating AI image...",level:2,className:"kaigen-text-center"});g(m.clientId,[a,m]),l(!0),o(e,(e=>{if(l(!1),e.error)console.error("Image generation failed:",e.error),wp.data.dispatch("core/notices").createErrorNotice("Failed to generate image: "+e.error,{type:"snackbar"}),g(a.clientId,[]);else{let t={url:e.url,alt:e.alt,caption:""};e.id&&"number"==typeof e.id&&e.id>0&&(t.id=e.id);const r=wp.blocks.createBlock("core/image",t);g(a.clientId,[r])}}))}}),[m,r.text,r.start,r.end,g]),p=""!==r.text.slice(r.start,r.end).trim();return(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isGenerating:i,onGenerateImage:u,isTextSelected:p}))}});const m=window.wp.hooks;(0,m.addFilter)("editor.MediaUpload","kaigen/add-ai-tab",(e=>a=>{const r=a.allowedTypes&&a.allowedTypes.includes("image")&&!a.multiple,n=wp.data.select("core/block-editor").getSelectedBlock(),o=n&&"core/image"===n.name,l=wp.data.select("core/editor")?.getEditorSettings()?.kaigen_has_api_key,c=r&&o&&!(n&&n.attributes&&n.attributes.url)&&l;return(0,t.createElement)(e,{...a,render:e=>(0,t.createElement)(t.Fragment,null,a.render(e),(0,t.createElement)(i,{onSelect:a.onSelect,shouldDisplay:c}))})}));const g=window.wp.apiFetch;var u=e.n(g);(0,m.addFilter)("editor.BlockEdit","kaigen/add-regenerate-button",(e=>n=>{if("core/image"!==n.name)return(0,t.createElement)(e,{...n});const i=n.attributes.id&&"number"==typeof n.attributes.id&&n.attributes.id>0,[l,d]=(0,a.useState)(!1),[m,g]=(0,a.useState)(null),[p,k]=(0,a.useState)(!1);return(0,a.useEffect)((()=>{(async()=>{try{const e=window.kaiGen?.provider,t=window.kaiGen?.supportsImageToImage||!1;if(!e)return;k(t)}catch(e){console.error("Failed to initialize provider:",e)}})()}),[]),(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...n}),(0,t.createElement)(s.BlockControls,null,(0,t.createElement)(c,{isRegenerating:l,onRegenerateImage:async e=>{g(null);const t=e||n.attributes.alt||"no alt text or prompt, please just enhance",a=window.kaiGen?.provider;if(!a)return console.error("No provider configured"),void wp.data.dispatch("core/notices").createErrorNotice("No AI provider configured. Please check your plugin settings.",{type:"snackbar"});d(!0);try{const e=n.attributes.url,a={};p&&e?a.sourceImageUrl=e:p&&!e&&(console.warn("Image-to-image requested but no source image URL available"),wp.data.dispatch("core/notices").createWarningNotice("Image-to-image generation requires a source image. Please ensure the image is properly loaded.",{type:"snackbar"}));const r=await new Promise(((e,r)=>{o(t,(t=>{t.error?r(new Error(t.error)):e(t)}),a)}));r.id&&"number"==typeof r.id&&r.id>0?n.setAttributes({url:r.url,id:r.id}):n.setAttributes({url:r.url,id:void 0}),wp.data.dispatch("core/notices").createSuccessNotice("Image regenerated successfully!",{type:"snackbar"})}catch(e){console.error("Image regeneration failed:",e);let t=e.message||"Unknown error",a="";t.includes("organization verification")?a=" Please verify your organization in the OpenAI dashboard.":t.includes("parameter")?t="API configuration error. Please contact the plugin developer.":t.includes("content policy")&&(a=" Try a different prompt."),wp.data.dispatch("core/notices").createErrorNotice("Failed to regenerate image: "+t+a,{type:"snackbar"})}finally{d(!1)}},isImageBlock:!0,supportsImageToImage:p})),i&&(0,t.createElement)(s.InspectorControls,null,(0,t.createElement)(r.PanelBody,{title:"KaiGen Settings",initialOpen:!1},(0,t.createElement)(r.CheckboxControl,{label:"Reference image",checked:n.attributes.kaigen_reference_image||!1,onChange:async e=>{n.setAttributes({kaigen_reference_image:e});try{await u()({path:`/wp/v2/media/${n.attributes.id}`,method:"POST",data:{meta:{kaigen_reference_image:e?1:0}}})}catch(e){console.error("Failed to update reference image meta:",e),wp.data.dispatch("core/notices").createErrorNotice("Failed to update reference image meta",{type:"snackbar"})}},help:"Add to the list of reference images."}))))})),(0,m.addFilter)("blocks.registerBlockType","kaigen/add-reference-image-attribute",((e,t)=>"core/image"!==t?e:{...e,attributes:{...e.attributes,kaigen_reference_image:{type:"boolean",default:!1}}}))})();
  • kaigen/trunk/inc/class-admin.php

    r3361350 r3401075  
    200200        // Add quality field
    201201        add_settings_field(
    202             'kaigen_quality_setting',
     202            'kaigen_quality_settings',
    203203            'Image Quality',
    204204            [$this, 'render_quality_field'],
     
    352352     */
    353353    public function render_quality_field() {
    354         $quality_settings = get_option('kaigen_quality_settings', []);
    355         $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     354        $quality = KaiGen_Image_Provider::get_quality_setting();
    356355        ?>
    357356        <select name="kaigen_quality_settings[quality]">
  • kaigen/trunk/inc/class-image-provider.php

    r3315977 r3401075  
    171171     */
    172172    abstract public function process_api_response($response);
     173
     174    /**
     175     * Gets the quality setting from the options.
     176     *
     177     * @return string The quality setting.
     178     */
     179    public static function get_quality_setting() {
     180        $quality_settings = get_option('kaigen_quality_settings', []);
     181
     182        // Ensure we have an array to work with.
     183        if ( ! is_array( $quality_settings ) ) {
     184            $quality_settings = [];
     185        }
     186
     187        // If the quality setting is not set, check for the old option and migrate it.
     188        if ( ! isset( $quality_settings['quality'] ) ) {
     189            $legacy_quality_setting = get_option('kaigen_quality_setting');
     190           
     191            if ( ! empty( $legacy_quality_setting ) && in_array( $legacy_quality_setting, ['low', 'medium', 'high'], true ) ) {
     192                $quality_settings['quality'] = $legacy_quality_setting;
     193                update_option('kaigen_quality_settings', $quality_settings);
     194                delete_option('kaigen_quality_setting');
     195            } else {
     196                // Default to medium quality if neither exists.
     197                $quality_settings['quality'] = 'medium'; 
     198                update_option('kaigen_quality_settings', $quality_settings);
     199            }
     200        }
     201
     202        return $quality_settings['quality'];
     203    }
    173204}
  • kaigen/trunk/inc/class-rest-api.php

    r3361350 r3401075  
    124124        // For Replicate, get the model based on quality setting
    125125        if ($provider_id === 'replicate') {
    126             $quality_settings = get_option('kaigen_quality_settings', []);
    127             $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     126            $quality = KaiGen_Image_Provider::get_quality_setting();
    128127           
    129128            $provider = kaigen_provider_manager()->get_provider($provider_id);
     
    161160    private function get_additional_params($request) {
    162161        // Get saved quality settings
     162        $quality = KaiGen_Image_Provider::get_quality_setting();
     163        $quality_value = $quality === 'hd' ? 100 : 80;
    163164        $quality_settings = get_option('kaigen_quality_settings', []);
    164         $quality_value = isset($quality_settings['quality']) && $quality_settings['quality'] === 'hd' ? 100 : 80;
    165165        $style_value = isset($quality_settings['style']) ? $quality_settings['style'] : 'natural';
    166166       
     
    183183
    184184        // Add source image URL if provided (single or array)
    185         $source_image_url = $request->get_param('source_image_url');
    186         if (!empty($source_image_url)) {
    187             $params['source_image_url'] = $source_image_url;
     185        $source_image_urls = $request->get_param('source_image_urls');
     186        if (!empty($source_image_urls)) {
     187            $params['source_image_urls'] = $source_image_urls;
     188        } else {
     189            $source_image_url = $request->get_param('source_image_url');
     190            if (!empty($source_image_url)) {
     191                $params['source_image_url'] = $source_image_url;
     192            }
    188193        }
    189194       
     
    274279                    'empty_image_data',
    275280                    'replicate_validation_error',
    276                     'content_moderation'
     281                    'content_moderation',
     282                    'api_error',
     283                    'openai_error',
     284                    'max_retries_exceeded',
     285                    'invalid_api_key_format'
    277286                ])) {
    278287                    return $result;
  • kaigen/trunk/inc/providers/class-image-provider-openai.php

    r3315977 r3401075  
    4848        }
    4949
     50        // Handle source image URLs
     51        $source_image_urls = $additional_params['source_image_urls'] ?? [];
    5052        $source_image_url = $additional_params['source_image_url'] ?? null;
    51         $max_retries = 3; // Reduce max retries to fail faster
    52         $timeout = 60; // Set request timeout to 60 seconds
     53        if ($source_image_url) {
     54            array_unshift($source_image_urls, $source_image_url);
     55        }
     56
     57        // Limit the number of source image URLs to 16.
     58        $source_image_urls = array_slice(array_unique($source_image_urls), 0, 16);
     59
     60        $max_retries = 3;
     61        $timeout = 150; // Increased timeout for image generation (docs say up to 2 mins)
    5362        $retry_delay = 2; // Seconds to wait between retries
    5463
     
    6170        add_filter('http_request_args', function($args) use ($timeout) {
    6271            $args['timeout'] = $timeout;
    63             $args['httpversion'] = '1.1';
    6472            $args['sslverify'] = true;
    6573            $args['blocking'] = true;
     
    7078            }
    7179            $args['curl'][CURLOPT_TIMEOUT] = $timeout;
    72             $args['curl'][CURLOPT_CONNECTTIMEOUT] = 10;
    73             $args['curl'][CURLOPT_LOW_SPEED_TIME] = 30; // Increased low speed time
    74             $args['curl'][CURLOPT_LOW_SPEED_LIMIT] = 1024; // 1KB/s minimum speed
     80            $args['curl'][CURLOPT_CONNECTTIMEOUT] = 30; // Increased connect timeout
     81            $args['curl'][CURLOPT_TCP_KEEPALIVE] = 1; // Enable TCP keepalive
     82
     83            // Adjust low speed settings to prevent timeouts on slow generation
     84            $args['curl'][CURLOPT_LOW_SPEED_TIME] = 600; // Wait 10 minutes before timing out due to low speed
     85            $args['curl'][CURLOPT_LOW_SPEED_LIMIT] = 1; // Only timeout if speed is effectively 0
    7586           
    7687            return $args;
     
    8192
    8293        // Log if we're using image-to-image
    83         if ( ! empty( $source_image_url ) ) {
     94        if (!empty($source_image_urls)) {
    8495            $endpoint = self::IMAGE_EDIT_API_BASE_URL;
    8596        }
    8697       
    8798        // Get quality setting from admin options
    88         $quality_settings = get_option('kaigen_quality_settings', []);
    89         $quality = isset($quality_settings['quality']) ? $quality_settings['quality'] : 'medium';
     99        $quality = self::get_quality_setting();
    90100       
    91101        // Map quality settings to supported values
     
    100110       
    101111        // Prepare the request based on the type of request
    102         if ( ! empty( $source_image_url ) ) {
     112        if (!empty($source_image_urls)) {
    103113            // For image edit requests, we need to use multipart/form-data
    104114            $boundary = wp_generate_password(24, false);
     
    125135            $body .= 'Content-Disposition: form-data; name="quality"' . "\r\n\r\n";
    126136            $body .= $quality . "\r\n";
     137
     138            // Add format parameter (jpeg is faster than png)
     139            $body .= "--{$boundary}\r\n";
     140            $body .= 'Content-Disposition: form-data; name="output_format"' . "\r\n\r\n";
     141            $body .= "jpeg\r\n";
    127142           
    128143            // Add image files
    129             if (is_array($source_image_url)) {
    130                 foreach ($source_image_url as $index => $image_url) {
    131                     $image_data = $this->get_image_data($image_url);
    132                     if (is_wp_error($image_data)) {
    133                         return $image_data;
    134                     }
    135                    
    136                     $body .= "--{$boundary}\r\n";
    137                     $body .= 'Content-Disposition: form-data; name="image[]"; filename="' . basename($image_url) . '"' . "\r\n";
    138                     $body .= 'Content-Type: ' . $this->get_image_mime_type($image_url) . "\r\n\r\n";
    139                     $body .= $image_data . "\r\n";
    140                 }
    141             } else {
    142                 $image_data = $this->get_image_data($source_image_url);
     144            foreach ($source_image_urls as $index => $image_url) {
     145                $image_data = $this->get_image_data($image_url);
    143146                if (is_wp_error($image_data)) {
    144147                    return $image_data;
     
    146149               
    147150                $body .= "--{$boundary}\r\n";
    148                 $body .= 'Content-Disposition: form-data; name="image"; filename="' . basename($source_image_url) . '"' . "\r\n";
    149                 $body .= 'Content-Type: ' . $this->get_image_mime_type($source_image_url) . "\r\n\r\n";
     151                $body .= 'Content-Disposition: form-data; name="image[]"; filename="' . basename($image_url) . '"' . "\r\n";
     152                $body .= 'Content-Type: ' . $this->get_image_mime_type($image_url) . "\r\n\r\n";
    150153                $body .= $image_data . "\r\n";
    151154            }
     
    158161            $headers = $this->get_request_headers();
    159162            $body = [
    160                 'model'   => 'gpt-image-1',
    161                 'prompt'  => $prompt,
    162                 'quality' => $quality,
     163                'model'          => 'gpt-image-1',
     164                'prompt'         => $prompt,
     165                'quality'        => $quality,
     166                'output_format'  => 'jpeg',
    163167            ];
    164            
     168
    165169            // Add size parameter if aspect ratio is specified
    166170            if (isset($additional_params['aspect_ratio'])) {
     
    168172                $body['size'] = "{$width}x{$height}";
    169173            }
    170            
     174
    171175            $body = wp_json_encode($body);
    172176        }
  • kaigen/trunk/inc/providers/class-image-provider-replicate.php

    r3361350 r3401075  
    5252     */
    5353    public function get_current_model() {
    54         // Get all quality-related options to debug
    55         $quality_settings = get_option('kaigen_quality_settings');
    56         $quality_setting = get_option('kaigen_quality_setting');
    57 
    58         // Use the correct option name
    59         $quality = 'medium'; // Default
    60         if (is_array($quality_settings) && isset($quality_settings['quality'])) {
    61             $quality = $quality_settings['quality'];
    62         }
    63        
     54        $quality = self::get_quality_setting();
    6455        $model = $this->get_model_from_quality_setting($quality);
    6556        return $model;
     
    8576        $model_to_use = $this->model;
    8677       
    87         // Handle source_image_url parameter (convert to image_input for Replicate seedream-4)
    88         $source_image_url = $additional_params['source_image_url'] ?? $additional_params['input_image'] ?? null;
    89        
    90         // If source image is provided, use the hardcoded image-to-image model
    91         if (!empty($source_image_url)) {
    92             $model_to_use = $this->get_image_to_image_model();
    93 
    94             // Process image URL (converts to base64 only if local)
    95             $processed_image = $this->process_image_url($source_image_url);
    96             if (is_wp_error($processed_image)) {
    97                 // Return image processing errors immediately without retry
    98                 return $processed_image;
    99             }
    100            
    101             // Use 'image_input' parameter as array for seedream-4 model (confirmed by schema)
    102             $input_data['image_input'] = [$processed_image];
    103         }
    104        
    105         // Remove these parameters to prevent duplication
     78        // Handle source image URLs (can be single string or array)
     79        $source_image_urls = $additional_params['source_image_urls'] ?? $additional_params['source_image_url'] ?? null;
     80        if (!empty($source_image_urls)) {
     81            if (!is_array($source_image_urls)) {
     82                $source_image_urls = [$source_image_urls];
     83            }
     84           
     85            $image_inputs = [];
     86            foreach ($source_image_urls as $url) {
     87                if (count($image_inputs) >= 10) break;
     88                $processed = $this->process_image_url($url);
     89                if (!is_wp_error($processed)) {
     90                    $image_inputs[] = $processed;
     91                } else {
     92                    // Log error but continue with other images
     93                    error_log('Failed to process image: ' . $processed->get_error_message());
     94                }
     95            }
     96           
     97            if (!empty($image_inputs)) {
     98                $model_to_use = $this->get_image_to_image_model();
     99                $input_data['image_input'] = $image_inputs;
     100
     101                // Set size to 1k for low quality image edits, assumes we are using seedream-4 model so if that changes, this will need to be updated.
     102                $quality = self::get_quality_setting();
     103
     104                if ($quality === 'low') {
     105                    $additional_params['size'] = '1K';
     106                }
     107            }
     108        }
     109       
     110        // Remove source image parameters to prevent duplication
     111        unset($additional_params['source_image_urls']);
    106112        unset($additional_params['source_image_url']);
    107113        unset($additional_params['input_image']);
    108114
    109115        // Filter parameters based on the model being used
    110         if (!empty($source_image_url)) {
     116        if (!empty($source_image_urls)) {
    111117            // For seedream-4, only keep valid parameters according to schema
    112118            $valid_params = ['size', 'width', 'height', 'max_images', 'aspect_ratio', 'sequential_image_generation'];
     
    358364            'black-forest-labs/flux-schnell' => 'Flux Schnell by Black Forest Labs (low quality)',
    359365            'bytedance/seedream-4'           => 'Seedream 4 by Bytedance (high quality)',
    360             'google/imagen-4-ultra'          => 'Imagen 4 Ultra by Google (highest quality)',
     366            'google/nano-banana-pro'         => 'Nano Banana Pro by Google (highest quality)',
    361367        ];
    362368    }
    363369
    364370    /**
    365      * Gets the hardcoded image-to-image model for Replicate.
     371     * Gets the image-to-image model for Replicate based on quality setting.
    366372     *
    367373     * @return string The image-to-image model.
    368374     */
    369375    private function get_image_to_image_model() {
    370         return 'bytedance/seedream-4';
     376        $model = 'bytedance/seedream-4';
     377        $quality = self::get_quality_setting();
     378
     379        if ($quality === 'high') {
     380            $model = 'google/nano-banana-pro';
     381        }
     382
     383        return $model;
    371384    }
    372385
     
    395408                break;
    396409            case 'high':
    397                 $model = 'google/imagen-4-ultra';
     410                $model = 'google/nano-banana-pro';
    398411                break;
    399412            default:
  • kaigen/trunk/kaigen.php

    r3361350 r3401075  
    55 * Requires at least: 6.1
    66 * Requires PHP:      7.0
    7  * Version:           0.2.3
     7 * Version:           0.2.4
    88 * Author:            Jacob Schweitzer
    99 * License:           GPL-2.0-or-later
Note: See TracChangeset for help on using the changeset viewer.