Skip to content
Merged

Splash #6511

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 75 additions & 75 deletions apps/remix-ide/src/app/components/preload.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { RemixApp } from '@remix-ui/app'
import axios from 'axios'
import React, { useState, useEffect, useRef, useContext } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import React, { useState, useEffect, useRef } from 'react'
import { useTracking, TrackingProvider } from '../contexts/TrackingContext'
import { TrackingFunction } from '../utils/TrackingFunction'
import * as packageJson from '../../../../../package.json'
import * as remixDesktopPackageJson from '../../../../../apps/remixdesktop/package.json'
import { fileSystem, fileSystems } from '../files/fileSystem'
import { indexedDBFileSystem } from '../files/filesystems/indexedDB'
import { localStorageFS } from '../files/filesystems/localStorage'
Expand All @@ -28,6 +28,7 @@ export const Preload = (props: PreloadProps) => {
const remixFileSystems = useRef<fileSystems>(new fileSystems())
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem())
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS())
const version = isElectron() ? remixDesktopPackageJson.version : packageJson.version
// url parameters to e2e test the fallbacks and error warnings
const testmigrationFallback = useRef<boolean>(
window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
Expand Down Expand Up @@ -95,14 +96,20 @@ export const Preload = (props: PreloadProps) => {
}
}

