diff --git a/frontend/src/components/ContextMenu.tsx b/frontend/src/components/ContextMenu.tsx index 762a7828..3713ffe6 100644 --- a/frontend/src/components/ContextMenu.tsx +++ b/frontend/src/components/ContextMenu.tsx @@ -30,7 +30,17 @@ export default function ContextMenu({ offset = { x: 0, y: 8 }, }: ContextMenuProps) { const menuRef = useRef(null); - + useEffect(() => { + if (isOpen && menuRef.current) { + const positionStyle = getMenuPosition(); + if (menuRef.current) { + Object.assign(menuRef.current.style, { + top: positionStyle.top, + left: positionStyle.left, + }); + } + } + }, [isOpen]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( @@ -61,20 +71,45 @@ export default function ContextMenu({ let top = rect.bottom + scrollY + offset.y; let left = rect.right + scrollX + offset.x; + // Get menu dimensions (need ref to be available) + const menuWidth = menuRef.current?.offsetWidth || 144; // Default min-width + const menuHeight = menuRef.current?.offsetHeight || 0; + + // Get viewport dimensions + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + + // Adjust position based on specified position switch (position) { case 'bottom-left': left = rect.left + scrollX - offset.x; break; case 'top-right': - top = rect.top + scrollY - offset.y; + top = rect.top + scrollY - offset.y - menuHeight; break; case 'top-left': - top = rect.top + scrollY - offset.y; + top = rect.top + scrollY - offset.y - menuHeight; left = rect.left + scrollX - offset.x; break; // bottom-right is default } + if (left + menuWidth > viewportWidth) { + left = Math.max(5, viewportWidth - menuWidth - 5); + } + + if (left < 5) { + left = 5; + } + + if (top + menuHeight > viewportHeight + scrollY) { + top = rect.top + scrollY - menuHeight - offset.y; + } + + if (top < scrollY + 5) { + top = rect.bottom + scrollY + offset.y; + } + return { position: 'fixed', top: `${top}px`, @@ -90,7 +125,7 @@ export default function ContextMenu({ onClick={(e) => e.stopPropagation()} >
{options.map((option, index) => ( @@ -109,7 +144,7 @@ export default function ContextMenu({ } `} > {option.icon && ( -
+
)} - {option.label} + {option.label} ))}
diff --git a/frontend/src/components/Input.tsx b/frontend/src/components/Input.tsx index a968511c..022c1632 100644 --- a/frontend/src/components/Input.tsx +++ b/frontend/src/components/Input.tsx @@ -60,7 +60,11 @@ const Input = ({ {placeholder && (
-

Back to all documents

+

{t('settings.documents.backToAll')}

-

{`${totalChunks} Chunks`}

+

{`${totalChunks} ${t('settings.documents.chunks')}`}

@@ -663,7 +663,7 @@ function DocumentChunks({ />
-
+ )} + + {tool.name === 'api_tool' ? ( + <> + {tool.config.actions && + Object.keys(tool.config.actions).length > 0 ? ( + + ) : ( +
+ No actions found +

+ {t('settings.tools.noActionsFound')} +

+
+ )} ) : (
- {'actions' in tool && - tool.actions.map((action, actionIndex) => { - return ( -
-
-

- {action.name} -

- { - setTool({ - ...tool, - actions: tool.actions.map((act, index) => { - if (index === actionIndex) { - return { ...act, active: checked }; - } - return act; - }), - }); - }} - size="small" - id={`actionToggle-${actionIndex}`} - /> -
-
- { - setTool({ - ...tool, - actions: tool.actions.map((act, index) => { - if (index === actionIndex) { - return { - ...act, - description: e.target.value, - }; - } - return act; - }), - }); - }} - borderVariant="thin" - /> -
-
- - - - - - - - - - - - {Object.entries(action.parameters?.properties).map( - (param, index) => { - const uniqueKey = `${actionIndex}-${param[0]}`; - return ( - - - - - - - - ); - }, - )} - -
Field NameField TypeFilled by LLMFIeld descriptionValue
{param[0]}{param[1].type} - - - { - setTool({ - ...tool, - actions: tool.actions.map( - (act, index) => { - if (index === actionIndex) { - return { - ...act, - parameters: { - ...act.parameters, - properties: { - ...act.parameters - .properties, - [param[0]]: { - ...act.parameters - .properties[param[0]], - description: - e.target.value, - }, - }, - }, - }; - } - return act; - }, - ), - }); - }} - > - - { - setTool({ - ...tool, - actions: tool.actions.map( - (act, index) => { - if (index === actionIndex) { - return { - ...act, - parameters: { - ...act.parameters, - properties: { - ...act.parameters - .properties, - [param[0]]: { - ...act.parameters - .properties[param[0]], - value: e.target.value, - }, - }, - }, - }; - } - return act; - }, - ), - }); - }} - > -
-
+ {'actions' in tool && tool.actions && tool.actions.length > 0 ? ( + tool.actions.map((action, actionIndex) => ( +
+
+

+ {action.name} +

+ { + setTool({ + ...tool, + actions: tool.actions.map((act, index) => { + if (index === actionIndex) { + return { ...act, active: checked }; + } + return act; + }), + }); + }} + size="small" + id={`actionToggle-${actionIndex}`} + />
- ); - })} +
+ { + setTool({ + ...tool, + actions: tool.actions.map((act, index) => { + if (index === actionIndex) { + return { + ...act, + description: e.target.value, + }; + } + return act; + }), + }); + }} + borderVariant="thin" + /> +
+
+ + + + + + + + + + + + {Object.entries(action.parameters?.properties).map( + (param, index) => { + const uniqueKey = `${actionIndex}-${param[0]}`; + return ( + + + + + + + + ); + }, + )} + +
Field NameField TypeFilled by LLMFIeld descriptionValue
{param[0]}{param[1].type} + + + { + setTool({ + ...tool, + actions: tool.actions.map( + (act, index) => { + if (index === actionIndex) { + return { + ...act, + parameters: { + ...act.parameters, + properties: { + ...act.parameters + .properties, + [param[0]]: { + ...act.parameters + .properties[param[0]], + description: + e.target.value, + }, + }, + }, + }; + } + return act; + }, + ), + }); + }} + > + + { + setTool({ + ...tool, + actions: tool.actions.map( + (act, index) => { + if (index === actionIndex) { + return { + ...act, + parameters: { + ...act.parameters, + properties: { + ...act.parameters + .properties, + [param[0]]: { + ...act.parameters + .properties[param[0]], + value: e.target.value, + }, + }, + }, + }; + } + return act; + }, + ), + }); + }} + > +
+
+
+ )) + ) : ( +
+ No actions found +

+ {t('settings.tools.noActionsFound')} +

+
+ )}
)} {showUnsavedModal && ( setShowUnsavedModal(state === 'ACTIVE')} - submitLabel={t('settings.tools.saveAndLeave', { - defaultValue: 'Save and Leave', - })} + submitLabel={t('settings.tools.saveAndLeave')} handleSubmit={() => { userService .updateTool( @@ -447,9 +472,7 @@ export default function ToolConfig({ handleGoBack(); }); }} - cancelLabel={t('settings.tools.leaveWithoutSaving', { - defaultValue: 'Leave without Saving', - })} + cancelLabel={t('settings.tools.leaveWithoutSaving')} handleCancel={() => { setShowUnsavedModal(false); handleGoBack(); @@ -567,36 +590,31 @@ function APIToolConfig({
-
- - URL - - { - setApiTool((prevApiTool) => { - const updatedActions = { - ...prevApiTool.config.actions, - }; - const updatedAction = { - ...updatedActions[actionName], - }; - updatedAction.url = e.target.value; - updatedActions[actionName] = updatedAction; - return { - ...prevApiTool, - config: { - ...prevApiTool.config, - actions: updatedActions, - }, - }; - }); - }} - borderVariant="thin" - placeholder="Enter url" - > -
+ { + setApiTool((prevApiTool) => { + const updatedActions = { + ...prevApiTool.config.actions, + }; + const updatedAction = { + ...updatedActions[actionName], + }; + updatedAction.url = e.target.value; + updatedActions[actionName] = updatedAction; + return { + ...prevApiTool, + config: { + ...prevApiTool.config, + actions: updatedActions, + }, + }; + }); + }} + borderVariant="thin" + placeholder={t('settings.tools.urlPlaceholder')} + />
@@ -636,36 +654,31 @@ function APIToolConfig({
-
- - Description - - { - setApiTool((prevApiTool) => { - const updatedActions = { - ...prevApiTool.config.actions, - }; - const updatedAction = { - ...updatedActions[actionName], - }; - updatedAction.description = e.target.value; - updatedActions[actionName] = updatedAction; - return { - ...prevApiTool, - config: { - ...prevApiTool.config, - actions: updatedActions, - }, - }; - }); - }} - borderVariant="thin" - placeholder="Enter description" - > -
+ { + setApiTool((prevApiTool) => { + const updatedActions = { + ...prevApiTool.config.actions, + }; + const updatedAction = { + ...updatedActions[actionName], + }; + updatedAction.description = e.target.value; + updatedActions[actionName] = updatedAction; + return { + ...prevApiTool, + config: { + ...prevApiTool.config, + actions: updatedActions, + }, + }; + }); + }} + borderVariant="thin" + placeholder={t('settings.tools.descriptionPlaceholder')} + />
void; }) { + const { t } = useTranslation(); + const [action, setAction] = React.useState(apiAction); const [newPropertyKey, setNewPropertyKey] = React.useState(''); const [addingPropertySection, setAddingPropertySection] = React.useState< @@ -1026,16 +1040,26 @@ function APIActionTable({

- Headers + {t('settings.tools.headers')}

- - - - - + + + + +
NameTypeFilled by LLMDescriptionValue + {t('settings.tools.name')} + + {t('settings.tools.type')} + + {t('settings.tools.filledByLLM')} + + {t('settings.tools.description')} + + {t('settings.tools.value')} +

- Query Parameters + {t('settings.tools.queryParameters')}

- - - - - + + + + +
NameTypeFilled by LLMDescriptionValue + {t('settings.tools.name')} + + {t('settings.tools.type')} + + {t('settings.tools.filledByLLM')} + + {t('settings.tools.description')} + + {t('settings.tools.value')} +

- Body + {t('settings.tools.body')}

- - - - - + + + + +
NameTypeFilled by LLMDescriptionValue + {t('settings.tools.name')} + + {t('settings.tools.type')} + + {t('settings.tools.filledByLLM')} + + {t('settings.tools.description')} + + {t('settings.tools.value')} +
-
-
- +
+