dsh-codebase-chat 0.16.0

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/lib/client.js ADDED
@@ -0,0 +1,1380 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-codebase-chat",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+
8
+ const React = require("react");
9
+ const { jsx, jsxs } = require("react/jsx-runtime");
10
+
11
+ const NS = "codebase-chat";
12
+ const inject = ["slots"];
13
+ const LOCALE_KEY = "codebase-locale";
14
+
15
+ const UI_LABELS = {
16
+ fr: {
17
+ title: "Codebase Pro",
18
+ badge: "Pro",
19
+ recent: "Récents",
20
+ recentMode: "Mode",
21
+ presets: "Presets",
22
+ presetsHint: "1 clic, réglages auto",
23
+ savePreset: "Sauvegarder le réglage actuel",
24
+ addPreset: "Nom du preset",
25
+ project: "Projet",
26
+ projectPlaceholder: "C:\\MonProjet (laissez vide pour auto-détection)",
27
+ autoDetect: "auto-détection",
28
+ previewLoading: "Aperçu en cours...",
29
+ mode: "Mode d'analyse",
30
+ style: "Style de sortie",
31
+ focus: "Focus (optionnel)",
32
+ focusPlaceholder: "Ex: focus sur le match engine, ou sur l'UI mobile...",
33
+ cancel: "Annuler",
34
+ run: "Lancer l'analyse",
35
+ footer: "Généré avec passion par shinzarou-eng",
36
+ loadingTitle: "Analyse en cours",
37
+ error: "Erreur",
38
+ successTitle: "Analyse lancée",
39
+ successBody: "Le brief a été envoyé. Le rapport apparaîtra dans la session.",
40
+ close: "Fermer",
41
+ sessionRequired: "Sélectionnez une session DSH",
42
+ actionTitle: "Lancer un brief codebase"
43
+ },
44
+ en: {
45
+ title: "Codebase Pro",
46
+ badge: "Pro",
47
+ recent: "Recent",
48
+ recentMode: "Mode",
49
+ presets: "Presets",
50
+ presetsHint: "1 click, auto settings",
51
+ savePreset: "Save current settings",
52
+ addPreset: "Preset name",
53
+ project: "Project",
54
+ projectPlaceholder: "C:\\MyProject (leave empty for auto-detect)",
55
+ autoDetect: "auto-detect",
56
+ previewLoading: "Loading preview...",
57
+ mode: "Analysis mode",
58
+ style: "Output style",
59
+ focus: "Focus (optional)",
60
+ focusPlaceholder: "Ex: focus on the match engine, or the mobile UI...",
61
+ cancel: "Cancel",
62
+ run: "Run analysis",
63
+ footer: "Generated with passion by shinzarou-eng",
64
+ loadingTitle: "Analysis in progress",
65
+ error: "Error",
66
+ successTitle: "Analysis started",
67
+ successBody: "The brief has been sent. The report will appear in the session.",
68
+ close: "Close",
69
+ sessionRequired: "Select a DSH session",
70
+ actionTitle: "Start a codebase brief"
71
+ }
72
+ };
73
+
74
+ function getLabels(locale) {
75
+ return UI_LABELS[locale] || UI_LABELS.fr;
76
+ }
77
+
78
+ const palette = {
79
+ accent: "var(--dsw-alias-accent, #3b82f6)",
80
+ accentGlow: "rgba(59, 130, 246, 0.25)",
81
+ bg: "#16181d",
82
+ card: "#1f2126",
83
+ border: "rgba(255, 255, 255, 0.08)",
84
+ text: "#f3f4f6",
85
+ muted: "#9ca3af",
86
+ success: "#10b981",
87
+ danger: "#ef4444",
88
+ overlay: "rgba(0, 0, 0, 0.6)"
89
+ };
90
+
91
+ const styles = {
92
+ button: {
93
+ display: "inline-flex",
94
+ alignItems: "center",
95
+ justifyContent: "center",
96
+ gap: "8px",
97
+ minWidth: 0,
98
+ height: "28px",
99
+ padding: "0 10px",
100
+ color: palette.text,
101
+ font: "inherit",
102
+ fontSize: "13px",
103
+ fontWeight: 500,
104
+ lineHeight: "20px",
105
+ whiteSpace: "nowrap",
106
+ cursor: "pointer",
107
+ background: "rgba(255,255,255,0.06)",
108
+ border: `1px solid ${palette.border}`,
109
+ borderRadius: "24px"
110
+ },
111
+ buttonDisabled: {
112
+ opacity: 0.45,
113
+ cursor: "not-allowed"
114
+ },
115
+ icon: {
116
+ flex: "none",
117
+ width: "15px",
118
+ height: "15px"
119
+ },
120
+ label: {
121
+ minWidth: 0,
122
+ overflow: "hidden",
123
+ textOverflow: "ellipsis"
124
+ },
125
+ backdrop: {
126
+ position: "fixed",
127
+ inset: 0,
128
+ zIndex: 999999,
129
+ display: "flex",
130
+ alignItems: "center",
131
+ justifyContent: "center",
132
+ background: palette.overlay,
133
+ backdropFilter: "blur(8px)",
134
+ WebkitBackdropFilter: "blur(8px)",
135
+ transition: "opacity 0.2s ease"
136
+ },
137
+ modal: {
138
+ width: "560px",
139
+ maxWidth: "94vw",
140
+ maxHeight: "96vh",
141
+ overflow: "auto",
142
+ padding: "14px",
143
+ borderRadius: "18px",
144
+ background: palette.card,
145
+ border: `1px solid ${palette.border}`,
146
+ boxShadow: "0 24px 80px rgba(0, 0, 0, 0.55)"
147
+ },
148
+ modalHeader: {
149
+ display: "flex",
150
+ alignItems: "center",
151
+ justifyContent: "space-between",
152
+ marginBottom: "14px",
153
+ padding: "8px 10px",
154
+ margin: "-14px -14px 14px -14px",
155
+ borderBottom: `1px solid ${palette.border}`,
156
+ background: `linear-gradient(135deg, ${palette.card} 0%, rgba(86,119,252,0.08) 100%)`
157
+ },
158
+ columns: {
159
+ display: "grid",
160
+ gridTemplateColumns: "1fr 1fr",
161
+ gap: "18px"
162
+ },
163
+ column: {
164
+ display: "flex",
165
+ flexDirection: "column"
166
+ },
167
+ kbdHint: {
168
+ fontSize: "9px",
169
+ color: palette.muted,
170
+ textAlign: "center",
171
+ marginTop: "6px",
172
+ marginBottom: "-4px"
173
+ },
174
+ miniChip: {
175
+ background: "rgba(86,119,252,0.08)",
176
+ border: `1px solid ${palette.border}`,
177
+ color: palette.text,
178
+ fontSize: "9px",
179
+ borderRadius: "999px",
180
+ padding: "2px 8px",
181
+ cursor: "pointer",
182
+ transition: "all 0.12s"
183
+ },
184
+ ghost: {
185
+ background: "transparent",
186
+ border: "none",
187
+ color: palette.accent,
188
+ fontSize: "16px",
189
+ fontWeight: 700,
190
+ cursor: "pointer",
191
+ padding: "0 6px",
192
+ borderRadius: "6px",
193
+ lineHeight: 1
194
+ },
195
+ badge: {
196
+ fontSize: "9px",
197
+ fontWeight: 600,
198
+ textTransform: "uppercase",
199
+ letterSpacing: "0.5px",
200
+ color: "#fff",
201
+ background: palette.accent,
202
+ padding: "2px 6px",
203
+ borderRadius: "4px",
204
+ marginLeft: "6px"
205
+ },
206
+ langSelect: {
207
+ background: palette.bg,
208
+ color: palette.text,
209
+ border: `1px solid ${palette.border}`,
210
+ borderRadius: "6px",
211
+ padding: "2px 6px",
212
+ fontSize: "11px",
213
+ fontWeight: 600,
214
+ cursor: "pointer",
215
+ outline: "none"
216
+ },
217
+ modalTitle: {
218
+ fontSize: "17px",
219
+ fontWeight: 700,
220
+ color: palette.text,
221
+ letterSpacing: "-0.3px",
222
+ display: "flex",
223
+ alignItems: "center",
224
+ gap: "10px"
225
+ },
226
+ modalClose: {
227
+ width: "28px",
228
+ height: "28px",
229
+ display: "flex",
230
+ alignItems: "center",
231
+ justifyContent: "center",
232
+ background: "transparent",
233
+ border: "none",
234
+ borderRadius: "50%",
235
+ color: palette.muted,
236
+ cursor: "pointer",
237
+ transition: "background 0.15s ease"
238
+ },
239
+ section: {
240
+ marginBottom: "10px"
241
+ },
242
+ sectionTitle: {
243
+ fontSize: "10px",
244
+ fontWeight: 600,
245
+ textTransform: "uppercase",
246
+ letterSpacing: "0.6px",
247
+ color: palette.muted,
248
+ marginBottom: "4px"
249
+ },
250
+ input: {
251
+ width: "100%",
252
+ boxSizing: "border-box",
253
+ padding: "8px 11px",
254
+ fontSize: "12px",
255
+ color: palette.text,
256
+ background: palette.bg,
257
+ border: `1px solid ${palette.border}`,
258
+ borderRadius: "9px",
259
+ outline: "none"
260
+ },
261
+ inputHint: {
262
+ fontSize: "11px",
263
+ color: palette.muted,
264
+ marginTop: "6px",
265
+ lineHeight: 1.4
266
+ },
267
+ grid: {
268
+ display: "grid",
269
+ gridTemplateColumns: "repeat(3, 1fr)",
270
+ gap: "8px"
271
+ },
272
+ modeCard: (selected) => ({
273
+ position: "relative",
274
+ display: "flex",
275
+ flexDirection: "column",
276
+ alignItems: "flex-start",
277
+ gap: "3px",
278
+ padding: "6px",
279
+ textAlign: "left",
280
+ cursor: "pointer",
281
+ background: selected ? "rgba(59, 130, 246, 0.12)" : palette.bg,
282
+ border: selected ? `1.5px solid ${palette.accent}` : `1px solid ${palette.border}`,
283
+ borderRadius: "10px",
284
+ transition: "all 0.15s ease",
285
+ boxShadow: selected ? `0 0 0 1px ${palette.accent}` : "none"
286
+ }),
287
+ modeIcon: {
288
+ width: "20px",
289
+ height: "20px",
290
+ color: palette.accent
291
+ },
292
+ modeTitle: {
293
+ fontSize: "12px",
294
+ fontWeight: 600,
295
+ color: palette.text
296
+ },
297
+ modeDesc: {
298
+ fontSize: "9px",
299
+ color: palette.muted,
300
+ lineHeight: 1.35,
301
+ whiteSpace: "nowrap",
302
+ overflow: "hidden",
303
+ textOverflow: "ellipsis",
304
+ width: "100%"
305
+ },
306
+ check: {
307
+ position: "absolute",
308
+ top: "10px",
309
+ right: "10px",
310
+ width: "16px",
311
+ height: "16px",
312
+ display: "flex",
313
+ alignItems: "center",
314
+ justifyContent: "center",
315
+ borderRadius: "50%",
316
+ background: palette.accent,
317
+ color: "#fff",
318
+ fontSize: "10px"
319
+ },
320
+ textarea: {
321
+ width: "100%",
322
+ minHeight: "38px",
323
+ boxSizing: "border-box",
324
+ padding: "8px 11px",
325
+ fontSize: "12px",
326
+ color: palette.text,
327
+ background: palette.bg,
328
+ border: `1px solid ${palette.border}`,
329
+ borderRadius: "9px",
330
+ outline: "none",
331
+ resize: "vertical",
332
+ fontFamily: "inherit"
333
+ },
334
+ footer: {
335
+ display: "flex",
336
+ alignItems: "center",
337
+ justifyContent: "space-between",
338
+ gap: "12px",
339
+ marginTop: "10px"
340
+ },
341
+ runButton: (disabled) => ({
342
+ display: "inline-flex",
343
+ alignItems: "center",
344
+ justifyContent: "center",
345
+ gap: "8px",
346
+ padding: "10px 18px",
347
+ fontSize: "13px",
348
+ fontWeight: 600,
349
+ color: "#fff",
350
+ background: disabled ? "rgba(59, 130, 246, 0.4)" : palette.accent,
351
+ border: "none",
352
+ borderRadius: "10px",
353
+ cursor: disabled ? "not-allowed" : "pointer",
354
+ boxShadow: disabled ? "none" : `0 4px 14px ${palette.accentGlow}`
355
+ }),
356
+ cancelButton: {
357
+ padding: "10px 14px",
358
+ fontSize: "13px",
359
+ fontWeight: 500,
360
+ color: palette.muted,
361
+ background: "transparent",
362
+ border: "none",
363
+ cursor: "pointer"
364
+ },
365
+ chipRow: {
366
+ display: "flex",
367
+ flexWrap: "wrap",
368
+ gap: "6px"
369
+ },
370
+ chip: (selected) => ({
371
+ display: "inline-flex",
372
+ alignItems: "center",
373
+ gap: "5px",
374
+ padding: "5px 9px",
375
+ fontSize: "11px",
376
+ fontWeight: 500,
377
+ color: selected ? "#fff" : palette.muted,
378
+ background: selected ? palette.accent : palette.bg,
379
+ border: `1px solid ${selected ? palette.accent : palette.border}`,
380
+ borderRadius: "20px",
381
+ cursor: "pointer",
382
+ transition: "all 0.15s ease"
383
+ }),
384
+ recentList: {
385
+ display: "flex",
386
+ flexDirection: "column",
387
+ gap: "6px"
388
+ },
389
+ recentItem: {
390
+ display: "flex",
391
+ alignItems: "center",
392
+ justifyContent: "space-between",
393
+ gap: "8px",
394
+ padding: "8px 10px",
395
+ background: palette.bg,
396
+ border: `1px solid ${palette.border}`,
397
+ borderRadius: "9px",
398
+ cursor: "pointer",
399
+ transition: "background 0.15s ease"
400
+ },
401
+ recentPath: {
402
+ fontSize: "11px",
403
+ color: palette.text,
404
+ overflow: "hidden",
405
+ textOverflow: "ellipsis",
406
+ whiteSpace: "nowrap",
407
+ flex: 1
408
+ },
409
+ recentMeta: {
410
+ fontSize: "9px",
411
+ color: palette.muted,
412
+ flex: "none"
413
+ },
414
+ previewCard: {
415
+ padding: "8px 10px",
416
+ background: palette.bg,
417
+ border: `1px solid ${palette.border}`,
418
+ borderRadius: "9px",
419
+ marginTop: "8px",
420
+ fontSize: "11px",
421
+ color: palette.text
422
+ },
423
+ previewStats: {
424
+ color: palette.muted,
425
+ fontSize: "10px"
426
+ },
427
+ microFooter: {
428
+ display: "flex",
429
+ alignItems: "center",
430
+ gap: "8px",
431
+ marginTop: "10px",
432
+ padding: "4px 0",
433
+ borderTop: `1px solid ${palette.border}`,
434
+ fontSize: "10px",
435
+ color: palette.muted,
436
+ flexWrap: "wrap"
437
+ },
438
+ proTip: {
439
+ marginTop: "14px",
440
+ padding: "8px 10px",
441
+ background: palette.bg,
442
+ border: `1px solid ${palette.border}`,
443
+ borderRadius: "9px",
444
+ fontSize: "10px",
445
+ color: palette.muted,
446
+ lineHeight: 1.35,
447
+ display: "flex",
448
+ alignItems: "flex-start",
449
+ gap: "8px"
450
+ },
451
+ dannaoFooter: {
452
+ marginTop: "12px",
453
+ paddingTop: "12px",
454
+ borderTop: `1px solid ${palette.border}`,
455
+ fontSize: "11px",
456
+ color: palette.muted,
457
+ textAlign: "center",
458
+ fontStyle: "italic"
459
+ },
460
+ overlay: {
461
+ position: "fixed",
462
+ inset: 0,
463
+ zIndex: 999999,
464
+ display: "flex",
465
+ alignItems: "center",
466
+ justifyContent: "center",
467
+ background: palette.overlay,
468
+ backdropFilter: "blur(8px)",
469
+ WebkitBackdropFilter: "blur(8px)",
470
+ transition: "opacity 0.2s ease"
471
+ },
472
+ overlayCard: {
473
+ width: "320px",
474
+ maxWidth: "92vw",
475
+ padding: "36px 32px",
476
+ borderRadius: "16px",
477
+ textAlign: "center",
478
+ background: palette.card,
479
+ border: `1px solid ${palette.border}`,
480
+ boxShadow: "0 20px 60px rgba(0, 0, 0, 0.45)"
481
+ },
482
+ spinnerWrap: {
483
+ position: "relative",
484
+ width: "48px",
485
+ height: "48px",
486
+ margin: "0 auto 22px"
487
+ },
488
+ overlayTitle: {
489
+ fontSize: "15px",
490
+ fontWeight: 600,
491
+ color: palette.text,
492
+ marginBottom: "6px",
493
+ letterSpacing: "-0.15px"
494
+ },
495
+ overlaySubtitle: {
496
+ fontSize: "12px",
497
+ color: palette.muted,
498
+ lineHeight: 1.45,
499
+ wordBreak: "break-word",
500
+ marginBottom: "18px"
501
+ },
502
+ progressTrack: {
503
+ width: "100%",
504
+ height: "3px",
505
+ background: "rgba(255,255,255,0.08)",
506
+ borderRadius: "999px",
507
+ marginBottom: "14px",
508
+ overflow: "hidden"
509
+ },
510
+ progressFill: (pct) => ({
511
+ width: `${pct}%`,
512
+ height: "100%",
513
+ background: palette.accent,
514
+ borderRadius: "999px",
515
+ transition: "width 0.3s ease"
516
+ }),
517
+ progressList: {
518
+ display: "flex",
519
+ flexDirection: "column",
520
+ gap: "6px",
521
+ textAlign: "left",
522
+ maxHeight: "120px",
523
+ overflow: "hidden",
524
+ fontSize: "11px",
525
+ color: palette.muted
526
+ },
527
+ progressItem: (active) => ({
528
+ display: "flex",
529
+ alignItems: "center",
530
+ gap: "8px",
531
+ padding: "4px 0",
532
+ color: active ? "#e5e7eb" : "#6b7280",
533
+ transition: "color 0.2s ease"
534
+ }),
535
+ successIcon: {
536
+ width: "56px",
537
+ height: "56px",
538
+ margin: "0 auto 22px",
539
+ display: "flex",
540
+ alignItems: "center",
541
+ justifyContent: "center",
542
+ borderRadius: "50%",
543
+ background: "rgba(16, 185, 129, 0.12)",
544
+ border: "1px solid rgba(16, 185, 129, 0.25)"
545
+ },
546
+ successTitle: {
547
+ fontSize: "17px",
548
+ fontWeight: 700,
549
+ color: palette.text,
550
+ marginBottom: "8px"
551
+ },
552
+ successSubtitle: {
553
+ fontSize: "12px",
554
+ color: palette.muted,
555
+ lineHeight: 1.45,
556
+ wordBreak: "break-word",
557
+ marginBottom: "20px"
558
+ },
559
+ successButton: {
560
+ display: "inline-flex",
561
+ alignItems: "center",
562
+ justifyContent: "center",
563
+ gap: "6px",
564
+ padding: "8px 16px",
565
+ fontSize: "12px",
566
+ fontWeight: 600,
567
+ color: "#ffffff",
568
+ background: palette.accent,
569
+ border: "none",
570
+ borderRadius: "8px",
571
+ cursor: "pointer",
572
+ boxShadow: `0 4px 14px ${palette.accentGlow}`
573
+ },
574
+ successTrack: {
575
+ width: "100%",
576
+ height: "3px",
577
+ background: "rgba(255,255,255,0.08)",
578
+ borderRadius: "999px",
579
+ marginTop: "20px",
580
+ overflow: "hidden"
581
+ },
582
+ successFill: (pct) => ({
583
+ width: `${pct}%`,
584
+ height: "100%",
585
+ background: "linear-gradient(90deg, #10b981, #34d399)",
586
+ borderRadius: "999px",
587
+ transition: "width 0.1s linear"
588
+ })
589
+ };
590
+
591
+ function CodebaseLogo(props) {
592
+ return jsx("svg", {
593
+ ...props,
594
+ viewBox: "0 0 32 32",
595
+ fill: "none",
596
+ stroke: "currentColor",
597
+ strokeWidth: "2.2",
598
+ strokeLinecap: "round",
599
+ strokeLinejoin: "round",
600
+ children: [
601
+ jsx("path", { d: "M6 11 L16 5 L26 11 L26 21 L16 27 L6 21 Z", key: "hex" }),
602
+ jsx("path", { d: "M16 5 L16 15 M16 15 L6 21 M16 15 L26 21", strokeOpacity: "0.5", key: "spokes" }),
603
+ jsx("circle", { cx: "16", cy: "15", r: "3.5", fill: "currentColor", fillOpacity: "0.18", stroke: "currentColor", key: "core" }),
604
+ jsx("circle", { cx: "16", cy: "5", r: "2.2", fill: "currentColor", stroke: "none", key: "n1" }),
605
+ jsx("circle", { cx: "6", cy: "21", r: "2.2", fill: "currentColor", stroke: "none", key: "n2" }),
606
+ jsx("circle", { cx: "26", cy: "21", r: "2.2", fill: "currentColor", stroke: "none", key: "n3" })
607
+ ]
608
+ });
609
+ }
610
+
611
+ function CloseIcon(props) {
612
+ return jsx("svg", {
613
+ ...props,
614
+ viewBox: "0 0 24 24",
615
+ fill: "none",
616
+ stroke: "currentColor",
617
+ strokeWidth: "2.2",
618
+ strokeLinecap: "round",
619
+ strokeLinejoin: "round",
620
+ children: [jsx("path", { d: "M18 6L6 18 M6 6L18 18", key: "x" })]
621
+ });
622
+ }
623
+
624
+ function CheckIcon(props) {
625
+ return jsx("svg", {
626
+ ...props,
627
+ viewBox: "0 0 24 24",
628
+ fill: "none",
629
+ stroke: "currentColor",
630
+ strokeWidth: "2.2",
631
+ strokeLinecap: "round",
632
+ strokeLinejoin: "round",
633
+ children: [jsx("polyline", { points: "20 6 9 17 4 12", key: "check" })]
634
+ });
635
+ }
636
+
637
+ function DotIcon(props) {
638
+ return jsx("svg", {
639
+ ...props,
640
+ viewBox: "0 0 24 24",
641
+ fill: "currentColor",
642
+ children: [jsx("circle", { cx: "12", cy: "12", r: "5", key: "dot" })]
643
+ });
644
+ }
645
+
646
+ function ModeIcon({ mode, ...props }) {
647
+ if (mode === "intel") {
648
+ return jsx("svg", {
649
+ ...props,
650
+ viewBox: "0 0 24 24",
651
+ fill: "none",
652
+ stroke: "currentColor",
653
+ strokeWidth: "1.8",
654
+ strokeLinecap: "round",
655
+ strokeLinejoin: "round",
656
+ children: [
657
+ jsx("path", { d: "M12 4 L12 14 M12 14 L5 20 M12 14 L19 20", key: "lines" }),
658
+ jsx("circle", { cx: "12", cy: "4", r: "2.5", fill: "currentColor", stroke: "none", key: "top" }),
659
+ jsx("circle", { cx: "5", cy: "20", r: "2.5", fill: "currentColor", stroke: "none", key: "left" }),
660
+ jsx("circle", { cx: "19", cy: "20", r: "2.5", fill: "currentColor", stroke: "none", key: "right" })
661
+ ]
662
+ });
663
+ }
664
+ if (mode === "report") {
665
+ return jsx("svg", {
666
+ ...props,
667
+ viewBox: "0 0 24 24",
668
+ fill: "none",
669
+ stroke: "currentColor",
670
+ strokeWidth: "1.8",
671
+ strokeLinecap: "round",
672
+ strokeLinejoin: "round",
673
+ children: [
674
+ jsx("path", { d: "M4 20 L20 20", key: "base" }),
675
+ jsx("path", { d: "M7 20 L7 14 M12 20 L12 8 M17 20 L17 11", key: "bars" })
676
+ ]
677
+ });
678
+ }
679
+ if (mode === "audit") {
680
+ return jsx("svg", {
681
+ ...props,
682
+ viewBox: "0 0 24 24",
683
+ fill: "none",
684
+ stroke: "currentColor",
685
+ strokeWidth: "1.8",
686
+ strokeLinecap: "round",
687
+ strokeLinejoin: "round",
688
+ children: [
689
+ jsx("circle", { cx: "11", cy: "11", r: "7", key: "ring" }),
690
+ jsx("path", { d: "M20 20 L16 16", key: "handle" })
691
+ ]
692
+ });
693
+ }
694
+ if (mode === "tasks") {
695
+ return jsx("svg", {
696
+ ...props,
697
+ viewBox: "0 0 24 24",
698
+ fill: "none",
699
+ stroke: "currentColor",
700
+ strokeWidth: "1.8",
701
+ strokeLinecap: "round",
702
+ strokeLinejoin: "round",
703
+ children: [
704
+ jsx("path", { d: "M4 7 L10 7 M4 12 L10 12 M4 17 L10 17", key: "rows" }),
705
+ jsx("path", { d: "M14 8 L17 11 L22 5", key: "check" }),
706
+ jsx("path", { d: "M14 13 L17 16 L22 10", key: "check2" }),
707
+ jsx("path", { d: "M14 18 L17 21 L22 15", key: "check3" })
708
+ ]
709
+ });
710
+ }
711
+ if (mode === "player") {
712
+ return jsx("svg", {
713
+ ...props,
714
+ viewBox: "0 0 24 24",
715
+ fill: "none",
716
+ stroke: "currentColor",
717
+ strokeWidth: "1.8",
718
+ strokeLinecap: "round",
719
+ strokeLinejoin: "round",
720
+ children: [
721
+ jsx("path", { d: "M4 20 C8 20 9 14 14 14 C19 14 20 18 20 20", key: "path" }),
722
+ jsx("circle", { cx: "14", cy: "14", r: "3", fill: "currentColor", fillOpacity: "0.2", key: "point" }),
723
+ jsx("circle", { cx: "20", cy: "20", r: "2.2", fill: "currentColor", stroke: "none", key: "end" })
724
+ ]
725
+ });
726
+ }
727
+ return jsx(CodebaseLogo, { ...props });
728
+ }
729
+
730
+ function SparklesIcon(props) {
731
+ return jsx("svg", {
732
+ ...props,
733
+ viewBox: "0 0 24 24",
734
+ fill: "none",
735
+ stroke: "currentColor",
736
+ strokeWidth: "2",
737
+ strokeLinecap: "round",
738
+ strokeLinejoin: "round",
739
+ children: [
740
+ jsx("path", { d: "M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.582a.5.5 0 0 1 0 .962L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z", key: "s" })
741
+ ]
742
+ });
743
+ }
744
+
745
+ function getModes(locale) {
746
+ const l = locale === "en" ? {
747
+ intel: { title: "Intelligence Pro", desc: "CTO brief: architecture, tech debt, opportunities, ideas." },
748
+ report: { title: "Strategic Report", desc: "Board-level: SWOT, scorecards, 90-day roadmap." },
749
+ audit: { title: "Non-Compliance Audit", desc: "Debt scan: TODO, any, console.log, proposed fixes." },
750
+ tasks: { title: "TASKS Action Plan", desc: "Generate a TASKS.md with sprints and Before/After." },
751
+ player: { title: "Player / UX", desc: "User journey, friction points, wow moments." }
752
+ } : {
753
+ intel: { title: "Intelligence Pro", desc: "CTO brief : architecture, dette, opportunités, créa." },
754
+ report: { title: "Rapport Stratégique", desc: "Constat board : SWOT, score cards, roadmap 90 jours." },
755
+ audit: { title: "Audit Non-Conformités", desc: "Scan dette : TODO, any, console.log, fix proposés." },
756
+ tasks: { title: "Plan d'Action TASKS", desc: "Génère un TASKS.md avec sprints et Avant/Après." },
757
+ player: { title: "Player / UX", desc: "Parcours utilisateur, frictions, wow moments." }
758
+ };
759
+ return [
760
+ { id: "intel", ...l.intel },
761
+ { id: "report", ...l.report },
762
+ { id: "audit", ...l.audit },
763
+ { id: "tasks", ...l.tasks },
764
+ { id: "player", ...l.player }
765
+ ];
766
+ }
767
+
768
+ function getStyleOptions(locale) {
769
+ const l = locale === "en" ? {
770
+ ouf: { label: "Ouf", desc: "Max impact, punchy." },
771
+ punchy: { label: "Punchy", desc: "Direct, hard-hitting." },
772
+ dense: { label: "Dense", desc: "Factual, rich, no-fluff." },
773
+ pedagogique: { label: "Pedagogical", desc: "Clear, progressive, explanatory." },
774
+ minimal: { label: "Minimal", desc: "Short, essential, bullet points." }
775
+ } : {
776
+ ouf: { label: "Ouf", desc: "Impact maximal, punchy." },
777
+ punchy: { label: "Punchy", desc: "Direct, coup de poing." },
778
+ dense: { label: "Dense", desc: "Factuel, riche, no-fluff." },
779
+ pedagogique: { label: "Pédagogique", desc: "Clair, progressif, explicatif." },
780
+ minimal: { label: "Minimal", desc: "Court, essentiel, bullet points." }
781
+ };
782
+ return [
783
+ { id: "ouf", ...l.ouf },
784
+ { id: "punchy", ...l.punchy },
785
+ { id: "dense", ...l.dense },
786
+ { id: "pedagogique", ...l.pedagogique },
787
+ { id: "minimal", ...l.minimal }
788
+ ];
789
+ }
790
+
791
+ const RECENT_KEY = "codebase-recent-projects";
792
+ const CUSTOM_PRESETS_KEY = "codebase-custom-presets";
793
+
794
+ function getFocusSuggestions(locale) {
795
+ if (locale === "en") {
796
+ return {
797
+ intel: ["architecture", "technical debt", "key engines", "opportunities"],
798
+ report: ["SWOT", "90-day roadmap", "scorecard", "risks"],
799
+ audit: ["TODO", "console.log", "any", "missing tests"],
800
+ tasks: ["quick-wins", "sprint 1", "prioritization", "Before/After"],
801
+ player: ["onboarding", "frictions", "wow moments", "journey"]
802
+ };
803
+ }
804
+ return {
805
+ intel: ["architecture", "dette technique", "moteurs cles", "opportunites"],
806
+ report: ["SWOT", "roadmap 90j", "score card", "risques"],
807
+ audit: ["TODO", "console.log", "any", "tests manquants"],
808
+ tasks: ["quick-wins", "sprint 1", "priorisation", "Avant/Après"],
809
+ player: ["onboarding", "frictions", "wow moments", "parcours"]
810
+ };
811
+ }
812
+
813
+ function getPresets(locale) {
814
+ if (locale === "en") {
815
+ return [
816
+ { id: "investor", label: "Investor", mode: "report", style: "punchy", focus: "Risk, opportunities and ROI summary for a board." },
817
+ { id: "techlead", label: "Tech Lead", mode: "intel", style: "dense", focus: "Architecture, technical debt, engine decisions and 90-day roadmap." },
818
+ { id: "recruit", label: "New Hire", mode: "intel", style: "pedagogique", focus: "Project overview, stack and how to get oriented quickly." },
819
+ { id: "delivery", label: "Delivery", mode: "tasks", style: "dense", focus: "Prioritized TASKS.md with quick-wins and realistic sprints." }
820
+ ];
821
+ }
822
+ return [
823
+ { id: "investor", label: "Investisseur", mode: "report", style: "punchy", focus: "Synthese risques, opportunites et ROI a destination d'un board." },
824
+ { id: "techlead", label: "Tech Lead", mode: "intel", style: "dense", focus: "Architecture, dette technique, decisions moteur et roadmap 90 jours." },
825
+ { id: "recruit", label: "Recrue", mode: "intel", style: "pedagogique", focus: "Vue d'ensemble du projet, stack et comment s'y retrouver rapidement." },
826
+ { id: "delivery", label: "Delivery", mode: "tasks", style: "dense", focus: "TASKS.md priorise avec quick-wins et sprints realistes." }
827
+ ];
828
+ }
829
+
830
+ function getProTips(locale) {
831
+ if (locale === "en") {
832
+ return [
833
+ "Focus sharpens the brief: mention a file, an engine, or a UI.",
834
+ "Styles change the tone without changing the quality.",
835
+ "An absolute path without quotes works if spaces are escaped.",
836
+ "The preview appears after 400 ms of pause in the project field."
837
+ ];
838
+ }
839
+ return [
840
+ "Le focus affine le brief : mentionne un fichier, un moteur ou une UI.",
841
+ "Les styles modifient le ton sans changer la qualite.",
842
+ "Un chemin absolu sans guillemets fonctionne si les espaces sont protégées.",
843
+ "L'apercu apparait apres 400 ms de pause dans le champ projet."
844
+ ];
845
+ }
846
+
847
+ const MODE_ALIASES = {
848
+ report: "report",
849
+ rapport: "report",
850
+ intel: "intel",
851
+ intelligence: "intel",
852
+ inteligence: "intel",
853
+ audit: "audit",
854
+ tasks: "tasks",
855
+ task: "tasks",
856
+ taches: "tasks",
857
+ tache: "tasks",
858
+ player: "player",
859
+ playthrough: "player",
860
+ ux: "player"
861
+ };
862
+
863
+ function showToast(message, type = "info") {
864
+ const existing = document.getElementById("codebase-chat-toast");
865
+ if (existing) existing.remove();
866
+ const toast = document.createElement("div");
867
+ toast.id = "codebase-chat-toast";
868
+ toast.textContent = message;
869
+ Object.assign(toast.style, {
870
+ position: "fixed",
871
+ bottom: "20px",
872
+ right: "20px",
873
+ padding: "12px 16px",
874
+ borderRadius: "10px",
875
+ fontSize: "13px",
876
+ fontWeight: 500,
877
+ color: "#fff",
878
+ background: type === "error" ? palette.danger : palette.bg,
879
+ border: `1px solid ${palette.border}`,
880
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
881
+ zIndex: 999999,
882
+ maxWidth: "320px",
883
+ lineHeight: 1.4,
884
+ transition: "opacity 0.3s ease"
885
+ });
886
+ document.body.appendChild(toast);
887
+ setTimeout(() => {
888
+ toast.style.opacity = "0";
889
+ setTimeout(() => toast.remove(), 300);
890
+ }, 4000);
891
+ }
892
+
893
+ function LoadingOverlay({ mode, projectPath, progress, locale }) {
894
+ const labels = getLabels(locale);
895
+ const modeLabel = getModes(locale).find((m) => m.id === mode)?.title || labels.loadingTitle;
896
+ const shortPath = projectPath?.length > 55 ? `...${projectPath.slice(-55)}` : (projectPath || "auto");
897
+ const css = `@keyframes codebase-spin { to { transform: rotate(360deg); } }`;
898
+ const visible = progress.slice(-4);
899
+ const pct = progress.length ? Math.min(100, Math.max(8, progress.length * 10)) : 8;
900
+ return jsxs(React.Fragment, {
901
+ children: [
902
+ jsx("style", { dangerouslySetInnerHTML: { __html: css }, key: "spin-css" }),
903
+ jsxs("div", {
904
+ key: "overlay-backdrop",
905
+ style: styles.overlay,
906
+ children: [
907
+ jsxs("div", {
908
+ style: styles.overlayCard,
909
+ children: [
910
+ jsxs("div", {
911
+ style: styles.spinnerWrap,
912
+ children: [
913
+ jsx("svg", {
914
+ width: "48",
915
+ height: "48",
916
+ viewBox: "0 0 48 48",
917
+ children: [
918
+ jsx("circle", {
919
+ cx: "24",
920
+ cy: "24",
921
+ r: "20",
922
+ fill: "none",
923
+ stroke: "rgba(255,255,255,0.08)",
924
+ strokeWidth: "4"
925
+ }),
926
+ jsx("circle", {
927
+ cx: "24",
928
+ cy: "24",
929
+ r: "20",
930
+ fill: "none",
931
+ stroke: palette.accent,
932
+ strokeWidth: "4",
933
+ strokeLinecap: "round",
934
+ strokeDasharray: "78 125",
935
+ style: {
936
+ transformOrigin: "24px 24px",
937
+ animation: "codebase-spin 1.1s linear infinite"
938
+ }
939
+ })
940
+ ]
941
+ })
942
+ ]
943
+ }),
944
+ jsx("div", { style: styles.overlayTitle, children: labels.loadingTitle, key: "title" }),
945
+ jsxs("div", { style: styles.overlaySubtitle, children: [modeLabel, " ", jsx("span", { style: { opacity: .65 }, children: `• ${shortPath}` })], key: "sub" }),
946
+ jsxs("div", { style: styles.progressTrack, children: [jsx("div", { style: styles.progressFill(pct) })] }),
947
+ jsxs("div", {
948
+ style: styles.progressList,
949
+ children: visible.map((msg, i) => jsxs("div", {
950
+ key: i,
951
+ style: styles.progressItem(i === visible.length - 1),
952
+ children: [
953
+ jsx("span", { style: { display: "inline-flex", alignItems: "center" }, children: i === visible.length - 1 ? jsx(DotIcon, { style: { width: 8, height: 8 } }) : jsx(CheckIcon, { style: { width: 8, height: 8 } }) }),
954
+ msg
955
+ ]
956
+ }))
957
+ })
958
+ ]
959
+ })
960
+ ]
961
+ })
962
+ ]
963
+ });
964
+ }
965
+
966
+ function SuccessOverlay({ mode, projectPath, onClose, locale }) {
967
+ const labels = getLabels(locale);
968
+ const modeLabel = getModes(locale).find((m) => m.id === mode)?.title || labels.loadingTitle;
969
+ const shortPath = projectPath?.length > 55 ? `...${projectPath.slice(-55)}` : (projectPath || labels.autoDetect || "auto");
970
+ const [remaining, setRemaining] = React.useState(100);
971
+ const duration = 3500;
972
+ const start = React.useRef(Date.now());
973
+
974
+ React.useEffect(() => {
975
+ const id = setInterval(() => {
976
+ const pct = Math.max(0, 100 - ((Date.now() - start.current) / duration) * 100);
977
+ setRemaining(pct);
978
+ if (pct <= 0) onClose();
979
+ }, 80);
980
+ const timeout = setTimeout(onClose, duration);
981
+ return () => { clearInterval(id); clearTimeout(timeout); };
982
+ }, [onClose]);
983
+
984
+ return jsxs("div", {
985
+ style: styles.overlay,
986
+ children: [
987
+ jsxs("div", {
988
+ style: { ...styles.overlayCard, border: "1px solid rgba(16, 185, 129, 0.22)", boxShadow: "0 20px 60px rgba(0, 0, 0, 0.45), 0 0 40px rgba(16, 185, 129, 0.10)" },
989
+ children: [
990
+ jsxs("div", {
991
+ style: styles.successIcon,
992
+ children: [
993
+ jsxs("svg", {
994
+ width: "32",
995
+ height: "32",
996
+ viewBox: "0 0 24 24",
997
+ fill: "none",
998
+ stroke: "#34d399",
999
+ strokeWidth: "2.5",
1000
+ strokeLinecap: "round",
1001
+ strokeLinejoin: "round",
1002
+ children: [
1003
+ jsx("polyline", { points: "20 6 9 17 4 12" })
1004
+ ]
1005
+ })
1006
+ ]
1007
+ }),
1008
+ jsx("div", { style: styles.successTitle, children: labels.successTitle }),
1009
+ jsxs("div", { style: styles.successSubtitle, children: [labels.successBody, jsx("span", { style: { color: "#d1d5db" }, children: ` — ${shortPath}` })] }),
1010
+ jsxs("button", {
1011
+ type: "button",
1012
+ style: styles.successButton,
1013
+ onClick: onClose,
1014
+ children: [labels.close, jsx("span", { children: " →" })]
1015
+ }),
1016
+ jsxs("div", { style: styles.successTrack, children: [jsx("div", { style: styles.successFill(remaining) })] })
1017
+ ]
1018
+ })
1019
+ ]
1020
+ });
1021
+ }
1022
+
1023
+ function useLocalStorage(key, initial) {
1024
+ const [value, setValue] = React.useState(() => {
1025
+ try { return JSON.parse(window.localStorage?.getItem(key)) || initial; } catch { return initial; }
1026
+ });
1027
+ const save = React.useCallback((next) => {
1028
+ setValue(next);
1029
+ try { window.localStorage?.setItem(key, JSON.stringify(next)); } catch {}
1030
+ }, [key]);
1031
+ return [value, save];
1032
+ }
1033
+
1034
+ function MenuModal({ sessionId, onClose }) {
1035
+ const savedPath = (() => {
1036
+ try { return window.localStorage?.getItem("codebase-project-path") || ""; } catch { return ""; }
1037
+ })();
1038
+ const [locale, setLocale] = useLocalStorage(LOCALE_KEY, "fr");
1039
+ const labels = getLabels(locale);
1040
+ const MODES = getModes(locale);
1041
+ const STYLE_OPTIONS = getStyleOptions(locale);
1042
+ const FOCUS_SUGGESTIONS = getFocusSuggestions(locale);
1043
+ const PRESETS = getPresets(locale);
1044
+ const PRO_TIPS = getProTips(locale);
1045
+
1046
+ const [projectPath, setProjectPath] = React.useState(savedPath);
1047
+ const [mode, setMode] = React.useState("intel");
1048
+ const [style, setStyle] = React.useState("ouf");
1049
+ const [focus, setFocus] = React.useState("");
1050
+ const [busy, setBusy] = React.useState(false);
1051
+ const [progress, setProgress] = React.useState([]);
1052
+ const [success, setSuccess] = React.useState(null);
1053
+ const [error, setError] = React.useState(null);
1054
+ const [preview, setPreview] = React.useState(null);
1055
+ const [previewLoading, setPreviewLoading] = React.useState(false);
1056
+ const [recent, setRecent] = useLocalStorage(RECENT_KEY, []);
1057
+ const [customPresets, setCustomPresets] = useLocalStorage(CUSTOM_PRESETS_KEY, []);
1058
+ const modalRef = React.useRef(null);
1059
+
1060
+ React.useEffect(() => {
1061
+ modalRef.current?.focus();
1062
+ }, []);
1063
+
1064
+ const modeInfo = MODES.find((m) => m.id === mode);
1065
+ const styleInfo = STYLE_OPTIONS.find((s) => s.id === style);
1066
+
1067
+ React.useEffect(() => {
1068
+ if (!projectPath.trim()) {
1069
+ setPreview(null);
1070
+ return;
1071
+ }
1072
+ const id = setTimeout(() => {
1073
+ setPreviewLoading(true);
1074
+ fetch("/codebase-chat/preview", {
1075
+ method: "POST",
1076
+ headers: { "Content-Type": "application/json" },
1077
+ body: JSON.stringify({ projectPath: projectPath.trim() })
1078
+ })
1079
+ .then((r) => r.json().catch(() => ({})))
1080
+ .then((data) => {
1081
+ if (data.ok) setPreview(data);
1082
+ else setPreview(null);
1083
+ })
1084
+ .catch(() => setPreview(null))
1085
+ .finally(() => setPreviewLoading(false));
1086
+ }, 400);
1087
+ return () => clearTimeout(id);
1088
+ }, [projectPath]);
1089
+
1090
+ const handleSubmit = React.useCallback(async () => {
1091
+ if (busy || !sessionId) return;
1092
+ if (!modeInfo) return;
1093
+
1094
+ const cleanPath = projectPath.trim();
1095
+ if (cleanPath) {
1096
+ try { window.localStorage?.setItem("codebase-project-path", cleanPath); } catch {}
1097
+ }
1098
+
1099
+ const nextRecent = [{ path: cleanPath || labels.autoDetect, mode, date: Date.now() }, ...recent.filter((r) => r.path !== (cleanPath || labels.autoDetect))].slice(0, 5);
1100
+ setRecent(nextRecent);
1101
+
1102
+ const defaultQueries = locale === "en" ? {
1103
+ report: focus.trim() || "Complete Strategic Report",
1104
+ intel: focus.trim() || "Complete Pro Intelligence Brief",
1105
+ audit: focus.trim() || "Complete non-conformities audit",
1106
+ tasks: focus.trim() || "Generate the TASKS.md",
1107
+ player: focus.trim() || "Player brief / user journey"
1108
+ } : {
1109
+ report: focus.trim() || "Rapport Stratégique complet",
1110
+ intel: focus.trim() || "Brief d'Intelligence Pro complet",
1111
+ audit: focus.trim() || "Audit non-conformités complet",
1112
+ tasks: focus.trim() || "Génère le TASKS.md",
1113
+ player: focus.trim() || "Player brief / parcours utilisateur"
1114
+ };
1115
+ const creaThemes = locale === "en" ? "generated with passion by shinzarou-eng" : "généré avec passion par shinzarou-eng";
1116
+
1117
+ setBusy(true);
1118
+ setProgress([]);
1119
+ setError(null);
1120
+
1121
+ const poll = () => {
1122
+ fetch(`/codebase-chat/progress?sessionId=${encodeURIComponent(sessionId)}`)
1123
+ .then((r) => r.json().catch(() => ({})))
1124
+ .then((data) => {
1125
+ if (data?.messages) setProgress(data.messages);
1126
+ })
1127
+ .catch(() => {});
1128
+ };
1129
+ poll();
1130
+ const pollId = setInterval(poll, 250);
1131
+
1132
+ const startAt = Date.now();
1133
+ let successInfo = null;
1134
+ try {
1135
+ const response = await fetch("/codebase-chat/ask", {
1136
+ method: "POST",
1137
+ headers: { "Content-Type": "application/json" },
1138
+ body: JSON.stringify({
1139
+ sessionId,
1140
+ projectPath: cleanPath || undefined,
1141
+ mode,
1142
+ query: defaultQueries[mode],
1143
+ style,
1144
+ lang: locale,
1145
+ crea: true,
1146
+ creaTheme: creaThemes
1147
+ })
1148
+ });
1149
+ const body = await response.json().catch(() => ({}));
1150
+ if (!response.ok) {
1151
+ throw new Error(body.error || `Erreur ${response.status}`);
1152
+ }
1153
+ successInfo = { mode, projectPath: cleanPath || labels.autoDetect };
1154
+ } catch (err) {
1155
+ const msg = err instanceof Error ? err.message : String(err);
1156
+ setError(msg);
1157
+ showToast(`${labels.error} : ${msg}`, "error");
1158
+ } finally {
1159
+ clearInterval(pollId);
1160
+ const elapsed = Date.now() - startAt;
1161
+ if (elapsed < 1500) await new Promise((r) => setTimeout(r, 1500 - elapsed));
1162
+ setBusy(false);
1163
+ setProgress([]);
1164
+ if (successInfo) {
1165
+ setSuccess(successInfo);
1166
+ }
1167
+ }
1168
+ }, [busy, sessionId, projectPath, mode, focus, modeInfo, style, recent, setRecent]);
1169
+
1170
+ const loadRecent = (r) => {
1171
+ setProjectPath(r.path === "auto-détection" ? "" : r.path);
1172
+ if (r.mode) setMode(r.mode);
1173
+ };
1174
+
1175
+ if (success) {
1176
+ return jsx(SuccessOverlay, { mode: success.mode, projectPath: success.projectPath, onClose: () => { onClose(); setSuccess(null); }, locale });
1177
+ }
1178
+
1179
+ if (busy) {
1180
+ return jsx(LoadingOverlay, { mode, projectPath: projectPath.trim() || labels.autoDetect, progress, locale });
1181
+ }
1182
+
1183
+ const topRecent = recent.slice(0, 4);
1184
+
1185
+ return jsxs("div", {
1186
+ ref: modalRef,
1187
+ tabIndex: -1,
1188
+ style: styles.backdrop,
1189
+ onClick: (e) => { if (e.target === e.currentTarget) onClose(); },
1190
+ onKeyDown: (e) => {
1191
+ if (e.key === "Escape") { onClose(); return; }
1192
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !busy) { e.preventDefault(); handleSubmit(); }
1193
+ },
1194
+ children: [
1195
+ jsxs("div", {
1196
+ style: styles.modal,
1197
+ children: [
1198
+ jsxs("div", {
1199
+ style: styles.modalHeader,
1200
+ children: [
1201
+ jsxs("div", { style: styles.modalTitle, children: [jsx(CodebaseLogo, { style: { width: 22, height: 22 } }), labels.title, jsx("span", { style: styles.badge, children: labels.badge })] }),
1202
+ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1203
+ jsxs("select", {
1204
+ value: locale,
1205
+ onChange: (e) => setLocale(e.target.value),
1206
+ style: styles.langSelect,
1207
+ children: [
1208
+ jsx("option", { value: "fr", children: "FR" }),
1209
+ jsx("option", { value: "en", children: "EN" })
1210
+ ]
1211
+ }),
1212
+ jsx("button", { style: styles.modalClose, onClick: onClose, children: jsx(CloseIcon, { style: { width: 14, height: 14 } }) })
1213
+ ]})
1214
+ ]
1215
+ }),
1216
+
1217
+ topRecent.length ? jsxs("div", { style: styles.section, children: [
1218
+ jsx("div", { style: styles.sectionTitle, children: labels.recent }),
1219
+ jsxs("div", { style: styles.recentList, children: topRecent.map((r, i) => jsxs("div", {
1220
+ style: styles.recentItem,
1221
+ onClick: () => loadRecent(r),
1222
+ children: [
1223
+ jsx("span", { style: styles.recentPath, children: r.path }),
1224
+ jsxs("span", { style: styles.recentMeta, children: [MODES.find((m) => m.id === r.mode)?.title || r.mode, " • ", new Date(r.date).toLocaleDateString()] })
1225
+ ]
1226
+ }, i)) })
1227
+ ]}) : null,
1228
+
1229
+ jsxs("div", { style: styles.section, children: [
1230
+ jsxs("div", { style: { ...styles.sectionTitle, display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1231
+ labels.presets,
1232
+ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1233
+ jsx("button", {
1234
+ type: "button",
1235
+ style: styles.ghost,
1236
+ onClick: () => {
1237
+ const label = window.prompt(labels.addPreset);
1238
+ if (!label || !focus.trim()) return;
1239
+ setCustomPresets([...customPresets, { id: `custom-${Date.now()}`, label, mode, style, focus }].slice(-8));
1240
+ },
1241
+ title: labels.savePreset,
1242
+ children: "+"
1243
+ }),
1244
+ jsx("span", { style: { fontSize: 10, color: palette.muted, fontWeight: 400 }, children: labels.presetsHint })
1245
+ ]})
1246
+ ]}),
1247
+ jsxs("div", { style: styles.chipRow, children: [...PRESETS, ...customPresets].map((p) => jsxs("button", {
1248
+ type: "button",
1249
+ style: styles.chip(false),
1250
+ onClick: () => { setMode(p.mode); setStyle(p.style); setFocus(p.focus); },
1251
+ title: `${p.label} : ${p.focus}`,
1252
+ children: p.label
1253
+ }, p.id)) })
1254
+ ]}),
1255
+
1256
+ jsxs("div", { style: styles.section, children: [
1257
+ jsx("div", { style: styles.sectionTitle, children: labels.project }),
1258
+ jsx("input", {
1259
+ type: "text",
1260
+ style: styles.input,
1261
+ value: projectPath,
1262
+ placeholder: labels.projectPlaceholder,
1263
+ onChange: (e) => setProjectPath(e.target.value)
1264
+ }),
1265
+ preview && !previewLoading ? jsxs("div", { style: styles.previewCard, children: [
1266
+ jsx("strong", { children: preview.name || labels.project }),
1267
+ " ",
1268
+ jsx("span", { style: styles.previewStats, children: `(${preview.summary?.name || preview.path}) · ${preview.files || 0} ${locale === "en" ? "files" : "fichiers"} · ${preview.sourceLines?.toLocaleString() || 0} ${locale === "en" ? "lines" : "lignes"} · ${preview.testFiles || 0} ${locale === "en" ? "tests" : "tests"}` })
1269
+ ]}) : previewLoading ? jsxs("div", { style: { ...styles.previewCard, textAlign: "center" }, children: [jsx("div", { style: { fontSize: 11, color: palette.muted }, children: labels.previewLoading })] }) : null
1270
+ ]}),
1271
+
1272
+ jsxs("div", { style: styles.section, children: [
1273
+ jsx("div", { style: styles.sectionTitle, children: labels.mode }),
1274
+ jsxs("div", { style: styles.grid, children: MODES.map((m) => jsxs("button", {
1275
+ type: "button",
1276
+ style: styles.modeCard(mode === m.id),
1277
+ onClick: () => setMode(m.id),
1278
+ children: [
1279
+ mode === m.id ? jsx("div", { style: styles.check, children: jsx(CheckIcon, { style: { width: 10, height: 10 } }) }) : null,
1280
+ jsx(ModeIcon, { mode: m.id, style: styles.modeIcon }),
1281
+ jsx("div", { style: styles.modeTitle, children: m.title }),
1282
+ jsx("div", { style: styles.modeDesc, children: m.desc })
1283
+ ]
1284
+ }, m.id)) })
1285
+ ]}),
1286
+
1287
+ jsxs("div", { style: styles.section, children: [
1288
+ jsx("div", { style: styles.sectionTitle, children: labels.style }),
1289
+ jsxs("div", { style: styles.chipRow, children: STYLE_OPTIONS.map((s) => jsxs("button", {
1290
+ type: "button",
1291
+ style: styles.chip(style === s.id),
1292
+ onClick: () => setStyle(s.id),
1293
+ title: s.desc,
1294
+ children: s.label
1295
+ }, s.id)) })
1296
+ ]}),
1297
+
1298
+ jsxs("div", { style: styles.section, children: [
1299
+ jsx("div", { style: styles.sectionTitle, children: labels.focus }),
1300
+ jsx("textarea", {
1301
+ style: styles.textarea,
1302
+ value: focus,
1303
+ placeholder: labels.focusPlaceholder,
1304
+ onChange: (e) => setFocus(e.target.value)
1305
+ }),
1306
+ jsxs("div", { style: { ...styles.chipRow, marginTop: "6px" }, children: (FOCUS_SUGGESTIONS[mode] || []).map((tag) => jsx("button", {
1307
+ type: "button",
1308
+ style: styles.miniChip,
1309
+ onClick: () => setFocus((prev) => prev ? `${prev}, ${tag}` : tag),
1310
+ children: tag
1311
+ }, tag)) })
1312
+ ]}),
1313
+
1314
+ jsxs("div", { style: styles.footer, children: [
1315
+ jsx("button", { type: "button", style: styles.cancelButton, onClick: onClose, children: labels.cancel }),
1316
+ jsxs("button", {
1317
+ type: "button",
1318
+ style: styles.runButton(!sessionId || !modeInfo),
1319
+ disabled: !sessionId || !modeInfo,
1320
+ onClick: handleSubmit,
1321
+ children: [jsx(SparklesIcon, { style: { width: 14, height: 14 } }), labels.run]
1322
+ })
1323
+ ]}),
1324
+
1325
+ jsx("div", { style: styles.microFooter, children: jsx("span", { style: { color: palette.muted }, children: labels.footer }) })
1326
+ ]
1327
+ })
1328
+ ]
1329
+ });
1330
+ }
1331
+
1332
+ function CodebaseButton(props) {
1333
+ const { wide, useSessions } = props;
1334
+ const sessionState = useSessions((state) => state);
1335
+ const sessionId = sessionState?.current;
1336
+ const [open, setOpen] = React.useState(false);
1337
+ const [locale] = useLocalStorage(LOCALE_KEY, "fr");
1338
+ const labels = getLabels(locale);
1339
+
1340
+ const disabled = !sessionId;
1341
+ const title = sessionId ? labels.actionTitle : labels.sessionRequired;
1342
+
1343
+ const buttonStyle = disabled
1344
+ ? { ...styles.button, ...styles.buttonDisabled }
1345
+ : styles.button;
1346
+
1347
+ return jsxs(React.Fragment, {
1348
+ children: [
1349
+ jsxs("button", {
1350
+ type: "button",
1351
+ style: buttonStyle,
1352
+ disabled,
1353
+ title,
1354
+ "aria-label": "Codebase Pro",
1355
+ onClick: () => setOpen(true),
1356
+ children: [
1357
+ jsx(CodebaseLogo, { style: styles.icon, key: "icon" }),
1358
+ wide ? jsx("span", { style: styles.label, children: "Codebase Pro", key: "label" }) : null
1359
+ ]
1360
+ }),
1361
+ open && sessionId ? jsx(MenuModal, { sessionId, onClose: () => setOpen(false) }) : null
1362
+ ]
1363
+ });
1364
+ }
1365
+
1366
+ function apply(ctx) {
1367
+ ctx.slots.inject("sidebar.footer.action", () => {
1368
+ return ctx.slots.register({
1369
+ name: "sidebar.footer.action",
1370
+ id: "codebase-chat-button",
1371
+ order: 60
1372
+ }, CodebaseButton);
1373
+ });
1374
+ }
1375
+
1376
+ exports.apply = apply;
1377
+ exports.inject = inject;
1378
+ return module.exports;
1379
+ }
1380
+ });