pull/5013/head
Your Name 4 months ago
commit 4d3dd97c80
  1. 50
      apps/learneth/src/pages/StepDetail/index.tsx
  2. 1
      apps/learneth/src/redux/models/remixide.ts
  3. 4
      apps/learneth/src/redux/models/workshop.ts
  4. 4
      apps/remix-ide-e2e/src/tests/dgit_local.test.ts
  5. 4
      apps/remix-ide/ci/deploy_from_travis_remix-alpha.sh
  6. 4
      apps/remix-ide/ci/deploy_from_travis_remix-beta.sh
  7. 4
      apps/remix-ide/ci/deploy_from_travis_remix-live.sh
  8. 6
      apps/remix-ide/src/app.js
  9. 190
      apps/remix-ide/src/app/providers/environment-explorer.tsx
  10. 6
      apps/remix-ide/src/app/providers/style/environment-explorer.css
  11. BIN
      apps/remix-ide/src/assets/img/EnvironmentExplorerLogo.webp
  12. 52
      apps/remix-ide/src/blockchain/blockchain.tsx
  13. 5
      apps/remix-ide/src/blockchain/execution-context.js
  14. 18
      apps/remix-ide/src/remixAppManager.js
  15. 13
      libs/remix-api/src/lib/plugins/layout-api.ts
  16. 11
      libs/remix-api/src/lib/plugins/pinned-panel-api.ts
  17. 11
      libs/remix-api/src/lib/plugins/sidePanel-api.ts
  18. 6
      libs/remix-api/src/lib/remix-api.ts
  19. 1
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  20. 69
      libs/remix-ui/git/src/components/branchHeader.tsx
  21. 3
      libs/remix-ui/git/src/components/github/repositoryselect.tsx
  22. 2
      libs/remix-ui/git/src/components/github/selectandclonerepositories.tsx
  23. 2
      libs/remix-ui/git/src/components/gitui.tsx
  24. 2
      libs/remix-ui/git/src/components/panels/remotesimport.tsx
  25. 23
      libs/remix-ui/git/src/lib/listeners.ts
  26. 7
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.css
  27. 13
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-cell.tsx
  28. 39
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-section.tsx
  29. 3
      libs/remix-ui/grid-view/src/lib/remix-ui-grid-view.tsx
  30. 4
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  31. 55
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  32. 17
      libs/remix-ui/statusbar/src/contexts/statusbarcontext.tsx
  33. 13
      libs/remix-ui/statusbar/src/css/statusbar.css
  34. 9
      libs/remix-ui/statusbar/src/lib/components/aiStatus.tsx
  35. 35
      libs/remix-ui/statusbar/src/lib/components/didYouKnow.tsx
  36. 5
      libs/remix-ui/statusbar/src/lib/components/gitStatus.tsx
  37. 2
      libs/remix-ui/statusbar/src/lib/components/scamAlertStatus.tsx
  38. 6
      libs/remix-ui/statusbar/src/lib/components/scamDetails.tsx
  39. 42
      libs/remix-ui/statusbar/src/lib/remixui-statusbar-panel.tsx
  40. 12
      libs/remix-ui/statusbar/src/lib/types/index.ts
  41. 0
      libs/remix-ui/statusbar/src/utils/index.ts
  42. 2
      libs/remix-ui/workspace/src/lib/components/flat-tree-drop.tsx
  43. 38
      libs/remix-ui/workspace/src/lib/components/flat-tree.tsx
  44. 31
      libs/remix-ui/workspace/src/lib/utils/getEventTarget.ts
  45. 5
      libs/remix-url-resolver/src/resolve.ts

