diff --git a/src/style.css b/src/style.css index ea66c7f..33eb6df 100644 --- a/src/style.css +++ b/src/style.css @@ -176,7 +176,7 @@ label { user-select: none; } -input[type=number] { +input[type=number], input[type=text], textarea { width: 100%; padding: 0.3rem; margin-bottom: 0.5rem; @@ -236,6 +236,10 @@ select:not(:disabled):hover, select:focus, input[type=number]:hover, input[type=number]:focus, +input[type=text]:hover, +input[type=text]:focus, +textarea:hover, +textarea:focus, input[type=checkbox]:focus { box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.25); } diff --git a/src/ui.tsx b/src/ui.tsx index 011023e..7744233 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -24,6 +24,13 @@ const defaultVisualizationOptions = { colorPathsByStrokeOrder: false, } +const defaultSvgIoOptions = { + apiKey: '', + prompt: '', + status: '', + vecType: 'FLAT_VECTOR' +} + const initialState = { connected: true, @@ -34,6 +41,7 @@ const initialState = { // UI state planOptions: defaultPlanOptions, visualizationOptions: defaultVisualizationOptions, + svgIoOptions: defaultSvgIoOptions, // Options used to produce the current value of |plan|. plannedOptions: null as PlanOptions | null, @@ -64,7 +72,9 @@ function reducer(state: State, action: any): State { case "SET_PLAN_OPTION": return { ...state, planOptions: { ...state.planOptions, ...action.value } }; case "SET_VISUALIZATION_OPTION": - return { ...state, visualizationOptions: { ...state.visualizationOptions, ...action.value } }; + return {...state, visualizationOptions: {...state.visualizationOptions, ...action.value }}; + case "SET_SVGIO_OPTION": + return {...state, svgIoOptions: {...state.svgIoOptions, ...action.value }} case "SET_DEVICE_INFO": return { ...state, deviceInfo: action.value }; case "SET_PAUSED": @@ -182,7 +192,7 @@ class WebSerialDriver implements Driver { if (!penIsUp) { // Move to the pen up position, or 50% if no position was found const penMotion = plan.motions.find((motion): motion is PenMotion => motion instanceof PenMotion) - const penUpPosition = penMotion ? Math.max(penMotion.initialPos, penMotion.finalPos) : device.penPctToPos(50) + const penUpPosition = penMotion ? Math.max(penMotion.initialPos, penMotion.finalPos) : device.penPctToPos(50) await this.ebb.setPenHeight(penUpPosition, 1000); } if (this.oncancelled) this.oncancelled() @@ -508,6 +518,66 @@ function VisualizationOptions({ state }: { state: State }) { ; } +/** + * Options to get an AI-Generated SVG image. + * Use svg.io API: https://api.svg.io/v1/docs + */ +function SvgIoOptions({ state }: { state: State }) { + const { apiKey, prompt, vecType, status } = state.svgIoOptions; + const dispatch = useContext(DispatchContext); + // Call the API and load the generated image + const generateImage = async () => { + dispatch({ type: "SET_SVGIO_OPTION", value: { status: 'Generating ...' } }) + try { + const apiResp = await fetch('https://api.svg.io/v1/generate-image', { + method: 'post', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ prompt, style: vecType, negativePrompt: '' }) + }); + dispatch({ type: "SET_SVGIO_OPTION", value: { status: 'Loading ...' } }) + const imageUrl = (await apiResp.json()).data[0].svgUrl; + const imgResp = await fetch(imageUrl); + const imgData = await imgResp.text(); + dispatch(setPaths(readSvg(imgData))); + } catch (error) { + console.error(error); + } finally { + dispatch({ type: "SET_SVGIO_OPTION", value: { status: '' } }); + } + } + return <> +
+ + + +
+ {apiKey !== '' && prompt !== '' + ?
+ {status ? {status} : } +
+ : ''} + ; +} + function SwapPaperSizesButton({ onClick }: { onClick: () => void }) { return rotate drawing (degrees)
- rotate drawing (degrees) + rotate drawing (degrees) { const value = (e.target as HTMLInputElement).value; @@ -620,7 +690,7 @@ function PlanStatistics({ plan }: { plan: Plan }) { function TimeLeft({ plan, progress, currentMotionStartedTime, paused }: { plan: Plan; - progress: number | null; + progress: number | null; currentMotionStartedTime: Date | null; paused: boolean; }) { @@ -921,7 +991,7 @@ function PlanOptions({ state }: { state: State }) {
+
+ AI +
+ +
+
plot
- @@ -1223,7 +1299,7 @@ function Root() { plan={plan} /> - {showDragTarget ? : null} + {showDragTarget ? : null}
;