ソースを参照

better site now

simo 1 ヶ月 前
コミット
fd28845b24

+ 1 - 3
README.md

@@ -1,3 +1 @@
-# My portfolio website (v2)
-
-figuring out git
+New and improved home of simo.ng

BIN
public/ms_sans_serif.woff


BIN
public/ms_sans_serif.woff2


BIN
public/ms_sans_serif_bold.woff2


+ 26 - 20
src/components/aboutme.astro

@@ -1,36 +1,42 @@
-<div
-  id="aboutme"
-  class="window"
-  style="width: 70%; height: auto; position: absolute; left: 15%; top: 22%; display: none;"
->
-  <div id="aboutmeheader" class="title-bar" style="background-color: #0078d7; color: #fff; display: flex; justify-content: space-between; align-items: center;">
-    <div class="title-bar-text" style="font-weight: bold;">About Me</div>
-    <div class="title-bar-controls">
-      <button class="close" aria-label="Minimize" style="background-color: silver;"></button>
-      <button class="close" aria-label="Close" style="background-color: silver;"></button>
-    </div>
-  </div>
-  <div class="window-body" >
+---
+import Win98Window from "./win98window.astro";
+---
+
+<Win98Window
+layout={{
+  mobile:  { left: '10%', top: '10%', width: '95%', height: '500px' },
+  // tablet:  { left: '30%', top: '20%', width: '50%', height: 'auto' },
+  desktop: { left: "10%", top: "20%", width: "65%", height: '600px' }
+}}
+title="About Me" >
+
     <div class="field-row-stacked">
       <!-- prettier-ignore -->
       <textarea
         readonly
         id="text201"
-        style="font-size: 22px; resize: none; padding: 10px; height: 25vh; font-family: inherit; width: 100%; background-color: #fff; border: 1px solid #ccc;  box-sizing: border-box;"
-        >I'm Simon, a self taught programmer
-
-My programming skills include proficiency in Javascript, Typescript, Html, and CSS
+        style="font-size: 22px; resize: none; padding: 10px; font-family: inherit; width: 100%; background-color: #fff; border: 1px solid #ccc;  box-sizing: border-box;"
+        >I'm Simon, a software developer studying computer science @ Widener university
 
-My Web Framework knowledge includes Sveltekit, React, Astro
+I am flexable with language choice. Development is a universal language, and I frequntly expand my stack knowledge through experience in different projects.
 
-Although not entirely proficient, I also write in Python and Golang from time to time</textarea>
+When it's up to me, I usually chose to write in TypeScript; sometimes GoLang if the project calls for it's usecase.
+        </textarea>
     </div>
   </div>
-</div>
+</Win98Window>
 
 <style>
     #aboutme {
         animation: createBox 0.2s;
+
+        width: 100%;
+
+	}
+
+	.field-row-stacked, #text201 {
+	    height: 100% !important;
+
 	}
 
     @keyframes createBox {

+ 3 - 9
src/components/app.astro

@@ -2,6 +2,8 @@
 let app = Astro.props.item;
 
 let itemName = app.name.replace(" ", "");
+
+
 ---
 
 <a id={itemName} data-id={app.name} href={app.link}>
@@ -12,15 +14,7 @@ let itemName = app.name.replace(" ", "");
 </a>
 
 <style>
-	@font-face {
-		font-family: "Pixelated MS Sans Serif";
-		src: url(https://unpkg.com/98.css@0.1.16/dist/ms_sans_serif.woff)
-			format("woff");
-		src: url(https://unpkg.com/98.css@0.1.16/dist/ms_sans_serif.woff2)
-			format("woff2");
-		font-weight: 400;
-		font-style: normal;
-	}
+
 	h3 {
 		font-weight: 300;
 		/* font-family: Arial, sans-serif; */

+ 13 - 17
src/components/projects.astro

@@ -1,23 +1,20 @@
 ---
 import scriptIcon from "../assets/script.png";
 
+import Win98Window from "./win98window.astro"
+
 let projects = Astro.props.projects;
 
 ---
 
-<div
-	class="window"
-	id="projects"
-	style="width: 70%; height: 45%; position: absolute; left: 15%; top: 22%; display: none;"
+<Win98Window
+  title="Projects "
+  layout={{
+    mobile:  { left: '10%', top: '60%', width: '80%', height: 'auto' },
+    // tablet:  { left: '30%', top: '20%', width: '50%', height: 'auto' },
+    desktop: { left: "40%", top: "55%", width: "40%", height: '185px;' }
+  }}
 >
-	<div id="projectsheader" class="title-bar">
-		<div class="title-bar-text">Projects</div>
-		<div class="title-bar-controls">
-			<button class="close" aria-label="Minimize"></button>
-			<button class="close" aria-label="Close"></button>
-		</div>
-	</div>
-	<div class="window-body" style="height: 85%;">
 		<div id="inner">
 			<div
 				id="projects-container"
@@ -42,13 +39,11 @@ let projects = Astro.props.projects;
 				}
 			</div>
 		</div>
-	</div>
-</div>
+</Win98Window>
 
 <style>
 	#inner {
 		height: 100%;
-
 		appearance: none;
 		background-color: #fff;
 		border-radius: 0;
@@ -59,7 +54,6 @@ let projects = Astro.props.projects;
 			inset 2px 2px #0a0a0a;
 		box-sizing: border-box;
 		padding: 3px 4px;
-
 		overflow: auto;
 	}
 
@@ -79,7 +73,6 @@ let projects = Astro.props.projects;
 	}
 }
 
