-
Notifications
You must be signed in to change notification settings - Fork 1.1k
refactor: allow undo redo action with font #1751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
26e4507
108784d
b0bde8e
7f520a8
4f25fb1
659f2b7
83be89c
41921e9
de9d51e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ import { | |
removeFontsFromClassName, | ||
} from './utils'; | ||
import type { TraverseCallback } from './types'; | ||
import type { CodeDiff } from '@onlook/models/code'; | ||
|
||
/** | ||
* Traverses JSX elements in a file to find and modify className attributes | ||
|
@@ -33,6 +34,7 @@ export async function traverseClassName( | |
|
||
const content = await readFile(filePath); | ||
if (!content) { | ||
console.error(`Failed to read file: ${filePath}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error logging for a missing file read is duplicated here. Consider refactoring the file reading logic into a helper to avoid repetition. |
||
return; | ||
} | ||
|
||
|
@@ -124,6 +126,7 @@ export async function addFontVariableToElement( | |
|
||
const content = await readFile(filePath); | ||
if (!content) { | ||
console.error(`Failed to read file: ${filePath}`); | ||
return; | ||
} | ||
|
||
|
@@ -235,6 +238,7 @@ export async function removeFontVariableFromLayout( | |
|
||
const content = await readFile(filePath); | ||
if (!content) { | ||
console.error(`Failed to read file: ${filePath}`); | ||
return; | ||
} | ||
|
||
|
@@ -293,11 +297,17 @@ export async function updateFontInLayout( | |
filePath: string, | ||
font: Font, | ||
targetElements: string[], | ||
): Promise<void> { | ||
): Promise<null | CodeDiff> { | ||
let updatedAst = false; | ||
const fontClassName = `font-${font.id}`; | ||
let result = null; | ||
|
||
const content = await readFile(filePath); | ||
if (!content) { | ||
console.error(`Failed to read file: ${filePath}`); | ||
return null; | ||
Kitenite marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
await traverseClassName(filePath, targetElements, (classNameAttr, ast) => { | ||
if (t.isStringLiteral(classNameAttr.value)) { | ||
classNameAttr.value = createStringLiteralWithFont( | ||
|
@@ -322,13 +332,16 @@ export async function updateFontInLayout( | |
} | ||
if (updatedAst) { | ||
const { code } = generate(ast); | ||
result = code; | ||
const codeDiff: CodeDiff = { | ||
original: content, | ||
generated: code, | ||
path: filePath, | ||
}; | ||
result = codeDiff; | ||
} | ||
}); | ||
|
||
if (result) { | ||
fs.writeFileSync(filePath, result); | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
|
@@ -399,10 +412,10 @@ export async function setDefaultFont(projectRoot: string, font: Font) { | |
|
||
if (routerConfig.type === 'app') { | ||
const layoutPath = pathModule.join(routerConfig.basePath, 'layout.tsx'); | ||
await updateFontInLayout(layoutPath, font, ['html']); | ||
return await updateFontInLayout(layoutPath, font, ['html']); | ||
} else { | ||
const appPath = pathModule.join(routerConfig.basePath, '_app.tsx'); | ||
await updateFontInLayout(appPath, font, ['div', 'main', 'section', 'body']); | ||
return await updateFontInLayout(appPath, font, ['div', 'main', 'section', 'body']); | ||
} | ||
} catch (error) { | ||
console.error('Error setting default font:', error); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import { subscribe, type AsyncSubscription } from '@parcel/watcher'; | ||
import { DefaultSettings } from '@onlook/models/constants'; | ||
import { DefaultSettings, MainChannels } from '@onlook/models/constants'; | ||
import * as pathModule from 'path'; | ||
import { scanFonts } from './scanner'; | ||
import fs from 'fs'; | ||
|
@@ -8,16 +8,20 @@ import { removeFontVariableFromLayout } from './layout'; | |
import { removeFontFromTailwindConfig, updateTailwindFontConfig } from './tailwind'; | ||
import type { Font } from '@onlook/models/assets'; | ||
import { detectRouterType } from '../../pages'; | ||
import { mainWindow } from '../../index'; | ||
|
||
export class FontFileWatcher { | ||
private subscription: AsyncSubscription | null = null; | ||
private previousFonts: Font[] = []; | ||
private selfModified: Set<string> = new Set(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think eventually we want a generic file watcher that any service can subscribe to instead of having the same pattern duplicated in multiple places. Out of scope for now. |
||
|
||
async watch(projectRoot: string) { | ||
await this.clearSubscription(); | ||
|
||
const fontPath = pathModule.resolve(projectRoot, DefaultSettings.FONT_CONFIG); | ||
const fontDir = pathModule.dirname(fontPath); | ||
const _fontPath = pathModule.resolve(projectRoot, DefaultSettings.FONT_CONFIG); | ||
const fontDir = pathModule.dirname(_fontPath); | ||
let _layoutPath: string = ''; | ||
let _appPath: string = ''; | ||
|
||
if (!fs.existsSync(fontDir)) { | ||
console.error(`Font directory does not exist: ${fontDir}`); | ||
|
@@ -30,6 +34,15 @@ export class FontFileWatcher { | |
this.previousFonts = []; | ||
} | ||
|
||
const routerConfig = await detectRouterType(projectRoot); | ||
if (routerConfig) { | ||
if (routerConfig.type === 'app') { | ||
_layoutPath = pathModule.join(routerConfig.basePath, 'layout.tsx'); | ||
} else { | ||
_appPath = pathModule.join(routerConfig.basePath, '_app.tsx'); | ||
} | ||
} | ||
|
||
try { | ||
this.subscription = await subscribe( | ||
fontDir, | ||
|
@@ -42,10 +55,16 @@ export class FontFileWatcher { | |
if (events.length > 0) { | ||
for (const event of events) { | ||
const eventPath = pathModule.normalize(event.path); | ||
const expectedPath = pathModule.normalize(fontPath); | ||
const fontPath = pathModule.normalize(_fontPath); | ||
const layoutPath = pathModule.normalize(_layoutPath); | ||
const appPath = pathModule.normalize(_appPath); | ||
if (this.selfModified.has(eventPath)) { | ||
this.selfModified.delete(eventPath); | ||
continue; | ||
} | ||
|
||
if ( | ||
(eventPath === expectedPath || | ||
(eventPath === fontPath || | ||
eventPath.endsWith(DefaultSettings.FONT_CONFIG)) && | ||
(event.type === 'update' || event.type === 'create') | ||
) { | ||
|
@@ -55,6 +74,17 @@ export class FontFileWatcher { | |
console.error('Error syncing fonts with configs:', error); | ||
} | ||
} | ||
if ( | ||
(eventPath === layoutPath || eventPath === appPath) && | ||
(event.type === 'update' || event.type === 'create') | ||
) { | ||
this.selfModified.add(eventPath); | ||
try { | ||
mainWindow?.webContents.send(MainChannels.GET_DEFAULT_FONT); | ||
} catch (error) { | ||
console.error('Error syncing fonts with configs:', error); | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
|
@@ -78,17 +108,19 @@ export class FontFileWatcher { | |
); | ||
|
||
const addedFonts = currentFonts.filter( | ||
(currFont) => !this.previousFonts.some((prevFont) => prevFont.id === currFont.id), | ||
(currFont) => !this.previousFonts.some((prevFont) => currFont.id === prevFont.id), | ||
); | ||
|
||
for (const font of removedFonts) { | ||
const routerConfig = await detectRouterType(projectRoot); | ||
if (routerConfig) { | ||
if (routerConfig.type === 'app') { | ||
const layoutPath = pathModule.join(routerConfig.basePath, 'layout.tsx'); | ||
this.selfModified.add(layoutPath); | ||
await removeFontVariableFromLayout(layoutPath, font.id, ['html']); | ||
} else { | ||
const appPath = pathModule.join(routerConfig.basePath, '_app.tsx'); | ||
this.selfModified.add(appPath); | ||
await removeFontVariableFromLayout(appPath, font.id, [ | ||
'div', | ||
'main', | ||
|
@@ -98,16 +130,40 @@ export class FontFileWatcher { | |
} | ||
} | ||
|
||
const tailwindConfigPath = pathModule.join(projectRoot, 'tailwind.config.ts'); | ||
this.selfModified.add(tailwindConfigPath); | ||
await removeFontFromTailwindConfig(projectRoot, font); | ||
} | ||
|
||
if (addedFonts.length > 0) { | ||
for (const font of addedFonts) { | ||
const tailwindConfigPath = pathModule.join(projectRoot, 'tailwind.config.ts'); | ||
this.selfModified.add(tailwindConfigPath); | ||
await updateTailwindFontConfig(projectRoot, font); | ||
await addFontVariableToLayout(projectRoot, font.id); | ||
|
||
const routerConfig = await detectRouterType(projectRoot); | ||
if (routerConfig) { | ||
if (routerConfig.type === 'app') { | ||
const layoutPath = pathModule.join(routerConfig.basePath, 'layout.tsx'); | ||
this.selfModified.add(layoutPath); | ||
await addFontVariableToLayout(projectRoot, font.id); | ||
} else { | ||
const appPath = pathModule.join(routerConfig.basePath, '_app.tsx'); | ||
this.selfModified.add(appPath); | ||
await addFontVariableToLayout(projectRoot, font.id); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (removedFonts.length > 0 || addedFonts.length > 0) { | ||
mainWindow?.webContents.send(MainChannels.FONTS_CHANGED, { | ||
currentFonts, | ||
removedFonts, | ||
addedFonts, | ||
}); | ||
} | ||
|
||
this.previousFonts = currentFonts; | ||
} catch (error) { | ||
console.error('Error syncing fonts:', error); | ||
|
Uh oh!
There was an error while loading. Please reload this page.