Skip to content

Modern IDE-style UI, code assistance, and streaming output - #80

Merged
royteeuwen merged 9 commits into
mainfrom
feature/modern-ui-and-streaming
Jun 13, 2026
Merged

Modern IDE-style UI, code assistance, and streaming output#80
royteeuwen merged 9 commits into
mainfrom
feature/modern-ui-and-streaming

Conversation

@royteeuwen

@royteeuwen royteeuwen commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a modern, IDE-style console UI alongside the classic one, with code assistance and live streaming output, and makes the modern UI the default at /groovyconsole. The classic UI is modernized to drop its AEM-only dependencies so it also works on plain Sling. All changes are backwards compatible for existing API clients (e.g. the IntelliJ plugin).

Built on top of the Groovy 5 / Java 11 baseline already on main.

What's included

Modern UI (ui.frontend — Lit + Spectrum Web Components + Monaco, Vite)

  • IDE shell: resizable editor/output split, tabbed output dock (Log default, Result, Table, Trace) with a running-time badge, activity-rail drawers for History / Scheduled Jobs / Help, and a status bar with run state + shortcuts.
  • No AEM Granite/Coral dependency — runs on AEM and plain Sling. Built into ui.apps at /apps/groovyconsole/spa.

Code assistance (/bin/groovyconsole/assist/*)

  • Class completion over the OSGi class space (with auto-import), member/method completion incl. Groovy GDK methods, binding & star-import awareness, hover docs, OSGi service-name completion inside getService("..."), and live compile diagnostics (parse-only, sharing the execution compiler config via the extracted GroovyShellFactory).

Streaming output

  • POST /bin/groovyconsole/post.json?async=true{executionId}, poll GET /bin/groovyconsole/stream.json; output streams while the script runs. Both UIs consume it (classic streams into its output panel). Without async the endpoint is unchanged, so curl / the IntelliJ plugin keep working.
  • Hardened for AEMaaCS: Cache-Control: no-store on polls, graceful degradation when a poll is routed to another cluster node.

Routing & config

  • New Default UI OSGi property (modern by default). Both UIs always reachable via .modern.html / .classic.html selectors and cross-link each other.

Classic UI modernization

  • Replaced the AEM-only ExtJS Open/Save dialogs with Bootstrap modals that also work on plain Sling; dropped cq.wcm.edit and cq.shared clientlib deps (CSRF token now via granite.csrf.standalone).

Fixes

  • ScheduledJobsServlet NPE on plain Sling (no next execution date).
  • Permission gate added to the services listing servlet.
  • CSRF token handling for modern-UI POST/DELETE on AEM author.

Tooling & tests

  • New ui.tests module with Playwright e2e; run Maven profile (mvn verify -Prun -pl it.tests) to launch the aggregated feature model for manual testing (shared config via pluginManagement).
  • AssistIT + StreamingIT integration tests.
  • README and CHANGELOG updated.

Verification

  • Full reactor clean install with bnd baseline enforced (no skip).
  • 29 integration tests + 27 Playwright e2e tests green.
  • Manually verified on the AEM as a Cloud Service SDK (Tools nav entry, Run-on-publish, streaming in both UIs, save/open modals, defaultUi toggle, assist over AEM APIs).

Notes for reviewers

  • A companion branch adds streaming support to the IntelliJ plugin (bobi/aem-groovyconsole-plugin) — kept separate, not part of this PR.

@royteeuwen
royteeuwen force-pushed the feature/modern-ui-and-streaming branch 2 times, most recently from 76ca186 to 5af6a9d Compare June 13, 2026 20:09
Modern UI (new ui.frontend module — Lit + Spectrum Web Components + Monaco, Vite):
- IDE-style shell: app bar with primary Run action, resizable editor/output
  split, tabbed output dock (Result/Log/Trace/Table) with running-time badge
  and clickable stacktrace frames that jump to the source line, activity rail
  with History / Scheduled Jobs / Help drawers, status bar with run state,
  cursor position and shortcut hints (Cmd+Enter run, Cmd+S save, Cmd+K commands)
- IDE-grade code assistance backed by new /bin/groovyconsole/assist/* servlets:
  class dictionary from OSGi bundle wiring (with auto-import), member
  completion via reflection incl. Groovy metaclass/GDK methods, binding and
  star-import context, and live compile diagnostics (parse-only, shared shell
  configuration with execution via the extracted GroovyShellFactory)
- Folder-navigable script browser and save dialog that also work on plain Sling
- Built into ui.apps at /apps/groovyconsole/spa with stable asset names

Routing and configuration:
- New defaultUi OSGi property (classic|modern, default classic); /groovyconsole
  serves the default while the classic/modern selectors always work; both UIs
  cross-link each other

Classic UI modernization:
- Replace the AEM-only ExtJS open/save dialogs with Bootstrap modals that also
  work on plain Sling; drop cq.wcm.edit and cq.shared clientlib dependencies
  (context path now injected via HTL); delete the obsolete groovyconsole-aem
  ExtJS dialog definitions
- Fix pre-existing NPE in ScheduledJobsServlet on plain Sling and add the
  permission gate to ServicesListServlet

Testing and infrastructure:
- New ui.tests module: Playwright end-to-end suite behind -Pui-tests, booting
  its own Sling feature-model instance; AssistIT integration tests; CI updated
Scripts can now be executed asynchronously (POST /bin/groovyconsole/post with
async=true) returning an execution id; output is polled incrementally from the
new /bin/groovyconsole/stream endpoint while the script runs. The execution
registry buffers output per execution, retains finished executions for ten
minutes, and audits results exactly as synchronous runs do.

Both UIs consume it: the modern UI shows a live output dock during the run and
the classic UI appends to its output panel. Both fall back transparently to
synchronous execution against older backends.

Backwards compatible: without the async parameter the endpoint behaves exactly
as before, keeping external clients such as the IntelliJ AEM Groovy Console
plugin working unchanged. Covered by StreamingIT (including a test proving
compile-only semantics and partial output before completion) and Playwright
specs for both UIs.
- Send Cache-Control: no-store on the stream polling endpoint so responses
  are never cached by a CDN (AEM as a Cloud Service)
- Degrade gracefully in both UIs when polling loses the execution (e.g. the
  request is routed to another cluster instance): explain that the script
  keeps running and point to the History panel, instead of reporting a
  failure
- Document the async/stream API contract and the cluster affinity caveat in
  the README
- Gate StreamingIT on actual endpoint readiness instead of only the system
  health check to avoid a startup race
Manual testing on the AEMaaCS SDK surfaced that browser POSTs were rejected
with 403 by the Granite CSRF filter (header-authenticated clients such as
curl and the IntelliJ plugin are unaffected):

- The modern UI now fetches /libs/granite/csrf/token.json and sends the
  CSRF-Token header on POST/DELETE requests when running on AEM (skipped on
  plain Sling)
- The classic UI's AEM clientlib declares the granite.csrf.standalone
  dependency, restoring the automatic token injection previously provided
  transitively by the removed cq.shared dependency
Extracts the shared Sling feature aggregation and launch configuration into
pluginManagement so the existing 'it' profile and a new 'run' profile reuse it
without duplication.

  mvn clean install
  mvn verify -Prun -pl it.tests   # boots Sling on http://localhost:8080 (admin/admin)

The run profile launches the same aggregated feature the integration tests use,
on a fixed port (override with -Dhttp.port), and blocks in the foreground tailing
the instance log; Ctrl+C stops it.
…ceholders

- The output dock now selects Log (script output) by default on completion, with
  Result as a secondary tab — matching the classic console where the log was the
  primary view. Errors still jump to the Trace tab.
- Widen the slide-out drawers (720 -> 960px) and keep timestamp links on one line
  so History/Scheduled Jobs dates no longer wrap.
- Mute sp-textfield placeholders (grey italic) in the scheduler and save dialogs;
  Spectrum renders them in the text colour by default, making empty fields look
  pre-filled.
- Change the Default UI OSGi property default from classic to modern, so
  /groovyconsole serves the modern UI out of the box (classic still reachable
  via the .classic.html selector).
- Document the modern UI, code assistance, streaming output, the run profile and
  the Default UI setting in the README, and record everything under [Unreleased]
  in the CHANGELOG.
@royteeuwen
royteeuwen force-pushed the feature/modern-ui-and-streaming branch from 5af6a9d to 4df7a96 Compare June 13, 2026 20:32
on: [push, pull_request] fired both events for same-repo PR branches, running
every commit twice. Restrict push to main and add a concurrency group so only
one run happens per PR commit and superseded runs are cancelled.
- Order static imports before regular imports in the new/modified Groovy servlets
  and models (Groovy rule S8314).
- Modernize the classic console.js code added for streaming and the open/save
  dialogs: const/let over var, optional chaining, globalThis, and a fixed regex
  character-class escape.
- Extract a SEVERITY_ERROR constant for the repeated compile-marker severity.
- Suppress S2925 on StreamingIT, where Thread.sleep is intentional remote polling
  (matching GroovyConsoleServiceIT).

Left as-is: the Bootstrap modal role="dialog" attributes (S6819) — Bootstrap 3
modals require the role and a native <dialog> would break their show/hide.
@sonarqubecloud

Copy link
Copy Markdown

@royteeuwen
royteeuwen merged commit 8726b38 into main Jun 13, 2026
3 of 4 checks passed
@royteeuwen
royteeuwen deleted the feature/modern-ui-and-streaming branch June 13, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant