fix: input alignment
This commit is contained in:
@@ -43,7 +43,9 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
|
||||
+11
-6
@@ -34,7 +34,7 @@ export default function AdminPage() {
|
||||
|
||||
const loadStats = async () => {
|
||||
if (!isAuthenticated) return;
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const data = await getAdminStats();
|
||||
@@ -116,14 +116,16 @@ export default function AdminPage() {
|
||||
<div className="stat-card highlight">
|
||||
<span className="stat-icon">🔤</span>
|
||||
<div className="stat-info">
|
||||
<span className="stat-value">{formatTokens(stats.totalTokens)}</span>
|
||||
<span className="stat-value">
|
||||
{formatTokens(stats.totalTokens)}
|
||||
</span>
|
||||
<span className="stat-label">Всего токенов</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Статистика по историям</h2>
|
||||
|
||||
|
||||
<div className="stories-table-wrapper">
|
||||
<table className="stories-table">
|
||||
<thead>
|
||||
@@ -137,7 +139,9 @@ export default function AdminPage() {
|
||||
<tbody>
|
||||
{stats.stories.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} className="no-data">Нет историй</td>
|
||||
<td colSpan={4} className="no-data">
|
||||
Нет историй
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
stats.stories.map((story) => (
|
||||
@@ -155,8 +159,9 @@ export default function AdminPage() {
|
||||
|
||||
<div className="token-info">
|
||||
<p>
|
||||
💡 <strong>Примечание:</strong> Токены рассчитаны приблизительно (1 токен ≈ 3 символа для русского текста).
|
||||
Реальное потребление может отличаться.
|
||||
💡 <strong>Примечание:</strong> Токены рассчитаны приблизительно (1
|
||||
токен ≈ 3 символа для русского текста). Реальное потребление может
|
||||
отличаться.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -288,6 +288,7 @@
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem 1.5rem;
|
||||
background: #131313;
|
||||
@@ -296,6 +297,8 @@
|
||||
|
||||
.input-container textarea {
|
||||
flex: 1;
|
||||
min-height: 50px;
|
||||
max-height: 120px;
|
||||
padding: 0.875rem 1rem;
|
||||
background: #1a1a1a;
|
||||
border: 2px solid #333;
|
||||
@@ -318,6 +321,7 @@
|
||||
|
||||
.send-btn {
|
||||
width: 50px;
|
||||
min-width: 50px;
|
||||
height: 50px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
@@ -328,7 +332,7 @@
|
||||
transition:
|
||||
transform 0.2s,
|
||||
opacity 0.2s;
|
||||
align-self: flex-end;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.send-btn:hover:not(:disabled) {
|
||||
|
||||
+1
-63
@@ -1,9 +1,5 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
useParams,
|
||||
Link,
|
||||
useSearchParams,
|
||||
} from "react-router-dom";
|
||||
import { useParams, Link, useSearchParams } from "react-router-dom";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import {
|
||||
@@ -44,61 +40,6 @@ function formatTokens(tokens: number): string {
|
||||
return tokens.toString();
|
||||
}
|
||||
|
||||
// Пытаемся определить локацию из последних сообщений
|
||||
function detectLocation(messages: ChatMessage[]): string {
|
||||
if (!messages || messages.length === 0) return "Неизвестно";
|
||||
|
||||
// Берём последние 3 сообщения ассистента
|
||||
const recentAssistant = messages
|
||||
.filter((m) => m.role === "assistant")
|
||||
.slice(-3)
|
||||
.map((m) => m.content)
|
||||
.join(" ");
|
||||
|
||||
// Паттерны для определения локации
|
||||
const locationPatterns = [
|
||||
/(?:находи(?:тесь|шься)|оказыва(?:етесь|ешься)|стои(?:те|шь))\s+(?:в|на|у)\s+([^.,!?]+)/i,
|
||||
/(?:вошл[аи]?|входи(?:те|шь)|попада(?:ете|ешь))\s+(?:в|на)\s+([^.,!?]+)/i,
|
||||
/(?:прибыл[аи]?|приход(?:ите|ишь)|добрал(?:ись|ась|ся))\s+(?:в|на|до)\s+([^.,!?]+)/i,
|
||||
/(?:комнат[аеуы]|зал[аеуы]?|пещер[аеуы]|лес[ау|замок|двор(?:ец)?|тавер[нуыа]|город[ауе]?|деревн[яюией]|тронн[ыйая]|подземель[яеи])\s*([^.,!?]*)/i,
|
||||
];
|
||||
|
||||
for (const pattern of locationPatterns) {
|
||||
const match = recentAssistant.match(pattern);
|
||||
if (match && match[1]) {
|
||||
// Чистим и обрезаем результат
|
||||
let location = match[1].trim();
|
||||
if (location.length > 25) location = location.substring(0, 25) + "...";
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
// Простой поиск ключевых слов
|
||||
const simpleLocations: [RegExp, string][] = [
|
||||
[/тронн(?:ый|ого|ом)\s*зал/i, "Тронный зал"],
|
||||
[/тавер[нуыа]/i, "Таверна"],
|
||||
[/замок|замк[ауе]/i, "Замок"],
|
||||
[/лес[ау]?/i, "Лес"],
|
||||
[/пещер[аеуы]/i, "Пещера"],
|
||||
[/город[ауе]?/i, "Город"],
|
||||
[/деревн[яюией]/i, "Деревня"],
|
||||
[/подземель[яеи]/i, "Подземелье"],
|
||||
[/двор(?:ец|ц[ауе])/i, "Дворец"],
|
||||
[/рын(?:ок|к[ауе])/i, "Рынок"],
|
||||
[/храм[ауе]?/i, "Храм"],
|
||||
[/библиотек/i, "Библиотека"],
|
||||
[/казарм/i, "Казармы"],
|
||||
];
|
||||
|
||||
for (const [pattern, name] of simpleLocations) {
|
||||
if (pattern.test(recentAssistant)) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return "Неизвестно";
|
||||
}
|
||||
|
||||
export default function GamePage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -409,9 +350,6 @@ export default function GamePage() {
|
||||
<span className="stat-badge tokens">
|
||||
🎟️ {formatTokens(estimateTokens(session?.messages || []))}
|
||||
</span>
|
||||
<span className="stat-badge location">
|
||||
📍 {detectLocation(session?.messages || [])}
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user