Spaces:
Sleeping
Sleeping
add app
Browse files- README.md +35 -1
- app.py +111 -0
- assets/styles.css +28 -0
- requirements.txt +3 -0
README.md
CHANGED
@@ -10,4 +10,38 @@ pinned: false
|
|
10 |
license: mit
|
11 |
---
|
12 |
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
license: mit
|
11 |
---
|
12 |
|
13 |
+
# 🧙🌀 Fantasy Characters Explorer
|
14 |
+
**Интерактивная библиотека персонажей для RPG и вдохновения**
|
15 |
+
|
16 |
+
[](https://huggingface.co/spaces/loim/ru_fantasy_characters_explorer)
|
17 |
+
[](https://huggingface.co/datasets/loim/ru_fantasy_characters)
|
18 |
+
|
19 |
+
## ✨ Особенности
|
20 |
+
- **Поиск** по имени, описанию или тегам
|
21 |
+
- **Фильтры**: абсурдность, настроение, теги
|
22 |
+
- **RPG-карточки** в формате Markdown (копирование в 1 клик)
|
23 |
+
- **Сортировка** по имени/уровню абсурдности
|
24 |
+
- Атмосферный дизайн
|
25 |
+
|
26 |
+
## 🎮 Как использовать
|
27 |
+
1. Выберите фильтры
|
28 |
+
2. Нажмите на персонажа, чтобы раскрыть детали
|
29 |
+
3. Скопируйте код для RPG-сессий через иконку 📋
|
30 |
+
4. Вдохновляйтесь и создавайте истории!
|
31 |
+
|
32 |
+
## 🗝️ Пример персонажа
|
33 |
+
```markdown
|
34 |
+
**Имя:** Каландра Сольна Губа
|
35 |
+
**Мир:** Омут Шепчущих Волн — океан, где вода тверже стали...
|
36 |
+
**Настроение:** Мрачный
|
37 |
+
**Абсурдность:** 7/10
|
38 |
+
|
39 |
+
**Описание:** Женщина с чешуйчатой кожей цвета шторма...
|
40 |
+
> *«Твой корабль... уже дал течь...»*
|
41 |
+
```
|
42 |
+
|
43 |
+
## 🛠️ Технологии
|
44 |
+
`Python` · `Streamlit` · `Hugging Face Datasets`
|
45 |
+
|
46 |
+
---
|
47 |
+
🔮 *Создано для тех, кто ищет магию в деталях*
|
app.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from datasets import load_dataset
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
# Настройки страницы
|
6 |
+
st.set_page_config(layout="wide", page_icon="🧙", page_title="Fantasy Characters Explorer")
|
7 |
+
|
8 |
+
# Загрузка данных
|
9 |
+
@st.cache_data
|
10 |
+
def load_data():
|
11 |
+
dataset = load_dataset("loim/ru_fantasy_characters")
|
12 |
+
return pd.DataFrame(dataset["train"])
|
13 |
+
|
14 |
+
df = load_data()
|
15 |
+
|
16 |
+
# Функция для генерации RPG-карточки
|
17 |
+
def generate_rpg_card(row):
|
18 |
+
return f"""
|
19 |
+
**Мир:** {row['world']}
|
20 |
+
|
21 |
+
**Имя:** {row['name']}
|
22 |
+
**Внешность:** {row['description']}
|
23 |
+
**Характер:** {row['short_story']}
|
24 |
+
**Речь:** {row['style']}
|
25 |
+
|
26 |
+
**Первая реплика:**
|
27 |
+
> {row['first_message']}
|
28 |
+
"""
|
29 |
+
|
30 |
+
# Стили
|
31 |
+
def local_css(file_name):
|
32 |
+
with open(file_name) as f:
|
33 |
+
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)
|
34 |
+
|
35 |
+
local_css("assets/styles.css")
|
36 |
+
|
37 |
+
# --- САЙДБАР С ФИЛЬТРАМИ ---
|
38 |
+
with st.sidebar:
|
39 |
+
st.title("🔮 Фильтры")
|
40 |
+
search_query = st.text_input("🔍 Поиск по имени/описанию", "")
|
41 |
+
sort_option = st.selectbox(
|
42 |
+
"Сортировка",
|
43 |
+
["По имени (А→Я)", "По имени (Я→А)", "По абсурдности (↑)", "По абсурдности (↓)"]
|
44 |
+
)
|
45 |
+
absurdity_range = st.slider("Уровень абсурдности", 0, 10, (0, 10))
|
46 |
+
moods = ["Все"] + sorted(df["mood"].unique().tolist())
|
47 |
+
selected_mood = st.selectbox("Настроение", moods)
|
48 |
+
all_tags = sorted({tag for tags in df["tags"] for tag in tags.split()})
|
49 |
+
selected_tags = st.multiselect("Теги", all_tags)
|
50 |
+
|
51 |
+
# --- ФИЛЬТРАЦИЯ И СОРТИРОВКА ---
|
52 |
+
filtered_df = df[
|
53 |
+
(df["absurdity"] >= absurdity_range[0]) &
|
54 |
+
(df["absurdity"] <= absurdity_range[1])
|
55 |
+
]
|
56 |
+
|
57 |
+
if selected_mood != "Все":
|
58 |
+
filtered_df = filtered_df[filtered_df["mood"] == selected_mood]
|
59 |
+
|
60 |
+
if selected_tags:
|
61 |
+
filtered_df = filtered_df[filtered_df["tags"].str.contains("|".join(selected_tags))]
|
62 |
+
|
63 |
+
if search_query:
|
64 |
+
filtered_df = filtered_df[
|
65 |
+
filtered_df["name"].str.contains(search_query, case=False) |
|
66 |
+
filtered_df["description"].str.contains(search_query, case=False) |
|
67 |
+
filtered_df["short_story"].str.contains(search_query, case=False)
|
68 |
+
]
|
69 |
+
|
70 |
+
# Применение сортировки
|
71 |
+
if "А→Я" in sort_option:
|
72 |
+
filtered_df = filtered_df.sort_values("name")
|
73 |
+
elif "Я→А" in sort_option:
|
74 |
+
filtered_df = filtered_df.sort_values("name", ascending=False)
|
75 |
+
elif "↑" in sort_option:
|
76 |
+
filtered_df = filtered_df.sort_values("absurdity")
|
77 |
+
else:
|
78 |
+
filtered_df = filtered_df.sort_values("absurdity", ascending=False)
|
79 |
+
|
80 |
+
# --- ОТОБРАЖЕНИЕ РЕЗУЛЬТАТОВ ---
|
81 |
+
st.title("🧙 Fantasy Characters Explorer")
|
82 |
+
st.markdown(f"**Найдено персонажей:** {len(filtered_df)}")
|
83 |
+
|
84 |
+
for idx, row in filtered_df.iterrows():
|
85 |
+
with st.container():
|
86 |
+
# Заголовок карточки
|
87 |
+
st.markdown(f"### {row['name']}")
|
88 |
+
st.caption(f"*{row['short_story']}*")
|
89 |
+
|
90 |
+
# Основные колонки
|
91 |
+
col1, col2 = st.columns([1, 3])
|
92 |
+
|
93 |
+
with col1:
|
94 |
+
st.markdown(f"**🌍 Мир:** {row['world']}")
|
95 |
+
st.markdown(f"**🎭 Настроение:** `{row['mood']}`")
|
96 |
+
st.markdown(f"**🌀 Абсурдность:** `{row['absurdity']}/10`")
|
97 |
+
st.markdown(f"**🏷️ Теги:** `{' '.join(row['tags'].split())}`")
|
98 |
+
|
99 |
+
with col2:
|
100 |
+
st.markdown(f"**📖 Описание:** {row['description']}")
|
101 |
+
st.markdown(f"**💬 Стиль речи:** *{row['style']}*")
|
102 |
+
|
103 |
+
# RPG-карточка
|
104 |
+
with st.expander("📋 RPG-карточка", expanded=False):
|
105 |
+
rpg_card = generate_rpg_card(row)
|
106 |
+
st.code(rpg_card, language="markdown")
|
107 |
+
|
108 |
+
st.markdown("---")
|
109 |
+
|
110 |
+
if len(filtered_df) == 0:
|
111 |
+
st.warning("Персонажи не найдены. Попробуйте изменить фильтры.")
|
assets/styles.css
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Основные стили */
|
2 |
+
.st-emotion-cache-1v0mbdj {
|
3 |
+
border-radius: 10px;
|
4 |
+
border: 1px solid #2d5d7b;
|
5 |
+
}
|
6 |
+
|
7 |
+
.st-emotion-cache-1v0mbdj:hover {
|
8 |
+
box-shadow: 0 0 10px #3d8fd1;
|
9 |
+
}
|
10 |
+
|
11 |
+
/* Карточки персонажей */
|
12 |
+
.stExpander {
|
13 |
+
background-color: #0e2a3a;
|
14 |
+
border-radius: 10px;
|
15 |
+
padding: 15px;
|
16 |
+
margin-bottom: 20px;
|
17 |
+
border-left: 5px solid #3d8fd1;
|
18 |
+
}
|
19 |
+
|
20 |
+
/* Заголовки */
|
21 |
+
h1 {
|
22 |
+
color: #3d8fd1 !important;
|
23 |
+
}
|
24 |
+
|
25 |
+
/* Сайдбар */
|
26 |
+
.st-emotion-cache-6qob1r {
|
27 |
+
background-color: #0a1f29 !important;
|
28 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
datasets
|
3 |
+
pandas
|