Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
26c3dbf
add topbar files and entries
joeizang Jul 15, 2025
7fd827d
temp increase max-old-space-size
joeizang Jul 9, 2025
6e49c69
add to ide shell
joeizang Jul 16, 2025
3caa3d4
topbar up and running
joeizang Jul 16, 2025
2517af7
props for react side
joeizang Jul 17, 2025
eb9334a
topbar context
joeizang Jul 17, 2025
a39c3fc
add topbar provider and inject filePanel into topbar
joeizang Jul 17, 2025
eef4a3a
fix styles. add new style. add markup
joeizang Jul 18, 2025
f773907
fix error in plugin
joeizang Jul 18, 2025
10802c2
fix version number button.
joeizang Jul 23, 2025
03b72f4
share types from hometab
joeizang Jul 23, 2025
a082c86
topbar workspaces dropdown
joeizang Jul 23, 2025
c0322dd
fix typescript error
joeizang Jul 23, 2025
963c123
get latest release url for topbar
joeizang Jul 23, 2025
7c64752
fix dropdown menu items
joeizang Jul 23, 2025
b792e07
custom menu for topbar
joeizang Jul 23, 2025
6a57c64
update Workspace dropdown component for topbar
joeizang Jul 23, 2025
590e5d5
working on flyoutsubmenu
joeizang Jul 24, 2025
4f12dc5
fix style and position
joeizang Jul 24, 2025
01376e4
fix submenus for workspaces dropdown
joeizang Jul 24, 2025
8d61d35
cleanup scratch code
joeizang Jul 24, 2025
25db57e
set topbar plugin permission
joeizang Jul 25, 2025
8749ef1
add actions for dropdown menu and submenus
joeizang Jul 25, 2025
fe18fa7
add modal and tooltip capabilities
joeizang Jul 25, 2025
984427c
update submenu actions
joeizang Jul 25, 2025
54e2f1c
remove previous dropdown and hamburger menu
joeizang Jul 25, 2025
f1c6dff
Update remix desktop download url
joeizang Jul 25, 2025
0a0e4cd
Merge branch 'master' into ide-topbar-feature
joeizang Jul 25, 2025
96b7e3b
fix the lack of data-id for menu items
joeizang Jul 25, 2025
66cb7a9
e2e fixes. direct calls to workspaces related functions.
joeizang Jul 28, 2025
a6396bc
Merge branch 'master' into ide-topbar-feature
joeizang Jul 29, 2025
30c21f1
fix e2e for settings
joeizang Jul 30, 2025
e1c3a41
remove dependence on currentBranch to detect submodules in git branch
joeizang Jul 30, 2025
f7fb664
clean up topbar provider
joeizang Jul 30, 2025
6eba25a
handle git ops
joeizang Jul 30, 2025
78c6378
fixing e2e tests
joeizang Jul 30, 2025
015a959
Merge branch 'master' into ide-topbar-feature
joeizang Jul 30, 2025
437e587
fix git workspace submodule e2e
joeizang Jul 30, 2025
b1be318
fix more workspaces e2e tests
joeizang Jul 30, 2025
16c7671
Add direct git login. Add themes
joeizang Jul 30, 2025
26eb132
fix more e2e tests. disabled test need git auth.
joeizang Jul 30, 2025
c7573a3
update data-ids
joeizang Jul 30, 2025
21d988d
fix e2es. fix file explorer bottom padding.
Jul 31, 2025
8d3dbe5
addressing dgit tests
Jul 31, 2025
4c020dd
fix dgit and npm import tests
Jul 31, 2025
14b2028
fix e2e needing logo. fix git related tests needing login
Jul 31, 2025
87e0634
clean up scriptrunner e2e
Jul 31, 2025
ec2e7f2
fix import from git test
Jul 31, 2025
5e128d8
fix import from Github test
Jul 31, 2025
e462d58
fix e2es for workspaces and workspaces.git
Jul 31, 2025
fbbefa6
fix e2e
Aug 1, 2025
f3053f5
update dgit_github teest
Aug 1, 2025
ee541ed
fix padding issues, simplify layout
Aug 1, 2025
3b05a5a
find settings in matomo test
Aug 1, 2025
5ca47e6
fix settings in e2e
Aug 1, 2025
fb96822
fix settings access for e2e
Aug 1, 2025
06f7bce
fixh e2e solidity import modal
Aug 1, 2025
71a2354
fix more tests
Aug 1, 2025
029075a
fix rename workspace test
Aug 1, 2025
b34b961
tagged rename workspace test with #pr
Aug 1, 2025
a474c19
fix workspace delete test
Aug 1, 2025
29e418b
remove wait from workspaces test
Aug 1, 2025
d8ebb8b
prtag for rename and delete workspace
Aug 1, 2025
b18a795
fix workspace rename test. reduce zindex which was interfering with m…
Aug 1, 2025
9ab1d2b
remove pr label
Aug 1, 2025
77aa532
Add connect to Remixd. Fix comments from @Aniket-Engg
Aug 4, 2025
4af6b18
removed duplicate temporarily
Aug 4, 2025
38ace61
remove topbar from plugin manager ui
Aug 4, 2025
c1ddf98
switch icon based on current theme. fix console error for settings
Aug 4, 2025
434314f
change theme icon when theme change happens.
Aug 4, 2025
8ac62b2
cleanup console logs
Aug 4, 2025
b4195a7
update workspaces based on events and not timeout
Aug 4, 2025
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
2 changes: 1 addition & 1 deletion .env.local
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
gist_token=<token>
account_passphrase=<passphrase>
account_password=<password>
NODE_OPTIONS=--max-old-space-size=2048

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2008

