Skip to content
Draft
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
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Bug report
about: Report a reproducible problem in the OpenClaw webapp
title: "[Bug] "
labels: bug
assignees: ''
---

## Description
A clear description of the bug.

## Steps to reproduce
1.
2.
3.

## Expected behavior
What you expected to happen.

## Actual behavior
What actually happened.

## Environment
- OS:
- Browser:
- Commit/branch:

## Additional context
Logs, screenshots, or recordings.
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions and support
url: https://github.com/<your-org>/<your-repo>/discussions
about: Please use Discussions for questions.
19 changes: 19 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest an idea to improve the OpenClaw webapp
title: "[Feature] "
labels: enhancement
assignees: ''
---

## Problem statement
What problem are you trying to solve?

## Proposed solution
Describe the solution you want.

## Alternatives considered
Other approaches you evaluated.

## Additional context
Mockups, references, or constraints.
17 changes: 17 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Summary
-

## Changes
-

## Testing
- [ ] I ran `node --check src/app.js` (or equivalent checks)
- [ ] I verified the app loads locally (`python3 -m http.server 4173`)

## Screenshots (if UI changed)
-

## Checklist
- [ ] My change is focused and minimal.
- [ ] I updated docs if behavior changed.
- [ ] No secrets/API keys are committed.
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
push:
branches: ["**"]
pull_request:

jobs:
syntax-check:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: JavaScript syntax check
run: |
node --check src/openclaw.js
node --check src/app.js
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# OS/editor
.DS_Store
.vscode/
.idea/

# Logs
*.log

# Env files (if users later add backend/proxy config)
.env
.env.*

# Node artifacts
node_modules/
dist/
coverage/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# OpenClaw Voice Chat Webapp

A lightweight browser UI to chat with an OpenClaw assistant powered by **Gemini** for chat responses, with optional **ElevenLabs** text-to-speech and speech-to-text.

## GitHub-ready project structure

```text
.
├── .github/
│ └── workflows/
│ └── ci.yml
├── src/
│ ├── app.js
│ ├── openclaw.js
│ └── styles.css
├── .gitignore
├── index.html
├── LICENSE
└── README.md
```

## GitHub collaboration files

This repository now includes:

- Issue templates for bug reports and feature requests in `.github/ISSUE_TEMPLATE/`
- A pull request template at `.github/pull_request_template.md`

> Note: Update `.github/ISSUE_TEMPLATE/config.yml` discussion URL to your real repository path.

## Features

- Gemini chat completion (`generateContent`) with model selection
- ElevenLabs TTS playback for assistant responses
- ElevenLabs STT from mic recording
- Browser SpeechRecognition fallback when no ElevenLabs key is provided
- Browser speech synthesis fallback when no ElevenLabs key/voice is provided

## Run locally

```bash
python3 -m http.server 4173
```

Open `http://localhost:4173` in your browser.

## Usage

1. Enter your Gemini API key.
2. (Optional) Enter ElevenLabs API key and voice ID.
3. Type a message or click **Voice input**.
4. Toggle **Auto speak assistant replies** to hear responses automatically.

## How OpenClaw bot behavior is implemented in this webapp

The "OpenClaw bot" in this project is implemented as a **prompted Gemini chat loop** in the browser:

1. `state.messages` starts with a system instruction that defines OpenClaw as concise, friendly, and practical.
2. Every user message is appended to `state.messages` and rendered in the chat UI.
3. `callGemini()` transforms conversation messages into Gemini `contents` and sends them to `models/{model}:generateContent`.
4. Gemini's reply is added back as an `assistant` message and can be auto-spoken.
5. Voice input is recorded with `MediaRecorder`; transcription uses ElevenLabs STT when configured, otherwise browser SpeechRecognition fallback.
6. Voice output uses ElevenLabs TTS when configured, otherwise browser `speechSynthesis` fallback.

In short: there is no separate server-side OpenClaw runtime here; the browser orchestrates prompts, model calls, and voice I/O.

The OpenClaw behavior implementation now lives in `src/openclaw.js`, while `src/app.js` handles UI and voice interactions.

> ⚠️ API keys are entered client-side and used directly by browser requests. For production deployments, route requests through your backend.
83 changes: 83 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="OpenClaw voice-enabled web chat UI powered by Gemini and ElevenLabs." />
<title>OpenClaw Voice Chat</title>
<link rel="stylesheet" href="src/styles.css" />
</head>
<body>
<main class="app-shell">
<aside class="settings-panel">
<h1>OpenClaw Voice Chat</h1>
<p class="subtitle">Talk with your OpenClaw bot using Gemini + ElevenLabs.</p>

<label>
Chat provider
<select id="chatProvider">
<option value="gemini">Gemini</option>
</select>
</label>

<label>
Gemini API key
<input id="geminiApiKey" type="password" placeholder="AIza..." />
</label>

<label>
Gemini model
<input id="geminiModel" value="gemini-1.5-flash" />
</label>

<hr />

<label>
ElevenLabs API key (TTS/STT)
<input id="elevenApiKey" type="password" placeholder="sk_..." />
</label>

<label>
ElevenLabs voice ID (TTS)
<input id="elevenVoiceId" value="EXAVITQu4vr4xnSDxMaL" />
</label>

<label>
ElevenLabs STT model
<input id="elevenSttModel" value="scribe_v1" />
</label>

<label class="checkbox-row">
<input id="autoSpeak" type="checkbox" />
Auto speak assistant replies
</label>

<button id="clearChat" class="secondary">Clear conversation</button>
</aside>

<section class="chat-panel">
<div id="messages" class="messages" aria-live="polite"></div>

<form id="composer" class="composer">
<textarea id="messageInput" rows="2" placeholder="Ask OpenClaw anything..."></textarea>
<div class="actions">
<button type="button" id="micButton" class="secondary">🎤 Voice input</button>
<button type="submit" id="sendButton">Send</button>
</div>
</form>
</section>
</main>

<template id="messageTemplate">
<article class="message">
<header>
<strong class="role"></strong>
<button type="button" class="speak secondary">🔊 Speak</button>
</header>
<p class="content"></p>
</article>
</template>

<script src="src/app.js" type="module"></script>
</body>
</html>
Loading