ReviewOS

stacks/voide

public
Clone

Push over the same URL. A password will not work: create a token under access tokens and use it in place of one.

main · 141 commits

Mirrored from stacksjs/voide · synced 7 minutes ago

README.md

Voide

Voice-controlled AI code assistant built with STX.

Features

  • Voice input with Web Speech API
  • Multiple AI drivers (Claude CLI, Claude API, OpenAI, Ollama)
  • Git integration (commit, push)
  • GitHub account connection
  • Native desktop support via Craft

Getting Started

# Install dependencies
bun install

# Build stores and CSS
bun run build:all

# Start development server
bun run dev

Single File Components (SFC)

Voide uses STX Single File Components.

Component Structure

Components live in components/ and are auto-imported using PascalCase:

<!-- pages/index.stx -->
@layout('default')

@section('content')
<VoideHeader />

<main class="flex-1 flex flex-col">
  <VoideTerminal />
  <VoideInputBar />
</main>

<VoideFooter />
<VoideModals />
@endsection

SFC Anatomy

Components use three sections: <script server>, <template>, and <script client>:

<!-- components/MyComponent.stx -->
<script server>
// Server-side only - extracted for SSR, stripped from output
const title = props.title || 'Default Title'
const count = props.count || 0
</script>

<template>
  <div class="card">
    <h1>{{ title }}</h1>
    <p>Count: {{ count }}</p>
    <slot />
  </div>
</template>

<script client>
// Client-side only - preserved for browser execution
(() => {
  console.log('Component mounted');
})();
</script>

Passing Props

Pass data to components via attributes:

<!-- String prop -->
<VoideHeader title="My App" />

<!-- Expression prop with :binding -->
<Card :count="items.length" />

<!-- Mustache interpolation -->
<Message text="" />

Slots

Use <slot /> to inject content into components:

<!-- Parent -->
<Card title="Welcome">
  <p>This content goes into the slot!</p>
</Card>

<!-- Card component -->
<template>
  <div class="card">
    <h2>{{ props.title }}</h2>
    <slot />
  </div>
</template>

Client Scripts with Stores

Components can subscribe to reactive stores:

<script client>
(() => {
  function init() {
    const stores = window.VoideStores;
    if (!stores) {
      setTimeout(init, 10);
      return;
    }

    // Subscribe to store changes
    stores.appStore.subscribe((state) => {
      const el = document.getElementById('myElement');
      if (el) {
        el.textContent = state.transcript;
      }
    });
  }

  init();
})();
</script>

Two-Way Binding (x-element)

For simple reactive forms, use x-element directives:

<div x-data="{ message: '', count: 0 }">
  <input x-model="message" placeholder="Type here..." />
  <p>You typed: <span x-text="message"></span></p>

  <button @click="count++">Increment</button>
  <span x-text="count"></span>
</div>

Architecture

Stores

Voide uses a reactive store pattern for state management:

  • appStore - Application state (recording, processing, repo path)
  • chatStore - Chat messages and history
  • settingsStore - API keys, GitHub connection, preferences
  • uiStore - Modal visibility, UI state

Composables

Browser API utilities available via VoideStores:

CategoryComposables
StorageuseStorage, useLocalStorage, useSessionStorage
CookiesuseCookie, getCookie, setCookie
ClipboarduseClipboard, copyToClipboard
MediauseMediaQuery, usePreferredDark, useIsMobile
NetworkuseNetwork, useOnline
WindowuseWindowSize, useScroll, useVisibility, useTitle
GeolocationuseGeolocation, getCurrentPosition
InputuseMouse, usePointer, useKeyboard, useHotkey
ObserversuseIntersectionObserver, useResizeObserver, useMutationObserver
DatauseFetch, useAsyncData
UIuseFullscreen, useNotification, useShare
PermissionsusePermission, usePermissions
DeviceuseBattery, useDeviceOrientation, useDeviceMotion
SpeechuseSpeechRecognition, useSpeechSynthesis
CommunicationuseWebSocket, useBroadcastChannel, useEventSource
IdleuseIdle, useIdleState, useAutoLogout
SelectionuseTextSelection, useCopySelection
ScreenuseWakeLock, useEyeDropper

Components

All components are in components/ and use the SFC pattern:

ComponentDescription
VoideHeaderApp header with repo input, driver selector, settings
VoideTerminalChat output with message rendering
VoideInputBarVoice button, text input, action buttons
VoideFooterApplication footer
VoideModalsGitHub and settings modals

Styling

Uses Headwind with a Monokai Pro color theme:

monokai-bg, monokai-bg-dark, monokai-fg
monokai-pink, monokai-orange, monokai-yellow
monokai-green, monokai-cyan, monokai-purple
monokai-gray, monokai-border

Scripts

bun run dev          # Start dev server
bun run build:all    # Build stores + CSS
bun run build:stores # Build stores only
bun run build:css    # Build CSS only

License

MIT