useEffect (() => {
if (isElectron()){
useEffect(() => {
// Remove pre-splash as soon as React preloader mounts
try {
const splash = document.getElementById('pre-splash')
if (splash && splash.parentNode) splash.parentNode.removeChild(splash)
} catch (_) { /* noop */ }

if (isElectron()) {
loadAppComponent()
return
}
async function loadStorage() {
;(await remixFileSystems.current.addFileSystem(remixIndexedDB.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'indexedDB not supported', isClick: false })
;(await remixFileSystems.current.addFileSystem(localStorageFileSystem.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'localstorage not supported', isClick: false })
; (await remixFileSystems.current.addFileSystem(remixIndexedDB.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'indexedDB not supported', isClick: false })
; (await remixFileSystems.current.addFileSystem(localStorageFileSystem.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'localstorage not supported', isClick: false })
await testmigration()
remixIndexedDB.current.loaded && (await remixIndexedDB.current.checkWorkspaces())
localStorageFileSystem.current.loaded && (await localStorageFileSystem.current.checkWorkspaces())
Expand All @@ -125,89 +132,82 @@ export const Preload = (props: PreloadProps) => {
} catch (e) {
console.log(e)
}

return () => {
abortController.abort();
};
}, [])

return (
<>
<div className="preload-container">
<div className="preload-logo pb-4">
{logo}
<div className="info-secondary splash">
REMIX IDE
<br />
<span className="version"> v{packageJson.version}</span>
<div className="preload-container" >
<div className="preload-main">
<div className="preload-logo text-center">
<img src="assets/img/remix-logo-blue.png" alt="Remix logo" width="64" height="64" />
<div className="preload-title">REMIX IDE</div>
<div className="preload-sub"><span className="version">v{version}</span></div>
</div>
</div>
{!supported ? (
<div className="preload-info-container alert alert-warning">
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
</div>
) : null}
{error ? (
<div className="preload-info-container alert alert-danger text-start">
An unknown error has occurred while loading the application.
<br></br>
Doing a hard refresh might fix this issue:<br></br>
<div className="pt-2">
Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
<br></br>- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
</div>
<div className="pt-2">
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
{!supported ? (
<div className="preload-info-container alert alert-warning">
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
</div>
<div className="pt-2">
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
) : null}
{error ? (
<div className="preload-info-container alert alert-danger text-start">
An unknown error has occurred while loading the application.
<br></br>
Doing a hard refresh might fix this issue:<br></br>
<div className="pt-2">
Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
<br></br>- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
</div>
<div className="pt-2">
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
</div>
<div className="pt-2">
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
</div>
</div>
</div>
) : null}
{showDownloader ? (
<div className="preload-info-container alert alert-info">
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
<br></br>
You don't need to do anything else, your files will be available when the app loads.
<div
onClick={async () => {
await downloadBackup()
}}
data-id="downloadbackup-btn"
className="btn btn-primary mt-1"
>
download backup
</div>
<div
onClick={async () => {
await migrateAndLoad()
}}
data-id="skipbackup-btn"
className="btn btn-primary mt-1"
>
skip backup
) : null}
{showDownloader ? (
<div className="preload-info-container alert alert-info">
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
<br></br>
You don't need to do anything else, your files will be available when the app loads.
<div
onClick={async () => {
await downloadBackup()
}}
data-id="downloadbackup-btn"
className="btn btn-primary mt-1"
>
download backup
</div>
<div
onClick={async () => {
await migrateAndLoad()
}}
data-id="skipbackup-btn"
className="btn btn-primary mt-1"
>
skip backup
</div>
</div>
</div>
) : null}
{supported && !error && !showDownloader ? (
<div>
<div className='text-center'>
<i className="fas fa-spinner fa-spin fa-2x"></i>
) : null}
{supported && !error && !showDownloader ? (
<div className='text-center' style={{ marginTop: '16px' }}>
<div className="pre-splash-spinner" role="progressbar" aria-label="Loading"></div>
</div>
{ tip && <div className='remix_tips text-center mt-3'>
<div><b>DID YOU KNOW</b></div>
<span>{tip}</span>
</div> }
</div>
) : null}
) : null}
</div>
<div className="preload-bottom opt-out">
{ tip && <div className='remix_tips text-center mt-3'>
<div><b>DID YOU KNOW</b></div>
<span>{tip}</span>
</div> }
</div>
</div>
</>
)
}

const logo = (
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100">
<path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" />
<path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" />
<path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" />
</svg>
)
69 changes: 66 additions & 3 deletions apps/remix-ide/src/app/components/styles/preload.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
.preload-container {
position: relative; /* allow absolute positioning inside */
display: flex;
flex-direction: column;
justify-content: center; /* centers .preload-main vertically */
align-items: center; /* centers .preload-main horizontally */
height: 100vh;
}

/* Center the main content while allowing a footer-like tips area below */
.preload-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
justify-content: center;
width: 100%;
position: relative;
}

.preload-info-container {
Expand All @@ -20,5 +31,57 @@
.preload-logo {
min-width: 200px;
max-width: 240px;
padding-bottom: 1.5rem !important;
}

/* Match index.html splash spinner exactly using theme variables */
.pre-splash-spinner {
width: 24px;
height: 24px;
border: 3px solid var(--bs-border-color-translucent, rgba(255,255,255,0.15));
border-top-color: var(--bs-primary, #3b82f6);
border-radius: 50%;
animation: pre-splash-spin 1s linear infinite;
display: inline-block;
}

@keyframes pre-splash-spin { to { transform: rotate(360deg); } }

/* Match index.html splash typography */
.preload-title {
margin-top: 12px;
font-size: 18px;
letter-spacing: 0.06em;
color: var(--bs-emphasis-color, #e6ecf8);
text-align: center;
}

.preload-sub {
margin-top: 4px;
font-size: 13px;
color: var(--bs-secondary-color, #9aa7bd);
text-align: center;
}

/* Keep tips concise and stable in height */
.remix_tips {
max-width: 800px;
margin: 0 auto;
}

.remix_tips span {
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}

.preload-bottom.opt-out {
position: absolute;
top: 50%;
left: 0;
right: 0;
text-align: center;
transform: translateY(calc(50% + 60px)); /* 50% moves it to center, additional offset moves it below the content */
margin-top: 40px;
}
34 changes: 25 additions & 9 deletions apps/remix-ide/src/app/tabs/theme-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,34 @@ export class ThemeModule extends Plugin {
initTheme(callback?: () => void): void { // callback is setTimeOut in app.js which is always passed
if (callback) this.initCallback = callback
if (this.active) {
document.getElementById('theme-link') ? document.getElementById('theme-link').remove() : null
const existingThemeLink = document.getElementById('theme-link')
if (existingThemeLink) existingThemeLink.remove()
const nextTheme = this.themes[this.active] // Theme
document.documentElement.style.setProperty('--theme', nextTheme.quality)

const theme = document.createElement('link')
theme.setAttribute('rel', 'stylesheet')
theme.setAttribute('href', nextTheme.url)
theme.setAttribute('id', 'theme-link')
theme.addEventListener('load', () => {
if (callback) callback()
})
document.head.insertBefore(theme, document.head.firstChild)
// Reuse preloaded theme link if present to avoid double loading
const preloaded = document.getElementById('pre-theme-css') as HTMLLinkElement | null
if (preloaded) {
preloaded.id = 'theme-link'
const desired = new URL(nextTheme.url, document.baseURI).href
const current = preloaded.href
if (current !== desired) {
preloaded.addEventListener('load', () => { if (callback) callback() }, { once: true })
preloaded.href = nextTheme.url
} else {
// Already loaded desired theme
if (callback) callback()
}
} else {
const theme = document.createElement('link')
theme.setAttribute('rel', 'stylesheet')
theme.setAttribute('href', nextTheme.url)
theme.setAttribute('id', 'theme-link')
theme.addEventListener('load', () => {
if (callback) callback()
})
document.head.insertBefore(theme, document.head.firstChild)
}
//if (callback) callback()
}
}
Expand Down
49 changes: 49 additions & 0 deletions apps/remix-ide/src/assets/js/pre-splash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
(function(){
try {
// 1) Determine theme from Remix config stored in localStorage
var raw = localStorage.getItem('config-v0.8:.remix.config');
var theme = '';
if (raw) {
try {
var cfg = JSON.parse(raw);
theme = String((cfg && (cfg['settings/theme'] || (cfg.settings && (cfg.settings.theme || '')))) || '').toLowerCase();
} catch (_) { theme = ''; }
}

var isLight = theme.indexOf('light') !== -1;
document.documentElement.setAttribute('data-pre-theme', isLight ? 'light' : 'dark');

// 2) Ensure the actual theme stylesheet is loaded ASAP (two themes supported)
var existing = document.getElementById('pre-theme-css');
if (!existing) {
var href = isLight
? 'assets/css/themes/remix-light_powaqg.css'
: 'assets/css/themes/remix-dark_tvx1s2.css';
var link = document.createElement('link');
link.id = 'pre-theme-css';
link.rel = 'stylesheet';
link.href = href;
document.head.appendChild(link);
}

// 3) Refine splash content for Electron + OS without waiting for bundles
var ua = navigator.userAgent || '';
var low = ua.toLowerCase();
var isElectron = low.indexOf('electron') !== -1;
var title = document.getElementById('pre-splash-title');
var sub = document.getElementById('pre-splash-sub');
if (isElectron) {
if (title) title.textContent = 'Remix Desktop';
if (sub) {
var os = low.indexOf('mac') !== -1 ? 'macOS' : (low.indexOf('win') !== -1 ? 'Windows' : (low.indexOf('linux') !== -1 ? 'Linux' : ''));
sub.textContent = os ? ('Loading… ' + os) : 'Loading…';
}
} else {
// Web: match Preload case for consistency
if (title) title.textContent = 'REMIX IDE';
}
} catch (e) {
// Last-resort fallback theme
try { document.documentElement.setAttribute('data-pre-theme','dark'); } catch(_) {}
}
})();
Loading