@springbrand/message-panel 0.1.3-alpha.9 → 0.2.0-alpha.38

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.
Files changed (67) hide show
  1. package/cloud-os/README.md +4 -3
  2. package/cloud-os/assets/followup-arrow.svg +3 -0
  3. package/cloud-os/assets/loading-corner.svg +3 -0
  4. package/cloud-os/assets/loading-mark.svg +16 -0
  5. package/cloud-os/assets/loading-spark.svg +3 -0
  6. package/cloud-os/assets/model-selected.svg +5 -0
  7. package/cloud-os/assets/thinking-star.svg +9 -0
  8. package/cloud-os/capability-chip.tsx +1 -1
  9. package/cloud-os/chat/cloud-os-chat-messages.tsx +441 -98
  10. package/cloud-os/chat/code-runner.tsx +106 -0
  11. package/cloud-os/chat/image-generation.tsx +76 -0
  12. package/cloud-os/chat/loading-state.tsx +25 -0
  13. package/cloud-os/chat/markdown-message.tsx +144 -41
  14. package/cloud-os/chat/range.ts +8 -0
  15. package/cloud-os/chat/rich-blocks.tsx +285 -227
  16. package/cloud-os/chat/surfaces.tsx +90 -0
  17. package/cloud-os/chat/tool-call.tsx +94 -0
  18. package/cloud-os/chat/tool-presentation.ts +31 -22
  19. package/cloud-os/chat/tool-rows.tsx +195 -143
  20. package/cloud-os/chat/tool-timeline.tsx +123 -0
  21. package/cloud-os/chat/transcript-model.ts +258 -20
  22. package/cloud-os/chat/web-search.tsx +135 -0
  23. package/cloud-os/composer/cloud-os-chat-input.tsx +29 -88
  24. package/cloud-os/composer/cloud-os-model-select.tsx +105 -0
  25. package/cloud-os/file-view.tsx +195 -12
  26. package/cloud-os/index.ts +24 -3
  27. package/cloud-os/layout/cloud-os-workspace-split.tsx +60 -3
  28. package/cloud-os/primitives/collapsible.tsx +21 -0
  29. package/cloud-os/primitives/workshop-controls.tsx +2 -2
  30. package/cloud-os/styles/cloud-os.css +114 -1
  31. package/demo/camel-chat-showcase.tsx +21 -13
  32. package/demo/chat-scenarios.ts +214 -40
  33. package/demo/cloud-os-chat-showcase.tsx +37 -14
  34. package/demo/fixtures.ts +4 -5
  35. package/demo/message-panel-gallery.tsx +16 -2
  36. package/package.json +8 -4
  37. package/springbrand-pet/LICENSE.bloub +21 -0
  38. package/springbrand-pet/bot/cycles.test.ts +221 -0
  39. package/springbrand-pet/bot/cycles.ts +225 -0
  40. package/springbrand-pet/bot/decor.ts +285 -0
  41. package/springbrand-pet/bot/engine.test.ts +543 -0
  42. package/springbrand-pet/bot/engine.ts +558 -0
  43. package/springbrand-pet/bot/expressions.test.ts +135 -0
  44. package/springbrand-pet/bot/expressions.ts +192 -0
  45. package/springbrand-pet/bot/eyefit.ts +455 -0
  46. package/springbrand-pet/bot/face.test.ts +101 -0
  47. package/springbrand-pet/bot/face.ts +179 -0
  48. package/springbrand-pet/bot/math.ts +42 -0
  49. package/springbrand-pet/bot/profiles.ts +22 -0
  50. package/springbrand-pet/bot/repere.ts +31 -0
  51. package/springbrand-pet/bot/shape.test.ts +97 -0
  52. package/springbrand-pet/bot/shape.ts +310 -0
  53. package/springbrand-pet/bot/skins.test.ts +325 -0
  54. package/springbrand-pet/bot/skins.ts +161 -0
  55. package/springbrand-pet/bot/states.ts +616 -0
  56. package/springbrand-pet/drag.test.ts +19 -0
  57. package/springbrand-pet/index.tsx +569 -0
  58. package/springbrand-pet/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  59. package/springbrand-pet/vitest.config.ts +9 -0
  60. package/src/camel/camel-chat-messages.tsx +78 -78
  61. package/src/camel/camel-tool-presentation.tsx +19 -15
  62. package/src/camel/camel-turn.ts +1 -17
  63. package/src/composer/composer-attachments.tsx +1 -1
  64. package/src/composer/composer.tsx +2 -2
  65. package/src/contracts.ts +0 -2
  66. package/src/message-panel.tsx +1 -24
  67. package/src/parts/index.tsx +103 -64