NODE_OPTIONS=--max-old-space-size=4096
WALLET_CONNECT_PROJECT_ID=<project_id>
NOIR_COMPILER_BASE_URL_DEV=<dev_base_endpoint>
NOIR_COMPILER_BASE_URL_PROD=<prod_base_endpoint>
Expand Down
19 changes: 12 additions & 7 deletions apps/remix-ide/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,30 @@
import { RunTab, makeUdapp } from './app/udapp'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run

import { RemixEngine } from './remixEngine'
import { RemixAppManager } from './remixAppManager'
import { ThemeModule } from './app/tabs/theme-module'
import { LocaleModule } from './app/tabs/locale-module'
import { NetworkModule } from './app/tabs/network-module'
import { Web3ProviderModule } from './app/tabs/web3-provider'
import { CompileAndRun } from './app/tabs/compile-and-run'
import { PluginStateLogger } from './app/tabs/state-logger'
import { SidePanel } from './app/components/side-panel'
import { StatusBar } from './app/components/status-bar'
import { HiddenPanel } from './app/components/hidden-panel'
import { PinnedPanel } from './app/components/pinned-panel'
import { PopupPanel } from './app/components/popup-panel'
import { VerticalIcons } from './app/components/vertical-icons'
import { LandingPage } from './app/ui/landing-page/landing-page'
import { MainPanel } from './app/components/main-panel'
import { PermissionHandlerPlugin } from './app/plugins/permission-handler-plugin'
import { AstWalker } from '@remix-project/remix-astwalker'
import { LinkLibraries, DeployLibraries, OpenZeppelinProxy } from '@remix-project/core-plugin'
import { CodeParser } from './app/plugins/parser/code-parser'
import { SolidityScript } from './app/plugins/solidity-script'
import { StatusBar } from './app/components/status-bar'
import { Topbar } from './app/components/top-bar'
import { ThemeModule } from './app/tabs/theme-module'
import { VerticalIcons } from './app/components/vertical-icons'
import { RemixAIAssistant } from './app/plugins/remix-ai-assistant'
import { SolidityUmlGen } from './app/plugins/solidity-umlgen'
import { VyperCompilationDetailsPlugin } from './app/plugins/vyper-compilation-details'
import { ContractFlattener } from './app/plugins/contractFlattener'

import { WalkthroughService } from './walkthroughService'

