| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | | import { ArchiveIcon, ArchiveX, LoaderIcon, SettingsIcon } from "lucide-react"; |
| 1 | import { ArchiveIcon, ArchiveX, ChevronRightIcon, LoaderIcon, SettingsIcon } from "lucide-react"; |
| 2 | 2 | import { Link } from "@tanstack/react-router"; |
| 3 | 3 | import type { CSSProperties, ReactNode } from "react"; |
| 4 | 4 | import { useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| ... | ... | @@ -79,6 +79,7 @@ import { useProjects } from "../../state/entities"; |
| 79 | 79 | import { useArchivedThreadSnapshots } from "../../lib/archivedThreadsState"; |
| 80 | 80 | import { formatRelativeTimeLabel } from "../../timestampFormat"; |
| 81 | 81 | import { Button } from "../ui/button"; |
| 82 | import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; |
| 82 | 83 | import { |
| 83 | 84 | Dialog, |
| 84 | 85 | DialogDescription, |
| ... | ... | @@ -476,8 +477,9 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 476 | 477 | ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace |
| 477 | 478 | ? ["Diff whitespace changes"] |
| 478 | 479 | : []), |
| 479 | | ...(settings.enableAssistantStreaming !== DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming |
| 480 | | ? ["Assistant output"] |
| 480 | ...(settings.enableLegacyTokenStreaming !== |
| 481 | DEFAULT_UNIFIED_SETTINGS.enableLegacyTokenStreaming |
| 482 | ? ["Stream token by token"] |
| 481 | 483 | : []), |
| 482 | 484 | ...(settings.enableProviderUpdateChecks !== |
| 483 | 485 | DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks |
| ... | ... | @@ -521,7 +523,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 521 | 523 | settings.fontSizePrompt, |
| 522 | 524 | settings.fontSizeTerminal, |
| 523 | 525 | settings.glassOpacity, |
| 524 | | settings.enableAssistantStreaming, |
| 526 | settings.enableLegacyTokenStreaming, |
| 525 | 527 | settings.enableProviderUpdateChecks, |
| 526 | 528 | settings.sidebarProjectGroupingMode, |
| 527 | 529 | settings.sidebarThreadPreviewCount, |
| ... | ... | @@ -601,7 +603,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 601 | 603 | glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity, |
| 602 | 604 | sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount, |
| 603 | 605 | sidebarProjectGroupingMode: DEFAULT_UNIFIED_SETTINGS.sidebarProjectGroupingMode, |
| 604 | | enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming, |
| 606 | enableLegacyTokenStreaming: DEFAULT_UNIFIED_SETTINGS.enableLegacyTokenStreaming, |
| 605 | 607 | enableProviderUpdateChecks: DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks, |
| 606 | 608 | backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity, |
| 607 | 609 | backgroundActivityProfile: DEFAULT_UNIFIED_SETTINGS.backgroundActivityProfile, |
| ... | ... | @@ -1504,6 +1506,96 @@ function FontFamilySettingsRow({ |
| 1504 | 1506 | ); |
| 1505 | 1507 | } |
| 1506 | 1508 | |
| 1509 | // Both legacy rows sit behind the fold, so a settings-search jump has to |
| 1510 | // expand the section before its target can mount and scroll. |
| 1511 | const LEGACY_FEATURE_TARGET_IDS: ReadonlySet<string> = new Set([ |
| 1512 | "legacy-plan-mode", |
| 1513 | "legacy-token-streaming", |
| 1514 | ]); |
| 1515 | |
| 1516 | /** |
| 1517 | * Retired features kept only for users who still depend on them. Collapsed by |
| 1518 | * default so they stay out of the everyday settings path; a settings-search |
| 1519 | * jump to one of the rows unfolds the section. |
| 1520 | */ |
| 1521 | function LegacyFeaturesSection() { |
| 1522 | const settings = usePrimarySettings(); |
| 1523 | const updateSettings = useUpdatePrimarySettings(); |
| 1524 | const [open, setOpen] = useState(false); |
| 1525 | const searchTargetId = useSettingsSearchTargetId(); |
| 1526 | // Unfold once per search jump; tracking the handled id lets the user fold |
| 1527 | // the section back up without the still-set target immediately reopening it. |
| 1528 | const lastExpandedTargetRef = useRef<string | null>(null); |
| 1529 | useEffect(() => { |
| 1530 | if (searchTargetId === null) { |
| 1531 | // A handled jump clears the target; forgetting it here lets a later |
| 1532 | // jump to the same row expand the section again. |
| 1533 | lastExpandedTargetRef.current = null; |
| 1534 | return; |
| 1535 | } |
| 1536 | if (!LEGACY_FEATURE_TARGET_IDS.has(searchTargetId)) return; |
| 1537 | if (lastExpandedTargetRef.current === searchTargetId) return; |
| 1538 | lastExpandedTargetRef.current = searchTargetId; |
| 1539 | setOpen(true); |
| 1540 | }, [searchTargetId]); |
| 1541 | |
| 1542 | return ( |
| 1543 | <section className="space-y-3"> |
| 1544 | <Collapsible open={open} onOpenChange={setOpen}> |
| 1545 | <CollapsibleTrigger className="group flex min-h-8 w-full items-center gap-2 px-3 sm:px-4"> |
| 1546 | <h2 className="text-lg font-semibold tracking-[-0.025em] text-muted-foreground transition-colors group-hover:text-foreground"> |
| 1547 | Legacy features |
| 1548 | </h2> |
| 1549 | <ChevronRightIcon className="size-4 text-muted-foreground transition-transform duration-200 group-data-panel-open:rotate-90" /> |
| 1550 | </CollapsibleTrigger> |
| 1551 | <CollapsiblePanel> |
| 1552 | <div className="relative space-y-1 overflow-visible pt-3 text-foreground"> |
| 1553 | <SettingsRow |
| 1554 | {...searchableSetting("legacy-plan-mode")} |
| 1555 | description="Brings back the Build/Plan toggle in the composer along with the /plan and /default commands and the Shift+Tab shortcut. While off, every thread runs in build mode." |
| 1556 | control={ |
| 1557 | <Switch |
| 1558 | checked={settings.planModeEnabled} |
| 1559 | onCheckedChange={(checked) => |
| 1560 | updateSettings({ planModeEnabled: Boolean(checked) }) |
| 1561 | } |
| 1562 | aria-label="Plan mode (legacy)" |
| 1563 | /> |
| 1564 | } |
| 1565 | /> |
| 1566 | <SettingsRow |
| 1567 | {...searchableSetting("legacy-token-streaming")} |
| 1568 | description="Paints assistant output token by token instead of in complete chunks. Not recommended: it is significantly slower, and long responses become harder to follow. Kept only for compatibility with the old behavior." |
| 1569 | control={ |
| 1570 | <Switch |
| 1571 | checked={settings.enableLegacyTokenStreaming} |
| 1572 | onCheckedChange={(checked) => { |
| 1573 | if (!checked) { |
| 1574 | updateSettings({ enableLegacyTokenStreaming: false }); |
| 1575 | return; |
| 1576 | } |
| 1577 | void (async () => { |
| 1578 | const api = readLocalApi(); |
| 1579 | const confirmed = await (api ?? ensureLocalApi()).dialogs.confirm( |
| 1580 | [ |
| 1581 | "Turn on token-by-token output?", |
| 1582 | "It is significantly slower than the default buffered output and hurts the reading experience. This switch exists only for backwards compatibility.", |
| 1583 | ].join("\n"), |
| 1584 | ); |
| 1585 | if (confirmed) updateSettings({ enableLegacyTokenStreaming: true }); |
| 1586 | })(); |
| 1587 | }} |
| 1588 | aria-label="Stream token by token (legacy)" |
| 1589 | /> |
| 1590 | } |
| 1591 | /> |
| 1592 | </div> |
| 1593 | </CollapsiblePanel> |
| 1594 | </Collapsible> |
| 1595 | </section> |
| 1596 | ); |
| 1597 | } |
| 1598 | |
| 1507 | 1599 | export function GeneralSettingsPanel() { |
| 1508 | 1600 | const settings = usePrimarySettings(); |
| 1509 | 1601 | const updateSettings = useUpdatePrimarySettings(); |
| ... | ... | @@ -1664,33 +1756,6 @@ export function GeneralSettingsPanel() { |
| 1664 | 1756 | } |
| 1665 | 1757 | /> |
| 1666 | 1758 | |
| 1667 | | <SettingsRow |
| 1668 | | {...searchableSetting("assistant-output")} |
| 1669 | | description="Show token-by-token output while a response is in progress." |
| 1670 | | resetAction={ |
| 1671 | | settings.enableAssistantStreaming !== |
| 1672 | | DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming ? ( |
| 1673 | | <SettingResetButton |
| 1674 | | label="assistant output" |
| 1675 | | onClick={() => |
| 1676 | | updateSettings({ |
| 1677 | | enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming, |
| 1678 | | }) |
| 1679 | | } |
| 1680 | | /> |
| 1681 | | ) : null |
| 1682 | | } |
| 1683 | | control={ |
| 1684 | | <Switch |
| 1685 | | checked={settings.enableAssistantStreaming} |
| 1686 | | onCheckedChange={(checked) => |
| 1687 | | updateSettings({ enableAssistantStreaming: Boolean(checked) }) |
| 1688 | | } |
| 1689 | | aria-label="Stream assistant messages" |
| 1690 | | /> |
| 1691 | | } |
| 1692 | | /> |
| 1693 | | |
| 1694 | 1759 | <SettingsRow |
| 1695 | 1760 | {...searchableSetting("provider-update-checks")} |
| 1696 | 1761 | description="Check installed provider CLIs for newer available versions." |
| ... | ... | @@ -2050,6 +2115,8 @@ export function GeneralSettingsPanel() { |
| 2050 | 2115 | } |
| 2051 | 2116 | /> |
| 2052 | 2117 | </SettingsSection> |
| 2118 | |
| 2119 | <LegacyFeaturesSection /> |
| 2053 | 2120 | </SettingsPageContainer> |
| 2054 | 2121 | ); |
| 2055 | 2122 | } |