Skip to content

Editor System Guide

SPEAR uses a unified Edra editor foundation built on Tiptap with four specialized variants for different use cases. This guide explains when to use each editor and how to configure them.

All editors share a common foundation:

  • Tiptap - Headless editor framework providing the core editing engine
  • ProseMirror - Document model and transaction system
  • Common Extensions - StarterKit, Image, Link, Table, CodeBlockLowlight, TextAlign, Placeholder, Underline, Highlight
  • Shared Styling - Editor CSS from frontend/src/lib/components/edra/editor.css
🎨 Editor Decision Matrix Diagram Illustration
flowchart TD
    Start[Need to edit content?] --> Collab{Need real-time<br/>collaboration?}
    Collab -->|Yes| Base[Use BaseEdraEditor]
    Collab -->|No| Type{What type of content?}

    Type -->|Vulnerability findings| Findings[Use FindingsRichTextEditor]
    Type -->|Validation notes,<br/>service descriptions| Validation[Use ValidationNotesEditor]
    Type -->|SOW templates with<br/>conditional blocks| SOW[Use SOWRichTextEditor]
    Type -->|General report sections| Base

    Base --> Features1[Y.js collaboration<br/>Live cursors<br/>Extensible]
    Findings --> Features2[Slash commands<br/>Tables<br/>Code blocks]
    Validation --> Features3[Shortcode picker<br/>Plain-text paste<br/>EditorApi]
    SOW --> Features4[Conditional blocks<br/>Scoping questions<br/>Shortcodes]
EditorUse CaseCollaborationKey Features
BaseEdraEditorReport sections, general contentYesY.js, live cursors, extensible
FindingsRichTextEditorVulnerability findingsNoSlash commands, tables, code blocks
ValidationNotesEditorValidation notes, descriptionsNoShortcode picker, EditorApi
SOWRichTextEditorSOW templates with conditionalsNoConditional blocks, scoping questions
🖥️ BaseEdraEditor with Collaboration Features Screenshot
4:20
BaseEdraEditor Collaboration Features Real-time collaboration in action: live cursors, connection status, and multi-user editing 🎥 Demo Video

The primary editor for report sections with optional real-time collaboration support.

  • Report sections requiring collaborative editing
  • General rich text content with team collaboration
  • Extensible editing with custom extensions
  • Y.js-powered real-time collaboration
  • Live cursors showing collaborator positions
  • Connection status indicator (Connected/Connecting/Disconnected)
  • Collaborator avatar display in toolbar
  • Extensible via additionalExtensions prop
PropTypeDefaultDescription
collaborationbooleanfalseEnable real-time collaboration
documentIdstring-Unique document ID for collaboration sync
contentstring''Initial HTML content
onUpdate(html: string) => void-Callback when content changes
additionalExtensionsExtension[][]Additional Tiptap extensions
placeholderstring-Placeholder text when empty
minHeightstring'200px'Minimum editor height
<script lang="ts">
import BaseEdraEditor from '$lib/components/edra/BaseEdraEditor.svelte';
let content = $state('<p>Initial content</p>');
const documentId = `section-${sectionId}`;
</script>
<BaseEdraEditor
collaboration={true}
{documentId}
{content}
onUpdate={(html) => content = html}
placeholder="Start typing..."
/>

frontend/src/lib/components/edra/BaseEdraEditor.svelte

🖥️ FindingsRichTextEditor with Slash Commands Screenshot
3:45
Using Slash Commands for Findings Quick formatting with slash commands, inserting tables, and adding code blocks 📚 Video Tutorial

Specialized editor for vulnerability findings, observations, and remediation recommendations.

  • Vulnerability finding descriptions
  • Technical observations with code samples
  • Remediation recommendations with structured content
  • Slash commands (/) for quick formatting
  • Advanced table manipulation
  • Code blocks with syntax highlighting (via Lowlight)
  • Optimized for technical security content
PropTypeDefaultDescription
contentstring''Initial HTML content
placeholderstring-Placeholder text when empty
onUpdate(html: string) => void-Callback when content changes
minHeightstring'150px'Minimum editor height
<script lang="ts">
import FindingsRichTextEditor from '$lib/components/findings/FindingsRichTextEditor.svelte';
let description = $state('');
</script>
<FindingsRichTextEditor
content={description}
placeholder="Describe the vulnerability..."
onUpdate={(html) => description = html}
minHeight="200px"
/>

frontend/src/lib/components/findings/FindingsRichTextEditor.svelte

  • frontend/src/lib/components/section-editor/FindingsSectionEditor.svelte
  • Vulnerability detail editors
  • Finding description fields
🖥️ ValidationNotesEditor with Shortcode Picker Screenshot
2:30
Shortcode Picker Workflow Inserting dynamic shortcodes and using the EditorApi for programmatic control 🎥 Demo Video

Lightweight editor for validation notes, service descriptions, and project descriptions with shortcode support.

  • Manual validation notes
  • Service descriptions
  • Project descriptions
  • Any content requiring shortcode insertion
  • Shortcode picker integration
  • Plain-text paste handling
  • EditorApi for programmatic control
  • Compact toolbar for focused editing
