Files
Print_server/template_editor_plan.md
2026-01-21 11:59:13 +01:00

28 KiB

Receipt Template Language (RTL) - Project Specification


Phase 1: Core Template Engine

1. Overview

A file-based template system for configuring receipt layouts per receipt type and printer model. Replaces hardcoded C# converters with editable .template files.

Deliverables

  • New project: Inspectron.Epson.Templates
  • Template language lexer, parser, and interpreter
  • File-based configuration loader
  • Integration with existing IReceiptConverterFactory
  • Fallback template for undefined combinations
  • Unit tests

2. Project Structure

Inspectron.Epson.Templates/
├── Inspectron.Epson.Templates.csproj
├── Language/
│   ├── Lexer.cs
│   ├── Parser.cs
│   ├── Tokens.cs
│   └── Nodes/
│       ├── TemplateNode.cs
│       ├── TextNode.cs
│       ├── StyledTextNode.cs
│       ├── BindingNode.cs
│       ├── SeparatorNode.cs
│       ├── RowNode.cs
│       ├── ColumnNode.cs
│       ├── IfNode.cs
│       ├── ForeachNode.cs
│       └── EmptyLineNode.cs
├── Interpreter/
│   ├── TemplateInterpreter.cs
│   ├── DataContext.cs
│   └── InterpreterException.cs
├── Configuration/
│   ├── TemplateConfiguration.cs
│   ├── TemplateAssignment.cs
│   ├── PrinterProfile.cs
│   ├── PrinterProfileRegistry.cs
│   └── TemplateResolver.cs
├── Storage/
│   ├── ITemplateStorage.cs
│   └── FileTemplateStorage.cs
├── TemplateEngine.cs
├── TemplateReceiptConverter.cs
└── TemplateReceiptConverterFactory.cs

3. File System Layout

templates/
├── kitchen-default.template        # Default kitchen receipt
├── kitchen-u220.template           # Kitchen receipt for TM-U220II
├── bar-default.template            # Bar receipt
├── final-receipt.template          # Customer receipt
├── orders-overview.template        # Orders overview
├── fallback.template               # Used when no match found
├── assignments.json                # Maps (receiptType, printer) → template
└── samples/                        # Sample data for preview/testing
    ├── kitchen-default.json
    ├── bar-default.json
    ├── final-receipt.json
    └── orders-overview.json

4. Template Language Specification

4.1 Line Types

Line Type Syntax Example
Styled Text #styles# content #bold,center# {title}
Plain Text content Hello World
Separator ---, ===, ***, ~~~ ---
Row Start @row ratios [styles] @row 60:40 bold
Row Content content | content {name} | >{price}
Row End @endrow @endrow
Condition @if condition @if comment
Loop @foreach item in collection @foreach dish in dishes
Block End @end @end
Empty (blank line)

4.2 Styles

Style Description
bold Bold text
big Large font (2x magnification)
red Red color (two-color printers only)
center Center-aligned
right Right-aligned
spacing:N Set line spacing (0-255)
spacing:default Reset to default spacing

Syntax: #style1,style2,...# content

Examples:

#bold,center,big# {title}
#red# WARNING
#spacing:0,bold# Compact line

4.3 Data Binding

Syntax: {path} or {path:format}

Path navigation:

{title}                 // Root property
{order.date}            // Nested property
{items.count}           // Collection count

Format specifiers:

{price:F2}              // Decimal, 2 places → "12.50"
{date:dd-MMM-yy}        // Date → "25-Jan-26"
{date:HH:mm}            // Time → "14:30"
{number:D4}             // Padded integer → "0042"

Missing data behavior: Returns empty string (silent).

4.4 Separators

---     // Dashed line (fills line width)
===     // Equals line
***     // Asterisk line
~~~     // Tilde line

Minimum 3 characters. Fills PrinterProfile.LineWidth.

4.5 Rows (Columns)

Multi-line syntax:

@row 60:40
{leftContent} | >{rightContent}
@endrow

Single-line shorthand:

