<?php
/**
* Plugin Name: MyAI Gemini Interface Settings & UI Widget
* Plugin URI: https://www.brettanthonydixon.com/bits.brettanthonydixon.com/projects/ai/
* Description: Extends WordPress user profiles with MyAI Gemini Interface settings and capabilities, and provides a UI widget.
* Version: 1.3.0
* Author: Brett Anthony Dixon
* Author URI: https://www.brettanthonydixon.com/
* License: GPL2
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* MyAI_Gemini_Interface_Settings Class
* Manages the custom settings for MyAI Gemini Interface.
*/
class MyAI_Gemini_Interface_Settings {
public function __construct() {
// Add fields to user profile for both regular users and admins
add_action( 'show_user_profile', array( $this, 'add_myai_user_profile_fields' ) );
add_action( 'edit_user_profile', array( $this, 'add_myai_user_profile_fields' ) );
// Save custom fields
add_action( 'personal_options_update', array( $this, 'save_myai_user_profile_fields' ) );
add_action( 'edit_user_profile_update', array( $this, 'save_myai_user_profile_fields' ) );
// Enqueue styles and scripts for the widget where needed
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_myai_scripts_and_styles' ) );
// NEW: Register custom blocks
add_action( 'init', array( $this, 'register_myai_blocks' ) );
}
/**
* Enqueue custom scripts and styles.
*/
public function enqueue_myai_scripts_and_styles() {
// Enqueue main UI stylesheet
wp_enqueue_style( 'myai-styles', plugins_url( 'styles.css', __FILE__ ), array(), '1.0.0', 'all' );
// Enqueue Split.js from a local copy in the plugin directory
wp_enqueue_script( 'split-js', plugins_url( 'js/split.min.js', __FILE__ ), array(), '1.6.5', true );
// Enqueue your custom app script
wp_enqueue_script( 'myai-app-script', plugins_url( 'js/myai-app.js', __FILE__ ), array('split-js'), '1.0.0', true );
// Pass dynamic data to the app script (e.g., user info, settings)
$current_user = wp_get_current_user();
$myai_user_role_config = get_user_meta( $current_user->ID, 'myai_user_role_config', true );
if ( empty( $myai_user_role_config ) ) {
// Default mapping if not explicitly set
if ( in_array( 'administrator', (array) $current_user->roles ) ) {
$myai_user_role_config = 'Administrator';
} elseif ( in_array( 'editor', (array) $current_user->roles ) || in_array( 'author', (array) $current_user->roles ) ) {
$myai_user_role_config = 'Writer';
} elseif ( in_array( 'contributor', (array) $current_user->roles ) ) {
$myai_user_role_config = 'Contributor';
} elseif ( in_array( 'subscriber', (array) $current_user->roles ) ) {
$myai_user_role_config = 'Subscriber';
} else {
$myai_user_role_config = 'Viewer';
}
}
$user_data = array(
'is_logged_in' => is_user_logged_in(),
'user_id' => get_current_user_id(),
'user_name' => is_user_logged_in() ? $current_user->display_name : '',
'user_email' => is_user_logged_in() ? $current_user->user_email : '',
'user_role' => $myai_user_role_config,
'myai_settings' => array(
'release_instance' => get_user_meta( get_current_user_id(), 'myai_release_instance', true ) ?: 'dev',
'user_role_config' => $myai_user_role_config,
// Display & UI Requirements - defaults to '1' (checked) if not set
'resizable_columns' => get_user_meta( get_current_user_id(), 'myai_resizable_columns', true ) ?: '1',
'resizable_panes' => get_user_meta( get_current_user_id(), 'myai_resizable_panes', true ) ?: '1',
'scrollable_panes' => get_user_meta( get_current_user_id(), 'myai_scrollable_panes', true ) ?: '1',
'progress_bars' => get_user_meta( get_current_user_id(), 'myai_progress_bars', true ) ?: '1',
'fractal_grid' => get_user_meta( get_current_user_id(), 'myai_fractal_grid', true ) ?: '1',
// Gemini Output Display Settings
'ai_rendering_display' => get_user_meta( get_current_user_id(), 'myai_ai_rendering_display', true ) ?: 'direct',
'ai_thinking_indicator' => get_user_meta( get_current_user_id(), 'myai_ai_thinking_indicator', true ) ?: 'none',
'ai_text_color' => get_user_meta( get_current_user_id(), 'myai_ai_text_color', true ) ?: '#E3E3E3',
'user_input_text_color' => get_user_meta( get_current_user_id(), 'myai_user_input_text_color', true ) ?: '#4285F4',
'system_messages_text_color' => get_user_meta( get_current_user_id(), 'myai_system_messages_text_color', true ) ?: '#ADD8E6',
'user_color_scheme' => get_user_meta( get_current_user_id(), 'myai_user_color_scheme', true ) ?: '',
// Explicitly state 'not available' features for frontend to ghost
'ada_compliance_available' => '0',
'auto_window_opening_available' => '0',
'contextual_understanding_available' => '0',
'interpret_misspellings_available' => '0',
'voice_inputs_available' => '0',
'humor_holiday_cards_available' => '0',
'source_credibility_threshold_available' => '0',
'output_truth_threshold_available' => '0',
// New color settings are also initially unavailable
'ai_text_color_available' => '0',
'user_input_text_color_available' => '0',
'system_messages_text_color_available' => '0',
'user_color_scheme_available' => '0',
),
'base_post_url_template' => 'https://bits.brettanthonydixon.com/{YYYY}/{MM}/{DD}/projects/ai/',
);
wp_localize_script( 'myai-app-script', 'myaiAppData', $user_data );
}
/**
* Register custom blocks for the dashboard layout.
*/
public function register_myai_blocks() {
// Register the main dashboard layout block
register_block_type( 'myai/dashboard-layout-block', array(
'render_callback' => array( $this, 'render_dashboard_layout_block' ),
'editor_script' => 'myai-block-editor-script', // JS for editor UI
'editor_style' => 'myai-block-editor-style', // CSS for editor UI
'style' => 'myai-styles', // Frontend CSS already enqueued
'script' => 'myai-app-script', // Frontend JS already enqueued
) );
// You would register other individual content blocks here as needed (e.g., myai/gmail-display-block)
// For now, the main dashboard block will render all 9 sections as simple divs.
}
/**
* Render callback for the MyAI Dashboard Layout Block.
* This will create the 10x10 grid with 9 major sections as divs.
*
* @param array $attributes Block attributes.
* @param string $content Block content.
* @param WP_Block $block The parsed block.
* @return string HTML output for the block.
*/
public function render_dashboard_layout_block( $attributes, $content, $block ) {
// This is a simplified representation of a 10x10 CSS Grid mapping to 9 conceptual areas.
// The actual CSS will handle the grid layout.
$output = '<div id="myai-dashboard-container" class="myai-dashboard-layout-bagua">';
// Define the 9 major sections conceptually within the 10x10 grid.
// The classes will be styled in styles.css to represent the background colors and grid areas.
// Row 1 (Top) - Visual (0:0 to X:2)
$output .= '<div class="myai-grid-section top-left" data-grid-cell="0:0-2:2">';
$output .= '<h3>Company/Logo</h3>'; // Placeholder name
$output .= '<p>Domain & Subdomains</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section top-middle" data-grid-cell="3:0-5:2">';
$output .= '<h3>Notification Center</h3>'; // Placeholder name
$output .= '<p>Bulletins, App Notifications, Meet Carousel</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section top-right" data-grid-cell="6:0-8:2">';
$output .= '<h3>User Account</h3>'; // Placeholder name
$output .= '<p>Sign In, Manage Accounts, Settings, Personal Apps</p>';
$output .= '</div>';
// Row 2 (Middle) - Visual (0:3 to X:5)
$output .= '<div class="myai-grid-section middle-left" data-grid-cell="0:3-2:5">';
$output .= '<h3>Navigation</h3>'; // Placeholder name
$output .= '<p>Project Hubs, Drive Tree, Search & Saved Views</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section center-center" data-grid-cell="3:3-5:5">';
$output .= '<h3>Main Display</h3>'; // Placeholder name
$output .= '<p>Canvas Previews, Shared Screen, Tool Displays, Saved Views</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section middle-right" data-grid-cell="6:3-8:5">';
$output .= '<h3>AI Info & Output</h3>'; // Placeholder name
$output .= '<p>AI Status, Gemini Output, Contacts, Proactive Advice</p>';
$output .= '</div>';
// Row 3 (Bottom) - Visual (0:6 to X:8)
$output .= '<div class="myai-grid-section bottom-left" data-grid-cell="0:6-2:8">';
$output .= '<h3>Libraries</h3>'; // Placeholder name
$output .= '<p>Resources, Support Docs, KBs, Wikipedia</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section bottom-center" data-grid-cell="3:6-5:8">';
$output .= '<h3>Command Prompt</h3>'; // Placeholder name
$output .= '<p>Terminal, Voice2Text, History, File Uploads, Video Chat</p>';
$output .= '</div>';
$output .= '<div class="myai-grid-section bottom-right" data-grid-cell="6:6-8:8">';
$output .= '<h3>Tools</h3>'; // Placeholder name
$output .= '<p>Maps, Drive</p>';
$output .= '</div>';
$output .= '</div>'; // Close myai-dashboard-container
return $output;
}
/**
* Save custom fields from the user profile screen.
* (Existing code, unchanged for layout changes)
*/
public function save_myai_user_profile_fields( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) { return false; }
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'update-user_' . $user_id ) ) { return $user_id; }
if ( isset( $_POST['myai_user_role_config'] ) ) { update_user_meta( $user_id, 'myai_user_role_config', sanitize_text_field( $_POST['myai_user_role_config'] ) ); }
if ( isset( $_POST['myai_release_instance'] ) ) { update_user_meta( $user_id, 'myai_release_instance', sanitize_text_field( $_POST['myai_release_instance'] ) ); }
update_user_meta( $user_id, 'myai_resizable_columns', isset( $_POST['myai_resizable_columns'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_resizable_panes', isset( $_POST['myai_resizable_panes'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_scrollable_panes', isset( $_POST['myai_scrollable_panes'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_progress_bars', isset( $_POST['myai_progress_bars'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_fractal_grid', isset( $_POST['myai_fractal_grid'] ) ? '1' : '0' );
if ( isset( $_POST['myai_ai_rendering_display'] ) ) { update_user_meta( $user_id, 'myai_ai_rendering_display', sanitize_text_field( $_POST['myai_ai_rendering_display'] ) ); }
if ( isset( $_POST['myai_ai_thinking_indicator'] ) ) { update_user_meta( $user_id, 'myai_ai_thinking_indicator', sanitize_text_field( $_POST['myai_ai_thinking_indicator'] ) ); }
if ( isset( $_POST['myai_user_color_scheme'] ) ) { update_user_meta( $user_id, 'myai_user_color_scheme', sanitize_text_field( $_POST['myai_user_color_scheme'] ) ); }
if ( isset( $_POST['myai_export_format'] ) ) { update_user_meta( $user_id, 'myai_export_format', sanitize_text_field( $_POST['myai_export_format'] ) ); }
update_user_meta( $user_id, 'myai_download_file_naming', isset( $_POST['myai_download_file_naming'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_command_prompt', isset( $_POST['myai_command_prompt'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_proactive_advisory', isset( $_POST['myai_proactive_advisory'] ) ? '1' : '0' );
update_user_meta( $user_id, 'myai_user_commands_no_return', isset( $_POST['myai_user_commands_no_return'] ) ? '1' : '0' );
}
}
/**
* Class for the MyAI Post Listing Shortcode.
* This class also handles the [myai_interface_widget] shortcode.
* (Existing code, mostly unchanged, moved shortcode to separate class for clarity)
*/
class MyAI_Post_Listing_Shortcode {
public function __construct() {
add_shortcode( 'myai_posts', array( $this, 'render_myai_posts_shortcode' ) );
add_shortcode( 'myai_interface_widget', array( $this, 'render_myai_interface_widget_shortcode' ) );
}
public function render_myai_posts_shortcode( $atts ) {
ob_start();
$atts = shortcode_atts( array(
'category' => 'ai', 'include_children' => true, 'orderby' => 'date', 'order' => 'DESC', 'posts_per_page' => -1,
), $atts, 'myai_posts' );
$categories = explode( ',', $atts['category'] );
$category_slugs = array_map( 'trim', $categories );
$args = array(
'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => (int) $atts['posts_per_page'], 'orderby' => sanitize_text_field( $atts['orderby'] ), 'order' => sanitize_text_field( $atts['order'] ), 'tax_query' => array(
array(
'taxonomy' => 'category', 'field' => 'slug', 'terms' => $category_slugs, 'include_children' => filter_var( $atts['include_children'], FILTER_VALIDATE_BOOLEAN ),
),
),
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
echo '<ul class="myai-post-list">';
while ( $query->have_posts() ) {
$query->the_post();
$post_id = get_the_ID();
$post_title = get_the_title();
$post_link = get_permalink();
$post_date = get_the_date();
$post_categories = get_the_category( $post_id );
$category_names = array_map( function( $cat ) {
if ( in_array( $cat->slug, array( 'projects', 'ai' ) ) ) { return ''; } return $cat->name;
}, $post_categories );
$category_names = array_filter( $category_names );
$categories_display = ! empty( $category_names ) ? ' (' . implode( ', ', $category_names ) . ')' : '';
echo '<li>';
echo '<a href="' . esc_url( $post_link ) . '">' . esc_html( $post_title ) . '</a>';
echo '<span class="myai-post-meta"> - Posted on ' . esc_html( $post_date ) . esc_html( $categories_display ) . '</span>';
echo '</li>';
}
echo '</ul>';
wp_reset_postdata();
} else {
echo '<p>No posts found in the specified categories.</p>';
}
return ob_get_clean();
}
public function render_myai_interface_widget_shortcode( $atts ) {
// This shortcode outputs the div that the JS app will target for the full layout.
// Now, this div will contain the custom block structure rendered by the block editor.
return '<div id="app-root" class="myai-app-root-container"></div>'; // Added a class for clearer CSS targeting
}
}
/**
* Register the MyAI Interface Widget (Legacy Widget for sidebar/footer, if needed)
* (Existing code, unchanged)
*/
class MyAI_Interface_Widget extends WP_Widget {
function __construct() {
parent::__construct( 'myai_interface_widget', __( 'MyAI Interface Widget', 'text_domain' ), array( 'description' => __( 'A widget to display the MyAI Gemini Interface grid container.', 'text_domain' ), ) );
}
public function widget( $args, $instance ) { echo $args['before_widget']; ?><div id="app-root"></div><?php echo $args['after_widget']; }
public function form( $instance ) { _e( 'This widget displays the MyAI Gemini Interface grid.', 'text_domain' ); }
public function update( $new_instance, $old_instance ) { return $new_instance; }
}
function register_myai_interface_widget() { register_widget( 'MyAI_Interface_Widget' ); }
add_action( 'widgets_init', 'register_myai_interface_widget' );
// Instantiate the plugin classes.
if ( is_admin() ) {
new MyAI_Gemini_Interface_Settings();
}
new MyAI_Post_Listing_Shortcode();
my-ai-interface-plugin.php