Skip to content

Commit ac17519

Browse files
authored
Merge pull request #846 from steveseguin/beta
Beta
2 parents 545bb14 + 6d59a21 commit ac17519

165 files changed

Lines changed: 23655 additions & 6913 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,23 @@ thirdparty/models/onnx-community/Kokoro-82M-v1.0-ONNX/
2323
tests/cohost-customopenai*.playwright.js
2424

2525
# Generated benchmark/stress outputs
26+
artifacts/
2627
tests/artifacts/
2728
tmp/
2829
debug.log
2930
docs/md/
3031
.tmp_*
32+
.codex-tmp/
33+
.codex-tmp-*.diff
34+
35+
# Local TTS service repo clones
36+
.codex-tmp/local-tts-services/
37+
local-tts-services/
38+
chatterbox-tts-api/
39+
Chatterbox-TTS-Server/
40+
F5-TTS/
41+
F5-TTS_server/
42+
GPT-SoVITS/
43+
MisoTTS/
44+
openedai-speech/
45+
Qwen3-TTS/

AGENTS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,14 @@ Sample payloads based on the fake test data in [background.js](./background.js):
143143
- When replying to Steve, prefer plain, everyday language over jargon.
144144
- Keep explanations direct and practical; explain technical terms briefly when they matter.
145145
146+
## Git Safety
147+
148+
- VERY IMPORTANT: Never use `git restore`, `git revert`, or any revert/restore operation unless Steve explicitly asks for that exact action.
149+
146150
## Git Push Contract
147151
148152
- VERY IMPORTANT: When Steve says `push`, treat it as an instruction to push to `beta`.
153+
- VERY IMPORTANT: Always push all current changes. Do not stash, exclude, unstage, or preserve local changes outside the push unless Steve explicitly says to leave something unpushed.
149154
- VERY IMPORTANT: Do it serially in this exact order only: `git add -A`, `git commit` (use `--allow-empty` if needed), `git pull --rebase origin beta`, `git push origin beta`.
150155
- VERY IMPORTANT: Do not parallelize any git commands in that flow.
151156
- VERY IMPORTANT: Do not add extra git inspection commands unless Steve explicitly asks for them.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,8 @@ Social Stream Ninja now includes Kokoro TTS, a high-quality browser-based text-t
890890

891891
**Note:** Kokoro requires a powerful computer and may be slow to generate responses.
892892

893+
For browser overlays, Kokoro's runtime can be forced with `&kokorodevice=wasm&kokorodtype=q8`. macOS browser overlays default to this Kokoro-only WASM path to avoid WebGPU audio distortion while leaving browser graphics acceleration enabled.
894+
893895
##### Google Cloud TTS
894896
For professional-quality voices, Google Cloud Text to Speech API integration is available:
895897

actions.html

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,34 @@ <h2>Session ID Required</h2>
344344
lastError: null,
345345
state: 'idle'
346346
};
347-
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
347+
var audioContext = null;
348+
function ensureAudioContext() {
349+
if (!audioContext && (window.AudioContext || window.webkitAudioContext)) {
350+
var AudioContextCtor = window.AudioContext || window.webkitAudioContext;
351+
audioContext = new AudioContextCtor();
352+
}
353+
return audioContext;
354+
}
355+
async function resumeAudioContext() {
356+
var ctx = ensureAudioContext();
357+
if (!ctx) {
358+
return null;
359+
}
360+
if (ctx.state === 'suspended') {
361+
await ctx.resume();
362+
}
363+
return ctx;
364+
}
365+
function attachAudioUnlockListeners() {
366+
var handler = function() {
367+
resumeAudioContext().catch(function(error) {
368+
console.error('Failed to resume AudioContext:', error);
369+
});
370+
};
371+
['pointerdown', 'mousedown', 'touchstart', 'keydown', 'click'].forEach(function(eventName) {
372+
document.addEventListener(eventName, handler, { once: true });
373+
});
374+
}
348375

349376
// Multi-layer overlay system references
350377
var overlayLayers = {
@@ -1693,15 +1720,13 @@ <h2>Session ID Required</h2>
16931720
}
16941721

16951722
// Function to play audio
1696-
async function playAudio(audioUrl, volume) {
1697-
try {
1698-
if (audioContext.state === 'suspended') {
1699-
await audioContext.resume();
1700-
}
1701-
1702-
const audio = new Audio(audioUrl);
1703-
audio.volume = volume !== undefined ? parseFloat(volume) : globalVolume;
1704-
await audio.play();
1723+
async function playAudio(audioUrl, volume) {
1724+
try {
1725+
await resumeAudioContext();
1726+
1727+
const audio = new Audio(audioUrl);
1728+
audio.volume = volume !== undefined ? parseFloat(volume) : globalVolume;
1729+
await audio.play();
17051730
console.log("Playing audio:", audioUrl);
17061731
} catch (e) {
17071732
console.error("Error playing audio:", e);
@@ -1731,16 +1756,8 @@ <h2>Session ID Required</h2>
17311756
}
17321757
});
17331758

1734-
// Handle audio context for browsers that require user interaction
1735-
document.addEventListener('click', () => {
1736-
if (audioContext.state === 'suspended') {
1737-
audioContext.resume().then(() => {
1738-
console.log('AudioContext resumed successfully');
1739-
}).catch(error => {
1740-
console.error('Failed to resume AudioContext:', error);
1741-
});
1742-
}
1743-
});
1759+
// Handle audio context for browsers that require user interaction
1760+
attachAudioUnlockListeners();
17441761

17451762
function testAction(actionData) {
17461763
processInput(actionData);

actions/EventFlowEditor.js

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5760,29 +5760,51 @@ class EventFlowEditor {
57605760
});
57615761
}
57625762