-
 	.project {
 		user-select: none;
 
@@ -101,5 +94,8 @@ let projects = Astro.props.projects;
 		padding: 10px; /* Add some padding around the container */
 
 		height: 100%;
+
+		/*overflow: hidden; */
+		overflow: auto;
 	}
 </style>

+ 17 - 44
src/components/projects/imdb.astro

@@ -1,47 +1,20 @@
-<div
-	id="imdb"
-	class="window projectwindow"
-	style="width: 60%; height: 70%; position: absolute; left: 10%; top: 10%; display: none; max-height: 820px; min-width: 400px;"
->
-	<div id="notionheader" class="title-bar">
-		<div class="title-bar-text">IMDB Frontend</div>
-		<div class="title-bar-controls">
-			<button class="close" aria-label="Minimize"></button>
-			<button class="close" aria-label="Close"></button>
-		</div>
-	</div>
-	<div class="window-body" style="height: 100%;">
-		<div class="field-row-stacked" style="height: 100%;">
-			<!-- prettier-ignore -->
-			<div
-        id="text205"
-        style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: hidden; padding-top: 0px; padding-bottom: 20px;"
-        >
-		<a href="https://github.com/S1monlol/Imdb-but-based"><h1 style="width: fit-content;"><i><u><b>IMDB Frontend</b></u></i></h1></a>
+---
+import Item from './item.astro';
+import summizeExample from "../../assets/summizeExample.png";
+---
 
-		<h2>
-			One of my first projects, this is an alternative frontend for IMDB that uses TMDB to fetch information about movies.
+<Item projectName="imdb" title="IMDB Frontend">
+  <div
+    id="text205"
+    style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: hidden; padding-top: 0px; padding-bottom: 20px;"
+  >
+    <a href="https://github.com/S1monlol/Imdb-but-based"><h1 style="width: fit-content;"><i><u><b>IMDB Frontend</b></u></i></h1></a>
 
-		</h2>
+    <h2>
+      One of my first projects, this is an alternative frontend for IMDB that uses TMDB to fetch information about movies.
+    </h2>
 
-		<iframe src="https://imdb.simo.ng/?search=invincible" width="800" height="400" frameborder="0" allowfullscreen="">
-        </iframe>
-		</div>
-		</div>
-	</div>
-</div>
-
-<style>
-	#imdb {
-		animation: createBox 0.2s;
-	}
-
-	@keyframes createBox {
-		from {
-			transform: scale(0);
-		}
-		to {
-			transform: scale(1);
-		}
-	}
-</style>
+    <iframe src="https://imdb.simo.ng/?search=invincible" width="800" height="400" frameborder="0" allowfullscreen="">
+    </iframe>
+  </div>
+</Item>

+ 95 - 0
src/components/projects/item.astro

