feat: consolidate docs, backend/session infra, and settings updates
This commit is contained in:
BIN
assets/.DS_Store
vendored
Normal file
BIN
assets/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -530,3 +530,43 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* GEO Score Indicator */
|
||||
.wpaw-geo-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 16px;
|
||||
background: var(--wpaw-bg-secondary);
|
||||
border-top: 1px solid var(--wpaw-border);
|
||||
font-size: var(--wpaw-text-sm);
|
||||
}
|
||||
|
||||
.wpaw-geo-score {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpaw-geo-score.excellent {
|
||||
color: var(--wpaw-success);
|
||||
}
|
||||
|
||||
.wpaw-geo-score.good {
|
||||
color: var(--wpaw-info);
|
||||
}
|
||||
|
||||
.wpaw-geo-score.fair {
|
||||
color: var(--wpaw-warning);
|
||||
}
|
||||
|
||||
.wpaw-geo-score.poor {
|
||||
color: var(--wpaw-error);
|
||||
}
|
||||
|
||||
.wpaw-geo-eligible {
|
||||
background: var(--wpaw-success);
|
||||
color: #000;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
366
assets/css/agentic-workflow.css
Normal file
366
assets/css/agentic-workflow.css
Normal file
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* Agentic Vibe - Workflow Pipeline Component
|
||||
* 5-step visualization for AI writing workflow
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
* @since 0.2.0
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
Workflow Container
|
||||
============================================ */
|
||||
|
||||
.wpaw-workflow-progress {
|
||||
background: var(--wpaw-bg-secondary);
|
||||
padding: var(--wpaw-space-lg);
|
||||
border-radius: var(--wpaw-radius-md);
|
||||
margin-bottom: var(--wpaw-space-lg);
|
||||
border: 1px solid var(--wpaw-border);
|
||||
}
|
||||
|
||||
.wpaw-progress-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--wpaw-space-lg);
|
||||
}
|
||||
|
||||
.wpaw-progress-title {
|
||||
font-size: var(--wpaw-text-sm);
|
||||
font-weight: 600;
|
||||
color: var(--wpaw-text-primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.wpaw-progress-status {
|
||||
font-size: var(--wpaw-text-xs);
|
||||
color: var(--wpaw-text-tertiary);
|
||||
font-family: var(--wpaw-font-mono);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Progress Steps Container
|
||||
============================================ */
|
||||
|
||||
.wpaw-progress-steps {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Individual Step
|
||||
============================================ */
|
||||
|
||||
.wpaw-step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--wpaw-space-sm);
|
||||
flex: 0 0 auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.wpaw-step-circle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: var(--wpaw-text-sm);
|
||||
border: 2px solid var(--wpaw-border);
|
||||
background: var(--wpaw-bg-primary);
|
||||
color: var(--wpaw-text-tertiary);
|
||||
transition: all var(--wpaw-transition-normal);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Step Icons */
|
||||
.wpaw-step-icon {
|
||||
font-size: var(--wpaw-text-lg);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Completed State */
|
||||
.wpaw-step.completed .wpaw-step-circle {
|
||||
background: var(--wpaw-success);
|
||||
border-color: var(--wpaw-success);
|
||||
color: white;
|
||||
box-shadow: 0 0 0 4px rgba(40, 167, 69, 0.2);
|
||||
}
|
||||
|
||||
/* Active State */
|
||||
.wpaw-step.active .wpaw-step-circle {
|
||||
background: var(--wpaw-primary);
|
||||
border-color: var(--wpaw-primary);
|
||||
color: white;
|
||||
box-shadow: 0 0 0 8px rgba(23, 162, 184, 0.15);
|
||||
animation: wpaw-step-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes wpaw-step-pulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 0 8px rgba(23, 162, 184, 0.15);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 12px rgba(23, 162, 184, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pending State */
|
||||
.wpaw-step.pending .wpaw-step-circle {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.wpaw-step.pending .wpaw-step-label {
|
||||
color: var(--wpaw-text-tertiary);
|
||||
}
|
||||
|
||||
/* Error State */
|
||||
.wpaw-step.error .wpaw-step-circle {
|
||||
background: var(--wpaw-error);
|
||||
border-color: var(--wpaw-error);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Step Label
|
||||
============================================ */
|
||||
|
||||
.wpaw-step-label {
|
||||
font-size: var(--wpaw-text-xs);
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
color: var(--wpaw-text-secondary);
|
||||
width: 70px;
|
||||
transition: color var(--wpaw-transition-fast);
|
||||
}
|
||||
|
||||
.wpaw-step.active .wpaw-step-label {
|
||||
color: var(--wpaw-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wpaw-step.completed .wpaw-step-label {
|
||||
color: var(--wpaw-success);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Step Connector (Line between steps)
|
||||
============================================ */
|
||||
|
||||
.wpaw-step-connector {
|
||||
flex: 1;
|
||||
height: 3px;
|
||||
background: var(--wpaw-border);
|
||||
margin: 0 var(--wpaw-space-sm);
|
||||
position: relative;
|
||||
top: -28px;
|
||||
min-width: 40px;
|
||||
border-radius: 2px;
|
||||
transition: background var(--wpaw-transition-normal);
|
||||
}
|
||||
|
||||
/* Completed Connector */
|
||||
.wpaw-step-connector.completed {
|
||||
background: var(--wpaw-success);
|
||||
}
|
||||
|
||||
/* Active Connector - Animated */
|
||||
.wpaw-step-connector.active {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--wpaw-primary) 0%,
|
||||
var(--wpaw-primary) 50%,
|
||||
var(--wpaw-border) 50%,
|
||||
var(--wpaw-border) 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: wpaw-slide-progress 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes wpaw-slide-progress {
|
||||
0% {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Step Status Message
|
||||
============================================ */
|
||||
|
||||
.wpaw-step-message {
|
||||
margin-top: var(--wpaw-space-md);
|
||||
padding: var(--wpaw-space-sm) var(--wpaw-space-md);
|
||||
background: var(--wpaw-bg-tertiary);
|
||||
border-radius: var(--wpaw-radius-sm);
|
||||
font-size: var(--wpaw-text-sm);
|
||||
color: var(--wpaw-text-secondary);
|
||||
text-align: center;
|
||||
font-family: var(--wpaw-font-mono);
|
||||
border-left: 3px solid var(--wpaw-primary);
|
||||
}
|
||||
|
||||
.wpaw-step-message.success {
|
||||
border-left-color: var(--wpaw-success);
|
||||
color: var(--wpaw-success);
|
||||
}
|
||||
|
||||
.wpaw-step-message.error {
|
||||
border-left-color: var(--wpaw-error);
|
||||
color: var(--wpaw-error);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Compact Version (for header)
|
||||
============================================ */
|
||||
|
||||
.wpaw-workflow-compact {
|
||||
padding: var(--wpaw-space-md);
|
||||
}
|
||||
|
||||
.wpaw-workflow-compact .wpaw-step-circle {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: var(--wpaw-text-xs);
|
||||
}
|
||||
|
||||
.wpaw-workflow-compact .wpaw-step-icon {
|
||||
font-size: var(--wpaw-text-sm);
|
||||
}
|
||||
|
||||
.wpaw-workflow-compact .wpaw-step-label {
|
||||
font-size: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.wpaw-workflow-compact .wpaw-step-connector {
|
||||
top: -20px;
|
||||
height: 2px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Responsive Design
|
||||
============================================ */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.wpaw-progress-steps {
|
||||
flex-wrap: wrap;
|
||||
gap: var(--wpaw-space-md);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wpaw-step-connector {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wpaw-step {
|
||||
flex: 0 0 20%;
|
||||
}
|
||||
|
||||
.wpaw-step-label {
|
||||
width: 100%;
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.wpaw-workflow-progress {
|
||||
padding: var(--wpaw-space-md);
|
||||
}
|
||||
|
||||
.wpaw-step-circle {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.wpaw-step-label {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.wpaw-progress-title {
|
||||
font-size: var(--wpaw-text-xs);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Animation for Active Step
|
||||
============================================ */
|
||||
|
||||
.wpaw-step.active .wpaw-step-circle::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--wpaw-primary);
|
||||
border-top-color: transparent;
|
||||
border-right-color: transparent;
|
||||
animation: wpaw-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes wpaw-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Tooltip for Steps
|
||||
============================================ */
|
||||
|
||||
.wpaw-step[data-tooltip] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wpaw-step[data-tooltip]:hover .wpaw-step-circle {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Mini Progress Bar (alternative)
|
||||
============================================ */
|
||||
|
||||
.wpaw-mini-progress {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--wpaw-space-xs);
|
||||
font-size: var(--wpaw-text-xs);
|
||||
color: var(--wpaw-text-tertiary);
|
||||
}
|
||||
|
||||
.wpaw-mini-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
background: var(--wpaw-bg-tertiary);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wpaw-mini-progress-fill {
|
||||
height: 100%;
|
||||
background: var(--wpaw-primary);
|
||||
transition: width var(--wpaw-transition-normal);
|
||||
}
|
||||
|
||||
.wpaw-mini-progress-fill.success {
|
||||
background: var(--wpaw-success);
|
||||
}
|
||||
|
||||
.wpaw-mini-progress-text {
|
||||
font-family: var(--wpaw-font-mono);
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -98,6 +98,28 @@ ul.select2-results__options {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Workflow Pipeline Override for Dark Theme
|
||||
============================================ */
|
||||
|
||||
.wpaw-settings-v2-wrap .wpaw-workflow-progress {
|
||||
background: var(--wpaw-bg-secondary);
|
||||
border: 1px solid var(--wpaw-border);
|
||||
}
|
||||
|
||||
.wpaw-settings-v2-wrap .wpaw-step-connector {
|
||||
background: var(--wpaw-border);
|
||||
}
|
||||
|
||||
.wpaw-settings-v2-wrap .wpaw-step-message {
|
||||
background: var(--wpaw-bg-tertiary);
|
||||
}
|
||||
|
||||
/* Compact mode adjustments */
|
||||
.wpaw-settings-v2-wrap .wpaw-workflow-compact {
|
||||
padding: var(--wpaw-space-md);
|
||||
}
|
||||
|
||||
.wpaw-settings-v2-wrap .select2-container--bootstrap-5 .select2-results__option--selected {
|
||||
background-color: #37373d !important;
|
||||
color: #ffffff !important;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,15 @@
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
// Debug logging utility
|
||||
const isDebug = typeof wpAgenticWriter !== 'undefined' && wpAgenticWriter.debug;
|
||||
const wpawLog = {
|
||||
log: (...args) => { if (isDebug) console.log('[WPAW]', ...args); },
|
||||
error: (...args) => console.error('[WPAW]', ...args),
|
||||
info: (...args) => { if (isDebug) console.info('[WPAW]', ...args); },
|
||||
warn: (...args) => { if (isDebug) console.warn('[WPAW]', ...args); },
|
||||
};
|
||||
|
||||
// Global state
|
||||
const state = {
|
||||
models: {},
|
||||
@@ -20,71 +29,48 @@
|
||||
}
|
||||
};
|
||||
|
||||
// Preset configurations
|
||||
const presets = {
|
||||
budget: {
|
||||
chat: 'google/gemini-2.5-flash',
|
||||
clarity: 'google/gemini-2.5-flash',
|
||||
planning: 'google/gemini-2.5-flash',
|
||||
writing: 'mistralai/mistral-small-creative',
|
||||
refinement: 'google/gemini-2.5-flash',
|
||||
image: 'openai/gpt-4o'
|
||||
},
|
||||
balanced: {
|
||||
chat: 'google/gemini-2.5-flash',
|
||||
clarity: 'google/gemini-2.5-flash',
|
||||
planning: 'google/gemini-2.5-flash',
|
||||
writing: 'anthropic/claude-3.5-sonnet',
|
||||
refinement: 'anthropic/claude-3.5-sonnet',
|
||||
image: 'openai/gpt-4o'
|
||||
},
|
||||
premium: {
|
||||
chat: 'google/gemini-3-flash-preview',
|
||||
clarity: 'anthropic/claude-sonnet-4',
|
||||
planning: 'google/gemini-3-flash-preview',
|
||||
writing: 'openai/gpt-4.1',
|
||||
refinement: 'openai/gpt-4.1',
|
||||
image: 'openai/gpt-4o'
|
||||
}
|
||||
};
|
||||
// Preset configurations (sourced from PHP for single-source-of-truth).
|
||||
// These presets represent intentional product decisions for different budget tiers.
|
||||
// Model IDs may differ from registry defaults to balance cost/quality per tier.
|
||||
const presets = wpawSettingsV2?.presets || {};
|
||||
|
||||
// Debug function to check models
|
||||
window.wpawDebugModels = function () {
|
||||
console.log('=== WPAW Models Debug ===');
|
||||
console.log('Total model categories:', Object.keys(state.models).length);
|
||||
wpawLog.log('=== WPAW Models Debug ===');
|
||||
wpawLog.log('Total model categories:', Object.keys(state.models).length);
|
||||
|
||||
Object.keys(state.models).forEach(category => {
|
||||
const models = state.models[category]?.all || [];
|
||||
console.log(`\n${category.toUpperCase()}: ${models.length} models`);
|
||||
wpawLog.log(`\n${category.toUpperCase()}: ${models.length} models`);
|
||||
|
||||
// Check for specific models
|
||||
const checkIds = ['deepseek/deepseek-chat-v3-0324', 'anthropic/claude-3.5-sonnet'];
|
||||
checkIds.forEach(id => {
|
||||
const found = models.find(m => m.id === id);
|
||||
if (found) {
|
||||
console.log(` ✓ FOUND: ${id} => ${found.name}`);
|
||||
wpawLog.log(` ✓ FOUND: ${id} => ${found.name}`);
|
||||
} else {
|
||||
console.log(` ✗ NOT FOUND: ${id}`);
|
||||
wpawLog.log(` ✗ NOT FOUND: ${id}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Show models with raw is_free and pricing data
|
||||
if (category === 'image') {
|
||||
console.log(` ALL image models (raw data from PHP):`);
|
||||
wpawLog.log(` ALL image models (raw data from PHP):`);
|
||||
models.forEach(m => {
|
||||
console.log(` - ${m.id} | is_free=${m.is_free} | pricing=`, m.pricing);
|
||||
wpawLog.log(` - ${m.id} | is_free=${m.is_free} | pricing=`, m.pricing);
|
||||
});
|
||||
} else {
|
||||
// Show first 10 models with is_free status
|
||||
console.log(` First 10 models (raw data from PHP):`);
|
||||
wpawLog.log(` First 10 models (raw data from PHP):`);
|
||||
models.slice(0, 10).forEach(m => {
|
||||
console.log(` - ${m.id} | is_free=${m.is_free} | pricing=`, m.pricing);
|
||||
wpawLog.log(` - ${m.id} | is_free=${m.is_free} | pricing=`, m.pricing);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX debug call
|
||||
console.log('\n=== Fetching from server ===');
|
||||
wpawLog.log('\n=== Fetching from server ===');
|
||||
$.ajax({
|
||||
url: wpawSettingsV2.ajaxUrl,
|
||||
type: 'POST',
|
||||
@@ -94,17 +80,17 @@
|
||||
},
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
console.log('Server response:', response.data);
|
||||
console.log('Total models from API:', response.data.total_models);
|
||||
console.log('Found models:', response.data.found_models);
|
||||
console.log('Missing models:', response.data.missing_models);
|
||||
console.log('Sample models:', response.data.sample_models);
|
||||
wpawLog.log('Server response:', response.data);
|
||||
wpawLog.log('Total models from API:', response.data.total_models);
|
||||
wpawLog.log('Found models:', response.data.found_models);
|
||||
wpawLog.log('Missing models:', response.data.missing_models);
|
||||
wpawLog.log('Sample models:', response.data.sample_models);
|
||||
} else {
|
||||
console.error('Error:', response.data.message);
|
||||
wpawLog.error('Error:', response.data.message);
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error('AJAX error:', error);
|
||||
wpawLog.error('AJAX error:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -127,7 +113,7 @@
|
||||
});
|
||||
|
||||
// Log debug info
|
||||
console.log('WPAW Settings V2 loaded. Run wpawDebugModels() to debug model issues.');
|
||||
wpawLog.log('WPAW Settings V2 loaded. Run wpawDebugModels() to debug model issues.');
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -170,7 +156,7 @@
|
||||
const newOption = new Option(modelData.name || currentValue, currentValue, true, true);
|
||||
$select.append(newOption).trigger('change');
|
||||
} else {
|
||||
console.warn('Model not found in list:', currentValue);
|
||||
wpawLog.warn('Model not found in list:', currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,17 +373,17 @@
|
||||
* Initialize cost log functionality
|
||||
*/
|
||||
function initCostLog() {
|
||||
console.log('Initializing cost log...');
|
||||
wpawLog.log('Initializing cost log...');
|
||||
|
||||
// Load on tab show
|
||||
$('#cost-log-tab').on('shown.bs.tab', function () {
|
||||
console.log('Cost log tab shown, loading data...');
|
||||
wpawLog.log('Cost log tab shown, loading data...');
|
||||
loadCostLogData();
|
||||
});
|
||||
|
||||
// Auto-load if cost-log tab is active on page load
|
||||
if ($('#cost-log-tab').hasClass('active')) {
|
||||
console.log('Cost log tab is active on load, loading data...');
|
||||
wpawLog.log('Cost log tab is active on load, loading data...');
|
||||
loadCostLogData();
|
||||
}
|
||||
|
||||
@@ -449,12 +435,12 @@
|
||||
* Load cost log data via AJAX
|
||||
*/
|
||||
function loadCostLogData() {
|
||||
console.log('loadCostLogData called');
|
||||
console.log('wpawSettingsV2:', wpawSettingsV2);
|
||||
console.log('State:', state);
|
||||
wpawLog.log('loadCostLogData called');
|
||||
wpawLog.log('wpawSettingsV2:', wpawSettingsV2);
|
||||
wpawLog.log('State:', state);
|
||||
|
||||
const $tbody = $('#wpaw-cost-log-tbody');
|
||||
console.log('Table tbody found:', $tbody.length);
|
||||
wpawLog.log('Table tbody found:', $tbody.length);
|
||||
|
||||
$tbody.html(`
|
||||
<tr>
|
||||
@@ -478,14 +464,14 @@
|
||||
filter_date_to: state.filters.dateTo
|
||||
};
|
||||
|
||||
console.log('AJAX request data:', ajaxData);
|
||||
wpawLog.log('AJAX request data:', ajaxData);
|
||||
|
||||
$.ajax({
|
||||
url: wpawSettingsV2.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: ajaxData,
|
||||
success: function (response) {
|
||||
console.log('Cost log response:', response);
|
||||
wpawLog.log('Cost log response:', response);
|
||||
if (response.success) {
|
||||
renderCostLogTable(response.data);
|
||||
updateCostLogStats(response.data.stats);
|
||||
@@ -493,14 +479,14 @@
|
||||
renderPagination(response.data);
|
||||
} else {
|
||||
const errorMsg = response.data?.message || 'Error loading data';
|
||||
console.error('Cost log error:', errorMsg);
|
||||
wpawLog.error('Cost log error:', errorMsg);
|
||||
$tbody.html('<tr><td colspan="7" class="text-center py-4 text-danger">' + escapeHtml(errorMsg) + '</td></tr>');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error('Cost log AJAX error:', status, error);
|
||||
console.error('XHR:', xhr);
|
||||
console.error('Response text:', xhr.responseText);
|
||||
wpawLog.error('Cost log AJAX error:', status, error);
|
||||
wpawLog.error('XHR:', xhr);
|
||||
wpawLog.error('Response text:', xhr.responseText);
|
||||
$tbody.html('<tr><td colspan="7" class="text-center py-4 text-danger">Failed to load cost log. Check browser console for details.</td></tr>');
|
||||
}
|
||||
});
|
||||
@@ -1022,4 +1008,133 @@
|
||||
initSelect2();
|
||||
}
|
||||
|
||||
/**
|
||||
* Workflow Pipeline Status Display
|
||||
* Updates the 5-step workflow visualization based on backend status
|
||||
*/
|
||||
function initWorkflowDisplay() {
|
||||
// Status mapping from backend to step index
|
||||
// Backend statuses: starting, planning, plan_complete, writing, writing_section, refinement, checking, complete
|
||||
const statusToStep = {
|
||||
'starting': 1, // Context
|
||||
'planning': 2, // Planning
|
||||
'plan_complete': 2, // Planning (done)
|
||||
'writing': 3, // Writing
|
||||
'writing_section': 3, // Writing
|
||||
'refinement': 4, // Refinement
|
||||
'refining': 4, // Refinement
|
||||
'checking': 4, // Refinement
|
||||
'complete': 5, // Done
|
||||
'done': 5, // Done
|
||||
};
|
||||
|
||||
// Status messages mapping
|
||||
const statusMessages = {
|
||||
'starting': 'Loading context and analyzing post...',
|
||||
'planning': 'Creating article outline...',
|
||||
'plan_complete': 'Outline ready, starting to write...',
|
||||
'writing': 'Generating article content...',
|
||||
'writing_section': 'Writing section content...',
|
||||
'refinement': 'Polishing and optimizing content...',
|
||||
'refining': 'Applying refinements...',
|
||||
'checking': 'Checking quality and consistency...',
|
||||
'complete': 'Article finished successfully!',
|
||||
'done': 'All done!',
|
||||
};
|
||||
|
||||
/**
|
||||
* Update workflow display based on status
|
||||
* @param {string} status - Backend status string
|
||||
* @param {string} message - Optional custom message
|
||||
*/
|
||||
window.updateWorkflowStatus = function(status, message) {
|
||||
const stepIndex = statusToStep[status] || 0;
|
||||
const $workflow = $('#wpaw-workflow-display');
|
||||
|
||||
if (!$workflow.length) return;
|
||||
|
||||
const $steps = $workflow.find('.wpaw-step');
|
||||
const $connectors = $workflow.find('.wpaw-step-connector');
|
||||
const $statusText = $('#wpaw-workflow-status');
|
||||
const $messageEl = $('#wpaw-workflow-message');
|
||||
|
||||
// Reset all steps
|
||||
$steps.removeClass('active completed pending error');
|
||||
$connectors.removeClass('active completed');
|
||||
|
||||
// Update steps based on current status
|
||||
$steps.each(function(index) {
|
||||
const $step = $(this);
|
||||
const stepNum = index + 1;
|
||||
|
||||
if (stepNum < stepIndex) {
|
||||
// Completed steps
|
||||
$step.addClass('completed');
|
||||
if ($connectors[index]) {
|
||||
$($connectors[index]).addClass('completed');
|
||||
}
|
||||
} else if (stepNum === stepIndex) {
|
||||
// Active step
|
||||
$step.addClass('active');
|
||||
if ($connectors[index]) {
|
||||
$($connectors[index]).addClass('active');
|
||||
}
|
||||
} else {
|
||||
// Pending steps
|
||||
$step.addClass('pending');
|
||||
}
|
||||
});
|
||||
|
||||
// Update status text
|
||||
const statusText = stepIndex > 0 ? `Step ${stepIndex} of 5` : 'Idle';
|
||||
$statusText.text(statusText);
|
||||
|
||||
// Show message if provided
|
||||
if (message || statusMessages[status]) {
|
||||
const displayMessage = message || statusMessages[status];
|
||||
$messageEl.text(displayMessage).show();
|
||||
|
||||
// Add appropriate class
|
||||
$messageEl.removeClass('success error');
|
||||
if (status === 'complete' || status === 'done') {
|
||||
$messageEl.addClass('success');
|
||||
} else if (status === 'error') {
|
||||
$messageEl.addClass('error');
|
||||
}
|
||||
} else {
|
||||
$messageEl.hide();
|
||||
}
|
||||
};
|
||||
|
||||
// Demo function for testing - cycles through all steps
|
||||
window.demoWorkflow = function() {
|
||||
const statuses = ['starting', 'planning', 'plan_complete', 'writing', 'refinement', 'complete'];
|
||||
let index = 0;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
updateWorkflowStatus(statuses[index]);
|
||||
index++;
|
||||
|
||||
if (index >= statuses.length) {
|
||||
clearInterval(interval);
|
||||
setTimeout(() => {
|
||||
// Reset to idle
|
||||
$('#wpaw-workflow-status').text('Idle');
|
||||
$('#wpaw-workflow-message').hide();
|
||||
$('.wpaw-step').removeClass('active completed').addClass('pending');
|
||||
$('.wpaw-step-connector').removeClass('active completed');
|
||||
}, 2000);
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// Initialize with idle state
|
||||
updateWorkflowStatus('idle');
|
||||
}
|
||||
|
||||
// Initialize workflow display on page load
|
||||
$(document).ready(function() {
|
||||
initWorkflowDisplay();
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
|
||||
215
assets/js/sidebar-utils.js
Normal file
215
assets/js/sidebar-utils.js
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* WP Agentic Writer - Utility Functions
|
||||
*
|
||||
* Pure utility functions with no React dependencies
|
||||
* These are shared utilities that can be used by any script
|
||||
*
|
||||
* @package WP_Agentic_Writer
|
||||
*/
|
||||
|
||||
// Escape HTML to prevent XSS
|
||||
const escapeHtml = (value) => {
|
||||
if (value === null || value === undefined) return '';
|
||||
return String(value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
};
|
||||
|
||||
// Normalize message content (convert objects/arrays to string)
|
||||
const normalizeMessageContent = (content) => {
|
||||
if (typeof content === 'string' || typeof content === 'number') {
|
||||
return String(content);
|
||||
}
|
||||
return JSON.stringify(content);
|
||||
};
|
||||
|
||||
// Truncate text with ellipsis
|
||||
const truncateText = (text, maxLength = 40) => {
|
||||
if (!text || text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
return text.substring(0, maxLength) + '...';
|
||||
};
|
||||
|
||||
// Convert markdown to HTML (full renderer)
|
||||
const markdownToHtml = (markdown, markdownit, DOMPurify) => {
|
||||
const raw = normalizeMessageContent(markdown);
|
||||
if (!raw) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let rendered = '';
|
||||
if (markdownit && DOMPurify) {
|
||||
const renderer = markdownit({
|
||||
html: false,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
});
|
||||
|
||||
if (window.markdownitTaskLists) {
|
||||
renderer.use(window.markdownitTaskLists, { enabled: true, label: true, labelAfter: true });
|
||||
}
|
||||
|
||||
rendered = renderer.render(raw);
|
||||
|
||||
if (DOMPurify.sanitize) {
|
||||
rendered = DOMPurify.sanitize(rendered, {
|
||||
ADD_TAGS: ['input'],
|
||||
ADD_ATTR: ['type', 'checked', 'disabled'],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return rendered;
|
||||
};
|
||||
|
||||
// Extract code blocks from HTML
|
||||
const extractCodeBlocks = (html) => {
|
||||
const codeBlocks = [];
|
||||
const preRegex = /<pre[^>]*><code(?:\s+class="language-([^"]*)")?>([\s\S]*?)<\/code><\/pre>/g;
|
||||
let match;
|
||||
|
||||
while ((match = preRegex.exec(html)) !== null) {
|
||||
const lang = match[1] || '';
|
||||
const code = match[2]
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'");
|
||||
codeBlocks.push({ lang, code });
|
||||
}
|
||||
|
||||
return codeBlocks;
|
||||
};
|
||||
|
||||
// Debounce function
|
||||
const debounce = (func, wait) => {
|
||||
let timeout;
|
||||
return function executedFunction(...args) {
|
||||
const later = () => {
|
||||
clearTimeout(timeout);
|
||||
func(...args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
};
|
||||
|
||||
// Parse outline plan from AI response
|
||||
const parseOutlinePlan = (content) => {
|
||||
const sections = [];
|
||||
const lines = content.split('\n');
|
||||
|
||||
let currentSection = null;
|
||||
let currentSubsection = null;
|
||||
|
||||
lines.forEach((line) => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) return;
|
||||
|
||||
// H2 section (## Title)
|
||||
const h2Match = trimmed.match(/^##\s+(.+)$/);
|
||||
if (h2Match) {
|
||||
if (currentSection) {
|
||||
sections.push(currentSection);
|
||||
}
|
||||
currentSection = {
|
||||
id: 'section-' + (sections.length + 1),
|
||||
title: h2Match[1].trim(),
|
||||
subsections: [],
|
||||
};
|
||||
currentSubsection = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// H3 subsection (### Title)
|
||||
const h3Match = trimmed.match(/^###\s+(.+)$/);
|
||||
if (h3Match && currentSection) {
|
||||
currentSubsection = {
|
||||
id: 'subsection-' + (currentSection.subsections.length + 1),
|
||||
title: h3Match[1].trim(),
|
||||
content: '',
|
||||
};
|
||||
currentSection.subsections.push(currentSubsection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Content line
|
||||
if (currentSection) {
|
||||
if (currentSubsection) {
|
||||
currentSubsection.content += (currentSubsection.content ? '\n' : '') + trimmed;
|
||||
} else {
|
||||
if (!currentSection.content) {
|
||||
currentSection.content = trimmed;
|
||||
} else {
|
||||
currentSection.content += '\n' + trimmed;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (currentSection) {
|
||||
sections.push(currentSection);
|
||||
}
|
||||
|
||||
return sections;
|
||||
};
|
||||
|
||||
// Parse FAQ schema from AI response
|
||||
const parseFaqSchema = (content) => {
|
||||
const faqs = [];
|
||||
const faqBlocks = content.split(/\n\s*#{1,2}\s*Q[^\n]*\n/);
|
||||
|
||||
faqBlocks.slice(1).forEach((block) => {
|
||||
const lines = block.trim().split('\n');
|
||||
if (lines.length >= 2) {
|
||||
const question = lines[0].replace(/^[#*]+\s*/, '').trim();
|
||||
const answer = lines.slice(1).join('\n').trim();
|
||||
if (question && answer) {
|
||||
faqs.push({ question, answer });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return faqs;
|
||||
};
|
||||
|
||||
// Extract block preview from content
|
||||
const extractBlockPreview = (block) => {
|
||||
if (!block) return '';
|
||||
|
||||
const content = block.innerHTML || block.content || '';
|
||||
const text = content.replace(/<[^>]+>/g, '').trim();
|
||||
return truncateText(text, 100);
|
||||
};
|
||||
|
||||
// To text value helper
|
||||
const toTextValue = (value) => {
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number') return String(value);
|
||||
if (Array.isArray(value)) return value.map(toTextValue).join(', ');
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// Export for use in other modules
|
||||
if (typeof window !== 'undefined') {
|
||||
window.WPAWUtils = {
|
||||
escapeHtml,
|
||||
normalizeMessageContent,
|
||||
truncateText,
|
||||
markdownToHtml,
|
||||
extractCodeBlocks,
|
||||
debounce,
|
||||
parseOutlinePlan,
|
||||
parseFaqSchema,
|
||||
extractBlockPreview,
|
||||
toTextValue,
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user