File size: 4,451 Bytes
3201ca6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import { useState, type ReactNode } from "react";
import { Trans, useTranslation } from "react-i18next";
import { I18nKey } from "#/i18n/declaration";
import ChevronDownIcon from "#/icons/chevron-down.svg?react";
import MessageSquareShareIcon from "#/icons/message-square-share.svg?react";
import { cn } from "#/utils/utils";
import { BrandButton } from "#/components/features/settings/brand-button";
import { useLaunchSkillInChat } from "#/hooks/use-launch-skill-in-chat";
import { useActiveBackend } from "#/contexts/active-backend-context";
import { useTracking } from "#/hooks/use-tracking";
import { getAutomationsDocsUrl } from "#/manifests/automation-interface";
function InlineExampleWrap({ children }: { children?: ReactNode }) {
return <span className="whitespace-nowrap">{children}</span>;
}
function InlineCodeChip({ children }: { children?: ReactNode }) {
return (
<code
data-testid="automations-create-instructions-example"
className={cn(
"mx-0.5 inline-block rounded-sm border border-[var(--oh-border-subtle)]",
"bg-[var(--oh-surface-raised)] px-1.5 py-0.5 align-baseline font-mono text-[11px] text-white",
)}
>
{children}
</code>
);
}
function InlinePunctuation({ children }: { children?: ReactNode }) {
return <>{children}</>;
}
const CREATE_INSTRUCTIONS_INLINE_COMPONENTS = {
example: <InlineExampleWrap />,
cmd: <InlineCodeChip />,
punct: <InlinePunctuation />,
};
interface CreateInstructionsProps {
/** If true, the instructions are collapsible and start collapsed */
collapsible?: boolean;
}
interface CreateInstructionsContentProps {
onLaunch?: () => void;
}
export function CreateInstructionsContent({
onLaunch,
}: CreateInstructionsContentProps = {}) {
const { t } = useTranslation("openhands");
const launchInChat = useLaunchSkillInChat();
const active = useActiveBackend();
const { trackAutomationCreatedButton } = useTracking();
const handleCreateAutomation = () => {
trackAutomationCreatedButton({ backendKind: active.backend.kind });
launchInChat(t(I18nKey.AUTOMATIONS$CREATE_AUTOMATION_PROMPT), onLaunch);
};
return (
<div className="flex flex-col gap-5">
<p className="text-sm leading-relaxed text-tertiary-light">
<Trans
ns="openhands"
i18nKey={I18nKey.AUTOMATIONS$EMPTY_OPTION_CONVERSATION_DESC}
components={CREATE_INSTRUCTIONS_INLINE_COMPONENTS}
/>{" "}
{t(I18nKey.AUTOMATIONS$CREATE_INSTRUCTIONS_GUIDANCE)}{" "}
<a
href={getAutomationsDocsUrl()}
target="_blank"
rel="noopener noreferrer"
className="text-muted underline transition-colors hover:text-foreground"
>
{t(I18nKey.AUTOMATIONS$EMPTY_LEARN_MORE)}
</a>
</p>
<div className="flex justify-center">
<BrandButton
type="button"
variant="primary"
testId="automations-create-automation"
onClick={handleCreateAutomation}
startContent={
<MessageSquareShareIcon className="size-4" aria-hidden />
}
>
{t(I18nKey.AUTOMATIONS$CREATE_AUTOMATION_BUTTON)}
</BrandButton>
</div>
</div>
);
}
export function CreateInstructions({
collapsible = false,
}: CreateInstructionsProps) {
const { t } = useTranslation("openhands");
const [isExpanded, setIsExpanded] = useState(!collapsible);
if (collapsible) {
return (
<div className="w-full rounded-lg border border-[var(--oh-border)] bg-[var(--oh-surface)]">
<button
type="button"
onClick={() => setIsExpanded(!isExpanded)}
aria-expanded={isExpanded}
className="flex w-full items-center justify-between rounded-lg p-4 text-left transition-colors hover:bg-surface-raised"
>
<span className="text-sm font-normal text-content">
{t(I18nKey.AUTOMATIONS$EMPTY_HOW_TO_CREATE_TITLE)}
</span>
<ChevronDownIcon
className={cn(
"size-5 text-muted transition-transform",
isExpanded && "rotate-180",
)}
/>
</button>
{isExpanded ? (
<div className="px-4 pb-4">
<CreateInstructionsContent />
</div>
) : null}
</div>
);
}
return (
<div className="w-full max-w-2xl">
<CreateInstructionsContent />
</div>
);
}
|