docs(slides): improve slides images and theme
@@ -1,9 +1,8 @@
|
||||
# ارائه WebSocket فارسی
|
||||
# WebSocket Presentation
|
||||
|
||||
- فایل `index.html` را در مرورگر باز کنید.
|
||||
- با کلیدهای چپ/راست بین اسلایدها حرکت کنید.
|
||||
- کلید `N` یادداشت سخنران را نشان میدهد.
|
||||
- کلید `F` حالت تمامصفحه را فعال میکند.
|
||||
- فونت اصلی `Vazirmatn` است؛ فایل ارائه از Google Fonts / فونت نصبشده روی سیستم استفاده میکند.
|
||||
- در نمایشگرهای بزرگ، اسلایدها به صورت ۱۶:۹ تا جای ممکن ارتفاع صفحه را پر میکنند.
|
||||
- در نمایشگرهای کوچک، اندازه فونت و چینش اسلایدها کوچکتر و عمودیتر میشود.
|
||||
- Open `index.html` in a browser.
|
||||
- Use Left/Right arrows to move between slides.
|
||||
- Press `N` to show speaker notes.
|
||||
- Press `F` for fullscreen mode.
|
||||
- The deck uses the local Vazirmatn font file.
|
||||
- Visible slide content is limited to titles, subtitles, diagrams, images, and bullet points.
|
||||
|
||||
47
docs/slides/assets/app-demo.svg
Normal file
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Chatroom app mockup</title>
|
||||
<desc id="desc">English mockup of the WebSocket chatroom user interface.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Vazirmatn";
|
||||
src: url("../../../app/static/Vazirmatn[wght].woff2") format("woff2");
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
</style>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<path d="M42 430 L260 322 L358 488 L96 548 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M720 34 L930 96 L820 228 L640 140 Z" fill="#6d8cff" opacity=".14"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="88" y="74" width="784" height="392" rx="28" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="132" y="122" fill="#f8fafc" font-size="28" font-weight="800">Simple WebSocket Chatroom</text>
|
||||
<rect x="710" y="98" width="96" height="34" rx="17" fill="#43d9a3" opacity=".22" stroke="#43d9a3"/>
|
||||
<text x="758" y="121" text-anchor="middle" fill="#9ff4d5" font-size="14" font-weight="800">Connected</text>
|
||||
<rect x="132" y="164" width="496" height="232" rx="18" fill="#ffffff" opacity=".07" stroke="#ffffff"/>
|
||||
<text x="162" y="204" fill="#f8fafc" font-size="20" font-weight="800">Room: internet-engineering</text>
|
||||
<text x="162" y="232" fill="#a8adbd" font-size="15">You are chatting as Ali.</text>
|
||||
<rect x="230" y="266" width="244" height="48" rx="14" fill="#ffffff" opacity=".10" stroke="#ffffff"/>
|
||||
<text x="352" y="296" text-anchor="middle" fill="#f8fafc" font-size="15">Hello from WebSocket!</text>
|
||||
<rect x="362" y="332" width="210" height="48" rx="14" fill="#6d8cff" opacity=".32" stroke="#6d8cff"/>
|
||||
<text x="467" y="362" text-anchor="middle" fill="#f8fafc" font-size="15">Instant delivery</text>
|
||||
<rect x="132" y="412" width="496" height="34" rx="12" fill="#ffffff" opacity=".09" stroke="#ffffff"/>
|
||||
<text x="162" y="434" fill="#a8adbd" font-size="14">Type a message...</text>
|
||||
<rect x="548" y="412" width="80" height="34" rx="12" fill="#f4b740"/>
|
||||
<text x="588" y="434" text-anchor="middle" fill="#1f2937" font-size="14" font-weight="800">Send</text>
|
||||
<rect x="660" y="164" width="166" height="282" rx="18" fill="#ffffff" opacity=".07" stroke="#ffffff"/>
|
||||
<text x="743" y="204" text-anchor="middle" fill="#f8fafc" font-size="20" font-weight="800">Online Users</text>
|
||||
<text x="704" y="252" fill="#a8adbd" font-size="16">Ali</text>
|
||||
<text x="704" y="292" fill="#a8adbd" font-size="16">Sara</text>
|
||||
<text x="704" y="332" fill="#a8adbd" font-size="16">Reza</text>
|
||||
<circle cx="684" cy="246" r="6" fill="#43d9a3"/>
|
||||
<circle cx="684" cy="286" r="6" fill="#43d9a3"/>
|
||||
<circle cx="684" cy="326" r="6" fill="#43d9a3"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Chatroom architecture</title>
|
||||
<desc id="desc">A compact architecture diagram for the chatroom project.</desc>
|
||||
<desc id="desc">A compact architecture diagram for the FastAPI WebSocket chatroom project.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
@@ -9,41 +9,46 @@
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.title { fill: #f8fafc; font-size: 38px; font-weight: 850; }
|
||||
.sub { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.node-title { fill: #f8fafc; font-size: 22px; font-weight: 850; }
|
||||
.node-sub { fill: #a8adbd; font-size: 16px; }
|
||||
.label { font-size: 16px; font-weight: 850; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#2563eb"/></marker>
|
||||
<marker id="arrow-green" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#10b981"/></marker>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".12"/>
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<path d="M714 38 L932 104 L802 224 L642 132 Z" fill="#6d8cff" opacity=".14"/>
|
||||
<path d="M36 414 L236 304 L342 478 L96 548 Z" fill="#f4b740" opacity=".14"/>
|
||||
<text x="72" y="76" fill="#f8fafc" font-size="34" font-weight="800">Chatroom Architecture</text>
|
||||
<text x="72" y="112" fill="#a8adbd" font-size="17">Browser Clients - FastAPI - Room Manager</text>
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="82" y="178" width="190" height="82" rx="20" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="82" y="302" width="190" height="82" rx="20" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="368" y="178" width="224" height="206" rx="26" fill="#2563eb"/>
|
||||
<rect x="690" y="162" width="196" height="94" rx="22" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="690" y="308" width="196" height="94" rx="22" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="106" y="174" width="236" height="88" rx="22" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="106" y="306" width="236" height="88" rx="22" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="476" y="164" width="248" height="230" rx="28" fill="#2563eb"/>
|
||||
<rect x="858" y="164" width="236" height="96" rx="24" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="858" y="316" width="236" height="96" rx="24" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="224" y="212" text-anchor="middle" class="node-title">Client A</text>
|
||||
<text x="224" y="240" text-anchor="middle" class="node-sub">Browser tab</text>
|
||||
<text x="224" y="344" text-anchor="middle" class="node-title">Client B</text>
|
||||
<text x="224" y="372" text-anchor="middle" class="node-sub">Browser tab</text>
|
||||
<text x="600" y="232" text-anchor="middle" fill="#ffffff" font-size="30" font-weight="850">Server</text>
|
||||
<text x="600" y="272" text-anchor="middle" fill="#dbeafe" font-size="17">WebSocket Endpoint</text>
|
||||
<text x="600" y="306" text-anchor="middle" fill="#dbeafe" font-size="17">ConnectionManager</text>
|
||||
<text x="600" y="342" text-anchor="middle" fill="#ffffff" font-size="17" font-weight="850">/ws/{room}/{user}</text>
|
||||
<text x="976" y="204" text-anchor="middle" class="node-title">Room: general</text>
|
||||
<text x="976" y="234" text-anchor="middle" class="node-sub">A - B - C</text>
|
||||
<text x="976" y="356" text-anchor="middle" class="node-title">Room: class</text>
|
||||
<text x="976" y="386" text-anchor="middle" class="node-sub">D - E</text>
|
||||
<path d="M342 218 C408 218 428 232 476 232" fill="none" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M342 350 C408 350 428 326 476 326" fill="none" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow)"/>
|
||||
<path d="M724 238 C790 210 812 212 858 212" fill="none" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<path d="M724 320 C790 354 812 364 858 364" fill="none" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="376" y="188" fill="#f7c75f" class="label">WebSocket Frames</text>
|
||||
<text x="778" y="164" fill="#43d9a3" class="label">Broadcast by room</text>
|
||||
</g>
|
||||
<text x="177" y="214" text-anchor="middle" fill="#f8fafc" font-size="19" font-weight="800">Client A</text>
|
||||
<text x="177" y="240" text-anchor="middle" fill="#a8adbd" font-size="15">Browser tab</text>
|
||||
<text x="177" y="338" text-anchor="middle" fill="#f8fafc" font-size="19" font-weight="800">Client B</text>
|
||||
<text x="177" y="364" text-anchor="middle" fill="#a8adbd" font-size="15">Browser tab</text>
|
||||
<text x="480" y="242" text-anchor="middle" fill="#fff" font-size="25" font-weight="800">FastAPI</text>
|
||||
<text x="480" y="276" text-anchor="middle" fill="#dbeafe" font-size="16">WebSocket Endpoint</text>
|
||||
<text x="480" y="306" text-anchor="middle" fill="#dbeafe" font-size="16">ConnectionManager</text>
|
||||
<text x="480" y="338" text-anchor="middle" fill="#fff" font-size="15" font-weight="800">/ws/{room}/{user}</text>
|
||||
<text x="788" y="202" text-anchor="middle" fill="#f8fafc" font-size="19" font-weight="800">Room: general</text>
|
||||
<text x="788" y="230" text-anchor="middle" fill="#a8adbd" font-size="15">A - B - C</text>
|
||||
<text x="788" y="348" text-anchor="middle" fill="#f8fafc" font-size="19" font-weight="800">Room: class</text>
|
||||
<text x="788" y="376" text-anchor="middle" fill="#a8adbd" font-size="15">D - E</text>
|
||||
<path d="M272 220 C320 220 330 230 368 230" fill="none" stroke="#2563eb" stroke-width="4" marker-end="url(#arrow)"/>
|
||||
<path d="M272 344 C320 344 330 330 368 330" fill="none" stroke="#2563eb" stroke-width="4" marker-end="url(#arrow)"/>
|
||||
<path d="M592 242 C642 220 650 208 690 208" fill="none" stroke="#10b981" stroke-width="4" marker-end="url(#arrow-green)"/>
|
||||
<path d="M592 318 C642 340 650 356 690 356" fill="none" stroke="#10b981" stroke-width="4" marker-end="url(#arrow-green)"/>
|
||||
<text x="320" y="178" fill="#f7c75f" font-size="14" font-weight="800">WebSocket Frames</text>
|
||||
<text x="632" y="156" fill="#43d9a3" font-size="14" font-weight="800">Broadcast by room</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
43
docs/slides/assets/http-mechanism.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">HTTP mechanism</title>
|
||||
<desc id="desc">A short HTTP request response exchange.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Vazirmatn";
|
||||
src: url("../../../app/static/Vazirmatn[wght].woff2") format("woff2");
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.title { fill: #f8fafc; font-size: 38px; font-weight: 850; }
|
||||
.sub { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.node-title { fill: #f8fafc; font-size: 25px; font-weight: 850; }
|
||||
.node-sub { fill: #a8adbd; font-size: 16px; }
|
||||
.label { font-size: 18px; font-weight: 850; }
|
||||
</style>
|
||||
<marker id="arrow-blue" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#2563eb"/></marker>
|
||||
<marker id="arrow-green" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#10b981"/></marker>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="92" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="858" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="217" y="220" text-anchor="middle" class="node-title">Browser</text>
|
||||
<text x="217" y="254" text-anchor="middle" class="node-sub">HTTP Client</text>
|
||||
<text x="983" y="220" text-anchor="middle" class="node-title">Server</text>
|
||||
<text x="983" y="254" text-anchor="middle" class="node-sub">HTTP Endpoint</text>
|
||||
<line x1="372" y1="188" x2="828" y2="188" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="166" text-anchor="middle" fill="#f7c75f" class="label">1. GET /resource</text>
|
||||
<line x1="828" y1="252" x2="372" y2="252" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="236" text-anchor="middle" fill="#43d9a3" class="label">2. 200 OK Response</text>
|
||||
<line x1="372" y1="304" x2="828" y2="304" stroke="#fb7185" stroke-width="4" stroke-dasharray="12 10"/>
|
||||
<text x="600" y="334" text-anchor="middle" fill="#fb7185" class="label">3. Connection closes</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
46
docs/slides/assets/long-polling-mechanism.svg
Normal file
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Long Polling mechanism</title>
|
||||
<desc id="desc">An HTTP request held by the server until data is ready.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Vazirmatn";
|
||||
src: url("../../../app/static/Vazirmatn[wght].woff2") format("woff2");
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.title { fill: #f8fafc; font-size: 38px; font-weight: 850; }
|
||||
.sub { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.node-title { fill: #f8fafc; font-size: 25px; font-weight: 850; }
|
||||
.node-sub { fill: #a8adbd; font-size: 16px; }
|
||||
.label { font-size: 18px; font-weight: 850; }
|
||||
</style>
|
||||
<marker id="arrow-blue" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#2563eb"/></marker>
|
||||
<marker id="arrow-green" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#10b981"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#7c3aed"/></marker>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="92" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="858" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="217" y="220" text-anchor="middle" class="node-title">Browser</text>
|
||||
<text x="217" y="254" text-anchor="middle" class="node-sub">Waiting request</text>
|
||||
<text x="983" y="220" text-anchor="middle" class="node-title">Server</text>
|
||||
<text x="983" y="254" text-anchor="middle" class="node-sub">Holds response</text>
|
||||
<line x1="372" y1="174" x2="828" y2="174" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="152" text-anchor="middle" fill="#f7c75f" class="label">1. HTTP Request</text>
|
||||
<line x1="468" y1="230" x2="732" y2="230" stroke="#f4b740" stroke-width="8" stroke-linecap="round" stroke-dasharray="14 12"/>
|
||||
<text x="600" y="216" text-anchor="middle" fill="#f7c75f" class="label">2. Hold Open</text>
|
||||
<line x1="828" y1="292" x2="372" y2="292" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="278" text-anchor="middle" fill="#43d9a3" class="label">3. Message Response</text>
|
||||
<line x1="372" y1="332" x2="828" y2="332" stroke="#7c3aed" stroke-width="4" stroke-dasharray="10 9" marker-end="url(#arrow-purple)"/>
|
||||
<text x="600" y="362" text-anchor="middle" fill="#aebdff" class="label">4. New Request</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
49
docs/slides/assets/polling-mechanism.svg
Normal file
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Polling mechanism</title>
|
||||
<desc id="desc">Repeated HTTP requests on a timer.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Vazirmatn";
|
||||
src: url("../../../app/static/Vazirmatn[wght].woff2") format("woff2");
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.title { fill: #f8fafc; font-size: 38px; font-weight: 850; }
|
||||
.sub { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.node-title { fill: #f8fafc; font-size: 25px; font-weight: 850; }
|
||||
.node-sub { fill: #a8adbd; font-size: 16px; }
|
||||
.label { font-size: 17px; font-weight: 850; }
|
||||
</style>
|
||||
<marker id="arrow-blue" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#2563eb"/></marker>
|
||||
<marker id="arrow-green" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#10b981"/></marker>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="92" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="858" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="217" y="220" text-anchor="middle" class="node-title">Browser</text>
|
||||
<text x="217" y="254" text-anchor="middle" class="node-sub">Timer based checks</text>
|
||||
<text x="983" y="220" text-anchor="middle" class="node-title">Server</text>
|
||||
<text x="983" y="254" text-anchor="middle" class="node-sub">HTTP Endpoint</text>
|
||||
<line x1="372" y1="146" x2="828" y2="146" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="132" text-anchor="middle" fill="#f7c75f" class="label">Request 1</text>
|
||||
<line x1="828" y1="182" x2="372" y2="182" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="176" text-anchor="middle" fill="#43d9a3" class="label">Empty Response</text>
|
||||
<line x1="372" y1="224" x2="828" y2="224" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="218" text-anchor="middle" fill="#f7c75f" class="label">Request 2</text>
|
||||
<line x1="828" y1="260" x2="372" y2="260" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="254" text-anchor="middle" fill="#43d9a3" class="label">Empty Response</text>
|
||||
<line x1="372" y1="302" x2="828" y2="302" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="296" text-anchor="middle" fill="#f7c75f" class="label">Request 3</text>
|
||||
<line x1="828" y1="338" x2="372" y2="338" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="332" text-anchor="middle" fill="#43d9a3" class="label">Message Response</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">WebSocket handshake</title>
|
||||
<desc id="desc">A compact HTTP Upgrade handshake diagram.</desc>
|
||||
<defs>
|
||||
@@ -9,32 +9,37 @@
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.title { fill: #f8fafc; font-size: 38px; font-weight: 850; }
|
||||
.sub { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.node-title { fill: #f8fafc; font-size: 25px; font-weight: 850; }
|
||||
.node-sub { fill: #a8adbd; font-size: 16px; }
|
||||
.label { font-size: 18px; font-weight: 850; }
|
||||
</style>
|
||||
<marker id="arrow-blue" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#2563eb"/></marker>
|
||||
<marker id="arrow-green" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#10b981"/></marker>
|
||||
<marker id="arrow-purple" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M2,2 L10,6 L2,10 Z" fill="#7c3aed"/></marker>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".12"/>
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<path d="M42 420 L236 318 L348 462 L96 548 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M710 38 L934 90 L842 224 L660 162 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<text x="72" y="76" fill="#f8fafc" font-size="34" font-weight="800">HTTP Upgrade Handshake</text>
|
||||
<text x="72" y="112" fill="#a8adbd" font-size="17">Starts with HTTP, continues with WebSocket Frames</text>
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="82" y="190" width="220" height="154" rx="24" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="658" y="190" width="220" height="154" rx="24" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="92" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="858" y="166" width="250" height="132" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<text x="217" y="220" text-anchor="middle" class="node-title">Browser</text>
|
||||
<text x="217" y="254" text-anchor="middle" class="node-sub">JavaScript WebSocket</text>
|
||||
<text x="983" y="220" text-anchor="middle" class="node-title">Server</text>
|
||||
<text x="983" y="254" text-anchor="middle" class="node-sub">/ws/{room}/{user}</text>
|
||||
<line x1="372" y1="176" x2="828" y2="176" stroke="#2563eb" stroke-width="5" marker-end="url(#arrow-blue)"/>
|
||||
<text x="600" y="154" text-anchor="middle" fill="#f7c75f" class="label">1. GET + Upgrade</text>
|
||||
<line x1="828" y1="240" x2="372" y2="240" stroke="#10b981" stroke-width="5" marker-end="url(#arrow-green)"/>
|
||||
<text x="600" y="224" text-anchor="middle" fill="#43d9a3" class="label">2. 101 Switching Protocols</text>
|
||||
<line x1="372" y1="298" x2="828" y2="298" stroke="#7c3aed" stroke-width="5" stroke-dasharray="10 9" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="828" y1="336" x2="372" y2="336" stroke="#7c3aed" stroke-width="5" stroke-dasharray="10 9" marker-end="url(#arrow-purple)"/>
|
||||
<text x="600" y="360" text-anchor="middle" fill="#aebdff" class="label">3. Full-duplex WebSocket Frames</text>
|
||||
</g>
|
||||
<text x="192" y="252" text-anchor="middle" fill="#f8fafc" font-size="24" font-weight="800">Browser</text>
|
||||
<text x="192" y="286" text-anchor="middle" fill="#a8adbd" font-size="16">JavaScript WebSocket</text>
|
||||
<text x="768" y="252" text-anchor="middle" fill="#f8fafc" font-size="24" font-weight="800">FastAPI Server</text>
|
||||
<text x="768" y="286" text-anchor="middle" fill="#a8adbd" font-size="16">/ws/{room}/{user}</text>
|
||||
<line x1="320" y1="206" x2="638" y2="206" stroke="#2563eb" stroke-width="4" marker-end="url(#arrow-blue)"/>
|
||||
<text x="479" y="186" text-anchor="middle" fill="#f7c75f" font-size="16" font-weight="800">1. GET + Upgrade</text>
|
||||
<line x1="638" y1="268" x2="320" y2="268" stroke="#10b981" stroke-width="4" marker-end="url(#arrow-green)"/>
|
||||
<text x="479" y="250" text-anchor="middle" fill="#43d9a3" font-size="16" font-weight="800">2. 101 Switching Protocols</text>
|
||||
<line x1="320" y1="332" x2="638" y2="332" stroke="#7c3aed" stroke-width="4" stroke-dasharray="10 9" marker-end="url(#arrow-purple)"/>
|
||||
<line x1="638" y1="372" x2="320" y2="372" stroke="#7c3aed" stroke-width="4" stroke-dasharray="10 9" marker-end="url(#arrow-purple)"/>
|
||||
<text x="480" y="414" text-anchor="middle" fill="#aebdff" font-size="16" font-weight="800">3. Full-duplex WebSocket Frames</text>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
@@ -17,8 +17,7 @@
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<path d="M34 112 L238 34 L340 146 L156 238 Z" fill="#f4b740" opacity=".12"/>
|
||||
<path d="M700 410 L930 318 L982 504 L746 548 Z" fill="#6d8cff" opacity=".16"/>
|
||||
<text x="72" y="76" fill="#f8fafc" font-size="34" font-weight="800">WebSocket History</text>
|
||||
<text x="72" y="112" fill="#a8adbd" font-size="17">From Classic HTTP to Real-time communication</text>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<line x1="128" y1="282" x2="832" y2="282" stroke="#4a5168" stroke-width="10" stroke-linecap="round"/>
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="76" y="164" width="808" height="238" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
@@ -60,4 +59,5 @@
|
||||
<text x="830" y="390" fill="#a8adbd" font-size="15">Dashboard - IoT</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -1,6 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="500" viewBox="0 0 1200 500" role="img" aria-labelledby="title desc">
|
||||
<title id="title">WebSocket in OSI model</title>
|
||||
<desc id="desc">A compact OSI stack with English technical names and Persian supporting labels.</desc>
|
||||
<desc id="desc">A compact OSI stack showing WebSocket at the application layer over TCP/IP.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
@font-face {
|
||||
@@ -9,35 +9,49 @@
|
||||
font-weight: 100 900;
|
||||
}
|
||||
text { font-family: "Vazirmatn", Arial, sans-serif; }
|
||||
.layer { fill: #f8fafc; font-size: 18px; font-weight: 850; }
|
||||
.value { fill: #a8adbd; font-size: 18px; font-weight: 500; }
|
||||
.strong { fill: #ffffff; font-size: 19px; font-weight: 850; }
|
||||
.strongValue { fill: #ffffff; font-size: 19px; font-weight: 500; }
|
||||
.mutedRow { fill: #ffffff; opacity: .08; stroke: #ffffff; }
|
||||
</style>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".12"/>
|
||||
<feDropShadow dx="0" dy="18" stdDeviation="18" flood-color="#0f172a" flood-opacity=".16"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<text x="72" y="76" fill="#f8fafc" font-size="34" font-weight="800">WebSocket in the OSI Model</text>
|
||||
<text x="72" y="112" fill="#a8adbd" font-size="17">Application Layer over TCP/IP</text>
|
||||
|
||||
<rect width="1200" height="500" fill="#262a3a"/>
|
||||
<path d="M42 396 L258 286 L374 438 L92 532 Z" fill="#f4b740" opacity=".13"/>
|
||||
<path d="M880 42 L1146 112 L1030 262 L812 182 Z" fill="#6d8cff" opacity=".16"/>
|
||||
|
||||
<!-- Diagram is intentionally placed slightly above center, with all rows inside the outer panel. -->
|
||||
<g id="diagram-content">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="106" y="142" width="748" height="350" rx="26" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="150" y="80" width="900" height="350" rx="30" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
</g>
|
||||
<g font-size="17">
|
||||
<rect x="138" y="170" width="684" height="52" rx="14" fill="#2563eb"/>
|
||||
<text x="162" y="203" fill="#fff" font-weight="800">Layer 7 - Application</text>
|
||||
<text x="516" y="203" fill="#fff">WebSocket - JSON - Chat</text>
|
||||
<rect x="138" y="232" width="684" height="44" rx="13" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<text x="162" y="260" fill="#f8fafc" font-weight="800">Layer 6 - Presentation</text>
|
||||
<text x="516" y="260" fill="#a8adbd">UTF-8 - Text</text>
|
||||
<rect x="138" y="286" width="684" height="44" rx="13" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<text x="162" y="314" fill="#f8fafc" font-weight="800">Layer 5 - Session</text>
|
||||
<text x="516" y="314" fill="#a8adbd">Long-lived conversation</text>
|
||||
<rect x="138" y="340" width="684" height="52" rx="14" fill="#10b981"/>
|
||||
<text x="162" y="373" fill="#fff" font-weight="800">Layer 4 - Transport</text>
|
||||
<text x="516" y="373" fill="#fff">TCP reliable stream</text>
|
||||
<rect x="138" y="402" width="684" height="44" rx="13" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<text x="162" y="430" fill="#f8fafc" font-weight="800">Layer 3 - Network</text>
|
||||
<text x="516" y="430" fill="#a8adbd">IP routing</text>
|
||||
<rect x="138" y="456" width="684" height="44" rx="13" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<text x="162" y="484" fill="#f8fafc" font-weight="800">Layers 2-1 - Link / Physical</text>
|
||||
<text x="516" y="484" fill="#a8adbd">Ethernet - Wi-Fi</text>
|
||||
|
||||
<rect x="190" y="105" width="820" height="48" rx="15" fill="#2563eb"/>
|
||||
<text x="222" y="136" class="strong">Layer 7 - Application</text>
|
||||
<text x="712" y="136" class="strongValue">WebSocket - JSON - Chat</text>
|
||||
|
||||
<rect x="190" y="165" width="820" height="40" rx="14" class="mutedRow"/>
|
||||
<text x="222" y="191" class="layer">Layer 6 - Presentation</text>
|
||||
<text x="712" y="191" class="value">UTF-8 - Text</text>
|
||||
|
||||
<rect x="190" y="215" width="820" height="40" rx="14" class="mutedRow"/>
|
||||
<text x="222" y="241" class="layer">Layer 5 - Session</text>
|
||||
<text x="712" y="241" class="value">Long-lived conversation</text>
|
||||
|
||||
<rect x="190" y="265" width="820" height="48" rx="15" fill="#10b981"/>
|
||||
<text x="222" y="296" class="strong">Layer 4 - Transport</text>
|
||||
<text x="712" y="296" class="strongValue">TCP reliable stream</text>
|
||||
|
||||
<rect x="190" y="325" width="820" height="40" rx="14" class="mutedRow"/>
|
||||
<text x="222" y="351" class="layer">Layer 3 - Network</text>
|
||||
<text x="712" y="351" class="value">IP routing</text>
|
||||
|
||||
<rect x="190" y="375" width="820" height="40" rx="14" class="mutedRow"/>
|
||||
<text x="222" y="401" class="layer">Layers 2-1 - Link / Physical</text>
|
||||
<text x="712" y="401" class="value">Ethernet - Wi-Fi</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -17,8 +17,7 @@
|
||||
<rect width="960" height="540" fill="#262a3a"/>
|
||||
<path d="M38 412 L244 320 L330 484 L96 548 Z" fill="#43d9a3" opacity=".12"/>
|
||||
<path d="M720 34 L936 112 L828 236 L640 136 Z" fill="#fb7185" opacity=".12"/>
|
||||
<text x="72" y="76" fill="#f8fafc" font-size="34" font-weight="800">WebSocket Trade-offs</text>
|
||||
<text x="72" y="112" fill="#a8adbd" font-size="17">Strengths and limitations at a glance</text>
|
||||
<g id="diagram-content-shift" transform="translate(0,-28)">
|
||||
<g filter="url(#shadow)">
|
||||
<rect x="92" y="154" width="350" height="286" rx="28" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
<rect x="518" y="154" width="350" height="286" rx="28" fill="#ffffff" opacity=".08" stroke="#ffffff"/>
|
||||
@@ -41,4 +40,5 @@
|
||||
<circle cx="562" cy="298" r="7" fill="#ef4444"/>
|
||||
<circle cx="562" cy="346" r="7" fill="#ef4444"/>
|
||||
<circle cx="562" cy="394" r="7" fill="#ef4444"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,241 +1,143 @@
|
||||
<!doctype html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ارائه WebSocket و Socket Programming</title>
|
||||
<title>WebSocket Protocol</title>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="deck" id="deck">
|
||||
<section class="slide active title-slide" data-title="شروع">
|
||||
<div class="kicker">پروژه مهندسی اینترنت</div>
|
||||
<h1>چتروم ساده با WebSocket</h1>
|
||||
<p class="subtitle">نمایش Socket Programming و ارتباط Real-time در وب</p>
|
||||
|
||||
<div class="hero-grid">
|
||||
<img class="hero-img glass-media" src="assets/home.png" alt="نمای صفحه چتروم" />
|
||||
<div class="talk-track glass-panel">
|
||||
<span>مسیر ارائه</span>
|
||||
<strong>History → Polling → Handshake → OSI → Architecture → Demo</strong>
|
||||
<div class="signal-board">
|
||||
<div class="signal"><i>1</i><div><b>Handshake</b><span>HTTP Upgrade</span></div></div>
|
||||
<div class="signal"><i>2</i><div><b>Channel</b><span>Persistent TCP</span></div></div>
|
||||
<div class="signal"><i>3</i><div><b>Frames</b><span>Full-duplex</span></div></div>
|
||||
<div class="signal"><i>4</i><div><b>Broadcast</b><span>Room delivery</span></div></div>
|
||||
<section class="slide active center-slide" data-title="Title">
|
||||
<div class="center-content">
|
||||
<h1>Websocket Protocol</h1>
|
||||
<p class="subtitle">Socket Programming with a FastAPI real-time demo chat app</p>
|
||||
<p class="members">Amirhossein Khalili • Morteza Khanbabaie • Alireza Khosravi</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">معرفی سریع پروژه و مسیر ارائه.</aside>
|
||||
<aside class="notes">Open with the project name and explain that the presentation focuses on WebSocket concepts through a simple chatroom implementation.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="History">
|
||||
<div class="kicker">History</div>
|
||||
<h2>چرا WebSocket لازم شد؟</h2>
|
||||
<p class="subtitle">مسیر تکامل از HTTP کلاسیک تا اتصال پایدار دوطرفه</p>
|
||||
|
||||
<div class="timeline-panel">
|
||||
<div class="image-focus glass-panel">
|
||||
<img src="assets/websocket-history.svg" alt="تاریخچه WebSocket" />
|
||||
<section class="slide" data-title="Timeline">
|
||||
<h2>WebSocket History Timeline</h2>
|
||||
<p class="subtitle">The communication model evolved from simple request/response to persistent real-time channels.</p>
|
||||
<div class="timeline-only glass-panel">
|
||||
<div class="timeline-method"><span>Classic HTTP</span><small>1991</small></div>
|
||||
<div class="timeline-method"><span>Polling</span><small>1995+</small></div>
|
||||
<div class="timeline-method"><span>Long Polling</span><small>2006</small></div>
|
||||
<div class="timeline-method"><span>WebSocket</span><small>2011</small></div>
|
||||
<div class="timeline-method"><span>Real-time Web</span><small>2010s+</small></div>
|
||||
</div>
|
||||
<div class="timeline-cards point-column">
|
||||
<article><small>HTTP</small><div><b>Request / Response</b><ul><li>ساده</li><li>بدون Server Push</li></ul></div></article>
|
||||
<article><small>AJAX</small><div><b>Polling</b><ul><li>درخواست تکراری</li><li>هزینه شبکه بیشتر</li></ul></div></article>
|
||||
<article><small>LONG</small><div><b>Long Polling</b><ul><li>اتصال منتظر</li><li>پیچیدگی بیشتر</li></ul></div></article>
|
||||
<article><small>WS</small><div><b>WebSocket</b><ul><li>اتصال پایدار</li><li>پیام دوطرفه</li></ul></div></article>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">توضیح کوتاه درباره نیاز به دریافت لحظهای داده.</aside>
|
||||
<aside class="notes">Use this slide to name the major approaches only. The next slides explain how each mechanism works and what it costs.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Cost">
|
||||
<div class="kicker">Resource Cost</div>
|
||||
<h2>هزینه هر روش برای سرور</h2>
|
||||
<p class="subtitle">هر روش، فشار متفاوتی روی Network، CPU، memory و socket دارد</p>
|
||||
|
||||
<div class="resource-grid point-cards">
|
||||
<article><b>Classic HTTP</b><ul><li>درخواست جداگانه</li><li>پردازش header</li><li>پایان چرخه</li></ul></article>
|
||||
<article><b>Polling</b><ul><li>درخواست زیاد</li><li>پاسخهای خالی</li><li>سربار Network</li></ul></article>
|
||||
<article><b>Long Polling</b><ul><li>اتصال باز</li><li>نگهداری state</li><li>مصرف buffer</li></ul></article>
|
||||
<article><b>WebSocket</b><ul><li>یک اتصال پایدار</li><li>فریم سبک</li><li>state فعال</li></ul></article>
|
||||
<section class="slide protocol-slide" data-title="HTTP">
|
||||
<h2>HTTP</h2>
|
||||
<p class="subtitle">A short-lived request/response cycle with no native live push.</p>
|
||||
<div class="protocol-layout">
|
||||
<div class="protocol-visual glass-panel">
|
||||
<img src="assets/http-mechanism.svg" alt="HTTP request response mechanism" />
|
||||
</div>
|
||||
|
||||
<div class="quote glass-panel">Polling → درخواست تکراری · Long Polling → انتظار طولانی · WebSocket → کانال پایدار</div>
|
||||
<aside class="notes">مقایسه فشرده هزینهها.</aside>
|
||||
<div class="protocol-points glass-panel">
|
||||
<article><b>Mechanism</b><ul><li>One request returns one response</li><li>The connection closes after the exchange</li></ul></article>
|
||||
<article><b>Cost</b><ul><li>Every update needs a new HTTP cycle</li><li>The server cannot push live data by itself</li></ul></article>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">HTTP is excellent for normal pages, forms, and APIs. Its basic model is not optimized for continuous server-to-client updates.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Handshake">
|
||||
<div class="kicker">HTTP Upgrade Handshake</div>
|
||||
<h2>WebSocket چطور شروع میشود؟</h2>
|
||||
<p class="subtitle">ابتدا HTTP، سپس پاسخ 101 و بعد WebSocket Frames</p>
|
||||
|
||||
<div class="upgrade-map">
|
||||
<div class="endpoint glass-panel"><span>Client</span><b>Browser</b><ul><li>ساخت WebSocket</li><li>درخواست Upgrade</li></ul></div>
|
||||
<div class="upgrade-steps">
|
||||
<div>1. GET + Upgrade</div>
|
||||
<div>2. 101 Switching Protocols</div>
|
||||
<div>3. WebSocket Frames</div>
|
||||
<section class="slide protocol-slide" data-title="Polling">
|
||||
<h2>Polling</h2>
|
||||
<p class="subtitle">The browser repeatedly asks the server for new data.</p>
|
||||
<div class="protocol-layout">
|
||||
<div class="protocol-visual glass-panel">
|
||||
<img src="assets/polling-mechanism.svg" alt="Polling repeated request mechanism" />
|
||||
</div>
|
||||
<div class="endpoint glass-panel"><span>Server</span><b>FastAPI</b><ul><li>پذیرش Upgrade</li><li>نگهداری socket</li></ul></div>
|
||||
<div class="protocol-points glass-panel">
|
||||
<article><b>Mechanism</b><ul><li>The client checks on a fixed timer</li><li>The server answers even when nothing changed</li></ul></article>
|
||||
<article><b>Cost</b><ul><li>Many empty requests waste network work</li><li>Latency depends on the timer interval</li></ul></article>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">Polling is simple but wasteful. Short intervals improve latency but increase server and network cost.</aside>
|
||||
</section>
|
||||
|
||||
<div class="split">
|
||||
<div class="diagram-card glass-panel">
|
||||
<section class="slide protocol-slide" data-title="Long Polling">
|
||||
<h2>Long Polling</h2>
|
||||
<p class="subtitle">The server holds the request until an update is available.</p>
|
||||
<div class="protocol-layout">
|
||||
<div class="protocol-visual glass-panel">
|
||||
<img src="assets/long-polling-mechanism.svg" alt="Long Polling held request mechanism" />
|
||||
</div>
|
||||
<div class="protocol-points glass-panel">
|
||||
<article><b>Mechanism</b><ul><li>The server keeps the request open</li><li>The client reconnects after each response</li></ul></article>
|
||||
<article><b>Cost</b><ul><li>Open requests consume server state</li><li>Reconnect and timeout handling is required</li></ul></article>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">Long Polling reduces empty responses compared with Polling, but it still works through repeated HTTP request cycles.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide protocol-slide" data-title="WebSocket">
|
||||
<h2>WebSocket</h2>
|
||||
<p class="subtitle">An HTTP Upgrade creates one persistent Full-duplex channel.</p>
|
||||
<div class="protocol-layout">
|
||||
<div class="protocol-visual glass-panel">
|
||||
<img src="assets/websocket-handshake.svg" alt="HTTP Upgrade Handshake" />
|
||||
</div>
|
||||
<div class="code-stack ltr">
|
||||
<pre><code>GET /ws/general/ali HTTP/1.1
|
||||
Upgrade: websocket
|
||||
Connection: Upgrade
|
||||
Sec-WebSocket-Key: ...</code></pre>
|
||||
<pre><code>HTTP/1.1 101 Switching Protocols
|
||||
Upgrade: websocket
|
||||
Connection: Upgrade</code></pre>
|
||||
<div class="protocol-points glass-panel">
|
||||
<article><b>Mechanism</b><ul><li>HTTP Upgrade switches the protocol</li><li>Frames move both ways on one socket</li></ul></article>
|
||||
<article><b>Cost</b><ul><li>Each client keeps a live connection</li><li>Scaling needs connection-aware design</li></ul></article>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">نمایش handshake و تبدیل HTTP به WebSocket.</aside>
|
||||
<aside class="notes">This is the key WebSocket slide. The browser starts with HTTP, upgrades the connection, and then both client and server can send frames whenever needed.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide image-slide" data-title="OSI">
|
||||
<h2>WebSocket in the OSI Model</h2>
|
||||
<p class="subtitle">Application semantics at Layer 7, reliable byte delivery through TCP/IP.</p>
|
||||
<div class="single-visual glass-panel">
|
||||
<img src="assets/websocket-osi.svg" alt="WebSocket in OSI model" />
|
||||
</div>
|
||||
<aside class="notes">WebSocket is an application-layer protocol. It uses TCP for reliable transport and IP for routing. The lower layers do not know what a chat message or room is.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="HTTP Versions">
|
||||
<div class="kicker">HTTP Versions</div>
|
||||
<h2>WebSocket در نسخههای HTTP</h2>
|
||||
<p class="subtitle">مدل آموزشی پروژه روی HTTP/1.1 Upgrade تمرکز دارد</p>
|
||||
|
||||
<div class="version-row point-cards">
|
||||
<article><span>HTTP/1.1</span><b>Upgrade + 101</b><ul><li>مدل کلاسیک</li><li>یک TCP connection</li><li>ساده برای آموزش</li></ul></article>
|
||||
<article><span>HTTP/2</span><b>Extended CONNECT</b><ul><li>روی یک stream</li><li>بدون Upgrade کلاسیک</li><li>پیچیدهتر</li></ul></article>
|
||||
<article><span>HTTP/3</span><b>QUIC + CONNECT</b><ul><li>بدون TCP</li><li>روی QUIC</li><li>مدرنتر</li></ul></article>
|
||||
<h2>HTTP Versions in WebSocket</h2>
|
||||
<p class="subtitle">Different HTTP versions start WebSocket connections differently.</p>
|
||||
<div class="version-row point-cards detailed-version-row">
|
||||
<article><span>HTTP/1.1</span><b>Upgrade + 101 Switching Protocols</b><ul><li>The browser sends a normal GET request with <code>Upgrade: websocket</code>.</li><li>The server replies <code>101 Switching Protocols</code> if it accepts.</li><li>After that, the same TCP connection carries WebSocket frames both ways.</li><li>This is the classic and easiest model to understand.</li></ul></article>
|
||||
<article><span>HTTP/2</span><b>Extended CONNECT over a Stream</b><ul><li>HTTP/2 does not use the old connection-level Upgrade flow.</li><li>It uses Extended CONNECT, so WebSocket traffic lives inside one HTTP/2 stream.</li><li>Other HTTP/2 streams can share the same underlying connection.</li><li>Support depends more on servers, clients, and proxies.</li></ul></article>
|
||||
<article><span>HTTP/3</span><b>CONNECT over QUIC</b><ul><li>HTTP/3 runs on QUIC over UDP instead of TCP.</li><li>WebSocket uses a CONNECT-based mapping similar in idea to HTTP/2.</li><li>QUIC streams reduce transport-level head-of-line blocking.</li><li>It is modern, but the demo is easier to explain with HTTP/1.1.</li></ul></article>
|
||||
</div>
|
||||
<div class="focus-line glass-panel"><b>HTTP/1.1:</b> Upgrade → 101 → WebSocket Frames</div>
|
||||
<aside class="notes">تفاوت نسخهها در یک نگاه.</aside>
|
||||
<aside class="notes">The project is best explained with HTTP/1.1 Upgrade. HTTP/2 and HTTP/3 support WebSocket through different mechanisms.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="OSI">
|
||||
<div class="kicker">OSI Model</div>
|
||||
<h2>جایگاه WebSocket در شبکه</h2>
|
||||
<p class="subtitle">WebSocket در لایه Application استفاده میشود؛ TCP/IP حمل داده را انجام میدهد</p>
|
||||
|
||||
<div class="osi-board">
|
||||
<div class="osi-stack glass-panel">
|
||||
<div class="osi-layer active"><b>Layer 7</b><span>WebSocket + JSON</span></div>
|
||||
<div class="osi-layer"><b>Layer 6</b><span>UTF-8 text</span></div>
|
||||
<div class="osi-layer"><b>Layer 5</b><span>Long-lived session</span></div>
|
||||
<div class="osi-layer transport"><b>Layer 4</b><span>TCP reliable stream</span></div>
|
||||
<div class="osi-layer"><b>Layer 3</b><span>IP routing</span></div>
|
||||
<div class="osi-layer"><b>Layer 2/1</b><span>Ethernet / Wi-Fi</span></div>
|
||||
<section class="slide image-slide" data-title="FastAPI Architecture">
|
||||
<h2>Project Architecture</h2>
|
||||
<p class="subtitle">Browsers connect to one WebSocket endpoint; ConnectionManager groups sockets by room.</p>
|
||||
<div class="single-visual glass-panel">
|
||||
<img src="assets/chatroom-architecture.svg" alt="WebSocket chatroom architecture" />
|
||||
</div>
|
||||
<div class="layer-note glass-panel point-column">
|
||||
<div><i class="metric-icon">A</i><b>Application</b><ul><li>اتاق</li><li>کاربر</li><li>پیام</li></ul></div>
|
||||
<div><i class="metric-icon">T</i><b>Transport</b><ul><li>TCP</li><li>تحویل قابل اعتماد</li></ul></div>
|
||||
<div><i class="metric-icon">N</i><b>Network</b><ul><li>IP</li><li>مسیریابی</li></ul></div>
|
||||
</div>
|
||||
</div>
|
||||
<aside class="notes">تفکیک لایه کاربرد از لایههای انتقال و شبکه.</aside>
|
||||
<aside class="notes">Explain the code structure: FastAPI serves the frontend, the WebSocket endpoint accepts connections, and the manager stores active clients by room before broadcasting messages.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Architecture">
|
||||
<div class="kicker">Project Architecture</div>
|
||||
<h2>معماری پروژه چتروم</h2>
|
||||
<p class="subtitle">هر تب مرورگر یک WebSocket connection دارد</p>
|
||||
|
||||
<div class="topology">
|
||||
<div class="node-column">
|
||||
<div class="node glass-panel"><b>Client A</b><span>Browser tab</span></div>
|
||||
<div class="node glass-panel"><b>Client B</b><span>Browser tab</span></div>
|
||||
<section class="slide" data-title="Takeaways">
|
||||
<h2>Keynotes and Takeaways</h2>
|
||||
<p class="subtitle">The essential points to remember from the project.</p>
|
||||
<div class="takeaway-grid point-cards">
|
||||
<article><b>HTTP is request/response</b><ul><li>Simple</li><li>Stateless</li><li>Not ideal for live push</li></ul></article>
|
||||
<article><b>Polling simulates real-time</b><ul><li>Easy</li><li>Wasteful</li><li>Interval-based latency</li></ul></article>
|
||||
<article><b>WebSocket keeps a channel</b><ul><li>Persistent</li><li>Full-duplex</li><li>Low latency</li></ul></article>
|
||||
<article><b>Server design matters</b><ul><li>Connection state</li><li>Disconnect handling</li><li>Scaling strategy</li></ul></article>
|
||||
</div>
|
||||
<div class="server-node glass-panel">
|
||||
<b>FastAPI WebSocket Endpoint</b>
|
||||
<p>/ws/{room}/{user}</p>
|
||||
<div class="room-badge">ConnectionManager</div>
|
||||
<div class="room-badge">Broadcast داخل همان room</div>
|
||||
</div>
|
||||
<div class="node-column">
|
||||
<div class="node glass-panel"><b>Room: general</b><span>A · B · C</span></div>
|
||||
<div class="node glass-panel"><b>Room: class</b><span>D · E</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mini-points">
|
||||
<span>Browser UI</span>
|
||||
<span>FastAPI endpoint</span>
|
||||
<span>Room Manager</span>
|
||||
<span>Broadcast</span>
|
||||
</div>
|
||||
<aside class="notes">نمای معماری و نقش ConnectionManager.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Flow">
|
||||
<div class="kicker">Message Flow</div>
|
||||
<h2>جریان پیام در پروژه</h2>
|
||||
<p class="subtitle">از ورود کاربر تا نمایش پیام بدون refresh</p>
|
||||
|
||||
<div class="pipeline">
|
||||
<div class="pipe-step glass-panel"><i class="step-dot">1</i><b>Join</b><span>name + room</span></div>
|
||||
<div class="pipe-step glass-panel"><i class="step-dot">2</i><b>Upgrade</b><span>HTTP → WebSocket</span></div>
|
||||
<div class="pipe-step glass-panel"><i class="step-dot">3</i><b>Send</b><span>JSON content</span></div>
|
||||
<div class="pipe-step glass-panel"><i class="step-dot">4</i><b>Broadcast</b><span>room sockets</span></div>
|
||||
<div class="pipe-step glass-panel"><i class="step-dot">5</i><b>Render</b><span>بدون refresh</span></div>
|
||||
</div>
|
||||
|
||||
<div class="flow">
|
||||
<div>نام و اتاق</div>
|
||||
<div>WebSocket در JS</div>
|
||||
<div>HTTP Upgrade</div>
|
||||
<div>قبول در FastAPI</div>
|
||||
<div>Room Manager</div>
|
||||
<div>JSON message</div>
|
||||
<div>Broadcast</div>
|
||||
<div>نمایش پیام</div>
|
||||
</div>
|
||||
<aside class="notes">جریان اجرایی پیام در پروژه.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Pros Cons">
|
||||
<div class="kicker">Trade-offs</div>
|
||||
<h2>مزایا و محدودیتها</h2>
|
||||
<p class="subtitle">WebSocket برای Real-time عالی است، اما state سرور را بیشتر میکند</p>
|
||||
|
||||
<div class="metrics">
|
||||
<article class="metric-card glass-panel"><i class="metric-icon">↔</i><b>Full-duplex</b><ul><li>ارسال دوطرفه</li><li>بدون انتظار request بعدی</li></ul></article>
|
||||
<article class="metric-card glass-panel"><i class="metric-icon">↓</i><b>Low latency</b><ul><li>تاخیر کم</li><li>فریم سبکتر</li></ul></article>
|
||||
<article class="metric-card glass-panel"><i class="metric-icon">!</i><b>Server state</b><ul><li>socket باز</li><li>مدیریت disconnect</li></ul></article>
|
||||
</div>
|
||||
|
||||
<div class="mini-points">
|
||||
<span>Chat</span>
|
||||
<span>Notifications</span>
|
||||
<span>Live dashboard</span>
|
||||
<span>Online game</span>
|
||||
</div>
|
||||
<aside class="notes">مزایا و هزینههای عملیاتی.</aside>
|
||||
</section>
|
||||
|
||||
<section class="slide" data-title="Demo">
|
||||
<div class="kicker">Demo</div>
|
||||
<h2>برنامه اجرای دمو</h2>
|
||||
<p class="subtitle">دو تب، یک room، پیام Real-time</p>
|
||||
|
||||
<div class="demo-layout">
|
||||
<img class="glass-media" src="assets/home.png" alt="نمای دمو" />
|
||||
<ol class="glass-panel">
|
||||
<li>اجرای <code>uvicorn app.main:app --reload</code></li>
|
||||
<li>باز کردن دو tab</li>
|
||||
<li>ورود دو user به یک room</li>
|
||||
<li>ارسال message</li>
|
||||
<li>نمایش بدون refresh</li>
|
||||
<li>تغییر online users</li>
|
||||
</ol>
|
||||
</div>
|
||||
<aside class="notes">جمعبندی و اجرای دمو.</aside>
|
||||
<aside class="notes">End with the central idea: WebSocket is useful when real-time bidirectional communication matters, but it moves responsibility to the server to manage long-lived connections.</aside>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="controls" aria-hidden="true">
|
||||
<button id="prev" title="قبلی">‹</button>
|
||||
<button id="prev" title="Previous"><</button>
|
||||
<span id="counter">1 / 10</span>
|
||||
<button id="next" title="بعدی">›</button>
|
||||
<button id="next" title="Next">></button>
|
||||
</div>
|
||||
<div class="hint">← → حرکت · N یادداشت · F تمامصفحه</div>
|
||||
<div class="hint">Left / Right to move - N for notes - F for fullscreen</div>
|
||||
<div class="progress"><span id="progressBar"></span></div>
|
||||
<div class="speaker-notes" id="speakerNotes"></div>
|
||||
<script src="script.js"></script>
|
||||
|
||||
@@ -54,8 +54,8 @@ document.getElementById('prev').addEventListener('click', () => go(-1));
|
||||
document.getElementById('next').addEventListener('click', () => go(1));
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (['ArrowRight', 'PageUp'].includes(e.key)) go(-1);
|
||||
if (['ArrowLeft', 'PageDown', ' '].includes(e.key)) go(1);
|
||||
if (['ArrowLeft', 'PageUp'].includes(e.key)) go(-1);
|
||||
if (['ArrowRight', 'PageDown', ' '].includes(e.key)) go(1);
|
||||
if (e.key.toLowerCase() === 'n') notesBox.classList.toggle('show');
|
||||
if (e.key.toLowerCase() === 'f') {
|
||||
if (!document.fullscreenElement) document.documentElement.requestFullscreen?.();
|
||||
|
||||
@@ -87,6 +87,16 @@ body {
|
||||
}
|
||||
.slide > * { position: relative; z-index: 1; }
|
||||
.slide.active { display: block; animation: fadeUp .28s ease-out; }
|
||||
.slide.active.center-slide {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.slide.active.protocol-slide,
|
||||
.slide.active.image-slide {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto minmax(0, 1fr);
|
||||
}
|
||||
@keyframes fadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.kicker {
|
||||
@@ -94,9 +104,9 @@ body {
|
||||
font-weight: 900;
|
||||
color: var(--amber);
|
||||
margin-bottom: clamp(5px, calc(8px * var(--slide-scale)), 14px);
|
||||
letter-spacing: -.2px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
h1, h2 { margin: 0; line-height: 1.08; letter-spacing: -1px; }
|
||||
h1, h2 { margin: 0; line-height: 1.08; letter-spacing: 0; }
|
||||
h1 { font-size: clamp(34px, calc(58px * var(--slide-scale)), 82px); }
|
||||
h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); }
|
||||
.subtitle {
|
||||
@@ -104,6 +114,26 @@ h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); }
|
||||
font-size: clamp(16px, calc(22px * var(--slide-scale)), 32px);
|
||||
margin: clamp(10px, calc(16px * var(--slide-scale)), 24px) 0 clamp(16px, calc(26px * var(--slide-scale)), 36px);
|
||||
}
|
||||
.center-content {
|
||||
width: min(900px, 100%);
|
||||
}
|
||||
.center-content .subtitle {
|
||||
max-width: 760px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.members {
|
||||
margin: clamp(6px, calc(10px * var(--slide-scale)), 14px) auto 0;
|
||||
color: rgba(248, 250, 252, .72);
|
||||
font-size: clamp(11px, calc(14px * var(--slide-scale)), 19px);
|
||||
font-weight: 300;
|
||||
letter-spacing: .01em;
|
||||
}
|
||||
.protocol-slide .subtitle,
|
||||
.image-slide .subtitle {
|
||||
margin-bottom: clamp(10px, calc(14px * var(--slide-scale)), 22px);
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
@@ -260,6 +290,68 @@ h2 { font-size: clamp(27px, calc(46px * var(--slide-scale)), 66px); }
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.timeline-only {
|
||||
margin-top: clamp(24px, calc(42px * var(--slide-scale)), 62px);
|
||||
padding: clamp(24px, calc(36px * var(--slide-scale)), 52px);
|
||||
border-radius: clamp(12px, calc(18px * var(--slide-scale)), 24px);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: clamp(10px, calc(16px * var(--slide-scale)), 24px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.timeline-only::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 9%;
|
||||
right: 9%;
|
||||
top: 50%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(244, 183, 64, .8), transparent);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.timeline-method {
|
||||
min-height: clamp(132px, calc(170px * var(--slide-scale)), 240px);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
border: 1px solid rgba(255,255,255,.14);
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
background: linear-gradient(145deg, rgba(255,255,255,.11), rgba(255,255,255,.045));
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 18px 42px rgba(0,0,0,.18);
|
||||
}
|
||||
.timeline-method::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: clamp(16px, calc(22px * var(--slide-scale)), 30px);
|
||||
height: clamp(16px, calc(22px * var(--slide-scale)), 30px);
|
||||
border-radius: 50%;
|
||||
background: var(--amber);
|
||||
box-shadow: 0 0 0 7px rgba(244, 183, 64, .15);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.timeline-method span {
|
||||
max-width: 95%;
|
||||
padding: 0 8px;
|
||||
font-size: clamp(15px, calc(21px * var(--slide-scale)), 30px);
|
||||
line-height: 1.25;
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
transform: translateY(calc(-44px * var(--slide-scale)));
|
||||
}
|
||||
|
||||
.timeline-method small {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: calc(50% + clamp(18px, calc(28px * var(--slide-scale)), 40px));
|
||||
transform: translateX(-50%);
|
||||
color: #ffe8aa;
|
||||
font-size: clamp(13px, calc(17px * var(--slide-scale)), 24px);
|
||||
font-weight: 300;
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -276,12 +368,193 @@ li::before {
|
||||
display: inline-block;
|
||||
width: .46em;
|
||||
height: .46em;
|
||||
margin-left: .55em;
|
||||
margin-right: .55em;
|
||||
margin-left: 0;
|
||||
border-radius: 50%;
|
||||
background: var(--amber);
|
||||
vertical-align: .08em;
|
||||
}
|
||||
|
||||
.mechanism-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.08fr .92fr;
|
||||
gap: clamp(18px, calc(28px * var(--slide-scale)), 40px);
|
||||
margin-top: clamp(20px, calc(34px * var(--slide-scale)), 48px);
|
||||
align-items: stretch;
|
||||
}
|
||||
.protocol-layout {
|
||||
--protocol-pad: clamp(10px, calc(16px * var(--slide-scale)), 24px);
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.protocol-visual {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: var(--protocol-pad);
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
.protocol-visual img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
display: block;
|
||||
border-radius: clamp(8px, calc(12px * var(--slide-scale)), 16px);
|
||||
background: #262a3a;
|
||||
}
|
||||
.protocol-points {
|
||||
position: absolute;
|
||||
right: clamp(18px, calc(26px * var(--slide-scale)), 38px);
|
||||
left: clamp(18px, calc(26px * var(--slide-scale)), 38px);
|
||||
bottom: clamp(14px, calc(22px * var(--slide-scale)), 32px);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(12px, calc(18px * var(--slide-scale)), 28px);
|
||||
padding: clamp(10px, calc(14px * var(--slide-scale)), 20px);
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
background: rgba(16, 20, 32, .76);
|
||||
border-color: rgba(255,255,255,.16);
|
||||
}
|
||||
.protocol-points article {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: clamp(6px, calc(9px * var(--slide-scale)), 14px);
|
||||
}
|
||||
.protocol-points b {
|
||||
display: block;
|
||||
color: #fff;
|
||||
font-size: clamp(15px, calc(19px * var(--slide-scale)), 28px);
|
||||
}
|
||||
.protocol-points li {
|
||||
color: #d3d8e6;
|
||||
font-size: clamp(11px, calc(13px * var(--slide-scale)), 18px);
|
||||
line-height: 1.35;
|
||||
}
|
||||
.bullet-panel {
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
padding: clamp(18px, calc(26px * var(--slide-scale)), 38px);
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: clamp(12px, calc(18px * var(--slide-scale)), 26px);
|
||||
}
|
||||
.bullet-panel b {
|
||||
display: block;
|
||||
font-size: clamp(18px, calc(24px * var(--slide-scale)), 34px);
|
||||
color: #fff;
|
||||
}
|
||||
.bullet-panel ul + b {
|
||||
margin-top: clamp(2px, calc(8px * var(--slide-scale)), 14px);
|
||||
}
|
||||
.mini-flow,
|
||||
.repeat-stack,
|
||||
.hold-diagram {
|
||||
width: 100%;
|
||||
min-height: clamp(280px, calc(360px * var(--slide-scale)), 500px);
|
||||
direction: ltr;
|
||||
text-align: center;
|
||||
}
|
||||
.mini-flow {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr auto 1fr;
|
||||
gap: clamp(10px, calc(14px * var(--slide-scale)), 22px);
|
||||
align-items: center;
|
||||
}
|
||||
.mini-flow div,
|
||||
.repeat-stack div,
|
||||
.hold-diagram div {
|
||||
min-height: clamp(70px, calc(94px * var(--slide-scale)), 138px);
|
||||
border: 1px solid rgba(255,255,255,.14);
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
background: linear-gradient(145deg, rgba(255,255,255,.12), rgba(255,255,255,.055));
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: clamp(10px, calc(16px * var(--slide-scale)), 24px);
|
||||
font-size: clamp(15px, calc(20px * var(--slide-scale)), 29px);
|
||||
font-weight: 900;
|
||||
line-height: 1.25;
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.14);
|
||||
}
|
||||
.mini-flow span,
|
||||
.hold-diagram span {
|
||||
color: var(--amber);
|
||||
font-size: clamp(14px, calc(18px * var(--slide-scale)), 26px);
|
||||
font-weight: 900;
|
||||
}
|
||||
.repeat-stack,
|
||||
.hold-diagram {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: clamp(12px, calc(18px * var(--slide-scale)), 26px);
|
||||
}
|
||||
.repeat-stack div:nth-child(3),
|
||||
.hold-diagram div:nth-child(3) {
|
||||
border-color: rgba(67, 217, 163, .42);
|
||||
background: linear-gradient(145deg, rgba(67, 217, 163, .18), rgba(255,255,255,.055));
|
||||
}
|
||||
|
||||
.single-visual {
|
||||
--visual-pad: clamp(12px, calc(18px * var(--slide-scale)), 26px);
|
||||
margin-top: clamp(18px, calc(32px * var(--slide-scale)), 48px);
|
||||
height: min(440px, calc(var(--slide-h) - 190px));
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
padding: var(--visual-pad);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.single-visual img {
|
||||
position: absolute;
|
||||
inset: var(--visual-pad);
|
||||
width: calc(100% - var(--visual-pad) - var(--visual-pad));
|
||||
height: calc(100% - var(--visual-pad) - var(--visual-pad));
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
.image-slide .single-visual {
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
}
|
||||
.image-slide .single-visual img {
|
||||
object-fit: fill;
|
||||
}
|
||||
.takeaway-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(14px, calc(20px * var(--slide-scale)), 30px);
|
||||
margin-top: clamp(12px, calc(20px * var(--slide-scale)), 32px);
|
||||
min-height: clamp(390px, calc(455px * var(--slide-scale)), 540px);
|
||||
}
|
||||
.takeaway-grid article {
|
||||
min-height: 0;
|
||||
background: rgba(255,255,255,.075);
|
||||
border: 1px solid rgba(255,255,255,.12);
|
||||
border-radius: clamp(10px, calc(14px * var(--slide-scale)), 18px);
|
||||
padding: clamp(16px, calc(24px * var(--slide-scale)), 36px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.takeaway-grid article::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 42px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: var(--amber);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.takeaway-grid b {
|
||||
display: block;
|
||||
font-size: clamp(17px, calc(22px * var(--slide-scale)), 32px);
|
||||
line-height: 1.25;
|
||||
margin-bottom: clamp(12px, calc(18px * var(--slide-scale)), 26px);
|
||||
}
|
||||
|
||||
.resource-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
@@ -379,6 +652,30 @@ pre {
|
||||
font-weight: 900;
|
||||
margin-bottom: clamp(12px, calc(20px * var(--slide-scale)), 30px);
|
||||
}
|
||||
|
||||
.detailed-version-row {
|
||||
margin-top: clamp(18px, calc(30px * var(--slide-scale)), 46px);
|
||||
}
|
||||
.detailed-version-row article {
|
||||
min-height: clamp(300px, calc(385px * var(--slide-scale)), 500px);
|
||||
padding: clamp(14px, calc(20px * var(--slide-scale)), 30px);
|
||||
}
|
||||
.detailed-version-row b {
|
||||
font-size: clamp(16px, calc(21px * var(--slide-scale)), 30px);
|
||||
line-height: 1.22;
|
||||
}
|
||||
.detailed-version-row li {
|
||||
font-size: clamp(10px, calc(13px * var(--slide-scale)), 17px);
|
||||
line-height: 1.42;
|
||||
}
|
||||
.detailed-version-row code {
|
||||
color: #ffe8aa;
|
||||
background: rgba(244, 183, 64, .10);
|
||||
border: 1px solid rgba(244, 183, 64, .18);
|
||||
border-radius: 6px;
|
||||
padding: 0 4px;
|
||||
font-size: .92em;
|
||||
}
|
||||
.focus-line {
|
||||
margin-top: clamp(18px, calc(30px * var(--slide-scale)), 44px);
|
||||
background: rgba(255,255,255,.075);
|
||||
@@ -410,9 +707,9 @@ pre {
|
||||
position: relative;
|
||||
}
|
||||
.flow div:not(:nth-child(4n))::after {
|
||||
content: "←";
|
||||
content: "->";
|
||||
position: absolute;
|
||||
left: clamp(-18px, calc(-16px * var(--slide-scale)), -10px);
|
||||
right: clamp(-18px, calc(-16px * var(--slide-scale)), -10px);
|
||||
top: 38%;
|
||||
color: var(--amber);
|
||||
font-size: clamp(18px, calc(24px * var(--slide-scale)), 34px);
|
||||
@@ -569,7 +866,7 @@ pre {
|
||||
position: relative;
|
||||
}
|
||||
.pipe-step:not(:last-child)::after {
|
||||
content: "→";
|
||||
content: "->";
|
||||
position: absolute;
|
||||
right: -17px;
|
||||
top: 44%;
|
||||
@@ -608,7 +905,8 @@ pre {
|
||||
}
|
||||
ol {
|
||||
margin: 0;
|
||||
padding-right: clamp(20px, calc(28px * var(--slide-scale)), 42px);
|
||||
padding-left: clamp(34px, calc(46px * var(--slide-scale)), 64px);
|
||||
padding-right: clamp(18px, calc(22px * var(--slide-scale)), 30px);
|
||||
font-size: clamp(15px, calc(21px * var(--slide-scale)), 30px);
|
||||
line-height: 2;
|
||||
color: #23324a;
|
||||
@@ -708,12 +1006,37 @@ code {
|
||||
h2 { font-size: clamp(24px, 7vw, 34px); }
|
||||
.subtitle { font-size: clamp(14px, 4vw, 18px); }
|
||||
.kicker { font-size: clamp(12px, 3.7vw, 16px); }
|
||||
.hero-grid, .split, .demo-layout, .version-row, .resource-grid { grid-template-columns: 1fr; }
|
||||
.hero-grid, .split, .demo-layout { gap: 18px; }
|
||||
.resource-grid, .version-row { gap: 12px; margin-top: 22px; }
|
||||
.resource-grid article, .version-row article { min-height: auto; padding: 16px; }
|
||||
.resource-grid b, .version-row b { font-size: 18px; }
|
||||
.slide.active.protocol-slide,
|
||||
.slide.active.image-slide { display: block; }
|
||||
.hero-grid, .split, .demo-layout, .version-row, .resource-grid, .mechanism-grid, .takeaway-grid { grid-template-columns: 1fr; }
|
||||
.takeaway-grid { grid-template-rows: none; min-height: 0; }
|
||||
.hero-grid, .split, .demo-layout, .mechanism-grid { gap: 18px; }
|
||||
.resource-grid, .version-row, .takeaway-grid { gap: 12px; margin-top: 22px; }
|
||||
.resource-grid article, .version-row article, .takeaway-grid article { min-height: auto; padding: 16px; }
|
||||
.resource-grid b, .version-row b, .takeaway-grid b { font-size: 18px; }
|
||||
.resource-grid p, .version-row p { font-size: 13px; }
|
||||
.timeline-only { grid-template-columns: 1fr; gap: 12px; padding: 16px; }
|
||||
.timeline-only::before { display: none; }
|
||||
.timeline-method { min-height: 74px; }
|
||||
.timeline-method::before { left: 18px; top: 50%; }
|
||||
.timeline-method span { transform: none; font-size: 16px; }
|
||||
.timeline-method small { position: static; transform: none; font-size: 13px; margin-top: 6px; }
|
||||
.mini-flow { grid-template-columns: 1fr; min-height: 0; }
|
||||
.mini-flow span { transform: rotate(90deg); }
|
||||
.repeat-stack, .hold-diagram { min-height: 0; }
|
||||
.protocol-layout { display: block; min-height: 0; overflow: visible; }
|
||||
.protocol-visual { position: relative; inset: auto; padding: 10px; }
|
||||
.protocol-visual img { width: 100%; height: auto; object-fit: contain; }
|
||||
.protocol-points {
|
||||
position: static;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 12px;
|
||||
padding: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
.bullet-panel { padding: 16px; gap: 12px; }
|
||||
.single-visual { height: auto; min-height: 0; padding: 10px; display: block; }
|
||||
.single-visual img { position: static; width: 100%; height: auto; padding: 0; object-fit: contain; }
|
||||
.image-focus, .image-focus.compact { height: auto; min-height: 0; padding: 10px; }
|
||||
.image-focus img { width: 100%; height: auto; }
|
||||
.flow { grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 22px; }
|
||||
|
||||