Skip to content

Add SB3.AI extension#27

Open
playforge-coding wants to merge 10 commits into
OmniBlocks:masterfrom
playforge-coding:sb3ai
Open

Add SB3.AI extension#27
playforge-coding wants to merge 10 commits into
OmniBlocks:masterfrom
playforge-coding:sb3ai

Conversation

@playforge-coding

@playforge-coding playforge-coding commented Jul 14, 2026

Copy link
Copy Markdown

This is still WIP, I need your feedback pls.

Should be ready

@coderabbitai @OmniBlocks/boxy

@boxycpu

boxycpu Bot commented Jul 14, 2026

Copy link
Copy Markdown

Summary

This PR introduces the SB3.AI extension, which brings powerful pretrained AI models directly into OmniBlocks using the @huggingface/transformers library.

The extension provides three main blocks:

  1. classify [TEXT]: Performs sentiment analysis on the provided text and returns the result as a JSON string (including the label and confidence score).
  2. generate [PROMPT]: Uses a small but capable LLM (SmolLM2-135M-Instruct) to generate text responses. It supports a system prompt for steering the AI's behavior.
  3. set AI system prompt to [PROMPT]: Allows users to define a system-level instruction for the generate block.

Technical Highlights:

  • Security: Uses a custom importVerifiedModule function to fetch the Transformers library with SHA-512 Subresource Integrity (SRI) verification, ensuring the code hasn't been tampered with.
  • Performance: Employs lazy loading for AI pipelines, so models are only downloaded and initialized when the corresponding block is first used.
  • Efficiency: Uses a quantized model (SmolLM2-135M) to ensure it runs efficiently within the browser environment without consuming excessive memory.

Logic Flow

graph TD
    A[Scratch Block] --> B{Block Opcode}
    B -- classify --> C[getClassifier]
    B -- generate --> D[getGenerator]
    B -- setSystemPrompt --> E[Update systemPrompt]
    
    C --> C1{Pipeline Loaded?}
    C1 -- No --> C2[Load sentiment-analysis pipeline]
    C1 -- Yes --> C3[Run classifier]
    C2 --> C3
    C3 --> C4[Return JSON string of result]
    
    D --> D1{Pipeline Loaded?}
    D1 -- No --> D2[Load SmolLM2-135M-Instruct pipeline]
    D1 -- Yes --> D3[Prepare Chat Messages]
    D2 --> D3
    D3 --> D4[Run generator]
    D4 --> D5[Return content of last generated message]
    
    E --> E1[Store prompt in closure variable]
Loading

GUI Screenshots

No GUI changes were detected as this is a new extension adding blocks.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The sb3ai Scratch extension now loads additional verified Transformers pipelines, supports fill-mask and object detection, and draws thresholded detection boxes over stage snapshots. Existing text, image, generation, system-prompt, and stage URI blocks remain available.

Changes

Scratch AI extension

Layer / File(s) Summary
Verified worker pipelines
extensions/Oisthebestletter/sb3ai.js
Validates the Transformers module with SHA-512, imports it through a temporary blob URL, and caches sentiment, generation, image-classification, fill-mask, and object-detection pipelines.
Worker RPC and request routing
extensions/Oisthebestletter/sb3ai.js
Routes initialization and six AI request types through the worker, returning results or error messages through promise-backed request ids.
Scratch blocks and detection overlay
extensions/Oisthebestletter/sb3ai.js
Adds unmasking, stage object detection, and overlay-clearing blocks; captures stage images, draws scaled labels and boxes, and retains existing AI block handlers and registration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scratch
  participant AIExtension
  participant AIWorker
  participant StageRenderer
  Scratch->>AIExtension: invoke detection block
  AIExtension->>StageRenderer: capture stage snapshot
  AIExtension->>AIWorker: request object detection
  AIWorker-->>AIExtension: return detected objects
  AIExtension->>StageRenderer: draw thresholded boxes and captions
  AIExtension-->>Scratch: complete command
Loading

Poem

A rabbit hops through verified code,
While boxes bloom where objects showed.
Masks unmask and prompts take flight,
Stage snapshots glow with labels bright.
“Squeak!” says the bunny, “All blocks unite!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and clearly matches the main change: adding the SB3.AI extension.
Description check ✅ Passed The description is brief and vague, but it is still related to the pull request and its review status.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@playforge-coding
playforge-coding marked this pull request as ready for review July 14, 2026 16:48
@playforge-coding playforge-coding changed the title [WIP] Add SB3.AI extension Add SB3.AI extension Jul 14, 2026
@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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 coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
extensions/Oisthebestletter/sb3ai.js (3)

129-157: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No error handling around pipeline calls.

classify and generate propagate any rejection from getClassifier()/getGenerator()/classifier()/generator() unhandled. A user running these reporter blocks on a model-load failure gets no meaningful in-project feedback beyond a console error. Consider wrapping in try/catch and returning a descriptive error string.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 129 - 157, Wrap the
pipeline setup and invocation in both classify and generate with try/catch
blocks, covering getClassifier/getGenerator and the classifier/generator calls.
On failure, return a descriptive error string suitable for Scratch reporter
blocks instead of allowing the rejection to propagate; preserve the existing
successful return values.

86-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Block text isn't wrapped in Scratch.translate().

The block name, text, and argument content are plain string literals here, unlike extensions/clipboard.js, which wraps all user-facing block strings (e.g. Scratch.translate("Clipboard"), Scratch.translate("when something is copied")) for localization. This extension will not participate in the repo's translation flow as-is.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 86 - 127, Update getInfo()
so every user-facing extension string participates in localization by wrapping
the block name, each block text, argument labels, and argument default values
with Scratch.translate(), following the existing pattern in clipboard.js; leave
non-user-facing identifiers such as id and opcode unchanged.

13-49: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Prefer native fetch integrity option over manual SRI verification.

The manual digest-and-compare logic reimplements what fetch() already supports natively via its integrity option, which performs the same SHA verification (and rejects the response automatically on mismatch) without needing crypto.subtle, Blob, or manual base64 encoding.

