copilot ui and sync with settings

aiui_disabled_init
lianahus 12 months ago
parent 0c616c641c
commit 3c2f0ee4f9
  1. 2
      apps/remix-ide/src/app/plugins/openaigpt.tsx
  2. 6
      apps/remix-ide/src/app/plugins/solcoderAI.tsx
  3. 7
      apps/remix-ide/src/app/tabs/locales/en/remixUiTabs.json
  4. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-cerulean.min.css
  5. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-cyborg.min.css
  6. 6
      apps/remix-ide/src/assets/css/themes/bootstrap-flatly.min.css
  7. 4
      apps/remix-ide/src/assets/css/themes/bootstrap-spacelab.min.css
  8. 4
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  9. 5
      apps/remix-ide/src/assets/css/themes/remix-candy_ikhg4m.css
  10. 4
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  11. 4
      apps/remix-ide/src/assets/css/themes/remix-hacker_owl.css
  12. 5
      apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css
  13. 5
      apps/remix-ide/src/assets/css/themes/remix-midcentury_hrzph3.css
  14. 5
      apps/remix-ide/src/assets/css/themes/remix-unicorn.css
  15. 5
      apps/remix-ide/src/assets/css/themes/remix-violet.css
  16. 15
      libs/remix-ui/renderer/src/lib/renderer.tsx
  17. 6
      libs/remix-ui/settings/src/lib/remix-ui-settings.tsx
  18. 23
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.css
  19. 191
      libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx
  20. 2
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  21. 14
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx

@ -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,9 +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.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"
}

@ -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;
}

@ -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>
)}

@ -120,7 +120,11 @@ export const RemixUiSettings = (props: RemixUiSettingsProps) => {
useEffect(() => {
if (props.useMatomoAnalytics !== null) useMatomoAnalytics(props.config, props.useMatomoAnalytics, dispatch)
}, [props.useMatomoAnalytics])
}, [props.useMatomoAnalytics])
useEffect(() => {
if (props.useCopilot !== null) copilotActivate(props.config, props.useCopilot, dispatch)
}, [props.useCopilot])
const onchangeGenerateContractMetadata = (event) => {
generateContractMetadat(props.config, event.target.checked, dispatch)

@ -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,101 +168,119 @@ 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')}
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])
}
}}
<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' ? (
<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' ? (
<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')}
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])
}
}}
>
<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 {

@ -44,7 +44,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: '',
@ -104,14 +103,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({
@ -658,11 +649,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
data-id="terminalInputSearch"
/>
</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