@websolutespa/bom-mixer-llm 0.0.1
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/CHANGELOG.md +7 -0
- package/README.md +11 -0
- package/dist/cjs/index.css +2264 -0
- package/dist/cjs/index.js +3566 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.css +2264 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/llm/components/Background/background.js +76 -0
- package/dist/esm/llm/components/Background/background.js.map +1 -0
- package/dist/esm/llm/components/BusyIndicator/busyindicator.js +23 -0
- package/dist/esm/llm/components/BusyIndicator/busyindicator.js.map +1 -0
- package/dist/esm/llm/components/Canvas/canvas.js +223 -0
- package/dist/esm/llm/components/Canvas/canvas.js.map +1 -0
- package/dist/esm/llm/components/Chunk/chunk.js +65 -0
- package/dist/esm/llm/components/Chunk/chunk.js.map +1 -0
- package/dist/esm/llm/components/Cta/cta.js +33 -0
- package/dist/esm/llm/components/Cta/cta.js.map +1 -0
- package/dist/esm/llm/components/Event/event.js +90 -0
- package/dist/esm/llm/components/Event/event.js.map +1 -0
- package/dist/esm/llm/components/EventGroup/eventgroup.js +61 -0
- package/dist/esm/llm/components/EventGroup/eventgroup.js.map +1 -0
- package/dist/esm/llm/components/FormRecap/FormRecap.js +240 -0
- package/dist/esm/llm/components/FormRecap/FormRecap.js.map +1 -0
- package/dist/esm/llm/components/FormRecapError/FormRecapError.js +13 -0
- package/dist/esm/llm/components/FormRecapError/FormRecapError.js.map +1 -0
- package/dist/esm/llm/components/FormRecapSuccess/FormRecapSuccess.js +13 -0
- package/dist/esm/llm/components/FormRecapSuccess/FormRecapSuccess.js.map +1 -0
- package/dist/esm/llm/components/FormRequest/FormRequest.js +34 -0
- package/dist/esm/llm/components/FormRequest/FormRequest.js.map +1 -0
- package/dist/esm/llm/components/Header/header.js +51 -0
- package/dist/esm/llm/components/Header/header.js.map +1 -0
- package/dist/esm/llm/components/Intro/intro.js +77 -0
- package/dist/esm/llm/components/Intro/intro.js.map +1 -0
- package/dist/esm/llm/components/Main/main.js +118 -0
- package/dist/esm/llm/components/Main/main.js.map +1 -0
- package/dist/esm/llm/components/Poi/poi.js +78 -0
- package/dist/esm/llm/components/Poi/poi.js.map +1 -0
- package/dist/esm/llm/components/PoiGroup/poigroup.js +14 -0
- package/dist/esm/llm/components/PoiGroup/poigroup.js.map +1 -0
- package/dist/esm/llm/components/Prompt/prompt.js +86 -0
- package/dist/esm/llm/components/Prompt/prompt.js.map +1 -0
- package/dist/esm/llm/components/Prompt/suggestions.js +45 -0
- package/dist/esm/llm/components/Prompt/suggestions.js.map +1 -0
- package/dist/esm/llm/components/QuickLinks/quicklinks.js +66 -0
- package/dist/esm/llm/components/QuickLinks/quicklinks.js.map +1 -0
- package/dist/esm/llm/components/Stream/stream.js +43 -0
- package/dist/esm/llm/components/Stream/stream.js.map +1 -0
- package/dist/esm/llm/components/Text/text.js +34 -0
- package/dist/esm/llm/components/Text/text.js.map +1 -0
- package/dist/esm/llm/components/Thread/thread.js +26 -0
- package/dist/esm/llm/components/Thread/thread.js.map +1 -0
- package/dist/esm/llm/components/Trigger/blob.js +122 -0
- package/dist/esm/llm/components/Trigger/blob.js.map +1 -0
- package/dist/esm/llm/components/Trigger/trigger.js +63 -0
- package/dist/esm/llm/components/Trigger/trigger.js.map +1 -0
- package/dist/esm/llm/components/Tripadvisor/tripadvisor.js +94 -0
- package/dist/esm/llm/components/Tripadvisor/tripadvisor.js.map +1 -0
- package/dist/esm/llm/hooks/useAutosizeTextArea.js +13 -0
- package/dist/esm/llm/hooks/useAutosizeTextArea.js.map +1 -0
- package/dist/esm/llm/llm.js +48 -0
- package/dist/esm/llm/llm.js.map +1 -0
- package/dist/esm/llm/utils.js +15 -0
- package/dist/esm/llm/utils.js.map +1 -0
- package/dist/esm/node_modules/tslib/tslib.es6.js +135 -0
- package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/esm/useLlm/emitter.js +32 -0
- package/dist/esm/useLlm/emitter.js.map +1 -0
- package/dist/esm/useLlm/message.service.js +308 -0
- package/dist/esm/useLlm/message.service.js.map +1 -0
- package/dist/esm/useLlm/mock.js +687 -0
- package/dist/esm/useLlm/mock.js.map +1 -0
- package/dist/esm/useLlm/speech.js +306 -0
- package/dist/esm/useLlm/speech.js.map +1 -0
- package/dist/esm/useLlm/useLlm.js +460 -0
- package/dist/esm/useLlm/useLlm.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/llm/components/Background/background.d.ts +34 -0
- package/dist/types/llm/components/Background/background.d.ts.map +1 -0
- package/dist/types/llm/components/BusyIndicator/busyindicator.d.ts +2 -0
- package/dist/types/llm/components/BusyIndicator/busyindicator.d.ts.map +1 -0
- package/dist/types/llm/components/Canvas/canvas.d.ts +12 -0
- package/dist/types/llm/components/Canvas/canvas.d.ts.map +1 -0
- package/dist/types/llm/components/Chunk/chunk.d.ts +9 -0
- package/dist/types/llm/components/Chunk/chunk.d.ts.map +1 -0
- package/dist/types/llm/components/Cta/cta.d.ts +10 -0
- package/dist/types/llm/components/Cta/cta.d.ts.map +1 -0
- package/dist/types/llm/components/Event/event.d.ts +3 -0
- package/dist/types/llm/components/Event/event.d.ts.map +1 -0
- package/dist/types/llm/components/EventGroup/eventgroup.d.ts +3 -0
- package/dist/types/llm/components/EventGroup/eventgroup.d.ts.map +1 -0
- package/dist/types/llm/components/FormRecap/FormRecap.d.ts +10 -0
- package/dist/types/llm/components/FormRecap/FormRecap.d.ts.map +1 -0
- package/dist/types/llm/components/FormRecapError/FormRecapError.d.ts +2 -0
- package/dist/types/llm/components/FormRecapError/FormRecapError.d.ts.map +1 -0
- package/dist/types/llm/components/FormRecapSuccess/FormRecapSuccess.d.ts +2 -0
- package/dist/types/llm/components/FormRecapSuccess/FormRecapSuccess.d.ts.map +1 -0
- package/dist/types/llm/components/FormRequest/FormRequest.d.ts +4 -0
- package/dist/types/llm/components/FormRequest/FormRequest.d.ts.map +1 -0
- package/dist/types/llm/components/Header/header.d.ts +4 -0
- package/dist/types/llm/components/Header/header.d.ts.map +1 -0
- package/dist/types/llm/components/Intro/intro.d.ts +2 -0
- package/dist/types/llm/components/Intro/intro.d.ts.map +1 -0
- package/dist/types/llm/components/Main/main.d.ts +2 -0
- package/dist/types/llm/components/Main/main.d.ts.map +1 -0
- package/dist/types/llm/components/Poi/poi.d.ts +3 -0
- package/dist/types/llm/components/Poi/poi.d.ts.map +1 -0
- package/dist/types/llm/components/PoiGroup/poigroup.d.ts +3 -0
- package/dist/types/llm/components/PoiGroup/poigroup.d.ts.map +1 -0
- package/dist/types/llm/components/Prompt/prompt.d.ts +2 -0
- package/dist/types/llm/components/Prompt/prompt.d.ts.map +1 -0
- package/dist/types/llm/components/Prompt/suggestions.d.ts +2 -0
- package/dist/types/llm/components/Prompt/suggestions.d.ts.map +1 -0
- package/dist/types/llm/components/QuickLinks/quicklinks.d.ts +3 -0
- package/dist/types/llm/components/QuickLinks/quicklinks.d.ts.map +1 -0
- package/dist/types/llm/components/Stream/stream.d.ts +4 -0
- package/dist/types/llm/components/Stream/stream.d.ts.map +1 -0
- package/dist/types/llm/components/Text/text.d.ts +7 -0
- package/dist/types/llm/components/Text/text.d.ts.map +1 -0
- package/dist/types/llm/components/Thread/thread.d.ts +4 -0
- package/dist/types/llm/components/Thread/thread.d.ts.map +1 -0
- package/dist/types/llm/components/Trigger/blob.d.ts +2 -0
- package/dist/types/llm/components/Trigger/blob.d.ts.map +1 -0
- package/dist/types/llm/components/Trigger/trigger.d.ts +2 -0
- package/dist/types/llm/components/Trigger/trigger.d.ts.map +1 -0
- package/dist/types/llm/components/Tripadvisor/tripadvisor.d.ts +4 -0
- package/dist/types/llm/components/Tripadvisor/tripadvisor.d.ts.map +1 -0
- package/dist/types/llm/hooks/useAutosizeTextArea.d.ts +3 -0
- package/dist/types/llm/hooks/useAutosizeTextArea.d.ts.map +1 -0
- package/dist/types/llm/llm.d.ts +5 -0
- package/dist/types/llm/llm.d.ts.map +1 -0
- package/dist/types/llm/utils.d.ts +7 -0
- package/dist/types/llm/utils.d.ts.map +1 -0
- package/dist/types/types.d.ts +163 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/useLlm/cors.d.ts +16 -0
- package/dist/types/useLlm/cors.d.ts.map +1 -0
- package/dist/types/useLlm/emitter.d.ts +8 -0
- package/dist/types/useLlm/emitter.d.ts.map +1 -0
- package/dist/types/useLlm/message.service.d.ts +24 -0
- package/dist/types/useLlm/message.service.d.ts.map +1 -0
- package/dist/types/useLlm/mock.d.ts +5 -0
- package/dist/types/useLlm/mock.d.ts.map +1 -0
- package/dist/types/useLlm/speech.d.ts +57 -0
- package/dist/types/useLlm/speech.d.ts.map +1 -0
- package/dist/types/useLlm/useLlm.d.ts +83 -0
- package/dist/types/useLlm/useLlm.d.ts.map +1 -0
- package/package.json +86 -0
- package/src/__tests/example.test.ts +8 -0
- package/src/index.ts +6 -0
- package/src/llm/components/Background/_style.scss +24 -0
- package/src/llm/components/Background/background.tsx +89 -0
- package/src/llm/components/BusyIndicator/_style.scss +73 -0
- package/src/llm/components/BusyIndicator/busyindicator.tsx +13 -0
- package/src/llm/components/Canvas/_style.scss +10 -0
- package/src/llm/components/Canvas/canvas.tsx +257 -0
- package/src/llm/components/Chunk/chunk.tsx +80 -0
- package/src/llm/components/Cta/_style.scss +72 -0
- package/src/llm/components/Cta/cta.tsx +33 -0
- package/src/llm/components/Event/_style.scss +144 -0
- package/src/llm/components/Event/event.tsx +70 -0
- package/src/llm/components/EventGroup/_style.scss +60 -0
- package/src/llm/components/EventGroup/eventgroup.tsx +59 -0
- package/src/llm/components/FormRecap/FormRecap.tsx +221 -0
- package/src/llm/components/FormRecap/_style.scss +101 -0
- package/src/llm/components/FormRecapError/FormRecapError.tsx +10 -0
- package/src/llm/components/FormRecapError/_style.scss +11 -0
- package/src/llm/components/FormRecapSuccess/FormRecapSuccess.tsx +10 -0
- package/src/llm/components/FormRecapSuccess/_style.scss +11 -0
- package/src/llm/components/FormRequest/FormRequest.tsx +21 -0
- package/src/llm/components/FormRequest/_style.scss +21 -0
- package/src/llm/components/Header/_style.scss +72 -0
- package/src/llm/components/Header/header.tsx +38 -0
- package/src/llm/components/Intro/_style.scss +36 -0
- package/src/llm/components/Intro/intro.tsx +65 -0
- package/src/llm/components/Main/_style.scss +75 -0
- package/src/llm/components/Main/main.tsx +125 -0
- package/src/llm/components/Poi/_style.scss +127 -0
- package/src/llm/components/Poi/poi.tsx +58 -0
- package/src/llm/components/PoiGroup/_style.scss +14 -0
- package/src/llm/components/PoiGroup/poigroup.tsx +15 -0
- package/src/llm/components/Prompt/_style.scss +132 -0
- package/src/llm/components/Prompt/prompt.tsx +73 -0
- package/src/llm/components/Prompt/suggestions.tsx +39 -0
- package/src/llm/components/QuickLinks/_style.scss +61 -0
- package/src/llm/components/QuickLinks/quicklinks.tsx +58 -0
- package/src/llm/components/Stream/stream.tsx +37 -0
- package/src/llm/components/Text/_style.scss +89 -0
- package/src/llm/components/Text/text.tsx +28 -0
- package/src/llm/components/Thread/_style.scss +73 -0
- package/src/llm/components/Thread/thread.tsx +27 -0
- package/src/llm/components/Trigger/_style.scss +104 -0
- package/src/llm/components/Trigger/blob.tsx +42 -0
- package/src/llm/components/Trigger/trigger.tsx +50 -0
- package/src/llm/components/Tripadvisor/_style.scss +113 -0
- package/src/llm/components/Tripadvisor/tripadvisor.tsx +72 -0
- package/src/llm/hooks/useAutosizeTextArea.tsx +15 -0
- package/src/llm/llm.tsx +48 -0
- package/src/llm/scss/_base.scss +20 -0
- package/src/llm/scss/_breakpoints.scss +8 -0
- package/src/llm/scss/_layout.scss +39 -0
- package/src/llm/scss/_mixin.scss +67 -0
- package/src/llm/scss/_reset.scss +104 -0
- package/src/llm/scss/_spacing.scss +17 -0
- package/src/llm/scss/_typography.scss +153 -0
- package/src/llm/scss/_vars.scss +23 -0
- package/src/llm/scss/style.scss +29 -0
- package/src/llm/utils.tsx +12 -0
- package/src/types.ts +215 -0
- package/src/useLlm/cors.ts +124 -0
- package/src/useLlm/emitter.ts +29 -0
- package/src/useLlm/message.service.ts +336 -0
- package/src/useLlm/mock.ts +801 -0
- package/src/useLlm/speech.ts +363 -0
- package/src/useLlm/useLlm.tsx +474 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { isBrowser } from '@websolutespa/bom-core';
|
|
2
|
+
import ArrayBuffer from 'nanogl/arraybuffer';
|
|
3
|
+
import Program from 'nanogl/program';
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
5
|
+
|
|
6
|
+
const vertex = `attribute vec2 aPosition;
|
|
7
|
+
attribute vec2 aTexCoord;
|
|
8
|
+
varying vec2 vUv;
|
|
9
|
+
|
|
10
|
+
void main(void){
|
|
11
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
12
|
+
vUv = aTexCoord;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const fragment = `
|
|
17
|
+
#ifdef GL_ES
|
|
18
|
+
precision mediump float;
|
|
19
|
+
#endif
|
|
20
|
+
|
|
21
|
+
varying vec2 vUv;
|
|
22
|
+
|
|
23
|
+
uniform int uColor1;
|
|
24
|
+
uniform int uColor2;
|
|
25
|
+
uniform int uColor3;
|
|
26
|
+
uniform int uColor4;
|
|
27
|
+
uniform float uBlobSize;
|
|
28
|
+
uniform float uCellSize;
|
|
29
|
+
uniform float uNoiseSize;
|
|
30
|
+
uniform float uOpacity;
|
|
31
|
+
uniform float uTime;
|
|
32
|
+
|
|
33
|
+
vec2 random2(vec2 st) {
|
|
34
|
+
st = vec2(dot(st, vec2(127.1, 311.7)), dot(st, vec2(269.5, 183.3)));
|
|
35
|
+
return - 1.0 + 2.0 * fract(sin(st) * 43758.5453123);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
float noise(vec2 st) {
|
|
39
|
+
vec2 i = floor(st);
|
|
40
|
+
vec2 f = fract(st);
|
|
41
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
42
|
+
return mix(mix(dot(random2(i + vec2(0.0, 0.0)), f - vec2(0.0, 0.0)),
|
|
43
|
+
dot(random2(i + vec2(1.0, 0.0)), f - vec2(1.0, 0.0)), u.x),
|
|
44
|
+
mix(dot(random2(i + vec2(0.0, 1.0)), f - vec2(0.0, 1.0)),
|
|
45
|
+
dot(random2(i + vec2(1.0, 1.0)), f - vec2(1.0, 1.0)), u.x), u.y) * 0.5 + 0.5;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
float gaussFunction(vec2 st, vec2 p, float r) {
|
|
49
|
+
return exp(-dot(st - p, st - p) / 2.0 / r / r);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
vec3 hash32(vec2 p) {
|
|
53
|
+
vec3 p3 = fract(vec3(p.xyx) * vec3(0.1031, 0.1030, 0.0973));
|
|
54
|
+
p3 += dot(p3, p3.yxz + 33.33);
|
|
55
|
+
return fract((p3.xxy + p3.yzz) * p3.zyx);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
vec3 blob(vec2 p) {
|
|
59
|
+
vec2 i = floor(p / uCellSize);
|
|
60
|
+
vec3 c = vec3(0.0);
|
|
61
|
+
for(int x = -1; x <= 1; x ++ )
|
|
62
|
+
for(int y = -1; y <= 1; y ++ ) {
|
|
63
|
+
vec2 v = i + vec2(x, y);
|
|
64
|
+
vec3 h = hash32(v);
|
|
65
|
+
float g = gaussFunction(p / uCellSize, v + h.xy, uBlobSize);
|
|
66
|
+
float n = smoothstep(0.0, 1.0, noise(uNoiseSize * p / uCellSize / uBlobSize));
|
|
67
|
+
c += uOpacity * g * n;
|
|
68
|
+
}
|
|
69
|
+
return c;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
float random(vec2 st) {
|
|
73
|
+
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
vec3 intToColor(int color) {
|
|
77
|
+
float r = float(color / 256 / 256);
|
|
78
|
+
float g = float(color / 256 - int(r * 256.0));
|
|
79
|
+
float b = float(color - int(r * 256.0 * 256.0) - int(g * 256.0));
|
|
80
|
+
return vec3(r / 255.0, g / 255.0, b / 255.0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void main(void) {
|
|
84
|
+
vec2 st = vUv;
|
|
85
|
+
|
|
86
|
+
vec2 p1 = st + vec2(cos(uTime * 0.1), sin(uTime * 0.1));
|
|
87
|
+
vec3 b1 = blob(p1);
|
|
88
|
+
|
|
89
|
+
vec2 p2 = st + 3.43 + vec2(sin(uTime * 0.2), cos(uTime * 0.2));
|
|
90
|
+
vec3 b2 = blob(p2);
|
|
91
|
+
|
|
92
|
+
vec2 p3 = st + 5.43 + vec2(cos(uTime * 0.2), cos(uTime * 0.2));
|
|
93
|
+
vec3 b3 = blob(p3);
|
|
94
|
+
|
|
95
|
+
vec3 mask = (1.0 - b1 - b2 - b3);
|
|
96
|
+
|
|
97
|
+
vec3 c1 = intToColor(uColor1);
|
|
98
|
+
vec3 c2 = intToColor(uColor2);
|
|
99
|
+
vec3 c3 = intToColor(uColor3);
|
|
100
|
+
vec3 c4 = intToColor(uColor4);
|
|
101
|
+
|
|
102
|
+
vec3 color = mask * c1;
|
|
103
|
+
|
|
104
|
+
color += b1 * c2;
|
|
105
|
+
|
|
106
|
+
color += b2 * c3;
|
|
107
|
+
|
|
108
|
+
color += b3 * c4;
|
|
109
|
+
|
|
110
|
+
vec2 n = random2(st) * 0.05;
|
|
111
|
+
color += n.x - n.y;
|
|
112
|
+
|
|
113
|
+
gl_FragColor = vec4(color, 1.0);
|
|
114
|
+
}
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
const shader = {
|
|
118
|
+
vertex,
|
|
119
|
+
fragment,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type ShaderProps = {
|
|
123
|
+
color1?: string;
|
|
124
|
+
color2?: string;
|
|
125
|
+
color3?: string;
|
|
126
|
+
color4?: string;
|
|
127
|
+
blobSize?: number;
|
|
128
|
+
cellSize?: number;
|
|
129
|
+
noiseSize?: number;
|
|
130
|
+
opacity?: number;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const initWebGl = (
|
|
134
|
+
canvas: HTMLCanvasElement,
|
|
135
|
+
{
|
|
136
|
+
color1 = '#003c8c',
|
|
137
|
+
color2 = '#73c3ff',
|
|
138
|
+
color3 = '#96dca0',
|
|
139
|
+
color4 = '#dcdc3c',
|
|
140
|
+
blobSize = 0.3,
|
|
141
|
+
cellSize = 0.6,
|
|
142
|
+
noiseSize = 0.3,
|
|
143
|
+
opacity = 0.5,
|
|
144
|
+
}: ShaderProps
|
|
145
|
+
) => {
|
|
146
|
+
const gl = canvas.getContext('webgl');
|
|
147
|
+
if (!gl) {
|
|
148
|
+
throw 'cannot initialize gl';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const pixelRatio = window.devicePixelRatio;
|
|
152
|
+
const size = {
|
|
153
|
+
width: 1,
|
|
154
|
+
height: 1,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const shouldRenderSingleFrame = isApple();
|
|
158
|
+
let canRender = true;
|
|
159
|
+
let isDirty = true;
|
|
160
|
+
let rafID: number | null = null;
|
|
161
|
+
|
|
162
|
+
const onResize = ([entry]: ResizeObserverEntry[]) => {
|
|
163
|
+
const canvasWidth = entry.contentRect.width;
|
|
164
|
+
const canvasHeight = entry.contentRect.height;
|
|
165
|
+
canvas.width = Math.round(canvasWidth * pixelRatio);
|
|
166
|
+
canvas.height = Math.round(canvasHeight * pixelRatio);
|
|
167
|
+
size.width = gl.drawingBufferWidth;
|
|
168
|
+
size.height = gl.drawingBufferHeight;
|
|
169
|
+
isDirty = true;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const resizeObserver = new ResizeObserver(onResize);
|
|
173
|
+
resizeObserver.observe(canvas);
|
|
174
|
+
|
|
175
|
+
const quadData = new Float32Array([-1, 3, 0, 2, -1, -1, 0, 0, 3, -1, 2, 0]);
|
|
176
|
+
const quad = new ArrayBuffer(gl, quadData);
|
|
177
|
+
quad.attrib('aPosition', 2, gl.FLOAT);
|
|
178
|
+
quad.attrib('aTexCoord', 2, gl.FLOAT);
|
|
179
|
+
|
|
180
|
+
const program = new Program(gl, shader.vertex, shader.fragment);
|
|
181
|
+
|
|
182
|
+
const render = (time = 0) => {
|
|
183
|
+
if (!canRender) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!isDirty) {
|
|
188
|
+
rafID = window.requestAnimationFrame(render);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
gl.viewport(0, 0, size.width, size.height);
|
|
193
|
+
gl.clearColor(0, 0, 0, 0);
|
|
194
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
195
|
+
|
|
196
|
+
program.use();
|
|
197
|
+
if (time === 0) {
|
|
198
|
+
program.uColor1(parseInt(color1.replace('#', ''), 16));
|
|
199
|
+
program.uColor2(parseInt(color2.replace('#', ''), 16));
|
|
200
|
+
program.uColor3(parseInt(color3.replace('#', ''), 16));
|
|
201
|
+
program.uColor4(parseInt(color4.replace('#', ''), 16));
|
|
202
|
+
program.uBlobSize(blobSize);
|
|
203
|
+
program.uCellSize(cellSize);
|
|
204
|
+
program.uNoiseSize(noiseSize);
|
|
205
|
+
program.uOpacity(opacity);
|
|
206
|
+
}
|
|
207
|
+
program.uTime(time * 0.001);
|
|
208
|
+
|
|
209
|
+
quad.attribPointer(program);
|
|
210
|
+
quad.drawTriangles();
|
|
211
|
+
|
|
212
|
+
isDirty = !shouldRenderSingleFrame;
|
|
213
|
+
|
|
214
|
+
rafID = window.requestAnimationFrame(render);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
setTimeout(render, 0);
|
|
218
|
+
|
|
219
|
+
const dispose = () => {
|
|
220
|
+
canRender = false;
|
|
221
|
+
if (rafID) {
|
|
222
|
+
window.cancelAnimationFrame(rafID);
|
|
223
|
+
}
|
|
224
|
+
resizeObserver.disconnect();
|
|
225
|
+
program.dispose();
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return dispose;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export function Canvas() {
|
|
232
|
+
const ref = useRef<HTMLCanvasElement>(null);
|
|
233
|
+
|
|
234
|
+
useEffect(() => {
|
|
235
|
+
if (isBrowser && ref.current) {
|
|
236
|
+
const dispose = initWebGl(ref.current, {});
|
|
237
|
+
return () => dispose();
|
|
238
|
+
} else {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
}, []);
|
|
242
|
+
|
|
243
|
+
return <canvas className="llm__canvas" ref={ref} />;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function isApple() {
|
|
247
|
+
return [
|
|
248
|
+
'iPad Simulator',
|
|
249
|
+
'iPhone Simulator',
|
|
250
|
+
'iPod Simulator',
|
|
251
|
+
'iPad',
|
|
252
|
+
'iPhone',
|
|
253
|
+
'iPod',
|
|
254
|
+
].includes(navigator.platform) ||
|
|
255
|
+
(navigator.platform.indexOf('Mac') === 0) ||
|
|
256
|
+
(navigator.userAgent.includes('Mac'));
|
|
257
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { LlmChunkItem } from '../../../types';
|
|
3
|
+
import { Event } from '../Event/event';
|
|
4
|
+
import { EventGroup } from '../EventGroup/eventgroup';
|
|
5
|
+
import { FormRecap } from '../FormRecap/FormRecap';
|
|
6
|
+
import { FormRecapError } from '../FormRecapError/FormRecapError';
|
|
7
|
+
import { FormRecapSuccess } from '../FormRecapSuccess/FormRecapSuccess';
|
|
8
|
+
import { FormRequest } from '../FormRequest/FormRequest';
|
|
9
|
+
import { Poi } from '../Poi/poi';
|
|
10
|
+
import { PoiGroup } from '../PoiGroup/poigroup';
|
|
11
|
+
import { Text } from '../Text/text';
|
|
12
|
+
import { TripAdvisorGroup } from '../Tripadvisor/tripadvisor';
|
|
13
|
+
|
|
14
|
+
export type ChunkProps = {
|
|
15
|
+
item: LlmChunkItem;
|
|
16
|
+
index: number;
|
|
17
|
+
total: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Chunk: React.FC<ChunkProps> = ({
|
|
21
|
+
item,
|
|
22
|
+
index,
|
|
23
|
+
total,
|
|
24
|
+
}: ChunkProps) => {
|
|
25
|
+
switch (item.type) {
|
|
26
|
+
case 'string': {
|
|
27
|
+
return item.content.length > 0 && (
|
|
28
|
+
<Text body={item.content} disclaimer={index === 0} />
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
case 'event':
|
|
32
|
+
case 'eventItem': {
|
|
33
|
+
return (
|
|
34
|
+
<Event {...item} />
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
case 'eventGroup':
|
|
38
|
+
return (
|
|
39
|
+
<EventGroup {...item} />
|
|
40
|
+
);
|
|
41
|
+
case 'poi':
|
|
42
|
+
case 'poiItem': {
|
|
43
|
+
return (
|
|
44
|
+
<Poi {...item} />
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
case 'poiGroup':
|
|
48
|
+
return (
|
|
49
|
+
<PoiGroup {...item} />
|
|
50
|
+
);
|
|
51
|
+
case 'tripadvisorGroup':
|
|
52
|
+
return (
|
|
53
|
+
<TripAdvisorGroup {...item} />
|
|
54
|
+
);
|
|
55
|
+
case 'formRequest':
|
|
56
|
+
return (
|
|
57
|
+
<FormRequest last={index === total - 1} />
|
|
58
|
+
);
|
|
59
|
+
case 'formRecap':
|
|
60
|
+
return (
|
|
61
|
+
<FormRecap />
|
|
62
|
+
);
|
|
63
|
+
case 'formRecapSuccess':
|
|
64
|
+
return (
|
|
65
|
+
<FormRecapSuccess />
|
|
66
|
+
);
|
|
67
|
+
case 'formRecapError':
|
|
68
|
+
return (
|
|
69
|
+
<FormRecapError />
|
|
70
|
+
);
|
|
71
|
+
case 'image':
|
|
72
|
+
return (
|
|
73
|
+
<img src={item.src as string} />
|
|
74
|
+
);
|
|
75
|
+
default:
|
|
76
|
+
return (
|
|
77
|
+
<code>{JSON.stringify(item)}</code>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.llm {
|
|
2
|
+
& &__cta {
|
|
3
|
+
@include text(cta-4);
|
|
4
|
+
position: relative;
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
gap: spacing(6xs);
|
|
8
|
+
color: var(--llm-color-ui-white);
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
white-space: nowrap;
|
|
11
|
+
transition: opacity 0.3s;
|
|
12
|
+
|
|
13
|
+
@include hover {
|
|
14
|
+
text-decoration: underline;
|
|
15
|
+
opacity: 0.7;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
& .llm__cta--button,
|
|
20
|
+
& .llm__cta--submit {
|
|
21
|
+
@include text(cta-3);
|
|
22
|
+
background: var(--llm-color-ui-2);
|
|
23
|
+
color: var(--llm-color-ui-1);
|
|
24
|
+
height: 42px;
|
|
25
|
+
font-style: normal;
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 0 spacing(xs);
|
|
28
|
+
border-radius: 50px;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
text-decoration: none;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
|
|
33
|
+
.llm__background {
|
|
34
|
+
z-index: 1;
|
|
35
|
+
background: none;
|
|
36
|
+
|
|
37
|
+
.llm__background-noise {
|
|
38
|
+
display: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.llm__background-shade {
|
|
42
|
+
opacity: 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
>span {
|
|
47
|
+
z-index: 2;
|
|
48
|
+
position: relative;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
& .llm__cta--simple {
|
|
53
|
+
@include text(cta-3);
|
|
54
|
+
padding: 0 spacing(xs);
|
|
55
|
+
height: 42px;
|
|
56
|
+
background: linear-gradient(rgba(154, 154, 154, 0.2) 10%,
|
|
57
|
+
rgba(221, 221, 221, 0.1) 80%);
|
|
58
|
+
border: 1px solid rgba(237, 237, 237, 0.3);
|
|
59
|
+
backdrop-filter: blur(20px);
|
|
60
|
+
color: var(--llm-color-ui-white);
|
|
61
|
+
font-style: normal;
|
|
62
|
+
border-radius: 12px;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
|
|
67
|
+
>span {
|
|
68
|
+
z-index: 2;
|
|
69
|
+
position: relative;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getClassNames } from '@websolutespa/bom-core';
|
|
2
|
+
import { IconLlmArrowRight } from '@websolutespa/bom-mixer-icons';
|
|
3
|
+
import { Background } from '../Background/background';
|
|
4
|
+
|
|
5
|
+
export type LlmCtaType = 'button' | 'text' | 'simple';
|
|
6
|
+
|
|
7
|
+
export type LlmCta = {
|
|
8
|
+
type: string;
|
|
9
|
+
label: string;
|
|
10
|
+
target?: '_blank' | '_self';
|
|
11
|
+
url?: string;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Cta = (props: LlmCta) => {
|
|
16
|
+
const onClick = () => {
|
|
17
|
+
if (typeof props.onClick === 'function') {
|
|
18
|
+
props.onClick();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const bg = props.type == 'button' || props.type == 'submit' ? <Background /> : '';
|
|
22
|
+
const icon = props.type == 'text' ? <IconLlmArrowRight /> : '';
|
|
23
|
+
const classNames = getClassNames('llm__cta', `llm__cta--${props.type}`);
|
|
24
|
+
return props.url ? (
|
|
25
|
+
<a className={classNames} href={props.url} target={props.target}>
|
|
26
|
+
{bg}<span>{props.label}</span>{icon}
|
|
27
|
+
</a>
|
|
28
|
+
) : (
|
|
29
|
+
<button className={classNames} onClick={() => onClick()} type={props.type == 'button' || props.type == 'submit' ? props.type : undefined}>
|
|
30
|
+
{bg}<span>{props.label}</span>{icon}
|
|
31
|
+
</button>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
.llm {
|
|
2
|
+
& &__event-inner {
|
|
3
|
+
border-radius: 5px;
|
|
4
|
+
background: rgba(0, 60, 140, 0.8);
|
|
5
|
+
box-shadow: -1px 1px 30px 0px rgba(0, 0, 0, 0.2);
|
|
6
|
+
height: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
flex-direction: column-reverse;
|
|
10
|
+
justify-content: flex-end;
|
|
11
|
+
|
|
12
|
+
@include bp(lg) {
|
|
13
|
+
padding-bottom: spacing(2xs);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
& &__event-media {
|
|
18
|
+
position: relative;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
& &__event-asset {
|
|
22
|
+
@include imgPad(2, 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
& &__event-content {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
padding: spacing(2xs);
|
|
29
|
+
gap: spacing(4xs);
|
|
30
|
+
height: 100%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
& &__event-pretitle {
|
|
34
|
+
@include text(label-1);
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
& &__event-title {
|
|
39
|
+
@include text(heading-1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
& &__event-body {
|
|
43
|
+
@include text(body-5);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
& &__event-location {
|
|
47
|
+
@include text(cta-2);
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: spacing(6xs);
|
|
51
|
+
padding-top: spacing(4xs);
|
|
52
|
+
|
|
53
|
+
svg {
|
|
54
|
+
color: var(--llm-color-ui-4);
|
|
55
|
+
width: 20px;
|
|
56
|
+
height: 20px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
& &__event-ctas {
|
|
61
|
+
display: flex;
|
|
62
|
+
gap: spacing(3xs);
|
|
63
|
+
padding-top: spacing(5xs);
|
|
64
|
+
margin-top: auto;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
& &__event-tags {
|
|
68
|
+
@include fill();
|
|
69
|
+
padding: spacing(5xs);
|
|
70
|
+
display: flex;
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
align-items: flex-start;
|
|
73
|
+
gap: 4px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
& &__event-tag {
|
|
77
|
+
padding: spacing(6xs);
|
|
78
|
+
border-radius: 2px;
|
|
79
|
+
@include text(label-1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
& &__event-time {
|
|
83
|
+
background: var(--llm-color-ui-7);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
& &__event-date {
|
|
87
|
+
background: var(--llm-color-ui-1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
& &__event--event {
|
|
91
|
+
margin-top: spacing(xl);
|
|
92
|
+
|
|
93
|
+
.llm {
|
|
94
|
+
&__event-inner {
|
|
95
|
+
@include bp(sm) {
|
|
96
|
+
flex-direction: row;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@include bp(lg) {
|
|
100
|
+
background: none;
|
|
101
|
+
box-shadow: none;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
&__event-content {
|
|
106
|
+
@include bp(sm) {
|
|
107
|
+
width: 50%;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@include bp(lg) {
|
|
111
|
+
padding: spacing(2xs) spacing(m);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&__event-title {
|
|
116
|
+
@include bp(lg) {
|
|
117
|
+
@include text(display-1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&__event-body {
|
|
122
|
+
@include bp(lg) {
|
|
123
|
+
@include text(body-1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
&__event-media {
|
|
128
|
+
@include bp(sm) {
|
|
129
|
+
width: 50%;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&__event-asset {
|
|
134
|
+
@include bp(sm) {
|
|
135
|
+
height: 100%;
|
|
136
|
+
|
|
137
|
+
&:after {
|
|
138
|
+
display: none;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getClassNames, isBrowser } from '@websolutespa/bom-core';
|
|
2
|
+
import { useLabel } from '@websolutespa/bom-mixer-hooks';
|
|
3
|
+
import { IconLlmPin } from '@websolutespa/bom-mixer-icons';
|
|
4
|
+
import { LlmChunkEvent, LlmChunkEventItem } from '../../../types';
|
|
5
|
+
import { useLlm } from '../../../useLlm/useLlm';
|
|
6
|
+
import { Cta } from '../Cta/cta';
|
|
7
|
+
|
|
8
|
+
export const Event = (props: LlmChunkEventItem | LlmChunkEvent) => {
|
|
9
|
+
const label = useLabel();
|
|
10
|
+
const { send, decorateUrl } = useLlm(state => state.actions);
|
|
11
|
+
const onTellMeMore = async () => {
|
|
12
|
+
const prompt = `${label('llm.tellMeMoreAbout')} ${props.title}`;
|
|
13
|
+
await send(prompt, (response) => {
|
|
14
|
+
// console.log('Prompt.onChunk', response.chunks);
|
|
15
|
+
}, (response) => {
|
|
16
|
+
// console.log('Prompt.onEnd', response);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const onGoToWebsite = async () => {
|
|
20
|
+
if (isBrowser) {
|
|
21
|
+
const url = await decorateUrl(props, props.url);
|
|
22
|
+
window.open(url, '_blank');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const classNames = getClassNames('llm__event', { [`llm__event--${props.type}`]: !!props.type });
|
|
26
|
+
return (
|
|
27
|
+
<div className={classNames}>
|
|
28
|
+
<div className="llm__event-inner">
|
|
29
|
+
<div className="llm__event-content">
|
|
30
|
+
{props.type == 'event' && <div className="llm__event-pretitle">
|
|
31
|
+
{label('llm.eventPretitle')}
|
|
32
|
+
</div>}
|
|
33
|
+
{props.title && (
|
|
34
|
+
<div className="llm__event-title" dangerouslySetInnerHTML={{ __html: props.title }} />
|
|
35
|
+
)}
|
|
36
|
+
{props.abstract && (
|
|
37
|
+
<div className="llm__event-body" dangerouslySetInnerHTML={{ __html: props.abstract }} />
|
|
38
|
+
)}
|
|
39
|
+
{props.place && (
|
|
40
|
+
<div className="llm__event-location">
|
|
41
|
+
<IconLlmPin /> {props.place}
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
<div className="llm__event-ctas">
|
|
45
|
+
<Cta type="button" label={label('llm.tellMeMore')} onClick={onTellMeMore} />
|
|
46
|
+
<Cta type="text" label={label('llm.goToWebsite')} onClick={onGoToWebsite} />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
{props.image && (
|
|
50
|
+
<div className="llm__event-media">
|
|
51
|
+
<div className="llm__event-asset">
|
|
52
|
+
<img src={props.image} alt={props.title} />
|
|
53
|
+
</div>
|
|
54
|
+
{(props.time || props.date) && (
|
|
55
|
+
<div className="llm__event-tags">
|
|
56
|
+
{props.date && (
|
|
57
|
+
<div className="llm__event-tag llm__event-date">{props.date}</div>
|
|
58
|
+
)}
|
|
59
|
+
{props.time && (
|
|
60
|
+
<div className="llm__event-tag llm__event-time">{props.time}</div>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
.llm {
|
|
2
|
+
& &__events {
|
|
3
|
+
width: 100%;
|
|
4
|
+
|
|
5
|
+
.swiper {
|
|
6
|
+
position: relative;
|
|
7
|
+
overflow: visible;
|
|
8
|
+
padding-top: spacing(2xl);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.swiper-slide {
|
|
12
|
+
height: auto;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.llm__event-inner {
|
|
16
|
+
padding-bottom: 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
& &__events-slide {
|
|
21
|
+
height: auto;
|
|
22
|
+
|
|
23
|
+
.llm__event {
|
|
24
|
+
height: 100%;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
& &__events-nav {
|
|
29
|
+
position: absolute;
|
|
30
|
+
top: 0;
|
|
31
|
+
left: 0;
|
|
32
|
+
color: var(--llm-color-ui-white);
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: spacing(4xs);
|
|
35
|
+
|
|
36
|
+
@include bp(md) {
|
|
37
|
+
right: 0;
|
|
38
|
+
left: auto;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
button {
|
|
42
|
+
color: currentColor;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
|
|
45
|
+
&.swiper-button-disabled {
|
|
46
|
+
opacity: 0.2;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.swiper-button-lock {
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
svg {
|
|
55
|
+
width: 24px;
|
|
56
|
+
height: 24px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|