Skip to content

feat(gnoweb): built-in playground(2)#5421

Open
moul wants to merge 48 commits into
masterfrom
playground2
Open

feat(gnoweb): built-in playground(2)#5421
moul wants to merge 48 commits into
masterfrom
playground2

Conversation

@moul

@moul moul commented Apr 3, 2026

Copy link
Copy Markdown
Member

Summary

Exploration PR: add playground capabilities directly into gnoweb instead of a separate standalone app (replacing the need for gnostudio/studio).

Context: The current play.gno.land is a complex React+WASM app in a separate repo. This PR explores integrating interactive features directly into gnoweb as a Go-native, single-binary solution.

Related:

New features

  • /_/play — Scratch pad editor with multi-file tabs, keyboard shortcuts (Ctrl+Enter = run), share via URL
  • ?eval on any realm page — Expression evaluator with live qeval against the node, quick-call buttons for all read-only functions, history with re-run
  • ?fork on any package/realm — Loads all .gno source files into the playground editor
  • POST /_/api/eval — JSON API for expression evaluation
  • GET /_/api/funcs?path=... — JSON API for listing package functions
  • "Eval" and "Fork" nav links added to header for realm/package pages

Architecture

  • Pure Go backend + vanilla JS (no React, no npm at runtime)
  • Server-side execution via existing vm/qeval RPC queries
  • All new code lives in gno.land/pkg/gnoweb/ — same binary, same deploy
  • Frontend uses the existing controller pattern (data-controller attributes + lazy-loaded JS)
  • ~460 lines of Go, ~280 lines of TS, ~270 lines of CSS

What's NOT done yet (follow-up work)

  • CodeMirror editor integration (currently textarea)
  • Server-side gno run / gno test / gno fmt
  • WebSocket REPL
  • Wallet/Adena integration for deploy
  • gnodev hot-reload integration via SSE
  • Sandbox/rate-limiting for public instances

Design principles

  1. Go-only — No Node.js runtime dependency
  2. Part of gnoweb — Not a separate app
  3. Server-side execution — Real node via RPC, no WASM
  4. Context-aware — Knows what realm/package you're viewing
  5. Progressive — Simple eval → scratch pad → full editor
  6. Single binarygnoweb embeds everything

Test plan

  • All existing gnoweb tests pass
  • Go compiles clean
  • TypeScript compiles clean (esbuild)
  • Manual test with gnodev
  • Test ?eval on a live realm
  • Test ?fork loads source correctly
  • Test /_/play scratch pad

@Gno2D2

Gno2D2 commented Apr 3, 2026

Copy link
Copy Markdown
Collaborator

🛠 PR Checks Summary

🔴 Changes related to gnoweb must be reviewed by its codeowners

Manual Checks (for Reviewers):
  • IGNORE the bot requirements for this PR (force green CI check)
Read More

🤖 This bot helps streamline PR reviews by verifying automated checks and providing guidance for contributors and reviewers.

✅ Automated Checks (for Contributors):

🔴 Changes related to gnoweb must be reviewed by its codeowners

☑️ Contributor Actions:
  1. Fix any issues flagged by automated checks.
  2. Follow the Contributor Checklist to ensure your PR is ready for review.
    • Add new tests, or document why they are unnecessary.
    • Provide clear examples/screenshots, if necessary.
    • Update documentation, if required.
    • Ensure no breaking changes, or include BREAKING CHANGE notes.
    • Link related issues/PRs, where applicable.
☑️ Reviewer Actions:
  1. Complete manual checks for the PR, including the guidelines and additional checks if applicable.
📚 Resources:
Debug
Automated Checks
Changes related to gnoweb must be reviewed by its codeowners

If

🟢 Condition met
└── 🟢 And
    ├── 🟢 The base branch matches this pattern: ^master$
    └── 🟢 A changed file matches this pattern: ^gno.land/pkg/gnoweb/ (filename: gno.land/pkg/gnoweb/Makefile)

Then

