solve library insert issue. also added mute functionality. now working on draggable.

This commit is contained in:
Jordan 2024-03-02 09:13:21 -08:00
parent bef7b8e2cf
commit 6da57d63bb
10 changed files with 130 additions and 35 deletions

View File

@ -28,6 +28,7 @@
"react-dropzone": "^14.2.3", "react-dropzone": "^14.2.3",
"react-redux": "^9.1.0", "react-redux": "^9.1.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sortable": "^2.0.0",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"vite": "^5.1.4", "vite": "^5.1.4",

View File

@ -83,6 +83,9 @@ dependencies:
react-scripts: react-scripts:
specifier: 5.0.1 specifier: 5.0.1
version: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.56.0)(react@18.2.0)(typescript@4.9.5) version: 5.0.1(@babel/plugin-syntax-flow@7.23.3)(@babel/plugin-transform-react-jsx@7.23.4)(eslint@8.56.0)(react@18.2.0)(typescript@4.9.5)
sortable:
specifier: ^2.0.0
version: 2.0.0
typescript: typescript:
specifier: ^4.9.5 specifier: ^4.9.5
version: 4.9.5 version: 4.9.5
@ -4930,6 +4933,10 @@ packages:
resolve: 1.1.7 resolve: 1.1.7
dev: true dev: true
/browser-split@0.0.0:
resolution: {integrity: sha512-CNXO3AXAS1H/kOGQkPjucm1161/XoF3aVkMfujqwk85XN/D/MkQMvoB81lXyX/2rerZS+hPAYYRR3mAW05awjQ==}
dev: false
/browserslist@4.23.0: /browserslist@4.23.0:
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==} resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@ -5107,6 +5114,12 @@ packages:
resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==}
dev: false dev: false
/class-list@0.1.1:
resolution: {integrity: sha512-zqR0uW+VsLtyQhixBhkdQ+z6B8+Y8HTh28kdSVjJ4zTTKM7Xz2asAQSya9VI6m/34F6N6Ktm0mrchKB+E5a8Xw==}
dependencies:
indexof: 0.0.1
dev: false
/class-utils@0.3.6: /class-utils@0.3.6:
resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -7511,6 +7524,13 @@ packages:
engines: {node: '>=10.17.0'} engines: {node: '>=10.17.0'}
dev: false dev: false
/hyperscript@1.0.7:
resolution: {integrity: sha512-dyfX683lwCsXiVUnmfnO6xji30exAUtr2yWWfCDz6FXjD+qNXwGsBKgSfFTEKNg+MArVI25ZdadfqBgsA32NMw==}
dependencies:
browser-split: 0.0.0
class-list: 0.1.1
dev: false
/hyphenate-style-name@1.0.4: /hyphenate-style-name@1.0.4:
resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==}
dev: false dev: false
@ -7586,6 +7606,10 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true dev: true
/indexof@0.0.1:
resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==}
dev: false
/inflight@1.0.6: /inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies: dependencies:
@ -9006,6 +9030,10 @@ packages:
hasBin: true hasBin: true
dev: false dev: false
/jquery-browserify@1.8.1:
resolution: {integrity: sha512-IDMCKuU5padhYWP21juFL10BOySPnlihoX7R1dHKeCcwl/JdeO3trDbimKQdPXtQsWIdYMwkAyxQ3+ksEj1iMQ==}
dev: false
/js-tokens@4.0.0: /js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -9861,6 +9889,10 @@ packages:
es-abstract: 1.22.4 es-abstract: 1.22.4
dev: false dev: false
/observable@1.3.1:
resolution: {integrity: sha512-n1QLn+I5eo/4TJxdrC54mHPYwDPvCZQ9FwwM2VE/jVkXf7aodqb0XImZnLbAIeSbnsWm1BzGcwjxeKktD/rb9g==}
dev: false
/obuf@1.1.2: /obuf@1.1.2:
resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
dev: false dev: false
@ -12024,6 +12056,14 @@ packages:
websocket-driver: 0.7.4 websocket-driver: 0.7.4
dev: false dev: false
/sortable@2.0.0:
resolution: {integrity: sha512-VB3IABlS7TQDd2sRE5zSqW6pPqDsuduyuJZ5vcuGpR9gPQKtFrh1Y3xUAPfxhJ6djF1HYZEfXSO9mFs2eldJ3w==}
dependencies:
hyperscript: 1.0.7
jquery-browserify: 1.8.1
observable: 1.3.1
dev: false
/source-list-map@2.0.1: /source-list-map@2.0.1:
resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
dev: false dev: false

