diff --git a/src/components/image-editor/LayersPanel.tsx b/src/components/image-editor/LayersPanel.tsx
index 0591f08..c5a0be8 100644
--- a/src/components/image-editor/LayersPanel.tsx
+++ b/src/components/image-editor/LayersPanel.tsx
@@ -1,4 +1,4 @@
-import { type DragEvent } from 'react'
+import { type DragEvent, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
ChevronDown,
@@ -20,7 +20,8 @@ import {
removeAtPath,
} from '@/lib/image-editor/layer-tree'
import { hasEffects } from '@/lib/image-editor/layer-effects'
-import type { EditorState, GroupLayer, Layer } from '@/lib/image-editor/types'
+import type { EditorState, GroupLayer, Layer, LayerColorTag } from '@/lib/image-editor/types'
+import { LAYER_TAG_COLORS, LAYER_TAGS } from '@/lib/image-editor/layer-color-tags'
type Props = {
state: EditorState
@@ -37,6 +38,14 @@ type Props = {
/** Add a mask to the given layer. For annotation / SO / group: insert a
* new raster MaskLayer above. For adjustment / filter: add maskDataUrl. */
onAddMask?: (id: string) => void
+ /** Which row is in inline-rename mode (double-click / context menu Rename). */
+ renamingId?: string | null
+ /** Begin inline rename for a row (double-click). */
+ onStartRename?: (id: string) => void
+ /** Commit a rename (Enter / blur) and exit edit mode. */
+ onCommitRename?: (id: string, name: string) => void
+ /** Set / clear a layer's organisational color tag. */
+ onSetColorTag?: (id: string, tag: LayerColorTag | undefined) => void
}
type DropMode = 'into' | 'sibling' | 'top'
@@ -66,6 +75,10 @@ export function LayersPanel({
onOpenStyle,
onLayerContextMenu,
onAddMask,
+ renamingId,
+ onStartRename,
+ onCommitRename,
+ onSetColorTag,
}: Props) {
const { t } = useTranslation()
@@ -156,6 +169,10 @@ export function LayersPanel({
onOpenStyle={onOpenStyle}
onLayerContextMenu={onLayerContextMenu}
onAddMask={onAddMask}
+ renamingId={renamingId}
+ onStartRename={onStartRename}
+ onCommitRename={onCommitRename}
+ onSetColorTag={onSetColorTag}
/>
))}
@@ -225,6 +242,10 @@ function LayerSubtree({
onOpenStyle,
onLayerContextMenu,
onAddMask,
+ renamingId,
+ onStartRename,
+ onCommitRename,
+ onSetColorTag,
}: {
layer: Layer
depth: number
@@ -238,6 +259,10 @@ function LayerSubtree({
onOpenStyle: (id: string) => void
onLayerContextMenu?: (id: string, x: number, y: number) => void
onAddMask?: (id: string) => void
+ renamingId?: string | null
+ onStartRename?: (id: string) => void
+ onCommitRename?: (id: string, name: string) => void
+ onSetColorTag?: (id: string, tag: LayerColorTag | undefined) => void
}) {
const group = isGroup(layer) ? layer : null
return (
@@ -257,6 +282,10 @@ function LayerSubtree({
onOpenStyle={() => onOpenStyle(layer.id)}
onContextMenu={(x, y) => onLayerContextMenu?.(layer.id, x, y)}
onAddMask={onAddMask}
+ renaming={renamingId === layer.id}
+ onStartRename={() => onStartRename?.(layer.id)}
+ onCommitRename={(name) => onCommitRename?.(layer.id, name)}
+ onSetColorTag={(tag) => onSetColorTag?.(layer.id, tag)}
/>
{group && group.expanded &&
[...group.children].reverse().map((c) => (
@@ -274,6 +303,10 @@ function LayerSubtree({
onOpenStyle={onOpenStyle}
onLayerContextMenu={onLayerContextMenu}
onAddMask={onAddMask}
+ renamingId={renamingId}
+ onStartRename={onStartRename}
+ onCommitRename={onCommitRename}
+ onSetColorTag={onSetColorTag}
/>
))}
>
@@ -304,6 +337,10 @@ function LayerRow({
onOpenStyle,
onContextMenu,
onAddMask,
+ renaming,
+ onStartRename,
+ onCommitRename,
+ onSetColorTag,
}: {
layer: Layer
depth: number
@@ -319,10 +356,15 @@ function LayerRow({
onOpenStyle: () => void
onContextMenu?: (x: number, y: number) => void
onAddMask?: (layerId: string) => void
+ renaming?: boolean
+ onStartRename?: () => void
+ onCommitRename?: (name: string) => void
+ onSetColorTag?: (tag: LayerColorTag | undefined) => void
}) {
const { t } = useTranslation()
const showFx = hasEffects(layer)
const labelKey = layerLabelKey(layer)
+ const [colorMenuOpen, setColorMenuOpen] = useState(false)
const labelArgs =
layer.kind === 'annotation' && layer.shape.kind === 'text'
? {
@@ -414,7 +456,73 @@ function LayerRow({
style={{ background: '#000' }}
/>
)}
- {t(labelKey, labelArgs)}
+ {/* Color tag dot — click cycles open a tiny swatch row. */}
+ {onSetColorTag && (
+
+
+ )}
+ {renaming ? (
+ e.stopPropagation()}
+ onKeyDown={(e) => {
+ e.stopPropagation()
+ if (e.key === 'Enter') onCommitRename?.((e.target as HTMLInputElement).value)
+ else if (e.key === 'Escape') onCommitRename?.(layer.name)
+ }}
+ onBlur={(e) => onCommitRename?.(e.target.value)}
+ />
+ ) : (
+ {
+ e.stopPropagation()
+ onStartRename?.()
+ }}
+ >
+ {layer.name && layer.name !== t(labelKey, labelArgs) ? layer.name : t(labelKey, labelArgs)}
+
+ )}
{showFx && (
{
diff --git a/src/components/image-editor/MenuBar.tsx b/src/components/image-editor/MenuBar.tsx
index 990c6f6..081dffb 100644
--- a/src/components/image-editor/MenuBar.tsx
+++ b/src/components/image-editor/MenuBar.tsx
@@ -112,6 +112,12 @@ type Props = {
mergeVisible?: () => void
stampVisible?: () => void
flatten?: () => void
+ /** Rasterize the selected layer to plain pixels. */
+ rasterizeLayer?: () => void
+ /** Copy / Paste / Clear the selected layer's fx stack. */
+ copyLayerStyle?: () => void
+ pasteLayerStyle?: () => void
+ clearLayerStyle?: () => void
/** Open Layer Style dialog. `kind` preselects an effect; undefined = "Blending Options" (no preselect). */
openLayerStyle?: (kind?: LayerEffectKind) => void
/** PS Type on Path — create a TextLayer that follows the selected path. */
@@ -881,6 +887,29 @@ export function MenuBar({ handlers }: Props) {
label: t('pages.imageEditor.menu.flatten'),
onClick: handlers.flatten,
},
+ {
+ id: 'rasterizeLayer',
+ label: t('pages.imageEditor.menu.rasterizeLayer'),
+ onClick: handlers.rasterizeLayer,
+ },
+ ],
+ { sep: true },
+ [
+ {
+ id: 'copyLayerStyle',
+ label: t('pages.imageEditor.menu.copyLayerStyle'),
+ onClick: handlers.copyLayerStyle,
+ },
+ {
+ id: 'pasteLayerStyle',
+ label: t('pages.imageEditor.menu.pasteLayerStyle'),
+ onClick: handlers.pasteLayerStyle,
+ },
+ {
+ id: 'clearLayerStyle',
+ label: t('pages.imageEditor.menu.clearLayerStyle'),
+ onClick: handlers.clearLayerStyle,
+ },
],
{ sep: true },
[
diff --git a/src/components/image-editor/RightSidebar.tsx b/src/components/image-editor/RightSidebar.tsx
index 286940d..1b3ea6c 100644
--- a/src/components/image-editor/RightSidebar.tsx
+++ b/src/components/image-editor/RightSidebar.tsx
@@ -11,7 +11,7 @@ import { LayersPanel } from './LayersPanel'
import { PathsPanel } from './PathsPanel'
import { PropertiesPanel } from './PropertiesPanel'
import type { ImageCache } from '@/lib/image-editor/drawing'
-import type { Action, Adjustments, BrushOptions, EditorState, Layer, LayerComp, Transforms } from '@/lib/image-editor/types'
+import type { Action, Adjustments, BrushOptions, EditorState, Layer, LayerColorTag, LayerComp, Transforms } from '@/lib/image-editor/types'
const LAYERS_HEIGHT_KEY = 'pf-layers-h'
const LAYERS_HEIGHT_DEFAULT = 320
@@ -37,6 +37,11 @@ type Props = {
/** Inline +Mask button in the layer row — adds an adjustment mask or a
* new MaskLayer above, depending on the target layer's kind. */
onAddMaskToLayer?: (id: string) => void
+ /** Layer rename (inline) + color tag plumbing → LayersPanel. */
+ renamingLayerId?: string | null
+ onStartRenameLayer?: (id: string) => void
+ onCommitRenameLayer?: (id: string, name: string) => void
+ onSetLayerColorTag?: (id: string, tag: LayerColorTag | undefined) => void
/** Paths panel — convert active selection ↔ vector path layer. */
onMakeWorkPath?: () => void
onMakeSelectionFromPath?: () => void
@@ -96,6 +101,10 @@ export function RightSidebar({
onReplaceSmartObjectContents,
onLayerContextMenu,
onAddMaskToLayer,
+ renamingLayerId,
+ onStartRenameLayer,
+ onCommitRenameLayer,
+ onSetLayerColorTag,
onMakeWorkPath,
onMakeSelectionFromPath,
image,
@@ -187,6 +196,10 @@ export function RightSidebar({
onOpenStyle={onOpenStyle}
onLayerContextMenu={onLayerContextMenu}
onAddMask={onAddMaskToLayer}
+ renamingId={renamingLayerId}
+ onStartRename={onStartRenameLayer}
+ onCommitRename={onCommitRenameLayer}
+ onSetColorTag={onSetLayerColorTag}
/>
)}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 6c55ddc..60d41e2 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1242,6 +1242,11 @@
"ungroupLayers": "Ungroup Layer",
"mergeDown": "Merge Down",
"mergeVisible": "Merge Visible",
+ "rasterizeLayer": "Rasterize Layer",
+ "renameLayer": "Rename Layer",
+ "copyLayerStyle": "Copy Layer Style",
+ "pasteLayerStyle": "Paste Layer Style",
+ "clearLayerStyle": "Clear Layer Style",
"stampVisible": "Stamp Visible",
"flatten": "Flatten Image",
"convertToSmartObject": "Convert to Smart Object",
@@ -1774,7 +1779,23 @@
"dropOutside": "Drop here to move out of group",
"resizeHandle": "Drag to resize Layers panel",
"editStyle": "Edit Layer Style",
- "addMask": "Add layer mask"
+ "addMask": "Add layer mask",
+ "colorTag": "Color label",
+ "colorTagNone": "No color"
+ },
+ "rasterize": {
+ "done": "Layer rasterized",
+ "failed": "Could not rasterize layer",
+ "noBbox": "Layer has no pixels to rasterize",
+ "unsupportedKind": "Adjustment, filter, and mask layers can't be rasterized directly.",
+ "alreadyRaster": "Layer is already a plain raster layer"
+ },
+ "layerStyleClip": {
+ "copied": "Layer style copied",
+ "pasted": "Layer style pasted",
+ "cleared": "Layer style cleared",
+ "empty": "This layer has no style to copy",
+ "nothingToPaste": "No layer style on the clipboard"
},
"layerStyle": {
"title": "Layer Style",
diff --git a/src/i18n/zh-CN.json b/src/i18n/zh-CN.json
index 03303e3..ffe8c31 100644
--- a/src/i18n/zh-CN.json
+++ b/src/i18n/zh-CN.json
@@ -1242,6 +1242,11 @@
"ungroupLayers": "取消图层编组",
"mergeDown": "向下合并",
"mergeVisible": "合并可见图层",
+ "rasterizeLayer": "栅格化图层",
+ "renameLayer": "重命名图层",
+ "copyLayerStyle": "拷贝图层样式",
+ "pasteLayerStyle": "粘贴图层样式",
+ "clearLayerStyle": "清除图层样式",
"stampVisible": "盖印可见图层",
"flatten": "拼合图像",
"convertToSmartObject": "转换为智能对象",
@@ -1774,7 +1779,23 @@
"dropOutside": "拖到此处移出组",
"resizeHandle": "拖动调整图层面板高度",
"editStyle": "编辑图层样式",
- "addMask": "添加图层蒙版"
+ "addMask": "添加图层蒙版",
+ "colorTag": "颜色标签",
+ "colorTagNone": "无颜色"
+ },
+ "rasterize": {
+ "done": "已栅格化图层",
+ "failed": "无法栅格化该图层",
+ "noBbox": "该图层没有可栅格化的像素",
+ "unsupportedKind": "调整、滤镜和蒙版图层不能直接栅格化。",
+ "alreadyRaster": "该图层已经是普通栅格图层"
+ },
+ "layerStyleClip": {
+ "copied": "已拷贝图层样式",
+ "pasted": "已粘贴图层样式",
+ "cleared": "已清除图层样式",
+ "empty": "该图层没有可拷贝的样式",
+ "nothingToPaste": "剪贴板上没有图层样式"
},
"layerStyle": {
"title": "图层样式",
diff --git a/src/lib/image-editor/layer-color-tags.ts b/src/lib/image-editor/layer-color-tags.ts
new file mode 100644
index 0000000..8beb396
--- /dev/null
+++ b/src/lib/image-editor/layer-color-tags.ts
@@ -0,0 +1,27 @@
+import type { LayerColorTag } from './types'
+
+/**
+ * PS-style layer color labels → CSS color, plus the ordered list for the
+ * picker. Kept in lib (not the LayersPanel component) so it can be imported by
+ * both the panel and any future consumer without tripping react-refresh's
+ * "components-only export" rule.
+ */
+export const LAYER_TAG_COLORS: Record = {
+ red: '#d9433c',
+ orange: '#e08a3c',
+ yellow: '#d7b13a',
+ green: '#4f9d52',
+ blue: '#3a8cff',
+ violet: '#8a63d2',
+ gray: '#8a8f98',
+}
+
+export const LAYER_TAGS: LayerColorTag[] = [
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'gray',
+]
diff --git a/src/lib/image-editor/types.ts b/src/lib/image-editor/types.ts
index edb988c..ae987cf 100644
--- a/src/lib/image-editor/types.ts
+++ b/src/lib/image-editor/types.ts
@@ -565,6 +565,9 @@ export type LayerEffect =
export type LayerEffectKind = LayerEffect['kind']
+/** PS-style layer color labels for organising the layer stack. */
+export type LayerColorTag = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'gray'
+
/** Canonical PS defaults for each effect kind — used when adding via the
* Layer Style dialog or Layer > Layer Style menu. */
export const DEFAULT_EFFECTS: { [K in LayerEffectKind]: Extract } = {
@@ -678,6 +681,13 @@ type LayerCommon = {
shadow?: Shadow
/** Modern fx stack. Render order is fixed (PS-aligned); see `effectsOf()`. */
effects?: LayerEffect[]
+ /**
+ * Optional organisational color tag (PS layer color label). Purely cosmetic —
+ * renders as a small dot on the LayersPanel row; round-trips through serialize
+ * for free since the whole layer object is persisted. One of the named tags
+ * below, or undefined for "none".
+ */
+ colorTag?: LayerColorTag
/**
* Optional clip baked in at commit time when a marquee/lasso selection was
* active. Confines the layer's drawn pixels to this region — matches PS
diff --git a/src/pages/ImageEditor.tsx b/src/pages/ImageEditor.tsx
index 5b66bc5..5af5869 100644
--- a/src/pages/ImageEditor.tsx
+++ b/src/pages/ImageEditor.tsx
@@ -128,6 +128,7 @@ import type {
FilterParams,
GroupLayer,
Layer,
+ LayerColorTag,
LayerEffect,
LayerEffectKind,
OutputFormat,
@@ -287,6 +288,9 @@ export function ImageEditorPage() {
const [liquifySize, setLiquifySize] = useState(60)
const [liquifyStrength, setLiquifyStrength] = useState(50) // %
const [selectedLayerId, setSelectedLayerId] = useState('image')
+ // Which layer row is in inline-rename mode (LayersPanel double-click / context
+ // menu Rename). null = none editing.
+ const [renamingLayerId, setRenamingLayerId] = useState(null)
const [outFormat, setOutFormat] = useState('png')
const [outQuality, setOutQuality] = useState(92)
@@ -1472,6 +1476,118 @@ export function ImageEditorPage() {
toast.success(t('pages.imageEditor.smartObject.converted'))
}, [image, selectedLayerId, state, imageCache, ensureImage, history, t])
+ /**
+ * Rasterize Layer — bake the selected layer (post effects / clip / opacity)
+ * into a plain image-shape layer at the same id/bbox. The prototype exposes
+ * this as a placeholder; the lib helper (rasterizeLayer) already existed and
+ * was only used internally by Convert-to-Smart-Object. Pixel-emitting layers
+ * (annotation / smartObject / group) qualify; the image background and pure
+ * adjustment/filter/mask layers have no standalone silhouette to bake.
+ */
+ const handleRasterizeLayer = useCallback(() => {
+ if (!image || !selectedLayerId || selectedLayerId === 'image') return
+ const layer = findLayerById(state.layers, selectedLayerId)
+ if (!layer) return
+ if (
+ layer.kind === 'mask' ||
+ layer.kind === 'adjustment' ||
+ layer.kind === 'filter'
+ ) {
+ toast.message(t('pages.imageEditor.rasterize.unsupportedKind'))
+ return
+ }
+ if (layer.kind === 'annotation' && layer.shape.kind === 'image' && !layer.effects?.length && !layer.shadow) {
+ toast.message(t('pages.imageEditor.rasterize.alreadyRaster'))
+ return
+ }
+ const bbox = getLayerBBox(layer)
+ if (!bbox || bbox.w <= 0 || bbox.h <= 0) {
+ toast.message(t('pages.imageEditor.rasterize.noBbox'))
+ return
+ }
+ const rast = rasterizeLayer({ image, state, imageCache, layerId: layer.id, crop: bbox })
+ if (!rast) {
+ toast.error(t('pages.imageEditor.rasterize.failed'))
+ return
+ }
+ const replacement = buildImageShapeLayer({
+ dataUrl: rast.dataUrl,
+ bbox: rast.bbox,
+ name: layer.name,
+ })
+ // Keep id + display fields; effects/shadow are now baked into the pixels.
+ const merged: Layer = {
+ ...replacement,
+ id: layer.id,
+ name: layer.name,
+ visible: layer.visible,
+ opacity: layer.opacity,
+ blend: layer.blend,
+ colorTag: layer.colorTag,
+ }
+ ensureImage(rast.dataUrl).catch(() => {})
+ history.set({ ...state, layers: mapLayerById(state.layers, layer.id, () => merged) })
+ setSelectedLayerId(merged.id)
+ toast.success(t('pages.imageEditor.rasterize.done'))
+ }, [image, selectedLayerId, state, imageCache, ensureImage, history, t])
+
+ // ── Layer Style clipboard (Copy / Paste / Clear) ───────────────────────
+ // Prototype exposes these as placeholders. We move the existing fx stack
+ // between layers; no render-pipeline change. The clipboard is a ref (does
+ // not need to trigger re-render) holding a deep-cloned effects array.
+ const layerStyleClipboard = useRef(null)
+ const handleCopyLayerStyle = useCallback(() => {
+ if (!selectedLayerId || selectedLayerId === 'image') return
+ const layer = findLayerById(state.layers, selectedLayerId)
+ if (!layer) return
+ const fx = layer.effects ?? []
+ if (fx.length === 0 && !layer.shadow) {
+ toast.message(t('pages.imageEditor.layerStyleClip.empty'))
+ return
+ }
+ // Deep clone so paste targets don't alias the source's gradient/id objects.
+ layerStyleClipboard.current = JSON.parse(JSON.stringify(fx)) as LayerEffect[]
+ toast.success(t('pages.imageEditor.layerStyleClip.copied'))
+ }, [selectedLayerId, state.layers, t])
+ const handlePasteLayerStyle = useCallback(() => {
+ if (!selectedLayerId || selectedLayerId === 'image') return
+ const fx = layerStyleClipboard.current
+ if (!fx) {
+ toast.message(t('pages.imageEditor.layerStyleClip.nothingToPaste'))
+ return
+ }
+ patchLayer(selectedLayerId, {
+ effects: JSON.parse(JSON.stringify(fx)) as LayerEffect[],
+ shadow: undefined,
+ })
+ toast.success(t('pages.imageEditor.layerStyleClip.pasted'))
+ }, [selectedLayerId, patchLayer, t])
+ const handleClearLayerStyle = useCallback(() => {
+ if (!selectedLayerId || selectedLayerId === 'image') return
+ const layer = findLayerById(state.layers, selectedLayerId)
+ if (!layer) return
+ if ((layer.effects?.length ?? 0) === 0 && !layer.shadow) {
+ toast.message(t('pages.imageEditor.layerStyleClip.empty'))
+ return
+ }
+ patchLayer(selectedLayerId, { effects: [], shadow: undefined })
+ toast.success(t('pages.imageEditor.layerStyleClip.cleared'))
+ }, [selectedLayerId, state.layers, patchLayer, t])
+
+ // ── Rename + Color Tag ─────────────────────────────────────────────────
+ const handleRenameLayer = useCallback(
+ (id: string, name: string) => {
+ const trimmed = name.trim()
+ if (!trimmed) return
+ patchLayer(id, { name: trimmed })
+ },
+ [patchLayer],
+ )
+ const handleSetLayerColorTag = useCallback(
+ (id: string, colorTag: LayerColorTag | undefined) => patchLayer(id, { colorTag }),
+ [patchLayer],
+ )
+
/**
* Replace Contents — pick a new image file, update the SO's source dataUrl
* (and dims). All other SO layers referencing the same sourceRef update
@@ -2958,6 +3074,11 @@ export function ImageEditorPage() {
disabled: !canHaveFx,
},
{ sep: true },
+ {
+ id: 'rename',
+ label: t('pages.imageEditor.menu.renameLayer') + '…',
+ onClick: () => setRenamingLayerId(id),
+ },
{
id: 'dup',
label: t('pages.imageEditor.menu.duplicateLayer'),
@@ -2972,6 +3093,43 @@ export function ImageEditorPage() {
danger: true,
},
{ sep: true },
+ {
+ id: 'copyStyle',
+ label: t('pages.imageEditor.menu.copyLayerStyle'),
+ onClick: () => {
+ setSelectedLayerId(id)
+ handleCopyLayerStyle()
+ },
+ disabled: !canHaveFx,
+ },
+ {
+ id: 'pasteStyle',
+ label: t('pages.imageEditor.menu.pasteLayerStyle'),
+ onClick: () => {
+ setSelectedLayerId(id)
+ handlePasteLayerStyle()
+ },
+ disabled: !canHaveFx,
+ },
+ {
+ id: 'clearStyle',
+ label: t('pages.imageEditor.menu.clearLayerStyle'),
+ onClick: () => {
+ setSelectedLayerId(id)
+ handleClearLayerStyle()
+ },
+ disabled: !canHaveFx,
+ },
+ { sep: true },
+ {
+ id: 'rasterize',
+ label: t('pages.imageEditor.menu.rasterizeLayer'),
+ onClick: () => {
+ setSelectedLayerId(id)
+ handleRasterizeLayer()
+ },
+ disabled: target.kind === 'adjustment' || target.kind === 'filter' || target.kind === 'mask',
+ },
{
id: 'convertSO',
label: t('pages.imageEditor.menu.convertToSmartObject'),
@@ -3000,7 +3158,16 @@ export function ImageEditorPage() {
]
setContextMenu({ x, y, items, header: target.name })
},
- [state.layers, t, handleConvertToSmartObject, handleReplaceContents],
+ [
+ state.layers,
+ t,
+ handleConvertToSmartObject,
+ handleReplaceContents,
+ handleRasterizeLayer,
+ handleCopyLayerStyle,
+ handlePasteLayerStyle,
+ handleClearLayerStyle,
+ ],
)
const handleLayerStyleApply = useCallback(
@@ -3598,6 +3765,10 @@ export function ImageEditorPage() {
mergeVisible: handleMergeVisible,
stampVisible: handleStampVisible,
flatten: handleFlatten,
+ rasterizeLayer: handleRasterizeLayer,
+ copyLayerStyle: handleCopyLayerStyle,
+ pasteLayerStyle: handlePasteLayerStyle,
+ clearLayerStyle: handleClearLayerStyle,
convertToSmartObject: handleConvertToSmartObject,
replaceSmartObjectContents: handleReplaceContents,
isSmartObjectSelected:
@@ -3883,6 +4054,13 @@ export function ImageEditorPage() {
void handleNewRasterMask()
}
}}
+ renamingLayerId={renamingLayerId}
+ onStartRenameLayer={setRenamingLayerId}
+ onCommitRenameLayer={(id, name) => {
+ handleRenameLayer(id, name)
+ setRenamingLayerId(null)
+ }}
+ onSetLayerColorTag={handleSetLayerColorTag}
image={image}
imageCache={imageCache}
history={{