Skip to content

Commit c091a34

Browse files
authored
Merge pull request #6511 from remix-project-org/splash
Splash
2 parents ea562e4 + 49bd26f commit c091a34

File tree

5 files changed

+275
-87
lines changed

5 files changed

+275
-87
lines changed

apps/remix-ide/src/app/components/preload.tsx

Lines changed: 75 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { RemixApp } from '@remix-ui/app'
22
import axios from 'axios'
3-
import React, { useState, useEffect, useRef, useContext } from 'react'
4-
import { FormattedMessage, useIntl } from 'react-intl'
3+
import React, { useState, useEffect, useRef } from 'react'
54
import { useTracking, TrackingProvider } from '../contexts/TrackingContext'
65
import { TrackingFunction } from '../utils/TrackingFunction'
76
import * as packageJson from '../../../../../package.json'
7+
import * as remixDesktopPackageJson from '../../../../../apps/remixdesktop/package.json'
88
import { fileSystem, fileSystems } from '../files/fileSystem'
99
import { indexedDBFileSystem } from '../files/filesystems/indexedDB'
1010
import { localStorageFS } from '../files/filesystems/localStorage'
@@ -28,6 +28,7 @@ export const Preload = (props: PreloadProps) => {
2828
const remixFileSystems = useRef<fileSystems>(new fileSystems())
2929
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem())
3030
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS())
31+
const version = isElectron() ? remixDesktopPackageJson.version : packageJson.version
3132
// url parameters to e2e test the fallbacks and error warnings
3233
const testmigrationFallback = useRef<boolean>(
3334
window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:'
@@ -95,14 +96,20 @@ export const Preload = (props: PreloadProps) => {
9596
}
9697
}
9798

