moonscratch 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/chunk-DQk6qfdC.mjs +18 -0
  2. package/dist/index.d.mts +1173 -0
  3. package/dist/index.mjs +27135 -0
  4. package/package.json +6 -1
  5. package/.agents/skills/moonbit-agent-guide/LICENSE +0 -202
  6. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +0 -1126
  7. package/.agents/skills/moonbit-agent-guide/SKILL.md +0 -1126
  8. package/.agents/skills/moonbit-agent-guide/ide.md +0 -116
  9. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +0 -106
  10. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +0 -422
  11. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +0 -422
  12. package/.agents/skills/moonbit-practice/SKILL.md +0 -258
  13. package/.agents/skills/moonbit-practice/assets/ci.yaml +0 -25
  14. package/.agents/skills/moonbit-practice/reference/agents.md +0 -1469
  15. package/.agents/skills/moonbit-practice/reference/configuration.md +0 -228
  16. package/.agents/skills/moonbit-practice/reference/ffi.md +0 -229
  17. package/.agents/skills/moonbit-practice/reference/ide.md +0 -189
  18. package/.agents/skills/moonbit-practice/reference/performance.md +0 -217
  19. package/.agents/skills/moonbit-practice/reference/refactor.md +0 -154
  20. package/.agents/skills/moonbit-practice/reference/stdlib.md +0 -351
  21. package/.agents/skills/moonbit-practice/reference/testing.md +0 -228
  22. package/.agents/skills/moonbit-refactoring/LICENSE +0 -21
  23. package/.agents/skills/moonbit-refactoring/SKILL.md +0 -323
  24. package/.githooks/README.md +0 -23
  25. package/.githooks/pre-commit +0 -3
  26. package/.github/workflows/copilot-setup-steps.yml +0 -40
  27. package/AGENTS.md +0 -91
  28. package/PLAN.md +0 -64
  29. package/TODO.md +0 -120
  30. package/benchmarks/calc.bench.ts +0 -144
  31. package/benchmarks/draw.bench.ts +0 -215
  32. package/benchmarks/load.bench.ts +0 -28
  33. package/benchmarks/render.bench.ts +0 -53
  34. package/benchmarks/run.bench.ts +0 -8
  35. package/benchmarks/types.d.ts +0 -15
  36. package/docs/scratch-vm-specs/eventloop.md +0 -103
  37. package/docs/scratch-vm-specs/moonscratch-time-separation.md +0 -50
  38. package/index.html +0 -91
  39. package/js/AGENTS.md +0 -5
  40. package/js/a.ts +0 -52
  41. package/js/assets/AGENTS.md +0 -5
  42. package/js/assets/base64.test.ts +0 -14
  43. package/js/assets/base64.ts +0 -21
  44. package/js/assets/build-asset.test.ts +0 -26
  45. package/js/assets/build-asset.ts +0 -28
  46. package/js/assets/create.test.ts +0 -142
  47. package/js/assets/create.ts +0 -122
  48. package/js/assets/index.test.ts +0 -15
  49. package/js/assets/index.ts +0 -2
  50. package/js/assets/types.ts +0 -26
  51. package/js/assets/validation.test.ts +0 -34
  52. package/js/assets/validation.ts +0 -25
  53. package/js/assets.test.ts +0 -14
  54. package/js/assets.ts +0 -1
  55. package/js/index.test.ts +0 -26
  56. package/js/index.ts +0 -3
  57. package/js/render/index.test.ts +0 -65
  58. package/js/render/index.ts +0 -13
  59. package/js/render/sharp.ts +0 -87
  60. package/js/render/svg.ts +0 -68
  61. package/js/render/types.ts +0 -35
  62. package/js/render/utils.ts +0 -108
  63. package/js/render/webgl.ts +0 -274
  64. package/js/sharp-optional.d.ts +0 -16
  65. package/js/test/helpers.ts +0 -116
  66. package/js/test/hikkaku-sample.test.ts +0 -37
  67. package/js/test/rubik-components.input-motion.test.ts +0 -60
  68. package/js/test/rubik-components.lists.test.ts +0 -49
  69. package/js/test/rubik-components.operators.test.ts +0 -104
  70. package/js/test/rubik-components.pen.test.ts +0 -112
  71. package/js/test/rubik-components.procedures-loops.test.ts +0 -72
  72. package/js/test/rubik-components.variables-branches.test.ts +0 -57
  73. package/js/test/rubik-components.visibility-entry.test.ts +0 -31
  74. package/js/test/test-projects.ts +0 -598
  75. package/js/test/variable.ts +0 -200
  76. package/js/test/warp.test.ts +0 -59
  77. package/js/vm/AGENTS.md +0 -6
  78. package/js/vm/README.md +0 -183
  79. package/js/vm/bindings.test.ts +0 -13
  80. package/js/vm/bindings.ts +0 -5
  81. package/js/vm/compare-operators.test.ts +0 -145
  82. package/js/vm/constants.test.ts +0 -11
  83. package/js/vm/constants.ts +0 -4
  84. package/js/vm/effect-guards.test.ts +0 -68
  85. package/js/vm/effect-guards.ts +0 -44
  86. package/js/vm/factory.test.ts +0 -486
  87. package/js/vm/factory.ts +0 -615
  88. package/js/vm/headless-vm.test.ts +0 -131
  89. package/js/vm/headless-vm.ts +0 -342
  90. package/js/vm/index.test.ts +0 -28
  91. package/js/vm/index.ts +0 -5
  92. package/js/vm/internal-types.ts +0 -32
  93. package/js/vm/json.test.ts +0 -40
  94. package/js/vm/json.ts +0 -273
  95. package/js/vm/normalize.test.ts +0 -48
  96. package/js/vm/normalize.ts +0 -65
  97. package/js/vm/options.test.ts +0 -30
  98. package/js/vm/options.ts +0 -55
  99. package/js/vm/pen-transparency.test.ts +0 -115
  100. package/js/vm/program-wasm.ts +0 -322
  101. package/js/vm/scheduler-render.test.ts +0 -401
  102. package/js/vm/scratch-assets.test.ts +0 -136
  103. package/js/vm/scratch-assets.ts +0 -202
  104. package/js/vm/types.ts +0 -358
  105. package/js/vm/value-guards.test.ts +0 -25
  106. package/js/vm/value-guards.ts +0 -18
  107. package/moon.mod.json +0 -10
  108. package/scripts/preinstall.ts +0 -4
  109. package/src/AGENTS.md +0 -6
  110. package/src/api.mbt +0 -161
  111. package/src/api_aot_commands.mbt +0 -184
  112. package/src/api_effects_json.mbt +0 -72
  113. package/src/api_options.mbt +0 -60
  114. package/src/api_program_wasm.mbt +0 -1647
  115. package/src/api_program_wat.mbt +0 -2206
  116. package/src/api_snapshot_json.mbt +0 -44
  117. package/src/cmd/AGENTS.md +0 -5
  118. package/src/cmd/main/AGENTS.md +0 -5
  119. package/src/cmd/main/main.mbt +0 -29
  120. package/src/cmd/main/moon.pkg +0 -7
  121. package/src/cmd/main/pkg.generated.mbti +0 -13
  122. package/src/json_helpers.mbt +0 -176
  123. package/src/moon.pkg +0 -65
  124. package/src/moonscratch.mbt +0 -3
  125. package/src/moonscratch_wbtest.mbt +0 -40
  126. package/src/parser_sb3.mbt +0 -890
  127. package/src/pkg.generated.mbti +0 -479
  128. package/src/runtime_eval.mbt +0 -2844
  129. package/src/runtime_exec.mbt +0 -3850
  130. package/src/runtime_render.mbt +0 -2550
  131. package/src/runtime_state.mbt +0 -870
  132. package/src/test/AGENTS.md +0 -3
  133. package/src/test/projects/AGENTS.md +0 -6
  134. package/src/test/projects/moon.pkg +0 -4
  135. package/src/test/projects/moonscratch_compat_test.mbt +0 -642
  136. package/src/test/projects/moonscratch_core_test.mbt +0 -1332
  137. package/src/test/projects/moonscratch_runtime_test.mbt +0 -1087
  138. package/src/test/projects/pkg.generated.mbti +0 -13
  139. package/src/test/projects/test_support.mbt +0 -35
  140. package/src/types_effects.mbt +0 -20
  141. package/src/types_error.mbt +0 -4
  142. package/src/types_options.mbt +0 -31
  143. package/src/types_runtime_structs.mbt +0 -254
  144. package/src/types_vm.mbt +0 -109
  145. package/tsconfig.json +0 -29
  146. package/viewer/index.ts +0 -399
  147. package/viewer/vite.d.ts +0 -1
  148. package/viewer/worker.ts +0 -161
  149. package/vite.config.ts +0 -61
