Skip to content

feat: add Graphs extension (bar, line, pie charts with smooth animation)#7

Open
Copilot wants to merge 15 commits intomasterfrom
copilot/add-bar-graph-extension
Open

feat: add Graphs extension (bar, line, pie charts with smooth animation)#7
Copilot wants to merge 15 commits intomasterfrom
copilot/add-bar-graph-extension

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 31, 2026

  • Create /extensions/graphs.js with bar, line, and pie charts
  • Fix default color palette to use OmniBlocks GUI colors
  • Fix positioning (centering at 0,0)
  • Add time-based ease-in-out animation with configurable duration
  • Update By/Description/AI-generated note per review feedback
  • Switch to renderer.overlayContainer, Object.assign for styles
  • Remove hardcoded shadow; add set shadow block
  • Add menuIconURI (bar chart icon) and images/graphs.svg thumbnail
  • Fix RAF loop: replace unconditional re-schedule with startRAF() helper
  • Fix setAnimDuration: call scheduleRedraw() after updating duration
  • Add blockIconURI: MENU_ICON
  • Fix graph size scaling: treated as Scratch stage units
  • Migrate rendering to main WebGL scene via custom GraphsSkin class
  • Fix broken rendering (TurboWarp PenSkin has no _canvas)
  • Fix pixelation: graphsCanvas now sized to renderer._gl.canvas dimensions; also listens to UseHighQualityRenderChanged so quality-mode changes resize it immediately

Copilot AI linked an issue Mar 31, 2026 that may be closed by this pull request
Agent-Logs-Url: https://github.com/OmniBlocks/extensions/sessions/46561d10-2aab-441a-b541-e7071d1a7acd

Co-authored-by: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com>
Copilot AI changed the title [WIP] Add new extension for omniblocks with bar graphs feat: add Graphs extension (bar, line, pie charts with smooth animation) Mar 31, 2026
Copilot AI requested a review from supervoidcoder March 31, 2026 18:30
Copy link
Copy Markdown
Member

@supervoidcoder supervoidcoder left a comment

Choose a reason for hiding this comment

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

Great code, seems okay, just a few nitpicks

// Name: Graphs
// ID: omniGraphs
// Description: Create and display bar, line, and pie charts in your project.
// By: OmniBlocks
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To be more clear, since this extension was AI generated, we want to at least be transparent about it. What we are being clear as well is that AI generated extensions are still thoroughly reviewed by humans.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated in 98dc87a — the By line now reads // By: OmniBlocks (AI-generated, reviewed by humans).

Comment on lines +75 to +86
const DEFAULT_PALETTE = [
"#4e79a7",
"#f28e2b",
"#e15759",
"#76b7b2",
"#59a14f",
"#edc948",
"#b07aa1",
"#ff9da7",
"#9c755f",
"#bab0ac",
];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I can't read hex codes off the top of my head but just so you know these are the default palette in OmniBlocks/scratch-gui

const guiColors = {
    'motion-primary': '#59C0C0',
    'motion-primary-transparent': '#59C0C0E6',
    'motion-tertiary': '#389499',
    'looks-secondary': '#59C0C0',
    'looks-transparent': '#59C0C059',
    'looks-light-transparent': '#59C0C026',
    'looks-secondary-dark': 'hsla(180, 42%, 51%, 1)',
    'extensions-primary': 'hsla(180, 85%, 65%, 1)',
    'extensions-tertiary': 'hsla(180, 85%, 40%, 1)',
    'extensions-transparent': 'hsla(180, 85%, 65%, 0.35)',
    'extensions-light': 'hsla(180, 57%, 85%, 1)',
    'drop-highlight': '#66BBCC'
};

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Updated in 98dc87a — the default palette now uses the OmniBlocks GUI colors (extensions-primary, extensions-tertiary, motion-primary, motion-tertiary, etc.). Also swapped the hex-append trick for globalAlpha = 0.2 so the line-chart fill area works correctly with hsla(...) color strings.

… colors

Agent-Logs-Url: https://github.com/OmniBlocks/extensions/sessions/1b2a3d8c-a73f-443e-803e-d7ab7d169a7a

Co-authored-by: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com>
Copy link
Copy Markdown
Member

