Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
pull_request:
branches: [main, master]
workflow_dispatch:
schedule:
- cron: '31 2 * * 1'

concurrency:
group: ci-${{ github.ref }}
Expand All @@ -20,22 +22,25 @@ jobs:
# report but don't block the build gate. Tighten back to blocking once the debt is cleared.
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: 10.12.4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20.19.2
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Audit production dependencies
run: pnpm audit --prod

- name: Lint libs
run: pnpm lint:libs

Expand All @@ -50,15 +55,15 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: 10.12.4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20.19.2
cache: pnpm
Expand All @@ -80,15 +85,15 @@ jobs:
continue-on-error: true
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: 10.12.4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20.19.2
cache: pnpm
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: 10.12.4

- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 20.19.2
cache: pnpm
Expand Down
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ It is a platform for development tools that uses a plugin architecture, dedicate

### Core Features

- **Full Contract Lifecycle Management**: Easily write, compile, deploy, debug, and test your Solidity smart contracts.
- **Rich Plugin Ecosystem**: A plugin market with intuitive GUIs to extend your IDE's capabilities on demand.
- **TRON Network Integration**: Optimized workflows for TRON, simplifying interaction with TronLink and the TRON network.
- **AI Assistant (New!)**: Integrated AI assistance to help you debug code, optimize contract gas consumption, explain complex logic, and generate unit tests.
- **Full Contract Lifecycle Management**: Write, compile, deploy, debug, and test Solidity smart contracts on a JavaScript VM (Tron), Injected TronWeb (TronLink), and TRON-focused examples.
- **AI Assistant**: An in-panel assistant with a real IDE tool belt — it can compile and set the compiler version, deploy, read/write contracts, run tests and static analysis, search the workspace, show diffs and make precise edits, drive local Git (clone / commit / push / pull), record–replay–export a deploy flow as a runnable TronBox project, and save a compiler-settings reference for the manual TronScan verification workflow. Every file/state write asks for your confirmation first, and API keys are kept in browser memory only.
- **TRON Network Integration**: Optimized workflows for TRON — connect TronLink, deploy and transact on Nile or Mainnet with clear wallet feedback, and TronScan-oriented contract verification.
- **Local Git & GitHub**: A per-workspace Git panel (init, stage, commit, branches, history) plus in-browser clone / fetch / pull / push, with GitHub connected via an OAuth popup and tokens held in memory only.
- **Rich Plugin Ecosystem**: A plugin architecture with intuitive GUIs to extend the IDE's capabilities on demand.

------

Expand Down Expand Up @@ -75,9 +76,17 @@ Getting TronIDE running on your local machine is straightforward.
* Use Nile/testnet for validation; do not treat mainnet deployment as a release pass criterion.
* Compile a sample Solidity contract, open Deploy & Run, connect TronLink on Nile, deploy, call a view method, send one state-changing transaction, and open the transaction on Tronscan.

### v2.3.0 scope notes
### v2.3.2 scope notes

TronIDE v2.3.0 is TRON-oriented and does not claim full Ethereum Remix parity. Current Home status is documented in `apps/remix-ide/docs/v2.3.0-roadmap-section-3-traceability.md` and `apps/remix-ide/docs/remix-220-home-parity.md`: AI, tutorials, templates, plugin manager, static analysis, public GitHub links/imports, and the TronScan-oriented Contract Verification plugin exposes its actual local state; account sign-in is token-only for this release, GitHub OAuth is deferred while private read/write uses user-provided fine-grained tokens in the browser, and automated TronScan source submission/receipts plus EVM-only verification services remain blocked/unavailable or not applicable to TRON rather than marked done; AI model/audio/history controls are not duplicated on Home.
TronIDE is TRON-oriented and does not claim full Ethereum Remix parity. Highlights of the current 2.3.x line:

- **AI assistant** with a real IDE tool belt (compile / deploy / read–write, workspace search, diff and precise edits, tests, static analysis, local Git, Recorder → TronBox export, and verification), gated by per-write confirmation.
- **Local Git panel** (init / stage / commit / branches / history) plus in-browser clone / fetch / pull / push.
- **GitHub connect via an OAuth popup**, with tokens held in browser memory only — no GitHub secret is persisted to disk (the earlier Settings-tab gist token has been retired).
- **Contract Verification** selects the deployable main contract and downloads a flattened `.sol` file for TronScan; the exported metadata JSON is a reference checklist only, and the final source upload stays manual.
- **In-app Release Notes** (open them from the version badge or the Release Notes button in the header) and a **Feedback** entry that links straight to the project's GitHub issues.

