Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
60e5364
feat(plot): make viewers session-aware and restorable after reload
Fred-Wu Nov 11, 2026
786957c
feat(plot): use separate JGD viewers per session
Fred-Wu Nov 11, 2026
73fb9d3
feat(plot): add shared session indicators to plot viewers
Fred-Wu Nov 11, 2026
773159f
fix: removed session label from viewer Tab
Fred-Wu Nov 11, 2026
de35731
fix(plot): restore viewers for the active R session after reload
Fred-Wu Nov 11, 2026
f0a80ae
fix: httpgd now stops refreshes and cancels pending requests when its…
Fred-Wu Nov 11, 2026
9a7d63b
fix(plot): preserve resize targets for restored JGD snapshots
Fred-Wu Nov 11, 2026
e0f54dc
fix(plot): retire reused R device registrations
Fred-Wu Nov 11, 2026
5d81f1f
fix(plot): route JGD metrics through the owning session viewer
Fred-Wu Nov 11, 2026
8227ef1
fix(plot): reconnect explicitly opened VS Code JGD devices
Fred-Wu Nov 11, 2026
36164c7
fix(plot): replace stale httpgd viewers when sessions change
Fred-Wu Nov 11, 2026
ef2a467
chore(sess): bump version to 3.4.0.9002
Fred-Wu Nov 11, 2026
4fdafba
fix(plot): share JGD history and sizing across session devices
Fred-Wu Nov 11, 2026
cb025c4
fix(plot): coalesce JGD resizes until frames confirm dimensions
Fred-Wu Nov 11, 2026
053acbd
test(plot): cover large plots on existing and new JGD devices
Fred-Wu Nov 11, 2026
542d34c
fix(plot): redraw retained JGD viewers when their tab becomes visible
Fred-Wu Nov 11, 2026
5078f2d
Revert "test(plot): cover large plots on existing and new JGD devices"
Fred-Wu Nov 11, 2026
56d0796
fix(plot): forget httpgd connections after confirmed session exit
Fred-Wu Nov 11, 2026
c5b14cc
fix(plot): preserve device identity and streamline history checkpoints
Fred-Wu Nov 11, 2026
5d75e63
fix(plot): preserve notifications during session attachment
Fred-Wu Nov 11, 2026
7621859
fix(plot): remove unnecessary fallbacks and obsolete viewer code
Fred-Wu Nov 11, 2026
afd8fde
fix(plot): preserve viewer state across reconnects and reopening
Fred-Wu Nov 11, 2026
066614a
fix(plots): unify viewer lifecycle and consolidate httpgd devices
Fred-Wu Nov 11, 2026
4b61f7f
fix(plots): restore toolbar navigation to the focused viewer
Fred-Wu Nov 11, 2026
39a7f4e
fix(plots): preserve httpgd updates across device failures and restor…
Fred-Wu Nov 11, 2026
48bb4f0
fix: preserve httpgd plot order and handle closed devices
Fred-Wu Nov 11, 2026
d33b9df
fix(plots): preserve history across device closure and reload
Fred-Wu Nov 11, 2026
7c15ebc
fix(plots): apply deferred httpgd resizes when devices reactivate
Fred-Wu Nov 11, 2026
997a4bc
fix(test): resolve R lint and session attachment test failures
Fred-Wu Nov 11, 2026
550a08e
fix(tests): align plot fixtures and CI with session-aware viewers
Nov 12, 2026
0909977
fix: repair JGD reconnect and stabilize CI test fixtures
Nov 12, 2026
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
31 changes: 28 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
workflow_call:
inputs:
prerelease-date:
Expand All @@ -20,6 +22,8 @@ concurrency:

