@christianriedl/utils 1.0.104 → 1.0.105
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/iOpenAI.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Camera.vue +20 -18
- package/src/components/OpenAI.vue +22 -20
package/dist/iOpenAI.d.ts
CHANGED
|
@@ -6,6 +6,6 @@ export interface ICompleteData {
|
|
|
6
6
|
}
|
|
7
7
|
export interface IOpenAIService extends ISpeechService {
|
|
8
8
|
complete(prompt: string): Promise<ICompleteData>;
|
|
9
|
-
completeImage(prompt: string, url: string): Promise<
|
|
9
|
+
completeImage(prompt: string, url: string): Promise<ICompleteData>;
|
|
10
10
|
completeJson(prompt: string, schema?: object): Promise<object>;
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { onMounted, onUnmounted, ref } from "vue";
|
|
3
3
|
import { MediaDeviceKind, Resolution } from '@christianriedl/utils';
|
|
4
4
|
|
|
5
|
-
const props = withDefaults(defineProps<{
|
|
6
|
-
|
|
7
|
-
{ resolution: { width: 1920, height: 1080}, facingMode: "environment", autoplay: true, playsinline: true });
|
|
5
|
+
const props = withDefaults(defineProps<{resolution: Resolution; facingMode?: string; autoplay?: boolean, playsinline?: boolean, constraints?: any}>(),
|
|
6
|
+
{ facingMode: "environment", autoplay: true, playsinline: true });
|
|
8
7
|
const emits = defineEmits(["loading", "started", "stopped", "paused", "resumed", "camera-change", "snapshot", "error" ]);
|
|
9
8
|
|
|
9
|
+
const resolution = props.resolution ? props.resolution : { width: 1920, height: 1080};
|
|
10
|
+
|
|
10
11
|
onMounted(() => {
|
|
11
12
|
if (!navigator.mediaDevices)
|
|
12
13
|
throw new Error("Media devices not available");
|
|
@@ -28,8 +29,8 @@
|
|
|
28
29
|
|
|
29
30
|
const constraints = props.constraints || {
|
|
30
31
|
video: {
|
|
31
|
-
width:
|
|
32
|
-
height:
|
|
32
|
+
width: resolution.width,
|
|
33
|
+
height: resolution.height,
|
|
33
34
|
facingMode: props.facingMode,
|
|
34
35
|
deviceId: {},
|
|
35
36
|
},
|
|
@@ -56,9 +57,7 @@
|
|
|
56
57
|
emits("loading");
|
|
57
58
|
|
|
58
59
|
try {
|
|
59
|
-
stream.value = await navigator.mediaDevices.getUserMedia(
|
|
60
|
-
constraints
|
|
61
|
-
);
|
|
60
|
+
stream.value = await navigator.mediaDevices.getUserMedia(constraints);
|
|
62
61
|
|
|
63
62
|
if (!video.value) throw new Error("Video ref is null");
|
|
64
63
|
|
|
@@ -70,11 +69,11 @@
|
|
|
70
69
|
}
|
|
71
70
|
};
|
|
72
71
|
|
|
73
|
-
function snapshot (
|
|
72
|
+
function snapshot (res: Resolution = resolution, type = "image/png", quality?: number): Promise<string | undefined> {
|
|
74
73
|
if (!video.value) throw new Error("Video ref is null");
|
|
75
74
|
if (!canvas.value) throw new Error("Canvas ref is null");
|
|
76
75
|
|
|
77
|
-
const { width, height } =
|
|
76
|
+
const { width, height } = res;
|
|
78
77
|
|
|
79
78
|
canvas.value.width = width;
|
|
80
79
|
canvas.value.height = height;
|
|
@@ -82,14 +81,9 @@
|
|
|
82
81
|
canvas.value.getContext("2d")?.drawImage(video.value, 0, 0, width, height);
|
|
83
82
|
|
|
84
83
|
return new Promise((resolve) => {
|
|
85
|
-
canvas.value?.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
resolve(blob);
|
|
89
|
-
},
|
|
90
|
-
type,
|
|
91
|
-
quality
|
|
92
|
-
);
|
|
84
|
+
const data = canvas.value?.toDataURL(type, quality);
|
|
85
|
+
emits("snapshot", data);
|
|
86
|
+
resolve(data);
|
|
93
87
|
});
|
|
94
88
|
};
|
|
95
89
|
|
|
@@ -114,6 +108,14 @@
|
|
|
114
108
|
stream.value?.getTracks().forEach((track) => track.stop());
|
|
115
109
|
emits("stopped");
|
|
116
110
|
};
|
|
111
|
+
defineExpose({
|
|
112
|
+
start,
|
|
113
|
+
stop,
|
|
114
|
+
pause,
|
|
115
|
+
resume,
|
|
116
|
+
snapshot,
|
|
117
|
+
devices
|
|
118
|
+
})
|
|
117
119
|
</script>
|
|
118
120
|
|
|
119
121
|
<template>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { inject, ref, toRef, computed, onUnmounted
|
|
2
|
+
import { inject, ref, toRef, computed, onUnmounted } from 'vue';
|
|
3
3
|
import { appStateSymbol, appConfigSymbol, getOpenAISymbol, IOpenAIService, ICompleteData, ESize } from '@christianriedl/utils';
|
|
4
4
|
import Microphone from '@christianriedl/utils/src/components/Microphone.vue';
|
|
5
5
|
import Camera from '@christianriedl/utils/src/components/Camera.vue';
|
|
@@ -22,8 +22,15 @@
|
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
async function onComplete() {
|
|
25
|
+
let answer: ICompleteData;
|
|
25
26
|
replyLines.value = [ "WAITING ..." ];
|
|
26
|
-
|
|
27
|
+
if (isCamera.value && camera.value) {
|
|
28
|
+
const dataurl = await camera.value.snapshot();
|
|
29
|
+
answer = await openAI.completeImage(question.value, dataurl!);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
answer = await openAI.complete(question.value);
|
|
33
|
+
}
|
|
27
34
|
if (answer && answer.choices && answer.choices.length > 0) {
|
|
28
35
|
replyLines.value = answer.choices[0].split('\n');
|
|
29
36
|
}
|
|
@@ -36,25 +43,14 @@
|
|
|
36
43
|
}
|
|
37
44
|
function onCamera() {
|
|
38
45
|
isCamera.value = !isCamera.value;
|
|
46
|
+
if (isCamera.value && !question.value) {
|
|
47
|
+
question.value = "Was erkennst du auf dem Bild ?";
|
|
48
|
+
}
|
|
39
49
|
if (!isCamera.value && camera.value) {
|
|
40
50
|
camera.value.stop();
|
|
41
51
|
camera.value = undefined;
|
|
42
52
|
}
|
|
43
53
|
}
|
|
44
|
-
async function onSnapshot() {
|
|
45
|
-
if (camera.value) {
|
|
46
|
-
const blob = await camera.value?.snapshot();
|
|
47
|
-
const url = URL.createObjectURL(blob);
|
|
48
|
-
replyLines.value = [ "WAITING ..." ];
|
|
49
|
-
const answer = await openAI.complete(question.value);
|
|
50
|
-
if (answer && answer.choices && answer.choices.length > 0) {
|
|
51
|
-
replyLines.value = answer.choices[0].split('\n');
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
replyLines.value = [ "NO CORRECT ANSWER" ];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
54
|
</script>
|
|
59
55
|
|
|
60
56
|
<template>
|
|
@@ -66,16 +62,22 @@
|
|
|
66
62
|
<v-col :cols="12 - qcols">
|
|
67
63
|
<v-btn @click="onComplete">AI</v-btn>
|
|
68
64
|
<microphone lang="de" @result="onResult"></microphone>
|
|
69
|
-
<v-btn
|
|
65
|
+
<v-btn variant="tonal" class="bg-office" @click="onCamera">
|
|
70
66
|
<v-icon size="large" icon="$webcam" color="primary"></v-icon>
|
|
71
67
|
</v-btn>
|
|
72
68
|
</v-col>
|
|
73
69
|
</v-row>
|
|
74
70
|
<v-row v-if="isCamera" dense align="center">
|
|
75
|
-
<
|
|
76
|
-
|
|
71
|
+
<v-col cols="6">
|
|
72
|
+
<camera :resolution="{ width: 640, height: 480 }" ref="camera" autoplay></camera>
|
|
73
|
+
</v-col>
|
|
74
|
+
<v-col cols="6">
|
|
75
|
+
<v-row v-for="(line, index) in replyLines" :key=index dense align="center">
|
|
76
|
+
<v-col cols="12">{{line}}</v-col>
|
|
77
|
+
</v-row>
|
|
78
|
+
</v-col>
|
|
77
79
|
</v-row>
|
|
78
|
-
<v-row v-for="(line, index) in replyLines" :key=index dense align="center">
|
|
80
|
+
<v-row v-else v-for="(line, index) in replyLines" :key=index dense align="center">
|
|
79
81
|
<v-col cols="12">{{line}}</v-col>
|
|
80
82
|
</v-row>
|
|
81
83
|
</v-container>
|