gradio 2.0 prelaunch

This commit is contained in:
Ali Abid 2021-05-24 12:07:00 -07:00
parent 7f135f9d74
commit 857dc3f1be
33 changed files with 3424 additions and 231 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
"@testing-library/user-event": "^12.8.3",
"audio-react-recorder": "^1.0.4",
"classnames": "^2.3.1",
"fabric": "^4.5.0",
"jspreadsheet-ce": "^4.7.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",

View File

@ -1,7 +1,7 @@
import React from 'react';
import { DataURLComponentExample } from '../component_example';
import Webcam from "react-webcam";
// import { SketchField, Tools } from 'react-sketch';
import { SketchField, Tools } from '../../vendor/ReactSketch';
class ImageInput extends React.Component {
constructor(props) {
@ -65,7 +65,7 @@ class ImageInput extends React.Component {
this.sketchKey += 1;
}
return (<div className="input_image">
{/* <div className="image_preview_holder sketch">
<div className="image_preview_holder sketch">
<SketchField
ref={this.sketchRef}
key={this.sketchKey}
@ -77,7 +77,7 @@ class ImageInput extends React.Component {
backgroundColor="white"
onChange={this.getSketch}
/>
</div>*/}
</div>
</div>);
}
}

View File

@ -23,13 +23,13 @@
@apply flex-1;
}
.component_set {
@apply bg-gray-100 p-2 rounded flex flex-col gap-2;
@apply bg-gray-50 p-2 rounded flex flex-col gap-2;
}
.panel_header {
@apply mb-1 uppercase text-sm font-semibold;
}
.panel_button {
@apply flex-grow p-3 rounded bg-gray-100 hover:bg-gray-200 transition font-semibold focus:outline-none;
@apply flex-grow p-3 rounded bg-gray-50 hover:bg-gray-100 transition font-semibold focus:outline-none;
}
.panel_button.disabled {
@apply text-gray-400 cursor-not-allowed;
@ -47,7 +47,7 @@
@apply rounded-tr-none rounded-br-none;
}
.panel_button.right_panel_button {
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-300;
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-200;
}
.examples {
h4 {
@ -100,7 +100,7 @@
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
.image_preview_holder {
@apply w-full h-full flex justify-center items-center bg-gray-300 relative inline-block;
@apply w-full h-full flex justify-center items-center bg-gray-200 relative inline-block;
}
.sketch > div {
@apply bg-white;
@ -118,7 +118,7 @@
.input_radio {
@apply flex flex-wrap gap-2;
.radio_item {
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
}
.radio_item.selected {
@apply bg-yellow-500 text-white;
@ -133,7 +133,7 @@
.input_checkbox_group {
@apply flex flex-wrap gap-2;
.checkbox_item {
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
}
.checkbox_item.selected {
@apply bg-yellow-500 text-white;
@ -159,7 +159,7 @@
.input_checkbox {
@apply flex flex-wrap gap-2;
.checkbox_item {
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded cursor-pointer flex items-center gap-1;
}
.checkbox_item.selected {
@apply bg-yellow-500 text-white;
@ -185,7 +185,7 @@
.input_dropdown {
@apply inline-block relative;
.selector {
@apply bg-gray-300 text-gray-700 py-2 px-4 font-semibold rounded inline-flex items-center;
@apply bg-gray-200 text-gray-700 py-2 px-4 font-semibold rounded inline-flex items-center;
}
.current {
@apply mr-1;
@ -212,7 +212,7 @@
.input_slider {
@apply text-center;
.range {
@apply w-full appearance-none bg-gray-300 hover:bg-gray-200 transition rounded h-4;
@apply w-full appearance-none bg-gray-200 hover:bg-gray-200 transition rounded h-4;
}
.range::-webkit-slider-thumb {
-webkit-appearance: none;
@ -245,7 +245,7 @@
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
.video_preview_holder {
@apply w-full h-full flex justify-center items-center bg-gray-300;
@apply w-full h-full flex justify-center items-center bg-gray-200;
}
.video_preview {
@apply w-full h-full object-contain;

View File

@ -5,10 +5,16 @@
.gradio_interface[theme="huggingface"] {
@apply container mx-auto my-4;
.title {
@apply text-center;
@apply text-center p-4 text-4xl;
}
.description {
@apply pb-4;
}
.loading {
@apply absolute right-0.5;
@apply absolute right-1;
}
.loading img {
@apply h-5;
}
.panels {
@apply flex flex-wrap justify-center gap-4;
@ -29,7 +35,7 @@
@apply flex gap-4 my-4;
}
.screenshot_set {
@apply flex flex-grow;
@apply hidden flex hidden flex-grow;
}
.panel_button.left_panel_button {
@apply rounded-tr-none rounded-br-none;
@ -37,6 +43,41 @@
.panel_button.right_panel_button {
@apply rounded-tl-none rounded-bl-none bg-gray-200 hover:bg-gray-300;
}
.examples {
h4 {
@apply text-lg font-semibold my-2;
}
.examples_control {
@apply hidden flex hidden gap-2;
}
.examples_control_left {
@apply flex gap-2;
}
.shortcut {
@apply block text-xs;
}
.examples_control button {
@apply bg-gray-100 hover:bg-gray-200 p-2;
}
.examples_table {
@apply table-auto p-2 bg-gray-100 mt-4 rounded;
tbody tr {
@apply cursor-pointer;
}
thead {
@apply border-b-2 border-gray-300;
}
tbody tr:hover {
@apply bg-purple-500 text-white;
}
tbody tr.selected {
@apply font-bold;
}
td, th {
@apply py-2 px-4;
}
}
}
/* Input Components */
textarea.input_text {
@apply w-full p-3 border border-gray-200 rounded-lg shadow-inner outline-none focus:ring-1 focus:ring-inset focus:ring-indigo-200 focus:shadow-inner placeholder-gray-400;
@ -51,13 +92,22 @@
@apply w-full h-80;
.upload_zone {
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
}
.image_preview_holder {
@apply w-full h-full flex justify-center items-center bg-gray-300;
@apply w-full h-full flex justify-center items-center bg-gray-300 relative inline-block;
}
.image_preview {
.sketch > div {
@apply bg-white;
}
.image_preview, video {
@apply w-full h-full object-contain;
}
.snapshot {
@apply absolute bottom-0 w-full bg-white bg-opacity-90 py-3 font-bold text-lg text-center;
}
}
.input_image_example {
@apply h-24;
}
.input_radio {
@apply flex flex-wrap gap-2;
@ -182,7 +232,36 @@
audio {
@apply w-full;
}
} /* Output Components */
}
.input_video {
@apply w-full h-80;
.upload_zone {
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
.video_preview_holder {
@apply w-full h-full flex justify-center items-center bg-gray-200;
}
.video_preview {
@apply w-full h-full object-contain;
}
}
.input_file {
@apply w-full h-80;
.upload_zone {
@apply border-8 border-gray-300 border-dashed w-full h-full flex justify-center items-center text-3xl text-gray-400 text-center cursor-pointer leading-10;
}
.file_preview_holder {
@apply w-full h-full flex flex-col justify-center items-center relative inline-block;
}
.file_name {
@apply text-6xl p-6;
}
.file_size {
@apply text-2xl p-2;
}
}
/* Output Components */
.output_label {
.output_class {
@apply hidden;
@ -212,7 +291,47 @@
}
.output_audio {
audio {
@apply w-full;
@apply w-full border-2 border-gray-300 rounded;
}
}
.output_highlightedtext {
.category_legend {
@apply flex flex-wrap gap-4 mb-2;
.category-label {
@apply flex gap-2;
}
.colorbox {
@apply w-6;
}
}
}
.output_keyvalues {
table {
@apply bg-white;
thead {
@apply font-bold;
}
td, th {
@apply p-2;
}
}
}
.output_video {
@apply w-full h-80 object-contain flex justify-center;
video {
@apply h-full;
}
}
.output_file {
@apply w-full h-80;
.file_display {
@apply w-full h-full flex flex-col justify-center items-center relative inline-block;
}
.file_name {
@apply text-6xl p-6;
}
.file_size {
@apply text-2xl p-2;
}
}
}

View File

@ -0,0 +1,725 @@
/*eslint no-unused-vars: 0*/
// @ts-nocheck
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import History from "../../history";
import { uuid4 } from "../../utils";
import Arrow from "./../../tools/Arrow";
import Tool from "./../../tools";
import DefaultTool from "./../../tools/defaul-tool";
import Select from "./../../tools/Select";
import Pencil from "./../../tools/Pencil";
import Line from "./../../tools/Line";
import Rectangle from "./../../tools/Rectangle";
import RectangleLabel from "./../../tools/Rectangle/rectangle-label";
import Circle from "./../../tools/Circle";
import Pan from "./../../tools/Pan";
import Highlighter from "./../../tools/Highlighter";
import { fabric } from "fabric";
/**
* Sketch Tool based on FabricJS for React Applications
*/
class SketchField extends PureComponent {
static propTypes = {
// the color of the line
lineColor: PropTypes.string,
// The width of the line
lineWidth: PropTypes.number,
// the fill color of the shape when applicable
fillColor: PropTypes.string,
// the background color of the sketch
backgroundColor: PropTypes.string,
// the opacity of the object
opacity: PropTypes.number,
// number of undo/redo steps to maintain
undoSteps: PropTypes.number,
// The tool to use, can be pencil, rectangle, circle, brush;
tool: PropTypes.string,
// image format when calling toDataURL
imageFormat: PropTypes.string,
// Sketch data for controlling sketch from
// outside the component
value: PropTypes.object,
// Set to true if you wish to force load the given value, even if it is the same
forceValue: PropTypes.bool,
// Specify some width correction which will be applied on auto resize
widthCorrection: PropTypes.number,
// Specify some height correction which will be applied on auto resize
heightCorrection: PropTypes.number,
// Specify action on change
onChange: PropTypes.func,
// Default initial value
defaultValue: PropTypes.object,
// Sketch width
width: PropTypes.number,
// Sketch height
height: PropTypes.number,
// event object added
onObjectAdded: PropTypes.func,
// event object modified
onObjectModified: PropTypes.func,
// event object removed
onObjectRemoved: PropTypes.func,
// event mouse down
onMouseDown: PropTypes.func,
// event mouse move
onMouseMove: PropTypes.func,
// event mouse up
onMouseUp: PropTypes.func,
// event mouse out
onMouseOut: PropTypes.func,
// event object move
onObjectMoving: PropTypes.func,
// event object scale
onObjectScaling: PropTypes.func,
// event object rotating
onObjectRotating: PropTypes.func,
// Class name to pass to container div of canvas
className: PropTypes.string,
// Style options to pass to container div of canvas
style: PropTypes.object,
};
static defaultProps = {
lineColor: "black",
lineWidth: 10,
fillColor: "transparent",
backgroundColor: "transparent",
opacity: 1.0,
undoSteps: 25,
tool: null,
widthCorrection: 0,
heightCorrection: 0,
forceValue: false,
onObjectAdded: () => null,
onObjectModified: () => null,
onObjectRemoved: () => null,
onMouseDown: () => null,
onMouseMove: () => null,
onMouseUp: () => null,
onMouseOut: () => null,
onObjectMoving: () => null,
onObjectScaling: () => null,
onObjectRotating: () => null,
};
state = {
action: true,
};
_initTools = (fabricCanvas) => {
this._tools = {};
this._tools[Tool.Select] = new Select(fabricCanvas);
this._tools[Tool.Pencil] = new Pencil(fabricCanvas);
this._tools[Tool.Line] = new Line(fabricCanvas);
this._tools[Tool.Arrow] = new Arrow(fabricCanvas);
this._tools[Tool.Rectangle] = new Rectangle(fabricCanvas);
this._tools[Tool.RectangleLabel] = new RectangleLabel(fabricCanvas);
this._tools[Tool.Circle] = new Circle(fabricCanvas);
this._tools[Tool.Pan] = new Pan(fabricCanvas);
this._tools[Tool.Highlighter] = new Highlighter(fabricCanvas);
this._tools[Tool.DefaultTool] = new DefaultTool(fabricCanvas);
};
/**
* Enable touch Scrolling on Canvas
*/
enableTouchScroll = () => {
let canvas = this._fc;
if (canvas.allowTouchScrolling) return;
canvas.allowTouchScrolling = true;
};
/**
* Disable touch Scrolling on Canvas
*/
disableTouchScroll = () => {
let canvas = this._fc;
if (canvas.allowTouchScrolling) {
canvas.allowTouchScrolling = false;
}
};
/**
* Add an image as object to the canvas
*
* @param dataUrl the image url or Data Url
* @param options object to pass and change some options when loading image, the format of the object is:
*
* {
* left: <Number: distance from left of canvas>,
* top: <Number: distance from top of canvas>,
* scale: <Number: initial scale of image>
* }
*/
addImg = (dataUrl, options = {}) => {
let canvas = this._fc;
fabric.Image.fromURL(dataUrl, (oImg) => {
let opts = {
left: Math.random() * (canvas.getWidth() - oImg.width * 0.5),
top: Math.random() * (canvas.getHeight() - oImg.height * 0.5),
scale: 0.5,
};
Object.assign(opts, options);
oImg.scale(opts.scale);
oImg.set({
left: opts.left,
top: opts.top,
});
canvas.add(oImg);
});
};
/**
* Action when an object is added to the canvas
*/
_onObjectAdded = (e) => {
const { onObjectAdded } = this.props;
if (!this.state.action) {
this.setState({ action: true });
return;
}
let obj = e.target;
obj.__version = 1;
// record current object state as json and save as originalState
let objState = obj.toJSON();
obj.__originalState = objState;
let state = JSON.stringify(objState);
// object, previous state, current state
this._history.keep([obj, state, state]);
onObjectAdded(e);
};
/**
* Action when an object is moving around inside the canvas
*/
_onObjectMoving = (e) => {
const { onObjectMoving } = this.props;
onObjectMoving(e);
};
/**
* Action when an object is scaling inside the canvas
*/
_onObjectScaling = (e) => {
const { onObjectScaling } = this.props;
onObjectScaling(e);
};
/**
* Action when an object is rotating inside the canvas
*/
_onObjectRotating = (e) => {
const { onObjectRotating } = this.props;
onObjectRotating(e);
};
_onObjectModified = (e) => {
const { onObjectModified } = this.props;
let obj = e.target;
obj.__version += 1;
let prevState = JSON.stringify(obj.__originalState);
let objState = obj.toJSON();
// record current object state as json and update to originalState
obj.__originalState = objState;
let currState = JSON.stringify(objState);
this._history.keep([obj, prevState, currState]);
onObjectModified(e);
};
/**
* Action when an object is removed from the canvas
*/
_onObjectRemoved = (e) => {
const { onObjectRemoved } = this.props;
let obj = e.target;
if (obj.__removed) {
obj.__version += 1;
return;
}
obj.__version = 0;
onObjectRemoved(e);
};
/**
* Action when the mouse button is pressed down
*/
_onMouseDown = (e) => {
const { onMouseDown } = this.props;
this._selectedTool.doMouseDown(e);
onMouseDown(e);
};
/**
* Action when the mouse cursor is moving around within the canvas
*/
_onMouseMove = (e) => {
const { onMouseMove } = this.props;
this._selectedTool.doMouseMove(e);
onMouseMove(e);
};
/**
* Action when the mouse cursor is moving out from the canvas
*/
_onMouseOut = (e) => {
const { onMouseOut } = this.props;
this._selectedTool.doMouseOut(e);
if (this.props.onChange) {
let onChange = this.props.onChange;
setTimeout(() => {
onChange(e.e);
}, 10);
}
onMouseOut(e);
};
_onMouseUp = (e) => {
const { onMouseUp } = this.props;
this._selectedTool.doMouseUp(e);
// Update the final state to new-generated object
// Ignore Path object since it would be created after mouseUp
// Assumed the last object in canvas.getObjects() in the newest object
if (this.props.tool !== Tool.Pencil) {
const canvas = this._fc;
const objects = canvas.getObjects();
const newObj = objects[objects.length - 1];
if (newObj && newObj.__version === 1) {
newObj.__originalState = newObj.toJSON();
}
}
if (this.props.onChange) {
let onChange = this.props.onChange;
setTimeout(() => {
onChange(e.e);
}, 10);
}
onMouseUp(e);
};
/**
* Track the resize of the window and update our state
*
* @param e the resize event
* @private
*/
_resize = (e, canvasWidth = null, canvasHeight = null) => {
if (e) e.preventDefault();
let { widthCorrection, heightCorrection } = this.props;
let canvas = this._fc;
let { offsetWidth, clientHeight } = this._container;
let prevWidth = canvasWidth || canvas.getWidth();
let prevHeight = canvasHeight || canvas.getHeight();
let wfactor = ((offsetWidth - widthCorrection) / prevWidth).toFixed(2);
let hfactor = ((clientHeight - heightCorrection) / prevHeight).toFixed(2);
canvas.setWidth(offsetWidth - widthCorrection);
canvas.setHeight(clientHeight - heightCorrection);
if (canvas.backgroundImage) {
// Need to scale background images as well
let bi = canvas.backgroundImage;
bi.width = bi.width * wfactor;
bi.height = bi.height * hfactor;
}
let objects = canvas.getObjects();
for (let i in objects) {
let obj = objects[i];
let scaleX = obj.scaleX;
let scaleY = obj.scaleY;
let left = obj.left;
let top = obj.top;
let tempScaleX = scaleX * wfactor;
let tempScaleY = scaleY * hfactor;
let tempLeft = left * wfactor;
let tempTop = top * hfactor;
obj.scaleX = tempScaleX;
obj.scaleY = tempScaleY;
obj.left = tempLeft;
obj.top = tempTop;
obj.setCoords();
}
canvas.renderAll();
canvas.calcOffset();
};
/**
* Sets the background color for this sketch
* @param color in rgba or hex format
*/
_backgroundColor = (color) => {
if (!color) return;
let canvas = this._fc;
canvas.setBackgroundColor(color, () => canvas.renderAll());
};
/**
* Zoom the drawing by the factor specified
*
* The zoom factor is a percentage with regards the original, for example if factor is set to 2
* it will double the size whereas if it is set to 0.5 it will half the size
*
* @param factor the zoom factor
*/
zoom = (factor) => {
let canvas = this._fc;
let objects = canvas.getObjects();
for (let i in objects) {
objects[i].scaleX = objects[i].scaleX * factor;
objects[i].scaleY = objects[i].scaleY * factor;
objects[i].left = objects[i].left * factor;
objects[i].top = objects[i].top * factor;
objects[i].setCoords();
}
canvas.renderAll();
canvas.calcOffset();
};
/**
* Perform an undo operation on canvas, if it cannot undo it will leave the canvas intact
*/
undo = () => {
let history = this._history;
let [obj, prevState, currState] = history.getCurrent();
history.undo();
if (obj.__removed) {
this.setState({ action: false }, () => {
this._fc.add(obj);
obj.__version -= 1;
obj.__removed = false;
});
} else if (obj.__version <= 1) {
this._fc.remove(obj);
} else {
obj.__version -= 1;
obj.setOptions(JSON.parse(prevState));
obj.setCoords();
this._fc.renderAll();
}
if (this.props.onChange) {
this.props.onChange();
}
};
/**
* Perform a redo operation on canvas, if it cannot redo it will leave the canvas intact
*/
redo = () => {
let history = this._history;
if (history.canRedo()) {
let canvas = this._fc;
//noinspection Eslint
let [obj, prevState, currState] = history.redo();
if (obj.__version === 0) {
this.setState({ action: false }, () => {
canvas.add(obj);
obj.__version = 1;
});
} else {
obj.__version += 1;
obj.setOptions(JSON.parse(currState));
}
obj.setCoords();
canvas.renderAll();
if (this.props.onChange) {
this.props.onChange();
}
}
};
/**
* Delegation method to check if we can perform an undo Operation, useful to disable/enable possible buttons
*
* @returns {*} true if we can undo otherwise false
*/
canUndo = () => {
return this._history.canUndo();
};
/**
* Delegation method to check if we can perform a redo Operation, useful to disable/enable possible buttons
*
* @returns {*} true if we can redo otherwise false
*/
canRedo = () => {
return this._history.canRedo();
};
/**
* Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
*
* Available Options are
* <table style="width:100%">
*
* <tr><td><b>Name</b></td><td><b>Type</b></td><td><b>Argument</b></td><td><b>Default</b></td><td><b>Description</b></td></tr>
* <tr><td>format</td> <td>String</td> <td><optional></td><td>png</td><td>The format of the output image. Either "jpeg" or "png"</td></tr>
* <tr><td>quality</td><td>Number</td><td><optional></td><td>1</td><td>Quality level (0..1). Only used for jpeg.</td></tr>
* <tr><td>multiplier</td><td>Number</td><td><optional></td><td>1</td><td>Multiplier to scale by</td></tr>
* <tr><td>left</td><td>Number</td><td><optional></td><td></td><td>Cropping left offset. Introduced in v1.2.14</td></tr>
* <tr><td>top</td><td>Number</td><td><optional></td><td></td><td>Cropping top offset. Introduced in v1.2.14</td></tr>
* <tr><td>width</td><td>Number</td><td><optional></td><td></td><td>Cropping width. Introduced in v1.2.14</td></tr>
* <tr><td>height</td><td>Number</td><td><optional></td><td></td><td>Cropping height. Introduced in v1.2.14</td></tr>
*
* </table>
*
* @returns {String} URL containing a representation of the object in the format specified by options.format
*/
toDataURL = (options) => this._fc.toDataURL(options);
/**
* Returns JSON representation of canvas
*
* @param propertiesToInclude Array <optional> Any properties that you might want to additionally include in the output
* @returns {string} JSON string
*/
toJSON = (propertiesToInclude) => this._fc.toJSON(propertiesToInclude);
/**
* Populates canvas with data from the specified JSON.
*
* JSON format must conform to the one of fabric.Canvas#toDatalessJSON
*
* @param json JSON string or object
*/
fromJSON = (json) => {
if (!json) return;
let canvas = this._fc;
setTimeout(() => {
canvas.loadFromJSON(json, () => {
if (this.props.tool === Tool.DefaultTool) {
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
}
canvas.renderAll();
if (this.props.onChange) {
this.props.onChange();
}
});
}, 100);
};
/**
* Clear the content of the canvas, this will also clear history but will return the canvas content as JSON to be
* used as needed in order to undo the clear if possible
*
* @param propertiesToInclude Array <optional> Any properties that you might want to additionally include in the output
* @returns {string} JSON string of the canvas just cleared
*/
clear = (propertiesToInclude) => {
let discarded = this.toJSON(propertiesToInclude);
this._fc.clear();
this._history.clear();
return discarded;
};
hasSelection = () => {
let canvas = this._fc;
return !!canvas.getActiveObject();
};
clearSelection = () => {
let canvas = this._fc;
canvas.discardActiveObject();
canvas.requestRenderAll();
};
/**
* Remove selected object from the canvas
*/
removeSelected = () => {
let canvas = this._fc;
let activeObj = canvas.getActiveObject();
if (activeObj) {
let selected = [];
if (activeObj.type === "activeSelection") {
activeObj.forEachObject((obj) => selected.push(obj));
} else {
selected.push(activeObj);
}
selected.forEach((obj) => {
obj.__removed = true;
let objState = obj.toJSON();
obj.__originalState = objState;
let state = JSON.stringify(objState);
this._history.keep([obj, state, state]);
canvas.remove(obj);
});
canvas.discardActiveObject();
canvas.requestRenderAll();
}
};
copy = () => {
let canvas = this._fc;
canvas.getActiveObject().clone((cloned) => (this._clipboard = cloned));
};
paste = () => {
// clone again, so you can do multiple copies.
this._clipboard.clone((clonedObj) => {
let canvas = this._fc;
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === "activeSelection") {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject((obj) => canvas.add(obj));
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
this._clipboard.top += 10;
this._clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
};
/**
* Sets the background from the dataUrl given
*
* @param dataUrl the dataUrl to be used as a background
* @param options
*/
setBackgroundFromDataUrl = (dataUrl, options = {}) => {
let canvas = this._fc;
let img = new Image();
img.setAttribute("crossOrigin", "anonymous");
const { stretched, stretchedX, stretchedY, ...fabricOptions } = options;
img.onload = () => {
const imgObj = new fabric.Image(img);
if (stretched || stretchedX) imgObj.scaleToWidth(canvas.width);
if (stretched || stretchedY) imgObj.scaleToHeight(canvas.height);
canvas.setBackgroundImage(imgObj, () => canvas.renderAll(), fabricOptions);
};
img.src = dataUrl;
};
addText = (text, options = {}) => {
let canvas = this._fc;
let iText = new fabric.IText(text, options);
let opts = {
left: (canvas.getWidth() - iText.width) * 0.5,
top: (canvas.getHeight() - iText.height) * 0.5,
};
Object.assign(options, opts);
iText.set({
left: options.left,
top: options.top,
});
canvas.add(iText);
};
callEvent = (e, eventFunction) => {
if (this._selectedTool) eventFunction(e);
};
componentDidMount = () => {
let { tool, value, undoSteps, defaultValue, backgroundColor } = this.props;
let canvas = (this._fc = new fabric.Canvas(
this._canvas /*, {
preserveObjectStacking: false,
renderOnAddRemove: false,
skipTargetFind: true
}*/
));
this._initTools(canvas);
// set initial backgroundColor
this._backgroundColor(backgroundColor);
let selectedTool = this._tools[tool];
if (selectedTool) selectedTool.configureCanvas(this.props);
this._selectedTool = selectedTool;
// Control resize
window.addEventListener("resize", this._resize, false);
// Initialize History, with maximum number of undo steps
this._history = new History(undoSteps);
// Events binding
canvas.on("object:added", (e) => this.callEvent(e, this._onObjectAdded));
canvas.on("object:modified", (e) => this.callEvent(e, this._onObjectModified));
canvas.on("object:removed", (e) => this.callEvent(e, this._onObjectRemoved));
canvas.on("mouse:down", (e) => this.callEvent(e, this._onMouseDown));
canvas.on("mouse:move", (e) => this.callEvent(e, this._onMouseMove));
canvas.on("mouse:up", (e) => this.callEvent(e, this._onMouseUp));
canvas.on("mouse:out", (e) => this.callEvent(e, this._onMouseOut));
canvas.on("object:moving", (e) => this.callEvent(e, this._onObjectMoving));
canvas.on("object:scaling", (e) => this.callEvent(e, this._onObjectScaling));
canvas.on("object:rotating", (e) => this.callEvent(e, this._onObjectRotating));
// IText Events fired on Adding Text
// canvas.on("text:event:changed", console.log)
// canvas.on("text:selection:changed", console.log)
// canvas.on("text:editing:entered", console.log)
// canvas.on("text:editing:exited", console.log)
this.disableTouchScroll();
this._resize();
// initialize canvas with controlled value if exists
(value || defaultValue) && this.fromJSON(value || defaultValue);
};
componentWillUnmount = () => window.removeEventListener("resize", this._resize);
componentDidUpdate = (prevProps, prevState) => {
if (
this.props.width !== prevProps.width ||
this.props.height !== prevProps.height
) {
this._resize();
}
if (this.props.tool !== prevProps.tool) {
this._selectedTool = this._tools[this.props.tool];
//Bring the cursor back to default if it is changed by a tool
this._fc.defaultCursor = "default";
if (this._selectedTool) {
this._selectedTool.configureCanvas(this.props);
}
}
if (this.props.backgroundColor !== prevProps.backgroundColor) {
this._backgroundColor(this.props.backgroundColor);
}
if (
this.props.value !== prevProps.value ||
(this.props.value && this.props.forceValue)
) {
this.fromJSON(this.props.value);
}
};
render = () => {
let { className, style, width, height } = this.props;
let canvasDivStyle = Object.assign(
{},
style ? style : {},
width ? { width: width } : {},
height ? { height: height } : { height: 512 }
);
return (
<div
className={className}
ref={(c) => (this._container = c)}
style={canvasDivStyle}>
<canvas id={uuid4()} ref={(c) => (this._canvas = c)}>
Sorry, Canvas HTML5 element is not supported by your browser :(
</canvas>
</div>
);
};
}
export default SketchField;

View File

@ -0,0 +1,215 @@
/* global expect, describe,it */
/* eslint no-console: 0 */
/* eslint-env node */
import React from "react";
import { mount } from "enzyme";
import SketchField from "./index";
function objectFromDrag(canvas, from = { x: 0, y: 0 }, to = { x: 10, y: 10 }, id) {
function MouseEventPositionGenerator(pos = { x: 0, y: 0 }) {
const eventX = ["x", "pageX", "screenX", "clientX", "offsetX"];
const eventY = ["y", "pageY", "screenY", "clientY", "offsetY"];
const generated = {};
eventX.forEach((key) => (generated[key] = pos.x));
eventY.forEach((key) => (generated[key] = pos.y));
return generated;
}
canvas.trigger("mouse:down", { e: MouseEventPositionGenerator(from) });
canvas.trigger("mouse:move", { e: MouseEventPositionGenerator(to) });
canvas.trigger("mouse:up", { e: MouseEventPositionGenerator(to) });
// Get the last object as the last created object
const objects = canvas.getObjects();
const newObj = objects[objects.length - 1];
id && (newObj.id = id);
return newObj;
}
describe("SketchField", () => {
it("Loads Normally", () => {
require("./index");
});
it("Contains canvas tag", () => {
const sketch = mount(<SketchField />);
expect(sketch.getDOMNode("canvas")).toBeDefined();
});
it("Drag to create rectangle", () => {
const sketch = mount(<SketchField tool="rectangle" />);
const canvas = sketch.instance()._fc;
expect(canvas).toBeDefined();
const startPt = { x: 10, y: 10 };
const endPt = { x: 40, y: 50 };
const bounding = {
left: startPt.x,
top: startPt.y,
width: endPt.x - startPt.x,
height: endPt.y - startPt.y,
};
// From left-top to right-bottom
objectFromDrag(canvas, startPt, endPt);
// Check the rectangle existed
expect(canvas.getObjects()[0]).toBeDefined();
const rect1 = canvas.getObjects()[0];
expect(rect1.type).toEqual("rect");
// Check the rectangle dimension
expect({
left: rect1.left,
top: rect1.top,
width: rect1.width,
height: rect1.height,
}).toEqual(bounding);
canvas.remove(rect1);
// From right-bottom to left-top;
objectFromDrag(canvas, endPt, startPt);
const rect2 = canvas.getObjects()[0];
expect(rect2.type).toEqual("rect");
// Check the rectangle dimension
expect({
left: rect2.left,
top: rect2.top,
width: rect2.width,
height: rect2.height,
}).toEqual(bounding);
});
it("Undo/Redo for multiple rectangles add to canvas", () => {
const sketch = mount(<SketchField tool="rectangle" />).instance();
const canvas = sketch._fc;
expect(canvas).toBeDefined();
const startPt = { x: 10, y: 10 };
const endPt = { x: 40, y: 50 };
canvas.renderOnAddRemove = false;
objectFromDrag(canvas, startPt, endPt, "rect1");
objectFromDrag(canvas, startPt, endPt, "rect2");
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1", "rect2"]);
sketch.undo();
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
sketch.undo();
expect(canvas.getObjects().map((o) => o.id)).toEqual([]);
sketch.redo();
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
objectFromDrag(canvas, startPt, endPt, "rect3");
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1", "rect3"]);
sketch.undo();
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect1"]);
});
it("Undo/Redo for multiple modification for single rectangle", () => {
const sketch = mount(<SketchField tool="rectangle" />).instance();
const canvas = sketch._fc;
expect(canvas).toBeDefined();
const startPt = { x: 10, y: 10 };
const endPt = { x: 40, y: 50 };
// [Action1] Add new rectange object and save its state
const stateStack = [];
const rect = objectFromDrag(canvas, startPt, endPt);
stateStack.push(rect.toJSON());
// [Action2] Change rectangle dimension and save its state
rect.set({ width: 50, height: 60 });
rect.setCoords();
canvas.trigger("object:modified", { target: rect });
stateStack.push(rect.toJSON());
// [Action3] Change the position and save its state
rect.set({ left: 20, top: 70 });
rect.setCoords();
canvas.trigger("object:modified", { target: rect });
stateStack.push(rect.toJSON());
// Undo Action3
sketch.undo();
void (function () {
const obj = canvas.getObjects()[0];
expect(obj.toJSON()).toEqual(stateStack[1]);
})();
// Undo Action2
sketch.undo();
void (function () {
const obj = canvas.getObjects()[0];
expect(obj.toJSON()).toEqual(stateStack[0]);
})();
// Undo Action1, and then redo Action1
sketch.undo();
sketch.redo();
void (function () {
const obj = canvas.getObjects()[0];
expect(obj.toJSON()).toEqual(stateStack[0]);
})();
// redo Action2
sketch.redo();
void (function () {
const obj = canvas.getObjects()[0];
expect(obj.toJSON()).toEqual(stateStack[1]);
})();
// redo Action3
sketch.redo();
void (function () {
const obj = canvas.getObjects()[0];
expect(obj.toJSON()).toEqual(stateStack[2]);
})();
});
it("Removes selected object", () => {
const sketch = mount(<SketchField tool="rectangle" />).instance();
const canvas = sketch._fc;
expect(canvas).toBeDefined();
const startPt = { x: 10, y: 10 };
const endPt = { x: 40, y: 50 };
canvas.renderOnAddRemove = false;
objectFromDrag(canvas, startPt, endPt, "rect1");
objectFromDrag(canvas, startPt, endPt, "rect2");
canvas.setActiveObject(canvas.getObjects()[0]);
sketch.removeSelected();
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect2"]);
sketch.undo();
expect(canvas.getObjects().map((o) => o.id)).toEqual(["rect2", "rect1"]);
});
it("Copy/Paste selected object", () => {
const sketch = mount(<SketchField tool="rectangle" />).instance();
const canvas = sketch._fc;
expect(canvas).toBeDefined();
const startPt = { x: 10, y: 10 };
const endPt = { x: 40, y: 50 };
canvas.renderOnAddRemove = false;
objectFromDrag(canvas, startPt, endPt, "rect1");
objectFromDrag(canvas, startPt, endPt, "rect2");
canvas.setActiveObject(canvas.getObjects()[0]);
expect(canvas.getObjects().length).toEqual(2);
sketch.copy();
sketch.paste();
expect(canvas.getObjects().length).toEqual(3);
});
});

View File

@ -0,0 +1,4 @@
import SketchField from "./SketchField";
export { SketchField };
export default SketchField;

View File

@ -0,0 +1,90 @@
/* eslint no-console: 0 */
/* eslint-env node, mocha */
import History from "./index";
describe("History", () => {
it("Loads Normally", () => {
require("./index");
});
it("Undo limit is set", () => {
const instance = new History();
const instanceWithCustomUndoSteps = new History(15);
expect(instance.getUndoLimit()).toEqual(10);
expect(instanceWithCustomUndoSteps.getUndoLimit()).toEqual(15);
});
it("Informs if can undo", () => {
const instance = new History();
expect(instance.canUndo()).toBeFalsy();
instance.keep("1");
expect(instance.canUndo()).toBeTruthy();
instance.keep("2");
expect(instance.canUndo()).toBeTruthy();
});
it("Can undo/redo object", () => {
const instance = new History(15, true);
instance.keep("1");
instance.keep("2");
expect(instance.canUndo()).toBeTruthy();
expect(instance.getCurrent()).toEqual("2");
expect(instance.undo()).toEqual("1");
expect(instance.undo()).toBeNull();
expect(instance.getCurrent()).toBeNull();
expect(instance.redo()).toEqual("1");
expect(instance.getCurrent()).toEqual("1");
});
it("Multiple undo/redo of objects", () => {
const instance = new History();
instance.keep("1");
instance.keep("2");
instance.keep("3");
instance.keep("4");
instance.keep("5");
expect(instance.canUndo()).toBeTruthy();
expect(instance.undo()).toEqual("4");
expect(instance.undo()).toEqual("3");
expect(instance.undo()).toEqual("2");
expect(instance.undo()).toEqual("1");
expect(instance.undo()).toBeNull();
expect(instance.redo()).toEqual("1");
expect(instance.redo()).toEqual("2");
expect(instance.redo()).toEqual("3");
expect(instance.redo()).toEqual("4");
expect(instance.redo()).toEqual("5");
expect(instance.redo()).toBeNull();
});
it("Redo is reset after a keep of a new object", () => {
const instance = new History();
instance.keep("1");
instance.keep("2");
instance.keep("3");
expect(instance.canUndo()).toBeTruthy();
expect(instance.canRedo()).toBeFalsy();
expect(instance.undo()).toEqual("2");
expect(instance.canRedo()).toBeTruthy();
instance.keep("4");
expect(instance.canRedo()).toBeFalsy();
expect(instance.redo()).toBeNull();
});
it("Can clear history", () => {
const instance = new History();
instance.keep("1");
instance.keep("2");
instance.keep("3");
expect(instance.undo()).toEqual("2");
expect(instance.redo()).toEqual("3");
instance.clear();
expect(instance.canUndo()).toBeFalsy();
expect(instance.canRedo()).toBeFalsy();
expect(instance.undo()).toBeNull();
expect(instance.redo()).toBeNull();
instance.undo();
instance.redo();
});
});

View File

@ -0,0 +1,135 @@
/**
* Maintains the history of an object
*/
class History {
constructor(undoLimit = 10, debug = false) {
this.undoLimit = undoLimit;
this.undoList = [];
this.redoList = [];
this.current = null;
this.debug = debug;
}
/**
* Get the limit of undo/redo actions
*
* @returns {number|*} the undo limit, as it is configured when constructing the history instance
*/
getUndoLimit() {
return this.undoLimit;
}
/**
* Get Current state
*
* @returns {null|*}
*/
getCurrent() {
return this.current;
}
/**
* Keep an object to history
*
* This method will set the object as current value and will push the previous "current" object to the undo history
*
* @param obj
*/
keep(obj) {
try {
this.redoList = [];
if (this.current) {
this.undoList.push(this.current);
}
if (this.undoList.length > this.undoLimit) {
this.undoList.shift();
}
this.current = obj;
} finally {
this.print();
}
}
/**
* Undo the last object, this operation will set the current object to one step back in time
*
* @returns the new current value after the undo operation, else null if no undo operation was possible
*/
undo() {
try {
if (this.current) {
this.redoList.push(this.current);
if (this.redoList.length > this.undoLimit) {
this.redoList.shift();
}
if (this.undoList.length === 0) this.current = null;
}
if (this.undoList.length > 0) {
this.current = this.undoList.pop();
return this.current;
}
return null;
} finally {
this.print();
}
}
/**
* Redo the last object, redo happens only if no keep operations have been performed
*
* @returns the new current value after the redo operation, or null if no redo operation was possible
*/
redo() {
try {
if (this.redoList.length > 0) {
if (this.current) this.undoList.push(this.current);
this.current = this.redoList.pop();
return this.current;
}
return null;
} finally {
this.print();
}
}
/**
* Checks whether we can perform a redo operation
*
* @returns {boolean}
*/
canRedo() {
return this.redoList.length > 0;
}
/**
* Checks whether we can perform an undo operation
*
* @returns {boolean}
*/
canUndo() {
return this.undoList.length > 0 || this.current !== null;
}
/**
* Clears the history maintained, can be undone
*/
clear() {
this.undoList = [];
this.redoList = [];
this.current = null;
this.print();
}
print() {
if (this.debug) {
/* eslint-disable no-console */
console.log(
this.undoList,
" -> " + this.current + " <- ",
this.redoList.slice(0).reverse()
);
}
}
}
export default History;

View File

@ -0,0 +1,11 @@
import "./polyfill";
import SketchField from "./components/SketchField";
import Tools from "./tools";
export { SketchField };
export { Tools };
export default {
SketchField,
Tools,
};

View File

@ -0,0 +1,30 @@
/**
* Object.assign() polyfill for IE11
* @see <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign>
*/
if (typeof Object.assign != "function") {
Object.defineProperty(Object, "assign", {
// eslint-disable-next-line no-unused-vars
value: function assign(target, varArgs) {
"use strict";
if (target == null) {
throw new TypeError("Cannot convert undefined or null to object");
}
let to = Object(target);
for (let index = 1; index < arguments.length; index++) {
let nextSource = arguments[index];
if (nextSource != null) {
for (let nextKey in nextSource) {
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true,
});
}

View File

@ -0,0 +1,12 @@
export default {
Circle: "circle",
Line: "line",
Arrow: "arrow",
Pencil: "pencil",
Rectangle: "rectangle",
RectangleLabel: "rectangle-label",
Select: "select",
Pan: "pan",
Highlighter: "highlighter",
DefaultTool: "default-tool",
};

View File

@ -0,0 +1,82 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
const fabric = require("fabric").fabric;
class Index extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
this._width = props.lineWidth;
this._color = props.lineColor;
}
doMouseDown(o) {
this.isDown = true;
let canvas = this._canvas;
var pointer = canvas.getPointer(o.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
this.line = new fabric.Line(points, {
strokeWidth: this._width,
fill: this._color,
stroke: this._color,
originX: "center",
originY: "center",
selectable: false,
evented: false,
});
this.head = new fabric.Triangle({
fill: this._color,
left: pointer.x,
top: pointer.y,
originX: "center",
originY: "center",
height: 3 * this._width,
width: 3 * this._width,
selectable: false,
evented: false,
angle: 90,
});
canvas.add(this.line);
canvas.add(this.head);
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
var pointer = canvas.getPointer(o.e);
this.line.set({ x2: pointer.x, y2: pointer.y });
this.line.setCoords();
let x_delta = pointer.x - this.line.x1;
let y_delta = pointer.y - this.line.y1;
this.head.set({
left: pointer.x,
top: pointer.y,
angle: 90 + (Math.atan2(y_delta, x_delta) * 180) / Math.PI,
});
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
let canvas = this._canvas;
canvas.remove(this.line);
canvas.remove(this.head);
let arrow = new fabric.Group([this.line, this.head]);
canvas.add(arrow);
}
doMouseOut(o) {
this.isDown = false;
}
}
export default Index;

View File

@ -0,0 +1,61 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
import { linearDistance } from "../../utils";
const fabric = require("fabric").fabric;
class Circle extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
this._width = props.lineWidth;
this._color = props.lineColor;
this._fill = props.fillColor;
}
doMouseDown(o) {
let canvas = this._canvas;
this.isDown = true;
let pointer = canvas.getPointer(o.e);
[this.startX, this.startY] = [pointer.x, pointer.y];
this.circle = new fabric.Circle({
left: this.startX,
top: this.startY,
originX: "left",
originY: "center",
strokeWidth: this._width,
stroke: this._color,
fill: this._fill,
selectable: false,
evented: false,
radius: 1,
});
canvas.add(this.circle);
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
let pointer = canvas.getPointer(o.e);
this.circle.set({
radius:
linearDistance(
{ x: this.startX, y: this.startY },
{ x: pointer.x, y: pointer.y }
) / 2,
angle:
(Math.atan2(pointer.y - this.startY, pointer.x - this.startX) * 180) /
Math.PI,
});
this.circle.setCoords();
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
}
}
export default Circle;

View File

@ -0,0 +1,15 @@
import FabricCanvasTool from "../fabrictool";
import { hexToRgbA, colorNameToHex } from "../../utils";
class Highlighter extends FabricCanvasTool {
configureCanvas(props) {
this._canvas.isDrawingMode = true;
this._canvas.freeDrawingBrush.width = props.lineWidth;
this._canvas.freeDrawingBrush.color =
props.lineColor.indexOf("#") > -1
? hexToRgbA(props.lineColor)
: hexToRgbA(colorNameToHex(props.lineColor));
}
}
export default Highlighter;

View File

@ -0,0 +1,51 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
const fabric = require("fabric").fabric;
class Line extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
this._width = props.lineWidth;
this._color = props.lineColor;
}
doMouseDown(o) {
this.isDown = true;
let canvas = this._canvas;
var pointer = canvas.getPointer(o.e);
var points = [pointer.x, pointer.y, pointer.x, pointer.y];
this.line = new fabric.Line(points, {
strokeWidth: this._width,
fill: this._color,
stroke: this._color,
originX: "center",
originY: "center",
selectable: false,
evented: false,
});
canvas.add(this.line);
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
var pointer = canvas.getPointer(o.e);
this.line.set({ x2: pointer.x, y2: pointer.y });
this.line.setCoords();
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
}
doMouseOut(o) {
this.isDown = false;
}
}
export default Line;

View File

@ -0,0 +1,41 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
const fabric = require("fabric").fabric;
class Pan extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
//Change the cursor to the move grabber
canvas.defaultCursor = "move";
}
doMouseDown(o) {
let canvas = this._canvas;
this.isDown = true;
let pointer = canvas.getPointer(o.e);
this.startX = pointer.x;
this.startY = pointer.y;
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
let pointer = canvas.getPointer(o.e);
canvas.relativePan({
x: pointer.x - this.startX,
y: pointer.y - this.startY,
});
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
}
}
export default Pan;

View File

@ -0,0 +1,11 @@
import FabricCanvasTool from "../fabrictool";
class Pencil extends FabricCanvasTool {
configureCanvas(props) {
this._canvas.isDrawingMode = true;
this._canvas.freeDrawingBrush.width = props.lineWidth;
this._canvas.freeDrawingBrush.color = props.lineColor;
}
}
export default Pencil;

View File

@ -0,0 +1,64 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
const fabric = require("fabric").fabric;
class Rectangle extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
this._width = props.lineWidth;
this._color = props.lineColor;
this._fill = props.fillColor;
}
doMouseDown(o) {
let canvas = this._canvas;
this.isDown = true;
let pointer = canvas.getPointer(o.e);
this.startX = pointer.x;
this.startY = pointer.y;
this.rect = new fabric.Rect({
left: this.startX,
top: this.startY,
originX: "left",
originY: "top",
width: pointer.x - this.startX,
height: pointer.y - this.startY,
stroke: this._color,
strokeWidth: this._width,
fill: this._fill,
transparentCorners: false,
selectable: false,
evented: false,
strokeUniform: true,
noScaleCache: false,
angle: 0,
});
canvas.add(this.rect);
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
let pointer = canvas.getPointer(o.e);
if (this.startX > pointer.x) {
this.rect.set({ left: Math.abs(pointer.x) });
}
if (this.startY > pointer.y) {
this.rect.set({ top: Math.abs(pointer.y) });
}
this.rect.set({ width: Math.abs(this.startX - pointer.x) });
this.rect.set({ height: Math.abs(this.startY - pointer.y) });
this.rect.setCoords();
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
}
}
export default Rectangle;

View File

@ -0,0 +1,35 @@
/* eslint no-unused-vars: 0 */
const fabric = require("fabric").fabric;
class RectangleLabelObject {
constructor(canvas, text, rectProps, textProps) {
this._canvas = canvas;
this._text = text;
this._rectObj = new fabric.Rect(rectProps);
this._textObj = new fabric.Textbox(text, textProps);
canvas.on({ "object:scaling": this.update });
canvas.on({ "object:moving": this.update });
}
update = (e) => {
//e.target.set({scaleX:1, scaleY:1})
if (!this._textObj || !this._rectObj) return;
if (e.target === this._rectObj) {
this._textObj.set({
width: this._rectObj.getScaledWidth(),
scaleX: 1,
scaleY: 1,
top: this._rectObj.top - this._textObj.getScaledHeight(),
left: this._rectObj.left,
});
}
};
setText(text) {
this._text = text;
this._textObj.set({ text });
}
}
export default RectangleLabelObject;

View File

@ -0,0 +1,114 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
import RectangleLabelObject from "./rectangle-label-object";
class RectangleLabel extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
this._width = props.lineWidth;
this._color = props.lineColor;
this._fill = props.fillColor;
this._textString = props.text;
this._maxFontSize = 12;
}
doMouseDown(o) {
let canvas = this._canvas;
this.isDown = true;
let pointer = canvas.getPointer(o.e);
this.startX = pointer.x;
this.startY = pointer.y;
this.rectangleLabel = new RectangleLabelObject(
canvas,
"New drawing",
{
left: this.startX,
top: this.startY,
originX: "left",
originY: "top",
width: pointer.x - this.startX,
height: pointer.y - this.startY,
stroke: this._color,
strokeWidth: this._width,
fill: this._fill,
transparentCorners: false,
selectable: false,
evented: false,
strokeUniform: true,
noScaleCache: false,
angle: 0,
},
{
left: this.startX,
top: this.startY - 12,
originX: "left",
originY: "top",
width: pointer.x - this.startX - this._width,
height: canvas.height / 3,
fontSize: this._maxFontSize,
noScaleCache: false,
backgroundColor: this._color,
transparentCorners: true,
hasControls: false,
angle: 0,
}
);
if (this._objects && this._objects.length > 0)
this._objects.push(this.rectangleLabel);
else this._objects = [this.rectangleLabel];
while (this.rectangleLabel._textObj.height > canvas.height / 3) {
this.rectangleLabel._textObj.set({
fontSize: this.rectangleLabel._textObj.fontSize - 1,
top: this.startY - this.rectangleLabel._textObj.fontSize - 12,
});
}
canvas.add(this.rectangleLabel._rectObj);
canvas.add(this.rectangleLabel._textObj);
canvas.renderAll();
}
doMouseMove(o) {
if (!this.isDown) return;
let canvas = this._canvas;
let pointer = canvas.getPointer(o.e);
if (this.startX > pointer.x) {
this.rectangleLabel._rectObj.set({ left: Math.abs(pointer.x) });
this.rectangleLabel._textObj.set({ left: Math.abs(pointer.x) });
}
if (this.startY > pointer.y) {
this.rectangleLabel._rectObj.set({ left: Math.abs(pointer.x) });
this.rectangleLabel._textObj.set({ top: Math.abs(pointer.y) });
}
this.rectangleLabel._textObj.setCoords();
this.rectangleLabel._rectObj.set({
width: Math.abs(this.startX - pointer.x),
});
this.rectangleLabel._textObj.set({
width: this.rectangleLabel._rectObj.getScaledWidth(),
});
this.rectangleLabel._rectObj.set({
height: Math.abs(this.startY - pointer.y),
});
this.rectangleLabel._rectObj.setCoords();
canvas.renderAll();
}
doMouseUp(o) {
this.isDown = false;
let canvas = this._canvas;
// var group = new fabric.Group([this.rectangleLabel._rectObj,this.rectangleLabel._textObj]);
// canvas.remove(this.rectangleLabel._rectObj);
// canvas.remove(this.rectangleLabel._textObj);
// canvas.add(group);
canvas.renderAll();
}
}
export default RectangleLabel;

View File

@ -0,0 +1,16 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "../fabrictool";
class Select extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = false;
canvas.selection = true;
canvas.forEachObject((o) => {
o.selectable = o.evented = true;
});
}
}
export default Select;

View File

@ -0,0 +1,18 @@
/*eslint no-unused-vars: 0*/
import FabricCanvasTool from "./fabrictool";
const fabric = require("fabric").fabric;
class DefaultTool extends FabricCanvasTool {
configureCanvas(props) {
let canvas = this._canvas;
canvas.isDrawingMode = canvas.selection = false;
canvas.forEachObject((o) => (o.selectable = o.evented = false));
canvas.discardActiveObject();
canvas.defaultCursor = "pointer";
canvas.renderAll();
}
}
export default DefaultTool;

View File

@ -0,0 +1,22 @@
/* eslint no-unused-vars: 0 */
/**
* "Abstract" like base class for a Canvas tool
*/
class FabricCanvasTool {
constructor(canvas) {
this._canvas = canvas;
}
configureCanvas(props) {}
doMouseUp(event) {}
doMouseDown(event) {}
doMouseMove(event) {}
doMouseOut(event) {}
}
export default FabricCanvasTool;

View File

@ -0,0 +1,23 @@
import Arrow from "./Arrow";
import Circle from "./Circle";
import Highlighter from "./Highlighter";
import Line from "./Line";
import Pan from "./Pan";
import Pencil from "./Pencil";
import Rectangle from "./Rectangle";
import RectangleLabel from "./Rectangle/rectangle-label";
import Select from "./Select/index";
import DefaultTool from "./defaul-tool";
export default {
Arrow,
Circle,
Highlighter,
Line,
Pan,
Pencil,
Rectangle,
RectangleLabel,
Select,
DefaultTool,
};

View File

@ -0,0 +1,15 @@
/* global expect,describe,it */
/* eslint no-console: 0 */
/* eslint-env node, mocha */
import { uuid4 } from "../index";
describe("Utils", () => {
it("Loads Normally", () => {
require("../index");
});
it("Generates random uuid", () => {
expect(uuid4()).toBeDefined();
});
});

View File

@ -0,0 +1,242 @@
/**
* Determine the mouse position
*
* @param event the canvas event
* @returns *[] tuple of position x,y
* @private
*/
export const pointerPosition = (event) => {
event = event || window.event;
var target = event.target || event.srcElement,
style = target.currentStyle || window.getComputedStyle(target, null),
borderLeftWidth = parseInt(style["borderLeftWidth"], 10),
borderTopWidth = parseInt(style["borderTopWidth"], 10),
rect = target.getBoundingClientRect(),
_x = event.clientX - borderLeftWidth - rect.left,
_y = event.clientY - borderTopWidth - rect.top,
_touchX = event.changedTouches
? event.changedTouches[0].clientX - borderLeftWidth - rect.left
: null,
_touchY = event.changedTouches
? event.changedTouches[0].clientY - borderTopWidth - rect.top
: null;
return [_x || _touchX, _y || _touchY];
};
/**
* Calculate the distance of two x,y points
*
* @param point1 an object with x,y attributes representing the start point
* @param point2 an object with x,y attributes representing the end point
*
* @returns {number}
*/
export const linearDistance = (point1, point2) => {
let xs = point2.x - point1.x;
let ys = point2.y - point1.y;
return Math.sqrt(xs * xs + ys * ys);
};
/**
* Return a random uuid of the form xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
* @returns {string}
*/
export const uuid4 = () => {
let uuid = "",
ii;
for (ii = 0; ii < 32; ii += 1) {
switch (ii) {
case 8:
case 20:
uuid += "-";
uuid += ((Math.random() * 16) | 0).toString(16);
break;
case 12:
uuid += "-";
uuid += "4";
break;
case 16:
uuid += "-";
uuid += ((Math.random() * 4) | 8).toString(16);
break;
default:
uuid += ((Math.random() * 16) | 0).toString(16);
}
}
return uuid;
};
/**
*
* @param hex is a color code in the form of hex.
* Return a rgba equivalent of given hex.
* @returns {string}
*/
export const hexToRgbA = (hex) => {
var c;
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
c = hex.substring(1).split("");
if (c.length == 3) {
c = [c[0], c[0], c[1], c[1], c[2], c[2]];
}
c = "0x" + c.join("");
return "rgba(" + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(",") + ",0.4)";
}
throw new Error("Bad Hex");
};
/**
*
* @param color is a color name to convert it to hex
* Return hex equivalent of given color name
*/
export const colorNameToHex = (color) => {
var colors = {
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aqua: "#00ffff",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
black: "#000000",
blanchedalmond: "#ffebcd",
blue: "#0000ff",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
fuchsia: "#ff00ff",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
gray: "#808080",
green: "#008000",
greenyellow: "#adff2f",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
"indianred ": "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgrey: "#d3d3d3",
lightgreen: "#90ee90",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
lime: "#00ff00",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
maroon: "#800000",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370d8",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
navy: "#000080",
oldlace: "#fdf5e6",
olive: "#808000",
olivedrab: "#6b8e23",
orange: "#ffa500",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#d87093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
purple: "#800080",
rebeccapurple: "#663399",
red: "#ff0000",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
silver: "#c0c0c0",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
teal: "#008080",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
white: "#ffffff",
whitesmoke: "#f5f5f5",
yellow: "#ffff00",
yellowgreen: "#9acd32",
};
if (typeof colors[color.toLowerCase()] != "undefined")
return colors[color.toLowerCase()];
return false;
};

View File

@ -9,13 +9,13 @@ gradio/external.py
gradio/inputs.py
gradio/interface.py
gradio/interpretation.py
gradio/mix.py
gradio/networking.py
gradio/notebook.py
gradio/outputs.py
gradio/processing_utils.py
gradio/strings.py
gradio/test_data.py
gradio/transforms.py
gradio/tunneling.py
gradio/utils.py
gradio.egg-info/PKG-INFO
@ -27,136 +27,26 @@ gradio/frontend/asset-manifest.json
gradio/frontend/index.html
gradio/frontend/static/css/2.c6f1eff9.chunk.css
gradio/frontend/static/css/2.c6f1eff9.chunk.css.map
gradio/frontend/static/css/3.40297d85.chunk.css
gradio/frontend/static/css/3.40297d85.chunk.css.map
gradio/frontend/static/css/4.e3609929.chunk.css
gradio/frontend/static/css/4.e3609929.chunk.css.map
gradio/frontend/static/js/2.62a34f98.chunk.js
gradio/frontend/static/js/2.62a34f98.chunk.js.LICENSE.txt
gradio/frontend/static/js/2.62a34f98.chunk.js.map
gradio/frontend/static/js/3.c9f63f14.chunk.js
gradio/frontend/static/js/3.c9f63f14.chunk.js.map
gradio/frontend/static/js/4.9ae33d51.chunk.js
gradio/frontend/static/js/4.9ae33d51.chunk.js.map
gradio/frontend/static/js/main.aca3848b.chunk.js
gradio/frontend/static/js/main.aca3848b.chunk.js.map
gradio/frontend/static/js/runtime-main.88972b1f.js
gradio/frontend/static/js/runtime-main.88972b1f.js.map
gradio/frontend/static/css/3.9a5b5cea.chunk.css
gradio/frontend/static/css/3.9a5b5cea.chunk.css.map
gradio/frontend/static/css/4.fae94bce.chunk.css
gradio/frontend/static/css/4.fae94bce.chunk.css.map
gradio/frontend/static/js/2.0081c136.chunk.js
gradio/frontend/static/js/2.0081c136.chunk.js.LICENSE.txt
gradio/frontend/static/js/2.0081c136.chunk.js.map
gradio/frontend/static/js/3.66c6a8f4.chunk.js
gradio/frontend/static/js/3.66c6a8f4.chunk.js.map
gradio/frontend/static/js/4.0a51054c.chunk.js
gradio/frontend/static/js/4.0a51054c.chunk.js.map
gradio/frontend/static/js/main.535df397.chunk.js
gradio/frontend/static/js/main.535df397.chunk.js.map
gradio/frontend/static/js/runtime-main.86797e02.js
gradio/frontend/static/js/runtime-main.86797e02.js.map
gradio/frontend/static/media/logo_loading.e93acd82.gif
gradio/static/css/gradio.css
gradio/static/css/loading.css
gradio/static/css/style.css
gradio/static/css/interfaces/input/checkbox_group.css
gradio/static/css/interfaces/input/dropdown.css
gradio/static/css/interfaces/input/file.css
gradio/static/css/interfaces/input/image.css
gradio/static/css/interfaces/input/microphone.css
gradio/static/css/interfaces/input/radio.css
gradio/static/css/interfaces/input/sketchpad.css
gradio/static/css/interfaces/input/slider.css
gradio/static/css/interfaces/input/textbox.css
gradio/static/css/interfaces/input/webcam.css
gradio/static/css/interfaces/output/audio.css
gradio/static/css/interfaces/output/highlighted_text.css
gradio/static/css/interfaces/output/html.css
gradio/static/css/interfaces/output/image.css
gradio/static/css/interfaces/output/json.css
gradio/static/css/interfaces/output/key_values.css
gradio/static/css/interfaces/output/label.css
gradio/static/css/interfaces/output/textbox.css
gradio/static/css/vendor/cropper.min.css
gradio/static/css/vendor/icons.svg
gradio/static/css/vendor/jexcel.min.css
gradio/static/css/vendor/jquery-ui.css
gradio/static/css/vendor/jsonTree.css
gradio/static/css/vendor/jsuites.min.css
gradio/static/css/vendor/tui-color-picker.css
gradio/static/css/vendor/tui-image-editor.css
gradio/static/css/vendor/images/ui-bg_flat_0_aaaaaa_40x100.png
gradio/static/css/vendor/images/ui-icons_444444_256x240.png
gradio/static/css/vendor/images/ui-icons_555555_256x240.png
gradio/static/css/vendor/images/ui-icons_777620_256x240.png
gradio/static/css/vendor/images/ui-icons_777777_256x240.png
gradio/static/css/vendor/images/ui-icons_cc0000_256x240.png
gradio/static/css/vendor/images/ui-icons_ffffff_256x240.png
gradio/static/img/logo.png
gradio/static/img/logo_error.png
gradio/static/img/logo_inline.png
gradio/static/img/logo_loading.gif
gradio/static/img/logo_mini.png
gradio/static/img/logo_only.png
gradio/static/img/mic.png
gradio/static/img/mic_recording.png
gradio/static/img/table.png
gradio/static/img/webcam.png
gradio/static/img/vendor/icon-a.svg
gradio/static/img/vendor/icon-b.svg
gradio/static/img/vendor/icon-c.svg
gradio/static/img/vendor/icon-d.svg
gradio/static/js/all_io.js
gradio/static/js/gradio.js
gradio/static/js/utils.js
gradio/static/js/interfaces/input/audio.js
gradio/static/js/interfaces/input/checkbox.js
gradio/static/js/interfaces/input/checkbox_group.js
gradio/static/js/interfaces/input/dataframe.js
gradio/static/js/interfaces/input/dropdown.js
gradio/static/js/interfaces/input/file.js
gradio/static/js/interfaces/input/image.js
gradio/static/js/interfaces/input/microphone.js
gradio/static/js/interfaces/input/number.js
gradio/static/js/interfaces/input/radio.js
gradio/static/js/interfaces/input/sketchpad.js
gradio/static/js/interfaces/input/slider.js
gradio/static/js/interfaces/input/textbox.js
gradio/static/js/interfaces/input/video.js
gradio/static/js/interfaces/input/webcam.js
gradio/static/js/interfaces/output/audio.js
gradio/static/js/interfaces/output/dataframe.js
gradio/static/js/interfaces/output/file.js
gradio/static/js/interfaces/output/highlighted_text.js
gradio/static/js/interfaces/output/html.js
gradio/static/js/interfaces/output/image.js
gradio/static/js/interfaces/output/json.js
gradio/static/js/interfaces/output/key_values.js
gradio/static/js/interfaces/output/label.js
gradio/static/js/interfaces/output/textbox.js
gradio/static/js/interfaces/output/video.js
gradio/static/js/vendor/Chart.min.js
gradio/static/js/vendor/FileSaver.min.js
gradio/static/js/vendor/black-theme.js
gradio/static/js/vendor/cropper.min.js
gradio/static/js/vendor/fabric.js
gradio/static/js/vendor/html2canvas.min.js
gradio/static/js/vendor/jexcel.min.js
gradio/static/js/vendor/jquery-ui.min.js
gradio/static/js/vendor/jquery.min.js
gradio/static/js/vendor/jquery.ui.touch-punch.js
gradio/static/js/vendor/jsonTree.js
gradio/static/js/vendor/jsuites.min.js
gradio/static/js/vendor/p5.dom.min.js
gradio/static/js/vendor/p5.min.js
gradio/static/js/vendor/p5.sound.min.js
gradio/static/js/vendor/papaparse.min.js
gradio/static/js/vendor/sketchpad.js
gradio/static/js/vendor/tui-code-snippet.min.js
gradio/static/js/vendor/tui-color-picker.js
gradio/static/js/vendor/tui-image-editor.js
gradio/static/js/vendor/wavesurfer.min.js
gradio/static/js/vendor/webcam.min.js
gradio/static/js/vendor/white-theme.js
gradio/static/js/vendor/gifcap/Makefile
gradio/static/js/vendor/gifcap/configure
gradio/static/js/vendor/gifcap/encoder.c
gradio/static/js/vendor/gifcap/encoder.js
gradio/static/js/vendor/gifcap/encoder.wasm
gradio/static/js/vendor/gifcap/gifencoder.js
gradio/static/js/vendor/gifcap/quantizer.js
gradio/static/js/vendor/gifcap/writer.js
gradio/templates/index.html
gradio/templates/login.html
test/test_demos.py
test/test_inputs.py
test/test_interfaces.py
test/test_interpretation.py
test/test_mix.py
test/test_outputs.py

View File

@ -158,8 +158,8 @@ def get_gradio_interface(model_name, api_key, alias):
query_gradio_api.__name__ = alias
pipeline = {
'inputs': [inp[0] for inp in config_info["input_interfaces"]],
'outputs': [out[0] for out in config_info["output_interfaces"]],
'inputs': [inp[0] for inp in config_info["input_components"]],
'outputs': [out[0] for out in config_info["output_components"]],
'preprocess': lambda x: {"data": [x]},
'postprocess': lambda r: r["data"][0],
}

View File

@ -130,10 +130,10 @@ class Interface:
if isinstance(outputs, list):
self.output_components = [get_output_instance(i) for i in outputs]
else:
self.output_interfaces = [get_output_instance(outputs)]
self.output_components = [get_output_instance(outputs)]
if repeat_outputs_per_model:
self.output_interfaces *= len(fn)
self.output_components *= len(fn)
self.predict = fn
self.function_names = [func.__name__ for func in fn]
self.__name__ = ", ".join(self.function_names)
@ -226,10 +226,10 @@ class Interface:
repr = "Gradio Interface for: {}".format(", ".join(fn.__name__ for fn in self.predict))
repr += "\n" + "-"*len(repr)
repr += "\ninputs:"
for component in self.input_interfaces:
for component in self.input_components:
repr += "\n|-{}".format(str(component))
repr += "\noutputs:"
for component in self.output_interfaces:
for component in self.output_components:
repr+= "\n|-{}".format(str(component))
return repr

View File

@ -18,11 +18,11 @@ class Parallel(Interface):
for io in interfaces:
fns.extend(io.predict)
outputs.extend(io.output_interfaces)
outputs.extend(io.output_components)
kwargs = {
"fn": fns,
"inputs": interfaces[0].input_interfaces,
"inputs": interfaces[0].input_components,
"outputs": outputs,
"repeat_outputs_per_model": False,
}
@ -47,7 +47,7 @@ class Series(Interface):
for idx, io in enumerate(interfaces):
# skip preprocessing for first interface since the compound interface will include it
if idx > 0:
data = [input_interface.preprocess(data[i]) for i, input_interface in enumerate(io.input_interfaces)]
data = [input_interface.preprocess(data[i]) for i, input_interface in enumerate(io.input_components)]
# run all of predictions sequentially
predictions = []
for predict_fn in io.predict:
@ -56,15 +56,15 @@ class Series(Interface):
data = predictions
# skip postprocessing for final interface since the compound interface will include it
if idx < len(interfaces) - 1:
data = [output_interface.postprocess(data[i]) for i, output_interface in enumerate(io.output_interfaces)]
data = [output_interface.postprocess(data[i]) for i, output_interface in enumerate(io.output_components)]
return data[0]
connected_fn.__name__ = " => ".join([f[0].__name__ for f in fns])
kwargs = {
"fn": connected_fn,
"inputs": interfaces[0].input_interfaces,
"outputs": interfaces[-1].output_interfaces,
"inputs": interfaces[0].input_components,
"outputs": interfaces[-1].output_components,
}
kwargs.update(options)
super().__init__(**kwargs)

View File

@ -121,24 +121,10 @@ def get_first_available_port(initial, final):
)
def home_page(examples=None, path=None):
return render_template("index.html",
config=app.interface.config,
vendor_prefix=(
GRADIO_STATIC_ROOT if app.interface.share else ""),
input_interfaces=[interface[0]
for interface in app.interface.config["input_interfaces"]],
output_interfaces=[interface[0]
for interface in app.interface.config["output_interfaces"]],
css=app.interface.css, examples=examples, path=path
)
@app.route("/", methods=["GET"])
@login_check
def main():
return home_page()
return render_template("index.html")
@app.route('/login', methods=["GET", "POST"])
@ -155,50 +141,6 @@ def login():
return abort(401)
@app.route("/from_dir", methods=["GET"])
@login_check
def main_from_flagging_dir():
return redirect("/from_dir/" + app.interface.flagging_dir)
@app.route("/from_dir/<path:path>", methods=["GET"])
@login_check
def main_from_dir(path):
log_file = os.path.join(path, "log.csv")
path_exists = os.path.exists(path)
log_file_exists = os.path.exists(log_file)
examples_from_folder = isinstance(app.interface.examples, str) and app.interface.examples == path
multiple_inputs = len(app.interface.input_interfaces) > 1
if path_exists:
if not log_file_exists and multiple_inputs:
if examples_from_folder:
abort(404, "log.csv file required for multiple inputs.")
else:
return redirect("/")
elif examples_from_folder:
abort(404, "Examples dir not found")
else:
return redirect("/")
if log_file_exists:
if app.interface.encrypt:
with open(log_file, "rb") as csvfile:
encrypted_csv = csvfile.read()
decrypted_csv = encryptor.decrypt(
app.interface.encryption_key, encrypted_csv)
csv_data = io.StringIO(decrypted_csv.decode())
examples = list(csv.reader(csv_data))
else:
with open(log_file) as logs:
examples = list(csv.reader(logs))
examples = examples[1:] # remove header
else:
examples = [[filename] for filename in os.listdir(path)]
for i, example in enumerate(examples):
for j, (interface, cell) in enumerate(zip(app.interface.input_components + app.interface.output_components, example)):
examples[i][j] = interface.restore_flagged(cell)
return home_page(examples=examples, path=path)
@app.route("/config/", methods=["GET"])
@login_check
def config():
@ -245,13 +187,13 @@ def score_similarity():
raw_input = request.json["data"]
preprocessed_input = [input_interface.preprocess(raw_input[i])
for i, input_interface in enumerate(app.interface.input_interfaces)]
for i, input_interface in enumerate(app.interface.input_components)]
input_embedding = app.interface.embed(preprocessed_input)
scores = list()
for example in app.interface.examples:
preprocessed_example = [iface.preprocess(iface.preprocess_example(example))
for iface, example in zip(app.interface.input_interfaces, example)]
for iface, example in zip(app.interface.input_components, example)]
example_embedding = app.interface.embed(preprocessed_example)
scores.append(calculate_similarity(input_embedding, example_embedding))
log_feature_analytics('score_similarity')
@ -265,13 +207,13 @@ def view_embeddings():
if "data" in request.json:
raw_input = request.json["data"]
preprocessed_input = [input_interface.preprocess(raw_input[i])
for i, input_interface in enumerate(app.interface.input_interfaces)]
for i, input_interface in enumerate(app.interface.input_components)]
sample_embedding.append(app.interface.embed(preprocessed_input))
example_embeddings = []
for example in app.interface.examples:
preprocessed_example = [iface.preprocess(iface.preprocess_example(example))
for iface, example in zip(app.interface.input_interfaces, example)]
for iface, example in zip(app.interface.input_components, example)]
example_embedding = app.interface.embed(preprocessed_example)
example_embeddings.append(example_embedding)
@ -291,7 +233,7 @@ def update_embeddings():
if "data" in request.json:
raw_input = request.json["data"]
preprocessed_input = [input_interface.preprocess(raw_input[i])
for i, input_interface in enumerate(app.interface.input_interfaces)]
for i, input_interface in enumerate(app.interface.input_components)]
sample_embedding.append(app.interface.embed(preprocessed_input))
sample_embedding_2d = transform_with_pca(
app.pca_model, sample_embedding)
@ -307,7 +249,7 @@ def predict_examples():
for example_id in example_ids:
example_set = app.interface.examples[example_id]
processed_example_set = [iface.preprocess_example(example)
for iface, example in zip(app.interface.input_interfaces, example_set)]
for iface, example in zip(app.interface.input_components, example_set)]
try:
predictions, _ = app.interface.process(processed_example_set)
except:
@ -321,19 +263,19 @@ def flag_data(input_data, output_data, flag_option=None):
flag_path = os.path.join(app.cwd, app.interface.flagging_dir)
csv_data = []
encryption_key = app.interface.encryption_key if app.interface.encrypt else None
for i, interface in enumerate(app.interface.input_interfaces):
for i, interface in enumerate(app.interface.input_components):
csv_data.append(interface.save_flagged(
flag_path, app.interface.config["input_interfaces"][i]["label"], input_data[i], encryption_key))
for i, interface in enumerate(app.interface.output_interfaces):
flag_path, app.interface.config["input_components"][i]["label"], input_data[i], encryption_key))
for i, interface in enumerate(app.interface.output_components):
csv_data.append(interface.save_flagged(
flag_path, app.interface.config["output_interfaces"][i]["label"], output_data[i], encryption_key))
flag_path, app.interface.config["output_components"][i]["label"], output_data[i], encryption_key))
if flag_option:
csv_data.append(flag_option)
headers = [interface["label"]
for interface in app.interface.config["input_interfaces"]]
for interface in app.interface.config["input_components"]]
headers += [interface["label"]
for interface in app.interface.config["output_interfaces"]]
for interface in app.interface.config["output_components"]]
if app.interface.flagging_options is not None:
headers.append("flag")