Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Characters feature #98

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/components/character/CharacterDetailsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useTranslation } from "react-i18next";
import { BasicPage, Link } from "../settings/common";
import { TextButton } from "../textButton";
import { useCharacterStoreContext } from "@/features/characters/characterStoreContext";
import { NameComponent } from "./NameComponent";
import { TagComponent } from "./TagComponent";
import { SystemPromptComponent } from "./SystemPromptComponent";

export function CharacterDetailsPage({
setSettingsUpdated
}: {
setSettingsUpdated: (updated: boolean) => void;
}) {
const { t } = useTranslation();
const characterContext = useCharacterStoreContext();

return (
<BasicPage
title={t("Character Model")}
description={t("character_desc", "Select the Character to play")}
>
{/* <div className="rounded-lg shadow-lg bg-white flex flex-wrap justify-center space-x-4 space-y-4 p-4"> */}
<ul role="list" className="divide-y divide-gray-100 max-w-xs">
<li className="py-4">
<NameComponent name={characterContext.name} setName={characterContext.setName} setSettingsUpdated={setSettingsUpdated} />
</li>
<li className="py-4">
<TagComponent tag={characterContext.characterTag} setTag={characterContext.setCharacterTag} setSettingsUpdated={setSettingsUpdated} />
</li>
<li className="py-4">
<SystemPromptComponent systemPrompt={characterContext.systemPrompt} setSystemPrompt={characterContext.setSystemPrompt} setSettingsUpdated={setSettingsUpdated} />
</li>
</ul>
{/* </div> */}
<TextButton
className="rounded-t-none text-lg ml-4 px-8 shadow-lg bg-secondary hover:bg-secondary-hover active:bg-secondary-active"
onClick={() => { characterContext.saveCharacter(); }}
>
{t("Save Character")}
</TextButton>
</BasicPage>

);
}

25 changes: 25 additions & 0 deletions src/components/character/CharacterListPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCharacterStoreContext } from "@/features/characters/characterStoreContext";
import { VrmListPage } from "../settings/VrmListPage";
import Character from "@/features/characters/character";

export const CharacterListPage = ({
viewer,
setSettingsUpdated,
handleClickOpenVrmFile,
}: {
viewer: any; // TODO
setSettingsUpdated: (updated: boolean) => void;
handleClickOpenVrmFile: () => void;
}) => {
const characterStoreContext = useCharacterStoreContext();

return <ul>
{characterStoreContext.characterList.map((character: Character) =>
<li>
<div>
{character.tag}
</div>
</li>
)}
</ul>
}
Empty file.
114 changes: 114 additions & 0 deletions src/components/character/CharactersPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useState } from "react";
import { Link } from "../settings/common";
import { ArrowUturnLeftIcon, ChevronRightIcon, HomeIcon } from "@heroicons/react/20/solid";
import { TextButton } from "../textButton";
import { CharacterDetailsPage } from "./CharacterDetailsPage";

export const CharactersPage = ({
setSettingsUpdated,
handleClickOpenVrmFile,
onClickClose
}: {
setSettingsUpdated: (updated: boolean) => void;
handleClickOpenVrmFile: () => void;
onClickClose: () => void;
}) => {
const [breadcrumbs, setBreadcrumbs] = useState<Link[]>([]);
const [page, setPage] = useState('current_character');

function renderPage() {
switch(page) {
case 'current_character':
return <CharacterDetailsPage setSettingsUpdated={setSettingsUpdated} />;

default:
throw new Error('page not found');
}
}

return (
<div className="fixed top-0 left-0 w-full max-h-full text-black text-xs text-left z-20 overflow-y-auto backdrop-blur">
<div
className="absolute top-0 left-0 w-full h-full bg-violet-700 opacity-10 z-index-50"
></div>
<div className="fixed w-full top-0 left-0 z-50 p-2 bg-white">

<nav aria-label="Breadcrumb" className="inline-block ml-4">
<ol role="list" className="flex items-center space-x-4">
<li className="flex">
<div className="flex items-center">
<span
onClick={() => {
if (breadcrumbs.length === 0) {
onClickClose();
return;
}
setPage('main_menu');
setBreadcrumbs([]);
}}
className="text-gray-400 hover:text-gray-500 cursor-pointer"
>
<HomeIcon className="h-5 w-5 flex-shrink-0" aria-hidden="true" />
<span className="sr-only">Home</span>
</span>
</div>
</li>

{breadcrumbs.map((breadcrumb) => (
<li key={breadcrumb.key} className="flex">
<div className="flex items-center">
<ChevronRightIcon className="h-5 w-5 flex-shrink-0 text-gray-400" aria-hidden="true" />
<span
onClick={() => {
setPage(breadcrumb.key);
const nb = [];
for (let b of breadcrumbs) {
nb.push(b);
if (b.key === breadcrumb.key) {
break;
}
}
setBreadcrumbs(nb);
}}
className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700 cursor-pointer"
>
{breadcrumb.label}
</span>
</div>
</li>
))}
</ol>
</nav>
</div>

<div className="h-screen overflow-auto opacity-95 backdrop-blur">
<div className="mx-auto max-w-2xl py-16 text-text1">
<div className="mt-16">
<TextButton
className="rounded-b-none text-lg ml-4 px-8 shadow-sm"
onClick={() => {
if (breadcrumbs.length === 0) {
onClickClose();
return;
}
if (breadcrumbs.length === 1) {
setPage('main_menu');
setBreadcrumbs([]);
return;
}

const prevPage = breadcrumbs[breadcrumbs.length - 2];
setPage(prevPage.key);
setBreadcrumbs(breadcrumbs.slice(0, -1));
}}
>
<ArrowUturnLeftIcon className="h-5 w-5 flex-none text-white" aria-hidden="true" />
</TextButton>

{ renderPage() }
</div>
</div>
</div>
</div>
);
}
36 changes: 36 additions & 0 deletions src/components/character/NameComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { t } from "@/i18n";
import { FormRow, ResetToDefaultButton } from "../settings/common";
import { TextInput } from "../textInput";
import { defaultConfig, updateConfig } from "@/utils/config";

