mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-10 12:01:36 +00:00
Feature/ add text (#44)
* can add text retain stroke width while transforming is now possible reorganize how the inheritance of node classes, now text can be selected, multi-selected, translate and transform, all while working with the unredo stack organize toolbarmain and dialogues, now add text brings up a dialog, meant for accepting user text input finished plumbing the data from user text input to adding text to baord clean up make the dialog less weirdly vertical clean up, and place node in the center of canvas block adding text with no text, make textbox wider transformation of old node works with new transformation setup undo redo of transformation also works for text nodes now cleanup geometry fix almost done text editor ui text formatting from text editor is sent to engine and used on text nodes * rid of useless button
This commit is contained in:
41 files changed
+1579
-1422
No files matched your search
Generated
+54
-564
File diff suppressed because it is too large.
Load diff
@@ -18,6 +18,7 @@
|
||||
"konva": "^9.3.14",
|
||||
"onnxruntime-web": "^1.19.2",
|
||||
"react": "^18.3.1",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-drag-drop-files": "^2.3.10",
|
||||
"react-easy-crop": "^5.0.8",
|
||||
|
||||
+88
-70
@@ -3,9 +3,7 @@ import Konva from "konva";
|
||||
import { RenderEngine } from "./RenderingPrimitives/RenderEngine";
|
||||
import { ResponseType } from "./WorkerPrimitives/SharedWorkerBase";
|
||||
|
||||
import { uiAccess } from "~/signals";
|
||||
import { uiEvents } from "~/signals";
|
||||
// import { toolbarNode } from "~/signals/uiAccess/toolbarNode";
|
||||
import { uiAccess, uiEvents } from "~/signals";
|
||||
|
||||
import {
|
||||
NodesManager,
|
||||
@@ -16,9 +14,8 @@ import {
|
||||
SelectionManagerEvents,
|
||||
SelectorSquare,
|
||||
} from "./NodesManagers";
|
||||
import { ImageNode } from "./Nodes/ImageNode";
|
||||
import { VideoNode } from "./Nodes/VideoNode";
|
||||
import { MediaNode, Position, Transformation } from "./types";
|
||||
import { ImageNode, VideoNode, TextNode } from "./Nodes";
|
||||
import { MediaNode, Position, TextNodeData, Transformation } from "./types";
|
||||
|
||||
import {
|
||||
CreateCommand,
|
||||
@@ -37,11 +34,13 @@ import {
|
||||
DiffusionSharedWorkerProgressData,
|
||||
DiffusionSharedWorkerResponseData,
|
||||
} from "./SharedWorkers/Diffusion/DiffusionSharedWorker";
|
||||
import { FileUtilities } from "./FileUtilities/FileUtilities";
|
||||
|
||||
import { AppModes, VideoResolutions } from "./constants";
|
||||
import { ToolbarMainButtonNames } from "~/components/features/ToolbarMain/enum";
|
||||
|
||||
// for testing loading files from system
|
||||
// import { FileUtilities } from "./FileUtilities/FileUtilities";
|
||||
|
||||
export interface RenderingOptions {
|
||||
artstyle: string;
|
||||
positivePrompt: string;
|
||||
@@ -177,7 +176,7 @@ export class Engine {
|
||||
|
||||
const videoNode = new VideoNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
videoURL: media_url,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
@@ -208,9 +207,10 @@ export class Engine {
|
||||
if (this.appMode === AppModes.SELECT) {
|
||||
this.selectorSquare.enable({
|
||||
captureCanvasRef: this.renderEngine.captureCanvas,
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
nodesManagerRef: this.nodesManager,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
stage: this.stage,
|
||||
stageRef: this.stage,
|
||||
});
|
||||
uiAccess.toolbarMain.changeButtonState(ToolbarMainButtonNames.SELECT, {
|
||||
active: true,
|
||||
@@ -274,6 +274,9 @@ export class Engine {
|
||||
console.log("Engine got video: " + video.url);
|
||||
this.addVideo(video.url);
|
||||
});
|
||||
uiEvents.onAddTextToEngine((textdata) => {
|
||||
this.addText(textdata);
|
||||
});
|
||||
|
||||
uiEvents.onChromakeyRequest((chromakeyProps) => {
|
||||
const node = this.selectionManager
|
||||
@@ -377,10 +380,25 @@ export class Engine {
|
||||
this.addKeyboardShortcuts();
|
||||
}
|
||||
|
||||
public addText(textNodeData: TextNodeData) {
|
||||
const textNode = new TextNode({
|
||||
textNodeData: textNodeData,
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
position: {
|
||||
x: this.renderEngine.captureCanvas.x() + 20,
|
||||
y:
|
||||
this.renderEngine.captureCanvas.y() +
|
||||
this.renderEngine.captureCanvas.height() / 3,
|
||||
},
|
||||
});
|
||||
this.createNode(textNode);
|
||||
}
|
||||
|
||||
public addImage(imageFile: File) {
|
||||
const imageNode = new ImageNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
imageFile: imageFile,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
@@ -393,7 +411,7 @@ export class Engine {
|
||||
public addVideo(url: string) {
|
||||
const videoNode = new VideoNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
videoURL: url,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
@@ -417,7 +435,7 @@ export class Engine {
|
||||
});
|
||||
}
|
||||
|
||||
createNode(node: VideoNode | ImageNode) {
|
||||
createNode(node: VideoNode | ImageNode | TextNode) {
|
||||
const command = new CreateCommand({
|
||||
nodes: new Set<MediaNode>([node]),
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
@@ -488,8 +506,8 @@ export class Engine {
|
||||
}
|
||||
transformNodes(props: {
|
||||
nodes: Set<MediaNode>;
|
||||
initialTransformations: Map<MediaNode, Transformation>;
|
||||
finalTransformations: Map<MediaNode, Transformation>;
|
||||
initialTransformations: Map<MediaNode, Transformation[]>;
|
||||
finalTransformations: Map<MediaNode, Transformation[]>;
|
||||
}) {
|
||||
const command = new TransformCommand({
|
||||
...props,
|
||||
@@ -503,68 +521,68 @@ export class Engine {
|
||||
* Code for debug and Testing
|
||||
*
|
||||
********************************/
|
||||
public async populateWithDebugItems() {
|
||||
const imageFile = await FileUtilities.createImageFileFromUrl(
|
||||
"https://static.miraheze.org/pgrwiki/0/0d/Dialogue-2B-Icon.png",
|
||||
);
|
||||
const imageNode = new ImageNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
imageFile: imageFile,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
});
|
||||
// public async populateWithDebugItems() {
|
||||
// const imageFile = await FileUtilities.createImageFileFromUrl(
|
||||
// "https://static.miraheze.org/pgrwiki/0/0d/Dialogue-2B-Icon.png",
|
||||
// );
|
||||
// const imageNode = new ImageNode({
|
||||
// mediaLayerRef: this.mediaLayer,
|
||||
// canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
// canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
// imageFile: imageFile,
|
||||
// selectionManagerRef: this.selectionManager,
|
||||
// });
|
||||
|
||||
// this.renderEngine.addNodes(videoNode3);
|
||||
// // this.renderEngine.addNodes(videoNode3);
|
||||
|
||||
const videoNode4 = new VideoNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
videoURL:
|
||||
"https://storage.googleapis.com/vocodes-public/media/0/2/8/1/n/0281nc0f3kgwvxf8eprywtd01r72rfp6/video_0281nc0f3kgwvxf8eprywtd01r72rfp6.mp4",
|
||||
selectionManagerRef: this.selectionManager,
|
||||
});
|
||||
const imageNode2 = new ImageNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
imageFile: imageFile,
|
||||
selectionManagerRef: this.selectionManager,
|
||||
});
|
||||
// const videoNode4 = new VideoNode({
|
||||
// mediaLayerRef: this.mediaLayer,
|
||||
// canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
// canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
// videoURL:
|
||||
// "https://storage.googleapis.com/vocodes-public/media/0/2/8/1/n/0281nc0f3kgwvxf8eprywtd01r72rfp6/video_0281nc0f3kgwvxf8eprywtd01r72rfp6.mp4",
|
||||
// selectionManagerRef: this.selectionManager,
|
||||
// });
|
||||
// const imageNode2 = new ImageNode({
|
||||
// mediaLayerRef: this.mediaLayer,
|
||||
// canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
// canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
// imageFile: imageFile,
|
||||
// selectionManagerRef: this.selectionManager,
|
||||
// });
|
||||
|
||||
this.renderEngine.addNodes(videoNode4);
|
||||
// this.renderEngine.addNodes(videoNode4);
|
||||
|
||||
const videoNode5 = new VideoNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
videoURL:
|
||||
"https://storage.googleapis.com/vocodes-public/media/0/2/8/1/n/0281nc0f3kgwvxf8eprywtd01r72rfp6/video_0281nc0f3kgwvxf8eprywtd01r72rfp6.mp4",
|
||||
selectionManagerRef: this.selectionManager,
|
||||
});
|
||||
// const videoNode5 = new VideoNode({
|
||||
// mediaLayerRef: this.mediaLayer,
|
||||
// canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
// canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
// videoURL:
|
||||
// "https://storage.googleapis.com/vocodes-public/media/0/2/8/1/n/0281nc0f3kgwvxf8eprywtd01r72rfp6/video_0281nc0f3kgwvxf8eprywtd01r72rfp6.mp4",
|
||||
// selectionManagerRef: this.selectionManager,
|
||||
// });
|
||||
|
||||
this.renderEngine.addNodes(videoNode5);
|
||||
// Adding nodes here
|
||||
const videoNode = new VideoNode({
|
||||
mediaLayerRef: this.mediaLayer,
|
||||
position: this.renderEngine.captureCanvas.position(),
|
||||
canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
videoURL:
|
||||
"https://storage.googleapis.com/vocodes-public/media/r/q/p/r/e/rqpret6mkh18dqwjqwghhdqf15x720s1/storyteller_rqpret6mkh18dqwjqwghhdqf15x720s1.mp4",
|
||||
selectionManagerRef: this.selectionManager,
|
||||
});
|
||||
// this.renderEngine.addNodes(videoNode5);
|
||||
// // Adding nodes here
|
||||
// const videoNode = new VideoNode({
|
||||
// mediaLayerRef: this.mediaLayer,
|
||||
// canvasPosition: this.renderEngine.captureCanvas.position(),
|
||||
// canvasSize: this.renderEngine.captureCanvas.size(),
|
||||
// videoURL:
|
||||
// "https://storage.googleapis.com/vocodes-public/media/r/q/p/r/e/rqpret6mkh18dqwjqwghhdqf15x720s1/storyteller_rqpret6mkh18dqwjqwghhdqf15x720s1.mp4",
|
||||
// selectionManagerRef: this.selectionManager,
|
||||
// });
|
||||
|
||||
// CODE TO TEST RENDER ENGINE
|
||||
// Testing render engine
|
||||
// this.renderEngine.addNodes(videoNode);
|
||||
// await this.renderEngine.startProcessing();
|
||||
//videoNode.simulatedLoading();
|
||||
// TODO support Text nodes
|
||||
// // CODE TO TEST RENDER ENGINE
|
||||
// // Testing render engine
|
||||
// // this.renderEngine.addNodes(videoNode);
|
||||
// // await this.renderEngine.startProcessing();
|
||||
// //videoNode.simulatedLoading();
|
||||
// // TODO support Text nodes
|
||||
|
||||
// this.renderEngine.addNodes(imageNode2);
|
||||
// // this.renderEngine.addNodes(imageNode2);
|
||||
|
||||
// // TODO if only image take image and just takes snapshots Edge case
|
||||
// this.renderEngine.addNodes(imageNode);
|
||||
}
|
||||
// // // TODO if only image take image and just takes snapshots Edge case
|
||||
// // this.renderEngine.addNodes(imageNode);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
import Konva from "konva";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { highlightStrokeWidth, primaryOrange } from "./constants";
|
||||
|
||||
export class BaseNode {
|
||||
protected selectionManagerRef: SelectionManager;
|
||||
public kNode: Konva.Node;
|
||||
protected mediaLayerRef: Konva.Layer;
|
||||
protected uuid: string;
|
||||
|
||||
// Internal State members
|
||||
// do not modify internal
|
||||
protected isSelecting: boolean = false;
|
||||
// This locks interaction when the render engine is rendering
|
||||
protected isProcessing: boolean = false;
|
||||
protected _isLocked: boolean = false;
|
||||
protected _isKEventRef: boolean = false;
|
||||
|
||||
constructor({
|
||||
selectionManagerRef,
|
||||
mediaLayerRef,
|
||||
kNode,
|
||||
}: {
|
||||
selectionManagerRef: SelectionManager;
|
||||
mediaLayerRef: Konva.Layer;
|
||||
kNode: Konva.Image | Konva.Group;
|
||||
}) {
|
||||
console.log("Node constructed");
|
||||
this.uuid = uuidv4();
|
||||
this.mediaLayerRef = mediaLayerRef;
|
||||
this.selectionManagerRef = selectionManagerRef;
|
||||
this.kNode = kNode;
|
||||
}
|
||||
|
||||
protected delete() {
|
||||
// Do any other clean up and delete the konva node.
|
||||
this.kNode.destroy();
|
||||
}
|
||||
protected async setProcessing() {
|
||||
this.isProcessing = true;
|
||||
}
|
||||
public isKEventRef() {
|
||||
return this._isKEventRef;
|
||||
}
|
||||
public setIsKEventRef(flag: boolean) {
|
||||
this._isKEventRef = flag;
|
||||
this.removeListenToBaseKNodeTransformations();
|
||||
this.removeListenToBaseKNodeDrags();
|
||||
if (flag) {
|
||||
this.listenToBaseKNodeTransformations();
|
||||
this.listenToBaseKNodeDrags();
|
||||
}
|
||||
}
|
||||
public highlight() {
|
||||
// console.log("Highlight", this.kNode._id);
|
||||
if (this.kNode instanceof Konva.Image) {
|
||||
this.kNode.stroke(primaryOrange);
|
||||
this.kNode.strokeWidth(highlightStrokeWidth);
|
||||
return;
|
||||
}
|
||||
if (this.kNode instanceof Konva.Group) {
|
||||
const wrapperRect = this.kNode.findOne(".wrapper") as Konva.Rect;
|
||||
wrapperRect.stroke(primaryOrange);
|
||||
wrapperRect.strokeWidth(highlightStrokeWidth);
|
||||
return;
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
"DEV: BaseNode Highlight Error, kNode of BaseNode should be override by MediaNodes that extends kNode",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public unhighlight() {
|
||||
// console.log("unHighlight", this.kNode._id);
|
||||
if (this.kNode instanceof Konva.Image) {
|
||||
this.kNode.strokeWidth(0);
|
||||
return;
|
||||
}
|
||||
if (this.kNode instanceof Konva.Group) {
|
||||
const wrapperRect = this.kNode.findOne(".wrapper") as Konva.Rect;
|
||||
wrapperRect.strokeWidth(0);
|
||||
return;
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn(
|
||||
"DEV: BaseNode UnHighlight Error, kNode of BaseNode should be override by MediaNodes that extends kNode",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public moveLayerUp() {
|
||||
this.kNode.moveUp();
|
||||
}
|
||||
public moveLayerDown() {
|
||||
this.kNode.moveDown();
|
||||
}
|
||||
public isLocked() {
|
||||
return this._isLocked;
|
||||
}
|
||||
public lock() {
|
||||
this._isLocked = true;
|
||||
this.kNode.setDraggable(false);
|
||||
if (this._isKEventRef) {
|
||||
this.selectionManagerRef.updateContextComponents(this);
|
||||
}
|
||||
}
|
||||
public unlock() {
|
||||
this._isLocked = false;
|
||||
this.kNode.setDraggable(true);
|
||||
if (this._isKEventRef) {
|
||||
this.selectionManagerRef.updateContextComponents(this);
|
||||
}
|
||||
}
|
||||
|
||||
public listenToBaseKNode() {
|
||||
const handleSelect = (isMultiSelect: boolean) => {
|
||||
if (!isMultiSelect) {
|
||||
// clear selection if not multislect
|
||||
//console.log("No Shift >> no multiselect");
|
||||
this.selectionManagerRef.clearSelection();
|
||||
}
|
||||
if (this.selectionManagerRef.isNodeSelected(this)) {
|
||||
this.selectionManagerRef.deselectNode(this);
|
||||
return;
|
||||
}
|
||||
this.selectionManagerRef.selectNode(this);
|
||||
};
|
||||
|
||||
this.kNode.on("mousedown", (e) => {
|
||||
// console.log("Mouse down");
|
||||
// Selection of Node
|
||||
if (!this.selectionManagerRef.isNodeSelected(this)) {
|
||||
this.isSelecting = true;
|
||||
//checking for multiselect
|
||||
handleSelect(e.evt.shiftKey);
|
||||
}
|
||||
});
|
||||
|
||||
this.kNode.on("mouseup", (e) => {
|
||||
// console.log("MOUSE UP");
|
||||
|
||||
// just coming out of dragging or selecting mode
|
||||
// no need to handle select
|
||||
if (this.selectionManagerRef.isDragging() || this.isSelecting) {
|
||||
this.isSelecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// checking for multiselect, in multiselect deselection is possible
|
||||
handleSelect(e.evt.shiftKey);
|
||||
});
|
||||
}
|
||||
public listenToBaseKNodeDrags() {
|
||||
this.kNode.on("dragstart", () => {
|
||||
// console.log("Drag start", this.kNode._id);
|
||||
if (this.isProcessing) {
|
||||
return;
|
||||
}
|
||||
this.selectionManagerRef.dragStart();
|
||||
});
|
||||
|
||||
this.kNode.on("dragend", () => {
|
||||
// console.log("Drag End", this.kNode._id);
|
||||
this.selectionManagerRef.dragEnd(this);
|
||||
});
|
||||
}
|
||||
public removeListenToBaseKNodeDrags() {
|
||||
this.kNode.removeEventListener("dragstart");
|
||||
this.kNode.removeEventListener("dragend");
|
||||
}
|
||||
public listenToBaseKNodeTransformations() {
|
||||
this.kNode.on("transformstart", (event) => {
|
||||
console.log("transformstart", event.target._id);
|
||||
this.selectionManagerRef.transformStart();
|
||||
});
|
||||
this.kNode.on("transform", () => {
|
||||
this.kNode.setAttrs({
|
||||
width: this.kNode.width() * this.kNode.scaleX(),
|
||||
height: this.kNode.height() * this.kNode.scaleY(),
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
});
|
||||
});
|
||||
this.kNode.on("transformend", (event) => {
|
||||
console.log("transformend", event.target._id);
|
||||
this.selectionManagerRef.transformEnd(this);
|
||||
});
|
||||
}
|
||||
public removeListenToBaseKNodeTransformations() {
|
||||
this.kNode.removeEventListener("transformend");
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
import Konva from "konva";
|
||||
import { NetworkedNodeContext } from "./NetworkedNodeContext";
|
||||
import { NetworkedNode } from "./NetworkedNode";
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { Position, Size } from "../types";
|
||||
import { minNodeSize, transparent } from "./constants";
|
||||
import { NodeUtilities } from "./NodeUtilities";
|
||||
|
||||
interface ImageNodeContructor {
|
||||
position: Position;
|
||||
canvasPosition: Position;
|
||||
canvasSize: Size;
|
||||
imageFile: File;
|
||||
mediaLayerRef: Konva.Layer;
|
||||
selectionManagerRef: SelectionManager;
|
||||
}
|
||||
|
||||
export class ImageNode extends NetworkedNodeContext {
|
||||
public imageURL: string;
|
||||
export class ImageNode extends NetworkedNode {
|
||||
// public imageURL: string;
|
||||
public kNode: Konva.Image;
|
||||
|
||||
private imageObject: HTMLImageElement;
|
||||
|
||||
constructor({
|
||||
position,
|
||||
canvasPosition,
|
||||
canvasSize,
|
||||
imageFile,
|
||||
mediaLayerRef,
|
||||
@@ -25,13 +28,16 @@ export class ImageNode extends NetworkedNodeContext {
|
||||
}: ImageNodeContructor) {
|
||||
// kNodes need to be created first to guaruntee it is not undefined in parent's context
|
||||
const kNode = new Konva.Image({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
image: undefined, // to do replace with placeholder
|
||||
width: 100,
|
||||
height: 100,
|
||||
size: minNodeSize,
|
||||
position: NodeUtilities.positionNodeOnCanvasCenter({
|
||||
canvasOffset: canvasPosition,
|
||||
componentSize: minNodeSize,
|
||||
maxSize: canvasSize,
|
||||
}),
|
||||
fill: "gray",
|
||||
draggable: true,
|
||||
strokeScaleEnabled: false,
|
||||
});
|
||||
|
||||
super({
|
||||
@@ -39,6 +45,7 @@ export class ImageNode extends NetworkedNodeContext {
|
||||
mediaLayerRef: mediaLayerRef,
|
||||
kNode: kNode,
|
||||
});
|
||||
this.kNode = kNode;
|
||||
this.mediaLayerRef.add(this.kNode);
|
||||
|
||||
const imageComponent = new Image();
|
||||
@@ -51,19 +58,24 @@ export class ImageNode extends NetworkedNodeContext {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderSize = this.calculateRenderSizeOnLoad({
|
||||
const adjustedSize = NodeUtilities.adjustNodeSizeToCanvas({
|
||||
componentSize: {
|
||||
width: imageComponent.width,
|
||||
height: imageComponent.height,
|
||||
},
|
||||
maxSize: canvasSize,
|
||||
});
|
||||
|
||||
const centerPosition = NodeUtilities.positionNodeOnCanvasCenter({
|
||||
canvasOffset: canvasPosition,
|
||||
componentSize: adjustedSize,
|
||||
maxSize: canvasSize,
|
||||
});
|
||||
this.kNode.image(imageComponent);
|
||||
this.kNode.setSize(renderSize);
|
||||
this.kNode.setSize(adjustedSize);
|
||||
this.kNode.setPosition(centerPosition);
|
||||
|
||||
this.listenToBaseKNode();
|
||||
this.kNode.fill(null);
|
||||
this.kNode.fill(transparent);
|
||||
this.mediaLayerRef.draw();
|
||||
|
||||
this.didFinishLoading = true;
|
||||
@@ -82,7 +94,7 @@ export class ImageNode extends NetworkedNodeContext {
|
||||
this.kNode.image(newImage);
|
||||
this.kNode.width(newImage.width);
|
||||
this.kNode.height(newImage.height);
|
||||
this.kNode.fill(null);
|
||||
this.kNode.fill(transparent);
|
||||
this.kNode.draw();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import Konva from "konva";
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { BaseNode } from "./BaseNode";
|
||||
|
||||
export class NetworkedNode extends BaseNode {
|
||||
public kNode: Konva.Image;
|
||||
public didFinishLoading: boolean = false;
|
||||
|
||||
constructor({
|
||||
kNode,
|
||||
selectionManagerRef,
|
||||
mediaLayerRef,
|
||||
}: {
|
||||
kNode: Konva.Image;
|
||||
selectionManagerRef: SelectionManager;
|
||||
mediaLayerRef: Konva.Layer;
|
||||
}) {
|
||||
super({
|
||||
kNode,
|
||||
selectionManagerRef,
|
||||
mediaLayerRef,
|
||||
});
|
||||
this.kNode = kNode;
|
||||
this.didFinishLoading = false;
|
||||
}
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
import Konva from "konva";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { Layer } from "konva/lib/Layer";
|
||||
|
||||
import { uiAccess } from "~/signals";
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { Size } from "../types";
|
||||
|
||||
const toolbarNode = uiAccess.toolbarNode;
|
||||
const loadingBar = uiAccess.loadingBar;
|
||||
|
||||
export class NetworkedNodeContext {
|
||||
public selectionManagerRef: SelectionManager;
|
||||
public kNode: Konva.Image;
|
||||
protected mediaLayerRef: Layer;
|
||||
public uuid: string;
|
||||
|
||||
// Internal State members
|
||||
// do not modify internal
|
||||
public didFinishLoading: boolean = false;
|
||||
// This locks interaction when the render engine is rendering
|
||||
protected isProcessing: boolean = false;
|
||||
protected isSelecting: boolean = false;
|
||||
protected _isLocked: boolean = false;
|
||||
|
||||
async setProcessing() {
|
||||
this.isProcessing = true;
|
||||
}
|
||||
|
||||
constructor({
|
||||
selectionManagerRef,
|
||||
mediaLayerRef,
|
||||
kNode,
|
||||
}: {
|
||||
selectionManagerRef: SelectionManager;
|
||||
mediaLayerRef: Layer;
|
||||
kNode: Konva.Image;
|
||||
}) {
|
||||
console.log("reconstructed");
|
||||
this.uuid = uuidv4();
|
||||
this.mediaLayerRef = mediaLayerRef;
|
||||
this.selectionManagerRef = selectionManagerRef;
|
||||
this.kNode = kNode;
|
||||
this.didFinishLoading = false;
|
||||
}
|
||||
protected calculateRenderSizeOnLoad({
|
||||
componentSize,
|
||||
maxSize,
|
||||
}: {
|
||||
componentSize: Size;
|
||||
maxSize: Size;
|
||||
}) {
|
||||
const renderSize = {
|
||||
width: componentSize.width,
|
||||
height: componentSize.height,
|
||||
};
|
||||
if (renderSize.width > maxSize.width) {
|
||||
const scaleDown = maxSize.width / renderSize.width;
|
||||
renderSize.width = renderSize.width * scaleDown;
|
||||
renderSize.height = renderSize.height * scaleDown;
|
||||
}
|
||||
if (renderSize.height > maxSize.height) {
|
||||
const scaleDownAgain = maxSize.height / renderSize.height;
|
||||
renderSize.width = renderSize.width * scaleDownAgain;
|
||||
renderSize.height = renderSize.height * scaleDownAgain;
|
||||
}
|
||||
return renderSize;
|
||||
}
|
||||
public delete() {
|
||||
// Do any other clean up and delete the konva node.
|
||||
this.kNode.destroy();
|
||||
}
|
||||
|
||||
public highlight() {
|
||||
this.kNode.stroke("salmon");
|
||||
this.kNode.strokeWidth(10);
|
||||
}
|
||||
|
||||
public unHighLight() {
|
||||
console.log("unHighlight", this.kNode._id);
|
||||
this.kNode.strokeWidth(0);
|
||||
}
|
||||
public moveLayerUp() {
|
||||
this.kNode.moveUp();
|
||||
}
|
||||
public moveLayerDown() {
|
||||
this.kNode.moveDown();
|
||||
}
|
||||
public isLocked() {
|
||||
return this._isLocked;
|
||||
}
|
||||
public lock() {
|
||||
this._isLocked = true;
|
||||
toolbarNode.lock();
|
||||
this.kNode.setDraggable(false);
|
||||
}
|
||||
public unlock() {
|
||||
this._isLocked = false;
|
||||
toolbarNode.unlock();
|
||||
this.kNode.setDraggable(true);
|
||||
}
|
||||
|
||||
calculateContextualsPosition(kNode: Konva.Node) {
|
||||
const w = kNode.getSize().width * kNode.scaleX();
|
||||
const h = kNode.getSize().height * kNode.scaleY();
|
||||
const x0 = kNode.getPosition().x;
|
||||
const y0 = kNode.getPosition().y;
|
||||
|
||||
const d = kNode.getAbsoluteRotation();
|
||||
const r = d >= 0 ? (d * Math.PI) / 180 : ((360 + d) * Math.PI) / 180;
|
||||
|
||||
let px: number, py: number;
|
||||
if (r < Math.PI / 2) {
|
||||
px = x0 + (h * Math.sin(r) + w * Math.cos(r)) / 2 - h * Math.sin(r);
|
||||
py = y0 + h * Math.cos(r) + w * Math.cos(Math.PI / 2 - r);
|
||||
} else if (r < Math.PI) {
|
||||
px = x0 - (h * Math.sin(r) - w * Math.cos(r)) / 2;
|
||||
py = y0 + w * Math.cos(Math.PI / 2 - r);
|
||||
} else if (r < (Math.PI * 3) / 2) {
|
||||
px = x0 + h * Math.cos(r) - (h * Math.cos(r) + w * Math.sin(r)) / 2;
|
||||
py = y0;
|
||||
} else {
|
||||
px = x0 + (-h * Math.sin(r) + w * Math.cos(r)) / 2;
|
||||
py = y0 + h * Math.cos(r);
|
||||
}
|
||||
return { x: px, y: py };
|
||||
}
|
||||
updateContextMenuPosition() {
|
||||
const coord = this.calculateContextualsPosition(this.kNode);
|
||||
toolbarNode.setPosition({
|
||||
x: coord.x,
|
||||
y: coord.y,
|
||||
});
|
||||
}
|
||||
|
||||
updateLoadingBarPosition() {
|
||||
const coord = this.calculateContextualsPosition(this.kNode);
|
||||
|
||||
loadingBar.updatePosition({
|
||||
x: coord.x,
|
||||
y: coord.y,
|
||||
});
|
||||
}
|
||||
|
||||
public updateContextComponents() {
|
||||
this.updateContextMenuPosition();
|
||||
this.updateLoadingBarPosition();
|
||||
if (this.didFinishLoading == false && !loadingBar.isShowing()) {
|
||||
loadingBar.show();
|
||||
} else {
|
||||
loadingBar.hide();
|
||||
}
|
||||
if (!toolbarNode.isShowing()) {
|
||||
toolbarNode.show({
|
||||
locked: this._isLocked,
|
||||
});
|
||||
} else {
|
||||
toolbarNode.update({
|
||||
locked: this._isLocked,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public listenToBaseKNode() {
|
||||
const handleSelect = (isMultiSelect: boolean) => {
|
||||
if (!isMultiSelect) {
|
||||
// clear selection if not multislect
|
||||
//console.log("No Shift >> no multiselect");
|
||||
this.selectionManagerRef.clearSelection();
|
||||
}
|
||||
if (this.selectionManagerRef.isNodeSelected(this)) {
|
||||
this.selectionManagerRef.deselectNode(this);
|
||||
return;
|
||||
}
|
||||
this.selectionManagerRef.selectNode(this);
|
||||
};
|
||||
|
||||
this.kNode.on("mousedown", (e) => {
|
||||
// console.log("Mouse down");
|
||||
// Selection of Node
|
||||
if (!this.selectionManagerRef.isNodeSelected(this)) {
|
||||
this.isSelecting = true;
|
||||
//checking for multiselect
|
||||
handleSelect(e.evt.shiftKey);
|
||||
}
|
||||
|
||||
if (!toolbarNode.isShowing()) {
|
||||
toolbarNode.show({
|
||||
locked: this._isLocked,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.kNode.on("mouseup", (e) => {
|
||||
// console.log("MOUSE UP");
|
||||
|
||||
// just coming out of dragging or selecting mode
|
||||
// no need to handle select
|
||||
if (this.selectionManagerRef.isDragging() || this.isSelecting) {
|
||||
this.isSelecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// checking for multiselect, in multiselect deselection is possible
|
||||
handleSelect(e.evt.shiftKey);
|
||||
});
|
||||
}
|
||||
public listenToBaseKNodeDrags() {
|
||||
this.kNode.on("dragstart", () => {
|
||||
// console.log("Drag start", this.kNode._id);
|
||||
if (this.isProcessing) {
|
||||
return;
|
||||
}
|
||||
this.selectionManagerRef.dragStart();
|
||||
this.updateContextComponents();
|
||||
});
|
||||
|
||||
this.kNode.on("dragmove", () => {
|
||||
// console.log("Drag Move");
|
||||
this.updateContextComponents();
|
||||
});
|
||||
|
||||
this.kNode.on("dragend", () => {
|
||||
// console.log("Drag End", this.kNode._id);
|
||||
this.selectionManagerRef.dragEnd();
|
||||
this.updateContextComponents();
|
||||
});
|
||||
}
|
||||
public removeListenToBaseKNodeDrags() {
|
||||
this.kNode.removeEventListener("dragstart");
|
||||
this.kNode.removeEventListener("dragmove");
|
||||
this.kNode.removeEventListener("dragend");
|
||||
}
|
||||
public listenToBaseKNodeTransformations() {
|
||||
this.kNode.on("transformstart", (event) => {
|
||||
console.log("transformstart", event.target._id);
|
||||
this.selectionManagerRef.transformStart();
|
||||
toolbarNode.hide();
|
||||
loadingBar.hide();
|
||||
});
|
||||
|
||||
this.kNode.on("transformend", (event) => {
|
||||
console.log("transformend", event.target._id);
|
||||
this.selectionManagerRef.transformEnd();
|
||||
this.updateContextComponents();
|
||||
});
|
||||
}
|
||||
public removeListenToBaseKNodeTransformations() {
|
||||
this.kNode.removeEventListener("transformend");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import Konva from "konva";
|
||||
import { Position, Size } from "../types";
|
||||
|
||||
export const NodeUtilities = {
|
||||
adjustNodeSizeToCanvas,
|
||||
positionNodeOnCanvasCenter,
|
||||
printKNodeAttrs,
|
||||
};
|
||||
function adjustNodeSizeToCanvas({
|
||||
componentSize,
|
||||
maxSize,
|
||||
}: {
|
||||
componentSize: Size;
|
||||
maxSize: Size;
|
||||
}) {
|
||||
const adjustedSize = {
|
||||
width: componentSize.width,
|
||||
height: componentSize.height,
|
||||
};
|
||||
if (adjustedSize.width > maxSize.width) {
|
||||
const scaleDown = maxSize.width / adjustedSize.width;
|
||||
adjustedSize.width = adjustedSize.width * scaleDown;
|
||||
adjustedSize.height = adjustedSize.height * scaleDown;
|
||||
}
|
||||
if (adjustedSize.height > maxSize.height) {
|
||||
const scaleDownAgain = maxSize.height / adjustedSize.height;
|
||||
adjustedSize.width = adjustedSize.width * scaleDownAgain;
|
||||
adjustedSize.height = adjustedSize.height * scaleDownAgain;
|
||||
}
|
||||
return adjustedSize;
|
||||
}
|
||||
|
||||
function positionNodeOnCanvasCenter({
|
||||
canvasOffset,
|
||||
componentSize,
|
||||
maxSize,
|
||||
}: {
|
||||
canvasOffset: Position;
|
||||
componentSize: Size;
|
||||
maxSize: Size;
|
||||
}) {
|
||||
return {
|
||||
x: canvasOffset.x + maxSize.width / 2 - componentSize.width / 2,
|
||||
y: canvasOffset.y + maxSize.height / 2 - componentSize.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
function printKNodeAttrs(kNode: Konva.Node) {
|
||||
console.log({
|
||||
position: kNode.position(),
|
||||
size: kNode.size(),
|
||||
scale: kNode.scale(),
|
||||
rotation: kNode.rotation(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import Konva from "konva";
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { BaseNode } from "./BaseNode";
|
||||
import { Position, Size, TextNodeData } from "../types";
|
||||
import { NodeUtilities } from "./NodeUtilities";
|
||||
|
||||
export class TextNode extends BaseNode {
|
||||
public kNode: Konva.Group;
|
||||
public rectNode: Konva.Rect;
|
||||
public textNode: Konva.Text;
|
||||
private originalTextSize: Size;
|
||||
private textNodeData: TextNodeData;
|
||||
|
||||
public didFinishLoading: boolean = true;
|
||||
|
||||
constructor({
|
||||
textNodeData,
|
||||
selectionManagerRef,
|
||||
mediaLayerRef,
|
||||
position,
|
||||
}: {
|
||||
textNodeData: TextNodeData;
|
||||
selectionManagerRef: SelectionManager;
|
||||
mediaLayerRef: Konva.Layer;
|
||||
position: Position;
|
||||
}) {
|
||||
console.log(textNodeData);
|
||||
const textNode = new Konva.Text({
|
||||
...textNodeData,
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 500,
|
||||
});
|
||||
const kNode = new Konva.Group({
|
||||
position: position,
|
||||
width: textNode.width() + 20,
|
||||
height: textNode.height() + 20,
|
||||
draggable: true,
|
||||
});
|
||||
const rectNode = new Konva.Rect({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: textNode.width() + 20,
|
||||
height: textNode.height() + 20,
|
||||
strokeScaleEnabled: false,
|
||||
name: "wrapper",
|
||||
});
|
||||
|
||||
super({
|
||||
selectionManagerRef: selectionManagerRef,
|
||||
mediaLayerRef: mediaLayerRef,
|
||||
kNode: kNode,
|
||||
});
|
||||
this.textNodeData = textNodeData;
|
||||
this.kNode = kNode;
|
||||
this.textNode = textNode;
|
||||
this.rectNode = rectNode;
|
||||
this.kNode.add(this.rectNode);
|
||||
this.kNode.add(this.textNode);
|
||||
this.mediaLayerRef.add(this.kNode);
|
||||
this.originalTextSize = this.textNode.size();
|
||||
this.listenToBaseKNode();
|
||||
}
|
||||
public listenToBaseKNodeTransformations() {
|
||||
this.kNode.on("transformstart", () => {
|
||||
// console.log("transformstart", event.target._id);
|
||||
this.selectionManagerRef.transformStart();
|
||||
});
|
||||
|
||||
this.kNode.on("transform", () => {
|
||||
const newBoxSize = {
|
||||
width: this.kNode.width() * this.kNode.scaleX(),
|
||||
height: this.kNode.height() * this.kNode.scaleY(),
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
};
|
||||
this.rectNode.setAttrs(newBoxSize);
|
||||
this.kNode.setAttrs(newBoxSize);
|
||||
const scaleX = (newBoxSize.width - 20) / this.originalTextSize.width;
|
||||
const scaleY = (newBoxSize.height - 20) / this.originalTextSize.height;
|
||||
this.textNode.setAttrs({
|
||||
// make sure these stay the same
|
||||
x: 10,
|
||||
y: 10,
|
||||
size: this.originalTextSize,
|
||||
//while these changes in size
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
});
|
||||
});
|
||||
this.kNode.on("transformend", () => {
|
||||
// console.log("transformend", event.target._id);
|
||||
NodeUtilities.printKNodeAttrs(this.kNode);
|
||||
NodeUtilities.printKNodeAttrs(this.textNode);
|
||||
this.selectionManagerRef.transformEnd(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
import Konva from "konva";
|
||||
import { NetworkedNodeContext } from "./NetworkedNodeContext";
|
||||
import { NetworkedNode } from "./NetworkedNode";
|
||||
import { uiAccess } from "~/signals";
|
||||
import { SelectionManager } from "../NodesManagers";
|
||||
import { Position, Size } from "../types";
|
||||
import ChromaWorker from "./ChromaWorker?sharedworker";
|
||||
import { minNodeSize, transparent } from "./constants";
|
||||
import { NodeUtilities } from "./NodeUtilities";
|
||||
|
||||
// const toolbarNode = uiAccess.toolbarNode;
|
||||
const loadingBar = uiAccess.loadingBar;
|
||||
|
||||
interface VideoNodeContructor {
|
||||
mediaLayerRef: Konva.Layer;
|
||||
position: Position;
|
||||
canvasPosition: Position;
|
||||
canvasSize: Size;
|
||||
videoURL: string;
|
||||
selectionManagerRef: SelectionManager;
|
||||
}
|
||||
|
||||
export class VideoNode extends NetworkedNodeContext {
|
||||
export class VideoNode extends NetworkedNode {
|
||||
public kNode: Konva.Image;
|
||||
public videoURL: string;
|
||||
public videoComponent: HTMLVideoElement;
|
||||
|
||||
@@ -91,7 +93,7 @@ export class VideoNode extends NetworkedNodeContext {
|
||||
|
||||
constructor({
|
||||
mediaLayerRef,
|
||||
position,
|
||||
canvasPosition,
|
||||
canvasSize,
|
||||
videoURL,
|
||||
selectionManagerRef,
|
||||
@@ -99,12 +101,16 @@ export class VideoNode extends NetworkedNodeContext {
|
||||
// kNodes need to be created first to guaruntee it is not undefined in parent's context
|
||||
const kNode = new Konva.Image({
|
||||
image: undefined,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
width: 200, // to do fix this with placeholder
|
||||
height: 200,
|
||||
draggable: true,
|
||||
// to do fix this with placeholder
|
||||
size: minNodeSize,
|
||||
position: NodeUtilities.positionNodeOnCanvasCenter({
|
||||
canvasOffset: canvasPosition,
|
||||
componentSize: minNodeSize,
|
||||
maxSize: canvasSize,
|
||||
}),
|
||||
fill: "grey",
|
||||
draggable: true,
|
||||
strokeScaleEnabled: false,
|
||||
});
|
||||
|
||||
super({
|
||||
@@ -112,6 +118,7 @@ export class VideoNode extends NetworkedNodeContext {
|
||||
mediaLayerRef: mediaLayerRef,
|
||||
kNode: kNode,
|
||||
});
|
||||
this.kNode = kNode;
|
||||
this.mediaLayerRef.add(this.kNode);
|
||||
|
||||
// state manage the node
|
||||
@@ -150,22 +157,28 @@ export class VideoNode extends NetworkedNodeContext {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderSize = this.calculateRenderSizeOnLoad({
|
||||
const adjustedSize = NodeUtilities.adjustNodeSizeToCanvas({
|
||||
componentSize: {
|
||||
width: this.videoComponent.videoWidth,
|
||||
height: this.videoComponent.videoHeight,
|
||||
},
|
||||
maxSize: canvasSize,
|
||||
});
|
||||
const centerPosition = NodeUtilities.positionNodeOnCanvasCenter({
|
||||
canvasOffset: canvasPosition,
|
||||
componentSize: adjustedSize,
|
||||
maxSize: canvasSize,
|
||||
});
|
||||
|
||||
this.kNode.image(this.videoComponent);
|
||||
this.kNode.setSize(renderSize);
|
||||
this.kNode.setSize(adjustedSize);
|
||||
this.kNode.setPosition(centerPosition);
|
||||
|
||||
this.videoComponent.currentTime = 0; // ensure it shows up on screen
|
||||
// it might have length here which we will need to trim down to 7 seconds.
|
||||
console.log(`Video Duration: ${this.videoComponent.duration}`);
|
||||
this.duration = this.videoComponent.duration;
|
||||
this.kNode.fill(null);
|
||||
this.kNode.fill(transparent);
|
||||
loadingBar.updateProgress(50);
|
||||
};
|
||||
|
||||
@@ -390,7 +403,7 @@ export class VideoNode extends NetworkedNodeContext {
|
||||
await this.updateImage(this.imageSources[this.imageIndex]);
|
||||
this.imageIndex = this.imageIndex + 1;
|
||||
|
||||
this.updateLoadingBarPosition();
|
||||
// this.updateLoadingBarPosition();
|
||||
|
||||
loadingBar.updateMessage("Generating");
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export const transparent = "rgba(0,0,0,0)";
|
||||
export const primaryOrange = "rgba(230,100,98,1)";
|
||||
export const highlightStrokeWidth = 10;
|
||||
export const minNodeSize = {
|
||||
width: 200,
|
||||
height: 200,
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export { ImageNode } from "./ImageNode";
|
||||
export { VideoNode } from "./VideoNode";
|
||||
export { TextNode } from "./TextNodes";
|
||||
@@ -6,6 +6,7 @@ export class NodeTransformer {
|
||||
|
||||
constructor() {
|
||||
this.kTransformer = new Konva.Transformer({
|
||||
padding: 0,
|
||||
anchorStyleFunc: (anchor) => {
|
||||
// anchor is Konva.Rect instance
|
||||
// you manually change its styling
|
||||
|
||||
@@ -2,6 +2,7 @@ import Konva from "konva";
|
||||
import { NodeTransformer } from "./NodeTransformer";
|
||||
import { MediaNode, Position, Transformation } from "../types";
|
||||
import { uiAccess } from "~/signals";
|
||||
import { NetworkedNode } from "../Nodes/NetworkedNode";
|
||||
|
||||
export enum SelectionManagerEvents {
|
||||
NODES_TRANSLATIONS = "nodestranslation",
|
||||
@@ -14,8 +15,8 @@ export interface NodesTranslationEventDetails {
|
||||
}
|
||||
export interface NodeTransformationEventDetails {
|
||||
nodes: Set<MediaNode>;
|
||||
initialTransformations: Map<MediaNode, Transformation>;
|
||||
finalTransformations: Map<MediaNode, Transformation>;
|
||||
initialTransformations: Map<MediaNode, Transformation[]>;
|
||||
finalTransformations: Map<MediaNode, Transformation[]>;
|
||||
}
|
||||
|
||||
export class SelectionManager {
|
||||
@@ -23,7 +24,7 @@ export class SelectionManager {
|
||||
private mediaLayerRef: Konva.Layer;
|
||||
private nodeTransformerRef: NodeTransformer;
|
||||
private initialPositions: Map<MediaNode, Position>;
|
||||
private initialTransformations: Map<MediaNode, Transformation>;
|
||||
private initialTransformations: Map<MediaNode, Transformation[]>;
|
||||
public eventTarget: EventTarget;
|
||||
private _isDragging: boolean = false;
|
||||
|
||||
@@ -36,12 +37,15 @@ export class SelectionManager {
|
||||
}) {
|
||||
this.selectedNodes = new Set();
|
||||
this.initialPositions = new Map<MediaNode, Position>();
|
||||
this.initialTransformations = new Map<MediaNode, Transformation>();
|
||||
this.initialTransformations = new Map<MediaNode, Transformation[]>();
|
||||
this.eventTarget = new EventTarget();
|
||||
this.mediaLayerRef = mediaLayerRef;
|
||||
this.nodeTransformerRef = nodeTransformerRef;
|
||||
}
|
||||
|
||||
// This lets us perfom operations on the selected node.
|
||||
public getSelectedNodes(): Set<MediaNode> {
|
||||
return this.selectedNodes;
|
||||
}
|
||||
public selectNodes(nodes: MediaNode[]) {
|
||||
const unlockedNodes = nodes.filter((node) => {
|
||||
return !node.isLocked();
|
||||
@@ -60,11 +64,9 @@ export class SelectionManager {
|
||||
}
|
||||
node.highlight();
|
||||
if (this.selectedNodes.size === 0) {
|
||||
node.removeListenToBaseKNodeTransformations();
|
||||
node.listenToBaseKNodeTransformations();
|
||||
node.removeListenToBaseKNodeDrags();
|
||||
node.listenToBaseKNodeDrags();
|
||||
node.updateContextComponents();
|
||||
node.setIsKEventRef(true);
|
||||
this.updateContextComponents(node);
|
||||
this.showContextComponents(node);
|
||||
}
|
||||
this.selectedNodes.add(node);
|
||||
this.updateNodeTransformer();
|
||||
@@ -75,9 +77,10 @@ export class SelectionManager {
|
||||
}
|
||||
|
||||
public deselectNode(node: MediaNode, doNotDraw?: boolean): void {
|
||||
node.unHighLight();
|
||||
node.removeListenToBaseKNodeTransformations();
|
||||
node.removeListenToBaseKNodeDrags();
|
||||
node.unhighlight();
|
||||
if (node.isKEventRef()) {
|
||||
node.setIsKEventRef(false);
|
||||
}
|
||||
this.selectedNodes.delete(node);
|
||||
this.updateNodeTransformer();
|
||||
if (!doNotDraw) {
|
||||
@@ -88,16 +91,21 @@ export class SelectionManager {
|
||||
public clearSelection(): void {
|
||||
console.log("Clearing Selection");
|
||||
this.selectedNodes.forEach((node) => {
|
||||
node.unHighLight();
|
||||
node.removeListenToBaseKNodeTransformations();
|
||||
node.removeListenToBaseKNodeDrags();
|
||||
node.unhighlight();
|
||||
if (node.isKEventRef()) {
|
||||
node.setIsKEventRef(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.selectedNodes.clear();
|
||||
this.nodeTransformerRef.clear();
|
||||
uiAccess.toolbarNode.hide();
|
||||
this.hideContextComponents();
|
||||
this.mediaLayerRef.batchDraw();
|
||||
}
|
||||
|
||||
public onToggleLock(node: MediaNode) {
|
||||
this.updateContextComponents(node);
|
||||
}
|
||||
public isDragging() {
|
||||
return this._isDragging;
|
||||
}
|
||||
@@ -108,13 +116,15 @@ export class SelectionManager {
|
||||
public dragStart() {
|
||||
// track the dragging state for mouseevents in nodes
|
||||
this._isDragging = true;
|
||||
this.hideContextComponents();
|
||||
|
||||
// track all nodes initial position when dragstart
|
||||
this.selectedNodes.forEach((selectedNode) => {
|
||||
this.initialPositions.set(selectedNode, selectedNode.kNode.position());
|
||||
});
|
||||
}
|
||||
|
||||
public dragEnd() {
|
||||
public dragEnd(refNode: MediaNode) {
|
||||
// release the dragging state for mouseevents in nodes
|
||||
this._isDragging = false;
|
||||
// track and map the final positions
|
||||
@@ -135,36 +145,49 @@ export class SelectionManager {
|
||||
},
|
||||
),
|
||||
);
|
||||
// done, clear data and show menu
|
||||
this.initialPositions.clear();
|
||||
this.updateContextComponents(refNode);
|
||||
this.showContextComponents(refNode);
|
||||
}
|
||||
private getKNodeTransformation(kNode: Konva.Node) {
|
||||
return {
|
||||
kNodeId: kNode._id,
|
||||
position: kNode.position(),
|
||||
size: kNode.size(),
|
||||
rotation: kNode.rotation(),
|
||||
scale: {
|
||||
x: kNode.scaleX(),
|
||||
y: kNode.scaleY(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public transformStart() {
|
||||
// track all nodes initial transformation when transformStart
|
||||
this.selectedNodes.forEach((selectedNode) => {
|
||||
this.initialTransformations.set(selectedNode, {
|
||||
position: selectedNode.kNode.position(),
|
||||
size: selectedNode.kNode.size(),
|
||||
rotation: selectedNode.kNode.rotation(),
|
||||
scale: {
|
||||
scaleX: selectedNode.kNode.scaleX(),
|
||||
scaleY: selectedNode.kNode.scaleY(),
|
||||
},
|
||||
});
|
||||
const transformations = [this.getKNodeTransformation(selectedNode.kNode)];
|
||||
if (selectedNode.kNode instanceof Konva.Group) {
|
||||
const childKNodes = selectedNode.kNode.getChildren();
|
||||
childKNodes.forEach((childKNode) => {
|
||||
transformations.push(this.getKNodeTransformation(childKNode));
|
||||
});
|
||||
}
|
||||
this.initialTransformations.set(selectedNode, transformations);
|
||||
});
|
||||
this.hideContextComponents();
|
||||
}
|
||||
public transformEnd() {
|
||||
public transformEnd(refNode: MediaNode) {
|
||||
// track and map the final positions
|
||||
const finalTransformation = new Map<MediaNode, Transformation>();
|
||||
const finalTransformations = new Map<MediaNode, Transformation[]>();
|
||||
this.selectedNodes.forEach((selectedNode) => {
|
||||
finalTransformation.set(selectedNode, {
|
||||
position: selectedNode.kNode.position(),
|
||||
size: selectedNode.kNode.size(),
|
||||
rotation: selectedNode.kNode.rotation(),
|
||||
scale: {
|
||||
scaleX: selectedNode.kNode.scaleX(),
|
||||
scaleY: selectedNode.kNode.scaleY(),
|
||||
},
|
||||
});
|
||||
const transformations = [this.getKNodeTransformation(selectedNode.kNode)];
|
||||
if (selectedNode.kNode instanceof Konva.Group) {
|
||||
const childKNodes = selectedNode.kNode.getChildren();
|
||||
childKNodes.forEach((childKNode) => {
|
||||
transformations.push(this.getKNodeTransformation(childKNode));
|
||||
});
|
||||
}
|
||||
finalTransformations.set(selectedNode, transformations);
|
||||
});
|
||||
// dispatch the info for engine to manage un-redo stack
|
||||
this.eventTarget.dispatchEvent(
|
||||
@@ -174,16 +197,13 @@ export class SelectionManager {
|
||||
detail: {
|
||||
nodes: this.selectedNodes,
|
||||
initialTransformations: new Map(this.initialTransformations),
|
||||
finalTransformations: finalTransformation,
|
||||
finalTransformations: finalTransformations,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// This lets us perfom operations on the selected node.
|
||||
public getSelectedNodes(): Set<MediaNode> {
|
||||
return this.selectedNodes;
|
||||
this.updateContextComponents(refNode);
|
||||
this.showContextComponents(refNode);
|
||||
}
|
||||
|
||||
private updateNodeTransformer() {
|
||||
@@ -194,4 +214,66 @@ export class SelectionManager {
|
||||
);
|
||||
this.nodeTransformerRef.enable({ selectedNodes: transformableNodes });
|
||||
}
|
||||
|
||||
protected calculateContextualsPosition(kNode: Konva.Node) {
|
||||
const w = kNode.getSize().width * kNode.scaleX();
|
||||
const h = kNode.getSize().height * kNode.scaleY();
|
||||
const x0 = kNode.getPosition().x;
|
||||
const y0 = kNode.getPosition().y;
|
||||
|
||||
const d = kNode.getAbsoluteRotation();
|
||||
const r = d >= 0 ? (d * Math.PI) / 180 : ((360 + d) * Math.PI) / 180;
|
||||
|
||||
let px: number, py: number;
|
||||
if (r < Math.PI / 2) {
|
||||
px = x0 + (h * Math.sin(r) + w * Math.cos(r)) / 2 - h * Math.sin(r);
|
||||
py = y0 + h * Math.cos(r) + w * Math.cos(Math.PI / 2 - r);
|
||||
} else if (r < Math.PI) {
|
||||
px = x0 - (h * Math.sin(r) - w * Math.cos(r)) / 2;
|
||||
py = y0 + w * Math.cos(Math.PI / 2 - r);
|
||||
} else if (r < (Math.PI * 3) / 2) {
|
||||
px = x0 + h * Math.cos(r) - (h * Math.cos(r) + w * Math.sin(r)) / 2;
|
||||
py = y0;
|
||||
} else {
|
||||
px = x0 + (-h * Math.sin(r) + w * Math.cos(r)) / 2;
|
||||
py = y0 + h * Math.cos(r);
|
||||
}
|
||||
return { x: px, y: py };
|
||||
}
|
||||
|
||||
public hideContextComponents() {
|
||||
uiAccess.toolbarNode.hide();
|
||||
}
|
||||
public showContextComponents(node: MediaNode) {
|
||||
if (!uiAccess.toolbarNode.isShowing()) {
|
||||
uiAccess.toolbarNode.show({
|
||||
locked: node.isLocked(),
|
||||
});
|
||||
} else {
|
||||
uiAccess.toolbarNode.update({
|
||||
locked: node.isLocked(),
|
||||
});
|
||||
}
|
||||
if (node instanceof NetworkedNode) {
|
||||
if (!node.didFinishLoading && !uiAccess.loadingBar.isShowing()) {
|
||||
uiAccess.loadingBar.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
public updateContextComponents(node: MediaNode) {
|
||||
const coord = this.calculateContextualsPosition(node.kNode);
|
||||
if (node.isLocked() !== uiAccess.toolbarNode.isLocked()) {
|
||||
uiAccess.toolbarNode.setLocked(node.isLocked());
|
||||
}
|
||||
uiAccess.toolbarNode.setPosition({
|
||||
x: coord.x,
|
||||
y: coord.y,
|
||||
});
|
||||
if (node instanceof NetworkedNode) {
|
||||
uiAccess.loadingBar.updatePosition({
|
||||
x: coord.x,
|
||||
y: coord.y,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,19 +33,21 @@ export class SelectorSquare {
|
||||
}
|
||||
public enable({
|
||||
captureCanvasRef,
|
||||
mediaLayerRef,
|
||||
nodesManagerRef,
|
||||
selectionManagerRef,
|
||||
stage,
|
||||
stageRef,
|
||||
}: {
|
||||
captureCanvasRef: Konva.Rect;
|
||||
mediaLayerRef: Konva.Layer;
|
||||
nodesManagerRef: NodesManager;
|
||||
selectionManagerRef: SelectionManager;
|
||||
stage: Konva.Stage;
|
||||
stageRef: Konva.Stage;
|
||||
}) {
|
||||
stage.on("mousedown touchstart", (e) => {
|
||||
const stagePointerPos = stage.getPointerPosition();
|
||||
stageRef.on("mousedown touchstart", (e) => {
|
||||
const stagePointerPos = stageRef.getPointerPosition();
|
||||
if (
|
||||
(e.target !== stage && e.target !== captureCanvasRef) || // do nothing if we mousedown on any shape
|
||||
(e.target !== stageRef && e.target !== captureCanvasRef) || // do nothing if we mousedown on any shape
|
||||
stagePointerPos === null || // do nothing if pointers not available
|
||||
e.evt.shiftKey // do nothing so then multiselect is more forgiving in misclicks
|
||||
) {
|
||||
@@ -54,7 +56,7 @@ export class SelectorSquare {
|
||||
|
||||
// start handle mousedown
|
||||
e.evt.preventDefault();
|
||||
if (e.target === stage || e.target === captureCanvasRef) {
|
||||
if (e.target === stageRef || e.target === captureCanvasRef) {
|
||||
//moused down on empty space, clear previous selection first
|
||||
selectionManagerRef.clearSelection();
|
||||
}
|
||||
@@ -73,9 +75,9 @@ export class SelectorSquare {
|
||||
this.selecting = true;
|
||||
});
|
||||
|
||||
stage.on("mousemove touchmove", (e) => {
|
||||
stageRef.on("mousemove touchmove", (e) => {
|
||||
// do nothing if we didn't start selection
|
||||
const stagePointerPos = stage.getPointerPosition();
|
||||
const stagePointerPos = stageRef.getPointerPosition();
|
||||
if (!this.selecting || stagePointerPos === null) {
|
||||
return;
|
||||
}
|
||||
@@ -97,7 +99,7 @@ export class SelectorSquare {
|
||||
height: Math.abs(y2 - y1),
|
||||
});
|
||||
|
||||
stage.on("mouseup touchend", (e) => {
|
||||
stageRef.on("mouseup touchend", (e) => {
|
||||
// do nothing if we didn't start selection
|
||||
this.selecting = false;
|
||||
if (!this.kSquare.visible()) {
|
||||
@@ -107,7 +109,7 @@ export class SelectorSquare {
|
||||
// update visibility
|
||||
this.kSquare.visible(false);
|
||||
// Find all the Nodes and feed them into Selectmanager
|
||||
var shapes = stage.find("Image");
|
||||
var shapes = mediaLayerRef.getChildren();
|
||||
var box = this.kSquare.getClientRect();
|
||||
var foundKNodes = shapes.filter((shape) =>
|
||||
Konva.Util.haveIntersection(box, shape.getClientRect()),
|
||||
|
||||
@@ -206,10 +206,11 @@ export class RenderEngine {
|
||||
}
|
||||
|
||||
// update the context menu
|
||||
this.videoNodes.forEach((node) => {
|
||||
// find selected node... TODO and update that position
|
||||
node.updateContextComponents();
|
||||
});
|
||||
// TODO: find selected node and update that position
|
||||
// this.videoNodes.forEach((node) => {
|
||||
// node.updateContextComponents();
|
||||
// });
|
||||
|
||||
this.mediaLayerRef.batchDraw();
|
||||
}
|
||||
|
||||
@@ -350,7 +351,7 @@ export class RenderEngine {
|
||||
return;
|
||||
}
|
||||
item.kNode.listening(false);
|
||||
item.unHighLight();
|
||||
item.unhighlight();
|
||||
if (item.didFinishLoading == false) {
|
||||
// error out and show error message
|
||||
//this.startProcessing();
|
||||
|
||||
@@ -4,8 +4,8 @@ import { MediaNode, Transformation } from "../types";
|
||||
|
||||
export class TransformCommand implements ICommand {
|
||||
private nodes: Set<MediaNode>;
|
||||
private initialTransformations: Map<MediaNode, Transformation>;
|
||||
private finalTransformations: Map<MediaNode, Transformation>;
|
||||
private initialTransformations: Map<MediaNode, Transformation[]>;
|
||||
private finalTransformations: Map<MediaNode, Transformation[]>;
|
||||
private layerRef: Konva.Layer;
|
||||
|
||||
constructor({
|
||||
@@ -15,24 +15,54 @@ export class TransformCommand implements ICommand {
|
||||
layerRef,
|
||||
}: {
|
||||
nodes: Set<MediaNode>;
|
||||
initialTransformations: Map<MediaNode, Transformation>;
|
||||
finalTransformations: Map<MediaNode, Transformation>;
|
||||
initialTransformations: Map<MediaNode, Transformation[]>;
|
||||
finalTransformations: Map<MediaNode, Transformation[]>;
|
||||
layerRef: Konva.Layer;
|
||||
}) {
|
||||
this.nodes = new Set<MediaNode>(nodes);
|
||||
this.initialTransformations = initialTransformations;
|
||||
this.finalTransformations = finalTransformations;
|
||||
this.layerRef = layerRef;
|
||||
console.log("transform command", this);
|
||||
}
|
||||
private transformKNode(kNode: Konva.Node, transform: Transformation) {
|
||||
kNode.setAttrs({
|
||||
position: transform.position,
|
||||
size: transform.size,
|
||||
rotation: transform.rotation,
|
||||
scale: transform.scale,
|
||||
});
|
||||
}
|
||||
private findKNodeOfTransform = (
|
||||
node: MediaNode,
|
||||
transform: Transformation,
|
||||
) => {
|
||||
const parentKNode = node.kNode;
|
||||
if (parentKNode._id === transform.kNodeId) {
|
||||
this.transformKNode(parentKNode, transform);
|
||||
return;
|
||||
}
|
||||
if (parentKNode instanceof Konva.Group) {
|
||||
const childKNode = parentKNode
|
||||
.getChildren()
|
||||
.find((currChild) => currChild._id === transform.kNodeId);
|
||||
if (childKNode) {
|
||||
this.transformKNode(childKNode, transform);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn("Error in Transform", node, transform);
|
||||
}
|
||||
};
|
||||
|
||||
execute() {
|
||||
this.nodes.forEach((node) => {
|
||||
const finalTransformation = this.finalTransformations.get(node);
|
||||
if (finalTransformation) {
|
||||
node.kNode.position(finalTransformation.position);
|
||||
node.kNode.size(finalTransformation.size);
|
||||
node.kNode.rotation(finalTransformation.rotation);
|
||||
node.kNode.scaleX(finalTransformation.scale.scaleX);
|
||||
node.kNode.scaleY(finalTransformation.scale.scaleY);
|
||||
const finalTransformations = this.finalTransformations.get(node);
|
||||
if (finalTransformations) {
|
||||
finalTransformations.forEach((transform) =>
|
||||
this.findKNodeOfTransform(node, transform),
|
||||
);
|
||||
}
|
||||
});
|
||||
this.layerRef.draw();
|
||||
@@ -40,13 +70,11 @@ export class TransformCommand implements ICommand {
|
||||
|
||||
undo() {
|
||||
this.nodes.forEach((node) => {
|
||||
const initialTransformation = this.initialTransformations.get(node);
|
||||
if (initialTransformation) {
|
||||
node.kNode.position(initialTransformation.position);
|
||||
node.kNode.size(initialTransformation.size);
|
||||
node.kNode.rotation(initialTransformation.rotation);
|
||||
node.kNode.scaleX(initialTransformation.scale.scaleX);
|
||||
node.kNode.scaleY(initialTransformation.scale.scaleY);
|
||||
const initialTransformations = this.initialTransformations.get(node);
|
||||
if (initialTransformations) {
|
||||
initialTransformations.forEach((transform) =>
|
||||
this.findKNodeOfTransform(node, transform),
|
||||
);
|
||||
}
|
||||
});
|
||||
this.layerRef.draw();
|
||||
|
||||
+15
-9
@@ -1,21 +1,27 @@
|
||||
import { Vector2d } from "konva/lib/types";
|
||||
import { BaseNode } from "./Nodes/BaseNode";
|
||||
// import { NetworkedNode } from "./Nodes/NetworkedNode";
|
||||
import { VideoNode } from "./Nodes/VideoNode";
|
||||
import { ImageNode } from "./Nodes/ImageNode";
|
||||
import { NetworkedNodeContext } from "./Nodes/NetworkedNodeContext";
|
||||
export type MediaNode = NetworkedNodeContext | VideoNode | ImageNode;
|
||||
import { TextNode } from "./Nodes/TextNodes";
|
||||
|
||||
export interface Scale {
|
||||
scaleX: number;
|
||||
scaleY: number;
|
||||
export interface TextNodeData {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export type MediaNode =
|
||||
// | NetworkedNode
|
||||
BaseNode | VideoNode | ImageNode | TextNode;
|
||||
|
||||
export interface Position extends Vector2d {}
|
||||
export interface Scale extends Vector2d {}
|
||||
export interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
export interface Position {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface Transformation {
|
||||
kNodeId: number | string;
|
||||
position: Position;
|
||||
size: Size;
|
||||
scale: Scale;
|
||||
|
||||
@@ -48,9 +48,7 @@ export const KonvaRootComponent = ({ className }: { className: string }) => {
|
||||
/>
|
||||
<SignaledToolbarMain
|
||||
layoutSignal={layoutContext.signal}
|
||||
openAddImage={appUiContext.openAddImage}
|
||||
openAddVideo={appUiContext.openAddVideo}
|
||||
openAIStylize={appUiContext.openAIStylize}
|
||||
appUiContext={appUiContext}
|
||||
/>
|
||||
<SignaledDialogs
|
||||
appUiSignal={appUiContext.signal}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useCallback } from "react";
|
||||
import { Signal } from "@preact/signals-react";
|
||||
import { dispatchUiEvents, uiEvents } from "~/signals";
|
||||
import { dispatchUiEvents } from "~/signals";
|
||||
import {
|
||||
DialogAddImage,
|
||||
DialogAddVideo,
|
||||
DialogAiStylize,
|
||||
DialogChromakey,
|
||||
DialogEditText,
|
||||
DialogError,
|
||||
} from "~/components/features";
|
||||
|
||||
@@ -22,6 +23,7 @@ export const SignaledDialogs = ({
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{/* Dialogs that are opened from within Reactland*/}
|
||||
<DialogAddImage
|
||||
isOpen={appUiSignal.value.isAddImageOpen ?? false}
|
||||
stagedImage={appUiSignal.value.stagedImage}
|
||||
@@ -54,6 +56,14 @@ export const SignaledDialogs = ({
|
||||
}}
|
||||
closeCallback={resetAll}
|
||||
/>
|
||||
<DialogEditText
|
||||
isOpen={appUiSignal.value.isEditTextOpen ?? false}
|
||||
onDoneEditText={(data) => {
|
||||
dispatchUiEvents.addTextToEngine(data);
|
||||
}}
|
||||
closeCallback={resetAll}
|
||||
/>
|
||||
{/* Dialogs that are opened from the Engine*/}
|
||||
<SignaledDialogChromakey />
|
||||
<SignaledDialogError />
|
||||
</>
|
||||
|
||||
@@ -9,17 +9,14 @@ import { LoadingBar } from "~/components/ui";
|
||||
|
||||
import { ToolbarMainButtonNames } from "~/components/features/ToolbarMain/enum";
|
||||
import { LayoutSignalType } from "./contextSignals/layout";
|
||||
import { AppUiContextInterface } from "./contextSignals/appUi";
|
||||
|
||||
export const SignaledToolbarMain = ({
|
||||
layoutSignal,
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
openAIStylize,
|
||||
appUiContext,
|
||||
}: {
|
||||
layoutSignal: LayoutSignalType;
|
||||
openAddImage: () => void;
|
||||
openAddVideo: () => void;
|
||||
openAIStylize: () => void;
|
||||
appUiContext: AppUiContextInterface;
|
||||
}) => {
|
||||
//// for testing
|
||||
const { isMobile } = layoutSignal;
|
||||
@@ -34,12 +31,26 @@ export const SignaledToolbarMain = ({
|
||||
/// end for testing
|
||||
|
||||
const loadingBar = toolbarMain.loadingBar.signal.value;
|
||||
const getButtonDispatcher = (buttonName: ToolbarMainButtonNames) => {
|
||||
switch (buttonName) {
|
||||
case ToolbarMainButtonNames.ADD_TEXT:
|
||||
return () => appUiContext.openEditText();
|
||||
case ToolbarMainButtonNames.AI_STYLIZE:
|
||||
return () => appUiContext.openAIStylize();
|
||||
case ToolbarMainButtonNames.ADD_IMAGE:
|
||||
return () => appUiContext.openAddImage();
|
||||
case ToolbarMainButtonNames.ADD_VIDEO:
|
||||
return () => appUiContext.openAddVideo();
|
||||
default:
|
||||
return dispatchers[buttonName];
|
||||
}
|
||||
};
|
||||
const buttonProps = Object.values(ToolbarMainButtonNames).reduce(
|
||||
(acc, buttonName) => {
|
||||
acc[buttonName] = {
|
||||
disabled: toolbarMain.signal.value.buttonStates[buttonName].disabled,
|
||||
active: toolbarMain.signal.value.buttonStates[buttonName].active,
|
||||
onClick: dispatchers[buttonName],
|
||||
onClick: getButtonDispatcher(buttonName),
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
@@ -74,9 +85,6 @@ export const SignaledToolbarMain = ({
|
||||
<ToolbarMain
|
||||
disabled={toolbarMain.signal.value.disabled}
|
||||
buttonProps={buttonProps}
|
||||
openAddImage={() => openAddImage()}
|
||||
openAddVideo={() => openAddVideo()}
|
||||
openAIStylize={() => openAIStylize()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { signal } from "@preact/signals-react";
|
||||
|
||||
export interface AppUiContextInterface {
|
||||
resetAll: () => void;
|
||||
openAddImage: (file?: File) => void;
|
||||
openAddVideo: (file?: File) => void;
|
||||
openAIStylize: () => void;
|
||||
openEditText: () => void;
|
||||
}
|
||||
export type AppUiSignalType = {
|
||||
isAddVideoOpen: boolean;
|
||||
stagedVideo: File | null;
|
||||
isAddImageOpen: boolean;
|
||||
stagedImage: File | null;
|
||||
isAiStylizeOpen: boolean;
|
||||
isEditTextOpen: boolean;
|
||||
};
|
||||
const appUiInitialState = {
|
||||
isAddVideoOpen: false,
|
||||
@@ -14,6 +22,7 @@ const appUiInitialState = {
|
||||
isAddImageOpen: false,
|
||||
stagedImage: null,
|
||||
isAiStylizeOpen: false,
|
||||
isEditTextOpen: false,
|
||||
};
|
||||
export const useAppUiContext = () => {
|
||||
const appUiRef = useRef(signal<AppUiSignalType>(appUiInitialState));
|
||||
@@ -42,11 +51,18 @@ export const useAppUiContext = () => {
|
||||
isAiStylizeOpen: true,
|
||||
};
|
||||
}, []);
|
||||
const openEditText = useCallback(() => {
|
||||
appUiSignal.value = {
|
||||
...appUiInitialState,
|
||||
isEditTextOpen: true,
|
||||
};
|
||||
}, []);
|
||||
return {
|
||||
signal: appUiSignal,
|
||||
resetAll,
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
openAIStylize,
|
||||
openEditText,
|
||||
};
|
||||
};
|
||||
@@ -86,9 +86,7 @@ export const DialogAiStylize = ({
|
||||
>
|
||||
{panelState === SubPanelNames.BASIC && (
|
||||
<>
|
||||
<DialogTitle className="font-bold">
|
||||
Use AI to Stylize{" "}
|
||||
</DialogTitle>
|
||||
<DialogTitle className="font-bold">Use AI to Stylize</DialogTitle>
|
||||
<SubPanelBasic
|
||||
selectedArtStyle={selectedArtStyle}
|
||||
positivePrompt={positivePrompt}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useState, ChangeEvent } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Dialog, DialogPanel, DialogTitle } from "@headlessui/react";
|
||||
import { Button, TextEditor } from "~/components/ui";
|
||||
import { TextNodeData } from "~/KonvaApp/types";
|
||||
|
||||
import {
|
||||
dialogBackgroundStyles,
|
||||
paperWrapperStyles,
|
||||
} from "~/components/styles";
|
||||
|
||||
import {
|
||||
TextFormatData,
|
||||
FontStyle,
|
||||
FontWeight,
|
||||
TextAlign,
|
||||
TextDecoration,
|
||||
} from "~/components/ui/TextEditor/types";
|
||||
|
||||
export const DialogEditText = ({
|
||||
isOpen,
|
||||
closeCallback,
|
||||
onDoneEditText,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
closeCallback: () => void;
|
||||
onDoneEditText: (doneData: TextNodeData) => void;
|
||||
}) => {
|
||||
const [text, setText] = useState<string>("");
|
||||
const [textFormatData, setTextFormatData] = useState<TextFormatData>({
|
||||
color: "#000000",
|
||||
fontFamily: "Arial",
|
||||
fontSize: 20,
|
||||
fontStyle: FontStyle.NORMAL,
|
||||
fontWeight: FontWeight.NORMAL,
|
||||
textAlign: TextAlign.CENTER,
|
||||
textDecoration: TextDecoration.NONE,
|
||||
});
|
||||
|
||||
const handleOnChangeText = (newText: string) => {
|
||||
setText(newText);
|
||||
};
|
||||
const handleOnChangeFormatting = (newFormatData: Partial<TextFormatData>) => {
|
||||
setTextFormatData((curr) => ({
|
||||
...curr,
|
||||
...newFormatData,
|
||||
}));
|
||||
};
|
||||
const handleOnDoneEditText = () => {
|
||||
const textNodeData = {
|
||||
text: text,
|
||||
fill: textFormatData.color,
|
||||
fontFamily: textFormatData.fontFamily,
|
||||
align: textFormatData.textAlign,
|
||||
fontStyle:
|
||||
textFormatData.fontStyle === FontStyle.NORMAL &&
|
||||
textFormatData.fontWeight === FontWeight.NORMAL
|
||||
? "normal"
|
||||
: `${textFormatData.fontWeight !== FontWeight.NORMAL && textFormatData.fontWeight} ${textFormatData.fontStyle !== FontStyle.NORMAL && textFormatData.fontStyle}`,
|
||||
textDecoration:
|
||||
textFormatData.textDecoration === TextDecoration.NONE
|
||||
? ""
|
||||
: textFormatData.textDecoration,
|
||||
};
|
||||
onDoneEditText(textNodeData);
|
||||
closeCallback();
|
||||
};
|
||||
return (
|
||||
<Dialog open={isOpen} onClose={closeCallback} className="relative z-50">
|
||||
<div className={dialogBackgroundStyles}>
|
||||
<DialogPanel
|
||||
className={twMerge(
|
||||
paperWrapperStyles,
|
||||
"flex w-full max-w-5xl flex-col justify-between gap-4 px-6 pb-6 pt-4",
|
||||
)}
|
||||
// style={{ height: "calc(100vh - 200px)" }}
|
||||
>
|
||||
<DialogTitle className="font-bold">Edit Text</DialogTitle>
|
||||
<TextEditor
|
||||
text={text}
|
||||
formatData={textFormatData}
|
||||
onChangeText={handleOnChangeText}
|
||||
onChangeFormatting={handleOnChangeFormatting}
|
||||
/>
|
||||
<div className="flex w-full justify-center gap-4">
|
||||
<Button onClick={closeCallback} variant="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="hover:animate-pulse"
|
||||
onClick={handleOnDoneEditText}
|
||||
disabled={text === ""}
|
||||
>
|
||||
Enter
|
||||
</Button>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { DialogEditText } from "./DialogEditText";
|
||||
@@ -0,0 +1,128 @@
|
||||
import { Fragment, MouseEventHandler } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faArrowRotateLeft,
|
||||
faArrowRotateRight,
|
||||
faBars,
|
||||
faCameraRotate,
|
||||
faDownload,
|
||||
faFilePlus,
|
||||
faFilm,
|
||||
faFloppyDisk,
|
||||
faHatWizard,
|
||||
faImage,
|
||||
faLocationArrow,
|
||||
faText,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { ToolbarButton } from "../ToolbarButton";
|
||||
|
||||
// style and constants
|
||||
import { paperWrapperStyles, toolTipStyles } from "~/components/styles";
|
||||
import { ToolbarMainButtonNames } from "./enum";
|
||||
|
||||
export const ToolbarMain = ({
|
||||
disabled = false,
|
||||
buttonProps,
|
||||
}: {
|
||||
disabled?: boolean;
|
||||
buttonProps: {
|
||||
[key in ToolbarMainButtonNames]: {
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
};
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex w-fit items-center divide-x divide-ui-border",
|
||||
paperWrapperStyles,
|
||||
disabled &&
|
||||
"pointer-events-none cursor-default bg-ui-border shadow-md",
|
||||
)}
|
||||
>
|
||||
<div className="pl-1 pr-2">
|
||||
<ToolbarButton icon={faBars} buttonProps={buttonProps.MENU} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButton
|
||||
icon={faLocationArrow}
|
||||
iconProps={{ className: "fa-flip-horizontal" }}
|
||||
buttonProps={buttonProps.SELECT}
|
||||
tooltip="Select"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faText}
|
||||
buttonProps={buttonProps.ADD_TEXT}
|
||||
tooltip="Add Text"
|
||||
/>
|
||||
<Popover className="relative">
|
||||
<PopoverButton as={Fragment}>
|
||||
<button
|
||||
data-tooltip="Add..."
|
||||
className={twMerge(
|
||||
"size-10 rounded-2xl p-2 hover:bg-secondary-500 hover:text-white",
|
||||
toolTipStyles,
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFilePlus} />
|
||||
</button>
|
||||
</PopoverButton>
|
||||
<PopoverPanel
|
||||
anchor="bottom"
|
||||
className={twMerge(
|
||||
// "absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2",
|
||||
"flex flex-col [--anchor-gap:16px]",
|
||||
paperWrapperStyles,
|
||||
)}
|
||||
>
|
||||
<ToolbarButton icon={faImage} buttonProps={buttonProps.ADD_IMAGE}>
|
||||
Add Image
|
||||
</ToolbarButton>
|
||||
<ToolbarButton icon={faFilm} buttonProps={buttonProps.ADD_VIDEO}>
|
||||
Add Video
|
||||
</ToolbarButton>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
{/* <ToolbarButton
|
||||
icon={faCameraRotate}
|
||||
buttonProps={buttonProps.CHANGE_CAMERA_ORIENTATION}
|
||||
tooltip="Change Orientation"
|
||||
/> */}
|
||||
<ToolbarButton
|
||||
icon={faHatWizard}
|
||||
buttonProps={buttonProps.AI_STYLIZE}
|
||||
tooltip="AI Stylize"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButton
|
||||
icon={faArrowRotateLeft}
|
||||
buttonProps={buttonProps.UNDO}
|
||||
tooltip="Undo"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faArrowRotateRight}
|
||||
buttonProps={buttonProps.REDO}
|
||||
tooltip="Redo"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faFloppyDisk}
|
||||
buttonProps={buttonProps.SAVE}
|
||||
tooltip="Save"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faDownload}
|
||||
buttonProps={buttonProps.DOWNLOAD}
|
||||
tooltip="Download"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,10 @@
|
||||
export enum ToolbarMainButtonNames {
|
||||
MENU = "MENU",
|
||||
SELECT = "SELECT",
|
||||
CHANGE_CAMERA_ORIENTATION = "CHANGE_CAMERA_ORIENTATION",
|
||||
ADD_TEXT = "ADD_TEXT",
|
||||
ADD_IMAGE = "ADD_IMAGE",
|
||||
ADD_VIDEO = "ADD_VIDEO",
|
||||
// CHANGE_CAMERA_ORIENTATION = "CHANGE_CAMERA_ORIENTATION",
|
||||
AI_STYLIZE = "AI_STYLIZE",
|
||||
UNDO = "UNDO",
|
||||
REDO = "REDO",
|
||||
|
||||
@@ -1,129 +1 @@
|
||||
import { Fragment, MouseEventHandler } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faArrowRotateLeft,
|
||||
faArrowRotateRight,
|
||||
faBars,
|
||||
faCameraRotate,
|
||||
faDownload,
|
||||
faFilePlus,
|
||||
faFilm,
|
||||
faFloppyDisk,
|
||||
faHatWizard,
|
||||
faImage,
|
||||
faLocationArrow,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
|
||||
import { ToolbarButton } from "../ToolbarButton";
|
||||
|
||||
// style and constants
|
||||
import { paperWrapperStyles, toolTipStyles } from "~/components/styles";
|
||||
import { ToolbarMainButtonNames } from "./enum";
|
||||
|
||||
export const ToolbarMain = ({
|
||||
disabled = false,
|
||||
buttonProps,
|
||||
openAddImage,
|
||||
openAddVideo,
|
||||
openAIStylize,
|
||||
}: {
|
||||
disabled?: boolean;
|
||||
buttonProps: {
|
||||
[key in ToolbarMainButtonNames]: {
|
||||
disabled?: boolean;
|
||||
active?: boolean;
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
};
|
||||
};
|
||||
openAddImage: () => void;
|
||||
openAddVideo: () => void;
|
||||
openAIStylize: () => void;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex w-fit items-center divide-x divide-ui-border",
|
||||
paperWrapperStyles,
|
||||
disabled &&
|
||||
"pointer-events-none cursor-default bg-ui-border shadow-md",
|
||||
)}
|
||||
>
|
||||
<div className="pl-1 pr-2">
|
||||
<ToolbarButton icon={faBars} buttonProps={buttonProps.MENU} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButton
|
||||
icon={faLocationArrow}
|
||||
iconProps={{ className: "fa-flip-horizontal" }}
|
||||
buttonProps={buttonProps.SELECT}
|
||||
tooltip="Select"
|
||||
/>
|
||||
<Popover className="relative">
|
||||
<PopoverButton as={Fragment}>
|
||||
<button
|
||||
data-tooltip="Add..."
|
||||
className={twMerge(
|
||||
"size-10 rounded-2xl p-2 hover:bg-secondary-500 hover:text-white",
|
||||
toolTipStyles,
|
||||
)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faFilePlus} />
|
||||
</button>
|
||||
</PopoverButton>
|
||||
<PopoverPanel
|
||||
anchor="bottom"
|
||||
className={twMerge(
|
||||
// "absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2",
|
||||
"flex flex-col [--anchor-gap:16px]",
|
||||
paperWrapperStyles,
|
||||
)}
|
||||
>
|
||||
<ToolbarButton icon={faImage} onClick={openAddImage}>
|
||||
Add Image
|
||||
</ToolbarButton>
|
||||
<ToolbarButton icon={faFilm} onClick={openAddVideo}>
|
||||
Add Video
|
||||
</ToolbarButton>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
<ToolbarButton
|
||||
icon={faCameraRotate}
|
||||
buttonProps={buttonProps.CHANGE_CAMERA_ORIENTATION}
|
||||
tooltip="Change Orientation"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faHatWizard}
|
||||
buttonProps={buttonProps.AI_STYLIZE}
|
||||
onClick={openAIStylize}
|
||||
tooltip="AI Stylize"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 px-2">
|
||||
<ToolbarButton
|
||||
icon={faArrowRotateLeft}
|
||||
buttonProps={buttonProps.UNDO}
|
||||
tooltip="Undo"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faArrowRotateRight}
|
||||
buttonProps={buttonProps.REDO}
|
||||
tooltip="Redo"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faFloppyDisk}
|
||||
buttonProps={buttonProps.SAVE}
|
||||
tooltip="Save"
|
||||
/>
|
||||
<ToolbarButton
|
||||
icon={faDownload}
|
||||
buttonProps={buttonProps.DOWNLOAD}
|
||||
tooltip="Download"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export { ToolbarMain } from "./ToolbarMain";
|
||||
@@ -3,6 +3,7 @@ export { DialogAddImage } from "./DialogAddImage";
|
||||
export { DialogAddVideo } from "./DialogAddVideo";
|
||||
export { DialogAiStylize } from "./DialogAiStylize";
|
||||
export { DialogChromakey } from "./DialogChromakey";
|
||||
export { DialogEditText } from "./DialogEditText";
|
||||
export { DialogError } from "./DialogError";
|
||||
export { ProfileDropdown } from "./ToolbarUserProfile/ProfileDropdown";
|
||||
export { ToolbarMain } from "./ToolbarMain";
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useState } from "react";
|
||||
import { faChevronDown } from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
Combobox as HeadlessComboBox,
|
||||
ComboboxButton,
|
||||
ComboboxInput,
|
||||
ComboboxOption,
|
||||
ComboboxOptions,
|
||||
} from "@headlessui/react";
|
||||
|
||||
interface ComboBoxInterface {
|
||||
options: string[];
|
||||
value: string;
|
||||
onChange: (newVal: string) => void;
|
||||
}
|
||||
export const Combobox = ({ options, value, onChange }: ComboBoxInterface) => {
|
||||
const [query, setQuery] = useState("");
|
||||
const filteredOptions =
|
||||
query === ""
|
||||
? options
|
||||
: options.filter((option) => {
|
||||
return option.toLowerCase().includes(option.toLowerCase());
|
||||
});
|
||||
|
||||
return (
|
||||
<HeadlessComboBox
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onClose={() => setQuery("")}
|
||||
>
|
||||
<div className="flex">
|
||||
<ComboboxInput
|
||||
aria-label="Assignee"
|
||||
displayValue={(font: string) => font}
|
||||
onChange={(event) => setQuery(event.target.value)}
|
||||
/>
|
||||
<ComboboxButton className="">
|
||||
<FontAwesomeIcon
|
||||
icon={faChevronDown}
|
||||
className="size-4 fill-black/60 group-data-[hover]:fill-black"
|
||||
/>
|
||||
</ComboboxButton>
|
||||
</div>
|
||||
<ComboboxOptions anchor="bottom" className="border empty:invisible">
|
||||
{filteredOptions.map((option, idx) => (
|
||||
<ComboboxOption
|
||||
key={idx}
|
||||
value={option}
|
||||
className="data-[focus]:bg-blue-100"
|
||||
>
|
||||
{option}
|
||||
</ComboboxOption>
|
||||
))}
|
||||
</ComboboxOptions>
|
||||
</HeadlessComboBox>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { TextareaHTMLAttributes } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { kebabCase } from "~/utilities";
|
||||
|
||||
type ResizeType =
|
||||
export type ResizeType =
|
||||
| "none"
|
||||
| "both"
|
||||
| "horizontal"
|
||||
@@ -11,16 +11,18 @@ type ResizeType =
|
||||
| "inline"
|
||||
| undefined;
|
||||
|
||||
export interface TextareaInterface
|
||||
extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
label?: string;
|
||||
resize?: ResizeType;
|
||||
}
|
||||
export const Textarea = ({
|
||||
className,
|
||||
label,
|
||||
resize = "vertical",
|
||||
id,
|
||||
...rest
|
||||
}: TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
||||
label?: string;
|
||||
resize?: ResizeType;
|
||||
}) => {
|
||||
}: TextareaInterface) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{label && <label htmlFor={id ? id : kebabCase(label)}>{label}</label>}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import {
|
||||
faBold,
|
||||
faItalic,
|
||||
faStrikethrough,
|
||||
faUnderline,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { buttonGroupButton, buttonGroupGrouper } from "./styles";
|
||||
import { FontStyle, FontWeight, TextDecoration } from "./types";
|
||||
export const ButtonsTextStyles = ({
|
||||
fontStyle: currStyle,
|
||||
fontWeight: currWeight,
|
||||
textDecoration: currDecoration,
|
||||
onChangeFontStyle,
|
||||
onChangeFontWeight,
|
||||
onChangeTextDecoration,
|
||||
}: {
|
||||
fontStyle: FontStyle;
|
||||
fontWeight: FontWeight;
|
||||
textDecoration: TextDecoration;
|
||||
onChangeFontStyle: (newVal: FontStyle) => void;
|
||||
onChangeFontWeight: (newVal: FontWeight) => void;
|
||||
onChangeTextDecoration: (newVal: TextDecoration) => void;
|
||||
}) => {
|
||||
const isBold = currWeight === FontWeight.BOLD ? true : undefined;
|
||||
const isItalic = currStyle === FontStyle.ITALIC ? true : undefined;
|
||||
const isSelectedDecoration = (buttonVal: TextDecoration) =>
|
||||
currDecoration === buttonVal ? true : undefined;
|
||||
|
||||
const toggleBold = () => {
|
||||
if (currWeight === FontWeight.BOLD) {
|
||||
onChangeFontWeight(FontWeight.NORMAL);
|
||||
} else {
|
||||
onChangeFontWeight(FontWeight.BOLD);
|
||||
}
|
||||
};
|
||||
const handleOnChangeFontStyle = (selected: FontStyle) => {
|
||||
if (selected === currStyle) {
|
||||
onChangeFontStyle(FontStyle.NORMAL);
|
||||
} else {
|
||||
onChangeFontStyle(selected);
|
||||
}
|
||||
};
|
||||
const handleOnChangeTextDecoration = (selected: TextDecoration) => {
|
||||
if (selected === currDecoration) {
|
||||
onChangeTextDecoration(TextDecoration.NONE);
|
||||
} else {
|
||||
onChangeTextDecoration(selected);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={buttonGroupGrouper}>
|
||||
<button
|
||||
data-selected={isBold}
|
||||
className={buttonGroupButton}
|
||||
onClick={toggleBold}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBold} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isItalic}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => handleOnChangeFontStyle(FontStyle.ITALIC)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faItalic} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isSelectedDecoration(TextDecoration.STRIKETHROUGH)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() =>
|
||||
handleOnChangeTextDecoration(TextDecoration.STRIKETHROUGH)
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faStrikethrough} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isSelectedDecoration(TextDecoration.UNDERLINE)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => handleOnChangeTextDecoration(TextDecoration.UNDERLINE)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faUnderline} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
faAlignCenter,
|
||||
faAlignJustify,
|
||||
faAlignLeft,
|
||||
faAlignRight,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { TextAlign } from "./types";
|
||||
import { buttonGroupButton, buttonGroupGrouper } from "./styles";
|
||||
export const ButtonsAlignments = ({
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
value: TextAlign;
|
||||
onChange: (newVal: TextAlign) => void;
|
||||
}) => {
|
||||
const isSelected = (alignment: TextAlign) =>
|
||||
value === alignment ? true : undefined;
|
||||
return (
|
||||
<div className={buttonGroupGrouper}>
|
||||
<button
|
||||
data-selected={isSelected(TextAlign.LEFT)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => onChange(TextAlign.LEFT)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAlignLeft} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isSelected(TextAlign.CENTER)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => onChange(TextAlign.CENTER)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAlignCenter} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isSelected(TextAlign.RIGHT)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => onChange(TextAlign.RIGHT)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAlignRight} />
|
||||
</button>
|
||||
<button
|
||||
data-selected={isSelected(TextAlign.JUSTIFY)}
|
||||
className={buttonGroupButton}
|
||||
onClick={() => onChange(TextAlign.JUSTIFY)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faAlignJustify} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
import { ChangeEvent } from "react";
|
||||
import { faFont } from "@fortawesome/pro-solid-svg-icons";
|
||||
import {
|
||||
CloseButton,
|
||||
Popover,
|
||||
PopoverButton,
|
||||
PopoverPanel,
|
||||
} from "@headlessui/react";
|
||||
import { HexColorPicker } from "react-colorful";
|
||||
|
||||
import { Textarea, TextareaInterface, ResizeType } from "../TextArea";
|
||||
import { InputNumber } from "../InputNumber";
|
||||
import { Combobox } from "../ComboBox";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { ButtonsAlignments } from "./ButtonsAlignment";
|
||||
import { ButtonsTextStyles } from "./ButtonTextStyles";
|
||||
|
||||
import {
|
||||
TextFormatData,
|
||||
TextAlign,
|
||||
TextDecoration,
|
||||
FontStyle,
|
||||
FontWeight,
|
||||
} from "./types";
|
||||
|
||||
const FontFamilies = ["Arial", "Helvetica", "Times New Roman", "Comic Sans MS"];
|
||||
|
||||
export const TextEditor = ({
|
||||
text,
|
||||
formatData,
|
||||
onChangeText,
|
||||
onChangeFormatting,
|
||||
TextareaProps,
|
||||
}: {
|
||||
text: string;
|
||||
formatData: TextFormatData;
|
||||
onChangeText: (newText: string) => void;
|
||||
onChangeFormatting: (newFormatData: Partial<TextFormatData>) => void;
|
||||
TextareaProps?: TextareaInterface;
|
||||
}) => {
|
||||
const handleOnChangeText = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
onChangeText(e.target.value);
|
||||
};
|
||||
const onChangeTextAlignment = (newAlignment: TextAlign) => {
|
||||
onChangeFormatting({
|
||||
textAlign: newAlignment,
|
||||
});
|
||||
};
|
||||
const onChangeFontStyle = (newStyle: FontStyle) => {
|
||||
onChangeFormatting({
|
||||
fontStyle: newStyle,
|
||||
});
|
||||
};
|
||||
const onChangeFontWeight = (newWeight: FontWeight) => {
|
||||
onChangeFormatting({
|
||||
fontWeight: newWeight,
|
||||
});
|
||||
};
|
||||
const onChangeTextDecoration = (newDecor: TextDecoration) => {
|
||||
onChangeFormatting({
|
||||
textDecoration: newDecor,
|
||||
});
|
||||
};
|
||||
const onChangeFontFamily = (newFontFamily: string) => {
|
||||
onChangeFormatting({
|
||||
fontFamily: newFontFamily,
|
||||
});
|
||||
};
|
||||
const onChangeTextColor = (newTextColor: string) => {
|
||||
onChangeFormatting({
|
||||
color: newTextColor,
|
||||
});
|
||||
};
|
||||
const onChangeFontSize = (newVal: number) => {
|
||||
onChangeFormatting({
|
||||
fontSize: newVal,
|
||||
});
|
||||
};
|
||||
|
||||
const TextAreaStates = {
|
||||
style: {
|
||||
...formatData,
|
||||
width: "500px",
|
||||
},
|
||||
rows: 7,
|
||||
placeholder: "...",
|
||||
resize: "none" as ResizeType,
|
||||
onChange: handleOnChangeText,
|
||||
value: text,
|
||||
};
|
||||
const unionTextAreaProps = {
|
||||
...TextareaProps,
|
||||
...TextAreaStates,
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Combobox
|
||||
options={FontFamilies}
|
||||
value={formatData.fontFamily}
|
||||
onChange={onChangeFontFamily}
|
||||
/>
|
||||
<Popover className="relative">
|
||||
<PopoverButton className="flex size-10 flex-col items-center gap-1 rounded-md border p-2">
|
||||
<FontAwesomeIcon icon={faFont} />
|
||||
<span
|
||||
className="h-1 w-full"
|
||||
style={{ backgroundColor: formatData.color }}
|
||||
/>
|
||||
</PopoverButton>
|
||||
<PopoverPanel anchor="bottom" className="flex flex-col">
|
||||
<HexColorPicker
|
||||
color={formatData.color}
|
||||
onChange={onChangeTextColor}
|
||||
/>
|
||||
<p>{formatData.color}</p>
|
||||
<CloseButton>OK</CloseButton>
|
||||
</PopoverPanel>
|
||||
</Popover>
|
||||
<InputNumber value={formatData.fontSize} onChange={onChangeFontSize} />
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<ButtonsAlignments
|
||||
value={formatData.textAlign}
|
||||
onChange={onChangeTextAlignment}
|
||||
/>
|
||||
<ButtonsTextStyles
|
||||
fontWeight={formatData.fontWeight}
|
||||
fontStyle={formatData.fontStyle}
|
||||
textDecoration={formatData.textDecoration}
|
||||
onChangeFontWeight={onChangeFontWeight}
|
||||
onChangeFontStyle={onChangeFontStyle}
|
||||
onChangeTextDecoration={onChangeTextDecoration}
|
||||
/>
|
||||
</div>
|
||||
<Textarea {...unionTextAreaProps} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { TextEditor } from "./TextEditor";
|
||||
@@ -0,0 +1,4 @@
|
||||
export const buttonGroupButton =
|
||||
"size-10 rounded-none bg-white text-black hover:bg-gray-200 data-[selected=true]:text-primary-400";
|
||||
export const buttonGroupGrouper =
|
||||
"flex divide-x overflow-hidden rounded-md border";
|
||||
@@ -0,0 +1,28 @@
|
||||
export enum FontWeight {
|
||||
NORMAL = "normal",
|
||||
BOLD = "bold",
|
||||
}
|
||||
export enum FontStyle {
|
||||
NORMAL = "normal",
|
||||
ITALIC = "italic",
|
||||
}
|
||||
export enum TextAlign {
|
||||
LEFT = "left",
|
||||
RIGHT = "right",
|
||||
CENTER = "center",
|
||||
JUSTIFY = "justify",
|
||||
}
|
||||
export enum TextDecoration {
|
||||
NONE = "none",
|
||||
STRIKETHROUGH = "line-through",
|
||||
UNDERLINE = "underline",
|
||||
}
|
||||
export type TextFormatData = {
|
||||
color: string;
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
fontStyle: FontStyle;
|
||||
fontWeight: FontWeight;
|
||||
textAlign: TextAlign;
|
||||
textDecoration: TextDecoration;
|
||||
};
|
||||
@@ -9,4 +9,5 @@ export * from "./Slider";
|
||||
export * from "./Spinner";
|
||||
export * from "./Switches";
|
||||
export * from "./TextArea";
|
||||
export * from "./TextEditor";
|
||||
export * from "./Tooltip";
|
||||
@@ -47,32 +47,21 @@ export const toolbarNode = {
|
||||
position,
|
||||
};
|
||||
},
|
||||
lock() {
|
||||
const { buttonStates } = toolbarNodeSignal.value;
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: {
|
||||
...buttonStates,
|
||||
[ButtonNames.TRANSFORM]: {
|
||||
disabled: true,
|
||||
active: buttonStates[ButtonNames.TRANSFORM].active,
|
||||
},
|
||||
},
|
||||
locked: true,
|
||||
};
|
||||
isLocked() {
|
||||
return toolbarNodeSignal.value.locked;
|
||||
},
|
||||
unlock() {
|
||||
setLocked(locked: boolean) {
|
||||
const { buttonStates } = toolbarNodeSignal.value;
|
||||
toolbarNodeSignal.value = {
|
||||
...toolbarNodeSignal.value,
|
||||
buttonStates: {
|
||||
...buttonStates,
|
||||
[ButtonNames.TRANSFORM]: {
|
||||
disabled: false,
|
||||
disabled: locked,
|
||||
active: buttonStates[ButtonNames.TRANSFORM].active,
|
||||
},
|
||||
},
|
||||
locked: false,
|
||||
locked: locked,
|
||||
};
|
||||
},
|
||||
show(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { signal, effect } from "@preact/signals-react";
|
||||
import { TextNodeData } from "~/KonvaApp/types";
|
||||
|
||||
// ADDING IMAGES
|
||||
const stagedImage = signal<File | null>(null);
|
||||
|
||||
const addImageToEngine = (image: File) => {
|
||||
stagedImage.value = image;
|
||||
};
|
||||
|
||||
const onGetStagedImage = (callback: (file: File) => void) => {
|
||||
effect(() => {
|
||||
if (stagedImage.value) {
|
||||
@@ -14,6 +14,7 @@ const onGetStagedImage = (callback: (file: File) => void) => {
|
||||
});
|
||||
};
|
||||
|
||||
// ADDING VIDEOS
|
||||
const stagedVideo = signal<{ url: string } | null>(null);
|
||||
|
||||
const addVideoToEngine = (videoData: { url: string }) => {
|
||||
@@ -28,12 +29,28 @@ const onGetStagedVideo = (callback: (videoData: { url: string }) => void) => {
|
||||
});
|
||||
};
|
||||
|
||||
// ADD and EDIT TEXT
|
||||
const stagedText = signal<TextNodeData | null>(null);
|
||||
const addTextToEngine = (data: TextNodeData) => {
|
||||
stagedText.value = data;
|
||||
};
|
||||
const onAddTextToEngine = (callback: (data: TextNodeData) => void) => {
|
||||
effect(() => {
|
||||
if (stagedText.value) {
|
||||
callback(stagedText.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//EXPORTS
|
||||
export const dispatchers = {
|
||||
addImageToEngine,
|
||||
addVideoToEngine,
|
||||
addTextToEngine,
|
||||
};
|
||||
|
||||
export const events = {
|
||||
onGetStagedImage,
|
||||
onGetStagedVideo,
|
||||
onAddTextToEngine,
|
||||
};
|
||||
@@ -28,7 +28,7 @@
|
||||
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz"
|
||||
integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==
|
||||
|
||||
"@babel/core@^7.24.5":
|
||||
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.24.5":
|
||||
version "7.25.2"
|
||||
resolved "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz"
|
||||
integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==
|
||||
@@ -222,121 +222,6 @@
|
||||
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
|
||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||
|
||||
"@esbuild/aix-ppc64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f"
|
||||
integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==
|
||||
|
||||
"@esbuild/android-arm64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052"
|
||||
integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==
|
||||
|
||||
"@esbuild/android-arm@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28"
|
||||
integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==
|
||||
|
||||
"@esbuild/android-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e"
|
||||
integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==
|
||||
|
||||
"@esbuild/darwin-arm64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a"
|
||||
integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==
|
||||
|
||||
"@esbuild/darwin-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22"
|
||||
integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==
|
||||
|
||||
"@esbuild/freebsd-arm64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e"
|
||||
integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==
|
||||
|
||||
"@esbuild/freebsd-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261"
|
||||
integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==
|
||||
|
||||
"@esbuild/linux-arm64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b"
|
||||
integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==
|
||||
|
||||
"@esbuild/linux-arm@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9"
|
||||
integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==
|
||||
|
||||
"@esbuild/linux-ia32@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2"
|
||||
integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==
|
||||
|
||||
"@esbuild/linux-loong64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df"
|
||||
integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==
|
||||
|
||||
"@esbuild/linux-mips64el@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe"
|
||||
integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==
|
||||
|
||||
"@esbuild/linux-ppc64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4"
|
||||
integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==
|
||||
|
||||
"@esbuild/linux-riscv64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc"
|
||||
integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==
|
||||
|
||||
"@esbuild/linux-s390x@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de"
|
||||
integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==
|
||||
|
||||
"@esbuild/linux-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz"
|
||||
integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
|
||||
|
||||
"@esbuild/netbsd-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047"
|
||||
integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==
|
||||
|
||||
"@esbuild/openbsd-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70"
|
||||
integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==
|
||||
|
||||
"@esbuild/sunos-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b"
|
||||
integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==
|
||||
|
||||
"@esbuild/win32-arm64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d"
|
||||
integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==
|
||||
|
||||
"@esbuild/win32-ia32@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b"
|
||||
integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==
|
||||
|
||||
"@esbuild/win32-x64@0.21.5":
|
||||
version "0.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c"
|
||||
integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
|
||||
@@ -373,7 +258,7 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@9.9.1", "@eslint/js@^9.9.0":
|
||||
"@eslint/js@^9.9.0", "@eslint/js@9.9.1":
|
||||
version "9.9.1"
|
||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz"
|
||||
integrity sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==
|
||||
@@ -424,7 +309,7 @@
|
||||
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-common-types/-/6.6.0/fontawesome-common-types-6.6.0.tgz"
|
||||
integrity sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==
|
||||
|
||||
"@fortawesome/fontawesome-svg-core@^6.6.0":
|
||||
"@fortawesome/fontawesome-svg-core@^6.6.0", "@fortawesome/fontawesome-svg-core@~1 || ~6":
|
||||
version "6.6.0"
|
||||
resolved "https://npm.fontawesome.com/@fortawesome/fontawesome-svg-core/-/6.6.0/fontawesome-svg-core-6.6.0.tgz"
|
||||
integrity sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==
|
||||
@@ -522,7 +407,7 @@
|
||||
"@nodelib/fs.stat" "2.0.5"
|
||||
run-parallel "^1.1.9"
|
||||
|
||||
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
|
||||
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
@@ -844,86 +729,6 @@
|
||||
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz"
|
||||
integrity sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz#c3a7938551273a2b72820cf5d22e54cf41dc206e"
|
||||
integrity sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz#fa3693e4674027702c42fcbbb86bbd0c635fd3b9"
|
||||
integrity sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz#e19922f4ac1e4552a230ff8f49d5688c5c07d284"
|
||||
integrity sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz#897f8d47b115ea84692a29cf2366899499d4d915"
|
||||
integrity sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz#7d1e2a542f3a5744f5c24320067bd5af99ec9d62"
|
||||
integrity sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz#88bec1c9df85fc5e24d49f783e19934717dd69b5"
|
||||
integrity sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz#6dc60f0fe7bd49ed07a2d4d9eab15e671b3bd59d"
|
||||
integrity sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz#a03b78775c129e8333aca9e1e420e8e217ee99b9"
|
||||
integrity sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz#ee3810647faf2c105a5a4e71260bb90b96bf87bc"
|
||||
integrity sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz#385d76a088c27db8054d9f3f28d64d89294f838e"
|
||||
integrity sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz#daa2b62a6e6f737ebef6700a12a93c9764e18583"
|
||||
integrity sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz"
|
||||
integrity sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz"
|
||||
integrity sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz#18349db8250559a5460d59eb3575f9781be4ab98"
|
||||
integrity sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz#199648b68271f7ab9d023f5c077725d51d12d466"
|
||||
integrity sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.21.1":
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz#4d3ec02dbf280c20bfeac7e50cd5669b66f9108f"
|
||||
integrity sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==
|
||||
|
||||
"@sigstore/bundle@^2.3.2":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz"
|
||||
@@ -1050,7 +855,7 @@
|
||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
|
||||
"@types/node@>=13.7.0":
|
||||
"@types/node@^18.0.0 || >=20.0.0", "@types/node@>=13.7.0":
|
||||
version "22.5.5"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz"
|
||||
integrity sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==
|
||||
@@ -1097,7 +902,7 @@
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/parser@8.3.0":
|
||||
"@typescript-eslint/parser@^8.0.0 || ^8.0.0-alpha.0", "@typescript-eslint/parser@8.3.0":
|
||||
version "8.3.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz"
|
||||
integrity sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==
|
||||
@@ -1189,7 +994,7 @@ acorn-jsx@^5.3.2:
|
||||
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.12.0:
|
||||
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.12.0:
|
||||
version "8.12.1"
|
||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
@@ -1236,7 +1041,14 @@ ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
ansi-styles@^4.0.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
|
||||
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
|
||||
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||
@@ -1403,7 +1215,12 @@ bin-links@^4.0.4:
|
||||
read-cmd-shim "^4.0.0"
|
||||
write-file-atomic "^5.0.0"
|
||||
|
||||
binary-extensions@^2.0.0, binary-extensions@^2.3.0:
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||
|
||||
binary-extensions@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
||||
integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
|
||||
@@ -1430,7 +1247,7 @@ braces@^3.0.3, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.23.1, browserslist@^4.23.3:
|
||||
browserslist@^4.23.1, browserslist@^4.23.3, "browserslist@>= 4.21.0":
|
||||
version "4.23.3"
|
||||
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz"
|
||||
integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
|
||||
@@ -1580,16 +1397,16 @@ color-convert@^2.0.1:
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
commander@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz"
|
||||
@@ -1670,7 +1487,7 @@ data-view-byte-offset@^1.0.0:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4:
|
||||
version "4.3.6"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz"
|
||||
integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
|
||||
@@ -2000,7 +1817,12 @@ eslint-scope@^8.0.2:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^5.2.0"
|
||||
|
||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
|
||||
eslint-visitor-keys@^3.3.0:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
|
||||
eslint-visitor-keys@^3.4.3:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
@@ -2010,7 +1832,7 @@ eslint-visitor-keys@^4.0.0:
|
||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz"
|
||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
||||
|
||||
eslint@^9.9.0:
|
||||
"eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^8.57.0 || ^9.0.0", eslint@^9.9.0, eslint@>=7:
|
||||
version "9.9.1"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz"
|
||||
integrity sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==
|
||||
@@ -2188,7 +2010,7 @@ fraction.js@^4.3.7:
|
||||
|
||||
fs-extra@^11.1.0:
|
||||
version "11.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
|
||||
resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz"
|
||||
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
@@ -2209,11 +2031,6 @@ fs-minipass@^3.0.0, fs-minipass@^3.0.3:
|
||||
dependencies:
|
||||
minipass "^7.0.3"
|
||||
|
||||
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
|
||||
@@ -2259,7 +2076,7 @@ get-symbol-description@^1.0.2:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.4"
|
||||
|
||||
glob-parent@^5.1.2, glob-parent@~5.1.2:
|
||||
glob-parent@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
@@ -2273,6 +2090,13 @@ glob-parent@^6.0.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@^10.2.2, glob@^10.3.10, glob@^10.4.5:
|
||||
version "10.4.5"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz"
|
||||
@@ -2320,7 +2144,12 @@ gopd@^1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.6:
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
|
||||
graceful-fs@^4.2.11, graceful-fs@^4.2.6:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
@@ -2735,7 +2564,7 @@ jackspeak@^3.1.2:
|
||||
optionalDependencies:
|
||||
"@pkgjs/parseargs" "^0.11.0"
|
||||
|
||||
jiti@^1.21.0:
|
||||
jiti@*, jiti@^1.21.0:
|
||||
version "1.21.6"
|
||||
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz"
|
||||
integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==
|
||||
@@ -2794,7 +2623,7 @@ json5@^2.2.3:
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
|
||||
resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
|
||||
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
|
||||
dependencies:
|
||||
universalify "^2.0.0"
|
||||
@@ -3104,16 +2933,16 @@ minipass@^3.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
minipass@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz"
|
||||
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
|
||||
|
||||
"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.1, minipass@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz"
|
||||
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
|
||||
|
||||
minipass@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz"
|
||||
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
|
||||
|
||||
minizlib@^2.1.1, minizlib@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz"
|
||||
@@ -3127,16 +2956,16 @@ mkdirp@^1.0.3:
|
||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@^2.1.2:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
mute-stream@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz"
|
||||
@@ -3614,7 +3443,15 @@ postcss-nested@^6.0.1:
|
||||
dependencies:
|
||||
postcss-selector-parser "^6.1.1"
|
||||
|
||||
postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
|
||||
postcss-selector-parser@^6.0.10:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
dependencies:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.1.1:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
@@ -3627,7 +3464,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^
|
||||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.4.23, postcss@^8.4.41:
|
||||
postcss@^8.0.0, postcss@^8.1.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.41, postcss@>=8.0.9:
|
||||
version "8.4.41"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz"
|
||||
integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==
|
||||
@@ -3646,7 +3483,7 @@ prettier-plugin-tailwindcss@^0.6.6:
|
||||
resolved "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.6.tgz"
|
||||
integrity sha512-OPva5S7WAsPLEsOuOWXATi13QrCKACCiIonFgIR6V4lYv4QLp++UXVhZSzRbZxXGimkQtQT86CC6fQqTOybGng==
|
||||
|
||||
prettier@^3.3.3:
|
||||
prettier@^3.0, prettier@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz"
|
||||
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
|
||||
@@ -3733,7 +3570,12 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
react-dom@^18.3.1:
|
||||
react-colorful@^5.6.1:
|
||||
version "5.6.1"
|
||||
resolved "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz"
|
||||
integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==
|
||||
|
||||
"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18, react-dom@^18.0.0, react-dom@^18.3.1, "react-dom@>= 16.8.0", react-dom@>=16.4.0, react-dom@>=16.8, react-dom@>=16.8.0:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
|
||||
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
|
||||
@@ -3757,7 +3599,7 @@ react-easy-crop@^5.0.8:
|
||||
normalize-wheel "^1.0.1"
|
||||
tslib "^2.0.1"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
react-is@^16.13.1, react-is@^16.7.0, "react-is@>= 16.8.0":
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
@@ -3782,7 +3624,7 @@ react-router@6.26.1:
|
||||
dependencies:
|
||||
"@remix-run/router" "1.19.1"
|
||||
|
||||
react@^18.3.1:
|
||||
"react@^16.14.0 || 17.x || 18.x", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0", react@^18, react@^18.0.0, react@^18.3.1, "react@>= 16.8.0", react@>=16.3, react@>=16.4.0, react@>=16.8, react@>=16.8.0:
|
||||
version "18.3.1"
|
||||
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
|
||||
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
|
||||
@@ -3851,7 +3693,16 @@ resolve-from@^4.0.0:
|
||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
|
||||
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
|
||||
|
||||
resolve@^1.1.7, resolve@^1.22.2:
|
||||
resolve@^1.1.7:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
dependencies:
|
||||
is-core-module "^2.13.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^1.22.2:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
@@ -3947,7 +3798,12 @@ semver@^6.3.1:
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3:
|
||||
semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
semver@^7.6.0:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
@@ -4075,9 +3931,7 @@ spdx-expression-parse@^4.0.0:
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
||||
spdx-license-ids@^3.0.0:
|
||||
version "3.0.20"
|
||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89"
|
||||
integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==
|
||||
version "3.0.18"
|
||||
|
||||
sprintf-js@^1.1.3:
|
||||
version "1.1.3"
|
||||
@@ -4205,7 +4059,7 @@ strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
styled-components@^5.3.0:
|
||||
styled-components@^5.3.0, "styled-components@>= 2":
|
||||
version "5.3.11"
|
||||
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz"
|
||||
integrity sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==
|
||||
@@ -4308,7 +4162,12 @@ tar@^6.1.11, tar@^6.2.1:
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
text-table@^0.2.0, text-table@~0.2.0:
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
|
||||
text-table@~0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
@@ -4438,7 +4297,7 @@ typescript-eslint@^8.0.1:
|
||||
"@typescript-eslint/parser" "8.3.0"
|
||||
"@typescript-eslint/utils" "8.3.0"
|
||||
|
||||
typescript@^5.5.3:
|
||||
typescript@^5.0.0, typescript@^5.5.3, typescript@>=4.2.0:
|
||||
version "5.5.4"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz"
|
||||
integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
|
||||
@@ -4474,7 +4333,7 @@ unique-slug@^4.0.0:
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz"
|
||||
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
|
||||
|
||||
update-browserslist-db@^1.1.0:
|
||||
@@ -4522,7 +4381,7 @@ validate-npm-package-name@^5.0.0, validate-npm-package-name@^5.0.1:
|
||||
|
||||
vite-plugin-static-copy@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.6.tgz#ff457c16e8cfa564472aafd1698790ac89d05508"
|
||||
resolved "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.6.tgz"
|
||||
integrity sha512-3uSvsMwDVFZRitqoWHj0t4137Kz7UynnJeq1EZlRW7e25h2068fyIZX4ORCCOAkfp1FklGxJNVJBkBOD+PZIew==
|
||||
dependencies:
|
||||
chokidar "^3.5.3"
|
||||
@@ -4539,7 +4398,7 @@ vite-tsconfig-paths@^5.0.1:
|
||||
globrex "^0.1.2"
|
||||
tsconfck "^3.0.3"
|
||||
|
||||
vite@^5.4.1:
|
||||
vite@*, "vite@^4.2.0 || ^5.0.0", vite@^5.0.0, vite@^5.4.1:
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz"
|
||||
integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
|
||||
@@ -4673,4 +4532,4 @@ yarn@^1.22.22:
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
Reference in new issue
Block a user