Detach custom roll panel from log feed

This commit is contained in:
2026-04-04 20:08:44 +02:00
parent 9e6e6fe8c7
commit 22ee512cb7
4 changed files with 93 additions and 73 deletions

View File

@@ -67,7 +67,7 @@ Gameplay capabilities now include:
- Rolemaster create/edit forms now keep the expression authoritative, show generic Rolemaster syntax help, and reveal `FumbleRange` only when the expression is an open-ended percentile roll - Rolemaster create/edit forms now keep the expression authoritative, show generic Rolemaster syntax help, and reveal `FumbleRange` only when the expression is an open-ended percentile roll
- Rolemaster roll execution now supports generic standard Rolemaster rolls (`NdS+x`, with implicit count `1` for `dS`) plus open-ended percentile (`d100!+x`) with recursive high-end chaining and low-end subtraction based on `FumbleRange`; low-end trigger rolls are shown for auditability but do not count toward the total - Rolemaster roll execution now supports generic standard Rolemaster rolls (`NdS+x`, with implicit count `1` for `dS`) plus open-ended percentile (`d100!+x`) with recursive high-end chaining and low-end subtraction based on `FumbleRange`; low-end trigger rolls are shown for auditability but do not count toward the total
- Compact campaign-log summaries stay dense for Rolemaster rolls, while lazy-loaded roll detail includes ordered die metadata for each open-ended follow-up step - Compact campaign-log summaries stay dense for Rolemaster rolls, while lazy-loaded roll detail includes ordered die metadata for each open-ended follow-up step
- Play screen campaign logs now include a bottom-mounted custom roll composer that records arbitrary expressions against the selected character without creating a skill; invalid expressions stay inline on the field with tooltip/error styling instead of using error toasts - Play screen campaign logs now include a detached bottom custom-roll panel below the scrollable log feed; it records arbitrary expressions against the selected character without creating a skill, and invalid expressions stay inline on the field with tooltip/error styling instead of using error toasts
- Startup migration coverage is validated against a copied temp-file instance of `RpgRoller/App_Data/rpgroller.development.db` before feature work is considered complete - Startup migration coverage is validated against a copied temp-file instance of `RpgRoller/App_Data/rpgroller.development.db` before feature work is considered complete
## Prerequisites ## Prerequisites

View File

@@ -1,5 +1,6 @@
<aside @ref="LogPanelRef" class="card log-panel"> <aside @ref="LogPanelRef" class="card log-panel">
<div class="section-head"><h2>Campaign Log</h2></div> <div class="section-head"><h2>Campaign Log</h2></div>
<div @ref="LogFeedRef" class="log-panel-feed">
@if (IsCampaignDataLoading) @if (IsCampaignDataLoading)
{ {
<div class="skeleton-stack"> <div class="skeleton-stack">
@@ -73,7 +74,9 @@
} }
</ul> </ul>
} }
</div>
<section class="custom-roll-panel" aria-label="Custom roll panel">
<form class="custom-roll-composer" @onsubmit="SubmitCustomRollAsync" @onsubmit:preventDefault> <form class="custom-roll-composer" @onsubmit="SubmitCustomRollAsync" @onsubmit:preventDefault>
<div class="custom-roll-composer-head"> <div class="custom-roll-composer-head">
<label for="custom-roll-expression" class="custom-roll-label">Custom roll</label> <label for="custom-roll-expression" class="custom-roll-label">Custom roll</label>
@@ -99,4 +102,5 @@
<p id="@CustomRollErrorElementId" class="field-error" role="alert">@CustomRollErrorMessage</p> <p id="@CustomRollErrorElementId" class="field-error" role="alert">@CustomRollErrorMessage</p>
} }
</form> </form>
</section>
</aside> </aside>

View File

@@ -23,7 +23,7 @@ public partial class CampaignLogPanel
{ {
try try
{ {
await JS.InvokeVoidAsync("rpgRollerApi.scrollElementToBottom", LogPanelRef); await JS.InvokeVoidAsync("rpgRollerApi.scrollElementToBottom", LogFeedRef);
} }
catch (JSDisconnectedException) catch (JSDisconnectedException)
{ {
@@ -44,6 +44,7 @@ public partial class CampaignLogPanel
private RpgRollerApiClient ApiClient { get; set; } = null!; private RpgRollerApiClient ApiClient { get; set; } = null!;
private ElementReference LogPanelRef { get; set; } private ElementReference LogPanelRef { get; set; }
private ElementReference LogFeedRef { get; set; }
private ElementReference CustomRollInputRef { get; set; } private ElementReference CustomRollInputRef { get; set; }
private int LastRenderedLogCount { get; set; } private int LastRenderedLogCount { get; set; }
private Guid? LastRenderedLogRollId { get; set; } private Guid? LastRenderedLogRollId { get; set; }

View File

@@ -336,12 +336,21 @@ select:focus-visible {
min-height: 0; min-height: 0;
display: grid; display: grid;
gap: 1rem; gap: 1rem;
align-content: start; grid-template-rows: auto minmax(0, 1fr) auto;
align-content: stretch;
} }
.app-play .log-panel { .app-play .log-panel {
overflow: hidden;
}
.log-panel-feed {
min-height: 0;
display: grid;
align-content: start;
overflow-y: auto; overflow-y: auto;
overscroll-behavior: contain; overscroll-behavior: contain;
padding-right: 0.15rem;
} }
.skill-list { .skill-list {
@@ -618,11 +627,17 @@ select:focus-visible {
gap: 0.7rem; gap: 0.7rem;
} }
.custom-roll-panel {
border-top: 1px solid color-mix(in srgb, var(--card-border) 72%, #ffffff 28%);
background: color-mix(in srgb, var(--card) 88%, #ffffff 12%);
border-radius: 0.95rem;
padding: 0.85rem 0.9rem 0.9rem;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.custom-roll-composer { .custom-roll-composer {
display: grid; display: grid;
gap: 0.45rem; gap: 0.45rem;
padding-top: 0.2rem;
border-top: 1px solid color-mix(in srgb, var(--card-border) 72%, #ffffff 28%);
} }
.custom-roll-composer-head { .custom-roll-composer-head {