This commit is contained in:
zuowei1216
2025-12-22 21:06:29 +08:00
parent 8ea58fe480
commit 1b19ff1b92
179 changed files with 21895 additions and 3774 deletions

View File

@@ -0,0 +1,98 @@
import { copyCepToDev, copyCepToProd } from "./copyCepToDev"
import { createConfig } from "./createConfig"
// 注意:已移除 buildJsx 依赖,现在使用内联 JSX 方式
//@ts-ignore
import packageConfig from "../../package.json"
import defaultConfig from '../../cep.config';
import { ICepConfig } from "./types";
let g_config: any = ''
interface CepPluginOptions {
cepConfig?: ICepConfig;
jsxInput?: string;
jsxOutput?: string;
jsxIncludes?: string[];
}
export default function cepPlugin(options: CepPluginOptions = {}) {
const config = options.cepConfig || defaultConfig;
return {
name: 'transform-file',
async buildStart(viteConfig: any) {
// 已移除 JSX 构建逻辑,现在使用内联 JSX 方式
if (process.env.NODE_ENV === 'production') {
console.log('正式环境 - 使用内联 JSX');
}
},
transform(src: any, id: any) {
},
configResolved(viteConfig: any) {
g_config = viteConfig
if (viteConfig.isProduction)
return console.log('[cepPlugin]打包不处理');
// 已移除 JSX 监听构建,现在使用内联 JSX 方式
console.log('[cepPlugin] 使用内联 JSX无需构建外部文件');
const name = `${getProjectName(viteConfig.root)}-dev`
// 使用配置的端口strictPort: true 确保端口不会变)
const port = viteConfig.server?.port || 5180
const serverURL = `http://localhost:${port}/`
try {
const cepConfig = createConfig(viteConfig.root, {
name: name,
id: `com.${name}`,
version: packageConfig.version,
}, "dev")
copyCepToDev({
serverURL: serverURL,
name: name,
isBuild: false
}, cepConfig)
} catch (error) {
console.log('[CEP] 初始化失败');
console.log(error);
}
},
async closeBundle() {
if (!g_config.isProduction)
return console.log('[cepPlugin]开发环境不处理');
// await buildJsxByProd()
const name = `${getProjectName(g_config.root)}`
try {
// Use the provided config instead of recreating from package.json if possible,
// but createConfig merges simple props.
// Wait, createConfig uses hardcoded template logic.
// Let's passed detailed config if needed.
// For now, we trust config passed in options.
// Note: createConfig logic might need review if we want full Custom Config control.
// But typically it uses the 'config' object we imported/selected at top.
// Actually createConfig generates the manifest content string.
// We should pass our 'config' object to the copy/write logic.
console.log('[id]:' + config.id);
copyCepToProd({
// 方案 BCEP 插件打开后直接跳转到服务器 Shell 登录页
serverURL: 'https://aidg168.uk/shell/#/login',
name: name,
isBuild: true,
distDir: g_config.build.outDir
}, config)
} catch (error) {
console.log('[CEP] 初始化失败');
console.log(error);
}
console.log('[cepPlugins] 编译成功');
}
}
}
function getProjectName(root: string) {
if(root.endsWith("src/launcher")) return "Designer"; // Hack for launcher root? No, Vite root usually project root.
return root.split('/').pop()
}

View File