@row 60:40 | {left} | >{right} |

Ratios:

@row 50:50           // Two equal columns
@row 60:30:10        // Three columns (percentages)
@row *:20            // First fills remainder, second is 20%
@row 30:*:20         // Middle fills remainder

Column alignment (prefix in content):

{text}      // Left-aligned (default)
>{text}     // Right-aligned
^{text}     // Center-aligned

Row styles (optional, applied to all columns):

@row 50:50 bold
@row 70:30 big,red

4.6 Conditionals

Syntax:

@if condition
  content
@end

Conditions:

@if propertyName              // Truthy (not null, not empty, not false, not 0)
@if !propertyName             // Falsy
@if items.count > 0           // Comparison
@if status == "active"        // String equality
@if total >= 100              // Numeric comparison

Operators: ==, !=, >, <, >=, <=

4.7 Loops

Syntax:

@foreach item in collection
  content using {item.property}
@end

Loop variables:

{item}              // Current item (if primitive)
{item.property}     // Item property
{item._index}       // 0-based index
{item._number}      // 1-based number
{item._first}       // true if first iteration
{item._last}        // true if last iteration

Nesting:

@foreach gang in gangs
  #red# {gang.name}
  @foreach dish in gang.dishes
    {dish.number}x {dish.name}
  @end
@end

5. Configuration

5.1 assignments.json

{
  "assignments": [
    {
      "receiptType": "Receipt",
      "printerProfile": null,
      "templateFile": "final-receipt.template"
    },
    {
      "receiptType": "WorkareaTicket",
      "printerProfile": null,
      "templateFile": "kitchen-default.template"
    },
    {
      "receiptType": "WorkareaTicket",
      "printerProfile": "tm-u220ii",
      "templateFile": "kitchen-u220.template"
    },
    {
      "receiptType": "NextCourse",
      "printerProfile": null,
      "templateFile": "kitchen-default.template"
    },
    {
      "receiptType": "OrdersOverview",
      "printerProfile": null,
      "templateFile": "orders-overview.template"
    }
  ],
  "fallbackTemplate": "fallback.template"
}

5.2 Printer Profiles (built-in)

Profile ID Printer LineWidth BigLineWidth SupportsRed
tm-t30iii TM-T30III 48 24 false
tm-u220ii TM-U220II 33 20 true

Mapped from printer ID byte:

  • 0x01tm-t30iii
  • 0x13tm-u220ii

5.3 Resolution Priority

  1. Exact match: receiptType + printerProfile
  2. Type only: receiptType + printerProfile: null
  3. Fallback: fallbackTemplate

6. Public API

6.1 TemplateEngine

namespace Inspectron.Epson.Templates;

public class TemplateEngine
{
    public TemplateEngine(string templatesDirectory);

    /// <summary>
    /// Renders a template with the given data.
    /// </summary>
    public List<PrintCommand> Render(
        string templateContent,
        string jsonData,
        PrinterProfile profile);

    /// <summary>
    /// Parses template and returns errors/warnings (for validation).
    /// </summary>
    public TemplateValidationResult Validate(string templateContent);
}

public class TemplateValidationResult
{
    public bool IsValid { get; }
    public List<TemplateError> Errors { get; }
    public List<TemplateWarning> Warnings { get; }
}

public class TemplateError
{
    public int Line { get; }
    public int Column { get; }
    public string Message { get; }
    public string Code { get; }  // e.g., "RTL001"
}

public class TemplateWarning
{
    public int Line { get; }
    public int Column { get; }
    public string Message { get; }
    public string Code { get; }  // e.g., "RTL100"
}

6.2 TemplateReceiptConverterFactory

namespace Inspectron.Epson.Templates;

public class TemplateReceiptConverterFactory : IReceiptConverterFactory
{
    public TemplateReceiptConverterFactory(string templatesDirectory);

    /// <summary>
    /// Creates a converter for the given receipt type and printer.
    /// Falls back to fallback template if no match found.
    /// </summary>
    public IReceiptConverter Create(int receiptType, byte printerId);

