Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0e9b71c
chore: add yalc for local package linking and development workflow
ahnv May 6, 2026
27ee907
feat: add initialTemplateId prop to ImageKitEditor and implement temp…
manu4543 May 7, 2026
b1fd803
feat: introduce canvas mode and editor mode configuration; enhance te…
manu4543 May 7, 2026
71cfda4
feat: add CanvasSettingsPopover component for canvas configuration; i…
manu4543 May 7, 2026
3fbc042
feat: enhance CanvasSettingsPopover with background toggle and update…
manu4543 May 8, 2026
ce0b2e7
feat: introduce variable support in transformation editor
manu4543 May 8, 2026
a5cdd6d
feat: prevent infinite loop in VariableField by stabilizing onChange …
manu4543 May 9, 2026
f302f99
feat: add unique id handling for VariableField and TransformationFiel…
manu4543 May 9, 2026
0b4e56b
feat: add async image picker support to VariableField and Transformat…
manu4543 May 9, 2026
a077453
feat: update @imagekit/javascript to version 5.3.0 and add layer anch…
manu4543 May 9, 2026
ca7e185
feat: add rawTransformation support for text and image layers in tran…
manu4543 May 9, 2026
6169cfc
fix: update help text for transformation schema to remove unnecessary…
manu4543 May 9, 2026
e609206
feat: add support for nested layers
manu4543 May 9, 2026
9cd602c
feat: enhance UI interactions in SortableTransformationItem with impr…
manu4543 May 9, 2026
13488c0
feat: add tests for buildVariablesSchema and enhance variable resolut…
manu4543 May 10, 2026
9fa9e6e
feat: add sourceUrl to CanvasConfig and enforce its presence in canva…
manu4543 May 10, 2026
de801a8
feat: enhance variable resolution to include nested layer children in…
manu4543 May 10, 2026
7872b88
feat: improve default values handling and error filtering for transfo…
manu4543 May 10, 2026
8419b8c
feat: enhance variable handling and validation in transformation config
manu4543 May 14, 2026
76e2472
feat: add VariablesListPopover component and integrate variable displ…
manu4543 May 14, 2026
c86d617
feat: enhance transformation handling with nested layer support and c…
manu4543 May 14, 2026
894c7eb
feat: enhance file picker with tooltip and add compact clear indicato…
manu4543 May 14, 2026
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ packages/imagekit-editor/*.tgz
builds
packages/imagekit-editor/README.md
.cursor
coverage
coverage
.yalc
yalc.lock
67 changes: 67 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Development

## Prerequisites

- Node.js v20 (use `nvm use`)
- Yarn 4 (via Corepack)
- [yalc](https://github.com/wclr/yalc) (included as a devDependency)

## Getting Started

```bash
nvm use
yarn install
yarn dev
```

`yarn dev` runs vite in watch mode and **automatically publishes `@imagekit/editor` to the local yalc store** on every rebuild.

## Linking to External Projects

Use yalc to test `@imagekit/editor` in any project outside this monorepo:

### 1. Start dev mode (this repo)

```bash
yarn dev
```

This watches for source changes, rebuilds, and runs `yalc publish --push` automatically after each build.

### 2. Install yalc globally (required for consuming projects)

```bash
npm i -g yalc
```

### 3. Link in your consuming project

```bash
# In your external project directory
yalc link @imagekit/editor
```

This creates a symlink to the yalc store. Every time the editor rebuilds, your project receives the update automatically via `--push`.

### 4. Remove the link when done

```bash
# In your external project directory
yalc remove @imagekit/editor
```

## Build

```bash
yarn build
```

Produces the production bundle in `packages/imagekit-editor/dist/`.

## Package

```bash
yarn package
```

Creates a `.tgz` tarball in `builds/` for manual distribution or testing.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"lint-staged": "^16.1.2",
"shx": "^0.4.0",
"turbo": "^2.0.1",
"vitest": "^2.1.9"
"vitest": "^2.1.9",
"yalc": "^1.0.0-pre.53"
},
"packageManager": "yarn@4.9.2",
"lint-staged": {
Expand Down
2 changes: 1 addition & 1 deletion packages/imagekit-editor-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@hookform/resolvers": "^5.1.1",
"@imagekit/javascript": "^5.1.0",
"@imagekit/javascript": "^5.3.0",
"@react-icons/all-files": "https://github.com/react-icons/react-icons/releases/download/v5.4.0/react-icons-all-files-5.4.0.tgz",
"@tanstack/react-virtual": "^3.13.12",
"framer-motion": "6.5.1",
Expand Down
94 changes: 93 additions & 1 deletion packages/imagekit-editor-dev/src/ImageKitEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ import type { GetTemplatePermissions } from "./context/TemplatePermissionsContex
import { TemplatePermissionsContextProvider } from "./context/TemplatePermissionsContext"
import { TemplateStorageContextProvider } from "./context/TemplateStorageContext"
import {
applyTemplateStorageAccessFailure,
isTemplateAccessDeniedError,
type TemplateStorageProvider,
} from "./storage"
import {
applyTemplateRecord,
type CanvasConfig,
type EditorMode,
type FocusObjects,
type InputFileElement,
type OnPickImage,
type RequiredMetadata,
type Signer,
type Transformation,
Expand Down Expand Up @@ -82,6 +87,16 @@ interface EditorProps<Metadata extends RequiredMetadata = RequiredMetadata> {
initialImages?: Array<string | InputFileElement<Metadata>>
signer?: Signer<Metadata>
onAddImage?: () => void
/**
* Optional async image picker. When provided, image-path fields (currently
* the image layer's `imageUrl`) render a small folder icon next to the
* input; clicking it invokes this callback. Resolve to a URL/path string to
* fill the field, or to `null`/`undefined` to leave the field unchanged.
*
* The host owns the picker UI and any backend calls; the editor never opens
* a media library itself.
*/
onPickImage?: OnPickImage
exportOptions?: HeaderProps<Metadata>["exportOptions"]
focusObjects?: ReadonlyArray<FocusObjects>
onClose: (args: { dirty: boolean; destroy: () => void }) => void
Expand All @@ -96,6 +111,26 @@ interface EditorProps<Metadata extends RequiredMetadata = RequiredMetadata> {
* If omitted, the editor defaults to allowing all actions.
*/
getTemplatePermissions?: GetTemplatePermissions
/**
* Open the editor with this template pre-loaded. The editor calls
* `templateStorage.getTemplate(initialTemplateId)` on mount and applies
* the result. Requires `templateStorage` to be configured.
*
* Failures (template not found, access denied, network error) are surfaced
* via the standard sync-status error UI; the editor still opens empty.
*/
initialTemplateId?: string
/**
* Editor authoring mode. Defaults to `"editing"` (regular media editing).
* Pass `"canvas"` to author a layer-only template against a sized blank
* canvas; `canvas` prop must also be provided in that case.
*
* If `initialTemplateId` is supplied and the loaded template has its own
* `mode`, that wins (the template carries its authoring context).
*/
mode?: EditorMode
/** Canvas dimensions and optional background. Required when `mode === "canvas"`. */
canvas?: CanvasConfig
}

