Skip to content

AS-4-inline-images #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions .github/workflows/generate-extension-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Generate Extension Documentation

on: [push]
permissions:
contents: write
pages: write
id-token: write
jobs:
generate:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.17.1]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Initialize
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*

.DS_Store
**/.DS_Store

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

Expand Down
56 changes: 55 additions & 1 deletion extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ This document will be most helpful for people doing more complex development, li
6. [Saving Custom Data for an Extension](#saving-custom-data-for-an-extension)
7. [Making use of the Block Utility](#making-use-of-the-block-utility)
8. [Adding Custom Arguments](#adding-custom-arguments)
9. [Reference](#reference)
9. [Adding inline images to the text of blocks](#adding-inline-images-to-the-text-of-blocks)
10. [Reference](#reference)

## Anatomy of an Extension Directory

Expand Down Expand Up @@ -1350,6 +1351,59 @@ Then, we modify the UI (Svelte) component we created earlier to match our block
> * https://github.com/mitmedialab/prg-extension-boilerplate/tree/dev/extensions#creating-ui-for-extensions


# Adding inline images to the text of blocks

> NOTE: This is a generated README section, so no edits you make to it in this file will be saved.
If you want to edit it, please go to [extensions/documentation/src/inlineImages/README.md](documentation/src/inlineImages/README.md)

As noted in [Scratch's extension documentation](https://github.com/scratchfoundation/scratch-vm/blob/develop/docs/extensions.md#adding-an-inline-image), Blocks support arguments that can display images inline within their text display.

We can make use of this feature within the framework by adding an extra argument of type `"inline image"` to our extension's method, and then seperately add an `arg` (or `args`) entry within the associated `@block` decorator invocation.

See the below example (which assumes that a file `myPic.png` is located in the same directory as our code):

```ts

import { Environment, block, extension } from "$common";
// We import our image as if it was a code file
import myPic from "./myPic.png";

export default class ExampleExtensionWithInlineImages extends extension({
name: "This is an example extension with inline images",
}) {
override init(env: Environment) { }

@block({
type: "command",
text: (image) => `Here's an inline image: ${image}`,
arg: {
type: "image",
uri: myPic,
alt: "this is a test image", // description of the image for screen readers
flipRTL: true,
}
})
methodWithOnlyInlineImage(image: "inline image") {
// NOTE: The `image` argument should not be used
}

@block({
type: "command",
text: (someNumber, image, someString) => `Here's a number ${someNumber} and picture ${image} and string ${someString}}`,
args: [
{ type: "number" },
{ type: "image", uri: myPic, alt: "this is a test image", flipRTL: true },
"string"
]
})
methodWithInlineImageAndOtherArguments(someNumber: number, image: "inline image", someString: string) {
// NOTE: The `image` argument should not be used
}
}

```


## Reference

> NOTE: This is a generated README section, so no edits you make to it in this file will be saved.
Expand Down
9 changes: 9 additions & 0 deletions extensions/documentation/src/inlineImages/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Adding inline images to the text of blocks

As noted in [Scratch's extension documentation](https://github.com/scratchfoundation/scratch-vm/blob/develop/docs/extensions.md#adding-an-inline-image), Blocks support arguments that can display images inline within their text display.

We can make use of this feature within the framework by adding an extra argument of type `"inline image"` to our extension's method, and then seperately add an `arg` (or `args`) entry within the associated `@block` decorator invocation.

See the below example (which assumes that a file `myPic.png` is located in the same directory as our code):

[](./index.ts?export=x)
42 changes: 42 additions & 0 deletions extensions/documentation/src/inlineImages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { codeSnippet } from "documentation";

export const x = codeSnippet();

import { Environment, block, extension } from "$common";
// We import our image as if it was a code file
import myPic from "./myPic.png";

export default class ExampleExtensionWithInlineImages extends extension({
name: "This is an example extension with inline images",
}) {
override init(env: Environment) { }

@block({
type: "command",
text: (image) => `Here's an inline image: ${image}`,
arg: {
type: "image",
uri: myPic,
alt: "this is a test image", // description of the image for screen readers
flipRTL: true,
}
})
methodWithOnlyInlineImage(image: "inline image") {
// NOTE: The `image` argument should not be used
}

@block({
type: "command",
text: (someNumber, image, someString) => `Here's a number ${someNumber} and picture ${image} and string ${someString}}`,
args: [
{ type: "number" },
{ type: "image", uri: myPic, alt: "this is a test image", flipRTL: true },
"string"
]
})
methodWithInlineImageAndOtherArguments(someNumber: number, image: "inline image", someString: string) {
// NOTE: The `image` argument should not be used
}
}

x.end;
6 changes: 5 additions & 1 deletion extensions/documentation/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,9 @@
"skipLibCheck": true,
"ignoreDeprecations": "5.0"
},
"include": [".**/*.ts", "**/*.ts"],
"include": [
".**/*.ts",
"**/*.ts",
"../src/declaration.d.ts"
],
}
2 changes: 2 additions & 0 deletions extensions/scripts/bundles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import commonjs from "@rollup/plugin-commonjs";
import { terser } from "rollup-plugin-terser";
import css from 'rollup-plugin-css-only';
import json from '@rollup/plugin-json';
import image from '@rollup/plugin-image';
import nodePolyfills from 'rollup-plugin-polyfill-node';
import babel from "@rollup/plugin-babel";
import chalk from "chalk";
Expand Down Expand Up @@ -79,6 +80,7 @@ export const getThirdPartyPlugins = (customizations?: { tsTransformers?: Program
babelHelpers: "bundled",
}),
css(),
image(),
terser(),
];

