Skip to content

Commit 9c9a1ed

Browse files
committed
Rewrite: migrate to Astro, add i18n, blog, and production build
1 parent 4c97aeb commit 9c9a1ed

73 files changed

Lines changed: 10376 additions & 15923 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.astro/astro/content.d.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
declare module 'astro:content' {
2+
interface Render {
3+
'.mdoc': Promise<{
4+
Content(props: Record<string, any>): import('astro').MarkdownInstance<{}>['Content'];
5+
headings: import('astro').MarkdownHeading[];
6+
}>;
7+
}
8+
}
9+
10+
declare module 'astro:content' {
11+
interface RenderResult {
12+
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
13+
headings: import('astro').MarkdownHeading[];
14+
remarkPluginFrontmatter: Record<string, any>;
15+
}
16+
interface Render {
17+
'.md': Promise<RenderResult>;
18+
}
19+
20+
export interface RenderedContent {
21+
html: string;
22+
metadata?: {
23+
imagePaths: Array<string>;
24+
[key: string]: unknown;
25+
};
26+
}
27+
}
28+
29+
declare module 'astro:content' {
30+
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
31+
32+
export type CollectionKey = keyof AnyEntryMap;
33+
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
34+
35+
export type ContentCollectionKey = keyof ContentEntryMap;
36+
export type DataCollectionKey = keyof DataEntryMap;
37+
38+
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
39+
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
40+
ContentEntryMap[C]
41+
>['slug'];
42+
43+
/** @deprecated Use `getEntry` instead. */
44+
export function getEntryBySlug<
45+
C extends keyof ContentEntryMap,
46+
E extends ValidContentEntrySlug<C> | (string & {}),
47+
>(
48+
collection: C,
49+
// Note that this has to accept a regular string too, for SSR
50+
entrySlug: E,
51+
): E extends ValidContentEntrySlug<C>
52+
? Promise<CollectionEntry<C>>
53+
: Promise<CollectionEntry<C> | undefined>;
54+
55+
/** @deprecated Use `getEntry` instead. */
56+
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
57+
collection: C,
58+
entryId: E,
59+
): Promise<CollectionEntry<C>>;
60+
61+
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
62+
collection: C,
63+
filter?: (entry: CollectionEntry<C>) => entry is E,
64+
): Promise<E[]>;
65+
export function getCollection<C extends keyof AnyEntryMap>(
66+
collection: C,
67+
filter?: (entry: CollectionEntry<C>) => unknown,
68+
): Promise<CollectionEntry<C>[]>;
69+
70+
export function getEntry<
71+
C extends keyof ContentEntryMap,
72+
E extends ValidContentEntrySlug<C> | (string & {}),
73+
>(entry: {
74+
collection: C;
75+
slug: E;
76+
}): E extends ValidContentEntrySlug<C>
77+
? Promise<CollectionEntry<C>>
78+
: Promise<CollectionEntry<C> | undefined>;
79+
export function getEntry<
80+
C extends keyof DataEntryMap,
81+
E extends keyof DataEntryMap[C] | (string & {}),
82+
>(entry: {
83+
collection: C;
84+
id: E;
85+
}): E extends keyof DataEntryMap[C]
86+
? Promise<DataEntryMap[C][E]>
87+
: Promise<CollectionEntry<C> | undefined>;
88+
export function getEntry<
89+
C extends keyof ContentEntryMap,
90+
E extends ValidContentEntrySlug<C> | (string & {}),
91+
>(
92+
collection: C,
93+
slug: E,
94+
): E extends ValidContentEntrySlug<C>
95+
? Promise<CollectionEntry<C>>
96+
: Promise<CollectionEntry<C> | undefined>;
97+
export function getEntry<
98+
C extends keyof DataEntryMap,
99+
E extends keyof DataEntryMap[C] | (string & {}),
100+
>(
101+
collection: C,
102+
id: E,
103+
): E extends keyof DataEntryMap[C]
104+
? Promise<DataEntryMap[C][E]>
105+
: Promise<CollectionEntry<C> | undefined>;
106+
107+
/** Resolve an array of entry references from the same collection */
108+
export function getEntries<C extends keyof ContentEntryMap>(
109+
entries: {
110+
collection: C;
111+
slug: ValidContentEntrySlug<C>;
112+
}[],
113+
): Promise<CollectionEntry<C>[]>;
114+
export function getEntries<C extends keyof DataEntryMap>(
115+
entries: {
116+
collection: C;
117+
id: keyof DataEntryMap[C];
118+
}[],
119+
): Promise<CollectionEntry<C>[]>;
120+
121+
export function render<C extends keyof AnyEntryMap>(
122+
entry: AnyEntryMap[C][string],
123+
): Promise<RenderResult>;
124+
125+
export function reference<C extends keyof AnyEntryMap>(
126+
collection: C,
127+
): import('astro/zod').ZodEffects<
128+
import('astro/zod').ZodString,
129+
C extends keyof ContentEntryMap
130+
? {
131+
collection: C;
132+
slug: ValidContentEntrySlug<C>;
133+
}
134+
: {
135+
collection: C;
136+
id: keyof DataEntryMap[C];
137+
}
138+
>;
139+
// Allow generic `string` to avoid excessive type errors in the config
140+
// if `dev` is not running to update as you edit.
141+
// Invalid collection names will be caught at build time.
142+
export function reference<C extends string>(
143+
collection: C,
144+
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
145+
146+
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
147+
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
148+
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
149+
>;
150+
151+
type ContentEntryMap = {
152+
"posts-en": {
153+
"welcome-to-nuros-blog.mdoc": {
154+
id: "welcome-to-nuros-blog.mdoc";
155+
slug: "welcome-to-nuros-blog";
156+
body: string;
157+
collection: "posts-en";
158+
data: InferEntrySchema<"posts-en">
159+
} & { render(): Render[".mdoc"] };
160+
};
161+
"posts-ru": {
162+
"welcome-to-nuros-blog.mdoc": {
163+
id: "welcome-to-nuros-blog.mdoc";
164+
slug: "welcome-to-nuros-blog";
165+
body: string;
166+
collection: "posts-ru";
167+
data: InferEntrySchema<"posts-ru">
168+
} & { render(): Render[".mdoc"] };
169+
};
170+
171+
};
172+
173+
type DataEntryMap = {
174+
175+
};
176+
177+
type AnyEntryMap = ContentEntryMap & DataEntryMap;
178+
179+
export type ContentConfig = typeof import("../../src/content/config.js");
180+
}