5763-
// Add upload button handlers
5764-
const uploadMediaBtn = document.getElementById('uploadMediaBtn');
5765-
if (uploadMediaBtn) {
5766-
uploadMediaBtn.addEventListener('click', () => {
5767-
const popup = window.open('https://fileuploads.socialstream.ninja/popup/upload', 'uploadMedia', 'width=640,height=640');
5768-
5769-
window.addEventListener('message', function handleMessage(event) {
5770-
if (event.origin !== 'https://fileuploads.socialstream.ninja') return;
5771-
5772-
if (event.data && event.data.type === 'media-uploaded') {
5773-
const mediaUrlInput = document.getElementById('prop-mediaUrl');
5774-
if (mediaUrlInput) {
5775-
mediaUrlInput.value = event.data.url;
5776-
mediaUrlInput.dispatchEvent(new Event('input', { bubbles: true }));
5777-
nodeData.config.mediaUrl = event.data.url;
5778-
this.markUnsavedChanges(true);
5779-
this.renderNodeOnCanvas(nodeData.id);
5780-
}
5781-
window.removeEventListener('message', handleMessage);
5782-
}
5783-
}.bind(this));
5784-
});
5785-
}
5763+
const openNodeMediaUpload = (popupName, inputId, configKey) => {
5764+
const applyUploadUrl = (uploadedUrl) => {
5765+
const input = document.getElementById(inputId);
5766+
if (input) {
5767+
input.value = uploadedUrl;
5768+
input.dispatchEvent(new Event('input', { bubbles: true }));
5769+
input.dispatchEvent(new Event('change', { bubbles: true }));
5770+
nodeData.config[configKey] = uploadedUrl;
5771+
this.markUnsavedChanges(true);
5772+
this.renderNodeOnCanvas(nodeData.id);
5773+
}
5774+
};
5775+
5776+
if (window.ninjafy && typeof window.ninjafy.startMediaUpload === 'function') {
5777+
window.ninjafy.startMediaUpload({ popupName })
5778+
.then((result) => {
5779+
if (result && result.success && result.url) {
5780+
applyUploadUrl(result.url);
5781+
}
5782+
})
5783+
.catch((error) => {
5784+
console.warn('Hosted media upload failed:', error && error.message ? error.message : error);
5785+
});
5786+
return;
5787+
}
5788+
5789+
window.open('https://fileuploads.socialstream.ninja/popup/upload', popupName, 'width=640,height=640');
5790+
5791+
window.addEventListener('message', function handleMessage(event) {
5792+
if (event.origin !== 'https://fileuploads.socialstream.ninja') return;
5793+
5794+
if (event.data && event.data.type === 'media-uploaded') {
5795+
applyUploadUrl(event.data.url);
5796+
window.removeEventListener('message', handleMessage);
5797+
}
5798+
});
5799+
};
5800+
5801+
// Add upload button handlers
5802+
const uploadMediaBtn = document.getElementById('uploadMediaBtn');
5803+
if (uploadMediaBtn) {
5804+
uploadMediaBtn.addEventListener('click', () => {
5805+
openNodeMediaUpload('uploadMedia', 'prop-mediaUrl', 'mediaUrl');
5806+
});
5807+
}
57865808

57875809
// Handle removeType dropdown changes
57885810
const removeTypeSelect = document.getElementById('prop-removeType');
@@ -5795,28 +5817,12 @@ class EventFlowEditor {
57955817
});
57965818
}
57975819

5798-
const uploadAudioBtn = document.getElementById('uploadAudioBtn');
5799-
if (uploadAudioBtn) {
5800-
uploadAudioBtn.addEventListener('click', () => {
5801-
const popup = window.open('https://fileuploads.socialstream.ninja/popup/upload', 'uploadAudio', 'width=640,height=640');
5802-
5803-
window.addEventListener('message', function handleMessage(event) {
5804-
if (event.origin !== 'https://fileuploads.socialstream.ninja') return;
5805-
5806-
if (event.data && event.data.type === 'media-uploaded') {
5807-
const audioUrlInput = document.getElementById('prop-audioUrl');
5808-
if (audioUrlInput) {
5809-
audioUrlInput.value = event.data.url;
5810-
audioUrlInput.dispatchEvent(new Event('input', { bubbles: true }));
5811-
nodeData.config.audioUrl = event.data.url;
5812-
this.markUnsavedChanges(true);
5813-
this.renderNodeOnCanvas(nodeData.id);
5814-
}
5815-
window.removeEventListener('message', handleMessage);
5816-
}
5817-
}.bind(this));
5818-
});
5819-
}
5820+
const uploadAudioBtn = document.getElementById('uploadAudioBtn');
5821+
if (uploadAudioBtn) {
5822+
uploadAudioBtn.addEventListener('click', () => {
5823+
openNodeMediaUpload('uploadAudio', 'prop-audioUrl', 'audioUrl');
5824+
});
5825+
}
58205826
}
58215827

58225828
renderNodeOnCanvas(nodeId) {

0 commit comments

Comments
 (0)