@ -12,7 +12,6 @@ function StepDetailPage() {
const location = useLocation()
const dispatch = useAppDispatch()
const [clonedStep, setClonedStep] = React.useState(null)
const [loading, setLoading] = React.useState(false)
const queryParams = new URLSearchParams(location.search)
const id = queryParams.get('id') as string
const stepId = Number(queryParams.get('stepId'))
@ -23,39 +22,24 @@ function StepDetailPage() {
const entity = detail[selectedId].entities[id]
const steps = entity.steps
const step = steps[stepId]
console.log(step)
useEffect(() => {
const clonedStep = JSON.parse(JSON.stringify(step))
setClonedStep(null)
const clonedStep = JSON.parse(JSON.stringify(step))
const loadFiles = async () => {
if (step.markdown && step.markdown.file && !step.markdown.content) {
console.log('loading md file', step.markdown.file)
clonedStep.markdown.content = (await remixClient.call('contentImport', 'resolve', step.markdown.file)).content
}
if (step.solidity && step.solidity.file && !step.solidity.content) {
console.log('loading sol file', step.solidity.file)
clonedStep.solidity.content = (await remixClient.call('contentImport', 'resolve', step.solidity.file)).content
}
if (step.test && step.test.file && !step.test.content) {
console.log('loading test file', step.test.file)
clonedStep.test.content = (await remixClient.call('contentImport', 'resolve', step.test.file)).content
}
if (step.answer && step.answer.file && !step.answer.content) {
console.log('loading answer file', step.answer.file)
clonedStep.answer.content = (await remixClient.call('contentImport', 'resolve', step.answer.file)).content
async function loadFile(step, fileType) {
if (step[fileType] && step[fileType].file && !step[fileType].content) {
clonedStep[fileType].content = (await remixClient.call('contentImport', 'resolve', step[fileType].file)).content;
}
}
if(step.js && step.js.file && !step.js.content) {
console.log('loading js file', step.js.file)
clonedStep.js.content = (await remixClient.call('contentImport', 'resolve', step.js.file)).content
}
if(step.vy && step.vy.file && !step.vy.content) {
console.log('loading vy file', step.vy.file)
clonedStep.vy.content = (await remixClient.call('contentImport', 'resolve', step.vy.file)).content
const fileTypes = ['markdown', 'solidity', 'test', 'answer', 'js', 'vy'];
for (const fileType of fileTypes) {
await loadFile(step, fileType);
}
}
loadFiles().then(() => {
console.log('displayFile', clonedStep)
setClonedStep(clonedStep)
dispatch({
type: 'remixide/displayFile',
@ -75,8 +59,16 @@ function StepDetailPage() {
}
}, [errors, success])
if(!clonedStep) {
return null
if (!clonedStep) {
return (<div className='pb-4'>
<div className="fixed-top">
<div className="bg-light">
<BackButton entity={entity} />
</div>
</div>
loading...
</div>
)
}
return (

@ -84,7 +84,6 @@ const Model: ModelType = {
const { detail, selectedId } = yield select((state) => state.workshop)
const workshop = detail[selectedId]
console.log('loading ', step, workshop)
path = `.learneth/${workshop.name}/${step.name}/${path}`
try {

@ -23,7 +23,7 @@ const Model: ModelType = {
},
effects: {
*init(_, { put }) {
const cache = null // localStorage.getItem('workshop.state')
const cache = null // don't use cache because remote might change
if (cache) {
const workshopState = JSON.parse(cache)
@ -90,7 +90,7 @@ const Model: ModelType = {
const key = stepKeysWithFile[k]
if (step[key]) {
try {
step[key].content = null// (yield remixClient.call('contentImport', 'resolve', step[key].file)).content
step[key].content = null // we load this later
} catch (error) {
console.error(error)
}

@ -60,6 +60,7 @@ module.exports = {
browser.
addFile('test.txt', { content: 'hello world' }, 'README.md')
.clickLaunchIcon('dgit')
.pause(3000)
.click('*[data-id="sourcecontrol-panel"]')
.waitForElementVisible({
selector: "//*[@data-status='new-untracked' and @data-file='/test.txt']",
@ -187,6 +188,7 @@ module.exports = {
'stage renamed file #group3': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(3000)
.waitForElementVisible({
selector: "//*[@data-status='deleted-unstaged' and @data-file='/test.txt']",
locateStrategy: 'xpath'
@ -228,6 +230,7 @@ module.exports = {
'create a branch #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(3000)
.click('*[data-id="branches-panel"]')
.waitForElementVisible('*[data-id="newbranchname"]')
.setValue('*[data-id="newbranchname"]', 'testbranch')
@ -244,6 +247,7 @@ module.exports = {
'publish the branch #group2': function (browser: NightwatchBrowser) {
browser
.clickLaunchIcon('dgit')
.pause(3000)
.waitForElementVisible('*[data-id="sourcecontrol-panel"]')
.click('*[data-id="sourcecontrol-panel"]')
.pause(1000)

@ -3,13 +3,13 @@
set -e
SHA=`git rev-parse --short --verify HEAD`
cd dist/apps/remix-ide
# this gh action is used to deploy the build to the gh pages
mkdir dist/apps/remix-ide/.github
mkdir dist/apps/remix-ide/.github/workflows
cp apps/remix-ide/ci/gh-actions-deploy.yml dist/apps/remix-ide/.github/workflows
cd dist/apps/remix-ide
git init
git checkout -b gh-pages
git config user.name "$COMMIT_AUTHOR"

@ -3,13 +3,13 @@
set -e
SHA=`git rev-parse --short --verify HEAD`
cd dist/apps/remix-ide
# this gh action is used to deploy the build to the gh pages
mkdir dist/apps/remix-ide/.github
mkdir dist/apps/remix-ide/.github/workflows
cp apps/remix-ide/ci/gh-actions-deploy.yml dist/apps/remix-ide/.github/workflows
cd dist/apps/remix-ide
git init
git checkout -b gh-pages
git config user.name "$COMMIT_AUTHOR"

@ -3,13 +3,13 @@
set -e
SHA=`git rev-parse --short --verify HEAD`
cd dist/apps/remix-ide
# this gh action is used to deploy the build to the gh pages
mkdir dist/apps/remix-ide/.github
mkdir dist/apps/remix-ide/.github/workflows
cp apps/remix-ide/ci/gh-actions-deploy.yml dist/apps/remix-ide/.github/workflows/gh-actions-deploy.yml
cd dist/apps/remix-ide
git init
git checkout -b gh-pages
git config user.name "$COMMIT_AUTHOR"

@ -40,6 +40,7 @@ import {HardhatProvider} from './app/providers/hardhat-provider'
import {GanacheProvider} from './app/providers/ganache-provider'
import {FoundryProvider} from './app/providers/foundry-provider'
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'
@ -276,6 +277,8 @@ class AppComponent {
const ganacheProvider = new GanacheProvider(blockchain)
const foundryProvider = new FoundryProvider(blockchain)
const externalHttpProvider = new ExternalHttpProvider(blockchain)
const environmentExplorer = new EnvironmentExplorer()
// ----------------- convert offset to line/column service -----------
const offsetToLineColumnConverter = new OffsetToLineColumnConverter()
Registry.getInstance().put({
@ -350,6 +353,7 @@ class AppComponent {
ganacheProvider,
foundryProvider,
externalHttpProvider,
environmentExplorer,
this.walkthroughService,
search,
solidityumlgen,
@ -505,7 +509,7 @@ class AppComponent {
])
await this.appManager.activatePlugin(['settings'])
await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder', 'dgit'])
await this.appManager.activatePlugin(['walkthrough', 'storage', 'search', 'compileAndRun', 'recorder', 'dgitApi', 'dgit'])
await this.appManager.activatePlugin(['solidity-script', 'remix-templates'])
if (isElectron()){

@ -0,0 +1,190 @@
import React from 'react' // eslint-disable-line
import { ViewPlugin } from '@remixproject/engine-web'
import { PluginViewWrapper } from '@remix-ui/helper'
import { RemixUIGridView } from '@remix-ui/remix-ui-grid-view'
import { RemixUIGridSection } from '@remix-ui/remix-ui-grid-section'
import { RemixUIGridCell } from '@remix-ui/remix-ui-grid-cell'
import './style/environment-explorer.css'
import type { Provider } from '../../blockchain/blockchain'
import * as packageJson from '../../../../../package.json'
const _paq = (window._paq = window._paq || [])
const profile = {
name: 'environmentExplorer',
displayName: 'Environment Explorer',
icon: 'assets/img/EnvironmentExplorerLogo.webp',
description: 'Explore providers and customize web3 provider list',
location: 'mainPanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/run.html',
version: packageJson.version,
maintainedBy: 'Remix',
permission: true,
events: [],
methods: []
}
type ProvidersSection = `Injected` | 'Remix VMs' | 'Externals'
export class EnvironmentExplorer extends ViewPlugin {
providers: { [key in ProvidersSection]: Provider[] }
providersFlat: { [key: string]: Provider }
pinnedProviders: string[]
dispatch: React.Dispatch<any> = () => {}
constructor() {
super(profile)
this.providersFlat = {}
this.providers = {
'Injected': [],
'Remix VMs': [],
'Externals': []
}
}
async onActivation(): Promise<void> {
this.providersFlat = await this.call('blockchain', 'getAllProviders')
this.pinnedProviders = await this.call('blockchain', 'getPinnedProviders')
this.renderComponent()
}
addProvider (provider: Provider) {
if (provider.isInjected) {
this.providers['Injected'].push(provider)
} else if (provider.isVM) {
this.providers['Remix VMs'].push(provider)
} else {
this.providers['Externals'].push(provider)
}
}
setDispatch(dispatch: React.Dispatch<any>): void {
this.dispatch = dispatch
this.renderComponent()
}
render() {
return (
<div className="bg-dark" id="environmentExplorer">
<PluginViewWrapper plugin={this} />
</div>
)
}
renderComponent() {
this.dispatch({
...this
})
}
updateComponent(state: any) {
this.providers = {
'Injected': [],
'Remix VMs': [],
'Externals': []
}
console.log(this.providersFlat)
for (const [key, provider] of Object.entries(this.providersFlat)) {
this.addProvider(provider)
}
return (
<RemixUIGridView
plugin={this}
styleList={""}
logo={profile.icon}
enableFilter={true}
showUntagged={true}
showPin={true}
title={profile.description}
description="Choose how you would like to interact with a chain."
>
<RemixUIGridSection
plugin={this}
title='Injected'
hScrollable={true}
>
{this.providers['Injected'].map(provider => {
return <RemixUIGridCell
plugin={this}
title={provider.name}
classList='EECellStyle'
pinned={this.pinnedProviders.includes(provider.name)}
pinStateCallback={async (pinned: boolean) => {
if (pinned) {
this.emit('providerPinned', provider.name, provider)
return true
}
const providerName = await this.call('blockchain', 'getProvider')
if (providerName !== provider.name) {
this.emit('providerUnpinned', provider.name, provider)
return true
} else {
this.call('notification', 'toast', 'Cannot unpin the current selected provider')
return false
}
}}
>
<div>{provider.name}</div>
</RemixUIGridCell>
})}
</RemixUIGridSection>
<RemixUIGridSection
plugin={this}
title='Remix VMs'
hScrollable= {true}
>{this.providers['Remix VMs'].map(provider => {
return <RemixUIGridCell
plugin={this}
title={provider.name}
classList='EECellStyle'
pinned={this.pinnedProviders.includes(provider.name)}
pinStateCallback={async (pinned: boolean) => {
if (pinned) {
this.emit('providerPinned', provider.name, provider)
return true
}
const providerName = await this.call('blockchain', 'getProvider')
if (providerName !== provider.name) {
this.emit('providerUnpinned', provider.name, provider)
return true
} else {
this.call('notification', 'toast', 'Cannot unpin the current selected provider')
return false
}
}}
>
<div>{provider.name}</div>
</RemixUIGridCell>
})}</RemixUIGridSection>
<RemixUIGridSection
plugin={this}
title='Externals'
hScrollable= {true}
>{this.providers['Externals'].map(provider => {
return <RemixUIGridCell
plugin={this}
title={provider.name}
classList='EECellStyle'
pinned={this.pinnedProviders.includes(provider.name)}
pinStateCallback={async (pinned: boolean) => {
if (pinned) {
this.emit('providerPinned', provider.name, provider)
return true
}
const providerName = await this.call('blockchain', 'getProvider')
if (providerName !== provider.name) {
this.emit('providerUnpinned', provider.name, provider)
return true
} else {
this.call('notification', 'toast', 'Cannot unpin the current selected provider')
return false
}
}}
>
<div>{provider.name}</div>
</RemixUIGridCell>
})}</RemixUIGridSection>
</RemixUIGridView>
)
}
}

@ -0,0 +1,6 @@
.EECellStyle {
min-height: 4rem;
max-width: 10rem;
min-width: 9rem;
max-height: 5rem;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

@ -23,7 +23,7 @@ const profile = {
name: 'blockchain',
displayName: 'Blockchain',
description: 'Blockchain - Logic',
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'web3', 'getProvider', 'getCurrentNetworkStatus'],
methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'web3', 'getProvider', 'getCurrentNetworkStatus', 'getAllProviders', 'getPinnedProviders'],
version: packageJson.version
}
@ -44,6 +44,21 @@ export type Transaction = {
timestamp?: number
}
export type Provider = {
options: { [key: string]: string }
dataId: string
name: string
displayName: string
fork: string
isInjected: boolean
isVM: boolean
title: string
init: () => Promise<void>
provider:{
sendAsync: (payload: any) => Promise<void>
}
}
export class Blockchain extends Plugin {
active: boolean
event: EventManager
@ -62,6 +77,7 @@ export class Blockchain extends Plugin {
providers: {[key: string]: VMProvider | InjectedProvider | NodeProvider}
transactionContextAPI: TransactionContextAPI
registeredPluginEvents: string[]
pinnedProviders: string[]
// NOTE: the config object will need to be refactored out in remix-lib
constructor(config: Config) {
@ -93,6 +109,7 @@ export class Blockchain extends Plugin {
this.networkcallid = 0
this.networkStatus = { network: { name: ' - ', id: ' - ' } }
this.registeredPluginEvents = []
this.pinnedProviders = ['vm-cancun', 'vm-shanghai', 'vm-mainnet-fork', 'vm-london', 'vm-berlin', 'vm-paris', 'walletconnect', 'injected-MetaMask', 'basic-http-provider', 'ganache-provider', 'hardhat-provider', 'foundry-provider']
this.setupEvents()
this.setupProviders()
}
@ -116,6 +133,14 @@ export class Blockchain extends Plugin {
})
}
})
this.on('environmentExplorer', 'providerPinned', (name, provider) => {
this.emit('shouldAddProvidertoUdapp', name, provider)
})
this.on('environmentExplorer', 'providerUnpinned', (name, provider) => {
this.emit('shouldRemoveProviderFromUdapp', name, provider)
})
}
onDeactivation() {
@ -136,12 +161,12 @@ export class Blockchain extends Plugin {
})
})
this.executionContext.event.register('addProvider', (network) => {
this._triggerEvent('addProvider', [network])
this.executionContext.event.register('providerAdded', (network) => {
this._triggerEvent('providerAdded', [network])
})
this.executionContext.event.register('removeProvider', (name) => {
this._triggerEvent('removeProvider', [name])
this.executionContext.event.register('providerRemoved', (name) => {
this._triggerEvent('providerRemoved', [name])
})
setInterval(() => {
@ -504,7 +529,11 @@ export class Blockchain extends Plugin {
}
changeExecutionContext(context, confirmCb, infoCb, cb) {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
if (context.context === 'item-another-chain') {
this.call('manager', 'activatePlugin', 'environmentExplorer').then(() => this.call('tabs', 'focus', 'environmentExplorer'))
} else {
return this.executionContext.executionContextChange(context, null, confirmCb, infoCb, cb)
}
}
detectNetwork(cb) {
@ -611,7 +640,8 @@ export class Blockchain extends Plugin {
this.executionContext.listenOnLastBlock()
}
addProvider(provider) {
addProvider(provider: Provider) {
if (this.pinnedProviders.includes(provider.name)) this.emit('shouldAddProvidertoUdapp', provider.name, provider)
this.executionContext.addProvider(provider)
}
@ -619,6 +649,14 @@ export class Blockchain extends Plugin {
this.executionContext.removeProvider(name)
}
getAllProviders() {
return this.executionContext.getAllProviders()
}
getPinnedProviders() {
return this.pinnedProviders
}
// TODO : event should be triggered by Udapp instead of TxListener
/** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
startListening(txlistener) {

@ -123,10 +123,13 @@ export class ExecutionContext {
addProvider (network) {
if (network && network.name && !this.customNetWorks[network.name]) {
this.customNetWorks[network.name] = network
this.event.trigger('addProvider', [network])
}
}
getAllProviders () {
return this.customNetWorks
}
internalWeb3 () {
return web3
}

@ -82,11 +82,10 @@ let requiredModules = [ // services + layout views + system views
'pinnedPanel',
'pluginStateLogger',
'remixGuide',
'matomo'
'matomo',
'walletconnect'
]
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']
@ -133,7 +132,7 @@ export function isNative(name) {
'circuit-compiler',
'compilationDetails',
'vyperCompilationDetails',
'remixGuide',
//'remixGuide',
'walletconnect'
]
return nativePlugins.includes(name) || requiredModules.includes(name) || isInjectedProvider(name) || isVM(name)
@ -389,7 +388,16 @@ class PluginLoader {
constructor() {
const queryParams = new QueryParams()
this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load.
// some plugins should not be activated at page load.
this.donotAutoReload = [
'remixd',
'environmentExplorer',
'templateSelection',
'compilationDetails',
'walletconnect',
'dapp-draft',
'solidityumlgen'
]
this.loaders = {}
this.loaders.localStorage = {
set: (plugin, actives) => {

@ -0,0 +1,13 @@
import { IFilePanel } from '@remixproject/plugin-api'
import { StatusEvents } from '@remixproject/plugin-utils'
export interface ILayoutApi {
events:{
} & StatusEvents
methods: {
maximisePinnedPanel: () => void
maximiseSidePanel: () => void
resetPinnedPanel: () => void
resetSidePanel: () => void
}
}

@ -0,0 +1,11 @@
import { IFilePanel } from '@remixproject/plugin-api'
import { StatusEvents } from '@remixproject/plugin-utils'
export interface IPinnedPanelApi {
events:{
} & StatusEvents
methods: {
currentFocus(): Promise<string>
}
}

@ -0,0 +1,11 @@
import { IFilePanel } from '@remixproject/plugin-api'
import { StatusEvents } from '@remixproject/plugin-utils'
export interface ISidePanelApi {
events:{
focusChanged: (name: string) => void;
} & StatusEvents
methods: {
}
}

@ -8,6 +8,9 @@ import { INotificationApi } from "./plugins/notification-api"
import { ISettings } from "./plugins/settings-api"
import { IFilePanelApi } from "./plugins/filePanel-api"
import { Plugin } from "@remixproject/engine"
import { ISidePanelApi } from "./plugins/sidePanel-api"
import { IPinnedPanelApi } from "./plugins/pinned-panel-api"
import { ILayoutApi } from "./plugins/layout-api"
export interface ICustomRemixApi extends IRemixApi {
dgitApi: IGitApi
@ -17,6 +20,9 @@ export interface ICustomRemixApi extends IRemixApi {
fileDecorator: IFileDecoratorApi
fileManager: IExtendedFileSystem
filePanel: IFilePanelApi
sidePanel: ISidePanelApi
pinnedPanel: IPinnedPanelApi
layout: ILayoutApi
}
export declare type CustomRemixApi = Readonly<ICustomRemixApi>

@ -699,6 +699,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
props.plugin.call('notification', 'alert', modalContent)
pasteCodeRef.current = true
_paq.push(['trackEvent', 'editor', 'onDidPaste', 'more_than_10_lines'])
}
})

@ -46,9 +46,9 @@ export const BranchHeader = () => {
const getName = () => {
const url = context.currentBranch?.remote?.url
if (!url) return
const regex = /https:\/\/github\.com\/[^/]+\/([^/]+)\.git/
const regex = /https:\/\/github\.com\/[^/]+\/([^/]+)(?:\.git)?/;
const match = url.match(regex)
return match ? match[1] : 'Couldn\'t get repo name!'
return match ? match[1] : null
}
const showDetachedWarningText = async () => {
@ -60,52 +60,37 @@ export const BranchHeader = () => {
const Heading = () => {
return (
<div className="container-fluid px-3">
<div className="container-fluid px-0">
<div className="d-flex flex-column pt-1 mb-1">
<div className="d-flex flex-column justify-content-start align-items-start">
{getName() !== "Couldn't get repo name!" ? (
<div className="pr-1 m-0">
<span className="col-4 px-0">Repository Name:</span>
<span className="" style={{ width: '15rem' }}>
<span className={`${ changed ? 'text-danger pl-2 text-truncate overflow-hidden whitespace-nowrap ml-4' : "text-secondary pl-2 text-truncate overflow-hidden whitespace-nowrap ml-4" }`}>
{getName() ?? ''}
</span>
</span>
</div>
<div className="d-flex flex-column justify-content-start align-items-start w-100">
{getName() ? (
<span className={`text-truncate overflow-hidden whitespace-nowrap w-100`}>
{getName() ?? ''}
</span>
) : null
}
<div className="pr-1 m-0">
<span className="col-4 px-0">Branch Name:</span>
<span className="pl-2 text-secondary text-truncate overflow-hidden whitespace-nowrap ml-4">
<span className={`${changed ? 'text-danger pl-2' : "pl-2"}`}>
<i className="fa fa-code-branch mr-1 pl-2"></i>
{context.currentBranch && context.currentBranch.name}
</span>
{context.currentBranch && context.currentBranch.name ?
<span className="text-secondary text-truncate overflow-hidden whitespace-nowrap w-100">
<i className="fa fa-code-branch mr-1"></i>{context.currentBranch && context.currentBranch.name}{changed?'*':''}
</span> : null}
{(latestCommit && latestCommit.commit && latestCommit.commit.message) ?
<span className="text-secondary text-truncate overflow-hidden whitespace-nowrap w-100">
{latestCommit ?
latestCommit.commit && latestCommit.commit.message ? `"${latestCommit.commit.message}"` : '' : null}
</span>
: null}
{isDetached ?
<span className="text-secondary text-truncate overflow-hidden whitespace-nowrap w-100">
{isDetached ?
<>You are in a detached state<i onClick={showDetachedWarningText} className="btn fa fa-info-circle mr-1"></i></> : null}
</span>
</div>
: null}
{context.storage.enabled ?
<div className="d-flex">
<span className="d-flex justify-between align-items-center" style={{ width: '15rem' }}>
<span className="col-4 px-0">Storage :</span>
<span className="text-secondary text-sm text-truncate overflow-hidden whitespace-nowrap ml-4">
{context.storage.used} MB used
({context.storage.percentUsed} %)
</span>
</span>
</div> : null}
<div className="d-flex flex-row">
<span className="d-flex justify-between align-items-center" style={{ width: '15rem' }}>
<span className="col-4 px-0">Messages :</span>
<span className="text-truncate overflow-hidden" >
<span className="text-secondary text-truncate overflow-hidden whitespace-nowrap ml-4">
{latestCommit ?
latestCommit.commit && latestCommit.commit.message ? latestCommit.commit.message : '' : null}
{isDetached ?
<>You are in a detached state<i onClick={showDetachedWarningText} className="btn fa fa-info-circle mr-1 pl-2"></i></>: null}
</span>
</span>
<span className="text-secondary text-sm text-truncate overflow-hidden whitespace-nowrap w-100">
{context.storage.used} MB used
({context.storage.percentUsed} %)
</span>
</div>
: null}
</div>
</div>
</div>

@ -9,6 +9,7 @@ import { TokenWarning } from '../panels/tokenWarning';
interface RepositorySelectProps {
select: (repo: repository) => void;
title: string;
}
const RepositorySelect = (props: RepositorySelectProps) => {
@ -65,7 +66,7 @@ const RepositorySelect = (props: RepositorySelectProps) => {
return (
<><button data-id='fetch-repositories' onClick={fetchRepositories} className="w-100 mt-1 btn btn-secondary mb-2">
<i className="fab fa-github mr-1"></i>Fetch Repositories from GitHub
<i className="fab fa-github mr-1"></i>{props.title}
</button>
{
show ?

@ -43,7 +43,7 @@ export const SelectAndCloneRepositories = (props: RepositoriesProps) => {
return (
<>
<RepositorySelect select={selectRepo} />
<RepositorySelect title="Clone from GitHub" select={selectRepo} />
<TokenWarning />
{ repo && <BranchSelect select={selectRemoteBranch} /> }

@ -164,7 +164,7 @@ export const GitUI = (props: IGitUi) => {
return (
<>{(!gitState.canUseApp) ? <Disabled></Disabled> :
<div className="m-1">
<div className="px-3">
<gitPluginContext.Provider value={gitState}>
<loaderContext.Provider value={loaderState}>
<gitActionsContext.Provider value={gitActionsProviderValue}>

@ -64,7 +64,7 @@ export const RemotesImport = () => {
return (
<>
<RepositorySelect select={selectRepo} />
<RepositorySelect title="Add from GitHub" select={selectRepo} />
<TokenWarning />
{repo ?
<input data-id='remote-panel-remotename' placeholder="remote name" name='remotename' onChange={e => onRemoteNameChange(e.target.value)} value={remoteName} className="form-control mb-2" type="text" id="remotename" />

@ -164,6 +164,23 @@ export const setCallBacks = (viewPlugin: Plugin, gitDispatcher: React.Dispatch<g
setAtivePanel(panelNumber)
})
plugin.on('sidePanel', 'focusChanged', async (name: string) => {
const pinnedPlugin = await plugin.call('pinnedPanel', 'currentFocus')
if (name == 'dgit') {
if (pinnedPlugin === 'dgit') {
plugin.call('layout', 'maximisePinnedPanel')
} else {
plugin.call('layout', 'maximiseSidePanel')
}
} else {
if (pinnedPlugin === 'dgit') {
plugin.call('layout', 'resetPinnedPanel')
} else {
plugin.call('layout', 'resetSidePanel')
}
}
})
callBackEnabled = true;
}
@ -223,12 +240,6 @@ const calculateLocalStorage = async () => {
const quotaMB = bytesToMB(estimate.quota);
const availableMB = bytesToMB(estimate.quota - estimate.usage);
const percentageUsed = calculatePercentage(estimate.usage, estimate.quota);
console.log(`Used storage: ${usedMB} MB`);
console.log(`Total quota: ${quotaMB} MB`);
console.log(`Available storage: ${availableMB} MB`);
console.log(`Percentage used: ${percentageUsed}%`);
storage = {
used: usedMB,
total: quotaMB,

@ -23,12 +23,13 @@
.remixui_grid_cell_pin:focus {
outline: none;
}
.remixui_grid_cell_pin {
width: 1rem;
width: 1rem;
height: 1rem;
position: relative;
right: 1rem;
top: -0.5rem;
top: -0.8rem;
background: transparent;
}
@ -41,7 +42,7 @@
.remixui_grid_cell_tags_no_pin {
position: relative;
right: 0rem;
top: 0.1rem;
top: -6.5rem;
}
.remixui_grid_cell_tag {

@ -34,8 +34,8 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
useEffect(() => {
if (props.tagList) setAnyEnabled(props.tagList.some((key) => filterCon.keyValueMap[key]?.enabled))
else setAnyEnabled(filterCon?.keyValueMap['no tag']?.enabled)
if (!props.tagList || props.tagList.length == 0) setAnyEnabled(true)
if (filterCon.filter != '') setAnyEnabled(anyEnabled && props.title.toLowerCase().includes(filterCon.filter))
console.log("pin ", pinned)
}, [filterCon, props.tagList])
@ -54,12 +54,12 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
*/
return (
<div className='mr-2 mt-3' onClick={() => {
<div data-values='gridCell' className='' onClick={() => {
if (props.expandViewEl)
props.handleExpand(!expand)
else return
}}>
{ anyEnabled && <div className='d-flex flex-column'>
{ anyEnabled && <div className='mr-2 mt-3 d-flex flex-column'>
<div className='d-flex flex-grid'>
<div className={"d-flex mx-0 p-2 bg-light border border-secondary remixui_grid_cell_container " + props.classList || ''} data-id={"remixUIGS" + props.title}>
<div className="d-flex remixui_grid_cell flex-column">
@ -77,9 +77,10 @@ export const RemixUIGridCell = (props: RemixUIGridCellProps) => {
</div>
{ filterCon.showPin && <button
className={`${pinned ? 'fa-duotone' : 'fa-light'}` + ` fa-map-pin text-info border-0 mb-0 remixui_grid_cell_pin`}
onClick={() => {
setPinned(!pinned)
props.pinStateCallback()
style={{ fontSize: 'large' }}
onClick={async () => {
if (!props.pinStateCallback) setPinned(!pinned)
if (await props.pinStateCallback(!pinned)) setPinned(!pinned)
}}
></button>}
{ props.tagList && <div className={`d-flex flex-column align-items-begin ` +`${filterCon.showPin ? 'remixui_grid_cell_tags' : 'remixui_grid_cell_tags_no_pin'}`}>

@ -1,6 +1,7 @@
import React, {useState, useEffect, useContext, useRef, ReactNode} from 'react' // eslint-disable-line
import './remix-ui-grid-section.css'
import FiltersContext from "./filtersContext"
declare global {
interface Window {
@ -19,7 +20,44 @@ interface RemixUIGridSectionProps {
expandedCell?: any
}
const hasChildCell = (children: ReactNode): boolean => {
return true
let found = false
const isElement = (child: ReactNode): child is React.ReactElement => {
return React.isValidElement(child)
}
const traverse = (child: ReactNode) => {
console.log('found ', children)
if (found) return
if (isElement(child)) {
if (child.props.classList === 'EECellStyle') {
found = true
console.log('found ', child.props.className)
return
}
if (child.props.children) {
React.Children.forEach(child.props.children, traverse)
}
}
}
React.Children.forEach(children, traverse)
return found
}
export const RemixUIGridSection = (props: RemixUIGridSectionProps) => {
const [children, setChildren] = useState(props.children)
const filterCon = useContext(FiltersContext)
useEffect(() => {
setChildren(props.children)
}, [props.children])
return (
<div
className={`d-flex px-4 py-2 flex-column w-100 remixui_grid_section_container ${props.classList}`}
@ -29,6 +67,7 @@ export const RemixUIGridSection = (props: RemixUIGridSectionProps) => {
<div className="d-flex flex-column w-100 remixui_grid_section">
{ props.title && <h6 className='mt-1 mb-0 align-items-left '>{ props.title }</h6> }
<div className={(props.hScrollable) ? `d-flex flex-row pb-2 overflow-auto` : `d-flex flex-wrap`}>
{ !hasChildCell(children) && <span> No items found </span>}
{ props.children }
</div>
{ props.expandedCell && <div>

@ -51,7 +51,6 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
searchInputRef.current.value = ''
} else {
setState((prevState) => {
console.log("update filter", searchInputRef.current.value)
return {
...prevState,
searchDisable: searchInputRef.current.value === '',
@ -120,7 +119,7 @@ export const RemixUIGridView = (props: RemixUIGridViewProps) => {
ref={searchInputRef}
type="text"
style={{ minWidth: '100px' }}
className="border form-control border-right-0 mr-4"
className="border form-control mr-4"
id="GVFilterInput"
placeholder={"Filter the list"}
data-id="RemixGVFilterInput"

@ -69,9 +69,9 @@ export const setupEvents = (plugin: RunTab) => {
}
})
plugin.blockchain.event.register('addProvider', provider => addExternalProvider(dispatch, provider))
plugin.on('blockchain', 'shouldAddProvidertoUdapp', (name, provider) => addExternalProvider(dispatch, provider))
plugin.blockchain.event.register('removeProvider', name => removeExternalProvider(dispatch, name))
plugin.on('blockchain', 'shouldRemoveProviderFromUdapp', (name, provider) => removeExternalProvider(dispatch, name))
plugin.blockchain.events.on('newProxyDeployment', (address, date, contractName) => addNewProxyDeployment(dispatch, address, date, contractName))

@ -6,6 +6,11 @@ import { Dropdown } from 'react-bootstrap'
import { CustomMenu, CustomToggle, CustomTooltip } from '@remix-ui/helper'
export function EnvironmentUI(props: EnvironmentProps) {
Object.entries(props.providers.providerList.filter((provider) => { return provider.isVM }))
Object.entries(props.providers.providerList.filter((provider) => { return provider.isInjected }))
Object.entries(props.providers.providerList.filter((provider) => { return !(provider.isVM || provider.isInjected) }))
const handleChangeExEnv = (env: string) => {
const provider = props.providers.providerList.find((exEnv) => exEnv.name === env)
const context = provider.name
@ -23,7 +28,6 @@ export function EnvironmentUI(props: EnvironmentProps) {
<div className="udapp_crow">
<label id="selectExEnv" className="udapp_settingsLabel">
<FormattedMessage id="udapp.environment" />
<CustomTooltip placement={'auto-end'} tooltipClasses="text-nowrap" tooltipId="info-recorder" tooltipText={<FormattedMessage id="udapp.tooltipText2" />}>
<a href="https://chainlist.org/" target="_blank">
<i className={'ml-2 fas fa-plug'} aria-hidden="true"></i>
@ -54,9 +58,42 @@ export function EnvironmentUI(props: EnvironmentProps) {
)}
</Dropdown.Toggle>
<Dropdown.Menu as={CustomMenu} className="w-100 custom-dropdown-items" data-id="custom-dropdown-items">
{props.providers.providerList.map(({ displayName, name }, index) => (
{props.providers.providerList.length === 0 && <Dropdown.Item>
<span className="">
No provider pinned
</span>
</Dropdown.Item>}
{ (props.providers.providerList.filter((provider) => { return provider.isInjected })).map(({ name, displayName }) => (
<Dropdown.Item
key={name}
onClick={() => {
handleChangeExEnv(name)
}}
data-id={`dropdown-item-${name}`}
>
<span className="">
{displayName}
</span>
</Dropdown.Item>
))}
{ props.providers.providerList.filter((provider) => { return provider.isInjected }).length !== 0 && <Dropdown.Divider className='border-secondary'></Dropdown.Divider> }
{ (props.providers.providerList.filter((provider) => { return provider.isVM })).map(({ displayName, name }) => (
<Dropdown.Item
key={name}
onClick={() => {
handleChangeExEnv(name)
}}
data-id={`dropdown-item-${name}`}
>
<span className="">
{displayName}
</span>
</Dropdown.Item>
))}
{ props.providers.providerList.filter((provider) => { return provider.isVM }).length !== 0 && <Dropdown.Divider className='border-secondary'></Dropdown.Divider> }
{ (props.providers.providerList.filter((provider) => { return !(provider.isVM || provider.isInjected) })).map(({ displayName, name }) => (
<Dropdown.Item
key={index}
key={name}
onClick={() => {
handleChangeExEnv(name)
}}
@ -68,6 +105,18 @@ export function EnvironmentUI(props: EnvironmentProps) {
</span>
</Dropdown.Item>
))}
<Dropdown.Divider className='border-secondary'></Dropdown.Divider>
<Dropdown.Item
key={10000}
onClick={() => {
props.setExecutionContext({ context: 'item-another-chain' })
}}
data-id={`dropdown-item-another-chain`}
>
<span className="">
Pin another chain...
</span>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</div>

@ -0,0 +1,17 @@
import React, { createContext } from 'react'
import { defaultStatusBarContext, StatusBarContextType, StatusBarInterface } from '../lib/types'
export const StatusBarContext = createContext<StatusBarContextType>(defaultStatusBarContext)
export function StatusBarContextProvider ({ children }) {
const statusBarProviderValues = {
test: true
}
return (
<>
<StatusBarContext.Provider value={statusBarProviderValues}>
{children}
</StatusBarContext.Provider>
</>
)
}

@ -14,6 +14,11 @@
cursor: pointer;
}
.remixui_statusbar_aistatusdisabled {
text-decoration: line-through;
text-decoration-thickness: 3px;
}
/**
* approximately same height with vscode statusbar
**/
@ -27,3 +32,11 @@
.remixui_statusbar_activelink:active {
color: var(--danger);
}
.remixui_statusbar_didyouknow {
}
.remixui_statusbar_custom_padding {
padding: 0.421em;
}

@ -33,11 +33,12 @@ export default function AIStatus(props: AIStatusProps) {
}, [props.plugin.isAiActive])
return (
<CustomTooltip
tooltipText={copilotActive ? "Remix Copilot activated" : "Remix Copilot disabled."}
tooltipText={copilotActive ? "Remix Copilot activated" : "Remix Copilot disabled. To activate copilot, open a .sol file and toggle the ai switch at the top of the Ide"}
>
<div className="d-flex flex-row pr-2 text-white justify-content-center align-items-center remixui_statusbar_aistatus">
<span className={copilotActive === false ? "fa-regular fa-microchip-ai ml-1 text-white" : "fa-regular fa-microchip-ai ml-1"}></span>
<span className={copilotActive === false ? "small mx-1 text-white semi-bold" : "small mx-1 semi-bold" }>Remix Copilot</span>
<div className="d-flex flex-row pr-2 text-white justify-content-center align-items-center">
<span className={copilotActive === false ? "small mx-1 text-white semi-bold" : "small mx-1 text-white semi-bold" }>
{copilotActive === false ? 'Remix Copilot (disabled)' : 'Remix Copilot (enabled)'}
</span>
</div>
</CustomTooltip>
)

@ -0,0 +1,35 @@
import { CustomTooltip } from '@remix-ui/helper'
import axios from 'axios'
import React, { useEffect, useState } from 'react'
export default function DidYouKnow () {
const [tip, setTip] = useState<string>('')
useEffect(() => {
const abortController = new AbortController()
const signal = abortController.signal
async function showRemixTips() {
const response = await axios.get('https://raw.githubusercontent.com/remix-project-org/remix-dynamics/main/ide/tips.json', { signal })
if (signal.aborted) return
const tips = response.data
const index = Math.floor(Math.random() * (tips.length - 1))
setTip(tips[index])
}
try {
showRemixTips()
} catch (e) {
console.log(e)
}
return () => {
abortController.abort()
}
}, [])
return (
<CustomTooltip tooltipText={'Did you know'}>
<div className="remixui_statusbar_didyouknow text-white small d-flex align-items-center">
<span className="pr-2 text-success fa-solid fa-lightbulb"></span>
<div className="mr-2">Did you know?</div>
{ tip && tip.length > 0 ? <div>{tip}</div> : null }
</div>
</CustomTooltip>
)
}

@ -52,10 +52,11 @@ export default function GitStatus({ plugin, gitBranchName, setGitBranchName }: G
}
const initializeNewGitRepo = async () => {
await plugin.call('dgitApi', 'init')
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (isLocalHost === false) {
const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')
await plugin.call('dgitApi', 'init')
;(window as any)._paq.push('trackEvent', 'statusbar', 'initNewRepo')
}
}

@ -15,7 +15,7 @@ export default function ScamAlertStatus ({ refs, getReferenceProps }: ScamAlertS
<CustomTooltip
tooltipText={"Scam Alerts"}
>
<div className="mr-2 d-flex align-items-center justify-content-center remixui_statusbar_scamAlert" id="hTScamAlertSection" ref={refs.setReference} {...getReferenceProps()}>
<div className="mr-1 d-flex align-items-center justify-content-center remixui_statusbar_scamAlert" id="hTScamAlertSection" ref={refs.setReference} {...getReferenceProps()}>
<span className="pr-2 far fa-exclamation-triangle text-white"></span>
<span className="text-white font-semibold small">
<FormattedMessage id="home.scamAlert" />

@ -19,12 +19,12 @@ export default function ScamDetails ({ refs, floatStyle, scamAlerts }: ScamDetai
<div
ref={refs.setFloating}
style={ floatStyle }
className="px-1 ml-1 mb-1 d-flex w-25 alert alert-danger border border-danger"
className="px-1 ml-1 mb-1 d-flex w-25 alert alert-warning border border-warning"
>
<span className="align-self-center pl-4 mt-1">
<i style={{ fontSize: 'xxx-large', fontWeight: 'lighter' }} className="pr-2 far text-danger fa-exclamation-triangle"></i>
<i style={{ fontSize: 'xxx-large', fontWeight: 'lighter' }} className="pr-2 far text-warning fa-exclamation-triangle"></i>
</span>
<div className="d-flex flex-column text-danger">
<div className="d-flex flex-column text-warning">
{scamAlerts && scamAlerts.map((alert, index) => (
<span className="pl-4 mt-1" key={`${alert.url}${index}`}>
{alert.url.length < 1 ? <FormattedMessage id={`home.scamAlertText${index + 1}`} defaultMessage={alert.message} />

@ -7,6 +7,8 @@ import { FloatingFocusManager, autoUpdate, flip, offset, shift, size, useClick,
import axios from 'axios'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { StatusBar } from 'apps/remix-ide/src/app/components/status-bar'
import { StatusBarContextProvider } from '../contexts/statusbarcontext'
import DidYouKnow from './components/didYouKnow'
export interface RemixUIStatusBarProps {
statusBarPlugin: StatusBar
@ -18,7 +20,7 @@ export type ScamAlert = {
}
export function RemixUIStatusBar({ statusBarPlugin }: RemixUIStatusBarProps) {
const [showScamDetails, setShowScamDetails] = useState(false)
const [showScamDetails, setShowScamDetails] = useState(true)
const [scamAlerts, setScamAlerts] = useState<ScamAlert[]>([])
const [gitBranchName, setGitBranchName] = useState('')
const [isAiActive, setIsAiActive] = useState(false)
@ -66,21 +68,31 @@ export function RemixUIStatusBar({ statusBarPlugin }: RemixUIStatusBarProps) {
return (
<>
{showScamDetails && (
<FloatingFocusManager context={context} modal={false}>
<ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} />
</FloatingFocusManager>
)}
<div className="d-flex remixui_statusbar_height flex-row bg-info justify-content-between align-items-center">
<div className="remixui_statusbar remixui_statusbar_gitstatus">
<GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} />
<StatusBarContextProvider>
{showScamDetails && (
<FloatingFocusManager context={context} modal={false}>
<ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} />
</FloatingFocusManager>
)}
<div className="d-flex remixui_statusbar_height flex-row bg-info justify-content-between align-items-center">
<div className="remixui_statusbar remixui_statusbar_gitstatus">
<GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} />
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar">
<DidYouKnow />
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar d-flex align-items-center p-0">
<div className="remixui_statusbar">
<AIStatus plugin={statusBarPlugin} aiActive={lightAiUp} isAiActive={isAiActive} setIsAiActive={setIsAiActive} />
</div>
<div className="remixui_statusbar bg-warning remixui_statusbar_custom_padding d-flex justify-center align-items-center">
<ScamAlertStatus refs={refs} getReferenceProps={getReferenceProps} />
</div>
</div>
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar d-flex flex-row">
<ScamAlertStatus refs={refs} getReferenceProps={getReferenceProps} />
<AIStatus plugin={statusBarPlugin} aiActive={lightAiUp} isAiActive={isAiActive} setIsAiActive={setIsAiActive} />
</div>
</div>
</StatusBarContextProvider>
</>
)
}

@ -3,6 +3,7 @@ import { Plugin } from '@remixproject/engine'
import { FilePanelType } from '@remix-ui/workspace'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { VerticalIcons } from 'apps/remix-ide/src/app/components/vertical-icons'
import { CustomRemixApi } from '@remix-api'
export interface PluginProfile {
name: string
displayName: string
@ -15,7 +16,7 @@ export interface PluginProfile {
version?: string
}
export interface StatusBarInterface extends Plugin {
export interface StatusBarInterface extends Plugin<any, CustomRemixApi> {
htmlElement: HTMLDivElement
events: EventEmitter
dispatch: React.Dispatch<any>
@ -25,3 +26,12 @@ export interface StatusBarInterface extends Plugin {
getGitBranchName: () => Promise<any>
currentWorkspaceName: string
}
export const defaultStatusBarContext: StatusBarContextType = {
test: false
}
export type StatusBarContextType = {
test: boolean
}

@ -1,6 +1,6 @@
import React, { SyntheticEvent, useContext, useEffect, useRef, useState } from 'react'
import { DragStructure, FileType, FlatTreeDropProps } from '../types'
import { buildMultiSelectedItemProfiles, getEventTarget } from '../utils/getEventTarget'
import { getEventTarget } from '../utils/getEventTarget'
import { extractParentFromKey } from '@remix-ui/helper'
import { FileSystemContext } from '../contexts'

@ -5,7 +5,7 @@ import { getPathIcon } from '@remix-ui/helper';
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso'
import { FlatTreeItemInput } from './flat-tree-item-input';
import { FlatTreeDrop } from './flat-tree-drop';
import { buildMultiSelectedItemProfiles, getEventTarget } from '../utils/getEventTarget';
import { getEventTarget } from '../utils/getEventTarget';
import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators';
import { FileHoverIcons } from './file-explorer-hovericons';
import { deletePath } from '../actions';
@ -75,6 +75,37 @@ export const FlatTree = (props: FlatTreeProps) => {
const virtuoso = useRef<VirtuosoHandle>(null)
const [selectedItems, setSelectedItems] = useState<DragStructure[]>([])
/**
* When multiple files are selected in FileExplorer,
* and these files are dragged to a target folder,
* this function will build the profile of each selected item
* so they can be moved when dropped on target folder
* @param target - Initial target item in FileExplorer
* @returns - {DragStructure} Array of selected items
*/
const buildMultiSelectedItemProfiles = (target: {
path: string
type: string
content: string
position: {
top: number
left: number
}
}) => {
const selectItems = []
selectItems.push(target)
containerRef.current?.querySelectorAll('li.remixui_selected').forEach(item => {
const dragTarget = {
position: { top: target?.position.top || 0, left: target?.position.left || 0 },
path: item.getAttribute('data-path') || item.getAttribute('data-label-path') || '',
type: item.getAttribute('data-type') || item.getAttribute('data-label-type') || '',
content: item.textContent || ''
}
if (dragTarget.path !== target.path) selectItems.push(dragTarget)
})
setSelectedItems(selectItems)
}
const labelClass = (file: FileType) =>
props.focusEdit.element === file.path
? 'bg-light'
@ -116,9 +147,8 @@ export const FlatTree = (props: FlatTreeProps) => {
const target = await getEventTarget(event)
setDragSource(flatTree.find((item) => item.path === target.path))
setIsDragging(true)
const items = buildMultiSelectedItemProfiles(target)
setSelectedItems(items)
setFilesSelected(items.map((item) => item.path))
buildMultiSelectedItemProfiles(target)
setFilesSelected(selectedItems.map((item) => item.path))
}
useEffect(() => {

@ -23,34 +23,3 @@ export const getEventTarget = async (e: any, useLabel: boolean = false) => {
}
}
}
/**
* When multiple files are selected in FileExplorer,
* and these files are dragged to a target folder,
* this function will build the profile of each selected item
* in FileExplorer so they can be moved when dropped
* @param target - Initial target item in FileExplorer
* @returns - {DragStructure} Array of selected items
*/
export const buildMultiSelectedItemProfiles = (target: {
path: string
type: string
content: string
position: {
top: number
left: number
}
}) => {
const selectItems = []
selectItems.push(target)
document.querySelectorAll('li.remixui_selected').forEach(item => {
const dragTarget = {
position: { top: target?.position.top || 0, left: target?.position.left || 0 },
path: item.getAttribute('data-path') || item.getAttribute('data-label-path') || '',
type: item.getAttribute('data-type') || item.getAttribute('data-label-type') || '',
content: item.textContent || ''
}
if (dragTarget.path !== target.path) selectItems.push(dragTarget)
})
return selectItems
}

@ -139,6 +139,7 @@ export class RemixURLResolver {
async handleNpmImport(url: string): Promise<HandlerResponse> {
if (!url) throw new Error('url is empty')
let fetchUrl = url
const isVersionned = semverRegex().exec(url.replace(/@/g, '@ ').replace(/\//g, ' /'))
if (this.getDependencies && !isVersionned) {
try {
@ -174,7 +175,7 @@ export class RemixURLResolver {
}
if (version) {
const versionSemver = semver.minVersion(version)
url = url.replace(pkg, `${pkg}@${versionSemver.version}`)
fetchUrl = url.replace(pkg, `${pkg}@${versionSemver.version}`)
}
}
}
@ -189,7 +190,7 @@ export class RemixURLResolver {
// get response from all urls
for (let i = 0; i < npm_urls.length; i++) {
try {
const req = npm_urls[i] + url
const req = npm_urls[i] + fetchUrl
const response: AxiosResponse = await axios.get(req, { transformResponse: []})
content = response.data
break

Loading…
Cancel
Save