Merge pull request #195 from teaxyz/open-terminal

open terminal on cli install or install package
This commit is contained in:
Neil 2023-02-11 09:35:49 +08:00 committed by GitHub
commit ed7f45a32f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 5 deletions

View file

@ -2,13 +2,11 @@ import windowStateManager from 'electron-window-state';
import { app, BrowserWindow, ipcMain } from 'electron';
import contextMenu from 'electron-context-menu';
import serve from 'electron-serve';
import path from 'path';
import fs from 'fs';
import { getInstalledPackages } from './libs/teaDir';
import { readSessionData, writeSessionData } from './libs/auth';
import type { Session } from '../src/libs/types';
import { installPackage } from './libs/cli';
import { installPackage, openTerminal } from './libs/cli';
// try {
// //@ts-ignore only used in dev should not be packaged inprod
@ -130,3 +128,14 @@ ipcMain.handle('install-package', async (_, data) => {
const result = await installPackage(data.full_name);
return result;
});
ipcMain.handle('open-terminal', async (_, data) => {
const { cmd } = data as { cmd: string };
try {
// TODO: detect if mac or linux
// current openTerminal is only design for Mac
await openTerminal(cmd);
} catch (error) {
console.error('elast:', error);
}
});

View file

@ -1,5 +1,8 @@
import { spawn } from 'child_process';
import { clean } from 'semver';
import { getGuiPath } from './teaDir';
import fs from 'fs';
import path from 'path';
export async function installPackage(full_name: string) {
return await new Promise((resolve, reject) => {
@ -27,3 +30,61 @@ export async function installPackage(full_name: string) {
});
});
}
export async function openTerminal(cmd: string) {
let scriptPath = '';
try {
// TODO SECURITY: escape the cmd if possible or create whitelist of acceptable commands
scriptPath = await createCommandScriptFile(cmd);
let stdout = ``;
let stderr = ``;
await new Promise((resolve, reject) => {
const child = spawn('/usr/bin/osascript', [scriptPath]);
child.stdout.on('data', (data) => {
stdout += data.toString().trim();
});
child.stderr.on('data', (data) => {
stderr += data.toString().trim();
});
child.on('exit', () => {
console.log('exit:', stdout);
resolve(stdout);
});
child.on('error', () => {
reject(new Error(stderr));
});
});
} catch (error) {
console.error('root:', error);
} finally {
if (scriptPath) await fs.unlinkSync(scriptPath);
}
}
const createCommandScriptFile = async (cmd: string): Promise<string> => {
const guiFolder = getGuiPath();
const tmpFilePath = path.join(guiFolder, `${+new Date()}.scpt`);
const command = `"${cmd.replace(/"/g, '\\"')}"`;
const script = `
tell application "iTerm"
activate
if application "iTerm" is running then
try
tell the first window to create tab with default profile
on error
create window with default profile
end try
end if
delay 0.1
tell the first window to tell current session to write text ${command}
end tell
`.trim();
await fs.writeFileSync(tmpFilePath, script, 'utf-8');
return tmpFilePath;
};

View file

@ -16,6 +16,10 @@ export const getTeaPath = () => {
return teaPath;
};
export const getGuiPath = () => {
return path.join(getTeaPath(), 'tea.xyz/gui');
};
export async function getInstalledPackages() {
const pkgsPath = getTeaPath();

View file

@ -1,6 +1,8 @@
<script lang="ts">
import '$appcss';
import Button from '@tea/ui/Button/Button.svelte';
const { ipcRenderer } = window.require('electron');
let copyButtonText = 'COPY';
const copyValue = `sh <(curl https://tea.xyz)`;
@ -8,6 +10,10 @@
copyButtonText = 'COPIED!';
navigator.clipboard.writeText(copyValue);
};
const onInstall = () => {
ipcRenderer.invoke('open-terminal', { cmd: copyValue });
}
</script>
<section class="border-gray mt-4 border bg-black">
@ -20,7 +26,7 @@
<footer class="border-gray flex h-20 border-t text-white">
<input class="flex-grow bg-black pl-4" disabled value="sh <(curl tea.xyz)>" />
<Button class="w-16 border-0 border-l-2 text-sm" onClick={onCopy}>{copyButtonText}</Button>
<Button class="w-56 border-0 border-l-2 text-sm" onClick={() => console.log('cli')}
<Button class="w-56 border-0 border-l-2 text-sm" onClick={onInstall}
>OPEN IN TERMINAL</Button
>
</footer>

View file

@ -7,6 +7,8 @@
import { onMount } from 'svelte';
import { getPackageBottles } from '@api';
const { ipcRenderer } = window.require('electron');
export let pkg: Package;
let bottles: Bottle[] = [];
let packageRating = 0;
@ -18,6 +20,10 @@
navigator.clipboard.writeText(copyValue);
};
const onOpenTerminal = () => {
ipcRenderer.invoke('open-terminal', { cmd: `sh <(curl tea.xyz) +${pkg.full_name}` });
}
onMount(async () => {
try {
bottles = await getPackageBottles(pkg.full_name);
@ -45,7 +51,7 @@
<footer class="border-gray flex h-20 border-t text-white">
<input class="click-copy flex-grow bg-black pl-4" disabled value={copyValue} />
<Button class="w-16 border-0 border-l-2 text-sm" onClick={onCopy}>{copyButtonText}</Button>
<Button class="w-56 border-0 border-l-2 text-sm" onClick={() => console.log('cli')}
<Button class="w-56 border-0 border-l-2 text-sm" onClick={onOpenTerminal}
>OPEN IN TERMINAL</Button
>
</footer>