Skip to content

Commit 367d007

Browse files
committed
Fix editor rendered authoring board
Refs #20, #47, #51
1 parent 673d14f commit 367d007

21 files changed

Lines changed: 629 additions & 58 deletions

src/PrompterOne.Shared/AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
- Assistant spotlight suggestions and script graph UI must not use ad-hoc language heuristics for intent or similarity. Use LLM-backed semantics when available, or explicit tokenizer/vector similarity over localized action descriptions and the user query when a non-LLM fallback is needed.
4848
- Script graph editing must support a split source/graph workflow: the user should be able to inspect the graph beside the editor, click a meaningful graph node, and have the owning source range revealed or highlighted without leaving the graph workspace.
4949
- Script graph split mode must expose a user-resizable divider between source and graph panes, and graph view must also support a graph-only workspace mode for focused exploration.
50+
- Script graph canvas must fill the graph panel's remaining workspace in split and graph-only modes; keep the G6 host and rendered canvas synchronized to the panel size after render, resize, and divider changes so the graph is not clipped unless the user intentionally pans or zooms.
5051
- Script graph nodes must explain article/book meaning through themes, terms, entities, characters, concepts, claims, and references; do not let generic layout groupings or unexplained extracted snippets dominate the graph.
5152
- Script graph controls must expose an explicit auto-layout action so users can recover a readable layout after a broken preset, resize, drag, pan, or zoom interaction without rebuilding the document.
5253
- Script graph layout presets must remain readable with PrompterOne's rectangular text nodes; do not add or keep a G6 layout mode without explicit node-size spacing, overlap prevention, or a deterministic fallback that prevents stacked or clipped node labels.
@@ -57,7 +58,11 @@
5758
- Editor dropdown rows must stay compact menu rows, not stacks of tall rounded mini-cards; overlays may feel premium, but menu items still need fast scannable list rhythm.
5859
- Dropdown item content across `PrompterOne.Shared` must align from the left edge as one readable cluster; do not push tags, shortcuts, or meta copy to a fake right column inside menu rows.
5960
- Editor Cards view must behave like a real card board: script cards should support drag-and-drop reordering, expose clear drag handles, and persist the resulting source order instead of relying only on up/down buttons.
61+
- Editor Cards view must use the available workspace as a multi-column scenario board: segments act as swimlanes or grouped sections, and block cards must wrap into multiple visible columns when a segment contains many blocks instead of collapsing into one narrow vertical list.
62+
- Editor Cards drag-and-drop must provide visible interaction feedback, including lifted-card state, drop target/position affordance, and smooth movement/settle animation so reordering reads like a real board interaction.
6063
- Editor Cards view must be theme-aware across light and dark themes; do not hardcode white card/page backgrounds or dark-only text that bypasses shared app color tokens.
64+
- Editor workspace authoring modes must be explicit: `Raw` is the canonical TPS source editor, `Rendered` is the visual editable authoring mode. Do not label the rendered authoring mode as generic cards when the user needs to edit without knowing Markdown or TPS syntax.
65+
- Editor Rendered mode must expose visible cue affordances directly on the rendered blocks and segments, including tone, pace, pause, energy, volume, delivery, pronunciation, and similar TPS intent, so non-technical authors can understand and revise the script without reading raw tags.
6166
- Editor header export actions must use one clear `Export` entry point with format choices inside it; do not render separate top-level buttons for each export format because they read as duplicated actions and crowd the shell chrome.
6267
- Tooltip surfaces across the app must feel intentional and premium: compact, aligned, clearly separated from the background, and positioned so they do not clip, overlap, or awkwardly fight the control that owns them.
6368
- Editor header toolbar tooltips must reveal more slowly than dropdown intent so quick menu interactions open the dropdown without competing hover tooltip paint.

src/PrompterOne.Shared/Contracts/UiTestIds.Editor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ public static class Editor
193193

