Implement critical editor override state

This commit is contained in:
2026-03-15 11:40:12 +01:00
parent e9e386aa6c
commit 8b345a7c37
16 changed files with 650 additions and 141 deletions

View File

@@ -65,7 +65,11 @@
"descriptionText": "Current curated prose",
"rawAffixText": "+8H - 2S",
"parseStatus": "verified",
"parsedJson": "{}",
"parsedJson": "{\"version\":1,\"isDescriptionOverridden\":false,\"isRawAffixTextOverridden\":false,\"areEffectsOverridden\":false,\"areBranchesOverridden\":false,\"effects\":[],\"branches\":[]}",
"isDescriptionOverridden": false,
"isRawAffixTextOverridden": false,
"areEffectsOverridden": false,
"areBranchesOverridden": false,
"validationMessages": [],
"effects": [],
"branches": []
@@ -77,9 +81,21 @@
<h2 class="panel-title">Cell re-parse</h2>
<p class="panel-copy"><code>POST /api/tables/critical/{slug}/cells/{resultId}/reparse</code></p>
<pre class="code-block">{
"rawCellText": "Strike to thigh. +8H\nWith greaves: blow glances aside."
"currentState": {
"rawCellText": "Strike to thigh. +8H\nWith greaves: blow glances aside.",
"descriptionText": "Curated prose",
"rawAffixText": "+8H",
"parseStatus": "partial",
"parsedJson": "{}",
"isDescriptionOverridden": true,
"isRawAffixTextOverridden": false,
"areEffectsOverridden": false,
"areBranchesOverridden": false,
"effects": [],
"branches": []
}
}</pre>
<p class="panel-copy">Re-runs the shared single-cell parser and returns a refreshed editor payload without saving changes.</p>
<p class="panel-copy">Re-runs the shared single-cell parser, merges the generated result with the current override state, and returns the refreshed editor payload without saving changes.</p>
</section>
<section class="panel">
@@ -91,6 +107,10 @@
"rawAffixText": "+10H - must parry 2 rnds",
"parseStatus": "manually_curated",
"parsedJson": "{\"reviewed\":true}",
"isDescriptionOverridden": true,
"isRawAffixTextOverridden": false,
"areEffectsOverridden": false,
"areBranchesOverridden": false,
"effects": [
{
"effectCode": "direct_hits",
@@ -104,7 +124,9 @@
"bodyPart": null,
"isPermanent": false,
"sourceType": "symbol",
"sourceText": "+10H"
"sourceText": "+10H",
"originKey": "base:direct_hits:1",
"isOverridden": true
}
],
"branches": []

View File

@@ -391,7 +391,7 @@
try
{
var response = await LookupService.ReparseCriticalCellAsync(selectedTableSlug, editingResultId.Value, editorModel.RawCellText);
var response = await LookupService.ReparseCriticalCellAsync(selectedTableSlug, editingResultId.Value, editorModel.ToRequest());
if (response is null)
{
editorReparseError = "The selected cell could not be re-parsed.";

View File

@@ -14,6 +14,9 @@ public sealed class CriticalBranchEditorModel
public string? RawAffixText { get; set; }
public string ParsedJson { get; set; } = "{}";
public int SortOrder { get; set; }
public string? OriginKey { get; set; }
public bool IsOverridden { get; set; }
public bool AreEffectsOverridden { get; set; }
public List<CriticalEffectEditorModel> Effects { get; set; } = [];
public static CriticalBranchEditorModel FromItem(CriticalBranchEditorItem item) =>
@@ -28,6 +31,9 @@ public sealed class CriticalBranchEditorModel
RawAffixText = item.RawAffixText,
ParsedJson = item.ParsedJson,
SortOrder = item.SortOrder,
OriginKey = item.OriginKey,
IsOverridden = item.IsOverridden,
AreEffectsOverridden = item.AreEffectsOverridden,
Effects = item.Effects.Select(CriticalEffectEditorModel.FromItem).ToList()
};
@@ -42,6 +48,9 @@ public sealed class CriticalBranchEditorModel
RawAffixText,
SerializeParsedEffects(Effects),
SortOrder,
OriginKey,
IsOverridden,
AreEffectsOverridden,
Effects.Select(effect => effect.ToItem()).ToList());
private string BuildRawText()