@@ -0,0 +1,95 @@
+---
+const { title, projectName, layout = {} } = Astro.props;
+
+const defaultLayout = {
+  mobile:   { left: '2%', top: '5%', width: '96%', height: 'auto' },
+  tablet:   { left: '10%', top: '10%', width: '80%', height: 'auto' },
+  desktop:  { left: '10%', top: '10%', width: '60%', height: '70%' }
+};
+
+const mergedLayout = {
+  mobile:   { ...defaultLayout.mobile, ...(layout.mobile || {}) },
+  tablet:   { ...defaultLayout.tablet, ...(layout.tablet || {}) },
+  desktop:  { ...defaultLayout.desktop, ...(layout.desktop || {}) }
+};
+---
+
+<div
+    id={title}
+    class=`window projectwindow ${projectName}`
+    style="display: none;"
+>
+    <div id={`${title}header`} class="title-bar">
+        <div class="title-bar-text">{title}</div>
+        <div class="title-bar-controls">
+            <button class="close" aria-label="Minimize"></button>
+            <button class="close" aria-label="Close"></button>
+        </div>
+    </div>
+    <div class="window-body" style="height: 100%;">
+        <slot/>
+    </div>
+</div>
+
+<style define:vars={{
+  mobileLeft: mergedLayout.mobile.left,
+  mobileTop: mergedLayout.mobile.top,
+  mobileWidth: mergedLayout.mobile.width,
+  mobileHeight: mergedLayout.mobile.height,
+  tabletLeft: mergedLayout.tablet.left,
+  tabletTop: mergedLayout.tablet.top,
+  tabletWidth: mergedLayout.tablet.width,
+  tabletHeight: mergedLayout.tablet.height,
+  desktopLeft: mergedLayout.desktop.left,
+  desktopTop: mergedLayout.desktop.top,
+  desktopWidth: mergedLayout.desktop.width,
+  desktopHeight: mergedLayout.desktop.height
+}}>
+.window {
+  animation: createBox 0.2s;
+  position: absolute;
+}
+
+@keyframes createBox {
+  from {
+    transform: scale(0);
+  }
+  to {
+    transform: scale(1);
+  }
+}
+
+/* Mobile */
+@media (max-width: 510px) {
+  .window {
+    left: var(--mobileLeft);
+    top: var(--mobileTop);
+    max-width: var(--mobileWidth);
+    max-height: var(--mobileHeight);
+  }
+}
+
+/* Tablet */
+@media (min-width: 541px) and (max-width: 1080px) {
+  .window {
+    left: var(--tabletLeft);
+    top: var(--tabletTop);
+    max-width: var(--tabletWidth);
+    max-height: var(--tabletHeight);
+  }
+}
+
+/* Desktop */
+@media (min-width: 1081px) {
+  .window {
+    left: var(--desktopLeft);
+    top: var(--desktopTop);
+    max-width: var(--desktopWidth);
+    max-height: var(--desktopHeight);
+
+    width: var(--desktopWidth);
+    height: var(--desktopHeight);
+
+  }
+}
+</style>

+ 21 - 47
src/components/projects/notion.astro

@@ -1,53 +1,27 @@
-<div
-	id="notion"
-	class="window projectwindow"
-	style="width: 50%; position: absolute; left: 10%; top: 10%; display: none; max-height: 820px; min-width: 400px;"
->
-	<div id="notionheader" class="title-bar">
-		<div class="title-bar-text">Notion Canvas</div>
-		<div class="title-bar-controls">
-			<button class="close" aria-label="Minimize"></button>
-			<button class="close" aria-label="Close"></button>
-		</div>
-	</div>
-	<div class="window-body" style="height: 100%;">
-		<div class="field-row-stacked" style="height: 100%;">
-			<!-- prettier-ignore -->
-			<div
-        id="text206"
-        style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: hidden; padding-top: 0px; padding-bottom: 20px;"
-        >
-		<a href="https://github.com/S1monlol/notionCanvas"><h1 style="width: fit-content;"><i><u><b>Notion Canvas</b></u></i></h1></a>
+---
+import Item from './item.astro';
+---
 