@@ -1,274 +0,0 @@
1
- import type { CanvasLike } from '../assets/types.ts'
2
- import type {
3
- RenderFrame,
4
- RenderImageData,
5
- RenderWithWebGLOptions,
6
- RenderWithWebGLResult,
7
- } from './types.ts'
8
- import { normalizeRenderFrame } from './utils.ts'
9
-
10
- const normalizeCanvas = (width: number, height: number): CanvasLike | null => {
11
- const global = globalThis as {
12
- OffscreenCanvas?: new (width: number, height: number) => CanvasLike
13
- document?: { createElement(tag: string): CanvasLike }
14
- }
15
- if (global.OffscreenCanvas) {
16
- return new global.OffscreenCanvas(width, height)
17
- }
18
- if (global.document?.createElement) {
19
- return global.document.createElement('canvas')
20
- }
21
- return null
22
- }
23
-
24
- const createWebGLContext = (canvas: CanvasLike): unknown => {
25
- const raw = canvas.getContext as (type: string, options?: unknown) => unknown
26
- return (
27
- raw('webgl2', { preserveDrawingBuffer: true }) ??
28
- raw('webgl', { preserveDrawingBuffer: true })
29
- )
30
- }
31
-
32
- const isWebGLContext = (
33
- context: unknown,
34
- ): context is WebGLRenderingContextLike =>
35
- typeof context === 'object' &&
36
- context !== null &&
37
- 'createTexture' in context &&
38
- 'createBuffer' in context &&
39
- 'createProgram' in context &&
40
- 'drawArrays' in context
41
-
42
- type WebGLRenderingContextLike = {
43
- ARRAY_BUFFER: number
44
- CLAMP_TO_EDGE: number
45
- COLOR_BUFFER_BIT: number
46
- COMPILE_STATUS: number
47
- FRAGMENT_SHADER: number
48
- FLOAT: number
49
- LINK_STATUS: number
50
- NEAREST: number
51
- RGBA: number
52
- STATIC_DRAW: number
53
- TEXTURE0: number
54
- TEXTURE_WRAP_S: number
55
- TEXTURE_WRAP_T: number
56
- TEXTURE_2D: number
57
- TEXTURE_MIN_FILTER: number
58
- TEXTURE_MAG_FILTER: number
59
- TRIANGLE_STRIP: number
60
- UNSIGNED_BYTE: number
61
- VERTEX_SHADER: number
62
- activeTexture(texture: number): void
63
- attachShader(program: unknown, shader: unknown): void
64
- bindBuffer(target: number, buffer: unknown): void
65
- bindTexture(target: number, texture: unknown): void
66
- clear(mask: number): void
67
- clearColor(r: number, g: number, b: number, a: number): void
68
- compileShader(shader: unknown): void
69
- createBuffer(): unknown
70
- createProgram(): unknown
71
- createShader(type: number): unknown
72
- createTexture(): unknown
73
- createFramebuffer?(): unknown
74
- drawArrays(mode: number, first: number, count: number): void
75
- enableVertexAttribArray(location: number): void
76
- bufferData(target: number, data: ArrayBufferView, usage: number): void
77
- getAttribLocation(program: unknown, name: string): number
78
- getError?(): number
79
- getProgramInfoLog(program: unknown): string | null
80
- getProgramParameter(program: unknown, pname: number): unknown
81
- getShaderInfoLog(shader: unknown): string | null
82
- getShaderParameter(shader: unknown, pname: number): unknown
83
- getUniformLocation(program: unknown, name: string): unknown
84
- linkProgram(program: unknown): void
85
- pixelStorei(...args: unknown[]): void
86
- readPixels(
87
- x: number,
88
- y: number,
89
- width: number,
90
- height: number,
91
- format: number,
92
- type: number,
93
- pixels: Uint8Array,
94
- ): void
95
- shaderSource(shader: unknown, source: string): void
96
- uniform1i(location: unknown, value: number): void
97
- useProgram(program: unknown): void
98
- vertexAttribPointer(
99
- index: number,
100
- size: number,
101
- type: number,
102
- normalized: boolean,
103
- stride: number,
104
- offset: number,
105
- ): void
106
- viewport(x: number, y: number, width: number, height: number): void
107
- texParameteri(target: number, pname: number, param: number): void
108
- texImage2D(
109
- target: number,
110
- level: number,
111
- internalformat: number,
112
- width: number,
113
- height: number,
114
- border: number,
115
- format: number,
116
- type: number,
117
- pixels: Uint8Array | null,
118
- ): void
119
- bindVertexArray?(vertexArray: unknown): void
120
- createVertexArray?(): unknown
121
- }
122
-
123
- const createProgram = (gl: WebGLRenderingContextLike): unknown => {
124
- const vertexShader = gl.createShader(gl.VERTEX_SHADER)
125
- const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
126
- if (!vertexShader || !fragmentShader) {
127
- throw new Error('Unable to create WebGL shaders')
128
- }
129
-
130
- const vertexSource = `
131
- attribute vec2 a_position;
132
- attribute vec2 a_tex_coord;
133
- varying vec2 v_tex_coord;
134
- void main() {
135
- v_tex_coord = a_tex_coord;
136
- gl_Position = vec4(a_position, 0.0, 1.0);
137
- }`
138
- const fragmentSource = `
139
- precision mediump float;
140
- varying vec2 v_tex_coord;
141
- uniform sampler2D u_texture;
142
- void main() {
143
- gl_FragColor = texture2D(u_texture, v_tex_coord);
144
- }`
145
-
146
- gl.shaderSource(vertexShader, vertexSource)
147
- gl.compileShader(vertexShader)
148
- if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
149
- throw new Error('Unable to compile WebGL vertex shader')
150
- }
151
-
152
- gl.shaderSource(fragmentShader, fragmentSource)
153
- gl.compileShader(fragmentShader)
154
- if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
155
- throw new Error('Unable to compile WebGL fragment shader')
156
- }
157
-
158
- const program = gl.createProgram()
159
- if (!program) {
160
- throw new Error('Unable to create WebGL program')
161
- }
162
- gl.attachShader(program, vertexShader)
163
- gl.attachShader(program, fragmentShader)
164
- gl.linkProgram(program)
165
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
166
- const message = gl.getProgramInfoLog(program) ?? 'linking failed'
167
- throw new Error(`Unable to link WebGL program: ${message}`)
168
- }
169
- return program
170
- }
171
-
172
- export const renderWithWebGL = (
173
- input: RenderFrame,
174
- options: RenderWithWebGLOptions = {},
175
- ): RenderWithWebGLResult => {
176
- const frame = normalizeRenderFrame(input)
177
- const canvas = options.canvas ?? normalizeCanvas(frame.width, frame.height)
178
- if (!canvas) {
179
- throw new Error(
180
- 'renderWithWebGL requires a canvas and an environment that supports canvas',
181
- )
182
- }
183
-
184
- canvas.width = frame.width
185
- canvas.height = frame.height
186
- const rawContext = createWebGLContext(canvas)
187
- if (!isWebGLContext(rawContext)) {
188
- throw new Error('renderWithWebGL requires a webgl-capable canvas')
189
- }
190
- const gl = rawContext
191
-
192
- const program = createProgram(gl)
193
- gl.useProgram(program)
194
-
195
- const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1])
196
- const texCoords = new Float32Array([0, 1, 1, 1, 0, 0, 1, 0])
197
- const buffer = gl.createBuffer()
198
- const texBuffer = gl.createBuffer()
199
- if (!buffer || !texBuffer) {
200
- throw new Error('Unable to allocate WebGL buffers')
201
- }
202
-
203
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
204
- gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
205
- const positionLocation = gl.getAttribLocation(program, 'a_position')
206
- gl.enableVertexAttribArray(positionLocation)
207
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)
208
-
209
- gl.bindBuffer(gl.ARRAY_BUFFER, texBuffer)
210
- gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW)
211
- const texCoordLocation = gl.getAttribLocation(program, 'a_tex_coord')
212
- gl.enableVertexAttribArray(texCoordLocation)
213
- gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0)
214
-
215
- const texture = gl.createTexture()
216
- gl.activeTexture(gl.TEXTURE0)
217
- gl.bindTexture(gl.TEXTURE_2D, texture)
218
- gl.pixelStorei(0x9240, true)
219
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
220
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
221
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
222
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
223
- gl.texImage2D(
224
- gl.TEXTURE_2D,
225
- 0,
226
- gl.RGBA,
227
- frame.width,
228
- frame.height,
229
- 0,
230
- gl.RGBA,
231
- gl.UNSIGNED_BYTE,
232
- frame.pixels,
233
- )
234
- const sampler = gl.getUniformLocation(program, 'u_texture')
235
- gl.uniform1i(sampler, 0)
236
-
237
- gl.viewport(0, 0, frame.width, frame.height)
238
- gl.clearColor(0, 0, 0, 0)
239
- gl.clear(gl.COLOR_BUFFER_BIT)
240
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
241
-
242
- let imageDataCache: RenderImageData | null = null
243
- const readback = (): RenderImageData => {
244
- if (imageDataCache !== null) {
245
- return imageDataCache
246
- }
247
- const out = new Uint8Array(frame.width * frame.height * 4)
248
- if (frame.width > 0 && frame.height > 0) {
249
- gl.readPixels(
250
- 0,
251
- 0,
252
- frame.width,
253
- frame.height,
254
- gl.RGBA,
255
- gl.UNSIGNED_BYTE,
256
- out,
257
- )
258
- }
259
- imageDataCache = {
260
- width: frame.width,
261
- height: frame.height,
262
- data: new Uint8ClampedArray(out),
263
- }
264
- return imageDataCache
265
- }
266
-
267
- const imageElement = () => canvas
268
-
269
- return {
270
- canvas,
271
- toImageData: readback,
272
- toImageElement: imageElement,
273
- }
274
- }
@@ -1,16 +0,0 @@
1
- declare module 'sharp' {
2
- type SharpPipeline = {
3
- ensureAlpha(): SharpPipeline
4
- raw(): SharpPipeline
5
- toBuffer(options: { resolveWithObject: true }): Promise<{
6
- data: Uint8Array
7
- info: {
8
- width: number
9
- height: number
10
- }
11
- }>
12
- }
13
-
14
- const sharp: (input?: unknown) => SharpPipeline
15
- export default sharp
16
- }
@@ -1,116 +0,0 @@
1
- import type { Project } from 'hikkaku'
2
- import { createHeadlessVMFromProject } from '../index.ts'
3
- import type {
4
- FrameReport,
5
- HeadlessVM,
6
- JsonValue,
7
- RenderFrame,
8
- VMSnapshotTarget,
9
- } from '../vm/index.ts'
10
-
11
- export const createVmFromProject = (project: Project): HeadlessVM => {
12
- return createHeadlessVMFromProject({
13
- projectJson: project.toScratch(),
14
- initialNowMs: 0,
15
- })
16
- }
17
-
18
- export const stepMany = (
19
- vm: HeadlessVM,
20
- count: number,
21
- frameMs = 33,
22
- ): FrameReport => {
23
- if (count <= 0) {
24
- throw new Error('count must be positive')
25
- }
26
- let report: FrameReport | null = null
27
- let nowMs = vm.snapshot().nowMs
28
- for (let index = 0; index < count; index += 1) {
29
- nowMs += frameMs
30
- vm.setTime(nowMs)
31
- report = vm.stepFrame()
32
- }
33
- if (!report) {
34
- throw new Error('stepMany did not execute any frames')
35
- }
36
- return report
37
- }
38
-
39
- export const runUntilFinished = (
40
- vm: HeadlessVM,
41
- maxFrames = 180,
42
- frameMs = 33,
43
- ): FrameReport => {
44
- let nowMs = vm.snapshot().nowMs
45
- for (let index = 0; index < maxFrames; index += 1) {
46
- nowMs += frameMs
47
- vm.setTime(nowMs)
48
- const report = vm.stepFrame()
49
- if (report.stopReason === 'finished') {
50
- return report
51
- }
52
- }
53
- throw new Error(`VM did not finish within ${String(maxFrames)} frames`)
54
- }
55
-
56
- export const getTargetByName = (
57
- vm: HeadlessVM,
58
- name: string,
59
- ): VMSnapshotTarget => {
60
- const target = vm.snapshot().targets.find((item) => item.name === name)
61
- if (!target) {
62
- throw new Error(`target "${name}" was not found in snapshot`)
63
- }
64
- return target
65
- }
66
-
67
- export const getStageTarget = (vm: HeadlessVM): VMSnapshotTarget => {
68
- const stage = vm.snapshot().targets.find((target) => target.isStage)
69
- if (!stage) {
70
- throw new Error('stage target was not found in snapshot')
71
- }
72
- return stage
73
- }
74
-
75
- export const getStageVariable = (vm: HeadlessVM, id: string): JsonValue => {
76
- const value = getStageTarget(vm).variables[id]
77
- if (value === undefined) {
78
- throw new Error(`stage variable "${id}" was not found in snapshot`)
79
- }
80
- return value
81
- }
82
-
83
- export const countNonTransparentPixels = (frame: RenderFrame): number => {
84
- let count = 0
85
- for (let index = 0; index < frame.pixels.length; index += 4) {
86
- if ((frame.pixels[index + 3] ?? 0) > 0) {
87
- count += 1
88
- }
89
- }
90
- return count
91
- }
92
-
93
- export const hasApproxColor = (
94
- frame: RenderFrame,
95
- rgb: readonly [number, number, number],
96
- tolerance = 24,
97
- minAlpha = 32,
98
- ): boolean => {
99
- for (let index = 0; index < frame.pixels.length; index += 4) {
100
- const r = frame.pixels[index] ?? 0
101
- const g = frame.pixels[index + 1] ?? 0
102
- const b = frame.pixels[index + 2] ?? 0
103
- const a = frame.pixels[index + 3] ?? 0
104
- if (a < minAlpha) {
105
- continue
106
- }
107
- if (
108
- Math.abs(r - rgb[0]) <= tolerance &&
109
- Math.abs(g - rgb[1]) <= tolerance &&
110
- Math.abs(b - rgb[2]) <= tolerance
111
- ) {
112
- return true
113
- }
114
- }
115
- return false
116
- }
@@ -1,37 +0,0 @@
1
- import { Project } from 'hikkaku'
2
- import { setVariableTo, whenFlagClicked } from 'hikkaku/blocks'
3
- import { describe, expect, test } from 'vite-plus/test'
4
- import { createHeadlessVM, createProgramModuleFromProject } from '../index.ts'
5
-
6
- describe('moonscratch/js/test/hikkaku-sample.test.ts', () => {
7
- test('runs a project generated with hikkaku', () => {
8
- const project = new Project()
9
- const score = project.stage.createVariable('score', 0)
10
-
11
- project.stage.run(() => {
12
- whenFlagClicked(() => {
13
- setVariableTo(score, 42)
14
- })
15
- })
16
-
17
- const program = createProgramModuleFromProject({
18
- projectJson: project.toScratch(),
19
- })
20
- const vm = createHeadlessVM({
21
- program,
22
- initialNowMs: 0,
23
- })
24
-
25
- vm.greenFlag()
26
- const report = vm.stepFrame()
27
-
28
- expect(report.stopReason).toBe('finished')
29
-
30
- const stage = vm.snapshot().targets.find((target) => target.isStage)
31
- if (!stage) {
32
- throw new Error('stage target was not found in snapshot')
33
- }
34
-
35
- expect(stage.variables[score.id]).toBe(42)
36
- })
37
- })
@@ -1,60 +0,0 @@
1
- import { Project } from 'hikkaku'
2
- import {
3
- getKeyPressed,
4
- getMouseDown,
5
- getMouseX,
6
- getMouseY,
7
- gotoXY,
8
- ifThen,
9
- setDragMode,
10
- setVariableTo,
11
- whenFlagClicked,
12
- } from 'hikkaku/blocks'
13
- import { describe, expect, test } from 'vite-plus/test'
14
- import {
15
- createVmFromProject,
16
- getStageVariable,
17
- getTargetByName,
18
- runUntilFinished,
19
- } from './helpers.ts'
20
-
21
- describe('rubik components: input and motion', () => {
22
- test('reads key/mouse input and moves sprite with motion_gotoxy', () => {
23
- const project = new Project()
24
- const sprite = project.createSprite('pointer')
25
- const mouseX = project.stage.createVariable('mouseX', 0)
26
- const mouseY = project.stage.createVariable('mouseY', 0)
27
- const mouseDown = project.stage.createVariable('mouseDown', 0)
28
- const keyPressed = project.stage.createVariable('keyPressed', 0)
29
-
30
- sprite.run(() => {
31
- whenFlagClicked(() => {
32
- setDragMode('draggable')
33
- setVariableTo(mouseX, getMouseX())
34
- setVariableTo(mouseY, getMouseY())
35
- ifThen(getMouseDown(), () => {
36
- setVariableTo(mouseDown, 1)
37
- })
38
- ifThen(getKeyPressed('space'), () => {
39
- setVariableTo(keyPressed, 1)
40
- })
41
- gotoXY(getMouseX(), getMouseY())
42
- })
43
- })
44
-
45
- const vm = createVmFromProject(project)
46
- vm.greenFlag()
47
- vm.setMouseState({ x: 40, y: -30, isDown: true })
48
- vm.setKeysDown(['space'])
49
- runUntilFinished(vm)
50
-
51
- expect(getStageVariable(vm, mouseX.id)).toBe(40)
52
- expect(getStageVariable(vm, mouseY.id)).toBe(-30)
53
- expect(getStageVariable(vm, mouseDown.id)).toBe(1)
54
- expect(getStageVariable(vm, keyPressed.id)).toBe(1)
55
-
56
- const moved = getTargetByName(vm, 'pointer')
57
- expect(moved.x).toBe(40)
58
- expect(moved.y).toBe(-30)
59
- })
60
- })
@@ -1,49 +0,0 @@
1
- import { Project } from 'hikkaku'
2
- import {
3
- changeVariableBy,
4
- getItemOfList,
5
- getVariable,
6
- lengthOfList,
7
- replaceItemOfList,
8
- setVariableTo,
9
- whenFlagClicked,
10
- } from 'hikkaku/blocks'
11
- import { describe, expect, test } from 'vite-plus/test'
12
- import {
13
- createVmFromProject,
14
- getStageTarget,
15
- getStageVariable,
16
- runUntilFinished,
17
- } from './helpers.ts'
18
-
19
- describe('rubik components: lists', () => {
20
- test('reads, replaces and measures list values', () => {
21
- const project = new Project()
22
- const values = project.stage.createList('values', [10, 20, 30])
23
- const picked = project.stage.createVariable('picked', 0)
24
- const length = project.stage.createVariable('length', 0)
25
- const total = project.stage.createVariable('total', 0)
26
-
27
- project.stage.run(() => {
28
- whenFlagClicked(() => {
29
- setVariableTo(picked, getItemOfList(values, 2))
30
- replaceItemOfList(values, 2, 99)
31
- changeVariableBy(picked, getItemOfList(values, 2))
32
- setVariableTo(length, lengthOfList(values))
33
- setVariableTo(total, getVariable(length))
34
- changeVariableBy(total, getVariable(picked))
35
- })
36
- })
37
-
38
- const vm = createVmFromProject(project)
39
- vm.greenFlag()
40
- runUntilFinished(vm)
41
-
42
- expect(getStageVariable(vm, picked.id)).toBe(119)
43
- expect(getStageVariable(vm, length.id)).toBe(3)
44
- expect(getStageVariable(vm, total.id)).toBe(122)
45
-
46
- const stage = getStageTarget(vm)
47
- expect(stage.lists[values.id]).toEqual([10, 99, 30])
48
- })
49
- })
@@ -1,104 +0,0 @@
1
- import { Project } from 'hikkaku'
2
- import {
3
- add,
4
- and,
5
- divide,
6
- equals,
7
- gt,
8
- ifThen,
9
- lt,
10
- mathop,
11
- multiply,
12
- not,
13
- or,
14
- setVariableTo,
15
- subtract,
16
- whenFlagClicked,
17
- } from 'hikkaku/blocks'
18
- import { describe, expect, test } from 'vite-plus/test'
19
- import {
20
- createVmFromProject,
21
- getStageVariable,
22
- runUntilFinished,
23
- } from './helpers.ts'
24
-
25
- describe('rubik components: operators', () => {
26
- test('evaluates arithmetic and boolean operator chains', () => {
27
- const project = new Project()
28
- const arithmetic = project.stage.createVariable('arithmetic', 0)
29
- const sine = project.stage.createVariable('sine', 0)
30
- const logic = project.stage.createVariable('logic', 0)
31
-
32
- project.stage.run(() => {
33
- whenFlagClicked(() => {
34
- setVariableTo(arithmetic, add(10, 5))
35
- setVariableTo(arithmetic, subtract(arithmetic.get(), 3))
36
- setVariableTo(arithmetic, multiply(arithmetic.get(), 4))
37
- setVariableTo(arithmetic, divide(arithmetic.get(), 6))
38
- setVariableTo(sine, mathop('sin', 30))
39
- ifThen(and(or(true, false), not(false)), () => {
40
- setVariableTo(logic, 1)
41
- })
42
- })
43
- })
44
-
45
- const vm = createVmFromProject(project)
46
- vm.greenFlag()
47
- runUntilFinished(vm)
48
-
49
- expect(getStageVariable(vm, arithmetic.id)).toBe(8)
50
- expect(getStageVariable(vm, logic.id)).toBe(1)
51
- expect(Number(getStageVariable(vm, sine.id))).toBeCloseTo(0.5, 6)
52
- })
53
-
54
- test('uses Scratch compare semantics for lt/gt/equals', () => {
55
- const project = new Project()
56
- const leftA = project.stage.createVariable('leftA', 0)
57
- const rightA = project.stage.createVariable('rightA', 0)
58
- const leftB = project.stage.createVariable('leftB', 0)
59
- const rightB = project.stage.createVariable('rightB', 0)
60
- const leftC = project.stage.createVariable('leftC', '')
61
- const rightC = project.stage.createVariable('rightC', '')
62
- const leftD = project.stage.createVariable('leftD', '')
63
- const rightD = project.stage.createVariable('rightD', 0)
64
- const numericLt = project.stage.createVariable('numericLt', 0)
65
- const numericGt = project.stage.createVariable('numericGt', 0)
66
- const stringLt = project.stage.createVariable('stringLt', 0)
67
- const whitespaceEq = project.stage.createVariable('whitespaceEq', 0)
68
-
69
- project.stage.run(() => {
70
- whenFlagClicked(() => {
71
- setVariableTo(leftA, -1)
72
- setVariableTo(rightA, -0.05)
73
- setVariableTo(leftB, 2)
74
- setVariableTo(rightB, 1)
75
- setVariableTo(leftC, 'apple')
76
- setVariableTo(rightC, 'Banana')
77
- setVariableTo(leftD, ' ')
78
- setVariableTo(rightD, 0)
79
-
80
- ifThen(lt(leftA.get(), rightA.get()), () => {
81
- setVariableTo(numericLt, 1)
82
- })
83
- ifThen(gt(leftB.get(), rightB.get()), () => {
84
- setVariableTo(numericGt, 1)
85
- })
86
- ifThen(lt(leftC.get(), rightC.get()), () => {
87
- setVariableTo(stringLt, 1)
88
- })
89
- ifThen(equals(leftD.get(), rightD.get()), () => {
90
- setVariableTo(whitespaceEq, 1)
91
- })
92
- })
93
- })
94
-
95
- const vm = createVmFromProject(project)
96
- vm.greenFlag()
97
- runUntilFinished(vm)
98
-
99
- expect(getStageVariable(vm, numericLt.id)).toBe(1)
100
- expect(getStageVariable(vm, numericGt.id)).toBe(1)
101
- expect(getStageVariable(vm, stringLt.id)).toBe(1)
102
- expect(getStageVariable(vm, whitespaceEq.id)).toBe(0)
103
- })
104
- })