@wakastellar/ui 3.3.3 → 3.5.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.
Files changed (140) hide show
  1. package/dist/badge-BbwO7QeZ.js +1 -0
  2. package/dist/badge-BfiocODp.mjs +23 -0
  3. package/dist/charts.cjs.js +1 -1
  4. package/dist/charts.es.js +1 -1
  5. package/dist/chunk-14q5BKub.js +1 -0
  6. package/dist/{chunk-BH6uBOac.mjs → chunk-Cr9pTUWm.mjs} +5 -5
  7. package/dist/cn-DEtaFQsA.js +1 -0
  8. package/dist/cn-DUn6aSIQ.mjs +24 -0
  9. package/dist/doc.cjs.js +2 -2
  10. package/dist/doc.es.js +19 -19
  11. package/dist/editor.cjs.js +48 -0
  12. package/dist/editor.d.ts +1 -0
  13. package/dist/editor.es.js +6551 -0
  14. package/dist/{exceljs.min-DG9M8IZ1.mjs → exceljs.min-DL1XYDll.mjs} +1 -1
  15. package/dist/{exceljs.min-BuefmDRS.js → exceljs.min-qeIfSCbF.js} +1 -1
  16. package/dist/export.cjs.js +1 -1
  17. package/dist/export.es.js +1 -1
  18. package/dist/index.cjs.js +150 -150
  19. package/dist/index.es.js +26782 -27591
  20. package/dist/input-BfaSAGVw.js +1 -0
  21. package/dist/input-DVr_Qkl8.mjs +14 -0
  22. package/dist/rich-text.cjs.js +1 -1
  23. package/dist/rich-text.es.js +1 -1
  24. package/dist/security-CyBpuklN.mjs +122 -0
  25. package/dist/security-bFWwDrlg.js +1 -0
  26. package/dist/separator-NrkltulH.js +1 -0
  27. package/dist/separator-ibN2mycs.mjs +51 -0
  28. package/dist/src/components/editor/blocks/index.d.ts +51 -0
  29. package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
  30. package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
  31. package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
  32. package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
  33. package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
  34. package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
  35. package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
  36. package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
  37. package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
  38. package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
  39. package/dist/src/components/editor/index.d.ts +66 -0
  40. package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
  41. package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
  42. package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
  43. package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
  44. package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
  45. package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
  46. package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
  47. package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
  48. package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
  49. package/dist/src/components/editor/waka-editor.d.ts +59 -0
  50. package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
  51. package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
  52. package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
  53. package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
  54. package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
  55. package/dist/src/components/index.d.ts +1 -15
  56. package/dist/src/editor.d.ts +26 -0
  57. package/dist/textarea-CdQWggYG.js +1 -0
  58. package/dist/textarea-DJDXJ3nd.mjs +23 -0
  59. package/dist/types-C2St0wOW.js +1 -0
  60. package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
  61. package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
  62. package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
  63. package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
  64. package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
  65. package/dist/waka-editor-plugins-DR6tpsUC.mjs +135 -0
  66. package/dist/waka-editor-plugins-sGSh9hn2.js +1 -0
  67. package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
  68. package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
  69. package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
  70. package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
  71. package/package.json +87 -2
  72. package/src/blocks/footer/index.tsx +1 -6
  73. package/src/blocks/login/index.tsx +1 -7
  74. package/src/blocks/profile/index.tsx +3 -5
  75. package/src/components/editor/blocks/index.ts +182 -0
  76. package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
  77. package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
  78. package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
  79. package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
  80. package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
  81. package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
  82. package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
  83. package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
  84. package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
  85. package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
  86. package/src/components/editor/index.ts +279 -0
  87. package/src/components/editor/waka-ai-writer.tsx +434 -0
  88. package/src/components/editor/waka-collaborative-editor.tsx +426 -0
  89. package/src/components/editor/waka-diff-viewer.tsx +352 -0
  90. package/src/components/editor/waka-dnd-editor.tsx +284 -0
  91. package/src/components/editor/waka-document-editor.tsx +502 -0
  92. package/src/components/editor/waka-editor-elements.tsx +312 -0
  93. package/src/components/editor/waka-editor-leaves.tsx +101 -0
  94. package/src/components/editor/waka-editor-plugins.ts +207 -0
  95. package/src/components/editor/waka-editor-toolbar.tsx +358 -0
  96. package/src/components/editor/waka-editor.tsx +431 -0
  97. package/src/components/editor/waka-floating-toolbar.tsx +268 -0
  98. package/src/components/editor/waka-markdown-editor.tsx +395 -0
  99. package/src/components/editor/waka-mention-editor.tsx +459 -0
  100. package/src/components/editor/waka-slash-menu.tsx +392 -0
  101. package/src/components/editor/waka-spec-editor.tsx +657 -0
  102. package/src/components/index.ts +1 -18
  103. package/dist/chunk-BDDJmn7V.js +0 -1
  104. package/dist/cn-DnPbmOCy.js +0 -1
  105. package/dist/cn-DpLcAzrf.mjs +0 -22
  106. package/dist/separator-BDReXBvI.mjs +0 -59
  107. package/dist/separator-BKjNl9sI.js +0 -1
  108. package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
  109. package/dist/src/components/waka-actors-list/index.d.ts +0 -18
  110. package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
  111. package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
  112. package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
  113. package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
  114. package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
  115. package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
  116. package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
  117. package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
  118. package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
  119. package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
  120. package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
  121. package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
  122. package/dist/types-BH9cQRqZ.js +0 -1
  123. package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
  124. package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
  125. package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
  126. package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
  127. package/src/components/waka-actor-badge/index.tsx +0 -34
  128. package/src/components/waka-actors-list/index.tsx +0 -125
  129. package/src/components/waka-ai-assistant-button/index.tsx +0 -31
  130. package/src/components/waka-document-flyover/index.tsx +0 -36
  131. package/src/components/waka-document-preview-popup/index.tsx +0 -103
  132. package/src/components/waka-hour-balance-badge/index.tsx +0 -43
  133. package/src/components/waka-hour-consumption-table/index.tsx +0 -72
  134. package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
  135. package/src/components/waka-project-stats-header/index.tsx +0 -69
  136. package/src/components/waka-step-comment-bubble/index.tsx +0 -71
  137. package/src/components/waka-step-comment-panel/index.tsx +0 -106
  138. package/src/components/waka-step-permission-matrix/index.tsx +0 -65
  139. package/src/components/waka-time-entry-dialog/index.tsx +0 -131
  140. package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