-		<h2>
-			This project allows for integration between a Canvas calendar and a Notion database. It makes it easy to import assignments into your notion assignment database
+<Item title="Notion Canvas" projectName="notion" layout={{
+  desktop: { width: '50%', height: 'auto' }
+}}>
+  <div
+    id="text206"
+    style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: hidden; padding-top: 0px; padding-bottom: 20px;"
+  >
+    <a href="https://github.com/S1monlol/notionCanvas"><h1 style="width: fit-content;"><i><u><b>Notion Canvas</b></u></i></h1></a>
 
-		</h2>
+    <h2>
+      This project allows for integration between a Canvas calendar and a Notion database. It makes it easy to import assignments into your notion assignment database
+    </h2>
 
-		<video id="vid" controls="true" src="/demo.mov" style="object-fit: fill; display: block;"></video>
-	</div>
-		</div>
-	</div>
-</div>
+    <video id="vid" controls="true" src="/demo.mov" style="object-fit: fill; display: block;"></video>
+  </div>
+</Item>
 
 <script>
-	// if document is phone size, hide video
-	if (window.innerWidth < 900) {
-		document.getElementById("vid").style.display = "none";
-	}
+  // if document is phone size, hide video
+  if (window.innerWidth < 900) {
+    document.getElementById("vid").style.display = "none";
+  }
 </script>
-
-<style>
-	#notion {
-		animation: createBox 0.2s;
-	}
-
-	@keyframes createBox {
-		from {
-			transform: scale(0);
-		}
-		to {
-			transform: scale(1);
-		}
-	}
-</style>

+ 15 - 45
src/components/projects/summize.astro

@@ -1,49 +1,19 @@
 ---
+import Item from './item.astro';
 import summizeExample from "../../assets/summizeExample.png";
 ---
 
-<div
-	id="summize"
-	class="window projectwindow"
-	style="width: 70%; height: 45%; position: absolute; left: 30%; top: 10%; display: none;"
->
-	<div id="summizeheader" class="title-bar">
-		<div class="title-bar-text">Summize</div>
-		<div class="title-bar-controls">
-			<button class="close" aria-label="Minimize"></button>
-			<button class="close" aria-label="Close"></button>
-		</div>
-	</div>
-	<div class="window-body" style="height: 100%;">
-		<div class="field-row-stacked" style="height: 100%;">
-			<!-- prettier-ignore -->
-			<div
-        id="text207"
-        style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: auto; padding-top: 0px;"
-        >
-		<a href="https://github.com/S1monlol/summize"><h1 style="width: fit-content;"><i><u><b>Summize - A Youtube Video Summarizer</b></u></i></h1></a>
-		<img src={summizeExample.src}>
-		<h2>
-			Summize is a browser extension that summarizes YouTube videos using OpenAI's GPT-3.5 language model.
-
-
-		</h2>
-	</div>
-		</div>
-	</div>
-</div>
-
-<style>
-	#summize {
-		animation: createBox 0.2s;
-	}
-
-	@keyframes createBox {
-		from {
-			transform: scale(0);
-		}
-		to {
-			transform: scale(1);
-		}
-	}
-</style>
+<Item id="summize" title="Summize - A Youtube Video Summarizer" layout={{
+  desktop: { width: '70%', height: '45%' }
+}}>
+  <div
+    id="text207"
+    style="font-size: 22px; resize: none; padding: 10px; height:90%; overflow-y: auto; padding-top: 0px;"
+  >
+    <a href="https://github.com/S1monlol/summize"><h1 style="width: fit-content;"><i><u><b>Summize - A Youtube Video Summarizer</b></u></i></h1></a>
+    <img src={summizeExample.src}>
+    <h2>
+      Summize is a browser extension that summarizes YouTube videos using OpenAI's GPT-3.5 language model.
+    </h2>
+  </div>
+</Item>

+ 11 - 45
src/components/redirect.astro

@@ -1,51 +1,17 @@
-<div
-    id="redirect"
-    class="window"
-    style="position: absolute; left: 12%; top: 22%; display: none; width: 30%;"
-    data-redirectUrl="test"
+---
+import Win98Window from "./win98window.astro"
+---
+<Win98Window
+  title="Redirect"
+  layout={{
+    mobile:  { left: '5%', top: '50%', width: '90%', height: 'auto' },
+    tablet:  { left: '30%', top: '20%', width: '50%', height: 'auto' },
+    desktop: { left: "30%", top: "30%", width: "40%", height: 'auto' }
+  }}
 >
