Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ jobs:
run: >
./gradlew test
--tests "org.zoocode.jetbrains.terminal.RawOutputTtyConnectorTest"
--tests "org.zoocode.jetbrains.util.JcefSupportTest"
--tests "org.zoocode.jetbrains.descriptor.PluginDescriptorTest"
--tests "org.zoocode.jetbrains.terminal.TerminalPtyBuilderTest"
--no-configuration-cache

- name: Build the plugin
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ deps/vscode/*
jetbrains_plugin/plugins
jetbrains_plugin/build
.gradle
/.gradle-user-home/
deps/vsix/package/*
deps/vsix/plugin/*
-e
# Roo build artifacts
.zoo-build/
dist-with-roo/
plans/
plans/
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Changelog

## Unreleased

### Terminal on Windows

- The plugin requests ConPTY explicitly through the public pty4j setter. The JetBrains registry key `terminal.use.conpty.on.windows` is no longer read.
- If the ConPTY native library fails to load, the process falls back to winpty and the session continues.
- Other process start failures propagate to the terminal error handling.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ The plugin targets IntelliJ Platform build **233** (JetBrains 2023.3) or newer.
- Android Studio 2026.1.4 or newer when using Android Studio; install a preview build until this version reaches the stable channel
- A JCEF-enabled IDE runtime, which Zoo Code requires to render its interface

Where JCEF support comes from depends on the IDE version. From IntelliJ Platform 2026.2 (build 262), JCEF ships as a separate bundled plugin. If that plugin is missing or disabled, Zoo Code shows a warning notification and the Zoo Code tool window cannot render. Enable the bundled JCEF plugin under **Settings → Plugins → Installed** and restart the IDE. On IDEs older than 2026.2, JCEF comes from the IDE runtime; see [Known Issues](docs/KNOWN_ISSUES.md).

On first launch the plugin automatically downloads a pinned, platform-specific Node.js runtime from [nodejs.org](https://nodejs.org) (checksum-verified) and uses it to run the extension host, so no local Node.js installation is required. If the download is unavailable, the plugin falls back to a bundled Node.js or a Node.js **20.6.0 or newer** found on your `PATH`.

### Building from source
Expand Down
35 changes: 28 additions & 7 deletions docs/KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ Android Studio's default startup runtime does not include JCEF capabilities, req
1. **Download a JCEF-Compatible JetBrains Runtime**

Download a JetBrains Runtime (JBR) with JCEF support:

👉 https://github.com/JetBrains/JetBrainsRuntime/releases

Choose a release with:
- `jbr_jcef` in the name
- Correct architecture (osx-aarch64, linux-x64, or windows-x64)

Example:
```
jbr_jcef-17.0.11-osx-aarch64-b1063.2.tar.gz
```

Unpack it somewhere, e.g.:
```
~/jbr/jbr_jcef
Expand All @@ -47,15 +47,15 @@ Android Studio's default startup runtime does not include JCEF capabilities, req
4. Select it and choose the folder where you extracted the JBR (e.g., ~/jbr/jbr_jcef).

3. **Restart Android Studio**

After selecting the new runtime, Android Studio will prompt you to restart. Confirm, and it will relaunch with the new runtime.

4. **Verify the Runtime**

Go to Help → About to confirm the IDE is now running with the new JBR. The version should indicate JCEF support (e.g., JetBrains Runtime jbr-17.0.11+7-b1238.56-jcef).

5. **Revert if Needed**

You can always use the same "Choose Boot Java Runtime for the IDE" action to switch back to the default runtime if needed.

##### Method 2: Using Edit Custom VM Options
Expand Down Expand Up @@ -91,6 +91,27 @@ On Linux ARM platforms, JCEF may fail to initialize native processes properly, l
#### Solutions
No solution available at this time.

### 1.3 JCEF Is Not Available on IDE 2026.2 or Newer

#### Problem Description

From IntelliJ Platform 2026.2 (build 262), JetBrains packages JCEF as a separate bundled plugin with the id `com.intellij.modules.jcef`. JCEF is no longer part of the IDE platform. When this bundled plugin is not present, the Zoo Code view cannot render.

#### Behavior

The plugin does not crash. It logs the reason and shows a warning notification. The Zoo Code tool window stays empty. The IDE log shows the state in the startup line `JCEF: CLASSES_MISSING`.

#### Affected Platforms
- IDEs based on IntelliJ Platform build 262 or newer without the bundled JCEF plugin

#### Solutions

1. Open **Settings → Plugins → Installed** and search for **JCEF**.
2. Enable the bundled JCEF plugin if it is disabled.
3. Restart the IDE.

If the bundled JCEF plugin is not in the list, this IDE build does not ship JCEF, and the Zoo Code view cannot run in it. IDEs older than 2026.2 get JCEF from the runtime; see section 1.1.

---

## 2. Node Related Issues
Expand Down
20 changes: 16 additions & 4 deletions jetbrains_plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ fun Sync.prepareSandbox() {
}
depfile.readLines().let {
it.forEach { line ->
depPatterns.add(line.substringAfterLast("node_modules/") + "/**")
depPatterns.add(line.replace('\\', '/').substringAfterLast("node_modules/") + "/**")
}
}

Expand Down Expand Up @@ -363,17 +363,29 @@ tasks {
// linked against the 233 platform jars already on the compile classpath; this task
// never needs the 2026.3 IDE. The scan target is the instrumented jar in the
// sandbox, the exact bytes that the Plugin Verifier checks and that the published
// zip packages. The marker output makes the task up-to-date when the sandbox and
// the checker are unchanged.
// zip packages. The task first runs the checker --self-test, which feeds it
// synthetic clean and banned jars, so a scanner regression fails this task. The
// marker output makes the task up-to-date when the sandbox and the checker are
// unchanged.
register("verifyPackagedBytecode") {
group = "verification"
description = "Reject packaged references to terminal APIs removed in IntelliJ 2026.3"
description =
"Reject packaged references to terminal APIs removed in IntelliJ 2026.3 and to internal LocalPtyOptions API"
val checker = layout.projectDirectory.file("smoke/Smoke263DispatchCheck.java")
inputs.files(prepareSandbox.map { it.destinationDir })
inputs.file(checker)
outputs.file(layout.buildDirectory.file("verification/packaged-bytecode.ok"))
dependsOn(prepareSandbox)
doLast {
project.exec {
commandLine(
verificationJavaLauncher.get().executablePath.asFile.absolutePath,
"-cp",
configurations.getByName("compileClasspath").asPath,
checker.asFile.absolutePath,
"--self-test",
)
}
val pluginJar = packagedPluginJar()
project.exec {
commandLine(
Expand Down
47 changes: 47 additions & 0 deletions jetbrains_plugin/smoke/PowerShellIntegrationCheck.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
$ErrorActionPreference = 'Stop'
$profilePath = Join-Path $PSScriptRoot '../src/main/resources/run-vs-agent-shell-integrations/vscode-powershell/profile.ps1'
$tokens = $null
$parseErrors = $null
[System.Management.Automation.Language.Parser]::ParseFile($profilePath, [ref]$tokens, [ref]$parseErrors) | Out-Null
if ($parseErrors.Count) { throw ($parseErrors.Message -join '; ') }
if ([IO.File]::ReadAllText($profilePath) -match '[^\x00-\x7F]') { throw 'Profile must remain ASCII for Windows PowerShell 5.1.' }

# Mock only console input/history; load the real profile in a child script scope.
Import-Module PSReadLine
$global:testHistoryId = 1
function global:Get-History { [pscustomobject]@{ Id = $global:testHistoryId } }
function global:PSConsoleHostReadLine { 'Write-Output "hello;world"' }
$env:VSCODE_NONCE = 'test-nonce'
$originalConsole = [Console]::Out
$capture = New-Object IO.StringWriter
try {
[Console]::SetOut($capture)
& $profilePath
$unicodeValue = 'caf' + [char]0xe9 + ' ' + [char]0x65e5 + [char]0x672c + [char]0x8a9e + ' ' + [char]::ConvertFromUtf32(0x1f680)
$escapedUnicode = __VSCode-Escape-Value $unicodeValue
if ($escapedUnicode -match '[^\x00-\x7F]') { throw 'Unicode marker is not ASCII-safe.' }
if ($escapedUnicode -ne 'caf\xc3\xa9 \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e \xf0\x9f\x9a\x80') { throw 'Incorrect UTF-8 Unicode escapes.' }
$initialPrompt = prompt
$command = PSConsoleHostReadLine
$global:testHistoryId = 2
$finishedPrompt = prompt
$null = PSConsoleHostReadLine
$global:testHistoryId = 3
cmd.exe /c exit 7
$failedPrompt = prompt
$null = PSConsoleHostReadLine
$global:testHistoryId = 4
Write-Output 'alive' | Out-Null
$recoveredPrompt = prompt
} finally { [Console]::SetOut($originalConsole) }
$esc = [char]27
$bell = [char]7
$markers = $capture.ToString()
if ($command -ne 'Write-Output "hello;world"') { throw 'ReadLine did not preserve the command.' }
if (-not $markers.Contains("${esc}]633;E;Write-Output `"hello\x3bworld`";test-nonce${bell}")) { throw 'Missing command/nonce marker after script scope exited.' }
if (-not $markers.Contains("${esc}]633;C${bell}")) { throw 'Missing execution start marker.' }
if (-not $initialPrompt.Contains("${esc}]633;P;Cwd=")) { throw 'Missing working directory marker.' }
if (-not $finishedPrompt.Contains("${esc}]633;D;0${bell}")) { throw 'Missing successful completion marker.' }
if (-not $failedPrompt.Contains("${esc}]633;D;7${bell}")) { throw 'Native exit code 7 was not preserved.' }
if (-not $recoveredPrompt.Contains("${esc}]633;D;0${bell}")) { throw 'Stale native exit code reused for successful command.' }
'PASS: Windows PowerShell parsing, script-scope lifetime, command escaping, nonce, start, cwd and completion markers.'
Loading
Loading