@@ -0,0 +1,181 @@
import fs from 'fs'
import path from 'path'
import { ICepConfig, IPanel } from './types';
import { joinDebug } from './template/debug';
import { copyFolderSync } from './utils/fs';
import { getAdobeCepDir } from './utils/cepDir';
import { joinManifest } from './template/manifest';
import { joinHtml } from './template/html';
import open from 'open';
/**
* 复制插件壳到ps插件目录
*/
export function copyCepToDev(options: Omit<IOptions, 'outCepPath' | 'inputCepTemp'>, cepConfig: ICepConfig) {
cepConfig.panels[0].displayName = `[dev]${cepConfig.panels[0].displayName}`
const cep = new CEP(options, cepConfig)
cep.write()
console.log(`[CEP] 插件壳已就绪`);
console.log(`[CEP] PS调试地址: http://localhost:7090`);
open('http://localhost:7090')
}
export function copyCepToProd(options: Omit<IOptions, 'outCepPath' | 'inputCepTemp'>, cepConfig: ICepConfig) {
cepConfig.panels[0].displayName = `${cepConfig.panels[0].displayName}`
const cep = new CEP(options, cepConfig)
cep.write()
}
type IOptions = {
name: string
serverURL: string
outCepPath: string
inputCepTemp: string
isBuild: boolean
distDir?: string // Added dynamic dist path
}
export class CEP {
constructor(private readonly options: Omit<IOptions, 'outCepPath' | 'inputCepTemp'>, private readonly cepConfig: ICepConfig) {
this.createDist()
}
private get cepInput() {
return path.join(__dirname, "./template/cep")
}
private get dist() {
if (this.options.distDir) {
if (path.isAbsolute(this.options.distDir)) return this.options.distDir;
return path.resolve(process.cwd(), this.options.distDir);
}
return path.join(__dirname, '../../dist')
}
private get cepOutput() {
return path.join(this.dist, this.options.name)
}
private get cepLink() {
return path.join(getAdobeCepDir(), this.options.name)
}
private get debug() {
return path.join(this.cepOutput, '.debug')
}
private createDist() {
if (!fs.existsSync(this.dist)) {
fs.mkdirSync(this.dist)
}
}
public write() {
this.copyFolder()
if (!this.options.isBuild)
this.writeHtml()
this.writeCSXS()
if (!this.options.isBuild)
this.writeDebug()
this.copyJson2()
// 创建符号链接或直接复制
if (!fs.existsSync(this.cepLink)) {
try {
fs.symlinkSync(this.cepOutput, this.cepLink, 'dir')
console.log('[CEP] 符号链接已创建')
} catch (error: any) {
// 权限不足时,改用复制
if (error.code === 'EPERM') {
console.warn('[CEP] 符号链接权限不足,改用复制方式')
this.copyToCepDir()
} else {
throw error
}
}
} else {
// 目录已存在,需要更新文件
this.copyToCepDir()
}
if (this.options.isBuild) {
this.copyBuildFiles()
}
console.log('[cepPlugin] 安装目录:', getAdobeCepDir());
}
private copyToCepDir() {
// 删除旧目录
if (fs.existsSync(this.cepLink)) {
fs.rmSync(this.cepLink, { recursive: true, force: true })
}
// 复制目录
copyFolderSync(this.cepOutput, this.cepLink)
console.log('[CEP] 已复制到 CEP 目录')
}
private copyFolder() {
copyFolderSync(this.cepInput, this.cepOutput)
}
private writeHtml() {
const outhtml = path.join(this.cepOutput, 'index.html')
let content = joinHtml(this.cepConfig.name, this.options.serverURL || 'http://localhost:5173/')
fs.writeFileSync(outhtml, content)
}
private writeCSXS() {
const info = joinManifest(this.cepConfig)
const output = path.join(this.cepOutput, 'CSXS/manifest.xml')
fs.writeFileSync(output, info)
}
private writeDebug() {
const info = joinDebug(this.cepConfig.id, this.cepConfig.hosts)
fs.writeFileSync(this.debug, info)
}
private copyJson2() {
const input = path.join(__dirname, '../utils/json')
const out = path.join(this.cepOutput, 'js')
copyFolderSync(input, out)
}
private copyBuildFiles() {
// 1. 复制 assets 目录
const assetsInput = path.join(this.dist, 'assets')
const assetsOut = path.join(this.cepOutput, 'assets')
if (fs.existsSync(assetsInput)) {
copyFolderSync(assetsInput, assetsOut)
console.log('[CEP] ✓ 已复制 assets 目录')
}
// 2. 复制 CSInterface.js
const csInterfaceSrc = path.join(this.dist, 'CSInterface.js')
const csInterfaceDst = path.join(this.cepOutput, 'CSInterface.js')
if (fs.existsSync(csInterfaceSrc)) {
fs.copyFileSync(csInterfaceSrc, csInterfaceDst)
console.log('[CEP] ✓ 已复制 CSInterface.js')
}
// 3. 复制并修正 HTML 路径
const builtHtmlPath = path.join(this.dist, 'src/launcher/index.html')
const targetHtmlPath = path.join(this.cepOutput, 'index.html')
if (fs.existsSync(builtHtmlPath)) {
// 读取 HTML 内容并修正路径
let htmlContent = fs.readFileSync(builtHtmlPath, 'utf-8')
// 修正路径:把 ../../ 替换成 ./
htmlContent = htmlContent.replace(/\.\.\/\.\.\//g, './')
// 确保 CSInterface.js 路径正确
htmlContent = htmlContent.replace(/src="\.\/CSInterface\.js"/g, 'src="CSInterface.js"')
fs.writeFileSync(targetHtmlPath, htmlContent)
console.log('[CEP] ✓ 已复制并修正 HTML 路径')
} else {
console.warn('[CEP] ✗ 未找到构建的 HTML:', builtHtmlPath)
}
}
}

View File

@@ -0,0 +1,40 @@
import fs from 'fs';
import path from 'path';
import defConfig from './template/cep.config';
import { ICepConfig } from './types';
const fileName = 'cep.config.ts';
/**
*
* @param root 创建cep.config.ts
*/
export function createConfig(root: string, config: Partial<ICepConfig>, env: 'dev' | 'prod') {
// 指定要检查的文件路径
const filePath = path.resolve(root, env + "." + fileName);
const temp: ICepConfig = Object.assign({}, defConfig, {
name: config.name,
id: config.id,
version: config.version,
});
// 判断文件是否存在
if (!fs.existsSync(filePath)) {
// 如果文件不存在,则创建文件
fs.writeFileSync(filePath, `import { ICepConfig } from "@/plugins";\n\nconst config: ICepConfig = ${JSON.stringify(temp, null, 4)}\n\nexport default config; `);
console.log(`[CEP] 添加配置文件 ${fileName} 成功!`);
} else {
console.log(`[CEP] 已有配置`);
return getFileConfig(filePath);
}
return temp;
}
function getFileConfig(configPath: string) {
let temp = fs.readFileSync(configPath, 'utf-8').split('ICepConfig = ')[1];
const temp2 = temp.split('}')
temp2.pop()
const str = temp2.join('}') + '}';
return JSON.parse(str);
}

View File

@@ -0,0 +1,3 @@
## 目录说明
- cep 插件壳模板文件
- cep.config.json 配置文件

View File

@@ -0,0 +1,65 @@
import { ICepConfig } from "@plugins";
const config: ICepConfig = {
name: "cepName",
id: "com.cepName",
version: "1.0.0",
extensionVersion: "6.1.0",
requiredRuntimeVersion: "9.0",
type: "Panel",
parameters: [
"--enable-nodejs",
],
panels: [
{
name: "cepName",
displayName: "panelName",
main: "./index.html",
width: 400,
height: 300,
minWidth: 400,
minHeight: 300,
maxWidth: 4000,
maxHeight: 3000,
}
],
hosts:[
{
name: "AEFT",
version: "[0.0,99.9]",
},
{
name: "PPRO",
version: "[0.0,99.9]",
},
{
name: "ILST",
version: "[0.0,99.9]",
},
{
name: "PHXS",
version: "[0.0,99.9]",
},
{
name: "FLPR",
version: "[0.0,99.9]",
},
],
build: {
jsxBin: false,
/**国家 */
country: "CN",
/**省份 */
province: "GD",
/**公司名称 */
org: "你的公司名称",
/**签名密码 */
password: "",
tsa: "",
},
zxp: {
jsxBin: false
}
}
export default config;

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="com.temp.id" ExtensionBundleVersion="1.0" Version="6.0"> <!-- MAJOR-VERSION-UPDATE-MARKER -->
<ExtensionList>
<Extension Id="com.temp.id" Version="6.1.0"/>
</ExtensionList>
<ExecutionEnvironment>
<HostList>
<Host Name="PHXS" Version="[11.0,99.9]"/>
<Host Name="ILST" Version="[11.0,99.9]"/>
</HostList>
<LocaleList>
<Locale Code="All"/>
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="7.0"/> <!-- MAJOR-VERSION-UPDATE-MARKER -->
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
</DispatchInfoList>
</ExtensionManifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{name}}</title>
<script>
window.location.href = "{{serverURL}}"
</script>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,15 @@
export function joinDebug(id: string, hosts: { name: string }[]) {
let port = 7090
return `
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionList>
<Extension Id="${id}">
<HostList>
${hosts
.map((host) => `<Host Name="${host.name}" Port="${port++}"/>`)
.join("\n")}
</HostList>
</Extension>
</ExtensionList>
`
}