View File

@@ -76,7 +76,7 @@
}
<div class="field-shell">
<label>Result Text</label>
<InputTextArea class="input-shell critical-editor-textarea compact" @bind-Value="Model.DescriptionText" />
<InputTextArea class="input-shell critical-editor-textarea compact" @bind-Value="Model.DescriptionText" @bind-Value:after="MarkDescriptionOverridden" />
</div>
</section>
@@ -134,11 +134,11 @@
<div class="critical-editor-branch-line">
<div class="field-shell">
<label>Condition</label>
<InputText class="input-shell" @bind-Value="branch.ConditionText" />
<InputText class="input-shell" @bind-Value="branch.ConditionText" @bind-Value:after="() => MarkBranchOverridden(branch)" />
</div>
<div class="field-shell critical-editor-branch-outcome">
<label>Outcome Text</label>
<InputText class="input-shell" @bind-Value="branch.DescriptionText" />
<InputText class="input-shell" @bind-Value="branch.DescriptionText" @bind-Value:after="() => MarkBranchOverridden(branch)" />
</div>
</div>
@@ -321,7 +321,13 @@
private void AddBaseEffect()
{
Model?.Effects.Add(CreateDefaultEffectModel());
if (Model is null)
{
return;
}
Model.AreEffectsOverridden = true;
Model.Effects.Add(CreateDefaultEffectModel());
}
private void RemoveBaseEffect(int index)
@@ -331,6 +337,7 @@
return;
}
Model.AreEffectsOverridden = true;
Model.Effects.RemoveAt(index);
}
@@ -344,8 +351,10 @@
Model.Branches.Add(new CriticalBranchEditorModel
{
ConditionText = $"Condition {Model.Branches.Count + 1}",
SortOrder = Model.Branches.Count + 1
SortOrder = Model.Branches.Count + 1,
IsOverridden = true
});
Model.AreBranchesOverridden = true;
}
private void RemoveBranch(int index)
@@ -355,11 +364,13 @@
return;
}
Model.AreBranchesOverridden = true;
Model.Branches.RemoveAt(index);
}
private static void AddBranchEffect(CriticalBranchEditorModel branch)
{
branch.AreEffectsOverridden = true;
branch.Effects.Add(CreateDefaultEffectModel());
}
@@ -370,6 +381,7 @@
return;
}
branch.AreEffectsOverridden = true;
branch.Effects.RemoveAt(index);
}
@@ -377,7 +389,8 @@
new()
{
EffectCode = CriticalEffectCodes.DirectHits,
SourceType = "symbol"
SourceType = "symbol",
IsOverridden = true
};
private static string GetBranchTitle(CriticalBranchEditorModel branch, int index) =>
@@ -428,6 +441,25 @@
effect.IsPermanent = false;
effect.SourceText = null;
effect.SourceType = AffixDisplayMap.TryGet(effect.EffectCode, out _) ? "symbol" : "manual";
effect.IsOverridden = true;
}
private void MarkDescriptionOverridden()
{
if (Model is not null)
{
Model.IsDescriptionOverridden = true;
}
}
private static void MarkBranchOverridden(CriticalBranchEditorModel branch)
{
branch.IsOverridden = true;
}
private static void MarkEffectOverridden(CriticalEffectEditorModel effect)
{
effect.IsOverridden = true;
}
private static string GetAdvancedSummary(CriticalCellEditorModel model)
@@ -531,7 +563,7 @@
{
<div class="field-shell critical-editor-effect-extra">
<label>Body Part</label>
<InputText class="input-shell" @bind-Value="effect.BodyPart" />
<InputText class="input-shell" @bind-Value="effect.BodyPart" @bind-Value:after="() => MarkEffectOverridden(effect)" />
</div>
}
</div>
@@ -543,30 +575,30 @@
{
case CriticalEffectCodes.DirectHits:
<label>Hits</label>
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.ValueInteger" />
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.ValueInteger" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
case CriticalEffectCodes.StunnedRounds:
case CriticalEffectCodes.MustParryRounds:
case CriticalEffectCodes.NoParryRounds:
<label>Rounds</label>
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.DurationRounds" />
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.DurationRounds" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
case CriticalEffectCodes.BleedPerRound:
<label>Bleed / Round</label>
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.PerRound" />
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.PerRound" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
case CriticalEffectCodes.FoePenalty:
case CriticalEffectCodes.AttackerBonusNextRound:
<label>Modifier</label>
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.Modifier" />
<InputNumber TValue="int?" class="input-shell" @bind-Value="effect.Modifier" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
case CriticalEffectCodes.PowerPointModifier:
<label>Expression</label>
<InputText class="input-shell" @bind-Value="effect.ValueExpression" />
<InputText class="input-shell" @bind-Value="effect.ValueExpression" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
default:
<label>Display Text</label>
<InputText class="input-shell" @bind-Value="effect.SourceText" />
<InputText class="input-shell" @bind-Value="effect.SourceText" @bind-Value:after="() => MarkEffectOverridden(effect)" />
break;
}
</div>;

