Merge pull request #4568 from ethereum/aiui

copilot ui and sync with settings
clean_user_consent
STetsing 8 months ago committed by GitHub
commit f21beafb38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts
  2. 16
      apps/remix-ide/src/app/plugins/copilot/suggestion-service/suggestion-service.ts
  3. 1
      apps/remix-ide/src/app/plugins/copilot/suggestion-service/worker.js
  4. 2
      apps/remix-ide/src/app/plugins/openaigpt.tsx
  5. 6
      apps/remix-ide/src/app/plugins/solcoderAI.tsx
  6. 8
      apps/remix-ide/src/app/tabs/locales/en/remixUiTabs.json
  7. 6
      apps/remix-ide/src/app/tabs/settings-tab.tsx
  8. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  9. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  10. 6
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  11. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  12. 4
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  13. 5
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  14. 4
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  15. 4
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  16. 5
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  17. 5
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  18. 5
      apps/remix-ide/src/assets/css/themes/remix-unicorn.css
  19. 5
      apps/remix-ide/src/assets/css/themes/remix-violet.css
  20. 6
      apps/remixdesktop/src/menus/view.ts
  21. 8355
      diff.diff
  22. 15
      libs/remix-ui/renderer/src/lib/renderer.tsx
  23. 91
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  24. 23
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.css
  25. 197
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  26. 2
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  27. 14
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx

