diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..c48f5de Binary files /dev/null and b/.DS_Store differ diff --git a/src/assets/.DS_Store b/src/assets/.DS_Store new file mode 100644 index 0000000..a1b2ef6 Binary files /dev/null and b/src/assets/.DS_Store differ diff --git a/src/assets/VoidPage.js b/src/assets/VoidPage.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/assets/backgrounds/bg1.jpg b/src/assets/backgrounds/bg1.jpg new file mode 100644 index 0000000..b5e33bd Binary files /dev/null and b/src/assets/backgrounds/bg1.jpg differ diff --git a/src/assets/backgrounds/bgs.json b/src/assets/backgrounds/bgs.json new file mode 100644 index 0000000..a818e13 --- /dev/null +++ b/src/assets/backgrounds/bgs.json @@ -0,0 +1,3 @@ +[ + "assets/backgrounds/bg1.jpg" +] \ No newline at end of file diff --git a/src/assets/config.json b/src/assets/config.json index e69de29..3b515b4 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -0,0 +1,60 @@ +{ + "settings": { + "title": "My Startpage", + "backgroundImgPath": "/src/backgrounds/bg1.jpg", + "headerBgColor": "rgba(0, 0, 0, 0.5)", + "headerOutlineColor": "rgba(255, 255, 255, 0.7)", + "headerOutlineThickness": 2, + "cardBgColor": "rgba(255, 255, 255, 0.8)", + "cornerRadius": 10, + "openLinksInNewTab": true, + "searchEngine": { + "name": "DuckDuckGo", + "url": "https://duckduckgo.com/?q=" + } + }, + "cards": [ + { + "title": "Category One", + "iconPath": "/src/icons/category1.png", + "links": [ + { + "name": "Example Link 1", + "url": "https://example1.com" + }, + { + "name": "Example Link 2", + "url": "https://example2.com" + } + ] + }, + { + "title": "Category Two", + "iconPath": "/src/icons/category2.png", + "links": [ + { + "name": "Example Link A", + "url": "https://exampleA.com" + }, + { + "name": "Example Link B", + "url": "https://exampleB.com" + } + ] + }, + { + "title": "Category Three", + "iconPath": "/src/icons/category3.png", + "links": [ + { + "name": "Example Link X", + "url": "https://exampleX.com" + }, + { + "name": "Example Link Y", + "url": "https://exampleY.com" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/assets/fonts/iosevka_ttf/iosevka-bold.ttf b/src/assets/fonts/iosevka_ttf/iosevka-bold.ttf new file mode 100644 index 0000000..b4e3dda Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-bold.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-bolditalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-bolditalic.ttf new file mode 100644 index 0000000..a459aa3 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-bolditalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-boldoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-boldoblique.ttf new file mode 100644 index 0000000..3572bb1 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-boldoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extrabold.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extrabold.ttf new file mode 100644 index 0000000..28a2c48 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extrabold.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extrabolditalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extrabolditalic.ttf new file mode 100644 index 0000000..f19849a Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extrabolditalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extraboldoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extraboldoblique.ttf new file mode 100644 index 0000000..f33d3ad Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extraboldoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extralight.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extralight.ttf new file mode 100644 index 0000000..39d3d3b Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extralight.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extralightitalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extralightitalic.ttf new file mode 100644 index 0000000..6d09d09 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extralightitalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-extralightoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-extralightoblique.ttf new file mode 100644 index 0000000..69c398f Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-extralightoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-heavy.ttf b/src/assets/fonts/iosevka_ttf/iosevka-heavy.ttf new file mode 100644 index 0000000..788b611 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-heavy.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-heavyitalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-heavyitalic.ttf new file mode 100644 index 0000000..1b1aaef Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-heavyitalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-heavyoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-heavyoblique.ttf new file mode 100644 index 0000000..ef19daf Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-heavyoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-italic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-italic.ttf new file mode 100644 index 0000000..f21ea73 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-italic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-light.ttf b/src/assets/fonts/iosevka_ttf/iosevka-light.ttf new file mode 100644 index 0000000..6d65d7e Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-light.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-lightitalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-lightitalic.ttf new file mode 100644 index 0000000..00293fa Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-lightitalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-lightoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-lightoblique.ttf new file mode 100644 index 0000000..9901a8b Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-lightoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-medium.ttf b/src/assets/fonts/iosevka_ttf/iosevka-medium.ttf new file mode 100644 index 0000000..fecc788 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-medium.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-mediumitalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-mediumitalic.ttf new file mode 100644 index 0000000..f282fb4 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-mediumitalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-mediumoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-mediumoblique.ttf new file mode 100644 index 0000000..476121e Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-mediumoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-oblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-oblique.ttf new file mode 100644 index 0000000..5482a33 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-oblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-regular.ttf b/src/assets/fonts/iosevka_ttf/iosevka-regular.ttf new file mode 100644 index 0000000..fe8f88a Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-regular.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-semibold.ttf b/src/assets/fonts/iosevka_ttf/iosevka-semibold.ttf new file mode 100644 index 0000000..e31eb5e Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-semibold.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-semibolditalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-semibolditalic.ttf new file mode 100644 index 0000000..01c62b9 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-semibolditalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-semiboldoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-semiboldoblique.ttf new file mode 100644 index 0000000..73f6bba Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-semiboldoblique.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-thin.ttf b/src/assets/fonts/iosevka_ttf/iosevka-thin.ttf new file mode 100644 index 0000000..d04d02a Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-thin.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-thinitalic.ttf b/src/assets/fonts/iosevka_ttf/iosevka-thinitalic.ttf new file mode 100644 index 0000000..4f93353 Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-thinitalic.ttf differ diff --git a/src/assets/fonts/iosevka_ttf/iosevka-thinoblique.ttf b/src/assets/fonts/iosevka_ttf/iosevka-thinoblique.ttf new file mode 100644 index 0000000..66517bc Binary files /dev/null and b/src/assets/fonts/iosevka_ttf/iosevka-thinoblique.ttf differ diff --git a/src/assets/images/favicon.ico b/src/assets/images/favicon.ico index e69de29..ff67d13 100644 Binary files a/src/assets/images/favicon.ico and b/src/assets/images/favicon.ico differ diff --git a/src/assets/images/fox.svg b/src/assets/images/fox.svg new file mode 100644 index 0000000..72b2545 --- /dev/null +++ b/src/assets/images/fox.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/assets/images/google.svg b/src/assets/images/google.svg new file mode 100644 index 0000000..1ec73e6 --- /dev/null +++ b/src/assets/images/google.svg @@ -0,0 +1,100 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/src/assets/images/search.svg b/src/assets/images/search.svg new file mode 100644 index 0000000..52206e5 --- /dev/null +++ b/src/assets/images/search.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/assets/images/shuttle-space-solid-full.svg b/src/assets/images/shuttle-space-solid-full.svg new file mode 100644 index 0000000..529d90b --- /dev/null +++ b/src/assets/images/shuttle-space-solid-full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/main.css b/src/assets/main.css index e69de29..cfa8fcd 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -0,0 +1,211 @@ +@font-face { + font-family: iosevka-light; + src: url('fonts/iosevka_ttf/iosevka-light.ttf'); +} + +html{ + display: flex; + flex-flow: row nowrap; + justify-content: center; + align-content: center; + align-items: center; + height: 100%; + width: 100%; + margin: 0; + padding: 0; + background:#1b1b1b; + } + +body { + /* background: #263238; */ + background-image: url('/src/backgrounds/bg1.jpg'); + background-size: cover; + background-position: center; + color: #ffa31a; + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} + +.container { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + justify-content: center; +} + +.head { + display: flex; + flex-direction: column; +} + +.category { + display: flex; + flex-direction: column; + width: 20%; + font-family: iosevka-light; +} + +.title { + font-size: 20px; + text-align: center; + background-color: #292929; + color: #ffa31a; + border-radius: 3px; + box-shadow: 0px 4px 5px #171D20; + align-items: center; + padding-top: 10px; + padding-bottom: 10px; + margin-bottom: 0px; + border: #ffa31a 1px; + border-style: solid; +} + +.bookmark { + display: flex; + justify-content: space-evenly; +} + +.bookmarks { + display: flex; + flex-direction: column; + border: #191C1E; + border-radius: 3px; + box-shadow: 0px 4px 5px #171D20; + align-items: center; + padding-top: 20px; + padding-bottom: 20px; + background: rgba(41, 41, 41, .6); + margin-top: 0px; +} + +.logo { + align-self: center; + padding: 15px; +} + +.icon { + align-self: center; + justify-content: center; + margin: 15px; +} + + +.input_box { + border: none; + outline: none; + background: none; + padding-right: 15px; + padding-left: 15px; + width: 100%; + color: #ffa31a; + font-size: 22px; + border-radius: 0px 0px 0px 0px; + height: 70px; + text-align: center; +} + +.button { + background: none; + border: none; + transition-duration: 0.6s; + position: fixed; + padding-left: 11px; + padding-right: 12px; + height: 70px; + opacity: 60%; +} + +.engine { + background: none; + padding-left: 12px; + padding-right: 11px; + border: none; + transition-duration: 0.6s; + position: relative; + opacity: 60%; +} + +form { + margin: 0px; + padding: 0px; +} + +.search_box { + display: flex; + background: rgba(41, 41, 41, .6); + width: 600px; + padding: 0px; + margin-bottom: 40px; + border-radius: 3px 3px 3px 3px; + box-shadow: 0px 4px 5px #171D20; + align-self: center; + height: 70px; +} + +.engine:hover { + filter: saturate(150%) brightness(120%) !important; + opacity: 100%; +} + +.button:hover { + filter: saturate(150%) brightness(120%) !important; + opacity: 100%; +} + +li { + font-size: 16px; + list-style-type: none; + padding: 5px +} + +a:link { + text-decoration: none; + color: #F8BE4D; +} + +a:visited { + color: #F8BE4D; +} + +a:hover { + color: #B5CCE8; +} + +#credit { + position: fixed; + right: 0; + bottom: 0; + font-family: iosevka-light; +} + +#gSearch { + width: 100%; +} + + + +@media screen and (max-width: 600px) { + .container { + display: block; + } + .bookmark { + flex-direction: column; + } + .category { + width: 90%; + align-self: center; + } + .search_box { + width: 90%; + } + .input_box { + width: 100%; + padding: 0px; + } + .engine { + padding: 0px 5px; + } +} \ No newline at end of file diff --git a/src/assets/scripts/VoidPage.js b/src/assets/scripts/VoidPage.js new file mode 100644 index 0000000..a70c9c6 --- /dev/null +++ b/src/assets/scripts/VoidPage.js @@ -0,0 +1,305 @@ +/** + * VoidPage - Main application controller + * Coordinates configuration loading, styling, and page initialization + */ + +class VoidPage { + constructor() { + this.config = null; + this.configLoader = null; + this.styleParser = null; + this.isInitialized = false; + } + + /** + * Initialize the VoidPage application + */ + async init() { + try { + console.log('Initializing VoidPage...'); + + // Import required modules + await this.loadModules(); + + // Load configuration + await this.loadConfiguration(); + + // Apply styles based on configuration + await this.applyStyles(); + + // Initialize page content + this.initializePageContent(); + + // Set up event listeners + this.setupEventListeners(); + + this.isInitialized = true; + console.log('VoidPage initialized successfully'); + + } catch (error) { + console.error('Failed to initialize VoidPage:', error); + this.handleInitializationError(error); + } + } + + /** + * Load required modules dynamically + */ + async loadModules() { + try { + // Import configLoader module + const configModule = await import('./configLoader.js'); + this.configLoader = configModule.configLoader; + + // Import styleParser module + const styleModule = await import('./styleParser.js'); + this.styleParser = styleModule.styleParser; + + console.log('Modules loaded successfully'); + } catch (error) { + console.error('Failed to load modules:', error); + throw new Error('Module loading failed'); + } + } + + /** + * Load and parse configuration + */ + async loadConfiguration() { + try { + this.config = await this.configLoader.loadConfig(); + + // Make config globally available for other scripts + window.VOID_CONFIG = this.config; + + console.log('Configuration loaded:', this.config); + } catch (error) { + console.error('Failed to load configuration:', error); + throw new Error('Configuration loading failed'); + } + } + + /** + * Apply styles based on configuration + */ + async applyStyles() { + try { + if (!this.config) { + throw new Error('Configuration not loaded'); + } + + const success = this.styleParser.applyConfigStyles(this.config); + if (!success) { + throw new Error('Style application failed'); + } + + console.log('Styles applied successfully'); + } catch (error) { + console.error('Failed to apply styles:', error); + // Don't throw - allow page to continue with default styles + } + } + + /** + * Initialize page content based on configuration + */ + initializePageContent() { + try { + const settings = this.config.settings; + const cards = this.config.cards; + + // Set page title + document.title = settings.title; + + // Create or update page header + this.createPageHeader(settings); + + // Generate cards + this.generateCards(cards); + + // Set up search functionality + this.setupSearch(settings.searchEngine); + + console.log('Page content initialized'); + } catch (error) { + console.error('Failed to initialize page content:', error); + } + } + + /** + * Create page header + */ + createPageHeader(settings) { + let header = document.querySelector('.header'); + if (!header) { + header = document.createElement('header'); + header.className = 'header'; + document.body.insertBefore(header, document.body.firstChild); + } + + header.innerHTML = ` +

${settings.title}

+
+ + +
+ `; + } + + /** + * Generate cards from configuration + */ + generateCards(cards) { + let container = document.querySelector('.cards-container'); + if (!container) { + container = document.createElement('div'); + container.className = 'cards-container'; + document.body.appendChild(container); + } + + // Clear existing cards + container.innerHTML = ''; + + cards.forEach(cardData => { + const cardElement = this.createCardElement(cardData); + container.appendChild(cardElement); + }); + } + + /** + * Create individual card element + */ + createCardElement(cardData) { + const card = document.createElement('div'); + card.className = 'card'; + + const openInNewTab = this.config.settings.openLinksInNewTab; + const targetAttr = openInNewTab ? 'target="_blank" rel="noopener noreferrer"' : ''; + + card.innerHTML = ` +
+
+

${cardData.title}

+
+ + `; + + return card; + } + + /** + * Set up search functionality + */ + setupSearch(searchEngine) { + const searchInput = document.getElementById('search-input'); + const searchButton = document.getElementById('search-button'); + + if (!searchInput || !searchButton) return; + + const performSearch = () => { + const query = searchInput.value.trim(); + if (query) { + const searchUrl = searchEngine.url + encodeURIComponent(query); + if (this.config.settings.openLinksInNewTab) { + window.open(searchUrl, '_blank', 'noopener,noreferrer'); + } else { + window.location.href = searchUrl; + } + } + }; + + searchButton.addEventListener('click', performSearch); + searchInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + performSearch(); + } + }); + } + + /** + * Set up additional event listeners + */ + setupEventListeners() { + // Handle configuration updates + window.addEventListener('configUpdated', (event) => { + this.handleConfigUpdate(event.detail); + }); + + // Handle window resize for responsive behavior + window.addEventListener('resize', () => { + this.handleResize(); + }); + } + + /** + * Handle configuration updates + */ + handleConfigUpdate(newConfig) { + console.log('Configuration updated, refreshing page...'); + this.config = newConfig; + window.VOID_CONFIG = newConfig; + + // Reapply styles and content + this.applyStyles(); + this.initializePageContent(); + } + + /** + * Handle window resize + */ + handleResize() { + // Add any responsive behavior here + console.log('Window resized'); + } + + /** + * Handle initialization errors + */ + handleInitializationError(error) { + console.error('VoidPage initialization failed:', error); + + // Create error message for user + const errorDiv = document.createElement('div'); + errorDiv.className = 'error-message'; + errorDiv.innerHTML = ` +

Loading Error

+

Failed to load the page configuration. Please check the console for details.

+ + `; + + document.body.appendChild(errorDiv); + } + + /** + * Get current configuration + */ + getConfig() { + return this.config; + } + + /** + * Check if page is initialized + */ + isReady() { + return this.isInitialized; + } +} + +// Create global VoidPage instance +const voidPage = new VoidPage(); + +// Initialize when DOM is ready +document.addEventListener('DOMContentLoaded', () => { + voidPage.init(); +}); + +// Make VoidPage globally available +window.VoidPage = VoidPage; +window.voidPage = voidPage; + +// Export for module usage +// export { VoidPage, voidPage }; \ No newline at end of file diff --git a/src/assets/scripts/configLoader.js b/src/assets/scripts/configLoader.js new file mode 100644 index 0000000..f4ceed0 --- /dev/null +++ b/src/assets/scripts/configLoader.js @@ -0,0 +1,94 @@ +/** + * Configuration loader utility + * Loads and parses the config.json file from the server + */ + +class ConfigLoader { + constructor() { + this.config = null; + this.configPath = '/src/config.json'; + } + + /** + * Load configuration from the server + * @returns {Promise} The parsed configuration object + */ + async loadConfig() { + try { + const response = await fetch(this.configPath); + + if (!response.ok) { + throw new Error(`Failed to load config: ${response.status} ${response.statusText}`); + } + + const configText = await response.text(); + this.config = JSON.parse(configText); + + console.log('Configuration loaded successfully:', this.config); + return this.config; + } catch (error) { + console.error('Error loading configuration:', error); + throw error; + } + } + + /** + * Get the loaded configuration + * @returns {Object|null} The configuration object or null if not loaded + */ + getConfig() { + return this.config; + } + + /** + * Get a specific configuration value by key + * @param {string} key - The configuration key + * @param {*} defaultValue - Default value if key is not found + * @returns {*} The configuration value + */ + get(key, defaultValue = null) { + if (!this.config) { + console.warn('Configuration not loaded yet. Call loadConfig() first.'); + return defaultValue; + } + + return this.config.hasOwnProperty(key) ? this.config[key] : defaultValue; + } + + /** + * Check if configuration is loaded + * @returns {boolean} True if config is loaded + */ + isLoaded() { + return this.config !== null; + } +} + +// Create a global instance for easy access +const configLoader = new ConfigLoader(); + +// Alternative function-based approach for simpler usage +async function loadConfiguration() { + try { + const response = await fetch('/src/config.json'); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const config = await response.json(); + console.log('Configuration loaded:', config); + return config; + } catch (error) { + console.error('Failed to load configuration:', error); + throw error; + } +} + +// Export for module usage (if using ES6 modules) +// export { ConfigLoader, configLoader, loadConfiguration }; + +// For immediate use in browser without modules +window.ConfigLoader = ConfigLoader; +window.configLoader = configLoader; +window.loadConfiguration = loadConfiguration; \ No newline at end of file diff --git a/src/assets/scripts/randomBackgrounds.js b/src/assets/scripts/randomBackgrounds.js new file mode 100644 index 0000000..53d4043 --- /dev/null +++ b/src/assets/scripts/randomBackgrounds.js @@ -0,0 +1,9 @@ +document.addEventListener("DOMContentLoaded", () => { + fetch('/assets/backgrounds/bgs.json') + .then(response => response.json()) + .then(images => { + const randomImage = images[Math.floor(Math.random() * images.length)]; + document.body.style.backgroundImage = `url(${randomImage})`; + }) + .catch(error => console.error('Error fetching image list:', error)); +}); diff --git a/src/assets/scripts/styleParser.js b/src/assets/scripts/styleParser.js new file mode 100644 index 0000000..f252a72 --- /dev/null +++ b/src/assets/scripts/styleParser.js @@ -0,0 +1,270 @@ +/** + * Style Parser and Applicator + * Builds and applies CSS stylesheets based on configuration values + */ + +class StyleParser { + constructor() { + this.dynamicStyleElement = null; + this.cssVariablesApplied = false; + } + + /** + * Build CSS stylesheet text from configuration + * @param {Object} config - The parsed configuration object + * @returns {string} Complete CSS stylesheet text + */ + buildStylesheet(config) { + const { settings, cards } = config; + + let css = ` +/* Dynamic stylesheet generated from config.json */ + +/* Root CSS Variables */ +:root { + --bg-image: url('${settings.backgroundImgPath}'); + --header-bg-color: ${settings.headerBgColor}; + --header-outline-color: ${settings.headerOutlineColor}; + --header-outline-thickness: ${settings.headerOutlineThickness}px; + --card-bg-color: ${settings.cardBgColor}; + --corner-radius: ${settings.cornerRadius}px; +} + +/* Body and Background */ +body { + background-image: var(--bg-image); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + background-attachment: fixed; +} + +/* Header Styles */ +.header, +header { + background-color: var(--header-bg-color); + border: var(--header-outline-thickness) solid var(--header-outline-color); + border-radius: var(--corner-radius); +} + +/* Card Styles */ +.card { + background-color: var(--card-bg-color); + border-radius: var(--corner-radius); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +/* Card Container */ +.cards-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + padding: 20px; +} + +/* Link Target Behavior */ +`; + + // Add link target behavior based on config + if (settings.openLinksInNewTab) { + css += ` +.card a { + target: _blank; +} +`; + } + + // Add card-specific styles if needed + cards.forEach((card, index) => { + if (card.iconPath) { + css += ` +.card:nth-child(${index + 1}) .card-icon { + background-image: url('${card.iconPath}'); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} +`; + } + }); + + css += ` +/* Card Icons */ +.card-icon { + width: 32px; + height: 32px; + display: inline-block; + margin-right: 10px; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .cards-container { + grid-template-columns: 1fr; + padding: 10px; + } + + .card { + margin-bottom: 15px; + } +} +`; + + return css; + } + + /** + * Apply CSS variables to document root + * @param {Object} settings - Configuration settings object + */ + applyCSSVariables(settings) { + const root = document.documentElement; + + root.style.setProperty('--bg-image', `url('${settings.backgroundImgPath}')`); + root.style.setProperty('--header-bg-color', settings.headerBgColor); + root.style.setProperty('--header-outline-color', settings.headerOutlineColor); + root.style.setProperty('--header-outline-thickness', `${settings.headerOutlineThickness}px`); + root.style.setProperty('--card-bg-color', settings.cardBgColor); + root.style.setProperty('--corner-radius', `${settings.cornerRadius}px`); + + this.cssVariablesApplied = true; + console.log('CSS variables applied to document root'); + } + + /** + * Create and inject dynamic stylesheet into document head + * @param {string} cssText - CSS stylesheet text + * @returns {HTMLStyleElement} The created style element + */ + injectStylesheet(cssText) { + // Remove existing dynamic stylesheet if present + this.removeDynamicStylesheet(); + + // Create new style element + this.dynamicStyleElement = document.createElement('style'); + this.dynamicStyleElement.type = 'text/css'; + this.dynamicStyleElement.id = 'dynamic-config-styles'; + this.dynamicStyleElement.textContent = cssText; + + // Inject into document head + document.head.appendChild(this.dynamicStyleElement); + + console.log('Dynamic stylesheet injected'); + return this.dynamicStyleElement; + } + + /** + * Remove the dynamic stylesheet from document + */ + removeDynamicStylesheet() { + if (this.dynamicStyleElement) { + this.dynamicStyleElement.remove(); + this.dynamicStyleElement = null; + console.log('Dynamic stylesheet removed'); + } + } + + /** + * Apply link target behavior to existing links + * @param {boolean} openInNewTab - Whether to open links in new tab + */ + applyLinkTargetBehavior(openInNewTab) { + const links = document.querySelectorAll('.card a, .card-link'); + + links.forEach(link => { + if (openInNewTab) { + link.setAttribute('target', '_blank'); + link.setAttribute('rel', 'noopener noreferrer'); + } else { + link.removeAttribute('target'); + link.removeAttribute('rel'); + } + }); + + console.log(`Link target behavior applied: ${openInNewTab ? 'new tab' : 'same tab'}`); + } + + /** + * Complete style application from configuration + * @param {Object} config - The complete configuration object + */ + applyConfigStyles(config) { + try { + // Build and inject stylesheet + const cssText = this.buildStylesheet(config); + this.injectStylesheet(cssText); + + // Apply CSS variables + this.applyCSSVariables(config.settings); + + // Apply link behavior + this.applyLinkTargetBehavior(config.settings.openLinksInNewTab); + + console.log('All configuration styles applied successfully'); + return true; + } catch (error) { + console.error('Error applying configuration styles:', error); + return false; + } + } + + /** + * Update specific style property + * @param {string} property - CSS property name + * @param {string} value - CSS property value + */ + updateStyleProperty(property, value) { + const root = document.documentElement; + const cssVarName = `--${property.replace(/([A-Z])/g, '-$1').toLowerCase()}`; + root.style.setProperty(cssVarName, value); + console.log(`Updated ${cssVarName}: ${value}`); + } + + /** + * Get current dynamic stylesheet text + * @returns {string|null} Current stylesheet text or null if not applied + */ + getCurrentStylesheet() { + return this.dynamicStyleElement ? this.dynamicStyleElement.textContent : null; + } + + /** + * Check if styles are currently applied + * @returns {boolean} True if dynamic styles are active + */ + isStylesApplied() { + return this.dynamicStyleElement !== null && this.cssVariablesApplied; + } +} + +// Create global instance +const styleParser = new StyleParser(); + +// Utility functions for external access +function applyConfigurationStyles(config) { + return styleParser.applyConfigStyles(config); +} + +function buildStylesheetFromConfig(config) { + return styleParser.buildStylesheet(config); +} + +function updateStyleProperty(property, value) { + return styleParser.updateStyleProperty(property, value); +} + +function removeConfigurationStyles() { + return styleParser.removeDynamicStylesheet(); +} + +// Export for module usage +// export { StyleParser, styleParser, applyConfigurationStyles, buildStylesheetFromConfig }; + +// Global access +window.StyleParser = StyleParser; +window.styleParser = styleParser; +window.applyConfigurationStyles = applyConfigurationStyles; +window.buildStylesheetFromConfig = buildStylesheetFromConfig; +window.updateStyleProperty = updateStyleProperty; +window.removeConfigurationStyles = removeConfigurationStyles; \ No newline at end of file diff --git a/src/index.html b/src/index.html index ca16172..93e7c1e 100644 --- a/src/index.html +++ b/src/index.html @@ -1,16 +1,49 @@ - - - VoidPage - - - -
- -
- - - - - - \ No newline at end of file + + + + + VoidPage + + + + + + + +
+
+ + + +
+
+
+

Category One

+
+
  • Google
  • +
    +
    +
    +

    Category Two

    +
    +
  • Steam
  • +
    +
    +
    +

    Category Three

    +
    +
  • GMail
  • +
    +
    +
    +
    + +