@@ -111,7 +111,9 @@ onMounted(async () => {
111
111
}
112
112
},
113
113
})
114
- selectedCamera .value && qrScanner .setCamera (selectedCamera .value )
114
+ selectedCamera .value && setTimeout (() => {
115
+ qrScanner ! .setCamera (selectedCamera .value ! )
116
+ })
115
117
qrScanner .setInversionMode (' both' )
116
118
qrScanner .start ()
117
119
updateCameraStatus ()
@@ -184,6 +186,7 @@ const receivedBytes = computed(() => decoderStatus.value.encodedCount * (decoder
184
186
const filename = ref <string | undefined >()
185
187
const contentType = ref <string | undefined >()
186
188
const textContent = ref <string | undefined >()
189
+ const dataType = ref <' file' | ' link' >(' file' )
187
190
188
191
const bytesFormatted = useKiloBytesNumberFormat (computed (() => (bytes .value / 1024 ).toFixed (2 )))
189
192
const receivedBytesFormatted = useKiloBytesNumberFormat (computed (() => (receivedBytes .value / 1024 ).toFixed (2 )))
@@ -295,38 +298,24 @@ async function scanFrame(result: QrScanner.ScanResult) {
295
298
contentType .value = meta .contentType
296
299
297
300
if (contentType .value .startsWith (' text/' )) {
298
- textContent . value = new TextDecoder ().decode (mergedData )
301
+ const text = new TextDecoder ().decode (mergedData )
299
302
303
+ textContent .value = text
300
304
// auto open if it's a URL
301
- if (/ ^ https? :\/\/ / .test (textContent .value )) {
302
- window .open (textContent .value , ' _blank' )
305
+ if (/ ^ https? :\/\/ / .test (text )) {
306
+ dataType .value = ' link'
307
+
308
+ setTimeout (() => {
309
+ try {
310
+ window .open (text , ' _blank' )
311
+ }
312
+ catch (e ) {
313
+ console .error (e )
314
+ }
315
+ }, 250 )
303
316
}
304
317
}
305
318
}
306
- // console.log({ data })
307
- // if (Array.isArray(data)) {
308
- // if (data[0] !== id.value) {
309
- // chunks.length = 0
310
- // dataUrl.value = undefined
311
- // }
312
-
313
- //
314
-
315
- // chunks[data[2]] = data
316
- // pluse(data[2])
317
-
318
- // if (!length.value)
319
- // return
320
- // if (picked.value.every(i => !!i)) {
321
- // try {
322
- // const merged = merge(picked.value as SliceData[])
323
- // dataUrl.value = URL.createObjectURL(new Blob([merged], { type: 'application/octet-stream' }))
324
- // }
325
- // catch (e) {
326
- // error.value = e
327
- // }
328
- // }
329
- // }
330
319
}
331
320
332
321
useUnsavedChange (() => {
@@ -344,20 +333,85 @@ function now() {
344
333
<div items-left flex flex-col gap-4 >
345
334
<pre v-if =" error" overflow-x-auto text-red v-text =" error" />
346
335
347
- <div w-full flex flex-wrap gap-2 >
348
- <button
349
- v-for =" item of cameras" :key =" item.deviceId" :class =" {
350
- 'text-blue': selectedCamera === item.deviceId,
351
- }"
352
- px2 py1 text-sm shadow-sm
353
- border =" ~ gray/25 rounded-lg"
354
- @click =" selectedCamera = item.deviceId"
355
- >
356
- {{ item.label }}
357
- </button >
336
+ <Collapsable label =" Cameras" :default =" true" >
337
+ <div w-full flex flex-wrap gap-2 p2 >
338
+ <button
339
+ v-for =" (item, index) of cameras" :key =" item.deviceId" :class =" {
340
+ 'text-blue bg-blue/20': selectedCamera === item.deviceId,
341
+ }"
342
+ px2 py1 text-sm shadow-sm
343
+ border =" ~ gray/25 rounded-lg"
344
+ @click =" selectedCamera = item.deviceId"
345
+ >
346
+ <span i-carbon-camera mr-1 inline-block align-text-top />
347
+ {{ item.label || `Camera ${index + 1}` }}
348
+ </button >
349
+ </div >
350
+ </Collapsable >
351
+
352
+ <Collapsable v-if =" dataUrl" label =" Result" :default =" true" >
353
+ <div flex =" ~ col gap-2" relative >
354
+ <div flex =" ~ col gap-2" p2 >
355
+ <div v-if =" dataType === 'link'" :src =" dataUrl" break-words text-wrap text-blue underline op80 hover:op100 >
356
+ <a :href =" textContent" target =" _blank" rel =" noopener noreferrer" >{{ textContent }}</a >
357
+ </div >
358
+ <img v-else-if =" contentType?.startsWith('image/')" :src =" dataUrl" >
359
+ <video v-else-if =" contentType?.startsWith('video/')" controls autoplay muted >
360
+ <source :src =" dataUrl" :type =" contentType" >
361
+ </video >
362
+ <div v-else-if =" contentType?.startsWith('text/')" :src =" dataUrl" break-words text-wrap >
363
+ {{ textContent }}
364
+ </div >
365
+ </div >
366
+ <div sticky bottom-0 p4 shadow backdrop-blur-xl >
367
+ <a
368
+ v-if =" dataType === 'file'"
369
+ :href =" dataUrl"
370
+ :download =" filename"
371
+ class =" block w-full rounded-md bg-white px2 py1 text-center text-sm dark:bg-neutral-8"
372
+ border =" ~ gray/25 hover:gray:10" shadow =" ~ gray/25"
373
+ >
374
+ Download as file
375
+ </a >
376
+ <a
377
+ v-else-if =" dataType === 'link'"
378
+ :href =" textContent"
379
+ target =" _blank"
380
+ rel =" noopener noreferrer"
381
+ class =" block w-full rounded-md bg-white px2 py1 text-center text-sm dark:bg-neutral-8"
382
+ border =" ~ gray/25 hover:gray:10" shadow =" ~ gray/25"
383
+ >
384
+ Open as link
385
+ </a >
386
+ </div >
387
+ </div >
388
+ </Collapsable >
389
+
390
+ <!-- This is a progress bar that is not accurate but feels comfortable. -->
391
+ <div v-if =" k" relative h-4 rounded bg-black:75 text-white font-mono shadow >
392
+ <div
393
+ bg =" green-400" border =" ~ green4 rounded" transition =" all ease" absolute inset-y-0 h-full w-full duration-1000
394
+ :style =" { maxWidth: `${decodedBlocks === k ? 100 : (Math.min(1, receivedBytes / bytes * 0.66) * 100).toFixed(2)}%` }"
395
+ />
358
396
</div >
359
397
360
- <Collapsable >
398
+ <Camera
399
+ :k =" k"
400
+ :fps =" fps"
401
+ :bytes =" bytes"
402
+ :received-bytes =" receivedBytes"
403
+ :current-bytes =" currentBytesFormatted"
404
+ :current-valid-bytes-speed =" currentValidBytesSpeedFormatted"
405
+ :camera-signal-status =" cameraSignalStatus"
406
+ >
407
+ <video
408
+ ref =" video"
409
+ :controls =" false"
410
+ autoplay muted playsinline h-full w-full rounded-lg
411
+ />
412
+ </Camera >
413
+
414
+ <Collapsable label =" Inspect" >
361
415
<div grid-cols =" [150px_1fr]" font =" mono!" :class =" endTime ? 'text-green-500' : ''" grid gap-x-4 gap-y-2 overflow-x-auto whitespace-nowrap p2 text-sm >
362
416
<span text-neutral-500 >Filename</span >
363
417
<span text-right md:text-left >{{ filename || '<unknown >' }}</span >
@@ -382,7 +436,7 @@ function now() {
382
436
</div >
383
437
</Collapsable >
384
438
385
- <Collapsable v-if =" k" :default = " k < 500 " >
439
+ <Collapsable v-if =" k" >
386
440
<template #label >
387
441
<span >Packets</span >
388
442
<span ml-2 text-neutral-400 >({{ k }})</span >
@@ -406,51 +460,8 @@ function now() {
406
460
</div >
407
461
</Collapsable >
408
462
409
- <Collapsable v-if =" dataUrl" label =" Download" :default =" true" >
410
- <div flex =" ~ col gap-2" p2 >
411
- <img v-if =" contentType?.startsWith('image/')" :src =" dataUrl" >
412
- <video v-else-if =" contentType?.startsWith('video/')" controls autoplay muted >
413
- <source :src =" dataUrl" :type =" contentType" >
414
- </video >
415
- <p v-else-if =" contentType?.startsWith('text/')" :src =" dataUrl" >
416
- {{ textContent }}
417
- </p >
418
- <a
419
- :href =" dataUrl"
420
- :download =" filename"
421
- class =" w-max border border-gray:50 rounded-md px2 py1 text-sm hover:bg-gray:10"
422
- >
423
- Download
424
- </a >
425
- </div >
426
- </Collapsable >
427
-
428
- <!-- This is a progress bar that is not accurate but feels comfortable. -->
429
- <div v-if =" k" relative h-4 rounded bg-black:75 text-white font-mono shadow >
430
- <div
431
- bg =" green-400" border =" ~ green4 rounded" transition =" all ease" absolute inset-y-0 h-full w-full duration-1000
432
- :style =" { maxWidth: `${decodedBlocks === k ? 100 : (Math.min(1, receivedBytes / bytes * 0.66) * 100).toFixed(2)}%` }"
433
- />
434
- </div >
435
-
436
- <Camera
437
- :k =" k"
438
- :fps =" fps"
439
- :bytes =" bytes"
440
- :received-bytes =" receivedBytes"
441
- :current-bytes =" currentBytesFormatted"
442
- :current-valid-bytes-speed =" currentValidBytesSpeedFormatted"
443
- :camera-signal-status =" cameraSignalStatus"
444
- >
445
- <video
446
- ref =" video"
447
- :controls =" false"
448
- autoplay muted playsinline h-full w-full rounded-lg
449
- />
450
- </Camera >
451
-
452
- <Collapsable label =" Blocks" >
453
- <div flex =" ~ gap-1 wrap" max-w-150 text-xs >
463
+ <Collapsable v-if =" k" label =" Blocks" >
464
+ <div flex =" ~ gap-1 wrap" max-w-150 p2 text-xs >
454
465
<div v-for =" i, idx of decoderStatus.encodedBlocks" :key =" idx" border =" ~ gray/10 rounded" p1 >
455
466
<template v-for =" x , idy of i .indices " :key =" x " >
456
467
<span v-if =" idy !== 0" op25 >, </span >
0 commit comments