194194
public static string RenderedBlockDragHandle(int segmentIndex, int blockIndex) => $"editor-rendered-block-drag-handle-{segmentIndex}-{blockIndex}";
195195

196+
public static string RenderedBlockCues(int segmentIndex, int blockIndex) => $"editor-rendered-block-cues-{segmentIndex}-{blockIndex}";
197+
196198
public static string RenderedBlockAttachment(int segmentIndex, int blockIndex, int attachmentIndex) =>
197199
$"editor-rendered-block-attachment-{segmentIndex}-{blockIndex}-{attachmentIndex}";
198200

@@ -208,6 +210,8 @@ public static string RenderedBlockAttachments(int segmentIndex, int blockIndex)
208210

209211
public static string RenderedBlockText(int segmentIndex, int blockIndex) => $"editor-rendered-block-text-{segmentIndex}-{blockIndex}";
210212

213+
public static string RenderedSegmentCues(int segmentIndex) => $"editor-rendered-segment-cues-{segmentIndex}";
214+
211215
public static string RenderedSegment(int segmentIndex) => $"editor-rendered-segment-{segmentIndex}";
212216

213217
public static string SplitResultItem(int index) => $"editor-split-result-item-{index}";

src/PrompterOne.Shared/Editor/Components/EditorRenderedTextView.razor

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
<div class="editor-rendered-view"
1111
@ref="_rootElement"
12+
data-rendered-authoring-mode="visual"
1213
data-test="@UiTestIds.Editor.RenderedView">
1314
<div class="editor-rendered-strip" data-test="@UiTestIds.Editor.RenderedStrip">
1415
@if (Segments.Count == 0)
@@ -39,6 +40,22 @@
3940
<span>@segment.DurationLabel</span>
4041
}
4142
</div>
43+
@if (segment.Cues.Count > 0)
44+
{
45+
<div class="editor-rendered-cue-row editor-rendered-cue-row--segment"
46+
aria-label="@segment.Name"
47+
data-test="@UiTestIds.Editor.RenderedSegmentCues(segment.Index)">
48+
@foreach (var cue in segment.Cues)
49+
{
50+
<span class="editor-rendered-cue-chip"
51+
data-cue-kind="@cue.Kind"
52+
data-cue-tone="@cue.Tone">
53+
<span class="editor-rendered-cue-icon" aria-hidden="true">@cue.Icon</span>
54+
<span class="editor-rendered-cue-label">@cue.Label</span>
55+
</span>
56+
}
57+
</div>
58+
}
4259

