From 434d68f28ade1aca1fa6f3a0a272539f44cfc770 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 15 Oct 2024 10:25:19 +0200 Subject: [PATCH 1/8] wip --- .../src/core/tailwind/tailwind-compilation.ts | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index 78f5ef83eae0..b2c65ddc4813 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -1,7 +1,7 @@ import React from 'react' import type { TailwindConfig, Tailwindcss } from '@mhsdesign/jit-browser-tailwindcss' import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss' -import type { ProjectContentTreeRoot, TextFile, TextFileContents } from 'utopia-shared/src/types' +import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' import { getProjectFileByFilePath, walkContentsTree } from '../../components/assets' import { interactionSessionIsActive } from '../../components/canvas/canvas-strategies/interaction-state' import { CanvasContainerID } from '../../components/canvas/canvas-types' @@ -98,16 +98,25 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { interactionSessionIsActive(store.editor.canvas.interactionSession), ) - const observerCallback = React.useCallback(() => { - if ( - isInteractionActiveRef.current || - ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress - !isFeatureEnabled('Tailwind') - ) { - return - } - generateTailwindClasses(projectContents, requireFn) - }, [isInteractionActiveRef, projectContents, requireFn]) + const observerCallback = React.useCallback( + (mutations: MutationRecord[]) => { + const updateHasNewTailwindData = mutations.some( + (m) => + m.addedNodes.length > 0 || // new DOM element was added with potentially new classes + m.attributeName === 'class', // a new class was added to the class attribute of an element + ) + if ( + isInteractionActiveRef.current || + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress + !updateHasNewTailwindData || + !isFeatureEnabled('Tailwind') + ) { + return + } + generateTailwindClasses(projectContents, requireFn) + }, + [isInteractionActiveRef, projectContents, requireFn], + ) React.useEffect(() => { const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath) @@ -122,7 +131,7 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { subtree: true, }) - observerCallback() + generateTailwindClasses(projectContents, requireFn) return () => { observer.disconnect() From 0bef374bf0421d040f1de95805664f547403e388 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 15 Oct 2024 10:36:45 +0200 Subject: [PATCH 2/8] guard useEffect as well --- .../src/core/tailwind/tailwind-compilation.ts | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index b2c65ddc4813..936be665a8c1 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -87,6 +87,26 @@ function generateTailwindClasses(projectContents: ProjectContentTreeRoot, requir void generateTailwindStyles(tailwindCss, allCSSFiles) } +function runTailwindClassGenerationOnDOMMutation( + mutations: MutationRecord[], + projectContents: ProjectContentTreeRoot, + requireFn: RequireFn, +) { + const updateHasNewTailwindData = mutations.some( + (m) => + m.addedNodes.length > 0 || // new DOM element was added with potentially new classes + m.attributeName === 'class', // a new class was added to the class attribute of an element + ) + if ( + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress + !updateHasNewTailwindData || + !isFeatureEnabled('Tailwind') + ) { + return + } + generateTailwindClasses(projectContents, requireFn) +} + export const useTailwindCompilation = (requireFn: RequireFn) => { const projectContents = useEditorState( Substores.projectContents, @@ -98,32 +118,19 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { interactionSessionIsActive(store.editor.canvas.interactionSession), ) - const observerCallback = React.useCallback( - (mutations: MutationRecord[]) => { - const updateHasNewTailwindData = mutations.some( - (m) => - m.addedNodes.length > 0 || // new DOM element was added with potentially new classes - m.attributeName === 'class', // a new class was added to the class attribute of an element - ) - if ( - isInteractionActiveRef.current || - ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress - !updateHasNewTailwindData || - !isFeatureEnabled('Tailwind') - ) { - return - } - generateTailwindClasses(projectContents, requireFn) - }, - [isInteractionActiveRef, projectContents, requireFn], - ) - React.useEffect(() => { const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath) - if (tailwindConfigFile == null || tailwindConfigFile.type !== 'TEXT_FILE') { - return // we consider tailwind to be enabled if there's a tailwind config file in the project + if ( + tailwindConfigFile == null || + isInteractionActiveRef.current || + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress + !isFeatureEnabled('Tailwind') + ) { + return } - const observer = new MutationObserver(observerCallback) + const observer = new MutationObserver((mutations) => { + runTailwindClassGenerationOnDOMMutation(mutations, projectContents, requireFn) + }) observer.observe(document.getElementById(CanvasContainerID)!, { attributes: true, @@ -136,5 +143,5 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { return () => { observer.disconnect() } - }, [isInteractionActiveRef, observerCallback, projectContents, requireFn]) + }, [isInteractionActiveRef, projectContents, requireFn]) } From 01e055fcaef594b185fef43b9075a3189d9cc282 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Tue, 15 Oct 2024 11:15:52 +0200 Subject: [PATCH 3/8] remove unnecessary guard clauses --- editor/src/core/tailwind/tailwind-compilation.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index 936be665a8c1..ba661abe6d37 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -97,11 +97,7 @@ function runTailwindClassGenerationOnDOMMutation( m.addedNodes.length > 0 || // new DOM element was added with potentially new classes m.attributeName === 'class', // a new class was added to the class attribute of an element ) - if ( - ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress - !updateHasNewTailwindData || - !isFeatureEnabled('Tailwind') - ) { + if (!updateHasNewTailwindData) { return } generateTailwindClasses(projectContents, requireFn) From e96d4612a8abd7b8056e0aff7626371831754335 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 17 Oct 2024 11:14:20 +0200 Subject: [PATCH 4/8] tests for tailwind rendering with Remix navigation --- .../core/tailwind/tailwind.spec.browser2.tsx | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/editor/src/core/tailwind/tailwind.spec.browser2.tsx b/editor/src/core/tailwind/tailwind.spec.browser2.tsx index b95956046c32..91b3eb6447a0 100644 --- a/editor/src/core/tailwind/tailwind.spec.browser2.tsx +++ b/editor/src/core/tailwind/tailwind.spec.browser2.tsx @@ -1,5 +1,13 @@ +import { mouseClickAtPoint } from '../../components/canvas/event-helpers.test-utils' +import type { EditorRenderResult } from '../../components/canvas/ui-jsx.test-utils' import { renderTestEditorWithModel } from '../../components/canvas/ui-jsx.test-utils' +import { switchEditorMode } from '../../components/editor/actions/action-creators' +import { EditorModes } from '../../components/editor/editor-modes' +import { StoryboardFilePath } from '../../components/editor/store/editor-state' +import { createModifiedProject } from '../../sample-projects/sample-project-utils.test-utils' import { setFeatureForBrowserTestsUseInDescribeBlockOnly } from '../../utils/utils.test-utils' +import { windowPoint } from '../shared/math-utils' +import { TailwindConfigPath } from './tailwind-config' import { Project } from './tailwind.test-utils' describe('rendering tailwind projects in the editor', () => { @@ -133,4 +141,166 @@ describe('rendering tailwind projects in the editor', () => { }) } }) + + describe('Remix', () => { + const projectWithMultipleRoutes = createModifiedProject({ + [StoryboardFilePath]: `import * as React from 'react' + import { RemixScene, Storyboard } from 'utopia-api' + + export var storyboard = ( + + + + ) + `, + ['/app/root.js']: `import React from 'react' + import { Outlet } from '@remix-run/react' + + export default function Root() { + return ( +
+ I am Root! + +
+ ) + } + `, + ['/app/routes/_index.js']: `import React from 'react' + import { Link } from '@remix-run/react' + + export default function Index() { + return ( +
+ Index page + About +
+ ) + } + `, + ['/app/routes/about.js']: `import React from 'react' + + export default function About() { + return ( +
+ About page +
+ ) + } + `, + '/src/app.css': ` + @tailwind base; + @tailwind components; + @tailwind utilities; + `, + [TailwindConfigPath]: ` + const Tailwind = { + theme: { + colors: { + transparent: 'transparent', + current: 'currentColor', + white: '#ffffff', + purple: '#3f3cbb', + midnight: '#121063', + metal: '#565584', + tahiti: '#3ab7bf', + silver: '#ecebff', + 'bubble-gum': '#ff77e9', + bermuda: '#78dcca', + }, + }, + plugins: [ + function ({ addUtilities }) { + const newUtilities = { + '.text-shadow': { + textShadow: '2px 2px 4px rgba(0, 0, 0, 0.1)', + }, + '.text-shadow-md': { + textShadow: '3px 3px 6px rgba(0, 0, 0, 0.2)', + }, + '.text-shadow-lg': { + textShadow: '4px 4px 8px rgba(0, 0, 0, 0.3)', + }, + '.text-shadow-none': { + textShadow: 'none', + }, + } + + addUtilities(newUtilities, ['responsive', 'hover']) + }, + ], + } + export default Tailwind`, + }) + + it('can render content in a RemixScene', async () => { + const editor = await renderTestEditorWithModel( + projectWithMultipleRoutes, + 'await-first-dom-report', + ) + { + const root = editor.renderedDOM.getByTestId('root') + const { backgroundColor, display, flexDirection, gap, fontSize } = getComputedStyle(root) + expect({ backgroundColor, display, flexDirection, gap, fontSize }).toEqual({ + backgroundColor: 'rgba(0, 0, 0, 0)', + display: 'flex', + flexDirection: 'column', + fontSize: '24px', + gap: '40px', + }) + } + { + const index = editor.renderedDOM.getByTestId('index') + const { display, flexDirection, gap } = getComputedStyle(index) + expect({ display, flexDirection, gap }).toEqual({ + display: 'flex', + flexDirection: 'column', + gap: '32px', + }) + } + }) + it('can render content after navigating to a different page', async () => { + const editor = await renderTestEditorWithModel( + projectWithMultipleRoutes, + 'await-first-dom-report', + ) + await switchToLiveMode(editor) + await clickRemixLink(editor) + + { + const about = editor.renderedDOM.getByTestId('about') + const { display, flexDirection, gap, padding } = getComputedStyle(about) + expect({ display, flexDirection, gap, padding }).toEqual({ + display: 'flex', + flexDirection: 'row', + gap: '24px', + padding: '16px', + }) + } + { + const aboutText = editor.renderedDOM.getByTestId('about-text') + const { textShadow } = getComputedStyle(aboutText) + expect(textShadow).toEqual('rgba(0, 0, 0, 0.2) 3px 3px 6px') + } + }) + }) }) + +const switchToLiveMode = (editor: EditorRenderResult) => + editor.dispatch([switchEditorMode(EditorModes.liveMode())], true) + +async function clickLinkWithTestId(editor: EditorRenderResult, testId: string) { + const targetElement = editor.renderedDOM.queryAllByTestId(testId)[0] + const targetElementBounds = targetElement.getBoundingClientRect() + + const clickPoint = windowPoint({ x: targetElementBounds.x + 5, y: targetElementBounds.y + 5 }) + await mouseClickAtPoint(targetElement, clickPoint) +} + +async function clickRemixLink(editor: EditorRenderResult) { + await clickLinkWithTestId(editor, 'remix-link') + await editor.getDispatchFollowUpActionsFinished() +} From 7d0e0996f30fec1374dcae5afa7747f71106d0bc Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 17 Oct 2024 11:31:42 +0200 Subject: [PATCH 5/8] update comment --- editor/src/core/tailwind/tailwind-compilation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index b1d6b8b9df4b..d4edd6ad1ced 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -95,7 +95,7 @@ function runTailwindClassGenerationOnDOMMutation( const updateHasNewTailwindData = mutations.some( (m) => m.addedNodes.length > 0 || // new DOM element was added with potentially new classes - m.attributeName === 'class', // a new class was added to the class attribute of an element + m.attributeName === 'class', // potentially new classes were added to the class attribute of an element ) if (!updateHasNewTailwindData) { return From 63f011f2e3b9603c1d84280806a4ce8aa783ab53 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Thu, 17 Oct 2024 17:32:49 +0200 Subject: [PATCH 6/8] wip --- .../src/core/tailwind/tailwind-compilation.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index d4edd6ad1ced..2ef42dab8a15 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -104,28 +104,30 @@ function runTailwindClassGenerationOnDOMMutation( } export const useTailwindCompilation = (requireFn: RequireFn) => { - const projectContents = useEditorState( - Substores.projectContents, - (store) => store.editor.projectContents, - 'useTailwindCompilation projectContents', - ) + const projectContentsRef = useRefEditorState((store) => store.editor.projectContents) const isInteractionActiveRef = useRefEditorState((store) => interactionSessionIsActive(store.editor.canvas.interactionSession), ) + const tailwindConfigExists = + getProjectFileByFilePath(projectContentsRef.current, TailwindConfigPath) != null + React.useEffect(() => { - const tailwindConfigFile = getProjectFileByFilePath(projectContents, TailwindConfigPath) - if ( - tailwindConfigFile == null || - isInteractionActiveRef.current || - ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' || // implies that an interaction is in progress - !isFeatureEnabled('Tailwind') - ) { + if (!tailwindConfigExists || !isFeatureEnabled('Tailwind')) { return } + + generateTailwindClasses(projectContentsRef.current, requireFn) const observer = new MutationObserver((mutations) => { - runTailwindClassGenerationOnDOMMutation(mutations, projectContents, requireFn) + if ( + isInteractionActiveRef.current || + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' // implies that an interaction is in progress) { + ) { + return + } + + runTailwindClassGenerationOnDOMMutation(mutations, projectContentsRef.current, requireFn) }) observer.observe(document.getElementById(CanvasContainerID)!, { @@ -134,10 +136,8 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { subtree: true, }) - generateTailwindClasses(projectContents, requireFn) - return () => { observer.disconnect() } - }, [isInteractionActiveRef, projectContents, requireFn]) + }, [isInteractionActiveRef, projectContentsRef, requireFn, tailwindConfigExists]) } From d7da464e4059c7221aea1436d457430034eea756 Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Fri, 18 Oct 2024 11:40:50 +0200 Subject: [PATCH 7/8] use all the refs --- .../src/components/canvas/ui-jsx-canvas.tsx | 2 +- .../src/core/tailwind/tailwind-compilation.ts | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/editor/src/components/canvas/ui-jsx-canvas.tsx b/editor/src/components/canvas/ui-jsx-canvas.tsx index 85239c3dce47..3f21fb976144 100644 --- a/editor/src/components/canvas/ui-jsx-canvas.tsx +++ b/editor/src/components/canvas/ui-jsx-canvas.tsx @@ -498,7 +498,7 @@ export const UiJsxCanvas = React.memo((props) const executionScope = scope - useTailwindCompilation(customRequire) + useTailwindCompilation() const topLevelElementsMap = useKeepReferenceEqualityIfPossible(new Map(topLevelJsxComponents)) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index 2ef42dab8a15..fdbd7c06ce88 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -3,13 +3,8 @@ import type { TailwindConfig, Tailwindcss } from '@mhsdesign/jit-browser-tailwin import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss' import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' import { getProjectFileByFilePath, walkContentsTree } from '../../components/assets' -import { interactionSessionIsActive } from '../../components/canvas/canvas-strategies/interaction-state' import { CanvasContainerID } from '../../components/canvas/canvas-types' -import { - Substores, - useEditorState, - useRefEditorState, -} from '../../components/editor/store/store-hook' +import { useRefEditorState } from '../../components/editor/store/store-hook' import { importDefault } from '../es-modules/commonjs-interop' import { rescopeCSSToTargetCanvasOnly } from '../shared/css-utils' import type { RequireFn } from '../shared/npm-dependency-types' @@ -90,6 +85,7 @@ function generateTailwindClasses(projectContents: ProjectContentTreeRoot, requir function runTailwindClassGenerationOnDOMMutation( mutations: MutationRecord[], projectContents: ProjectContentTreeRoot, + isInteractionActive: boolean, requireFn: RequireFn, ) { const updateHasNewTailwindData = mutations.some( @@ -97,17 +93,25 @@ function runTailwindClassGenerationOnDOMMutation( m.addedNodes.length > 0 || // new DOM element was added with potentially new classes m.attributeName === 'class', // potentially new classes were added to the class attribute of an element ) - if (!updateHasNewTailwindData) { + if ( + !updateHasNewTailwindData || + isInteractionActive || + ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' // implies that an interaction is in progress) + ) { return } generateTailwindClasses(projectContents, requireFn) } -export const useTailwindCompilation = (requireFn: RequireFn) => { +export const useTailwindCompilation = () => { + const requireFnRef = useRefEditorState((store) => { + const requireFn = store.editor.codeResultCache.curriedRequireFn(store.editor.projectContents) + return (importOrigin: string, toImport: string) => requireFn(importOrigin, toImport, false) + }) const projectContentsRef = useRefEditorState((store) => store.editor.projectContents) - const isInteractionActiveRef = useRefEditorState((store) => - interactionSessionIsActive(store.editor.canvas.interactionSession), + const isInteractionActiveRef = useRefEditorState( + (store) => store.editor.canvas.interactionSession != null, ) const tailwindConfigExists = @@ -118,16 +122,13 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { return } - generateTailwindClasses(projectContentsRef.current, requireFn) const observer = new MutationObserver((mutations) => { - if ( - isInteractionActiveRef.current || - ElementsToRerenderGLOBAL.current !== 'rerender-all-elements' // implies that an interaction is in progress) { - ) { - return - } - - runTailwindClassGenerationOnDOMMutation(mutations, projectContentsRef.current, requireFn) + runTailwindClassGenerationOnDOMMutation( + mutations, + projectContentsRef.current, + isInteractionActiveRef.current, + requireFnRef.current, + ) }) observer.observe(document.getElementById(CanvasContainerID)!, { @@ -136,8 +137,11 @@ export const useTailwindCompilation = (requireFn: RequireFn) => { subtree: true, }) + // run the initial tailwind class generation + generateTailwindClasses(projectContentsRef.current, requireFnRef.current) + return () => { observer.disconnect() } - }, [isInteractionActiveRef, projectContentsRef, requireFn, tailwindConfigExists]) + }, [isInteractionActiveRef, projectContentsRef, requireFnRef, tailwindConfigExists]) } From 243afaf31467f574e545920cd9c465c5f8e3e01d Mon Sep 17 00:00:00 2001 From: Berci Kormendy Date: Fri, 18 Oct 2024 11:56:11 +0200 Subject: [PATCH 8/8] get tailwind config file with reselect --- .../src/core/tailwind/tailwind-compilation.ts | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/editor/src/core/tailwind/tailwind-compilation.ts b/editor/src/core/tailwind/tailwind-compilation.ts index fdbd7c06ce88..b71edcc1b587 100644 --- a/editor/src/core/tailwind/tailwind-compilation.ts +++ b/editor/src/core/tailwind/tailwind-compilation.ts @@ -4,7 +4,11 @@ import { createTailwindcss } from '@mhsdesign/jit-browser-tailwindcss' import type { ProjectContentTreeRoot } from 'utopia-shared/src/types' import { getProjectFileByFilePath, walkContentsTree } from '../../components/assets' import { CanvasContainerID } from '../../components/canvas/canvas-types' -import { useRefEditorState } from '../../components/editor/store/store-hook' +import { + Substores, + useEditorState, + useRefEditorState, +} from '../../components/editor/store/store-hook' import { importDefault } from '../es-modules/commonjs-interop' import { rescopeCSSToTargetCanvasOnly } from '../shared/css-utils' import type { RequireFn } from '../shared/npm-dependency-types' @@ -13,6 +17,8 @@ import { ElementsToRerenderGLOBAL } from '../../components/canvas/ui-jsx-canvas' import { isFeatureEnabled } from '../../utils/feature-switches' import type { Config } from 'tailwindcss/types/config' import type { EditorState } from '../../components/editor/store/editor-state' +import { createSelector } from 'reselect' +import type { ProjectContentSubstate } from '../../components/editor/store/store-hook-substore-types' const LatestConfig: { current: { code: string; config: Config } | null } = { current: null } export function getTailwindConfigCached(editorState: EditorState): Config | null { @@ -103,6 +109,11 @@ function runTailwindClassGenerationOnDOMMutation( generateTailwindClasses(projectContents, requireFn) } +const tailwindConfigSelector = createSelector( + (store: ProjectContentSubstate) => store.editor.projectContents, + (projectContents) => getProjectFileByFilePath(projectContents, TailwindConfigPath), +) + export const useTailwindCompilation = () => { const requireFnRef = useRefEditorState((store) => { const requireFn = store.editor.codeResultCache.curriedRequireFn(store.editor.projectContents) @@ -114,11 +125,20 @@ export const useTailwindCompilation = () => { (store) => store.editor.canvas.interactionSession != null, ) - const tailwindConfigExists = - getProjectFileByFilePath(projectContentsRef.current, TailwindConfigPath) != null + // this is not a ref, beacuse we want to re-compile the Tailwind classes when the tailwind config changes + const tailwindConfig = useEditorState( + Substores.projectContents, + tailwindConfigSelector, + 'useTailwindCompilation tailwindConfig', + ) React.useEffect(() => { - if (!tailwindConfigExists || !isFeatureEnabled('Tailwind')) { + const canvasContainer = document.getElementById(CanvasContainerID) + if ( + tailwindConfig == null || // TODO: read this from the utopia key in package.json + canvasContainer == null || + !isFeatureEnabled('Tailwind') + ) { return } @@ -131,7 +151,7 @@ export const useTailwindCompilation = () => { ) }) - observer.observe(document.getElementById(CanvasContainerID)!, { + observer.observe(canvasContainer, { attributes: true, childList: true, subtree: true, @@ -143,5 +163,5 @@ export const useTailwindCompilation = () => { return () => { observer.disconnect() } - }, [isInteractionActiveRef, projectContentsRef, requireFnRef, tailwindConfigExists]) + }, [isInteractionActiveRef, projectContentsRef, requireFnRef, tailwindConfig]) }