mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 06:20:32 +00:00
* 🎉 Add multi-file session routing to the MCP server Session routing: - Track each user's Penpot connections by session ID. - Dispatch explicit sessions directly; otherwise discover the sole session. - Return a retryable error when discovery fails or is incomplete. Plugin and tools: - Initialize connections with a fresh session ID and file metadata. - Share the optional sessionId schema and place it last in tool inputs. Validation: - 84 tests, server type checking, and MCP formatting checks pass. - Disable test logging to avoid the logging worker shutdown hang. AI-assisted-by: gpt-6 * 🎉 Support independent MCP connections - Require explicit connection intent for each integrated workspace. - Derive short session IDs from the Penpot app instance and file. - Show and copy session IDs in the MCP menu and standalone plugin UI. - Stop stale callbacks and reconnect attempts after disconnect. Validate with frontend and plugin tests, type checking, live multi-tab checks, and standalone UI checks with a simulated connection. Server tests pass with --test-force-exit; the normal runner can hang on shutdown. AI-assisted-by: gpt-6 * 📚 Document multi-file MCP sessions - Explain independent connections, session selection, and copying IDs. - Describe session ID lifetimes for integrated and standalone plugins. - Correct browser focus, connection, and recovery guidance. Validate with the documentation site build and diff checks. AI-assisted-by: gpt-6 * ✨ Change behavior on session ID duplication * ✨ Open MCP toolbar menu on hover and match menu style --------- Co-authored-by: alonso.torres <alonso.torres@kaleidos.net> Co-authored-by: elhombretecla <delacruzgarciajuan@gmail.com>
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
/** Displays the active session ID and provides clipboard feedback. */
|
|
export class SessionIdDisplay {
|
|
private sessionId: string | null = null;
|
|
private readonly value: HTMLElement;
|
|
private readonly button: HTMLButtonElement;
|
|
private readonly feedback: HTMLElement;
|
|
|
|
private readonly root: HTMLElement;
|
|
private readonly clipboard: Pick<Clipboard, "writeText">;
|
|
|
|
constructor(root: HTMLElement, clipboard: Pick<Clipboard, "writeText">) {
|
|
this.root = root;
|
|
this.clipboard = clipboard;
|
|
this.value = root.querySelector<HTMLElement>("#session-id-value")!;
|
|
this.button = root.querySelector<HTMLButtonElement>("#copy-session-id-btn")!;
|
|
this.feedback = root.querySelector<HTMLElement>("#session-id-feedback")!;
|
|
this.button.addEventListener("click", () => this.copy());
|
|
this.setSessionId(null);
|
|
}
|
|
|
|
public setSessionId(sessionId: string | null): void {
|
|
this.sessionId = sessionId;
|
|
this.root.hidden = sessionId === null;
|
|
this.value.textContent = sessionId ?? "";
|
|
this.button.disabled = sessionId === null;
|
|
this.feedback.textContent = "";
|
|
}
|
|
|
|
protected async copy(): Promise<void> {
|
|
const sessionId = this.sessionId;
|
|
if (!sessionId) return;
|
|
try {
|
|
await this.clipboard.writeText(sessionId);
|
|
if (this.sessionId === sessionId) this.feedback.textContent = "Copied";
|
|
} catch {
|
|
if (this.sessionId === sessionId) {
|
|
this.feedback.textContent = "Could not copy. Select the ID to copy it manually.";
|
|
}
|
|
}
|
|
}
|
|
}
|