Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,13 @@ Built with [Tauri v2](https://tauri.app/) and [whisper.cpp](https://github.com/g
- **Backend**: Rust with [whisper-rs](https://github.com/tazz4843/whisper-rs) bindings
- **Audio conversion**: FFmpeg (must be installed separately)

## Installation

## Usage
You can download the latest installers and packages for your operating system from the [GitHub Releases](https://github.com/kylethedeveloper/OratioText/releases) page.

Choose the appropriate `.dmg` or `.msi` file to install OratioText easily!

## Basic Usage

1. Select an audio/video file using the file picker
2. Choose a Whisper model (download if needed)
Expand All @@ -36,7 +41,7 @@ Built with [Tauri v2](https://tauri.app/) and [whisper.cpp](https://github.com/g
- FFmpeg installed and in PATH
- Internet connection for initial model download

## macOS Installation Note
### macOS Installation Note

Since the app is not signed with an Apple Developer certificate (yet), macOS Gatekeeper may show a warning saying **"OratioText is damaged and can't be opened"**. This is expected for unsigned open-source apps.

Expand All @@ -46,7 +51,7 @@ To fix this, run the following command in Terminal after installing the app:
xattr -cr /Applications/OratioText.app
```

Then open the app normally. Alternatively, you can right-click the app and select **Open** to bypass the warning on first launch.
Then open the app normally.

## Development

Expand Down
8 changes: 6 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
- [x] Add an icon
- [x] Build for Windows and macOS (see [BUILD.md](./BUILD.md))
- [x] Add language selection option (or detect automatically)
- [ ] Add an about menu (feedback, donate, etc)
- [x] Add an about menu (feedback, donate, etc)
- [x] Add a settings menu
- [ ] Add option to transcribe from URL
- [ ] Build for Linux
- [ ] Add history of transcriptions
- [ ] Add history of transcriptions
- [ ] Send notifications when transcription is complete
- [ ] Add option to manage downloaded models in Settings (delete, etc.)
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "oratiotext"
version = "1.0.2"
version = "1.0.3"
description = "A cross-platform desktop application for converting speech to text using Whisper"
authors = ["kylethedeveloper"]
edition = "2021"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json",
"productName": "OratioText",
"version": "1.0.2",
"version": "1.0.3",
"identifier": "com.oratiotext.app",
"build": {
"frontendDist": "../src"
Expand Down
43 changes: 41 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
<div id="nav-dropdown" class="nav-dropdown hidden">
<button class="nav-item active" data-page="home">Home</button>
<button class="nav-item" data-page="history">History</button>
<button class="nav-item" data-page="settings">Settings</button>
<button class="nav-item" data-page="about">About</button>
</div>

<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
<svg id="icon-moon" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark/system theme">
<svg id="icon-moon" class="hidden" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454l0 .008" />
Expand All @@ -39,6 +40,10 @@
<path d="M8 12a4 4 0 1 0 8 0a4 4 0 1 0 -8 0" />
<path d="M3 12h1m8 -9v1m8 8h1m-9 8v1m-6.4 -15.4l.7 .7m12.1 -.7l-.7 .7m0 11.4l.7 .7m-12.1 -.7l-.7 .7" />
</svg>
<svg id="icon-system" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-brightness">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M17 3.34a10 10 0 1 1 -15 8.66l.005 -.324a10 10 0 0 1 14.995 -8.336m-9 1.732a8 8 0 0 0 4.001 14.928l-.001 -16a8 8 0 0 0 -4 1.072" />
</svg>
</button>
<div class="logo-area">
<img src="appicon.png" alt="OratioText" class="app-icon" />
Expand Down Expand Up @@ -154,6 +159,40 @@ <h1>OratioText</h1>
</section>
</main>

<main class="app-main hidden" id="page-settings">
<section class="card">
<label class="section-label">Settings</label>

<div class="settings-list">
<div class="setting-item">
<div class="setting-info">
<span class="setting-name">Theme</span>
<span class="setting-description">Application appearance</span>
</div>
<div class="setting-control">
<select id="app-theme-select">
<option value="system">System Default</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
</div>

<div class="setting-item">
<div class="setting-info">
<span class="setting-name">Language</span>
<span class="setting-description">Application interface language</span>
</div>
<div class="setting-control">
<select id="app-language-select">
<option value="en" selected>English</option>
</select>
</div>
</div>
</div>
</section>
</main>

<main class="app-main hidden" id="page-about">
<section class="card about-section">
<div
Expand Down
103 changes: 84 additions & 19 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const navItems = document.querySelectorAll(".nav-item");
const pages = {
home: document.getElementById("page-home"),
history: document.getElementById("page-history"),
settings: document.getElementById("page-settings"),
about: document.getElementById("page-about"),
};

Expand Down Expand Up @@ -422,45 +423,109 @@ function setLoading(loading, message = "Processing...") {
}
}

// ---- Theme Toggle ---------------------------------------------------------
// ---- Theme & App Settings ---------------------------------------------------------

const themeToggle = document.getElementById("theme-toggle");
const iconMoon = document.getElementById("icon-moon");
const iconSun = document.getElementById("icon-sun");
const iconSystem = document.getElementById("icon-system");
const appThemeSelect = document.getElementById("app-theme-select");
const appLanguageSelect = document.getElementById("app-language-select");

function setThemeIcons(isLight) {
if (isLight) {
iconMoon.classList.add("hidden");
function setThemeIcons(theme) {
iconMoon.classList.add("hidden");
iconSun.classList.add("hidden");
iconSystem.classList.add("hidden");

if (theme === "light") {
iconSun.classList.remove("hidden");
} else {
} else if (theme === "dark") {
iconMoon.classList.remove("hidden");
iconSun.classList.add("hidden");
} else {
iconSystem.classList.remove("hidden");
}
}

function initTheme() {
const saved = localStorage.getItem("oratiotext-theme");
if (saved === "light") {
function applyTheme(theme) {
let isLight = false;

if (theme === "system") {
isLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
} else {
isLight = (theme === "light");
}

if (isLight) {
document.documentElement.setAttribute("data-theme", "light");
setThemeIcons(true);
} else {
document.documentElement.removeAttribute("data-theme");
}

setThemeIcons(theme);
}

function initTheme() {
const savedTheme = localStorage.getItem("oratiotext-theme") || "system";

if (appThemeSelect) {
appThemeSelect.value = savedTheme;
}

applyTheme(savedTheme);

// Listen for system theme changes if set to system
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => {
if ((localStorage.getItem("oratiotext-theme") || "system") === "system") {
applyTheme("system");
}
});
}

function toggleTheme() {
const isLight = document.documentElement.getAttribute("data-theme") === "light";
if (isLight) {
document.documentElement.removeAttribute("data-theme");
localStorage.setItem("oratiotext-theme", "dark");
setThemeIcons(false);
const currentSaved = localStorage.getItem("oratiotext-theme") || "system";
let nextTheme = "dark";

if (currentSaved === "dark") {
nextTheme = "light";
} else if (currentSaved === "light") {
nextTheme = "system";
} else {
document.documentElement.setAttribute("data-theme", "light");
localStorage.setItem("oratiotext-theme", "light");
setThemeIcons(true);
nextTheme = "dark"; // Default to dark from system
}

localStorage.setItem("oratiotext-theme", nextTheme);
if (appThemeSelect) {
appThemeSelect.value = nextTheme;
}
applyTheme(nextTheme);
}

themeToggle.addEventListener("click", toggleTheme);
initTheme();

function initSettings() {
initTheme();

if (appThemeSelect) {
appThemeSelect.addEventListener("change", (e) => {
const theme = e.target.value;
localStorage.setItem("oratiotext-theme", theme);
applyTheme(theme);
});
}

const savedLang = localStorage.getItem("oratiotext-app-language");
if (savedLang && appLanguageSelect) {
appLanguageSelect.value = savedLang;
}

if (appLanguageSelect) {
appLanguageSelect.addEventListener("change", (e) => {
localStorage.setItem("oratiotext-app-language", e.target.value);
});
}
}

initSettings();

// ---- Update Check ---------------------------------------------------------

Expand Down
62 changes: 62 additions & 0 deletions src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,68 @@ body {
margin-bottom: var(--spacing-sm);
}

/* ==========================================================================
Settings Page
========================================================================== */

.settings-list {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
margin-top: var(--spacing-md);
}

.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-border);
}

.setting-item:last-child {
padding-bottom: 0;
border-bottom: none;
}

.setting-info {
display: flex;
flex-direction: column;
gap: 2px;
}

.setting-name {
font-size: 0.95rem;
font-weight: 600;
color: var(--color-text);
}

.setting-description {
font-size: 0.8rem;
color: var(--color-text-muted);
}

.setting-control select {
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
padding: var(--spacing-xs) var(--spacing-md);
color: var(--color-text);
font-family: var(--font-family);
font-size: 0.875rem;
outline: none;
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238e8ea0' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 36px;
width: 180px;
}

.setting-control select:focus {
border-color: var(--color-primary);
}
/* ==========================================================================
File Picker
========================================================================== */
Expand Down
Loading