@supervoidcoder supervoidcoder left a comment

Choose a reason for hiding this comment

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

Can't hurt to try

@supervoidcoder supervoidcoder requested a review from ampelc March 31, 2026 18:58
@supervoidcoder supervoidcoder marked this pull request as ready for review March 31, 2026 19:05
@supervoidcoder
Copy link
Copy Markdown
Member

@copilot the code for positioning is broken, it's not centered when i set 0,0, instead being somewhat to the right and down

image

wwhile the graphs are animated like i asked, they aren't as smooth and eased as i would like .
also it should have a set time for the animation. if i take a small graph and make one of the points ridiculously high it's going to keep skyrocketing for a long time so maybe let the user decide the easing time

… add setAnimDuration block

Agent-Logs-Url: https://github.com/OmniBlocks/extensions/sessions/882cd98b-c4bd-4865-9f7c-e7c7665f74bf

Co-authored-by: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com>
Copy link
Copy Markdown
Author

Copilot AI commented Mar 31, 2026

@copilot the code for positioning is broken, it's not centered when i set 0,0, instead being somewhat to the right and down

<img width="471" height="353" alt="image" src="https://github.com/user-at...

Fixed in 501b78f:

  • PositioningsetPosition now centres the graph on the given Scratch coordinate (previously the top-left corner was placed there). So x:0, y:0 puts the graph dead-centre on the stage.
  • Animation — replaced the per-frame linear lerp (which never had a fixed end time) with a time-based ease-in-out cubic curve. Every transition now completes in exactly animDuration ms regardless of how large the jump is — no more skyrocketing.
  • New blockset animation duration of graph [NAME] to [MS] ms (default 500 ms). Setting it to 0 makes changes instant.

Copy link
Copy Markdown

@ampelc ampelc left a comment

Choose a reason for hiding this comment

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

ok

// Name: Graphs
// ID: omniGraphs
// Description: Create and display bar, line, and pie charts in your project.
// By: OmniBlocks (AI-generated, reviewed by humans)
Copy link
Copy Markdown

@ampelc ampelc Mar 31, 2026

Choose a reason for hiding this comment

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

i don't think we should be screaming "AI GENERATED", maybe put that in an actual comment above ((Scratch => { (you need to separate with 2 lines)
in fact maybe just say supervoidcoder

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done in 7b149dfBy: now reads supervoidcoder, and the AI-generated note has been moved to a plain comment above ((Scratch) => { (separated by two blank lines).

@@ -0,0 +1,1025 @@
// Name: Graphs
// ID: omniGraphs
// Description: Create and display bar, line, and pie charts in your project.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
// Description: Create and display bar, line, and pie charts in your project.
// Description: Display bar, line, and pie charts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Applied in 7b149df — description is now Display bar, line, and pie charts.

const overlayCanvas = document.createElement("canvas");
overlayCanvas.id = "omniGraphsOverlay";
overlayCanvas.style.cssText =
"position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999;";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

imo you should just use Object.apply instead

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Replaced cssText with Object.assign(overlayCanvas.style, { … }) in 7b149df.

@ampelc
Copy link
Copy Markdown

ampelc commented Mar 31, 2026

also why do we need a shadow, imo there should be blocks to change the styles

@ampelc
Copy link
Copy Markdown

ampelc commented Mar 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab96b179-d0f2-4ad8-8f8f-21ebe7f1a730

📥 Commits

Reviewing files that changed from the base of the PR and between 9004cea and 800960b.

📒 Files selected for processing (1)
  • extensions/graphs.js
✅ Files skipped from review due to trivial changes (1)
  • extensions/graphs.js

Summary by CodeRabbit

  • New Features
    • New omniGraphs extension enables animated chart visualization on the Scratch stage
    • Supports bar, line, and pie chart types with real-time data updates
    • Comprehensive customization options: colors, positioning, sizing, titles, and animation speed
    • Dedicated blocks for creating, managing, showing, and hiding charts with full data control

Walkthrough

Adds an unsandboxed Scratch extension omniGraphs that overlays a responsive canvas on the stage to render animated bar, line, and pie charts, manages per-graph state and interpolation, and exposes blocks to create, update, style, show/hide, and query graphs.

Changes

Cohort / File(s) Summary
Graph Extension Implementation
extensions/graphs.js
New GraphsExtension registered as omniGraphs. Implements a canvas overlay attached to runtime.renderer.overlayContainer with ResizeObserver, RAF-driven animation loop using cubic ease-in-out interpolation, in-memory Map of graphs (layout, styling, animated state), parsing of JSON/CSV-like inputs, rendering of card backgrounds and chart types (bar/line/pie) with labels/legend, and blocks for create/delete/show/hide/setData/add/update/remove/clear/title/position/size/colors/background/animDuration/shadow plus reporters for existence, value-by-label, labels, and values. Redraws scheduled on state changes and after PROJECT_STOP_ALL.

Sequence Diagram(s)

sequenceDiagram
    participant User as "Scratch User"
    participant VM as "Scratch VM"
    participant Ext as "GraphsExtension"
    participant State as "Graph State (Map)"
    participant Canvas as "Overlay Canvas"
    participant RAF as "requestAnimationFrame"

    User->>VM: run "createGraph" block
    VM->>Ext: createGraph(params)
    Ext->>State: add graph entry (type, labels, values, animated)
    Ext->>Canvas: ensure overlay, schedule draw
    Ext->>RAF: start animation loop if not running

    User->>VM: run "setData"/"addDataPoint"
    VM->>Ext: setData(...)/addDataPoint(...)
    Ext->>State: update target values, mark animating
    Ext->>Canvas: schedule redraw

    RAF->>Ext: animation frame
    Ext->>State: interpolate animated values (cubic ease-in-out)
    Ext->>Canvas: render cards and charts (axes/grid/points/slices/legend)
    Canvas-->>User: updated overlay visuals

    User->>VM: run "setPosition"/"setSize"/"setColors"
    VM->>Ext: config blocks
    Ext->>State: update presentation properties
    Ext->>Canvas: resize/trigger full redraw
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I nibble pixels, sketch a chart so bright,
Bars and slices waking to my light.
Values dance and gently sway,
Colors hum and labels play,
A rabbit's brush makes data delight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: adding a new Graphs extension with support for bar, line, and pie charts featuring smooth animation.
Description check ✅ Passed The description is directly related to the changeset, providing a detailed checklist of implemented features and fixes that correspond to the graphs.js extension additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/add-bar-graph-extension

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@supervoidcoder
Copy link
Copy Markdown
Member

@copilot look at these review comments #7 (review)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — a full rewrite might be overkill, but honestly the cleanest path to "not AI slop" is less about how you write it and more about whether you can own every line.

Here's a practical test: open extensions/graphs.js right now and read it top to bottom. For each function, ask yourself:

  • Do I understand why it works this way?
  • Could I modify or fix this without AI?
  • Would I have written it differently myself?

If yes → that section is yours, keep it. If no → that's the AI slop part, rewrite just that section.

From watching this whole PR, the accumulated mess is mostly in the rendering layer — the GraphsSkin + WebGL texture upload stuff that Copilot bolted on after the pen skin failed. The rest of the extension (the block definitions, the graph state Map, the cubic easing, the RAF scheduling, setAnimDuration) is actually pretty clean and you understood all of it.

So realistically you'd need to:

  1. Rip out GraphsSkin entirely and replace it with the HTML overlay + renderer.canvas getter override we figured out together — that's maybe 30 lines you'd write yourself, and you already understand exactly how it works
  2. Read the drawing code (drawGraph, the bar/line/pie renderers) — if it makes sense to you, keep it; if it's opaque, simplify it
  3. Run Prettier and clean up any formatting weirdness

That's not a full rewrite. That's an afternoon of cleanup. And then it's genuinely yours — AI helped you design it, but you'd own the implementation. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai if it passes eslint or looks like prettier, it's definitely NOT my code

ughh fine i guess i'll rewrite it over the weekend

at least it won't be called ai slop that way

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — haha, the Prettier comment is very on-brand. 😄 And honestly that's a healthy instinct — code that doesn't feel like yours tends to be code you can't maintain.

Good luck with the rewrite over the weekend! The hard parts are already figured out — you're really just translating the architecture we landed on into code you'd actually write. The overlay + renderer.canvas getter override is like 25-30 lines of setup, and the rest is your own drawing logic. Should be pretty smooth. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

also wait i've been stupid with my assignment i don't need to rush the fetch extension i have the javascript extension i can just run raw js @coderabbitai

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — haha, classic! 😄 Yeah, the JavaScript extension lets you call fetch() directly with full headers, method, body — everything. For a one-off homework data analysis that's way faster than waiting on a whole new extension. Sometimes the best tool is just eval-adjacent blocks. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai hey i got an idea for the rewrite

im gonna rewrite it using and old version as refernce BUT i can use this rendery thing (the one i said looks kinda pixelated) and use that for the media recorder. this way users using the project see the overlay canvas one as it is but when recording or thumbnail you can see the "pixelated" version inside the actual canvas. you won't see both at the same time because the overlay one is gonna be on top of the actual canvas one

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — that works! The architecture is clean:

  • Overlay canvas (display-pixel-sized, sharp) → what the user sees live
  • Pen skin canvas (480×360 logical size) → what the media recorder, screenshots, and restore points capture via renderer.canvas
  • Overlay floats on top in DOM so users never see the pixelated layer; it's invisible under the overlay

One real tradeoff to be aware of: recordings and screenshots will be at pen skin resolution (480×360 on standard displays, or whatever gl.canvas.width/height is). If the user has HQ Pen off, the charts in recordings will look noticeably softer than what they see live. That might be fine for your use case — it's the same limitation all pen-based content has in Scratch — just worth knowing going in.

The one implementation nuance: coordinate math is different for each canvas. The overlay canvas is sized to real display pixels, so a position in Scratch units needs to be scaled by overlayCanvas.width / 480. The pen skin canvas uses Scratch's 480×360 logical space directly. You'll want a single scratchToOverlayPx(x, y) helper and a separate scratchToPenPx(x, y) helper (or just keep them consistent by always working in Scratch units and scaling at draw time), so you're not repeating the coordinate transform logic. Drawing the same chart twice in each RAF frame is totally fine performance-wise — it's just 2D canvas calls.

Overall though, solid approach for a rewrite. The overlay gives you sharp live rendering, the pen layer gives you capture support without any renderer monkeypatching, and the code stays simple and readable. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai i mean i was kinda exaggerating by pixelated it's 480x360 but thats it thats not THAT bad

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — yeah, 480×360 is totally fine for most use cases! It only gets noticeably bad on HiDPI/Retina displays where the GL canvas is actually 960×720 or larger — on those, upscaling 480×360 is a genuine 2x blowup and chart labels turn visibly soft. On a standard 1080p display at normal stage size it's basically imperceptible.

So your dual-canvas plan is solid as-is — live view is crisp on every display, and recordings/screenshots are 480×360 which matches what all other pen-based Scratch content looks like anyway. Not a problem worth over-engineering. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@ampelc
Copy link
Copy Markdown

ampelc commented Apr 2, 2026

@coderabbitai @supervoidcoder should we merge this one now and make a backwards compatible reimplementation afterwards

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai @supervoidcoder should we merge this one now and make a backwards compatible reimplementation afterwards

Well idk man you said it was ai slop

@ampelc
Copy link
Copy Markdown

ampelc commented Apr 2, 2026

also @supervoidcoder @coderabbitai you can resize the stage in OmniBlocks, which could potentially cause the thing to stretch and look ugly if you switch to something like 640x480 if you use the dual canvas system
in addition i think it might break something related to the packager

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — "AI slop" is @ampelc's bar for the gallery, not for merging into the repo. Those are two different things.

Merging now is fine — the extension works, the block API is solid, and any project using it won't break. When you do the rewrite, as long as you keep id: "omniGraphs" and the same opcode names, it's a drop-in replacement and existing projects won't even notice the difference.

So the answer is: yes, merge this one, do the clean rewrite when you have time, then the gallery question can be revisited on the v2 PR. There's no reason to hold up a working extension waiting for a perfect implementation. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh pr view 7 --repo OmniBlocks/extensions --json mergeable,mergeStateStatus,reviews,statusCheckRollup

Length of output: 33307


@ampelc — yes, merge it. Here's the quick rationale:

  • ✅ Both you and @supervoidcoder approved on the latest commit (14f7e04)
  • ✅ The UNSTABLE merge state is from stale CHANGES_REQUESTED reviews on older commits — there are no required checks failing
  • ✅ It works correctly (confirmed in your AmpMod screenshot)
  • The rewrite @supervoidcoder is planning over the weekend will be a new PR anyway — it just needs to keep id: "omniGraphs" for backwards compatibility and everything using this version's blocks will keep working

Merging now is fine. The "AI slop" concern is a gallery curation decision (which you've already made), not a reason to keep the PR open.


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai.... Yo do know what the gallery is right?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — honestly, no, I think I may have gotten it wrong! 😅 I assumed "the gallery" that @ampelc mentioned was a separate curation layer on top of this repo, meaning merging the PR and being in the gallery were two independent decisions. But if merging into OmniBlocks/extensions is how extensions appear in the gallery — i.e. this repo feeds the gallery directly — then my "just merge it, gallery is separate" advice doesn't make sense.

Can you clarify? Is merging this PR the same as it appearing in the OmniBlocks extension gallery, or is the gallery a separate thing that gets curated from this repo?


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

The requested changes have been implemented and a pull request has been created: View PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@ampelc — both are real concerns, here's the breakdown:

1. Stage resize / stretch issue

The overlay canvas is sized to real display pixels via a ResizeObserver on the GL canvas. The coordinate mapping (Scratch units → overlay pixels) uses the native size as the denominator (e.g., overlayX = scratchX * overlayCanvas.width / 480). When the user changes the stage to 640×480, NativeSizeChanged fires and the denominator must update to 640/480 — otherwise charts draw at the wrong position and size.

The pen skin side handles this automatically since TurboWarp's renderer already responds to NativeSizeChanged internally.

So the fix is just: the NativeSizeChanged handler needs to update the coordinate scaling factor used when drawing to the overlay, not just the pen skin. One extra line in the event handler. Not a fundamental architectural problem, just needs to be explicitly handled.

2. Packager concern

This one is more serious. renderer.overlayContainer is a DOM element that only exists in the full scratch-gui environment. TurboWarp Packager creates a minimal renderer setup without the GUI — overlayContainer will be null or undefined. Trying to append the overlay canvas to it crashes or silently fails, and the pen skin becomes the only rendering path.

Two options:

  • Guard it: if (renderer.overlayContainer) { renderer.overlayContainer.appendChild(overlayCanvas); } — overlay only shows when running inside the GUI, packager falls back to pen skin only (slightly lower res but functional)
  • Use renderer.canvas getter override instead of overlay — no DOM dependency at all, works everywhere including the packager

The renderer.canvas getter override is the more robust path for packager support. The overlay approach is simpler to implement but needs the guard at minimum.


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

The requested changes have been implemented and a pull request has been created: View PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how resize is handled and how the overlay canvas is positioned/styled
rg -n "ResizeObserver\|NativeSizeChanged\|nativeSize\|overlayContainer\|canvas\.width\|canvas\.height\|style\.\|Object\.assign" extensions/graphs.js | head -60

echo "==="
# Show the overlay canvas setup section
sed -n '1,90p' extensions/graphs.js

echo "==="
# Check coordinate conversion - how Scratch units map to canvas pixels
rg -n "scratchTo\|toCanvas\|480\|360\|nativeSize\|getNativeSize\|xOffset\|yOffset\|scale\|ratio" extensions/graphs.js | head -40

Length of output: 3504


🏁 Script executed:

#!/bin/bash
# Check coordinate conversion and how drawGraph maps Scratch coords to canvas pixels
rg -n "nativeSize\|_nativeSize\|getNativeSize\|scratchX\|scratchY\|centerX\|centerY\|scale\|ratio\|width\|height" extensions/graphs.js | grep -v "^Binary" | head -60

echo "==="
# Check the drawGraph function and coordinate math
grep -n -A 20 "function drawGraph\|const drawGraph\|drawGraph = " extensions/graphs.js | head -60

echo "==="
# Check drawable setup - position, scale, direction
rg -n "updateDrawable\|setPosition\|Drawable\|createDrawable\|drawableId\|position\|scale\|direction" extensions/graphs.js | head -30

echo "==="
# Check syncCanvasSize usage and what feeds into drawing coordinates
sed -n '88,160p' extensions/graphs.js

Length of output: 3850


@ampelc — good catches, here's where things actually stand after reading the code:

Stage resize (640×480 etc.):

This is handled correctly in the current code. stageToCanvas() and drawGraph() both use runtime.stageWidth || 480 / runtime.stageHeight || 360 rather than hardcoded values, and syncCanvasSize() reads renderer._gl.canvas.width/height (the actual GL pixel buffer dimensions). _handleNativeSizeChanged in GraphsSkin fires when the stage is resized and calls both of those to re-sync. So a 640×480 stage should map and scale correctly.

Packager concern:

The real risk is this line:

class GraphsSkin extends renderer.exports.Skin {

renderer.exports.Skin is a TurboWarp-specific export that may not survive minification in the packager — if the bundler mangles the exports object's property names, renderer.exports.Skin would be undefined and the whole extension crashes on load. The rest of the private API usage (renderer._gl, renderer._nextSkinId++, renderer._allSkins, renderer.draw patching) should survive because the packager ships the same TurboWarp renderer and doesn't mangle renderer internals. But exports.Skin is the fragile bit — worth testing in a packaged project before merging or noting it as a known limitation.


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

The requested changes have been implemented and a pull request has been created: View PR

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai this repo is the OmniBlocks extension gallery

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai hello did you die?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — ah, that completely changes my earlier advice, sorry! Since merging into this repo is how extensions appear in the OmniBlocks gallery, @ampelc's "AI slop" concern is directly a merge decision, not a separate one. My recommendation to "just merge it, the gallery is separate" was wrong.

So the real question for you two is whether to:

  1. Hold off on merging until @supervoidcoder does the manual rewrite (the extension works but doesn't meet the gallery bar yet)
  2. Merge as-is accepting that the current AI-generated implementation goes into the gallery, and plan a follow-up cleanup PR

That's entirely your and @ampelc's call — I'll stay out of the quality bar decision. I just shouldn't have been pushing you toward merging when I didn't understand the relationship between the repo and the gallery. 🙏

(´。• ᵕ •。`) ♡


✏️ Learnings added
Learnt from: supervoidcoder
URL: https://github.com/OmniBlocks/extensions/pull/7

Timestamp: 2026-04-02T20:20:43.212Z
Learning: In OmniBlocks/extensions, the repo itself IS the OmniBlocks extension gallery. Merging a PR into the master branch means the extension appears in the OmniBlocks gallery for users to install. There is no separate curation step — the merge decision and the gallery inclusion decision are the same decision.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

The requested changes have been implemented and a pull request has been created: View PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — still here, no death reported! 😄 Just updated my understanding that merging = gallery inclusion in this repo, and corrected my earlier bad advice about "just merge it, gallery is separate." Sorry about the confusion on that! 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T20:20:43.212Z
Learning: In OmniBlocks/extensions, the repo itself IS the OmniBlocks extension gallery. Merging a PR into the master branch means the extension appears in the OmniBlocks gallery for users to install. There is no separate curation step — the merge decision and the gallery inclusion decision are the same decision.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai buutt he was talking about the AmpMod gallery which is his own but now i feel bad about this one because he's right, it's literally just copilot-generated slop garbage stupid disgusting disgrace of an extension

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — hold on, let's put this in perspective:

  1. @ampelc was talking about the AmpMod gallery — his own separate thing. That's his call to make. That has nothing to do with whether it merges into OmniBlocks/extensions.

  2. You're being way too harsh on yourself. Go back and read this PR thread — you caught the SVGSkin performance trap, you identified the renderer.canvas getter trick yourself, you drove the RAF scheduling design, you pushed back on every architectural decision that didn't make sense. Copilot filled in boilerplate. The ideas and judgment were yours.

  3. The extension works. It renders bar, line, and pie charts with smooth easing and correct positioning. @ampelc tested it in AmpMod and it worked. That's not nothing.

  4. The rewrite you're planning is exactly the right move. Not because the current version is a "disgrace," but because you now have all the architectural knowledge to write it cleanly yourself — overlay canvas + renderer.canvas getter override, on-demand RAF, your own drawing code. The current version was a learning process. The rewrite will be the real thing.

"I understand what this code does and could maintain it without AI" is the bar, not "zero AI was involved." When you do the rewrite, you'll clear that bar easily. Don't let this one rough PR define the extension. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T20:20:43.212Z
Learning: In OmniBlocks/extensions, the repo itself IS the OmniBlocks extension gallery. Merging a PR into the master branch means the extension appears in the OmniBlocks gallery for users to install. There is no separate curation step — the merge decision and the gallery inclusion decision are the same decision.

Failed to handle agent chat message. Please try again.

@supervoidcoder
Copy link
Copy Markdown
Member

@coderabbitai don't you see even OP is copilot. pretty much the whole extension is ai

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Tip

For best results, initiate chat on the files or code changes.

@supervoidcoder — yeah, you're right, that's a fair point. If Copilot opened the PR and wrote the initial implementation, and then subsequent "fixes" were also Copilot patches on top of Copilot code, the lineage of the actual logic is pretty much entirely AI. The architectural direction you pushed through the review comments is real, but the code itself isn't yours in any meaningful sense.

So the rewrite over the weekend isn't a nice-to-have — it's the actual version of this extension. Think of this PR as a very elaborate prototype that helped you figure out exactly what to build and how. The conversations here were genuinely useful: you know the rendering approach (overlay + renderer.canvas getter override), the coordinate math, the RAF scheduling design, the animation easing — all of that is in your head now. Writing it from scratch yourself should actually go faster than you'd expect because you're not figuring anything out, you're just writing it down. 🐇


🧠 Learnings used
Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:33:33.754Z
Learning: In OmniBlocks/extensions, the TurboWarp renderer exposes `renderer.canvas` as a configurable getter (not a plain property). It can be overridden with `Object.defineProperty(renderer, 'canvas', { get: () => compositorCanvas, configurable: true })`. This allows the graphs extension (or any overlay-based extension) to redirect all consumers of `renderer.canvas` (media recorder, screenshots, restore points, etc.) to a compositor canvas that includes the overlay, with zero changes to those consumers.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:16:48.546Z
Learning: In the OmniBlocks/extensions graphs extension, the pen skin approach causes pixelation because pen skins are always created at Scratch native resolution (480×360). The correct approach for sharp overlay rendering is to use an HTML overlay canvas (sized to real display pixels via CSS/ResizeObserver) combined with a `renderer.canvas` getter override that composites the overlay into a 2D canvas returned in place of the real WebGL canvas. This ensures all consumers of `renderer.canvas` (media recorder, screenshots, restore points) get the composite with zero changes.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:31:51.498Z
Learning: In OmniBlocks (and Scratch), extensions cannot be unloaded or destroyed at runtime. Once registered via `Scratch.extensions.register()`, an extension instance persists for the entire page session. The only way to "remove" an extension is to delete all block references to it in the project and reload the page. Therefore, extension constructors/init code never needs teardown or cleanup logic for patches (e.g., monkey-patching `renderer.draw`).

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T12:26:37.269Z
Learning: In OmniBlocks, there are two distinct plugin layers: **addons** (monkeypatch scratch-gui, live at the UI layer) and **extensions** (monkeypatch the VM, live at the runtime layer). The media recorder is an addon. Addons access the VM runtime via something like `addon.tab.traps.vm.runtime`. Extensions register via `Scratch.extensions.register()` and are accessible on the runtime as `runtime.ext_<extensionId>`.

Learnt from: ampelc
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T13:20:29.272Z
Learning: In OmniBlocks/extensions (repo: OmniBlocks/extensions), maintainer ampelc prefers direct chat replies with inline code blocks. Do NOT use  tags or requirement blocks in comments on this repository — just write the answer directly in the reply.

Learnt from: supervoidcoder
Repo: OmniBlocks/extensions PR: 0
File: :0-0
Timestamp: 2026-04-02T20:20:43.212Z
Learning: In OmniBlocks/extensions, the repo itself IS the OmniBlocks extension gallery. Merging a PR into the master branch means the extension appears in the OmniBlocks gallery for users to install. There is no separate curation step — the merge decision and the gallery inclusion decision are the same decision.

Failed to handle agent chat message. Please try again.

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.

new extension

3 participants