|
|
|
|
@@ -0,0 +1,882 @@
|
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
```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:
|
|
|
|
|
- `0x01` → `tm-t30iii`
|
|
|
|
|
- `0x13` → `tm-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
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
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`:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"templatesDirectory": "./templates",
|
|
|
|
|
"theme": "dark",
|
|
|
|
|
"fontSize": 14,
|
|
|
|
|
"showLineNumbers": true,
|
|
|
|
|
"wordWrap": false,
|
|
|
|
|
"autoSave": true,
|
|
|
|
|
"autoSaveDelayMs": 2000,
|
|
|
|
|
"previewDebounceMs": 300,
|
|
|
|
|
"lastOpenedTemplate": "kitchen-default.template"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 10. Dependencies
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
<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
|