Skip to content

Commit

Permalink
Update repository-manager extension (raycast#14372)
Browse files Browse the repository at this point in the history
* update repository-manager extension
add customizable primary action
fix list items indexes by ensuring they are unique

* add release changelog

* Add the possibility to mark a project as favorite to show it at the top of the list

* Update CHANGELOG.md

---------

Co-authored-by: Per Nielsen Tikær <per@raycast.com>
  • Loading branch information
Frameck and pernielsentikaer authored Sep 11, 2024
1 parent ebb40d3 commit a43ecfc
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 48 deletions.
9 changes: 8 additions & 1 deletion extensions/repository-manager/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Repository Manager Changelog

## [Initial Version] - 2023-10-06
## [Add Customizable Primary Action] - 2024-09-07
- Fix issue with duplicated repositories not showing up because of not unique indexes
- Add customizable primary action in settings

## [Add Favorites] - 2024-09-07
- Add the possibility to mark a project as favorite to show it at the top of the list

## [Initial Version] - 2023-10-06
15 changes: 13 additions & 2 deletions extensions/repository-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ For a project to show up in the list it must be a valid git repo (it checks if e
## Config
Every project can have a customized config which tweaks the behaviour of the commands

For example you can define custom urls with `dynamicUrlElements` to replace inside the template
For example you can define custom url placeholders with `dynamicUrlElements` to replace inside the template

In the case below the `{project}` placeholder will be replaced with `custom-value`

If `{project}` placeholder is not overwritten then the name key will be used as default

```json
{
Expand All @@ -24,9 +28,16 @@ For example you can define custom urls with `dynamicUrlElements` to replace insi
```
### Development command
`developmentCommand` has two keys:
- `apps`: valid options are `editor`, `terminal`
- `apps`: valid options are `editor` and `terminal`
- `urls`: as shown above, you can reference any url present in `urls` by key `{urls.local}`

you can completely omit `urls` key if you don't want to open the project in a browser
or you can provide `terminal` inside apps array to also open it inside your terminal app

```json
"developmentCommand": { "apps": ["editor", "terminal"] }
```

## Projects caching
Since this extension performs quite a lot of reads from the file system, it might be useful to enable projects caching in the extension preferences to avoid re-scanning every time the command is launched.

Expand Down
31 changes: 31 additions & 0 deletions extensions/repository-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@
"default": "~/dev",
"placeholder": "~/dev"
},
{
"type": "dropdown",
"name": "primaryAction",
"title": "Primary Action",
"description": "The primary action to perform when pressing Enter on a project",
"required": false,
"default": "open-in-editor",
"placeholder": "open-in-editor",
"data": [
{
"title": "Open in Editor",
"value": "open-in-editor"
},
{
"title": "Open in Terminal",
"value": "open-in-terminal"
},
{
"title": "Start Development",
"value": "start-development"
},
{
"title": "Open in Browser",
"value": "open-url"
},
{
"title": "Open Git Remotes",
"value": "open-git-remotes"
}
]
},
{
"type": "textfield",
"name": "maxScanningLevels",
Expand Down
57 changes: 57 additions & 0 deletions extensions/repository-manager/src/components/AddToFavorites.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Action, Icon, LocalStorage } from '@raycast/api'
import { withToast } from '../ui/toast'
import { getFavoriteProjects } from '../helpers'
import { Project } from '../project'

type AddToFavoritesProps = {
project: Project
onFavoriteChange: (project: Project) => void
}

export default function AddToFavorites({ project, onFavoriteChange }: AddToFavoritesProps) {
async function addProjectToFavorites() {
const favorites = await getFavoriteProjects()

if (favorites) {
const newFavorites = [...favorites, project.name].filter((value, index, self) => self.indexOf(value) === index)
await LocalStorage.setItem('favorites', JSON.stringify(newFavorites))
} else {
await LocalStorage.setItem('favorites', JSON.stringify([project.name]))
}

onFavoriteChange(project)
}

async function removeProjectFromFavorites() {
const favorites = await getFavoriteProjects()

if (favorites) {
const newFavorites = favorites.filter((value) => value !== project.name)
await LocalStorage.setItem('favorites', JSON.stringify(newFavorites))
}

onFavoriteChange(project)
}

async function toggleFavorite() {
if (project.isFavorite) {
await removeProjectFromFavorites()
} else {
await addProjectToFavorites()
}
}

return (
<Action
title={project.isFavorite ? 'Remove From Favorites' : 'Add to Favorites'}
key="add-to-favorites"
icon={project.isFavorite ? Icon.StarDisabled : Icon.Star}
shortcut={{ modifiers: ['cmd', 'shift'], key: 'f' }}
onAction={withToast({
action: toggleFavorite,
onSuccess: () => (project.isFavorite ? 'Removed from favorites' : 'Added to favorites'),
onFailure: () => (project.isFavorite ? 'Failed to remove from favorites' : 'Failed to add to favorites'),
})}
/>
)
}
2 changes: 1 addition & 1 deletion extensions/repository-manager/src/components/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function Config({ project }: ConfigProps) {
clearCache()

// append '.raycast' to the end of .gitignore file inside project directory
const gitIgnoreLines = '\n\n# Raycast Project Manager config file\n.raycast\n'
const gitIgnoreLines = '\n\n# Raycast Repository Manager config file\n.raycast\n'

fs.appendFile(`${project.fullPath}/.gitignore`, gitIgnoreLines, (err) => {
if (err) {
Expand Down
4 changes: 2 additions & 2 deletions extensions/repository-manager/src/components/Copy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ export function Copy({ project }: CopyProps) {
content={project.displayPath}
/>
{project.config.urls &&
Object.entries(project.config.urls).map(([key, value]) => {
Object.entries(project.config.urls).map(([key, value], i) => {
if (!value) {
return null
}

return (
<Action.CopyToClipboard
key={key}
key={key + i}
title={`Copy ${key} URL`}
content={value}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,21 @@ export function DirectoriesDropdown({ directories }: DirectoriesDropdownProps) {
value="all"
icon={Icon.HardDrive}
/>
<List.Dropdown.Item
key="favorites"
title="Favorites"
value="favorites"
icon={{
source: Icon.Star,
tintColor: Color.Yellow,
}}
/>
</List.Dropdown.Section>
<List.Dropdown.Section>
{directories.map((dir: Directory) => {
{directories.map((dir: Directory, i) => {
return (
<List.Dropdown.Item
key={dir.name}
key={dir.name + i}
title={dir.name}
value={dir.name}
icon={dir.icon}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export default function GitStatisticsDetail({ project }: GitStatisticsDetailProp
const markdown = `
# ${project.name}
## Git Status
${isLoading ? 'Checking status...' : ''}
## Git Statistics
${isLoading ? 'Checking statistics...' : ''}
- Total Commits: ${totalCommits}
- Total Branches: ${totalBranches}
- Total Tags: ${totalTags}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ export default function OpenGitRemotes({ project }: OpenGitRemotesProps) {
shortcut={{ modifiers: ['cmd', 'shift'], key: 'o' }}
icon={Icon.Globe}
>
{project.gitRemotes.map((remote) => {
{project.gitRemotes.map((remote, i) => {
return (
<Action.OpenInBrowser
title={`Open on ${remote.hostDisplayName} (${remote.name})`}
key={`open remote ${remote.name}`}
key={`open remote ${remote.name}-${i}`}
url={remote.url}
icon={remote.icon}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ function MetadataUrls({ project }: { project: Project }) {

return (
<React.Fragment>
{Object.entries(project.config.urls).map(([key, value]) => {
{Object.entries(project.config.urls).map(([key, value], i) => {
if (!value) {
return null
}

return (
<Detail.Metadata.Link
key={key}
key={key + i}
title=""
target={value}
text={key}
Expand All @@ -99,10 +99,10 @@ function MetadataUrls({ project }: { project: Project }) {
function MetadataGitRemotes({ project }: { project: Project }) {
return (
<React.Fragment>
{project.gitRemotes.map((remote) => {
{project.gitRemotes.map((remote, i) => {
return (
<Detail.Metadata.Link
key={remote.name}
key={remote.name + i}
title=""
target={remote.url}
text={`${remote.hostDisplayName} (${remote.name})`}
Expand Down
45 changes: 36 additions & 9 deletions extensions/repository-manager/src/components/ProjectListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Action, ActionPanel, Color, Icon, List } from '@raycast/api'
import { Action, ActionPanel, Color, Icon, List, getPreferenceValues } from '@raycast/api'
import { Project } from '../project'
import StartDevelopment from './StartDevelopment'
import { OpenInEditor, OpenInTerminal, OpenUrl } from './Open'
Expand All @@ -9,13 +9,41 @@ import Config from './Config'
import { Copy } from './Copy'
import Cache from './Cache'
import { Directory } from './DirectoriesDropdown'
import { PrimaryAction } from '../helpers'
import React from 'react'
import AddToFavorites from './AddToFavorites'

type ProjectListItemProps = {
project: Project
directories: Directory[]
onFavoriteChange: (project: Project) => void
}

export default function ProjectListItem({ project, directories }: ProjectListItemProps) {
export default function ProjectListItem({ project, directories, onFavoriteChange }: ProjectListItemProps) {
const preferences = getPreferenceValues()

const actionsMap: Record<PrimaryAction, JSX.Element> = {
'start-development': <StartDevelopment project={project} />,
'open-in-editor': <OpenInEditor project={project} />,
'open-in-terminal': <OpenInTerminal project={project} />,
'open-url': <OpenUrl project={project} />,
'open-git-remotes': <OpenGitRemotes project={project} />,
}

const defaultOrder: PrimaryAction[] = ['open-in-editor', 'start-development', 'open-in-terminal', 'open-url', 'open-git-remotes']
const primaryAction = preferences.primaryAction as PrimaryAction
const actionOrder = [primaryAction, ...defaultOrder.filter((action) => action !== primaryAction)]

const actions = () => {
return (
<>
{actionOrder.map((action) => React.cloneElement(actionsMap[action], { key: action }))}
<OpenUrl project={project} />
<OpenGitRemotes project={project} />
</>
)
}

return (
<List.Item
key={project.name}
Expand All @@ -24,19 +52,18 @@ export default function ProjectListItem({ project, directories }: ProjectListIte
subtitle={project.description || ''}
data-directory={project.primaryDirectory.name}
accessories={[
{ icon: project.isFavorite ? { source: Icon.Star, tintColor: Color.Yellow } : null, tooltip: project.isFavorite ? 'Favorite' : null },
{ text: project.displayPath, tooltip: 'Full Path' },
{ tag: { value: project.primaryDirectory.name, color: directories.find((conf) => conf.name === project.primaryDirectory.name)?.icon?.tintColor || Color.Orange }, tooltip: 'Main Directory' },
]}
actions={
<ActionPanel>
<ActionPanel.Section title="Open project">
<StartDevelopment project={project} />
<OpenInEditor project={project} />
<OpenInTerminal project={project} />
<OpenUrl project={project} />
<OpenGitRemotes project={project} />
</ActionPanel.Section>
<ActionPanel.Section title="Open project">{actions()}</ActionPanel.Section>
<ActionPanel.Section title="Actions">
<AddToFavorites
project={project}
onFavoriteChange={onFavoriteChange}
/>
<Config project={project} />
<Copy project={project} />
<Action.Push
Expand Down
32 changes: 29 additions & 3 deletions extensions/repository-manager/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import fs from 'fs'
import { getPreferenceValues, open, Cache, Color, Application, closeMainWindow, PopToRootType } from '@raycast/api'
import { getPreferenceValues, open, Cache, Color, Application, closeMainWindow, PopToRootType, LocalStorage } from '@raycast/api'
import { exec } from 'child_process'
import { Project, ProjectList } from './project'
import { Directory } from './components/DirectoriesDropdown'

export type PrimaryAction = 'start-development' | 'open-in-editor' | 'open-in-terminal' | 'open-url' | 'open-git-remotes'

interface Preferences {
projectsPath: string
primaryAction: PrimaryAction
maxScanningLevels: number
enableProjectsCaching: boolean
enableProjectsGrouping: boolean
Expand Down Expand Up @@ -68,7 +71,7 @@ const getDirectories = (path: string, depth?: number): ProjectList => {
}
}

export function fetchProjects(): ProjectList {
export async function fetchProjects(): Promise<ProjectList> {
if (!preferences.enableProjectsCaching) {
return getDirectories(preferences.projectsPath)
}
Expand All @@ -85,7 +88,12 @@ export function fetchProjects(): ProjectList {
cache.set('projects', JSON.stringify(projects))
}

return projects
const favorites = await getFavoriteProjects()

return projects.map((project: Project) => {
project.isFavorite = favorites.includes(project.name)
return project
})
}

export function fetchPrimaryDirectories(projectList: ProjectList): Directory[] {
Expand Down Expand Up @@ -148,3 +156,21 @@ export async function openUrl(url: string): Promise<void> {
await open(url)
}
}

export async function getFavoriteProjects(): Promise<string[]> {
const favorites = await LocalStorage.getItem<string>('favorites')

return favorites ? JSON.parse(favorites) : []
}

// export async function addProjectToFavorites(project: Project): Promise<void> {
// const favorites = await getFavoriteProjects();

// if (favorites) {
// const newFavorites = [...favorites, project.name]
// .filter((value, index, self) => self.indexOf(value) === index);
// await LocalStorage.setItem("favorites", JSON.stringify(newFavorites));
// } else {
// await LocalStorage.setItem("favorites", JSON.stringify([project.name]));
// }
// }
Loading

0 comments on commit a43ecfc

Please sign in to comment.