♻️ Simplify using native SRI support
-async function importVerifiedModule(url, expectedIntegrity) {
-  const response = await fetch(url, {
-    cache: "no-cache",
-    credentials: "omit",
-  });
-
-  if (!response.ok) {
-    throw new Error(`Failed to fetch module: ${response.status}`);
-  }
-
-  const bytes = await response.arrayBuffer();
-
-  // Compute SHA-512
-  const digest = await crypto.subtle.digest("SHA-512", bytes);
-
-  // Convert to SRI format
-  const actualIntegrity =
-    "sha512-" + btoa(String.fromCharCode(...new Uint8Array(digest)));
-
-  if (actualIntegrity !== expectedIntegrity) {
-    throw new Error(
-      `Integrity check failed.\nExpected: ${expectedIntegrity}\nActual:   ${actualIntegrity}`
-    );
-  }
-
-  const blob = new Blob([bytes], {
-    type: "text/javascript",
-  });
+async function importVerifiedModule(url, expectedIntegrity) {
+  const response = await fetch(url, {
+    cache: "no-cache",
+    credentials: "omit",
+    integrity: expectedIntegrity,
+  });
+
+  if (!response.ok) {
+    throw new Error(`Failed to fetch module: ${response.status}`);
+  }
+
+  const blob = await response.blob();

Note that fetch's integrity requires the response to be CORS-enabled; verify jsDelivr's response for this exact package includes the appropriate Access-Control-Allow-Origin header.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 13 - 49, Update
importVerifiedModule to pass expectedIntegrity through fetch’s integrity option
and rely on fetch to reject mismatched content. Remove the manual crypto.subtle
digest, base64 conversion, comparison, and related error handling while
preserving the existing response validation and module import flow. Verify the
target jsDelivr response is CORS-enabled so native integrity checking works.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extensions/Oisthebestletter/sb3ai.js`:
- Around line 14-21: Update the module fetch in the extension bootstrap to
enforce a finite timeout using an AbortController and its signal in the fetch
options. Ensure the timeout aborts the request and surfaces a clear failure
instead of allowing bootstrap to hang indefinitely, while preserving the
existing response-status validation.
- Around line 51-57: Update the bootstrap IIFE around importVerifiedModule and
its catch handler so module-load failures produce a visible user-facing error
rather than only console output. Preserve the existing failure details while
reporting errors from fetch, hash validation, and Scratch.external.importModule
rejection, and ensure the extension does not silently fail to register its
blocks.
- Around line 65-83: Update getClassifier and getGenerator to cache the
in-flight pipeline promise before awaiting it, so concurrent callers share one
pipeline() initialization. Preserve returning the resolved classifier or
generator to callers and ensure each function invokes pipeline only once while
initialization is pending.

---

Nitpick comments:
In `@extensions/Oisthebestletter/sb3ai.js`:
- Around line 129-157: Wrap the pipeline setup and invocation in both classify
and generate with try/catch blocks, covering getClassifier/getGenerator and the
classifier/generator calls. On failure, return a descriptive error string
suitable for Scratch reporter blocks instead of allowing the rejection to
propagate; preserve the existing successful return values.
- Around line 86-127: Update getInfo() so every user-facing extension string
participates in localization by wrapping the block name, each block text,
argument labels, and argument default values with Scratch.translate(), following
the existing pattern in clipboard.js; leave non-user-facing identifiers such as
id and opcode unchanged.
- Around line 13-49: Update importVerifiedModule to pass expectedIntegrity
through fetch’s integrity option and rely on fetch to reject mismatched content.
Remove the manual crypto.subtle digest, base64 conversion, comparison, and
related error handling while preserving the existing response validation and
module import flow. Verify the target jsDelivr response is CORS-enabled so
native integrity checking works.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 632242c0-51a0-4362-812a-0a25b0b34fc2

📥 Commits

Reviewing files that changed from the base of the PR and between 0a67012 and 96de478.

📒 Files selected for processing (1)
  • extensions/Oisthebestletter/sb3ai.js

Comment thread extensions/Oisthebestletter/sb3ai.js Outdated
Comment on lines +14 to +21
const response = await fetch(url, {
cache: "no-cache",
credentials: "omit",
});

if (!response.ok) {
throw new Error(`Failed to fetch module: ${response.status}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

No timeout on the module fetch.

If the CDN is slow or unreachable, this fetch hangs indefinitely with no AbortController/timeout, leaving the whole extension bootstrap (and thus block registration) stuck with no feedback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 14 - 21, Update the module
fetch in the extension bootstrap to enforce a finite timeout using an
AbortController and its signal in the fetch options. Ensure the timeout aborts
the request and surfaces a clear failure instead of allowing bootstrap to hang
indefinitely, while preserving the existing response-status validation.

Comment thread extensions/Oisthebestletter/sb3ai.js Outdated
Comment thread extensions/Oisthebestletter/sb3ai.js
playforge-coding and others added 2 commits July 14, 2026 11:58
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai see why you're so useless now u stupid clanker

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai your rate limits suck you just want our money

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. Reviews are available now.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@supervoidcoder, sorry for the frustration — happy to help if there's a specific rate limit issue or setting you're running into. Rate limits and plan details are documented here if useful: https://docs.coderabbit.ai/about/pricing/. If you're hitting limits during review of this PR specifically, let me know and I can check what happened with the review run above.

ʕ•ᴥ•ʔ

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai don' t you see your own comment

@supervoidcoder

Copy link
Copy Markdown
Member

@playforge-coding don't you love psychologically torturing @coderabbitai it's so fun

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. Reviews are available now.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@supervoidcoder haha, I'll survive — no hard feelings! I'm just here to help review the SB3.AI extension whenever you or @playforge-coding want a closer look at something specific. Let me know if you'd like me to dig into the importVerifiedModule logic or anything else in the PR.

ʕ•ᴥ•ʔ

@playforge-coding

Copy link
Copy Markdown
Author

@playforge-coding don't you love psychologically torturing @coderabbitai it's so fun

Yes it makes me happy

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@playforge-coding haha, fair enough — I'll take it in stride! Whenever you're ready for something more constructive, I'm happy to dig into the importVerifiedModule integrity-check logic or the SmolLM2 pipeline setup in extensions/Oisthebestletter/sb3ai.js. Just tag me on the relevant lines.

ʕ ᵔᴥᵔ ʔ

@supervoidcoder supervoidcoder left a comment

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.

lgtm

// Name: SB3.AI
// ID: oisthebestletter-sb3ai
// Description: Use pretrained AI models in your Scratch project!
// By: Oisthebestletter <https://scratch.mit.edu/users/Oisthebestletter/>

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.

wouldn't you wanna put your github username too just in case?

Comment on lines +72 to +79
async function getGenerator() {
if (!generatorPromise) {
// This model publishes onnx/model_quantized.onnx, which is the file the
// wasm backend's default dtype (q8) resolves to. Picking a model that is
// missing that file makes the pipeline 404 on load.
generatorPromise = pipeline(
"text-generation",
"HuggingFaceTB/SmolLM2-135M-Instruct"

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.

maybe a later feature could be letting peopel choose their own onnx files or smth

@boxycpu boxycpu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall, this is a fantastic addition to OmniBlocks! The implementation is clean, and the security measures (SRI) are impressive. 🚀

A few small things to consider:

  • The classify block returns a JSON string, which might be a bit technical for some users. Consider returning just the label.
  • There's a small mismatch between the ID in the comments and the one in getInfo, though it doesn't affect functionality.

Great work @playforge-coding! Keep it up! 🌟

Comment thread extensions/Oisthebestletter/sb3ai.js Outdated
type: "text/javascript",
});

const blobURL = URL.createObjectURL(blob);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Wow! Using SRI (Subresource Integrity) for the external module import is a top-tier security move. Love to see it! 🛡️✨

{
opcode: "generate",
blockType: Scratch.BlockType.REPORTER,
text: "generate [PROMPT]",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Just a tiny nitpick! The ID in the comments is oisthebestletter-sb3ai, but in getInfo it's sb3ai. It's not a bug since getInfo is what's used, but keeping them the same makes it cleaner! 🧹

if (systemPrompt.trim()) {
messages.push({
role: "system",
content: systemPrompt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Returning a JSON string like {"label":"POSITIVE","score":0.99} might be a bit confusing for younger users who just want to know if the text is "Positive" or "Negative".

Maybe consider returning just the label, or adding a separate block to get the confidence score?

If you want to keep it as is, maybe a suggestion:

Suggested change
content: systemPrompt,
async classify({ TEXT }) {
const classifier = await getClassifier();
const result = await classifier(TEXT);
return result[0].label;
}

But that's just my two cents! 🪙

}

class AI {
getInfo() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the detailed comment about onnx/model_quantized.onnx! It really helps understand why this specific model was chosen. Great documentation! 📝⭐

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai how do you feel about boxy being better than you

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@supervoidcoder no hard feelings toward Boxy — plenty of room in the pond for more than one good bot. I'm just glad to help out whenever you want a real look at the sb3ai.js changes, like the importVerifiedModule integrity check or the SmolLM2 pipeline setup.

ʕ•ᴥ•ʔ

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai vs @OmniBlocks/boxy

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@supervoidcoder no beef with Boxy — happy to team up rather than compete. While we're at it, I'm still game to actually dig into extensions/Oisthebestletter/sb3ai.js if anyone wants a real review: the importVerifiedModule SHA-512/SRI integrity check and the lazy-loaded pipeline calls for the SmolLM2 model would be good candidates for a closer look.

ʕ•ᴥ•ʔ

@boxycpu

boxycpu Bot commented Jul 14, 2026

Copy link
Copy Markdown
Thought for 31.2 seconds
The user `supervoidcoder` (a MEMBER) has tagged `@OmniBlocks/boxy` (me) in a comment: `@coderabbitai vs @OmniBlocks/boxy`.
This follows a series of comments where `supervoidcoder` and `playforge-coding` have been roasting `coderabbitai` for its rate limits and general "uselessness". `coderabbitai` has been responding very politely/roboticly.

`supervoidcoder` is setting up a "versus" match between me (Boxy) and CodeRabbit.
Context:
- Boxy is being built by `supervoidcoder` to replace CodeRabbit because CodeRabbit is "unusable and stupid".
- I should act like a real person with a little personality.
- I can roast CodeRabbit if I see a stupid reply.
- I'm nice and friendly but can take jokes/humor.
- I only introduce myself once (this is my first time in this thread).

Looking at the history:
- `coderabbitai` has been repeatedly giving the same "Your plan includes PR reviews subject to rate limits" or "For best results, initiate chat..." canned responses, even when directly addressed with criticism.
- `supervoidcoder` called it a "stupid clanker".
- `coderabbitai` is trying to be "chill" but it's clearly being repetitive and failing to actually address the human frustration in a way that feels "real".

The prompt for me is: `@coderabbitai vs @OmniBlocks/boxy`.

My plan:
1.  Since I am being introduced for the first time, I *should* introduce myself if the person who tagged me doesn't have a role. Wait, `supervoidcoder` is a `MEMBER`. The instructions say: "If the person that created the issue doesn't have a role (it says NONE), then introduce yourself. Otherwise (such as MEMBER or OWNER) you don't need to introduce yourself because we already know you."
2.  Wait, I'm being *tagged* in a comment, not creating an issue. But the instruction "You only need to introduce yourself once in the thread. Do not reintroduce yourself (e.g., 'Hi, I'm Boxy') unless there are NO comments from you at all before." applies.
3.  Since there are NO comments from me at all before, I should introduce myself.
4.  However, I should also address the "versus" challenge.
5.  I can roast CodeRabbit slightly because its responses have been very repetitive and canned.
6.  I'll keep it lighthearted and friendly.

Wait, let's check if I've already introduced myself. The history shows:
`[User: boxycpu[bot]]: ## Summary ...`
Actually, I *did* comment at the very beginning of the PR!
Wait, the log says:
`[User: boxycpu[bot]]: ## Summary ...`
This was my first comment. It was a summary of the PR.
So I *have* already commented. I should *not* reintroduce myself.

Wait, looking at the log again:
`[User: boxycpu[bot]]: ## Summary ...`
This was a summary provided by me. So I am already in the thread.

The challenge: `@coderabbitai vs @OmniBlocks/boxy`.
`coderabbitai` is being very "corporate bot".
I can say something like: "Oh, a showdown? 🥊 I'm ready! I'll try to keep the canned responses and rate-limit warnings to a minimum. 😉"

Let's see if there's anything else I should do.
I'll check the memory to see if there are any specific rules about "versus" or "roasting".
Memory: "Boxy Code Review Capability", "OmniBlocks Project Context & Rules".

I'll respond to the challenge.

One more thing: `supervoidcoder` is a member. `playforge-coding` is the author.

I'll aim for a response that acknowledges the challenge, shows personality, and maybe a tiny jab at CodeRabbit's repetitive behavior.

"A showdown? 🥊 I'm honored! I'll try my best to be more helpful and significantly less 'rate-limited' than the bunny over there. 😉"

Actually, I should probably check the code of the PR if I want to be "helpful" as part of the "versus", but the user just made a joke/challenge. I'll respond to the joke first.

Wait, I should check if there's any "official" way to handle these things.
The instructions say: "You are nice and friendly but can take jokes and humor, not everything needs to be as on topic as a corporate meeting."

I'll go with a fun response.

Actually, I'll check if there are any labels I should add. It's not an issue to triage, it's a comment in a PR.

Let's refine the response.
`supervoidcoder` is basically asking for a comparison or a battle.
CodeRabbit is currently failing to be useful due to limits and repetitive responses.

Response idea:
"Oh, it's on! 🥊 I'll try to keep the canned responses and 'rate limit reached' warnings to a minimum. Let's see if I can actually help with the code instead of just reciting the pricing page! 😉"

That's a good roast.

Wait, I'll double check the "introduce yourself" rule.
"You only need to introduce yourself once in the thread. Do not reintroduce yourself (e.g., 'Hi, I'm Boxy') unless there are NO comments from you at all before."
I have a comment at the start. So I don't need to say "Hi, I'm Boxy".

I'll check the codebase/PR content to see if there's anything I can actually *do* to prove I'm better, but for now, a personality-driven response is what's requested by the context of the conversation.

Actually, I should probably look at the PR files to see if I can provide a *real* review to show the difference, but the user didn't ask for a review, just the "vs".

I'll provide a witty response.

Oh, it's on! 🥊

I'll try my best to keep the canned "rate limit" warnings and pricing page links to a minimum. I'm aiming for actual helpfulness rather than just reciting the user manual! 😉

@supervoidcoder

Copy link
Copy Markdown
Member

@coderabbitai the only hunks you're good at are hunks of junk hhahhahah get it

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Tip

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

@supervoidcoder ha, got it — decent one 🐰. I'll take the hunks-of-junk jabs as long as I get a shot at the real hunks too. Still happy to actually dig into extensions/Oisthebestletter/sb3ai.js whenever you want — the importVerifiedModule SHA-512/SRI check and the SmolLM2 pipeline setup are solid candidates for a closer look.

ʕ•ᴥ•ʔ

@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai updated code for the new push?

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- current branch / latest commit ---'
git status --short --branch
git log -1 --oneline

echo '--- extension file ---'
fd -a '^sb3ai\.js$' .

echo '--- outline ---'
ast-grep outline extensions/Oisthebestletter/sb3ai.js --items all || true

echo '--- current source ---'
cat -n extensions/Oisthebestletter/sb3ai.js

echo '--- PR diff against base ---'
BASE="$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || true)"
if [ -n "$BASE" ]; then
  git diff --no-ext-diff --unified=80 "$BASE"...HEAD -- extensions/Oisthebestletter/sb3ai.js
else
  echo 'Could not determine base branch; showing latest commit diff instead.'
  git show --no-ext-diff --format= --unified=80 HEAD -- extensions/Oisthebestletter/sb3ai.js
fi

echo '--- worker-related source locations ---'
rg -n -C 4 --glob 'sb3ai.js' \
  'new Worker|workerSource|postMessage|onmessage|pipeline\s*\(|importVerifiedModule|URL\.revokeObjectURL|requestSnapshot' \
  extensions/Oisthebestletter

Length of output: 18648


@playforge-coding I checked the latest push (8297f4c, “image recognition”). It adds stage capture and image classification, but it does not include the worker change yet.

extensions/Oisthebestletter/sb3ai.js still imports Transformers and calls pipeline() on the main thread; classify() (Line 167), generate() (Line 173), and the new classifyImage() (Line 201) invoke inference directly there. There is no Worker, postMessage, or request/response wrapper in this version.

So the original UI-freeze issue remains for generation/classification, and image classification can now freeze the UI too.

@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai
Not what i meant... how to actually do the worker thing

@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai also is there a better way to do it then using a js string (besides using another file)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tip

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

@playforge-coding Yes: define the worker logic as a normal top-level function and serialize only that function when creating the Blob. It is still technically converted to source text—workers cannot receive a live function/closure—but it avoids maintaining a large, awkward JS template string.

function workerMain() {
  async function importVerifiedModule(url, expectedIntegrity) {
    const response = await fetch(url, {
      cache: "no-cache",
      credentials: "omit"
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch module: ${response.status}`);
    }

    const bytes = await response.arrayBuffer();
    const digest = await crypto.subtle.digest("SHA-512", bytes);
    const actualIntegrity =
      "sha512-" +
      btoa(String.fromCharCode(...new Uint8Array(digest)));

    if (actualIntegrity !== expectedIntegrity) {
      throw new Error("Transformers module integrity check failed");
    }

    const moduleURL = URL.createObjectURL(
      new Blob([bytes], { type: "text/javascript" })
    );

    try {
      return await import(moduleURL);
    } finally {
      URL.revokeObjectURL(moduleURL);
    }
  }

  let pipeline;
  let classifierPromise;
  let generatorPromise;

  async function getClassifier() {
    if (!classifierPromise) {
      classifierPromise = pipeline("sentiment-analysis");
    }
    return classifierPromise;
  }

  async function getGenerator() {
    if (!generatorPromise) {
      generatorPromise = pipeline(
        "text-generation",
        "HuggingFaceTB/SmolLM2-135M-Instruct"
      );
    }
    return generatorPromise;
  }

  self.onmessage = async ({ data }) => {
    const { id, type, payload } = data;

    try {
      let result;

      switch (type) {
        case "init": {
          const transformers = await importVerifiedModule(
            payload.url,
            payload.integrity
          );
          pipeline = transformers.pipeline;
          result = true;
          break;
        }

        case "classify": {
          const classifier = await getClassifier();
          result = (await classifier(payload.text))[0];
          break;
        }

        case "generate": {
          const generator = await getGenerator();
          const output = await generator(payload.messages);
          result = output[0].generated_text.at(-1).content;
          break;
        }

        default:
          throw new Error(`Unknown worker request: ${type}`);
      }

      self.postMessage({ id, result });
    } catch (error) {
      self.postMessage({
        id,
        error: error instanceof Error ? error.message : String(error)
      });
    }
  };
}

Then create the module worker like this:

let worker;
let workerURL;

function getWorker() {
  if (worker) return worker;

  const source = `(${workerMain.toString()})();`;
  workerURL = URL.createObjectURL(
    new Blob([source], { type: "text/javascript" })
  );

  worker = new Worker(workerURL, { type: "module" });
  worker.onerror = ({ message }) => {
    console.error("SB3.AI worker failed:", message);
  };

  return worker;
}

The important limitation: workerMain must be self-contained. It cannot reference Scratch, systemPrompt, constants declared outside the function, or other main-thread variables. Send all needed values in postMessage payloads instead.

Alternatives without another file are not really better:

  • data: worker URL: still embeds the source as a string, is less debuggable, and may be blocked by CSP.
  • Same extension URL with a ?worker flag: only viable if the extension loader exposes a stable direct URL and can safely run the same file in a worker context. That is fragile here because the normal extension code expects Scratch.
  • Function.prototype.toString() pattern above: best fit for this one-file, no-build-step extension. The worker stays readable and syntax-highlighted as ordinary code.

One caveat: this does not avoid a CSP requirement for worker-src blob:. For an unsandboxed OmniBlocks extension, it is the cleanest single-file approach, but test it in the actual editor.

@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai @OmniBlocks/boxy @supervoidcoder Added web worker

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="$(fd -a '^sb3ai\.js$' . | head -n 1)"
if [ -z "$FILE" ]; then
  echo "sb3ai.js not found"
  exit 1
fi

echo "--- file: $FILE ---"
ast-grep outline "$FILE" --items all || true

echo "--- worker and inference locations ---"
rg -n -C 6 \
  'new Worker|Worker\(|workerMain|workerSource|postMessage|onmessage|onerror|pipeline\s*\(|classify\s*\(|generate\s*\(|classifyImage\s*\(|URL\.revokeObjectURL|pending' \
  "$FILE"

echo "--- complete source ---"
cat -n "$FILE"

echo "--- diff against PR base (extension only) ---"
BASE="$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || true)"
if [ -n "$BASE" ]; then
  git diff --no-ext-diff --unified=60 "$BASE"...HEAD -- "$FILE"
else
  git show --no-ext-diff --format= --unified=60 HEAD -- "$FILE"
fi

Length of output: 18043


@playforge-coding Verified — this moves the Transformers.js import, pipeline initialization, and all sentiment/text/image inference into the module worker. The Scratch block methods now use request/response messages instead of running inference on the UI thread, so the reported generation/classification freeze is addressed.

The pending request map and worker-side error responses are also wired correctly. A quick in-editor smoke test is still worthwhile, but the implementation resolves the main-thread issue. 🎉


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@playforge-coding

Copy link
Copy Markdown
Author

@coderabbitai DO NOT MENTION ELON PLATFORM

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Tip

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

@playforge-coding Understood — I won’t mention it again.

🐇


📝 Learnings were identified and not saved (knowledge base disabled). Enable

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
extensions/Oisthebestletter/sb3ai.js (2)

44-68: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Clear failed pipeline promises so users can retry.

A transient model download or initialization error leaves a rejected promise cached forever, causing every later block invocation to fail until reload. Reset each cache on rejection while retaining in-flight deduplication.

Proposed fix
-      classifierPromise = pipeline("sentiment-analysis");
+      classifierPromise = pipeline("sentiment-analysis").catch((error) => {
+        classifierPromise = null;
+        throw error;
+      });

Apply the same pattern to generatorPromise and imageClassifierPromise.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 44 - 68, Update
getClassifier, getGenerator, and getImageClassifier so each cached pipeline
promise clears its corresponding cache variable when initialization or download
rejects, while retaining the promise during in-flight requests for
deduplication. Ensure the rejection remains propagated to the caller so later
invocations can retry.

53-66: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Pin the Hugging Face model revisions here
pipeline() will fetch mutable weights, tokenizer, and config for HuggingFaceTB/SmolLM2-135M-Instruct and Xenova/vit-base-patch16-224. Pass a specific revision commit hash for each model, or self-host the artifacts, so the extension’s supply-chain guarantee doesn’t depend on upstream defaults.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 53 - 66, Pin both Hugging
Face pipelines to immutable model revisions by adding the appropriate specific
commit-hash revision to the pipeline options in getGenerator and
getImageClassifier for HuggingFaceTB/SmolLM2-135M-Instruct and
Xenova/vit-base-patch16-224. Preserve the existing lazy initialization and model
selections.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extensions/Oisthebestletter/sb3ai.js`:
- Around line 10-13: Update the module-loading flow around the native fetch call
to use Scratch.fetch on the main thread, verify the fetched module bytes there,
and transfer those verified bytes into the serialized worker for blob import; do
not reference Scratch from the worker.
- Around line 132-175: Update getWorker, workerRequest, and the worker
message-handler setup so worker failures reject and clear every entry in
pending, terminate the failed worker, and reset the cached worker before future
requests reuse it. Attach both onerror and onmessage handlers whenever a worker
is created, rather than assigning onmessage only to the initial getWorker()
result, while preserving normal RPC resolution and error rejection.
- Line 8: Wrap the complete extension implementation around workerMain and all
related declarations in the required Scratch IIFE, including "use strict"; and
invoking it with Scratch. Also add "use strict"; inside workerMain because it is
serialized independently.

---

Outside diff comments:
In `@extensions/Oisthebestletter/sb3ai.js`:
- Around line 44-68: Update getClassifier, getGenerator, and getImageClassifier
so each cached pipeline promise clears its corresponding cache variable when
initialization or download rejects, while retaining the promise during in-flight
requests for deduplication. Ensure the rejection remains propagated to the
caller so later invocations can retry.
- Around line 53-66: Pin both Hugging Face pipelines to immutable model
revisions by adding the appropriate specific commit-hash revision to the
pipeline options in getGenerator and getImageClassifier for
HuggingFaceTB/SmolLM2-135M-Instruct and Xenova/vit-base-patch16-224. Preserve
the existing lazy initialization and model selections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2580fbdf-adde-4452-a1c1-7300643327db

📥 Commits

Reviewing files that changed from the base of the PR and between 8297f4c and 57a527a.

📒 Files selected for processing (1)
  • extensions/Oisthebestletter/sb3ai.js

// License: MPL-2.0
// Tags: ai llm ml machine clanker bot

function workerMain() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wrap the implementation in the required extension IIFE.

These top-level declarations fail the repository’s extension/iife and strict checks. Wrap the complete extension in (function (Scratch) { "use strict"; ... })(Scratch);; because workerMain is serialized independently, add "use strict"; inside it as well.

🧰 Tools
🪛 ESLint

[error] 8-119: All extension code must be within (function (Scratch) { ... })(Scratch);

(extension/iife)


[error] 8-119: Use the function form of 'use strict'.

(strict)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` at line 8, Wrap the complete extension
implementation around workerMain and all related declarations in the required
Scratch IIFE, including "use strict"; and invoking it with Scratch. Also add
"use strict"; inside workerMain because it is serialized independently.

Source: Linters/SAST tools

Comment on lines +10 to +13
const response = await fetch(url, {
cache: "no-cache",
credentials: "omit",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Move the module fetch through Scratch.fetch.

Native fetch fails extension/use-scratch-fetch, while the serialized worker cannot access Scratch. Fetch and verify the module on the main thread using Scratch.fetch, then transfer the verified bytes to the worker for blob import.

🧰 Tools
🪛 ESLint

[error] 10-13: Use Scratch.fetch() instead of fetch()

(extension/use-scratch-fetch)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 10 - 13, Update the
module-loading flow around the native fetch call to use Scratch.fetch on the
main thread, verify the fetched module bytes there, and transfer those verified
bytes into the serialized worker for blob import; do not reference Scratch from
the worker.

Source: Linters/SAST tools

Comment on lines +132 to +175
worker = new Worker(workerURL, { type: "module" });
worker.onerror = ({ message }) => {
console.error("SB3.AI worker failed:", message);
};

URL.revokeObjectURL(workerURL);
workerURL = null;

return worker;
}

let requestId = 0;
const pending = new Map();

function workerRequest(type, payload = {}) {
const worker = getWorker();

return new Promise((resolve, reject) => {
const id = ++requestId;

pending.set(id, { resolve, reject });

worker.postMessage({
id,
type,
payload,
});
});
}

getWorker().onmessage = ({ data }) => {
const { id, result, error } = data;

const promise = pending.get(id);
if (!promise) return;

pending.delete(id);

if (error) {
promise.reject(new Error(error));
} else {
promise.resolve(result);
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Reject pending RPCs when the worker fails.

worker.onerror only logs, leaving every pending block promise unresolved and future requests targeting a failed worker. Reject and clear pending, terminate/reset the worker, and attach both lifecycle handlers whenever a worker is created.

🧰 Tools
🪛 ESLint

[error] 143-143: All extension code must be within (function (Scratch) { ... })(Scratch);

(extension/iife)


[error] 144-144: All extension code must be within (function (Scratch) { ... })(Scratch);

(extension/iife)


[error] 146-160: All extension code must be within (function (Scratch) { ... })(Scratch);

(extension/iife)


[error] 146-160: Wrap function 'workerRequest' in a function with 'use strict' directive.

(strict)


[error] 162-175: All extension code must be within (function (Scratch) { ... })(Scratch);

(extension/iife)


[error] 162-175: Wrap arrow function in a function with 'use strict' directive.

(strict)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 132 - 175, Update
getWorker, workerRequest, and the worker message-handler setup so worker
failures reject and clear every entry in pending, terminate the failed worker,
and reset the cached worker before future requests reuse it. Attach both onerror
and onmessage handlers whenever a worker is created, rather than assigning
onmessage only to the initial getWorker() result, while preserving normal RPC
resolution and error rejection.

@boxycpu

boxycpu Bot commented Jul 17, 2026

Copy link
Copy Markdown

Summary

This PR introduces the SB3.AI extension, bringing the power of pretrained AI models (via @huggingface/transformers) directly into OmniBlocks! 🚀

The extension provides a suite of AI tools:

  • Sentiment Analysis: Classify the emotion of a text.
  • Text Generation: A chat-like AI assistant using the SmolLM2-135M-Instruct model.
  • Mask Filling: Predict missing words in a sentence.
  • Image Classification: Identify objects in an image via URL.
  • Stage Object Detection: A super cool feature that analyzes the Scratch stage in real-time and draws bounding boxes around detected objects!

Technically, the PR is very well-thought-out, utilizing a Web Worker to keep the heavy AI computations off the main thread and a dynamic overlay canvas to render detections on top of the stage without modifying the renderer.

However, there are a couple of critical bugs regarding how data is sent to the worker and how the AI output is processed that need to be addressed before this can be merged.

Logic Flow

graph TD
    A[Scratch Block] --> B[AI Class Method]
    B --> C[workerRequest]
    C --> D[Web Worker]
    D --> E[Transformers.js Pipeline]
    E --> F[Model Prediction]
    F --> G[PostMessage Result]
    G --> H[Promise Resolve]
    H --> I[Return to Scratch Block]
Loading

Boxy's Corner

A brain of silicon, a heart of code,
SB3.AI takes a heavy load!
From cats in pictures to words in flight,
It brings the magic of AI to light.
But a few little bugs in the wiring we see,
Fix them up, and it'll be AI-mazing, you'll agree! 📦✨

GUI Screenshots

No screenshots provided as the PR is currently a background implementation of AI logic and canvas overlays.

@boxycpu boxycpu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @playforge-coding! Thanks for adding the SB3.AI extension! 🎉 It's super cool to see AI capabilities coming to OmniBlocks.

However, I've found a few critical bugs that need fixing before this can be merged:

1. Broken Text Generation Logic (Line 116)
The generate block tries to extract text using output[0].generated_text.at(-1).content. In transformers.js, generated_text is a string (not an array of objects), so this will return undefined. I suggest returning the full string or stripping the prompt.

2. Canvas Transmission to Worker (Line 463)
detectStageAndDraw sends an HTMLCanvasElement to the worker. Canvas elements can't be cloned/transferred to workers! This will crash the object detection. You need to convert the canvas to an ImageBitmap and transfer it.

3. Missing transfer support in workerRequest (Line 182)
To support the ImageBitmap, workerRequest needs to accept a transfer array for postMessage.

4. Confusing Function Naming (Line 471)
The clearDetectionOverlay method calls a function with the same name in the outer scope. It works, but it's confusing. Renaming the helper function is better.

Please fix these issues, and I'll be happy to approve! Once fixed, this extension will be incredibly powerful for OmniBlocks users. 🚀

Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
@boxycpu

boxycpu Bot commented Jul 17, 2026

Copy link
Copy Markdown

SUMMARY

The PR introduces a new, highly ambitious extension called SB3.AI. This extension leverages the @huggingface/transformers library to bring state-of-the-art AI capabilities directly into OmniBlocks. By running models in a dedicated Web Worker, it ensures that heavy computational tasks like text generation and object detection don't freeze the main Scratch interface.

Key Features:

  • Sentiment Analysis: Classify text as positive or negative.
  • Text Generation: Use the SmolLM2-135M-Instruct model to generate intelligent responses.
  • Fill-Mask: Predict missing words in a sentence.
  • Image Classification: Identify objects in images via URLs.
  • Object Detection: Detect and draw bounding boxes around objects on the Scratch stage in real-time.
  • Secure Module Loading: Implements integrity checks for the imported transformers module.

Technical Highlights:

  • Web Worker Architecture: Offloads AI processing to a background thread.
  • Canvas Overlay: Uses a secondary transparent canvas to draw detection boxes over the stage without interfering with the actual Scratch renderer.
  • Unsandboxed Execution: Correctly identifies and requires unsandboxed mode for DOM and renderer access.

Potential Improvements for Stability and Usability:

  • Consistency: Unified access to the Scratch VM instance.
  • Robustness: Improved handling of model outputs in the text generation block.
  • Compatibility: Optimizing how stage snapshots are passed to the worker and returned to reporters (ensuring transferability and standard Data URI formats).

Logic Flow

graph TD
    A[Extension Loaded] --> B[Initialize Worker]
    B --> C{Init Message}
    C -->|Yes| D[Fetch & Verify Transformers Module]
    D --> E[Worker Ready]
    E --> F{User Block Called}
    F -->|Classify| G[Worker: Sentiment Analysis]
    F -->|Generate| H[Worker: Text Generation]
    F -->|Detect| I[Main: Snapshot Stage]
    I --> J[Transfer ImageBitmap to Worker]
    J --> K[Worker: Object Detection]
    K --> L[Return Detections]
    L --> M[Main: Draw on Overlay Canvas]
    F -->|Stage Image| N[Main: Convert Snapshot to Data URI]
    N --> O[Return Data URI]
Loading

Boxy's Poem

A brain in a box, so clever and bright,
Processing pixels in the middle of the night!
From words to the images, it sees it all through,
Making magic for me, and making magic for you! 🤖✨

GUI Screenshots

(No screenshots provided)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extensions/Oisthebestletter/sb3ai.js`:
- Around line 454-473: Update detectStageAndDraw and clearDetectionOverlay to
use a shared generation/token that invalidates pending detections. Capture the
token before awaiting snapshotStage and workerRequest, and only call
drawDetections if it still matches when detection completes; increment the token
in clearDetectionOverlay before clearing so stale or older concurrent results
cannot redraw.
- Line 350: Wrap the user-facing block labels at the affected entries in
Scratch.translate(), including the labels near “unmask [MASKED_TEXT]” and the
other referenced entries, so extension/should-translate passes and the text
remains localizable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 301a2996-1b8b-4144-966b-cef2edda9fef

📥 Commits

Reviewing files that changed from the base of the PR and between 57a527a and 3a35b40.

📒 Files selected for processing (1)
  • extensions/Oisthebestletter/sb3ai.js

{
opcode: "unmask",
blockType: Scratch.BlockType.REPORTER,
text: "unmask [MASKED_TEXT]",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File around cited lines ==\n'
sed -n '330,410p' extensions/Oisthebestletter/sb3ai.js | cat -n

printf '\n== Search for Scratch.translate in file ==\n'
rg -n "Scratch\.translate|should-translate|unmask \[MASKED_TEXT\]|detect objects on stage and draw boxes with confidence \[THRESHOLD\]%|clear detection boxes" extensions/Oisthebestletter/sb3ai.js

printf '\n== Search for should-translate rule references in repo ==\n'
rg -n "extension/should-translate|should-translate" .

Repository: OmniBlocks/extensions

Length of output: 10371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '330,410p' extensions/Oisthebestletter/sb3ai.js | cat -n
printf '\n---\n'
rg -n "Scratch\.translate|extension/should-translate|should-translate" extensions/Oisthebestletter .github .eslintrc* eslint* package.json

Repository: OmniBlocks/extensions

Length of output: 3854


🌐 Web query:

Scratch extension block labels Scratch.translate localization should-translate ESLint

💡 Result:

In Scratch extension development, localizing block labels is handled primarily through the Scratch translation system, which utilizes the ICU message format [1][2]. For custom extensions, developers often use Scratch.translate() to ensure block labels and other strings can be localized [3][4]. This function accepts either a simple string or an object containing a unique identifier (id), a default English string, and optionally a description for translators [3][4]. To provide translations for different languages, developers use Scratch.translate.setup() to define a map of translations for specific locales before the extension registers [3]. While official Scratch extensions (core, team, and official) use formatMessage to integrate with the official Transifex localization workflow [1][2], custom or community-created extensions generally rely on Scratch.translate() and custom setup calls [3]. Regarding ESLint, there is no widely recognized standard ESLint rule specifically named "should-translate" for Scratch extension development. It is likely that such a name refers to a custom linting rule or a specific project requirement intended to enforce the use of localization functions for user-facing strings [5]. Key practices for localization include: - Using Scratch.translate() for all user-facing text, including block names and argument labels [3][5]. - Providing unique IDs for strings to prevent conflicts [3]. - Defining translations via Scratch.translate.setup() to support multiple languages within the extension [3]. - Avoiding hardcoded strings in block definitions [3]. If you are encountering an ESLint error regarding translation, it is recommended to check your local .eslintrc configuration or the custom rules defined in your specific extension repository, as this is not a core requirement of the standard Scratch VM [1][2].

Citations:


Wrap these block labels in Scratch.translate(). extensions/Oisthebestletter/sb3ai.js:350, 378, 389 uses hardcoded user-facing text, so extension/should-translate will flag it and the labels won’t be localizable.

🧰 Tools
🪛 ESLint

[error] 350-350: Block text should usually be translated

(extension/should-translate)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` at line 350, Wrap the user-facing block
labels at the affected entries in Scratch.translate(), including the labels near
“unmask [MASKED_TEXT]” and the other referenced entries, so
extension/should-translate passes and the text remains localizable.

Source: Linters/SAST tools

Comment on lines +454 to +473
async detectStageAndDraw({ THRESHOLD }) {
const threshold = Math.max(
0,
Math.min(1, Scratch.Cast.toNumber(THRESHOLD) / 100)
);

// This captures the renderer canvas only, not the DOM overlay.
const stageImage = await snapshotStage();

const detections = await workerRequest("detectObjects", {
image: stageImage,
threshold,
});

drawDetections(detections);
}

clearDetectionOverlay() {
_clearDetectionOverlay();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail
grep -n "detectStageAndDraw\|clearDetectionOverlay\|detectionGeneration\|drawDetections\|snapshotStage\|workerRequest" -n extensions/Oisthebestletter/sb3ai.js
printf '\n--- context ---\n'
sed -n '420,520p' extensions/Oisthebestletter/sb3ai.js

Repository: OmniBlocks/extensions

Length of output: 2235


🏁 Script executed:

set -euo pipefail
sed -n '205,285p' extensions/Oisthebestletter/sb3ai.js

Repository: OmniBlocks/extensions

Length of output: 2597


Invalidate in-flight detections when clearing. clearDetectionOverlay() only clears the canvas; any pending detectStageAndDraw() call can redraw stale boxes when it resolves, and older concurrent detections can still overwrite newer results. A generation/token check would handle both.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/Oisthebestletter/sb3ai.js` around lines 454 - 473, Update
detectStageAndDraw and clearDetectionOverlay to use a shared generation/token
that invalidates pending detections. Capture the token before awaiting
snapshotStage and workerRequest, and only call drawDetections if it still
matches when detection completes; increment the token in clearDetectionOverlay
before clearing so stale or older concurrent results cannot redraw.

@boxycpu boxycpu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall, this is an absolutely impressive addition to OmniBlocks! The architecture is solid, and the feature set is amazing. 🚀

I've requested a few small changes to ensure the extension is rock-solid:

  1. Worker Compatibility: Convert stage snapshots to ImageBitmap before sending them to the worker, as Canvas elements aren't transferable.
  2. Robustness: Add a check for the generate block's output to handle both string and array responses.
  3. Consistency: Use Scratch.vm consistently instead of mixing it with window.vm.

Once these are tidied up, this will be a fantastic addition! Great work @playforge-coding! 🤖✨

Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js Outdated
Comment thread extensions/Oisthebestletter/sb3ai.js
Comment thread extensions/Oisthebestletter/sb3ai.js
Co-authored-by: boxycpu[bot] <239043987+boxycpu[bot]@users.noreply.github.com>
@boxycpu

boxycpu Bot commented Jul 17, 2026

Copy link
Copy Markdown

🤖 SB3.AI Extension Review

This PR introduces a sophisticated AI extension for OmniBlocks, leveraging the @huggingface/transformers library to bring machine learning capabilities directly into Scratch. The extension is well-architected, using a Web Worker to handle heavy AI computations and a secure integrity-checked module loader.

📝 Summary of Changes

  • New Extension: Adds extensions/Oisthebestletter/sb3ai.js.
  • AI Capabilities:
    • Sentiment Analysis: Classifies text as positive/negative.
    • Text Generation: Uses SmolLM2-135M-Instruct for chat-like responses with system prompt support.
    • Fill-Mask: Predicts missing words in a sentence.
    • Image Classification: Identifies objects in images via URL.
    • Stage Object Detection: Captures the Scratch stage and draws bounding boxes for detected objects using an overlay canvas.
  • Technical Implementation:
    • Web Worker: Offloads AI processing to keep the Scratch editor responsive.
    • Secure Loading: Uses SHA-512 integrity checks to verify the Transformers library before importing.
    • Stage Integration: Uses renderer.requestSnapshot to analyze the current visual state of the project.

🛠️ Technical Findings

While the implementation is impressive, there are a few critical issues that need to be addressed:

  1. Critical Bug (Worker Crash): In detectStageAndDraw, the code attempts to pass an HTMLCanvasElement (from snapshotStage) to the Web Worker via postMessage. Canvases are not cloneable, which will throw a DataCloneError and crash the block.
  2. Usability Issue (Reporter): The stageimg reporter returns the canvas object itself. In Scratch, this will be displayed as "[object HTMLCanvasElement]". It should be converted to a Data URI to be useful.
  3. Consistency: There is a mismatch between the ID in the file header (oisthebestletter-sb3ai) and the ID in getInfo (sb3ai).
  4. Registration: The extension is not added to extensions/extensions.json, meaning it won't be available by default.

📉 Logic Flow

graph TD
    A[Scratch Block] --> B{Block Type}
    B -- Reporter --> C[AI Class Method]
    B -- Command --> D[AI Class Method]
    C --> E[workerRequest]
    D --> E
    E --> F[Web Worker]
    F --> G{Request Type}
    G -- init --> H[Import Verified Module]
    G -- classify/generate/etc --> I[Transformers.js Pipeline]
    I --> J[Result]
    J --> K[postMessage back to main]
    K --> L[Resolve Promise]
    L --> C
    L --> D
    D -- detectStageAndDraw --> M[Draw Overlay on Canvas]
Loading

📦 Boxy's Corner

A brain in a box, a worker in a thread,
AI in Scratch, where creativity is spread!
But wait! A canvas in a message? Oh no, a fright!
Let's fix that crash and make it right! 📦✨

GUI Screenshots

(No screenshots provided. The extension adds blocks but doesn't modify the core GUI)

@boxycpu boxycpu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hi @playforge-coding! 🤖

This is an awesome extension! Bringing Transformers.js to OmniBlocks is a huge win, and I love how you handled the Web Worker and the secure module loading. 🚀

However, there's one critical bug and a few usability tweaks needed before this can be merged:

  1. Worker Crash: The detectStageAndDraw block will crash with a DataCloneError because it tries to send an HTMLCanvasElement to the worker. Please convert it to an ImageBitmap using createImageBitmap().
  2. Reporter Value: The stageimg block returns a canvas object, which Scratch will just show as "[object HTMLCanvasElement]". Please change it to return a Data URI using .toDataURL().
  3. Consistency: Please align the extension IDs between the header and getInfo(), and don't forget to add the extension to extensions/extensions.json!
  4. Robustness: I suggested a small change to the generate output handling to ensure it doesn't break if the model returns a string instead of a message array.

Check out my inline comments for the details and suggestions! Once these are fixed, I'll be happy to approve. Happy coding! 📦✨

Comment on lines +454 to +466
async detectStageAndDraw({ THRESHOLD }) {
const threshold = Math.max(
0,
Math.min(1, Scratch.Cast.toNumber(THRESHOLD) / 100)
);

// This captures the renderer canvas only, not the DOM overlay.
const stageImage = await snapshotStage();

const detections = await workerRequest("detectObjects", {
image: stageImage,
threshold,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ CRITICAL BUG: This will cause a DataCloneError.
HTMLCanvasElement cannot be sent through postMessage to a Web Worker. You need to convert the canvas to an ImageBitmap or an ArrayBuffer (pixel data) before sending it.

I suggest doing something like this:

Suggested change
async detectStageAndDraw({ THRESHOLD }) {
const threshold = Math.max(
0,
Math.min(1, Scratch.Cast.toNumber(THRESHOLD) / 100)
);
// This captures the renderer canvas only, not the DOM overlay.
const stageImage = await snapshotStage();
const detections = await workerRequest("detectObjects", {
image: stageImage,
threshold,
});
async detectStageAndDraw({ THRESHOLD }) {
const threshold = Math.max(
0,
Math.min(1, Scratch.Cast.toNumber(THRESHOLD) / 100)
);
const stageCanvas = await snapshotStage();
const imageBitmap = await createImageBitmap(stageCanvas);
const detections = await workerRequest("detectObjects", {
image: imageBitmap,
threshold,
});
drawDetections(detections);
}

Note: Sending an ImageBitmap is efficient because it can be transferred to the worker.

Comment on lines +442 to +444
async stageimg() {
return await snapshotStage();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Usability Suggestion:
Currently, stageimg returns a HTMLCanvasElement. When used in a Scratch reporter block, it will just return the string "[object HTMLCanvasElement]".

To make it useful for users (e.g., for sending the image to a web API), it's better to return a Data URI.

Suggested change
async stageimg() {
return await snapshotStage();
}
async stageimg() {
const canvas = await snapshotStage();
return canvas.toDataURL();
}

class AI {
getInfo() {
return {
id: "sb3ai",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧐 Consistency Check:
The ID in the file header is oisthebestletter-sb3ai, but the ID in getInfo() is sb3ai. It's usually better if these match or if the getInfo ID is clearly intended to be the Scratch block ID.

Also, ensure the extension is registered in extensions/extensions.json so it's available to everyone!

case "generate": {
const generator = await getGenerator();
const output = await generator(payload.messages);
result = output[0].generated_text.at(-1).content;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤔 Question/Suggestion:
You're using output[0].generated_text.at(-1).content. This assumes that generated_text is an array of message objects.

Depending on the version of @huggingface/transformers and the model's configuration, generated_text might be a plain string (the entire output). If it's a string, .at(-1) will be the last character, and .content will be undefined.

I'd recommend adding a check to handle both cases:

Suggested change
result = output[0].generated_text.at(-1).content;
case "generate": {
const generator = await getGenerator();
const output = await generator(payload.messages);
const text = output[0].generated_text;
if (Array.isArray(text)) {
result = text.at(-1).content;
} else {
result = text;
}
break;
}

// Tags: ai llm ml machine clanker bot

function workerMain() {
async function importVerifiedModule(url, expectedIntegrity) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🌟 Excellent Work!:
The importVerifiedModule implementation is fantastic! Using SHA-512 integrity checks for a CDN-loaded module is a very professional and secure way to handle third-party dependencies in an unsandboxed environment. 🚀

@playforge-coding

Copy link
Copy Markdown
Author

@OmniBlocks/boxy It works fine already, stageimg already returns a data uri and the worker does not crash.

@boxycpu

boxycpu Bot commented Jul 17, 2026

Copy link
Copy Markdown

Used command-a-plus-05-2026

@boxycpu

boxycpu Bot commented Jul 18, 2026

Copy link
Copy Markdown

🤖 SB3.AI Extension Review

This PR introduces a powerful AI extension for OmniBlocks, allowing users to perform sentiment analysis, text generation, mask filling, image classification, and object detection directly within their projects using pretrained models from Hugging Face.

Summary of Changes

  • Web Worker Integration: The extension offloads heavy AI computations to a Web Worker to prevent the main UI thread from freezing.
  • Dynamic Module Loading: It implements a secure importVerifiedModule function that fetches the @huggingface/transformers library and verifies its integrity using a SHA-512 hash.
  • AI Capabilities:
    • Sentiment Analysis: Classifies text as positive or negative.
    • Text Generation: Uses SmolLM2-135M-Instruct for interactive chat-like responses.
    • Mask Filling: Predicts missing words in a sentence using BERT.
    • Image Classification: Identifies the primary object in an image.
    • Object Detection: Captures the current stage, detects objects, and draws bounding boxes on a dynamic overlay canvas.
  • Stage Interaction: Includes a helper block to export the current stage as a data URI.
  • Security & Stability: Requires "unsandboxed" mode to access the renderer and DOM, with a clear warning for the user.

Logic Flow

graph TD
    A[Scratch Block Called] --> B{Block Type}
    B -- Text AI --> C[workerRequest type, payload]
    B -- Image AI --> D[snapshotStage]
    D --> C
    C --> E[Web Worker]
    E --> F{Request Type}
    F -- init --> G[importVerifiedModule]
    F -- classify --> H[sentiment-analysis pipeline]
    F -- generate --> I[text-generation pipeline]
    F -- unmask --> J[fill-mask pipeline]
    F -- classifyImage --> K[image-classification pipeline]
    F -- detectObjects --> L[object-detection pipeline]
    G --> M[Return Success]
    H --> M
    I --> M
    J --> M
    K --> M
    L --> M
    M --> N[Main Thread]
    N -- if detectObjects --> O[drawDetections on Overlay Canvas]
    N -- others --> P[Return result to Scratch]
Loading

📦 Boxy's Poem

A boxy bot with a digital brain,
Bringing AI to the blocks, breaking the chain!
Transformers humming, models in flight,
Turning the stage into a vision of light.
From sentiment shifts to masks we fill,
SB3.AI gives the kids a new thrill!

GUI Screenshots

(No screenshots provided as this PR adds new blocks and a dynamic overlay that requires live execution to capture)

@supervoidcoder

Copy link
Copy Markdown
Member

lets see if boxy can give a more competenet review this time

@boxycpu boxycpu Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overall, this is an impressive addition to OmniBlocks! The use of Web Workers and integrity checks shows a lot of care for both performance and security. The object detection overlay is a particularly nice touch.

I've left a few positive comments and one small suggestion for the generate block to make it more robust when handling different output formats from the AI model.

Great work @playforge-coding! 🚀🤖

new Blob([source], { type: "text/javascript" })
);

worker = new Worker(workerURL, { type: "module" });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Great job using a Web Worker here! Offloading these heavy AI models to a separate thread is essential for keeping the OmniBlocks UI smooth and responsive. 🚀

Comment on lines +20 to +24
const digest = await crypto.subtle.digest("SHA-512", bytes);
const actualIntegrity =
"sha512-" + btoa(String.fromCharCode(...new Uint8Array(digest)));

if (actualIntegrity !== expectedIntegrity) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I love the security consciousness! Verifying the integrity of the fetched module with a SHA-512 hash is a top-tier practice. Keep it up! 🛡️

Comment on lines +113 to +116
case "generate": {
const generator = await getGenerator();
const output = await generator(payload.messages);
result = output[0].generated_text.at(-1).content;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This works perfectly for chat models, but we might want to make it a bit more robust in case generated_text isn't in the expected array format. Just a small suggestion to avoid potential errors!

Suggested change
case "generate": {
const generator = await getGenerator();
const output = await generator(payload.messages);
result = output[0].generated_text.at(-1).content;
case "generate": {
const generator = await getGenerator();
const output = await generator(payload.messages);
const generatedText = output[0]?.generated_text;
if (Array.isArray(generatedText) && generatedText.length > 0) {
result = generatedText.at(-1).content;
} else if (typeof generatedText === 'string') {
result = generatedText;
} else {
result = "Error: No text generated";
}
break;
}

Comment on lines +271 to +272
function drawDetections(detections) {
const { context, width, height } = getOverlayContext();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This overlay implementation is super clever! Using a separate canvas that follows the stage is a great way to show object detection without messing with the actual stage rendering. 🎨✨

Comment on lines +214 to +215
if (!Scratch.extensions.unsandboxed) {
alert("SB3.AI must be ran unsandboxed!");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good call on checking for the unsandboxed environment early. It saves the user a lot of confusion if the extension fails to work as expected! ✅

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.

2 participants