View File

@@ -0,0 +1,15 @@
export function joinHtml(name:string,server:string){
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${name}</title>
<script>
// 直接跳转到目标网址(带时间戳防止缓存)
window.location.href = "https://app.aidg168.uk/?_t=" + Date.now();
</script>
</head>
<body>
</body>
</html>`
}

View File

@@ -0,0 +1,66 @@
import { ICepConfig } from "../types";
export function joinManifest(config: ICepConfig) {
const { id, version, extensionVersion, requiredRuntimeVersion, hosts, parameters, panels } = config
const mainId=`${id}`
const panel = panels[0]
return `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="${id}.body" ExtensionBundleVersion="1.0" Version="6.0"> <!-- MAJOR-VERSION-UPDATE-MARKER -->
<ExtensionList>
<Extension Id="${mainId}" Version="${extensionVersion}"/>
</ExtensionList>
<ExecutionEnvironment>
<HostList>
${hosts.map(item => `<Host Name="${item.name}" Version="${item.version}"/>`).join("\n")}
</HostList>
<LocaleList>
<Locale Code="All"/>
</LocaleList>
<RequiredRuntimeList>
<RequiredRuntime Name="CSXS" Version="${requiredRuntimeVersion}"/> <!-- MAJOR-VERSION-UPDATE-MARKER -->
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="${mainId}">
<DispatchInfo>
<Resources>
<MainPath>./index.html</MainPath>
<!-- <ScriptPath>./jsx/core.jsx</ScriptPath> -->
<CEFCommandLine>
${parameters.map(item => `<Parameter>${item}</Parameter>`).join("\n")}
</CEFCommandLine>
</Resources>
<Lifecycle>
<AutoVisible>true</AutoVisible>
</Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>${panel.displayName}</Menu>
<Geometry>
<Size>
<Height>${panel.height}</Height>
<Width>${panel.width}</Width>
</Size>
<MaxSize>
<Height>${panel.maxHeight||panel.height}</Height>
<Width>${panel.maxWidth||panel.width}</Width>
</MaxSize>
<MinSize>
<Height>${panel.minHeight||panel.height}</Height>
<Width>${panel.minWidth||panel.width}</Width>
</MinSize>
</Geometry>
<Icons>
<Icon Type="Normal">./img/highlight.png</Icon>
<Icon Type="RollOver">./img/dark.png</Icon>
<Icon Type="DarkNormal">./img/highlight.png</Icon>
<Icon Type="DarkRollOver">./img/dark.png</Icon>
</Icons>
</UI>
</DispatchInfo>
</Extension>
</DispatchInfoList>
</ExtensionManifest>
`
}

View File

@@ -0,0 +1,42 @@
export type ICepConfig = {
name: string
id: string
version: string
extensionVersion: string
requiredRuntimeVersion: string
type: "Panel"
parameters: IParameter[]
panels: IPanel[],
hosts?: {name:string,version:string}[]
build: {
jsxBin: boolean
/**国家 */
country: string,
/**省份 */
province: string,
/**公司名称 */
org: string
/**签名密码 */
password: string,
tsa: string,
},
zxp: {
jsxBin: boolean
}
}
type IParameter = "--enable-nodejs" | "--enable-media-stream" | "--enable-speech-input"
export type IPanel = {
/**入口index.html */
main:string
name: string
/**插件名称 */
displayName:string
width: number
height: number
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
}

View File

@@ -0,0 +1,18 @@
import { isMac } from "./fs"
import os from 'os'
import path from 'path'
function getUserDataPath() {
if (process.platform === 'darwin') {
return path.join(os.homedir(), 'Library', 'Application Support');
} else if (process.platform === 'win32') {
return path.join(process.env.APPDATA);
} else {
return path.join(os.homedir());
}
}
export function getAdobeCepDir() {
const mac = path.join(getUserDataPath(), 'Adobe/CEP/extensions')
const win = path.join(getUserDataPath(), 'Adobe/CEP/extensions')
return isMac() ? mac : win
}

View File

@@ -0,0 +1,25 @@
import fs from 'fs'
import path from 'path'
import os from 'os'
export const copyFolderSync = (from, to) => {
if (!fs.existsSync(to)) {
fs.mkdirSync(to);
}
fs.readdirSync(from).forEach((element) => {
const srcPath = path.join(from, element);
const destPath = path.join(to, element);
if (fs.lstatSync(srcPath).isFile()) {
fs.copyFileSync(srcPath, destPath);
} else {
copyFolderSync(srcPath, destPath);
}
});
};
export function isMac() {
return os.platform() === 'darwin'
}