parent
40a291b4e4
commit
41f441748a
@ -0,0 +1,115 @@ |
|||||||
|
import { RemixApp } from '@remix-ui/app' |
||||||
|
import React, { useEffect, useRef, useState } from 'react' |
||||||
|
import { render } from 'react-dom' |
||||||
|
import * as packageJson from '../../../../../package.json' |
||||||
|
import { fileSystems } from '../files/fileSystem' |
||||||
|
import { indexedDBFileSystem } from '../files/filesystems/indexedDB' |
||||||
|
import { localStorageFS } from '../files/filesystems/localStorage' |
||||||
|
import { fileSystemUtility } from '../files/filesystems/migrateFileSystem' |
||||||
|
|
||||||
|
export const Preload = () => { |
||||||
|
|
||||||
|
const [supported, setSupported] = useState<boolean>(true) |
||||||
|
const [error, setError] = useState<boolean>(false) |
||||||
|
const [showDownloader, setShowDownloader] = useState<boolean>(false) |
||||||
|
const remixFileSystems = useRef<fileSystems>(new fileSystems()) |
||||||
|
|
||||||
|
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> |
||||||
|
|
||||||
|
function loadAppComponent() { |
||||||
|
import('../../app').then((AppComponent) => { |
||||||
|
const appComponent = new AppComponent.default() |
||||||
|
appComponent.run().then(() => { |
||||||
|
render( |
||||||
|
<> |
||||||
|
<RemixApp app={appComponent} /> |
||||||
|
</>, |
||||||
|
document.getElementById('root') |
||||||
|
) |
||||||
|
}) |
||||||
|
}).catch(err => { |
||||||
|
console.log('Error loading Remix:', err) |
||||||
|
setError(true) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const downloadBackup = async () =>{ |
||||||
|
const migrator = new fileSystemUtility() |
||||||
|
migrator.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) |
||||||
|
loadAppComponent() |
||||||
|
} |
||||||
|
|
||||||
|
const skipBackup = async() => { |
||||||
|
loadAppComponent() |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
|
||||||
|
|
||||||
|
async function loadStorage() { |
||||||
|
const remixIndexedDB = new indexedDBFileSystem() |
||||||
|
const localStorageFileSystem = new localStorageFS() |
||||||
|
|
||||||
|
|
||||||
|
await remixFileSystems.current.addFileSystem(remixIndexedDB) |
||||||
|
await remixFileSystems.current.addFileSystem(localStorageFileSystem) |
||||||
|
|
||||||
|
setShowDownloader(true) |
||||||
|
|
||||||
|
const migrator = new fileSystemUtility() |
||||||
|
await migrator.migrate(localStorageFileSystem, remixIndexedDB) |
||||||
|
const fsLoaded = await remixFileSystems.current.setFileSystem([remixIndexedDB, localStorageFileSystem]) |
||||||
|
|
||||||
|
if (fsLoaded) { |
||||||
|
console.log(fsLoaded.name + ' activated') |
||||||
|
console.log(localStorageFileSystem) |
||||||
|
// migrator.downloadBackup(localStorageFileSystem)
|
||||||
|
//loadAppComponent()
|
||||||
|
} else { |
||||||
|
console.log('No filesystem could be loaded') |
||||||
|
setSupported(false) |
||||||
|
// displayBrowserNotSupported()
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
loadStorage() |
||||||
|
}, []) |
||||||
|
|
||||||
|
return <> |
||||||
|
<div style={{ display: 'block' }} className='centered'> |
||||||
|
{logo} |
||||||
|
<div className="info-secondary splash"> |
||||||
|
REMIX IDE |
||||||
|
<br /> |
||||||
|
<span className='version'> v{packageJson.version}</span> |
||||||
|
</div> |
||||||
|
{!supported ? |
||||||
|
<div style={{ marginTop: '50%', textAlign: 'center' }}> |
||||||
|
Your browser does not support any of the filesytems required by Remix. |
||||||
|
Either change the settings in your browser or use a supported browser. |
||||||
|
</div> : null} |
||||||
|
{error ? |
||||||
|
<div style={{ marginTop: '50%', textAlign: 'center' }}> |
||||||
|
An unknown error has occured loading the application. |
||||||
|
</div> : null} |
||||||
|
{showDownloader ? |
||||||
|
<div style={{ marginTop: '20%', textAlign: 'center' }}> |
||||||
|
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()}} className='btn btn-primary mt-1'>download backup</div> |
||||||
|
<div onClick={async() => {await skipBackup()}}className='btn btn-primary mt-1'>skip backup</div> |
||||||
|
</div> : null} |
||||||
|
{(supported && !error && !showDownloader) ? |
||||||
|
<div style={{ marginTop: '50%', textAlign: 'center' }}> |
||||||
|
<i className="fas fa-spinner fa-spin fa-2x"></i> |
||||||
|
</div> : null} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
export class fileSystem { |
||||||
|
name: string |
||||||
|
enabled: boolean |
||||||
|
available: boolean |
||||||
|
fs: any |
||||||
|
fsCallBack: any; |
||||||
|
hasWorkSpaces: boolean |
||||||
|
loaded: boolean |
||||||
|
load: () => Promise<unknown> |
||||||
|
test: () => Promise<unknown> |
||||||
|
|
||||||
|
constructor() { |
||||||
|
this.available = false |
||||||
|
this.enabled = false |
||||||
|
this.hasWorkSpaces = false |
||||||
|
this.loaded = false |
||||||
|
} |
||||||
|
ReadWriteTest = async () => { |
||||||
|
try { |
||||||
|
const str = 'Hello World' |
||||||
|
await this.fs.writeFile('/test.txt', str, 'utf8') |
||||||
|
if (await this.fs.readFile('/test.txt', 'utf8') === str) { |
||||||
|
console.log('Read/Write Test Passed') |
||||||
|
return true |
||||||
|
} |
||||||
|
await this.fs.remove('/test.txt', 'utf8') |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
checkWorkspaces = async () => { |
||||||
|
try { |
||||||
|
await this.fs.stat('.workspaces') |
||||||
|
this.hasWorkSpaces = true |
||||||
|
} catch (e) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
set = async () => { |
||||||
|
const w = (window as any) |
||||||
|
if (!this.loaded) return false |
||||||
|
w.remixFileSystem = this.fs |
||||||
|
w.remixFileSystemCallback = this.fsCallBack |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export class fileSystems { |
||||||
|
fileSystems: Record<string, fileSystem> |
||||||
|
constructor() { |
||||||
|
this.fileSystems = {} |
||||||
|
} |
||||||
|
|
||||||
|
addFileSystem = async (fs: fileSystem) => { |
||||||
|
try { |
||||||
|
this.fileSystems[fs.name] = fs |
||||||
|
if (await fs.test()) await fs.load() |
||||||
|
console.log(fs.name + ' is loaded...') |
||||||
|
return true |
||||||
|
} catch (e) { |
||||||
|
console.log(fs.name + ' not available...') |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* sets filesystem using list as fallback |
||||||
|
* @param {string[]} names |
||||||
|
* @returns {Promise} |
||||||
|
*/ |
||||||
|
setFileSystem = async (filesystems?: fileSystem[]): Promise<fileSystem> => { |
||||||
|
for (const fs of filesystems) { |
||||||
|
if (this.fileSystems[fs.name]) { |
||||||
|
const result = await this.fileSystems[fs.name].set() |
||||||
|
if (result) return this.fileSystems[fs.name] |
||||||
|
} |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,91 @@ |
|||||||
|
import LightningFS from "@isomorphic-git/lightning-fs" |
||||||
|
import { fileSystem } from "../fileSystem" |
||||||
|
|
||||||
|
export class IndexedDBStorage extends LightningFS { |
||||||
|
base: LightningFS.PromisifedFS |
||||||
|
addSlash: (file: string) => string |
||||||
|
extended: { exists: (path: string) => Promise<unknown>; rmdir: (path: any) => Promise<void>; readdir: (path: any) => Promise<string[]>; unlink: (path: any) => Promise<void>; mkdir: (path: any) => Promise<void>; readFile: (path: any, options: any) => Promise<Uint8Array>; rename: (from: any, to: any) => Promise<void>; writeFile: (path: any, content: any, options: any) => Promise<void>; stat: (path: any) => Promise<import("fs").Stats>; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise<void>; deactivate(): Promise<void>; lstat(filePath: string): Promise<import("fs").Stats>; readlink(filePath: string): Promise<string>; symlink(target: string, filePath: string): Promise<void> } |
||||||
|
constructor(name: string) { |
||||||
|
super(name) |
||||||
|
this.addSlash = (file) => { |
||||||
|
if (!file.startsWith('/')) file = '/' + file |
||||||
|
return file |
||||||
|
} |
||||||
|
this.base = this.promises |
||||||
|
this.extended = { |
||||||
|
...this.promises, |
||||||
|
exists: async (path: string) => { |
||||||
|
return new Promise((resolve) => { |
||||||
|
this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) |
||||||
|
}) |
||||||
|
}, |
||||||
|
rmdir: async (path) => { |
||||||
|
return this.base.rmdir(this.addSlash(path)) |
||||||
|
}, |
||||||
|
readdir: async (path) => { |
||||||
|
return this.base.readdir(this.addSlash(path)) |
||||||
|
}, |
||||||
|
unlink: async (path) => { |
||||||
|
return this.base.unlink(this.addSlash(path)) |
||||||
|
}, |
||||||
|
mkdir: async (path) => { |
||||||
|
return this.base.mkdir(this.addSlash(path)) |
||||||
|
}, |
||||||
|
readFile: async (path, options) => { |
||||||
|
return this.base.readFile(this.addSlash(path), options) |
||||||
|
}, |
||||||
|
rename: async (from, to) => { |
||||||
|
return this.base.rename(this.addSlash(from), this.addSlash(to)) |
||||||
|
}, |
||||||
|
writeFile: async (path, content, options) => { |
||||||
|
return this.base.writeFile(this.addSlash(path), content, options) |
||||||
|
}, |
||||||
|
stat: async (path) => { |
||||||
|
return this.base.stat(this.addSlash(path)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
export class indexedDBFileSystem extends fileSystem { |
||||||
|
constructor() { |
||||||
|
super() |
||||||
|
this.name = 'indexedDB' |
||||||
|
} |
||||||
|
|
||||||
|
load = async () => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
try { |
||||||
|
const fs = new IndexedDBStorage('RemixFileSystem') |
||||||
|
fs.init('RemixFileSystem') |
||||||
|
this.fs = fs.extended |
||||||
|
this.fsCallBack = fs |
||||||
|
this.loaded = true |
||||||
|
resolve(true) |
||||||
|
} catch (e) { |
||||||
|
reject(e) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
test = async () => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!window.indexedDB) { |
||||||
|
this.available = false |
||||||
|
reject('No indexedDB on window') |
||||||
|
} |
||||||
|
const request = window.indexedDB.open("RemixTestDataBase", 3); |
||||||
|
request.onerror = () => { |
||||||
|
this.available = false |
||||||
|
reject('Error creating test database') |
||||||
|
}; |
||||||
|
request.onsuccess = () => { |
||||||
|
window.indexedDB.deleteDatabase("RemixTestDataBase"); |
||||||
|
this.available = true |
||||||
|
resolve(true) |
||||||
|
}; |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
import { fileSystem } from "../fileSystem"; |
||||||
|
|
||||||
|
export class localStorageFS extends fileSystem { |
||||||
|
|
||||||
|
constructor() { |
||||||
|
super() |
||||||
|
this.name = 'localstorage' |
||||||
|
} |
||||||
|
load = async () => { |
||||||
|
const me = this |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
try { |
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
BrowserFS.install(window) |
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
BrowserFS.configure({ |
||||||
|
fs: 'LocalStorage' |
||||||
|
}, async function (e) { |
||||||
|
if (e) { |
||||||
|
console.log('BROWSEFS Error: ' + e) |
||||||
|
reject(e) |
||||||
|
} else { |
||||||
|
me.fs = { ...window.require('fs') } |
||||||
|
me.fsCallBack = window.require('fs') |
||||||
|
me.fs.readdir = me.fs.readdirSync |
||||||
|
me.fs.readFile = me.fs.readFileSync |
||||||
|
me.fs.writeFile = me.fs.writeFileSync |
||||||
|
me.fs.stat = me.fs.statSync |
||||||
|
me.fs.unlink = me.fs.unlinkSync |
||||||
|
me.fs.rmdir = me.fs.rmdirSync |
||||||
|
me.fs.mkdir = me.fs.mkdirSync |
||||||
|
me.fs.rename = me.fs.renameSync |
||||||
|
me.fs.exists = me.fs.existsSync |
||||||
|
me.loaded = true |
||||||
|
resolve(true) |
||||||
|
} |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
console.log('BrowserFS is not ready!') |
||||||
|
reject(e) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
test = async () => { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
const test = 'test'; |
||||||
|
try { |
||||||
|
localStorage.setItem(test, test); |
||||||
|
localStorage.removeItem(test); |
||||||
|
resolve(true) |
||||||
|
} catch(e) { |
||||||
|
reject(e) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,189 @@ |
|||||||
|
import { hashMessage } from "ethers/lib/utils" |
||||||
|
import JSZip from "jszip" |
||||||
|
import { fileSystem } from "../fileSystem" |
||||||
|
|
||||||
|
export class fileSystemUtility { |
||||||
|
migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { |
||||||
|
try { |
||||||
|
await fsFrom.checkWorkspaces() |
||||||
|
await fsTo.checkWorkspaces() |
||||||
|
|
||||||
|
if (fsTo.hasWorkSpaces) { |
||||||
|
console.log(`${fsTo.name} already has files`) |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
if (!fsFrom.hasWorkSpaces) { |
||||||
|
console.log('no files to migrate') |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
await this.populateWorkspace(testData, fsFrom.fs) |
||||||
|
const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) |
||||||
|
console.log(fsFrom.name, hashMessage(JSON.stringify(fromFiles)), fromFiles) |
||||||
|
await this.populateWorkspace(fromFiles, fsTo.fs) |
||||||
|
const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) |
||||||
|
|
||||||
|
if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { |
||||||
|
console.log('file migration successful') |
||||||
|
return true |
||||||
|
} else { |
||||||
|
console.log('file migration failed falling back to ' + fsFrom.name) |
||||||
|
fsTo.loaded = false |
||||||
|
return false |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
console.log('file migration failed falling back to ' + fsFrom.name) |
||||||
|
fsTo.loaded = false |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
downloadBackup = async (fs: fileSystem) => { |
||||||
|
try { |
||||||
|
const zip = new JSZip() |
||||||
|
await fs.checkWorkspaces() |
||||||
|
await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { |
||||||
|
zip.file(path, content) |
||||||
|
}) |
||||||
|
const blob = await zip.generateAsync({ type: 'blob' }) |
||||||
|
const today = new Date() |
||||||
|
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() |
||||||
|
const time = today.getHours() + 'h' + today.getMinutes() + 'min' |
||||||
|
this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) |
||||||
|
|
||||||
|
} catch (e) { |
||||||
|
console.log(e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
populateWorkspace = async (json, fs) => { |
||||||
|
for (const item in json) { |
||||||
|
const isFolder = json[item].content === undefined |
||||||
|
if (isFolder) { |
||||||
|
await this.createDir(item, fs) |
||||||
|
await this.populateWorkspace(json[item].children, fs) |
||||||
|
} else { |
||||||
|
await fs.writeFile(item, json[item].content, 'utf8') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* copy the folder recursively |
||||||
|
* @param {string} path is the folder to be copied over |
||||||
|
* @param {Function} visitFile is a function called for each visited files |
||||||
|
* @param {Function} visitFolder is a function called for each visited folders |
||||||
|
*/ |
||||||
|
copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { |
||||||
|
visitFile = visitFile || (() => { }) |
||||||
|
visitFolder = visitFolder || (() => { }) |
||||||
|
return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* copy the folder recursively (internal use) |
||||||
|
* @param {string} path is the folder to be copied over |
||||||
|
* @param {Function} visitFile is a function called for each visited files |
||||||
|
* @param {Function} visitFolder is a function called for each visited folders |
||||||
|
*/ |
||||||
|
async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { |
||||||
|
visitFile = visitFile || function () { /* do nothing. */ } |
||||||
|
visitFolder = visitFolder || function () { /* do nothing. */ } |
||||||
|
|
||||||
|
const json = {} |
||||||
|
// path = this.removePrefix(path)
|
||||||
|
if (await fs.exists(path)) { |
||||||
|
const items = await fs.readdir(path) |
||||||
|
visitFolder({ path }) |
||||||
|
if (items.length !== 0) { |
||||||
|
for (const item of items) { |
||||||
|
const file: any = {} |
||||||
|
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` |
||||||
|
if ((await fs.stat(curPath)).isDirectory()) { |
||||||
|
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) |
||||||
|
} else { |
||||||
|
file.content = await fs.readFile(curPath, 'utf8') |
||||||
|
if (cb) cb({ path: curPath, content: file.content }) |
||||||
|
visitFile({ path: curPath, content: file.content }) |
||||||
|
|
||||||
|
} |
||||||
|
json[curPath] = file |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return json |
||||||
|
} |
||||||
|
|
||||||
|
createDir = async (path, fs) => { |
||||||
|
const paths = path.split('/') |
||||||
|
if (paths.length && paths[0] === '') paths.shift() |
||||||
|
let currentCheck = '' |
||||||
|
for (const value of paths) { |
||||||
|
currentCheck = currentCheck + (currentCheck ? '/' : '') + value |
||||||
|
if (!await fs.exists(currentCheck)) { |
||||||
|
await fs.mkdir(currentCheck) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
saveAs = (blob, name) => { |
||||||
|
const node = document.createElement('a') |
||||||
|
node.download = name |
||||||
|
node.rel = 'noopener' |
||||||
|
node.href = URL.createObjectURL(blob) |
||||||
|
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
|
||||||
|
setTimeout(function () { |
||||||
|
try { |
||||||
|
node.dispatchEvent(new MouseEvent('click')) |
||||||
|
} catch (e) { |
||||||
|
const evt = document.createEvent('MouseEvents') |
||||||
|
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, |
||||||
|
20, false, false, false, false, 0, null) |
||||||
|
node.dispatchEvent(evt) |
||||||
|
} |
||||||
|
}, 0) // 40s
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/* eslint-disable no-template-curly-in-string */ |
||||||
|
const testData = { |
||||||
|
'.workspaces': { |
||||||
|
children: { |
||||||
|
'.workspaces/default_workspace': { |
||||||
|
children: { |
||||||
|
'.workspaces/default_workspace/README.txt': { |
||||||
|
content: 'TEST README' |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
'.workspaces/emptyspace': { |
||||||
|
|
||||||
|
}, |
||||||
|
'.workspaces/workspace_test': { |
||||||
|
children: { |
||||||
|
'.workspaces/workspace_test/TEST_README.txt': { |
||||||
|
content: 'TEST README' |
||||||
|
}, |
||||||
|
'.workspaces/workspace_test/test_contracts': { |
||||||
|
children: { |
||||||
|
'.workspaces/workspace_test/test_contracts/1_Storage.sol': { |
||||||
|
content: 'testing' |
||||||
|
}, |
||||||
|
'.workspaces/workspace_test/test_contracts/artifacts': { |
||||||
|
children: { |
||||||
|
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { |
||||||
|
content: '{ "test": "data" }' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,45 +1,29 @@ |
|||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
import React from 'react' |
import React, { useState } from 'react' |
||||||
import { render } from 'react-dom' |
import { render } from 'react-dom' |
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
import { RemixApp } from '@remix-ui/app' |
|
||||||
import * as packageJson from '../../../package.json' |
|
||||||
import './index.css' |
import './index.css' |
||||||
|
import { ThemeModule } from './app/tabs/theme-module' |
||||||
|
import { Preload } from './app/components/preload' |
||||||
|
import Config from './config' |
||||||
|
import Registry from './app/state/registry' |
||||||
|
import { Storage } from '@remix-project/remix-lib' |
||||||
|
|
||||||
|
(async function () { |
||||||
|
|
||||||
|
// load app config
|
||||||
|
const configStorage = new Storage('config-v0.8:') |
||||||
|
const config = new Config(configStorage) |
||||||
|
Registry.getInstance().put({ api: config, name: 'config' }) |
||||||
|
const theme = new ThemeModule() |
||||||
|
theme.initTheme() |
||||||
|
|
||||||
(function () { |
|
||||||
render( |
render( |
||||||
<React.StrictMode> |
<React.StrictMode> |
||||||
<div style={{ display: 'block' }} className='centered'> |
<Preload></Preload> |
||||||
<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> |
|
||||||
<div className="info-secondary splash"> |
|
||||||
REMIX IDE |
|
||||||
<br /> |
|
||||||
<span className='version'> v{ packageJson.version }</span> |
|
||||||
</div> |
|
||||||
<div style={{ marginTop: '50%', textAlign: 'center' }}> |
|
||||||
<i className="fas fa-spinner fa-spin fa-2x"></i> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</React.StrictMode>, |
</React.StrictMode>, |
||||||
document.getElementById('root') |
document.getElementById('root') |
||||||
) |
) |
||||||
|
|
||||||
})() |
})() |
||||||
|
|
||||||
import ('./app').then((AppComponent) => { |
|
||||||
const appComponent = new AppComponent.default() |
|
||||||
appComponent.run().then(() => { |
|
||||||
render( |
|
||||||
<> |
|
||||||
<RemixApp app={appComponent} /> |
|
||||||
</>, |
|
||||||
document.getElementById('root') |
|
||||||
) |
|
||||||
}) |
|
||||||
}).catch(err => { |
|
||||||
console.log('Error on loading Remix:', err) |
|
||||||
}) |
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue