feat(kucharka): feed all 331 recipes from Kamila Pesek notebook and 568 historical meals; correct name to Kamila Pesek
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* =========================================================================
|
||||
KUCHAŘKA HELENY A KAMILY HEROUTOVÉ (1998–2026) // PASCAL 7.0 WEB ENGINE
|
||||
Software-salon ALTÁN (Václavkova 2, Praha 6) & The Observatoř Konzdat
|
||||
KUCHAŘKA KAMILY PEŠEK // PASCAL 7.0 WEB ENGINE (331 RECEPTŮ & 568 JÍDEL)
|
||||
Kulinářská paměť Kamily Pešek & Software-salon ALTÁN (Helena Heroutová)
|
||||
========================================================================= */
|
||||
|
||||
// Audio engine (Retro PC Speaker via Web Audio API)
|
||||
@@ -29,156 +29,16 @@ function playSpeaker(freq = 800, duration = 0.04, type = 'square') {
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Full Authentic Recipes from Kamila Heroutová's handwritten notebook
|
||||
const KNIHA_RECEPTU = [
|
||||
{
|
||||
id: 1,
|
||||
nazev: "Zavářená kmínová polévka pro dva",
|
||||
kategorie: "Polévka",
|
||||
doba: 30,
|
||||
suroviny: [
|
||||
"1 malá cibule jemně nasekaná",
|
||||
"Kousek másla na zpěnění",
|
||||
"2 brambory nakrájené na drobné kostičky",
|
||||
"1 lžička sladké a špetka pálivé papriky",
|
||||
"Celý český kmín a sůl",
|
||||
"1 litr mléka (nebo kysaná smetana) s lžící mouky"
|
||||
],
|
||||
postup: "Cibuli zpěnit na másle do sklovata. Přidat brambory, papriku a kmín. Zalít vodou a uvařit doměkka. V mléce rozkvedlat lžíci hrubé mouky, vlažné vlít do vroucí polévky a nechat provařit.",
|
||||
tajemstvi: "Před podáváním zjemnit lžící poctivé kysané smetany a celý kmín před vhozením promnout v dlani pro uvolnění silic."
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
nazev: "Babiččino kuře na česneku a slanině",
|
||||
kategorie: "Hlavní chod",
|
||||
doba: 85,
|
||||
suroviny: [
|
||||
"1 celé kuře naporcované na čtvrtky",
|
||||
"15 dkg domácí uzené slaniny na kostičky",
|
||||
"1 celá hlavička třeného českého česneku",
|
||||
"Kousek másla a sůl s černým pepřem",
|
||||
"Příloha: vařené brambory s petrželkou"
|
||||
],
|
||||
postup: "Do litinového pekáče nasypat kostičky slaniny a česnek. Čtvrtky kuřete osolit, opepřit, položit kůží nahoru na lůžko, poklást plátky másla a pomalu péct přikryté.",
|
||||
tajemstvi: "Posledních 15 minut odklopit poklop a zvednout žár v troubě, aby kůžička dokonale zkřupala a česnek zezlátl."
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
nazev: "Vosí hnízda s rumovým krémem",
|
||||
kategorie: "Vánoční cukroví",
|
||||
doba: 50,
|
||||
suroviny: [
|
||||
"16 dkg jemně rozdrcených piškotů",
|
||||
"16 dkg moučkového cukru",
|
||||
"6 dkg másla, 2 lžíce holandského kakaa",
|
||||
"2 lžíce mléka a pořádná dávka tuzemského rumu",
|
||||
"Krém: 5 dkg másla utřeného se žloutkem a vanilkovým cukrem",
|
||||
"Celé dětské piškoty na přiklopení"
|
||||
],
|
||||
postup: "Z těsta vyválet váleček. Kousky vmačkat do formiček vysypaných moučkovým cukrem, udělat důlek, naplnit žloutkovým krémem a zaklopit piškotem.",
|
||||
tajemstvi: "Formičku před každým kouskem vyprášit moučkovým cukrem, hnízdo pak vyklouzne jedním lehkým klepnutím o vál."
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
nazev: "Dělové koule (nepečené kakao)",
|
||||
kategorie: "Cukroví",
|
||||
doba: 40,
|
||||
suroviny: [
|
||||
"10 dkg pravého změklého másla",
|
||||
"10 lžic moučkového cukru",
|
||||
"1 sáček vanilkového cukru",
|
||||
"1 čerstvý vaječný žloutek",
|
||||
"2 plné lžíce holandského kakaa",
|
||||
"Strouhaný kokos nebo mleté vlašské ořechy na obalení"
|
||||
],
|
||||
postup: "Máslo utřít s cukrem, kakaem a žloutkem do kompaktní hmoty. Tvarovat kuličky a obalovat v kokosu či ořeších. Nechat odležet v chladu.",
|
||||
tajemstvi: "Přímo do kakaového základu kápnout tři kapky rumu pro sametový lesk a plnost čokoládové chuti."
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
nazev: "Kočičí oči s rybízovou marmeládou",
|
||||
kategorie: "Pečení",
|
||||
doba: 45,
|
||||
suroviny: [
|
||||
"10 dkg hladké mouky",
|
||||
"8 dkg poctivého másla",
|
||||
"3 dkg moučkového cukru",
|
||||
"1 vaječný žloutek",
|
||||
"Ostrá domácí rybízová marmeláda"
|
||||
],
|
||||
postup: "Křehké těsto nechat odpočinout v chladu. Vyválet na tenko, vykrajovat plná kolečka a kolečka se třemi dírkami. Péct do světle růžova a slepovat rybízem.",
|
||||
tajemstvi: "Rybízovou marmeládu před mazáním mírně nahřát v kastrůlku, aby se dokonale vpila do křehkého těsta."
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
nazev: "Sváteční taštičky s vepřovým masem",
|
||||
kategorie: "Sváteční pečení",
|
||||
doba: 55,
|
||||
suroviny: [
|
||||
"2 balíčky listového těsta",
|
||||
"30 dkg mletého vepřového masa",
|
||||
"1 cibule nasekaná najemno",
|
||||
"Domácí slanina na kostičky",
|
||||
"1 vejce do náplně, 1 vejce na potření",
|
||||
"Jemně strouhaná citronová kůra a pepř"
|
||||
],
|
||||
postup: "Mleté maso orestovat na cibulce se slaninou a citronovou kůrou. Z těsta vyválet čtverce, plnit masem, okraje stisknout vidličkou, potřít vejcem a péct zprudka v horké troubě.",
|
||||
tajemstvi: "Citronová kůra dodá vepřové náplni neuvěřitelnou svěžest a lehkost (ochranná známka Kamily)."
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
nazev: "Zapečené fleky se špenátem a uzeným masem",
|
||||
kategorie: "Hlavní chod",
|
||||
doba: 40,
|
||||
suroviny: [
|
||||
"Těstoviny fleky",
|
||||
"Listový špenát čerstvý či drcený",
|
||||
"Uzené maso nebo klobása na kostičky",
|
||||
"3 vejce a trochu plnotučného mléka",
|
||||
"4 stroužky třeného českého česneku"
|
||||
],
|
||||
postup: "Fleky uvařit al dente, špenát podusit na česneku. Promíchat s uzeným masem, rozprostřít do vymazaného pekáčku a zapéct zalité rozšlehaným vejcem s mlékem.",
|
||||
tajemstvi: "Do vaječné zálivky přistrouhnout čerstvý muškátový oříšek a na povrch položit plátky uzeného boku."
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
nazev: "Kuřecí srdíčka na smetaně s rýží",
|
||||
kategorie: "Hlavní chod",
|
||||
doba: 50,
|
||||
suroviny: [
|
||||
"Čerstvá kuřecí srdíčka (500g)",
|
||||
"Hustá 33% smetana ke šlehání",
|
||||
"Rýže parboiled nebo jasmínová",
|
||||
"Cibule a poctivé máslo",
|
||||
"Nové koření, bobkový list a černý pepř"
|
||||
],
|
||||
postup: "Srdíčka očistit a podusit na cibulce s divokým kořením doměkka. Vyjmout koření, zalít smetanou a nechat zredukovat do sametové omáčky.",
|
||||
tajemstvi: "Omáčku nezahušťovat moukou! Nechat ji přirozeně zhoustnout pouhou redukcí poctivé smetany."
|
||||
}
|
||||
];
|
||||
|
||||
// Historical Database records (510 entries sample from 1998-2021 Bohnice)
|
||||
const HISTORICKA_JIDLA = [
|
||||
{ nazev: "PIZZA [založení deníku v Bohnicích]", datum: "03.08.1998", typ: "Večeře", vareni: 12, tagy: ["pizza", "syr", "testo"] },
|
||||
{ nazev: "Špenát, uzené maso, bramborový knedlík", datum: "04.08.1998", typ: "Večeře", vareni: 19, tagy: ["spenat", "maso", "knedlik", "uzene"] },
|
||||
{ nazev: "Kuře, nudle, ananas (Kryštofovo oblíbené)", datum: "06.09.1998", typ: "Oběd", vareni: 18, tagy: ["kure", "nudle", "ananas", "maso"] },
|
||||
{ nazev: "Maso – uzený jazyk, brambory, obloha", datum: "13.09.1998", typ: "Oslava", vareni: 7, tagy: ["maso", "brambory", "jazyk"] },
|
||||
{ nazev: "Smažený sýr, vařené brambory a tatarka", datum: "14.05.2021", typ: "Večeře", vareni: 32, tagy: ["syr", "brambory", "tatarka", "vejce"] },
|
||||
{ nazev: "Špagety milánské se sýrem a masem", datum: "10.04.2021", typ: "Oběd", vareni: 29, tagy: ["spagety", "maso", "syr", "rajcata"] },
|
||||
{ nazev: "Domácí lečo s klobásou a čerstvým chlebem", datum: "12.08.2020", typ: "Večeře", vareni: 27, tagy: ["leco", "vejce", "klobasa", "chleb"] },
|
||||
{ nazev: "Rizoto se zeleninou, kuřecím masem a sýrem", datum: "02.03.2021", typ: "Oběd", vareni: 25, tagy: ["ryze", "kure", "zelenina", "syr"] },
|
||||
{ nazev: "Smažený vepřový řízek s bramborovým salátem", datum: "23.02.2019", typ: "Oběd", vareni: 24, tagy: ["maso", "brambory", "vejce", "salat"] },
|
||||
{ nazev: "Královská drůbeží sekaná s bramborovou kaší", datum: "15.01.2021", typ: "Oběd", vareni: 23, tagy: ["maso", "brambory", "kase", "mleko"] },
|
||||
{ nazev: "Křupavý bramborák s česnekem a majoránkou", datum: "05.11.2020", typ: "Večeře", vareni: 22, tagy: ["brambory", "cesnek", "vejce", "majoranka"] },
|
||||
{ nazev: "Fazolky na kyselo se smetanou a bramborem", datum: "18.06.2020", typ: "Oběd", vareni: 17, tagy: ["fazolky", "smetana", "brambory", "ocet"] },
|
||||
{ nazev: "Hovězí guláš s cibulí a houskovým knedlíkem", datum: "20.10.2019", typ: "Oběd", vareni: 20, tagy: ["maso", "hovezi", "cibule", "knedlik"] },
|
||||
{ nazev: "Palačinky s tvarohem a jahodovou marmeládou", datum: "11.04.2020", typ: "Večeře", vareni: 16, tagy: ["mleko", "mouka", "vejce", "tvaroh"] },
|
||||
{ nazev: "Květákový mozeček s vařeným bramborem", datum: "28.05.2019", typ: "Oběd", vareni: 14, tagy: ["kvetak", "vejce", "brambory", "cibule"] }
|
||||
];
|
||||
// Data references
|
||||
const KNIHA_RECEPTU = window.KNIHA_RECEPTU || [];
|
||||
const HISTORICKA_JIDLA = window.HISTORICKA_JIDLA || [];
|
||||
|
||||
// App State
|
||||
let currentView = 'menu'; // 'menu', 'recept', 'recepty_list', 'suroviny', 'davno', 'oblibena', 'kronika', 'altan'
|
||||
let currentRecipeId = 1;
|
||||
let currentCategory = 'Vše';
|
||||
let listPage = 0;
|
||||
const PAGE_SIZE = 12;
|
||||
|
||||
const screenEl = document.getElementById('screen-content');
|
||||
const winTitleEl = document.getElementById('win-title');
|
||||
@@ -190,50 +50,56 @@ setInterval(() => {
|
||||
timeEl.textContent = d.toTimeString().split(' ')[0];
|
||||
}, 1000);
|
||||
|
||||
// Normalization function (strip accents)
|
||||
// Normalization function (strip accents & non-alphanumeric)
|
||||
function norm(str) {
|
||||
return (str || "").toLowerCase()
|
||||
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/[^a-z0-9]/g, "");
|
||||
}
|
||||
|
||||
// Categories list
|
||||
const KATEGORIE = [
|
||||
'Vše',
|
||||
'Polévky',
|
||||
'Masitá jídla',
|
||||
'Vánoční cukroví',
|
||||
'Moučníky a sladké',
|
||||
'Přílohy a bezmasá',
|
||||
'Omáčky',
|
||||
'Saláty a zelenina'
|
||||
];
|
||||
|
||||
// Views renderers
|
||||
function renderMainMenu() {
|
||||
currentView = 'menu';
|
||||
winTitleEl.textContent = "KUCHAŘKA HELENY A KAMILY HEROUTOVÉ // HLAVNÍ NABÍDKA";
|
||||
winTitleEl.textContent = "KUCHAŘKA KAMILY PEŠEK // HLAVNÍ NABÍDKA PASCAL 7.0";
|
||||
|
||||
let html = `
|
||||
<div class="dos-header">════════════════════════════════════════════════════════════════════════════════
|
||||
KUCHAŘKA HELENY A KAMILY HEROUTOVÉ // SOFTWARE-SALON ALTÁN (1998–2026)
|
||||
Dejvice, Václavkova 2 & Bohnice | Rekonstrukce v Pascalu pro The Observatoř
|
||||
KUCHAŘKA KAMILY PEŠEK // AUTENTICKÝ SEŠIT RECEPTŮ (1998–2026)
|
||||
Software-salon ALTÁN (Ing. Helena Heroutová, Dejvice) & Konzdat
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
|
||||
<div class="dos-box">
|
||||
<span style="color:var(--dos-yellow)">Vítejte v rodinném softwarovém archivu receptů a jídelníčků.</span>
|
||||
Tento systém propojil procedurální přesnost Turbo Pascalu s každodenní
|
||||
péčí o teplo domova a společný stůl po více než dvě dekády.
|
||||
<span style="color:var(--dos-yellow)">Vítejte v rodinném gastronomickém archivu babičky <strong>Kamily Pešek</strong>.</span>
|
||||
Tento systém obsahuje <strong>${KNIHA_RECEPTU.length} přepsaných receptů</strong> z jejího ručně psaného
|
||||
sešitu (strany 1–43) a <strong>${HISTORICKA_JIDLA.length} záznamů rodinného jídelníčku</strong> z let 1998–2021.
|
||||
</div>
|
||||
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── RUČNĚ PSANÝ SEŠIT RECEPTŮ KAMILY HEROUTOVÉ ─────────────────────────────</div>
|
||||
<div class="dos-menu-list">
|
||||
`;
|
||||
|
||||
KNIHA_RECEPTU.forEach(r => {
|
||||
html += `<div class="dos-menu-item" onclick="openRecipe(${r.id})">
|
||||
<span class="hotkey">[${r.id}]</span>
|
||||
<span style="color:var(--dos-white)">${r.nazev}</span>
|
||||
<span class="tag-badge" style="margin-left:auto">${r.kategorie} (${r.doba} min)</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
html += `</div>
|
||||
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── ALGORITMICKÉ MODULY & VAŘENÍ (KUCHARKA.EXE) ─────────────────────────────</div>
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── HLAVNÍ PROGRAMOVÉ MODULY ────────────────────────────────────────────────</div>
|
||||
<div class="dos-menu-list">
|
||||
<div class="dos-menu-item" onclick="openReceptyList('Vše')">
|
||||
<span class="hotkey">[R]</span>
|
||||
<span style="color:var(--dos-white); font-weight:bold">>> PROCHÁZET CELÝ RECEPTÁŘ KAMILY PEŠEK (${KNIHA_RECEPTU.length} receptů dle kategorií) <<</span>
|
||||
</div>
|
||||
<div class="dos-menu-item" onclick="openSuroviny()">
|
||||
<span class="hotkey">[S]</span>
|
||||
<span style="color:var(--dos-yellow); font-weight:bold">>> VAŘIT PODLE SUROVIN VE ŠPAJZU << (Algoritmus prohledání lednice)</span>
|
||||
</div>
|
||||
<div class="dos-menu-item" onclick="openRandomRecipe()">
|
||||
<span class="hotkey">[N]</span>
|
||||
<span style="color:var(--dos-green)">Náhodný recept pro dnešní den (Inspirace z Kamilina sešitu)</span>
|
||||
</div>
|
||||
<div class="dos-menu-item" onclick="openDavno()">
|
||||
<span class="hotkey">[D]</span>
|
||||
<span>Doporučení: Dávno nevařeno (Prevence stereotypu - DoporucD)</span>
|
||||
@@ -244,7 +110,7 @@ function renderMainMenu() {
|
||||
</div>
|
||||
<div class="dos-menu-item" onclick="openKronika()">
|
||||
<span class="hotkey">[K]</span>
|
||||
<span>Kronika rodinného stolu (Prohlížeč 510 záznamů JÍDLA 1998–2021)</span>
|
||||
<span>Kronika rodinného stolu (Prohlížeč ${HISTORICKA_JIDLA.length} záznamů z Bohnic a Dejvic)</span>
|
||||
</div>
|
||||
<div class="dos-menu-item" onclick="openAltan()">
|
||||
<span class="hotkey">[A]</span>
|
||||
@@ -252,13 +118,106 @@ function renderMainMenu() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── RYCHLÝ VÝBĚR KATEGORIE ──────────────────────────────────────────────────</div>
|
||||
<div style="display:flex; flex-wrap:wrap; gap:8px; margin:10px 0">
|
||||
`;
|
||||
|
||||
KATEGORIE.slice(1).forEach((cat, idx) => {
|
||||
const count = KNIHA_RECEPTU.filter(r => r.kategorie === cat).length;
|
||||
html += `<button class="dos-btn" onclick="openReceptyList('${cat}')">[${idx + 1}] ${cat} (${count})</button>`;
|
||||
});
|
||||
|
||||
html += `</div>
|
||||
|
||||
<div style="margin-top:20px; color:var(--dos-grey)">
|
||||
Zvolte číslo <span class="hotkey">[1-8]</span> nebo klávesu <span class="hotkey">[S, D, O, K, A]</span>. Na mobilu klepněte prstem.
|
||||
Zvolte klávesu <span class="hotkey">[R, S, N, D, O, K, A]</span> nebo kategorii <span class="hotkey">[1-7]</span>. Na mobilu klepněte prstem.
|
||||
</div>
|
||||
`;
|
||||
screenEl.innerHTML = html;
|
||||
}
|
||||
|
||||
// List of recipes with category filter & pagination
|
||||
function openReceptyList(category = 'Vše', page = 0) {
|
||||
currentView = 'recepty_list';
|
||||
currentCategory = category;
|
||||
listPage = page;
|
||||
playSpeaker(700, 0.04);
|
||||
|
||||
const filtered = category === 'Vše' ? KNIHA_RECEPTU : KNIHA_RECEPTU.filter(r => r.kategorie === category);
|
||||
const totalPages = Math.ceil(filtered.length / PAGE_SIZE);
|
||||
const pageItems = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);
|
||||
|
||||
winTitleEl.textContent = `RECEPTÁŘ KAMILY PEŠEK // ${category.toUpperCase()} (${filtered.length} RECEPTŮ)`;
|
||||
|
||||
let html = `
|
||||
<div class="dos-header">════════════════════════════════════════════════════════════════════════════════
|
||||
RECEPTÁŘ KAMILY PEŠEK // KATEGORIE: ${category.toUpperCase()} (Strana ${page + 1}/${totalPages || 1})
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
|
||||
<div style="display:flex; gap:6px; flex-wrap:wrap; margin-bottom:12px">
|
||||
`;
|
||||
KATEGORIE.forEach(cat => {
|
||||
const activeClass = cat === category ? 'style="background-color:var(--dos-yellow); color:var(--dos-black)"' : '';
|
||||
html += `<button class="dos-btn" ${activeClass} onclick="openReceptyList('${cat}', 0)">${cat}</button>`;
|
||||
});
|
||||
|
||||
html += `</div>
|
||||
|
||||
<div class="dos-input-group" style="margin:8px 0 14px 0">
|
||||
<input type="text" id="recipe-search-input" class="dos-input" placeholder="Rychlé hledání v receptech (např. bábovka, kuře, kmínová, závin)...">
|
||||
</div>
|
||||
|
||||
<div class="dos-menu-list" id="recipe-items-container">
|
||||
`;
|
||||
|
||||
pageItems.forEach((r, idx) => {
|
||||
html += `<div class="dos-menu-item" onclick="openRecipe(${r.id})">
|
||||
<span class="hotkey">[${r.id}]</span>
|
||||
<span style="color:var(--dos-white)">${r.nazev}</span>
|
||||
<span class="tag-badge" style="margin-left:auto">Str. ${r.strana} | ${r.kategorie}</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
html += `</div>
|
||||
|
||||
<div style="margin-top:16px; display:flex; justify-content:space-between; align-items:center">
|
||||
<div style="display:flex; gap:10px">
|
||||
<button class="dos-btn" onclick="renderMainMenu()">[ESC] HLAVNÍ NABÍDKA</button>
|
||||
${page > 0 ? `<button class="dos-btn" onclick="openReceptyList('${category}', ${page - 1})">[◄] PŘEDCHOZÍ STRANA</button>` : ''}
|
||||
${page < totalPages - 1 ? `<button class="dos-btn" onclick="openReceptyList('${category}', ${page + 1})">[►] DALŠÍ STRANA</button>` : ''}
|
||||
</div>
|
||||
<div style="color:var(--dos-yellow); font-size:13px">
|
||||
Celkem ${filtered.length} receptů
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
screenEl.innerHTML = html;
|
||||
|
||||
// Search input live filtering
|
||||
const searchInput = document.getElementById('recipe-search-input');
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const q = norm(e.target.value);
|
||||
if (!q) {
|
||||
openReceptyList(category, page);
|
||||
return;
|
||||
}
|
||||
const matched = filtered.filter(r => norm(r.nazev).includes(q) || norm(r.postup).includes(q) || r.suroviny.some(s => norm(s).includes(q)));
|
||||
const container = document.getElementById('recipe-items-container');
|
||||
let mHtml = "";
|
||||
matched.slice(0, 15).forEach(r => {
|
||||
mHtml += `<div class="dos-menu-item" onclick="openRecipe(${r.id})">
|
||||
<span class="hotkey">[${r.id}]</span>
|
||||
<span style="color:var(--dos-white)">${r.nazev}</span>
|
||||
<span class="tag-badge" style="margin-left:auto">Str. ${r.strana} | ${r.kategorie}</span>
|
||||
</div>`;
|
||||
});
|
||||
if (matched.length === 0) {
|
||||
mHtml = `<div style="color:var(--dos-red); padding:10px">Žádný recept neodpovídá hledanému výrazu "${e.target.value}".</div>`;
|
||||
}
|
||||
container.innerHTML = mHtml;
|
||||
});
|
||||
}
|
||||
|
||||
function openRecipe(id) {
|
||||
const r = KNIHA_RECEPTU.find(x => x.id === id);
|
||||
if (!r) return;
|
||||
@@ -266,15 +225,15 @@ function openRecipe(id) {
|
||||
currentRecipeId = id;
|
||||
playSpeaker(900, 0.05);
|
||||
|
||||
winTitleEl.textContent = `RECEPT [${r.id}]: ${r.nazev.toUpperCase()}`;
|
||||
winTitleEl.textContent = `RECEPT [${r.id}/${KNIHA_RECEPTU.length}]: ${r.nazev.toUpperCase()}`;
|
||||
|
||||
let html = `
|
||||
<div class="dos-header">════════════════════════════════════════════════════════════════════════════════
|
||||
RECEPT Č. ${r.id}: ${r.nazev}
|
||||
Kategorie: ${r.kategorie} | Odhadovaná doba přípravy: ${r.doba} minut
|
||||
Původní sešit Kamily Pešek: Strana ${r.strana} | Kategorie: ${r.kategorie}
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── POTŘEBNÉ SUROVINY ───────────────────────────────────────────────────────</div>
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── POTŘEBNÉ SUROVINY ZE SEŠITU ─────────────────────────────────────────────</div>
|
||||
<ul style="list-style:none; padding-left:10px; margin-bottom:15px">
|
||||
`;
|
||||
r.suroviny.forEach(s => {
|
||||
@@ -283,25 +242,31 @@ function openRecipe(id) {
|
||||
|
||||
html += `</ul>
|
||||
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── POSTUP PŘÍPRAVY ZE SEŠITU ──────────────────────────────────────────────</div>
|
||||
<div class="dos-box" style="line-height:1.5; color:var(--dos-white)">
|
||||
<div class="dos-header" style="color:var(--dos-cyan)">── POSTUP PŘÍPRAVY ZE SEŠITU KAMILY PEŠEK ─────────────────────────────────</div>
|
||||
<div class="dos-box" style="line-height:1.55; color:var(--dos-white)">
|
||||
${r.postup}
|
||||
</div>
|
||||
|
||||
<div class="secret-badge">
|
||||
<strong>★ TAJEMSTVÍ KAMILY HEROUTOVÉ:</strong><br>
|
||||
<strong>★ TAJEMSTVÍ KAMILY PEŠEK:</strong><br>
|
||||
<em>"${r.tajemstvi}"</em>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px; display:flex; gap:10px">
|
||||
<button class="dos-btn" onclick="renderMainMenu()">[ESC] ZPĚT DO NABÍDKY</button>
|
||||
${id > 1 ? `<button class="dos-btn" onclick="openRecipe(${id - 1})">[◄] PŘEDCHOZÍ</button>` : ''}
|
||||
${id < KNIHA_RECEPTU.length ? `<button class="dos-btn" onclick="openRecipe(${id + 1})">[►] DALŠÍ</button>` : ''}
|
||||
<div style="margin-top:20px; display:flex; gap:10px; flex-wrap:wrap">
|
||||
<button class="dos-btn" onclick="openReceptyList(currentCategory, listPage)">[ESC] ZPĚT DO SEZNAMU</button>
|
||||
<button class="dos-btn" onclick="renderMainMenu()">[H] HLAVNÍ NABÍDKA</button>
|
||||
${id > 1 ? `<button class="dos-btn" onclick="openRecipe(${id - 1})">[◄] PŘEDCHOZÍ (${id - 1})</button>` : ''}
|
||||
${id < KNIHA_RECEPTU.length ? `<button class="dos-btn" onclick="openRecipe(${id + 1})">[►] DALŠÍ (${id + 1})</button>` : ''}
|
||||
</div>
|
||||
`;
|
||||
screenEl.innerHTML = html;
|
||||
}
|
||||
|
||||
function openRandomRecipe() {
|
||||
const randomId = Math.floor(Math.random() * KNIHA_RECEPTU.length) + 1;
|
||||
openRecipe(randomId);
|
||||
}
|
||||
|
||||
function openSuroviny(defaultQuery = "") {
|
||||
currentView = 'suroviny';
|
||||
playSpeaker(700, 0.06);
|
||||
@@ -313,12 +278,12 @@ function openSuroviny(defaultQuery = "") {
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
<div class="dos-box">
|
||||
Zadejte suroviny, které máte právě doma ve špajzu a lednici (oddělené čárkou či mezerou).
|
||||
Algoritmus porovná databázi receptů Kamily i 510 historických jídel a spočítá shodu.
|
||||
Algoritmus prohledá <strong>všech ${KNIHA_RECEPTU.length} receptů Kamily Pešek</strong> i <strong>${HISTORICKA_JIDLA.length} jídel z kroniky</strong>.
|
||||
</div>
|
||||
|
||||
<div class="dos-input-group">
|
||||
<span style="color:var(--dos-yellow); font-weight:bold">MÁM DOMA:</span>
|
||||
<input type="text" id="suroviny-input" class="dos-input" placeholder="např. brambory, maso, smetana, vejce, kure" value="${defaultQuery}">
|
||||
<input type="text" id="suroviny-input" class="dos-input" placeholder="např. brambory, maso, smetana, vejce, kuře, jablka..." value="${defaultQuery}">
|
||||
<button class="dos-btn" onclick="doSearchSuroviny()">HLEDAT [ENTER]</button>
|
||||
</div>
|
||||
|
||||
@@ -356,16 +321,16 @@ function doSearchSuroviny() {
|
||||
|
||||
const tokens = query.toLowerCase().split(/[\s,]+/).filter(t => t.length > 1).map(norm);
|
||||
|
||||
// Score Recipes
|
||||
// Score Recipes (both suroviny and postup)
|
||||
const scoredRecipes = KNIHA_RECEPTU.map(r => {
|
||||
let matches = [];
|
||||
r.suroviny.forEach(sur => {
|
||||
const nSur = norm(sur);
|
||||
tokens.forEach(tok => {
|
||||
if (nSur.includes(tok)) {
|
||||
matches.push(tok);
|
||||
}
|
||||
});
|
||||
const fullText = (r.suroviny.join(' ') + ' ' + r.postup).toLowerCase();
|
||||
const nFull = norm(fullText);
|
||||
|
||||
tokens.forEach(tok => {
|
||||
if (nFull.includes(tok)) {
|
||||
matches.push(tok);
|
||||
}
|
||||
});
|
||||
matches = [...new Set(matches)];
|
||||
return { recipe: r, score: matches.length, matchedTokens: matches };
|
||||
@@ -374,23 +339,21 @@ function doSearchSuroviny() {
|
||||
// Score Historical Database
|
||||
const scoredHist = HISTORICKA_JIDLA.map(j => {
|
||||
let matches = [];
|
||||
j.tagy.forEach(tag => {
|
||||
const nTag = norm(tag);
|
||||
tokens.forEach(tok => {
|
||||
if (nTag.includes(tok) || norm(j.nazev).includes(tok)) {
|
||||
matches.push(tok);
|
||||
}
|
||||
});
|
||||
const nJidlo = norm(j.jidlo);
|
||||
tokens.forEach(tok => {
|
||||
if (nJidlo.includes(tok)) {
|
||||
matches.push(tok);
|
||||
}
|
||||
});
|
||||
matches = [...new Set(matches)];
|
||||
return { jidlo: j, score: matches.length, matchedTokens: matches };
|
||||
}).filter(x => x.score > 0).sort((a, b) => b.score - a.score);
|
||||
|
||||
let html = `<div class="dos-header" style="color:var(--dos-green)">VÝSLEDEK SHODY PRO: "${query}"</div>`;
|
||||
let html = `<div class="dos-header" style="color:var(--dos-green)">VÝSLEDEK SHODY PRO: "${query}" (Nalezeno ${scoredRecipes.length} receptů Kamily Pešek)</div>`;
|
||||
|
||||
if (scoredRecipes.length > 0) {
|
||||
html += `<div style="color:var(--dos-yellow); margin:10px 0; font-weight:bold">★ RECEPTY Z RUČNÍHO SEŠITU KAMILY:</div>`;
|
||||
scoredRecipes.forEach(item => {
|
||||
html += `<div style="color:var(--dos-yellow); margin:10px 0; font-weight:bold">★ NALEZENÉ RECEPTY ZE SEŠITU KAMILY PEŠEK:</div>`;
|
||||
scoredRecipes.slice(0, 15).forEach(item => {
|
||||
html += `<div class="dos-menu-item" onclick="openRecipe(${item.recipe.id})">
|
||||
<span class="hotkey">[${item.recipe.id}]</span>
|
||||
<span style="color:var(--dos-white)">${item.recipe.nazev}</span>
|
||||
@@ -401,9 +364,9 @@ function doSearchSuroviny() {
|
||||
|
||||
if (scoredHist.length > 0) {
|
||||
html += `<div style="color:var(--dos-cyan); margin:15px 0 5px 0; font-weight:bold">📜 ZÁZNAMY Z HISTORICKÉHO JÍDELNÍČKU (1998–2021):</div>`;
|
||||
scoredHist.slice(0, 8).forEach(item => {
|
||||
scoredHist.slice(0, 10).forEach(item => {
|
||||
html += `<div style="padding:3px 8px; border-left:2px solid var(--dos-cyan); margin:4px 0">
|
||||
<span style="color:var(--dos-white)">${item.jidlo.nazev}</span>
|
||||
<span style="color:var(--dos-white)">${item.jidlo.jidlo}</span>
|
||||
<span style="color:var(--dos-grey)"> [${item.jidlo.datum}, ${item.jidlo.typ}]</span>
|
||||
<span style="color:var(--dos-yellow); float:right">shoda: ${item.matchedTokens.join(', ')}</span>
|
||||
</div>`;
|
||||
@@ -411,7 +374,7 @@ function doSearchSuroviny() {
|
||||
}
|
||||
|
||||
if (scoredRecipes.length === 0 && scoredHist.length === 0) {
|
||||
html += `<div style="color:var(--dos-red); padding:10px">Pro tyto suroviny nebyla v databázi nalezena přímá shoda. Zkuste zadat základní ingredience jako: brambory, maso, vejce, smetana, kure.</div>`;
|
||||
html += `<div style="color:var(--dos-red); padding:10px">Pro tyto suroviny nebyla v databázi nalezena přímá shoda. Zkuste zadat základní ingredience jako: brambory, maso, vejce, smetana, kuře.</div>`;
|
||||
}
|
||||
|
||||
resEl.innerHTML = html;
|
||||
@@ -434,10 +397,10 @@ function openDavno() {
|
||||
<div class="dos-header" style="color:var(--dos-yellow)">DOPORUČENÁ JÍDLA S NEJDELŠÍM ČASOVÝM ODSTUPEM:</div>
|
||||
<div class="dos-menu-list">
|
||||
`;
|
||||
HISTORICKA_JIDLA.slice(0, 8).forEach((j, idx) => {
|
||||
html += `<div style="padding:6px 10px; border-bottom:1px dashed var(--dos-cyan); display:flex; justify-content:space-between">
|
||||
<span><strong style="color:var(--dos-cyan)">${idx + 1}.</strong> <span style="color:var(--dos-white)">${j.nazev}</span></span>
|
||||
<span><span style="color:var(--dos-yellow)">Naposledy: ${j.datum}</span> | Celkem vařeno: ${j.vareni}×</span>
|
||||
HISTORICKA_JIDLA.slice(0, 12).forEach((j, idx) => {
|
||||
html += `<div style="padding:5px 10px; border-bottom:1px dashed var(--dos-cyan); display:flex; justify-content:space-between">
|
||||
<span><strong style="color:var(--dos-cyan)">${idx + 1}.</strong> <span style="color:var(--dos-white)">${j.jidlo}</span></span>
|
||||
<span><span style="color:var(--dos-yellow)">Naposledy: ${j.datum}</span> | ${j.typ}</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
@@ -454,26 +417,34 @@ function openOblibena() {
|
||||
playSpeaker(850, 0.05);
|
||||
winTitleEl.textContent = "OBLÍBENÁ JÍDLA // ŽELEZNÁ STATISTIKA STOLU";
|
||||
|
||||
const top = [...HISTORICKA_JIDLA].sort((a, b) => b.vareni - a.vareni);
|
||||
// Count occurrences of keywords in historical records
|
||||
const freq = {};
|
||||
HISTORICKA_JIDLA.forEach(j => {
|
||||
const title = j.jidlo.split(',')[0].trim();
|
||||
freq[title] = (freq[title] || 0) + 1;
|
||||
});
|
||||
|
||||
const top = Object.entries(freq).sort((a, b) => b[1] - a[1]).slice(0, 12);
|
||||
|
||||
let html = `
|
||||
<div class="dos-header">════════════════════════════════════════════════════════════════════════════════
|
||||
ŽELEZNÁ STATISTIKA OBLÍBENÝCH JÍDEL RODINNÉHO STOLU (DoporucO)
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
<div class="dos-box">
|
||||
Frekvenční žebříček z 510 záznamů rodinného jídelníčku z let 1998–2021:
|
||||
Frekvenční žebříček z ${HISTORICKA_JIDLA.length} záznamů rodinného jídelníčku z let 1998–2021:
|
||||
</div>
|
||||
|
||||
<div class="dos-menu-list">
|
||||
`;
|
||||
top.slice(0, 10).forEach((j, idx) => {
|
||||
const barWidth = Math.round((j.vareni / top[0].vareni) * 25);
|
||||
const maxVal = top[0] ? top[0][1] : 1;
|
||||
top.forEach(([name, count], idx) => {
|
||||
const barWidth = Math.max(2, Math.round((count / maxVal) * 24));
|
||||
const bar = "█".repeat(barWidth);
|
||||
html += `<div style="padding:4px 8px; font-family:monospace">
|
||||
<span style="color:var(--dos-yellow); width:24px; display:inline-block">${idx + 1}.</span>
|
||||
<span style="color:var(--dos-white); display:inline-block; width:340px">${j.nazev}</span>
|
||||
<span style="color:var(--dos-white); display:inline-block; width:300px">${name}</span>
|
||||
<span style="color:var(--dos-cyan)">${bar}</span>
|
||||
<span style="color:var(--dos-yellow); margin-left:10px">${j.vareni}×</span>
|
||||
<span style="color:var(--dos-yellow); margin-left:10px">${count}×</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
@@ -485,33 +456,38 @@ function openOblibena() {
|
||||
screenEl.innerHTML = html;
|
||||
}
|
||||
|
||||
function openKronika() {
|
||||
function openKronika(page = 0) {
|
||||
currentView = 'kronika';
|
||||
playSpeaker(650, 0.05);
|
||||
winTitleEl.textContent = "KRONIKA RODINNÉHO STOLU // 510 ZÁZNAMŮ BOHNICE (1998–2021)";
|
||||
winTitleEl.textContent = `KRONIKA RODINNÉHO STOLU // ${HISTORICKA_JIDLA.length} ZÁZNAMŮ BOHNICE (1998–2021)`;
|
||||
|
||||
const totalPages = Math.ceil(HISTORICKA_JIDLA.length / 20);
|
||||
const items = HISTORICKA_JIDLA.slice(page * 20, (page + 1) * 20);
|
||||
|
||||
let html = `
|
||||
<div class="dos-header">════════════════════════════════════════════════════════════════════════════════
|
||||
KRONIKA RODINNÉHO STOLU // BOHNICE & DEJVICE (1998–2021)
|
||||
KRONIKA RODINNÉHO STOLU // BOHNICE & DEJVICE (Strana ${page + 1}/${totalPages})
|
||||
════════════════════════════════════════════════════════════════════════════════</div>
|
||||
<div class="dos-box">
|
||||
Autentické záznamy z databázových souborů JIDLA.TXT a JIDLA-bohnice.TXT.
|
||||
Celkem ${HISTORICKA_JIDLA.length} autentických záznamů z databázových souborů JIDLA.TXT a JIDLA-bohnice.TXT.
|
||||
Od srpna 1998, přes dětská léta až po rok 2021.
|
||||
</div>
|
||||
|
||||
<div style="max-height:360px; overflow-y:auto; border:1px solid var(--dos-cyan); padding:10px">
|
||||
<div style="max-height:360px; overflow-y:auto; border:1px solid var(--dos-cyan); padding:8px">
|
||||
`;
|
||||
HISTORICKA_JIDLA.forEach(j => {
|
||||
html += `<div style="padding:4px 0; border-bottom:1px dotted rgba(0,170,170,0.4)">
|
||||
<span style="color:var(--dos-yellow); font-weight:bold">${j.datum}</span>
|
||||
items.forEach(j => {
|
||||
html += `<div style="padding:3px 0; border-bottom:1px dotted rgba(0,170,170,0.3)">
|
||||
<span style="color:var(--dos-yellow); font-weight:bold; width:95px; display:inline-block">${j.datum}</span>
|
||||
<span class="tag-badge">[${j.typ}]</span>
|
||||
<span style="color:var(--dos-white)">${j.nazev}</span>
|
||||
<span style="color:var(--dos-white); margin-left:6px">${j.jidlo}</span>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
html += `</div>
|
||||
<div style="margin-top:20px">
|
||||
<div style="margin-top:16px; display:flex; gap:10px">
|
||||
<button class="dos-btn" onclick="renderMainMenu()">[ESC] ZPĚT DO MENU</button>
|
||||
${page > 0 ? `<button class="dos-btn" onclick="openKronika(${page - 1})">[◄] PŘEDCHOZÍ (20)</button>` : ''}
|
||||
${page < totalPages - 1 ? `<button class="dos-btn" onclick="openKronika(${page + 1})">[►] DALŠÍ (20)</button>` : ''}
|
||||
</div>
|
||||
`;
|
||||
screenEl.innerHTML = html;
|
||||
@@ -540,9 +516,10 @@ function openAltan() {
|
||||
které v 80. a 90. letech tvořily základní procedurální knihovny pro Turbo Pascal
|
||||
a DOS v dejvickém ateliéru na Václavkově ulici.<br><br>
|
||||
Když se na přelomu tisíciletí objevilo vývojové prostředí Borland Delphi, koncept
|
||||
rodinné kuchařky se přetavil do plnohodnotné 32bitové aplikace KUCHARKA.EXE.
|
||||
Tato webová aplikace věrně rekonstruuje veškeré algoritmy a zachovává
|
||||
rodinný odkaz pro další generace.
|
||||
rodinné kuchařky se přetavil do plnohodnotné 32bitové aplikace KUCHARKA.EXE.<br><br>
|
||||
Tato webová aplikace věrně propojuje kulinářské dědictví babičky <strong>Kamily Pešek</strong>
|
||||
(kompletní rukopisný sešit čítající <strong>${KNIHA_RECEPTU.length} receptů</strong>)
|
||||
s procedurální logikou Turbo Pascalu pro budoucí generace.
|
||||
</div>
|
||||
|
||||
<div style="margin-top:20px">
|
||||
@@ -554,7 +531,7 @@ function openAltan() {
|
||||
|
||||
// Global Keyboard Handler (Turbo Pascal Hotkeys)
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// If in search input, don't hijack all keys except ESC
|
||||
// If in input, only hijack ESC
|
||||
if (document.activeElement && document.activeElement.tagName === 'INPUT') {
|
||||
if (e.key === 'Escape') {
|
||||
document.activeElement.blur();
|
||||
@@ -566,14 +543,17 @@ window.addEventListener('keydown', (e) => {
|
||||
playSpeaker(800, 0.03);
|
||||
|
||||
switch (e.key) {
|
||||
case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8':
|
||||
openRecipe(parseInt(e.key));
|
||||
case 'r': case 'R': case 'F2':
|
||||
e.preventDefault();
|
||||
openReceptyList('Vše');
|
||||
break;
|
||||
case 's': case 'S': case 'F3':
|
||||
e.preventDefault();
|
||||
openSuroviny();
|
||||
break;
|
||||
case 'n': case 'N':
|
||||
openRandomRecipe();
|
||||
break;
|
||||
case 'd': case 'D': case 'F4':
|
||||
e.preventDefault();
|
||||
openDavno();
|
||||
@@ -589,37 +569,24 @@ window.addEventListener('keydown', (e) => {
|
||||
e.preventDefault();
|
||||
openAltan();
|
||||
break;
|
||||
case 'F2':
|
||||
e.preventDefault();
|
||||
renderMainMenu();
|
||||
break;
|
||||
case 'Escape':
|
||||
renderMainMenu();
|
||||
break;
|
||||
case '1': case '2': case '3': case '4': case '5': case '6': case '7':
|
||||
if (currentView === 'menu') {
|
||||
const idx = parseInt(e.key);
|
||||
if (KATEGORIE[idx]) openReceptyList(KATEGORIE[idx]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Touch button handler
|
||||
document.querySelectorAll('.t-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const key = btn.getAttribute('data-key');
|
||||
if (key === 'Escape') renderMainMenu();
|
||||
else if (key === 's') openSuroviny();
|
||||
else if (key === 'd') openDavno();
|
||||
else if (key === 'o') openOblibena();
|
||||
else if (key === 'k') openKronika();
|
||||
else {
|
||||
const num = parseInt(key);
|
||||
if (!isNaN(num)) openRecipe(num);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Menu bar clicks
|
||||
document.querySelectorAll('.menu-item').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const act = item.getAttribute('data-act');
|
||||
if (act === 'menu' || act === 'recepty') renderMainMenu();
|
||||
// Touch / Navigation controls handler
|
||||
document.querySelectorAll('[data-act]').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
const act = el.getAttribute('data-act');
|
||||
if (act === 'menu') renderMainMenu();
|
||||
else if (act === 'recepty') openReceptyList('Vše');
|
||||
else if (act === 'suroviny') openSuroviny();
|
||||
else if (act === 'davno') openDavno();
|
||||
else if (act === 'oblibena') openOblibena();
|
||||
|
||||
Reference in New Issue
Block a user