From b97925a27cff4ec44333ab872c2f825ec4d2af83 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Wed, 17 Apr 2024 21:49:22 -0400 Subject: [PATCH 1/7] initial project setup --- PersonalWebsite.sln | 12 ++ PersonalWebsite/PersonalWebsite.csproj | 2 +- PersonalWebsite/wwwroot/css/app.css | 2 - .../Components/StarryBackground.razor | 7 + .../Components/StarryBackground.razor.css | 78 ++++++++++ .../Site.Client/Components/Terminal.razor | 15 ++ .../Site.Client/Components/Terminal.razor.css | 80 ++++++++++ .../Components/TerminalInput.razor | 38 +++++ .../Components/TerminalInput.razor.css | 14 ++ ...VeryRealisticAnimationOfMyselfAndJax.razor | 11 ++ ...RealisticAnimationOfMyselfAndJax.razor.css | 142 ++++++++++++++++++ src/Site/Site.Client/Pages/Counter.razor | 19 +++ src/Site/Site.Client/Program.cs | 5 + src/Site/Site.Client/Site.Client.csproj | 15 ++ src/Site/Site.Client/_Imports.razor | 11 ++ .../wwwroot/appsettings.Development.json | 8 + src/Site/Site.Client/wwwroot/appsettings.json | 8 + src/Site/Site/Components/App.razor | 22 +++ .../Site/Components/Layout/MainLayout.razor | 9 ++ .../Components/Layout/MainLayout.razor.css | 18 +++ src/Site/Site/Components/Pages/Error.razor | 36 +++++ src/Site/Site/Components/Pages/Home.razor | 24 +++ src/Site/Site/Components/Pages/Home.razor.css | 26 ++++ src/Site/Site/Components/Pages/Weather.razor | 64 ++++++++ src/Site/Site/Components/Routes.razor | 6 + src/Site/Site/Components/TerminalChat.razor | 58 +++++++ src/Site/Site/Components/_Imports.razor | 12 ++ src/Site/Site/Program.cs | 35 +++++ src/Site/Site/Properties/launchSettings.json | 41 +++++ src/Site/Site/Site.csproj | 14 ++ src/Site/Site/appsettings.Development.json | 8 + src/Site/Site/appsettings.json | 9 ++ src/Site/Site/wwwroot/app.css | 34 +++++ src/Site/Site/wwwroot/favicon.png | Bin 0 -> 1148 bytes .../Site/wwwroot/lottie/cat_sleeping.json | 1 + .../Site/wwwroot/lottie/woman_working.json | 1 + 36 files changed, 882 insertions(+), 3 deletions(-) create mode 100644 src/Site/Site.Client/Components/StarryBackground.razor create mode 100644 src/Site/Site.Client/Components/StarryBackground.razor.css create mode 100644 src/Site/Site.Client/Components/Terminal.razor create mode 100644 src/Site/Site.Client/Components/Terminal.razor.css create mode 100644 src/Site/Site.Client/Components/TerminalInput.razor create mode 100644 src/Site/Site.Client/Components/TerminalInput.razor.css create mode 100644 src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor create mode 100644 src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor.css create mode 100644 src/Site/Site.Client/Pages/Counter.razor create mode 100644 src/Site/Site.Client/Program.cs create mode 100644 src/Site/Site.Client/Site.Client.csproj create mode 100644 src/Site/Site.Client/_Imports.razor create mode 100644 src/Site/Site.Client/wwwroot/appsettings.Development.json create mode 100644 src/Site/Site.Client/wwwroot/appsettings.json create mode 100644 src/Site/Site/Components/App.razor create mode 100644 src/Site/Site/Components/Layout/MainLayout.razor create mode 100644 src/Site/Site/Components/Layout/MainLayout.razor.css create mode 100644 src/Site/Site/Components/Pages/Error.razor create mode 100644 src/Site/Site/Components/Pages/Home.razor create mode 100644 src/Site/Site/Components/Pages/Home.razor.css create mode 100644 src/Site/Site/Components/Pages/Weather.razor create mode 100644 src/Site/Site/Components/Routes.razor create mode 100644 src/Site/Site/Components/TerminalChat.razor create mode 100644 src/Site/Site/Components/_Imports.razor create mode 100644 src/Site/Site/Program.cs create mode 100644 src/Site/Site/Properties/launchSettings.json create mode 100644 src/Site/Site/Site.csproj create mode 100644 src/Site/Site/appsettings.Development.json create mode 100644 src/Site/Site/appsettings.json create mode 100644 src/Site/Site/wwwroot/app.css create mode 100644 src/Site/Site/wwwroot/favicon.png create mode 100644 src/Site/Site/wwwroot/lottie/cat_sleeping.json create mode 100644 src/Site/Site/wwwroot/lottie/woman_working.json diff --git a/PersonalWebsite.sln b/PersonalWebsite.sln index ac92acf..173eda7 100644 --- a/PersonalWebsite.sln +++ b/PersonalWebsite.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PersonalWebsite", "PersonalWebsite\PersonalWebsite.csproj", "{85090680-1997-49A8-BAB0-2283BD353DC8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Site", "src\Site\Site\Site.csproj", "{478039A1-B85B-4EF9-943E-D2EEEB522919}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Site.Client", "src\Site\Site.Client\Site.Client.csproj", "{905FA50A-C616-4339-BFE1-D68CA9C262A8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +19,14 @@ Global {85090680-1997-49A8-BAB0-2283BD353DC8}.Debug|Any CPU.Build.0 = Debug|Any CPU {85090680-1997-49A8-BAB0-2283BD353DC8}.Release|Any CPU.ActiveCfg = Release|Any CPU {85090680-1997-49A8-BAB0-2283BD353DC8}.Release|Any CPU.Build.0 = Release|Any CPU + {478039A1-B85B-4EF9-943E-D2EEEB522919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {478039A1-B85B-4EF9-943E-D2EEEB522919}.Debug|Any CPU.Build.0 = Debug|Any CPU + {478039A1-B85B-4EF9-943E-D2EEEB522919}.Release|Any CPU.ActiveCfg = Release|Any CPU + {478039A1-B85B-4EF9-943E-D2EEEB522919}.Release|Any CPU.Build.0 = Release|Any CPU + {905FA50A-C616-4339-BFE1-D68CA9C262A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {905FA50A-C616-4339-BFE1-D68CA9C262A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {905FA50A-C616-4339-BFE1-D68CA9C262A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {905FA50A-C616-4339-BFE1-D68CA9C262A8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PersonalWebsite/PersonalWebsite.csproj b/PersonalWebsite/PersonalWebsite.csproj index b9ab117..970a6ad 100644 --- a/PersonalWebsite/PersonalWebsite.csproj +++ b/PersonalWebsite/PersonalWebsite.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/PersonalWebsite/wwwroot/css/app.css b/PersonalWebsite/wwwroot/css/app.css index 25c14f7..c360e32 100644 --- a/PersonalWebsite/wwwroot/css/app.css +++ b/PersonalWebsite/wwwroot/css/app.css @@ -1,6 +1,5 @@ :root { --woman-width: 550px; - } * { @@ -15,7 +14,6 @@ html, body, #app { html, body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; - background-color: var(#f1f5f9); } html { diff --git a/src/Site/Site.Client/Components/StarryBackground.razor b/src/Site/Site.Client/Components/StarryBackground.razor new file mode 100644 index 0000000..342823a --- /dev/null +++ b/src/Site/Site.Client/Components/StarryBackground.razor @@ -0,0 +1,7 @@ +
+
+
+
+
+
+
diff --git a/src/Site/Site.Client/Components/StarryBackground.razor.css b/src/Site/Site.Client/Components/StarryBackground.razor.css new file mode 100644 index 0000000..d21b7b5 --- /dev/null +++ b/src/Site/Site.Client/Components/StarryBackground.razor.css @@ -0,0 +1,78 @@ +.background-container { + background-image: linear-gradient(to top, #30cfd0 0%, #330867 100%); + width: 100%; + height: 100%; + position: fixed; +} + +.stars-container { + width: 100%; + max-width: 2000px; + height: 100%; + margin: auto; +} + +/* Star animations from CodePen: https://codepen.io/sarazond/pen/LYGbwj */ +#stars { + width: 1px; + height: 1px; + background: transparent; + box-shadow: 536px 840px #FFF, 1494px 1370px #FFF, 5px 1432px #FFF, 1042px 751px #FFF, 603px 356px #FFF, 1328px 1383px #FFF, 1776px 1680px #FFF, 175px 1080px #FFF, 530px 150px #FFF, 1974px 1026px #FFF, 514px 1633px #FFF, 921px 929px #FFF, 869px 1072px #FFF, 843px 1316px #FFF, 528px 1027px #FFF, 1286px 1112px #FFF, 1956px 1486px #FFF, 1644px 1062px #FFF, 408px 634px #FFF, 1507px 891px #FFF, 974px 739px #FFF, 585px 54px #FFF, 100px 1574px #FFF, 1018px 1800px #FFF, 1475px 1814px #FFF, 1230px 114px #FFF, 1444px 896px #FFF, 757px 757px #FFF, 1371px 342px #FFF, 734px 496px #FFF, 922px 1153px #FFF, 1856px 111px #FFF, 1698px 765px #FFF, 973px 1458px #FFF, 1201px 1536px #FFF, 5px 680px #FFF, 870px 546px #FFF, 289px 4px #FFF, 1132px 709px #FFF, 1766px 642px #FFF, 166px 1860px #FFF, 639px 1324px #FFF, 1331px 1555px #FFF, 1629px 669px #FFF, 1374px 1663px #FFF, 1433px 629px #FFF, 212px 1986px #FFF, 1922px 402px #FFF, 815px 1631px #FFF, 1255px 636px #FFF, 561px 1600px #FFF, 832px 639px #FFF, 605px 418px #FFF, 1176px 435px #FFF, 1253px 400px #FFF, 1809px 31px #FFF, 1530px 1551px #FFF, 523px 1175px #FFF, 1080px 1017px #FFF, 212px 1232px #FFF, 1782px 966px #FFF, 1917px 1551px #FFF, 300px 329px #FFF, 1427px 1515px #FFF, 33px 1807px #FFF, 1459px 1166px #FFF, 1229px 1619px #FFF, 278px 201px #FFF, 1823px 832px #FFF, 961px 1610px #FFF, 282px 332px #FFF, 1087px 1523px #FFF, 951px 1196px #FFF, 985px 82px #FFF, 834px 672px #FFF, 650px 1964px #FFF, 362px 182px #FFF, 1082px 610px #FFF, 1442px 174px #FFF, 794px 875px #FFF, 363px 1486px #FFF, 406px 1592px #FFF, 1745px 1681px #FFF, 217px 1304px #FFF, 236px 1666px #FFF, 474px 266px #FFF, 1948px 1617px #FFF, 1534px 1749px #FFF, 1506px 639px #FFF, 1125px 28px #FFF, 842px 192px #FFF, 1477px 1609px #FFF, 1538px 1544px #FFF, 973px 1801px #FFF, 969px 1973px #FFF, 1057px 842px #FFF, 9px 1119px #FFF, 1714px 1845px #FFF, 790px 300px #FFF, 283px 1137px #FFF, 1453px 441px #FFF, 54px 1992px #FFF, 1590px 1603px #FFF, 142px 140px #FFF, 1078px 1241px #FFF, 1307px 1586px #FFF, 278px 313px #FFF, 386px 456px #FFF, 878px 1299px #FFF, 1471px 1022px #FFF, 1770px 1963px #FFF, 55px 380px #FFF, 24px 1571px #FFF, 1301px 1666px #FFF, 1979px 545px #FFF, 1409px 490px #FFF, 1283px 1190px #FFF, 556px 344px #FFF, 1452px 1373px #FFF, 626px 1264px #FFF, 168px 928px #FFF, 395px 454px #FFF, 550px 1165px #FFF, 1533px 1739px #FFF, 1674px 94px #FFF, 1495px 947px #FFF, 763px 879px #FFF, 1140px 1201px #FFF, 548px 1836px #FFF, 1121px 1132px #FFF, 1856px 1286px #FFF, 474px 1540px #FFF, 44px 23px #FFF, 215px 1089px #FFF, 1294px 430px #FFF, 748px 129px #FFF, 1772px 779px #FFF, 551px 159px #FFF, 1301px 1707px #FFF, 1561px 1582px #FFF, 1999px 384px #FFF, 776px 348px #FFF, 621px 1168px #FFF, 1629px 1855px #FFF, 837px 1839px #FFF, 1363px 120px #FFF, 1294px 480px #FFF, 1124px 617px #FFF, 449px 497px #FFF, 290px 614px #FFF, 1143px 682px #FFF, 732px 810px #FFF, 773px 1361px #FFF, 16px 1444px #FFF, 1292px 1129px #FFF, 44px 221px #FFF, 313px 176px #FFF, 1835px 393px #FFF, 312px 844px #FFF, 114px 215px #FFF, 1424px 406px #FFF, 952px 612px #FFF, 1694px 1555px #FFF, 1777px 902px #FFF, 1214px 1076px #FFF, 1227px 490px #FFF, 1021px 14px #FFF, 786px 884px #FFF, 1173px 1545px #FFF, 1849px 28px #FFF, 723px 409px #FFF, 1423px 204px #FFF, 410px 1747px #FFF, 641px 1486px #FFF, 896px 1947px #FFF, 646px 135px #FFF, 1832px 1287px #FFF, 1963px 1336px #FFF, 811px 1193px #FFF, 414px 1314px #FFF, 1114px 447px #FFF, 792px 1479px #FFF, 643px 1296px #FFF, 1940px 1825px #FFF, 1048px 1268px #FFF, 626px 1081px #FFF, 908px 652px #FFF, 1633px 1711px #FFF, 574px 1866px #FFF, 74px 1831px #FFF, 38px 680px #FFF, 478px 181px #FFF, 1054px 928px #FFF, 964px 342px #FFF, 1315px 1836px #FFF, 417px 724px #FFF, 442px 1681px #FFF, 541px 1153px #FFF, 987px 1822px #FFF, 516px 1344px #FFF, 545px 1729px #FFF, 109px 445px #FFF, 618px 1708px #FFF, 1233px 1298px #FFF, 715px 1717px #FFF, 469px 1858px #FFF, 238px 1613px #FFF, 1631px 354px #FFF, 62px 1485px #FFF, 18px 380px #FFF, 366px 214px #FFF, 539px 948px #FFF, 846px 1745px #FFF, 17px 693px #FFF, 1475px 1131px #FFF, 1480px 1333px #FFF, 564px 331px #FFF, 1022px 479px #FFF, 1936px 149px #FFF, 166px 768px #FFF, 674px 1541px #FFF, 1041px 1268px #FFF, 810px 244px #FFF, 579px 1112px #FFF, 817px 1670px #FFF, 676px 318px #FFF, 1882px 1178px #FFF, 801px 47px #FFF, 881px 1173px #FFF, 60px 1996px #FFF, 1973px 1902px #FFF, 923px 295px #FFF, 1228px 1560px #FFF, 319px 1827px #FFF, 385px 1139px #FFF, 797px 274px #FFF, 419px 1114px #FFF, 1153px 79px #FFF, 1965px 888px #FFF, 408px 188px #FFF, 1735px 1430px #FFF, 160px 3px #FFF, 147px 213px #FFF, 1685px 1417px #FFF, 1452px 1242px #FFF, 798px 1331px #FFF, 1882px 1478px #FFF, 1530px 173px #FFF, 1609px 188px #FFF, 257px 265px #FFF, 267px 523px #FFF, 339px 215px #FFF, 1831px 1986px #FFF, 1096px 1771px #FFF, 20px 1748px #FFF, 833px 759px #FFF, 687px 1324px #FFF, 1637px 1906px #FFF, 652px 179px #FFF, 1145px 1547px #FFF, 1904px 304px #FFF, 1413px 1810px #FFF, 1908px 499px #FFF, 1919px 1546px #FFF, 898px 685px #FFF, 589px 32px #FFF, 1046px 1232px #FFF, 1054px 1802px #FFF, 1135px 1990px #FFF, 1311px 878px #FFF, 1445px 1697px #FFF, 1784px 803px #FFF, 1427px 1718px #FFF, 1300px 394px #FFF, 347px 1661px #FFF, 264px 499px #FFF, 67px 1009px #FFF, 878px 234px #FFF, 54px 446px #FFF, 308px 1766px #FFF, 1221px 280px #FFF, 101px 1479px #FFF, 832px 155px #FFF, 479px 309px #FFF, 1676px 102px #FFF, 449px 1185px #FFF, 393px 320px #FFF, 146px 1476px #FFF, 1455px 456px #FFF, 79px 1494px #FFF, 926px 24px #FFF, 1824px 428px #FFF, 82px 1227px #FFF, 369px 321px #FFF, 1767px 1841px #FFF, 1539px 1113px #FFF, 343px 368px #FFF, 1775px 103px #FFF, 1412px 25px #FFF, 1185px 377px #FFF, 717px 1642px #FFF, 1508px 265px #FFF, 1137px 665px #FFF, 1817px 316px #FFF, 1818px 1442px #FFF, 1524px 1262px #FFF, 468px 1332px #FFF, 239px 88px #FFF, 1480px 111px #FFF, 1400px 1086px #FFF, 1002px 183px #FFF, 1183px 830px #FFF, 1076px 867px #FFF, 15px 1760px #FFF, 1938px 975px #FFF, 1875px 1528px #FFF, 841px 1511px #FFF, 727px 1859px #FFF, 941px 1048px #FFF, 689px 978px #FFF, 1993px 1081px #FFF, 1180px 99px #FFF, 1205px 1628px #FFF, 1688px 287px #FFF, 372px 586px #FFF, 1096px 1945px #FFF, 1873px 229px #FFF, 1940px 476px #FFF, 1867px 1670px #FFF, 1333px 394px #FFF, 1233px 927px #FFF, 659px 1322px #FFF, 1553px 296px #FFF, 1976px 1307px #FFF, 131px 884px #FFF, 1486px 553px #FFF, 158px 401px #FFF, 616px 312px #FFF, 1267px 1519px #FFF, 666px 526px #FFF, 1715px 1854px #FFF, 1177px 941px #FFF, 879px 760px #FFF, 1025px 1934px #FFF, 128px 169px #FFF, 462px 1054px #FFF, 1016px 1782px #FFF, 1894px 225px #FFF, 93px 173px #FFF, 470px 1951px #FFF, 1379px 1336px #FFF, 202px 1099px #FFF, 20px 780px #FFF, 1332px 153px #FFF, 789px 226px #FFF, 1452px 375px #FFF, 868px 1675px #FFF, 144px 1489px #FFF, 1301px 161px #FFF, 17px 1149px #FFF, 719px 1178px #FFF, 251px 73px #FFF, 1138px 1573px #FFF, 1810px 364px #FFF, 171px 1250px #FFF, 310px 1178px #FFF, 1039px 1622px #FFF, 915px 1350px #FFF, 1336px 442px #FFF, 302px 397px #FFF, 52px 970px #FFF, 1107px 1324px #FFF, 569px 1992px #FFF, 1263px 1998px #FFF, 1944px 893px #FFF, 958px 699px #FFF, 1073px 583px #FFF, 596px 343px #FFF, 99px 1976px #FFF, 1494px 772px #FFF, 1607px 778px #FFF, 1925px 889px #FFF, 90px 1570px #FFF, 1122px 326px #FFF, 1564px 1935px #FFF, 1017px 22px #FFF, 1654px 1445px #FFF, 1060px 1146px #FFF, 1353px 189px #FFF, 1136px 1386px #FFF, 1403px 815px #FFF, 364px 1555px #FFF, 1695px 1616px #FFF, 1757px 258px #FFF, 28px 565px #FFF, 84px 407px #FFF, 1464px 16px #FFF, 636px 1214px #FFF, 867px 260px #FFF, 1445px 1731px #FFF, 103px 8px #FFF, 132px 1637px #FFF, 1435px 1245px #FFF, 1381px 200px #FFF, 666px 221px #FFF, 368px 876px #FFF, 1193px 1195px #FFF, 1775px 1384px #FFF, 854px 350px #FFF, 1881px 329px #FFF, 1124px 970px #FFF, 1272px 368px #FFF, 1633px 1993px #FFF, 421px 289px #FFF, 1343px 1333px #FFF, 1410px 1388px #FFF, 563px 456px #FFF, 1662px 752px #FFF, 1554px 795px #FFF, 597px 1447px #FFF, 871px 1843px #FFF, 366px 1125px #FFF, 364px 694px #FFF, 839px 86px #FFF, 1754px 1963px #FFF, 1562px 1822px #FFF, 198px 1734px #FFF, 1974px 605px #FFF, 1065px 41px #FFF, 1546px 347px #FFF, 1829px 1493px #FFF, 276px 953px #FFF, 1844px 842px #FFF, 372px 191px #FFF, 873px 1377px #FFF, 814px 58px #FFF, 236px 45px #FFF, 1714px 1670px #FFF, 497px 393px #FFF, 1947px 1796px #FFF, 1523px 1258px #FFF, 20px 1831px #FFF, 1021px 1278px #FFF, 344px 72px #FFF, 1999px 172px #FFF, 1585px 463px #FFF, 222px 1388px #FFF, 1548px 1267px #FFF, 1268px 1872px #FFF, 1069px 833px #FFF, 225px 1974px #FFF, 154px 1170px #FFF, 394px 819px #FFF, 1967px 678px #FFF, 602px 1008px #FFF, 25px 87px #FFF, 916px 1888px #FFF, 1085px 631px #FFF, 273px 1812px #FFF, 660px 498px #FFF, 1536px 1172px #FFF, 231px 1703px #FFF, 1434px 885px #FFF, 188px 1762px #FFF, 52px 1689px #FFF, 1142px 27px #FFF, 1007px 1865px #FFF, 1784px 1693px #FFF, 1529px 524px #FFF, 153px 690px #FFF, 1179px 287px #FFF, 1042px 1283px #FFF, 440px 620px #FFF, 1459px 1468px #FFF, 1458px 1897px #FFF, 1909px 886px #FFF, 1122px 613px #FFF, 507px 1046px #FFF, 353px 703px #FFF, 173px 1262px #FFF, 1609px 461px #FFF, 1337px 1669px #FFF, 967px 1556px #FFF, 75px 1868px #FFF, 477px 1917px #FFF, 926px 269px #FFF, 202px 74px #FFF, 639px 231px #FFF, 334px 294px #FFF, 1904px 150px #FFF, 747px 743px #FFF, 79px 1268px #FFF, 295px 1294px #FFF, 531px 1061px #FFF, 1448px 1588px #FFF, 1095px 170px #FFF, 55px 929px #FFF, 1192px 1957px #FFF, 690px 639px #FFF, 189px 299px #FFF, 677px 113px #FFF, 1670px 1473px #FFF, 669px 1134px #FFF, 1606px 80px #FFF, 707px 296px #FFF, 245px 1147px #FFF, 1610px 443px #FFF, 171px 1112px #FFF, 1909px 1125px #FFF, 129px 768px #FFF, 59px 1133px #FFF, 893px 412px #FFF, 1598px 523px #FFF, 110px 656px #FFF, 1263px 532px #FFF, 1002px 206px #FFF, 109px 321px #FFF, 1304px 855px #FFF, 801px 604px #FFF, 1924px 835px #FFF, 130px 512px #FFF, 735px 327px #FFF, 1255px 648px #FFF, 670px 189px #FFF, 1884px 1968px #FFF, 1343px 911px #FFF, 1207px 661px #FFF, 1599px 853px #FFF, 1255px 1007px #FFF, 930px 1053px #FFF, 1217px 1606px #FFF, 323px 332px #FFF, 244px 1578px #FFF, 1048px 106px #FFF, 1530px 1216px #FFF, 883px 569px #FFF, 1202px 1520px #FFF, 1899px 1095px #FFF, 145px 647px #FFF, 855px 270px #FFF, 1537px 1303px #FFF, 1620px 1077px #FFF, 202px 428px #FFF, 1626px 464px #FFF, 1580px 65px #FFF, 1855px 961px #FFF, 1091px 1930px #FFF, 520px 1479px #FFF, 1016px 577px #FFF, 1002px 517px #FFF, 1743px 978px #FFF, 303px 1096px #FFF, 637px 1961px #FFF, 1954px 1719px #FFF, 1205px 1324px #FFF, 94px 1172px #FFF, 51px 1794px #FFF, 788px 1086px #FFF, 33px 343px #FFF, 1323px 106px #FFF, 659px 1604px #FFF, 1149px 413px #FFF, 232px 1951px #FFF, 1779px 1352px #FFF, 420px 844px #FFF, 315px 432px #FFF, 1763px 93px #FFF, 1274px 714px #FFF, 298px 237px #FFF, 237px 696px #FFF, 357px 1535px #FFF, 351px 809px #FFF, 1023px 392px #FFF, 588px 1225px #FFF, 21px 985px #FFF, 991px 1079px #FFF, 634px 1340px #FFF, 1004px 1907px #FFF, 463px 1794px #FFF, 1409px 593px #FFF, 1916px 1761px #FFF, 1028px 821px #FFF, 1480px 1173px #FFF, 683px 1172px #FFF, 1286px 650px #FFF, 1307px 922px #FFF, 64px 1924px #FFF, 1203px 1621px #FFF, 1157px 1422px #FFF, 703px 933px #FFF, 1229px 1075px #FFF, 1556px 705px #FFF, 1121px 660px #FFF, 105px 1080px #FFF, 532px 1087px #FFF, 1066px 56px #FFF, 572px 295px #FFF, 1658px 915px #FFF, 670px 905px #FFF, 618px 385px #FFF, 1690px 1917px #FFF, 636px 687px #FFF, 695px 856px #FFF, 1541px 1434px #FFF, 1433px 1955px #FFF, 1899px 1014px #FFF, 846px 739px #FFF, 1607px 1986px #FFF, 442px 495px #FFF, 1370px 1047px #FFF, 661px 1022px #FFF, 1517px 719px #FFF, 1490px 129px #FFF, 154px 900px #FFF, 116px 735px #FFF, 1725px 1312px #FFF, 1624px 379px #FFF, 638px 177px #FFF, 1568px 1535px #FFF, 484px 1541px #FFF, 1333px 1917px #FFF, 1631px 284px #FFF, 127px 1980px #FFF, 949px 13px #FFF, 1265px 728px #FFF, 773px 1794px #FFF, 1210px 1440px #FFF, 1498px 1797px #FFF, 1060px 1998px #FFF, 545px 1909px #FFF, 132px 799px #FFF, 1273px 1611px #FFF, 220px 121px #FFF, 66px 719px #FFF, 1709px 1399px #FFF, 477px 498px #FFF, 1546px 174px #FFF, 1644px 1015px #FFF, 496px 994px #FFF, 968px 784px #FFF, 1481px 1723px #FFF, 1252px 876px #FFF, 646px 1991px #FFF, 10px 1048px #FFF, 1514px 1463px #FFF, 754px 1802px #FFF, 1568px 742px #FFF, 1169px 796px #FFF, 1494px 357px #FFF, 1513px 1312px #FFF, 190px 169px #FFF, 1310px 1468px #FFF, 443px 734px #FFF, 1921px 439px #FFF, 1375px 967px #FFF, 1465px 763px #FFF, 1737px 1140px #FFF, 404px 1279px #FFF, 130px 1321px #FFF, 1420px 606px #FFF, 1675px 186px #FFF, 1372px 211px #FFF, 856px 1724px #FFF, 995px 1674px #FFF, 768px 544px #FFF, 619px 212px #FFF, 124px 971px #FFF, 750px 723px #FFF, 446px 687px #FFF, 106px 559px #FFF, 1461px 848px #FFF, 1232px 908px #FFF, 1998px 119px #FFF, 1361px 175px #FFF, 861px 1062px #FFF, 974px 1318px #FFF, 219px 1281px #FFF, 828px 1214px #FFF, 1491px 1696px #FFF, 1873px 945px #FFF, 330px 1136px #FFF, 1785px 143px #FFF, 1079px 1605px #FFF, 1317px 1856px #FFF, 977px 1602px #FFF, 601px 1026px #FFF, 1864px 1528px #FFF, 1668px 1261px #FFF, 470px 1303px #FFF, 481px 262px #FFF, 1060px 252px #FFF, 1794px 904px #FFF, 1203px 172px #FFF, 993px 780px #FFF, 258px 509px #FFF, 1993px 75px #FFF, 1365px 1039px #FFF, 1568px 1633px #FFF, 1537px 1525px #FFF, 1342px 1775px #FFF; + animation: animStar 50s linear infinite; +} + + #stars:after { + content: " "; + position: absolute; + top: 2000px; + width: 1px; + height: 1px; + background: transparent; + box-shadow: 536px 840px #FFF, 1494px 1370px #FFF, 5px 1432px #FFF, 1042px 751px #FFF, 603px 356px #FFF, 1328px 1383px #FFF, 1776px 1680px #FFF, 175px 1080px #FFF, 530px 150px #FFF, 1974px 1026px #FFF, 514px 1633px #FFF, 921px 929px #FFF, 869px 1072px #FFF, 843px 1316px #FFF, 528px 1027px #FFF, 1286px 1112px #FFF, 1956px 1486px #FFF, 1644px 1062px #FFF, 408px 634px #FFF, 1507px 891px #FFF, 974px 739px #FFF, 585px 54px #FFF, 100px 1574px #FFF, 1018px 1800px #FFF, 1475px 1814px #FFF, 1230px 114px #FFF, 1444px 896px #FFF, 757px 757px #FFF, 1371px 342px #FFF, 734px 496px #FFF, 922px 1153px #FFF, 1856px 111px #FFF, 1698px 765px #FFF, 973px 1458px #FFF, 1201px 1536px #FFF, 5px 680px #FFF, 870px 546px #FFF, 289px 4px #FFF, 1132px 709px #FFF, 1766px 642px #FFF, 166px 1860px #FFF, 639px 1324px #FFF, 1331px 1555px #FFF, 1629px 669px #FFF, 1374px 1663px #FFF, 1433px 629px #FFF, 212px 1986px #FFF, 1922px 402px #FFF, 815px 1631px #FFF, 1255px 636px #FFF, 561px 1600px #FFF, 832px 639px #FFF, 605px 418px #FFF, 1176px 435px #FFF, 1253px 400px #FFF, 1809px 31px #FFF, 1530px 1551px #FFF, 523px 1175px #FFF, 1080px 1017px #FFF, 212px 1232px #FFF, 1782px 966px #FFF, 1917px 1551px #FFF, 300px 329px #FFF, 1427px 1515px #FFF, 33px 1807px #FFF, 1459px 1166px #FFF, 1229px 1619px #FFF, 278px 201px #FFF, 1823px 832px #FFF, 961px 1610px #FFF, 282px 332px #FFF, 1087px 1523px #FFF, 951px 1196px #FFF, 985px 82px #FFF, 834px 672px #FFF, 650px 1964px #FFF, 362px 182px #FFF, 1082px 610px #FFF, 1442px 174px #FFF, 794px 875px #FFF, 363px 1486px #FFF, 406px 1592px #FFF, 1745px 1681px #FFF, 217px 1304px #FFF, 236px 1666px #FFF, 474px 266px #FFF, 1948px 1617px #FFF, 1534px 1749px #FFF, 1506px 639px #FFF, 1125px 28px #FFF, 842px 192px #FFF, 1477px 1609px #FFF, 1538px 1544px #FFF, 973px 1801px #FFF, 969px 1973px #FFF, 1057px 842px #FFF, 9px 1119px #FFF, 1714px 1845px #FFF, 790px 300px #FFF, 283px 1137px #FFF, 1453px 441px #FFF, 54px 1992px #FFF, 1590px 1603px #FFF, 142px 140px #FFF, 1078px 1241px #FFF, 1307px 1586px #FFF, 278px 313px #FFF, 386px 456px #FFF, 878px 1299px #FFF, 1471px 1022px #FFF, 1770px 1963px #FFF, 55px 380px #FFF, 24px 1571px #FFF, 1301px 1666px #FFF, 1979px 545px #FFF, 1409px 490px #FFF, 1283px 1190px #FFF, 556px 344px #FFF, 1452px 1373px #FFF, 626px 1264px #FFF, 168px 928px #FFF, 395px 454px #FFF, 550px 1165px #FFF, 1533px 1739px #FFF, 1674px 94px #FFF, 1495px 947px #FFF, 763px 879px #FFF, 1140px 1201px #FFF, 548px 1836px #FFF, 1121px 1132px #FFF, 1856px 1286px #FFF, 474px 1540px #FFF, 44px 23px #FFF, 215px 1089px #FFF, 1294px 430px #FFF, 748px 129px #FFF, 1772px 779px #FFF, 551px 159px #FFF, 1301px 1707px #FFF, 1561px 1582px #FFF, 1999px 384px #FFF, 776px 348px #FFF, 621px 1168px #FFF, 1629px 1855px #FFF, 837px 1839px #FFF, 1363px 120px #FFF, 1294px 480px #FFF, 1124px 617px #FFF, 449px 497px #FFF, 290px 614px #FFF, 1143px 682px #FFF, 732px 810px #FFF, 773px 1361px #FFF, 16px 1444px #FFF, 1292px 1129px #FFF, 44px 221px #FFF, 313px 176px #FFF, 1835px 393px #FFF, 312px 844px #FFF, 114px 215px #FFF, 1424px 406px #FFF, 952px 612px #FFF, 1694px 1555px #FFF, 1777px 902px #FFF, 1214px 1076px #FFF, 1227px 490px #FFF, 1021px 14px #FFF, 786px 884px #FFF, 1173px 1545px #FFF, 1849px 28px #FFF, 723px 409px #FFF, 1423px 204px #FFF, 410px 1747px #FFF, 641px 1486px #FFF, 896px 1947px #FFF, 646px 135px #FFF, 1832px 1287px #FFF, 1963px 1336px #FFF, 811px 1193px #FFF, 414px 1314px #FFF, 1114px 447px #FFF, 792px 1479px #FFF, 643px 1296px #FFF, 1940px 1825px #FFF, 1048px 1268px #FFF, 626px 1081px #FFF, 908px 652px #FFF, 1633px 1711px #FFF, 574px 1866px #FFF, 74px 1831px #FFF, 38px 680px #FFF, 478px 181px #FFF, 1054px 928px #FFF, 964px 342px #FFF, 1315px 1836px #FFF, 417px 724px #FFF, 442px 1681px #FFF, 541px 1153px #FFF, 987px 1822px #FFF, 516px 1344px #FFF, 545px 1729px #FFF, 109px 445px #FFF, 618px 1708px #FFF, 1233px 1298px #FFF, 715px 1717px #FFF, 469px 1858px #FFF, 238px 1613px #FFF, 1631px 354px #FFF, 62px 1485px #FFF, 18px 380px #FFF, 366px 214px #FFF, 539px 948px #FFF, 846px 1745px #FFF, 17px 693px #FFF, 1475px 1131px #FFF, 1480px 1333px #FFF, 564px 331px #FFF, 1022px 479px #FFF, 1936px 149px #FFF, 166px 768px #FFF, 674px 1541px #FFF, 1041px 1268px #FFF, 810px 244px #FFF, 579px 1112px #FFF, 817px 1670px #FFF, 676px 318px #FFF, 1882px 1178px #FFF, 801px 47px #FFF, 881px 1173px #FFF, 60px 1996px #FFF, 1973px 1902px #FFF, 923px 295px #FFF, 1228px 1560px #FFF, 319px 1827px #FFF, 385px 1139px #FFF, 797px 274px #FFF, 419px 1114px #FFF, 1153px 79px #FFF, 1965px 888px #FFF, 408px 188px #FFF, 1735px 1430px #FFF, 160px 3px #FFF, 147px 213px #FFF, 1685px 1417px #FFF, 1452px 1242px #FFF, 798px 1331px #FFF, 1882px 1478px #FFF, 1530px 173px #FFF, 1609px 188px #FFF, 257px 265px #FFF, 267px 523px #FFF, 339px 215px #FFF, 1831px 1986px #FFF, 1096px 1771px #FFF, 20px 1748px #FFF, 833px 759px #FFF, 687px 1324px #FFF, 1637px 1906px #FFF, 652px 179px #FFF, 1145px 1547px #FFF, 1904px 304px #FFF, 1413px 1810px #FFF, 1908px 499px #FFF, 1919px 1546px #FFF, 898px 685px #FFF, 589px 32px #FFF, 1046px 1232px #FFF, 1054px 1802px #FFF, 1135px 1990px #FFF, 1311px 878px #FFF, 1445px 1697px #FFF, 1784px 803px #FFF, 1427px 1718px #FFF, 1300px 394px #FFF, 347px 1661px #FFF, 264px 499px #FFF, 67px 1009px #FFF, 878px 234px #FFF, 54px 446px #FFF, 308px 1766px #FFF, 1221px 280px #FFF, 101px 1479px #FFF, 832px 155px #FFF, 479px 309px #FFF, 1676px 102px #FFF, 449px 1185px #FFF, 393px 320px #FFF, 146px 1476px #FFF, 1455px 456px #FFF, 79px 1494px #FFF, 926px 24px #FFF, 1824px 428px #FFF, 82px 1227px #FFF, 369px 321px #FFF, 1767px 1841px #FFF, 1539px 1113px #FFF, 343px 368px #FFF, 1775px 103px #FFF, 1412px 25px #FFF, 1185px 377px #FFF, 717px 1642px #FFF, 1508px 265px #FFF, 1137px 665px #FFF, 1817px 316px #FFF, 1818px 1442px #FFF, 1524px 1262px #FFF, 468px 1332px #FFF, 239px 88px #FFF, 1480px 111px #FFF, 1400px 1086px #FFF, 1002px 183px #FFF, 1183px 830px #FFF, 1076px 867px #FFF, 15px 1760px #FFF, 1938px 975px #FFF, 1875px 1528px #FFF, 841px 1511px #FFF, 727px 1859px #FFF, 941px 1048px #FFF, 689px 978px #FFF, 1993px 1081px #FFF, 1180px 99px #FFF, 1205px 1628px #FFF, 1688px 287px #FFF, 372px 586px #FFF, 1096px 1945px #FFF, 1873px 229px #FFF, 1940px 476px #FFF, 1867px 1670px #FFF, 1333px 394px #FFF, 1233px 927px #FFF, 659px 1322px #FFF, 1553px 296px #FFF, 1976px 1307px #FFF, 131px 884px #FFF, 1486px 553px #FFF, 158px 401px #FFF, 616px 312px #FFF, 1267px 1519px #FFF, 666px 526px #FFF, 1715px 1854px #FFF, 1177px 941px #FFF, 879px 760px #FFF, 1025px 1934px #FFF, 128px 169px #FFF, 462px 1054px #FFF, 1016px 1782px #FFF, 1894px 225px #FFF, 93px 173px #FFF, 470px 1951px #FFF, 1379px 1336px #FFF, 202px 1099px #FFF, 20px 780px #FFF, 1332px 153px #FFF, 789px 226px #FFF, 1452px 375px #FFF, 868px 1675px #FFF, 144px 1489px #FFF, 1301px 161px #FFF, 17px 1149px #FFF, 719px 1178px #FFF, 251px 73px #FFF, 1138px 1573px #FFF, 1810px 364px #FFF, 171px 1250px #FFF, 310px 1178px #FFF, 1039px 1622px #FFF, 915px 1350px #FFF, 1336px 442px #FFF, 302px 397px #FFF, 52px 970px #FFF, 1107px 1324px #FFF, 569px 1992px #FFF, 1263px 1998px #FFF, 1944px 893px #FFF, 958px 699px #FFF, 1073px 583px #FFF, 596px 343px #FFF, 99px 1976px #FFF, 1494px 772px #FFF, 1607px 778px #FFF, 1925px 889px #FFF, 90px 1570px #FFF, 1122px 326px #FFF, 1564px 1935px #FFF, 1017px 22px #FFF, 1654px 1445px #FFF, 1060px 1146px #FFF, 1353px 189px #FFF, 1136px 1386px #FFF, 1403px 815px #FFF, 364px 1555px #FFF, 1695px 1616px #FFF, 1757px 258px #FFF, 28px 565px #FFF, 84px 407px #FFF, 1464px 16px #FFF, 636px 1214px #FFF, 867px 260px #FFF, 1445px 1731px #FFF, 103px 8px #FFF, 132px 1637px #FFF, 1435px 1245px #FFF, 1381px 200px #FFF, 666px 221px #FFF, 368px 876px #FFF, 1193px 1195px #FFF, 1775px 1384px #FFF, 854px 350px #FFF, 1881px 329px #FFF, 1124px 970px #FFF, 1272px 368px #FFF, 1633px 1993px #FFF, 421px 289px #FFF, 1343px 1333px #FFF, 1410px 1388px #FFF, 563px 456px #FFF, 1662px 752px #FFF, 1554px 795px #FFF, 597px 1447px #FFF, 871px 1843px #FFF, 366px 1125px #FFF, 364px 694px #FFF, 839px 86px #FFF, 1754px 1963px #FFF, 1562px 1822px #FFF, 198px 1734px #FFF, 1974px 605px #FFF, 1065px 41px #FFF, 1546px 347px #FFF, 1829px 1493px #FFF, 276px 953px #FFF, 1844px 842px #FFF, 372px 191px #FFF, 873px 1377px #FFF, 814px 58px #FFF, 236px 45px #FFF, 1714px 1670px #FFF, 497px 393px #FFF, 1947px 1796px #FFF, 1523px 1258px #FFF, 20px 1831px #FFF, 1021px 1278px #FFF, 344px 72px #FFF, 1999px 172px #FFF, 1585px 463px #FFF, 222px 1388px #FFF, 1548px 1267px #FFF, 1268px 1872px #FFF, 1069px 833px #FFF, 225px 1974px #FFF, 154px 1170px #FFF, 394px 819px #FFF, 1967px 678px #FFF, 602px 1008px #FFF, 25px 87px #FFF, 916px 1888px #FFF, 1085px 631px #FFF, 273px 1812px #FFF, 660px 498px #FFF, 1536px 1172px #FFF, 231px 1703px #FFF, 1434px 885px #FFF, 188px 1762px #FFF, 52px 1689px #FFF, 1142px 27px #FFF, 1007px 1865px #FFF, 1784px 1693px #FFF, 1529px 524px #FFF, 153px 690px #FFF, 1179px 287px #FFF, 1042px 1283px #FFF, 440px 620px #FFF, 1459px 1468px #FFF, 1458px 1897px #FFF, 1909px 886px #FFF, 1122px 613px #FFF, 507px 1046px #FFF, 353px 703px #FFF, 173px 1262px #FFF, 1609px 461px #FFF, 1337px 1669px #FFF, 967px 1556px #FFF, 75px 1868px #FFF, 477px 1917px #FFF, 926px 269px #FFF, 202px 74px #FFF, 639px 231px #FFF, 334px 294px #FFF, 1904px 150px #FFF, 747px 743px #FFF, 79px 1268px #FFF, 295px 1294px #FFF, 531px 1061px #FFF, 1448px 1588px #FFF, 1095px 170px #FFF, 55px 929px #FFF, 1192px 1957px #FFF, 690px 639px #FFF, 189px 299px #FFF, 677px 113px #FFF, 1670px 1473px #FFF, 669px 1134px #FFF, 1606px 80px #FFF, 707px 296px #FFF, 245px 1147px #FFF, 1610px 443px #FFF, 171px 1112px #FFF, 1909px 1125px #FFF, 129px 768px #FFF, 59px 1133px #FFF, 893px 412px #FFF, 1598px 523px #FFF, 110px 656px #FFF, 1263px 532px #FFF, 1002px 206px #FFF, 109px 321px #FFF, 1304px 855px #FFF, 801px 604px #FFF, 1924px 835px #FFF, 130px 512px #FFF, 735px 327px #FFF, 1255px 648px #FFF, 670px 189px #FFF, 1884px 1968px #FFF, 1343px 911px #FFF, 1207px 661px #FFF, 1599px 853px #FFF, 1255px 1007px #FFF, 930px 1053px #FFF, 1217px 1606px #FFF, 323px 332px #FFF, 244px 1578px #FFF, 1048px 106px #FFF, 1530px 1216px #FFF, 883px 569px #FFF, 1202px 1520px #FFF, 1899px 1095px #FFF, 145px 647px #FFF, 855px 270px #FFF, 1537px 1303px #FFF, 1620px 1077px #FFF, 202px 428px #FFF, 1626px 464px #FFF, 1580px 65px #FFF, 1855px 961px #FFF, 1091px 1930px #FFF, 520px 1479px #FFF, 1016px 577px #FFF, 1002px 517px #FFF, 1743px 978px #FFF, 303px 1096px #FFF, 637px 1961px #FFF, 1954px 1719px #FFF, 1205px 1324px #FFF, 94px 1172px #FFF, 51px 1794px #FFF, 788px 1086px #FFF, 33px 343px #FFF, 1323px 106px #FFF, 659px 1604px #FFF, 1149px 413px #FFF, 232px 1951px #FFF, 1779px 1352px #FFF, 420px 844px #FFF, 315px 432px #FFF, 1763px 93px #FFF, 1274px 714px #FFF, 298px 237px #FFF, 237px 696px #FFF, 357px 1535px #FFF, 351px 809px #FFF, 1023px 392px #FFF, 588px 1225px #FFF, 21px 985px #FFF, 991px 1079px #FFF, 634px 1340px #FFF, 1004px 1907px #FFF, 463px 1794px #FFF, 1409px 593px #FFF, 1916px 1761px #FFF, 1028px 821px #FFF, 1480px 1173px #FFF, 683px 1172px #FFF, 1286px 650px #FFF, 1307px 922px #FFF, 64px 1924px #FFF, 1203px 1621px #FFF, 1157px 1422px #FFF, 703px 933px #FFF, 1229px 1075px #FFF, 1556px 705px #FFF, 1121px 660px #FFF, 105px 1080px #FFF, 532px 1087px #FFF, 1066px 56px #FFF, 572px 295px #FFF, 1658px 915px #FFF, 670px 905px #FFF, 618px 385px #FFF, 1690px 1917px #FFF, 636px 687px #FFF, 695px 856px #FFF, 1541px 1434px #FFF, 1433px 1955px #FFF, 1899px 1014px #FFF, 846px 739px #FFF, 1607px 1986px #FFF, 442px 495px #FFF, 1370px 1047px #FFF, 661px 1022px #FFF, 1517px 719px #FFF, 1490px 129px #FFF, 154px 900px #FFF, 116px 735px #FFF, 1725px 1312px #FFF, 1624px 379px #FFF, 638px 177px #FFF, 1568px 1535px #FFF, 484px 1541px #FFF, 1333px 1917px #FFF, 1631px 284px #FFF, 127px 1980px #FFF, 949px 13px #FFF, 1265px 728px #FFF, 773px 1794px #FFF, 1210px 1440px #FFF, 1498px 1797px #FFF, 1060px 1998px #FFF, 545px 1909px #FFF, 132px 799px #FFF, 1273px 1611px #FFF, 220px 121px #FFF, 66px 719px #FFF, 1709px 1399px #FFF, 477px 498px #FFF, 1546px 174px #FFF, 1644px 1015px #FFF, 496px 994px #FFF, 968px 784px #FFF, 1481px 1723px #FFF, 1252px 876px #FFF, 646px 1991px #FFF, 10px 1048px #FFF, 1514px 1463px #FFF, 754px 1802px #FFF, 1568px 742px #FFF, 1169px 796px #FFF, 1494px 357px #FFF, 1513px 1312px #FFF, 190px 169px #FFF, 1310px 1468px #FFF, 443px 734px #FFF, 1921px 439px #FFF, 1375px 967px #FFF, 1465px 763px #FFF, 1737px 1140px #FFF, 404px 1279px #FFF, 130px 1321px #FFF, 1420px 606px #FFF, 1675px 186px #FFF, 1372px 211px #FFF, 856px 1724px #FFF, 995px 1674px #FFF, 768px 544px #FFF, 619px 212px #FFF, 124px 971px #FFF, 750px 723px #FFF, 446px 687px #FFF, 106px 559px #FFF, 1461px 848px #FFF, 1232px 908px #FFF, 1998px 119px #FFF, 1361px 175px #FFF, 861px 1062px #FFF, 974px 1318px #FFF, 219px 1281px #FFF, 828px 1214px #FFF, 1491px 1696px #FFF, 1873px 945px #FFF, 330px 1136px #FFF, 1785px 143px #FFF, 1079px 1605px #FFF, 1317px 1856px #FFF, 977px 1602px #FFF, 601px 1026px #FFF, 1864px 1528px #FFF, 1668px 1261px #FFF, 470px 1303px #FFF, 481px 262px #FFF, 1060px 252px #FFF, 1794px 904px #FFF, 1203px 172px #FFF, 993px 780px #FFF, 258px 509px #FFF, 1993px 75px #FFF, 1365px 1039px #FFF, 1568px 1633px #FFF, 1537px 1525px #FFF, 1342px 1775px #FFF; + } + +#stars2 { + width: 2px; + height: 2px; + background: transparent; + box-shadow: 427px 120px #FFF, 1321px 1612px #FFF, 1406px 571px #FFF, 382px 1111px #FFF, 1306px 1378px #FFF, 1252px 216px #FFF, 1534px 545px #FFF, 1704px 1682px #FFF, 1681px 188px #FFF, 1651px 1520px #FFF, 377px 1563px #FFF, 1915px 911px #FFF, 322px 1137px #FFF, 1053px 324px #FFF, 1729px 1027px #FFF, 1755px 70px #FFF, 906px 379px #FFF, 1536px 1604px #FFF, 1863px 583px #FFF, 852px 726px #FFF, 1733px 966px #FFF, 1736px 110px #FFF, 1780px 612px #FFF, 281px 1825px #FFF, 719px 1975px #FFF, 1218px 1681px #FFF, 79px 1170px #FFF, 1043px 365px #FFF, 1230px 209px #FFF, 1238px 1924px #FFF, 340px 1892px #FFF, 1347px 875px #FFF, 1491px 942px #FFF, 1743px 165px #FFF, 1350px 220px #FFF, 545px 420px #FFF, 1596px 1597px #FFF, 181px 1753px #FFF, 1615px 529px #FFF, 1473px 1363px #FFF, 1478px 455px #FFF, 447px 484px #FFF, 413px 1672px #FFF, 907px 1646px #FFF, 1012px 355px #FFF, 1434px 604px #FFF, 936px 788px #FFF, 314px 1561px #FFF, 1353px 891px #FFF, 1306px 1176px #FFF, 1269px 427px #FFF, 1887px 1494px #FFF, 895px 1373px #FFF, 909px 33px #FFF, 1369px 1140px #FFF, 1498px 475px #FFF, 1960px 1424px #FFF, 857px 9px #FFF, 1485px 272px #FFF, 1246px 624px #FFF, 1763px 930px #FFF, 1063px 605px #FFF, 865px 916px #FFF, 1168px 704px #FFF, 323px 999px #FFF, 729px 845px #FFF, 201px 1589px #FFF, 1626px 1497px #FFF, 1579px 370px #FFF, 1380px 1059px #FFF, 754px 892px #FFF, 140px 613px #FFF, 149px 1897px #FFF, 57px 1760px #FFF, 396px 765px #FFF, 465px 855px #FFF, 991px 1626px #FFF, 1178px 538px #FFF, 760px 1439px #FFF, 1325px 1022px #FFF, 1683px 1336px #FFF, 1233px 973px #FFF, 211px 508px #FFF, 1524px 1500px #FFF, 1105px 63px #FFF, 165px 1316px #FFF, 1581px 87px #FFF, 140px 1881px #FFF, 888px 1121px #FFF, 1335px 186px #FFF, 1327px 643px #FFF, 60px 1375px #FFF, 1677px 915px #FFF, 8px 1741px #FFF, 1322px 94px #FFF, 1430px 1153px #FFF, 870px 579px #FFF, 1886px 1519px #FFF, 191px 71px #FFF, 218px 602px #FFF, 766px 1273px #FFF, 1118px 1284px #FFF, 1112px 444px #FFF, 1646px 506px #FFF, 1483px 578px #FFF, 994px 1443px #FFF, 549px 936px #FFF, 125px 526px #FFF, 478px 1994px #FFF, 948px 1339px #FFF, 1591px 1231px #FFF, 1440px 1774px #FFF, 1919px 1369px #FFF, 1832px 1336px #FFF, 1156px 1908px #FFF, 274px 839px #FFF, 336px 830px #FFF, 1251px 1463px #FFF, 934px 627px #FFF, 724px 1824px #FFF, 301px 1427px #FFF, 1006px 1014px #FFF, 984px 134px #FFF, 361px 260px #FFF, 119px 1284px #FFF, 120px 1190px #FFF, 1157px 787px #FFF, 1644px 1255px #FFF, 1506px 1216px #FFF, 1558px 611px #FFF, 313px 286px #FFF, 1706px 262px #FFF, 1874px 1173px #FFF, 921px 997px #FFF, 216px 1302px #FFF, 1500px 1731px #FFF, 1678px 983px #FFF, 841px 670px #FFF, 364px 1223px #FFF, 1502px 901px #FFF, 1855px 235px #FFF, 1264px 523px #FFF, 829px 570px #FFF, 1238px 747px #FFF, 1255px 1330px #FFF, 765px 847px #FFF, 1153px 1209px #FFF, 805px 588px #FFF, 1815px 1189px #FFF, 900px 589px #FFF, 201px 980px #FFF, 627px 1734px #FFF, 1044px 480px #FFF, 95px 727px #FFF, 1027px 1979px #FFF, 1491px 837px #FFF, 1771px 1668px #FFF, 1904px 1868px #FFF, 73px 122px #FFF, 473px 826px #FFF, 1019px 1207px #FFF, 460px 526px #FFF, 1646px 1994px #FFF, 637px 181px #FFF, 316px 329px #FFF, 1735px 1009px #FFF, 220px 651px #FFF, 1715px 1134px #FFF, 1909px 512px #FFF, 1954px 1136px #FFF, 700px 175px #FFF, 677px 1448px #FFF, 498px 32px #FFF, 802px 1384px #FFF, 1678px 307px #FFF, 106px 212px #FFF, 211px 1682px #FFF, 1052px 495px #FFF, 150px 1327px #FFF, 911px 1728px #FFF, 633px 189px #FFF, 1195px 537px #FFF, 723px 1165px #FFF, 405px 73px #FFF, 1946px 667px #FFF, 1412px 480px #FFF, 359px 1946px #FFF, 106px 442px #FFF, 40px 868px #FFF, 1253px 1207px #FFF, 1681px 319px #FFF, 473px 1344px #FFF, 635px 1508px #FFF, 1947px 171px #FFF, 1187px 212px #FFF, 659px 1088px #FFF, 1964px 1023px #FFF, 1830px 1446px #FFF, 1856px 1644px #FFF, 1818px 574px #FFF; + animation: animStar 100s linear infinite; +} + + #stars2:after { + content: " "; + position: absolute; + top: 2000px; + width: 2px; + height: 2px; + background: transparent; + box-shadow: 427px 120px #FFF, 1321px 1612px #FFF, 1406px 571px #FFF, 382px 1111px #FFF, 1306px 1378px #FFF, 1252px 216px #FFF, 1534px 545px #FFF, 1704px 1682px #FFF, 1681px 188px #FFF, 1651px 1520px #FFF, 377px 1563px #FFF, 1915px 911px #FFF, 322px 1137px #FFF, 1053px 324px #FFF, 1729px 1027px #FFF, 1755px 70px #FFF, 906px 379px #FFF, 1536px 1604px #FFF, 1863px 583px #FFF, 852px 726px #FFF, 1733px 966px #FFF, 1736px 110px #FFF, 1780px 612px #FFF, 281px 1825px #FFF, 719px 1975px #FFF, 1218px 1681px #FFF, 79px 1170px #FFF, 1043px 365px #FFF, 1230px 209px #FFF, 1238px 1924px #FFF, 340px 1892px #FFF, 1347px 875px #FFF, 1491px 942px #FFF, 1743px 165px #FFF, 1350px 220px #FFF, 545px 420px #FFF, 1596px 1597px #FFF, 181px 1753px #FFF, 1615px 529px #FFF, 1473px 1363px #FFF, 1478px 455px #FFF, 447px 484px #FFF, 413px 1672px #FFF, 907px 1646px #FFF, 1012px 355px #FFF, 1434px 604px #FFF, 936px 788px #FFF, 314px 1561px #FFF, 1353px 891px #FFF, 1306px 1176px #FFF, 1269px 427px #FFF, 1887px 1494px #FFF, 895px 1373px #FFF, 909px 33px #FFF, 1369px 1140px #FFF, 1498px 475px #FFF, 1960px 1424px #FFF, 857px 9px #FFF, 1485px 272px #FFF, 1246px 624px #FFF, 1763px 930px #FFF, 1063px 605px #FFF, 865px 916px #FFF, 1168px 704px #FFF, 323px 999px #FFF, 729px 845px #FFF, 201px 1589px #FFF, 1626px 1497px #FFF, 1579px 370px #FFF, 1380px 1059px #FFF, 754px 892px #FFF, 140px 613px #FFF, 149px 1897px #FFF, 57px 1760px #FFF, 396px 765px #FFF, 465px 855px #FFF, 991px 1626px #FFF, 1178px 538px #FFF, 760px 1439px #FFF, 1325px 1022px #FFF, 1683px 1336px #FFF, 1233px 973px #FFF, 211px 508px #FFF, 1524px 1500px #FFF, 1105px 63px #FFF, 165px 1316px #FFF, 1581px 87px #FFF, 140px 1881px #FFF, 888px 1121px #FFF, 1335px 186px #FFF, 1327px 643px #FFF, 60px 1375px #FFF, 1677px 915px #FFF, 8px 1741px #FFF, 1322px 94px #FFF, 1430px 1153px #FFF, 870px 579px #FFF, 1886px 1519px #FFF, 191px 71px #FFF, 218px 602px #FFF, 766px 1273px #FFF, 1118px 1284px #FFF, 1112px 444px #FFF, 1646px 506px #FFF, 1483px 578px #FFF, 994px 1443px #FFF, 549px 936px #FFF, 125px 526px #FFF, 478px 1994px #FFF, 948px 1339px #FFF, 1591px 1231px #FFF, 1440px 1774px #FFF, 1919px 1369px #FFF, 1832px 1336px #FFF, 1156px 1908px #FFF, 274px 839px #FFF, 336px 830px #FFF, 1251px 1463px #FFF, 934px 627px #FFF, 724px 1824px #FFF, 301px 1427px #FFF, 1006px 1014px #FFF, 984px 134px #FFF, 361px 260px #FFF, 119px 1284px #FFF, 120px 1190px #FFF, 1157px 787px #FFF, 1644px 1255px #FFF, 1506px 1216px #FFF, 1558px 611px #FFF, 313px 286px #FFF, 1706px 262px #FFF, 1874px 1173px #FFF, 921px 997px #FFF, 216px 1302px #FFF, 1500px 1731px #FFF, 1678px 983px #FFF, 841px 670px #FFF, 364px 1223px #FFF, 1502px 901px #FFF, 1855px 235px #FFF, 1264px 523px #FFF, 829px 570px #FFF, 1238px 747px #FFF, 1255px 1330px #FFF, 765px 847px #FFF, 1153px 1209px #FFF, 805px 588px #FFF, 1815px 1189px #FFF, 900px 589px #FFF, 201px 980px #FFF, 627px 1734px #FFF, 1044px 480px #FFF, 95px 727px #FFF, 1027px 1979px #FFF, 1491px 837px #FFF, 1771px 1668px #FFF, 1904px 1868px #FFF, 73px 122px #FFF, 473px 826px #FFF, 1019px 1207px #FFF, 460px 526px #FFF, 1646px 1994px #FFF, 637px 181px #FFF, 316px 329px #FFF, 1735px 1009px #FFF, 220px 651px #FFF, 1715px 1134px #FFF, 1909px 512px #FFF, 1954px 1136px #FFF, 700px 175px #FFF, 677px 1448px #FFF, 498px 32px #FFF, 802px 1384px #FFF, 1678px 307px #FFF, 106px 212px #FFF, 211px 1682px #FFF, 1052px 495px #FFF, 150px 1327px #FFF, 911px 1728px #FFF, 633px 189px #FFF, 1195px 537px #FFF, 723px 1165px #FFF, 405px 73px #FFF, 1946px 667px #FFF, 1412px 480px #FFF, 359px 1946px #FFF, 106px 442px #FFF, 40px 868px #FFF, 1253px 1207px #FFF, 1681px 319px #FFF, 473px 1344px #FFF, 635px 1508px #FFF, 1947px 171px #FFF, 1187px 212px #FFF, 659px 1088px #FFF, 1964px 1023px #FFF, 1830px 1446px #FFF, 1856px 1644px #FFF, 1818px 574px #FFF; + } + +#stars3 { + width: 3px; + height: 3px; + background: transparent; + box-shadow: 640px 1993px #FFF, 200px 1561px #FFF, 1434px 1790px #FFF, 725px 596px #FFF, 723px 109px #FFF, 1543px 1316px #FFF, 1635px 705px #FFF, 308px 1053px #FFF, 1527px 520px #FFF, 554px 1520px #FFF, 733px 59px #FFF, 1884px 311px #FFF, 33px 14px #FFF, 188px 670px #FFF, 1705px 1539px #FFF, 1295px 767px #FFF, 1150px 497px #FFF, 1378px 1174px #FFF, 1016px 1082px #FFF, 1539px 1336px #FFF, 212px 1348px #FFF, 1127px 1852px #FFF, 647px 1427px #FFF, 346px 1124px #FFF, 1766px 1048px #FFF, 438px 668px #FFF, 1866px 1264px #FFF, 649px 245px #FFF, 1503px 1611px #FFF, 1294px 1711px #FFF, 404px 1575px #FFF, 1336px 63px #FFF, 699px 568px #FFF, 1703px 203px #FFF, 330px 617px #FFF, 448px 380px #FFF, 174px 805px #FFF, 245px 938px #FFF, 1270px 916px #FFF, 1653px 642px #FFF, 1953px 1682px #FFF, 1478px 1110px #FFF, 206px 1582px #FFF, 162px 136px #FFF, 329px 1641px #FFF, 1623px 1007px #FFF, 1549px 630px #FFF, 348px 1513px #FFF, 1188px 1362px #FFF, 1476px 53px #FFF, 310px 1831px #FFF, 863px 1504px #FFF, 1168px 1151px #FFF, 1353px 548px #FFF, 1063px 1117px #FFF, 163px 882px #FFF, 335px 831px #FFF, 1960px 684px #FFF, 382px 1603px #FFF, 1213px 415px #FFF, 1346px 349px #FFF, 1114px 866px #FFF, 717px 1438px #FFF, 753px 713px #FFF, 853px 1521px #FFF, 236px 1244px #FFF, 575px 1609px #FFF, 1177px 949px #FFF, 567px 1953px #FFF, 1926px 1377px #FFF, 735px 1669px #FFF, 872px 752px #FFF, 1200px 723px #FFF, 1685px 1881px #FFF, 675px 333px #FFF, 1367px 780px #FFF, 725px 732px #FFF, 745px 367px #FFF, 1549px 1243px #FFF, 833px 979px #FFF, 1353px 1676px #FFF, 690px 478px #FFF, 1886px 48px #FFF, 530px 42px #FFF, 1760px 478px #FFF, 133px 1078px #FFF, 275px 1241px #FFF, 1896px 1151px #FFF, 1254px 1331px #FFF, 367px 1789px #FFF, 744px 1456px #FFF, 922px 1714px #FFF, 908px 1309px #FFF, 1758px 665px #FFF, 1398px 1182px #FFF, 865px 972px #FFF, 297px 1920px #FFF, 1387px 1435px #FFF, 381px 795px #FFF, 1365px 906px #FFF; + animation: animStar 150s linear infinite; +} + + #stars3:after { + content: " "; + position: absolute; + top: 2000px; + width: 3px; + height: 3px; + background: transparent; + box-shadow: 640px 1993px #FFF, 200px 1561px #FFF, 1434px 1790px #FFF, 725px 596px #FFF, 723px 109px #FFF, 1543px 1316px #FFF, 1635px 705px #FFF, 308px 1053px #FFF, 1527px 520px #FFF, 554px 1520px #FFF, 733px 59px #FFF, 1884px 311px #FFF, 33px 14px #FFF, 188px 670px #FFF, 1705px 1539px #FFF, 1295px 767px #FFF, 1150px 497px #FFF, 1378px 1174px #FFF, 1016px 1082px #FFF, 1539px 1336px #FFF, 212px 1348px #FFF, 1127px 1852px #FFF, 647px 1427px #FFF, 346px 1124px #FFF, 1766px 1048px #FFF, 438px 668px #FFF, 1866px 1264px #FFF, 649px 245px #FFF, 1503px 1611px #FFF, 1294px 1711px #FFF, 404px 1575px #FFF, 1336px 63px #FFF, 699px 568px #FFF, 1703px 203px #FFF, 330px 617px #FFF, 448px 380px #FFF, 174px 805px #FFF, 245px 938px #FFF, 1270px 916px #FFF, 1653px 642px #FFF, 1953px 1682px #FFF, 1478px 1110px #FFF, 206px 1582px #FFF, 162px 136px #FFF, 329px 1641px #FFF, 1623px 1007px #FFF, 1549px 630px #FFF, 348px 1513px #FFF, 1188px 1362px #FFF, 1476px 53px #FFF, 310px 1831px #FFF, 863px 1504px #FFF, 1168px 1151px #FFF, 1353px 548px #FFF, 1063px 1117px #FFF, 163px 882px #FFF, 335px 831px #FFF, 1960px 684px #FFF, 382px 1603px #FFF, 1213px 415px #FFF, 1346px 349px #FFF, 1114px 866px #FFF, 717px 1438px #FFF, 753px 713px #FFF, 853px 1521px #FFF, 236px 1244px #FFF, 575px 1609px #FFF, 1177px 949px #FFF, 567px 1953px #FFF, 1926px 1377px #FFF, 735px 1669px #FFF, 872px 752px #FFF, 1200px 723px #FFF, 1685px 1881px #FFF, 675px 333px #FFF, 1367px 780px #FFF, 725px 732px #FFF, 745px 367px #FFF, 1549px 1243px #FFF, 833px 979px #FFF, 1353px 1676px #FFF, 690px 478px #FFF, 1886px 48px #FFF, 530px 42px #FFF, 1760px 478px #FFF, 133px 1078px #FFF, 275px 1241px #FFF, 1896px 1151px #FFF, 1254px 1331px #FFF, 367px 1789px #FFF, 744px 1456px #FFF, 922px 1714px #FFF, 908px 1309px #FFF, 1758px 665px #FFF, 1398px 1182px #FFF, 865px 972px #FFF, 297px 1920px #FFF, 1387px 1435px #FFF, 381px 795px #FFF, 1365px 906px #FFF; + } + +@keyframes animStar { + from { + transform: translateY(0px); + } + + to { + transform: translateY(-2000px); + } +} diff --git a/src/Site/Site.Client/Components/Terminal.razor b/src/Site/Site.Client/Components/Terminal.razor new file mode 100644 index 0000000..5cfac89 --- /dev/null +++ b/src/Site/Site.Client/Components/Terminal.razor @@ -0,0 +1,15 @@ +
+
+
+
+
+
+
+ @ChildContent +
+
+ +@code { + [Parameter] + public RenderFragment? ChildContent { get; set; } +} diff --git a/src/Site/Site.Client/Components/Terminal.razor.css b/src/Site/Site.Client/Components/Terminal.razor.css new file mode 100644 index 0000000..369f0b8 --- /dev/null +++ b/src/Site/Site.Client/Components/Terminal.razor.css @@ -0,0 +1,80 @@ +.terminal-window { + position: absolute; + top: 140px; + left: 0.5rem; + right: 0.5rem; + bottom: 0.5rem; + box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); +} + + .terminal-window header { + background: #E0E8F0; + height: 30px; + border-radius: 8px 8px 0 0; + padding-right: 10px; + text-align: right; + } + + .terminal-window header .button { + width: 12px; + height: 12px; + margin: 10px 4px 0 0; + display: inline-block; + border-radius: 8px; + } + + .terminal-window header .button.green { + background: #3BB662; + } + + .terminal-window header .button.yellow { + background: #E5C30F; + } + + .terminal-window header .button.red { + background: #E75448; + } + + .terminal-window section.terminal { + background: #30353A; + box-sizing: border-box; + position: absolute; + width: 100%; + top: 30px; + bottom: 0; + overflow-y: auto; + overflow-x: hidden; + border-radius: 0 0 8px 8px; + display: flex; + flex-direction: column; + gap: 0.5em; + padding: 0.5em; + } + + .terminal-window section.terminal, + .terminal-window section.terminal ::deep input { + color: white; + font-size: 11pt; + font-family: Menlo, Monaco, "Consolas", "Courier New", "Courier"; + } + +.terminal-data { + display: none; +} + +.terminal-window .gray { + color: gray; +} + +.terminal-window .green { + color: green; +} + +@media only screen and (min-width: 600px) { + .terminal-window { + top: 183px; + left: 1rem; + right: 1rem; + bottom: 2rem; + } +} diff --git a/src/Site/Site.Client/Components/TerminalInput.razor b/src/Site/Site.Client/Components/TerminalInput.razor new file mode 100644 index 0000000..7407627 --- /dev/null +++ b/src/Site/Site.Client/Components/TerminalInput.razor @@ -0,0 +1,38 @@ +@rendermode InteractiveAuto + +
+ + + + + +
+ +@code { + + private InputText? _inputField; + + [Required] + private string Message { get; set; } = string.Empty; + + [Parameter] + public EventCallback OnUserInputReceived { get; set; } + + protected override async void OnAfterRender(bool firstRender) + { + if (firstRender) + await FocusInputFieldAsync(); + } + + private async Task FocusInputFieldAsync() + { + if (_inputField?.Element is not null) + await _inputField.Element.Value.FocusAsync(); + } + + private async Task SubmitAsync() + { + await OnUserInputReceived.InvokeAsync(Message); + } + +} diff --git a/src/Site/Site.Client/Components/TerminalInput.razor.css b/src/Site/Site.Client/Components/TerminalInput.razor.css new file mode 100644 index 0000000..3e29fa8 --- /dev/null +++ b/src/Site/Site.Client/Components/TerminalInput.razor.css @@ -0,0 +1,14 @@ +div, +div ::deep form { + cursor: text; + height: 100%; +} + + div ::deep input { + outline: transparent; + border: 0; + background-color: transparent; + width: 100%; + resize: none; + padding: 0; + } diff --git a/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor new file mode 100644 index 0000000..f4d5400 --- /dev/null +++ b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor @@ -0,0 +1,11 @@ +
+
+
z
+
z
+
+ +
+ +
+ +
diff --git a/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor.css b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor.css new file mode 100644 index 0000000..400d81f --- /dev/null +++ b/src/Site/Site.Client/Components/VeryRealisticAnimationOfMyselfAndJax.razor.css @@ -0,0 +1,142 @@ +.cat-container, +.woman-container { + width: 400px; + height: 302.5px; + margin-top: calc(-0.375 * 302.5px); + position: absolute; + right: 0; + pointer-events: none; +} + +.cat-container { + margin-right: 145px; +} + + .cat-container .snoring-container { + position: absolute; + height: 100px; + width: 100px; + right: 50px; + bottom: 33px; + opacity: 0.6; + } + +.woman-container { + transform: rotateY(180deg); +} + +@media only screen and (min-width: 600px) { + .cat-container, + .woman-container { + width: 550px; + height: 416px; + margin-top: calc(-0.375 * 416px); + } + + .cat-container { + margin-right: 200px; + } +} + +/* zzz animations from CodePen: https://codepen.io/gorenburg/pen/MWwmLYM */ + +.z { + position: absolute; + bottom: 50%; + right: 10%; + animation-name: zzz; + animation-duration: 4s; + animation-delay: 0; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +.zz { + opacity: 0; + position: absolute; + bottom: 50%; + right: 10%; + animation-name: zzz-2; + animation-duration: 4s; + animation-delay: 2s; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes zzz { + 0% { + bottom: 50%; + right: 10%; + opacity: 0; + } + + 20% { + bottom: 65%; + right: 13%; + } + + 40% { + bottom: 80%; + right: 10%; + } + + 50% { + opacity: 1; + } + + 60% { + bottom: 95%; + right: 13%; + } + + 80% { + bottom: 110%; + right: 10%; + } + + 100% { + bottom: 125%; + right: 13%; + opacity: 0; + font-size: 20px; + } +} + +@keyframes zzz-2 { + 0% { + bottom: 50%; + right: 0; + opacity: 0; + } + + 20% { + bottom: 65%; + right: 3%; + } + + 40% { + bottom: 80%; + right: 0; + } + + 50% { + opacity: 1; + } + + 60% { + bottom: 95%; + right: 3%; + } + + 80% { + bottom: 110%; + right: 0; + } + + 100% { + bottom: 125%; + right: 3%; + opacity: 0; + font-size: 20px; + } +} diff --git a/src/Site/Site.Client/Pages/Counter.razor b/src/Site/Site.Client/Pages/Counter.razor new file mode 100644 index 0000000..e7d4f94 --- /dev/null +++ b/src/Site/Site.Client/Pages/Counter.razor @@ -0,0 +1,19 @@ +@page "/counter" +@rendermode InteractiveAuto + +Counter + +

Counter

+ +

Current count: @currentCount

+ + + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Site/Site.Client/Program.cs b/src/Site/Site.Client/Program.cs new file mode 100644 index 0000000..519269f --- /dev/null +++ b/src/Site/Site.Client/Program.cs @@ -0,0 +1,5 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +await builder.Build().RunAsync(); diff --git a/src/Site/Site.Client/Site.Client.csproj b/src/Site/Site.Client/Site.Client.csproj new file mode 100644 index 0000000..22be631 --- /dev/null +++ b/src/Site/Site.Client/Site.Client.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + enable + enable + true + Default + + + + + + + diff --git a/src/Site/Site.Client/_Imports.razor b/src/Site/Site.Client/_Imports.razor new file mode 100644 index 0000000..2ccac9a --- /dev/null +++ b/src/Site/Site.Client/_Imports.razor @@ -0,0 +1,11 @@ +@using System.ComponentModel.DataAnnotations +@using System.Diagnostics.CodeAnalysis +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using Site.Client diff --git a/src/Site/Site.Client/wwwroot/appsettings.Development.json b/src/Site/Site.Client/wwwroot/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Site/Site.Client/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Site/Site.Client/wwwroot/appsettings.json b/src/Site/Site.Client/wwwroot/appsettings.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Site/Site.Client/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Site/Site/Components/App.razor b/src/Site/Site/Components/App.razor new file mode 100644 index 0000000..cb1afa5 --- /dev/null +++ b/src/Site/Site/Components/App.razor @@ -0,0 +1,22 @@ + + + + + + + + erinnmclaughlin + + + + + + + + + + + + + + diff --git a/src/Site/Site/Components/Layout/MainLayout.razor b/src/Site/Site/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..0fd1b20 --- /dev/null +++ b/src/Site/Site/Components/Layout/MainLayout.razor @@ -0,0 +1,9 @@ +@inherits LayoutComponentBase + +@Body + +
+ An unhandled error has occurred. + Reload + 🗙 +
diff --git a/src/Site/Site/Components/Layout/MainLayout.razor.css b/src/Site/Site/Components/Layout/MainLayout.razor.css new file mode 100644 index 0000000..df8c10f --- /dev/null +++ b/src/Site/Site/Components/Layout/MainLayout.razor.css @@ -0,0 +1,18 @@ +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } diff --git a/src/Site/Site/Components/Pages/Error.razor b/src/Site/Site/Components/Pages/Error.razor new file mode 100644 index 0000000..576cc2d --- /dev/null +++ b/src/Site/Site/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/src/Site/Site/Components/Pages/Home.razor b/src/Site/Site/Components/Pages/Home.razor new file mode 100644 index 0000000..3b3cd23 --- /dev/null +++ b/src/Site/Site/Components/Pages/Home.razor @@ -0,0 +1,24 @@ +@page "/" + + + +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/Site/Site/Components/Pages/Home.razor.css b/src/Site/Site/Components/Pages/Home.razor.css new file mode 100644 index 0000000..89c3443 --- /dev/null +++ b/src/Site/Site/Components/Pages/Home.razor.css @@ -0,0 +1,26 @@ +header { + display: flex; + position: relative; + min-width: 290px; + z-index: 10; + height: 185px; + padding: 1.5rem 1rem; + color: white; + font-family: monospace; +} + + header .links { + z-index: 10; /* in front of lottie */ + } + + header .title { + display: block; + text-decoration: none; + font-size: 1.75rem; + font-weight: 700; + margin-bottom: 0.33rem; + } + + header a { + color: white; + } diff --git a/src/Site/Site/Components/Pages/Weather.razor b/src/Site/Site/Components/Pages/Weather.razor new file mode 100644 index 0000000..43a1ecb --- /dev/null +++ b/src/Site/Site/Components/Pages/Weather.razor @@ -0,0 +1,64 @@ +@page "/weather" +@attribute [StreamRendering] + +Weather + +

Weather

+ +

This component demonstrates showing data.

+ +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + + + + + + @foreach (var forecast in forecasts) + { + + + + + + + } + +
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
+} + +@code { + private WeatherForecast[]? forecasts; + + protected override async Task OnInitializedAsync() + { + // Simulate asynchronous loading to demonstrate streaming rendering + await Task.Delay(500); + + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).ToArray(); + } + + private class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/src/Site/Site/Components/Routes.razor b/src/Site/Site/Components/Routes.razor new file mode 100644 index 0000000..d39c7e8 --- /dev/null +++ b/src/Site/Site/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site/Components/TerminalChat.razor new file mode 100644 index 0000000..8587eb1 --- /dev/null +++ b/src/Site/Site/Components/TerminalChat.razor @@ -0,0 +1,58 @@ +@rendermode InteractiveServer +@attribute [StreamRendering] + +@foreach (var message in Messages) +{ +

@message

+} + +@if (State is ChatState.Loading) +{ +

Loading...

+} +else if (State is ChatState.WaitingForAssistant) +{ +

Thinking...

+} +else if (State is ChatState.WaitingForUser) +{ + +} + +@code { + + private int Count { get; set; } = 0; + private List Messages { get; } = []; + private ChatState State { get; set; } = ChatState.Loading; + + protected override void OnInitialized() + { + State = ChatState.WaitingForAssistant; + InvokeAsync(() => RenderAssistantMessage(0)); + } + + private async Task RenderUserMessage(string message) + { + Messages.Add(message); + State = ChatState.WaitingForAssistant; + StateHasChanged(); + + await RenderAssistantMessage(); + } + + private async Task RenderAssistantMessage(int delay = 3000) + { + await Task.Delay(delay); // simulated delay + + Messages.Add($"Message {Count++}"); + State = ChatState.WaitingForUser; + } + + enum ChatState + { + Loading, + WaitingForAssistant, + StreamingAssistantMessage, + WaitingForUser + } +} \ No newline at end of file diff --git a/src/Site/Site/Components/_Imports.razor b/src/Site/Site/Components/_Imports.razor new file mode 100644 index 0000000..e9c9f43 --- /dev/null +++ b/src/Site/Site/Components/_Imports.razor @@ -0,0 +1,12 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using Site +@using Site.Client +@using Site.Components +@using Site.Client.Components diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs new file mode 100644 index 0000000..61036e0 --- /dev/null +++ b/src/Site/Site/Program.cs @@ -0,0 +1,35 @@ +using Site.Client.Pages; +using Site.Components; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseWebAssemblyDebugging(); +} +else +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); +app.UseAntiforgery(); + +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(Site.Client._Imports).Assembly); + +app.Run(); diff --git a/src/Site/Site/Properties/launchSettings.json b/src/Site/Site/Properties/launchSettings.json new file mode 100644 index 0000000..8ccb377 --- /dev/null +++ b/src/Site/Site/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:10230", + "sslPort": 44394 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5172", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7007;http://localhost:5172", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/src/Site/Site/Site.csproj b/src/Site/Site/Site.csproj new file mode 100644 index 0000000..64d037c --- /dev/null +++ b/src/Site/Site/Site.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/src/Site/Site/appsettings.Development.json b/src/Site/Site/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/Site/Site/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Site/Site/appsettings.json b/src/Site/Site/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/Site/Site/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Site/Site/wwwroot/app.css b/src/Site/Site/wwwroot/app.css new file mode 100644 index 0000000..93399e4 --- /dev/null +++ b/src/Site/Site/wwwroot/app.css @@ -0,0 +1,34 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + color-scheme: dark; + background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%); + overflow: hidden; +} + +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + height: 100%; +} + +a { + cursor: pointer; +} + +h1:focus { + outline: none; +} + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::after { + content: "An error has occurred." + } diff --git a/src/Site/Site/wwwroot/favicon.png b/src/Site/Site/wwwroot/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8422b59695935d180d11d5dbe99653e711097819 GIT binary patch literal 1148 zcmV-?1cUpDP)9h26h2-Cs%i*@Moc3?#6qJID|D#|3|2Hn7gTIYEkr|%Xjp);YgvFmB&0#2E2b=| zkVr)lMv9=KqwN&%obTp-$<51T%rx*NCwceh-E+=&e(oLO`@Z~7gybJ#U|^tB2Pai} zRN@5%1qsZ1e@R(XC8n~)nU1S0QdzEYlWPdUpH{wJ2Pd4V8kI3BM=)sG^IkUXF2-j{ zrPTYA6sxpQ`Q1c6mtar~gG~#;lt=s^6_OccmRd>o{*=>)KS=lM zZ!)iG|8G0-9s3VLm`bsa6e ze*TlRxAjXtm^F8V`M1%s5d@tYS>&+_ga#xKGb|!oUBx3uc@mj1%=MaH4GR0tPBG_& z9OZE;->dO@`Q)nr<%dHAsEZRKl zedN6+3+uGHejJp;Q==pskSAcRcyh@6mjm2z-uG;s%dM-u0*u##7OxI7wwyCGpS?4U zBFAr(%GBv5j$jS@@t@iI8?ZqE36I^4t+P^J9D^ELbS5KMtZ z{Qn#JnSd$15nJ$ggkF%I4yUQC+BjDF^}AtB7w348EL>7#sAsLWs}ndp8^DsAcOIL9 zTOO!!0!k2`9BLk25)NeZp7ev>I1Mn={cWI3Yhx2Q#DnAo4IphoV~R^c0x&nw*MoIV zPthX?{6{u}sMS(MxD*dmd5rU(YazQE59b|TsB5Tm)I4a!VaN@HYOR)DwH1U5y(E)z zQqQU*B%MwtRQ$%x&;1p%ANmc|PkoFJZ%<-uq%PX&C!c-7ypis=eP+FCeuv+B@h#{4 zGx1m0PjS~FJt}3mdt4c!lel`1;4W|03kcZRG+DzkTy|7-F~eDsV2Tx!73dM0H0CTh zl)F-YUkE1zEzEW(;JXc|KR5{ox%YTh{$%F$a36JP6Nb<0%#NbSh$dMYF-{ z1_x(Vx)}fs?5_|!5xBTWiiIQHG<%)*e=45Fhjw_tlnmlixq;mUdC$R8v#j( zhQ$9YR-o%i5Uc`S?6EC51!bTRK=Xkyb<18FkCKnS2;o*qlij1YA@-nRpq#OMTX&RbL<^2q@0qja!uIvI;j$6>~k@IMwD42=8$$!+R^@5o6HX(*n~ Date: Wed, 17 Apr 2024 23:07:11 -0400 Subject: [PATCH 2/7] add semantic kernel --- .editorconfig | 7 ++ .../Components/TerminalCursor.razor | 2 +- .../Site.Client/Components/Terminal.razor.css | 4 ++ .../Components/TerminalInput.razor | 3 +- .../Components/TerminalInput.razor.css | 2 + src/Site/Site/AI/AIOptions.cs | 8 +++ src/Site/Site/Components/TerminalChat.razor | 45 ++----------- .../Site/Components/TerminalChat.razor.cs | 67 +++++++++++++++++++ src/Site/Site/Program.cs | 13 ++-- src/Site/Site/Site.csproj | 2 + src/Site/Site/appsettings.json | 8 ++- 11 files changed, 113 insertions(+), 48 deletions(-) create mode 100644 .editorconfig create mode 100644 src/Site/Site/AI/AIOptions.cs create mode 100644 src/Site/Site/Components/TerminalChat.razor.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2f0f0f2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +[*.cs] + +# SKEXP0001: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +dotnet_diagnostic.SKEXP0001.severity = none + +# SKEXP0010: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +dotnet_diagnostic.SKEXP0010.severity = none diff --git a/PersonalWebsite/Components/TerminalCursor.razor b/PersonalWebsite/Components/TerminalCursor.razor index 286844e..170a6c7 100644 --- a/PersonalWebsite/Components/TerminalCursor.razor +++ b/PersonalWebsite/Components/TerminalCursor.razor @@ -1 +1 @@ -_ \ No newline at end of file +@("> ") \ No newline at end of file diff --git a/src/Site/Site.Client/Components/Terminal.razor.css b/src/Site/Site.Client/Components/Terminal.razor.css index 369f0b8..34b07ae 100644 --- a/src/Site/Site.Client/Components/Terminal.razor.css +++ b/src/Site/Site.Client/Components/Terminal.razor.css @@ -58,6 +58,10 @@ font-family: Menlo, Monaco, "Consolas", "Courier New", "Courier"; } + .terminal-window section.terminal ::deep label { + font-weight: 700; + } + .terminal-data { display: none; } diff --git a/src/Site/Site.Client/Components/TerminalInput.razor b/src/Site/Site.Client/Components/TerminalInput.razor index 7407627..a457f4c 100644 --- a/src/Site/Site.Client/Components/TerminalInput.razor +++ b/src/Site/Site.Client/Components/TerminalInput.razor @@ -1,6 +1,7 @@ @rendermode InteractiveAuto
+ @@ -18,7 +19,7 @@ [Parameter] public EventCallback OnUserInputReceived { get; set; } - protected override async void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) await FocusInputFieldAsync(); diff --git a/src/Site/Site.Client/Components/TerminalInput.razor.css b/src/Site/Site.Client/Components/TerminalInput.razor.css index 3e29fa8..1e6acf3 100644 --- a/src/Site/Site.Client/Components/TerminalInput.razor.css +++ b/src/Site/Site.Client/Components/TerminalInput.razor.css @@ -11,4 +11,6 @@ div ::deep form { width: 100%; resize: none; padding: 0; + color: transparent; + text-shadow: 0 0 0 white; } diff --git a/src/Site/Site/AI/AIOptions.cs b/src/Site/Site/AI/AIOptions.cs new file mode 100644 index 0000000..79bae88 --- /dev/null +++ b/src/Site/Site/AI/AIOptions.cs @@ -0,0 +1,8 @@ +namespace Site.AI; + +internal sealed class AIOptions +{ + public string ApiKey { get; set; } = string.Empty; + public string TextCompletionModel { get; set; } = string.Empty; + public string TextEmbeddingModel { get; set; } = string.Empty; +} diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site/Components/TerminalChat.razor index 8587eb1..580a25b 100644 --- a/src/Site/Site/Components/TerminalChat.razor +++ b/src/Site/Site/Components/TerminalChat.razor @@ -1,9 +1,12 @@ @rendermode InteractiveServer @attribute [StreamRendering] -@foreach (var message in Messages) +@foreach (var (authorName, message) in Messages) { -

@message

+
+ +

@message

+
} @if (State is ChatState.Loading) @@ -18,41 +21,3 @@ else if (State is ChatState.WaitingForUser) { } - -@code { - - private int Count { get; set; } = 0; - private List Messages { get; } = []; - private ChatState State { get; set; } = ChatState.Loading; - - protected override void OnInitialized() - { - State = ChatState.WaitingForAssistant; - InvokeAsync(() => RenderAssistantMessage(0)); - } - - private async Task RenderUserMessage(string message) - { - Messages.Add(message); - State = ChatState.WaitingForAssistant; - StateHasChanged(); - - await RenderAssistantMessage(); - } - - private async Task RenderAssistantMessage(int delay = 3000) - { - await Task.Delay(delay); // simulated delay - - Messages.Add($"Message {Count++}"); - State = ChatState.WaitingForUser; - } - - enum ChatState - { - Loading, - WaitingForAssistant, - StreamingAssistantMessage, - WaitingForUser - } -} \ No newline at end of file diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs new file mode 100644 index 0000000..042afd7 --- /dev/null +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -0,0 +1,67 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; + +namespace Site.Components; + +public sealed partial class TerminalChat +{ + private readonly CancellationTokenSource _ctSource = new(); + + [Inject] + private Kernel Kernel { get; set; } = default!; + + private ChatHistory ChatHistory { get; } = []; + private List<(string AuthorName, string Message)> Messages { get; } = []; + private ChatState State { get; set; } = ChatState.Loading; + + protected override void OnInitialized() + { + ChatHistory.AddSystemMessage("You are an AI assistant on Erin McLaughlin's personal website & portfolio. Erin is the software engineer that programmed you. Talk to users about her."); + ChatHistory.AddAssistantMessage("Oh hey, you found my website! What would you like to know?"); + Messages.Add(("AI", ChatHistory.Last().Content ?? "No comment.")); + StateHasChanged(); + + State = ChatState.WaitingForUser; + } + + private async Task RenderUserMessage(string message) + { + ChatHistory.AddUserMessage(message); + Messages.Add(("User", message)); + State = ChatState.WaitingForAssistant; + StateHasChanged(); + + await RenderAssistantMessage(); + } + + private async Task RenderAssistantMessage() + { + var chatService = Kernel.GetRequiredService(); + var response = await chatService.GetChatMessageContentAsync(ChatHistory, null, Kernel, _ctSource.Token); + + var content = response.Content ?? "Hrm. Looks like I've encountered an error. Sorry about that! Maybe try back later?"; + Messages.Add(("User", content)); + ChatHistory.AddAssistantMessage(content); + + State = ChatState.WaitingForUser; + StateHasChanged(); + } + + private IEnumerable<(string AuthorName, string Message)> GetChatHistory() + { + foreach (var message in ChatHistory) + { + if (message.AuthorName is not null && message.Content is not null) + yield return (message.AuthorName, message.Content); + } + } + + private enum ChatState + { + Loading, + WaitingForAssistant, + StreamingAssistantMessage, + WaitingForUser + } +} \ No newline at end of file diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs index 61036e0..13204e5 100644 --- a/src/Site/Site/Program.cs +++ b/src/Site/Site/Program.cs @@ -1,16 +1,22 @@ -using Site.Client.Pages; +using Microsoft.SemanticKernel; +using Site.AI; using Site.Components; var builder = WebApplication.CreateBuilder(args); -// Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); +if (builder.Configuration.GetSection("AI").Get() is { } aiOptions) +{ + builder.Services.AddKernel() + .AddOpenAIChatCompletion(aiOptions.TextCompletionModel, aiOptions.ApiKey) + .AddOpenAITextEmbeddingGeneration(aiOptions.TextEmbeddingModel, aiOptions.ApiKey); +} + var app = builder.Build(); -// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); @@ -18,7 +24,6 @@ else { app.UseExceptionHandler("/Error", createScopeForErrors: true); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } diff --git a/src/Site/Site/Site.csproj b/src/Site/Site/Site.csproj index 64d037c..1e90c52 100644 --- a/src/Site/Site/Site.csproj +++ b/src/Site/Site/Site.csproj @@ -4,11 +4,13 @@ net8.0 enable enable + a6484596-76d4-4d4c-bb0c-b258ade580fe + diff --git a/src/Site/Site/appsettings.json b/src/Site/Site/appsettings.json index 10f68b8..2077c9a 100644 --- a/src/Site/Site/appsettings.json +++ b/src/Site/Site/appsettings.json @@ -1,9 +1,13 @@ { + "AllowedHosts": "*", + "AI": { + "TextCompletionModel": "gpt-3.5-turbo", + "TextEmbeddingModel": "text-embedding-3-small" + }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } - }, - "AllowedHosts": "*" + } } From cd32d6b3240d9a705b8856a62b3aa387a4e701fd Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Wed, 17 Apr 2024 23:46:59 -0400 Subject: [PATCH 3/7] a real chat! --- .../Components/TerminalInput.razor.css | 2 + src/Site/Site.Client/Site.Client.csproj | 1 + src/Site/Site/AI/WebsitePlugin.cs | 26 ++++++++++ src/Site/Site/Components/TerminalChat.razor | 6 +-- .../Site/Components/TerminalChat.razor.cs | 51 ++++++++++++------- src/Site/Site/wwwroot/app.css | 1 - 6 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 src/Site/Site/AI/WebsitePlugin.cs diff --git a/src/Site/Site.Client/Components/TerminalInput.razor.css b/src/Site/Site.Client/Components/TerminalInput.razor.css index 1e6acf3..09078b6 100644 --- a/src/Site/Site.Client/Components/TerminalInput.razor.css +++ b/src/Site/Site.Client/Components/TerminalInput.razor.css @@ -1,6 +1,8 @@ div, div ::deep form { cursor: text; + display: flex; + flex-direction: column; height: 100%; } diff --git a/src/Site/Site.Client/Site.Client.csproj b/src/Site/Site.Client/Site.Client.csproj index 22be631..f6b3bd7 100644 --- a/src/Site/Site.Client/Site.Client.csproj +++ b/src/Site/Site.Client/Site.Client.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Site/Site/AI/WebsitePlugin.cs b/src/Site/Site/AI/WebsitePlugin.cs new file mode 100644 index 0000000..8f52af7 --- /dev/null +++ b/src/Site/Site/AI/WebsitePlugin.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.SemanticKernel; +using System.ComponentModel; + +namespace Site.AI; + +public class WebsitePlugin(NavigationManager navigationManager) +{ + [KernelFunction, Description("Geta link to Erin's GitHub profile.")] + public static string GetGitHubUrl() => "https://github.com/erinnmclaughlin"; + + [KernelFunction, Description("Get a link to Erin's LinkedIn profile.")] + public static string GetLinkedInUrl() => "https://www.linkedin.com/in/e1mclaughlin/"; + + [KernelFunction, Description("Navigate to Erin's GitHub profile.")] + public void NavigateToGitHub() + { + navigationManager.NavigateTo(GetGitHubUrl()); + } + + [KernelFunction, Description("Navigate to Erin's LinkedIn profile.")] + public void NavigateToLinkedIn() + { + navigationManager.NavigateTo(GetLinkedInUrl()); + } +} diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site/Components/TerminalChat.razor index 580a25b..0afcc0e 100644 --- a/src/Site/Site/Components/TerminalChat.razor +++ b/src/Site/Site/Components/TerminalChat.razor @@ -1,11 +1,11 @@ @rendermode InteractiveServer @attribute [StreamRendering] -@foreach (var (authorName, message) in Messages) +@foreach (var message in Messages) {
- -

@message

+ +

@((MarkupString)Markdig.Markdown.ToHtml(message.Message))

} diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs index 042afd7..810e9c4 100644 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -1,25 +1,42 @@ using Microsoft.AspNetCore.Components; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; +using Microsoft.SemanticKernel.Connectors.OpenAI; +using Site.AI; namespace Site.Components; +public class ChatMessage(string author) +{ + public string Author { get; } = author; + public string Message { get; set; } = string.Empty; +} + public sealed partial class TerminalChat { private readonly CancellationTokenSource _ctSource = new(); + private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; [Inject] private Kernel Kernel { get; set; } = default!; + [Inject] + private NavigationManager NavigationManager { get; set; } = default!; + private ChatHistory ChatHistory { get; } = []; - private List<(string AuthorName, string Message)> Messages { get; } = []; + private List Messages { get; } = []; private ChatState State { get; set; } = ChatState.Loading; protected override void OnInitialized() { + Kernel.Plugins.AddFromObject(new WebsitePlugin(NavigationManager)); + ChatHistory.AddSystemMessage("You are an AI assistant on Erin McLaughlin's personal website & portfolio. Erin is the software engineer that programmed you. Talk to users about her."); - ChatHistory.AddAssistantMessage("Oh hey, you found my website! What would you like to know?"); - Messages.Add(("AI", ChatHistory.Last().Content ?? "No comment.")); + ChatHistory.AddAssistantMessage("Oh hey, you found Erin's website! What would you like to know?"); + Messages.Add(new ChatMessage("AI") + { + Message = ChatHistory.Last().Content ?? "" + }); StateHasChanged(); State = ChatState.WaitingForUser; @@ -28,7 +45,7 @@ protected override void OnInitialized() private async Task RenderUserMessage(string message) { ChatHistory.AddUserMessage(message); - Messages.Add(("User", message)); + Messages.Add(new ("User") { Message = message }); State = ChatState.WaitingForAssistant; StateHasChanged(); @@ -37,26 +54,26 @@ private async Task RenderUserMessage(string message) private async Task RenderAssistantMessage() { + State = ChatState.StreamingAssistantMessage; + + var message = new ChatMessage("AI"); + Messages.Add(message); var chatService = Kernel.GetRequiredService(); - var response = await chatService.GetChatMessageContentAsync(ChatHistory, null, Kernel, _ctSource.Token); + var response = chatService.GetStreamingChatMessageContentsAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); - var content = response.Content ?? "Hrm. Looks like I've encountered an error. Sorry about that! Maybe try back later?"; - Messages.Add(("User", content)); - ChatHistory.AddAssistantMessage(content); + await foreach(var part in response) + { + message.Message += part.Content ?? ""; + await Task.Delay(50); + StateHasChanged(); + } + + ChatHistory.AddAssistantMessage(message.Message); State = ChatState.WaitingForUser; StateHasChanged(); } - private IEnumerable<(string AuthorName, string Message)> GetChatHistory() - { - foreach (var message in ChatHistory) - { - if (message.AuthorName is not null && message.Content is not null) - yield return (message.AuthorName, message.Content); - } - } - private enum ChatState { Loading, diff --git a/src/Site/Site/wwwroot/app.css b/src/Site/Site/wwwroot/app.css index 93399e4..35b516f 100644 --- a/src/Site/Site/wwwroot/app.css +++ b/src/Site/Site/wwwroot/app.css @@ -1,6 +1,5 @@ * { margin: 0; - padding: 0; box-sizing: border-box; } From 21d9c9a3f2048faf91ca15725e8dbe0a6fdd72a3 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 11:58:24 -0400 Subject: [PATCH 4/7] feature flag --- .github/workflows/main_erinnmclaughlin.yml | 2 +- PersonalWebsite.sln | 13 +-- .../Components/ComingSoon/GlitchText.razor | 9 ++ .../ComingSoon/GlitchText.razor.css | 59 ++++++++++ .../ComingSoon/StartupProgram.razor | 46 ++++++++ .../ComingSoon/StartupProgram.razor.css | 7 ++ .../ComingSoon/TerminalCommand.razor | 35 ++++++ .../ComingSoon/TerminalCommands.razor | 95 ++++++++++++++++ .../ComingSoon/TerminalCommands.razor.css | 31 ++++++ .../ComingSoon/TerminalCursor.razor | 1 + .../ComingSoon/TerminalCursor.razor.css | 48 ++++++++ .../ComingSoon/VaporwaveBackground.razor | 3 + .../ComingSoon/VaporwaveBackground.razor.css | 105 ++++++++++++++++++ .../Site.Client/Components/Terminal.razor.css | 2 +- src/Site/Site/AI/AIOptions.cs | 1 + src/Site/Site/Components/Pages/Home.razor | 12 +- src/Site/Site/Components/Pages/Home.razor.cs | 48 ++++++++ src/Site/Site/Components/Pages/Weather.razor | 64 ----------- .../Site/Components/TerminalChat.razor.cs | 7 +- src/Site/Site/Program.cs | 8 +- src/Site/Site/appsettings.json | 1 + 21 files changed, 510 insertions(+), 87 deletions(-) create mode 100644 src/Site/Site.Client/Components/ComingSoon/GlitchText.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/GlitchText.razor.css create mode 100644 src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor.css create mode 100644 src/Site/Site.Client/Components/ComingSoon/TerminalCommand.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor.css create mode 100644 src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor.css create mode 100644 src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor create mode 100644 src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor.css create mode 100644 src/Site/Site/Components/Pages/Home.razor.cs delete mode 100644 src/Site/Site/Components/Pages/Weather.razor diff --git a/.github/workflows/main_erinnmclaughlin.yml b/.github/workflows/main_erinnmclaughlin.yml index 70efd08..0e5edd5 100644 --- a/.github/workflows/main_erinnmclaughlin.yml +++ b/.github/workflows/main_erinnmclaughlin.yml @@ -19,7 +19,7 @@ jobs: - name: Set up .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.x' + dotnet-version: '8.x' include-prerelease: true - name: Build with dotnet diff --git a/PersonalWebsite.sln b/PersonalWebsite.sln index 173eda7..5803083 100644 --- a/PersonalWebsite.sln +++ b/PersonalWebsite.sln @@ -3,11 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PersonalWebsite", "PersonalWebsite\PersonalWebsite.csproj", "{85090680-1997-49A8-BAB0-2283BD353DC8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Site", "src\Site\Site\Site.csproj", "{478039A1-B85B-4EF9-943E-D2EEEB522919}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Site", "src\Site\Site\Site.csproj", "{478039A1-B85B-4EF9-943E-D2EEEB522919}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Site.Client", "src\Site\Site.Client\Site.Client.csproj", "{905FA50A-C616-4339-BFE1-D68CA9C262A8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Site.Client", "src\Site\Site.Client\Site.Client.csproj", "{905FA50A-C616-4339-BFE1-D68CA9C262A8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6680D40B-9A58-4838-BCAD-56D382006878}" + ProjectSection(SolutionItems) = preProject + .github\workflows\main_erinnmclaughlin.yml = .github\workflows\main_erinnmclaughlin.yml + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,10 +18,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {85090680-1997-49A8-BAB0-2283BD353DC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85090680-1997-49A8-BAB0-2283BD353DC8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85090680-1997-49A8-BAB0-2283BD353DC8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85090680-1997-49A8-BAB0-2283BD353DC8}.Release|Any CPU.Build.0 = Release|Any CPU {478039A1-B85B-4EF9-943E-D2EEEB522919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {478039A1-B85B-4EF9-943E-D2EEEB522919}.Debug|Any CPU.Build.0 = Debug|Any CPU {478039A1-B85B-4EF9-943E-D2EEEB522919}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor b/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor new file mode 100644 index 0000000..5b59d09 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor @@ -0,0 +1,9 @@ +

+ + @Value + +

+ +@code { + [Parameter] public required string Value { get; set; } +} diff --git a/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor.css b/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor.css new file mode 100644 index 0000000..368cd5c --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/GlitchText.razor.css @@ -0,0 +1,59 @@ +/* https://codepen.io/cbanlawi/pen/xxRBeMY */ + +.glitch { + font-size: 5rem; + font-weight: bold; + position: relative; + text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff, 0.025em 0.04em 0 #fffc00; + animation: glitch 725ms infinite; +} + + .glitch span { + position: absolute; + top: 0; + left: 0; + } + + .glitch span:first-child { + animation: glitch 500ms infinite; + clip-path: polygon(0 0, 100% 0, 100% 35%, 0 35%); + transform: translate(-0.04em, -0.03em); + opacity: 0.75; + } + + .glitch span:last-child { + animation: glitch 375ms infinite; + clip-path: polygon(0 65%, 100% 65%, 100% 100%, 0 100%); + transform: translate(0.04em, 0.03em); + opacity: 0.75; + } + +@keyframes glitch { + 0% { + text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff, 0.025em 0.04em 0 #fffc00; + } + + 15% { + text-shadow: 0.05em 0 0 #00fffc, -0.03em -0.04em 0 #fc00ff, 0.025em 0.04em 0 #fffc00; + } + + 16% { + text-shadow: -0.05em -0.025em 0 #00fffc, 0.025em 0.035em 0 #fc00ff, -0.05em -0.05em 0 #fffc00; + } + + 49% { + text-shadow: -0.05em -0.025em 0 #00fffc, 0.025em 0.035em 0 #fc00ff, -0.05em -0.05em 0 #fffc00; + } + + 50% { + text-shadow: 0.05em 0.035em 0 #00fffc, 0.03em 0 0 #fc00ff, 0 -0.04em 0 #fffc00; + } + + 99% { + text-shadow: 0.05em 0.035em 0 #00fffc, 0.03em 0 0 #fc00ff, 0 -0.04em 0 #fffc00; + } + + 100% { + text-shadow: -0.05em 0 0 #00fffc, -0.025em -0.04em 0 #fc00ff, -0.04em -0.025em 0 #fffc00; + } +} diff --git a/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor b/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor new file mode 100644 index 0000000..1fbeafb --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor @@ -0,0 +1,46 @@ +@attribute [StreamRendering] +@rendermode InteractiveAuto + +@if (CurrentState is not State.Running) +{ +
+ + + @if (CurrentState is State.Building) + { +

Building...

+ } +
+} +else +{ +
+ +
+ +
+
+} + +@code { + + private string[] Commands { get; set; } = new[] + { + "cd source/repos", + "git clone https://github.com/erinnmclaughlin/PersonalWebsite.git", + "cd PersonalWebsite", + "dotnet run" + }; + + private State CurrentState { get; set; } = State.EnteringCommands; + + private async Task OnCommandsCompleted() + { + CurrentState = State.Building; + await Task.Delay(2000); + + CurrentState = State.Running; + } + + enum State { EnteringCommands, Building, Running } +} diff --git a/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor.css b/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor.css new file mode 100644 index 0000000..ca27374 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/StartupProgram.razor.css @@ -0,0 +1,7 @@ +.history { + display: flex; + flex-direction: column; + gap: 5px; + height: 100%; + padding: 10px; +} diff --git a/src/Site/Site.Client/Components/ComingSoon/TerminalCommand.razor b/src/Site/Site.Client/Components/ComingSoon/TerminalCommand.razor new file mode 100644 index 0000000..d53e109 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/TerminalCommand.razor @@ -0,0 +1,35 @@ +@attribute [StreamRendering] + +

+ > @TypedCommand +

+ +@code { + + [Parameter] public required string Command { get; set; } + [Parameter] public EventCallback OnAnimationComplete { get; set; } + + private string TypedCommand { get; set; } = ""; + + protected override async Task OnInitializedAsync() + { + await Animate(); + } + + public async Task Animate() + { + TypedCommand = ""; + var length = 0; + + while (length <= Command.Length) + { + TypedCommand = Command.Substring(0, length); + length++; + StateHasChanged(); + await Task.Delay(50); + } + + await Task.Delay(200); + await OnAnimationComplete.InvokeAsync(); + } +} \ No newline at end of file diff --git a/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor b/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor new file mode 100644 index 0000000..d3cc545 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor @@ -0,0 +1,95 @@ +@attribute [StreamRendering] +@rendermode InteractiveAuto + +@if (CurrentState is State.Waiting) +{ + +} +else +{ + for (int i = 0; i <= ExecutedCommandCount - 1; i++) + { + + } + + @if (ShowCursor) + { +
>
+ } +} + +@code { + + private State CurrentState { get; set; } = State.Waiting; + + private bool IsTyping { get; set; } + private bool ShowCursor => HasNext() && !IsTyping; + private bool ShowInstructions { get; set; } + + private int ExecutedCommandCount { get; set; } = 0; + + [Parameter] + public required string[] Commands { get; set; } + + [Parameter] + public EventCallback OnComplete { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await Task.Delay(1500); + ShowInstructions = true; + StateHasChanged(); + } + } + + private async Task OnCommandCompleted() + { + IsTyping = false; + StateHasChanged(); + + await Task.Delay(600); + + if (!HasNext()) + { + await OnComplete.InvokeAsync(); + return; + } + + EnterNextCommand(); + } + + private bool HasNext() + { + return ExecutedCommandCount < Commands.Length; + } + + private void EnterNextCommand() + { + IsTyping = true; + ExecutedCommandCount++; + } + + private void Start() + { + CurrentState = State.Running; + EnterNextCommand(); + } + + private enum State { Waiting, Running, Completed } +} diff --git a/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor.css b/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor.css new file mode 100644 index 0000000..87d923c --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/TerminalCommands.razor.css @@ -0,0 +1,31 @@ +#start { + background: unset; + color: unset; + font-family: unset; + font-size: unset; + cursor: pointer; + border: 0; + height: 100%; + width: 100%; + display: flex; +} + + #start:hover { + color: lime; + } + +#desktop-instructions { + display: none; +} + + +@media only screen and (min-width: 600px) { + + #desktop-instructions { + display: unset; + } + + #mobile-instructions { + display: none; + } +} \ No newline at end of file diff --git a/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor b/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor new file mode 100644 index 0000000..286844e --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor @@ -0,0 +1 @@ +_ \ No newline at end of file diff --git a/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor.css b/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor.css new file mode 100644 index 0000000..f4602f5 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/TerminalCursor.razor.css @@ -0,0 +1,48 @@ +.terminal-cursor { + opacity: 1; + -webkit-animation: blink 0.8s infinite; + -moz-animation: blink 0.8s infinite; + animation: blink 0.8s infinite; +} + +@keyframes blink { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@-webkit-keyframes blink { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@-moz-keyframes blink { + 0% { + opacity: 1; + } + + 50% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} diff --git a/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor b/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor new file mode 100644 index 0000000..a9f4197 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor @@ -0,0 +1,3 @@ +
+
+
diff --git a/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor.css b/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor.css new file mode 100644 index 0000000..1b34a52 --- /dev/null +++ b/src/Site/Site.Client/Components/ComingSoon/VaporwaveBackground.razor.css @@ -0,0 +1,105 @@ +/* https://codepen.io/joelbrewster/pen/qNmoJE */ +#background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + /*Alternate colors with black at the bottom when it rotates */ + background-image: -webkit-linear-gradient(90deg, black, #F33CA4); + -webkit-transition: opacity ease 0.9s; + -moz-transition: opacity ease 0.9s; + -ms-transition: opacity ease 0.9s; + -o-transition: opacity ease 0.9s; + transition: opacity ease 0.9s; + /*Change color time */ + -webkit-animation: hue 30s infinite linear; + opacity: .9; + z-index: -1; +} + +/* Fade in and out */ +.fadeIn { + -webkit-animation: fadeIn 0.5s; +} + +/* Rotate the colors */ +@-webkit-keyframes hue { + from { + -webkit-filter: hue-rotate(0deg); + } + + to { + /* Looks like it circles if -360 */ + -webkit-filter: hue-rotate(-360deg); + } +} + +/* Add of css grids */ +body { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + background: #000; + /*Change the 'fov' */ + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-perspective: 500px; + perspective: 500px; +} + +.grid-top, +.grid-bottom { + position: absolute; + z-index: 0; /* to be below the parent element - add js visualizer on top */ + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + background-size: 50px 50px; +} + +/* Copy and paste grid effect */ +.grid-top { + background-image: -webkit-linear-gradient(90deg, transparent 19%, rgba(184, 1, 179, 0.5) 24%, #b900b4 25%, #b900b4 26%, rgba(184, 1, 179, 0.5) 27%, transparent 32%, transparent 69%, rgba(184, 1, 179, 0.5) 74%, #b900b4 75%, #b900b4 76%, rgba(184, 1, 179, 0.5) 77%, transparent 82%, transparent), -webkit-linear-gradient(0deg, transparent 19%, rgba(184, 1, 179, 0.5) 24%, #b900b4 25%, #b900b4 26%, rgba(184, 1, 179, 0.5) 27%, transparent 32%, transparent 69%, rgba(184, 1, 179, 0.5) 74%, #b900b4 75%, #b900b4 76%, rgba(184, 1, 179, 0.5) 77%, transparent 82%, transparent); + background-image: linear-gradient(0deg, transparent 19%, rgba(184, 1, 179, 0.5) 24%, #b900b4 25%, #b900b4 26%, rgba(184, 1, 179, 0.5) 27%, transparent 32%, transparent 69%, rgba(184, 1, 179, 0.5) 74%, #b900b4 75%, #b900b4 76%, rgba(184, 1, 179, 0.5) 77%, transparent 82%, transparent), linear-gradient(90deg, transparent 19%, rgba(184, 1, 179, 0.5) 24%, #b900b4 25%, #b900b4 26%, rgba(184, 1, 179, 0.5) 27%, transparent 32%, transparent 69%, rgba(184, 1, 179, 0.5) 74%, #b900b4 75%, #b900b4 76%, rgba(184, 1, 179, 0.5) 77%, transparent 82%, transparent); + /* Angle the square */ + -webkit-transform: rotateX(-80deg) scale(2); + transform: rotateX(-80deg) scale(2); + -webkit-transform-origin: top center; + transform-origin: top center; + box-shadow: inset 0px -100px 100px 100px #000; + -webkit-animation: rad 1s reverse linear infinite; + animation: rad 1s reverse linear infinite; +} + +/* Add grid to bottom */ +.grid-bottom { + background-image: -webkit-linear-gradient(90deg, transparent 19%, rgba(13, 229, 255, 0.5) 24%, #0de5ff 25%, #0de5ff 26%, rgba(13, 229, 255, 0.5) 27%, transparent 32%, transparent 69%, rgba(13, 229, 255, 0.5) 74%, #0de5ff 75%, #0de5ff 76%, rgba(13, 229, 255, 0.5) 77%, transparent 82%, transparent), -webkit-linear-gradient(0deg, transparent 19%, rgba(13, 229, 255, 0.5) 24%, #0de5ff 25%, #0de5ff 26%, rgba(13, 229, 255, 0.5) 27%, transparent 32%, transparent 69%, rgba(13, 229, 255, 0.5) 74%, #0de5ff 75%, #0de5ff 76%, rgba(13, 229, 255, 0.5) 77%, transparent 82%, transparent); + background-image: linear-gradient(0deg, transparent 19%, rgba(13, 229, 255, 0.5) 24%, #0de5ff 25%, #0de5ff 26%, rgba(13, 229, 255, 0.5) 27%, transparent 32%, transparent 69%, rgba(13, 229, 255, 0.5) 74%, #0de5ff 75%, #0de5ff 76%, rgba(13, 229, 255, 0.5) 77%, transparent 82%, transparent), linear-gradient(90deg, transparent 19%, rgba(13, 229, 255, 0.5) 24%, #0de5ff 25%, #0de5ff 26%, rgba(13, 229, 255, 0.5) 27%, transparent 32%, transparent 69%, rgba(13, 229, 255, 0.5) 74%, #0de5ff 75%, #0de5ff 76%, rgba(13, 229, 255, 0.5) 77%, transparent 82%, transparent); + -webkit-transform: rotateX(80deg) scale(2); + transform: rotateX(80deg) scale(2); + -webkit-transform-origin: bottom center; + transform-origin: bottom center; + /* Make it looks like a grid */ + box-shadow: inset 0px 100px 100px 100px #000; + -webkit-animation: rad 1s linear infinite; + animation: rad 1s linear infinite; +} + +/* Set the speed/radius of the grid, intervals of 50/25/etc */ +@-webkit-keyframes rad { + 100% { + background-position: 0px 50px; + } +} + +@keyframes rad { + 100% { + background-position: 0px 50px; + } +} diff --git a/src/Site/Site.Client/Components/Terminal.razor.css b/src/Site/Site.Client/Components/Terminal.razor.css index 34b07ae..5519e33 100644 --- a/src/Site/Site.Client/Components/Terminal.razor.css +++ b/src/Site/Site.Client/Components/Terminal.razor.css @@ -48,7 +48,7 @@ display: flex; flex-direction: column; gap: 0.5em; - padding: 0.5em; + padding: 0; } .terminal-window section.terminal, diff --git a/src/Site/Site/AI/AIOptions.cs b/src/Site/Site/AI/AIOptions.cs index 79bae88..5c0c619 100644 --- a/src/Site/Site/AI/AIOptions.cs +++ b/src/Site/Site/AI/AIOptions.cs @@ -3,6 +3,7 @@ internal sealed class AIOptions { public string ApiKey { get; set; } = string.Empty; + public bool Enabled { get; set; } public string TextCompletionModel { get; set; } = string.Empty; public string TextEmbeddingModel { get; set; } = string.Empty; } diff --git a/src/Site/Site/Components/Pages/Home.razor b/src/Site/Site/Components/Pages/Home.razor index 3b3cd23..a3fd363 100644 --- a/src/Site/Site/Components/Pages/Home.razor +++ b/src/Site/Site/Components/Pages/Home.razor @@ -1,4 +1,5 @@ @page "/" +@rendermode InteractiveServer @@ -20,5 +21,12 @@ - - \ No newline at end of file + @if (Kernel is null) + { + + } + else + { + + } + diff --git a/src/Site/Site/Components/Pages/Home.razor.cs b/src/Site/Site/Components/Pages/Home.razor.cs new file mode 100644 index 0000000..c700366 --- /dev/null +++ b/src/Site/Site/Components/Pages/Home.razor.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Options; +using Microsoft.SemanticKernel; +using Site.AI; +using System.Diagnostics.CodeAnalysis; + +namespace Site.Components.Pages; + +public sealed partial class Home : IDisposable +{ + [Inject, NotNull] + private IOptionsMonitor? AIOptionsMonitor { get; set; } + + [Inject, NotNull] + private NavigationManager? NavigationManager { get; set; } + + private IDisposable? AIOptionsSubscription { get; set; } + private Kernel? Kernel { get; set; } + + public void Dispose() + { + AIOptionsSubscription?.Dispose(); + } + + protected override void OnInitialized() + { + UpdateAIOptions(AIOptionsMonitor.CurrentValue); + AIOptionsSubscription = AIOptionsMonitor.OnChange(UpdateAIOptions); + } + + private void UpdateAIOptions(AIOptions options) + { + Kernel = null; + + if (options.Enabled) + { + var kernelBuilder = Kernel.CreateBuilder() + .AddOpenAIChatCompletion(options.TextCompletionModel, options.ApiKey) + .AddOpenAITextEmbeddingGeneration(options.TextEmbeddingModel, options.ApiKey); + + kernelBuilder.Plugins.AddFromObject(new WebsitePlugin(NavigationManager)); + + Kernel = kernelBuilder.Build(); + } + + InvokeAsync(StateHasChanged); + } +} diff --git a/src/Site/Site/Components/Pages/Weather.razor b/src/Site/Site/Components/Pages/Weather.razor deleted file mode 100644 index 43a1ecb..0000000 --- a/src/Site/Site/Components/Pages/Weather.razor +++ /dev/null @@ -1,64 +0,0 @@ -@page "/weather" -@attribute [StreamRendering] - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate streaming rendering - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs index 810e9c4..55aace7 100644 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -2,7 +2,6 @@ using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; -using Site.AI; namespace Site.Components; @@ -17,8 +16,8 @@ public sealed partial class TerminalChat private readonly CancellationTokenSource _ctSource = new(); private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; - [Inject] - private Kernel Kernel { get; set; } = default!; + [Parameter, EditorRequired] + public required Kernel Kernel { get; set; } [Inject] private NavigationManager NavigationManager { get; set; } = default!; @@ -29,8 +28,6 @@ public sealed partial class TerminalChat protected override void OnInitialized() { - Kernel.Plugins.AddFromObject(new WebsitePlugin(NavigationManager)); - ChatHistory.AddSystemMessage("You are an AI assistant on Erin McLaughlin's personal website & portfolio. Erin is the software engineer that programmed you. Talk to users about her."); ChatHistory.AddAssistantMessage("Oh hey, you found Erin's website! What would you like to know?"); Messages.Add(new ChatMessage("AI") diff --git a/src/Site/Site/Program.cs b/src/Site/Site/Program.cs index 13204e5..3176226 100644 --- a/src/Site/Site/Program.cs +++ b/src/Site/Site/Program.cs @@ -1,4 +1,3 @@ -using Microsoft.SemanticKernel; using Site.AI; using Site.Components; @@ -8,12 +7,7 @@ .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); -if (builder.Configuration.GetSection("AI").Get() is { } aiOptions) -{ - builder.Services.AddKernel() - .AddOpenAIChatCompletion(aiOptions.TextCompletionModel, aiOptions.ApiKey) - .AddOpenAITextEmbeddingGeneration(aiOptions.TextEmbeddingModel, aiOptions.ApiKey); -} +builder.Services.Configure(builder.Configuration.GetSection("AI")); var app = builder.Build(); diff --git a/src/Site/Site/appsettings.json b/src/Site/Site/appsettings.json index 2077c9a..1f17aa4 100644 --- a/src/Site/Site/appsettings.json +++ b/src/Site/Site/appsettings.json @@ -1,6 +1,7 @@ { "AllowedHosts": "*", "AI": { + "Enabled": false, "TextCompletionModel": "gpt-3.5-turbo", "TextEmbeddingModel": "text-embedding-3-small" }, From ea010360a56650cdd32f50031885520e0de4ff6c Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 12:51:01 -0400 Subject: [PATCH 5/7] sanitize inputs --- .../Site.Client/Components/Terminal.razor.css | 3 -- src/Site/Site/Components/ChatContent.razor | 20 ++++++++++ .../Site/Components/{Pages => }/Error.razor | 0 .../Site/Components/{Pages => }/Home.razor | 0 .../Site/Components/{Pages => }/Home.razor.cs | 2 +- .../Components/{Pages => }/Home.razor.css | 0 .../Components/{Layout => }/MainLayout.razor | 0 .../{Layout => }/MainLayout.razor.css | 0 src/Site/Site/Components/Routes.razor | 2 +- src/Site/Site/Components/TerminalChat.razor | 40 ++++++++++--------- .../Site/Components/TerminalChat.razor.cs | 28 ++++++------- .../Site/Components/TerminalChat.razor.css | 7 ++++ src/Site/Site/Site.csproj | 1 + src/Site/Site/appsettings.Development.json | 3 ++ 14 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 src/Site/Site/Components/ChatContent.razor rename src/Site/Site/Components/{Pages => }/Error.razor (100%) rename src/Site/Site/Components/{Pages => }/Home.razor (100%) rename src/Site/Site/Components/{Pages => }/Home.razor.cs (97%) rename src/Site/Site/Components/{Pages => }/Home.razor.css (100%) rename src/Site/Site/Components/{Layout => }/MainLayout.razor (100%) rename src/Site/Site/Components/{Layout => }/MainLayout.razor.css (100%) create mode 100644 src/Site/Site/Components/TerminalChat.razor.css diff --git a/src/Site/Site.Client/Components/Terminal.razor.css b/src/Site/Site.Client/Components/Terminal.razor.css index 5519e33..98024c6 100644 --- a/src/Site/Site.Client/Components/Terminal.razor.css +++ b/src/Site/Site.Client/Components/Terminal.razor.css @@ -45,9 +45,6 @@ overflow-y: auto; overflow-x: hidden; border-radius: 0 0 8px 8px; - display: flex; - flex-direction: column; - gap: 0.5em; padding: 0; } diff --git a/src/Site/Site/Components/ChatContent.razor b/src/Site/Site/Components/ChatContent.razor new file mode 100644 index 0000000..cdda809 --- /dev/null +++ b/src/Site/Site/Components/ChatContent.razor @@ -0,0 +1,20 @@ +@using Vereyon.Web + +@if (Content is { Length: > 0 }) +{ +

+ @Sanitize(Content) +

+} + +@code { + private static readonly HtmlSanitizer _htmlSanitizer = HtmlSanitizer.SimpleHtml5Sanitizer(); + + [Parameter] + public string? Content { get; set; } + + private static MarkupString Sanitize(string html) + { + return new MarkupString((_htmlSanitizer.Sanitize(Markdig.Markdown.ToHtml(html)))); + } +} diff --git a/src/Site/Site/Components/Pages/Error.razor b/src/Site/Site/Components/Error.razor similarity index 100% rename from src/Site/Site/Components/Pages/Error.razor rename to src/Site/Site/Components/Error.razor diff --git a/src/Site/Site/Components/Pages/Home.razor b/src/Site/Site/Components/Home.razor similarity index 100% rename from src/Site/Site/Components/Pages/Home.razor rename to src/Site/Site/Components/Home.razor diff --git a/src/Site/Site/Components/Pages/Home.razor.cs b/src/Site/Site/Components/Home.razor.cs similarity index 97% rename from src/Site/Site/Components/Pages/Home.razor.cs rename to src/Site/Site/Components/Home.razor.cs index c700366..66cc329 100644 --- a/src/Site/Site/Components/Pages/Home.razor.cs +++ b/src/Site/Site/Components/Home.razor.cs @@ -4,7 +4,7 @@ using Site.AI; using System.Diagnostics.CodeAnalysis; -namespace Site.Components.Pages; +namespace Site.Components; public sealed partial class Home : IDisposable { diff --git a/src/Site/Site/Components/Pages/Home.razor.css b/src/Site/Site/Components/Home.razor.css similarity index 100% rename from src/Site/Site/Components/Pages/Home.razor.css rename to src/Site/Site/Components/Home.razor.css diff --git a/src/Site/Site/Components/Layout/MainLayout.razor b/src/Site/Site/Components/MainLayout.razor similarity index 100% rename from src/Site/Site/Components/Layout/MainLayout.razor rename to src/Site/Site/Components/MainLayout.razor diff --git a/src/Site/Site/Components/Layout/MainLayout.razor.css b/src/Site/Site/Components/MainLayout.razor.css similarity index 100% rename from src/Site/Site/Components/Layout/MainLayout.razor.css rename to src/Site/Site/Components/MainLayout.razor.css diff --git a/src/Site/Site/Components/Routes.razor b/src/Site/Site/Components/Routes.razor index d39c7e8..9705353 100644 --- a/src/Site/Site/Components/Routes.razor +++ b/src/Site/Site/Components/Routes.razor @@ -1,6 +1,6 @@  - + diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site/Components/TerminalChat.razor index 0afcc0e..81cd574 100644 --- a/src/Site/Site/Components/TerminalChat.razor +++ b/src/Site/Site/Components/TerminalChat.razor @@ -1,23 +1,25 @@ @rendermode InteractiveServer @attribute [StreamRendering] -@foreach (var message in Messages) -{ -
- -

@((MarkupString)Markdig.Markdown.ToHtml(message.Message))

-
-} +
+ @foreach (var message in Messages) + { +
+ + +
+ } -@if (State is ChatState.Loading) -{ -

Loading...

-} -else if (State is ChatState.WaitingForAssistant) -{ -

Thinking...

-} -else if (State is ChatState.WaitingForUser) -{ - -} + @if (State is ChatState.Loading) + { +

Loading...

+ } + else if (State is ChatState.WaitingForAssistant) + { +

Thinking...

+ } + else if (State is ChatState.WaitingForUser) + { + + } +
\ No newline at end of file diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs index 55aace7..b5f7c88 100644 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -8,14 +8,14 @@ namespace Site.Components; public class ChatMessage(string author) { public string Author { get; } = author; - public string Message { get; set; } = string.Empty; + public string? Content { get; set; } } public sealed partial class TerminalChat { private readonly CancellationTokenSource _ctSource = new(); private readonly OpenAIPromptExecutionSettings _promptExecutionSettings = new() { ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions }; - + [Parameter, EditorRequired] public required Kernel Kernel { get; set; } @@ -32,9 +32,8 @@ protected override void OnInitialized() ChatHistory.AddAssistantMessage("Oh hey, you found Erin's website! What would you like to know?"); Messages.Add(new ChatMessage("AI") { - Message = ChatHistory.Last().Content ?? "" + Content = ChatHistory.Last().Content }); - StateHasChanged(); State = ChatState.WaitingForUser; } @@ -42,7 +41,7 @@ protected override void OnInitialized() private async Task RenderUserMessage(string message) { ChatHistory.AddUserMessage(message); - Messages.Add(new ("User") { Message = message }); + Messages.Add(new ("User") { Content = message }); State = ChatState.WaitingForAssistant; StateHasChanged(); @@ -51,24 +50,23 @@ private async Task RenderUserMessage(string message) private async Task RenderAssistantMessage() { - State = ChatState.StreamingAssistantMessage; - - var message = new ChatMessage("AI"); - Messages.Add(message); var chatService = Kernel.GetRequiredService(); - var response = chatService.GetStreamingChatMessageContentsAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); + var message = chatService.GetStreamingChatMessageContentsAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); + + var chatMessage = new ChatMessage("AI") { Content = string.Empty }; + Messages.Add(chatMessage); - await foreach(var part in response) + State = ChatState.StreamingAssistantMessage; + + await foreach (var part in message) { - message.Message += part.Content ?? ""; + chatMessage.Content += part; await Task.Delay(50); StateHasChanged(); } - ChatHistory.AddAssistantMessage(message.Message); - + ChatHistory.AddAssistantMessage(chatMessage.Content); State = ChatState.WaitingForUser; - StateHasChanged(); } private enum ChatState diff --git a/src/Site/Site/Components/TerminalChat.razor.css b/src/Site/Site/Components/TerminalChat.razor.css new file mode 100644 index 0000000..8542f9b --- /dev/null +++ b/src/Site/Site/Components/TerminalChat.razor.css @@ -0,0 +1,7 @@ +.terminal-chat { + height: 100%; + display: flex; + flex-direction: column; + gap: 0.5em; + padding: 0.5em; +} diff --git a/src/Site/Site/Site.csproj b/src/Site/Site/Site.csproj index 1e90c52..60d88db 100644 --- a/src/Site/Site/Site.csproj +++ b/src/Site/Site/Site.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Site/Site/appsettings.Development.json b/src/Site/Site/appsettings.Development.json index 0c208ae..15086e9 100644 --- a/src/Site/Site/appsettings.Development.json +++ b/src/Site/Site/appsettings.Development.json @@ -1,4 +1,7 @@ { + "AI": { + "Enabled": true + }, "Logging": { "LogLevel": { "Default": "Information", From 415ef80786e4cf168a6c13f5bb2ae09982f00b61 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 13:28:02 -0400 Subject: [PATCH 6/7] add a little more --- src/Site/Site/AI/WebsitePlugin.cs | 11 ++- src/Site/Site/Components/TerminalChat.razor | 5 +- .../Site/Components/TerminalChat.razor.cs | 71 ++++++++++++++----- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/src/Site/Site/AI/WebsitePlugin.cs b/src/Site/Site/AI/WebsitePlugin.cs index 8f52af7..59d0b2f 100644 --- a/src/Site/Site/AI/WebsitePlugin.cs +++ b/src/Site/Site/AI/WebsitePlugin.cs @@ -6,12 +6,15 @@ namespace Site.AI; public class WebsitePlugin(NavigationManager navigationManager) { - [KernelFunction, Description("Geta link to Erin's GitHub profile.")] + [KernelFunction, Description("Get a link to Erin's GitHub profile.")] public static string GetGitHubUrl() => "https://github.com/erinnmclaughlin"; [KernelFunction, Description("Get a link to Erin's LinkedIn profile.")] public static string GetLinkedInUrl() => "https://www.linkedin.com/in/e1mclaughlin/"; + [KernelFunction, Description("Get a link to Erin's resume.")] + public static string GetResume() => "https://erinnmclaughlin.github.io/Resume/"; + [KernelFunction, Description("Navigate to Erin's GitHub profile.")] public void NavigateToGitHub() { @@ -23,4 +26,10 @@ public void NavigateToLinkedIn() { navigationManager.NavigateTo(GetLinkedInUrl()); } + + [KernelFunction, Description("Navigate to Erin's resume.")] + public void NavigateToResume() + { + navigationManager.NavigateTo(GetResume()); + } } diff --git a/src/Site/Site/Components/TerminalChat.razor b/src/Site/Site/Components/TerminalChat.razor index 81cd574..fe14cdb 100644 --- a/src/Site/Site/Components/TerminalChat.razor +++ b/src/Site/Site/Components/TerminalChat.razor @@ -2,10 +2,13 @@ @attribute [StreamRendering]
+ @foreach (var message in Messages) {
- +
} diff --git a/src/Site/Site/Components/TerminalChat.razor.cs b/src/Site/Site/Components/TerminalChat.razor.cs index b5f7c88..115eb8b 100644 --- a/src/Site/Site/Components/TerminalChat.razor.cs +++ b/src/Site/Site/Components/TerminalChat.razor.cs @@ -24,56 +24,93 @@ public sealed partial class TerminalChat private ChatHistory ChatHistory { get; } = []; private List Messages { get; } = []; - private ChatState State { get; set; } = ChatState.Loading; + private ChatState State { get; set; } = ChatState.StreamingMessage; protected override void OnInitialized() { - ChatHistory.AddSystemMessage("You are an AI assistant on Erin McLaughlin's personal website & portfolio. Erin is the software engineer that programmed you. Talk to users about her."); - ChatHistory.AddAssistantMessage("Oh hey, you found Erin's website! What would you like to know?"); - Messages.Add(new ChatMessage("AI") + ChatHistory.AddSystemMessage(""" + You are an AI assistant named Aibba and you are running on Erin McLaughlin's personal website. + Erin is the software engineer that programmed you. Talk to users about her. + """); + + InvokeAsync(RenderGreeting); + } + + private async Task RenderGreeting() + { + foreach (var message in WelcomeMessages) { - Content = ChatHistory.Last().Content - }); + await RenderGreeting(message); + await Task.Delay(300); + } - State = ChatState.WaitingForUser; + await RenderAssistantMessage("Sure thing! Feel free to ask me if there's anything else you'd like to know about Erin!"); + } + + private async Task RenderGreeting(string message) + { + var chatMessage = new ChatMessage("Erin") { Content = string.Empty }; + Messages.Add(chatMessage); + + foreach (var part in message) + { + chatMessage.Content += part; + await Task.Delay(30); + StateHasChanged(); + } + + ChatHistory.AddSystemMessage(message); } private async Task RenderUserMessage(string message) { ChatHistory.AddUserMessage(message); Messages.Add(new ("User") { Content = message }); - State = ChatState.WaitingForAssistant; - StateHasChanged(); await RenderAssistantMessage(); } - private async Task RenderAssistantMessage() + private async Task RenderAssistantMessage(string? message = null) { - var chatService = Kernel.GetRequiredService(); - var message = chatService.GetStreamingChatMessageContentsAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); + State = ChatState.WaitingForAssistant; + StateHasChanged(); + + if (message is null) + { + var chatService = Kernel.GetRequiredService(); + var chatMessageContent = await chatService.GetChatMessageContentAsync(ChatHistory, _promptExecutionSettings, Kernel, _ctSource.Token); + message = chatMessageContent.Content ?? string.Empty; + } - var chatMessage = new ChatMessage("AI") { Content = string.Empty }; + var chatMessage = new ChatMessage("Aibba") { Content = string.Empty }; Messages.Add(chatMessage); - State = ChatState.StreamingAssistantMessage; + State = ChatState.StreamingMessage; - await foreach (var part in message) + foreach (var part in message) { chatMessage.Content += part; - await Task.Delay(50); + await Task.Delay(30); StateHasChanged(); } ChatHistory.AddAssistantMessage(chatMessage.Content); State = ChatState.WaitingForUser; + StateHasChanged(); } private enum ChatState { Loading, WaitingForAssistant, - StreamingAssistantMessage, + StreamingMessage, WaitingForUser } + + private readonly List WelcomeMessages = [ + "Hi! Welcome to my website. I'm a software engineer with a passion for building context-driven systems.", + "You can check out my work on [GitHub](https://github.com/erinnmclaughlin), or ask my AI friend Aibba about me!", + "Aibba is a large language model I've integrated into my website to answer questions you might have about me.", + "Alright - I gotta go! Aibba, can you take it from here?" + ]; } \ No newline at end of file From 0b21768ffc60a96578f00583867266e8e168f341 Mon Sep 17 00:00:00 2001 From: Erin McLaughlin Date: Thu, 18 Apr 2024 13:31:13 -0400 Subject: [PATCH 7/7] enable preview view --- src/Site/Site/Components/Home.razor.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Site/Site/Components/Home.razor.cs b/src/Site/Site/Components/Home.razor.cs index 66cc329..70bd7e4 100644 --- a/src/Site/Site/Components/Home.razor.cs +++ b/src/Site/Site/Components/Home.razor.cs @@ -8,6 +8,9 @@ namespace Site.Components; public sealed partial class Home : IDisposable { + [Parameter, SupplyParameterFromQuery] + public bool Preview { get; set; } + [Inject, NotNull] private IOptionsMonitor? AIOptionsMonitor { get; set; } @@ -32,7 +35,7 @@ private void UpdateAIOptions(AIOptions options) { Kernel = null; - if (options.Enabled) + if (Preview || options.Enabled) { var kernelBuilder = Kernel.CreateBuilder() .AddOpenAIChatCompletion(options.TextCompletionModel, options.ApiKey)