feat(gnoweb): built-in playground(2)#5421
Conversation
🛠 PR Checks Summary🔴 Changes related to gnoweb must be reviewed by its codeowners Manual Checks (for Reviewers):
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:
☑️ Reviewer Actions:
📚 Resources:Debug
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
- 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
…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
|
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. |
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.
|
status? |
moul
left a comment
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 != "" { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Note: Compare with faucet IP spoofing control. #5353
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| pkg_path: `${domain}/r/playground_preview`, |
There was a problem hiding this comment.
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.
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:
If I run gnoweb with |
|
|
||
| ### 1. `/_/play` — Playground scratch pad | ||
|
|
||
| A multi-file code editor backed by a plain `<textarea>` (no CodeMirror yet). |
There was a problem hiding this comment.
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.
@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 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. |

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?evalon any realm page — Expression evaluator with live qeval against the node, quick-call buttons for all read-only functions, history with re-run?forkon any package/realm — Loads all .gno source files into the playground editorPOST /_/api/eval— JSON API for expression evaluationGET /_/api/funcs?path=...— JSON API for listing package functionsArchitecture
vm/qevalRPC queriesgno.land/pkg/gnoweb/— same binary, same deployWhat's NOT done yet (follow-up work)
gno run/gno test/gno fmtDesign principles
gnowebembeds everythingTest plan
?evalon a live realm?forkloads source correctly/_/playscratch pad