415 lines
9.6 KiB
HTML
415 lines
9.6 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
width: 360px;
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: #0a0a0a;
|
|
color: #e5e5e5;
|
|
font-size: 13px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 8px 14px;
|
|
background: #171717;
|
|
border-bottom: 1px solid #262626;
|
|
-webkit-app-region: drag;
|
|
}
|
|
.header .brand {
|
|
font-weight: 700;
|
|
font-size: 14px;
|
|
color: #ef4444;
|
|
}
|
|
.header .brand-sub {
|
|
color: #a3a3a3;
|
|
font-weight: 400;
|
|
font-size: 12px;
|
|
}
|
|
.header .close-btn {
|
|
-webkit-app-region: no-drag;
|
|
background: none;
|
|
border: none;
|
|
color: #737373;
|
|
cursor: pointer;
|
|
font-size: 18px;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
}
|
|
.header .close-btn:hover {
|
|
color: #e5e5e5;
|
|
background: #262626;
|
|
}
|
|
|
|
.auth-warning {
|
|
padding: 10px 14px;
|
|
background: #451a03;
|
|
border-bottom: 1px solid #78350f;
|
|
text-align: center;
|
|
font-size: 12px;
|
|
color: #fbbf24;
|
|
}
|
|
|
|
.recorder {
|
|
padding: 20px 14px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* Record button */
|
|
.rec-btn {
|
|
width: 72px;
|
|
height: 72px;
|
|
border-radius: 50%;
|
|
border: 3px solid #404040;
|
|
background: #171717;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: all 0.2s;
|
|
position: relative;
|
|
}
|
|
.rec-btn:hover {
|
|
border-color: #ef4444;
|
|
}
|
|
.rec-btn .inner {
|
|
width: 32px;
|
|
height: 32px;
|
|
background: #ef4444;
|
|
border-radius: 50%;
|
|
transition: all 0.2s;
|
|
}
|
|
.rec-btn.recording {
|
|
border-color: #ef4444;
|
|
}
|
|
.rec-btn.recording .inner {
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 4px;
|
|
background: #ef4444;
|
|
}
|
|
.rec-btn.recording::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: -6px;
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(239, 68, 68, 0.3);
|
|
animation: pulse-ring 1.5s infinite;
|
|
}
|
|
|
|
@keyframes pulse-ring {
|
|
0% { transform: scale(1); opacity: 1; }
|
|
100% { transform: scale(1.15); opacity: 0; }
|
|
}
|
|
|
|
.timer {
|
|
font-size: 28px;
|
|
font-family: 'SF Mono', 'Consolas', 'Courier New', monospace;
|
|
font-weight: 600;
|
|
color: #e5e5e5;
|
|
letter-spacing: 2px;
|
|
}
|
|
.timer.recording {
|
|
color: #ef4444;
|
|
}
|
|
|
|
.status-label {
|
|
font-size: 11px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
font-weight: 600;
|
|
}
|
|
.status-label.idle { color: #737373; }
|
|
.status-label.recording { color: #ef4444; }
|
|
.status-label.processing { color: #f59e0b; }
|
|
.status-label.done { color: #4ade80; }
|
|
|
|
/* Transcript area */
|
|
.transcript-area {
|
|
width: 100%;
|
|
padding: 0 14px 12px;
|
|
display: none;
|
|
}
|
|
.transcript-area.visible {
|
|
display: block;
|
|
}
|
|
.transcript-label {
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
color: #737373;
|
|
margin-bottom: 6px;
|
|
font-weight: 600;
|
|
}
|
|
.transcript-text {
|
|
background: #171717;
|
|
border: 1px solid #262626;
|
|
border-radius: 6px;
|
|
padding: 10px 12px;
|
|
font-size: 13px;
|
|
line-height: 1.5;
|
|
color: #d4d4d4;
|
|
max-height: 120px;
|
|
overflow-y: auto;
|
|
min-height: 40px;
|
|
white-space: pre-wrap;
|
|
}
|
|
.transcript-text.editable {
|
|
outline: none;
|
|
border-color: #404040;
|
|
cursor: text;
|
|
}
|
|
.transcript-text.editable:focus {
|
|
border-color: #f59e0b;
|
|
}
|
|
.transcript-text .placeholder {
|
|
color: #525252;
|
|
font-style: italic;
|
|
}
|
|
.transcript-text .final-text {
|
|
color: #d4d4d4;
|
|
}
|
|
.transcript-text .interim-text {
|
|
color: #737373;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Controls row */
|
|
.controls {
|
|
width: 100%;
|
|
padding: 0 14px 10px;
|
|
}
|
|
.controls select {
|
|
width: 100%;
|
|
padding: 6px 8px;
|
|
background: #171717;
|
|
border: 1px solid #404040;
|
|
border-radius: 4px;
|
|
color: #e5e5e5;
|
|
font-size: 12px;
|
|
outline: none;
|
|
}
|
|
.controls select:focus {
|
|
border-color: #f59e0b;
|
|
}
|
|
.controls label {
|
|
display: block;
|
|
font-size: 10px;
|
|
color: #737373;
|
|
margin-bottom: 3px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
/* Action buttons */
|
|
.actions {
|
|
width: 100%;
|
|
padding: 0 14px 12px;
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
.actions button {
|
|
flex: 1;
|
|
padding: 8px 12px;
|
|
border: none;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: opacity 0.15s;
|
|
}
|
|
.actions button:hover:not(:disabled) { opacity: 0.85; }
|
|
.actions button:disabled { opacity: 0.35; cursor: not-allowed; }
|
|
|
|
.btn-save {
|
|
background: #f59e0b;
|
|
color: #0a0a0a;
|
|
}
|
|
.btn-discard {
|
|
background: #262626;
|
|
color: #a3a3a3;
|
|
border: 1px solid #404040;
|
|
}
|
|
.btn-copy {
|
|
background: #172554;
|
|
color: #93c5fd;
|
|
border: 1px solid #1e40af;
|
|
}
|
|
|
|
/* Status bar */
|
|
.status-bar {
|
|
padding: 8px 14px;
|
|
border-top: 1px solid #262626;
|
|
font-size: 11px;
|
|
color: #525252;
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
.status-bar.visible {
|
|
display: block;
|
|
}
|
|
.status-bar.success { color: #4ade80; background: #052e16; border-top-color: #166534; }
|
|
.status-bar.error { color: #fca5a5; background: #450a0a; border-top-color: #991b1b; }
|
|
.status-bar.loading { color: #93c5fd; background: #172554; border-top-color: #1e40af; }
|
|
|
|
/* Live indicator */
|
|
.live-indicator {
|
|
display: none;
|
|
align-items: center;
|
|
gap: 5px;
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1.5px;
|
|
color: #4ade80;
|
|
}
|
|
.live-indicator.visible {
|
|
display: flex;
|
|
}
|
|
.live-indicator .dot {
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: #4ade80;
|
|
animation: pulse-dot 1s infinite;
|
|
}
|
|
@keyframes pulse-dot {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.3; }
|
|
}
|
|
|
|
/* Progress bar (for model download) */
|
|
.progress-area {
|
|
width: 100%;
|
|
padding: 0 14px 8px;
|
|
display: none;
|
|
}
|
|
.progress-area.visible {
|
|
display: block;
|
|
}
|
|
.progress-label {
|
|
font-size: 11px;
|
|
color: #a3a3a3;
|
|
margin-bottom: 4px;
|
|
}
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 6px;
|
|
background: #262626;
|
|
border-radius: 3px;
|
|
overflow: hidden;
|
|
}
|
|
.progress-bar .fill {
|
|
height: 100%;
|
|
background: #f59e0b;
|
|
border-radius: 3px;
|
|
transition: width 0.3s;
|
|
width: 0%;
|
|
}
|
|
|
|
/* Audio preview */
|
|
.audio-preview {
|
|
width: 100%;
|
|
padding: 0 14px 8px;
|
|
display: none;
|
|
}
|
|
.audio-preview.visible {
|
|
display: block;
|
|
}
|
|
.audio-preview audio {
|
|
width: 100%;
|
|
height: 32px;
|
|
}
|
|
|
|
/* Keyboard hint */
|
|
.kbd-hint {
|
|
padding: 4px 14px 8px;
|
|
text-align: center;
|
|
font-size: 10px;
|
|
color: #404040;
|
|
}
|
|
.kbd-hint kbd {
|
|
background: #1a1a1a;
|
|
border: 1px solid #333;
|
|
border-radius: 3px;
|
|
padding: 1px 5px;
|
|
font-family: inherit;
|
|
font-size: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header">
|
|
<span>
|
|
<span class="brand">rVoice</span>
|
|
<span class="brand-sub">voice notes</span>
|
|
</span>
|
|
<button class="close-btn" id="closeBtn" title="Close">×</button>
|
|
</div>
|
|
|
|
<div id="authWarning" class="auth-warning" style="display: none;">
|
|
Sign in via rNotes Clipper settings first.
|
|
</div>
|
|
|
|
<div class="recorder">
|
|
<div class="status-label idle" id="statusLabel">Ready</div>
|
|
<button class="rec-btn" id="recBtn" title="Start recording">
|
|
<div class="inner"></div>
|
|
</button>
|
|
<div class="timer" id="timer">00:00</div>
|
|
<div class="live-indicator" id="liveIndicator">
|
|
<span class="dot"></span>
|
|
Live transcribe
|
|
</div>
|
|
</div>
|
|
|
|
<div class="progress-area" id="progressArea">
|
|
<div class="progress-label" id="progressLabel">Loading model...</div>
|
|
<div class="progress-bar"><div class="fill" id="progressFill"></div></div>
|
|
</div>
|
|
|
|
<div class="audio-preview" id="audioPreview">
|
|
<audio controls id="audioPlayer"></audio>
|
|
</div>
|
|
|
|
<div class="transcript-area" id="transcriptArea">
|
|
<div class="transcript-label">Transcript</div>
|
|
<div class="transcript-text editable" id="transcriptText" contenteditable="true">
|
|
<span class="placeholder">Transcribing...</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="controls" id="notebookControls">
|
|
<label for="notebook">Save to notebook</label>
|
|
<select id="notebook">
|
|
<option value="">Default notebook</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="actions" id="postActions" style="display: none;">
|
|
<button class="btn-discard" id="discardBtn">Discard</button>
|
|
<button class="btn-copy" id="copyBtn" title="Copy transcript">Copy</button>
|
|
<button class="btn-save" id="saveBtn">Save to rNotes</button>
|
|
</div>
|
|
|
|
<div class="status-bar" id="statusBar"></div>
|
|
|
|
<div class="kbd-hint">
|
|
<kbd>Space</kbd> to record · <kbd>Esc</kbd> to close · Offline ready
|
|
</div>
|
|
|
|
<script src="parakeet-offline.js" type="module"></script>
|
|
<script src="voice.js"></script>
|
|
</body>
|
|
</html>
|