(e: React.ChangeEvent<HTMLInputElement>)
| 98 | |
| 99 | // Handle image file selection |
| 100 | const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => { |
| 101 | const file = e.target.files?.[0] |
| 102 | // Always reset the input up front so the same file can be selected |
| 103 | // again even if we bail below. |
| 104 | const resetInput = () => { |
| 105 | e.target.value = '' |
| 106 | } |
| 107 | if (!file) { |
| 108 | resetInput() |
| 109 | return |
| 110 | } |
| 111 | |
| 112 | // Bail if the file has no MIME type — in practice an empty `type` is |
| 113 | // a sign of a corrupt file or a browser that couldn't sniff it, and |
| 114 | // the OpenAI-compatible realtime API requires an explicit mime. |
| 115 | if (!file.type) { |
| 116 | // eslint-disable-next-line no-console |
| 117 | console.error('[realtime] Cannot send image: file has no MIME type', file) |
| 118 | window.alert( |
| 119 | 'Could not determine the image type. Please try a different file.', |
| 120 | ) |
| 121 | resetInput() |
| 122 | return |
| 123 | } |
| 124 | |
| 125 | const reader = new FileReader() |
| 126 | reader.onerror = () => { |
| 127 | // eslint-disable-next-line no-console |
| 128 | console.error('[realtime] FileReader failed', reader.error) |
| 129 | window.alert( |
| 130 | `Failed to read image file: ${reader.error?.message ?? 'Unknown error'}`, |
| 131 | ) |
| 132 | resetInput() |
| 133 | } |
| 134 | reader.onload = () => { |
| 135 | const result = reader.result |
| 136 | // `result` is null on abort/error, and is an ArrayBuffer (not a |
| 137 | // string) if someone changes the readAs* method later. Guard both. |
| 138 | if (result == null || typeof result !== 'string') { |
| 139 | // eslint-disable-next-line no-console |
| 140 | console.error('[realtime] FileReader result was not a string', result) |
| 141 | window.alert('Failed to read image file: unexpected reader output.') |
| 142 | resetInput() |
| 143 | return |
| 144 | } |
| 145 | // Extract base64 data (remove data:image/xxx;base64, prefix). A |
| 146 | // malformed data URL (no comma, or empty payload after the comma) |
| 147 | // means there's nothing sendable — surface it instead of silently |
| 148 | // no-op'ing. |
| 149 | const parts = result.split(',') |
| 150 | const base64 = parts[1] |
| 151 | if (!base64) { |
| 152 | // eslint-disable-next-line no-console |
| 153 | console.error( |
| 154 | '[realtime] FileReader produced a malformed data URL', |
| 155 | result.slice(0, 64), |
| 156 | ) |
| 157 | window.alert('Failed to read image file: malformed image data.') |
nothing calls this directly
no test coverage detected