98-
useEffect (() => {
99-
if (isElectron()){
99+
useEffect(() => {
100+
// Remove pre-splash as soon as React preloader mounts
101+
try {
102+
const splash = document.getElementById('pre-splash')
103+
if (splash && splash.parentNode) splash.parentNode.removeChild(splash)
104+
} catch (_) { /* noop */ }
105+
106+
if (isElectron()) {
100107
loadAppComponent()
101108
return
102109
}
103110
async function loadStorage() {
104-
;(await remixFileSystems.current.addFileSystem(remixIndexedDB.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'indexedDB not supported', isClick: false })
105-
;(await remixFileSystems.current.addFileSystem(localStorageFileSystem.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'localstorage not supported', isClick: false })
111+
; (await remixFileSystems.current.addFileSystem(remixIndexedDB.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'indexedDB not supported', isClick: false })
112+
; (await remixFileSystems.current.addFileSystem(localStorageFileSystem.current)) || trackMatomoEvent?.({ category: 'Storage', action: 'error', name: 'localstorage not supported', isClick: false })
106113
await testmigration()
107114
remixIndexedDB.current.loaded && (await remixIndexedDB.current.checkWorkspaces())
108115
localStorageFileSystem.current.loaded && (await localStorageFileSystem.current.checkWorkspaces())
@@ -125,89 +132,82 @@ export const Preload = (props: PreloadProps) => {
125132
} catch (e) {
126133
console.log(e)
127134
}
135+
128136
return () => {
129137
abortController.abort();
130138
};
131139
}, [])
132140

133141
return (
134142
<>
135-
<div className="preload-container">
136-
<div className="preload-logo pb-4">
137-
{logo}
138-
<div className="info-secondary splash">
139-
REMIX IDE
140-
<br />
141-
<span className="version"> v{packageJson.version}</span>
143+
<div className="preload-container" >
144+
<div className="preload-main">
145+
<div className="preload-logo text-center">
146+
<img src="assets/img/remix-logo-blue.png" alt="Remix logo" width="64" height="64" />
147+
<div className="preload-title">REMIX IDE</div>
148+
<div className="preload-sub"><span className="version">v{version}</span></div>
142149
</div>
143-
</div>
144-
{!supported ? (
145-
<div className="preload-info-container alert alert-warning">
146-
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
147-
</div>
148-
) : null}
149-
{error ? (
150-
<div className="preload-info-container alert alert-danger text-start">
151-
An unknown error has occurred while loading the application.
152-
<br></br>
153-
Doing a hard refresh might fix this issue:<br></br>
154-
<div className="pt-2">
155-
Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
156-
<br></br>- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
157-
</div>
158-
<div className="pt-2">
159-
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
150+
{!supported ? (
151+
<div className="preload-info-container alert alert-warning">
152+
Your browser does not support any of the filesystems required by Remix. Either change the settings in your browser or use a supported browser.
160153
</div>
161-
<div className="pt-2">
162-
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
154+
) : null}
155+
{error ? (
156+
<div className="preload-info-container alert alert-danger text-start">
157+
An unknown error has occurred while loading the application.
158+
<br></br>
159+
Doing a hard refresh might fix this issue:<br></br>
160+
<div className="pt-2">
161+
Windows:<br></br>- Chrome: CTRL + F5 or CTRL + Reload Button
162+
<br></br>- Firefox: CTRL + SHIFT + R or CTRL + F5<br></br>
163+
</div>
164+
<div className="pt-2">
165+
MacOS:<br></br>- Chrome & FireFox: CMD + SHIFT + R or SHIFT + Reload Button<br></br>
166+
</div>
167+
<div className="pt-2">
168+
Linux:<br></br>- Chrome & FireFox: CTRL + SHIFT + R<br></br>
169+
</div>
163170
</div>
164-
</div>
165-
) : null}
166-
{showDownloader ? (
167-
<div className="preload-info-container alert alert-info">
168-
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
169-
<br></br>
170-
You don't need to do anything else, your files will be available when the app loads.
171-
<div
172-
onClick={async () => {
173-
await downloadBackup()
174-
}}
175-
data-id="downloadbackup-btn"
176-
className="btn btn-primary mt-1"
177-
>
178-
download backup
179-
</div>
180-
<div
181-
onClick={async () => {
182-
await migrateAndLoad()
183-
}}
184-
data-id="skipbackup-btn"
185-
className="btn btn-primary mt-1"
186-
>
187-
skip backup
171+
) : null}
172+
{showDownloader ? (
173+
<div className="preload-info-container alert alert-info">
174+
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work.
175+
<br></br>
176+
You don't need to do anything else, your files will be available when the app loads.
177+
<div
178+
onClick={async () => {
179+
await downloadBackup()
180+
}}
181+
data-id="downloadbackup-btn"
182+
className="btn btn-primary mt-1"
183+
>
184+
download backup
185+
</div>
186+
<div
187+
onClick={async () => {
188+
await migrateAndLoad()
189+
}}
190+
data-id="skipbackup-btn"
191+
className="btn btn-primary mt-1"
192+
>
193+
skip backup
194+
</div>
188195
</div>
189-
</div>
190-
) : null}
191-
{supported && !error && !showDownloader ? (
192-
<div>
193-
<div className='text-center'>
194-
<i className="fas fa-spinner fa-spin fa-2x"></i>
196+
) : null}
197+
{supported && !error && !showDownloader ? (
198+
<div className='text-center' style={{ marginTop: '16px' }}>
199+
<div className="pre-splash-spinner" role="progressbar" aria-label="Loading"></div>
195200
</div>
196-
{ tip && <div className='remix_tips text-center mt-3'>
197-
<div><b>DID YOU KNOW</b></div>
198-
<span>{tip}</span>
199-
</div> }
200-
</div>
201-
) : null}
201+
) : null}
202+
</div>
203+
<div className="preload-bottom opt-out">
204+
{ tip && <div className='remix_tips text-center mt-3'>
205+
<div><b>DID YOU KNOW</b></div>
206+
<span>{tip}</span>
207+
</div> }
208+
</div>
202209
</div>
203210
</>
204211
)
205212
}
206213

207-
const logo = (
208-
<svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100">
209-
<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" />
210-
<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" />
211-
<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" />
212-
</svg>
213-
)

apps/remix-ide/src/app/components/styles/preload.css

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
.preload-container {
2+
position: relative; /* allow absolute positioning inside */
3+
display: flex;
4+
flex-direction: column;
5+
justify-content: center; /* centers .preload-main vertically */
6+
align-items: center; /* centers .preload-main horizontally */
7+
height: 100vh;
8+
}
9+
10+
/* Center the main content while allowing a footer-like tips area below */
11+
.preload-main {
212
display: flex;
313
flex-direction: column;
4-
justify-content: center;
514
align-items: center;
6-
height: 100vh;
15+
justify-content: center;
16+
width: 100%;
17+
position: relative;
718
}
819

920
.preload-info-container {
@@ -20,5 +31,57 @@
2031
.preload-logo {
2132
min-width: 200px;
2233
max-width: 240px;
23-
padding-bottom: 1.5rem !important;
2434
}
35+
36+
/* Match index.html splash spinner exactly using theme variables */
37+
.pre-splash-spinner {
38+
width: 24px;
39+
height: 24px;
40+
border: 3px solid var(--bs-border-color-translucent, rgba(255,255,255,0.15));
41+
border-top-color: var(--bs-primary, #3b82f6);
42+
border-radius: 50%;
43+
animation: pre-splash-spin 1s linear infinite;
44+
display: inline-block;
45+
}
46+
47+
@keyframes pre-splash-spin { to { transform: rotate(360deg); } }
48+
49+
/* Match index.html splash typography */
50+
.preload-title {
51+
margin-top: 12px;
52+
font-size: 18px;
53+
letter-spacing: 0.06em;
54+
color: var(--bs-emphasis-color, #e6ecf8);
55+
text-align: center;
56+
}
57+
58+
.preload-sub {
59+
margin-top: 4px;
60+
font-size: 13px;
61+
color: var(--bs-secondary-color, #9aa7bd);
62+
text-align: center;
63+
}
64+
65+
/* Keep tips concise and stable in height */
66+
.remix_tips {
67+
max-width: 800px;
68+
margin: 0 auto;
69+
}
70+
71+
.remix_tips span {
72+
display: -webkit-box;
73+
line-clamp: 2;
74+
-webkit-line-clamp: 2;
75+
-webkit-box-orient: vertical;
76+
overflow: hidden;
77+
}
78+
79+
.preload-bottom.opt-out {
80+
position: absolute;
81+
top: 50%;
82+
left: 0;
83+
right: 0;
84+
text-align: center;
85+
transform: translateY(calc(50% + 60px)); /* 50% moves it to center, additional offset moves it below the content */
86+
margin-top: 40px;
87+
}

apps/remix-ide/src/app/tabs/theme-module.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,34 @@ export class ThemeModule extends Plugin {
9393
initTheme(callback?: () => void): void { // callback is setTimeOut in app.js which is always passed
9494
if (callback) this.initCallback = callback
9595
if (this.active) {
96-
document.getElementById('theme-link') ? document.getElementById('theme-link').remove() : null
96+
const existingThemeLink = document.getElementById('theme-link')
97+
if (existingThemeLink) existingThemeLink.remove()
9798
const nextTheme = this.themes[this.active] // Theme
9899
document.documentElement.style.setProperty('--theme', nextTheme.quality)
99100

100-
const theme = document.createElement('link')
101-
theme.setAttribute('rel', 'stylesheet')
102-
theme.setAttribute('href', nextTheme.url)
103-
theme.setAttribute('id', 'theme-link')
104-
theme.addEventListener('load', () => {
105-
if (callback) callback()
106-
})
107-
document.head.insertBefore(theme, document.head.firstChild)
101+
// Reuse preloaded theme link if present to avoid double loading
102+
const preloaded = document.getElementById('pre-theme-css') as HTMLLinkElement | null
103+
if (preloaded) {
104+
preloaded.id = 'theme-link'
105+
const desired = new URL(nextTheme.url, document.baseURI).href
106+
const current = preloaded.href
107+
if (current !== desired) {
108+
preloaded.addEventListener('load', () => { if (callback) callback() }, { once: true })
109+
preloaded.href = nextTheme.url
110+
} else {
111+
// Already loaded desired theme
112+
if (callback) callback()
113+
}
114+
} else {
115+
const theme = document.createElement('link')
116+
theme.setAttribute('rel', 'stylesheet')
117+
theme.setAttribute('href', nextTheme.url)
118+
theme.setAttribute('id', 'theme-link')
119+
theme.addEventListener('load', () => {
120+
if (callback) callback()
121+
})
122+
document.head.insertBefore(theme, document.head.firstChild)
123+
}
108124
//if (callback) callback()
109125
}
110126
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
(function(){
2+
try {
3+
// 1) Determine theme from Remix config stored in localStorage
4+
var raw = localStorage.getItem('config-v0.8:.remix.config');
5+
var theme = '';
6+
if (raw) {
7+
try {
8+
var cfg = JSON.parse(raw);
9+
theme = String((cfg && (cfg['settings/theme'] || (cfg.settings && (cfg.settings.theme || '')))) || '').toLowerCase();
10+
} catch (_) { theme = ''; }
11+
}
12+
13+
var isLight = theme.indexOf('light') !== -1;
14+
document.documentElement.setAttribute('data-pre-theme', isLight ? 'light' : 'dark');
15+
16+
// 2) Ensure the actual theme stylesheet is loaded ASAP (two themes supported)
17+
var existing = document.getElementById('pre-theme-css');
18+
if (!existing) {
19+
var href = isLight
20+
? 'assets/css/themes/remix-light_powaqg.css'
21+
: 'assets/css/themes/remix-dark_tvx1s2.css';
22+
var link = document.createElement('link');
23+
link.id = 'pre-theme-css';
24+
link.rel = 'stylesheet';
25+
link.href = href;
26+
document.head.appendChild(link);
27+
}
28+
29+
// 3) Refine splash content for Electron + OS without waiting for bundles
30+
var ua = navigator.userAgent || '';
31+
var low = ua.toLowerCase();
32+
var isElectron = low.indexOf('electron') !== -1;
33+
var title = document.getElementById('pre-splash-title');
34+
var sub = document.getElementById('pre-splash-sub');
35+
if (isElectron) {
36+
if (title) title.textContent = 'Remix Desktop';
37+
if (sub) {
38+
var os = low.indexOf('mac') !== -1 ? 'macOS' : (low.indexOf('win') !== -1 ? 'Windows' : (low.indexOf('linux') !== -1 ? 'Linux' : ''));
39+
sub.textContent = os ? ('Loading… ' + os) : 'Loading…';
40+
}
41+
} else {
42+
// Web: match Preload case for consistency
43+
if (title) title.textContent = 'REMIX IDE';
44+
}
45+
} catch (e) {
46+
// Last-resort fallback theme
47+
try { document.documentElement.setAttribute('data-pre-theme','dark'); } catch(_) {}
48+
}
49+
})();

0 commit comments

Comments
 (0)