PhotoshopのCEPカスタムパネルは、HTMLのボタンからExtendScript(JSX)を呼び出す仕組みです。既存のCEPパネルを保守する人向けに、manifest・画面・JSXをそろえる最小例と、表示されないときの確認順を説明します。新しく長期運用するパネルを開発する場合は、UXPプラグインの開発方法を確認してください。
今回の例はWindowsのCEP開発用サンプルです。Photoshopでの読み込み・グループ追加・新しい操作画面の撮影は未検証です。旧記事の画面とコードは後半に保存し、現在の実行結果とは区別しています。
CEPとUXP、アプリ版とランタイム版を分ける
| 項目 | この例で指定するもの |
|---|---|
| Photoshopのホスト版 | manifestの[27.0,28.0)は27.xを対象として宣言する範囲。動作保証ではない。 |
| CEPランタイム | RequiredRuntimeのCSXS 12.0は要求する最低版。Photoshopの27という番号とは別。 |
| 画面側 | HTML・CSS・JavaScript。CSInterface.jsを通じて呼び出す。 |
| Photoshop側 | ExtendScriptの.jsx。UXPの.psjsを同じファイルとして扱わない。 |
AdobeのCEP 12 CookbookではPhotoshop 25.12とCEP 12の組合せが掲載されています。利用中の版・OSがCEPを読み込めるかを別に確認してください。Apple SiliconのPhotoshopで旧CEPを使う条件はAdobeのRosettaに関する案内で確認できます。
2026年9月のAdobeの移行計画は、Photoshopの新規CEP受付を2027年3月に終了、2027年12月に初期状態で無効化、2029年12月に新しいアプリ版から除去する予定を示しています。ExtendScript自体はこの移行の対象外です。現在すでに除去されたという意味ではありません。
5つのファイルとCSInterface.jsを配置する
Windowsでは、%APPDATA%\Adobe\CEP\extensionsの下にcom.cgmethod.cep.sampleフォルダーを作成します。別の場所に保管した開発元をコピーし、既存パネルを上書きしない固有IDを使ってください。Bracketsは必須ではありません。AdobeはBracketsのサポートを2021年9月1日に終了しました。Adobeリポジトリの案内を確認し、UTF-8で保存できるエディターで編集します。
com.cgmethod.cep.sample/
CSXS/manifest.xml
index.html
css/style.css
js/CSInterface.js
js/main.js
jsx/hostscript.jsx
CSInterface.jsはAdobe CEP 12の公式ライブラリを保存し、jsフォルダーへ配置します。以下の5ファイルは同じサンプルの構成です。jQuery・外部CDN・個人の絶対パス・動的HTML読込は使いません。
CSXS/manifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="7.0" ExtensionBundleId="com.cgmethod.cep.sample" ExtensionBundleVersion="1.0.0" ExtensionBundleName="CGM CEP Sample">
<ExtensionList><Extension Id="com.cgmethod.cep.sample.panel" Version="1.0.0"/></ExtensionList>
<ExecutionEnvironment>
<HostList><Host Name="PHXS" Version="[27.0,28.0)"/></HostList>
<LocaleList><Locale Code="All"/></LocaleList>
<RequiredRuntimeList><RequiredRuntime Name="CSXS" Version="12.0"/></RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList><Extension Id="com.cgmethod.cep.sample.panel"><DispatchInfo>
<Resources><MainPath>./index.html</MainPath><ScriptPath>./jsx/hostscript.jsx</ScriptPath></Resources>
<Lifecycle><AutoVisible>true</AutoVisible></Lifecycle>
<UI><Type>Panel</Type><Menu>CGM CEP Sample</Menu><Geometry><Size><Height>220</Height><Width>280</Width></Size></Geometry></UI>
</DispatchInfo></Extension></DispatchInfoList>
</ExtensionManifest>
index.html
<!doctype html>
<html lang="ja"><head><meta charset="utf-8"><title>CGM CEP Sample</title>
<link rel="stylesheet" href="css/style.css"></head><body>
<button id="make-group" type="button">テストグループを追加</button>
<p id="result" role="status">未実行</p>
<script src="js/CSInterface.js"></script><script src="js/main.js"></script>
</body></html>
css/style.css
body { margin: 12px; background: #303030; color: #eee; font: 14px sans-serif; }
button { width: 100%; padding: 10px; }
#result { overflow-wrap: anywhere; }
js/main.js
(function () {
"use strict";
var button = document.getElementById("make-group");
var result = document.getElementById("result");
if (typeof CSInterface !== "function") {
button.disabled = true;
result.textContent = "CSInterface.jsとCEP内での読込を確認してください。";
return;
}
var cs = new CSInterface();
button.addEventListener("click", function () {
button.disabled = true;
result.textContent = "処理中";
try {
cs.evalScript("cgmMakeGroup()", function (response) {
button.disabled = false;
result.textContent = response || "応答がありません。文書とログを確認してください。";
});
} catch (error) {
button.disabled = false;
result.textContent = "呼出し失敗: " + error.message;
}
});
}());
jsx/hostscript.jsx
function cgmMakeGroup() {
if (app.documents.length === 0) {
return "ERROR: 文書のコピーを開いてください。";
}
try {
var doc = app.activeDocument;
var group = doc.layerSets.add();
group.name = "CGM_panel_test";
return "OK: " + doc.name + " / " + group.name;
} catch (error) {
return "ERROR: " + error.message;
}
}
MainPathが画面の入口、ScriptPathがPhotoshop側の関数を読み込む指定です。main.jsからcgmMakeGroup()を呼び、JSXの戻り値を画面に表示します。ボタンを押すと開いた文書にグループを追加するため、原本ではなくコピーで確認してください。この例はファイルを保存しません。
未署名パネルの開発設定と復元
署名済みの配布物と、未署名の開発元は扱いが異なります。自作の未署名パネルを読む場合だけ、使用中のCEPに合うPlayerDebugModeを設定します。CEP 12 CookbookのWindows例はHKEY_CURRENT_USER\Software\Adobe\CSXS.12に文字列値PlayerDebugModeを作り、値を1にします。CSXS.7をすべてのPhotoshopに使う設定ではありません。
- 変更前に対象キーとPlayerDebugModeの有無・種類・値を記録する。既存値を消さない。
- 変更後にPhotoshopを再起動し、ウィンドウ→エクステンション(従来)など利用版のメニューからCGM CEP Sampleを探す。
- 確認後は、以前からあった値なら元の値へ戻す。自分が追加した値ならその値だけを削除する。無関係なキーやほかの開発設定は消さない。
画面表示とJSX実行を順に確認する
| 段階 | 期待する状態・記録 |
|---|---|
| 表示 | CGM CEP Sampleのボタンと「未実行」が見える。これだけでJSX成功とはしない。 |
| 文書なし | ボタンを押すとERROR: 文書のコピーを開いてください。新規文書を勝手に作らない。 |
| 文書のコピーあり | 元のグループ数を控え、1回押すとCGM_panel_testが1つ増える。OK:に表示された文書名を照合する。 |
| 復元 | 元に戻すで追加グループを戻すか、保存せずコピーを閉じる。原本が変わっていないことを確認する。 |
以上は確認用の期待値です。このPCでのCEP実行結果ではありません。保存を伴う自作JSXを組み込む場合は、枚数・ファイル名・内容・保存先を開き直して照合し、パネルのOK表示だけで完了とは判断しないでください。
表示されない・ボタンが動かない場合の確認順
| 症状 | 確認する箇所 |
|---|---|
| メニューにない | CEPに対応する起動方式、配置先、拡張IDの重複、PHXSとホスト版の範囲、要求ランタイム。範囲を広げても互換性は増えない。 |
| 未署名の警告 | 使用中のCEPに合うPlayerDebugModeのキー・文字列型・値。無関係なCSXSキーへ1を追加し続けない。 |
| パネルが空 | MainPathとindex.html、JavaScript/CSSの相対パス、ファイル名・文字コード。 |
| CSInterfaceの読込エラー | 公式ライブラリの保存位置とscriptの順序。通常ブラウザーだけではPhotoshopのCEP APIは使えない。 |
| EvalScript error.や関数未定義 | ScriptPath・JSXの関数名・保存後の再読込とAdobeのCEPログを確認。 |
| 途中の処理失敗 | 対象文書・レイヤー・色モードなどJSXの条件を確認する。処理を自動で繰り返さず、文書の変化も確認する。 |
ログと開発者ツールの手順はAdobeのクライアント側デバッグ資料を参照してください。CSS・HTML・JSXを保存しただけで、開いているパネルが必ず再読込されるとは限りません。いったんパネルを読み直し、必要ならPhotoshopを再起動して変更を照合します。
タブ・折りたたみ・外部JSXへ拡張する
最小例の呼出しが通ったあとで、ボタンを追加し、HTMLのidと関数名を1組ずつ合わせます。タブはボタンと内容領域を同じ数にし、最初の領域だけを表示します。JSXを別ファイルに分ける場合は、拡張ルートからの相対配置と読込順を明示してください。利用者名を埋め込んだ絶対パスは移行時に壊れます。
旧記事のタブ・折りたたみ・CSSは後半の参照資料に保存しました。外部HTMLをiframeで取り込む旧include.jsは引用符が崩れているため、現行の最小例では使いません。必要なUIをindex.htmlへ配置し、1つずつ確認してください。
配布時の署名とZXP
配布用CEPは、開発時のデバッグ許可を前提にしない形でパッケージ化します。Adobeの署名・配布資料でZXPSignCmdと証明書を確認してください。署名後にファイルを変更すると同じ署名での検証対象ではなくなります。今回の例では署名・ZXP導入を実行していません。旧署名画面のtestは例示用で、自分の証明書とパスワードを公開コードへ保存しないでください。
旧記事の画面記録













旧コードの参照資料(実行しない)
以下は旧記事のコードを保存した参照資料です。新しい例と混ぜて実行しないでください。旧配布リポジトリも今回更新していません。
Bracketsによる雛形、CSXS.7、CC 2017の18.x指定、絶対パスのincludeと動的HTML読込を含みます。include.jsには引用符の不整合、HTMLにはタグ間の余分な空白があり、そのままの実行用ではありません。
旧コード 1
<Host Name="PHXS" Version="[15.0,15.9]" />
<Host Name="PHSP" Version="[15.0,15.9]" />旧コード 2
<Host Name="PHSP" Version="[18.0,18.9]" />
<Host Name="PHXS" Version="[18.0,18.9]" />旧コード 3
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Adobe\CSXS.7]
"PlayerDebugMode"="1"旧コード 4
ZXPSignCmd -selfSignedCert JP Tokyo My MIN test custompanel.test旧コード 5
ZXPSignCmd -sign "C:\Users\ユーザー名\AppData\Roaming\Adobe\CEP\extensions\com.example.helloworld" custompanel.zxp custompanel.test test旧コード 6
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="5.0" ExtensionBundleId="com.example.test" ExtensionBundleVersion="1.0"
ExtensionBundleName="TEST" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionList>
<Extension Id="com.example.test" Version="1.0" />
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="PHSP" Version="[18.0,18.9]" />
<Host Name="PHXS" Version="[18.0,18.9]" />
</HostList>
<LocaleList>
<Locale Code="All" />
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="5.0" />
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="com.example.test">
<DispatchInfo>
<Resources>
<MainPath>./index.html</MainPath>
</Resources>
<Lifecycle>
<AutoVisible>true</AutoVisible>
</Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>TEST</Menu>
<Geometry>
<Size>
<Height>300</Height>
<Width>300</Width>
</Size>
</Geometry>
</UI>
</DispatchInfo>
</Extension>
</DispatchInfoList>
</ExtensionManifest>旧コード 7
<html>
<head></head>
<body>
<img src="icons/suimin.png">
</body>
</html>旧コード 8
#include "/c/Users/ユーザー名/AppData/Roaming/Adobe/CEP/extensions/custompanel/jsx/scripts/AAA.jsx"旧コード 9
$("#AAA").click(function () {
csInterface.evalScript('AAA()');
});旧コード 10
$("#AAA").click(function () {
csInterface.evalScript('AAA()');
});旧コード 11
<!-- 折りたたみ -->
<div id="AAAAA" class="topcoat-button--large--cta hostFontSize" onclick="obj=document.getElementById('BBBBB').style; obj.display=(obj.display=='none')?'block':'none';">
便利ツール
</div>
<!--// 折りたたみ -->
<!-- 折りたたまれ -->
<div id="BBBBB" style="display:none;clear:both;">
<button id="AAA" class="topcoat-button--large hostFontSize">あいうえお</button>
</div>
<!--// 折りたたまれ -->旧コード 12
<!-- 折りたたみ -->
<div id="AAAAA" class="topcoat-button--large--cta hostFontSize" onclick="obj=document.getElementById('BBBBB').style; obj.display=(obj.display=='block')?'none':'block';">
便利ツール
</div>
<!--// 折りたたみ -->
<!-- 折りたたまれ -->
<div id="BBBBB" style="display:block;clear:both;">
<button id="AAA" class="topcoat-button--large hostFontSize">あいうえお</button>
</div>旧コード 13
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>旧コード 14
<script>
$(function() {
$(".tab li").click(function() {
var index = $(".tab li").index(this);
$(".tabContent").removeClass('active');
$(".tabContent").eq(index).addClass('active');
$(".tab li").removeClass('active');
$(this).addClass('active');
});
});
</script>旧コード 15
<ul class="tab">
<li class="topcoat-button--large hostFontSize active">メニューA</li>
<li class="topcoat-button--large hostFontSize">メニューB</li>
<li class="topcoat-button--large hostFontSize">メニューC</li>
</ul>旧コード 16
/* ボタンを中央寄せ */
div { text-align: center; }
/* ボタンサイズの統一 */
.topcoat-button--large--cta, .topcoat-button--large {
width: 100%;
}
/* タブデザイン */
ul.tab {
width: 100%;
list-style: none;
padding-left: 0;
}
ul.tab li {
float: left;
height: 30px;
text-align: center;
width: 33.3%;
padding: 5px;
background-color: #595b5b;
color: #fff;
cursor: pointer;
}
ul.tab li:hover {
background-color: #539fe1;
}
ul.tab li.active {
background-color: #368bdc;
}
div.tabContent {
clear: both;
width: 100%;
display: none;
}
div.tabContent.active {
display: block;
}
旧コード 17
function makeGroup(){
var layer_face = app.activeDocument.layerSets.add();
layer_face.name = '顔';
}旧コード 18
(function () {
'use strict';
var csInterface = new CSInterface();
function init() {
$("#btn_test").click(function () {
csInterface.evalScript('makeGroup()');
});
}
init();
}());旧コード 19
<button id="btn_test" class="topcoat-button--large hostFontSize">グループ作成</button>旧コード 20
#include "/C/Users/Namae/AppData/Roaming/Adobe/CEP/extensions/custompanel/jsx/hostscript.jsx"旧コード 21
<script src="C:/Users/Namae/AppData/Roaming/Adobe/CEP/extensions/custompanel/js/main.js"></script>旧コード 22
function include(filename, afterfunc) {
include.seq = (include.seq) ? include.seq + 1 : 1;
var id = new Date().getTime() + "-" + include.seq;
var inc = document.createElement("iframe");
inc.id = "inc-" + id;
inc.src = filename;
inc.style.display = "none";
document.write("<span id="" + id + ""></span>");
var incfunc = function() {
var s = (function() {
var suffix = (n = filename.lastIndexOf(".")) >= 0 ? filename.substring(n) : "default";
if (suffix == ".html") return inc.contentWindow.document.body.innerHTML;
if (suffix == ".txt") return inc.contentWindow.document.body.firstChild.innerHTML;
if (suffix == "default") return inc.contentWindow.document.body.innerHTML;
})();
var span = document.getElementById(id);
var insertBeforeHTML = function(htmlStr, refNode) {
if (document.createRange) {
var range = document.createRange();
range.setStartBefore(refNode);
refNode.parentNode.insertBefore(range.createContextualFragment(htmlStr), refNode);
} else {
refNode.insertAdjacentHTML('BeforeBegin', htmlStr);
}
};
insertBeforeHTML(s.split(">").join(">").split("<").join("<"), span);
document.body.removeChild(inc);
span.parentNode.removeChild(span);
if (afterfunc) afterfunc();
};
if (window.attachEvent) {
window.attachEvent('onload',
function() {
document.body.appendChild(inc);
inc.onreadystatechange = function() {
if (this.readyState == "complete") incfunc();
};
});
} else {
document.body.appendChild(inc);
inc.onload = incfunc;
}
}旧コード 23
< !doctype html >
< html >
< head >
< meta charset = "utf-8" >
< link rel = "stylesheet"
href = "css/topcoat-desktop-dark.min.css" / >
< link id = "hostStyle"
rel = "stylesheet"
href = "css/styles.css" / >
< script type = "text/javascript"
src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js" > < /script> < script >
$(function() {
$(".tab li").click(function() {
var num = $(".tab li").index(this);
$(".tabContent").removeClass('active');
$(".tabContent").eq(num).addClass('active');
$(".tab li").removeClass('active');
$(this).addClass('active')
});
}); < /script> < title > < /title> < /head> < body class = "hostElt" >
< ul class = "tab" >
< li class = "topcoat-button--large hostFontSize active" > メニューA < /li> < li class = "topcoat-button--large hostFontSize" > メニューB < /li> < li class = "topcoat-button--large hostFontSize" > メニューC < /li> < /ul> < script type = "text/javascript"
src = "js/include.js" > < /script> < script type = "text/javascript" >
include("C:/Users/Namae/AppData/Roaming/Adobe/CEP/extensions/custompanel/contents.html"); < /script>配布元と関連する実行方法
旧コードの配布元はMOTOKI-LLC/cg-method-codeです。今回の最小例とは別の参照資料で、リポジトリを更新していません。
JSXを参照から実行する方法、既存JSXLauncherの条件、UXPプラグインも確認できます。