    /// <summary>
    /// Reloads templates and assignments from disk.
    /// </summary>
    public void Reload();
}

6.3 Integration Example

// In EpsonPrintService startup
var converterFactory = new TemplateReceiptConverterFactory("./templates");

// In DI registration (Ninject)
kernel.Bind<IReceiptConverterFactory>()
    .ToConstant(converterFactory)
    .InSingletonScope();

// Usage remains unchanged
var converter = converterFactory.Create(receiptType, printerId);
var commands = converter.Convert(jsonContent);

7. Error Codes

Errors (prevent rendering)

Code Message
RTL001 Unexpected token '{token}' at line {line}
RTL002 Unclosed block '@{block}' started at line {line}
RTL003 '@end' without matching '@if' or '@foreach'
RTL004 Invalid style '{style}'
RTL005 Invalid ratio format '{ratio}'
RTL006 Column count mismatch: expected {expected}, got {actual}
RTL007 Invalid condition syntax
RTL008 Invalid binding path '{path}'
RTL009 Unclosed binding '{' at line {line}
RTL010 Unclosed style marker '#' at line {line}

Warnings (render anyway)

Code Message
RTL100 Unused loop variable '{variable}'
RTL101 Empty @if block
RTL102 Empty @foreach block
RTL103 Style '{style}' has no effect on this printer

8. Default Templates

Provide default templates matching current hardcoded behavior:

  • kitchen-default.template - matches KitchenReceiptConverter
  • kitchen-u220.template - compact version for narrow printers
  • bar-default.template - matches BarReceiptConverter
  • final-receipt.template - matches FinalReceiptConverter
  • orders-overview.template - matches OrderItemsReceiptConverter
  • fallback.template - minimal template that dumps JSON fields

9. Testing

Unit Tests

Inspectron.Epson.Templates.Tests/
├── Lexer/
│   ├── LexerBasicTests.cs
│   ├── LexerStyleTests.cs
│   ├── LexerBindingTests.cs
│   └── LexerControlFlowTests.cs
├── Parser/
│   ├── ParserBasicTests.cs
│   ├── ParserRowTests.cs
│   ├── ParserNestingTests.cs
│   └── ParserErrorTests.cs
├── Interpreter/
│   ├── InterpreterTextTests.cs
│   ├── InterpreterBindingTests.cs
│   ├── InterpreterConditionTests.cs
│   ├── InterpreterLoopTests.cs
│   └── InterpreterRowTests.cs
├── Configuration/
│   ├── TemplateResolverTests.cs
│   └── PrinterProfileTests.cs
└── Integration/
    ├── KitchenReceiptTests.cs
    ├── FinalReceiptTests.cs
    └── FallbackTests.cs

Test Coverage Goals

  • Lexer: All token types, edge cases, error handling
  • Parser: All node types, nesting, malformed input
  • Interpreter: All features, format specifiers, missing data
  • Integration: Match output of existing converters

10. Migration Plan

  1. Create Inspectron.Epson.Templates project
  2. Implement and test template engine
  3. Create default templates matching current output
  4. Add TemplateReceiptConverterFactory to DI as alternative
  5. Integration test: compare output of both factories
  6. Switch DI registration to use template factory
  7. Remove legacy converter code (optional, can keep as reference)

Phase 2: Template Editor

1. Overview

Desktop application for creating and editing receipt templates with live preview, syntax highlighting, autocomplete, and validation.

Deliverables

  • Avalonia desktop application: Inspectron.Epson.TemplateEditor
  • Syntax highlighting for RTL
  • Autocomplete (styles, control flow, data bindings)
  • Real-time validation with error/warning display
  • Live preview with sample data
  • Template assignment management UI

2. Project Structure

