Expose dashboard template-launch and preview action control surfaces

This commit is contained in:
axiomlogicnexus 2026-05-10 16:08:13 +02:00
parent a052777bed
commit 9fafb7d5c7
5 changed files with 515 additions and 5 deletions

View file

@ -12683,6 +12683,259 @@ UHyperTwistCoachDashboardWidget::GetDisplayedRunRecapActionControlInspectSurface
return Surface;
}
FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedPreferredTemplateLaunchActionInspectSurface() const
{
FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface Surface;
Surface.Headline = TEXT("Preferred template launch action");
const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid()
&& CachedRunState.ActiveDeck.IsStructurallyValid();
FHyperTwistTrainingTemplateLaunchSelectionProvenance SelectionProvenance;
const FHyperTwistTrainingSessionTemplate* PreferredTemplateLaunch =
FindPreferredDashboardTemplateLaunch(&SelectionProvenance);
int32 EligibleTemplateCount = 0;
for (const FHyperTwistTrainingSessionTemplate& SessionTemplate : CachedActiveTrainingSessionTemplates)
{
if (HyperTwistCoachDashboardWidgetInternal::ScoreTemplateForDashboardLaunch(SessionTemplate) > 0)
{
EligibleTemplateCount += 1;
}
}
const bool bHasPreferredTemplate = PreferredTemplateLaunch != nullptr;
const bool bUsesReviewLane =
bHasPreferredTemplate
&& (PreferredTemplateLaunch->bPreferReviewRun
|| PreferredTemplateLaunch->TemplateKind == EHyperTwistTrainingSessionTemplateKind::ReviewPlan
|| PreferredTemplateLaunch->TemplateKind == EHyperTwistTrainingSessionTemplateKind::Coach);
const FString PreferredTemplateTitle = bHasPreferredTemplate
? (PreferredTemplateLaunch->Title.IsEmpty()
? PreferredTemplateLaunch->TemplateId
: PreferredTemplateLaunch->Title)
: TEXT("n/a");
const FString PreferredTemplateUserId = bHasPreferredTemplate
? (PreferredTemplateLaunch->UserId.IsEmpty() ? DefaultUserId : PreferredTemplateLaunch->UserId)
: FString();
const FHyperTwistTrainingTemplateLaunchScopedStats* PreferredTemplateStats =
bHasPreferredTemplate
? FindCachedTemplateLaunchScopedStats(
PreferredTemplateLaunch->TemplateId,
PreferredTemplateUserId)
: nullptr;
const FString TemplateSelectionDetail =
HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchSelectionDetailFragment(
SelectionProvenance.IsStructurallyValid() ? &SelectionProvenance : nullptr
);
const bool bCanLaunchPreferredTemplate =
!bHasLiveAttemptTimer && !bHasActiveRun && bHasPreferredTemplate;
FString TemplateActionLabel = TEXT("No Template");
if (bHasPreferredTemplate)
{
TemplateActionLabel =
PreferredTemplateTitle.Len() <= 28
? PreferredTemplateTitle
: (bUsesReviewLane ? TEXT("Launch Review Template") : TEXT("Launch Recovery Template"));
}
if (bHasLiveAttemptTimer && bHasPreferredTemplate)
{
TemplateActionLabel = TEXT("Template Busy");
}
else if (bHasActiveRun && bHasPreferredTemplate)
{
TemplateActionLabel = TEXT("Clear Run For Template");
}
Surface.ActionLabel = !DisplayedTemplateLaunchActionLabel.IsEmpty()
? DisplayedTemplateLaunchActionLabel
: TemplateActionLabel;
Surface.DisplayedTemplateStatusLine = !DisplayedTemplateLaunchStatusLine.IsEmpty()
? DisplayedTemplateLaunchStatusLine
: TEXT("Template launch status: not yet displayed.");
Surface.DisplayedTemplateDetailLine = !DisplayedTemplateLaunchDetailLine.IsEmpty()
? DisplayedTemplateLaunchDetailLine
: TEXT("Template launch detail: not yet displayed.");
Surface.TemplateTitle = PreferredTemplateTitle;
Surface.TemplateId = bHasPreferredTemplate && !PreferredTemplateLaunch->TemplateId.IsEmpty()
? PreferredTemplateLaunch->TemplateId
: TEXT("n/a");
Surface.TemplateKindLabel = bHasPreferredTemplate
? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(PreferredTemplateLaunch->TemplateKind)
: TEXT("n/a");
Surface.UserId = PreferredTemplateUserId.IsEmpty() ? TEXT("n/a") : PreferredTemplateUserId;
Surface.FocusDeckId =
bHasPreferredTemplate && !PreferredTemplateLaunch->FocusDeckId.IsEmpty()
? PreferredTemplateLaunch->FocusDeckId
: TEXT("n/a");
Surface.GuidanceLabel =
bHasPreferredTemplate && !PreferredTemplateLaunch->GuidanceLabel.IsEmpty()
? PreferredTemplateLaunch->GuidanceLabel
: TEXT("n/a");
Surface.SuggestedModeLabel = bHasPreferredTemplate
? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(PreferredTemplateLaunch->SuggestedMode)
: TEXT("n/a");
Surface.SuggestedSelectionLabel = bHasPreferredTemplate
? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
PreferredTemplateLaunch->SuggestedSelectionPolicy)
: TEXT("n/a");
Surface.SelectionDetailLine =
TemplateSelectionDetail.IsEmpty() ? TEXT("none") : TemplateSelectionDetail;
Surface.EligibleTemplateCount = EligibleTemplateCount;
Surface.ActiveTemplateCount = CachedActiveTrainingSessionTemplates.Num();
Surface.RecommendedCaseCount =
bHasPreferredTemplate ? PreferredTemplateLaunch->RecommendedCaseCount : 0;
Surface.FocusCaseCount = bHasPreferredTemplate ? PreferredTemplateLaunch->FocusCaseIds.Num() : 0;
Surface.bHasPreferredTemplate = bHasPreferredTemplate;
Surface.bUsesReviewLane = bUsesReviewLane;
Surface.bHasScopedStats =
PreferredTemplateStats != nullptr && PreferredTemplateStats->IsStructurallyValid();
Surface.bHasSelectionProvenance = SelectionProvenance.IsStructurallyValid();
Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer;
Surface.bHasActiveRun = bHasActiveRun;
Surface.bActionEnabled = bCanLaunchPreferredTemplate;
Surface.StatusLine = FString::Printf(
TEXT("%s | Action: %s (%s)"),
*Surface.DisplayedTemplateStatusLine,
*Surface.ActionLabel,
Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled"));
Surface.DetailLine = FString::Printf(
TEXT("%s | active run %s | live timer %s | review lane %s | selection provenance %s"),
*Surface.DisplayedTemplateDetailLine,
Surface.bHasActiveRun ? TEXT("yes") : TEXT("no"),
Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"),
Surface.bUsesReviewLane ? TEXT("yes") : TEXT("no"),
Surface.bHasSelectionProvenance ? TEXT("yes") : TEXT("no"));
return Surface;
}
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedRecommendedPreviewActionInspectSurface() const
{
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface Surface;
Surface.Headline = TEXT("Recommended preview action");
Surface.GuidanceLaneLabel = TEXT("recommended");
const auto BuildPreviewLaneLabel = [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString
{
return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended
? TEXT("recommended")
: (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp
? TEXT("follow-up")
: TEXT("none"));
};
const FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface PreviewSurface =
GetDisplayedRecommendedGuidanceDeckPreviewInspectSurface();
const bool bActionEnabled =
!bHasLiveAttemptTimer && CachedCoachPanelState.bCanStartCoachRecommendedRun;
Surface.ActionLabel = !DisplayedPreviewRecommendedActionLabel.IsEmpty()
? DisplayedPreviewRecommendedActionLabel
: TEXT("Preview Recommended");
Surface.PreviewStatusLine = PreviewSurface.StatusLine.IsEmpty()
? TEXT("Recommended guidance: no guidance available.")
: PreviewSurface.StatusLine;
Surface.DeckLabel = PreviewSurface.DeckLabel.IsEmpty() ? TEXT("n/a") : PreviewSurface.DeckLabel;
Surface.BriefHeadline =
PreviewSurface.BriefHeadline.IsEmpty() ? TEXT("n/a") : PreviewSurface.BriefHeadline;
Surface.ActivePreviewLaneLabel = BuildPreviewLaneLabel(ActiveGuidancePreviewLane);
Surface.FocusDeckId =
PreviewSurface.FocusDeckId.IsEmpty() ? TEXT("n/a") : PreviewSurface.FocusDeckId;
Surface.FocusMethodSegmentId = PreviewSurface.FocusMethodSegmentId.IsEmpty()
? TEXT("n/a")
: PreviewSurface.FocusMethodSegmentId;
Surface.CaseCount = PreviewSurface.CaseCount;
Surface.FocusCaseCount = PreviewSurface.FocusCaseCount;
Surface.bGuidanceReadyToLaunch = PreviewSurface.bGuidanceReadyToLaunch;
Surface.bHasGuidancePayload =
PreviewSurface.bHasDeck || PreviewSurface.bHasBrief || PreviewSurface.bGuidanceReadyToLaunch;
Surface.bHasDeck = PreviewSurface.bHasDeck;
Surface.bHasBrief = PreviewSurface.bHasBrief;
Surface.bUsesBriefFallback = PreviewSurface.bUsesBriefFallback;
Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer;
Surface.bActionEnabled = bActionEnabled;
Surface.bActivePreviewLane = PreviewSurface.bActivePreviewLane;
Surface.bClosedLoopIdleDashboard = PreviewSurface.bClosedLoopIdleDashboard;
Surface.StatusLine = FString::Printf(
TEXT("%s | Action: %s (%s)"),
*Surface.PreviewStatusLine,
*Surface.ActionLabel,
Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled"));
Surface.DetailLine = FString::Printf(
TEXT("%s | live timer %s | active preview lane %s"),
*PreviewSurface.DetailLine,
Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"),
*Surface.ActivePreviewLaneLabel);
return Surface;
}
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedFollowUpPreviewActionInspectSurface() const
{
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface Surface;
Surface.Headline = TEXT("Follow-up preview action");
Surface.GuidanceLaneLabel = TEXT("follow-up");
const auto BuildPreviewLaneLabel = [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString
{
return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended
? TEXT("recommended")
: (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp
? TEXT("follow-up")
: TEXT("none"));
};
const FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface PreviewSurface =
GetDisplayedFollowUpGuidanceDeckPreviewInspectSurface();
const bool bActionEnabled =
!bHasLiveAttemptTimer
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance;
Surface.ActionLabel = !DisplayedPreviewFollowUpActionLabel.IsEmpty()
? DisplayedPreviewFollowUpActionLabel
: TEXT("Preview Follow-Up");
Surface.PreviewStatusLine = PreviewSurface.StatusLine.IsEmpty()
? TEXT("Follow-up guidance: no guidance available.")
: PreviewSurface.StatusLine;
Surface.DeckLabel = PreviewSurface.DeckLabel.IsEmpty() ? TEXT("n/a") : PreviewSurface.DeckLabel;
Surface.BriefHeadline =
PreviewSurface.BriefHeadline.IsEmpty() ? TEXT("n/a") : PreviewSurface.BriefHeadline;
Surface.ActivePreviewLaneLabel = BuildPreviewLaneLabel(ActiveGuidancePreviewLane);
Surface.FocusDeckId =
PreviewSurface.FocusDeckId.IsEmpty() ? TEXT("n/a") : PreviewSurface.FocusDeckId;
Surface.FocusMethodSegmentId = PreviewSurface.FocusMethodSegmentId.IsEmpty()
? TEXT("n/a")
: PreviewSurface.FocusMethodSegmentId;
Surface.CaseCount = PreviewSurface.CaseCount;
Surface.FocusCaseCount = PreviewSurface.FocusCaseCount;
Surface.bGuidanceReadyToLaunch = PreviewSurface.bGuidanceReadyToLaunch;
Surface.bHasGuidancePayload =
CachedCoachPanelState.bHasFollowUpGuidance
|| PreviewSurface.bHasDeck
|| PreviewSurface.bHasBrief
|| PreviewSurface.bGuidanceReadyToLaunch;
Surface.bHasDeck = PreviewSurface.bHasDeck;
Surface.bHasBrief = PreviewSurface.bHasBrief;
Surface.bUsesBriefFallback = PreviewSurface.bUsesBriefFallback;
Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer;
Surface.bActionEnabled = bActionEnabled;
Surface.bActivePreviewLane = PreviewSurface.bActivePreviewLane;
Surface.bClosedLoopIdleDashboard = PreviewSurface.bClosedLoopIdleDashboard;
Surface.StatusLine = FString::Printf(
TEXT("%s | Action: %s (%s)"),
*Surface.PreviewStatusLine,
*Surface.ActionLabel,
Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled"));
Surface.DetailLine = FString::Printf(
TEXT("%s | live timer %s | active preview lane %s"),
*PreviewSurface.DetailLine,
Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"),
*Surface.ActivePreviewLaneLabel);
return Surface;
}
FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedActiveAttemptTimerStatusInspectSurface() const
{
@ -25851,6 +26104,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
{
CoachHandoffRecommendationDetailTextBlock->SetText(FText::FromString(CoachHandoffRecommendationDetailLine));
}
DisplayedTemplateLaunchStatusLine = TemplateLaunchStatusLine;
DisplayedTemplateLaunchDetailLine = TemplateLaunchDetailLine;
if (TemplateLaunchHeaderTextBlock != nullptr)
{
TemplateLaunchHeaderTextBlock->SetText(FText::FromString(TEXT("[Template Launch]")));
@ -26062,17 +26317,20 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
{
TemplateLaunchButtonLabel = TEXT("Clear Run For Template");
}
DisplayedTemplateLaunchActionLabel = TemplateLaunchButtonLabel;
if (StartTemplateLaunchButtonLabel != nullptr)
{
StartTemplateLaunchButtonLabel->SetText(FText::FromString(TemplateLaunchButtonLabel));
StartTemplateLaunchButtonLabel->SetText(FText::FromString(DisplayedTemplateLaunchActionLabel));
}
if (StartTemplateLaunchButton != nullptr)
{
StartTemplateLaunchButton->SetIsEnabled(bCanLaunchPreferredTemplate);
}
DisplayedPreviewRecommendedActionLabel = TEXT("Preview Recommended");
if (PreviewRecommendedButtonLabel != nullptr)
{
PreviewRecommendedButtonLabel->SetText(FText::FromString(TEXT("Preview Recommended")));
PreviewRecommendedButtonLabel->SetText(FText::FromString(
DisplayedPreviewRecommendedActionLabel));
}
if (PreviewRecommendedButton != nullptr)
{
@ -26080,9 +26338,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
!bHasLiveAttemptTimer && CachedCoachPanelState.bCanStartCoachRecommendedRun
);
}
DisplayedPreviewFollowUpActionLabel = TEXT("Preview Follow-Up");
if (PreviewFollowUpButtonLabel != nullptr)
{
PreviewFollowUpButtonLabel->SetText(FText::FromString(TEXT("Preview Follow-Up")));
PreviewFollowUpButtonLabel->SetText(FText::FromString(
DisplayedPreviewFollowUpActionLabel));
}
if (PreviewFollowUpButton != nullptr)
{

View file

@ -887,6 +887,18 @@ public:
FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface
GetDisplayedRunRecapActionControlInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface
GetDisplayedPreferredTemplateLaunchActionInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface
GetDisplayedRecommendedPreviewActionInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface
GetDisplayedFollowUpPreviewActionInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface
GetDisplayedActiveAttemptTimerStatusInspectSurface() const;
@ -2026,8 +2038,11 @@ private:
FString DisplayedTransitionStatusLine;
FString DisplayedActiveRunStatusLine;
FString DisplayedActiveRunSummaryLine;
FString DisplayedTemplateLaunchStatusLine;
FString DisplayedTemplateLaunchDetailLine;
FString DisplayedRunRecapStatusLine;
FString DisplayedRunRecapDetailLine;
FString DisplayedTemplateLaunchActionLabel;
FString DisplayedRepeatRunActionLabel;
FString DisplayedDismissRecapActionLabel;
FString DisplayedCurrentCasePromptLine;
@ -2049,6 +2064,8 @@ private:
FString DisplayedAttemptInputStatusLine;
FString DisplayedSplitCaptureStatusLine;
FString DisplayedTimerStatusLine;
FString DisplayedPreviewRecommendedActionLabel;
FString DisplayedPreviewFollowUpActionLabel;
FString DisplayedRecommendedDeckPreviewLine;
FString DisplayedFollowUpDeckPreviewLine;
FString DisplayedMethodDrillDiagnosticsStatusLine;

View file

@ -13536,6 +13536,179 @@ struct FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Headline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DisplayedTemplateStatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DisplayedTemplateDetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TemplateTitle;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TemplateId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TemplateKindLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString UserId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusDeckId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString GuidanceLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SuggestedModeLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SuggestedSelectionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectionDetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 EligibleTemplateCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 ActiveTemplateCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RecommendedCaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 FocusCaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasPreferredTemplate = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bUsesReviewLane = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasScopedStats = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelectionProvenance = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasLiveAttemptTimer = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasActiveRun = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bActionEnabled = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !DetailLine.IsEmpty()
|| bHasPreferredTemplate
|| bActionEnabled;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Headline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString GuidanceLaneLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreviewStatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DeckLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString BriefHeadline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActivePreviewLaneLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusDeckId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusMethodSegmentId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 FocusCaseCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bGuidanceReadyToLaunch = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasGuidancePayload = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasDeck = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasBrief = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bUsesBriefFallback = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasLiveAttemptTimer = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bActionEnabled = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bActivePreviewLane = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bClosedLoopIdleDashboard = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !DetailLine.IsEmpty()
|| bGuidanceReadyToLaunch
|| bHasGuidancePayload
|| bActionEnabled;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface
{

View file

@ -69,8 +69,8 @@ Current correction call from the source-exposed audit and current execution refr
- the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete
- the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle
- the now-complete deliberate `UHyperTwistTrainingRuntimeLibrary` world-context consumer lane reaches through active generated-mode selector option owned-execution split-phase current-selection merged ordered roster phase-id lookup, normalized-order lookup, and evaluation-order lookup inspect surfaces, so gameplay/Blueprint callers can inspect one retained merged-roster row directly by phase id, normalized order, or evaluation order without scanning the merged roster array or re-running inspect derivation details directly
- the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the run-recap action control inspect surface over the already-retained recap action labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced repeat/dismiss labels and their gating state without scraping widget button text
- the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture
- the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced template/preview action labels and their gating state without scraping widget button text
- the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the guidance preview case navigation control surface pair over the already-derived `Prev Guidance Case` / `Next Guidance Case` labels and enablement posture
- the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it
## Donor portfolio phase taxonomy

View file

@ -0,0 +1,60 @@
# HyperTwist Phase 4 dashboard live template-launch and preview action control inspect surface packet
- bounded Phase `4` dashboard-consumer slice
- date: `2026-05-10`
## Intent
Stay adjacent in the live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action controls directly to gameplay and Blueprint callers.
## Why this packet exists
`UHyperTwistCoachDashboardWidget` already displayed:
- the preferred template-launch action label
- the recommended preview action label
- the follow-up preview action label
- the enablement posture behind those three controls
But that control band still only surfaced through button labels and `SetIsEnabled` state. Callers still had to scrape widget controls to understand whether the dashboard was willing to launch the preferred template, whether recommended guidance preview was currently actionable, and whether follow-up guidance preview was currently blocked by timer or guidance availability.
## What changed
- added `FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface`
- added `FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface`
- added `GetDisplayedPreferredTemplateLaunchActionInspectSurface()`
- added `GetDisplayedRecommendedPreviewActionInspectSurface()`
- added `GetDisplayedFollowUpPreviewActionInspectSurface()`
- retained the exact displayed template-launch, recommended-preview, and follow-up-preview action labels inside the widget so the new surfaces report the same live strings the dashboard is already presenting
- retained the exact displayed template-launch status/detail lines inside the widget so the preferred-template action surface reports the same live template band the dashboard is already presenting
- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no template-selection refactor and no backend continuity changes
## Files
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistTrainingTypes.h`
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistCoachDashboardWidget.h`
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Private\HyperTwistTraining\HyperTwistCoachDashboardWidget.cpp`
- `C:\HyperTwist\docs\HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md`
## Outcome
Gameplay and Blueprint callers can now inspect:
- the exact displayed preferred template-launch action label
- the exact displayed recommended preview action label
- the exact displayed follow-up preview action label
- whether each of those three actions is currently enabled
- the displayed template-launch status/detail band aligned with the preferred template action
- whether recommended or follow-up preview is currently backed by a live deck, a brief fallback, or neither
- which preview lane is currently active behind those controls
without scraping widget button text or reopening launch/preview gating logic.
## Validation
- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln`
- passed with `0 Warning(s), 0 Error(s)`
## Next clean slice
Stay on the same live dashboard consumer lane and expose the guidance preview case navigation control surface pair over the already-derived `Prev Guidance Case` / `Next Guidance Case` labels and enablement posture.