Files
qiming/qiming-vite-plugin-design-mode/packages/client-shared/src/messages.ts
2026-06-01 13:43:09 +08:00

341 lines
7.8 KiB
TypeScript

// Message types for iframe ↔ parent window communication
export interface SourceInfo {
fileName: string;
lineNumber: number;
columnNumber: number;
elementType?: string;
componentName?: string;
functionName?: string;
elementId?: string;
importPath?: string;
isUIComponent?: boolean; // Whether this is a UI-kit component (e.g. under components/ui)
}
export interface ElementInfo {
tagName: string;
className: string;
textContent: string;
sourceInfo: SourceInfo;
isStaticText: boolean;
isStaticClass?: boolean; // Whether className is a pure static string (editable)
componentName?: string;
componentPath?: string;
props?: Record<string, string>;
hierarchy?: {
tagName: string;
componentName?: string;
fileName?: string;
}[];
}
// Message validation types
export interface MessageValidationResult {
isValid: boolean;
errors?: string[];
}
export interface MessageValidator {
validate: (message: any) => MessageValidationResult;
}
// Bridge Ready Message
export interface BridgeReadyMessage {
type: 'BRIDGE_READY';
payload: {
timestamp: number;
environment: 'iframe' | 'main';
};
timestamp?: number;
requestId?: string;
}
// iframe → Parent messages
export interface ElementSelectedMessage {
type: 'ELEMENT_SELECTED';
payload: {
elementInfo: ElementInfo;
};
requestId?: string;
timestamp?: number;
}
export interface ElementDeselectedMessage {
type: 'ELEMENT_DESELECTED';
requestId?: string;
timestamp?: number;
payload?: null; // Added optional payload to match usage
}
export interface ContentUpdatedMessage {
type: 'CONTENT_UPDATED';
payload: {
sourceInfo: SourceInfo;
oldValue: string;
newValue: string;
realtime?: boolean; // Whether this is a realtime (throttled) update
};
requestId?: string;
timestamp?: number;
}
export interface ContentUpdatedCallbackMessage {
type: 'CONTENT_UPDATED_CALLBACK';
payload: {
sourceInfo: SourceInfo;
oldValue: string;
newValue: string;
realtime?: boolean; // Whether this is a realtime (throttled) update
};
requestId?: string;
timestamp?: number;
}
export interface StyleUpdatedMessage {
type: 'STYLE_UPDATED';
payload: {
sourceInfo: SourceInfo;
oldClass: string;
newClass: string;
};
requestId?: string;
timestamp?: number;
}
export interface DesignModeChangedMessage {
type: 'DESIGN_MODE_CHANGED';
enabled: boolean;
requestId?: string;
timestamp?: number;
}
// Parent → iframe messages
export interface ToggleDesignModeMessage {
type: 'TOGGLE_DESIGN_MODE';
enabled: boolean;
requestId?: string;
timestamp?: number;
}
export interface UpdateStyleMessage {
type: 'UPDATE_STYLE';
payload: {
sourceInfo: SourceInfo;
newClass: string;
persist?: boolean;
};
requestId?: string;
timestamp?: number;
enabled?: boolean; // Added to fix TS error where enabled was accessed on this type (though likely a bug in usage, adding optional property avoids strict error if discriminated union fails)
}
export interface UpdateContentMessage {
type: 'UPDATE_CONTENT';
payload: {
sourceInfo: SourceInfo;
newContent: string;
persist?: boolean;
};
requestId?: string;
timestamp?: number;
}
export interface BatchUpdateItem {
type: 'style' | 'content';
sourceInfo: SourceInfo;
newValue: string;
originalValue?: string;
}
export interface BatchUpdateMessage {
type: 'BATCH_UPDATE';
payload: {
updates: BatchUpdateItem[];
};
requestId?: string;
timestamp?: number;
}
// Add to Chat Message
export interface AddToChatMessage {
type: 'ADD_TO_CHAT';
payload: {
content: string;
context?: {
elementInfo?: ElementInfo;
sourceInfo?: SourceInfo;
};
};
requestId?: string;
timestamp?: number;
}
// Copy Element Message
export interface CopyElementMessage {
type: 'COPY_ELEMENT';
payload: {
elementInfo: {
tagName: string;
className: string;
content: string;
sourceInfo?: SourceInfo;
};
textContent: string; // JSON string for clipboard
success: boolean; // Whether copy succeeded
error?: string; // Error message when copy failed
};
requestId?: string;
timestamp?: number;
}
// Error message
export interface ErrorMessage {
type: 'ERROR';
payload: {
code: string;
message: string;
details?: any;
};
requestId?: string;
timestamp?: number;
}
// Acknowledgement message
export interface AcknowledgementMessage {
type: 'ACKNOWLEDGEMENT';
payload: {
messageType: string;
};
requestId: string; // Response MUST have requestId
timestamp?: number;
}
// Heartbeat and health-check messages
export interface HeartbeatMessage {
type: 'HEARTBEAT';
payload?: {
timestamp: number;
};
timestamp?: number;
}
export interface HealthCheckMessage {
type: 'HEALTH_CHECK';
requestId?: string; // Made optional for sender
timestamp?: number; // Made optional for sender
}
export interface HealthCheckResponseMessage {
type: 'HEALTH_CHECK_RESPONSE';
payload: {
status: 'healthy' | 'unhealthy' | 'degraded' | 'connecting' | 'unnecessary';
version?: string;
uptime?: number;
message?: string;
response?: any;
error?: string;
isIframe?: boolean;
isConnected?: boolean;
origin?: string;
userAgent?: string;
location?: string;
};
requestId: string; // Response MUST have requestId
timestamp?: number;
}
// Union types
export type IframeToParentMessage =
| ElementSelectedMessage
| ElementDeselectedMessage
| ContentUpdatedMessage
| StyleUpdatedMessage
| DesignModeChangedMessage
| ErrorMessage
| AcknowledgementMessage
| HeartbeatMessage
| HealthCheckResponseMessage
| BridgeReadyMessage
| AddToChatMessage
| CopyElementMessage
| ContentUpdatedCallbackMessage;
export type ParentToIframeMessage =
| ToggleDesignModeMessage
| UpdateStyleMessage
| UpdateContentMessage
| BatchUpdateMessage
| HealthCheckMessage
| HeartbeatMessage; // Added HeartbeatMessage here too for bidirectional support
export type DesignModeMessage = IframeToParentMessage | ParentToIframeMessage;
// Promise-based request/response types
export type RequestMessage = ParentToIframeMessage & {
requestId: string;
timestamp: number;
};
export type ResponseMessage = IframeToParentMessage & {
requestId: string;
success?: boolean;
error?: string;
timestamp: number;
};
export type RequestResponseMessage = RequestMessage | ResponseMessage | AcknowledgementMessage;
// Message handler type
export interface MessageHandler<T extends DesignModeMessage = DesignModeMessage> {
type: T['type'];
handler: (message: T) => Promise<any> | any;
validator?: MessageValidator;
}
// Config-related types
export interface IframeModeConfig {
enabled: boolean;
hideUI: boolean;
enableSelection: boolean;
enableDirectEdit: boolean;
}
export interface BatchUpdateConfig {
enabled: boolean;
debounceMs: number;
}
export interface BridgeConfig {
timeout: number;
retryAttempts: number;
retryDelay: number;
heartbeatInterval: number;
debug: boolean;
}
// Message utility helpers
export interface MessageUtilsInterface {
generateRequestId: () => string;
createTimestamp: () => number;
isValidMessage: (message: any) => boolean;
createResponse: <T extends IframeToParentMessage>(
originalMessage: ParentToIframeMessage,
payload: T extends { payload: infer P } ? P : never,
success?: boolean,
error?: string
) => T;
}
// Bridge interface
export interface BridgeInterface {
send: <T extends DesignModeMessage>(message: T) => Promise<void>;
sendWithResponse: <T extends DesignModeMessage, R extends DesignModeMessage>(
message: T,
responseType: R['type']
) => Promise<R>;
on: <T extends DesignModeMessage>(type: T['type'], handler: (message: T) => void) => void;
off: (type: string, handler: Function) => void;
isConnected: () => boolean;
disconnect: () => void;
}