diff --git a/css/base.css b/css/base.css index 1296516..5064885 100644 --- a/css/base.css +++ b/css/base.css @@ -25,6 +25,7 @@ html[data-light] { --rosewater: #dc8a78; --flamingo: #dd7878; --pink: #ea76cb; - text-decoration-color: var(--base); } +.red { color: var(--red); } .green { color: var(--green); } .blue { color: var(--blue); } .pink { color: var(--pink); } @@ -38,13 +39,9 @@ a:focus { outline: 1px solid var(--pink); } +.red::selection, .red ::selection { background-color: var(--red); } .green::selection, .green ::selection { background-color: var(--green); } -.blue::selection, .blue ::selection { background-color: var(--blue); } - -.cursor::selection { - color: var(--text); - background-color: var(--text); -} +.blue::selection, .blue ::selection { background-color: var(--blue); } a:link::selection, a:visited::selection, @@ -58,12 +55,15 @@ a:visited ::selection, body { margin: 0; font-family: "Ubuntu Mono"; - min-height: calc(100vh + 40px); max-width: 100vw; overflow-x: hidden; position: relative; } +body:has(footer) { + min-height: calc(100vh + 40px); +} + noscript { color: var(--red); } @@ -89,8 +89,17 @@ header > nav { } .cursor { - color: var(--text); + color: var(--crust); background-color: var(--text); +} + +.cursor::selection { + color: var(--text) !important; + background-color: transparent !important +} + +.window-container:focus .cursor, +.window-container:focus-within .cursor { animation: cursor-blink 1s infinite; } @@ -99,10 +108,25 @@ pre { } @keyframes cursor-blink { - 0% { opacity: 100%; } - 50% { opacity: 100%; } - 50.001% { opacity: 0%; } - 100% { opacity: 0%; } + 0% { + background-color: var(--text); + color: var(--crust); + } + + 50% { + background-color: var(--text); + color: var(--crust); + } + + 50.001% { + background-color: transparent; + color: var(--text); + } + + 100% { + background-color: transparent; + color: var(--text); + } } .header-title { @@ -140,6 +164,20 @@ pre { transition: border-color 0.1s; } +.window-input { + position: absolute; + display: block; + top: 0; + left: 0; + width: 0; + height: 0; + opacity: 0; +} + +[data-type-area] { + white-space: pre-wrap; +} + .window-outer { padding: 10px; } diff --git a/img/favicon.png b/img/favicon.png new file mode 100644 index 0000000..1385eb8 Binary files /dev/null and b/img/favicon.png differ diff --git a/index.html b/index.html index 6211888..a186446 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ <head> <title>trinkey's website!!!</title> <link rel="stylesheet" href="css/base.css"> + <link rel="icon" href="img/favicon.png"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> @@ -18,6 +19,7 @@ <meta property="og:description" content="meoww :3"> <meta name="twitter:description" content="meoww :3"> + <script src="js/shell.js"></script> <script> let _themeMM = matchMedia("(prefers-color-scheme: light)"); let light = _themeMM.matches; @@ -72,200 +74,37 @@ </footer> <div hidden id="window-templates"> - <div data-template-id="about"> - <div data-template-field="title">~/about</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~/about</b>$ cat about-me.txt</div> - <div><b>hi there! i'm trinkey!</b></div> - <div>--------------------</div> - <div>i'm a silly little kitty cat who lives in the usa.</div> - <div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/her, they/them and it/its are also fine) and a bit gay sometimes.</div> - <div>i'm not actively in a relationship, however i'm also not looking to get into one either.</div> - <div>--------------------</div> - <div>i like to code stuff (mostly websites)! some of my programs can be found on the <a href="javascript:windowPreset('projects')">projects page</a>. i know a few languages, those being python, javascript/typescript, html/css (if you count those), and a little bit of java.</div> - <div>--------------------</div> - <div>well, that's about it! i hope you like my website!</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/about</b>$ <i class="cursor">.</i></div> - </div> - </div> - <div data-template-id="socials"> - <div data-template-field="title">~/socials</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~/socials</b>$ cat socials.html</div> - <div>- fedi - <b>@trinkey@trinkey.com</b> (or @trinkey@is.trinkey.com)</div> - <div>- forgejo - <a href="https://git.trinkey.com/trinkey/" target="_blank"><b>trinkey</b></a></div> - <div>- github - <a href="https://github.com/trinkey/" target="_blank"><b>trinkey</b></a></div> - <div>- git.gay - <a href="https://git.gay/trinkey/" target="_blank"><b>trinkey</b></a> (inactive)</div> - <div>- smiggins - <a href="https://smiggins.trinkey.com/u/trinkey/" target="_blank"><b>trinkey</b></a></div> - <div>- signal - <b>@trinkey.01</b></div> - <div>- email - <b>trinkey [at] proton [dot] me</b></div> - <div>- youtube - <a href="https://youtube.com/@trinkey" target="_blank"><b>@trinkey</b></a> (inactive)</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/socials</b>$ <i class="cursor">.</i></div> - </div> - </div> - <div data-template-id="people"> - <div data-template-field="title">~/webrings</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~/buttons</b>$ cat 88x31.html</div> - <div><b>my button:</b> (click to copy html)</div> - <div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of my cat on the right with the word trinkey taking up the rest of the button." title="trinkey's 88x31. image of my cat on the right with the word trinkey taking up the rest of the button." onclick="copyButton()"></div> - <div>--------------------</div> - <div><b>cool people:</b></div> - <div class="buttons-88x31"> - <a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc" title="notfire.cc"></a> - <a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro"" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro""></a> - <a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a> - <a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a> - <a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a> - </div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/buttons</b>$ cd ~/testimonials</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/testimonials</b>$ cat testimonials.html</div> - <div>"warning: this user is trinkey"</div> - <div>- <a href="https://booping.synth.download/@breaadyboy" target="_blank">bread</a></div><br> - <div>"This user is only slightly crazy once was I. 10/10 would recommend"</div> - <div>- <a href="https://lea.pet/@subroutine" target="_blank">subroutine</a></div><br> - <div>"the f slur but repeated 36 times"</div> - <div>- <a href="https://oomfie.city/@cornfields74">corn fields seventy four</a></div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/testimonials</b>$ cd ~/webrings</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/webrings</b>$ cat rings.html</div> - <div> - <a href="https://ctp-webr.ing/trinkey/previous">←</a> - <a href="https://ctp-webr.ing/">catppuccin webring</a> - <a href="https://ctp-webr.ing/trinkey/next">→</a> - </div> - <div> - <a href="https://fediring.net/previous?host=trinkey.com">←</a> - <a href="https://fediring.net/">fediring</a> - <a href="https://fediring.net/next?host=trinkey.com">→</a> - </div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/webrings</b>$ <i class="cursor">.</i></div> - </div> - </div> - <div data-template-id="projects"> - <div data-template-field="title">~/projects</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~/projects</b>$ cat projects.html</div> - <div><b>projects</b> - the things i made</div> - <div>- <a href="https://github.com/jerimiah-smiggins/smiggins/" target="_blank"><b>smiggins</b></a> (<a href="https://smiggins.trinkey.com/" target="_blank">website</a>) - a social media platform i made</div> - <div>- <a href="https://git.trinkey.com/trinkey/website/" target="_blank"><b>this website</b></a> - check out the code</div> - <div>- <a href="https://git.gay/trinkey/dotindex/" target="_blank"><b>dotindex</b></a> (<a href="https://pypi.org/project/DotIndex/" target="_blank">pypi</a>) - a python library that lets you access dicts using the dot notation (dict.key) instead of whatever python does (dict["key"])</div> - <div>- <a href="https://git.gay/trinkey/infopage/" target="_blank"><b>infopage</b></a> (<a href="https://infpg.pythonanywhere.com/" target="_blank">website</a>) - my very own pronouns.page clone</div> - <div>- <a href="https://git.trinkey.com/t" target="_blank"><b>tSuite</b></a> (<a href="https://auth.trinkey.com/" target="_blank">website</a>) - a collection of services that are all interconnected</div><br> - <div>i'll likely add more in the future, these are just the ones i'm most proud of at the moment.</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~/projects</b>$ <i class="cursor">.</i></div> - </div> - </div> - <div data-template-id="directory"> - <div data-template-field="title">~/dir</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~/dir</b>$ cat subdomains.html</div> - <div>there's a lot that goes into this website. here are some links for your usage to help you navigate this hellhole</div><br> - <div><b><a href="https://trinkey.com/">trinkey.com</a>:</b></div> - <div>this is where you are right now</div><br> - <div>*<b><a href="https://akkofe.trinkey.com/">akkofe.trinkey.com</a>:</b></div> - <div>the frontend i use for <a href="https://fediverse.info/" target="_blank">fedi</a></div><br> - <div><b><a href="https://auth.trinkey.com/">auth.trinkey.com</a>:</b></div> - <div>authentication manager for tSuite</div><br> - <div><b><a href="https://blog.trinkey.com/">blog.trinkey.com</a>:</b></div> - <div>tBlog, from tSuite</div><br> - <div><b><a href="https://everyone.trinkey.com/">everyone.trinkey.com</a>:</b></div> - <div>frontend to a fedi bot that anyone can post to (@everyonebot@is.trinkey.com)</div><br> - <div>*<b><a href="https://git.trinkey.com/">git.trinkey.com</a>:</b></div> - <div>holds some of my git projects (older ones on <a href="https://github.com/trinkey/" target="_blank">github</a> or <a href="https://git.gay/trinkey/" target="_blank">git.gay</a>)</div><br> - <div>*<b><a href="https://is.trinkey.com/">is.trinkey.com</a>:</b></div> - <div>hosts <a href="https://iceshrimp.dev/iceshrimp/iceshrimp.net" target="_blank">iceshrimp.net</a>, which is the fedi backend i use</div><br> - <div><b><a href="https://message.trinkey.com/">message.trinkey.com</a>:</b></div> - <div>tMessage, from tSuite</div><br> - <div><b><a href="https://music.trinkey.com/">music.trinkey.com</a>:</b></div> - <div>has some music. i haven't actually updated the site in a while but i've been meaning to do rewrite it at some point. 100% legal i pinky promise</div><br> - <div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div> - <div>official jerimiah smiggins instance, that being my own social media platform</div><br> - <div>(asterisk (*) means i haven't written the code for it)</div><br> - <div><b class="green">trinkey@website</b>:<b class="blue">~/dir</b>$ <i class="cursor">.</i></div> - </div> - </div> - <div data-template-id="specs"> - <div data-template-field="title">~</div> - <div data-template-field="content"> - <div><b class="green">trinkey@website</b>:<b class="blue">~</b>$ ssh trinkey@desktop</div> - <div>Last login: Tue Sep 11 8:46:40 2001 from 192.168.1.254</div> - <div><b class="green">trinkey@desktop</b>:<b class="blue">~</b>$ neofetch</div> - -<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">desktop</b> - <b>.-MMMMMMMMMMMMMMM-.</b> --------------- - <b>.-MMMM<span class="green">`..-:::::::-..`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.3 x86_64 - <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: MS-7E27 1.0 - <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 1680x1050, 2560x1440 - <b>`:MMM<span class="green">:MM` :MMMM:....::-...-MMMM:</span>MMM:`</b> <b class="green">DE</b>: Cinnamon 6.0.4 - <b>:MMM<span class="green">:MMM` :MM:` `` `` `:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) -<b>.MMM<span class="green">.MMMM` :MM. -MM. .MM- `MMMM.</span>MMM.</b> <b class="green">CPU</b>: AMD Ryzen 9 7950X (32) @ 5.881GHz -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 03:00.0 Device 747e -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM:</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 11:00.0 Device 164e -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> <b class="green">Memory</b>: 1MiB / 127901MiB -<b>.MMM<span class="green">.MMMM` :MM:--:MM:--:MM: `MMMM.</span>MMM.</b> - <b>:MMM<span class="green">:MMM- `-MMMMMMMMMMMM-` -MMM-</span>MMM:</b> - <b>:MMM<span class="green">:MMM:` `:MMM:</span>MMM:</b> - <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> - <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> - <b>'.-MMMM<span class="green">``--:::::--``</span>MMMM-.'</b> - <b>'-MMMMMMMMMMMMM-'</b> - <b>``-:::::-``</b></pre> - - <div><b class="green">trinkey@desktop</b>:<b class="blue">~</b>$ exit</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~</b>$ ssh trinkey@laptop</div> - <div>Last login: Tue Sep 11 8:46:40 2001 from 192.168.1.254</div> - <div><b class="green">trinkey@laptop</b>:<b class="blue">~</b>$ neofetch</div> - -<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">laptop</b> - <b>.-MMMMMMMMMMMMMMM-.</b> --------------- - <b>.-MMMM<span class="green">`..-:::::::-..`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 - <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Dell G15 5510 - <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 2560x1440, 1920x1080, 1680x1050 - <b>`:MMM<span class="green">:MM` :MMMM:....::-...-MMMM:</span>MMM:`</b> <b class="green">DE</b>: Cinnamon 5.8.4 - <b>:MMM<span class="green">:MMM` :MM:` `` `` `:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) -<b>.MMM<span class="green">.MMMM` :MM. -MM. .MM- `MMMM.</span>MMM.</b> <b class="green">CPU</b>: Intel i5-10500H (12) @ 4.500GHz -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> <b class="green">GPU</b>: NVIDIA GeForce RTX 3050 Ti Mobile -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM:</span>MMM:</b> <b class="green">GPU</b>: Intel CometLake-H GT2 [UHD Graphics] -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> <b class="green">Memory</b>: 2001MiB / 15765MiB -<b>.MMM<span class="green">.MMMM` :MM:--:MM:--:MM: `MMMM.</span>MMM.</b> - <b>:MMM<span class="green">:MMM- `-MMMMMMMMMMMM-` -MMM-</span>MMM:</b> - <b>:MMM<span class="green">:MMM:` `:MMM:</span>MMM:</b> - <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> - <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> - <b>'.-MMMM<span class="green">``--:::::--``</span>MMMM-.'</b> - <b>'-MMMMMMMMMMMMM-'</b> - <b>``-:::::-``</b></pre> - - <div><b class="green">trinkey@laptop</b>:<b class="blue">~</b>$ exit</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~</b>$ ssh trinkey@server</div> - <div>Last login: Tue Sep 11 9:03:02 2001 from 192.168.1.254</div> - <div><b class="green">trinkey@server</b>:<b class="blue">~</b>$ neofetch</div> - <pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">server</b> - <b>.-MMMMMMMMMMMMMMM-.</b> --------------- - <b>.-MMMM<span class="green">`..-:::::::-..`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 - <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Macmini7,1 1.0 - <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">CPU</b>: Intel i5-4278U (4) @ 3.100GHz - <b>`:MMM<span class="green">:MM` :MMMM:....::-...-MMMM:</span>MMM:`</b> <b class="green">GPU</b>: Intel Haswell-ULT - <b>:MMM<span class="green">:MMM` :MM:` `` `` `:MMM:</span>MMM:</b> <b class="green">Memory</b>: 9011MiB / 15866MiB -<b>.MMM<span class="green">.MMMM` :MM. -MM. .MM- `MMMM.</span>MMM.</b> -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM:</span>MMM:</b> -<b>:MMM<span class="green">:MMMM` :MM. -MM- .MM: `MMMM-</span>MMM:</b> -<b>.MMM<span class="green">.MMMM` :MM:--:MM:--:MM: `MMMM.</span>MMM.</b> - <b>:MMM<span class="green">:MMM- `-MMMMMMMMMMMM-` -MMM-</span>MMM:</b> - <b>:MMM<span class="green">:MMM:` `:MMM:</span>MMM:</b> - <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> - <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> - <b>'.-MMMM<span class="green">``--:::::--``</span>MMMM-.'</b> - <b>'-MMMMMMMMMMMMM-'</b> - <b>``-:::::-``</b></pre> - - <div><b class="green">trinkey@server</b>:<b class="blue">~</b>$ exit</div> - <div><b class="green">trinkey@website</b>:<b class="blue">~</b>$ <i class="cursor">.</i></div> - </div> + <ol data-template-id="about"> + <li>cat about-me.txt</li> + </ol> + <ol data-template-id="socials"> + <li>cat socials.txt</li> + </ol> + <ol data-template-id="people"> + <li>cd people</li> + <li>cat 88x31.txt</li> + <li>cat testimonials.txt</li> + <li>cat webrings.txt</li> + </ol> + <ol data-template-id="projects"> + <li>cat projects.txt</li> + </ol> + <ol data-template-id="directory"> + <li>cat subdomains.txt</li> + </ol> + <ol data-template-id="specs"> + <li>_internal_set_ps1 trinkey@desktop|ssh trinkey@desktop<br>Last login: Tue Sep 10 12:00:24 2024 from 192.168.1.254</li> + <li>_internal_neofetch desktop</li> + <li>_internal_set_ps1 trinkey@website|exit</li> + <li>_internal_set_ps1 trinkey@laptop|ssh trinkey@laptop<br>Last login: Tue Sep 11 8:46:40 2001 from 192.168.1.254</li> + <li>_internal_neofetch laptop</li> + <li>_internal_set_ps1 trinkey@website|exit</li> + <li>_internal_set_ps1 trinkey@server|ssh trinkey@server<br>Last login: Tue Sep 11 9:03:02 2001 from 192.168.1.254</li> + <li>_internal_neofetch server</li> + <li>_internal_set_ps1 trinkey@website|exit</li> <div data-template-field="width" data-is-number>1000</div> <div data-template-field="height" data-is-number>800</div> - </div> + </ol> </div> <script src="js/index.js"></script> diff --git a/js/blobcat.js b/js/blobcat.js index 87eb340..5b2ca15 100644 --- a/js/blobcat.js +++ b/js/blobcat.js @@ -1,92 +1,71 @@ let points = [[0.3971449136734009, 0.2810116410255432, -0.3767290413379669], [0.23243144154548645, 0.3703259527683258, -0.3767290413379669], [0.30626851320266724, 0.23012861609458923, 0.08654531836509705], [0.15029972791671753, -0.29410237073898315, -0.3767290413379669], [0.3065343499183655, -0.2082172930240631, -0.3767290413379669], [0.3111562132835388, -0.06059299036860466, 0.13771235942840576], [0.16147799789905548, -0.09371967613697052, 0.23468825221061707], [0.36976975202560425, 0.12388059496879578, 0.08632178604602814], [0.31842663884162903, -0.019846681505441666, 0.3223131597042084], [0.20939035713672638, 0.15419937670230865, 0.2242213636636734], [0.17552560567855835, 0.012471526861190796, 0.27797016501426697], [0.16723567247390747, 0.1499461680650711, -0.3767290413379669], [0.4233801066875458, 0.1499461680650711, -0.3767290413379669], [0.3200271725654602, -0.22131121158599854, 0.026444125920534134], [0.15823997557163239, -0.25929194688796997, 0.0673588365316391], [0.44944703578948975, 0.1499461680650711, -0.06793694198131561], [0.2280614674091339, 0.36611324548721313, -0.2975170314311981], [0.38751867413520813, 0.2724340260028839, -0.293896347284317], [0.2147921323776245, 0.3551100790500641, -0.2049424648284912], [0.3594103455543518, 0.2775343060493469, -0.19233158230781555], [0.0, 0.42153987288475037, -0.2989161014556885], [0.0, 0.4475290775299072, -0.3767290413379669], [0.0, 0.10908085107803345, 0.23466001451015472], [0.0, -0.29616573452949524, 0.08896146714687347], [0.0, 0.1499461680650711, -0.3767290413379669], [0.0, -0.1545914262533188, 0.23468825221061707], [0.0, 0.012471422553062439, 0.28773677349090576], [0.0, -0.29410237073898315, -0.3767290413379669], [0.0, 0.3850858211517334, -0.2098153829574585], [0.15266574919223785, 0.33635783195495605, 0.08654531091451645], [0.0, 0.34858277440071106, 0.08654531836509705], [0.3163855969905853, -0.30245065689086914, -0.2832803726196289], [0.15313652157783508, -0.3652721643447876, -0.2640822231769562], [0.43641358613967896, 0.1499461680650711, -0.2795500159263611], [0.0, -0.3652721643447876, -0.2640821933746338], [0.3884555399417877, 0.012471545487642288, -0.3767290413379669], [0.34046298265457153, 0.012471519410610199, 0.11201705783605576], [0.1587677001953125, 0.01247154176235199, -0.3767290413379669], [0.384737104177475, 0.012471544556319714, -0.026815753430128098], [0.0, 0.012471549212932587, -0.3767290413379669], [0.39989548921585083, 0.012471545487642288, -0.30725497007369995], [-0.3971449136734009, 0.2810116410255432, -0.3767290413379669], [-0.23243144154548645, 0.3703259527683258, -0.3767290413379669], [-0.30626851320266724, 0.23012861609458923, 0.08654531836509705], [-0.15029972791671753, -0.29410237073898315, -0.3767290413379669], [-0.3065343499183655, -0.2082172930240631, -0.3767290413379669], [-0.3111562132835388, -0.06059299036860466, 0.13771235942840576], [-0.16147799789905548, -0.09371967613697052, 0.23468825221061707], [-0.36976975202560425, 0.12388059496879578, 0.08632178604602814], [-0.31842663884162903, -0.019846681505441666, 0.3223131597042084], [-0.20939035713672638, 0.15419937670230865, 0.2242213636636734], [-0.17552560567855835, 0.012471526861190796, 0.27797016501426697], [-0.16723567247390747, 0.1499461680650711, -0.3767290413379669], [-0.4233801066875458, 0.1499461680650711, -0.3767290413379669], [-0.3200271725654602, -0.22131121158599854, 0.026444125920534134], [-0.15823997557163239, -0.25929194688796997, 0.0673588365316391], [-0.44944703578948975, 0.1499461680650711, -0.06793694198131561], [-0.2280614674091339, 0.36611324548721313, -0.2975170314311981], [-0.38751867413520813, 0.2724340260028839, -0.293896347284317], [-0.2147921323776245, 0.3551100790500641, -0.2049424648284912], [-0.3594103455543518, 0.2775343060493469, -0.19233158230781555], [-0.15266574919223785, 0.33635783195495605, 0.08654531091451645], [-0.3163855969905853, -0.30245065689086914, -0.2832803726196289], [-0.15313652157783508, -0.3652721643447876, -0.2640822231769562], [-0.43641358613967896, 0.1499461680650711, -0.2795500159263611], [-0.3884555399417877, 0.012471545487642288, -0.3767290413379669], [-0.34046298265457153, 0.012471519410610199, 0.11201705783605576], [-0.1587677001953125, 0.01247154176235199, -0.3767290413379669], [-0.384737104177475, 0.012471544556319714, -0.026815753430128098], [-0.39989548921585083, 0.012471545487642288, -0.30725497007369995]]; const connections = [[0, 1], [0, 12], [0, 17], [1, 11], [1, 16], [1, 21], [2, 7], [2, 9], [2, 19], [2, 29], [3, 4], [3, 27], [3, 32], [3, 37], [4, 31], [4, 35], [5, 6], [5, 8], [5, 13], [5, 36], [6, 8], [6, 10], [6, 14], [6, 25], [7, 8], [7, 9], [7, 15], [7, 36], [8, 9], [8, 10], [8, 36], [9, 10], [9, 22], [9, 29], [10, 26], [11, 12], [11, 24], [11, 37], [12, 33], [12, 35], [13, 14], [13, 31], [13, 38], [14, 23], [14, 32], [15, 19], [15, 33], [15, 38], [16, 17], [16, 18], [16, 20], [17, 19], [17, 33], [18, 19], [18, 28], [18, 29], [20, 21], [20, 28], [20, 57], [21, 24], [21, 42], [22, 26], [22, 30], [22, 50], [23, 25], [23, 34], [23, 55], [24, 39], [24, 52], [25, 26], [25, 47], [26, 51], [27, 34], [27, 39], [27, 44], [28, 30], [28, 59], [29, 30], [30, 61], [31, 32], [31, 40], [32, 34], [33, 40], [34, 63], [35, 37], [35, 40], [36, 38], [37, 39], [38, 40], [39, 67], [41, 42], [41, 53], [41, 58], [42, 52], [42, 57], [43, 48], [43, 50], [43, 60], [43, 61], [44, 45], [44, 63], [44, 67], [45, 62], [45, 65], [46, 47], [46, 49], [46, 54], [46, 66], [47, 49], [47, 51], [47, 55], [48, 49], [48, 50], [48, 56], [48, 66], [49, 50], [49, 51], [49, 66], [50, 51], [50, 61], [52, 53], [52, 67], [53, 64], [53, 65], [54, 55], [54, 62], [54, 68], [55, 63], [56, 60], [56, 64], [56, 68], [57, 58], [57, 59], [58, 60], [58, 64], [59, 60], [59, 61], [62, 63], [62, 69], [64, 69], [65, 67], [65, 69], [66, 68], [68, 69]]; -const blobcatColor = { light: "#df8e1d", dark: "#f9e2af" } +const blobcatColor = { light: "#df8e1d", dark: "#f9e2af" }; const focalLength = 1; const radians = (2 * Math.PI / 360) * 3; const rotationalMatrix = [ - [Math.cos(radians), -Math.sin(radians), 0], - [Math.sin(radians), Math.cos(radians), 0], - [0, 0, 1] -] - + [Math.cos(radians), -Math.sin(radians), 0], + [Math.sin(radians), Math.cos(radians), 0], + [0, 0, 1] +]; let blobcatElement = null; - function dot(a, b) { - const rows = a.length; - const colsA = a[0].length; - const colsB = b[0].length; - const result = Array.from({ length: rows }, () => Array(colsB).fill(0)); - - for (let i = 0; i < rows; i++) { - for (let j = 0; j < colsB; j++) { - for (let k = 0; k < colsA; k++) { - result[i][j] += a[i][k] * b[k][j]; - } + const rows = a.length; + const colsA = a[0].length; + const colsB = b[0].length; + const result = Array.from({ length: rows }, () => Array(colsB).fill(0)); + for (let i = 0; i < rows; i++) { + for (let j = 0; j < colsB; j++) { + for (let k = 0; k < colsA; k++) { + result[i][j] += a[i][k] * b[k][j]; + } + } } - } - - return result; + return result; } - -function getLine( - x1, y1, x2, y2, - multiply, - offsetX, - offsetY, - zIndex -) { - let hr = document.createElement("hr"); - - x1 = x1 * multiply; - y1 = y1 * multiply; - x2 = x2 * multiply; - y2 = y2 * multiply; - - hr.style.position = "absolute"; - hr.style.left = `${x1 + offsetX}px`; - hr.style.top = `${y1 + offsetY}px`; - hr.style.height = `1px`; - hr.style.width = `${Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)))}px`; - hr.style.rotate = `${Math.atan2(y2 - y1, x2 - x1)}rad`; - hr.style.transformOrigin = `0 0.5px`; - hr.style.backgroundColor = light ? blobcatColor.light : blobcatColor.dark; - hr.style.border = "0"; - hr.style.zIndex = zIndex - - return hr; +function getLine(x1, y1, x2, y2, multiply, offsetX, offsetY, zIndex) { + let hr = document.createElement("hr"); + x1 = x1 * multiply; + y1 = y1 * multiply; + x2 = x2 * multiply; + y2 = y2 * multiply; + hr.style.position = "absolute"; + hr.style.left = `${x1 + offsetX}px`; + hr.style.top = `${y1 + offsetY}px`; + hr.style.height = `1px`; + hr.style.width = `${Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)))}px`; + hr.style.rotate = `${Math.atan2(y2 - y1, x2 - x1)}rad`; + hr.style.transformOrigin = `0 0.5px`; + hr.style.backgroundColor = light ? blobcatColor.light : blobcatColor.dark; + hr.style.border = "0"; + hr.style.zIndex = String(zIndex); + return hr; } - function blobcatFrame() { - if (!blobcatElement || !WINDOWS.blob) { return; } - - let screenCoordinates = points.map((a) => ([ - (focalLength * a[0]) / (a[1] + focalLength), - -(focalLength * a[2]) / (a[1] + focalLength) - ])); - - let hrElements = connections.map((a) => ( - getLine(...screenCoordinates[a[0]], ...screenCoordinates[a[1]], 300, WINDOWS.blob.width / 2 + 11, WINDOWS.blob.height / 2 + 11, WINDOWS.blob.zIndex) - )); - - blobcatElement.innerHTML = ""; - blobcatElement.append(...hrElements); - points = dot(points, rotationalMatrix); + if (!blobcatElement || !WINDOWS.blob) { + return; + } + let screenCoordinates = points.map((a) => ([ + (focalLength * a[0]) / (a[1] + focalLength), + -(focalLength * a[2]) / (a[1] + focalLength) + ])); + let hrElements = connections.map((a) => (getLine(...screenCoordinates[a[0]], ...screenCoordinates[a[1]], 300, WINDOWS.blob.width / 2 + 11, WINDOWS.blob.height / 2 + 11, WINDOWS.blob.zIndex))); + blobcatElement.innerHTML = ""; + blobcatElement.append(...hrElements); + points = dot(points, rotationalMatrix); } - function createBlob() { - createWindow({ - id: "blob", - title: "~/spinny-cat", - content: "<div id='blobcat'></div>", - onDestroy: destroyBlob, - height: 450 - }); - - blobcatElement = document.getElementById("blobcat"); + createWindow({ + id: "blob", + title: "~/spinny-cat", + content: "<div id='blobcat'></div>", + onDestroy: destroyBlob, + height: 450, + typeable: false + }); + blobcatElement = document.getElementById("blobcat"); } - function destroyBlob() { - blobcatElement = null; + blobcatElement = null; } - setInterval(blobcatFrame, 1000 / 30); diff --git a/js/index.js b/js/index.js index 90b329a..5357657 100644 --- a/js/index.js +++ b/js/index.js @@ -1,57 +1,42 @@ -/* config: { - title: string, - content: string, - id: string, - width?: number = 600, - height?: number = 400, - minWidth?: number = 200, - minHeight?: number = 200, - posX?: number = null, // automatically centers window based on w/h - posY?: number = null, - onDestroy?: () => void -} */ - let WINDOWS = {}; let MOUSE_MOVE_PROCESSING = {}; let globalIncrement = 1; - -function createWindow(config) { - if (document.getElementById(config.id)) { - WINDOWS[config.id].element.style.zIndex = globalIncrement; - WINDOWS[config.id].zIndex = globalIncrement; +function escapeHTML(string) { + return string.replaceAll("&", "&").replaceAll("<", "<").replaceAll("\"", """); +} +function incrementZIndex(windowID, focus = false) { + WINDOWS[windowID].element.style.zIndex = String(globalIncrement); + WINDOWS[windowID].zIndex = globalIncrement; globalIncrement++; - return; - } - - // 1 - border - // 10 - padding - // 35 - header - let _windowPaddingX = 1*2 + 10*2; - let _windowPaddingY = 1*2 + 10*2 + 35; - - config.width = config.width || 600; - config.height = config.height || 400; - config.minWidth = config.minWidth || 200; - config.minHeight = config.minHeight || 200; - - let realWidth = Math.max(config.minWidth, Math.min(config.width, innerWidth - _windowPaddingX - 20)); - let realHeight = Math.max(config.minHeight, Math.min(config.height, innerHeight - _windowPaddingY - 20)); - - let posX = config.posX || Math.round((innerWidth / 2) - ((realWidth + _windowPaddingX) / 2)); - let posY = config.posY || Math.round((innerHeight / 2) - ((realHeight + _windowPaddingY) / 2)); - - let wO = document.createElement("div"); - wO.classList.add("window-outer"); - - let w = document.createElement("div"); - w.classList.add("window"); - w.style.width = `${realWidth}px`; - w.style.height = `${realHeight}px`; - w.innerHTML = config.content; - - let wH = document.createElement("div"); - wH.classList.add("window-header"); - wH.innerHTML = ` + if (focus) { + WINDOWS[windowID].element.focus(); + } +} +function createWindow(config) { + if (document.getElementById(config.id)) { + incrementZIndex(config.id); + return; + } + let _windowPaddingX = 1 * 2 + 10 * 2; + let _windowPaddingY = 1 * 2 + 10 * 2 + 35; + config.width = config.width || 600; + config.height = config.height || 400; + config.minWidth = config.minWidth || 200; + config.minHeight = config.minHeight || 200; + let realWidth = Math.max(config.minWidth, Math.min(config.width, innerWidth - _windowPaddingX - 20)); + let realHeight = Math.max(config.minHeight, Math.min(config.height, innerHeight - _windowPaddingY - 20)); + let posX = config.posX || Math.round((innerWidth / 2) - ((realWidth + _windowPaddingX) / 2)); + let posY = config.posY || Math.round((innerHeight / 2) - ((realHeight + _windowPaddingY) / 2)); + let wO = document.createElement("div"); + wO.classList.add("window-outer"); + let w = document.createElement("div"); + w.classList.add("window"); + w.style.width = `${realWidth}px`; + w.style.height = `${realHeight}px`; + w.innerHTML = config.content; + let wH = document.createElement("div"); + wH.classList.add("window-header"); + wH.innerHTML = ` <i class="window-header-button blank"></i> <i class="window-header-button blank"></i> <i class="window-header-button blank"></i> @@ -60,135 +45,185 @@ function createWindow(config) { <i data-no-move class="window-header-button fullscreen"></i> <i data-no-move class="window-header-button close"></i> `; - - let wC = document.createElement("div"); - wC.classList.add("window-container"); - wC.style.left = `${posX}px`; - wC.style.top = `${posY}px`; - wC.id = config.id; - wC.style.zIndex = globalIncrement; - wC.style.width = `${realWidth + _windowPaddingX - 2}px`; - - wO.append(w) - wC.append(wH, wO); - - document.body.append(wC); - WINDOWS[config.id] = { - element: wC, - height: realHeight, - width: realWidth, - posX: posX, - posY: posY, - mouseDown: false, - fullscreen: false, - zIndex: globalIncrement, - vars: {} - }; - - function mouseMoveEvent(x, y) { - WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX)); - WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY)); - wC.style.left = `${WINDOWS[config.id].posX}px`; - wC.style.top = `${WINDOWS[config.id].posY}px`; - } - - wC.addEventListener("mousedown", function() { - wC.style.zIndex = globalIncrement; - WINDOWS[config.id].zIndex = globalIncrement; + let wC; + let wI = null; + if (config.typeable !== false) { + function syncInputs() { + setTimeout(function () { + let text = wI.value; + let cursor = wI.selectionStart; + let el = wC.querySelector("[data-type-area]"); + if (!el) { + return; + } + if (cursor == text.length) { + el.innerHTML = `${escapeHTML(text)}<i class="cursor"> </i>`; + } + else { + el.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`; + } + }, 1); + } + function setCursor() { + setTimeout(() => { + wI.setSelectionRange(wI.value.length, wI.value.length); + syncInputs(); + }, 0); + } + wI = document.createElement("input"); + wI.classList.add("window-input"); + wI.id = `${config.id}__input`; + wI.oninput = (event) => { + syncInputs(); + w.scrollTop = w.scrollHeight; + }; + wI.onkeydown = (event) => { + if (event.key == "Enter") { + commandManager(config.id, wI.value.trim()); + w.scrollTop = w.scrollHeight; + wI.value = ""; + } + else { + syncInputs(); + } + }; + wI.onfocus = setCursor; + wI.onclick = setCursor; + wC = document.createElement("label"); + wC.htmlFor = `${config.id}__input`; + } + else { + wC = document.createElement("div"); + } + wC.classList.add("window-container"); + wC.tabIndex = 0; + wC.style.left = `${posX}px`; + wC.style.top = `${posY}px`; + wC.id = config.id; + wC.style.zIndex = String(globalIncrement); + wC.style.width = `${realWidth + _windowPaddingX - 2}px`; + wO.append(w); + wC.append(wH, wO); + document.body.append(wC); + if (config.typeable !== false) { + wC.append(wI); + wI.focus(); + } + else { + wC.focus(); + } + WINDOWS[config.id] = { + element: wC, + height: realHeight, + width: realWidth, + posX: posX, + posY: posY, + fullscreen: false, + zIndex: globalIncrement, + vars: {} + }; + function mouseMoveEvent(x, y) { + WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX)); + WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY)); + wC.style.left = `${WINDOWS[config.id].posX}px`; + wC.style.top = `${WINDOWS[config.id].posY}px`; + } + wC.addEventListener("focus", function () { incrementZIndex(config.id); }); + for (const link of wC.querySelectorAll("a")) { + link.addEventListener("focus", function () { incrementZIndex(config.id); }); + } + wH.addEventListener("mousedown", function (e) { + if (e.target.dataset.noMove !== undefined) { + return; + } + WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX; + WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY; + MOUSE_MOVE_PROCESSING[config.id] = { + callback: mouseMoveEvent, + mouseUp: true + }; + }); + wH.querySelector(".close").addEventListener("click", function () { + delete WINDOWS[config.id]; + delete windowInformation[config.id]; + delete MOUSE_MOVE_PROCESSING[config.id]; + wC.remove(); + if (typeof config.onDestroy === "function") { + config.onDestroy(); + } + }); + wH.querySelector(".fullscreen").addEventListener("click", function () { + if (WINDOWS[config.id].fullscreen) { + WINDOWS[config.id].posX = WINDOWS[config.id].vars.oldPosX; + WINDOWS[config.id].posY = WINDOWS[config.id].vars.oldPosY; + WINDOWS[config.id].width = WINDOWS[config.id].vars.oldWidth; + WINDOWS[config.id].height = WINDOWS[config.id].vars.oldHeight; + WINDOWS[config.id].fullscreen = false; + delete WINDOWS[config.id].vars.oldPosX; + delete WINDOWS[config.id].vars.oldPosY; + delete WINDOWS[config.id].vars.oldWidth; + delete WINDOWS[config.id].vars.oldHeight; + wC.style.left = `${WINDOWS[config.id].posX}px`; + wC.style.top = `${WINDOWS[config.id].posY}px`; + wC.style.width = `${WINDOWS[config.id].width + _windowPaddingX - 2}px`; + w.style.width = `${WINDOWS[config.id].width}px`; + w.style.height = `${WINDOWS[config.id].height}px`; + } + else { + WINDOWS[config.id].vars.oldPosX = WINDOWS[config.id].posX; + WINDOWS[config.id].vars.oldPosY = WINDOWS[config.id].posY; + WINDOWS[config.id].vars.oldWidth = WINDOWS[config.id].width; + WINDOWS[config.id].vars.oldHeight = WINDOWS[config.id].height; + WINDOWS[config.id].fullscreen = true; + WINDOWS[config.id].posX = 0; + WINDOWS[config.id].posY = 0; + WINDOWS[config.id].width = innerWidth; + WINDOWS[config.id].height = innerHeight; + wC.style.left = "0px"; + wC.style.top = "0px"; + wC.style.width = `${WINDOWS[config.id].width}px`; + w.style.width = `${WINDOWS[config.id].width - _windowPaddingX}px`; + w.style.height = `${WINDOWS[config.id].height - _windowPaddingY}px`; + } + }); globalIncrement++; - }) - - wH.addEventListener("mousedown", function(e) { - if (e.target.dataset.noMove !== undefined) { - return; - } - - WINDOWS[config.id].mouseDown = true; - WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX; - WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY; - MOUSE_MOVE_PROCESSING[config.id] = { - callback: mouseMoveEvent, - mouseUp: true - }; - }); - - wH.querySelector(".close").addEventListener("click", function() { - delete WINDOWS[config.id]; - delete MOUSE_MOVE_PROCESSING[config.id]; - wC.remove(); - - if (typeof config.onDestroy === "function") { - config.onDestroy(); - } - }); - - wH.querySelector(".fullscreen").addEventListener("click", function() { - if (WINDOWS[config.id].fullscreen) { - WINDOWS[config.id].posX = WINDOWS[config.id].vars.oldPosX; - WINDOWS[config.id].posY = WINDOWS[config.id].vars.oldPosY; - WINDOWS[config.id].width = WINDOWS[config.id].vars.oldWidth; - WINDOWS[config.id].height = WINDOWS[config.id].vars.oldHeight; - WINDOWS[config.id].fullscreen = false; - delete WINDOWS[config.id].vars.oldPosX; - delete WINDOWS[config.id].vars.oldPosY; - delete WINDOWS[config.id].vars.oldWidth; - delete WINDOWS[config.id].vars.oldHeight; - wC.style.left = `${WINDOWS[config.id].posX}px`; - wC.style.top = `${WINDOWS[config.id].posY}px`; - wC.style.width = `${WINDOWS[config.id].width + _windowPaddingX - 2}px`; - w.style.width = `${WINDOWS[config.id].width}px`; - w.style.height = `${WINDOWS[config.id].height}px`; - } else { - WINDOWS[config.id].vars.oldPosX = WINDOWS[config.id].posX; - WINDOWS[config.id].vars.oldPosY = WINDOWS[config.id].posY; - WINDOWS[config.id].vars.oldWidth = WINDOWS[config.id].width; - WINDOWS[config.id].vars.oldHeight = WINDOWS[config.id].height; - WINDOWS[config.id].fullscreen = true; - WINDOWS[config.id].posX = 0; - WINDOWS[config.id].posY = 0; - WINDOWS[config.id].width = innerWidth; - WINDOWS[config.id].height = innerHeight; - wC.style.left = "0px"; - wC.style.top = "0px"; - wC.style.width = `${WINDOWS[config.id].width}px`; - w.style.width = `${WINDOWS[config.id].width - _windowPaddingX}px`; - w.style.height = `${WINDOWS[config.id].height - _windowPaddingY}px`; - } - }); - - globalIncrement++; } - -window.addEventListener("mousemove", function(e) { - for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { - MOUSE_MOVE_PROCESSING[key].callback(e.clientX, e.clientY); - } +window.addEventListener("mousemove", function (e) { + for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { + MOUSE_MOVE_PROCESSING[key].callback(e.clientX, e.clientY); + } }); - -window.addEventListener("mouseup", function() { - for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { - if (MOUSE_MOVE_PROCESSING[key].mouseUp) { - delete MOUSE_MOVE_PROCESSING[key]; - }; - } +window.addEventListener("mouseup", function () { + for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { + if (MOUSE_MOVE_PROCESSING[key].mouseUp) { + delete MOUSE_MOVE_PROCESSING[key]; + } + ; + } }); - function windowPreset(template) { - let el = document.querySelector(`#window-templates > [data-template-id="${template}"]`); - - if (!el) { return; } - - let config = { - id: template - }; - - for (const field of el.querySelectorAll("[data-template-field]")) { - config[field.dataset.templateField] = field.dataset.isNumber === "" ? +field.innerText : field.innerHTML; - } - - createWindow(config); + let el = document.querySelector(`#window-templates > [data-template-id="${template}"]`); + if (!el) { + return; + } + if (WINDOWS[template]) { + incrementZIndex(template, true); + return; + } + let config = { + id: template, + title: "~ - tSh", + content: "<div><b class=\"green\">trinkey@website</b>:<b class=\"blue\">~</b>$ <span data-type-area><i class=\"cursor\"> </i></span></div>" + }; + for (const field of el.querySelectorAll("[data-template-field]")) { + config[field.dataset.templateField] = field.dataset.isNumber === "" ? +field.innerText : field.innerHTML; + } + createWindow(config); + for (const command of el.querySelectorAll("li")) { + WINDOWS[template].element.querySelector("[data-type-area]").innerHTML = command.innerHTML; + commandManager(template, command.innerHTML); + } } - function copyButton() { - navigator.clipboard.writeText("<a href=\"https://trinkey.com/\" target=\"_blank\"><img src=\"https://trinkey.com/img/88x31.png\" alt=\"trinkey's 88x31. image of her cat on the right with the word trinkey taking up the rest of the button.\" title=\"trinkey's 88x31. image of her cat on the right with the word trinkey taking up the rest of the button.\"></a>"); + navigator.clipboard.writeText("<a href=\"https://trinkey.com/\" target=\"_blank\"><img src=\"https://trinkey.com/img/88x31.png\" alt=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\" title=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\"></a>"); } diff --git a/js/shell.js b/js/shell.js new file mode 100644 index 0000000..3c41c0a --- /dev/null +++ b/js/shell.js @@ -0,0 +1,449 @@ +function _internal_joinPaths(path1, path2) { + if (path2[0] == "/") { + return _internal_sanitizePath(path2); + } + else if (path2[0] == "~" && (path2.length == 1 || path2[1] == "/")) { + return _internal_sanitizePath(HOME_DIR + path2.slice(1)); + } + else { + return _internal_sanitizePath(path1 + "/" + path2); + } +} +function _internal_sanitizePath(path) { + while (path.includes("//")) { + path = path.replaceAll("//", "/"); + } + while (path[path.length - 1] == "/") { + path = path.slice(0, path.length - 1); + } + let newPath = []; + for (const dir of path.split("/")) { + if (dir == ".") { + } + else if (dir == "..") { + if (newPath.length) { + newPath = newPath.slice(0, newPath.length - 1); + } + } + else if (dir) { + newPath.push(dir); + } + } + return "/" + newPath.join("/"); +} +function _internal_getFile(path) { + function getFile_recursive(files, path) { + console.log(files, path); + let file = files[path[0]]; + if (!file) { + return null; + } + if (path.length == 1) { + return file; + } + if (file.type == "file") { + return null; + } + return getFile_recursive(file.files, path.slice(1)); + } + if (path == "/") { + return { + type: "directory", + name: "/", + files: FILESYSTEM + }; + } + let dirs = path.split("/"); + if (dirs[0] == "") { + dirs = dirs.slice(1); + } + return getFile_recursive(FILESYSTEM, dirs); +} +function _internal_stringifyPath(path) { + if (path.startsWith(HOME_DIR)) { + return path.replace(HOME_DIR, "~"); + } + return path; +} +function _internal_getPS1(winInfo) { + return `<b class="green">${winInfo.ps1Override || "trinkey@website"}</b>:<b class="blue">${_internal_stringifyPath(winInfo.PWD)}</b>$ `; +} +function _internal_getFlags(command) { + let flags = []; + let newCommand = []; + for (const thing of command.split(" ")) { + if (thing.startsWith("-")) { + flags.push(...thing.slice(1).split("")); + } + else { + newCommand.push(thing); + } + } + return { + flags: flags, + removed: newCommand.join(" ") + }; +} +function _internal_fileSize(size) { + let suffix = ""; + let amount = size; + const sizes = [ + { suffix: "K", amount: 1024, threshold: 10000 }, + { suffix: "M", amount: Math.pow(1024, 2), threshold: 1000000 } + ]; + for (const info of sizes) { + if (size > info.threshold) { + suffix = info.suffix; + amount = Math.round(size / info.amount * 10) / 10; + if (String(amount).length > 3) { + amount = Math.round(info.amount); + } + } + } + return `${amount}${suffix}`.padStart(4, " "); +} +function cat(path, windowID) { + if (!path) { + return "<div>cat: You must specify a file</div>"; + } + path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, path); + let file = _internal_getFile(path); + if (!file) { + return `<div>cat: ${escapeHTML(path)}: No such file or directory</div>`; + } + else if (file.type == "directory") { + return `<div>cat: ${escapeHTML(path)}: Is a directory</div>`; + } + else { + return file.content; + } +} +function cd(path, windowID) { + if (!path) { + windowInformation[windowID].PWD = HOME_DIR; + return ""; + } + let newPWD = _internal_joinPaths(windowInformation[windowID].PWD, path); + let newFileObj = _internal_getFile(newPWD); + if (newFileObj === null) { + return `<div>cd: ${escapeHTML(path)}: No such file or directory</div>`; + } + else if (newFileObj.type == "file") { + return `<div>cd: ${escapeHTML(path)}: Not a directory</div>`; + } + windowInformation[windowID].PWD = newPWD; + return ""; +} +function clear(path, windowID) { + WINDOWS[windowID].element.querySelector(".window").innerHTML = ""; + return ""; +} +function exit(path, windowID) { + setTimeout(() => { + WINDOWS[windowID].element.querySelector(".close").click(); + }, 1); + return ""; +} +function help(path, windowID) { + return helpText; +} +function ls(path, windowID) { + let { flags, removed } = _internal_getFlags(path); + path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, removed); + let file = _internal_getFile(path); + let files; + if (!file) { + return `<div>ls: ${escapeHTML(path)}: No such file or directory</div>`; + } + else if (file.type == "file") { + files = { [file.name]: file }; + } + else { + files = file.files; + } + let directories = []; + let hidden = flags.includes("a") || flags.includes("A"); + let long = flags.includes("l"); + let out = flags.includes("a") ? (long ? "<div>drwxrwxr-x trinkey trinkey 4096 <span class=\"blue\">.</span></div><div>drwxr-xr-x trinkey trinkey 4096 <span class=\"blue\">..</span></div>" : "<span class=\"blue\">.</span> <span class=\"blue\">..</span> ") : ""; + for (const file of Object.keys(files).sort((a, b) => (({ true: 1, false: -1 })[String(flags.includes("r") ? a < b : a > b)]))) { + if (file.startsWith(".") && !hidden) { + continue; + } + let fObj = files[file]; + if (long) { + if (fObj.type == "directory") { + directories.push(fObj); + out += `<div>drwxrwxr-x trinkey trinkey 4096 <span class=\"blue\">${escapeHTML(file)}</span></div>`; + } + else { + out += `<div>-rw-rw-r-- trinkey trinkey ${_internal_fileSize(fObj.content.length).replaceAll(" ", " ")} ${escapeHTML(file)}</div>`; + } + } + else { + if (fObj.type == "directory") { + directories.push(fObj); + out += `<span class=\"blue\">${escapeHTML(file)}</span> `; + } + else { + out += `${escapeHTML(file)} `; + } + } + } + if (!long) { + out = out.slice(0, out.length - 7); + } + if (flags.includes("R")) { + for (const dir of directories) { + out += `<div><br>${escapeHTML(_internal_stringifyPath(_internal_joinPaths(path, dir.name)))}:</div>${ls(`${_internal_joinPaths(path, dir.name)} -${flags.join("")}`, windowID)}`; + } + } + return out; +} +function _internal_set_ps1(args, windowID) { + windowInformation[windowID].ps1Override = args.split("|")[0]; + WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = args.split("|")[1]; + return ""; +} +function _internal_neofetch(args, windowID) { + WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = "neofetch"; + return _internal_neofetchOutputs[args]; +} +const _internal_commands = { + cat: cat, + cd: cd, + clear: clear, + help: help, + ls: ls, + exit: exit, + _internal_set_ps1: _internal_set_ps1, + _internal_neofetch: _internal_neofetch +}; +const _internal_defaultFiles = { + about: `<div><b>hi there! i'm trinkey!</b></div> + <div>--------------------</div> + <div>i'm a silly little kitty cat who lives in the usa.</div> + <div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/her, they/them and it/its are also fine).</div> + <div>i'm not actively in a relationship, however i'm also not looking to get into one either.</div> + <div>--------------------</div> + <div>i like to code stuff (mostly websites)! some of my programs can be found on the <a href="javascript:windowPreset('projects')">projects page</a>. i know a few languages, those being python, javascript/typescript, html/css (if you count those), and a little bit of java.</div> + <div>--------------------</div> + <div>well, that's about it! i hope you like my website!</div>`, + socials: `<div>- fedi - <b>@trinkey@trinkey.com</b> (or @trinkey@is.trinkey.com)</div> + <div>- forgejo - <a href="https://git.trinkey.com/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- github - <a href="https://github.com/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- git.gay - <a href="https://git.gay/trinkey/" target="_blank"><b>trinkey</b></a> (inactive)</div> + <div>- smiggins - <a href="https://smiggins.trinkey.com/u/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- signal - <b>@trinkey.01</b></div> + <div>- email - <b>trinkey [at] proton [dot] me</b></div> + <div>- youtube - <a href="https://youtube.com/@trinkey" target="_blank"><b>@trinkey</b></a> (inactive)</div>`, + buttons: `<div><b>my button:</b> (click to copy html)</div> + <div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." onclick="copyButton()"></div> + <div>--------------------</div> + <div><b>cool people:</b></div> + <div class="buttons-88x31"> + <a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc" title="notfire.cc"></a> + <a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro"" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro""></a> + <a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a> + <a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a> + <a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a> + </div>`, + testimonials: `<div>"warning: this user is trinkey"</div> + <div>- <a href="https://booping.synth.download/@breaadyboy" target="_blank">bread</a></div><br> + <div>"This user is only slightly crazy once was I. 10/10 would recommend"</div> + <div>- <a href="https://lea.pet/@subroutine" target="_blank">subroutine</a></div><br> + <div>"the f slur but repeated 36 times"</div> + <div>- <a href="https://oomfie.city/@cornfields74">corn fields seventy four</a></div>`, + webrings: `<div> + <a href="https://ctp-webr.ing/trinkey/previous">←</a> + <a href="https://ctp-webr.ing/">catppuccin webring</a> + <a href="https://ctp-webr.ing/trinkey/next">→</a> + </div> + <div> + <a href="https://fediring.net/previous?host=trinkey.com">←</a> + <a href="https://fediring.net/">fediring</a> + <a href="https://fediring.net/next?host=trinkey.com">→</a> + </div>`, + projects: `<div><b>projects</b> - the things i made</div> + <div>- <a href="https://github.com/jerimiah-smiggins/smiggins/" target="_blank"><b>smiggins</b></a> (<a href="https://smiggins.trinkey.com/" target="_blank">website</a>) - a social media platform i made</div> + <div>- <a href="https://git.trinkey.com/trinkey/website/" target="_blank"><b>this website</b></a> - check out the code</div> + <div>- <a href="https://git.gay/trinkey/dotindex/" target="_blank"><b>dotindex</b></a> (<a href="https://pypi.org/project/DotIndex/" target="_blank">pypi</a>) - a python library that lets you access dicts using the dot notation (dict.key) instead of whatever python does (dict["key"])</div> + <div>- <a href="https://git.gay/trinkey/infopage/" target="_blank"><b>infopage</b></a> (<a href="https://infpg.pythonanywhere.com/" target="_blank">website</a>) - my very own pronouns.page clone</div> + <div>- <a href="https://git.trinkey.com/t" target="_blank"><b>tSuite</b></a> (<a href="https://auth.trinkey.com/" target="_blank">website</a>) - a collection of services that are all interconnected</div><br> + <div>i'll likely add more in the future, these are just the ones i'm most proud of at the moment.</div>`, + directory: `<div>there's a lot that goes into this website. here are some links for your usage to help you navigate this hellhole</div><br> + <div><b><a href="https://trinkey.com/">trinkey.com</a>:</b></div> + <div>this is where you are right now</div><br> + <div>*<b><a href="https://akkofe.trinkey.com/">akkofe.trinkey.com</a>:</b></div> + <div>the frontend i use for <a href="https://fediverse.info/" target="_blank">fedi</a></div><br> + <div><b><a href="https://auth.trinkey.com/">auth.trinkey.com</a>:</b></div> + <div>authentication manager for tSuite</div><br> + <div><b><a href="https://blog.trinkey.com/">blog.trinkey.com</a>:</b></div> + <div>tBlog, from tSuite</div><br> + <div><b><a href="https://everyone.trinkey.com/">everyone.trinkey.com</a>:</b></div> + <div>frontend to a fedi bot that anyone can post to (@everyonebot@is.trinkey.com)</div><br> + <div>*<b><a href="https://git.trinkey.com/">git.trinkey.com</a>:</b></div> + <div>holds some of my git projects (older ones on <a href="https://github.com/trinkey/" target="_blank">github</a> or <a href="https://git.gay/trinkey/" target="_blank">git.gay</a>)</div><br> + <div>*<b><a href="https://is.trinkey.com/">is.trinkey.com</a>:</b></div> + <div>hosts <a href="https://iceshrimp.dev/iceshrimp/iceshrimp.net" target="_blank">iceshrimp.net</a>, which is the fedi backend i use</div><br> + <div><b><a href="https://message.trinkey.com/">message.trinkey.com</a>:</b></div> + <div>tMessage, from tSuite</div><br> + <div><b><a href="https://music.trinkey.com/">music.trinkey.com</a>:</b></div> + <div>has some music. i haven't actually updated the site in a while but i've been meaning to do rewrite it at some point. 100% legal i pinky promise</div><br> + <div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div> + <div>official jerimiah smiggins instance, that being my own social media platform</div><br> + <div>(asterisk (*) means i haven't written the code for it)</div><br>` +}; +const _internal_neofetchOutputs = { + desktop: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">desktop</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.3 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: MS-7E27 1.0 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 1680x1050, 2560x1440 + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">DE</b>: Cinnamon 6.0.4 + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> <b class="green">CPU</b>: AMD Ryzen 9 7950X (32) @ 5.881GHz +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 03:00.0 Device 747e +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 11:00.0 Device 164e +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">Memory</b>: 1MiB / 127901MiB +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>`, + laptop: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">laptop</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Dell G15 5510 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 2560x1440, 1920x1080, 1680x1050 + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">DE</b>: Cinnamon 5.8.4 + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> <b class="green">CPU</b>: Intel i5-10500H (12) @ 4.500GHz +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">GPU</b>: NVIDIA GeForce RTX 3050 Ti Mobile +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> <b class="green">GPU</b>: Intel CometLake-H GT2 [UHD Graphics] +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">Memory</b>: 2001MiB / 15765MiB +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>`, + server: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">server</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Macmini7,1 1.0 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">CPU</b>: Intel i5-4278U (4) @ 3.100GHz + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">GPU</b>: Intel Haswell-ULT + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">Memory</b>: 9011MiB / 15866MiB +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>` +}; +const helpText = `<div> -=== <b class="pink">tSh help</b> ===-</div> +<div>--------------------</div> +<div>-= <b class="green">cat</b> =-</div> +<div>Displays the contents of a file.</div> +<div>-= <b class="green">cd</b> =-</div> +<div>Changes the working directory.</div> +<div>-= <b class="green">clear</b> =-</div> +<div>Clears the terminal output.</div> +<div>-= <b class="green">help</b> =-</div> +<div>Shows this help menu.</div> +<div>-= <b class="green">ls</b> =-</div> +<div>Lists all files in a directory.</div> +<div> -l - displays more information about each file</div> +<div> -a - displays all files</div> +<div> -A - displays all files except implied . and ..</div> +<div> -r - reverses the order of the files</div> +<div> -R - recurse through all subdirectories</div> +<div>-= <b class="green">exit</b> =-</div> +<div>Closes the terminal.</div>`; +const HOME_DIR = "/home/trinkey"; +let FILESYSTEM = { + home: { + type: "directory", + name: "home", + files: { + trinkey: { + type: "directory", + name: "trinkey", + files: { + people: { type: "directory", name: "people", files: { + "88x31.txt": { type: "file", name: "88x31.txt", content: _internal_defaultFiles.buttons }, + "testimonials.txt": { type: "file", name: "testimonials.txt", content: _internal_defaultFiles.testimonials }, + "webrings.txt": { type: "file", name: "webrings.txt", content: _internal_defaultFiles.webrings } + } }, + "about-me.txt": { type: "file", name: "about-me.txt", content: _internal_defaultFiles.about }, + "socials.txt": { type: "file", name: "socials.txt", content: _internal_defaultFiles.socials }, + "projects.txt": { type: "file", name: "projects.txt", content: _internal_defaultFiles.projects }, + "subdomains.txt": { type: "file", name: "subdomains.txt", content: _internal_defaultFiles.directory } + } + } + } + }, + bin: { + type: "directory", + name: "bin", + files: { + cat: { type: "file", name: "cat", content: "<div>function cat(file: string): string { ... }</div>" }, + cd: { type: "file", name: "cd", content: "<div>function cd(directory: string): void { ... }</div>" }, + clear: { type: "file", name: "clear", content: "<div>function clear(): void { ... }</div>" }, + help: { type: "file", name: "help", content: "<div>function help(): string { ... }</div>" }, + ls: { type: "file", name: "ls", content: "<div>function ls(directory: string): string { ... }</div>" }, + neofetch: { type: "file", name: "neofetch", content: "<div>function neofetch(): string { ... }</div>" } + } + }, + ".secret-file": { type: "file", name: ".secret-file", content: "<div>meow :3</div>" } +}; +let windowInformation = {}; +function commandManager(windowID, command) { + if (!windowInformation[windowID]) { + windowInformation[windowID] = { + PWD: HOME_DIR + }; + } + let out; + if (_internal_commands[command.split(" ")[0]]) { + out = _internal_commands[command.split(" ")[0]](command.split(" ").slice(1).join(" ").trim(), windowID); + } + else if (command == "") { + out = ""; + } + else { + out = `<div class="red">Unknown command '${escapeHTML(command.split(" ")[0])}'.</div><div>Type 'help' for a list of commands</div>`; + } + let el = document.createElement("div"); + el.innerHTML = out; + WINDOWS[windowID].element.querySelector(".window").append(el); + let dTE = WINDOWS[windowID].element.querySelector("[data-type-area]"); + if (dTE) { + dTE.removeAttribute("data-type-area"); + if (dTE.querySelector("i.cursor")) { + dTE.querySelector("i.cursor").remove(); + } + else if (dTE.querySelector(".cursor")) { + dTE.querySelector(".cursor").classList.remove("cursor"); + } + } + WINDOWS[windowID].element.querySelector(".window-header-title").innerText = `${_internal_stringifyPath(windowInformation[windowID].PWD)} - tSh`; + let ps1 = document.createElement("div"); + ps1.innerHTML = _internal_getPS1(windowInformation[windowID]); + let typeArea = document.createElement("span"); + typeArea.dataset.typeArea = ""; + typeArea.innerHTML = "<i class=\"cursor\"> </i>"; + ps1.append(typeArea); + WINDOWS[windowID].element.querySelector(".window").append(ps1); +} diff --git a/no-js.html b/no-js.html index 0414349..1530b4d 100644 --- a/no-js.html +++ b/no-js.html @@ -56,7 +56,7 @@ <h2>people</h2> <h3>my button:</h3> - <div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of my cat on the right with the word trinkey taking up the rest of the button." title="trinkey's 88x31. image of my cat on the right with the word trinkey taking up the rest of the button."></div> + <div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button."></div> <div>html:</div> <code style="overflow-x: scroll; display: inline-block; background-color: var(--mantle); padding: 5px 8px; border-radius: 5px;"><a href="https://trinkey.com/" target="_blank"><img src="https://trinkey.com/img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word trinkey name taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word trinkey name taking up the rest of the button."></a></code> <div>--------------------</div> diff --git a/ts/blobcat.ts b/ts/blobcat.ts new file mode 100644 index 0000000..062b760 --- /dev/null +++ b/ts/blobcat.ts @@ -0,0 +1,93 @@ +let points: [number, number, number][] = [[0.3971449136734009, 0.2810116410255432, -0.3767290413379669], [0.23243144154548645, 0.3703259527683258, -0.3767290413379669], [0.30626851320266724, 0.23012861609458923, 0.08654531836509705], [0.15029972791671753, -0.29410237073898315, -0.3767290413379669], [0.3065343499183655, -0.2082172930240631, -0.3767290413379669], [0.3111562132835388, -0.06059299036860466, 0.13771235942840576], [0.16147799789905548, -0.09371967613697052, 0.23468825221061707], [0.36976975202560425, 0.12388059496879578, 0.08632178604602814], [0.31842663884162903, -0.019846681505441666, 0.3223131597042084], [0.20939035713672638, 0.15419937670230865, 0.2242213636636734], [0.17552560567855835, 0.012471526861190796, 0.27797016501426697], [0.16723567247390747, 0.1499461680650711, -0.3767290413379669], [0.4233801066875458, 0.1499461680650711, -0.3767290413379669], [0.3200271725654602, -0.22131121158599854, 0.026444125920534134], [0.15823997557163239, -0.25929194688796997, 0.0673588365316391], [0.44944703578948975, 0.1499461680650711, -0.06793694198131561], [0.2280614674091339, 0.36611324548721313, -0.2975170314311981], [0.38751867413520813, 0.2724340260028839, -0.293896347284317], [0.2147921323776245, 0.3551100790500641, -0.2049424648284912], [0.3594103455543518, 0.2775343060493469, -0.19233158230781555], [0.0, 0.42153987288475037, -0.2989161014556885], [0.0, 0.4475290775299072, -0.3767290413379669], [0.0, 0.10908085107803345, 0.23466001451015472], [0.0, -0.29616573452949524, 0.08896146714687347], [0.0, 0.1499461680650711, -0.3767290413379669], [0.0, -0.1545914262533188, 0.23468825221061707], [0.0, 0.012471422553062439, 0.28773677349090576], [0.0, -0.29410237073898315, -0.3767290413379669], [0.0, 0.3850858211517334, -0.2098153829574585], [0.15266574919223785, 0.33635783195495605, 0.08654531091451645], [0.0, 0.34858277440071106, 0.08654531836509705], [0.3163855969905853, -0.30245065689086914, -0.2832803726196289], [0.15313652157783508, -0.3652721643447876, -0.2640822231769562], [0.43641358613967896, 0.1499461680650711, -0.2795500159263611], [0.0, -0.3652721643447876, -0.2640821933746338], [0.3884555399417877, 0.012471545487642288, -0.3767290413379669], [0.34046298265457153, 0.012471519410610199, 0.11201705783605576], [0.1587677001953125, 0.01247154176235199, -0.3767290413379669], [0.384737104177475, 0.012471544556319714, -0.026815753430128098], [0.0, 0.012471549212932587, -0.3767290413379669], [0.39989548921585083, 0.012471545487642288, -0.30725497007369995], [-0.3971449136734009, 0.2810116410255432, -0.3767290413379669], [-0.23243144154548645, 0.3703259527683258, -0.3767290413379669], [-0.30626851320266724, 0.23012861609458923, 0.08654531836509705], [-0.15029972791671753, -0.29410237073898315, -0.3767290413379669], [-0.3065343499183655, -0.2082172930240631, -0.3767290413379669], [-0.3111562132835388, -0.06059299036860466, 0.13771235942840576], [-0.16147799789905548, -0.09371967613697052, 0.23468825221061707], [-0.36976975202560425, 0.12388059496879578, 0.08632178604602814], [-0.31842663884162903, -0.019846681505441666, 0.3223131597042084], [-0.20939035713672638, 0.15419937670230865, 0.2242213636636734], [-0.17552560567855835, 0.012471526861190796, 0.27797016501426697], [-0.16723567247390747, 0.1499461680650711, -0.3767290413379669], [-0.4233801066875458, 0.1499461680650711, -0.3767290413379669], [-0.3200271725654602, -0.22131121158599854, 0.026444125920534134], [-0.15823997557163239, -0.25929194688796997, 0.0673588365316391], [-0.44944703578948975, 0.1499461680650711, -0.06793694198131561], [-0.2280614674091339, 0.36611324548721313, -0.2975170314311981], [-0.38751867413520813, 0.2724340260028839, -0.293896347284317], [-0.2147921323776245, 0.3551100790500641, -0.2049424648284912], [-0.3594103455543518, 0.2775343060493469, -0.19233158230781555], [-0.15266574919223785, 0.33635783195495605, 0.08654531091451645], [-0.3163855969905853, -0.30245065689086914, -0.2832803726196289], [-0.15313652157783508, -0.3652721643447876, -0.2640822231769562], [-0.43641358613967896, 0.1499461680650711, -0.2795500159263611], [-0.3884555399417877, 0.012471545487642288, -0.3767290413379669], [-0.34046298265457153, 0.012471519410610199, 0.11201705783605576], [-0.1587677001953125, 0.01247154176235199, -0.3767290413379669], [-0.384737104177475, 0.012471544556319714, -0.026815753430128098], [-0.39989548921585083, 0.012471545487642288, -0.30725497007369995]]; +const connections: [number, number][] = [[0, 1], [0, 12], [0, 17], [1, 11], [1, 16], [1, 21], [2, 7], [2, 9], [2, 19], [2, 29], [3, 4], [3, 27], [3, 32], [3, 37], [4, 31], [4, 35], [5, 6], [5, 8], [5, 13], [5, 36], [6, 8], [6, 10], [6, 14], [6, 25], [7, 8], [7, 9], [7, 15], [7, 36], [8, 9], [8, 10], [8, 36], [9, 10], [9, 22], [9, 29], [10, 26], [11, 12], [11, 24], [11, 37], [12, 33], [12, 35], [13, 14], [13, 31], [13, 38], [14, 23], [14, 32], [15, 19], [15, 33], [15, 38], [16, 17], [16, 18], [16, 20], [17, 19], [17, 33], [18, 19], [18, 28], [18, 29], [20, 21], [20, 28], [20, 57], [21, 24], [21, 42], [22, 26], [22, 30], [22, 50], [23, 25], [23, 34], [23, 55], [24, 39], [24, 52], [25, 26], [25, 47], [26, 51], [27, 34], [27, 39], [27, 44], [28, 30], [28, 59], [29, 30], [30, 61], [31, 32], [31, 40], [32, 34], [33, 40], [34, 63], [35, 37], [35, 40], [36, 38], [37, 39], [38, 40], [39, 67], [41, 42], [41, 53], [41, 58], [42, 52], [42, 57], [43, 48], [43, 50], [43, 60], [43, 61], [44, 45], [44, 63], [44, 67], [45, 62], [45, 65], [46, 47], [46, 49], [46, 54], [46, 66], [47, 49], [47, 51], [47, 55], [48, 49], [48, 50], [48, 56], [48, 66], [49, 50], [49, 51], [49, 66], [50, 51], [50, 61], [52, 53], [52, 67], [53, 64], [53, 65], [54, 55], [54, 62], [54, 68], [55, 63], [56, 60], [56, 64], [56, 68], [57, 58], [57, 59], [58, 60], [58, 64], [59, 60], [59, 61], [62, 63], [62, 69], [64, 69], [65, 67], [65, 69], [66, 68], [68, 69]]; +const blobcatColor: { light: string, dark: string } = { light: "#df8e1d", dark: "#f9e2af" } +const focalLength: number = 1; +const radians: number = (2 * Math.PI / 360) * 3; +const rotationalMatrix: [number, number, number][] = [ + [Math.cos(radians), -Math.sin(radians), 0], + [Math.sin(radians), Math.cos(radians), 0], + [0, 0, 1] +] + +let blobcatElement: HTMLDivElement | null = null; + +function dot(a: number[][], b: number[][]): number[][] { + const rows: number = a.length; + const colsA: number = a[0].length; + const colsB: number = b[0].length; + const result: number[][] = Array.from({ length: rows }, (): number[] => Array(colsB).fill(0)); + + for (let i: number = 0; i < rows; i++) { + for (let j: number = 0; j < colsB; j++) { + for (let k: number = 0; k < colsA; k++) { + result[i][j] += a[i][k] * b[k][j]; + } + } + } + + return result; +} + +function getLine( + x1: number, y1: number, x2: number, y2: number, + multiply: number, + offsetX: number, + offsetY: number, + zIndex: number +): HTMLHRElement { + let hr: HTMLHRElement = document.createElement("hr"); + + x1 = x1 * multiply; + y1 = y1 * multiply; + x2 = x2 * multiply; + y2 = y2 * multiply; + + hr.style.position = "absolute"; + hr.style.left = `${x1 + offsetX}px`; + hr.style.top = `${y1 + offsetY}px`; + hr.style.height = `1px`; + hr.style.width = `${Math.sqrt(((x2-x1) * (x2-x1)) + ((y2-y1) * (y2-y1)))}px`; + hr.style.rotate = `${Math.atan2(y2 - y1, x2 - x1)}rad`; + hr.style.transformOrigin = `0 0.5px`; + hr.style.backgroundColor = light ? blobcatColor.light : blobcatColor.dark; + hr.style.border = "0"; + hr.style.zIndex = String(zIndex) + + return hr; +} + +function blobcatFrame(): void { + if (!blobcatElement || !WINDOWS.blob) { return; } + + let screenCoordinates: [number, number][] = points.map((a: [number, number, number]): [number, number] => ([ + (focalLength * a[0]) / (a[1] + focalLength), + -(focalLength * a[2]) / (a[1] + focalLength) + ])); + + let hrElements: HTMLHRElement[] = connections.map((a: [number, number]): HTMLHRElement => ( + getLine(...screenCoordinates[a[0]], ...screenCoordinates[a[1]], 300, WINDOWS.blob.width / 2 + 11, WINDOWS.blob.height / 2 + 11, WINDOWS.blob.zIndex) + )); + + blobcatElement.innerHTML = ""; + blobcatElement.append(...hrElements); + points = dot(points, rotationalMatrix) as [number, number, number][]; +} + +function createBlob(): void { + createWindow({ + id: "blob", + title: "~/spinny-cat", + content: "<div id='blobcat'></div>", + onDestroy: destroyBlob, + height: 450, + typeable: false + }); + + blobcatElement = document.getElementById("blobcat") as HTMLDivElement; +} + +function destroyBlob(): void { + blobcatElement = null; +} + +setInterval(blobcatFrame, 1000 / 30); diff --git a/ts/globals.d.ts b/ts/globals.d.ts new file mode 100644 index 0000000..5c5d8a2 --- /dev/null +++ b/ts/globals.d.ts @@ -0,0 +1,49 @@ +declare let light: boolean; + +type StringDict = { [key: string]: string } + +type _winInitConf = { + title: string, + content: string, + id: string, + width?: number, // = 600 + height?: number, // = 400 + minWidth?: number, // = 200 + minHeight?: number, // = 200 + typeable?: boolean, // true + posX?: number, // = automatically centered + posY?: number, // = automatically centered + onDestroy?: () => void +} + +type _winConf = { + element: HTMLElement, + height: number, + width: number, + posX: number, + posY: number, + fullscreen: boolean, + zIndex: number, + vars: { + [key: string]: any + } +} + +type _files = { + [key: string]: _file +} + +type _file = { + type: "directory", + name: string, + files: _files +} | { + type: "file", + name: string, + content: string +} + +type _tShWinInfo = { + PWD: string, + ps1Override?: string +} diff --git a/ts/index.ts b/ts/index.ts new file mode 100644 index 0000000..3352958 --- /dev/null +++ b/ts/index.ts @@ -0,0 +1,272 @@ +let WINDOWS: { [key: string]: _winConf } = {}; +let MOUSE_MOVE_PROCESSING: { + [key: string]: { + callback: (x: number, y: number) => void, + mouseUp: boolean + } +} = {}; + +let globalIncrement: number = 1; + +function escapeHTML(string: string): string { + return string.replaceAll("&", "&").replaceAll("<", "<").replaceAll("\"", """); +} + +function incrementZIndex(windowID: string, focus: boolean=false): void { + WINDOWS[windowID].element.style.zIndex = String(globalIncrement); + WINDOWS[windowID].zIndex = globalIncrement; + globalIncrement++; + + if (focus) { + WINDOWS[windowID].element.focus(); + } +} + +function createWindow(config: _winInitConf): void { + if (document.getElementById(config.id)) { + incrementZIndex(config.id); + return; + } + + // 1 - border + // 10 - padding + // 35 - header + let _windowPaddingX: number = 1*2 + 10*2; + let _windowPaddingY: number = 1*2 + 10*2 + 35; + + config.width = config.width || 600; + config.height = config.height || 400; + config.minWidth = config.minWidth || 200; + config.minHeight = config.minHeight || 200; + + let realWidth: number = Math.max(config.minWidth, Math.min(config.width, innerWidth - _windowPaddingX - 20)); + let realHeight: number = Math.max(config.minHeight, Math.min(config.height, innerHeight - _windowPaddingY - 20)); + + let posX: number = config.posX || Math.round((innerWidth / 2) - ((realWidth + _windowPaddingX) / 2)); + let posY: number = config.posY || Math.round((innerHeight / 2) - ((realHeight + _windowPaddingY) / 2)); + + let wO: HTMLDivElement = document.createElement("div"); + wO.classList.add("window-outer"); + + let w: HTMLDivElement = document.createElement("div"); + w.classList.add("window"); + w.style.width = `${realWidth}px`; + w.style.height = `${realHeight}px`; + w.innerHTML = config.content; + + let wH: HTMLDivElement = document.createElement("div"); + wH.classList.add("window-header"); + wH.innerHTML = ` + <i class="window-header-button blank"></i> + <i class="window-header-button blank"></i> + <i class="window-header-button blank"></i> + <strong class="window-header-title">${config.title}</strong> + <i data-no-move class="window-header-button minimize"></i> + <i data-no-move class="window-header-button fullscreen"></i> + <i data-no-move class="window-header-button close"></i> + `; + + let wC: HTMLDivElement | HTMLLabelElement; + let wI: HTMLInputElement = null; + if (config.typeable !== false) { + function syncInputs(): void { + setTimeout(function(): void { + let text: string = wI.value; + let cursor: number = wI.selectionStart; + let el: HTMLElement = wC.querySelector("[data-type-area]"); + + if (!el) { return; } + + if (cursor == text.length) { + el.innerHTML = `${escapeHTML(text)}<i class="cursor"> </i>`; + } else { + el.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`; + } + }, 1); + } + + function setCursor(): void { + setTimeout((): void => { + wI.setSelectionRange(wI.value.length, wI.value.length); + syncInputs(); + }, 0); + } + + wI = document.createElement("input"); + wI.classList.add("window-input"); + wI.id = `${config.id}__input`; + + wI.oninput = (event: KeyboardEvent): void => { + syncInputs(); + w.scrollTop = w.scrollHeight; + }; + + wI.onkeydown = (event: KeyboardEvent): void => { + if (event.key == "Enter") { + commandManager(config.id, wI.value.trim()); + w.scrollTop = w.scrollHeight; + wI.value = ""; + } else { + syncInputs(); + } + }; + + wI.onfocus = setCursor; + wI.onclick = setCursor; + + wC = document.createElement("label"); + wC.htmlFor = `${config.id}__input`; + } else { + wC = document.createElement("div"); + } + + wC.classList.add("window-container"); + wC.tabIndex = 0; + wC.style.left = `${posX}px`; + wC.style.top = `${posY}px`; + wC.id = config.id; + wC.style.zIndex = String(globalIncrement); + wC.style.width = `${realWidth + _windowPaddingX - 2}px`; + + wO.append(w); + wC.append(wH, wO); + document.body.append(wC); + + if (config.typeable !== false) { + wC.append(wI); + wI.focus(); + } else { + wC.focus(); + } + + WINDOWS[config.id] = { + element: wC, + height: realHeight, + width: realWidth, + posX: posX, + posY: posY, + fullscreen: false, + zIndex: globalIncrement, + vars: {} + }; + + function mouseMoveEvent(x: number, y: number): void { + WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX)); + WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY)); + wC.style.left = `${WINDOWS[config.id].posX}px`; + wC.style.top = `${WINDOWS[config.id].posY}px`; + } + + // wC.addEventListener("mousedown", function(): void { incrementZIndex(config.id); }); + wC.addEventListener("focus", function(): void { incrementZIndex(config.id); }); + + for (const link of wC.querySelectorAll("a")) { + link.addEventListener("focus", function(): void { incrementZIndex(config.id); }); + } + + wH.addEventListener("mousedown", function(e: MouseEvent): void { + if ((e.target as HTMLElement).dataset.noMove !== undefined) { + return; + } + + WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX; + WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY; + MOUSE_MOVE_PROCESSING[config.id] = { + callback: mouseMoveEvent, + mouseUp: true + }; + }); + + wH.querySelector(".close").addEventListener("click", function(): void { + delete WINDOWS[config.id]; + delete windowInformation[config.id]; + delete MOUSE_MOVE_PROCESSING[config.id]; + wC.remove(); + + if (typeof config.onDestroy === "function") { + config.onDestroy(); + } + }); + + wH.querySelector(".fullscreen").addEventListener("click", function(): void { + if (WINDOWS[config.id].fullscreen) { + WINDOWS[config.id].posX = WINDOWS[config.id].vars.oldPosX; + WINDOWS[config.id].posY = WINDOWS[config.id].vars.oldPosY; + WINDOWS[config.id].width = WINDOWS[config.id].vars.oldWidth; + WINDOWS[config.id].height = WINDOWS[config.id].vars.oldHeight; + WINDOWS[config.id].fullscreen = false; + delete WINDOWS[config.id].vars.oldPosX; + delete WINDOWS[config.id].vars.oldPosY; + delete WINDOWS[config.id].vars.oldWidth; + delete WINDOWS[config.id].vars.oldHeight; + wC.style.left = `${WINDOWS[config.id].posX}px`; + wC.style.top = `${WINDOWS[config.id].posY}px`; + wC.style.width = `${WINDOWS[config.id].width + _windowPaddingX - 2}px`; + w.style.width = `${WINDOWS[config.id].width}px`; + w.style.height = `${WINDOWS[config.id].height}px`; + } else { + WINDOWS[config.id].vars.oldPosX = WINDOWS[config.id].posX; + WINDOWS[config.id].vars.oldPosY = WINDOWS[config.id].posY; + WINDOWS[config.id].vars.oldWidth = WINDOWS[config.id].width; + WINDOWS[config.id].vars.oldHeight = WINDOWS[config.id].height; + WINDOWS[config.id].fullscreen = true; + WINDOWS[config.id].posX = 0; + WINDOWS[config.id].posY = 0; + WINDOWS[config.id].width = innerWidth; + WINDOWS[config.id].height = innerHeight; + wC.style.left = "0px"; + wC.style.top = "0px"; + wC.style.width = `${WINDOWS[config.id].width}px`; + w.style.width = `${WINDOWS[config.id].width - _windowPaddingX}px`; + w.style.height = `${WINDOWS[config.id].height - _windowPaddingY}px`; + } + }); + + globalIncrement++; +} + +window.addEventListener("mousemove", function(e: MouseEvent): void { + for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { + MOUSE_MOVE_PROCESSING[key].callback(e.clientX, e.clientY); + } +}); + +window.addEventListener("mouseup", function(): void { + for (const key of Object.keys(MOUSE_MOVE_PROCESSING)) { + if (MOUSE_MOVE_PROCESSING[key].mouseUp) { + delete MOUSE_MOVE_PROCESSING[key]; + }; + } +}); + +function windowPreset(template: string): void { + let el: HTMLElement = document.querySelector(`#window-templates > [data-template-id="${template}"]`); + + if (!el) { return; } + + if (WINDOWS[template]) { + incrementZIndex(template, true); + return; + } + + let config: _winInitConf = { + id: template, + title: "~ - tSh", + content: "<div><b class=\"green\">trinkey@website</b>:<b class=\"blue\">~</b>$ <span data-type-area><i class=\"cursor\"> </i></span></div>" + }; + + for (const field of el.querySelectorAll("[data-template-field]")) { + config[(field as HTMLElement).dataset.templateField] = (field as HTMLElement).dataset.isNumber === "" ? +(field as HTMLElement).innerText : (field as HTMLElement).innerHTML; + } + + createWindow(config); + + for (const command of el.querySelectorAll("li")) { + WINDOWS[template].element.querySelector("[data-type-area]").innerHTML = command.innerHTML; + commandManager(template, command.innerHTML); + } +} + +function copyButton(): void { + navigator.clipboard.writeText("<a href=\"https://trinkey.com/\" target=\"_blank\"><img src=\"https://trinkey.com/img/88x31.png\" alt=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\" title=\"trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button.\"></a>"); +} diff --git a/ts/shell.ts b/ts/shell.ts new file mode 100644 index 0000000..4785be9 --- /dev/null +++ b/ts/shell.ts @@ -0,0 +1,504 @@ +// -= Helper Functions =- // +function _internal_joinPaths(path1: string, path2: string): string { + if (path2[0] == "/") { + return _internal_sanitizePath(path2); + } else if (path2[0] == "~" && (path2.length == 1 || path2[1] == "/")) { + return _internal_sanitizePath(HOME_DIR + path2.slice(1)); + } else { + return _internal_sanitizePath(path1 + "/" + path2); + } +} + +function _internal_sanitizePath(path: string): string { + while (path.includes("//")) { + path = path.replaceAll("//", "/"); + } + + while (path[path.length - 1] == "/") { + path = path.slice(0, path.length - 1); + } + + let newPath: string[] = [] + for (const dir of path.split("/")) { + if (dir == ".") { + // + } else if (dir == "..") { + if (newPath.length) { + newPath = newPath.slice(0, newPath.length - 1); + } + } else if (dir) { + newPath.push(dir); + } + } + + return "/" + newPath.join("/"); +} + +function _internal_getFile(path: string): _file | null { + function getFile_recursive(files: _files, path: string[]): _file | null { + console.log(files, path) + let file: _file = files[path[0]]; + + if (!file) { + return null; + } + + if (path.length == 1) { + return file; + } + + if (file.type == "file") { + return null; + } + + return getFile_recursive(file.files, path.slice(1)); + } + + if (path == "/") { + return { + type: "directory", + name: "/", + files: FILESYSTEM + }; + } + + let dirs: string[] = path.split("/"); + + if (dirs[0] == "") { + dirs = dirs.slice(1); + } + + return getFile_recursive(FILESYSTEM, dirs); +} + +function _internal_stringifyPath(path: string): string { + if (path.startsWith(HOME_DIR)) { + return path.replace(HOME_DIR, "~"); + } + + return path; +} + +function _internal_getPS1(winInfo: _tShWinInfo): string { + return `<b class="green">${winInfo.ps1Override || "trinkey@website"}</b>:<b class="blue">${_internal_stringifyPath(winInfo.PWD)}</b>$ ` +} + +function _internal_getFlags(command: string): { + flags: string[] + removed: string +} { + let flags: string[] = []; + let newCommand: string[] = []; + + for (const thing of command.split(" ")) { + if (thing.startsWith("-")) { + flags.push(...thing.slice(1).split("")) + } else { + newCommand.push(thing); + } + } + + return { + flags: flags, + removed: newCommand.join(" ") + }; +} + +function _internal_fileSize(size: number): string { + let suffix: string = "" + let amount: number = size; + + const sizes: { suffix: string, amount: number, threshold: number }[] = [ + { suffix: "K", amount: 1024 , threshold: 10_000 }, + { suffix: "M", amount: 1024 ** 2, threshold: 1_000_000 } + ] + + for (const info of sizes) { + if (size > info.threshold) { + suffix = info.suffix; + amount = Math.round(size / info.amount * 10) / 10; + + if (String(amount).length > 3) { + amount = Math.round(info.amount); + } + } + } + + return `${amount}${suffix}`.padStart(4, " "); +} + +// -= Commands =- // +function cat(path: string, windowID: string): string { + if (!path) { + return "<div>cat: You must specify a file</div>"; + } + + path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, path); + let file: _file = _internal_getFile(path); + + if (!file) { + return `<div>cat: ${escapeHTML(path)}: No such file or directory</div>`; + } else if (file.type == "directory") { + return `<div>cat: ${escapeHTML(path)}: Is a directory</div>`; + } else { + return file.content; + } +} + +function cd(path: string, windowID: string): string { + if (!path) { + windowInformation[windowID].PWD = HOME_DIR; + return ""; + } + + let newPWD: string = _internal_joinPaths(windowInformation[windowID].PWD, path); + let newFileObj: _file | null = _internal_getFile(newPWD); + + if (newFileObj === null) { + return `<div>cd: ${escapeHTML(path)}: No such file or directory</div>`; + } else if (newFileObj.type == "file") { + return `<div>cd: ${escapeHTML(path)}: Not a directory</div>`; + } + + windowInformation[windowID].PWD = newPWD; + + return ""; +} + +function clear(path: string, windowID: string): string { + WINDOWS[windowID].element.querySelector(".window").innerHTML = ""; + return ""; +} + +function exit(path: string, windowID: string): string { + setTimeout((): void => { + (WINDOWS[windowID].element.querySelector(".close") as HTMLElement).click(); + }, 1); + return ""; +} + +function help(path: string, windowID: string): string { + return helpText; +} + +function ls(path: string, windowID: string): string { + let { flags, removed } = _internal_getFlags(path); + + path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, removed); + + let file: _file = _internal_getFile(path); + let files: _files; + + if (!file) { + return `<div>ls: ${escapeHTML(path)}: No such file or directory</div>`; + } else if (file.type == "file") { + files = { [file.name]: file }; + } else { + files = file.files; + } + + let directories: _file[] = []; + let hidden: boolean = flags.includes("a") || flags.includes("A"); + let long: boolean = flags.includes("l"); + let out: string = flags.includes("a") ? (long ? "<div>drwxrwxr-x trinkey trinkey 4096 <span class=\"blue\">.</span></div><div>drwxr-xr-x trinkey trinkey 4096 <span class=\"blue\">..</span></div>" : "<span class=\"blue\">.</span> <span class=\"blue\">..</span> ") : ""; + + for (const file of Object.keys(files).sort((a: string, b: string): number => ( + ({ true: 1, false: -1 })[String(flags.includes("r") ? a < b : a > b)] + ))) { + if (file.startsWith(".") && !hidden) { + continue; + } + + let fObj: _file = files[file]; + if (long) { + if (fObj.type == "directory") { + directories.push(fObj); + out += `<div>drwxrwxr-x trinkey trinkey 4096 <span class=\"blue\">${escapeHTML(file)}</span></div>`; + } else { + out += `<div>-rw-rw-r-- trinkey trinkey ${_internal_fileSize(fObj.content.length).replaceAll(" ", " ")} ${escapeHTML(file)}</div>`; + } + } else { + if (fObj.type == "directory") { + directories.push(fObj); + out += `<span class=\"blue\">${escapeHTML(file)}</span> `; + } else { + out += `${escapeHTML(file)} `; + } + } + } + + if (!long) { + out = out.slice(0, out.length - 7); + } + + if (flags.includes("R")) { + for (const dir of directories) { + out += `<div><br>${escapeHTML(_internal_stringifyPath(_internal_joinPaths(path, dir.name)))}:</div>${ls(`${_internal_joinPaths(path, dir.name)} -${flags.join("")}`, windowID)}`; + } + } + + return out; +} + +function _internal_set_ps1(args: string, windowID: string): string { + windowInformation[windowID].ps1Override = args.split("|")[0]; + WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = args.split("|")[1]; + return ""; +} + +function _internal_neofetch(args: string, windowID: string): string { + WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = "neofetch"; + return _internal_neofetchOutputs[args]; +} + +const _internal_commands: { [key: string]: (args: string, windowID: string) => string } = { + cat: cat, + cd: cd, + clear: clear, + help: help, // NEEDS UPDATING + ls: ls, + exit: exit, + _internal_set_ps1: _internal_set_ps1, + _internal_neofetch: _internal_neofetch +}; + +// -= Variables + Other =- // +const _internal_defaultFiles: StringDict = { + about: `<div><b>hi there! i'm trinkey!</b></div> + <div>--------------------</div> + <div>i'm a silly little kitty cat who lives in the usa.</div> + <div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/her, they/them and it/its are also fine).</div> + <div>i'm not actively in a relationship, however i'm also not looking to get into one either.</div> + <div>--------------------</div> + <div>i like to code stuff (mostly websites)! some of my programs can be found on the <a href="javascript:windowPreset('projects')">projects page</a>. i know a few languages, those being python, javascript/typescript, html/css (if you count those), and a little bit of java.</div> + <div>--------------------</div> + <div>well, that's about it! i hope you like my website!</div>`, + socials: `<div>- fedi - <b>@trinkey@trinkey.com</b> (or @trinkey@is.trinkey.com)</div> + <div>- forgejo - <a href="https://git.trinkey.com/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- github - <a href="https://github.com/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- git.gay - <a href="https://git.gay/trinkey/" target="_blank"><b>trinkey</b></a> (inactive)</div> + <div>- smiggins - <a href="https://smiggins.trinkey.com/u/trinkey/" target="_blank"><b>trinkey</b></a></div> + <div>- signal - <b>@trinkey.01</b></div> + <div>- email - <b>trinkey [at] proton [dot] me</b></div> + <div>- youtube - <a href="https://youtube.com/@trinkey" target="_blank"><b>@trinkey</b></a> (inactive)</div>`, + buttons: `<div><b>my button:</b> (click to copy html)</div> + <div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." onclick="copyButton()"></div> + <div>--------------------</div> + <div><b>cool people:</b></div> + <div class="buttons-88x31"> + <a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc" title="notfire.cc"></a> + <a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro"" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of "Micro""></a> + <a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a> + <a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a> + <a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a> + </div>`, + testimonials: `<div>"warning: this user is trinkey"</div> + <div>- <a href="https://booping.synth.download/@breaadyboy" target="_blank">bread</a></div><br> + <div>"This user is only slightly crazy once was I. 10/10 would recommend"</div> + <div>- <a href="https://lea.pet/@subroutine" target="_blank">subroutine</a></div><br> + <div>"the f slur but repeated 36 times"</div> + <div>- <a href="https://oomfie.city/@cornfields74">corn fields seventy four</a></div>`, + webrings: `<div> + <a href="https://ctp-webr.ing/trinkey/previous">←</a> + <a href="https://ctp-webr.ing/">catppuccin webring</a> + <a href="https://ctp-webr.ing/trinkey/next">→</a> + </div> + <div> + <a href="https://fediring.net/previous?host=trinkey.com">←</a> + <a href="https://fediring.net/">fediring</a> + <a href="https://fediring.net/next?host=trinkey.com">→</a> + </div>`, + projects: `<div><b>projects</b> - the things i made</div> + <div>- <a href="https://github.com/jerimiah-smiggins/smiggins/" target="_blank"><b>smiggins</b></a> (<a href="https://smiggins.trinkey.com/" target="_blank">website</a>) - a social media platform i made</div> + <div>- <a href="https://git.trinkey.com/trinkey/website/" target="_blank"><b>this website</b></a> - check out the code</div> + <div>- <a href="https://git.gay/trinkey/dotindex/" target="_blank"><b>dotindex</b></a> (<a href="https://pypi.org/project/DotIndex/" target="_blank">pypi</a>) - a python library that lets you access dicts using the dot notation (dict.key) instead of whatever python does (dict["key"])</div> + <div>- <a href="https://git.gay/trinkey/infopage/" target="_blank"><b>infopage</b></a> (<a href="https://infpg.pythonanywhere.com/" target="_blank">website</a>) - my very own pronouns.page clone</div> + <div>- <a href="https://git.trinkey.com/t" target="_blank"><b>tSuite</b></a> (<a href="https://auth.trinkey.com/" target="_blank">website</a>) - a collection of services that are all interconnected</div><br> + <div>i'll likely add more in the future, these are just the ones i'm most proud of at the moment.</div>`, + directory: `<div>there's a lot that goes into this website. here are some links for your usage to help you navigate this hellhole</div><br> + <div><b><a href="https://trinkey.com/">trinkey.com</a>:</b></div> + <div>this is where you are right now</div><br> + <div>*<b><a href="https://akkofe.trinkey.com/">akkofe.trinkey.com</a>:</b></div> + <div>the frontend i use for <a href="https://fediverse.info/" target="_blank">fedi</a></div><br> + <div><b><a href="https://auth.trinkey.com/">auth.trinkey.com</a>:</b></div> + <div>authentication manager for tSuite</div><br> + <div><b><a href="https://blog.trinkey.com/">blog.trinkey.com</a>:</b></div> + <div>tBlog, from tSuite</div><br> + <div><b><a href="https://everyone.trinkey.com/">everyone.trinkey.com</a>:</b></div> + <div>frontend to a fedi bot that anyone can post to (@everyonebot@is.trinkey.com)</div><br> + <div>*<b><a href="https://git.trinkey.com/">git.trinkey.com</a>:</b></div> + <div>holds some of my git projects (older ones on <a href="https://github.com/trinkey/" target="_blank">github</a> or <a href="https://git.gay/trinkey/" target="_blank">git.gay</a>)</div><br> + <div>*<b><a href="https://is.trinkey.com/">is.trinkey.com</a>:</b></div> + <div>hosts <a href="https://iceshrimp.dev/iceshrimp/iceshrimp.net" target="_blank">iceshrimp.net</a>, which is the fedi backend i use</div><br> + <div><b><a href="https://message.trinkey.com/">message.trinkey.com</a>:</b></div> + <div>tMessage, from tSuite</div><br> + <div><b><a href="https://music.trinkey.com/">music.trinkey.com</a>:</b></div> + <div>has some music. i haven't actually updated the site in a while but i've been meaning to do rewrite it at some point. 100% legal i pinky promise</div><br> + <div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div> + <div>official jerimiah smiggins instance, that being my own social media platform</div><br> + <div>(asterisk (*) means i haven't written the code for it)</div><br>` +}; + +const _internal_neofetchOutputs: StringDict = { + desktop: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">desktop</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.3 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: MS-7E27 1.0 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 1680x1050, 2560x1440 + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">DE</b>: Cinnamon 6.0.4 + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> <b class="green">CPU</b>: AMD Ryzen 9 7950X (32) @ 5.881GHz +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 03:00.0 Device 747e +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> <b class="green">GPU</b>: AMD ATI 11:00.0 Device 164e +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">Memory</b>: 1MiB / 127901MiB +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>`, + laptop: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">laptop</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Dell G15 5510 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">Resolution</b>: 2560x1440, 1920x1080, 1680x1050 + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">DE</b>: Cinnamon 5.8.4 + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">WM</b>: Mutter (Muffin) +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> <b class="green">CPU</b>: Intel i5-10500H (12) @ 4.500GHz +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">GPU</b>: NVIDIA GeForce RTX 3050 Ti Mobile +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> <b class="green">GPU</b>: Intel CometLake-H GT2 [UHD Graphics] +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> <b class="green">Memory</b>: 2001MiB / 15765MiB +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>`, + server: `<pre> <b>...-:::::-...</b> <b class="green">trinkey</b>@<b class="green">server</b> + <b>.-MMMMMMMMMMMMMMM-.</b> --------------- + <b>.-MMMM<span class="green">\`..-:::::::-..\`</span>MMMM-.</b> <b class="green">OS</b>: Linux Mint 21.2 x86_64 + <b>.:MMMM<span class="green">.:MMMMMMMMMMMMMMM:.</span>MMMM:.</b> <b class="green">Host</b>: Macmini7,1 1.0 + <b>-MMM<span class="green">-M---MMMMMMMMMMMMMMMMMMM.</span>MMM-</b> <b class="green">CPU</b>: Intel i5-4278U (4) @ 3.100GHz + <b>\`:MMM<span class="green">:MM\` :MMMM:....::-...-MMMM:</span>MMM:\`</b> <b class="green">GPU</b>: Intel Haswell-ULT + <b>:MMM<span class="green">:MMM\` :MM:\` \`\` \`\` \`:MMM:</span>MMM:</b> <b class="green">Memory</b>: 9011MiB / 15866MiB +<b>.MMM<span class="green">.MMMM\` :MM. -MM. .MM- \`MMMM.</span>MMM.</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM:</span>MMM:</b> +<b>:MMM<span class="green">:MMMM\` :MM. -MM- .MM: \`MMMM-</span>MMM:</b> +<b>.MMM<span class="green">.MMMM\` :MM:--:MM:--:MM: \`MMMM.</span>MMM.</b> + <b>:MMM<span class="green">:MMM- \`-MMMMMMMMMMMM-\` -MMM-</span>MMM:</b> + <b>:MMM<span class="green">:MMM:\` \`:MMM:</span>MMM:</b> + <b>.MMM<span class="green">.MMMM:--------------:MMMM.</span>MMM.</b> + <b>'-MMMM<span class="green">.-MMMMMMMMMMMMMMM-.</span>MMMM-'</b> + <b>'.-MMMM<span class="green">\`\`--:::::--\`\`</span>MMMM-.'</b> + <b>'-MMMMMMMMMMMMM-'</b> + <b>\`\`-:::::-\`\`</b></pre>` +}; + +const helpText: string = `<div> -=== <b class="pink">tSh help</b> ===-</div> +<div>--------------------</div> +<div>-= <b class="green">cat</b> =-</div> +<div>Displays the contents of a file.</div> +<div>-= <b class="green">cd</b> =-</div> +<div>Changes the working directory.</div> +<div>-= <b class="green">clear</b> =-</div> +<div>Clears the terminal output.</div> +<div>-= <b class="green">help</b> =-</div> +<div>Shows this help menu.</div> +<div>-= <b class="green">ls</b> =-</div> +<div>Lists all files in a directory.</div> +<div> -l - displays more information about each file</div> +<div> -a - displays all files</div> +<div> -A - displays all files except implied . and ..</div> +<div> -r - reverses the order of the files</div> +<div> -R - recurse through all subdirectories</div> +<div>-= <b class="green">exit</b> =-</div> +<div>Closes the terminal.</div>`; + +const HOME_DIR: string = "/home/trinkey"; + +let FILESYSTEM: _files = { + home: { + type: "directory", + name: "home", + files: { + trinkey: { + type: "directory", + name: "trinkey", + files: { + people: { type: "directory", name: "people", files: { + "88x31.txt": { type: "file", name: "88x31.txt", content: _internal_defaultFiles.buttons }, + "testimonials.txt": { type: "file", name: "testimonials.txt", content: _internal_defaultFiles.testimonials }, + "webrings.txt": { type: "file", name: "webrings.txt", content: _internal_defaultFiles.webrings } + }}, + "about-me.txt": { type: "file", name: "about-me.txt", content: _internal_defaultFiles.about }, + "socials.txt": { type: "file", name: "socials.txt", content: _internal_defaultFiles.socials }, + "projects.txt": { type: "file", name: "projects.txt", content: _internal_defaultFiles.projects }, + "subdomains.txt": { type: "file", name: "subdomains.txt", content: _internal_defaultFiles.directory } + } + } + } + }, + bin: { + type: "directory", + name: "bin", + files: { + cat: { type: "file", name: "cat", content: "<div>function cat(file: string): string { ... }</div>" }, + cd: { type: "file", name: "cd", content: "<div>function cd(directory: string): void { ... }</div>" }, + clear: { type: "file", name: "clear", content: "<div>function clear(): void { ... }</div>" }, + help: { type: "file", name: "help", content: "<div>function help(): string { ... }</div>" }, + ls: { type: "file", name: "ls", content: "<div>function ls(directory: string): string { ... }</div>" }, + neofetch: { type: "file", name: "neofetch", content: "<div>function neofetch(): string { ... }</div>" } + } + }, + ".secret-file": { type: "file", name: ".secret-file", content: "<div>meow :3</div>" } +}; + +let windowInformation: { [key: string]: _tShWinInfo } = {}; + +function commandManager(windowID: string, command: string): void { + if (!windowInformation[windowID]) { + windowInformation[windowID] = { + PWD: HOME_DIR + }; + } + + let out: string; + if (_internal_commands[command.split(" ")[0]]) { + out = _internal_commands[command.split(" ")[0]](command.split(" ").slice(1).join(" ").trim(), windowID); + } else if (command == "") { + out = "" + } else { + out = `<div class="red">Unknown command '${escapeHTML(command.split(" ")[0])}'.</div><div>Type 'help' for a list of commands</div>`; + } + + let el: HTMLDivElement = document.createElement("div"); + el.innerHTML = out; + WINDOWS[windowID].element.querySelector(".window").append(el); + + let dTE: HTMLElement = WINDOWS[windowID].element.querySelector("[data-type-area]"); + if (dTE) { + dTE.removeAttribute("data-type-area"); + if (dTE.querySelector("i.cursor")) { + dTE.querySelector("i.cursor").remove(); + } else if (dTE.querySelector(".cursor")) { + dTE.querySelector(".cursor").classList.remove("cursor"); + } + } + + (WINDOWS[windowID].element.querySelector(".window-header-title") as HTMLElement).innerText = `${_internal_stringifyPath(windowInformation[windowID].PWD)} - tSh`; + + let ps1: HTMLDivElement = document.createElement("div"); + ps1.innerHTML = _internal_getPS1(windowInformation[windowID]); + + let typeArea: HTMLSpanElement = document.createElement("span"); + typeArea.dataset.typeArea = ""; + typeArea.innerHTML = "<i class=\"cursor\"> </i>"; + + ps1.append(typeArea) + WINDOWS[windowID].element.querySelector(".window").append(ps1); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..df99030 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "lib": [ "dom", "es2021", "dom.iterable" ], + "noUnusedLocals": true, + "outDir": "./js/", + "removeComments": true, + "target": "es6" + }, + + "include": [ + "./ts/*.ts" + ] +}