Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plainly-aescripts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { relinkFootage } from './relink';
import './shims';
import {
getCompLayerNames,
getInstalledFontsByFamilyNameAndStyleName,
getInstalledFontsByPostScriptName,
selectComp,
Expand Down Expand Up @@ -40,6 +41,7 @@ const utilsFunctions = {
selectLayer,
selectComp,
selectFile,
getCompLayerNames,
};

const validateFunctions = {
Expand Down
18 changes: 18 additions & 0 deletions plainly-aescripts/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,23 @@ function getFolderPath(folder: FolderItem): string {
return pathJoin(parentPath, folder.name);
}

/**
* @function getCompLayerNames
* @description Get all layer names in a given After Effects comp by ID
* @param {string} compId - The ID of the comp
* @returns {string} JSON array of layer names
*/
function getCompLayerNames(compId: string): string {
const comp = app.project.itemByID(Number(compId));
if (!comp || !(comp instanceof CompItem)) return JSON.stringify([]);

const names: string[] = [];
for (let i = 1; i <= comp.numLayers; i++) {
names.push(comp.layer(i).name);
}
return JSON.stringify(names);
}

function getInstalledFontsByPostScriptName(
postScriptName: string,
): string | undefined {
Expand Down Expand Up @@ -245,6 +262,7 @@ export {
getFolderPath,
getInstalledFontsByFamilyNameAndStyleName,
getInstalledFontsByPostScriptName,
getCompLayerNames,
getTextLayersByComp,
isWin,
pathJoin,
Expand Down
8 changes: 6 additions & 2 deletions plainly-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,25 @@
"test:ci:coverage": "jest --ci --coverage"
},
"dependencies": {
"@base-ui/react": "^1.3.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^8.0.0",
"@headlessui/react": "^2.2.0",
"@heroicons/react": "^2.1.5",
"@tanstack/react-query": "^5.66.0",
"archiver": "^5.3.2",
"axios": "^1.13.5",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"form-data": "^4.0.4",
"lodash-es": "^4.17.23",
"lucide-react": "^0.577.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hooks-global-state": "^2.1.0",
"semver": "^7.7.4"
"semver": "^7.7.4",
"tailwind-merge": "^3.5.0"
},
"devDependencies": {
"@babel/core": "^7.28.6",
Expand All @@ -35,7 +40,6 @@
"@babel/preset-typescript": "^7.28.5",
"@jest/globals": "^30.2.0",
"@tailwindcss/forms": "^0.5.9",
"@tailwindcss/typography": "^0.5.15",
"@types/archiver": "^6.0.3",
"@types/dotenv-webpack": "^7.0.8",
"@types/jest": "^30.0.0",
Expand Down
19 changes: 18 additions & 1 deletion plainly-plugin/src/node/bridge/AeScriptsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,24 @@ class AeScriptsApiClass {
}

/**
* Re-links footage items in the After Effects project to new file paths.
* Returns all layer names in a given After Effects composition.
* @param compId - The ID of the composition
* @returns An array of layer names
*/
async getCompLayerNames(compId: number): Promise<string[]> {
const result = await evalScriptAsync(
`getCompLayerNames(${JSON.stringify(compId)})`,
);
if (!result) return [];
try {
return JSON.parse(result);
} catch {
throw new Error('Failed to parse layer names data.');
}
}

/**
* Relinks footage items in the After Effects project to new file paths.
* @param relinkData - Object mapping item IDs to new file paths
*/
async relinkFootage(relinkData: RelinkData): Promise<void> {
Expand Down
16 changes: 12 additions & 4 deletions plainly-plugin/src/node/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,22 @@ async function get<T>(
return await instance.get(path, auth(apiKey));
}

async function post<T>(
async function post<T, B>(
path: string,
apiKey: string,
body: string,
): Promise<AxiosResponse<T, unknown>> {
body: B,
): Promise<AxiosResponse<T, B>> {
return await instance.post(path, body, auth(apiKey));
}

async function put<T, B>(
path: string,
apiKey: string,
body: B,
): Promise<AxiosResponse<T, B>> {
return await instance.put(path, body, auth(apiKey));
}

async function postFormData<T>(
path: string,
apiKey: string,
Expand Down Expand Up @@ -131,4 +139,4 @@ export const toPlainlyError = (error: unknown): PlainlyApiError => {
}
};

export { get, post, postFormData };
export { get, post, put, postFormData };
48 changes: 29 additions & 19 deletions plainly-plugin/src/ui/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { isDev, pluginBundleVersion } from '@src/env';
import { useMemo } from 'react';
import { Banner, Button, ExternalLink, Sidebar } from './components';
import {
Banner,
Button,
ExternalLink,
Sidebar,
TooltipProvider,
} from './components';
import { useGetLatestGithubRelease, useNavigate } from './hooks';
import {
AboutRoute,
ExportRoute,
ParametrizationRoute,
ProjectsRoute,
SettingsRoute,
UploadRoute,
Expand All @@ -28,6 +35,7 @@ export function App() {
if (currentPage === '/validate') return <ValidateRoute />;
if (currentPage === '/settings') return <SettingsRoute />;
if (currentPage === '/about') return <AboutRoute />;
if (currentPage === '/parametrization') return <ParametrizationRoute />;

return null;
}, [currentPage]);
Expand All @@ -38,29 +46,31 @@ export function App() {
<Button
secondary
onClick={reloadExtension}
className="absolute top-3 right-3 cursor-pointer z-40"
className="absolute top-3 right-3 cursor-pointer z-50"
>
Reload extension
</Button>
)}
<Sidebar />
<Banner show={showBanner}>
<p className="text-white text-xs font-medium">
A new version is available! πŸš€ See{' '}
<ExternalLink
to={`https://github.com/plainly-videos/after-effects-plugin/releases/tag/${data?.tag_name}`}
text="what's new"
/>{' '}
and{' '}
<ExternalLink
to="https://exchange.adobe.com/apps/cc/202811/plainly-videos"
text="upgrade"
/>
.
</p>
</Banner>
<TooltipProvider>
<Sidebar />
<Banner show={showBanner}>
<p className="text-white text-xs font-medium">
A new version is available! πŸš€ See{' '}
<ExternalLink
to={`https://github.com/plainly-videos/after-effects-plugin/releases/tag/${data?.tag_name}`}
text="what's new"
/>{' '}
and{' '}
<ExternalLink
to="https://exchange.adobe.com/apps/cc/202811/plainly-videos"
text="upgrade"
/>
.
</p>
</Banner>

<div className="flex-1 overflow-y-auto">{route}</div>
<div className="flex-1 overflow-y-auto">{route}</div>
</TooltipProvider>
</div>
);
}
2 changes: 1 addition & 1 deletion plainly-plugin/src/ui/components/common/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function Banner({
show ? 'flex' : 'hidden',
'h-6 bg-indigo-500 opacity-70 bg-opacity-60 justify-center items-center',
sidebarOpen
? 'ml-[3.75rem] xs:ml-36 mr-[3.75rem] w-[calc(100%-3.75rem)] xs:w-[calc(100%-9rem)]'
? 'ml-[3.75rem] xs:ml-[var(--sidebar-width)] mr-[3.75rem] w-[calc(100%-3.75rem)] xs:w-[calc(100%-var(--sidebar-width))]'
: 'ml-[3.75rem] w-[calc(100%-3.75rem)]',
)}
>
Expand Down
20 changes: 18 additions & 2 deletions plainly-plugin/src/ui/components/common/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,27 @@ export function Button({
loading,
type = 'submit',
icon: Icon,
iconRight: IconRight,
iconClassName,
}: {
secondary?: boolean;
loading?: boolean;
icon?: LucideIcon;
iconRight?: LucideIcon;
iconClassName?: string;
} & ComponentPropsWithRef<'button'>) {
const primary = !secondary;
const classPrimary =
'bg-indigo-500 hover:bg-indigo-400 focus-visible:outline-indigo-500 border-indigo-500 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2';
const classSecondary =
'bg-white/10 hover:bg-white/20 text-white border-white/10';

const iconClass = classNames('size-4 shrink-0', iconClassName);
const loaderClass = classNames('size-4 shrink-0 animate-spin', iconClassName);

const showLoaderLeft = loading && !IconRight;
const showLoaderRight = loading && !!IconRight;

return (
<button
onClick={onClick}
Expand All @@ -35,9 +45,15 @@ export function Button({
type={type}
disabled={disabled}
>
{Icon && !loading && <Icon className="size-4 shrink-0" />}
{loading && <LoaderCircleIcon className="size-4 shrink-0 animate-spin" />}
{/* LEFT */}
{!loading && Icon && <Icon className={iconClass} />}
{showLoaderLeft && <LoaderCircleIcon className={loaderClass} />}

{children}

{/* RIGHT */}
{!loading && IconRight && <IconRight className={iconClass} />}
{showLoaderRight && <LoaderCircleIcon className={loaderClass} />}
</button>
);
}
30 changes: 23 additions & 7 deletions plainly-plugin/src/ui/components/common/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
import classNames from 'classnames';

export function Checkbox({
id,
name,
label,
description,
defaultChecked = true,
onChange,
disabled = false,
}: {
id: string;
name: string;
label: string;
description?: string;
defaultChecked?: boolean;
onChange: React.Dispatch<React.SetStateAction<boolean>>;
disabled?: boolean;
}) {
return (
<div className="flex gap-3">
<div
className={classNames(
'flex gap-3',
disabled && 'pointer-events-none opacity-50',
)}
>
<div className="flex h-6 shrink-0 items-center">
<div className="group grid size-4 grid-cols-1">
<input
defaultChecked
id="comments"
name="comments"
defaultChecked={defaultChecked}
id={id}
name={name}
type="checkbox"
aria-describedby="comments-description"
aria-describedby={`${id}-description`}
className="col-start-1 row-start-1 appearance-none rounded border border-white/10 bg-white/5 checked:border-indigo-600 checked:bg-indigo-600 indeterminate:border-indigo-600 indeterminate:bg-indigo-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:border-white/10 disabled:bg-transparent forced-colors:appearance-auto"
onChange={(e) => onChange(e.target.checked)}
disabled={disabled}
/>
<svg
fill="none"
Expand All @@ -44,10 +60,10 @@ export function Checkbox({
</div>
</div>
<div className="text-xs">
<label htmlFor="comments" className="font-medium text-white">
<label htmlFor={id} className="font-medium text-white">
{label}
</label>
<p id="comments-description" className="text-gray-400">
<p id={`${id}-description`} className="text-gray-400">
{description}
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,18 @@ export function ConfirmationDialog({
<Dialog open={open} onClose={setOpen} className="relative">
<DialogBackdrop
transition
className="fixed inset-0 backdrop-blur-md transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in"
className="fixed inset-0 z-20 backdrop-blur-md transition-opacity data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in"
/>

<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="fixed inset-0 z-30 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<DialogPanel
transition
className={classNames(
'relative transform overflow-hidden rounded-lg bg-[rgb(29,29,30)] px-4 pb-4 pt-5 text-left shadow-xl transition-all data-[closed]:translate-y-4 data-[closed]:opacity-0 data-[enter]:duration-300 data-[leave]:duration-200 data-[enter]:ease-out data-[leave]:ease-in sm:my-8 sm:w-full sm:max-w-lg sm:p-6 data-[closed]:sm:translate-y-0 data-[closed]:sm:scale-95 border border-white/10',
sidebarOpen ? 'ml-[3.75rem] xs:ml-36' : 'ml-[3.75rem]',
sidebarOpen
? 'ml-[3.75rem] xs:ml-[var(--sidebar-width)]'
: 'ml-[3.75rem]',
)}
>
<div className="sm:flex sm:items-start">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function NotificationsOverlay() {
return (
<div
aria-live="assertive"
className="flex flex-col fixed pointer-events-none inset-0 justify-end px-4 py-6 sm:justify-start sm:p-6 z-50"
className="flex flex-col fixed pointer-events-none inset-0 justify-end px-4 py-6 sm:justify-start sm:p-6 z-40"
>
{notifications.map((notification) => (
<Notification
Expand Down
Loading
Loading