Browse Source

Add mobile ELF test script and initial UI layout for Yidong application

newdev_shunjiawei
liwei1dao 7 months ago
parent
commit
eeda106629
  1. 1018
      lib/modules/mobile_elf/controllers/mobile_elf_controller.dart
  2. 972
      lib/modules/mobile_elf/views/mobile_elf_view.dart
  3. 81
      test_mobile_elf.py
  4. 563
      ui/yidong.html

1018
lib/modules/mobile_elf/controllers/mobile_elf_controller.dart

File diff suppressed because it is too large

972
lib/modules/mobile_elf/views/mobile_elf_view.dart

File diff suppressed because it is too large

81
test_mobile_elf.py

@ -0,0 +1,81 @@
#!/usr/bin/env python3
import ssl, time, hashlib, json, websocket
DEVICE_NO = '199839C0AAAE99653D2E'
DEVICE_ID = '2032013791868043266'
PRODUCT_ID = '2031271967792472065'
PRODUCT_KEY = 'hotepZZwvouWQjHz'
DEVICE_SECRET = 'ijdthZGQELhkmigf'
ts = str(int(time.time() * 1000))
sign = hashlib.md5((DEVICE_SECRET + ts).encode()).hexdigest()
url = (f'wss://ivs.chinamobiledevice.com:11443/app-ws/v2/asr'
f'?deviceNo={DEVICE_NO}&deviceId={DEVICE_ID}'
f'&productId={PRODUCT_ID}&productKey={PRODUCT_KEY}'
f'&sign={sign}&sn=py-test&ts={ts}')
print(f'connecting...\n{url}')
ws = websocket.WebSocket(sslopt={'cert_reqs': ssl.CERT_NONE})
ws.settimeout(15)
ws.connect(url)
print('connected! sending start frame immediately...')
import uuid
start_frame = json.dumps({
'type': 'start',
'data': {
'cuid': DEVICE_ID,
'format': 'pcm',
'sample': 16000,
'support_dcs': 2,
'chunk_size': 10240,
'support_tts': True,
'support_text2dcs': True,
'user_agent': 'voitrans/1.0',
'dialog_request_id': str(uuid.uuid4()),
'access_rc': True,
'support_part_tts': True,
'need_stoplisten': True,
'need_dialogue_finish': True,
'result_trans2directive': False,
'rc_version': '5',
'messages': [],
'client_context': [],
}
})
ws.send(start_frame)
print('start frame sent, waiting for ready...')
# 等待 ready
while True:
msg = ws.recv()
print('received:', msg)
data = json.loads(msg)
if data.get('type') == 'ready':
print('--- ready received, sending text "你好" ---')
break
# 发送文本
text_frame = json.dumps({'type': 'text', 'data': {'text': '你好'}})
ws.send(text_frame)
print('text frame sent')
# 发送 finish
finish_frame = json.dumps({'type': 'finish'})
ws.send(finish_frame)
print('finish frame sent, waiting for AI response...')
# 接收所有回包直到 end=1
while True:
msg = ws.recv()
print('received:', msg)
try:
data = json.loads(msg)
if data.get('end') == 1:
print('--- session end (end=1), closing ---')
break
except Exception:
pass
ws.close()
print('done.')

563
ui/yidong.html

