website-xp-phone 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/.astro/content-assets.mjs +1 -0
  2. package/.astro/content-modules.mjs +1 -0
  3. package/.astro/content.d.ts +199 -0
  4. package/.astro/data-store.json +1 -0
  5. package/.astro/settings.json +8 -0
  6. package/.astro/types.d.ts +1 -0
  7. package/.devcontainer/devcontainer.json +23 -0
  8. package/.env.firebase.example +8 -0
  9. package/.firebaserc +5 -0
  10. package/.gitattributes +2 -0
  11. package/.github/copilot-instructions.md +131 -0
  12. package/.github/dependabot.yml +11 -0
  13. package/.github/workflows/ci.yml +45 -0
  14. package/.github/workflows/deploy-admin.yml +48 -0
  15. package/.github/workflows/static.yml +43 -0
  16. package/.gitmodules +5 -0
  17. package/FIREBASE_SETUP.md +69 -0
  18. package/README.md +63 -0
  19. package/SECURITY.md +11 -0
  20. package/admin/Admin.csproj +7 -0
  21. package/admin/Dockerfile +14 -0
  22. package/admin/Program.cs +8 -0
  23. package/deploy-admin-cloud-run.md +229 -0
  24. package/eslint.config.js +28 -0
  25. package/firebase.json +5 -0
  26. package/firestore.rules +29 -0
  27. package/index.html +52 -0
  28. package/package.json +48 -0
  29. package/pagerts_output.json +1 -0
  30. package/public/5.html +967 -0
  31. package/public/BAHNSCHRIFT.TTF +0 -0
  32. package/public/Beep.ogg +0 -0
  33. package/public/Clippy.png +0 -0
  34. package/public/Layered Network Security Model for Home Networks (slides).pdf +0 -0
  35. package/public/Layered Network Security Model for Home Networks.pdf +0 -0
  36. package/public/TODO.pdf +0 -0
  37. package/public/WoW_Config.zip +3 -0
  38. package/public/addons/energy-swing.txt +1 -0
  39. package/public/addons/lego-yoda-death-readme.txt +11 -0
  40. package/public/addons/lego-yoda-death.mp3 +0 -0
  41. package/public/addons/mana-blast.txt +1 -0
  42. package/public/addons/rage-volley.txt +1 -0
  43. package/public/addons/rueg-cell.txt +1 -0
  44. package/public/addons/rueg-elvui-profile.txt +1 -0
  45. package/public/addons/rueg-grid2.txt +214 -0
  46. package/public/addons/rueg-plater-smol.txt +1 -0
  47. package/public/addons/rueg-plater.txt +1 -0
  48. package/public/addons/rueg-wa-druid.txt +1 -0
  49. package/public/addons/rueg-wa-priest.txt +1 -0
  50. package/public/addons/rueg-wa-rogue.txt +1 -0
  51. package/public/addons/rueg-wa-shaman.txt +1 -0
  52. package/public/addons/rueg-wa-warrior.txt +1 -0
  53. package/public/addons/spirit-smash.txt +1 -0
  54. package/public/avatar.jpg +0 -0
  55. package/public/avatar.png +0 -0
  56. package/public/crunchy_kick.ogg +0 -0
  57. package/public/documents/resume.html +312 -0
  58. package/public/favicon.ico +0 -0
  59. package/public/images/Ateric1.png +0 -0
  60. package/public/images/Ateric2.png +0 -0
  61. package/public/images/equal1.png +0 -0
  62. package/public/images/hyperawareofwhatacatis.png +0 -0
  63. package/public/images/kogg1.png +0 -0
  64. package/public/images/kogg2.png +0 -0
  65. package/public/images/rueg1.png +0 -0
  66. package/public/images/rueg2.png +0 -0
  67. package/public/incorrect_responses.txt +126 -0
  68. package/public/loading.css +51 -0
  69. package/public/resume.pdf +0 -0
  70. package/public/robots.txt +9 -0
  71. package/public/soundcloud.json +57 -0
  72. package/public/spinner.svg +12 -0
  73. package/public/tada.wav +0 -0
  74. package/public/yooh.mp3 +0 -0
  75. package/render.yaml +5 -0
  76. package/scripts/ensure-blog-worktree.mjs +24 -0
  77. package/scripts/generate-soundcloud-json.mjs +198 -0
  78. package/scripts/git-worktree-helper.mjs +122 -0
  79. package/scripts/hoist-dev-blog-local.mjs +149 -0
  80. package/scripts/music-schema.mjs +56 -0
  81. package/scripts/publish-soundcloud-json.mjs +32 -0
  82. package/scripts/sync-music-links-from-worktree.mjs +32 -0
  83. package/src/App.tsx +1500 -0
  84. package/src/addons.json +76 -0
  85. package/src/components/Addon.tsx +223 -0
  86. package/src/components/BlogContent.tsx +103 -0
  87. package/src/components/CopyToClipboardButton.tsx +21 -0
  88. package/src/components/MenuBar.tsx +151 -0
  89. package/src/components/MenuBarWithContext.tsx +6 -0
  90. package/src/components/Modal.tsx +17 -0
  91. package/src/components/MusicContent.tsx +309 -0
  92. package/src/components/NavBarController.tsx +55 -0
  93. package/src/components/NavBarControllerWrapper.tsx +13 -0
  94. package/src/components/Page.tsx +56 -0
  95. package/src/components/SitemapContent.tsx +125 -0
  96. package/src/contacts.json +32 -0
  97. package/src/env.d.ts +13 -0
  98. package/src/lib/assistantStateMachine.ts +80 -0
  99. package/src/lib/audioOverlap.ts +99 -0
  100. package/src/lib/keyboardInputUtils.ts +182 -0
  101. package/src/lib/musicSchema.ts +85 -0
  102. package/src/lib/naggingAssistantClient.ts +241 -0
  103. package/src/lib/resumeAnalytics.ts +163 -0
  104. package/src/main.tsx +35 -0
  105. package/src/pages.json +50 -0
  106. package/src/sections.json +243 -0
  107. package/src/src+addons.zip +3 -0
  108. package/src/styles/main.css +465 -0
  109. package/src/utils/blogSecurity.ts +87 -0
  110. package/src/utils/menuItems.ts +33 -0
  111. package/src/windowing/MinimizedSections.tsx +86 -0
  112. package/src/windowing/Section.tsx +586 -0
  113. package/src/windowing/context.tsx +13 -0
  114. package/src/windowing/hooks.ts +10 -0
  115. package/src/windowing/index.ts +7 -0
  116. package/src/windowing/provider.tsx +74 -0
  117. package/src/windowing/server.ts +3 -0
  118. package/src/windowing/types.ts +33 -0
  119. package/src/windowing/utils.ts +135 -0
  120. package/tests/generate-soundcloud-json.test.mjs +63 -0
  121. package/tests/music-schema.test.mjs +53 -0
  122. package/tsconfig.json +26 -0
  123. package/vite.config.ts +304 -0
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # kine's website
2
+
3
+ This is the source code for my personal website. I built it as a landing page for myself, and as a point-of-interest for those that stumble upon it.
4
+
5
+ This code shows who I am, what I am building, and the things I care about in software.
6
+
7
+ The design is very human. The code is built with modern tooling so I can keep iterating quickly.
8
+
9
+ ## Why this exists
10
+
11
+ This repo is where I experiment with:
12
+
13
+ - Personal storytelling through UI
14
+ - JSON-driven content that is easy to edit
15
+ - Small interactive features (windows, modals, media embeds)
16
+ - A workflow that is simple enough to maintain long-term
17
+
18
+ ## Tech stack
19
+
20
+ - React + TypeScript
21
+ - ~~Astro~~
22
+ - Vite for development and builds
23
+ - xp.css for the nostalgia
24
+
25
+ ## Local development
26
+
27
+ Start the dev server:
28
+
29
+ ```
30
+ npm run dev
31
+ ```
32
+
33
+ Build for production:
34
+
35
+ ```
36
+ npm run build
37
+ ```
38
+
39
+ Run linting:
40
+
41
+ ```
42
+ npm run lint
43
+ ```
44
+
45
+ Dependency audit:
46
+
47
+ ```
48
+ npm run check:audit
49
+ ```
50
+
51
+ ## Notes
52
+
53
+ Most content is managed through JSON, rendered into components in the browser.
54
+
55
+ This keeps content updates straightforward while still allowing rich UI behavior.
56
+
57
+ Some pages rely on raw github user content to serve the json so that a full website-rebuild is not necessary for updating content on those pages, such as blog.
58
+
59
+ ## Contact
60
+
61
+ If you are here to learn more about me professionally, the website includes my resume and contact details.
62
+
63
+ Thanks for visiting.
package/SECURITY.md ADDED
@@ -0,0 +1,11 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ This website is live updated from this repository through continuous deployment.
6
+
7
+ Please consult the version history, or any markdown files in the root of the project for support!
8
+
9
+ ## Reporting a Vulnerability
10
+
11
+ Please report vulnerabilities directly through Github issues.
@@ -0,0 +1,7 @@
1
+ <Project Sdk="Microsoft.NET.Sdk.Web">
2
+ <PropertyGroup>
3
+ <TargetFramework>net9.0</TargetFramework>
4
+ <Nullable>enable</Nullable>
5
+ <ImplicitUsings>enable</ImplicitUsings>
6
+ </PropertyGroup>
7
+ </Project>
@@ -0,0 +1,14 @@
1
+ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
2
+ WORKDIR /app
3
+ EXPOSE 8080
4
+ ENV ASPNETCORE_URLS=http://+:8080
5
+
6
+ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
7
+ WORKDIR /src
8
+ COPY . .
9
+ RUN dotnet publish -c Release -o /app/publish
10
+
11
+ FROM base AS final
12
+ WORKDIR /app
13
+ COPY --from=build /app/publish .
14
+ ENTRYPOINT ["dotnet", "Admin.dll"]
@@ -0,0 +1,8 @@
1
+ var builder = WebApplication.CreateBuilder(args);
2
+
3
+ var app = builder.Build();
4
+
5
+ app.MapGet("/", () => Results.Text("nothing to see here", "text/plain"));
6
+ app.MapGet("/status", () => TypedResults.Ok(new { ok = true, service = "admin", timestamp = DateTimeOffset.UtcNow }));
7
+
8
+ app.Run();
@@ -0,0 +1,229 @@
1
+ # ASP.NET Minimal API → Cloud Run via GitHub Actions
2
+
3
+ Deploy a single-file ASP.NET endpoint to Google Cloud Run, accessible at `admin.akinevz.com`. Everything triggered on `git push main`.
4
+
5
+ **Flow:** push → Actions → docker build → Artifact Registry → Cloud Run → Cloudflare DNS → `admin.akinevz.com`
6
+
7
+ Cloud Run free tier: 2 million requests/month, 360,000 GB-seconds. Sufficient for a personal site.
8
+ Nearest region to Hove, UK: **europe-west1** (Belgium).
9
+
10
+ ---
11
+
12
+ ## Step 1 — GCP Setup
13
+
14
+ Firebase already lives in a GCP project. Enable two additional APIs in GCP Console:
15
+
16
+ - Artifact Registry API
17
+ - Cloud Run Admin API
18
+
19
+ Create a Service Account for GitHub Actions:
20
+
21
+ - Name: `github-actions-deploy`
22
+ - Roles: `roles/run.admin`, `roles/artifactregistry.writer`, `roles/iam.serviceAccountUser`
23
+ - Download JSON key → add to GitHub repo secrets as `GCP_SA_KEY`
24
+ - Also add secret `GCP_PROJECT_ID` = your GCP project ID
25
+
26
+ ---
27
+
28
+ ## Step 2 — Create Artifact Registry Repository
29
+
30
+ GCP Console → Artifact Registry → Create Repository:
31
+
32
+ - Name: `admin`
33
+ - Format: Docker
34
+ - Region: `europe-west1`
35
+
36
+ ---
37
+
38
+ ## Step 3 — ASP.NET Minimal API
39
+
40
+ Create folder `admin/` in the repo root.
41
+
42
+ **admin/Admin.csproj**
43
+ ```xml
44
+ <Project Sdk="Microsoft.NET.Sdk.Web">
45
+ <PropertyGroup>
46
+ <TargetFramework>net9.0</TargetFramework>
47
+ </PropertyGroup>
48
+ </Project>
49
+ ```
50
+
51
+ **admin/Program.cs**
52
+ ```csharp
53
+ var app = WebApplication.Create(args);
54
+
55
+ app.MapGet("/", () => "nothing to see here");
56
+ app.MapGet("/status", () => Results.Ok(new { ok = true }));
57
+
58
+ app.Run();
59
+ ```
60
+
61
+ **admin/Dockerfile**
62
+ ```dockerfile
63
+ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
64
+ WORKDIR /app
65
+ EXPOSE 8080
66
+ ENV ASPNETCORE_URLS=http://+:8080
67
+
68
+ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
69
+ WORKDIR /src
70
+ COPY . .
71
+ RUN dotnet publish -c Release -o /app/publish
72
+
73
+ FROM base AS final
74
+ WORKDIR /app
75
+ COPY --from=build /app/publish .
76
+ ENTRYPOINT ["dotnet", "Admin.dll"]
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Step 4 — GitHub Actions Workflow
82
+
83
+ Create `.github/workflows/deploy-admin.yml`:
84
+
85
+ ```yaml
86
+ name: Deploy Admin to Cloud Run
87
+
88
+ on:
89
+ push:
90
+ branches: [main]
91
+ paths: ['admin/**', '.github/workflows/deploy-admin.yml']
92
+
93
+ env:
94
+ REGION: europe-west1
95
+ SERVICE: admin
96
+ IMAGE: europe-west1-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/admin/app
97
+
98
+ jobs:
99
+ deploy:
100
+ runs-on: ubuntu-latest
101
+ steps:
102
+ - uses: actions/checkout@v4
103
+
104
+ - uses: google-github-actions/auth@v2
105
+ with:
106
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
107
+
108
+ - uses: google-github-actions/setup-gcloud@v2
109
+
110
+ - run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
111
+
112
+ - run: |
113
+ docker build -t ${{ env.IMAGE }}:${{ github.sha }} ./admin
114
+ docker push ${{ env.IMAGE }}:${{ github.sha }}
115
+
116
+ - run: |
117
+ gcloud run deploy ${{ env.SERVICE }} \
118
+ --image ${{ env.IMAGE }}:${{ github.sha }} \
119
+ --region ${{ env.REGION }} \
120
+ --platform managed \
121
+ --allow-unauthenticated \
122
+ --port 8080
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Step 5 — Custom Domain: admin.akinevz.com
128
+
129
+ Cloud Run supports custom domains directly — no Firebase rewrite needed for a subdomain.
130
+
131
+ ### 5a — Map domain in Cloud Run
132
+
133
+ ```bash
134
+ gcloud beta run domain-mappings create \
135
+ --service admin \
136
+ --domain admin.akinevz.com \
137
+ --region europe-west1
138
+ ```
139
+
140
+ This outputs DNS records to add. They will look something like:
141
+
142
+ ```
143
+ Type Name Value
144
+ CNAME admin ghs.googlehosted.com
145
+ ```
146
+
147
+ ### 5b — Add DNS record in Cloudflare
148
+
149
+ 1. Cloudflare Dashboard → akinevz.com → DNS
150
+ 2. Add record:
151
+ - Type: `CNAME`
152
+ - Name: `admin`
153
+ - Target: `ghs.googlehosted.com`
154
+ - **Proxy status: DNS only (grey cloud)** ← required, Cloud Run handles TLS itself
155
+ 3. Save
156
+
157
+ > ⚠️ **Reminder:** Cloudflare proxy (orange cloud) must be OFF for Cloud Run custom domains. Cloud Run provisions its own TLS certificate via Google-managed SSL. Orange cloud will break certificate verification.
158
+
159
+ SSL certificate provisioning takes ~15 minutes after DNS propagates.
160
+
161
+ ### 5c — Verify
162
+
163
+ ```bash
164
+ gcloud beta run domain-mappings describe \
165
+ --domain admin.akinevz.com \
166
+ --region europe-west1
167
+ ```
168
+
169
+ Status should show `CertificateProvisioned`.
170
+
171
+ ---
172
+
173
+ ## Step 6 — Firebase Hosting (main site, unchanged)
174
+
175
+ `firebase.json` stays as-is for the React SPA. The `/admin` path on the main domain is now unused — subdomain handles it instead.
176
+
177
+ ```json
178
+ {
179
+ "hosting": {
180
+ "public": "dist",
181
+ "rewrites": [
182
+ {
183
+ "source": "**",
184
+ "destination": "/index.html"
185
+ }
186
+ ]
187
+ }
188
+ }
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Step 7 — First Deploy
194
+
195
+ Push `admin/` and the workflow file. Actions will:
196
+
197
+ - Build the Docker image
198
+ - Push to Artifact Registry
199
+ - Deploy to Cloud Run as service `admin`
200
+
201
+ Then run domain mapping command from Step 5a, add Cloudflare DNS, wait for cert.
202
+
203
+ ---
204
+
205
+ ## Repo Structure
206
+
207
+ ```
208
+ frontend/
209
+ ├── admin/
210
+ │ ├── Admin.csproj
211
+ │ ├── Program.cs
212
+ │ └── Dockerfile
213
+ ├── .github/
214
+ │ └── workflows/
215
+ │ ├── deploy-firebase.yml ← existing
216
+ │ └── deploy-admin.yml ← new
217
+ ├── firebase.json
218
+ └── src/ ← React frontend
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Notes
224
+
225
+ - Cloud Run scales to zero when idle — no cost when unused.
226
+ - `ASPNETCORE_URLS` must be `http://+:8080` — Cloud Run routes to port 8080 by default.
227
+ - `--allow-unauthenticated` exposes the endpoint publicly. Add auth middleware in `Program.cs` if needed.
228
+ - Artifact Registry storage billed after 500 MB free. Prune old image tags periodically.
229
+ - Cloudflare proxy must stay grey (DNS only) on the `admin` CNAME — never orange.
@@ -0,0 +1,28 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist', '.astro'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ },
27
+ },
28
+ )
package/firebase.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "firestore": {
3
+ "rules": "firestore.rules"
4
+ }
5
+ }
@@ -0,0 +1,29 @@
1
+ rules_version = '2';
2
+ service cloud.firestore {
3
+ match /databases/{database}/documents {
4
+ match /resume_interest/{docId} {
5
+ allow create: if
6
+ request.resource.data.keys().hasOnly([
7
+ 'email',
8
+ 'source',
9
+ 'userAgent',
10
+ 'createdAt'
11
+ ])
12
+ && request.resource.data.email is string
13
+ && request.resource.data.email.size() > 5
14
+ && request.resource.data.email.size() <= 254
15
+ && request.resource.data.email.matches('^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$')
16
+ && request.resource.data.source == 'resume_page'
17
+ && request.resource.data.userAgent is string
18
+ && request.resource.data.userAgent.size() > 0
19
+ && request.resource.data.userAgent.size() <= 1024
20
+ && request.resource.data.createdAt == request.time;
21
+
22
+ allow read, update, delete: if false;
23
+ }
24
+
25
+ match /{document=**} {
26
+ allow read, write: if false;
27
+ }
28
+ }
29
+ }
package/index.html ADDED
@@ -0,0 +1,52 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1.0, maximum-scale=10.0, user-scalable=yes"
8
+ />
9
+ <meta
10
+ name="description"
11
+ content="my cozy little personal website"
12
+ data-route-meta="description"
13
+ />
14
+ <link
15
+ rel="canonical"
16
+ href="https://akinevz.com/"
17
+ data-route-meta="canonical"
18
+ />
19
+ <meta property="og:type" content="website" />
20
+ <meta property="og:title" content="home of kine" data-route-meta="og:title" />
21
+ <meta
22
+ property="og:description"
23
+ content="my cozy little personal website"
24
+ data-route-meta="og:description"
25
+ />
26
+ <meta property="og:url" content="https://akinevz.com/" data-route-meta="og:url" />
27
+ <meta property="og:image" content="https://akinevz.com/avatar.png" data-route-meta="og:image" />
28
+ <meta name="twitter:card" content="summary" data-route-meta="twitter:card" />
29
+ <meta
30
+ name="twitter:image"
31
+ content="https://akinevz.com/avatar.png"
32
+ data-route-meta="twitter:image"
33
+ />
34
+ <meta name="twitter:title" content="home of kine" data-route-meta="twitter:title" />
35
+ <meta
36
+ name="twitter:description"
37
+ content="my cozy little personal website"
38
+ data-route-meta="twitter:description"
39
+ />
40
+ <title data-route-meta="title">home of kine</title>
41
+ <link
42
+ rel="stylesheet"
43
+ href="https://unpkg.com/xp.css@0.2.3/dist/98.css"
44
+ id="theme-css"
45
+ />
46
+ <noscript><link rel="stylesheet" href="/loading.css" /></noscript>
47
+ </head>
48
+ <body>
49
+ <div id="root"></div>
50
+ <script type="module" src="/src/main.tsx"></script>
51
+ </body>
52
+ </html>
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "website-xp-phone",
3
+ "version": "1.5.0",
4
+ "author": "Kirill <kine> Nevzorov <akinevz@outlook.com>",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "npx -y node@20 ./scripts/hoist-dev-blog-local.mjs",
8
+ "build": "npm run generate:music && vite build",
9
+ "build:local": "npm run generate:music && npm run publish:music && vite build",
10
+ "preview": "vite preview --host 127.0.0.1 --port 8086",
11
+ "lint": "eslint ./src --ext .ts,.tsx",
12
+ "test": "node --test",
13
+ "test:watch": "node --test --watch",
14
+ "check:audit": "npm audit --audit-level=low",
15
+ "security:check": "npm run lint && npm run test && npm run check:audit",
16
+ "pagerts": "NPM_CONFIG_LOGLEVEL=error npx --yes pagerts@latest",
17
+ "generate:music": "node ./scripts/generate-soundcloud-json.mjs",
18
+ "publish:music": "node ./scripts/publish-soundcloud-json.mjs"
19
+ },
20
+ "dependencies": {
21
+ "firebase": "^12.14.0",
22
+ "react": "^19.2.4",
23
+ "react-dom": "^19.2.4",
24
+ "react-markdown": "^10.1.0",
25
+ "react-toastify": "^11.0.5",
26
+ "rehype-raw": "^7.0.0",
27
+ "rehype-sanitize": "^6.0.0",
28
+ "xp.css": "^0.2.6"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/js": "^9.39.4",
32
+ "@types/node": "^24.9.1",
33
+ "@types/react": "^19.2.14",
34
+ "@types/react-dom": "^19.2.3",
35
+ "@vitejs/plugin-react": "^6.0.1",
36
+ "eslint": "^9.39.4",
37
+ "eslint-plugin-react-hooks": "^7.0.1",
38
+ "eslint-plugin-react-refresh": "^0.5.2",
39
+ "globals": "^17.4.0",
40
+ "pagerts": "^1.5.4",
41
+ "typescript": "^5.9.3",
42
+ "typescript-eslint": "^8.58.0",
43
+ "vite": "^8.0.10"
44
+ },
45
+ "overrides": {
46
+ "lodash": "^4.17.21"
47
+ }
48
+ }