diff --git a/index.html b/index.html
index cee6bcf..935f224 100644
--- a/index.html
+++ b/index.html
@@ -1149,12 +1149,13 @@
-
-
-
-
-
Select and copy the link above
-
+
+
+
+
+
+
+
Select and copy the link above
diff --git a/play.html b/play.html
index b78ab93..94f5aa5 100644
--- a/play.html
+++ b/play.html
@@ -16,7 +16,7 @@
-
+
diff --git a/server/src/server.ts b/server/src/server.ts
index b9860b9..c16d0f5 100644
--- a/server/src/server.ts
+++ b/server/src/server.ts
@@ -220,8 +220,10 @@ app.get('/s/:code', async (req, res) => {
const imageUrl = origin ? `${origin}/wariosynthlogo.png` : '/wariosynthlogo.png';
const sharedTitle = (payload.title || '').trim();
- const ogTitle = sharedTitle || 'WARIO SYNTH';
- const ogDescription = 'I made this in Wario Synth, click to listen or generate your own';
+ const ogTitle = sharedTitle ? `${sharedTitle} - WARIO SYNTH` : 'WARIO SYNTH';
+ const ogDescription = sharedTitle
+ ? `I made ${sharedTitle} game boy version. Click to listen or generate your own, made in Wario Synth by @b1rdmania.`
+ : 'Turn any song into retro game console music. Made by @b1rdmania.';
const html = `
@@ -239,7 +241,7 @@ app.get('/s/:code', async (req, res) => {
-
+
diff --git a/src/main.ts b/src/main.ts
index 8108e86..b9ea817 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -58,6 +58,7 @@ class MotifApp {
private iosAudioState!: HTMLElement;
private copyLinkBtn!: HTMLButtonElement;
+ private shareToXBtn!: HTMLButtonElement;
private shareLinkBox!: HTMLElement;
private shareLinkInput!: HTMLInputElement;
@@ -135,6 +136,7 @@ class MotifApp {
this.motifDuration = document.getElementById('motifDuration')!;
this.copyLinkBtn = document.getElementById('copyLinkBtn') as HTMLButtonElement;
+ this.shareToXBtn = document.getElementById('shareToXBtn') as HTMLButtonElement;
this.shareLinkBox = document.getElementById('shareLinkBox') as HTMLElement;
this.shareLinkInput = document.getElementById('shareLinkInput') as HTMLInputElement;
@@ -184,6 +186,7 @@ class MotifApp {
this.motifProgressBar.addEventListener('change', seekHandler);
this.copyLinkBtn.addEventListener('click', () => void this.handleCopyLink());
+ this.shareToXBtn.addEventListener('click', () => void this.handleShareToX());
// Embed snippet copy (may be disabled / not-live)
this.copyEmbedBtn?.addEventListener('click', () => void this.copyEmbedSnippet());
@@ -744,6 +747,47 @@ class MotifApp {
}
}
+ private async handleShareToX(): Promise {
+ if (!this.hasGenerated) return;
+ const result = this.searchResults[this.selectedResultIndex];
+ if (!result?.midiUrl) return;
+
+ const title = this.cleanSongTitle(result.title || 'a song');
+
+ // Get share URL (try short link first)
+ let shareUrl: string | null = null;
+ try {
+ let payload: any = null;
+ if (result.source === 'bitmidi') {
+ const m = String(result.midiUrl).match(/\/uploads\/(\d+)\.mid/i);
+ if (m?.[1]) payload = { src: 'bitmidi', id: m[1], title: result.title };
+ }
+ if (!payload) payload = { u: result.midiUrl, title: result.title };
+
+ const resp = await fetch('/api/share', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ });
+ if (resp.ok) {
+ const data = await resp.json();
+ if (data?.url) shareUrl = `${window.location.origin}${data.url}`;
+ }
+ } catch {
+ // Fall through
+ }
+
+ if (!shareUrl) {
+ shareUrl = window.location.href;
+ }
+
+ // Compose tweet text
+ const tweetText = `I made ${title} game boy version. Click to listen or generate your own, made in Wario Synth by @b1rdmania`;
+ const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(shareUrl)}`;
+
+ window.open(twitterUrl, '_blank', 'width=550,height=420');
+ }
+
private setState(state: 'idle' | 'results' | 'selected' | 'generated'): void {
// results - keep visible in all states except idle so user can pick a different source
const hasResults = state === 'results' || state === 'selected' || state === 'generated';
@@ -754,9 +798,11 @@ class MotifApp {
const hasSelection = state === 'selected' || state === 'generated';
this.playerSection.classList.toggle('visible', hasSelection);
- // share link only after generation
+ // share buttons only after generation
this.copyLinkBtn.style.display = state === 'generated' ? 'inline-block' : 'none';
this.copyLinkBtn.disabled = !(state === 'generated' && this.hasGenerated);
+ this.shareToXBtn.style.display = state === 'generated' ? 'inline-block' : 'none';
+ this.shareToXBtn.disabled = !(state === 'generated' && this.hasGenerated);
// Dim results when selected/generated, but keep them interactive
this.resultsSection.classList.toggle('collapsed', state === 'selected' || state === 'generated');