Merge branch 'development' into releases/3.3.3

This commit is contained in:
tidy-dev 2023-09-21 13:41:20 -04:00
commit 679df48fa2
22 changed files with 220 additions and 30 deletions

View file

@ -230,6 +230,9 @@ export interface IAppState {
/** Whether or not the app should use Windows' OpenSSH client */
readonly useWindowsOpenSSH: boolean
/** Whether or not the app should show the commit length warning */
readonly showCommitLengthWarning: boolean
/** The current setting for whether the user has disable usage reports */
readonly optOutOfUsageTracking: boolean
/**

View file

@ -27,6 +27,50 @@ const editors: IDarwinExternalEditor[] = [
name: 'Aptana Studio',
bundleIdentifiers: ['aptana.studio'],
},
{
name: 'Eclipse IDE for Java Developers',
bundleIdentifiers: ['epp.package.java'],
},
{
name: 'Eclipse IDE for Enterprise Java and Web Developers',
bundleIdentifiers: ['epp.package.jee'],
},
{
name: 'Eclipse IDE for C/C++ Developers',
bundleIdentifiers: ['epp.package.cpp'],
},
{
name: 'Eclipse IDE for Eclipse Committers',
bundleIdentifiers: ['epp.package.committers'],
},
{
name: 'Eclipse IDE for Embedded C/C++ Developers',
bundleIdentifiers: ['epp.package.embedcpp'],
},
{
name: 'Eclipse IDE for PHP Developers',
bundleIdentifiers: ['epp.package.php'],
},
{
name: 'Eclipse IDE for Java and DSL Developers',
bundleIdentifiers: ['epp.package.dsl'],
},
{
name: 'Eclipse IDE for RCP and RAP Developers',
bundleIdentifiers: ['epp.package.rcp'],
},
{
name: 'Eclipse Modeling Tools',
bundleIdentifiers: ['epp.package.modeling'],
},
{
name: 'Eclipse IDE for Scientific Computing',
bundleIdentifiers: ['epp.package.parallel'],
},
{
name: 'Eclipse IDE for Scout Developers',
bundleIdentifiers: ['epp.package.scout'],
},
{
name: 'MacVim',
bundleIdentifiers: ['org.vim.MacVim'],

View file

@ -147,6 +147,10 @@ const editors: ILinuxExternalEditor[] = [
name: 'Mousepad',
paths: ['/usr/bin/mousepad'],
},
{
name: 'Pulsar',
paths: ['/usr/bin/pulsar'],
},
]
async function getAvailablePath(paths: string[]): Promise<string | null> {

View file

@ -351,6 +351,7 @@ const pullRequestFileListConfigKey: string = 'pull-request-files-width'
const askToMoveToApplicationsFolderDefault: boolean = true
const confirmRepoRemovalDefault: boolean = true
const showCommitLengthWarningDefault: boolean = false
const confirmDiscardChangesDefault: boolean = true
const confirmDiscardChangesPermanentlyDefault: boolean = true
const confirmDiscardStashDefault: boolean = true
@ -359,6 +360,7 @@ const askForConfirmationOnForcePushDefault = true
const confirmUndoCommitDefault: boolean = true
const askToMoveToApplicationsFolderKey: string = 'askToMoveToApplicationsFolder'
const confirmRepoRemovalKey: string = 'confirmRepoRemoval'
const showCommitLengthWarningKey: string = 'showCommitLengthWarning'
const confirmDiscardChangesKey: string = 'confirmDiscardChanges'
const confirmDiscardStashKey: string = 'confirmDiscardStash'
const confirmCheckoutCommitKey: string = 'confirmCheckoutCommit'
@ -514,6 +516,8 @@ export class AppStore extends TypedBaseStore<IAppState> {
private useWindowsOpenSSH: boolean = false
private showCommitLengthWarning: boolean = showCommitLengthWarningDefault
private hasUserViewedStash = false
private repositoryIndicatorsEnabled: boolean
@ -994,6 +998,7 @@ export class AppStore extends TypedBaseStore<IAppState> {
currentTheme: this.currentTheme,
apiRepositories: this.apiRepositoriesStore.getState(),
useWindowsOpenSSH: this.useWindowsOpenSSH,
showCommitLengthWarning: this.showCommitLengthWarning,
optOutOfUsageTracking: this.statsStore.getOptOut(),
currentOnboardingTutorialStep: this.currentOnboardingTutorialStep,
repositoryIndicatorsEnabled: this.repositoryIndicatorsEnabled,
@ -2092,6 +2097,19 @@ export class AppStore extends TypedBaseStore<IAppState> {
confirmRepoRemovalDefault
)
// We're planning to flip the default value to false. As such we'll
// start persisting the current behavior to localstorage, so we
// can change the default in the future without affecting current
// users by removing this if statement.
if (getBoolean(showCommitLengthWarningKey) === undefined) {
setBoolean(showCommitLengthWarningKey, true)
}
this.showCommitLengthWarning = getBoolean(
showCommitLengthWarningKey,
showCommitLengthWarningDefault
)
this.confirmDiscardChanges = getBoolean(
confirmDiscardChangesKey,
confirmDiscardChangesDefault
@ -3541,6 +3559,12 @@ export class AppStore extends TypedBaseStore<IAppState> {
this.emitUpdate()
}
public _setShowCommitLengthWarning(showCommitLengthWarning: boolean) {
setBoolean(showCommitLengthWarningKey, showCommitLengthWarning)
this.showCommitLengthWarning = showCommitLengthWarning
this.emitUpdate()
}
public _setNotificationsEnabled(notificationsEnabled: boolean) {
this.notificationsStore.setNotificationsEnabled(notificationsEnabled)
this.emitUpdate()

View file

@ -1616,6 +1616,7 @@ export class App extends React.Component<IAppProps, IAppState> {
uncommittedChangesStrategy={this.state.uncommittedChangesStrategy}
selectedExternalEditor={this.state.selectedExternalEditor}
useWindowsOpenSSH={this.state.useWindowsOpenSSH}
showCommitLengthWarning={this.state.showCommitLengthWarning}
notificationsEnabled={this.state.notificationsEnabled}
optOutOfUsageTracking={this.state.optOutOfUsageTracking}
enterpriseAccount={this.getEnterpriseAccount()}
@ -2172,6 +2173,7 @@ export class App extends React.Component<IAppProps, IAppState> {
commitAuthor={repositoryState.commitAuthor}
commitMessage={popup.commitMessage}
commitSpellcheckEnabled={this.state.commitSpellcheckEnabled}
showCommitLengthWarning={this.state.showCommitLengthWarning}
dialogButtonText={popup.dialogButtonText}
dialogTitle={popup.dialogTitle}
dispatcher={this.props.dispatcher}
@ -3267,6 +3269,7 @@ export class App extends React.Component<IAppProps, IAppState> {
isShowingFoldout={this.state.currentFoldout !== null}
aheadBehindStore={this.props.aheadBehindStore}
commitSpellcheckEnabled={this.state.commitSpellcheckEnabled}
showCommitLengthWarning={this.state.showCommitLengthWarning}
onCherryPick={this.startCherryPickWithoutBranch}
pullRequestSuggestedNextAction={state.pullRequestSuggestedNextAction}
/>

View file

@ -219,6 +219,8 @@ interface IChangesListProps {
readonly shouldNudgeToCommit: boolean
readonly commitSpellcheckEnabled: boolean
readonly showCommitLengthWarning: boolean
}
interface IChangesState {
@ -818,6 +820,7 @@ export class ChangesList extends React.Component<
showNoWriteAccess={fileCount > 0 && !hasWritePermissionForRepository}
shouldNudge={this.props.shouldNudgeToCommit}
commitSpellcheckEnabled={this.props.commitSpellcheckEnabled}
showCommitLengthWarning={this.props.showCommitLengthWarning}
onCoAuthorsUpdated={this.onCoAuthorsUpdated}
onShowCoAuthoredByChanged={this.onShowCoAuthoredByChanged}
onConfirmCommitWithUnknownCoAuthors={

View file

@ -117,6 +117,8 @@ interface ICommitMessageProps {
readonly commitSpellcheckEnabled: boolean
readonly showCommitLengthWarning: boolean
/** Optional text to override default commit button text */
readonly commitButtonText?: string
@ -1344,6 +1346,7 @@ export class CommitMessage extends React.Component<
this.state.repoRuleCommitMessageFailures.status !== 'pass'
const showSummaryLengthHint =
this.props.showCommitLengthWarning &&
!showRepoRuleCommitMessageFailureHint &&
this.state.summary.length > IdealSummaryLength

View file

@ -78,6 +78,8 @@ interface IChangesSidebarProps {
readonly shouldNudgeToCommit: boolean
readonly commitSpellcheckEnabled: boolean
readonly showCommitLengthWarning: boolean
}
export class ChangesSidebar extends React.Component<IChangesSidebarProps, {}> {
@ -432,6 +434,7 @@ export class ChangesSidebar extends React.Component<IChangesSidebarProps, {}> {
currentBranchProtected={currentBranchProtected}
shouldNudgeToCommit={this.props.shouldNudgeToCommit}
commitSpellcheckEnabled={this.props.commitSpellcheckEnabled}
showCommitLengthWarning={this.props.showCommitLengthWarning}
currentRepoRulesInfo={currentRepoRulesInfo}
aheadBehind={this.props.aheadBehind}
/>

View file

@ -54,6 +54,8 @@ interface ICommitMessageDialogProps {
*/
readonly commitSpellcheckEnabled: boolean
readonly showCommitLengthWarning: boolean
/** Text for the ok button */
readonly dialogButtonText: string
@ -140,6 +142,7 @@ export class CommitMessageDialog extends React.Component<
aheadBehind={this.props.aheadBehind}
showNoWriteAccess={this.props.showNoWriteAccess}
commitSpellcheckEnabled={this.props.commitSpellcheckEnabled}
showCommitLengthWarning={this.props.showCommitLengthWarning}
onCoAuthorsUpdated={this.onCoAuthorsUpdated}
onShowCoAuthoredByChanged={this.onShowCoAuthorsChanged}
onConfirmCommitWithUnknownCoAuthors={

View file

@ -2884,6 +2884,10 @@ export class Dispatcher {
this.appStore._setUseWindowsOpenSSH(useWindowsOpenSSH)
}
public setShowCommitLengthWarning(showCommitLengthWarning: boolean) {
this.appStore._setShowCommitLengthWarning(showCommitLengthWarning)
}
public setNotificationsEnabled(notificationsEnabled: boolean) {
this.appStore._setNotificationsEnabled(notificationsEnabled)
}

View file

@ -23,7 +23,6 @@ import { openFile } from '../open-file'
import { shell } from 'electron'
import { Button } from '../button'
import { IMenuItem } from '../../../lib/menu-item'
import { LinkButton } from '../link-button'
import {
hasUnresolvedConflicts,
getUnmergedStatusEntryDescription,
@ -73,6 +72,10 @@ export const renderUnmergedFile: React.FunctionComponent<{
readonly resolvedExternalEditor: string | null
readonly openFileInExternalEditor: (path: string) => void
readonly dispatcher: Dispatcher
readonly isFileResolutionOptionsMenuOpen: boolean
readonly setIsFileResolutionOptionsMenuOpen: (
isFileResolutionOptionsMenuOpen: boolean
) => void
}> = props => {
if (
isConflictWithMarkers(props.status) &&
@ -88,6 +91,9 @@ export const renderUnmergedFile: React.FunctionComponent<{
dispatcher: props.dispatcher,
ourBranch: props.ourBranch,
theirBranch: props.theirBranch,
isFileResolutionOptionsMenuOpen: props.isFileResolutionOptionsMenuOpen,
setIsFileResolutionOptionsMenuOpen:
props.setIsFileResolutionOptionsMenuOpen,
})
}
if (
@ -140,6 +146,16 @@ const renderResolvedFile: React.FunctionComponent<{
dispatcher: props.dispatcher,
})}
</div>
<Button
className="undo-button"
onClick={makeUndoManualResolutionClickHandler(
props.path,
props.repository,
props.dispatcher
)}
>
Undo
</Button>
<div className="green-circle">
<Octicon symbol={OcticonSymbol.check} />
</div>
@ -223,6 +239,10 @@ const renderConflictedFileWithConflictMarkers: React.FunctionComponent<{
readonly dispatcher: Dispatcher
readonly ourBranch?: string
readonly theirBranch?: string
readonly isFileResolutionOptionsMenuOpen: boolean
readonly setIsFileResolutionOptionsMenuOpen: (
isFileResolutionOptionsMenuOpen: boolean
) => void
}> = props => {
const humanReadableConflicts = calculateConflicts(
props.status.conflictMarkerCount
@ -240,7 +260,8 @@ const renderConflictedFileWithConflictMarkers: React.FunctionComponent<{
props.dispatcher,
props.status,
props.ourBranch,
props.theirBranch
props.theirBranch,
props.setIsFileResolutionOptionsMenuOpen
)
const content = (
@ -261,6 +282,9 @@ const renderConflictedFileWithConflictMarkers: React.FunctionComponent<{
<Button
onClick={onDropdownClick}
className="small-button button-group-item arrow-menu"
ariaLabel="File resolution options"
ariaHaspopup="menu"
ariaExpanded={props.isFileResolutionOptionsMenuOpen}
>
<Octicon symbol={OcticonSymbol.triangleDown} />
</Button>
@ -313,8 +337,11 @@ const makeMarkerConflictDropdownClickHandler = (
repository: Repository,
dispatcher: Dispatcher,
status: ConflictsWithMarkers,
ourBranch?: string,
theirBranch?: string
ourBranch: string | undefined,
theirBranch: string | undefined,
setIsFileResolutionOptionsMenuOpen: (
isFileResolutionOptionsMenuOpen: boolean
) => void
) => {
return () => {
const absoluteFilePath = join(repository.path, relativeFilePath)
@ -339,7 +366,10 @@ const makeMarkerConflictDropdownClickHandler = (
theirBranch
),
]
showContextualMenu(items)
setIsFileResolutionOptionsMenuOpen(true)
showContextualMenu(items).then(() => {
setIsFileResolutionOptionsMenuOpen(false)
})
}
}
@ -409,21 +439,7 @@ const renderResolvedFileStatusSummary: React.FunctionComponent<{
props.branch
)
return (
<div className="file-conflicts-status">
{statusString}
&nbsp;
<LinkButton
onClick={makeUndoManualResolutionClickHandler(
props.path,
props.repository,
props.dispatcher
)}
>
Undo
</LinkButton>
</div>
)
return <div className="file-conflicts-status">{statusString}</div>
}
/** returns the name of the branch that corresponds to the chosen manual resolution */

View file

@ -45,6 +45,7 @@ interface IConflictsDialogProps {
interface IConflictsDialogState {
readonly isCommitting: boolean
readonly isAborting: boolean
readonly isFileResolutionOptionsMenuOpen: boolean
}
/**
@ -61,6 +62,7 @@ export class ConflictsDialog extends React.Component<
this.state = {
isCommitting: false,
isAborting: false,
isFileResolutionOptionsMenuOpen: false,
}
}
@ -116,6 +118,12 @@ export class ConflictsDialog extends React.Component<
private openThisRepositoryInShell = () =>
this.props.openRepositoryInShell(this.props.repository)
private setIsFileResolutionOptionsMenuOpen = (
isFileResolutionOptionsMenuOpen: boolean
) => {
this.setState({ isFileResolutionOptionsMenuOpen })
}
/**
* Renders the list of conflicts in the dialog
*/
@ -136,6 +144,10 @@ export class ConflictsDialog extends React.Component<
manualResolution: this.props.manualResolutions.get(f.path),
ourBranch: this.props.ourBranch,
theirBranch: this.props.theirBranch,
isFileResolutionOptionsMenuOpen:
this.state.isFileResolutionOptionsMenuOpen,
setIsFileResolutionOptionsMenuOpen:
this.setIsFileResolutionOptionsMenuOpen,
})
: null
)}

View file

@ -76,17 +76,29 @@ export class Advanced extends React.Component<
<div className="advanced-section">
<h2>Background updates</h2>
<Checkbox
label="Periodically fetch and refresh status of all repositories"
label="Show status icons in the repository list"
value={
this.props.repositoryIndicatorsEnabled
? CheckboxValue.On
: CheckboxValue.Off
}
onChange={this.onRepositoryIndicatorsEnabledChanged}
ariaDescribedBy="periodic-fecth-description"
/>
<p className="git-settings-description">
Allows the display of up-to-date status indicators in the repository
list. Disabling this may improve performance with many repositories.
<p
id="periodic-fecth-description"
className="git-settings-description"
>
<p>
These icons indicate which repositories have local or remote
changes, and require the periodic fetching of repositories that
are not currently selected.
</p>
<p>
Turning this off will not stop the periodic fetching of your
currently selected repository, but may improve overall app
performance for users with many repositories.
</p>
</p>
</div>
{this.renderSSHSettings()}

View file

@ -49,6 +49,7 @@ interface IPreferencesProps {
readonly repository: Repository | null
readonly onDismissed: () => void
readonly useWindowsOpenSSH: boolean
readonly showCommitLengthWarning: boolean
readonly notificationsEnabled: boolean
readonly optOutOfUsageTracking: boolean
readonly initialSelectedTab?: PreferencesTab
@ -76,6 +77,7 @@ interface IPreferencesState {
readonly initialDefaultBranch: string | null
readonly disallowedCharactersMessage: string | null
readonly useWindowsOpenSSH: boolean
readonly showCommitLengthWarning: boolean
readonly notificationsEnabled: boolean
readonly optOutOfUsageTracking: boolean
readonly confirmRepositoryRemoval: boolean
@ -124,6 +126,7 @@ export class Preferences extends React.Component<
disallowedCharactersMessage: null,
availableEditors: [],
useWindowsOpenSSH: false,
showCommitLengthWarning: false,
notificationsEnabled: true,
optOutOfUsageTracking: false,
confirmRepositoryRemoval: false,
@ -184,6 +187,7 @@ export class Preferences extends React.Component<
initialCommitterEmail,
initialDefaultBranch,
useWindowsOpenSSH: this.props.useWindowsOpenSSH,
showCommitLengthWarning: this.props.showCommitLengthWarning,
notificationsEnabled: this.props.notificationsEnabled,
optOutOfUsageTracking: this.props.optOutOfUsageTracking,
confirmRepositoryRemoval: this.props.confirmRepositoryRemoval,
@ -386,6 +390,10 @@ export class Preferences extends React.Component<
onUncommittedChangesStrategyChanged={
this.onUncommittedChangesStrategyChanged
}
showCommitLengthWarning={this.state.showCommitLengthWarning}
onShowCommitLengthWarningChanged={
this.onShowCommitLengthWarningChanged
}
/>
)
break
@ -430,6 +438,12 @@ export class Preferences extends React.Component<
this.setState({ useWindowsOpenSSH })
}
private onShowCommitLengthWarningChanged = (
showCommitLengthWarning: boolean
) => {
this.setState({ showCommitLengthWarning })
}
private onNotificationsEnabledChanged = (notificationsEnabled: boolean) => {
this.setState({ notificationsEnabled })
}
@ -573,6 +587,9 @@ export class Preferences extends React.Component<
}
this.props.dispatcher.setUseWindowsOpenSSH(this.state.useWindowsOpenSSH)
this.props.dispatcher.setShowCommitLengthWarning(
this.state.showCommitLengthWarning
)
this.props.dispatcher.setNotificationsEnabled(
this.state.notificationsEnabled
)

View file

@ -12,6 +12,7 @@ interface IPromptsPreferencesProps {
readonly confirmCheckoutCommit: boolean
readonly confirmForcePush: boolean
readonly confirmUndoCommit: boolean
readonly showCommitLengthWarning: boolean
readonly uncommittedChangesStrategy: UncommittedChangesStrategy
readonly onConfirmDiscardChangesChanged: (checked: boolean) => void
readonly onConfirmDiscardChangesPermanentlyChanged: (checked: boolean) => void
@ -20,6 +21,7 @@ interface IPromptsPreferencesProps {
readonly onConfirmRepositoryRemovalChanged: (checked: boolean) => void
readonly onConfirmForcePushChanged: (checked: boolean) => void
readonly onConfirmUndoCommitChanged: (checked: boolean) => void
readonly onShowCommitLengthWarningChanged: (checked: boolean) => void
readonly onUncommittedChangesStrategyChanged: (
value: UncommittedChangesStrategy
) => void
@ -126,6 +128,12 @@ export class Prompts extends React.Component<
this.props.onUncommittedChangesStrategyChanged(value)
}
private onShowCommitLengthWarningChanged = (
event: React.FormEvent<HTMLInputElement>
) => {
this.props.onShowCommitLengthWarningChanged(event.currentTarget.checked)
}
public render() {
return (
<DialogContent>
@ -226,6 +234,18 @@ export class Prompts extends React.Component<
onSelected={this.onUncommittedChangesStrategyChanged}
/>
</div>
<div className="advanced-section">
<h2>Commit Length</h2>
<Checkbox
label="Show commit length warning"
value={
this.props.showCommitLengthWarning
? CheckboxValue.On
: CheckboxValue.Off
}
onChange={this.onShowCommitLengthWarningChanged}
/>
</div>
</DialogContent>
)
}

View file

@ -54,6 +54,7 @@ interface IRepositoryViewProps {
readonly askForConfirmationOnCheckoutCommit: boolean
readonly focusCommitMessage: boolean
readonly commitSpellcheckEnabled: boolean
readonly showCommitLengthWarning: boolean
readonly accounts: ReadonlyArray<Account>
/**
@ -250,6 +251,7 @@ export class RepositoryView extends React.Component<
this.props.currentTutorialStep === TutorialStep.MakeCommit
}
commitSpellcheckEnabled={this.props.commitSpellcheckEnabled}
showCommitLengthWarning={this.props.showCommitLengthWarning}
/>
)
}

View file

@ -126,7 +126,7 @@
border-radius: var(--border-radius);
min-width: 150px;
&:focus {
&:focus-visible {
border-radius: var(--outlined-border-radius);
outline-offset: 3px;
}

View file

@ -77,7 +77,7 @@ fieldset.vertical-segmented-control {
}
}
&:focus li {
&:focus-visible li {
&:first-child {
border-top-left-radius: var(--outlined-border-radius);
border-top-right-radius: var(--outlined-border-radius);

View file

@ -54,8 +54,8 @@ dialog#conflicts-dialog {
.column-left {
display: flex;
flex-flow: column nowrap;
flex-grow: 1;
align-items: start;
max-width: 50%;
padding-right: var(--spacing);
.path-text-component {
@ -77,11 +77,16 @@ dialog#conflicts-dialog {
}
}
.undo-button {
align-self: center;
margin-right: var(--spacing-half);
}
.green-circle:last-child {
margin-left: auto;
margin-top: var(--spacing);
flex-shrink: 0;
flex: 0 1 auto;
align-self: center;
}
}

View file

@ -153,8 +153,8 @@
display: inline-block;
content: '';
pointer-events: none;
border: 7.5px solid transparent;
left: 7px;
border: 8px solid transparent;
left: 6px;
}
&::before {

View file

@ -1,6 +1,15 @@
{
"releases": {
"3.3.3": ["[Improved] Upgrade to Electron v24.8.3 - #17416"],
"3.3.3-beta2": ["[Improved] Upgrade to Electron v24.8.3 - #17416"],
"3.3.3-beta1": [
"[Added] Add support for Pulsar code editor on Linux - #17397. Thanks @Daeraxa!",
"[Added] Add Eclipse IDE integrations for macOS - #16991. Thanks @yuzawa-san!",
"[Improved] Added a setting to allow toggling the availability of the commit message length warning - #17370. Thanks @rystills!",
"[Improved] Clarified the outcome of toggling the setting under \"Background Updates\" in the \"Advanced\" settings - #17389",
"[Fixed] Tip of comment bubbles in Pull Request notifications is rendered correctly - #17411",
"[Improved] Undo link when resolving conflicts is now a button - #17373"
],
"3.3.2": [
"[Fixed] Use forked repository when checking repo rules on forks - #17382. Thanks @vaindil!",
"[Fixed] On macOs, the scroll bar only present when scrolling no longer overlaps conflict resolution buttons - #17374",

View file

@ -247,6 +247,8 @@ The source for the editor integration on macOS is found in
These editors are currently supported:
- [Atom](https://atom.io/)
- [Eclipse](https://www.eclipse.org/downloads/packages/release/)
- All IDE variants (Java, JavaEE, C/C++, PHP, etc.) are supported.
- [MacVim](https://macvim-dev.github.io/macvim/)
- [Neovide](https://github.com/neovide/neovide)
- [VimR](https://github.com/qvacua/vimr)
@ -351,6 +353,7 @@ These editors are currently supported:
- [JetBrains PHPStorm](https://www.jetbrains.com/phpstorm/)
- [JetBrains WebStorm](https://www.jetbrains.com/webstorm/)
- [Emacs](https://www.gnu.org/software/emacs/)
- [Pulsar](https://pulsar-edit.dev/)
These are defined in a list at the top of the file: