Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/ExecutionReplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ const styles: Record<string, CSSProperties> = {
fontFamily: MONO,
fontSize: 13,
fontWeight: 700,
color: "var(--color-text-inverse)",
color: "#ffffff",
background: "var(--color-text)",
border: "1px solid var(--color-text)",
borderRadius: 10,
Expand Down
2 changes: 1 addition & 1 deletion src/components/PairQueuePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ const styles: Record<string, CSSProperties> = {
fontFamily: MONO,
fontSize: 13,
fontWeight: 700,
color: "var(--color-text-inverse)",
color: "#ffffff",
background: "var(--color-text)",
border: "1px solid var(--color-text)",
borderRadius: 6,
Expand Down
4 changes: 2 additions & 2 deletions src/components/ProfileSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const ProfileSetup: FC<ProfileSetupProps> = ({
<svg width="10" height="10" viewBox="0 0 12 12" fill="none">
<path
d="M2.5 6.5l2.5 2.5 5-5"
stroke="var(--color-text-inverse)"
stroke="#ffffff"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round"
Expand Down Expand Up @@ -512,7 +512,7 @@ const styles: Record<string, CSSProperties> = {
fontFamily: MONO,
fontSize: 15,
fontWeight: 700,
color: "var(--color-text-inverse)",
color: "#ffffff",
background: "var(--color-text)",
border: "1px solid var(--color-text)",
borderRadius: 10,
Expand Down
62 changes: 54 additions & 8 deletions src/components/Tutorial.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,61 @@ export const Tutorial: FC<TutorialProps> = ({
if (!step) return null;

// Card position — derived from rect when available, fallback centre.
// Clamped to the viewport so the buttons never disappear off-screen,
// even when the spotlighted target is near the page bottom (e.g. the
// "Built with" footer step) or near a viewport edge.
const CARD_WIDTH = 340;
// Slight over-estimate so the card always reserves enough room for
// a 4-line body plus all three action buttons (real measured height
// is ~246px on the longest step, so 270 gives a small safety margin).
const CARD_EST_HEIGHT = 270;
const MARGIN = 20;
const GAP = 16;
let cardLeft: number | string = "50%";
let cardTop: number | string = "50%";
if (rect) {
const spaceBelow = window.innerHeight - rect.bottom - GAP;
const spaceAbove = rect.top - GAP;
const preferTop = step.side === "top";
if (preferTop && spaceAbove >= CARD_EST_HEIGHT + MARGIN) {
cardTop = rect.top - CARD_EST_HEIGHT - GAP;
} else if (!preferTop && spaceBelow >= CARD_EST_HEIGHT + MARGIN) {
cardTop = rect.bottom + GAP;
} else if (spaceBelow >= CARD_EST_HEIGHT + MARGIN) {
cardTop = rect.bottom + GAP;
} else if (spaceAbove >= CARD_EST_HEIGHT + MARGIN) {
cardTop = rect.top - CARD_EST_HEIGHT - GAP;
} else {
// Neither above nor below has enough room — pin near the
// bottom of the viewport so the action buttons stay visible.
cardTop = Math.max(MARGIN, window.innerHeight - CARD_EST_HEIGHT - MARGIN);
}
const idealLeft = rect.left + rect.width / 2 - CARD_WIDTH / 2;
cardLeft = Math.max(
MARGIN,
Math.min(window.innerWidth - CARD_WIDTH - MARGIN, idealLeft),
);
// Final hard clamp: regardless of branch chosen above, never let
// the card extend past the viewport bottom. If the estimate was
// tight, the inner scroll (overflowY: auto) keeps the body
// readable while the action buttons remain visible.
if (typeof cardTop === "number") {
cardTop = Math.max(
MARGIN,
Math.min(cardTop, window.innerHeight - CARD_EST_HEIGHT - MARGIN),
);
}
}
const cardStyle: CSSProperties = {
position: "fixed",
zIndex: 401,
left: rect ? Math.max(20, Math.min(window.innerWidth - 360, rect.left)) : "50%",
top: rect
? step.side === "top"
? Math.max(20, rect.top - 140)
: rect.bottom + 16
: "50%",
left: cardLeft,
top: cardTop,
transform: rect ? undefined : "translate(-50%, -50%)",
width: 340,
width: CARD_WIDTH,
maxWidth: "calc(100vw - 40px)",
maxHeight: `calc(100vh - ${MARGIN * 2}px)`,
overflowY: "auto",
background: "var(--surface-raised)",
border: "1px solid var(--color-stroke)",
borderRadius: 14,
Expand Down Expand Up @@ -338,7 +381,10 @@ const styles: Record<string, CSSProperties> = {
fontFamily: "var(--font-mono)",
fontSize: 12,
fontWeight: 700,
color: "var(--color-text-inverse)",
// Fixed white — `--color-text-inverse` in light theme is the same
// near-black as `--color-text`, so using it here made the label
// invisible on the black button.
color: "#ffffff",
background: "var(--color-text)",
border: "1px solid var(--color-text)",
borderRadius: 6,
Expand Down
Loading