add option to run script directly in desktop app
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
export class Clipboard {
|
||||
public static copyText(text: string): void {
|
||||
const el = document.createElement('textarea');
|
||||
|
||||
69
src/infrastructure/CodeRunner.ts
Normal file
69
src/infrastructure/CodeRunner.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Environment } from '@/application/Environment/Environment';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import child_process from 'child_process';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
|
||||
export async function runCodeAsync(
|
||||
code: string, folderName: string, fileExtension: string,
|
||||
node = getNodeJs(), environment = Environment.CurrentEnvironment): Promise<void> {
|
||||
const dir = node.path.join(node.os.tmpdir(), folderName);
|
||||
await node.fs.promises.mkdir(dir, {recursive: true});
|
||||
const filePath = node.path.join(dir, `run.${fileExtension}`);
|
||||
await node.fs.promises.writeFile(filePath, code);
|
||||
await node.fs.promises.chmod(filePath, '755');
|
||||
const command = getExecuteCommand(filePath, environment);
|
||||
node.child_process.exec(command);
|
||||
}
|
||||
|
||||
function getExecuteCommand(scriptPath: string, environment: Environment): string {
|
||||
switch (environment.os) {
|
||||
case OperatingSystem.macOS:
|
||||
return `open -a Terminal.app ${scriptPath}`;
|
||||
// Another option with graphical sudo would be
|
||||
// `osascript -e "do shell script \\"${scriptPath}\\" with administrator privileges"`
|
||||
// However it runs in background
|
||||
case OperatingSystem.Windows:
|
||||
return scriptPath;
|
||||
default:
|
||||
throw Error('undefined os');
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeJs(): INodeJs {
|
||||
return { os, path, fs, child_process };
|
||||
}
|
||||
|
||||
export interface INodeJs {
|
||||
os: INodeOs;
|
||||
path: INodePath;
|
||||
fs: INodeFs;
|
||||
child_process: INodeChildProcess;
|
||||
}
|
||||
|
||||
export interface INodeOs {
|
||||
tmpdir(): string;
|
||||
}
|
||||
|
||||
export interface INodePath {
|
||||
join(...paths: string[]): string;
|
||||
}
|
||||
|
||||
export interface INodeChildProcess {
|
||||
exec(command: string): void;
|
||||
}
|
||||
|
||||
export interface INodeFs {
|
||||
readonly promises: INodeFsPromises;
|
||||
}
|
||||
|
||||
interface INodeFsPromisesMakeDirectoryOptions {
|
||||
recursive?: boolean;
|
||||
}
|
||||
|
||||
interface INodeFsPromises { // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v13/fs.d.ts
|
||||
chmod(path: string, mode: string | number): Promise<void>;
|
||||
mkdir(path: string, options: INodeFsPromisesMakeDirectoryOptions): Promise<string>;
|
||||
writeFile(path: string, data: string): Promise<void>;
|
||||
}
|
||||
@@ -7,8 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons';
|
||||
/** SOLID ICONS (PREFIX: fas (default)) */
|
||||
import { faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop,
|
||||
faTag, faGlobe, faSave, faBatteryFull, faBatteryHalf } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
faTag, faGlobe, faSave, faBatteryFull, faBatteryHalf, faPlay } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export class IconBootstrapper implements IVueBootstrapper {
|
||||
public bootstrap(vue: VueConstructor): void {
|
||||
@@ -24,6 +23,7 @@ export class IconBootstrapper implements IVueBootstrapper {
|
||||
faTimes,
|
||||
faFileDownload, faSave,
|
||||
faCopy,
|
||||
faPlay,
|
||||
faSearch,
|
||||
faBatteryFull, faBatteryHalf,
|
||||
faInfoCircle);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="instructions">
|
||||
<!-- <p>
|
||||
<p>
|
||||
Since you're using online version of {{ this.appName }}, you will need to do additional steps after downloading the file to execute your script on macOS:
|
||||
</p> -->
|
||||
</p>
|
||||
<p>
|
||||
<ol>
|
||||
<li>
|
||||
@@ -73,9 +73,9 @@
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<!-- <p>
|
||||
<p>
|
||||
Or download the <a :href="this.macOsDownloadUrl">offline version</a> to run your scripts directly to skip these steps.
|
||||
</p> -->
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="container" v-if="hasCode">
|
||||
<IconButton
|
||||
v-if="this.canRun"
|
||||
text="Run"
|
||||
v-on:click="executeCodeAsync"
|
||||
icon-prefix="fas" icon-name="play">
|
||||
</IconButton>
|
||||
<IconButton
|
||||
:text="this.isDesktopVersion ? 'Save' : 'Download'"
|
||||
v-on:click="saveCodeAsync"
|
||||
@@ -38,6 +44,8 @@ import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { IApplicationCode } from '@/application/Context/State/Code/IApplicationCode';
|
||||
import { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
import { runCodeAsync } from '@/infrastructure/CodeRunner';
|
||||
import { IApplicationContext } from '@/application/Context/IApplicationContext';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
@@ -48,8 +56,9 @@ import { OperatingSystem } from '@/domain/OperatingSystem';
|
||||
export default class TheCodeButtons extends StatefulVue {
|
||||
public readonly macOsModalName = 'macos-instructions';
|
||||
|
||||
public readonly isDesktopVersion = Environment.CurrentEnvironment.isDesktop;
|
||||
public canRun = false;
|
||||
public hasCode = false;
|
||||
public isDesktopVersion = Environment.CurrentEnvironment.isDesktop;
|
||||
public isMacOsCollection = false;
|
||||
public fileName = '';
|
||||
|
||||
@@ -64,8 +73,13 @@ export default class TheCodeButtons extends StatefulVue {
|
||||
this.$modal.show(this.macOsModalName);
|
||||
}
|
||||
}
|
||||
public async executeCodeAsync() {
|
||||
const context = await this.getCurrentContextAsync();
|
||||
await executeCodeAsync(context);
|
||||
}
|
||||
|
||||
protected handleCollectionState(newState: ICategoryCollectionState): void {
|
||||
this.canRun = this.isDesktopVersion && newState.collection.os === Environment.CurrentEnvironment.os;
|
||||
this.isMacOsCollection = newState.collection.os === OperatingSystem.macOS;
|
||||
this.fileName = buildFileName(newState.collection.scripting);
|
||||
this.react(newState.code);
|
||||
@@ -108,6 +122,15 @@ function buildFileName(scripting: IScriptingDefinition) {
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
async function executeCodeAsync(context: IApplicationContext) {
|
||||
await runCodeAsync(
|
||||
/*code*/ context.state.code.current,
|
||||
/*appName*/ context.app.info.name,
|
||||
/*fileExtension*/ context.state.collection.scripting.fileExtension,
|
||||
);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user