Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
03c35cc
adding Jibo extensions to racia/main with a few edits
Sep 4, 2022
a611a11
Adding similarity block
randi-c-dubs Oct 17, 2022
92fd165
Commit again with package lock files
randi-c-dubs Oct 17, 2022
4442fc8
Removing package lock to see if that allows build
randi-c-dubs Oct 17, 2022
20b7e2d
Merged together 'raica' with 'beta'
p-buddy Oct 27, 2022
39488fb
piped jibo extension to typescript
sanions Oct 31, 2022
9a96171
Update extension-manager.js and extensions index.jsx to reflect types…
sanions Nov 2, 2022
fa2f8b9
Jibo extension icons
sanions Nov 16, 2022
1d9a784
pre-merge commit
sanions Nov 16, 2022
47c18f6
Merge remote-tracking branch 'origin/beta' into raica_TypescriptPort
sanions Nov 16, 2022
b4ec9a9
Emoji and Custom Animation UI's added
sanions Nov 22, 2022
f5fca37
overarching changes to all of the blocks
sanions Jan 25, 2023
61b0791
Merge
sanions Jan 25, 2023
48eb3d0
adding new custom argument
sanions Jan 26, 2023
c9d5d1f
re-installing node modules
sanions Jan 26, 2023
c8f73a7
remove scratch-vm dependencies
sanions Jan 29, 2023
acb7e81
debugging
sanions Jan 31, 2023
d067873
Added json rollup plugin to bundle process
p-buddy Jan 31, 2023
6d7e24d
Merge remote-tracking branch 'origin/addJsonPluginToRollup' into raic…
sanions Jan 31, 2023
67b589f
Added node polyfills and babel plugins to fix bundling errors. Add ro…
p-buddy Feb 2, 2023
c00afef
added custom arguments for emoji, emote, and led
sanions Feb 23, 2023
d847545
Cleaning up latest version of jibo blocks
randi-c-dubs Jun 12, 2023
ab914bc
Cleaning up latest version of Jibo blocks
randi-c-dubs Jun 13, 2023
e483c89
Putting Jibo icon with correct aspect ratio
randi-c-dubs Jun 13, 2023
c90af1b
Cleaning build errors
randi-c-dubs Jun 13, 2023
ceb6b94
Pushing again because of build fail
randi-c-dubs Jun 14, 2023
3f24f6e
Parker updated raice ts (#288)
pmalacho-mit Jun 14, 2023
6d85e91
Cleaning up block operation functions
randi-c-dubs Jun 14, 2023
2da3b64
Removing strange reference to self in jiboTTSFn
randi-c-dubs Jun 14, 2023
7d0697b
Improving wording of blocks
randi-c-dubs Jun 14, 2023
43e22e0
Getting rid of enums
randi-c-dubs Jun 15, 2023
9f92c34
Latest package-lock
randi-c-dubs Jun 15, 2023
343f6ba
working build tasneem
tasneem-wb Jun 16, 2023
58a471b
working build tasneem
tasneem-wb Jun 16, 2023
2ae7853
jibo name modal added and firebase connected to first block
tasneem-wb Jun 23, 2023
d364966
first firebase commit
tasneem-wb Jul 11, 2023
4f31daa
queue implemented
tasneem-wb Jul 13, 2023
4efdaf4
queue without flag
tasneem-wb Jul 13, 2023
abd11fe
jibo working with firebase
tasneem-wb Jul 13, 2023
7d2ca65
Update index.ts
sghowinem Jul 15, 2023
760eeeb
Update index.ts
sghowinem Jul 15, 2023
2e63c6e
Adding latest package lock
randi-c-dubs Jul 16, 2023
674ebc4
Merge branch 'dev' into firebase-scratch
randi-c-dubs Jul 16, 2023
ee989d2
[skip ci] re-generate extension documentation
Jul 16, 2023
5cbe059
Cleaning up unnecessary code
randi-c-dubs Jul 17, 2023
59974b4
Save previous jibo name
randi-c-dubs Jul 17, 2023
9c59bf0
Removing unwanted extensions
randi-c-dubs Jul 17, 2023
3ca1338
Redoing Jibo timing to use await instead of 'then'
randi-c-dubs Jul 17, 2023
8ba06c9
Fixing Jibo ask block
randi-c-dubs Jul 17, 2023
5685b72
Validating Jibo name and adding error message
randi-c-dubs Jul 18, 2023
dc0bb76
Making color argument capital letters
randi-c-dubs Jul 18, 2023
2c37215
Cleaning code and fixing Jibo blocks bugs
randi-c-dubs Jul 18, 2023
a996d56
Separating const defs into separate files
randi-c-dubs Jul 19, 2023
1d5fb41
Improving audio names
randi-c-dubs Jul 19, 2023
1fef973
Poking around with firebase timings more
randi-c-dubs Jul 19, 2023
9ba2e14
Choosing better options for emotion animations
randi-c-dubs Jul 19, 2023
41b9077
Playing around with animation timings again
randi-c-dubs Jul 19, 2023
7fe3357
Readding virtual jibo control
randi-c-dubs Jul 19, 2023
27e1403
Trying to resolve build issue
randi-c-dubs Jul 21, 2023
7be3db3
Found issue in jiboNameModal
randi-c-dubs Jul 21, 2023
0569e81
Bringing relevant extensions to the front
randi-c-dubs Jul 21, 2023
bebd11f
Updating text classification extension
randi-c-dubs Jul 21, 2023
6747a4a
Combining TTS and STT extensions
randi-c-dubs Jul 21, 2023
71035cf
Adding Spotify extension
randi-c-dubs Jul 21, 2023
95b2dd1
Removing menu bar link and spotify extension
randi-c-dubs Jul 21, 2023
3d69ded
Putting back tutorials button
randi-c-dubs Jul 21, 2023
67aeed8
Adding in progress bar
randi-c-dubs Jul 21, 2023
40285aa
Virtual Jibo ask block
randi-c-dubs Jul 22, 2023
31281fc
Add animation for switching Jibo icon
randi-c-dubs Jul 23, 2023
de80ebe
Adding virtual Jibo lookat
randi-c-dubs Jul 23, 2023
a604725
Adding Jibo emotion animation
randi-c-dubs Jul 23, 2023
55fd86a
Updating Dropbox regex
randi-c-dubs Jul 23, 2023
8d631eb
Removing Teachable Machine link
randi-c-dubs Jul 23, 2023
08b057d
Adding tutorials
randi-c-dubs Jul 23, 2023
754a8bf
Updated wording of text classifier blocks
randi-c-dubs Jul 25, 2023
cbf2266
Changing text classifier block wording
randi-c-dubs Jul 25, 2023
fe1ee21
Updating text classifier tutorial
randi-c-dubs Jul 25, 2023
431038c
Fixing boolean blocks
randi-c-dubs Jul 25, 2023
471cb67
Removing comments with my name
randi-c-dubs Jul 26, 2023
7ee3ff4
Fixing bug
randi-c-dubs Jul 26, 2023
3d91652
Save log of progress tab accesses
randi-c-dubs Jul 26, 2023
3e87354
Removing erroneous reference to old jibo ext
randi-c-dubs Jul 26, 2023
3ccaafb
Fixing no-cors errors
randi-c-dubs Jul 26, 2023
d515954
Jibo disconnect functionality
randi-c-dubs Nov 14, 2023
a9ae111
Update IconArgument.svelte with new airplane emoji
randi-c-dubs Jun 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/generate-extension-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ jobs:
run: npm run init
- name: Generate
run: npm run document:extensions
- name: Commit (without CI)
run: |
git config --local user.email ""
git config --local user.name "GitHub Action"
git add extensions/README.md
git commit -m "[skip ci] re-generate extension documentation"
- name: Push
run: git push
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 're-generate extension documentation\n\nskip-checks:true'
file_pattern: 'extensions/README.md'
file_pattern: 'extensions/README.md'
1 change: 1 addition & 0 deletions extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,7 @@ export default class extends extension({ name: "Block Utility example" }) {
```



## Adding Custom Arguments

> NOTE: This is a generated README section, so no edits you make to it in this file will be saved.
Expand Down
2 changes: 1 addition & 1 deletion extensions/documentation/src/blockUtility/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ This can help you do things like:

PRG has added an additional property to the `BlockUtility`, the `blockID` field, which allows you to uniquely associate an invocation of your block method with a block in the user's environment. Access it as demonstrated below:

[](./index.ts)
[](./index.ts)
1 change: 1 addition & 0 deletions extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"author": "",
"license": "ISC",
"devDependencies": {
"stacktrace-js": "^2.0.2",
"svelte": "^3.55.0"
}
}
502 changes: 258 additions & 244 deletions extensions/scripts/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion extensions/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
"typescript": "$typescript"
}
}
}
}
2 changes: 1 addition & 1 deletion extensions/src/common/extension/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export type TypedSetterDecorator<This, TValue> =
(target: (value: TValue) => void, context: ClassSetterDecoratorContext<This, TValue>) => void;

export type TypedGetterDecorator<This, Return> =
(target: () => Return, context: ClassGetterDecoratorContext<This, Return>) => void;
(target: () => Return, context: ClassGetterDecoratorContext<This, Return>) => void;
2 changes: 1 addition & 1 deletion extensions/src/common/extension/mixins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ export type ExtensionWithFunctionality<TSupported extends MixinName[], TBase ext
/** Base case */
: TBase;

export type ExtensionInstanceWithFunctionality<TSupported extends MixinName[]> = InstanceType<ExtensionWithFunctionality<TSupported>>;
export type ExtensionInstanceWithFunctionality<TSupported extends MixinName[]> = InstanceType<ExtensionWithFunctionality<TSupported>>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Class adapted from: https://github.com/LLK/scratch-svg-renderer/blob/develop/src/bitmap-adapter.js
*/
export default class {
private makeImage() { return new Image() }
private makeCanvas() { return document.createElement('canvas') }

/**
* Return a canvas with the resized version of the given image, done using nearest-neighbor interpolation
* @param {CanvasImageSource} image The image to resize
* @param {int} newWidth The desired post-resize width of the image
* @param {int} newHeight The desired post-resize height of the image
* @returns {HTMLCanvasElement} A canvas with the resized image drawn on it.
*/
resize(image, newWidth, newHeight) {
// We want to always resize using nearest-neighbor interpolation. However, canvas implementations are free to
// use linear interpolation (or other "smooth" interpolation methods) when downscaling:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1360415
// It seems we can get around this by resizing in two steps: first width, then height. This will always result
// in nearest-neighbor interpolation, even when downscaling.
const stretchWidthCanvas = this.makeCanvas();
stretchWidthCanvas.width = newWidth;
stretchWidthCanvas.height = image.height;
let context = stretchWidthCanvas.getContext('2d');
context.imageSmoothingEnabled = false;
context.drawImage(image, 0, 0, stretchWidthCanvas.width, stretchWidthCanvas.height);
const stretchHeightCanvas = this.makeCanvas();
stretchHeightCanvas.width = newWidth;
stretchHeightCanvas.height = newHeight;
context = stretchHeightCanvas.getContext('2d');
context.imageSmoothingEnabled = false;
context.drawImage(stretchWidthCanvas, 0, 0, stretchHeightCanvas.width, stretchHeightCanvas.height);
return stretchHeightCanvas;
}

/**
* Scratch 2.0 had resolution 1 and 2 bitmaps. All bitmaps in Scratch 3.0 are equivalent
* to resolution 2 bitmaps. Therefore, converting a resolution 1 bitmap means doubling
* it in width and height.
* @param {!string} dataURI Base 64 encoded image data of the bitmap
* @param {!function} callback Node-style callback that returns updated dataURI if conversion succeeded
*/
convertResolution1Bitmap(dataURI, callback) {
const image = new Image();
image.src = dataURI;
image.onload = () => {
callback(null, this.resize(image, image.width * 2, image.height * 2).toDataURL());
};
image.onerror = () => {
callback('Image load failed');
};
}

/**
* Given width/height of an uploaded item, return width/height the image will be resized
* to in Scratch 3.0
* @param {!number} oldWidth original width
* @param {!number} oldHeight original height
* @return {object} Array of new width, new height
*/
getResizedWidthHeight(oldWidth, oldHeight) {
const STAGE_WIDTH = 480;
const STAGE_HEIGHT = 360;
const STAGE_RATIO = STAGE_WIDTH / STAGE_HEIGHT;

// If both dimensions are smaller than or equal to corresponding stage dimension,
// double both dimensions
if ((oldWidth <= STAGE_WIDTH) && (oldHeight <= STAGE_HEIGHT)) {
return { width: oldWidth * 2, height: oldHeight * 2 };
}

// If neither dimension is larger than 2x corresponding stage dimension,
// this is an in-between image, return it as is
if ((oldWidth <= STAGE_WIDTH * 2) && (oldHeight <= STAGE_HEIGHT * 2)) {
return { width: oldWidth, height: oldHeight };
}

const imageRatio = oldWidth / oldHeight;
// Otherwise, figure out how to resize
if (imageRatio >= STAGE_RATIO) {
// Wide Image
return { width: STAGE_WIDTH * 2, height: STAGE_WIDTH * 2 / imageRatio };
}
// In this case we have either:
// - A wide image, but not with as big a ratio between width and height,
// making it so that fitting the width to double stage size would leave
// the height too big to fit in double the stage height
// - A square image that's still larger than the double at least
// one of the stage dimensions, so pick the smaller of the two dimensions (to fit)
// - A tall image
// In any of these cases, resize the image to fit the height to double the stage height
return { width: STAGE_HEIGHT * 2 * imageRatio, height: STAGE_HEIGHT * 2 };
}

/**
* Given bitmap data, resize as necessary.
* @param {string} fileData Base 64 encoded image data of the bitmap
* @param {string} fileType The MIME type of this file
* @returns {Promise} Resolves to resized image data Uint8Array
*/
importBitmap(dataURI: string): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
const image = this.makeImage();
image.src = dataURI;
image.onload = () => {
const newSize = this.getResizedWidthHeight(image.width, image.height);
if (newSize.width === image.width && newSize.height === image.height) {
// No change
resolve(this.convertDataURIToBinary(dataURI));
} else {
const resizedDataURI = this.resize(image, newSize.width, newSize.height).toDataURL();
resolve(this.convertDataURIToBinary(resizedDataURI));
}
};
image.onerror = () => {
reject('Image load failed');
};
});
}

// TODO consolidate with scratch-vm/src/util/base64-util.js
// From https://gist.github.com/borismus/1032746
convertDataURIToBinary(dataURI) {
const BASE64_MARKER = ';base64,';
const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
const base64 = dataURI.substring(base64Index);
const raw = window.atob(base64);
const rawLength = raw.length;
const array = new Uint8Array(new ArrayBuffer(rawLength));

for (let i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}
}
112 changes: 112 additions & 0 deletions extensions/src/common/extension/mixins/optional/addCostumes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type RenderedTarget from "$scratch-vm/sprites/rendered-target";
import Target from "$scratch-vm/engine/target";
import { MinimalExtensionConstructor } from "../../required";
import MockBitmapAdapter from "./MockBitmapAdapter";
import { getUrlHelper } from "./utils";

let bitmapAdapter: MockBitmapAdapter;
let urlHelper: ReturnType<typeof getUrlHelper>;

const rendererKey: keyof RenderedTarget = "renderer";
const isRenderedTarget = (target: Target | RenderedTarget): target is RenderedTarget => rendererKey in target;

/**
* Mixin the ability for extensions to add costumes to sprites
* @param Ctor
* @returns
* @see https://www.typescriptlang.org/docs/handbook/mixins.html
*/
export default function <T extends MinimalExtensionConstructor>(Ctor: T) {
abstract class ExtensionWithCustomSupport extends Ctor {

/**
* Add a costume to the current sprite based on some image data
* @param {RenderedTarget} target (e.g. `util.target`)
* @param {ImageData} image What image to use to create the costume
* @param {"add only" | "add and set"} action What action should be applied
* - **_add only_**: generates the costume and append it it to the sprite's costume library
* - **_add and set_**: Both generate the costume (adding it to the sprite's costume library) and set it as the sprite's current costume
* @param {string?} name optional name to attach to the costume
*/
async addCostume(target: Target, image: ImageData, action: "add only" | "add and set", name?: string) {
if (!isRenderedTarget(target)) return console.warn("Costume could not be added as the supplied target wasn't a rendered target");

name ??= `${this.id}_generated_${Date.now()}`;
bitmapAdapter ??= new MockBitmapAdapter();
urlHelper ??= getUrlHelper(image);

// storage is of type: https://github.com/LLK/scratch-storage/blob/develop/src/ScratchStorage.js
const { storage } = this.runtime;
const dataFormat = storage.DataFormat.PNG;
const assetType = storage.AssetType.ImageBitmap;
const dataBuffer = await bitmapAdapter.importBitmap(urlHelper.getDataURL(image));

const asset = storage.createAsset(assetType, dataFormat, dataBuffer, null, true);
const { assetId } = asset;
const costume = { name, dataFormat, asset, md5: `${assetId}.${dataFormat}`, assetId };

await this.runtime.addCostume(costume);

const { length } = target.getCostumes();

target.addCostume(costume, length);
if (action === "add and set") target.setCostume(length);
}

/**
* Add a costume to the current sprite based on a bitmpa input
* @param {RenderedTarget} target (e.g. `util.target`)
* @param {string} bitmapImage What image to use to create the costume
* @param {"add only" | "add and set"} action What action should be applied
* - **_add only_**: generates the costume and append it it to the sprite's costume library
* - **_add and set_**: Both generate the costume (adding it to the sprite's costume library) and set it as the sprite's current costume
* @param {string?} name optional name to attach to the costume
*/
async addCostumeBitmap(target: Target, bitmapImage: string, action: "add only" | "add and set", name?: string) {
if (!isRenderedTarget(target)) return console.warn("Costume could not be added as the supplied target wasn't a rendered target");

name ??= `${this.id}_generated_${Date.now()}`;
bitmapAdapter ??= new MockBitmapAdapter();
//urlHelper ??= getUrlHelper(image);

// storage is of type: https://github.com/LLK/scratch-storage/blob/develop/src/ScratchStorage.js
const { storage } = this.runtime;
const dataFormat = storage.DataFormat.PNG;
const assetType = storage.AssetType.ImageBitmap;
const dataBuffer = await bitmapAdapter.importBitmap(bitmapImage);

const asset = storage.createAsset(assetType, dataFormat, dataBuffer, null, true);
const { assetId } = asset;
const costume = { name, dataFormat, asset, md5: `${assetId}.${dataFormat}`, assetId };

await this.runtime.addCostume(costume);

const { length } = target.getCostumes();

target.addCostume(costume, length);
if (action === "add and set") target.setCostume(length);
}

/**
* Add a costume to the current sprite based on same image data
* @param {RenderedTarget} target (e.g. `util.target`)
* @param {string?} name costume name to look for
*/
setCostumeByName(target: Target, name: string): boolean {
if (!isRenderedTarget(target)) {
console.warn("Costume could not be set as the supplied target wasn't a rendered target");
return false;
}

let costumeIdx = target.getCostumeIndexByName(name);
if (costumeIdx >= 0) {
target.setCostume(costumeIdx);
return true;
}
return false;
}

}

return ExtensionWithCustomSupport;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export const getUrlHelper = (dimensions: { width: number, height: number }) => {
const canvas = document.body.appendChild(document.createElement("canvas"));

const setDimensions = ({ width, height }: Parameters<typeof getUrlHelper>[0]) => {
if (canvas.width !== width) canvas.width = width;
if (canvas.height !== height) canvas.height = height;
};

setDimensions(dimensions);

canvas.hidden = true;
const context = canvas.getContext("2d");

return {
/**
*
* @param image
* @returns
*/
getDataURL(image: ImageData) {
const { width, height } = image;
setDimensions(image);
context.save();
context.clearRect(0, 0, width, height);
context.putImageData(image, 0, 0);
const url = canvas.toDataURL('image/png');
context.restore();
return url;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { block } from "$common/extension/decorators/blocks";
import { withDependencies } from "../../dependencies";
import { MinimalExtensionConstructor } from "../../required";
import video from "../video";

/**
* Mixin a 'setVideoTransparency' Block to control the transparency of the videofeed
* @param Ctor
* @returns
* @see https://www.typescriptlang.org/docs/handbook/mixins.html
*/
export default function <T extends MinimalExtensionConstructor>(Ctor: T) {
abstract class ExtensionWithSetVideoTransparencyBlock extends withDependencies(Ctor, video) {
/**
* A `command` block that takes a single number argument and uses it to set the transparency of the video feed.
* @param transparency What transparency to set -- a higher number is more transparent (thus '100' is fully invisible)
*/
@block({
type: "command",
text: (transparency) => `Set video to ${transparency}% transparent`,
arg: "number"
})
setVideoTransparencyBlock(transparency: number) {
this.setVideoTransparency(transparency);
}
}

return ExtensionWithSetVideoTransparencyBlock;
}
Loading
Loading