Skip to content

Commit 7a2c75a

Browse files
authored
Add: Google Fonts
1 parent c1eb4d1 commit 7a2c75a

1 file changed

Lines changed: 83 additions & 9 deletions

File tree

index.html

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
#scene-editor-preview.layout-image-as-bg { align-items: flex-end; }
187187
#scene-editor-preview, #scene-editor-preview #preview-player-inner-container { background-size: cover; background-position: center; }
188188
#scene-editor-preview #preview-player-inner-container { width: 100%; max-width: 800px; margin: 20px; box-shadow: 0 5px 20px rgba(0,0,0,.5); gap: 20px; padding: var(--padding); border-radius: 12px; box-sizing: border-box; background-color: var(--container-bg-color); display: flex; }
189-
#scene-editor-preview .choice-btn { padding: 15px 20px; pointer-events: none; background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid var(--btn-color); text-align: left; border-radius: 6px; font-size: 1em; }
189+
#scene-editor-preview .choice-btn { padding: 15px 20px; pointer-events: none; background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid var(--btn-color); text-align: left; border-radius: 6px; font-size: 1em; font-family: var(--font-family); }
190190
#scene-editor-preview #preview-player-image { max-width: 100%; max-height: 50vh; display: block; margin: 0 auto; border-radius: 8px; object-fit: cover; }
191191
#scene-editor-preview #preview-player-text { white-space: pre-wrap; margin-bottom: 20px; line-height: 1.6; }
192192
#scene-editor-preview #preview-player-choices { display: flex; flex-direction: column; gap: 12px; }
@@ -245,7 +245,7 @@
245245
#player-text { white-space: pre-wrap; margin-bottom: 20px; line-height: 1.6; }
246246
#player-text a { color: #8ab4f8; }
247247
#player-choices { display: flex; flex-direction: column; gap: 12px; }
248-
#player-choices .choice-btn { width: 100%; text-align: left; padding: 15px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; transition: background-color .2s, transform .1s, border-color .2s; background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid transparent; }
248+
#player-choices .choice-btn { width: 100%; text-align: left; padding: 15px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; transition: background-color .2s, transform .1s, border-color .2s; background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid transparent; font-family: var(--font-family); }
249249
#player-choices .choice-btn:hover { transform: translateY(-2px); background-color: var(--btn-hover-color); }
250250
#player-choices .choice-btn:active { transform: translateY(1px); }
251251
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@@ -622,12 +622,14 @@ <h3>Manage Variables</h3>
622622
<meta charset="UTF-8">
623623
<meta name="viewport" content="width=device-width, initial-scale=1.0">
624624
<title>__STORY_TITLE__</title>
625+
<link rel="preconnect" href="https://fonts.googleapis.com">
626+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
627+
__GOOGLE_FONT_LINK__
625628
<style>
626629
:root { --primary-color: #4a78ad; --secondary-color: #6c757d; }
627630
* { box-sizing: border-box; }
628631
body {
629632
margin: 0;
630-
font-family: sans-serif;
631633
display: flex;
632634
justify-content: center;
633635
min-height: 100vh;
@@ -817,6 +819,40 @@ <h2 id="about-title"></h2>
817819
}
818820
},
819821
elements: {},
822+
constants: {
823+
GOOGLE_FONTS: [
824+
{ name: "Default System Font", value: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif' },
825+
{ name: "Architects Daughter", value: "'Architects Daughter', cursive" },
826+
{ name: "Asset", value: "'Asset', cursive" },
827+
{ name: "Atma", value: "'Atma', cursive" },
828+
{ name: "Autour One", value: "'Autour One', cursive" },
829+
{ name: "Averia Libre", value: "'Averia Libre', cursive" },
830+
{ name: "Big Shoulders Text", value: "'Big Shoulders Text', cursive" },
831+
{ name: "Calistoga", value: "'Calistoga', cursive" },
832+
{ name: "Caveat Brush", value: "'Caveat Brush', cursive" },
833+
{ name: "Comic Neue", value: "'Comic Neue', cursive" },
834+
{ name: "Gamja Flower", value: "'Gamja Flower', cursive" },
835+
{ name: "Inconsolata", value: "'Inconsolata', monospace" },
836+
{ name: "Just Another Hand", value: "'Just Another Hand', cursive" },
837+
{ name: "Lato", value: "'Lato', sans-serif" },
838+
{ name: "Limelight", value: "'Limelight', cursive" },
839+
{ name: "Lora", value: "'Lora', serif" },
840+
{ name: "Merriweather", value: "'Merriweather', serif" },
841+
{ name: "Montserrat", value: "'Montserrat', sans-serif" },
842+
{ name: "Nothing You Could Do", value: "'Nothing You Could Do', cursive" },
843+
{ name: "Nunito", value: "'Nunito', sans-serif" },
844+
{ name: "Open Sans", value: "'Open Sans', sans-serif" },
845+
{ name: "Pirata One", value: "'Pirata One', cursive" },
846+
{ name: "Playfair Display", value: "'Playfair Display', serif" },
847+
{ name: "Poiret One", value: "'Poiret One', cursive" },
848+
{ name: "Road Rage", value: "'Road Rage', cursive" },
849+
{ name: "Roboto", value: "'Roboto', sans-serif" },
850+
{ name: "Smokum", value: "'Smokum', cursive" },
851+
{ name: "Snowburst One", value: "'Snowburst One', cursive" },
852+
{ name: "Source Code Pro", value: "'Source Code Pro', monospace" },
853+
{ name: "Special Elite", value: "'Special Elite', cursive" }
854+
]
855+
},
820856

821857
init() {
822858
this.cacheElements();
@@ -1562,6 +1598,7 @@ <h2 id="about-title"></h2>
15621598
// Populate the application with the new project data
15631599
App.state.storyData = storyData;
15641600
App.state.projectDirectoryHandle = dirHandle;
1601+
App.styles.loadGoogleFont(App.state.storyData.meta.styles['--font-family']);
15651602
App.modals.hideAll();
15661603
App.ui.enableEditor(true);
15671604
App.ui.updateMetaInputs();
@@ -1660,6 +1697,17 @@ <h2 id="about-title"></h2>
16601697
storyJsonString = JSON.stringify(storyData);
16611698
}
16621699

1700+
let googleFontLink = '';
1701+
const fontFamily = storyData.meta.styles['--font-family'] || App.styles.getDefaults()['--font-family'];
1702+
const fontName = fontFamily.split(',')[0].replace(/'/g, '').trim();
1703+
const isGoogleFont = App.constants.GOOGLE_FONTS.find(f => f.name === fontName && !f.value.startsWith('-'));
1704+
1705+
if (isGoogleFont) {
1706+
const formattedFontName = fontName.replace(/ /g, '+');
1707+
googleFontLink = `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=${formattedFontName}:wght@400;700&display=swap">`;
1708+
}
1709+
1710+
16631711
const playerScript = `
16641712
<script>
16651713
document.addEventListener('DOMContentLoaded', () => {
@@ -1914,6 +1962,7 @@ <h2 id="about-title"></h2>
19141962
return document.getElementById('player-template').innerHTML
19151963
.replace(/__STORY_TITLE__/g, storyData.meta.title || 'My Story')
19161964
.replace(/__LAYOUT_CLASS__/g, storyData.meta.layout)
1965+
.replace('__GOOGLE_FONT_LINK__', googleFontLink)
19171966
.replace('__CUSTOM_STYLES__', App.styles.generateCSS(storyData.meta.styles))
19181967
.replace('__PLAYER_SCRIPT__', playerScript);
19191968
},
@@ -2320,8 +2369,6 @@ <h2 id="about-title"></h2>
23202369

23212370
editor: {
23222371
render() {
2323-
// FIX: Add optional chaining (?.) to safely access .scenes.
2324-
// This prevents a crash if storyData exists but .scenes does not (e.g., during project loading).
23252372
const scenes = App.state.storyData?.scenes;
23262373
const scene = scenes ? scenes[App.state.currentSceneId] : null;
23272374

@@ -2458,7 +2505,6 @@ <h2 id="about-title"></h2>
24582505

24592506
App.setDirty(true);
24602507
App.ui.invalidateNavViews();
2461-
// App.ui.renderCurrentView();
24622508

24632509
if (showAlert) console.log(`Scene '${App.state.currentSceneId}' changes saved to memory.`);
24642510
},
@@ -2915,9 +2961,12 @@ <h2 id="about-title"></h2>
29152961
const grid = document.getElementById('style-editor-grid');
29162962
const styles = App.state.storyData.meta.styles;
29172963
const defaults = this.getDefaults();
2964+
2965+
const fontOptions = App.constants.GOOGLE_FONTS.map(font => `<option value="${font.value}">${font.name}</option>`).join('');
2966+
29182967
grid.innerHTML = `
29192968
<div class="grid-full-width"> <label>Layout <select id="style-layout"><option value="layout-top-down">Image on Top</option><option value="layout-side-by-side">Image on Side</option><option value="layout-image-as-bg">Image as Background</option></select></label> </div>
2920-
<div class="grid-span-2"> <label>Font Family <select id="style-font-family"><option value="sans-serif">Sans-Serif</option><option value="serif">Serif</option><option value="monospace">Monospace</option></select></label> <label>Font Size (px) <input type="number" id="style-font-size" min="8" max="48" step="1"></label> </div>
2969+
<div class="grid-span-2"> <label>Font Family <select id="style-font-family">${fontOptions}</select></label> <label>Font Size (px) <input type="number" id="style-font-size" min="8" max="48" step="1"></label> </div>
29212970
<div class="grid-span-2"> <label>Text Color <input type="color" id="style-text-color"></label> <label>Typewriter Effect <select id="style-typewriter-effect"><option value="off">Off</option><option value="on">On</option></select></label> </div>
29222971
<hr class="grid-separator">
29232972
<label>Page BG Color <input type="color" id="style-bg-color"></label>
@@ -2959,13 +3008,38 @@ <h2 id="about-title"></h2>
29593008
else if (id === 'layout') { App.state.storyData.meta.layout = value; }
29603009
else { App.state.storyData.meta.styles[`--${id}`] = (target.type === 'number') ? value + 'px' : value; }
29613010

3011+
if (id === 'font-family') { this.loadGoogleFont(value); }
29623012
if (id === 'music-volume') App.elements['music-player'].volume = parseFloat(target.value);
29633013
else if (id === 'ambience-volume') App.elements['ambience-player'].volume = parseFloat(target.value);
29643014

29653015
App.setDirty(true);
29663016
if (App.elements['player-container'].style.display === 'flex') App.player.renderScene(App.state.currentSceneId);
29673017
if (!App.elements['editor-fieldset'].disabled) App.editor.renderPreview();
29683018
},
3019+
loadGoogleFont(fontFamily) {
3020+
const fontName = fontFamily.split(',')[0].replace(/'/g, '').trim();
3021+
const isGoogleFont = App.constants.GOOGLE_FONTS.find(f => f.name === fontName && !f.value.startsWith('-'));
3022+
3023+
const existingLink = document.getElementById('google-font-stylesheet');
3024+
if (!isGoogleFont) {
3025+
if (existingLink) existingLink.remove();
3026+
return;
3027+
}
3028+
3029+
const formattedFontName = fontName.replace(/ /g, '+');
3030+
const fontUrl = `https://fonts.googleapis.com/css2?family=${formattedFontName}:wght@400;700&display=swap`;
3031+
3032+
let link = existingLink;
3033+
if (!link) {
3034+
link = document.createElement('link');
3035+
link.id = 'google-font-stylesheet';
3036+
link.rel = 'stylesheet';
3037+
document.head.appendChild(link);
3038+
}
3039+
if (link.href !== fontUrl) {
3040+
link.href = fontUrl;
3041+
}
3042+
},
29693043
applyToContainer(container) {
29703044
const meta = App.state.storyData.meta; const styles = meta.styles; const defaults = this.getDefaults();
29713045
const innerContainer = container.querySelector('#preview-player-inner-container, #player-inner-container');
@@ -3000,12 +3074,12 @@ <h2 id="about-title"></h2>
30003074
generateCSS(stylesOverride) {
30013075
const styles = stylesOverride || App.state.storyData.meta.styles; const defaults = this.getDefaults(); let rootVars = '';
30023076
Object.keys(defaults).forEach(key => { if (key.startsWith('--') && !key.includes('path') && !key.includes('image')) rootVars += ` ${key}: ${styles[key] || defaults[key]};\n`; });
3003-
let css = `:root {\n${rootVars}} body { background-color: var(--bg-color); color: var(--text-color); font-family: var(--font-family, sans-serif); } #game-container { background-color: var(--container-bg-color); font-size: var(--font-size); } .choice-btn { background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid transparent; } .choice-btn:hover { background-color: var(--btn-hover-color); }`;
3077+
let css = `:root {\n${rootVars}} body { background-color: var(--bg-color); color: var(--text-color); font-family: var(--font-family); } #game-container { background-color: var(--container-bg-color); font-size: var(--font-size); } .choice-btn { font-family: var(--font-family); background-color: var(--btn-color); color: var(--btn-text-color); border: 1px solid transparent; } .choice-btn:hover { background-color: var(--btn-hover-color); }`;
30043078
if (styles['--screen-bg-image']) css += `body { background-image: url('${styles['--screen-bg-image']}'); }\n`;
30053079
if (styles['--container-bg-image']) css += `#game-container { background-image: url('${styles['--container-bg-image']}'); }\n`;
30063080
return css;
30073081
},
3008-
getDefaults() { return { '--bg-color': '#212529', '--text-color': '#f8f9fa', '--btn-color': 'rgba(108, 117, 125, 1)', '--btn-text-color': '#ffffff', '--btn-hover-color': 'rgba(90, 98, 104, 1)', '--font-family': 'sans-serif', '--font-size': '16px', '--padding': '30px', '--screen-bg-image': '', '--container-bg-image': '', '--container-bg-color': 'rgba(0,0,0,0.15)', '--music-path': '', '--music-volume': '0.4', '--ambience-volume': '0.4', '--typewriter-effect': 'on' }; }
3082+
getDefaults() { return { '--bg-color': '#212529', '--text-color': '#f8f9fa', '--btn-color': 'rgba(108, 117, 125, 1)', '--btn-text-color': '#ffffff', '--btn-hover-color': 'rgba(90, 98, 104, 1)', '--font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', '--font-size': '16px', '--padding': '30px', '--screen-bg-image': '', '--container-bg-image': '', '--container-bg-color': 'rgba(0,0,0,0.15)', '--music-path': '', '--music-volume': '0.4', '--ambience-volume': '0.4', '--typewriter-effect': 'on' }; }
30093083
},
30103084

30113085
modals: {

0 commit comments

Comments
 (0)