├── .env.production ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── cep.config.ts ├── create-bolt-cep ├── .gitattributes ├── .gitignore ├── .vscode │ └── settings.json ├── LICENSE ├── README.md ├── package.json ├── src │ └── index.ts ├── tsconfig.json └── yarn.lock ├── package.json ├── package.react.jsonc ├── package.svelte.jsonc ├── package.vue.jsonc ├── src ├── js │ ├── assets │ │ ├── adobe.svg │ │ ├── bolt-cep-react.png │ │ ├── bolt-cep-vue.png │ │ ├── bolt-cep.svg │ │ ├── built-with-bolt-cep.png │ │ ├── built-with-bolt-cep │ │ │ ├── Built_With_BOLT_CEP_Logo_Black_V01.png │ │ │ ├── Built_With_BOLT_CEP_Logo_Black_V01.svg │ │ │ ├── Built_With_BOLT_CEP_Logo_White_V01.png │ │ │ └── Built_With_BOLT_CEP_Logo_White_V01.svg │ │ ├── create-bolt-cep--demo.gif │ │ ├── node-js.svg │ │ ├── react.svg │ │ ├── sass.svg │ │ ├── svelte.svg │ │ ├── typescript.svg │ │ ├── vite.svg │ │ └── vue.svg │ ├── favicon.svg │ ├── global.d.ts │ ├── index.scss │ ├── lib │ │ ├── cep │ │ │ ├── .gitattributes │ │ │ ├── cep-types.d.ts │ │ │ ├── cep_engine_extensions.js │ │ │ ├── csinterface.d.ts │ │ │ ├── csinterface.js │ │ │ ├── es-types │ │ │ │ └── index.ts │ │ │ ├── node.ts │ │ │ ├── vulcan.d.ts │ │ │ └── vulcan.js │ │ └── utils │ │ │ ├── aeft.ts │ │ │ ├── bolt.ts │ │ │ ├── cep.ts │ │ │ ├── init-cep.ts │ │ │ └── ppro.ts │ ├── main │ │ ├── env.d.ts │ │ ├── index-react.tsx │ │ ├── index-svelte.ts │ │ ├── index-vue.ts │ │ ├── index.html │ │ ├── main.scss │ │ ├── main.svelte │ │ ├── main.tsx │ │ ├── main.vue │ │ └── vite-env.d.ts │ ├── variables.scss │ └── vite-env.d.ts ├── jsx │ ├── aeft │ │ ├── aeft-utils.ts │ │ ├── aeft.ts │ │ └── tsconfig.json │ ├── ame │ │ ├── ame.ts │ │ └── tsconfig.json │ ├── anim │ │ ├── anim.ts │ │ └── tsconfig.json │ ├── audt │ │ ├── audt.ts │ │ └── tsconfig.json │ ├── global.d.ts │ ├── idsn │ │ ├── idsn.ts │ │ └── tsconfig.json │ ├── ilst │ │ ├── ilst.ts │ │ └── tsconfig.json │ ├── index.ts │ ├── kbrg │ │ ├── kbrg.ts │ │ └── tsconfig.json │ ├── lib │ │ ├── .gitattributes │ │ └── json2.js │ ├── phxs │ │ ├── phxs-utils.ts │ │ ├── phxs.ts │ │ └── tsconfig.json │ ├── ppro │ │ ├── ppro-utils.ts │ │ ├── ppro.ts │ │ └── tsconfig.json │ ├── tsconfig.json │ └── utils │ │ ├── samples.ts │ │ └── utils.ts └── shared │ ├── shared.ts │ └── universals.d.ts ├── tsconfig-build.json ├── tsconfig.json ├── tsconfig.react.json ├── tsconfig.svelte.json ├── tsconfig.vue.json ├── vite-cep-plugin ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── scripts │ ├── copy-files.js │ └── copy-files.ts ├── src │ ├── bin │ │ ├── ZXPSignCmd │ │ └── ZXPSignCmd.exe │ ├── cep-config.ts │ ├── copy-node.ts │ ├── index.ts │ ├── lib │ │ ├── lib.ts │ │ ├── node-built-ins.ts │ │ ├── require-js.js │ │ └── zxp.ts │ └── templates │ │ ├── debug-template.ts │ │ ├── dev-html-template.ts │ │ ├── extension-template.ts │ │ ├── manifest-template.ts │ │ └── menu-html-template.ts ├── tsconfig.json └── yarn.lock ├── vite.config.ts ├── vite.es.config.ts └── yarn.lock /.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV=production -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: ZXP Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*.*.*" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | build: 13 | runs-on: windows-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18.x] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - run: npm i --legacy-peer-deps 25 | - run: npm run zxp 26 | 27 | - name: GitHub Release 28 | uses: softprops/action-gh-release@v1 29 | if: startsWith(github.ref, 'refs/tags/') 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | SOURCE_NAME: ${{ steps.extract_names.outputs.SOURCE_NAME }} 33 | SOURCE_BRANCH: ${{ steps.extract_names.outputs.SOURCE_BRANCH }} 34 | SOURCE_TAG: ${{ steps.extract_names.outputs.SOURCE_TAG }} 35 | with: 36 | files: "./dist/zxp/*" 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | dist 7 | yarn-error.log 8 | .media -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": [ 5 | "*.jsonc", 6 | "devcontainer.json", 7 | "jsconfig.json", 8 | "language-configuration.json", 9 | "tsconfig.json" 10 | ], 11 | "options": { 12 | "trailingComma": "none" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.tabWidth": 2, 3 | "prettier.useTabs": false, 4 | "prettier.printWidth": 80 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Hyper Brew LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Bolt CEP 2 | 3 | A lightning-fast boilerplate for building Adobe CEP Extensions in React, Vue, or Svelte built on Vite + TypeScript + Sass 4 | 5 | ![npm](https://img.shields.io/npm/v/bolt-cep) 6 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/hyperbrew/bolt-cep/blob/master/LICENSE) 7 | [![Chat](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/PC3EvvuRbc) 8 | 9 | ## Features 10 | 11 | - Lightning Fast Hot Module Replacement (HMR) 12 | - Write Modern ES6 in both the JavaScript and ExtendScript layers 13 | - Type-safe ExtendScript with Types-for-Adobe 14 | - End-to-End Type Safety with evalTS() 15 | - Easily configure in cep.config.ts 16 | - Setup for single or multi-panel extensions 17 | - Comes with multi-host-app configuration 18 | - Optimized Build Size 19 | - Easy Publish to ZXP for Distribution 20 | - Easy Package to ZIP archive with sidecar assets 21 | - GitHub Actions ready-to-go for ZXP Releases 22 | 23 | _Full Blog Post:_ https://hyperbrew.co/blog/bolt-cep-build-extensions-faster/ 24 | 25 | ### Compatibility 26 | 27 | - [Adobe CC Apps](https://www.adobe.com/creativecloud/desktop-app.html) version 2022 or later 28 | - Windows & Mac Intel 29 | - Mac Arm64 (M1-M4) require special setup ([more details](#misc-troubleshooting)) 30 | 31 | --- 32 | 33 | ## Backers 34 | 35 | Huge thanks to our backers who have made this project possible! 36 | 37 | 38 | Battle Axe 39 | 40 | If you're interested in supporting this open-source project, please [contact the Hyper Brew team](https://hyperbrew.co/contact/). 41 | 42 | --- 43 | 44 | ## Tools Built with Bolt CEP 45 | 46 | Tools like Rubberhose 3, Klutz GPT, Brevity, and more are powered by Bolt CEP! Check out the full library of tools built with Bolt CEP: 47 | 48 | [Built with Bolt CEP](https://hyperbrew.co/resources/bolt-cep/) 49 | 50 | Battle Axe 51 | 52 | --- 53 | 54 | ## Support 55 | 56 | ### Free Support 🙌 57 | 58 | If you have questions with getting started using Bolt CEP, feel free to ask and discuss in our free Discord community [Discord Community](https://discord.gg/PC3EvvuRbc). 59 | 60 | ### Paid Priority Support 🥇 61 | 62 | If your team is interested in paid consulting or development with Bolt CEP, please [contact the Hyper Brew team](https://hyperbrew.co/contact/). More info on our [Adobe Plugin Development & Consulting Services](https://hyperbrew.co/landings/boost-development) 63 | 64 | --- 65 | 66 | ## Can I use Bolt CEP in my free or commercial project? 67 | 68 | Yes! Bolt CEP is **100% free and open source**, being released under the MIT license with no attribution required. This means you are free to use it in your free or commercial projects. 69 | 70 | We would greatly appreciate it if you could provide a link back to this tool's info page in your product's site or about page: 71 | 72 | Bolt CEP Info Page Link: https://hyperbrew.co/resources/bolt-cep 73 | 74 | **Built with Bolt CEP** button graphics: 75 | 76 | **PNG Files** 77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 | **SVG Files** 88 | 89 |
90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | ## Prerequisites 98 | 99 | - [Node.js 18](https://nodejs.org/en/) or later 100 | - Package manager either 101 | - NPM (comes with Node.js) 102 | - [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/) ( ensure by running `yarn set version classic` ) 103 | - [PNPM](https://pnpm.io/installation) ( ensure by running `pnpm --version` ) 104 | 105 | ## Quick Start 106 | 107 | Bolt CEP 108 | 109 | Create your new Bolt CEP project (follow CLI prompts) 110 | 111 | - yarn - `yarn create bolt-cep` 112 | - npm - `npx create-bolt-cep` 113 | - pnpm - `pnpm create-bolt-cep` 114 | 115 | Change directory to the new project 116 | 117 | - `cd project` 118 | 119 | Install Dependencies (if not already done by create command) 120 | 121 | - yarn - `yarn` 122 | - npm - `npm i` 123 | - pnpm - `pnpm i` 124 | 125 | **⚠️ Enable PlayerDebugMode** 126 | 127 | - Adobe CEP's PlayerDebugMode must be enabled on your machine to test `yarn build` or `yarn dev` builds. Only an installed ZXP with `yarn zxp` will work without PlayerDebugMode enabled. 128 | - Enable this easily with the [aescripts ZXP Installer](https://aescripts.com/learn/zxp-installer/) > Settings > Debug > Enable Debugging 129 | - Or enable manually per OS by following the CEP Cookbook Instructions: [Adobe CEP 12 Cookbook](https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_12.x/Documentation/CEP%2012%20HTML%20Extension%20Cookbook.md#debugging-unsigned-extensions) 130 | 131 | Build the extension (must run before `dev`, can also run after for panel to work statically without the process) Symlink is created to extensions folder. 132 | 133 | - yarn `yarn build` 134 | - npm `npm run build` 135 | - pnpm `pnpm build` 136 | 137 | Run the extension in HMR Hot-reload mode for rapid development. Both JS and ExtendScript folders re-build on changes. 138 | Viewable in browser via localhost:3000/panel/ (see [Panel Structure](#cep-panel-structure) to set up multiple panels) 139 | 140 | - yarn `yarn dev` 141 | - npm `npm run dev` 142 | - pnpm `pnpm dev` 143 | 144 | Build & Package the extension as a ZXP for delivery to the `dist/zxp` folder (install with [aescripts ZXP Installer](https://aescripts.com/learn/zxp-installer/) or another ZXP installer) 145 | 146 | - yarn `yarn zxp` 147 | - npm `npm run zxp` 148 | - pnpm `pnpm zxp` 149 | 150 | Bundles your packaged zxp file and specified assets from `copyZipAssets` to a zip archive in the `./zip` folder 151 | 152 | - yarn `yarn zip` 153 | - npm `npm run zip` 154 | - pnpm `pnpm zip` 155 | 156 | --- 157 | 158 | ## Config 159 | 160 | Update your CEP build and package settings in `cep.config.ts` safely typed 161 | 162 | Start building your app per framework in: 163 | 164 | - `src/js/main/main.tsx` 165 | - `src/js/main/main.vue` 166 | - `src/js/main/main.svelte` 167 | 168 | Write ExtendScript code in `src/jsx/main.ts` 169 | 170 | --- 171 | 172 | ## CEP Panel Structure 173 | 174 | Each panel is treated as it's own page, with shared code for efficiency. The Boilerplate currently comes with 2 panels, `main` and `settings`. These are configured in the `cep.config.ts`. 175 | 176 | Each panel can be edited in their respective folders: 177 | 178 | ``` 179 | src 180 | └─ js 181 | ├─ main 182 | │ ├─ index.html 183 | | └─ index.tsx 184 | └─ settings 185 | ├─ index.html 186 | └─ index.tsx 187 | ``` 188 | 189 | To add panels, add an item to the panels object in `cep.config.ts`, and duplicate the folder structure and adjust as needed. 190 | 191 | --- 192 | 193 | ## ExtendScript 194 | 195 | ExtendScript can be written in ES6 and will be compiled down to a single ES3 file for compatibility. 196 | 197 | JSON 2 is included by default, and any external JS libraries added with the include directive will be bundled as well: 198 | 199 | ```js 200 | // @include './lib/library.js' 201 | ``` 202 | 203 | App-specific code is split into modules for type-safe development by the application's name as seen in the `index.ts`. 204 | 205 | ``` 206 | aftereffects >> aeft/aeft.ts 207 | illustrator >> ilst/ilst.ts 208 | animate >> anim/anim.ts 209 | ``` 210 | 211 | Write your app-specific functions in each of these separate modules, and they will be required per each application. 212 | 213 | To add support for additional host apps: 214 | 215 | - Add additional app module files (aeft.ts, anim.ts, etc). 216 | - Extend the main `switch()` in `scr/jsx/index.ts` with your additional. 217 | - Add the host to your `cep.config.ts` file. 218 | 219 | --- 220 | 221 | ## Calling ExtendScript from CEP JavaScript 222 | 223 | All ExtendScript function are appended to your panel's namespace in the background to avoid namespace clashes when using `evalTS()` and `evalES()`. 224 | 225 | We have now introduced a new and improved end-to-end type-safe way to interact with ExtendScript from CEP using `evalTS()`. This function dynamically infers types from 226 | ExtendScript functions and handles both stringifying and parsing of the results so your developer interaction can be as simple as possible. 227 | 228 | As demonstrated in `main.tsx`, your ExtendScript functions can be called with `evalTS()` by passing the name of the function, followed by the arguments. 229 | 230 | CEP 231 | 232 | ```js 233 | evalTS("myFunc", "test").then((res) => { 234 | console.log(res); 235 | }); 236 | 237 | evalTS("myFuncObj", { height: 90, width: 100 }).then((res) => { 238 | console.log(res.x); 239 | console.log(res.y); 240 | }); 241 | ``` 242 | 243 | ExtendScript 244 | 245 | ```js 246 | export const myFunc = (str: string) => { 247 | return str; 248 | }; 249 | 250 | export const myFuncObj = (obj: { height: number, width: number }) => { 251 | return { 252 | y: obj.height, 253 | x: obj.width, 254 | }; 255 | }; 256 | ``` 257 | 258 | For any existing Bolt CEP projects, rest assured that the legacy `evalES()` function remains in place as usual as demonstrated in `main.tsx`. 259 | 260 | ```js 261 | evalES(`helloWorld("${csi.getApplicationID()}")`); 262 | ``` 263 | 264 | You will also want to use this function for calling ExtendScript functions in the global scope directly, by passing `true` to the second parameter: 265 | 266 | ```js 267 | evalES( 268 | `alert("Hello from ExtendScript :: " + app.appName + " " + app.version)`, 269 | true, 270 | ); 271 | ``` 272 | 273 | --- 274 | 275 | ## Calling CEP JavaScript from ExtendScript 276 | 277 | For certain situations such as hooking into event listeners or sending updates during long functions, it makes sense to trigger events from the ExtendScript environment to the JavaScript environment. This can be done with `listenTS()` and `dispatchTS()`. 278 | 279 | Using this method accounts for: 280 | 281 | - Setting up a scoped listener on the JS side for the CSEvent 282 | - Setting up PlugPlug CSEvent event on ExtendScript side 283 | - Ensuring End-to-End Type-Safety for the event 284 | 285 | ### 1. Declare the Event Type in EventTS in shared/universals.ts 286 | 287 | ```js 288 | export type EventTS = { 289 | myCustomEvent: { 290 | oneValue: string, 291 | anotherValue: number, 292 | }, 293 | // [... other events] 294 | }; 295 | ``` 296 | 297 | ### 2. Listen in CEP JavaScript 298 | 299 | ```js 300 | import { listenTS } from "../lib/utils/bolt"; 301 | 302 | listenTS("myCustomEvent", (data) => { 303 | console.log("oneValue is", data.oneValue); 304 | console.log("anotherValue is", data.anotherValue); 305 | }); 306 | ``` 307 | 308 | ### 3. Dispatch in ExtendScript 309 | 310 | ```js 311 | import { dispatchTS } from "../utils/utils"; 312 | 313 | dispatchTS("myCustomEvent", { oneValue: "name", anotherValue: 20 }); 314 | ``` 315 | 316 | Alternatively, `dispatchTS()` can also be used in the same way from the CEP side to trigger events within or between CEP panels, just ensure you're importing the dispatchTS() function from the correct file within the `js` folder. 317 | 318 | ```js 319 | import { dispatchTS } from "../lib/utils/bolt"; 320 | 321 | dispatchTS("myCustomEvent", { oneValue: "name", anotherValue: 20 }); 322 | ``` 323 | 324 | --- 325 | 326 | ## GitHub Actions ZXP Releases 327 | 328 | This repo comes with a configured GitHub Action workflow to build a ZXP and add to the releases each time a git tag is added. 329 | 330 | ``` 331 | git tag 1.0.0 332 | git push origin --tags 333 | ``` 334 | 335 | Then your new build will be available under GitHub Releases. For more info, see the [YML config](.github\workflows\main.yml) 336 | 337 | --- 338 | 339 | --- 340 | 341 | ## Copy Assets 342 | 343 | If you have assets that you would like copied without being affected by the bundler, you can add the optional `copyAssets:[]` array inside your cep.config.ts to include files or entire folders. 344 | 345 | ```js 346 | copyAssets: ["public", "custom/my.jsx"], 347 | ``` 348 | 349 | **Example:** 350 | 351 | Files placed in `src/public` will be copied to `dist/public` with config set to `copyAssets: ["public"]`. 352 | 353 | --- 354 | 355 | --- 356 | 357 | ## Copy Zip Assets 358 | 359 | If you have assets that you would like copied with your zxp into a zip archive for delivery, you can add the optional `copyZipAssets:[]` array inside your cep.config.ts to include files or entire folders. A folder ending in "/\*" will copy the contents without the folder structure into the zip destination. 360 | 361 | ```js 362 | copyZipAssets: ["instructions/*", "icons"], 363 | ``` 364 | 365 | --- 366 | 367 | ## Custom Ponyfills 368 | 369 | Unlike Polyfills which modify the global prototype, Ponyfills replace functionality with custom methods. Built-in Ponyfills include: 370 | 371 | - Object.freeze() 372 | - Array.isArray() 373 | 374 | You can add your own Ponyfils by passing them into the `jsxPonyfill()` function in `vite.es.config.ts`: 375 | 376 | ```js 377 | jsxPonyfill([ 378 | { 379 | find: "Array.isArray", 380 | replace: "__isArray", 381 | inject: `function __isArray(arr) { try { return arr instanceof Array; } catch (e) { return false; } };`, 382 | }, 383 | ]); 384 | ``` 385 | 386 | If you have a common Ponyfill you feel should be built-in, create a ticket and we'll look into it. 387 | 388 | --- 389 | 390 | ## ExtendScript Scope 391 | 392 | This boilerplate is flavored for a single JSX object attached to helper object `$` for all your panels to prevent pollution in the global namespace. If you prefer to include your own raw JSX, include it in the Copy Assets object (above), and add the optional scriptPath object to your cep.config.ts file. 393 | 394 | ```js 395 | panels: [ 396 | { 397 | name: "main", 398 | scriptPath: "custom/index.jsx", 399 | [...] 400 | }, 401 | { 402 | name: "settings", 403 | scriptPath: "custom/settings.jsx", 404 | [...] 405 | }, 406 | ], 407 | copyAssets: ["custom"], 408 | ``` 409 | 410 | --- 411 | 412 | ## Troubleshooting Modules 413 | 414 | Node.js Built-in modules can be imported from the `src/js/lib/node.ts` file. 415 | 416 | ```js 417 | import { os, path, fs } from "../lib/node"; 418 | ``` 419 | 420 | To use 3rd party libraries, first attempt to use with the standard import syntax. 421 | 422 | ```js 423 | import { FaBolt } from "react-icons/fa"; 424 | ``` 425 | 426 | If the import syntax fails (typically with modules that use the Node.js runtime) you can resort to the Node.js `require()` syntax, 427 | 428 | ```js 429 | const unzipper = require("unzipper"); 430 | ``` 431 | 432 | The build system will detect any non-built-in Node.js modules using `require()` and copy them to the output `node_modules` folder, but if a package is missed, you can add it explicitly to the `installModules:[]` array inside your `cep.config.ts` file. 433 | 434 | ```js 435 | installModules: ["unzipper"], 436 | ``` 437 | 438 | Also if they're Node.js-specific modules, it's best to place the requires inside functions so they are only required at runtime and don't break your panel when previewing in the browser. 439 | 440 | --- 441 | 442 | ## A Note on Routers 443 | 444 | If you would like to set up a routing system like react-router, be aware that you'll have to make adjustments for CEP. React Router for instance bases the router path off of `window.location.pathname` which in the browser resolves to the page: 445 | 446 | `/main/index.html` 447 | 448 | yet in CEP context resolves to the full system path: 449 | 450 | `file:///C:/Users/Username/AppData/Roaming/Adobe/CEP/extensions/com.bolt.cep/main/index.html` 451 | 452 | To solve this, you'll need to adjust the router basename for each context, here is one way of accomplishing that with the panel named `main`: 453 | 454 | ```js 455 | const posix = (str: string) => str.replace(/\\/g, "/"); 456 | 457 | const cepBasename = window.cep_node 458 | ? `${posix(window.cep_node.global.__dirname)}/` 459 | : "/main/"; 460 | 461 | ReactDOM.render( 462 | 463 | [...] 464 | , 465 | document.getElementById("app") 466 | ); 467 | ``` 468 | 469 | ## Misc Troubleshooting 470 | 471 | **React Spectrum won't allow certain UI items to be clicked on MacOS**: 472 | 473 | There is an ongoing bug with React Spectrum and other UI libraries on MacOS with clicking elements. To resolve this issue, run the helper function `enableSpectrum()` to resolve this issue on Mac. 474 | 475 | `main.ts` 476 | 477 | ```js 478 | import { initBolt, enableSpectrum } from "../lib/utils/bolt"; 479 | 480 | enableSpectrum(); 481 | initBolt(); 482 | ``` 483 | 484 | Additionally, some users have reported that adding the following CEF Flag will resolve the issue as well: 485 | 486 | `cep.config.ts` 487 | 488 | ```js 489 | ... 490 | parameters: [ 491 | ... 492 | '--disable-site-isolation-trials' 493 | ], 494 | ... 495 | ``` 496 | 497 | **ZXPSignCmd Fails on Mac or Windows**: 498 | 499 | 4/18/2025 ZXPSignCmd broke on Windows across the board and on MacOS for most TSA services. ( [more info](https://community.adobe.com/t5/premiere-pro-bugs/zxpsigncmd-sign-process-is-broken-segmentation-fault/idc-p/15276912#M49107) ) 500 | 501 | 4/30/2025 Adobe fixed the issues for Windows and MacOS, and we have included the updated ZXPSignCmd for both OS's in the latest release of `vite-cep-plugin@1.2.9`. 502 | 503 | To use the latest in your existing Bolt CEP project, run `yarn add vite-cep-plugin`, and make sure your `zxp.tsa` settings in `cep.config.ts` match the [latest format](./cep.config.ts). 504 | 505 | **Build Issues on Mac Arm64 Apple Silicon Machines (M1/M2/M3)** 506 | 507 | 5/13/2025 - jsxbin is now natively supported on Apple Silicon machines. More info here: [Setup ExtendScript Dev for Apple Silicon Macs](https://hyperbrew.co/blog/adobe-extendscript-support-for-apple-silicon/) 508 | 509 | **Update a Bolt CEP Project** To update an existing Bolt CEP project to the the latest version, create a new Bolt CEP project with the same framework (React, Vue, Svelte), then compare and update the following files: 510 | 511 | 1. `package.json` - Update all dependencies and scripts ( `vite-cep-plugin` - usually contains the most frequent updates ) 512 | 2. `vite.config.ts` - Unless you've modified the vite config yourself, you can just copy the contents of the latest into yours. 513 | 3. `vite.es.config.ts` - Like the previous config, unless you've modified it yourself, you can just copy the contents of the latest into yours. 514 | 4. `cep.config.ts` - Check if any new properties have been added that don't exist in your config. 515 | 5. `src/js/lib` - Update this entire folder. 516 | 6. `src/jsx/index.ts` - Check if any new properties have been added that don't exist in your config. 517 | 7. `src/shared/universals.d.ts` - Check if any new properties have been added that don't exist in your config. 518 | 519 | **ZXPSignCmd Permissions issues on Mac**: 520 | 521 | Previously, you would need to fix the permissions of ZXPSignCmd by running the following command in the terminal, however this is now automated since `vite-cep-plugin@1.1.15`. If you still experience problems other reasons you can manually fix the executable as follows: 522 | 523 | If you're getting permissions errors running ZXPSignCmd on the latest Mac releases, try a fresh clone. If that does't work, reset permissions for ZXPSignCmd by opening the directory `node_modules/vite-cep-plugin/lib/bin` and running `chmod 700 ./ZXPSignCmd`. 524 | -------------------------------------------------------------------------------- /cep.config.ts: -------------------------------------------------------------------------------- 1 | import { CEP_Config } from "vite-cep-plugin"; 2 | import { version } from "./package.json"; 3 | 4 | const config: CEP_Config = { 5 | version, 6 | id: "com.bolt.cep", // BOLT_ID_REPLACE 7 | displayName: "Bolt CEP", // BOLT_DISPLAYNAME_REPLACE 8 | symlink: "local", 9 | port: 3000, 10 | servePort: 5000, 11 | startingDebugPort: 8860, 12 | extensionManifestVersion: 6.0, 13 | requiredRuntimeVersion: 9.0, 14 | hosts: [ 15 | { name: "AEFT", version: "[0.0,99.9]" }, // BOLT_AEFT_ONLY 16 | { name: "AME", version: "[0.0,99.9]" }, // BOLT_AME_ONLY 17 | { name: "FLPR", version: "[0.0,99.9]" }, // BOLT_ANIM_ONLY 18 | { name: "AUDT", version: "[0.0,99.9]" }, // BOLT_AUDT_ONLY 19 | { name: "IDSN", version: "[0.0,99.9]" }, // BOLT_IDSN_ONLY 20 | { name: "ILST", version: "[0.0,99.9]" }, // BOLT_ILST_ONLY 21 | { name: "KBRG", version: "[0.0,99.9]" }, // BOLT_KBRG_ONLY 22 | { name: "PHXS", version: "[0.0,99.9]" }, // BOLT_PHXS_ONLY 23 | { name: "PPRO", version: "[0.0,99.9]" }, // BOLT_PPRO_ONLY 24 | ], 25 | 26 | type: "Panel", 27 | iconDarkNormal: "./src/assets/light-icon.png", 28 | iconNormal: "./src/assets/dark-icon.png", 29 | iconDarkNormalRollOver: "./src/assets/light-icon.png", 30 | iconNormalRollOver: "./src/assets/dark-icon.png", 31 | parameters: ["--v=0", "--enable-nodejs", "--mixed-context"], 32 | width: 500, 33 | height: 550, 34 | 35 | panels: [ 36 | { 37 | mainPath: "./main/index.html", 38 | name: "main", 39 | panelDisplayName: "Bolt CEP", // BOLT_DISPLAYNAME_REPLACE 40 | autoVisible: true, 41 | width: 600, 42 | height: 650, 43 | }, 44 | ], 45 | build: { 46 | jsxBin: "off", 47 | sourceMap: true, 48 | }, 49 | zxp: { 50 | country: "US", 51 | province: "CA", 52 | org: "Company", 53 | password: "password", 54 | tsa: [ 55 | "http://timestamp.digicert.com/", // Windows Only 56 | "http://timestamp.apple.com/ts01", // MacOS Only 57 | ], 58 | allowSkipTSA: false, 59 | sourceMap: false, 60 | jsxBin: "off", 61 | }, 62 | installModules: [], 63 | copyAssets: [], 64 | copyZipAssets: [], 65 | }; 66 | export default config; 67 | -------------------------------------------------------------------------------- /create-bolt-cep/.gitattributes: -------------------------------------------------------------------------------- 1 | *.ts linguist-language=TypeScript -------------------------------------------------------------------------------- /create-bolt-cep/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | test* 4 | .DS_Store 5 | yarn-error.log 6 | .test* -------------------------------------------------------------------------------- /create-bolt-cep/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.tabWidth": 2, 3 | "prettier.useTabs": false, 4 | "prettier.printWidth": 80 5 | } 6 | -------------------------------------------------------------------------------- /create-bolt-cep/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Hyper Brew LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /create-bolt-cep/README.md: -------------------------------------------------------------------------------- 1 | # Create Bolt CEP 2 | 3 | ![npm](https://img.shields.io/npm/v/create-bolt-cep) 4 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/hyperbrew/create-bolt-cep/blob/master/LICENSE) 5 | [![Chat](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/PC3EvvuRbc) 6 | 7 | Generate a new CEP Extension from the Bolt CEP boilerplate 8 | 9 | https://github.com/hyperbrew/bolt-cep 10 | 11 | Usage Examples: 12 | 13 | `yarn create bolt-cep` 14 | -------------------------------------------------------------------------------- /create-bolt-cep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-bolt-cep", 3 | "version": "2.0.2", 4 | "license": "MIT", 5 | "bin": { 6 | "create-bolt-cep": "dist/index.js" 7 | }, 8 | "files": [ 9 | "dist/*" 10 | ], 11 | "main": "dist/index.js", 12 | "engines": { 13 | "node": ">=16.0.0" 14 | }, 15 | "scripts": { 16 | "build": "tsc", 17 | "dev": "tsc --watch", 18 | "prepare": "npm run build", 19 | "publish": "npm publish --access public", 20 | "start": "node dist/index.js", 21 | "test": "node scripts/test.js" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/hyper-brew/bolt-cep.git", 26 | "directory": "packages/create-vite" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/hyper-brew/bolt-cep/issues" 30 | }, 31 | "dependencies": { 32 | "bolt-cep": "^2.0.0", 33 | "meta-bolt": "^0.0.15" 34 | }, 35 | "devDependencies": {} 36 | } 37 | -------------------------------------------------------------------------------- /create-bolt-cep/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { main } from "meta-bolt"; 4 | import type { BoltInitData, ArgOpt } from "meta-bolt"; 5 | 6 | export const frameworkOptions: ArgOpt[] = [ 7 | { 8 | value: "svelte", 9 | label: "Svelte", 10 | files: [ 11 | "src/js/main/index-svelte.ts", 12 | "src/js/main/main.svelte", 13 | "src/js/main/vite-env.d.ts", 14 | "package.svelte.jsonc", 15 | "tsconfig.svelte.json", 16 | ], 17 | }, 18 | { 19 | value: "react", 20 | label: "React", 21 | files: [ 22 | "src/js/main/index-react.tsx", 23 | "src/js/main/main.tsx", 24 | "package.react.jsonc", 25 | "tsconfig.react.json", 26 | ], 27 | }, 28 | { 29 | value: "vue", 30 | label: "Vue", 31 | files: [ 32 | "src/js/main/index-vue.ts", 33 | "src/js/main/main.vue", 34 | "src/js/main/env.d.ts", 35 | "package.vue.jsonc", 36 | "tsconfig.vue.json", 37 | ], 38 | }, 39 | ]; 40 | 41 | export const appOptions: ArgOpt[] = [ 42 | { value: "aeft", label: "After Effects", files: ["src/jsx/aeft"] }, 43 | { value: "ppro", label: "Premiere Pro", files: ["src/jsx/ppro"] }, 44 | { value: "phxs", label: "Photoshop", files: ["src/jsx/phxs"] }, 45 | { value: "ilst", label: "Illustrator", files: ["src/jsx/ilst"] }, 46 | { value: "idsn", label: "InDesign", files: ["src/jsx/idsn"] }, 47 | { value: "anim", label: "Animate", files: ["src/jsx/anim"] }, 48 | { value: "ame", label: "Media Encoder", files: ["src/jsx/ame"] }, 49 | { value: "kbrg", label: "Bridge", files: ["src/jsx/kbrg"] }, 50 | { value: "audt", label: "Audition", files: ["src/jsx/audt"] }, 51 | ]; 52 | 53 | const initData: BoltInitData = { 54 | intro: { 55 | name: "create-bolt-cep", 56 | prettyName: "Bolt CEP", 57 | }, 58 | base: { 59 | module: "bolt-cep", 60 | createDirName: __dirname, 61 | globalIncludes: [ 62 | "*", 63 | "src/**/*", 64 | ".github/**/*", 65 | ".gitignore", 66 | ".npmrc", 67 | ".prettierrc", 68 | ".env.example", 69 | ], 70 | globalExcludes: [".env", "yarn-error.log", "package.json", "tsconfig.json"], 71 | fileRenames: [ 72 | ["package.svelte.jsonc", "package.json"], 73 | ["package.react.jsonc", "package.json"], 74 | ["package.vue.jsonc", "package.json"], 75 | 76 | ["tsconfig.svelte.json", "tsconfig.json"], 77 | ["tsconfig.react.json", "tsconfig.json"], 78 | ["tsconfig.vue.json", "tsconfig.json"], 79 | 80 | [".npmignore", ".gitignore"], 81 | ], 82 | }, 83 | argsTemplate: [ 84 | { 85 | name: "folder", 86 | type: "folder", 87 | message: "Where do you want to create your project?", 88 | initialValue: "./", 89 | required: true, 90 | validator: (input: string) => { 91 | if (input.length < 3) return `Value is required!`; 92 | }, 93 | describe: "Name of the folder for the new Adobe extension ", 94 | }, 95 | { 96 | name: "displayName", 97 | type: "string", 98 | message: "Choose a unique Display Name for your extension:", 99 | initialValue: "Bolt CEP", 100 | required: true, 101 | validator: (input: string) => { 102 | if (input.length < 1) return `Value is required!`; 103 | }, 104 | describe: "Panel's display name", 105 | alias: "n", 106 | }, 107 | { 108 | name: "id", 109 | type: "string", 110 | message: "Choose a unique ID for your extension:", 111 | initialValue: "com.bolt.cep", 112 | required: true, 113 | validator: (input: string) => { 114 | if (input.length < 1) return `Value is required!`; 115 | }, 116 | describe: "Unique ID for your extension (e.g. com.bolt.cep)", 117 | alias: "i", 118 | }, 119 | { 120 | name: "framework", 121 | type: "select", 122 | message: "Select framework:", 123 | alias: "f", 124 | describe: "Select a Framework for your extension:", 125 | options: frameworkOptions, 126 | required: true, 127 | }, 128 | { 129 | name: "apps", 130 | type: "multiselect", 131 | message: "Select app:", 132 | alias: "a", 133 | describe: "Select app(s) for your extension:", 134 | options: appOptions, 135 | validator: (input: string[]) => { 136 | if (input.length < 1) return `At Least One value Required!`; 137 | }, 138 | required: true, 139 | }, 140 | { 141 | name: "installDeps", 142 | type: "boolean", 143 | message: "Install dependencies?", 144 | initialValue: true, 145 | required: true, 146 | alias: "d", 147 | describe: "Install dependencies (default: false)", 148 | }, 149 | { 150 | name: "sampleCode", 151 | type: "boolean", 152 | message: "Keep Sample Code Snippets?", 153 | initialValue: true, 154 | required: true, 155 | alias: "s", 156 | describe: "Keep Sample Code (default: true)", 157 | }, 158 | ], 159 | }; 160 | 161 | //* if not using as a module, run immediately 162 | console.log("BOLT_MODULEONLY", process.env.BOLT_MODULEONLY); 163 | if (!process.env.BOLT_MODULEONLY) main(initData); 164 | -------------------------------------------------------------------------------- /create-bolt-cep/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "strict": true, 7 | "allowJs": true, 8 | "skipLibCheck": true, 9 | "newLine": "lf", 10 | "outDir": "dist" 11 | }, 12 | "files": ["src/index.ts"], 13 | "exclude": ["node_modules"] 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bolt-cep", 3 | "version": "2.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "watch": "tsc && vite build --watch true", 7 | "build": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && vite build --watch false", 8 | "zxp": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZXP_PACKAGE=true vite build --watch false", 9 | "zip": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZIP_PACKAGE=true vite build --watch false", 10 | "serve": "cross-env SERVE_PANEL=true vite preview", 11 | "symlink": "cross-env BOLT_ACTION=symlink vite", 12 | "delsymlink": "cross-env BOLT_ACTION=delsymlink vite", 13 | "dep": "cross-env BOLT_ACTION=dependencyCheck vite" 14 | }, 15 | "dependencies": { 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "ts-node": "^10.9.1", 19 | "vue": "^3.2.45" 20 | }, 21 | "devDependencies": { 22 | "source-map": "^0.7.4", 23 | "@babel/core": "^7.19.0", 24 | "@babel/plugin-proposal-class-properties": "^7.16.0", 25 | "@babel/plugin-proposal-object-rest-spread": "^7.16.0", 26 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 27 | "@babel/plugin-transform-runtime": "^7.16.4", 28 | "@babel/preset-env": "^7.19.0", 29 | "@babel/preset-react": "^7.16.0", 30 | "@babel/preset-typescript": "^7.16.0", 31 | "@babel/runtime": "^7.16.3", 32 | "@rollup/plugin-babel": "^5.3.1", 33 | "@rollup/plugin-commonjs": "^21.0.2", 34 | "@rollup/plugin-image": "^2.1.1", 35 | "@rollup/plugin-json": "^4.1.0", 36 | "@rollup/plugin-node-resolve": "^13.1.3", 37 | "@rollup/plugin-replace": "^4.0.0", 38 | "@sveltejs/vite-plugin-svelte": "^1.2.0", 39 | "@tsconfig/svelte": "^3.0.0", 40 | "@types/fs-extra": "^9.0.13", 41 | "@types/node": "^16.11.7", 42 | "@types/react": "^18.0.24", 43 | "@types/react-dom": "^18.0.8", 44 | "@types/trusted-types": "^2.0.2", 45 | "@vitejs/plugin-react": "^3.0.1", 46 | "@vitejs/plugin-vue": "^4.0.0", 47 | "babel-plugin-react-require": "^3.1.3", 48 | "babel-plugin-transform-scss": "^1.1.0", 49 | "babel-preset-env": "^1.7.0", 50 | "core-js": "3", 51 | "cross-env": "^7.0.3", 52 | "fs-extra": "^10.0.0", 53 | "rimraf": "^3.0.2", 54 | "rollup": "^2.79.2", 55 | "rollup-plugin-multi-input": "^1.3.1", 56 | "rollup-plugin-node-copy": "^1.0.4", 57 | "rollup-plugin-scss": "^3.0.0", 58 | "sass": "^1.43.4", 59 | "svelte": "^4.2.19", 60 | "svelte-check": "^2.9.2", 61 | "svelte-preprocess": "^4.10.7", 62 | "types-for-adobe": "^7.2.3", 63 | "types-for-adobe-extras": "^0.0.9", 64 | "typescript": "^4.6.4", 65 | "vite": "^5.4.19", 66 | "vite-cep-plugin": "^2.0.0" 67 | }, 68 | "resolutions": { 69 | "typescript": "4.6.4" 70 | }, 71 | "main": "index.js", 72 | "license": "MIT", 73 | "files": [ 74 | "package.json", 75 | "package.react.jsonc", 76 | "package.svelte.jsonc", 77 | "package.vue.jsonc", 78 | "src/**/*", 79 | ".github/**/*", 80 | ".vscode/**/*", 81 | ".env.production", 82 | "tsconfig.json", 83 | "tsconfig.react.json", 84 | "tsconfig.svelte.json", 85 | "tsconfig.vue.json", 86 | "tsconfig-build.json", 87 | "README.md", 88 | "cep.config.ts", 89 | "vite.config.ts", 90 | "vite.es.config.ts", 91 | "yarn.lock", 92 | ".npmrc", 93 | ".gitignore" 94 | ] 95 | } 96 | -------------------------------------------------------------------------------- /package.react.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bolt-cep", // BOLT_ID_REPLACE 3 | "private": true, 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "vite", 7 | "watch": "tsc && vite build --watch true", 8 | "build": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && vite build --watch false", 9 | "zxp": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZXP_PACKAGE=true vite build --watch false", 10 | "zip": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZIP_PACKAGE=true vite build --watch false", 11 | "serve": "cross-env SERVE_PANEL=true vite preview", 12 | "symlink": "cross-env BOLT_ACTION=symlink vite", 13 | "delsymlink": "cross-env BOLT_ACTION=delsymlink vite" 14 | }, 15 | "dependencies": { 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0" 18 | }, 19 | "devDependencies": { 20 | "source-map": "^0.7.4", 21 | "@babel/core": "^7.19.0", 22 | "@babel/plugin-proposal-class-properties": "^7.16.0", 23 | "@babel/plugin-proposal-object-rest-spread": "^7.16.0", 24 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 25 | "@babel/plugin-transform-runtime": "^7.16.4", 26 | "@babel/preset-env": "^7.19.0", 27 | "@babel/preset-react": "^7.16.0", 28 | "@babel/preset-typescript": "^7.16.0", 29 | "@babel/runtime": "^7.16.3", 30 | "@rollup/plugin-babel": "^5.3.1", 31 | "@rollup/plugin-commonjs": "^21.0.2", 32 | "@rollup/plugin-image": "^2.1.1", 33 | "@rollup/plugin-json": "^4.1.0", 34 | "@rollup/plugin-node-resolve": "^13.1.3", 35 | "@rollup/plugin-replace": "^4.0.0", 36 | "@types/fs-extra": "^9.0.13", 37 | "@types/node": "^16.11.7", 38 | "@types/react": "^18.0.24", 39 | "@types/react-dom": "^18.0.8", 40 | "@types/trusted-types": "^2.0.2", 41 | "@vitejs/plugin-react": "^3.0.1", 42 | "babel-plugin-react-require": "^3.1.3", 43 | "babel-plugin-transform-scss": "^1.1.0", 44 | "babel-preset-env": "^1.7.0", 45 | "core-js": "3", 46 | "cross-env": "^7.0.3", 47 | "fs-extra": "^10.0.0", 48 | "rimraf": "^3.0.2", 49 | "rollup": "^2.79.2", 50 | "rollup-plugin-multi-input": "^1.3.1", 51 | "rollup-plugin-node-copy": "^1.0.4", 52 | "rollup-plugin-scss": "^3.0.0", 53 | "sass": "^1.43.4", 54 | "types-for-adobe": "^7.2.3", 55 | "types-for-adobe-extras": "^0.0.9", 56 | "typescript": "^4.6.4", 57 | "vite": "^5.4.19", 58 | "vite-cep-plugin": "^2.0.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /package.svelte.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bolt-cep", // BOLT_ID_REPLACE 3 | "private": true, 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "vite", 7 | "watch": "tsc && vite build --watch true", 8 | "build": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && vite build --watch false", 9 | "zxp": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZXP_PACKAGE=true vite build --watch false", 10 | "zip": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZIP_PACKAGE=true vite build --watch false", 11 | "serve": "cross-env SERVE_PANEL=true vite preview", 12 | "symlink": "cross-env BOLT_ACTION=symlink vite", 13 | "delsymlink": "cross-env BOLT_ACTION=delsymlink vite" 14 | }, 15 | "dependencies": {}, 16 | "devDependencies": { 17 | "source-map": "^0.7.4", 18 | "@babel/core": "^7.19.0", 19 | "@babel/plugin-proposal-class-properties": "^7.16.0", 20 | "@babel/plugin-proposal-object-rest-spread": "^7.16.0", 21 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 22 | "@babel/plugin-transform-runtime": "^7.16.4", 23 | "@babel/preset-env": "^7.19.0", 24 | "@babel/preset-typescript": "^7.16.0", 25 | "@babel/runtime": "^7.16.3", 26 | "@rollup/plugin-babel": "^5.3.1", 27 | "@rollup/plugin-commonjs": "^21.0.2", 28 | "@rollup/plugin-image": "^2.1.1", 29 | "@rollup/plugin-json": "^4.1.0", 30 | "@rollup/plugin-node-resolve": "^13.1.3", 31 | "@rollup/plugin-replace": "^4.0.0", 32 | "@sveltejs/vite-plugin-svelte": "^1.2.0", 33 | "@tsconfig/svelte": "^3.0.0", 34 | "@types/fs-extra": "^9.0.13", 35 | "@types/node": "^16.11.7", 36 | "@types/trusted-types": "^2.0.2", 37 | "babel-plugin-transform-scss": "^1.1.0", 38 | "babel-preset-env": "^1.7.0", 39 | "core-js": "3", 40 | "cross-env": "^7.0.3", 41 | "fs-extra": "^10.0.0", 42 | "rimraf": "^3.0.2", 43 | "rollup": "^2.79.2", 44 | "rollup-plugin-multi-input": "^1.3.1", 45 | "rollup-plugin-node-copy": "^1.0.4", 46 | "rollup-plugin-scss": "^3.0.0", 47 | "sass": "^1.43.4", 48 | "svelte": "^4.2.19", 49 | "svelte-check": "^2.9.2", 50 | "svelte-preprocess": "^4.10.7", 51 | "types-for-adobe": "^7.2.3", 52 | "types-for-adobe-extras": "^0.0.9", 53 | "typescript": "^4.6.4", 54 | "vite": "^5.4.19", 55 | "vite-cep-plugin": "^2.0.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /package.vue.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bolt-cep", // BOLT_ID_REPLACE 3 | "private": true, 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "vite", 7 | "watch": "tsc && vite build --watch true", 8 | "build": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && vite build --watch false", 9 | "zxp": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZXP_PACKAGE=true vite build --watch false", 10 | "zip": "rimraf dist/* && tsc -p \"tsconfig-build.json\" && cross-env ZIP_PACKAGE=true vite build --watch false", 11 | "serve": "cross-env SERVE_PANEL=true vite preview", 12 | "symlink": "cross-env BOLT_ACTION=symlink vite", 13 | "delsymlink": "cross-env BOLT_ACTION=delsymlink vite" 14 | }, 15 | "dependencies": { 16 | "vue": "^3.2.45" 17 | }, 18 | "devDependencies": { 19 | "source-map": "^0.7.4", 20 | "@babel/core": "^7.19.0", 21 | "@babel/plugin-proposal-class-properties": "^7.16.0", 22 | "@babel/plugin-proposal-object-rest-spread": "^7.16.0", 23 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 24 | "@babel/plugin-transform-runtime": "^7.16.4", 25 | "@babel/preset-env": "^7.19.0", 26 | "@babel/preset-typescript": "^7.16.0", 27 | "@babel/runtime": "^7.16.3", 28 | "@rollup/plugin-babel": "^5.3.1", 29 | "@rollup/plugin-commonjs": "^21.0.2", 30 | "@rollup/plugin-image": "^2.1.1", 31 | "@rollup/plugin-json": "^4.1.0", 32 | "@rollup/plugin-node-resolve": "^13.1.3", 33 | "@rollup/plugin-replace": "^4.0.0", 34 | "@types/fs-extra": "^9.0.13", 35 | "@types/node": "^16.11.7", 36 | "@types/trusted-types": "^2.0.2", 37 | "@vitejs/plugin-vue": "^4.0.0", 38 | "babel-plugin-transform-scss": "^1.1.0", 39 | "babel-preset-env": "^1.7.0", 40 | "core-js": "3", 41 | "cross-env": "^7.0.3", 42 | "fs-extra": "^10.0.0", 43 | "rimraf": "^3.0.2", 44 | "rollup": "^2.79.2", 45 | "rollup-plugin-multi-input": "^1.3.1", 46 | "rollup-plugin-node-copy": "^1.0.4", 47 | "rollup-plugin-scss": "^3.0.0", 48 | "sass": "^1.43.4", 49 | "types-for-adobe": "^7.2.3", 50 | "types-for-adobe-extras": "^0.0.9", 51 | "typescript": "^4.6.4", 52 | "vite": "^5.4.19", 53 | "vite-cep-plugin": "^2.0.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/js/assets/adobe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/js/assets/bolt-cep-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/bolt-cep-react.png -------------------------------------------------------------------------------- /src/js/assets/bolt-cep-vue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/bolt-cep-vue.png -------------------------------------------------------------------------------- /src/js/assets/bolt-cep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/js/assets/built-with-bolt-cep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/built-with-bolt-cep.png -------------------------------------------------------------------------------- /src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_Black_V01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_Black_V01.png -------------------------------------------------------------------------------- /src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_Black_V01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_White_V01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_White_V01.png -------------------------------------------------------------------------------- /src/js/assets/built-with-bolt-cep/Built_With_BOLT_CEP_Logo_White_V01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 66 | 68 | 69 | 70 | 71 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/js/assets/create-bolt-cep--demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/src/js/assets/create-bolt-cep--demo.gif -------------------------------------------------------------------------------- /src/js/assets/node-js.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/js/assets/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/js/assets/sass.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/js/assets/svelte.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/js/assets/typescript.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | TypeScript logo 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/js/assets/vite.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/js/assets/vue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/js/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/js/global.d.ts: -------------------------------------------------------------------------------- 1 | import { cep_node, cep, __adobe_cep__ } from "./lib/cep-types"; 2 | 3 | declare module "*.png"; 4 | declare module "*.gif"; 5 | declare module "*.jpg"; 6 | declare module "*.svg"; 7 | 8 | declare global { 9 | interface Window { 10 | cep_node: cep_node; 11 | cep: cep; 12 | __adobe_cep__: __adobe_cep__; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/js/index.scss: -------------------------------------------------------------------------------- 1 | @use "./variables.scss" as *; 2 | 3 | body { 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 6 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 7 | sans-serif; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | 12 | code { 13 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 14 | monospace; 15 | } 16 | 17 | .app { 18 | background-color: #282c34; 19 | text-align: center; 20 | } 21 | 22 | .app-header { 23 | min-height: 100vh; 24 | display: flex; 25 | flex-direction: column; 26 | align-items: center; 27 | justify-content: center; 28 | font-size: 1rem; 29 | color: white; 30 | } 31 | 32 | .app-link { 33 | color: white; 34 | } 35 | 36 | button { 37 | font-size: 0.8rem; 38 | background-color: $darker; 39 | padding: 0.2rem 0.5rem; 40 | text-align: center; 41 | vertical-align: middle; 42 | border-radius: 5px; 43 | color: white; 44 | border: none; 45 | outline: none; 46 | user-select: none; 47 | } 48 | 49 | button:hover { 50 | background-color: $active; 51 | } 52 | 53 | button:active { 54 | background-color: $active; 55 | } 56 | 57 | label { 58 | color: $font; 59 | } 60 | 61 | .button-group { 62 | display: flex; 63 | gap: 0.5rem; 64 | } 65 | 66 | .stack-icons { 67 | display: flex; 68 | margin: 2rem 0rem 2rem 0rem; 69 | font-size: 2.5rem; 70 | 71 | > div { 72 | display: flex; 73 | flex-direction: column; 74 | text-align: center; 75 | justify-content: center; 76 | font-size: 1rem; 77 | margin: 0rem 1rem 0rem 1rem; 78 | } 79 | 80 | img { 81 | margin: auto; 82 | display: block; 83 | height: 3rem; 84 | width: 3rem; 85 | } 86 | } 87 | 88 | .icon { 89 | width: 350px; 90 | height: 150px; 91 | } 92 | 93 | .icon-button { 94 | width: 20px; 95 | height: 20px; 96 | } 97 | 98 | ::-webkit-scrollbar { 99 | width: 10px; 100 | -webkit-appearance: always; 101 | } 102 | 103 | ::-webkit-scrollbar-track { 104 | background: $darkest; 105 | padding: 2.25em 1.6875em; 106 | } 107 | 108 | ::-webkit-scrollbar-thumb { 109 | background: $darker; 110 | border-radius: 5px; 111 | } 112 | 113 | ::-webkit-scrollbar-thumb:hover { 114 | background: $dark; 115 | } 116 | -------------------------------------------------------------------------------- /src/js/lib/cep/.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /src/js/lib/cep/cep-types.d.ts: -------------------------------------------------------------------------------- 1 | export declare interface cep_node { 2 | global: any; 3 | process: any; 4 | buffer: any; 5 | require: any; 6 | } 7 | export declare interface cep { 8 | encoding: { 9 | Base64: "Base64" | string; 10 | UTF8: "UTF-8" | string; 11 | convertion: { 12 | utf8_to_b64: (...params: any) => {}; 13 | b64_to_utf8: (...params: any) => {}; 14 | binary_to_b64: (...params: any) => {}; 15 | b64_to_binary: (...params: any) => {}; 16 | ascii_to_b64: (...params: any) => {}; 17 | }; 18 | }; 19 | fs: { 20 | ERR_CANT_READ: number; 21 | ERR_CANT_WRITE: number; 22 | ERR_FILE_EXISTS: number; 23 | ERR_INVALID_PARAMS: number; 24 | ERR_NOT_DIRECTORY: number; 25 | ERR_NOT_FILE: number; 26 | ERR_NOT_FOUND: number; 27 | ERR_OUT_OF_SPACE: number; 28 | ERR_UNKNOWN: number; 29 | ERR_UNSUPPORTED_ENCODING: number; 30 | NO_ERROR: number; 31 | chmod: (...params: any) => {}; 32 | deleteFile: (...params: any) => {}; 33 | makedir: (...params: any) => {}; 34 | readFile: (...params: any) => {}; 35 | readdir: (...params: any) => {}; 36 | rename: (...params: any) => {}; 37 | showOpenDialog: (...params: any) => {}; 38 | showOpenDialogEx: (...params: any) => {}; 39 | showSaveDialogEx: (...params: any) => {}; 40 | stat: (...params: any) => {}; 41 | writeFile: (...params: any) => {}; 42 | }; 43 | process: { 44 | ERR_EXCEED_MAX_NUM_PROCESS: number; 45 | createProcess: (...params: any) => {}; 46 | getWorkingDirectory: (...params: any) => {}; 47 | isRunning: (...params: any) => {}; 48 | onquit: (...params: any) => {}; 49 | stderr: (...params: any) => {}; 50 | stdin: (...params: any) => {}; 51 | stdout: (...params: any) => {}; 52 | terminate: (...params: any) => {}; 53 | waitfor: (...params: any) => {}; 54 | }; 55 | util: { 56 | DEPRECATED_API: number; 57 | ERR_INVALID_URL: number; 58 | openURLInDefaultBrowser: (...params: any) => {}; 59 | registerExtensionUnloadCallback: (...params: any) => {}; 60 | storeProxyCredentials: (...params: any) => {}; 61 | }; 62 | } 63 | 64 | export interface __adobe_cep__ { 65 | addEventListener: (...params: any) => {}; 66 | analyticsLogging: (...params: any) => {}; 67 | autoThemeColorChange: (...params: any) => {}; 68 | closeExtension: (...params: any) => {}; 69 | dispatchEvent: (...params: any) => {}; 70 | dumpInstallationInfo: (...params: any) => {}; 71 | evalScript: (...params: any) => {}; 72 | getCurrentApiVersion: (...params: any) => {}; 73 | getCurrentImsUserId: (...params: any) => {}; 74 | getExtensionId: (...params: any) => {}; 75 | getExtensions: (...params: any) => {}; 76 | getHostCapabilities: (...params: any) => {}; 77 | getHostEnvironment: (...params: any) => {}; 78 | getMonitorScaleFactor: (...params: any) => {}; 79 | getNetworkPreferences: (...params: any) => {}; 80 | getScaleFactor: (...params: any) => {}; 81 | getSystemPath: (...params: any) => {}; 82 | imsConnect: (...params: any) => {}; 83 | imsDisconnect: (...params: any) => {}; 84 | imsFetchAccessToken: (...params: any) => {}; 85 | imsFetchAccounts: (...params: any) => {}; 86 | imsSetProxyCredentials: (...params: any) => {}; 87 | initResourceBundle: (...params: any) => {}; 88 | invokeAsync: (...params: any) => {}; 89 | invokeSync: (...params: any) => {}; 90 | nglImsFetchAccessToken: (...params: any) => {}; 91 | nglImsFetchProfile: (...params: any) => {}; 92 | registerInvalidCertificateCallback: (...params: any) => {}; 93 | registerKeyEventsInterest: (...params: any) => {}; 94 | removeEventListener: (...params: any) => {}; 95 | requestOpenExtension: (...params: any) => {}; 96 | resizeContent: (...params: any) => {}; 97 | setScaleFactorChangedHandler: (...params: any) => {}; 98 | showAAM: (...params: any) => {}; 99 | } 100 | -------------------------------------------------------------------------------- /src/js/lib/cep/es-types/index.ts: -------------------------------------------------------------------------------- 1 | export type Scripts = { 2 | [key: string]: (...ags: any) => any; 3 | }; 4 | -------------------------------------------------------------------------------- /src/js/lib/cep/node.ts: -------------------------------------------------------------------------------- 1 | // Abstracted built-in Node.js Modules 2 | 3 | //@ts-ignore 4 | export const crypto = ( 5 | typeof window.cep !== "undefined" ? require("crypto") : {} 6 | ) as typeof import("crypto"); 7 | export const assert = ( 8 | typeof window.cep !== "undefined" ? require("assert") : {} 9 | ) as typeof import("assert"); 10 | export const buffer = ( 11 | typeof window.cep !== "undefined" ? require("buffer") : {} 12 | ) as typeof import("buffer"); 13 | export const child_process = ( 14 | typeof window.cep !== "undefined" ? require("child_process") : {} 15 | ) as typeof import("child_process"); 16 | export const cluster = ( 17 | typeof window.cep !== "undefined" ? require("cluster") : {} 18 | ) as typeof import("cluster"); 19 | export const dgram = ( 20 | typeof window.cep !== "undefined" ? require("dgram") : {} 21 | ) as typeof import("dgram"); 22 | export const dns = ( 23 | typeof window.cep !== "undefined" ? require("dns") : {} 24 | ) as typeof import("dns"); 25 | export const domain = ( 26 | typeof window.cep !== "undefined" ? require("domain") : {} 27 | ) as typeof import("domain"); 28 | export const events = ( 29 | typeof window.cep !== "undefined" ? require("events") : {} 30 | ) as typeof import("events"); 31 | export const fs = ( 32 | typeof window.cep !== "undefined" ? require("fs") : {} 33 | ) as typeof import("fs"); 34 | export const http = ( 35 | typeof window.cep !== "undefined" ? require("http") : {} 36 | ) as typeof import("http"); 37 | export const https = ( 38 | typeof window.cep !== "undefined" ? require("https") : {} 39 | ) as typeof import("https"); 40 | export const net = ( 41 | typeof window.cep !== "undefined" ? require("net") : {} 42 | ) as typeof import("net"); 43 | export const os = ( 44 | typeof window.cep !== "undefined" ? require("os") : {} 45 | ) as typeof import("os"); 46 | export const path = ( 47 | typeof window.cep !== "undefined" ? require("path") : {} 48 | ) as typeof import("path"); 49 | export const punycode = ( 50 | typeof window.cep !== "undefined" ? require("punycode") : {} 51 | ) as typeof import("punycode"); 52 | export const querystring = ( 53 | typeof window.cep !== "undefined" ? require("querystring") : {} 54 | ) as typeof import("querystring"); 55 | export const readline = ( 56 | typeof window.cep !== "undefined" ? require("readline") : {} 57 | ) as typeof import("readline"); 58 | export const stream = ( 59 | typeof window.cep !== "undefined" ? require("stream") : {} 60 | ) as typeof import("stream"); 61 | export const string_decoder = ( 62 | typeof window.cep !== "undefined" ? require("string_decoder") : {} 63 | ) as typeof import("string_decoder"); 64 | export const timers = ( 65 | typeof window.cep !== "undefined" ? require("timers") : {} 66 | ) as typeof import("timers"); 67 | export const tls = ( 68 | typeof window.cep !== "undefined" ? require("tls") : {} 69 | ) as typeof import("tls"); 70 | export const tty = ( 71 | typeof window.cep !== "undefined" ? require("tty") : {} 72 | ) as typeof import("tty"); 73 | export const url = ( 74 | typeof window.cep !== "undefined" ? require("url") : {} 75 | ) as typeof import("url"); 76 | export const util = ( 77 | typeof window.cep !== "undefined" ? require("util") : {} 78 | ) as typeof import("util"); 79 | export const v8 = ( 80 | typeof window.cep !== "undefined" ? require("v8") : {} 81 | ) as typeof import("v8"); 82 | export const vm = ( 83 | typeof window.cep !== "undefined" ? require("vm") : {} 84 | ) as typeof import("vm"); 85 | export const zlib = ( 86 | typeof window.cep !== "undefined" ? require("zlib") : {} 87 | ) as typeof import("zlib"); 88 | -------------------------------------------------------------------------------- /src/js/lib/cep/vulcan.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Vulcan 3 | 4 | The singleton instance, VulcanInterface, provides an interface 5 | to the Vulcan. Allows you to launch CC applications 6 | and discover information about them. 7 | */ 8 | export default class Vulcan { 9 | constructor(); 10 | 11 | /** 12 | * Gets all available application SAPCode-Specifiers on the local machine. 13 | * 14 | * Vulcan Control New 6.x APIs, and Deprecating older Vulcan Control APIs. 15 | * Changes : New getTargetSpecifiersEx returns productSAPCodeSpecifiers 16 | * 17 | * @return The array of all available application SAPCode-Specifiers. 18 | */ 19 | getTargetSpecifiersEx(): any; 20 | 21 | /** 22 | * Launches a CC application on the local machine, if it is not already running. 23 | * 24 | * Vulcan Control New 6.x APIs, and Deprecating older Vulcan Control APIs. 25 | * Changes : New launchAppEx uses productSAPCodeSpecifiers 26 | * 27 | * @param productSAPCodeSpecifier The application specifier; for example "ILST-25.2.3", "ILST-25", "ILST-25.2.3-en_US" and "ILST. Refer to `Documentation/CEP 11.1 HTML Extension Cookbook.md#applications-integrated-with-cep` for product SAPCode. 28 | * @param focus True to launch in foreground, or false to launch in the background. 29 | * @param cmdLine Optional, command-line parameters to supply to the launch command. 30 | * @return True if the app can be launched, false otherwise. 31 | */ 32 | launchAppEx( 33 | productSAPCodeSpecifier: string, 34 | focus: boolean, 35 | cmdLine?: string, 36 | ): boolean; 37 | 38 | /** 39 | * Checks whether a CC application is running on the local machine. 40 | * 41 | * Vulcan Control New 6.x APIs, and Deprecating older Vulcan Control APIs. 42 | * Changes : New isAppRunningEx uses productSAPCodeSpecifiers 43 | * 44 | * @param productSAPCodeSpecifier The application specifier; for example "ILST-25.2.3", "ILST-25", "ILST-25.2.3-en_US" and "ILST. Refer to `Documentation/CEP 11.1 HTML Extension Cookbook.md#applications-integrated-with-cep` for product SAPCode. 45 | * @return True if the app is running, false otherwise. 46 | */ 47 | isAppRunningEx(productSAPCodeSpecifier: string): boolean; 48 | 49 | /** 50 | * Checks whether a CC application is installed on the local machine. 51 | * 52 | * Vulcan Control New 6.x APIs, and Deprecating older Vulcan Control APIs. 53 | * Changes : New isAppInstalledEx uses productSAPCodeSpecifiers 54 | * 55 | * @param productSAPCodeSpecifier The application specifier; for example "ILST-25.2.3", "ILST-25", "ILST-25.2.3-en_US" and "ILST. Refer to `Documentation/CEP 11.1 HTML Extension Cookbook.md#applications-integrated-with-cep` for product SAPCode. 56 | * @return True if the app is installed, false otherwise. 57 | */ 58 | isAppInstalledEx(productSAPCodeSpecifier: string): any; 59 | 60 | /**s 61 | * Retrieves the local install path of a CC application. 62 | * 63 | * Vulcan Control New 6.x APIs, and Deprecating older Vulcan Control APIs. 64 | * Changes : New getAppPathEx uses productSAPCodeSpecifiers 65 | * 66 | * @param productSAPCodeSpecifier The application specifier; for example "ILST-25.2.3", "ILST-25", "ILST-25.2.3-en_US" and "ILST. Refer to `Documentation/CEP 11.1 HTML Extension Cookbook.md#applications-integrated-with-cep` for product SAPCode. 67 | * @return The path string if the application is found, "" otherwise. 68 | */ 69 | getAppPathEx(): any; 70 | 71 | // OLD FUNCTIONS 72 | // OLD FUNCTIONS 73 | // OLD FUNCTIONS 74 | // OLD FUNCTIONS 75 | 76 | /** 77 | * Gets all available application specifiers on the local machine. 78 | * @returns The array of all available application specifiers. 79 | */ 80 | getTargetSpecifiers(): any; 81 | /** 82 | * Launches a CC application on the local machine, if it is not already running. 83 | * @param targetSpecifier - The application specifier; for example "indesign". 84 | 85 | Note: In Windows 7 64-bit or Windows 8 64-bit system, some target applications (like Photoshop and Illustrator) have both 32-bit version 86 | and 64-bit version. Therefore, we need to specify the version by this parameter with "photoshop-70.032" or "photoshop-70.064". If you 87 | installed Photoshop 32-bit and 64-bit on one Windows 64-bit system and invoke this interface with parameter "photoshop-70.032", you may 88 | receive wrong result. 89 | The specifiers for Illustrator is "illustrator-17.032", "illustrator-17.064", "illustrator-17" and "illustrator". 90 | 91 | In other platforms there is no such issue, so we can use "photoshop" or "photoshop-70" as specifier. 92 | * @param focus - True to launch in foreground, or false to launch in the background. 93 | * @param cmdLine - Optional, command-line parameters to supply to the launch command. 94 | * @returns True if the app can be launched, false otherwise. 95 | */ 96 | launchApp(targetSpecifier: any, focus: any, cmdLine: any): any; 97 | /** 98 | * Checks whether a CC application is running on the local machine. 99 | * @param targetSpecifier - The application specifier; for example "indesign". 100 | 101 | Note: In Windows 7 64-bit or Windows 8 64-bit system, some target applications (like Photoshop and Illustrator) have both 32-bit version 102 | and 64-bit version. Therefore, we need to specify the version by this parameter with "photoshop-70.032" or "photoshop-70.064". If you 103 | installed Photoshop 32-bit and 64-bit on one Windows 64-bit system and invoke this interface with parameter "photoshop-70.032", you may 104 | receive wrong result. 105 | The specifiers for Illustrator is "illustrator-17.032", "illustrator-17.064", "illustrator-17" and "illustrator". 106 | 107 | In other platforms there is no such issue, so we can use "photoshop" or "photoshop-70" as specifier. 108 | * @returns True if the app is running, false otherwise. 109 | */ 110 | isAppRunning(targetSpecifier: any): any; 111 | /** 112 | * Checks whether a CC application is installed on the local machine. 113 | * @param targetSpecifier - The application specifier; for example "indesign". 114 | 115 | Note: In Windows 7 64-bit or Windows 8 64-bit system, some target applications (like Photoshop and Illustrator) have both 32-bit version 116 | and 64-bit version. Therefore, we need to specify the version by this parameter with "photoshop-70.032" or "photoshop-70.064". If you 117 | installed Photoshop 32-bit and 64-bit on one Windows 64-bit system and invoke this interface with parameter "photoshop-70.032", you may 118 | receive wrong result. 119 | The specifiers for Illustrator is "illustrator-17.032", "illustrator-17.064", "illustrator-17" and "illustrator". 120 | 121 | In other platforms there is no such issue, so we can use "photoshop" or "photoshop-70" as specifier. 122 | * @returns True if the app is installed, false otherwise. 123 | */ 124 | isAppInstalled(targetSpecifier: any): any; 125 | /** 126 | * Retrieves the local install path of a CC application. 127 | * @param targetSpecifier - The application specifier; for example "indesign". 128 | 129 | Note: In Windows 7 64-bit or Windows 8 64-bit system, some target applications (like Photoshop and Illustrator) have both 32-bit version 130 | and 64-bit version. Therefore, we need to specify the version by this parameter with "photoshop-70.032" or "photoshop-70.064". If you 131 | installed Photoshop 32-bit and 64-bit on one Windows 64-bit system and invoke this interface with parameter "photoshop-70.032", you may 132 | receive wrong result. 133 | The specifiers for Illustrator is "illustrator-17.032", "illustrator-17.064", "illustrator-17" and "illustrator". 134 | 135 | In other platforms there is no such issue, so we can use "photoshop" or "photoshop-70" as specifier. 136 | * @returns The path string if the application is found, "" otherwise. 137 | */ 138 | getAppPath(targetSpecifier: any): any; 139 | /** 140 | * Registers a message listener callback function for a Vulcan message. 141 | * @param type - The message type. 142 | * @param callback - The callback function that handles the message. 143 | Takes one argument, the message object. 144 | * @param obj - Optional, the object containing the callback method, if any. 145 | Default is null. 146 | */ 147 | addMessageListener(type: any, callback: any, obj: any): void; 148 | /** 149 | * Removes a registered message listener callback function for a Vulcan message. 150 | * @param type - The message type. 151 | * @param callback - The callback function that was registered. 152 | Takes one argument, the message object. 153 | * @param obj - Optional, the object containing the callback method, if any. 154 | Default is null. 155 | */ 156 | removeMessageListener(type: any, callback: any, obj: any): void; 157 | /** 158 | * Dispatches a Vulcan message. 159 | * @param vulcanMessage - The message object. 160 | */ 161 | dispatchMessage(vulcanMessage: any): void; 162 | /** 163 | * Retrieves the message payload of a Vulcan message for the registered message listener callback function. 164 | * @param vulcanMessage - The message object. 165 | * @returns A string containing the message payload. 166 | */ 167 | getPayload(vulcanMessage: any): any; 168 | /** 169 | * Gets all available endpoints of the running Vulcan-enabled applications. 170 | 171 | Since 7.0.0 172 | * @returns The array of all available endpoints. 173 | An example endpoint string: 174 | 175 | PHXS 176 | 16.1.0 177 | 178 | */ 179 | getEndPoints(): any; 180 | /** 181 | * Gets the endpoint for itself. 182 | 183 | Since 7.0.0 184 | * @returns The endpoint string for itself. 185 | */ 186 | getSelfEndPoint(): any; 187 | } 188 | 189 | /** 190 | * Singleton instance of Vulcan 191 | */ 192 | declare var VulcanInterface: any; 193 | 194 | /** 195 | * VulcanMessage 196 | Message type for sending messages between host applications. 197 | A message of this type can be sent to the designated destination 198 | when appId and appVersion are provided and valid. Otherwise, 199 | the message is broadcast to all running Vulcan-enabled applications. 200 | 201 | To send a message between extensions running within one 202 | application, use the CSEvent type in CSInterface.js. 203 | * @param type - The message type. 204 | * @param appId - The peer appId. 205 | * @param appVersion - The peer appVersion. 206 | */ 207 | export declare class VulcanMessage { 208 | static TYPE_PREFIX: string; 209 | static SCOPE_SUITE: string; 210 | static DEFAULT_APP_ID: string; 211 | static DEFAULT_APP_VERSION: string; 212 | static DEFAULT_DATA: string; 213 | static dataTemplate: string; 214 | static payloadTemplate: string; 215 | constructor(type: any, appId: any, appVersion: any); 216 | /** 217 | * Initializes this message instance. 218 | * @param message - A \c message instance to use for initialization. 219 | */ 220 | initialize(message: any): void; 221 | /** 222 | * Retrieves the message data. 223 | * @returns A data string in XML format. 224 | */ 225 | xmlData(): any; 226 | /** 227 | * Sets the message payload of this message. 228 | * @param payload - A string containing the message payload. 229 | */ 230 | setPayload(payload: any): void; 231 | /** 232 | * Retrieves the message payload of this message. 233 | * @returns A string containing the message payload. 234 | */ 235 | getPayload(): any; 236 | /** 237 | * Converts the properties of this instance to a string. 238 | * @returns The string version of this instance. 239 | */ 240 | toString(): any; 241 | } 242 | 243 | /** 244 | * Retrieves the content of an XML element. 245 | * @param xmlStr - The XML string. 246 | * @param key - The name of the tag. 247 | * @returns The content of the tag, or the empty string 248 | if such tag is not found or the tag has no content. 249 | */ 250 | declare function GetValueByKey(xmlStr: any, key: any): any; 251 | 252 | /** 253 | * Reports whether required parameters are valid. 254 | * @returns True if all required parameters are valid, 255 | false if any of the required parameters are invalid. 256 | */ 257 | declare function requiredParamsValid(): any; 258 | 259 | /** 260 | * Reports whether a string has a given prefix. 261 | * @param str - The target string. 262 | * @param prefix - The specific prefix string. 263 | * @returns True if the string has the prefix, false if not. 264 | */ 265 | declare function strStartsWith(str: any, prefix: any): any; 266 | -------------------------------------------------------------------------------- /src/js/lib/utils/aeft.ts: -------------------------------------------------------------------------------- 1 | import { fs, path } from "../cep/node"; 2 | import { csi } from "./bolt"; 3 | 4 | const getLatestFile = (dir: string, suffix: string): string | null => { 5 | const getModified = (filePath: string) => 6 | fs.statSync(filePath).mtime.valueOf(); 7 | let latestFile: string | null = null; 8 | fs.readdirSync(dir) 9 | .filter((file) => file.includes(suffix)) 10 | .map((file) => { 11 | if ( 12 | latestFile === null || 13 | getModified(path.join(dir, file)) > 14 | getModified(path.join(dir, latestFile)) 15 | ) { 16 | latestFile = file; 17 | } 18 | }); 19 | return latestFile; 20 | }; 21 | 22 | export const getPrefsDir = (): string => { 23 | const appVersion = csi.getHostEnvironment().appVersion; 24 | const { platform, env } = window.cep_node.process; 25 | const mainDir = 26 | platform == "darwin" 27 | ? `${env.HOME}/Library/Preferences` 28 | : env.APPDATA || ""; 29 | const prefsDir = path.join( 30 | mainDir, 31 | "Adobe", 32 | "After Effects", 33 | parseFloat(appVersion).toFixed(1).toString() 34 | ); 35 | return prefsDir; 36 | }; 37 | 38 | export const getOutputModules = (): string[] => { 39 | const prefsDir = getPrefsDir(); 40 | const prefsSuffix = "indep-output.txt"; 41 | const outputPref = getLatestFile(prefsDir, prefsSuffix); 42 | if (outputPref) { 43 | const txt = fs.readFileSync(path.join(prefsDir, outputPref), { 44 | encoding: "utf-8", 45 | }); 46 | const matches = txt.match( 47 | /\"Output Module Spec Strings Name .* = \".*.\"/g 48 | ); 49 | if (matches) { 50 | let outputModules: string[] = []; 51 | matches.map((line) => { 52 | const str = line.split("=").pop()?.trim().replace(/"/g, ""); 53 | if (str && !str.includes("_HIDDEN X-Factor")) { 54 | outputModules.push(str); 55 | } 56 | }); 57 | return outputModules; 58 | } 59 | } 60 | return []; 61 | }; 62 | 63 | export const getRenderSettingsList = (): string[] => { 64 | const prefsDir = getPrefsDir(); 65 | const prefsSuffix = "indep-render.txt"; 66 | const renderPref = getLatestFile(prefsDir, prefsSuffix); 67 | if (renderPref) { 68 | const txt = fs.readFileSync(path.join(prefsDir, renderPref), { 69 | encoding: "utf-8", 70 | }); 71 | const lines = txt.match(/[^\r\n]+/g); 72 | if (lines) { 73 | const firstLine = lines.findIndex((line) => 74 | line.includes("Render Settings List") 75 | ); 76 | const lastLine = lines.findIndex((line) => 77 | line.includes("Still Frame RS Index") 78 | ); 79 | const settingBlock = lines 80 | .slice(firstLine, lastLine) 81 | .join("") 82 | .trim() 83 | .replace(/^.*\=/g, "") 84 | .replace(/\t/g, "") 85 | .replace(/\\/g, "") 86 | .replace(/\"\"/g, ""); 87 | let renderSettings: string[] = []; 88 | settingBlock.match(/\".*?\"/g)?.map((str) => { 89 | if (str && !str.includes("_HIDDEN X-Factor")) { 90 | renderSettings.push(str.replace(/\"/g, "")); 91 | } 92 | }); 93 | return renderSettings; 94 | } 95 | } 96 | return []; 97 | }; 98 | -------------------------------------------------------------------------------- /src/js/lib/utils/bolt.ts: -------------------------------------------------------------------------------- 1 | import CSInterface, { CSEvent } from "../cep/csinterface"; 2 | import Vulcan, { VulcanMessage } from "../cep/vulcan"; 3 | import { ns } from "../../../shared/shared"; 4 | import { fs } from "../cep/node"; 5 | 6 | export const csi = new CSInterface(); 7 | export const vulcan = new Vulcan(); 8 | 9 | // jsx utils 10 | 11 | /** 12 | * @function EvalES 13 | * Evaluates a string in ExtendScript scoped to the project's namespace 14 | * Optionally, pass true to the isGlobal param to avoid scoping 15 | * 16 | * @param script The script as a string to be evaluated 17 | * @param isGlobal Optional. Defaults to false, 18 | * 19 | * @return String Result. 20 | */ 21 | 22 | export const evalES = (script: string, isGlobal = false): Promise => { 23 | return new Promise(function (resolve, reject) { 24 | const pre = isGlobal 25 | ? "" 26 | : `var host = typeof $ !== 'undefined' ? $ : window; host["${ns}"].`; 27 | const fullString = pre + script; 28 | csi.evalScript( 29 | "try{" + fullString + "}catch(e){alert(e);}", 30 | (res: string) => { 31 | resolve(res); 32 | } 33 | ); 34 | }); 35 | }; 36 | 37 | import type { Scripts } from "@esTypes/index"; 38 | import type { EventTS } from "../../../shared/universals"; 39 | import { initializeCEP } from "./init-cep"; 40 | 41 | type ArgTypes = F extends (...args: infer A) => any 42 | ? A 43 | : never; 44 | type ReturnType = F extends (...args: infer A) => infer B 45 | ? B 46 | : never; 47 | 48 | /** 49 | * @description End-to-end type-safe ExtendScript evaluation with error handling 50 | * Call ExtendScript functions from CEP with type-safe parameters and return types. 51 | * Any ExtendScript errors are captured and logged to the CEP console for tracing 52 | * 53 | * @param functionName The name of the function to be evaluated. 54 | * @param args the list of arguments taken by the function. 55 | * 56 | * @return Promise resolving to function native return type. 57 | * 58 | * @example 59 | * // CEP 60 | * evalTS("myFunc", 60, 'test').then((res) => { 61 | * console.log(res.word); 62 | * }); 63 | * 64 | * // ExtendScript 65 | * export const myFunc = (num: number, word: string) => { 66 | * return { num, word }; 67 | * } 68 | * 69 | */ 70 | 71 | export const evalTS = < 72 | Key extends string & keyof Scripts, 73 | Func extends Function & Scripts[Key] 74 | >( 75 | functionName: Key, 76 | ...args: ArgTypes 77 | ): Promise> => { 78 | return new Promise(function (resolve, reject) { 79 | const formattedArgs = args 80 | .map((arg) => { 81 | console.log(JSON.stringify(arg)); 82 | return `${JSON.stringify(arg)}`; 83 | }) 84 | .join(","); 85 | csi.evalScript( 86 | `try{ 87 | var host = typeof $ !== 'undefined' ? $ : window; 88 | var res = host["${ns}"].${functionName}(${formattedArgs}); 89 | JSON.stringify(res); 90 | }catch(e){ 91 | e.fileName = new File(e.fileName).fsName; 92 | JSON.stringify(e); 93 | }`, 94 | (res: string) => { 95 | try { 96 | //@ts-ignore 97 | if (res === "undefined") return resolve(); 98 | const parsed = JSON.parse(res); 99 | if ( 100 | typeof parsed.name === "string" && 101 | (parsed.name).toLowerCase().includes("error") 102 | ) { 103 | console.error(parsed.message); 104 | reject(parsed); 105 | } else { 106 | resolve(parsed); 107 | } 108 | } catch (error) { 109 | reject(res); 110 | } 111 | } 112 | ); 113 | }); 114 | }; 115 | 116 | export const evalFile = (file: string) => { 117 | return evalES( 118 | "typeof $ !== 'undefined' ? $.evalFile(\"" + 119 | file + 120 | '") : fl.runScript(FLfile.platformPathToURI("' + 121 | file + 122 | '"));', 123 | true 124 | ); 125 | }; 126 | 127 | /** 128 | * @function listenTS End-to-end Type-Safe ExtendScript to JavaScript Events 129 | * Uses the PlugPlug ExternalObject to trigger events in CEP panels 130 | * Function comes scoped to the panel's namespace to avoid conflicts 131 | * Simply declare your event name and value in the shared/universals.ts file 132 | * Listen for events with listenTS() in your CEP panel 133 | * Trigger those events with dispatchTS() ExtendScript 134 | * @param event The event name to listen for (defined in EventTS in shared/universals.ts) 135 | * @param callback The callback function to be executed when the event is triggered 136 | * @param isLocal Whether to scope the event to the panel's namespace. Defaults to true 137 | * 138 | * @example 139 | * 140 | * // 1. Declare Type in EventTS in shared/universals.ts 141 | * export type EventTS = { 142 | * 'myCustomEvent': { 143 | * name: string; 144 | * value: number; 145 | * } 146 | * // [... other events] 147 | * }; 148 | * 149 | * // 2. Listen in CEP 150 | * listenTS("myCustomEvent", (data) => { 151 | * console.log("name is", data.name); 152 | * console.log("value is", data.value); 153 | * }); 154 | * 155 | * // 3. Dispatch in ExtendScript 156 | * dispatchTS("myCustomEvent", { name: "name", value: 20 }); 157 | * 158 | */ 159 | export const listenTS = ( 160 | event: Key, 161 | callback: (data: EventTS[Key]) => void, 162 | isLocal = true 163 | ) => { 164 | const fullEvent = isLocal ? `${ns}.${event}` : event; 165 | const csi = new CSInterface(); 166 | // console.log(`listening to ${fullEvent}`); 167 | const thisCallback = (e: { data: EventTS[Key] }) => { 168 | callback(e.data); 169 | }; 170 | 171 | // remove any existing listeners 172 | csi.removeEventListener(fullEvent, thisCallback, null); 173 | // add the event listener 174 | csi.addEventListener(fullEvent, thisCallback); 175 | }; 176 | 177 | /** 178 | * @function dispatchTS Displatches an event within or between CEP panels with Type-Safety 179 | * See listenTS() in the CEP panel for more info 180 | * @param event The event name to listen for (defined in EventTS in shared/universals.ts) 181 | * @param callback The callback function to be executed when the event is triggered 182 | * @param scope The scope of the event. Defaults to "APPLICATION" 183 | * @param appId The application ID. Defaults to the current application 184 | * @param id The extension ID. Defaults to the current extension 185 | * @param isLocal Whether to scope the event to the panel's namespace. Defaults to true 186 | */ 187 | export const dispatchTS = ( 188 | event: Key, 189 | data: EventTS[Key], 190 | scope = "APPLICATION", 191 | appId = csi.getApplicationID() as string, 192 | id = csi.getExtensionID() as string, 193 | isLocal = true 194 | ) => { 195 | const fullEvent = isLocal ? `${ns}.${event}` : event; 196 | // console.log(`dispatching ${fullEvent}`); 197 | const csEvent = new CSEvent(fullEvent, scope, appId, id); 198 | csEvent.data = data; 199 | csi.dispatchEvent(csEvent); 200 | }; 201 | 202 | // js utils 203 | 204 | export const initBolt = (log = true) => { 205 | if (window.cep) { 206 | const extRoot = csi.getSystemPath("extension"); 207 | const jsxSrc = `${extRoot}/jsx/index.js`; 208 | const jsxBinSrc = `${extRoot}/jsx/index.jsxbin`; 209 | if (fs.existsSync(jsxSrc)) { 210 | if (log) console.log(jsxSrc); 211 | evalFile(jsxSrc); 212 | } else if (fs.existsSync(jsxBinSrc)) { 213 | if (log) console.log(jsxBinSrc); 214 | evalFile(jsxBinSrc); 215 | } 216 | initializeCEP(); 217 | } 218 | }; 219 | 220 | export const posix = (str: string) => str.replace(/\\/g, "/"); 221 | 222 | export const openLinkInBrowser = (url: string) => { 223 | if (window.cep) { 224 | csi.openURLInDefaultBrowser(url); 225 | } else { 226 | location.href = url; 227 | } 228 | }; 229 | 230 | export const getAppBackgroundColor = () => { 231 | const { green, blue, red } = JSON.parse( 232 | window.__adobe_cep__.getHostEnvironment() as string 233 | ).appSkinInfo.panelBackgroundColor.color; 234 | return { 235 | rgb: { 236 | r: red, 237 | g: green, 238 | b: blue, 239 | }, 240 | hex: `#${red.toString(16)}${green.toString(16)}${blue.toString(16)}`, 241 | }; 242 | }; 243 | 244 | export const subscribeBackgroundColor = (callback: (color: string) => void) => { 245 | const getColor = () => { 246 | const newColor = getAppBackgroundColor(); 247 | console.log("BG Color Updated: ", { rgb: newColor.rgb }); 248 | const { r, g, b } = newColor.rgb; 249 | return `rgb(${r}, ${g}, ${b})`; 250 | }; 251 | // get current color 252 | callback(getColor()); 253 | // listen for changes 254 | csi.addEventListener( 255 | "com.adobe.csxs.events.ThemeColorChanged", 256 | () => callback(getColor()), 257 | {} 258 | ); 259 | }; 260 | 261 | // vulcan 262 | 263 | declare type IVulcanMessageObject = { 264 | event: string; 265 | callbackID?: string; 266 | data?: string | null; 267 | payload?: object; 268 | }; 269 | 270 | export const vulcanSend = (id: string, msgObj: IVulcanMessageObject) => { 271 | const msg = new VulcanMessage(VulcanMessage.TYPE_PREFIX + id, null, null); 272 | const msgStr = JSON.stringify(msgObj); 273 | msg.setPayload(msgStr); 274 | vulcan.dispatchMessage(msg); 275 | }; 276 | 277 | export const vulcanListen = (id: string, callback: Function) => { 278 | vulcan.addMessageListener( 279 | VulcanMessage.TYPE_PREFIX + id, 280 | (res: any) => { 281 | var msgStr = vulcan.getPayload(res); 282 | const msgObj = JSON.parse(msgStr); 283 | callback(msgObj); 284 | }, 285 | null 286 | ); 287 | }; 288 | 289 | export const isAppRunning = (targetSpecifier: string) => { 290 | const { major, minor, micro } = csi.getCurrentApiVersion(); 291 | const version = parseFloat(`${major}.${minor}`); 292 | if (version >= 11.2) { 293 | return vulcan.isAppRunningEx(targetSpecifier.toUpperCase()); 294 | } else { 295 | return vulcan.isAppRunning(targetSpecifier); 296 | } 297 | }; 298 | 299 | interface IOpenDialogResult { 300 | data: string[]; 301 | } 302 | export const selectFolder = ( 303 | dir: string, 304 | msg: string, 305 | callback: (res: string) => void 306 | ) => { 307 | const result = ( 308 | window.cep.fs.showOpenDialogEx || window.cep.fs.showOpenDialog 309 | )(false, true, msg, dir) as IOpenDialogResult; 310 | if (result.data?.length > 0) { 311 | const folder = decodeURIComponent(result.data[0].replace("file://", "")); 312 | callback(folder); 313 | } 314 | }; 315 | 316 | export const selectFile = ( 317 | dir: string, 318 | msg: string, 319 | callback: (res: string) => void 320 | ) => { 321 | const result = ( 322 | window.cep.fs.showOpenDialogEx || window.cep.fs.showOpenDialog 323 | )(false, false, msg, dir) as IOpenDialogResult; 324 | if (result.data?.length > 0) { 325 | const folder = decodeURIComponent(result.data[0].replace("file://", "")); 326 | callback(folder); 327 | } 328 | }; 329 | 330 | /** 331 | * @function enableSpectrum fixes an issue with React Spectrum and PointerEvents on MacOS 332 | * Run once at the start of your app to fix this issue 333 | */ 334 | 335 | export const enableSpectrum = () => { 336 | if (window.PointerEvent) { 337 | //@ts-ignore 338 | delete window.PointerEvent; 339 | } 340 | }; 341 | -------------------------------------------------------------------------------- /src/js/lib/utils/cep.ts: -------------------------------------------------------------------------------- 1 | import { os } from "../cep/node"; 2 | import { csi } from "./bolt"; 3 | 4 | /** 5 | * Register all possible keyboard shortcuts on Mac and Windows for you CEP Panel 6 | * Warning: Note that certain keys will not work per OS regardless of registration 7 | */ 8 | 9 | export const keyRegisterOverride = () => { 10 | //@ts-ignore 11 | const platform = navigator.platform.substring(0, 3); 12 | let maxKey = 0; 13 | if (platform === "Mac") maxKey = 126; // Mac Max Key Code 14 | else if (platform === "Win") maxKey = 222; // HTML Max Key Code 15 | let allKeys: { 16 | keyCode: number; 17 | ctrlKey: boolean; 18 | altKey: boolean; 19 | shiftKey: boolean; 20 | metaKey: boolean; 21 | }[] = []; 22 | for (let k = 0; k <= maxKey; k++) { 23 | for (let j = 0; j <= 15; j++) { 24 | const guide = (j >>> 0).toString(2).padStart(4, "0"); 25 | allKeys.push({ 26 | keyCode: k, 27 | ctrlKey: guide[0] === "1", 28 | altKey: guide[1] === "1", 29 | shiftKey: guide[2] === "1", 30 | metaKey: guide[3] === "1", 31 | }); 32 | } 33 | } 34 | const keyRes = csi.registerKeyEventsInterest(JSON.stringify(allKeys)); 35 | console.log("Key Events Registered Completed: " + keyRes); 36 | }; 37 | 38 | export const textCepPatch = (e: KeyboardEvent) => { 39 | const isMac = os.platform() === "darwin"; 40 | if (!isMac) return; // Only needed on MacOS, Windows handles this natively 41 | 42 | // console.log("keyup", e); 43 | 44 | const isShiftKey = e.shiftKey; 45 | const input = e.target as HTMLTextAreaElement | HTMLInputElement; 46 | const start = input.selectionStart; 47 | let end = input.selectionEnd; 48 | 49 | const selectionExists = start !== null && end !== null && start !== end; 50 | 51 | if (start === null || end === null) return; 52 | 53 | if (e.key === "ArrowLeft") { 54 | if (start === 0) return; // Prevents going to -1 55 | if (isShiftKey) { 56 | input.setSelectionRange(start - 1, end); 57 | } else { 58 | input.setSelectionRange(start - 1, start - 1); 59 | } 60 | } else if (e.key === "ArrowRight") { 61 | if (end === input.value.length) return; // Prevents going to start 62 | if (isShiftKey) { 63 | input.setSelectionRange(start, end + 1); 64 | } else { 65 | input.setSelectionRange(end + 1, end + 1); 66 | } 67 | } 68 | }; 69 | 70 | /** 71 | * Prevents the user from dropping files or URLs onto the panel and navigating away 72 | */ 73 | 74 | export const dropDisable = () => { 75 | window.addEventListener("dragover", (e) => e.preventDefault(), false); 76 | window.addEventListener("drop", (e) => e.preventDefault(), false); 77 | }; 78 | -------------------------------------------------------------------------------- /src/js/lib/utils/init-cep.ts: -------------------------------------------------------------------------------- 1 | import { company, displayName, version } from "../../../shared/shared"; 2 | import { dispatchTS, openLinkInBrowser } from "./bolt"; 3 | import { keyRegisterOverride, dropDisable } from "./cep"; 4 | 5 | const buildFlyoutMenu = () => { 6 | const menu = ` 7 | 8 | 9 | 10 | 11 | `; 12 | 13 | interface FlyoutMenuEvent { 14 | data: 15 | | { 16 | menuId: string; 17 | } 18 | | string; 19 | } 20 | const flyoutHandler = (event: FlyoutMenuEvent) => { 21 | let menuId; 22 | if (typeof event.data === "string") { 23 | try { 24 | //? On build the events come in garbled string which requires some replacing and then parsing to get the data 25 | menuId = JSON.parse( 26 | event.data.replace(/\$/g, "").replace(/\=2/g, ":") 27 | ).menuId; 28 | } catch (e) { 29 | console.error(e); 30 | } 31 | } else { 32 | menuId = event.data.menuId; 33 | } 34 | if (menuId === "website") { 35 | // openLinkInBrowser(homePage); 36 | } else if (menuId === "info") { 37 | // openLinkInBrowser(productPage); 38 | } else if (menuId === "refresh") { 39 | location.reload(); 40 | } 41 | }; 42 | 43 | window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); 44 | window.__adobe_cep__.addEventListener( 45 | "com.adobe.csxs.events.flyoutMenuClicked", 46 | flyoutHandler 47 | ); 48 | }; 49 | 50 | const buildContextMenu = () => { 51 | console.log("buildContextMenu"); 52 | const menuObj = { 53 | menu: [ 54 | { 55 | label: "Reload", 56 | enabled: true, 57 | checked: false, 58 | checkable: false, 59 | id: "c-0", 60 | callback: () => { 61 | location.reload(); 62 | }, 63 | }, 64 | { 65 | label: "Force Reload", 66 | enabled: true, 67 | checked: false, 68 | checkable: false, 69 | id: "c-1", 70 | callback: () => { 71 | process.abort(); 72 | }, 73 | }, 74 | ], 75 | }; 76 | window.__adobe_cep__.invokeAsync( 77 | "setContextMenuByJSON", 78 | JSON.stringify(menuObj), 79 | (e: string) => { 80 | menuObj.menu.find((m) => m.id === e)?.callback(); 81 | } 82 | ); 83 | }; 84 | 85 | export const initializeCEP = () => { 86 | buildFlyoutMenu(); 87 | buildContextMenu(); 88 | // keyRegisterOverride(); // Capture all Key Events Possible (many limitations on MacOS) 89 | dropDisable(); // to prevent drop files on panel and taking over 90 | }; 91 | -------------------------------------------------------------------------------- /src/js/lib/utils/ppro.ts: -------------------------------------------------------------------------------- 1 | import { fs, os, path } from "../cep/node"; 2 | import { csi } from "./bolt"; 3 | 4 | const readDirSafe = (dir: string) => 5 | fs.existsSync(dir) ? fs.readdirSync(dir) : []; 6 | 7 | export const getAllLuts = (): { creative: string[]; technical: string[] } => { 8 | const isWin = os.platform() === "win32"; 9 | 10 | const appPath = path.dirname(csi.getSystemPath("hostApplication")); 11 | const appLutsDir = path.join( 12 | isWin ? appPath : path.dirname(appPath), 13 | "Lumetri", 14 | "LUTs" 15 | ); 16 | 17 | const winLocal = path.join( 18 | os.homedir(), 19 | "AppData", 20 | "Roaming", 21 | "Adobe", 22 | "Common", 23 | "LUTs" 24 | ); 25 | const winGlobal = path.join("C:", "Program Files", "Adobe", "Common", "LUTs"); 26 | const macLocal = path.join( 27 | os.homedir(), 28 | "Library", 29 | "Application Support", 30 | "Adobe", 31 | "Common", 32 | "LUTs" 33 | ); 34 | const macGlobal = path.join( 35 | "Library", 36 | "Application Support", 37 | "Adobe", 38 | "Common", 39 | "LUTs" 40 | ); 41 | 42 | const appCreative = path.join(appLutsDir, "Creative"); 43 | const appTechnical = path.join(appLutsDir, "Technical"); 44 | const localCreative = isWin 45 | ? path.join(winLocal, "Creative") 46 | : path.join(macLocal, "Creative"); 47 | const localTechnical = isWin 48 | ? path.join(winLocal, "Technical") 49 | : path.join(macLocal, "Technical"); 50 | const globalCreative = isWin 51 | ? path.join(winGlobal, "Creative") 52 | : path.join(macGlobal, "Creative"); 53 | const globalTechnical = isWin 54 | ? path.join(winGlobal, "Technical") 55 | : path.join(macGlobal, "Technical"); 56 | 57 | const appCreativeLuts = readDirSafe(appCreative); 58 | const appTechnicalLuts = readDirSafe(appTechnical); 59 | 60 | const localCreativeLuts = readDirSafe(localCreative); 61 | const localTechnicalLuts = readDirSafe(localTechnical); 62 | const globalCreativeLuts = readDirSafe(globalCreative); 63 | const globalTechnicalLuts = readDirSafe(globalTechnical); 64 | const creative = [ 65 | ...appCreativeLuts, 66 | ...localCreativeLuts, 67 | ...globalCreativeLuts, 68 | ] 69 | .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())) 70 | .map((lut) => path.basename(lut, path.extname(lut))); 71 | const technical = [ 72 | ...appTechnicalLuts, 73 | ...localTechnicalLuts, 74 | ...globalTechnicalLuts, 75 | ] 76 | .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())) 77 | .map((lut) => path.basename(lut, path.extname(lut))); 78 | 79 | return { creative, technical }; 80 | }; 81 | 82 | export const allowedImportFiles: string[] = [ 83 | "264", 84 | "3g2", 85 | "3gp", 86 | "3gpp", 87 | "aac", 88 | "aaf", 89 | "ac3", 90 | "ai", 91 | "aif", 92 | "aiff", 93 | "ari", 94 | "asf", 95 | "asnd", 96 | "asx", 97 | "avc", 98 | "avi", 99 | "bmp", 100 | "bwf", 101 | "cin", 102 | "cine", 103 | "crm", 104 | "dfxp", 105 | "dib", 106 | "dif", 107 | "dng", 108 | "dpx", 109 | "dv", 110 | "eps", 111 | "exr", 112 | "f4v", 113 | "f4v", 114 | "fli", 115 | "gif", 116 | "icb", 117 | "ico", 118 | "jfif", 119 | "jpe", 120 | "jpeg", 121 | "jpg", 122 | "m15", 123 | "m1a", 124 | "m1s", 125 | "m1v", 126 | "m2a", 127 | "m2p", 128 | "m2t", 129 | "m2ts", 130 | "m2v", 131 | "m4a", 132 | "m4v", 133 | "m75", 134 | "mcc", 135 | "m0d", 136 | "mov", 137 | "mp2", 138 | "mp3", 139 | "mp4", 140 | "mpa", 141 | "mpe", 142 | "mpeg", 143 | "mpg", 144 | "mpg4", 145 | "mpm", 146 | "mpv", 147 | "mts", 148 | "mxf", 149 | "mxv", 150 | "mxr", 151 | "pct", 152 | "pict", 153 | "png", 154 | "prt", 155 | "ptl", 156 | "qt", 157 | "r3d", 158 | "rle", 159 | "rmf", 160 | "scc", 161 | "srt", 162 | "stl", 163 | "sxr", 164 | "tga", 165 | "tif", 166 | "tiff", 167 | "ts", 168 | "vda", 169 | "vob", 170 | "vst", 171 | "wav", 172 | "wma", 173 | "wmv", 174 | "psd", 175 | ]; 176 | -------------------------------------------------------------------------------- /src/js/main/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "*.vue" { 4 | import type { DefineComponent } from "vue"; 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | -------------------------------------------------------------------------------- /src/js/main/index-react.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { initBolt } from "../lib/utils/bolt"; 4 | import "../index.scss"; 5 | import { App } from "./main"; 6 | 7 | initBolt(); 8 | 9 | ReactDOM.createRoot(document.getElementById("app") as HTMLElement).render( 10 | 11 | 12 | 13 | ); 14 | -------------------------------------------------------------------------------- /src/js/main/index-svelte.ts: -------------------------------------------------------------------------------- 1 | import App from "./main.svelte"; 2 | import { initBolt } from "../lib/utils/bolt"; 3 | 4 | initBolt(); 5 | 6 | const app = new App({ 7 | target: document.getElementById("app") as Element, 8 | }); 9 | 10 | export default app; 11 | -------------------------------------------------------------------------------- /src/js/main/index-vue.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./main.vue"; 3 | import { initBolt } from "../lib/utils/bolt"; 4 | 5 | initBolt(); 6 | 7 | createApp(App).mount("#app"); 8 | -------------------------------------------------------------------------------- /src/js/main/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bolt CEP 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/js/main/main.scss: -------------------------------------------------------------------------------- 1 | @use "../variables.scss" as *; 2 | -------------------------------------------------------------------------------- /src/js/main/main.svelte: -------------------------------------------------------------------------------- 1 | 70 | 71 |
72 |
73 | 74 | 75 |
76 |
77 | 78 | Vite 79 |
80 | + 81 |
82 | 83 | Svelte 84 |
85 | + 86 |
87 | 88 | TypeScript 89 |
90 | + 91 |
92 | 93 | Sass 94 |
95 |
96 |
97 | 98 | 101 | 104 | 105 |
106 | 107 |

Edit main.svelte and save to test HMR updates.

108 |

109 | 115 | | 116 | 119 | | 120 | 126 |

127 | 128 |
129 |
130 | 131 | 134 | -------------------------------------------------------------------------------- /src/js/main/main.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { os, path } from "../lib/cep/node"; 3 | import { 4 | csi, 5 | evalES, 6 | openLinkInBrowser, 7 | subscribeBackgroundColor, 8 | evalTS, 9 | } from "../lib/utils/bolt"; 10 | import "./main.scss"; 11 | 12 | // BOLT_SAMPLECODE_START 13 | import reactLogo from "../assets/react.svg"; 14 | import viteLogo from "../assets/vite.svg"; 15 | import tsLogo from "../assets/typescript.svg"; 16 | import sassLogo from "../assets/sass.svg"; 17 | import nodeJs from "../assets/node-js.svg"; 18 | import adobe from "../assets/adobe.svg"; 19 | import bolt from "../assets/bolt-cep.svg"; 20 | // BOLT_SAMPLECODE_END 21 | 22 | export const App = () => { 23 | const [bgColor, setBgColor] = useState("#282c34"); 24 | // BOLT_SAMPLECODE_START 25 | const [count, setCount] = useState(0); 26 | 27 | //* Demonstration of Traditional string eval-based ExtendScript Interaction 28 | const jsxTest = () => { 29 | console.log(evalES(`helloWorld("${csi.getApplicationID()}")`)); 30 | }; 31 | 32 | //* Demonstration of End-to-End Type-safe ExtendScript Interaction 33 | const jsxTestTS = () => { 34 | evalTS("helloStr", "test").then((res) => { 35 | console.log(res); 36 | }); 37 | evalTS("helloNum", 1000).then((res) => { 38 | console.log(typeof res, res); 39 | }); 40 | evalTS("helloArrayStr", ["ddddd", "aaaaaa", "zzzzzzz"]).then((res) => { 41 | console.log(typeof res, res); 42 | }); 43 | evalTS("helloObj", { height: 90, width: 100 }).then((res) => { 44 | console.log(typeof res, res); 45 | console.log(res.x); 46 | console.log(res.y); 47 | }); 48 | evalTS("helloVoid").then(() => { 49 | console.log("function returning void complete"); 50 | }); 51 | evalTS("helloError", "test").catch((e) => { 52 | console.log("there was an error", e); 53 | }); 54 | }; 55 | 56 | const nodeTest = () => { 57 | alert( 58 | `Node.js ${process.version}\nPlatform: ${ 59 | os.platform 60 | }\nFolder: ${path.basename(window.cep_node.global.__dirname)}` 61 | ); 62 | }; 63 | // BOLT_SAMPLECODE_END 64 | 65 | useEffect(() => { 66 | if (window.cep) { 67 | subscribeBackgroundColor(setBgColor); 68 | } 69 | }, []); 70 | 71 | return ( 72 |
73 |
74 | {/* BOLT_SAMPLECODE_START */} 75 | 76 |
77 |
78 | 79 | Vite 80 |
81 | + 82 |
83 | 84 | React 85 |
86 | + 87 |
88 | 89 | TypeScript 90 |
91 | + 92 |
93 | 94 | Sass 95 |
96 |
97 |
98 | 101 | 104 | 107 | 108 |
109 |

110 | Edit main.tsx and save to test HMR updates. 111 |

112 |

113 | 121 | {" | "} 122 | 128 | {" | "} 129 | 137 |

138 | {/* BOLT_SAMPLECODE_END */} 139 |
140 |
141 | ); 142 | }; 143 | -------------------------------------------------------------------------------- /src/js/main/main.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 126 | 127 | 130 | -------------------------------------------------------------------------------- /src/js/main/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/js/variables.scss: -------------------------------------------------------------------------------- 1 | $darkest: #222222; 2 | $darker: #333333; 3 | $dark: #444444; 4 | $font: #bbbbbb; 5 | $highlight: #aaaaaa; 6 | 7 | $primary: #88715a; 8 | $secondary: #4a3928; 9 | 10 | $active: #20639b; 11 | $changed: #3caea3; 12 | $warning: #f6d55c; 13 | $error: #ed553b; 14 | -------------------------------------------------------------------------------- /src/js/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/jsx/aeft/aeft-utils.ts: -------------------------------------------------------------------------------- 1 | export const forEachLayer = ( 2 | comp: CompItem, 3 | callback: (item: Layer, index: number) => void 4 | ) => { 5 | const len = comp.numLayers; 6 | for (let i = 1; i < len + 1; i++) { 7 | callback(comp.layers[i], i); 8 | } 9 | }; 10 | 11 | export const forEachComp = ( 12 | folder: FolderItem | Project, 13 | callback: (item: CompItem, index: number) => void 14 | ) => { 15 | const len = folder.numItems; 16 | let comps: CompItem[] = []; 17 | for (let i = 1; i < len + 1; i++) { 18 | const item = folder.items[i]; 19 | if (item instanceof CompItem) { 20 | comps.push(item); 21 | } 22 | } 23 | for (let i = 0; i < comps.length; i++) { 24 | let comp = comps[i]; 25 | callback(comp, i); 26 | } 27 | }; 28 | 29 | export const compFromFootage = (item: FootageItem): CompItem => { 30 | return app.project.items.addComp( 31 | item.name, 32 | item.width, 33 | item.height, 34 | item.pixelAspect, 35 | item.duration, 36 | item.frameRate 37 | ); 38 | }; 39 | 40 | export const getProjectDir = () => { 41 | app.project.file; 42 | if (app.project.file !== null) { 43 | return app.project.file.parent; 44 | } else { 45 | return ""; 46 | } 47 | }; 48 | 49 | export const getActiveComp = () => { 50 | if (app.project.activeItem instanceof CompItem === false) { 51 | app.activeViewer?.setActive(); 52 | } 53 | return app.project.activeItem as CompItem; 54 | }; 55 | 56 | // Project Item Helpers 57 | 58 | export const getItemByName = (parent: FolderItem, name: string) => { 59 | for (var i = 0; i < parent.numItems; i++) { 60 | const item = parent.items[i + 1]; 61 | if (item.name === name) { 62 | return item; 63 | } 64 | } 65 | }; 66 | 67 | // Metadata helpers 68 | 69 | export const setAeMetadata = (propName: string, propValue: any) => { 70 | if (ExternalObject.AdobeXMPScript === undefined) { 71 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 72 | } 73 | if (!app.project || !ExternalObject.AdobeXMPScript || !XMPMeta) return; 74 | const prefix = "xmp:"; 75 | const uri = XMPMeta.getNamespaceURI(prefix); 76 | const newPropName = prefix + propName; 77 | let metadata = new XMPMeta(app.project.xmpPacket); 78 | metadata.setProperty(uri, newPropName, propValue.toString()); 79 | app.project.xmpPacket = metadata.serialize(); 80 | }; 81 | 82 | export const getAeMetadata = (propName: string) => { 83 | if (ExternalObject.AdobeXMPScript === undefined) { 84 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 85 | } 86 | if (!app.project || !ExternalObject.AdobeXMPScript || !XMPMeta) return; 87 | const prefix = "xmp:"; 88 | const uri = XMPMeta.getNamespaceURI(prefix); 89 | const newPropName = prefix + propName; 90 | const metadata = new XMPMeta(app.project.xmpPacket); 91 | return metadata.getProperty(uri, newPropName); 92 | }; 93 | -------------------------------------------------------------------------------- /src/jsx/aeft/aeft.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from After Effects!"); 14 | app.project.activeItem; 15 | }; 16 | -------------------------------------------------------------------------------- /src/jsx/aeft/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/AfterEffects/22.0", 9 | "../../../node_modules/types-for-adobe/shared/PlugPlugExternalObject", 10 | "../../../node_modules/types-for-adobe/shared/XMPScript" 11 | ] 12 | }, 13 | "include": ["./**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /src/jsx/ame/ame.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Media Encoder"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/ame/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/shared/global" 9 | ] 10 | }, 11 | "include": ["./**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /src/jsx/anim/anim.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Animate"); 14 | document.path; 15 | }; 16 | -------------------------------------------------------------------------------- /src/jsx/anim/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": ["../global", "../../../node_modules/types-for-adobe/Animate/22.0"] 7 | }, 8 | "include": ["./**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /src/jsx/audt/audt.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Audtion"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/audt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/Audition/2018" 9 | ] 10 | }, 11 | "include": ["./**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /src/jsx/global.d.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | declare var JSON: { 3 | stringify(object: object): string; 4 | parse(string: string): object; 5 | }; 6 | -------------------------------------------------------------------------------- /src/jsx/idsn/idsn.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from InDesign"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/idsn/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/InDesign/2021" 9 | ] 10 | }, 11 | "include": ["./**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /src/jsx/ilst/ilst.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Illustrator"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/ilst/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/Illustrator/2015.3", 9 | "../../../node_modules/types-for-adobe/shared/PlugPlugExternalObject" 10 | ] 11 | }, 12 | "include": ["./**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /src/jsx/index.ts: -------------------------------------------------------------------------------- 1 | // @include './lib/json2.js' 2 | 3 | import { ns } from "../shared/shared"; 4 | 5 | import * as aeft from "./aeft/aeft"; // BOLT_AEFT_ONLY 6 | import * as ame from "./ame/ame"; // BOLT_AME_ONLY 7 | import * as anim from "./anim/anim"; // BOLT_ANIM_ONLY 8 | import * as audt from "./audt/audt"; // BOLT_AUDT_ONLY 9 | import * as idsn from "./idsn/idsn"; // BOLT_IDSN_ONLY 10 | import * as ilst from "./ilst/ilst"; // BOLT_ILST_ONLY 11 | import * as kbrg from "./kbrg/kbrg"; // BOLT_KBRG_ONLY 12 | import * as phxs from "./phxs/phxs"; // BOLT_PHXS_ONLY 13 | import * as ppro from "./ppro/ppro"; // BOLT_PPRO_ONLY 14 | 15 | //@ts-ignore 16 | const host = typeof $ !== "undefined" ? $ : window; 17 | 18 | // A safe way to get the app name since some versions of Adobe Apps broken BridgeTalk in various places (e.g. After Effects 24-25) 19 | // in that case we have to do various checks per app to deterimine the app name 20 | 21 | const getAppNameSafely = (): ApplicationName | "unknown" => { 22 | const compare = (a: string, b: string) => { 23 | return a.toLowerCase().indexOf(b.toLowerCase()) > -1; 24 | }; 25 | const exists = (a: any) => typeof a !== "undefined"; 26 | const isBridgeTalkWorking = 27 | typeof BridgeTalk !== "undefined" && 28 | typeof BridgeTalk.appName !== "undefined"; 29 | 30 | if (isBridgeTalkWorking) { 31 | return BridgeTalk.appName; 32 | } else if (app) { 33 | //@ts-ignore 34 | if (exists(app.name)) { 35 | //@ts-ignore 36 | const name: string = app.name; 37 | if (compare(name, "photoshop")) return "photoshop"; 38 | if (compare(name, "illustrator")) return "illustrator"; 39 | if (compare(name, "audition")) return "audition"; 40 | if (compare(name, "bridge")) return "bridge"; 41 | if (compare(name, "indesign")) return "indesign"; 42 | } 43 | //@ts-ignore 44 | if (exists(app.appName)) { 45 | //@ts-ignore 46 | const appName: string = app.appName; 47 | if (compare(appName, "after effects")) return "aftereffects"; 48 | if (compare(appName, "animate")) return "animate"; 49 | } 50 | //@ts-ignore 51 | if (exists(app.path)) { 52 | //@ts-ignore 53 | const path = app.path; 54 | if (compare(path, "premiere")) return "premierepro"; 55 | } 56 | //@ts-ignore 57 | if (exists(app.getEncoderHost) && exists(AMEFrontendEvent)) { 58 | return "ame"; 59 | } 60 | } 61 | return "unknown"; 62 | }; 63 | 64 | switch (getAppNameSafely()) { 65 | // BOLT_AEFT_START 66 | case "aftereffects": 67 | case "aftereffectsbeta": 68 | host[ns] = aeft; 69 | break; 70 | // BOLT_AEFT_END 71 | 72 | // BOLT_AME_START 73 | case "ame": 74 | case "amebeta": 75 | host[ns] = ame; 76 | break; 77 | // BOLT_AME_END 78 | 79 | // BOLT_ANIM_START 80 | case "animate": 81 | case "animatebeta": 82 | host[ns] = anim; 83 | break; 84 | // BOLT_ANIM_END 85 | 86 | // BOLT_AUDT_START 87 | case "audition": 88 | case "auditionbeta": 89 | host[ns] = audt; 90 | break; 91 | // BOLT_AUDT_END 92 | 93 | // BOLT_IDSN_START 94 | case "indesign": 95 | case "indesignbeta": 96 | host[ns] = idsn; 97 | break; 98 | // BOLT_IDSN_END 99 | 100 | // BOLT_ILST_START 101 | case "illustrator": 102 | case "illustratorbeta": 103 | host[ns] = ilst; 104 | break; 105 | // BOLT_ILST_END 106 | 107 | // BOLT_KBRG_START 108 | case "bridge": 109 | case "bridgebeta": 110 | host[ns] = kbrg; 111 | break; 112 | // BOLT_KBRG_END 113 | 114 | // BOLT_PHXS_START 115 | case "photoshop": 116 | case "photoshopbeta": 117 | host[ns] = phxs; 118 | break; 119 | // BOLT_PHXS_END 120 | 121 | // BOLT_PPRO_START 122 | case "premierepro": 123 | case "premiereprobeta": 124 | host[ns] = ppro; 125 | break; 126 | // BOLT_PPRO_END 127 | } 128 | 129 | const empty = {}; 130 | // prettier-ignore 131 | export type Scripts = typeof empty 132 | & typeof aeft // BOLT_AEFT_ONLY 133 | & typeof ame // BOLT_AME_ONLY 134 | & typeof anim // BOLT_ANIM_ONLY 135 | & typeof audt // BOLT_AUDT_ONLY 136 | & typeof idsn // BOLT_IDSN_ONLY 137 | & typeof ilst // BOLT_ILST_ONLY 138 | & typeof kbrg // BOLT_KBRG_ONLY 139 | & typeof phxs // BOLT_PHXS_ONLY 140 | & typeof ppro // BOLT_PPRO_ONLY 141 | ; 142 | 143 | // https://extendscript.docsforadobe.dev/interapplication-communication/bridgetalk-class.html?highlight=bridgetalk#appname 144 | type ApplicationName = 145 | | "aftereffects" 146 | | "aftereffectsbeta" 147 | | "ame" 148 | | "amebeta" 149 | | "audition" 150 | | "auditionbeta" 151 | | "animate" 152 | | "animatebeta" 153 | | "bridge" 154 | | "bridgebeta" 155 | // | "flash" 156 | | "illustrator" 157 | | "illustratorbeta" 158 | | "indesign" 159 | | "indesignbeta" 160 | // | "indesignserver" 161 | | "photoshop" 162 | | "photoshopbeta" 163 | | "premierepro" 164 | | "premiereprobeta"; 165 | -------------------------------------------------------------------------------- /src/jsx/kbrg/kbrg.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Bridge"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/kbrg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/shared/global" 9 | ] 10 | }, 11 | "include": ["./**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /src/jsx/lib/.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-detectable=false -------------------------------------------------------------------------------- /src/jsx/lib/json2.js: -------------------------------------------------------------------------------- 1 | "object"!=typeof JSON&&(JSON={}),function(){"use strict";var rx_one=/^[\],:{}\s]*$/,rx_two=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,rx_three=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,rx_four=/(?:^|:|,)(?:\s*\[)+/g,rx_escapable=/[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta,rep;function f(t){return t<10?"0"+t:t}function this_value(){return this.valueOf()}function quote(t){return rx_escapable.lastIndex=0,rx_escapable.test(t)?'"'+t.replace(rx_escapable,function(t){var e=meta[t];return"string"==typeof e?e:"\\u"+("0000"+t.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+t+'"'}function str(t,e){var r,n,o,u,f,a=gap,i=e[t];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(t)),"function"==typeof rep&&(i=rep.call(e,t,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,f=[],"[object Array]"===Object.prototype.toString.apply(i)){for(u=i.length,r=0;r app.activeDocument?.path.fsName; 2 | 3 | export const getDocumentPath = () => app.activeDocument?.fullName.fsName; 4 | 5 | export const getDocumentName = () => app.activeDocument?.name; 6 | -------------------------------------------------------------------------------- /src/jsx/phxs/phxs.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const helloWorld = () => { 13 | alert("Hello from Photoshop"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/jsx/phxs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/Photoshop/2015.5", 9 | "../../../node_modules/types-for-adobe/shared/PlugPlugExternalObject" 10 | ] 11 | }, 12 | "include": ["./**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /src/jsx/ppro/ppro-utils.ts: -------------------------------------------------------------------------------- 1 | // ProjectItem Helpers 2 | 3 | export const forEachChild = ( 4 | item: ProjectItem, 5 | callback: (item: ProjectItem) => void 6 | ) => { 7 | const len = item.children.numItems; 8 | for (let i = 0; i < len; i++) { 9 | callback(item.children[i]); 10 | } 11 | }; 12 | 13 | export const deleteItem = (item: ProjectItem) => { 14 | if (item.type === 2 /* BIN */) { 15 | item.deleteBin(); 16 | } else { 17 | const tmpBin = app.project.rootItem.createBin("tmp"); 18 | item.moveBin(tmpBin); 19 | tmpBin.deleteBin(); 20 | } 21 | }; 22 | 23 | export const getChildByName = (item: ProjectItem, name: string) => { 24 | for (let i = 0; i < item.children.numItems; i++) { 25 | const child = item.children[i]; 26 | if (child.name === name) { 27 | return child; 28 | } 29 | } 30 | }; 31 | 32 | export const getChildByNodeId = (item: ProjectItem, nodeId: string) => { 33 | for (let i = 0; i < item.children.numItems; i++) { 34 | const child = item.children[i]; 35 | if (child.nodeId === nodeId) { 36 | return child; 37 | } 38 | } 39 | }; 40 | 41 | export const getChildFromTreePath = (project: Project, treePath: string) => { 42 | const elements = treePath.split("\\"); // first item is blank, second is root 43 | let projectItem: ProjectItem | undefined = project.rootItem; 44 | for (let i = 2; i < elements.length; i++) { 45 | const item = elements[i]; 46 | projectItem = getChildByName(projectItem, item); 47 | if (!projectItem) return null; 48 | } 49 | return projectItem; 50 | }; 51 | 52 | export const getDescendantByNodeId = ( 53 | item: ProjectItem, 54 | nodeId: string 55 | ): ProjectItem | undefined => { 56 | for (let i = 0; i < item.children.numItems; i++) { 57 | const child = item.children[i]; 58 | if (child.nodeId === nodeId) { 59 | return child; 60 | } else if (child.type === 2 /* BIN */) { 61 | const found = getDescendantByNodeId(child, nodeId); 62 | if (found) return found; 63 | } 64 | } 65 | }; 66 | 67 | export const getParentItem = (item: ProjectItem) => { 68 | const dir = item.treePath.split("\\"); 69 | if (dir.length < 2) { 70 | return app.project.rootItem; 71 | } 72 | let current = app.project.rootItem; 73 | for (let i = 2; i < dir.length - 1; i++) { 74 | const name = dir[i]; 75 | const next = getChildByName(current, name); 76 | if (next) { 77 | current = next; 78 | } 79 | } 80 | return current; 81 | }; 82 | 83 | export const findItemByPath = ( 84 | item: ProjectItem, 85 | path: string 86 | ): ProjectItem | undefined => { 87 | const len = item.children.numItems; 88 | for (let i = 0; i < len; i++) { 89 | const child = item.children[i]; 90 | if (child.children && child.children.numItems > 0) { 91 | const res = findItemByPath(child, path); 92 | if (res) { 93 | return res; 94 | } 95 | } else if (child.getMediaPath() === path) { 96 | return child; 97 | } 98 | } 99 | }; 100 | 101 | // Sequence Helpers 102 | 103 | export const getSequenceFromProjectItem = (item: ProjectItem) => { 104 | for (let i = 0; i < app.project.sequences.numSequences; i++) { 105 | const seq = app.project.sequences[i]; 106 | if (seq.projectItem.nodeId === item.nodeId) { 107 | return seq; 108 | } 109 | } 110 | }; 111 | 112 | export const getSequenceLengthInFrames = (seq: Sequence) => { 113 | const settings = seq.getSettings(); 114 | const end = seq.end; 115 | const fps = settings.videoFrameRate.ticks; 116 | const frames = parseInt(end) / parseInt(fps); 117 | return frames; 118 | }; 119 | 120 | export const forEachVideoTrack = ( 121 | sequence: Sequence, 122 | callback: (track: Track, index: number) => void, 123 | reverse?: boolean 124 | ) => { 125 | const num = sequence.videoTracks.numTracks; 126 | if (reverse) { 127 | for (let i = num - 1; i > -1; i--) { 128 | callback(sequence.videoTracks[i], i); 129 | } 130 | } else { 131 | for (let i = 0; i < num; i++) { 132 | callback(sequence.videoTracks[i], i); 133 | } 134 | } 135 | }; 136 | 137 | export const forEachAudioTrack = ( 138 | sequence: Sequence, 139 | callback: (track: Track, index: number) => void, 140 | reverse?: boolean 141 | ) => { 142 | const num = sequence.audioTracks.numTracks; 143 | if (reverse) { 144 | for (let i = num - 1; i > -1; i--) { 145 | callback(sequence.audioTracks[i], i); 146 | } 147 | } else { 148 | for (let i = 0; i < num; i++) { 149 | callback(sequence.audioTracks[i], i); 150 | } 151 | } 152 | }; 153 | 154 | export const forEachClip = ( 155 | track: Track, 156 | callback: (clip: TrackItem, index: number) => void, 157 | reverse?: boolean 158 | ) => { 159 | const num = track.clips.numItems; 160 | if (reverse) { 161 | for (let i = num - 1; i > -1; i--) { 162 | callback(track.clips[i], i); 163 | } 164 | } else { 165 | for (let i = 0; i < num; i++) { 166 | callback(track.clips[i], i); 167 | } 168 | } 169 | }; 170 | 171 | // Time Helpers 172 | 173 | export const addTime = (a: Time, b: Time) => { 174 | const ticks = parseInt(a.ticks) + parseInt(b.ticks); 175 | let time = new Time(); 176 | time.ticks = ticks.toString(); 177 | return time; 178 | }; 179 | 180 | export const subtractTime = (a: Time, b: Time) => { 181 | const ticks = parseInt(a.ticks) - parseInt(b.ticks); 182 | let time = new Time(); 183 | time.ticks = ticks.toString(); 184 | return time; 185 | }; 186 | export const multiplyTime = (a: Time, factor: number) => { 187 | const ticks = parseInt(a.ticks) * factor; 188 | let time = new Time(); 189 | time.ticks = ticks.toString(); 190 | return time; 191 | }; 192 | export const divideTime = (a: Time, factor: number) => { 193 | const ticks = parseInt(a.ticks) / factor; 194 | let time = new Time(); 195 | time.ticks = ticks.toString(); 196 | return time; 197 | }; 198 | 199 | export const ticksToTime = (ticks: string) => { 200 | let time = new Time(); 201 | time.ticks = ticks; 202 | return time; 203 | }; 204 | 205 | const fpsTicksTable: { [key: number]: number } = { 206 | 23.976: 10594584000, 207 | 24: 10584000000, 208 | 25: 10160640000, 209 | 29.97: 8475667200, 210 | 30: 8467200000, 211 | 50: 5080320000, 212 | 59.94: 4237833600, 213 | 60: 4233600000, 214 | }; 215 | 216 | export const getItemFrameRate = (item: ProjectItem) => { 217 | if (item.isSequence()) { 218 | const sequence = getSequenceFromProjectItem(item); 219 | if (sequence) { 220 | return 1 / sequence.getSettings().videoFrameRate.seconds; 221 | } 222 | } else { 223 | const key = "Column.Intrinsic.MediaTimebase"; 224 | const mediaTimeBase = getPrMetadata(item, [key]); 225 | return parseFloat(mediaTimeBase[key]); 226 | } 227 | }; 228 | 229 | export const getItemDuration = (item: ProjectItem) => { 230 | const key = "Column.Intrinsic.MediaDuration"; 231 | const res = getPrMetadata(item, [key]); 232 | return parseFloat(res[key]); 233 | }; 234 | 235 | export const getFPSTime = (fps: number) => { 236 | let time = new Time(); 237 | let ticks = fpsTicksTable[fps]; 238 | if (!ticks) return false; 239 | time.ticks = ticks.toString(); 240 | return time; 241 | }; 242 | 243 | export const ticksToFrames = (ticks: string, timebase: string) => { 244 | const timebaseNum = parseInt(timebase); 245 | return parseInt(ticks) / timebaseNum; 246 | }; 247 | 248 | export const timecodeToSeconds = (timecode: string, frameRate: number) => { 249 | const segments = timecode.split(":"); 250 | const hours = parseInt(segments[0]); 251 | const minutes = parseInt(segments[1]); 252 | const seconds = parseInt(segments[2]); 253 | const frames = parseInt(segments[3]); 254 | return hours * 3600 + minutes * 60 + seconds + frames / frameRate; 255 | }; 256 | 257 | export const timecodeToTicks = (timecode: string, frameRate: number) => { 258 | const segments = timecode.split(":"); 259 | const hours = parseInt(segments[0]); 260 | const minutes = parseInt(segments[1]); 261 | const seconds = parseInt(segments[2]); 262 | const frames = parseInt(segments[3]); 263 | const totalSeconds = 264 | hours * 3600 + minutes * 60 + seconds + frames / frameRate; 265 | const ticks = totalSeconds * 10000000; // 1 second = 10,000,000 ticks 266 | return Math.round(ticks); 267 | }; 268 | 269 | export const secondsToTime = (seconds: number) => { 270 | let time = new Time(); 271 | time.seconds = seconds; 272 | return time; 273 | }; 274 | 275 | export const getTimecode = ( 276 | t: Time, 277 | frameRateTime: Time, 278 | videoDisplayFormat: number 279 | ) => { 280 | const timecode = t.getFormatted(frameRateTime, videoDisplayFormat) as string; 281 | return timecode; 282 | }; 283 | 284 | export const getTimecodeFromSequence = (t: Time, sequence: Sequence) => { 285 | return getTimecode( 286 | t, 287 | sequence.getSettings().videoFrameRate, 288 | sequence.getSettings().videoDisplayFormat 289 | ); 290 | }; 291 | 292 | // QE DOM Methods 293 | 294 | export const qeGetClipAt = (track: Track, index: number) => { 295 | let curClipIndex = -1; 296 | for (let i = 0; i < track.numItems; i++) { 297 | const item = track.getItemAt(i); 298 | //@ts-ignore 299 | const type = item.type as "Empty" | "Clip"; 300 | if (type === "Clip") { 301 | curClipIndex++; 302 | if (curClipIndex === index) { 303 | return item; 304 | } 305 | } 306 | } 307 | }; 308 | 309 | // QE DOM doesn't understand some format, so this function so we convert to compatible ones 310 | export const qeSafeTimeDisplayFormat = (timeDisplayFormat: number) => { 311 | const conversionTable: { 312 | [key: number]: number; 313 | } = { 314 | 998: 110, // 23.89 > 23.976 315 | }; 316 | const match = conversionTable[timeDisplayFormat]; 317 | return match ? match : timeDisplayFormat; 318 | }; 319 | 320 | // Metadata Helpers 321 | 322 | export const getPrMetadata = (projectItem: ProjectItem, fields: string[]) => { 323 | let PProMetaURI = "http://ns.adobe.com/premierePrivateProjectMetaData/1.0/"; 324 | if (ExternalObject.AdobeXMPScript === undefined) { 325 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 326 | } 327 | if (!app.isDocumentOpen() || !ExternalObject.AdobeXMPScript || !XMPMeta) { 328 | return {}; 329 | } 330 | let xmp = new XMPMeta(projectItem.getProjectMetadata()); 331 | let result: { 332 | [key: string]: string; 333 | } = {}; 334 | for (let i = 0; i < fields.length; i++) { 335 | if (xmp.doesPropertyExist(PProMetaURI, fields[i])) { 336 | result[fields[i]] = xmp.getProperty(PProMetaURI, fields[i]).value; 337 | } 338 | } 339 | return result; 340 | }; 341 | 342 | export const setPrMetadata = ( 343 | projectItem: ProjectItem, 344 | data: { 345 | fieldName: string; 346 | fieldId: string; 347 | value: string; 348 | }[] 349 | ) => { 350 | let PProMetaURI = "http://ns.adobe.com/premierePrivateProjectMetaData/1.0/"; 351 | if (ExternalObject.AdobeXMPScript === undefined) { 352 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 353 | } 354 | if (!app.isDocumentOpen() || !ExternalObject.AdobeXMPScript || !XMPMeta) { 355 | return {}; 356 | } 357 | let xmp = new XMPMeta(projectItem.getProjectMetadata()); 358 | for (var i = 0; i < data.length; i++) { 359 | let item = data[i]; 360 | var successfullyAdded = app.project.addPropertyToProjectMetadataSchema( 361 | item.fieldName, 362 | item.fieldId, 363 | 2 364 | ); 365 | } 366 | var array = []; 367 | for (var i = 0; i < data.length; i++) { 368 | let item = data[i]; 369 | xmp.setProperty(PProMetaURI, item.fieldName, item.value); 370 | array.push(item.fieldName); 371 | } 372 | var str = xmp.serialize(); 373 | projectItem.setProjectMetadata(str, array); 374 | }; 375 | 376 | export const removePrMetadata = ( 377 | projectItem: ProjectItem, 378 | fields: string[] 379 | ) => { 380 | let PProMetaURI = "http://ns.adobe.com/premierePrivateProjectMetaData/1.0/"; 381 | if (ExternalObject.AdobeXMPScript === undefined) { 382 | ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript"); 383 | } 384 | if (!app.isDocumentOpen() || !ExternalObject.AdobeXMPScript || !XMPMeta) { 385 | return {}; 386 | } 387 | let xmp = new XMPMeta(projectItem.getProjectMetadata()); 388 | var array = []; 389 | for (var i = 0; i < fields.length; i++) { 390 | xmp.deleteProperty(PProMetaURI, fields[i]); 391 | array.push(fields[i]); 392 | } 393 | var str = xmp.serialize(); 394 | projectItem.setProjectMetadata(str, array); 395 | }; 396 | 397 | // Motion Graphics Template ( MOGRT ) Helpers 398 | 399 | export const fillMogrtText = ( 400 | clip: TrackItem, 401 | propName: string, 402 | text: string 403 | ) => { 404 | const mgt = clip.getMGTComponent(); 405 | const prop = mgt.properties.getParamForDisplayName(propName); 406 | if (prop) { 407 | const valueStr = prop.getValue(); 408 | let value = JSON.parse(valueStr) as any; 409 | value.textEditValue = text; 410 | prop.setValue(JSON.stringify(value), true); 411 | } 412 | }; 413 | 414 | // Audio Conversions 415 | 416 | export const dbToDec = (x: number) => Math.pow(10, (x - 15) / 20); 417 | 418 | export const decToDb = (x: number) => 20 * Math.log(x) * Math.LOG10E + 15; 419 | -------------------------------------------------------------------------------- /src/jsx/ppro/ppro.ts: -------------------------------------------------------------------------------- 1 | import { 2 | helloVoid, 3 | helloError, 4 | helloStr, 5 | helloNum, 6 | helloArrayStr, 7 | helloObj, 8 | } from "../utils/samples"; 9 | export { helloError, helloStr, helloNum, helloArrayStr, helloObj, helloVoid }; 10 | import { dispatchTS } from "../utils/utils"; 11 | 12 | export const qeDomFunction = () => { 13 | if (typeof qe === "undefined") { 14 | app.enableQE(); 15 | } 16 | if (qe) { 17 | qe.name; 18 | qe.project.getVideoEffectByName("test"); 19 | } 20 | }; 21 | 22 | export const helloWorld = () => { 23 | alert("Hello from Premiere Pro."); 24 | }; 25 | -------------------------------------------------------------------------------- /src/jsx/ppro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "../global", 8 | "../../../node_modules/types-for-adobe/Premiere/24.0", 9 | "../../../node_modules/types-for-adobe-extras/Premiere/12.0/qeDom", 10 | "../../../node_modules/types-for-adobe/shared/PlugPlugExternalObject" 11 | ] 12 | }, 13 | "include": ["./**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /src/jsx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es3", 4 | "noLib": true, 5 | "strict": true, 6 | "types": [ 7 | "./global", 8 | "../../node_modules/types-for-adobe/shared/global", 9 | "../../node_modules/types-for-adobe/shared/PlugPlugExternalObject" 10 | ] 11 | }, 12 | "include": ["./**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /src/jsx/utils/samples.ts: -------------------------------------------------------------------------------- 1 | export const helloVoid = (): void => { 2 | alert("test"); 3 | }; 4 | export const helloError = (str: string) => { 5 | // Intentional Error for Error Handling Demonstration 6 | //@ts-ignore 7 | throw new Error(`We're throwing an error`); 8 | }; 9 | 10 | export const helloStr = (str: string) => { 11 | alert(`ExtendScript received a string: ${str}`); 12 | return str; 13 | }; 14 | export const helloNum = (n: number) => { 15 | alert(`ExtendScript received a number: ${n.toString()}`); 16 | return n; 17 | }; 18 | export const helloArrayStr = (arr: string[]) => { 19 | alert( 20 | `ExtendScript received an array of ${arr.length} strings: ${arr.toString()}` 21 | ); 22 | return arr; 23 | }; 24 | export const helloObj = (obj: { height: number; width: number }) => { 25 | alert(`ExtendScript received an object: ${JSON.stringify(obj)}`); 26 | return { 27 | y: obj.height, 28 | x: obj.width, 29 | }; 30 | }; 31 | -------------------------------------------------------------------------------- /src/jsx/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import type { EventTS } from "../../shared/universals"; 2 | import { ns } from "../../shared/shared"; 3 | 4 | /** 5 | * @function dispatchTS Displatches an event to the CEP panel with Type-Safety 6 | * See listenTS() in the CEP panel for more info 7 | * @param event The event name to listen for (defined in EventTS in shared/universals.ts) 8 | * @param callback The callback function to be executed when the event is triggered 9 | */ 10 | export const dispatchTS = ( 11 | event: Key, 12 | data: EventTS[Key] 13 | ) => { 14 | if (new ExternalObject("lib:PlugPlugExternalObject")) { 15 | var eventObj = new CSXSEvent(); 16 | eventObj.type = `${ns}.${event}`; 17 | eventObj.data = JSON.stringify(data); 18 | eventObj.dispatch(); 19 | } 20 | }; 21 | 22 | export const forEach = ( 23 | arr: T[], 24 | callback: (item: T, i: number) => void 25 | ): void => { 26 | for (let i = 0; i < arr.length; i++) { 27 | callback(arr[i], i); 28 | } 29 | }; 30 | 31 | export const map = ( 32 | arr: T[], 33 | callback: (item: T, i: number) => any 34 | ): T[] => { 35 | let res = []; 36 | for (let i = 0; i < arr.length; i++) { 37 | res.push(callback(arr[i], i)); 38 | } 39 | return res; 40 | }; 41 | 42 | export const filter = ( 43 | arr: T[], 44 | func: (item: T, i: number) => boolean 45 | ): T[] => { 46 | let res = []; 47 | for (let i = 0; i < arr.length; i++) { 48 | if (func(arr[i], i)) { 49 | res.push(arr[i]); 50 | } 51 | } 52 | return res; 53 | }; 54 | 55 | export const includes = (arr: T[], value: string | number) => { 56 | for (let i = 0; i < arr.length; i++) { 57 | const element = arr[i]; 58 | if (element === value) { 59 | return true; 60 | } 61 | } 62 | return false; 63 | }; 64 | 65 | export const indexOf = (arr: T[], value: string | number) => { 66 | for (let i = 0; i < arr.length; i++) { 67 | const element = arr[i]; 68 | if (element === value) { 69 | return i; 70 | } 71 | } 72 | return -1; 73 | }; 74 | 75 | // Joins paths 76 | export const join = (...args: string[]) => { 77 | const sep = $.os === "Windows" ? "\\" : "/"; 78 | const len = args.length; 79 | let res = args[0]; 80 | for (let i = 1; i < len; i++) { 81 | res = res + sep + args[i]; 82 | } 83 | return res; 84 | }; 85 | -------------------------------------------------------------------------------- /src/shared/shared.ts: -------------------------------------------------------------------------------- 1 | import config from "../../cep.config"; 2 | export const ns = config.id; 3 | export const company = config.zxp.org; 4 | export const displayName = config.displayName; 5 | export const version = config.version; 6 | -------------------------------------------------------------------------------- /src/shared/universals.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description Declare event types for listening with listenTS() and dispatching with dispatchTS() 3 | */ 4 | export type EventTS = { 5 | myCustomEvent: { 6 | oneValue: string; 7 | anotherValue: number; 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noImplicitAny": false, 5 | "paths": { 6 | "@esTypes/*": ["./src/js/lib/cep/es-types"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": false, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "paths": { 19 | "@esTypes/*": ["./src/jsx"] 20 | } 21 | }, 22 | "include": ["./src"], 23 | "exclude": [ 24 | "./src/jsx", 25 | "./src/js/template-*", 26 | "node_modules/@types/react/index.d.ts" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.react.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "paths": { 19 | "@esTypes/*": ["./src/jsx"] 20 | } 21 | }, 22 | "include": ["./src"], 23 | "exclude": ["./src/jsx"] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.svelte.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "preserve", 18 | "paths": { 19 | "@esTypes/*": ["./src/jsx"] 20 | } 21 | }, 22 | "include": ["./src"], 23 | "exclude": ["./src/jsx"] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.vue.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": false, 16 | "noEmit": true, 17 | "jsx": "preserve", 18 | "paths": { 19 | "@esTypes/*": ["./src/jsx"] 20 | } 21 | }, 22 | "include": ["./src"], 23 | "exclude": ["./src/jsx"] 24 | } 25 | -------------------------------------------------------------------------------- /vite-cep-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.p12 3 | /lib 4 | yarn-error.log 5 | *.DS_Store -------------------------------------------------------------------------------- /vite-cep-plugin/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Hyper Brew LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vite-cep-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Vite CEP Plugin 2 | 3 | ![npm](https://img.shields.io/npm/v/vite-cep-plugin) 4 | [![License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/hyperbrew/vite-cep-plugin/blob/master/LICENSE) 5 | [![Chat](https://img.shields.io/badge/chat-discord-7289da.svg)](https://discord.gg/PC3EvvuRbc) 6 | 7 | A plugin for bundling Adobe CEP Extension panels with the Vite.js bundler. 8 | 9 | Intented to be used with the `Bolt CEP` bolierplate: https://github.com/hyperbrew/bolt-cep 10 | 11 | Install or update with `yarn add vite-cep-plugin` 12 | -------------------------------------------------------------------------------- /vite-cep-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-cep-plugin", 3 | "version": "2.0.0", 4 | "description": "A Vite Plugin for building Adobe CEP Extension Panels", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/hyperbrew/bolt-cep.git" 10 | }, 11 | "homepage": "https://github.com/hyperbrew/bolt-cep", 12 | "license": "MIT", 13 | "scripts": { 14 | "build": "tsc && node scripts/copy-files.js", 15 | "prepare": "npm run build", 16 | "publish": "npm publish --access public", 17 | "publish-beta": "npm publish --access public --tag beta" 18 | }, 19 | "devDependencies": { 20 | "@types/fs-extra": "^9.0.13", 21 | "@types/ws": "^8.2.2", 22 | "ts-node": "^10.4.0", 23 | "typescript": "^4.6.4", 24 | "vite": "^4.0.5" 25 | }, 26 | "dependencies": { 27 | "@types/archiver": "^5.3.1", 28 | "archiver": "^5.3.1", 29 | "fs-extra": "^10.0.0", 30 | "jszip": "^3.10.1", 31 | "magic-string": "^0.25.7", 32 | "meta-bolt": "^0.0.15", 33 | "prettify-xml": "^1.2.0" 34 | }, 35 | "optionalDependencies": { 36 | "jsxbin": "^2.3.0" 37 | }, 38 | "files": [ 39 | "lib/**/*" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /vite-cep-plugin/scripts/copy-files.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs-extra"); 2 | var path = require("path"); 3 | var src = path.join(process.cwd(), "src", "bin"); 4 | var dst = path.join(process.cwd(), "lib", "bin"); 5 | fs.ensureDirSync(dst); 6 | fs.copySync(src, dst); 7 | -------------------------------------------------------------------------------- /vite-cep-plugin/scripts/copy-files.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | import * as fs from "fs-extra"; 3 | import * as path from "path"; 4 | 5 | const src = path.join(process.cwd(), "src", "bin"); 6 | const dst = path.join(process.cwd(), "lib", "bin"); 7 | fs.ensureDirSync(dst); 8 | fs.copySync(src, dst); 9 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/bin/ZXPSignCmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/vite-cep-plugin/src/bin/ZXPSignCmd -------------------------------------------------------------------------------- /vite-cep-plugin/src/bin/ZXPSignCmd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperbrew/bolt-cep/e23b2d0408a5962c3041ac0926feed86704f76bb/vite-cep-plugin/src/bin/ZXPSignCmd.exe -------------------------------------------------------------------------------- /vite-cep-plugin/src/cep-config.ts: -------------------------------------------------------------------------------- 1 | // For more details on Manifest Preferences see: 2 | // https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_11.x/Documentation/CEP%2011.1%20HTML%20Extension%20Cookbook.md 3 | 4 | type CEP_Host_Name = 5 | | "PHSP" 6 | | "PHXS" 7 | | "IDSN" 8 | | "AICY" 9 | | "ILST" 10 | | "PPRO" 11 | | "PRLD" 12 | | "AEFT" 13 | | "FLPR" 14 | | "AUDT" 15 | | "DRWV" 16 | | "KBRG" 17 | | "AME" 18 | | "MUSE" 19 | | "LTRM" 20 | | "DEMO" 21 | | "BRDG" 22 | | "RUSH"; 23 | 24 | type CEP_Host = { 25 | name: CEP_Host_Name; 26 | version: string; 27 | }; 28 | 29 | export type JSXBIN_MODE = "off" | "copy" | "replace"; 30 | 31 | type CEF_Command = 32 | | "--enable-media-stream" 33 | | "--enable-speech-input" 34 | | "--enable-file-cookies" 35 | | "--enable-nodejs" 36 | | "--persist-session-cookies" 37 | | "--disable-image-loading" 38 | | "--disable-javascript-open-windows" 39 | | "--disable-javascript-close-windows" 40 | | "--disable-javascript-access-clipboard" 41 | | "--disable-site-isolation-trials" 42 | | "--enable-caret-browsing" 43 | | "--proxy-auto-detect" 44 | | "--user-agent" 45 | | "--disable-application-cache" 46 | | "--disable-pinch" 47 | | "--mixed-context" 48 | | "--allow-file-access" 49 | | "--allow-file-access-from-files" 50 | | "--disable-popup-blocking" 51 | | "--aggressive-cache-discard" 52 | | "--winhttp-proxy-resolver" 53 | | "--v=0" 54 | | "--v=1" 55 | | "--v=2" 56 | | "--v=3" 57 | | "--v=4" 58 | | "--v=5"; 59 | 60 | type CEP_Panel_Type = 61 | | "Panel" 62 | | "ModalDialog" 63 | | "Modeless" 64 | | "Custom" 65 | | "Embedded"; 66 | 67 | export interface CEP_Panel { 68 | mainPath: string; 69 | name: string; 70 | panelDisplayName?: string | null; 71 | autoVisible: boolean; 72 | width?: number; 73 | height?: number; 74 | maxWidth?: number; 75 | maxHeight?: number; 76 | minWidth?: number; 77 | minHeight?: number; 78 | scriptPath?: string; 79 | host?: string; 80 | type?: CEP_Panel_Type; 81 | id?: string; 82 | iconDarkNormal?: string; 83 | iconNormal?: string; 84 | iconDarkNormalRollOver?: string; 85 | iconNormalRollOver?: string; 86 | parameters?: CEF_Command[]; 87 | startOnEvents?: string[]; 88 | } 89 | 90 | export interface CEP_Extended_Panel extends CEP_Panel { 91 | id: string; 92 | parameters: CEF_Command[]; 93 | type: CEP_Panel_Type; 94 | } 95 | 96 | export interface CEP_Config { 97 | port: number; 98 | servePort: number; 99 | symlink: "local" | "global"; 100 | startingDebugPort: number; 101 | version: string; 102 | id: string; 103 | displayName: string; 104 | extensionManifestVersion: number; 105 | requiredRuntimeVersion: number; 106 | hosts: CEP_Host[]; 107 | type: CEP_Panel_Type; 108 | iconDarkNormal?: string; 109 | iconNormal?: string; 110 | iconDarkNormalRollOver?: string; 111 | iconNormalRollOver?: string; 112 | parameters: CEF_Command[]; 113 | scriptPath?: string; 114 | width?: number; 115 | height?: number; 116 | maxWidth?: number; 117 | maxHeight?: number; 118 | minWidth?: number; 119 | minHeight?: number; 120 | standalone?: boolean; 121 | 122 | panels: CEP_Panel[]; 123 | 124 | build?: { 125 | sourceMap?: boolean; 126 | jsxBin?: JSXBIN_MODE; 127 | }; 128 | zxp: { 129 | country: string; 130 | province: string; 131 | org: string; 132 | password: string; 133 | tsa?: string | string[]; 134 | allowSkipTSA?: boolean; 135 | sourceMap?: boolean; 136 | jsxBin?: JSXBIN_MODE; 137 | }; 138 | installModules?: string[]; 139 | copyAssets?: string[]; 140 | copyZipAssets?: string[]; 141 | } 142 | 143 | export interface CEP_Config_Extended extends CEP_Config { 144 | panels: CEP_Extended_Panel[]; 145 | } 146 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/copy-node.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as fs from "fs-extra"; 3 | 4 | export const unique = (array: any) => { 5 | return array.filter((v: string, i: number, a: string) => a.indexOf(v) === i); 6 | }; 7 | 8 | interface NodeSolveArgs { 9 | src: string; 10 | pkg: string; 11 | keepDevDependencies: boolean; 12 | } 13 | 14 | const nodeSolve = ({ src, pkg, keepDevDependencies }: NodeSolveArgs) => { 15 | let allDependencies = [pkg]; 16 | const fullPath = path.join(src, "node_modules", pkg); 17 | // console.log(`getting pkgs for ${fullPath}`); 18 | const pkgJson = path.join(fullPath, "package.json"); 19 | if (fs.existsSync(pkgJson)) { 20 | const raw = fs.readFileSync(pkgJson, { encoding: "utf-8" }); 21 | const json = JSON.parse(raw); 22 | let { dependencies, devDependencies } = json; 23 | const depList = dependencies ? Object.keys(dependencies) : []; 24 | const devDepList = devDependencies ? Object.keys(devDependencies) : []; 25 | const resDepList = keepDevDependencies 26 | ? depList.concat(devDepList) 27 | : depList; 28 | if (resDepList.length > 0) { 29 | allDependencies = allDependencies.concat(resDepList); 30 | resDepList.map((name) => { 31 | allDependencies = allDependencies.concat( 32 | nodeSolve({ src, pkg: name, keepDevDependencies }) 33 | ); 34 | }); 35 | } 36 | } 37 | return allDependencies || []; 38 | }; 39 | 40 | interface CopyModulesArgs { 41 | packages: string[]; 42 | src: string; 43 | dest: string; 44 | symlink: boolean; 45 | } 46 | 47 | export const copyModules = ({ 48 | packages, 49 | src, 50 | dest, 51 | symlink, 52 | }: CopyModulesArgs) => { 53 | const allPkg = packages.flatMap((pkg) => 54 | nodeSolve({ src, pkg, keepDevDependencies: false }) 55 | ); 56 | const uniqePkg = unique(allPkg); 57 | console.log( 58 | `Copying ${packages.length} Node Module(s) (${ 59 | uniqePkg.length 60 | } Dependencies) : ${packages.join(",")}` 61 | ); 62 | fs.ensureDirSync(path.join(dest, "node_modules")); 63 | uniqePkg.map((pkg: string) => { 64 | const fullSrcPath = path.join(process.cwd(), src, "node_modules", pkg); 65 | const fullDstPath = path.join(process.cwd(), dest, "node_modules", pkg); 66 | fs.ensureDirSync(path.dirname(fullDstPath)); 67 | if (!symlink) { 68 | fs.copySync(fullSrcPath, fullDstPath, {dereference: true}); 69 | } else { 70 | fs.ensureSymlink(fullSrcPath, fullDstPath, "dir"); 71 | } 72 | }); 73 | }; 74 | 75 | interface CopyFilesArgs { 76 | src: string; 77 | dest: string; 78 | assets: string[]; 79 | } 80 | 81 | export const copyFiles = ({ src, dest, assets }: CopyFilesArgs) => { 82 | console.log(`Copying ${assets.length} Assets`); 83 | // fs.ensureDirSync(path.join(dest, "node_modules")); 84 | assets.map((asset: string) => { 85 | const fullSrcPath = path.join(src, asset); 86 | if (asset.indexOf("/*") === asset.length - 2) { 87 | // flatten folder 88 | const folder = asset.substring(0, asset.length - 2); 89 | const files = fs.readdirSync(path.join(src, folder)); 90 | 91 | files.map((file) => { 92 | const fullSrcPath = path.join(src, folder, file); 93 | const fullDstPath = path.join(dest, file); 94 | console.log(`COPY ${fullSrcPath} to ${fullDstPath}`); 95 | fs.ensureDirSync(path.dirname(fullDstPath)); 96 | fs.copySync(fullSrcPath, fullDstPath); 97 | }); 98 | } else { 99 | const fullDstPath = path.join(dest, asset); 100 | console.log(`COPY ${fullSrcPath} to ${fullDstPath}`); 101 | fs.ensureDirSync(path.dirname(fullDstPath)); 102 | fs.copySync(fullSrcPath, fullDstPath); 103 | } 104 | }); 105 | }; 106 | 107 | const rollupNodeCopyPlugin = ({ 108 | packages, 109 | src, 110 | dest, 111 | symlink, 112 | }: CopyModulesArgs) => { 113 | return { 114 | name: "copy-node-modules", 115 | buildEnd: async () => { 116 | copyModules({ packages, src, dest, symlink }); 117 | }, 118 | }; 119 | }; 120 | 121 | export default rollupNodeCopyPlugin; 122 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/lib/lib.ts: -------------------------------------------------------------------------------- 1 | export const fixAssetPathJS = (code: string) => { 2 | code = code.replace(/\=\"\.\/assets/g, `="../assets`); 3 | code = code.replace(/\=\"\/assets/g, `="../assets`); 4 | code = code.replace(/\(\"\.\/assets/g, `("../assets`); 5 | code = code.replace(/\(\"\/assets/g, `("../assets`); 6 | return code; 7 | }; 8 | 9 | export const fixAssetPathCSS = (code: string) => { 10 | code = code.replace(/\(\.\/assets/g, `(../assets`); 11 | code = code.replace(/\(\/assets/g, `(./`); 12 | return code; 13 | }; 14 | 15 | export const fixAssetPathHTML = (code: string) => { 16 | code = code.replace(/\=\"\/assets/g, `="../assets`); 17 | return code; 18 | }; 19 | 20 | export const removeModuleTags = (code: string) => { 21 | code = code.replace(/\/g, ""); 22 | code = code.replace(/\ 18 | 19 | 20 | 21 |
22 | 25 | 26 | 27 | 28 | `; 29 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/templates/extension-template.ts: -------------------------------------------------------------------------------- 1 | import type { CEP_Extended_Panel } from "../cep-config"; 2 | 3 | export const extensionTemplate = ({ 4 | id, 5 | name, 6 | parameters, 7 | autoVisible, 8 | mainPath, 9 | type, 10 | host, 11 | panelDisplayName, 12 | width, 13 | height, 14 | minWidth, 15 | minHeight, 16 | maxWidth, 17 | maxHeight, 18 | iconNormal, 19 | iconDarkNormal, 20 | iconNormalRollOver, 21 | iconDarkNormalRollOver, 22 | scriptPath, 23 | startOnEvents, 24 | }: CEP_Extended_Panel) => ` 25 | 26 | 27 | ${mainPath}${ 28 | (scriptPath && `${scriptPath}`) || "" 29 | }${ 30 | (parameters && 31 | parameters 32 | .map((item) => `\n${item.toString()}`) 33 | .join("")) || 34 | "" 35 | } 36 | 37 | 38 | 39 | ${autoVisible}${ 40 | (startOnEvents && 41 | `${startOnEvents 42 | .map((event) => `\n${event}`) 43 | .join("")}`) || 44 | "" 45 | } 46 | 47 | 48 | ${type} 49 | ${panelDisplayName ? `${panelDisplayName}` : ""} 50 | ${ 51 | width && height 52 | ? ` 53 | ${width} 54 | ${height} 55 | ` 56 | : "" 57 | }${ 58 | maxWidth && maxHeight 59 | ? ` 60 | ${maxWidth} 61 | ${maxHeight} 62 | ` 63 | : "" 64 | }${ 65 | minWidth && minHeight 66 | ? ` 67 | ${minWidth} 68 | ${minHeight} 69 | ` 70 | : "" 71 | } 72 | 73 | ${iconNormal} 74 | ${iconDarkNormal} 75 | ${iconNormalRollOver} 76 | ${iconDarkNormalRollOver} 77 | 78 | 79 | 80 | 81 | `; 82 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/templates/manifest-template.ts: -------------------------------------------------------------------------------- 1 | import type { CEP_Config_Extended } from "../cep-config"; 2 | import { extensionTemplate } from "./extension-template"; 3 | export const manifestTemplate = (props: CEP_Config_Extended) => { 4 | const { 5 | extensionManifestVersion, 6 | id, 7 | displayName, 8 | version, 9 | hosts, 10 | requiredRuntimeVersion, 11 | standalone, 12 | panels, 13 | } = props; 14 | return ` 17 | 24 | 25 | ${panels 26 | .map((panel) => ``) 27 | .filter((value, index, self) => self.indexOf(value) === index) // remove duplicates 28 | .join("")} 29 | 30 | 31 | 32 | ${hosts 33 | .map((host) => ``) 34 | .join("")} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ${panels 44 | .map((panel) => extensionTemplate(panel)) 45 | .join("")} 46 | `; 47 | }; 48 | -------------------------------------------------------------------------------- /vite-cep-plugin/src/templates/menu-html-template.ts: -------------------------------------------------------------------------------- 1 | export const menuHtmlTemplate = ({ 2 | displayName, 3 | menu, 4 | }: { 5 | displayName: string; 6 | menu: [{ name: string; url: string }]; 7 | }) => ` 8 | 9 | 10 | 11 | 12 | 13 | 14 | ${displayName} 15 | 34 | 35 | 36 | 37 |

Select Panel

38 | 43 | 44 | 45 | 46 | `; 47 | -------------------------------------------------------------------------------- /vite-cep-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "strict": true, 8 | "allowJs": true, 9 | "skipLibCheck": true 10 | }, 11 | "include": ["src", "src/lib/require-js.js", "src/bin/*"], 12 | "exclude": ["node_modules"] 13 | } 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | 3 | import react from "@vitejs/plugin-react"; // BOLT_REACT_ONLY 4 | import vue from "@vitejs/plugin-vue"; // BOLT_VUE_ONLY 5 | import { svelte } from "@sveltejs/vite-plugin-svelte"; // BOLT_SVELTE_ONLY 6 | import sveltePreprocess from "svelte-preprocess"; // BOLT_SVELTE_ONLY 7 | 8 | import { cep, runAction } from "vite-cep-plugin"; 9 | import cepConfig from "./cep.config"; 10 | import path from "path"; 11 | import { extendscriptConfig } from "./vite.es.config"; 12 | 13 | const extensions = [".js", ".ts", ".tsx"]; 14 | 15 | const devDist = "dist"; 16 | const cepDist = "cep"; 17 | 18 | const src = path.resolve(__dirname, "src"); 19 | const root = path.resolve(src, "js"); 20 | const outDir = path.resolve(__dirname, "dist", cepDist); 21 | 22 | const debugReact = process.env.DEBUG_REACT === "true"; 23 | const isProduction = process.env.NODE_ENV === "production"; 24 | const isMetaPackage = process.env.ZIP_PACKAGE === "true"; 25 | const isPackage = process.env.ZXP_PACKAGE === "true" || isMetaPackage; 26 | const isServe = process.env.SERVE_PANEL === "true"; 27 | const action = process.env.BOLT_ACTION; 28 | 29 | let input = {}; 30 | cepConfig.panels.map((panel) => { 31 | input[panel.name] = path.resolve(root, panel.mainPath); 32 | }); 33 | 34 | const config = { 35 | cepConfig, 36 | isProduction, 37 | isPackage, 38 | isMetaPackage, 39 | isServe, 40 | debugReact, 41 | dir: `${__dirname}/${devDist}`, 42 | cepDist: cepDist, 43 | zxpDir: `${__dirname}/${devDist}/zxp`, 44 | zipDir: `${__dirname}/${devDist}/zip`, 45 | packages: cepConfig.installModules || [], 46 | }; 47 | 48 | if (action) runAction(config, action); 49 | 50 | // https://vitejs.dev/config/ 51 | export default defineConfig({ 52 | plugins: [ 53 | react(), // BOLT_REACT_ONLY 54 | vue(), // BOLT_VUE_ONLY 55 | svelte({ preprocess: sveltePreprocess({ typescript: true }) }), // BOLT_SVELTE_ONLY 56 | cep(config), 57 | ], 58 | resolve: { 59 | alias: [{ find: "@esTypes", replacement: path.resolve(__dirname, "src") }], 60 | }, 61 | root, 62 | clearScreen: false, 63 | server: { 64 | port: cepConfig.port, 65 | }, 66 | preview: { 67 | port: cepConfig.servePort, 68 | }, 69 | 70 | build: { 71 | sourcemap: isPackage ? cepConfig.zxp.sourceMap : cepConfig.build?.sourceMap, 72 | watch: { 73 | include: "src/jsx/**", 74 | }, 75 | // commonjsOptions: { 76 | // transformMixedEsModules: true, 77 | // }, 78 | rollupOptions: { 79 | input, 80 | output: { 81 | manualChunks: {}, 82 | // esModule: false, 83 | preserveModules: false, 84 | format: "cjs", 85 | }, 86 | }, 87 | target: "chrome74", 88 | outDir, 89 | }, 90 | }); 91 | 92 | // rollup es3 build 93 | const outPathExtendscript = path.join("dist", cepDist, "jsx", "index.js"); 94 | extendscriptConfig( 95 | `src/jsx/index.ts`, 96 | outPathExtendscript, 97 | cepConfig, 98 | extensions, 99 | isProduction, 100 | isPackage 101 | ); 102 | -------------------------------------------------------------------------------- /vite.es.config.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { rollup, watch, RollupOptions, OutputOptions } from "rollup"; 3 | import nodeResolve from "@rollup/plugin-node-resolve"; 4 | import babel from "@rollup/plugin-babel"; 5 | import { jsxInclude, jsxBin, jsxPonyfill } from "vite-cep-plugin"; 6 | import { CEP_Config } from "vite-cep-plugin"; 7 | import json from "@rollup/plugin-json"; 8 | import path from "path"; 9 | 10 | const GLOBAL_THIS = "thisObj"; 11 | 12 | export const extendscriptConfig = ( 13 | extendscriptEntry: string, 14 | outPath: string, 15 | cepConfig: CEP_Config, 16 | extensions: string[], 17 | isProduction: boolean, 18 | isPackage: boolean 19 | ) => { 20 | console.log(outPath); 21 | const config: RollupOptions = { 22 | input: extendscriptEntry, 23 | treeshake: true, 24 | output: { 25 | file: outPath, 26 | sourcemap: isPackage 27 | ? cepConfig.zxp.sourceMap 28 | : cepConfig.build?.sourceMap, 29 | }, 30 | plugins: [ 31 | json(), 32 | nodeResolve({ 33 | extensions, 34 | }), 35 | babel({ 36 | extensions, 37 | exclude: /node_modules/, 38 | babelrc: false, 39 | babelHelpers: "inline", 40 | presets: ["@babel/preset-env", "@babel/preset-typescript"], 41 | plugins: [ 42 | "@babel/plugin-syntax-dynamic-import", 43 | "@babel/plugin-proposal-class-properties", 44 | ], 45 | }), 46 | jsxPonyfill(), 47 | jsxInclude({ 48 | iife: true, 49 | globalThis: GLOBAL_THIS, 50 | }), 51 | jsxBin(isPackage ? cepConfig.zxp.jsxBin : cepConfig.build?.jsxBin), 52 | ], 53 | }; 54 | 55 | async function build() { 56 | const bundle = await rollup(config); 57 | await bundle.write(config.output as OutputOptions); 58 | await bundle.close(); 59 | } 60 | 61 | const triggerHMR = () => { 62 | // No built-in way to trigger Vite's HMR reload from outside the root folder 63 | // Workaround will read and save index.html file for each panel to triggger reload 64 | console.log("ExtendScript Change"); 65 | cepConfig.panels.map((panel) => { 66 | const tmpPath = path.join(process.cwd(), "src", "js", panel.mainPath); 67 | if (fs.existsSync(tmpPath)) { 68 | const txt = fs.readFileSync(tmpPath, { encoding: "utf-8" }); 69 | fs.writeFileSync(tmpPath, txt, { encoding: "utf-8" }); 70 | } 71 | }); 72 | }; 73 | 74 | const watchRollup = async () => { 75 | const watcher = watch(config); 76 | watcher.on("event", ({ result }: any) => { 77 | if (result) { 78 | triggerHMR(); 79 | result.close(); 80 | } 81 | }); 82 | watcher.close(); 83 | }; 84 | 85 | if (isProduction) { 86 | build(); 87 | } else { 88 | watchRollup(); 89 | } 90 | }; 91 | --------------------------------------------------------------------------------