GitHub Action
Sync ling-space changes (filtered) from commit 127300e
b931367
raw
history blame
11.2 kB
<script>
(function () {
console.info("Space App JS executing...");
// --- Logger utility (structured, levelled, timestamped) ---
const SpaceApp = {
hasBooted: false,
fnUnloadLastTab: null,
toastInfo: function(...args) {
// use toastify
window.Toastify({
text: args.join(' '),
duration: 3000,
gravity: "top",
position: "right",
backgroundColor: "green",
}).showToast();
console.info("TOAST_INFO", ...args);
},
toastError: function(...args) {
window.Toastify({
text: args.join(' '),
duration: 5000,
gravity: "top",
position: "right",
backgroundColor: "red",
}).showToast();
console.error("TOAST_ERROR", ...args);
},
unloadLastTab: function() {
if (this.fnUnloadLastTab) {
this.fnUnloadLastTab();
this.fnUnloadLastTab = null;
}
},
TextGeneratorTab: (function () {
return {
init: function () {
console.info("TextGeneratorTab initialized.");
},
toggle: function () {
// Placeholder for future functionality
SpaceApp.toastInfo("TextGeneratorTab toggled.");
SpaceApp.unloadLastTab();
}
};
})(),
WebGeneratorTab: (function () {
return {
init: function () {
console.info("WebGeneratorTab initialized.");
},
toggle: function () {
// Placeholder for future functionality
SpaceApp.toastInfo("WebGeneratorTab toggled.");
SpaceApp.unloadLastTab();
}
};
})(),
WritingAssistantTab: (function () {
return {
init: function () {
console.info("WritingAssistantTab initialized.");
},
toggle: function () {
// Placeholder for future functionality
SpaceApp.toastInfo("WritingAssistantTab toggled.");
SpaceApp.unloadLastTab();
// 找到 id = 'writing-editor' 的元素,设置操作快捷键。
// Tab - 'btn-action-accept-flow' 按钮点击
// Shift + Tab - 'btn-action-change-flow' 按钮点击
// Cmd/Ctrl + Enter - 'btn-action-create-paragraph' 按钮点击
// Shift + Enter - 'btn-action-change-paragraph' 按钮点击
const editor = document.getElementById('writing-editor');
if (editor) {
// 如果已经设置过监听器,就不重复设置
if (editor.getAttribute('data-listener-set') === 'true') {
console.info("Writing Assistant Editor already has listeners set.");
return;
}
const idToEventFilterList = [
['btn-action-change-flow', (e) => e.shiftKey && e.key === 'Tab'],
['btn-action-accept-flow', (e) => !e.shiftKey && e.key === 'Tab'],
['btn-action-change-paragraph', (e) => e.shiftKey && e.key === 'Enter'],
['btn-action-create-paragraph', (e) => (e.metaKey || e.ctrlKey) && e.key === 'Enter'],
]
editor.addEventListener('keydown', (e) => {
for (const [buttonId, filterFn] of idToEventFilterList) {
if (filterFn(e)) {
e.preventDefault();
const button = document.getElementById(buttonId);
if (button) {
SpaceApp.toastInfo(`Writing Assistant: Triggered action for ${buttonId}`);
button.click();
} else {
SpaceApp.toastError(`Writing Assistant: Button with id ${buttonId} not found.`);
}
break; // Only trigger one action per keydown
}
}
});
// 对已经设置过监听器的 editor,不要重复设置
editor.setAttribute('data-listener-set', 'true');
}
}
};
})(),
registerEventListeners: function() {
const listeners = {
'tabSelect.chat': () => SpaceApp.TextGeneratorTab.toggle(),
'tabSelect.code': () => SpaceApp.WebGeneratorTab.toggle(),
'tabSelect.writing': () => SpaceApp.WritingAssistantTab.toggle(),
};
for (const [event, handler] of Object.entries(listeners)) {
window.addEventListener(event, handler);
console.info(`Registered event listener for ${event}`);
}
// type_filter, handler
// Listen for messages {type: string, payload: object}
const messages = {
'iframeError': (data) => {
// 1. Show visual toast
SpaceApp.toastError("Iframe Error Detected:", JSON.stringify(data));
// 2. Propagate to Backend via Gradio
const jsErrorChannel = document.querySelector('.js_error_channel textarea');
if (jsErrorChannel) {
// Format as string for backend
const errorMessage = data.message || JSON.stringify(data);
const timestamp = new Date().toLocaleTimeString();
jsErrorChannel.value = `[${timestamp}] ${errorMessage}\nStack: ${data.stack || 'N/A'}`;
jsErrorChannel.dispatchEvent(new Event('input', { bubbles: true }));
} else {
console.warn("Gradio channel '.js_error_channel' not found. Backend logging skipped.");
}
},
}
window.addEventListener('message', (event) => {
const {type, payload} = event.data || {};
if (type && messages[type]) {
messages[type](payload);
}
});
},
};
// Expose bootstrap globally so external scripts can call it after page load
window.bootApp = function () {
if (SpaceApp.hasBooted) {
console.warn("Space App has already booted. Ignoring duplicate boot call.");
return;
}
SpaceApp.hasBooted = true;
SpaceApp.toastInfo("Booting Space App...");
SpaceApp.registerEventListeners();
SpaceApp.TextGeneratorTab.init();
SpaceApp.WebGeneratorTab.init();
SpaceApp.WritingAssistantTab.init();
SpaceApp.toastInfo("Space App booted successfully.");
};
window.SpaceApp = SpaceApp;
console.info("Space App JS execution completed.");
})();
</script>
<script>
// 注册一个 appStart 事件监听器,在 Gradio app 启动时调用 bootApp
window.addEventListener('appStart', function () {
console.info("Gradio appStart event detected. Booting Space App...");
if (typeof window.bootApp === "function") {
window.bootApp();
} else {
console.error("bootApp function is not defined.");
}
});
</script>
<script>
(function() {
const ErrorPoller = {
lastErrorMessage: '',
startPolling: () => {
},
stopPolling: () => {
}
}
console.info('Iframe Error Poller Initialized');
let lastErrorMessage = '';
// Function to start polling for errors in the iframe
function startPolling() {
console.info('Starting to poll iframe for errors...');
setInterval(() => {
// Re-select the iframe on every interval tick because Gradio replaces it.
const previewIframe = document.querySelector('iframe[srcdoc]');
if (!previewIframe) return;
try {
const iframeDoc = previewIframe.contentWindow.document;
const errorContainer = iframeDoc.querySelector('#global-error');
const jsErrorChannel = document.querySelector('.js_error_channel textarea');
if (!jsErrorChannel) {
console.error("Gradio channel '.js_error_channel' not found.");
return;
}
if (errorContainer && errorContainer.style.display !== 'none') {
const currentErrorMessage = errorContainer.innerText;
// If the error message is new, send it to the backend
if (currentErrorMessage && currentErrorMessage !== lastErrorMessage) {
lastErrorMessage = currentErrorMessage;
// Set the value on the hidden Gradio textbox
jsErrorChannel.value = currentErrorMessage;
// Dispatch an 'input' event to notify Gradio of the change
jsErrorChannel.dispatchEvent(new Event('input', { bubbles: true }));
}
} else {
// If the error container is not visible or doesn't exist, and there was a previous error, clear it.
if (lastErrorMessage) {
lastErrorMessage = '';
jsErrorChannel.value = '';
jsErrorChannel.dispatchEvent(new Event('input', { bubbles: true }));
}
}
} catch (e) {
// This can happen due to cross-origin restrictions if the iframe src changes.
// We can ignore it for srcdoc iframes.
}
}, 1000);
}
// 在页面加载之后设置
document.addEventListener('readystatechange', (event) => {
if (document.readyState === 'complete') {
setTimeout(startPolling, 500);
}
});
console.info('Iframe Error Poller Script Loaded');
})();
</script>
<script>
// 我们当前存活在一个 iframe 里面。观察 document, window, window.parent。
console.log('document is', document);
console.log('window is ', window);
console.log('window parent is ', window.parent);
</script>