PropTypeDefaultDescription
contentstring''Initial HTML content
onUpdate(html: string) => void-Callback when content changes
onEditorReady(api: EditorApi) => void-Callback with editor API reference
placeholderstring-Placeholder text
interface EditorApi {
insertContent(html: string): void; // Insert content at cursor
focus(): void; // Focus the editor
getHTML(): string; // Get current content as HTML
}
<script lang="ts">
import ValidationNotesEditor from '$lib/components/vulnerabilities/ValidationNotesEditor.svelte';
let notes = $state('');
let editorApi: EditorApi | null = null;
function insertShortcode(shortcode: string) {
editorApi?.insertContent(`{{${shortcode}}}`);
}
</script>
<ValidationNotesEditor
content={notes}
onUpdate={(html) => notes = html}
onEditorReady={(api) => editorApi = api}
placeholder="Enter validation notes..."
/>
<button onclick={() => insertShortcode('project.name')}>
Insert Project Name
</button>

frontend/src/lib/components/vulnerabilities/ValidationNotesEditor.svelte

  • frontend/src/lib/components/vulnerabilities/ManualValidationForm.svelte
  • Service description editors
  • Project detail forms
🖥️ SOWRichTextEditor with Conditional Blocks Screenshot
7:15
Building Conditional SOW Templates Creating conditional blocks, linking to scoping questions, and testing conditional logic 📚 Video Tutorial

Specialized editor for SOW (Statement of Work) templates with conditional content blocks.

  • SOW template sections with conditional logic
  • Content that varies based on scoping question responses
  • Rules of Engagement (ROE) template editing
  • Conditional block builder
  • Scoping question integration
  • Visual node views for conditional blocks
  • Shortcode support for dynamic content
PropTypeDefaultDescription
contentstring''Initial HTML content
scopingQuestionsScopingQuestion[][]Available scoping questions for conditions
onUpdate(html: string) => void-Callback when content changes
onEditorReady(api: EditorApi) => void-Callback with editor API reference

Conditional blocks wrap content that should only appear when specific scoping conditions are met:

<conditional-block
data-question-id="q123"
data-operator="equals"
data-value="true">
<p>This content only appears when the condition is met.</p>
</conditional-block>
<script lang="ts">
import SOWRichTextEditor from '$lib/components/services/SOWRichTextEditor.svelte';
let templateContent = $state('');
const scopingQuestions = [
{ id: 'q1', text: 'Include penetration testing?', type: 'boolean' },
{ id: 'q2', text: 'Target environment', type: 'select', options: ['production', 'staging'] }
];
</script>
<SOWRichTextEditor
content={templateContent}
{scopingQuestions}
onUpdate={(html) => templateContent = html}
/>

frontend/src/lib/components/services/SOWRichTextEditor.svelte

  • frontend/src/lib/components/sow/SowTemplateSectionEditor.svelte
  • SOW template management
  • ROE template editing

All editors include these core extensions:

ExtensionPurpose
StarterKitBasic formatting (bold, italic, headings, lists)
ImageImage insertion and display
LinkHyperlink support
TableTable creation and editing
CodeBlockLowlightSyntax-highlighted code blocks
TextAlignText alignment (left, center, right, justify)
PlaceholderPlaceholder text when empty
UnderlineUnderline text formatting
HighlightText highlighting
  • Default to BaseEdraEditor for general report sections
  • Use FindingsRichTextEditor only for vulnerability-specific content
  • Use ValidationNotesEditor when shortcode insertion is needed
  • Use SOWRichTextEditor only for SOW/ROE templates

All editors support dark mode. Test your content in both light and dark modes to ensure readability.

Use debouncing for onUpdate callbacks to avoid excessive saves:

import { debounce } from '$lib/utils/debounce';
const debouncedSave = debounce((html: string) => {
saveContent(html);
}, 500);

Don’t create custom editor implementations. If you need additional functionality, extend an existing editor using additionalExtensions.

  • Verify onUpdate callback is connected
  • Check that content is being passed correctly
  • Ensure you’re not overwriting content during save
  • Verify shortcode syntax: {{shortcode.path}}
  • Check that shortcode extension is included
  • Ensure shortcode values are resolved on the backend
  • Verify collaboration={true} is set
  • Check documentId is unique and consistent
  • Ensure WebSocket connection is established
  • Check browser console for connection errors
  • Use the EditorApi’s focus() method
  • Check for z-index conflicts with overlays
  • Verify editor container is visible
ResourceLocation
BaseEdraEditorfrontend/src/lib/components/edra/BaseEdraEditor.svelte
FindingsRichTextEditorfrontend/src/lib/components/findings/FindingsRichTextEditor.svelte
ValidationNotesEditorfrontend/src/lib/components/vulnerabilities/ValidationNotesEditor.svelte
SOWRichTextEditorfrontend/src/lib/components/services/SOWRichTextEditor.svelte
Editor CSSfrontend/src/lib/components/edra/editor.css
Architecture GuideEDITOR_ARCHITECTURE.md
Internal Guidefrontend/src/lib/components/edra/EDITOR_GUIDE.md