🔴 Requirement not satisfied
└── 🔴 Or
    ├── 🔴 Or
    │   ├── 🔴 And
    │   │   ├── 🔴 Pull request author is user: alexiscolin
    │   │   └── 🔴 This user reviewed pull request: gfanton (with state "APPROVED")
    │   └── 🔴 And
    │       ├── 🔴 Pull request author is user: gfanton
    │       └── 🔴 This user reviewed pull request: alexiscolin (with state "APPROVED")
    └── 🔴 And
        ├── 🟢 Not (🔴 Pull request author is user: alexiscolin)
        ├── 🟢 Not (🔴 Pull request author is user: gfanton)
        └── 🔴 Or
            ├── 🔴 This user reviewed pull request: alexiscolin (with state "APPROVED")
            └── 🔴 This user reviewed pull request: gfanton (with state "APPROVED")

Manual Checks
**IGNORE** the bot requirements for this PR (force green CI check)

If

🟢 Condition met
└── 🟢 On every pull request

Can be checked by

  • Any user with comment edit permission

@codecov

codecov Bot commented Apr 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.83333% with 46 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
gno.land/pkg/gnoweb/handler_playground.go 80.50% 16 Missing and 7 partials ⚠️
gno.land/pkg/gnoweb/handler_http.go 77.77% 12 Missing and 4 partials ⚠️
gno.land/pkg/gnoweb/components/view_run.go 0.00% 4 Missing ⚠️
gno.land/pkg/gnoweb/client.go 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@alexiscolin alexiscolin added the a/ux User experience, product, marketing community, developer experience team label Apr 7, 2026
@moul moul changed the title feat(gnoweb): integrated playground, eval, and fork views feat(gnoweb): built-in playground(2) Apr 8, 2026
moul added 3 commits April 9, 2026 16:21
- pre-allocate `funcs` slice in BuildEvalFuncs (prealloc lint)
- fix biome formatting in controller-eval.ts and controller-playground.ts
…code

- Add TestHTTPHandler_PlaygroundPage, TestHTTPHandler_EvalView, TestHTTPHandler_ForkView
- Add TestHandlerPlaygroundEval and TestHandlerPlaygroundFuncs
- Remove unused playgroundViewParams wrapper and ui/playground_editor template
@alexiscolin alexiscolin assigned jeronimoalbi and unassigned moul May 18, 2026
…ns (#5666)

Changes playground output to optionally support copying single lines
from the output, which can be useful when multiple commands are printed
between text and links.

Related to #5421
…5674)

The new built-in Playground changes include a run view where Gno code
for run TXs can be written. This PR changes the textarea for a
CodeMirror editor, the same used by the Playground, same configuration.

The `gnokey` commands copy buttons were moved into "inline" copy icons
to follow Gnoweb patterns.

Related to #5421
@moul

moul commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Status on this one?

@jefft0

jefft0 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Status on this one?

As a checkpoint, @alexiscolin just merged PR #5774 into this playground2 branch. I'm made a PR against playground2 to fix the conflicts with master and do fixes that we decide on. #5849

This idea is to make small PRs against playground2, then merge this PR into master when it's good.

@alexiscolin

Copy link
Copy Markdown
Member

Status on this one?

As a checkpoint, @alexiscolin just merged PR #5774 into this playground2 branch. I'm made a PR against playground2 to fix the conflicts with master and do fixes that we decide on. #5849

This idea is to make small PRs against playground2, then merge this PR into master when it's good.

Agree on that we should merge that PR if it works and make smaller PRs to improve it.

@jefft0 jefft0 assigned jefft0 and unassigned jeronimoalbi Jun 24, 2026
Resolve conflicts between the built-in playground/run/eval feature and the
State Explorer feature that landed on master. master is the source of truth;
the playground is adapted to master's logic, both features coexist.

Resolutions:
- client.go / client_mock.go: ClientAdapter unions Doc(height)+Eval+State*;
  Eval adapted to the 4-arg query(...,height) signature (height 0 = latest).
- handler_http.go: NewHTTPHandler wires State + Playground + Run together;
  playgroundClientAdapter bridges to master's height-aware ClientAdapter
  (File/Doc/ListFiles called with height 0).
- weburl/url.go: keep master's reGnolandPath validation + height pinning;
  add the PR's IsPlayground(); drop the PR's looser rePkgPath/reUserPath.
- components: header links union to Content/State/Source/Actions/Fork/Run;
  layout view-type switch unions State+Playground+Run; tests updated.
