@developer_tribe/react-builder 1.2.46 → 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.
Files changed (94) hide show
  1. package/dist/attributes-editor/Field.d.ts +3 -1
  2. package/dist/attributes-editor/attributesEditorModelTypes.d.ts +3 -0
  3. package/dist/attributes-editor/useAttributesEditorModel.d.ts +1 -1
  4. package/dist/build-components/FormSubmitButton/FormSubmitButtonProps.generated.d.ts +8 -3
  5. package/dist/build-components/GlobalProvider/GlobalContext.d.ts +28 -0
  6. package/dist/build-components/GlobalProvider/GlobalProvider.d.ts +5 -0
  7. package/dist/build-components/GlobalProvider/GlobalProviderProps.generated.d.ts +60 -0
  8. package/dist/build-components/GlobalProvider/globalProviderUtils.d.ts +28 -0
  9. package/dist/build-components/GlobalProvider/useGlobalNavigation.d.ts +19 -0
  10. package/dist/build-components/GlobalProvider/useGlobalProviderLogic.d.ts +15 -0
  11. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +8 -3
  12. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +2 -0
  13. package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +2 -0
  14. package/dist/build-components/SystemButton/SystemButtonProps.generated.d.ts +8 -3
  15. package/dist/build-components/SystemButton/usePlacementButtonEvents.d.ts +15 -2
  16. package/dist/build-components/TermsProvider/TermsProvider.d.ts +5 -0
  17. package/dist/build-components/TermsProvider/TermsProviderProps.generated.d.ts +55 -0
  18. package/dist/build-components/index.d.ts +3 -1
  19. package/dist/build-components/patterns.generated.d.ts +2128 -1333
  20. package/dist/components/DeviceButton.d.ts +2 -1
  21. package/dist/index.cjs.js +3 -3
  22. package/dist/index.cjs.js.map +1 -1
  23. package/dist/index.esm.js +3 -3
  24. package/dist/index.esm.js.map +1 -1
  25. package/dist/index.web.cjs.js +3 -3
  26. package/dist/index.web.cjs.js.map +1 -1
  27. package/dist/index.web.d.ts +5 -1
  28. package/dist/index.web.esm.js +3 -3
  29. package/dist/index.web.esm.js.map +1 -1
  30. package/dist/modals/PromptManagerModal.d.ts +5 -1
  31. package/dist/store.d.ts +65 -0
  32. package/dist/styles.css +1 -1
  33. package/dist/utils/nodeTree.d.ts +18 -0
  34. package/package.json +1 -1
  35. package/scripts/.DS_Store +0 -0
  36. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +68 -4
  37. package/src/.DS_Store +0 -0
  38. package/src/assets/meta.json +1 -1
  39. package/src/assets/prompt-scheme-onboard.generated.ts +1 -1
  40. package/src/assets/prompt-scheme-paywall.generated.ts +1 -1
  41. package/src/assets/samples/getSamples.ts +2 -0
  42. package/src/assets/samples/global-onboard-flow.json +735 -0
  43. package/src/assets/samples/terms-and-privacy-no-form.json +1 -1
  44. package/src/assets/samples/terms-and-privacy.json +1 -1
  45. package/src/attributes-editor/AttributesEditorView.tsx +3 -0
  46. package/src/attributes-editor/Field.tsx +144 -2
  47. package/src/attributes-editor/attributesEditorModelTypes.ts +3 -0
  48. package/src/attributes-editor/useAttributesEditorModel.ts +8 -0
  49. package/src/build-components/FormCheckbox/FormCheckbox.tsx +3 -3
  50. package/src/build-components/FormSubmitButton/FormSubmitButton.tsx +6 -0
  51. package/src/build-components/FormSubmitButton/FormSubmitButtonProps.generated.ts +26 -3
  52. package/src/build-components/GlobalProvider/GlobalContext.ts +48 -0
  53. package/src/build-components/GlobalProvider/GlobalProvider.tsx +51 -0
  54. package/src/build-components/GlobalProvider/GlobalProviderProps.generated.ts +78 -0
  55. package/src/build-components/GlobalProvider/globalProviderUtils.ts +204 -0
  56. package/src/build-components/GlobalProvider/pattern.json +55 -0
  57. package/src/build-components/GlobalProvider/useGlobalNavigation.ts +65 -0
  58. package/src/build-components/GlobalProvider/useGlobalProviderLogic.ts +172 -0
  59. package/src/build-components/OnboardButton/OnboardButton.tsx +8 -1
  60. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +26 -3
  61. package/src/build-components/OnboardButton/pattern.json +5 -3
  62. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +12 -0
  63. package/src/build-components/OnboardProvider/pattern.json +9 -1
  64. package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +12 -0
  65. package/src/build-components/PaywallProvider/pattern.json +9 -1
  66. package/src/build-components/RenderNode.generated.tsx +10 -0
  67. package/src/build-components/SystemButton/SystemButton.tsx +6 -0
  68. package/src/build-components/SystemButton/SystemButtonProps.generated.ts +26 -3
  69. package/src/build-components/SystemButton/pattern.json +5 -3
  70. package/src/build-components/SystemButton/usePlacementButtonEvents.ts +51 -27
  71. package/src/build-components/TermsProvider/TermsProvider.tsx +45 -0
  72. package/src/build-components/TermsProvider/TermsProviderProps.generated.ts +82 -0
  73. package/src/build-components/TermsProvider/pattern.json +35 -0
  74. package/src/build-components/index.ts +10 -0
  75. package/src/build-components/patterns.generated.ts +2290 -1399
  76. package/src/components/AttributesEditorPanel.tsx +1 -0
  77. package/src/components/BottomBar.tsx +134 -14
  78. package/src/components/DeviceButton.tsx +81 -22
  79. package/src/components/EditorHeader.tsx +3 -2
  80. package/src/index.web.ts +32 -1
  81. package/src/modals/CreateDeviceModal.tsx +12 -2
  82. package/src/modals/DeviceSelectorModal.tsx +3 -1
  83. package/src/modals/PromptManagerModal.tsx +228 -38
  84. package/src/patterns/event-constants.json +19 -0
  85. package/src/store.ts +85 -0
  86. package/src/styles/components/_bottom-bar.scss +79 -0
  87. package/src/styles/components/_editor-shell.scss +235 -9
  88. package/src/styles/components/_global-provider.scss +131 -0
  89. package/src/styles/index.scss +1 -0
  90. package/src/styles/modals/_device-selector.scss +2 -2
  91. package/src/styles/modals/_prompt-manager-modal.scss +75 -5
  92. package/src/utils/analyseNodeByPatterns.ts +5 -2
  93. package/src/utils/nodeTree.ts +115 -0
  94. package/src/assets/.DS_Store +0 -0
