experiments for the new client
This commit is contained in:
@@ -378,15 +378,16 @@ receipt.Gangs[0].Dishes.Add(new Dish(1, "Salmon Taco")
|
|||||||
|
|
||||||
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
var serializedReceipt = JsonSerializer.Serialize(receipt, new JsonSerializerOptions { WriteIndented = true });
|
||||||
var deserializedReceipt = JsonSerializer.Deserialize<KitchenReceipt>(serializedReceipt);
|
var deserializedReceipt = JsonSerializer.Deserialize<KitchenReceipt>(serializedReceipt);
|
||||||
KitchenReceiptConverter converter = new KitchenReceiptConverter(bigLineWidth: 18, lineWidth: 33);
|
KitchenReceiptConverter converter = new KitchenReceiptConverter(bigLineWidth: 12, lineWidth: 33);
|
||||||
var printCommands = converter.ConvertToPrintCommands(deserializedReceipt);
|
var printCommands = converter.ConvertToPrintCommands(deserializedReceipt);
|
||||||
|
|
||||||
Console.WriteLine("=== PRINT COMMANDS ===\n");
|
Console.WriteLine("=== PRINT COMMANDS ===\n");
|
||||||
//var printer = new HtmlPrinter(@".\test.html", paperWidth: 258);
|
//var printer = new HtmlPrinter(@".\test.html", paperWidth: 258);
|
||||||
//await printer.ConnectAsync("");
|
//await printer.ConnectAsync("");
|
||||||
|
bool useDelay=true;
|
||||||
var printer = new EpsonPrinter();
|
var printer = new EpsonPrinter();
|
||||||
await printer.ConnectAsync("127.0.0.1",8888);
|
await printer.ConnectAsync("127.0.0.1", 8888);
|
||||||
await Task.Delay(200);
|
if (useDelay)await Task.Delay(200);
|
||||||
await printer.FeedLinesAsync(1);
|
await printer.FeedLinesAsync(1);
|
||||||
await printer.SetCustomLineSpacing(22);
|
await printer.SetCustomLineSpacing(22);
|
||||||
foreach (var command in printCommands)
|
foreach (var command in printCommands)
|
||||||
@@ -397,25 +398,26 @@ foreach (var command in printCommands)
|
|||||||
if (command.IsRed) attributes += "[RED] ";
|
if (command.IsRed) attributes += "[RED] ";
|
||||||
|
|
||||||
Console.WriteLine($"{attributes}{command.Text}");
|
Console.WriteLine($"{attributes}{command.Text}");
|
||||||
await printer.SetBiggerFontTM220(command.IsBig);
|
await printer.SetBiggerFontTM220(command.IsBig, command.IsTall, secondaryFont: false);
|
||||||
|
|
||||||
|
|
||||||
await Task.Delay(200);
|
|
||||||
|
|
||||||
|
if (useDelay) await Task.Delay(200);
|
||||||
|
|
||||||
await printer.SetRedColor(command.IsRed);
|
await printer.SetRedColor(command.IsRed);
|
||||||
|
|
||||||
await Task.Delay(200);
|
if (useDelay) await Task.Delay(200);
|
||||||
|
|
||||||
await printer.SetEmphasized(command.IsBold);
|
await printer.SetEmphasized(command.IsBold);
|
||||||
await Task.Delay(200);
|
if (useDelay) await Task.Delay(200);
|
||||||
|
|
||||||
await printer.PrintTextAsync(command.Text + "\n");
|
await printer.PrintTextAsync(command.Text + "\n");
|
||||||
await Task.Delay(200);
|
if (useDelay) await Task.Delay(200);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await printer.FeedLinesAsync(5);
|
await printer.FeedLinesAsync(5);
|
||||||
await Task.Delay(200*5);
|
if (useDelay) await Task.Delay(200*5);
|
||||||
await printer.CutAsync();
|
await printer.CutAsync();
|
||||||
|
|
||||||
//await printer.SetDefaultLineSpacing();
|
//await printer.SetDefaultLineSpacing();
|
||||||
|
|||||||
@@ -318,9 +318,9 @@ public class EpsonPrinter : IEpsonPrinter
|
|||||||
|
|
||||||
byte value = 0x00;
|
byte value = 0x00;
|
||||||
if (biggerWidth)
|
if (biggerWidth)
|
||||||
value += 0x10;
|
|
||||||
if (biggerHeight)
|
|
||||||
value += 0x20;
|
value += 0x20;
|
||||||
|
if (biggerHeight)
|
||||||
|
value += 0x10;
|
||||||
if (secondaryFont)
|
if (secondaryFont)
|
||||||
value += 0x01;
|
value += 0x01;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class KitchenReceiptConverter
|
|||||||
var commands = new List<PrintCommand>();
|
var commands = new List<PrintCommand>();
|
||||||
|
|
||||||
// Header - Restaurant name in red
|
// Header - Restaurant name in red
|
||||||
commands.Add(new PrintCommand(Center(kitchenReceipt.Title, true)) { IsRed = true , IsBig = true});
|
commands.Add(new PrintCommand(Center(kitchenReceipt.Title, true)) { IsRed = true , IsBig = true, IsTall = true });
|
||||||
commands.Add(new PrintCommand(CreateDashedLine()));
|
commands.Add(new PrintCommand(CreateDashedLine()));
|
||||||
commands.Add(new PrintCommand(""));
|
commands.Add(new PrintCommand(""));
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ public class KitchenReceiptConverter
|
|||||||
foreach (Gang gang in kitchenReceipt.Gangs)
|
foreach (Gang gang in kitchenReceipt.Gangs)
|
||||||
{
|
{
|
||||||
|
|
||||||
commands.Add(new PrintCommand(Center($"{gang.Id}. {gang.Name}", false)) { IsRed = true });
|
commands.Add(new PrintCommand(Center($"{gang.Id}. {gang.Name}", true)) { IsRed = true, IsBig = true, IsTall = true});
|
||||||
foreach (var dish in gang.Dishes)
|
foreach (var dish in gang.Dishes)
|
||||||
{
|
{
|
||||||
PrintDish(dish, commands);
|
PrintDish(dish, commands);
|
||||||
@@ -96,25 +96,25 @@ public class KitchenReceiptConverter
|
|||||||
private static void PrintDish(Dish dish, List<PrintCommand> commands)
|
private static void PrintDish(Dish dish, List<PrintCommand> commands)
|
||||||
{
|
{
|
||||||
string dishLine = $"{dish.Number}x {dish.Name}";
|
string dishLine = $"{dish.Number}x {dish.Name}";
|
||||||
commands.Add(new PrintCommand(dishLine));
|
commands.Add(new PrintCommand(dishLine){IsTall = true});
|
||||||
// Modifications
|
// Modifications
|
||||||
if (dish.Modifications != null && (dish.Modifications.Removed.Any() || dish.Modifications.Added.Any()))
|
if (dish.Modifications != null && (dish.Modifications.Removed.Any() || dish.Modifications.Added.Any()))
|
||||||
{
|
{
|
||||||
foreach (var removed in dish.Modifications.Removed)
|
foreach (var removed in dish.Modifications.Removed)
|
||||||
{
|
{
|
||||||
commands.Add(new PrintCommand($" - {removed}", isBold:true));
|
commands.Add(new PrintCommand($" - {removed}", isBold:true) { IsTall = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var added in dish.Modifications.Added)
|
foreach (var added in dish.Modifications.Added)
|
||||||
{
|
{
|
||||||
commands.Add(new PrintCommand($" + {added}", isBold: true));
|
commands.Add(new PrintCommand($" + {added}", isBold: true) { IsTall = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpecialInstruction
|
// SpecialInstruction
|
||||||
if (!string.IsNullOrEmpty(dish.Comment))
|
if (!string.IsNullOrEmpty(dish.Comment))
|
||||||
{
|
{
|
||||||
commands.Add(new PrintCommand($" Comment: {dish.Comment}", isBold: true));
|
commands.Add(new PrintCommand($" Comment: {dish.Comment}", isBold: true) { IsTall = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
//commands.Add(new PrintCommand($""));
|
//commands.Add(new PrintCommand($""));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ public class PrintCommand
|
|||||||
{
|
{
|
||||||
public string Text { get; set; }
|
public string Text { get; set; }
|
||||||
public bool IsBig { get; set; }
|
public bool IsBig { get; set; }
|
||||||
|
public bool IsTall { get; set; }
|
||||||
public bool IsBold { get; set; }
|
public bool IsBold { get; set; }
|
||||||
public bool IsRed { get; set; }
|
public bool IsRed { get; set; }
|
||||||
public int? SetLineSpacing { get; set; }=null;
|
public int? SetLineSpacing { get; set; }=null;
|
||||||
|
|||||||
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