@admin-layout/gluestack-ui-mobile 12.2.4-alpha.4 → 12.2.4-alpha.49
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/CHANGELOG.md +52 -0
- package/lib/components/InputToolBar/InputToolBar.d.ts +8 -0
- package/lib/components/InputToolBar/InputToolBar.js +219 -0
- package/lib/components/InputToolBar/InputToolBar.js.map +1 -0
- package/lib/components/InputToolBar/defaults.d.ts +14 -0
- package/lib/components/InputToolBar/defaults.js +60 -0
- package/lib/components/InputToolBar/defaults.js.map +1 -0
- package/lib/components/InputToolBar/index.d.ts +4 -0
- package/lib/components/InputToolBar/index.js +3 -0
- package/lib/components/InputToolBar/index.js.map +1 -0
- package/lib/components/InputToolBar/types.d.ts +153 -0
- package/lib/components/InputToolBar/types.js +2 -0
- package/lib/components/InputToolBar/types.js.map +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/index.js.map +1 -1
- package/lib/components/usePermissionAutoFetch.js +2 -3
- package/lib/components/usePermissionAutoFetch.js.map +1 -1
- package/lib/containers/layout/DrawerBottomNavigationConfig.d.ts +63 -303
- package/lib/containers/layout/DrawerConfig.d.ts +42 -206
- package/package.json +4 -4
- package/src/components/InputToolBar/InputToolBar.tsx +666 -0
- package/src/components/InputToolBar/README.md +239 -0
- package/src/components/InputToolBar/defaults.ts +72 -0
- package/src/components/InputToolBar/index.ts +18 -0
- package/src/components/InputToolBar/types.ts +166 -0
- package/src/components/index.ts +1 -0
- package/src/components/usePermissionAutoFetch.tsx +4 -4
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# InputToolBar (Mobile)
|
|
2
|
+
|
|
3
|
+
A **UI-only** horizontal toolbar for text-input UIs on mobile (e.g. chat, composer, search). Built with gluestack-ui and NativeWind. All data and behavior come from props; the component does not know about modes, methods, or app logic.
|
|
4
|
+
|
|
5
|
+
## Layout
|
|
6
|
+
|
|
7
|
+
When `inputConfig` or `children` is provided:
|
|
8
|
+
|
|
9
|
+
- **First row**: Optional `topContent`, then built-in TextInput (if `inputConfig` is set) or `children`.
|
|
10
|
+
- **Second row**: Left section (`leftItems` + optional `templateButton`) and right section (`rightItems` + optional `micSendButton`).
|
|
11
|
+
|
|
12
|
+
When neither is provided, a single toolbar row is rendered. Parent passes `leftItems` and `rightItems` with full config (e.g. `active`, `onClick`, `enabled`) and optional helpers `getDefaultLeftItems` / `getDefaultRightItems`.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
The component is part of `@admin-layout/gluestack-ui-mobile`:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { InputToolBar, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
All data and behavior are passed from the parent. No internal mode or method logic.
|
|
25
|
+
|
|
26
|
+
| Prop | Type | Description |
|
|
27
|
+
| ----------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `className` | `string` | Optional class for the toolbar container (merged with `classNames.container`). |
|
|
29
|
+
| `classNames` | `InputToolBarClassNames` | Optional class overrides per section for theme (light/dark) or custom styling. |
|
|
30
|
+
| `inputConfig` | `InputConfig \| null` | When set, a built-in TextInput is rendered; pass value, onChange, etc. |
|
|
31
|
+
| `topContent` | `ReactNode` | Rendered above the input when `inputConfig` is set. |
|
|
32
|
+
| `leftItems` | `ToolbarItemConfig<LeftToolbarItemId>[]` | Left section items (active, onClick, etc. from parent). |
|
|
33
|
+
| `rightItems` | `ToolbarItemConfig<RightToolbarItemId>[]` | Right section items (onClick, etc. from parent). |
|
|
34
|
+
| `templateButton` | `TemplateButtonConfig \| null` | Optional "+ Template (count)" pill; data from parent. |
|
|
35
|
+
| `leftCustomRender` | `ReactNode` | Custom left content (overrides `leftItems`). |
|
|
36
|
+
| `rightCustomRender` | `ReactNode` | Custom right content (overrides `rightItems` and `micSendButton`). |
|
|
37
|
+
| `micSendButton` | `MicSendButtonConfig \| null` | Rightmost button (Stop/Send/Mic); all data and handlers from parent. |
|
|
38
|
+
| `projectSettingsModalOpen` | `boolean` | When true, the project settings modal (from `projectSettingsModalRender`) is shown. Set by parent when projectSettings is clicked. |
|
|
39
|
+
| `onProjectSettingsModalClose` | `() => void` | Called when the project settings modal should close (overlay/close button). |
|
|
40
|
+
| `projectSettingsModalRender` | `(props: { onClose }) => ReactNode \| null` | Renders the project settings modal content (e.g. Configuration / Other Settings / Secret). Parent passes config and onChange so values update via parent (same pattern as ModelConfigPanel). |
|
|
41
|
+
| `children` | `ReactNode` | Optional middle content when not using `inputConfig`. |
|
|
42
|
+
| `onContainerClick` | `(e) => void` | Called when the container is clicked (e.g. to focus input). |
|
|
43
|
+
|
|
44
|
+
## Theme and class overrides
|
|
45
|
+
|
|
46
|
+
Default styles use gluestack-ui tokens (`$primary500`, `$background100`, `$outline200`, etc.) so the toolbar works in both **light and dark** themes. Buttons and the template pill use borders and backgrounds that stay visible in either mode.
|
|
47
|
+
|
|
48
|
+
To override from outside, pass **`classNames`**:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
interface InputToolBarClassNames {
|
|
52
|
+
container?: string; // root section
|
|
53
|
+
leftGroup?: string; // left section wrapper
|
|
54
|
+
templatePill?: string; // "+ Template (N)" button
|
|
55
|
+
templatePillSelected?: string; // selected/suggested pill
|
|
56
|
+
leftButtonActive?: string; // left mode button – active
|
|
57
|
+
leftButtonInactive?: string; // left mode button – inactive
|
|
58
|
+
rightButton?: string; // right toolbar button – default
|
|
59
|
+
rightButtonActive?: string; // right toolbar button – active
|
|
60
|
+
rightButtonDisabled?: string; // right toolbar button – disabled
|
|
61
|
+
micSendButton?: string; // primary mic/send button
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Types
|
|
66
|
+
|
|
67
|
+
### ToolbarItemConfig
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
interface ToolbarItemConfig<TId> {
|
|
71
|
+
id: TId;
|
|
72
|
+
label: string;
|
|
73
|
+
icon?: ReactNode;
|
|
74
|
+
enabled?: boolean; // default true; when false, the item is not rendered and takes no space
|
|
75
|
+
disabled?: boolean;
|
|
76
|
+
loading?: boolean;
|
|
77
|
+
active?: boolean; // dark/primary style when true
|
|
78
|
+
onClick?: () => void;
|
|
79
|
+
customButton?: ReactNode; // use instead of icon when provided
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### LeftToolbarItemId
|
|
84
|
+
|
|
85
|
+
`'search' | 'zap' | 'lightbulb' | 'template'`
|
|
86
|
+
|
|
87
|
+
### RightToolbarItemId
|
|
88
|
+
|
|
89
|
+
`'projectSettings' | 'tag' | 'chip' | 'camera' | 'image' | 'attach' | 'mic'`
|
|
90
|
+
|
|
91
|
+
- **projectSettings** – Project settings (gear icon). **Disabled by default.** Enable with `getDefaultRightItems({ projectSettings: { enabled: true, onClick: () => setProjectSettingsModalOpen(true) } })`.
|
|
92
|
+
- **tag** – Enhance prompt with AI
|
|
93
|
+
- **chip** – Configure chat model
|
|
94
|
+
- **camera** – Capture image / screenshot
|
|
95
|
+
- **image** – Insert image / picture
|
|
96
|
+
- **attach** – Attach file (paperclip)
|
|
97
|
+
- **mic** – Mic (or replaced by micSendButton)
|
|
98
|
+
|
|
99
|
+
### InputConfig (React Native)
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
interface InputConfig {
|
|
103
|
+
value: string;
|
|
104
|
+
onChange: (e: NativeSyntheticEvent<TextInputChangeEventData>) => void;
|
|
105
|
+
placeholder?: string;
|
|
106
|
+
disabled?: boolean;
|
|
107
|
+
onKeyDown?: (e: any) => void;
|
|
108
|
+
onPaste?: (e: any) => void;
|
|
109
|
+
id?: string;
|
|
110
|
+
name?: string;
|
|
111
|
+
inputRef?: React.RefObject<any>;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
> **Note:** Unlike the web version, `onChange` receives a React Native event. Use `e.nativeEvent.text` to get the value.
|
|
116
|
+
|
|
117
|
+
### MicSendButtonConfig
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
interface MicSendButtonConfig {
|
|
121
|
+
hasContent: boolean;
|
|
122
|
+
onSend: () => void;
|
|
123
|
+
onMic: () => void;
|
|
124
|
+
disabled?: boolean;
|
|
125
|
+
isLoading?: boolean; // when true, show Stop icon and call onStop
|
|
126
|
+
onStop?: () => void;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Template visibility (build mode)
|
|
131
|
+
|
|
132
|
+
The template button should only be visible when the user is in **build mode** (lightbulb/workflow). The parent controls this by passing `templateButton` only when `activeMode === 'workflow'`:
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
templateButton={
|
|
136
|
+
activeMode === 'workflow'
|
|
137
|
+
? { label: '+ Template', count: templates.length, onClick: openTemplateModal }
|
|
138
|
+
: null
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### TemplateButtonConfig
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
interface TemplateButtonConfig {
|
|
146
|
+
label: string; // e.g. "+ Template" when no selection
|
|
147
|
+
count?: number; // e.g. (1)
|
|
148
|
+
selectedLabel?: string | null; // when set, show selected template pill (name + remove + change)
|
|
149
|
+
onClick?: () => void; // open modal; also "change" when selected
|
|
150
|
+
onClearTemplate?: () => void; // when selected, called on remove (X)
|
|
151
|
+
disabled?: boolean;
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Template selection modal
|
|
156
|
+
|
|
157
|
+
When you pass **`templateModalConfig`**, InputToolBar renders a built-in modal with search, category filters, and a grid of template items. Each item needs: `id`, `label`, optional `description`, optional `category` (`TemplateModalItem`). Pass **`templateModalRender`** to use your own custom modal UI.
|
|
158
|
+
|
|
159
|
+
## Helpers
|
|
160
|
+
|
|
161
|
+
- **`getDefaultLeftItems(overrides?)`** – Returns default left items (search, zap, lightbulb). Override per id: `label`, `enabled`, `active`, `onClick`, `icon`, `customButton`.
|
|
162
|
+
- **`getDefaultRightItems(overrides?)`** – Returns default right items (tag, chip, camera, image, attach, mic). Override per id (e.g. `image: { enabled: false }` or `image: { onClick: handleInsertImage }`).
|
|
163
|
+
|
|
164
|
+
When using `micSendButton`, the parent typically omits `mic` from `rightItems` (e.g. `mic: { enabled: false }` in overrides) so the mic/send button is the rightmost action.
|
|
165
|
+
|
|
166
|
+
## Examples
|
|
167
|
+
|
|
168
|
+
### Basic usage with input
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
import { InputToolBar, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';
|
|
172
|
+
|
|
173
|
+
const [value, setValue] = useState('');
|
|
174
|
+
const textareaRef = useRef<any>(null);
|
|
175
|
+
|
|
176
|
+
const leftItems = getDefaultLeftItems({
|
|
177
|
+
search: { active: activeMode === 'chat', onClick: () => setMode('chat') },
|
|
178
|
+
zap: { active: activeMode === 'deep-search', onClick: () => setMode('deep-search') },
|
|
179
|
+
lightbulb: { active: activeMode === 'workflow', onClick: () => setMode('workflow') },
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const rightItems = getDefaultRightItems({
|
|
183
|
+
tag: { enabled: false },
|
|
184
|
+
chip: { enabled: false },
|
|
185
|
+
camera: { onClick: handleCaptureImage },
|
|
186
|
+
attach: { onClick: handleAttachFile },
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
<InputToolBar
|
|
190
|
+
inputConfig={{
|
|
191
|
+
value,
|
|
192
|
+
onChange: (e) => setValue(e.nativeEvent.text),
|
|
193
|
+
placeholder: 'Ask anything...',
|
|
194
|
+
disabled: isLoading,
|
|
195
|
+
inputRef: textareaRef,
|
|
196
|
+
}}
|
|
197
|
+
topContent={<>… panels / banners …</>}
|
|
198
|
+
leftItems={leftItems}
|
|
199
|
+
rightItems={rightItems}
|
|
200
|
+
templateButton={{
|
|
201
|
+
label: '+ Template',
|
|
202
|
+
count: templates.length,
|
|
203
|
+
onClick: openTemplateModal,
|
|
204
|
+
}}
|
|
205
|
+
micSendButton={{
|
|
206
|
+
hasContent: value.trim().length > 0,
|
|
207
|
+
onSend: handleSend,
|
|
208
|
+
onMic: handleMic,
|
|
209
|
+
disabled,
|
|
210
|
+
isLoading,
|
|
211
|
+
onStop,
|
|
212
|
+
}}
|
|
213
|
+
onContainerClick={() => textareaRef.current?.focus()}
|
|
214
|
+
/>;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Custom left/right content
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<InputToolBar leftCustomRender={<YourLeftButtons />} rightCustomRender={<YourRightButtons />}>
|
|
221
|
+
<TextInput placeholder="Message" />
|
|
222
|
+
</InputToolBar>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Mic / Send behavior
|
|
226
|
+
|
|
227
|
+
When `micSendButton` is provided:
|
|
228
|
+
|
|
229
|
+
- **Empty input** (`hasContent === false`): rightmost button shows the **Mic** icon and calls `onMic` on click.
|
|
230
|
+
- **Input has text** (`hasContent === true`): rightmost button shows the **Send** (arrow) icon and calls `onSend` on click.
|
|
231
|
+
|
|
232
|
+
The button uses the primary style in both states. Any item with `id === 'mic'` in `rightItems` is not rendered when `micSendButton` is set.
|
|
233
|
+
|
|
234
|
+
## Platform notes
|
|
235
|
+
|
|
236
|
+
- Built with **@gluestack-ui/themed** (Box, HStack, VStack, Text, Pressable, etc.)
|
|
237
|
+
- Uses **React Native** TextInput, Modal, ScrollView, Pressable
|
|
238
|
+
- Icons from **@expo/vector-icons** (Ionicons)
|
|
239
|
+
- Supports **NativeWind** for Tailwind styling where needed (e.g. `className` on RN Text)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { LeftToolbarItemId, RightToolbarItemId, ToolbarItemConfig } from './types';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_LEFT_IDS: LeftToolbarItemId[] = ['search', 'zap', 'lightbulb'];
|
|
4
|
+
const DEFAULT_RIGHT_IDS: RightToolbarItemId[] = ['projectSettings', 'tag', 'chip', 'camera', 'image', 'attach', 'mic'];
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LEFT_LABELS: Record<LeftToolbarItemId, string> = {
|
|
7
|
+
search: 'Chat mode',
|
|
8
|
+
zap: 'Deep Search',
|
|
9
|
+
lightbulb: 'Build mode',
|
|
10
|
+
template: 'Template',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const DEFAULT_RIGHT_LABELS: Record<RightToolbarItemId, string> = {
|
|
14
|
+
projectSettings: 'Project settings',
|
|
15
|
+
tag: 'Enhance your prompt with AI',
|
|
16
|
+
chip: 'Configure chat model',
|
|
17
|
+
camera: 'Capture image',
|
|
18
|
+
image: 'Insert image',
|
|
19
|
+
attach: 'Attach file',
|
|
20
|
+
mic: 'Mic / Send',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type LeftItemOverrides = Partial<
|
|
24
|
+
Record<LeftToolbarItemId, Partial<Omit<ToolbarItemConfig<LeftToolbarItemId>, 'id'>>>
|
|
25
|
+
>;
|
|
26
|
+
export type RightItemOverrides = Partial<
|
|
27
|
+
Record<RightToolbarItemId, Partial<Omit<ToolbarItemConfig<RightToolbarItemId>, 'id'>>>
|
|
28
|
+
>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build default left toolbar items (search, zap, lightbulb). Omit or set enabled: false to hide.
|
|
32
|
+
* Override label, enabled, active, onClick, icon, customButton per id.
|
|
33
|
+
*/
|
|
34
|
+
export function getDefaultLeftItems(overrides?: LeftItemOverrides): ToolbarItemConfig<LeftToolbarItemId>[] {
|
|
35
|
+
return DEFAULT_LEFT_IDS.map((id) => {
|
|
36
|
+
const o = overrides?.[id];
|
|
37
|
+
return {
|
|
38
|
+
id,
|
|
39
|
+
label: o?.label ?? DEFAULT_LEFT_LABELS[id],
|
|
40
|
+
enabled: o?.enabled ?? true,
|
|
41
|
+
disabled: o?.disabled,
|
|
42
|
+
loading: o?.loading,
|
|
43
|
+
active: o?.active,
|
|
44
|
+
onClick: o?.onClick,
|
|
45
|
+
icon: o?.icon,
|
|
46
|
+
customButton: o?.customButton,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Build default right toolbar items (projectSettings, tag, chip, camera, image, attach, mic).
|
|
53
|
+
* projectSettings is disabled by default; pass projectSettings: { enabled: true, onClick } to show.
|
|
54
|
+
* Override label, enabled, disabled, loading, active, onClick, icon, customButton per id.
|
|
55
|
+
*/
|
|
56
|
+
export function getDefaultRightItems(overrides?: RightItemOverrides): ToolbarItemConfig<RightToolbarItemId>[] {
|
|
57
|
+
return DEFAULT_RIGHT_IDS.map((id) => {
|
|
58
|
+
const o = overrides?.[id];
|
|
59
|
+
const defaultEnabled = id === 'projectSettings' ? false : true;
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
label: o?.label ?? DEFAULT_RIGHT_LABELS[id],
|
|
63
|
+
enabled: o?.enabled ?? defaultEnabled,
|
|
64
|
+
disabled: o?.disabled,
|
|
65
|
+
loading: o?.loading,
|
|
66
|
+
active: o?.active,
|
|
67
|
+
onClick: o?.onClick,
|
|
68
|
+
icon: o?.icon,
|
|
69
|
+
customButton: o?.customButton,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { InputToolBar } from './InputToolBar';
|
|
2
|
+
export { getDefaultLeftItems, getDefaultRightItems } from './defaults';
|
|
3
|
+
export type {
|
|
4
|
+
InputConfig,
|
|
5
|
+
InputToolBarClassNames,
|
|
6
|
+
InputToolBarProps,
|
|
7
|
+
LeftToolbarItemId,
|
|
8
|
+
MicSendButtonConfig,
|
|
9
|
+
ProjectSettingsModalRender,
|
|
10
|
+
ProjectSettingsModalRenderProps,
|
|
11
|
+
RightToolbarItemId,
|
|
12
|
+
TemplateButtonConfig,
|
|
13
|
+
TemplateModalConfig,
|
|
14
|
+
TemplateModalItem,
|
|
15
|
+
TemplateModalRender,
|
|
16
|
+
ToolbarItemConfig,
|
|
17
|
+
} from './types';
|
|
18
|
+
export type { LeftItemOverrides, RightItemOverrides } from './defaults';
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { NativeSyntheticEvent, TextInputChangeEventData } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/** Data-only config for built-in textarea. When provided, InputToolBar renders the textarea with default styling. */
|
|
5
|
+
export interface InputConfig {
|
|
6
|
+
value: string;
|
|
7
|
+
onChange: (e: NativeSyntheticEvent<TextInputChangeEventData>) => void;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
onKeyDown?: (e: any) => void;
|
|
11
|
+
onPaste?: (e: any) => void;
|
|
12
|
+
id?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
/** Optional ref for the textarea (e.g. for focus / height adjustment) */
|
|
15
|
+
inputRef?: React.RefObject<any>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Predefined toolbar action ids for left section (mode toggles, template) */
|
|
19
|
+
export type LeftToolbarItemId = 'search' | 'zap' | 'lightbulb' | 'template';
|
|
20
|
+
|
|
21
|
+
/** Predefined toolbar action ids for right section. projectSettings = first item, disabled by default; enable via overrides. tag = enhance prompt, chip = configure model; both support enabled/disable and custom label, icon, onClick for app-specific use. */
|
|
22
|
+
export type RightToolbarItemId = 'projectSettings' | 'tag' | 'chip' | 'camera' | 'image' | 'attach' | 'mic';
|
|
23
|
+
|
|
24
|
+
/** Single toolbar button/item configuration */
|
|
25
|
+
export interface ToolbarItemConfig<TId extends string = string> {
|
|
26
|
+
/** Unique id for the item (used for enable/disable and callbacks) */
|
|
27
|
+
id: TId;
|
|
28
|
+
/** Icon (React node) or custom content for the button */
|
|
29
|
+
icon?: ReactNode;
|
|
30
|
+
/** Accessible label / title (e.g. "Enhance your prompt with AI" when enabled, "Type something to enhance" when disabled) */
|
|
31
|
+
label: string;
|
|
32
|
+
/** Whether the item is enabled (default true). When false, item is hidden. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
/** When true, button is visible but disabled (greyed out, not clickable). Use for e.g. "enhance" when input is empty. */
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
/** When true, show loading spinner and treat as disabled. Use for e.g. "enhance" while request is in flight. */
|
|
37
|
+
loading?: boolean;
|
|
38
|
+
/** Whether the item is in active/selected state (e.g. primary color + hover style for right items) */
|
|
39
|
+
active?: boolean;
|
|
40
|
+
/** Click handler */
|
|
41
|
+
onClick?: () => void;
|
|
42
|
+
/** Optional custom button element; when set, icon/label are ignored and this is rendered */
|
|
43
|
+
customButton?: ReactNode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** When provided, the rightmost button shows Stop when loading, Send when hasContent, else Mic. Replaces any "mic" item in rightItems. */
|
|
47
|
+
export interface MicSendButtonConfig {
|
|
48
|
+
/** True when input has content – show send icon; otherwise show mic icon (unless loading). */
|
|
49
|
+
hasContent: boolean;
|
|
50
|
+
onSend: () => void;
|
|
51
|
+
onMic: () => void;
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
/** When true, show stop icon and call onStop on click. */
|
|
54
|
+
isLoading?: boolean;
|
|
55
|
+
onStop?: () => void;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Configuration for the optional template pill: either "+ Template (N)" or selected template name with remove/change */
|
|
59
|
+
export interface TemplateButtonConfig {
|
|
60
|
+
/** Button label prefix when no selection, e.g. "+ Template" */
|
|
61
|
+
label: string;
|
|
62
|
+
/** Count to show in parentheses when no selection, e.g. (1) */
|
|
63
|
+
count?: number;
|
|
64
|
+
/** When set, show selected template pill (name + remove + change) instead of "+ Template (N)" */
|
|
65
|
+
selectedLabel?: string | null;
|
|
66
|
+
/** Open template modal / change template (used for main pill click and for "change" when selected) */
|
|
67
|
+
onClick?: () => void;
|
|
68
|
+
/** When template is selected, called when user clicks remove (X) */
|
|
69
|
+
onClearTemplate?: () => void;
|
|
70
|
+
/** Disabled state */
|
|
71
|
+
disabled?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Minimal template item for the default template selection modal. Map your app's template type to this shape. */
|
|
75
|
+
export interface TemplateModalItem {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
category?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Config for the template selection modal. Pass data from props; InputToolBar renders default modal or custom via templateModalRender. */
|
|
83
|
+
export interface TemplateModalConfig {
|
|
84
|
+
isOpen: boolean;
|
|
85
|
+
onClose: () => void;
|
|
86
|
+
templates: TemplateModalItem[];
|
|
87
|
+
selectedId: string | null;
|
|
88
|
+
onSelect: (id: string) => void;
|
|
89
|
+
suggestedId?: string | null;
|
|
90
|
+
title?: string;
|
|
91
|
+
/** Optional class for the modal content box (inner container). Use to standardize width/size, e.g. `max-w-md` or `max-w-xl`. */
|
|
92
|
+
modalClassName?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Render prop for custom template modal. When provided, used instead of the default modal UI. */
|
|
96
|
+
export type TemplateModalRender = (config: TemplateModalConfig) => ReactNode;
|
|
97
|
+
|
|
98
|
+
/** Props passed to the project settings modal render function. Values are updated via the parent's callback (e.g. onModelConfigChange). */
|
|
99
|
+
export interface ProjectSettingsModalRenderProps {
|
|
100
|
+
onClose: () => void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Render prop for project settings modal content. When projectSettings is clicked, parent opens modal (sets projectSettingsModalOpen). Modal content receives onClose; parent passes config and onChange from props so values update via parent. */
|
|
104
|
+
export type ProjectSettingsModalRender = (props: ProjectSettingsModalRenderProps) => ReactNode;
|
|
105
|
+
|
|
106
|
+
/** Optional class overrides for InputToolBar sections. Use to adapt to light/dark theme or custom styling. */
|
|
107
|
+
export interface InputToolBarClassNames {
|
|
108
|
+
/** Root section (default: rounded border bg-card shadow) */
|
|
109
|
+
container?: string;
|
|
110
|
+
/** Left toolbar group wrapper */
|
|
111
|
+
leftGroup?: string;
|
|
112
|
+
/** Template pill when no selection (e.g. "+ Template (N)") */
|
|
113
|
+
templatePill?: string;
|
|
114
|
+
/** Template pill when selected/suggested (name + remove + change) */
|
|
115
|
+
templatePillSelected?: string;
|
|
116
|
+
/** Left mode buttons – active state (filled) */
|
|
117
|
+
leftButtonActive?: string;
|
|
118
|
+
/** Left mode buttons – inactive state (outline) */
|
|
119
|
+
leftButtonInactive?: string;
|
|
120
|
+
/** Right toolbar buttons – default state */
|
|
121
|
+
rightButton?: string;
|
|
122
|
+
/** Right toolbar buttons – active state */
|
|
123
|
+
rightButtonActive?: string;
|
|
124
|
+
/** Right toolbar buttons – disabled state */
|
|
125
|
+
rightButtonDisabled?: string;
|
|
126
|
+
/** Mic/Send button (primary action) */
|
|
127
|
+
micSendButton?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Props for the InputToolBar container. UI only: all data and behavior come from props. */
|
|
131
|
+
export interface InputToolBarProps {
|
|
132
|
+
/** Optional class name for the toolbar container (merged with container in classNames) */
|
|
133
|
+
className?: string;
|
|
134
|
+
/** Optional class overrides for toolbar sections (works in both light and dark theme) */
|
|
135
|
+
classNames?: InputToolBarClassNames;
|
|
136
|
+
/** When provided, InputToolBar renders a built-in textarea; pass only data (value, onChange, etc.). */
|
|
137
|
+
inputConfig?: InputConfig | null;
|
|
138
|
+
/** Optional content above the input when inputConfig is set (e.g. panels, banners). */
|
|
139
|
+
topContent?: ReactNode;
|
|
140
|
+
/** Left section: items and template pill. All data (active, onClick, etc.) from parent. */
|
|
141
|
+
leftItems?: Array<ToolbarItemConfig<LeftToolbarItemId>>;
|
|
142
|
+
/** Right section: items. All data (onClick, etc.) from parent. */
|
|
143
|
+
rightItems?: Array<ToolbarItemConfig<RightToolbarItemId>>;
|
|
144
|
+
/** Optional template pill (e.g. "+ Template (1)"). Data from parent. */
|
|
145
|
+
templateButton?: TemplateButtonConfig | null;
|
|
146
|
+
/** Template selection modal: pass data from props. When set, InputToolBar renders the modal (default UI or custom). */
|
|
147
|
+
templateModalConfig?: TemplateModalConfig | null;
|
|
148
|
+
/** When provided, used instead of the default template modal UI. Receives templateModalConfig. */
|
|
149
|
+
templateModalRender?: TemplateModalRender | null;
|
|
150
|
+
/** Custom content for the left section (overrides leftItems) */
|
|
151
|
+
leftCustomRender?: ReactNode;
|
|
152
|
+
/** Custom content for the right section (overrides rightItems and micSendButton) */
|
|
153
|
+
rightCustomRender?: ReactNode;
|
|
154
|
+
/** When set, rightmost button: Stop/Send/Mic; all data and handlers from parent. */
|
|
155
|
+
micSendButton?: MicSendButtonConfig | null;
|
|
156
|
+
/** When true, the project settings modal (from projectSettingsModalRender) is shown. Set by parent when projectSettings is clicked. */
|
|
157
|
+
projectSettingsModalOpen?: boolean;
|
|
158
|
+
/** Called when the project settings modal should close (e.g. overlay click or close button). */
|
|
159
|
+
onProjectSettingsModalClose?: () => void;
|
|
160
|
+
/** Renders the project settings modal content (e.g. Configuration / Other Settings / Secret tabs). Parent passes config and onChange so values update via parent (same pattern as ModelConfigPanel). */
|
|
161
|
+
projectSettingsModalRender?: ProjectSettingsModalRender | null;
|
|
162
|
+
/** Optional content in the middle when not using inputConfig */
|
|
163
|
+
children?: ReactNode;
|
|
164
|
+
/** Click on container (e.g. to focus input) */
|
|
165
|
+
onContainerClick?: (e: any) => void;
|
|
166
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -2,21 +2,21 @@ import { ResourceAuthority, ConfigFragmentName, IContext } from 'common';
|
|
|
2
2
|
import { generateUserUri } from '@adminide-stack/core';
|
|
3
3
|
import { useGetContextDataQuery } from 'common/lib/generated/generated.js';
|
|
4
4
|
import { useSetting } from './useSetting';
|
|
5
|
+
import type { URI } from '@vscode-alt/monaco-editor/esm/vs/base/common/uri.js';
|
|
5
6
|
|
|
6
7
|
interface userPermissionAutoFetchProps {
|
|
7
8
|
configKey?: string;
|
|
8
9
|
}
|
|
9
10
|
export const usePermissionAutoFetch = (options?: userPermissionAutoFetchProps) => {
|
|
10
11
|
const { data, loading } = useGetContextDataQuery();
|
|
11
|
-
const {
|
|
12
|
+
const { orgResource, userId } = (data?.getContextData as IContext) || {};
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
let resource = orgUri || (userId ? generateUserUri({ _id: userId }) : generateUserUri({ _id: 'guest' }));
|
|
14
|
+
let resource = orgResource || (userId ? generateUserUri({ _id: userId }) : generateUserUri({ _id: 'guest' }));
|
|
15
15
|
|
|
16
16
|
const { loading: settingLoading, ...remaining } = useSetting({
|
|
17
17
|
configKey: options?.configKey || '',
|
|
18
18
|
overrides: {
|
|
19
|
-
resource: resource,
|
|
19
|
+
resource: resource as unknown as URI,
|
|
20
20
|
},
|
|
21
21
|
options: {
|
|
22
22
|
forceExist: false,
|