diff --git a/web/index.html b/web/index.html
index 2ff78b5..fd51816 100644
--- a/web/index.html
+++ b/web/index.html
@@ -5,6 +5,7 @@
+
VoicePaste
@@ -24,13 +25,21 @@
-
+
+
+
+
+
+
+
+
+ 按住说话
diff --git a/web/style.css b/web/style.css
index 9fb99b7..2cd0aa9 100644
--- a/web/style.css
+++ b/web/style.css
@@ -7,17 +7,25 @@
}
:root {
- --bg: #0a0a0a;
- --surface: #161616;
- --surface-hover: #1e1e1e;
- --border: #2a2a2a;
- --text: #e8e8e8;
- --text-dim: #888;
- --accent: #3b82f6;
- --accent-glow: rgba(59, 130, 246, 0.3);
- --danger: #ef4444;
- --success: #22c55e;
- --radius: 12px;
+ /* Warm dark palette — subtle indigo undertone */
+ --bg: #08080d;
+ --surface: #111117;
+ --surface-hover: #17171e;
+ --surface-active: #1c1c25;
+ --border: #1e1e2a;
+ --border-active: #2c2c3e;
+ --text: #eaeaef;
+ --text-secondary: #9e9eb5;
+ --text-dim: #5a5a6e;
+ --accent: #6366f1;
+ --accent-hover: #818cf8;
+ --accent-glow: rgba(99, 102, 241, 0.2);
+ --accent-glow-md: rgba(99, 102, 241, 0.35);
+ --accent-glow-lg: rgba(99, 102, 241, 0.08);
+ --danger: #f43f5e;
+ --success: #34d399;
+ --radius: 14px;
+ --radius-sm: 8px;
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
}
@@ -26,8 +34,8 @@ html,
body {
height: 100%;
font-family:
- -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue",
- sans-serif;
+ "SF Pro Display", -apple-system, BlinkMacSystemFont, "PingFang SC",
+ "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
background: var(--bg);
color: var(--text);
-webkit-font-smoothing: antialiased;
@@ -37,55 +45,90 @@ body {
overflow: hidden;
}
+/* Subtle ambient glow at top */
+body::after {
+ content: "";
+ position: fixed;
+ top: -30%;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 600px;
+ height: 600px;
+ background: radial-gradient(
+ circle,
+ rgba(99, 102, 241, 0.04) 0%,
+ transparent 70%
+ );
+ pointer-events: none;
+ z-index: 0;
+}
+
#app {
+ position: relative;
+ z-index: 1;
display: flex;
flex-direction: column;
height: 100%;
max-width: 480px;
margin: 0 auto;
- padding: calc(16px + var(--safe-top)) 16px calc(16px + var(--safe-bottom));
+ padding: calc(16px + var(--safe-top)) 20px calc(16px + var(--safe-bottom));
}
-/* Header */
+/* ─── Header ─── */
header {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 8px 0 16px;
+ padding: 8px 0 20px;
flex-shrink: 0;
}
header h1 {
- font-size: 20px;
- font-weight: 600;
- letter-spacing: -0.02em;
+ font-size: 22px;
+ font-weight: 700;
+ letter-spacing: -0.03em;
}
.status {
display: flex;
align-items: center;
- gap: 6px;
- font-size: 13px;
+ gap: 7px;
+ font-size: 12px;
+ font-weight: 500;
color: var(--text-dim);
+ padding: 5px 12px;
+ border-radius: 20px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ transition: all 0.25s ease;
}
.status .dot {
- width: 8px;
- height: 8px;
+ width: 7px;
+ height: 7px;
border-radius: 50%;
background: var(--text-dim);
- transition: background 0.3s;
+ transition: all 0.3s ease;
+ flex-shrink: 0;
+}
+
+.status.connected {
+ border-color: rgba(52, 211, 153, 0.15);
}
.status.connected .dot {
background: var(--success);
+ box-shadow: 0 0 6px rgba(52, 211, 153, 0.5);
}
+
.status.disconnected .dot {
background: var(--danger);
+ box-shadow: 0 0 6px rgba(244, 63, 94, 0.4);
}
+
.status.connecting .dot {
background: var(--accent);
- animation: pulse 1.2s ease-in-out infinite;
+ animation: pulse 1.4s ease-in-out infinite;
}
@keyframes pulse {
@@ -94,97 +137,175 @@ header h1 {
opacity: 1;
}
50% {
- opacity: 0.4;
+ opacity: 0.3;
}
}
-/* Preview */
+/* ─── Preview ─── */
#preview-section {
flex-shrink: 0;
- padding-bottom: 16px;
+ padding-bottom: 12px;
}
.preview-box {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
- padding: 16px;
+ padding: 16px 18px;
min-height: 80px;
max-height: 160px;
overflow-y: auto;
- transition: border-color 0.3s;
+ transition:
+ border-color 0.3s ease,
+ box-shadow 0.3s ease,
+ background 0.3s ease;
}
.preview-box.active {
- border-color: var(--accent);
- box-shadow: 0 0 0 1px var(--accent-glow);
+ border-color: rgba(99, 102, 241, 0.4);
+ box-shadow:
+ 0 0 0 1px var(--accent-glow),
+ 0 4px 24px -4px rgba(99, 102, 241, 0.15);
+ background: linear-gradient(
+ 180deg,
+ rgba(99, 102, 241, 0.03) 0%,
+ var(--surface) 100%
+ );
}
#preview-text {
font-size: 16px;
- line-height: 1.5;
+ line-height: 1.6;
word-break: break-word;
}
#preview-text.placeholder {
color: var(--text-dim);
- font-style: italic;
}
-/* Mic Button */
+/* ─── Mic Button ─── */
#mic-section {
display: flex;
- justify-content: center;
- padding: 24px 0;
+ flex-direction: column;
+ align-items: center;
+ padding: 20px 0 16px;
flex-shrink: 0;
+ gap: 14px;
+}
+
+.mic-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
#mic-btn {
- width: 88px;
- height: 88px;
+ position: relative;
+ z-index: 1;
+ width: 96px;
+ height: 96px;
border-radius: 50%;
border: 2px solid var(--border);
- background: var(--surface);
- color: var(--text-dim);
+ background: linear-gradient(145deg, var(--surface-hover), var(--surface));
+ color: var(--text-secondary);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
- transition: all 0.2s;
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-user-select: none;
touch-action: none;
+ box-shadow:
+ 0 2px 12px rgba(0, 0, 0, 0.3),
+ inset 0 1px 0 rgba(255, 255, 255, 0.04);
+}
+
+#mic-btn svg {
+ transition: transform 0.2s ease;
}
#mic-btn:disabled {
- opacity: 0.4;
+ opacity: 0.3;
cursor: not-allowed;
}
#mic-btn:not(:disabled):active,
#mic-btn.recording {
background: var(--accent);
- border-color: var(--accent);
+ border-color: var(--accent-hover);
color: #fff;
- transform: scale(1.08);
- box-shadow: 0 0 24px var(--accent-glow);
+ transform: scale(1.06);
+ box-shadow:
+ 0 0 32px var(--accent-glow-md),
+ 0 0 80px var(--accent-glow);
}
#mic-btn.recording {
- animation: mic-pulse 1s ease-in-out infinite;
+ animation: mic-breathe 1.8s ease-in-out infinite;
}
-@keyframes mic-pulse {
+@keyframes mic-breathe {
0%,
100% {
- box-shadow: 0 0 24px var(--accent-glow);
+ box-shadow:
+ 0 0 32px var(--accent-glow-md),
+ 0 0 80px var(--accent-glow);
}
50% {
box-shadow:
- 0 0 48px var(--accent-glow),
- 0 0 80px rgba(59, 130, 246, 0.15);
+ 0 0 48px var(--accent-glow-md),
+ 0 0 120px var(--accent-glow),
+ 0 0 200px var(--accent-glow-lg);
}
}
-/* History */
+
+/* Wave rings — radiate outward when recording */
+.mic-rings {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+
+.mic-rings .ring {
+ position: absolute;
+ inset: 0;
+ border-radius: 50%;
+ border: 1.5px solid var(--accent);
+ opacity: 0;
+}
+
+#mic-btn.recording + .mic-rings .ring {
+ animation: ring-expand 2.4s cubic-bezier(0.2, 0, 0.2, 1) infinite;
+}
+
+#mic-btn.recording + .mic-rings .ring:nth-child(2) {
+ animation-delay: 0.8s;
+}
+
+#mic-btn.recording + .mic-rings .ring:nth-child(3) {
+ animation-delay: 1.6s;
+}
+
+@keyframes ring-expand {
+ 0% {
+ transform: scale(1);
+ opacity: 0.35;
+ }
+ 100% {
+ transform: scale(2);
+ opacity: 0;
+ }
+}
+
+.mic-hint {
+ font-size: 13px;
+ color: var(--text-dim);
+ letter-spacing: 0.01em;
+ transition: color 0.3s ease;
+}
+
+/* ─── History ─── */
#history-section {
flex: 1;
min-height: 0;
@@ -192,102 +313,145 @@ header h1 {
flex-direction: column;
overflow: hidden;
}
+
.history-header {
display: flex;
align-items: center;
justify-content: space-between;
- padding-bottom: 8px;
+ padding-bottom: 10px;
flex-shrink: 0;
}
+
.history-header h2 {
- font-size: 15px;
+ font-size: 13px;
font-weight: 600;
color: var(--text-dim);
text-transform: uppercase;
- letter-spacing: 0.04em;
+ letter-spacing: 0.06em;
}
+
.text-btn {
background: none;
border: none;
- color: var(--accent);
- font-size: 13px;
+ color: var(--text-dim);
+ font-size: 12px;
+ font-weight: 500;
cursor: pointer;
- padding: 4px 8px;
- border-radius: 6px;
- transition: background 0.2s;
+ padding: 4px 10px;
+ border-radius: var(--radius-sm);
+ transition: all 0.15s ease;
}
+
.text-btn:active {
- background: rgba(59, 130, 246, 0.1);
+ color: var(--danger);
+ background: rgba(244, 63, 94, 0.08);
}
+
#history-list {
list-style: none;
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
+
#history-list li {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
- padding: 12px 14px;
+ padding: 14px 16px;
margin-bottom: 8px;
font-size: 14px;
- line-height: 1.4;
+ line-height: 1.5;
cursor: pointer;
- transition: background 0.15s;
+ transition: all 0.15s ease;
display: flex;
align-items: flex-start;
- gap: 10px;
+ gap: 12px;
+ animation: slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
+ animation-delay: calc(var(--i, 0) * 40ms);
}
+
#history-list li:active {
- background: var(--surface-hover);
+ background: var(--surface-active);
+ border-color: var(--border-active);
+ transform: scale(0.985);
}
+
#history-list li .hist-text {
flex: 1;
word-break: break-word;
}
+
#history-list li .hist-time {
font-size: 11px;
color: var(--text-dim);
white-space: nowrap;
flex-shrink: 0;
padding-top: 2px;
+ font-variant-numeric: tabular-nums;
}
+
+@keyframes slide-up {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
#history-empty {
text-align: center;
- padding: 32px 0;
+ padding: 40px 0;
}
+
.placeholder {
color: var(--text-dim);
font-size: 14px;
}
-/* Scrollbar */
+
+/* ─── Scrollbar ─── */
+.preview-box::-webkit-scrollbar,
#history-list::-webkit-scrollbar {
- width: 4px;
+ width: 3px;
}
+
+.preview-box::-webkit-scrollbar-track,
#history-list::-webkit-scrollbar-track {
background: transparent;
}
+
+.preview-box::-webkit-scrollbar-thumb,
#history-list::-webkit-scrollbar-thumb {
background: var(--border);
- border-radius: 2px;
+ border-radius: 3px;
}
-/* Toast */
+
+/* ─── Toast ─── */
.toast {
position: fixed;
- bottom: calc(100px + var(--safe-bottom, 0px));
+ bottom: calc(80px + var(--safe-bottom, 0px));
left: 50%;
- transform: translateX(-50%);
- background: #222;
- color: #eee;
- padding: 8px 18px;
- border-radius: 20px;
- font-size: 14px;
+ transform: translateX(-50%) translateY(8px);
+ background: rgba(28, 28, 37, 0.85);
+ color: var(--text);
+ padding: 10px 22px;
+ border-radius: 24px;
+ font-size: 13px;
+ font-weight: 500;
z-index: 999;
opacity: 0;
- transition: opacity 0.3s;
+ transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
pointer-events: none;
+ border: 1px solid var(--border);
+ backdrop-filter: blur(16px);
+ -webkit-backdrop-filter: blur(16px);
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
+
.toast.show {
opacity: 1;
+ transform: translateX(-50%) translateY(0);
}