authorgravatar for 62337003+Bil0000@users.noreply.github.comBilal Bakr <62337003+Bil0000@users.noreply.github.com> 2026-09-16 20:54:34+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-16 10:54:34-07:00
logf4ef5155fb8aab7776a41fc8ec73d5367f53ba1f
tree775471832b0f2f9c17bad372a47f79fb5ffafe42
parent0b83045d00d88d0a0b0866fe4942a50a41a08471
signature Signed by PGP key B5690EEEBB952194

feat(web): add send shortcut and follow-up controls (#12075)


11 files changed, 172 insertions(+), 23 deletions(-)

apps/web/src/components/ChatView.tsx+1-1
...@@ -7516,7 +7516,7 @@ export default function ChatView(props: ChatViewProps) {...@@ -7516,7 +7516,7 @@ export default function ChatView(props: ChatViewProps) {
7516 !directAnnotation &&7516 !directAnnotation &&
7517 phase === "running" &&7517 phase === "running" &&
7518 activeThreadKey &&7518 activeThreadKey &&
7519 settings.followUpBehavior === "queue"7519 (settings.followUpBehavior === "queue") !== (submissionIntent === "alternate")
7520 ) {7520 ) {
7521 if (composerRef.current?.validateProviderInput(promptForSend) === false) {7521 if (composerRef.current?.validateProviderInput(promptForSend) === false) {
7522 return;7522 return;
apps/web/src/components/chat/ChatComposer.tsx+3
...@@ -3955,6 +3955,9 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -3955,6 +3955,9 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
3955 shiftKey: event.shiftKey,3955 shiftKey: event.shiftKey,
3956 modifierKey: event.metaKey || event.ctrlKey,3956 modifierKey: event.metaKey || event.ctrlKey,
3957 isDraftThread: routeKind === "draft",3957 isDraftThread: routeKind === "draft",
3958 isRunning: phase === "running",
3959 sendShortcut: settings.sendShortcut,
3960 prompt: promptRef.current,
3958 })3961 })
3959 : null;3962 : null;
3960 if (submissionIntent) {3963 if (submissionIntent) {
apps/web/src/components/settings/SettingsPanels.tsx+76-19
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1import { Spinner } from "~/components/ui/spinner";1import { Spinner } from "~/components/ui/spinner";
2import { NotificationSettings } from "./NotificationSettings";2import { NotificationSettings } from "./NotificationSettings";
3import { ArchiveIcon, ArchiveX, ChevronRightIcon, SettingsIcon } from "lucide-react";3import { ArchiveIcon, ArchiveX, CheckIcon, ChevronRightIcon, SettingsIcon } from "lucide-react";
4import { Link, useNavigate } from "@tanstack/react-router";4import { Link, useNavigate } from "@tanstack/react-router";
5import type { CSSProperties, ReactNode } from "react";5import type { CSSProperties, ReactNode } from "react";
6import { useCallback, useEffect, useMemo, useRef, useState } from "react";6import { useCallback, useEffect, useMemo, useRef, useState } from "react";
...@@ -105,6 +105,7 @@ import {...@@ -105,6 +105,7 @@ import {
105 AlertDialogTitle,105 AlertDialogTitle,
106} from "../ui/alert-dialog";106} from "../ui/alert-dialog";
107import { Button } from "../ui/button";107import { Button } from "../ui/button";
108import { Toggle, ToggleGroup } from "../ui/toggle-group";
108import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible";109import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible";
109import {110import {
110 Dialog,111 Dialog,
...@@ -578,6 +579,7 @@ export function useSettingsRestore(onRestored?: () => void) {...@@ -578,6 +579,7 @@ export function useSettingsRestore(onRestored?: () => void) {
578 ...(settings.composerCollapseOnScroll !== DEFAULT_UNIFIED_SETTINGS.composerCollapseOnScroll579 ...(settings.composerCollapseOnScroll !== DEFAULT_UNIFIED_SETTINGS.composerCollapseOnScroll
579 ? ["Collapse composer on scroll"]580 ? ["Collapse composer on scroll"]
580 : []),581 : []),
582 ...(settings.sendShortcut !== DEFAULT_UNIFIED_SETTINGS.sendShortcut ? ["Send shortcut"] : []),
581 ...(settings.followUpBehavior !== DEFAULT_UNIFIED_SETTINGS.followUpBehavior583 ...(settings.followUpBehavior !== DEFAULT_UNIFIED_SETTINGS.followUpBehavior
582 ? ["Follow-up behavior"]584 ? ["Follow-up behavior"]
583 : []),585 : []),
...@@ -639,6 +641,7 @@ export function useSettingsRestore(onRestored?: () => void) {...@@ -639,6 +641,7 @@ export function useSettingsRestore(onRestored?: () => void) {
639 settings.confirmThreadDelete,641 settings.confirmThreadDelete,
640 settings.confirmThreadUnpin,642 settings.confirmThreadUnpin,
641 settings.composerCollapseOnScroll,643 settings.composerCollapseOnScroll,
644 settings.sendShortcut,
642 settings.followUpBehavior,645 settings.followUpBehavior,
643 settings.addProjectBaseDirectory,646 settings.addProjectBaseDirectory,
644 settings.defaultThreadEnvMode,647 settings.defaultThreadEnvMode,
...@@ -752,6 +755,7 @@ export function useSettingsRestore(onRestored?: () => void) {...@@ -752,6 +755,7 @@ export function useSettingsRestore(onRestored?: () => void) {
752 proactivePanelsEnabled: DEFAULT_UNIFIED_SETTINGS.proactivePanelsEnabled,755 proactivePanelsEnabled: DEFAULT_UNIFIED_SETTINGS.proactivePanelsEnabled,
753 showSkillsInSlashMenu: DEFAULT_UNIFIED_SETTINGS.showSkillsInSlashMenu,756 showSkillsInSlashMenu: DEFAULT_UNIFIED_SETTINGS.showSkillsInSlashMenu,
754 composerCollapseOnScroll: DEFAULT_UNIFIED_SETTINGS.composerCollapseOnScroll,757 composerCollapseOnScroll: DEFAULT_UNIFIED_SETTINGS.composerCollapseOnScroll,
758 sendShortcut: DEFAULT_UNIFIED_SETTINGS.sendShortcut,
755 followUpBehavior: DEFAULT_UNIFIED_SETTINGS.followUpBehavior,759 followUpBehavior: DEFAULT_UNIFIED_SETTINGS.followUpBehavior,
756 contextWindowMeterEnabled: DEFAULT_UNIFIED_SETTINGS.contextWindowMeterEnabled,760 contextWindowMeterEnabled: DEFAULT_UNIFIED_SETTINGS.contextWindowMeterEnabled,
757 environmentIdentificationMode: DEFAULT_UNIFIED_SETTINGS.environmentIdentificationMode,761 environmentIdentificationMode: DEFAULT_UNIFIED_SETTINGS.environmentIdentificationMode,
...@@ -2086,6 +2090,12 @@ function LegacyFeaturesSection() {...@@ -2086,6 +2090,12 @@ function LegacyFeaturesSection() {
2086}2090}
20872091
2088export function GeneralSettingsPanel() {2092export function GeneralSettingsPanel() {
2093 const modifierLabel = isMacPlatform(navigator.platform) ? "⌘" : "Ctrl";
2094 const sendShortcutOptions = [
2095 { value: "enter", label: "Enter" },
2096 { value: "mod-enter-multiline", label: `${modifierLabel} + Enter for multiline prompts` },
2097 { value: "mod-enter", label: `${modifierLabel} + Enter always` },
2098 ] as const;
2089 const settings = useScopedSettings();2099 const settings = useScopedSettings();
2090 const updateSettings = useUpdateScopedSettings();2100 const updateSettings = useUpdateScopedSettings();
2091 const navigate = useNavigate();2101 const navigate = useNavigate();
...@@ -2594,9 +2604,61 @@ export function GeneralSettingsPanel() {...@@ -2594,9 +2604,61 @@ export function GeneralSettingsPanel() {
2594 }2604 }
2595 />2605 />
25962606
2607 <SettingsRow
2608 {...searchableSetting("send-shortcut")}
2609 description="Choose when Enter sends a prompt or inserts a new line"
2610 resetAction={
2611 settings.sendShortcut !== DEFAULT_UNIFIED_SETTINGS.sendShortcut ? (
2612 <SettingResetButton
2613 label="send shortcut"
2614 onClick={() =>
2615 updateSettings({ sendShortcut: DEFAULT_UNIFIED_SETTINGS.sendShortcut })
2616 }
2617 />
2618 ) : null
2619 }
2620 control={
2621 <Select
2622 value={settings.sendShortcut}
2623 onValueChange={(value) => {
2624 const option = sendShortcutOptions.find((option) => option.value === value);
2625 if (option) updateSettings({ sendShortcut: option.value });
2626 }}
2627 >
2628 <SelectTrigger
2629 size="sm"
2630 className="w-auto min-w-0 max-w-full"
2631 aria-label="Send shortcut"
2632 >
2633 <SelectValue>
2634 {
2635 sendShortcutOptions.find((option) => option.value === settings.sendShortcut)
2636 ?.label
2637 }
2638 </SelectValue>
2639 </SelectTrigger>
2640 <SelectPopup align="end" alignItemWithTrigger={false}>
2641 {sendShortcutOptions.map((option) => (
2642 <SelectItem key={option.value} value={option.value}>
2643 <span className="flex items-center justify-between gap-4">
2644 {option.label}
2645 {settings.sendShortcut === option.value && <CheckIcon aria-hidden="true" />}
2646 </span>
2647 </SelectItem>
2648 ))}
2649 </SelectPopup>
2650 </Select>
2651 }
2652 />
2653
2597 <SettingsRow2654 <SettingsRow
2598 {...searchableSetting("follow-up-behavior")}2655 {...searchableSetting("follow-up-behavior")}
2599 description="Queue follow-ups while the agent runs or steer the current turn."2656 description={
2657 "Queue follow-ups while the agent runs or steer the current run. " +
2658 (settings.sendShortcut === "mod-enter-multiline"
2659 ? `Press ${modifierLabel} + Enter for single-line prompts or ${modifierLabel} + Shift + Enter for multiline prompts to do the opposite for one message.`
2660 : `Press ${modifierLabel}${settings.sendShortcut === "mod-enter" ? " + Shift" : ""} + Enter to do the opposite for one message.`)
2661 }
2600 resetAction={2662 resetAction={
2601 settings.followUpBehavior !== DEFAULT_UNIFIED_SETTINGS.followUpBehavior ? (2663 settings.followUpBehavior !== DEFAULT_UNIFIED_SETTINGS.followUpBehavior ? (
2602 <SettingResetButton2664 <SettingResetButton
...@@ -2610,28 +2672,23 @@ export function GeneralSettingsPanel() {...@@ -2610,28 +2672,23 @@ export function GeneralSettingsPanel() {
2610 ) : null2672 ) : null
2611 }2673 }
2612 control={2674 control={
2613 <Select2675 <ToggleGroup
2614 value={settings.followUpBehavior}2676 aria-label="Follow-up behavior"
2615 onValueChange={(value) => {2677 variant="default"
2678 value={[settings.followUpBehavior]}
2679 onValueChange={(values) => {
2680 const value = values[0];
2616 if (value === "queue" || value === "steer") {2681 if (value === "queue" || value === "steer") {
2617 updateSettings({ followUpBehavior: value });2682 updateSettings({ followUpBehavior: value });
2618 }2683 }
2619 }}2684 }}
2620 >2685 >
2621 <SelectTrigger size="sm" className="w-full sm:w-40" aria-label="Follow-up behavior">2686 {(["queue", "steer"] as const).map((value) => (
2622 <SelectValue>2687 <Toggle key={value} value={value} variant="pill">
2623 {settings.followUpBehavior === "queue" ? "Queue" : "Steer"}2688 {value === "queue" ? "Queue" : "Steer"}
2624 </SelectValue>2689 </Toggle>
2625 </SelectTrigger>2690 ))}
2626 <SelectPopup align="end" alignItemWithTrigger={false}>2691 </ToggleGroup>
2627 <SelectItem hideIndicator value="queue">
2628 Queue
2629 </SelectItem>
2630 <SelectItem hideIndicator value="steer">
2631 Steer
2632 </SelectItem>
2633 </SelectPopup>
2634 </Select>
2635 }2692 }
2636 />2693 />
26372694
apps/web/src/components/settings/settingsSearch.test.ts+4
...@@ -45,6 +45,10 @@ const ITEMS: ReadonlyArray<SettingsSearchItem> = [...@@ -45,6 +45,10 @@ const ITEMS: ReadonlyArray<SettingsSearchItem> = [
45];45];
4646
47describe("searchSettings", () => {47describe("searchSettings", () => {
48 it.each(["send shortcut", "multiline", "new line"])("finds Send shortcut for %s", (query) => {
49 expect(searchSettings(query).map((item) => item.id)).toContain("send-shortcut");
50 });
51
48 it("matches titles, sections, and remembered setting details", () => {52 it("matches titles, sections, and remembered setting details", () => {
49 expect(searchSettings("word", ITEMS).map((item) => item.id)).toEqual(["word-wrap"]);53 expect(searchSettings("word", ITEMS).map((item) => item.id)).toEqual(["word-wrap"]);
50 expect(searchSettings("network", ITEMS).map((item) => item.id)).toEqual(["network-access"]);54 expect(searchSettings("network", ITEMS).map((item) => item.id)).toEqual(["network-access"]);
apps/web/src/components/settings/settingsSearch.ts+6
...@@ -298,6 +298,12 @@ export const SETTINGS_SEARCH_ITEMS = [...@@ -298,6 +298,12 @@ export const SETTINGS_SEARCH_ITEMS = [
298 to: "/settings/general",298 to: "/settings/general",
299 searchTerms: ["composer rest resting scroll wheel conversation timeline shrink minimize"],299 searchTerms: ["composer rest resting scroll wheel conversation timeline shrink minimize"],
300 },300 },
301 {
302 id: "send-shortcut",
303 title: "Send shortcut",
304 to: "/settings/general",
305 searchTerms: ["enter return command ctrl multiline prompt new line composer"],
306 },
301 {307 {
302 id: "follow-up-behavior",308 id: "follow-up-behavior",
303 title: "Follow-up behavior",309 title: "Follow-up behavior",
apps/web/src/components/ui/toggle.tsx+1
...@@ -25,6 +25,7 @@ const toggleVariants = cva(...@@ -25,6 +25,7 @@ const toggleVariants = cva(
25 },25 },
26 variant: {26 variant: {
27 default: "border-transparent",27 default: "border-transparent",
28 pill: "border-transparent rounded-full px-3 font-normal text-muted-foreground data-pressed:text-foreground",
28 ghost:29 ghost:
29 "border-transparent text-foreground shadow-none [:disabled,:active,[data-pressed]]:shadow-none before:shadow-none data-pressed:bg-accent data-pressed:text-accent-foreground disabled:opacity-100 disabled:text-muted-foreground disabled:[&_svg]:opacity-100",30 "border-transparent text-foreground shadow-none [:disabled,:active,[data-pressed]]:shadow-none before:shadow-none data-pressed:bg-accent data-pressed:text-accent-foreground disabled:opacity-100 disabled:text-muted-foreground disabled:[&_svg]:opacity-100",
30 outline:31 outline:
apps/web/src/composer-logic.test.ts+45
...@@ -61,6 +61,51 @@ describe("formatAssistantCitationForComposer", () => {...@@ -61,6 +61,51 @@ describe("formatAssistantCitationForComposer", () => {
61});61});
6262
63describe("composerSubmissionIntentForEnter", () => {63describe("composerSubmissionIntentForEnter", () => {
64 it.each([
65 ["enter", "one line", false, "foreground"],
66 ["enter", "two\nlines", false, "foreground"],
67 ["mod-enter-multiline", "one line", false, "foreground"],
68 ["mod-enter-multiline", "two\nlines", false, null],
69 ["mod-enter-multiline", "two\nlines", true, "foreground"],
70 ["mod-enter", "one line", false, null],
71 ["mod-enter", "one line", true, "foreground"],
72 ] as const)("uses %s for %j with modifier=%s", (sendShortcut, prompt, modifierKey, expected) => {
73 expect(
74 composerSubmissionIntentForEnter({
75 isMobileViewport: false,
76 shiftKey: false,
77 modifierKey,
78 isDraftThread: false,
79 sendShortcut,
80 prompt,
81 }),
82 ).toBe(expected);
83 });
84
85 it.each([
86 ["enter", false, "alternate"],
87 ["enter", true, null],
88 ["mod-enter-multiline", false, "foreground"],
89 ["mod-enter-multiline", true, "alternate"],
90 ["mod-enter", false, "foreground"],
91 ["mod-enter", true, "alternate"],
92 ] as const)(
93 "resolves running follow-ups with %s and shift=%s",
94 (sendShortcut, shiftKey, expected) => {
95 expect(
96 composerSubmissionIntentForEnter({
97 isMobileViewport: false,
98 shiftKey,
99 modifierKey: true,
100 isDraftThread: false,
101 isRunning: true,
102 sendShortcut,
103 prompt: "two\nlines",
104 }),
105 ).toBe(expected);
106 },
107 );
108
64 it("submits plain Enter on desktop", () => {109 it("submits plain Enter on desktop", () => {
65 expect(110 expect(
66 composerSubmissionIntentForEnter({111 composerSubmissionIntentForEnter({
apps/web/src/composer-logic.ts+12-3
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1import type { ClientSettings } from "@t3tools/contracts/settings";
1import type { AssistantCitation } from "@t3tools/contracts";2import type { AssistantCitation } from "@t3tools/contracts";
2import {3import {
3 serializeAssistantCitation,4 serializeAssistantCitation,
...@@ -10,7 +11,7 @@ import {...@@ -10,7 +11,7 @@ import {
1011
11export type ComposerTriggerKind = "path" | "pull-request" | "slash-command" | "skill";12export type ComposerTriggerKind = "path" | "pull-request" | "slash-command" | "skill";
12export type ComposerSlashCommand = "model" | "plan" | "default";13export type ComposerSlashCommand = "model" | "plan" | "default";
13export type ComposerSubmissionIntent = "foreground" | "background";14export type ComposerSubmissionIntent = "foreground" | "background" | "alternate";
1415
15export interface ComposerTrigger {16export interface ComposerTrigger {
16 kind: ComposerTriggerKind;17 kind: ComposerTriggerKind;
...@@ -28,9 +29,17 @@ export function composerSubmissionIntentForEnter(input: {...@@ -28,9 +29,17 @@ export function composerSubmissionIntentForEnter(input: {
28 shiftKey: boolean;29 shiftKey: boolean;
29 modifierKey: boolean;30 modifierKey: boolean;
30 isDraftThread: boolean;31 isDraftThread: boolean;
32 isRunning?: boolean;
33 sendShortcut?: ClientSettings["sendShortcut"];
34 prompt?: string;
31}): ComposerSubmissionIntent | null {35}): ComposerSubmissionIntent | null {
32 if (input.isMobileViewport || input.shiftKey) {36 const requiresModifier =
33 return null;37 input.sendShortcut === "mod-enter" ||
38 (input.sendShortcut === "mod-enter-multiline" && /[\r\n]/.test(input.prompt ?? ""));
39 if (input.isMobileViewport || (requiresModifier && !input.modifierKey)) return null;
40 if (input.shiftKey && !(requiresModifier && input.modifierKey && input.isRunning)) return null;
41 if (input.isRunning && input.modifierKey && (!requiresModifier || input.shiftKey)) {
42 return "alternate";
34 }43 }
35 return input.modifierKey && input.isDraftThread ? "background" : "foreground";44 return input.modifierKey && input.isDraftThread ? "background" : "foreground";
36}45}
docs/user/keybindings.md+9
...@@ -5,6 +5,15 @@ also lists the command IDs and defaults available in your version....@@ -5,6 +5,15 @@ also lists the command IDs and defaults available in your version.
55
6## Composer controls6## Composer controls
77
8In **Settings → General → Send shortcut**, choose whether Enter sends, requires
9`mod+Enter` for multiline prompts, or always requires `mod+Enter`. `Shift+Enter`
10inserts a new line. This applies to the web and desktop composer at desktop widths.
11
12**Follow-up behavior** chooses Queue or Steer while the agent runs. Use
13`mod+Enter` to do the opposite for one message. When sending requires `mod+Enter`,
14use `mod+Shift+Enter` for the opposite action. In a new thread, `mod+Enter` keeps
15starting the thread in the background.
16
8Use `mod+shift+m` to choose a model and `mod+shift+h` to choose a host.17Use `mod+shift+m` to choose a model and `mod+shift+h` to choose a host.
9Use `mod+shift+e` for effort, `mod+shift+a` for access mode, `mod+shift+x` for the18Use `mod+shift+e` for effort, `mod+shift+a` for access mode, `mod+shift+x` for the
10workspace, and `mod+shift+g` for the Git branch. The workspace menu includes the19workspace, and `mod+shift+g` for the Git branch. The workspace menu includes the
packages/contracts/src/settings.test.ts+11
...@@ -529,6 +529,17 @@ describe("ClientSettings context window meter", () => {...@@ -529,6 +529,17 @@ describe("ClientSettings context window meter", () => {
529 });529 });
530});530});
531531
532describe("ClientSettings send shortcut", () => {
533 it("defaults to Enter and validates the supported choices", () => {
534 expect(decodeClientSettings({}).sendShortcut).toBe("enter");
535 for (const sendShortcut of ["enter", "mod-enter-multiline", "mod-enter"]) {
536 expect(decodeClientSettings({ sendShortcut }).sendShortcut).toBe(sendShortcut);
537 expect(decodeClientSettingsPatch({ sendShortcut }).sendShortcut).toBe(sendShortcut);
538 }
539 expect(() => decodeClientSettingsPatch({ sendShortcut: "invalid" })).toThrow();
540 });
541});
542
532describe("ClientSettings follow-up behavior", () => {543describe("ClientSettings follow-up behavior", () => {
533 it("defaults to queue and accepts either behavior", () => {544 it("defaults to queue and accepts either behavior", () => {
534 expect(decodeClientSettings({}).followUpBehavior).toBe("queue");545 expect(decodeClientSettings({}).followUpBehavior).toBe("queue");
packages/contracts/src/settings.ts+4
...@@ -430,6 +430,9 @@ export const ClientSettingsSchema = Schema.Struct({...@@ -430,6 +430,9 @@ export const ClientSettingsSchema = Schema.Struct({
430 // Desktop resting composer: scrolling an existing thread's conversation430 // Desktop resting composer: scrolling an existing thread's conversation
431 // settles the composer into its single-line layout. Losing focus never does.431 // settles the composer into its single-line layout. Losing focus never does.
432 composerCollapseOnScroll: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),432 composerCollapseOnScroll: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
433 sendShortcut: Schema.Literals(["enter", "mod-enter-multiline", "mod-enter"]).pipe(
434 Schema.withDecodingDefault(Effect.succeed("enter")),
435 ),
433 followUpBehavior: Schema.Literals(["queue", "steer"]).pipe(436 followUpBehavior: Schema.Literals(["queue", "steer"]).pipe(
434 Schema.withDecodingDefault(Effect.succeed("queue")),437 Schema.withDecodingDefault(Effect.succeed("queue")),
435 ),438 ),
...@@ -1502,6 +1505,7 @@ export const ClientSettingsPatch = Schema.Struct({...@@ -1502,6 +1505,7 @@ export const ClientSettingsPatch = Schema.Struct({
1502 planModeEnabled: Schema.optionalKey(Schema.Boolean),1505 planModeEnabled: Schema.optionalKey(Schema.Boolean),
1503 contextWindowMeterEnabled: Schema.optionalKey(Schema.Boolean),1506 contextWindowMeterEnabled: Schema.optionalKey(Schema.Boolean),
1504 composerCollapseOnScroll: Schema.optionalKey(Schema.Boolean),1507 composerCollapseOnScroll: Schema.optionalKey(Schema.Boolean),
1508 sendShortcut: Schema.optionalKey(Schema.Literals(["enter", "mod-enter-multiline", "mod-enter"])),
1505 followUpBehavior: Schema.optionalKey(Schema.Literals(["queue", "steer"])),1509 followUpBehavior: Schema.optionalKey(Schema.Literals(["queue", "steer"])),
1506 proactivePanelsEnabled: Schema.optionalKey(Schema.Boolean),1510 proactivePanelsEnabled: Schema.optionalKey(Schema.Boolean),
1507 showSkillsInSlashMenu: Schema.optionalKey(Schema.Boolean),1511 showSkillsInSlashMenu: Schema.optionalKey(Schema.Boolean),