@@ -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 { useRenderStore } from '../store';
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 [activeTab, setActiveTab] = useState<PromptManagerTab>('xml-editor');
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="prompt-manager-modal__content"
65
+ contentClassName={[
66
+ 'prompt-manager-modal__content',
67
+ isAiLayoutMode ? 'prompt-manager-modal__content--ai-layout' : '',
68
+ ]
69
+ .filter(Boolean)
70
+ .join(' ')}
31
71
  >
32
- <div className="modal__header prompt-manager-modal__header">
33
- <h3 id="prompt-manager-modal-title" className="modal__title">
34
- Prompt Manager
35
- </h3>
36
- <button type="button" className="editor-button" onClick={onClose}>
37
- Close
38
- </button>
39
- </div>
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
- <div className="prompt-manager-modal__tabs">
42
- <button
43
- type="button"
44
- className={`prompt-manager-modal__tab${
45
- activeTab === 'xml-editor' ? ' is-active' : ''
46
- }`}
47
- onClick={() => setActiveTab('xml-editor')}
48
- >
49
- XML Editor
50
- </button>
51
- <button
52
- type="button"
53
- className={`prompt-manager-modal__tab${
54
- activeTab === 'prompt-schema' ? ' is-active' : ''
55
- }`}
56
- onClick={() => setActiveTab('prompt-schema')}
57
- >
58
- Prompt Şeması
59
- </button>
60
- </div>
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 className="prompt-manager-modal__body">
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 – Prompt Şeması */
242
+ /* Tab 2 – AI Layout */
181
243
  /* ------------------------------------------------------------------ */
182
244
 
183
- type PromptSchemaSubTab = 'onboard' | 'paywall';
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 {
@@ -0,0 +1,19 @@
1
+ {
2
+ "$comment": "Central event constants. Referenced via $ref in pattern.json types blocks. Run 'yarn prebuild' after changes.",
3
+ "eventTypes": ["Permission", "Navigate", "Placement", "SetCondition"],
4
+ "placementKeys": ["terms", "onboard", "paywall", "subscription", "home"],
5
+ "conditionKeys": ["termsAccepted"],
6
+ "permissionTypes": [
7
+ "notification",
8
+ "camera",
9
+ "microphone",
10
+ "location",
11
+ "photos",
12
+ "contacts",
13
+ "att",
14
+ "rating",
15
+ "GDPR"
16
+ ],
17
+ "validationTypes": ["required", "email", "url", "min", "max"],
18
+ "formFieldNames": ["email", "firstName", "lastName", "phone", "birthday"]
19
+ }
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
+ }