refactor: 统一实时协议类型并扩展会话状态字段

This commit is contained in:
2026-03-06 06:53:50 +08:00
parent 669bfac722
commit df2cf549c9
3 changed files with 86 additions and 12 deletions

View File

@@ -8,15 +8,22 @@ import "encoding/json"
type MsgType string type MsgType string
const ( const (
MsgStart MsgType = "start" // Begin recording session MsgHello MsgType = "hello"
MsgStop MsgType = "stop" // End recording session MsgStart MsgType = "start"
MsgPaste MsgType = "paste" // Re-paste a history item MsgStop MsgType = "stop"
MsgPaste MsgType = "paste"
MsgPing MsgType = "ping"
MsgPong MsgType = "pong"
) )
// ClientMsg is a JSON control message from the phone. // ClientMsg is a JSON control message from the phone.
type ClientMsg struct { type ClientMsg struct {
Type MsgType `json:"type"` Type MsgType `json:"type"`
SessionID string `json:"sessionId,omitempty"`
Seq int64 `json:"seq,omitempty"`
Text string `json:"text,omitempty"` // Only for "paste" Text string `json:"text,omitempty"` // Only for "paste"
Version int `json:"version,omitempty"`
TS int64 `json:"ts,omitempty"`
// Future extension: dynamic hotwords (Phase 2) // Future extension: dynamic hotwords (Phase 2)
// Hotwords []string `json:"hotwords,omitempty"` // Hotwords []string `json:"hotwords,omitempty"`
} }
@@ -24,17 +31,33 @@ type ClientMsg struct {
// ── Server → Client messages ── // ── Server → Client messages ──
const ( const (
MsgPartial MsgType = "partial" // Interim ASR result MsgReady MsgType = "ready"
MsgFinal MsgType = "final" // Final ASR result MsgState MsgType = "state"
MsgPasted MsgType = "pasted" // Paste confirmed MsgStartAck MsgType = "start_ack"
MsgError MsgType = "error" // Error notification MsgStopAck MsgType = "stop_ack"
MsgPartial MsgType = "partial"
MsgFinal MsgType = "final"
MsgPasted MsgType = "pasted"
MsgError MsgType = "error"
)
const (
StateIdle = "idle"
StateRecording = "recording"
StateStopping = "stopping"
) )
// ServerMsg is a JSON message sent to the phone. // ServerMsg is a JSON message sent to the phone.
type ServerMsg struct { type ServerMsg struct {
Type MsgType `json:"type"` Type MsgType `json:"type"`
State string `json:"state,omitempty"`
SessionID string `json:"sessionId,omitempty"`
Seq int64 `json:"seq,omitempty"`
Text string `json:"text,omitempty"` Text string `json:"text,omitempty"`
Message string `json:"message,omitempty"` // For errors Message string `json:"message,omitempty"` // For errors
Code string `json:"code,omitempty"`
Retryable bool `json:"retryable,omitempty"`
TS int64 `json:"ts,omitempty"`
} }
func (m ServerMsg) Bytes() []byte { func (m ServerMsg) Bytes() []byte {

35
web/src/protocol.ts Normal file
View File

@@ -0,0 +1,35 @@
export type ClientMsgType = "hello" | "start" | "stop" | "paste" | "ping";
export type ServerMsgType =
| "ready"
| "state"
| "start_ack"
| "stop_ack"
| "partial"
| "final"
| "pasted"
| "error"
| "pong";
export type SessionState = "idle" | "recording" | "stopping";
export interface ClientMsg {
type: ClientMsgType;
sessionId?: string;
seq?: number;
text?: string;
version?: number;
ts?: number;
}
export interface ServerMsg {
type: ServerMsgType;
state?: SessionState;
sessionId?: string;
seq?: number;
text?: string;
message?: string;
code?: string;
retryable?: boolean;
ts?: number;
}

View File

@@ -13,9 +13,13 @@ type ConnectionStatus = "connected" | "disconnected" | "connecting";
interface AppState { interface AppState {
// Connection // Connection
connectionStatus: ConnectionStatus; connectionStatus: ConnectionStatus;
weakNetwork: boolean;
// Recording // Recording
recording: boolean; recording: boolean;
pendingStart: boolean; pendingStart: boolean;
stopping: boolean;
micReady: boolean;
activeSessionId: string | null;
// Preview // Preview
previewText: string; previewText: string;
previewActive: boolean; previewActive: boolean;
@@ -24,8 +28,12 @@ interface AppState {
// Actions // Actions
setConnectionStatus: (status: ConnectionStatus) => void; setConnectionStatus: (status: ConnectionStatus) => void;
setWeakNetwork: (weak: boolean) => void;
setRecording: (recording: boolean) => void; setRecording: (recording: boolean) => void;
setPendingStart: (pending: boolean) => void; setPendingStart: (pending: boolean) => void;
setStopping: (stopping: boolean) => void;
setMicReady: (ready: boolean) => void;
setActiveSessionId: (sessionId: string | null) => void;
setPreview: (text: string, isFinal: boolean) => void; setPreview: (text: string, isFinal: boolean) => void;
clearPreview: () => void; clearPreview: () => void;
addHistory: (text: string) => void; addHistory: (text: string) => void;
@@ -36,15 +44,23 @@ export const useAppStore = create<AppState>()(
persist( persist(
(set, get) => ({ (set, get) => ({
connectionStatus: "connecting", connectionStatus: "connecting",
weakNetwork: false,
recording: false, recording: false,
pendingStart: false, pendingStart: false,
stopping: false,
micReady: false,
activeSessionId: null,
previewText: "", previewText: "",
previewActive: false, previewActive: false,
history: [], history: [],
setConnectionStatus: (connectionStatus) => set({ connectionStatus }), setConnectionStatus: (connectionStatus) => set({ connectionStatus }),
setWeakNetwork: (weakNetwork) => set({ weakNetwork }),
setRecording: (recording) => set({ recording }), setRecording: (recording) => set({ recording }),
setPendingStart: (pendingStart) => set({ pendingStart }), setPendingStart: (pendingStart) => set({ pendingStart }),
setStopping: (stopping) => set({ stopping }),
setMicReady: (micReady) => set({ micReady }),
setActiveSessionId: (activeSessionId) => set({ activeSessionId }),
setPreview: (text, isFinal) => setPreview: (text, isFinal) =>
set({ set({