@ -0,0 +1,563 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=375, initial-scale=1">
<title>移动精灵 UI 预览</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #e8edf2;
font-family: -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
display: flex; gap: 20px; padding: 24px;
flex-wrap: wrap; justify-content: center; align-items: flex-start;
}
/* ── Phone shell ── */
.phone {
width: 375px; height: 700px;
background: #f4f6f9;
border-radius: 44px;
border: 1px solid #d0d5dd;
overflow: hidden;
display: flex; flex-direction: column;
box-shadow: 0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.06);
}
/* ── AppBar ── */
.appbar {
height: 56px; flex-shrink: 0;
background: #fff;
border-bottom: 1px solid #eaecf0;
display: flex; align-items: center;
padding: 0 16px; gap: 8px;
}
.appbar-back { color: #667085; font-size: 22px; line-height: 1; cursor: pointer; }
.appbar-avatar {
width: 32px; height: 32px; border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 14px; font-weight: 600; flex-shrink: 0;
}
.appbar-name { font-size: 16px; font-weight: 600; color: #101828; flex: 1; }
.status-pill {
font-size: 11px; font-weight: 500;
padding: 3px 10px; border-radius: 20px;
display: flex; align-items: center; gap: 4px;
}
.pill-idle { background: #f2f4f7; color: #667085; }
.pill-connecting { background: #fffaeb; color: #b54708; }
.pill-recording { background: #ecfdf3; color: #027a48; }
.pill-thinking { background: #eff8ff; color: #175cd3; }
.pill-speaking { background: #ecfdf3; color: #027a48; }
.pill-timer { font-weight: 400; }
/* ── Chat area ── */
.chat-area {
flex: 1; overflow-y: auto;
padding: 16px 16px 8px;
display: flex; flex-direction: column; gap: 14px;
}
.empty-state {
flex: 1; display: flex; flex-direction: column;
align-items: center; justify-content: center; gap: 8px;
}
.empty-avatar {
width: 80px; height: 80px; border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 32px; font-weight: 600; margin-bottom: 12px;
}
.empty-title { font-size: 20px; font-weight: bold; color: #101828; }
.empty-sub { font-size: 13px; color: #667085; }
.msg-row { display: flex; flex-direction: column; }
.msg-row.user { align-items: flex-end; }
.msg-row.ai { align-items: flex-start; gap: 4px; }
.ai-label { display: flex; align-items: center; gap: 6px; }
.ai-dot {
width: 22px; height: 22px; border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; align-items: center; justify-content: center;
color: #fff; font-size: 10px;
}
.ai-name { font-size: 11px; color: #98a2b3; }
.bubble {
max-width: 72%; padding: 10px 14px;
font-size: 15px; line-height: 1.55;
word-break: break-all;
}
.bubble-user {
background: #2e7cf6; color: #fff;
border-radius: 18px 18px 4px 18px;
}
.bubble-ai {
background: #fff; color: #1d2939;
border-radius: 4px 18px 18px 18px;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
.msg-time { font-size: 11px; color: #d0d5dd; margin-top: 3px; }
/* ── Bottom bar ── */
.bottom-bar {
flex-shrink: 0;
background: #fff;
border-top: 1px solid #eaecf0;
box-shadow: 0 -2px 12px rgba(0,0,0,0.05);
}
/* ── 输入区域 ── */
.input-row {
display: flex; align-items: center; gap: 8px;
padding: 8px 12px 18px;
}
.mode-toggle {
width: 38px; height: 38px; border-radius: 50%;
background: #f2f4f7; border: none;
display: flex; align-items: center; justify-content: center;
cursor: pointer; flex-shrink: 0; color: #667085; font-size: 18px;
}
/* 文本输入框(内含发送按钮) */
.text-input-wrap {
flex: 1; height: 42px; border-radius: 21px;
background: #f2f4f7;
display: flex; align-items: center;
padding: 0 4px 0 16px;
}
.text-input-wrap input {
flex: 1; height: 100%; border: none; background: transparent;
font-size: 14px; color: #101828; outline: none;
}
.text-input-wrap input::placeholder { color: #98a2b3; }
.send-btn-inner {
width: 32px; height: 32px; border-radius: 50%;
background: linear-gradient(135deg, #667eea, #5a67d8);
border: none; cursor: pointer; color: #fff; font-size: 16px;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.call-btn {
width: 44px; height: 44px; border-radius: 50%;
background: linear-gradient(135deg, #34c759, #28a745);
border: none; cursor: pointer; color: #fff; font-size: 18px;
display: flex; align-items: center; justify-content: center; flex-shrink: 0;
box-shadow: 0 4px 14px rgba(52,199,89,.4);
}
/* 按住说话按钮(微信风格) */
.ptt-btn {
flex: 1; height: 42px; border-radius: 21px;
background: #fff; border: 1px solid #d0d5dd;
display: flex; align-items: center; justify-content: center;
cursor: pointer; font-size: 15px; color: #101828; font-weight: 600;
letter-spacing: 2px;
transition: all .15s;
user-select: none;
}
.ptt-btn:active {
background: #f2f4f7;
border-color: #98a2b3;
}
/* 禁用状态 */
.ptt-btn.disabled {
opacity: 0.5;
pointer-events: none;
}
/* PTT 录音中 - 中间按钮(橙色) */
.ptt-record-center {
flex: 1; height: 42px; border-radius: 21px;
background: linear-gradient(135deg, #f79009, #dc6803);
display: flex; align-items: center; justify-content: center; gap: 8px;
box-shadow: 0 2px 8px rgba(247,144,9,.3);
color: #fff; font-size: 14px; font-weight: 500;
}
/* PTT 连接中 - 与按住说话空闲状态相同 */
.ptt-connecting-center {
flex: 1; height: 42px; border-radius: 21px;
background: #fff; border: 1px solid #d0d5dd;
display: flex; align-items: center; justify-content: center; gap: 8px;
color: #667085; font-size: 14px; font-weight: 500;
}
/* 取消按钮(替换左侧切换按钮位置) */
.cancel-toggle {
width: 38px; height: 38px; border-radius: 50%;
background: #f2f4f7; border: 1px solid #eaecf0;
display: flex; align-items: center; justify-content: center;
cursor: pointer; flex-shrink: 0; color: #ef4444; font-size: 16px;
}
/* PTT 实时识别文字(渐变背景) */
.ptt-realtime {
padding: 12px 20px; margin: 0 12px 4px;
background: linear-gradient(135deg, #f0f4ff, #e8f5e9);
border: 0.5px solid #e2e8f0;
border-radius: 16px;
font-size: 15px; color: #101828;
text-align: center;
line-height: 1.4;
}
/* ── 状态提示条 ── */
.status-strip {
padding: 8px 16px;
display: flex; align-items: center; gap: 8px;
font-size: 13px; font-weight: 500;
}
.strip-speaking { background: #ecfdf3; color: #16a34a; }
.strip-thinking { background: #eff8ff; color: #175cd3; }
.strip-connecting { background: #fffaeb; color: #b54708; }
.strip-recording { background: #ecfdf3; color: #16a34a; }
.interrupt-btn {
margin-left: auto;
padding: 4px 12px; border-radius: 16px;
border: 1px solid #ef4444; background: transparent;
color: #ef4444; font-size: 12px; font-weight: 500;
cursor: pointer;
}
/* ── 通话底栏 ── */
.call-bottom {
padding: 12px 20px 18px;
display: flex; align-items: center; gap: 12px;
}
.call-status {
display: flex; align-items: center; gap: 6px; flex: 1;
}
.call-dot { width: 8px; height: 8px; border-radius: 50%; }
.call-label { font-size: 13px; color: #667085; }
/* 音量指示器 */
.vol-indicator {
width: 44px; height: 44px; border-radius: 50%;
background: #f2f4f7;
display: flex; align-items: center; justify-content: center; gap: 2px;
flex-shrink: 0;
}
.vol-bar {
width: 3px; border-radius: 1.5px; background: #d0d5dd;
transition: background .15s;
}
.vol-bar.active { background: #34c759; }
.hangup-btn {
width: 52px; height: 52px; border-radius: 50%;
background: #ef4444; border: none; color: #fff;
display: flex; align-items: center; justify-content: center;
cursor: pointer; flex-shrink: 0;
box-shadow: 0 4px 14px rgba(239,68,68,.3);
}
/* Spinner */
.spinner {
width: 22px; height: 22px;
border: 2.5px solid currentColor; border-top-color: transparent;
border-radius: 50%; animation: spin .7s linear infinite;
}
.spinner-sm { width: 14px; height: 14px; border-width: 2px; }
@keyframes spin { to { transform: rotate(360deg); } }
/* Mini wave */
.mini-wave { display: flex; gap: 2px; align-items: center; }
.mini-wave span {
display: block; width: 2.5px; border-radius: 2px;
animation: mwave .8s ease-in-out infinite;
}
.mini-wave span:nth-child(1) { animation-delay: 0s; }
.mini-wave span:nth-child(2) { animation-delay: .13s; }
.mini-wave span:nth-child(3) { animation-delay: .26s; }
@keyframes mwave {
0%,100% { height: 3px; }
50% { height: 10px; }
}
.label {
font-size: 11px; color: #98a2b3; text-align: center;
padding: 6px 0 0; letter-spacing: .3px; font-weight: 500;
}
</style>
</head>
<body>
<!-- ① 文本输入模式(空闲) -->
<div>
<p class="label">① 文本输入模式</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-idle">空闲</span>
</div>
<div class="chat-area">
<div class="empty-state">
<div class="empty-avatar">晓</div>
<div class="empty-title">移动精灵</div>
<div class="empty-sub">点击右下角按钮,即可与晓晓对话</div>
</div>
</div>
<div class="bottom-bar">
<div class="input-row">
<button class="mode-toggle" title="切换到语音">🎤</button>
<div class="text-input-wrap">
<input placeholder="继续提问..." />
<button class="send-btn-inner">↑</button>
</div>
<button class="call-btn">📞</button>
</div>
</div>
</div>
</div>
<!-- ② 语音输入模式(空闲) -->
<div>
<p class="label">② 语音模式 - 按住说话</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-idle">空闲</span>
</div>
<div class="chat-area">
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">你好!我是晓晓,有什么可以帮你的吗?</div>
<span class="msg-time">14:23</span>
</div>
</div>
<div class="bottom-bar">
<div class="input-row">
<button class="mode-toggle" title="切换到文本">⌨</button>
<div class="ptt-btn">按住 说话</div>
<button class="call-btn">📞</button>
</div>
</div>
</div>
</div>
<!-- ③ PTT 按住中 - 连接阶段(灰色,保留两侧按钮) -->
<div>
<p class="label">③ 按住说话 - 连接中(同空闲样式)</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-connecting">连接中…</span>
</div>
<div class="chat-area">
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">你好!我是晓晓,有什么可以帮你的吗?</div>
<span class="msg-time">14:23</span>
</div>
</div>
<div class="bottom-bar">
<div class="input-row">
<button class="mode-toggle" style="opacity:0.4;pointer-events:none;">⌨</button>
<div class="ptt-connecting-center">
<div class="spinner spinner-sm" style="color:#667085;"></div>
<span>连接中...</span>
</div>
<button class="call-btn" style="opacity:0.4;pointer-events:none;">📞</button>
</div>
</div>
</div>
</div>
<!-- ④ PTT 按住中 - 录音中 + 实时识别(绿色,渐变识别背景) -->
<div>
<p class="label">④ 按住说话 - 录音中(橙色)</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-recording">识别中</span>
</div>
<div class="chat-area">
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">你好!我是晓晓,有什么可以帮你的吗?</div>
<span class="msg-time">14:23</span>
</div>
</div>
<div class="bottom-bar">
<!-- 居中实时识别文字(渐变背景) -->
<div class="ptt-realtime">今天天气怎么样?</div>
<div class="input-row" style="padding-top:4px;">
<button class="mode-toggle" style="opacity:0.4;pointer-events:none;">⌨</button>
<div class="ptt-record-center">
<div class="mini-wave"><span style="background:#fff;height:6px;"></span><span style="background:#fff;height:10px;"></span><span style="background:#fff;height:4px;"></span></div>
<span>松开发送 · 2s</span>
</div>
<button class="call-btn" style="opacity:0.4;pointer-events:none;">📞</button>
</div>
</div>
</div>
</div>
<!-- ⑤ AI 思考中(输入栏保留,半透明禁用) -->
<div>
<p class="label">⑤ AI 思考中(输入栏禁用)</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-thinking">处理中</span>
</div>
<div class="chat-area">
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">你好!我是晓晓,有什么可以帮你的吗?</div>
<span class="msg-time">14:23</span>
</div>
<div class="msg-row user">
<div class="bubble bubble-user">今天天气怎么样?</div>
<span class="msg-time">14:24</span>
</div>
</div>
<div class="bottom-bar">
<div class="status-strip strip-thinking">
<div class="spinner spinner-sm" style="color:#175cd3;"></div>
<span>AI 思考中...</span>
</div>
<div class="input-row" style="padding-top:4px; opacity:0.5; pointer-events:none;">
<button class="mode-toggle">⌨</button>
<div class="ptt-btn">按住 说话</div>
<button class="call-btn">📞</button>
</div>
</div>
</div>
</div>
<!-- ⑥ AI 播报中(输入栏保留,可打断) -->
<div>
<p class="label">⑥ AI 播报中(可打断)</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-speaking">朗读中</span>
</div>
<div class="chat-area">
<div class="msg-row user">
<div class="bubble bubble-user">今天天气怎么样?</div>
<span class="msg-time">14:24</span>
</div>
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">今天晴天,气温 18°C,微风,空气质量优,很适合出行哦</div>
<span class="msg-time">14:24</span>
</div>
</div>
<div class="bottom-bar">
<div class="status-strip strip-speaking">
<div class="mini-wave"><span style="background:#16a34a;"></span><span style="background:#16a34a;"></span><span style="background:#16a34a;"></span></div>
<span>晓晓正在朗读...</span>
<button class="interrupt-btn">✕ 打断</button>
</div>
<div class="input-row" style="padding-top:4px; opacity:0.5; pointer-events:none;">
<button class="mode-toggle">⌨</button>
<div class="ptt-btn">按住 说话</div>
<button class="call-btn">📞</button>
</div>
</div>
</div>
</div>
<!-- ⑦ 通话模式 - 等待说话(音量指示器 + 挂断) -->
<div>
<p class="label">⑦ 通话模式 - 等待说话</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-recording">通话中 <span class="pill-timer">01:23</span></span>
</div>
<div class="chat-area">
<div class="msg-row user">
<div class="bubble bubble-user">今天天气怎么样?</div>
<span class="msg-time">14:24</span>
</div>
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">今天晴天,气温 18°C,微风,空气质量优</div>
<span class="msg-time">14:24</span>
</div>
</div>
<div class="bottom-bar">
<div class="call-bottom">
<div class="call-status">
<div class="call-dot" style="background:#34c759;"></div>
<span class="call-label">通话中</span>
<span class="call-label">01:23</span>
</div>
<!-- 音量指示器 -->
<div class="vol-indicator">
<div class="vol-bar active" style="height:8px;"></div>
<div class="vol-bar active" style="height:12px;"></div>
<div class="vol-bar active" style="height:16px;"></div>
<div class="vol-bar" style="height:20px;"></div>
<div class="vol-bar" style="height:24px;"></div>
</div>
<!-- 挂断按钮 -->
<button class="hangup-btn">
<svg width="26" height="26" viewBox="0 0 24 24" fill="currentColor"><path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08a.956.956 0 0 1 0-1.36C3.36 8.85 7.47 7 12 7s8.64 1.85 11.71 4.72c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.1-.7-.28-.79-.73-1.68-1.36-2.66-1.85-.33-.16-.56-.5-.56-.9v-3.1C14.15 9.25 12.6 9 12 9z"/></svg>
</button>
</div>
</div>
</div>
</div>
<!-- ⑧ 通话模式 - AI 播报中 -->
<div>
<p class="label">⑧ 通话模式 - AI 播报</p>
<div class="phone">
<div class="appbar">
<span class="appbar-back">‹</span>
<div class="appbar-avatar">晓</div>
<span class="appbar-name">移动精灵</span>
<span class="status-pill pill-speaking">朗读中 <span class="pill-timer">01:48</span></span>
</div>
<div class="chat-area">
<div class="msg-row user">
<div class="bubble bubble-user">Flutter 有什么优势?</div>
<span class="msg-time">14:41</span>
</div>
<div class="msg-row ai">
<div class="ai-label"><div class="ai-dot">晓</div><span class="ai-name">晓晓</span></div>
<div class="bubble bubble-ai">Flutter 的核心优势在于跨平台能力强、渲染性能高,以及 Dart 语言的高效开发体验...</div>
<span class="msg-time">14:41</span>
</div>
</div>
<div class="bottom-bar">
<div class="status-strip strip-speaking">
<div class="mini-wave"><span style="background:#16a34a;"></span><span style="background:#16a34a;"></span><span style="background:#16a34a;"></span></div>
<span>晓晓正在朗读...</span>
<button class="interrupt-btn">✕ 打断</button>
</div>
<div class="call-bottom" style="padding-top:6px;">
<div class="call-status">
<div class="call-dot" style="background:#34c759;"></div>
<span class="call-label">通话中</span>
<span class="call-label">01:48</span>
</div>
<div class="vol-indicator">
<div class="vol-bar" style="height:8px;"></div>
<div class="vol-bar" style="height:12px;"></div>
<div class="vol-bar" style="height:16px;"></div>
<div class="vol-bar" style="height:20px;"></div>
<div class="vol-bar" style="height:24px;"></div>
</div>
<button class="hangup-btn">
<svg width="26" height="26" viewBox="0 0 24 24" fill="currentColor"><path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08a.956.956 0 0 1 0-1.36C3.36 8.85 7.47 7 12 7s8.64 1.85 11.71 4.72c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.1-.7-.28-.79-.73-1.68-1.36-2.66-1.85-.33-.16-.56-.5-.56-.9v-3.1C14.15 9.25 12.6 9 12 9z"/></svg>
</button>
</div>
</div>
</div>
</div>
</body>
</html>
Loading…
Cancel
Save