Expand All @@ -45,11 +49,8 @@ import { ExternalHttpProvider } from './app/providers/external-http-provider'
import { EnvironmentExplorer } from './app/providers/environment-explorer'
import { FileDecorator } from './app/plugins/file-decorator'
import { CodeFormat } from './app/plugins/code-format'
import { SolidityUmlGen } from './app/plugins/solidity-umlgen'
import { CompilationDetailsPlugin } from './app/plugins/compile-details'
import { VyperCompilationDetailsPlugin } from './app/plugins/vyper-compilation-details'
import { RemixGuidePlugin } from './app/plugins/remixGuide'
import { ContractFlattener } from './app/plugins/contractFlattener'
import { TemplatesPlugin } from './app/plugins/remix-templates'
import { fsPlugin } from './app/plugins/electron/fsPlugin'
import { isoGitPlugin } from './app/plugins/electron/isoGitPlugin'
Expand Down Expand Up @@ -154,6 +155,7 @@ class AppComponent {
pinnedPanel: PinnedPanel
popupPanel: PopupPanel
statusBar: StatusBar
topBar: Topbar
settings: SettingsTab
params: any
desktopClientMode: boolean
Expand Down Expand Up @@ -520,10 +522,11 @@ class AppComponent {
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new Filepanel(appManager, contentImport)
this.statusBar = new StatusBar(filePanel, this.menuicons)
this.topBar = new Topbar(filePanel)
const landingPage = new LandingPage(appManager, this.menuicons, fileManager, filePanel, contentImport)
this.settings = new SettingsTab(Registry.getInstance().get('config').api, editor)//, appManager)

this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, this.statusBar, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel, this.popupPanel])
this.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, this.statusBar, this.topBar, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel, this.popupPanel])

// CONTENT VIEWS & DEFAULT PLUGINS
const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
Expand Down Expand Up @@ -600,7 +603,9 @@ class AppComponent {
'pluginStateLogger',
'matomo'
])

