Skip to content

Commit 2b9a3df

Browse files
committed
witdh for teleprompter
1 parent 9aac463 commit 2b9a3df

19 files changed

+123
-78
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Rule format:
9393
- Product localization must be complete across all supported UI languages: audit hardcoded user-facing strings with an explicit inventory file, move them into shared localization catalogs, and include tooltip text in the same localization pass instead of leaving tooltip copy or chrome labels hardcoded.
9494
- PrompterOne must ship a first-run onboarding flow that explains the product basics, TPS, RSVP, Editor, Learn, Teleprompter, and Go Live in-app; the walkthrough must appear on first launch until the user explicitly completes or dismisses it, persist that choice locally, and be fully localized across all supported UI languages.
9595
- The onboarding flow must include a dedicated TPS explainer step or page, separate from the generic editor step, that tells users what TPS is, why it exists, and how PrompterOne uses it.
96+
- The dedicated TPS onboarding step must explain TPS in concrete beginner terms: what the format is, why it exists, how PrompterOne uses it across Editor/Learn/Teleprompter/Go Live, and where users can continue with the official TPS site or glossary.
9697
- Script discovery and authoring surfaces must support real search by script name and script content; Library/script pages and editor flows must not force manual browsing when the user needs to find files or text inside files.
9798

9899
## Rules to Follow (Mandatory)

src/PrompterOne.Shared/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
- Editor TPS menus in `PrompterOne.Shared` must use one spec-driven grouping and command inventory across top toolbar, floating toolbar, and Monaco authoring help; if a TPS tag or header pattern is supported, every relevant editor command surface must expose it coherently.
4747
- `PrompterOne.Shared` must provide a localized first-run onboarding flow that walks new users through PrompterOne, TPS, RSVP, Editor, Learn, Teleprompter, and Go Live, and it must persist completion or dismissal in browser-owned settings.
4848
- `PrompterOne.Shared` onboarding must give TPS its own dedicated explainer step or page, separate from the editor overview, so new users understand what TPS is and why PrompterOne centers it.
49+
- The TPS onboarding content in `PrompterOne.Shared` must speak to beginners directly: define TPS, explain why plain-text teleprompter markup matters, show how PrompterOne carries that source into rehearsal/reading/live flows, and point users to official TPS documentation or glossary paths for deeper orientation.
4950
- Library and editor routed surfaces in `PrompterOne.Shared` must expose real search affordances for script names and script content instead of relying only on manual browsing.
5051
- Teleprompter routed surfaces in `PrompterOne.Shared` must expose a direct in-reader text-size control, owned by the reader UI itself, so operators can tune readability during playback without leaving the route.
5152
- Teleprompter width controls in `PrompterOne.Shared` must scale relative to the active viewport or screen size and persist that adaptive ratio; do not clamp the readable lane around a fixed pixel-only desktop maximum.

src/PrompterOne.Shared/AppShell/Layout/MainLayout.Onboarding.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ private int ResolveOnboardingStepIndex(string uri)
104104
}
105105

106106
var currentRoute = GetNormalizedAppRoute(uri);
107+
if (_onboardingStepIndex >= 0 &&
108+
_onboardingStepIndex < _onboardingSteps.Count &&
109+
string.Equals(_onboardingSteps[_onboardingStepIndex].Route, currentRoute, StringComparison.Ordinal))
110+
{
111+
return _onboardingStepIndex;
112+
}
113+
107114
var routeIndex = _onboardingSteps
108115
.Select((step, index) => (step.Route, index))
109116
.FirstOrDefault(candidate => string.Equals(candidate.Route, currentRoute, StringComparison.Ordinal))