-    <div id="redirectheader" class="title-bar">
-        <div class="title-bar-text">Redirect</div>
-        <div class="title-bar-controls">
-            <button class="close" aria-label="Minimize" style="background-color: silver;"></button>
-            <button class="close" aria-label="Close" style="background-color: silver;"></button>
-        </div>
-    </div>
-    <div class="window-body">
-        <!-- prettier-ignore -->
         <h2 style="margin-bottom: 10px; font-size: 22px;">You are about to be redirected to</h2>
         <section class="field-row" style="justify-content: flex-end">
             <button id="okButton" style="margin-top: 0;">OK</button>
             <button class="close" style="margin-top: 0;">Cancel</button>
         </section>
-    </div>
-</div>
-
-<style>
-
-    #redirect {
-
-    animation: createBox 0.2s;
-	}
-
-    @keyframes createBox {
-       	from {
-       		transform: scale(0);
-       	}
-       	to {
-       		transform: scale(1);
-       	}
-    }
-
-    @media (max-width: 540px) {
-        #redirect {
-            width: 80% !important;
-        }
-    }
-
-    @media (max-width: 1080px) and (min-width: 540px) {
-        #redirect {
-            width: 60% !important;
-        }
-    }
-</style>
+</Win98Window>

+ 107 - 0
src/components/win98window.astro

@@ -0,0 +1,107 @@
+---
+const { title, layout = {} } = Astro.props;
+
+const defaultLayout = {
+  mobile:   { left: '2%', top: '5%', width: '96%', height: 'auto' },
+  // tablet:   { left: '10%', top: '10%', width: '80%', height: 'auto' },
+  desktop:  { left: '12%', top: '22%', width: '30%', height: 'auto' }
+};
+
+const mergedLayout = {
+  mobile:   { ...defaultLayout.mobile, ...(layout.mobile || {}) },
+  // tablet:   { ...defaultLayout.tablet, ...(layout.tablet || {}) },
+  desktop:  { ...defaultLayout.desktop, ...(layout.desktop || {}) }
+};
+---
+
+<div
+    id={title}
+    class="window"
+    data-redirectUrl="test"
+    style="display: none;"
+>
+    <div id={`${title}header`} class="title-bar">
+        <div class="title-bar-text">{title}</div>
+        <div class="title-bar-controls">
+            <button class="close" aria-label="Minimize" style="background-color: silver;"></button>
+            <button class="close" aria-label="Close" style="background-color: silver;"></button>
+        </div>
+    </div>
+    <div class="window-body">
+        <slot/>
+    </div>
+</div>
+
+<style define:vars={{
+  mobileLeft: mergedLayout.mobile.left,
+  mobileTop: mergedLayout.mobile.top,
+  mobileWidth: mergedLayout.mobile.width,
+  mobileHeight: mergedLayout.mobile.height,
+
+  desktopLeft: mergedLayout.desktop.left,
+  desktopTop: mergedLayout.desktop.top,
+  desktopWidth: mergedLayout.desktop.width,
+  desktopHeight: mergedLayout.desktop.height
+}}>
+
+.title-bar-text {
+    font-size: 14px;
+}
+
+.window {
+  animation: createBox 0.15s;
+  position: absolute;
+}
+
+
+.closed {
+  animation: destroyBox 0.15s;
+}
+
+@keyframes createBox {
+  from { transform: scale(0); }
+  to { transform: scale(1); }
+}
+@keyframes destroyBox {
+  from { transform: scale(1); }
+  to { transform: scale(0); }
+}
+
+/* Mobile */
+@media (max-width: 510px) {
+  .window {
+    left: var(--mobileLeft);
+    top: var(--mobileTop);
+    max-width: var(--mobileWidth);
+    max-height: var(--mobileHeight);
+  }
+
+  .window-body {
+      height: var(--desktopHeight);
+      max-height: calc(var(--desktopHeight) - 45px);
+  }
+}
+
+/* Desktop */
+@media (min-width: 511px) {
+  .window {
+    left: var(--desktopLeft);
+    top: var(--desktopTop);
+
+    /*width: var(--desktopWidth);*/
+    max-width: var(--desktopWidth);
+
+    /*height: calc(var(--desktopHeight));*/
+    max-height: calc(var(--desktopHeight));
+  }
+
+  .window-body {
+      height: var(--desktopHeight);
+      max-height: calc(var(--desktopHeight) - 45px);
+
+      width: var(--desktopWitdth);
+      max-widtH: (var--desktopWidth);
+  }
+
+}
+</style>