@ -20,8 +20,11 @@ export class CopilotSuggestion extends Plugin {
ready: boolean
constructor() {
super(profile)
this.service = new SuggestionService()
this.context = ''
}
onActivation(): void {
this.service = new SuggestionService()
this.service.events.on('progress', (data) => {
this.emit('loading', data)
})
@ -29,7 +32,7 @@ export class CopilotSuggestion extends Plugin {
})
this.service.events.on('ready', (data) => {
this.ready = true
})
})
}
useRemoteService(service: string) {

@ -1,11 +1,13 @@
import EventEmitter from 'events'
export type SuggestOptions = { max_new_tokens: number,
temperature: number,
do_sample:boolean
top_k: number,
top_p:number,
stream_result:boolean}
export type SuggestOptions = {
max_new_tokens: number,
temperature: number,
do_sample:boolean
top_k: number,
top_p:number,
stream_result:boolean
}
export class SuggestionService {
worker: Worker
@ -17,12 +19,12 @@ export class SuggestionService {
this.worker = new Worker(new URL('./worker.js', import.meta.url), {
type: 'module'
});
this.init()
this.events = new EventEmitter()
this.responses = {}
this.current
}
//todo ask Yann if we should keep the model
terminate(): void {
this.worker.terminate()
this.worker = new Worker(new URL('./worker.js', import.meta.url), {

@ -22,6 +22,7 @@ class CodeCompletionPipeline {
// Listen for messages from the main thread
self.addEventListener('message', async (event) => {
console.log("worker message ", event.data)
const {
id, model, text, max_new_tokens, cmd,

@ -19,7 +19,7 @@ export class OpenAIGpt extends Plugin {
async message(prompt): Promise<CreateChatCompletionResponse> {
this.call('layout', 'maximizeTerminal')
this.call('terminal', 'log', 'Waiting for GPT answer...')
this.call('terminal', 'log', { type: 'typewriterwarning', value: 'Waiting for GPT answer...'})
let result
try {
result = await (

@ -22,7 +22,7 @@ export class SolCoder extends Plugin {
async code_generation(prompt): Promise<any> {
this.emit("aiInfering")
this.call('layout', 'maximizeTerminal')
this.call('terminal', 'log', 'Waiting for Solcoder answer...')
this.call('terminal', 'log', { type: 'typewriterwarning', value: 'Waiting for Solcoder answer...'})
let result
try {
result = await(
@ -47,7 +47,7 @@ export class SolCoder extends Plugin {
async solidity_answer(prompt): Promise<any> {
this.emit("aiInfering")
this.call('layout', 'maximizeTerminal')
this.call('terminal', 'log', 'Waiting for Solcoder answer...')
this.call('terminal', 'log', { type: 'typewriterwarning', value: 'Waiting for Solcoder answer...'})
let result
try {
result = await(
@ -77,7 +77,7 @@ export class SolCoder extends Plugin {
async code_explaining(prompt): Promise<any> {
this.emit("aiInfering")
this.call('layout', 'maximizeTerminal')
this.call('terminal', 'log', 'Waiting for Solcoder answer...')
this.call('terminal', 'log', { type: 'typewriterwarning', value: 'Waiting for Solcoder answer...'})
let result
try {
result = await(

@ -2,10 +2,10 @@
"remixUiTabs.tooltipText1": "Run script (CTRL + SHIFT + S)",
"remixUiTabs.tooltipText2": "Compile CTRL + S",
"remixUiTabs.tooltipText3": "Select .sol or .yul file to compile or a .ts or .js file and run it",
"remixUiTabs.tooltipText4": "Select .sol file to explain with AI [BETA]",
"remixUiTabs.tooltipText5": "Explain the contract/s in current file [BETA]",
"remixUiTabs.tooltipText6": "AI Copilot [BETA]",
"remixUiTabs.tooltipText3": "Select .sol, .vy or .yul file to compile or a .ts or .js file and run it",
"remixUiTabs.tooltipText4": "Select .sol file to use AI tools [BETA]",
"remixUiTabs.tooltipText5": "Explain the contract(s) in current file [BETA]",
"remixUiTabs.tooltipText6": "Enable AI Copilot [BETA]. Note that the AI model is downloaded once to your browser's memory - approximately 50MB.",
"remixUiTabs.tooltipText7": "Disable AI Copilot [BETA]",
"remixUiTabs.zoomOut": "Zoom out",
"remixUiTabs.zoomIn": "Zoom in"
}

@ -60,6 +60,12 @@ module.exports = class SettingsTab extends ViewPlugin {
this.renderComponent()
}
onActivation(): void {
this.on('copilot-suggestion', 'loading', (data) => {
this.call('terminal', 'log', { type: 'typewritererror', value: `.` })
console.log("oninit")
})
}
render() {
return (
<div id="settingsTab">

@ -10,6 +10,7 @@
* Copyright 2011-2020 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/:root {
--ai: #da2de4;
--blue:#033c73;
--indigo:#6610f2;
--purple:#6f42c1;
@ -8373,6 +8374,9 @@ a.text-danger:focus,a.text-danger:hover {
a.text-light:focus,a.text-light:hover {
color:#cbd3da!important
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color:#343a40!important
}

@ -11,6 +11,7 @@
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap);
:root {
--ai: #2de7f3;
--blue:#2a9fd6;
--indigo:#6610f2;
--purple:#6f42c1;
@ -8375,6 +8376,9 @@ a.text-danger:focus,a.text-danger:hover {
a.text-light:focus,a.text-light:hover {
color:#000!important
}
.text-dark {
color: #babbcc !important;
}
.text-dark {
color:#adafae!important
}

@ -10,7 +10,8 @@
* Copyright 2011-2020 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/@import url(https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400&display=swap);:root {
--blue:#2c3e50;
--ai: #da2de4;
--blue:#2c3e50;
--indigo:#6610f2;
--purple:#6f42c1;
--pink:#e83e8c;
@ -7004,6 +7005,9 @@ a.text-danger:focus,a.text-danger:hover {
a.text-light:focus,a.text-light:hover {
color:#c0cdd1!important
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color:#7b8a8b!important
}

@ -11,6 +11,7 @@
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/@import url(https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap);
:root {
--ai: #da2de4;
--blue:#446e9b;
--indigo:#6610f2;
--purple:#6f42c1;
@ -8375,6 +8376,9 @@ a.text-danger:focus,a.text-danger:hover {
a.text-light:focus,a.text-light:hover {
color:#c8c8c8!important
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color:#333!important
}

@ -1,5 +1,6 @@
@import url('https://fonts.googleapis.com/css?family=Nunito+Sans:400,600&display=swap');
:root {
--ai: #2de7f3;
--blue: #90c3f6;
--indigo: #6610f2;
--purple: #9e77f6;
@ -8590,6 +8591,9 @@ a.text-light:hover {
.text-dark {
color: #babbcc !important;
}
.text-ai {
color: #2de7f3 !important;
}
a.text-dark:focus,
a.text-dark:hover {
color: #6f7087 !important;

@ -1,4 +1,5 @@
:root {
--ai: #da2de4;
--blue: #007bff;
--indigo: #6610f2;
--purple: #6f42c1;
@ -9364,7 +9365,9 @@ a.text-light:hover,
a.text-light:focus {
color: #d9d9d9 !important;
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color: #11556c !important;
}

@ -1,4 +1,5 @@
:root {
--ai: #2de7f3;
--blue: #007aa6;
--indigo: #6610f2;
--purple: #9e77f6;
@ -8570,6 +8571,9 @@ a.text-info:hover {
.text-warning {
color: #c97539 !important;
}
.text-ai {
color: #2de7f3 !important;
}
a.text-warning:focus,
a.text-warning:hover {
color: #8f5227 !important;

@ -1,6 +1,7 @@
@import url('https://fonts.googleapis.com/css2?family=Saira:ital,wght@0,300;0,400;0,500;1,300;1,400&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&display=swap');
:root {
--ai: #2de7f3;
--blue: #2cc1f7;
--indigo: #6610f2;
--purple: #6f42c1;
@ -8604,6 +8605,9 @@ a.text-light:hover {
.text-dark {
color: #babbcc !important;
}
.text-ai {
color: #2de7f3 !important;
}
a.text-dark:focus,
a.text-dark:hover {
color: #6f7087 !important;

@ -1,4 +1,5 @@
:root {
--ai: #da2de4;
--blue: #007bff;
--indigo: #6610f2;
--purple: #7c47b9;
@ -9360,7 +9361,9 @@ a.text-light:hover,
a.text-light:focus {
color: #d9d9d9 !important;
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color: #747B90 !important;
}

@ -1,4 +1,5 @@
:root {
--ai: #da2de4;
--blue: #007bff;
--indigo: #6610f2;
--purple: #6f42c1;
@ -9366,7 +9367,9 @@ a.text-light:hover,
a.text-light:focus {
color: #d9d9d9 !important;
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color: #11556c !important;
}

@ -1,4 +1,5 @@
:root {
--ai: #da2de4;
--blue: #007bff;
--indigo: #6610f2;
--purple: #7c47b9;
@ -9360,7 +9361,9 @@ a.text-light:hover,
a.text-light:focus {
color: #d9d9d9 !important;
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color: #747B90 !important;
}

@ -1,4 +1,5 @@
:root {
--ai: #da2de4;
--blue: #007bff;
--indigo: #6610f2;
--purple: #7c47b9;
@ -9356,7 +9357,9 @@ a.text-light:hover,
a.text-light:focus {
color: #d9d9d9 !important;
}
.text-ai {
color: #da2de4 !important;
}
.text-dark {
color: #747B90 !important;
}

@ -79,9 +79,9 @@ export default (
accelerator: 'CmdOrCtrl+0',
click: function(item, focusedWindow) {
if (focusedWindow)
{
focusedWindow.webContents.setZoomFactor(1)
}
{
focusedWindow.webContents.setZoomFactor(1)
}
}
},

File diff suppressed because it is too large Load Diff

@ -101,7 +101,20 @@ export const Renderer = ({message, opt = {}, plugin}: RendererProps) => {
<span className="ml-3 pt-1 py-1" >
<CopyToClipboard content={messageText} className={` p-0 m-0 far fa-copy ${classList}`} direction={'top'} />
</span>
<span className="border border-success text-success btn-sm" onClick={() => { askGtp() }}>ASK GPT</span>
<span
className="position-relative text-ai text-sm pl-0 pr-2"
style={{fontSize: "x-small", alignSelf: "end"}}
>
AI
</span>
<span
className="button border text-ai btn-sm"
onClick={() => { askGtp() }}
style={{borderColor: "var(--ai)"}}
>
ASK GPT
</span>
</div>
</div>
)}

@ -4,6 +4,12 @@ import React, {useState, useRef, useReducer, useEffect, useCallback} from 'react
import {AppModal, AlertModal, ModalTypes} from '@remix-ui/app'
import {labels, textDark, textSecondary} from './constants'
enum CONSENT {
GIVEN = 0,
NOT_GIVEN,
NOT_ASKED
}
import './remix-ui-settings.css'
import {
generateContractMetadat,
@ -56,6 +62,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const [ipfsProjectSecret, setipfsProjectSecret] = useState('')
const copilotDownload = useRef(null)
let consentGivenForAI = CONSENT.NOT_ASKED
const intl = useIntl()
const initValue = () => {
const metadataConfig = props.config.get('settings/generate-contract-metadata')
@ -72,7 +79,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
}
useEffect(() => initValue(), [resetState, props.config])
useEffect(() => initValue(), [])
useEffect(() => {
const token = props.config.get('settings/' + labels['gist'].key)
if (token) {
@ -120,8 +127,17 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
useEffect(() => {
if (props.useMatomoAnalytics !== null) useMatomoAnalytics(props.config, props.useMatomoAnalytics, dispatch)
}, [props.useMatomoAnalytics])
}, [props.useMatomoAnalytics])
useEffect(() => {
console.log("useEffect on useCopilot")
if (props.useCopilot !== null) copilotActivate(props.config, props.useCopilot, dispatch)
if (props.useCopilot) {
onchangeCopilotActivate()
}
console.log("useEffect on useCopilot finish")
}, [props.useCopilot])
const onchangeGenerateContractMetadata = (event) => {
generateContractMetadat(props.config, event.target.checked, dispatch)
}
@ -130,9 +146,10 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
textWrapEventAction(props.config, props.editor, event.target.checked, dispatch)
}
const onchangeCopilotActivate = async (event) => {
if (!event.target.checked) {
copilotActivate(props.config, event.target.checked, dispatch)
const onchangeCopilotActivate = () => {
console.log("onchangeCopilotActivate ", props.useCopilot)
if (!props.useCopilot) {
copilotActivate(props.config, props.useCopilot, dispatch)
props.plugin.call('copilot-suggestion', 'uninstall')
return
}
@ -144,41 +161,54 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
const dot = loaded.match(/(.*)\./g)
copilotDownload.current.innerText = dot ? dot[0].replace('.', '') : loaded
})
const startCopilot = async () => {
await props.plugin.call('copilot-suggestion', 'init')
props.plugin.off('copilot-suggestion', 'loading')
if (await props.plugin.call('copilot-suggestion', 'status')) {
copilotActivate(props.config, true, dispatch)
} else {
props.plugin.call('copilot-suggestion', 'uninstall')
copilotActivate(props.config, false, dispatch)
}
}
const modalActivate: AppModal = {
id: 'loadcopilotActivate',
title: 'Download Solidity copilot',
modalType: ModalTypes.default,
okLabel: 'Close',
okLabel: 'Hide',
cancelLabel: 'Cancel',
message,
okFn: async() => {
props.plugin.off('copilot-suggestion', 'loading')
if (await props.plugin.call('copilot-suggestion', 'status')) {
copilotActivate(props.config, true, dispatch)
} else {
props.plugin.call('copilot-suggestion', 'uninstall')
copilotActivate(props.config, false, dispatch)
}
consentGivenForAI = CONSENT.GIVEN
startCopilot()
},
hideFn: async () => {
consentGivenForAI = CONSENT.NOT_GIVEN
props.plugin.off('copilot-suggestion', 'loading')
if (await props.plugin.call('copilot-suggestion', 'status')) {
copilotActivate(props.config, true, dispatch)
} else {
props.plugin.call('copilot-suggestion', 'uninstall')
copilotActivate(props.config, false, dispatch)
}
// if (await props.plugin.call('copilot-suggestion', 'status')) {
// copilotActivate(props.config, true, dispatch)
// } else {
// props.plugin.call('copilot-suggestion', 'uninstall')
// copilotActivate(props.config, false, dispatch)
// }
}
}
if (consentGivenForAI === CONSENT.NOT_ASKED) {
console.log("CONSENT.NOT_ASKED modal")
props.plugin.call('notification', 'modal', modalActivate)
} else if (consentGivenForAI === CONSENT.GIVEN) {
startCopilot()
} else {
// NOT_GIVEN
}
if (await props.plugin.call('copilot-suggestion', 'status')) {
if (props.plugin.call('copilot-suggestion', 'status')) {
copilotActivate(props.config, true, dispatch)
} else {
props.plugin.call('copilot-suggestion', 'uninstall')
copilotActivate(props.config, false, dispatch)
}
props.plugin.call('copilot-suggestion', 'init')
props.plugin.call('notification', 'modal', modalActivate)
}
const onchangeCopilotMaxNewToken = (event) => {
@ -456,7 +486,7 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
copilotTemperatureValue = 0.5
}
if (isCopilotActivated) props.plugin.call('copilot-suggestion', 'init')
//if (isCopilotActivated) props.plugin.call('copilot-suggestion', 'init')
const copilotSettings = () => (
<div className="border-top">
<div className="card-body pt-3 pb-2">
@ -464,19 +494,6 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
<FormattedMessage id="settings.copilot" />
</h6>
<div className="pt-2 mb-0">
<div className="text-secondary mb-0 h6">
<div>
<div className="custom-control custom-checkbox mb-1">
<input onChange={onchangeCopilotActivate} id="copilot-activate" type="checkbox" className="custom-control-input" checked={isCopilotActivated} />
<label className={`form-check-label custom-control-label align-middle ${getTextClass('settings/copilot/suggest/activate')}`} htmlFor="copilot-activate">
<FormattedMessage id="settings.copilot.activate" />
</label>
</div>
</div>
</div>
</div>
<div className="pt-2 mb-0">
<div className="text-secondary mb-0 h6">
<div>

@ -56,3 +56,26 @@
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.loadingExplanation {
animation: fancy-spin 2000ms;
animation-iteration-count: infinite;
}
@keyframes fancy-spin {
0% {
transform: scale(1);
}
25% {
transform: scale(1);
}
50% {
transform: scale(1.3);
}
75% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}

@ -60,6 +60,7 @@ const tabsReducer = (state: ITabsState, action: ITabsAction) => {
export const TabsUI = (props: TabsUIProps) => {
const [tabsState, dispatch] = useReducer(tabsReducer, initialTabsState)
const currentIndexRef = useRef(-1)
const [explaining, setExplaining] = useState<boolean>(false)
const tabsRef = useRef({})
const tabsElement = useRef(null)
const [ai_switch, setAI_switch] = useState<boolean>(false)
@ -167,104 +168,122 @@ export const TabsUI = (props: TabsUIProps) => {
<div className="remix-ui-tabs d-flex justify-content-between border-0 header nav-tabs" data-id="tabs-component">
<div className="d-flex flex-row" style={{maxWidth: 'fit-content', width: '99%'}}>
<div className="d-flex flex-row justify-content-center align-items-center m-1 mt-1">
<button
data-id="play-editor"
className="btn text-success py-0"
disabled={!(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' || tabsState.currentExt === 'sol' || tabsState.currentExt === 'circom' || tabsState.currentExt === 'vy')}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path)
if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') {
await props.plugin.call('scriptRunner', 'execute', content, path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') {
await props.plugin.call('solidity', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'circom') {
await props.plugin.call('circuit-compiler', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'vy') {
await props.plugin.call('vyper', 'vyperCompileCustomAction')
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
}
}}
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-run-script"
tooltipText={
<span>
{tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' ? (
<FormattedMessage id="remixUiTabs.tooltipText1" />
) : tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul' || tabsState.currentExt === 'circom' || tabsState.currentExt === 'vy' ? (
<FormattedMessage id="remixUiTabs.tooltipText2" />
) : (
<FormattedMessage id="remixUiTabs.tooltipText3" />
)}
</span>
}
>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-run-script"
tooltipText={
<span>
{tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' ? (
<FormattedMessage id="remixUiTabs.tooltipText1" />
) : tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul' || tabsState.currentExt === 'circom' || tabsState.currentExt === 'vy' ? (
<FormattedMessage id="remixUiTabs.tooltipText2" />
) : (
<FormattedMessage id="remixUiTabs.tooltipText3" />
)}
</span>
}
<button
data-id="play-editor"
className="btn text-success py-0"
disabled={!(tabsState.currentExt === 'js' || tabsState.currentExt === 'ts' || tabsState.currentExt === 'sol' || tabsState.currentExt === 'circom' || tabsState.currentExt === 'vy')}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path)
if (tabsState.currentExt === 'js' || tabsState.currentExt === 'ts') {
await props.plugin.call('scriptRunner', 'execute', content, path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'sol' || tabsState.currentExt === 'yul') {
await props.plugin.call('solidity', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'circom') {
await props.plugin.call('circuit-compiler', 'compile', path)
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
} else if (tabsState.currentExt === 'vy') {
await props.plugin.call('vyper', 'vyperCompileCustomAction')
_paq.push(['trackEvent', 'editor', 'clickRunFromEditor', tabsState.currentExt])
}
}}
>
<i className="fad fa-play"></i>
</CustomTooltip>
</button>
<button
data-id="explain-editor"
id='explain_btn'
className="btn py-0"
disabled={!(tabsState.currentExt === 'sol' )}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path)
if (tabsState.currentExt === 'sol') {
(document.getElementById('explain_btn') as HTMLButtonElement).disabled = true;
const result = await props.plugin.call('solcoder', 'code_explaining', content)
_paq.push(['trackEvent', 'ai', 'solcoder', 'explain_file'])
}
(document.getElementById('explain_btn') as HTMLButtonElement).disabled = false;
}}
</button>
</CustomTooltip>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-explaination"
tooltipText={
<span>
{tabsState.currentExt === 'sol'? (
<FormattedMessage id="remixUiTabs.tooltipText5" />
) : (
<FormattedMessage id="remixUiTabs.tooltipText4" />
)}
</span>
}
>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-explaination"
tooltipText={
<span>
{tabsState.currentExt === 'sol'? (
<FormattedMessage id="remixUiTabs.tooltipText5" />
) : (
<FormattedMessage id="remixUiTabs.tooltipText4" />
)}
</span>
}
<button
data-id="explain-editor"
id='explain_btn'
className='btn py-0 text-ai px-0 d-flex'
disabled={!(tabsState.currentExt === 'sol') || explaining}
onClick={async () => {
const path = active().substr(active().indexOf('/') + 1, active().length)
const content = await props.plugin.call('fileManager', 'readFile', path)
if (tabsState.currentExt === 'sol') {
setExplaining(true)
await props.plugin.call('solcoder', 'code_explaining', content)
setExplaining(false)
_paq.push(['trackEvent', 'ai', 'solcoder', 'explain_file'])
}
}}
>
<i className="fa-solid text-dark fa-message-exclamation"></i>
</CustomTooltip>
</button>
<button
data-id="remix_ai_switch"
id='remix_ai_switch'
className="btn ai-switch py-0"
disabled={!(tabsState.currentExt === 'sol' )}
onClick={async () => {
await props.plugin.call('settings', 'updateCopilotChoice', ai_switch)
setAI_switch(!ai_switch)
}}
<i className={`fa-solid fa-user-robot ${explaining ? 'loadingExplanation' : ''}`}></i>
<span
className="position-relative text-ai text-sm pl-1"
style={{fontSize: "x-small", alignSelf: "end"}}
>
AI
</span>
</button>
</CustomTooltip>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-copilot"
tooltipText={
<span>
{ tabsState.currentExt === 'sol'? (
!ai_switch ? (
<FormattedMessage id="remixUiTabs.tooltipText6" />
) : (<FormattedMessage id="remixUiTabs.tooltipText7" />)
) : (
<FormattedMessage id="remixUiTabs.tooltipText4" />
)}
</span>
}
>
<CustomTooltip
placement="bottom"
tooltipId="overlay-tooltip-copilot"
tooltipText={
<span>
<FormattedMessage id="remixUiTabs.tooltipText6" />
</span>
}
<button
data-id="remix_ai_switch"
id='remix_ai_switch'
className="btn ai-switch text-ai pl-2 pr-0 py-0 d-flex"
disabled={!(tabsState.currentExt === 'sol' )}
onClick={async () => {
await props.plugin.call('settings', 'updateCopilotChoice', !ai_switch)
setAI_switch(!ai_switch)
}}
>
<i className= {ai_switch ? "fa-solid text-success fa-toggle-on" :"fa-solid text-dark fa-toggle-off"}></i>
</CustomTooltip>
</button>
<i
className={ai_switch ? "fa-solid fa-toggle-on" : "fa-solid fa-toggle-off"}
></i>
<span
className="position-relative text-ai text-sm pl-1"
style={{fontSize: "x-small", alignSelf: "end"}}
>
AI
</span>
</button>
</CustomTooltip>
<script>
const button = document.querySelector('#button');
</script>
<CustomTooltip placement="bottom" tooltipId="overlay-tooltip-zoom-out" tooltipText={<FormattedMessage id="remixUiTabs.zoomOut" />}>
<span data-id="tabProxyZoomOut" className="btn btn-sm px-2 fas fa-search-minus text-dark" onClick={() => props.onZoomOut()}></span>

@ -159,7 +159,7 @@ export const registerScriptRunnerReducer = (state, action) => {
case TYPEWRITERWARNING:
return {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-warning', provider: action.payload.provider })
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, typewriter: true, style: 'text-ai', provider: action.payload.provider })
}
case TYPEWRITERSUCCESS:
return {

@ -45,7 +45,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState)
const [, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState)
const [toaster, setToaster] = useState(false)
const [aiLoading, setAILoading] = useState(false)
const [toastProvider, setToastProvider] = useState({
show: false,
fileName: '',
@ -105,14 +104,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setIsVM(provider.startsWith('vm-'))
})
props.plugin.on('solcoder', 'aiInfering', () => {
setAILoading(true)
})
props.plugin.on('solcoder', 'aiInferingDone', () => {
setAILoading(false)
})
props.onReady({
logHtml: (html) => {
scriptRunnerDispatch({
@ -675,11 +666,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
data-id="terminalInputSearchTerminal"
/>
</div>
{aiLoading && <div className="text-center py-5 ml-5">
<i className="fas fa-spinner fa-pulse fa-2x"></i>
<span> AI Running ...</span>
</div>}
</div>
</div>
<div tabIndex={-1} className="remix_ui_terminal_container d-flex h-100 m-0 flex-column" data-id="terminalContainer">

Loading…
Cancel
Save