Files
alva/src/felsokning/rost.ts
T
Claude 7b6619133f Master Prompt v2.0 + röstinmatning, verifierade checklistor och export
Dokumentation:
- MASTER-PROMPT.md uppdaterad till v2.0 (SaaS/AI-wrapper, roller,
  multi-tenant, onboarding, beta-fokus).
- Nya moduler: kommunikationsmodell (tal in/text ut, Push-to-Talk,
  redigering före skick), Live Share (behörighetsstyrd realtidslänk,
  versionsmärkta exporter), verifierade checklistor (bevis och kontext
  per kontrolltyp).

Implementation i MVP:t:
- Push-to-Talk-röstinmatning via webbläsarens taligenkänning (sv-SE):
  lyssnar endast efter aktivt tryck, röd indikator med realtids-
  transkript, texten hamnar i redigerbart fält och skickas aldrig
  automatiskt. Döljs i webbläsare utan talstöd.
- Verifierade checklistor: varje kontroll i metodikerna har minimikrav
  (foto/mätvärde/kommentar); fotokontroller verifieras med bild och
  mätkontroller kan inte markeras verifierade utan värde.
- Versionsmärkt JSON-export ur händelseloggen; exporten loggas själv
  som händelse med format, version och användare.

Verifierat: 12 vitest-tester gröna, produktionsbygge ok, utökat
Playwright-röktest genom hela flödet inkl. fotokravet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-02 22:40:47 +00:00

66 lines
2.0 KiB
TypeScript

// Tal-till-text enligt kommunikationsmodellen: rösten är ett inmatningssätt,
// inte ett separat gränssnitt. All logik bygger på text — transkriberingen
// hamnar i ett redigerbart fält och ingenting skickas utan bekräftelse.
//
// MVP:t använder webbläsarens SpeechRecognition (sv-SE). I produktions-
// versionen ersätts motorn av leverantörens Voice-to-Text-API bakom samma
// gränssnitt.
interface IgenkanningsResultat {
transkript: string;
slutgiltigt: boolean;
}
interface Igenkanning {
start(): void;
stop(): void;
abort(): void;
}
type SpeechRecognitionCtor = new () => {
lang: string;
continuous: boolean;
interimResults: boolean;
onresult: ((event: { resultIndex: number; results: ArrayLike<{ 0: { transcript: string }; isFinal: boolean }> }) => void) | null;
onend: (() => void) | null;
onerror: ((event: { error: string }) => void) | null;
start(): void;
stop(): void;
abort(): void;
};
function ctor(): SpeechRecognitionCtor | undefined {
if (typeof window === "undefined") return undefined;
const w = window as unknown as {
SpeechRecognition?: SpeechRecognitionCtor;
webkitSpeechRecognition?: SpeechRecognitionCtor;
};
return w.SpeechRecognition ?? w.webkitSpeechRecognition;
}
export function stodRost(): boolean {
return ctor() !== undefined;
}
export function startaIgenkanning(
paResultat: (r: IgenkanningsResultat) => void,
paSlut: () => void,
): Igenkanning | undefined {
const Ctor = ctor();
if (!Ctor) return undefined;
const igenkanning = new Ctor();
igenkanning.lang = "sv-SE";
igenkanning.continuous = true;
igenkanning.interimResults = true;
igenkanning.onresult = (event) => {
for (let i = event.resultIndex; i < event.results.length; i++) {
const resultat = event.results[i];
paResultat({ transkript: resultat[0].transcript, slutgiltigt: resultat.isFinal });
}
};
igenkanning.onend = paSlut;
igenkanning.onerror = paSlut;
igenkanning.start();
return igenkanning;
}