Skip to main content

Lexxy Rich Text Templates 📝

This feature provides AI-powered rich text template integration using Lexxy, allowing users to create reusable prompt templates with category-based filtering and slash command access.

Overview​

The Lexxy Rich Text Templates feature introduces:

  • CmPrompt Model: Stores reusable rich text templates with category associations
  • Lexxy Integration: AI-powered slash commands (/) in rich text editors
  • Category-Based Filtering: Organize templates by categories for easy access
  • Policy Scoping: Role-based access control for template visibility
  • Dynamic Scopes: Automatic scope methods for category filtering (e.g., estimation_only)

Installation​

1. Run the Generator​

rails generate cm_admin:add_prompts_with_constants

This creates two migrations:

  • create_cm_prompts - Creates the prompts table
  • create_constant_maps - Creates the polymorphic association table

2. Run Migrations​

rails db:migrate

3. Add CmPrompt to Included Models​

Update your CM Admin configuration:

# config/initializers/zcm_admin.rb
CmAdmin.configure do |config|
config.included_models = [Constant, User, CmRole, DemoForm, CmPlatformSetting, CmSupportTicket, CmCronJob, CmCronJobLog, ApiToken, CmPrompt]
# ... other configuration
end

4. Add Lexxy Template Platform Setting​

The generator automatically adds a platform setting for the default Lexxy template. You can configure it in the admin panel under Platform Settings > RichText Templates.

Model Structure​

CmPrompt​

class CmPrompt < ApplicationRecord
has_rich_text :prompt
has_many :categories, as: :mappable, dependent: :destroy, class_name: 'ConstantMap'
has_many :constants, through: :categories

validates :name, :slug, presence: true, uniqueness: { case_sensitive: false }
end

Fields:

  • name - Template name (required, unique)
  • slug - URL-friendly identifier (auto-generated from name)
  • prompt - Rich text content (ActionText)
  • created_by - User who created the template
  • updated_by - User who last updated the template

ConstantMap​

Polymorphic association table linking Constants to any model:

class ConstantMap < ApplicationRecord
belongs_to :constant
belongs_to :mappable, polymorphic: true
end

Usage​

Creating Templates​

  1. Navigate to Cm Prompts in the admin panel
  2. Click New Cm Prompt
  3. Fill in the template details:
    • Name: Template name (e.g., "Project Estimation")
    • Slug: Auto-generated, can be customized
    • Categories: Select one or more categories from cm_prompt_category constants
    • Prompt: Rich text content with formatting
  4. Save the template

Using Templates in Rich Text Fields​

When editing any rich text field in the admin panel:

  1. Type / in the rich text editor
  2. A dropdown appears with available templates
  3. Select a template to insert its content
  4. The template content is inserted as editable text

Category-Based Filtering​

Templates can be filtered by category using dynamic scope methods:

# Filter by specific category
CmPrompt.with_category('Estimation')

# Dynamic scope method (auto-generated from category name)
CmPrompt.estimation_only
CmPrompt.documentation_only
CmPrompt.reporting_only

The _only suffix is automatically added to category names to create scope methods.

Policy Scoping​

Templates respect role-based access control through policy scopes:

class CmPromptPolicy < ApplicationPolicy
class RichTextTemplateScope < Scope
def resolve
# Implement your access control logic
# Example: Only show templates for user's categories
scope.all
end
end
end

The policy scope name must follow the pattern: CmAdmin::{ModelName}PolicyRichTextTemplateScope

Platform Settings​

A new platform setting is added for Lexxy configuration:

  • Lexxy Template: The default Lexxy template to use

Access this in Platform Settings > RichText Templates.

API Endpoints​

Rich Text Templates Endpoint​

# lib/cm_admin/constants.rb
rich_text_templates: {
verb: :get,
path: 'rich_text_templates'
}

This endpoint is used by the Lexxy integration to fetch available templates.

Technical Details​

Lexxy Integration​

The Lexxy prompt system is integrated into rich text fields via the build_lexxy_prompt helper:

def build_lexxy_prompt
policy_scope_name = "CmAdmin::#{@model.ar_model.name}PolicyRichTextTemplateScope"
templates = policy_scope_name.constantize.new(Current.user, ::CmPrompt).resolve
template_items = templates.map { |t| build_lexxy_item(t) }.join
<<~HTML.html_safe
<lexxy-prompt trigger="/" name="template" insert-editable-text>#{template_items}</lexxy-prompt>
HTML
end

Dynamic Scope Methods​

The CmPrompt model uses method_missing to create dynamic scope methods:

def self.method_missing(method_name, *args, &)
if method_name.to_s.end_with?('_only')
category_name = method_name.to_s.gsub('_only', '').titleize
with_category(category_name)
else
super
end
end

This allows calling CmPrompt.estimation_only which internally calls CmPrompt.with_category('Estimation').

Category Association Management​

Categories are managed through the category_ids virtual attribute:

def category_ids=(ids)
@category_ids = Array(ids).reject(&:blank?)
end

def create_category_associations
return if @category_ids.blank?
categories.destroy_all
@category_ids.each do |category_id|
categories.create(constant_id: category_id)
end
end

Database Schema​

cm_prompts Table​

create_table :cm_prompts do |t|
t.string :name, null: false
t.citext :slug, null: false
t.references :created_by, foreign_key: { to_table: :users }
t.references :updated_by, foreign_key: { to_table: :users }
t.timestamps
end

add_index :cm_prompts, :name, unique: true
add_index :cm_prompts, :slug, unique: true

constant_maps Table​

create_table :constant_maps do |t|
t.references :constant, null: false
t.references :mappable, polymorphic: true, null: false
t.timestamps
end

add_index :constant_maps, %i[constant_id mappable_id mappable_type], unique: true

Dependencies​

This feature requires:

  • Lexxy (~> 0.8) - AI-powered rich text editor integration
  • ActionText - Rails rich text framework
  • pg_trgm PostgreSQL extension (for citext support)

Troubleshooting​

Templates Not Appearing in Editor​

  1. Check Policy Scope: Ensure the policy scope returns templates for the current user
  2. Verify Categories: Ensure templates have at least one category assigned
  3. Check Lexxy Configuration: Verify the Lexxy template platform setting is configured

Category Scope Methods Not Working​

  1. Verify Category Name: Ensure the category exists in cm_prompt_category constants
  2. Check Naming Convention: Use the _only suffix (e.g., estimation_only)
  3. Review Category Association: Ensure templates are properly associated with categories

Migration Errors​

If the constant_maps table already exists, the migration will skip creation:

return if table_exists?(:constant_maps)

Examples​

Creating a Template with Categories​

# In Rails console
template = CmPrompt.create!(
name: 'Project Estimation Template',
slug: 'project-estimation',
prompt: '<h1>Project Estimation</h1><p>Enter your estimation details...</p>',
category_ids: [1, 2] # IDs of cm_prompt_category constants
)

Filtering Templates by Category​

# Using scope method
estimation_templates = CmPrompt.estimation_only

# Using with_category
documentation_templates = CmPrompt.with_category('Documentation')

# Combining with other scopes
recent_estimation = CmPrompt.estimation_only.order(created_at: :desc).limit(10)

Accessing Template Categories​

template = CmPrompt.first
template.category_names # => "Estimation, Documentation"
template.category_ids # => [1, 2]

Future Enhancements​

Potential improvements for this feature:

  • Template versioning with history tracking
  • Template preview before insertion
  • Template variables/placeholders support
  • Template sharing between users
  • Template analytics (usage tracking)