@developer_tribe/react-builder 1.2.47 → 1.2.48
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/dist/components/DeviceButton.d.ts +2 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.web.cjs.js +3 -3
- package/dist/index.web.cjs.js.map +1 -1
- package/dist/index.web.d.ts +5 -1
- package/dist/index.web.esm.js +3 -3
- package/dist/index.web.esm.js.map +1 -1
- package/dist/modals/PromptManagerModal.d.ts +5 -1
- package/dist/store.d.ts +65 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/scripts/.DS_Store +0 -0
- package/src/.DS_Store +0 -0
- package/src/assets/meta.json +1 -1
- package/src/attributes-editor/Field.tsx +53 -0
- package/src/components/BottomBar.tsx +134 -14
- package/src/components/DeviceButton.tsx +81 -22
- package/src/components/EditorHeader.tsx +3 -2
- package/src/index.web.ts +32 -1
- package/src/modals/DeviceSelectorModal.tsx +2 -0
- package/src/modals/PromptManagerModal.tsx +228 -38
- package/src/store.ts +85 -0
- package/src/styles/components/_bottom-bar.scss +79 -0
- package/src/styles/components/_editor-shell.scss +235 -9
- package/src/styles/modals/_device-selector.scss +2 -2
- package/src/styles/modals/_prompt-manager-modal.scss +75 -5
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useMemo,
|
|
3
|
+
useState,
|
|
4
|
+
useCallback,
|
|
5
|
+
useRef,
|
|
6
|
+
useEffect,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import { Icon } from './Icon.generated';
|
|
3
9
|
import type { IconsType } from '../types/Icons';
|
|
4
10
|
import { useRenderStore } from '../store';
|
|
@@ -9,6 +15,7 @@ import {
|
|
|
9
15
|
Modal,
|
|
10
16
|
PromptManagerModal,
|
|
11
17
|
} from '../modals';
|
|
18
|
+
import type { PromptManagerTab } from '../modals/PromptManagerModal';
|
|
12
19
|
import type { Node } from '../types/Node';
|
|
13
20
|
import type { Project } from '../types/Project';
|
|
14
21
|
import { DebugJsonPage } from '../pages/DebugJsonPage';
|
|
@@ -46,6 +53,8 @@ export function BottomBar({
|
|
|
46
53
|
setPreviewMode,
|
|
47
54
|
setIsRtl,
|
|
48
55
|
languageColumns,
|
|
56
|
+
quickActionConfig,
|
|
57
|
+
promptManagerConfig,
|
|
49
58
|
} = useRenderStore((s) => ({
|
|
50
59
|
localization: s.localization,
|
|
51
60
|
setLocalization: s.setLocalization,
|
|
@@ -57,14 +66,23 @@ export function BottomBar({
|
|
|
57
66
|
setPreviewMode: s.setPreviewMode,
|
|
58
67
|
setIsRtl: s.setIsRtl,
|
|
59
68
|
languageColumns: s.languageColumns,
|
|
69
|
+
quickActionConfig: s.quickActionConfig,
|
|
70
|
+
promptManagerConfig: s.promptManagerConfig,
|
|
60
71
|
}));
|
|
72
|
+
const hasAiLayoutTool = Boolean(
|
|
73
|
+
promptManagerConfig?.generate && promptManagerConfig?.renderAiLayout,
|
|
74
|
+
);
|
|
61
75
|
|
|
62
76
|
const [isDebugOpen, setIsDebugOpen] = useState(false);
|
|
63
77
|
const [isLocalizationOpen, setIsLocalizationOpen] = useState(false);
|
|
64
78
|
const [isInspectOpen, setIsInspectOpen] = useState(false);
|
|
65
|
-
const [
|
|
79
|
+
const [promptMenuOpen, setPromptMenuOpen] = useState(false);
|
|
80
|
+
const [promptModalTab, setPromptModalTab] = useState<PromptManagerTab | null>(
|
|
81
|
+
null,
|
|
82
|
+
);
|
|
66
83
|
const [langDropdownOpen, setLangDropdownOpen] = useState(false);
|
|
67
84
|
const langTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
85
|
+
const promptMenuTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
68
86
|
|
|
69
87
|
// Use languageColumns from API, fallback to localization object keys
|
|
70
88
|
const availableLanguages = useMemo(() => {
|
|
@@ -99,6 +117,34 @@ export function BottomBar({
|
|
|
99
117
|
langTimeoutRef.current = setTimeout(() => setLangDropdownOpen(false), 200);
|
|
100
118
|
};
|
|
101
119
|
|
|
120
|
+
const handlePromptMenuMouseEnter = () => {
|
|
121
|
+
if (promptMenuTimeoutRef.current) {
|
|
122
|
+
clearTimeout(promptMenuTimeoutRef.current);
|
|
123
|
+
}
|
|
124
|
+
setPromptMenuOpen(true);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handlePromptMenuMouseLeave = () => {
|
|
128
|
+
promptMenuTimeoutRef.current = setTimeout(
|
|
129
|
+
() => setPromptMenuOpen(false),
|
|
130
|
+
200,
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const openPromptModal = (tab: PromptManagerTab) => {
|
|
135
|
+
setPromptModalTab(tab);
|
|
136
|
+
setPromptMenuOpen(false);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
useEffect(
|
|
140
|
+
() => () => {
|
|
141
|
+
if (langTimeoutRef.current) clearTimeout(langTimeoutRef.current);
|
|
142
|
+
if (promptMenuTimeoutRef.current)
|
|
143
|
+
clearTimeout(promptMenuTimeoutRef.current);
|
|
144
|
+
},
|
|
145
|
+
[],
|
|
146
|
+
);
|
|
147
|
+
|
|
102
148
|
const handleLocalicationChange = (next: Localication) => {
|
|
103
149
|
setLocalization(next);
|
|
104
150
|
};
|
|
@@ -174,17 +220,86 @@ export function BottomBar({
|
|
|
174
220
|
<Icon iconType={inspectIcon} size={20} color="currentColor" alt="" />
|
|
175
221
|
</button>
|
|
176
222
|
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}`}
|
|
182
|
-
aria-label="Prompt Manager"
|
|
183
|
-
aria-pressed={isPromptManagerOpen}
|
|
184
|
-
onClick={() => setIsPromptManagerOpen(true)}
|
|
223
|
+
<div
|
|
224
|
+
className="rb-bottom-bar__tool-menu-anchor"
|
|
225
|
+
onMouseEnter={handlePromptMenuMouseEnter}
|
|
226
|
+
onMouseLeave={handlePromptMenuMouseLeave}
|
|
185
227
|
>
|
|
186
|
-
<
|
|
187
|
-
|
|
228
|
+
<button
|
|
229
|
+
type="button"
|
|
230
|
+
className={`rb-bottom-bar__button${
|
|
231
|
+
promptMenuOpen || promptModalTab ? ' is-active' : ''
|
|
232
|
+
}`}
|
|
233
|
+
aria-label="Prompt Tools"
|
|
234
|
+
aria-pressed={promptMenuOpen || !!promptModalTab}
|
|
235
|
+
onClick={() => setPromptMenuOpen((prev) => !prev)}
|
|
236
|
+
>
|
|
237
|
+
<Icon
|
|
238
|
+
iconType={promptAiIcon}
|
|
239
|
+
size={20}
|
|
240
|
+
color="currentColor"
|
|
241
|
+
alt=""
|
|
242
|
+
/>
|
|
243
|
+
</button>
|
|
244
|
+
|
|
245
|
+
{promptMenuOpen && (
|
|
246
|
+
<div className="rb-bottom-bar__tool-menu">
|
|
247
|
+
<div className="rb-bottom-bar__tool-menu-title">Prompt Tools</div>
|
|
248
|
+
{hasAiLayoutTool && (
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
className="rb-bottom-bar__tool-menu-item"
|
|
252
|
+
onClick={() => openPromptModal('ai-layout')}
|
|
253
|
+
>
|
|
254
|
+
<span className="rb-bottom-bar__tool-menu-item-label">
|
|
255
|
+
AI Layout Studio
|
|
256
|
+
</span>
|
|
257
|
+
<span className="rb-bottom-bar__tool-menu-item-desc">
|
|
258
|
+
Queue + realtime ile XML generate/edit
|
|
259
|
+
</span>
|
|
260
|
+
</button>
|
|
261
|
+
)}
|
|
262
|
+
<button
|
|
263
|
+
type="button"
|
|
264
|
+
className="rb-bottom-bar__tool-menu-item"
|
|
265
|
+
onClick={() => openPromptModal('xml-editor')}
|
|
266
|
+
>
|
|
267
|
+
<span className="rb-bottom-bar__tool-menu-item-label">
|
|
268
|
+
XML Editor
|
|
269
|
+
</span>
|
|
270
|
+
<span className="rb-bottom-bar__tool-menu-item-desc">
|
|
271
|
+
XML'i manuel düzenle ve uygula
|
|
272
|
+
</span>
|
|
273
|
+
</button>
|
|
274
|
+
<button
|
|
275
|
+
type="button"
|
|
276
|
+
className="rb-bottom-bar__tool-menu-item"
|
|
277
|
+
onClick={() => openPromptModal('prompt-schema')}
|
|
278
|
+
>
|
|
279
|
+
<span className="rb-bottom-bar__tool-menu-item-label">
|
|
280
|
+
Prompt Şeması
|
|
281
|
+
</span>
|
|
282
|
+
<span className="rb-bottom-bar__tool-menu-item-desc">
|
|
283
|
+
Onboard/Paywall şema referansı
|
|
284
|
+
</span>
|
|
285
|
+
</button>
|
|
286
|
+
</div>
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
{quickActionConfig && (
|
|
291
|
+
<button
|
|
292
|
+
type="button"
|
|
293
|
+
className="rb-bottom-bar__button"
|
|
294
|
+
aria-label={quickActionConfig.title || quickActionConfig.label}
|
|
295
|
+
title={quickActionConfig.title || quickActionConfig.label}
|
|
296
|
+
onClick={quickActionConfig.onClick}
|
|
297
|
+
>
|
|
298
|
+
<span style={{ fontSize: 11, fontWeight: 700 }}>
|
|
299
|
+
{quickActionConfig.label}
|
|
300
|
+
</span>
|
|
301
|
+
</button>
|
|
302
|
+
)}
|
|
188
303
|
|
|
189
304
|
<div className="rb-bottom-bar__spacer" />
|
|
190
305
|
|
|
@@ -323,11 +438,16 @@ export function BottomBar({
|
|
|
323
438
|
<InspectModal onClose={() => setIsInspectOpen(false)} />
|
|
324
439
|
)}
|
|
325
440
|
|
|
326
|
-
{
|
|
441
|
+
{promptModalTab && (
|
|
327
442
|
<PromptManagerModal
|
|
328
443
|
data={data}
|
|
329
444
|
setData={setData}
|
|
330
|
-
|
|
445
|
+
initialTab={promptModalTab}
|
|
446
|
+
showTabs={promptModalTab !== 'ai-layout'}
|
|
447
|
+
title={
|
|
448
|
+
promptModalTab === 'ai-layout' ? 'AI Layout Studio' : undefined
|
|
449
|
+
}
|
|
450
|
+
onClose={() => setPromptModalTab(null)}
|
|
331
451
|
/>
|
|
332
452
|
)}
|
|
333
453
|
</>
|
|
@@ -14,6 +14,40 @@ type DeviceButtonProps = {
|
|
|
14
14
|
isFavorite?: boolean;
|
|
15
15
|
onToggleFavorite?: (device: Device, e: React.MouseEvent) => void;
|
|
16
16
|
onEdit?: (device: Device, e: React.MouseEvent) => void;
|
|
17
|
+
sizeVariant?: 'header' | 'modal';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const SHORT_NAME_PREFIXES = [
|
|
21
|
+
'samsung galaxy',
|
|
22
|
+
'google pixel',
|
|
23
|
+
'motorola',
|
|
24
|
+
'oneplus',
|
|
25
|
+
'huawei',
|
|
26
|
+
'xiaomi',
|
|
27
|
+
'redmi',
|
|
28
|
+
'galaxy',
|
|
29
|
+
'iphone',
|
|
30
|
+
'ipad',
|
|
31
|
+
'pixel',
|
|
32
|
+
'sony',
|
|
33
|
+
'nokia',
|
|
34
|
+
'oppo',
|
|
35
|
+
'vivo',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const clamp = (value: number, min: number, max: number): number =>
|
|
39
|
+
Math.min(max, Math.max(min, value));
|
|
40
|
+
|
|
41
|
+
const getCompactDeviceName = (name: string): string => {
|
|
42
|
+
const trimmed = name.trim();
|
|
43
|
+
const lower = trimmed.toLowerCase();
|
|
44
|
+
const matchedPrefix = SHORT_NAME_PREFIXES.find((prefix) =>
|
|
45
|
+
lower.startsWith(`${prefix} `),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (!matchedPrefix) return trimmed;
|
|
49
|
+
const compact = trimmed.slice(matchedPrefix.length).trim();
|
|
50
|
+
return compact || trimmed;
|
|
17
51
|
};
|
|
18
52
|
|
|
19
53
|
export function DeviceButton({
|
|
@@ -23,24 +57,37 @@ export function DeviceButton({
|
|
|
23
57
|
isFavorite,
|
|
24
58
|
onToggleFavorite,
|
|
25
59
|
onEdit,
|
|
60
|
+
sizeVariant = 'header',
|
|
26
61
|
}: DeviceButtonProps) {
|
|
27
62
|
const platformIcon = platformIcons[device.platform];
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
63
|
+
const safeWidth = Math.max(1, Number(device.width) || 1);
|
|
64
|
+
const safeHeight = Math.max(1, Number(device.height) || 1);
|
|
65
|
+
const compactName = getCompactDeviceName(device.name);
|
|
66
|
+
const tooltipText = `${device.name} - ${safeWidth}x${safeHeight}`;
|
|
67
|
+
const longSide = Math.max(safeWidth, safeHeight);
|
|
68
|
+
const baseLongSide = sizeVariant === 'modal' ? 2600 : 2400;
|
|
69
|
+
const basePreviewHeight = sizeVariant === 'modal' ? 58 : 30;
|
|
70
|
+
const typeBoost = device.type === 'tablet' ? 1.12 : 1;
|
|
71
|
+
const previewScale = clamp(
|
|
72
|
+
Math.sqrt(longSide / baseLongSide) * typeBoost,
|
|
73
|
+
sizeVariant === 'modal' ? 0.82 : 0.78,
|
|
74
|
+
sizeVariant === 'modal' ? 1.25 : 1.18,
|
|
75
|
+
);
|
|
76
|
+
const previewHeight = Math.round(basePreviewHeight * previewScale);
|
|
77
|
+
const frameStyle = {
|
|
78
|
+
aspectRatio: `${safeWidth} / ${safeHeight}`,
|
|
79
|
+
'--device-preview-height': `${previewHeight}px`,
|
|
80
|
+
} as React.CSSProperties;
|
|
36
81
|
|
|
37
82
|
return (
|
|
38
83
|
<button
|
|
39
84
|
type="button"
|
|
40
85
|
className={`editor-device-button${
|
|
41
86
|
selectedDevice === device ? ' editor-device-button--selected' : ''
|
|
42
|
-
}`}
|
|
87
|
+
} editor-device-button--${sizeVariant}`}
|
|
43
88
|
onClick={() => onSelect(device)}
|
|
89
|
+
title={tooltipText}
|
|
90
|
+
aria-label={tooltipText}
|
|
44
91
|
>
|
|
45
92
|
{onToggleFavorite && (
|
|
46
93
|
<span
|
|
@@ -51,10 +98,6 @@ export function DeviceButton({
|
|
|
51
98
|
}}
|
|
52
99
|
title={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
|
|
53
100
|
style={{
|
|
54
|
-
position: 'absolute',
|
|
55
|
-
top: 4,
|
|
56
|
-
right: 4,
|
|
57
|
-
cursor: 'pointer',
|
|
58
101
|
opacity: isFavorite ? 1 : 0.3,
|
|
59
102
|
color: isFavorite ? '#ecc538' : 'currentColor',
|
|
60
103
|
}}
|
|
@@ -73,8 +116,30 @@ export function DeviceButton({
|
|
|
73
116
|
</svg>
|
|
74
117
|
</span>
|
|
75
118
|
)}
|
|
76
|
-
|
|
77
|
-
|
|
119
|
+
|
|
120
|
+
<span className="editor-device-button__popover" aria-hidden="true">
|
|
121
|
+
<span className="editor-device-button__popover-title">
|
|
122
|
+
{device.name}
|
|
123
|
+
</span>
|
|
124
|
+
<span className="editor-device-button__popover-meta">
|
|
125
|
+
{safeWidth}x{safeHeight}
|
|
126
|
+
</span>
|
|
127
|
+
</span>
|
|
128
|
+
|
|
129
|
+
<span className="editor-device-button__preview" aria-hidden="true">
|
|
130
|
+
<span
|
|
131
|
+
className="editor-device-button__frame"
|
|
132
|
+
data-platform={device.platform}
|
|
133
|
+
data-type={device.type}
|
|
134
|
+
style={frameStyle}
|
|
135
|
+
>
|
|
136
|
+
<span className="editor-device-button__screen" />
|
|
137
|
+
<span className="editor-device-button__notch" />
|
|
138
|
+
</span>
|
|
139
|
+
</span>
|
|
140
|
+
|
|
141
|
+
<span className="editor-device-button__name">{compactName}</span>
|
|
142
|
+
|
|
78
143
|
{platformIcon && (
|
|
79
144
|
<span className="editor-device-button__platform">
|
|
80
145
|
<img src={platformIcon} alt="" aria-hidden="true" />
|
|
@@ -88,13 +153,7 @@ export function DeviceButton({
|
|
|
88
153
|
onEdit(device, e);
|
|
89
154
|
}}
|
|
90
155
|
title="Edit device"
|
|
91
|
-
style={{
|
|
92
|
-
position: 'absolute',
|
|
93
|
-
bottom: 4,
|
|
94
|
-
left: 4,
|
|
95
|
-
cursor: 'pointer',
|
|
96
|
-
opacity: 0.5,
|
|
97
|
-
}}
|
|
156
|
+
style={{ opacity: 0.5 }}
|
|
98
157
|
>
|
|
99
158
|
<svg
|
|
100
159
|
width="12"
|
|
@@ -288,14 +288,15 @@ export function EditorHeader({
|
|
|
288
288
|
device={deviceOption}
|
|
289
289
|
isFavorite={favoriteDevices.includes(deviceOption.name)}
|
|
290
290
|
onToggleFavorite={(d) => toggleFavoriteDevice(d.name)}
|
|
291
|
+
sizeVariant="header"
|
|
291
292
|
/>
|
|
292
293
|
))}
|
|
293
294
|
<button
|
|
294
|
-
className="editor-device-button"
|
|
295
|
+
className="editor-device-button editor-device-button--header editor-device-button--more"
|
|
295
296
|
aria-label="More devices"
|
|
296
297
|
onClick={() => setIsDevicesModalOpen(true)}
|
|
297
298
|
>
|
|
298
|
-
More
|
|
299
|
+
<span className="editor-device-button__more-label">More</span>
|
|
299
300
|
</button>
|
|
300
301
|
</div>
|
|
301
302
|
<div className="editor-header__actions">
|
package/src/index.web.ts
CHANGED
|
@@ -14,11 +14,26 @@ export type { EventObjectGenerated } from './build-components/OnboardButton/Onbo
|
|
|
14
14
|
|
|
15
15
|
// Host-app API: store (tema vb. builder içinde kullanılır; örn. tema toggle'ı builder'a senkronize etmek için).
|
|
16
16
|
export { useRenderStore } from './store';
|
|
17
|
+
export type {
|
|
18
|
+
BuilderPromptManagerAiLayoutProps,
|
|
19
|
+
BuilderPromptManagerConfig,
|
|
20
|
+
BuilderPromptManagerGenerateRequest,
|
|
21
|
+
BuilderPromptManagerGenerateResponse,
|
|
22
|
+
BuilderPromptManagerMode,
|
|
23
|
+
BuilderPromptManagerModel,
|
|
24
|
+
BuilderPromptSchemaType,
|
|
25
|
+
} from './store';
|
|
17
26
|
|
|
18
27
|
// Host-app API: let parent apps inject a custom "Text" field renderer.
|
|
19
28
|
import { useRenderStore } from './store';
|
|
20
29
|
import type { ComponentType } from 'react';
|
|
21
|
-
import type {
|
|
30
|
+
import type {
|
|
31
|
+
BuilderMediaFieldProps,
|
|
32
|
+
BuilderPromptManagerConfig,
|
|
33
|
+
LocalizationApiConfig,
|
|
34
|
+
LanguageColumn,
|
|
35
|
+
QuickActionConfig,
|
|
36
|
+
} from './store';
|
|
22
37
|
|
|
23
38
|
export function setBuilderStringChildrenField(
|
|
24
39
|
comp?: ComponentType<{ value: string; onChange: (v: string) => void }>,
|
|
@@ -33,3 +48,19 @@ export function setBuilderLocalizationConfig(config?: LocalizationApiConfig) {
|
|
|
33
48
|
export function setBuilderLanguageColumns(cols: LanguageColumn[]) {
|
|
34
49
|
useRenderStore.getState().setLanguageColumns(cols);
|
|
35
50
|
}
|
|
51
|
+
|
|
52
|
+
export function setBuilderQuickAction(config?: QuickActionConfig) {
|
|
53
|
+
useRenderStore.getState().setQuickActionConfig(config);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function setBuilderMediaField(
|
|
57
|
+
comp?: ComponentType<BuilderMediaFieldProps>,
|
|
58
|
+
) {
|
|
59
|
+
useRenderStore.getState().setRenderMediaField(comp);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function setBuilderPromptManagerConfig(
|
|
63
|
+
config?: BuilderPromptManagerConfig,
|
|
64
|
+
) {
|
|
65
|
+
useRenderStore.getState().setPromptManagerConfig(config);
|
|
66
|
+
}
|
|
@@ -116,6 +116,7 @@ export function DeviceSelectorModal({
|
|
|
116
116
|
isFavorite={true}
|
|
117
117
|
onToggleFavorite={(d) => toggleFavoriteDevice(d.name)}
|
|
118
118
|
onEdit={(d) => setEditingDevice(d)}
|
|
119
|
+
sizeVariant="modal"
|
|
119
120
|
/>
|
|
120
121
|
))}
|
|
121
122
|
</div>
|
|
@@ -147,6 +148,7 @@ export function DeviceSelectorModal({
|
|
|
147
148
|
? (d) => setEditingDevice(d)
|
|
148
149
|
: undefined
|
|
149
150
|
}
|
|
151
|
+
sizeVariant="modal"
|
|
150
152
|
/>
|
|
151
153
|
))}
|
|
152
154
|
</div>
|