TronLink is the wallet path for real networks. Automated TronScan source submission/receipts and EVM-only verification services are unavailable or not applicable to TRON rather than marked as done.

------

Expand Down
4 changes: 2 additions & 2 deletions apps/remix-ide-e2e/src/helpers/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ function initModules (browser: NightwatchBrowser, callback: VoidFunction) {
.scrollAndClick('[data-id="verticalIconsKindfilePanel"]')
.clickLaunchIcon('settings')
.click('*[data-id="settingsTabGenerateContractMetadataLabel"]')
.setValue('[data-id="settingsTabGistAccessToken"]', process.env.gist_token)
.click('[data-id="settingsTabSaveGistToken"]')
// The Settings gist-token panel is retired (GitHub tokens are memory-only);
// gist specs that need auth connect through the Home GitHub flow instead.
.clickIfPresent('[data-id="settingsTabThemeLabelFlatly"]') // e2e tests were initially developed with Flatly. Some tests are failing with the default one (Dark), because the dark theme put uppercase everywhere.
.perform(() => { callback() })
}
32 changes: 8 additions & 24 deletions apps/remix-ide-e2e/src/tests/generalSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,16 @@ module.exports = {
})
},

'Should add new github access token': function (browser: NightwatchBrowser) {
// The gist access-token panel was retired from Settings (GitHub tokens are
// memory-only, managed by the Home/header Connect GitHub flow), so make sure
// it is actually gone.
'Should not render the retired gist token panel': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="verticalIconsKindsettings"]')
.setValue('*[data-id="settingsTabGistAccessToken"]', '**********')
.click('*[data-id="settingsTabSaveGistToken"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]', 'Access token has been saved')
.pause(3000)
},

'Should copy github access token to clipboard': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.click('*[data-id="copyToClipboardCopyIcon"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
// .waitForElementVisible('*[data-shared="tooltipPopup"]:nth-last-of-type(1) , 5000)
// .assert.containsText('*[data-shared="tooltipPopup"]', 'Copied value to clipboard.')
// .assert.containsText('*[data-shared="tooltipPopup"]:nth-last-of-type(1)', 'Copied value to clipboard.')
},

'Should remove github access token': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]', 5000)
.pause(1000)
.click('*[data-id="settingsTabRemoveGistToken"]')
.waitForElementVisible('*[data-shared="tooltipPopup"]', 5000)
.assert.containsText('*[data-shared="tooltipPopup"]', 'Access token removed')
.assert.containsText('*[data-id="settingsTabGistAccessToken"]', '')
.waitForElementVisible('*[data-id="settingsTabThemePanel"]', 5000)
.assert.not.elementPresent('*[data-id="settingsTabGistAccessToken"]')
.assert.not.elementPresent('*[data-id="settingsTabSaveGistToken"]')
.assert.not.elementPresent('*[data-id="settingsTabRemoveGistToken"]')
},

