From f744cf797000af3cb7b5ad3167ea7c18403e7033 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 23 Sep 2026 16:27:32 +0500 Subject: [PATCH 1/5] Next.js scaffold: home page in tk/ru/en, static export --- .gitignore | 5 + 404.html | 33 - app/(tk)/layout.tsx | 12 + app/(tk)/page.tsx | 7 + app/[lang]/layout.tsx | 21 + app/[lang]/page.tsx | 16 + assets/site.css => app/globals.css | 17 +- assets/site.js | 69 -- components/Footer.tsx | 16 + components/Header.tsx | 35 ++ components/HomePage.tsx | 156 +++++ components/home.css | 68 ++ content/home.ts | 161 +++++ content/people.ts | 58 ++ content/site.ts | 41 ++ index.html | 350 ----------- lib/document.tsx | 26 + lib/i18n.ts | 27 + next.config.ts | 12 + package-lock.json | 590 ++++++++++++++++++ package.json | 20 + projects/index.html | 350 ----------- public/.nojekyll | 0 .../assets}/apple-touch-icon.png | Bin {assets => public/assets}/blueplayer.png | Bin {assets => public/assets}/hytay-dili-1.png | Bin {assets => public/assets}/inlis-dili-1.png | Bin {assets => public/assets}/kopri.png | Bin {assets => public/assets}/kust.png | Bin {assets => public/assets}/og.png | Bin {assets => public/assets}/ykjam-terjime.png | Bin .../hytay-dili-1}/privacy-policy.html | 0 icon.png => public/icon.png | Bin logo.png => public/logo.png | Bin logo.svg => public/logo.svg | 0 robots.txt => public/robots.txt | 0 sitemap.xml => public/sitemap.xml | 0 make_logo_svg.py => scripts/make_logo_svg.py | 0 tsconfig.json | 41 ++ ykjam-terjime/privacy-policy.html | 36 -- 40 files changed, 1318 insertions(+), 849 deletions(-) create mode 100644 .gitignore delete mode 100644 404.html create mode 100644 app/(tk)/layout.tsx create mode 100644 app/(tk)/page.tsx create mode 100644 app/[lang]/layout.tsx create mode 100644 app/[lang]/page.tsx rename assets/site.css => app/globals.css (82%) delete mode 100644 assets/site.js create mode 100644 components/Footer.tsx create mode 100644 components/Header.tsx create mode 100644 components/HomePage.tsx create mode 100644 components/home.css create mode 100644 content/home.ts create mode 100644 content/people.ts create mode 100644 content/site.ts delete mode 100644 index.html create mode 100644 lib/document.tsx create mode 100644 lib/i18n.ts create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 projects/index.html create mode 100644 public/.nojekyll rename {assets => public/assets}/apple-touch-icon.png (100%) rename {assets => public/assets}/blueplayer.png (100%) rename {assets => public/assets}/hytay-dili-1.png (100%) rename {assets => public/assets}/inlis-dili-1.png (100%) rename {assets => public/assets}/kopri.png (100%) rename {assets => public/assets}/kust.png (100%) rename {assets => public/assets}/og.png (100%) rename {assets => public/assets}/ykjam-terjime.png (100%) rename {hytay-dili-1 => public/hytay-dili-1}/privacy-policy.html (100%) rename icon.png => public/icon.png (100%) rename logo.png => public/logo.png (100%) rename logo.svg => public/logo.svg (100%) rename robots.txt => public/robots.txt (100%) rename sitemap.xml => public/sitemap.xml (100%) rename make_logo_svg.py => scripts/make_logo_svg.py (100%) create mode 100644 tsconfig.json delete mode 100644 ykjam-terjime/privacy-policy.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29c353c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.next/ +out/ +next-env.d.ts +*.tsbuildinfo diff --git a/404.html b/404.html deleted file mode 100644 index e8bb569..0000000 --- a/404.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - - -404 — Şapak Apps - - - - - -
- şapak -

Sahypa tapylmady · Страница не найдена · Page not found

-

Bu salgy boýunça sahypa ýok. Baş sahypa geçiň ýa-da GitHub-daky repozitoriýalara serediň.

-

Страницы по этому адресу нет. Перейдите на главную или посмотрите репозитории на GitHub.

-

There is no page at this address. Go to the home page or look at the repositories on GitHub.

- Baş sahypa · Главная · Home → -
- - diff --git a/app/(tk)/layout.tsx b/app/(tk)/layout.tsx new file mode 100644 index 0000000..660ca9f --- /dev/null +++ b/app/(tk)/layout.tsx @@ -0,0 +1,12 @@ +import type { Metadata, Viewport } from "next"; +import type { ReactNode } from "react"; +import { Document, baseMetadata, baseViewport } from "@/lib/document"; +import "../globals.css"; + +// Туркменский — язык по умолчанию, живёт в корне сайта без префикса. +export const metadata: Metadata = baseMetadata; +export const viewport: Viewport = baseViewport; + +export default function TurkmenLayout({ children }: { children: ReactNode }) { + return {children}; +} diff --git a/app/(tk)/page.tsx b/app/(tk)/page.tsx new file mode 100644 index 0000000..cbcd3bf --- /dev/null +++ b/app/(tk)/page.tsx @@ -0,0 +1,7 @@ +import { HomePage, homeMetadata } from "@/components/HomePage"; + +export const metadata = homeMetadata("tk"); + +export default function Page() { + return ; +} diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx new file mode 100644 index 0000000..11596d2 --- /dev/null +++ b/app/[lang]/layout.tsx @@ -0,0 +1,21 @@ +import type { Metadata, Viewport } from "next"; +import type { ReactNode } from "react"; +import { notFound } from "next/navigation"; +import { Document, baseMetadata, baseViewport } from "@/lib/document"; +import { PREFIXED_LANGS, isLang } from "@/lib/i18n"; +import "../globals.css"; + +// Статический экспорт: собираются только /ru/ и /en/, других языков нет. +export const dynamicParams = false; +export function generateStaticParams() { + return PREFIXED_LANGS.map((lang) => ({ lang })); +} + +export const metadata: Metadata = baseMetadata; +export const viewport: Viewport = baseViewport; + +export default async function LangLayout({ children, params }: { children: ReactNode; params: Promise<{ lang: string }> }) { + const { lang } = await params; + if (!isLang(lang)) notFound(); + return {children}; +} diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx new file mode 100644 index 0000000..f4293ee --- /dev/null +++ b/app/[lang]/page.tsx @@ -0,0 +1,16 @@ +import { notFound } from "next/navigation"; +import { HomePage, homeMetadata } from "@/components/HomePage"; +import { isLang } from "@/lib/i18n"; + +type Props = { params: Promise<{ lang: string }> }; + +export async function generateMetadata({ params }: Props) { + const { lang } = await params; + return isLang(lang) ? homeMetadata(lang) : {}; +} + +export default async function Page({ params }: Props) { + const { lang } = await params; + if (!isLang(lang)) notFound(); + return ; +} diff --git a/assets/site.css b/app/globals.css similarity index 82% rename from assets/site.css rename to app/globals.css index 444c11c..b0d037c 100644 --- a/assets/site.css +++ b/app/globals.css @@ -1,4 +1,4 @@ -/* Şapak Apps — общие стили всех страниц. Стили конкретной страницы — в её - - -
- -
- - şapak - - -
- - - -
-
- -
- -
-

Open Source Turkmenistan

-

- Türkmenistanda döredilýän açyk kodly programma üpjünçiligi - Open-source software built in Turkmenistan - Открытое программное обеспечение, созданное в Туркменистане -

-

- Şapak Apps — türkmen dilinde işleýän, açyk kodly we mugt programmalar döredýän jemgyýet. Kod GitHub-da, programmalar dükanlarda. - Şapak Apps is a community that builds free, open-source software in Turkmen. The code is on GitHub. The apps are in the stores. - Şapak Apps — сообщество, которое создаёт бесплатные программы с открытым кодом на туркменском языке. Код — на GitHub, приложения — в магазинах. -