.astro/keystatic-imports.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import "@keystatic/astro/ui";
2+
import "@keystatic/astro/api";
3+
import "@keystatic/core/ui";

.astro/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"_variables": {
3+
"lastUpdateCheck": 1772522189306
4+
}
5+
}

.astro/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="astro/client" />
2+
/// <reference path="astro/content.d.ts" />

astro.config.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Dev config — hybrid output for Keystatic admin UI (SSR)
2+
import { defineConfig } from 'astro/config';
3+
import react from '@astrojs/react';
4+
import markdoc from '@astrojs/markdoc';
5+
import keystatic from '@keystatic/astro';
6+
import node from '@astrojs/node';
7+
8+
export default defineConfig({
9+
integrations: [react(), markdoc(), keystatic()],
10+
output: 'hybrid',
11+
adapter: node({ mode: 'standalone' }),
12+
i18n: {
13+
defaultLocale: 'en',
14+
locales: ['en', 'ru'],
15+
routing: { prefixDefaultLocale: false },
16+
},
17+
});

astro.config.prod.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Production config — fully static for GitHub Pages
2+
import { defineConfig } from 'astro/config';
3+
import react from '@astrojs/react';
4+
import markdoc from '@astrojs/markdoc';
5+
6+
const keystaticStub = {
7+
name: 'keystatic-stub',
8+
resolveId(id) {
9+
if (id === 'virtual:keystatic-config') return '\0virtual:keystatic-config';
10+
},
11+
load(id) {
12+
if (id === '\0virtual:keystatic-config') return 'export default {}';
13+
},
14+
};
15+
16+
export default defineConfig({
17+
integrations: [react(), markdoc()],
18+
output: 'static',
19+
i18n: {
20+
defaultLocale: 'en',
21+
locales: ['en', 'ru'],
22+
routing: { prefixDefaultLocale: false },
23+
},
24+
vite: {
25+
plugins: [keystaticStub],
26+
},
27+
});

