f2ed66ed17
A bar widget plus a stdlib-only Python daemon. Press a language's key to record, press it again to stop: the text is pasted at the cursor (or handed to the default coding agent with a second key). While recording the bar shows a waveform (yellow while the microphone opens, green while listening) and the words as they are recognised; the recording is transcribed at every pause, so stopping only transcribes the last phrase. Every recording and its text are kept in a searchable history with playback, copy, paste and delete. Uses Omarchy's own dictation engine (voxtype, local Whisper) by default and downloads any model a language needs by itself; whisper.cpp or a custom command can be picked instead. Key bindings are applied at runtime through Hyprland's Lua API and never touch a key something else already uses.
62 lines
1.8 KiB
QML
62 lines
1.8 KiB
QML
import QtQuick
|
|
|
|
// A row of thin bars mirrored around the middle. `levels` holds 0..1 values,
|
|
// newest last; the bars show the most recent `bars` of them. With `idle` on
|
|
// (nothing to show yet, or transcribing) the bars breathe gently instead.
|
|
Item {
|
|
id: root
|
|
|
|
property var levels: []
|
|
property int bars: 20
|
|
property real barWidth: 2
|
|
property real gap: 2
|
|
property color color: "white"
|
|
property bool idle: false // gentle breathing (nothing to show yet, or transcribing)
|
|
property bool sine: false // a travelling sine wave (the microphone is still connecting)
|
|
property real minHeight: 2
|
|
|
|
implicitWidth: bars * (barWidth + gap) - gap
|
|
implicitHeight: 16
|
|
|
|
property real phase: 0
|
|
Timer {
|
|
interval: 40
|
|
repeat: true
|
|
running: (root.idle || root.sine) && root.visible
|
|
onTriggered: root.phase += root.sine ? 0.35 : 0.2
|
|
}
|
|
|
|
function levelAt(i) {
|
|
var lv = levels || []
|
|
var offset = lv.length - bars
|
|
var v = offset + i >= 0 ? Number(lv[offset + i]) : 0
|
|
return isFinite(v) ? Math.max(0, Math.min(1, v)) : 0
|
|
}
|
|
|
|
function breath(i) {
|
|
return 0.12 + 0.1 * (1 + Math.sin(phase + i * 0.45)) / 2
|
|
}
|
|
|
|
function wave(i) {
|
|
return 0.15 + 0.75 * (1 + Math.sin(i * 0.7 - phase)) / 2
|
|
}
|
|
|
|
Row {
|
|
anchors.centerIn: parent
|
|
spacing: root.gap
|
|
Repeater {
|
|
model: root.bars
|
|
Rectangle {
|
|
required property int index
|
|
readonly property real lv: root.sine ? root.wave(index) : root.idle ? root.breath(index) : root.levelAt(index)
|
|
width: root.barWidth
|
|
height: Math.max(root.minHeight, Math.round(lv * root.height))
|
|
radius: root.barWidth / 2
|
|
color: root.color
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
Behavior on height { NumberAnimation { duration: 70 } }
|
|
}
|
|
}
|
|
}
|
|
}
|