function ImageKitEditorImpl<M extends RequiredMetadata>(
Expand All @@ -106,9 +141,13 @@ function ImageKitEditorImpl<M extends RequiredMetadata>(
theme,
initialImages,
signer,
onPickImage,
focusObjects,
templateStorage,
getTemplatePermissions,
initialTemplateId,
mode,
canvas,
} = props
const {
addImage,
Expand Down Expand Up @@ -143,6 +182,8 @@ function ImageKitEditorImpl<M extends RequiredMetadata>(
...(state.templateIsPrivate !== null
? { isPrivate: state.templateIsPrivate }
: {}),
mode: state.mode,
...(state.mode === "canvas" ? { canvas: state.canvas } : {}),
})
const after = useEditorStore.getState()
after.hydrateTemplateMetadata({
Expand Down Expand Up @@ -203,9 +244,60 @@ function ImageKitEditorImpl<M extends RequiredMetadata>(
initialize({
imageList: initialImages,
signer,
onPickImage,
focusObjects,
mode,
canvas,
})
}, [initialImages, signer, focusObjects, initialize])
}, [initialImages, signer, onPickImage, focusObjects, initialize, mode, canvas])

// Load template by id from the configured storage provider when
// `initialTemplateId` is supplied. This runs after `initialize` so it can
// overwrite any reset metadata. Keyed on (provider, id) so switching either
// re-fetches.
React.useEffect(() => {
if (!initialTemplateId) return
if (!resolvedProvider) {
console.warn(
"ImageKitEditor: `initialTemplateId` was provided but no `templateStorage` is configured.",
)
return
}

let cancelled = false
const store = useEditorStore.getState()

resolvedProvider
.getTemplate(initialTemplateId)
.then((record) => {
if (cancelled) return
if (!record) {
useEditorStore
.getState()
.setSyncStatus("error", "Template not found.")
return
}
applyTemplateRecord(record)
})
.catch((err) => {
if (cancelled) return
const handled = applyTemplateStorageAccessFailure(err, {
denyTemplateStorageAccessAndReset:
store.denyTemplateStorageAccessAndReset,
})
if (handled) return
useEditorStore
.getState()
.setSyncStatus(
"error",
err instanceof Error ? err.message : "Failed to load template",
)
})

return () => {
cancelled = true
}
}, [resolvedProvider, initialTemplateId])

useImperativeHandle(
ref,
Expand Down
Loading
Loading