addons/isl/src/SettingsTooltip.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {ThemeColor} from './theme';
b69ab319import type {PreferredSubmitCommand} from './types';
b69ab3110
b69ab3111import {Button} from 'isl-components/Button';
b69ab3112import {Checkbox} from 'isl-components/Checkbox';
b69ab3113import {Dropdown} from 'isl-components/Dropdown';
b69ab3114import {Icon} from 'isl-components/Icon';
b69ab3115import {Kbd} from 'isl-components/Kbd';
b69ab3116import {KeyCode, Modifier} from 'isl-components/KeyboardShortcuts';
b69ab3117import {Subtle} from 'isl-components/Subtle';
b69ab3118import {Tooltip} from 'isl-components/Tooltip';
b69ab3119import {useAtom, useAtomValue} from 'jotai';
b69ab3120import {Suspense} from 'react';
b69ab3121import {nullthrows, tryJsonParse} from 'shared/utils';
b69ab3122import {
b69ab3123 distantRebaseWarningEnabled,
b69ab3124 rebaseOffWarmWarningEnabled,
b69ab3125 rebaseOntoMasterWarningEnabled,
b69ab3126} from './Commit';
b69ab3127import {condenseObsoleteStacks} from './CommitTreeList';
b69ab3128import {Column, Row} from './ComponentUtils';
b69ab3129import {confirmShouldSubmitEnabledAtom} from './ConfirmSubmitStack';
b69ab3130import {DropdownField, DropdownFields} from './DropdownFields';
b69ab3131import {useShowKeyboardShortcutsHelp} from './ISLShortcuts';
b69ab3132import {Link} from './Link';
b69ab3133import {RestackBehaviorSetting} from './RestackBehavior';
b69ab3134import {Setting} from './Setting';
b69ab3135import {
b69ab3136 currentExperimentalFeaturesList,
b69ab3137 hasExperimentalFeatures,
b69ab3138} from './atoms/experimentalFeatureAtoms';
b69ab3139import {codeReviewProvider} from './codeReview/CodeReviewInfo';
b69ab3140import {showDiffNumberConfig} from './codeReview/DiffBadge';
b69ab3141import {SubmitAsDraftCheckbox} from './codeReview/DraftCheckbox';
b69ab3142import {
b69ab3143 branchPRsSupported,
b69ab3144 experimentalBranchPRsEnabled,
b69ab3145 overrideDisabledSubmitModes,
b69ab3146} from './codeReview/github/branchPrState';
b69ab3147import {debugToolsEnabledState} from './debug/DebugToolsState';
b69ab3148import {externalMergeToolAtom} from './externalMergeTool';
b69ab3149import {t, T} from './i18n';
b69ab3150import {configBackedAtom, readAtom} from './jotaiUtils';
b69ab3151import {AutoResolveSettingCheckbox} from './mergeConflicts/state';
b69ab3152import {SetConfigOperation} from './operations/SetConfigOperation';
b69ab3153import {useRunOperation} from './operationsState';
b69ab3154import platform from './platform';
b69ab3155import {irrelevantCwdDisplayModeAtom} from './repositoryData';
b69ab3156import {renderCompactAtom, useZoomShortcut, zoomUISettingAtom} from './responsive';
b69ab3157import {mainCommandName, repositoryInfo} from './serverAPIState';
b69ab3158import {themeState, useThemeShortcut} from './theme';
b69ab3159
b69ab3160import './SettingsTooltip.css';
b69ab3161import {enableSaplingDebugFlag, enableSaplingVerboseFlag} from './atoms/debugToolAtoms';
b69ab3162
b69ab3163export function SettingsGearButton() {
b69ab3164 useThemeShortcut();
b69ab3165 useZoomShortcut();
b69ab3166 const showShortcutsHelp = useShowKeyboardShortcutsHelp();
b69ab3167 return (
b69ab3168 <Tooltip
b69ab3169 trigger="click"
b69ab3170 component={dismiss => (
b69ab3171 <SettingsDropdown dismiss={dismiss} showShortcutsHelp={showShortcutsHelp} />
b69ab3172 )}
b69ab3173 group="topbar"
b69ab3174 placement="bottom">
b69ab3175 <Button icon data-testid="settings-gear-button">
b69ab3176 <Icon icon="gear" />
b69ab3177 </Button>
b69ab3178 </Tooltip>
b69ab3179 );
b69ab3180}
b69ab3181
b69ab3182function SettingsDropdown({
b69ab3183 dismiss,
b69ab3184 showShortcutsHelp,
b69ab3185}: {
b69ab3186 dismiss: () => unknown;
b69ab3187 showShortcutsHelp: () => unknown;
b69ab3188}) {
b69ab3189 const [theme, setTheme] = useAtom(themeState);
b69ab3190 const [repoInfo, setRepoInfo] = useAtom(repositoryInfo);
b69ab3191 const runOperation = useRunOperation();
b69ab3192 const [showDiffNumber, setShowDiffNumber] = useAtom(showDiffNumberConfig);
b69ab3193 return (
b69ab3194 <DropdownFields title={<T>Settings</T>} icon="gear" data-testid="settings-dropdown">
b69ab3195 <Button
b69ab3196 style={{justifyContent: 'center', gap: 0}}
b69ab3197 icon
b69ab3198 onClick={() => {
b69ab3199 dismiss();
b69ab31100 showShortcutsHelp();
b69ab31101 }}>
b69ab31102 <T
b69ab31103 replace={{
b69ab31104 $shortcut: <Kbd keycode={KeyCode.QuestionMark} modifiers={[Modifier.SHIFT]} />,
b69ab31105 }}>
b69ab31106 View Keyboard Shortcuts - $shortcut
b69ab31107 </T>
b69ab31108 </Button>
b69ab31109 {platform.theme != null ? null : (
b69ab31110 <Setting title={<T>Theme</T>}>
b69ab31111 <Dropdown
b69ab31112 options={
b69ab31113 [
b69ab31114 {value: 'light', name: 'Light'},
b69ab31115 {value: 'dark', name: 'Dark'},
b69ab31116 ] as Array<{value: ThemeColor; name: string}>
b69ab31117 }
b69ab31118 value={theme}
b69ab31119 onChange={event => setTheme(event.currentTarget.value as ThemeColor)}
b69ab31120 />
b69ab31121 <div style={{marginTop: 'var(--pad)'}}>
b69ab31122 <Subtle>
b69ab31123 <T>Toggle: </T>
b69ab31124 <Kbd keycode={KeyCode.T} modifiers={[Modifier.ALT]} />
b69ab31125 </Subtle>
b69ab31126 </div>
b69ab31127 </Setting>
b69ab31128 )}
b69ab31129
b69ab31130 <Setting title={<T>UI Scale</T>}>
b69ab31131 <ZoomUISetting />
b69ab31132 </Setting>
b69ab31133 <Setting title={<T>Commits</T>}>
b69ab31134 <Column alignStart>
b69ab31135 <RenderCompactSetting />
b69ab31136 <CondenseObsoleteSetting />
b69ab31137 <RebaseOffWarmWarningSetting />
b69ab31138 <DistantRebaseWarningSetting />
b69ab31139 <RebaseOntoMasterWarningSetting />
b69ab31140 <DeemphasizeIrrelevantCommitsSetting />
b69ab31141 </Column>
b69ab31142 </Setting>
b69ab31143 <Setting title={<T>Conflicts</T>}>
b69ab31144 <AutoResolveSettingCheckbox />
b69ab31145 <RestackBehaviorSetting />
b69ab31146 </Setting>
b69ab31147 {/* TODO: enable this setting when there is actually a chocie to be made here. */}
b69ab31148 {/* <Setting
b69ab31149 title={<T>Language</T>}
b69ab31150 description={<T>Locale for translations used in the UI. Currently only en supported.</T>}>
b69ab31151 <Dropdown value="en" options=['en'] />
b69ab31152 </Setting> */}
b69ab31153 {repoInfo == null ? (
b69ab31154 <Icon icon="loading" />
b69ab31155 ) : repoInfo.codeReviewSystem.type === 'github' ? (
b69ab31156 <Setting
b69ab31157 title={<T>Preferred Code Review Submit Method</T>}
b69ab31158 description={
b69ab31159 <>
b69ab31160 <T>How to submit code for code review on GitHub.</T>{' '}
b69ab31161 {/* TODO: update this to document branchign PRs */}
b69ab31162 <Link href="https://sapling-scm.com/docs/git/intro#pull-requests">
b69ab31163 <T>Learn More</T>
b69ab31164 </Link>
b69ab31165 </>
b69ab31166 }>
b69ab31167 <Dropdown
b69ab31168 value={repoInfo.preferredSubmitCommand ?? 'not set'}
b69ab31169 options={(repoInfo.preferredSubmitCommand == null
b69ab31170 ? [{value: 'not set', name: '(not set)'}]
b69ab31171 : []
b69ab31172 ).concat([
b69ab31173 {value: 'ghstack', name: 'sl ghstack (stacked PRs)'},
b69ab31174 {value: 'pr', name: 'sl pr (stacked PRs)'},
b69ab31175 ...(readAtom(branchPRsSupported)
b69ab31176 ? [{value: 'push', name: 'sl push (branching PR)'}]
b69ab31177 : []),
b69ab31178 ])}
b69ab31179 onChange={event => {
b69ab31180 const value = (event as React.FormEvent<HTMLSelectElement>).currentTarget.value as
b69ab31181 | PreferredSubmitCommand
b69ab31182 | 'not set';
b69ab31183 if (value === 'not set') {
b69ab31184 return;
b69ab31185 }
b69ab31186
b69ab31187 runOperation(
b69ab31188 new SetConfigOperation('local', 'github.preferred_submit_command', value),
b69ab31189 );
b69ab31190 setRepoInfo(info => ({...nullthrows(info), preferredSubmitCommand: value}));
b69ab31191 }}
b69ab31192 />
b69ab31193 </Setting>
b69ab31194 ) : null}
b69ab31195 <Setting title={<T>Code Review</T>}>
b69ab31196 <div className="multiple-settings">
b69ab31197 <Checkbox
b69ab31198 checked={showDiffNumber}
b69ab31199 onChange={checked => {
b69ab31200 setShowDiffNumber(checked);
b69ab31201 }}>
b69ab31202 <T>Show copyable Diff / Pull Request numbers inline for each commit</T>
b69ab31203 </Checkbox>
b69ab31204 <ConfirmSubmitStackSetting />
b69ab31205 <SubmitAsDraftCheckbox forceShow />
b69ab31206 </div>
b69ab31207 </Setting>
b69ab31208 {platform.canCustomizeFileOpener && (
b69ab31209 <Setting title={<T>Environment</T>}>
b69ab31210 <Column alignStart>
b69ab31211 <OpenFilesCmdSetting />
b69ab31212 <ExternalMergeToolSetting />
b69ab31213 </Column>
b69ab31214 </Setting>
b69ab31215 )}
b69ab31216 <Suspense>{platform.Settings == null ? null : <platform.Settings />}</Suspense>
b69ab31217 <DebugToolsField />
b69ab31218 </DropdownFields>
b69ab31219 );
b69ab31220}
b69ab31221
b69ab31222function ConfirmSubmitStackSetting() {
b69ab31223 const [value, setValue] = useAtom(confirmShouldSubmitEnabledAtom);
b69ab31224 const provider = useAtomValue(codeReviewProvider);
b69ab31225 if (provider == null || !provider.supportSubmittingAsDraft) {
b69ab31226 return null;
b69ab31227 }
b69ab31228 return (
b69ab31229 <Tooltip
b69ab31230 title={t(
b69ab31231 'This lets you choose to submit as draft and provide an update message. ' +
b69ab31232 'If false, no confirmation is shown and it will submit as draft if you previously ' +
b69ab31233 'checked the submit as draft checkbox.',
b69ab31234 )}>
b69ab31235 <Checkbox
b69ab31236 checked={value}
b69ab31237 onChange={checked => {
b69ab31238 setValue(checked);
b69ab31239 }}>
b69ab31240 <T>Show confirmation when submitting a stack</T>
b69ab31241 </Checkbox>
b69ab31242 </Tooltip>
b69ab31243 );
b69ab31244}
b69ab31245
b69ab31246function RenderCompactSetting() {
b69ab31247 const [value, setValue] = useAtom(renderCompactAtom);
b69ab31248 return (
b69ab31249 <Tooltip
b69ab31250 title={t(
b69ab31251 'Render commits in the tree more compactly, by reducing spacing and not wrapping Diff info to multiple lines. ' +
b69ab31252 'May require more horizontal scrolling.',
b69ab31253 )}>
b69ab31254 <Checkbox
b69ab31255 checked={value}
b69ab31256 onChange={checked => {
b69ab31257 setValue(checked);
b69ab31258 }}>
b69ab31259 <T>Compact Mode</T>
b69ab31260 </Checkbox>
b69ab31261 </Tooltip>
b69ab31262 );
b69ab31263}
b69ab31264
b69ab31265function CondenseObsoleteSetting() {
b69ab31266 const [value, setValue] = useAtom(condenseObsoleteStacks);
b69ab31267 return (
b69ab31268 <Tooltip
b69ab31269 title={t(
b69ab31270 'Visually condense a continuous stack of obsolete commits into just the top and bottom commits.',
b69ab31271 )}>
b69ab31272 <Checkbox
b69ab31273 data-testid="condense-obsolete-stacks"
b69ab31274 checked={value !== false}
b69ab31275 onChange={checked => {
b69ab31276 setValue(checked);
b69ab31277 }}>
b69ab31278 <T>Condense Obsolete Stacks</T>
b69ab31279 </Checkbox>
b69ab31280 </Tooltip>
b69ab31281 );
b69ab31282}
b69ab31283
b69ab31284function DeemphasizeIrrelevantCommitsSetting() {
b69ab31285 const [value, setValue] = useAtom(irrelevantCwdDisplayModeAtom);
b69ab31286 return (
b69ab31287 <Tooltip
b69ab31288 title={t(
b69ab31289 'How to display commits which only change files in an unrelated directory to your current working directory.\n',
b69ab31290 )}>
b69ab31291 <div className="dropdown-container setting-inline-dropdown">
b69ab31292 <T>Cwd-Irrelevant Commits</T>
b69ab31293 <Dropdown<{value: typeof value; name: string}>
b69ab31294 data-testid="cwd-irrelevant-commits"
b69ab31295 options={[
b69ab31296 {value: 'show', name: t('Show')},
b69ab31297 {value: 'deemphasize', name: t('Deemphasize')},
b69ab31298 {value: 'hide', name: t('Hide')},
b69ab31299 ]}
b69ab31300 value={value}
b69ab31301 onChange={event => {
b69ab31302 setValue(event.currentTarget.value as typeof value);
b69ab31303 }}
b69ab31304 />
b69ab31305 </div>
b69ab31306 </Tooltip>
b69ab31307 );
b69ab31308}
b69ab31309
b69ab31310function RebaseOffWarmWarningSetting() {
b69ab31311 const [value, setValue] = useAtom(rebaseOffWarmWarningEnabled);
b69ab31312 return (
b69ab31313 <Tooltip
b69ab31314 title={t(
b69ab31315 'Show a warning when rebasing off a commit that is not warm (i.e. not in the current stack).',
b69ab31316 )}>
b69ab31317 <Checkbox
b69ab31318 data-testid="rebase-off-warm-warning-enabled"
b69ab31319 checked={value}
b69ab31320 onChange={checked => {
b69ab31321 setValue(checked);
b69ab31322 }}>
b69ab31323 <T>Show Warning on Rebase Off Warm</T>
b69ab31324 </Checkbox>
b69ab31325 </Tooltip>
b69ab31326 );
b69ab31327}
b69ab31328
b69ab31329function DistantRebaseWarningSetting() {
b69ab31330 const [value, setValue] = useAtom(distantRebaseWarningEnabled);
b69ab31331 return (
b69ab31332 <Tooltip
b69ab31333 title={t(
b69ab31334 'Show a warning when rebasing onto a commit that is significantly older than the current commit.',
b69ab31335 )}>
b69ab31336 <Checkbox
b69ab31337 data-testid="distant-rebase-warning-enabled"
b69ab31338 checked={value}
b69ab31339 onChange={checked => {
b69ab31340 setValue(checked);
b69ab31341 }}>
b69ab31342 <T>Show Warning on Distant Rebase</T>
b69ab31343 </Checkbox>
b69ab31344 </Tooltip>
b69ab31345 );
b69ab31346}
b69ab31347
b69ab31348function RebaseOntoMasterWarningSetting() {
b69ab31349 const [value, setValue] = useAtom(rebaseOntoMasterWarningEnabled);
b69ab31350 return (
b69ab31351 <Tooltip
b69ab31352 title={t(
b69ab31353 'Show a warning when rebasing directly onto master branch, which can cause unexpected failures and slower builds.',
b69ab31354 )}>
b69ab31355 <Checkbox
b69ab31356 data-testid="rebase-master-warning-enabled"
b69ab31357 checked={value}
b69ab31358 onChange={checked => {
b69ab31359 setValue(checked);
b69ab31360 }}>
b69ab31361 <T>Show Warning on Rebase onto Master</T>
b69ab31362 </Checkbox>
b69ab31363 </Tooltip>
b69ab31364 );
b69ab31365}
b69ab31366
b69ab31367export const openFileCmdAtom = configBackedAtom<string | null>(
b69ab31368 'isl.open-file-cmd',
b69ab31369 null,
b69ab31370 /* readonly */ true,
b69ab31371 /* use raw value */ true,
b69ab31372);
b69ab31373
b69ab31374function OpenFilesCmdSetting() {
b69ab31375 const cmdRaw = useAtomValue(openFileCmdAtom);
b69ab31376 const cmd = cmdRaw == null ? null : ((tryJsonParse(cmdRaw) as string | Array<string>) ?? cmdRaw);
b69ab31377 const cmdEl =
b69ab31378 cmd == null ? (
b69ab31379 <T>OS Default Program</T>
b69ab31380 ) : (
b69ab31381 <code>{Array.isArray(cmd) ? cmd.join(' ') : cmd}</code>
b69ab31382 );
b69ab31383 return (
b69ab31384 <Tooltip
b69ab31385 component={() => (
b69ab31386 <div>
b69ab31387 <div>
b69ab31388 <T>You can configure how to open files from ISL via</T>
b69ab31389 </div>
b69ab31390 <pre>sl config --user isl.open-file-cmd "/path/to/command"</pre>
b69ab31391 <div>
b69ab31392 <T>or</T>
b69ab31393 </div>
b69ab31394 <pre>sl config --user isl.open-file-cmd '["cmd", "with", "args"]'</pre>
b69ab31395 </div>
b69ab31396 )}>
b69ab31397 <Row>
b69ab31398 <T replace={{$cmd: cmdEl}}>Open files in: $cmd</T>
b69ab31399 <Subtle>
b69ab31400 <T>How to configure?</T>
b69ab31401 </Subtle>
b69ab31402 <Icon icon="question" />
b69ab31403 </Row>
b69ab31404 </Tooltip>
b69ab31405 );
b69ab31406}
b69ab31407
b69ab31408function ExternalMergeToolSetting() {
b69ab31409 const mergeTool = useAtomValue(externalMergeToolAtom);
b69ab31410 const cmdEl = mergeTool == null ? <T>None</T> : <code>{mergeTool}</code>;
b69ab31411 return (
b69ab31412 <Tooltip
b69ab31413 component={() => (
b69ab31414 <div>
b69ab31415 <div style={{alignItems: 'flex-start', maxWidth: 400}}>
b69ab31416 <T
b69ab31417 replace={{
b69ab31418 $help: <code>sl help config.merge-tools</code>,
b69ab31419 $configedit: <code>sl config --edit</code>,
b69ab31420 $mymergetool: <code>merge-tools.mymergetool</code>,
b69ab31421 $uimerge: <code>ui.merge = mymergetool</code>,
b69ab31422 $gui: <code>merge-tools.mymergetool.gui</code>,
b69ab31423 $local: <code>--local</code>,
b69ab31424 $br: (
b69ab31425 <>
b69ab31426 <br />
b69ab31427 <br />
b69ab31428 </>
b69ab31429 ),
b69ab31430 }}>
b69ab31431 You can configure Sapling and ISL to use a custom external merge tool, which is used
b69ab31432 when a merge conflict is detected.$br Define your tool with $configedit (or with
b69ab31433 $local to configure only for the current repository), by setting $mymergetool and
b69ab31434 $uimerge$brCLI merge tools like vimdiff won't be used from ISL. Ensure $gui is set to
b69ab31435 True.$br For more information, see: $help
b69ab31436 </T>
b69ab31437 </div>
b69ab31438 </div>
b69ab31439 )}>
b69ab31440 <Row>
b69ab31441 <T replace={{$cmd: cmdEl}}>External Merge Tool: $cmd</T>
b69ab31442 <Subtle>
b69ab31443 <T>How to configure?</T>
b69ab31444 </Subtle>
b69ab31445 <Icon icon="question" />
b69ab31446 </Row>
b69ab31447 </Tooltip>
b69ab31448 );
b69ab31449}
b69ab31450
b69ab31451function ZoomUISetting() {
b69ab31452 const [zoom, setZoom] = useAtom(zoomUISettingAtom);
b69ab31453 function roundToPercent(n: number): number {
b69ab31454 return Math.round(n * 100) / 100;
b69ab31455 }
b69ab31456 return (
b69ab31457 <div className="zoom-setting">
b69ab31458 <Tooltip title={t('Decrease UI Zoom')}>
b69ab31459 <Button
b69ab31460 icon
b69ab31461 onClick={() => {
b69ab31462 setZoom(roundToPercent(zoom - 0.1));
b69ab31463 }}>
b69ab31464 <Icon icon="zoom-out" />
b69ab31465 </Button>
b69ab31466 </Tooltip>
b69ab31467 <span>{`${Math.round(100 * zoom)}%`}</span>
b69ab31468 <Tooltip title={t('Increase UI Zoom')}>
b69ab31469 <Button
b69ab31470 icon
b69ab31471 onClick={() => {
b69ab31472 setZoom(roundToPercent(zoom + 0.1));
b69ab31473 }}>
b69ab31474 <Icon icon="zoom-in" />
b69ab31475 </Button>
b69ab31476 </Tooltip>
b69ab31477 <div style={{width: '20px'}} />
b69ab31478 <label>
b69ab31479 <T>Presets:</T>
b69ab31480 </label>
b69ab31481 <Button
b69ab31482 style={{fontSize: '80%'}}
b69ab31483 icon
b69ab31484 onClick={() => {
b69ab31485 setZoom(0.8);
b69ab31486 }}>
b69ab31487 <T>Small</T>
b69ab31488 </Button>
b69ab31489 <Button
b69ab31490 icon
b69ab31491 onClick={() => {
b69ab31492 setZoom(1.0);
b69ab31493 }}>
b69ab31494 <T>Normal</T>
b69ab31495 </Button>
b69ab31496 <Button
b69ab31497 style={{fontSize: '120%'}}
b69ab31498 icon
b69ab31499 onClick={() => {
b69ab31500 setZoom(1.2);
b69ab31501 }}>
b69ab31502 <T>Large</T>
b69ab31503 </Button>
b69ab31504 </div>
b69ab31505 );
b69ab31506}
b69ab31507
b69ab31508function DebugToolsField() {
b69ab31509 const [isDebug, setIsDebug] = useAtom(debugToolsEnabledState);
b69ab31510 const [overrideDisabledSubmit, setOverrideDisabledSubmit] = useAtom(overrideDisabledSubmitModes);
b69ab31511 const [debugFlag, setDebugFlag] = useAtom(enableSaplingDebugFlag);
b69ab31512 const [verboseFlag, setVerboseFlag] = useAtom(enableSaplingVerboseFlag);
b69ab31513 const provider = useAtomValue(codeReviewProvider);
b69ab31514 const commandName = useAtomValue(mainCommandName);
b69ab31515
b69ab31516 const [branchPrsEnabled, setBranchPrsEnabled] = useAtom(experimentalBranchPRsEnabled);
b69ab31517
b69ab31518 return (
b69ab31519 <DropdownField title={t('Debug Tools & Experimental')}>
b69ab31520 <Column alignStart>
b69ab31521 <Checkbox
b69ab31522 checked={isDebug}
b69ab31523 onChange={checked => {
b69ab31524 setIsDebug(checked);
b69ab31525 }}>
b69ab31526 <T>Enable Debug Tools</T>
b69ab31527 </Checkbox>
b69ab31528 <ExperimentalFeaturesCheckbox />
b69ab31529 {provider?.submitDisabledReason?.() != null && (
b69ab31530 <Checkbox
b69ab31531 checked={overrideDisabledSubmit}
b69ab31532 onChange={setOverrideDisabledSubmit}
b69ab31533 data-testid="force-enable-github-submit">
b69ab31534 <T>Force enable `sl pr submit` and `sl ghstack submit`</T>
b69ab31535 </Checkbox>
b69ab31536 )}
b69ab31537 {provider?.supportBranchingPrs === true && (
b69ab31538 <Checkbox
b69ab31539 checked={branchPrsEnabled}
b69ab31540 onChange={checked => {
b69ab31541 setBranchPrsEnabled(checked);
b69ab31542 }}>
b69ab31543 <T>Enable Experimental Branching PRs for GitHub</T>
b69ab31544 </Checkbox>
b69ab31545 )}
b69ab31546 <Row>
b69ab31547 <T
b69ab31548 replace={{
b69ab31549 $sl: <code>{commandName}</code>,
b69ab31550 $verbose: (
b69ab31551 <Checkbox checked={verboseFlag} onChange={setVerboseFlag}>
b69ab31552 <code>--verbose</code>
b69ab31553 </Checkbox>
b69ab31554 ),
b69ab31555 $debug: (
b69ab31556 <Checkbox checked={debugFlag} onChange={setDebugFlag}>
b69ab31557 <code>--debug</code>
b69ab31558 </Checkbox>
b69ab31559 ),
b69ab31560 }}>
b69ab31561 Pass extra flags to $sl: $verbose $debug
b69ab31562 </T>
b69ab31563 </Row>
b69ab31564 </Column>
b69ab31565 </DropdownField>
b69ab31566 );
b69ab31567}
b69ab31568
b69ab31569function ExperimentalFeaturesCheckbox() {
b69ab31570 const [experimentalFeaturesEnabled, setExperimentalFeaturesEnabled] =
b69ab31571 useAtom(hasExperimentalFeatures);
b69ab31572
b69ab31573 if (currentExperimentalFeaturesList.length === 0) {
b69ab31574 return null;
b69ab31575 }
b69ab31576
b69ab31577 return (
b69ab31578 <Tooltip
b69ab31579 title={t(
b69ab31580 `Enable experimental features that are still being developed and may not work as expected.
b69ab31581
b69ab31582Current experimental features: ${currentExperimentalFeaturesList.join(', ')}`,
b69ab31583 )}>
b69ab31584 <Checkbox
b69ab31585 checked={experimentalFeaturesEnabled}
b69ab31586 onChange={checked => {
b69ab31587 setExperimentalFeaturesEnabled(checked);
b69ab31588 }}>
b69ab31589 <T>Enable Experimental Features</T>
b69ab31590 </Checkbox>
b69ab31591 </Tooltip>
b69ab31592 );
b69ab31593}