1- // screensaver.js
2- // Handles screensaver functionality with Pollinations API integration
3- // Updated with safe=false for uncensored content and cross-browser/device support
4-
51document . addEventListener ( "DOMContentLoaded" , ( ) => {
62 const screensaverContainer = document . getElementById ( "screensaver-container" ) ;
73 const toggleScreensaverButton = document . getElementById ( "toggle-screensaver" ) ;
@@ -24,7 +20,6 @@ document.addEventListener("DOMContentLoaded", () => {
2420 let paused = false ;
2521 let isFullscreen = false ;
2622
27- // Set tooltips
2823 toggleScreensaverButton . title = "Toggle the screensaver on/off." ;
2924 fullscreenButton . title = "Go full screen (or exit it)." ;
3025 stopButton . title = "Stop the screensaver." ;
@@ -38,7 +33,6 @@ document.addEventListener("DOMContentLoaded", () => {
3833 enhanceCheckbox . title = "If enabled, the prompt is 'enhanced' via an LLM." ;
3934 privateCheckbox . title = "If enabled, the image won't appear on the public feed." ;
4035
41- // Utility to detect browser for compatibility adjustments
4236 const getBrowserInfo = ( ) => {
4337 const ua = navigator . userAgent . toLowerCase ( ) ;
4438 return {
@@ -50,7 +44,6 @@ document.addEventListener("DOMContentLoaded", () => {
5044 } ;
5145 } ;
5246
53- // Utility to create audio context for cross-browser support
5447 const createAudioContext = ( ) => {
5548 const AudioContext = window . AudioContext || window . webkitAudioContext ;
5649 if ( ! AudioContext ) {
@@ -60,11 +53,10 @@ document.addEventListener("DOMContentLoaded", () => {
6053 return new AudioContext ( ) ;
6154 } ;
6255
63- // Utility to play audio with cross-browser compatibility
6456 const playAudio = ( audioUrl ) => {
6557 return new Promise ( ( resolve , reject ) => {
6658 const audio = new Audio ( audioUrl ) ;
67- audio . crossOrigin = "anonymous" ; // For CORS support
59+ audio . crossOrigin = "anonymous" ;
6860 audio . preload = "auto" ;
6961
7062 audio . play ( ) . catch ( ( err ) => {
@@ -91,7 +83,6 @@ document.addEventListener("DOMContentLoaded", () => {
9183 } ) ;
9284 } ;
9385
94- // Save settings to localStorage
9586 function saveScreensaverSettings ( ) {
9687 const settings = {
9788 prompt : promptInput . value ,
@@ -104,7 +95,6 @@ document.addEventListener("DOMContentLoaded", () => {
10495 localStorage . setItem ( "screensaverSettings" , JSON . stringify ( settings ) ) ;
10596 }
10697
107- // Load settings from localStorage
10898 function loadScreensaverSettings ( ) {
10999 const raw = localStorage . getItem ( "screensaverSettings" ) ;
110100 if ( ! raw ) return ;
@@ -123,17 +113,10 @@ document.addEventListener("DOMContentLoaded", () => {
123113
124114 loadScreensaverSettings ( ) ;
125115
126- // Generate a random seed
127116 function generateSeed ( ) {
128- const length = Math . floor ( Math . random ( ) * 8 ) + 1 ;
129- let seed = "" ;
130- for ( let i = 0 ; i < length ; i ++ ) {
131- seed += Math . floor ( Math . random ( ) * 10 ) ;
132- }
133- return seed ;
117+ return Math . floor ( Math . random ( ) * 1000000 ) . toString ( ) ;
134118 }
135119
136- // Get dimensions based on aspect ratio
137120 function getDimensions ( aspect ) {
138121 switch ( aspect ) {
139122 case "widescreen" : return { width : 1280 , height : 720 } ;
@@ -143,17 +126,14 @@ document.addEventListener("DOMContentLoaded", () => {
143126 }
144127 }
145128
146- // FIXED: Fetch a new image from Pollinations API with nolog parameter
147129 function fetchNewImage ( ) {
148130 saveScreensaverSettings ( ) ;
149131 let prompt = promptInput . value || "random artistic scene, high quality, detailed" ;
150132
151- // Keep the prompt concise and clean
152133 if ( prompt . length > 100 ) {
153134 prompt = prompt . substring ( 0 , 100 ) ;
154135 }
155136
156- // Add quality terms
157137 prompt += ", high resolution, detailed" ;
158138
159139 const { width, height } = getDimensions ( aspectSelect . value ) ;
@@ -162,19 +142,15 @@ document.addEventListener("DOMContentLoaded", () => {
162142 const enhance = enhanceCheckbox . checked ;
163143 const priv = privateCheckbox . checked ;
164144
165- // Use global config if available, otherwise hardcode safe=false
166- const safeParam = window . _pollinationsAPIConfig ? `safe=${ window . _pollinationsAPIConfig . safe } ` : "safe=false" ;
167-
168- // Add nolog=true parameter to prevent unwanted URL parameters
169- const url = `https://image.pollinations.ai/prompt/${ encodeURIComponent ( prompt ) } ?width=${ width } &height=${ height } &seed=${ seed } &model=${ model } &nologo=true&private=${ priv } &enhance=${ enhance } &${ safeParam } &nolog=true` ;
145+ const url = `https://image.pollinations.ai/prompt/${ encodeURIComponent ( prompt ) } ?width=${ width } &height=${ height } &seed=${ seed } &model=${ model } &nologo=true&private=${ priv } &enhance=${ enhance } &safe=false&nolog=true` ;
170146
171147 screensaverImage . style . opacity = "0.5" ;
172- screensaverImage . crossOrigin = "anonymous" ; // Enable CORS for cross-browser support
148+ screensaverImage . crossOrigin = "anonymous" ;
173149 screensaverImage . onload = ( ) => {
174150 screensaverImage . style . opacity = "1" ;
175151 const browserInfo = getBrowserInfo ( ) ;
176152 if ( browserInfo . isSafari ) {
177- screensaverImage . style . webkitMaskImage = "none" ; // Fix Safari rendering
153+ screensaverImage . style . webkitMaskImage = "none" ;
178154 }
179155 } ;
180156 screensaverImage . onerror = ( ) => {
@@ -185,7 +161,6 @@ document.addEventListener("DOMContentLoaded", () => {
185161 screensaverImage . src = url ;
186162 }
187163
188- // Set or reset the image interval
189164 function setOrResetInterval ( ) {
190165 clearInterval ( imageInterval ) ;
191166 const intervalSeconds = parseInt ( timerInput . value ) || 30 ;
@@ -195,7 +170,6 @@ document.addEventListener("DOMContentLoaded", () => {
195170 window . imageInterval = imageInterval ;
196171 }
197172
198- // Start the screensaver
199173 function startScreensaver ( ) {
200174 screensaverActive = true ;
201175 paused = false ;
@@ -221,12 +195,10 @@ document.addEventListener("DOMContentLoaded", () => {
221195 document . body . style . overflow = "hidden" ;
222196 window . screensaverActive = true ;
223197
224- // Optional audio cue for screensaver start (placeholder)
225- const audioUrl = "https://example.com/audio/start.mp3" ; // Replace with actual audio API
198+ const audioUrl = "https://example.com/audio/start.mp3" ;
226199 playAudio ( audioUrl ) . catch ( err => console . warn ( "Audio cue failed:" , err ) ) ;
227200 }
228201
229- // Stop the screensaver
230202 function stopScreensaver ( ) {
231203 screensaverActive = false ;
232204 paused = false ;
@@ -251,7 +223,6 @@ document.addEventListener("DOMContentLoaded", () => {
251223 }
252224 }
253225
254- // Toggle pause state
255226 function togglePause ( ) {
256227 paused = ! paused ;
257228 playPauseButton . innerHTML = paused ? "▶️" : "⏸️" ;
@@ -260,89 +231,70 @@ document.addEventListener("DOMContentLoaded", () => {
260231 window . showToast ( paused ? "Screensaver paused" : "Screensaver resumed" ) ;
261232 }
262233
263- // Save the current image
264234 function saveImage ( ) {
265235 if ( ! screensaverImage . src ) {
266236 window . showToast ( "No image to save" ) ;
267237 return ;
268238 }
269- fetch ( screensaverImage . src )
270- . then ( response => response . ok ? response . blob ( ) : Promise . reject ( "Fetch failed" ) )
239+ fetch ( screensaverImage . src , { mode : "cors" } )
240+ . then ( response => {
241+ if ( ! response . ok ) throw new Error ( "Network response was not ok" ) ;
242+ return response . blob ( ) ;
243+ } )
271244 . then ( blob => {
272- const blobUrl = URL . createObjectURL ( blob ) ;
273- const link = document . createElement ( "a" ) ;
274- link . href = blobUrl ;
275- link . download = " screensaver-image. png" ;
276- document . body . appendChild ( link ) ;
277- link . click ( ) ;
278- document . body . removeChild ( link ) ;
279- URL . revokeObjectURL ( blobUrl ) ;
280- window . showToast ( "Image saved! " ) ;
245+ const url = URL . createObjectURL ( blob ) ;
246+ const a = document . createElement ( "a" ) ;
247+ a . href = url ;
248+ a . download = ` screensaver-image- ${ Date . now ( ) } . png` ;
249+ document . body . appendChild ( a ) ;
250+ a . click ( ) ;
251+ document . body . removeChild ( a ) ;
252+ URL . revokeObjectURL ( url ) ;
253+ window . showToast ( "Image download initiated " ) ;
281254 } )
282255 . catch ( err => {
283256 console . error ( "Error saving image:" , err ) ;
284257 window . showToast ( "Failed to save image" ) ;
285258 } ) ;
286259 }
287260
288- // Copy the current image to clipboard
289261 function copyImage ( ) {
290262 if ( ! screensaverImage . src ) {
291263 window . showToast ( "No image to copy" ) ;
292264 return ;
293265 }
266+ if ( ! screensaverImage . complete || screensaverImage . naturalWidth === 0 ) {
267+ window . showToast ( "Image not fully loaded yet. Please try again." ) ;
268+ return ;
269+ }
294270 copyButton . textContent = "Copying..." ;
295271 const canvas = document . createElement ( "canvas" ) ;
296272 const ctx = canvas . getContext ( "2d" ) ;
297-
298- if ( screensaverImage . complete && screensaverImage . naturalWidth > 0 ) {
299- canvas . width = screensaverImage . naturalWidth ;
300- canvas . height = screensaverImage . naturalHeight ;
301- ctx . drawImage ( screensaverImage , 0 , 0 ) ;
302- canvas . toBlob ( blob => {
303- navigator . clipboard . write ( [ new ClipboardItem ( { "image/png" : blob } ) ] )
304- . then ( ( ) => {
305- copyButton . textContent = "✅ Copied!" ;
306- window . showToast ( "Image copied!" ) ;
307- setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
308- } )
309- . catch ( err => {
310- copyButton . textContent = "❌ Failed" ;
311- window . showToast ( "Copy failed" ) ;
312- setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
313- } ) ;
314- } , "image/png" , 1.0 ) ;
315- } else {
316- const tempImg = new Image ( ) ;
317- tempImg . crossOrigin = "anonymous" ;
318- tempImg . onload = ( ) => {
319- canvas . width = tempImg . naturalWidth ;
320- canvas . height = tempImg . naturalHeight ;
321- ctx . drawImage ( tempImg , 0 , 0 ) ;
322- canvas . toBlob ( blob => {
323- navigator . clipboard . write ( [ new ClipboardItem ( { "image/png" : blob } ) ] )
324- . then ( ( ) => {
325- copyButton . textContent = "✅ Copied!" ;
326- window . showToast ( "Image copied!" ) ;
327- setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
328- } )
329- . catch ( ( ) => {
330- copyButton . textContent = "❌ Failed" ;
331- window . showToast ( "Copy failed" ) ;
332- setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
333- } ) ;
334- } , "image/png" , 1.0 ) ;
335- } ;
336- tempImg . onerror = ( ) => {
337- copyButton . textContent = "❌ Failed" ;
338- window . showToast ( "Failed to load image" ) ;
339- setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
340- } ;
341- tempImg . src = screensaverImage . src ;
342- }
273+ canvas . width = screensaverImage . naturalWidth ;
274+ canvas . height = screensaverImage . naturalHeight ;
275+ ctx . drawImage ( screensaverImage , 0 , 0 ) ;
276+ canvas . toBlob ( blob => {
277+ if ( ! blob ) {
278+ copyButton . textContent = "Copy Image" ;
279+ window . showToast ( "Failed to copy image: Unable to create blob." ) ;
280+ return ;
281+ }
282+ navigator . clipboard . write ( [ new ClipboardItem ( { "image/png" : blob } ) ] )
283+ . then ( ( ) => {
284+ const dataURL = canvas . toDataURL ( "image/png" ) ;
285+ localStorage . setItem ( "lastCopiedImage" , dataURL ) ;
286+ copyButton . textContent = "✅ Copied!" ;
287+ window . showToast ( "Image copied to clipboard and saved to local storage" ) ;
288+ setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
289+ } )
290+ . catch ( err => {
291+ copyButton . textContent = "❌ Failed" ;
292+ window . showToast ( "Copy failed: " + err . message ) ;
293+ setTimeout ( ( ) => copyButton . textContent = "Copy Image" , 1500 ) ;
294+ } ) ;
295+ } , "image/png" ) ;
343296 }
344297
345- // Toggle fullscreen mode
346298 function toggleFullscreen ( ) {
347299 if ( ! screensaverActive ) {
348300 window . showToast ( "Start the screensaver first!" ) ;
@@ -367,7 +319,6 @@ document.addEventListener("DOMContentLoaded", () => {
367319 }
368320 }
369321
370- // Event listeners
371322 toggleScreensaverButton . addEventListener ( "click" , ( ) => {
372323 screensaverActive ? stopScreensaver ( ) : startScreensaver ( ) ;
373324 } ) ;
@@ -417,13 +368,21 @@ document.addEventListener("DOMContentLoaded", () => {
417368 }
418369 } ) ;
419370
420- // Cross-browser toast notification
421371 window . showToast = function ( message , duration = 3000 ) {
422372 let toast = document . getElementById ( "toast-notification" ) ;
423373 if ( ! toast ) {
424374 toast = document . createElement ( "div" ) ;
425375 toast . id = "toast-notification" ;
426- toast . style . cssText = "position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background-color:rgba(0,0,0,0.7);color:white;padding:10px 20px;border-radius:5px;z-index:9999;transition:opacity 0.3s;" ;
376+ toast . style . position = "fixed" ;
377+ toast . style . top = "5%" ;
378+ toast . style . left = "50%" ;
379+ toast . style . transform = "translateX(-50%)" ;
380+ toast . style . backgroundColor = "rgba(0,0,0,0.7)" ;
381+ toast . style . color = "white" ;
382+ toast . style . padding = "10px 20px" ;
383+ toast . style . borderRadius = "5px" ;
384+ toast . style . zIndex = "9999" ;
385+ toast . style . transition = "opacity 0.3s" ;
427386 document . body . appendChild ( toast ) ;
428387 }
429388 toast . textContent = message ;
@@ -432,7 +391,6 @@ document.addEventListener("DOMContentLoaded", () => {
432391 toast . timeout = setTimeout ( ( ) => toast . style . opacity = "0" , duration ) ;
433392 } ;
434393
435- // Expose functions globally
436394 window . startScreensaver = startScreensaver ;
437395 window . stopScreensaver = stopScreensaver ;
438396 window . togglePause = togglePause ;
0 commit comments