|
|
@@ -1,14 +1,6 @@
|
|
|
-import React, { useState, useEffect, useMemo, useCallback } from "react"
|
|
|
+import React, { useState, useEffect, useMemo, useCallback, useRef } from "react"
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
-import {
|
|
|
- VSCodeTextArea,
|
|
|
- VSCodeDropdown,
|
|
|
- VSCodeOption,
|
|
|
- VSCodeTextField,
|
|
|
- VSCodeCheckbox,
|
|
|
- VSCodeRadioGroup,
|
|
|
- VSCodeRadio,
|
|
|
-} from "@vscode/webview-ui-toolkit/react"
|
|
|
+import { VSCodeCheckbox, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
|
|
|
|
|
|
import { useExtensionState } from "@src/context/ExtensionStateContext"
|
|
|
import {
|
|
|
@@ -29,6 +21,25 @@ import { Tab, TabContent, TabHeader } from "../common/Tab"
|
|
|
import i18next from "i18next"
|
|
|
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
|
|
import { Trans } from "react-i18next"
|
|
|
+import {
|
|
|
+ Select,
|
|
|
+ SelectContent,
|
|
|
+ SelectItem,
|
|
|
+ SelectTrigger,
|
|
|
+ SelectValue,
|
|
|
+ Textarea,
|
|
|
+ Popover,
|
|
|
+ PopoverContent,
|
|
|
+ PopoverTrigger,
|
|
|
+ Command,
|
|
|
+ CommandInput,
|
|
|
+ CommandList,
|
|
|
+ CommandEmpty,
|
|
|
+ CommandItem,
|
|
|
+ CommandGroup,
|
|
|
+ Input,
|
|
|
+} from "../ui"
|
|
|
+import { ChevronsUpDown, X } from "lucide-react"
|
|
|
|
|
|
// Get all available groups that should show in prompts view
|
|
|
const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable)
|
|
|
@@ -78,9 +89,14 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
const [isToolsEditMode, setIsToolsEditMode] = useState(false)
|
|
|
const [showConfigMenu, setShowConfigMenu] = useState(false)
|
|
|
const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false)
|
|
|
- const [activeSupportTab, setActiveSupportTab] = useState<SupportPromptType>("ENHANCE")
|
|
|
+ const [activeSupportOption, setActiveSupportOption] = useState<SupportPromptType>("ENHANCE")
|
|
|
const [isSystemPromptDisclosureOpen, setIsSystemPromptDisclosureOpen] = useState(false)
|
|
|
|
|
|
+ // State for mode selection popover and search
|
|
|
+ const [open, setOpen] = useState(false)
|
|
|
+ const [searchValue, setSearchValue] = useState("")
|
|
|
+ const searchInputRef = useRef<HTMLInputElement>(null)
|
|
|
+
|
|
|
// Direct update functions
|
|
|
const updateAgentPrompt = useCallback(
|
|
|
(mode: Mode, promptData: PromptComponent) => {
|
|
|
@@ -144,9 +160,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
// Exit tools edit mode when switching modes
|
|
|
setIsToolsEditMode(false)
|
|
|
},
|
|
|
- [visualMode, switchMode, setIsToolsEditMode],
|
|
|
+ [visualMode, switchMode],
|
|
|
)
|
|
|
|
|
|
+ // Handler for popover open state change
|
|
|
+ const onOpenChange = useCallback((open: boolean) => {
|
|
|
+ setOpen(open)
|
|
|
+ // Reset search when closing the popover
|
|
|
+ if (!open) {
|
|
|
+ setTimeout(() => setSearchValue(""), 100)
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ // Handler for clearing search input
|
|
|
+ const onClearSearch = useCallback(() => {
|
|
|
+ setSearchValue("")
|
|
|
+ searchInputRef.current?.focus()
|
|
|
+ }, [])
|
|
|
+
|
|
|
// Helper function to get current mode's config
|
|
|
const getCurrentMode = useCallback((): ModeConfig | undefined => {
|
|
|
const findMode = (m: ModeConfig): boolean => m.slug === visualMode
|
|
|
@@ -480,45 +511,95 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
{t("prompts:modes.createModeHelpText")}
|
|
|
</div>
|
|
|
|
|
|
- <div className="flex gap-2 items-center mb-3 flex-wrap py-1">
|
|
|
- {modes.map((modeConfig) => {
|
|
|
- const isActive = visualMode === modeConfig.slug
|
|
|
- return (
|
|
|
- <button
|
|
|
- key={modeConfig.slug}
|
|
|
- data-testid={`${modeConfig.slug}-tab`}
|
|
|
- data-active={isActive ? "true" : "false"}
|
|
|
- onClick={() => handleModeSwitch(modeConfig)}
|
|
|
- className={`px-2 py-1 border-none rounded cursor-pointer font-bold ${
|
|
|
- isActive
|
|
|
- ? "bg-vscode-button-background text-vscode-button-foreground opacity-100"
|
|
|
- : "bg-transparent text-vscode-foreground opacity-80"
|
|
|
- }`}>
|
|
|
- {modeConfig.name}
|
|
|
- </button>
|
|
|
- )
|
|
|
- })}
|
|
|
+ <div className="flex items-center gap-1 mb-3">
|
|
|
+ <Popover open={open} onOpenChange={onOpenChange}>
|
|
|
+ <PopoverTrigger asChild>
|
|
|
+ <Button
|
|
|
+ variant="combobox"
|
|
|
+ role="combobox"
|
|
|
+ aria-expanded={open}
|
|
|
+ className="grow justify-between"
|
|
|
+ data-testid="mode-select-trigger">
|
|
|
+ <div>{getCurrentMode()?.name || t("prompts:modes.selectMode")}</div>
|
|
|
+ <ChevronsUpDown className="opacity-50" />
|
|
|
+ </Button>
|
|
|
+ </PopoverTrigger>
|
|
|
+ <PopoverContent className="p-0 w-[var(--radix-popover-trigger-width)]">
|
|
|
+ <Command>
|
|
|
+ <div className="relative">
|
|
|
+ <CommandInput
|
|
|
+ ref={searchInputRef}
|
|
|
+ value={searchValue}
|
|
|
+ onValueChange={setSearchValue}
|
|
|
+ placeholder={t("prompts:modes.selectMode")}
|
|
|
+ className="h-9 mr-4"
|
|
|
+ data-testid="mode-search-input"
|
|
|
+ />
|
|
|
+ {searchValue.length > 0 && (
|
|
|
+ <div className="absolute right-2 top-0 bottom-0 flex items-center justify-center">
|
|
|
+ <X
|
|
|
+ className="text-vscode-input-foreground opacity-50 hover:opacity-100 size-4 p-0.5 cursor-pointer"
|
|
|
+ onClick={onClearSearch}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <CommandList>
|
|
|
+ <CommandEmpty>
|
|
|
+ {searchValue && (
|
|
|
+ <div className="py-2 px-1 text-sm">
|
|
|
+ {t("prompts:modes.noMatchFound")}
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </CommandEmpty>
|
|
|
+ <CommandGroup>
|
|
|
+ {modes
|
|
|
+ .filter((modeConfig) =>
|
|
|
+ searchValue
|
|
|
+ ? modeConfig.name
|
|
|
+ .toLowerCase()
|
|
|
+ .includes(searchValue.toLowerCase())
|
|
|
+ : true,
|
|
|
+ )
|
|
|
+ .map((modeConfig) => (
|
|
|
+ <CommandItem
|
|
|
+ key={modeConfig.slug}
|
|
|
+ value={modeConfig.slug}
|
|
|
+ onSelect={() => {
|
|
|
+ handleModeSwitch(modeConfig)
|
|
|
+ setOpen(false)
|
|
|
+ }}
|
|
|
+ data-testid={`mode-option-${modeConfig.slug}`}>
|
|
|
+ <div className="flex items-center justify-between w-full">
|
|
|
+ <span>{modeConfig.name}</span>
|
|
|
+ <span className="text-foreground">{modeConfig.slug}</span>
|
|
|
+ </div>
|
|
|
+ </CommandItem>
|
|
|
+ ))}
|
|
|
+ </CommandGroup>
|
|
|
+ </CommandList>
|
|
|
+ </Command>
|
|
|
+ </PopoverContent>
|
|
|
+ </Popover>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div style={{ marginBottom: "20px" }}>
|
|
|
+ <div className="mb-5">
|
|
|
{/* Only show name and delete for custom modes */}
|
|
|
{visualMode && findModeBySlug(visualMode, customModes) && (
|
|
|
<div className="flex gap-3 mb-4">
|
|
|
<div className="flex-1">
|
|
|
<div className="font-bold mb-1">{t("prompts:createModeDialog.name.label")}</div>
|
|
|
<div className="flex gap-2">
|
|
|
- <VSCodeTextField
|
|
|
+ <Input
|
|
|
+ type="text"
|
|
|
value={getModeProperty(findModeBySlug(visualMode, customModes), "name") ?? ""}
|
|
|
- onChange={(e: Event | React.FormEvent<HTMLElement>) => {
|
|
|
- const target =
|
|
|
- (e as CustomEvent)?.detail?.target ||
|
|
|
- ((e as any).target as HTMLInputElement)
|
|
|
+ onChange={(e) => {
|
|
|
const customMode = findModeBySlug(visualMode, customModes)
|
|
|
if (customMode) {
|
|
|
updateCustomMode(visualMode, {
|
|
|
...customMode,
|
|
|
- name: target.value,
|
|
|
+ name: e.target.value,
|
|
|
source: customMode.source || "global",
|
|
|
})
|
|
|
}
|
|
|
@@ -541,7 +622,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
</div>
|
|
|
)}
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
+ <div className="mb-4">
|
|
|
<div className="flex justify-between items-center mb-1">
|
|
|
<div className="font-bold">{t("prompts:roleDefinition.title")}</div>
|
|
|
{!findModeBySlug(visualMode, customModes) && (
|
|
|
@@ -563,7 +644,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
<div className="text-sm text-vscode-descriptionForeground mb-2">
|
|
|
{t("prompts:roleDefinition.description")}
|
|
|
</div>
|
|
|
- <VSCodeTextArea
|
|
|
+ <Textarea
|
|
|
value={(() => {
|
|
|
const customMode = findModeBySlug(visualMode, customModes)
|
|
|
const prompt = customModePrompts?.[visualMode] as PromptComponent
|
|
|
@@ -575,7 +656,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
})()}
|
|
|
onChange={(e) => {
|
|
|
const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
+ (e as unknown as CustomEvent)?.detail?.target?.value ||
|
|
|
((e as any).target as HTMLTextAreaElement).value
|
|
|
const customMode = findModeBySlug(visualMode, customModes)
|
|
|
if (customMode) {
|
|
|
@@ -592,35 +673,35 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
})
|
|
|
}
|
|
|
}}
|
|
|
+ className="resize-y w-full"
|
|
|
rows={4}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
data-testid={`${getCurrentMode()?.slug || "code"}-prompt-textarea`}
|
|
|
/>
|
|
|
</div>
|
|
|
{/* Mode settings */}
|
|
|
<>
|
|
|
- <div style={{ marginBottom: "12px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
- {t("prompts:apiConfiguration.title")}
|
|
|
- </div>
|
|
|
- <div style={{ marginBottom: "8px" }}>
|
|
|
- <VSCodeDropdown
|
|
|
- value={currentApiConfigName || ""}
|
|
|
- onChange={(e: any) => {
|
|
|
- const value = e.detail?.target?.value || e.target?.value
|
|
|
+ <div className="mb-3">
|
|
|
+ <div className="font-bold mb-1">{t("prompts:apiConfiguration.title")}</div>
|
|
|
+ <div className="mb-2">
|
|
|
+ <Select
|
|
|
+ value={currentApiConfigName}
|
|
|
+ onValueChange={(value) => {
|
|
|
vscode.postMessage({
|
|
|
type: "loadApiConfiguration",
|
|
|
text: value,
|
|
|
})
|
|
|
- }}
|
|
|
- className="w-full">
|
|
|
- {(listApiConfigMeta || []).map((config) => (
|
|
|
- <VSCodeOption key={config.id} value={config.name}>
|
|
|
- {config.name}
|
|
|
- </VSCodeOption>
|
|
|
- ))}
|
|
|
- </VSCodeDropdown>
|
|
|
+ }}>
|
|
|
+ <SelectTrigger className="w-full">
|
|
|
+ <SelectValue placeholder={t("settings:common.select")} />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ {(listApiConfigMeta || []).map((config) => (
|
|
|
+ <SelectItem key={config.id} value={config.name}>
|
|
|
+ {config.name}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
<div className="text-xs mt-1.5 text-vscode-descriptionForeground">
|
|
|
{t("prompts:apiConfiguration.select")}
|
|
|
</div>
|
|
|
@@ -721,15 +802,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</>
|
|
|
|
|
|
{/* Role definition for both built-in and custom modes */}
|
|
|
- <div style={{ marginBottom: "8px" }}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "flex",
|
|
|
- justifyContent: "space-between",
|
|
|
- alignItems: "center",
|
|
|
- marginBottom: "4px",
|
|
|
- }}>
|
|
|
- <div style={{ fontWeight: "bold" }}>{t("prompts:customInstructions.title")}</div>
|
|
|
+ <div className="mb-2">
|
|
|
+ <div className="flex justify-between items-center mb-1">
|
|
|
+ <div className="font-bold">{t("prompts:customInstructions.title")}</div>
|
|
|
{!findModeBySlug(visualMode, customModes) && (
|
|
|
<Button
|
|
|
variant="ghost"
|
|
|
@@ -746,17 +821,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</Button>
|
|
|
)}
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "13px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginBottom: "8px",
|
|
|
- }}>
|
|
|
+ <div className="text-[13px] text-vscode-descriptionForeground mb-2">
|
|
|
{t("prompts:customInstructions.description", {
|
|
|
modeName: getCurrentMode()?.name || "Code",
|
|
|
})}
|
|
|
</div>
|
|
|
- <VSCodeTextArea
|
|
|
+ <Textarea
|
|
|
value={(() => {
|
|
|
const customMode = findModeBySlug(visualMode, customModes)
|
|
|
const prompt = customModePrompts?.[visualMode] as PromptComponent
|
|
|
@@ -768,7 +838,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
})()}
|
|
|
onChange={(e) => {
|
|
|
const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
+ (e as unknown as CustomEvent)?.detail?.target?.value ||
|
|
|
((e as any).target as HTMLTextAreaElement).value
|
|
|
const customMode = findModeBySlug(visualMode, customModes)
|
|
|
if (customMode) {
|
|
|
@@ -788,16 +858,10 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
}
|
|
|
}}
|
|
|
rows={4}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full resize-y"
|
|
|
data-testid={`${getCurrentMode()?.slug || "code"}-custom-instructions-textarea`}
|
|
|
/>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "12px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginTop: "5px",
|
|
|
- }}>
|
|
|
+ <div className="text-xs text-vscode-descriptionForeground mt-1.5">
|
|
|
<Trans
|
|
|
i18nKey="prompts:customInstructions.loadFromFile"
|
|
|
values={{
|
|
|
@@ -807,11 +871,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
components={{
|
|
|
span: (
|
|
|
<span
|
|
|
- style={{
|
|
|
- color: "var(--vscode-textLink-foreground)",
|
|
|
- cursor: "pointer",
|
|
|
- textDecoration: "underline",
|
|
|
- }}
|
|
|
+ className="text-vscode-textLink-foreground cursor-pointer underline"
|
|
|
onClick={() => {
|
|
|
const currentMode = getCurrentMode()
|
|
|
if (!currentMode) return
|
|
|
@@ -834,13 +894,8 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div
|
|
|
- style={{
|
|
|
- paddingBottom: "40px",
|
|
|
- marginBottom: "20px",
|
|
|
- borderBottom: "1px solid var(--vscode-input-border)",
|
|
|
- }}>
|
|
|
- <div style={{ display: "flex", gap: "8px" }}>
|
|
|
+ <div className="pb-4 border-b border-vscode-input-border">
|
|
|
+ <div className="flex gap-2">
|
|
|
<Button
|
|
|
variant="default"
|
|
|
onClick={() => {
|
|
|
@@ -874,7 +929,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
|
|
|
{/* Custom System Prompt Disclosure */}
|
|
|
- <div className="mt-12">
|
|
|
+ <div className="mt-4">
|
|
|
<button
|
|
|
onClick={() => setIsSystemPromptDisclosureOpen(!isSystemPromptDisclosureOpen)}
|
|
|
className="flex items-center text-xs text-vscode-foreground hover:text-vscode-textLink-foreground focus:outline-none"
|
|
|
@@ -918,18 +973,18 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
|
|
|
<div className="pb-5 border-b border-vscode-input-border">
|
|
|
- <h3 style={{ color: "var(--vscode-foreground)", marginBottom: "12px" }}>
|
|
|
- {t("prompts:globalCustomInstructions.title")}
|
|
|
- </h3>
|
|
|
+ <h3 className="text-vscode-foreground mb-3">{t("prompts:globalCustomInstructions.title")}</h3>
|
|
|
|
|
|
<div className="text-sm text-vscode-descriptionForeground mb-2">
|
|
|
- {t("prompts:globalCustomInstructions.description", { language: i18next.language })}
|
|
|
+ {t("prompts:globalCustomInstructions.description", {
|
|
|
+ language: i18next.language,
|
|
|
+ })}
|
|
|
</div>
|
|
|
- <VSCodeTextArea
|
|
|
- value={customInstructions ?? ""}
|
|
|
+ <Textarea
|
|
|
+ value={customInstructions}
|
|
|
onChange={(e) => {
|
|
|
const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
+ (e as unknown as CustomEvent)?.detail?.target?.value ||
|
|
|
((e as any).target as HTMLTextAreaElement).value
|
|
|
setCustomInstructions(value || undefined)
|
|
|
vscode.postMessage({
|
|
|
@@ -938,21 +993,16 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
})
|
|
|
}}
|
|
|
rows={4}
|
|
|
- resize="vertical"
|
|
|
- className="w-full"
|
|
|
+ className="w-full resize-y"
|
|
|
data-testid="global-custom-instructions-textarea"
|
|
|
/>
|
|
|
- <div className="text-xs text-vscode-descriptionForeground mt-1.5 mb-10">
|
|
|
+ <div className="text-xs text-vscode-descriptionForeground mt-1.5">
|
|
|
<Trans
|
|
|
i18nKey="prompts:globalCustomInstructions.loadFromFile"
|
|
|
components={{
|
|
|
span: (
|
|
|
<span
|
|
|
- style={{
|
|
|
- color: "var(--vscode-textLink-foreground)",
|
|
|
- cursor: "pointer",
|
|
|
- textDecoration: "underline",
|
|
|
- }}
|
|
|
+ className="text-vscode-textLink-foreground cursor-pointer underline"
|
|
|
onClick={() =>
|
|
|
vscode.postMessage({
|
|
|
type: "openFile",
|
|
|
@@ -970,156 +1020,113 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div
|
|
|
- style={{
|
|
|
- marginTop: "20px",
|
|
|
- paddingBottom: "60px",
|
|
|
- borderBottom: "1px solid var(--vscode-input-border)",
|
|
|
- }}>
|
|
|
- <h3 style={{ color: "var(--vscode-foreground)", marginBottom: "12px" }}>
|
|
|
- {t("prompts:supportPrompts.title")}
|
|
|
- </h3>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "flex",
|
|
|
- gap: "8px",
|
|
|
- alignItems: "center",
|
|
|
- marginBottom: "12px",
|
|
|
- flexWrap: "wrap",
|
|
|
- padding: "4px 0",
|
|
|
- }}>
|
|
|
- {Object.keys(supportPrompt.default).map((type) => (
|
|
|
- <button
|
|
|
- key={type}
|
|
|
- data-testid={`${type}-tab`}
|
|
|
- data-active={activeSupportTab === type ? "true" : "false"}
|
|
|
- onClick={() => setActiveSupportTab(type as SupportPromptType)}
|
|
|
- style={{
|
|
|
- padding: "4px 8px",
|
|
|
- border: "none",
|
|
|
- background: activeSupportTab === type ? "var(--vscode-button-background)" : "none",
|
|
|
- color:
|
|
|
- activeSupportTab === type
|
|
|
- ? "var(--vscode-button-foreground)"
|
|
|
- : "var(--vscode-foreground)",
|
|
|
- cursor: "pointer",
|
|
|
- opacity: activeSupportTab === type ? 1 : 0.8,
|
|
|
- borderRadius: "3px",
|
|
|
- fontWeight: "bold",
|
|
|
- }}>
|
|
|
- {t(`prompts:supportPrompts.types.${type}.label`)}
|
|
|
- </button>
|
|
|
- ))}
|
|
|
+ <div className="mt-5 pb-15 border-b border-vscode-input-border">
|
|
|
+ <h3 className="text-vscode-foreground mb-3">{t("prompts:supportPrompts.title")}</h3>
|
|
|
+ <div className="flex gap-4 items-center flex-wrap py-1">
|
|
|
+ <Select
|
|
|
+ value={activeSupportOption}
|
|
|
+ onValueChange={(type) => setActiveSupportOption(type as SupportPromptType)}>
|
|
|
+ <SelectTrigger className="w-full" data-testid="support-prompt-select-trigger">
|
|
|
+ <SelectValue placeholder={t("settings:common.select")} />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ {Object.keys(supportPrompt.default).map((type) => (
|
|
|
+ <SelectItem key={type} value={type} data-testid={`${type}-option`}>
|
|
|
+ {t(`prompts:supportPrompts.types.${type}.label`)}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
|
|
|
{/* Support prompt description */}
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "13px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- margin: "8px 0 16px",
|
|
|
- }}>
|
|
|
- {t(`prompts:supportPrompts.types.${activeSupportTab}.description`)}
|
|
|
+ <div className="text-[13px] text-vscode-descriptionForeground my-2 mb-4">
|
|
|
+ {t(`prompts:supportPrompts.types.${activeSupportOption}.description`)}
|
|
|
</div>
|
|
|
|
|
|
- {/* Show active tab content */}
|
|
|
- <div key={activeSupportTab}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "flex",
|
|
|
- justifyContent: "space-between",
|
|
|
- alignItems: "center",
|
|
|
- marginBottom: "4px",
|
|
|
- }}>
|
|
|
- <div style={{ fontWeight: "bold" }}>{t("prompts:supportPrompts.prompt")}</div>
|
|
|
+ <div key={activeSupportOption}>
|
|
|
+ <div className="flex justify-between items-center mb-1">
|
|
|
+ <div className="font-bold">{t("prompts:supportPrompts.prompt")}</div>
|
|
|
<Button
|
|
|
variant="ghost"
|
|
|
size="icon"
|
|
|
- onClick={() => handleSupportReset(activeSupportTab)}
|
|
|
- title={t("prompts:supportPrompts.resetPrompt", { promptType: activeSupportTab })}>
|
|
|
+ onClick={() => handleSupportReset(activeSupportOption)}
|
|
|
+ title={t("prompts:supportPrompts.resetPrompt", {
|
|
|
+ promptType: activeSupportOption,
|
|
|
+ })}>
|
|
|
<span className="codicon codicon-discard"></span>
|
|
|
</Button>
|
|
|
</div>
|
|
|
|
|
|
- <VSCodeTextArea
|
|
|
- value={getSupportPromptValue(activeSupportTab)}
|
|
|
+ <Textarea
|
|
|
+ value={getSupportPromptValue(activeSupportOption)}
|
|
|
onChange={(e) => {
|
|
|
const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
+ (e as unknown as CustomEvent)?.detail?.target?.value ||
|
|
|
((e as any).target as HTMLTextAreaElement).value
|
|
|
const trimmedValue = value.trim()
|
|
|
- updateSupportPrompt(activeSupportTab, trimmedValue || undefined)
|
|
|
+ updateSupportPrompt(activeSupportOption, trimmedValue || undefined)
|
|
|
}}
|
|
|
rows={6}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="resize-y w-full"
|
|
|
/>
|
|
|
|
|
|
- {activeSupportTab === "ENHANCE" && (
|
|
|
+ {activeSupportOption === "ENHANCE" && (
|
|
|
<>
|
|
|
<div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- color: "var(--vscode-foreground)",
|
|
|
- fontSize: "13px",
|
|
|
- marginBottom: "20px",
|
|
|
- marginTop: "5px",
|
|
|
- }}></div>
|
|
|
- <div style={{ marginBottom: "12px" }}>
|
|
|
- <div style={{ marginBottom: "8px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
+ <div className="text-vscode-foreground text-[13px] mb-5 mt-1.5"></div>
|
|
|
+ <div className="mb-3">
|
|
|
+ <div className="mb-2">
|
|
|
+ <div className="font-bold mb-1">
|
|
|
{t("prompts:supportPrompts.enhance.apiConfiguration")}
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "13px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- }}>
|
|
|
+ <div className="text-[13px] text-vscode-descriptionForeground">
|
|
|
{t("prompts:supportPrompts.enhance.apiConfigDescription")}
|
|
|
</div>
|
|
|
</div>
|
|
|
- <VSCodeDropdown
|
|
|
- value={enhancementApiConfigId || ""}
|
|
|
- data-testid="api-config-dropdown"
|
|
|
- onChange={(e: any) => {
|
|
|
- const value = e.detail?.target?.value || e.target?.value
|
|
|
- setEnhancementApiConfigId(value)
|
|
|
+ <Select
|
|
|
+ value={enhancementApiConfigId || "-"}
|
|
|
+ onValueChange={(value) => {
|
|
|
+ // normalise to empty string for empty value
|
|
|
+ // because we can't use it directly for the select element
|
|
|
+ setEnhancementApiConfigId(value === "-" ? "" : value)
|
|
|
vscode.postMessage({
|
|
|
type: "enhancementApiConfigId",
|
|
|
text: value,
|
|
|
})
|
|
|
- }}
|
|
|
- style={{ width: "300px" }}>
|
|
|
- <VSCodeOption value="">
|
|
|
- {t("prompts:supportPrompts.enhance.useCurrentConfig")}
|
|
|
- </VSCodeOption>
|
|
|
- {(listApiConfigMeta || []).map((config) => (
|
|
|
- <VSCodeOption key={config.id} value={config.id}>
|
|
|
- {config.name}
|
|
|
- </VSCodeOption>
|
|
|
- ))}
|
|
|
- </VSCodeDropdown>
|
|
|
+ }}>
|
|
|
+ <SelectTrigger data-testid="api-config-select" className="w-full">
|
|
|
+ <SelectValue
|
|
|
+ placeholder={t("prompts:supportPrompts.enhance.useCurrentConfig")}
|
|
|
+ />
|
|
|
+ </SelectTrigger>
|
|
|
+ <SelectContent>
|
|
|
+ <SelectItem value="-">
|
|
|
+ {t("prompts:supportPrompts.enhance.useCurrentConfig")}
|
|
|
+ </SelectItem>
|
|
|
+ {(listApiConfigMeta || []).map((config) => (
|
|
|
+ <SelectItem
|
|
|
+ key={config.id}
|
|
|
+ value={config.id}
|
|
|
+ data-testid={`${config.id}-option`}>
|
|
|
+ {config.name}
|
|
|
+ </SelectItem>
|
|
|
+ ))}
|
|
|
+ </SelectContent>
|
|
|
+ </Select>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div style={{ marginTop: "12px" }}>
|
|
|
- <VSCodeTextArea
|
|
|
+ <div className="mt-4">
|
|
|
+ <Textarea
|
|
|
value={testPrompt}
|
|
|
onChange={(e) => setTestPrompt((e.target as HTMLTextAreaElement).value)}
|
|
|
placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")}
|
|
|
rows={3}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full resize-y"
|
|
|
data-testid="test-prompt-textarea"
|
|
|
/>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- marginTop: "8px",
|
|
|
- display: "flex",
|
|
|
- justifyContent: "flex-start",
|
|
|
- alignItems: "center",
|
|
|
- gap: 8,
|
|
|
- }}>
|
|
|
+ <div className="mt-2 flex justify-start items-center gap-2">
|
|
|
<Button
|
|
|
variant="default"
|
|
|
onClick={handleTestEnhancement}
|
|
|
@@ -1135,92 +1142,50 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</TabContent>
|
|
|
|
|
|
{isCreateModeDialogOpen && (
|
|
|
- <div
|
|
|
- style={{
|
|
|
- position: "fixed",
|
|
|
- inset: 0,
|
|
|
- display: "flex",
|
|
|
- justifyContent: "flex-end",
|
|
|
- backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
|
- zIndex: 1000,
|
|
|
- }}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- width: "calc(100vw - 100px)",
|
|
|
- height: "100%",
|
|
|
- backgroundColor: "var(--vscode-editor-background)",
|
|
|
- boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.2)",
|
|
|
- display: "flex",
|
|
|
- flexDirection: "column",
|
|
|
- position: "relative",
|
|
|
- }}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- flex: 1,
|
|
|
- padding: "20px",
|
|
|
- overflowY: "auto",
|
|
|
- minHeight: 0,
|
|
|
- }}>
|
|
|
+ <div className="fixed inset-0 flex justify-end bg-black/50 z-[1000]">
|
|
|
+ <div className="w-[calc(100vw-100px)] h-full bg-vscode-editor-background shadow-md flex flex-col relative">
|
|
|
+ <div className="flex-1 p-5 overflow-y-auto min-h-0">
|
|
|
<Button
|
|
|
variant="ghost"
|
|
|
size="icon"
|
|
|
onClick={() => setIsCreateModeDialogOpen(false)}
|
|
|
- style={{
|
|
|
- position: "absolute",
|
|
|
- top: "20px",
|
|
|
- right: "20px",
|
|
|
- }}>
|
|
|
+ className="absolute top-5 right-5">
|
|
|
<span className="codicon codicon-close"></span>
|
|
|
</Button>
|
|
|
- <h2 style={{ margin: "0 0 16px" }}>{t("prompts:createModeDialog.title")}</h2>
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
- {t("prompts:createModeDialog.name.label")}
|
|
|
- </div>
|
|
|
- <VSCodeTextField
|
|
|
+ <h2 className="mb-4">{t("prompts:createModeDialog.title")}</h2>
|
|
|
+ <div className="mb-4">
|
|
|
+ <div className="font-bold mb-1">{t("prompts:createModeDialog.name.label")}</div>
|
|
|
+ <Input
|
|
|
+ type="text"
|
|
|
value={newModeName}
|
|
|
- onChange={(e: Event | React.FormEvent<HTMLElement>) => {
|
|
|
- const target =
|
|
|
- (e as CustomEvent)?.detail?.target ||
|
|
|
- ((e as any).target as HTMLInputElement)
|
|
|
- handleNameChange(target.value)
|
|
|
+ onChange={(e) => {
|
|
|
+ handleNameChange(e.target.value)
|
|
|
}}
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full"
|
|
|
/>
|
|
|
{nameError && (
|
|
|
<div className="text-xs text-vscode-errorForeground mt-1">{nameError}</div>
|
|
|
)}
|
|
|
</div>
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
- {t("prompts:createModeDialog.slug.label")}
|
|
|
- </div>
|
|
|
- <VSCodeTextField
|
|
|
+ <div className="mb-4">
|
|
|
+ <div className="font-bold mb-1">{t("prompts:createModeDialog.slug.label")}</div>
|
|
|
+ <Input
|
|
|
+ type="text"
|
|
|
value={newModeSlug}
|
|
|
- onChange={(e: Event | React.FormEvent<HTMLElement>) => {
|
|
|
- const target =
|
|
|
- (e as CustomEvent)?.detail?.target ||
|
|
|
- ((e as any).target as HTMLInputElement)
|
|
|
- setNewModeSlug(target.value)
|
|
|
+ onChange={(e) => {
|
|
|
+ setNewModeSlug(e.target.value)
|
|
|
}}
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full"
|
|
|
/>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "12px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginTop: "4px",
|
|
|
- }}>
|
|
|
+ <div className="text-xs text-vscode-descriptionForeground mt-1">
|
|
|
{t("prompts:createModeDialog.slug.description")}
|
|
|
</div>
|
|
|
{slugError && (
|
|
|
<div className="text-xs text-vscode-errorForeground mt-1">{slugError}</div>
|
|
|
)}
|
|
|
</div>
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
- {t("prompts:createModeDialog.saveLocation.label")}
|
|
|
- </div>
|
|
|
+ <div className="mb-4">
|
|
|
+ <div className="font-bold mb-1">{t("prompts:createModeDialog.saveLocation.label")}</div>
|
|
|
<div className="text-sm text-vscode-descriptionForeground mb-2">
|
|
|
{t("prompts:createModeDialog.saveLocation.description")}
|
|
|
</div>
|
|
|
@@ -1233,12 +1198,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
}}>
|
|
|
<VSCodeRadio value="global">
|
|
|
{t("prompts:createModeDialog.saveLocation.global.label")}
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "12px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginTop: "2px",
|
|
|
- }}>
|
|
|
+ <div className="text-xs text-vscode-descriptionForeground mt-0.5">
|
|
|
{t("prompts:createModeDialog.saveLocation.global.description")}
|
|
|
</div>
|
|
|
</VSCodeRadio>
|
|
|
@@ -1263,17 +1223,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
}}>
|
|
|
{t("prompts:createModeDialog.roleDefinition.description")}
|
|
|
</div>
|
|
|
- <VSCodeTextArea
|
|
|
+ <Textarea
|
|
|
value={newModeRoleDefinition}
|
|
|
onChange={(e) => {
|
|
|
- const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
- ((e as any).target as HTMLTextAreaElement).value
|
|
|
- setNewModeRoleDefinition(value)
|
|
|
+ setNewModeRoleDefinition(e.target.value)
|
|
|
}}
|
|
|
rows={4}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full resize-y"
|
|
|
/>
|
|
|
{roleDefinitionError && (
|
|
|
<div className="text-xs text-vscode-errorForeground mt-1">
|
|
|
@@ -1281,24 +1237,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
- {t("prompts:createModeDialog.tools.label")}
|
|
|
- </div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "13px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginBottom: "8px",
|
|
|
- }}>
|
|
|
+ <div className="mb-4">
|
|
|
+ <div className="font-bold mb-1">{t("prompts:createModeDialog.tools.label")}</div>
|
|
|
+ <div className="text-[13px] text-vscode-descriptionForeground mb-2">
|
|
|
{t("prompts:createModeDialog.tools.description")}
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "grid",
|
|
|
- gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
|
|
|
- gap: "8px",
|
|
|
- }}>
|
|
|
+ <div className="grid grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-2">
|
|
|
{availableGroups.map((group) => (
|
|
|
<VSCodeCheckbox
|
|
|
key={group}
|
|
|
@@ -1323,41 +1267,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
<div className="text-xs text-vscode-errorForeground mt-1">{groupsError}</div>
|
|
|
)}
|
|
|
</div>
|
|
|
- <div style={{ marginBottom: "16px" }}>
|
|
|
- <div style={{ fontWeight: "bold", marginBottom: "4px" }}>
|
|
|
+ <div className="mb-4">
|
|
|
+ <div className="font-bold mb-1">
|
|
|
{t("prompts:createModeDialog.customInstructions.label")}
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- fontSize: "13px",
|
|
|
- color: "var(--vscode-descriptionForeground)",
|
|
|
- marginBottom: "8px",
|
|
|
- }}>
|
|
|
+ <div className="text-[13px] text-vscode-descriptionForeground mb-2">
|
|
|
{t("prompts:createModeDialog.customInstructions.description")}
|
|
|
</div>
|
|
|
- <VSCodeTextArea
|
|
|
+ <Textarea
|
|
|
value={newModeCustomInstructions}
|
|
|
onChange={(e) => {
|
|
|
- const value =
|
|
|
- (e as CustomEvent)?.detail?.target?.value ||
|
|
|
- ((e as any).target as HTMLTextAreaElement).value
|
|
|
- setNewModeCustomInstructions(value)
|
|
|
+ setNewModeCustomInstructions(e.target.value)
|
|
|
}}
|
|
|
rows={4}
|
|
|
- resize="vertical"
|
|
|
- style={{ width: "100%" }}
|
|
|
+ className="w-full resize-y"
|
|
|
/>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "flex",
|
|
|
- justifyContent: "flex-end",
|
|
|
- padding: "12px 20px",
|
|
|
- gap: "8px",
|
|
|
- borderTop: "1px solid var(--vscode-editor-lineHighlightBorder)",
|
|
|
- backgroundColor: "var(--vscode-editor-background)",
|
|
|
- }}>
|
|
|
+ <div className="flex justify-end p-3 px-5 gap-2 border-t border-vscode-editor-lineHighlightBorder bg-vscode-editor-background">
|
|
|
<Button variant="secondary" onClick={() => setIsCreateModeDialogOpen(false)}>
|
|
|
{t("prompts:createModeDialog.buttons.cancel")}
|
|
|
</Button>
|
|
|
@@ -1370,71 +1297,27 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
|
|
)}
|
|
|
|
|
|
{isDialogOpen && (
|
|
|
- <div
|
|
|
- style={{
|
|
|
- position: "fixed",
|
|
|
- inset: 0,
|
|
|
- display: "flex",
|
|
|
- justifyContent: "flex-end",
|
|
|
- backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
|
- zIndex: 1000,
|
|
|
- }}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- width: "calc(100vw - 100px)",
|
|
|
- height: "100%",
|
|
|
- backgroundColor: "var(--vscode-editor-background)",
|
|
|
- boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.2)",
|
|
|
- display: "flex",
|
|
|
- flexDirection: "column",
|
|
|
- position: "relative",
|
|
|
- }}>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- flex: 1,
|
|
|
- padding: "20px",
|
|
|
- overflowY: "auto",
|
|
|
- minHeight: 0,
|
|
|
- }}>
|
|
|
+ <div className="fixed inset-0 flex justify-end bg-black/50 z-[1000]">
|
|
|
+ <div className="w-[calc(100vw-100px)] h-full bg-vscode-editor-background shadow-md flex flex-col relative">
|
|
|
+ <div className="flex-1 p-5 overflow-y-auto min-h-0">
|
|
|
<Button
|
|
|
variant="ghost"
|
|
|
size="icon"
|
|
|
onClick={() => setIsDialogOpen(false)}
|
|
|
- style={{
|
|
|
- position: "absolute",
|
|
|
- top: "20px",
|
|
|
- right: "20px",
|
|
|
- }}>
|
|
|
+ className="absolute top-5 right-5">
|
|
|
<span className="codicon codicon-close"></span>
|
|
|
</Button>
|
|
|
- <h2 style={{ margin: "0 0 16px" }}>
|
|
|
+ <h2 className="mb-4">
|
|
|
{selectedPromptTitle ||
|
|
|
- t("prompts:systemPrompt.title", { modeName: getCurrentMode()?.name || "Code" })}
|
|
|
+ t("prompts:systemPrompt.title", {
|
|
|
+ modeName: getCurrentMode()?.name || "Code",
|
|
|
+ })}
|
|
|
</h2>
|
|
|
- <pre
|
|
|
- style={{
|
|
|
- padding: "8px",
|
|
|
- whiteSpace: "pre-wrap",
|
|
|
- wordBreak: "break-word",
|
|
|
- fontFamily: "var(--vscode-editor-font-family)",
|
|
|
- fontSize: "var(--vscode-editor-font-size)",
|
|
|
- color: "var(--vscode-editor-foreground)",
|
|
|
- backgroundColor: "var(--vscode-editor-background)",
|
|
|
- border: "1px solid var(--vscode-editor-lineHighlightBorder)",
|
|
|
- borderRadius: "4px",
|
|
|
- overflowY: "auto",
|
|
|
- }}>
|
|
|
+ <pre className="p-2 whitespace-pre-wrap break-words font-mono text-vscode-editor-font-size text-vscode-editor-foreground bg-vscode-editor-background border border-vscode-editor-lineHighlightBorder rounded overflow-y-auto">
|
|
|
{selectedPromptContent}
|
|
|
</pre>
|
|
|
</div>
|
|
|
- <div
|
|
|
- style={{
|
|
|
- display: "flex",
|
|
|
- justifyContent: "flex-end",
|
|
|
- padding: "12px 20px",
|
|
|
- borderTop: "1px solid var(--vscode-editor-lineHighlightBorder)",
|
|
|
- backgroundColor: "var(--vscode-editor-background)",
|
|
|
- }}>
|
|
|
+ <div className="flex justify-end p-3 px-5 border-t border-vscode-editor-lineHighlightBorder bg-vscode-editor-background">
|
|
|
<Button variant="secondary" onClick={() => setIsDialogOpen(false)}>
|
|
|
{t("prompts:createModeDialog.close")}
|
|
|
</Button>
|