mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Show study slides as one column and fill in the empty lessons.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
10980f24b5
commit
3ffe01af1d
4 changed files with 175 additions and 91 deletions
|
|
@ -1034,8 +1034,30 @@ fun SlidesScreen(
|
|||
}
|
||||
}
|
||||
}
|
||||
slide.strs("lines").forEach { line ->
|
||||
Text(line, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(vertical = 6.dp))
|
||||
slide.strs("lines").forEachIndexed { index, line ->
|
||||
val numbered = Regex("""^(\d+)\.\s+(.*)$""").find(line)
|
||||
val badge = numbered?.groupValues?.get(1) ?: "${index + 1}"
|
||||
val text = numbered?.groupValues?.get(2) ?: line
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f), androidx.compose.foundation.shape.RoundedCornerShape(16.dp))
|
||||
.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(top = 2.dp)
|
||||
.size(28.dp)
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), CircleShape),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(badge, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(text, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(24.dp))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1519,84 +1519,129 @@ struct SlidesScreen: View {
|
|||
}
|
||||
|
||||
private func slidePage(_ slide: J) -> some View {
|
||||
#if os(macOS)
|
||||
GeometryReader { geo in
|
||||
ScrollView {
|
||||
slideStack(slide)
|
||||
.padding(.horizontal, MacChrome.pageInset)
|
||||
.padding(.vertical, 28)
|
||||
.frame(maxWidth: .infinity, minHeight: geo.size.height, alignment: .center)
|
||||
}
|
||||
}
|
||||
#else
|
||||
ScrollView {
|
||||
slideStack(slide)
|
||||
.padding()
|
||||
.padding(.bottom, 24)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, MacChrome.pageInset)
|
||||
.padding(.top, 22)
|
||||
.padding(.bottom, 28)
|
||||
.frame(maxWidth: 860)
|
||||
.frame(maxWidth: .infinity, alignment: .top)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func slideStack(_ slide: J) -> some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
Text(slide.str("kicker"))
|
||||
#if os(macOS)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
#else
|
||||
.font(.caption.weight(.semibold))
|
||||
#endif
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.accentColor)
|
||||
Text(slide.str("title"))
|
||||
#if os(macOS)
|
||||
.font(.title.bold())
|
||||
#else
|
||||
.font(.title2.bold())
|
||||
#endif
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
if let tip = slide.strOrNull("tip") {
|
||||
GlassCard {
|
||||
Label(tip, systemImage: "lightbulb.fill")
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
Image(systemName: "lightbulb.fill")
|
||||
.foregroundStyle(Color.accentColor)
|
||||
.padding(.top, 2)
|
||||
Text(tip)
|
||||
#if os(macOS)
|
||||
.font(.body)
|
||||
#else
|
||||
.font(.subheadline)
|
||||
#endif
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.padding(16)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color.accentColor.opacity(0.12), in: RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||
}
|
||||
slideLines(slide.strs("lines"))
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func slideLines(_ lines: [String]) -> some View {
|
||||
#if os(macOS)
|
||||
if lines.count >= 3 {
|
||||
LazyVGrid(
|
||||
columns: [
|
||||
GridItem(.flexible(), spacing: 28, alignment: .topLeading),
|
||||
GridItem(.flexible(), spacing: 28, alignment: .topLeading),
|
||||
],
|
||||
alignment: .leading,
|
||||
spacing: 12
|
||||
) {
|
||||
ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
|
||||
Text(line)
|
||||
let points = studyPoints(lines)
|
||||
return VStack(alignment: .leading, spacing: 10) {
|
||||
ForEach(Array(points.enumerated()), id: \.offset) { _, point in
|
||||
HStack(alignment: .top, spacing: 14) {
|
||||
Text(point.0)
|
||||
.font(.callout.weight(.bold))
|
||||
.foregroundStyle(Color.accentColor)
|
||||
.frame(minWidth: 28, minHeight: 28)
|
||||
.background(Color.accentColor.opacity(0.14), in: Circle())
|
||||
Text(point.1)
|
||||
#if os(macOS)
|
||||
.font(.title3)
|
||||
#else
|
||||
.font(.body)
|
||||
#endif
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.top, 3)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
|
||||
Text(line)
|
||||
.font(.title3)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 14)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
.fill(Color.primary.opacity(0.045))
|
||||
}
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
.strokeBorder(Color.primary.opacity(0.08), lineWidth: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
ForEach(Array(lines.enumerated()), id: \.offset) { _, line in
|
||||
Text(line)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
/// Lines that end in the middle of a sentence stay one point. A leading
|
||||
/// "1. " is the point number, not part of the sentence.
|
||||
private func studyPoints(_ lines: [String]) -> [(String, String)] {
|
||||
var chunks: [String] = []
|
||||
var buffer = ""
|
||||
func flush() {
|
||||
let text = buffer.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !text.isEmpty { chunks.append(text) }
|
||||
buffer = ""
|
||||
}
|
||||
#endif
|
||||
for line in lines {
|
||||
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if trimmed.isEmpty { continue }
|
||||
if buffer.isEmpty {
|
||||
buffer = trimmed
|
||||
} else if continuesStudyPoint(buffer) {
|
||||
buffer += " " + trimmed
|
||||
} else {
|
||||
flush()
|
||||
buffer = trimmed
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return chunks.enumerated().map { index, chunk in
|
||||
let pattern = #"^(\d+)\.\s+(.*)$"#
|
||||
if let regex = try? NSRegularExpression(pattern: pattern),
|
||||
let match = regex.firstMatch(in: chunk, range: NSRange(chunk.startIndex..., in: chunk)),
|
||||
let number = Range(match.range(at: 1), in: chunk),
|
||||
let rest = Range(match.range(at: 2), in: chunk),
|
||||
!continuesStudyPoint(String(chunk[rest])) {
|
||||
return (String(chunk[number]), String(chunk[rest]))
|
||||
}
|
||||
return (String(index + 1), chunk)
|
||||
}
|
||||
}
|
||||
|
||||
private func continuesStudyPoint(_ text: String) -> Bool {
|
||||
guard let last = text.last else { return false }
|
||||
return last == "," || last == ":" || last == ";" || last == "–" || last == "-" || last == "("
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
69
src/hw.c
69
src/hw.c
|
|
@ -877,64 +877,79 @@ GtkWidget *build_hw_unit2_page(void) {
|
|||
static const NetSlide slides[HW2_SLIDES] = {
|
||||
{
|
||||
"1 / 6 • Přehled", "Historie počítačů",
|
||||
"Generace se dělí podle klíčové technologie své doby.",
|
||||
"Generace se dělí podle součástky, která v té době počítala: relé, elektronka, tranzistor, integrovaný obvod a nakonec mikroprocesor.",
|
||||
{
|
||||
"1. Předchůdci – do 30. let 19. století",
|
||||
"2. Nultá generace – 1938–1944",
|
||||
"3. První generace – 1944–1955",
|
||||
"4. Druhá generace – 1955–1964",
|
||||
"5. Třetí generace – 1964–1971",
|
||||
"Další snímky shrnují znaky jednotlivých generací.",
|
||||
"Předchůdci do 19. století jsou mechanické pomůcky. Pascaline sčítala ozubenými koly, Babbage navrhl analytický stroj a Ada Lovelace k němu popsala postup výpočtu. Elektronika ještě chyběla.",
|
||||
"Nultá generace, zhruba 1938–1944, spíná elektromagnetická relé. Stroje jsou kusové a často vojenské.",
|
||||
"První generace, 1944–1955, bere elektronku. Počítač zabere sál a program se dává děrnými štítky.",
|
||||
"Druhá generace, 1955–1964, nahrazuje elektronku tranzistorem. Objevují se vyšší programovací jazyky.",
|
||||
"Třetí generace, 1964–1971, staví na integrovaném obvodu. Vznikají rodiny počítačů a operační systémy.",
|
||||
"Čtvrtá generace od roku 1971 staví na mikroprocesoru. Počítač se vejde na stůl a později do kapsy.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"2 / 6 • 0. generace", "Počítače 0. generace (1938–1944)",
|
||||
"Tip: počítačů bylo málo a často sloužily armádě.",
|
||||
"Tip: počítačů byly jednotky kusů a často sloužily armádě. Základem je relé, ne elektronka.",
|
||||
{
|
||||
"Existovalo jen několik počítačů na světě.",
|
||||
"Většinou měly vojenský účel.",
|
||||
"Relé je elektromagnetický spínač. Je pomalejší a hlučnější než pozdější elektronka, ale umí už skládat logické obvody.",
|
||||
"Konrad Zuse dokončil v roce 1941 Z3: programovatelný binární stroj s plovoucí řádovou čárkou.",
|
||||
"Harvard Mark I (1944) byl elektromechanický a počítal balistické tabulky pro americkou armádu.",
|
||||
"Každý stroj se stavěl na zakázku. Obsluhoval ho tým, ne jeden člověk u stolu.",
|
||||
"Program býval na děrné pásce nebo na přepínačích, ne v dnešní operační paměti.",
|
||||
"Typická práce byla vojenská: šifry a tabulky střelby, ne kancelář nebo domácnost.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"3 / 6 • 1. generace", "Počítače 1. generace (1944–1955)",
|
||||
"Klíčový objev: elektronka",
|
||||
"Klíčový objev je elektronka. Spíná rychleji než relé, ale hřeje, často se pálí a počítač kvůli ní zabere celý sál.",
|
||||
{
|
||||
"Objev elektronky umožnil tuto generaci.",
|
||||
"Ještě neexistoval software v dnešním smyslu.",
|
||||
"Data a programy se zadávaly pomocí děrných štítků.",
|
||||
"ENIAC (1945) měl kolem 18 tisíc elektronek a počítal hlavně balistiku.",
|
||||
"Von Neumannova architektura ukládá program i data do stejné paměti. Na tom stojí i dnešní počítače.",
|
||||
"UNIVAC I (1951) byl první sériově prodávaný komerční počítač v USA.",
|
||||
"Program a data se děrovaly do štítků. Operátor vložil balík a čekal na výsledek.",
|
||||
"Operační systém ani vyšší jazyk ještě nebyl. Psalo se ve strojovém kódu.",
|
||||
"Sál potřeboval klimatizaci a stálý dohled. Do domácnosti se takový stroj nevešel.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"4 / 6 • 2. generace", "Počítače 2. generace (1955–1964)",
|
||||
"Klíčový objev: tranzistor",
|
||||
"Klíčový objev je tranzistor. V Bellových laboratořích vznikl roku 1947 a do počítačů se dostal zhruba od roku 1955.",
|
||||
{
|
||||
"Objev tranzistoru nahradil elektronky.",
|
||||
"Používala se hlavní externí paměť.",
|
||||
"Počítače měly menší rozměry než v 1. generaci.",
|
||||
"Tranzistor nahradí elektronku: je menší, spolehlivější a méně se hřeje.",
|
||||
"Stroj už nemusí zabírat celý sál, pořád je to ale zařízení pro firmu nebo ústav.",
|
||||
"Vnitřní paměť bývá feritová. Vnější paměť jsou magnetické pásky a bubny.",
|
||||
"FORTRAN (1957) a COBOL (1959) dovolí psát program blíž lidské řeči než strojový kód.",
|
||||
"Úlohy se zpracovávají dávkově. Operátor seřadí balíky a počítač je jede postupně.",
|
||||
"Typické stroje této doby jsou IBM 1401 a IBM 7090.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"5 / 6 • 3. generace", "Počítače 3. generace (1964–1971)",
|
||||
"Klíčový objev: integrovaný obvod",
|
||||
"Klíčový objev je integrovaný obvod: mnoho tranzistorů na jednom křemíkovém čipu. Kilby a Noyce ho představili na konci 50. let.",
|
||||
{
|
||||
"Objev integrovaného obvodu.",
|
||||
"Stále se používala hlavní externí paměť.",
|
||||
"Rozměry počítačů se dál zmenšovaly.",
|
||||
"IBM System/360 (1964) je rodina počítačů se stejným instrukčním souborem. Program z menšího modelu běží i na větším.",
|
||||
"Operační systém už řídí více úloh a periferie. Objevuje se sdílení času mezi uživateli.",
|
||||
"Magnetický disk doplňuje pásku a zkracuje čekání na data.",
|
||||
"Minipočítače jako PDP-8 se vejdou do jedné skříně. Kupují je laboratoře a firmy, ne jen stát.",
|
||||
"Stroj je menší a levnější než sál první generace, pořád to ale není osobní počítač na stůl.",
|
||||
"Hlavní vnější pamětí zůstává magnetické médium, ne děrný štítek.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"6 / 6 • 4. generace", "Počítače 4. generace",
|
||||
"Klíčové: programovatelné mikroprocesory",
|
||||
"Klíčová součástka je programovatelný mikroprocesor: celý procesor na jednom čipu.",
|
||||
{
|
||||
"Nastupují programovatelné mikroprocesory.",
|
||||
"1972 – první počítačová hra",
|
||||
"1973 – první disketa",
|
||||
"1979 – první CD",
|
||||
"Intel 4004 z roku 1971 je první komerční mikroprocesor. Pozdější čipy z něj udělaly mozek osobního počítače.",
|
||||
"Na stůl se počítač dostává postupně: Altair 1975, Apple II 1977 a IBM PC 1981.",
|
||||
"Roku 1972 vychází Pong, první komerčně úspěšná arkádová hra. Lekce ji bere jako první počítačovou hru.",
|
||||
"Roku 1971 představuje IBM osmipalcovou disketu. Menší formáty přišly až v dalších letech.",
|
||||
"Roku 1979 Philips a Sony předvádějí kompaktní disk. V obchodech je od roku 1982.",
|
||||
"Paměti a disky rostou, takže počítač opouští sál a později se vejde i do kapsy.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
38
src/net.c
38
src/net.c
|
|
@ -1029,43 +1029,45 @@ GtkWidget *build_net_unit5_page(void) {
|
|||
static const NetSlide slides[NET5_SLIDES] = {
|
||||
{
|
||||
"1 / 4 • Signál", "Analogový a digitální signál",
|
||||
"Tip: f = 1/T",
|
||||
"Tip: frekvence a perioda jsou převrácené hodnoty. Platí f = 1/T.",
|
||||
{
|
||||
"Analogový: spojitý, nabývá libovolných hodnot.",
|
||||
"Digitální: diskrétní, hodnoty 0 a 1.",
|
||||
"Parametry signálu:",
|
||||
"perioda T, frekvence f = 1/T,",
|
||||
"amplituda A, fázový posun Φ.",
|
||||
"Analogový signál je spojitý. Mezi dvěma hodnotami nabývá i všech hodnot mezi nimi, třeba napětí na mikrofonu nebo teplota.",
|
||||
"Digitální signál je diskrétní. V počítači nabývá jen hodnot 0 a 1, takže se dá uložit a zkopírovat bez postupného šumu.",
|
||||
"Perioda T je doba jednoho opakování. Frekvence f = 1/T říká, kolikrát za sekundu se průběh zopakuje. Jednotka je hertz.",
|
||||
"Amplituda A je velikost výchylky od středu. Fázový posun Φ říká, o kolik je průběh posunutý proti jinému signálu se stejnou frekvencí.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"2 / 4 • Modulace", "Základní typy modulace",
|
||||
"Modulace = přenos v přeloženém pásmu",
|
||||
"Modulace nasadí zprávu na nosnou vlnu, aby prošla kabelem nebo éterem. Tomu se říká přenos v přeloženém pásmu.",
|
||||
{
|
||||
"AM – amplitudová modulace.",
|
||||
"FM – frekvenční modulace.",
|
||||
"PM – fázová modulace.",
|
||||
"AM, amplitudová modulace, mění velikost nosné podle zprávy. Používá ji třeba rozhlas na středních vlnách. Rušení amplitudu snadno pokazí.",
|
||||
"FM, frekvenční modulace, mění kmitočet nosné a amplitudu nechává. VKV rádio je odolnější proti šumu než AM.",
|
||||
"PM, fázová modulace, posouvá fázi nosné. Změna fáze a změna frekvence spolu souvisejí, proto se FM a PM často popisují podobně.",
|
||||
"Bez modulace by pomalá zpráva, třeba hlas, na rádiové frekvenci sama neodešla. Nosná je rychlá vlna, zpráva ji jen tvaruje.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"3 / 4 • Kombinované", "QPSK a 256-QAM",
|
||||
NULL,
|
||||
"Jeden stav signálu, symbol, může nést víc než jeden bit. Čím víc stavů, tím víc bitů, ale tím snáz se stavy zamění.",
|
||||
{
|
||||
"QPSK: 2 bity v jednom prvku (symbolu).",
|
||||
"256-QAM: 8 bitů v jednom prvku.",
|
||||
"QPSK má čtyři fázové stavy. Čtyři možnosti jsou dva bity, takže jeden symbol nese 2 bity.",
|
||||
"256-QAM skládá amplitudu i fázi do 256 stavů. 256 = 2^8, takže jeden symbol nese 8 bitů.",
|
||||
"Wi-Fi a kabelové modemy QAM používají, protože za stejnou šířku pásma přenesou víc dat než prosté AM.",
|
||||
"Hustší konstelace chce čistší signál. Když je rušení velké, přijímač spadne na řidší modulaci s menším počtem bitů na symbol.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
{
|
||||
"4 / 4 • Rychlosti", "Modulační, přenosová a šířka pásma",
|
||||
NULL,
|
||||
"Baud a bit za sekundu nejsou totéž. Baud počítá změny signálu, bit za sekundu počítá přenesenou informaci.",
|
||||
{
|
||||
"Modulační rychlost: počet změn signálu za sekundu [Baud, Bd/s].",
|
||||
"Přenosová rychlost: velikost přenesené informace [bit/s, bps].",
|
||||
"Šířka pásma: rozsah frekvencí –",
|
||||
"určuje maximální možnou rychlost.",
|
||||
"Modulační rychlost je počet změn signálu za sekundu. Jednotka je baud, Bd/s. U QPSK jedna změna nese 2 bity, takže bitů je víc než baudů.",
|
||||
"Přenosová rychlost je množství informace za sekundu, v bit/s. Když symbol nese víc bitů, přenosová rychlost je vyšší než modulační.",
|
||||
"Šířka pásma je rozsah frekvencí, který kanál propustí. U vozovky je to počet pruhů: užší pásmo znamená nižší strop rychlosti.",
|
||||
"Nyquistův a Shannonův vztah z šířky pásma a šumu odvozují, kolik bitů za sekundu kanál vůbec unese. Větší pásmo nebo čistší signál strop zvedne.",
|
||||
NULL,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue