@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,68 +1,130 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import Modal from './Modal';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useRenderStore,
|
|
5
|
+
type BuilderPromptManagerConfig,
|
|
6
|
+
type BuilderPromptManagerGenerateRequest,
|
|
7
|
+
type BuilderPromptManagerGenerateResponse,
|
|
8
|
+
type BuilderPromptSchemaType,
|
|
9
|
+
} from '../store';
|
|
4
10
|
import type { Node } from '../types/Node';
|
|
5
11
|
import { nodeToXml, xmlToNode } from '../utils/nodeXml';
|
|
6
12
|
import { analyseAndProccess } from '../utils/analyseNode';
|
|
7
13
|
import { onboardPromptScheme } from '../assets/prompt-scheme-onboard.generated';
|
|
8
14
|
import { paywallPromptScheme } from '../assets/prompt-scheme-paywall.generated';
|
|
9
15
|
|
|
10
|
-
type PromptManagerTab = 'xml-editor' | 'prompt-schema';
|
|
16
|
+
export type PromptManagerTab = 'xml-editor' | 'ai-layout' | 'prompt-schema';
|
|
17
|
+
type PromptSchemaSubTab = BuilderPromptSchemaType;
|
|
18
|
+
|
|
19
|
+
const promptSchemas: Record<PromptSchemaSubTab, string> = {
|
|
20
|
+
onboard: onboardPromptScheme,
|
|
21
|
+
paywall: paywallPromptScheme,
|
|
22
|
+
};
|
|
11
23
|
|
|
12
24
|
type PromptManagerModalProps = {
|
|
13
25
|
data: Node;
|
|
14
26
|
setData: React.Dispatch<React.SetStateAction<Node>>;
|
|
15
27
|
onClose: () => void;
|
|
28
|
+
initialTab?: PromptManagerTab;
|
|
29
|
+
showTabs?: boolean;
|
|
30
|
+
title?: string;
|
|
16
31
|
};
|
|
17
32
|
|
|
18
33
|
export function PromptManagerModal({
|
|
19
34
|
data,
|
|
20
35
|
setData,
|
|
21
36
|
onClose,
|
|
37
|
+
initialTab = 'xml-editor',
|
|
38
|
+
showTabs = true,
|
|
39
|
+
title,
|
|
22
40
|
}: PromptManagerModalProps) {
|
|
23
|
-
const
|
|
41
|
+
const promptManagerConfig = useRenderStore((s) => s.promptManagerConfig);
|
|
42
|
+
const hasAiLayout = Boolean(
|
|
43
|
+
promptManagerConfig?.generate && promptManagerConfig?.renderAiLayout,
|
|
44
|
+
);
|
|
45
|
+
const [activeTab, setActiveTab] = useState<PromptManagerTab>(initialTab);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setActiveTab(initialTab);
|
|
49
|
+
}, [initialTab]);
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (activeTab === 'ai-layout' && !hasAiLayout) {
|
|
53
|
+
setActiveTab('xml-editor');
|
|
54
|
+
}
|
|
55
|
+
}, [activeTab, hasAiLayout]);
|
|
56
|
+
|
|
57
|
+
const isAiLayoutMode =
|
|
58
|
+
activeTab === 'ai-layout' && hasAiLayout && Boolean(promptManagerConfig);
|
|
24
59
|
|
|
25
60
|
return (
|
|
26
61
|
<Modal
|
|
27
62
|
onClose={onClose}
|
|
28
63
|
ariaLabelledBy="prompt-manager-modal-title"
|
|
29
64
|
className="modal--large modal--scrollable"
|
|
30
|
-
contentClassName=
|
|
65
|
+
contentClassName={[
|
|
66
|
+
'prompt-manager-modal__content',
|
|
67
|
+
isAiLayoutMode ? 'prompt-manager-modal__content--ai-layout' : '',
|
|
68
|
+
]
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.join(' ')}
|
|
31
71
|
>
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
{!isAiLayoutMode && (
|
|
73
|
+
<div className="modal__header prompt-manager-modal__header">
|
|
74
|
+
<h3 id="prompt-manager-modal-title" className="modal__title">
|
|
75
|
+
{title ||
|
|
76
|
+
(activeTab === 'ai-layout'
|
|
77
|
+
? 'AI Layout Studio'
|
|
78
|
+
: 'Prompt Manager')}
|
|
79
|
+
</h3>
|
|
80
|
+
<button type="button" className="editor-button" onClick={onClose}>
|
|
81
|
+
Close
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
40
85
|
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
{!isAiLayoutMode && showTabs && (
|
|
87
|
+
<div className="prompt-manager-modal__tabs">
|
|
88
|
+
<button
|
|
89
|
+
type="button"
|
|
90
|
+
className={`prompt-manager-modal__tab${
|
|
91
|
+
activeTab === 'xml-editor' ? ' is-active' : ''
|
|
92
|
+
}`}
|
|
93
|
+
onClick={() => setActiveTab('xml-editor')}
|
|
94
|
+
>
|
|
95
|
+
XML Editor
|
|
96
|
+
</button>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
className={`prompt-manager-modal__tab${
|
|
100
|
+
activeTab === 'prompt-schema' ? ' is-active' : ''
|
|
101
|
+
}`}
|
|
102
|
+
onClick={() => setActiveTab('prompt-schema')}
|
|
103
|
+
>
|
|
104
|
+
Prompt Şeması
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
61
108
|
|
|
62
|
-
<div
|
|
109
|
+
<div
|
|
110
|
+
className={[
|
|
111
|
+
'prompt-manager-modal__body',
|
|
112
|
+
isAiLayoutMode ? 'prompt-manager-modal__body--ai-layout' : '',
|
|
113
|
+
]
|
|
114
|
+
.filter(Boolean)
|
|
115
|
+
.join(' ')}
|
|
116
|
+
>
|
|
63
117
|
{activeTab === 'xml-editor' && (
|
|
64
118
|
<XmlEditorTab data={data} setData={setData} />
|
|
65
119
|
)}
|
|
120
|
+
{activeTab === 'ai-layout' && hasAiLayout && promptManagerConfig && (
|
|
121
|
+
<InjectedAiLayoutTab
|
|
122
|
+
data={data}
|
|
123
|
+
setData={setData}
|
|
124
|
+
config={promptManagerConfig}
|
|
125
|
+
onClose={onClose}
|
|
126
|
+
/>
|
|
127
|
+
)}
|
|
66
128
|
{activeTab === 'prompt-schema' && <PromptSchemaTab />}
|
|
67
129
|
</div>
|
|
68
130
|
</Modal>
|
|
@@ -177,17 +239,145 @@ function XmlEditorTab({
|
|
|
177
239
|
}
|
|
178
240
|
|
|
179
241
|
/* ------------------------------------------------------------------ */
|
|
180
|
-
/* Tab 2 –
|
|
242
|
+
/* Tab 2 – AI Layout */
|
|
181
243
|
/* ------------------------------------------------------------------ */
|
|
182
244
|
|
|
183
|
-
|
|
245
|
+
function normalizeGeneratedResult(
|
|
246
|
+
value: BuilderPromptManagerGenerateResponse,
|
|
247
|
+
): { xml: string; rawText?: string; model?: string } {
|
|
248
|
+
if (typeof value === 'string') {
|
|
249
|
+
const xml = extractXml(value);
|
|
250
|
+
return { xml, rawText: value };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const xml = extractXml(value.xml ?? '');
|
|
254
|
+
return {
|
|
255
|
+
xml,
|
|
256
|
+
rawText: value.rawText,
|
|
257
|
+
model: value.model,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function extractXml(text: string): string {
|
|
262
|
+
const trimmed = text.trim();
|
|
263
|
+
if (!trimmed) return '';
|
|
264
|
+
|
|
265
|
+
const fenced = trimmed.match(/```(?:xml)?\s*([\s\S]*?)```/i);
|
|
266
|
+
const withoutFence = fenced?.[1]?.trim() ?? trimmed;
|
|
267
|
+
const firstTag = withoutFence.indexOf('<');
|
|
268
|
+
const lastTag = withoutFence.lastIndexOf('>');
|
|
269
|
+
|
|
270
|
+
if (firstTag >= 0 && lastTag > firstTag) {
|
|
271
|
+
return withoutFence.slice(firstTag, lastTag + 1).trim();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return withoutFence;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function InjectedAiLayoutTab({
|
|
278
|
+
data,
|
|
279
|
+
setData,
|
|
280
|
+
config,
|
|
281
|
+
onClose,
|
|
282
|
+
}: {
|
|
283
|
+
data: Node;
|
|
284
|
+
setData: React.Dispatch<React.SetStateAction<Node>>;
|
|
285
|
+
config: BuilderPromptManagerConfig;
|
|
286
|
+
onClose: () => void;
|
|
287
|
+
}) {
|
|
288
|
+
const setCurrent = useRenderStore((s) => s.setCurrent);
|
|
289
|
+
const Renderer = config.renderAiLayout;
|
|
290
|
+
|
|
291
|
+
const initialXml = useMemo(() => {
|
|
292
|
+
try {
|
|
293
|
+
return nodeToXml(data);
|
|
294
|
+
} catch {
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
}, [data]);
|
|
298
|
+
|
|
299
|
+
const modelOptions =
|
|
300
|
+
config.models && config.models.length > 0
|
|
301
|
+
? config.models
|
|
302
|
+
: [{ id: 'gpt-5.4', label: 'GPT 5.4' }];
|
|
303
|
+
|
|
304
|
+
const defaultModel =
|
|
305
|
+
(config.defaultModel &&
|
|
306
|
+
modelOptions.find((item) => item.id === config.defaultModel)?.id) ||
|
|
307
|
+
modelOptions[0].id;
|
|
308
|
+
const defaultMode = config.defaultMode ?? 'edit-current';
|
|
309
|
+
|
|
310
|
+
const applyGeneratedXml = useCallback(
|
|
311
|
+
(xml: string) => {
|
|
312
|
+
const extractedXml = extractXml(xml);
|
|
313
|
+
if (!extractedXml) {
|
|
314
|
+
return {
|
|
315
|
+
success: false,
|
|
316
|
+
error: 'Geçerli bir XML bulunamadı.',
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
const parsed = xmlToNode(extractedXml);
|
|
322
|
+
const processed = analyseAndProccess(parsed as Node) as Node;
|
|
323
|
+
setData(processed);
|
|
324
|
+
setCurrent(processed);
|
|
325
|
+
|
|
326
|
+
return { success: true };
|
|
327
|
+
} catch (error) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
error:
|
|
331
|
+
error instanceof Error
|
|
332
|
+
? error.message
|
|
333
|
+
: 'Generated XML apply edilemedi.',
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
[setCurrent, setData],
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const generate = useCallback(
|
|
341
|
+
async (payload: BuilderPromptManagerGenerateRequest) => {
|
|
342
|
+
const normalized = normalizeGeneratedResult(
|
|
343
|
+
await config.generate(payload),
|
|
344
|
+
);
|
|
345
|
+
if (!normalized.xml) {
|
|
346
|
+
throw new Error('Model geçerli bir XML döndürmedi.');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return normalized;
|
|
350
|
+
},
|
|
351
|
+
[config],
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
if (!Renderer) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return (
|
|
359
|
+
<Renderer
|
|
360
|
+
modelOptions={modelOptions}
|
|
361
|
+
defaultModel={defaultModel}
|
|
362
|
+
defaultMode={defaultMode}
|
|
363
|
+
currentXml={initialXml}
|
|
364
|
+
promptSchemas={promptSchemas}
|
|
365
|
+
onClose={onClose}
|
|
366
|
+
generate={generate}
|
|
367
|
+
applyGeneratedXml={applyGeneratedXml}
|
|
368
|
+
/>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* ------------------------------------------------------------------ */
|
|
373
|
+
/* Tab 3 – Prompt Şeması */
|
|
374
|
+
/* ------------------------------------------------------------------ */
|
|
184
375
|
|
|
185
376
|
function PromptSchemaTab() {
|
|
186
377
|
const [subTab, setSubTab] = useState<PromptSchemaSubTab>('onboard');
|
|
187
378
|
const [copied, setCopied] = useState(false);
|
|
188
379
|
|
|
189
|
-
const activeScheme =
|
|
190
|
-
subTab === 'onboard' ? onboardPromptScheme : paywallPromptScheme;
|
|
380
|
+
const activeScheme = promptSchemas[subTab];
|
|
191
381
|
|
|
192
382
|
const handleCopy = async () => {
|
|
193
383
|
try {
|
package/src/store.ts
CHANGED
|
@@ -13,6 +13,79 @@ export type LanguageColumn = {
|
|
|
13
13
|
iso_code: string;
|
|
14
14
|
is_right_alignment: boolean;
|
|
15
15
|
};
|
|
16
|
+
|
|
17
|
+
export type QuickActionConfig = {
|
|
18
|
+
label: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
onClick: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type BuilderMediaFieldKind = 'image' | 'lottie' | 'video';
|
|
24
|
+
|
|
25
|
+
export type BuilderMediaFieldProps = {
|
|
26
|
+
value?: string;
|
|
27
|
+
onChange: (v?: string) => void;
|
|
28
|
+
fieldName: string;
|
|
29
|
+
componentType?: string;
|
|
30
|
+
fieldKind: BuilderMediaFieldKind;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type BuilderPromptManagerMode = 'edit-current' | 'create-new';
|
|
34
|
+
export type BuilderPromptSchemaType = 'onboard' | 'paywall';
|
|
35
|
+
|
|
36
|
+
export type BuilderPromptManagerModel = {
|
|
37
|
+
id: string;
|
|
38
|
+
label: string;
|
|
39
|
+
provider?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type BuilderPromptManagerGenerateRequest = {
|
|
43
|
+
prompt: string;
|
|
44
|
+
model: string;
|
|
45
|
+
mode: BuilderPromptManagerMode;
|
|
46
|
+
currentXml: string;
|
|
47
|
+
promptSchema: string;
|
|
48
|
+
promptSchemaType: BuilderPromptSchemaType;
|
|
49
|
+
xmlStructureHint?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type BuilderPromptManagerGenerateResponse =
|
|
53
|
+
| {
|
|
54
|
+
xml: string;
|
|
55
|
+
rawText?: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
}
|
|
58
|
+
| string;
|
|
59
|
+
|
|
60
|
+
export type BuilderPromptManagerApplyResult = {
|
|
61
|
+
success: boolean;
|
|
62
|
+
error?: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type BuilderPromptManagerAiLayoutProps = {
|
|
66
|
+
modelOptions: BuilderPromptManagerModel[];
|
|
67
|
+
defaultModel: string;
|
|
68
|
+
defaultMode: BuilderPromptManagerMode;
|
|
69
|
+
currentXml: string;
|
|
70
|
+
promptSchemas: Record<BuilderPromptSchemaType, string>;
|
|
71
|
+
onClose: () => void;
|
|
72
|
+
generate: (payload: BuilderPromptManagerGenerateRequest) => Promise<{
|
|
73
|
+
xml: string;
|
|
74
|
+
rawText?: string;
|
|
75
|
+
model?: string;
|
|
76
|
+
}>;
|
|
77
|
+
applyGeneratedXml: (xml: string) => BuilderPromptManagerApplyResult;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type BuilderPromptManagerConfig = {
|
|
81
|
+
models: BuilderPromptManagerModel[];
|
|
82
|
+
defaultModel?: string;
|
|
83
|
+
defaultMode?: BuilderPromptManagerMode;
|
|
84
|
+
generate: (
|
|
85
|
+
payload: BuilderPromptManagerGenerateRequest,
|
|
86
|
+
) => Promise<BuilderPromptManagerGenerateResponse>;
|
|
87
|
+
renderAiLayout?: ComponentType<BuilderPromptManagerAiLayoutProps>;
|
|
88
|
+
};
|
|
16
89
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
17
90
|
import { shallow } from 'zustand/shallow';
|
|
18
91
|
import type { Device } from './types/Device';
|
|
@@ -127,9 +200,15 @@ type RenderStore = {
|
|
|
127
200
|
setRenderStringChildrenField: (
|
|
128
201
|
comp?: ComponentType<{ value: string; onChange: (v: string) => void }>,
|
|
129
202
|
) => void;
|
|
203
|
+
renderMediaField?: ComponentType<BuilderMediaFieldProps>;
|
|
204
|
+
setRenderMediaField: (comp?: ComponentType<BuilderMediaFieldProps>) => void;
|
|
130
205
|
|
|
131
206
|
localizationApiConfig?: LocalizationApiConfig;
|
|
132
207
|
setLocalizationApiConfig: (config?: LocalizationApiConfig) => void;
|
|
208
|
+
quickActionConfig?: QuickActionConfig;
|
|
209
|
+
setQuickActionConfig: (config?: QuickActionConfig) => void;
|
|
210
|
+
promptManagerConfig?: BuilderPromptManagerConfig;
|
|
211
|
+
setPromptManagerConfig: (config?: BuilderPromptManagerConfig) => void;
|
|
133
212
|
};
|
|
134
213
|
|
|
135
214
|
export const useRenderStore = createWithEqualityFn<RenderStore>()(
|
|
@@ -331,10 +410,16 @@ export const useRenderStore = createWithEqualityFn<RenderStore>()(
|
|
|
331
410
|
renderStringChildrenField: undefined,
|
|
332
411
|
setRenderStringChildrenField: (comp) =>
|
|
333
412
|
set({ renderStringChildrenField: comp }),
|
|
413
|
+
renderMediaField: undefined,
|
|
414
|
+
setRenderMediaField: (comp) => set({ renderMediaField: comp }),
|
|
334
415
|
|
|
335
416
|
localizationApiConfig: undefined,
|
|
336
417
|
setLocalizationApiConfig: (config) =>
|
|
337
418
|
set({ localizationApiConfig: config }),
|
|
419
|
+
quickActionConfig: undefined,
|
|
420
|
+
setQuickActionConfig: (config) => set({ quickActionConfig: config }),
|
|
421
|
+
promptManagerConfig: undefined,
|
|
422
|
+
setPromptManagerConfig: (config) => set({ promptManagerConfig: config }),
|
|
338
423
|
}),
|
|
339
424
|
{
|
|
340
425
|
name: 'render-store',
|
|
@@ -55,6 +55,18 @@
|
|
|
55
55
|
margin-left: 4px;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
.rb-bottom-bar__button + .rb-bottom-bar__tool-menu-anchor {
|
|
59
|
+
border-left: 1px solid colors.$borderColor;
|
|
60
|
+
padding-left: 14px;
|
|
61
|
+
margin-left: 4px;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.rb-bottom-bar__tool-menu-anchor + .rb-bottom-bar__button {
|
|
65
|
+
border-left: 1px solid colors.$borderColor;
|
|
66
|
+
padding-left: 14px;
|
|
67
|
+
margin-left: 4px;
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
.rb-bottom-bar__button--rtl {
|
|
59
71
|
gap: 6px;
|
|
60
72
|
padding-right: 10px;
|
|
@@ -102,3 +114,70 @@
|
|
|
102
114
|
margin-left: 8px;
|
|
103
115
|
padding-left: 14px;
|
|
104
116
|
}
|
|
117
|
+
|
|
118
|
+
.rb-bottom-bar__tool-menu-anchor {
|
|
119
|
+
position: relative;
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.rb-bottom-bar__tool-menu {
|
|
125
|
+
position: absolute;
|
|
126
|
+
bottom: calc(100% + 10px);
|
|
127
|
+
left: 50%;
|
|
128
|
+
transform: translateX(-50%);
|
|
129
|
+
min-width: 280px;
|
|
130
|
+
padding: 10px;
|
|
131
|
+
border-radius: 14px;
|
|
132
|
+
border: 1px solid colors.$borderColor;
|
|
133
|
+
background:
|
|
134
|
+
linear-gradient(
|
|
135
|
+
160deg,
|
|
136
|
+
hsl(var(--primary, var(--rb-primary, 221.2 83.2% 53.3%)) / 0.16),
|
|
137
|
+
hsl(var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.02)
|
|
138
|
+
),
|
|
139
|
+
colors.$surfaceColor;
|
|
140
|
+
box-shadow: 0 22px 44px
|
|
141
|
+
hsl(var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.22);
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
gap: 6px;
|
|
145
|
+
z-index: 260;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.rb-bottom-bar__tool-menu-title {
|
|
149
|
+
font-size: 11px;
|
|
150
|
+
letter-spacing: 0.4px;
|
|
151
|
+
text-transform: uppercase;
|
|
152
|
+
color: colors.$mutedTextColor;
|
|
153
|
+
padding: 2px 8px 6px;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.rb-bottom-bar__tool-menu-item {
|
|
157
|
+
border: 1px solid transparent;
|
|
158
|
+
background: transparent;
|
|
159
|
+
color: colors.$textColor;
|
|
160
|
+
text-align: left;
|
|
161
|
+
padding: 10px 12px;
|
|
162
|
+
border-radius: 10px;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
display: flex;
|
|
165
|
+
flex-direction: column;
|
|
166
|
+
gap: 2px;
|
|
167
|
+
transition: all 0.18s ease;
|
|
168
|
+
|
|
169
|
+
&:hover {
|
|
170
|
+
border-color: hsl(var(--primary, var(--rb-primary, 221.2 83.2% 53.3%)) / 0.45);
|
|
171
|
+
background: hsl(var(--primary, var(--rb-primary, 221.2 83.2% 53.3%)) / 0.12);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.rb-bottom-bar__tool-menu-item-label {
|
|
176
|
+
font-size: 13px;
|
|
177
|
+
font-weight: 700;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.rb-bottom-bar__tool-menu-item-desc {
|
|
181
|
+
font-size: 11px;
|
|
182
|
+
color: colors.$mutedTextColor;
|
|
183
|
+
}
|