-
Notifications
You must be signed in to change notification settings - Fork 3
Add text2speech, translate, and videosensing blocks with tests #154
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
Open
nakasyou
wants to merge
1
commit into
main
Choose a base branch
from
codex/resolve-issue-151-f8fmgz
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { describe, expect, test } from 'vite-plus/test' | ||
| import { block, createBlocks } from '../core/composer' | ||
| import { setLanguage, setVoice, speakAndWait } from './text2speech' | ||
|
|
||
| describe('blocks/text2speech', () => { | ||
| test('creates text to speech extension blocks', () => { | ||
| const blocks = createBlocks(() => { | ||
| block('event_whenflagclicked', { topLevel: true }) | ||
| setVoice('TENOR') | ||
| setLanguage('ja') | ||
| speakAndWait('hello') | ||
| }) | ||
|
|
||
| const opcodes = Object.values(blocks).map((b) => b.opcode) | ||
| expect(opcodes).toContain('text2speech_setVoice') | ||
| expect(opcodes).toContain('text2speech_menu_voices') | ||
| expect(opcodes).toContain('text2speech_setLanguage') | ||
| expect(opcodes).toContain('text2speech_menu_languages') | ||
| expect(opcodes).toContain('text2speech_speakAndWait') | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| import { InputType } from 'sb3-types/enum' | ||
| import { fromPrimitiveSource, menuInput } from '../core/block-helper' | ||
| import { block, valueBlock } from '../core/composer' | ||
| import type { HikkakuString, PrimitiveSource } from '../core/types' | ||
|
|
||
| export type TextToSpeechVoice = | ||
| | 'ALTO' | ||
| | 'TENOR' | ||
| | 'SQUEAK' | ||
| | 'GIANT' | ||
| | 'KITTEN' | ||
| | 'GOOGLE' | ||
|
|
||
| /** | ||
| * Speaks text and waits until completion. | ||
| * | ||
| * Input: `words`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param words See function signature for accepted input values. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { speakAndWait } from 'hikkaku/blocks' | ||
| * | ||
| * speakAndWait('hello') | ||
| * ``` | ||
| */ | ||
| export const speakAndWait = (words: PrimitiveSource<HikkakuString>) => { | ||
| return block('text2speech_speakAndWait', { | ||
| inputs: { | ||
| WORDS: fromPrimitiveSource(InputType.String, words, 'hello'), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Sets the text-to-speech voice. | ||
| * | ||
| * Input: `voice`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param voice See function signature for accepted input values. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { setVoice } from 'hikkaku/blocks' | ||
| * | ||
| * setVoice('TENOR') | ||
| * ``` | ||
| */ | ||
| export function setVoice(voice: TextToSpeechVoice): ReturnType<typeof block> | ||
| export function setVoice( | ||
| voice: PrimitiveSource<HikkakuString>, | ||
| ): ReturnType<typeof block> | ||
| export function setVoice(voice: PrimitiveSource<HikkakuString>) { | ||
| return block('text2speech_setVoice', { | ||
| inputs: { | ||
| VOICE: menuInput(voice, menuOfTextToSpeechVoice), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfTextToSpeechVoice = (voice: TextToSpeechVoice = 'ALTO') => { | ||
| return valueBlock<HikkakuString>('text2speech_menu_voices', { | ||
| fields: { | ||
| voices: [voice, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Sets the text-to-speech language. | ||
| * | ||
| * Input: `language`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param language See function signature for accepted input values. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { setLanguage } from 'hikkaku/blocks' | ||
| * | ||
| * setLanguage('ja') | ||
| * ``` | ||
| */ | ||
| export const setLanguage = (language: PrimitiveSource<HikkakuString>) => { | ||
| return block('text2speech_setLanguage', { | ||
| inputs: { | ||
| LANGUAGE: menuInput(language, menuOfTextToSpeechLanguage), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfTextToSpeechLanguage = (language: string = 'en') => { | ||
| return valueBlock<HikkakuString>('text2speech_menu_languages', { | ||
| fields: { | ||
| languages: [language, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { describe, expect, test } from 'vite-plus/test' | ||
| import { block, createBlocks } from '../core/composer' | ||
| import { say } from './looks' | ||
| import { getViewerLanguage, translate } from './translate' | ||
|
|
||
| describe('blocks/translate', () => { | ||
| test('creates translate extension blocks', () => { | ||
| const blocks = createBlocks(() => { | ||
| block('event_whenflagclicked', { topLevel: true }) | ||
| say(translate('hello', 'ja')) | ||
| say(getViewerLanguage()) | ||
| }) | ||
|
|
||
| const opcodes = Object.values(blocks).map((b) => b.opcode) | ||
| expect(opcodes).toContain('translate_getTranslate') | ||
| expect(opcodes).toContain('translate_menu_languages') | ||
| expect(opcodes).toContain('translate_getViewerLanguage') | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { InputType } from 'sb3-types/enum' | ||
| import { fromPrimitiveSource, menuInput } from '../core/block-helper' | ||
| import { valueBlock } from '../core/composer' | ||
| import type { HikkakuString, PrimitiveSource } from '../core/types' | ||
|
|
||
| /** | ||
| * Translates words to a target language. | ||
| * | ||
| * Input: `words`, `language`. | ||
| * Output: Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * | ||
| * @param words See function signature for accepted input values. | ||
| * @param language See function signature for accepted input values. | ||
| * @returns Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * @example | ||
| * ```ts | ||
| * import { translate } from 'hikkaku/blocks' | ||
| * | ||
| * translate('hello', 'ja') | ||
| * ``` | ||
| */ | ||
| export const translate = ( | ||
| words: PrimitiveSource<HikkakuString>, | ||
| language: PrimitiveSource<HikkakuString>, | ||
| ) => { | ||
| return valueBlock<HikkakuString>('translate_getTranslate', { | ||
| inputs: { | ||
| WORDS: fromPrimitiveSource(InputType.String, words, 'hello'), | ||
| LANGUAGE: menuInput(language, menuOfTranslateLanguage), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfTranslateLanguage = (language: string = 'ja') => { | ||
| return valueBlock<HikkakuString>('translate_menu_languages', { | ||
| fields: { | ||
| languages: [language, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Returns viewer language. | ||
| * | ||
| * Input: none. | ||
| * Output: Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * | ||
| * @returns Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * @example | ||
| * ```ts | ||
| * import { getViewerLanguage } from 'hikkaku/blocks' | ||
| * | ||
| * getViewerLanguage() | ||
| * ``` | ||
| */ | ||
| export const getViewerLanguage = () => { | ||
| return valueBlock<HikkakuString>('translate_getViewerLanguage', {}) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { describe, expect, test } from 'vite-plus/test' | ||
| import { createBlocks } from '../core/composer' | ||
| import { say } from './looks' | ||
| import { | ||
| setVideoTransparency, | ||
| videoOn, | ||
| videoToggle, | ||
| whenMotionGreaterThan, | ||
| } from './videosensing' | ||
|
|
||
| describe('blocks/videosensing', () => { | ||
| test('creates video sensing extension blocks', () => { | ||
| const blocks = createBlocks(() => { | ||
| whenMotionGreaterThan(10, () => { | ||
| setVideoTransparency(50) | ||
| }) | ||
| say(videoOn('motion', 'this sprite')) | ||
| videoToggle('on') | ||
| }) | ||
|
|
||
| const opcodes = Object.values(blocks).map((b) => b.opcode) | ||
| expect(opcodes).toContain('videoSensing_whenMotionGreaterThan') | ||
| expect(opcodes).toContain('videoSensing_videoOn') | ||
| expect(opcodes).toContain('videoSensing_menu_ATTRIBUTE') | ||
| expect(opcodes).toContain('videoSensing_menu_SUBJECT') | ||
| expect(opcodes).toContain('videoSensing_videoToggle') | ||
| expect(opcodes).toContain('videoSensing_menu_VIDEO_STATE') | ||
| expect(opcodes).toContain('videoSensing_setVideoTransparency') | ||
|
|
||
| const hat = Object.values(blocks).find( | ||
| (b) => b.opcode === 'videoSensing_whenMotionGreaterThan', | ||
| ) | ||
| expect(hat?.topLevel).toBe(true) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { InputType } from 'sb3-types/enum' | ||
| import { fromPrimitiveSource, menuInput } from '../core/block-helper' | ||
| import { attachStack, block, valueBlock } from '../core/composer' | ||
| import type { | ||
| HikkakuNumber, | ||
| HikkakuString, | ||
| PrimitiveSource, | ||
| } from '../core/types' | ||
|
|
||
| export type VideoSensingAttribute = 'motion' | 'direction' | ||
| export type VideoSensingSubject = 'this sprite' | 'stage' | ||
| export type VideoState = 'on' | 'off' | 'on-flipped' | ||
|
|
||
| /** | ||
| * Runs when motion is greater than a threshold. | ||
| * | ||
| * Input: `reference`, `stack`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param reference See function signature for accepted input values. | ||
| * @param stack See function signature for accepted input values. Optional. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { whenMotionGreaterThan } from 'hikkaku/blocks' | ||
| * | ||
| * whenMotionGreaterThan(10, () => {}) | ||
| * ``` | ||
| */ | ||
| export const whenMotionGreaterThan = ( | ||
| reference: PrimitiveSource<HikkakuNumber>, | ||
| stack?: () => void, | ||
| ) => { | ||
| const res = block('videoSensing_whenMotionGreaterThan', { | ||
| topLevel: true, | ||
| inputs: { | ||
| REFERENCE: fromPrimitiveSource(InputType.Number, reference, 10), | ||
| }, | ||
| }) | ||
| attachStack(res.id, stack) | ||
| return res | ||
| } | ||
|
|
||
| /** | ||
| * Returns motion or direction detected from a subject. | ||
| * | ||
| * Input: `attribute`, `subject`. | ||
| * Output: Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * | ||
| * @param attribute See function signature for accepted input values. | ||
| * @param subject See function signature for accepted input values. | ||
| * @returns Scratch reporter block definition that can be used as an input value in other blocks. | ||
| * @example | ||
| * ```ts | ||
| * import { videoOn } from 'hikkaku/blocks' | ||
| * | ||
| * videoOn('motion', 'this sprite') | ||
| * ``` | ||
| */ | ||
| export const videoOn = ( | ||
| attribute: PrimitiveSource<HikkakuString>, | ||
| subject: PrimitiveSource<HikkakuString>, | ||
| ) => { | ||
| return valueBlock<HikkakuNumber>('videoSensing_videoOn', { | ||
| inputs: { | ||
| ATTRIBUTE: menuInput(attribute, menuOfVideoSensingAttribute), | ||
| SUBJECT: menuInput(subject, menuOfVideoSensingSubject), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfVideoSensingAttribute = ( | ||
| attribute: VideoSensingAttribute = 'motion', | ||
| ) => { | ||
| return valueBlock<HikkakuString>('videoSensing_menu_ATTRIBUTE', { | ||
| fields: { | ||
| ATTRIBUTE: [attribute, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfVideoSensingSubject = ( | ||
| subject: VideoSensingSubject = 'this sprite', | ||
| ) => { | ||
| return valueBlock<HikkakuString>('videoSensing_menu_SUBJECT', { | ||
| fields: { | ||
| SUBJECT: [subject, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Turns video on/off/flipped. | ||
| * | ||
| * Input: `state`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param state See function signature for accepted input values. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { videoToggle } from 'hikkaku/blocks' | ||
| * | ||
| * videoToggle('on') | ||
| * ``` | ||
| */ | ||
| export const videoToggle = (state: PrimitiveSource<HikkakuString>) => { | ||
| return block('videoSensing_videoToggle', { | ||
| inputs: { | ||
| VIDEO_STATE: menuInput(state, menuOfVideoSensingState), | ||
| }, | ||
| }) | ||
| } | ||
|
|
||
| export const menuOfVideoSensingState = (state: VideoState = 'on') => { | ||
| return valueBlock<HikkakuString>('videoSensing_menu_VIDEO_STATE', { | ||
| fields: { | ||
| VIDEO_STATE: [state, null], | ||
| }, | ||
| isShadow: true, | ||
| }) | ||
| } | ||
|
|
||
| /** | ||
| * Sets video transparency. | ||
| * | ||
| * Input: `transparency`. | ||
| * Output: Scratch statement block definition that is appended to the current script stack. | ||
| * | ||
| * @param transparency See function signature for accepted input values. | ||
| * @returns Scratch statement block definition that is appended to the current script stack. | ||
| * @example | ||
| * ```ts | ||
| * import { setVideoTransparency } from 'hikkaku/blocks' | ||
| * | ||
| * setVideoTransparency(50) | ||
| * ``` | ||
| */ | ||
| export const setVideoTransparency = ( | ||
| transparency: PrimitiveSource<HikkakuNumber>, | ||
| ) => { | ||
| return block('videoSensing_setVideoTransparency', { | ||
| inputs: { | ||
| TRANSPARENCY: fromPrimitiveSource(InputType.Number, transparency, 50), | ||
| }, | ||
| }) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this test, blocks created after calling
whenMotionGreaterThan(…, () => { … })are emitted in the root build scope; duringcreateBlocks(),applyNextAndParent()will link those root blocks after the hat and can overwrite the hat’snextpointer (whichattachStack()set to the substack). That can disconnect the intended stack fromwhenMotionGreaterThan, meaning the test isn’t actually validating the hat+stack wiring. Consider movingsay(videoOn(...))/videoToggle('on')into the hat callback (if they should be in that script), or start a separate top-level script before them (if they should be separate), and optionally assert the hat’snextpoints at the first stacked block.