From 6bfcd53e9a105f5d9560fb7f21f48ff12b5b8679 Mon Sep 17 00:00:00 2001
From: Josh Grams
Date: Wed, 4 Jun 2025 04:32:36 -0400
Subject: [PATCH 1/3] add basic transcripting.
* created a lib/ui/content/transcript.js inspired by lib/ui/content/text.js
* updated lib/engine.js to use it (and turned enableTranscript on)
* added a window.saveTranscript() function to lib/ui/browser.js
* added a link to the header and footer (currently commented out?) of
lib/templates/default/+index.html
---
lib/engine.js | 9 +-
lib/templates/html/default/+index.html | 2 +
lib/ui/browser.js | 26 ++++++
lib/ui/content/transcript.js | 119 +++++++++++++++++++++++++
4 files changed, 153 insertions(+), 3 deletions(-)
create mode 100644 lib/ui/content/transcript.js
diff --git a/lib/engine.js b/lib/engine.js
index f45fb6e..0fce079 100644
--- a/lib/engine.js
+++ b/lib/engine.js
@@ -7,6 +7,8 @@
(function() {
'use strict';
+ var toTranscript = require('./ui/content/transcript');
+
// To avoid the need to include any utility libraries when this is
// used in a browser, define some helper functions we'd normally
// rely on libraries for.
@@ -235,12 +237,13 @@
return this;
};
+
DendryEngine.prototype.displayChoices = function() {
var choices = this.getCurrentChoices();
assert(choices);
this.ui.displayChoices(choices);
if (this.state.enableTranscript) {
- this.transcript.push(choices);
+ toTranscript.logChoices(choices, this.transcript);
}
return this;
};
@@ -269,7 +272,7 @@
if (scene.content !== undefined && !restorePage) {
var displayContent = this._makeDisplayContent(scene.content, true);
if (this.state.enableTranscript) {
- this.transcript = this.transcript.concat(displayContent);
+ toTranscript.log(displayContent, this.transcript);
}
this.state.currentContent = this.state.currentContent.concat(displayContent);
this.ui.displayContent(displayContent);
@@ -358,7 +361,7 @@
// Should the transcription feature be part of the UI or the engine?
// Should the transcript-enabling flag be part of the game state, or be part of the UI options?
// let's just say that it's part of the game state. But the actual transcript-writing process is done in the UIs? Because different UIs might want to save states...
- enableTranscript: false,
+ enableTranscript: true,
};
// TODO: transcript
this.transcript = [];
diff --git a/lib/templates/html/default/+index.html b/lib/templates/html/default/+index.html
index edb5ad6..8cbf29d 100644
--- a/lib/templates/html/default/+index.html
+++ b/lib/templates/html/default/+index.html
@@ -26,6 +26,7 @@ {{game.author}}
Save/Load
Options
+ Save Transcript
@@ -224,6 +225,7 @@ {{game.author}}
Save/Load
Quick Save
Quick Load
+ Save Transcript
© 2021 Autumn Chen.
diff --git a/lib/ui/browser.js b/lib/ui/browser.js
index 26df780..10786db 100644
--- a/lib/ui/browser.js
+++ b/lib/ui/browser.js
@@ -620,6 +620,32 @@
save_element.style.display = 'none';
};
+ var _0pad = function(n) {
+ return (n < 10 ? '0' : '') + n;
+ }
+
+ window.saveTranscript = function() {
+ var transcript = window.dendryUI.dendryEngine.transcript;
+ var blob = new Blob([transcript.join('\n\n')]);
+ var data = URL.createObjectURL(blob);
+ var now = new Date();
+ var year = '' + now.getFullYear();
+ var month = _0pad(now.getMonth() + 1);
+ var day = _0pad(now.getDate());
+ var hours = _0pad(now.getHours());
+ var minutes = _0pad(now.getMinutes());
+ var date = year + '-' + month + '-' + day;
+ var time = hours + '-' + minutes;
+ var when = date + '_' + time;
+ var link = document.createElement('a');
+ link.setAttribute('href', data);
+ link.setAttribute('download', 'bee-transcript' + when + '.txt');
+ link.style.visibility = 'hidden';
+ document.body.appendChild(link);
+ link.click();
+ link.remove();
+ };
+
// functions for dealing with options
BrowserUserInterface.prototype.setOption = function(option, toggle) {
diff --git a/lib/ui/content/transcript.js b/lib/ui/content/transcript.js
new file mode 100644
index 0000000..d207845
--- /dev/null
+++ b/lib/ui/content/transcript.js
@@ -0,0 +1,119 @@
+/* dendry
+ * http://github.com/idmillington/dendry
+ *
+ * MIT License
+ */
+/*jshint indent:2 */
+(function() {
+ 'use strict';
+
+ var _repeat = function(str, times) {
+ if (times < 1) return '';
+ if (times % 2 !== 0) return str + _repeat(str, times - 1);
+ var half = _repeat(str, times / 2);
+ return half + half;
+ }
+
+ var _indent = function(spaces, text) {
+ return _repeat(' ', spaces) + text;
+ }
+
+ var _objToText = function(obj) {
+ if (obj.type == null) {
+ return obj;
+ } else {
+ switch (obj.type) {
+ case 'emphasis-1':
+ case 'emphasis-2':
+ case 'hidden':
+ return _lineToText(obj.content);
+ case 'line-break':
+ return '\n';
+
+ // We can't handle elements that require state-dependency.
+ case 'insert':
+ case 'conditional':
+ throw new Error(
+ obj.type + ' should have been evaluated by now.'
+ );
+ }
+ }
+ };
+
+ var _lineToText = function(content) {
+ var output;
+ if (Array.isArray(content)) {
+ output = content.map(_objToText).join('')
+ } else {
+ output = _objToText(content);
+ }
+ return output.replace(/ +/g, ' ').replace(/ *\n */g, '\n');
+ };
+
+ var _paragraphsToText = function(paragraphs) {
+ var text;
+ var result = [];
+ for (var i = 0; i < paragraphs.length; ++i) {
+ var paragraph = paragraphs[i];
+ switch (paragraph.type) {
+ case 'heading':
+ text = _lineToText(paragraph.content).trim();
+ result.push('\n' + text);
+ result.push(_repeat('-', text.length) + '\n');
+ break;
+ case 'paragraph':
+ text = _lineToText(paragraph.content);
+ if (text !== '')
+ result.push(text.trim() + '\n');
+ break;
+ case 'quotation':
+ text = _lineToText(paragraph.content);
+ result.push(
+ _indent(4, text.trim()) +
+ ((i === paragraphs.length - 1 ||
+ paragraphs[i + 1].type !== 'attribution') ? '\n' : '')
+ );
+ break;
+ case 'attribution':
+ text = _lineToText(paragraph.content);
+ result.push(_indent(8, text.trim()) + '\n');
+ break;
+ case 'hrule':
+ result.push('-----\n');
+ break;
+ }
+ }
+ return result.join('\n');
+ };
+
+ var _logParagraphs = function(paragraphs, log) {
+ var text = _paragraphsToText(paragraphs).trimEnd();
+ log.push(text);
+ };
+
+ var _logChoices = function(choices, log) {
+ var out = [];
+ var titleIndent = 4;
+ var subtitleIndent = 7;
+ for (var i = 0; i < choices.length; ++i) {
+ var choice = choices[i];
+ var title = (i + 1) + '. ' + _lineToText(choice.title);
+ if (!choice.canChoose) {
+ title += ' [Unavailable]';
+ }
+ out.push(_indent(titleIndent, title));
+ if (choice.subtitle) {
+ out.push(_indent(subtitleIndent, choice.subtitle));
+ }
+ }
+ log.push(out.join('\n'));
+ };
+
+ module.exports = {
+ indent: _indent,
+ convert: _paragraphsToText,
+ convertLine: _lineToText,
+ log: _logParagraphs,
+ logChoices: _logChoices
+ };
+}());
From 06f13ad2c4264d6cbd0c60c443860bed0ceb4e3c Mon Sep 17 00:00:00 2001
From: Josh Grams
Date: Wed, 4 Jun 2025 05:40:20 -0400
Subject: [PATCH 2/3] Add asterisks for markdown style italic and bold.
---
lib/ui/content/transcript.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/ui/content/transcript.js b/lib/ui/content/transcript.js
index d207845..951aef8 100644
--- a/lib/ui/content/transcript.js
+++ b/lib/ui/content/transcript.js
@@ -24,7 +24,9 @@
} else {
switch (obj.type) {
case 'emphasis-1':
+ return '*' + _lineToText(obj.content) + '*';
case 'emphasis-2':
+ return '**' + _lineToText(obj.content) + '**';
case 'hidden':
return _lineToText(obj.content);
case 'line-break':
From cc55cba425cd1ebb36049d7e6ea98bdeb224ee32 Mon Sep 17 00:00:00 2001
From: Josh Grams
Date: Thu, 5 Jun 2025 08:00:01 -0400
Subject: [PATCH 3/3] added enableTranscript to Options (off by default).
---
lib/engine.js | 2 +-
lib/templates/html/default/+game.js | 18 ++++++++++++++++++
lib/templates/html/default/+index.html | 12 ++++++++++--
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/lib/engine.js b/lib/engine.js
index 0fce079..7b1200d 100644
--- a/lib/engine.js
+++ b/lib/engine.js
@@ -361,7 +361,7 @@
// Should the transcription feature be part of the UI or the engine?
// Should the transcript-enabling flag be part of the game state, or be part of the UI options?
// let's just say that it's part of the game state. But the actual transcript-writing process is done in the UIs? Because different UIs might want to save states...
- enableTranscript: true,
+ enableTranscript: false
};
// TODO: transcript
this.transcript = [];
diff --git a/lib/templates/html/default/+game.js b/lib/templates/html/default/+game.js
index 7f18a50..e67ab32 100644
--- a/lib/templates/html/default/+game.js
+++ b/lib/templates/html/default/+game.js
@@ -46,6 +46,22 @@
save_element.style.display = "none";
};
+ window.enableTranscript = function() {
+ var checkbox = document.getElementById('transcript_checkbox');
+ // Update the engine's transcripting state
+ window.dendryUI.dendryEngine.state.enableTranscript = checkbox.checked;
+ // Show or hide the Save Transcript links
+ var save_links = document.querySelectorAll('.save-transcript');
+ for (var i = 0; i < save_links.length; ++i) {
+ var link = save_links[i];
+ if (checkbox.checked) {
+ link.style.display = '';
+ } else {
+ link.style.display = 'none';
+ }
+ }
+ }
+
window.disableBg = function() {
window.dendryUI.disable_bg = true;
document.body.style.backgroundImage = 'none';
@@ -80,9 +96,11 @@
// populates the checkboxes in the options view
window.populateOptions = function() {
+ var enable_transcript = window.dendryUI.dendryEngine.state.enableTranscript;
var disable_bg = window.dendryUI.disable_bg;
var animate = window.dendryUI.animate;
var animate_bg = window.dendryUI.animate_bg;
+ $('#transcript_checkbox')[0].checked = enable_transcript;
if (disable_bg) {
$('#backgrounds_no')[0].checked = true;
} else {
diff --git a/lib/templates/html/default/+index.html b/lib/templates/html/default/+index.html
index 8cbf29d..d08a4a3 100644
--- a/lib/templates/html/default/+index.html
+++ b/lib/templates/html/default/+index.html
@@ -26,7 +26,7 @@ {{game.author}}
Save/Load
Options
- Save Transcript
+ Save Transcript
@@ -34,6 +34,14 @@ {{game.author}}