| author | |
| committer | |
| log | 5c66110e269cc5a852763e5f06a81f2a763d3181 |
| tree | 988805f7a6913fd53b48b92667c0a92f0a225737 |
| parent | da3c2030f710c4e1243035613eda94f6526afa1f |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: codex <codex@users.noreply.github.com>3 files changed, 131 insertions(+), 88 deletions(-)
apps/web/src/components/chat/ChatComposer.tsx+19-9| ... | @@ -95,6 +95,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; | ... | @@ -95,6 +95,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; |
| 95 | import { toastManager } from "../ui/toast"; | 95 | import { toastManager } from "../ui/toast"; |
| 96 | import { | 96 | import { |
| 97 | BotIcon, | 97 | BotIcon, |
| 98 | CheckIcon, | ||
| 98 | CircleAlertIcon, | 99 | CircleAlertIcon, |
| 99 | ListTodoIcon, | 100 | ListTodoIcon, |
| 100 | PencilRulerIcon, | 101 | PencilRulerIcon, |
| ... | @@ -273,16 +274,25 @@ const ComposerFooterModeControls = memo(function ComposerFooterModeControls(prop | ... | @@ -273,16 +274,25 @@ const ComposerFooterModeControls = memo(function ComposerFooterModeControls(prop |
| 273 | {runtimeModeOptions.map((mode) => { | 274 | {runtimeModeOptions.map((mode) => { |
| 274 | const option = runtimeModeConfig[mode]; | 275 | const option = runtimeModeConfig[mode]; |
| 275 | const OptionIcon = option.icon; | 276 | const OptionIcon = option.icon; |
| 277 | const isSelected = props.runtimeMode === mode; | ||
| 276 | return ( | 278 | return ( |
| 277 | <SelectItem key={mode} value={mode} className="min-w-64 py-2"> | 279 | <SelectItem key={mode} value={mode} hideIndicator className="min-w-64 py-2"> |
| 278 | <div className="grid min-w-0 gap-0.5"> | 280 | <div className="flex min-w-0 items-center gap-3"> |
| 279 | <span className="inline-flex items-center gap-1.5 font-medium text-foreground"> | 281 | <div className="grid min-w-0 flex-1 gap-0.5"> |
| 280 | <OptionIcon className="size-3.5 shrink-0 text-muted-foreground" /> | 282 | <span className="inline-flex items-center gap-1.5 font-medium text-foreground"> |
| 281 | {option.label} | 283 | <OptionIcon className="size-3.5 shrink-0 text-muted-foreground" /> |
| 282 | </span> | 284 | {option.label} |
| 283 | <span className="text-muted-foreground text-xs leading-4"> | 285 | </span> |
| 284 | {option.description} | 286 | <span className="text-muted-foreground text-xs leading-4"> |
| 285 | </span> | 287 | {option.description} |
| 288 | </span> | ||
| 289 | </div> | ||
| 290 | <CheckIcon | ||
| 291 | className={cn( | ||
| 292 | "size-4 text-blue-400", | ||
| 293 | isSelected ? "opacity-100" : "opacity-0", | ||
| 294 | )} | ||
| 295 | /> | ||
| 286 | </div> | 296 | </div> |
| 287 | </SelectItem> | 297 | </SelectItem> |
| 288 | ); | 298 | ); |
apps/web/src/components/chat/TraitsPicker.tsx+84-61| ... | @@ -16,7 +16,7 @@ import { | ... | @@ -16,7 +16,7 @@ import { |
| 16 | } from "@t3tools/shared/model"; | 16 | } from "@t3tools/shared/model"; |
| 17 | import { memo, useCallback, useState } from "react"; | 17 | import { memo, useCallback, useState } from "react"; |
| 18 | import type { VariantProps } from "class-variance-authority"; | 18 | import type { VariantProps } from "class-variance-authority"; |
| 19 | import { ChevronDownIcon } from "lucide-react"; | 19 | import { CheckIcon, ChevronDownIcon } from "lucide-react"; |
| 20 | import { Button, buttonVariants } from "../ui/button"; | 20 | import { Button, buttonVariants } from "../ui/button"; |
| 21 | import { | 21 | import { |
| 22 | Menu, | 22 | Menu, |
| ... | @@ -297,67 +297,90 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ | ... | @@ -297,67 +297,90 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ |
| 297 | 297 | ||
| 298 | return ( | 298 | return ( |
| 299 | <> | 299 | <> |
| 300 | {selectDescriptors.map((descriptor, index) => ( | 300 | {selectDescriptors.map((descriptor, index) => { |
| 301 | <div key={descriptor.id}> | 301 | const selectedValue = |
| 302 | {index > 0 ? <MenuDivider /> : null} | 302 | ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id |
| 303 | <MenuGroup> | 303 | ? "ultrathink" |
| 304 | <div className="px-2 pt-1.5 pb-1 font-medium text-muted-foreground text-xs"> | 304 | : (getDescriptorStringValue(descriptor) ?? ""); |
| 305 | {descriptor.label} | 305 | |
| 306 | </div> | 306 | return ( |
| 307 | {ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id ? ( | 307 | <div key={descriptor.id}> |
| 308 | <div className="px-2 pb-1.5 text-muted-foreground/80 text-xs"> | 308 | {index > 0 ? <MenuDivider /> : null} |
| 309 | Your prompt contains &quot;ultrathink&quot; in the text. Remove it to change this | 309 | <MenuGroup> |
| 310 | option. | 310 | <div className="px-2 pt-1.5 pb-1 font-medium text-muted-foreground text-xs"> |
| 311 | {descriptor.label} | ||
| 312 | </div> | ||
| 313 | {ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id ? ( | ||
| 314 | <div className="px-2 pb-1.5 text-muted-foreground/80 text-xs"> | ||
| 315 | Your prompt contains &quot;ultrathink&quot; in the text. Remove it to change this | ||
| 316 | option. | ||
| 317 | </div> | ||
| 318 | ) : null} | ||
| 319 | <MenuRadioGroup | ||
| 320 | value={selectedValue} | ||
| 321 | onValueChange={(value) => handleSelectChange(descriptor, value)} | ||
| 322 | > | ||
| 323 | {descriptor.options.map((option) => ( | ||
| 324 | <MenuRadioItem | ||
| 325 | key={option.id} | ||
| 326 | value={option.id} | ||
| 327 | hideIndicator | ||
| 328 | disabled={ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id} | ||
| 329 | > | ||
| 330 | <span className="flex w-full min-w-0 items-center justify-between gap-3"> | ||
| 331 | <span className="min-w-0 truncate"> | ||
| 332 | {option.label} | ||
| 333 | {option.isDefault ? ( | ||
| 334 | <> | ||
| 335 | {" "} | ||
| 336 | <DefaultBadge /> | ||
| 337 | </> | ||
| 338 | ) : null} | ||
| 339 | </span> | ||
| 340 | {option.id === selectedValue ? ( | ||
| 341 | <CheckIcon className="size-3.5 shrink-0 text-blue-400" /> | ||
| 342 | ) : null} | ||
| 343 | </span> | ||
| 344 | </MenuRadioItem> | ||
| 345 | ))} | ||
| 346 | </MenuRadioGroup> | ||
| 347 | </MenuGroup> | ||
| 348 | </div> | ||
| 349 | ); | ||
| 350 | })} | ||
| 351 | {booleanDescriptors.map((descriptor, index) => { | ||
| 352 | const selectedValue = descriptor.currentValue === true ? "on" : "off"; | ||
| 353 | |||
| 354 | return ( | ||
| 355 | <div key={descriptor.id}> | ||
| 356 | {index > 0 || selectDescriptors.length > 0 ? <MenuDivider /> : null} | ||
| 357 | <MenuGroup> | ||
| 358 | <div className="px-2 py-1.5 font-medium text-muted-foreground text-xs"> | ||
| 359 | {descriptor.label} | ||
| 311 | </div> | 360 | </div> |
| 312 | ) : null} | 361 | <MenuRadioGroup |
| 313 | <MenuRadioGroup | 362 | value={selectedValue} |
| 314 | value={ | 363 | onValueChange={(value) => { |
| 315 | ultrathinkPromptControlled && descriptor.id === primarySelectDescriptor?.id | 364 | updateDescriptors( |
| 316 | ? "ultrathink" | 365 | replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"), |
| 317 | : (getDescriptorStringValue(descriptor) ?? "") | 366 | ); |
| 318 | } | 367 | }} |
| 319 | onValueChange={(value) => handleSelectChange(descriptor, value)} | 368 | > |
| 320 | > | 369 | {(["on", "off"] as const).map((value) => ( |
| 321 | {descriptor.options.map((option) => ( | 370 | <MenuRadioItem key={value} value={value} hideIndicator> |
| 322 | <MenuRadioItem | 371 | <span className="flex w-full min-w-0 items-center justify-between gap-3"> |
| 323 | key={option.id} | 372 | <span>{value === "on" ? "On" : "Off"}</span> |
| 324 | value={option.id} | 373 | {value === selectedValue ? ( |
| 325 | disabled={ultrathinkInBodyText && descriptor.id === primarySelectDescriptor?.id} | 374 | <CheckIcon className="size-3.5 shrink-0 text-blue-400" /> |
| 326 | > | 375 | ) : null} |
| 327 | {option.label} | 376 | </span> |
| 328 | {option.isDefault ? ( | 377 | </MenuRadioItem> |
| 329 | <> | 378 | ))} |
| 330 | {" "} | 379 | </MenuRadioGroup> |
| 331 | <DefaultBadge /> | 380 | </MenuGroup> |
| 332 | </> | 381 | </div> |
| 333 | ) : null} | 382 | ); |
| 334 | </MenuRadioItem> | 383 | })} |
| 335 | ))} | ||
| 336 | </MenuRadioGroup> | ||
| 337 | </MenuGroup> | ||
| 338 | </div> | ||
| 339 | ))} | ||
| 340 | {booleanDescriptors.map((descriptor, index) => ( | ||
| 341 | <div key={descriptor.id}> | ||
| 342 | {index > 0 || selectDescriptors.length > 0 ? <MenuDivider /> : null} | ||
| 343 | <MenuGroup> | ||
| 344 | <div className="px-2 py-1.5 font-medium text-muted-foreground text-xs"> | ||
| 345 | {descriptor.label} | ||
| 346 | </div> | ||
| 347 | <MenuRadioGroup | ||
| 348 | value={descriptor.currentValue === true ? "on" : "off"} | ||
| 349 | onValueChange={(value) => { | ||
| 350 | updateDescriptors( | ||
| 351 | replaceDescriptorCurrentValue(descriptors, descriptor.id, value === "on"), | ||
| 352 | ); | ||
| 353 | }} | ||
| 354 | > | ||
| 355 | <MenuRadioItem value="on">On</MenuRadioItem> | ||
| 356 | <MenuRadioItem value="off">Off</MenuRadioItem> | ||
| 357 | </MenuRadioGroup> | ||
| 358 | </MenuGroup> | ||
| 359 | </div> | ||
| 360 | ))} | ||
| 361 | </> | 384 | </> |
| 362 | ); | 385 | ); |
| 363 | }); | 386 | }); |
apps/web/src/components/ui/menu.tsx+28-18| ... | @@ -147,32 +147,42 @@ function MenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) { | ... | @@ -147,32 +147,42 @@ function MenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) { |
| 147 | return <MenuPrimitive.RadioGroup data-slot="menu-radio-group" {...props} />; | 147 | return <MenuPrimitive.RadioGroup data-slot="menu-radio-group" {...props} />; |
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | function MenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) { | 150 | function MenuRadioItem({ |
| 151 | className, | ||
| 152 | children, | ||
| 153 | hideIndicator = false, | ||
| 154 | ...props | ||
| 155 | }: MenuPrimitive.RadioItem.Props & { | ||
| 156 | hideIndicator?: boolean; | ||
| 157 | }) { | ||
| 151 | return ( | 158 | return ( |
| 152 | <MenuPrimitive.RadioItem | 159 | <MenuPrimitive.RadioItem |
| 153 | className={cn( | 160 | className={cn( |
| 154 | "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default grid-cols-[1rem_1fr] items-center gap-2 rounded-sm py-1 ps-2 pe-4 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", | 161 | "grid min-h-8 in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] cursor-default items-center gap-2 rounded-sm py-1 text-base text-foreground outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", |
| 162 | hideIndicator ? "grid-cols-[1fr] ps-3 pe-3" : "grid-cols-[1rem_1fr] ps-2 pe-4", | ||
| 155 | className, | 163 | className, |
| 156 | )} | 164 | )} |
| 157 | data-slot="menu-radio-item" | 165 | data-slot="menu-radio-item" |
| 158 | {...props} | 166 | {...props} |
| 159 | > | 167 | > |
| 160 | <MenuPrimitive.RadioItemIndicator className="col-start-1"> | 168 | {hideIndicator ? null : ( |
| 161 | <svg | 169 | <MenuPrimitive.RadioItemIndicator className="col-start-1"> |
| 162 | fill="none" | 170 | <svg |
| 163 | height="24" | 171 | fill="none" |
| 164 | stroke="currentColor" | 172 | height="24" |
| 165 | strokeLinecap="round" | 173 | stroke="currentColor" |
| 166 | strokeLinejoin="round" | 174 | strokeLinecap="round" |
| 167 | strokeWidth="2" | 175 | strokeLinejoin="round" |
| 168 | viewBox="0 0 24 24" | 176 | strokeWidth="2" |
| 169 | width="24" | 177 | viewBox="0 0 24 24" |
| 170 | xmlns="http://www.w3.org/2000/svg" | 178 | width="24" |
| 171 | > | 179 | xmlns="http://www.w3.org/2000/svg" |
| 172 | <path d="M5.252 12.7 10.2 18.63 18.748 5.37" /> | 180 | > |
| 173 | </svg> | 181 | <path d="M5.252 12.7 10.2 18.63 18.748 5.37" /> |
| 174 | </MenuPrimitive.RadioItemIndicator> | 182 | </svg> |
| 175 | <span className="col-start-2">{children}</span> | 183 | </MenuPrimitive.RadioItemIndicator> |
| 184 | )} | ||
| 185 | <span className={hideIndicator ? "col-start-1" : "col-start-2"}>{children}</span> | ||
| 176 | </MenuPrimitive.RadioItem> | 186 | </MenuPrimitive.RadioItem> |
| 177 | ); | 187 | ); |
| 178 | } | 188 | } |