diff --git a/packages/hikkaku/src/blocks/index.ts b/packages/hikkaku/src/blocks/index.ts index 0eebc3f..3ace958 100644 --- a/packages/hikkaku/src/blocks/index.ts +++ b/packages/hikkaku/src/blocks/index.ts @@ -9,3 +9,6 @@ export * from './pen' export * from './procedures' export * from './sensing' export * from './sound' +export * from './text2speech' +export * from './translate' +export * from './videosensing' diff --git a/packages/hikkaku/src/blocks/text2speech.test.ts b/packages/hikkaku/src/blocks/text2speech.test.ts new file mode 100644 index 0000000..7e173d7 --- /dev/null +++ b/packages/hikkaku/src/blocks/text2speech.test.ts @@ -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') + }) +}) diff --git a/packages/hikkaku/src/blocks/text2speech.ts b/packages/hikkaku/src/blocks/text2speech.ts new file mode 100644 index 0000000..8e65ed1 --- /dev/null +++ b/packages/hikkaku/src/blocks/text2speech.ts @@ -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) => { + 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 +export function setVoice( + voice: PrimitiveSource, +): ReturnType +export function setVoice(voice: PrimitiveSource) { + return block('text2speech_setVoice', { + inputs: { + VOICE: menuInput(voice, menuOfTextToSpeechVoice), + }, + }) +} + +export const menuOfTextToSpeechVoice = (voice: TextToSpeechVoice = 'ALTO') => { + return valueBlock('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) => { + return block('text2speech_setLanguage', { + inputs: { + LANGUAGE: menuInput(language, menuOfTextToSpeechLanguage), + }, + }) +} + +export const menuOfTextToSpeechLanguage = (language: string = 'en') => { + return valueBlock('text2speech_menu_languages', { + fields: { + languages: [language, null], + }, + isShadow: true, + }) +} diff --git a/packages/hikkaku/src/blocks/translate.test.ts b/packages/hikkaku/src/blocks/translate.test.ts new file mode 100644 index 0000000..8afad74 --- /dev/null +++ b/packages/hikkaku/src/blocks/translate.test.ts @@ -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') + }) +}) diff --git a/packages/hikkaku/src/blocks/translate.ts b/packages/hikkaku/src/blocks/translate.ts new file mode 100644 index 0000000..9c8750c --- /dev/null +++ b/packages/hikkaku/src/blocks/translate.ts @@ -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, + language: PrimitiveSource, +) => { + return valueBlock('translate_getTranslate', { + inputs: { + WORDS: fromPrimitiveSource(InputType.String, words, 'hello'), + LANGUAGE: menuInput(language, menuOfTranslateLanguage), + }, + }) +} + +export const menuOfTranslateLanguage = (language: string = 'ja') => { + return valueBlock('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('translate_getViewerLanguage', {}) +} diff --git a/packages/hikkaku/src/blocks/videosensing.test.ts b/packages/hikkaku/src/blocks/videosensing.test.ts new file mode 100644 index 0000000..481b8d1 --- /dev/null +++ b/packages/hikkaku/src/blocks/videosensing.test.ts @@ -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) + }) +}) diff --git a/packages/hikkaku/src/blocks/videosensing.ts b/packages/hikkaku/src/blocks/videosensing.ts new file mode 100644 index 0000000..4e6736b --- /dev/null +++ b/packages/hikkaku/src/blocks/videosensing.ts @@ -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, + 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, + subject: PrimitiveSource, +) => { + return valueBlock('videoSensing_videoOn', { + inputs: { + ATTRIBUTE: menuInput(attribute, menuOfVideoSensingAttribute), + SUBJECT: menuInput(subject, menuOfVideoSensingSubject), + }, + }) +} + +export const menuOfVideoSensingAttribute = ( + attribute: VideoSensingAttribute = 'motion', +) => { + return valueBlock('videoSensing_menu_ATTRIBUTE', { + fields: { + ATTRIBUTE: [attribute, null], + }, + isShadow: true, + }) +} + +export const menuOfVideoSensingSubject = ( + subject: VideoSensingSubject = 'this sprite', +) => { + return valueBlock('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) => { + return block('videoSensing_videoToggle', { + inputs: { + VIDEO_STATE: menuInput(state, menuOfVideoSensingState), + }, + }) +} + +export const menuOfVideoSensingState = (state: VideoState = 'on') => { + return valueBlock('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, +) => { + return block('videoSensing_setVideoTransparency', { + inputs: { + TRANSPARENCY: fromPrimitiveSource(InputType.Number, transparency, 50), + }, + }) +}