Report a problem or share an observation from Studio, with enough context to make it useful.
Help ▸ Send Feedback… — in the native menu bar and its egui fallback, in the header's MORE ▾ ▸ Help menu (the browser shell's door), and in the command palette (help.send-feedback). The command is omitted unless the shell has a community backend (community_available). Programmatic: ShellCommand::OpenFeedbackDialog; browser shell_command {"type":"openFeedbackDialog"}.
What the user gets#
Opening the dialog first captures a screenshot of the workbench as it stands (the dialog stays invisible until the capture lands, so it never photographs itself), then offers: a free-text note (capped 4000 chars, live), the screenshot as a thumbnail with an "include screenshot" opt-out and a "retake" button (re-hides the window for a frame or two), a byline note (signed-in login, or "sending anonymously"), and send. Success is a toast; failure is an inline error with the text preserved. Shells that can't answer the capture (volta-shot, the plugin window, kittest) open with "no screenshot captured on this shell".
Wire#
CommunityQuery::Feedback { body, meta, shot_b64 } → POST /api/feedback with { body, meta?, shot? } — shot is base64 PNG, downscaled client-side until ≤ 4 MiB decoded. Anonymous OK; session uid attached server-side when a Bearer rides along. Replies: 202 {ok, id}, else 400 with a human message (an invalid, oversized or non-PNG shot is a 400, never silently dropped); the global per-IP POST limiter applies. The admin reads below answer 403 without the token.
Storage & reading it#
D1 feedback(id, uid, body, meta, shot_key, at) (migration 0008); PNG at R2 feedback/${id}.png; 400-day retention via the cron sweep (rows + blobs). Read with the admin token:
curl -H "Authorization: Bearer $ADMIN_TOKEN" $SERVER/api/admin/feedback
curl -H "Authorization: Bearer $ADMIN_TOKEN" $SERVER/api/admin/feedback/<id>/shot > shot.png
meta records { shell: "native"|"web", version, view } — view in the shell-state token vocabulary (bench, mixer, editor, …).
Code anchors#
ui/src/app/dialogs.rs—open_feedback_dialog/feedback_window/encode_feedback_shot(capture pump, PNG+base64,FEEDBACK_*caps)ui/src/bridge.rs—CommunityQuery::Feedback(wire())ui/src/community.rs—Pending::Feedback,start_feedback/take_feedback_result(the panel owns the only reply drain)web/src/handlers.ts—fileFeedback,adminFeedback,adminFeedbackShotweb/src/maintenance.ts— retention sweep- tests:
ui/tests/behavior.rs feedback_dialog_sends_through_community_seam,web/test/feedback.test.ts