v1.0.0: Add version display in footer

This commit is contained in:
Alexej Wolff
2026-05-04 18:56:56 +02:00
parent f73a218745
commit efd2332875
8 changed files with 39 additions and 9 deletions
+2
View File
@@ -16,11 +16,13 @@
## Технологии ## Технологии
### Frontend ### Frontend
- React 18 + TypeScript - React 18 + TypeScript
- Vite - Vite
- CSS (без фреймворков) - CSS (без фреймворков)
### Backend ### Backend
- Express.js (прокси-сервер) - Express.js (прокси-сервер)
- DeepSeek API (модель deepseek-chat V3) - DeepSeek API (модель deepseek-chat V3)
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "resekai", "name": "resekai",
"private": true, "private": true,
"version": "0.0.0", "version": "1.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+8
View File
@@ -26,6 +26,14 @@
color: #888; color: #888;
} }
.footer-version {
font-size: 0.75rem;
color: #555;
padding: 0.15rem 0.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
.footer-copyright { .footer-copyright {
font-size: 0.8rem; font-size: 0.8rem;
color: #555; color: #555;
+1
View File
@@ -30,6 +30,7 @@ export function Footer() {
<div className="footer-content"> <div className="footer-content">
<div className="footer-left"> <div className="footer-left">
<span className="footer-brand"> ReSekai</span> <span className="footer-brand"> ReSekai</span>
<span className="footer-version">v{__APP_VERSION__}</span>
<span className="footer-copyright">© 2026</span> <span className="footer-copyright">© 2026</span>
</div> </div>
+3 -3
View File
@@ -88,11 +88,11 @@ export default function GamePage() {
const handleBeforeUnload = (e: BeforeUnloadEvent) => { const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (hasUnsavedChangesRef.current || isLoading) { if (hasUnsavedChangesRef.current || isLoading) {
e.preventDefault(); e.preventDefault();
e.returnValue = ''; e.returnValue = "";
} }
}; };
window.addEventListener('beforeunload', handleBeforeUnload); window.addEventListener("beforeunload", handleBeforeUnload);
return () => window.removeEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener("beforeunload", handleBeforeUnload);
}, [isLoading]); }, [isLoading]);
// Throttled streaming update (every 50ms instead of every chunk) // Throttled streaming update (every 50ms instead of every chunk)
+15 -5
View File
@@ -271,7 +271,10 @@ ${story.plot}`;
/** /**
* Builds dynamic context (state + summary + rule reminders) * Builds dynamic context (state + summary + rule reminders)
*/ */
export function buildDynamicContext(session: GameSession, messageCount?: number): string { export function buildDynamicContext(
session: GameSession,
messageCount?: number,
): string {
const state = session.currentState; const state = session.currentState;
const summary = session.storySummary || "The story just began."; const summary = session.storySummary || "The story just began.";
const keyEvents = session.keyEvents?.length const keyEvents = session.keyEvents?.length
@@ -279,13 +282,16 @@ export function buildDynamicContext(session: GameSession, messageCount?: number)
: "No significant events yet."; : "No significant events yet.";
// Add rule reminders after 10+ messages to prevent drift // Add rule reminders after 10+ messages to prevent drift
const ruleReminder = (messageCount && messageCount >= 10) ? ` const ruleReminder =
messageCount && messageCount >= 10
? `
=== REMINDER === === REMINDER ===
• Do NOT act for the player — only describe reactions and consequences • Do NOT act for the player — only describe reactions and consequences
• Do NOT ask "What do you do?" — end with atmosphere, not questions • Do NOT ask "What do you do?" — end with atmosphere, not questions
• Format dialogue: **"text"** (double asterisks = bold) • Format dialogue: **"text"** (double asterisks = bold)
• React to player's words explicitly` : ''; • React to player's words explicitly`
: "";
return ` return `
=== CURRENT STATE === === CURRENT STATE ===
@@ -327,7 +333,9 @@ export async function generateStoryResponse(
const worldContext = buildWorldContext(story); const worldContext = buildWorldContext(story);
// 3. Dynamic context (state + summary + rule reminders after 10+ messages) // 3. Dynamic context (state + summary + rule reminders after 10+ messages)
const dynamicContext = session ? buildDynamicContext(session, chatHistory.length) : ""; const dynamicContext = session
? buildDynamicContext(session, chatHistory.length)
: "";
// 4. Last N messages (not the full history!) // 4. Last N messages (not the full history!)
const recentMessages = chatHistory.slice(-RECENT_MESSAGES_COUNT); const recentMessages = chatHistory.slice(-RECENT_MESSAGES_COUNT);
@@ -362,7 +370,9 @@ export async function generateStoryResponseStream(
): Promise<string> { ): Promise<string> {
const styleRules = buildStyleRules(story, player); const styleRules = buildStyleRules(story, player);
const worldContext = buildWorldContext(story); const worldContext = buildWorldContext(story);
const dynamicContext = session ? buildDynamicContext(session, chatHistory.length) : ""; const dynamicContext = session
? buildDynamicContext(session, chatHistory.length)
: "";
const recentMessages = chatHistory.slice(-RECENT_MESSAGES_COUNT); const recentMessages = chatHistory.slice(-RECENT_MESSAGES_COUNT);
const systemPrompt = styleRules + "\n" + worldContext + "\n" + dynamicContext; const systemPrompt = styleRules + "\n" + worldContext + "\n" + dynamicContext;
+3
View File
@@ -0,0 +1,3 @@
/// <reference types="vite/client" />
declare const __APP_VERSION__: string;
+6
View File
@@ -1,7 +1,13 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import { readFileSync } from 'fs'
const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'))
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
define: {
__APP_VERSION__: JSON.stringify(pkg.version),
},
}) })