Expand Down
47 changes: 47 additions & 0 deletions extensions/scripts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions extensions/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^23.0.4",
"@rollup/plugin-image": "^3.0.2",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-typescript": "^11.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Argument, ArgumentType, BlockMetadata, BlockOperation, ExtensionArgumentMetadata, Menu, MultipleArgsBlock, OneArgBlock, ValidKey, ValueOf, VerboseArgument } from "$common/types";
import { Argument, ArgumentType, BlockMetadata, BlockOperation, ExtensionArgumentMetadata, InlineImage, Menu, MultipleArgsBlock, OneArgBlock, ValidKey, ValueOf, VerboseArgument } from "$common/types";
import { assertSameLength, isPrimitive, isString } from "$common/utils";
import { extractHandlers } from "./handlers";
import { setMenu } from "./menus";
Expand Down Expand Up @@ -42,6 +42,8 @@ export const convertToArgumentInfo = (opcode: string, args: readonly Argument<an
return Object.fromEntries(
args
.map((element, index) => {
if (isInlineImage(element)) return { ...element, dataURI: element.uri };

const entry = {} as ExtensionArgumentMetadata;
entry.type = getArgumentType(element);

Expand Down Expand Up @@ -70,4 +72,6 @@ const getDefaultValue = (defaultValue: any, opcode: string, index: number) => is
const setDefaultValue = (entry: ExtensionArgumentMetadata, opcode: string, index: number, defaultValue: any,) => {
if (defaultValue === undefined) return;
entry.defaultValue = getDefaultValue(defaultValue, opcode, index)
}
}

const isInlineImage = (arg: Argument<any>): arg is InlineImage => !isString(arg) && arg.type === ArgumentType.Image;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Argument, DynamicMenuThatAcceptsReporters, Menu, MenuThatAcceptsReporters, VerboseArgument } from "$common/types";
import { Argument, ArgumentType, DynamicMenuThatAcceptsReporters, Menu, MenuThatAcceptsReporters, VerboseArgument } from "$common/types";
import { isPrimitive, identity } from "$common/utils";

export type Handler = (MenuThatAcceptsReporters<any>['handler']);
Expand All @@ -8,6 +8,7 @@ const hasHandler = (options: Menu<any>): options is MenuThatAcceptsReporters<any

export const extractHandlers = (args: readonly Argument<any>[]): Handler[] => args.map(element => {
if (!isVerbose(element)) return identity;
if (element.type === ArgumentType.Image) return identity;
const { options } = element;
if (!hasHandler(options)) return identity;
return options.handler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { ExtensionIntanceWithFunctionality } from "../..";

export const getImplementationName = (opcode: string) => `internal_${opcode}`;

const inlineImageAccessError = "ERROR: This argument represents an inline image and should not be accessed.";

/**
* Wraps a blocks operation so that the arguments passed from Scratch are first extracted and then passed as indices in a parameter array.
* @param _this What will be bound to the 'this' context of the underlying operation
Expand All @@ -29,6 +31,7 @@ export const wrapOperation = <T extends MinimalExtensionInstance>(
) => _this.supports("customArguments")
? function (this: ExtensionIntanceWithFunctionality<["customArguments"]>, argsFromScratch: Record<string, any>, blockUtility: BlockUtility) {
const castedArguments = args.map(({ name, type, handler }) => {
if (type === ArgumentType.Image) return inlineImageAccessError;
const param = argsFromScratch[name];
switch (type) {
case ArgumentType.Custom:
Expand All @@ -43,7 +46,9 @@ export const wrapOperation = <T extends MinimalExtensionInstance>(
}
: function (this: T, argsFromScratch: Record<string, any>, blockUtility: BlockUtility) {
const castedArguments = args.map(({ name, type, handler }) =>
castToType(type, handler.call(_this, argsFromScratch[name]))
type === ArgumentType.Image
? inlineImageAccessError
: castToType(type, handler.call(_this, argsFromScratch[name]))
);
return operation.call(_this, ...castedArguments, blockUtility);
}
Expand Down
Loading