@elizaos/plugin-facewear 2.0.3-beta.6 → 2.0.3-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/display-text.d.ts +4 -0
- package/dist/actions/display-text.d.ts.map +1 -0
- package/dist/actions/display-text.js +90 -0
- package/dist/actions/display-text.js.map +1 -0
- package/dist/actions/facewear-connect.d.ts +3 -0
- package/dist/actions/facewear-connect.d.ts.map +1 -0
- package/dist/actions/facewear-connect.js +70 -0
- package/dist/actions/facewear-connect.js.map +1 -0
- package/dist/actions/facewear-control.d.ts +4 -0
- package/dist/actions/facewear-control.d.ts.map +1 -0
- package/dist/actions/facewear-control.js +627 -0
- package/dist/actions/facewear-control.js.map +1 -0
- package/dist/actions/facewear-debug.d.ts +3 -0
- package/dist/actions/facewear-debug.d.ts.map +1 -0
- package/dist/actions/facewear-debug.js +62 -0
- package/dist/actions/facewear-debug.js.map +1 -0
- package/dist/actions/facewear-status.d.ts +4 -0
- package/dist/actions/facewear-status.d.ts.map +1 -0
- package/dist/actions/facewear-status.js +66 -0
- package/dist/actions/facewear-status.js.map +1 -0
- package/dist/actions/microphone.d.ts +4 -0
- package/dist/actions/microphone.d.ts.map +1 -0
- package/dist/actions/microphone.js +63 -0
- package/dist/actions/microphone.js.map +1 -0
- package/dist/actions/view-actions.d.ts +23 -0
- package/dist/actions/view-actions.d.ts.map +1 -0
- package/dist/actions/view-actions.js +314 -0
- package/dist/actions/view-actions.js.map +1 -0
- package/dist/actions/vision-query.d.ts +4 -0
- package/dist/actions/vision-query.d.ts.map +1 -0
- package/dist/actions/vision-query.js +41 -0
- package/dist/actions/vision-query.js.map +1 -0
- package/dist/actions/xr-view-actions.d.ts +14 -0
- package/dist/actions/xr-view-actions.d.ts.map +1 -0
- package/dist/actions/xr-view-actions.js +29 -0
- package/dist/actions/xr-view-actions.js.map +1 -0
- package/dist/components/FacewearSpatialView.d.ts +50 -0
- package/dist/components/FacewearSpatialView.d.ts.map +1 -0
- package/dist/components/FacewearSpatialView.js +129 -0
- package/dist/components/FacewearSpatialView.js.map +1 -0
- package/dist/components/FacewearView.d.ts +17 -0
- package/dist/components/FacewearView.d.ts.map +1 -0
- package/dist/components/FacewearView.js +88 -0
- package/dist/components/FacewearView.js.map +1 -0
- package/dist/components/SmartglassesPanelView.d.ts +22 -0
- package/dist/components/SmartglassesPanelView.d.ts.map +1 -0
- package/dist/components/SmartglassesPanelView.js +140 -0
- package/dist/components/SmartglassesPanelView.js.map +1 -0
- package/dist/components/SmartglassesSpatialView.d.ts +46 -0
- package/dist/components/SmartglassesSpatialView.d.ts.map +1 -0
- package/dist/components/SmartglassesSpatialView.js +240 -0
- package/dist/components/SmartglassesSpatialView.js.map +1 -0
- package/dist/components/facewear-profiles.d.ts +27 -0
- package/dist/components/facewear-profiles.d.ts.map +1 -0
- package/dist/components/facewear-profiles.js +40 -0
- package/dist/components/facewear-profiles.js.map +1 -0
- package/dist/devices/apple-vision-pro.d.ts +7 -0
- package/dist/devices/apple-vision-pro.d.ts.map +1 -0
- package/dist/devices/apple-vision-pro.js +21 -0
- package/dist/devices/apple-vision-pro.js.map +1 -0
- package/dist/devices/even-realities.d.ts +7 -0
- package/dist/devices/even-realities.d.ts.map +1 -0
- package/dist/devices/even-realities.js +13 -0
- package/dist/devices/even-realities.js.map +1 -0
- package/dist/devices/meta-quest.d.ts +5 -0
- package/dist/devices/meta-quest.d.ts.map +1 -0
- package/dist/devices/meta-quest.js +21 -0
- package/dist/devices/meta-quest.js.map +1 -0
- package/dist/devices/registry.d.ts +19 -0
- package/dist/devices/registry.d.ts.map +1 -0
- package/dist/devices/registry.js +96 -0
- package/dist/devices/registry.js.map +1 -0
- package/dist/devices/xreal.d.ts +7 -0
- package/dist/devices/xreal.d.ts.map +1 -0
- package/dist/devices/xreal.js +19 -0
- package/dist/devices/xreal.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +260 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol/smartglasses.d.ts +306 -0
- package/dist/protocol/smartglasses.d.ts.map +1 -0
- package/dist/protocol/smartglasses.js +1485 -0
- package/dist/protocol/smartglasses.js.map +1 -0
- package/dist/protocol/xr.d.ts +137 -0
- package/dist/protocol/xr.d.ts.map +1 -0
- package/dist/protocol/xr.js +18 -0
- package/dist/protocol/xr.js.map +1 -0
- package/dist/providers/facewear-context.d.ts +3 -0
- package/dist/providers/facewear-context.d.ts.map +1 -0
- package/dist/providers/facewear-context.js +59 -0
- package/dist/providers/facewear-context.js.map +1 -0
- package/dist/providers/smartglasses-status.d.ts +3 -0
- package/dist/providers/smartglasses-status.d.ts.map +1 -0
- package/dist/providers/smartglasses-status.js +33 -0
- package/dist/providers/smartglasses-status.js.map +1 -0
- package/dist/register-terminal-view.d.ts +21 -0
- package/dist/register-terminal-view.d.ts.map +1 -0
- package/dist/register-terminal-view.js +70 -0
- package/dist/register-terminal-view.js.map +1 -0
- package/dist/register.d.ts +8 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +116 -0
- package/dist/register.js.map +1 -0
- package/dist/routes/connect.d.ts +3 -0
- package/dist/routes/connect.d.ts.map +1 -0
- package/dist/routes/connect.js +86 -0
- package/dist/routes/connect.js.map +1 -0
- package/dist/routes/device-config.d.ts +5 -0
- package/dist/routes/device-config.d.ts.map +1 -0
- package/dist/routes/device-config.js +56 -0
- package/dist/routes/device-config.js.map +1 -0
- package/dist/routes/simulator-route.d.ts +8 -0
- package/dist/routes/simulator-route.d.ts.map +1 -0
- package/dist/routes/simulator-route.js +32 -0
- package/dist/routes/simulator-route.js.map +1 -0
- package/dist/routes/status.d.ts +3 -0
- package/dist/routes/status.d.ts.map +1 -0
- package/dist/routes/status.js +34 -0
- package/dist/routes/status.js.map +1 -0
- package/dist/routes/view-host.d.ts +24 -0
- package/dist/routes/view-host.d.ts.map +1 -0
- package/dist/routes/view-host.js +339 -0
- package/dist/routes/view-host.js.map +1 -0
- package/dist/routes/views.d.ts +8 -0
- package/dist/routes/views.d.ts.map +1 -0
- package/dist/routes/views.js +31 -0
- package/dist/routes/views.js.map +1 -0
- package/dist/services/audio-pipeline.d.ts +20 -0
- package/dist/services/audio-pipeline.d.ts.map +1 -0
- package/dist/services/audio-pipeline.js +87 -0
- package/dist/services/audio-pipeline.js.map +1 -0
- package/dist/services/facewear-service.d.ts +26 -0
- package/dist/services/facewear-service.d.ts.map +1 -0
- package/dist/services/facewear-service.js +45 -0
- package/dist/services/facewear-service.js.map +1 -0
- package/dist/services/smartglasses-service.d.ts +244 -0
- package/dist/services/smartglasses-service.d.ts.map +1 -0
- package/dist/services/smartglasses-service.js +821 -0
- package/dist/services/smartglasses-service.js.map +1 -0
- package/dist/services/vision-pipeline.d.ts +16 -0
- package/dist/services/vision-pipeline.d.ts.map +1 -0
- package/dist/services/vision-pipeline.js +39 -0
- package/dist/services/vision-pipeline.js.map +1 -0
- package/dist/services/xr-session-service.d.ts +54 -0
- package/dist/services/xr-session-service.d.ts.map +1 -0
- package/dist/services/xr-session-service.js +345 -0
- package/dist/services/xr-session-service.js.map +1 -0
- package/dist/status-format.d.ts +15 -0
- package/dist/status-format.d.ts.map +1 -0
- package/dist/status-format.js +89 -0
- package/dist/status-format.js.map +1 -0
- package/dist/transport/even-bridge.d.ts +69 -0
- package/dist/transport/even-bridge.d.ts.map +1 -0
- package/dist/transport/even-bridge.js +510 -0
- package/dist/transport/even-bridge.js.map +1 -0
- package/dist/transport/mock.d.ts +42 -0
- package/dist/transport/mock.d.ts.map +1 -0
- package/dist/transport/mock.js +124 -0
- package/dist/transport/mock.js.map +1 -0
- package/dist/transport/noble.d.ts +62 -0
- package/dist/transport/noble.d.ts.map +1 -0
- package/dist/transport/noble.js +256 -0
- package/dist/transport/noble.js.map +1 -0
- package/dist/transport/types.d.ts +36 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/transport/types.js +1 -0
- package/dist/transport/types.js.map +1 -0
- package/dist/transport/web-bluetooth.d.ts +58 -0
- package/dist/transport/web-bluetooth.d.ts.map +1 -0
- package/dist/transport/web-bluetooth.js +164 -0
- package/dist/transport/web-bluetooth.js.map +1 -0
- package/dist/ui/FacewearAppView.d.ts +4 -0
- package/dist/ui/FacewearAppView.d.ts.map +1 -0
- package/dist/ui/FacewearAppView.js +257 -0
- package/dist/ui/FacewearAppView.js.map +1 -0
- package/dist/ui/SmartglassesView.d.ts +10 -0
- package/dist/ui/SmartglassesView.d.ts.map +1 -0
- package/dist/ui/SmartglassesView.helpers.d.ts +104 -0
- package/dist/ui/SmartglassesView.helpers.d.ts.map +1 -0
- package/dist/ui/SmartglassesView.helpers.js +261 -0
- package/dist/ui/SmartglassesView.helpers.js.map +1 -0
- package/dist/ui/SmartglassesView.js +1189 -0
- package/dist/ui/SmartglassesView.js.map +1 -0
- package/dist/ui/facewear-view-bundle.d.ts +5 -0
- package/dist/ui/facewear-view-bundle.d.ts.map +1 -0
- package/dist/ui/facewear-view-bundle.js +17 -0
- package/dist/ui/facewear-view-bundle.js.map +1 -0
- package/dist/views/bundle.js +2950 -0
- package/dist/views/bundle.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVEN_G1_UART,
|
|
3
|
+
encodeMicCommand,
|
|
4
|
+
parseG1Notification
|
|
5
|
+
} from "../protocol/smartglasses.js";
|
|
6
|
+
class WebBluetoothG1Transport {
|
|
7
|
+
constructor(bluetooth = getNavigatorBluetooth()) {
|
|
8
|
+
this.bluetooth = bluetooth;
|
|
9
|
+
}
|
|
10
|
+
bluetooth;
|
|
11
|
+
name = "web-bluetooth-g1";
|
|
12
|
+
sides = /* @__PURE__ */ new Map();
|
|
13
|
+
eventCallbacks = /* @__PURE__ */ new Set();
|
|
14
|
+
audioCallbacks = /* @__PURE__ */ new Set();
|
|
15
|
+
async connect() {
|
|
16
|
+
try {
|
|
17
|
+
await this.connectLens("left");
|
|
18
|
+
await this.connectLens("right");
|
|
19
|
+
} catch (error) {
|
|
20
|
+
await this.disconnect();
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async disconnect() {
|
|
25
|
+
for (const [side, connection] of this.sides) {
|
|
26
|
+
await connection.rx.stopNotifications?.();
|
|
27
|
+
connection.rx.removeEventListener?.(
|
|
28
|
+
"characteristicvaluechanged",
|
|
29
|
+
connection.listener
|
|
30
|
+
);
|
|
31
|
+
connection.server.disconnect?.();
|
|
32
|
+
this.sides.delete(side);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
isConnected() {
|
|
36
|
+
return this.sides.size === 2 && [...this.sides.values()].every(
|
|
37
|
+
(connection) => connection.server.connected !== false
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
getConnectedLenses() {
|
|
41
|
+
const lenses = {};
|
|
42
|
+
for (const [side, connection] of this.sides) {
|
|
43
|
+
lenses[side] = {
|
|
44
|
+
connected: connection.server.connected !== false,
|
|
45
|
+
name: connection.device.name,
|
|
46
|
+
address: connection.device.id
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return lenses;
|
|
50
|
+
}
|
|
51
|
+
async write(side, data) {
|
|
52
|
+
const connection = this.sides.get(side);
|
|
53
|
+
if (!connection) throw new Error(`G1 ${side} lens is not connected`);
|
|
54
|
+
const buffer = toArrayBuffer(data);
|
|
55
|
+
if (connection.tx.writeValueWithoutResponse) {
|
|
56
|
+
await connection.tx.writeValueWithoutResponse(buffer);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (connection.tx.writeValueWithResponse) {
|
|
60
|
+
await connection.tx.writeValueWithResponse(buffer);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await connection.tx.writeValue?.(buffer);
|
|
64
|
+
}
|
|
65
|
+
async writeBoth(data) {
|
|
66
|
+
await this.write("left", data);
|
|
67
|
+
await this.write("right", data);
|
|
68
|
+
}
|
|
69
|
+
async openMicrophone(enabled) {
|
|
70
|
+
await this.write("right", encodeMicCommand(enabled));
|
|
71
|
+
}
|
|
72
|
+
onEvent(callback) {
|
|
73
|
+
this.eventCallbacks.add(callback);
|
|
74
|
+
return () => this.eventCallbacks.delete(callback);
|
|
75
|
+
}
|
|
76
|
+
onAudio(callback) {
|
|
77
|
+
this.audioCallbacks.add(callback);
|
|
78
|
+
return () => this.audioCallbacks.delete(callback);
|
|
79
|
+
}
|
|
80
|
+
async connectLens(side) {
|
|
81
|
+
if (this.sides.has(side)) return;
|
|
82
|
+
const nameMarker = side === "left" ? "_L_" : "_R_";
|
|
83
|
+
const device = await this.bluetooth.requestDevice({
|
|
84
|
+
filters: [
|
|
85
|
+
{ namePrefix: "Even" },
|
|
86
|
+
{ namePrefix: "G1" },
|
|
87
|
+
{ namePrefix: "ER" }
|
|
88
|
+
],
|
|
89
|
+
optionalServices: [EVEN_G1_UART.service]
|
|
90
|
+
});
|
|
91
|
+
if (device.name && !device.name.includes(nameMarker)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Selected ${device.name} for ${side}, expected a lens name containing ${nameMarker}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const duplicateSide = this.findConnectedDeviceSide(device);
|
|
97
|
+
if (duplicateSide) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Selected the ${duplicateSide} lens again while connecting the ${side} lens`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (!device.gatt)
|
|
103
|
+
throw new Error(`Selected G1 ${side} device does not expose GATT`);
|
|
104
|
+
const server = await device.gatt.connect();
|
|
105
|
+
const service = await server.getPrimaryService(EVEN_G1_UART.service);
|
|
106
|
+
const tx = await service.getCharacteristic(EVEN_G1_UART.tx);
|
|
107
|
+
const rx = await service.getCharacteristic(EVEN_G1_UART.rx);
|
|
108
|
+
const listener = (event) => {
|
|
109
|
+
const value = event.target?.value;
|
|
110
|
+
if (!value) return;
|
|
111
|
+
const bytes = new Uint8Array(
|
|
112
|
+
value.buffer.slice(
|
|
113
|
+
value.byteOffset,
|
|
114
|
+
value.byteOffset + value.byteLength
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
this.emitParsed(parseG1Notification(side, bytes));
|
|
118
|
+
};
|
|
119
|
+
rx.addEventListener("characteristicvaluechanged", listener);
|
|
120
|
+
await rx.startNotifications();
|
|
121
|
+
this.sides.set(side, { device, server, tx, rx, listener });
|
|
122
|
+
}
|
|
123
|
+
findConnectedDeviceSide(device) {
|
|
124
|
+
for (const [side, connection] of this.sides) {
|
|
125
|
+
if (connection.device === device) return side;
|
|
126
|
+
if (device.id && connection.device.id === device.id) return side;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
emitParsed(event) {
|
|
131
|
+
for (const callback of this.eventCallbacks) callback(event);
|
|
132
|
+
const audioData = event.audioPcm ?? event.audioData;
|
|
133
|
+
if (audioData) {
|
|
134
|
+
for (const callback of this.audioCallbacks)
|
|
135
|
+
callback(
|
|
136
|
+
audioData,
|
|
137
|
+
16e3,
|
|
138
|
+
event.side,
|
|
139
|
+
event.audioEncoding,
|
|
140
|
+
event.sequence
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function toArrayBuffer(data) {
|
|
146
|
+
const buffer = new ArrayBuffer(data.byteLength);
|
|
147
|
+
new Uint8Array(buffer).set(data);
|
|
148
|
+
return buffer;
|
|
149
|
+
}
|
|
150
|
+
function getWebBluetoothG1Transport() {
|
|
151
|
+
const nav = globalThis.navigator;
|
|
152
|
+
return nav?.bluetooth ? new WebBluetoothG1Transport(nav.bluetooth) : null;
|
|
153
|
+
}
|
|
154
|
+
function getNavigatorBluetooth() {
|
|
155
|
+
const nav = globalThis.navigator;
|
|
156
|
+
if (!nav?.bluetooth)
|
|
157
|
+
throw new Error("Web Bluetooth is not available in this runtime");
|
|
158
|
+
return nav.bluetooth;
|
|
159
|
+
}
|
|
160
|
+
export {
|
|
161
|
+
WebBluetoothG1Transport,
|
|
162
|
+
getWebBluetoothG1Transport
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=web-bluetooth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/transport/web-bluetooth.ts"],"sourcesContent":["import {\n EVEN_G1_UART,\n encodeMicCommand,\n type G1Event,\n type GlassSide,\n parseG1Notification,\n type SmartglassesAudioEncoding,\n} from \"../protocol/smartglasses.js\";\nimport type {\n SmartglassesConnectedLenses,\n SmartglassesTransport,\n} from \"./types.js\";\n\ntype BluetoothRemoteGATTCharacteristicLike = {\n value?: DataView;\n writeValueWithoutResponse?: (data: ArrayBuffer) => Promise<void>;\n writeValueWithResponse?: (data: ArrayBuffer) => Promise<void>;\n writeValue?: (data: ArrayBuffer) => Promise<void>;\n startNotifications: () => Promise<BluetoothRemoteGATTCharacteristicLike>;\n stopNotifications?: () => Promise<void>;\n addEventListener: (type: string, listener: (event: Event) => void) => void;\n removeEventListener?: (\n type: string,\n listener: (event: Event) => void,\n ) => void;\n};\n\ntype BluetoothRemoteGATTServerLike = {\n connected?: boolean;\n getPrimaryService: (service: string) => Promise<{\n getCharacteristic: (\n characteristic: string,\n ) => Promise<BluetoothRemoteGATTCharacteristicLike>;\n }>;\n disconnect?: () => void;\n};\n\ntype BluetoothDeviceLike = {\n name?: string;\n id?: string;\n gatt?: {\n connect: () => Promise<BluetoothRemoteGATTServerLike>;\n };\n};\n\ntype NavigatorBluetoothLike = {\n requestDevice: (options: {\n filters?: Array<{ namePrefix?: string; services?: string[] }>;\n optionalServices?: string[];\n }) => Promise<BluetoothDeviceLike>;\n};\n\ntype SideConnection = {\n device: BluetoothDeviceLike;\n server: BluetoothRemoteGATTServerLike;\n tx: BluetoothRemoteGATTCharacteristicLike;\n rx: BluetoothRemoteGATTCharacteristicLike;\n listener: (event: Event) => void;\n};\n\nexport class WebBluetoothG1Transport implements SmartglassesTransport {\n readonly name = \"web-bluetooth-g1\";\n private readonly sides = new Map<GlassSide, SideConnection>();\n private eventCallbacks = new Set<(event: G1Event) => void>();\n private audioCallbacks = new Set<\n (\n audioData: Uint8Array,\n sampleRate: number,\n side: GlassSide,\n encoding?: SmartglassesAudioEncoding,\n sequence?: number,\n ) => void\n >();\n\n constructor(\n private readonly bluetooth: NavigatorBluetoothLike = getNavigatorBluetooth(),\n ) {}\n\n async connect(): Promise<void> {\n try {\n await this.connectLens(\"left\");\n await this.connectLens(\"right\");\n } catch (error) {\n await this.disconnect();\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n for (const [side, connection] of this.sides) {\n await connection.rx.stopNotifications?.();\n connection.rx.removeEventListener?.(\n \"characteristicvaluechanged\",\n connection.listener,\n );\n connection.server.disconnect?.();\n this.sides.delete(side);\n }\n }\n\n isConnected(): boolean {\n return (\n this.sides.size === 2 &&\n [...this.sides.values()].every(\n (connection) => connection.server.connected !== false,\n )\n );\n }\n\n getConnectedLenses(): SmartglassesConnectedLenses {\n const lenses: SmartglassesConnectedLenses = {};\n for (const [side, connection] of this.sides) {\n lenses[side] = {\n connected: connection.server.connected !== false,\n name: connection.device.name,\n address: connection.device.id,\n };\n }\n return lenses;\n }\n\n async write(side: GlassSide, data: Uint8Array): Promise<void> {\n const connection = this.sides.get(side);\n if (!connection) throw new Error(`G1 ${side} lens is not connected`);\n const buffer = toArrayBuffer(data);\n if (connection.tx.writeValueWithoutResponse) {\n await connection.tx.writeValueWithoutResponse(buffer);\n return;\n }\n if (connection.tx.writeValueWithResponse) {\n await connection.tx.writeValueWithResponse(buffer);\n return;\n }\n await connection.tx.writeValue?.(buffer);\n }\n\n async writeBoth(data: Uint8Array): Promise<void> {\n await this.write(\"left\", data);\n await this.write(\"right\", data);\n }\n\n async openMicrophone(enabled: boolean): Promise<void> {\n await this.write(\"right\", encodeMicCommand(enabled));\n }\n\n onEvent(callback: (event: G1Event) => void): () => void {\n this.eventCallbacks.add(callback);\n return () => this.eventCallbacks.delete(callback);\n }\n\n onAudio(\n callback: (\n audioData: Uint8Array,\n sampleRate: number,\n side: GlassSide,\n encoding?: SmartglassesAudioEncoding,\n sequence?: number,\n ) => void,\n ): () => void {\n this.audioCallbacks.add(callback);\n return () => this.audioCallbacks.delete(callback);\n }\n\n async connectLens(side: GlassSide): Promise<void> {\n if (this.sides.has(side)) return;\n const nameMarker = side === \"left\" ? \"_L_\" : \"_R_\";\n const device = await this.bluetooth.requestDevice({\n filters: [\n { namePrefix: \"Even\" },\n { namePrefix: \"G1\" },\n { namePrefix: \"ER\" },\n ],\n optionalServices: [EVEN_G1_UART.service],\n });\n if (device.name && !device.name.includes(nameMarker)) {\n throw new Error(\n `Selected ${device.name} for ${side}, expected a lens name containing ${nameMarker}`,\n );\n }\n const duplicateSide = this.findConnectedDeviceSide(device);\n if (duplicateSide) {\n throw new Error(\n `Selected the ${duplicateSide} lens again while connecting the ${side} lens`,\n );\n }\n if (!device.gatt)\n throw new Error(`Selected G1 ${side} device does not expose GATT`);\n const server = await device.gatt.connect();\n const service = await server.getPrimaryService(EVEN_G1_UART.service);\n const tx = await service.getCharacteristic(EVEN_G1_UART.tx);\n const rx = await service.getCharacteristic(EVEN_G1_UART.rx);\n const listener = (event: Event) => {\n const value = (\n event.target as BluetoothRemoteGATTCharacteristicLike | null\n )?.value;\n if (!value) return;\n const bytes = new Uint8Array(\n value.buffer.slice(\n value.byteOffset,\n value.byteOffset + value.byteLength,\n ),\n );\n this.emitParsed(parseG1Notification(side, bytes));\n };\n rx.addEventListener(\"characteristicvaluechanged\", listener);\n await rx.startNotifications();\n this.sides.set(side, { device, server, tx, rx, listener });\n }\n\n private findConnectedDeviceSide(\n device: BluetoothDeviceLike,\n ): GlassSide | null {\n for (const [side, connection] of this.sides) {\n if (connection.device === device) return side;\n if (device.id && connection.device.id === device.id) return side;\n }\n return null;\n }\n\n private emitParsed(event: G1Event): void {\n for (const callback of this.eventCallbacks) callback(event);\n const audioData = event.audioPcm ?? event.audioData;\n if (audioData) {\n for (const callback of this.audioCallbacks)\n callback(\n audioData,\n 16_000,\n event.side,\n event.audioEncoding,\n event.sequence,\n );\n }\n }\n}\n\nfunction toArrayBuffer(data: Uint8Array): ArrayBuffer {\n const buffer = new ArrayBuffer(data.byteLength);\n new Uint8Array(buffer).set(data);\n return buffer;\n}\n\nexport function getWebBluetoothG1Transport(): SmartglassesTransport | null {\n const nav = (\n globalThis as { navigator?: { bluetooth?: NavigatorBluetoothLike } }\n ).navigator;\n return nav?.bluetooth ? new WebBluetoothG1Transport(nav.bluetooth) : null;\n}\n\nfunction getNavigatorBluetooth(): NavigatorBluetoothLike {\n const nav = (\n globalThis as { navigator?: { bluetooth?: NavigatorBluetoothLike } }\n ).navigator;\n if (!nav?.bluetooth)\n throw new Error(\"Web Bluetooth is not available in this runtime\");\n return nav.bluetooth;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function FacewearAppView(): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function FacewearTuiView(): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export declare function SmartglassesTuiView(): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
//# sourceMappingURL=FacewearAppView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FacewearAppView.d.ts","sourceRoot":"","sources":["../../src/ui/FacewearAppView.tsx"],"names":[],"mappings":"AA+FA,wBAAgB,eAAe,4CA+K9B;AAED,wBAAgB,eAAe,4CAc9B;AAED,wBAAgB,mBAAmB,4CAUlC"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TerminalPluginView } from "@elizaos/ui";
|
|
3
|
+
import { useAgentElement } from "@elizaos/ui/agent-surface";
|
|
4
|
+
import { Bluetooth, Glasses, Wifi, Zap } from "lucide-react";
|
|
5
|
+
import { useCallback, useEffect, useState } from "react";
|
|
6
|
+
import {
|
|
7
|
+
FACEWEAR_DEVICE_PROFILES,
|
|
8
|
+
isProfileConnected
|
|
9
|
+
} from "../components/facewear-profiles.js";
|
|
10
|
+
const ACTIVE_DEVICE_LIMIT = 4;
|
|
11
|
+
function ConnectionIcon({ connectionType }) {
|
|
12
|
+
if (connectionType.toLowerCase().includes("bluetooth")) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Bluetooth, { className: "h-4 w-4" });
|
|
14
|
+
}
|
|
15
|
+
if (connectionType.toLowerCase().includes("wifi")) {
|
|
16
|
+
return /* @__PURE__ */ jsx(Wifi, { className: "h-4 w-4" });
|
|
17
|
+
}
|
|
18
|
+
return /* @__PURE__ */ jsx(Zap, { className: "h-4 w-4" });
|
|
19
|
+
}
|
|
20
|
+
function DeviceCard({
|
|
21
|
+
profile,
|
|
22
|
+
connectedDevices,
|
|
23
|
+
onConnect
|
|
24
|
+
}) {
|
|
25
|
+
const isConnected = isProfileConnected(profile, connectedDevices);
|
|
26
|
+
const Icon = profile.connectionType.toLowerCase().includes("bluetooth") ? Bluetooth : Glasses;
|
|
27
|
+
const actionLabel = isConnected ? `Manage ${profile.name}` : `Connect ${profile.name}`;
|
|
28
|
+
const { ref: connectRef, agentProps: connectAgentProps } = useAgentElement({
|
|
29
|
+
id: `device-${profile.type}`,
|
|
30
|
+
role: "button",
|
|
31
|
+
label: actionLabel,
|
|
32
|
+
group: "devices",
|
|
33
|
+
status: isConnected ? "active" : "inactive",
|
|
34
|
+
description: `${isConnected ? "Manage" : "Connect"} the ${profile.name} device`
|
|
35
|
+
});
|
|
36
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-3 transition-colors", children: [
|
|
37
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
38
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
39
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx(
|
|
40
|
+
Icon,
|
|
41
|
+
{
|
|
42
|
+
className: `h-5 w-5 ${isConnected ? "text-green-500" : "text-muted"}`
|
|
43
|
+
}
|
|
44
|
+
) }),
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
46
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold text-txt", children: profile.name }),
|
|
47
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted", children: profile.manufacturer })
|
|
48
|
+
] })
|
|
49
|
+
] }),
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
"span",
|
|
52
|
+
{
|
|
53
|
+
className: `flex-shrink-0 inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium ${isConnected ? "text-green-600 dark:text-green-400" : "text-muted"}`,
|
|
54
|
+
children: isConnected ? "On" : "Off"
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
] }),
|
|
58
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [
|
|
59
|
+
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs text-muted", children: [
|
|
60
|
+
/* @__PURE__ */ jsx(ConnectionIcon, { connectionType: profile.connectionType }),
|
|
61
|
+
profile.connectionType
|
|
62
|
+
] }),
|
|
63
|
+
/* @__PURE__ */ jsx(
|
|
64
|
+
"button",
|
|
65
|
+
{
|
|
66
|
+
ref: connectRef,
|
|
67
|
+
type: "button",
|
|
68
|
+
onClick: () => onConnect(profile.type),
|
|
69
|
+
"aria-label": actionLabel,
|
|
70
|
+
className: "inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors",
|
|
71
|
+
...connectAgentProps,
|
|
72
|
+
children: isConnected ? "Manage" : "Connect"
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
] })
|
|
76
|
+
] });
|
|
77
|
+
}
|
|
78
|
+
function FacewearAppView() {
|
|
79
|
+
const [status, setStatus] = useState({
|
|
80
|
+
connected: false,
|
|
81
|
+
devices: []
|
|
82
|
+
});
|
|
83
|
+
const [loading, setLoading] = useState(true);
|
|
84
|
+
const [error, setError] = useState(null);
|
|
85
|
+
const fetchStatus = useCallback(async () => {
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch("/api/facewear/status");
|
|
88
|
+
if (res.ok) {
|
|
89
|
+
const data = await res.json();
|
|
90
|
+
setStatus(data);
|
|
91
|
+
setError(null);
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
95
|
+
} finally {
|
|
96
|
+
setLoading(false);
|
|
97
|
+
}
|
|
98
|
+
}, []);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
void fetchStatus();
|
|
101
|
+
const interval = setInterval(() => void fetchStatus(), 5e3);
|
|
102
|
+
return () => clearInterval(interval);
|
|
103
|
+
}, [fetchStatus]);
|
|
104
|
+
function handleConnect(deviceType) {
|
|
105
|
+
if (deviceType === "even-realities") {
|
|
106
|
+
window.location.assign("/apps/smartglasses");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
window.open("/api/xr/connect", "_blank", "noopener,noreferrer");
|
|
110
|
+
}
|
|
111
|
+
const activeCount = status.devices.length;
|
|
112
|
+
const { ref: xrConnectRef, agentProps: xrConnectAgentProps } = useAgentElement({
|
|
113
|
+
id: "link-xr-connect",
|
|
114
|
+
role: "link",
|
|
115
|
+
label: "Connect",
|
|
116
|
+
group: "quick-actions",
|
|
117
|
+
description: "Open the XR device connect page in a new tab"
|
|
118
|
+
});
|
|
119
|
+
const { ref: xrStatusRef, agentProps: xrStatusAgentProps } = useAgentElement({
|
|
120
|
+
id: "link-xr-status",
|
|
121
|
+
role: "link",
|
|
122
|
+
label: "Status",
|
|
123
|
+
group: "quick-actions",
|
|
124
|
+
description: "Open the XR status API in a new tab"
|
|
125
|
+
});
|
|
126
|
+
const { ref: refreshRef, agentProps: refreshAgentProps } = useAgentElement({
|
|
127
|
+
id: "action-refresh",
|
|
128
|
+
role: "button",
|
|
129
|
+
label: "Refresh",
|
|
130
|
+
group: "quick-actions",
|
|
131
|
+
description: "Refresh the connected device status"
|
|
132
|
+
});
|
|
133
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto bg-bg text-txt", children: [
|
|
134
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
136
|
+
/* @__PURE__ */ jsx(Glasses, { className: "h-4 w-4 text-accent" }),
|
|
137
|
+
/* @__PURE__ */ jsx("h1", { className: "text-sm font-semibold", children: "Facewear" })
|
|
138
|
+
] }) }),
|
|
139
|
+
/* @__PURE__ */ jsxs(
|
|
140
|
+
"span",
|
|
141
|
+
{
|
|
142
|
+
className: `inline-flex h-7 items-center gap-1.5 px-1.5 text-xs font-medium ${activeCount > 0 ? "text-green-700 dark:text-green-300" : "text-muted"}`,
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ jsx(Zap, { className: "h-3.5 w-3.5" }),
|
|
145
|
+
activeCount > 0 ? `${activeCount} on` : "0 on"
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
] }) }),
|
|
150
|
+
/* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl p-4", children: [
|
|
151
|
+
loading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-border border-t-accent" }) }),
|
|
152
|
+
error && /* @__PURE__ */ jsx("div", { className: "mb-4 px-1 py-2 text-xs text-destructive", children: error }),
|
|
153
|
+
!loading && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
154
|
+
activeCount > 0 && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
155
|
+
status.devices.slice(0, ACTIVE_DEVICE_LIMIT).map((device) => /* @__PURE__ */ jsx(
|
|
156
|
+
"span",
|
|
157
|
+
{
|
|
158
|
+
className: "inline-flex items-center gap-1 px-1.5 py-0.5 text-xs text-green-700 dark:text-green-300",
|
|
159
|
+
children: device.deviceType ?? device.kind
|
|
160
|
+
},
|
|
161
|
+
device.id
|
|
162
|
+
)),
|
|
163
|
+
status.devices.length > ACTIVE_DEVICE_LIMIT ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center px-1.5 py-0.5 text-xs text-muted", children: [
|
|
164
|
+
"+",
|
|
165
|
+
status.devices.length - ACTIVE_DEVICE_LIMIT
|
|
166
|
+
] }) : null
|
|
167
|
+
] }) }),
|
|
168
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-3", children: FACEWEAR_DEVICE_PROFILES.map((profile) => /* @__PURE__ */ jsx(
|
|
169
|
+
DeviceCard,
|
|
170
|
+
{
|
|
171
|
+
profile,
|
|
172
|
+
connectedDevices: status.devices,
|
|
173
|
+
onConnect: handleConnect
|
|
174
|
+
},
|
|
175
|
+
profile.type
|
|
176
|
+
)) }),
|
|
177
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
178
|
+
/* @__PURE__ */ jsxs(
|
|
179
|
+
"a",
|
|
180
|
+
{
|
|
181
|
+
ref: xrConnectRef,
|
|
182
|
+
href: "/api/xr/connect",
|
|
183
|
+
target: "_blank",
|
|
184
|
+
rel: "noreferrer",
|
|
185
|
+
"aria-label": "XR connect",
|
|
186
|
+
className: "inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors",
|
|
187
|
+
...xrConnectAgentProps,
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ jsx(Zap, { className: "h-3.5 w-3.5" }),
|
|
190
|
+
"Connect"
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
"a",
|
|
196
|
+
{
|
|
197
|
+
ref: xrStatusRef,
|
|
198
|
+
href: "/api/xr/status",
|
|
199
|
+
target: "_blank",
|
|
200
|
+
rel: "noreferrer",
|
|
201
|
+
"aria-label": "XR status",
|
|
202
|
+
className: "inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors",
|
|
203
|
+
...xrStatusAgentProps,
|
|
204
|
+
children: "Status"
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
/* @__PURE__ */ jsx(
|
|
208
|
+
"button",
|
|
209
|
+
{
|
|
210
|
+
ref: refreshRef,
|
|
211
|
+
type: "button",
|
|
212
|
+
onClick: () => void fetchStatus(),
|
|
213
|
+
"aria-label": "Refresh",
|
|
214
|
+
className: "inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors",
|
|
215
|
+
...refreshAgentProps,
|
|
216
|
+
children: "Refresh"
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
] }) })
|
|
220
|
+
] })
|
|
221
|
+
] })
|
|
222
|
+
] });
|
|
223
|
+
}
|
|
224
|
+
function FacewearTuiView() {
|
|
225
|
+
return /* @__PURE__ */ jsx(
|
|
226
|
+
TerminalPluginView,
|
|
227
|
+
{
|
|
228
|
+
id: "facewear",
|
|
229
|
+
label: "Facewear TUI",
|
|
230
|
+
description: "Status",
|
|
231
|
+
commands: [],
|
|
232
|
+
endpoints: [
|
|
233
|
+
"/api/facewear/status",
|
|
234
|
+
"/api/facewear/devices",
|
|
235
|
+
"/api/facewear/views"
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function SmartglassesTuiView() {
|
|
241
|
+
return /* @__PURE__ */ jsx(
|
|
242
|
+
TerminalPluginView,
|
|
243
|
+
{
|
|
244
|
+
id: "smartglasses",
|
|
245
|
+
label: "Smartglasses TUI",
|
|
246
|
+
description: "Status",
|
|
247
|
+
commands: [],
|
|
248
|
+
endpoints: ["/api/facewear/status", "/api/facewear/devices"]
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
export {
|
|
253
|
+
FacewearAppView,
|
|
254
|
+
FacewearTuiView,
|
|
255
|
+
SmartglassesTuiView
|
|
256
|
+
};
|
|
257
|
+
//# sourceMappingURL=FacewearAppView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ui/FacewearAppView.tsx"],"sourcesContent":["import { TerminalPluginView } from \"@elizaos/ui\";\nimport { useAgentElement } from \"@elizaos/ui/agent-surface\";\nimport { Bluetooth, Glasses, Wifi, Zap } from \"lucide-react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n type ConnectedDevice,\n FACEWEAR_DEVICE_PROFILES,\n type FacewearDeviceProfileRow,\n type FacewearStatusResponse,\n isProfileConnected,\n} from \"../components/facewear-profiles.js\";\nimport type { FacewearDeviceType } from \"../devices/registry.js\";\n\nconst ACTIVE_DEVICE_LIMIT = 4;\n\nfunction ConnectionIcon({ connectionType }: { connectionType: string }) {\n if (connectionType.toLowerCase().includes(\"bluetooth\")) {\n return <Bluetooth className=\"h-4 w-4\" />;\n }\n if (connectionType.toLowerCase().includes(\"wifi\")) {\n return <Wifi className=\"h-4 w-4\" />;\n }\n return <Zap className=\"h-4 w-4\" />;\n}\n\nfunction DeviceCard({\n profile,\n connectedDevices,\n onConnect,\n}: {\n profile: FacewearDeviceProfileRow;\n connectedDevices: ConnectedDevice[];\n onConnect: (type: FacewearDeviceType) => void;\n}) {\n const isConnected = isProfileConnected(profile, connectedDevices);\n const Icon = profile.connectionType.toLowerCase().includes(\"bluetooth\")\n ? Bluetooth\n : Glasses;\n const actionLabel = isConnected\n ? `Manage ${profile.name}`\n : `Connect ${profile.name}`;\n const { ref: connectRef, agentProps: connectAgentProps } =\n useAgentElement<HTMLButtonElement>({\n id: `device-${profile.type}`,\n role: \"button\",\n label: actionLabel,\n group: \"devices\",\n status: isConnected ? \"active\" : \"inactive\",\n description: `${isConnected ? \"Manage\" : \"Connect\"} the ${profile.name} device`,\n });\n\n return (\n <div className=\"py-3 transition-colors\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"flex h-10 w-10 flex-shrink-0 items-center justify-center\">\n <Icon\n className={`h-5 w-5 ${isConnected ? \"text-green-500\" : \"text-muted\"}`}\n />\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-txt\">\n {profile.name}\n </p>\n <p className=\"text-xs text-muted\">{profile.manufacturer}</p>\n </div>\n </div>\n <span\n className={`flex-shrink-0 inline-flex items-center gap-1 px-1.5 py-0.5 text-xs font-medium ${\n isConnected ? \"text-green-600 dark:text-green-400\" : \"text-muted\"\n }`}\n >\n {isConnected ? \"On\" : \"Off\"}\n </span>\n </div>\n <div className=\"mt-2 flex items-center justify-between gap-2\">\n <span className=\"inline-flex items-center gap-1.5 text-xs text-muted\">\n <ConnectionIcon connectionType={profile.connectionType} />\n {profile.connectionType}\n </span>\n <button\n ref={connectRef}\n type=\"button\"\n onClick={() => onConnect(profile.type)}\n aria-label={actionLabel}\n className=\"inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors\"\n {...connectAgentProps}\n >\n {isConnected ? \"Manage\" : \"Connect\"}\n </button>\n </div>\n </div>\n );\n}\n\nexport function FacewearAppView() {\n const [status, setStatus] = useState<FacewearStatusResponse>({\n connected: false,\n devices: [],\n });\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchStatus = useCallback(async (): Promise<void> => {\n try {\n const res = await fetch(\"/api/facewear/status\");\n if (res.ok) {\n const data = (await res.json()) as FacewearStatusResponse;\n setStatus(data);\n setError(null);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n void fetchStatus();\n const interval = setInterval(() => void fetchStatus(), 5000);\n return () => clearInterval(interval);\n }, [fetchStatus]);\n\n function handleConnect(deviceType: FacewearDeviceType): void {\n if (deviceType === \"even-realities\") {\n window.location.assign(\"/apps/smartglasses\");\n return;\n }\n window.open(\"/api/xr/connect\", \"_blank\", \"noopener,noreferrer\");\n }\n\n const activeCount = status.devices.length;\n\n const { ref: xrConnectRef, agentProps: xrConnectAgentProps } =\n useAgentElement<HTMLAnchorElement>({\n id: \"link-xr-connect\",\n role: \"link\",\n label: \"Connect\",\n group: \"quick-actions\",\n description: \"Open the XR device connect page in a new tab\",\n });\n const { ref: xrStatusRef, agentProps: xrStatusAgentProps } =\n useAgentElement<HTMLAnchorElement>({\n id: \"link-xr-status\",\n role: \"link\",\n label: \"Status\",\n group: \"quick-actions\",\n description: \"Open the XR status API in a new tab\",\n });\n const { ref: refreshRef, agentProps: refreshAgentProps } =\n useAgentElement<HTMLButtonElement>({\n id: \"action-refresh\",\n role: \"button\",\n label: \"Refresh\",\n group: \"quick-actions\",\n description: \"Refresh the connected device status\",\n });\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col overflow-y-auto bg-bg text-txt\">\n <div className=\"px-4 py-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Glasses className=\"h-4 w-4 text-accent\" />\n <h1 className=\"text-sm font-semibold\">Facewear</h1>\n </div>\n </div>\n <span\n className={`inline-flex h-7 items-center gap-1.5 px-1.5 text-xs font-medium ${\n activeCount > 0\n ? \"text-green-700 dark:text-green-300\"\n : \"text-muted\"\n }`}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n {activeCount > 0 ? `${activeCount} on` : \"0 on\"}\n </span>\n </div>\n </div>\n\n <div className=\"mx-auto w-full max-w-3xl p-4\">\n {loading && (\n <div className=\"flex items-center justify-center py-8\">\n <div className=\"h-6 w-6 animate-spin rounded-full border-2 border-border border-t-accent\" />\n </div>\n )}\n\n {error && (\n <div className=\"mb-4 px-1 py-2 text-xs text-destructive\">{error}</div>\n )}\n\n {!loading && (\n <>\n {activeCount > 0 && (\n <div className=\"mb-3\">\n <div className=\"flex flex-wrap gap-2\">\n {status.devices\n .slice(0, ACTIVE_DEVICE_LIMIT)\n .map((device) => (\n <span\n key={device.id}\n className=\"inline-flex items-center gap-1 px-1.5 py-0.5 text-xs text-green-700 dark:text-green-300\"\n >\n {device.deviceType ?? device.kind}\n </span>\n ))}\n {status.devices.length > ACTIVE_DEVICE_LIMIT ? (\n <span className=\"inline-flex items-center px-1.5 py-0.5 text-xs text-muted\">\n +{status.devices.length - ACTIVE_DEVICE_LIMIT}\n </span>\n ) : null}\n </div>\n </div>\n )}\n\n <div className=\"grid gap-3\">\n {FACEWEAR_DEVICE_PROFILES.map((profile) => (\n <DeviceCard\n key={profile.type}\n profile={profile}\n connectedDevices={status.devices}\n onConnect={handleConnect}\n />\n ))}\n </div>\n\n <div className=\"mt-4\">\n <div className=\"flex flex-wrap gap-2\">\n <a\n ref={xrConnectRef}\n href=\"/api/xr/connect\"\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"XR connect\"\n className=\"inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors\"\n {...xrConnectAgentProps}\n >\n <Zap className=\"h-3.5 w-3.5\" />\n Connect\n </a>\n <a\n ref={xrStatusRef}\n href=\"/api/xr/status\"\n target=\"_blank\"\n rel=\"noreferrer\"\n aria-label=\"XR status\"\n className=\"inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors\"\n {...xrStatusAgentProps}\n >\n Status\n </a>\n <button\n ref={refreshRef}\n type=\"button\"\n onClick={() => void fetchStatus()}\n aria-label=\"Refresh\"\n className=\"inline-flex h-8 items-center gap-1.5 px-3 text-xs font-medium hover:bg-muted/20 transition-colors\"\n {...refreshAgentProps}\n >\n Refresh\n </button>\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\nexport function FacewearTuiView() {\n return (\n <TerminalPluginView\n id=\"facewear\"\n label=\"Facewear TUI\"\n description=\"Status\"\n commands={[]}\n endpoints={[\n \"/api/facewear/status\",\n \"/api/facewear/devices\",\n \"/api/facewear/views\",\n ]}\n />\n );\n}\n\nexport function SmartglassesTuiView() {\n return (\n <TerminalPluginView\n id=\"smartglasses\"\n label=\"Smartglasses TUI\"\n description=\"Status\"\n commands={[]}\n endpoints={[\"/api/facewear/status\", \"/api/facewear/devices\"]}\n />\n );\n}\n"],"mappings":"AAiBW,SAiLD,UAjLC,KA2CD,YA3CC;AAjBX,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,WAAW,SAAS,MAAM,WAAW;AAC9C,SAAS,aAAa,WAAW,gBAAgB;AACjD;AAAA,EAEE;AAAA,EAGA;AAAA,OACK;AAGP,MAAM,sBAAsB;AAE5B,SAAS,eAAe,EAAE,eAAe,GAA+B;AACtE,MAAI,eAAe,YAAY,EAAE,SAAS,WAAW,GAAG;AACtD,WAAO,oBAAC,aAAU,WAAU,WAAU;AAAA,EACxC;AACA,MAAI,eAAe,YAAY,EAAE,SAAS,MAAM,GAAG;AACjD,WAAO,oBAAC,QAAK,WAAU,WAAU;AAAA,EACnC;AACA,SAAO,oBAAC,OAAI,WAAU,WAAU;AAClC;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,cAAc,mBAAmB,SAAS,gBAAgB;AAChE,QAAM,OAAO,QAAQ,eAAe,YAAY,EAAE,SAAS,WAAW,IAClE,YACA;AACJ,QAAM,cAAc,cAChB,UAAU,QAAQ,IAAI,KACtB,WAAW,QAAQ,IAAI;AAC3B,QAAM,EAAE,KAAK,YAAY,YAAY,kBAAkB,IACrD,gBAAmC;AAAA,IACjC,IAAI,UAAU,QAAQ,IAAI;AAAA,IAC1B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,QAAQ,cAAc,WAAW;AAAA,IACjC,aAAa,GAAG,cAAc,WAAW,SAAS,QAAQ,QAAQ,IAAI;AAAA,EACxE,CAAC;AAEH,SACE,qBAAC,SAAI,WAAU,0BACb;AAAA,yBAAC,SAAI,WAAU,0CACb;AAAA,2BAAC,SAAI,WAAU,mCACb;AAAA,4BAAC,SAAI,WAAU,4DACb;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,WAAW,cAAc,mBAAmB,YAAY;AAAA;AAAA,QACrE,GACF;AAAA,QACA,qBAAC,SAAI,WAAU,WACb;AAAA,8BAAC,OAAE,WAAU,2CACV,kBAAQ,MACX;AAAA,UACA,oBAAC,OAAE,WAAU,sBAAsB,kBAAQ,cAAa;AAAA,WAC1D;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,kFACT,cAAc,uCAAuC,YACvD;AAAA,UAEC,wBAAc,OAAO;AAAA;AAAA,MACxB;AAAA,OACF;AAAA,IACA,qBAAC,SAAI,WAAU,gDACb;AAAA,2BAAC,UAAK,WAAU,uDACd;AAAA,4BAAC,kBAAe,gBAAgB,QAAQ,gBAAgB;AAAA,QACvD,QAAQ;AAAA,SACX;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,MAAM,UAAU,QAAQ,IAAI;AAAA,UACrC,cAAY;AAAA,UACZ,WAAU;AAAA,UACT,GAAG;AAAA,UAEH,wBAAc,WAAW;AAAA;AAAA,MAC5B;AAAA,OACF;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB;AAChC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAiC;AAAA,IAC3D,WAAW;AAAA,IACX,SAAS,CAAC;AAAA,EACZ,CAAC;AACD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,QAAM,cAAc,YAAY,YAA2B;AACzD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,sBAAsB;AAC9C,UAAI,IAAI,IAAI;AACV,cAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,kBAAU,IAAI;AACd,iBAAS,IAAI;AAAA,MACf;AAAA,IACF,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC3D,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,SAAK,YAAY;AACjB,UAAM,WAAW,YAAY,MAAM,KAAK,YAAY,GAAG,GAAI;AAC3D,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,WAAW,CAAC;AAEhB,WAAS,cAAc,YAAsC;AAC3D,QAAI,eAAe,kBAAkB;AACnC,aAAO,SAAS,OAAO,oBAAoB;AAC3C;AAAA,IACF;AACA,WAAO,KAAK,mBAAmB,UAAU,qBAAqB;AAAA,EAChE;AAEA,QAAM,cAAc,OAAO,QAAQ;AAEnC,QAAM,EAAE,KAAK,cAAc,YAAY,oBAAoB,IACzD,gBAAmC;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,aAAa;AAAA,EACf,CAAC;AACH,QAAM,EAAE,KAAK,aAAa,YAAY,mBAAmB,IACvD,gBAAmC;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,aAAa;AAAA,EACf,CAAC;AACH,QAAM,EAAE,KAAK,YAAY,YAAY,kBAAkB,IACrD,gBAAmC;AAAA,IACjC,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,aAAa;AAAA,EACf,CAAC;AAEH,SACE,qBAAC,SAAI,WAAU,+DACb;AAAA,wBAAC,SAAI,WAAU,aACb,+BAAC,SAAI,WAAU,qDACb;AAAA,0BAAC,SACC,+BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,WAAQ,WAAU,uBAAsB;AAAA,QACzC,oBAAC,QAAG,WAAU,yBAAwB,sBAAQ;AAAA,SAChD,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,mEACT,cAAc,IACV,uCACA,YACN;AAAA,UAEA;AAAA,gCAAC,OAAI,WAAU,eAAc;AAAA,YAC5B,cAAc,IAAI,GAAG,WAAW,QAAQ;AAAA;AAAA;AAAA,MAC3C;AAAA,OACF,GACF;AAAA,IAEA,qBAAC,SAAI,WAAU,gCACZ;AAAA,iBACC,oBAAC,SAAI,WAAU,yCACb,8BAAC,SAAI,WAAU,4EAA2E,GAC5F;AAAA,MAGD,SACC,oBAAC,SAAI,WAAU,2CAA2C,iBAAM;AAAA,MAGjE,CAAC,WACA,iCACG;AAAA,sBAAc,KACb,oBAAC,SAAI,WAAU,QACb,+BAAC,SAAI,WAAU,wBACZ;AAAA,iBAAO,QACL,MAAM,GAAG,mBAAmB,EAC5B,IAAI,CAAC,WACJ;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cAET,iBAAO,cAAc,OAAO;AAAA;AAAA,YAHxB,OAAO;AAAA,UAId,CACD;AAAA,UACF,OAAO,QAAQ,SAAS,sBACvB,qBAAC,UAAK,WAAU,6DAA4D;AAAA;AAAA,YACxE,OAAO,QAAQ,SAAS;AAAA,aAC5B,IACE;AAAA,WACN,GACF;AAAA,QAGF,oBAAC,SAAI,WAAU,cACZ,mCAAyB,IAAI,CAAC,YAC7B;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,kBAAkB,OAAO;AAAA,YACzB,WAAW;AAAA;AAAA,UAHN,QAAQ;AAAA,QAIf,CACD,GACH;AAAA,QAEA,oBAAC,SAAI,WAAU,QACb,+BAAC,SAAI,WAAU,wBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cACP,KAAI;AAAA,cACJ,cAAW;AAAA,cACX,WAAU;AAAA,cACT,GAAG;AAAA,cAEJ;AAAA,oCAAC,OAAI,WAAU,eAAc;AAAA,gBAAE;AAAA;AAAA;AAAA,UAEjC;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cACP,KAAI;AAAA,cACJ,cAAW;AAAA,cACX,WAAU;AAAA,cACT,GAAG;AAAA,cACL;AAAA;AAAA,UAED;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,SAAS,MAAM,KAAK,YAAY;AAAA,cAChC,cAAW;AAAA,cACX,WAAU;AAAA,cACT,GAAG;AAAA,cACL;AAAA;AAAA,UAED;AAAA,WACF,GACF;AAAA,SACF;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEO,SAAS,kBAAkB;AAChC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAG;AAAA,MACH,OAAM;AAAA,MACN,aAAY;AAAA,MACZ,UAAU,CAAC;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,sBAAsB;AACpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,IAAG;AAAA,MACH,OAAM;AAAA,MACN,aAAY;AAAA,MACZ,UAAU,CAAC;AAAA,MACX,WAAW,CAAC,wBAAwB,uBAAuB;AAAA;AAAA,EAC7D;AAEJ;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type HardwareReport, type SmartglassesBridge } from "./SmartglassesView.helpers.js";
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
__evenBridge?: SmartglassesBridge;
|
|
5
|
+
__mentraBridge?: SmartglassesBridge;
|
|
6
|
+
facewearSmartglassesReport?: HardwareReport;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export declare function SmartglassesView(): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=SmartglassesView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartglassesView.d.ts","sourceRoot":"","sources":["../../src/ui/SmartglassesView.tsx"],"names":[],"mappings":"AAgCA,OAAO,EAIL,KAAK,cAAc,EAWnB,KAAK,kBAAkB,EAMxB,MAAM,+BAA+B,CAAC;AAQvC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,YAAY,CAAC,EAAE,kBAAkB,CAAC;QAClC,cAAc,CAAC,EAAE,kBAAkB,CAAC;QACpC,0BAA0B,CAAC,EAAE,cAAc,CAAC;KAC7C;CACF;AAqFD,wBAAgB,gBAAgB,4CAq8B/B"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { type GlassSide, type SmartglassesAudioEncoding } from "../protocol/smartglasses.js";
|
|
2
|
+
export type LensState = "idle" | "prompting" | "connected" | "failed";
|
|
3
|
+
export type ViewScanDiagnosis = "not_scanned" | "left_lens_missing" | "right_lens_missing" | "whole_headset_seen" | "pairing_failed";
|
|
4
|
+
export type ViewPhysicalBlocker = "not_connected" | "partial_headset" | "in_charging_base" | "wearing_state_missing" | "evidence_missing" | null;
|
|
5
|
+
export interface ReportEvent {
|
|
6
|
+
at: string;
|
|
7
|
+
type: string;
|
|
8
|
+
detail: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ReportWrite {
|
|
11
|
+
at: string;
|
|
12
|
+
side: GlassSide | "both";
|
|
13
|
+
command: string;
|
|
14
|
+
bytes: number;
|
|
15
|
+
hex: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ReportAudio {
|
|
18
|
+
at: string;
|
|
19
|
+
side: GlassSide;
|
|
20
|
+
sampleRate: number;
|
|
21
|
+
encoding: SmartglassesAudioEncoding | null;
|
|
22
|
+
sequence?: number;
|
|
23
|
+
bytes: number;
|
|
24
|
+
}
|
|
25
|
+
export interface HardwareReport {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
generatedAt: string;
|
|
28
|
+
transport: string | null;
|
|
29
|
+
connected: boolean;
|
|
30
|
+
lenses: Record<GlassSide, LensState>;
|
|
31
|
+
scanDiagnosis: ViewScanDiagnosis;
|
|
32
|
+
physicalBlocker: ViewPhysicalBlocker;
|
|
33
|
+
setupHint: string | null;
|
|
34
|
+
nextAction: string | null;
|
|
35
|
+
serialNumber: string | null;
|
|
36
|
+
tests: Record<string, boolean>;
|
|
37
|
+
missingEvidence: string[];
|
|
38
|
+
events: ReportEvent[];
|
|
39
|
+
writes: ReportWrite[];
|
|
40
|
+
audio: ReportAudio[];
|
|
41
|
+
wifi: {
|
|
42
|
+
available: boolean;
|
|
43
|
+
status: string;
|
|
44
|
+
networks: string[];
|
|
45
|
+
};
|
|
46
|
+
headsetState: {
|
|
47
|
+
physical: string | null;
|
|
48
|
+
battery: string | null;
|
|
49
|
+
batteryLevels: Partial<Record<GlassSide, number>>;
|
|
50
|
+
device: string | null;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
type BridgeResult = unknown;
|
|
54
|
+
type BridgeSubscription = undefined | (() => void) | {
|
|
55
|
+
unsubscribe?: () => void;
|
|
56
|
+
off?: () => void;
|
|
57
|
+
remove?: () => void;
|
|
58
|
+
};
|
|
59
|
+
export type SmartglassesBridge = {
|
|
60
|
+
requestWifiScan?: () => Promise<BridgeResult> | BridgeResult;
|
|
61
|
+
requestWifiStatus?: () => Promise<BridgeResult> | BridgeResult;
|
|
62
|
+
requestWifiSetup?: (reason?: string) => Promise<BridgeResult> | BridgeResult;
|
|
63
|
+
setWifiCredentials?: (ssid: string, password: string) => Promise<BridgeResult> | BridgeResult;
|
|
64
|
+
sendWifiCredentials?: (ssid: string, password: string) => Promise<BridgeResult> | BridgeResult;
|
|
65
|
+
audioControl?: (enabled: boolean) => Promise<BridgeResult> | BridgeResult;
|
|
66
|
+
clearDisplay?: () => Promise<BridgeResult> | BridgeResult;
|
|
67
|
+
createStartUpPageContainer?: (container: Record<string, unknown>) => Promise<BridgeResult> | BridgeResult;
|
|
68
|
+
displayText?: (params: Record<string, unknown>) => Promise<BridgeResult> | BridgeResult;
|
|
69
|
+
onEvent?: (callback: (event: unknown) => void) => BridgeSubscription;
|
|
70
|
+
onEvenHubEvent?: (callback: (event: unknown) => void) => BridgeSubscription;
|
|
71
|
+
rebuildPageContainer?: (container: Record<string, unknown>) => Promise<BridgeResult> | BridgeResult;
|
|
72
|
+
sendStartUpPage?: (container: unknown) => Promise<BridgeResult> | BridgeResult;
|
|
73
|
+
setMicState?: (sendPcmData: boolean, sendTranscript: boolean, bypassVad: boolean) => Promise<BridgeResult> | BridgeResult;
|
|
74
|
+
write?: (side: GlassSide, data: Uint8Array) => Promise<BridgeResult> | BridgeResult;
|
|
75
|
+
send?: (side: GlassSide, data: Uint8Array) => Promise<BridgeResult> | BridgeResult;
|
|
76
|
+
rawBridge?: {
|
|
77
|
+
audioControl?: (enabled: boolean) => Promise<BridgeResult> | BridgeResult;
|
|
78
|
+
callEvenApp?: (name: string, payload?: Record<string, unknown>) => Promise<BridgeResult> | BridgeResult;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
export declare function isMicEnableTap(label?: string | null): boolean;
|
|
82
|
+
export declare function isMicDisableTap(label?: string | null): boolean;
|
|
83
|
+
export declare function isCradleOrChargingState(physicalState: string | null, batteryState: string | null): boolean;
|
|
84
|
+
export declare function headsetValidationBlocker(physicalState: string | null, batteryState: string | null): string | null;
|
|
85
|
+
export declare function missingViewEvidence(tests: Record<string, boolean>, lenses: Record<GlassSide, LensState>, physicalState: string | null, batteryState: string | null, events?: ReportEvent[], writes?: ReportWrite[]): string[];
|
|
86
|
+
export declare function viewScanDiagnosis(lenses: Record<GlassSide, LensState>): ViewScanDiagnosis;
|
|
87
|
+
export declare function viewPhysicalBlocker(tests: Record<string, boolean>, lenses: Record<GlassSide, LensState>, physicalState: string | null, batteryState: string | null, events?: ReportEvent[], writes?: ReportWrite[]): ViewPhysicalBlocker;
|
|
88
|
+
export declare function viewSetupHint(blocker: ViewPhysicalBlocker, physicalState: string | null, batteryState: string | null): string | null;
|
|
89
|
+
export declare function viewNextAction(blocker: ViewPhysicalBlocker): string | null;
|
|
90
|
+
export declare function viewCommandName(data: Uint8Array): string;
|
|
91
|
+
export declare function buildViewDisplayPackets(text: string, options?: {
|
|
92
|
+
startSeq?: number;
|
|
93
|
+
mode?: "ai" | "text";
|
|
94
|
+
includeCompletion?: boolean;
|
|
95
|
+
}): {
|
|
96
|
+
packets: Uint8Array[];
|
|
97
|
+
pages: number;
|
|
98
|
+
nextSeq: number;
|
|
99
|
+
};
|
|
100
|
+
export declare function parseWifiNetworks(result: unknown): string[];
|
|
101
|
+
export declare function formatWifiStatus(result: unknown, fallback?: string): string;
|
|
102
|
+
export declare function callWifiBridge(bridge: SmartglassesBridge, command: string, payload?: Record<string, unknown>): Promise<unknown>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=SmartglassesView.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartglassesView.helpers.d.ts","sourceRoot":"","sources":["../../src/ui/SmartglassesView.helpers.ts"],"names":[],"mappings":"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"}
|