Merge branch 'master' into master

pull/5332/head
Tomer 3 weeks ago committed by GitHub
commit 1579aca1d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 25
      apps/remix-ide-e2e/src/commands/hidePopupPanel.ts
  2. 5
      apps/remix-ide-e2e/src/commands/hideToolTips.ts
  3. 3
      apps/remix-ide-e2e/src/commands/refreshPage.ts
  4. 4
      apps/remix-ide-e2e/src/helpers/init.ts
  5. 3
      apps/remix-ide-e2e/src/tests/circom.test.ts
  6. 2
      apps/remix-ide-e2e/src/tests/matomo.test.ts
  7. 13
      apps/remix-ide-e2e/src/tests/url.test.ts
  8. 1
      apps/remix-ide-e2e/src/types/index.d.ts
  9. 5
      apps/remix-ide/src/app.js
  10. 123
      apps/remix-ide/src/app/components/popup-panel.tsx
  11. 4
      apps/remix-ide/src/app/components/preload.tsx
  12. 37
      apps/remix-ide/src/app/plugins/remixAIPlugin.tsx
  13. 8
      apps/remix-ide/src/app/tabs/compile-tab.js
  14. 10
      apps/remix-ide/src/app/tabs/locales/ru/home.json
  15. 7
      apps/remix-ide/src/app/udapp/run-tab.tsx
  16. 1
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  17. 1
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  18. 1
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  19. 1
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  20. 1
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  21. 1
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  22. 1
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  23. 2
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  24. 1
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  25. 1
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  26. 1
      apps/remix-ide/src/assets/css/themes/remix-unicorn.css
  27. 1
      apps/remix-ide/src/assets/css/themes/remix-violet.css
  28. 77
      apps/remix-ide/src/assets/img/aiLogo.svg
  29. BIN
      apps/remix-ide/src/assets/img/aiLogoHead.webp
  30. 1
      apps/remixdesktop/test/types/index.d.ts
  31. 10
      libs/remix-api/src/lib/plugins/popuppanel-api.ts
  32. 23
      libs/remix-api/src/lib/plugins/remixaiDesktop-api.ts
  33. 3
      libs/remix-api/src/lib/remix-api.ts
  34. 2
      libs/remix-ui/app/src/index.ts
  35. 2
      libs/remix-ui/app/src/lib/remix-app/actions/app.ts
  36. 1
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  37. 7
      libs/remix-ui/app/src/lib/remix-app/reducer/app.ts
  38. 17
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  39. 3
      libs/remix-ui/app/src/lib/remix-app/state/app.ts
  40. 31
      libs/remix-ui/helper/src/lib/components/PluginViewWrapper.tsx
  41. 3
      libs/remix-ui/helper/src/lib/components/custom-tooltip.tsx
  42. 1
      libs/remix-ui/helper/src/types/customtooltip.ts
  43. 2
      libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx
  44. 11
      libs/remix-ui/panel/src/lib/plugins/remix-ui-panel.tsx
  45. 3
      libs/remix-ui/plugin-manager/src/lib/components/ActivePluginCard.tsx
  46. 3
      libs/remix-ui/plugin-manager/src/lib/components/InactivePluginCard.tsx
  47. 4
      libs/remix-ui/remix-ai/src/lib/components/Default.tsx
  48. 12
      libs/remix-ui/remix-ai/src/lib/components/RemixAI.tsx
  49. 40
      libs/remix-ui/remix-ai/src/lib/components/color.css
  50. 4
      libs/remix-ui/remix-ai/src/lib/components/personas.tsx
  51. 265
      libs/remix-ui/run-tab/src/lib/actions/evmmap.ts
  52. 5
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  53. 21
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  54. 4
      libs/remix-ui/run-tab/src/lib/components/environment.tsx
  55. 2
      libs/remix-ui/run-tab/src/lib/components/instanceContainerUI.tsx
  56. 4
      libs/remix-ui/run-tab/src/lib/components/settingsUI.tsx
  57. 8
      libs/remix-ui/run-tab/src/lib/components/universalDappUI.tsx
  58. 76
      libs/remix-ui/run-tab/src/lib/run-tab.tsx
  59. 21
      libs/remix-ui/run-tab/src/lib/types/index.ts
  60. 2
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  61. 2
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  62. 4
      libs/remix-ui/statusbar/src/css/statusbar.css
  63. 69
      libs/remix-ui/statusbar/src/lib/components/aiStatus.tsx
  64. 2
      libs/remix-ui/statusbar/src/lib/components/scamAlertStatus.tsx
  65. 15
      libs/remix-ui/statusbar/src/lib/components/scamDetails.tsx
  66. 28
      libs/remix-ui/statusbar/src/lib/remixui-statusbar-panel.tsx

@ -0,0 +1,25 @@
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class HidePopupPanel extends EventEmitter {
command(this: NightwatchBrowser) {
browser
.perform((done) => {
browser.execute(function () {
return localStorage.getItem('did_show_popup_panel')
}, [], function (result) {
if (!result.value) {
browser.waitForElementVisible('*[data-id="popupPanelToggle"]')
.click('*[data-id="popupPanelToggle"]')
}
done()
})
})
.perform((done) => {
done()
this.emit('complete')
})
}
}
module.exports = HidePopupPanel

@ -1,4 +1,4 @@
import {NightwatchBrowser} from 'nightwatch'
import { NightwatchBrowser } from 'nightwatch'
import EventEmitter from 'events'
class HideToolTips extends EventEmitter {
@ -16,6 +16,9 @@ class HideToolTips extends EventEmitter {
.popover {
display:none !important;
}
#scamDetails {
display:none !important;
}
`)
}, [], done())
})

@ -20,6 +20,9 @@ class RefreshPage extends EventEmitter {
.popover {
display:none !important;
}
#scamDetails {
display:none !important;
}
`)
}, [], done())
})

