Skip to content

Feature/#587 implement vscode extension#588

Open
Ivanruii wants to merge 15 commits into
vnextfrom
feature/#587-implement-vscode-extension
Open

Feature/#587 implement vscode extension#588
Ivanruii wants to merge 15 commits into
vnextfrom
feature/#587-implement-vscode-extension

Conversation

@Ivanruii
Copy link
Copy Markdown
Collaborator

@Ivanruii Ivanruii commented May 4, 2026

Depends on #586
Close #587


if (content === lastSavedContentRef.current) return;

debounceTimerRef.current = setTimeout(() => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Debounce timer no se limpia entre renders ni en early-returns

El efecto depende de [canvasSchema]. Cuando el schema cambia rápido, cada render entra en este useEffect, asigna un timer nuevo a debounceTimerRef.current y deja al anterior huérfano (la cleanup function solo corre cuando el efecto se va a re-ejecutar o el componente se desmonta, y para entonces ya hemos pisado la referencia). Además, las early-returns de las líneas 19, 25 y 28 tampoco limpian un timer en vuelo, así que pueden disparar un SAVE con contenido obsoleto capturado en el closure.

Sugerencia: limpiar el timer previo antes de programar el nuevo y al inicio del efecto:

useEffect(() => {
  if (debounceTimerRef.current) {
    clearTimeout(debounceTimerRef.current);
    debounceTimerRef.current = null;
  }
  if (!isVSCodeEnv() || !hasReceivedFileRef.current) return;
  // ...resto igual
});


export const sendToExtension = (msg: AppMessage): void => {
if (!isVSCodeEnv()) return;
window.parent.postMessage(msg, '*');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 postMessage con origen wildcard

window.parent.postMessage(msg, '*') envía el payload (schema completo, contenido del archivo en cada SAVE) a cualquier frame padre. Si la página padre no es la esperada — o si el webview de VS Code se anida en algún contexto inesperado — el contenido se filtra.

Sugerencia: usar un origen concreto. El webview de VS Code expone el origen del host vía acquireVsCodeApi (lado del bridge en webview/bridge.ts), pero desde el iframe podemos restringir al origen del padre conocido — o, como mínimo, validar event.origin en el listener inverso y reciclar ese mismo origen como target.

case APP_MESSAGE_TYPE.SAVE:
doc.content = msg.payload.content;
await writeFile(doc.uri, doc.content);
postMessage({ type: HOST_MESSAGE_TYPE.SAVED });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 SAVE escribe a disco pero nunca dispara _onDidChangeCustomDocument

El handler escribe el archivo y emite SAVED, pero el provider expone _onDidChangeCustomDocument (provider.ts:39) que jamás se .fire(). Consecuencias en el ciclo CustomEditorProvider de VS Code:

  • El tab nunca aparece como modificado.
  • saveCustomDocument / saveCustomDocumentAs / backupCustomDocument definidos en el provider son código muerto: VS Code solo los invoca cuando el documento está dirty.
  • Hot-exit y revert nativo no funcionan.

Sugerencia: o bien disparar _onDidChangeCustomDocument.fire({ document: doc }) desde aquí (pasando el provider/event al handler), o bien documentar explícitamente que el editor hace auto-save bypaseando el ciclo nativo y eliminar los métodos del provider que no se van a usar.

this.panels.set(key, [...(this.panels.get(key) ?? []), panel]);
panel.onDidDispose(() => {
const remaining = (this.panels.get(key) ?? []).filter(p => p !== panel);
this.panels.set(key, remaining);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Leak en el mapa panels cuando se cierran todos los paneles de un documento

Al cerrar el último panel, remaining queda como [] y se guarda en el mapa para siempre. Cada archivo abierto y cerrado acumula una entrada huérfana.

Sugerencia:

panel.onDidDispose(() => {
  const remaining = (this.panels.get(key) ?? []).filter(p => p !== panel);
  if (remaining.length === 0) {
    this.panels.delete(key);
  } else {
    this.panels.set(key, remaining);
  }
});

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

};
window.addEventListener('message', onIframeReady);

const observer = new MutationObserver(sendTheme);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 MutationObserver no se debouncea y la cleanup retornada no se captura

MutationObserver sobre document.body dispara sendTheme() por cada mutación de class/style sin debouncing — en escenarios con animaciones o cambios de estado dinámicos esto puede ser muy frecuente.

Además, setupThemeSync retorna una función de cleanup (líneas 48-51) que webview/main.ts:22 no captura ni invoca, así que el listener message y el observer nunca se desmontan.

Sugerencias:

  1. Debouncear sendTheme (≈150ms con setTimeout/clearTimeout).
  2. Capturar la cleanup en main.ts (al menos asignarla a una variable, idealmente registrarla en algún teardown del webview).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

import { onMessage } from './vscode-bridge.helpers';

const CSS_VAR_MAP: Record<keyof ThemePayload, readonly string[]> = {
background: ['--bg-canvas', '--background-800', '--background-900'],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Mapeo background → tres variables CSS con tonos distintos

--bg-canvas, --background-800 y --background-900 reciben el mismo valor. En el sistema de diseño actual --background-800 y --background-900 son tonos diferentes; aplanarlos al mismo color puede romper jerarquía visual (mismo gris para canvas, paneles y bordes). Lo mismo con backgroundSecondary y sus cinco vars.

Sugerencia: ampliar ThemePayload para exponer más niveles (backgroundElevated, backgroundMuted, etc.) y mapear cada variable a su nivel real; o, si es intencional aplanar la paleta en VS Code, dejar un comentario explicándolo.

@@ -1,21 +1,22 @@
import { isVSCodeEnv } from '@/core/vscode/env.helpers';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Import order y comentario huérfano

Dos cosas en este reorder:

  1. isVSCodeEnv (interno, @/core/vscode/...) queda antes que React (externo). El resto del repo agrupa externos primero, luego internos con alias @/.
  2. La línea 17 deja un comentario // CanvasSettingButton, dentro del bloque de imports — ya estaba antes, pero este es buen momento para limpiarlo.

Sugerencia: mover import React arriba del todo y borrar el comentario huérfano.

import { useVSCodeFileLoad } from './use-vscode-file-load.hook';
import { useVSCodeTheme } from './use-vscode-theme.hook';

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 JSDoc multilínea para algo que el código ya transmite

El proyecto evita JSDoc multilínea cuando el nombre del símbolo es autoexplicativo. useVSCodeSync + el comportamiento de los hooks internos (cada uno hace su propio early-return en !isVSCodeEnv()) ya dicen lo mismo.

Sugerencia: borrar el bloque o reducirlo a una línea (// no-ops when not inside the VS Code webview).

? vscode.Uri.parse(openContext.backupId)
: uri;
const content = await readFile(source);
return { uri, content, dispose: () => {} };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 dispose: () => {} sin contexto

Es un stub para cumplir con vscode.CustomDocument. Está bien hoy, pero quien lea esto en seis meses se preguntará si se olvidó algo.

Sugerencia: comentario corto in-line // no resources to release; content is a plain string o, mejor, mover la construcción del objeto a una factory con nombre explícito.

@@ -0,0 +1,38 @@
import { basename } from 'node:path';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Cobertura de tests inexistente para el nuevo módulo VS Code

Ninguno de los nuevos módulos lleva tests: hooks de auto-save / file-load / theme en apps/web/src/core/vscode/, helpers del bridge, provider del editor, ni este handler. La superficie es relevante: IO de archivos, mensajería entre procesos, CSP, ciclo de paneles.

Sugerencia mínima:

  • use-vscode-auto-save.hook — debounce y limpieza de timer.
  • vscode-sync.helpers — round-trip de serialize/deserialize.
  • handlers.ts — ramas WEBVIEW_READY (con y sin JSON válido) y SAVE.
  • MongoModelerEditorProvider — ciclo de vida de paneles, revert.
  • webview/theme.ts — cleanup del observer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[VScode Extension] Implement VScode extension

2 participants