Inspectron.Epson.TemplateEditor/
├── Inspectron.Epson.TemplateEditor.csproj
├── App.axaml
├── App.axaml.cs
├── Program.cs
├── ViewModels/
│   ├── MainViewModel.cs
│   ├── EditorViewModel.cs
│   ├── PreviewViewModel.cs
│   ├── AssignmentsViewModel.cs
│   └── TemplateListViewModel.cs
├── Views/
│   ├── MainWindow.axaml
│   ├── EditorView.axaml
│   ├── PreviewView.axaml
│   ├── AssignmentsView.axaml
│   └── TemplateListView.axaml
├── Controls/
│   ├── ReceiptPreviewControl.cs
│   └── ValidationMargin.cs
├── Editor/
│   ├── RtlSyntaxHighlighting.cs
│   ├── RtlCompletionProvider.cs
│   ├── RtlFoldingStrategy.cs
│   └── JsonSchemaExtractor.cs
├── Services/
│   ├── TemplateService.cs
│   ├── SampleDataService.cs
│   └── ValidationService.cs
└── Resources/
    └── RTL.xshd

3. UI Layout

┌─────────────────────────────────────────────────────────────────────────────┐
│  File   Edit   View   Tools   Help                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│ Templates              │                                                     │
│ ┌────────────────────┐ │ ┌─────────────────────────┬───────────────────────┐ │
│ │ 📄 kitchen-default │ │ │  Template Editor        │  Preview              │ │
│ │ 📄 kitchen-u220    │ │ │                         │                       │ │
│ │ 📄 bar-default     │ │ │  1│#bold,center,big#   │ ┌───────────────────┐ │ │
│ │ 📄 final-receipt   │ │ │  2│{title}              │ │   Warme Küche     │ │ │
│ │ 📄 orders-overview │ │ │  3│---                  │ │───────────────────│ │ │
│ │ 📄 fallback        │ │ │  4│                     │ │                   │ │ │
│ │                    │ │ │  5│@row 60:40           │ │ 25-Jan  Nr.:42    │ │ │
│ │ ──────────────────│ │ │  6│{date} | >{number}   │ │                   │ │ │
│ │ 📋 Assignments     │ │ │  7│@endrow              │ │  Max Mustermann   │ │ │
│ │                    │ │ │  8│#center# {waiter}    │ │                   │ │ │
│ └────────────────────┘ │ │  9│                     │ │  Tisch: 12        │ │ │
│                        │ │ 10│@foreach dish in...  │ │───────────────────│ │ │
│ Sample Data            │ │ 11│  {dish.number}x...  │ │ 2x Tomatensuppe   │ │ │
│ ┌────────────────────┐ │ │ 12│@end                 │ │ 1x Schnitzel      │ │ │
│ │kitchen-default.json│ │ │                         │ │   - Pommes        │ │ │
│ │                    │ │ │                         │ │   + Reis          │ │ │
│ │ [Edit Sample]      │ │ │                         │ └───────────────────┘ │ │
│ └────────────────────┘ │ │                         │                       │ │
│                        │ │                         │  Printer: [TM-T30III▼]│ │
├────────────────────────┴─┴─────────────────────────┴───────────────────────┴─┤
│ Problems                                                                      │
│ ┌──────────────────────────────────────────────────────────────────────────┐ │
│ │ ⚠ Line 11: Unused loop variable 'dish' in nested scope                   │ │
│ │ ❌ Line 15: Unclosed '@foreach' block started at line 10                  │ │
│ └──────────────────────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────────────────────┤
│ ✓ Saved │ kitchen-default.template │ Ln 8, Col 15 │ 2 warnings, 1 error     │
└──────────────────────────────────────────────────────────────────────────────┘

4. Features

4.1 Template List Panel

  • List all .template files from templates/ directory
  • Click to open in editor
  • Right-click context menu: Rename, Delete, Duplicate
  • "New Template" button
  • "Assignments" entry opens assignment editor

4.2 Editor Panel (AvaloniaEdit)

Syntax Highlighting:

Element Color
Style markers #...# Blue
Bindings {...} Teal
Control flow @if, @foreach, @end, @row, @endrow Purple
Separators --- Gray
Column delimiters | Yellow
Alignment markers >, ^ Orange

