Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fixed saving from the Figma plugin, which did nothing and logged `SecurityError: Failed to read the 'localStorage' property from 'Window'`. The editor runs inside Figma's sandboxed `about:srcdoc` frame, where reading `localStorage` is denied, and the save's rate limiter read it on every push and threw before the save could run. Storage access now falls back to an in-memory store when the browser blocks it, so saving from the Figma Desktop app works again.
- Fixed the Oxygen Classic builder hanging on its loading screen when the active preset contained custom fonts. The font list was written into Oxygen's `ng-init` attribute without HTML-escaping, so the first quote closed the attribute and left AngularJS with a truncated expression that never finished loading the builder. The value is now escaped as Oxygen's own core does.
- Stopped disabled fonts from reaching the Oxygen and Bricks builders. The Oxygen font dropdown and the styles injected into both builders now cover only the fonts you have enabled, matching what the editor previews.
- Fixed local fonts whose family name contains a space (for example Source Sans 3) never applying on the front end. WordPress stores the uploaded file under a sanitized name (spaces become dashes), but the generated `@font-face` `src` kept the spaces, so it pointed at a file that returns 404 and the browser dropped the font silently. The upload, the generated CSS, and deletion now all use the same sanitized file name. Re-save an affected font once after updating to regenerate its CSS.

## [2.0.1] - 2026-08-18

Expand Down
Binary file not shown.
40 changes: 40 additions & 0 deletions packages/core/src/components/modules/fonts/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,46 @@ export const mergeRootSelectors = (cssString: string): string => {
return mergedRoot + cleanedCss;
};

/**
* Sanitize a locally-stored font file name so it matches the file WordPress
* actually writes to disk.
*
* The WordPress upload endpoint runs the name through `sanitize_file_name()`,
* which collapses runs of whitespace and hyphens to a single `-` and strips a
* set of special characters. When a font family contains a space (e.g.
* "Source Sans 3") the raw name and the on-disk name diverge, so a
* `src: url(...)` built from the raw family points at a file that does not
* exist and the browser silently drops the `@font-face`.
*
* This mirrors the transforms `sanitize_file_name()` applies, so its output is
* a fixed point of that function: whatever the client sends is left unchanged
* by the server, and the generated CSS URL matches the stored file.
*/
export const sanitizeFontFileName = (name: string): string => {
// Special characters WordPress strips (wp-includes/formatting.php). Spaces
// are NOT in this set: they are collapsed to "-" by the step below, exactly
// as WordPress does.
const specialChars = new RegExp(
"[?\\[\\]/\\\\=<>:;,'\"&$#*()|~`!{}%+\\u2019\\u00ab\\u00bb\\u201d\\u201c\\u0000]",
"g",
);

return name
.replace(/ /g, " ")
.replace(specialChars, "")
.replace(/%20|\+/g, "-")
.replace(/[\r\n\t -]+/g, "-")
.replace(/^[.\-_]+|[.\-_]+$/g, "");
};

/**
* Build the `.woff2` file name for a locally-stored font variant, matching the
* name WordPress writes on upload. Single source of truth for both the upload
* request and the generated `@font-face` `src`, so they can never diverge.
*/
export const localFontFileName = (family: string, variantId: string): string =>
sanitizeFontFileName(`${family}-${variantId}.woff2`);

export const applyFontToStylesheet = (fontFamily: string): void => {
const fontUrl = `https://fonts.googleapis.com/css2?family=${fontFamily?.replace(" ", "+")}`;
const linkElement = document.createElement("link");
Expand Down
4 changes: 2 additions & 2 deletions packages/wp/src/components/modules/fonts/FontsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { FontNotFound } from "@core-framework/core/components/modules/fonts/components/FontNotFound";
import { FontsList } from "./components/FontsList";
import { FontData, FontVariantData } from "@core-framework/core/components/modules/fonts/types";
import { blobToBase64, generateFontFaceCSS } from "./utils/utils";
import { blobToBase64, generateFontFaceCSS, localFontFileName } from "./utils/utils";

enum Tabs {
USER_FONTS = "user_fonts",
Expand Down Expand Up @@ -67,7 +67,7 @@ export function FontsTab() {

return {
font_base64: fontBase64.split(",")[1],
filename: `${font.family}-${variant}.woff2`,
filename: localFontFileName(font.family, variant),
};
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { GoogleFontLogo } from "../../../../assets/icons/GoogleFontLogo.icon";
import { Remove } from "../../../../assets/icons/Remove.icon";
import { ClassHeader } from "../../../ClassHeader";
import { FontData, FontVariantData } from "@core-framework/core/components/modules/fonts/types";
import { generateFontFaceCSS } from "../utils/utils";
import { generateFontFaceCSS, localFontFileName } from "../utils/utils";
import { Switch } from "@mantine/core";
import clsx from "clsx";
import { useAtom } from "jotai/index";
Expand Down Expand Up @@ -72,7 +72,7 @@ export function FontsList({ googleFonts }: { googleFonts: FontFace[] }) {
"X-WP-Nonce": window.wpApiSettings.nonce,
},
body: JSON.stringify({
fonts: selectedVariants.map((v: string) => ({ filename: `${font.family}-${v}.woff2` })),
fonts: selectedVariants.map((v: string) => ({ filename: localFontFileName(font.family, v) })),
}),
});
const result = await response.json();
Expand Down
5 changes: 3 additions & 2 deletions packages/wp/src/components/modules/fonts/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { getFontProps, mergeRootSelectors, applyFontToStylesheet } from '@core-framework/core/components/modules/fonts/utils/utils';
export { getFontProps, mergeRootSelectors, applyFontToStylesheet, sanitizeFontFileName, localFontFileName } from '@core-framework/core/components/modules/fonts/utils/utils';
import { localFontFileName } from '@core-framework/core/components/modules/fonts/utils/utils';
import { FontData, FontVariantData } from '@core-framework/core/components/modules/fonts/types';
import { Font } from "fontkit";
import * as fontkit from "fontkit";
Expand Down Expand Up @@ -36,7 +37,7 @@ ${selectedFont.customSelectors} {
const weight = variant.id.match(/\d{3}/)?.[0] || "400";
const style = variant.id.includes("italic") ? "italic" : "normal";
const subPath = getCustomSubpath();
const fontURL = `${subPath}/wp-content/uploads/core-framework/fonts/${selectedFont.family}-${variant.id}.woff2`;
const fontURL = `${subPath}/wp-content/uploads/core-framework/fonts/${localFontFileName(selectedFont.family, variant.id)}`;
const comment = variant.comment ? `\n//${variant.comment}` : "";
const cssSelector = variant.cssSelector.length
? `
Expand Down
Loading