@ -14,6 +14,7 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
.url(url || 'http://127.0.0.1:8080')
.pause(5000)
.switchBrowserTab(0)
.hidePopupPanel()
.perform((done) => {
if (!loadPlugin) return done()
browser
@ -39,6 +40,9 @@ export default function (browser: NightwatchBrowser, callback: VoidFunction, url
.popover {
display:none !important;
}
#scamDetails {
display:none !important;
}
`);
}, [], done())
})

@ -5,6 +5,7 @@ import init from '../helpers/init'
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
browser.globals.asyncHookTimeout = 30000000;
init(browser, done)
},
@ -195,7 +196,7 @@ module.exports = {
.waitForElementPresent('[data-id="verticalIconsKindcircuit-compiler"]')
.waitForElementVisible('[data-id="verticalIconsKindcircuit-compiler"]')
.click('[data-id="play-editor"]')
.pause(7000)
.pause(10000)
.journalLastChildIncludes('newZkey')
.pause(25000)
.journalLastChildIncludes('setup done.')

@ -439,7 +439,7 @@ module.exports = {
return (window as any)._paq
}, [], (res) => {
const expectedEvents = [
["trackEvent", "Preload", "start"],
["trackEvent", "App", "Preload", "start"],
["trackEvent", "Storage", "activate", "indexedDB"],
["trackEvent", "App", "load"],
];

@ -91,23 +91,14 @@ module.exports = {
})
},
'Should load Etherscan verified contracts from URL "address" param) #group1': function (browser: NightwatchBrowser) {
'Should load Etherscan verified contracts from URL "address" param) #flaky #group1': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#address=0xdac17f958d2ee523a2206206994597c13d831ec7')
.refreshPage()
.pause(7000)
.pause(2000)
.currentWorkspaceIs('code-sample')
.waitForElementVisible('*[data-id=treeViewLitreeViewItemsepolia]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemsepolia/0xdac17f958d2ee523a2206206994597c13d831ec7/contracts/MetaMultiSigWallet.sol"]')
.getEditorValue((content) => {
browser.assert.ok(content && content.indexOf(
'contract MetaMultiSigWallet {') !== -1)
})
.waitForElementVisible('*[data-id=treeViewLitreeViewItemmainnet]')
.click('*[data-id=treeViewLitreeViewItemmainnet]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7"]')
.click('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7"]')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7/TetherToken.sol"]')
.click('*[data-id="treeViewLitreeViewItemmainnet/0xdac17f958d2ee523a2206206994597c13d831ec7/TetherToken.sol"]')
.getEditorValue((content) => {

@ -74,6 +74,7 @@ declare module 'nightwatch' {
connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser
waitForElementNotContainsText: (id: string, value: string, timeout: number = 10000) => NightwatchBrowser
hideToolTips: (this: NightwatchBrowser) => NightwatchBrowser
hidePopupPanel: (this: NightwatchBrowser) => NightwatchBrowser
enableClipBoard: () => NightwatchBrowser
addFileSnekmate: (name: string, content: NightwatchContractContent) => NightwatchBrowser
selectFiles: (selelectedElements: any[]) => NightwatchBrowser

@ -12,6 +12,7 @@ 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'
@ -450,6 +451,7 @@ class AppComponent {
this.sidePanel = new SidePanel()
this.hiddenPanel = new HiddenPanel()
this.pinnedPanel = new PinnedPanel()
this.popupPanel = new PopupPanel()
const pluginManagerComponent = new PluginManagerComponent(appManager, this.engine)
const filePanel = new FilePanel(appManager, contentImport)
@ -457,7 +459,7 @@ class AppComponent {
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.engine.register([this.menuicons, landingPage, this.hiddenPanel, this.sidePanel, this.statusBar, filePanel, pluginManagerComponent, this.settings, this.pinnedPanel, this.popupPanel])
// CONTENT VIEWS & DEFAULT PLUGINS
const openZeppelinProxy = new OpenZeppelinProxy(blockchain)
@ -540,6 +542,7 @@ class AppComponent {
await this.appManager.activatePlugin(['statusBar'])
await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately
await this.appManager.activatePlugin(['pinnedPanel'])
await this.appManager.activatePlugin(['popupPanel'])
await this.appManager.activatePlugin(['home'])
await this.appManager.activatePlugin(['settings', 'config'])
await this.appManager.activatePlugin([

@ -0,0 +1,123 @@
import React from 'react' // eslint-disable-line
import { AbstractPanel } from './panel'
import { PluginRecord, RemixPluginPanel } from '@remix-ui/panel'
import packageJson from '../../../../../package.json'
import { PluginViewWrapper } from '@remix-ui/helper'
import { EventEmitter } from 'events'
import { AppAction, appActionTypes, AppState } from '@remix-ui/app'
const profile = {
name: 'popupPanel',
displayName: 'Popup Panel',
description: 'Remix IDE popup panel',
version: packageJson.version,
events: [],
methods: ['addView', 'removeView', 'showContent', 'showPopupPanel']
}
type popupPanelState = {
plugins: Record<string, PluginRecord>
}
export class PopupPanel extends AbstractPanel {
element: HTMLDivElement
dispatch: React.Dispatch<any> = () => { }
appStateDispatch: React.Dispatch<AppAction> = () => { }
constructor(config) {
super(profile)
this.event = new EventEmitter()
}
setDispatch(dispatch: React.Dispatch<any>) {
this.dispatch = dispatch
}
setAppStateDispatch(appStateDispatch: React.Dispatch<AppAction>) {
this.appStateDispatch = appStateDispatch
}
onActivation() {
this.renderComponent()
}
focus(name) {
this.emit('focusChanged', name)
super.focus(name)
this.renderComponent()
}
addView(profile, view) {
super.addView(profile, view)
this.renderComponent()
this.showContent(profile.name) // should be handled by some click
}
removeView(profile) {
super.removeView(profile)
this.renderComponent()
}
async showContent(name) {
super.showContent(name)
this.renderComponent()
}
async showPopupPanel(show) {
this.appStateDispatch({
type: appActionTypes.setShowPopupPanel,
payload: show
})
this.renderComponent()
}
renderComponent() {
this.dispatch({
plugins: this.plugins
})
}
render() {
return (
<PluginViewWrapper useAppContext={true} plugin={this} />
)
}
updateComponent(state: popupPanelState, appState: Partial<AppState>) {
return (
<div
className={`px-0 bg-light border-info ${appState?.showPopupPanel ? 'd-flex' : 'd-none'}`}
style={{
maxHeight: '40rem',
maxWidth: '25rem',
width: 'max-content',
height: '40rem',
position: 'fixed',
bottom: '2rem',
right: '1.5rem',
zIndex: 200,
boxShadow: "0 1px 7px var(--secondary)"
}}
data-id="popupPanelPluginsContainer"
>
<div className='d-flex flex-column'>
<RemixPluginPanel
header={
<span id='popupPanelToggle' className='d-flex flex-row'>
<button
data-id='popupPanelToggle'
className='btn fas fa-angle-double-down'
onClick={async () => {
await this.showPopupPanel(false)
}}
>
</button>
</span>
}
plugins={state.plugins} />
</div>
</div>
)
}
}

@ -10,7 +10,7 @@ import './styles/preload.css'
import isElectron from 'is-electron'
const _paq = (window._paq = window._paq || [])
_paq.push(['trackEvent', 'Preload', 'start'])
_paq.push(['trackEvent', 'App', 'Preload', 'start'])
export const Preload = (props: any) => {
const [tip, setTip] = useState<string>('')
@ -40,7 +40,7 @@ export const Preload = (props: any) => {
})
})
.catch((err) => {
_paq.push(['trackEvent', 'Preload', 'error', err && err.message])
_paq.push(['trackEvent', 'App', 'PreloadError', err && err.message])
console.error('Error loading Remix:', err)
setError(true)
})

@ -5,6 +5,7 @@ import { RemixAITab, ChatApi } from '@remix-ui/remix-ai'
import React, { useCallback } from 'react';
import { ICompletions, IModel, RemoteInferencer, IRemoteModel, IParams, GenerationParams, CodeExplainAgent } from '@remix/remix-ai-core';
import { CustomRemixApi } from '@remix-api'
import { PluginViewWrapper } from '@remix-ui/helper'
type chatRequestBufferT<T> = {
[key in keyof T]: T[key]
@ -16,12 +17,13 @@ const profile = {
methods: ['code_generation', 'code_completion',
"solidity_answer", "code_explaining",
"code_insertion", "error_explaining",
"initialize", 'chatPipe', 'ProcessChatRequestBuffer', 'isChatRequestPending'],
"initialize", 'chatPipe', 'ProcessChatRequestBuffer',
'isChatRequestPending'],
events: [],
icon: 'assets/img/remix-logo-blue.png',
description: 'RemixAI provides AI services to Remix IDE.',
kind: '',
location: 'sidePanel',
location: 'popupPanel',
documentation: 'https://remix-ide.readthedocs.io/en/latest/remixai.html',
version: packageJson.version,
maintainedBy: 'Remix'
@ -37,6 +39,7 @@ export class RemixAIPlugin extends ViewPlugin {
chatRequestBuffer: chatRequestBufferT<any> = null
agent: CodeExplainAgent
useRemoteInferencer:boolean = false
dispatch: any
constructor(inDesktop:boolean) {
super(profile)
@ -46,6 +49,7 @@ export class RemixAIPlugin extends ViewPlugin {
}
onActivation(): void {
if (this.isOnDesktop) {
console.log('Activating RemixAIPlugin on desktop')
// this.on(this.remixDesktopPluginName, 'activated', () => {
@ -201,13 +205,40 @@ export class RemixAIPlugin extends ViewPlugin {
return ""
}
}
isChatRequestPending(){
return this.chatRequestBuffer != null
}
setDispatch(dispatch) {
this.dispatch = dispatch
this.renderComponent()
}
renderComponent () {
this.dispatch({
plugin: this,
})
}
render() {
return <div
id='ai-view'
className='h-100 d-flex'
data-id='aichat-view'
style={{
minHeight: 'max-content',
maxWidth: '25rem',
width: '24rem',
}}
>
<PluginViewWrapper plugin={this} />
</div>
}
updateComponent(state) {
return (
<RemixAITab plugin={this}></RemixAITab>
<RemixAITab plugin={state.plugin}></RemixAITab>
)
}
}

@ -99,13 +99,17 @@ class CompileTab extends CompilerApiMixin(ViewPlugin) { // implements ICompilerA
* This function is used by remix-plugin compiler API.
* @param {object} settings {evmVersion, optimize, runs, version, language}
*/
setCompilerConfig (settings) {
async setCompilerConfig (settings) {
super.setCompilerConfig(settings)
this.renderComponent()
// @todo(#2875) should use loading compiler return value to check whether the compiler is loaded instead of "setInterval"
const value = JSON.stringify(settings, null, '\t')
let pluginInfo
pluginInfo = await this.call('udapp', 'showPluginDetails')
this.call('notification', 'toast', compilerConfigChangedToastMsg(this.currentRequest.from, value))
if (this.currentRequest.from === 'udapp') {
this.call('notification', 'toast', compilerConfigChangedToastMsg((pluginInfo ? pluginInfo.displayName : this.currentRequest.from ), value))
}
}
compile (fileName) {

@ -1,4 +1,5 @@
{
"home.home": "Главная",
"home.scamAlert": "Предупреждение о мошенничестве",
"home.scamAlertText": "Единственный URL, который использует Remix, - это remix.ethereum.org",
"home.scamAlertText2": "Остерегайтесь видеороликов, рекламирующих \"ботов-передовиков ликвидности\"",
@ -6,6 +7,9 @@
"home.learnMore": "Узнать больше",
"home.here": "здесь",
"home.featured": "Рекомендуемые",
"home.learnEthPromoTitle": "LearnEth: Учебники внутри Remix",
"home.learnEthPromoButton": "Начать обучение",
"home.learnEthPromoText": "Ознакомьтесь с уроками по Remix, Solidity и другим проектам Web3. Отлично подходит для всех уровней навыков.",
"home.jumpIntoWeb3": "Перейти в WEB3",
"home.jumpIntoWeb3More": "Подробнее",
"home.jumpIntoWeb3Text": "Remix IDE является частью проекта Remix, широкий выбор инструментов которого, может быть использован для всего путешествия по разработке контракта пользователями любого уровня знаний. Узнайте больше на сайте проекта Remix.",
@ -36,6 +40,7 @@
"home.ozerc1155TemplateDesc": "Создайте ERC1155 токен, импортируя библиотеку OpenZeppelin.",
"home.gnosisSafeMultisigTemplateDesc": "Создайте кошельки с мульти-подписью с использованием этого шаблона.",
"home.zeroxErc20TemplateDesc": "Создайте токен ERC20, импортируя контракт с 0xProject.",
"home.learnEthPluginDesc": "Узнайте о Remix, Solidity и других проектах Web3.",
"home.learn": "Обучение",
"home.learnEth1": "Основы Remix",
"home.learnEth1Desc": "Введение в интерфейс Remix-а и основные операции.",
@ -65,5 +70,8 @@
"home.resources": "Источники",
"home.connectToLocalhost": "Подключиться к локальному хосту",
"home.seeAllTutorials": "Посмотреть все уроки",
"home.maintainedByRemix": "Поддерживается Remix"
"home.maintainedByRemix": "Поддерживается Remix",
"home.gitCloneTooltip": "Клонировать репозиторий Github в новую рабочую область",
"home.gistTooltip": "Открыть репозиторий Gist",
"home.newFileTooltip": "Добавить новый файл в рабочую область"
}

@ -35,7 +35,8 @@ const profile = {
'setEnvironmentMode',
'clearAllInstances',
'addInstance',
'resolveContractAndAddInstance'
'resolveContractAndAddInstance',
'showPluginDetails'
]
}
@ -87,6 +88,10 @@ export class RunTab extends ViewPlugin {
})
}
showPluginDetails() {
return profile
}
async setEnvironmentMode(env) {
const canCall = await this.askUserPermission('setEnvironmentMode', 'change the environment used')
if (canCall) {

@ -36,6 +36,7 @@
--body-bg: #fff;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs:0;
--breakpoint-sm:576px;
--breakpoint-md:768px;

@ -37,6 +37,7 @@
--body-bg: #060606;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs:0;
--breakpoint-sm:576px;
--breakpoint-md:768px;

@ -36,6 +36,7 @@
--body-bg: #fff;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs:0;
--breakpoint-sm:576px;
--breakpoint-md:768px;

@ -39,6 +39,7 @@
--body-bg:#fff;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs:0;
--breakpoint-sm:576px;
--breakpoint-md:768px;

@ -25,6 +25,7 @@
--text: #babbcc;
--body-bg: #1a1a1a;
--custom-select: #252525;
--brand-dark-blue: #222496;
--text-bg-mark: #a5a5a5;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;

@ -25,6 +25,7 @@
--body-bg: #d5efff;
--text-bg-mark: #fcf8e3;
--custom-select: #ffffff;
--brand-dark-blue: #222496;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;

@ -25,6 +25,7 @@
--text-background: #222336;
--text-bg-mark: #8388b2;
--custom-select: #35384c;
--brand-dark-blue: #222496;
--runtab: #8A93B0;
--body-bg: #222336;
--breakpoint-xs: 0;

@ -27,7 +27,7 @@
--body-bg: #011628;
--custom-select: #252525;
--text-bg-mark: #a5a5a5;
--custom-select: #011627;
--brand-dark-blue: #222496;
--text-background: #011626;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;

@ -25,6 +25,7 @@
--body-bg: #eef1f6;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;

@ -25,6 +25,7 @@
--body-bg: #DBE2E0;
--text-bg-mark: #fcf8e3;
--custom-select: #eeede9;
--brand-dark-blue: #222496;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;

@ -25,6 +25,7 @@
--body-bg: #f1eef6;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;

@ -25,6 +25,7 @@
--body-bg: #f1eef6;
--text-bg-mark: #fcf8e3;
--custom-select: #fff;
--brand-dark-blue: #222496;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;

@ -0,0 +1,77 @@
<svg width="1080" height="1080" viewBox="0 0 1080 1080" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_62_13806)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M243.632 1074.71C243.014 1074.6 242.439 1074.32 241.962 1073.92C234.393 1067.49 170.155 1012.98 156.436 1001.34C155.758 1000.76 155.338 999.934 155.267 999.041C155.191 998.157 155.49 997.272 156.069 996.594C166.824 984.128 213.289 930.238 213.289 930.238C213.289 930.238 136.374 884.225 116.785 872.51C115.815 871.923 115.202 870.875 115.163 869.736C115.124 868.596 115.683 867.513 116.628 866.876C132.253 856.218 184.848 820.367 184.848 820.367C184.848 820.367 143.189 796.562 129.723 788.866C128.778 788.325 128.154 787.351 128.057 786.265C127.955 785.188 128.395 784.118 129.226 783.407C150.037 765.75 253.071 678.318 253.071 678.318C253.071 678.318 120.643 568.156 101.261 552.032C99.9274 550.923 99.6631 548.979 100.649 547.553C112.476 530.493 180.345 432.538 180.345 432.538C180.345 432.538 81.023 322.558 61.4772 300.915C60.7298 300.088 60.4511 298.952 60.7139 297.871C60.9764 296.791 61.7588 295.914 62.8047 295.531C89.0112 285.876 217.225 238.624 217.225 238.624C217.225 238.624 243.762 97.0242 248.974 69.227C249.176 68.154 249.883 67.2436 250.88 66.7932C251.877 66.3438 253.027 66.4057 253.965 66.9628C277.983 81.2078 398.947 152.954 398.947 152.954C398.947 152.954 464.447 41.4414 478.238 17.9661C478.8 17.0021 479.805 16.3832 480.922 16.3181C482.039 16.2539 483.108 16.745 483.783 17.6328C500.981 40.2573 585.732 151.762 585.732 151.762C585.732 151.762 716.782 81.8336 742.178 68.2874C743.147 67.7743 744.298 67.7605 745.274 68.2579C746.249 68.7553 746.922 69.6978 747.066 70.7796C750.766 97.4147 768.733 226.986 768.733 226.986C768.733 226.986 930.559 247.282 963.181 251.372C964.362 251.521 965.375 252.284 965.842 253.38C966.31 254.474 966.161 255.735 965.451 256.689C946.661 281.981 856.82 402.908 856.82 402.908C856.82 402.908 958.366 483.76 977.854 499.277C978.641 499.903 979.105 500.851 979.116 501.857C979.128 502.863 978.686 503.821 977.913 504.465C956.879 522.007 837.76 621.353 837.76 621.353L840.361 769.536C840.361 769.536 950.632 834.064 970.301 845.574C971.084 846.032 971.648 846.787 971.862 847.667C972.077 848.548 971.927 849.479 971.444 850.245C959.731 868.857 896.099 969.963 896.099 969.963C896.099 969.963 970.865 1044 987.455 1060.44C988.211 1061.19 988.568 1062.24 988.41 1063.31C988.255 1064.36 987.611 1065.27 986.669 1065.77C965.466 1077 867.657 1128.83 867.657 1128.83C867.657 1128.83 908.489 1214.19 916.057 1230.01C916.755 1231.46 916.313 1233.21 915.012 1234.16C900.776 1244.56 823.272 1301.2 812.11 1309.36C811.266 1309.97 810.182 1310.16 809.178 1309.86C785.939 1302.94 515.724 1222.5 515.724 1222.5C515.724 1222.5 348.111 1100.64 336.681 1092.33C336.268 1092.04 335.803 1091.84 335.324 1091.75C327.065 1090.22 252.859 1076.43 243.632 1074.71Z" fill="#222496"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M154.261 999.55L211.042 931.338C211.042 931.338 134.129 885.32 114.546 873.596C113.566 873.016 112.953 871.967 112.919 870.829C112.886 869.68 113.436 868.609 114.38 867.962C130.006 857.308 182.604 821.464 182.604 821.464C182.604 821.464 140.948 797.658 127.482 789.96C126.535 789.414 125.911 788.443 125.811 787.361C125.709 786.279 126.146 785.208 126.978 784.505C147.792 766.834 250.823 679.405 250.823 679.405C250.823 679.405 118.398 569.241 99.0156 553.121C97.6824 552.016 97.4169 550.064 98.4053 548.647C110.227 531.579 178.099 433.63 178.099 433.63C178.099 433.63 78.7798 323.645 59.2336 302.002C58.4884 301.177 58.2017 300.039 58.4672 298.957C58.7338 297.886 59.517 297.004 60.5601 296.625C86.763 286.964 214.983 239.719 214.983 239.719C214.983 239.719 241.518 98.1174 246.727 70.317C246.929 69.246 247.641 68.331 248.637 67.8848C249.632 67.4274 250.784 67.4945 251.724 68.0523C275.739 82.2983 396.704 154.042 396.704 154.042C396.704 154.042 462.203 42.5274 475.991 19.0554C476.557 18.096 477.564 17.4713 478.679 17.4044C479.792 17.3374 480.867 17.8397 481.541 18.721L488.245 27.5452L503.432 226.655L638.418 238.927L536.398 388.415L659.614 382.235L541.976 593.684L530.764 836.96L274.271 1104.43L154.261 999.55Z" fill="#2F63D1"/>
<ellipse cx="562.952" cy="985.462" rx="11.4792" ry="6.78319" transform="rotate(60 562.952 985.462)" fill="#B9F3FF"/>
<ellipse cx="585.067" cy="970.117" rx="11.4792" ry="3.69524" transform="rotate(60 585.067 970.117)" fill="#74E8FF"/>
<path d="M522.73 768.855V375.5H869.678L792.144 705.156L711.264 779.9L522.73 768.855Z" fill="url(#paint0_linear_62_13806)"/>
<path d="M545.599 760.506V375.5H198.651L276.185 705.156L357.065 779.9L545.599 760.506Z" fill="url(#paint1_linear_62_13806)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M165.466 403.392C302.125 355.422 474.484 429.608 539.188 476.462L480.619 17.3984L379.659 146.249L254.713 67.5999L241.884 246.094L62.832 297.969L165.466 403.392Z" fill="#2F63D1"/>
<path d="M565.962 132.304L480.619 17.3984L539.188 476.462C597.756 425.145 781.27 378.848 883.904 374.386L973.709 255.576L800.235 231.591L756.169 211.511V61.4641L639.591 138.44L565.962 132.304Z" fill="#222496"/>
<path d="M717.787 315.353C717.787 385.91 560.987 397.932 538.049 397.932C515.111 397.932 351.302 384.865 357.953 315.353C359.358 300.671 465.858 348.803 537.87 348.803C609.882 348.803 717.787 299.982 717.787 315.353Z" fill="#252773"/>
<path d="M856.05 466.132C806.158 516.024 765.042 444.604 748.822 428.384C732.602 412.164 626.012 265.5 679.867 221.05C691.242 211.661 816.509 256.793 867.429 307.713C918.349 358.633 866.919 455.263 856.05 466.132Z" fill="#252773" fill-opacity="0.32"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M214.781 528.645C240.56 340.364 331.35 317.227 542.858 324.882C672.596 329.578 780.994 300.874 823.392 390.627C854.249 462.633 854.249 538.814 854.249 538.814L901.357 531.269C901.357 531.269 933.524 487.297 873.483 357.234C825.386 253.05 727.906 270.659 542.858 270.659C328.136 270.66 224.635 257.102 186.294 372.886C155.177 466.866 172.657 533.118 172.657 533.118C172.657 533.118 198.422 531.104 214.781 528.645Z" fill="#46D0FF"/>
<path d="M870.313 568.843C870.313 568.843 905.92 514.415 851.046 384.371C807.089 280.202 711.791 308.27 542.671 308.27C346.43 308.271 252.121 283.212 217.08 398.98C188.641 492.946 204.332 570.692 204.332 570.692" stroke="#29C0F4" stroke-width="25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M910.424 311.537L874.083 408.681C872.608 412.624 874.839 416.982 878.901 418.093L911.092 426.897C915.196 428.02 919.368 425.338 920.049 421.139L936.885 317.416C937.513 313.549 934.914 309.895 931.054 309.218L918.368 306.994C914.972 306.398 911.633 308.307 910.424 311.537Z" fill="#03D5FE"/>
<path d="M958.156 289.86C958.156 305.673 945.333 318.493 929.514 318.493C913.696 318.493 900.873 305.673 900.873 289.86C900.873 274.046 913.696 261.227 929.514 261.227C945.333 261.227 958.156 274.046 958.156 289.86Z" fill="#7FE5FF"/>
<path d="M910.424 311.537L874.083 408.681C872.608 412.624 874.839 416.982 878.901 418.093L911.092 426.897C915.196 428.02 919.368 425.338 920.049 421.139L936.885 317.416C937.513 313.549 934.914 309.895 931.054 309.218L918.368 306.994C914.972 306.398 911.633 308.307 910.424 311.537Z" fill="white" fill-opacity="0.32"/>
<path d="M958.156 289.86C958.156 305.673 945.333 318.493 929.514 318.493C913.696 318.493 900.873 305.673 900.873 289.86C900.873 274.046 913.696 261.227 929.514 261.227C945.333 261.227 958.156 274.046 958.156 289.86Z" fill="white" fill-opacity="0.32"/>
<path d="M164.075 319.884L200.416 417.029C201.891 420.972 199.66 425.33 195.598 426.441L163.407 435.245C159.303 436.368 155.131 433.686 154.45 429.487L137.614 325.763C136.986 321.896 139.585 318.243 143.445 317.566L156.131 315.341C159.527 314.745 162.866 316.655 164.075 319.884Z" fill="#03D5FE"/>
<path d="M116.343 298.207C116.343 314.021 129.166 326.84 144.985 326.84C160.803 326.84 173.626 314.021 173.626 298.207C173.626 282.394 160.803 269.574 144.985 269.574C129.166 269.574 116.343 282.394 116.343 298.207Z" fill="#7FE5FF"/>
<path d="M164.075 319.884L200.416 417.029C201.891 420.972 199.66 425.33 195.598 426.441L163.407 435.245C159.303 436.368 155.131 433.686 154.45 429.487L137.614 325.763C136.986 321.896 139.585 318.243 143.445 317.566L156.131 315.341C159.527 314.745 162.866 316.655 164.075 319.884Z" fill="white" fill-opacity="0.32"/>
<path d="M116.343 298.207C116.343 314.021 129.166 326.84 144.985 326.84C160.803 326.84 173.626 314.021 173.626 298.207C173.626 282.394 160.803 269.574 144.985 269.574C129.166 269.574 116.343 282.394 116.343 298.207Z" fill="white" fill-opacity="0.32"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M817.838 672.654C817.838 672.654 858.785 537.91 811.604 381.191C811.604 381.191 910.242 373.886 947.161 398.765C971.679 415.292 989.826 472.937 979.882 546.77C972.463 601.858 936.353 638.341 911.889 654.663C885.655 672.164 830.694 672.675 817.838 672.654Z" fill="#7FE5FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M904.192 384.41C921.06 387.19 936.629 391.665 947.162 398.769C971.68 415.296 989.827 472.941 979.883 546.774C972.464 601.862 936.353 638.345 911.89 654.667C909.445 656.297 906.695 657.789 903.719 659.136C946.83 529.006 914.015 413.662 904.192 384.41Z" fill="#46D0FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M969.057 524.311C969.057 524.311 975.676 481.083 956.955 429.507C955.893 426.579 952.654 425.067 949.727 426.129C946.799 427.192 945.285 430.43 946.348 433.357C963.98 481.93 957.9 522.621 957.9 522.621C957.434 525.7 959.556 528.578 962.634 529.043C965.713 529.51 968.591 527.388 969.057 524.311Z" fill="#7FE5FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M964.603 562.474C964.603 562.474 967.01 557.577 967.99 547.819C968.302 544.719 966.039 541.951 962.941 541.64C959.842 541.329 957.075 543.592 956.763 546.69C956.03 553.977 954.394 557.667 954.394 557.667C953.067 560.486 954.278 563.849 957.095 565.176C959.912 566.502 963.276 565.292 964.603 562.474Z" fill="#7FE5FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M257.492 378.07C257.492 378.07 211.004 507.589 279.465 701.67C279.465 701.67 131.521 668.521 110.893 624.987C72.8177 544.634 100.945 434.897 117.928 418.428C138.184 398.786 235.97 380.605 257.492 378.07Z" fill="#7FE5FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M169.372 667.29C142.49 655.654 118.605 641.263 110.892 624.986C72.8168 544.633 100.945 434.896 117.928 418.427C126.05 410.55 146.172 402.784 168.751 396.142C153.398 429.283 129.164 512.994 169.372 667.29Z" fill="#47D0FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M118.306 441.607C118.306 441.607 100.064 484.057 105.62 542.033C105.918 545.133 108.676 547.409 111.776 547.112C114.876 546.814 117.15 544.056 116.854 540.956C111.603 486.175 128.682 446.044 128.682 446.044C129.906 443.181 128.576 439.863 125.712 438.638C122.85 437.413 119.531 438.743 118.306 441.607Z" fill="#7FE5FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M108.52 570.949C108.52 570.949 107.328 578.232 114.124 591.271C115.562 594.033 118.973 595.106 121.734 593.667C124.496 592.228 125.569 588.818 124.131 586.056C119.856 577.855 119.685 573.034 119.74 572.139C119.742 572.092 119.745 572.068 119.745 572.068C120.093 569.193 117.882 566.384 114.791 566.014C111.698 565.646 108.888 567.857 108.52 570.949Z" fill="#7FE5FF"/>
<path d="M649.442 993.36C649.442 1028.81 633.393 1060.5 608.166 1081.58C588.226 1098.24 562.552 1108.27 534.537 1108.27C507.904 1108.27 483.387 1099.21 463.902 1084C436.957 1062.97 419.631 1030.19 419.631 993.36C419.631 929.9 471.076 878.455 534.537 878.455C597.997 878.455 649.442 929.9 649.442 993.36Z" fill="#252773"/>
<path d="M622.668 993.36C622.668 1042.03 583.21 1081.49 534.537 1081.49C485.863 1081.49 446.405 1042.03 446.405 993.36C446.405 944.687 485.863 905.229 534.537 905.229C583.21 905.229 622.668 944.687 622.668 993.36Z" fill="#01026D"/>
<path d="M601.472 993.36C601.472 1030.33 571.504 1060.3 534.537 1060.3C497.57 1060.3 467.602 1030.33 467.602 993.36C467.602 956.393 497.57 926.425 534.537 926.425C571.504 926.425 601.472 956.393 601.472 993.36Z" fill="#03D5FE"/>
<path d="M580.276 993.36C580.276 1018.62 559.798 1039.1 534.537 1039.1C509.276 1039.1 488.798 1018.62 488.798 993.36C488.798 968.099 509.276 947.621 534.537 947.621C559.798 947.621 580.276 968.099 580.276 993.36Z" fill="#74E8FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M313.3 1145.64C303.718 1143.94 292.13 1142.33 279.727 1140.6L279.726 1140.6C245.407 1135.82 204.854 1130.17 183.317 1119.56C170.44 1198.14 171.819 1281.17 299.08 1249.4C302.881 1248.46 305.654 1245.05 305.817 1241.14C306.843 1216.5 309.196 1183.05 313.3 1145.64Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M770.277 1249.4C897.538 1281.17 898.918 1198.14 886.041 1119.56C864.504 1130.17 823.95 1135.82 789.632 1140.6L789.631 1140.6C777.228 1142.33 765.64 1143.94 756.058 1145.64C760.161 1183.05 762.515 1216.5 763.54 1241.14C763.703 1245.05 766.477 1248.46 770.277 1249.4Z" fill="white"/>
<path d="M901.007 1104.92C906.625 1078.7 882.6 1020.69 873.676 1012.33C870.553 1009.4 864.704 1008.06 857.025 1007.91C827.957 1007.35 772.675 1023.84 739.902 1035.75C746.997 1073.22 752.272 1111.12 756.058 1145.64C765.64 1143.94 777.228 1142.33 789.631 1140.6L789.632 1140.6C823.95 1135.82 864.504 1130.17 886.041 1119.56C894.307 1115.49 899.772 1110.68 901.007 1104.92Z" fill="#252773"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M279.727 1140.6L279.726 1140.6C245.407 1135.82 204.854 1130.17 183.317 1119.56C175.051 1115.49 169.586 1110.68 168.35 1104.92" fill="#40B7E2"/>
<path d="M195.682 1012.33C186.757 1020.69 162.732 1078.7 168.35 1104.92C169.586 1110.68 175.051 1115.49 183.317 1119.56C204.854 1130.17 245.407 1135.82 279.726 1140.6L279.727 1140.6C292.13 1142.33 303.718 1143.94 313.3 1145.64C317.086 1111.12 322.361 1073.22 329.455 1035.75C296.683 1023.84 241.401 1007.35 212.333 1007.91C204.654 1008.06 198.804 1009.4 195.682 1012.33Z" fill="#252773"/>
<path d="M756.058 1145.64C752.272 1111.12 746.997 1073.22 739.902 1035.75C772.675 1023.84 827.957 1007.35 857.025 1007.91C825.139 945.778 758.557 875.025 722.514 841.641H346.844C310.8 875.025 244.219 945.778 212.333 1007.91C241.401 1007.35 296.683 1023.84 329.455 1035.75C322.361 1073.22 317.086 1111.12 313.3 1145.64L379.47 1165.16L463.902 1084C436.957 1062.97 419.631 1030.19 419.631 993.36C419.631 929.9 471.076 878.455 534.537 878.455C597.997 878.455 649.442 929.9 649.442 993.36C649.442 1028.81 633.393 1060.5 608.166 1081.58L697.971 1165.16L756.058 1145.64Z" fill="white"/>
<path d="M372.501 888.758C354.265 929.264 340.599 981.375 330.57 1034.34C324.567 1031.88 314.131 1027.18 302.13 1024.05C324.045 961.958 336.458 922.143 372.501 888.758Z" fill="#F8F8F8"/>
<path d="M696.598 888.758C714.835 929.264 728.501 981.375 738.53 1034.34C744.533 1031.88 754.969 1027.18 766.97 1024.05C745.055 961.958 732.642 922.143 696.598 888.758Z" fill="#F8F8F8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M733.5 835.04L740.447 821.394C743.987 814.44 738.589 806.264 730.802 806.774C719.229 807.532 719.381 808.575 699.999 809.679C651.355 812.451 584.839 815.607 534.184 815.607C483.53 815.607 417.013 812.451 368.37 809.679C349.157 808.585 349.429 807.55 337.871 806.794C330.02 806.28 324.618 814.583 328.288 821.543L335.468 835.16C337.075 838.208 340.107 840.215 343.542 840.469C369.079 842.358 461.349 850.087 534.498 850.087C607.547 850.087 699.62 842.379 725.349 840.477C728.836 840.219 731.914 838.155 733.5 835.04Z" fill="#252773"/>
<path d="M368.37 809.679C417.013 812.451 483.53 815.607 534.184 815.607C584.839 815.607 651.355 812.451 699.999 809.679L678.577 750.744C677.136 746.78 673.369 744.141 669.15 744.141H399.218C395 744.141 391.232 746.78 389.791 750.744L368.37 809.679Z" fill="url(#paint2_linear_62_13806)"/>
<path d="M380.957 774.33C379.595 779.241 383.106 784.156 388.196 784.42C433.839 786.781 487.656 787.531 533.851 787.531C580.045 787.531 634.682 789.52 680.436 787.532C685.531 787.311 689.043 782.389 687.674 777.476L675.172 732.615C673.761 728.62 670.074 725.961 665.945 725.961H401.756C397.628 725.961 393.94 728.62 392.53 732.615L380.957 774.33Z" fill="#D9D9D9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M326.121 305.055C259.847 305.055 206.121 358.78 206.121 425.055V659.878C206.121 726.152 259.847 779.878 326.121 779.878H743.568C809.843 779.878 863.568 726.152 863.568 659.878V425.055C863.568 358.781 809.843 305.055 743.568 305.055H326.121ZM351.866 353.059C307.683 353.059 271.866 388.876 271.866 433.059V652.917C271.866 697.1 307.683 732.917 351.866 732.917H717.824C762.007 732.917 797.824 697.1 797.824 652.917V433.059C797.824 388.876 762.007 353.059 717.824 353.059H351.866Z" fill="white"/>
<rect x="271.866" y="353.062" width="525.958" height="379.858" rx="80" fill="url(#paint3_radial_62_13806)"/>
<rect x="277.866" y="359.062" width="513.958" height="367.858" rx="74" stroke="url(#paint4_linear_62_13806)" stroke-opacity="0.32" stroke-width="12"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M457.177 582.156C457.177 582.156 425.533 544.994 366.765 538.982C362.781 538.575 359.226 541.48 358.819 545.462C358.411 549.443 361.311 553.004 365.293 553.412C417.752 558.775 446.175 591.603 446.175 591.603C448.78 594.64 453.363 594.99 456.397 592.382C459.431 589.773 459.782 585.193 457.177 582.156Z" fill="#222496"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M616.002 582.156C616.002 582.156 647.647 544.994 706.415 538.982C710.398 538.575 713.954 541.48 714.361 545.462C714.768 549.443 711.869 553.004 707.886 553.412C655.427 558.775 627.005 591.603 627.005 591.603C624.4 594.64 619.816 594.99 616.783 592.382C613.749 589.773 613.397 585.193 616.002 582.156Z" fill="#222496"/>
</g>
<defs>
<linearGradient id="paint0_linear_62_13806" x1="504.67" y1="375.298" x2="838.931" y2="558.421" gradientUnits="userSpaceOnUse">
<stop stop-color="#D5CDFB"/>
<stop offset="1" stop-color="#DBD4FC"/>
</linearGradient>
<linearGradient id="paint1_linear_62_13806" x1="198.931" y1="375.297" x2="522.238" y2="571.176" gradientUnits="userSpaceOnUse">
<stop stop-color="#F6F5FC"/>
<stop offset="1" stop-color="#DBD6F4"/>
</linearGradient>
<linearGradient id="paint2_linear_62_13806" x1="534.184" y1="744.141" x2="534.406" y2="835.015" gradientUnits="userSpaceOnUse">
<stop offset="0.359682" stop-color="#D9D9D9"/>
<stop offset="1" stop-color="#FDFDFD"/>
</linearGradient>
<radialGradient id="paint3_radial_62_13806" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(422.432 470.962) rotate(66.7746) scale(285.06 382.801)">
<stop offset="0.219155" stop-color="#D6F8FF"/>
<stop offset="1" stop-color="#74E8FF"/>
</radialGradient>
<linearGradient id="paint4_linear_62_13806" x1="534.845" y1="353.062" x2="534.845" y2="732.921" gradientUnits="userSpaceOnUse">
<stop stop-color="#526178" stop-opacity="0.16"/>
<stop offset="1" stop-color="#97B4DE"/>
</linearGradient>
<clipPath id="clip0_62_13806">
<rect width="1080" height="1080" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

@ -71,6 +71,7 @@ declare module 'nightwatch' {
connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser
waitForElementNotContainsText: (id: string, value: string, timeout: number = 10000) => NightwatchBrowser
hideToolTips: (this: NightwatchBrowser) => NightwatchBrowser
hidePopupPanel: (this: NightwatchBrowser) => NightwatchBrowser
enableClipBoard: () => NightwatchBrowser
}

@ -0,0 +1,10 @@
import { IFilePanel } from '@remixproject/plugin-api'
import { StatusEvents } from '@remixproject/plugin-utils'
export interface IPopupPanelAPI {
events:{
} & StatusEvents
methods: {
showPopupPanel(state: boolean): void
}
}

@ -1,23 +0,0 @@
import { IParams } from "@remix/remix-ai-core";
import { StatusEvents } from "@remixproject/plugin-utils";
export interface IRemixAID {
events: {
activated():void,
onInference():void,
onInferenceDone():void,
onStreamResult(streamText: string):void,
} & StatusEvents,
methods: {
code_completion(context: string): Promise<string>
code_insertion(msg_pfx: string, msg_sfx: string): Promise<string>,
code_generation(prompt: string): Promise<string | null>,
code_explaining(code: string, context?: string): Promise<string | null>,
error_explaining(prompt: string): Promise<string | null>,
solidity_answer(prompt: string): Promise<string | null>,
initializeModelBackend(local: boolean, generalModel?, completionModel?): Promise<boolean>,
chatPipe(pipeMessage: string): Promise<void>,
ProcessChatRequestBuffer(params:IParams): Promise<void>,
}
}

@ -17,8 +17,10 @@ import { IRemixAI } from "./plugins/remixai-api"
import { IRemixAID } from "./plugins/remixAIDesktop-api"
import { IMenuIconsApi } from "./plugins/menuicons-api"
import { IDgitPlugin } from "./plugins/dgitplugin-api"
import { IPopupPanelAPI } from "./plugins/popuppanel-api"
export interface ICustomRemixApi extends IRemixApi {
popupPanel: IPopupPanelAPI
dgitApi: IGitApi
dgit: IDgitPlugin
config: IConfigApi
@ -39,4 +41,5 @@ export interface ICustomRemixApi extends IRemixApi {
remixAID: IRemixAID
}
export declare type CustomRemixApi = Readonly<ICustomRemixApi>

@ -2,6 +2,6 @@ export { default as RemixApp } from './lib/remix-app/remix-app'
export { dispatchModalContext, dispatchModalInterface, AppContext, appProviderContextType, appPlatformTypes, platformContext, onLineContext } from './lib/remix-app/context/context'
export { ModalProvider, useDialogDispatchers } from './lib/remix-app/context/provider'
export { AppModal } from './lib/remix-app/interface/index'
export { AlertModal } from './lib/remix-app/interface/index'
export { AlertModal, AppState } from './lib/remix-app/interface/index'
export { ModalTypes, AppModalCancelTypes } from './lib/remix-app/types/index'
export { AppAction, appActionTypes } from './lib/remix-app/actions/app'

@ -17,6 +17,7 @@ export const enum appActionTypes {
setCurrentBranch = 'SET_CURRENT_BRANCH',
setNeedsGitInit = 'SET_NEEDS_GIT_INIT',
setCanUseGit = 'SET_CAN_USE_GIT',
setShowPopupPanel = 'SET_SHOW_POPUP_PANEL',
}
type AppPayload = {
@ -24,6 +25,7 @@ type AppPayload = {
[appActionTypes.setCurrentBranch]: branch,
[appActionTypes.setNeedsGitInit]: boolean,
[appActionTypes.setCanUseGit]: boolean,
[appActionTypes.setShowPopupPanel]: boolean,
}
export type AppAction = ActionMap<AppPayload>[keyof ActionMap<

@ -52,5 +52,6 @@ export interface AppState {
currentBranch: branch
needsGitInit: boolean
canUseGit: boolean
showPopupPanel: boolean
}

@ -27,5 +27,12 @@ export const appReducer = (state: AppState, action: AppAction): AppState => {
canUseGit: action.payload
}
}
case appActionTypes.setShowPopupPanel: {
return {
...state,
showPopupPanel: action.payload
}
}
}
}

@ -9,12 +9,11 @@ import { AppProvider } from './context/provider'
import AppDialogs from './components/modals/dialogs'
import DialogViewPlugin from './components/modals/dialogViewPlugin'
import { appProviderContextType, onLineContext, platformContext } from './context/context'
import { FormattedMessage, IntlProvider } from 'react-intl'
import { CustomTooltip } from '@remix-ui/helper'
import { IntlProvider } from 'react-intl'
import { UsageTypes } from './types'
import { AppState } from './interface'
import { appReducer } from './reducer/app'
import { appInitialState } from './state/app'
import isElectron from 'is-electron'
declare global {
interface Window {
@ -45,7 +44,10 @@ const RemixApp = (props: IRemixAppUi) => {
const sidePanelRef = useRef(null)
const pinnedPanelRef = useRef(null)
const [appState, appStateDispatch] = useReducer(appReducer, appInitialState)
const [appState, appStateDispatch] = useReducer(appReducer, {
...appInitialState,
showPopupPanel: !window.localStorage.getItem('did_show_popup_panel') && !isElectron()
})
useEffect(() => {
async function activateApp() {
@ -77,6 +79,12 @@ const RemixApp = (props: IRemixAppUi) => {
}
}, [])
useEffect(() => {
if (!appState.showPopupPanel) {
window.localStorage.setItem('did_show_popup_panel', 'true')
}
},[appState.showPopupPanel])
function setListeners() {
props.app.sidePanel.events.on('toggle', () => {
setHideSidePanel((prev) => {
@ -244,6 +252,7 @@ const RemixApp = (props: IRemixAppUi) => {
}
<div>{props.app.hiddenPanel.render()}</div>
</div>
<div>{props.app.popupPanel.render()}</div>
<div className="statusBar fixed-bottom">
{props.app.statusBar.render()}
</div>

@ -5,5 +5,6 @@ export const appInitialState: AppState = {
gitHubUser: {} as GitHubUser,
currentBranch: null,
needsGitInit: true,
canUseGit: false
canUseGit: false,
showPopupPanel: false
}

@ -1,18 +1,35 @@
import React from 'react'
import { useEffect, useState } from 'react'
import { AppContext } from '@remix-ui/app'
import React, { useContext, useEffect, useState } from 'react'
interface IPluginViewWrapperProps {
plugin: any
useAppContext?: boolean // Optional flag to decide whether to use AppContext
}
export const PluginViewWrapper = (props: IPluginViewWrapperProps) => {
export const PluginViewWrapper = ({ plugin, useAppContext = false }: IPluginViewWrapperProps) => {
const [state, setState] = useState<any>(null)
const appContext = useAppContext ? useContext(AppContext) : null
useEffect(() => {
if (props.plugin.setDispatch) {
props.plugin.setDispatch(setState)
if (plugin.setDispatch) {
plugin.setDispatch(setState)
}
if (useAppContext && appContext.appStateDispatch && plugin.setAppStateDispatch) {
plugin.setAppStateDispatch(appContext.appStateDispatch)
}
}, [plugin])
if (useAppContext && appContext && appContext.appState) {
return (
<>
{state ? <>{plugin.updateComponent(state, appContext.appState)}</> : <></>}
</>
)
}
}, [])
return <>{state ? <>{props.plugin.updateComponent(state)}</> : <></>}</>
return (
<>
{state ? <>{plugin.updateComponent(state)}</> : <></>}
</>
)
}

@ -4,7 +4,7 @@ import { Fragment } from 'react'
import { OverlayTrigger, Popover, Tooltip } from 'react-bootstrap'
import { CustomTooltipType } from '../../types/customtooltip'
export function CustomTooltip({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay, hide }: CustomTooltipType) {
export function CustomTooltip({ children, placement, tooltipId, tooltipClasses, tooltipText, tooltipTextClasses, delay, hide, show }: CustomTooltipType) {
if (typeof tooltipText !== 'string') {
const newTooltipText = React.cloneElement(tooltipText, {
className: ' bg-secondary text-wrap p-1 px-2 '
@ -16,6 +16,7 @@ export function CustomTooltip({ children, placement, tooltipId, tooltipClasses,
(!hide ? (
<Fragment>
<OverlayTrigger
show={show}
placement={placement}
overlay={
<Popover id={`popover-positioned-${placement}`}>

@ -10,4 +10,5 @@ export type CustomTooltipType = {
tooltipTextClasses?: string
delay?: OverlayDelay
hide?: boolean
show?: boolean
}

@ -171,7 +171,7 @@ function HomeTabGetStarted({ plugin }: HomeTabGetStartedProps) {
<button
key={index}
className={index === 0 ?
'btn btn-primary border p-2 text-nowrap mr-3 mb-2' :
'btn btn-primary border p-2 text-nowrap mr-3 mb-3' :
index === workspaceTemplates.length - 1 ?
'btn border p-2 text-nowrap mr-2 mb-3' :
'btn border p-2 text-nowrap mr-3 mb-3'

@ -18,9 +18,14 @@ export function RemixPluginPanel(props: RemixPanelProps) {
{props.header}
<div className="pluginsContainer">
<div className="plugins" id="plugins">
{Object.values(props.plugins).map((pluginRecord) => {
return <RemixUIPanelPlugin key={pluginRecord.profile.name} pluginRecord={pluginRecord} initialState={props.pluginState} highlightStamp={props.highlightStamp} />
})}
{ Object.values(props.plugins).map((pluginRecord) => {
return <RemixUIPanelPlugin
key={pluginRecord.profile.name}
pluginRecord={pluginRecord}
initialState={props.pluginState}
highlightStamp={props.highlightStamp}
/>
}) }
</div>
</div>
</>

@ -3,6 +3,8 @@ import React from 'react'
import { FormattedMessage } from 'react-intl'
import '../remix-ui-plugin-manager.css'
import { CustomTooltip } from '@remix-ui/helper'
const _paq = (window._paq = window._paq || [])
interface PluginCardProps {
profile: any
buttonText: string
@ -85,6 +87,7 @@ function ActivePluginCard({ profile, buttonText, deactivatePlugin }: PluginCardP
>
<button
onClick={() => {
_paq.push(['trackEvent', 'pluginManager', 'deactivateBtn', 'deactivate btn ' + profile.name])
deactivatePlugin(profile.name)
}}
className="btn btn-secondary btn-sm"

@ -5,6 +5,8 @@ import { FormattedMessage, useIntl } from 'react-intl'
import '../remix-ui-plugin-manager.css'
import { CustomTooltip } from '@remix-ui/helper'
import { onLineContext } from '@remix-ui/app'
const _paq = (window._paq = window._paq || [])
interface PluginCardProps {
profile: any
buttonText: string
@ -103,6 +105,7 @@ function InactivePluginCard({ profile, buttonText, activatePlugin }: PluginCardP
{!canBeActivated ? <button className="btn btn-secondary btn-sm">{intl.formatMessage({ id: 'pluginManager.UnavailableOffline' })}</button> : (
<button
onClick={() => {
_paq.push(['trackEvent', 'pluginManager', 'activateBtn', 'activate btn' + profile.name])
activatePlugin(profile.name)
}}
className="btn btn-success btn-sm"

@ -42,8 +42,8 @@ export const Default = (props) => {
};
ChatApi = useAiChatApi();
const conversationStarters: ConversationStarter[] = [
{ prompt: 'Explain briefly the current file in Editor', icon: <span></span> },
{ prompt: 'Explain what is a solidity contract!' }]
{ prompt: 'Explain what is a solidity contract!' },
{ prompt: 'Explain briefly the current file in Editor' }]
// Define initial messages
const initialMessages: ChatItem[] = [

@ -2,13 +2,17 @@ import React, { useContext } from 'react'
import '../remix-ai.css'
import { Default, ChatApi } from './Default'
export const RemixAITab = (props) => {
interface IRemixAITab {
plugin: any,
}
export const RemixAITab = (props: IRemixAITab) => {
const plugin = props.plugin
return (
<>
<div id="remixAITab" className="px-2 pb-4">
<Default plugin={plugin}></Default>
<div id="remixAITab" className="mx-2 h-100 w-100 px-0">
<div className='h-100'>
<Default plugin={props.plugin}></Default>
</div>
</div>
</>
)

@ -1,9 +1,37 @@
.nlux-theme-remix_ai_theme[data-color-scheme='light'] {
--nlux-ChatRoom--BackgroundColor: var(--text-background);
padding-bottom: 0.1rem;
}
.nlux-theme-remix_ai_theme[data-color-scheme='dark'] {
--nlux-ChatRoom--BackgroundColor: var(--text-background);
padding-bottom: 0.1rem;
}
.nlux-launchPad-container {
padding-bottom: 2rem;
}
.nlux-conversationStarters-container {
overflow-y: hidden;
}
.nlux-comp-composer {
line-height: normal;
}
.nlux-comp-composer>textarea {
padding: 10px !important
}
.nlux-comp-conversationStarters {
width: auto !important;
padding: 0px !important
}
.nlux-conversationStarters-container>.nlux-comp-conversationStarters>.nlux-comp-conversationStarter {
border-width: 1px !important;
border-color: var(--text-background) !important;
}
.nlux-theme-remix_ai_theme {
@ -35,10 +63,11 @@
--nlux-PromptInput-Focus-Outline--Width: 10px;
--nlux-PromptInput-Max-Height: 50px;
--nlux-PromptInput--BorderWidth: 0;
.nlux-comp-composer > textarea {padding: 8px;}
--nlux-PromptInput--BorderRadius: 10px 0 0 10px;
--nlux-PromptInput-Height: 50px;
--nlux-PromptInput-Height: 30px;
--nlux-cvStrt--brdrWd: 0;
--nlux-cvStrt--brdClr: var(--light);
--nlux-cvStrt--brdrWd: 'hidden';
/* Override input colors */
--nlux-PromptInput--BackgroundColor: var(--light);
@ -52,7 +81,7 @@
--nlux-SubmitButton--BackgroundColor: var(--primary);
--nlux-SubmitButton-Active--BackgroundColor:var(--primary);
--nlux-SubmitButton-Disabled--BackgroundColor: var(--dark);
--nlux-SubmitButton-Active--TextColor: var(--text);
--nlux-SubmitButton-Active--TextColor: var(--white);
--nlux-SubmitButton-Disabled--TextColor: var(--text);
/** Inline code in markdown */
@ -61,7 +90,6 @@
--nlux-InlineCode--Padding: 0 2px;
--nlux-InlineCode--FontSize: 14px;
/*code block */
--nlux-CodeBlock-CopyButton--BackgroundColor: var(--bg-text);
--nlux-CodeBlock-CopyButton--TextColor: var(--text);
@ -86,4 +114,6 @@
/* Override icon for the send button */
--nlux-send-icon: url('data:image/svg+xml, <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM297 385c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l71-71L120 280c-13.3 0-24-10.7-24-24s10.7-24 24-24l214.1 0-71-71c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0L409 239c9.4 9.4 9.4 24.6 0 33.9L297 385z"/></svg>');
--nlux-comp-composer > textarea {padding: 10px;};
}

@ -1,8 +1,8 @@
import { PersonaOptions, UserPersona } from '@nlux/react';
export const user: UserPersona = {
name: 'Pipper',
name: 'Remi',
avatar: 'assets/img/remix-logo-blue.png'
};
export const assistantAvatar = 'assets/img/remi-prof.webp';
export const assistantAvatar = 'assets/img/aiLogo.svg'

@ -0,0 +1,265 @@
export type ChainInfo = {
id: number | string
name: string
}
export type ChainCompatibleInfo = {
chain: ChainInfo
minCompilerVersion: string
evmVersion: HardFork
}
export type HardFork =
| 'cancun'
| 'shanghai'
| 'paris'
| 'london'
| 'berlin'
| 'istanbul'
| 'petersburg'
| 'constantinople'
| 'byzantium'
| 'spuriousDragon'
| 'tangerineWhistle'
| 'homestead'
export const evmMap: Map<HardFork, { chainId: ChainInfo[], minCompilerVersion: string }> = new Map([
['cancun', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 100, name: "Gnosis Chain" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 300, name: "zkSync Era Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 43114, name: "Avalanche C-Chain" },
{ id: 44787, name: "Celo Alfajores Testnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 421614, name: "Arbitrum Sepolia" },
{ id: 534352, name: "Scroll" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.8.24+commit.e11b9ed9",
evmVersion: 'cancun'
}],
['shanghai', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 30, name: "Rootstock Mainnet" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 100, name: "Gnosis Chain" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 300, name: "zkSync Era Mainnet" },
{ id: 302, name: "zkSync Era Testnet" },
{ id: 314, name: "Filecoin - Mainnet" },
{ id: 324, name: "zkSync Era Mainnet" },
{ id: 369, name: "PulseChain" },
{ id: 388, name: "HALO Mainnet" },
{ id: 1101, name: "Polygon zkEVM" },
{ id: 1088, name: "Metis Andromeda Mainnet" },
{ id: 1284, name: "Moonbeam" },
{ id: 2000, name: "Dogechain Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 44787, name: "Celo Alfajores Testnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 59902, name: "Metis Sepolia Testnet" },
{ id: 421614, name: "Arbitrum Sepolia" },
{ id: 534352, name: "Scroll" },
{ id: 11155111, name: "Sepolia" },
{ id: 11155420, name: "Optimism Sepolia Testnet" },
{ id: 1666600000, name: "Harmony Mainnet Shard 0" }
],
minCompilerVersion: "0.8.20+commit.a1b79de6",
evmVersion: 'shanghai'
}],
['paris', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 30, name: "Rootstock Mainnet" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 100, name: "Gnosis Chain" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 300, name: "zkSync Era Mainnet" },
{ id: 302, name: "zkSync Era Testnet" },
{ id: 314, name: "Filecoin - Mainnet" },
{ id: 324, name: "zkSync Era Mainnet" },
{ id: 369, name: "PulseChain" },
{ id: 388, name: "HALO Mainnet" },
{ id: 1088, name: "Metis Andromeda Mainnet" },
{ id: 1101, name: "Polygon zkEVM" },
{ id: 1284, name: "Moonbeam" },
{ id: 2000, name: "Dogechain Mainnet" },
{ id: 42220, name: "Celo Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 44787, name: "Celo Alfajores Testnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 59902, name: "Metis Sepolia Testnet" },
{ id: 421614, name: "Arbitrum Sepolia" },
{ id: 534352, name: "Scroll" },
{ id: 11155111, name: "Sepolia" },
{ id: 11155420, name: "Optimism Sepolia Testnet" },
{ id: 1666600000, name: "Harmony Mainnet Shard 0" }
],
minCompilerVersion: "0.8.18+commit.87f61d96",
evmVersion: 'paris'
}],
['london', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 25, name: "Cronos Mainnet" },
{ id: 30, name: "Rootstock Mainnet" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 1280, name: "HALO Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 42220, name: "Celo Mainnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 11155111, name: "Sepolia" },
],
minCompilerVersion: "0.8.7+commit.e28d00a7",
evmVersion: 'london'
}],
['berlin', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 25, name: "Cronos Mainnet" },
{ id: 30, name: "Rootstock Mainnet" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 1280, name: "HALO Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 42220, name: "Celo Mainnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.8.5+commit.a4f2e591",
evmVersion: 'berlin'
}],
['istanbul', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 10, name: "Optimism" },
{ id: 25, name: "Cronos Mainnet" },
{ id: 30, name: "Rootstock Mainnet" },
{ id: 56, name: "BNB Smart Chain Mainnet" },
{ id: 137, name: "Polygon Mainnet" },
{ id: 250, name: "Fantom Opera" },
{ id: 1280, name: "HALO Mainnet" },
{ id: 42161, name: "Arbitrum One" },
{ id: 42170, name: "Arbitrum Nova" },
{ id: 42220, name: "Celo Mainnet" },
{ id: 59144, name: "Linea Mainnet" },
{ id: 59141, name: "Linea Testnet" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.5.14+commit.01f1aaa4",
evmVersion: 'istanbul'
}],
['petersburg', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.5.5+commit.47a71e8f",
evmVersion: 'petersburg'
}],
['constantinople', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.5.5+commit.47a71e8f",
evmVersion: 'constantinople'
}],
['byzantium', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" }
],
minCompilerVersion: "0.4.21+commit.dfe3193c",
evmVersion: 'byzantium'
}],
['spuriousDragon', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" }
],
minCompilerVersion: "0.4.9+commit.364da425",
evmVersion: 'spuriousDragon'
}],
['tangerineWhistle', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" }
],
minCompilerVersion: "0.4.0+commit.acd334c9",
evmVersion: 'tangerineWhistle'
}],
['homestead', {
chainId: [
{ id: 1, name: "Ethereum Mainnet" },
{ id: 5, name: "Goerli" },
{ id: 11155111, name: "Sepolia" }
],
minCompilerVersion: "0.1.2+commit.d0d36e3",
evmVersion: 'homestead'
}],
])
export function getCompatibleChains(fork: HardFork): ChainInfo[] {
const forkData = evmMap.get(fork)
return forkData ? forkData.chainId : []
}
export function isChainCompatible(fork: HardFork, chainId: number): boolean {
const compatibleChains = getCompatibleChains(fork)
return compatibleChains.some(chain => chain.id === chainId)
}
export function isChainCompatibleWithAnyFork(chainId: number, forks: HardFork[]): boolean {
return forks.some(fork => isChainCompatible(fork, chainId))
}
export function getCompatibleChain(
fork: HardFork,
chainId: number
): ChainCompatibleInfo | undefined {
for (const [forkKey, forkData] of evmMap) {
const compatibleChain = forkData.chainId.find(chain => chain.id === chainId)
if (compatibleChain) {
return {
chain: compatibleChain,
minCompilerVersion: forkData.minCompilerVersion,
evmVersion: forkKey
}
}
}
return undefined;
}

@ -462,6 +462,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
<div>
<ContractGUI
title={intl.formatMessage({ id: 'udapp.deploy' })}
getCompilerDetails={props.getCompilerDetails}
isDeploy={true}
deployOption={deployOptions[currentFile] && deployOptions[currentFile][currentContract] ? deployOptions[currentFile][currentContract].options : null}
initializerOptions={
@ -481,6 +482,10 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
solcVersion={props.solCompilerVersion}
setSolcVersion={props.setCompilerVersion}
getVersion={props.getCompilerVersion}
evmCheckComplete={props.evmCheckComplete}
setEvmCheckComplete={props.setEvmCheckComplete}
plugin={props.plugin}
runTabState={props.runTabState}
/>
<div className="d-flex py-1 align-items-center custom-control custom-checkbox">
<input

@ -6,6 +6,7 @@ import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard'
import { CustomTooltip, ProxyAddressToggle, ProxyDropdownMenu, shortenDate, shortenProxyAddress, unavailableProxyLayoutMsg, upgradeReportMsg } from '@remix-ui/helper'
import { Dropdown } from 'react-bootstrap'
import { getCompatibleChains, isChainCompatible, isChainCompatibleWithAnyFork } from '../actions/evmmap'
const txFormat = remixLib.execution.txFormat
const txHelper = remixLib.execution.txHelper
@ -171,8 +172,7 @@ export function ContractGUI(props: ContractGUIProps) {
}
}
const handleActionClick = async () => {
props.getVersion()
const handleDeploy = async () => {
if (deployState.deploy) {
const proxyInitializeString = getMultiValsString(initializeFields.current)
props.clickCallBack(props.initializerOptions.inputs.inputs, proxyInitializeString, ['Deploy with Proxy'])
@ -224,6 +224,23 @@ export function ContractGUI(props: ContractGUIProps) {
}
}
const handleActionClick = async () => {
props.getVersion()
if (props.runTabState.selectExEnv.toLowerCase().startsWith('vm-') || props.runTabState.selectExEnv.toLowerCase().includes('basic-http-provider')) {
await handleDeploy()
} else {
const status = await props.getCompilerDetails()
if (status === 'Failed') {
props.plugin.call('terminal', 'log', { type: 'log', value: 'Consider opening an issue to update our internal store with your desired chainId.' })
return
}
if (props.evmCheckComplete) {
await handleDeploy()
}
}
}
const handleBasicInput = (e) => {
const value = e.target.value

@ -1,5 +1,5 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import React, { useEffect } from 'react'
import { FormattedMessage } from 'react-intl'
import { EnvironmentProps, Provider } from '../types'
import { Dropdown } from 'react-bootstrap'
@ -66,7 +66,7 @@ export function EnvironmentUI(props: EnvironmentProps) {
{ (props.providers.providerList.filter((provider) => { return provider.isInjected })).map(({ name, displayName }) => (
<Dropdown.Item
key={name}
onClick={() => {
onClick={async () => {
handleChangeExEnv(name)
}}
data-id={`dropdown-item-${name}`}

@ -62,6 +62,8 @@ export function InstanceContainerUI(props: InstanceContainerProps) {
editInstance={props.editInstance}
solcVersion={props.solcVersion}
getVersion={props.getVersion}
getCompilerDetails={props.getCompilerDetails}
runTabState={props.runTabState}
/>
)
})}

@ -1,5 +1,5 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import React, { useEffect } from 'react'
import { SettingsProps } from '../types'
import { EnvironmentUI } from './environment'
import { NetworkUI } from './network'
@ -12,7 +12,7 @@ export function SettingsUI(props: SettingsProps) {
return (
<div className="udapp_settings">
<EnvironmentUI selectedEnv={props.selectExEnv} providers={props.providers} setExecutionContext={props.setExecutionContext} />
<EnvironmentUI runTabPlugin={props.runTabPlugin} selectedEnv={props.selectExEnv} providers={props.providers} setExecutionContext={props.setExecutionContext} checkSelectionCorrectness={props.EvaluateEnvironmentSelection} />
<NetworkUI networkName={props.networkName} />
<AccountUI
addFile={props.addFile}

@ -287,12 +287,12 @@ export function UniversalDappUI(props: UdappProps) {
<b><FormattedMessage id="udapp.balance" />:</b> {instanceBalance} ETH
</span>
<div></div>
<div className="d-flex align-self-center">
<div className="btn d-flex p-0 align-self-center">
{props.exEnvironment && props.exEnvironment.startsWith('injected') && (
<CustomTooltip placement="top" tooltipClasses="text-nowrap" tooltipId="udapp_udappEditTooltip" tooltipText={<FormattedMessage id="udapp.tooltipTextEdit" />}>
<i
data-id="instanceEditIcon"
className="fas fa-edit pr-3"
className="fas fa-sparkles"
onClick={() => {
props.editInstance(props.instance)
}}
@ -326,6 +326,10 @@ export function UniversalDappUI(props: UdappProps) {
<div key={index}>
<ContractGUI
getVersion={props.getVersion}
getCompilerDetails={props.getCompilerDetails}
evmCheckComplete={false}
plugin={props.plugin}
runTabState={props.runTabState}
funcABI={funcABI}
clickCallBack={(valArray: {name: string; type: string}[], inputsValues: string) => {
runTransaction(lookupOnly, funcABI, valArray, inputsValues, index)

@ -1,5 +1,5 @@
// eslint-disable-next-line no-use-before-define
import React, { Fragment, useEffect, useReducer, useState } from 'react'
import React, { Fragment, useCallback, useEffect, useReducer, useState } from 'react'
import semver from 'semver'
import { FormattedMessage } from 'react-intl'
import { ModalDialog } from '@remix-ui/modal-dialog'
@ -56,6 +56,9 @@ import { PassphrasePrompt } from './components/passphrase'
import { MainnetPrompt } from './components/mainnet'
import { ScenarioPrompt } from './components/scenario'
import { setIpfsCheckedState, setRemixDActivated } from './actions/payload'
import { ChainCompatibleInfo, getCompatibleChain, getCompatibleChains, HardFork, isChainCompatible, isChainCompatibleWithAnyFork } from './actions/evmmap'
export type CheckStatus = 'Passed' | 'Failed'
export function RunTabUI(props: RunTabProps) {
const { plugin } = props
@ -85,6 +88,7 @@ export function RunTabUI(props: RunTabProps) {
const REACT_API = { runTab }
const currentfile = plugin.config.get('currentFile')
const [solcVersion, setSolcVersion] = useState<{version: string, canReceive: boolean}>({ version: '', canReceive: true })
const [evmCheckComplete, setEvmCheckComplete] = useState(false)
const getVersion = () => {
let version = '0.8.25'
@ -102,6 +106,67 @@ export function RunTabUI(props: RunTabProps) {
}
}
const getCompilerDetails = async () => await checkEvmChainCompatibility()
const returnCompatibleChain = async (evmVersion: HardFork, targetChainId: number) => {
const result = getCompatibleChain(evmVersion ?? 'paris', targetChainId) // using paris evm as a default fallback version
return result
}
const checkEvmChainCompatibilityOkFunction = async (fetchDetails: ChainCompatibleInfo) => {
const compilerParams = {
evmVersion: fetchDetails.evmVersion,
optimize: false,
language: 'Solidity',
runs: '200',
version: fetchDetails.minCompilerVersion
}
await plugin.call('solidity', 'setCompilerConfig', compilerParams)
const currentFile = await plugin.call('fileManager', 'getCurrentFile')
await plugin.call('solidity', 'compile', currentFile)
setEvmCheckComplete(true)
}
const checkEvmChainCompatibility = async () => {
const fetchDetails = await plugin.call('solidity', 'getCompilerQueryParameters')
const compilerState = await plugin.call('solidity', 'getCompilerState')
// if no contract file is open, don't do anything
if (compilerState.target !== null) {
const targetChainId = runTab.chainId
const ideDefault = fetchDetails && fetchDetails.evmVersion !== null ? fetchDetails.evmVersion : 'cancun'
const IsCompatible = isChainCompatible(ideDefault, targetChainId)
const chain = await returnCompatibleChain(ideDefault, targetChainId)
if (chain === undefined) {
//show modal
plugin.call('terminal', 'log', { type: 'log', value: 'No compatible chain found for the selected EVM version.' })
return 'Failed'
} else {
if (!IsCompatible) {
//show modal
plugin.call('notification', 'modal', {
id: 'evm-chainId-incompatible',
title: 'Incompatible EVM for the selected chain',
message: <div className="px-3">
<p>The smart contract has not been compiled with an EVM version that is compatible with the selected chain.</p>
<ul className="px-3">
<li>Have Remix switch to a compatible EVM version for this chain and recompile the contract.</li>
<li>Cancel to keep the current EVM version.</li>
</ul>
<p>To manually change the EVM version, go to the Advanced Configurations section of the Solidity compiler.</p>
</div>,
modalType: 'modal',
okLabel: 'Switch EVM and Recompile',
cancelLabel: 'Cancel',
okFn: () => checkEvmChainCompatibilityOkFunction(chain),
cancelFn: () => {}
})
}
}
return 'Passed'
}
}
useEffect(() => {
if (!props.initialState) {
initRunTab(plugin, true)(dispatch)
@ -285,6 +350,7 @@ export function RunTabUI(props: RunTabProps) {
networkName={runTab.networkName}
personalMode={runTab.personalMode}
selectExEnv={runTab.selectExEnv}
EvaluateEnvironmentSelection={checkEvmChainCompatibility}
accounts={runTab.accounts}
setAccount={setAccountAddress}
setUnit={setUnitValue}
@ -294,6 +360,7 @@ export function RunTabUI(props: RunTabProps) {
gasLimit={runTab.gasLimit}
setGasFee={setGasFeeAmount}
providers={runTab.providers}
runTabPlugin={plugin}
setExecutionContext={setExecutionEnvironment}
createNewBlockchainAccount={createNewAddress}
setPassphrase={setPassphraseModal}
@ -331,6 +398,11 @@ export function RunTabUI(props: RunTabProps) {
solCompilerVersion={solcVersion}
setCompilerVersion={setSolcVersion}
getCompilerVersion={getVersion}
getCompilerDetails={getCompilerDetails}
evmCheckComplete={evmCheckComplete}
setEvmCheckComplete={setEvmCheckComplete}
plugin={plugin}
runTabState={runTab}
/>
<RecorderUI
plugin={plugin}
@ -345,6 +417,8 @@ export function RunTabUI(props: RunTabProps) {
/>
<InstanceContainerUI
plugin={plugin}
getCompilerDetails={getCompilerDetails}
runTabState={runTab}
instances={runTab.instances}
clearInstances={removeInstances}
unpinInstance={unpinPinnedInstance}

@ -1,9 +1,10 @@
import { Ref } from 'react'
import { Dispatch, Ref } from 'react'
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { ContractData, FuncABI, OverSizeLimit } from '@remix-project/core-plugin'
import { RunTab } from './run-tab'
import { SolcInput, SolcOutput } from '@openzeppelin/upgrades-core'
import { LayoutCompatibilityReport } from '@openzeppelin/upgrades-core/dist/storage/report'
import { CheckStatus } from '../run-tab'
export interface RunTabProps {
plugin: RunTab,
initialState?: RunTabState
@ -123,7 +124,9 @@ export interface RunTabState {
}
export interface SettingsProps {
runTabPlugin: RunTab,
selectExEnv: string,
EvaluateEnvironmentSelection: any
accounts: {
loadedAccounts: Record<string, any>,
selectedAccount: string,
@ -158,6 +161,8 @@ export interface SettingsProps {
}
export interface EnvironmentProps {
checkSelectionCorrectness: any
runTabPlugin: RunTab,
selectedEnv: string,
providers: {
providerList: Provider[],
@ -228,6 +233,11 @@ export type MainnetPrompt = (
) => JSX.Element
export interface ContractDropdownProps {
getCompilerDetails: () => Promise<CheckStatus>
evmCheckComplete?: boolean,
setEvmCheckComplete?: Dispatch<React.SetStateAction<boolean>>,
plugin: RunTab,
runTabState: RunTabState
selectedAccount: string,
exEnvironment: string,
contracts: {
@ -292,6 +302,8 @@ export interface RecorderProps {
}
export interface InstanceContainerProps {
getCompilerDetails: () => Promise<CheckStatus>
runTabState: RunTabState
instances: {
instanceList: {
contractData?: ContractData,
@ -373,6 +385,11 @@ export interface DeployOptions {
}
export interface ContractGUIProps {
getCompilerDetails: () => Promise<CheckStatus>
evmCheckComplete?: boolean,
setEvmCheckComplete?: React.Dispatch<React.SetStateAction<boolean>>,
plugin: RunTab,
runTabState: RunTabState
title?: string,
funcABI: FuncABI,
inputs: string,
@ -412,6 +429,8 @@ export interface MainnetProps {
}
export interface UdappProps {
getCompilerDetails: () => Promise<CheckStatus>
runTabState: RunTabState
instance: {
contractData?: ContractData,
address: string,

@ -473,7 +473,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
<span
data-id="remix_ai_docs"
id="remix_ai_docs"
className="btn pl-2 pr-0 py-0 d-inline ai-docs"
className="btn pl-2 pr-0 py-0 d-inline ai-docs text-dark"
role='link'
onClick={()=>{
window.open("https://remix-ide.readthedocs.io/en/latest/ai.html")

@ -974,7 +974,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
>
{compileTabLogic.evmVersions.map((version, index) => (
<option key={index} data-id={state.evmVersion === version ? 'selected' : ''} value={version}>
{version}
{version === 'default' ? `default (${compileTabLogic.evmVersions[index + 1]})` : version}
</option>
))}
</select>

@ -1,4 +1,6 @@
.remixui_statusbar {
white-space: nowrap;
}
.remixui_statusbar_gitstatus
.remixui_statusbar_gitstatus:hover {
cursor: pointer;

@ -1,7 +1,8 @@
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { StatusBar } from 'apps/remix-ide/src/app/components/status-bar'
import { CustomTooltip } from '@remix-ui/helper'
import React, { useEffect, useState } from 'react'
import React, { useContext, useEffect, useState } from 'react'
import { appActionTypes, AppContext } from '@remix-ui/app'
interface AIStatusProps {
plugin: StatusBar
@ -12,12 +13,15 @@ interface AIStatusProps {
export default function AIStatus(props: AIStatusProps) {
const [copilotActive, setCopilotActive] = useState(false)
const appContext = useContext(AppContext)
useEffect(() => {
const run = async () => {
const aiActivate = await props.plugin.call('settings', 'get', 'settings/copilot/suggest/activate')
setCopilotActive(aiActivate)
}
run()
}, [])
useEffect(() => {
@ -29,15 +33,70 @@ export default function AIStatus(props: AIStatusProps) {
}
run()
}, [props.plugin.isAiActive, props.plugin.isAiActive])
return (
<div>
<CustomTooltip
tooltipText={copilotActive ? "RemixAI Copilot enabled" : "RemixAI Copilot disabled. To enable, open a .sol file and toggle the switch at the left-top of the editor"}
tooltipText={copilotActive ? "Disable RemixAI Copilot" : "Enable RemixAI Copilot. Switch to .sol file to try it."}
>
<span
style={{ cursor: 'pointer' }}
className={"small mx-1 bg-info border-0 text-white " + (copilotActive === false ? "semi-bold" : "")}
onClick={async () => {
await props.plugin.call('settings' as any, 'updateCopilotChoice', !copilotActive)
}}
>
<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 ? 'RemixAI Copilot (disabled)' : 'RemixAI Copilot (enabled)'}
</span>
</div>
</CustomTooltip>
<div className="d-flex text-sm flex-row pr-2 text-white justify-content-center align-items-center">
<style>{`
button.aiButton:focus {
outline: none;
box-shadow: none;
}
button.aiButton:hover {
border-color: var(--info)
}
`}</style>
{ !appContext.appState.showPopupPanel && <div className='d-flex flex-column' style={{
position: 'absolute',
bottom: '1.5rem',
right: '0.5rem',
color: 'var(--ai)',
alignItems: 'self-end',
}}>
<span className='p-1 text-info alert alert-secondary' style={{
boxShadow: "0 1px 7px var(--secondary)",
zIndex: '200',
marginRight: '1.8rem',
marginBottom: '-7px'
}}>
👋 I'm here to help you!
</span>
<button
style={{
backgroundColor: 'var(--brand-dark-blue)',
height: '3rem',
width: '3rem',
borderRadius: '50%',
color: 'var(--ai)',
boxShadow: "0 1px 7px var(--secondary)"
}}
data-id="aiStatusButton"
className='aiButton d-flex align-items-center h3 p-1'
onClick={async () => {
appContext.appStateDispatch({
type: appActionTypes.setShowPopupPanel,
payload: !appContext.appState.showPopupPanel
})
}}
>
<img className="align-self-start" src="assets/img/aiLogoHead.webp" alt="" style={{ height: "2rem" }}></img>
</button>
</div>
}
</div>
</div>
)
}

@ -15,7 +15,7 @@ export default function ScamAlertStatus ({ refs, getReferenceProps }: ScamAlertS
<CustomTooltip
tooltipText={"Scam Alerts"}
>
<div className="mr-1 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" data-id="hTScamAlertButton" 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" />

@ -18,13 +18,22 @@ export default function ScamDetails ({ refs, floatStyle, scamAlerts }: ScamDetai
return (
<div
ref={refs.setFloating}
style={ floatStyle }
className="px-1 ml-1 mb-1 d-flex w-25 alert alert-warning border border-warning"
id='scamDetails'
style={{
position: 'absolute',
bottom: '-3.3rem',
left: '-4rem',
height: '6rem',
transform: 'translate(88.5px, -80px)',
willChange: 'transform',
boxShadow: "0 1px 7px var(--secondary)"
} }
className="p-1 pb-0 mb-1 d-flex 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 fa-exclamation-triangle"></i>
</span>
<div className="d-flex flex-column">
<div className="d-flex flex-column pr-4 pt-2">
{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} />

@ -72,25 +72,37 @@ export function RemixUIStatusBar({ statusBarPlugin }: RemixUIStatusBarProps) {
<StatusBarContextProvider>
{(platform !== appPlatformTypes.desktop) && showScamDetails && (
<FloatingFocusManager context={context} modal={false}>
<ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} />
<ScamDetails
refs={refs}
floatStyle={{
...floatingStyles,
minHeight: 'auto',
alignContent: 'center',
paddingRight: '0.5rem',
bottom: '-8,5rem',
left: '0rem'
}}
getFloatingProps={getFloatingProps}
scamAlerts={scamAlerts}
/>
</FloatingFocusManager>
)}
<div className="d-flex remixui_statusbar_height flex-row bg-info justify-content-between align-items-center">
{ (platform !== appPlatformTypes.desktop) && <div className="remixui_statusbar bg-warning px-2 remixui_statusbar_custom_padding d-flex justify-center align-items-center">
<ScamAlertStatus refs={refs} getReferenceProps={getReferenceProps} />
</div> }
<div className='d-flex w-100 justify-content-between'>
<div className="remixui_statusbar remixui_statusbar_gitstatus">
<GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} />
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar">
</div>
<div className="w-100 remixui_statusbar">
<DidYouKnow />
</div>
<div className="remixui_statusbar"></div>
<div className="remixui_statusbar d-flex align-items-center p-0">
<div className="remixui_statusbar d-flex w-100 justify-content-end p-0">
<div className="remixui_statusbar">
<AIStatus plugin={statusBarPlugin} aiActive={lightAiUp} isAiActive={isAiActive} setIsAiActive={setIsAiActive} />
</div>
{ (platform !== appPlatformTypes.desktop) && <div className="remixui_statusbar bg-warning px-2 remixui_statusbar_custom_padding d-flex justify-center align-items-center">
<ScamAlertStatus refs={refs} getReferenceProps={getReferenceProps} />
</div> }
</div>
</div>
</StatusBarContextProvider>

Loading…
Cancel
Save