reload custom css

This commit is contained in:
Julie Blanc 2026-03-09 15:37:05 +01:00
parent 9c58edb425
commit b544928a65
7 changed files with 115 additions and 39 deletions

View file

@ -161,7 +161,7 @@
.element-selected {
outline: 2px dashed #7136ff !important;
background-color: #7136ff1a !important;
/* background-color: #7136ff1a !important; */
}
.element-hover-label {

View file

@ -31,21 +31,30 @@ body {
}
p {
font-size: 14px;
font-size: 22px;
line-height: 18px;
}
h1 {
font-size: 38px;
font-size: 28px;
line-height: 38px;
background: rgba(255, 255, 255, 0.521);
background: rgba(60, 143, 66, 0.52);
padding-top: 10px;
padding-right: 20px;
padding-bottom: 10px;
padding-left: 20px;
margin-top: 60px;
margin-bottom: 30px;
font-family: "Montserrat";
font-family: "EB Garamond";
font-weight: normal;
margin-right: 0mm;
margin-left: 0mm;
color: rgb(224, 9, 9);
border-style: solid;
border-color: #e01b1b;
border-width: 3px;
text-align: right;
font-style: italic;
}
em {
@ -68,6 +77,54 @@ a {
color: #0000ee;
text-decoration-line: underline;
}
h5 {
font-weight: bold;
}
h4 {
font-size: 20px;
background: #dedede;
font-weight: bold;
text-align: center;
margin-bottom: 30px;
}
h3 {
font-size: 20px;
border-width: 2px;
font-weight: bold;
border-style: dotted;
border-color: #000000;
margin-top: 30px;
margin-bottom: 20px;
}
h2 {
font-size: 38px;
border-width: 2px;
font-weight: bold;
border-style: solid;
border-color: #000000;
padding-top: 20px;
padding-right: 20px;
padding-bottom: 20px;
padding-left: 20px;
margin-top: 36px;
}
p.author {
font-size: 24px;
background: rgba(255, 255, 255, 0.521);
font-weight: bold;
padding-top: 10px;
padding-right: 20px;
padding-bottom: 10px;
padding-left: 20px;
margin-top: 20px;
margin-right: 0px;
margin-bottom: 0px;
margin-left: 0px;
}
----

View file

@ -14,9 +14,11 @@ import { useKeyboardShortcuts } from './composables/useKeyboardShortcuts';
import { useIframeInteractions } from './composables/useIframeInteractions';
import { usePreviewRenderer } from './composables/usePreviewRenderer';
import { usePrintPreview } from './composables/usePrintPreview';
import { useProjectFonts } from './composables/useProjectFonts';
const stylesheetStore = useStylesheetStore();
const narrativeStore = useNarrativeStore();
const { loadFontsFromCss } = useProjectFonts();
const previewFrame1 = ref(null);
const previewFrame2 = ref(null);
@ -86,6 +88,8 @@ onMounted(async () => {
// Initialize stylesheet with custom CSS
if (narrativeStore.data) {
await stylesheetStore.initializeFromNarrative(narrativeStore.data);
// Pre-load any fonts referenced in the saved CSS so @font-face rules are ready for the preview
await loadFontsFromCss(stylesheetStore.customCss);
}
// Render preview after data is loaded

View file

@ -228,7 +228,9 @@ export function useElementSettings({ margin, padding, basePopup }) {
removeProps(['color']);
stylesheetStore.updateProperty('body', 'color', textDefaults.color);
} else if (group === 'font') {
removeProps(['font-family', 'font-style', 'font-weight']);
removeProps(['font-family', 'font-style']);
// Write font-weight: normal explicitly so applyDefaultsCss() won't re-apply bold defaults on reload
stylesheetStore.updateProperty(selector.value, 'font-weight', 'normal');
const fontVal = textDefaults.fontFamily === 'sans-serif' ? 'sans-serif' : `"${textDefaults.fontFamily}"`;
stylesheetStore.updateProperty('body', 'font-family', fontVal);
}
@ -422,36 +424,14 @@ export function useElementSettings({ margin, padding, basePopup }) {
const specialGroupDiffersFromDefault = (group, parsed) => {
switch (group) {
case 'font': {
if ('font-family' in parsed) {
const val = parsed['font-family'].split(',')[0].trim().replace(/['"]/g, '');
if (val !== textDefaults.fontFamily) return true;
}
if ('font-style' in parsed && parsed['font-style'].trim() === 'italic') return true;
if ('font-weight' in parsed) {
const v = parsed['font-weight'].trim();
if (v === 'bold' || parseInt(v) >= 700) return true;
}
return false;
}
case 'fontSize': {
if ('font-size' in parsed) {
const m = parsed['font-size'].match(/([\d.]+)(px|rem|em|mm|cm|in)?/);
if (m) return parseFloat(m[1]) !== textDefaults.fontSize.value || (m[2] || 'px') !== textDefaults.fontSize.unit;
}
return false;
}
case 'lineHeight': {
if ('line-height' in parsed) {
const m = parsed['line-height'].match(/([\d.]+)(px|rem|em|mm|cm|in)?/);
if (m) return parseFloat(m[1]) !== textDefaults.lineHeight.value || (m[2] || 'px') !== textDefaults.lineHeight.unit;
}
return false;
}
case 'color': {
if ('color' in parsed) return parsed['color'].trim() !== textDefaults.color;
return false;
}
case 'font':
return 'font-family' in parsed || 'font-style' in parsed || 'font-weight' in parsed;
case 'fontSize':
return 'font-size' in parsed;
case 'lineHeight':
return 'line-height' in parsed;
case 'color':
return 'color' in parsed;
}
return false;
};
@ -734,6 +714,13 @@ export function useElementSettings({ margin, padding, basePopup }) {
loadValuesFromStylesheet(true);
// If no session state, sync toggles from custom CSS block (handles page reload with saved CSS)
// Uses extractCustomBlock (not extractBlock) to avoid activating toggles for base stylesheet rules
if (!stored) {
const cssBlock = stylesheetStore.extractCustomBlock(selector.value);
if (cssBlock) syncRefsFromCss(cssBlock);
}
if (stored && stored.values) {
if (settingEnabled.font) {
fontFamily.value = stored.values.fontFamily;

View file

@ -134,6 +134,19 @@ async function loadFont(fontName) {
stylesheetStore.setFontFaceCss(loadedFontsCss.value);
}
async function loadFontsFromCss(css) {
if (!css) return;
const matches = css.matchAll(/font-family:\s*["']?([^"';,\n]+)["']?/gi);
const names = new Set();
for (const m of matches) {
const name = m[1].trim().replace(/['"]/g, '');
if (name && name !== 'sans-serif' && name !== 'serif' && name !== 'monospace') {
names.add(name);
}
}
await Promise.all([...names].map((name) => loadFont(name)));
}
async function loadAllFontPreviews() {
// Only loads fonts into document.fonts for select preview — does NOT update the stylesheet store
const promises = fonts
@ -148,6 +161,7 @@ export function useProjectFonts() {
fonts,
loadFont,
loadAllFontPreviews,
loadFontsFromCss,
loadedFontsCss,
};
}

View file

@ -116,6 +116,11 @@ export const useStylesheetStore = defineStore('stylesheet', () => {
return cssParsingUtils.extractCssBlock(baseCss.value, selector);
};
// Extract block only from custom CSS (used for toggle activation on load)
const extractCustomBlock = (selector) => {
return cssParsingUtils.extractCssBlock(customCss.value, selector);
};
// Replace a CSS block in custom CSS (handles blocks from base CSS too)
const replaceBlock = (oldBlock, newBlock) => {
// Check if the old block exists in custom CSS
@ -369,6 +374,7 @@ export const useStylesheetStore = defineStore('stylesheet', () => {
updateProperty,
extractValue,
extractBlock,
extractCustomBlock,
replaceBlock,
replaceInCustomCss,
setCustomCss,

View file

@ -6,12 +6,20 @@ const extractCssBlock = (css, selector) => {
const extractCssValue = (css, selector, property) => {
const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const regex = new RegExp(
// Try numeric with unit first
const numericRegex = new RegExp(
`${escaped}\\s*{[^}]*${property}:\\s*([\\d.]+)(px|rem|em|mm|cm|in)`,
'i'
);
const match = css.match(regex);
return match ? { value: parseFloat(match[1]), unit: match[2] } : null;
const numericMatch = css.match(numericRegex);
if (numericMatch) return { value: parseFloat(numericMatch[1]), unit: numericMatch[2] };
// Fallback: any string value (colors, keywords, font names, etc.)
const stringRegex = new RegExp(
`${escaped}\\s*\\{[^}]*?${property}:\\s*([^;}\\n]+)`,
'i'
);
const stringMatch = css.match(stringRegex);
return stringMatch ? stringMatch[1].trim() : null;
};
const updateCssValue = ({ css, selector, property, value, unit }) => {