+ 96 - 0
src/content/blogs/pgpcord.md

@@ -0,0 +1,96 @@
+Tiny Rocket Blog
+PGP + Discord = ??
+
+Table of Contents
+- TL;DR
+- Introduction
+  - Just Use Matrix?
+  - Why PGP?
+- Design Goals
+- Methodology
+- Implementation Notes
+- Potential Problems
+- Next Steps
+- Appendix: Quick Usage
+
+TL;DR
+I built a prototype extension (PGPCord) that lets you send PGP-encrypted messages over Discord by encrypting payloads client-side before they're posted. It demonstrates a practical way to layer end‑to‑end encryption on top of an untrusted platform. There are important limitations and attack surfaces — this is proof of concept, not a battle-tested privacy product.
+
+Introduction
+When you want to send truly private messages, you end up choosing between convenience and security. Centralized chat platforms are convenient but they require you to trust the client/server stack. Traditional E2EE messaging (Matrix, Signal, etc.) reduces server trust, but often pushes complexity into key‑exchange and relies heavily on client and server implementations being correct.
+
+Just Use Matrix?
+Matrix is a great open protocol with first‑class E2EE in many clients, but:
+- You still need to trust the client implementation (and, if you run your own server, the server implementation).
+- Many people will not adopt a new platform, so you lose the network effect.
+- Matrix E2EE can be brittle (key handling, device sync, and trust management introduce complexity).
+So while Matrix is ideal in many contexts, it’s not a practical migration for everyone you talk to.
+
+Why PGP?
+PGP has a few pragmatic properties that make it useful as a compatibility layer:
+- Keys are user‑generated and exportable. Public keys can be shared freely; private keys remain local.
+- No server can decrypt messages if encryption is done correctly client‑side.
+- People already know PGP and key distribution practices (web of trust, key servers, posting public key on websites).
+PGP is not perfect UX, but for an add‑on that encrypts text before it hits Discord, it brings a clear separation of trust: Discord carries ciphertext only.
+
+Design Goals
+- Minimal change to user workflow: write a message in Discord as usual, but allow the extension to encrypt it before sending.
+- Client-side private key storage: never upload or expose private keys to Discord or external services.
+- Shareable public keys: let users publish their public key (profile, blog, keyserver) for others to use.
+- Transparent UX: decrypt messages when they appear in the client UI if the recipient has the private key available locally.
+
+Methodology
+1. Key management:
+   - Users generate a PGP keypair in the extension or import an existing one.
+   - The extension stores the private key in the browser's secure storage (IndexedDB / WebCrypto-wrapped) and exposes the public key for sharing.
+2. Encrypt before send:
+   - When composing a message, the extension intercepts the send action, encrypts the plaintext using recipients' public keys, and posts the ciphertext instead.
+   - Format the ciphertext in a detectable wrapper (PGP ASCII armored block, optionally with a small header to help the extension detect it later).
+3. Detect and decrypt:
+   - When the client receives messages (including messages from other users), the extension scans for armored blocks, attempts decryption with local private keys, and replaces the ciphertext inline with the decrypted text in the UI.
+4. Fallback:
+   - If a recipient lacks a public key in their local address book, fall back to the plaintext send (or warn).
+   - Provide a way to send a "public key request" message to a user.
+
+Implementation Notes
+- Interception methods vary by platform and client:
+  - Web client: content scripts can modify the compose box, listen for submit events, and change outgoing payloads.
+  - Desktop clients: depends on extensibility (Electron clients might be patchable, native clients are harder).
+- Cryptography:
+  - Use a modern OpenPGP implementation (OpenPGP.js or similar).
+  - Use reasonable defaults for key size and algorithms, but allow user control.
+  - Protect private keys with a passphrase and, when possible, integrate with WebCrypto for key wrapping.
+- UI:
+  - Simple indicators for encrypted messages (lock icon, origin public key fingerprint).
+  - Automatic decryption should be opt-in per user to avoid leaking metadata or surprising the user.
+
+Potential Problems
+- Interception before encryption: the extension must be able to intercept and encrypt messages before they are sent. If the platform or client modifies content after the extension runs (or the extension hooks into the wrong event), plaintext might leak.
+- Metadata leakage: while the message body can be encrypted, metadata (recipient, timestamps, channel names) stays visible to the platform.
+- Key discovery and authenticity: publishing a public key somewhere doesn't guarantee it's the right key for a given Discord user account — an attacker could publish a rogue key. You still need a way to verify key ownership (fingerprint verification, posting key on authoritative profile pages).
+- Usability: key handling, passphrases, and import/export are UX pain points; users may make mistakes (e.g., losing private keys).
+- Platform countermeasures: some clients may obfuscate or restrict script-based modification, breaking the interception approach.
+- Legal and policy: automating encryption in messages might run afoul of platform policies or moderation tools that rely on plaintext scanning.
+
+Next Steps
+- Harden interception points and test across desktop and web clients.
+- Add a simple key discovery flow (profile metadata, pastebin, or DNS TXT records pointing to a public key).
+- Improve private key security by integrating browser-native key stores where available.
+- Explore message formats that minimize accidental plaintext leaks (e.g., envelope with metadata encrypted) and robust detection heuristics.
+- Perform threat modeling and consider consequences if keys are exfiltrated or if an attacker controls the extension update mechanism.
+
+Appendix: Quick Usage (Prototype)
+1. Install the extension in your browser.
+2. Generate or import your PGP key within the extension and publish your public key where your contacts can find it.
+3. When composing a Discord message, select the recipient or channel and use the extension's encrypt action — your text will be replaced with an armored PGP block before send.
+4. When you receive an armored block, the extension will attempt to decrypt and render it inline.
+
+Closing thoughts
+This approach is a pragmatic way to add end‑to‑end confidentiality on top of an existing platform without requiring everyone to switch services. It doesn't eliminate all risks, and it's not a drop-in replacement for a secure messaging app, but it can be a useful bridge while preserving familiar workflows. The biggest practical problems are interception timing, key authenticity, and handling metadata — if you can accept those constraints, PGPCord-style tooling can meaningfully reduce exposure of message plaintext to untrusted servers.
+
+If you want, I can:
+- Produce the extension skeleton (OpenPGP.js + content script hooks for Discord web),
+- Implement a robust key import/export UI,
+- Or adapt the design into a small demo you can run locally.
+
+(End of post)