Code Folding:

  • Collapse @if...@end blocks
  • Collapse @foreach...@end blocks
  • Collapse @row...@endrow blocks

Autocomplete Triggers:

Trigger Shows
# Style keywords
, (inside #...#) Style keywords
@ (at line start) Control flow keywords
{ Root data properties + loop variables
. (inside {...}) Child properties
: (inside {...}) Format specifiers
in (after @foreach x) Collection properties

Validation:

  • Real-time parsing (debounced 300ms)
  • Error squiggles (red underline)
  • Warning squiggles (yellow underline)
  • Gutter icons ( error, ⚠ warning)
  • Click error → jump to line

4.3 Preview Panel

Receipt Preview Control:

  • Renders List<PrintCommand> visually
  • Mimics thermal receipt appearance
  • Monospace font
  • Configurable width based on selected printer
  • Updates live as template changes (debounced)

Printer Selector:

  • Dropdown: TM-T30III, TM-U220II
  • Changes LineWidth and BigLineWidth
  • Shows/hides red color support

4.4 Sample Data Panel

  • Shows current sample JSON file
  • "Edit Sample" button → opens JSON in editor (modal or panel)
  • JSON syntax highlighting
  • Validates JSON on save
  • Shows extracted schema (tree view of available properties)

4.5 Problems Panel

  • Lists all errors and warnings
  • Grouped by severity (errors first)
  • Each item shows: icon, line number, message
  • Double-click → jump to location in editor
  • Filterable: Show errors only, Show all

4.6 Assignments Editor

┌─────────────────────────────────────────────────────────────────────┐
│ Template Assignments                                                 │
├─────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┬──────────────────┬─────────────────────┬──────────┐ │
│ │ Receipt Type│ Printer          │ Template            │ Actions  │ │
│ ├─────────────┼──────────────────┼─────────────────────┼──────────┤ │
│ │ Receipt     │ (Any)            │ final-receipt      ▼│ [Delete] │ │
│ │ WorkareaTicket│ (Any)          │ kitchen-default    ▼│ [Delete] │ │
│ │ WorkareaTicket│ TM-U220II      │ kitchen-u220       ▼│ [Delete] │ │
│ │ NextCourse  │ (Any)            │ kitchen-default    ▼│ [Delete] │ │
│ │ OrdersOverview│ (Any)          │ orders-overview    ▼│ [Delete] │ │
│ └─────────────┴──────────────────┴─────────────────────┴──────────┘ │
│                                                                      │
│ [+ Add Assignment]                                                   │
│                                                                      │
│ Fallback Template: [fallback.template              ▼]                │
│                                                                      │
│ ⚠ Warning: No specific template for Receipt + TM-U220II             │
│                                                                      │
│                                              [Cancel]  [Save]        │
└─────────────────────────────────────────────────────────────────────┘

5. Data Flow

┌──────────────┐
│ Template     │──────┐
│ (.template)  │      │
└──────────────┘      │
                      ▼
┌──────────────┐   ┌──────────────┐   ┌──────────────┐
│ Sample JSON  │──▶│ Template     │──▶│ PrintCommands│
│ (.json)      │   │ Engine       │   │              │
└──────────────┘   └──────────────┘   └──────┬───────┘
                      │                       │
                      │                       ▼
                      │               ┌──────────────┐
                      │               │ Preview      │
                      │               │ Renderer     │
                      ▼               └──────────────┘
               ┌──────────────┐
               │ Validation   │
               │ Results      │
               └──────────────┘

6. Autocomplete Implementation

6.1 Context Detection

public enum CompletionContext
{
    None,
    Style,           // Inside #...#
    ControlFlow,     // After @ at line start
    Binding,         // Inside {...}
    BindingProperty, // After . inside {...}
    BindingFormat,   // After : inside {...}
    ForeachCollection, // After "in" in @foreach
    RowContent       // Inside @row...@endrow
}

6.2 Schema Extraction

public class JsonSchemaExtractor
{
    public SchemaNode Extract(string json);
}

public abstract record SchemaNode(string Name);
public record ObjectNode(string Name, List<SchemaNode> Properties) : SchemaNode(Name);
public record ArrayNode(string Name, SchemaNode ItemType) : SchemaNode(Name);
public record ValueNode(string Name, ValueType Type) : SchemaNode(Name);

public enum ValueType { String, Number, Boolean, DateTime, Unknown }

6.3 Scope Tracking

Track active @foreach loops to resolve loop variable bindings:

public record LoopScope(string VariableName, string CollectionPath, SchemaNode ItemSchema);

public class ScopeTracker
{
    public List<LoopScope> GetActiveScopes(string templateText, int cursorPosition);
}

7. File Operations

Action Behavior
New Template Prompt for name, create empty file, open in editor
Open Template Load from disk, show in editor
Save Write to disk, trigger reload hint for print service
Save As Prompt for new name, save copy
Delete Confirm dialog, remove file
Rename Prompt for new name, rename file, update assignments
Duplicate Prompt for new name, copy content

8. Keyboard Shortcuts

Shortcut Action
Ctrl+S Save current template
Ctrl+N New template
Ctrl+O Open templates folder
Ctrl+Shift+P Toggle preview panel
Ctrl+Space Trigger autocomplete
F2 Rename selected template
Delete Delete selected template
Ctrl+G Go to line
Ctrl+F Find in template
Ctrl+H Find and replace
F8 Go to next problem
Shift+F8 Go to previous problem

9. Configuration

Editor settings stored in editor-settings.json:

{
  "templatesDirectory": "./templates",
  "theme": "dark",
  "fontSize": 14,
  "showLineNumbers": true,
  "wordWrap": false,
  "autoSave": true,
  "autoSaveDelayMs": 2000,
  "previewDebounceMs": 300,
  "lastOpenedTemplate": "kitchen-default.template"
}

10. Dependencies

<ItemGroup>
  <!-- Avalonia -->
  <PackageReference Include="Avalonia" Version="11.*" />
  <PackageReference Include="Avalonia.Desktop" Version="11.*" />
  <PackageReference Include="Avalonia.Themes.Fluent" Version="11.*" />

  <!-- Editor -->
  <PackageReference Include="AvaloniaEdit" Version="11.*" />

  <!-- MVVM -->
  <PackageReference Include="CommunityToolkit.Mvvm" Version="8.*" />

  <!-- Project Reference -->
  <ProjectReference Include="../Inspectron.Epson.Templates/Inspectron.Epson.Templates.csproj" />
</ItemGroup>

11. Testing

Manual Test Cases

  1. Basic Editing

    • Create new template
    • Edit with syntax highlighting
    • Save and verify file content
  2. Autocomplete

    • Type # → shows styles
    • Type { → shows data properties
    • Type {dish. inside foreach → shows dish properties + loop vars
  3. Validation

    • Missing @end → shows error
    • Unused loop variable → shows warning
    • Fix error → error disappears
  4. Preview

    • Edit template → preview updates
    • Switch printer → width changes
    • Missing data → shows empty (no crash)
  5. Assignments

    • Add new assignment
    • Change template mapping
    • Delete assignment
    • Save and verify JSON

12. Timeline

Phase 1: Core Engine (Foundation)

  1. Project setup and structure
  2. Lexer implementation + tests
  3. Parser implementation + tests
  4. Interpreter implementation + tests
  5. Configuration and resolver
  6. File storage
  7. Factory integration
  8. Default templates
  9. Integration testing
  10. Migration and deployment

Phase 2: Editor (After Phase 1 Complete)

  1. Avalonia project setup
  2. Basic UI layout (panels, splitters)
  3. Template list with file operations
  4. Editor integration with AvaloniaEdit
  5. Syntax highlighting
  6. Basic preview rendering
  7. Validation display
  8. Autocomplete - styles and control flow
  9. Autocomplete - data bindings with scope
  10. Assignments editor
  11. Sample data editor
  12. Polish and testing