View File

@@ -20,6 +20,10 @@ public sealed class CriticalCellEditorModel
public string? RawAffixText { get; set; }
public string ParseStatus { get; set; } = string.Empty;
public string ParsedJson { get; set; } = "{}";
public bool IsDescriptionOverridden { get; set; }
public bool IsRawAffixTextOverridden { get; set; }
public bool AreEffectsOverridden { get; set; }
public bool AreBranchesOverridden { get; set; }
public List<string> ValidationMessages { get; set; } = [];
public List<CriticalEffectEditorModel> Effects { get; set; } = [];
public List<CriticalBranchEditorModel> Branches { get; set; } = [];
@@ -42,18 +46,27 @@ public sealed class CriticalCellEditorModel
RawAffixText = response.RawAffixText,
ParseStatus = response.ParseStatus,
ParsedJson = response.ParsedJson,
IsDescriptionOverridden = response.IsDescriptionOverridden,
IsRawAffixTextOverridden = response.IsRawAffixTextOverridden,
AreEffectsOverridden = response.AreEffectsOverridden,
AreBranchesOverridden = response.AreBranchesOverridden,
ValidationMessages = response.ValidationMessages.ToList(),
Effects = response.Effects.Select(CriticalEffectEditorModel.FromItem).ToList(),
Branches = response.Branches.Select(CriticalBranchEditorModel.FromItem).ToList()
};
public CriticalCellUpdateRequest ToRequest() =>
new(
public CriticalCellUpdateRequest ToRequest()
{
var request = new CriticalCellUpdateRequest(
RawCellText,
DescriptionText,
RawAffixText,
ResolveParseStatus(Effects, Branches),
SerializeParsedEffects(Effects),
IsDescriptionOverridden,
IsRawAffixTextOverridden,
AreEffectsOverridden,
AreBranchesOverridden,
Effects.Select(effect => effect.ToItem()).ToList(),
Branches
.OrderBy(branch => branch.SortOrder)
@@ -64,6 +77,12 @@ public sealed class CriticalCellEditorModel
})
.ToList());
return request with
{
ParsedJson = CriticalCellEditorSnapshot.FromRequest(request).ToJson()
};
}
private static string ResolveParseStatus(
IReadOnlyList<CriticalEffectEditorModel> effects,
IReadOnlyList<CriticalBranchEditorModel> branches) =>

View File

@@ -16,6 +16,8 @@ public sealed class CriticalEffectEditorModel
public bool IsPermanent { get; set; }
public string SourceType { get; set; } = "symbol";
public string? SourceText { get; set; }
public string? OriginKey { get; set; }
public bool IsOverridden { get; set; }
public static CriticalEffectEditorModel FromItem(CriticalEffectEditorItem item) =>
new()
@@ -31,7 +33,9 @@ public sealed class CriticalEffectEditorModel
BodyPart = item.BodyPart,
IsPermanent = item.IsPermanent,
SourceType = item.SourceType,
SourceText = item.SourceText
SourceText = item.SourceText,
OriginKey = item.OriginKey,
IsOverridden = item.IsOverridden
};
public CriticalEffectEditorItem ToItem() =>
@@ -47,5 +51,7 @@ public sealed class CriticalEffectEditorModel
BodyPart,
IsPermanent,
SourceType,
SourceText);
SourceText,
OriginKey,
IsOverridden);
}