+ 9 - 9
src/env.d.ts

@@ -1,15 +1,15 @@
+/// <reference path="../.astro/types.d.ts" />
 /// <reference types="astro/client" />
 
 interface app {
-    name: string;
-    link?: string;
-    logo: string;
-    row: number;
-    onclick?: (() => void) | string;
+	name: string;
+	link?: string;
+	logo: string;
+	row: number;
+	onclick?: (() => void) | string;
 }
 
 interface project {
-    title: string;
-    id: string;
-    
-}
+	title: string;
+	id: string;
+}

+ 2 - 1
src/layouts/Layout.astro

@@ -4,6 +4,7 @@ interface Props {
 }
 
 const { title } = Astro.props;
+import "../styles/98.css"
 ---
 
 <!doctype html>
@@ -15,7 +16,7 @@ const { title } = Astro.props;
 		<meta content="#ff0504" name="theme-color">
 		<link type="application/json+oembed" href="https://simo.ng/oembed.json" />
 		<link rel="icon" type="image/svg+xml" href="/favicon.gif" />
-		<link rel="stylesheet" href="https://unpkg.com/98.css@0.1.16" />
+		<!-- <link rel="stylesheet" href="https://unpkg.com/98.css@0.1.16" /> -->
 		<meta name="generator" content={Astro.generator} />
 		<title>{title}</title>
 	</head>

+ 25 - 19
src/pages/index.astro