src/PrompterOne.Shared/Components/AppOnboardingCatalog.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,22 @@ public static IReadOnlyList<AppOnboardingStepDescriptor> Build(string scriptId)
1919
UiTextKey.OnboardingWelcomeBulletModes,
2020
UiTextKey.OnboardingWelcomeBulletLocal
2121
],
22-
UiTextKey.OnboardingOpenEditor,
22+
UiTextKey.OnboardingOpenTps,
2323
AppRoutes.Library),
24+
CreateStep(
25+
AppOnboardingStepId.Tps,
26+
UiIconKind.CodeArrows,
27+
UiTextKey.OnboardingStepTps,
28+
UiTextKey.OnboardingTpsEyebrow,
29+
UiTextKey.OnboardingTpsTitle,
30+
UiTextKey.OnboardingTpsBody,
31+
[
32+
UiTextKey.OnboardingTpsBulletFormat,
33+
UiTextKey.OnboardingTpsBulletRuntime,
34+
UiTextKey.OnboardingTpsBulletGlossary
35+
],
36+
UiTextKey.OnboardingOpenEditor,
37+
AppRoutes.EditorWithId(scriptId)),
2438
CreateStep(
2539
AppOnboardingStepId.Editor,
2640
UiIconKind.DocumentLines,

src/PrompterOne.Shared/Components/AppOnboardingStepId.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace PrompterOne.Shared.Components;
33
public enum AppOnboardingStepId
44
{
55
Library = 0,
6+
Tps,
67
Editor,
78
Learn,
89
Teleprompter,

src/PrompterOne.Shared/Localization/UiTextKey.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public enum UiTextKey
3232
OnboardingDismiss,
3333
OnboardingBack,
3434
OnboardingRestartTour,
35+
OnboardingOpenTps,
3536
OnboardingOpenEditor,
3637
OnboardingOpenLearn,
3738
OnboardingOpenTeleprompter,
@@ -40,6 +41,7 @@ public enum UiTextKey
4041
OnboardingReopenBody,
4142
OnboardingFinish,
4243
OnboardingStepLibrary,
44+
OnboardingStepTps,
4345
OnboardingStepEditor,
4446
OnboardingStepLearn,
4547
OnboardingStepTeleprompter,
@@ -50,6 +52,12 @@ public enum UiTextKey
5052
OnboardingWelcomeBulletSearch,
5153
OnboardingWelcomeBulletModes,
5254
OnboardingWelcomeBulletLocal,
55+
OnboardingTpsEyebrow,
56+
OnboardingTpsTitle,
57+
OnboardingTpsBody,
58+
OnboardingTpsBulletFormat,
59+
OnboardingTpsBulletRuntime,
60+
OnboardingTpsBulletGlossary,
5361
OnboardingEditorEyebrow,
5462
OnboardingEditorTitle,
5563
OnboardingEditorBody,

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderPlayback.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ private async Task HandleReaderFocalPointInputAsync(ChangeEventArgs args)
8484

8585
private async Task HandleReaderTextWidthInputAsync(ChangeEventArgs args)
8686
{
87-
_readerTextWidth = ParseReaderControlValue(
87+
_readerTextWidthPercent = ParseReaderControlValue(
8888
args.Value,
89-
ReaderMinTextWidth,
90-
ReaderMaxTextWidth,
91-
_readerTextWidth);
89+
ReaderMinTextWidthPercent,
90+
ReaderMaxTextWidthPercent,
91+
_readerTextWidthPercent);
9292
RequestReaderAlignment();
9393
await PersistCurrentReaderLayoutAsync();
9494
_areWidthGuidesActive = true;

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderPreferences.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private async Task PersistCurrentReaderLayoutAsync()
1010
await PersistReaderSettingsAsync(currentSettings => currentSettings with
1111
{
1212
FontScale = BuildReaderFontScale(_readerFontSize),
13-
TextWidth = BuildReaderTextWidthRatio(_readerTextWidth),
13+
TextWidth = BuildReaderTextWidthRatio(_readerTextWidthPercent),
1414
FocalPointPercent = _readerFocalPointPercent,
1515
MirrorText = _isReaderMirrorHorizontal,
1616
MirrorVertical = _isReaderMirrorVertical,
@@ -41,6 +41,6 @@ private async Task PersistReaderSettingsAsync(Func<ReaderSettings, ReaderSetting
4141
private static double BuildReaderFontScale(int fontSize) =>
4242
Math.Round(fontSize / (double)DefaultReaderFontSize, 2, MidpointRounding.AwayFromZero);
4343

44-
private static double BuildReaderTextWidthRatio(int textWidth) =>
45-
Math.Round(textWidth / (double)ReaderMaxTextWidth, 4, MidpointRounding.AwayFromZero);
44+
private static double BuildReaderTextWidthRatio(int textWidthPercent) =>
45+
Math.Round(textWidthPercent / (double)ReaderMaxTextWidthPercent, 4, MidpointRounding.AwayFromZero);
4646
}

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.ReaderRendering.cs

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ public partial class TeleprompterPage
1313
private const string ReaderCardNextCssClass = "rd-card-next";
1414
private const string ReaderCardPreviousCssClass = "rd-card-prev";
1515
private const string ReaderAlignmentButtonCssClass = "rd-align-btn";
16-
private const int ReaderCenterOpticalInsetPixels = 0;
17-
private const int ReaderComfortableContentMaxWidth = 1080;
18-
private const int ReaderComfortablePortraitContentMaxWidth = 760;
19-
private const string ReaderContentMaxWidthVariableName = "--rd-content-max-width";
16+
private const double ReaderContentWidthScaleFactor = 0.92d;
2017
private const string ReaderControlButtonCssClass = "rd-ctrl-btn";
2118
private const string ReaderCountdownCssClass = "rd-countdown";
2219
private const string ReaderGradientCssClass = "rd-gradient";
@@ -28,15 +25,18 @@ public partial class TeleprompterPage
2825
private const string ReaderHorizontalGuideCssClass = "rd-guide-h";
2926
private const string ReaderMirrorButtonCssClass = "rd-mirror-btn";
3027
private const string ReaderMirrorHorizontalTransform = "scaleX(-1)";
31-
private const string ReaderOpticalInsetVariableName = "--rd-text-optical-inset";
3228
private const string ReaderOrientationLandscapeValue = "landscape";
3329
private const string ReaderOrientationPortraitTransform = "rotate(90deg)";
3430
private const string ReaderOrientationPortraitValue = "portrait";
31+
private const string ReaderPercentSuffix = "%";
3532
private const string ReaderTextAlignmentCenterValue = "center";
3633
private const string ReaderTextAlignmentJustifyValue = "justify";
3734
private const string ReaderTextAlignmentLeftValue = "left";
3835
private const string ReaderTextAlignmentRightValue = "right";
3936
private const string ReaderMirrorTransformOrigin = "center center";
37+
private const string ReaderStageContentScaleVariableName = "--rd-stage-content-scale";
38+
private const string ReaderStageShellWidthVariableName = "--rd-stage-shell-width";
39+
private const string ReaderStageWidthScaleVariableName = "--rd-stage-width-scale";
4040
private const string ReaderMirrorVerticalTransform = "scaleY(-1)";
4141
private const string ReaderVerticalGuideCssClass = "rd-guide-v";
4242
private const string ReaderVerticalGuideLeftCssClass = "rd-guide-v-l";
@@ -130,27 +130,28 @@ private string BuildFocalGuideCssClass() =>
130130
private string BuildFocalGuideStyle() =>
131131
$"top:{_readerFocalPointPercent.ToString(CultureInfo.InvariantCulture)}%;";
132132

133+
private string BuildReaderStageStyle()
134+
{
135+
var widthScale = BuildReaderWidthScale(_readerTextWidthPercent).ToString("0.####", CultureInfo.InvariantCulture);
136+
var contentScale = BuildReaderContentScale(_readerTextWidthPercent).ToString("0.####", CultureInfo.InvariantCulture);
137+
return $"{ReaderStageWidthScaleVariableName}:{widthScale};{ReaderStageContentScaleVariableName}:{contentScale};";
138+
}
139+
133140
private string BuildWidthGuideCssClass(bool isLeft) =>
134141
BuildClassList(
135142
ReaderVerticalGuideCssClass,
136143
isLeft ? ReaderVerticalGuideLeftCssClass : ReaderVerticalGuideRightCssClass,
137144
_areWidthGuidesActive ? ActiveCssClass : null);
138145

139-
private string BuildWidthGuideStyle(bool isLeft)
140-
{
141-
var halfWidth = (_readerTextWidth / 2d).ToString("0.##", CultureInfo.InvariantCulture);
142-
var sign = isLeft ? '-' : '+';
143-
return $"left:calc(50% {sign} {halfWidth}px);";
144-
}
146+
private static string BuildWidthGuideStyle(bool isLeft) =>
147+
isLeft
148+
? $"left:calc(50% - (var({ReaderStageShellWidthVariableName}) / 2));"
149+
: $"left:calc(50% + (var({ReaderStageShellWidthVariableName}) / 2));";
145150

146151
private string BuildClusterWrapStyle()
147152
{
148-
var contentMaxWidth = ResolveReaderContentMaxWidth();
149153
var styleParts = new List<string>
150154
{
151-
$"max-width:{_readerTextWidth.ToString(CultureInfo.InvariantCulture)}px",
152-
$"{ReaderContentMaxWidthVariableName}:{contentMaxWidth.ToString(CultureInfo.InvariantCulture)}px",
153-
$"{ReaderOpticalInsetVariableName}:{ResolveReaderTextOpticalInset(contentMaxWidth).ToString(CultureInfo.InvariantCulture)}px",
154155
$"--rd-font-size:{_readerFontSize.ToString(CultureInfo.InvariantCulture)}px"
155156
};
156157
var readerTransform = BuildReaderTransform();
@@ -164,23 +165,14 @@ private string BuildClusterWrapStyle()
164165
return string.Join(';', styleParts) + ';';
165166
}
166167

167-
private int ResolveReaderContentMaxWidth()
168-
{
169-
var contentMaxWidth = Math.Min(_readerTextWidth, ReaderComfortableContentMaxWidth);
170-
return _readerTextOrientation == ReaderTextOrientation.Portrait
171-
? Math.Min(contentMaxWidth, ReaderComfortablePortraitContentMaxWidth)
172-
: contentMaxWidth;
173-
}
168+
private string BuildReaderWidthLabel() =>
169+
$"{_readerTextWidthPercent.ToString(CultureInfo.InvariantCulture)}{ReaderPercentSuffix}";
174170

175-
private int ResolveReaderTextOpticalInset(int contentMaxWidth)
176-
{
177-
if (_readerTextAlignment is ReaderTextAlignment.Center or ReaderTextAlignment.Justify)
178-
{
179-
return ReaderCenterOpticalInsetPixels;
180-
}
171+
private static double BuildReaderWidthScale(int textWidthPercent) =>
172+
Math.Clamp(textWidthPercent / (double)ReaderMaxTextWidthPercent, ReaderMinTextWidthPercent / 100d, 1d);
181173

182-
return Math.Clamp((int)Math.Round(contentMaxWidth * 0.06d, MidpointRounding.AwayFromZero), 28, 64);
183-
}
174+
private static double BuildReaderContentScale(int textWidthPercent) =>
175+
Math.Round(BuildReaderWidthScale(textWidthPercent) * ReaderContentWidthScaleFactor, 4, MidpointRounding.AwayFromZero);
184176

185177
private string BuildReaderTransform()
186178
{

src/PrompterOne.Shared/Teleprompter/Pages/TeleprompterPage.razor

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -232,15 +232,15 @@
232232
<input type="range"
233233
class="rd-vslider"
234234
id="rd-width-slider"
235-
min="@ReaderMinTextWidth"
236-
max="@ReaderMaxTextWidth"
237-
value="@_readerTextWidth"
235+
min="@ReaderMinTextWidthPercent"
236+
max="@ReaderMaxTextWidthPercent"
237+
value="@_readerTextWidthPercent"
238238
@oninput="HandleReaderTextWidthInputAsync"
239239
@onkeydown:stopPropagation="true"
240240
aria-label="@ReaderWidthSliderTitle"
241241
data-testid="@UiTestIds.Teleprompter.WidthSlider"
242242
data-test-id="@UiTestIds.Teleprompter.WidthSlider">
243-
<span class="rd-slider-val" id="@UiDomIds.Teleprompter.WidthValue">@_readerTextWidth</span>
243+
<span class="rd-slider-val" id="@UiDomIds.Teleprompter.WidthValue">@BuildReaderWidthLabel()</span>
244244
</div>
245245
<span id="@BuildRailTooltipDomId(UiTestIds.Teleprompter.AlignmentTooltipWidthKey)"
246246
class="@BuildRailTooltipCssClass(placeOnRightSide: false)"
@@ -252,7 +252,8 @@
252252

253253
<div class="rd-stage"
254254
id="@UiDomIds.Teleprompter.Stage"
255-
data-testid="@UiTestIds.Teleprompter.Stage">
255+
data-testid="@UiTestIds.Teleprompter.Stage"
256+
style="@BuildReaderStageStyle()">
256257
<div class="rd-center-guide"></div>
257258
<div class="@BuildFocalGuideCssClass()"
258259
id="@UiDomIds.Teleprompter.FocalGuide"

0 commit comments

Comments
 (0)