prompts can be added to prompt composer.
This commit is contained in:
parent
b0c70f6d17
commit
d9c1282d99
@ -27,6 +27,7 @@
|
|||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
|
"uuid": "^9.0.1",
|
||||||
"vite": "^5.1.4",
|
"vite": "^5.1.4",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
@ -57,11 +58,12 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
|
"@types/uuid": "^9.0.8",
|
||||||
"jest-without-globals": "^0.0.3"
|
"jest-without-globals": "^0.0.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
"!node_modules/"
|
"!node_modules/"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,9 @@ dependencies:
|
|||||||
typescript:
|
typescript:
|
||||||
specifier: ^4.9.5
|
specifier: ^4.9.5
|
||||||
version: 4.9.5
|
version: 4.9.5
|
||||||
|
uuid:
|
||||||
|
specifier: ^9.0.1
|
||||||
|
version: 9.0.1
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.1.4
|
specifier: ^5.1.4
|
||||||
version: 5.1.4(@types/node@16.18.82)
|
version: 5.1.4(@types/node@16.18.82)
|
||||||
@ -94,6 +97,9 @@ devDependencies:
|
|||||||
'@types/jest':
|
'@types/jest':
|
||||||
specifier: ^27.5.2
|
specifier: ^27.5.2
|
||||||
version: 27.5.2
|
version: 27.5.2
|
||||||
|
'@types/uuid':
|
||||||
|
specifier: ^9.0.8
|
||||||
|
version: 9.0.8
|
||||||
jest-without-globals:
|
jest-without-globals:
|
||||||
specifier: ^0.0.3
|
specifier: ^0.0.3
|
||||||
version: 0.0.3(jest@25.5.4)
|
version: 0.0.3(jest@25.5.4)
|
||||||
@ -3816,6 +3822,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
|
resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/uuid@9.0.8:
|
||||||
|
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/ws@8.5.10:
|
/@types/ws@8.5.10:
|
||||||
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -12908,6 +12918,11 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/uuid@9.0.1:
|
||||||
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/v8-to-istanbul@4.1.4:
|
/v8-to-istanbul@4.1.4:
|
||||||
resolution: {integrity: sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==}
|
resolution: {integrity: sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==}
|
||||||
engines: {node: 8.x.x || >=10.10.0}
|
engines: {node: 8.x.x || >=10.10.0}
|
||||||
|
@ -3,6 +3,7 @@ import './App.css';
|
|||||||
import PromptComposer from './components/PromptComposer';
|
import PromptComposer from './components/PromptComposer';
|
||||||
import { Box, Tabs, Tab, Typography } from '@material-ui/core';
|
import { Box, Tabs, Tab, Typography } from '@material-ui/core';
|
||||||
import { $textComposition } from './lib/prompt';
|
import { $textComposition } from './lib/prompt';
|
||||||
|
import { TextPrompt } from './components/TextPrompt';
|
||||||
|
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
@ -56,9 +57,7 @@ function App() {
|
|||||||
<PromptComposer />
|
<PromptComposer />
|
||||||
</CustomTabPanel>
|
</CustomTabPanel>
|
||||||
<CustomTabPanel value={value} index={1}>
|
<CustomTabPanel value={value} index={1}>
|
||||||
<div>
|
<TextPrompt />
|
||||||
<textarea aria-label='text-area'>{ $textComposition.get() }</textarea>
|
|
||||||
</div>
|
|
||||||
</CustomTabPanel>
|
</CustomTabPanel>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { Component, ReactComponentElement, ReactInstance, ReactNode } from "react";
|
|
||||||
|
|
||||||
export type Composable = {
|
|
||||||
|
|
||||||
} & ReactNode
|
|
@ -1,4 +1,3 @@
|
|||||||
import { Composable } from "./IComposable";
|
|
||||||
import { LibraryItem as LibItem } from "../lib/prompt";
|
import { LibraryItem as LibItem } from "../lib/prompt";
|
||||||
import AddCircleOutlineOutlinedIcon from '@mui/icons-material/AddCircleOutlineOutlined';
|
import AddCircleOutlineOutlinedIcon from '@mui/icons-material/AddCircleOutlineOutlined';
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
@ -23,5 +22,5 @@ export function LibraryItem(props: StyleProps) {
|
|||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) as Composable;
|
);
|
||||||
};
|
};
|
77
src/components/NewLibraryItem.test.tsx
Normal file
77
src/components/NewLibraryItem.test.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||||
|
import {NewLibraryItem} from './NewLibraryItem';
|
||||||
|
import { Category, addItemToLibrary, categoryHasName } from '../lib/prompt';
|
||||||
|
|
||||||
|
jest.mock('../lib/prompt', () => ({
|
||||||
|
Category: Category,
|
||||||
|
addItemToLibrary: jest.fn(),
|
||||||
|
categoryHasName: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('NewLibraryItem', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(addItemToLibrary as jest.Mock).mockReset();
|
||||||
|
(categoryHasName as jest.Mock).mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the form', () => {
|
||||||
|
render(<NewLibraryItem />);
|
||||||
|
|
||||||
|
expect(screen.getByText('Category')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Subject')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Prompt')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Create')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the category', () => {
|
||||||
|
render(<NewLibraryItem />);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText('Prompt Item Category'), {
|
||||||
|
target: { value: Category.vibes },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByLabelText('Prompt Item Name')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the name', () => {
|
||||||
|
render(<NewLibraryItem />);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText('Name'), {
|
||||||
|
target: { value: 'Test' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByDisplayValue('Test')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('changes the prompt', () => {
|
||||||
|
render(<NewLibraryItem />);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText('Prompt'), {
|
||||||
|
target: { value: 'Test' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByDisplayValue('Test')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a new library item', () => {
|
||||||
|
render(<NewLibraryItem />);
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText('Category'), {
|
||||||
|
target: { value: Category.subject },
|
||||||
|
});
|
||||||
|
fireEvent.change(screen.getByLabelText('Name'), {
|
||||||
|
target: { value: 'Test' },
|
||||||
|
});
|
||||||
|
fireEvent.change(screen.getByLabelText('Prompt'), {
|
||||||
|
target: { value: 'Test' },
|
||||||
|
});
|
||||||
|
fireEvent.click(screen.getByText('Create'));
|
||||||
|
|
||||||
|
expect(addItemToLibrary).toHaveBeenCalledWith({
|
||||||
|
category: Category.subject,
|
||||||
|
name: 'Test',
|
||||||
|
prompt: 'Test',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
81
src/components/NewLibraryItem.tsx
Normal file
81
src/components/NewLibraryItem.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { Button, FormControl, InputLabel, MenuItem, TextField } from "@material-ui/core";
|
||||||
|
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
||||||
|
import { Category, LibraryItem, addItemToLibrary, categoryHasName } from "../lib/prompt";
|
||||||
|
import { ChangeEvent, useState } from "react";
|
||||||
|
import {v4 as uuidv4} from "uuid"
|
||||||
|
|
||||||
|
export interface NewLibraryItemProps {
|
||||||
|
onNewCreated?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NewLibraryItem(props: NewLibraryItemProps) {
|
||||||
|
const { onNewCreated } = props;
|
||||||
|
|
||||||
|
const [category, setCategory] = useState(Category.subject);
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [prompt, setPrompt] = useState("");
|
||||||
|
|
||||||
|
const handleCategoryChange = (e: any | SelectChangeEvent) => {
|
||||||
|
setCategory(e.target.value as Category);
|
||||||
|
}
|
||||||
|
const handleNameChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||||
|
setName(e.target.value);
|
||||||
|
}
|
||||||
|
const handlePromptChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||||
|
setPrompt(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleCase = (s: string) => {
|
||||||
|
return s[0].toUpperCase() + s.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateItem = () => {
|
||||||
|
const libraryItem = {
|
||||||
|
id: uuidv4(),
|
||||||
|
category,
|
||||||
|
name: categoryHasName(category) ? name : null,
|
||||||
|
prompt,
|
||||||
|
} as LibraryItem;
|
||||||
|
addItemToLibrary(libraryItem);
|
||||||
|
setCategory(Category.subject);
|
||||||
|
setName("");
|
||||||
|
setPrompt("");
|
||||||
|
onNewCreated ? onNewCreated() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const catChoices = Object.keys(Category);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<FormControl>
|
||||||
|
<InputLabel htmlFor="new-prompt-category">Category</InputLabel>
|
||||||
|
<Select
|
||||||
|
native
|
||||||
|
id="new-prompt-category"
|
||||||
|
aria-label="Prompt Item Category"
|
||||||
|
value={category}
|
||||||
|
onChange={(e) => handleCategoryChange(e)}
|
||||||
|
>
|
||||||
|
{catChoices.map(cat => (
|
||||||
|
<option value={cat} id={cat} key={cat}>{titleCase(cat)}</option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl>
|
||||||
|
{categoryHasName(category) ? (<InputLabel htmlFor="name">Name</InputLabel>) : <></>}
|
||||||
|
{categoryHasName(category) ? (<TextField aria-label="Prompt Item Name" value={name} onChange={handleNameChange} id="name" />) : <></>}
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl>
|
||||||
|
<InputLabel htmlFor="prompt">Prompt</InputLabel>
|
||||||
|
<TextField aria-label="Prompt Item Text" value={prompt} onChange={handlePromptChange} id="prompt" />
|
||||||
|
<Button onClick={handleCreateItem} >Create</Button>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -2,7 +2,6 @@ import { Button, ButtonGroup, Chip, Divider } from '@material-ui/core';
|
|||||||
import React, { Component, useState } from 'react';
|
import React, { Component, 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 {Composable} from "./IComposable"
|
|
||||||
|
|
||||||
import "./Nugget.css";
|
import "./Nugget.css";
|
||||||
import { Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore } from '../lib/prompt';
|
import { Nugget as NuggetType, decreaseNuggetScore, increaseNuggetScore } from '../lib/prompt';
|
||||||
@ -32,5 +31,5 @@ export default function Nugget(props : NuggetProps) {
|
|||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) as Composable;
|
);
|
||||||
}
|
}
|
@ -2,8 +2,7 @@ import { Menu, MenuItem } from "@material-ui/core";
|
|||||||
import React, { Children, ReactNode } from 'react';
|
import React, { Children, ReactNode } from 'react';
|
||||||
import "./Operation.css";
|
import "./Operation.css";
|
||||||
import { Op } from "../lib/operator";
|
import { Op } from "../lib/operator";
|
||||||
import { randomUUID } from "crypto";
|
import { v4 as randomUUID } from "uuid";
|
||||||
import { Composable } from "./IComposable";
|
|
||||||
import { Operation as OperationType, changeOperationOp } from "../lib/prompt";
|
import { Operation as OperationType, changeOperationOp } from "../lib/prompt";
|
||||||
import Nugget from "./Nugget";
|
import Nugget from "./Nugget";
|
||||||
|
|
||||||
@ -72,7 +71,7 @@ function Operation(props : OperationProps) {
|
|||||||
})}
|
})}
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
) as Composable;
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Operation, Op };
|
export { Operation, Op };
|
@ -1,27 +1,29 @@
|
|||||||
import { Button } from '@material-ui/core';
|
import { Button } from '@material-ui/core';
|
||||||
import Masonry from '@mui/lab/Masonry';
|
import Masonry from '@mui/lab/Masonry';
|
||||||
import Nugget from './Nugget';
|
|
||||||
import { Operation } from './Operation';
|
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
import "./PromptArea.css";
|
import "./PromptComposer.css";
|
||||||
import { PromptLibrary } from './PromptLibrary';
|
import { PromptLibrary } from './PromptLibrary';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { $composition, $slottedComposition, LibraryItem, insertIntoComposition } from '../lib/prompt';
|
import { $slottedComposition, LibraryItem, PromptItem, insertIntoComposition } from '../lib/prompt';
|
||||||
import { Category } from '@mui/icons-material';
|
import { Category } from '@mui/icons-material';
|
||||||
import { useStore } from '@nanostores/react'
|
import { useStore } from '@nanostores/react'
|
||||||
|
import Nugget from './Nugget';
|
||||||
|
import { Stack } from '@mui/material';
|
||||||
|
import { Operation } from './Operation';
|
||||||
|
|
||||||
type Composable = (typeof Nugget) | (typeof Operation);
|
export interface PromptComposerProps {
|
||||||
|
|
||||||
export default function PromptComposer(props) {
|
}
|
||||||
const [open, setOpen] = React.useState(false);
|
|
||||||
|
export default function PromptComposer(props: PromptComposerProps) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const handleClickOpen = () => {
|
const handleClickOpen = () => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = (value: string) => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
// setSelectedValue(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnInsertItem = (item: LibraryItem) => {
|
const handleOnInsertItem = (item: LibraryItem) => {
|
||||||
@ -30,19 +32,31 @@ export default function PromptComposer(props) {
|
|||||||
|
|
||||||
const slottedComposition = useStore($slottedComposition);
|
const slottedComposition = useStore($slottedComposition);
|
||||||
|
|
||||||
|
const promptItemFactory = (promptItem : PromptItem, key : string) => {
|
||||||
|
return "op" in promptItem ? <Operation operation={promptItem} key={key} /> : <Nugget nugget={promptItem} key={key} />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button className="add-button">
|
<div>
|
||||||
<AddIcon />
|
<Button className="add-button" onClick={handleClickOpen}>
|
||||||
<PromptLibrary
|
<AddIcon />
|
||||||
open={open}
|
<PromptLibrary
|
||||||
onInsertItem={handleOnInsertItem}
|
open={open}
|
||||||
></PromptLibrary>
|
onInsertItem={handleOnInsertItem}
|
||||||
|
onClose={handleClose}
|
||||||
</Button>
|
></PromptLibrary>
|
||||||
<Masonry columns={Object.keys(Category).length} spacing={2} sequential>
|
</Button>
|
||||||
<div>Something</div>
|
</div>
|
||||||
</Masonry>
|
<div>
|
||||||
|
{
|
||||||
|
slottedComposition.map((itemCol, i) => (
|
||||||
|
<Stack>
|
||||||
|
{itemCol.map((promptItem, j) => promptItemFactory(promptItem, `item-${j}-${j}`))}
|
||||||
|
</Stack>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
3
src/components/PromptLibrary.css
Normal file
3
src/components/PromptLibrary.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.prompt-library-dialog .categories div {
|
||||||
|
display: inline;
|
||||||
|
}
|
@ -31,6 +31,7 @@ const mockOpen: boolean = true;
|
|||||||
const mockProps: SimpleDialogProps = {
|
const mockProps: SimpleDialogProps = {
|
||||||
open: mockOpen,
|
open: mockOpen,
|
||||||
onInsertItem: mockOnAddItem,
|
onInsertItem: mockOnAddItem,
|
||||||
|
onClose: mockOnClose,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockLibrary: LibraryType = [
|
const mockLibrary: LibraryType = [
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { Checkbox, Dialog, DialogTitle } from "@material-ui/core";
|
import { Button, Checkbox, Dialog, DialogTitle } from "@material-ui/core";
|
||||||
import { LibraryItem as LibItemType, $library, Category } from "../lib/prompt";
|
import { LibraryItem as LibItemType, $library, Category } from "../lib/prompt";
|
||||||
import { LibraryItem } from "./LibraryItem";
|
import { LibraryItem } from "./LibraryItem";
|
||||||
import { ChangeEvent } from "react";
|
import { ChangeEvent, useState } from "react";
|
||||||
import { useStore } from "@nanostores/react";
|
import { useStore } from "@nanostores/react";
|
||||||
|
import { ExitToAppOutlined } from "@mui/icons-material";
|
||||||
|
import { NewLibraryItem } from "./NewLibraryItem";
|
||||||
|
import "./PromptLibrary.css"
|
||||||
|
|
||||||
export interface SimpleDialogProps {
|
export interface SimpleDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
// onClose: (composable: Composable) => void,
|
onClose: () => void,
|
||||||
// onAddItem: (item: LibItemType) => void,
|
// onAddItem: (item: LibItemType) => void,
|
||||||
onInsertItem: (item: LibItemType) => void,
|
onInsertItem: (item: LibItemType) => void,
|
||||||
}
|
}
|
||||||
@ -25,10 +28,14 @@ const show = ($el: Element) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PromptLibrary(props: SimpleDialogProps) {
|
export function PromptLibrary(props: SimpleDialogProps) {
|
||||||
const { open, onInsertItem } = props;
|
const { open, onInsertItem, onClose } = props;
|
||||||
|
|
||||||
const library = useStore($library);
|
const library = useStore($library);
|
||||||
|
|
||||||
|
const [doCreate, setDoCreate] = useState(false);
|
||||||
|
|
||||||
|
const [visibleCategories, setVisibleCategories] = useState([] as Category[]);
|
||||||
|
|
||||||
const handleOnAddItem = (item: LibItemType) => {
|
const handleOnAddItem = (item: LibItemType) => {
|
||||||
// onAddItem(item);
|
// onAddItem(item);
|
||||||
}
|
}
|
||||||
@ -37,32 +44,47 @@ export function PromptLibrary(props: SimpleDialogProps) {
|
|||||||
onInsertItem(item);
|
onInsertItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterCat = (catKey: string, catVal: string, v: ChangeEvent<HTMLInputElement>) => {
|
const filterCat = (catKey: string, v: ChangeEvent<HTMLInputElement>) => {
|
||||||
const isChecked = v.target.value === '1';
|
const isChecked = v.target.value === '1';
|
||||||
document.querySelectorAll(`.category-${catVal}`).forEach($el => {
|
setVisibleCategories((visibleCategories.includes(catKey as Category) && !isChecked) ? visibleCategories.filter(c => c != catKey) : [...visibleCategories, catKey as Category]);
|
||||||
|
document.querySelectorAll(`.category-${catKey}`).forEach($el => {
|
||||||
if (isChecked) show($el)
|
if (isChecked) show($el)
|
||||||
else hide($el)
|
else hide($el)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnNewCreated = () => {
|
||||||
|
setDoCreate(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const categoryChoices = Object.keys(Category);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open}>
|
<Dialog className="prompt-library-dialog" onClose={handleClose} open={open}>
|
||||||
<DialogTitle>Prompt Library</DialogTitle>
|
<DialogTitle>Prompt Library</DialogTitle>
|
||||||
<div className="categories">
|
<div className="categories">
|
||||||
{Object.entries(Category).map(([catKey, catVal]) => {
|
{categoryChoices.map(catKey => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div key={catKey}>
|
||||||
<Checkbox onChange={v => filterCat(catKey, catVal, v)} />
|
<Checkbox onChange={v => filterCat(catKey, v)} />
|
||||||
<span>{title(catVal)}</span>
|
<span>{title(catKey)}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
library?.map(item => <LibraryItem item={item} onInsertItem={handleOnInsertItem} />)
|
library?.map(item => <LibraryItem key={item.id} item={item} onInsertItem={handleOnInsertItem} />)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button onClick={() => setDoCreate(true)}>Create</Button>
|
||||||
|
</div>
|
||||||
|
{doCreate ? (<NewLibraryItem onNewCreated={handleOnNewCreated} />) : (<></>)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
6
src/components/TextPrompt.css
Normal file
6
src/components/TextPrompt.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.text-prompt {
|
||||||
|
width: 75%;
|
||||||
|
height: 75%;
|
||||||
|
padding: 10pt;
|
||||||
|
margin: 4pt;
|
||||||
|
}
|
11
src/components/TextPrompt.tsx
Normal file
11
src/components/TextPrompt.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { TextareaAutosize } from "@material-ui/core";
|
||||||
|
import { $textComposition } from "../lib/prompt";
|
||||||
|
import "./TextPrompt.css";
|
||||||
|
import { useStore } from "@nanostores/react";
|
||||||
|
|
||||||
|
export function TextPrompt () {
|
||||||
|
const text = useStore($textComposition);
|
||||||
|
return (
|
||||||
|
<TextareaAutosize content={text} className="text-prompt" />
|
||||||
|
)
|
||||||
|
}
|
@ -3,17 +3,13 @@ import ReactDOM from 'react-dom/client';
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import store from "./store"
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
);
|
);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<App />
|
||||||
<App />
|
|
||||||
</Provider >
|
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { randomUUID } from "crypto";
|
import { v4 as randomUUID } from "uuid";
|
||||||
import { Op } from "./operator";
|
import { Op } from "./operator";
|
||||||
import { createSlice } from '@reduxjs/toolkit'
|
import { createSlice } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { randomUUID } from "crypto";
|
import { v4 as randomUUID } from "uuid";
|
||||||
import { Op } from "./operator";
|
import { Op } from "./operator";
|
||||||
import {
|
import {
|
||||||
Library as LibraryType,
|
Library as LibraryType,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { randomUUID } from "crypto";
|
import { v4 as randomUUID } from "uuid";
|
||||||
import { Op } from "./operator";
|
import { Op } from "./operator";
|
||||||
import { atom, computed } from "nanostores";
|
import { atom, computed } from "nanostores";
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ type IdAble = {
|
|||||||
id: Id,
|
id: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only vibes and styles will have a name.
|
||||||
export enum Category {
|
export enum Category {
|
||||||
subject = "subject",
|
subject = "subject",
|
||||||
style = "style",
|
style = "style",
|
||||||
@ -15,6 +16,10 @@ export enum Category {
|
|||||||
medium = "medium",
|
medium = "medium",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function categoryHasName(cat : Category) {
|
||||||
|
return (cat === Category.style || cat === Category.vibes)
|
||||||
|
}
|
||||||
|
|
||||||
const N_CATEGORIES = Object.keys(Category).length;
|
const N_CATEGORIES = Object.keys(Category).length;
|
||||||
|
|
||||||
export function catI(c: Category | string): number {
|
export function catI(c: Category | string): number {
|
||||||
|
Loading…
Reference in New Issue
Block a user