dist/404.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="description" content="Page not found"><link rel="icon" href="/favicon.png" type="image/png"><title>404 — NurOS</title><link rel="stylesheet" href="/_astro/_slug_.BmiWAgRC.css">
2+
<style>.nuros-app[data-astro-cid-zetdm5md]{min-height:100vh;display:flex;flex-direction:column}.not-found[data-astro-cid-zetdm5md]{flex:1;display:flex;align-items:center;padding:var(--spacing-xl) 0}.not-found-inner[data-astro-cid-zetdm5md]{text-align:center;max-width:480px;margin:0 auto}.code[data-astro-cid-zetdm5md]{font-size:clamp(6rem,18vw,10rem);font-weight:800;line-height:1;letter-spacing:-.04em;background:linear-gradient(135deg,var(--primary-color) 0%,var(--accent-color) 100%);-webkit-background-clip:text;background-clip:text;color:transparent;margin-bottom:.25rem;user-select:none}.title[data-astro-cid-zetdm5md]{font-size:1.5rem;font-weight:700;color:var(--text-color);margin-bottom:var(--spacing-sm)}.desc[data-astro-cid-zetdm5md]{font-size:1rem;color:var(--text-muted);line-height:1.7;margin-bottom:var(--spacing-md)}.actions[data-astro-cid-zetdm5md]{display:flex;justify-content:center;gap:.75rem;flex-wrap:wrap}.btn-home[data-astro-cid-zetdm5md]{display:inline-flex;align-items:center;padding:.65rem 1.4rem;background:var(--primary-color);color:#fff;font-weight:600;font-size:.95rem;border-radius:var(--border-radius);transition:background .2s,transform .2s}.btn-home[data-astro-cid-zetdm5md]:hover{background:var(--primary-hover);transform:translateY(-2px);opacity:1;color:#fff}.btn-blog[data-astro-cid-zetdm5md]{display:inline-flex;align-items:center;padding:.65rem 1.4rem;border:1px solid rgba(138,180,232,.35);color:var(--accent-color);font-weight:600;font-size:.95rem;border-radius:var(--border-radius);transition:background .2s,border-color .2s,transform .2s}.btn-blog[data-astro-cid-zetdm5md]:hover{background:#8ab4e814;border-color:var(--accent-color);transform:translateY(-2px);opacity:1}
3+
</style><script type="module" src="/_astro/hoisted.CRzPjfg7.js"></script></head> <body> <div class="nuros-app" data-astro-cid-zetdm5md> <header data-astro-cid-3ef6ksr2> <div class="container" data-astro-cid-3ef6ksr2> <a href="/" class="logo-link" data-astro-cid-3ef6ksr2> <div class="logo" data-astro-cid-3ef6ksr2> <h1 data-astro-cid-3ef6ksr2>NurOS</h1> </div> </a> <nav id="main-nav" data-astro-cid-3ef6ksr2> <ul data-astro-cid-3ef6ksr2> <li data-astro-cid-3ef6ksr2><a href="/#home" data-astro-cid-3ef6ksr2>Home</a></li> <li data-astro-cid-3ef6ksr2><a href="/#about" data-astro-cid-3ef6ksr2>About</a></li> <li data-astro-cid-3ef6ksr2><a href="/#features" data-astro-cid-3ef6ksr2>Features</a></li> <li data-astro-cid-3ef6ksr2> <a href="/releases" data-astro-cid-3ef6ksr2>Download</a> </li> <li data-astro-cid-3ef6ksr2> <a href="/team" data-astro-cid-3ef6ksr2>Team</a> </li> <li data-astro-cid-3ef6ksr2><a href="/#community" data-astro-cid-3ef6ksr2>Community</a></li> <li data-astro-cid-3ef6ksr2> <a href="/blog" data-astro-cid-3ef6ksr2>Blog</a> </li> </ul> </nav> <div class="header-actions" data-astro-cid-3ef6ksr2> <div class="lang-toggle" data-astro-cid-3ef6ksr2> <a href="/404/" class="active" data-astro-cid-3ef6ksr2>EN</a> <a href="/ru/404/" data-astro-cid-3ef6ksr2>RU</a> </div> <button class="menu-toggle" id="menu-toggle" aria-label="Menu" data-astro-cid-3ef6ksr2> <span data-astro-cid-3ef6ksr2></span> <span data-astro-cid-3ef6ksr2></span> </button> </div> </div> </header> <main class="not-found" data-astro-cid-zetdm5md> <div class="container" data-astro-cid-zetdm5md> <div class="not-found-inner" data-astro-cid-zetdm5md> <p class="code" data-astro-cid-zetdm5md>404</p> <h1 class="title" data-astro-cid-zetdm5md>Page not found</h1> <p class="desc" data-astro-cid-zetdm5md>
4+
The page you're looking for doesn't exist or has been moved.
5+
</p> <div class="actions" data-astro-cid-zetdm5md> <a href="/" class="btn-home" data-astro-cid-zetdm5md>Go home</a> <a href="/blog" class="btn-blog" data-astro-cid-zetdm5md>Blog</a> </div> </div> </div> </main> <footer data-astro-cid-sz7xmlte> <div class="container" data-astro-cid-sz7xmlte> <div class="footer-content" data-astro-cid-sz7xmlte> <p data-astro-cid-sz7xmlte>NurOS © 2026. GNU AGPL v3 License.</p> <p data-astro-cid-sz7xmlte><a href="https://nuros.org" target="_blank" rel="noopener" data-astro-cid-sz7xmlte>nuros.org</a></p> </div> </div> </footer> </div> </body></html>

dist/_astro/_slug_.BmiWAgRC.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/_astro/client.12hVY3n3.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/_astro/hoisted.BG8SLDj6.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import"./hoisted.CRzPjfg7.js";const n=document.querySelector(".hero");n?.addEventListener("mousemove",e=>{n.style.backgroundPosition=`${e.clientX/window.innerWidth*100}% ${e.clientY/window.innerHeight*100}%`});

0 commit comments

Comments
 (0)