fix: restore tree editor visibility after Tailwind v4 upgrade and add Sentry DSN build arg
- Add missing `flex` class on TreeEditorPage editor wrapper (collapsed canvas to 0 height) - Rewrite React Flow CSS overrides to use --xy-* custom properties (v12 compat with TW4) - Move React Flow CSS import from component to index.css (CSS layer ordering) - Add VITE_SENTRY_DSN build arg to Dockerfile for Railway builds - Use env var for Sentry DSN in instrument.ts with hardcoded fallback - Add lessons learned #53-55 to CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -337,6 +337,12 @@ navigate(`/trees/${newTree.id}/edit`)
|
||||
|
||||
**52. Mobile scroll-to-top — use `scrollIntoView`, not `window.scrollTo`:** Mobile browsers (iOS Safari, Firefox Android) often ignore `window.scrollTo()`. Use a ref at the top of the page and call `ref.current.scrollIntoView({ behavior: 'smooth', block: 'start' })` instead. Trigger via `useEffect` on the state change (not inline with `setState`) so the DOM has committed before scrolling.
|
||||
|
||||
**53. Flex height chain — every ancestor must be a flex container for `flex-1` to work:** If a child uses `flex-1` to fill its parent, the parent MUST have `display: flex` (the `flex` class). A missing `flex` on any wrapper div breaks the entire height chain, causing React Flow (and any `h-full` descendant) to collapse to 0 height. Debug with: `let el = document.querySelector('.react-flow'); while(el) { console.log(el.getBoundingClientRect().height, el.className); el = el.parentElement; }`. The break is where height drops to 0. Common symptom: React Flow error `"parent container needs a width and a height"`.
|
||||
|
||||
**54. React Flow CSS in Tailwind v4 — import in `index.css`, not component JS:** With `@tailwindcss/vite`, importing `@xyflow/react/dist/style.css` inside a component file causes the plugin to process/wrap it in a cascade layer, lowering specificity. Import it in `index.css` instead: `@import '@xyflow/react/dist/style.css';` after `@import 'tailwindcss';`. Override dark theme using `--xy-*` CSS custom properties (e.g., `--xy-edge-stroke-default`) on `.react-flow.dark`, NOT old-style direct property selectors like `.react-flow__edge-path { stroke: ... }`.
|
||||
|
||||
**55. App shell height chain for full-height pages (tree editor, procedural editor):** The CSS Grid app shell (`app-shell`) → `.main-content` → page component chain must preserve height. `.main-content` is a grid cell with implicit height from `1fr`. Pages using React Flow or other full-height layouts need every wrapper div between `.main-content` and the canvas to either use `flex` + `flex-1` + `min-h-0` or explicit `h-full`. Adding ANY wrapper div (e.g., for animations, transitions) without proper height classes will collapse the canvas to 0.
|
||||
|
||||
---
|
||||
|
||||
## RBAC & Permissions
|
||||
|
||||
@@ -12,9 +12,11 @@ RUN npm ci
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build argument for API URL (set at build time)
|
||||
# Build arguments (set at build time)
|
||||
ARG VITE_API_URL
|
||||
ARG VITE_SENTRY_DSN
|
||||
ENV VITE_API_URL=$VITE_API_URL
|
||||
ENV VITE_SENTRY_DSN=$VITE_SENTRY_DSN
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
@@ -183,7 +183,7 @@ export function AppLayout() {
|
||||
)}
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="main-content overflow-y-auto">
|
||||
<main className="main-content">
|
||||
<EmailVerificationBanner />
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
/* React Flow — imported here (not in component JS) so @tailwindcss/vite
|
||||
doesn't wrap it in a cascade layer, which would lower its specificity
|
||||
below Tailwind's own styles and hide nodes/edges. */
|
||||
@import '@xyflow/react/dist/style.css';
|
||||
|
||||
@theme {
|
||||
/* ── Brand tokens ─────────────────────────────────── */
|
||||
--color-brand-gradient-from: oklch(0.65 0.13 195); /* #06b6d4 cyan-500 */
|
||||
@@ -364,58 +369,59 @@
|
||||
}
|
||||
|
||||
/* ── React Flow dark theme overrides ─────────────────── */
|
||||
.react-flow__background {
|
||||
background-color: transparent !important;
|
||||
/* React Flow v12 uses --xy-* CSS custom properties for theming.
|
||||
Override the defaults to match our Slate & Ice design system. */
|
||||
.react-flow.dark {
|
||||
--xy-background-color-default: transparent;
|
||||
--xy-edge-stroke-default: var(--color-border);
|
||||
--xy-edge-stroke-selected-default: var(--color-primary);
|
||||
--xy-edge-label-color-default: var(--color-muted-foreground);
|
||||
--xy-edge-label-background-color-default: var(--color-card);
|
||||
--xy-node-background-color-default: var(--color-card);
|
||||
--xy-node-color-default: var(--color-foreground);
|
||||
--xy-node-border-default: 1px solid var(--color-border);
|
||||
--xy-handle-background-color-default: var(--color-border);
|
||||
--xy-handle-border-color-default: var(--color-card);
|
||||
--xy-minimap-background-color-default: var(--color-card);
|
||||
--xy-minimap-mask-background-color-default: oklch(0.22 0.008 264 / 0.6);
|
||||
--xy-controls-button-background-color-default: var(--color-card);
|
||||
--xy-controls-button-background-color-hover-default: var(--color-accent);
|
||||
--xy-controls-button-color-default: var(--color-muted-foreground);
|
||||
--xy-controls-button-color-hover-default: var(--color-foreground);
|
||||
--xy-controls-button-border-color-default: var(--color-border);
|
||||
}
|
||||
|
||||
.react-flow__controls {
|
||||
background-color: var(--color-card) !important;
|
||||
border: 1px solid var(--color-border) !important;
|
||||
border-radius: 0.75rem !important;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3) !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.react-flow__controls-button {
|
||||
background-color: var(--color-card) !important;
|
||||
border-color: var(--color-border) !important;
|
||||
fill: var(--color-muted-foreground) !important;
|
||||
color: var(--color-muted-foreground) !important;
|
||||
}
|
||||
|
||||
.react-flow__controls-button:hover {
|
||||
background-color: var(--color-accent) !important;
|
||||
fill: var(--color-foreground) !important;
|
||||
}
|
||||
|
||||
.react-flow__controls-button svg {
|
||||
fill: inherit !important;
|
||||
}
|
||||
|
||||
.react-flow__minimap {
|
||||
background-color: var(--color-card) !important;
|
||||
border: 1px solid var(--color-border) !important;
|
||||
border-radius: 0.75rem !important;
|
||||
}
|
||||
|
||||
.react-flow__edge-path {
|
||||
stroke: var(--color-border);
|
||||
}
|
||||
|
||||
.react-flow__edge-text {
|
||||
fill: var(--color-muted-foreground);
|
||||
}
|
||||
|
||||
.react-flow__edge-textbg {
|
||||
fill: var(--color-card);
|
||||
}
|
||||
|
||||
.react-flow__attribution {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.react-flow__handle {
|
||||
background-color: var(--color-border);
|
||||
/* ── Glow edge animations ────────────────────────────── */
|
||||
@keyframes glow-flow-downstream {
|
||||
from { stroke-dashoffset: 40; }
|
||||
to { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
@keyframes glow-flow-upstream {
|
||||
from { stroke-dashoffset: 0; }
|
||||
to { stroke-dashoffset: 40; }
|
||||
}
|
||||
|
||||
.glow-edge-flow-downstream {
|
||||
animation: glow-flow-downstream 1s linear infinite;
|
||||
}
|
||||
|
||||
.glow-edge-flow-upstream {
|
||||
animation: glow-flow-upstream 1s linear infinite;
|
||||
}
|
||||
|
||||
/* ── Accessibility: Reduce motion ────────────────────── */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
Sentry.init({
|
||||
dsn: "https://23937b8c0cea2484f6a9d5b97d0b7d4b@o4511005918887936.ingest.us.sentry.io/4511005926883328",
|
||||
dsn: import.meta.env.VITE_SENTRY_DSN || "https://23937b8c0cea2484f6a9d5b97d0b7d4b@o4511005918887936.ingest.us.sentry.io/4511005926883328",
|
||||
environment: import.meta.env.MODE,
|
||||
|
||||
integrations: [
|
||||
|
||||
@@ -517,7 +517,7 @@ export function TreeEditorPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<div className="flex h-[calc(100vh-56px)] overflow-hidden">
|
||||
{/* Main content column */}
|
||||
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
||||
|
||||
@@ -805,7 +805,7 @@ export function TreeEditorPage() {
|
||||
)}
|
||||
|
||||
{/* Main Editor */}
|
||||
<div className="min-h-0 flex-1 overflow-hidden">
|
||||
<div className="flex min-h-0 flex-1 overflow-hidden">
|
||||
<TreeEditorLayout
|
||||
isMobile={isMobile}
|
||||
isMetadataOpen={isMetadataOpen}
|
||||
|
||||
Reference in New Issue
Block a user