@wakastellar/ui 3.3.3 → 3.5.2

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 (149) 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 +25694 -26503
  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/blocks/login/index.d.ts +4 -4
  29. package/dist/src/components/editor/blocks/index.d.ts +51 -0
  30. package/dist/src/components/editor/blocks/waka-acceptance-criteria-block.d.ts +60 -0
  31. package/dist/src/components/editor/blocks/waka-ai-assist-block.d.ts +58 -0
  32. package/dist/src/components/editor/blocks/waka-api-endpoint-block.d.ts +63 -0
  33. package/dist/src/components/editor/blocks/waka-code-playground-block.d.ts +61 -0
  34. package/dist/src/components/editor/blocks/waka-comment-thread-block.d.ts +85 -0
  35. package/dist/src/components/editor/blocks/waka-diagram-block.d.ts +52 -0
  36. package/dist/src/components/editor/blocks/waka-embed-block.d.ts +58 -0
  37. package/dist/src/components/editor/blocks/waka-slash-menu-block.d.ts +67 -0
  38. package/dist/src/components/editor/blocks/waka-user-story-block.d.ts +79 -0
  39. package/dist/src/components/editor/blocks/waka-version-diff-block.d.ts +73 -0
  40. package/dist/src/components/editor/index.d.ts +66 -0
  41. package/dist/src/components/editor/waka-ai-writer.d.ts +80 -0
  42. package/dist/src/components/editor/waka-collaborative-editor.d.ts +93 -0
  43. package/dist/src/components/editor/waka-diff-viewer.d.ts +71 -0
  44. package/dist/src/components/editor/waka-dnd-editor.d.ts +64 -0
  45. package/dist/src/components/editor/waka-document-editor.d.ts +92 -0
  46. package/dist/src/components/editor/waka-editor-elements.d.ts +79 -0
  47. package/dist/src/components/editor/waka-editor-leaves.d.ts +39 -0
  48. package/dist/src/components/editor/waka-editor-plugins.d.ts +41 -0
  49. package/dist/src/components/editor/waka-editor-toolbar.d.ts +20 -0
  50. package/dist/src/components/editor/waka-editor.d.ts +59 -0
  51. package/dist/src/components/editor/waka-floating-toolbar.d.ts +47 -0
  52. package/dist/src/components/editor/waka-markdown-editor.d.ts +60 -0
  53. package/dist/src/components/editor/waka-mention-editor.d.ts +125 -0
  54. package/dist/src/components/editor/waka-slash-menu.d.ts +70 -0
  55. package/dist/src/components/editor/waka-spec-editor.d.ts +88 -0
  56. package/dist/src/components/index.d.ts +1 -15
  57. package/dist/src/editor.d.ts +26 -0
  58. package/dist/src/stories/editor/EditorStoryWrapper.d.ts +11 -0
  59. package/dist/textarea-CdQWggYG.js +1 -0
  60. package/dist/textarea-DJDXJ3nd.mjs +23 -0
  61. package/dist/types-C2St0wOW.js +1 -0
  62. package/dist/{types-B6GVaSIP.mjs → types-JnqoLyuv.mjs} +214 -211
  63. package/dist/{useDataTableImport-BPvfo--2.mjs → useDataTableImport-BWUFesPi.mjs} +3 -3
  64. package/dist/{useDataTableImport-Cm_pCKnO.js → useDataTableImport-T7ddpN5k.js} +3 -3
  65. package/dist/waka-doc-renderer-CTxC7Trf.js +3 -0
  66. package/dist/{waka-doc-renderer-BkIvas3z.mjs → waka-doc-renderer-Cw-Xnyen.mjs} +264 -281
  67. package/dist/waka-editor-plugins-CGojOMS5.js +1 -0
  68. package/dist/waka-editor-plugins-Dwh4Vreq.mjs +135 -0
  69. package/dist/waka-rich-text-editor-BlIdtknG.js +1 -0
  70. package/dist/waka-rich-text-editor-D1uA3zbB.js +1 -0
  71. package/dist/waka-rich-text-editor-DgSWiXMW.mjs +342 -0
  72. package/dist/waka-rich-text-editor-DndVJuDw.mjs +2 -0
  73. package/package.json +87 -2
  74. package/src/blocks/footer/index.tsx +4 -9
  75. package/src/blocks/login/index.tsx +14 -20
  76. package/src/blocks/profile/index.tsx +5 -8
  77. package/src/blocks/sidebar/index.tsx +0 -2
  78. package/src/components/dropdown-menu/DropdownMenu.stories.tsx +2 -2
  79. package/src/components/editor/blocks/index.ts +182 -0
  80. package/src/components/editor/blocks/waka-acceptance-criteria-block.tsx +326 -0
  81. package/src/components/editor/blocks/waka-ai-assist-block.tsx +284 -0
  82. package/src/components/editor/blocks/waka-api-endpoint-block.tsx +382 -0
  83. package/src/components/editor/blocks/waka-code-playground-block.tsx +331 -0
  84. package/src/components/editor/blocks/waka-comment-thread-block.tsx +448 -0
  85. package/src/components/editor/blocks/waka-diagram-block.tsx +293 -0
  86. package/src/components/editor/blocks/waka-embed-block.tsx +416 -0
  87. package/src/components/editor/blocks/waka-slash-menu-block.tsx +432 -0
  88. package/src/components/editor/blocks/waka-user-story-block.tsx +295 -0
  89. package/src/components/editor/blocks/waka-version-diff-block.tsx +426 -0
  90. package/src/components/editor/index.ts +279 -0
  91. package/src/components/editor/waka-ai-writer.tsx +434 -0
  92. package/src/components/editor/waka-collaborative-editor.tsx +426 -0
  93. package/src/components/editor/waka-diff-viewer.tsx +352 -0
  94. package/src/components/editor/waka-dnd-editor.tsx +284 -0
  95. package/src/components/editor/waka-document-editor.tsx +502 -0
  96. package/src/components/editor/waka-editor-elements.tsx +312 -0
  97. package/src/components/editor/waka-editor-leaves.tsx +101 -0
  98. package/src/components/editor/waka-editor-plugins.ts +207 -0
  99. package/src/components/editor/waka-editor-toolbar.tsx +358 -0
  100. package/src/components/editor/waka-editor.tsx +431 -0
  101. package/src/components/editor/waka-floating-toolbar.tsx +268 -0
  102. package/src/components/editor/waka-markdown-editor.tsx +395 -0
  103. package/src/components/editor/waka-mention-editor.tsx +459 -0
  104. package/src/components/editor/waka-slash-menu.tsx +392 -0
  105. package/src/components/editor/waka-spec-editor.tsx +657 -0
  106. package/src/components/index.ts +1 -18
  107. package/src/components/waka-approval-chain/WakaApprovalChain.stories.tsx +15 -17
  108. package/src/components/waka-compare-period/WakaComparePeriod.stories.tsx +35 -19
  109. package/src/components/waka-content-recommendation/WakaContentRecommendation.stories.tsx +18 -5
  110. package/src/components/waka-password-strength/WakaPasswordStrength.stories.tsx +11 -34
  111. package/src/components/waka-streak-counter/WakaStreakCounter.stories.tsx +30 -30
  112. package/dist/chunk-BDDJmn7V.js +0 -1
  113. package/dist/cn-DnPbmOCy.js +0 -1
  114. package/dist/cn-DpLcAzrf.mjs +0 -22
  115. package/dist/separator-BDReXBvI.mjs +0 -59
  116. package/dist/separator-BKjNl9sI.js +0 -1
  117. package/dist/src/components/waka-actor-badge/index.d.ts +0 -8
  118. package/dist/src/components/waka-actors-list/index.d.ts +0 -18
  119. package/dist/src/components/waka-ai-assistant-button/index.d.ts +0 -8
  120. package/dist/src/components/waka-document-flyover/index.d.ts +0 -10
  121. package/dist/src/components/waka-document-preview-popup/index.d.ts +0 -26
  122. package/dist/src/components/waka-hour-balance-badge/index.d.ts +0 -8
  123. package/dist/src/components/waka-hour-consumption-table/index.d.ts +0 -15
  124. package/dist/src/components/waka-hour-pack-dialog/index.d.ts +0 -8
  125. package/dist/src/components/waka-project-stats-header/index.d.ts +0 -15
  126. package/dist/src/components/waka-step-comment-bubble/index.d.ts +0 -13
  127. package/dist/src/components/waka-step-comment-panel/index.d.ts +0 -20
  128. package/dist/src/components/waka-step-permission-matrix/index.d.ts +0 -12
  129. package/dist/src/components/waka-time-entry-dialog/index.d.ts +0 -16
  130. package/dist/src/components/waka-time-tracking-flyover/index.d.ts +0 -11
  131. package/dist/types-BH9cQRqZ.js +0 -1
  132. package/dist/waka-doc-renderer-BZ2-SqyT.js +0 -3
  133. package/dist/waka-rich-text-editor-BJGlQgpq.js +0 -1
  134. package/dist/waka-rich-text-editor-BJzzxeP1.mjs +0 -361
  135. package/dist/waka-rich-text-editor-wnXLwvUo.js +0 -1
  136. package/src/components/waka-actor-badge/index.tsx +0 -34
  137. package/src/components/waka-actors-list/index.tsx +0 -125
  138. package/src/components/waka-ai-assistant-button/index.tsx +0 -31
  139. package/src/components/waka-document-flyover/index.tsx +0 -36
  140. package/src/components/waka-document-preview-popup/index.tsx +0 -103
  141. package/src/components/waka-hour-balance-badge/index.tsx +0 -43
  142. package/src/components/waka-hour-consumption-table/index.tsx +0 -72
  143. package/src/components/waka-hour-pack-dialog/index.tsx +0 -72
  144. package/src/components/waka-project-stats-header/index.tsx +0 -69
  145. package/src/components/waka-step-comment-bubble/index.tsx +0 -71
  146. package/src/components/waka-step-comment-panel/index.tsx +0 -106
  147. package/src/components/waka-step-permission-matrix/index.tsx +0 -65
  148. package/src/components/waka-time-entry-dialog/index.tsx +0 -131
  149. package/src/components/waka-time-tracking-flyover/index.tsx +0 -41
