Skip to content

Instantly share code, notes, and snippets.

@kieranklaassen
Created May 28, 2025 03:38
Show Gist options
  • Save kieranklaassen/d02ae3df17460c8ae581b36502d95b5e to your computer and use it in GitHub Desktop.
Save kieranklaassen/d02ae3df17460c8ae581b36502d95b5e to your computer and use it in GitHub Desktop.

DraftKit JavaScript Library

🚀 What is DraftKit?

DraftKit transforms any HTML form into an AI-powered content generator with just a few data attributes. No complex setup, no API integration headaches - just add our script and watch your forms come alive with intelligent content generation.

✨ The Magic

Imagine your users filling out a product form. They enter a product name and category, then click "Generate Description" - and boom! A compelling, context-aware product description appears instantly. That's DraftKit.

<\!-- This is all it takes -->
<script src="https://app.draftkit.io/draftkit.js" data-api-key="YOUR_KEY"></script>

<form>
  <input name="product_name" data-draftkit-context>
  <select name="category" data-draftkit-context>
    <option>Electronics</option>
    <option>Fashion</option>
  </select>
  
  <textarea name="description" 
            data-draftkit-prompt="prompt_product_desc_v2"></textarea>
  <\!-- ✨ A "Generate Draft" button appears automatically\! -->
</form>

🎯 Why DraftKit?

For Your Users:

  • Beat writer's block with AI-powered suggestions
  • Generate professional content in seconds
  • Maintain consistency across all content
  • Save hours on repetitive writing tasks

For Developers:

  • Zero dependencies - pure vanilla JavaScript
  • Works with any framework (React, Vue, vanilla HTML)
  • Simple data attributes API
  • Automatic UI generation
  • Full event system for customization
  • Respects your existing form structure

🛠️ Key Features

  • Smart Context Collection - Automatically gathers relevant form data to generate contextual content
  • Flexible Integration - Use auto-generated buttons or create your own custom UI
  • Event-Driven - Hook into the generation lifecycle with standard DOM events
  • Privacy-First - Explicitly exclude sensitive fields from context collection
  • Framework Agnostic - Works everywhere: static sites, SPAs, server-rendered apps

Technical Specification

Description

Develop a standalone, embeddable JavaScript library that can be included in any website to add AI-powered content generation capabilities to HTML forms. The library will identify form fields with specific data attributes, add "Generate Draft" buttons, collect context from specified fields, send it to our backend API, process it with an LLM, and insert the generated content back into the form field.

Technical Requirements

  • Create a simple JavaScript library served via Rails route (no build tools/bundling)
  • Library will be available at /draftkit.js route in Rails application
  • Support modern browsers (Chrome, Firefox, Safari, Edge)
  • No external dependencies (vanilla JavaScript only)
  • Support both synchronous and asynchronous loading patterns
  • CORS support for cross-domain API requests
  • Lightweight implementation (<50KB uncompressed)

