forked from raycast/extensions
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update tmux-sessioner extension (raycast#6502)
* Add tmux-sessioner extension - add feature default terminal app - fix PATH is hard code - Better error message - add command icon - simple version * better description form * implement new command to change default terminal app * Update tmux-sessioner extension - filter only terminal apps - remove unused code * update the flow to avoid laggy rendering * Update tmux-sessioner extension - Merge pull request #1 from louishuyng/chore/show-hud - chore: should hud after selecting session * better description and title command * Update tmux-sessioner extension - rename tmux session - delete tmux session - create new session command - create new session command * Update tmux-sessioner extension - Update README.md - Merge pull request #3 from louishuyng/manipulate-basic-tmux-actions * Update tmux-sessioner extension - Better icon display - Update How to Use section in README.md * Update index.tsx * Update create_new_session.tsx * metadata * Update tmux-sessioner extension - fix merge conflict - Pull contributions - create tmux manage windows command * Update tmux-sessioner extension - fix build conflict - Pull contributions * delete tmux window action --------- Co-authored-by: Per Nielsen Tikær <per@raycast.com>
- Loading branch information
1 parent
a6e6000
commit 21c4773
Showing
12 changed files
with
351 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
# Tmux Sessioner Changelog | ||
|
||
## [v0.0.1] - 2021-05-12 | ||
### Added | ||
- Allow Switching between windows 🔄 | ||
### Fixed | ||
- Fix code structure to be more readable 📝 | ||
- Refactor utils folder | ||
|
||
## [Initial Version] - 2023-04-26 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { useState, useEffect } from "react"; | ||
import { List, Icon, Action, ActionPanel, Detail } from "@raycast/api"; | ||
import { SelectTerminalApp } from "./SelectTermnialApp"; | ||
import { checkTerminalSetup } from "./utils/terminalUtils"; | ||
import { getAllWindow, switchToWindow, TmuxWindow, deleteWindow } from "./utils/windowUtils"; | ||
|
||
export default function ManageTmuxWindows() { | ||
const [windows, setWindows] = useState<Array<TmuxWindow & { keyIndex: number }>>([]); | ||
const [isLoading, setIsLoading] = useState(true); | ||
const [isTerminalSetup, setIsTerminalSetup] = useState(false); | ||
|
||
const setupListWindows = () => { | ||
getAllWindow((error, stdout) => { | ||
if (error) { | ||
console.error(`exec error: ${error}`); | ||
setIsLoading(false); | ||
return; | ||
} | ||
|
||
const lines = stdout.trim().split("\n"); | ||
|
||
if (lines?.length > 0) { | ||
let keyIndex = 0; | ||
const windows = lines.map((line) => { | ||
const [sessionName, windowName, windowIndex] = line.split(":"); | ||
keyIndex += 1; // NOTE: using key index for easily delete and remove window outside the original list | ||
return { | ||
keyIndex, | ||
sessionName, | ||
windowIndex: parseInt(windowIndex), | ||
windowName, | ||
}; | ||
}); | ||
|
||
setWindows(windows); | ||
} | ||
|
||
setIsLoading(false); | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
(async () => { | ||
setIsLoading(true); | ||
|
||
const isSetup = await checkTerminalSetup(setIsTerminalSetup); | ||
|
||
if (!isSetup) { | ||
setIsLoading(false); | ||
return; | ||
} | ||
})(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (!isTerminalSetup) { | ||
return; | ||
} | ||
|
||
// List down all tmux session | ||
setIsLoading(true); | ||
setupListWindows(); | ||
}, [isTerminalSetup]); | ||
|
||
return ( | ||
<> | ||
<List isLoading={isLoading}> | ||
{windows.map((window, index) => ( | ||
<List.Item | ||
key={index} | ||
icon={Icon.Window} | ||
title={window.windowName} | ||
subtitle={window.sessionName} | ||
actions={ | ||
<ActionPanel> | ||
<Action title="Switch To Selected Window" onAction={() => switchToWindow(window, setIsLoading)} /> | ||
<Action | ||
title="Delete This Window" | ||
onAction={() => | ||
deleteWindow(window, setIsLoading, () => | ||
setWindows(windows.filter((w) => w.keyIndex !== window.keyIndex)) | ||
) | ||
} | ||
shortcut={{ modifiers: ["cmd"], key: "d" }} | ||
/> | ||
</ActionPanel> | ||
} | ||
/> | ||
))} | ||
</List> | ||
|
||
{!isTerminalSetup && !isLoading && ( | ||
<Detail | ||
markdown="**Setup Default Terminal App Before Usage** `Go to Actions or using Cmd + k`" | ||
actions={ | ||
<ActionPanel> | ||
<Action.Push title="Setup Here" target={<SelectTerminalApp setIsTerminalSetup={setIsTerminalSetup} />} /> | ||
</ActionPanel> | ||
} | ||
/> | ||
)} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// Inspired in here https://github.com/raycast/extensions/blob/bbde227e17134f245eff10e59c8a7c2556da976c/extensions/quit-applications/src/index.tsx#L6 | ||
|
||
import { execSync } from "child_process"; | ||
|
||
export function applicationIconFromPath(path: string): string { | ||
/* Example: | ||
* '/Applications/Visual Studio Code.app' -> '/Applications/Visual Studio Code.app/Contents/Resources/{file name}.icns' | ||
*/ | ||
|
||
// read path/Contents/Info.plist and look for <key>CFBundleIconFile</key> or <key>CFBundleIconName</key> | ||
// the actual icon file is located at path/Contents/Resources/{file name}.icns | ||
|
||
const infoPlist = `${path}/Contents/Info.plist`; | ||
|
||
const possibleIconKeyNames = ["CFBundleIconFile", "CFBundleIconName"]; | ||
|
||
let iconFileName = null; | ||
|
||
for (const keyName of possibleIconKeyNames) { | ||
try { | ||
iconFileName = execSync(["plutil", "-extract", keyName, "raw", '"' + infoPlist + '"'].join(" ")) | ||
.toString() | ||
.trim(); | ||
break; | ||
} catch (error) { | ||
continue; | ||
} | ||
} | ||
|
||
if (!iconFileName) { | ||
// no icon found. fallback to empty string (no icon) | ||
return ""; | ||
} | ||
|
||
// if icon doesn't end with .icns, add it | ||
if (!iconFileName.endsWith(".icns")) { | ||
iconFileName = `${iconFileName}.icns`; | ||
} | ||
|
||
const iconPath = `${path}/Contents/Resources/${iconFileName}`; | ||
console.log(iconPath); | ||
return iconPath; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { ChildProcess, exec, ExecException, execSync } from "child_process"; | ||
import { env } from "../config"; | ||
import { LocalStorage, showHUD, showToast, Toast } from "@raycast/api"; | ||
import { openTerminal } from "./terminalUtils"; | ||
|
||
export function getAllSession( | ||
callback: (error: ExecException | null, stdout: string, stderr: string) => void | ||
): ChildProcess { | ||
return exec(`tmux list-sessions | awk '{print $1}' | sed 's/://'`, { env }, callback); | ||
} | ||
|
||
export function creatNewSession( | ||
sessionName: string, | ||
callback: (error: ExecException | null, stdout: string, stderr: string) => void | ||
): ChildProcess { | ||
return exec(`tmux new-session -d -s ${sessionName}`, { env }, callback); | ||
} | ||
|
||
export function renameSession( | ||
oldSessionName: string, | ||
newSessionName: string, | ||
callback: (error: ExecException | null, stdout: string, stderr: string) => void | ||
): ChildProcess { | ||
return exec(`tmux rename-session -t ${oldSessionName} ${newSessionName}`, { env }, callback); | ||
} | ||
|
||
export async function switchToSession(session: string, setLoading: (value: boolean) => void) { | ||
const toast = await showToast({ style: Toast.Style.Animated, title: "" }); | ||
setLoading(true); | ||
|
||
exec(`tmux switch -t ${session}`, { env }, async (error, stdout, stderr) => { | ||
if (error || stderr) { | ||
console.error(`exec error: ${error || stderr}`); | ||
|
||
toast.style = Toast.Style.Failure; | ||
toast.title = "No tmux client found 😢"; | ||
toast.message = error ? error.message : stderr; | ||
setLoading(false); | ||
|
||
return; | ||
} | ||
|
||
try { | ||
await openTerminal(); | ||
|
||
toast.style = Toast.Style.Success; | ||
toast.title = `Switched to session ${session}`; | ||
await showHUD(`Switched to session ${session}`); | ||
setLoading(false); | ||
} catch (e) { | ||
toast.style = Toast.Style.Failure; | ||
toast.title = "Terminal not supported 😢"; | ||
setLoading(false); | ||
} | ||
return; | ||
}); | ||
} | ||
|
||
export async function deleteSession(session: string, setLoading: (value: boolean) => void, callback: () => void) { | ||
setLoading(true); | ||
const toast = await showToast({ style: Toast.Style.Animated, title: "" }); | ||
|
||
exec(`tmux kill-session -t ${session}`, { env }, (error, stdout, stderr) => { | ||
if (error || stderr) { | ||
console.error(`exec error: ${error || stderr}`); | ||
|
||
toast.style = Toast.Style.Failure; | ||
toast.title = "Something went wrong 😢"; | ||
toast.message = error ? error.message : stderr; | ||
setLoading(false); | ||
return; | ||
} | ||
|
||
toast.style = Toast.Style.Success; | ||
toast.title = `Deleted session ${session}`; | ||
callback(); | ||
setLoading(false); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { LocalStorage, showToast, Toast } from "@raycast/api"; | ||
import { execSync } from "child_process"; | ||
|
||
export async function checkTerminalSetup(callback: (isTerminalSetup: boolean) => void): Promise<boolean> { | ||
const localTerminalAppName = await LocalStorage.getItem<string>("terminalAppName"); | ||
|
||
const toast = await showToast({ | ||
style: Toast.Style.Animated, | ||
title: "Checking terminal App setup", | ||
}); | ||
|
||
if (!localTerminalAppName) { | ||
toast.style = Toast.Style.Failure; | ||
toast.title = "No default terminal setup for tmux sessioner"; | ||
callback(false); | ||
|
||
return false; | ||
} else { | ||
toast.hide(); | ||
callback(true); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
export async function openTerminal() { | ||
const localTerminalAppName = await LocalStorage.getItem<string>("terminalAppName"); | ||
execSync(`open -a ${localTerminalAppName}`); | ||
} |
Oops, something went wrong.