- handler_http_test.go: keep both test suites (playground/fork + state/DoS).
- Makefile / frontend (utils.ts, controller.ts, index.ts, css): unions;
  index.ts keeps master's versionSuffix cache-busting (drop PR duplicate).

Verified: go build (pkg+cmd), go vet, golangci-lint (0 issues), biome,
and the full ./pkg/gnoweb/... test suite all pass.
Rebuild public/js/* and public/main.css from the merged TS/CSS sources
(make generate) and reconcile package-lock.json against the union of
master's and the playground feature's dependencies (CodeMirror + postcss).
No source changes; generated artifacts only.
@alexiscolin alexiscolin marked this pull request as ready for review June 30, 2026 04:15
@moul

moul commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

status?

@moul moul left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the current playground2 head (13124c534). I left a few actionable comments around the new eval/playground flow.

}

var req evalRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This public endpoint decodes an unbounded JSON body. A single request can force large allocations by sending a huge pkg_path or expression, and the request-count limiter does not protect against payload size. Please wrap the body with http.MaxBytesReader and add explicit length limits for pkg_path / expression before calling Eval.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. See PR #5897 and the question if the size limits for pkg_path and expression are good.

// clientIP extracts the real client IP, respecting X-Forwarded-For when
// present (first hop wins; downstream proxies should overwrite it).
func clientIP(r *http.Request) string {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trusts X-Forwarded-For from every client, so callers can bypass the eval rate limit by sending a different XFF value per request. The state limiter already has trusted-proxy handling; this endpoint should either use RemoteAddr only or share that trusted-proxy extraction instead of accepting spoofable forwarded headers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Compare with faucet IP spoofing control. #5353

method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
pkg_path: `${domain}/r/playground_preview`,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not evaluate the editor contents. The Run button detects func Render, but then qevals the fixed package ${domain}/r/playground_preview; the playground never builds or deploys that package from code / this.files. On a fresh install this will fail with package-not-found, and if such a package exists it evaluates unrelated chain state. Until scratch execution exists server-side, this branch should show the local/deploy instructions or send the actual source to an endpoint that can compile it.

@jefft0

jefft0 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

status?

I looked into a problem in the code editor. When I run in gnodev, http://localhost:8888/r/demo/counter/$run looks correct. But when I run gnoland and gnoweb separately, it looks like this:

image

If I run gnoweb with -no-strict, then it looks correct. This is because in strict mode the Content-Security-Policy prevents CodeMirror from dynamically injecting the HTML elements that it needs to do for the editor. Do we care about getting the editor to work correctly in strict mode?


### 1. `/_/play` — Playground scratch pad

A multi-file code editor backed by a plain `<textarea>` (no CodeMirror yet).

@jefft0 jefft0 Jul 2, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now out of date. PR #5674 was merged into this PR branch which implements CodeMirror. When we are ready to merge to master, we should update this ADR.

@jeronimoalbi

Copy link
Copy Markdown
Contributor

status?

@alexiscolin pointed out some improvements and security fixes which are not yet addressed, some of which are the issues that you're mentioning. They could be checked in #5774.

Playground has no server side format, test and run support, instead commands are printed as output.

I think a deploy button should be added which could show instructions on how to do it with gnokey to complete the user's journey, until there is wallet integration support.

A link to the Playground should be added somewhere too, because right now there is no link to open it. Also Playground UI probably should be improved.

Forking is supported via "Fork" tab or when a source file is being viewed though the "Edit" link. Forking is a special case of the playground where there is a "realm context", so the functionality is basically the same as Playground at this point.

Gno expression eval support is implemented within the "Actions" tabs. In the PoC there was an "Eval" tab if I recall correctly, which is now merged into the "Actions" one.

The "Run" tab for run TXs should work, it displays the commands to run the TX until wallet integration is implemented. It has an editor with syntax highlighting and script download support.

Another look could be taken to further improve UI/UX.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a/ux User experience, product, marketing community, developer experience team 🌍 gnoweb Issues & PRs related to gnoweb and render 📦 ⛰️ gno.land Issues or PRs gno.land package related

Projects

Status: 📥 Inbox
Status: In Progress

Development

Successfully merging this pull request may close these issues.

6 participants