- - -
- -
-

MaksadymyzMissionЦели

-
-

Türkmen dilinde programma üpjünçiligi az. Dünýä programmalary iňlis dilinden öwredýär, rus programmalary rus dilinden. Türkmen ulanyjy üçin ýazylany welin az.

-

Biz şol boşlugy doldurýarys — okuw taslamalary bilen däl-de, hakykatdan hem goýberilýän programmalar bilen. Ýazan kodyň dükandaky programma düşýär.

-

Açyk kod bu ýerde goşmaça däl, ikinji önüm: türkmen dilinde kod barlagy geçirilýän janly programmada ilkinji Pull Request etmäge başga ýer ýok.

-

There is very little software in Turkmen. Global apps teach from English, Russian apps teach from Russian. Almost nobody builds for a Turkmen speaker.

-

We fill that gap — not with tutorial projects, but with apps that are really released. The code you write ends up in an app people install.

-

Open code here is not an extra, it is the second product: there is no other place to make a first pull request into a live app with code review in Turkmen.

-

Программного обеспечения на туркменском языке почти нет. Мировые приложения учат с английского, российские — с русского. Для туркменоязычного пользователя почти никто не разрабатывает.

-

Мы заполняем этот пробел — не учебными проектами, а приложениями, которые действительно выходят. Код, который вы пишете, попадает в приложение, которое люди устанавливают.

-

Открытый код здесь не дополнение, а второй продукт: больше негде сделать первый pull request в живое приложение с ревью кода на туркменском.

-
-
- -
-

ÝörelgelerPrinciplesПринципы

-
-
-

Açyk kodOpen codeОткрытый код

-

Ähli repozitoriýalar açyk. MIT, Apache-2.0 we GPL-3.0: alyp bolýar, üýtgedip bolýar, öz işiňde ulanyp bolýar.

-

Every repository is public. MIT, Apache-2.0 and GPL-3.0: take it, change it, use it in your own work.

-

Все репозитории публичны. MIT, Apache-2.0 и GPL-3.0: берите, меняйте, используйте в своей работе.

-
-
-

InternetsizOfflineОфлайн

-

Programmalaryň hiç biri internet talap etmeýär. Türkmenistanda internet gymmat we haýal — bu tehniki karar hem, garaýyş hem.

-

None of the apps needs a network. Internet in Turkmenistan is expensive and slow — this is a technical choice and a position.

-

Ни одно из приложений не требует сети. Интернет в Туркменистане дорогой и медленный — это и техническое решение, и позиция.

-
-
-

Mahabatsyz we tölegsizNo ads, no feesБез рекламы и платежей

-

Dükandaky programmalar mugt, mahabatsyz we hiç hili maglumat ýygnamaýar. Gizlinlik syýasatlary aşakda.

-

The apps in the stores are free, without ads, and collect no data at all. Privacy policies are below.

-

Приложения в магазинах бесплатны, без рекламы и не собирают никаких данных. Политики конфиденциальности — ниже.

-
-
-

Türkmen dilindeIn TurkmenНа туркменском

-

Interfeýs türkmen dilinde. Kod barlagy türkmen we rus dillerinde geçirilýär — iňlis diliň gowşak bolsa-da päsgel däl.

-

The interface is in Turkmen. Code review is in Turkmen and Russian — weak English is not a blocker.

-

Интерфейс на туркменском языке. Ревью кода проходит на туркменском и русском — слабый английский не помеха.

-
-
-
- -
-

TaslamalarProjectsПроекты

-
-

Häzir iki ugur bar. Dil öwrediş programmalary — Ykjam Terjime, Hytaý dili 1, Iňlis dili 1 — we gurallar: Köpri, BluePlayer, Küşt. Hemmesi açyk, hemmesi GitHub-da.

-

Esasy ünsümiz dil öwrediş liniýasynda. Hereketlendiriji bir, her täze dil — diňe täze mazmun we täze nyşan, şonuň üçin her indiki programma öňküden çalt çykýar.

-

Öz açyk kodly taslamaň bar bolsa we ony türkmen jemgyýetiniň içinde ösdürmek isleseň — hat ýaz. Repozitoriý guramanyň astyna geçýär, awtorlyk seniňki bolup galýar.

-

There are two lines today. Language learning apps — Ykjam Terjime, Hytaý dili 1, Iňlis dili 1 — and tools: Köpri, BluePlayer, Küşt. All open, all on GitHub.

-

The main focus is the language line. The engine is shared; each new language is only new content and a new icon, so every next app ships faster than the one before.

-

If you have an open-source project and want to grow it inside the Turkmen community — write to us. The repository moves under the organization, the authorship stays yours.

-

Сегодня два направления. Приложения для изучения языков — Ykjam Terjime, Hytaý dili 1, Iňlis dili 1 — и инструменты: Köpri, BluePlayer, Küşt. Всё открыто, всё на GitHub.

-

Главный фокус — языковая линейка. Движок общий, каждый новый язык — только новый контент и новый значок, поэтому каждое следующее приложение выходит быстрее предыдущего.

-

Если у вас есть проект с открытым кодом и вы хотите развивать его внутри туркменского сообщества — напишите нам. Репозиторий переходит под организацию, авторство остаётся за вами.

-
- -
- -
-

AdamlarPeopleЛюди

-
-

Şapak Apps — bir adamyň studiýasy däl. Taslamalar dürli awtorlardan gelýär, hemmesi bir guramada we bir düzgün bilen: açyk kod, açyk meseleler, düşnükli dilde barlag.

-

Şapak Apps is not one person's studio. Projects come from different authors, all in one organization and under the same rules: open code, open issues, review in a language you understand.

-

Şapak Apps — не студия одного человека. Проекты приходят от разных авторов, все в одной организации и по одним правилам: открытый код, открытые задачи, ревью на понятном языке.

-
-
-
-
- -
-

Seydi Charyyev

-

Full Stack JavaScript inženeri · React NativeFull Stack JavaScript Engineer · React NativeFull Stack JavaScript-инженер · React Native

-
-
- -
-
-
- -
-

Aýnazar Sylyýew

-

Programma inženeri · Android, web, kross-platformaSoftware Engineer · Android, web, cross-platformИнженер-программист · Android, веб, кроссплатформа

-
-
- -
-
-
- -
-

Bahram Myradow

-

Fullstack JavaScript inženeri · React NativeFullstack JavaScript Engineer · React NativeFullstack JavaScript-инженер · React Native

-
-
- -
-
-
- -
-

Merdan Jumamyradow

-

Web we mobil programmaçy · JavaScript, Dart, GoWeb and mobile developer · JavaScript, Dart, GoВеб- и мобильный разработчик · JavaScript, Dart, Go

-
-
- -
-
-
- -
-

Ahmed Batyrov

-

Web programmaçy · Node.js, ReactWeb developer · Node.js, ReactВеб-разработчик · Node.js, React

-
-
- -
-
-
- -
-

Goşant goşmakContributingУчастие

-
    -
  1. - Täze gelenler üçin meseleleri aç — olar kiçi we anyk bölünen, ilkinji Pull Request üçin amatly. - Open the issues for newcomers — they are small and clearly scoped, a good place for a first pull request. - Откройте задачи для новичков — они небольшие и чётко очерчены, подходят для первого pull request. -
  2. -
  3. - Repozitoriýany klonla we onuň README-däki gurnama ädimlerini ýerine ýetir. - Clone the repository and follow the setup steps in its README. - Клонируйте репозиторий и выполните шаги установки из его README. -
  4. -
  5. - Kiçi Pull Request aç — uly üýtgetmeden gowy. - Open a small pull request — better than one big change. - Откройте небольшой pull request — это лучше одного большого изменения. -
  6. -
-
-
-

Näme wada berýärisWhat we promiseЧто мы обещаем