@@ -7,13 +7,7 @@ import { Input } from "../../components/input"
7
7
  import { Label } from "../../components/label"
8
8
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../components/card"
9
9
  import { Separator } from "../../components/separator"
10
- import { Mail, Apple } from "lucide-react"
11
-
12
- // Inline SVG for renamed/removed lucide icons
13
- const Github = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
14
- const Linkedin = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/></svg>
15
- const Facebook = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
16
- const Instagram = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="20" height="20" x="2" y="2" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" x2="17.51" y1="6.5" y2="6.5"/></svg>
10
+ import { Globe, Mail, Apple, ExternalLink, Users, Camera } from "lucide-react"
17
11
  import { useTheme } from "../../context/theme-provider"
18
12
  import type { LoginConfig } from "./types"
19
13
 
@@ -44,9 +38,9 @@ export interface LoginProps {
44
38
  onSSOMicrosoft?: () => void
45
39
  onSSOApple?: () => void
46
40
  onSSOLinkedIn?: () => void
47
- onSSOGithub?: () => void
48
- onSSOFacebook?: () => void
49
- onSSOInstagram?: () => void
41
+ onSSOGlobe?: () => void
42
+ onSSOUsers?: () => void
43
+ onSSOCamera?: () => void
50
44
  onSSOFranceConnect?: () => void
51
45
  /** Afficher la connexion sociale (déprécié, utilise config.signup_options) */
52
46
  showSocialLogin?: boolean
@@ -69,9 +63,9 @@ export function Login({
69
63
  onSSOMicrosoft,
70
64
  onSSOApple,
71
65
  onSSOLinkedIn,
72
- onSSOGithub,
73
- onSSOFacebook,
74
- onSSOInstagram,
66
+ onSSOGlobe,
67
+ onSSOUsers,
68
+ onSSOCamera,
75
69
  onSSOFranceConnect,
76
70
  showSocialLogin = false,
77
71
  className,
@@ -119,8 +113,8 @@ export function Login({
119
113
  // Mode legacy (sans config)
120
114
  if (!config?.signup_options) {
121
115
  if (showSocialLogin) {
122
- if (onSSOGithub) {
123
- ssoList.push({ id: "github", label: "GitHub", icon: <Github className="h-4 w-4" />, onClick: onSSOGithub })
116
+ if (onSSOGlobe) {
117
+ ssoList.push({ id: "github", label: "GitHub", icon: <Globe className="h-4 w-4" />, onClick: onSSOGlobe })
124
118
  }
125
119
  if (onSSOGoogle) {
126
120
  ssoList.push({ id: "google", label: "Google", icon: <Mail className="h-4 w-4" />, onClick: onSSOGoogle })
@@ -143,23 +137,23 @@ export function Login({
143
137
  ssoList.push({ id: "apple", label: "Apple", icon: <Apple className="h-4 w-4" />, onClick: onSSOApple })
144
138
  }
145
139
  if (options.allow_sso_linkedIn) {
146
- ssoList.push({ id: "linkedin", label: "LinkedIn", icon: <Linkedin className="h-4 w-4" />, onClick: onSSOLinkedIn })
140
+ ssoList.push({ id: "linkedin", label: "LinkedIn", icon: <ExternalLink className="h-4 w-4" />, onClick: onSSOLinkedIn })
147
141
  }
148
142
  if (options.allow_sso_github) {
149
- ssoList.push({ id: "github", label: "GitHub", icon: <Github className="h-4 w-4" />, onClick: onSSOGithub })
143
+ ssoList.push({ id: "github", label: "GitHub", icon: <Globe className="h-4 w-4" />, onClick: onSSOGlobe })
150
144
  }
151
145
  if (options.allow_sso_facebook) {
152
- ssoList.push({ id: "facebook", label: "Facebook", icon: <Facebook className="h-4 w-4" />, onClick: onSSOFacebook })
146
+ ssoList.push({ id: "facebook", label: "Users", icon: <Users className="h-4 w-4" />, onClick: onSSOUsers })
153
147
  }
154
148
  if (options.allow_sso_instagram) {
155
- ssoList.push({ id: "instagram", label: "Instagram", icon: <Instagram className="h-4 w-4" />, onClick: onSSOInstagram })
149
+ ssoList.push({ id: "instagram", label: "Camera", icon: <Camera className="h-4 w-4" />, onClick: onSSOCamera })
156
150
  }
157
151
  if (options.allow_sso_france_connect) {
158
152
  ssoList.push({ id: "france-connect", label: "France Connect", icon: <Mail className="h-4 w-4" />, onClick: onSSOFranceConnect })
159
153
  }
160
154
 
161
155
  return ssoList
162
- }, [config?.signup_options, showSocialLogin, onSSOGoogle, onSSOMicrosoft, onSSOApple, onSSOLinkedIn, onSSOGithub, onSSOFacebook, onSSOInstagram, onSSOFranceConnect])
156
+ }, [config?.signup_options, showSocialLogin, onSSOGoogle, onSSOMicrosoft, onSSOApple, onSSOLinkedIn, onSSOGlobe, onSSOUsers, onSSOCamera, onSSOFranceConnect])
163
157
 
164
158
  // Vérifie si la connexion classique est autorisée
165
159
  // Si config n'est pas fourni, on autorise le login par défaut
@@ -33,13 +33,10 @@ import {
33
33
  X,
34
34
  Loader2,
35
35
  Link as LinkIcon,
36
+ Share2,
37
+ ExternalLink,
36
38
  } from "lucide-react"
37
39
 
38
- // Inline SVG for renamed/removed lucide icons
39
- const Twitter = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"/></svg>
40
- const Github = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
41
- const Linkedin = (props: any) => <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/></svg>
42
-
43
40
  // ============================================
44
41
  // TYPES
45
42
  // ============================================
@@ -330,11 +327,11 @@ function SocialLinks({ links, isEditing }: SocialLinksProps) {
330
327
  const getIcon = (type: ProfileSocialLink["type"]) => {
331
328
  switch (type) {
332
329
  case "twitter":
333
- return <Twitter className="h-4 w-4" />
330
+ return <Share2 className="h-4 w-4" />
334
331
  case "github":
335
- return <Github className="h-4 w-4" />
332
+ return <Globe className="h-4 w-4" />
336
333
  case "linkedin":
337
- return <Linkedin className="h-4 w-4" />
334
+ return <ExternalLink className="h-4 w-4" />
338
335
  case "website":
339
336
  return <Globe className="h-4 w-4" />
340
337
  default:
@@ -596,8 +596,6 @@ export function WakaSidebar({
596
596
  "bg-[var(--sidebar-bg)] text-[var(--sidebar-text)]",
597
597
  "[&_.bg-sidebar-active]:bg-[var(--sidebar-active)]",
598
598
  "[&_.text-sidebar-active-foreground]:text-[var(--sidebar-active-foreground)]",
599
- "[&_.text-sidebar-foreground\\/70]:text-[var(--sidebar-text)]",
600
- "[&_.text-sidebar-foreground\\/70]:opacity-70",
601
599
  "[&_.border-sidebar-active]:border-[var(--sidebar-active)]",
602
600
  contentClassName
603
601
  )
@@ -27,7 +27,7 @@ import {
27
27
  MessageSquare,
28
28
  PlusCircle,
29
29
  Plus,
30
- Github,
30
+ Globe,
31
31
  LifeBuoy,
32
32
  Cloud,
33
33
  LogOut,
@@ -190,7 +190,7 @@ export const WithSubmenu: Story = {
190
190
  </DropdownMenuGroup>
191
191
  <DropdownMenuSeparator />
192
192
  <DropdownMenuItem>
193
- <Github className="mr-2 h-4 w-4" />
193
+ <Globe className="mr-2 h-4 w-4" />
194
194
  <span>GitHub</span>
195
195
  </DropdownMenuItem>
196
196
  <DropdownMenuItem>
@@ -0,0 +1,182 @@
1
+ /**
2
+ * WakaStart Editor Blocks — Custom Plate.js block elements for the WakaStart ecosystem.
3
+ *
4
+ * These blocks extend the standard Plate editor with domain-specific elements
5
+ * designed for PaaS project management, specification writing, and technical documentation.
6
+ *
7
+ * Usage:
8
+ * ```ts
9
+ * import { WakaUserStoryBlock, createUserStoryPlugin } from "@wakastellar/ui/editor"
10
+ * ```
11
+ *
12
+ * Each block provides:
13
+ * - A React component for rendering in the Plate editor
14
+ * - A node factory function for creating Slate nodes
15
+ * - A plugin factory for registering with the Plate editor
16
+ * - Full TypeScript types for the Slate element shape
17
+ */
18
+
19
+ // ── User Story Block ────────────────────────────────────────────────────────
20
+ export {
21
+ WakaUserStoryBlock,
22
+ createUserStoryNodes,
23
+ createUserStoryPlugin,
24
+ USER_STORY_BLOCK_TYPE,
25
+ type UserStoryElement,
26
+ type WakaUserStoryBlockProps,
27
+ } from "./waka-user-story-block"
28
+
29
+ // ── Acceptance Criteria Block ───────────────────────────────────────────────
30
+ export {
31
+ WakaAcceptanceCriteriaBlock,
32
+ createAcceptanceCriteriaNodes,
33
+ createAcceptanceCriteriaPlugin,
34
+ ACCEPTANCE_CRITERIA_BLOCK_TYPE,
35
+ type AcceptanceCriterion,
36
+ type AcceptanceCriteriaElement,
37
+ type WakaAcceptanceCriteriaBlockProps,
38
+ } from "./waka-acceptance-criteria-block"
39
+
40
+ // ── API Endpoint Block ──────────────────────────────────────────────────────
41
+ export {
42
+ WakaApiEndpointBlock,
43
+ createApiEndpointNodes,
44
+ createApiEndpointPlugin,
45
+ API_ENDPOINT_BLOCK_TYPE,
46
+ type HttpMethod,
47
+ type ApiParam,
48
+ type ApiResponse,
49
+ type ApiEndpointElement,
50
+ type WakaApiEndpointBlockProps,
51
+ } from "./waka-api-endpoint-block"
52
+
53
+ // ── Diagram Block ───────────────────────────────────────────────────────────
54
+ export {
55
+ WakaDiagramBlock,
56
+ createDiagramNodes,
57
+ createDiagramPlugin,
58
+ DIAGRAM_BLOCK_TYPE,
59
+ type DiagramSyntax,
60
+ type DiagramElement,
61
+ type WakaDiagramBlockProps,
62
+ } from "./waka-diagram-block"
63
+
64
+ // ── AI Assist Block ─────────────────────────────────────────────────────────
65
+ export {
66
+ WakaAiAssistBlock,
67
+ createAiAssistNodes,
68
+ createAiAssistPlugin,
69
+ AI_ASSIST_BLOCK_TYPE,
70
+ type AiAssistStatus,
71
+ type AiAssistElement,
72
+ type WakaAiAssistBlockProps,
73
+ } from "./waka-ai-assist-block"
74
+
75
+ // ── Version Diff Block ──────────────────────────────────────────────────────
76
+ export {
77
+ WakaVersionDiffBlock,
78
+ createVersionDiffNodes,
79
+ createVersionDiffPlugin,
80
+ VERSION_DIFF_BLOCK_TYPE,
81
+ type DiffLine,
82
+ type DiffHunk,
83
+ type VersionDiffElement,
84
+ type WakaVersionDiffBlockProps,
85
+ } from "./waka-version-diff-block"
86
+
87
+ // ── Embed Block ─────────────────────────────────────────────────────────────
88
+ export {
89
+ WakaEmbedBlock,
90
+ createEmbedNodes,
91
+ createEmbedPlugin,
92
+ detectEmbedProvider,
93
+ resolveEmbedUrl,
94
+ EMBED_BLOCK_TYPE,
95
+ type EmbedProvider,
96
+ type EmbedElement,
97
+ type WakaEmbedBlockProps,
98
+ } from "./waka-embed-block"
99
+
100
+ // ── Code Playground Block ───────────────────────────────────────────────────
101
+ export {
102
+ WakaCodePlaygroundBlock,
103
+ createCodePlaygroundNodes,
104
+ createCodePlaygroundPlugin,
105
+ CODE_PLAYGROUND_BLOCK_TYPE,
106
+ type PlaygroundLanguage,
107
+ type ExecutionStatus,
108
+ type CodePlaygroundElement,
109
+ type WakaCodePlaygroundBlockProps,
110
+ } from "./waka-code-playground-block"
111
+
112
+ // ── Comment Thread Block ────────────────────────────────────────────────────
113
+ export {
114
+ WakaCommentThreadBlock,
115
+ createCommentThreadNodes,
116
+ createCommentThreadPlugin,
117
+ COMMENT_THREAD_BLOCK_TYPE,
118
+ type ThreadComment,
119
+ type ThreadStatus,
120
+ type CommentThreadElement,
121
+ type WakaCommentThreadBlockProps,
122
+ } from "./waka-comment-thread-block"
123
+
124
+ // ── Slash Menu with WakaStart Blocks ────────────────────────────────────────
125
+ export {
126
+ WakaSlashMenuBlock,
127
+ getAllWakaSlashCommands,
128
+ createWakaSlashPlugins,
129
+ WAKA_BLOCK_COMMANDS,
130
+ type WakaSlashBlockCommand,
131
+ type WakaSlashMenuBlockProps,
132
+ } from "./waka-slash-menu-block"
133
+
134
+ // ── Convenience: load all block plugins at once ─────────────────────────────
135
+
136
+ /**
137
+ * Loads all WakaStart block plugins at once.
138
+ * Returns an array of Plate plugins to spread into your editor configuration.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * const blockPlugins = await loadAllWakaBlockPlugins()
143
+ * const editor = usePlateEditor({
144
+ * plugins: [...corePlugins, ...blockPlugins],
145
+ * })
146
+ * ```
147
+ */
148
+ export async function loadAllWakaBlockPlugins(options?: {
149
+ /** Mermaid/diagram render function */
150
+ diagramRenderer?: (source: string, syntax: string) => Promise<string>
151
+ /** AI generation function */
152
+ aiGenerator?: (prompt: string) => Promise<string>
153
+ /** Code execution function */
154
+ codeExecutor?: (code: string, language: string) => Promise<string>
155
+ /** Current user ID for comments */
156
+ currentUserId?: string
157
+ /** Comment reply handler */
158
+ onCommentReply?: (threadId: string, content: string) => void
159
+ }): Promise<unknown[]> {
160
+ const plugins: unknown[] = []
161
+
162
+ const loaders = [
163
+ createUserStoryPlugin(),
164
+ createAcceptanceCriteriaPlugin(),
165
+ createApiEndpointPlugin(),
166
+ createDiagramPlugin(options?.diagramRenderer as never),
167
+ createAiAssistPlugin(options?.aiGenerator),
168
+ createVersionDiffPlugin(),
169
+ createEmbedPlugin(),
170
+ createCodePlaygroundPlugin(options?.codeExecutor as never),
171
+ createCommentThreadPlugin(options?.currentUserId, options?.onCommentReply),
172
+ ]
173
+
174
+ const results = await Promise.allSettled(loaders)
175
+ for (const result of results) {
176
+ if (result.status === "fulfilled" && result.value) {
177
+ plugins.push(result.value)
178
+ }
179
+ }
180
+
181
+ return plugins
182
+ }
@@ -0,0 +1,326 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../../utils/cn"
5
+ import type { PlateElementProps } from "../waka-editor-elements"
6
+
7
+ // ============================================================================
8
+ // Types
9
+ // ============================================================================
10
+
11
+ export const ACCEPTANCE_CRITERIA_BLOCK_TYPE = "acceptance_criteria" as const
12
+
13
+ /** A single acceptance criterion in Gherkin format */
14
+ export interface AcceptanceCriterion {
15
+ /** Unique ID (e.g. "AC-01") */
16
+ id: string
17
+ /** Scenario name */
18
+ scenario: string
19
+ /** Given (context / precondition) */
20
+ given: string
21
+ /** When (action / trigger) */
22
+ when: string
23
+ /** Then (expected result) */
24
+ then: string
25
+ /** Whether this criterion has been validated */
26
+ validated: boolean
27
+ }
28
+
29
+ /** Slate node for acceptance criteria block */
30
+ export interface AcceptanceCriteriaElement {
31
+ type: typeof ACCEPTANCE_CRITERIA_BLOCK_TYPE
32
+ /** Block title */
33
+ title: string
34
+ /** List of criteria */
35
+ criteria: AcceptanceCriterion[]
36
+ children: Array<{ text: string }>
37
+ }
38
+
39
+ export interface WakaAcceptanceCriteriaBlockProps extends PlateElementProps {
40
+ element?: AcceptanceCriteriaElement & Record<string, unknown>
41
+ /** Whether the block is read-only */
42
+ readOnly?: boolean
43
+ }
44
+
45
+ // ============================================================================
46
+ // Gherkin keyword badge
47
+ // ============================================================================
48
+
49
+ function GherkinKeyword({ keyword, variant }: { keyword: string; variant: "given" | "when" | "then" }) {
50
+ const styles = {
51
+ given: "bg-violet-100 text-violet-700 dark:bg-violet-500/15 dark:text-violet-400",
52
+ when: "bg-amber-100 text-amber-700 dark:bg-amber-500/15 dark:text-amber-400",
53
+ then: "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-400",
54
+ }
55
+
56
+ return (
57
+ <span className={cn(
58
+ "inline-block text-[10px] font-bold uppercase tracking-wider px-1.5 py-0.5 rounded shrink-0 w-[52px] text-center",
59
+ styles[variant]
60
+ )}>
61
+ {keyword}
62
+ </span>
63
+ )
64
+ }
65
+
66
+ // ============================================================================
67
+ // Criterion Row
68
+ // ============================================================================
69
+
70
+ interface CriterionRowProps {
71
+ criterion: AcceptanceCriterion
72
+ index: number
73
+ readOnly?: boolean
74
+ }
75
+
76
+ function CriterionRow({ criterion, index, readOnly }: CriterionRowProps) {
77
+ const [isExpanded, setIsExpanded] = React.useState(true)
78
+
79
+ return (
80
+ <div className={cn(
81
+ "rounded-lg border transition-all duration-200",
82
+ criterion.validated
83
+ ? "border-emerald-200 dark:border-emerald-800/40 bg-emerald-50/30 dark:bg-emerald-950/10"
84
+ : "border-border bg-card/50"
85
+ )}>
86
+ {/* Header */}
87
+ <div className="flex items-center gap-3 px-3 py-2">
88
+ {/* Checkbox */}
89
+ <button
90
+ type="button"
91
+ disabled={readOnly}
92
+ className={cn(
93
+ "flex-shrink-0 h-5 w-5 rounded-md border-2 flex items-center justify-center transition-all duration-200",
94
+ criterion.validated
95
+ ? "bg-emerald-500 border-emerald-500 text-white"
96
+ : "border-border hover:border-emerald-400",
97
+ readOnly && "cursor-default"
98
+ )}
99
+ aria-label={criterion.validated ? "Mark as not validated" : "Mark as validated"}
100
+ aria-checked={criterion.validated}
101
+ role="checkbox"
102
+ >
103
+ {criterion.validated && (
104
+ <svg className="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={3}>
105
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
106
+ </svg>
107
+ )}
108
+ </button>
109
+
110
+ {/* ID badge */}
111
+ <span className="text-[10px] font-mono font-bold text-muted-foreground bg-muted px-1.5 py-0.5 rounded shrink-0">
112
+ {criterion.id}
113
+ </span>
114
+
115
+ {/* Scenario name */}
116
+ <span className={cn(
117
+ "text-sm font-medium flex-1 min-w-0 truncate",
118
+ criterion.validated && "line-through text-muted-foreground"
119
+ )}>
120
+ {criterion.scenario || "Unnamed scenario"}
121
+ </span>
122
+
123
+ {/* Toggle */}
124
+ <button
125
+ type="button"
126
+ onClick={() => setIsExpanded(!isExpanded)}
127
+ className="text-muted-foreground hover:text-foreground transition-colors p-0.5"
128
+ aria-label={isExpanded ? "Collapse" : "Expand"}
129
+ >
130
+ <svg
131
+ className={cn("h-3.5 w-3.5 transition-transform duration-200", isExpanded && "rotate-180")}
132
+ viewBox="0 0 24 24"
133
+ fill="none"
134
+ stroke="currentColor"
135
+ strokeWidth={2}
136
+ >
137
+ <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
138
+ </svg>
139
+ </button>
140
+ </div>
141
+
142
+ {/* Gherkin steps */}
143
+ {isExpanded && (
144
+ <div className="px-3 pb-3 pt-1 space-y-1.5 border-t border-border/50">
145
+ <div className="flex items-start gap-2">
146
+ <GherkinKeyword keyword="Given" variant="given" />
147
+ <span className="text-sm text-foreground/80 pt-0.5">
148
+ {criterion.given || <span className="italic text-muted-foreground">precondition...</span>}
149
+ </span>
150
+ </div>
151
+ <div className="flex items-start gap-2">
152
+ <GherkinKeyword keyword="When" variant="when" />
153
+ <span className="text-sm text-foreground/80 pt-0.5">
154
+ {criterion.when || <span className="italic text-muted-foreground">action...</span>}
155
+ </span>
156
+ </div>
157
+ <div className="flex items-start gap-2">
158
+ <GherkinKeyword keyword="Then" variant="then" />
159
+ <span className="text-sm text-foreground/80 pt-0.5">
160
+ {criterion.then || <span className="italic text-muted-foreground">expected result...</span>}
161
+ </span>
162
+ </div>
163
+ </div>
164
+ )}
165
+ </div>
166
+ )
167
+ }
168
+
169
+ // ============================================================================
170
+ // Element Component
171
+ // ============================================================================
172
+
173
+ /**
174
+ * WakaAcceptanceCriteriaBlock - A Plate.js block for acceptance criteria in
175
+ * Gherkin format (Given/When/Then). Each criterion has a checkbox for
176
+ * validation tracking.
177
+ *
178
+ * Register in Plate editor:
179
+ * ```ts
180
+ * components: {
181
+ * [ACCEPTANCE_CRITERIA_BLOCK_TYPE]: WakaAcceptanceCriteriaBlock,
182
+ * }
183
+ * ```
184
+ */
185
+ export function WakaAcceptanceCriteriaBlock({
186
+ attributes,
187
+ children,
188
+ element,
189
+ className,
190
+ }: WakaAcceptanceCriteriaBlockProps) {
191
+ const el = element as AcceptanceCriteriaElement | undefined
192
+ const criteria = el?.criteria || []
193
+ const title = el?.title || "Acceptance Criteria"
194
+
195
+ const validatedCount = criteria.filter((c) => c.validated).length
196
+ const totalCount = criteria.length
197
+ const progress = totalCount > 0 ? (validatedCount / totalCount) * 100 : 0
198
+
199
+ return (
200
+ <div
201
+ {...attributes}
202
+ contentEditable={false}
203
+ className={cn(
204
+ "my-4 rounded-lg overflow-hidden",
205
+ "border border-border",
206
+ "bg-card/30",
207
+ "shadow-sm",
208
+ className
209
+ )}
210
+ >
211
+ {/* Header */}
212
+ <div className="flex items-center justify-between px-4 py-2.5 bg-muted/30 border-b border-border">
213
+ <div className="flex items-center gap-2">
214
+ <svg
215
+ className="h-4 w-4 text-emerald-600 dark:text-emerald-400"
216
+ viewBox="0 0 24 24"
217
+ fill="none"
218
+ stroke="currentColor"
219
+ strokeWidth={2}
220
+ aria-hidden="true"
221
+ >
222
+ <path strokeLinecap="round" strokeLinejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
223
+ </svg>
224
+ <span className="text-xs font-bold uppercase tracking-wider text-foreground">
225
+ {title}
226
+ </span>
227
+ </div>
228
+
229
+ {/* Progress */}
230
+ <div className="flex items-center gap-2">
231
+ <span className="text-[10px] font-medium text-muted-foreground">
232
+ {validatedCount}/{totalCount}
233
+ </span>
234
+ <div className="w-16 h-1.5 bg-muted rounded-full overflow-hidden">
235
+ <div
236
+ className="h-full bg-emerald-500 rounded-full transition-all duration-500"
237
+ style={{ width: `${progress}%` }}
238
+ />
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ {/* Criteria list */}
244
+ <div className="p-3 space-y-2">
245
+ {criteria.length > 0 ? (
246
+ criteria.map((criterion, index) => (
247
+ <CriterionRow
248
+ key={criterion.id}
249
+ criterion={criterion}
250
+ index={index}
251
+ readOnly
252
+ />
253
+ ))
254
+ ) : (
255
+ <div className="py-6 text-center text-sm text-muted-foreground italic">
256
+ No acceptance criteria defined yet.
257
+ </div>
258
+ )}
259
+ </div>
260
+
261
+ {/* Hidden Slate children */}
262
+ <div className="hidden">{children}</div>
263
+ </div>
264
+ )
265
+ }
266
+
267
+ WakaAcceptanceCriteriaBlock.displayName = "WakaAcceptanceCriteriaBlock"
268
+
269
+ // ============================================================================
270
+ // Node Factory
271
+ // ============================================================================
272
+
273
+ /**
274
+ * Creates Slate nodes for an acceptance criteria block.
275
+ */
276
+ export function createAcceptanceCriteriaNodes(options?: {
277
+ title?: string
278
+ criteria?: AcceptanceCriterion[]
279
+ }): AcceptanceCriteriaElement[] {
280
+ return [
281
+ {
282
+ type: ACCEPTANCE_CRITERIA_BLOCK_TYPE,
283
+ title: options?.title || "Acceptance Criteria",
284
+ criteria: options?.criteria || [
285
+ {
286
+ id: "AC-01",
287
+ scenario: "Nominal case",
288
+ given: "The user is authenticated",
289
+ when: "They perform the action",
290
+ then: "The expected result is displayed",
291
+ validated: false,
292
+ },
293
+ {
294
+ id: "AC-02",
295
+ scenario: "Error case",
296
+ given: "The service is unavailable",
297
+ when: "They perform the action",
298
+ then: "An error message is displayed",
299
+ validated: false,
300
+ },
301
+ ],
302
+ children: [{ text: "" }],
303
+ },
304
+ ]
305
+ }
306
+
307
+ /**
308
+ * Creates a Plate plugin for acceptance criteria blocks.
309
+ */
310
+ export async function createAcceptanceCriteriaPlugin() {
311
+ try {
312
+ const { createPlatePlugin } = await import("platejs/react")
313
+ return createPlatePlugin({
314
+ key: ACCEPTANCE_CRITERIA_BLOCK_TYPE,
315
+ node: {
316
+ isElement: true,
317
+ isVoid: true,
318
+ type: ACCEPTANCE_CRITERIA_BLOCK_TYPE,
319
+ component: WakaAcceptanceCriteriaBlock,
320
+ },
321
+ })
322
+ } catch {
323
+ console.warn("[WakaAcceptanceCriteriaBlock] platejs not installed")
324
+ return null
325
+ }
326
+ }