View File

@ -2,12 +2,13 @@ import { Button, ButtonGroup, Chip, Divider } from '@material-ui/core';
import React, { Component, DragEvent, useEffect, useState } from 'react'; import React, { Component, DragEvent, useEffect, useState } from 'react';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowUp'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowUp';
import { $composition, Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore } from '../lib/prompt'; import { $composition, Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore, togglePromptItemMute } from '../lib/prompt';
import "./Nugget.css"; import "./Nugget.css";
import "./PromptItem.css" import "./PromptItem.css"
import { $sourceItem, cancelDrop, completeDrop, isPromptItemDropTarget, startDrag, startHoverOver } from '../store/prompt-dnd'; import { $sourceItem, cancelDrop, completeDrop, isPromptItemDropTarget, startDrag, startHoverOver } from '../store/prompt-dnd';
import { PromptItemProps } from './PromptItem'; import { PromptItemProps } from './PromptItem';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { VolumeMute, VolumeOff, VolumeUp } from '@mui/icons-material';
export interface NuggetProps extends PromptItemProps { export interface NuggetProps extends PromptItemProps {
nugget: NuggetType, nugget: NuggetType,
@ -28,7 +29,13 @@ export default function Nugget(props: NuggetProps) {
const composition = useStore($composition) const composition = useStore($composition)
const thisId = `prompt-item-${nugget.id}` const thisId = `prompt-item-${nugget.id}`
const className = isTopLevel ? 'nugget toplevel prompt-item' : 'nugget child prompt-item'; const className = [...(nugget.muted === true ? ["muted"] : []), ...[
isTopLevel ? "toplevel" : "child",
"nugget",
"prompt-item",
]].join(" ");
console.log("nugget classname: %s", className);
const handleOnDragStart = () => { const handleOnDragStart = () => {
onDragStart ? onDragStart(nugget) : null; onDragStart ? onDragStart(nugget) : null;
@ -89,6 +96,13 @@ export default function Nugget(props: NuggetProps) {
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</span> </span>
{isTopLevel &&
<span className='hide'>
<Button onClick={() => togglePromptItemMute(nugget.id)}>
{nugget.muted ? <VolumeUp /> : <VolumeOff /> }
</Button>
</span>
}
</div> </div>
); );
} }

View File

@ -1,13 +1,14 @@
import { Menu, MenuItem } from "@material-ui/core"; import { Button, Menu, MenuItem } from "@material-ui/core";
import React, { Children, DragEvent, ReactNode, useEffect } from 'react'; import React, { Children, DragEvent, ReactNode, useEffect } from 'react';
import "./Operation.css"; import "./Operation.css";
import { Op } from "../lib/operator"; import { Op } from "../lib/operator";
import { v4 as randomUUID } from "uuid"; import { v4 as randomUUID } from "uuid";
import { $composition, Operation as OperationType, changeOperationOp } from "../lib/prompt"; import { $composition, Operation as OperationType, changeOperationOp, togglePromptItemMute } from "../lib/prompt";
import Nugget from "./Nugget"; import Nugget from "./Nugget";
import { PromptItemProps } from "./PromptItem"; import { PromptItemProps } from "./PromptItem";
import { useStore } from "@nanostores/react"; import { useStore } from "@nanostores/react";
import { $sourceItem, cancelDrop, completeDrop, startHoverOver } from "../store/prompt-dnd"; import { $sourceItem, cancelDrop, completeDrop, startHoverOver } from "../store/prompt-dnd";
import { VolumeUp, VolumeOff } from "@mui/icons-material";
interface OperationProps extends PromptItemProps { interface OperationProps extends PromptItemProps {
operation: OperationType operation: OperationType
@ -84,6 +85,16 @@ function Operation(props: OperationProps) {
handleClose(); handleClose();
} }
const className = [...(operation.muted === true ? ["muted"] : []), ...[
"operation",
operation.op,
"prompt-item",
]].join(" ");
console.log("operation classname: %s", className);
return ( return (
<div <div
draggable draggable
@ -92,7 +103,7 @@ function Operation(props: OperationProps) {
onDragOver={handleOnDragOver} onDragOver={handleOnDragOver}
onMouseEnter={handleOnMouseEnter} onMouseEnter={handleOnMouseEnter}
onMouseOut={handleOnMouseLeave} onMouseOut={handleOnMouseLeave}
className={`operation ${operation.op} prompt-item`} className={className}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
data-promptitem-id={operation.id} data-promptitem-id={operation.id}
> >
@ -104,6 +115,11 @@ function Operation(props: OperationProps) {
}) })
} }
</div> </div>
<span className='hide'>
<Button onClick={() => togglePromptItemMute(operation.id)}>
{operation.muted ? <VolumeUp /> : <VolumeOff />}
</Button>
</span>
<Menu <Menu
open={contextMenu !== null} open={contextMenu !== null}
onClose={handleClose} onClose={handleClose}

View File

@ -18,7 +18,7 @@ export interface PromptComposerProps {
} }
export default function PromptComposer(props: PromptComposerProps) { export default function PromptComposer(props: PromptComposerProps) {
const [open, setOpen] = useState(true); const [open, setOpen] = useState(false);
const handleClickOpen = () => { const handleClickOpen = () => {
setOpen(true); setOpen(true);
@ -28,21 +28,16 @@ export default function PromptComposer(props: PromptComposerProps) {
setOpen(false); setOpen(false);
}; };
const composition = useStore($composition);
// const [composition, setComposition] = useState(compStore);
const handleOnInsertItem = (item: LibraryItem) => { const handleOnInsertItem = (item: LibraryItem) => {
console.log("INSERT %x INTO %s", item, composition)
insertIntoComposition(item); insertIntoComposition(item);
console.log(composition)
} }
// const composition = useStore($composition);
const [composition, setComposition] = useState($composition.get())
useEffect(() => {
$composition.subscribe((comp) => {
console.log("composition changed!");
setComposition(comp as Composition);
});
console.log("subscribe -- composition")
})
/** /**
* *
* @param promptItem The prompt item that we're rendering * @param promptItem The prompt item that we're rendering

View File

@ -5,3 +5,7 @@
.prompt-item .drag-target-hover { .prompt-item .drag-target-hover {
border: 1px solid blue; border: 1px solid blue;
} }
.prompt-item.muted, .operation.muted .nugget {
color: gray;
}

View File

@ -47,7 +47,7 @@ export function PromptLibrary(props: SimpleDialogProps) {
$e.stopPropagation(); $e.stopPropagation();
const libItem = library.find(l => l.id === params.id) as LibItemType; const libItem = library.find(l => l.id === params.id) as LibItemType;
console.log("Inserting %o into composition", libItem); console.log("Inserting %o into composition", libItem);
libItem ?? onInsertItem(libItem); if (libItem) onInsertItem(libItem);
} }
return ( return (
<Button onClick={handleClick}> <Button onClick={handleClick}>
@ -69,7 +69,14 @@ export function PromptLibrary(props: SimpleDialogProps) {
})); }));
return ( return (
<Dialog className="prompt-library-dialog" onClose={handleClose} open={open}> <Dialog
hideBackdrop
disableEnforceFocus
style={{ position: "initial", top: "30%", left: "30%", height: "fit-content", width: "fit-content" }}
className="prompt-library-dialog"
onClose={handleClose}
open={open}
>
<DialogTitle>Prompt Library</DialogTitle> <DialogTitle>Prompt Library</DialogTitle>
<div> <div>
<DataGrid rows={rows} columns={columns} /> <DataGrid rows={rows} columns={columns} />

View File

@ -1,7 +1,7 @@
export enum Op { export enum Op {
JOINED = "joined", JOINED = "joined",
AND = "and", AND = "and",
SWAPPED = "swaped", SWAPPED = "swapped",
SWAP = "swap", SWAP = "swap",
BLENDED = "blended", BLENDED = "blended",
BLEND = "blend", BLEND = "blend",

View File

@ -1,6 +1,6 @@
import { v4 as randomUUID, v4 as uuidv4 } from "uuid"; import { v4 as randomUUID, v4 as uuidv4 } from "uuid";
import { Op } from "./operator"; import { Op } from "./operator";
import { atom, computed } from "nanostores"; import { Atom, ReadableAtom, Store, WritableAtom, atom, computed } from "nanostores";
type Id = string; type Id = string;
@ -16,7 +16,7 @@ export enum Category {
medium = "medium", medium = "medium",
} }
export function categoryHasName(cat : Category) { export function categoryHasName(cat: Category) {
return (cat === Category.style || cat === Category.vibes) return (cat === Category.style || cat === Category.vibes)
} }
@ -35,12 +35,16 @@ export type LibraryItem = {
export type Library = Array<LibraryItem>; export type Library = Array<LibraryItem>;
export type Nugget = IdAble & { export type Muteable = {
muted?: boolean,
}
export type Nugget = IdAble & Muteable & {
item: LibraryItem, item: LibraryItem,
score: number, score: number,
} }
export type Operation = IdAble & { export type Operation = IdAble & Muteable & {
op: Op, op: Op,
items: Array<Nugget> items: Array<Nugget>
} }
@ -74,7 +78,7 @@ export const $composition = atom<Composition>([])
export function insertIntoComposition(item: LibraryItem) { export function insertIntoComposition(item: LibraryItem) {
$composition.set([ $composition.set([
...$composition.get(), ...$composition.get(),
{ id: randomUUID(), item, score: 0 } as Nugget, { item, id: randomUUID(), score: 0, muted: false } as Nugget,
]); ]);
} }
@ -84,10 +88,10 @@ export function removeFromComposition(item: PromptItem) {
]); ]);
} }
function nuggetDelta(nuggetId : Id, delta : number) { function nuggetDelta(nuggetId: Id, delta: number) {
$composition.set($composition.get().map(item => { $composition.set($composition.get().map(item => {
if ((item.id === nuggetId) && ("score" in item)) { if ((item.id === nuggetId) && ("score" in item)) {
const o = {...item, score: item.score + delta}; const o = { ...item, score: item.score + delta };
return o; return o;
} }
return item; return item;
@ -115,24 +119,25 @@ export function changeOperationOp(operationId: Id, op: Op) {
* utility method to remove & return a prompt item by ID * utility method to remove & return a prompt item by ID
* @param id PromptItem ID * @param id PromptItem ID
*/ */
export function popPromptItem (id : Id) { export function popPromptItem(id: Id) {
const found = $composition.get().find(item => item.id === id); const found = $composition.get().find(item => item.id === id);
$composition.set($composition.get().filter(item => item.id !== id)); $composition.set($composition.get().filter(item => item.id !== id));
return found; return found;
} }
export function lassoNuggets (n1id : Id, n2id : Id, op : Op) { export function lassoNuggets(n1id: Id, n2id: Id, op: Op) {
const n1 = popPromptItem(n1id); const n1 = popPromptItem(n1id);
const n2 = popPromptItem(n2id); const n2 = popPromptItem(n2id);
$composition.set([...$composition.get(), { $composition.set([...$composition.get(), {
id: uuidv4(), id: uuidv4(),
op, op,
items: [n1, n2], items: [n1, n2],
muted: false,
} as Operation]) } as Operation])
} }
export function addToOperation(nId : Id, opId : Id) { export function addToOperation(nId: Id, opId: Id) {
const comp = $composition.get(); const comp = $composition.get();
const nugget = comp.find(i => i.id === nId); const nugget = comp.find(i => i.id === nId);
$composition.set($composition.get().map(i => { $composition.set($composition.get().map(i => {
@ -179,8 +184,8 @@ export function operationToText(operation: Operation): string {
export const $textComposition = computed($composition, (composition) => { export const $textComposition = computed($composition, (composition) => {
const JOINER = ", "; const JOINER = ", ";
return composition.map(item => { return composition.map(item => {
return "op" in item ? operationToText(item) : nuggetToText(item); return item.muted ? null : ("op" in item ? operationToText(item) : nuggetToText(item));
}).join(JOINER); }).filter(i => !!i).join(JOINER);
}); });
export type SlottedComposition = PromptItem[][]; export type SlottedComposition = PromptItem[][];
@ -212,3 +217,14 @@ export const $slottedComposition = computed($composition, (composition) => {
}) })
return slotted; return slotted;
}); });
export function togglePromptItemMute(id: Id) {
$composition.set($composition.get().map(
c => {
return c.id === id ? {
...c,
muted: !c.muted,
} : c;
}
));
}

View File

@ -2,6 +2,8 @@ import { Atom, atom, computed } from "nanostores"
import { $composition, Nugget, Operation, PromptItem, addToOperation, itemIsNugget, itemIsOperation, lassoNuggets } from "../lib/prompt"; import { $composition, Nugget, Operation, PromptItem, addToOperation, itemIsNugget, itemIsOperation, lassoNuggets } from "../lib/prompt";
import { Op } from "../lib/operator"; import { Op } from "../lib/operator";
export type DropCandidate = string | string []
export type DragDropState = { export type DragDropState = {
currentSourceId?: string | null, currentSourceId?: string | null,
currentDropCandidateId?: string | null, currentDropCandidateId?: string | null,