jobs:
test:
env:
VSCR_REQUIRE_PLOT_TESTS: '1'
strategy:
fail-fast: false
matrix:
Expand All @@ -45,7 +49,22 @@ jobs:
- name: Install R test dependencies
run: install.packages(c("remotes", "pkgbuild", "languageserver", "lintr", "styler", "renv"))
shell: Rscript {0}
- name: Install Linux plot system dependencies
if: runner.os == 'Linux'
# The unigd binary links libtiffxx.so.6, which the hosted image lacks.
run: |
sudo apt-get update
sudo apt-get install -y libtiffxx6
- run: pnpm run build
- name: Verify live plot lifecycle dependencies
run: |
packages <- c("processx", "later", "jsonlite", "jgd", "httpgd", "svglite")
for (package in packages) {
message("Loading plot test dependency: ", package)
loadNamespace(package)
}
stopifnot(packageVersion("httpgd") >= "2.0.1")
shell: Rscript {0}
- name: Verify managed jgd preparation
run: pnpm run test:prepare-jgd
- name: Verify managed rstudioapi preparation
Expand Down Expand Up @@ -88,14 +107,20 @@ jobs:
sudo apt-get update
sudo apt-get install -y tmux
VSCR_TEST_TMUX=1 pnpm exec mocha out/test/node/interactiveRuntime.test.js --ui tdd --timeout 60000 --grep "^Interactive real R runtime standalone agent survives its launcher process exiting$"
- name: Run Node tests
- name: Run Node tests including live plot lifecycle tests
run: pnpm run test:node
- name: Run plot lifecycle extension tests (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a pnpm run test:extension --label plot-lifecycle
- name: Run plot lifecycle extension tests
if: runner.os != 'Linux'
run: pnpm run test:extension --label plot-lifecycle
- name: Run extension tests (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a pnpm run test:extension
run: xvfb-run -a pnpm run test:extension --label provider-tests --label editor-command-tests --label interactive-integration
- name: Run extension tests
if: runner.os != 'Linux'
run: pnpm run test:extension
run: pnpm run test:extension --label provider-tests --label editor-command-tests --label interactive-integration
- name: Preserve extension test failure logs
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
Expand Down
14 changes: 13 additions & 1 deletion .vscode-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ const common = {
}
};

const plotSuites = [
'jgdPlotHistory', 'jgdSocketServer', 'standardPlotViewer', 'plotCommands',
'plotBackend', 'plotRestoration', 'httpgdViewer', 'viewerSession', 'sessionConnectionLifecycle'
];

// Keep mock/provider tests independent of R language-server startup. Editor
// command tests also need a fresh window after fixtures create terminals and
// webviews. The real Interactive suite has its own window with the LSP enabled.
Expand All @@ -32,10 +37,17 @@ export default defineConfig([
{
...common,
label: 'provider-tests',
files: 'out/test/suite/**/!(interactiveEditor|interactiveInputFeatures|completionMiddleware).test.js',
files: `out/test/suite/**/!(${['interactiveEditor', 'interactiveInputFeatures', 'completionMiddleware', ...plotSuites].join('|')}).test.js`,
mocha: { ...common.mocha, require: './out/test/common/extensionSetup.js' },
launchArgs: testProfile('provider-tests', false)
},
{
...common,
label: 'plot-lifecycle',
files: plotSuites.map(name => `out/test/suite/${name}.test.js`),
mocha: { ...common.mocha, require: './out/test/common/extensionSetup.js' },
launchArgs: testProfile('plots', false)
},
{
...common,
label: 'editor-command-tests',
Expand Down
10 changes: 6 additions & 4 deletions R/tests/attach_sess.R
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ writeLines(c(
" sys.source(file.path(getwd(), 'sess', 'R', 'server.R'), scheduler)",
" scheduler$.read_discovery <- function(path) list(endpoint='endpoint-b')",
" scheduler$.configure_discovery_jgd <- function(discovery, enabled) NULL",
" scheduler$runtime_stop <- function() invisible(NULL)",
" scheduler$runtime_stop <- function(preserve_plots=FALSE) invisible(NULL)",
" scheduler$connect <- function(endpoint, ...) {",
" connect_count <<- connect_count + 1L",
" observed[[connect_count]] <<- readLines(reconnect_file, warn=FALSE)",
Expand Down Expand Up @@ -410,14 +410,16 @@ writeLines(c(
" inherited <- vscode_r_startup_existing('endpoint')",
" if (!is.null(inherited)) stop('child reused the parent startup context')",
"}",
"ids <- character(); identities <- character(); deadline <- Sys.time() + 30",
"ids <- character(); identities <- character(); deadline <- Sys.time() + 30; read_error <- NULL",
"while (length(ids) < count && Sys.time() < deadline) {",
" requests <- list.files(directory, pattern='\\\\.request$', full.names=TRUE)",
" for (request in requests) {",
" id <- sub('\\\\.request$', '', basename(request))",
" if (id %in% ids) next",
# Requests can disappear or be briefly locked during cancellation on Windows.
# Retry on the next scan; the existing deadline still bounds persistent errors.
" lines <- suppressWarnings(tryCatch(readLines(request, warn=FALSE),",
" error=function(e) { if (!file.exists(request)) NULL else stop(e) }))",
" error=function(e) { read_error <<- conditionMessage(e); NULL }))",
" if (is.null(lines)) next",
" if (length(lines) != 5L || lines[[1L]] != 'vscode-r-sess-consent-v1' ||",
" lines[[2L]] != id) stop('bad request')",
Expand All @@ -431,7 +433,7 @@ writeLines(c(
" }",
" Sys.sleep(0.025)",
"}",
"if (length(ids) != count) stop('timed out waiting for consent requests')",
"if (length(ids) != count) stop('timed out waiting for consent requests: ', read_error)",
"writeLines(ids, log)",
"writeLines(identities, identity_log)",
"writeLines('ok', status_log)",
Expand Down
22 changes: 21 additions & 1 deletion R/tests/prepare-jgd.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,20 @@ local({
"jgd <- function() {",
" path <- Sys.getenv('JGD_TEST_DEVICE_LOG', '')",
" if (nzchar(path)) cat('jgd\\n', file=path, append=TRUE)",
# sess now tracks the opened device and attaches its session identity.
# A null PDF supplies a real device without requiring a JGD renderer.
" grDevices::pdf(NULL)",
" invisible(NULL)",
"}",
"jgd_server_info <- function() list(server_info=c(deviceId=as.character(grDevices::dev.cur())))",
"jgd_frame_ext <- function(value) {",
" path <- Sys.getenv('JGD_TEST_DEVICE_LOG', '')",
" if (nzchar(path)) cat(value, '\\n', file=paste0(path, '-frames'), append=TRUE)",
" invisible(NULL)",
"}"
), file.path(package, "R", "api.R"))
writeLines(c("export(fixture_device)", "export(jgd)"), file.path(package, "NAMESPACE"))
writeLines(c("export(fixture_device)", "export(jgd)",
"export(jgd_server_info)", "export(jgd_frame_ext)"), file.path(package, "NAMESPACE"))
previous <- setwd(root)
on.exit(setwd(previous), add = TRUE)
output <- system2_with_child_environment(
Expand Down Expand Up @@ -336,6 +346,16 @@ local({
"fixture-jgd-socket-next", "TRUE")))
stopifnot(identical(readLines(file.path(root, "real-approved-real-device"), warn = FALSE),
c("jgd", "jgd")))
frames <- lapply(readLines(file.path(root, "real-approved-real-device-frames"), warn = FALSE),
jsonlite::fromJSON)
stopifnot(length(frames) == 2L)
stopifnot(all(vapply(frames, function(frame) {
identity <- frame$vscodeR
nzchar(identity$sessionId) && nzchar(identity$pid) &&
nzchar(identity$deviceId) && identical(identity$restoring, FALSE)
}, logical(1))))
stopifnot(identical(frames[[1L]]$vscodeR$sessionId, frames[[2L]]$vscodeR$sessionId))
stopifnot(!identical(frames[[1L]]$vscodeR$deviceId, frames[[2L]]$vscodeR$deviceId))

approved_root <- file.path(root, "approved")
dir.create(approved_root)
Expand Down
25 changes: 0 additions & 25 deletions html/httpgd/index.ejs

This file was deleted.

Loading