-
    -
  • Kod barlagy türkmen we rus dillerinde geçirilýär.Code review is in Turkmen and Russian.Ревью кода проходит на туркменском и русском.
  • -
  • Her Pull Request-e birnäçe günüň içinde jogap berilýär.Every pull request gets an answer within a few days.На каждый pull request отвечаем в течение нескольких дней.
  • -
  • Kabul edilen kod indiki goýberilişe düşýär.Accepted code ships in the next release.Принятый код попадает в следующий релиз.
  • -
-
-
-

HabarlaşmakContactКонтакты

-

shapak.apps@gmail.com — türkmen, rus ýa-da iňlis dilinde ýazyp bolýar.

-

shapak.apps@gmail.com — write in Turkmen, Russian or English.

-

shapak.apps@gmail.com — можно писать на туркменском, русском или английском.

-
-
- -
- -
- - - -
- - - diff --git a/lib/document.tsx b/lib/document.tsx new file mode 100644 index 0000000..774ba6c --- /dev/null +++ b/lib/document.tsx @@ -0,0 +1,26 @@ +import type { Metadata, Viewport } from "next"; +import type { ReactNode } from "react"; +import type { Lang } from "./i18n"; + +/** + * Общее для обеих корневых раскладок: app/(tk)/layout.tsx и app/[lang]/layout.tsx. + * Корневых раскладок две, потому что у каждого языка свой. + */ +export const baseMetadata: Metadata = { + icons: { icon: "/icon.png", apple: "/assets/apple-touch-icon.png" }, +}; + +export const baseViewport: Viewport = { + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#FBFAF8" }, + { media: "(prefers-color-scheme: dark)", color: "#131210" }, + ], +}; + +export function Document({ lang, children }: { lang: Lang; children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/lib/i18n.ts b/lib/i18n.ts new file mode 100644 index 0000000..1554cca --- /dev/null +++ b/lib/i18n.ts @@ -0,0 +1,27 @@ +export const LANGS = ["tk", "ru", "en"] as const; +export type Lang = (typeof LANGS)[number]; + +/** Туркменский живёт в корне (/), остальные — под префиксом (/ru/, /en/). */ +export const DEFAULT_LANG: Lang = "tk"; +export const PREFIXED_LANGS = LANGS.filter((l) => l !== DEFAULT_LANG); + +export const SITE_URL = "https://shapak-apps.github.io"; + +/** Строка на трёх языках. */ +export type L = Record; + +export function isLang(value: string): value is Lang { + return (LANGS as readonly string[]).includes(value); +} + +/** Адрес страницы на нужном языке: localePath("ru", "/projects/") → "/ru/projects/". */ +export function localePath(lang: Lang, path = "/"): string { + return lang === DEFAULT_LANG ? path : `/${lang}${path}`; +} + +/** hreflang-альтернативы для metadata.alternates.languages. */ +export function languageAlternates(path = "/"): Record { + return Object.fromEntries(LANGS.map((l) => [l, localePath(l, path)])); +} + +export const OG_LOCALE: L = { tk: "tk_TM", ru: "ru_RU", en: "en_US" }; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..85baf62 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,12 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + // GitHub Pages отдаёт только статику: собираем готовый HTML в out/. + output: "export", + // /projects/ → out/projects/index.html — адреса как у прежнего сайта. + trailingSlash: true, + // Оптимизатор картинок требует сервер; на Pages его нет. + images: { unoptimized: true }, +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..af5a6bd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,590 @@ +{ + "name": "shapak-apps-site", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "shapak-apps-site", + "version": "0.1.0", + "dependencies": { + "next": "16.3.6", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@types/node": "^22", + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@next/env": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.6.tgz", + "integrity": "sha512-x9Vblze1EbtltQYnNH38xCPWU3TVfBd1eXqA3+w9+BTpedkkdNpAaltXlGQ/nsc1+E0mVTNrtcbX3GoO09zeLQ==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.6.tgz", + "integrity": "sha512-E/7GEqaUkt8mk/T8v9lAnrhzR06kdq1ZBkC12F8tAMkdIadwNp3H1KqHynDHrpcTlGCUdq/qu6vUL2aYVyYBdw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.6.tgz", + "integrity": "sha512-yBE893/nDWTlaiBD1p+qgt7NUen4U5R6FXyH0s67Npq1S3E0cVSef1WIXC2xBRgQvwAvJq6DnS6Y6PrY0cy4Ew==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.6.tgz", + "integrity": "sha512-KJDpjBqBPYlvkivmyrp+Qys6k/7ksbqGQvRVc6ZEGfR+cjQxx+nUkJaWmNZJsmoOrqYNbaXByF8wa0lBwDhB3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.6.tgz", + "integrity": "sha512-mqNg2K+hvWskSRb/QM+Ix412DvBsuSF0XV+frTSw5vmoucNnIlynFwKYew8D01bfATErMOM7Bujrf0BA5DRKFA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.6.tgz", + "integrity": "sha512-nFncBNGAYouRHjRVaITs9beZRfhX4ssVwpnvPIAbkZVH6LtGoAVlH4bJ8Cnf9SOo9bsXgPFer/GdHtEE3JNOkw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.6.tgz", + "integrity": "sha512-5Mf3cHDGR/Iz0ng2Bj3zUR3p5QS9YK3Hn2QiAfavFmyF48zwThAjpFoiTKNIcOHLYS4zEk+gzyJ/9deQ2ZB8yQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.6.tgz", + "integrity": "sha512-0jkJy0C2kbrJWTk4YLa3xk80pVBpx8FCHJym7CnUfDAXe/FWv5qT7SQJbR0KuemyxaEDlEx5WT4VQJoTW+/9Qw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.6.tgz", + "integrity": "sha512-/YXjI1e5OXcZ7YpxRwgP/1jAV/SBKTzeVKqN2mk7mLpcICsyn3Gl5+dIfDTJp70M0ccMhyMMRso4v6mPDCGepg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/node": { + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.25.tgz", + "integrity": "sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "16.3.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.6.tgz", + "integrity": "sha512-L+otWM/aQbYTx98aZhgEoMb4bZAXx1YVW4UMA/vuCyCoWG5HJyZUili8QAkqzrcC+5///tsz3s0M+SlyB5bLMw==", + "license": "MIT", + "dependencies": { + "@next/env": "16.3.6", + "@swc/helpers": "0.5.23", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.5.23", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.3.6", + "@next/swc-darwin-x64": "16.3.6", + "@next/swc-linux-arm64-gnu": "16.3.6", + "@next/swc-linux-arm64-musl": "16.3.6", + "@next/swc-linux-x64-gnu": "16.3.6", + "@next/swc-linux-x64-musl": "16.3.6", + "@next/swc-win32-arm64-msvc": "16.3.6", + "@next/swc-win32-x64-msvc": "16.3.6", + "sharp": "^0.35.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.0" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/@img/sharp-darwin-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-darwin-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-freebsd-wasm32": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-ppc64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-riscv64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-s390x": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linux-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-arm": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-ppc64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-riscv64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-s390x": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linux-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linuxmusl-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-linuxmusl-x64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-webcontainers-wasm32": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-win32-arm64": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-win32-ia32": { + "optional": true + }, + "node_modules/sharp/node_modules/@img/sharp-win32-x64": { + "optional": true + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f77d4da --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "shapak-apps-site", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build" + }, + "dependencies": { + "next": "16.3.6", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@types/node": "^22", + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "^5" + } +} diff --git a/projects/index.html b/projects/index.html deleted file mode 100644 index 1996efc..0000000 --- a/projects/index.html +++ /dev/null @@ -1,350 +0,0 @@ - - - - - -Taslamalar — Şapak Apps - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - şapak - - -
- - - -
-
- -
- -
-

Open Source Turkmenistan

-

TaslamalarProjectsПроекты

-

- Alty açyk taslama, iki ugur. Ikisi dükanlarda, hemmesi GitHub-da. Her biriniň öz awtory, ygtyýarnamasy we açyk meseleleri bar. - Six open projects in two lines. Two are in the stores, all are on GitHub. Each has its own author, license and open issues. - Шесть открытых проектов в двух направлениях. Два — в магазинах, все — на GitHub. У каждого свой автор, лицензия и открытые задачи. -

-
- -
-

Dil öwrenmekLanguage learningИзучение языков

-
- -
-
- -
-

Ykjam Terjime

- DükanlardaIn the storesВ магазинах -
-
-

- Türkmen gepleşik kitaby we terjimeçi. Hakyky ýagdaýlar üçin taýýar sözlemler, türkmen harplary klawiaturasyz. Web wersiýasy brauzerde açylýar. - Turkmen phrasebook and translator. Ready phrases for real situations, Turkmen letters without a special keyboard. The web version opens in the browser. - Туркменский разговорник и переводчик. Готовые фразы для реальных ситуаций, туркменские буквы без специальной клавиатуры. Веб-версия открывается в браузере. -

-
- 31 dil31 languages31 язык - internetsizofflineофлайн - MIT -
-
React Native · Expo · TypeScript
@TheSeydiCharyyev
- -
- -
-
- -
-

Hytaý dili 1

- App Store-da · Google Play ýakyndaOn the App Store · Google Play soonВ App Store · скоро в Google Play -
-
-

- Türkmen dilinden hytaý dili. Her bapda teoriýa, gönükmeler we synag; indiki bap synagdan soň açylýar. Doly oflaýn. - Chinese from Turkmen. Every chapter has theory, exercises and an exam; the next chapter opens after the exam. Fully offline. - Китайский с туркменского. В каждой главе теория, упражнения и экзамен; следующая глава открывается после экзамена. Полностью офлайн. -

-
- 31 bap31 chapters31 глава - 600 gönükme600 exercises600 упражнений - MIT -
-
React Native · Expo · TypeScript
@TheSeydiCharyyev
- -
- -
-
- -
-

Iňlis dili 1

- TaýýarlanýarIn developmentВ разработке -
-
-

- Türkmen dilinden iňlis dili, A1 derejesi. Hytaý dili 1 bilen bir hereketlendirijide: şol bir baplar, synaglar we ösüş. - English from Turkmen, level A1. Same engine as Hytaý dili 1: the same chapters, exams and progress. - Английский с туркменского, уровень A1. Тот же движок, что у Hytaý dili 1: те же главы, экзамены и прогресс. -

-
- A1 - 20 bap20 chapters20 глав - MIT -
-
React Native · Expo · TypeScript
@TheSeydiCharyyev
- -
- -
-
- -
-

GurallarToolsИнструменты

-
- -
-
- -
-

Köpri

- TaýýarlanýarIn developmentВ разработке -
-
-

- Android üçin oflaýn terjimeçi: rus we iňlis dillerinden türkmen diline oflaýn sözlük, beýleki jübütler üçin onlaýn hyzmatlar, uly köpdilli gepleşik kitaby. Kamera arkaly terjime taýýarlanýar. - Offline translator for Android: an offline dictionary from Russian and English to Turkmen, online services for other pairs, a large multilingual phrasebook. Camera translation is coming. - Офлайн-переводчик для Android: офлайн-словарь с русского и английского на туркменский, онлайн-сервисы для остальных пар, большой многоязычный разговорник. Готовится перевод через камеру. -

-
- 50+ dil50+ languages50+ языков - Apache-2.0 -
-
Flutter · Dart
@aynazar-sylyyew-dev
- -
- -
-
- -
-

BluePlayer

- TaýýarlanýarIn developmentВ разработке -
-
-

- Android üçin döwrebap aýdym pleýeri. Kotlin we natiw C++. - Modern music player for Android. Kotlin and native C++. - Современный музыкальный плеер для Android. Kotlin и нативный C++. -

-
- Kotlin - C++ - Apache-2.0 -
- - -
- -
-
- -
-

Küşt

- TaýýarlanýarIn developmentВ разработке -
-
-

- Küşt öwrenmek, türgenleşmek we oýnamak üçin programma. Stockfish botlaryna garşy oýun, oýundan soň derňew: her göçüm bahalandyrylýar we düşündirilýär. - A chess app to learn, practice and play. Games against Stockfish bots, and post-game analysis: every move is classified and explained. - Шахматное приложение: учиться, тренироваться, играть. Партии против ботов на Stockfish и разбор после игры: каждый ход оценивается и объясняется. -

-
- Stockfish - GPL-3.0 -
-
Flutter · Dart
@fihdeveloper
- -
- -
-
- -
-

- Öz açyk kodly taslamaň bar bolsa we ony türkmen jemgyýetiniň içinde ösdürmek isleseň — hat ýaz. Repozitoriý guramanyň astyna geçýär, awtorlyk seniňki bolup galýar. - If you have an open-source project and want to grow it inside the Turkmen community — write to us. The repository moves under the organization, the authorship stays yours. - Если у вас есть проект с открытым кодом и вы хотите развивать его внутри туркменского сообщества — напишите нам. Репозиторий переходит под организацию, авторство остаётся за вами. -

- -
- -
- - - -
- - - diff --git a/public/.nojekyll b/public/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/assets/apple-touch-icon.png b/public/assets/apple-touch-icon.png similarity index 100% rename from assets/apple-touch-icon.png rename to public/assets/apple-touch-icon.png diff --git a/assets/blueplayer.png b/public/assets/blueplayer.png similarity index 100% rename from assets/blueplayer.png rename to public/assets/blueplayer.png diff --git a/assets/hytay-dili-1.png b/public/assets/hytay-dili-1.png similarity index 100% rename from assets/hytay-dili-1.png rename to public/assets/hytay-dili-1.png diff --git a/assets/inlis-dili-1.png b/public/assets/inlis-dili-1.png similarity index 100% rename from assets/inlis-dili-1.png rename to public/assets/inlis-dili-1.png diff --git a/assets/kopri.png b/public/assets/kopri.png similarity index 100% rename from assets/kopri.png rename to public/assets/kopri.png diff --git a/assets/kust.png b/public/assets/kust.png similarity index 100% rename from assets/kust.png rename to public/assets/kust.png diff --git a/assets/og.png b/public/assets/og.png similarity index 100% rename from assets/og.png rename to public/assets/og.png diff --git a/assets/ykjam-terjime.png b/public/assets/ykjam-terjime.png similarity index 100% rename from assets/ykjam-terjime.png rename to public/assets/ykjam-terjime.png diff --git a/hytay-dili-1/privacy-policy.html b/public/hytay-dili-1/privacy-policy.html similarity index 100% rename from hytay-dili-1/privacy-policy.html rename to public/hytay-dili-1/privacy-policy.html diff --git a/icon.png b/public/icon.png similarity index 100% rename from icon.png rename to public/icon.png diff --git a/logo.png b/public/logo.png similarity index 100% rename from logo.png rename to public/logo.png diff --git a/logo.svg b/public/logo.svg similarity index 100% rename from logo.svg rename to public/logo.svg diff --git a/robots.txt b/public/robots.txt similarity index 100% rename from robots.txt rename to public/robots.txt diff --git a/sitemap.xml b/public/sitemap.xml similarity index 100% rename from sitemap.xml rename to public/sitemap.xml diff --git a/make_logo_svg.py b/scripts/make_logo_svg.py similarity index 100% rename from make_logo_svg.py rename to scripts/make_logo_svg.py diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e7ff3a2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/ykjam-terjime/privacy-policy.html b/ykjam-terjime/privacy-policy.html deleted file mode 100644 index 7ef0186..0000000 --- a/ykjam-terjime/privacy-policy.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Privacy Policy — Ykjam Terjime - - - -

Privacy Policy — Ykjam Terjime

-

Effective date: 13 August 2026 · Developer: Seydi Charyyev (Şapak Apps)

- -

Summary

-

Ykjam Terjime does not collect any data. The app has no accounts, - no registration, no analytics and no advertising.

- -

Data collection

-

We do not collect, store or share any personal information. Your settings and - favorites are stored only on your device and never leave it.

- -

Türkmençe gysgaça

-

Ykjam Terjime hiç hili maglumat ýygnamaýar. Hasap açmak talap edilmeýär, mahabat - we analitika ýok. Sazlamalaryňyz diňe öz enjamyňyzda saklanýar.

- -

Contact

-

Questions about this policy: shapak.apps@gmail.com

-

← Şapak Apps

- - From ce54e715be9968f0cb30d314ef4600edca5d3ab8 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 23 Sep 2026 16:34:09 +0500 Subject: [PATCH 2/5] Projects page in tk/ru/en with live star counts --- app/(tk)/projects/page.tsx | 7 ++ app/[lang]/projects/page.tsx | 16 ++++ components/HomePage.tsx | 24 ++--- components/ProjectsPage.tsx | 106 ++++++++++++++++++++++ components/RevealOnScroll.tsx | 36 ++++++++ components/StarCount.tsx | 55 ++++++++++++ components/projects.css | 93 ++++++++++++++++++++ content/projects.ts | 160 ++++++++++++++++++++++++++++++++++ lib/seo.ts | 38 ++++++++ 9 files changed, 518 insertions(+), 17 deletions(-) create mode 100644 app/(tk)/projects/page.tsx create mode 100644 app/[lang]/projects/page.tsx create mode 100644 components/ProjectsPage.tsx create mode 100644 components/RevealOnScroll.tsx create mode 100644 components/StarCount.tsx create mode 100644 components/projects.css create mode 100644 content/projects.ts create mode 100644 lib/seo.ts diff --git a/app/(tk)/projects/page.tsx b/app/(tk)/projects/page.tsx new file mode 100644 index 0000000..f44ecca --- /dev/null +++ b/app/(tk)/projects/page.tsx @@ -0,0 +1,7 @@ +import { ProjectsPage, projectsMetadata } from "@/components/ProjectsPage"; + +export const metadata = projectsMetadata("tk"); + +export default function Page() { + return ; +} diff --git a/app/[lang]/projects/page.tsx b/app/[lang]/projects/page.tsx new file mode 100644 index 0000000..97235c4 --- /dev/null +++ b/app/[lang]/projects/page.tsx @@ -0,0 +1,16 @@ +import { notFound } from "next/navigation"; +import { ProjectsPage, projectsMetadata } from "@/components/ProjectsPage"; +import { isLang } from "@/lib/i18n"; + +type Props = { params: Promise<{ lang: string }> }; + +export async function generateMetadata({ params }: Props) { + const { lang } = await params; + return isLang(lang) ? projectsMetadata(lang) : {}; +} + +export default async function Page({ params }: Props) { + const { lang } = await params; + if (!isLang(lang)) notFound(); + return ; +} diff --git a/components/HomePage.tsx b/components/HomePage.tsx index 9280a74..02b4378 100644 --- a/components/HomePage.tsx +++ b/components/HomePage.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; -import { LANGS, OG_LOCALE, SITE_URL, languageAlternates, localePath, type Lang } from "@/lib/i18n"; +import { SITE_URL, localePath, type Lang } from "@/lib/i18n"; +import { pageMetadata } from "@/lib/seo"; import { EMAIL, NEWCOMER_ISSUES_URL, ORG_URL, orgOnGitHub } from "@/content/site"; import { members } from "@/content/people"; import { contribute, hero, meta, mission, people, principles, projectsIntro, stats } from "@/content/home"; @@ -8,24 +9,13 @@ import { Footer } from "./Footer"; import "./home.css"; export function homeMetadata(lang: Lang): Metadata { - const url = localePath(lang, "/"); - return { - metadataBase: new URL(SITE_URL), + return pageMetadata({ + lang, + path: "/", title: meta.title[lang], description: meta.description[lang], - alternates: { canonical: url, languages: languageAlternates("/") }, - openGraph: { - type: "website", - siteName: "Şapak Apps", - title: meta.ogTitle, - description: meta.description[lang], - url, - locale: OG_LOCALE[lang], - alternateLocale: LANGS.filter((l) => l !== lang).map((l) => OG_LOCALE[l]), - images: [{ url: "/assets/og.png", width: 1200, height: 630 }], - }, - twitter: { card: "summary_large_image", title: meta.ogTitle, description: meta.description[lang], images: ["/assets/og.png"] }, - }; + ogTitle: meta.ogTitle, + }); } const orgJsonLd = { diff --git a/components/ProjectsPage.tsx b/components/ProjectsPage.tsx new file mode 100644 index 0000000..a8d8fcd --- /dev/null +++ b/components/ProjectsPage.tsx @@ -0,0 +1,106 @@ +import type { Metadata } from "next"; +import type { CSSProperties } from "react"; +import type { Lang } from "@/lib/i18n"; +import { pageMetadata } from "@/lib/seo"; +import { EMAIL, NEWCOMER_ISSUES_URL } from "@/content/site"; +import { contribute } from "@/content/home"; +import { after, giveStar, groups, hero, meta, type Project } from "@/content/projects"; +import { Header } from "./Header"; +import { Footer } from "./Footer"; +import { StarCount } from "./StarCount"; +import { RevealOnScroll } from "./RevealOnScroll"; +import "./projects.css"; + +export function projectsMetadata(lang: Lang): Metadata { + return pageMetadata({ + lang, + path: "/projects/", + title: meta.title[lang], + description: meta.description[lang], + ogTitle: meta.ogTitle[lang], + }); +} + +/** Каскад появления внутри ряда: 0, .08s, .16s — как на прежнем сайте. */ +const DELAYS = [undefined, ".08s", ".16s"]; + +function ProjectCard({ project: p, lang, index }: { project: Project; lang: Lang; index: number }) { + const repoUrl = `https://github.com/Shapak-Apps/${p.repo}`; + const delay = DELAYS[index % DELAYS.length]; + return ( + + ); +} + +export function ProjectsPage({ lang }: { lang: Lang }) { + return ( +
+
+ +
+
+

{hero.eyebrow}

+

{hero.title[lang]}

+

{hero.lead[lang]}

+
+ + {groups.map((g) => ( +
+

{g.title[lang]}

+
+ {g.projects.map((p, i) => ( + + ))} +
+
+ ))} + + +
+ +
+ +
+ ); +} diff --git a/components/RevealOnScroll.tsx b/components/RevealOnScroll.tsx new file mode 100644 index 0000000..768a0b0 --- /dev/null +++ b/components/RevealOnScroll.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { useEffect } from "react"; + +/** + * Элементы `.reveal` ниже первого экрана плавно появляются при прокрутке. + * То, что уже видно, не трогаем — ничего не мигает после загрузки JS. + * Без JS и при prefers-reduced-motion всё видно сразу. + */ +export function RevealOnScroll({ selector = ".reveal" }: { selector?: string }) { + useEffect(() => { + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; + if (!("IntersectionObserver" in window)) return; + + const below = Array.from(document.querySelectorAll(selector)).filter( + (el) => el.getBoundingClientRect().top > window.innerHeight, + ); + if (!below.length) return; + + below.forEach((el) => el.classList.add("pending")); + const io = new IntersectionObserver( + (entries) => { + entries.forEach((e) => { + if (!e.isIntersecting) return; + e.target.classList.remove("pending"); + io.unobserve(e.target); + }); + }, + { rootMargin: "0px 0px -8% 0px", threshold: 0.1 }, + ); + below.forEach((el) => io.observe(el)); + return () => io.disconnect(); + }, [selector]); + + return null; +} diff --git a/components/StarCount.tsx b/components/StarCount.tsx new file mode 100644 index 0000000..76fb606 --- /dev/null +++ b/components/StarCount.tsx @@ -0,0 +1,55 @@ +"use client"; + +import { useEffect, useState } from "react"; + +// Тот же ключ, что у прежнего сайта: накопленный кеш посетителей не теряется. +const KEY = "shapak-stars"; +const TTL = 60 * 60 * 1000; + +type Cache = Record; + +function readCache(): Cache { + try { + return JSON.parse(localStorage.getItem(KEY) || "{}"); + } catch { + return {}; + } +} + +function writeCache(repo: string, v: number) { + try { + const cache = readCache(); + cache[repo] = { v, t: Date.now() }; + localStorage.setItem(KEY, JSON.stringify(cache)); + } catch {} +} + +/** + * Живое число звёзд репозитория. Сначала показывает кеш (даже устаревший), + * в сеть идёт, только если кеш старше часа. При ошибке — пусто, без числа: + * анонимный лимит GitHub API — 60 запросов в час на IP. + */ +export function StarCount({ repo }: { repo: string }) { + const [stars, setStars] = useState(null); + + useEffect(() => { + const cached = readCache()[repo]; + if (cached) setStars(cached.v); + if (cached && Date.now() - cached.t < TTL) return; + + let alive = true; + fetch(`https://api.github.com/repos/${repo}`, { headers: { Accept: "application/vnd.github+json" } }) + .then((r) => (r.ok ? r.json() : null)) + .then((d) => { + if (!d || typeof d.stargazers_count !== "number") return; + writeCache(repo, d.stargazers_count); + if (alive) setStars(d.stargazers_count); + }) + .catch(() => {}); + return () => { + alive = false; + }; + }, [repo]); + + return {stars === null ? null : `★ ${stars}`}; +} diff --git a/components/projects.css b/components/projects.css new file mode 100644 index 0000000..c2f9727 --- /dev/null +++ b/components/projects.css @@ -0,0 +1,93 @@ +/* Страница проектов. Общие стили — app/globals.css. */ +/* ---------- заголовок страницы ---------- */ +.page-hero{padding:72px 0 24px} +.page-hero h1{ + margin:0; font-size:clamp(34px,4.6vw,58px); font-weight:500; + line-height:1.05; letter-spacing:-.03em; +} +.page-hero .lead{margin:22px 0 0; color:var(--muted); font-size:19px; max-width:58ch} + +/* ---------- группы ---------- */ +.group{padding:56px 0 0} +.group h2{ + font-family:var(--mono); font-size:11px; letter-spacing:.15em; text-transform:uppercase; + color:var(--muted); font-weight:500; margin:0 0 24px; + padding-bottom:14px; border-bottom:1px solid var(--rule); +} +.projects{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:24px} + +/* ---------- карточка проекта ---------- */ +.project{ + border:1px solid var(--rule); border-radius:14px; padding:24px; + display:flex; flex-direction:column; gap:18px; + transition:border-color .3s ease, transform .3s ease; +} +.project:hover{border-color:var(--fg); transform:translateY(-2px)} +.project .head{display:flex; gap:16px; align-items:center} +.project .icon{ + width:64px; height:64px; border-radius:18px; flex:none; display:block; + transition:transform .45s cubic-bezier(.2,.8,.2,1); +} +.project:hover .icon{transform:scale(1.06) rotate(-2deg)} +.project h3{margin:0; font-size:20px; font-weight:600; letter-spacing:-.02em} +.status{ + display:inline-flex; align-items:center; gap:8px; margin-top:6px; + font-family:var(--mono); font-size:10.5px; letter-spacing:.09em; + text-transform:uppercase; color:var(--muted); +} +.status.live{color:var(--accent)} +.status.live::before{ + content:""; width:7px; height:7px; border-radius:50%; background:var(--brand); + animation:pulse 2.4s ease-out infinite; +} +@keyframes pulse{ + 0%{box-shadow:0 0 0 0 rgba(255,133,27,.45)} + 70%{box-shadow:0 0 0 9px rgba(255,133,27,0)} + 100%{box-shadow:0 0 0 0 rgba(255,133,27,0)} +} +.project p{margin:0; color:var(--muted); font-size:15.5px; flex:1} +.nums{display:flex; flex-wrap:wrap; gap:8px} +.nums span{ + font-family:var(--mono); font-size:11.5px; letter-spacing:.04em; + padding:5px 9px; border-radius:6px; background:var(--accent-soft); +} +.facts{font-family:var(--mono); font-size:11.5px; color:var(--muted); letter-spacing:.02em; line-height:1.9} +.facts a{text-decoration:none; border-bottom:1px solid var(--rule)} +.facts a:hover{color:var(--fg)} +.links{ + display:flex; gap:18px; flex-wrap:wrap; font-size:14.5px; + padding-top:16px; border-top:1px solid var(--rule); +} +.links a{text-decoration:none; border-bottom:1px solid var(--rule); padding-bottom:1px} +.links a:hover{border-bottom-color:var(--accent); color:var(--accent)} +.star .n{color:var(--fg); font-weight:600} +.star .n:empty{display:none} +.star .n::after{content:" · "; font-weight:400; color:var(--muted)} + +/* ---------- появление при прокрутке ---------- */ +/* Прячутся только карточки ниже первого экрана (класс .pending ставит RevealOnScroll). + Без JS, до загрузки JS и при reduced motion всё видно сразу. */ +.project.reveal{ + transition:opacity .7s ease, transform .7s cubic-bezier(.2,.8,.2,1); + transition-delay:var(--d,0s); +} +.project.reveal.pending{opacity:0; transform:translateY(16px); transition:none} + +/* ---------- завершение ---------- */ +.after{padding:64px 0 0} +.after p{margin:0; font-size:16.5px; max-width:60ch} + +@media (prefers-reduced-motion:reduce){ + .project.reveal,.project.reveal.pending{opacity:1; transform:none; transition:none} + .status.live::before{animation:none} + .project,.project .icon{transition:none} + .project:hover,.project:hover .icon{transform:none} +} +@media (max-width:960px){ .projects{grid-template-columns:repeat(2,minmax(0,1fr))} } +@media (max-width:640px){ .projects{grid-template-columns:1fr} } +@media (max-width:640px){ + .page-hero{padding:48px 0 16px} + .page-hero .lead{font-size:17px} + .group{padding-top:44px} + .project{padding:22px} +} diff --git a/content/projects.ts b/content/projects.ts new file mode 100644 index 0000000..61276b0 --- /dev/null +++ b/content/projects.ts @@ -0,0 +1,160 @@ +import type { L } from "@/lib/i18n"; + +export type Project = { + name: string; + /** Иконка из public/assets/. */ + icon: string; + /** Репозиторий в Shapak-Apps — ссылка на GitHub и счётчик звёзд. */ + repo: string; + status: { live: boolean; text: L }; + text: L; + /** Плашки: строка одинакова на всех языках, L — переводится. */ + nums: (string | L)[]; + stack: string; + author: string; + /** Ссылки перед GitHub: сторы, веб-версия. */ + links?: { label: string; href: string }[]; +}; + +const IN_DEVELOPMENT: L = { tk: "Taýýarlanýar", ru: "В разработке", en: "In development" }; + +export const meta = { + title: { tk: "Taslamalar — Şapak Apps", ru: "Проекты — Şapak Apps", en: "Projects — Şapak Apps" }, + description: { + tk: "Şapak Apps-yň açyk taslamalary: Ykjam Terjime, Hytaý dili 1, Iňlis dili 1, Köpri, BluePlayer, Küşt. Kod GitHub-da, MIT, Apache-2.0 we GPL-3.0.", + ru: "Открытые проекты Şapak Apps: Ykjam Terjime, Hytaý dili 1, Iňlis dili 1, Köpri, BluePlayer, Küşt. Код на GitHub, MIT, Apache-2.0 и GPL-3.0.", + en: "Open-source projects by Şapak Apps: Ykjam Terjime, Hytaý dili 1, Iňlis dili 1, Köpri, BluePlayer, Küşt. Apps in Turkmen, code on GitHub. MIT, Apache-2.0 and GPL-3.0.", + }, + ogTitle: { + tk: "Taslamalar — Open Source Turkmenistan", + ru: "Проекты — Open Source Turkmenistan", + en: "Projects — Open Source Turkmenistan", + }, +} satisfies Record; + +export const hero = { + eyebrow: "Open Source Turkmenistan", + title: { tk: "Taslamalar", ru: "Проекты", en: "Projects" }, + lead: { + tk: "Alty açyk taslama, iki ugur. Ikisi dükanlarda, hemmesi GitHub-da. Her biriniň öz awtory, ygtyýarnamasy we açyk meseleleri bar.", + ru: "Шесть открытых проектов в двух направлениях. Два — в магазинах, все — на GitHub. У каждого свой автор, лицензия и открытые задачи.", + en: "Six open projects in two lines. Two are in the stores, all are on GitHub. Each has its own author, license and open issues.", + }, +}; + +export const giveStar: L = { tk: "Ýyldyz ber", ru: "Поставить звезду", en: "Give a star" }; + +export const groups: { title: L; projects: Project[] }[] = [ + { + title: { tk: "Dil öwrenmek", ru: "Изучение языков", en: "Language learning" }, + projects: [ + { + name: "Ykjam Terjime", + icon: "/assets/ykjam-terjime.png", + repo: "turkmen-phrasebook", + status: { live: true, text: { tk: "Dükanlarda", ru: "В магазинах", en: "In the stores" } }, + text: { + tk: "Türkmen gepleşik kitaby we terjimeçi. Hakyky ýagdaýlar üçin taýýar sözlemler, türkmen harplary klawiaturasyz. Web wersiýasy brauzerde açylýar.", + ru: "Туркменский разговорник и переводчик. Готовые фразы для реальных ситуаций, туркменские буквы без специальной клавиатуры. Веб-версия открывается в браузере.", + en: "Turkmen phrasebook and translator. Ready phrases for real situations, Turkmen letters without a special keyboard. The web version opens in the browser.", + }, + nums: [{ tk: "31 dil", ru: "31 язык", en: "31 languages" }, { tk: "internetsiz", ru: "офлайн", en: "offline" }, "MIT"], + stack: "React Native · Expo · TypeScript", + author: "TheSeydiCharyyev", + links: [ + { label: "App Store", href: "https://apps.apple.com/app/ykjam-terjime/id6758071845" }, + { label: "Google Play", href: "https://play.google.com/store/apps/details?id=com.shapak.translator" }, + // Отдельный репозиторий Shapak-Apps/ykjam-terjime со своим Pages. + { label: "Web", href: "https://shapak-apps.github.io/ykjam-terjime/" }, + ], + }, + { + name: "Hytaý dili 1", + icon: "/assets/hytay-dili-1.png", + repo: "turkmen-chinese", + status: { + live: true, + text: { tk: "App Store-da · Google Play ýakynda", ru: "В App Store · скоро в Google Play", en: "On the App Store · Google Play soon" }, + }, + text: { + tk: "Türkmen dilinden hytaý dili. Her bapda teoriýa, gönükmeler we synag; indiki bap synagdan soň açylýar. Doly oflaýn.", + ru: "Китайский с туркменского. В каждой главе теория, упражнения и экзамен; следующая глава открывается после экзамена. Полностью офлайн.", + en: "Chinese from Turkmen. Every chapter has theory, exercises and an exam; the next chapter opens after the exam. Fully offline.", + }, + nums: [{ tk: "31 bap", ru: "31 глава", en: "31 chapters" }, { tk: "600 gönükme", ru: "600 упражнений", en: "600 exercises" }, "MIT"], + stack: "React Native · Expo · TypeScript", + author: "TheSeydiCharyyev", + links: [{ label: "App Store", href: "https://apps.apple.com/app/id6801010061" }], + }, + { + name: "Iňlis dili 1", + icon: "/assets/inlis-dili-1.png", + repo: "turkmen-english", + status: { live: false, text: IN_DEVELOPMENT }, + text: { + tk: "Türkmen dilinden iňlis dili, A1 derejesi. Hytaý dili 1 bilen bir hereketlendirijide: şol bir baplar, synaglar we ösüş.", + ru: "Английский с туркменского, уровень A1. Тот же движок, что у Hytaý dili 1: те же главы, экзамены и прогресс.", + en: "English from Turkmen, level A1. Same engine as Hytaý dili 1: the same chapters, exams and progress.", + }, + nums: ["A1", { tk: "20 bap", ru: "20 глав", en: "20 chapters" }, "MIT"], + stack: "React Native · Expo · TypeScript", + author: "TheSeydiCharyyev", + }, + ], + }, + { + title: { tk: "Gurallar", ru: "Инструменты", en: "Tools" }, + projects: [ + { + name: "Köpri", + icon: "/assets/kopri.png", + repo: "K-pri-App-for-phone", + status: { live: false, text: IN_DEVELOPMENT }, + text: { + tk: "Android üçin oflaýn terjimeçi: rus we iňlis dillerinden türkmen diline oflaýn sözlük, beýleki jübütler üçin onlaýn hyzmatlar, uly köpdilli gepleşik kitaby. Kamera arkaly terjime taýýarlanýar.", + ru: "Офлайн-переводчик для Android: офлайн-словарь с русского и английского на туркменский, онлайн-сервисы для остальных пар, большой многоязычный разговорник. Готовится перевод через камеру.", + en: "Offline translator for Android: an offline dictionary from Russian and English to Turkmen, online services for other pairs, a large multilingual phrasebook. Camera translation is coming.", + }, + nums: [{ tk: "50+ dil", ru: "50+ языков", en: "50+ languages" }, "Apache-2.0"], + stack: "Flutter · Dart", + author: "aynazar-sylyyew-dev", + }, + { + name: "BluePlayer", + icon: "/assets/blueplayer.png", + repo: "BluePlayer", + status: { live: false, text: IN_DEVELOPMENT }, + text: { + tk: "Android üçin döwrebap aýdym pleýeri. Kotlin we natiw C++.", + ru: "Современный музыкальный плеер для Android. Kotlin и нативный C++.", + en: "Modern music player for Android. Kotlin and native C++.", + }, + nums: ["Kotlin", "C++", "Apache-2.0"], + stack: "Android", + author: "aynazar-sylyyew-dev", + }, + { + name: "Küşt", + icon: "/assets/kust.png", + repo: "kust", + status: { live: false, text: IN_DEVELOPMENT }, + text: { + tk: "Küşt öwrenmek, türgenleşmek we oýnamak üçin programma. Stockfish botlaryna garşy oýun, oýundan soň derňew: her göçüm bahalandyrylýar we düşündirilýär.", + ru: "Шахматное приложение: учиться, тренироваться, играть. Партии против ботов на Stockfish и разбор после игры: каждый ход оценивается и объясняется.", + en: "A chess app to learn, practice and play. Games against Stockfish bots, and post-game analysis: every move is classified and explained.", + }, + nums: ["Stockfish", "GPL-3.0"], + stack: "Flutter · Dart", + author: "fihdeveloper", + }, + ], + }, +]; + +export const after = { + text: { + tk: "Öz açyk kodly taslamaň bar bolsa we ony türkmen jemgyýetiniň içinde ösdürmek isleseň — hat ýaz. Repozitoriý guramanyň astyna geçýär, awtorlyk seniňki bolup galýar.", + ru: "Если у вас есть проект с открытым кодом и вы хотите развивать его внутри туркменского сообщества — напишите нам. Репозиторий переходит под организацию, авторство остаётся за вами.", + en: "If you have an open-source project and want to grow it inside the Turkmen community — write to us. The repository moves under the organization, the authorship stays yours.", + }, +}; diff --git a/lib/seo.ts b/lib/seo.ts new file mode 100644 index 0000000..445e1c5 --- /dev/null +++ b/lib/seo.ts @@ -0,0 +1,38 @@ +import type { Metadata } from "next"; +import { LANGS, OG_LOCALE, SITE_URL, languageAlternates, localePath, type Lang } from "./i18n"; + +const OG_IMAGE = { url: "/assets/og.png", width: 1200, height: 630 }; + +/** Метаданные страницы на одном языке: canonical, hreflang на все три версии, OG и Twitter. */ +export function pageMetadata({ + lang, + path, + title, + description, + ogTitle, +}: { + lang: Lang; + path: string; + title: string; + description: string; + ogTitle: string; +}): Metadata { + const url = localePath(lang, path); + return { + metadataBase: new URL(SITE_URL), + title, + description, + alternates: { canonical: url, languages: languageAlternates(path) }, + openGraph: { + type: "website", + siteName: "Şapak Apps", + title: ogTitle, + description, + url, + locale: OG_LOCALE[lang], + alternateLocale: LANGS.filter((l) => l !== lang).map((l) => OG_LOCALE[l]), + images: [OG_IMAGE], + }, + twitter: { card: "summary_large_image", title: ogTitle, description, images: [OG_IMAGE.url] }, + }; +} From 43749a26220018cb207646c4a394c0cfa7cd3f06 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 23 Sep 2026 16:40:47 +0500 Subject: [PATCH 3/5] Global 404 in three languages, generated sitemap with hreflang --- app/global-not-found.tsx | 37 +++++++++++++++++++++++++++++++++++++ app/sitemap.ts | 24 ++++++++++++++++++++++++ components/not-found.css | 14 ++++++++++++++ next.config.ts | 4 ++++ public/sitemap.xml | 6 ------ 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 app/global-not-found.tsx create mode 100644 app/sitemap.ts create mode 100644 components/not-found.css delete mode 100644 public/sitemap.xml diff --git a/app/global-not-found.tsx b/app/global-not-found.tsx new file mode 100644 index 0000000..a913cab --- /dev/null +++ b/app/global-not-found.tsx @@ -0,0 +1,37 @@ +import type { Metadata, Viewport } from "next"; +import { baseViewport } from "@/lib/document"; +import "./globals.css"; +import "@/components/not-found.css"; + +// Одна 404 на весь сайт: у сайта две корневые раскладки, поэтому обычный +// app/not-found.tsx собрать не из чего. Язык запроса неизвестен — текст на трёх. +export const metadata: Metadata = { + title: "404 — Şapak Apps", + robots: { index: false }, + icons: { icon: "/icon.png" }, +}; +export const viewport: Viewport = baseViewport; + +export default function GlobalNotFound() { + return ( + + +
+ + şapak + +

+ Sahypa tapylmady · Страница не найдена · Page not found +

+

Bu salgy boýunça sahypa ýok. Baş sahypa geçiň ýa-da GitHub-daky repozitoriýalara serediň.

+

Страницы по этому адресу нет. Перейдите на главную или посмотрите репозитории на GitHub.

+

There is no page at this address. Go to the home page or look at the repositories on GitHub.

+ +
+ + + ); +} diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 0000000..87696c1 --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,24 @@ +import type { MetadataRoute } from "next"; +import { LANGS, SITE_URL, languageAlternates, localePath } from "@/lib/i18n"; +import { PRIVACY_URLS } from "@/content/site"; + +// Статический экспорт: sitemap.xml собирается один раз при сборке. +export const dynamic = "force-static"; + +/** Страницы, которые есть на всех трёх языках. Новая страница — добавить сюда. */ +const PAGES = ["/", "/projects/"]; + +const abs = (path: string) => `${SITE_URL}${path}`; + +export default function sitemap(): MetadataRoute.Sitemap { + const pages = PAGES.flatMap((path) => + LANGS.map((lang) => ({ + url: abs(localePath(lang, path)), + alternates: { + languages: Object.fromEntries(Object.entries(languageAlternates(path)).map(([l, p]) => [l, abs(p)])), + }, + })), + ); + // Политика Hytaý dili 1 — единственная, что лежит в этом репозитории. + return [...pages, { url: abs(PRIVACY_URLS.hytay) }]; +} diff --git a/components/not-found.css b/components/not-found.css new file mode 100644 index 0000000..6253411 --- /dev/null +++ b/components/not-found.css @@ -0,0 +1,14 @@ +/* Страница 404. Общие стили — app/globals.css. */ +.nf{padding-top:34px} +.nf .brand{display:inline-block} +.nf h1{ + margin:76px 0 0; font-size:clamp(30px,4vw,48px); font-weight:500; + letter-spacing:-.03em; line-height:1.06; +} +.nf h1 span{color:var(--muted); font-weight:400} +.nf p{color:var(--muted); font-size:18px; margin:22px 0 0; max-width:60ch} +.nf .ctas{margin-top:30px} + +@media (max-width:640px){ + .nf{padding-top:26px} +} diff --git a/next.config.ts b/next.config.ts index 85baf62..9f3e43b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -7,6 +7,10 @@ const nextConfig: NextConfig = { trailingSlash: true, // Оптимизатор картинок требует сервер; на Pages его нет. images: { unoptimized: true }, + experimental: { + // Одна 404 на весь сайт при двух корневых раскладках (app/global-not-found.tsx). + globalNotFound: true, + }, }; export default nextConfig; diff --git a/public/sitemap.xml b/public/sitemap.xml deleted file mode 100644 index e1f4bcb..0000000 --- a/public/sitemap.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - https://shapak-apps.github.io/2026-09-11 - https://shapak-apps.github.io/projects/2026-09-11 - https://shapak-apps.github.io/hytay-dili-1/privacy-policy.html2026-08-13 - From f2213db81c598b810928dd30947bb50b1eb1c7b7 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 23 Sep 2026 16:45:14 +0500 Subject: [PATCH 4/5] Build and deploy with GitHub Actions --- .github/workflows/deploy.yml | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..89567ff --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,63 @@ +# Сборка сайта на каждый PR в main; сборка и выкладка на GitHub Pages — на каждый пуш в main. +# Выкладывать в окружение github-pages разрешено только из main (настройки окружения). +name: Build and deploy + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +# PR: новая сборка отменяет устаревшую. main: деплои идут по очереди, не прерываясь. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: 22 + cache: npm + + - run: npm ci + + - run: npm run build + env: + NEXT_TELEMETRY_DISABLED: 1 + + # Эти адреса прописаны в App Store / Google Play и в самих приложениях. + # 22.09.2026 политика Hytaý dili 1 уже пропадала вместе с перестройкой сайта. + - name: Check pages the stores depend on + run: | + for f in index.html ru/index.html en/index.html projects/index.html 404.html \ + hytay-dili-1/privacy-policy.html sitemap.xml robots.txt; do + test -s "out/$f" || { echo "::error::out/$f is missing"; exit 1; } + done + + - uses: actions/upload-pages-artifact@v5 + if: github.event_name != 'pull_request' + with: + path: out + + deploy: + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v5 From e902906fa50684c7b698cd3453c3266226a764c4 Mon Sep 17 00:00:00 2001 From: Seydi Charyyev Date: Wed, 23 Sep 2026 16:55:17 +0500 Subject: [PATCH 5/5] Regenerate package-lock: sharp platform entries had no version --- package-lock.json | 576 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 501 insertions(+), 75 deletions(-) diff --git a/package-lock.json b/package-lock.json index af5a6bd..cc651eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,16 @@ "typescript": "^5" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", @@ -29,6 +39,497 @@ "node": ">=18" } }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@next/env": { "version": "16.3.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.6.tgz", @@ -452,81 +953,6 @@ } } }, - "node_modules/sharp/node_modules/@img/sharp-darwin-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-darwin-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-freebsd-wasm32": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-ppc64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-riscv64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-s390x": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-arm": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-ppc64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-riscv64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-s390x": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linux-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linuxmusl-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-linuxmusl-x64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-webcontainers-wasm32": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-win32-arm64": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-win32-ia32": { - "optional": true - }, - "node_modules/sharp/node_modules/@img/sharp-win32-x64": { - "optional": true - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",