feat: Add editor UI toggle functionality

- 캔버스 편집 UI 표시/숨김 기능을 추가했습니다.
- 에디터 툴바에 토글 버튼을 추가하여 UI 표시 상태를 제어할 수 있습니다.
- 에디터 UI가 숨겨졌을 때 캔버스에 마우스 이벤트가 전달되지 않도록 수정했습니다.
This commit is contained in:
BaekRyang 2025-11-05 11:52:33 +09:00
parent 0c3c0b606e
commit fed9dc7606
11 changed files with 299 additions and 99 deletions

6
.idea/AICommit.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="com.github.blarc.ai.commits.intellij.plugin.settings.ProjectSettings">
<option name="splitButtonActionSelectedLLMClientId" value="2f900cba-1f90-431b-acb2-e4e6ac66b31e" />
</component>
</project>

30
dist/index.css vendored
View File

@ -11,6 +11,36 @@
min-height: 100vh;
padding: 20px;
}
.editor-toolbar {
max-width: 1600px;
margin: 0 auto 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.editor-toggle-btn {
padding: 10px 20px;
background: #383838;
color: #e0e0e0;
border: 2px solid #555;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.editor-toggle-btn:hover {
background: #404040;
border-color: #00aaff;
}
.editor-toggle-btn.active {
background: #2d5a7a;
border-color: #00aaff;
color: #fff;
}
.editor-main {
display: flex;
gap: 20px;

2
dist/index.css.map vendored

File diff suppressed because one or more lines are too long

89
dist/index.js vendored
View File

@ -655,7 +655,8 @@ var EditorCanvas = ({
draggingPointIndex,
onStartDragging,
onStopDragging,
style: customStyle
style: customStyle,
showEditor = true
}) => {
const containerRef = (0, import_react4.useRef)(null);
const [canvasSize, setCanvasSize] = (0, import_react4.useState)({ width: 0, height: 0 });
@ -704,7 +705,7 @@ var EditorCanvas = ({
);
const handleCanvasMouseDown = (0, import_react4.useCallback)(
(e) => {
if (!selectedArea || !containerRef.current) return;
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
@ -715,11 +716,11 @@ var EditorCanvas = ({
e.preventDefault();
}
},
[selectedArea, isPointInPolygon]
[showEditor, selectedArea, isPointInPolygon]
);
const handleMouseMove = (0, import_react4.useCallback)(
(e) => {
if (!selectedArea || !containerRef.current) return;
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
@ -738,7 +739,7 @@ var EditorCanvas = ({
setDragStartPos({ x, y });
}
},
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
);
const handleMouseUp = (0, import_react4.useCallback)(() => {
if (draggingPointIndex !== null) {
@ -829,12 +830,19 @@ var EditorCanvas = ({
{
ref: containerRef,
className: "editor-canvas",
style: { width, height, position: "relative", cursor: getCursorStyle() },
onMouseDown: handleCanvasMouseDown,
onMouseMove: handleMouseMove,
style: {
width,
height,
position: "relative",
cursor: showEditor ? getCursorStyle() : "default",
pointerEvents: showEditor ? "auto" : "none"
// 에디터 숨김 시 포인터 이벤트 비활성화
},
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
onMouseMove: showEditor ? handleMouseMove : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ImageDistortion, { imageSrc, areas }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
showEditor && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"svg",
{
style: {
@ -863,7 +871,7 @@ var EditorCanvas = ({
})
}
),
selectedArea && canvasSize.width > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
showEditor && selectedArea && canvasSize.width > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"canvas",
{
style: {
@ -887,7 +895,7 @@ var EditorCanvas = ({
}
}
),
selectedArea && selectedArea.basePoints.map((point, index) => {
showEditor && selectedArea && selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {};
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"div",
@ -1149,6 +1157,7 @@ var DistortionEditor = ({
stopDragging,
getSelectedArea
} = useDistortionEditor(initialAreas);
const [showEditor, setShowEditor] = (0, import_react5.useState)(true);
(0, import_react5.useEffect)(() => {
onAreasChange?.(state.areas);
}, [state.areas, onAreasChange]);
@ -1182,37 +1191,49 @@ var DistortionEditor = ({
}
};
const selectedArea = getSelectedArea();
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "distortion-editor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-main", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-canvas-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
EditorCanvas,
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "distortion-editor", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-toolbar", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
"button",
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
imageSrc,
width,
height,
onUpdatePoint: updatePoint,
onUpdateArea: updateArea,
draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging,
onStopDragging: stopDragging,
style: canvasStyle
className: `editor-toggle-btn ${showEditor ? "active" : ""}`,
onClick: () => setShowEditor(!showEditor),
title: showEditor ? "\uC5D0\uB514\uD130 \uC228\uAE30\uAE30 (\uC65C\uACE1 \uD6A8\uACFC\uB9CC \uBCF4\uAE30)" : "\uC5D0\uB514\uD130 \uD45C\uC2DC",
children: showEditor ? "\u{1F441}\uFE0F \uC5D0\uB514\uD130 \uC228\uAE30\uAE30" : "\u270F\uFE0F \uC5D0\uB514\uD130 \uD45C\uC2DC"
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-sidebar", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
AreaList,
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-main", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "editor-canvas-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
EditorCanvas,
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
onSelectArea: selectArea,
onRemoveArea: removeArea,
onAddArea: handleAddArea
imageSrc,
width,
height,
onUpdatePoint: updatePoint,
onUpdateArea: updateArea,
draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging,
onStopDragging: stopDragging,
style: canvasStyle,
showEditor
}
),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
) }),
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "editor-sidebar", children: [
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
AreaList,
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
onSelectArea: selectArea,
onRemoveArea: removeArea,
onAddArea: handleAddArea
}
),
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
] })
] })
] }) });
] });
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

