@promakeai/inspector 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/__tests__/App.test.d.ts.map +1 -0
- package/dist/components/Badge.d.ts.map +1 -0
- package/dist/components/ControlBox/ContentArea.d.ts.map +1 -0
- package/dist/components/ControlBox/PromptInput.d.ts.map +1 -0
- package/dist/components/ControlBox/index.d.ts.map +1 -0
- package/dist/components/ImageEditor/UploadBox.d.ts.map +1 -0
- package/dist/components/ImageEditor/index.d.ts.map +1 -0
- package/dist/components/Overlay.d.ts.map +1 -0
- package/dist/components/StyleEditor/BorderSection.d.ts.map +1 -0
- package/dist/components/StyleEditor/ColorPicker.d.ts.map +1 -0
- package/dist/components/StyleEditor/DisplaySection.d.ts.map +1 -0
- package/dist/components/StyleEditor/ImageSection.d.ts.map +1 -0
- package/dist/components/StyleEditor/LayoutSection.d.ts.map +1 -0
- package/dist/components/StyleEditor/NumberInput.d.ts.map +1 -0
- package/dist/components/StyleEditor/SliderInput.d.ts.map +1 -0
- package/dist/components/StyleEditor/SpacingSection.d.ts.map +1 -0
- package/dist/components/StyleEditor/TextSection.d.ts.map +1 -0
- package/dist/components/StyleEditor/index.d.ts.map +1 -0
- package/dist/components/TextEditor/index.d.ts.map +1 -0
- package/dist/components/ui/CustomCollapsible.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/color-picker.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/core/highlighter.d.ts.map +1 -0
- package/dist/hooks/useMessageBridge.d.ts.map +1 -0
- package/dist/hooks/useStylePreview.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/store/useInspectorStore.d.ts.map +1 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/utils/colorUtils.d.ts.map +1 -0
- package/dist/utils/elementNames.d.ts.map +1 -0
- package/dist/utils/elementUtils.d.ts.map +1 -0
- package/dist/utils/errorTracker.d.ts.map +1 -0
- package/dist/utils/inputStyles.d.ts.map +1 -0
- package/dist/utils/styleUtils.d.ts.map +1 -0
- package/dist/utils/tailwindMapper.d.ts.map +1 -0
- package/dist/utils/urlTracker.d.ts.map +1 -0
- package/package.json +15 -10
- package/dist/packages/inspector/src/App.d.ts.map +0 -1
- package/dist/packages/inspector/src/__tests__/App.test.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/Badge.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ControlBox/ContentArea.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ControlBox/PromptInput.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ControlBox/index.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ImageEditor/UploadBox.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ImageEditor/index.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/Overlay.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/BorderSection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/ColorPicker.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/DisplaySection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/ImageSection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/LayoutSection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/NumberInput.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/SliderInput.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/SpacingSection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/TextSection.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/StyleEditor/index.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/TextEditor/index.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/CustomCollapsible.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/button.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/color-picker.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/input.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/popover.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/select.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/slider.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/textarea.d.ts.map +0 -1
- package/dist/packages/inspector/src/components/ui/tooltip.d.ts.map +0 -1
- package/dist/packages/inspector/src/core/highlighter.d.ts.map +0 -1
- package/dist/packages/inspector/src/hooks/useMessageBridge.d.ts.map +0 -1
- package/dist/packages/inspector/src/hooks/useStylePreview.d.ts.map +0 -1
- package/dist/packages/inspector/src/index.d.ts.map +0 -1
- package/dist/packages/inspector/src/lib/utils.d.ts.map +0 -1
- package/dist/packages/inspector/src/plugin.d.ts.map +0 -1
- package/dist/packages/inspector/src/store/useInspectorStore.d.ts.map +0 -1
- package/dist/packages/inspector/src/styles.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/colorUtils.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/elementNames.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/elementUtils.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/errorTracker.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/inputStyles.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/styleUtils.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/tailwindMapper.d.ts.map +0 -1
- package/dist/packages/inspector/src/utils/urlTracker.d.ts.map +0 -1
- package/dist/packages/inspector/tsconfig.tsbuildinfo +0 -1
- package/src/App.tsx +0 -912
- package/src/__tests__/App.test.tsx +0 -373
- package/src/assets/fonts/Satoshi-Variable.woff +0 -0
- package/src/assets/fonts/Satoshi-Variable.woff2 +0 -0
- package/src/components/Badge.tsx +0 -118
- package/src/components/ControlBox/ContentArea.tsx +0 -13
- package/src/components/ControlBox/PromptInput.module.css +0 -66
- package/src/components/ControlBox/PromptInput.tsx +0 -104
- package/src/components/ControlBox/index.module.css +0 -81
- package/src/components/ControlBox/index.tsx +0 -409
- package/src/components/ImageEditor/UploadBox.module.css +0 -69
- package/src/components/ImageEditor/UploadBox.tsx +0 -113
- package/src/components/ImageEditor/index.module.css +0 -11
- package/src/components/ImageEditor/index.tsx +0 -84
- package/src/components/Overlay.tsx +0 -157
- package/src/components/StyleEditor/BorderSection.tsx +0 -147
- package/src/components/StyleEditor/ColorPicker.tsx +0 -182
- package/src/components/StyleEditor/DisplaySection.tsx +0 -349
- package/src/components/StyleEditor/ImageSection.tsx +0 -105
- package/src/components/StyleEditor/LayoutSection.tsx +0 -63
- package/src/components/StyleEditor/NumberInput.tsx +0 -138
- package/src/components/StyleEditor/SliderInput.tsx +0 -121
- package/src/components/StyleEditor/SpacingSection.tsx +0 -365
- package/src/components/StyleEditor/TextSection.tsx +0 -381
- package/src/components/StyleEditor/index.module.css +0 -133
- package/src/components/StyleEditor/index.tsx +0 -612
- package/src/components/StyleEditor/shared.module.css +0 -193
- package/src/components/TextEditor/index.module.css +0 -31
- package/src/components/TextEditor/index.tsx +0 -166
- package/src/components/ui/CustomCollapsible.tsx +0 -159
- package/src/components/ui/button.module.css +0 -141
- package/src/components/ui/button.tsx +0 -73
- package/src/components/ui/color-picker.module.css +0 -112
- package/src/components/ui/color-picker.tsx +0 -146
- package/src/components/ui/input.module.css +0 -49
- package/src/components/ui/input.tsx +0 -34
- package/src/components/ui/popover.module.css +0 -42
- package/src/components/ui/popover.tsx +0 -59
- package/src/components/ui/select.module.css +0 -160
- package/src/components/ui/select.tsx +0 -216
- package/src/components/ui/slider.module.css +0 -75
- package/src/components/ui/slider.tsx +0 -60
- package/src/components/ui/textarea.module.css +0 -30
- package/src/components/ui/textarea.tsx +0 -23
- package/src/components/ui/tooltip.module.css +0 -11
- package/src/components/ui/tooltip.tsx +0 -37
- package/src/core/highlighter.ts +0 -197
- package/src/hooks/useMessageBridge.ts +0 -49
- package/src/hooks/useStylePreview.ts +0 -332
- package/src/index.ts +0 -20
- package/src/lib/utils.ts +0 -5
- package/src/plugin.ts +0 -11
- package/src/store/useInspectorStore.ts +0 -235
- package/src/styles/fonts.css +0 -15
- package/src/styles/global.css +0 -138
- package/src/styles/variables.css +0 -151
- package/src/styles.ts +0 -5
- package/src/utils/colorUtils.ts +0 -133
- package/src/utils/elementNames.ts +0 -103
- package/src/utils/elementUtils.ts +0 -90
- package/src/utils/errorTracker.ts +0 -186
- package/src/utils/inputStyles.ts +0 -30
- package/src/utils/styleUtils.ts +0 -226
- package/src/utils/tailwindMapper.ts +0 -554
- package/src/utils/urlTracker.ts +0 -75
- package/src/vite-env.d.ts +0 -7
- /package/dist/{packages/inspector/src/App.d.ts → App.d.ts} +0 -0
- /package/dist/{packages/inspector/src/__tests__ → __tests__}/App.test.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/Badge.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ControlBox/ContentArea.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ControlBox/PromptInput.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ControlBox/index.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ImageEditor/UploadBox.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ImageEditor/index.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/Overlay.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/BorderSection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/ColorPicker.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/DisplaySection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/ImageSection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/LayoutSection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/NumberInput.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/SliderInput.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/SpacingSection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/TextSection.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/StyleEditor/index.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/TextEditor/index.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/CustomCollapsible.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/button.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/color-picker.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/input.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/popover.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/select.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/slider.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/textarea.d.ts +0 -0
- /package/dist/{packages/inspector/src/components → components}/ui/tooltip.d.ts +0 -0
- /package/dist/{packages/inspector/src/core → core}/highlighter.d.ts +0 -0
- /package/dist/{packages/inspector/src/hooks → hooks}/useMessageBridge.d.ts +0 -0
- /package/dist/{packages/inspector/src/hooks → hooks}/useStylePreview.d.ts +0 -0
- /package/dist/{packages/inspector/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{packages/inspector/src/lib → lib}/utils.d.ts +0 -0
- /package/dist/{packages/inspector/src/plugin.d.ts → plugin.d.ts} +0 -0
- /package/dist/{packages/inspector/src/store → store}/useInspectorStore.d.ts +0 -0
- /package/dist/{packages/inspector/src/styles.d.ts → styles.d.ts} +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/colorUtils.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/elementNames.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/elementUtils.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/errorTracker.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/inputStyles.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/styleUtils.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/tailwindMapper.d.ts +0 -0
- /package/dist/{packages/inspector/src/utils → utils}/urlTracker.d.ts +0 -0
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
.controlBox {
|
|
2
|
-
position: fixed;
|
|
3
|
-
display: flex;
|
|
4
|
-
flex-direction: column;
|
|
5
|
-
padding: var(--spacing-3);
|
|
6
|
-
gap: var(--spacing-2);
|
|
7
|
-
border-radius: var(--radius-lg);
|
|
8
|
-
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
|
|
9
|
-
width: 100%;
|
|
10
|
-
max-width: 480px;
|
|
11
|
-
min-width: 320px;
|
|
12
|
-
box-sizing: border-box;
|
|
13
|
-
overflow: hidden;
|
|
14
|
-
backdrop-filter: blur(8px);
|
|
15
|
-
z-index: var(--z-inspector-controlbox);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.tabsContainer {
|
|
19
|
-
flex: 1;
|
|
20
|
-
display: flex;
|
|
21
|
-
flex-direction: column;
|
|
22
|
-
min-height: 0;
|
|
23
|
-
/* width: 100%; */
|
|
24
|
-
gap: var(--spacing-2);
|
|
25
|
-
overflow: hidden;
|
|
26
|
-
box-sizing: border-box;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.tabsList {
|
|
30
|
-
display: flex;
|
|
31
|
-
align-items: center;
|
|
32
|
-
height: var(--spacing-9);
|
|
33
|
-
/* width: 100%; */
|
|
34
|
-
border-radius: var(--radius-md);
|
|
35
|
-
flex-shrink: 0;
|
|
36
|
-
box-sizing: border-box;
|
|
37
|
-
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.tabTrigger {
|
|
41
|
-
display: inline-flex;
|
|
42
|
-
align-items: center;
|
|
43
|
-
justify-content: center;
|
|
44
|
-
gap: var(--spacing-1-5);
|
|
45
|
-
height: calc(100% - 2 * var(--spacing-1));
|
|
46
|
-
margin: var(--spacing-1);
|
|
47
|
-
border-radius: var(--radius-md);
|
|
48
|
-
border: 1px solid transparent;
|
|
49
|
-
padding: var(--spacing-1) var(--spacing-3);
|
|
50
|
-
font-family: 'Satoshi', var(--font-family-sans);
|
|
51
|
-
font-size: var(--text-sm);
|
|
52
|
-
font-weight: var(--font-medium);
|
|
53
|
-
white-space: nowrap;
|
|
54
|
-
cursor: pointer;
|
|
55
|
-
transition: all var(--transition-fast);
|
|
56
|
-
outline: none;
|
|
57
|
-
background: none;
|
|
58
|
-
box-sizing: border-box;
|
|
59
|
-
box-shadow: none;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.tabTrigger:hover {
|
|
63
|
-
opacity: 0.85;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/* Active tab */
|
|
67
|
-
.tabTrigger[data-active="true"] {
|
|
68
|
-
box-shadow: none;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.tabContent {
|
|
72
|
-
flex: 1;
|
|
73
|
-
display: flex;
|
|
74
|
-
flex-direction: column;
|
|
75
|
-
min-height: 0;
|
|
76
|
-
width: 100%;
|
|
77
|
-
max-width: 100%;
|
|
78
|
-
overflow-y: auto;
|
|
79
|
-
overflow-x: hidden;
|
|
80
|
-
box-sizing: border-box;
|
|
81
|
-
}
|
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ControlBox - Main control interface for inspector
|
|
3
|
-
* Layout: TOP (tabs) | CONTENT (editors) | BOTTOM (prompt input)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useRef, useState } from "react";
|
|
7
|
-
import { PromptInput } from "./PromptInput";
|
|
8
|
-
import { TextEditor } from "../TextEditor";
|
|
9
|
-
import { ImageEditor } from "../ImageEditor";
|
|
10
|
-
import { StyleEditor } from "../StyleEditor";
|
|
11
|
-
import { useInspectorStore } from "../../store/useInspectorStore";
|
|
12
|
-
import type { SelectedElementData } from "@promakeai/inspector-types";
|
|
13
|
-
import { getElementLabel } from "../../utils/elementNames";
|
|
14
|
-
import clsx from "clsx";
|
|
15
|
-
import styles from "./index.module.css";
|
|
16
|
-
|
|
17
|
-
interface ControlBoxProps {
|
|
18
|
-
className?: string;
|
|
19
|
-
element: HTMLElement;
|
|
20
|
-
elementData: SelectedElementData;
|
|
21
|
-
activeTab: "text" | "image" | "style" | null;
|
|
22
|
-
availableTabs: ("text" | "image" | "style")[];
|
|
23
|
-
onTabChange: (tab: "text" | "image" | "style" | null) => void;
|
|
24
|
-
onClose: () => void;
|
|
25
|
-
onPromptSubmit: (prompt: string) => void;
|
|
26
|
-
elementStack?: HTMLElement[];
|
|
27
|
-
onElementSelect?: (element: HTMLElement) => void;
|
|
28
|
-
labels?: Record<string, string>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Custom Tab Components
|
|
32
|
-
const TabButton = ({
|
|
33
|
-
active,
|
|
34
|
-
onClick,
|
|
35
|
-
children,
|
|
36
|
-
theme,
|
|
37
|
-
}: {
|
|
38
|
-
active: boolean;
|
|
39
|
-
onClick: () => void;
|
|
40
|
-
children: React.ReactNode;
|
|
41
|
-
theme: any;
|
|
42
|
-
}) => {
|
|
43
|
-
return (
|
|
44
|
-
<button
|
|
45
|
-
onClick={onClick}
|
|
46
|
-
className={styles.tabTrigger}
|
|
47
|
-
data-active={active}
|
|
48
|
-
style={{
|
|
49
|
-
backgroundColor: active ? theme.tabActiveBg : theme.tabInactiveBg,
|
|
50
|
-
color: active ? theme.tabActiveColor : theme.tabInactiveColor,
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
{children}
|
|
54
|
-
</button>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export function ControlBox({
|
|
59
|
-
className,
|
|
60
|
-
element,
|
|
61
|
-
elementData,
|
|
62
|
-
activeTab,
|
|
63
|
-
availableTabs,
|
|
64
|
-
onTabChange,
|
|
65
|
-
onClose,
|
|
66
|
-
onPromptSubmit,
|
|
67
|
-
elementStack = [],
|
|
68
|
-
onElementSelect,
|
|
69
|
-
labels: customLabels,
|
|
70
|
-
}: ControlBoxProps) {
|
|
71
|
-
const { theme, labels } = useInspectorStore();
|
|
72
|
-
const boxRef = useRef<HTMLDivElement>(null);
|
|
73
|
-
const [position, setPosition] = useState<{
|
|
74
|
-
top?: number;
|
|
75
|
-
bottom?: number;
|
|
76
|
-
left: number;
|
|
77
|
-
}>({ left: 0 });
|
|
78
|
-
const [maxHeight, setMaxHeight] = useState<number>(0);
|
|
79
|
-
const [anchorPosition, setAnchorPosition] = useState<
|
|
80
|
-
"above" | "below" | "inside"
|
|
81
|
-
>("below");
|
|
82
|
-
const [isPositioned, setIsPositioned] = useState(false);
|
|
83
|
-
|
|
84
|
-
const TAB_LABELS = {
|
|
85
|
-
text: labels.textTabLabel || "Content",
|
|
86
|
-
image: labels.imageTabLabel || "Image",
|
|
87
|
-
style: labels.styleTabLabel || "Design",
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// Reset positioned state and anchor when element changes
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
setIsPositioned(false);
|
|
93
|
-
// Don't reset anchor here - let calculatePosition do it
|
|
94
|
-
}, [element]);
|
|
95
|
-
|
|
96
|
-
// Calculate position based on element
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (!boxRef.current || !element) return;
|
|
99
|
-
|
|
100
|
-
let initialAnchor: "above" | "below" | "inside" | null = null;
|
|
101
|
-
|
|
102
|
-
const calculatePosition = (isInitial = false) => {
|
|
103
|
-
const rect = element.getBoundingClientRect();
|
|
104
|
-
const viewportWidth = window.innerWidth;
|
|
105
|
-
const viewportHeight = window.innerHeight;
|
|
106
|
-
const padding = 10;
|
|
107
|
-
const gap = 10;
|
|
108
|
-
const insidePadding = 16;
|
|
109
|
-
const minRequiredHeight = 250;
|
|
110
|
-
const largeElementThreshold = 0.7;
|
|
111
|
-
|
|
112
|
-
// Calculate box dimensions
|
|
113
|
-
const maxWidth = Math.min(600, viewportWidth - padding * 2);
|
|
114
|
-
const minWidth = Math.min(320, maxWidth);
|
|
115
|
-
const boxWidth = Math.max(minWidth, Math.min(rect.width, maxWidth));
|
|
116
|
-
|
|
117
|
-
// Horizontal position (always calculated)
|
|
118
|
-
let centerLeft = rect.left + rect.width / 2 - boxWidth / 2;
|
|
119
|
-
centerLeft = Math.max(
|
|
120
|
-
padding,
|
|
121
|
-
Math.min(centerLeft, viewportWidth - boxWidth - padding)
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Determine anchor position only on initial calculation
|
|
125
|
-
if (isInitial || initialAnchor === null) {
|
|
126
|
-
// Calculate actual visible space
|
|
127
|
-
const spaceBelow = Math.max(0, viewportHeight - rect.bottom);
|
|
128
|
-
const spaceAbove = Math.max(0, rect.top);
|
|
129
|
-
|
|
130
|
-
// Check element size ratios
|
|
131
|
-
const elementHeightRatio = rect.height / viewportHeight;
|
|
132
|
-
const elementWidthRatio = rect.width / viewportWidth;
|
|
133
|
-
|
|
134
|
-
// Element is large in both dimensions (e.g., full page, large section)
|
|
135
|
-
const isLargeInBothDimensions =
|
|
136
|
-
elementHeightRatio > largeElementThreshold &&
|
|
137
|
-
elementWidthRatio > largeElementThreshold;
|
|
138
|
-
|
|
139
|
-
// Element is wide but short (e.g., navbar, header) - should use outside positioning
|
|
140
|
-
const isWideButShort =
|
|
141
|
-
elementWidthRatio > largeElementThreshold &&
|
|
142
|
-
elementHeightRatio <= largeElementThreshold;
|
|
143
|
-
|
|
144
|
-
// Check if element extends beyond viewport (partially visible)
|
|
145
|
-
const isPartiallyVisible = rect.top < 0 || rect.bottom > viewportHeight;
|
|
146
|
-
|
|
147
|
-
// Check if there's insufficient space outside
|
|
148
|
-
const hasInsufficientSpace =
|
|
149
|
-
spaceAbove < minRequiredHeight && spaceBelow < minRequiredHeight;
|
|
150
|
-
|
|
151
|
-
// Check if there's sufficient space outside (prefer outside for wide but short elements)
|
|
152
|
-
const hasSufficientSpaceOutside =
|
|
153
|
-
spaceAbove >= minRequiredHeight || spaceBelow >= minRequiredHeight;
|
|
154
|
-
|
|
155
|
-
// Decision logic:
|
|
156
|
-
// 1. Wide but short elements with space outside -> use outside positioning
|
|
157
|
-
// 2. Large in both dimensions -> use inside positioning
|
|
158
|
-
// 3. Tall elements OR partially visible large elements -> use inside if insufficient space
|
|
159
|
-
if (isWideButShort && hasSufficientSpaceOutside) {
|
|
160
|
-
// Wide but short (navbar/header): use outside positioning
|
|
161
|
-
initialAnchor = spaceAbove > spaceBelow ? "above" : "below";
|
|
162
|
-
} else if (
|
|
163
|
-
isLargeInBothDimensions ||
|
|
164
|
-
hasInsufficientSpace ||
|
|
165
|
-
(isPartiallyVisible && elementHeightRatio > 0.5)
|
|
166
|
-
) {
|
|
167
|
-
// Large element or insufficient space: use inside positioning
|
|
168
|
-
initialAnchor = "inside";
|
|
169
|
-
} else {
|
|
170
|
-
// Normal elements: choose based on available space
|
|
171
|
-
initialAnchor = spaceAbove > spaceBelow ? "above" : "below";
|
|
172
|
-
}
|
|
173
|
-
setAnchorPosition(initialAnchor);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Calculate dynamic max-height based on anchor
|
|
177
|
-
let calculatedMaxHeight: number;
|
|
178
|
-
let newPosition: { top?: number; bottom?: number; left: number };
|
|
179
|
-
|
|
180
|
-
if (initialAnchor === "inside") {
|
|
181
|
-
// Position inside the element, but respect viewport boundaries
|
|
182
|
-
// If element is scrolled partially off screen, start from viewport edge
|
|
183
|
-
const topPos = Math.max(rect.top + insidePadding, insidePadding);
|
|
184
|
-
|
|
185
|
-
// Calculate available height: from topPos to element bottom or viewport bottom
|
|
186
|
-
const availableBottom = Math.min(
|
|
187
|
-
rect.bottom - insidePadding,
|
|
188
|
-
viewportHeight - insidePadding
|
|
189
|
-
);
|
|
190
|
-
calculatedMaxHeight = availableBottom - topPos;
|
|
191
|
-
|
|
192
|
-
// Horizontal positioning
|
|
193
|
-
let insideLeft = rect.left + insidePadding;
|
|
194
|
-
|
|
195
|
-
// If element is scrolled partially off screen horizontally, start from viewport edge
|
|
196
|
-
if (rect.left < 0) {
|
|
197
|
-
insideLeft = insidePadding;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Ensure the box fits within element width and viewport
|
|
201
|
-
const elementRightEdge = Math.min(rect.right, viewportWidth);
|
|
202
|
-
const maxBoxWidth = elementRightEdge - insideLeft - insidePadding;
|
|
203
|
-
const actualBoxWidth = Math.min(boxWidth, maxBoxWidth);
|
|
204
|
-
|
|
205
|
-
// Center horizontally within element if there's room
|
|
206
|
-
const elementVisibleWidth =
|
|
207
|
-
Math.min(rect.right, viewportWidth) - Math.max(rect.left, 0);
|
|
208
|
-
if (actualBoxWidth < elementVisibleWidth - insidePadding * 2) {
|
|
209
|
-
const elementVisibleLeft = Math.max(rect.left, 0);
|
|
210
|
-
insideLeft =
|
|
211
|
-
elementVisibleLeft + (elementVisibleWidth - actualBoxWidth) / 2;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
newPosition = { top: topPos, left: insideLeft };
|
|
215
|
-
|
|
216
|
-
// Update box width for inside positioning
|
|
217
|
-
if (boxRef.current && actualBoxWidth !== boxWidth) {
|
|
218
|
-
boxRef.current.style.width = `${actualBoxWidth}px`;
|
|
219
|
-
}
|
|
220
|
-
} else if (initialAnchor === "above") {
|
|
221
|
-
// Anchored above: grow upward from element
|
|
222
|
-
calculatedMaxHeight = rect.top - padding - gap;
|
|
223
|
-
const bottomPos = viewportHeight - rect.top + gap;
|
|
224
|
-
newPosition = { bottom: bottomPos, left: centerLeft };
|
|
225
|
-
} else {
|
|
226
|
-
// Anchored below: grow downward from element
|
|
227
|
-
calculatedMaxHeight = viewportHeight - rect.bottom - padding - gap;
|
|
228
|
-
const topPos = rect.bottom + gap;
|
|
229
|
-
newPosition = { top: topPos, left: centerLeft };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Ensure minimum height
|
|
233
|
-
calculatedMaxHeight = Math.max(200, calculatedMaxHeight);
|
|
234
|
-
|
|
235
|
-
setMaxHeight(calculatedMaxHeight);
|
|
236
|
-
setPosition(newPosition);
|
|
237
|
-
|
|
238
|
-
// Update box width (for non-inside positioning)
|
|
239
|
-
if (boxRef.current && initialAnchor !== "inside") {
|
|
240
|
-
boxRef.current.style.width = `${boxWidth}px`;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Mark as positioned after first calculation
|
|
244
|
-
if (isInitial) {
|
|
245
|
-
setIsPositioned(true);
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// Initial calculation
|
|
250
|
-
calculatePosition(true);
|
|
251
|
-
|
|
252
|
-
// Update position on scroll/resize (but keep anchor)
|
|
253
|
-
const handleUpdate = () => calculatePosition(false);
|
|
254
|
-
window.addEventListener("scroll", handleUpdate, true);
|
|
255
|
-
window.addEventListener("resize", handleUpdate);
|
|
256
|
-
|
|
257
|
-
// Watch for content height changes (e.g., tab switches)
|
|
258
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
259
|
-
// Only update if already positioned (avoid flash on initial render)
|
|
260
|
-
if (initialAnchor !== null) {
|
|
261
|
-
calculatePosition(false);
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
resizeObserver.observe(boxRef.current);
|
|
265
|
-
|
|
266
|
-
return () => {
|
|
267
|
-
window.removeEventListener("scroll", handleUpdate, true);
|
|
268
|
-
window.removeEventListener("resize", handleUpdate);
|
|
269
|
-
resizeObserver.disconnect();
|
|
270
|
-
initialAnchor = null;
|
|
271
|
-
};
|
|
272
|
-
}, [element]);
|
|
273
|
-
|
|
274
|
-
return (
|
|
275
|
-
<div
|
|
276
|
-
ref={boxRef}
|
|
277
|
-
className={clsx(styles.controlBox, className)}
|
|
278
|
-
style={{
|
|
279
|
-
top: position.top !== undefined ? `${position.top}px` : undefined,
|
|
280
|
-
bottom:
|
|
281
|
-
position.bottom !== undefined ? `${position.bottom}px` : undefined,
|
|
282
|
-
left: `${position.left}px`,
|
|
283
|
-
maxHeight: maxHeight > 0 ? `${maxHeight}px` : undefined,
|
|
284
|
-
backgroundColor: theme.backgroundColor,
|
|
285
|
-
opacity: isPositioned ? 1 : 0,
|
|
286
|
-
transform: isPositioned ? "scale(1)" : "scale(0.95)",
|
|
287
|
-
transition: "opacity 0.2s ease, transform 0.2s ease",
|
|
288
|
-
}}
|
|
289
|
-
>
|
|
290
|
-
{/* Element name tags - at top of control box */}
|
|
291
|
-
{(() => {
|
|
292
|
-
const shouldShow = elementStack.length > 1;
|
|
293
|
-
console.log("🔘 Element Tags Debug:", {
|
|
294
|
-
shouldShow,
|
|
295
|
-
stackLength: elementStack.length,
|
|
296
|
-
stack: elementStack.map((el) => ({
|
|
297
|
-
tag: el.tagName,
|
|
298
|
-
id: el.id,
|
|
299
|
-
className: el.className,
|
|
300
|
-
devId: el.getAttribute("data-dev-id"),
|
|
301
|
-
})),
|
|
302
|
-
isPositioned,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
return (
|
|
306
|
-
shouldShow && (
|
|
307
|
-
<div
|
|
308
|
-
className="inspector-element-tags"
|
|
309
|
-
data-inspector-ignore
|
|
310
|
-
style={{
|
|
311
|
-
display: "flex",
|
|
312
|
-
gap: "4px",
|
|
313
|
-
overflowX: "auto",
|
|
314
|
-
overflowY: "hidden",
|
|
315
|
-
width: "calc(100% + 24px)",
|
|
316
|
-
marginLeft: "-12px",
|
|
317
|
-
marginRight: "-12px",
|
|
318
|
-
marginTop: "-12px",
|
|
319
|
-
paddingLeft: "12px",
|
|
320
|
-
paddingRight: "12px",
|
|
321
|
-
paddingTop: "12px",
|
|
322
|
-
paddingBottom: "10px",
|
|
323
|
-
borderBottom: "1px solid rgba(0, 0, 0, 0.05)",
|
|
324
|
-
}}
|
|
325
|
-
>
|
|
326
|
-
{elementStack.map((el, index) => {
|
|
327
|
-
const labelText = getElementLabel(el, customLabels || labels);
|
|
328
|
-
const isSelected = el === element; // Check if this element is the currently selected one
|
|
329
|
-
console.log(`🏷️ Button ${index}:`, labelText, el, {
|
|
330
|
-
isSelected,
|
|
331
|
-
});
|
|
332
|
-
return (
|
|
333
|
-
<button
|
|
334
|
-
key={index}
|
|
335
|
-
className="inspector-element-tag"
|
|
336
|
-
onClick={() => onElementSelect?.(el)}
|
|
337
|
-
style={{
|
|
338
|
-
background: isSelected ? "rgb(58, 18, 189)" : "#ffffff",
|
|
339
|
-
color: isSelected ? "#ffffff" : "#475569",
|
|
340
|
-
border: isSelected ? "none" : "1px solid #e2e8f0",
|
|
341
|
-
padding: "4px 8px",
|
|
342
|
-
borderRadius: "4px",
|
|
343
|
-
fontSize: "11px",
|
|
344
|
-
fontWeight: "500",
|
|
345
|
-
cursor: "pointer",
|
|
346
|
-
whiteSpace: "nowrap",
|
|
347
|
-
flexShrink: 0,
|
|
348
|
-
height: "24px",
|
|
349
|
-
display: "flex",
|
|
350
|
-
alignItems: "center",
|
|
351
|
-
boxShadow: isSelected
|
|
352
|
-
? "0 1px 3px rgba(58, 18, 189, 0.2)"
|
|
353
|
-
: "0 1px 2px rgba(0, 0, 0, 0.05)",
|
|
354
|
-
}}
|
|
355
|
-
>
|
|
356
|
-
{String(labelText)}
|
|
357
|
-
</button>
|
|
358
|
-
);
|
|
359
|
-
})}
|
|
360
|
-
</div>
|
|
361
|
-
)
|
|
362
|
-
);
|
|
363
|
-
})()}
|
|
364
|
-
|
|
365
|
-
<PromptInput
|
|
366
|
-
placeholder={labels.promptPlaceholder || "Ask AI..."}
|
|
367
|
-
onSubmit={onPromptSubmit}
|
|
368
|
-
onClose={onClose}
|
|
369
|
-
autoFocus={!activeTab}
|
|
370
|
-
/>
|
|
371
|
-
{/* Custom Tabs */}
|
|
372
|
-
{availableTabs.length > 0 && activeTab ? (
|
|
373
|
-
<div className={styles.tabsContainer}>
|
|
374
|
-
{/* Tab Buttons */}
|
|
375
|
-
<div
|
|
376
|
-
className={styles.tabsList}
|
|
377
|
-
style={{
|
|
378
|
-
backgroundColor: theme.tabContainerBg,
|
|
379
|
-
}}
|
|
380
|
-
>
|
|
381
|
-
{availableTabs.map((tab) => (
|
|
382
|
-
<TabButton
|
|
383
|
-
key={tab}
|
|
384
|
-
active={activeTab === tab}
|
|
385
|
-
onClick={() => onTabChange(tab)}
|
|
386
|
-
theme={theme}
|
|
387
|
-
>
|
|
388
|
-
{TAB_LABELS[tab]}
|
|
389
|
-
</TabButton>
|
|
390
|
-
))}
|
|
391
|
-
</div>
|
|
392
|
-
|
|
393
|
-
{/* Tab Content */}
|
|
394
|
-
<div className={styles.tabContent}>
|
|
395
|
-
{activeTab === "text" && (
|
|
396
|
-
<TextEditor element={element} elementData={elementData} />
|
|
397
|
-
)}
|
|
398
|
-
{activeTab === "image" && (
|
|
399
|
-
<ImageEditor element={element} elementData={elementData} />
|
|
400
|
-
)}
|
|
401
|
-
{activeTab === "style" && (
|
|
402
|
-
<StyleEditor element={element} elementData={elementData} />
|
|
403
|
-
)}
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
) : null}
|
|
407
|
-
</div>
|
|
408
|
-
);
|
|
409
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
.hiddenInput {
|
|
2
|
-
position: absolute;
|
|
3
|
-
width: 1px;
|
|
4
|
-
height: 1px;
|
|
5
|
-
padding: 0;
|
|
6
|
-
margin: -1px;
|
|
7
|
-
overflow: hidden;
|
|
8
|
-
clip: rect(0, 0, 0, 0);
|
|
9
|
-
white-space: nowrap;
|
|
10
|
-
border-width: 0;
|
|
11
|
-
opacity: 0;
|
|
12
|
-
pointer-events: none;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.uploadBox {
|
|
16
|
-
min-height: 100px;
|
|
17
|
-
border: 2px dashed;
|
|
18
|
-
border-radius: var(--radius-lg);
|
|
19
|
-
cursor: pointer;
|
|
20
|
-
transition: all var(--transition-fast);
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
flex-direction: column;
|
|
25
|
-
gap: var(--spacing-2);
|
|
26
|
-
padding: var(--spacing-4);
|
|
27
|
-
position: relative;
|
|
28
|
-
overflow: hidden;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.uploadBox:hover {
|
|
32
|
-
opacity: 0.9;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.uploadContent {
|
|
36
|
-
text-align: center;
|
|
37
|
-
pointer-events: none;
|
|
38
|
-
display: flex;
|
|
39
|
-
flex-direction: column;
|
|
40
|
-
align-items: center;
|
|
41
|
-
gap: var(--spacing-2);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.uploadIcon {
|
|
45
|
-
width: 32px;
|
|
46
|
-
height: 32px;
|
|
47
|
-
margin-bottom: var(--spacing-2);
|
|
48
|
-
opacity: 0.5;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.uploadTitle {
|
|
52
|
-
font-family: "Satoshi", var(--font-family-sans);
|
|
53
|
-
font-size: var(--text-sm);
|
|
54
|
-
font-weight: var(--font-medium);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.uploadHint {
|
|
58
|
-
font-family: "Satoshi", var(--font-family-sans);
|
|
59
|
-
font-size: var(--text-xs);
|
|
60
|
-
margin-top: var(--spacing-0_5);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.previewImage {
|
|
64
|
-
width: 100%;
|
|
65
|
-
height: auto;
|
|
66
|
-
max-height: 100px;
|
|
67
|
-
object-fit: contain;
|
|
68
|
-
border-radius: var(--radius-lg);
|
|
69
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* UploadBox component - Drag & drop image upload
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { useRef, useState } from "react";
|
|
6
|
-
import { useInspectorStore } from "../../store/useInspectorStore";
|
|
7
|
-
import styles from "./UploadBox.module.css";
|
|
8
|
-
|
|
9
|
-
interface UploadBoxProps {
|
|
10
|
-
onFileSelect: (file: File) => void;
|
|
11
|
-
previewImage: string | null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function UploadBox({ onFileSelect, previewImage }: UploadBoxProps) {
|
|
15
|
-
const { theme, labels } = useInspectorStore();
|
|
16
|
-
const inputRef = useRef<HTMLInputElement>(null);
|
|
17
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
18
|
-
|
|
19
|
-
const handleClick = () => {
|
|
20
|
-
inputRef.current?.click();
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
24
|
-
const file = e.target.files?.[0];
|
|
25
|
-
if (file && file.type.startsWith("image/")) {
|
|
26
|
-
onFileSelect(file);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
31
|
-
e.preventDefault();
|
|
32
|
-
setIsDragging(true);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
setIsDragging(false);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
|
41
|
-
e.preventDefault();
|
|
42
|
-
setIsDragging(false);
|
|
43
|
-
|
|
44
|
-
const file = e.dataTransfer?.files[0];
|
|
45
|
-
if (file && file.type.startsWith("image/")) {
|
|
46
|
-
onFileSelect(file);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<>
|
|
52
|
-
<input
|
|
53
|
-
ref={inputRef}
|
|
54
|
-
type="file"
|
|
55
|
-
accept="image/*"
|
|
56
|
-
onChange={handleFileChange}
|
|
57
|
-
className={styles.hiddenInput}
|
|
58
|
-
/>
|
|
59
|
-
<div
|
|
60
|
-
onClick={handleClick}
|
|
61
|
-
onDragOver={handleDragOver}
|
|
62
|
-
onDragLeave={handleDragLeave}
|
|
63
|
-
onDrop={handleDrop}
|
|
64
|
-
className={styles.uploadBox}
|
|
65
|
-
style={{
|
|
66
|
-
borderColor: isDragging ? theme.buttonColor : theme.inputBorderColor,
|
|
67
|
-
backgroundColor: isDragging
|
|
68
|
-
? `${theme.buttonColor}10`
|
|
69
|
-
: theme.inputBackgroundColor,
|
|
70
|
-
}}
|
|
71
|
-
>
|
|
72
|
-
{!previewImage ? (
|
|
73
|
-
<div className={styles.uploadContent}>
|
|
74
|
-
<svg
|
|
75
|
-
width="32"
|
|
76
|
-
height="32"
|
|
77
|
-
viewBox="0 0 24 24"
|
|
78
|
-
fill="none"
|
|
79
|
-
className={styles.uploadIcon}
|
|
80
|
-
style={{ color: theme.secondaryTextColor }}
|
|
81
|
-
>
|
|
82
|
-
<path
|
|
83
|
-
d="M21 15V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V15M17 8L12 3M12 3L7 8M12 3V15"
|
|
84
|
-
stroke="currentColor"
|
|
85
|
-
strokeWidth="2"
|
|
86
|
-
strokeLinecap="round"
|
|
87
|
-
strokeLinejoin="round"
|
|
88
|
-
/>
|
|
89
|
-
</svg>
|
|
90
|
-
<div
|
|
91
|
-
className={styles.uploadTitle}
|
|
92
|
-
style={{ color: theme.textColor }}
|
|
93
|
-
>
|
|
94
|
-
{labels.imageUploadTitle || "Click or drag image"}
|
|
95
|
-
</div>
|
|
96
|
-
<div
|
|
97
|
-
className={styles.uploadHint}
|
|
98
|
-
style={{ color: theme.secondaryTextColor }}
|
|
99
|
-
>
|
|
100
|
-
{labels.imageUploadHint || "Supports: JPG, PNG, GIF"}
|
|
101
|
-
</div>
|
|
102
|
-
</div>
|
|
103
|
-
) : (
|
|
104
|
-
<img
|
|
105
|
-
src={previewImage}
|
|
106
|
-
alt="Preview"
|
|
107
|
-
className={styles.previewImage}
|
|
108
|
-
/>
|
|
109
|
-
)}
|
|
110
|
-
</div>
|
|
111
|
-
</>
|
|
112
|
-
);
|
|
113
|
-
}
|