mdeck 3.7.1 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +3 -3
- package/assets/base.css +9 -5
- package/assets/extensions/themes/academic/extension.toml +4 -1
- package/assets/extensions/themes/academic/styles.css +22 -22
- package/assets/extensions/themes/glass/extension.toml +7 -1
- package/assets/extensions/themes/glass/styles.css +17 -17
- package/assets/extensions/themes/minimal/extension.toml +3 -1
- package/assets/extensions/themes/minimal/styles.css +14 -14
- package/assets/extensions/themes/neue/extension.toml +3 -1
- package/assets/extensions/themes/neue/styles.css +14 -14
- package/assets/extensions/themes/plain/extension.toml +7 -1
- package/assets/extensions/themes/plain/styles.css +17 -17
- package/assets/extensions/themes/work/extension.toml +8 -2
- package/assets/extensions/themes/work/styles.css +26 -26
- package/docs/reference/authoring.md +21 -4
- package/docs/reference/extensions.md +5 -1
- package/docs/reference/themes.md +6 -2
- package/docs/site/content/drawing.md +1 -1
- package/docs/site/content/presenting.md +45 -0
- package/docs/site/content/theme-authoring.md +28 -0
- package/examples/python/slides.de.md +493 -0
- package/examples/python/{slides.md → slides.en.md} +1 -1
- package/examples/python/slides.fa.md +492 -0
- package/examples/python/slides.hi.md +492 -0
- package/examples/showcase/slides.md +33 -0
- package/package.json +1 -1
- package/skills/write-slides/SKILL.md +1 -0
- package/src/build/config.js +1 -1
- package/src/build/slidesPlugin.js +34 -4
- package/src/cli/index.js +4 -0
- package/src/cli/themes.js +2 -0
- package/src/components/callout.css +10 -10
- package/src/components/citation.css +2 -2
- package/src/components/code-block.css +3 -3
- package/src/components/poll.css +6 -6
- package/src/components/video-player.css +1 -1
- package/src/core/editDeck.js +3 -2
- package/src/core/icons.js +1 -0
- package/src/core/languages.js +51 -0
- package/src/core/parseSlides.js +15 -3
- package/src/core/validateDeck.js +13 -1
- package/src/editor/components/DeckSettings.jsx +37 -3
- package/src/editor/components/ExtensionForms.jsx +12 -0
- package/src/editor/components/MetaFields.jsx +7 -0
- package/src/editor/extensions.js +6 -3
- package/src/extensions/appearance.js +6 -2
- package/src/extensions/manifest.js +3 -3
- package/src/extensions/params.js +113 -0
- package/src/layouts/layoutApi.jsx +7 -1
- package/src/live/answer/answer.css +2 -2
- package/src/runtime/chrome.jsx +6 -7
- package/src/runtime/deck-stage.js +249 -11
- package/src/runtime/hotspots.js +98 -0
- package/src/runtime/ink/bus.js +12 -1
- package/src/runtime/ink/room.js +20 -8
- package/src/runtime/main.jsx +152 -22
- package/src/runtime/zoom/camera.js +77 -0
- /package/examples/python/{slides.drawings.json → slides.en.drawings.json} +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
---
|
|
2
|
+
theme: academic
|
|
3
|
+
meta:
|
|
4
|
+
title: "Python का परिचय"
|
|
5
|
+
author: "Tilman Schieber"
|
|
6
|
+
organization: "FHNW"
|
|
7
|
+
date: "2026-05-19"
|
|
8
|
+
palette: paper
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
:::meta
|
|
13
|
+
layout: title
|
|
14
|
+
id: python-for-life-sciences
|
|
15
|
+
:::
|
|
16
|
+
# लाइफ़ साइंसेज़ के लिए Python।
|
|
17
|
+
## मास्टर्स के छात्रों के लिए क़दम-दर-क़दम परिचय।
|
|
18
|
+
|
|
19
|
+
:::notes
|
|
20
|
+
Python मॉड्यूल में आपका स्वागत है। इस सत्र के लिए प्रोग्रामिंग का कोई पूर्व अनुभव ज़रूरी नहीं है।
|
|
21
|
+
|
|
22
|
+
- हम क़दम-दर-क़दम चलेंगे — सवाल पूछने में बिल्कुल न झिझकें
|
|
23
|
+
- सारा कोड सीधे ब्राउज़र में चलता है — आज कुछ इंस्टॉल करने की ज़रूरत नहीं
|
|
24
|
+
- लक्ष्य: अंत तक आप एक डेटासेट लोड कर पाएँगे, उसे जाँच पाएँगे और उसका प्लॉट बना पाएँगे
|
|
25
|
+
:::
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
:::meta
|
|
29
|
+
layout: focus
|
|
30
|
+
eyebrow: Python क्यों?
|
|
31
|
+
id: the-tools-your-field-uses-are-written-in
|
|
32
|
+
:::
|
|
33
|
+
# आपके क्षेत्र के टूल *Python में लिखे गए हैं*।
|
|
34
|
+
|
|
35
|
+
:::notes
|
|
36
|
+
डेटा पर आधारित काम के लिए Python शिक्षा जगत और उद्योग, दोनों में सबसे प्रचलित भाषा है।
|
|
37
|
+
लाइफ़ साइंसेज़ भी अपवाद नहीं — बायोइन्फ़ॉर्मेटिक्स, इमेजिंग और क्लिनिकल डेटा विश्लेषण, सब Python पर चलते हैं।
|
|
38
|
+
|
|
39
|
+
इसका इकोसिस्टम (NumPy, Pandas, Matplotlib, scikit-learn, BioPython) बेजोड़ है।
|
|
40
|
+
बताएँ कि सांख्यिकी में R अब भी आम है, पर Python काफ़ी हद तक साझा भाषा बन चुकी है।
|
|
41
|
+
:::
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
:::meta
|
|
45
|
+
layout: chapter
|
|
46
|
+
number: 1
|
|
47
|
+
part: शुरुआत
|
|
48
|
+
description: वेरिएबल, टाइप और आपके कोड की पहली पंक्तियाँ।
|
|
49
|
+
id: variables-and-data-types
|
|
50
|
+
:::
|
|
51
|
+
# वेरिएबल और डेटा टाइप।
|
|
52
|
+
|
|
53
|
+
:::notes
|
|
54
|
+
यहीं से शुरू करें, भले ही छात्रों ने पहले थोड़ा Python देखा हो। हम बुनियाद से बनाते हैं।
|
|
55
|
+
इस अध्याय का अनुमानित समय: 20 मिनट।
|
|
56
|
+
:::
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
:::meta
|
|
60
|
+
layout: split
|
|
61
|
+
section: वेरिएबल
|
|
62
|
+
id: variables-store-data
|
|
63
|
+
props:
|
|
64
|
+
ratio: [3, 2]
|
|
65
|
+
:::
|
|
66
|
+
# वेरिएबल किसी मान का नाम है।
|
|
67
|
+
:::slot left
|
|
68
|
+
```python live editable copy
|
|
69
|
+
name = "E. coli"
|
|
70
|
+
genome_size = 4_641_652 # बेस पेयर
|
|
71
|
+
gc_content = 0.506 # अनुपात
|
|
72
|
+
|
|
73
|
+
print(name, genome_size, gc_content)
|
|
74
|
+
print(f"GC content: {gc_content:.1%}")
|
|
75
|
+
```
|
|
76
|
+
:::
|
|
77
|
+
:::slot right
|
|
78
|
+
कोई घोषणा नहीं: Python मान से ही टाइप पहचान लेता है।
|
|
79
|
+
|
|
80
|
+
**आज़माएँ:** `gc_content` को `0.65` कर दें और फिर से चलाएँ।
|
|
81
|
+
:::
|
|
82
|
+
|
|
83
|
+
:::notes
|
|
84
|
+
वेरिएबल बस किसी मान से जुड़ा एक नाम है। Python टाइप अपने-आप पहचान लेता है।
|
|
85
|
+
|
|
86
|
+
ध्यान दिलाएँ: न सेमीकोलन, न टाइप घोषणा, न अतिरिक्त ढाँचा। `f"…"` स्ट्रिंग किसी मान को टेक्स्ट के भीतर फ़ॉर्मैट करती है; `:.1%` अनुपात को प्रतिशत में दिखाता है।
|
|
87
|
+
छात्रों को मान बदलकर फिर से चलाने के लिए प्रोत्साहित करें — लाइव ब्लॉक का यही मक़सद है।
|
|
88
|
+
:::
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
:::meta
|
|
92
|
+
section: डेटा टाइप
|
|
93
|
+
id: four-types-cover-most-biology-data
|
|
94
|
+
:::
|
|
95
|
+
# चार टाइप से जीवविज्ञान का ज़्यादातर डेटा बन जाता है।
|
|
96
|
+
|
|
97
|
+
:::steps
|
|
98
|
+
- **`int`** — पूर्ण संख्याएँ: गिनती, स्थिति, इंडेक्स
|
|
99
|
+
- **`float`** — दशमलव संख्याएँ: सांद्रता, p-मान, फ़ोल्ड चेंज
|
|
100
|
+
- **`str`** — टेक्स्ट: जीन के नाम, सीक्वेंस, सैंपल ID
|
|
101
|
+
- **`bool`** — True या False: सार्थकता फ़्लैग, गुणवत्ता फ़िल्टर
|
|
102
|
+
:::
|
|
103
|
+
|
|
104
|
+
:::notes
|
|
105
|
+
एक बार में एक टाइप दिखाएँ और अगला दिखाने से पहले लैब से एक उदाहरण पूछें।
|
|
106
|
+
|
|
107
|
+
ज़ोर दें कि बूलियन आमतौर पर तुलना से आते हैं, सीधे लिखे True/False से नहीं। अगली स्लाइड पर पूरे कमरे से पूछा जाएगा।
|
|
108
|
+
:::
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
:::meta
|
|
112
|
+
section: डेटा टाइप
|
|
113
|
+
title: "पोल: p-मान का टाइप"
|
|
114
|
+
id: poll-the-type-of-a-p-value
|
|
115
|
+
:::
|
|
116
|
+
# p-मान किस टाइप का होता है?
|
|
117
|
+
|
|
118
|
+
<poll room="p-value" options="int|float|str|bool" answer="float" />
|
|
119
|
+
|
|
120
|
+
:::notes
|
|
121
|
+
आगे बढ़ने से पहले एक छोटी जाँच। फ़ोन QR कोड से जुड़ते हैं; डेक को `mdeck run slides.hi.md --network` से शुरू करें ताकि वे इस कंप्यूटर तक पहुँच सकें।
|
|
122
|
+
|
|
123
|
+
उत्तर: `float`; बार के नीचे का टिक बटन इसे चिह्नित करता है। अगर कई लोगों ने `bool` चुना, तो वे उससे निकाले गए *सार्थकता फ़्लैग* (`p < 0.05`) के बारे में सोच रहे हैं: यह दिखाने का अच्छा मौक़ा है कि तुलना से बूलियन बनते हैं।
|
|
124
|
+
:::
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
:::meta
|
|
128
|
+
layout: split
|
|
129
|
+
section: स्ट्रिंग
|
|
130
|
+
props:
|
|
131
|
+
ratio: [3, 2]
|
|
132
|
+
:::
|
|
133
|
+
# स्ट्रिंग सीक्वेंस की तरह व्यवहार करती हैं।
|
|
134
|
+
:::slot left
|
|
135
|
+
```python live editable copy
|
|
136
|
+
dna = "ATGCGTAAGCTTGAC"
|
|
137
|
+
|
|
138
|
+
print("Length: ", len(dna))
|
|
139
|
+
print("First codon:", dna[:3])
|
|
140
|
+
print("A count: ", dna.count("A"))
|
|
141
|
+
print("Reversed: ", dna[::-1])
|
|
142
|
+
print("As RNA: ", dna.replace("T", "U"))
|
|
143
|
+
```
|
|
144
|
+
:::
|
|
145
|
+
:::slot right
|
|
146
|
+
स्लाइसिंग, गिनना और बदलना किसी भी टेक्स्ट पर एक जैसा काम करता है।
|
|
147
|
+
|
|
148
|
+
**आज़माएँ:** `dna[-3:]` से आख़िरी कोडॉन प्रिंट करें।
|
|
149
|
+
:::
|
|
150
|
+
|
|
151
|
+
:::notes
|
|
152
|
+
स्ट्रिंग सीक्वेंस होती हैं — आप उन्हें न्यूक्लियोटाइड सीक्वेंस की तरह ही काट सकते हैं।
|
|
153
|
+
`replace("T", "U")` एक पंक्ति में ट्रांसक्रिप्शन है। ये अवधारणाएँ बाद में सीधे BioPython में काम आती हैं।
|
|
154
|
+
:::
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
:::meta
|
|
158
|
+
layout: chapter
|
|
159
|
+
number: 2
|
|
160
|
+
part: कलेक्शन
|
|
161
|
+
description: एक साथ कई मान रखना और उन तक पहुँचना।
|
|
162
|
+
:::
|
|
163
|
+
# लिस्ट और डिक्शनरी।
|
|
164
|
+
|
|
165
|
+
:::notes
|
|
166
|
+
ज़्यादातर असली डेटा एक अकेला मान नहीं होता। लिस्ट और डिक्शनरी दो सबसे काम के कलेक्शन हैं।
|
|
167
|
+
इस अध्याय का अनुमानित समय: 25 मिनट।
|
|
168
|
+
:::
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
:::meta
|
|
172
|
+
layout: split
|
|
173
|
+
section: लिस्ट
|
|
174
|
+
props:
|
|
175
|
+
ratio: [3, 2]
|
|
176
|
+
:::
|
|
177
|
+
# लिस्ट मानों को क्रम में रखती है।
|
|
178
|
+
:::slot left
|
|
179
|
+
```python live editable copy
|
|
180
|
+
conc = [0.12, 0.45, 0.33, 0.78, 0.21]
|
|
181
|
+
|
|
182
|
+
print("Samples:", len(conc))
|
|
183
|
+
print("First: ", conc[0])
|
|
184
|
+
print("Last: ", conc[-1])
|
|
185
|
+
print("Sorted: ", sorted(conc))
|
|
186
|
+
print("Mean: ", sum(conc) / len(conc))
|
|
187
|
+
```
|
|
188
|
+
:::
|
|
189
|
+
:::slot right
|
|
190
|
+
गिनती शून्य से शुरू होती है: `conc[0]` पहला आइटम है, `conc[-1]` आख़िरी।
|
|
191
|
+
|
|
192
|
+
**आज़माएँ:** छठा मान जोड़ें और देखें कि औसत कैसे बदलता है।
|
|
193
|
+
:::
|
|
194
|
+
|
|
195
|
+
:::notes
|
|
196
|
+
लिस्ट क्रम बनाए रखती है और दोहराए गए मान भी रख सकती है। इंडेक्स 0 से शुरू होता है। ऋणात्मक इंडेक्स अंत से गिनते हैं।
|
|
197
|
+
इनके लिए बढ़िया: टाइम सीरीज़, दोहराए गए माप, क्रम से होने वाले प्रोसेसिंग चरण।
|
|
198
|
+
|
|
199
|
+
आम ग़लती: छात्र पहले आइटम के लिए इंडेक्स 1 लेते हैं। 0 से इंडेक्सिंग शुरू में ही पक्की करवाएँ।
|
|
200
|
+
:::
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
:::meta
|
|
204
|
+
layout: split
|
|
205
|
+
section: डिक्शनरी
|
|
206
|
+
props:
|
|
207
|
+
ratio: [3, 2]
|
|
208
|
+
:::
|
|
209
|
+
# डिक्शनरी मानों को नाम से ढूँढती है।
|
|
210
|
+
:::slot left
|
|
211
|
+
```python live editable copy
|
|
212
|
+
sample = {
|
|
213
|
+
"id": "S042",
|
|
214
|
+
"organism": "Mus musculus",
|
|
215
|
+
"tissue": "liver",
|
|
216
|
+
"weight_mg": 312,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
print(sample["organism"])
|
|
220
|
+
sample["weight_mg"] = 318 # एक फ़ील्ड बदलें
|
|
221
|
+
print(sample)
|
|
222
|
+
```
|
|
223
|
+
:::
|
|
224
|
+
:::slot right
|
|
225
|
+
एक डिक्शनरी एक सैंपल का वर्णन करती है: कॉलम नंबर याद रखने की ज़रूरत नहीं।
|
|
226
|
+
|
|
227
|
+
**आज़माएँ:** `"treated": True` जोड़ें।
|
|
228
|
+
:::
|
|
229
|
+
|
|
230
|
+
:::notes
|
|
231
|
+
डिक्शनरी कुंजियों (keys) को मानों से जोड़ती है। संरचित रिकॉर्ड के लिए एकदम सही — एक डिक्शनरी = एक सैंपल।
|
|
232
|
+
बाद में डिक्शनरियों की एक लिस्ट Pandas DataFrame बन जाती है। यह बीज अभी बो दें।
|
|
233
|
+
:::
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
:::meta
|
|
237
|
+
layout: chapter
|
|
238
|
+
number: 3
|
|
239
|
+
part: कंट्रोल फ़्लो
|
|
240
|
+
description: फ़ैसले लेना और पूरे डेटा पर काम दोहराना।
|
|
241
|
+
:::
|
|
242
|
+
# फ़ैसले और लूप।
|
|
243
|
+
|
|
244
|
+
:::notes
|
|
245
|
+
ये दो संरचनाएँ — if और for — डेटा प्रोसेसिंग स्क्रिप्ट के ज़्यादातर तर्क को संभाल लेती हैं।
|
|
246
|
+
इस अध्याय का अनुमानित समय: 25 मिनट।
|
|
247
|
+
:::
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
:::meta
|
|
251
|
+
layout: split
|
|
252
|
+
section: शर्तें
|
|
253
|
+
props:
|
|
254
|
+
ratio: [3, 2]
|
|
255
|
+
:::
|
|
256
|
+
# `if` रास्ता चुनता है।
|
|
257
|
+
:::slot left
|
|
258
|
+
```python live editable copy
|
|
259
|
+
p_value = 0.023
|
|
260
|
+
fold_change = 2.4
|
|
261
|
+
|
|
262
|
+
if p_value < 0.05 and fold_change > 2:
|
|
263
|
+
print("Significant upregulation")
|
|
264
|
+
elif p_value < 0.05:
|
|
265
|
+
print("Significant, modest effect")
|
|
266
|
+
else:
|
|
267
|
+
print("Not significant")
|
|
268
|
+
```
|
|
269
|
+
:::
|
|
270
|
+
:::slot right
|
|
271
|
+
इंडेंटेशन ही ब्लॉक है: Python ब्रेसेज़ नहीं, ख़ाली जगह का इस्तेमाल करता है।
|
|
272
|
+
|
|
273
|
+
**आज़माएँ:** `fold_change = 1.5` कर दें।
|
|
274
|
+
:::
|
|
275
|
+
|
|
276
|
+
:::notes
|
|
277
|
+
if / elif / else से हर तरह की शाखा बनती है। Python में इंडेंटेशन शैली की बात नहीं — यह सिंटैक्स है।
|
|
278
|
+
दूसरी भाषाओं या R से आए छात्र यहाँ अटकते हैं। कोलन और इंडेंट अनिवार्य हैं।
|
|
279
|
+
दिखाएँ कि कोलन भूलने पर क्या होता है: Python SyntaxError देता है।
|
|
280
|
+
:::
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
:::meta
|
|
284
|
+
layout: split
|
|
285
|
+
section: लूप
|
|
286
|
+
props:
|
|
287
|
+
ratio: [3, 2]
|
|
288
|
+
:::
|
|
289
|
+
# `for` हर आइटम के लिए एक काम दोहराता है।
|
|
290
|
+
:::slot left
|
|
291
|
+
```python live editable copy
|
|
292
|
+
values = [1.2, 0.8, 2.1, 1.5, 0.9, 2.8]
|
|
293
|
+
|
|
294
|
+
above = []
|
|
295
|
+
for value in values:
|
|
296
|
+
if value > 1.0:
|
|
297
|
+
above.append(value)
|
|
298
|
+
|
|
299
|
+
print(len(above), "of", len(values))
|
|
300
|
+
print(above)
|
|
301
|
+
```
|
|
302
|
+
:::
|
|
303
|
+
:::slot right
|
|
304
|
+
लूप, जाँच, संग्रह: डेटा फ़िल्टर करने का मूल पैटर्न।
|
|
305
|
+
|
|
306
|
+
**आज़माएँ:** इसके बजाय 1.0 से छोटे मान गिनें।
|
|
307
|
+
:::
|
|
308
|
+
|
|
309
|
+
:::notes
|
|
310
|
+
for लूप किसी भी कलेक्शन पर चलते हैं। यह पैटर्न — लूप, जाँच, संग्रह — लगभग हर उस डेटा प्रोसेसिंग स्क्रिप्ट के केंद्र में है जो छात्र लिखेंगे।
|
|
311
|
+
|
|
312
|
+
**D** दबाएँ और स्लाइड पर एक मान को लूप में ट्रेस करें: `value` में, `if` से होकर, `above` में। Escape से ड्रॉइंग बंद होती है; ड्रॉइंग स्लाइड के साथ बनी रहती है।
|
|
313
|
+
:::
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
:::meta
|
|
317
|
+
layout: chapter
|
|
318
|
+
number: 4
|
|
319
|
+
part: फ़ंक्शन
|
|
320
|
+
description: तर्क को पैक करना ताकि आप उसे दोबारा इस्तेमाल और टेस्ट कर सकें।
|
|
321
|
+
:::
|
|
322
|
+
# फ़ंक्शन तर्क को दोबारा इस्तेमाल के लिए पैक करते हैं।
|
|
323
|
+
|
|
324
|
+
:::notes
|
|
325
|
+
फ़ंक्शन दोबारा इस्तेमाल होने लायक़ और टेस्ट करने लायक़ कोड की ओर पहला क़दम हैं।
|
|
326
|
+
प्रेरणा के लिए: "आप यही गणना 200 सैंपल पर चलाएँगे — इसे एक बार लिखें, 200 बार बुलाएँ।"
|
|
327
|
+
इस अध्याय का अनुमानित समय: 20 मिनट।
|
|
328
|
+
:::
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
:::meta
|
|
332
|
+
layout: split
|
|
333
|
+
section: फ़ंक्शन
|
|
334
|
+
props:
|
|
335
|
+
ratio: [3, 2]
|
|
336
|
+
:::
|
|
337
|
+
# फ़ंक्शन इनपुट को आउटपुट में बदलता है।
|
|
338
|
+
:::slot left
|
|
339
|
+
```python live editable copy
|
|
340
|
+
def gc_content(seq):
|
|
341
|
+
"""सीक्वेंस में GC का अनुपात लौटाता है।"""
|
|
342
|
+
gc = seq.count("G") + seq.count("C")
|
|
343
|
+
return gc / len(seq)
|
|
344
|
+
|
|
345
|
+
for seq in ["ATGCGC", "AATTAA", "GCGCGC"]:
|
|
346
|
+
print(seq, f"{gc_content(seq):.0%}")
|
|
347
|
+
```
|
|
348
|
+
:::
|
|
349
|
+
:::slot right
|
|
350
|
+
इसे एक बार लिखें, फिर हर सैंपल पर बुलाएँ।
|
|
351
|
+
|
|
352
|
+
**आज़माएँ:** लिस्ट में अपना सीक्वेंस जोड़ें।
|
|
353
|
+
:::
|
|
354
|
+
|
|
355
|
+
:::notes
|
|
356
|
+
def, पैरामीटर, return — पूरा सिंटैक्स बस इतना है।
|
|
357
|
+
समझाएँ: एक बार परिभाषित करें, अलग-अलग इनपुट के साथ कई बार बुलाएँ।
|
|
358
|
+
docstring (def के बाद तीन उद्धरण चिह्नों वाली स्ट्रिंग) अच्छी आदत है — इसे जल्दी सिखाएँ।
|
|
359
|
+
:::
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
:::meta
|
|
363
|
+
layout: chapter
|
|
364
|
+
number: 5
|
|
365
|
+
part: वैज्ञानिक Python
|
|
366
|
+
description: NumPy, Pandas और Matplotlib — तीन स्तंभ।
|
|
367
|
+
:::
|
|
368
|
+
# वैज्ञानिक स्टैक।
|
|
369
|
+
|
|
370
|
+
:::notes
|
|
371
|
+
यही तीन लाइब्रेरी Python को डेटा साइंस की सबसे प्रचलित भाषा बनाती हैं।
|
|
372
|
+
व्यवहार में छात्र ज़्यादा शुद्ध Python नहीं लिखेंगे — वे इन्हीं का इस्तेमाल करेंगे।
|
|
373
|
+
इस अध्याय का अनुमानित समय: 30 मिनट।
|
|
374
|
+
:::
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
:::meta
|
|
378
|
+
section: वैज्ञानिक Python
|
|
379
|
+
:::
|
|
380
|
+
# तीन लाइब्रेरी से लाइफ़ साइंसेज़ के ज़्यादातर काम हो जाते हैं।
|
|
381
|
+
|
|
382
|
+
:::steps
|
|
383
|
+
- **NumPy** — तेज़ ऐरे: पूरे डेटासेट पर एक साथ गणना
|
|
384
|
+
- **Pandas** — तालिकाएँ: स्प्रेडशीट डेटा लोड करना, फ़िल्टर करना, समूह बनाना और सारांश निकालना
|
|
385
|
+
- **Matplotlib** — प्लॉट, हर तत्व पर नियंत्रण के साथ
|
|
386
|
+
:::
|
|
387
|
+
|
|
388
|
+
:::notes
|
|
389
|
+
गहराई में जाने से पहले एक संक्षिप्त झलक। मुख्य संदेश: ये लाइब्रेरी भारी काम संभालती हैं,
|
|
390
|
+
इसलिए छात्र कम कोड लिखते हैं और शुद्ध Python लूप की तुलना में तेज़ और भरोसेमंद नतीजे पाते हैं।
|
|
391
|
+
:::
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
:::meta
|
|
395
|
+
layout: split
|
|
396
|
+
section: NumPy
|
|
397
|
+
props:
|
|
398
|
+
ratio: [3, 2]
|
|
399
|
+
:::
|
|
400
|
+
# NumPy पूरे ऐरे पर गणना करता है।
|
|
401
|
+
:::slot left
|
|
402
|
+
```python live copy
|
|
403
|
+
import numpy as np
|
|
404
|
+
|
|
405
|
+
m = np.array([1.2, 0.8, 2.1, 1.5, 0.9, 2.8])
|
|
406
|
+
|
|
407
|
+
print("Mean: ", m.mean().round(3))
|
|
408
|
+
print("Std: ", m.std().round(3))
|
|
409
|
+
print("Median:", np.median(m))
|
|
410
|
+
print("> 1.5: ", m[m > 1.5])
|
|
411
|
+
```
|
|
412
|
+
:::
|
|
413
|
+
:::slot right
|
|
414
|
+
`m[m > 1.5]` बिना लूप के फ़िल्टर करता है।
|
|
415
|
+
|
|
416
|
+
इसकी तुलना दो अध्याय पहले वाले `for` लूप से करें।
|
|
417
|
+
:::
|
|
418
|
+
|
|
419
|
+
:::notes
|
|
420
|
+
NumPy के ऑपरेशन पूरे ऐरे पर एक साथ लागू होते हैं — कोई लूप नहीं। यह तेज़ भी है और पढ़ने में आसान भी।
|
|
421
|
+
इस वेक्टराइज़्ड शैली की आदत पड़ने में समय लगता है, पर इसे जल्दी सीखना फ़ायदेमंद है।
|
|
422
|
+
डेमो: चलाने से पहले छात्रों से अनुमान लगवाएँ कि `m > 1.5` क्या लौटाता है।
|
|
423
|
+
:::
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
:::meta
|
|
427
|
+
layout: focus
|
|
428
|
+
eyebrow: मुख्य सीख
|
|
429
|
+
:::
|
|
430
|
+
# ऑपरेशन अकेले मानों पर नहीं, *ऐरे* पर लिखें।
|
|
431
|
+
|
|
432
|
+
:::notes
|
|
433
|
+
शुद्ध Python से वैज्ञानिक Python की ओर सोच में यह सबसे अहम बदलाव है।
|
|
434
|
+
इसे दोहराएँ, बैठने दें। जो छात्र इसे जल्दी आत्मसात कर लेते हैं, वे बाद में कहीं साफ़ कोड लिखते हैं।
|
|
435
|
+
:::
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
:::meta
|
|
439
|
+
layout: chapter
|
|
440
|
+
number: 6
|
|
441
|
+
part: आगे के क़दम
|
|
442
|
+
description: यहाँ से आगे कहाँ जाएँ।
|
|
443
|
+
id: keep-going
|
|
444
|
+
:::
|
|
445
|
+
# आगे बढ़ते रहें।
|
|
446
|
+
|
|
447
|
+
:::notes
|
|
448
|
+
एक रोडमैप के साथ समापन करें। परिचय के बाद छात्र अक्सर भटका हुआ महसूस करते हैं — उन्हें ठोस अगले क़दम दें
|
|
449
|
+
और सबसे अच्छे मुफ़्त संसाधनों की ओर इशारा करें।
|
|
450
|
+
:::
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
:::meta
|
|
454
|
+
section: आगे के क़दम
|
|
455
|
+
:::
|
|
456
|
+
# आगे क्या सीखें।
|
|
457
|
+
|
|
458
|
+
- **Pandas** — CSV लोड करना, पंक्तियाँ फ़िल्टर करना, समूहों के आँकड़े निकालना
|
|
459
|
+
- **Matplotlib और Seaborn** — स्कैटर प्लॉट, हीटमैप, वॉल्केनो प्लॉट
|
|
460
|
+
- **scikit-learn** — वर्गीकरण, क्लस्टरिंग, आयाम घटाना
|
|
461
|
+
- **BioPython** — सीक्वेंस, BLAST, GenBank और PDB फ़ाइलें
|
|
462
|
+
|
|
463
|
+
:::notes
|
|
464
|
+
स्व-अध्ययन के लिए: सिंटैक्स के लिए आधिकारिक Python ट्यूटोरियल, डेटा के काम के लिए Pandas का दस्तावेज़।
|
|
465
|
+
Kaggle के मुफ़्त Python और Pandas कोर्स बेहतरीन हैं और अपनी गति से किए जा सकते हैं।
|
|
466
|
+
ख़ास तौर पर लाइफ़ साइंसेज़ के लिए: BioPython का दस्तावेज़ और Python for Bioinformatics पाठ्यपुस्तक (Bassi)।
|
|
467
|
+
:::
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
:::meta
|
|
471
|
+
layout: focus
|
|
472
|
+
eyebrow: याद रखें
|
|
473
|
+
:::
|
|
474
|
+
# कोड लिखना सीखने का सबसे अच्छा तरीक़ा है *कोड लिखना*।
|
|
475
|
+
|
|
476
|
+
:::notes
|
|
477
|
+
इसी पर समापन करें। प्रोग्रामिंग एक कौशल है — इसमें अभ्यास लगता है। पहले कुछ हफ़्ते धीमे लगते हैं,
|
|
478
|
+
फिर बात बैठ जाती है। रोज़ अभ्यास के लिए प्रोत्साहित करें, चाहे छोटी स्क्रिप्ट ही क्यों न हों। अभ्यास-प्रश्नों की ओर इशारा करें।
|
|
479
|
+
:::
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
:::meta
|
|
483
|
+
layout: title
|
|
484
|
+
:::
|
|
485
|
+
# सवाल?
|
|
486
|
+
## धन्यवाद — अब अपने लैपटॉप खोलिए।
|
|
487
|
+
|
|
488
|
+
:::notes
|
|
489
|
+
सवालों के लिए समय छोड़ें।
|
|
490
|
+
छात्रों को याद दिलाएँ कि कोर्स सामग्री कहाँ मिलेगी और आपसे कैसे संपर्क करें।
|
|
491
|
+
अगले सत्र में Pandas और असली डेटासेट लोड करना शामिल होगा।
|
|
492
|
+
:::
|
|
@@ -350,6 +350,39 @@ The underline and the circle on this slide were drawn with the pen and saved; th
|
|
|
350
350
|
On an iPad, pair it from the launch page: you draw and steer there while the laptop drives the projector.
|
|
351
351
|
:::
|
|
352
352
|
|
|
353
|
+
---
|
|
354
|
+
:::meta
|
|
355
|
+
layout: split
|
|
356
|
+
hotspots: auto
|
|
357
|
+
id: zoom-in-on-a-part
|
|
358
|
+
:::
|
|
359
|
+
# Zoom in on a part
|
|
360
|
+
:::slot left
|
|
361
|
+
```markdown
|
|
362
|
+
:::meta
|
|
363
|
+
hotspots: auto
|
|
364
|
+
:::
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
In the presenter view, press a **number** to zoom into that part. The same number, **0** or **Escape** goes back.
|
|
368
|
+
:::
|
|
369
|
+
:::slot right
|
|
370
|
+
| Layer | Who owns it |
|
|
371
|
+
|---|---|
|
|
372
|
+
| Words | You |
|
|
373
|
+
| Look | The theme |
|
|
374
|
+
|
|
375
|
+
$$
|
|
376
|
+
A = \pi r^2
|
|
377
|
+
$$
|
|
378
|
+
:::
|
|
379
|
+
|
|
380
|
+
:::notes
|
|
381
|
+
The numbered keys show on this slide only here in the presenter view, never on the projector. Press 2 for the table, 3 for the formula; the audience window flies along. The arrow keys leave the zoom and move on in one press.
|
|
382
|
+
|
|
383
|
+
`hotspots: auto` finds pictures, code, tables, formulas and callouts. Put it in the deck's settings at the top to have keys on every slide, or write `marked` to give keys only to parts marked `data-hotspot`. H switches the keys off and on during the talk.
|
|
384
|
+
:::
|
|
385
|
+
|
|
353
386
|
---
|
|
354
387
|
# Which part will you try first?
|
|
355
388
|
|
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@ meta:
|
|
|
44
44
|
date: "2026-09-16"
|
|
45
45
|
bibliography: refs.bib # optional: .bib, CSL-JSON or Hayagriva; cite [@key, p. 3] or @key, list with <bibliography />
|
|
46
46
|
csl: apa # optional: apa, vancouver, harvard1 or a .csl file
|
|
47
|
+
hotspots: auto # optional: number keys zoom in on pictures, code and tables while presenting; marked: only data-hotspot parts
|
|
47
48
|
---
|
|
48
49
|
|
|
49
50
|
---
|
package/src/build/config.js
CHANGED
|
@@ -38,7 +38,7 @@ export function baseConfig(slidesPath, { selfContained = false, defaultView = 'd
|
|
|
38
38
|
return {
|
|
39
39
|
configFile: false,
|
|
40
40
|
root: runtimeRoot,
|
|
41
|
-
plugins: [preact(), slidesPlugin(abs, source === undefined ? {} : { source })],
|
|
41
|
+
plugins: [preact(), slidesPlugin(abs, source === undefined ? { languages: true } : { source })],
|
|
42
42
|
// Deck-local components live outside the framework root and have no
|
|
43
43
|
// node_modules of their own, so their preact imports must resolve back to
|
|
44
44
|
// the framework's copy — and to the *same* instance, or hooks break.
|
|
@@ -15,6 +15,7 @@ import { assertDrawings } from './drawings.js'
|
|
|
15
15
|
import { marked } from 'marked'
|
|
16
16
|
import { scanTags, replaceTagsIn, withAttribute } from '../core/tags.js'
|
|
17
17
|
import { resolveBibliography, sampleBibliography } from './bibliography.js'
|
|
18
|
+
import { languageOfFile, languageFiles } from '../core/languages.js'
|
|
18
19
|
|
|
19
20
|
const VIRTUAL_ID = 'virtual:slides'
|
|
20
21
|
const RESOLVED_ID = '\0virtual:slides'
|
|
@@ -217,12 +218,19 @@ export function deckLookOnly(registry, deckConfig = {}) {
|
|
|
217
218
|
return { ...registry, themes: { [theme.id]: theme }, palettes: palette ? { [palette.id]: registry.palettes[palette.id] } : {} }
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
// The same deck in its other languages (talk.en.md beside talk.de.md), for
|
|
222
|
+
// the presenter view's language switch: `[{ lang, file }]`, or none.
|
|
223
|
+
export function deckLanguages(abs) {
|
|
224
|
+
try { return languageFiles(basename(abs), readdirSync(dirname(abs))).map(({ lang, file }) => ({ lang, file: resolve(dirname(abs), file) })) } catch { return [] }
|
|
225
|
+
}
|
|
226
|
+
|
|
220
227
|
// `editor` keeps the page alive while `mdeck edit` rewrites the deck: deck
|
|
221
228
|
// changes only invalidate the module, validation errors are warnings, and
|
|
222
229
|
// extension changes are announced instead of forcing a reload. `source` is
|
|
223
230
|
// the deck's text where there is no file (`mdeck design`, on the sample deck);
|
|
224
|
-
// `slidesPath` then only says where its folder is.
|
|
225
|
-
|
|
231
|
+
// `slidesPath` then only says where its folder is. `languages` also brings
|
|
232
|
+
// the deck's other languages along, for `mdeck run`; a built file has one.
|
|
233
|
+
export function slidesPlugin(slidesPath, { languages = false, inlineImages = false, inlineMedia = false, editor = false, stripNotes = false, stripActivities = false, ink = true, results = true, embedFonts: embedThemeFonts = false, deckLook = false, source: given = null } = {}) {
|
|
226
234
|
const abs = resolve(slidesPath)
|
|
227
235
|
const readDeck = () => given ?? readFileSync(abs, 'utf-8')
|
|
228
236
|
const watchDeck = context => { if (given === null) context.addWatchFile(abs) }
|
|
@@ -231,6 +239,10 @@ export function slidesPlugin(slidesPath, { inlineImages = false, inlineMedia = f
|
|
|
231
239
|
return stripActivities ? stripActivitiesFrom(shared).source : shared
|
|
232
240
|
}
|
|
233
241
|
const inkPath = inkFileFor(abs)
|
|
242
|
+
// The language the file name gives, for a deck that sets no `lang`.
|
|
243
|
+
const fileLanguage = given === null ? languageOfFile(basename(abs))?.lang ?? null : null
|
|
244
|
+
const otherLanguages = () => languages && given === null ? deckLanguages(abs).filter(({ file }) => file !== abs) : []
|
|
245
|
+
let languageWatch = []
|
|
234
246
|
const resultsPath = resultsFileFor(abs)
|
|
235
247
|
const extensionDirs = extensionRoots(abs).map(root => root.dir)
|
|
236
248
|
let componentDirs = componentFolders(abs, readDeck()).map(folder => folder.dir)
|
|
@@ -306,7 +318,20 @@ export function slidesPlugin(slidesPath, { inlineImages = false, inlineMedia = f
|
|
|
306
318
|
if (errors.length && !editor) throw new Error(formatDiagnostics(errors, abs))
|
|
307
319
|
if (diagnostics.length) this.warn(formatDiagnostics(diagnostics, abs))
|
|
308
320
|
const source = maybeInlineAssets(sourceFor(raw), abs, { inlineImages, inlineMedia })
|
|
309
|
-
|
|
321
|
+
// The other languages are read as they are: `mdeck check` checks each file.
|
|
322
|
+
const others = otherLanguages()
|
|
323
|
+
languageWatch = others.map(({ file }) => file)
|
|
324
|
+
for (const file of languageWatch) this.addWatchFile(file)
|
|
325
|
+
for (const { file } of others) {
|
|
326
|
+
const found = validateDeck(parseSlides(readFileSync(file, 'utf-8')), {
|
|
327
|
+
layouts: manifestsOf(registry, 'layout'), themes: manifestsOf(registry, 'theme'), palettes: manifestsOf(registry, 'palette'),
|
|
328
|
+
})
|
|
329
|
+
if (found.length) this.warn(formatDiagnostics(found, file))
|
|
330
|
+
}
|
|
331
|
+
const translations = others.length && fileLanguage
|
|
332
|
+
? { [fileLanguage]: source, ...Object.fromEntries(others.map(({ lang, file }) => [lang, maybeInlineAssets(sourceFor(readFileSync(file, 'utf-8')), file, { inlineImages, inlineMedia })])) }
|
|
333
|
+
: {}
|
|
334
|
+
return `export default ${JSON.stringify(source)}\nexport const language = ${JSON.stringify(fileLanguage)}\nexport const languages = ${JSON.stringify(translations)}`
|
|
310
335
|
}
|
|
311
336
|
// The deck's saved ink (<deck>.drawings.json), scaled to its design size.
|
|
312
337
|
// Builds bundle it, so the reader view and PDFs show it too.
|
|
@@ -385,7 +410,7 @@ export function slidesPlugin(slidesPath, { inlineImages = false, inlineMedia = f
|
|
|
385
410
|
if (!/(^|[\/])index\.html$/.test(context.filename ?? context.path ?? '')) return html
|
|
386
411
|
let config = {}
|
|
387
412
|
try { config = parseSlides(readDeck()).deckConfig ?? {} } catch {}
|
|
388
|
-
const lang = config.lang
|
|
413
|
+
const lang = config.lang ?? fileLanguage
|
|
389
414
|
if (typeof lang === 'string' && /^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(lang)) html = html.replace(/<html lang="[^"]*"/, `<html lang="${lang}"`)
|
|
390
415
|
if (embedThemeFonts) {
|
|
391
416
|
const theme = loadRegistry(abs).themes[config.theme ?? 'neue']?.manifest
|
|
@@ -414,6 +439,11 @@ export function slidesPlugin(slidesPath, { inlineImages = false, inlineMedia = f
|
|
|
414
439
|
reload(server, [RESOLVED_INK_ID], changed)
|
|
415
440
|
return []
|
|
416
441
|
}
|
|
442
|
+
if (languageWatch.includes(changed)) {
|
|
443
|
+
invalidate(server, [RESOLVED_ID])
|
|
444
|
+
if (!editor) server.ws.send({ type: 'full-reload' })
|
|
445
|
+
return []
|
|
446
|
+
}
|
|
417
447
|
if (bibFiles.includes(changed)) {
|
|
418
448
|
invalidate(server, [RESOLVED_BIB_ID])
|
|
419
449
|
if (editor) return bibUpdate(server)
|
package/src/cli/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { renderPdf, attachPdf, findChrome } from '../build/pdf.js'
|
|
|
22
22
|
import { installSkill, readSkill, TARGETS } from './skill.js'
|
|
23
23
|
import { runPackages } from './themes.js'
|
|
24
24
|
import { ManifestError, KINDS } from '../extensions/manifest.js'
|
|
25
|
+
import { paramKind } from '../extensions/params.js'
|
|
25
26
|
|
|
26
27
|
import { frameworkRoot } from '../paths.js'
|
|
27
28
|
import { parseSlides } from '../core/parseSlides.js'
|
|
@@ -523,6 +524,9 @@ if (command === 'new') {
|
|
|
523
524
|
console.log(` ${c.cyan}${record.id}${c.reset} — ${record.title} ${c.dim}(${record.source})${record.description ? ' ' + record.description : ''}${c.reset}`)
|
|
524
525
|
// What a theme suits, from its guide; --json has the whole guide.
|
|
525
526
|
if (record.manifest?.guide?.suits) console.log(` ${c.dim}Suits: ${record.manifest.guide.suits.join(' · ')}${c.reset}`)
|
|
527
|
+
// The settings a deck may change under params:, with the kind of value each takes.
|
|
528
|
+
const params = Object.entries(record.manifest?.params ?? {})
|
|
529
|
+
if (params.length) console.log(` ${c.dim}Settings: ${params.map(([name, param]) => name + paramKind(param)).join(' · ')}${c.reset}`)
|
|
526
530
|
}
|
|
527
531
|
}
|
|
528
532
|
|
package/src/cli/themes.js
CHANGED
|
@@ -34,6 +34,8 @@ function looksOf(registry, catalogue) {
|
|
|
34
34
|
// The oldest mdeck it works with, for the gallery's install note.
|
|
35
35
|
...(registry.themes[theme.id]?.package?.mdeck ? { mdeck: registry.themes[theme.id].package.mdeck } : {}),
|
|
36
36
|
palette: theme.palette, appearance: theme.appearance ?? 'light', palettes: palettesFor(theme, palettes).map(p => p.id), ...(theme.guide ? { guide: theme.guide } : {}),
|
|
37
|
+
// Its settings, for the gallery's controls: token, default, kind and range.
|
|
38
|
+
...(Object.keys(theme.params ?? {}).length ? { params: theme.params } : {}),
|
|
37
39
|
...(icons.get(theme.id) ? { icon: icons.get(theme.id) } : {}) })),
|
|
38
40
|
palettes: Object.values(palettes).map(palette => ({ id: palette.id, title: palette.title, description: palette.description ?? '', builtIn: builtIn.has(palette.id + ':p'),
|
|
39
41
|
...(palette.theme ? { theme: palette.theme } : {}), light: colours(palette.light), dark: colours(palette.dark) })),
|