diff --git a/app.js b/app.js
index a5b9a9e..0c8a042 100644
--- a/app.js
+++ b/app.js
@@ -1024,9 +1024,66 @@ window.addEventListener('player-left', (/** @type {CustomEvent} */ event) => {
console.log('Player left:', event.detail);
});
+// === SESSION EXPORT ===
+/** @type {{ ts: number, speaker: string, text: string }[]} */
+const sessionLog = [];
+const sessionStart = Date.now();
+
+/**
+ * Appends an entry to the in-memory session log.
+ * @param {string} speaker
+ * @param {string} text
+ */
+function logMessage(speaker, text) {
+ sessionLog.push({ ts: Date.now(), speaker, text });
+}
+
+/**
+ * Formats the session log as Markdown and triggers a browser download.
+ */
+function exportSessionAsMarkdown() {
+ const startStr = new Date(sessionStart).toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
+ const lines = [
+ '# Nexus Session Export',
+ '',
+ `**Session started:** ${startStr}`,
+ `**Messages:** ${sessionLog.length}`,
+ '',
+ '---',
+ '',
+ ];
+
+ for (const entry of sessionLog) {
+ const timeStr = new Date(entry.ts).toISOString().replace('T', ' ').slice(0, 19) + ' UTC';
+ lines.push(`### ${entry.speaker} — ${timeStr}`);
+ lines.push('');
+ lines.push(entry.text);
+ lines.push('');
+ }
+
+ if (sessionLog.length === 0) {
+ lines.push('*No messages recorded this session.*');
+ lines.push('');
+ }
+
+ const blob = new Blob([lines.join('\n')], { type: 'text/markdown' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `nexus-session-${new Date(sessionStart).toISOString().slice(0, 10)}.md`;
+ a.click();
+ URL.revokeObjectURL(url);
+}
+
+const exportBtn = document.getElementById('export-session');
+if (exportBtn) {
+ exportBtn.addEventListener('click', exportSessionAsMarkdown);
+}
+
window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => {
console.log('Chat message:', event.detail);
if (typeof event.detail?.text === 'string') {
+ logMessage(event.detail.speaker || 'TIMMY', event.detail.text);
showTimmySpeech(event.detail.text);
if (event.detail.text.toLowerCase().includes('sovereignty')) {
triggerSovereigntyEasterEgg();
diff --git a/index.html b/index.html
index 69d6b65..73dd69a 100644
--- a/index.html
+++ b/index.html
@@ -33,6 +33,9 @@
+
diff --git a/style.css b/style.css
index 8ccbc2d..3dc0e04 100644
--- a/style.css
+++ b/style.css
@@ -61,6 +61,25 @@ canvas {
margin-left: 8px;
}
+/* === SESSION EXPORT === */
+#export-session {
+ margin-left: 8px;
+ background-color: var(--color-secondary);
+ color: var(--color-text);
+ padding: 4px 8px;
+ border: none;
+ border-radius: 4px;
+ font-size: 12px;
+ cursor: pointer;
+ font-family: var(--font-body);
+ transition: background-color 0.2s ease;
+}
+
+#export-session:hover {
+ background-color: var(--color-primary);
+ color: var(--color-bg);
+}
+
.collision-box {
outline: 2px solid red;
outline-offset: 2px;