Desktop/remove hard requirement auth wall on desktop (#5956)

Co-authored-by: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
EthanHealy01
2026-03-23 19:36:48 +00:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent 081b1ec49e
commit 2e2b55e87d
87 changed files with 1522 additions and 339 deletions
@@ -78,7 +78,8 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
id: 'welcome',
type: 'modal-slide',
slideId: 'welcome',
condition: () => true,
// Desktop has its own onboarding modal (DesktopOnboardingModal)
condition: (ctx) => !ctx.isDesktopApp,
},
{
id: 'admin-overview',
@@ -107,7 +108,7 @@ export const ONBOARDING_STEPS: OnboardingStep[] = [
id: 'tour-overview',
type: 'modal-slide',
slideId: 'tour-overview',
condition: (ctx) => !ctx.effectiveIsAdmin && ctx.tourType !== 'admin',
condition: (ctx) => !ctx.effectiveIsAdmin && ctx.tourType !== 'admin' && !ctx.isDesktopApp,
},
{
id: 'server-license',
@@ -12,6 +12,7 @@ import ToolButton from "@app/components/tools/toolPicker/ToolButton";
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
import { ToolId } from "@app/types/toolId";
import { getSubcategoryLabel } from "@app/data/toolsTaxonomy";
import { ToolPickerFooterExtensions } from "@app/components/tools/toolPicker/ToolPickerFooterExtensions";
interface ToolPickerProps {
selectedToolKey: string | null;
@@ -150,6 +151,7 @@ const ToolPicker = ({ selectedToolKey, onSelect, filteredTools, isSearching = fa
</>
)}
</Box>
<ToolPickerFooterExtensions />
</Box>
);
};
@@ -3,11 +3,14 @@ import { useTranslation } from 'react-i18next';
import { Tooltip } from '@app/components/shared/Tooltip';
import { useBackendHealth } from '@app/hooks/useBackendHealth';
import { CloudBadge } from '@app/components/shared/CloudBadge';
import type { ExecuteDisabledReason } from '@app/hooks/tools/shared/toolOperationTypes';
import { useToolActions } from '@app/contexts/ToolActionsContext';
export interface OperationButtonProps {
onClick?: () => void;
isLoading?: boolean;
disabled?: boolean;
disabledReason?: ExecuteDisabledReason;
loadingText?: string;
submitText?: string;
variant?: 'filled' | 'outline' | 'subtle';
@@ -24,6 +27,7 @@ const OperationButton = ({
onClick,
isLoading = false,
disabled = false,
disabledReason,
loadingText,
submitText,
variant = 'filled',
@@ -37,20 +41,32 @@ const OperationButton = ({
}: OperationButtonProps) => {
const { t } = useTranslation();
const { isOnline, message: backendMessage } = useBackendHealth();
const { onEndpointUnavailableClick } = useToolActions();
const blockedByBackend = !isOnline;
const combinedDisabled = disabled || blockedByBackend;
const effectiveDisabled = disabled || disabledReason !== null && disabledReason !== undefined;
const combinedDisabled = effectiveDisabled || blockedByBackend;
const reasonTooltip: Record<NonNullable<ExecuteDisabledReason>, string> = {
endpointUnavailable: onEndpointUnavailableClick
? t('tool.endpointUnavailableClickable', "Not available in this mode. Click to sign in.")
: t('tool.endpointUnavailable', 'This tool is unavailable on your server.'),
noFiles: t('tool.noFiles', 'Add a file to get started.'),
invalidParams: t('tool.invalidParams', 'Fill in the required settings.'),
};
const tooltipLabel = blockedByBackend
? (backendMessage ?? t('backendHealth.checking', 'Checking backend status...'))
: null;
: (disabledReason ? (reasonTooltip[disabledReason] ?? null) : null);
const button = (
<Button
type={type}
onClick={onClick}
fullWidth={fullWidth}
mr='md'
ml='md'
mt={mt}
fullWidth={fullWidth || !!tooltipLabel}
mr={tooltipLabel ? 0 : 'md'}
ml={tooltipLabel ? 0 : 'md'}
mt={tooltipLabel ? 0 : mt}
loading={isLoading}
disabled={combinedDisabled}
variant={variant}
@@ -72,9 +88,22 @@ const OperationButton = ({
);
if (tooltipLabel) {
// Disabled buttons suppress pointer events at the browser level, so the Tooltip's
// cloneElement handlers would never fire. Wrap in a Box to capture them instead.
// When endpointUnavailable and a click handler is provided (desktop), the Box
// also acts as the click target to open the sign-in / connect modal.
const boxClickHandler = disabledReason === 'endpointUnavailable' ? onEndpointUnavailableClick : undefined;
return (
<Tooltip content={tooltipLabel} position="top" arrow>
{button}
<Box
mr="md"
ml="md"
mt={mt}
style={{ display: 'block', cursor: boxClickHandler ? 'pointer' : undefined }}
onClick={boxClickHandler}
>
{button}
</Box>
</Tooltip>
);
}
@@ -6,6 +6,7 @@ import { ToolOperationHook } from '@app/hooks/tools/shared/useToolOperation';
import { ToolWorkflowTitle, ToolWorkflowTitleProps } from '@app/components/tools/shared/ToolWorkflowTitle';
import { StirlingFile } from '@app/types/fileContext';
import type { TooltipTip } from '@app/types/tips';
import type { ExecuteDisabledReason } from '@app/hooks/tools/shared/toolOperationTypes';
export interface FilesStepConfig {
selectedFiles: StirlingFile[];
@@ -36,6 +37,23 @@ export interface ExecuteButtonConfig {
loadingText: string;
onClick: () => Promise<void>;
isVisible?: boolean;
/**
* Pass the raw endpoint-enabled flag from useEndpointEnabled / useBaseTool.
* createToolFlow derives the correct disabled reason automatically.
* Priority: endpointUnavailable > noFiles > invalidParams
*/
endpointEnabled?: boolean | null;
/**
* Pass the result of params.validateParameters().
* createToolFlow uses this to show the 'invalidParams' disabled reason.
*/
paramsValid?: boolean;
/**
* Explicit disabled reason override — use when the automatic computation
* from endpointEnabled + paramsValid is insufficient.
*/
disabledReason?: ExecuteDisabledReason;
/** Raw override for tools with fully custom disable logic (e.g. Compare, ShowJS). */
disabled?: boolean;
testId?: string;
showCloudBadge?: boolean;
@@ -99,18 +117,30 @@ export function createToolFlow<TParams = unknown>(config: ToolFlowConfig<TParams
{!config.review.isVisible && (config.files.selectedFiles?.length ?? 0) > 0 && config.preview}
{/* Execute Button */}
{config.executeButton && config.executeButton.isVisible !== false && (
<OperationButton
onClick={config.executeButton.onClick}
isLoading={config.review.operation.isLoading}
disabled={config.executeButton.disabled}
loadingText={config.executeButton.loadingText}
submitText={config.executeButton.text}
showCloudBadge={config.executeButton.showCloudBadge ?? config.review.operation.willUseCloud ?? false}
data-testid={config.executeButton.testId}
data-tour="run-button"
/>
)}
{config.executeButton && config.executeButton.isVisible !== false && (() => {
const eb = config.executeButton;
const hasFiles = (config.files.selectedFiles?.length ?? 0) > 0;
// Compute the disabled reason from structured fields; explicit disabledReason wins if set.
const effectiveDisabledReason: ExecuteDisabledReason =
eb.disabledReason !== undefined ? eb.disabledReason
: eb.endpointEnabled === false ? 'endpointUnavailable'
: !hasFiles ? 'noFiles'
: eb.paramsValid === false ? 'invalidParams'
: null;
return (
<OperationButton
onClick={eb.onClick}
isLoading={config.review.operation.isLoading}
disabled={eb.disabled}
disabledReason={effectiveDisabledReason}
loadingText={eb.loadingText}
submitText={eb.text}
showCloudBadge={eb.showCloudBadge ?? config.review.operation.willUseCloud ?? false}
data-testid={eb.testId}
data-tour="run-button"
/>
);
})()}
{/* Review Step */}
{steps.createReviewStep({
@@ -11,7 +11,7 @@ import { useHotkeys } from "@app/contexts/HotkeyContext";
import HotkeyDisplay from "@app/components/hotkeys/HotkeyDisplay";
import FavoriteStar from "@app/components/tools/toolPicker/FavoriteStar";
import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext";
import { ToolId } from "@app/types/toolId";
import type { ToolId } from "@app/types/toolId";
import { getToolDisabledReason, getDisabledLabel } from "@app/components/tools/fullscreen/shared";
import { useAppConfig } from "@app/contexts/AppConfigContext";
import { CloudBadge } from "@app/components/shared/CloudBadge";
@@ -26,15 +26,21 @@ interface ToolButtonProps {
disableNavigation?: boolean;
matchedSynonym?: string;
hasStars?: boolean;
/** Called when an unavailable tool is clicked; if provided, overrides the default no-op */
onUnavailableClick?: () => void;
}
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect, disableNavigation = false, matchedSynonym, hasStars = false }) => {
const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect, disableNavigation = false, matchedSynonym, hasStars = false, onUnavailableClick }) => {
const { t } = useTranslation();
const { config } = useAppConfig();
const premiumEnabled = config?.premiumEnabled;
const { isFavorite, toggleFavorite, toolAvailability } = useToolWorkflow();
const disabledReason = getToolDisabledReason(id, tool, toolAvailability, premiumEnabled);
const isUnavailable = disabledReason !== null;
// If onUnavailableClick is provided for a non-comingSoon tool, render as "cloud-available":
// full opacity, cloud badge, normal tooltip — clicking still fires onUnavailableClick (e.g. sign-in).
const showAsCloudAvailable = isUnavailable && !!onUnavailableClick && disabledReason !== 'comingSoon' && disabledReason !== 'selfHostedOffline';
const visuallyUnavailable = isUnavailable && !showAsCloudAvailable;
const { hotkeys } = useHotkeys();
const binding = hotkeys[id];
const { getToolNavigation } = useToolNavigation();
@@ -46,7 +52,10 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
const usesCloud = useWillUseCloud(endpointString);
const handleClick = (id: ToolId) => {
if (isUnavailable) return;
if (isUnavailable) {
onUnavailableClick?.();
return;
}
if (tool.link) {
// Open external link in new tab
window.open(tool.link, '_blank', 'noopener,noreferrer');
@@ -62,7 +71,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
const { key: disabledKey, fallback: disabledFallback } = getDisabledLabel(disabledReason);
const disabledMessage = t(disabledKey, disabledFallback);
const tooltipContent = isUnavailable
const tooltipContent = visuallyUnavailable
? (<span><strong>{disabledMessage}</strong> {tool.description}</span>)
: (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.35rem' }}>
@@ -84,7 +93,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
<>
<ToolIcon
icon={tool.icon}
opacity={isUnavailable ? 0.25 : 1}
opacity={visuallyUnavailable ? 0.25 : 1}
/>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', flex: 1, overflow: 'visible' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', width: '100%' }}>
@@ -93,19 +102,19 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
lines={1}
minimumFontScale={0.8}
as="span"
style={{ display: 'inline-block', maxWidth: '100%', opacity: isUnavailable ? 0.25 : 1 }}
style={{ display: 'inline-block', maxWidth: '100%', opacity: visuallyUnavailable ? 0.25 : 1 }}
/>
{tool.versionStatus === 'alpha' && (
<Badge
size="xs"
variant="light"
color="orange"
style={{ flexShrink: 0, opacity: isUnavailable ? 0.25 : 1 }}
style={{ flexShrink: 0, opacity: visuallyUnavailable ? 0.25 : 1 }}
>
{t('toolPanel.alpha', 'Alpha')}
</Badge>
)}
{usesCloud && !isUnavailable && (
{(usesCloud && !visuallyUnavailable) && (
<CloudBadge />
)}
</div>
@@ -113,7 +122,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
<span style={{
fontSize: '0.75rem',
color: 'var(--mantine-color-dimmed)',
opacity: isUnavailable ? 0.25 : 1,
opacity: visuallyUnavailable ? 0.25 : 1,
marginTop: '1px',
overflow: 'visible',
whiteSpace: 'nowrap'
@@ -195,7 +204,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
root: {
borderRadius: 0,
color: "var(--tools-text-and-icon-color)",
cursor: isUnavailable ? 'not-allowed' : undefined,
cursor: visuallyUnavailable ? 'not-allowed' : undefined,
overflow: 'visible'
},
label: { overflow: 'visible' }
@@ -205,7 +214,7 @@ const ToolButton: React.FC<ToolButtonProps> = ({ id, tool, isSelected, onSelect,
</Button>
);
const star = hasStars && !isUnavailable ? (
const star = hasStars && !visuallyUnavailable ? (
<FavoriteStar
isFavorite={fav}
onToggle={() => toggleFavorite(id as ToolId)}
@@ -0,0 +1,7 @@
/**
* Stub — returns null in core/web builds.
* Desktop build shadows this with a sign-in prompt for local mode.
*/
export function ToolPickerFooterExtensions() {
return null;
}
@@ -0,0 +1,16 @@
import { createContext, useContext } from 'react';
interface ToolActionsContextValue {
/**
* Called when the user clicks the disabled execute button while the reason
* is 'endpointUnavailable'. Desktop provides a sign-in modal dispatch;
* web builds leave this undefined (button stays disabled with tooltip only).
*/
onEndpointUnavailableClick?: () => void;
}
export const ToolActionsContext = createContext<ToolActionsContextValue>({});
export function useToolActions(): ToolActionsContextValue {
return useContext(ToolActionsContext);
}
@@ -66,6 +66,10 @@ interface ToolWorkflowContextValue extends ToolWorkflowState {
// Workflow Actions (compound actions)
handleToolSelect: (toolId: ToolId) => void;
/** Like handleToolSelect but bypasses the availability guard — use when you want to
* navigate to a tool's UI even if it's marked unavailable (e.g. to show a disabled
* execute button with a sign-in prompt rather than blocking navigation entirely). */
handleToolSelectForced: (toolId: ToolId) => void;
handleBackToTools: () => void;
handleReaderToggle: () => void;
@@ -321,6 +325,23 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
setReaderMode(false); // Disable read mode when selecting tools
}, [actions, getSelectedTool, navigationState.workbench, navigationState.hasUnsavedChanges, navigationState.selectedTool, setLeftPanelView, setReaderMode, setSearchQuery, toolAvailability]);
const handleToolSelectForced = useCallback((toolId: ToolId) => {
const validToolId = isValidToolId(toolId) ? toolId : null;
actions.setSelectedTool(validToolId);
const tool = getSelectedTool(toolId);
const wasInCustomWorkbench = !isBaseWorkbench(navigationState.workbench);
if (wasInCustomWorkbench) {
actions.setWorkbench(getDefaultWorkbench());
} else if (tool && tool.workbench) {
actions.setWorkbench(tool.workbench);
} else {
actions.setWorkbench(getDefaultWorkbench());
}
setSearchQuery('');
setLeftPanelView('toolContent');
setReaderMode(false);
}, [actions, getSelectedTool, navigationState.workbench, setLeftPanelView, setReaderMode, setSearchQuery]);
const handleBackToTools = useCallback(() => {
setLeftPanelView('toolPicker');
setReaderMode(false);
@@ -378,6 +399,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
// Workflow Actions
handleToolSelect,
handleToolSelectForced,
handleBackToTools,
handleReaderToggle,
@@ -11,6 +11,16 @@ export enum ToolType {
custom,
}
/**
* Reason the execute button is disabled. Resolved to a translated tooltip by OperationButton.
* null means the button is enabled.
*/
export type ExecuteDisabledReason =
| 'endpointUnavailable'
| 'noFiles'
| 'invalidParams'
| null;
/**
* Result from custom processor with optional metadata about input consumption.
*/
@@ -152,7 +152,6 @@ export function useBaseTool<TParams, TParamsHook extends BaseParametersHook<TPar
// Endpoint validation
endpointEnabled,
endpointLoading,
// Handlers
handleExecute,
handleThumbnailClick,
+1 -1
View File
@@ -36,7 +36,7 @@ export const accountService = {
* Get current user account data
*/
async getAccountData(): Promise<AccountData> {
const response = await apiClient.get<AccountData>('/api/v1/proprietary/ui-data/account');
const response = await apiClient.get<AccountData>('/api/v1/proprietary/ui-data/account', { suppressErrorToast: true });
return response.data;
},
@@ -42,6 +42,7 @@ class SignatureStorageService {
// Probe the proprietary signatures endpoint (requires authentication)
await apiClient.get('/api/v1/proprietary/signatures', {
timeout: 3000,
suppressErrorToast: true,
});
// 200 = Backend available and accessible (authenticated)
+3
View File
@@ -22,6 +22,9 @@ export const Z_INDEX_DRAG_BADGE = 1001;
// Modal that appears on top of config modal (e.g., restart confirmation, update modal)
export const Z_INDEX_OVER_CONFIG_MODAL = 2000;
// Sign-in modal — must appear above all app UI including config and analytics modals
export const Z_INDEX_SIGN_IN_MODAL = 9000;
// Toast notifications and error displays - Always on top (higher than rainbow theme at 10000)
export const Z_INDEX_TOAST = 10001;
+2 -1
View File
@@ -90,7 +90,8 @@ const AddAttachments = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =
isVisible: !hasResults,
loadingText: t('loading'),
onClick: handleExecute,
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: params.validateParameters(),
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -106,7 +106,8 @@ const AddPageNumbers = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =
isVisible: !hasResults,
loadingText: t('loading'),
onClick: handleExecute,
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: params.validateParameters(),
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -106,7 +106,8 @@ const AddPassword = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
isVisible: !hasResults,
loadingText: t("loading"),
onClick: handleAddPassword,
disabled: !addPasswordParams.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: addPasswordParams.validateParameters(),
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -167,7 +167,8 @@ const AddStamp = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
isVisible: !hasResults,
loadingText: t('loading'),
onClick: handleExecute,
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: params.validateParameters(),
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -199,7 +199,8 @@ const AddWatermark = ({ onPreviewFile, onComplete, onError }: BaseToolProps) =>
isVisible: !hasResults,
loadingText: t("loading"),
onClick: handleAddWatermark,
disabled: !watermarkParams.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: watermarkParams.validateParameters(),
},
review: {
isVisible: hasResults,
+1 -1
View File
@@ -103,7 +103,7 @@ const AdjustContrast = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t('loading'),
onClick: base.handleExecute,
disabled: !base.hasFiles,
paramsValid: true,
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const AdjustPageScale = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -36,7 +36,8 @@ return createToolFlow({
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -44,7 +44,8 @@ const BookletImposition = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -113,7 +113,8 @@ const CertSign = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -141,7 +141,8 @@ const ChangeMetadata = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -43,7 +43,8 @@ const ChangePermissions = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const Compress = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -152,7 +152,8 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
loadingText: t("convert.converting", "Converting..."),
onClick: handleConvert,
isVisible: !hasResults,
disabled: !convertParams.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: convertParams.validateParameters(),
testId: "convert-button",
},
review: {
+2 -1
View File
@@ -44,7 +44,8 @@ const Crop = (props: BaseToolProps) => {
loadingText: t("loading"),
onClick: base.handleExecute,
isVisible: !base.hasResults,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -40,7 +40,8 @@ const ExtractImages = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -45,7 +45,8 @@ const ExtractPages = (props: BaseToolProps) => {
loadingText: t("loading"),
onClick: base.handleExecute,
isVisible: !base.hasResults,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const Flatten = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -5
View File
@@ -163,11 +163,8 @@ const GetPdfInfo = (props: BaseToolProps) => {
text: t('getPdfInfo.submit', 'Generate'),
loadingText: t('loading', 'Loading...'),
onClick: base.handleExecute,
disabled:
!base.params.validateParameters() ||
!base.hasFiles ||
base.operation.isLoading ||
!base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
isVisible: true,
},
review: {
+2 -1
View File
@@ -134,7 +134,8 @@ const Merge = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+3 -2
View File
@@ -128,8 +128,9 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
text: t("ocr.operation.submit", "Process OCR and Review"),
loadingText: t("loading"),
onClick: handleOCR,
isVisible: hasValidSettings && !hasResults,
disabled: !ocrParams.validateParameters() || !hasFiles || !endpointEnabled,
isVisible: !hasResults,
endpointEnabled: endpointEnabled,
paramsValid: hasValidSettings,
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const OverlayPdfs = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t('loading'),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -40,7 +40,8 @@ const PageLayout = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t('loading'),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -37,7 +37,7 @@ const RemoveAnnotations = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading", "Processing..."),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -51,7 +51,8 @@ const RemoveBlanks = (props: BaseToolProps) => {
loadingText: t("loading"),
onClick: base.handleExecute,
isVisible: !base.hasResults,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -26,7 +26,8 @@ const RemoveCertificateSign = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -26,7 +26,8 @@ const RemoveImage = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -46,7 +46,8 @@ const RemovePages = (props: BaseToolProps) => {
loadingText: t("loading"),
onClick: base.handleExecute,
isVisible: !base.hasResults,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const RemovePassword = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -82,7 +82,8 @@ const ReorganizePages = ({ onPreviewFile, onComplete, onError }: BaseToolProps)
isVisible: !hasResults,
loadingText: t('loading'),
onClick: handleExecute,
disabled: !params.validateParameters() || !hasFiles || !endpointEnabled,
endpointEnabled: endpointEnabled,
paramsValid: params.validateParameters(),
},
review: {
isVisible: hasResults,
+2 -1
View File
@@ -26,7 +26,8 @@ const Repair = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -43,7 +43,8 @@ const ReplaceColor = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -42,7 +42,8 @@ const Rotate = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -40,7 +40,8 @@ const Sanitize = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -43,7 +43,8 @@ const ScannerImageSplit = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -26,7 +26,8 @@ const SingleLargePage = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -89,7 +89,8 @@ const Split = (props: BaseToolProps) => {
loadingText: t("loading"),
onClick: base.handleExecute,
isVisible: !base.hasResults,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
+2 -1
View File
@@ -26,7 +26,8 @@ const UnlockPdfForms = (props: BaseToolProps) => {
isVisible: !base.hasResults,
loadingText: t("loading"),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
},
review: {
isVisible: base.hasResults,
@@ -142,11 +142,8 @@ const ValidateSignature = (props: BaseToolProps) => {
text: t('validateSignature.submit', 'Validate Signatures'),
loadingText: t('loading', 'Loading...'),
onClick: base.handleExecute,
disabled:
!base.params.validateParameters() ||
!base.hasFiles ||
base.operation.isLoading ||
!base.endpointEnabled,
endpointEnabled: base.endpointEnabled,
paramsValid: base.params.validateParameters(),
isVisible: true,
},
review: {