@@ -90,7 +90,7 @@ function dragElement(elmnt) {
 
 /** Raise up window with given ID */
 function raiseUpSpec(id: string) {
-	// console.log("raising ", id);
+	console.log("raising ", id);
 	let els = document.getElementsByClassName("window");
 
 	let elArray = Array.from(els as HTMLCollectionOf<HTMLElement>);
@@ -110,7 +110,7 @@ function raiseUpSpec(id: string) {
 }
 
 function updateRedirectMessage() {
-	const redirectElement = document.getElementById("redirect");
+	const redirectElement = document.getElementById("Redirect");
 
 	const redirectUrl = redirectElement.dataset.redirecturl; // Access the data attribute
 
@@ -132,8 +132,8 @@ let apps: app[] = [
 		name: "About Me",
 		logo: me.src,
 		onclick: () => {
-			raiseUpSpec("aboutme");
-			document.getElementById("aboutme").style.display = "block";
+			raiseUpSpec("About Me");
+			document.getElementById("About Me").style.display = "block";
 		},
 		row: 1,
 	},
@@ -141,9 +141,9 @@ let apps: app[] = [
 		name: "Blog",
 		logo: blog.src,
 		onclick: () => {
-			raiseUpSpec("redirect");
-			document.getElementById("redirect").style.display = "block";
-			document.getElementById("redirect").dataset.redirecturl =
+			raiseUpSpec("Redirect");
+			document.getElementById("Redirect").style.display = "block";
+			document.getElementById("Redirect").dataset.redirecturl =
 				"https://blog.simo.ng/";
 			updateRedirectMessage();
 		},
@@ -153,8 +153,8 @@ let apps: app[] = [
 		name: "Projects",
 		logo: projects.src,
 		onclick: () => {
-			raiseUpSpec("projects");
-			document.getElementById("projects").style.display = "block";
+			raiseUpSpec("Projects ");
+			document.getElementById("Projects ").style.display = "block";
 		},
 		row: 1,
 	},
@@ -168,9 +168,9 @@ let apps: app[] = [
 		logo: github.src,
 		onclick: () => {
 			// ignore
-			raiseUpSpec("redirect");
-			document.getElementById("redirect").style.display = "block";
-			document.getElementById("redirect").dataset.redirecturl =
+			raiseUpSpec("Redirect");
+			document.getElementById("Redirect").style.display = "block";
+			document.getElementById("Redirect").dataset.redirecturl =
 				"https://github.com/s1monlol/";
 			updateRedirectMessage();
 		},
@@ -319,8 +319,8 @@ apps = apps.map((item) => {
 		}
 		project.onclick = () => {
 			setTimeout(() => {
-				document.getElementById(id).style.display = "block";
-				raiseUpSpec(id);
+				document.getElementsByClassName(id)[0].style.display = "block";
+				raiseUpSpec(document.getElementsByClassName(id)[0].id);
 			}, 1);
 		};
 	});
@@ -352,14 +352,19 @@ apps = apps.map((item) => {
 
 		Array.from(window.getElementsByClassName("close")).forEach((button, index) => {
 			if (index === 0) {
-				// First button is minimize
 				button.addEventListener("click", () => {
 					minimizeWindow(window.id);
 				});
 			} else {
-				// Second button is close
 				button.addEventListener("click", () => {
-					window.style.display = "none";
+				window.classList.add("closed")
+
+				setTimeout(() => {
+				        window.classList.remove("closed")
+						window.style.display = "none";
+				}, 100)
+
+					// window.style.display = "none";
 					removeFromTaskbar(window.id);
 				});
 			}
@@ -566,9 +571,10 @@ apps = apps.map((item) => {
 		startButton.addEventListener("click", () => {
 			startButtonClicks++;
 			if (startButtonClicks === 1) {
-				showDuolingo();
+			showGitHubGraph();
+
 			} else if (startButtonClicks === 2) {
-				showGitHubGraph();
+				showDuolingo();
 			}
 		});
 	}

ファイルの差分が大きいため隠しています
+ 0 - 0
src/styles/98.css


+ 11 - 0
src/styles/global.css

@@ -16,6 +16,17 @@
 	left: 0px !important;
 }
 
+@font-face {
+	font-family: "Pixelated MS Sans Serif";
+	src: url("/ms_sans_serif.woff") format("woff");
+	src: url("/ms_sans_serif.woff2") format("woff2");
+	font-weight: 1000;
+
+	font-style: normal;
+
+	font-smooth: always;
+}
+
 @keyframes minimizeWindow {
 	0% {
 		opacity: 1;

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません