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}}