feat: initial commit - Jhonny Editor

- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
This commit is contained in:
João Henrique
2026-09-08 09:59:31 -04:00
commit b541f502ba
1507 changed files with 387650 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
/* Minimal CEP bridge API used by the local After Effects connector. */
function CSInterface() {}
CSInterface.prototype.evalScript = function (script, callback) {
if (typeof __adobe_cep__ !== "undefined") {
__adobe_cep__.evalScript(script, callback || function () {});
} else if (callback) {
callback("EvalScript Error: Not in CEP environment");
}
};
+38
View File
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.aftereffects.bridge" ExtensionBundleVersion="1.14.9" ExtensionBundleName="MCP for Adobe After Effects">
<ExtensionList>
<Extension Id="com.mcp.aftereffects.bridge.panel" Version="1.14.9"/>
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="AEFT" Version="15.0"/>
</HostList>
<LocaleList>
<Locale Code="All"/>
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="9.0"/>
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="com.mcp.aftereffects.bridge.panel">
<DispatchInfo>
<Resources>
<MainPath>./index.html</MainPath>
<ScriptPath>./host.jsx</ScriptPath>
<CEFCommandLine>
<Parameter>--allow-file-access-from-files</Parameter>
<Parameter>--enable-nodejs</Parameter>
</CEFCommandLine>
</Resources>
<Lifecycle><AutoVisible>true</AutoVisible></Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>MCP for Adobe After Effects</Menu>
<Geometry><Size><Height>250</Height><Width>390</Width></Size><MinSize><Height>200</Height><Width>320</Width></MinSize></Geometry>
<Icons/>
</UI>
</DispatchInfo>
</Extension>
</DispatchInfoList>
</ExtensionManifest>
+5
View File
@@ -0,0 +1,5 @@
// The command body is supplied through the local bridge. Keeping this host
// script minimal prevents unreviewed global helpers from persisting in AE.
function mcpAfterEffectsBridgePing() {
return "pong";
}
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MCP for Adobe After Effects</title>
<style>
body { margin: 0; background: #202124; color: #f3f4f6; font: 13px/1.45 Arial, sans-serif; }
main { padding: 18px; } h1 { margin: 0 0 5px; font-size: 16px; } p { color: #c7cbd1; }
label { display: block; margin: 16px 0 6px; font-weight: bold; } input { box-sizing: border-box; width: 100%; padding: 8px; border: 1px solid #555; border-radius: 4px; background: #111827; color: white; }
button { margin-top: 12px; padding: 8px 12px; border: 0; border-radius: 4px; background: #2563eb; color: white; cursor: pointer; } #status { display: inline-block; margin-left: 9px; color: #86efac; }
small { display: block; margin-top: 8px; color: #9ca3af; }
</style>
</head>
<body>
<main>
<h1>MCP for Adobe After Effects</h1>
<p>Local authoring connector for approval-gated MOGRT recipes.</p>
<label for="tempDir">After Effects bridge directory</label>
<input id="tempDir" spellcheck="false" autocomplete="off" aria-describedby="bridgeHelp">
<button id="toggle" type="button">Start connector</button><strong id="status" role="status">Stopped</strong>
<small id="bridgeHelp">This must match AFTER_EFFECTS_MCP_TEMP_DIR when that environment variable is set for your MCP client.</small>
</main>
<script src="CSInterface.js"></script>
<script src="main.js"></script>
</body>
</html>
+129
View File
@@ -0,0 +1,129 @@
/* Dedicated AE CEP file bridge. It deliberately uses a different directory
* from the Premiere connector so simultaneous Adobe hosts cannot claim each
* other's ExtendScript commands. */
(function () {
var cs = new CSInterface();
var fs = nodeRequire("fs");
var path = nodeRequire("path");
var os = nodeRequire("os");
var pollTimer = null;
var heartbeatTimer = null;
var running = false;
var tempDir = defaultBridgeDirectory();
var engineId = Math.random().toString(36).slice(2, 8);
function nodeRequire(moduleName) {
if (typeof require !== "undefined") return require(moduleName);
var node = typeof cep_node !== "undefined" ? cep_node : window.cep_node;
if (node && typeof node.require === "function") return node.require(moduleName);
throw new Error("Node.js is unavailable. Confirm --enable-nodejs in the CEP manifest, then fully restart After Effects.");
}
function defaultBridgeDirectory() {
try {
var process = nodeRequire("process");
var configured = process && process.env && process.env.AFTER_EFFECTS_MCP_TEMP_DIR;
if (typeof configured === "string" && configured.trim()) return configured.trim();
} catch (ignored) {}
return path.join(os.tmpdir(), "after-effects-mcp-bridge");
}
function setStatus(value, active) {
document.getElementById("status").textContent = value;
document.getElementById("status").style.color = active ? "#86efac" : "#fca5a5";
document.getElementById("toggle").textContent = active ? "Stop connector" : "Start connector";
}
function writeFileAtomic(filePath, text) {
var staged = filePath + "." + engineId + ".staged";
try {
fs.writeFileSync(staged, text, "utf8");
fs.renameSync(staged, filePath);
return true;
} catch (error) {
try { if (fs.existsSync(staged)) fs.unlinkSync(staged); } catch (ignored) {}
setStatus("Connector needs attention", false);
return false;
}
}
function heartbeat() {
if (!tempDir) return;
writeFileAtomic(path.join(tempDir, "bridge-heartbeat.json"), JSON.stringify({ protocolVersion: 1, state: running ? "running" : "waiting" }));
}
function readCommandFiles() {
try {
return fs.readdirSync(tempDir).filter(function (entry) {
return entry.indexOf("cmd_") === 0 && entry.slice(-4) === ".jsx";
}).sort();
} catch (ignored) { return []; }
}
function replyFor(result) {
if (!result || result === "undefined" || result === "null") {
return JSON.stringify({ success: false, error: "The After Effects connector received an empty evalScript result. Reopen the panel and retry once." });
}
try { return JSON.stringify(JSON.parse(result)); }
catch (ignored) {
return result.indexOf("Error") === 0
? JSON.stringify({ success: false, error: result })
: JSON.stringify({ success: true, data: result });
}
}
function processOne(fileName) {
var source = path.join(tempDir, fileName);
var claim = source + "." + engineId + ".claimed";
try { fs.renameSync(source, claim); } catch (ignored) { return; }
var script;
try { script = fs.readFileSync(claim, "utf8"); } catch (error) { script = null; }
try { if (fs.existsSync(claim)) fs.unlinkSync(claim); } catch (ignored) {}
if (!script) return;
var id = fileName.replace("cmd_", "").replace(".jsx", "");
var busy = path.join(tempDir, "busy_" + id + ".json");
var started = Date.now();
var busyTimer = setInterval(function () {
try { fs.writeFileSync(busy, JSON.stringify({ id: id, elapsedMs: Date.now() - started }), "utf8"); } catch (ignored) {}
}, 2000);
cs.evalScript(script, function (result) {
clearInterval(busyTimer);
try { if (fs.existsSync(busy)) fs.unlinkSync(busy); } catch (ignored) {}
writeFileAtomic(path.join(tempDir, "res_" + id + ".json"), replyFor(String(result || "")));
});
}
function processCommands() {
var files = readCommandFiles();
for (var index = 0; index < files.length; index++) processOne(files[index]);
}
function start() {
tempDir = document.getElementById("tempDir").value.trim();
if (!tempDir) { setStatus("Set a bridge directory", false); return; }
try { fs.mkdirSync(tempDir, { recursive: true, mode: 0o700 }); }
catch (error) { setStatus("Cannot create bridge directory", false); return; }
running = true;
heartbeat();
if (pollTimer) clearInterval(pollTimer);
if (heartbeatTimer) clearInterval(heartbeatTimer);
pollTimer = setInterval(processCommands, 200);
heartbeatTimer = setInterval(heartbeat, 1000);
try { localStorage.setItem("after_effects_mcp_temp_dir", tempDir); } catch (ignored) {}
setStatus("Connector running", true);
}
function stop() {
running = false;
heartbeat();
if (pollTimer) clearInterval(pollTimer);
if (heartbeatTimer) clearInterval(heartbeatTimer);
pollTimer = null;
heartbeatTimer = null;
setStatus("Stopped", false);
}
var field = document.getElementById("tempDir");
try { field.value = localStorage.getItem("after_effects_mcp_temp_dir") || tempDir; } catch (ignored) { field.value = tempDir; }
document.getElementById("toggle").onclick = function () { if (running) stop(); else start(); };
}());