'Should load dark theme': function (browser: NightwatchBrowser) {
Expand Down
14 changes: 5 additions & 9 deletions apps/remix-ide-e2e/src/tests/gist.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,11 @@ module.exports = {
.modalFooterOKClick()
},

'Display Error Message For Missing Gist Token When Publishing': function (browser: NightwatchBrowser) {
// The Settings gist-token panel is retired: with no in-memory GitHub
// connection, publishing must point the user at the Connect GitHub flow.
'Display Error Message For Missing GitHub Connection When Publishing': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('settings')
.waitForElementVisible('[data-id="settingsTabRemoveGistToken"]')
.click('[data-id="settingsTabRemoveGistToken"]')
.clickLaunchIcon('filePanel')
.waitForElementVisible('*[data-id="fileExplorerNewFilepublishToGist"]')
.click('*[data-id="fileExplorerNewFilepublishToGist"]')
Expand All @@ -126,18 +125,15 @@ module.exports = {
.click('*[data-id="default_workspaceModalDialogContainer-react"] .modal-ok')
.pause(10000)
.getText('[data-id="default_workspaceModalDialogModalBody-react"]', (result) => {
browser.assert.ok(result.value === 'TronIDE requires an access token (which includes gists creation permission). Please go to the settings tab to create one.', 'Assert failed. Gist token error message not displayed.')
browser.assert.ok(result.value === 'Publishing a gist needs a GitHub connection that can create gists. Use "Connect GitHub" on the Home page (or the header button) to sign in, then publish again.', 'Assert failed. Gist connect-GitHub error message not displayed.')
})
.click('[data-id="default_workspace-modal-footer-ok-react"]')
},

'Import From Gist For Valid Gist ID': function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="remixIdeIconPanel"]', 10000)
.clickLaunchIcon('settings')
.click('*[data-id="settingsTabGenerateContractMetadataLabel"]')
.setValue('[data-id="settingsTabGistAccessToken"]', process.env.gist_token)
.click('[data-id="settingsTabSaveGistToken"]')
// Public gists load anonymously — the Settings gist-token panel is retired.
.clickLaunchIcon('filePanel')
.scrollAndClick('*[data-id="landingPageImportFromGistButton"]')
.waitForElementVisible('*[data-id="modalDialogCustomPromptText"]')
Expand Down
38 changes: 37 additions & 1 deletion apps/remix-ide-pw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,43 @@ Reports land in `playwright-report/` (gitignored under `reports/*`).
| Spec | What it asserts |
|---|---|
| `home.spec.ts` | Landing page renders, primary Home actions are present, Advanced tools remain collapsed until expanded, the expanded advanced sections render, the state is persisted, the tabbar compile shortcut starts disabled without an active Solidity tab, and no console errors occur during initial load |
| `github-token-modal.spec.ts` | "Connect token" opens the modal **without** the legacy "Remember in this browser" checkbox; the tab-only storage notice is present (regression for the 2026-05-27 HIGH finding) |
| `github-token-modal.spec.ts` | "Connect token" opens the modal **without** the legacy "Remember in this browser" checkbox; the tab-only storage notice is present (regression guard for the token-persistence security fix) |
| `solidity-uml-interactions.spec.ts` | UML diagram redraws on file switch, replays after rapid switches (dropped-redraw guard), survives a panel toggle-close/reopen, and Copy-Mermaid writes the source to the clipboard |
| `solidity-lint-lifecycle.spec.ts` | Lint annotations clear when a finding is fixed, track the active file (no stale buffer), and stay responsive under rapid edits |
| `git-panel-reactivity.spec.ts` | Unstage returns a file to Changes, A/M status badges render, and an in-editor save surfaces in the panel via the 500 ms reactive refresh (no re-open) |
| `github-token-memory.spec.ts` | A reload drops the in-memory GitHub token and the UI re-reads disconnected; web storage is never written (memory-only token regression guard) |
| `autosave-reactivity.spec.ts` | An idle edit autosaves (no Ctrl+S) and survives a full reload |

## Deterministic gate subset (`@gate`)

The v2.3.2 interaction-regression specs are tagged `@gate` and run
deterministically — VM/local only, no network, wallet, or live clone. Specs
that press Ctrl+S first abort every compiler source (remote binaries and the
bundled same-origin fallback) via the `blockCompilerSources` helper: the save
still happens (it runs before the compile), but no solc run ever starts, so the
compile-saturation flake stays out of this subset by construction. Saves are
then confirmed by polling the in-browser workspace FS (`readSavedFile`) instead
of sleeping. Run just the gate subset with:

```bash
pnpm test:pw:gate # playwright test --grep @gate
```

Unlike the full suite, this subset is meant to be **release-blocking**. The
Playwright suite is `continue-on-error` / `allow_failure: true` in CI today
because the full set carries network + compile-saturation flake; the `@gate`
subset does not, so it can be promoted to a required check. Proposed CI change
(Ops-owned — not wired here):

- GitHub `ci.yml`: add a job running `pnpm test:pw:gate` **without**
`continue-on-error` (leave the existing `e2e-smoke` job informational).
- Any other CI provider: the equivalent is a required (no allow-failure) job in
the test stage running `pnpm test:pw:gate`, keeping the full suite as a
separate informational job.

To extend the gate, add `{ tag: '@gate' }` to any other deterministic spec.
`autosave-reactivity.spec.ts` is intentionally **not** tagged — its idle-debounce
+ reload timing suits the smoke run, not a required gate.

## What it's NOT for

Expand Down
8 changes: 4 additions & 4 deletions apps/remix-ide-pw/scripts/wallet-cdp-phase1.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function startApprovePump (browser, active) {
try {
await page.waitForFunction(() => {
const el = document.querySelector('[data-id="headerWalletConnect"]')
return el && /Wallet T/.test(el.textContent || '')
return el?.getAttribute('aria-haspopup') === 'menu'
}, null, { timeout: 45000 })
} catch (e) {
const label = await headerBtn.getAttribute('title').catch(() => null)
Expand All @@ -99,10 +99,10 @@ function startApprovePump (browser, active) {
log('runtab env:', envValue, '| net:', netText, '| account:', account0)
await page.screenshot({ path: `${SHOTS}/02-connected.png`, fullPage: false })

check('TC-WAL-004 header', /Wallet T\w{3,6}…?\w*|Wallet T/.test(headerLabel) && /nile/i.test(headerLabel), `header="${headerLabel}"`)
check('TC-WAL-004 header', /T[A-Za-z0-9]{5}…[A-Za-z0-9]{6}\s·\sNile/i.test(headerLabel), `header="${headerLabel}"`)
check('TC-WAL-004 runtab env', envValue === 'injected', `env=${envValue}`)
check('TC-WAL-004 runtab net', /nile/i.test(netText), `net="${netText}"`)
const headerShort = (headerLabel.match(/Wallet (T\w{3,5})/) || [])[1]
const headerShort = (headerLabel.match(/(T[A-Za-z0-9]{5})/) || [])[1]
check('TC-IX-ENV-002 account sync', account0 && headerShort && account0.startsWith(headerShort.slice(0, 4)), `header=${headerShort} runtab=${account0}`)

// Wallet menu read surfaces.
Expand Down Expand Up @@ -141,7 +141,7 @@ function startApprovePump (browser, active) {
const pump2 = startApprovePump(browser, active2)
await page.locator('[data-id="headerWalletConnect"]').click()
try {
await page.waitForFunction(() => /Wallet T/.test((document.querySelector('[data-id="headerWalletConnect"]') || {}).textContent || ''), null, { timeout: 30000 })
await page.waitForFunction(() => document.querySelector('[data-id="headerWalletConnect"]')?.getAttribute('aria-haspopup') === 'menu', null, { timeout: 30000 })
} finally {
active2.on = false
await pump2
Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide-pw/scripts/wallet-cdp-phase2a.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function clickInPopup (browser, namePattern, timeoutMs) {

async function connectViaHeader (page) {
await page.locator('[data-id="headerWalletConnect"]').click()
await page.waitForFunction(() => /Wallet T/.test((document.querySelector('[data-id="headerWalletConnect"]') || {}).textContent || ''), null, { timeout: 30000 })
await page.waitForFunction(() => document.querySelector('[data-id="headerWalletConnect"]')?.getAttribute('aria-haspopup') === 'menu', null, { timeout: 30000 })
}

;(async () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide-pw/scripts/wallet-cdp-phase2b.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ async function clickInPopup (browser, namePattern, timeoutMs) {
const ok = await clickInPopup(browser, /^(连接|Connect)$/, 15000)
check('approve connect', ok, 'clicked Connect')
}
await page.waitForFunction(() => /Wallet T/.test((document.querySelector('[data-id="headerWalletConnect"]') || {}).textContent || ''), null, { timeout: 30000 })
await page.waitForFunction(() => document.querySelector('[data-id="headerWalletConnect"]')?.getAttribute('aria-haspopup') === 'menu', null, { timeout: 30000 })
log('connected')
await page.locator('#runTabView select[class^="contractNames"]').selectOption('Storage')

Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide-pw/scripts/wallet-cdp-phase3a.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async function tapInPopup (browser, label, timeoutMs) {
// Connect — the user clicks the re-auth popup if one shows (90s budget).
log('connecting… (if a TronLink popup appears, the USER approves this one)')
await page.locator('[data-id="headerWalletConnect"]').click()
await page.waitForFunction(() => /Wallet T/.test((document.querySelector('[data-id="headerWalletConnect"]') || {}).textContent || ''), null, { timeout: 90000 })
await page.waitForFunction(() => document.querySelector('[data-id="headerWalletConnect"]')?.getAttribute('aria-haspopup') === 'menu', null, { timeout: 90000 })
log('connected')
await page.locator('#runTabView select[class^="contractNames"]').selectOption('Storage')

Expand Down
Loading