Skip to content

Commit

Permalink
Text edit - Write to code (#347)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kitenite authored Sep 14, 2024
1 parent adef501 commit a26f741
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 10 deletions.
1 change: 1 addition & 0 deletions app/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum EditorAttributes {
DATA_ONLOOK_NEW_INDEX = 'data-onlook-new-index',
DATA_ONLOOK_UNIQUE_ID = 'data-onlook-unique-id',
DATA_ONLOOK_EDITING_TEXT = 'data-onlook-editing-text',
DATA_ONLOOK_ORIGINAL_CONTENT = 'data-onlook-original-content',
}

export enum WebviewChannels {
Expand Down
1 change: 1 addition & 0 deletions app/common/models/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface CodeDiffRequest {
insertedElements: InsertedElement[];
movedElements: MovedElementWithTemplate[];
attributes: Record<string, string>;
textContent?: string;
}

export interface CodeDiff {
Expand Down
6 changes: 6 additions & 0 deletions app/common/models/element/domAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export interface MovedElementWithTemplate extends MovedElement {
templateNode: TemplateNode;
}

export interface TextEditedElement {
timestamp: number;
selector: string;
content: string;
}

export interface InsertedElement extends DomActionElement {
type: DomActionType.INSERT;
tagName: string;
Expand Down
5 changes: 1 addition & 4 deletions app/electron/main/code/diff/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,5 @@ function processGroupedRequests(groupedRequests: Map<string, RequestsByPath>): 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);
}
12 changes: 12 additions & 0 deletions app/electron/main/code/diff/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export function transformAst(
if (codeDiffRequest.attributes && codeDiffRequest.attributes.className) {
addClassToNode(path.node, codeDiffRequest.attributes.className);
}
if (codeDiffRequest.textContent) {
updateNodeTextContent(path.node, codeDiffRequest.textContent);
}
const structureChangeElements = getStructureChangeElements(codeDiffRequest);
applyStructureChanges(path, filepath, structureChangeElements);
}
Expand Down Expand Up @@ -191,3 +194,12 @@ function moveElementInNode(
console.error('Element to be moved not found');
}
}

function updateNodeTextContent(node: t.JSXElement, textContent: string): void {
const textNode = node.children.find((child) => t.isJSXText(child)) as t.JSXText | undefined;
if (textNode) {
textNode.value = textContent;
} else {
console.error('Text node not found');
}
}
8 changes: 7 additions & 1 deletion app/electron/preload/webview/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { getElementAtLoc, getElementWithSelector } from './elements';
import { getInsertedElements, getInsertLocation } from './elements/insert';
import { getMovedElements } from './elements/move';
import { drag, endDrag, startDrag } from './elements/move/drag';
import { editText, startEditingText, stopEditingText } from './elements/text';
import {
editText,
getTextEditedElements,
startEditingText,
stopEditingText,
} from './elements/text';

export function setApi() {
contextBridge.exposeInMainWorld('api', {
Expand All @@ -26,5 +31,6 @@ export function setApi() {
startEditingText: startEditingText,
editText: editText,
stopEditingText: stopEditingText,
getTextEditedElements: getTextEditedElements,
});
}
37 changes: 36 additions & 1 deletion app/electron/preload/webview/elements/text.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import { publishEditText } from '../events/publish';
import { getDomElement, restoreElementStyle, saveTimestamp } from './helpers';
import { EditorAttributes } from '/common/constants';
import { getUniqueSelector } from '/common/helpers';
import { TextDomElement } from '/common/models/element';
import { TextEditedElement } from '/common/models/element/domAction';

export function getTextEditedElements(): TextEditedElement[] {
const textEditElements = Array.from(
document.querySelectorAll(`[${EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT}]`),
)
.filter(
(el) =>
el.getAttribute(EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT) !== el.textContent,
)
.map((el) => getTextEditedElement(el as HTMLElement));
return textEditElements;
}

function getTextEditedElement(el: HTMLElement): TextEditedElement {
return {
selector: getUniqueSelector(el),
timestamp: parseInt(el.getAttribute(EditorAttributes.DATA_ONLOOK_TIMESTAMP) || '0'),
content: el.textContent || '',
};
}

export function editTextBySelector(selector: string, content: string): TextDomElement | null {
const el: HTMLElement | null = document.querySelector(selector);
Expand Down Expand Up @@ -46,7 +68,7 @@ export function editText(content: string): TextDomElement | null {
return getTextEditElement(el);
}

export function stopEditingText() {
export function stopEditingText(): void {
const el = getEditingElement();
if (!el) {
return;
Expand Down Expand Up @@ -85,6 +107,10 @@ function prepareElementForEditing(el: HTMLElement) {
el.style.color = 'transparent';
el.setAttribute(EditorAttributes.DATA_ONLOOK_SAVED_STYLE, JSON.stringify(style));
el.setAttribute(EditorAttributes.DATA_ONLOOK_EDITING_TEXT, 'true');

if (!el.hasAttribute(EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT)) {
el.setAttribute(EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT, el.textContent || '');
}
}

function cleanUpElementAfterDragging(el: HTMLElement) {
Expand All @@ -97,3 +123,12 @@ function removeEditingAttributes(el: HTMLElement) {
el.removeAttribute(EditorAttributes.DATA_ONLOOK_SAVED_STYLE);
el.removeAttribute(EditorAttributes.DATA_ONLOOK_EDITING_TEXT);
}

export function clearTextEditedElements() {
const textEditedEls = document.querySelectorAll(
`[${EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT}]`,
);
for (const el of textEditedEls) {
el.removeAttribute(EditorAttributes.DATA_ONLOOK_ORIGINAL_CONTENT);
}
}
3 changes: 2 additions & 1 deletion app/electron/preload/webview/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CssStyleChange } from '../changes';
import { processDom } from '../dom';
import { insertElement, removeElement, removeInsertedElements } from '../elements/insert';
import { clearMovedElements, moveElement } from '../elements/move';
import { editTextBySelector } from '../elements/text';
import { clearTextEditedElements, editTextBySelector } from '../elements/text';
import { listenForDomMutation } from './dom';
import {
publishEditText,
Expand Down Expand Up @@ -79,6 +79,7 @@ function listenForEditEvents() {
change.clearStyleSheet();
removeInsertedElements();
clearMovedElements();
clearTextEditedElements();
setTimeout(processDom, 500);
});
}
29 changes: 28 additions & 1 deletion app/src/lib/editor/engine/code/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AstManager } from '../ast';
import { WebviewManager } from '../webview';
import { EditorAttributes, MainChannels } from '/common/constants';
import { CodeDiff, CodeDiffRequest } from '/common/models/code';
import { InsertedElement, MovedElement } from '/common/models/element/domAction';
import { InsertedElement, MovedElement, TextEditedElement } from '/common/models/element/domAction';
import { TemplateNode } from '/common/models/element/templateNode';

export class CodeManager {
Expand Down Expand Up @@ -35,11 +35,13 @@ export class CodeManager {
const tailwindResults = await this.getTailwindClasses(webview);
const insertedEls = await this.getInsertedElements(webview);
const movedEls = await this.getMovedElements(webview);
const textEditEls = await this.getTextEditElements(webview);

const codeDiffRequest = await this.getCodeDiffRequests(
tailwindResults,
insertedEls,
movedEls,
textEditEls,
);
const codeDiffs = await this.getCodeDiff(codeDiffRequest);
return codeDiffs;
Expand All @@ -66,15 +68,21 @@ export class CodeManager {
return webview.executeJavaScript(`window.api?.getMovedElements()`);
}

private async getTextEditElements(webview: Electron.WebviewTag): Promise<TextEditedElement[]> {
return webview.executeJavaScript(`window.api?.getTextEditedElements()`);
}

private async getCodeDiffRequests(
tailwindResults: ResultCode[],
insertedEls: InsertedElement[],
movedEls: MovedElement[],
textEditEls: TextEditedElement[],
): Promise<Map<TemplateNode, CodeDiffRequest>> {
const templateToRequest = new Map<TemplateNode, CodeDiffRequest>();
await this.processTailwindChanges(tailwindResults, templateToRequest);
await this.processInsertedElements(insertedEls, tailwindResults, templateToRequest);
await this.processMovedElements(movedEls, templateToRequest);
await this.processTextEditElements(textEditEls, templateToRequest);
return templateToRequest;
}

Expand Down Expand Up @@ -155,6 +163,25 @@ export class CodeManager {
}
}

private async processTextEditElements(
textEditEls: TextEditedElement[],
templateToCodeChange: Map<TemplateNode, CodeDiffRequest>,
) {
for (const textEl of textEditEls) {
const templateNode = await this.getTemplateNodeForSelector(textEl.selector);
if (!templateNode) {
continue;
}

const request = await this.getOrCreateCodeDiffRequest(
templateNode,
textEl.selector,
templateToCodeChange,
);
request.textContent = textEl.content;
}
}

private async getTemplateNodeForSelector(selector: string): Promise<TemplateNode | undefined> {
return (
(await this.astManager.getInstance(selector)) ??
Expand Down
7 changes: 7 additions & 0 deletions app/src/lib/editor/engine/overlay/textEdit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class EditTextInput {
this.element.setAttribute('id', EditorAttributes.ONLOOK_RECT_ID);

this.editorView = this.initProseMirror();
this.element.addEventListener('blur', this.handleBlur.bind(this), true);
}

render(
Expand Down Expand Up @@ -120,4 +121,10 @@ export class EditTextInput {
this.onStop && this.onStop();
return true;
}

private handleBlur(event: FocusEvent) {
if (!this.element.contains(event.relatedTarget as Node)) {
this.stopEditor();
}
}
}
3 changes: 2 additions & 1 deletion app/src/lib/editor/engine/text/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DomElement, TextDomElement } from '/common/models/element';

export class TextEditingManager {
isEditing = false;

constructor(
private overlay: OverlayManager,
private history: HistoryManager,
Expand Down Expand Up @@ -45,7 +46,7 @@ export class TextEditingManager {
return;
}
const textDomEl: TextDomElement | null = await webview.executeJavaScript(
`window.api?.editText("${jsStringEscape(newContent)}")`,
`window.api?.editText('${jsStringEscape(newContent)}')`,
);
if (!textDomEl) {
return;
Expand Down
3 changes: 2 additions & 1 deletion app/src/routes/project/TopBar/PublishModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { sendAnalytics } from '@/lib/utils';
import { CodeIcon, ExternalLinkIcon, ShadowIcon } from '@radix-ui/react-icons';
import { observer } from 'mobx-react-lite';
import { useState } from 'react';
import ReactDiffViewer from 'react-diff-viewer-continued';
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued';
import { useEditorEngine } from '../..';
import { MainChannels, WebviewChannels } from '/common/constants';
import { CodeDiff } from '/common/models/code';
Expand Down Expand Up @@ -149,6 +149,7 @@ const PublishModal = observer(() => {
oldValue={item.original}
newValue={item.generated}
splitView={true}
compareMethod={DiffMethod.WORDS_WITH_SPACE}
/>
</div>
</div>
Expand Down

0 comments on commit a26f741

Please sign in to comment.