experiments for the new client
This commit is contained in:
882
template_editor_plan.md
Normal file
882
template_editor_plan.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user