XMWAI 0.3.25__py3-none-any.whl → 0.3.27__py3-none-any.whl
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.
- XMWAI/web_core copy.py +283 -0
- XMWAI/web_core.py +61 -106
- {xmwai-0.3.25.dist-info → xmwai-0.3.27.dist-info}/METADATA +1 -1
- {xmwai-0.3.25.dist-info → xmwai-0.3.27.dist-info}/RECORD +7 -6
- {xmwai-0.3.25.dist-info → xmwai-0.3.27.dist-info}/WHEEL +0 -0
- {xmwai-0.3.25.dist-info → xmwai-0.3.27.dist-info}/licenses/LICENSE.txt +0 -0
- {xmwai-0.3.25.dist-info → xmwai-0.3.27.dist-info}/top_level.txt +0 -0
XMWAI/web_core copy.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import requests
|
|
4
|
+
from flask import Flask, render_template
|
|
5
|
+
import re
|
|
6
|
+
from pyecharts import options as opts
|
|
7
|
+
from pyecharts.charts import Pie
|
|
8
|
+
import webbrowser
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
from importlib.resources import files
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
'''Super堡'''
|
|
16
|
+
# 搭建flask框架
|
|
17
|
+
app = Flask(
|
|
18
|
+
__name__,
|
|
19
|
+
template_folder=os.path.join(os.path.dirname(__file__), "templates"),
|
|
20
|
+
static_folder=os.path.join(os.path.dirname(__file__), "static")
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
mapping = {
|
|
24
|
+
"面包底": "BottomBun",
|
|
25
|
+
"生菜": "lettuce",
|
|
26
|
+
"番茄": "tomato",
|
|
27
|
+
"牛肉饼": "beef",
|
|
28
|
+
"芝士": "cheese",
|
|
29
|
+
"酱料": "sauce",
|
|
30
|
+
"面包顶": "TopBun"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ingredients_order = []
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def burger(result):
|
|
37
|
+
global ingredients_order
|
|
38
|
+
inputs = result.strip().split("→")
|
|
39
|
+
ingredients_order = [mapping[i] for i in inputs]
|
|
40
|
+
ingredients_order = ingredients_order[::-1]
|
|
41
|
+
|
|
42
|
+
# 自动启动服务器
|
|
43
|
+
start_server()
|
|
44
|
+
return ingredients_order
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.route('/')
|
|
48
|
+
def show_burger():
|
|
49
|
+
return render_template("burger.html", ingredients=ingredients_order)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def run_server(port=5050):
|
|
53
|
+
"""在后台线程中运行服务器"""
|
|
54
|
+
app.run(debug=False, host='0.0.0.0', port=port, use_reloader=False)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def start_server(port=5050):
|
|
58
|
+
"""启动服务器并打开浏览器"""
|
|
59
|
+
url = f"http://127.0.0.1:{port}/"
|
|
60
|
+
|
|
61
|
+
# 在后台线程中启动服务器
|
|
62
|
+
server_thread = threading.Thread(target=run_server, args=(port,))
|
|
63
|
+
server_thread.daemon = True
|
|
64
|
+
server_thread.start()
|
|
65
|
+
|
|
66
|
+
# 等待服务器启动
|
|
67
|
+
time.sleep(2)
|
|
68
|
+
|
|
69
|
+
# 打开浏览器
|
|
70
|
+
webbrowser.open(url)
|
|
71
|
+
|
|
72
|
+
# 保持服务器运行
|
|
73
|
+
try:
|
|
74
|
+
server_thread.join()
|
|
75
|
+
except KeyboardInterrupt:
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
'''Random料理屋'''
|
|
80
|
+
# 食材对应 emoji
|
|
81
|
+
emoji_dict = {
|
|
82
|
+
"鸡": "🍗", "牛": "🥩", "猪": "🥓", "鱼": "🐟", "虾": "🦐", "蟹": "🦀",
|
|
83
|
+
"豆腐": "🧈", "土豆": "🥔", "胡萝卜": "🥕", "西红柿": "🍅", "青菜": "🥬",
|
|
84
|
+
"菠菜": "🥬", "蘑菇": "🍄", "玉米": "🌽", "米饭": "🍚", "面条": "🍜",
|
|
85
|
+
"面包": "🍞", "奶酪": "🧀", "鸡蛋": "🥚", "牛奶": "🥛", "橙子": "🍊",
|
|
86
|
+
"苹果": "🍎", "香蕉": "🍌"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# 动作对应 emoji
|
|
90
|
+
action_dict = {
|
|
91
|
+
"炒": "🍳", "煮": "🍲", "烤": "🔥", "蒸": "♨️", "炸": "🍟", "拌": "🥣",
|
|
92
|
+
"切": "🔪", "腌": "🫙", "炖": "🥘"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def add_emoji_to_text(text):
|
|
97
|
+
for key, val in action_dict.items():
|
|
98
|
+
text = re.sub(f'({key})', f'{val} \\1', text)
|
|
99
|
+
for key, val in emoji_dict.items():
|
|
100
|
+
text = re.sub(f'({key})', f'{val} \\1', text)
|
|
101
|
+
return text
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def format_section_steps(text):
|
|
105
|
+
lines = [line.strip() for line in text.strip().split("\n") if line.strip()]
|
|
106
|
+
lines = lines[:50]
|
|
107
|
+
return "<br>".join(add_emoji_to_text(line) for line in lines)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def parse_nutrition_section(text):
|
|
111
|
+
"""解析 API 返回的营养 JSON 并提取数值"""
|
|
112
|
+
default_data = {"蛋白质": 30, "脂肪": 20, "碳水化合物": 50, "维生素": 10, "矿物质": 5}
|
|
113
|
+
|
|
114
|
+
def extract_number(val):
|
|
115
|
+
if isinstance(val, (int, float)):
|
|
116
|
+
return val
|
|
117
|
+
if isinstance(val, str):
|
|
118
|
+
match = re.search(r"(\d+(\.\d+)?)", val)
|
|
119
|
+
if match:
|
|
120
|
+
return float(match.group(1))
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
match = re.search(r"\{.*\}", text, re.S)
|
|
125
|
+
if match:
|
|
126
|
+
parsed = json.loads(match.group())
|
|
127
|
+
result = {}
|
|
128
|
+
for key in default_data.keys():
|
|
129
|
+
if key in parsed:
|
|
130
|
+
val = parsed[key]
|
|
131
|
+
if isinstance(val, dict):
|
|
132
|
+
total = sum(extract_number(v) for v in val.values())
|
|
133
|
+
result[key] = total
|
|
134
|
+
else:
|
|
135
|
+
result[key] = extract_number(val)
|
|
136
|
+
else:
|
|
137
|
+
result[key] = default_data[key]
|
|
138
|
+
return result
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print("JSON解析失败:", e)
|
|
141
|
+
return default_data
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def generate_pie_chart(data_dict, filename: Path):
|
|
145
|
+
data = [(k, v) for k, v in data_dict.items()]
|
|
146
|
+
pie = (
|
|
147
|
+
Pie(init_opts=opts.InitOpts(width="1100px", height="500px"))
|
|
148
|
+
.add("", data)
|
|
149
|
+
.set_global_opts(title_opts=opts.TitleOpts(title="营养价值分布"))
|
|
150
|
+
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
|
|
151
|
+
)
|
|
152
|
+
pie.render(str(filename))
|
|
153
|
+
return filename
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def cookbook(m, t, s, key):
|
|
157
|
+
if key != "CaJQ":
|
|
158
|
+
return "密钥错误,无法生成食谱。"
|
|
159
|
+
|
|
160
|
+
# 调用 API 生成创意菜谱
|
|
161
|
+
messagesList = [
|
|
162
|
+
{"role": "system", "content": "天马行空的创意菜厨师"},
|
|
163
|
+
{"role": "user", "content": f"请以{m}为主菜,{s}为配菜,{t}为烹饪方式写一个创意食谱,"
|
|
164
|
+
"结果中不要*,并且结果只需要创意灵感、食材清单、制作步骤、"
|
|
165
|
+
"食材搭配的营养价值四种大标题内容。食材搭配的营养价值部分请输出标准 JSON,"
|
|
166
|
+
"键为蛋白质、脂肪、碳水化合物、维生素、矿物质,值为数值及说明。"}
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
url = "https://qianfan.baidubce.com/v2/chat/completions"
|
|
170
|
+
payload = json.dumps({"model": "ernie-4.5-turbo-32k",
|
|
171
|
+
"messages": messagesList}, ensure_ascii=False)
|
|
172
|
+
headers = {
|
|
173
|
+
"Content-Type": "application/json",
|
|
174
|
+
"appid": "",
|
|
175
|
+
"Authorization": "Bearer bce-v3/ALTAK-cGbxpVA5AbSz6h8nbLaFh/b539762075d55c76d93dc78bcf0a91beeaf0490a"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
response = requests.post(url, headers=headers,
|
|
180
|
+
data=payload.encode("utf-8"))
|
|
181
|
+
response_data = response.json()
|
|
182
|
+
content = response_data["choices"][0]["message"]["content"]
|
|
183
|
+
except Exception as e:
|
|
184
|
+
return f"接口调用失败:{e}"
|
|
185
|
+
|
|
186
|
+
# 分割内容
|
|
187
|
+
sections = re.split(r"(创意灵感|食材清单|制作步骤|食材搭配的营养价值)", content)
|
|
188
|
+
body_sections = [""]*4
|
|
189
|
+
title_map = {"创意灵感": 0, "食材清单": 1, "制作步骤": 2, "食材搭配的营养价值": 3}
|
|
190
|
+
i = 1
|
|
191
|
+
while i < len(sections):
|
|
192
|
+
header = sections[i]
|
|
193
|
+
text_sec = sections[i+1] if i+1 < len(sections) else ""
|
|
194
|
+
idx = title_map.get(header.strip(), None)
|
|
195
|
+
if idx is not None:
|
|
196
|
+
body_sections[idx] = text_sec.strip()
|
|
197
|
+
i += 2
|
|
198
|
+
|
|
199
|
+
# 模板和图片目录
|
|
200
|
+
templates_dir = Path(files("XMWAI") / "templates")
|
|
201
|
+
templates_dir.mkdir(exist_ok=True)
|
|
202
|
+
|
|
203
|
+
# 静态图片路径(PyPI 安装后能读取)
|
|
204
|
+
bg_path = Path(files("XMWAI") / "static" / "images" / "bg.jpeg")
|
|
205
|
+
|
|
206
|
+
# 生成饼图文件
|
|
207
|
+
pie_chart_file = templates_dir / "nutrition_pie.html"
|
|
208
|
+
nutrient_data = parse_nutrition_section(body_sections[3])
|
|
209
|
+
generate_pie_chart(nutrient_data, pie_chart_file)
|
|
210
|
+
|
|
211
|
+
# 添加 emoji
|
|
212
|
+
m_emoji = add_emoji_to_text(m)
|
|
213
|
+
s_emoji = add_emoji_to_text(s)
|
|
214
|
+
t_emoji = add_emoji_to_text(t)
|
|
215
|
+
|
|
216
|
+
# 步骤顺序 HTML
|
|
217
|
+
step_titles = ["食材搭配的营养价值", "创意灵感", "食材清单", "制作步骤"]
|
|
218
|
+
steps_order = [3, 0, 1, 2]
|
|
219
|
+
steps_html = ""
|
|
220
|
+
for i, idx in enumerate(steps_order):
|
|
221
|
+
if idx == 3:
|
|
222
|
+
section_content_html = "根据食材搭配生成的营养价值饼图如下 ⬇️"
|
|
223
|
+
else:
|
|
224
|
+
section_content_html = format_section_steps(body_sections[idx])
|
|
225
|
+
steps_html += f"""
|
|
226
|
+
<div class="step-card" style="animation-delay:{(i+1)*0.2}s;">
|
|
227
|
+
<div class="step-title">Step {i+1} 📝 {step_titles[i]}</div>
|
|
228
|
+
<div class="step-content">{section_content_html}</div>
|
|
229
|
+
</div>
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
# HTML 页面
|
|
233
|
+
html = f"""
|
|
234
|
+
<!DOCTYPE html>
|
|
235
|
+
<html lang="zh">
|
|
236
|
+
<head>
|
|
237
|
+
<meta charset="UTF-8">
|
|
238
|
+
<title>创意菜谱</title>
|
|
239
|
+
<style>
|
|
240
|
+
html, body {{ margin:0; padding:0; width:100%; height:100%; overflow-x:hidden; }}
|
|
241
|
+
body {{ font-family:"微软雅黑",sans-serif; background:#2c2c2c url('{bg_path.resolve()}') no-repeat center center fixed; background-size:cover; color:#333; }}
|
|
242
|
+
.container {{ max-width:1200px; margin:30px auto; background:rgba(255,248,220,0.95); border-radius:15px; padding:30px; box-shadow:0 0 20px rgba(0,0,0,0.2); }}
|
|
243
|
+
.banner {{ width:100%; height:220px; background:url('{bg_path.resolve()}') center/cover no-repeat; border-radius:15px 15px 0 0; display:flex; align-items:center; justify-content:center; }}
|
|
244
|
+
.banner h1 {{ color:#fff; font-size:28px; text-shadow:1px 1px 3px #666; }}
|
|
245
|
+
p {{ font-size:18px; margin:8px 0; }}
|
|
246
|
+
.step-card {{ background:#fff0b3; margin:10px 0; border-radius:12px; overflow:hidden; opacity:0; transform:translateY(20px) scale(0.98); animation:fadeInUp 0.6s forwards; }}
|
|
247
|
+
.step-title {{ font-weight:bold; padding:10px 15px; cursor:pointer; background:#ffb347; color:#fff; border-bottom:1px solid #ffd27f; }}
|
|
248
|
+
.step-content {{ padding:10px 15px; display:block; font-size:16px; opacity:0; max-height:0; overflow:hidden; transition: opacity 0.4s ease, max-height 0.4s ease; }}
|
|
249
|
+
.step-card.hover .step-content {{ opacity:1; max-height:800px; }}
|
|
250
|
+
iframe {{ width:100%; height:500px; border:none; margin-top:20px; }}
|
|
251
|
+
@keyframes fadeInUp {{ to {{ opacity:1; transform:translateY(0) scale(1); }} }}
|
|
252
|
+
</style>
|
|
253
|
+
</head>
|
|
254
|
+
<body>
|
|
255
|
+
<div class="container">
|
|
256
|
+
<div class="banner"><h1>🍽 {m+t+s}</h1></div>
|
|
257
|
+
<p>🍖 <strong>主菜:</strong>{m_emoji}</p>
|
|
258
|
+
<p>🥗 <strong>配菜:</strong>{s_emoji}</p>
|
|
259
|
+
<p>👩🍳 <strong>做法:</strong>{t_emoji}</p>
|
|
260
|
+
{steps_html}
|
|
261
|
+
<iframe src="{pie_chart_file.resolve()}"></iframe>
|
|
262
|
+
</div>
|
|
263
|
+
<script>
|
|
264
|
+
const steps = document.querySelectorAll('.step-card');
|
|
265
|
+
steps.forEach(card => {{
|
|
266
|
+
card.addEventListener('mouseenter', () => {{ card.classList.add('hover'); }});
|
|
267
|
+
card.addEventListener('mouseleave', () => {{ card.classList.remove('hover'); }});
|
|
268
|
+
}});
|
|
269
|
+
</script>
|
|
270
|
+
</body>
|
|
271
|
+
</html>
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
# 保存 HTML 文件到包内 templates
|
|
275
|
+
safe_title = re.sub(r'[\/\\:*?"<>|]', "", m+t+s)
|
|
276
|
+
html_file = templates_dir / f"{safe_title}_菜谱.html"
|
|
277
|
+
with open(html_file, "w", encoding="utf-8") as f:
|
|
278
|
+
f.write(html)
|
|
279
|
+
|
|
280
|
+
# 打开浏览器
|
|
281
|
+
webbrowser.open(f"file://{html_file.resolve()}")
|
|
282
|
+
|
|
283
|
+
return content
|
XMWAI/web_core.py
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from PIL import Image, ImageDraw
|
|
2
|
-
import random
|
|
3
1
|
import os
|
|
4
2
|
import json
|
|
5
3
|
import requests
|
|
6
4
|
from flask import Flask, render_template
|
|
7
5
|
import re
|
|
6
|
+
import shutil
|
|
8
7
|
from pyecharts import options as opts
|
|
9
8
|
from pyecharts.charts import Pie
|
|
10
9
|
import webbrowser
|
|
@@ -90,8 +89,8 @@ emoji_dict = {
|
|
|
90
89
|
|
|
91
90
|
# 动作对应 emoji
|
|
92
91
|
action_dict = {
|
|
93
|
-
"炒": "🍳", "煮": "🍲", "烤": "🔥", "蒸": "♨️", "炸": "🍟",
|
|
94
|
-
"
|
|
92
|
+
"炒": "🍳", "煮": "🍲", "烤": "🔥", "蒸": "♨️", "炸": "🍟", "拌": "🥣",
|
|
93
|
+
"切": "🔪", "腌": "🫙", "炖": "🥘"
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
|
|
@@ -110,6 +109,7 @@ def format_section_steps(text):
|
|
|
110
109
|
|
|
111
110
|
|
|
112
111
|
def parse_nutrition_section(text):
|
|
112
|
+
"""解析 API 返回的营养 JSON 并提取数值"""
|
|
113
113
|
default_data = {"蛋白质": 30, "脂肪": 20, "碳水化合物": 50, "维生素": 10, "矿物质": 5}
|
|
114
114
|
|
|
115
115
|
def extract_number(val):
|
|
@@ -154,34 +154,11 @@ def generate_pie_chart(data_dict, filename: Path):
|
|
|
154
154
|
return filename
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def random_gradient_bg(bg_path: Path):
|
|
158
|
-
"""生成一张随机渐变背景图"""
|
|
159
|
-
width, height = 1200, 800
|
|
160
|
-
|
|
161
|
-
# 随机挑两个颜色 (RGB)
|
|
162
|
-
start_color = (random.randint(100, 255), random.randint(
|
|
163
|
-
100, 255), random.randint(100, 255))
|
|
164
|
-
end_color = (random.randint(100, 255), random.randint(
|
|
165
|
-
100, 255), random.randint(100, 255))
|
|
166
|
-
|
|
167
|
-
img = Image.new("RGB", (width, height), start_color)
|
|
168
|
-
draw = ImageDraw.Draw(img)
|
|
169
|
-
|
|
170
|
-
for y in range(height):
|
|
171
|
-
r = int(start_color[0] + (end_color[0] - start_color[0]) * y / height)
|
|
172
|
-
g = int(start_color[1] + (end_color[1] - start_color[1]) * y / height)
|
|
173
|
-
b = int(start_color[2] + (end_color[2] - start_color[2]) * y / height)
|
|
174
|
-
draw.line([(0, y), (width, y)], fill=(r, g, b))
|
|
175
|
-
|
|
176
|
-
img.save(bg_path, "JPEG")
|
|
177
|
-
print(f"🎨 生成随机渐变背景:{bg_path}")
|
|
178
|
-
|
|
179
|
-
|
|
180
157
|
def cookbook(m, t, s, key):
|
|
181
158
|
if key != "CaJQ":
|
|
182
159
|
return "密钥错误,无法生成食谱。"
|
|
183
160
|
|
|
184
|
-
# 调用 API
|
|
161
|
+
# 调用 API 生成创意菜谱
|
|
185
162
|
messagesList = [
|
|
186
163
|
{"role": "system", "content": "天马行空的创意菜厨师"},
|
|
187
164
|
{"role": "user", "content": f"请以{m}为主菜,{s}为配菜,{t}为烹饪方式写一个创意食谱,"
|
|
@@ -193,8 +170,11 @@ def cookbook(m, t, s, key):
|
|
|
193
170
|
url = "https://qianfan.baidubce.com/v2/chat/completions"
|
|
194
171
|
payload = json.dumps({"model": "ernie-4.5-turbo-32k",
|
|
195
172
|
"messages": messagesList}, ensure_ascii=False)
|
|
196
|
-
headers = {
|
|
197
|
-
|
|
173
|
+
headers = {
|
|
174
|
+
"Content-Type": "application/json",
|
|
175
|
+
"appid": "",
|
|
176
|
+
"Authorization": "Bearer bce-v3/ALTAK-cGbxpVA5AbSz6h8nbLaFh/b539762075d55c76d93dc78bcf0a91beeaf0490a"
|
|
177
|
+
}
|
|
198
178
|
|
|
199
179
|
try:
|
|
200
180
|
response = requests.post(url, headers=headers,
|
|
@@ -206,12 +186,12 @@ def cookbook(m, t, s, key):
|
|
|
206
186
|
|
|
207
187
|
# 分割内容
|
|
208
188
|
sections = re.split(r"(创意灵感|食材清单|制作步骤|食材搭配的营养价值)", content)
|
|
209
|
-
body_sections = [""]
|
|
189
|
+
body_sections = [""]*4
|
|
210
190
|
title_map = {"创意灵感": 0, "食材清单": 1, "制作步骤": 2, "食材搭配的营养价值": 3}
|
|
211
191
|
i = 1
|
|
212
192
|
while i < len(sections):
|
|
213
193
|
header = sections[i]
|
|
214
|
-
text_sec = sections[i
|
|
194
|
+
text_sec = sections[i+1] if i+1 < len(sections) else ""
|
|
215
195
|
idx = title_map.get(header.strip(), None)
|
|
216
196
|
if idx is not None:
|
|
217
197
|
body_sections[idx] = text_sec.strip()
|
|
@@ -221,20 +201,23 @@ def cookbook(m, t, s, key):
|
|
|
221
201
|
templates_dir = Path(files("XMWAI") / "templates")
|
|
222
202
|
templates_dir.mkdir(exist_ok=True)
|
|
223
203
|
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
204
|
+
# 从包内 static 拷贝背景图到模板目录
|
|
205
|
+
bg_src = Path(files("XMWAI") / "static" / "images" / "bg.jpeg")
|
|
206
|
+
bg_copy = templates_dir / "bg.jpeg"
|
|
207
|
+
if not bg_copy.exists():
|
|
208
|
+
shutil.copy(bg_src, bg_copy)
|
|
227
209
|
|
|
228
|
-
#
|
|
210
|
+
# 生成饼图文件
|
|
229
211
|
pie_chart_file = templates_dir / "nutrition_pie.html"
|
|
230
212
|
nutrient_data = parse_nutrition_section(body_sections[3])
|
|
231
213
|
generate_pie_chart(nutrient_data, pie_chart_file)
|
|
232
214
|
|
|
233
215
|
# 添加 emoji
|
|
234
|
-
m_emoji
|
|
235
|
-
|
|
216
|
+
m_emoji = add_emoji_to_text(m)
|
|
217
|
+
s_emoji = add_emoji_to_text(s)
|
|
218
|
+
t_emoji = add_emoji_to_text(t)
|
|
236
219
|
|
|
237
|
-
#
|
|
220
|
+
# 步骤顺序 HTML
|
|
238
221
|
step_titles = ["食材搭配的营养价值", "创意灵感", "食材清单", "制作步骤"]
|
|
239
222
|
steps_order = [3, 0, 1, 2]
|
|
240
223
|
steps_html = ""
|
|
@@ -244,89 +227,61 @@ def cookbook(m, t, s, key):
|
|
|
244
227
|
else:
|
|
245
228
|
section_content_html = format_section_steps(body_sections[idx])
|
|
246
229
|
steps_html += f"""
|
|
247
|
-
<div class="step-card" style="animation-delay:{(i
|
|
248
|
-
<div class="step-title">Step {i
|
|
230
|
+
<div class="step-card" style="animation-delay:{(i+1)*0.2}s;">
|
|
231
|
+
<div class="step-title">Step {i+1} 📝 {step_titles[i]}</div>
|
|
249
232
|
<div class="step-content">{section_content_html}</div>
|
|
250
233
|
</div>
|
|
251
234
|
"""
|
|
252
235
|
|
|
253
|
-
#
|
|
254
|
-
bg_rel_path = os.path.relpath(bg_path, templates_dir)
|
|
255
|
-
pie_rel_path = os.path.relpath(pie_chart_file, templates_dir)
|
|
256
|
-
|
|
257
|
-
# HTML 文件
|
|
258
|
-
safe_title = re.sub(r'[\/\\:*?"<>|]', "", m + t + s)
|
|
259
|
-
html_file = templates_dir / f"{safe_title}_菜谱.html"
|
|
260
|
-
|
|
236
|
+
# HTML 页面 (背景图引用相对路径 bg.jpeg)
|
|
261
237
|
html = f"""
|
|
262
238
|
<!DOCTYPE html>
|
|
263
239
|
<html lang="zh">
|
|
264
240
|
<head>
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.banner {{
|
|
282
|
-
width:100%; height:220px;
|
|
283
|
-
background:url('{bg_rel_path}') center/cover no-repeat;
|
|
284
|
-
border-radius:15px 15px 0 0;
|
|
285
|
-
display:flex; align-items:center; justify-content:center;
|
|
286
|
-
}}
|
|
287
|
-
.banner h1 {{ color:#fff; font-size:28px; text-shadow:1px 1px 3px #666; }}
|
|
288
|
-
p {{ font-size:18px; margin:8px 0; }}
|
|
289
|
-
.step-card {{
|
|
290
|
-
background:#fff0b3; margin:10px 0; border-radius:12px;
|
|
291
|
-
overflow:hidden; opacity:0; transform:translateY(20px) scale(0.98);
|
|
292
|
-
animation:fadeInUp 0.6s forwards;
|
|
293
|
-
}}
|
|
294
|
-
.step-title {{
|
|
295
|
-
font-weight:bold; padding:10px 15px; cursor:pointer;
|
|
296
|
-
background:#ffb347; color:#fff; border-bottom:1px solid #ffd27f;
|
|
297
|
-
}}
|
|
298
|
-
.step-content {{
|
|
299
|
-
padding:10px 15px; display:block; font-size:16px;
|
|
300
|
-
opacity:0; max-height:0; overflow:hidden;
|
|
301
|
-
transition: opacity 0.4s ease, max-height 0.4s ease;
|
|
302
|
-
}}
|
|
303
|
-
.step-card.hover .step-content {{ opacity:1; max-height:800px; }}
|
|
304
|
-
iframe {{ width:100%; height:500px; border:none; margin-top:20px; }}
|
|
305
|
-
@keyframes fadeInUp {{ to {{ opacity:1; transform:translateY(0) scale(1); }} }}
|
|
306
|
-
</style>
|
|
241
|
+
<meta charset="UTF-8">
|
|
242
|
+
<title>创意菜谱</title>
|
|
243
|
+
<style>
|
|
244
|
+
html, body {{ margin:0; padding:0; width:100%; height:100%; overflow-x:hidden; }}
|
|
245
|
+
body {{ font-family:"微软雅黑",sans-serif; background:#2c2c2c url('bg.jpeg') no-repeat center center fixed; background-size:cover; color:#333; }}
|
|
246
|
+
.container {{ max-width:1200px; margin:30px auto; background:rgba(255,248,220,0.95); border-radius:15px; padding:30px; box-shadow:0 0 20px rgba(0,0,0,0.2); }}
|
|
247
|
+
.banner {{ width:100%; height:220px; background:url('bg.jpeg') center/cover no-repeat; border-radius:15px 15px 0 0; display:flex; align-items:center; justify-content:center; }}
|
|
248
|
+
.banner h1 {{ color:#fff; font-size:28px; text-shadow:1px 1px 3px #666; }}
|
|
249
|
+
p {{ font-size:18px; margin:8px 0; }}
|
|
250
|
+
.step-card {{ background:#fff0b3; margin:10px 0; border-radius:12px; overflow:hidden; opacity:0; transform:translateY(20px) scale(0.98); animation:fadeInUp 0.6s forwards; }}
|
|
251
|
+
.step-title {{ font-weight:bold; padding:10px 15px; cursor:pointer; background:#ffb347; color:#fff; border-bottom:1px solid #ffd27f; }}
|
|
252
|
+
.step-content {{ padding:10px 15px; display:block; font-size:16px; opacity:0; max-height:0; overflow:hidden; transition: opacity 0.4s ease, max-height 0.4s ease; }}
|
|
253
|
+
.step-card.hover .step-content {{ opacity:1; max-height:800px; }}
|
|
254
|
+
iframe {{ width:100%; height:500px; border:none; margin-top:20px; }}
|
|
255
|
+
@keyframes fadeInUp {{ to {{ opacity:1; transform:translateY(0) scale(1); }} }}
|
|
256
|
+
</style>
|
|
307
257
|
</head>
|
|
308
258
|
<body>
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
259
|
+
<div class="container">
|
|
260
|
+
<div class="banner"><h1>🍽 {m+t+s}</h1></div>
|
|
261
|
+
<p>🍖 <strong>主菜:</strong>{m_emoji}</p>
|
|
262
|
+
<p>🥗 <strong>配菜:</strong>{s_emoji}</p>
|
|
263
|
+
<p>👩🍳 <strong>做法:</strong>{t_emoji}</p>
|
|
264
|
+
{steps_html}
|
|
265
|
+
<iframe src="{pie_chart_file.name}"></iframe>
|
|
266
|
+
</div>
|
|
267
|
+
<script>
|
|
268
|
+
const steps = document.querySelectorAll('.step-card');
|
|
269
|
+
steps.forEach(card => {{
|
|
270
|
+
card.addEventListener('mouseenter', () => {{ card.classList.add('hover'); }});
|
|
271
|
+
card.addEventListener('mouseleave', () => {{ card.classList.remove('hover'); }});
|
|
272
|
+
}});
|
|
273
|
+
</script>
|
|
324
274
|
</body>
|
|
325
275
|
</html>
|
|
326
276
|
"""
|
|
327
277
|
|
|
278
|
+
# 保存 HTML 文件到包内 templates
|
|
279
|
+
safe_title = re.sub(r'[\/\\:*?"<>|]', "", m+t+s)
|
|
280
|
+
html_file = templates_dir / f"{safe_title}_菜谱.html"
|
|
328
281
|
with open(html_file, "w", encoding="utf-8") as f:
|
|
329
282
|
f.write(html)
|
|
330
283
|
|
|
284
|
+
# 打开浏览器
|
|
331
285
|
webbrowser.open(f"file://{html_file.resolve()}")
|
|
286
|
+
|
|
332
287
|
return content
|
|
@@ -4,7 +4,8 @@ XMWAI/core.py,sha256=XG2ZLhW9CETUQMr-8FLZonL_aQg9jfohGnm9oU5XyVQ,10018
|
|
|
4
4
|
XMWAI/idiom_core.py,sha256=yU-VHhqqoutVm6GVikcjL3m9yuB1hUsFBpPYvwY4n5g,1689
|
|
5
5
|
XMWAI/magic_core.py,sha256=Ms4b12PJ8rjsmceg1VUqWCWx2ebvdhLp4sIF6K_Vaok,3491
|
|
6
6
|
XMWAI/trial_class.py,sha256=GRDEe24a-DYMRyoTLgJbE5dCSQY0CbYqB5ALsBfYmtA,10226
|
|
7
|
-
XMWAI/web_core.py,sha256=
|
|
7
|
+
XMWAI/web_core copy.py,sha256=3nXhv2VdLT5RbWpRF2f7bBPmMaEDt5ZvLl_LarseByI,10781
|
|
8
|
+
XMWAI/web_core.py,sha256=3bDNyo5n-l2TsDfuPJv_DRT6sKxr6ZcYF176uIOQOVM,10913
|
|
8
9
|
XMWAI/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
10
|
XMWAI/file/idiom.json,sha256=HUtPRUzhxBbWoasjadbmbA_5ngQ5AXLu9weQSZ4hzhk,10319857
|
|
10
11
|
XMWAI/gif/0.gif,sha256=LGpAReVTyZEb1J-bWYTpxxHbIxmLJ-3wA0lw4cBqdsY,303
|
|
@@ -107,8 +108,8 @@ XMWAI/static/images/tomato.png,sha256=FEOEAOdUhW_BDFgTpxOkYc0I5Iu29_gtHb3RIPEej0
|
|
|
107
108
|
XMWAI/templates/burger.html,sha256=vDnxpSW8phetyScySsalScZnFKl3LNpy5lJjKxGXgbI,3320
|
|
108
109
|
XMWAI/templates/nutrition_pie.html,sha256=yJVXI28i-UfvF0xOXGSNLMb8oCJNhh2J3zoRDr5_7DM,5567
|
|
109
110
|
XMWAI/templates/创意菜谱.html,sha256=RcDgH58QLyUJ9A59wobu3wvchGBY1snVsXcZQZam5M0,4805
|
|
110
|
-
xmwai-0.3.
|
|
111
|
-
xmwai-0.3.
|
|
112
|
-
xmwai-0.3.
|
|
113
|
-
xmwai-0.3.
|
|
114
|
-
xmwai-0.3.
|
|
111
|
+
xmwai-0.3.27.dist-info/licenses/LICENSE.txt,sha256=bcaIQMrIhdQ3O-PoZlexjmW6h-wLGvHxh5Oksl4ohtc,1066
|
|
112
|
+
xmwai-0.3.27.dist-info/METADATA,sha256=4VP7uX2hEuNK_fvh1PoRZEdaTljaEYCJ5zXcYoWinl8,1198
|
|
113
|
+
xmwai-0.3.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
114
|
+
xmwai-0.3.27.dist-info/top_level.txt,sha256=yvGcDI-sggK5jqd9wz0saipZvk3oIE3hNGHlqUjxf0c,6
|
|
115
|
+
xmwai-0.3.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|