fix: close presentation visual pass review findings

This commit is contained in:
lda
2026-07-12 21:46:55 +07:00 Verified
parent 26efa88817
commit 1602f6946f
6 changed files with 178 additions and 106 deletions
+2 -2
View File
@@ -303,13 +303,13 @@ separate activity after these surfaces are stable.
[`presentation demo-chrome ownership`](superpowers/specs/2026-07-12-demo-chrome-ownership-design.md).
Implementation:
[`presentation demo-chrome ownership plan`](historical/superpowers/plans/2026-07-12-presentation-demo-chrome-ownership.md).
7. **Visual scale and color pass:** remove unwanted blue from Scenes 2 and 14,
7. **Completed: visual scale and color pass:** removed unwanted blue from Scenes 2 and 14,
shorten Scene 2's two-column composition, enlarge the focal diagrams in
Scenes 7, 9, 13, and 14, separate Scene 7 Validate from Repair visuals, and
improve Scene 1 title-box padding and contrast. Design:
[`presentation visual scale and color pass`](superpowers/specs/2026-07-12-presentation-visual-scale-color-pass-design.md).
Implementation:
[`presentation visual scale and color pass plan`](superpowers/plans/2026-07-12-presentation-visual-scale-color-pass.md).
[`presentation visual scale and color pass plan`](historical/superpowers/plans/2026-07-12-presentation-visual-scale-color-pass.md).
8. **Rehearsal gate:** run all 14 scenes at 16:9 and 4:3, verify the Scene 8 →
Scene 11 → Scene 12 path in live and replay modes, then review the story
flow, coverage, and ordering separately.
@@ -46,16 +46,16 @@ Files likely involved:
Steps:
- [ ] Add focused failing assertions for the title, problem, authoring,
- [x] Add focused failing assertions for the title, problem, authoring,
lifecycle, evaluation, and conclusion surfaces.
- [ ] Assert semantic markers for the dominant visual and active beat rather
- [x] Assert semantic markers for the dominant visual and active beat rather
than fragile CSS pixel values.
- [ ] Add regression assertions that these editorial scenes do not gain demo
- [x] Add regression assertions that these editorial scenes do not gain demo
footer controls, live target badges, or unexpected chat chrome.
- [ ] Add CSS contract checks only for durable selectors/tokens that must not
- [x] Add CSS contract checks only for durable selectors/tokens that must not
regress, such as the neutral editorial surface and scene-specific layout
markers.
- [ ] Run the focused tests and confirm the new assertions fail for the
- [x] Run the focused tests and confirm the new assertions fail for the
intended reasons.
Do not encode exact color strings or arbitrary pixel coordinates in tests.
@@ -73,17 +73,17 @@ Likely files:
Steps:
- [ ] Make the Scene 1 title beat title-first: one primary title treatment,
- [x] Make the Scene 1 title beat title-first: one primary title treatment,
more internal padding, stronger text contrast, and no duplicate framing.
- [ ] Preserve the later Scene 1 substrate/decomposition content without
- [x] Preserve the later Scene 1 substrate/decomposition content without
forcing it into the title beat's dimensions.
- [ ] Keep Scene 2's transcript in normal chat reading order: user request,
- [x] Keep Scene 2's transcript in normal chat reading order: user request,
agent/tool activity, observation, and answer.
- [ ] Shorten the two Scene 2 columns so the right automation explanation does
- [x] Shorten the two Scene 2 columns so the right automation explanation does
not become a tall dashboard card.
- [ ] Remove decorative blue from the editorial Scene 2 surface. Keep only
- [x] Remove decorative blue from the editorial Scene 2 surface. Keep only
state or focus colors that communicate something specific.
- [ ] Verify both Scene 2 beats and the narrow canvas before moving on.
- [x] Verify both Scene 2 beats and the narrow canvas before moving on.
If the opening components need shared layout, extract a small semantic wrapper;
do not create another generic card primitive.
@@ -101,16 +101,16 @@ Likely files:
Steps:
- [ ] Identify the existing authoring loop and its active beat mapping before
- [x] Identify the existing authoring loop and its active beat mapping before
changing markup.
- [ ] Give the active phase a larger, readable visual while keeping the full
- [x] Give the active phase a larger, readable visual while keeping the full
loop as a compact orientation rail.
- [ ] Make `validate` show a diagnostic/contract-checking visual.
- [ ] Make `repair` show a correction/revision visual that is structurally
- [x] Make `validate` show a diagnostic/contract-checking visual.
- [x] Make `repair` show a correction/revision visual that is structurally
distinct from `validate`.
- [ ] Preserve factual command labels and existing icons; do not add invented
- [x] Preserve factual command labels and existing icons; do not add invented
tool output.
- [ ] Add tests proving the active phase and the Validate/Repair visual
- [x] Add tests proving the active phase and the Validate/Repair visual
distinction.
Avoid adding five equally sized cards. The point is a dominant phase plus a
@@ -129,14 +129,14 @@ Likely files:
Steps:
- [ ] Identify which surface is primary for each lifecycle beat: discovery,
- [x] Identify which surface is primary for each lifecycle beat: discovery,
draft, validate, artifact, and deployment.
- [ ] Give the lifecycle visual most of the available stage area.
- [ ] Keep the prepared assistant as supporting context, not a second equal
- [x] Give the lifecycle visual most of the available stage area.
- [x] Keep the prepared assistant as supporting context, not a second equal
hero surface.
- [ ] Preserve the current footer rail ownership and avoid adding another run
- [x] Preserve the current footer rail ownership and avoid adding another run
or live-status control.
- [ ] Add layout/beat tests that verify the primary surface remains present and
- [x] Add layout/beat tests that verify the primary surface remains present and
the scene does not regress to the old crowded composition.
Do not fold Scene 9 into the real live execution slice. This task is visual
@@ -155,15 +155,15 @@ Likely files:
Steps:
- [ ] Choose one dominant evaluation visual per beat: cohort, validity, and
- [x] Choose one dominant evaluation visual per beat: cohort, validity, and
findings should not be three near-identical text panels.
- [ ] Increase the scale of the relevant diagram/stat treatment while keeping
- [x] Increase the scale of the relevant diagram/stat treatment while keeping
methodology limits legible as support.
- [ ] Give the conclusion one clear contribution/limits visual rather than a
- [x] Give the conclusion one clear contribution/limits visual rather than a
dense summary wall.
- [ ] Remove unnecessary blue from Scene 14 while preserving readable contrast.
- [ ] Keep the Questions beat usable and free of accidental demo chrome.
- [ ] Add tests for dominant-beat markers and conclusion surface behavior.
- [x] Remove unnecessary blue from Scene 14 while preserving readable contrast.
- [x] Keep the Questions beat usable and free of accidental demo chrome.
- [x] Add tests for dominant-beat markers and conclusion surface behavior.
Do not invent new evaluation numbers or claims. All visual labels must come
from the storyboard or existing factual projections.
@@ -174,21 +174,21 @@ from the storyboard or existing factual projections.
Steps:
- [ ] Run focused presentation tests after each task.
- [ ] Run the full web test suite:
- [x] Run focused presentation tests after each task.
- [x] Run the full web test suite:
`pnpm --dir web test`.
- [ ] Run typecheck:
- [x] Run typecheck:
`pnpm --dir web typecheck`.
- [ ] Run build:
- [x] Run build:
`pnpm --dir web build`.
- [ ] Capture screenshots for the representative routes at `1280x720` and
- [x] Capture screenshots for the representative routes at `1280x720` and
`1024x768`.
- [ ] Confirm no accidental outer scroll, clipped title text, unreadable
- [x] Confirm no accidental outer scroll, clipped title text, unreadable
diagram labels, or duplicate chrome.
- [ ] Re-run `git diff --check`.
- [ ] Run the two-axis review before declaring completion.
- [ ] Update `docs/current_roadmap.md` with the completed plan link.
- [ ] Move this plan to
- [x] Re-run `git diff --check`.
- [x] Run the two-axis review before declaring completion.
- [x] Update `docs/current_roadmap.md` with the completed plan link.
- [x] Move this plan to
`docs/historical/superpowers/plans/2026-07-12-presentation-visual-scale-color-pass.md`
only after implementation and review are complete.
@@ -217,6 +217,34 @@ describe("AssistantOperatorThread", () => {
}
});
it("can show the latest response in a static comparison transcript", async () => {
const setScrollTop = vi.fn();
const descriptors = {
scrollTop: Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "scrollTop"),
scrollHeight: Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "scrollHeight"),
clientHeight: Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, "clientHeight"),
};
try {
Object.defineProperties(HTMLDivElement.prototype, {
scrollTop: { configurable: true, get: () => 0, set: setScrollTop },
scrollHeight: { configurable: true, get: () => 240 },
clientHeight: { configurable: true, get: () => 80 },
});
render(<AssistantOperatorThread mode="dock" messages={[]} scrollMode="end" />);
await waitFor(() => expect(setScrollTop).toHaveBeenCalledWith(160));
} finally {
for (const [name, descriptor] of Object.entries(descriptors)) {
if (descriptor) {
Object.defineProperty(HTMLDivElement.prototype, name, descriptor);
} else {
delete (HTMLDivElement.prototype as unknown as Record<string, unknown>)[name];
}
}
}
});
it("pairs a lone tool call with its result", () => {
const messages: ReadonlyArray<AgentMessage> = [
{
@@ -23,7 +23,7 @@ import {
type AssistantOperatorThreadProps = {
readonly mode: "hidden" | "full" | "rail" | "dock";
readonly messages: ReadonlyArray<AgentMessage>;
readonly scrollMode?: "active" | "start" | undefined;
readonly scrollMode?: "active" | "start" | "end" | undefined;
readonly submitApproval?: (() => void) | undefined;
readonly requestRevision?: (() => void) | undefined;
readonly ariaLabel?: string | undefined;
@@ -273,13 +273,22 @@ export const AssistantOperatorThread = ({
}, [activeToolGroupId, toolGroupOverrides]);
useEffect(() => {
const viewport = viewportRef.current;
if (!viewport) return;
// Static comparison transcripts should show their final answer; live
// presentation docks instead keep the beat-owned tool group in view.
if (!activeToolGroupId && scrollMode === "end") {
viewport.scrollTop = Math.max(0, viewport.scrollHeight - viewport.clientHeight);
return;
}
if (!activeToolGroupId) return;
// The same transcript can be much taller than the Scene 9 dock. Keep the
// beat-owned group visible without maintaining a second scroll-state model.
const viewport = viewportRef.current;
const activeGroup = viewport
?.querySelector<HTMLElement>(`[data-tool-group-id="${activeToolGroupId}"]`);
if (!viewport || !activeGroup) return;
if (!activeGroup) return;
const bottomAlignedTop = activeGroup.offsetTop + activeGroup.offsetHeight - viewport.clientHeight;
const dockCenteredTop = activeGroup.offsetTop - (viewport.clientHeight - activeGroup.offsetHeight) / 2;
const requestedTop = scrollMode === "start"
@@ -78,7 +78,12 @@ export const ProblemLoopScene = ({ scene, beat }: ProblemLoopSceneProps) => {
<p>Good at getting through one request.</p>
</header>
<div className="problem-chat-card__transcript" aria-label="one-off assistant transcript" role="group">
<AssistantOperatorThread mode="dock" messages={oneOffToolLoopMessages} ariaLabel="one-off assistant transcript" />
<AssistantOperatorThread
mode="dock"
messages={oneOffToolLoopMessages}
scrollMode="end"
ariaLabel="one-off assistant transcript"
/>
</div>
<p className="problem-artifact-note">The useful work lives in the conversation history.</p>
</article>
@@ -537,8 +537,8 @@
.conclusion-map[data-conclusion-beat="conclusion"] .conclusion-map__statement {
padding-block: 1.1rem 0.45rem;
border-top-color: var(--accent-cyan);
color: var(--text-primary);
border-top-color: var(--conclusion-ink);
color: var(--conclusion-ink);
font-size: 1.25rem;
font-style: normal;
font-weight: 650;
@@ -2412,10 +2412,12 @@
color: var(--problem-muted);
}
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-operator-thread[data-surface="dock"] .assistant-thread,
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-thread {
border-color: var(--problem-rule);
border-radius: 0.35rem;
background: color-mix(in oklch, var(--problem-paper) 92%, var(--problem-muted));
color: var(--problem-ink);
}
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-message[data-role="assistant"] {
@@ -2459,6 +2461,38 @@
background: transparent;
}
/* Keep the compact assistant-ui transcript readable inside the fixed comparison card. */
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-thread__viewport,
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-message[data-role="assistant"] p,
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-args-value"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-result-content"] {
color: var(--problem-ink);
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-args-value"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-result-content"] {
border-color: var(--problem-rule);
background: color-mix(in oklch, var(--problem-paper) 86%, var(--problem-muted));
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-trigger"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-trigger"] {
border-color: transparent;
background: transparent;
color: var(--problem-muted);
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-content"] > div,
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-content"] > div {
border-color: var(--problem-rule);
background: transparent;
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-content"] [data-slot="tool-fallback-root"] {
border-color: var(--problem-rule);
background: color-mix(in oklch, var(--problem-paper) 90%, var(--problem-muted));
}
.problem-artifact-header {
display: grid;
gap: 0.18rem;
@@ -2490,7 +2524,31 @@
.problem-chat-card__transcript {
min-height: 0;
overflow: hidden;
overflow: auto;
scrollbar-width: none;
}
.problem-chat-card__transcript::-webkit-scrollbar {
display: none;
}
.problem-chat-card .assistant-thread__viewport {
padding: 0.45rem;
}
.problem-chat-card .assistant-message {
margin-bottom: 0.25rem;
}
.problem-chat-card .assistant-message__user-bubble {
padding: 0.35rem 0.55rem;
font-size: 0.72rem;
}
.problem-chat-card [data-slot="tool-group-trigger"],
.problem-chat-card [data-slot="tool-fallback-trigger"] {
padding-block: 0.22rem;
font-size: 0.62rem;
}
.problem-blueprint .concept-rail {
@@ -2546,6 +2604,7 @@
.problem-loop-scene__bridge {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
@@ -3677,6 +3736,23 @@
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane {
background: color-mix(in oklch, var(--authoring-paper) 92%, var(--authoring-muted));
border-color: var(--authoring-rule);
color: var(--authoring-ink);
}
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__header > p,
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__composer label,
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__composer-help,
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__run-status {
color: var(--authoring-muted);
}
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__header h2 {
color: var(--authoring-ink);
}
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__composer textarea::placeholder {
color: var(--authoring-muted);
opacity: 1;
}
.prepared-lifecycle-scene[data-presentation-surface="editorial"] .presentation-assistant-pane__conversation {
@@ -3740,6 +3816,19 @@
grid-template-columns: minmax(17rem, 0.65fr) auto minmax(0, 1.35fr);
}
@container presentation-canvas (max-width: 1050px) {
.problem-loop-scene[data-presentation-surface="editorial"] {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
}
.problem-loop-scene[data-presentation-surface="editorial"] .problem-chat-card,
.problem-loop-scene[data-presentation-surface="editorial"] .problem-blueprint {
width: 100%;
height: min(22rem, 56vh);
}
}
@keyframes authoring-canvas-enter {
from { opacity: 0; }
to { opacity: 1; }
@@ -3988,62 +4077,3 @@
.authoring-bindings__rows code:last-child {
color: var(--accent-cyan);
}
/* Keep the editorial problem comparison readable after the shared chat rules. */
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-operator-thread[data-surface="dock"] .assistant-thread,
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-thread {
border-color: var(--problem-rule);
background: color-mix(in oklch, var(--problem-paper) 92%, var(--problem-muted));
color: var(--problem-ink);
}
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-thread__viewport,
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-message[data-role="assistant"],
.problem-loop-scene[data-presentation-surface="editorial"] .assistant-message[data-role="assistant"] p,
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-root"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-root"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-args-value"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-result-content"] {
color: var(--problem-ink);
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-args-value"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-result-content"] {
border-color: var(--problem-rule);
background: color-mix(in oklch, var(--problem-paper) 86%, var(--problem-muted));
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-trigger"],
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-trigger"] {
border-color: transparent;
background: transparent;
color: var(--problem-muted);
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-content"] > div,
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-fallback-content"] > div {
border-color: var(--problem-rule);
background: transparent;
}
.problem-loop-scene[data-presentation-surface="editorial"] [data-slot="tool-group-content"] [data-slot="tool-fallback-root"] {
border-color: var(--problem-rule);
background: color-mix(in oklch, var(--problem-paper) 90%, var(--problem-muted));
}
@container presentation-canvas (max-width: 1050px) {
.problem-loop-scene[data-presentation-surface="editorial"] {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
}
.problem-loop-scene[data-presentation-surface="editorial"] .problem-loop-scene__bridge {
display: none;
}
.problem-loop-scene[data-presentation-surface="editorial"] .problem-chat-card,
.problem-loop-scene[data-presentation-surface="editorial"] .problem-blueprint {
width: 100%;
height: min(22rem, 56vh);
}
}