Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf936d1371 | ||
|
|
fed67b9cf7 | ||
|
|
3f10da2918 | ||
|
|
b3ffd587a6 | ||
|
|
58ab777269 | ||
|
|
7719a3131c | ||
|
|
fbd95135a5 | ||
|
|
f5cd6ef4d7 | ||
|
|
3f4424ee5f | ||
|
|
42193a64f0 | ||
|
|
d800214667 | ||
|
|
8f51dae51c | ||
|
|
8a82741626 | ||
|
|
18f59c0015 | ||
|
|
a49b9ad996 | ||
|
|
41d708f123 | ||
|
|
12d72f1a61 | ||
|
|
5a714e7c32 | ||
|
|
93ece03830 | ||
|
|
a555a8c10f | ||
|
|
4633f512e6 | ||
|
|
c2b9d41343 | ||
|
|
a3cfbcae80 | ||
|
|
e94ed032e9 | ||
|
|
b8d6d52515 | ||
|
|
21ac3dd330 | ||
|
|
e32fdb8ab2 | ||
|
|
cb9492570b | ||
|
|
051a04c613 | ||
|
|
c5aa2468ae | ||
|
|
202238a3d1 | ||
|
|
f6c44b85a3 | ||
|
|
560a057e15 | ||
|
|
9b0edd6590 | ||
|
|
3ab338167f | ||
|
|
1d701912c3 | ||
|
|
1576e11347 | ||
|
|
9c07bee5a0 | ||
|
|
de9ef4b0c3 | ||
|
|
bc801cb009 | ||
|
|
61dfcc7fcb | ||
|
|
80eecb6cb4 | ||
|
|
e130705823 | ||
|
|
090eadd1a9 | ||
|
|
3bb819b0ed | ||
|
|
82571e03f6 | ||
|
|
6410108da7 | ||
|
|
81efb99fbe | ||
|
|
999d143d0b | ||
|
|
dae95150ab | ||
|
|
79d5fff426 | ||
|
|
aebeb7ba75 | ||
|
|
874841157a | ||
|
|
1767dbd7d3 | ||
|
|
fa0b6e9f71 | ||
|
|
8fb88f25af | ||
|
|
073f0e5315 | ||
|
|
e2ce299604 | ||
|
|
dcbf575d93 | ||
|
|
d1bedf6e37 | ||
|
|
64d375ca3f | ||
|
|
5ef39df14a | ||
|
|
16f467041f | ||
|
|
9ed27cffaa | ||
|
|
4d8ecc3e41 | ||
|
|
cdf0a9d545 | ||
|
|
41e1fc4825 | ||
|
|
d87d0c0770 | ||
|
|
0474b49f25 | ||
|
|
5c0e7737d6 | ||
|
|
91d5035592 | ||
|
|
e7764c5676 | ||
|
|
ccb60e399f | ||
|
|
130428e4a8 | ||
|
|
0fba935ec9 | ||
|
|
23527096c0 | ||
|
|
e34a453f8f | ||
|
|
17e6e59d89 | ||
|
|
568d642e48 | ||
|
|
9532c3dab2 | ||
|
|
7d88656ec5 | ||
|
|
a0abf3ca19 | ||
|
|
d86db2c66a | ||
|
|
c342463415 | ||
|
|
bcc8f787b1 | ||
|
|
6129c6dd46 | ||
|
|
37beb338da | ||
|
|
7ba07c805e | ||
|
|
6c03b8e034 | ||
|
|
6e6288a2ab | ||
|
|
a9a4b1b79b | ||
|
|
54d7ce2b9b | ||
|
|
c17edd0e05 | ||
|
|
4d22bf270b | ||
|
|
7206495177 | ||
|
|
24323842bf | ||
|
|
3703fa618e | ||
|
|
ff2921c605 | ||
|
|
35399063f3 | ||
|
|
d4a77c6f38 | ||
|
|
d4d794e730 | ||
|
|
85fa229d94 | ||
|
|
6164327b45 | ||
|
|
1a0ee34f0b | ||
|
|
86a1ffde90 | ||
|
|
17088f64b4 | ||
|
|
e7c2abed13 | ||
|
|
719ef3b388 | ||
|
|
e4e9ab5e1b | ||
|
|
4d92e92709 | ||
|
|
4b2222fa3e | ||
|
|
7dfaebd263 | ||
|
|
884843fb82 | ||
|
|
432c448e5c | ||
|
|
a1fa132280 | ||
|
|
ad1af074fc | ||
|
|
952a584d74 | ||
|
|
501caf7793 | ||
|
|
c3b32570be | ||
|
|
f9183b954b | ||
|
|
39ceac4abf | ||
|
|
cc22bfc636 | ||
|
|
ed945d392d | ||
|
|
dee7462c57 | ||
|
|
87b076c6a1 | ||
|
|
c966acd987 | ||
|
|
84d000a277 | ||
|
|
fde5ecf24d | ||
|
|
932542e30a | ||
|
|
3e1f3b743b | ||
|
|
886e2ebb8f | ||
|
|
d2f120da7e | ||
|
|
45752c2660 | ||
|
|
2f7e9967ab | ||
|
|
fe7a943780 | ||
|
|
637a09d4e9 | ||
|
|
cf52d1ee6c | ||
|
|
2eabf2aefd | ||
|
|
8412cfc46e | ||
|
|
832cf69e5d | ||
|
|
c039845b26 | ||
|
|
f6c586cec6 | ||
|
|
b36b300fc2 | ||
|
|
5c0a0c58e5 | ||
|
|
a64f623c20 | ||
|
|
c688210751 | ||
|
|
c740407aea | ||
|
|
05afad1f73 | ||
|
|
c4676cc5a6 | ||
|
|
f4c09b35e9 | ||
|
|
25136598e3 | ||
|
|
a7d141a83a | ||
|
|
4366b08f94 | ||
|
|
dd43c53db8 | ||
|
|
b77b3464db | ||
|
|
065e5b6d77 | ||
|
|
636286b427 | ||
|
|
bcbf4259a0 | ||
|
|
b6f4de8796 | ||
|
|
ce7e4d085f | ||
|
|
5910c170f1 | ||
|
|
cfcba71f94 | ||
|
|
3131ece71e | ||
|
|
93525be462 | ||
|
|
9ce52307d2 | ||
|
|
41f27bd860 | ||
|
|
6260f9c093 | ||
|
|
fbff6843f4 | ||
|
|
80662eb585 | ||
|
|
dd90ec866c | ||
|
|
893ab2c406 | ||
|
|
d25afb7c0e | ||
|
|
86a05f55f3 | ||
|
|
27c352196d | ||
|
|
8e9a17b4e7 | ||
|
|
3e2686cd67 | ||
|
|
a8595edf8f | ||
|
|
1c2530ae09 | ||
|
|
527cc0b0fb | ||
|
|
78143a1b35 | ||
|
|
ae71ac1941 | ||
|
|
180192ae15 | ||
|
|
e74bba9257 | ||
|
|
55b3584799 | ||
|
|
773a2e8672 | ||
|
|
083bb169d4 | ||
|
|
eb180f6f03 | ||
|
|
98fff1e044 | ||
|
|
45de3567db | ||
|
|
6320ae3a99 | ||
|
|
24957f3de4 | ||
|
|
22bd7219d7 | ||
|
|
aeb8238ecb | ||
|
|
bab0e4fd5c | ||
|
|
266350153e | ||
|
|
f425b27cf6 | ||
|
|
95ef21d3ad | ||
|
|
9aba9ec3fb | ||
|
|
3e4cf00eca | ||
|
|
a8c5083aa3 | ||
|
|
a1e72203c8 | ||
|
|
cc8f39e590 | ||
|
|
6eb0f57736 | ||
|
|
c1d4b8745a | ||
|
|
9219af1db1 | ||
|
|
6f7962abd4 | ||
|
|
dcf92b2455 | ||
|
|
7a1db103ba | ||
|
|
394f2c8c16 | ||
|
|
bbd95f4672 | ||
|
|
5378c57537 | ||
|
|
a93e9df129 | ||
|
|
221978b24e | ||
|
|
333e95c898 | ||
|
|
953702e238 | ||
|
|
fb3b1ced7c | ||
|
|
17ae4ea197 | ||
|
|
16d15bd4f1 | ||
|
|
a6b5c237cc | ||
|
|
10d014a7ff | ||
|
|
e0019c93fc | ||
|
|
00e8b3cdb2 | ||
|
|
f7853a9354 | ||
|
|
d972629c29 | ||
|
|
9d27a1af23 | ||
|
|
98ebd453d7 | ||
|
|
63b5a9a059 | ||
|
|
8ea62f5ea5 | ||
|
|
bc2152641b | ||
|
|
9883e0efb6 | ||
|
|
cf8a2e41df | ||
|
|
25986f0f31 | ||
|
|
230503cb27 | ||
|
|
5a614a1041 | ||
|
|
988ce22121 | ||
|
|
ebf369e293 | ||
|
|
b6a63a55ad | ||
|
|
78fe4c921c | ||
|
|
953a42d35e | ||
|
|
584bfeee5e | ||
|
|
f7d5c1898e | ||
|
|
77c5bc7254 | ||
|
|
a150efc743 | ||
|
|
8b53d1ed57 | ||
|
|
8e4c76f74f | ||
|
|
9053277889 | ||
|
|
6baf5c0e48 | ||
|
|
98c4e4df65 | ||
|
|
08160e588a | ||
|
|
fc998b9779 | ||
|
|
d0eb28f11c | ||
|
|
4f7fd39f5e | ||
|
|
3aed7007e8 | ||
|
|
731f3bbf5d | ||
|
|
715e46fe1c | ||
|
|
cc68eef196 | ||
|
|
550e236f48 | ||
|
|
40415e2359 | ||
|
|
d20b68c939 | ||
|
|
6af3fda54a | ||
|
|
43191504ff | ||
|
|
3008feb1de | ||
|
|
78c2aea407 | ||
|
|
b41b1c11bb | ||
|
|
8e82c2d1e3 | ||
|
|
a75e2b0ecf | ||
|
|
a731370478 | ||
|
|
f3ced9c9ea | ||
|
|
dee0320164 | ||
|
|
942b8d6e6a | ||
|
|
c296732753 | ||
|
|
212ffb42d3 | ||
|
|
af0088df88 | ||
|
|
b9518788dc | ||
|
|
63bdc80388 | ||
|
|
a27ddaf4ad | ||
|
|
b941d99f5f | ||
|
|
528e8c9c2e | ||
|
|
5ee641e70f | ||
|
|
e9d16dcd2a | ||
|
|
d156a3d995 | ||
|
|
1019726bcf | ||
|
|
3fbb7fe8d4 | ||
|
|
124a6496f5 | ||
|
|
2e11787ac5 | ||
|
|
53bdab3425 | ||
|
|
058fd000fa | ||
|
|
dc727f1b57 | ||
|
|
bab503addb | ||
|
|
662726fa52 | ||
|
|
a0b4aea8a1 | ||
|
|
b180dd4c0d | ||
|
|
02cdbf68cd | ||
|
|
f1f1f54ec6 | ||
|
|
256bb87858 | ||
|
|
bdbf99f606 | ||
|
|
8e3603aa8b | ||
|
|
a5baf0cf5f | ||
|
|
7912e4306c | ||
|
|
5fa8beb230 | ||
|
|
9b4b47367b | ||
|
|
f69f4bfe2b | ||
|
|
306497c095 | ||
|
|
17cff4775c | ||
|
|
2ee25bcc0f | ||
|
|
7ed3a6b7c8 | ||
|
|
73fad0b7f5 | ||
|
|
147a4ed586 | ||
|
|
b1a3c4b446 | ||
|
|
4563b41429 | ||
|
|
0c8b982479 | ||
|
|
e5811f15be | ||
|
|
a4624da5f2 | ||
|
|
89a748dd2b | ||
|
|
5cccd63f6c | ||
|
|
33e01c1bdd | ||
|
|
8c6b52f43c | ||
|
|
2ebcd850d6 | ||
|
|
9d307f0e08 | ||
|
|
fde7349b2b | ||
|
|
93406fbef7 | ||
|
|
d7cb0126ad | ||
|
|
41831eede7 | ||
|
|
923b931d90 | ||
|
|
600172f8fb | ||
|
|
41a50b87ab | ||
|
|
d99360e021 | ||
|
|
4e4d359d18 | ||
|
|
92f05792f9 | ||
|
|
cd9d25ad48 | ||
|
|
e34d5ed67e | ||
|
|
af4ad81811 | ||
|
|
c368de81fa | ||
|
|
0104f5bec8 | ||
|
|
8690b696a5 | ||
|
|
ae8624e7e7 | ||
|
|
7f8b84829e | ||
|
|
d4078a795f | ||
|
|
f381a6a0d8 | ||
|
|
962ce680c9 | ||
|
|
630ba84350 | ||
|
|
51843d6bef | ||
|
|
02be2155d9 | ||
|
|
cca695b54e | ||
|
|
4e633cb596 | ||
|
|
8dc343c90d | ||
|
|
369eb8c3cf | ||
|
|
a7021e32a4 | ||
|
|
863276e75d | ||
|
|
de9dbe08f4 | ||
|
|
c050829a75 | ||
|
|
6e57ea1c91 | ||
|
|
53d09e2162 | ||
|
|
5d9afb008a | ||
|
|
d19cf91205 | ||
|
|
4f70b0f924 | ||
|
|
f8a0f46959 | ||
|
|
faf0dd2f13 | ||
|
|
cbb908831d | ||
|
|
b44c0c355a | ||
|
|
ffa364ed64 | ||
|
|
017de39c3b | ||
|
|
01c7da1965 | ||
|
|
59f428900c | ||
|
|
48245be395 | ||
|
|
ca38789e3f | ||
|
|
efed4d3721 | ||
|
|
95532e147d | ||
|
|
3a56e609a5 | ||
|
|
e63842a336 | ||
|
|
c9173427b9 | ||
|
|
3e5b65072c | ||
|
|
74828b3b09 | ||
|
|
e77162791f | ||
|
|
0c17cd956d | ||
|
|
287b29019a | ||
|
|
ed3b58bfb5 | ||
|
|
c2cd6f21da | ||
|
|
5ac16672e5 | ||
|
|
066e9dd6bd | ||
|
|
5a0860228e | ||
|
|
f2291d963c | ||
|
|
97885c7518 | ||
|
|
2cf2d4c455 | ||
|
|
3a8fe3f1b2 | ||
|
|
a23dd3e349 | ||
|
|
79f3f6f3f8 | ||
|
|
175395cce3 | ||
|
|
ee67ddf56f | ||
|
|
5c259b70db | ||
|
|
3dd86c28f9 | ||
|
|
4b75bc8f9d | ||
|
|
85cd56307d | ||
|
|
8a4adccae5 | ||
|
|
9908d44d81 | ||
|
|
d2c74c5198 | ||
|
|
dcf2b8e07c | ||
|
|
80e91a3c5e | ||
|
|
797ce1894b | ||
|
|
f41e1c829a | ||
|
|
fa5994abbd | ||
|
|
76f5cfe1cf | ||
|
|
fcbf1b051b | ||
|
|
1dcba6ecd5 | ||
|
|
79277a1227 | ||
|
|
e476548c64 | ||
|
|
8adb097cd1 | ||
|
|
403a7a5883 | ||
|
|
dd5562323e | ||
|
|
f261f4614a | ||
|
|
a93248df31 | ||
|
|
1fa8742dec | ||
|
|
8b0f2c16bc | ||
|
|
57988d63c2 | ||
|
|
c3d3a7a464 | ||
|
|
e1102f91ef | ||
|
|
e10a3f282f | ||
|
|
0ef59001f1 | ||
|
|
ad96f2eb30 | ||
|
|
5fac48acc9 | ||
|
|
b19139696b | ||
|
|
a5ccbfb8b9 | ||
|
|
7960eed191 | ||
|
|
cb0e24b5be | ||
|
|
ccc12a02c3 | ||
|
|
d505af579a | ||
|
|
504097b495 | ||
|
|
484eb6bc9f | ||
|
|
96392659fd | ||
|
|
a4e27112ab | ||
|
|
50265fe843 | ||
|
|
461877b733 | ||
|
|
2c16a16da5 | ||
|
|
ed0db53f36 | ||
|
|
f244da7705 | ||
|
|
3d1205e453 | ||
|
|
3b1889b941 | ||
|
|
a908345491 | ||
|
|
50de01086a | ||
|
|
57863d99a1 | ||
|
|
3e93db7c0b | ||
|
|
28f4cc737d | ||
|
|
fa5cfb495f | ||
|
|
5b2e8a29e3 | ||
|
|
a7fce92b18 | ||
|
|
12b8cfbb43 | ||
|
|
d1d98337c8 | ||
|
|
480f17d872 | ||
|
|
274cbbd356 | ||
|
|
bb80fa49ac | ||
|
|
38e4c5bbfa | ||
|
|
b3e5dd88fb | ||
|
|
6027bb9011 | ||
|
|
d62ede33e5 | ||
|
|
1a479c61b4 | ||
|
|
2e8adc7ac7 | ||
|
|
5c377e70c9 | ||
|
|
67fdd9bb51 | ||
|
|
c68ec4ad16 | ||
|
|
91e87f7d16 | ||
|
|
762ffe53f0 | ||
|
|
d63c8b1dbb | ||
|
|
e799708707 | ||
|
|
b699e76972 | ||
|
|
914ba4910b | ||
|
|
748f8559e4 | ||
|
|
8fae109811 | ||
|
|
7980c23ecc | ||
|
|
5e4a2b788c | ||
|
|
8caff78e08 | ||
|
|
e1c3bb6d96 | ||
|
|
aebdcc855c | ||
|
|
86fd6ec8bf | ||
|
|
b351cc599d | ||
|
|
0230546a20 | ||
|
|
9638444a49 | ||
|
|
984b87ead5 | ||
|
|
59c4f36b90 | ||
|
|
21a6fe3a27 | ||
|
|
02f806fc9d | ||
|
|
d9003c9d56 | ||
|
|
c944295140 | ||
|
|
2c64a62f23 | ||
|
|
8639f1838c | ||
|
|
4e80510b98 | ||
|
|
9f58bd9ee3 | ||
|
|
236cc6f872 | ||
|
|
b3d336a08f | ||
|
|
97fcbf85fc | ||
|
|
e3b3bcc963 | ||
|
|
775b805e56 | ||
|
|
a1124807ca | ||
|
|
b1674c1b0c | ||
|
|
062e1ff4cc | ||
|
|
7148733acb | ||
|
|
e98862ef66 | ||
|
|
53a805026f | ||
|
|
84b168f757 | ||
|
|
0a86a769e7 | ||
|
|
816fb10e16 | ||
|
|
eece6a5f8d | ||
|
|
27766042fa | ||
|
|
639330b9ce | ||
|
|
6f6a34ec50 | ||
|
|
2560c0736e | ||
|
|
7a2d7d035e | ||
|
|
62ee7e069f | ||
|
|
3b82d52a84 | ||
|
|
435f335cff | ||
|
|
6822d3b7dd | ||
|
|
40e9c07b00 | ||
|
|
c4bf772dd1 | ||
|
|
886dbca01c | ||
|
|
3ee5b44e7e | ||
|
|
0eb7d3017b | ||
|
|
139c9f8043 | ||
|
|
3faedc4301 | ||
|
|
faa81e77c7 | ||
|
|
dc3b177219 | ||
|
|
58ff2606b3 | ||
|
|
dabdaf00d2 | ||
|
|
3eb56b4e9f | ||
|
|
61102e9de8 | ||
|
|
ce164d81d7 | ||
|
|
21d6ce111e | ||
|
|
fcb6de39f3 | ||
|
|
e428083da8 | ||
|
|
3c6e9723e3 | ||
|
|
5afd19b1ce | ||
|
|
17ba917399 | ||
|
|
73b53988fc | ||
|
|
39e1bac823 | ||
|
|
c5f17db7d1 | ||
|
|
ca06418287 | ||
|
|
a98562e03c | ||
|
|
036c627130 | ||
|
|
dd8fb2024d | ||
|
|
c4b43dd321 | ||
|
|
f4061aedaa | ||
|
|
c3d7c086f8 | ||
|
|
5ecaa2fb44 | ||
|
|
3a35693ce3 | ||
|
|
cd23935bc3 | ||
|
|
055853d746 | ||
|
|
bae72c0413 | ||
|
|
ca927f111b | ||
|
|
13a0e9098f | ||
|
|
3d3eb3d059 | ||
|
|
9dcaa0985f | ||
|
|
f788870dc2 | ||
|
|
6eaf6312b9 | ||
|
|
d3474992b7 | ||
|
|
bdfe83e110 | ||
|
|
19c9db0eb1 | ||
|
|
2b642e9ef0 | ||
|
|
a55f9ae9ed | ||
|
|
054d3b1a8f | ||
|
|
a27b27fa71 | ||
|
|
3f51a86d9f | ||
|
|
3fe0fa9b44 | ||
|
|
269ee1dd8e | ||
|
|
46cecc0c3a | ||
|
|
b0945f80cf | ||
|
|
89c88fec4c | ||
|
|
9a43442cf5 | ||
|
|
2bf8db0dfa | ||
|
|
78c38af7a6 | ||
|
|
98c1846c12 | ||
|
|
03f3f12fcc | ||
|
|
47271e7af5 | ||
|
|
b02e5140b9 | ||
|
|
2429f63e02 | ||
|
|
eeb5cfd930 | ||
|
|
99bdad90d6 | ||
|
|
28cda95346 | ||
|
|
1f5ac6e383 | ||
|
|
9cb5f3f96d | ||
|
|
09b14ed4d4 | ||
|
|
5859af7efa | ||
|
|
42375da2e1 | ||
|
|
79fa9bef65 | ||
|
|
d4e71024fb | ||
|
|
a83d9369dc | ||
|
|
012bb612ad | ||
|
|
b9e7bde676 | ||
|
|
992f012ff3 | ||
|
|
c53b88dbad |
No files matched your search
@@ -12,6 +12,7 @@
|
||||
*.svg binary
|
||||
|
||||
# Prefer LF for most file types
|
||||
*.css text eol=lf
|
||||
*.frag text eol=lf
|
||||
*.htm text eol=lf
|
||||
*.html text eol=lf
|
||||
|
||||
@@ -1,11 +1,64 @@
|
||||
## Contributing
|
||||
The development of Scratch is an ongoing process, and we love to have people in the Scratch and open source communities help us along the way.
|
||||
|
||||
If you're interested in contributing, please take a look at the [issues](https://github.com/LLK/scratch-gui/issues) on this repository.
|
||||
Two great ways of helping are by identifying bugs and documenting them as issues, or fixing issues and creating pull requests. When looking for bugs to fix, please look for the ["Help Wanted" label](https://github.com/LLK/scratch-gui/issues?q=label%3A%22help+wanted%22). Bugs with this label have been specifically set aside for Open Source contributors. Issues without the label can also be worked on but we ask that you comment on the issue prior to starting work. When submitting pull requests please be patient -- it can take a while to find time to review them. The organization and class structures can't be radically changed without significant coordination and collaboration from the Scratch Team, so these types of changes should be avoided.
|
||||
### Ways to Help
|
||||
|
||||
It's been said that the Scratch Team spends about one hour of design discussion for every pixel in Scratch, but some think that estimate is a little low. While we welcome suggestions for new features in our [suggestions forum](https://scratch.mit.edu/discuss/1/) (especially ones that come with mockups), we are unlikely to accept PRs with new features that haven't been thought through and discussed as a group. Why? Because we have a strong belief in the value of keeping things simple for new users. To learn more about our design philosophy, see [the Scratch Developers page](https://scratch.mit.edu/developers), or [this paper](http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf).
|
||||
* **Documenting bugs**
|
||||
* If you've identified a bug in Scratch you should first check to see if it's been filed as an issue, if not you can file one. Make sure you follow the issue template.
|
||||
* It's important that we can consistently reproduce issues. When writing an issue, be sure to follow our [reproduction step guidelines](https://github.com/LLK/scratch-gui/wiki/Writing-good-repro-steps).
|
||||
* Some issues are marked "Needs Repro". Adding a comment with good reproduction steps to those issues is a great way to help.
|
||||
* If you don't have an issue in mind already, you can look through the [Bugs & Glitches forum.](https://scratch.mit.edu/discuss/3/) Look for users reporting problems, reproduce the problem yourself, and file new issues following our guidelines.
|
||||
|
||||
* **Fixing bugs**
|
||||
* You can request to fix a bug in a comment on the issue if you at mention the repo coordinator, who for this repo is @chrisgarrity.
|
||||
* If the issue is marked "Help Wanted" you can go ahead and start working on it!
|
||||
* **We will only accept Pull Requests for bugs that have an issue filed that has a priority label**
|
||||
* If you're interested in fixing a bug with no issue, file the issue first and wait for it to have a priority added to it.
|
||||
|
||||
* We are not looking for Pull Requests ("PR") for every issue and may deny a PR if it doesn't fit our criteria.
|
||||
* We are far more likely to accept a PR if it is for an issue marked with Help Wanted.
|
||||
* We will not accept PRs for issues marked with "Needs Discussion" or "Needs Design."
|
||||
* Wait until the Repo Coordinator assigns the issue to you before you begin work or submit a PR.
|
||||
|
||||
### Learning Git and Github
|
||||
|
||||
If you want to work on fixing issues, you should be familiar with Git and Github.
|
||||
|
||||
* [Learn Git branching](https://learngitbranching.js.org/) includes an introduction to basic git commands and useful branching features.
|
||||
* Here's a general introduction to [contributing to an open source project](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github).
|
||||
|
||||
**Important:** we follow the [Github Flow process](https://guides.github.com/introduction/flow/) as our development process.
|
||||
|
||||
### How to Fix Bugs
|
||||
1. Identify which Github issue you are working on. Leave a comment on the issue to let us (and other contributors) know you're working on it.
|
||||
2. Make sure you have a fork of this repo (see [Github's forking a repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) for details)
|
||||
3. Switch to the `develop` branch, and pull down the latest changes from upstream
|
||||
4. Run the code, and reproduce the problem
|
||||
5. Create your branch from the `develop` branch
|
||||
6. Make code changes to fix the problem
|
||||
7. Run `npm test` to make sure that your changes pass our tests
|
||||
8. Commit your changes
|
||||
9. Push your branch to your fork
|
||||
10. Create your pull request
|
||||
1. Make sure to follow the template in the PR description
|
||||
1. Remember to check the “[Allow edits from maintainers](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork)” box
|
||||
|
||||
When submitting pull requests keep in mind:
|
||||
* please be patient -- it can take a while to find time to review them
|
||||
* try to change the least amount of code necessary to fix the bug
|
||||
* the code can't be radically changed without significant coordination with the Scratch Team, so these types of changes should be avoided
|
||||
* if you find yourself changing a substantial amount of code or considering radical changes, please ask for clarification -- we may have envisioned a different approach, or underestimated the amount of effort
|
||||
|
||||
### Suggestions
|
||||

|
||||
|
||||
Please note: **_we are unlikely to accept PRs with new features that haven't been thought through and discussed as a group_**.
|
||||
|
||||
Why? Because we have a strong belief in the value of keeping things simple for new users. It's been said that the Scratch Team spends about one hour of design discussion for every pixel in Scratch. To learn more about our design philosophy, see [the Scratch Developers page](https://scratch.mit.edu/developers), or [this paper](http://web.media.mit.edu/~mres/papers/Scratch-CACM-final.pdf).
|
||||
|
||||
We welcome suggestions! If you want to suggest a feature, please post in our [suggestions forum](https://scratch.mit.edu/discuss/1/). Your suggestion will be helped if you include a mockup design; this can be simple, even hand-drawn.
|
||||
|
||||
### Other resources
|
||||
Beyond this repo, there are also some other resources that you might want to take a look at:
|
||||
* [Community Guidelines](https://github.com/LLK/scratch-www/wiki/Community-Guidelines) (we find it important to maintain a constructive and welcoming community, just like on Scratch)
|
||||
* [Open Source forum](https://scratch.mit.edu/discuss/49/) on Scratch
|
||||
|
||||
@@ -14,6 +14,6 @@ npm-*
|
||||
/dist
|
||||
/.opt-in
|
||||
|
||||
# generated translation files
|
||||
# Generated translation files
|
||||
/translations
|
||||
/locale
|
||||
@@ -5,8 +5,11 @@
|
||||
/node_modules
|
||||
npm-*
|
||||
|
||||
# Double copies of all the static assets and tutorial gifs
|
||||
/src
|
||||
# TO REVERT: the scratch-desktop branch wants to build scratch-gui from source.
|
||||
# To support that we need to un-ignore the /src directory.
|
||||
# Once we merge the scratch-desktop branch into develop we should re-ignore /src here.
|
||||
# We can also do that by omitting or reverting this commit during the merge.
|
||||
# /src
|
||||
|
||||
# Testing
|
||||
/.nyc_output
|
||||
|
||||
@@ -4,7 +4,7 @@ dist: xenial
|
||||
addons:
|
||||
chrome: stable
|
||||
node_js:
|
||||
- 8
|
||||
- 10
|
||||
env:
|
||||
global:
|
||||
- CHROMEDRIVER_VERSION=LATEST
|
||||
@@ -12,22 +12,13 @@ env:
|
||||
- NODE_OPTIONS=--max-old-space-size=7250
|
||||
- NPM_TAG=latest
|
||||
- RELEASE_VERSION="0.1.0-prerelease.$(date +'%Y%m%d%H%M%S')"
|
||||
- SMOKE_URL=https://llk.github.io/scratch-gui/$TRAVIS_PULL_REQUEST_BRANCH
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
# package-lock.json was introduced in npm@5
|
||||
- '[[ $(node -v) =~ ^v9.*$ ]] || npm install -g npm@latest' # skipped when using node 9
|
||||
- npm install -g greenkeeper-lockfile
|
||||
install:
|
||||
- npm --production=false install
|
||||
- npm --production=false update
|
||||
before_script: greenkeeper-lockfile-update
|
||||
script:
|
||||
- npm test
|
||||
- if [ "$TRAVIS_EVENT_TYPE" == "pull_request" ] && [ "$TRAVIS_BRANCH" == "master" ]; then npm run test:smoke; fi
|
||||
after_script: greenkeeper-lockfile-upload
|
||||
before_deploy:
|
||||
- >
|
||||
if [ -z "$BEFORE_DEPLOY_RAN" ]; then
|
||||
@@ -44,7 +35,6 @@ deploy:
|
||||
branch:
|
||||
- master
|
||||
- develop
|
||||
- smoke
|
||||
- hotfix/*
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
skip_cleanup: true
|
||||
@@ -57,25 +47,14 @@ deploy:
|
||||
- master
|
||||
- develop
|
||||
- smoke
|
||||
- hotfix/*
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
skip_cleanup: true
|
||||
script: if npm info scratch-gui | grep -q $RELEASE_VERSION; then git tag $RELEASE_VERSION && git push https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git $RELEASE_VERSION; fi
|
||||
- provider: s3
|
||||
on:
|
||||
branch:
|
||||
- master
|
||||
- $PREVIEW_BRANCH
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
access_key_id: $AWS_ACCESS_KEY_ID
|
||||
secret_access_key: $AWS_SECRET_ACCESS_KEY
|
||||
bucket: $AWS_BUCKET_NAME
|
||||
acl: public_read
|
||||
skip_cleanup: true
|
||||
local_dir: build
|
||||
- provider: script
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_EVENT_TYPE != cron && ! $TRAVIS_BRANCH =~ ^greenkeeper/
|
||||
condition: $TRAVIS_EVENT_TYPE != cron && ! $TRAVIS_BRANCH =~ ^dependabot/
|
||||
tags: false # Don't push tags to gh-pages
|
||||
skip_cleanup: true
|
||||
script: npm run deploy -- -x -e $TRAVIS_BRANCH -r https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
|
||||
@@ -85,5 +64,3 @@ deploy:
|
||||
condition: $TRAVIS_EVENT_TYPE == cron
|
||||
skip_cleanup: true
|
||||
script: npm run i18n:src && npm run i18n:push
|
||||
after_deploy:
|
||||
- 'curl -X POST -H "Fastly-Key: $FASTLY_TOKEN" -H "Accept: application/json" https://api.fastly.com/service/$FASTLY_SERVICE_ID/purge_all'
|
||||
@@ -18,6 +18,8 @@ cd scratch-gui
|
||||
npm install
|
||||
```
|
||||
|
||||
**You may want to add `--depth=1` to the `git clone` command because there are some [large files in the git repository history](https://github.com/LLK/scratch-gui/issues/5140).**
|
||||
|
||||
## Getting started
|
||||
Running the project requires Node.js to be installed.
|
||||
|
||||
@@ -74,7 +76,7 @@ See [jest cli docs](https://facebook.github.io/jest/docs/en/cli.html#content) fo
|
||||
|
||||
*NOTE: If you're a windows user, please run these scripts in Windows `cmd.exe` instead of Git Bash/MINGW64.*
|
||||
|
||||
Before running any test, make sure you have run `npm install` from this (scratch-gui) repository's top level.
|
||||
Before running any tests, make sure you have run `npm install` from this (scratch-gui) repository's top level.
|
||||
|
||||
#### Main testing command
|
||||
|
||||
@@ -194,5 +196,60 @@ Further reading: [Stack Overflow](https://stackoverflow.com/questions/46602286/n
|
||||
You can publish the GUI to github.io so that others on the Internet can view it.
|
||||
[Read the wiki for a step-by-step guide.](https://github.com/LLK/scratch-gui/wiki/Publishing-to-GitHub-Pages)
|
||||
|
||||
## Understanding the project state machine
|
||||
|
||||
Since so much code throughout scratch-gui depends on the state of the project, which goes through many different phases of loading, displaying and saving, we created a "finite state machine" to make it clear which state it is in at any moment. This is contained in the file src/reducers/project-state.js .
|
||||
|
||||
It can be hard to understand the code in src/reducers/project-state.js . There are several types of data and functions used, which relate to each other:
|
||||
|
||||
### Loading states
|
||||
|
||||
These include state constant strings like:
|
||||
|
||||
* `NOT_LOADED` (the default state),
|
||||
* `ERROR`,
|
||||
* `FETCHING_WITH_ID`,
|
||||
* `LOADING_VM_WITH_ID`,
|
||||
* `REMIXING`,
|
||||
* `SHOWING_WITH_ID`,
|
||||
* `SHOWING_WITHOUT_ID`,
|
||||
* etc.
|
||||
|
||||
### Transitions
|
||||
|
||||
These are names for the action which causes a state change. Some examples are:
|
||||
|
||||
* `START_FETCHING_NEW`,
|
||||
* `DONE_FETCHING_WITH_ID`,
|
||||
* `DONE_LOADING_VM_WITH_ID`,
|
||||
* `SET_PROJECT_ID`,
|
||||
* `START_AUTO_UPDATING`,
|
||||
|
||||
### How transitions relate to loading states
|
||||
|
||||
As this diagram of the project state machine shows, various transition actions can move us from one loading state to another:
|
||||
|
||||

|
||||
|
||||
_Note: for clarity, the diagram above excludes states and transitions relating to error handling._
|
||||
|
||||
#### Example
|
||||
|
||||
Here's an example of how states transition.
|
||||
|
||||
Suppose a user clicks on a project, and the page starts to load with url https://scratch.mit.edu/projects/123456 .
|
||||
|
||||
Here's what will happen in the project state machine:
|
||||
|
||||

|
||||
|
||||
1. When the app first mounts, the project state is `NOT_LOADED`.
|
||||
2. The `SET_PROJECT_ID` redux action is dispatched (from src/lib/project-fetcher-hoc.jsx), with `projectId` set to `123456`. This transitions the state from `NOT_LOADED` to `FETCHING_WITH_ID`.
|
||||
3. The `FETCHING_WITH_ID` state. In src/lib/project-fetcher-hoc.jsx, the `projectId` value `123456` is used to request the data for that project from the server.
|
||||
4. When the server responds with the data, src/lib/project-fetcher-hoc.jsx dispatches the `DONE_FETCHING_WITH_ID` action, with `projectData` set. This transitions the state from `FETCHING_WITH_ID` to `LOADING_VM_WITH_ID`.
|
||||
5. The `LOADING_VM_WITH_ID` state. In src/lib/vm-manager-hoc.jsx, we load the `projectData` into Scratch's virtual machine ("the vm").
|
||||
6. When loading is done, src/lib/vm-manager-hoc.jsx dispatches the `DONE_LOADING_VM_WITH_ID` action. This transitions the state from `LOADING_VM_WITH_ID` to `SHOWING_WITH_ID`
|
||||
7. The `SHOWING_WITH_ID` state. Now the project appears normally and is playable and editable.
|
||||
|
||||
## Donate
|
||||
We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1381px" height="1012px" viewBox="-0.5 -0.5 1381 1012" content="<mxfile host="www.draw.io" modified="2020-02-04T16:31:44.889Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36" etag="8a2X5Jj65jOGG04xBBh1" version="12.6.4" type="device"><diagram name="Page-1" id="58cdce13-f638-feb5-8d6f-7d28b1aa9fa0">7V1Zd6O4Ev41OffJHImdRydxujOT7XSSXuYlh9g4YcY2GUw6yfz6KxYBEqtBErjbeYkthMCo9FXVV6XiSDlZv3/y7ZfnS2/hrI5ksHg/Uk6PZBmqEKJ/YctH0gKBFrc8+e4iacsabt3/nKQRJK2v7sLZEh0Dz1sF7gvZOPc2G2ceEG2273tvZLeltyKv+mI/JVcEWcPt3F45hW7f3EXwnP6MXPfPjvv0nFza1JIDj/b8nyffe90k1zuSlWX0Fx9e23ispP/22V54b7kmZXaknPieF8Sf1u8nzip8uPixxeedVRxN79t3NkGbExRTfrTgo6ZB+LgAS2OSjPDTXr06+CdENxp84IeDRkDzgL4cPwfrFWqD6CP6IS/h8W1g+8FtYAfh8aW7Wp14K8+PTlRA9Bd2DnzvHyd3ZLlMjkSPzllkg8ZPJzw099buPPm8sh+d1XH6rPFIG28TXdbbBGf22l2F4vfV8Rf2xk6aE1GDcvK97O7slfu0QW1z9AwddPB44fpIyFwvbNx6r+EUHhcfNH5yjh8477mm5MF/cry1E/gfqEtyVMeLIlklE6gnUvGWyZySND3npA232YmUP6VDZzONPiSTXT7x/z3CCw98/uv8z5/G2fftuxxsLieqWZzsBVoYyVfPD569J29jr2ZZ6/H81f+ZzljW5cLzXpLGv50g+EievP0aeKTkOO9u8D2cV0lLvv3IHTl9T6Y8+vKBv2zQz82dFH79kT+WnRZ9w+dVzhqaVn/uVKyJZJqQZD85Qc3jS55e+Mhq5993Vnbg/iSxps9knn9Tzq//Mh+Xp856onvw6uz934lSspL1VZAIPjHL+r+vHj4w2UYTNUUdIHh5zw6iT0/h/9Prq9nDxfX09Pzq08PXy4dv53efH85P8eDoXuPx494FgUKLIyAFoLDewiXkIjCeJgfW7mIRiZrvoJuzH6Ohwul88dxNED037fhIO60GhgyG8R0VlmmqTJLx87ohE5eaZVO5zidAUlVFjQdrPffJcDfhT8zGope+t1xukUzSspLeRCvxqRL4StHBUxu3bV/sTStxgmXiVCtJ8dDk5R59uqWtPFtl11959mIbzrLv/Y1m/9QO7PDRbBBMoSewrhTrqptpkn9Sw709u4Fz+2JH2POGrClqafjzBDdltahPQ4Ge1+vTkWhQBtoyXY4f1PecskwVaF5b6ry0JSzMbbOy7KUfIdDzGlICWGGyVZKEQg9HvHF8Fz2ycCZ7aFG9qEULfQzWGjQ6der79keuQ6I2KkFW0wxC1CBlQxcwGdR0Rx/i63dF51LZM3RG2j2eT7ByN84Er5roaLi8tCJc3s7uHm6+XP8xO7nLQXUGg28uWowpmp4vwivIur0OgS0BxPBxdUXPkVgPjSZBFbxVWRl1xoNpQtJPMJiYEhMKUHUOlkXps1E64CapNQV5GcxwrvQxyEN5C+WAUub4jxVQ0E2E97HXQIJM27m7eUKtentYwT5db1hBLommkvSDzAZVVBKrhKGKJsgaY2VUxWOYxg6kB09TrNyx5WKOFWTG1EmhgbpGDhHfaHJWJjq72nW6SbkQ9XadpdZ152TXyYxguACzd9Mvd3na5uz8YvZwfxO27A1mtkRJTF+wML5kA1DCyQYmddLFmMgWB6CsWvjMpevqOpatWWtZOtAg3WgQjQKlSRqTGowHMfbFnhcbNTCKSraKaB1JxICX7okiBmezu5PPoe6hWN7MdEd4VyBi90oxsYwpGE2KSVGxrZAiQT/NxFfzGA2yxTKkUCVqYgIKTyViDJa+tw5HdJAX4B9iCiNRpnRMQbMGVqVQeEyhk/ZUCO0pAWjs5P2Gh9g4sjhfqNGTxR25u7IK6S0otF1f4cp2gNhSFW7ygdnWiQDX93ddALffjZCGQ0oR5vjB1jfidlUFZtmNIRNi4wVJD3SXQUj4kTf67PhOOKnbzf/C6yBjwdlubSQDWrUrlTYX7nYPeVD2phOsB31kO2mqbpJOPWDi1dNxGgnIVv5PhIuPfwpzKz630q9m3x5OZ2fT+4u7/mu6Z1ZGu3V9sKDE0BFCszJkW3mcyxqw50ugQ2MxsTpYT7l5rX/6zmYxDVOLw5FenE3ckjx3s3a6W84bnp+VswzqZqfeImrBOuhDsQ6lcK13mLTxhoPrQr21k8LeQu3FBbEKB9dzQZVKpJoLOpg4eM3UWjgQ9qSDsEvMwWapWyNCPJdU/koMGaEk0cJZ2q+roCXreTBuhjVuBqeHtC700C+3P6MuVWEAIqhf7Kwp1Z4fOVOlbMv4k71Vus25AiwzNTUyIiP308BpqgBJK1ADMNHI5eHAIXeDwRzWAMmoz3QvXdI9cIQdLYxza4FMnsIoowlCi/K/k9Spqpymwgl452rhhPLjvbOeyiXNEoSCd9cPt9Ovsz0DsY6eQ82irgMxS4MWISITyAbIdIlKeoKKKOdCrZSvfo5EYz6dAFeCySAU3xq6JhTlKu3slrCNpVylgRSSE/Yekxt2w/vfOFF8HURyJCB8cnCturlWpj4gb1zuGHThIflYO2239RGeVeZoMfetzJa+lczatepkE6kGKVpqw/Y9ur9p8Td4qvXRXm22QVAhowcA4uTyPKu2944j04B0I11r6NhZxHkjChOTS5MlqsaIbIiyubQu+/x+D7KqNaCOq6IIbEri7Lc55f7mdHoX4sfx7Oz6y+zh5Mss/n5yffPjN4aPeCFVwgeSWquUIerJWfDgmcqDWYOy2oTlBRWlHVRACQCTtL+Sc2sAo2wDX/dkyF7Igl38sSALK4OoHFkup1f304sUYPYGS7I9wnJ6gucvHD/X+Sz64wI8emMiHTAU0m7BPkBfzlshRwWSZYL8nyoMnIa1YgAgXUNdV9vaMqZMARQAewRQeMCxAFR1nK4fgxgD1PT+7roMnkj6cGzwJB6Q6i2hsNCabJDQwahWClQhWlC5VF4yf1hXJCis0oHepdQBO0gKK8VkkBQqgYg9bGM07UZYda530M8yYl6Dqh/wVJdxZAE8X2aX598PmNO42uq8L6iRkMOm0qMmqSTGTFS00oRhTJdN3b0xJjNeZDZMTLOZwdwPKp9NhdppqKpUHCPGr94JAfR1tAbyWzNr+/Nhv3E8WTSPlOOlR+/6cQCyxi3lumlSLLTOBMp0XbLIyL/Ox1wqlbYucT1moTy5ZSyPMo2MJtuIgNpSCGNmJZVv5Rce1VNIoFKs+kQnuj9MKAOuwMaNH2dZdPPXhr5qCKh1Gy1QWvK1fzU8HvW6y5UqKMykSK/QVOUc8IX0eEuiikI+ubtXyAsA+eWJ7mzdaWQ0VwP11h1M5bjrCeiSAuxBXpvNfqGidyzhEDaXHFYBmQPKiEfT0AKnCoRaEhCWBtpUHaNrwaHbz9ffyusNHZIHBZTtUbATO9zGrC7Kt4e+jVjXPA2LlqzSkoUFkmVRBXySc2u0biaqlTGhneha3nyLphiSSiKNKsuSBXMMPrW1tYKBKSphw5TMXDRS2e0yjHZ+aJR/o8pK7V039Oe0r4NXLZJoX0cZr1O3qW1vMxKFc95ac1lcgHc0Mao+iIkhUaZA044jlvUIKwhIAXtDts6mfFtHtKkDVyM8KV6dLGIYP8zDtvRxmD/0tvThzR+1S+GdvuYPnRhj1Js/JdYJadRUzkwjZyCquKCB43+pDqcmlJFtUXUdvrYCr53wMQmwl0Eg4Yo/XsiVyEPVt4Ogp9LHm8Go/aZAMpR82TwML/ytAmPYbD/iZSCgHs8yd84gYuUpn8p810LrYqsqazjsV2KDb27xL8EuNpcjZ/jmIRz6ZeQ54CAzRViaHDCjXHENsnG0R05dcdtDQ5S6dDmzwxA+AeailqFSXRSDsp4YpeAYNDvZEKku9K+qyJFy6P36i9jfKh8i4eMM/cjNW1Flk0xnZAPPikZX/5hwKf9R/ir0YeumQbmDXbdrRhDnuHhr409mAd67Ym4B2xsw0TD69ReSTYS3VnFK/S7brj/69G/2iKjUk21o9aoGm+A3lVqhwBQR+Tu2LKrCN4a+Cy9/ONC/AkrnDE//6uMpndNQKDDHmuS2a7fZYsB4GyQseTtcXcrKSHYjydVcCoN3yTRuRsqs/a7XYXO3VbHl0atP8bunGv0NXQEUH6SxcTiIQXmwQbULu88KKVW0OywOsmXh/mxzVX61BXcMQlfEl1Fz9EtG9uN2D5TTP+RgFjExi7IX4WIGePCSgro8pF2EX8uX2kZKhxB5DwtH0M5KOhlUbVlqmRnVWb1Bm0GO296VrBFvYVQQTpmFAS2TTNHsyWgKMCKqs9P6GRFxIaQGE6IfIVAQWcZvhmtnTRxeDzdOpTwCskIdUCkDyQL5WECYyAa6lfs1x1BurnWdhZHRGNXMOgedvYcZ5xzUdMWGyVzgUaNSOLDQ9CUCoCQT42qSQtSSE1ZUJcVP1mo9LB92UOoLine truncated
|
||||
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "npm run clean && webpack --progress --colors --bail",
|
||||
"clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist",
|
||||
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"",
|
||||
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"",
|
||||
"prune": "./prune-gh-pages.sh",
|
||||
"i18n:push": "tx-push-src scratch-editor interface translations/en.json",
|
||||
"i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push",
|
||||
@@ -44,7 +44,7 @@
|
||||
"babel-loader": "^8.0.4",
|
||||
"base64-loader": "1.0.0",
|
||||
"bowser": "1.9.4",
|
||||
"chromedriver": "77.0.0",
|
||||
"chromedriver": "83.0.0",
|
||||
"classnames": "2.2.6",
|
||||
"computed-style-to-inline-style": "3.0.0",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
@@ -78,9 +78,9 @@
|
||||
"lodash.pick": "4.4.0",
|
||||
"lodash.throttle": "4.0.1",
|
||||
"minilog": "3.1.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"omggif": "1.0.9",
|
||||
"papaparse": "4.6.2",
|
||||
"papaparse": "5.1.1",
|
||||
"postcss-import": "^12.0.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-simple-vars": "^5.0.1",
|
||||
@@ -103,18 +103,19 @@
|
||||
"react-test-renderer": "16.2.0",
|
||||
"react-tooltip": "3.8.0",
|
||||
"react-virtualized": "9.20.1",
|
||||
"react-visibility-sensor": "5.0.2",
|
||||
"redux": "3.7.2",
|
||||
"redux-mock-store": "^1.2.3",
|
||||
"redux-throttle": "0.1.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"scratch-audio": "0.1.0-prerelease.20190925183642",
|
||||
"scratch-l10n": "3.6.20191008224547",
|
||||
"scratch-blocks": "0.1.0-prerelease.1570730963",
|
||||
"scratch-paint": "0.2.0-prerelease.20191010161144",
|
||||
"scratch-render": "0.1.0-prerelease.20191021180653",
|
||||
"scratch-storage": "1.3.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20190822202608",
|
||||
"scratch-vm": "0.2.0-prerelease.20191017151616",
|
||||
"scratch-audio": "0.1.0-prerelease.20200528195344",
|
||||
"scratch-blocks": "0.1.0-prerelease.20200617163252",
|
||||
"scratch-l10n": "3.10.20200617210158",
|
||||
"scratch-paint": "0.2.0-prerelease.20200615150428",
|
||||
"scratch-render": "0.1.0-prerelease.20200617185826",
|
||||
"scratch-storage": "1.3.3",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20200610220938",
|
||||
"scratch-vm": "0.2.0-prerelease.20200618162938",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"startaudiocontext": "1.2.1",
|
||||
"style-loader": "^0.23.0",
|
||||
@@ -139,7 +140,8 @@
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
|
||||
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
|
||||
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
|
||||
"editor-msgs(\\.js)?$": "<rootDir>/test/__mocks__/editor-msgs-mock.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
padding-right: 1rem;
|
||||
margin-bottom: 7px;
|
||||
min-height: 1.5rem;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.alert.warn {
|
||||
|
||||
@@ -309,6 +309,7 @@ $fade-out-distance: 15px;
|
||||
z-index: $z-index-alerts;
|
||||
position: absolute;
|
||||
margin-top: 53px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -89,6 +89,7 @@ const GUIComponent = props => {
|
||||
loading,
|
||||
logo,
|
||||
renderLogin,
|
||||
onClickAbout,
|
||||
onClickAccountNav,
|
||||
onCloseAccountNav,
|
||||
onLogOut,
|
||||
@@ -105,6 +106,7 @@ const GUIComponent = props => {
|
||||
onRequestCloseTelemetryModal,
|
||||
onSeeCommunity,
|
||||
onShare,
|
||||
onShowPrivacyPolicy,
|
||||
onTelemetryModalCancel,
|
||||
onTelemetryModalOptIn,
|
||||
onTelemetryModalOptOut,
|
||||
@@ -162,6 +164,7 @@ const GUIComponent = props => {
|
||||
onOptIn={onTelemetryModalOptIn}
|
||||
onOptOut={onTelemetryModalOptOut}
|
||||
onRequestClose={onRequestCloseTelemetryModal}
|
||||
onShowPrivacyPolicy={onShowPrivacyPolicy}
|
||||
/>
|
||||
) : null}
|
||||
{loading ? (
|
||||
@@ -218,6 +221,7 @@ const GUIComponent = props => {
|
||||
logo={logo}
|
||||
renderLogin={renderLogin}
|
||||
showComingSoon={showComingSoon}
|
||||
onClickAbout={onClickAbout}
|
||||
onClickAccountNav={onClickAccountNav}
|
||||
onClickLogo={onClickLogo}
|
||||
onCloseAccountNav={onCloseAccountNav}
|
||||
@@ -388,6 +392,7 @@ GUIComponent.propTypes = {
|
||||
onActivateCostumesTab: PropTypes.func,
|
||||
onActivateSoundsTab: PropTypes.func,
|
||||
onActivateTab: PropTypes.func,
|
||||
onClickAbout: PropTypes.func,
|
||||
onClickAccountNav: PropTypes.func,
|
||||
onClickLogo: PropTypes.func,
|
||||
onCloseAccountNav: PropTypes.func,
|
||||
@@ -399,6 +404,7 @@ GUIComponent.propTypes = {
|
||||
onRequestCloseTelemetryModal: PropTypes.func,
|
||||
onSeeCommunity: PropTypes.func,
|
||||
onShare: PropTypes.func,
|
||||
onShowPrivacyPolicy: PropTypes.func,
|
||||
onTabSelect: PropTypes.func,
|
||||
onTelemetryModalCancel: PropTypes.func,
|
||||
onTelemetryModalOptIn: PropTypes.func,
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
|
||||
import Box from '../box/box.jsx';
|
||||
import PlayButton from '../../containers/play-button.jsx';
|
||||
import ScratchImage from '../scratch-image/scratch-image.jsx';
|
||||
import styles from './library-item.css';
|
||||
import classNames from 'classnames';
|
||||
|
||||
@@ -36,10 +37,12 @@ class LibraryItemComponent extends React.PureComponent {
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<img
|
||||
className={styles.featuredImage}
|
||||
src={this.props.iconURL}
|
||||
/>
|
||||
{this.props.iconSource ? (
|
||||
<ScratchImage
|
||||
className={styles.featuredImage}
|
||||
imageSource={this.props.iconSource}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
{this.props.insetIconURL ? (
|
||||
<div className={styles.libraryItemInsetImageContainer}>
|
||||
@@ -127,9 +130,9 @@ class LibraryItemComponent extends React.PureComponent {
|
||||
onMouseEnter={this.props.showPlayButton ? this.props.onMouseEnter : null}
|
||||
onMouseLeave={this.props.showPlayButton ? this.props.onMouseLeave : null}
|
||||
>
|
||||
<img
|
||||
<ScratchImage
|
||||
className={styles.libraryItemImage}
|
||||
src={this.props.iconURL}
|
||||
imageSource={this.props.iconSource}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -159,7 +162,7 @@ LibraryItemComponent.propTypes = {
|
||||
extensionId: PropTypes.string,
|
||||
featured: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
iconURL: PropTypes.string,
|
||||
iconSource: ScratchImage.ImageSourcePropType,
|
||||
insetIconURL: PropTypes.string,
|
||||
internetConnectionRequired: PropTypes.bool,
|
||||
isPlaying: PropTypes.bool,
|
||||
|
||||
@@ -10,6 +10,7 @@ import Divider from '../divider/divider.jsx';
|
||||
import Filter from '../filter/filter.jsx';
|
||||
import TagButton from '../../containers/tag-button.jsx';
|
||||
import Spinner from '../spinner/spinner.jsx';
|
||||
import storage from '../../lib/storage';
|
||||
|
||||
import styles from './library.css';
|
||||
|
||||
@@ -29,6 +30,61 @@ const messages = defineMessages({
|
||||
const ALL_TAG = {tag: 'all', intlLabel: messages.allTag};
|
||||
const tagListPrefix = [ALL_TAG];
|
||||
|
||||
/**
|
||||
* Find the AssetType which corresponds to a particular file extension. For example, 'png' => AssetType.ImageBitmap.
|
||||
* @param {string} fileExtension - the file extension to look up.
|
||||
* @returns {AssetType} - the AssetType corresponding to the extension, if any.
|
||||
*/
|
||||
const getAssetTypeForFileExtension = function (fileExtension) {
|
||||
const compareOptions = {
|
||||
sensitivity: 'accent',
|
||||
usage: 'search'
|
||||
};
|
||||
for (const assetTypeId in storage.AssetType) {
|
||||
const assetType = storage.AssetType[assetTypeId];
|
||||
if (fileExtension.localeCompare(assetType.runtimeFormat, compareOptions) === 0) {
|
||||
return assetType;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Figure out one or more icon(s) for a library item.
|
||||
* If it's an animated thumbnail, this will return an array of `imageSource`.
|
||||
* Otherwise it'll return just one `imageSource`.
|
||||
* @param {object} item - either a library item or one of a library item's costumes.
|
||||
* The latter is used internally as part of processing an animated thumbnail.
|
||||
* @returns {LibraryItem.PropTypes.icons} - an `imageSource` or array of them, ready for `LibraryItem` & `ScratchImage`
|
||||
*/
|
||||
const getItemIcons = function (item) {
|
||||
const costumes = (item.json && item.json.costumes) || item.costumes;
|
||||
if (costumes) {
|
||||
return costumes.map(getItemIcons);
|
||||
}
|
||||
|
||||
if (item.rawURL) {
|
||||
return {
|
||||
uri: item.rawURL
|
||||
};
|
||||
}
|
||||
|
||||
if (item.assetId && item.dataFormat) {
|
||||
return {
|
||||
assetId: item.assetId,
|
||||
assetType: getAssetTypeForFileExtension(item.dataFormat)
|
||||
};
|
||||
}
|
||||
|
||||
const md5ext = item.md5ext || item.md5 || item.baseLayerMD5;
|
||||
if (md5ext) {
|
||||
const [assetId, fileExtension] = md5ext.split('.');
|
||||
return {
|
||||
assetId: assetId,
|
||||
assetType: getAssetTypeForFileExtension(fileExtension)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class LibraryComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
@@ -208,8 +264,9 @@ class LibraryComponent extends React.Component {
|
||||
})}
|
||||
ref={this.setFilteredDataRef}
|
||||
>
|
||||
{this.state.loaded ? this.getFilteredData().map((dataItem, index) => (
|
||||
<LibraryItem
|
||||
{this.state.loaded ? this.getFilteredData().map((dataItem, index) => {
|
||||
const icons = getItemIcons(dataItem);
|
||||
return (<LibraryItem
|
||||
bluetoothRequired={dataItem.bluetoothRequired}
|
||||
collaborator={dataItem.collaborator}
|
||||
description={dataItem.description}
|
||||
@@ -217,9 +274,7 @@ class LibraryComponent extends React.Component {
|
||||
extensionId={dataItem.extensionId}
|
||||
featured={dataItem.featured}
|
||||
hidden={dataItem.hidden}
|
||||
iconMd5={dataItem.md5}
|
||||
iconRawURL={dataItem.rawURL}
|
||||
icons={dataItem.json && dataItem.json.costumes}
|
||||
icons={icons}
|
||||
id={index}
|
||||
insetIconURL={dataItem.insetIconURL}
|
||||
internetConnectionRequired={dataItem.internetConnectionRequired}
|
||||
@@ -230,8 +285,8 @@ class LibraryComponent extends React.Component {
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onSelect={this.handleSelect}
|
||||
/>
|
||||
)) : (
|
||||
/>);
|
||||
}) : (
|
||||
<div className={styles.spinnerWrapper}>
|
||||
<Spinner
|
||||
large
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M10 2c4.418 0 8 3.582 8 8s-3.582 8-8 8-8-3.582-8-8 3.582-8 8-8zm-.072 10.42c-.256 0-.472.076-.648.227-.176.151-.264.338-.264.56 0 .25.091.445.273.584.182.139.395.209.639.209.236 0 .444-.07.624-.211s.27-.335.27-.581c0-.222-.086-.409-.258-.56-.172-.151-.384-.227-.636-.227zm.144-6.42c-.624 0-1.17.113-1.638.338-.468.225-.824.51-1.068.853-.244.343-.366.683-.366 1.017 0 .162.077.313.231.452.154.139.343.209.567.209.38 0 .638-.199.774-.597.144-.38.32-.668.528-.864.208-.196.532-.293.972-.293.376 0 .683.097.921.291.238.194.357.432.357.713 0 .144-.039.278-.117.402-.078.123-.174.235-.288.336-.114.1-.299.249-.555.446-.292.225-.524.42-.696.584-.172.164-.31.354-.414.571-.104.217-.156.473-.156.769 0 .236.071.414.213.534.142.12.317.18.525.18.4 0 .638-.183.714-.55l.099-.362.093-.206.183-.227.327-.288c.492-.387.833-.663 1.023-.827.19-.164.354-.358.492-.584.138-.225.207-.488.207-.787 0-.38-.121-.733-.363-1.057-.242-.324-.585-.58-1.029-.769-.444-.188-.956-.283-1.536-.283z" id="a"/></defs><g transform="translate(-2 -2)" fill="none"><mask><use xlink:href="#a"/></mask><use fill="#fff" xlink:href="#a"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -218,3 +218,9 @@
|
||||
.mystuff > a {
|
||||
background-image: url("/images/mystuff.png");
|
||||
}
|
||||
|
||||
.about-icon {
|
||||
height: 1.25rem;
|
||||
margin: 0.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -69,6 +69,7 @@ import profileIcon from './icon--profile.png';
|
||||
import remixIcon from './icon--remix.svg';
|
||||
import dropdownCaret from './dropdown-caret.svg';
|
||||
import languageIcon from '../language-selector/language-icon.svg';
|
||||
import aboutIcon from './icon--about.svg';
|
||||
|
||||
import scratchLogo from './scratch-logo.svg';
|
||||
|
||||
@@ -141,6 +142,19 @@ MenuItemTooltip.propTypes = {
|
||||
isRtl: PropTypes.bool
|
||||
};
|
||||
|
||||
const AboutButton = props => (
|
||||
<Button
|
||||
className={classNames(styles.menuBarItem, styles.hoverable)}
|
||||
iconClassName={styles.aboutIcon}
|
||||
iconSrc={aboutIcon}
|
||||
onClick={props.onClick}
|
||||
/>
|
||||
);
|
||||
|
||||
AboutButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class MenuBar extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
@@ -311,6 +325,8 @@ class MenuBar extends React.Component {
|
||||
{remixMessage}
|
||||
</Button>
|
||||
);
|
||||
// Show the About button only if we have a handler for it (like in the desktop app)
|
||||
const aboutButton = this.props.onClickAbout ? <AboutButton onClick={this.props.onClickAbout} /> : null;
|
||||
return (
|
||||
<Box
|
||||
className={classNames(
|
||||
@@ -689,6 +705,8 @@ class MenuBar extends React.Component {
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{aboutButton}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -722,6 +740,7 @@ MenuBar.propTypes = {
|
||||
locale: PropTypes.string.isRequired,
|
||||
loginMenuOpen: PropTypes.bool,
|
||||
logo: PropTypes.string,
|
||||
onClickAbout: PropTypes.func,
|
||||
onClickAccount: PropTypes.func,
|
||||
onClickEdit: PropTypes.func,
|
||||
onClickFile: PropTypes.func,
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import VisibilitySensor from 'react-visibility-sensor';
|
||||
|
||||
import storage from '../../lib/storage';
|
||||
|
||||
class ScratchImage extends React.PureComponent {
|
||||
static init () {
|
||||
this._maxParallelism = 6;
|
||||
this._currentJobs = 0;
|
||||
this._pendingImages = new Set();
|
||||
}
|
||||
|
||||
static loadPendingImages () {
|
||||
if (this._currentJobs >= this._maxParallelism) {
|
||||
// already busy
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first visible image. If there aren't any, find the first non-visible image.
|
||||
let nextImage;
|
||||
for (const image of this._pendingImages) {
|
||||
if (image.isVisible) {
|
||||
nextImage = image;
|
||||
break;
|
||||
} else {
|
||||
nextImage = nextImage || image;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found an image to load:
|
||||
// 1) Remove it from the queue
|
||||
// 2) Load the image
|
||||
// 3) Pump the queue again
|
||||
if (nextImage) {
|
||||
this._pendingImages.delete(nextImage);
|
||||
const imageSource = nextImage.props.imageSource;
|
||||
++this._currentJobs;
|
||||
storage
|
||||
.load(imageSource.assetType, imageSource.assetId)
|
||||
.then(asset => {
|
||||
if (!nextImage.wasUnmounted) {
|
||||
const dataURI = asset.encodeDataURI();
|
||||
|
||||
nextImage.setState({
|
||||
imageURI: dataURI
|
||||
});
|
||||
}
|
||||
--this._currentJobs;
|
||||
this.loadPendingImages();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
Object.assign(this.state, this._loadImageSource(props.imageSource));
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
const newState = this._loadImageSource(nextProps.imageSource);
|
||||
this.setState(newState);
|
||||
}
|
||||
componentWillUnmount () {
|
||||
this.wasUnmounted = true;
|
||||
ScratchImage._pendingImages.delete(this);
|
||||
}
|
||||
/**
|
||||
* Calculate the state changes necessary to load the image specified in the provided source info. If the component
|
||||
* is mounted, call setState() with the return value of this function. If the component has not yet mounted, use
|
||||
* the return value of this function as initial state for the component.
|
||||
*
|
||||
* @param {object} imageSource - the new source for the image, including either assetId or URI
|
||||
* @returns {object} - the new state values, if any.
|
||||
*/
|
||||
_loadImageSource (imageSource) {
|
||||
if (imageSource) {
|
||||
if (imageSource.uri) {
|
||||
ScratchImage._pendingImages.delete(this);
|
||||
return {
|
||||
imageURI: imageSource.uri,
|
||||
lastRequestedAsset: null
|
||||
};
|
||||
}
|
||||
if (this.state.lastRequestedAsset !== imageSource.assetId) {
|
||||
ScratchImage._pendingImages.add(this);
|
||||
return {
|
||||
lastRequestedAsset: imageSource.assetId
|
||||
};
|
||||
}
|
||||
}
|
||||
// Nothing to do - don't change any state.
|
||||
return {};
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
imageSource: _imageSource,
|
||||
...imgProps
|
||||
} = this.props;
|
||||
return (
|
||||
<VisibilitySensor
|
||||
intervalCheck
|
||||
scrollCheck
|
||||
>
|
||||
{
|
||||
({isVisible}) => {
|
||||
this.isVisible = isVisible;
|
||||
ScratchImage.loadPendingImages();
|
||||
return (
|
||||
<img
|
||||
{...imgProps} // do this first in case it contains `src`
|
||||
src={this.state.imageURI} // overrides imgProps.src if present
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
</VisibilitySensor>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ScratchImage.ImageSourcePropType = PropTypes.oneOfType([
|
||||
PropTypes.shape({
|
||||
assetId: PropTypes.string.isRequired,
|
||||
assetType: PropTypes.oneOf(Object.values(storage.AssetType)).isRequired
|
||||
}),
|
||||
PropTypes.shape({
|
||||
uri: PropTypes.string.isRequired
|
||||
})
|
||||
]);
|
||||
|
||||
ScratchImage.propTypes = {
|
||||
imageSource: ScratchImage.ImageSourcePropType.isRequired
|
||||
};
|
||||
|
||||
ScratchImage.init();
|
||||
|
||||
export default ScratchImage;
|
||||
@@ -66,7 +66,6 @@
|
||||
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -133,7 +132,6 @@ to adjust for the border using a different method */
|
||||
}
|
||||
|
||||
.frame {
|
||||
box-sizing: content-box !important;
|
||||
background: $motion-transparent;
|
||||
border: 2px solid $motion-primary;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
@@ -101,6 +101,9 @@ class TelemetryModal extends React.PureComponent {
|
||||
privacyPolicyLink: (<a
|
||||
className={styles.privacyPolicyLink}
|
||||
href="https://scratch.mit.edu/privacy_policy/"
|
||||
onClick={this.props.onShowPrivacyPolicy}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FormattedMessage {...messages.privacyPolicyLink} />
|
||||
</a>)
|
||||
@@ -134,7 +137,8 @@ TelemetryModal.propTypes = {
|
||||
onCancel: PropTypes.func,
|
||||
onOptIn: PropTypes.func.isRequired,
|
||||
onOptOut: PropTypes.func.isRequired,
|
||||
onRequestClose: PropTypes.func
|
||||
onRequestClose: PropTypes.func,
|
||||
onShowPrivacyPolicy: PropTypes.func
|
||||
};
|
||||
|
||||
export default injectIntl(TelemetryModal);
|
||||
@@ -338,7 +338,7 @@ class Blocks extends React.Component {
|
||||
const stageCostumes = stage.getCostumes();
|
||||
const targetCostumes = target.getCostumes();
|
||||
const targetSounds = target.getSounds();
|
||||
const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML();
|
||||
const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target);
|
||||
return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML,
|
||||
targetCostumes[targetCostumes.length - 1].name,
|
||||
stageCostumes[stageCostumes.length - 1].name,
|
||||
|
||||
@@ -21,6 +21,7 @@ class LibraryItem extends React.PureComponent {
|
||||
'startRotatingIcons',
|
||||
'stopRotatingIcons'
|
||||
]);
|
||||
this.hasIconsArray = Array.isArray(props.icons);
|
||||
this.state = {
|
||||
iconIndex: 0,
|
||||
isRotatingIcon: false
|
||||
@@ -53,7 +54,7 @@ class LibraryItem extends React.PureComponent {
|
||||
// only show hover effects on the item if not showing a play button
|
||||
if (!this.props.showPlayButton) {
|
||||
this.props.onMouseEnter(this.props.id);
|
||||
if (this.props.icons && this.props.icons.length) {
|
||||
if (this.hasIconsArray) {
|
||||
this.stopRotatingIcons();
|
||||
this.setState({
|
||||
isRotatingIcon: true
|
||||
@@ -65,7 +66,7 @@ class LibraryItem extends React.PureComponent {
|
||||
// only show hover effects on the item if not showing a play button
|
||||
if (!this.props.showPlayButton) {
|
||||
this.props.onMouseLeave(this.props.id);
|
||||
if (this.props.icons && this.props.icons.length) {
|
||||
if (this.hasIconsArray) {
|
||||
this.setState({
|
||||
isRotatingIcon: false
|
||||
}, this.stopRotatingIcons);
|
||||
@@ -91,21 +92,22 @@ class LibraryItem extends React.PureComponent {
|
||||
const nextIconIndex = (this.state.iconIndex + 1) % this.props.icons.length;
|
||||
this.setState({iconIndex: nextIconIndex});
|
||||
}
|
||||
curIconMd5 () {
|
||||
if (this.props.icons &&
|
||||
this.state.isRotatingIcon &&
|
||||
this.state.iconIndex < this.props.icons.length &&
|
||||
this.props.icons[this.state.iconIndex] &&
|
||||
this.props.icons[this.state.iconIndex].baseLayerMD5) {
|
||||
return this.props.icons[this.state.iconIndex].baseLayerMD5;
|
||||
curIconSource () {
|
||||
if (this.hasIconsArray) {
|
||||
if (this.state.isRotatingIcon &&
|
||||
this.state.iconIndex < this.props.icons.length &&
|
||||
this.props.icons[this.state.iconIndex]) {
|
||||
// multiple icons, currently animating: show current frame
|
||||
return this.props.icons[this.state.iconIndex];
|
||||
}
|
||||
// multiple icons, not currently animating: show first frame
|
||||
return this.props.icons[0];
|
||||
}
|
||||
return this.props.iconMd5;
|
||||
// single icon
|
||||
return this.props.icons;
|
||||
}
|
||||
render () {
|
||||
const iconMd5 = this.curIconMd5();
|
||||
const iconURL = iconMd5 ?
|
||||
`https://cdn.assets.scratch.mit.edu/internalapi/asset/${iconMd5}/get/` :
|
||||
this.props.iconRawURL;
|
||||
const iconSource = this.curIconSource();
|
||||
return (
|
||||
<LibraryItemComponent
|
||||
bluetoothRequired={this.props.bluetoothRequired}
|
||||
@@ -115,8 +117,7 @@ class LibraryItem extends React.PureComponent {
|
||||
extensionId={this.props.extensionId}
|
||||
featured={this.props.featured}
|
||||
hidden={this.props.hidden}
|
||||
iconURL={iconURL}
|
||||
icons={this.props.icons}
|
||||
iconSource={iconSource}
|
||||
id={this.props.id}
|
||||
insetIconURL={this.props.insetIconURL}
|
||||
internetConnectionRequired={this.props.internetConnectionRequired}
|
||||
@@ -147,13 +148,10 @@ LibraryItem.propTypes = {
|
||||
extensionId: PropTypes.string,
|
||||
featured: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
iconMd5: PropTypes.string,
|
||||
iconRawURL: PropTypes.string,
|
||||
icons: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
baseLayerMD5: PropTypes.string
|
||||
})
|
||||
),
|
||||
icons: PropTypes.oneOfType([
|
||||
LibraryItemComponent.propTypes.iconSource, // single icon
|
||||
PropTypes.arrayOf(LibraryItemComponent.propTypes.iconSource) // rotating icons
|
||||
]),
|
||||
id: PropTypes.number.isRequired,
|
||||
insetIconURL: PropTypes.string,
|
||||
internetConnectionRequired: PropTypes.bool,
|
||||
|
||||
@@ -6,7 +6,12 @@ import VM from 'scratch-vm';
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {computeChunkedRMS, encodeAndAddSoundToVM, SOUND_BYTE_LIMIT} from '../lib/audio/audio-util.js';
|
||||
import {
|
||||
computeChunkedRMS,
|
||||
encodeAndAddSoundToVM,
|
||||
downsampleIfNeeded,
|
||||
dropEveryOtherSample
|
||||
} from '../lib/audio/audio-util.js';
|
||||
import AudioEffects from '../lib/audio/audio-effects.js';
|
||||
import SoundEditorComponent from '../components/sound-editor/sound-editor.jsx';
|
||||
import AudioBufferPlayer from '../lib/audio/audio-buffer-player.js';
|
||||
@@ -39,7 +44,8 @@ class SoundEditor extends React.Component {
|
||||
'paste',
|
||||
'handleKeyPress',
|
||||
'handleContainerClick',
|
||||
'setRef'
|
||||
'setRef',
|
||||
'resampleBufferToRate'
|
||||
]);
|
||||
this.state = {
|
||||
copyBuffer: null,
|
||||
@@ -132,45 +138,32 @@ class SoundEditor extends React.Component {
|
||||
});
|
||||
}
|
||||
submitNewSamples (samples, sampleRate, skipUndo) {
|
||||
// Encode the new sound into a wav so that it can be stored
|
||||
let wavBuffer = null;
|
||||
try {
|
||||
wavBuffer = WavEncoder.encode.sync({
|
||||
sampleRate: sampleRate,
|
||||
channelData: [samples]
|
||||
return downsampleIfNeeded({samples, sampleRate}, this.resampleBufferToRate)
|
||||
.then(({samples: newSamples, sampleRate: newSampleRate}) =>
|
||||
WavEncoder.encode({
|
||||
sampleRate: newSampleRate,
|
||||
channelData: [newSamples]
|
||||
}).then(wavBuffer => {
|
||||
if (!skipUndo) {
|
||||
this.redoStack = [];
|
||||
if (this.undoStack.length >= UNDO_STACK_SIZE) {
|
||||
this.undoStack.shift(); // Drop the first element off the array
|
||||
}
|
||||
this.undoStack.push(this.getUndoItem());
|
||||
}
|
||||
this.resetState(newSamples, newSampleRate);
|
||||
this.props.vm.updateSoundBuffer(
|
||||
this.props.soundIndex,
|
||||
this.audioBufferPlayer.buffer,
|
||||
new Uint8Array(wavBuffer));
|
||||
return true; // Edit was successful
|
||||
})
|
||||
)
|
||||
.catch(e => {
|
||||
// Encoding failed, or the sound was too large to save so edit is rejected
|
||||
log.error(`Encountered error while trying to encode sound update: ${e}`);
|
||||
return false; // Edit was not applied
|
||||
});
|
||||
|
||||
if (wavBuffer.byteLength > SOUND_BYTE_LIMIT) {
|
||||
// Cancel the sound update by setting to null
|
||||
wavBuffer = null;
|
||||
log.error(`Refusing to encode sound larger than ${SOUND_BYTE_LIMIT} bytes`);
|
||||
}
|
||||
} catch (e) {
|
||||
// This error state is mostly for the mock sounds used during testing.
|
||||
// Any incorrect sound buffer trying to get interpretd as a Wav file
|
||||
// should yield this error.
|
||||
// This can also happen if the sound is too be allocated in memory.
|
||||
log.error(`Encountered error while trying to encode sound update: ${e}`);
|
||||
}
|
||||
|
||||
// Do not submit sound if it could not be encoded (i.e. if too large)
|
||||
if (wavBuffer) {
|
||||
if (!skipUndo) {
|
||||
this.redoStack = [];
|
||||
if (this.undoStack.length >= UNDO_STACK_SIZE) {
|
||||
this.undoStack.shift(); // Drop the first element off the array
|
||||
}
|
||||
this.undoStack.push(this.getUndoItem());
|
||||
}
|
||||
this.resetState(samples, sampleRate);
|
||||
this.props.vm.updateSoundBuffer(
|
||||
this.props.soundIndex,
|
||||
this.audioBufferPlayer.buffer,
|
||||
new Uint8Array(wavBuffer));
|
||||
|
||||
return true; // Update succeeded
|
||||
}
|
||||
return false; // Update failed
|
||||
}
|
||||
handlePlay () {
|
||||
this.audioBufferPlayer.stop();
|
||||
@@ -209,13 +202,15 @@ class SoundEditor extends React.Component {
|
||||
newSamples.set(firstPart, 0);
|
||||
newSamples.set(secondPart, firstPart.length);
|
||||
}
|
||||
this.submitNewSamples(newSamples, sampleRate);
|
||||
this.setState({
|
||||
trimStart: null,
|
||||
trimEnd: null
|
||||
this.submitNewSamples(newSamples, sampleRate).then(() => {
|
||||
this.setState({
|
||||
trimStart: null,
|
||||
trimEnd: null
|
||||
});
|
||||
});
|
||||
}
|
||||
handleDeleteInverse () {
|
||||
// Delete everything outside of the trimmers
|
||||
const {samples, sampleRate} = this.copyCurrentBuffer();
|
||||
const sampleCount = samples.length;
|
||||
const startIndex = Math.floor(this.state.trimStart * sampleCount);
|
||||
@@ -224,10 +219,13 @@ class SoundEditor extends React.Component {
|
||||
if (clippedSamples.length === 0) {
|
||||
clippedSamples = new Float32Array(1);
|
||||
}
|
||||
this.submitNewSamples(clippedSamples, sampleRate);
|
||||
this.setState({
|
||||
trimStart: null,
|
||||
trimEnd: null
|
||||
this.submitNewSamples(clippedSamples, sampleRate).then(success => {
|
||||
if (success) {
|
||||
this.setState({
|
||||
trimStart: null,
|
||||
trimEnd: null
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
handleUpdateTrim (trimStart, trimEnd) {
|
||||
@@ -257,14 +255,15 @@ class SoundEditor extends React.Component {
|
||||
effects.process((renderedBuffer, adjustedTrimStart, adjustedTrimEnd) => {
|
||||
const samples = renderedBuffer.getChannelData(0);
|
||||
const sampleRate = renderedBuffer.sampleRate;
|
||||
const success = this.submitNewSamples(samples, sampleRate);
|
||||
if (success) {
|
||||
if (this.state.trimStart === null) {
|
||||
this.handlePlay();
|
||||
} else {
|
||||
this.setState({trimStart: adjustedTrimStart, trimEnd: adjustedTrimEnd}, this.handlePlay);
|
||||
this.submitNewSamples(samples, sampleRate).then(success => {
|
||||
if (success) {
|
||||
if (this.state.trimStart === null) {
|
||||
this.handlePlay();
|
||||
} else {
|
||||
this.setState({trimStart: adjustedTrimStart, trimEnd: adjustedTrimEnd}, this.handlePlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
tooLoud () {
|
||||
@@ -287,16 +286,22 @@ class SoundEditor extends React.Component {
|
||||
this.redoStack.push(this.getUndoItem());
|
||||
const {samples, sampleRate, trimStart, trimEnd} = this.undoStack.pop();
|
||||
if (samples) {
|
||||
this.submitNewSamples(samples, sampleRate, true);
|
||||
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
|
||||
return this.submitNewSamples(samples, sampleRate, true).then(success => {
|
||||
if (success) {
|
||||
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleRedo () {
|
||||
const {samples, sampleRate, trimStart, trimEnd} = this.redoStack.pop();
|
||||
if (samples) {
|
||||
this.undoStack.push(this.getUndoItem());
|
||||
this.submitNewSamples(samples, sampleRate, true);
|
||||
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
|
||||
return this.submitNewSamples(samples, sampleRate, true).then(success => {
|
||||
if (success) {
|
||||
this.setState({trimStart: trimStart, trimEnd: trimEnd}, this.handlePlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handleCopy () {
|
||||
@@ -322,25 +327,39 @@ class SoundEditor extends React.Component {
|
||||
});
|
||||
}
|
||||
resampleBufferToRate (buffer, newRate) {
|
||||
return new Promise(resolve => {
|
||||
if (window.OfflineAudioContext) {
|
||||
const sampleRateRatio = newRate / buffer.sampleRate;
|
||||
const newLength = sampleRateRatio * buffer.samples.length;
|
||||
const offlineContext = new window.OfflineAudioContext(1, newLength, newRate);
|
||||
const source = offlineContext.createBufferSource();
|
||||
const audioBuffer = offlineContext.createBuffer(1, buffer.samples.length, buffer.sampleRate);
|
||||
audioBuffer.getChannelData(0).set(buffer.samples);
|
||||
source.buffer = audioBuffer;
|
||||
source.connect(offlineContext.destination);
|
||||
source.start();
|
||||
offlineContext.startRendering();
|
||||
offlineContext.oncomplete = ({renderedBuffer}) => {
|
||||
resolve({
|
||||
samples: renderedBuffer.getChannelData(0),
|
||||
sampleRate: newRate
|
||||
});
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
const sampleRateRatio = newRate / buffer.sampleRate;
|
||||
const newLength = sampleRateRatio * buffer.samples.length;
|
||||
let offlineContext;
|
||||
// Try to use either OfflineAudioContext or webkitOfflineAudioContext to resample
|
||||
// The constructors will throw if trying to resample at an unsupported rate
|
||||
// (e.g. Safari/webkitOAC does not support lower than 44khz).
|
||||
try {
|
||||
if (window.OfflineAudioContext) {
|
||||
offlineContext = new window.OfflineAudioContext(1, newLength, newRate);
|
||||
} else if (window.webkitOfflineAudioContext) {
|
||||
offlineContext = new window.webkitOfflineAudioContext(1, newLength, newRate);
|
||||
}
|
||||
} catch {
|
||||
// If no OAC available and downsampling by 2, downsample by dropping every other sample.
|
||||
if (newRate === buffer.sampleRate / 2) {
|
||||
return resolve(dropEveryOtherSample(buffer));
|
||||
}
|
||||
return reject('Could not resample');
|
||||
}
|
||||
const source = offlineContext.createBufferSource();
|
||||
const audioBuffer = offlineContext.createBuffer(1, buffer.samples.length, buffer.sampleRate);
|
||||
audioBuffer.getChannelData(0).set(buffer.samples);
|
||||
source.buffer = audioBuffer;
|
||||
source.connect(offlineContext.destination);
|
||||
source.start();
|
||||
offlineContext.startRendering();
|
||||
offlineContext.oncomplete = ({renderedBuffer}) => {
|
||||
resolve({
|
||||
samples: renderedBuffer.getChannelData(0),
|
||||
sampleRate: newRate
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
paste () {
|
||||
@@ -351,8 +370,11 @@ class SoundEditor extends React.Component {
|
||||
const newSamples = new Float32Array(newLength);
|
||||
newSamples.set(samples, 0);
|
||||
newSamples.set(this.state.copyBuffer.samples, samples.length);
|
||||
this.submitNewSamples(newSamples, this.props.sampleRate, false);
|
||||
this.handlePlay();
|
||||
this.submitNewSamples(newSamples, this.props.sampleRate, false).then(success => {
|
||||
if (success) {
|
||||
this.handlePlay();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// else replace the selection with the pasted sound
|
||||
const trimStartSamples = this.state.trimStart * samples.length;
|
||||
@@ -371,11 +393,14 @@ class SoundEditor extends React.Component {
|
||||
const newDurationSeconds = newSamples.length / this.state.copyBuffer.sampleRate;
|
||||
const adjustedTrimStart = trimStartSeconds / newDurationSeconds;
|
||||
const adjustedTrimEnd = trimEndSeconds / newDurationSeconds;
|
||||
this.submitNewSamples(newSamples, this.props.sampleRate, false);
|
||||
this.setState({
|
||||
trimStart: adjustedTrimStart,
|
||||
trimEnd: adjustedTrimEnd
|
||||
}, this.handlePlay);
|
||||
this.submitNewSamples(newSamples, this.props.sampleRate, false).then(success => {
|
||||
if (success) {
|
||||
this.setState({
|
||||
trimStart: adjustedTrimStart,
|
||||
trimEnd: adjustedTrimEnd
|
||||
}, this.handlePlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
handlePaste () {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import GUI from './containers/gui.jsx';
|
||||
import AppStateHOC from './lib/app-state-hoc.jsx';
|
||||
import TitledHOC from './lib/titled-hoc.jsx';
|
||||
import GuiReducer, {guiInitialState, guiMiddleware, initEmbedded, initFullScreen, initPlayer} from './reducers/gui';
|
||||
import LocalesReducer, {localesInitialState, initLocale} from './reducers/locales';
|
||||
import {ScratchPaintReducer} from 'scratch-paint';
|
||||
@@ -16,6 +17,7 @@ const guiReducers = {
|
||||
export {
|
||||
GUI as default,
|
||||
AppStateHOC,
|
||||
TitledHOC,
|
||||
setAppElement,
|
||||
guiReducers,
|
||||
guiInitialState,
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import GoogleAnalytics from 'react-ga';
|
||||
|
||||
GoogleAnalytics.initialize(process.env.GA_ID || window.GA_ID, {
|
||||
debug: (process.env.NODE_ENV !== 'production'),
|
||||
titleCase: true,
|
||||
sampleRate: (process.env.NODE_ENV === 'production') ? 100 : 0,
|
||||
forceSSL: true
|
||||
});
|
||||
import log from './log';
|
||||
|
||||
const GA_ID = (process.env.GA_ID || window.GA_ID);
|
||||
if (GA_ID) {
|
||||
GoogleAnalytics.initialize(GA_ID, {
|
||||
debug: (process.env.NODE_ENV !== 'production'),
|
||||
titleCase: true,
|
||||
sampleRate: (process.env.NODE_ENV === 'production') ? 100 : 0,
|
||||
forceSSL: true
|
||||
});
|
||||
} else {
|
||||
log.info('Disabling GA because GA_ID is not set.');
|
||||
window.ga = () => {
|
||||
// The `react-ga` module calls this function to implement all Google Analytics calls. Providing an empty
|
||||
// function effectively disables `react-ga`. This is similar to the `testModeAPI` feature of `react-ga` except
|
||||
// that `testModeAPI` logs the arguments of every call into an array. That's nice for testing purposes but
|
||||
// would look like a memory leak in a live program.
|
||||
};
|
||||
}
|
||||
|
||||
export default GoogleAnalytics;
|
||||
@@ -59,9 +59,57 @@ const encodeAndAddSoundToVM = function (vm, samples, sampleRate, name, callback)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@typedef SoundBuffer
|
||||
@type {Object}
|
||||
@property {Float32Array} samples Array of audio samples
|
||||
@property {number} sampleRate Audio sample rate
|
||||
*/
|
||||
|
||||
/**
|
||||
* Downsample the given buffer to try to reduce file size below SOUND_BYTE_LIMIT
|
||||
* @param {SoundBuffer} buffer - Buffer to resample
|
||||
* @param {function(SoundBuffer):Promise<SoundBuffer>} resampler - resampler function
|
||||
* @returns {SoundBuffer} Downsampled buffer with half the sample rate
|
||||
*/
|
||||
const downsampleIfNeeded = (buffer, resampler) => {
|
||||
const {samples, sampleRate} = buffer;
|
||||
const duration = samples.length / sampleRate;
|
||||
const encodedByteLength = samples.length * 2; /* bitDepth 16 bit */
|
||||
// Resolve immediately if already within byte limit
|
||||
if (encodedByteLength < SOUND_BYTE_LIMIT) {
|
||||
return Promise.resolve({samples, sampleRate});
|
||||
}
|
||||
// If encodeable at 22khz, resample and call submitNewSamples again
|
||||
if (duration * 22050 * 2 < SOUND_BYTE_LIMIT) {
|
||||
return resampler({samples, sampleRate}, 22050);
|
||||
}
|
||||
// Cannot save this sound at 22khz, refuse to edit
|
||||
// In the future we could introduce further compression here
|
||||
return Promise.reject('Sound too large to save, refusing to edit');
|
||||
};
|
||||
|
||||
/**
|
||||
* Drop every other sample of an audio buffer as a last-resort way of downsampling.
|
||||
* @param {SoundBuffer} buffer - Buffer to resample
|
||||
* @returns {SoundBuffer} Downsampled buffer with half the sample rate
|
||||
*/
|
||||
const dropEveryOtherSample = buffer => {
|
||||
const newLength = Math.floor(buffer.samples.length / 2);
|
||||
const newSamples = new Float32Array(newLength);
|
||||
for (let i = 0; i < newLength; i++) {
|
||||
newSamples[i] = buffer.samples[i * 2];
|
||||
}
|
||||
return {
|
||||
samples: newSamples,
|
||||
sampleRate: buffer.sampleRate / 2
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
computeRMS,
|
||||
computeChunkedRMS,
|
||||
encodeAndAddSoundToVM,
|
||||
SOUND_BYTE_LIMIT
|
||||
downsampleIfNeeded,
|
||||
dropEveryOtherSample
|
||||
};
|
||||
@@ -2,10 +2,22 @@ import StartAudioContext from 'startaudiocontext';
|
||||
import bowser from 'bowser';
|
||||
|
||||
let AUDIO_CONTEXT;
|
||||
if (!bowser.msie) {
|
||||
AUDIO_CONTEXT = new (window.AudioContext || window.webkitAudioContext)();
|
||||
|
||||
StartAudioContext(AUDIO_CONTEXT);
|
||||
if (!bowser.msie) {
|
||||
/**
|
||||
* AudioContext can be initialized only when user interaction event happens
|
||||
*/
|
||||
const event =
|
||||
typeof document.ontouchstart === 'undefined' ?
|
||||
'mousedown' :
|
||||
'touchstart';
|
||||
const initAudioContext = () => {
|
||||
document.removeEventListener(event, initAudioContext);
|
||||
AUDIO_CONTEXT = new (window.AudioContext ||
|
||||
window.webkitAudioContext)();
|
||||
StartAudioContext(AUDIO_CONTEXT);
|
||||
};
|
||||
document.addEventListener(event, initAudioContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,13 +54,10 @@ const cloudManagerHOC = function (WrappedComponent) {
|
||||
canUseCloud (props) {
|
||||
return !!(props.cloudHost && props.username && props.vm && props.projectId && props.hasCloudPermission);
|
||||
}
|
||||
shouldNotModifyCloudData (props) {
|
||||
return (props.hasEverEnteredEditor && !props.canSave);
|
||||
}
|
||||
shouldConnect (props) {
|
||||
return !this.isConnected() && this.canUseCloud(props) &&
|
||||
props.isShowingWithId && props.vm.runtime.hasCloudData() &&
|
||||
!this.shouldNotModifyCloudData(props);
|
||||
props.canModifyCloudData;
|
||||
}
|
||||
shouldDisconnect (props, prevProps) {
|
||||
return this.isConnected() &&
|
||||
@@ -70,7 +67,7 @@ const cloudManagerHOC = function (WrappedComponent) {
|
||||
(props.projectId !== prevProps.projectId) ||
|
||||
(props.username !== prevProps.username) ||
|
||||
// Editing someone else's project
|
||||
this.shouldNotModifyCloudData(props)
|
||||
!props.canModifyCloudData
|
||||
);
|
||||
}
|
||||
isConnected () {
|
||||
@@ -102,11 +99,11 @@ const cloudManagerHOC = function (WrappedComponent) {
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
canModifyCloudData,
|
||||
cloudHost,
|
||||
projectId,
|
||||
username,
|
||||
hasCloudPermission,
|
||||
hasEverEnteredEditor,
|
||||
isShowingWithId,
|
||||
onShowCloudInfo,
|
||||
/* eslint-enable no-unused-vars */
|
||||
@@ -124,23 +121,30 @@ const cloudManagerHOC = function (WrappedComponent) {
|
||||
}
|
||||
|
||||
CloudManager.propTypes = {
|
||||
canSave: PropTypes.bool.isRequired,
|
||||
canModifyCloudData: PropTypes.bool.isRequired,
|
||||
cloudHost: PropTypes.string,
|
||||
hasCloudPermission: PropTypes.bool,
|
||||
hasEverEnteredEditor: PropTypes.bool,
|
||||
isShowingWithId: PropTypes.bool,
|
||||
isShowingWithId: PropTypes.bool.isRequired,
|
||||
onShowCloudInfo: PropTypes.func,
|
||||
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
username: PropTypes.string,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
CloudManager.defaultProps = {
|
||||
cloudHost: null,
|
||||
hasCloudPermission: false,
|
||||
onShowCloudInfo: () => {},
|
||||
username: null
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const loadingState = state.scratchGui.projectState.loadingState;
|
||||
return {
|
||||
hasEverEnteredEditor: state.scratchGui.mode.hasEverEnteredEditor,
|
||||
isShowingWithId: getIsShowingWithId(loadingState),
|
||||
projectId: state.scratchGui.projectState.projectId
|
||||
projectId: state.scratchGui.projectState.projectId,
|
||||
// if you're editing someone else's project, you can't modify cloud data
|
||||
canModifyCloudData: (!state.scratchGui.mode.hasEverEnteredEditor || ownProps.canSave)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -8,11 +8,23 @@ export default (filename, blob) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
downloadLink.href = url;
|
||||
downloadLink.download = filename;
|
||||
downloadLink.type = blob.type;
|
||||
downloadLink.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(downloadLink);
|
||||
if ('download' in HTMLAnchorElement.prototype) {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
downloadLink.href = url;
|
||||
downloadLink.download = filename;
|
||||
downloadLink.type = blob.type;
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} else {
|
||||
// iOS Safari, open a new page and set href to data-uri
|
||||
let popup = window.open('', '_blank');
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = function () {
|
||||
popup.location.href = reader.result;
|
||||
popup = null;
|
||||
};
|
||||
reader.readAsDataURL(blob);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -78,7 +78,7 @@ const DropAreaHOC = function (dragTypes) {
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const componentProps = omit(this.props, ['onDrop', 'dragInfo']);
|
||||
const componentProps = omit(this.props, ['onDrop', 'dragInfo', 'componentRef']);
|
||||
return (
|
||||
<WrappedComponent
|
||||
containerRef={this.setRef}
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Blue Sky 2 ",
|
||||
"md5": "5a906c2f272b77f59f0ef207857a8b3a.svg",
|
||||
"md5": "8eb8790be5507fdccf73e7c1570bbbab.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"outdoors",
|
||||
@@ -286,7 +286,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Circles",
|
||||
"md5": "4e29033ec2b891a8f1ca21242811d403.svg",
|
||||
"md5": "c9847be305920807c5597d81576dd0c4.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"patterns"
|
||||
@@ -491,7 +491,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Hearts",
|
||||
"md5": "26d3418b2fbc1af2c5fea82e1c3df1db.svg",
|
||||
"md5": "f98526ccb0eec3ac7d6c8f8ab502825e.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"patterns"
|
||||
@@ -1050,7 +1050,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Underwater 1",
|
||||
"md5": "fb907f72b310acc8b95cbf2d2cccabc9.svg",
|
||||
"md5": "d3344650f594bcecdf46aa4a9441badd.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"ocean",
|
||||
@@ -1175,7 +1175,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Witch House",
|
||||
"md5": "597b9a9813fe5d8d387283138a0b8f2b.svg",
|
||||
"md5": "30085b2d27beb5acdbe895d8b3e64b04.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"fantasy",
|
||||
@@ -1190,7 +1190,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Woods",
|
||||
"md5": "d26cfd278999c9e37d6af75c296a58df.svg",
|
||||
"md5": "f3eb165d6f3fd23370f97079f2e631bf.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"fantasy",
|
||||
|
||||
@@ -3,7 +3,7 @@ import introMove from './steps/intro-move.en.gif';
|
||||
import introSayHello from './steps/intro-say-hello.en.gif';
|
||||
|
||||
// Text to Speech
|
||||
import speechAddExtension from './steps/speech-add-extension.en.gif';
|
||||
import speechAddExtension from './steps/talking-1-add-extension.en.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.en.gif';
|
||||
import speechSetVoice from './steps/speech-set-voice.en.gif';
|
||||
import speechMoveAround from './steps/speech-move-around.en.gif';
|
||||
@@ -15,12 +15,12 @@ import speechSpin from './steps/speech-spin.en.gif';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.en.gif';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.en.gif';
|
||||
import cnShowCharacter from './steps/cn-show-character.gif';
|
||||
import cnSay from './steps/cn-say.en.gif';
|
||||
import cnGlide from './steps/cn-glide.en.gif';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.en.gif';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.gif';
|
||||
import cnCollect from './steps/cn-collect.en.gif';
|
||||
import cnVariable from './steps/cn-variable.en.gif';
|
||||
import cnVariable from './steps/add-variable.en.gif';
|
||||
import cnScore from './steps/cn-score.en.gif';
|
||||
import cnBackdrop from './steps/cn-backdrop.en.gif';
|
||||
|
||||
@@ -43,20 +43,20 @@ import musicMakeBeat from './steps/music-make-beat.en.gif';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.en.gif';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.en.gif';
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.en.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.en.gif';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.en.gif';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.en.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.en.gif';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.en.gif';
|
||||
import chaseGameAddVariable from './steps/chase-game-add-variable.en.gif';
|
||||
import chaseGameAddVariable from './steps/add-variable.en.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.en.gif';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.en.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.en.gif';
|
||||
import popGameAddScore from './steps/pop-game-add-score.en.gif';
|
||||
import popGameAddScore from './steps/add-variable.en.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.en.gif';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.en.gif';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.en.gif';
|
||||
@@ -73,22 +73,22 @@ import animateCharJump from './steps/animate-char-jump.en.gif';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.en.gif';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.en.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.en.gif';
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.gif';
|
||||
import storySaySomething from './steps/story-say-something.en.gif';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.en.gif';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.gif';
|
||||
import storyFlip from './steps/story-flip.en.gif';
|
||||
import storyConversation from './steps/story-conversation.en.gif';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.en.gif';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.en.gif';
|
||||
import storyHideCharacter from './steps/story-hide-character.en.gif';
|
||||
import storyShowCharacter from './steps/story-show-character.en.gif';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.en.gif';
|
||||
import videoPet from './steps/video-pet.en.gif';
|
||||
import videoAnimate from './steps/video-animate.en.gif';
|
||||
import videoPop from './steps/video-pop.en.gif';
|
||||
import videoPet from './steps/video-pet.en.png';
|
||||
import videoAnimate from './steps/video-animate.en.png';
|
||||
import videoPop from './steps/video-pop.en.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.en.gif';
|
||||
@@ -98,7 +98,7 @@ import flyMoveArrows from './steps/fly-make-interactive.en.gif';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.en.gif';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.en.gif';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.en.jpg';
|
||||
import flyAddScore from './steps/fly-add-score.en.gif';
|
||||
import flyAddScore from './steps/add-variable.en.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.en.gif';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.en.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.en.gif';
|
||||
@@ -106,13 +106,13 @@ import flySwitchLooks from './steps/fly-switch-costume.en.gif';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.en.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.en.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.en.gif';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.en.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.en.gif';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.en.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.en.png';
|
||||
import pongAddAScore from './steps/pong-add-score.en.gif';
|
||||
import pongAddAScore from './steps/add-variable.en.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.en.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.en.png';
|
||||
import pongResetScore from './steps/pong-reset-score.en.png';
|
||||
@@ -122,16 +122,16 @@ import pongGameOver from './steps/pong-game-over.en.png';
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.en.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.en.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.en.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.en.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.en.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.en.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.en.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.en.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.en.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.en.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.en.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.en.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.en.gif';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.en.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.en.png';
|
||||
@@ -173,10 +173,10 @@ import glideAroundPoint from './steps/glide-around-point.en.png';
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.en.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.en.gif';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.en.gif';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.en.gif';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.en.gif';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.en.gif';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.en.gif';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.en.gif';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.en.gif';
|
||||
|
||||
@@ -3,119 +3,227 @@ import introMove from './steps/intro-move.es.gif';
|
||||
import introSayHello from './steps/intro-say-hello.es.gif';
|
||||
|
||||
// Text to Speech
|
||||
// import speechAddExtension from './steps/speech-add-extension.es.gif';
|
||||
// import speechSaySomething from './steps/speech-say-something.es.gif';
|
||||
// import speechSetVoice from './steps/speech-set-voice.es.gif';
|
||||
// import speechMoveAround from './steps/speech-move-around.es.gif';
|
||||
// import speechAddBackdrop from './steps/speech-add-backdrop.es.gif';
|
||||
// import speechAddSprite from './steps/speech-add-sprite.es.gif';
|
||||
// import speechSong from './steps/speech-song.es.gif';
|
||||
// import speechChangeColor from './steps/speech-change-color.es.gif';
|
||||
// import speechSpin from './steps/speech-spin.es.gif';
|
||||
// import speechGrowShrink from './steps/speech-grow-shrink.es.gif';
|
||||
//
|
||||
// // Cartoon Network
|
||||
// import cnShowCharacter from './steps/cn-show-character.es.gif';
|
||||
// import cnSay from './steps/cn-say.es.gif';
|
||||
// import cnGlide from './steps/cn-glide.es.gif';
|
||||
// import cnPickSprite from './steps/cn-pick-sprite.es.gif';
|
||||
// import cnCollect from './steps/cn-collect.es.gif';
|
||||
// import cnVariable from './steps/cn-variable.es.gif';
|
||||
// import cnScore from './steps/cn-score.es.gif';
|
||||
// import cnBackdrop from './steps/cn-backdrop.es.gif';
|
||||
import speechAddExtension from './steps/speech-add-extension.es.gif';
|
||||
import speechSaySomething from './steps/speech-say-something.es.png';
|
||||
import speechSetVoice from './steps/speech-set-voice.es.png';
|
||||
import speechMoveAround from './steps/speech-move-around.es.png';
|
||||
import speechAddBackdrop from './steps/speech-add-backdrop.gif';
|
||||
import speechAddSprite from './steps/speech-add-sprite.gif';
|
||||
import speechSong from './steps/speech-song.es.png';
|
||||
import speechChangeColor from './steps/speech-change-color.es.png';
|
||||
import speechSpin from './steps/speech-spin.es.png';
|
||||
import speechGrowShrink from './steps/speech-grow-shrink.es.png';
|
||||
|
||||
// Cartoon Network
|
||||
import cnShowCharacter from './steps/cn-show-character.gif';
|
||||
import cnSay from './steps/cn-say.es.png';
|
||||
import cnGlide from './steps/cn-glide.es.png';
|
||||
import cnPickSprite from './steps/cn-pick-sprite.gif';
|
||||
import cnCollect from './steps/cn-collect.es.png';
|
||||
import cnVariable from './steps/add-variable.es.gif';
|
||||
import cnScore from './steps/cn-score.es.png';
|
||||
import cnBackdrop from './steps/cn-backdrop.es.png';
|
||||
|
||||
// Add sprite
|
||||
import addSprite from './steps/add-sprite.es.gif';
|
||||
import addSprite from './steps/add-sprite.gif';
|
||||
|
||||
// Animate a name
|
||||
import namePickLetter from './steps/name-pick-letter.es.gif';
|
||||
import namePlaySound from './steps/name-play-sound.es.gif';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.es.gif';
|
||||
import nameChangeColor from './steps/name-change-color.es.gif';
|
||||
import nameSpin from './steps/name-spin.es.gif';
|
||||
import nameGrow from './steps/name-grow.es.gif';
|
||||
import namePickLetter from './steps/name-pick-letter.gif';
|
||||
import namePlaySound from './steps/name-play-sound.es.png';
|
||||
import namePickLetter2 from './steps/name-pick-letter2.gif';
|
||||
import nameChangeColor from './steps/name-change-color.es.png';
|
||||
import nameSpin from './steps/name-spin.es.png';
|
||||
import nameGrow from './steps/name-grow.es.png';
|
||||
|
||||
// Make-Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.es.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.es.gif';
|
||||
import musicMakeSong from './steps/music-make-song.es.gif';
|
||||
import musicMakeBeat from './steps/music-make-beat.es.gif';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.es.gif';
|
||||
// Make Music
|
||||
import musicPickInstrument from './steps/music-pick-instrument.gif';
|
||||
import musicPlaySound from './steps/music-play-sound.es.png';
|
||||
import musicMakeSong from './steps/music-make-song.es.png';
|
||||
import musicMakeBeat from './steps/music-make-beat.es.png';
|
||||
import musicMakeBeatbox from './steps/music-make-beatbox.es.png';
|
||||
|
||||
// Chase-Game
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.es.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.es.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.es.gif';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.es.gif';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.es.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.es.gif';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.es.gif';
|
||||
import chaseGameAddVariable from './steps/chase-game-add-variable.es.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.es.gif';
|
||||
import chaseGameAddBackdrop from './steps/chase-game-add-backdrop.gif';
|
||||
import chaseGameAddSprite1 from './steps/chase-game-add-sprite1.gif';
|
||||
import chaseGameRightLeft from './steps/chase-game-right-left.es.png';
|
||||
import chaseGameUpDown from './steps/chase-game-up-down.es.png';
|
||||
import chaseGameAddSprite2 from './steps/chase-game-add-sprite2.gif';
|
||||
import chaseGameMoveRandomly from './steps/chase-game-move-randomly.es.png';
|
||||
import chaseGamePlaySound from './steps/chase-game-play-sound.es.png';
|
||||
import chaseGameAddVariable from './steps/add-variable.es.gif';
|
||||
import chaseGameChangeScore from './steps/chase-game-change-score.es.png';
|
||||
|
||||
// Clicker-Game (Pop Game)
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.es.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.es.gif';
|
||||
import popGameAddScore from './steps/pop-game-add-score.es.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.es.gif';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.es.gif';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.es.gif';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.es.gif';
|
||||
import popGamePickSprite from './steps/pop-game-pick-sprite.gif';
|
||||
import popGamePlaySound from './steps/pop-game-play-sound.es.png';
|
||||
import popGameAddScore from './steps/add-variable.es.gif';
|
||||
import popGameChangeScore from './steps/pop-game-change-score.es.png';
|
||||
import popGameRandomPosition from './steps/pop-game-random-position.es.png';
|
||||
import popGameChangeColor from './steps/pop-game-change-color.es.png';
|
||||
import popGameResetScore from './steps/pop-game-reset-score.es.png';
|
||||
|
||||
// Animate A Character
|
||||
import animateCharPickBackdrop from './steps/animate-char-pick-backdrop.es.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.es.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.es.gif';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.es.gif';
|
||||
import animateCharTalk from './steps/animate-char-talk.es.gif';
|
||||
import animateCharMove from './steps/animate-char-move.es.gif';
|
||||
import animateCharJump from './steps/animate-char-jump.es.gif';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.es.gif';
|
||||
import animateCharPickBackdrop from './steps/animate-char-pick-backdrop.gif';
|
||||
import animateCharPickSprite from './steps/animate-char-pick-sprite.gif';
|
||||
import animateCharSaySomething from './steps/animate-char-say-something.es.png';
|
||||
import animateCharAddSound from './steps/animate-char-add-sound.es.png';
|
||||
import animateCharTalk from './steps/animate-char-talk.es.png';
|
||||
import animateCharMove from './steps/animate-char-move.es.png';
|
||||
import animateCharJump from './steps/animate-char-jump.es.png';
|
||||
import animateCharChangeColor from './steps/animate-char-change-color.es.png';
|
||||
|
||||
// Tell A Story
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.es.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.es.gif';
|
||||
import storySaySomething from './steps/story-say-something.es.gif';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.es.gif';
|
||||
import storyPickBackdrop from './steps/story-pick-backdrop.gif';
|
||||
import storyPickSprite from './steps/story-pick-sprite.gif';
|
||||
import storySaySomething from './steps/story-say-something.es.png';
|
||||
import storyPickSprite2 from './steps/story-pick-sprite2.gif';
|
||||
import storyFlip from './steps/story-flip.es.gif';
|
||||
import storyConversation from './steps/story-conversation.es.gif';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.es.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.es.gif';
|
||||
import storyHideCharacter from './steps/story-hide-character.es.gif';
|
||||
import storyShowCharacter from './steps/story-show-character.es.gif';
|
||||
import storyConversation from './steps/story-conversation.es.png';
|
||||
import storyPickBackdrop2 from './steps/story-pick-backdrop2.gif';
|
||||
import storySwitchBackdrop from './steps/story-switch-backdrop.es.png';
|
||||
import storyHideCharacter from './steps/story-hide-character.es.png';
|
||||
import storyShowCharacter from './steps/story-show-character.es.png';
|
||||
|
||||
// Video Sensing
|
||||
import videoAddExtension from './steps/video-add-extension.es.gif';
|
||||
import videoPet from './steps/video-pet.es.gif';
|
||||
import videoAnimate from './steps/video-animate.es.gif';
|
||||
import videoPop from './steps/video-pop.es.gif';
|
||||
import videoPet from './steps/video-pet.es.png';
|
||||
import videoAnimate from './steps/video-animate.es.png';
|
||||
import videoPop from './steps/video-pop.es.png';
|
||||
|
||||
// Make it Fly
|
||||
import flyChooseBackdrop from './steps/fly-choose-backdrop.gif';
|
||||
import flyChooseCharacter from './steps/fly-choose-character.png';
|
||||
import flySaySomething from './steps/fly-say-something.es.png';
|
||||
import flyMoveArrows from './steps/fly-make-interactive.es.png';
|
||||
import flyChooseObject from './steps/fly-object-to-collect.png';
|
||||
import flyFlyingObject from './steps/fly-flying-heart.es.png';
|
||||
import flySelectFlyingSprite from './steps/fly-select-flyer.png';
|
||||
import flyAddScore from './steps/add-variable.es.gif';
|
||||
import flyKeepScore from './steps/fly-keep-score.es.png';
|
||||
import flyAddScenery from './steps/fly-choose-scenery.gif';
|
||||
import flyMoveScenery from './steps/fly-move-scenery.es.png';
|
||||
import flySwitchLooks from './steps/fly-switch-costume.es.png';
|
||||
|
||||
// Pong
|
||||
import pongAddBackdrop from './steps/pong-add-backdrop.png';
|
||||
import pongAddBallSprite from './steps/pong-add-ball-sprite.png';
|
||||
import pongBounceAround from './steps/pong-bounce-around.es.png';
|
||||
import pongAddPaddle from './steps/pong-add-a-paddle.gif';
|
||||
import pongMoveThePaddle from './steps/pong-move-the-paddle.es.png';
|
||||
import pongSelectBallSprite from './steps/pong-select-ball.png';
|
||||
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.es.png';
|
||||
import pongAddAScore from './steps/add-variable.es.gif';
|
||||
import pongChooseScoreFromMenu from './steps/pong-choose-score.es.png';
|
||||
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.es.png';
|
||||
import pongResetScore from './steps/pong-reset-score.es.png';
|
||||
import pongAddLineSprite from './steps/pong-add-line.gif';
|
||||
import pongGameOver from './steps/pong-game-over.es.png';
|
||||
|
||||
// Imagine a World
|
||||
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.es.png';
|
||||
import imagineClickGreenFlag from './steps/imagine-click-green-flag.es.png';
|
||||
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.png';
|
||||
import imagineChooseSprite from './steps/imagine-choose-any-sprite.png';
|
||||
import imagineFlyAround from './steps/imagine-fly-around.es.png';
|
||||
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.png';
|
||||
import imagineLeftRight from './steps/imagine-left-right.es.png';
|
||||
import imagineUpDown from './steps/imagine-up-down.es.png';
|
||||
import imagineChangeCostumes from './steps/imagine-change-costumes.es.png';
|
||||
import imagineGlideToPoint from './steps/imagine-glide-to-point.es.png';
|
||||
import imagineGrowShrink from './steps/imagine-grow-shrink.es.png';
|
||||
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.png';
|
||||
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.es.png';
|
||||
import imagineRecordASound from './steps/imagine-record-a-sound.es.gif';
|
||||
import imagineChooseSound from './steps/imagine-choose-sound.es.png';
|
||||
|
||||
// Add a Backdrop
|
||||
import addBackdrop from './steps/add-backdrop.png';
|
||||
|
||||
// Add Effects
|
||||
import addEffects from './steps/add-effects.es.png';
|
||||
|
||||
// Hide and Show
|
||||
import hideAndShow from './steps/hide-show.es.png';
|
||||
|
||||
// Switch Costumes
|
||||
import switchCostumes from './steps/switch-costumes.es.png';
|
||||
|
||||
// Change Size
|
||||
import changeSize from './steps/change-size.es.png';
|
||||
|
||||
// Spin
|
||||
import spinTurn from './steps/spin-turn.es.png';
|
||||
import spinPointInDirection from './steps/spin-point-in-direction.es.png';
|
||||
|
||||
// Record a Sound
|
||||
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.es.png';
|
||||
import recordASoundClickRecord from './steps/record-a-sound-click-record.es.png';
|
||||
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.es.png';
|
||||
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.es.png';
|
||||
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.es.png';
|
||||
|
||||
// Use Arrow Keys
|
||||
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.es.png';
|
||||
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.es.png';
|
||||
|
||||
// Glide Around
|
||||
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.es.png';
|
||||
import glideAroundPoint from './steps/glide-around-point.es.png';
|
||||
|
||||
// Code a Cartoon
|
||||
import codeCartoonSaySomething from './steps/code-cartoon-01-say-something.es.png';
|
||||
import codeCartoonAnimate from './steps/code-cartoon-02-animate.es.png';
|
||||
import codeCartoonSelectDifferentCharacter from './steps/code-cartoon-03-select-different-character.png';
|
||||
import codeCartoonUseMinusSign from './steps/code-cartoon-04-use-minus-sign.es.png';
|
||||
import codeCartoonGrowShrink from './steps/code-cartoon-05-grow-shrink.es.png';
|
||||
import codeCartoonSelectDifferentCharacter2 from './steps/code-cartoon-06-select-another-different-character.png';
|
||||
import codeCartoonJump from './steps/code-cartoon-07-jump.es.png';
|
||||
import codeCartoonChangeScenes from './steps/code-cartoon-08-change-scenes.es.png';
|
||||
import codeCartoonGlideAround from './steps/code-cartoon-09-glide-around.es.png';
|
||||
import codeCartoonChangeCostumes from './steps/code-cartoon-10-change-costumes.es.png';
|
||||
import codeCartoonChooseMoreCharacters from './steps/code-cartoon-11-choose-more-characters.png';
|
||||
|
||||
// Talking Tales
|
||||
import talesAddExtension from './steps/speech-add-extension.es.gif';
|
||||
import talesChooseSprite from './steps/talking-2-choose-sprite.png';
|
||||
import talesSaySomething from './steps/talking-3-say-something.es.png';
|
||||
import talesChooseBackdrop from './steps/talking-4-choose-backdrop.png';
|
||||
import talesSwitchBackdrop from './steps/talking-5-switch-backdrop.es.png';
|
||||
import talesChooseAnotherSprite from './steps/talking-6-choose-another-sprite.png';
|
||||
import talesMoveAround from './steps/talking-7-move-around.es.png';
|
||||
import talesChooseAnotherBackdrop from './steps/talking-8-choose-another-backdrop.png';
|
||||
import talesAnimateTalking from './steps/talking-9-animate.es.png';
|
||||
import talesChooseThirdBackdrop from './steps/talking-10-choose-third-backdrop.png';
|
||||
import talesChooseSound from './steps/talking-11-choose-sound.es.gif';
|
||||
import talesDanceMoves from './steps/talking-12-dance-moves.es.png';
|
||||
import talesAskAnswer from './steps/talking-13-ask-and-answer.es.png';
|
||||
|
||||
const esImages = {
|
||||
// Intro
|
||||
introMove: introMove,
|
||||
introSayHello: introSayHello,
|
||||
|
||||
// // Text to Speech
|
||||
// speechAddExtension: speechAddExtension,
|
||||
// speechSaySomething: speechSaySomething,
|
||||
// speechSetVoice: speechSetVoice,
|
||||
// speechMoveAround: speechMoveAround,
|
||||
// speechAddBackdrop: speechAddBackdrop,
|
||||
// speechAddSprite: speechAddSprite,
|
||||
// speechSong: speechSong,
|
||||
// speechChangeColor: speechChangeColor,
|
||||
// speechSpin: speechSpin,
|
||||
// speechGrowShrink: speechGrowShrink,
|
||||
//
|
||||
// // Cartoon Network
|
||||
// cnShowCharacter: cnShowCharacter,
|
||||
// cnSay: cnSay,
|
||||
// cnGlide: cnGlide,
|
||||
// cnPickSprite: cnPickSprite,
|
||||
// cnCollect: cnCollect,
|
||||
// cnVariable: cnVariable,
|
||||
// cnScore: cnScore,
|
||||
// cnBackdrop: cnBackdrop,
|
||||
// Text to Speech
|
||||
speechAddExtension: speechAddExtension,
|
||||
speechSaySomething: speechSaySomething,
|
||||
speechSetVoice: speechSetVoice,
|
||||
speechMoveAround: speechMoveAround,
|
||||
speechAddBackdrop: speechAddBackdrop,
|
||||
speechAddSprite: speechAddSprite,
|
||||
speechSong: speechSong,
|
||||
speechChangeColor: speechChangeColor,
|
||||
speechSpin: speechSpin,
|
||||
speechGrowShrink: speechGrowShrink,
|
||||
|
||||
// Cartoon Network
|
||||
cnShowCharacter: cnShowCharacter,
|
||||
cnSay: cnSay,
|
||||
cnGlide: cnGlide,
|
||||
cnPickSprite: cnPickSprite,
|
||||
cnCollect: cnCollect,
|
||||
cnVariable: cnVariable,
|
||||
cnScore: cnScore,
|
||||
cnBackdrop: cnBackdrop,
|
||||
|
||||
// Add sprite
|
||||
addSprite: addSprite,
|
||||
@@ -181,8 +289,115 @@ const esImages = {
|
||||
videoAddExtension: videoAddExtension,
|
||||
videoPet: videoPet,
|
||||
videoAnimate: videoAnimate,
|
||||
videoPop: videoPop
|
||||
videoPop: videoPop,
|
||||
|
||||
// Make it Fly
|
||||
flyChooseBackdrop: flyChooseBackdrop,
|
||||
flyChooseCharacter: flyChooseCharacter,
|
||||
flySaySomething: flySaySomething,
|
||||
flyMoveArrows: flyMoveArrows,
|
||||
flyChooseObject: flyChooseObject,
|
||||
flyFlyingObject: flyFlyingObject,
|
||||
flySelectFlyingSprite: flySelectFlyingSprite,
|
||||
flyAddScore: flyAddScore,
|
||||
flyKeepScore: flyKeepScore,
|
||||
flyAddScenery: flyAddScenery,
|
||||
flyMoveScenery: flyMoveScenery,
|
||||
flySwitchLooks: flySwitchLooks,
|
||||
|
||||
// Pong
|
||||
pongAddBackdrop: pongAddBackdrop,
|
||||
pongAddBallSprite: pongAddBallSprite,
|
||||
pongBounceAround: pongBounceAround,
|
||||
pongAddPaddle: pongAddPaddle,
|
||||
pongMoveThePaddle: pongMoveThePaddle,
|
||||
pongSelectBallSprite: pongSelectBallSprite,
|
||||
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
|
||||
pongAddAScore: pongAddAScore,
|
||||
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
|
||||
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
|
||||
pongResetScore: pongResetScore,
|
||||
pongAddLineSprite: pongAddLineSprite,
|
||||
pongGameOver: pongGameOver,
|
||||
|
||||
// Imagine a World
|
||||
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
|
||||
imagineClickGreenFlag: imagineClickGreenFlag,
|
||||
imagineChooseBackdrop: imagineChooseBackdrop,
|
||||
imagineChooseSprite: imagineChooseSprite,
|
||||
imagineFlyAround: imagineFlyAround,
|
||||
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
|
||||
imagineLeftRight: imagineLeftRight,
|
||||
imagineUpDown: imagineUpDown,
|
||||
imagineChangeCostumes: imagineChangeCostumes,
|
||||
imagineGlideToPoint: imagineGlideToPoint,
|
||||
imagineGrowShrink: imagineGrowShrink,
|
||||
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
|
||||
imagineSwitchBackdrops: imagineSwitchBackdrops,
|
||||
imagineRecordASound: imagineRecordASound,
|
||||
imagineChooseSound: imagineChooseSound,
|
||||
|
||||
// Add a Backdrop
|
||||
addBackdrop: addBackdrop,
|
||||
|
||||
// Add Effects
|
||||
addEffects: addEffects,
|
||||
|
||||
// Hide and Show
|
||||
hideAndShow: hideAndShow,
|
||||
|
||||
// Switch Costumes
|
||||
switchCostumes: switchCostumes,
|
||||
|
||||
// Change Size
|
||||
changeSize: changeSize,
|
||||
|
||||
// Spin
|
||||
spinTurn: spinTurn,
|
||||
spinPointInDirection: spinPointInDirection,
|
||||
|
||||
// Record a Sound
|
||||
recordASoundSoundsTab: recordASoundSoundsTab,
|
||||
recordASoundClickRecord: recordASoundClickRecord,
|
||||
recordASoundPressRecordButton: recordASoundPressRecordButton,
|
||||
recordASoundChooseSound: recordASoundChooseSound,
|
||||
recordASoundPlayYourSound: recordASoundPlayYourSound,
|
||||
|
||||
// Use Arrow Keys
|
||||
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
|
||||
moveArrowKeysUpDown: moveArrowKeysUpDown,
|
||||
|
||||
// Glide Around
|
||||
glideAroundBackAndForth: glideAroundBackAndForth,
|
||||
glideAroundPoint: glideAroundPoint,
|
||||
|
||||
// Code a Cartoon
|
||||
codeCartoonSaySomething: codeCartoonSaySomething,
|
||||
codeCartoonAnimate: codeCartoonAnimate,
|
||||
codeCartoonSelectDifferentCharacter: codeCartoonSelectDifferentCharacter,
|
||||
codeCartoonUseMinusSign: codeCartoonUseMinusSign,
|
||||
codeCartoonGrowShrink: codeCartoonGrowShrink,
|
||||
codeCartoonSelectDifferentCharacter2: codeCartoonSelectDifferentCharacter2,
|
||||
codeCartoonJump: codeCartoonJump,
|
||||
codeCartoonChangeScenes: codeCartoonChangeScenes,
|
||||
codeCartoonGlideAround: codeCartoonGlideAround,
|
||||
codeCartoonChangeCostumes: codeCartoonChangeCostumes,
|
||||
codeCartoonChooseMoreCharacters: codeCartoonChooseMoreCharacters,
|
||||
|
||||
// Talking Tales
|
||||
talesAddExtension: talesAddExtension,
|
||||
talesChooseSprite: talesChooseSprite,
|
||||
talesSaySomething: talesSaySomething,
|
||||
talesAskAnswer: talesAskAnswer,
|
||||
talesChooseBackdrop: talesChooseBackdrop,
|
||||
talesSwitchBackdrop: talesSwitchBackdrop,
|
||||
talesChooseAnotherSprite: talesChooseAnotherSprite,
|
||||
talesMoveAround: talesMoveAround,
|
||||
talesChooseAnotherBackdrop: talesChooseAnotherBackdrop,
|
||||
talesAnimateTalking: talesAnimateTalking,
|
||||
talesChooseThirdBackdrop: talesChooseThirdBackdrop,
|
||||
talesChooseSound: talesChooseSound,
|
||||
talesDanceMoves: talesDanceMoves
|
||||
};
|
||||
|
||||
export {esImages};
|
||||
@@ -104,82 +104,6 @@ export default {
|
||||
urlId: 'getStarted'
|
||||
},
|
||||
|
||||
'animate-a-name': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate a Name"
|
||||
description="Name for the 'Animate a Name' how-to"
|
||||
id="gui.howtos.animate-a-name.name"
|
||||
/>
|
||||
),
|
||||
img: libraryAnimate,
|
||||
tags: ['animation', 'art', 'spin', 'grow'],
|
||||
steps: [{
|
||||
video: 'animate-a-name'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick a Letter Sprite"
|
||||
description="Step name for 'Pick a Letter Sprite' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePickLetter"
|
||||
/>
|
||||
),
|
||||
image: 'namePickLetter'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Play a Sound When Clicked"
|
||||
description="Step name for 'Play a Sound When Clicked' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePlaySound"
|
||||
/>
|
||||
),
|
||||
image: 'namePlaySound'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite"
|
||||
description="Step name for 'Pick Another Letter Sprite"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePickLetter2"
|
||||
/>
|
||||
),
|
||||
image: 'namePickLetter2'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change color"
|
||||
description="Step name for 'Change color' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateChangeColor"
|
||||
/>
|
||||
),
|
||||
image: 'nameChangeColor'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite & Make It Spin"
|
||||
description="Step name for 'Pick Another Letter Sprite & Make It Spin' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateSpin"
|
||||
/>
|
||||
),
|
||||
image: 'nameSpin'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite & Make It Grow"
|
||||
description="Step name for 'Pick Another Letter Sprite & Make It Grow!' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateGrow"
|
||||
/>
|
||||
),
|
||||
image: 'nameGrow'
|
||||
}, {
|
||||
deckIds: [
|
||||
'add-a-backdrop',
|
||||
'glide-around'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'name'
|
||||
},
|
||||
|
||||
'imagine': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
@@ -337,6 +261,457 @@ export default {
|
||||
urlId: 'imagine'
|
||||
},
|
||||
|
||||
'code-cartoon': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Code a Cartoon"
|
||||
description="Name for the 'Code a Cartoon' how-to"
|
||||
id="gui.howtos.code-cartoon"
|
||||
/>
|
||||
),
|
||||
tags: ['code-cartoon'],
|
||||
requiredProjectId: '331474033',
|
||||
img: libraryCodeCartoon,
|
||||
steps: [{
|
||||
video: 'code-cartoon'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Say Something When You Click the Green Flag"
|
||||
description="Step name for 'Say Something When You Click the Green Flag' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSaySomething"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate a Character When You Click It"
|
||||
description="Step name for 'Animate a Character When You Click It' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonAnimate"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonAnimate'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Select a Different Character"
|
||||
description="Step name for 'Select a Different Character' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSelectDifferentCharacter'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Use a Minus Sign to Get Smaller"
|
||||
description="Step name for 'Use a Minus Sign to Get Smaller' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonUseMinusSign"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonUseMinusSign'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Make a Character Grow and Shrink"
|
||||
description="Step name for 'Make a Character Grow and Shrink' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonGrowShrink"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonGrowShrink'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Select a Different Character"
|
||||
description="Step name for 'Select a Different Character' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter2"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSelectDifferentCharacter2'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Jump Up and Down"
|
||||
description="Step name for 'Jump Up and Down' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonJump"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonJump'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click a Character to Change Scenes"
|
||||
description="Step name for 'Click a Character to Change Scenes' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChangeScenes"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChangeScenes'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Glide Around"
|
||||
description="Step name for 'Glide Around' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonGlideAround"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonGlideAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change Costumes"
|
||||
description="Step name for 'Change Costumes' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChangeCostumes"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChangeCostumes'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose More Characters to Add to Your Cartoon"
|
||||
description="Step name for 'Choose More Characters to Add to Your Cartoon' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChooseMoreCharacters"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChooseMoreCharacters'
|
||||
}, {
|
||||
deckIds: [
|
||||
'Chase-Game',
|
||||
'Tell-A-Story'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'code-cartoon'
|
||||
},
|
||||
|
||||
'say-it-out-loud': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Create Animations That Talk"
|
||||
description="Name for the 'Create Animations That Talk' how-to"
|
||||
id="gui.howtos.say-it-out-loud"
|
||||
/>
|
||||
),
|
||||
img: libraryTXTSpeech,
|
||||
steps: [{
|
||||
video: 'k54n8uwcty',
|
||||
trackingPixel: (
|
||||
<img src="https://code.org/api/hour/begin_scratch_talk.png" />
|
||||
)
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add the Text to Speech blocks"
|
||||
description="Step name for 'Add the Text to Speech blocks' step"
|
||||
id="gui.howtos.say-it-out-loud.step_AddTXTextension"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddExtension'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Say Something"
|
||||
description="Step name for 'Say Something' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSpeech"
|
||||
/>
|
||||
),
|
||||
image: 'speechSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Set a Voice"
|
||||
description="Step name for 'Set a Voice"
|
||||
id="gui.howtos.say-it-out-loud_TXTSetVoice"
|
||||
/>
|
||||
),
|
||||
image: 'speechSetVoice'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Move Around"
|
||||
description="Step name for 'Move Around' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTMove"
|
||||
/>
|
||||
),
|
||||
image: 'speechMoveAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add a Backdrop"
|
||||
description="Step name for 'Add a Backdrop' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add Another Character"
|
||||
description="Step name for 'Add Another Character' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTAddSprite"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Perform a Song"
|
||||
description="Step name for 'Perform a Song' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSong"
|
||||
/>
|
||||
),
|
||||
image: 'speechSong'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change Color"
|
||||
description="Step name for 'Change Color' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTColor"
|
||||
/>
|
||||
),
|
||||
image: 'speechChangeColor'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Spin Around"
|
||||
description="Step name for 'Spin Around"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSpin"
|
||||
/>
|
||||
),
|
||||
image: 'speechSpin'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Grow and Shrink"
|
||||
description="Step name for 'Grow and Shrink' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTGrow"
|
||||
/>
|
||||
),
|
||||
image: 'speechGrowShrink'
|
||||
}, {
|
||||
deckIds: [
|
||||
'animate-a-name',
|
||||
'Make-Music'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'animations-that-talk'
|
||||
},
|
||||
|
||||
'talking': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Talking Tales"
|
||||
description="Name for the 'Talking Tales' how-to"
|
||||
id="gui.howtos.talking"
|
||||
/>
|
||||
),
|
||||
tags: ['talking'],
|
||||
img: libraryTalking,
|
||||
steps: [{
|
||||
video: 'talking'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click to Add the Text-to-Speech Blocks"
|
||||
description="Step name for 'Click to Add the Text-to-Speech Blocks' step"
|
||||
id="gui.howtos.talking.step_talesAddExtension"
|
||||
/>
|
||||
),
|
||||
image: 'talesAddExtension'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Sprite"
|
||||
description="Step name for 'Choose a Sprite' step"
|
||||
id="gui.howtos.talking.step_talesChooseSprite"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Make a Character Speak"
|
||||
description="Step name for 'Make a Character Speak' step"
|
||||
id="gui.howtos.talking.step_talesSaySomething"
|
||||
/>
|
||||
),
|
||||
image: 'talesSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Backdrop"
|
||||
description="Step name for 'Choose a Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click a Character to Go to the Next Backdrop"
|
||||
description="Step name for 'Click a Character to Go to the Next Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesSwitchBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesSwitchBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Sprite"
|
||||
description="Step name for 'Choose Another Sprite' step"
|
||||
id="gui.howtos.talking.step_talesChooseAnotherSprite"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseAnotherSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Move Around"
|
||||
description="Step name for 'Move Around' step"
|
||||
id="gui.howtos.talking.step_talesMoveAround"
|
||||
/>
|
||||
),
|
||||
image: 'talesMoveAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Backdrop"
|
||||
description="Step name for 'Choose Another Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseAnotherBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseAnotherBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate Talking"
|
||||
description="Step name for 'Animate Talking' step"
|
||||
id="gui.howtos.talking.step_talesAnimateTalking"
|
||||
/>
|
||||
),
|
||||
image: 'talesAnimateTalking'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Backdrop"
|
||||
description="Step name for 'Choose Another Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseThirdBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseThirdBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Song to Dance To"
|
||||
description="Step name for 'Choose a Song to Dance To' step"
|
||||
id="gui.howtos.talking.step_talesChooseSound"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseSound'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Dance Moves"
|
||||
description="Step name for 'Dance Moves' step"
|
||||
id="gui.howtos.talking.step_talesDanceMoves"
|
||||
/>
|
||||
),
|
||||
image: 'talesDanceMoves'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Get the Ask and Answer Blocks from the Sensing Category"
|
||||
description="Step name for 'Get the Ask and Answer Blocks from the Sensing Category' step"
|
||||
id="gui.howtos.talking.step_talesAskAnswer"
|
||||
/>
|
||||
),
|
||||
image: 'talesAskAnswer'
|
||||
}, {
|
||||
deckIds: [
|
||||
'Tell-A-Story',
|
||||
'Animate-A-Character'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'talking'
|
||||
},
|
||||
|
||||
'animate-a-name': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate a Name"
|
||||
description="Name for the 'Animate a Name' how-to"
|
||||
id="gui.howtos.animate-a-name.name"
|
||||
/>
|
||||
),
|
||||
img: libraryAnimate,
|
||||
tags: ['animation', 'art', 'spin', 'grow'],
|
||||
steps: [{
|
||||
video: 'animate-a-name'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick a Letter Sprite"
|
||||
description="Step name for 'Pick a Letter Sprite' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePickLetter"
|
||||
/>
|
||||
),
|
||||
image: 'namePickLetter'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Play a Sound When Clicked"
|
||||
description="Step name for 'Play a Sound When Clicked' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePlaySound"
|
||||
/>
|
||||
),
|
||||
image: 'namePlaySound'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite"
|
||||
description="Step name for 'Pick Another Letter Sprite"
|
||||
id="gui.howtos.animate-a-name.step_AnimatePickLetter2"
|
||||
/>
|
||||
),
|
||||
image: 'namePickLetter2'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change color"
|
||||
description="Step name for 'Change color' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateChangeColor"
|
||||
/>
|
||||
),
|
||||
image: 'nameChangeColor'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite & Make It Spin"
|
||||
description="Step name for 'Pick Another Letter Sprite & Make It Spin' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateSpin"
|
||||
/>
|
||||
),
|
||||
image: 'nameSpin'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Pick Another Letter Sprite & Make It Grow"
|
||||
description="Step name for 'Pick Another Letter Sprite & Make It Grow!' step"
|
||||
id="gui.howtos.animate-a-name.step_AnimateGrow"
|
||||
/>
|
||||
),
|
||||
image: 'nameGrow'
|
||||
}, {
|
||||
deckIds: [
|
||||
'add-a-backdrop',
|
||||
'glide-around'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'name'
|
||||
},
|
||||
|
||||
'Make-Music': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
@@ -1075,128 +1450,6 @@ export default {
|
||||
urlId: 'pong'
|
||||
},
|
||||
|
||||
'code-cartoon': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Code a Cartoon"
|
||||
description="Name for the 'Code a Cartoon' how-to"
|
||||
id="gui.howtos.code-cartoon"
|
||||
/>
|
||||
),
|
||||
tags: ['code-cartoon'],
|
||||
requiredProjectId: '331474033',
|
||||
img: libraryCodeCartoon,
|
||||
steps: [{
|
||||
video: 'code-cartoon'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Say Something When You Click the Green Flag"
|
||||
description="Step name for 'Say Something When You Click the Green Flag' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSaySomething"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate a Character When You Click It"
|
||||
description="Step name for 'Animate a Character When You Click It' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonAnimate"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonAnimate'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Select a Different Character"
|
||||
description="Step name for 'Select a Different Character' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSelectDifferentCharacter'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Use a Minus Sign to Get Smaller"
|
||||
description="Step name for 'Use a Minus Sign to Get Smaller' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonUseMinusSign"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonUseMinusSign'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Make a Character Grow and Shrink"
|
||||
description="Step name for 'Make a Character Grow and Shrink' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonGrowShrink"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonGrowShrink'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Select a Different Character"
|
||||
description="Step name for 'Select a Different Character' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonSelectDifferentCharacter2"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonSelectDifferentCharacter2'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Jump Up and Down"
|
||||
description="Step name for 'Jump Up and Down' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonJump"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonJump'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click a Character to Change Scenes"
|
||||
description="Step name for 'Click a Character to Change Scenes' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChangeScenes"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChangeScenes'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Glide Around"
|
||||
description="Step name for 'Glide Around' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonGlideAround"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonGlideAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change Costumes"
|
||||
description="Step name for 'Change Costumes' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChangeCostumes"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChangeCostumes'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose More Characters to Add to Your Cartoon"
|
||||
description="Step name for 'Choose More Characters to Add to Your Cartoon' step"
|
||||
id="gui.howtos.code-cartoon.step_codeCartoonChooseMoreCharacters"
|
||||
/>
|
||||
),
|
||||
image: 'codeCartoonChooseMoreCharacters'
|
||||
}, {
|
||||
deckIds: [
|
||||
'Chase-Game',
|
||||
'Tell-A-Story'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'code-cartoon'
|
||||
},
|
||||
|
||||
'cartoon-network': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
@@ -1356,259 +1609,6 @@ export default {
|
||||
urlId: 'video-sensing'
|
||||
},
|
||||
|
||||
'say-it-out-loud': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Create Animations That Talk"
|
||||
description="Name for the 'Create Animations That Talk' how-to"
|
||||
id="gui.howtos.say-it-out-loud"
|
||||
/>
|
||||
),
|
||||
img: libraryTXTSpeech,
|
||||
steps: [{
|
||||
video: 'k54n8uwcty',
|
||||
trackingPixel: (
|
||||
<img src="https://code.org/api/hour/begin_scratch_talk.png" />
|
||||
)
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add the Text to Speech blocks"
|
||||
description="Step name for 'Add the Text to Speech blocks' step"
|
||||
id="gui.howtos.say-it-out-loud.step_AddTXTextension"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddExtension'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Say Something"
|
||||
description="Step name for 'Say Something' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSpeech"
|
||||
/>
|
||||
),
|
||||
image: 'speechSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Set a Voice"
|
||||
description="Step name for 'Set a Voice"
|
||||
id="gui.howtos.say-it-out-loud_TXTSetVoice"
|
||||
/>
|
||||
),
|
||||
image: 'speechSetVoice'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Move Around"
|
||||
description="Step name for 'Move Around' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTMove"
|
||||
/>
|
||||
),
|
||||
image: 'speechMoveAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add a Backdrop"
|
||||
description="Step name for 'Add a Backdrop' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Add Another Character"
|
||||
description="Step name for 'Add Another Character' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTAddSprite"
|
||||
/>
|
||||
),
|
||||
image: 'speechAddSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Perform a Song"
|
||||
description="Step name for 'Perform a Song' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSong"
|
||||
/>
|
||||
),
|
||||
image: 'speechSong'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Change Color"
|
||||
description="Step name for 'Change Color' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTColor"
|
||||
/>
|
||||
),
|
||||
image: 'speechChangeColor'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Spin Around"
|
||||
description="Step name for 'Spin Around"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTSpin"
|
||||
/>
|
||||
),
|
||||
image: 'speechSpin'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Grow and Shrink"
|
||||
description="Step name for 'Grow and Shrink' step"
|
||||
id="gui.howtos.say-it-out-loud.step_TXTGrow"
|
||||
/>
|
||||
),
|
||||
image: 'speechGrowShrink'
|
||||
}, {
|
||||
deckIds: [
|
||||
'animate-a-name',
|
||||
'Make-Music'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'animations-that-talk'
|
||||
},
|
||||
|
||||
'talking': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Talking Tales"
|
||||
description="Name for the 'Talking Tales' how-to"
|
||||
id="gui.howtos.talking"
|
||||
/>
|
||||
),
|
||||
tags: ['talking'],
|
||||
img: libraryTalking,
|
||||
steps: [{
|
||||
video: 'talking'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click to Add the Text-to-Speech Blocks"
|
||||
description="Step name for 'Click to Add the Text-to-Speech Blocks' step"
|
||||
id="gui.howtos.talking.step_talesAddExtension"
|
||||
/>
|
||||
),
|
||||
image: 'talesAddExtension'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Sprite"
|
||||
description="Step name for 'Choose a Sprite' step"
|
||||
id="gui.howtos.talking.step_talesChooseSprite"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Make a Character Speak"
|
||||
description="Step name for 'Make a Character Speak' step"
|
||||
id="gui.howtos.talking.step_talesSaySomething"
|
||||
/>
|
||||
),
|
||||
image: 'talesSaySomething'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Backdrop"
|
||||
description="Step name for 'Choose a Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Click a Character to Go to the Next Backdrop"
|
||||
description="Step name for 'Click a Character to Go to the Next Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesSwitchBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesSwitchBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Sprite"
|
||||
description="Step name for 'Choose Another Sprite' step"
|
||||
id="gui.howtos.talking.step_talesChooseAnotherSprite"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseAnotherSprite'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Move Around"
|
||||
description="Step name for 'Move Around' step"
|
||||
id="gui.howtos.talking.step_talesMoveAround"
|
||||
/>
|
||||
),
|
||||
image: 'talesMoveAround'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Backdrop"
|
||||
description="Step name for 'Choose Another Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseAnotherBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseAnotherBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Animate Talking"
|
||||
description="Step name for 'Animate Talking' step"
|
||||
id="gui.howtos.talking.step_talesAnimateTalking"
|
||||
/>
|
||||
),
|
||||
image: 'talesAnimateTalking'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose Another Backdrop"
|
||||
description="Step name for 'Choose Another Backdrop' step"
|
||||
id="gui.howtos.talking.step_talesChooseThirdBackdrop"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseThirdBackdrop'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Choose a Song to Dance To"
|
||||
description="Step name for 'Choose a Song to Dance To' step"
|
||||
id="gui.howtos.talking.step_talesChooseSound"
|
||||
/>
|
||||
),
|
||||
image: 'talesChooseSound'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Dance Moves"
|
||||
description="Step name for 'Dance Moves' step"
|
||||
id="gui.howtos.talking.step_talesDanceMoves"
|
||||
/>
|
||||
),
|
||||
image: 'talesDanceMoves'
|
||||
}, {
|
||||
title: (
|
||||
<FormattedMessage
|
||||
defaultMessage="Get the Ask and Answer Blocks from the Sensing Category"
|
||||
description="Step name for 'Get the Ask and Answer Blocks from the Sensing Category' step"
|
||||
id="gui.howtos.talking.step_talesAskAnswer"
|
||||
/>
|
||||
),
|
||||
image: 'talesAskAnswer'
|
||||
}, {
|
||||
deckIds: [
|
||||
'Tell-A-Story',
|
||||
'Animate-A-Character'
|
||||
]
|
||||
}
|
||||
],
|
||||
urlId: 'talking'
|
||||
},
|
||||
|
||||
'add-sprite': {
|
||||
name: (
|
||||
<FormattedMessage
|
||||
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 652 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 802 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 520 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 718 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 487 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 872 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 669 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 487 KiB |
|
After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 541 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 710 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 367 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 283 KiB After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 308 KiB After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 373 KiB After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 157 KiB |