[topic.id, topic]));
-export function AskStackMirea() {
- const [query, setQuery] = useState("");
+interface AskStackMireaProps {
+ initialQuery?: string;
+}
+
+export function AskStackMirea({ initialQuery = "" }: AskStackMireaProps) {
+ const searchParams = useSearchParams();
+ const [query, setQuery] = useState(initialQuery);
const { index, error, isLoading } = useSearchIndex();
const deferredQuery = useDeferredValue(query);
@@ -35,6 +41,11 @@ export function AskStackMirea() {
applyQuery(query.trim());
}
+ useEffect(() => {
+ const queryFromUrl = searchParams.get("q");
+ setQuery(queryFromUrl?.trim() || initialQuery);
+ }, [initialQuery, searchParams]);
+
return (
diff --git a/components/ui/ThemeToggle.tsx b/components/ui/ThemeToggle.tsx
index 52cb389..b32b745 100644
--- a/components/ui/ThemeToggle.tsx
+++ b/components/ui/ThemeToggle.tsx
@@ -6,6 +6,7 @@ import { useTheme } from "next-themes";
import { flushSync } from "react-dom";
import { Button } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
type ThemeName = "light" | "dark";
@@ -17,7 +18,11 @@ type ViewTransitionDocument = Document & {
startViewTransition?: (update: () => void | Promise) => ViewTransition;
};
-export function ThemeToggle() {
+interface ThemeToggleProps {
+ className?: string;
+}
+
+export function ThemeToggle({ className }: ThemeToggleProps) {
const { resolvedTheme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
const buttonRef = useRef(null);
@@ -27,7 +32,7 @@ export function ThemeToggle() {
}, []);
if (!mounted) {
- return ;
+ return ;
}
const isDark = resolvedTheme !== "light";
@@ -81,7 +86,7 @@ export function ThemeToggle() {
size="icon"
aria-label="Переключить тему"
onClick={toggleTheme}
- className="text-muted-foreground hover:text-foreground"
+ className={cn("text-muted-foreground hover:text-foreground", className)}
>
{isDark ? : }
diff --git a/lib/home.ts b/lib/home.ts
new file mode 100644
index 0000000..4681145
--- /dev/null
+++ b/lib/home.ts
@@ -0,0 +1,74 @@
+import { getAuthorsWithSummary } from "@/lib/contributors";
+import { getPublishedDocs } from "@/lib/navigation";
+import { getTrackDefinitions, type TrackIconKey } from "@/lib/tracks";
+import { getWhatsNewOverview, type WhatsNewAuthor, type WhatsNewMaterial } from "@/lib/whats-new";
+
+export interface HomeTrackCard {
+ id: string;
+ href: string;
+ title: string;
+ description: string;
+ iconKey: TrackIconKey;
+ itemsCount: number;
+}
+
+export interface HomePopularTrack {
+ id: string;
+ href: string;
+ title: string;
+ description: string;
+ itemsCount: number;
+}
+
+export interface HomePageOverview {
+ trackCards: HomeTrackCard[];
+ popularTracks: HomePopularTrack[];
+ recentMaterials: WhatsNewMaterial[];
+ featuredAuthors: WhatsNewAuthor[];
+}
+
+function getPublishedDocsCountByTrack() {
+ return getPublishedDocs().reduce(
+ (counts, doc) => counts.set(doc.section, (counts.get(doc.section) ?? 0) + 1),
+ new Map()
+ );
+}
+
+export function getHomePageOverview(): HomePageOverview {
+ const countsByTrack = getPublishedDocsCountByTrack();
+ const whatsNew = getWhatsNewOverview();
+ const fallbackAuthors = getAuthorsWithSummary();
+
+ const sortedTracks = getTrackDefinitions()
+ .map((track) => ({
+ id: track.id,
+ href: `/docs/${track.id}`,
+ title: track.title,
+ description: track.homeSubtitle,
+ iconKey: track.iconKey,
+ itemsCount: countsByTrack.get(track.id) ?? 0
+ }))
+ .sort((left, right) => {
+ if (left.itemsCount !== right.itemsCount) {
+ return right.itemsCount - left.itemsCount;
+ }
+
+ return left.title.localeCompare(right.title);
+ });
+
+ return {
+ trackCards: sortedTracks.slice(0, 10),
+ popularTracks: sortedTracks.slice(0, 3),
+ recentMaterials: whatsNew.materials.slice(0, 3),
+ featuredAuthors:
+ whatsNew.authors.slice(0, 3).length > 0
+ ? whatsNew.authors.slice(0, 3)
+ : fallbackAuthors.slice(0, 3).map((author) => ({
+ github: author.github,
+ profileUrl: author.profileUrl,
+ avatarUrl: author.avatarUrl,
+ docsCount: author.docsCount,
+ firstContributionAt: null
+ }))
+ };
+}
diff --git a/public/search-index.json b/public/search-index.json
index b31b609..0986233 100644
--- a/public/search-index.json
+++ b/public/search-index.json
@@ -1 +1 @@
-{"version":1,"generatedAt":"2026-05-05T08:47:47.095Z","docs":[{"id":"ai","href":"/docs/ai","slug":["ai"],"section":"ai","sectionTitle":"AI","title":"AI","description":"Рабочие тетради по искусственному интеллекту в формате MDX.","preview":"AI обзор Раздел объединяет 8 рабочих тетрадей по дисциплине «Искусственный интеллект», перенесенных в MDX формат. Материалы идут от базового Python и научных библиотек к классическим ML методам, нейросетям, эволюционным алгоритмам и кластеризации. Что внутри P","keywords":["notebook","python","ai","ml","данных","деревья","методы","раздел","решений","knn","mdx","numpy","pandas","алгоритмам","базового","библиотек","внутри","генетические"],"topics":["python","ai","knn","pandas","numpy","oop","algorithms","clustering","regression"],"chunks":[{"id":"chunk-0","heading":"AI обзор","text":"Раздел объединяет 8 рабочих тетрадей по дисциплине «Искусственный интеллект», перенесенных в MDX формат. Материалы идут от базового Python и научных библиотек к классическим ML методам, нейросетям, эволюционным алгоритмам и кластеризации.","keywords":["ai","mdx","mdx.","ml","python","алгоритмам","базового","библиотек","дисциплине","идут","интеллект","интеллекту"],"topics":["python","ai","oop","algorithms"]},{"id":"chunk-1","heading":"Что внутри","text":"Python, NumPy и pandas для подготовки данных; метрики расстояния, KNN и базовые техники классификации; регрессия и деревья решений; эволюционные методы, нейросети и кластеризация.","keywords":["ai","knn","mdx.","numpy","pandas","python","базовые","внутри","данных","деревья","интеллекту","искусственному"],"topics":["python","knn","pandas","numpy","oop","clustering","regression"]},{"id":"chunk-2","heading":"Ноутбуки","text":"Notebook 1 — Основа Python типы данных, условия, циклы и вводные примеры. Notebook 2 — NumPy и pandas массивы, таблицы и базовая подготовка данных. Notebook 3 — Метрики и KNN расстояния между объектами и классификация ближайших соседей. Notebook 4 — Регрессия линейные модели, аппроксимация и оценка качества. Notebook 5 — Деревья решений деревья решений и работа с классификаторами","keywords":["notebook","ai","деревья","решений","knn","mdx.","numpy","pandas","python","аппроксимация","базовая","ближайших"],"topics":["python","knn","pandas","numpy","oop","regression"]},{"id":"chunk-3","heading":"Ноутбуки","text":". Notebook 6 — Генетические и эволюционные методы оптимизация, генетические алгоритмы и отжиг. Notebook 7 — Нейронные сети персептрон, MLP и основы обучения сети. Notebook 8 — Кластеризация методы группировки данных без учителя.","keywords":["ai","notebook","генетические","методы","mdx.","mlp","алгоритмы","без","группировки","данных","интеллекту","искусственному"],"topics":["ai","algorithms","clustering"]},{"id":"chunk-4","heading":"Как читать раздел","text":"Начните с Notebook 1 и Notebook 2 , если нужно выровнять базу по Python и обработке данных. Для классического ML переходите к Notebook 3 , Notebook 4 и Notebook 5 . Темы оптимизации и более продвинутых подходов собраны в Notebook 6 , Notebook 7 и Notebook 8 .","keywords":["notebook","ai","mdx.","ml","python","базу","более","выровнять","данных.","если","интеллекту","искусственному"],"topics":["python","ai","oop"]}]},{"id":"ai/notebook-01-python-basics","href":"/docs/ai/notebook-01-python-basics","slug":["ai","notebook-01-python-basics"],"section":"ai","sectionTitle":"AI","title":"AI Notebook 1 — Основа Python","description":"Типы данных, условия, циклы и старт работы с NumPy.","preview":"Основа Python. Библиотеки. Дата 25.02.2023 1.1. Теоретический материал – Типы данных Типы данных Все типы данных в Python относятся к одной из 2 х категорий: изменяемые (mutable) и неизменяемые (immutable). Неизменяемые объекты: исловые данные (int, float), bo","keywords":["print","python","type","задача","10","elif","dev","от","plt.plot","range","данных","apple","df","if","import","rng","trapz","массива"],"topics":["python","ai","numpy","knn"],"chunks":[{"id":"chunk-0","heading":"","text":"Типы данных Все типы данных в Python относятся к одной из 2 х категорий: изменяемые (mutable) и неизменяемые (immutable). Неизменяемые объекты: исловые данные (int, float), bool, None, символьные строки (class 'str'), кортежи (tuple). Изменяемые объекты: списки (list), множества (set), словари (dict).","keywords":["ai","данных","типы","python","изменяемые","неизменяемые","объекты","bool","class","dict","float","immutable"],"topics":["python"]},{"id":"chunk-1","heading":"","text":"python x= 3+5.2 7 y= None z= 'a',5,12.345, (2,'b') df= [['Антонова Антонина',34,'ж'],['Борисов Борис',26,'м']] A={1,'title',2,'content'} print(x,' ',type(x),'\\n',y,' ',type(y),'\\n',df,' ',type(df),'\\n',A,' ',type(A),'\\n')","keywords":["type","ai","df","python","12.345","26","3+5.2","34","content","none","notebook","numpy."],"topics":["python"]},{"id":"chunk-2","heading":"","text":"code 39.4 None [['Антонова Антонина', 34, 'ж'], ['Борисов Борис', 26, 'м']] {'content', 1, 2, 'title'}","keywords":["ai","26","34","39.4","code","content","none","notebook","numpy.","python","title","антонина"],"topics":[]},{"id":"chunk-3","heading":"","text":"python x=5 =2 A={ 1,3,7,8} B={2,4,5,10,'apple'} C=A&B df= 'Антонова Антонина',34,'ж' z='type' D=[1,'title',2,'connect'] print(x,' ',type(x),'\\n',A,' ',type(A),'\\n',B,' ',type(B),'\\n',C,' ',type(C),'\\n',df,' ',type(df),'\\n',z,' ',type(z),'\\n',D,' ',type(D),'\\n')","keywords":["type","ai","df","python","10","34","apple","connect","notebook","numpy.","print","title"],"topics":["python"]},{"id":"chunk-4","heading":"","text":"text True {8, 1, 3, 7} {2, 'apple', 4, 5, 10} set() ('Антонова Антонина', 34, 'ж') type [1, 'title', 2, 'connect']","keywords":["ai","10","34","apple","connect","notebook","numpy.","python","set","text","title","true"],"topics":[]},{"id":"chunk-5","heading":"","text":"В коде часто приходится проверять выполнимость или невыполнимость каких то условий. Синтаксис:","keywords":["ai","notebook","numpy.","python","выполнимость","данных","каких","коде","невыполнимость","основа","приходится","проверять"],"topics":[]},{"id":"chunk-6","heading":"","text":"Обратите внимание, что код, который должен выполняться внутри каждого условия, записывается с отступом в 4 пробела от уровня if, elif и else: в питоне области видимости переменных обозначаются отступами.","keywords":["ai","условия","elif","else","if","notebook","numpy.","python","видимости","внимание","внутри","выполняться"],"topics":["python"]},{"id":"chunk-7","heading":"","text":"То есть, отступы позволяют понять, где начинается код, который должен выполняться при выполнении условия в if, и где заканчивается.","keywords":["ai","условия","if","notebook","numpy.","python","выполнении","выполняться","данных","должен","заканчивается.","код"],"topics":[]},{"id":"chunk-8","heading":"","text":"Задача:Вывести на экран является ли переменная х положительной, отрицательной или равна нулю.","keywords":["ai","notebook","numpy.","python","вывести","данных","задача","ли","нулю.","основа","отрицательной","переменная"],"topics":[]},{"id":"chunk-9","heading":"","text":"python x=125 if x<0: print('x отрицательный') elif x==0: print('x равен 0') else: print('x положительный')","keywords":["ai","print","python","125","elif","else","if","notebook","numpy.","данных","основа","отрицательный"],"topics":["python"]},{"id":"chunk-10","heading":"","text":"Задача: Напишите код. Задается х, напечатать какому из интервалов принадлежит: ( infinity, 5), [ 5, 5] или от (5, +infinity)","keywords":["ai","+infinity","infinity","notebook","numpy.","python","данных","задается","задача","интервалов","какому","код."],"topics":[]},{"id":"chunk-11","heading":"","text":"python x=int(input()) if x < 5: print('x пренадлежит интервалу от бесконечности до 5') elif 5