feat: update main chat and system chat UI
This commit is contained in:
@@ -2,11 +2,14 @@ const PREVIEW_RUNTIME_QUERY_KEY = 'appPreviewMode';
|
||||
const PREVIEW_RUNTIME_PARENT_ORIGIN_KEY = 'previewParentOrigin';
|
||||
const PREVIEW_RUNTIME_TOKEN_QUERY_KEY = 'registeredAccessToken';
|
||||
const PREVIEW_RUNTIME_DEVICE_MODE_QUERY_KEY = 'previewDeviceMode';
|
||||
const PREVIEW_RUNTIME_CONSOLE_BRIDGE_EVENT = 'sm-home.preview-runtime.console';
|
||||
const PREVIEW_TARGET_TYPE_QUERY_KEY = 'previewTargetType';
|
||||
const PREVIEW_TARGET_COMPONENT_ID_QUERY_KEY = 'previewComponentId';
|
||||
const PREVIEW_TARGET_SAMPLE_ID_QUERY_KEY = 'previewSampleId';
|
||||
const PREVIEW_RUNTIME_CACHE_RESET_MARKER_KEY = 'work-app.preview-runtime.cache-reset.v1';
|
||||
const PREVIEW_RUNTIME_CACHE_RESET_PARAM_KEY = '__previewRuntimeCacheReset';
|
||||
const PREVIEW_RUNTIME_CLEANUP_TIMEOUT_MS = 2500;
|
||||
const PREVIEW_RUNTIME_NAVIGATION_GRACE_TIMEOUT_MS = 1200;
|
||||
const PREVIEW_RUNTIME_PRESERVED_QUERY_KEYS = [
|
||||
PREVIEW_RUNTIME_QUERY_KEY,
|
||||
PREVIEW_RUNTIME_PARENT_ORIGIN_KEY,
|
||||
@@ -22,6 +25,16 @@ export type PreviewTargetDescriptor =
|
||||
}
|
||||
| null;
|
||||
|
||||
export type PreviewRuntimeConsoleLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
|
||||
|
||||
export type PreviewRuntimeConsoleBridgeMessage = {
|
||||
source: typeof PREVIEW_RUNTIME_CONSOLE_BRIDGE_EVENT;
|
||||
level: PreviewRuntimeConsoleLevel;
|
||||
args: string[];
|
||||
timestamp: string;
|
||||
href: string;
|
||||
};
|
||||
|
||||
export function isPreviewRuntime() {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
@@ -116,8 +129,17 @@ async function clearPreviewRuntimeServiceWorkersAndCaches() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
async function withTimeout<T>(task: Promise<T>, timeoutMs: number, fallbackValue: T) {
|
||||
return await Promise.race([
|
||||
task,
|
||||
new Promise<T>((resolve) => {
|
||||
window.setTimeout(() => resolve(fallbackValue), timeoutMs);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function ensurePreviewRuntimeFreshState() {
|
||||
if (typeof window === 'undefined' || (!isPreviewRuntime() && !isPreviewAppOrigin())) {
|
||||
if (typeof window === 'undefined' || !isPreviewRuntime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,7 +151,7 @@ export async function ensurePreviewRuntimeFreshState() {
|
||||
return;
|
||||
}
|
||||
|
||||
const changed = await clearPreviewRuntimeServiceWorkersAndCaches();
|
||||
const changed = await withTimeout(clearPreviewRuntimeServiceWorkersAndCaches(), PREVIEW_RUNTIME_CLEANUP_TIMEOUT_MS, false);
|
||||
|
||||
if (!changed) {
|
||||
if (resetSearchParam) {
|
||||
@@ -144,8 +166,8 @@ export async function ensurePreviewRuntimeFreshState() {
|
||||
|
||||
writePreviewRuntimeCacheResetMarker(currentLocationKey);
|
||||
window.location.replace(buildPreviewRuntimeCacheResetUrl());
|
||||
await new Promise(() => {
|
||||
// Keep the bootstrap suspended until the browser navigates away.
|
||||
await new Promise<void>((resolve) => {
|
||||
window.setTimeout(resolve, PREVIEW_RUNTIME_NAVIGATION_GRACE_TIMEOUT_MS);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,6 +179,148 @@ export function getPreviewRuntimeParentOrigin() {
|
||||
return new URLSearchParams(window.location.search).get(PREVIEW_RUNTIME_PARENT_ORIGIN_KEY)?.trim() ?? '';
|
||||
}
|
||||
|
||||
function stringifyPreviewRuntimeConsoleArg(value: unknown, seen = new WeakSet<object>()): string {
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
if (typeof value === 'undefined') {
|
||||
return 'undefined';
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (typeof value === 'function') {
|
||||
return `[function ${value.name || 'anonymous'}]`;
|
||||
}
|
||||
|
||||
if (value instanceof Error) {
|
||||
return value.stack || `${value.name}: ${value.message}`;
|
||||
}
|
||||
|
||||
if (value instanceof URL) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
if (seen.has(value)) {
|
||||
return '[circular]';
|
||||
}
|
||||
|
||||
seen.add(value);
|
||||
|
||||
try {
|
||||
return JSON.stringify(
|
||||
value,
|
||||
(_key, nestedValue) => {
|
||||
if (typeof nestedValue === 'bigint') {
|
||||
return nestedValue.toString();
|
||||
}
|
||||
|
||||
if (nestedValue instanceof Error) {
|
||||
return {
|
||||
name: nestedValue.name,
|
||||
message: nestedValue.message,
|
||||
stack: nestedValue.stack,
|
||||
};
|
||||
}
|
||||
|
||||
if (nestedValue instanceof URL) {
|
||||
return nestedValue.toString();
|
||||
}
|
||||
|
||||
if (typeof nestedValue === 'function') {
|
||||
return `[function ${nestedValue.name || 'anonymous'}]`;
|
||||
}
|
||||
|
||||
if (nestedValue && typeof nestedValue === 'object') {
|
||||
if (seen.has(nestedValue)) {
|
||||
return '[circular]';
|
||||
}
|
||||
|
||||
seen.add(nestedValue);
|
||||
}
|
||||
|
||||
return nestedValue;
|
||||
},
|
||||
2,
|
||||
);
|
||||
} catch {
|
||||
return Object.prototype.toString.call(value);
|
||||
}
|
||||
}
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function postPreviewRuntimeConsoleMessage(level: PreviewRuntimeConsoleLevel, args: unknown[]) {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentOrigin = getPreviewRuntimeParentOrigin();
|
||||
|
||||
if (!parentOrigin || window.parent === window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: PreviewRuntimeConsoleBridgeMessage = {
|
||||
source: PREVIEW_RUNTIME_CONSOLE_BRIDGE_EVENT,
|
||||
level,
|
||||
args: args.map((arg) => stringifyPreviewRuntimeConsoleArg(arg)),
|
||||
timestamp: new Date().toISOString(),
|
||||
href: window.location.href,
|
||||
};
|
||||
|
||||
window.parent.postMessage(payload, parentOrigin);
|
||||
}
|
||||
|
||||
export function installPreviewRuntimeConsoleBridge() {
|
||||
if (typeof window === 'undefined' || !isPreviewRuntime()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const consoleMethods: PreviewRuntimeConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug'];
|
||||
const previewConsole = window.console as Console & {
|
||||
__smHomePreviewConsoleBridgeInstalled?: boolean;
|
||||
};
|
||||
|
||||
if (previewConsole.__smHomePreviewConsoleBridgeInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
previewConsole.__smHomePreviewConsoleBridgeInstalled = true;
|
||||
|
||||
consoleMethods.forEach((level) => {
|
||||
const originalMethod = previewConsole[level].bind(previewConsole);
|
||||
|
||||
previewConsole[level] = (...args: unknown[]) => {
|
||||
postPreviewRuntimeConsoleMessage(level, args);
|
||||
originalMethod(...args);
|
||||
};
|
||||
});
|
||||
|
||||
window.addEventListener('error', (event) => {
|
||||
postPreviewRuntimeConsoleMessage('error', [
|
||||
event.message || 'Unknown error',
|
||||
event.filename ? `${event.filename}:${event.lineno}:${event.colno}` : '',
|
||||
event.error ?? '',
|
||||
]);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
postPreviewRuntimeConsoleMessage('error', ['Unhandled promise rejection', event.reason ?? '']);
|
||||
});
|
||||
|
||||
postPreviewRuntimeConsoleMessage('info', ['Preview runtime console bridge connected']);
|
||||
}
|
||||
|
||||
export function readPreviewRuntimeDeviceModeFromUrl(): 'desktop' | 'mobile' | null {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user