first commit all files
This commit is contained in:
114
assets/js/block-refine.js
Normal file
114
assets/js/block-refine.js
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* WP Agentic Writer - Block Toolbar Chat Mention
|
||||
*
|
||||
* Adds "@chat" button to block toolbar to insert the block mention into chat input.
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
( function ( wp ) {
|
||||
const { BlockControls } = wp.blockEditor;
|
||||
const { ToolbarButton, ToolbarGroup } = wp.components;
|
||||
const { createHigherOrderComponent } = wp.compose;
|
||||
const { useSelect } = wp.data;
|
||||
const { addFilter } = wp.hooks;
|
||||
const { __ } = wp.i18n;
|
||||
|
||||
const buildMentionToken = ( block, allBlocks ) => {
|
||||
if ( ! block ) {
|
||||
return '@this';
|
||||
}
|
||||
|
||||
if ( block.name === 'core/list-item' ) {
|
||||
let listIndex = 0;
|
||||
|
||||
for ( const parent of allBlocks ) {
|
||||
if ( parent.name !== 'core/list' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
listIndex += 1;
|
||||
const innerItems = Array.isArray( parent.innerBlocks ) ? parent.innerBlocks : [];
|
||||
const itemIndex = innerItems.findIndex( ( item ) => item.clientId === block.clientId );
|
||||
if ( itemIndex !== -1 ) {
|
||||
return `@list-${ listIndex }.list-item-${ itemIndex }`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! block.name || ! block.name.startsWith( 'core/' ) ) {
|
||||
return '@this';
|
||||
}
|
||||
|
||||
const typeName = block.name.replace( 'core/', '' );
|
||||
let count = 0;
|
||||
|
||||
for ( const entry of allBlocks ) {
|
||||
if ( ! entry.name || ! entry.name.startsWith( 'core/' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( entry.name.replace( 'core/', '' ) === typeName ) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
if ( entry.clientId === block.clientId ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return `@${ typeName }-${ count }`;
|
||||
};
|
||||
|
||||
const withChatMentionToolbar = createHigherOrderComponent( ( BlockEdit ) => {
|
||||
return ( props ) => {
|
||||
const { clientId } = props;
|
||||
const block = useSelect(
|
||||
( select ) => select( 'core/block-editor' ).getBlock( clientId ),
|
||||
[ clientId ]
|
||||
);
|
||||
const allBlocks = useSelect(
|
||||
( select ) => select( 'core/block-editor' ).getBlocks(),
|
||||
[]
|
||||
);
|
||||
const mentionToken = buildMentionToken( block, allBlocks );
|
||||
|
||||
const sendToChat = () => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent( 'wpaw:insert-mention', {
|
||||
detail: {
|
||||
token: `${ mentionToken } `,
|
||||
blockId: block?.clientId || clientId,
|
||||
},
|
||||
} )
|
||||
);
|
||||
};
|
||||
|
||||
return wp.element.createElement(
|
||||
wp.element.Fragment,
|
||||
null,
|
||||
wp.element.createElement( BlockEdit, props ),
|
||||
wp.element.createElement(
|
||||
BlockControls,
|
||||
null,
|
||||
wp.element.createElement(
|
||||
ToolbarGroup,
|
||||
null,
|
||||
wp.element.createElement( ToolbarButton, {
|
||||
icon: 'format-chat',
|
||||
label: __( 'Send to chat', 'wp-agentic-writer' ),
|
||||
onClick: sendToChat,
|
||||
children: '@chat',
|
||||
} )
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
}, 'withChatMentionToolbar' );
|
||||
|
||||
addFilter(
|
||||
'editor.BlockEdit',
|
||||
'wp-agentic-writer/block-chat-mention',
|
||||
withChatMentionToolbar
|
||||
);
|
||||
} )( window.wp );
|
||||
1013
assets/js/settings-v2.js
Normal file
1013
assets/js/settings-v2.js
Normal file
File diff suppressed because it is too large
Load Diff
293
assets/js/settings.js
Normal file
293
assets/js/settings.js
Normal file
@@ -0,0 +1,293 @@
|
||||
/**
|
||||
* WP Agentic Writer - Settings Page Scripts
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
'use strict';
|
||||
|
||||
// ===========================
|
||||
// TAB NAVIGATION
|
||||
// ===========================
|
||||
$( '.wpaw-settings-nav-btn' ).on( 'click', function() {
|
||||
const tab = $( this ).data( 'tab' );
|
||||
|
||||
// Update nav buttons
|
||||
$( '.wpaw-settings-nav-btn' ).removeClass( 'active' );
|
||||
$( this ).addClass( 'active' );
|
||||
|
||||
// Update tab content
|
||||
$( '.wpaw-tab-content' ).removeClass( 'active' );
|
||||
$( `.wpaw-tab-content[data-tab="${tab}"]` ).addClass( 'active' );
|
||||
} );
|
||||
|
||||
// ===========================
|
||||
// SELECT2 INITIALIZATION
|
||||
// ===========================
|
||||
function initSelect2() {
|
||||
if ( ! $.fn.select2 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize Select2 for model dropdowns
|
||||
$( '.wpaw-select2-model' ).each( function() {
|
||||
const $select = $( this );
|
||||
|
||||
// Destroy existing Select2 if present
|
||||
if ( $select.hasClass( 'select2-hidden-accessible' ) ) {
|
||||
$select.select2( 'destroy' );
|
||||
}
|
||||
|
||||
$select.select2( {
|
||||
placeholder: 'Search for a model...',
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
templateResult: formatModelOption,
|
||||
templateSelection: formatModelSelection,
|
||||
} );
|
||||
} );
|
||||
|
||||
// Initialize Select2 for Context Categories (multiple with tags)
|
||||
const $contextCategories = $( '#required_context_categories' );
|
||||
if ( $contextCategories.length && ! $contextCategories.hasClass( 'select2-hidden-accessible' ) ) {
|
||||
$contextCategories.select2( {
|
||||
placeholder: 'Select categories...',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
tags: false,
|
||||
closeOnSelect: false,
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
function formatModelOption( model ) {
|
||||
if ( ! model.id ) {
|
||||
return model.text;
|
||||
}
|
||||
const isFree = model.text && model.text.includes( '(Free)' );
|
||||
const $option = $( '<span>' + model.text + '</span>' );
|
||||
if ( isFree ) {
|
||||
$option.css( 'color', '#28a745' );
|
||||
}
|
||||
return $option;
|
||||
}
|
||||
|
||||
function formatModelSelection( model ) {
|
||||
return model.text || model.id;
|
||||
}
|
||||
|
||||
// Store model pricing data from API (populated by updateModelDropdowns)
|
||||
let modelPricing = {};
|
||||
|
||||
// Estimate cost per article using dynamic pricing from API (6 models)
|
||||
// Estimates: chat 200 tokens, clarity 300, planning 500, writing 3K, refinement 1K, 1 image
|
||||
function estimateArticleCost() {
|
||||
const chatModel = $( '#chat_model' ).val();
|
||||
const clarityModel = $( '#clarity_model' ).val();
|
||||
const planningModel = $( '#planning_model' ).val();
|
||||
const writingModel = $( '#writing_model' ).val();
|
||||
const refinementModel = $( '#refinement_model' ).val();
|
||||
const imageModel = $( '#image_model' ).val();
|
||||
|
||||
let totalCost = 0;
|
||||
|
||||
// Helper to calculate cost for a model
|
||||
const calcCost = ( modelId, inputTokens, outputTokens ) => {
|
||||
const pricing = modelPricing[modelId];
|
||||
if ( pricing ) {
|
||||
return ( pricing.prompt * inputTokens ) + ( pricing.completion * outputTokens );
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Chat cost (minimal - 100 in, 100 out)
|
||||
totalCost += calcCost( chatModel, 100, 100 );
|
||||
|
||||
// Clarity cost (300 in, 300 out)
|
||||
totalCost += calcCost( clarityModel, 300, 300 );
|
||||
|
||||
// Planning cost (500 in, 500 out)
|
||||
totalCost += calcCost( planningModel, 500, 500 );
|
||||
|
||||
// Writing cost (1.5K in, 1.5K out - main article generation)
|
||||
totalCost += calcCost( writingModel, 1500, 1500 );
|
||||
|
||||
// Refinement cost (500 in, 500 out)
|
||||
totalCost += calcCost( refinementModel, 500, 500 );
|
||||
|
||||
// Image cost (1 image)
|
||||
const imagePricing = modelPricing[imageModel];
|
||||
if ( imagePricing && imagePricing.image > 0 ) {
|
||||
totalCost += imagePricing.image;
|
||||
}
|
||||
|
||||
return totalCost;
|
||||
}
|
||||
|
||||
// Update cost estimation display
|
||||
function updateCostEstimate() {
|
||||
const cost = estimateArticleCost();
|
||||
const $display = $( '#wpaw-cost-estimate' );
|
||||
|
||||
if ( $display.length ) {
|
||||
if ( cost > 0 ) {
|
||||
$display.text( '~$' + cost.toFixed(4) + ' per article' );
|
||||
} else {
|
||||
$display.text( 'Free or pricing unavailable' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for model changes (6 models)
|
||||
$( '#chat_model, #clarity_model, #planning_model, #writing_model, #refinement_model, #image_model' ).on( 'change', function() {
|
||||
updateCostEstimate();
|
||||
} );
|
||||
|
||||
// Initialize on page load
|
||||
updateCostEstimate();
|
||||
initSelect2();
|
||||
|
||||
// Refresh models button.
|
||||
$( '#wpaw-refresh-models' ).on( 'click', function() {
|
||||
const $button = $( this );
|
||||
const $spinner = $( '#wpaw-models-spinner' );
|
||||
|
||||
$button.prop( 'disabled', true );
|
||||
$spinner.show();
|
||||
|
||||
$.ajax( {
|
||||
url: wpawSettings.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wpaw_refresh_models',
|
||||
nonce: wpawSettings.nonce,
|
||||
},
|
||||
success: function( response ) {
|
||||
if ( response.success ) {
|
||||
// Update model dropdowns.
|
||||
updateModelDropdowns( response.data.models );
|
||||
|
||||
// Show success message.
|
||||
$( '#wpaw-models-message' )
|
||||
.removeClass( 'notice-error' )
|
||||
.addClass( 'notice-success' )
|
||||
.text( response.data.message )
|
||||
.show();
|
||||
} else {
|
||||
showError( response.data.message || 'Unknown error' );
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showError( 'Failed to refresh models. Please try again.' );
|
||||
},
|
||||
complete: function() {
|
||||
$button.prop( 'disabled', false );
|
||||
$spinner.hide();
|
||||
|
||||
// Auto-hide message after 3 seconds.
|
||||
setTimeout( function() {
|
||||
$( '#wpaw-models-message' ).fadeOut();
|
||||
}, 3000 );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
// Initialize model dropdowns on page load.
|
||||
if ( wpawSettings.models ) {
|
||||
updateModelDropdowns( wpawSettings.models );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update model dropdowns with fetched models.
|
||||
* Shows all models in a flat list (no optgroups).
|
||||
*/
|
||||
function updateModelDropdowns( models ) {
|
||||
/**
|
||||
* Populate a select dropdown with models and store pricing.
|
||||
* @param {jQuery} $select - The select element
|
||||
* @param {Array} allModels - Array of all models
|
||||
* @param {string} currentValue - Currently saved value
|
||||
*/
|
||||
const populateSelect = ( $select, allModels, currentValue ) => {
|
||||
$select.empty();
|
||||
|
||||
// Add all models to dropdown and store pricing
|
||||
if ( allModels && allModels.length ) {
|
||||
allModels.forEach( function( model ) {
|
||||
// Clean model name - remove existing (free) suffix to avoid duplication
|
||||
let cleanName = model.name.replace( /\s*\(free\)\s*/gi, '' ).trim();
|
||||
$select.append( $( '<option>', {
|
||||
value: model.id,
|
||||
text: cleanName + ( model.is_free ? ' (Free)' : '' )
|
||||
} ) );
|
||||
|
||||
// Store pricing data for cost calculation
|
||||
if ( model.pricing ) {
|
||||
modelPricing[ model.id ] = model.pricing;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
// Ensure saved value exists in dropdown (legacy/custom models)
|
||||
if ( currentValue && $select.find( `option[value="${currentValue}"]` ).length === 0 ) {
|
||||
$select.prepend( $( '<option>', {
|
||||
value: currentValue,
|
||||
text: currentValue + ' (Saved)'
|
||||
} ) );
|
||||
}
|
||||
|
||||
// Set the current value
|
||||
if ( currentValue ) {
|
||||
$select.val( currentValue );
|
||||
}
|
||||
};
|
||||
|
||||
// Update chat model dropdown
|
||||
const $chatSelect = $( '#chat_model' );
|
||||
const currentChat = wpawSettings.currentModels?.chat || $chatSelect.val();
|
||||
populateSelect( $chatSelect, models.chat?.all || [], currentChat );
|
||||
|
||||
// Update clarity model dropdown
|
||||
const $claritySelect = $( '#clarity_model' );
|
||||
const currentClarity = wpawSettings.currentModels?.clarity || $claritySelect.val();
|
||||
populateSelect( $claritySelect, models.planning?.all || [], currentClarity );
|
||||
|
||||
// Update planning model dropdown
|
||||
const $planningSelect = $( '#planning_model' );
|
||||
const currentPlanning = wpawSettings.currentModels?.planning || $planningSelect.val();
|
||||
populateSelect( $planningSelect, models.planning?.all || [], currentPlanning );
|
||||
|
||||
// Update writing model dropdown
|
||||
const $writingSelect = $( '#writing_model' );
|
||||
const currentWriting = wpawSettings.currentModels?.writing || wpawSettings.currentModels?.execution || $writingSelect.val();
|
||||
populateSelect( $writingSelect, models.execution?.all || [], currentWriting );
|
||||
|
||||
// Update refinement model dropdown
|
||||
const $refinementSelect = $( '#refinement_model' );
|
||||
const currentRefinement = wpawSettings.currentModels?.refinement || $refinementSelect.val();
|
||||
populateSelect( $refinementSelect, models.execution?.all || [], currentRefinement );
|
||||
|
||||
// Update image model dropdown
|
||||
const $imageSelect = $( '#image_model' );
|
||||
const currentImage = wpawSettings.currentModels?.image || $imageSelect.val();
|
||||
populateSelect( $imageSelect, models.image?.all || [], currentImage );
|
||||
|
||||
// Reinitialize Select2 after updating dropdowns
|
||||
initSelect2();
|
||||
|
||||
// Update cost estimate
|
||||
updateCostEstimate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error message.
|
||||
*/
|
||||
function showError( message ) {
|
||||
$( '#wpaw-models-message' )
|
||||
.removeClass( 'notice-success' )
|
||||
.addClass( 'notice-error' )
|
||||
.text( message )
|
||||
.show();
|
||||
}
|
||||
} );
|
||||
59
assets/js/sidebar-test.js
Normal file
59
assets/js/sidebar-test.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* WP Agentic Writer - Test Script
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
console.log('WP Agentic Writer script loaded!');
|
||||
console.log('wpAgenticWriter data:', typeof wpAgenticWriter !== 'undefined' ? wpAgenticWriter : 'NOT DEFINED');
|
||||
|
||||
// Check if wp object is available
|
||||
if (typeof wp !== 'undefined') {
|
||||
console.log('WordPress wp object available:', wp);
|
||||
} else {
|
||||
console.error('WordPress wp object NOT available');
|
||||
}
|
||||
|
||||
// Check dependencies
|
||||
const deps = {
|
||||
wp: typeof wp !== 'undefined',
|
||||
element: typeof wp !== 'undefined' && typeof wp.element !== 'undefined',
|
||||
components: typeof wp !== 'undefined' && typeof wp.components !== 'undefined',
|
||||
data: typeof wp !== 'undefined' && typeof wp.data !== 'undefined',
|
||||
i18n: typeof wp !== 'undefined' && typeof wp.i18n !== 'undefined',
|
||||
};
|
||||
|
||||
console.log('Dependencies check:', deps);
|
||||
|
||||
// Try to register a simple plugin
|
||||
if (deps.wp && deps.element && deps.components) {
|
||||
const { registerPlugin } = wp.plugins;
|
||||
const { PluginSidebar } = wp.editPost;
|
||||
const { Panel, PanelBody } = wp.components;
|
||||
const { __ } = wp.i18n;
|
||||
|
||||
const TestSidebar = () => {
|
||||
return wp.element.createElement(
|
||||
PluginSidebar,
|
||||
{ name: 'wp-agentic-writer-test', title: 'WP Agentic Writer Test' },
|
||||
wp.element.createElement(
|
||||
Panel,
|
||||
null,
|
||||
wp.element.createElement(
|
||||
PanelBody,
|
||||
null,
|
||||
wp.element.createElement('p', null, 'Plugin loaded successfully! 🎉')
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
registerPlugin('wp-agentic-writer-test', {
|
||||
icon: 'edit',
|
||||
render: TestSidebar,
|
||||
});
|
||||
|
||||
console.log('Plugin registered successfully!');
|
||||
} else {
|
||||
console.error('Cannot register plugin - missing dependencies');
|
||||
}
|
||||
5713
assets/js/sidebar.js
Normal file
5713
assets/js/sidebar.js
Normal file
File diff suppressed because it is too large
Load Diff
5693
assets/js/sidebar.js.backup
Normal file
5693
assets/js/sidebar.js.backup
Normal file
File diff suppressed because it is too large
Load Diff
5152
assets/js/sidebar.js.bak
Normal file
5152
assets/js/sidebar.js.bak
Normal file
File diff suppressed because it is too large
Load Diff
2
assets/js/vendor/markdown-it-task-lists.min.js
vendored
Normal file
2
assets/js/vendor/markdown-it-task-lists.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/*! markdown-it-task-lists 2.1.0 https://github.com/revin/markdown-it-task-lists#readme by @license {ISC} */
|
||||
!function(n){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.markdownitTaskLists=n()}}(function(){return function(){function n(e,t,i){function r(c,l){if(!t[c]){if(!e[c]){var f="function"==typeof require&&require;if(!l&&f)return f(c,!0);if(o)return o(c,!0);var u=new Error("Cannot find module '"+c+"'");throw u.code="MODULE_NOT_FOUND",u}var a=t[c]={exports:{}};e[c][0].call(a.exports,function(n){var t=e[c][1][n];return r(t?t:n)},a,a.exports,n,e,t,i)}return t[c].exports}for(var o="function"==typeof require&&require,c=0;c<i.length;c++)r(i[c]);return r}return n}()({1:[function(n,e,t){function i(n,e,t){var i=n.attrIndex(e),r=[e,t];0>i?n.attrPush(r):n.attrs[i]=r}function r(n,e){for(var t=n[e].level-1,i=e-1;i>=0;i--)if(n[i].level===t)return i;return-1}function o(n,e){return s(n[e])&&d(n[e-1])&&h(n[e-2])&&p(n[e])}function c(n,e){if(n.children.unshift(l(n,e)),n.children[1].content=n.children[1].content.slice(3),n.content=n.content.slice(3),b)if(v){n.children.pop();var t="task-item-"+Math.ceil(1e7*Math.random()-1e3);n.children[0].content=n.children[0].content.slice(0,-1)+' id="'+t+'">',n.children.push(a(n.content,t,e))}else n.children.unshift(f(e)),n.children.push(u(e))}function l(n,e){var t=new e("html_inline","",0),i=x?' disabled="" ':"";return 0===n.content.indexOf("[ ] ")?t.content='<input class="task-list-item-checkbox"'+i+'type="checkbox">':(0===n.content.indexOf("[x] ")||0===n.content.indexOf("[X] "))&&(t.content='<input class="task-list-item-checkbox" checked=""'+i+'type="checkbox">'),t}function f(n){var e=new n("html_inline","",0);return e.content="<label>",e}function u(n){var e=new n("html_inline","",0);return e.content="</label>",e}function a(n,e,t){var i=new t("html_inline","",0);return i.content='<label class="task-list-item-label" for="'+e+'">'+n+"</label>",i.attrs=[{"for":e}],i}function s(n){return"inline"===n.type}function d(n){return"paragraph_open"===n.type}function h(n){return"list_item_open"===n.type}function p(n){return 0===n.content.indexOf("[ ] ")||0===n.content.indexOf("[x] ")||0===n.content.indexOf("[X] ")}var x=!0,b=!1,v=!1;e.exports=function(n,e){e&&(x=!e.enabled,b=!!e.label,v=!!e.labelAfter),n.core.ruler.after("inline","github-task-lists",function(n){for(var e=n.tokens,t=2;t<e.length;t++)o(e,t)&&(c(e[t],n.Token),i(e[t-2],"class","task-list-item"+(x?"":" enabled")),i(e[r(e,t-2)],"class","contains-task-list"))})}},{}]},{},[1])(1)});
|
||||
3
assets/js/vendor/markdown-it.min.js
vendored
Normal file
3
assets/js/vendor/markdown-it.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
assets/js/vendor/purify.min.js
vendored
Normal file
3
assets/js/vendor/purify.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user