await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['topbar'])
await this.appManager.activatePlugin(['statusBar'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['pinnedPanel'])
Expand Down
116 changes: 116 additions & 0 deletions apps/remix-ide/src/app/components/top-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable @nrwl/nx/enforce-module-boundaries */
import React from 'react'
import { TopbarProvider } from '@remix-ui/top-bar'
import packageJson from '../../../../../package.json'
import { EventEmitter } from 'events'
import { Plugin } from '@remixproject/engine'
import { PluginViewWrapper } from '@remix-ui/helper'
import { AppAction } from 'libs/remix-ui/app/src/lib/remix-app/actions/app'
import FilePanel from '../panels/file-panel'
import { WorkspaceMetadata } from 'libs/remix-ui/workspace/src/lib/types'
import { gitUIPanels } from '@remix-ui/git'
import { HOME_TAB_NEW_UPDATES } from 'libs/remix-ui/home-tab/src/lib/components/constant'
import axios from 'axios'
import { UpdateInfo } from 'libs/remix-ui/home-tab/src/lib/components/types/carouselTypes'
import { loginWithGitHub } from 'libs/remix-ui/git/src/lib/pluginActions'

const TopBarProfile = {
name: 'topbar',
displayName: 'Top Bar',
description: '',
version: packageJson.version,
icon: '',
methods: [],
events: []
}

export class Topbar extends Plugin {
dispatch: React.Dispatch<any> = () => { }
appStateDispatch: React.Dispatch<AppAction> = () => { }
htmlElement: HTMLDivElement
events: EventEmitter
topbarExpandPath: string
filePanel: FilePanel
workspaces: WorkspaceMetadata[]
currentWorkspaceMetadata: WorkspaceMetadata

constructor(filePanel: FilePanel) {
super(TopBarProfile)
this.filePanel = filePanel
this.workspaces = []
this.currentWorkspaceMetadata = null
}

onActivation(): void {
this.renderComponent()
}

onDeactivation(): void {

}

async getWorkspaces() {
while (this.workspaces.length === 0) {
await new Promise(resolve => setTimeout(resolve, 100))
this.workspaces = await this.call('filePanel', 'getWorkspaces')
}
return this.workspaces
}

async getCurrentWorkspaceMetadata() {
while (!this.currentWorkspaceMetadata) {
await new Promise(resolve => setTimeout(resolve, 100))
this.currentWorkspaceMetadata = await this.call('filePanel', 'getCurrentWorkspace')
}
}

async logInGithub () {
await this.call('menuicons', 'select', 'dgit')
await this.call('dgit', 'open', gitUIPanels.GITHUB)
}

async getLatestUpdates() {
try {
const response = await axios.get(HOME_TAB_NEW_UPDATES)
return response.data
} catch (error) {
console.error('Error fetching plugin list:', error)
}
}

async getLatestReleaseNotesUrl () {
const response = await this.getLatestUpdates()
const data: UpdateInfo[] = response
const interim = data.find(x => x.action.label.includes('Release notes'))
const targetUrl = interim.action.url
const currentReleaseVersion = interim.badge.split(' ')[0]
return [targetUrl, currentReleaseVersion]
}

setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}

setAppStateDispatch(appStateDispatch: React.Dispatch<AppAction>) {
this.appStateDispatch = appStateDispatch
}

renderComponent() {
this.dispatch({
plugins: this,
})
}

updateComponent(state: any) {
return <TopbarProvider plugin={this} />
}

render() {
return (
<div data-id="top-bar-container">
<PluginViewWrapper useAppContext={true} plugin={this} />
</div>
)
}

}
42 changes: 21 additions & 21 deletions apps/remix-ide/src/app/files/fileProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default class FileProvider {
async _exists (path) {
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
const unprefixedpath = this.removePrefix(path)
return path === this.type ? true : await window.remixFileSystem.exists(unprefixedpath)
return path === this.type ? true : await (window as any).remixFileSystem.exists(unprefixedpath)
}

init (cb) {
Expand All @@ -96,7 +96,7 @@ export default class FileProvider {
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
const unprefixedpath = this.removePrefix(path)
try {
const content = await window.remixFileSystem.readFile(unprefixedpath, options)
const content = await (window as any).remixFileSystem.readFile(unprefixedpath, options)
if (cb) cb(null, content)
return content
} catch (err) {
Expand All @@ -108,14 +108,14 @@ export default class FileProvider {
async set (path, content, cb, options = { encoding: 'utf8' }) {
cb = cb || function () { /* do nothing. */ }
const unprefixedpath = this.removePrefix(path)
const exists = await window.remixFileSystem.exists(unprefixedpath)
if (exists && await window.remixFileSystem.readFile(unprefixedpath, options) === content) {
const exists = await (window as any).remixFileSystem.exists(unprefixedpath)
if (exists && await (window as any).remixFileSystem.readFile(unprefixedpath, options) === content) {
if (cb) cb()
return null
}
await this.createDir(path.substr(0, path.lastIndexOf('/')), null)
try {
await window.remixFileSystem.writeFile(unprefixedpath, content, options)
await (window as any).remixFileSystem.writeFile(unprefixedpath, content, options)
} catch (e) {
if (cb) cb(e)
return false
Expand All @@ -142,9 +142,9 @@ export default class FileProvider {
let currentCheck = ''
for (const value of paths) {
currentCheck = currentCheck + '/' + value
if (!await window.remixFileSystem.exists(currentCheck)) {
if (!await (window as any).remixFileSystem.exists(currentCheck)) {
try {
await window.remixFileSystem.mkdir(currentCheck)
await (window as any).remixFileSystem.mkdir(currentCheck)
this.event.emit('folderAdded', this._normalizePath(currentCheck))
} catch (error) {
console.log(error)
Expand All @@ -165,14 +165,14 @@ export default class FileProvider {

async isDirectory (path) {
const unprefixedpath = this.removePrefix(path)
const isDirectory = path === this.type ? true : (await window.remixFileSystem.stat(unprefixedpath)).isDirectory()
const isDirectory = path === this.type ? true : (await (window as any).remixFileSystem.stat(unprefixedpath)).isDirectory()
return isDirectory
}

async isFile (path) {
path = this.getPathFromUrl(path) || path // ensure we actually use the normalized path from here
path = this.removePrefix(path)
return (await window.remixFileSystem.stat(path)).isFile()
return (await (window as any).remixFileSystem.stat(path)).isFile()
}

/**
Expand All @@ -181,13 +181,13 @@ export default class FileProvider {
*/
async remove (path: string) {
path = this.removePrefix(path)
if (await window.remixFileSystem.exists(path)) {
const stat = await window.remixFileSystem.stat(path)
if (await (window as any).remixFileSystem.exists(path)) {
const stat = await( window as any).remixFileSystem.stat(path)
try {
if (!stat.isDirectory()) {
return await this.removeFile(path)
} else {
await window.remixFileSystem.unlink(path)
await (window as any).remixFileSystem.unlink(path)
this.event.emit('fileRemoved', this._normalizePath(path))
}
} catch (e) {
Expand All @@ -209,18 +209,18 @@ export default class FileProvider {

const json = {}
path = this.removePrefix(path)
if (await window.remixFileSystem.exists(path)) {
if (await (window as any).remixFileSystem.exists(path)) {
try {
const items = await window.remixFileSystem.readdir(path)
const items = await (window as any).remixFileSystem.readdir(path)
visitFolder({ path })
if (items.length !== 0) {
for (const item of items) {
const file: any = {}
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}`
if ((await window.remixFileSystem.stat(curPath)).isDirectory()) {
if ((await (window as any).remixFileSystem.stat(curPath)).isDirectory()) {
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder)
} else {
file.content = await window.remixFileSystem.readFile(curPath, 'utf8')
file.content = await (window as any).remixFileSystem.readFile(curPath, 'utf8')
visitFile({ path: curPath, content: file.content })
}
json[curPath] = file
Expand Down Expand Up @@ -248,8 +248,8 @@ export default class FileProvider {

async removeFile (path) {
path = this.removePrefix(path)
if (await window.remixFileSystem.exists(path) && !(await window.remixFileSystem.stat(path)).isDirectory()) {
await window.remixFileSystem.unlink(path)
if (await (window as any).remixFileSystem.exists(path) && !(await (window as any).remixFileSystem.stat(path)).isDirectory()) {
await (window as any).remixFileSystem.unlink(path)
this.event.emit('fileRemoved', this._normalizePath(path))
return true
} else return false
Expand All @@ -259,7 +259,7 @@ export default class FileProvider {
const unprefixedoldPath = this.removePrefix(oldPath)
const unprefixednewPath = this.removePrefix(newPath)
if (await this._exists(unprefixedoldPath)) {
await window.remixFileSystem.rename(unprefixedoldPath, unprefixednewPath)
await (window as any).remixFileSystem.rename(unprefixedoldPath, unprefixednewPath)
this.event.emit('fileRenamed',
this._normalizePath(unprefixedoldPath),
this._normalizePath(unprefixednewPath),
Expand All @@ -275,14 +275,14 @@ export default class FileProvider {
path = this.removePrefix(path)
if (path.indexOf('/') !== 0) path = '/' + path
try {
const files = await window.remixFileSystem.readdir(path)
const files = await (window as any).remixFileSystem.readdir(path)
const ret = {}
if (files) {
for (let element of files) {
path = path.replace(/^\/|\/$/g, '') // remove first and last slash
element = element.replace(/^\/|\/$/g, '') // remove first and last slash
const absPath = (path === '/' ? '' : path) + '/' + element
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await window.remixFileSystem.stat(absPath)).isDirectory() }
ret[absPath.indexOf('/') === 0 ? absPath.substr(1, absPath.length) : absPath] = { isDirectory: (await (window as any).remixFileSystem.stat(absPath)).isDirectory() }
// ^ ret does not accept path starting with '/'
}
}
Expand Down
1 change: 1 addition & 0 deletions apps/remix-ide/src/remixAppManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export function isNative(name) {
'solidityUnitTesting',
'layout',
'statusBar',
'topbar',
'notification',
'hardhat-provider',
'ganache-provider',
Expand Down
3 changes: 3 additions & 0 deletions libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ const RemixApp = (props: IRemixAppUi) => {
{showEnterDialog && <EnterDialog handleUserChoice={(type) => handleUserChosenType(type)}></EnterDialog>}
{showManagePreferencesDialog && <ManagePreferencesDialog savePreferencesFn={() => setShowEnterDialog(true)}></ManagePreferencesDialog>}
<div className='d-flex flex-column'>
<div className='top-bar'>
{props.app.topBar.render()}
</div>
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
<div id="icon-panel" data-id="remixIdeIconPanel" className="custom_icon_panel iconpanel bg-light">
{props.app.menuicons.render()}
Expand Down
6 changes: 5 additions & 1 deletion libs/remix-ui/app/src/lib/remix-app/style/remix-app.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pre {
}
.remixIDE {
width : 100%;
height : 100vh;
height : 95vh;
overflow : hidden;
flex-direction : row;
display : flex;
Expand Down Expand Up @@ -39,6 +39,10 @@ pre {
}
.statusBar {

}

.top-bar {

}
.pinnedpanel {
width : 320px;
Expand Down
Loading