@@ -0,0 +1,657 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import {
5
+ BookOpen,
6
+ CheckCircle2,
7
+ GitBranch,
8
+ Users,
9
+ AlertTriangle,
10
+ Shield,
11
+ Zap,
12
+ Database,
13
+ type LucideIcon,
14
+ } from "lucide-react"
15
+ import { cn } from "../../utils/cn"
16
+ import { Label } from "../label"
17
+ import { Textarea } from "../textarea"
18
+
19
+ // ─── Types ───────────────────────────────────────────────────────────────────
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ type SlateNode = any
23
+
24
+ /** A predefined spec block template */
25
+ export interface SpecBlockTemplate {
26
+ /** Unique key */
27
+ key: string
28
+ /** Display label */
29
+ label: string
30
+ /** Description for the slash menu */
31
+ description: string
32
+ /** Lucide icon */
33
+ icon: LucideIcon
34
+ /** Category for grouping */
35
+ category: string
36
+ /** Generates the Slate nodes for this block */
37
+ createNodes: () => SlateNode[]
38
+ }
39
+
40
+ export interface WakaSpecEditorProps {
41
+ /** Initial Slate content */
42
+ value?: SlateNode[]
43
+ /** Change callback */
44
+ onChange?: (value: SlateNode[]) => void
45
+ /** Read-only mode */
46
+ readOnly?: boolean
47
+ /** Placeholder */
48
+ placeholder?: string
49
+ /** CSS class */
50
+ className?: string
51
+ /** Editor CSS class */
52
+ editorClassName?: string
53
+ /** Minimum height in px */
54
+ minHeight?: number
55
+ /** Label */
56
+ label?: string
57
+ /** Description */
58
+ description?: string
59
+ /** Error */
60
+ error?: string
61
+ /** Custom spec block templates (merged with defaults) */
62
+ customTemplates?: SpecBlockTemplate[]
63
+ /** Show the template insertion sidebar */
64
+ showTemplateSidebar?: boolean
65
+ /** Enable AI features for spec generation */
66
+ enableAI?: boolean
67
+ /** AI endpoint */
68
+ aiEndpoint?: string
69
+ /** Document title */
70
+ title?: string
71
+ /** Title change callback */
72
+ onTitleChange?: (title: string) => void
73
+ /** Extra Plate plugins */
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ extraPlugins?: any[]
76
+ /** Ref to editor instance */
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ editorRef?: React.MutableRefObject<any>
79
+ }
80
+
81
+ // ─── Default spec block templates ────────────────────────────────────────────
82
+
83
+ /** Default specification block templates for WakaStart projects */
84
+ export const DEFAULT_SPEC_TEMPLATES: SpecBlockTemplate[] = [
85
+ {
86
+ key: "user-story",
87
+ label: "User Story",
88
+ description: "En tant que [role], je veux [action] afin de [benefice]",
89
+ icon: Users,
90
+ category: "Fonctionnel",
91
+ createNodes: () => [
92
+ { type: "callout", variant: "info", children: [{ text: "" }] },
93
+ { type: "h3", children: [{ text: "User Story" }] },
94
+ { type: "p", children: [
95
+ { text: "En tant que ", bold: true },
96
+ { text: "[role]" },
97
+ ]},
98
+ { type: "p", children: [
99
+ { text: "Je veux ", bold: true },
100
+ { text: "[action]" },
101
+ ]},
102
+ { type: "p", children: [
103
+ { text: "Afin de ", bold: true },
104
+ { text: "[benefice attendu]" },
105
+ ]},
106
+ { type: "p", children: [{ text: "" }] },
107
+ ],
108
+ },
109
+ {
110
+ key: "acceptance-criteria",
111
+ label: "Criteres d'acceptation",
112
+ description: "Given/When/Then avec scenarios de validation",
113
+ icon: CheckCircle2,
114
+ category: "Fonctionnel",
115
+ createNodes: () => [
116
+ { type: "h3", children: [{ text: "Criteres d'acceptation" }] },
117
+ { type: "table", children: [
118
+ { type: "tr", children: [
119
+ { type: "th", children: [{ type: "p", children: [{ text: "#", bold: true }] }] },
120
+ { type: "th", children: [{ type: "p", children: [{ text: "Scenario", bold: true }] }] },
121
+ { type: "th", children: [{ type: "p", children: [{ text: "Given", bold: true }] }] },
122
+ { type: "th", children: [{ type: "p", children: [{ text: "When", bold: true }] }] },
123
+ { type: "th", children: [{ type: "p", children: [{ text: "Then", bold: true }] }] },
124
+ ]},
125
+ { type: "tr", children: [
126
+ { type: "td", children: [{ type: "p", children: [{ text: "AC-01" }] }] },
127
+ { type: "td", children: [{ type: "p", children: [{ text: "Nominal" }] }] },
128
+ { type: "td", children: [{ type: "p", children: [{ text: "L'utilisateur est connecte" }] }] },
129
+ { type: "td", children: [{ type: "p", children: [{ text: "Il clique sur le bouton" }] }] },
130
+ { type: "td", children: [{ type: "p", children: [{ text: "Le resultat s'affiche" }] }] },
131
+ ]},
132
+ { type: "tr", children: [
133
+ { type: "td", children: [{ type: "p", children: [{ text: "AC-02" }] }] },
134
+ { type: "td", children: [{ type: "p", children: [{ text: "Erreur" }] }] },
135
+ { type: "td", children: [{ type: "p", children: [{ text: "Le service est indisponible" }] }] },
136
+ { type: "td", children: [{ type: "p", children: [{ text: "Il clique sur le bouton" }] }] },
137
+ { type: "td", children: [{ type: "p", children: [{ text: "Un message d'erreur s'affiche" }] }] },
138
+ ]},
139
+ ]},
140
+ { type: "p", children: [{ text: "" }] },
141
+ ],
142
+ },
143
+ {
144
+ key: "use-case",
145
+ label: "Cas d'utilisation",
146
+ description: "Acteur, pre-conditions, flux nominal, flux alternatifs",
147
+ icon: GitBranch,
148
+ category: "Fonctionnel",
149
+ createNodes: () => [
150
+ { type: "h3", children: [{ text: "Cas d'utilisation : [Nom]" }] },
151
+ { type: "column_group", children: [
152
+ { type: "column", children: [
153
+ { type: "h4", children: [{ text: "ACTEUR" }] },
154
+ { type: "p", children: [{ text: "[Role utilisateur]" }] },
155
+ { type: "h4", children: [{ text: "PRE-CONDITIONS" }] },
156
+ { type: "p", children: [{ text: "- L'utilisateur est authentifie" }] },
157
+ { type: "p", children: [{ text: "- [Autre condition]" }] },
158
+ { type: "h4", children: [{ text: "POST-CONDITIONS" }] },
159
+ { type: "p", children: [{ text: "- [Etat apres execution]" }] },
160
+ ]},
161
+ { type: "column", children: [
162
+ { type: "h4", children: [{ text: "FLUX NOMINAL" }] },
163
+ { type: "p", children: [{ text: "1. L'utilisateur accede a la page" }] },
164
+ { type: "p", children: [{ text: "2. Le systeme affiche le formulaire" }] },
165
+ { type: "p", children: [{ text: "3. L'utilisateur remplit et soumet" }] },
166
+ { type: "p", children: [{ text: "4. Le systeme enregistre et confirme" }] },
167
+ { type: "h4", children: [{ text: "FLUX ALTERNATIFS" }] },
168
+ { type: "p", children: [{ text: "3a. Donnees invalides : erreur affichee" }] },
169
+ ]},
170
+ ]},
171
+ { type: "p", children: [{ text: "" }] },
172
+ ],
173
+ },
174
+ {
175
+ key: "api-endpoint",
176
+ label: "Endpoint API",
177
+ description: "Specification d'un endpoint REST avec requete/reponse",
178
+ icon: Zap,
179
+ category: "Technique",
180
+ createNodes: () => [
181
+ { type: "h3", children: [{ text: "API: [METHOD] /api/v1/[resource]" }] },
182
+ { type: "table", children: [
183
+ { type: "tr", children: [
184
+ { type: "th", children: [{ type: "p", children: [{ text: "Propriete", bold: true }] }] },
185
+ { type: "th", children: [{ type: "p", children: [{ text: "Valeur", bold: true }] }] },
186
+ ]},
187
+ { type: "tr", children: [
188
+ { type: "td", children: [{ type: "p", children: [{ text: "Methode" }] }] },
189
+ { type: "td", children: [{ type: "p", children: [{ text: "GET | POST | PUT | DELETE" }] }] },
190
+ ]},
191
+ { type: "tr", children: [
192
+ { type: "td", children: [{ type: "p", children: [{ text: "Authentification" }] }] },
193
+ { type: "td", children: [{ type: "p", children: [{ text: "Bearer JWT" }] }] },
194
+ ]},
195
+ { type: "tr", children: [
196
+ { type: "td", children: [{ type: "p", children: [{ text: "Permissions" }] }] },
197
+ { type: "td", children: [{ type: "p", children: [{ text: "[ROLE_REQUIRED]" }] }] },
198
+ ]},
199
+ { type: "tr", children: [
200
+ { type: "td", children: [{ type: "p", children: [{ text: "Rate Limit" }] }] },
201
+ { type: "td", children: [{ type: "p", children: [{ text: "100 req/min" }] }] },
202
+ ]},
203
+ ]},
204
+ { type: "h4", children: [{ text: "CORPS DE LA REQUETE" }] },
205
+ { type: "blockquote", children: [{ text: "{ \"field\": \"value\" }" }] },
206
+ { type: "h4", children: [{ text: "REPONSE 200" }] },
207
+ { type: "blockquote", children: [{ text: "{ \"data\": { ... }, \"meta\": { \"total\": 42 } }" }] },
208
+ { type: "p", children: [{ text: "" }] },
209
+ ],
210
+ },
211
+ {
212
+ key: "data-model",
213
+ label: "Modele de donnees",
214
+ description: "Schema d'entite avec champs, types et contraintes",
215
+ icon: Database,
216
+ category: "Technique",
217
+ createNodes: () => [
218
+ { type: "h3", children: [{ text: "Modele : [NomEntite]" }] },
219
+ { type: "table", children: [
220
+ { type: "tr", children: [
221
+ { type: "th", children: [{ type: "p", children: [{ text: "Champ", bold: true }] }] },
222
+ { type: "th", children: [{ type: "p", children: [{ text: "Type", bold: true }] }] },
223
+ { type: "th", children: [{ type: "p", children: [{ text: "Nullable", bold: true }] }] },
224
+ { type: "th", children: [{ type: "p", children: [{ text: "Description", bold: true }] }] },
225
+ ]},
226
+ { type: "tr", children: [
227
+ { type: "td", children: [{ type: "p", children: [{ text: "id" }] }] },
228
+ { type: "td", children: [{ type: "p", children: [{ text: "UUID" }] }] },
229
+ { type: "td", children: [{ type: "p", children: [{ text: "Non" }] }] },
230
+ { type: "td", children: [{ type: "p", children: [{ text: "Identifiant unique" }] }] },
231
+ ]},
232
+ { type: "tr", children: [
233
+ { type: "td", children: [{ type: "p", children: [{ text: "createdAt" }] }] },
234
+ { type: "td", children: [{ type: "p", children: [{ text: "DateTime" }] }] },
235
+ { type: "td", children: [{ type: "p", children: [{ text: "Non" }] }] },
236
+ { type: "td", children: [{ type: "p", children: [{ text: "Date de creation" }] }] },
237
+ ]},
238
+ ]},
239
+ { type: "p", children: [{ text: "" }] },
240
+ ],
241
+ },
242
+ {
243
+ key: "security-requirements",
244
+ label: "Exigences de securite",
245
+ description: "Regles OWASP, chiffrement, acces, conformite",
246
+ icon: Shield,
247
+ category: "Non-fonctionnel",
248
+ createNodes: () => [
249
+ { type: "h3", children: [{ text: "Exigences de securite" }] },
250
+ { type: "callout", variant: "warning", children: [{ text: "Section obligatoire pour les modules HDS et les donnees sensibles" }] },
251
+ { type: "table", children: [
252
+ { type: "tr", children: [
253
+ { type: "th", children: [{ type: "p", children: [{ text: "Categorie", bold: true }] }] },
254
+ { type: "th", children: [{ type: "p", children: [{ text: "Exigence", bold: true }] }] },
255
+ { type: "th", children: [{ type: "p", children: [{ text: "Niveau", bold: true }] }] },
256
+ ]},
257
+ { type: "tr", children: [
258
+ { type: "td", children: [{ type: "p", children: [{ text: "Authentification" }] }] },
259
+ { type: "td", children: [{ type: "p", children: [{ text: "OAuth2 PKCE via Keycloak" }] }] },
260
+ { type: "td", children: [{ type: "p", children: [{ text: "Obligatoire" }] }] },
261
+ ]},
262
+ { type: "tr", children: [
263
+ { type: "td", children: [{ type: "p", children: [{ text: "Chiffrement" }] }] },
264
+ { type: "td", children: [{ type: "p", children: [{ text: "AES-256-GCM au repos, TLS 1.3 en transit" }] }] },
265
+ { type: "td", children: [{ type: "p", children: [{ text: "Obligatoire" }] }] },
266
+ ]},
267
+ { type: "tr", children: [
268
+ { type: "td", children: [{ type: "p", children: [{ text: "Audit" }] }] },
269
+ { type: "td", children: [{ type: "p", children: [{ text: "TracingInterceptor sur toutes les operations" }] }] },
270
+ { type: "td", children: [{ type: "p", children: [{ text: "Obligatoire" }] }] },
271
+ ]},
272
+ ]},
273
+ { type: "p", children: [{ text: "" }] },
274
+ ],
275
+ },
276
+ {
277
+ key: "risk-assessment",
278
+ label: "Risques et mitigations",
279
+ description: "Matrice de risques projet avec probabilite et impact",
280
+ icon: AlertTriangle,
281
+ category: "Non-fonctionnel",
282
+ createNodes: () => [
283
+ { type: "h3", children: [{ text: "Analyse des risques" }] },
284
+ { type: "table", children: [
285
+ { type: "tr", children: [
286
+ { type: "th", children: [{ type: "p", children: [{ text: "Risque", bold: true }] }] },
287
+ { type: "th", children: [{ type: "p", children: [{ text: "Probabilite", bold: true }] }] },
288
+ { type: "th", children: [{ type: "p", children: [{ text: "Impact", bold: true }] }] },
289
+ { type: "th", children: [{ type: "p", children: [{ text: "Mitigation", bold: true }] }] },
290
+ ]},
291
+ { type: "tr", children: [
292
+ { type: "td", children: [{ type: "p", children: [{ text: "[Description du risque]" }] }] },
293
+ { type: "td", children: [{ type: "p", children: [{ text: "Moyen" }] }] },
294
+ { type: "td", children: [{ type: "p", children: [{ text: "Haut" }] }] },
295
+ { type: "td", children: [{ type: "p", children: [{ text: "[Plan de mitigation]" }] }] },
296
+ ]},
297
+ ]},
298
+ { type: "p", children: [{ text: "" }] },
299
+ ],
300
+ },
301
+ {
302
+ key: "spec-header",
303
+ label: "En-tete de specification",
304
+ description: "Bloc d'en-tete avec version, auteur, statut, date",
305
+ icon: BookOpen,
306
+ category: "Document",
307
+ createNodes: () => [
308
+ { type: "h1", children: [{ text: "[Titre de la specification]" }] },
309
+ { type: "table", children: [
310
+ { type: "tr", children: [
311
+ { type: "td", children: [{ type: "p", children: [{ text: "Version", bold: true }] }] },
312
+ { type: "td", children: [{ type: "p", children: [{ text: "1.0.0" }] }] },
313
+ { type: "td", children: [{ type: "p", children: [{ text: "Statut", bold: true }] }] },
314
+ { type: "td", children: [{ type: "p", children: [{ text: "Brouillon" }] }] },
315
+ ]},
316
+ { type: "tr", children: [
317
+ { type: "td", children: [{ type: "p", children: [{ text: "Auteur", bold: true }] }] },
318
+ { type: "td", children: [{ type: "p", children: [{ text: "[Nom]" }] }] },
319
+ { type: "td", children: [{ type: "p", children: [{ text: "Date", bold: true }] }] },
320
+ { type: "td", children: [{ type: "p", children: [{ text: new Date().toLocaleDateString("fr-FR") }] }] },
321
+ ]},
322
+ ]},
323
+ { type: "h2", children: [{ text: "CONTEXTE" }] },
324
+ { type: "p", children: [{ text: "[Description du contexte et des objectifs]" }] },
325
+ { type: "h2", children: [{ text: "PERIMETRE" }] },
326
+ { type: "p", children: [{ text: "[Ce qui est inclus et exclu du perimetre]" }] },
327
+ { type: "p", children: [{ text: "" }] },
328
+ ],
329
+ },
330
+ ]
331
+
332
+ // ─── Component ───────────────────────────────────────────────────────────────
333
+
334
+ /**
335
+ * WakaSpecEditor -- Specification-oriented document editor.
336
+ *
337
+ * Extends the standard WakaEditor with domain-specific block templates
338
+ * for functional specifications, technical specs, and project documentation.
339
+ *
340
+ * Templates include: User Story, Acceptance Criteria, Use Case, API Endpoint,
341
+ * Data Model, Security Requirements, Risk Assessment, and Spec Header.
342
+ *
343
+ * Each template generates pre-formatted Slate nodes with placeholders
344
+ * that the user can fill in. Apps can register custom templates.
345
+ *
346
+ * Designed for WakaStart (project specs), WakaSign (contract specs),
347
+ * WakaPress (content specs), and TopFlix (content guidelines).
348
+ *
349
+ * @example
350
+ * ```tsx
351
+ * <WakaSpecEditor
352
+ * title="Specification fonctionnelle - Module HDS"
353
+ * onTitleChange={setTitle}
354
+ * showTemplateSidebar
355
+ * enableAI
356
+ * aiEndpoint="/api/ai/completion"
357
+ * value={content}
358
+ * onChange={setContent}
359
+ * />
360
+ * ```
361
+ */
362
+ export const WakaSpecEditor = React.forwardRef<HTMLDivElement, WakaSpecEditorProps>(
363
+ (
364
+ {
365
+ value,
366
+ onChange,
367
+ readOnly = false,
368
+ placeholder = "Commencez votre specification...",
369
+ className,
370
+ editorClassName,
371
+ minHeight = 500,
372
+ label,
373
+ description,
374
+ error,
375
+ customTemplates = [],
376
+ showTemplateSidebar = true,
377
+ enableAI = false,
378
+ aiEndpoint,
379
+ title,
380
+ onTitleChange,
381
+ extraPlugins,
382
+ editorRef,
383
+ },
384
+ ref
385
+ ) => {
386
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
387
+ const [bundle, setBundle] = React.useState<any>(null)
388
+ const [loadError, setLoadError] = React.useState(false)
389
+ const internalEditorRef = React.useRef<unknown>(null)
390
+ const effectiveEditorRef = editorRef ?? internalEditorRef
391
+
392
+ const allTemplates = React.useMemo(
393
+ () => [...DEFAULT_SPEC_TEMPLATES, ...customTemplates],
394
+ [customTemplates]
395
+ )
396
+
397
+ // Group templates by category
398
+ const templateGroups = React.useMemo(() => {
399
+ const map = new Map<string, SpecBlockTemplate[]>()
400
+ for (const tpl of allTemplates) {
401
+ const cat = tpl.category || "Autre"
402
+ if (!map.has(cat)) map.set(cat, [])
403
+ map.get(cat)!.push(tpl)
404
+ }
405
+ return map
406
+ }, [allTemplates])
407
+
408
+ // ── Load Plate ────────────────────────────────────────────────────────
409
+ React.useEffect(() => {
410
+ let cancelled = false
411
+
412
+ const load = async () => {
413
+ try {
414
+ const [plateReact, basicNodes, tableModule, layoutModule, calloutModule, linkModule, indentModule] =
415
+ await Promise.all([
416
+ import("platejs/react"),
417
+ import("@platejs/basic-nodes/react"),
418
+ import("@platejs/table/react"),
419
+ import("@platejs/layout/react"),
420
+ import("@platejs/callout/react"),
421
+ import("@platejs/link/react"),
422
+ import("@platejs/indent/react"),
423
+ ])
424
+
425
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
426
+ const plugins: any[] = [
427
+ basicNodes.BoldPlugin, basicNodes.ItalicPlugin, basicNodes.UnderlinePlugin,
428
+ basicNodes.StrikethroughPlugin, basicNodes.CodePlugin, basicNodes.HighlightPlugin,
429
+ basicNodes.SuperscriptPlugin, basicNodes.SubscriptPlugin,
430
+ basicNodes.H1Plugin, basicNodes.H2Plugin, basicNodes.H3Plugin,
431
+ basicNodes.H4Plugin, basicNodes.H5Plugin, basicNodes.H6Plugin,
432
+ basicNodes.BlockquotePlugin,
433
+ tableModule.TablePlugin, tableModule.TableRowPlugin,
434
+ tableModule.TableCellPlugin, tableModule.TableCellHeaderPlugin,
435
+ layoutModule.ColumnPlugin, layoutModule.ColumnItemPlugin,
436
+ calloutModule.CalloutPlugin,
437
+ linkModule.LinkPlugin.configure({
438
+ options: { allowedSchemes: ["http", "https", "mailto", "tel"] },
439
+ }),
440
+ indentModule.IndentPlugin,
441
+ ]
442
+
443
+ // Optional: TOC
444
+ try { const { TocPlugin } = await import("@platejs/toc/react"); plugins.push(TocPlugin) } catch { /* */ }
445
+
446
+ // Optional: Selection
447
+ try { const { BlockSelectionPlugin } = await import("@platejs/selection/react"); plugins.push(BlockSelectionPlugin) } catch { /* */ }
448
+
449
+ // Optional: Markdown
450
+ try { const { MarkdownPlugin } = await import("@platejs/markdown"); plugins.push(MarkdownPlugin) } catch { /* */ }
451
+
452
+ // Optional: AI
453
+ if (enableAI && aiEndpoint) {
454
+ try {
455
+ const { AIPlugin, AIChatPlugin } = await import("@platejs/ai/react")
456
+ plugins.push(
457
+ AIPlugin,
458
+ AIChatPlugin.configure({
459
+ options: { chat: { api: aiEndpoint, body: { system: "You are a specification writing assistant for a PaaS platform." } } },
460
+ }),
461
+ )
462
+ } catch { /* */ }
463
+ }
464
+
465
+ if (!cancelled) {
466
+ setBundle({
467
+ Plate: plateReact.Plate,
468
+ PlateContent: plateReact.PlateContent,
469
+ usePlateEditor: plateReact.usePlateEditor,
470
+ plugins,
471
+ })
472
+ }
473
+ } catch (err) {
474
+ console.error("[WakaSpecEditor] Failed to load:", err)
475
+ if (!cancelled) setLoadError(true)
476
+ }
477
+ }
478
+
479
+ load()
480
+ return () => { cancelled = true }
481
+ }, [enableAI, aiEndpoint])
482
+
483
+ // ── Insert template ───────────────────────────────────────────────────
484
+ const handleInsertTemplate = React.useCallback(
485
+ (template: SpecBlockTemplate) => {
486
+ const nodes = template.createNodes()
487
+ const currentValue = value ?? []
488
+ const newValue = [...currentValue, ...nodes]
489
+ onChange?.(newValue)
490
+ },
491
+ [value, onChange]
492
+ )
493
+
494
+ // ── Render ────────────────────────────────────────────────────────────
495
+ return (
496
+ <div ref={ref} className={cn("space-y-1.5", className)}>
497
+ {label && <Label>{label}</Label>}
498
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
499
+
500
+ <div className={cn(
501
+ "flex border rounded-lg overflow-hidden",
502
+ error && "border-destructive",
503
+ )}>
504
+ {/* Template sidebar */}
505
+ {showTemplateSidebar && !readOnly && (
506
+ <aside className="w-52 shrink-0 border-r bg-muted/20 hidden md:block">
507
+ <div className="p-3 border-b">
508
+ <h4 className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
509
+ Blocs specification
510
+ </h4>
511
+ </div>
512
+ <div className="p-2 space-y-3 max-h-[600px] overflow-y-auto">
513
+ {Array.from(templateGroups.entries()).map(([category, templates]) => (
514
+ <div key={category}>
515
+ <div className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground px-1 mb-1">
516
+ {category}
517
+ </div>
518
+ <div className="space-y-0.5">
519
+ {templates.map((tpl) => {
520
+ const Icon = tpl.icon
521
+ return (
522
+ <button
523
+ key={tpl.key}
524
+ type="button"
525
+ onClick={() => handleInsertTemplate(tpl)}
526
+ className={cn(
527
+ "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left",
528
+ "text-xs transition-colors cursor-pointer",
529
+ "hover:bg-accent/50 text-muted-foreground hover:text-foreground",
530
+ )}
531
+ title={tpl.description}
532
+ >
533
+ <Icon className="h-3.5 w-3.5 shrink-0" />
534
+ <span className="truncate">{tpl.label}</span>
535
+ </button>
536
+ )
537
+ })}
538
+ </div>
539
+ </div>
540
+ ))}
541
+ </div>
542
+ </aside>
543
+ )}
544
+
545
+ {/* Editor */}
546
+ <div className="flex-1 min-w-0">
547
+ {/* Title */}
548
+ {(title !== undefined || onTitleChange) && (
549
+ <div className="border-b px-6 py-4">
550
+ <input
551
+ type="text"
552
+ value={title ?? ""}
553
+ onChange={(e) => onTitleChange?.(e.target.value)}
554
+ placeholder="Titre de la specification"
555
+ readOnly={readOnly}
556
+ className={cn(
557
+ "w-full bg-transparent text-xl font-bold text-foreground",
558
+ "placeholder:text-muted-foreground/50 focus:outline-none",
559
+ )}
560
+ />
561
+ </div>
562
+ )}
563
+
564
+ {/* Plate editor */}
565
+ {bundle ? (
566
+ <SpecEditorInner
567
+ bundle={bundle}
568
+ value={value}
569
+ onChange={onChange}
570
+ readOnly={readOnly}
571
+ placeholder={placeholder}
572
+ editorClassName={editorClassName}
573
+ minHeight={minHeight}
574
+ extraPlugins={extraPlugins}
575
+ editorRef={effectiveEditorRef}
576
+ />
577
+ ) : (
578
+ <div className="relative" style={{ minHeight }}>
579
+ <div className="absolute inset-0 flex items-center justify-center bg-background/50">
580
+ <span className="text-sm text-muted-foreground animate-pulse">
581
+ {loadError ? "Erreur de chargement" : "Chargement de l'editeur..."}
582
+ </span>
583
+ </div>
584
+ </div>
585
+ )}
586
+ </div>
587
+ </div>
588
+
589
+ {error && <p className="text-sm text-destructive">{error}</p>}
590
+ </div>
591
+ )
592
+ }
593
+ )
594
+
595
+ WakaSpecEditor.displayName = "WakaSpecEditor"
596
+
597
+ // ─── Inner Plate component ──────────────────────────────────────────────────
598
+
599
+ function SpecEditorInner({
600
+ bundle: { Plate, PlateContent, usePlateEditor, plugins },
601
+ value,
602
+ onChange,
603
+ readOnly,
604
+ placeholder,
605
+ editorClassName,
606
+ minHeight,
607
+ extraPlugins,
608
+ editorRef,
609
+ }: {
610
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
611
+ bundle: any
612
+ value?: SlateNode[]
613
+ onChange?: (value: SlateNode[]) => void
614
+ readOnly?: boolean
615
+ placeholder?: string
616
+ editorClassName?: string
617
+ minHeight?: number
618
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
619
+ extraPlugins?: any[]
620
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
621
+ editorRef?: React.MutableRefObject<any>
622
+ }) {
623
+ const allPlugins = React.useMemo(
624
+ () => [...plugins, ...(extraPlugins ?? [])],
625
+ [plugins, extraPlugins]
626
+ )
627
+
628
+ const editor = usePlateEditor({
629
+ plugins: allPlugins,
630
+ value: value ?? [{ type: "p", children: [{ text: "" }] }],
631
+ })
632
+
633
+ React.useEffect(() => {
634
+ if (editorRef) editorRef.current = editor
635
+ }, [editor, editorRef])
636
+
637
+ return (
638
+ <Plate
639
+ editor={editor}
640
+ onChange={onChange ? ({ value: v }: { value: SlateNode[] }) => onChange(v) : undefined}
641
+ readOnly={readOnly}
642
+ >
643
+ <div style={{ minHeight }}>
644
+ <PlateContent
645
+ placeholder={placeholder}
646
+ readOnly={readOnly}
647
+ className={cn(
648
+ "prose prose-sm max-w-none px-6 py-4",
649
+ "focus-within:outline-none",
650
+ "[&_[data-slate-editor]]:outline-none [&_[data-slate-editor]]:min-h-[inherit]",
651
+ editorClassName,
652
+ )}
653
+ />
654
+ </div>
655
+ </Plate>
656
+ )
657
+ }
@@ -716,23 +716,6 @@ export * from './waka-doc-nav'
716
716
  export * from './waka-doc-search'
717
717
  export * from './waka-doc-version'
718
718
  export * from './waka-doc-toolbar'
719
- // waka-doc-breadcrumb: BreadcrumbItem conflicts with waka-breadcrumb, re-export only the component
720
- export { WakaDocBreadcrumb, type WakaDocBreadcrumbProps } from './waka-doc-breadcrumb'
719
+ export * from './waka-doc-breadcrumb'
721
720
  export * from './waka-doc-table'
722
721
  export * from './waka-doc-renderer'
723
-
724
- // WakaProject Sprint Components
725
- export * from './waka-hour-balance-badge'
726
- export * from './waka-actor-badge'
727
- export * from './waka-step-comment-bubble'
728
- export * from './waka-step-comment-panel'
729
- export * from './waka-time-entry-dialog'
730
- export * from './waka-time-tracking-flyover'
731
- export * from './waka-document-preview-popup'
732
- export * from './waka-document-flyover'
733
- export * from './waka-ai-assistant-button'
734
- export * from './waka-step-permission-matrix'
735
- export * from './waka-actors-list'
736
- export * from './waka-project-stats-header'
737
- export * from './waka-hour-pack-dialog'
738
- export * from './waka-hour-consumption-table'
@@ -1 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return o}});