91
dist/index.mjs vendored
View File

@ -459,7 +459,7 @@ var ImageDistortion = ({
};
// src/editor/DistortionEditor.tsx
import { useEffect as useEffect4 } from "react";
import { useEffect as useEffect4, useState as useState4 } from "react";
// src/editor/hooks/useDistortionEditor.ts
import { useState as useState2, useCallback as useCallback2 } from "react";
@ -609,7 +609,8 @@ var EditorCanvas = ({
draggingPointIndex,
onStartDragging,
onStopDragging,
style: customStyle
style: customStyle,
showEditor = true
}) => {
const containerRef = useRef3(null);
const [canvasSize, setCanvasSize] = useState3({ width: 0, height: 0 });
@ -658,7 +659,7 @@ var EditorCanvas = ({
);
const handleCanvasMouseDown = useCallback3(
(e) => {
if (!selectedArea || !containerRef.current) return;
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
@ -669,11 +670,11 @@ var EditorCanvas = ({
e.preventDefault();
}
},
[selectedArea, isPointInPolygon]
[showEditor, selectedArea, isPointInPolygon]
);
const handleMouseMove = useCallback3(
(e) => {
if (!selectedArea || !containerRef.current) return;
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
@ -692,7 +693,7 @@ var EditorCanvas = ({
setDragStartPos({ x, y });
}
},
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
);
const handleMouseUp = useCallback3(() => {
if (draggingPointIndex !== null) {
@ -783,12 +784,19 @@ var EditorCanvas = ({
{
ref: containerRef,
className: "editor-canvas",
style: { width, height, position: "relative", cursor: getCursorStyle() },
onMouseDown: handleCanvasMouseDown,
onMouseMove: handleMouseMove,
style: {
width,
height,
position: "relative",
cursor: showEditor ? getCursorStyle() : "default",
pointerEvents: showEditor ? "auto" : "none"
// 에디터 숨김 시 포인터 이벤트 비활성화
},
onMouseDown: showEditor ? handleCanvasMouseDown : void 0,
onMouseMove: showEditor ? handleMouseMove : void 0,
children: [
/* @__PURE__ */ jsx2(ImageDistortion, { imageSrc, areas }),
/* @__PURE__ */ jsx2(
showEditor && /* @__PURE__ */ jsx2(
"svg",
{
style: {
@ -817,7 +825,7 @@ var EditorCanvas = ({
})
}
),
selectedArea && canvasSize.width > 0 && /* @__PURE__ */ jsx2(
showEditor && selectedArea && canvasSize.width > 0 && /* @__PURE__ */ jsx2(
"canvas",
{
style: {
@ -841,7 +849,7 @@ var EditorCanvas = ({
}
}
),
selectedArea && selectedArea.basePoints.map((point, index) => {
showEditor && selectedArea && selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {};
return /* @__PURE__ */ jsx2(
"div",
@ -1103,6 +1111,7 @@ var DistortionEditor = ({
stopDragging,
getSelectedArea
} = useDistortionEditor(initialAreas);
const [showEditor, setShowEditor] = useState4(true);
useEffect4(() => {
onAreasChange?.(state.areas);
}, [state.areas, onAreasChange]);
@ -1136,37 +1145,49 @@ var DistortionEditor = ({
}
};
const selectedArea = getSelectedArea();
return /* @__PURE__ */ jsx5("div", { className: "distortion-editor", children: /* @__PURE__ */ jsxs4("div", { className: "editor-main", children: [
/* @__PURE__ */ jsx5("div", { className: "editor-canvas-container", children: /* @__PURE__ */ jsx5(
EditorCanvas,
return /* @__PURE__ */ jsxs4("div", { className: "distortion-editor", children: [
/* @__PURE__ */ jsx5("div", { className: "editor-toolbar", children: /* @__PURE__ */ jsx5(
"button",
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
imageSrc,
width,
height,
onUpdatePoint: updatePoint,
onUpdateArea: updateArea,
draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging,
onStopDragging: stopDragging,
style: canvasStyle
className: `editor-toggle-btn ${showEditor ? "active" : ""}`,
onClick: () => setShowEditor(!showEditor),
title: showEditor ? "\uC5D0\uB514\uD130 \uC228\uAE30\uAE30 (\uC65C\uACE1 \uD6A8\uACFC\uB9CC \uBCF4\uAE30)" : "\uC5D0\uB514\uD130 \uD45C\uC2DC",
children: showEditor ? "\u{1F441}\uFE0F \uC5D0\uB514\uD130 \uC228\uAE30\uAE30" : "\u270F\uFE0F \uC5D0\uB514\uD130 \uD45C\uC2DC"
}
) }),
/* @__PURE__ */ jsxs4("div", { className: "editor-sidebar", children: [
/* @__PURE__ */ jsx5(
AreaList,
/* @__PURE__ */ jsxs4("div", { className: "editor-main", children: [
/* @__PURE__ */ jsx5("div", { className: "editor-canvas-container", children: /* @__PURE__ */ jsx5(
EditorCanvas,
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
onSelectArea: selectArea,
onRemoveArea: removeArea,
onAddArea: handleAddArea
imageSrc,
width,
height,
onUpdatePoint: updatePoint,
onUpdateArea: updateArea,
draggingPointIndex: state.draggingPointIndex,
onStartDragging: startDragging,
onStopDragging: stopDragging,
style: canvasStyle,
showEditor
}
),
/* @__PURE__ */ jsx5(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
) }),
/* @__PURE__ */ jsxs4("div", { className: "editor-sidebar", children: [
/* @__PURE__ */ jsx5(
AreaList,
{
areas: state.areas,
selectedAreaId: state.selectedAreaId,
onSelectArea: selectArea,
onRemoveArea: removeArea,
onAddArea: handleAddArea
}
),
/* @__PURE__ */ jsx5(ParameterPanel, { area: selectedArea, onUpdateArea: handleUpdateArea })
] })
] })
] }) });
] });
};
export {
ANIMATION_CONFIG,

2
dist/index.mjs.map vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { DistortionArea } from '../types/area';
import { DistortionEditorProps } from './types';
import { useDistortionEditor } from './hooks/useDistortionEditor';
@ -28,6 +28,9 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
getSelectedArea,
} = useDistortionEditor(initialAreas);
// 에디터 모드 토글 상태
const [showEditor, setShowEditor] = useState(true);
// 영역 변경 시 콜백 호출
useEffect(() => {
onAreasChange?.(state.areas);
@ -72,6 +75,17 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
return (
<div className="distortion-editor">
{/* 에디터 모드 토글 버튼 */}
<div className="editor-toolbar">
<button
className={`editor-toggle-btn ${showEditor ? 'active' : ''}`}
onClick={() => setShowEditor(!showEditor)}
title={showEditor ? '에디터 숨기기 (왜곡 효과만 보기)' : '에디터 표시'}
>
{showEditor ? '👁️ 에디터 숨기기' : '✏️ 에디터 표시'}
</button>
</div>
<div className="editor-main">
{/* 왼쪽: 캔버스 */}
<div className="editor-canvas-container">
@ -87,6 +101,7 @@ export const DistortionEditor: React.FC<DistortionEditorProps> = ({
onStartDragging={startDragging}
onStopDragging={stopDragging}
style={canvasStyle}
showEditor={showEditor}
/>
</div>

View File

@ -17,6 +17,8 @@ interface EditorCanvasProps {
onStopDragging: () => void;
/** 에디터 캔버스 스타일 커스터마이징 */
style?: EditorCanvasStyle;
/** 에디터 UI 표시 여부 (기본값: true) */
showEditor?: boolean;
}
export const EditorCanvas: React.FC<EditorCanvasProps> = ({
@ -31,6 +33,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
onStartDragging,
onStopDragging,
style: customStyle,
showEditor = true,
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const [canvasSize, setCanvasSize] = useState({width: 0, height: 0});
@ -93,7 +96,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
// 캔버스 클릭 (사각형 내부 클릭 감지)
const handleCanvasMouseDown = useCallback(
(e: React.MouseEvent) => {
if (!selectedArea || !containerRef.current) return;
// 에디터가 숨겨진 상태면 동작하지 않음
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
@ -107,12 +111,13 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
e.preventDefault();
}
},
[selectedArea, isPointInPolygon]
[showEditor, selectedArea, isPointInPolygon]
);
const handleMouseMove = useCallback(
(e: React.MouseEvent) => {
if (!selectedArea || !containerRef.current) return;
// 에디터가 숨겨진 상태면 동작하지 않음
if (!showEditor || !selectedArea || !containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
@ -139,7 +144,7 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
setDragStartPos({ x, y }); // 다음 프레임을 위해 시작 위치 업데이트
}
},
[draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
[showEditor, draggingPointIndex, isDraggingArea, dragStartPos, selectedArea, onUpdatePoint, onUpdateArea]
);
const handleMouseUp = useCallback(() => {
@ -270,26 +275,33 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
<div
ref={containerRef}
className="editor-canvas"
style={{width, height, position: 'relative', cursor: getCursorStyle()}}
onMouseDown={handleCanvasMouseDown}
onMouseMove={handleMouseMove}
style={{
width,
height,
position: 'relative',
cursor: showEditor ? getCursorStyle() : 'default',
pointerEvents: showEditor ? 'auto' : 'none', // 에디터 숨김 시 포인터 이벤트 비활성화
}}
onMouseDown={showEditor ? handleCanvasMouseDown : undefined}
onMouseMove={showEditor ? handleMouseMove : undefined}
>
{/* ImageDistortion 컴포넌트 */}
<ImageDistortion imageSrc={imageSrc} areas={areas}/>
{/* 오버레이 SVG */}
<svg
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'none',
}}
>
{/* 모든 영역의 사각형 표시 */}
{areas.map((area) => {
{/* 오버레이 SVG - 에디터 모드일 때만 표시 */}
{showEditor && (
<svg
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'none',
}}
>
{/* 모든 영역의 사각형 표시 */}
{areas.map((area) => {
const isSelected = area.id === selectedAreaId;
const points = area.basePoints;
const outlineStyle = editorStyle.areaOutline || {};
@ -308,11 +320,12 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
/>
</g>
);
})}
</svg>
})}
</svg>
)}
{/* 선택된 영역의 타원 가이드 (Canvas로 그리기) */}
{selectedArea && canvasSize.width > 0 && (
{/* 선택된 영역의 타원 가이드 (Canvas로 그리기) - 에디터 모드일 때만 표시 */}
{showEditor && selectedArea && canvasSize.width > 0 && (
<canvas
style={{
position: 'absolute',
@ -336,8 +349,8 @@ export const EditorCanvas: React.FC<EditorCanvasProps> = ({
/>
)}
{/* 선택된 영역의 포인트 핸들 */}
{selectedArea &&
{/* 선택된 영역의 포인트 핸들 - 에디터 모드일 때만 표시 */}
{showEditor && selectedArea &&
selectedArea.basePoints.map((point, index) => {
const handleStyle = editorStyle.pointHandle || {};
return (

59
src/editor/constants.ts Normal file
View File

@ -0,0 +1,59 @@
import { EditorCanvasStyle } from './types';
/**
*
*/
export const DEFAULT_EDITOR_CANVAS_STYLE: EditorCanvasStyle = {
// 3단계 원 스타일 (외부 -> 내부)
circleLevels: [
{
radius: 0.5,
opacity: 0.3,
lineWidth: 2,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
{
radius: 0.33,
opacity: 0.6,
lineWidth: 2.5,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
{
radius: 0.167,
opacity: 0.9,
lineWidth: 3,
color: 'rgba(255, 200, 0, 1)',
dashPattern: [8, 4],
},
],
// 원 내부 채우기
circleFillColor: 'rgba(255, 200, 0, 0.08)',
// 중심점
centerPoint: {
radius: 5,
fillColor: 'rgba(255, 200, 0, 1)',
strokeColor: 'rgba(255, 255, 255, 0.8)',
strokeWidth: 2,
},
// 포인트 핸들
pointHandle: {
size: 16,
fillColor: '#00aaff',
strokeColor: 'white',
strokeWidth: 2,
labelColor: '#00aaff',
labelFontSize: 11,
},
// 영역 외곽선
areaOutline: {
selectedColor: '#00aaff',
unselectedColor: '#888',
selectedWidth: 2,
unselectedWidth: 1,
unselectedDashPattern: [5, 5],
selectedFillColor: 'rgba(0, 170, 255, 0.08)', // 선택된 영역 배경 (연한 파란색)
unselectedFillColor: 'rgba(136, 136, 136, 0.03)', // 선택 안된 영역 배경 (연한 회색)
},
};

View File

@ -7,6 +7,41 @@
padding: 20px;
}
/* 에디터 툴바 */
.editor-toolbar {
max-width: 1600px;
margin: 0 auto 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.editor-toggle-btn {
padding: 10px 20px;
background: #383838;
color: #e0e0e0;
border: 2px solid #555;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.editor-toggle-btn:hover {
background: #404040;
border-color: #00aaff;
}
.editor-toggle-btn.active {
background: #2d5a7a;
border-color: #00aaff;
color: #fff;
}
.editor-main {
display: flex;
gap: 20px;