|
186 | 186 | #scene-editor-preview.layout-image-as-bg { align-items: flex-end; } |
187 | 187 | #scene-editor-preview, #scene-editor-preview #preview-player-inner-container { background-size: cover; background-position: center; } |
188 | 188 | #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); } |
190 | 190 | #scene-editor-preview #preview-player-image { max-width: 100%; max-height: 50vh; display: block; margin: 0 auto; border-radius: 8px; object-fit: cover; } |
191 | 191 | #scene-editor-preview #preview-player-text { white-space: pre-wrap; margin-bottom: 20px; line-height: 1.6; } |
192 | 192 | #scene-editor-preview #preview-player-choices { display: flex; flex-direction: column; gap: 12px; } |
|
245 | 245 | #player-text { white-space: pre-wrap; margin-bottom: 20px; line-height: 1.6; } |
246 | 246 | #player-text a { color: #8ab4f8; } |
247 | 247 | #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); } |
249 | 249 | #player-choices .choice-btn:hover { transform: translateY(-2px); background-color: var(--btn-hover-color); } |
250 | 250 | #player-choices .choice-btn:active { transform: translateY(1px); } |
251 | 251 | @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } |
@@ -622,12 +622,14 @@ <h3>Manage Variables</h3> |
622 | 622 | <meta charset="UTF-8"> |
623 | 623 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
624 | 624 | <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__ |
625 | 628 | <style> |
626 | 629 | :root { --primary-color: #4a78ad; --secondary-color: #6c757d; } |
627 | 630 | * { box-sizing: border-box; } |
628 | 631 | body { |
629 | 632 | margin: 0; |
630 | | - font-family: sans-serif; |
631 | 633 | display: flex; |
632 | 634 | justify-content: center; |
633 | 635 | min-height: 100vh; |
@@ -817,6 +819,40 @@ <h2 id="about-title"></h2> |
817 | 819 | } |
818 | 820 | }, |
819 | 821 | 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 | + }, |
820 | 856 |
|
821 | 857 | init() { |
822 | 858 | this.cacheElements(); |
@@ -1562,6 +1598,7 @@ <h2 id="about-title"></h2> |
1562 | 1598 | // Populate the application with the new project data |
1563 | 1599 | App.state.storyData = storyData; |
1564 | 1600 | App.state.projectDirectoryHandle = dirHandle; |
| 1601 | + App.styles.loadGoogleFont(App.state.storyData.meta.styles['--font-family']); |
1565 | 1602 | App.modals.hideAll(); |
1566 | 1603 | App.ui.enableEditor(true); |
1567 | 1604 | App.ui.updateMetaInputs(); |
@@ -1660,6 +1697,17 @@ <h2 id="about-title"></h2> |
1660 | 1697 | storyJsonString = JSON.stringify(storyData); |
1661 | 1698 | } |
1662 | 1699 |
|
| 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 | + |
1663 | 1711 | const playerScript = ` |
1664 | 1712 | <script> |
1665 | 1713 | document.addEventListener('DOMContentLoaded', () => { |
@@ -1914,6 +1962,7 @@ <h2 id="about-title"></h2> |
1914 | 1962 | return document.getElementById('player-template').innerHTML |
1915 | 1963 | .replace(/__STORY_TITLE__/g, storyData.meta.title || 'My Story') |
1916 | 1964 | .replace(/__LAYOUT_CLASS__/g, storyData.meta.layout) |
| 1965 | + .replace('__GOOGLE_FONT_LINK__', googleFontLink) |
1917 | 1966 | .replace('__CUSTOM_STYLES__', App.styles.generateCSS(storyData.meta.styles)) |
1918 | 1967 | .replace('__PLAYER_SCRIPT__', playerScript); |
1919 | 1968 | }, |
@@ -2320,8 +2369,6 @@ <h2 id="about-title"></h2> |
2320 | 2369 |
|
2321 | 2370 | editor: { |
2322 | 2371 | 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). |
2325 | 2372 | const scenes = App.state.storyData?.scenes; |
2326 | 2373 | const scene = scenes ? scenes[App.state.currentSceneId] : null; |
2327 | 2374 |
|
@@ -2458,7 +2505,6 @@ <h2 id="about-title"></h2> |
2458 | 2505 |
|
2459 | 2506 | App.setDirty(true); |
2460 | 2507 | App.ui.invalidateNavViews(); |
2461 | | - // App.ui.renderCurrentView(); |
2462 | 2508 |
|
2463 | 2509 | if (showAlert) console.log(`Scene '${App.state.currentSceneId}' changes saved to memory.`); |
2464 | 2510 | }, |
@@ -2915,9 +2961,12 @@ <h2 id="about-title"></h2> |
2915 | 2961 | const grid = document.getElementById('style-editor-grid'); |
2916 | 2962 | const styles = App.state.storyData.meta.styles; |
2917 | 2963 | const defaults = this.getDefaults(); |
| 2964 | + |
| 2965 | + const fontOptions = App.constants.GOOGLE_FONTS.map(font => `<option value="${font.value}">${font.name}</option>`).join(''); |
| 2966 | + |
2918 | 2967 | grid.innerHTML = ` |
2919 | 2968 | <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> |
2921 | 2970 | <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> |
2922 | 2971 | <hr class="grid-separator"> |
2923 | 2972 | <label>Page BG Color <input type="color" id="style-bg-color"></label> |
@@ -2959,13 +3008,38 @@ <h2 id="about-title"></h2> |
2959 | 3008 | else if (id === 'layout') { App.state.storyData.meta.layout = value; } |
2960 | 3009 | else { App.state.storyData.meta.styles[`--${id}`] = (target.type === 'number') ? value + 'px' : value; } |
2961 | 3010 |
|
| 3011 | + if (id === 'font-family') { this.loadGoogleFont(value); } |
2962 | 3012 | if (id === 'music-volume') App.elements['music-player'].volume = parseFloat(target.value); |
2963 | 3013 | else if (id === 'ambience-volume') App.elements['ambience-player'].volume = parseFloat(target.value); |
2964 | 3014 |
|
2965 | 3015 | App.setDirty(true); |
2966 | 3016 | if (App.elements['player-container'].style.display === 'flex') App.player.renderScene(App.state.currentSceneId); |
2967 | 3017 | if (!App.elements['editor-fieldset'].disabled) App.editor.renderPreview(); |
2968 | 3018 | }, |
| 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 | + }, |
2969 | 3043 | applyToContainer(container) { |
2970 | 3044 | const meta = App.state.storyData.meta; const styles = meta.styles; const defaults = this.getDefaults(); |
2971 | 3045 | const innerContainer = container.querySelector('#preview-player-inner-container, #player-inner-container'); |
@@ -3000,12 +3074,12 @@ <h2 id="about-title"></h2> |
3000 | 3074 | generateCSS(stylesOverride) { |
3001 | 3075 | const styles = stylesOverride || App.state.storyData.meta.styles; const defaults = this.getDefaults(); let rootVars = ''; |
3002 | 3076 | 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); }`; |
3004 | 3078 | if (styles['--screen-bg-image']) css += `body { background-image: url('${styles['--screen-bg-image']}'); }\n`; |
3005 | 3079 | if (styles['--container-bg-image']) css += `#game-container { background-image: url('${styles['--container-bg-image']}'); }\n`; |
3006 | 3080 | return css; |
3007 | 3081 | }, |
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' }; } |
3009 | 3083 | }, |
3010 | 3084 |
|
3011 | 3085 | modals: { |
|
0 commit comments