Skip to content

Commit

Permalink
cli utils support more syntax samples execcontext with cli examples
Browse files Browse the repository at this point in the history
  • Loading branch information
cancerberoSgx committed Nov 16, 2018
1 parent 775021f commit 84728ba
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 27 deletions.
52 changes: 34 additions & 18 deletions samples/interactive-execute-context/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { sampleCommandTemplates } from 'imagemagick-browser'
import pMap from 'p-map'
import * as React from 'react'
import { style } from 'typestyle'
import {
arrayToCli, buildImageSrc, buildInputFile, cliToArray, ExecutionContext, extractInfo, getBuiltInImages,
getInputFilesFromHtmlInputElement, MagickFile, MagickInputFile,
} from 'wasm-imagemagick'
import pMap from 'p-map';
import * as React from 'react';
import { style } from 'typestyle';
import { arrayToCli, asCommand, buildImageSrc, buildInputFile, cliToArray, Command, ExecutionContext, extractInfo, getBuiltInImages, getInputFilesFromHtmlInputElement, MagickFile, MagickInputFile } from 'wasm-imagemagick';
import { commandExamples, Example } from './commandExamples';

export interface AppProps {
context: ExecutionContext
Expand All @@ -25,6 +22,7 @@ export interface AppState {
stdout: string
stderr: string
exitCode: number
prettyJSON: boolean
}

export class App extends React.Component<AppProps, AppState> {
Expand All @@ -43,6 +41,7 @@ export class App extends React.Component<AppProps, AppState> {
stdout: '',
stderr: '',
exitCode: 0,
prettyJSON: false,
}

protected styles = {
Expand Down Expand Up @@ -123,13 +122,18 @@ export class App extends React.Component<AppProps, AppState> {
<div>Command (Array syntax):
<textarea className={this.styles.textarea} onChange={this.commandArrayChange.bind(this)} value={this.state.commandArray}></textarea>
{(this.state.jsonError || '') && <div>Execution error: {this.state.jsonError} <br />See browser console for more information.</div>}
<label>Pretty JSON ? <input type='checkbox' onChange={this.prettyJSONChange.bind(this)}></input></label>
</div>
<div>
Or select one example
<select disabled={this.state.files.length === 0} onChange={this.selectExampleChange.bind(this)}>
{sampleCommandTemplates.map(t =>
<select disabled={this.state.files.length === 0} onChange={this.selectExampleChange.bind(this)}>
{commandExamples.map(t =>
<option>{t.name}</option>)}
</select>
{/* <select disabled={this.state.files.length === 0} onChange={this.selectExampleChange.bind(this)}>
{sampleCommandTemplates.map(t =>
<option>{t.name}</option>)}
</select> */}
</div>
</div>

Expand Down Expand Up @@ -161,6 +165,13 @@ export class App extends React.Component<AppProps, AppState> {
}
}

protected async prettyJSONChange(e: React.ChangeEvent<HTMLInputElement>) {
const arr = JSON.parse(this.state.commandArray)
const prettyJSON = e.target.checked
const commandArray = prettyJSON ? JSON.stringify(arr, null, 2) : JSON.stringify(arr)
this.setState({ ...this.state, commandArray, prettyJSON })
}

removeImage(e: React.MouseEvent<HTMLButtonElement>) {
const name = e.currentTarget.getAttribute('data-image')
this.props.context.removeFiles([name])
Expand All @@ -176,7 +187,8 @@ export class App extends React.Component<AppProps, AppState> {
}

protected commandStringChange(e: React.ChangeEvent<HTMLTextAreaElement>) {
const commandArray = JSON.stringify(cliToArray(e.target.value)) // TODO: validate
const commandArray = this.state.prettyJSON ? JSON.stringify(cliToArray(e.target.value), null, 2) : JSON.stringify(cliToArray(e.target.value))
// TODO: validate
this.setState({ ...this.state, commandString: e.target.value, commandArray })
}

Expand Down Expand Up @@ -232,13 +244,17 @@ export class App extends React.Component<AppProps, AppState> {
await this.updateImages()
}

private selectExampleCounter = 0
protected async selectExampleChange(e: React.ChangeEvent<HTMLSelectElement>) {
const template = sampleCommandTemplates[e.target.selectedIndex]
const img = this.state.files[0]
const info = await extractInfo(img)
const context = { ...template.defaultTemplateContext, imageWidth: info[0].image.geometry.width, imageHeight: info[0].image.geometry.height }
const command = template.template(context)[0].map(s => s === '$INPUT' ? img.name : s === '$OUTPUT' ? `output${this.selectExampleCounter++}.png` : s)
this.setState({ ...this.state, commandArray: JSON.stringify(command), commandString: arrayToCli(command) })
const example = commandExamples[e.currentTarget.selectedIndex]
const command = await this.commandExampleAsCommand(example)
const commandString = typeof example.command === 'string' ? example.command : arrayToCli(command)
this.setState({ ...this.state, commandArray: JSON.stringify(command), commandString })
}

protected async commandExampleAsCommand(example: Example): Promise<Command[]> {
const c = example.command as any
const command = typeof c === 'function' ? await c(this.state.files) : asCommand(c)
return command
}

}
48 changes: 48 additions & 0 deletions samples/interactive-execute-context/src/commandExamples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ExecuteCommand, asCommand, Command, MagickInputFile, extractInfo } from "wasm-imagemagick";
import { sampleCommandTemplates } from "imagemagick-browser";

export interface Example {
name: string
description: string
command: ExecuteCommand | ((images: MagickInputFile[]) => Promise<ExecuteCommand>)
}

export const commandExamples: Example[] = [
{
name: 'stars spiral and inwards',
description: `By Polar Distorting the image we can make the comets flying or spiraling into a point!`,
command: `
convert -size 250x100 xc: +noise Random -channel R -threshold .4% \\
-negate -channel RG -separate +channel \\
\( +clone \) -compose multiply -flatten \\
-virtual-pixel Tile -background Black \\
-blur 0x.6 -motion-blur 0x15-90 -normalize \\
+distort Polar 0 +repage star_inward.gif
convert -size 250x100 xc: +noise Random -channel R -threshold .4% \\
-negate -channel RG -separate +channel \\
\( +clone \) -compose multiply -flatten \\
-virtual-pixel Tile -background Black \\
-blur 0x.6 -motion-blur 0x15-60 -normalize \\
+distort Polar 0 +repage star_spiral.gif`.trim()
}
]

let selectExampleCounter = 0

sampleCommandTemplates.forEach(template => {

const example : Example = {
name: template.name,
description: template.description,
command: async function(inputFiles: MagickInputFile[]) {
const img = inputFiles[0]
const info = await extractInfo(img)
const context = { ...template.defaultTemplateContext, imageWidth: info[0].image.geometry.width, imageHeight: info[0].image.geometry.height }
const command = template.template(context)[0].map(s => s === '$INPUT' ? img.name : s === '$OUTPUT' ? `output${selectExampleCounter++}.png` : s)
return command
}
}
commandExamples.push(example)

})
34 changes: 28 additions & 6 deletions spec/util/cliSpec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { arrayToCli, cliToArray } from '../../src'
import { arrayToCli, cliToArray, asCommand } from '../../src'

export default describe('util/cli', () => {

Expand Down Expand Up @@ -52,11 +52,6 @@ compare -compose src rose: reconstruct.jpg difference.png
})

it('should support multiple commands separated by new line and respect the \\ character to continue the same command in another line', () => {
console.log(cliToArray(`
convert foo.png \\( +clone -channel R -fx B \\) \\
+swap -channel B -fx v.R bar.gif
convert bar.gif -resize 50% out.tiff
`))

expect(cliToArray(`
convert foo.png \\( +clone -channel R -fx B \\) \\
Expand All @@ -69,6 +64,33 @@ convert bar.gif -resize 50% out.tiff

})

describe('asCommand', () => {
it('should support multiple commands separated by new line and respect the \\ character to continue the same command in another line - all in the same string - no arrays', () => {

expect(asCommand(`
convert foo.png \\( +clone -channel R -fx B \\) \\
+swap -channel B -fx v.R bar.gif
convert bar.gif -resize 50% out.tiff
`))
.toEqual([
['convert', 'foo.png', '(', '+clone', '-channel', 'R', '-fx', 'B', ')', '+swap', '-channel', 'B', '-fx', 'v.R', 'bar.gif'],
['convert', 'bar.gif', '-resize', '50%', 'out.tiff']])

})

it('should support multiple commands separated by new line and respect the \\ character to continue the same command in another line - all as an array of 1 element', () => {

expect(asCommand([`
convert foo.png \\( +clone -channel R -fx B \\) \\
+swap -channel B -fx v.R bar.gif
convert bar.gif -resize 50% out.tiff
`]))
.toEqual([
['convert', 'foo.png', '(', '+clone', '-channel', 'R', '-fx', 'B', ')', '+swap', '-channel', 'B', '-fx', 'v.R', 'bar.gif'],
['convert', 'bar.gif', '-resize', '50%', 'out.tiff']])

})
})
})

})
1 change: 1 addition & 0 deletions src/executionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ExecutionContextImpl implements ExecutionContext {

async execute(configOrCommands: ExecuteConfig|ExecuteCommand|string): Promise<ExecuteResult> {
const config = asConfig(configOrCommands)
// debugger
config.inputFiles.forEach(f => {
this.imageHome.register(f)
})
Expand Down
16 changes: 13 additions & 3 deletions src/util/cli.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Command } from '..'
import { ExecuteCommand } from '../execute'

/** generates a valid command line command from given Call/execute Command */
export function arrayToCli(command: Command): string {
/** generates a valid command line command from given Call/execute Command. Works in a single command */
export function arrayToCliOne(command: Command): string {
return command
.map(c => c + '')

Expand All @@ -15,6 +15,12 @@ export function arrayToCli(command: Command): string {
.join(' ')
}

/** generates a valid command line command from given Call/execute Command . Works with multiple commands */
export function arrayToCli(command: Command|Command[]): string {
const cmd = typeof command[0] === 'string' ? [command as Command] : command as Command[]
return cmd.map(arrayToCliOne).join('\n')
}

/** generates a valid Call/execute string[] command from given command line command.
* This works only for a single command
*/
Expand Down Expand Up @@ -73,7 +79,11 @@ export function asCommand(c: ExecuteCommand): Command[] {
if (typeof c === 'string') { return asCommand([c]) }
if (!c[0]) { return [] }
if (typeof c[0] === 'string') {
return (c as string[]).map((subCommand: string) => cliToArrayOne(subCommand))
return flat((c as string[]).map((subCommand: string) => cliToArray(subCommand)))
}
return c as Command[]
}

export function flat<T>(arr: T[][]): T[] {
return arr.reduce((a, b) => a.concat(b))
}

0 comments on commit 84728ba

Please sign in to comment.