| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | import { ArchiveIcon, ArchiveX, LoaderIcon, SettingsIcon } from "lucide-react"; | 1 | import { ArchiveIcon, ArchiveX, ChevronRightIcon, LoaderIcon, SettingsIcon } from "lucide-react"; |
| 2 | import { Link } from "@tanstack/react-router"; | 2 | import { Link } from "@tanstack/react-router"; |
| 3 | import type { CSSProperties, ReactNode } from "react"; | 3 | import type { CSSProperties, ReactNode } from "react"; |
| 4 | import { useCallback, useEffect, useMemo, useRef, useState } from "react"; | 4 | import { useCallback, useEffect, useMemo, useRef, useState } from "react"; |
| ... | @@ -79,6 +79,7 @@ import { useProjects } from "../../state/entities"; | ... | @@ -79,6 +79,7 @@ import { useProjects } from "../../state/entities"; |
| 79 | import { useArchivedThreadSnapshots } from "../../lib/archivedThreadsState"; | 79 | import { useArchivedThreadSnapshots } from "../../lib/archivedThreadsState"; |
| 80 | import { formatRelativeTimeLabel } from "../../timestampFormat"; | 80 | import { formatRelativeTimeLabel } from "../../timestampFormat"; |
| 81 | import { Button } from "../ui/button"; | 81 | import { Button } from "../ui/button"; |
| | 82 | import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; |
| 82 | import { | 83 | import { |
| 83 | Dialog, | 84 | Dialog, |
| 84 | DialogDescription, | 85 | DialogDescription, |
| ... | @@ -476,8 +477,9 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -476,8 +477,9 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 476 | ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace | 477 | ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace |
| 477 | ? ["Diff whitespace changes"] | 478 | ? ["Diff whitespace changes"] |
| 478 | : []), | 479 | : []), |
| 479 | ...(settings.enableAssistantStreaming !== DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming | 480 | ...(settings.enableLegacyTokenStreaming !== |
| 480 | ? ["Assistant output"] | 481 | DEFAULT_UNIFIED_SETTINGS.enableLegacyTokenStreaming |
| | 482 | ? ["Stream token by token"] |
| 481 | : []), | 483 | : []), |
| 482 | ...(settings.enableProviderUpdateChecks !== | 484 | ...(settings.enableProviderUpdateChecks !== |
| 483 | DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks | 485 | DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks |
| ... | @@ -521,7 +523,7 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -521,7 +523,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 521 | settings.fontSizePrompt, | 523 | settings.fontSizePrompt, |
| 522 | settings.fontSizeTerminal, | 524 | settings.fontSizeTerminal, |
| 523 | settings.glassOpacity, | 525 | settings.glassOpacity, |
| 524 | settings.enableAssistantStreaming, | 526 | settings.enableLegacyTokenStreaming, |
| 525 | settings.enableProviderUpdateChecks, | 527 | settings.enableProviderUpdateChecks, |
| 526 | settings.sidebarProjectGroupingMode, | 528 | settings.sidebarProjectGroupingMode, |
| 527 | settings.sidebarThreadPreviewCount, | 529 | settings.sidebarThreadPreviewCount, |
| ... | @@ -601,7 +603,7 @@ export function useSettingsRestore(onRestored?: () => void) { | ... | @@ -601,7 +603,7 @@ export function useSettingsRestore(onRestored?: () => void) { |
| 601 | glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity, | 603 | glassOpacity: DEFAULT_UNIFIED_SETTINGS.glassOpacity, |
| 602 | sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount, | 604 | sidebarThreadPreviewCount: DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount, |
| 603 | sidebarProjectGroupingMode: DEFAULT_UNIFIED_SETTINGS.sidebarProjectGroupingMode, | 605 | sidebarProjectGroupingMode: DEFAULT_UNIFIED_SETTINGS.sidebarProjectGroupingMode, |
| 604 | enableAssistantStreaming: DEFAULT_UNIFIED_SETTINGS.enableAssistantStreaming, | 606 | enableLegacyTokenStreaming: DEFAULT_UNIFIED_SETTINGS.enableLegacyTokenStreaming, |
| 605 | enableProviderUpdateChecks: DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks, | 607 | enableProviderUpdateChecks: DEFAULT_UNIFIED_SETTINGS.enableProviderUpdateChecks, |
| 606 | backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity, | 608 | backgroundActivity: DEFAULT_UNIFIED_SETTINGS.backgroundActivity, |
| 607 | backgroundActivityProfile: DEFAULT_UNIFIED_SETTINGS.backgroundActivityProfile, | 609 | backgroundActivityProfile: DEFAULT_UNIFIED_SETTINGS.backgroundActivityProfile, |
| ... | @@ -1504,6 +1506,96 @@ function FontFamilySettingsRow({ | ... | @@ -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 | export function GeneralSettingsPanel() { | 1599 | export function GeneralSettingsPanel() { |
| 1508 | const settings = usePrimarySettings(); | 1600 | const settings = usePrimarySettings(); |
| 1509 | const updateSettings = useUpdatePrimarySettings(); | 1601 | const updateSettings = useUpdatePrimarySettings(); |
| ... | @@ -1664,33 +1756,6 @@ export function GeneralSettingsPanel() { | ... | @@ -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 | <SettingsRow | 1759 | <SettingsRow |
| 1695 | {...searchableSetting("provider-update-checks")} | 1760 | {...searchableSetting("provider-update-checks")} |
| 1696 | description="Check installed provider CLIs for newer available versions." | 1761 | description="Check installed provider CLIs for newer available versions." |
| ... | @@ -2050,6 +2115,8 @@ export function GeneralSettingsPanel() { | ... | @@ -2050,6 +2115,8 @@ export function GeneralSettingsPanel() { |
| 2050 | } | 2115 | } |
| 2051 | /> | 2116 | /> |
| 2052 | </SettingsSection> | 2117 | </SettingsSection> |
| | 2118 | |
| | 2119 | <LegacyFeaturesSection /> |
| 2053 | </SettingsPageContainer> | 2120 | </SettingsPageContainer> |
| 2054 | ); | 2121 | ); |
| 2055 | } | 2122 | } |