@@ -0,0 +1,285 @@
1
+ import { TAU, clamp, createRng, r2 } from './math'
2
+
3
+ /* ------------------------------------------------------------------ couleurs */
4
+
5
+ /**
6
+ * Les anneaux ne sont pas des couleurs plates : la video montre une roue de
7
+ * teintes complete a luminosite constante, avec un degrade le long de chaque
8
+ * trace. Mesure : S 45-62 %, L 50-67 %.
9
+ */
10
+ function wheel(hue: number, s = 0.55, l = 0.62): string {
11
+ const h = ((hue % 360) + 360) % 360
12
+ const c = (1 - Math.abs(2 * l - 1)) * s
13
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1))
14
+ const m = l - c / 2
15
+ const [r, g, b] =
16
+ h < 60
17
+ ? [c, x, 0]
18
+ : h < 120
19
+ ? [x, c, 0]
20
+ : h < 180
21
+ ? [0, c, x]
22
+ : h < 240
23
+ ? [0, x, c]
24
+ : h < 300
25
+ ? [x, 0, c]
26
+ : [c, 0, x]
27
+ const hex = (v: number) =>
28
+ Math.round((v + m) * 255)
29
+ .toString(16)
30
+ .padStart(2, '0')
31
+ return `#${hex(r)}${hex(g)}${hex(b)}`
32
+ }
33
+
34
+ /* ------------------------------------------------------------- types de rendu */
35
+
36
+ export interface DotRender {
37
+ x: number
38
+ y: number
39
+ r: number
40
+ opacity: number
41
+ /** couleur explicite ; par defaut le rendu prend celle du corps */
42
+ color?: string
43
+ /**
44
+ * Brume de profondeur : 0 = fondu dans le fond, 1 = couleur du corps pleine.
45
+ * Le melange se fait au rendu, qui seul connait la couleur choisie.
46
+ */
47
+ depth?: number
48
+ /**
49
+ * Forme non circulaire, en unites de rayon de boule et centree sur l'origine
50
+ * (le point du "!" penche est une goutte, pas un disque). Quand elle est
51
+ * fournie, `r` n'est plus utilise pour le trace.
52
+ */
53
+ d?: string
54
+ /** rotation appliquee a `d`, en degres */
55
+ rot?: number
56
+ }
57
+
58
+ /**
59
+ * Ce qu'un etat declare : la geometrie de l'arc reste en unites de rayon de
60
+ * boule, c'est le moteur (seul a connaitre l'echelle du viewBox) qui la
61
+ * rasterise. Sans ca les etats devraient connaitre le viewBox.
62
+ */
63
+ export interface ArcSpec {
64
+ id: string
65
+ seed: ArcSeed
66
+ t: number
67
+ opacity: number
68
+ }
69
+
70
+ export interface ArcRender {
71
+ id: string
72
+ /** portion devant le corps */
73
+ front: string
74
+ /** portion derriere le corps (dessinee avant, donc masquee par la silhouette) */
75
+ back: string
76
+ width: number
77
+ opacity: number
78
+ /** degrade de teinte le long du trace */
79
+ grad: { x1: number; y1: number; x2: number; y2: number; stops: string[] }
80
+ }
81
+
82
+ /* --------------------------------------------------------- arc elliptique 3D */
83
+
84
+ export interface ArcSeed {
85
+ /** demi-grand axe, en unites de rayon de boule */
86
+ a: number
87
+ /** aplatissement b/a : mesure <= 0.45, les plans d'orbite sont vus par la tranche */
88
+ k: number
89
+ /** inclinaison du grand axe a l'ecran, radians */
90
+ tilt: number
91
+ /** tours par seconde */
92
+ speed: number
93
+ phase: number
94
+ /** fraction du tour reellement tracee */
95
+ sweep: number
96
+ hue: number
97
+ hueSpan: number
98
+ width: number
99
+ cx: number
100
+ cy: number
101
+ }
102
+
103
+ /**
104
+ * Projette un cercle 3D incline en orthographique.
105
+ *
106
+ * Le cercle vit dans le plan engendre par u (dans l'ecran) et v (qui plonge
107
+ * dans la profondeur). La composante z sert a couper l'arc en deux : la moitie
108
+ * arriere est dessinee avant le corps, donc occultee par lui. C'est ce vrai tri
109
+ * en profondeur qui fait lire les anneaux comme des orbites et pas comme un
110
+ * dessin plat.
111
+ */
112
+ export function arcRender(seed: ArcSeed, t: number, scale: number, id: string, opacity = 1): ArcRender {
113
+ const spin = seed.phase + t * seed.speed * TAU
114
+ const cu = Math.cos(seed.tilt)
115
+ const su = Math.sin(seed.tilt)
116
+ const kz = Math.sqrt(Math.max(0, 1 - seed.k * seed.k))
117
+
118
+ const N = 64
119
+ const span = seed.sweep * TAU
120
+ let front = ''
121
+ let back = ''
122
+ let prev: boolean | null = null
123
+
124
+ for (let i = 0; i <= N; i++) {
125
+ const th = spin + (i / N) * span
126
+ const ct = Math.cos(th)
127
+ const st = Math.sin(th)
128
+ // u = (cos tilt, sin tilt, 0) ; v = (-sin tilt * k, cos tilt * k, kz)
129
+ const x = seed.a * (ct * cu + st * -su * seed.k) + seed.cx
130
+ const y = seed.a * (ct * su + st * cu * seed.k) + seed.cy
131
+ const z = seed.a * st * kz
132
+
133
+ const behind = z < 0
134
+ const sx = r2(x * scale)
135
+ const sy = r2(y * scale)
136
+ const cmd = behind !== prev ? 'M' : 'L'
137
+ if (behind) back += `${cmd}${sx} ${sy}`
138
+ else front += `${cmd}${sx} ${sy}`
139
+ prev = behind
140
+ }
141
+
142
+ const gx = Math.cos(seed.tilt) * seed.a * scale
143
+ const gy = Math.sin(seed.tilt) * seed.a * scale
144
+ return {
145
+ id,
146
+ front,
147
+ back,
148
+ width: seed.width * scale,
149
+ opacity,
150
+ grad: {
151
+ x1: r2(seed.cx * scale - gx),
152
+ y1: r2(seed.cy * scale - gy),
153
+ x2: r2(seed.cx * scale + gx),
154
+ y2: r2(seed.cy * scale + gy),
155
+ stops: [wheel(seed.hue), wheel(seed.hue + seed.hueSpan * 0.5), wheel(seed.hue + seed.hueSpan)]
156
+ }
157
+ }
158
+ }
159
+
160
+ /* ---------------------------------------------------------------- anneaux */
161
+
162
+ const RING_RNG = createRng(0xa11ce)
163
+
164
+ /**
165
+ * 6 anneaux, demi-grand axe 1.30-1.40 (donc nettement plus grands que la
166
+ * boule), aplatissement toujours <= 0.45, epaisseur 0.055, ~3.3 tours/s.
167
+ */
168
+ export const RINGS: ArcSeed[] = Array.from({ length: 6 }, (_, i) => ({
169
+ a: 1.3 + RING_RNG() * 0.1,
170
+ k: 0.05 + RING_RNG() * 0.4,
171
+ tilt: (i / 6) * Math.PI + RING_RNG() * 0.5,
172
+ speed: 3 + RING_RNG() * 0.7,
173
+ phase: RING_RNG() * TAU,
174
+ sweep: 0.6 + RING_RNG() * 0.25,
175
+ hue: (i * 360) / 6 + RING_RNG() * 30,
176
+ hueSpan: 60 + RING_RNG() * 60,
177
+ width: 0.05 + RING_RNG() * 0.012,
178
+ cx: 0,
179
+ cy: 0.1
180
+ }))
181
+
182
+ /**
183
+ * Bouquet d'arcs emboites qui balaie le triangle juste avant les orbites.
184
+ * Vus quasiment par la tranche (d'ou la forme en epingle a cheveux), rmax 1.37.
185
+ */
186
+ export const SWOOSH: ArcSeed[] = Array.from({ length: 4 }, (_, i) => ({
187
+ a: 0.78 + i * 0.2,
188
+ k: 0.05 + i * 0.02,
189
+ tilt: -0.62 + i * 0.05,
190
+ speed: 0.3,
191
+ phase: 0.06 * i,
192
+ sweep: 0.4,
193
+ hue: 95 + i * 62,
194
+ hueSpan: 100,
195
+ width: 0.05,
196
+ cx: 0,
197
+ cy: -0.12
198
+ }))
199
+
200
+ /* ------------------------------------------------------------- 3 points */
201
+
202
+ /** x mesures : -0.557 / -0.013 / +0.532, y = 0. */
203
+ export const DOT_X = [-0.557, -0.013, 0.532] as const
204
+ export const DOT_R = 0.165
205
+ export const DOT_PEAK = 1.25
206
+
207
+ /* ------------------------------------------------------------ particules */
208
+
209
+ const P_RNG = createRng(0xbeef)
210
+
211
+ /** 5 particules, une nouvelle toutes les 0.2 s, duree de vie 0.55 s. */
212
+ const PARTICLES = Array.from({ length: 5 }, (_, i) => ({
213
+ birth: i * 0.2,
214
+ angle: P_RNG() * TAU,
215
+ rho: 0.58 + P_RNG() * 0.18
216
+ }))
217
+
218
+ /**
219
+ * Les particules ne partent pas en ligne droite : elles spiralent vers le
220
+ * centre (rayon x0.75 par frame, angle +100 deg/s) en grossissant, et passent
221
+ * derriere le noyau ou elles sont avalees.
222
+ */
223
+ export function particles(t: number, scale: number): DotRender[] {
224
+ const out: DotRender[] = []
225
+ for (const p of PARTICLES) {
226
+ const u = t - p.birth
227
+ if (u < 0 || u > 0.62) continue
228
+ const rho = p.rho * Math.pow(0.75, u * 10)
229
+ const a = p.angle + (u * 100 * Math.PI) / 180
230
+ out.push({
231
+ x: Math.cos(a) * rho * scale,
232
+ y: Math.sin(a) * rho * scale,
233
+ r: (0.04 + 0.028 * clamp(u / 0.55)) * scale,
234
+ depth: clamp(1 - rho / 0.8),
235
+ opacity: clamp(u / 0.06) * clamp((0.62 - u) / 0.08)
236
+ })
237
+ }
238
+ return out
239
+ }
240
+
241
+ /* ------------------------------------------------------------------ comete */
242
+
243
+ /**
244
+ * Contrairement a l'intuition, le point ne traverse pas l'ecran : il reste au
245
+ * centre et c'est la trainee qui l'orbite. Ellipse a = 0.85, b = 0.15,
246
+ * grand axe incline de +34deg, 4 rubans, ~210 deg/s.
247
+ */
248
+ const COMET_RNG = createRng(0xc0e7)
249
+ export const COMET_RIBBONS: ArcSeed[] = Array.from({ length: 4 }, (_, i) => {
250
+ const d = i - 1.5
251
+ return {
252
+ a: 0.85 * (1 + d * 0.03),
253
+ // meme aplatissement a +-5 % pres : les rubans forment un faisceau serre
254
+ k: (0.15 / 0.85) * (1 + d * 0.16),
255
+ tilt: (34 * Math.PI) / 180 + d * 0.035,
256
+ speed: 210 / 360,
257
+ // dephasage mesure : 10 a 20 degres entre rubans, pas davantage
258
+ phase: -i * 0.045 + COMET_RNG() * 0.012,
259
+ sweep: 0.34,
260
+ hue: i * 85 + COMET_RNG() * 20,
261
+ hueSpan: 80,
262
+ width: 0.095,
263
+ cx: 0,
264
+ cy: 0
265
+ }
266
+ })
267
+
268
+ /** Rayon du point de la comete, mesure a 0.129. */
269
+ export const COMET_DOT = 0.129
270
+
271
+ /* --------------------------------------------------- pastille notification */
272
+
273
+ /** Bleu releve au pixel. */
274
+ export const NOTIF_BLUE = '#2496e8'
275
+ /** La pastille est posee exactement sur la circonference, a -42deg. */
276
+ export const NOTIF_ANGLE = -42
277
+ export const NOTIF_DIST = 1.003
278
+ /** Rayon au repos ; le pop culmine 14 % au-dessus. */
279
+ export const NOTIF_R = 0.15
280
+ export const NOTIF_POP = 1.14
281
+ /**
282
+ * L'encoche est un disque concentrique a la pastille, soustrait du corps.
283
+ * La marge est constante (0.054 R) et suit l'echelle du corps.
284
+ */
285
+ export const NOTIF_MARGIN = 0.054