-
-
Client A Browser tab
-
Client B Browser tab
-
-
-
FastAPI WebSocket Endpoint
-
/ws/{room}/{user}
-
ConnectionManager
-
Broadcast داخل همان room
-
-
-
Room: general A · B · C
-
Room: class D · E
-
+
+ Keynotes and Takeaways
+ The essential points to remember from the project.
+
+
HTTP is request/response Simple Stateless Not ideal for live push
+
Polling simulates real-time Easy Wasteful Interval-based latency
+
WebSocket keeps a channel Persistent Full-duplex Low latency
+
Server design matters Connection state Disconnect handling Scaling strategy
-
-
- Browser UI
- FastAPI endpoint
- Room Manager
- Broadcast
-
- نمای معماری و نقش ConnectionManager.
-
-
-
- Message Flow
- جریان پیام در پروژه
- از ورود کاربر تا نمایش پیام بدون refresh
-
-
-
1 Join name + room
-
2 Upgrade HTTP → WebSocket
-
3 Send JSON content
-
4 Broadcast room sockets
-
5 Render بدون refresh
-
-
-
-
نام و اتاق
-
WebSocket در JS
-
HTTP Upgrade
-
قبول در FastAPI
-
Room Manager
-
JSON message
-
Broadcast
-
نمایش پیام
-
- جریان اجرایی پیام در پروژه.
-
-
-
- Trade-offs
- مزایا و محدودیتها
- WebSocket برای Real-time عالی است، اما state سرور را بیشتر میکند
-
-
-
↔ Full-duplex ارسال دوطرفه بدون انتظار request بعدی
-
↓ Low latency
-
! Server state socket باز مدیریت disconnect
-
-
-
- Chat
- Notifications
- Live dashboard
- Online game
-
- مزایا و هزینههای عملیاتی.
-
-
-
- Demo
- برنامه اجرای دمو
- دو تب، یک room، پیام Real-time
-
-
-
-
- اجرای uvicorn app.main:app --reload
- باز کردن دو tab
- ورود دو user به یک room
- ارسال message
- نمایش بدون refresh
- تغییر online users
-
-
-
+ 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.
- ‹
+ <
1 / 10
- ›
+ >
-
← → حرکت · N یادداشت · F تمامصفحه
+
Left / Right to move - N for notes - F for fullscreen
diff --git a/docs/slides/script.js b/docs/slides/script.js
index 21a8089..3b5e014 100644
--- a/docs/slides/script.js
+++ b/docs/slides/script.js
@@ -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?.();
diff --git a/docs/slides/styles.css b/docs/slides/styles.css
index 56c853d..cfd8fab 100644
--- a/docs/slides/styles.css
+++ b/docs/slides/styles.css
@@ -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; }