Functionality Requirements

  • Detect form fields with data-draftkit-prompt attribute
  • Identify context fields with data-draftkit-context attribute
  • Automatically create and insert "Generate Draft" buttons next to target fields
  • Support custom button implementation via data-draftkit-button attribute
  • Collect context from marked fields (using field's name attribute by default)
  • Handle button click events and loading states
  • Make API requests to DraftKit backend (mock endpoints for this PR)
  • Insert generated content into target fields
  • Support rich text editors (if present)
  • Add thumbs up/down rating UI after content generation
  • Handle and display errors gracefully
  • Fire custom events for developer hooks

HTML API Design

Data Attributes

  • data-draftkit-context: Mark fields to include in context (uses field's name attribute as key)
  • data-draftkit-context="custom_key": Optionally specify a custom key for context
  • data-draftkit-context='{"key": "value"}': Add static context via JSON
  • data-draftkit-prompt="prompt_abc123": Mark field for generation with prompt token
  • data-draftkit-button="field_name": Create custom button that targets a specific field
  • data-draftkit-exclude: Exclude sensitive fields from context collection

Global Configuration

<\!-- Simple configuration via data attributes -->
<script src="https://app.draftkit.io/draftkit.js" 
        data-api-key="YOUR_API_KEY"
        data-locale="en"
        data-auto-buttons="true"
        data-button-position="right">
</script>

Event System

DraftKit fires standard DOM events (similar to Turbo) that you can listen to:

// Listen to generation events
document.addEventListener('draftkit:before-generate', (event) => {
  console.log('About to generate for:', event.detail.fieldName);
  console.log('Context:', event.detail.context);
  
  // Prevent generation if needed
  if (\!validContext(event.detail.context)) {
    event.preventDefault();
  }
});

document.addEventListener('draftkit:generate', (event) => {
  console.log('Generation completed:', event.detail.content);
});

document.addEventListener('draftkit:error', (event) => {
  console.error('Generation failed:', event.detail.error);
});

document.addEventListener('draftkit:rate', (event) => {
  console.log('User rated:', event.detail.rating);
});

Automatic Button Placement

When a field has data-draftkit-prompt, a "Generate Draft" button will be automatically inserted:

  • For <textarea>: Button appears in top-right corner of the field
  • For <input>: Button appears to the right of the field
  • For rich text editors: Button integrates with the editor's toolbar

API Contract (Mock Endpoints)

Note: These endpoints will be mocked for this PR. Actual implementation will be done separately.

  • POST /api/v1/drafts

    • Request:
      {
        "context": {
          "field_name": "field_value",
          ...
        },
        "prompt_id": "prompt_abc123", 
        "target_field": "field_name"
      }
    • Response:
      {
        "id": "generation_id",
        "content": "generated_text",
        "status": "success"
      }
  • POST /api/v1/ratings

    • Request:
      {
        "generation_id": "generation_id",
        "rating": 1|0, // 1 = thumbs up, 0 = thumbs down
        "feedback": "optional feedback text"
      }

Example Usage

Basic Usage with Automatic Buttons

<\!-- Include the library -->
<script src="https://app.draftkit.io/draftkit.js" 
        data-api-key="YOUR_API_KEY"></script>

<\!-- Product description form -->
<form>
  <\!-- Context field (no button) -->
  <label for="product_name">Product Name:</label>
  <input type="text" 
         id="product_name" 
         name="product_name" 
         data-draftkit-context>
  
  <\!-- Context field with custom key -->
  <label for="category">Category:</label>
  <select id="category" 
          name="category" 
          data-draftkit-context="product_category">
    <option>Electronics</option>
    <option>Clothing</option>
  </select>
  
  <\!-- Target field with auto-generated button -->
  <label for="description">Description:</label>
  <textarea id="description" 
            name="description"
            data-draftkit-prompt="prompt_product_desc_v2"
            rows="6"></textarea>
  <\!-- ✨ Generate Draft button appears here automatically -->
  
  <\!-- Sensitive field excluded from context -->
  <input type="password" 
         name="api_secret" 
         data-draftkit-exclude>
  
  <button type="submit">Submit</button>
</form>

Static Context via JSON

<\!-- Add static context data -->
<textarea name="email_body"
          data-draftkit-prompt="prompt_email_v1"
          data-draftkit-context='{"tone": "professional", "max_length": 200}'
          rows="10"></textarea>

Custom Button Implementation

<\!-- Custom styled button example -->
<form>
  <input type="text" 
         name="company_name" 
         data-draftkit-context 
         placeholder="Company Name">
  
  <input type="text" 
         name="industry" 
         data-draftkit-context 
         placeholder="Industry">
  
  <\!-- Target field WITHOUT auto button -->
  <textarea id="tagline" 
            name="tagline"
            data-draftkit-prompt="prompt_tagline_v1"
            data-draftkit-no-button
            placeholder="Company tagline"></textarea>
  
  <\!-- Custom button with your own design -->
  <button type="button" 
          data-draftkit-button="tagline"
          class="my-custom-btn">
    <svg>...</svg> Generate Tagline with AI
  </button>
</form>

Using Events for Custom Behavior

<form id="blog-form">
  <input name="topic" data-draftkit-context>
  <textarea name="content" 
            data-draftkit-prompt="prompt_blog_v1"></textarea>
</form>

<script>
// Track generations
document.addEventListener('draftkit:before-generate', (event) => {
  if (event.target.matches('#blog-form *')) {
    analytics.track('AI Generation Started', {
      field: event.detail.fieldName,
      prompt: event.detail.promptId
    });
  }
});

// Show custom success message
document.addEventListener('draftkit:generate', (event) => {
  showToast('Content generated successfully\!');
});

// Handle errors gracefully
document.addEventListener('draftkit:error', (event) => {
  if (event.detail.error.code === 'rate_limit') {
    showError('Too many requests. Please try again later.');
  }
});
</script>

React Application

import React, { useEffect } from 'react';

function BlogForm() {
  useEffect(() => {
    // Load DraftKit script
    const script = document.createElement('script');
    script.src = 'https://app.draftkit.io/draftkit.js';
    script.setAttribute('data-api-key', 'YOUR_API_KEY');
    script.async = true;
    document.body.appendChild(script);
    
    // Listen to events
    const handleGenerate = (event) => {
      console.log('Generated:', event.detail);
    };
    
    document.addEventListener('draftkit:generate', handleGenerate);
    
    return () => {
      document.body.removeChild(script);
      document.removeEventListener('draftkit:generate', handleGenerate);
    };
  }, []);
  
  return (
    <form>
      {/* Context fields */}
      <input 
        name="topic" 
        data-draftkit-context
        placeholder="Blog Topic" 
      />
      
      {/* Add static context */}
      <input 
        name="audience" 
        data-draftkit-context='{"importance": "high"}'
        placeholder="Target Audience" 
      />
      
      {/* Auto button (DraftKit handles it) */}
      <textarea 
        name="content"
        data-draftkit-prompt="prompt_blog_v1"
        placeholder="Blog Content"
        rows="10"
      />
    </form>
  );
}

Rails Controller (for serving the JS)

# app/controllers/draftkit_controller.rb
class DraftkitController < ApplicationController
  def javascript
    respond_to do |format|
      format.js { render layout: false, content_type: 'text/javascript' }
    end
  end
end

# config/routes.rb
get '/draftkit.js', to: 'draftkit#javascript', as: :draftkit_js

Button Styling and Customization

Default Button Styles

The auto-generated buttons will have:

  • Class: draftkit-generate-btn
  • Minimal, unobtrusive design
  • Loading state animation
  • Hover and active states
  • Responsive sizing based on field size

CSS Customization

/* Override default button styles */
.draftkit-generate-btn {
  background: #your-color;
  /* your custom styles */
}

/* Loading state */
.draftkit-generate-btn.loading {
  /* custom loading styles */
}

/* Rating UI */
.draftkit-rating {
  /* custom rating styles */
}

Testing Strategy

JavaScript Unit Tests

We'll use Rails' built-in JavaScript testing capabilities with a simple test runner. Tests will be in test/javascript/draftkit_test.js:

// Test core functionality
- DraftKit initialization with valid API key
- DraftKit initialization without API key (should fail gracefully)
- Form field detection (data-draftkit-prompt)
- Context collection from marked fields
- Context exclusion (data-draftkit-exclude)
- Button creation and placement
- Custom button binding
- Event firing (before-generate, generate, error, rate)
- API request formatting
- Response handling and field updates
- Error handling and display
- Static context parsing from JSON
- Dynamic form handling (MutationObserver)

Rails System Tests

System tests will verify the full integration in test/system/draftkit_system_test.rb:

class DraftkitSystemTest < ApplicationSystemTestCase
  test "loads DraftKit library from Rails route" do
    visit "/draftkit.js"
    assert_match "window.DraftKit", page.body
  end
  
  test "automatic button generation" do
    visit draftkit_demo_path
    assert_selector ".draftkit-generate-btn"
  end
  
  test "generates content and updates field" do
    visit draftkit_demo_path
    fill_in "product_name", with: "Test Product"
    click_button "Generate Draft"
    
    assert_selector ".draftkit-loading"
    assert_field "description", with: /generated content/
  end
  
  test "fires custom events" do
    # Test event listeners work correctly
  end
  
  test "handles errors gracefully" do
    # Test error scenarios
  end
end

Controller Tests

Test the Rails endpoint in test/controllers/draftkit_controller_test.rb:

test "serves JavaScript with correct content type" do
  get draftkit_js_path
  assert_response :success
  assert_equal "text/javascript", response.content_type
end

Manual Testing Checklist

  • Library loads without errors
  • Buttons appear for all prompt fields
  • Context collection works for all field types
  • Custom buttons trigger generation
  • Events fire in correct order
  • Errors display user-friendly messages
  • Works in Chrome, Firefox, Safari, Edge
  • Works with Turbo navigation
  • Rating UI appears and submits

Demo Page

Create a comprehensive demo page at /draftkit/demo showing all features:

Demo Page Structure

<\!-- app/views/static/draftkit_demo.html.erb -->
<div class="container">
  <h1>DraftKit Demo</h1>
  
  <\!-- Example 1: Basic Product Form -->
  <section>
    <h2>1. Basic Product Description</h2>
    <form>
      <input name="product_name" data-draftkit-context placeholder="Product Name">
      <select name="category" data-draftkit-context>
        <option>Electronics</option>
        <option>Fashion</option>
      </select>
      <textarea name="description" data-draftkit-prompt="prompt_product_desc_v2"></textarea>
    </form>
  </section>
  
  <\!-- Example 2: Custom Button -->
  <section>
    <h2>2. Custom Button Design</h2>
    <form>
      <input name="company" data-draftkit-context>
      <textarea name="tagline" data-draftkit-prompt="prompt_tagline_v1" data-draftkit-no-button></textarea>
      <button data-draftkit-button="tagline" class="btn-primary">✨ Generate</button>
    </form>
  </section>
  
  <\!-- Example 3: Multiple Fields -->
  <section>
    <h2>3. Blog Post Generator</h2>
    <form>
      <input name="topic" data-draftkit-context>
      <input name="title" data-draftkit-prompt="prompt_title_v1">
      <textarea name="intro" data-draftkit-prompt="prompt_intro_v1"></textarea>
      <textarea name="content" data-draftkit-prompt="prompt_content_v1"></textarea>
    </form>
  </section>
  
  <\!-- Example 4: Static Context -->
  <section>
    <h2>4. Email with Tone</h2>
    <form>
      <input name="recipient" data-draftkit-context>
      <textarea name="email" 
                data-draftkit-prompt="prompt_email_v1"
                data-draftkit-context='{"tone": "professional", "length": "brief"}'>
      </textarea>
    </form>
  </section>
  
  <\!-- Example 5: Field Exclusion -->
  <section>
    <h2>5. Secure Form</h2>
    <form>
      <input name="username" data-draftkit-context>
      <input type="password" name="api_key" data-draftkit-exclude>
      <textarea name="bio" data-draftkit-prompt="prompt_bio_v1"></textarea>
    </form>
  </section>
  
  <\!-- Event Monitor -->
  <section>
    <h2>Event Monitor</h2>
    <div id="event-log"></div>
  </section>
  
  <script>
    // Log all DraftKit events
    ['before-generate', 'generate', 'error', 'rate'].forEach(eventName => {
      document.addEventListener(`draftkit:${eventName}`, (event) => {
        const log = document.getElementById('event-log');
        log.innerHTML += `<div>${new Date().toISOString()} - ${eventName}: ${JSON.stringify(event.detail)}</div>`;
      });
    });
  </script>
</div>

Routes Addition

# config/routes.rb
get '/draftkit/demo', to: 'static#draftkit_demo' if Rails.env.development?

Acceptance Criteria

  • Library correctly identifies fields with prompt attributes and adds buttons
  • Context collection works automatically using field names
  • Static context can be added via JSON in data attributes
  • Fields can be excluded from context with data-draftkit-exclude
  • Custom buttons can trigger generation for specific fields
  • Standard DOM events are fired for all generation lifecycle stages
  • Rails controller serves the JavaScript
  • Generated content is correctly inserted into target fields
  • Rating UI appears after generation
  • Error states are handled gracefully
  • Works with vanilla JavaScript and React
  • All tests pass (unit, system, controller)
  • Demo page showcases all features

Definition of Done

  • Core library is implemented with refined HTML API
  • JavaScript file is served through Rails controller/view
  • Event system is implemented using standard DOM events
  • Mock API endpoints return sample data for testing
  • Automatic button placement works correctly
  • Custom button support is implemented
  • Static context via JSON is supported
  • Exclude attribute prevents fields from context collection
  • JavaScript unit tests are written and passing
  • Rails system tests verify full integration
  • Controller tests verify endpoint behavior
  • Demo page is created showing all examples
  • Documentation is complete with multiple examples
  • Cross-browser testing is complete
  • Security review is completed
  • Code follows DraftKit conventions and style guide

Out of Scope for This PR

  • Actual API endpoint implementation (will be done separately)
  • CDN deployment and configuration
  • Production build pipeline
  • Cloudflare caching configuration
  • Real LLM integration
  • NPM package for single-page apps (future enhancement)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment