diff --git a/app/electron/main/code/diff/index.ts b/app/electron/main/code/diff/index.ts index ba73c1358..90455ae69 100644 --- a/app/electron/main/code/diff/index.ts +++ b/app/electron/main/code/diff/index.ts @@ -59,8 +59,5 @@ function processGroupedRequests(groupedRequests: Map): C } function generateCode(ast: t.File, options: GeneratorOptions, codeBlock: string): string { - return removeSemiColonIfApplicable( - generate(ast, { ...options, retainLines: false }, codeBlock).code, - codeBlock, - ); + return removeSemiColonIfApplicable(generate(ast, options, codeBlock).code, codeBlock); } diff --git a/app/electron/main/code/diff/transform.ts b/app/electron/main/code/diff/transform.ts index 0b709dabb..cdb789e26 100644 --- a/app/electron/main/code/diff/transform.ts +++ b/app/electron/main/code/diff/transform.ts @@ -84,13 +84,23 @@ function insertElementToNode(path: NodePath, element: InsertedElem path.node.children.unshift(newElement); break; case InsertPos.INDEX: - if ( - element.location.index !== undefined && - element.location.index < path.node.children.length - ) { - path.node.children.splice(element.location.index + 1, 0, newElement); + // Note: children includes non-JSXElement which our index does not account for. We need to find the JSXElement/JSXFragment-only index. + if (element.location.index !== undefined) { + const jsxElements = path.node.children.filter( + (child) => t.isJSXElement(child) || t.isJSXFragment(child), + ) as t.JSXElement[]; + + const targetIndex = Math.min(element.location.index, jsxElements.length); + + if (targetIndex === path.node.children.length) { + path.node.children.push(newElement); + } else { + const targetChild = jsxElements[targetIndex]; + const targetChildIndex = path.node.children.indexOf(targetChild); + path.node.children.splice(targetChildIndex, 0, newElement); + } } else { - console.error(`Invalid index: ${element.location.index}`); + console.error('Invalid index: undefined'); path.node.children.push(newElement); } break; @@ -170,8 +180,7 @@ function moveElementInNode( filepath: string, element: MovedElementWithTemplate, ): void { - // Note: children includes non-JSXElement which our index does not account for. We need to find the JSXElement-only index. - + // Note: children includes non-JSXElement which our index does not account for. We need to find the JSXElement/JSXFragment-only index. const children = path.node.children; const elementToMoveIndex = children.findIndex((child) => { if (t.isJSXElement(child)) { @@ -187,7 +196,7 @@ function moveElementInNode( const [elementToMove] = children.splice(elementToMoveIndex, 1); const jsxElements = children.filter( - (child) => t.isJSXElement(child) || child === elementToMove, + (child) => t.isJSXElement(child) || t.isJSXFragment(child) || child === elementToMove, ) as t.JSXElement[]; const targetIndex = Math.min(element.location.index, jsxElements.length); diff --git a/app/electron/preload/webview/elements/helpers.ts b/app/electron/preload/webview/elements/helpers.ts index 447cc0193..9e61d6c6a 100644 --- a/app/electron/preload/webview/elements/helpers.ts +++ b/app/electron/preload/webview/elements/helpers.ts @@ -102,3 +102,14 @@ export const isElementInserted = (selector: string): boolean => { } return targetEl.hasAttribute(EditorAttributes.DATA_ONLOOK_INSERTED); }; + +export const getImmediateTextContent = (el: HTMLElement): string | undefined => { + const stringArr = Array.from(el.childNodes) + .filter((node) => node.nodeType === Node.TEXT_NODE) + .map((node) => node.textContent); + + if (stringArr.length === 0) { + return; + } + return stringArr.join(''); +}; diff --git a/app/electron/preload/webview/elements/insert.ts b/app/electron/preload/webview/elements/insert.ts index 0f72646f9..e9b737412 100644 --- a/app/electron/preload/webview/elements/insert.ts +++ b/app/electron/preload/webview/elements/insert.ts @@ -1,5 +1,5 @@ import { CssStyleChange } from '../changes'; -import { getDeepElement, getDomElement } from './helpers'; +import { getDeepElement, getDomElement, getImmediateTextContent } from './helpers'; import { ActionElement, ActionElementLocation } from '/common/actions'; import { EditorAttributes, INLINE_ONLY_CONTAINERS } from '/common/constants'; import { getUniqueSelector } from '/common/helpers'; @@ -161,7 +161,7 @@ function getInsertedElement(el: HTMLElement): InsertedElement { timestamp: parseInt(el.getAttribute(EditorAttributes.DATA_ONLOOK_TIMESTAMP) || '0'), attributes: {}, location: getInsertedLocation(el), - textContent: el.textContent || undefined, + textContent: getImmediateTextContent(el), }; } diff --git a/app/src/routes/editor/EditPanel/inputs/NestedInputs.tsx b/app/src/routes/editor/EditPanel/inputs/NestedInputs.tsx index 1c478dedf..7404f1e1d 100644 --- a/app/src/routes/editor/EditPanel/inputs/NestedInputs.tsx +++ b/app/src/routes/editor/EditPanel/inputs/NestedInputs.tsx @@ -1,6 +1,5 @@ import { useEditorEngine } from '@/components/Context'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; -import { constructChangeCurried } from '@/lib/editor/styles/inputs'; import { ElementStyle } from '@/lib/editor/styles/models'; import { BorderAllIcon, @@ -30,15 +29,12 @@ const DISPLAY_NAME_OVERRIDE: Record = { 'Bottom Left': , }; -const VALID_KEYS = ['margin', 'padding', 'borderRadius']; +const TOP_ELEMENT_KEYS = ['margin', 'padding', 'borderRadius']; const NestedInputs = observer(({ elementStyles: styles }: { elementStyles: ElementStyle[] }) => { const editorEngine = useEditorEngine(); const [showGroup, setShowGroup] = useState(false); const [elementStyles, setElementStyles] = useState(styles); - const constructChangeMultiple = elementStyles.map((style) => - constructChangeCurried(style.value), - ); useEffect(() => { setElementStyles(styles); @@ -55,15 +51,6 @@ const NestedInputs = observer(({ elementStyles: styles }: { elementStyles: Eleme const onTopValueChanged = (key: string, value: string) => { setElementStyles(elementStyles.map((style) => ({ ...style, value }))); - elementStyles.forEach((elementStyle) => { - if (elementStyle.key === key) { - return; - } - editorEngine.style.updateElementStyle( - elementStyle.key, - constructChangeMultiple[elementStyles.indexOf(elementStyle)](value), - ); - }); }; function renderTopInputs(elementStyle: ElementStyle) { @@ -119,7 +106,7 @@ const NestedInputs = observer(({ elementStyles: styles }: { elementStyles: Eleme return (
{elementStyles.map((elementStyle) => - VALID_KEYS.includes(elementStyle.key) + TOP_ELEMENT_KEYS.includes(elementStyle.key) ? renderTopInputs(elementStyle) : renderBottomInputs(elementStyle), )} diff --git a/demos/remix/app/routes/_index.tsx b/demos/remix/app/routes/_index.tsx index b6d719f2a..b7a1817f4 100644 --- a/demos/remix/app/routes/_index.tsx +++ b/demos/remix/app/routes/_index.tsx @@ -2,54 +2,54 @@ import type { MetaFunction } from "@remix-run/node"; import React, { Fragment } from "react"; export const meta: MetaFunction = () => { - return [ - { title: "New Remix App" }, - { name: "description", content: "Welcome to Remix!" }, - ]; + return [ + { title: "New Remix App" }, + { name: "description", content: "Welcome to Remix!" }, + ]; }; export default function Index() { - return ( -
- <> - - -

Welcome to Remix

-
-
- - -
- ); + return ( +
+ <> + + +

Welcome to Remix

+
+
+ + +
+ ); }