interface NameComponentProps {
name: string
setName: (name: string) => void
setSettingsUpdated: (updated: boolean) => void
};

export const NameComponent: React.FC<NameComponentProps> = ({name, setName, setSettingsUpdated}) => {
return <>
<FormRow label={t("Name")}>
<TextInput
value={name}
onChange={(event: React.ChangeEvent<any>) => {
setName(event.target.value);
updateConfig("name", event.target.value);
setSettingsUpdated(true);
}}
/>

{ name !== defaultConfig("name") && (
<p className="mt-2">
<ResetToDefaultButton onClick={() => {
setName(defaultConfig("name"));
updateConfig("name", defaultConfig("name"));
setSettingsUpdated(true);
}}
/>
</p>
)}
</FormRow>
</>;
}
41 changes: 41 additions & 0 deletions src/components/character/SystemPromptComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useTranslation } from 'react-i18next';

import { updateConfig, defaultConfig } from "@/utils/config";
import { FormRow, ResetToDefaultButton } from '../settings/common';

export function SystemPromptComponent({
systemPrompt,
setSystemPrompt,
setSettingsUpdated,
}: {
systemPrompt: string;
setSystemPrompt: (prompt: string) => void;
setSettingsUpdated: (updated: boolean) => void;
}) {
const { t } = useTranslation();

return (
<FormRow label={t("System Prompt")}>
<textarea
value={systemPrompt}
rows={8}
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
onChange={(event: React.ChangeEvent<any>) => {
event.preventDefault();
setSystemPrompt(event.target.value);
setSettingsUpdated(true);
}} />

{ systemPrompt !== defaultConfig("system_prompt") && (
<p className="mt-2">
<ResetToDefaultButton onClick={() => {
setSystemPrompt(defaultConfig("system_prompt"));
updateConfig("system_prompt", defaultConfig("system_prompt"));
setSettingsUpdated(true);
}}
/>
</p>
)}
</FormRow>
);
}
36 changes: 36 additions & 0 deletions src/components/character/TagComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { t } from "@/i18n";
import { FormRow, ResetToDefaultButton } from "../settings/common";
import { TextInput } from "../textInput";
import { defaultConfig, updateConfig } from "@/utils/config";

interface TagComponentProps {
tag: string
setTag: (tag: string) => void
setSettingsUpdated: (updated: boolean) => void
};

export const TagComponent: React.FC<TagComponentProps> = ({tag, setTag, setSettingsUpdated}) => {
return <>
<FormRow label={t("Tag")}>
<TextInput
value={tag}
onChange={(event: React.ChangeEvent<any>) => {
setTag(event.target.value);
updateConfig("character_tag", event.target.value);
setSettingsUpdated(true);
}}
/>

{ tag !== defaultConfig("character_tag") && (
<p className="mt-2">
<ResetToDefaultButton onClick={() => {
setTag(defaultConfig("character_tag"));
updateConfig("character_tag", defaultConfig("character_tag"));
setSettingsUpdated(true);
}}
/>
</p>
)}
</FormRow>
</>;
}
Loading
Loading