mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
feat(frontend): create new planner tab in the interface (#11646)
This commit is contained in:
parent
a1d4d62f68
commit
b678d548c2
@ -15,6 +15,7 @@ const EditorTab = lazy(() => import("#/routes/changes-tab"));
|
||||
const BrowserTab = lazy(() => import("#/routes/browser-tab"));
|
||||
const ServedTab = lazy(() => import("#/routes/served-tab"));
|
||||
const VSCodeTab = lazy(() => import("#/routes/vscode-tab"));
|
||||
const PlannerTab = lazy(() => import("#/routes/planner-tab"));
|
||||
|
||||
export function ConversationTabContent() {
|
||||
const { selectedTab, shouldShownAgentLoading } = useConversationStore();
|
||||
@ -28,6 +29,7 @@ export function ConversationTabContent() {
|
||||
const isServedActive = selectedTab === "served";
|
||||
const isVSCodeActive = selectedTab === "vscode";
|
||||
const isTerminalActive = selectedTab === "terminal";
|
||||
const isPlannerActive = selectedTab === "planner";
|
||||
|
||||
// Define tab configurations
|
||||
const tabs = [
|
||||
@ -44,6 +46,11 @@ export function ConversationTabContent() {
|
||||
component: Terminal,
|
||||
isActive: isTerminalActive,
|
||||
},
|
||||
{
|
||||
key: "planner",
|
||||
component: PlannerTab,
|
||||
isActive: isPlannerActive,
|
||||
},
|
||||
];
|
||||
|
||||
const conversationTabTitle = useMemo(() => {
|
||||
@ -62,6 +69,9 @@ export function ConversationTabContent() {
|
||||
if (isTerminalActive) {
|
||||
return t(I18nKey.COMMON$TERMINAL);
|
||||
}
|
||||
if (isPlannerActive) {
|
||||
return t(I18nKey.COMMON$PLANNER);
|
||||
}
|
||||
return "";
|
||||
}, [
|
||||
isEditorActive,
|
||||
@ -69,6 +79,7 @@ export function ConversationTabContent() {
|
||||
isServedActive,
|
||||
isVSCodeActive,
|
||||
isTerminalActive,
|
||||
isPlannerActive,
|
||||
]);
|
||||
|
||||
if (shouldShownAgentLoading) {
|
||||
|
||||
@ -6,6 +6,7 @@ import GlobeIcon from "#/icons/globe.svg?react";
|
||||
import ServerIcon from "#/icons/server.svg?react";
|
||||
import GitChanges from "#/icons/git_changes.svg?react";
|
||||
import VSCodeIcon from "#/icons/vscode.svg?react";
|
||||
import LessonPlanIcon from "#/icons/lesson-plan.svg?react";
|
||||
import { cn } from "#/utils/utils";
|
||||
import { ConversationTabNav } from "./conversation-tab-nav";
|
||||
import { ChatActionTooltip } from "../../chat/chat-action-tooltip";
|
||||
@ -15,6 +16,7 @@ import {
|
||||
useConversationStore,
|
||||
type ConversationTab,
|
||||
} from "#/state/conversation-store";
|
||||
import { USE_PLANNING_AGENT } from "#/utils/feature-flags";
|
||||
|
||||
export function ConversationTabs() {
|
||||
const {
|
||||
@ -34,6 +36,8 @@ export function ConversationTabs() {
|
||||
const [persistedIsRightPanelShown, setPersistedIsRightPanelShown] =
|
||||
useLocalStorage<boolean>("conversation-right-panel-shown", true);
|
||||
|
||||
const shouldUsePlanningAgent = USE_PLANNING_AGENT();
|
||||
|
||||
const onTabChange = (value: ConversationTab | null) => {
|
||||
setSelectedTab(value);
|
||||
// Persist the selected tab to localStorage
|
||||
@ -129,6 +133,17 @@ export function ConversationTabs() {
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldUsePlanningAgent) {
|
||||
tabs.unshift({
|
||||
isActive: isTabActive("planner"),
|
||||
icon: LessonPlanIcon,
|
||||
onClick: () => onTabSelected("planner"),
|
||||
tooltipContent: t(I18nKey.COMMON$PLANNER),
|
||||
tooltipAriaLabel: t(I18nKey.COMMON$PLANNER),
|
||||
label: t(I18nKey.COMMON$PLANNER),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@ -857,6 +857,7 @@ export enum I18nKey {
|
||||
BUTTON$DELETE_CONVERSATION = "BUTTON$DELETE_CONVERSATION",
|
||||
BUTTON$RENAME = "BUTTON$RENAME",
|
||||
COMMON$APP = "COMMON$APP",
|
||||
COMMON$PLANNER = "COMMON$PLANNER",
|
||||
COMMON$APPLICATION_SETTINGS = "COMMON$APPLICATION_SETTINGS",
|
||||
COMMON$BROWSER = "COMMON$BROWSER",
|
||||
COMMON$CHANGES = "COMMON$CHANGES",
|
||||
@ -931,4 +932,5 @@ export enum I18nKey {
|
||||
TOAST$FAILED_TO_STOP_CONVERSATION = "TOAST$FAILED_TO_STOP_CONVERSATION",
|
||||
TOAST$CONVERSATION_STOPPED = "TOAST$CONVERSATION_STOPPED",
|
||||
AGENT_STATUS$WAITING_FOR_USER_CONFIRMATION = "AGENT_STATUS$WAITING_FOR_USER_CONFIRMATION",
|
||||
COMMON$CREATE_A_PLAN = "COMMON$CREATE_A_PLAN",
|
||||
}
|
||||
|
||||
@ -6912,20 +6912,20 @@
|
||||
"tr": "Bu alan zorunludur"
|
||||
},
|
||||
"PLANNER$EMPTY_MESSAGE": {
|
||||
"en": "No plan created.",
|
||||
"zh-CN": "计划未创建",
|
||||
"zh-TW": "未建立任何計劃。",
|
||||
"de": "Kein Plan erstellt.",
|
||||
"ko-KR": "생성된 계획이 없습니다.",
|
||||
"no": "Ingen plan opprettet.",
|
||||
"it": "Nessun piano creato.",
|
||||
"pt": "Nenhum plano criado.",
|
||||
"es": "Ningún plan creado.",
|
||||
"ar": "لم يتم إنشاء أي خطة.",
|
||||
"fr": "Aucun plan créé.",
|
||||
"tr": "Plan oluşturulmadı.",
|
||||
"ja": "プランナーは空です",
|
||||
"uk": "План не створено."
|
||||
"en": "There is currently no plan for this repo",
|
||||
"uk": "Наразі для цього репозиторію немає плану",
|
||||
"ja": "現在このリポジトリには計画がありません",
|
||||
"zh-CN": "当前此仓库没有计划",
|
||||
"zh-TW": "目前此存儲庫沒有計劃",
|
||||
"ko-KR": "이 저장소에 대한 계획이 현재 없습니다",
|
||||
"no": "Det finnes foreløpig ingen plan for dette repoet",
|
||||
"ar": "لا يوجد حالياً خطة لهذا المستودع",
|
||||
"de": "Derzeit gibt es keinen Plan für dieses Repository",
|
||||
"fr": "Il n'y a actuellement aucun plan pour ce dépôt",
|
||||
"it": "Attualmente non c'è un piano per questo repository",
|
||||
"pt": "Atualmente não há plano para este repositório",
|
||||
"es": "Actualmente no hay un plan para este repositorio",
|
||||
"tr": "Şu anda bu depo için bir plan yok"
|
||||
},
|
||||
"FEEDBACK$PUBLIC_LABEL": {
|
||||
"en": "Public",
|
||||
@ -13711,6 +13711,22 @@
|
||||
"de": "App",
|
||||
"uk": "Додаток"
|
||||
},
|
||||
"COMMON$PLANNER": {
|
||||
"en": "Planner",
|
||||
"ja": "プランナー",
|
||||
"zh-CN": "计划器",
|
||||
"zh-TW": "規劃器",
|
||||
"ko-KR": "플래너",
|
||||
"no": "Planlegger",
|
||||
"it": "Pianificatore",
|
||||
"pt": "Planejador",
|
||||
"es": "Planificador",
|
||||
"ar": "المخطط",
|
||||
"fr": "Planificateur",
|
||||
"tr": "Planlayıcı",
|
||||
"de": "Planer",
|
||||
"uk": "Планувальник"
|
||||
},
|
||||
"COMMON$APPLICATION_SETTINGS": {
|
||||
"en": "Application Settings",
|
||||
"ja": "アプリケーション設定",
|
||||
@ -14894,5 +14910,21 @@
|
||||
"tr": "Kullanıcı onayı bekleniyor",
|
||||
"de": "Warte auf Benutzerbestätigung",
|
||||
"uk": "Очікується підтвердження користувача"
|
||||
},
|
||||
"COMMON$CREATE_A_PLAN": {
|
||||
"en": "Create a plan",
|
||||
"ja": "プランを作成する",
|
||||
"zh-CN": "创建计划",
|
||||
"zh-TW": "建立計劃",
|
||||
"ko-KR": "계획 만들기",
|
||||
"no": "Lag en plan",
|
||||
"it": "Crea un piano",
|
||||
"pt": "Criar um plano",
|
||||
"es": "Crear un plan",
|
||||
"ar": "إنشاء خطة",
|
||||
"fr": "Créer un plan",
|
||||
"tr": "Bir plan oluştur",
|
||||
"de": "Einen Plan erstellen",
|
||||
"uk": "Створити план"
|
||||
}
|
||||
}
|
||||
|
||||
8
frontend/src/icons/lesson-plan.svg
Normal file
8
frontend/src/icons/lesson-plan.svg
Normal file
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="109" height="109" viewBox="0 0 109 109" fill="none">
|
||||
<path d="M40.1979 21.8969L34.7311 17.832L25.2691 30.5574L20.2094 26.784L16.1367 32.2451L26.6653 40.0969L40.1979 21.8969Z" fill="currentColor"/>
|
||||
<path d="M90.8342 35.1983H50.4639V28.3858H90.8342V35.1983Z" fill="currentColor"/>
|
||||
<path d="M90.8342 57.9067H50.4638V51.0942H90.8342V57.9067Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.2508 63.5837C32.2674 63.5837 36.3342 59.517 36.3342 54.5004C36.3342 49.4838 32.2674 45.4171 27.2508 45.4171C22.2342 45.4171 18.1675 49.4838 18.1675 54.5004C18.1675 59.517 22.2342 63.5837 27.2508 63.5837ZM27.2508 59.0421C29.7591 59.0421 31.7925 57.0087 31.7925 54.5004C31.7925 51.9921 29.7591 49.9587 27.2508 49.9587C24.7425 49.9587 22.7092 51.9921 22.7092 54.5004C22.7092 57.0087 24.7425 59.0421 27.2508 59.0421Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.3342 77.2087C36.3342 82.2253 32.2674 86.2921 27.2508 86.2921C22.2342 86.2921 18.1675 82.2253 18.1675 77.2087C18.1675 72.1922 22.2342 68.1254 27.2508 68.1254C32.2674 68.1254 36.3342 72.1922 36.3342 77.2087ZM31.7925 77.2087C31.7925 79.717 29.7591 81.7504 27.2508 81.7504C24.7425 81.7504 22.7092 79.717 22.7092 77.2087C22.7092 74.7005 24.7425 72.6671 27.2508 72.6671C29.7591 72.6671 31.7925 74.7005 31.7925 77.2087Z" fill="currentColor"/>
|
||||
<path d="M50.4637 80.615H90.834V73.8025H50.4637V80.615Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
25
frontend/src/routes/planner-tab.tsx
Normal file
25
frontend/src/routes/planner-tab.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import LessonPlanIcon from "#/icons/lesson-plan.svg?react";
|
||||
|
||||
function PlannerTab() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center w-full h-full p-10">
|
||||
<LessonPlanIcon width={109} height={109} color="#A1A1A1" />
|
||||
<span className="text-[#8D95A9] text-[19px] font-normal leading-5 pb-9">
|
||||
{t(I18nKey.PLANNER$EMPTY_MESSAGE)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {}}
|
||||
className="flex w-[164px] h-[40px] p-2 justify-center items-center shrink-0 rounded-lg bg-white overflow-hidden text-black text-ellipsis font-sans text-[16px] not-italic font-normal leading-[20px] hover:cursor-pointer hover:opacity-80"
|
||||
>
|
||||
{t(I18nKey.COMMON$CREATE_A_PLAN)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlannerTab;
|
||||
@ -6,7 +6,8 @@ export type ConversationTab =
|
||||
| "browser"
|
||||
| "served"
|
||||
| "vscode"
|
||||
| "terminal";
|
||||
| "terminal"
|
||||
| "planner";
|
||||
|
||||
export interface IMessageToSend {
|
||||
text: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user