Files
gods-eye-view/src/ui/stores/voice.js
T
manjunath22466andDarren Dignam af461febae feat(voice): add typed AI chat beside voice
Chat sits next to the mic in the command dock. Replies are silent and never
request the microphone; the first message connects with the selected provider
and runs the same map actions as voice through each provider's text API
(OpenAI OPENAI_CHAT_MODEL, Gemini GEMINI_CHAT_MODEL via generateContent, which
never starts Live). Only one mode runs at a time.

- Chat panel rendered by the voice control presenter: conversation, recent and
  quick-command suggestions, Up/Down history, attachments (map-layer
  references or small UTF-8 text files, sent only with the message)
- Chat and voice panels sit above the dock or dock into the right rail's
  column; minimized bars sit centred above the dock, and height animates on
  swaps
- A centred panel steps down to its bar for the Location and Visual Presets
  trays without moving focus, rests above an open tray, and docks right while
  expanded over one; it returns to the centre once the trays close
- Minimized voice previews the last sentence from each side; the empty Voice
  Preview suggests things to say
- Settings opened from chat keep chat's frame and show the shared AI provider
  and quick commands; voice Settings show Back to voice
- Closing chat ends a typed session; the voice panel's close turns voice off
- The dock widens its voice control for the chat button and restyles its
  wings, which show icons only at 900px and below; assistant panels share the
  left panels' glass, and Cyber keeps its own surfaces
- Server text-chat handlers with body, time and per-IP limits
  (GEV_RATELIMIT_*_CHAT_PER_MIN), documented in SECURITY.md; tests and all
  eleven locales

Co-authored-by: Darren Dignam <[email protected]>
2026-11-02 20:56:39 +05:30

60 lines
1.7 KiB
JavaScript

import { defineStore } from 'pinia';
import { markRaw, shallowRef } from 'vue';
/**
* The voice control as the command dock renders it. The voice control
* presenter (src/voice/control.js) holds the control's state for the session
* owners and publishes it here; the view sends clicks, keys and form changes
* back to the connected presenter.
*/
export const useVoiceStore = defineStore('gev-voice', () => {
/** The presented control; null while no voice session owns one. */
const view = shallowRef(null);
/** @param {?object} next A fresh presentation; every publish re-renders. */
function publish(next) {
view.value = next && markRaw(next);
}
/** The presenter the view calls, or null while none is wired. */
const controls = shallowRef(null);
/** @param {?object} next The presenter, or null to disconnect. */
function connect(next) {
controls.value = next && markRaw(next);
}
/**
* The waveform's bars, apart from the view: they change every animation
* frame while voice is live, and only the visualizer renders them.
*/
const bars = shallowRef(null);
/** @param {?Array<{level: string, opacity: string}>} next */
function publishBars(next) {
bars.value = next && markRaw(next);
}
/**
* The chat conversation, apart from the view: a typed reply reveals frame
* by frame, and only the message list renders it.
*/
const chat = shallowRef([]);
/** @param {Array<{id: number, role: string, text: string}>} next */
function publishChat(next) {
chat.value = markRaw(next ?? []);
}
return {
view,
publish,
controls,
connect,
bars,
publishBars,
chat,
publishChat,
};
});