From b1cab340ca5b3d304eb9179b005e246e5bbe7d14 Mon Sep 17 00:00:00 2001 From: Pabloader Date: Sat, 2 Nov 2024 11:48:10 +0000 Subject: [PATCH] AIStory: some refactoring --- build/isGame.ts | 2 +- src/common/components/modal/modal.module.css | 2 +- src/common/hooks/useBool.ts | 2 +- src/common/hooks/useInputState.ts | 18 +++ src/games/ai/assets/favicon.ico | Bin 51262 -> 3262 bytes src/games/ai/assets/style.css | 147 +++--------------- src/games/ai/components/app.tsx | 2 +- src/games/ai/components/chat.tsx | 2 +- src/games/ai/components/header.tsx | 45 ------ .../ai/components/header/header.module.css | 29 ++++ src/games/ai/components/header/header.tsx | 79 ++++++++++ src/games/ai/components/input.tsx | 8 +- .../components/message/formattedMessage.tsx | 20 +++ .../ai/components/message/message.module.css | 76 +++++++++ .../ai/components/{ => message}/message.tsx | 54 +++---- .../components/minichat/minichat.module.css | 10 ++ src/games/ai/components/minichat/minichat.tsx | 40 +++-- src/games/ai/const.ts | 66 +------- src/games/ai/contexts/llm.tsx | 117 +++++++++++++- src/games/ai/contexts/state.tsx | 21 +-- src/games/ai/dom.ts | 19 ++- src/games/ai/messages.ts | 99 ------------ 22 files changed, 441 insertions(+), 417 deletions(-) create mode 100644 src/common/hooks/useInputState.ts delete mode 100644 src/games/ai/components/header.tsx create mode 100644 src/games/ai/components/header/header.module.css create mode 100644 src/games/ai/components/header/header.tsx create mode 100644 src/games/ai/components/message/formattedMessage.tsx create mode 100644 src/games/ai/components/message/message.module.css rename src/games/ai/components/{ => message}/message.tsx (62%) diff --git a/build/isGame.ts b/build/isGame.ts index 5bd4825..f6be86d 100644 --- a/build/isGame.ts +++ b/build/isGame.ts @@ -21,5 +21,5 @@ export async function getGames() { if (!stat.isDirectory()) return []; const list = await fs.readdir(dir); - return list.filter(d => d !== 'index'); + return list.filter(d => d !== 'index').sort(); } \ No newline at end of file diff --git a/src/common/components/modal/modal.module.css b/src/common/components/modal/modal.module.css index 58df899..0dc6ad7 100644 --- a/src/common/components/modal/modal.module.css +++ b/src/common/components/modal/modal.module.css @@ -10,7 +10,7 @@ height: fit-content; max-height: 80dvh; overflow: hidden; - background-color: var(--backgroundColorDark, #111); + background-color: var(--backgroundColor, #333333); color: var(--color, white); border: var(--border, 1px solid white); outline: none; diff --git a/src/common/hooks/useBool.ts b/src/common/hooks/useBool.ts index 9ca862d..bc34179 100644 --- a/src/common/hooks/useBool.ts +++ b/src/common/hooks/useBool.ts @@ -1,6 +1,6 @@ import { useCallback, useState } from "preact/hooks"; -export const useBool = (initialValue: boolean) => { +export const useBool = (initialValue: boolean = false) => { const [value, setValue] = useState(initialValue); const setTrue = useCallback(() => setValue(true), []); diff --git a/src/common/hooks/useInputState.ts b/src/common/hooks/useInputState.ts new file mode 100644 index 0000000..5e0b121 --- /dev/null +++ b/src/common/hooks/useInputState.ts @@ -0,0 +1,18 @@ +import { useCallback, useState } from "preact/hooks"; + +export const useInputState = (defaultValue = ''): [string, (e: Event | string) => void] => { + const [value, setValue] = useState(defaultValue); + + const handleInput = useCallback((e: Event | string) => { + if (typeof e === 'string') { + setValue(e); + } else { + const { target } = e; + if (target && 'value' in target && typeof target.value === 'string') { + setValue(target.value); + } + } + }, []); + + return [value, handleInput]; +} \ No newline at end of file diff --git a/src/games/ai/assets/favicon.ico b/src/games/ai/assets/favicon.ico index c355512cdb3180e90174150f838b134bcf847a14..057ea8e950d3a65d5c90ad0f216dc320e3518710 100644 GIT binary patch literal 3262 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%0D2yg%vO0dca^HvM8)?pt z^Um7i#qNnt3e5YgW8*P951ExURx;J~b&v770C`+NMGpL#jsw#`B z-b2-9&^4J%%^rW4o`}#N*jx9BcL#|HN2f;kpyCeUce-Oc%b+lbPp7Q*&qY;0* zYA&%B(*OJ``A37AmvU63Ni2SqlJyNR<|YPeud4JZ%RPz;0t}S50I#YX@Tn^#P?!;Qyw~cg>;+AU**6 z-<-AFaP({YwItKUZSu4gitQ0fD16LB#kL89N%OSlHaXhPx%x_-t>;aJ+m*XN(k9GR zZk^J(?u}M3kFpD$pO^?s&bM?~3RSg>u1=?`GnuMAOmzlRgTo98Dl}Li_`3OEmtHhM zh<{SO@+@*l?Vb3SlT|J7Z*RYJJQe)^-3!lkR9_?;ZrxoxrUoVmrKF*0!EZnm2<1Yd z0nY~_0xOJ&3t@){9O$FE1{r;$PX*7v>64fF6t@73J<6M2RTlGgg7K(WXZI#chL03uSTBm2p$FXj#X>1he#r$S^_X5<}K9Q`RzbmgxD!`QJC~ zU12=-m9uQOyCFw?{QJIyS+W@Pn#QXl$19?zD))V2sXS=uEw&Ao*;?}rg|V0g16wAy zuY05AkMR7r`Xi@Xk9|eoh^H&ksETyDl7JDxR;GKZGW{sEknpf1=H3IeP#%_1Gg!S9 z^(W;2D1cvhZis(QXYW`Q6_`@{mTK!zCG)G+rAYBv@cGqu~!~o)zy|;`+IjS>yMcviyo_p94n6; z*Bv)aeLl+2dd@1Vu*)m$jVF``S9Sd~zGM9x9qZrf+cL#=_zU)W99@y@uSlWGQzbAJ zDQrbDQ<3h&YL~elv6UeS&tJqpHdeX28AsctoHU95_Noi4r3p0xa@0HUk7dW8{tpdU zF(Qmiiiq=v<_L$xl|lJz;*is#yi%0==>yv)4{V#Hh@Gm6kAMp4(iUI}fPCxW53PqkwjKJ& za`+=t_7|2bF%Wsq)(**Fmh~PbFYyju@%CT!4qWpNUh~O{J+flAtkfyL=~h)zmUh9f z7kDQxc)&j|_yxfy@P3}7c)#A=bV|N^Wl!|9zNqno(G!RX5R_5UW-PSqeb;hir7dT* zJ@-r7*)`7VF;xF~*3m6s&6LtOTl4$(eAF8~2_Ni-3AT>yMoN5Qy{#qRttc~;XLY46 z?cOqB0BbROS9lA5q;4kqZ|;;pmF=L)5~#97>Q=HJAz+|L@C=H4LQ+I>B$W<(1!H1R zCVK@*&Le{8527Iq=f|!p@ZZJQTMKg1CH}jr&f{()UgV#&K&1^ooSNs<93Vy$!auJG zw*p7_*Aon%1!4S?z=4R2l`?@fu!c6@;0^DJp6wk^6kFJ2|WpO4Hu#vwTIPbe&~sxg$)~q ztQFpi8>rG)|BVFdMq&Ur6WE*abXlUeBAx0v&$~J}hAfUt2!zNm@;U`ep;}% zvXly$Nr+qIKf-Om$~8^l{i{Vr>cd|MSnbm*=x&eY~CX_VCC~ z$%aFvz{3AE98^fOuju*1{9l?2P@zyz2nA>uf)$M-oQ#A4Qktun@fnC>03 zy5Y=Q$I7B6DA7Gjnx#vhr{BHEaNs@5v5)O}Ydq!gjH-zDf+#yuS>HgZ?C2MRv6up5 zdm`T=PD=uCX?5f@ib|pNq}?S1!%+x zEJ-zi&_`Grjb#}};eLu<0VF%x7)xW}sa??j7oK~z}A^we@=kN_?Fcz1>%#%sd0l2l)&E@qbZ#evW?F0 zFnQrX)C5S-VB|aUxGBc0<&M+ec#5`ou0%@kUX3Dy%aOjT#3J5XNt~>J_jSX2As`a& zLO$L?)d_^I34DYC`4EU9#60JNwp0Cu>g;d( zVyE?O9Xl8~UKKM*xnr6(b*3?6jxlqdZqGtp)?(A)<=VV&EY(?LcNunHsLGW&Yy0D; z$zm~OCh7MsA>?1$?7p(q2f~B!UXJt@MdH=VdPb_rqpvzEq7C(}{?KKIP%>TAa-V-0H~A(%x7K!HHXcm@@yoaYoghg_88LcoevWcF!|r&evuwQ65@hDB8j(im_oB z8!*&+-&d#4HSBxOdg^P}#ZB&lAA{H;4Ln7gy+uE{u15Lm_Vbn+ArKHn^1(liCGr4J z2!tjDF^vd@meC@lE8+GW1xZKRIl%h+SWgd+d%F8Mn0@Cq(X=VV)7rzhyr8eq7n%IyhrfKoz?q$ zlNJyaihO4D^_8bQ!%>`QKKZrz=%?0G z-#E_WCeY3B{{SxjBn`ZUn|*~_-8WNN^>xA5i-Hv17@}8VRWbpG^Fa`g6#<(PVkRgz48tvj(+;dCmw&S=+tgM;lGh{5&jXQF=!mUE^Ks#aDEfU?u#3p7k_jXZ)bYW3+^sBKOC6#55lX%Sq<`!E&9Z_ z0g^XLfYg9?5h4BwrQ~Kt>}~EQB4cZz4Ryydwmtd8V-G#_tD_mQl&+ew)-%pF#?yt% zh}CG(1dyN*`NIYfkN}4G7fWWTa2|SPQNf9E98Dp?A#x~2Tglt;I+A(lkB2Am+6BoH zt$@rEh9D*&9SMv{6vj0!|S6%o8r_OWSg#Y8yx31IL`gx6oE7D{EhB&8{FqNItn7GmJV{JfFh;^e1>*lCljvgT9qY(vy&OeO*p_5_!504R21uX?}GS>0}rT{8j z=ueml0{n~lC-H5B32g2%^Sp%@5Vc+8BTyUAE`WT{?h!bc!-YMNES4es3UA}7!95@L zMo#XF95)a(Ua@VGI)17;ahf`Dsy=B3$k(St=yuOHATP~d=K%BPHiB?x{tu3`-#hXN zoM(S<<$dqT|K4?eleaRHEUb9@h+$hHrzWkz5oQlAxcl z!p~iR3@QlH$`Az9VlV;N4w_(ye<`Yo_0BWv9B0-#&#ZOlt#_Q=;4I$3sjdk=FwjqSgX0yVEebfaD1adUgx=sd zz_Y}5*eekeAMt$f&$vN;JL_nrEe%%LjSrU2g7c3WHR`|q{dcP7ODy=e;;|3d2-is# z7qC(ZbCbg1ujgM036iY9BM=ix;mlzsAhrPcWCsgJ8?XUSyvu|1{V)+o;0`d4xd2^q zU`veHT2zY#4(=0?bH*z3rD$|@`l80kqL7YawQHJe#|*`ucg^QF(byTQyy9s(=B(Q7 zuH0=ejB%db>^!sHk-HADz>&Mwl?(oVuopyg-RNC(fOdpS_`k#|@pk{f?fehDiwk^x z62c83k&l>*!j^^eyE_>0Z);{O4XXCSac{i>=Li4KjD8Bas9#sbSq< zmg<8F1wx6Kj)pScUlgU#bO?QfLsh~mSr~$sOn_uYO#6u(LC~~NI(BTzB~ zy#R||0m@Yg7~ctERE4}qfXD+xYQh28N6rsnXPxb|y$Ry)(N(5xU;pq!4@vyv^|@!C zt}Z^tpbKPa5bRisY~v6wz(3TFrX_ z4=EE7H!sgvIOhO%hOqi-PyG?|nT>@1lixT_t+iZ=VzH#_CM!^&T>@Nsi2slwerCWU zv6nEMf6xf!!P<)G{H@4*{N`Fi@3n=qCQ9NXnc)8W?pyQ4dv(m%aeb@dR9MufeAb2kZY{2mgB1*(sjfChen|Byv^ zW{7`@TS70b2wecLmVg;CbU~2GJMk}v`h^H59YRu;O0AW^EBF`fLT;NSsSAJs?p9`@ zU6=)u+#%M`0!Lw?%~pNXdKNkVkG}Fu-c-eV@Vuixmw)mRg5f?Nu!X0K2l_ulAh=otSG(Zp63C(eR0ttS ztcwG&V5kieniOFJc{syFb|n76VQ>aX18CZis|WcE=b!Vz)kzwRWEg|HfDgV)XqWi+ z4l+J`Kk9VM0+bR+U$LN13oM5|wzsduUbx*?n!?Gik+r{|fP_zh(8=2dBb}cR1t4_{ z16eOaOTpF3x>^`}6Kk#a=_;?D+5HbMz7WoQX#D(hqjUCecVl-Vw$(YB$+l9rSPl>q z^pZ>%u>dt4jaI7I?ifxcC>1CI;;WF0k$@nGpBnxO(U2gL44w(Z))r|wN`&8qf6y*L z@R1%P0iIcejeH^)A98qnA=a{ybio9u;Lrm^qua+?dtDVrxULJlvxS4N{u%tk?Zfyd zpB2d$`G?N~9LiV#ONvfBPotG{HbGS=L$#*k@{&1I9uU+15bfcEfBWsP>q@hIhDy#_ z&pQ!_NkxVU#i0l8>qbQ8DJ<;z@V+Y63BtM?Mb>2@au11Ox{K6zj%*&Q`>9-q8%%_gm`R zT0ABp{hwd@#}S0ze)!;n53E|bK-F4+SPTn~W0D%YfEcyW5&@t@95CZ|G0Ez-T>rsMy(w4txge$x>Ms67b*+~>&e}Vt7D}xlJ1a>s<|`9H&dm*6503)tbW-~m!20Y2}9O0;keJUy}rKF`|f8CxxFsrKq_ zl@;xuGX8a_%H0XS{oThOd*s{I%Q|cFJlZPO(!e|LCy1;exUimffcJIsK5@MOeUG3O z!~yAF1u*RVy>(R3%L>*(QeTC9XV^)k#Nq{gf@kPTh)g(m+)nH64ZzdGdb$|64M`5+ zGnfKc_ji(BANdp_>CgCoFqwq5hZH6-K!go~1@lg<-oou0St}kIU(Z4K?`JUrBDxSi*_oO$$-hi6TF8#_g<@-oI;%VCWSnufdx!3FW%(~eG^Sh;kQGJ$lT zf*=kF&m_WoJ3{n^55g}K83xHgNWGWPVb~x{w*zU3^n1u&8d6e8khCJ`%vj*S+6t>d zrAEjH$)b}7{9H^Rr8F}1Dsmq3_IA#R^afcaXeVHa;4}gZJRfpxuB8lB=7Hi%r&1@6 z`vaQ%cVYM!--2rWkC*=OhwnbBD?4eEm-$g3qdKrR3Ych4L>cT_A)WwRh$(@nqGEb1 zcwrPERMCS>=}h>^(7PCf-bHr(^APiGq}(F9kv#@5?0gsY(g5&wa0vF`z@(g%;1+xA zMDV+zsDw;s8=7MhLYYkX0LdizpNb$NfG)AKS>(KdNWBfuO(mdAwSGgDLvgdU{Pd4& zKY973fB3UB|1P$GeEsHM{^xgJuPDBlrRlul(p2Jakr`ocVjKi4CI!|G5ThL(j@KI3TlYpB7*)#T#ir_K$st*XGkF227LLwK>Q#a9*j}R{Z<}p zOys8$5dVQr7z+l#}x59Y2(KCOaiRC;h_d%)+yK2=ez7p4$r)z{+3XqPe)H>p9zFW0{|=wO zqX|MMVQ4h&9(?e@haP(9kw+eR?6F55e;j!1i6VFvB?Gw^VE}15IjZj^ixj)VC>Pgm!y}KC!{2{k zX_wO5d95v$o5yzu`>3I0Z|912@2L8st-x&BC1OLAmh#K)lBaZJId0aO9`jL8n zTYOLszy7xPGBO^GIDUC#JR2GBM#jTo;g`hAVLur0bl49.header { - display: flex; - flex-direction: row; - height: 36px; - width: 100%; - border: var(--border); - - >input { - &.valid { - background-color: var(--green); - } - - &.invalid { - background-color: var(--red); - } - } - } - >.chat { display: flex; flex-direction: column; @@ -112,108 +99,18 @@ body { border: var(--border); border-bottom: none; border-top: none; - - >.message { - width: 100%; - padding: 12px; - - &.role-user { - background-color: var(--shadeColor); - - &:not(.last-user) .content .text { - opacity: 0.5; - font-size: 12px; - } - } - - &.role-assistant { - border-top: 1px solid var(--backgroundColorDark); - } - - >.content { - white-space: pre-wrap; - line-height: 1.5; - display: flex; - flex-direction: column; - width: 100%; - gap: 8px; - - >textarea { - background-color: var(--backgroundColorDark); - border: var(--border); - min-height: 100px; - height: unset; - resize: vertical; - line-height: 1.5; - padding: 5px; - border-radius: var(--border-radius); - } - - >.text { - flex-grow: 1; - width: 100%; - animation-duration: 300ms; - - >.bold { - font-weight: bold; - } - - >.italic { - font-style: italic; - color: var(--italicColor); - } - - >.quote { - color: var(--quoteColor); - } - } - - >.buttons { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-end; - gap: 8px; - - >.icon { - font-family: var(--emojiFont); - font-weight: bold; - font-size: 20px; - border: none; - background: transparent; - padding: 0; - color: var(--color); - cursor: pointer; - - &.color { - font-family: var(--emojiColorFont); - } - } - - >.swipes { - display: flex; - width: 100%; - flex-direction: row; - justify-content: space-between; - gap: 8px; - - >div { - cursor: pointer; - font-size: 20px; - } - } - } - } - } } >.chat-input { display: flex; flex-direction: row; height: auto; - min-height: 48px; width: 100%; - border: var(--border); + > textarea { + min-height: 48px; + resize: none; + background-color: var(--backgroundColor); + } } } } diff --git a/src/games/ai/components/app.tsx b/src/games/ai/components/app.tsx index e0e62cb..97b1265 100644 --- a/src/games/ai/components/app.tsx +++ b/src/games/ai/components/app.tsx @@ -1,4 +1,4 @@ -import { Header } from "./header"; +import { Header } from "./header/header"; import { Chat } from "./chat"; import { Input } from "./input"; diff --git a/src/games/ai/components/chat.tsx b/src/games/ai/components/chat.tsx index a7ba117..75ec7bb 100644 --- a/src/games/ai/components/chat.tsx +++ b/src/games/ai/components/chat.tsx @@ -1,6 +1,6 @@ import { useContext, useEffect, useRef } from "preact/hooks"; import { StateContext } from "../contexts/state"; -import { Message } from "./message"; +import { Message } from "./message/message"; import { MessageTools } from "../messages"; import { DOMTools } from "../dom"; diff --git a/src/games/ai/components/header.tsx b/src/games/ai/components/header.tsx deleted file mode 100644 index ffd405e..0000000 --- a/src/games/ai/components/header.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useCallback, useContext, useEffect, useState } from "preact/hooks"; -import { StateContext } from "../contexts/state"; -import { LLMContext } from "../contexts/llm"; - -export const Header = () => { - const llm = useContext(LLMContext); - const { connectionUrl, setConnectionUrl } = useContext(StateContext); - const [urlValid, setUrlValid] = useState(false); - const [urlEditing, setUrlEditing] = useState(false); - - const handleEditUrl = useCallback((e: InputEvent) => { - if (e.target instanceof HTMLInputElement) { - setConnectionUrl(e.target.value.trim()); - } - }, [setConnectionUrl]); - - const handleFocusUrl = useCallback(() => setUrlEditing(true), []); - - const handleBlurUrl = useCallback(() => { - const regex = /^(?:http(s?):\/\/)?(.*?)\/?$/i - const normalizedConnectionUrl = connectionUrl.replace(regex, 'http$1://$2'); - console.log({ connectionUrl, normalizedConnectionUrl }) - setConnectionUrl(normalizedConnectionUrl); - setUrlEditing(false); - setUrlValid(false); - }, [connectionUrl, setConnectionUrl]); - - useEffect(() => { - if (!urlEditing) { - llm.getContextLength().then(length => { - setUrlValid(length > 0); - }); - } - }, [connectionUrl, urlEditing]); - - return ( -
- -
- ); -} \ No newline at end of file diff --git a/src/games/ai/components/header/header.module.css b/src/games/ai/components/header/header.module.css new file mode 100644 index 0000000..865fd77 --- /dev/null +++ b/src/games/ai/components/header/header.module.css @@ -0,0 +1,29 @@ +.header { + display: flex; + flex-direction: row; + justify-content: space-between; + height: 36px; + width: 100%; + border: var(--border); + + >input { + &.valid { + background-color: var(--green); + } + + &.invalid { + background-color: var(--red); + } + } + + .buttons { + display: flex; + flex-direction: row; + gap: 8px; + padding: 0 8px; + } +} + +.modalTitle { + margin-top: 0; +} \ No newline at end of file diff --git a/src/games/ai/components/header/header.tsx b/src/games/ai/components/header/header.tsx new file mode 100644 index 0000000..567eded --- /dev/null +++ b/src/games/ai/components/header/header.tsx @@ -0,0 +1,79 @@ +import { useCallback, useContext, useEffect, useRef, useState } from "preact/hooks"; +import { useBool } from "@common/hooks/useBool"; +import { Modal } from "@common/components/modal/modal"; + +import { StateContext } from "../../contexts/state"; +import { LLMContext } from "../../contexts/llm"; +import { MiniChat } from "../minichat/minichat"; + +import styles from './header.module.css'; +import { DOMTools } from "../../dom"; + +export const Header = () => { + const llm = useContext(LLMContext); + const { messages, connectionUrl, lore, setConnectionUrl, setLore, addSwipe } = useContext(StateContext); + const [urlValid, setUrlValid] = useState(false); + const [urlEditing, setUrlEditing] = useState(false); + + const loreAreaRef = useRef(null); + + const loreOpen = useBool(); + const assistantOpen = useBool(); + + const handleFocusUrl = useCallback(() => setUrlEditing(true), []); + + const handleBlurUrl = useCallback(() => { + const regex = /^(?:http(s?):\/\/)?(.*?)\/?$/i + const normalizedConnectionUrl = connectionUrl.replace(regex, 'http$1://$2'); + console.log({ connectionUrl, normalizedConnectionUrl }) + setConnectionUrl(normalizedConnectionUrl); + setUrlEditing(false); + setUrlValid(false); + }, [connectionUrl, setConnectionUrl]); + + useEffect(() => { + if (!urlEditing) { + llm.getContextLength().then(length => { + setUrlValid(length > 0); + }); + } + }, [connectionUrl, urlEditing]); + + useEffect(() => { + DOMTools.fixHeight(loreAreaRef.current); + }, [lore, loreOpen.value]); + + const handleAssistantAddSwipe = useCallback((answer: string) => { + const index = messages.findLastIndex(m => m.role === 'assistant'); + addSwipe(index, answer); + assistantOpen.setFalse(); + }, [addSwipe, messages]); + + return ( +
+ +
+ + +
+ +

Lore Editor

+