4360
@foreach (var block in segment.Blocks)
4461
{
@@ -76,6 +93,22 @@
7693
</label>
7794
</span>
7895
</div>
96+
@if (block.Cues.Count > 0)
97+
{
98+
<div class="editor-rendered-cue-row"
99+
aria-label="@block.Name"
100+
data-test="@UiTestIds.Editor.RenderedBlockCues(block.SegmentIndex, block.BlockIndex)">
101+
@foreach (var cue in block.Cues)
102+
{
103+
<span class="editor-rendered-cue-chip"
104+
data-cue-kind="@cue.Kind"
105+
data-cue-tone="@cue.Tone">
106+
<span class="editor-rendered-cue-icon" aria-hidden="true">@cue.Icon</span>
107+
<span class="editor-rendered-cue-label">@cue.Label</span>
108+
</span>
109+
}
110+
</div>
111+
}
79112
<textarea class="editor-rendered-text"
80113
aria-label="@block.Name"
81114
data-test="@UiTestIds.Editor.RenderedBlockText(block.SegmentIndex, block.BlockIndex)"

src/PrompterOne.Shared/Editor/Components/EditorRenderedTextView.razor.css

Lines changed: 158 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
--editor-rendered-control-hover: color-mix(in srgb, var(--gold-09) 70%, var(--bg-4));
88
--editor-rendered-drop: color-mix(in srgb, var(--gold-10) 22%, transparent);
99
--editor-rendered-drop-line: color-mix(in srgb, var(--gold) 72%, transparent);
10+
--editor-rendered-lane: color-mix(in srgb, var(--bg-3) 54%, transparent);
11+
--editor-rendered-lift-shadow: 0 22px 48px color-mix(in srgb, var(--bg-deep) 46%, transparent);
1012
display: flex;
1113
flex: 1 1 auto;
1214
min-width: 0;
@@ -20,27 +22,30 @@
2022

2123
.editor-rendered-strip {
2224
display: grid;
23-
grid-auto-columns: minmax(280px, 360px);
24-
grid-auto-flow: column;
25+
grid-template-columns: minmax(0, 1fr);
26+
align-content: start;
2527
gap: 12px;
2628
min-width: 0;
2729
width: 100%;
28-
overflow-x: auto;
29-
overflow-y: hidden;
30+
overflow-x: hidden;
31+
overflow-y: auto;
3032
padding: 12px;
3133
scrollbar-color: color-mix(in srgb, var(--text-4) 42%, transparent) transparent;
3234
}
3335

3436
.editor-rendered-card {
35-
display: flex;
36-
flex-direction: column;
37+
display: grid;
38+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
39+
align-content: start;
3740
min-width: 0;
38-
min-height: 100%;
39-
gap: 18px;
40-
padding: 34px 24px 28px;
41+
min-height: 0;
42+
gap: 12px;
43+
padding: 18px;
4144
border: 1px solid var(--editor-rendered-card-border);
4245
border-radius: 8px;
43-
background: var(--editor-rendered-card);
46+
background:
47+
linear-gradient(180deg, color-mix(in srgb, var(--editor-rendered-lane) 86%, transparent), transparent 190px),
48+
var(--editor-rendered-card);
4449
box-shadow: var(--editor-rendered-card-shadow), var(--glass-inset);
4550
}
4651

@@ -50,6 +55,7 @@
5055

5156
.editor-rendered-card-header {
5257
display: flex;
58+
grid-column: 1 / -1;
5359
align-items: flex-start;
5460
justify-content: space-between;
5561
gap: 12px;
@@ -74,6 +80,7 @@
7480
}
7581

7682
.editor-rendered-segment-title {
83+
grid-column: 1 / -1;
7784
color: var(--text-1);
7885
font-size: 25px;
7986
font-weight: 760;
@@ -83,6 +90,7 @@
8390

8491
.editor-rendered-segment-detail {
8592
display: flex;
93+
grid-column: 1 / -1;
8694
flex-wrap: wrap;
8795
gap: 8px;
8896
min-height: 18px;
@@ -93,11 +101,99 @@
93101
text-transform: uppercase;
94102
}
95103

104+
.editor-rendered-cue-row {
105+
display: flex;
106+
flex-wrap: wrap;
107+
gap: 6px;
108+
min-width: 0;
109+
}
110+
111+
.editor-rendered-cue-row--segment {
112+
grid-column: 1 / -1;
113+
}
114+
115+
.editor-rendered-cue-chip {
116+
display: inline-flex;
117+
align-items: center;
118+
max-width: 100%;
119+
min-height: 24px;
120+
gap: 5px;
121+
padding: 3px 7px;
122+
border: 1px solid color-mix(in srgb, var(--text-4) 19%, transparent);
123+
border-radius: 999px;
124+
background: color-mix(in srgb, var(--bg-1) 42%, transparent);
125+
color: var(--text-3);
126+
font-size: 11px;
127+
font-weight: 740;
128+
line-height: 1.15;
129+
}
130+
131+
.editor-rendered-cue-icon {
132+
display: inline-grid;
133+
min-width: 13px;
134+
place-items: center;
135+
color: var(--text-2);
136+
font-family: var(--font-mono);
137+
font-size: 11px;
138+
font-weight: 800;
139+
line-height: 1;
140+
}
141+
142+
.editor-rendered-cue-label {
143+
min-width: 0;
144+
overflow: hidden;
145+
text-overflow: ellipsis;
146+
white-space: nowrap;
147+
}
148+
149+
.editor-rendered-cue-chip[data-cue-kind="emotion"] {
150+
border-color: color-mix(in srgb, #d8ab65 38%, transparent);
151+
background: color-mix(in srgb, #d8ab65 12%, var(--bg-1));
152+
}
153+
154+
.editor-rendered-cue-chip[data-cue-tone="warm"],
155+
.editor-rendered-cue-chip[data-cue-tone="happy"] {
156+
border-color: color-mix(in srgb, #f1b85f 45%, transparent);
157+
color: color-mix(in srgb, #f5d7a5 74%, var(--text-1));
158+
}
159+
160+
.editor-rendered-cue-chip[data-cue-tone="urgent"] {
161+
border-color: color-mix(in srgb, #ff756f 46%, transparent);
162+
color: color-mix(in srgb, #ffc0b9 70%, var(--text-1));
163+
}
164+
165+
.editor-rendered-cue-chip[data-cue-tone="calm"],
166+
.editor-rendered-cue-chip[data-cue-tone="focused"] {
167+
border-color: color-mix(in srgb, #7db7ff 40%, transparent);
168+
color: color-mix(in srgb, #b9d8ff 68%, var(--text-1));
169+
}
170+
171+
.editor-rendered-cue-chip[data-cue-kind="pace"],
172+
.editor-rendered-cue-chip[data-cue-kind="timing"],
173+
.editor-rendered-cue-chip[data-cue-kind="pause"] {
174+
border-color: color-mix(in srgb, var(--gold) 34%, transparent);
175+
background: color-mix(in srgb, var(--gold-09) 32%, transparent);
176+
}
177+
178+
.editor-rendered-cue-chip[data-cue-kind="energy"],
179+
.editor-rendered-cue-chip[data-cue-kind="delivery"] {
180+
border-color: color-mix(in srgb, #8cd68f 34%, transparent);
181+
color: color-mix(in srgb, #bfe9c1 68%, var(--text-1));
182+
}
183+
184+
.editor-rendered-cue-chip[data-cue-kind="pronunciation"],
185+
.editor-rendered-cue-chip[data-cue-kind="phonetic"],
186+
.editor-rendered-cue-chip[data-cue-kind="stress"] {
187+
border-color: color-mix(in srgb, #a89cff 38%, transparent);
188+
color: color-mix(in srgb, #d5d0ff 68%, var(--text-1));
189+
}
190+
96191
.editor-rendered-block {
97192
display: flex;
98193
flex-direction: column;
99194
gap: 10px;
100195
min-width: 0;
196+
min-height: 244px;
101197
padding: 9px 10px 10px;
102198
border: 1px solid color-mix(in srgb, var(--text-4) 13%, transparent);
103199
border-radius: 8px;
@@ -108,7 +204,9 @@
108204
border-color .16s ease,
109205
box-shadow .16s ease,
110206
opacity .16s ease,
111-
transform .16s ease;
207+
transform .16s ease,
208+
filter .16s ease;
209+
will-change: transform;
112210
}
113211

114212
.editor-rendered-block:hover {
@@ -117,15 +215,55 @@
117215
}
118216

119217
.editor-rendered-block--dragging {
120-
opacity: .62;
121-
transform: rotate(.25deg) scale(.99);
218+
opacity: .48;
219+
transform: scale(.985);
122220
cursor: grabbing;
221+
filter: saturate(.84);
123222
}
124223

125224
.editor-rendered-block--drop-target {
126225
background: var(--editor-rendered-drop);
127226
border-color: var(--editor-rendered-drop-line);
128-
box-shadow: inset 3px 0 0 var(--editor-rendered-drop-line);
227+
box-shadow:
228+
inset 0 0 0 1px var(--editor-rendered-drop-line),
229+
var(--editor-rendered-card-shadow);
230+
transform: translateY(-2px);
231+
}
232+
233+
.editor-rendered-block--drop-before,
234+
.editor-rendered-block--drop-after {
235+
position: relative;
236+
}
237+
238+
.editor-rendered-block--drop-before::before,
239+
.editor-rendered-block--drop-after::after {
240+
position: absolute;
241+
z-index: 2;
242+
right: 12px;
243+
left: 12px;
244+
height: 3px;
245+
content: "";
246+
border-radius: 999px;
247+
background: var(--editor-rendered-drop-line);
248+
box-shadow: 0 0 18px color-mix(in srgb, var(--gold) 38%, transparent);
249+
}
250+
251+
.editor-rendered-block--drop-before::before {
252+
top: -7px;
253+
}
254+
255+
.editor-rendered-block--drop-after::after {
256+
bottom: -7px;
257+
}
258+
259+
.editor-rendered-drag-ghost {
260+
position: fixed;
261+
z-index: 1000;
262+
width: min(330px, calc(100vw - 32px));
263+
pointer-events: none;
264+
opacity: .92;
265+
transform: translate3d(-50%, -18px, 0) rotate(.45deg);
266+
box-shadow: var(--editor-rendered-lift-shadow);
129267
}
130268

131269
.editor-rendered-block-head {
@@ -222,7 +360,7 @@
222360

223361
.editor-rendered-text {
224362
width: 100%;
225-
min-height: 150px;
363+
min-height: 126px;
226364
padding: 0;
227365
resize: vertical;
228366
border: 0;
@@ -311,14 +449,17 @@ body.theme-light .editor-rendered-view {
311449
--editor-rendered-card-shadow: 0 14px 28px color-mix(in srgb, var(--bg-deep) 15%, transparent);
312450
--editor-rendered-control: color-mix(in srgb, var(--bg-1) 86%, transparent);
313451
--editor-rendered-control-hover: color-mix(in srgb, var(--gold-09) 48%, var(--bg-1));
452+
--editor-rendered-lane: color-mix(in srgb, var(--bg-2) 70%, transparent);
453+
--editor-rendered-lift-shadow: 0 22px 44px color-mix(in srgb, var(--bg-deep) 22%, transparent);
314454
}
315455

316456
@media (max-width: 760px) {
317457
.editor-rendered-strip {
318-
grid-auto-columns: minmax(240px, 82vw);
458+
padding: 10px;
319459
}
320460

321461
.editor-rendered-card {
322-
padding: 28px 18px 22px;
462+
grid-template-columns: minmax(0, 1fr);
463+
padding: 14px;
323464
}
324465
}

src/PrompterOne.Shared/Editor/Components/EditorViewModels.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public sealed record EditorRenderedSegmentViewModel(
5353
string EmotionLabel,
5454
string TargetWpmLabel,
5555
string DurationLabel,
56+
IReadOnlyList<EditorRenderedCueViewModel> Cues,
5657
IReadOnlyList<EditorRenderedBlockViewModel> Blocks);
5758

5859
public sealed record EditorRenderedBlockViewModel(
@@ -64,8 +65,15 @@ public sealed record EditorRenderedBlockViewModel(
6465
string EmotionLabel,
6566
string TargetWpmLabel,
6667
string Text,
68+
IReadOnlyList<EditorRenderedCueViewModel> Cues,
6769
IReadOnlyList<EditorBlockAttachmentViewModel> Attachments);
6870

71+
public sealed record EditorRenderedCueViewModel(
72+
string Kind,
73+
string Icon,
74+
string Label,
75+
string Tone);
76+
6977
public sealed record EditorBlockAttachmentViewModel(
7078
string Id,
7179
string FileName,

0 commit comments

Comments
 (0)