diff --git a/HISTORY.md b/HISTORY.md
index 05eed535ad..e7838e780b 100755
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,9 +1,12 @@
# Snap! (BYOB) History
## in development:
+* **New Features:**
+ * new 'text' list conversion selector, recursively joins all text and number leaf elements with spaces in between, filtering out and ignoring whitespace
2023-11-22
* new dev version for v9.0.15
+* lists, blocks, threads: new 'text' list conversion selector, recursively joins all text and number leaf elements with spaces in between, filtering out and ignoring whitespace
## 9.0.14:
* **Notable Fixes:**
diff --git a/snap.html b/snap.html
index b2954624bf..693c1242c1 100755
--- a/snap.html
+++ b/snap.html
@@ -16,13 +16,13 @@
-
-
+
+
-
+
diff --git a/src/blocks.js b/src/blocks.js
index aa69bae20a..1227c14271 100644
--- a/src/blocks.js
+++ b/src/blocks.js
@@ -161,7 +161,7 @@ SVG_Costume, embedMetadataPNG, ThreadManager, snapEquals*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2023-November-07';
+modules.blocks = '2023-November-22';
var SyntaxElementMorph;
var BlockMorph;
@@ -499,6 +499,7 @@ SyntaxElementMorph.prototype.labelParts = {
'shuffled' : ['shuffled'],
'reverse' : ['reverse'],
'~' : null,
+ 'text' : ['text'],
'lines' : ['lines'],
'csv' : ['csv'],
'json' : ['json']
diff --git a/src/lists.js b/src/lists.js
index e4ac092715..3a765e75e0 100644
--- a/src/lists.js
+++ b/src/lists.js
@@ -65,7 +65,7 @@ Context, ZERO, WHITE*/
// Global settings /////////////////////////////////////////////////////
-modules.lists = '2023-July-18';
+modules.lists = '2023-November-22';
var List;
var ListWatcherMorph;
@@ -1081,10 +1081,27 @@ List.prototype.canBeTXT = function () {
List.prototype.asTXT = function () {
// Caution, no error catching!
- // this method assumes that the list.canBeJSON()
+ // this method assumes that the list.canBeTXT()
return this.itemsArray().join('\n');
};
+List.prototype.canBeWords = function () {
+ return this.itemsArray().every(item =>
+ isString(item) ||
+ (typeof item === 'number') ||
+ (item instanceof List && item.canBeWords())
+ );
+};
+
+List.prototype.asWords = function () {
+ // recursively join all leaf items with spaces between.
+ // Caution, no error catching!
+ // this method assumes that the list.canBeWords()
+ return this.itemsArray().map(each =>
+ each instanceof List ? each.asWords() : each.trim()
+ ).filter(word => word.length).join(' ');
+};
+
// List testing
List.prototype.equalTo = function (other) {
diff --git a/src/threads.js b/src/threads.js
index 37aa93e058..d7f12c60eb 100644
--- a/src/threads.js
+++ b/src/threads.js
@@ -65,7 +65,7 @@ StagePickerMorph, CustomBlockDefinition, CommentMorph*/
/*jshint esversion: 11, bitwise: false, evil: true*/
-modules.threads = '2023-November-21';
+modules.threads = '2023-November-22';
var ThreadManager;
var Process;
@@ -2317,6 +2317,14 @@ Process.prototype.reportListAttribute = function (choice, list) {
case 'reverse':
this.assertType(list, 'list');
return list.reversed();
+ case 'text':
+ this.assertType(list, 'list');
+ if (list.canBeWords()) {
+ return list.asWords();
+ }
+ throw new Error(
+ localize('unable to convert to') + ' ' + localize('text')
+ );
case 'lines':
this.assertType(list, 'list');
if (list.canBeTXT()) {