@pascal-app/editor 0.9.2 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/package.json +9 -7
  2. package/src/components/editor/bake-exporter.tsx +3 -1
  3. package/src/components/editor/bake-thumbnail.tsx +95 -0
  4. package/src/components/editor/camera-dragging-lifecycle.test.ts +49 -0
  5. package/src/components/editor/camera-dragging-lifecycle.ts +41 -0
  6. package/src/components/editor/custom-camera-controls.tsx +307 -212
  7. package/src/components/editor/delete-confirmation-dialog.tsx +54 -0
  8. package/src/components/editor/elevation-3d-guide-layer.tsx +120 -0
  9. package/src/components/editor/first-person/build-collider-world.test.ts +66 -2
  10. package/src/components/editor/first-person/build-collider-world.ts +20 -0
  11. package/src/components/editor/first-person/terrain-collider.test.ts +132 -0
  12. package/src/components/editor/first-person/terrain-collider.ts +212 -0
  13. package/src/components/editor/first-person-controls.tsx +295 -132
  14. package/src/components/editor/floating-action-menu.tsx +59 -65
  15. package/src/components/editor/floorplan-background-selection.test.ts +16 -2
  16. package/src/components/editor/floorplan-background-selection.ts +4 -18
  17. package/src/components/editor/floorplan-camera-sync.test.ts +249 -0
  18. package/src/components/editor/floorplan-camera-sync.ts +213 -0
  19. package/src/components/editor/floorplan-mode-coordinator.tsx +82 -0
  20. package/src/components/editor/floorplan-navigation-presentation.test.ts +142 -0
  21. package/src/components/editor/floorplan-navigation-presentation.ts +156 -0
  22. package/src/components/editor/floorplan-panel.tsx +931 -861
  23. package/src/components/editor/group-actions.ts +155 -15
  24. package/src/components/editor/group-floating-action-menu.tsx +1 -1
  25. package/src/components/editor/group-rotate-handle.tsx +1 -1
  26. package/src/components/editor/group-selection-box-3d.tsx +1 -1
  27. package/src/components/editor/handles/handle-drag-history.test.ts +96 -0
  28. package/src/components/editor/handles/handle-drag-history.ts +14 -0
  29. package/src/components/editor/handles/preview-overrides.test.ts +26 -0
  30. package/src/components/editor/handles/preview-overrides.ts +13 -0
  31. package/src/components/editor/handles/resize-snap.test.ts +52 -0
  32. package/src/components/editor/handles/resize-snap.ts +23 -0
  33. package/src/components/editor/handles/use-handle-drag.ts +17 -7
  34. package/src/components/editor/index.tsx +42 -9
  35. package/src/components/editor/measurement-pill.tsx +6 -1
  36. package/src/components/editor/node-arrow-handles.tsx +51 -15
  37. package/src/components/editor/quick-measurement-card.tsx +9 -3
  38. package/src/components/editor/quick-measurement-hud.tsx +7 -1
  39. package/src/components/editor/selection-manager.tsx +3 -1
  40. package/src/components/editor/site-edge-labels.tsx +24 -2
  41. package/src/components/editor/snapshot-capture-overlay.tsx +4 -1
  42. package/src/components/editor/thumbnail-generator.tsx +66 -294
  43. package/src/components/editor/use-floorplan-hit-testing.test.ts +82 -0
  44. package/src/components/editor/use-floorplan-hit-testing.ts +144 -161
  45. package/src/components/editor/use-mesh-settle-epoch.ts +1 -0
  46. package/src/components/editor/wall-measurement-label.tsx +8 -16
  47. package/src/components/editor/wall-move-side-handles.tsx +193 -18
  48. package/src/components/editor/wall-opening-highlights.tsx +63 -32
  49. package/src/components/editor/wall-snap-beacon-layer.tsx +6 -4
  50. package/src/components/editor-2d/floorplan-alignment-guide-layer.tsx +2 -1
  51. package/src/components/editor-2d/floorplan-cursor-indicator-overlay.tsx +39 -8
  52. package/src/components/editor-2d/floorplan-cursor-indicator-position.test.ts +31 -0
  53. package/src/components/editor-2d/floorplan-cursor-indicator-position.ts +39 -0
  54. package/src/components/editor-2d/floorplan-group-move.tsx +47 -4
  55. package/src/components/editor-2d/floorplan-measurement-tool-layer.tsx +21 -5
  56. package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +72 -0
  57. package/src/components/editor-2d/floorplan-registry-action-menu.tsx +93 -59
  58. package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +27 -0
  59. package/src/components/editor-2d/floorplan-render-context.test.ts +27 -0
  60. package/src/components/editor-2d/floorplan-render-context.tsx +113 -8
  61. package/src/components/editor-2d/renderers/floorplan-annotation-layout.test.ts +285 -0
  62. package/src/components/editor-2d/renderers/floorplan-annotation-layout.ts +516 -0
  63. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.test.tsx +236 -0
  64. package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +618 -0
  65. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +369 -0
  66. package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +431 -32
  67. package/src/components/editor-2d/renderers/floorplan-label-angle.test.ts +75 -1
  68. package/src/components/editor-2d/renderers/floorplan-label-angle.ts +92 -0
  69. package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +93 -28
  70. package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +279 -0
  71. package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +621 -281
  72. package/src/components/editor-2d/renderers/floorplan-stair-layer.tsx +20 -10
  73. package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +12 -7
  74. package/src/components/tools/fence/fence-drafting.ts +27 -0
  75. package/src/components/tools/item/use-draft-node.ts +131 -72
  76. package/src/components/tools/item/use-placement-coordinator.tsx +52 -4
  77. package/src/components/tools/registry/move-registry-node-tool.tsx +75 -4
  78. package/src/components/tools/roof/roof-tool.tsx +16 -0
  79. package/src/components/tools/shared/floor-stack-preview.ts +4 -0
  80. package/src/components/tools/shared/horizontal-construction-plane.ts +123 -0
  81. package/src/components/tools/shared/pointer-support-cap.test.ts +98 -0
  82. package/src/components/tools/shared/pointer-support-cap.ts +273 -0
  83. package/src/components/tools/site/site-boundary-editor.tsx +13 -6
  84. package/src/components/tools/site/stroke-pointer-ownership.test.ts +91 -0
  85. package/src/components/tools/site/stroke-pointer-ownership.ts +57 -0
  86. package/src/components/tools/site/terrain-brush-cursor.tsx +247 -0
  87. package/src/components/tools/site/terrain-sculpt-grid.tsx +192 -0
  88. package/src/components/tools/site/terrain-sculpt-tool.tsx +356 -0
  89. package/src/components/tools/stair/stair-click-guard.test.ts +58 -0
  90. package/src/components/tools/stair/stair-click-guard.ts +72 -0
  91. package/src/components/tools/stair/stair-tool.tsx +98 -47
  92. package/src/components/tools/tool-manager.tsx +67 -9
  93. package/src/components/tools/wall/wall-drafting.test.ts +118 -1
  94. package/src/components/tools/wall/wall-drafting.ts +128 -9
  95. package/src/components/tools/wall/wall-snap-geometry.test.ts +8 -0
  96. package/src/components/tools/wall/wall-snap-geometry.ts +70 -3
  97. package/src/components/tools/zone/zone-tool.tsx +16 -0
  98. package/src/components/ui/action-menu/control-modes.tsx +4 -1
  99. package/src/components/ui/action-menu/measurement-control.tsx +107 -28
  100. package/src/components/ui/command-palette/editor-commands.tsx +14 -1
  101. package/src/components/ui/controls/material-paint-panel.tsx +7 -1
  102. package/src/components/ui/controls/material-picker.tsx +86 -7
  103. package/src/components/ui/controls/terrain-sculpt-panel.tsx +193 -0
  104. package/src/components/ui/floating-level-selector.tsx +73 -19
  105. package/src/components/ui/helpers/helper-manager.tsx +46 -0
  106. package/src/components/ui/panels/multi-selection-panel.tsx +57 -0
  107. package/src/components/ui/panels/panel-manager.tsx +14 -1
  108. package/src/components/ui/panels/parametric-inspector.tsx +4 -2
  109. package/src/components/ui/panels/selection-breakdown.test.ts +24 -0
  110. package/src/components/ui/panels/selection-breakdown.ts +20 -0
  111. package/src/components/ui/sidebar/panels/settings-panel/index.tsx +14 -4
  112. package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +16 -0
  113. package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +2 -1
  114. package/src/components/ui/sidebar/panels/site-panel/index.tsx +2 -0
  115. package/src/components/viewer/viewer-controls-bar.tsx +466 -0
  116. package/src/components/viewer/viewer-scene-header.tsx +235 -0
  117. package/src/components/viewer-overlay.tsx +20 -655
  118. package/src/components/walkthrough-hud.tsx +111 -0
  119. package/src/hooks/use-grid-events.ts +24 -0
  120. package/src/hooks/use-keyboard.ts +53 -30
  121. package/src/index.tsx +137 -4
  122. package/src/lib/active-placement-surface.test.ts +24 -0
  123. package/src/lib/active-placement-surface.ts +8 -1
  124. package/src/lib/camera-pose.test.ts +237 -0
  125. package/src/lib/camera-pose.ts +172 -0
  126. package/src/lib/direct-manipulation.test.ts +83 -0
  127. package/src/lib/direct-manipulation.ts +9 -0
  128. package/src/lib/door-interaction.ts +1 -1
  129. package/src/lib/elevation-guides.test.ts +107 -0
  130. package/src/lib/elevation-guides.ts +236 -0
  131. package/src/lib/floorplan/annotation-visibility.test.ts +327 -0
  132. package/src/lib/floorplan/annotation-visibility.ts +133 -0
  133. package/src/lib/floorplan/drawing-coordination.test.ts +46 -0
  134. package/src/lib/floorplan/drawing-coordination.ts +13 -0
  135. package/src/lib/floorplan/floorplan-export.test.ts +319 -0
  136. package/src/lib/floorplan/floorplan-export.tsx +745 -131
  137. package/src/lib/floorplan/floorplan-extension.test.ts +43 -0
  138. package/src/lib/floorplan/floorplan-extension.ts +181 -0
  139. package/src/lib/floorplan/floorplan-mode.test.ts +120 -0
  140. package/src/lib/floorplan/floorplan-mode.ts +73 -0
  141. package/src/lib/floorplan/floorplan-pdfkit-document.ts +194 -0
  142. package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +107 -0
  143. package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +680 -0
  144. package/src/lib/floorplan/geometry.ts +2 -3
  145. package/src/lib/fresh-planar-placement.test.ts +44 -1
  146. package/src/lib/fresh-planar-placement.ts +21 -4
  147. package/src/lib/glb-export.test.ts +116 -1
  148. package/src/lib/glb-export.ts +204 -7
  149. package/src/lib/ground-surface.ts +95 -0
  150. package/src/lib/history.test.ts +96 -0
  151. package/src/lib/history.ts +22 -0
  152. package/src/lib/interaction/overlay-policy.test.ts +2 -1
  153. package/src/lib/interaction/scope.ts +40 -8
  154. package/src/lib/level-duplication.test.ts +2 -1
  155. package/src/lib/measurements.test.ts +5 -0
  156. package/src/lib/measurements.ts +12 -1
  157. package/src/lib/quick-action-feedback.ts +33 -0
  158. package/src/lib/quick-action-nodes.test.ts +94 -0
  159. package/src/lib/quick-action-nodes.ts +47 -0
  160. package/src/lib/scene-clipboard.test.ts +130 -1
  161. package/src/lib/scene-clipboard.ts +237 -13
  162. package/src/lib/selection-routing.test.ts +16 -1
  163. package/src/lib/selection-routing.ts +5 -0
  164. package/src/lib/sfx-bus.ts +33 -2
  165. package/src/lib/sfx-player.test.ts +148 -6
  166. package/src/lib/sfx-player.ts +156 -8
  167. package/src/lib/site-boundary.test.ts +53 -0
  168. package/src/lib/site-boundary.ts +27 -0
  169. package/src/lib/snapping-mode.test.ts +29 -2
  170. package/src/lib/snapping-mode.ts +15 -3
  171. package/src/lib/stair-duplication.test.ts +103 -0
  172. package/src/lib/stair-duplication.ts +57 -85
  173. package/src/lib/stair-levels.ts +2 -0
  174. package/src/lib/terrain-sculpt.test.ts +429 -0
  175. package/src/lib/terrain-sculpt.ts +301 -0
  176. package/src/lib/terrain-verb-color.test.ts +51 -0
  177. package/src/lib/terrain-verb-color.ts +58 -0
  178. package/src/lib/touch-gesture-priority.test.ts +45 -0
  179. package/src/lib/touch-gesture-priority.ts +30 -0
  180. package/src/lib/window-interaction.ts +1 -1
  181. package/src/store/camera-pose-store.test.ts +29 -0
  182. package/src/store/camera-pose-store.ts +27 -0
  183. package/src/store/live-draft-preview-stores.test.ts +136 -0
  184. package/src/store/navigation-sync-pose-store.test.ts +34 -0
  185. package/src/store/navigation-sync-pose-store.ts +27 -0
  186. package/src/store/terrain-sculpt-mode.test.ts +223 -0
  187. package/src/store/use-delete-confirmation.ts +27 -0
  188. package/src/store/use-drawing-view.test.ts +23 -0
  189. package/src/store/use-drawing-view.ts +84 -0
  190. package/src/store/use-editor.tsx +184 -15
  191. package/src/store/use-elevation-guides.ts +40 -0
  192. package/src/store/use-fence-curve-draft.ts +38 -8
  193. package/src/store/use-first-person-hud.ts +40 -0
  194. package/src/store/use-floorplan-annotation-visibility.ts +60 -0
  195. package/src/store/use-floorplan-draft-preview.ts +54 -4
  196. package/src/store/use-floorplan-mode.ts +110 -0
  197. package/src/store/use-interaction-scope.test.ts +23 -3
  198. package/src/store/use-interaction-scope.ts +8 -0
  199. package/src/store/use-measurement-draft.test.ts +29 -0
  200. package/src/store/use-measurement-draft.ts +33 -6
  201. package/src/store/use-path-draft-preview.ts +90 -0
  202. package/src/store/use-stair-build-preview.ts +12 -2
  203. package/src/lib/floorplan/selection-tool.ts +0 -271
@@ -0,0 +1,235 @@
1
+ 'use client'
2
+
3
+ import {
4
+ type AnyNode,
5
+ type AnyNodeId,
6
+ type BuildingNode,
7
+ getLevelDisplayName,
8
+ type LevelNode,
9
+ useScene,
10
+ type ZoneNode,
11
+ } from '@pascal-app/core'
12
+ import { useViewer } from '@pascal-app/viewer'
13
+ import { ArrowLeft, ChevronRight, Layers } from 'lucide-react'
14
+ import Link from 'next/link'
15
+ import type { ReactNode } from 'react'
16
+ import { useShallow } from 'zustand/react/shallow'
17
+ import { cn } from '../../lib/utils'
18
+
19
+ const getNodeName = (node: AnyNode): string => {
20
+ if ('name' in node && node.name) return node.name
21
+ if (node.type === 'wall') return 'Wall'
22
+ if (node.type === 'fence') return 'Fence'
23
+ if (node.type === 'item') return (node as { asset: { name: string } }).asset?.name || 'Item'
24
+ if (node.type === 'slab') return 'Slab'
25
+ if (node.type === 'ceiling') return 'Ceiling'
26
+ if (node.type === 'roof') return 'Roof'
27
+ if (node.type === 'roof-segment') return 'Roof Segment'
28
+ return node.type
29
+ }
30
+
31
+ export type ViewerSceneHeaderProps = {
32
+ projectName?: string | null
33
+ owner?: { username?: string | null } | null
34
+ onBack?: () => void
35
+ /** Fallback destination when no `onBack` handler is supplied. Must already be
36
+ * sanitized by the caller. */
37
+ backHref?: string
38
+ /** Extra row under the project info (e.g. likes/fork actions). */
39
+ stats?: ReactNode
40
+ }
41
+
42
+ export const ViewerSceneHeader = ({
43
+ projectName,
44
+ owner,
45
+ onBack,
46
+ backHref = '/',
47
+ stats,
48
+ }: ViewerSceneHeaderProps) => {
49
+ const selection = useViewer((s) => s.selection)
50
+
51
+ // Subscribe only to the specific nodes we read so that creating an unrelated
52
+ // node elsewhere in the scene doesn't re-render this overlay.
53
+ const firstSelectedId = selection.selectedIds[0] ?? null
54
+ const building = useScene((s) =>
55
+ selection.buildingId ? (s.nodes[selection.buildingId] as BuildingNode | undefined) : null,
56
+ )
57
+ const level = useScene((s) =>
58
+ selection.levelId ? (s.nodes[selection.levelId] as LevelNode | undefined) : null,
59
+ )
60
+ const zone = useScene((s) =>
61
+ selection.zoneId ? (s.nodes[selection.zoneId] as ZoneNode | undefined) : null,
62
+ )
63
+ const selectedNode = useScene((s) =>
64
+ firstSelectedId ? (s.nodes[firstSelectedId as AnyNodeId] as AnyNode | undefined) : null,
65
+ )
66
+ // Highest first so the list reads top-down like a building section.
67
+ const levels = useScene(
68
+ useShallow((s) => {
69
+ if (!building) return []
70
+ return building.children
71
+ .map((id) => s.nodes[id as AnyNodeId] as LevelNode | undefined)
72
+ .filter((n): n is LevelNode => n?.type === 'level')
73
+ .sort((a, b) => b.level - a.level)
74
+ }),
75
+ )
76
+
77
+ const handleLevelClick = (levelId: LevelNode['id']) => {
78
+ // When switching levels, deselect zone and items
79
+ useViewer.getState().setSelection({ levelId })
80
+ }
81
+
82
+ const handleBreadcrumbClick = (depth: 'root' | 'building' | 'level') => {
83
+ switch (depth) {
84
+ case 'root':
85
+ useViewer.getState().resetSelection()
86
+ break
87
+ case 'building':
88
+ useViewer.getState().setSelection({ levelId: null })
89
+ break
90
+ case 'level':
91
+ useViewer.getState().setSelection({ zoneId: null })
92
+ break
93
+ }
94
+ }
95
+
96
+ return (
97
+ <div className="dark absolute top-4 left-4 z-20 flex flex-col gap-3 text-foreground">
98
+ <div className="corner-smooth pointer-events-auto flex min-w-[200px] flex-col overflow-hidden rounded-2xl border border-border/40 bg-background/95 shadow-elevation-4 backdrop-blur-xl transition-colors duration-200 ease-out">
99
+ {/* Project info + back */}
100
+ <div className="flex items-center gap-3 px-3 py-2.5">
101
+ {onBack ? (
102
+ <button
103
+ aria-label="Back"
104
+ className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md transition-colors hover:bg-white/10"
105
+ onClick={onBack}
106
+ type="button"
107
+ >
108
+ <ArrowLeft className="h-4 w-4 text-muted-foreground" />
109
+ </button>
110
+ ) : (
111
+ <Link
112
+ aria-label="Back"
113
+ className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md transition-colors hover:bg-white/10"
114
+ href={backHref}
115
+ prefetch={false}
116
+ >
117
+ <ArrowLeft className="h-4 w-4 text-muted-foreground" />
118
+ </Link>
119
+ )}
120
+ <div className="min-w-0 flex-1">
121
+ <div className="truncate font-medium text-foreground text-sm">
122
+ {projectName || 'Untitled'}
123
+ </div>
124
+ {owner?.username && (
125
+ <Link
126
+ className="text-muted-foreground text-xs transition-colors hover:text-foreground"
127
+ href={`/u/${owner.username}`}
128
+ >
129
+ @{owner.username}
130
+ </Link>
131
+ )}
132
+ </div>
133
+ </div>
134
+
135
+ {stats && (
136
+ <div className="flex items-center gap-1 border-border/40 border-t px-3 py-2">{stats}</div>
137
+ )}
138
+
139
+ {/* Breadcrumb — only shown when navigated into a building */}
140
+ {building && (
141
+ <div className="border-border/40 border-t px-3 py-2">
142
+ <div className="flex items-center gap-1.5 text-xs">
143
+ <button
144
+ className="text-muted-foreground transition-colors hover:text-foreground"
145
+ onClick={() => handleBreadcrumbClick('root')}
146
+ >
147
+ Site
148
+ </button>
149
+
150
+ <ChevronRight className="h-3 w-3 text-muted-foreground/50" />
151
+ <button
152
+ className={`truncate transition-colors ${level ? 'text-muted-foreground hover:text-foreground' : 'font-medium text-foreground'}`}
153
+ onClick={() => handleBreadcrumbClick('building')}
154
+ >
155
+ {building.name || 'Building'}
156
+ </button>
157
+
158
+ {level && (
159
+ <>
160
+ <ChevronRight className="h-3 w-3 text-muted-foreground/50" />
161
+ <button
162
+ className={`truncate transition-colors ${zone ? 'text-muted-foreground hover:text-foreground' : 'font-medium text-foreground'}`}
163
+ onClick={() => handleBreadcrumbClick('level')}
164
+ >
165
+ {getLevelDisplayName(level)}
166
+ </button>
167
+ </>
168
+ )}
169
+
170
+ {zone && (
171
+ <>
172
+ <ChevronRight className="h-3 w-3 text-muted-foreground/50" />
173
+ <span
174
+ className={`truncate transition-colors ${selectedNode ? 'text-muted-foreground' : 'font-medium text-foreground'}`}
175
+ >
176
+ {zone.name}
177
+ </span>
178
+ </>
179
+ )}
180
+
181
+ {selectedNode && zone && (
182
+ <>
183
+ <ChevronRight className="h-3 w-3 text-muted-foreground/50" />
184
+ <span className="truncate font-medium text-foreground">
185
+ {getNodeName(selectedNode)}
186
+ </span>
187
+ </>
188
+ )}
189
+ </div>
190
+ </div>
191
+ )}
192
+ </div>
193
+
194
+ {/* Level list (only when a building is selected) */}
195
+ {building && levels.length > 0 && (
196
+ <div className="corner-smooth pointer-events-auto flex w-48 flex-col overflow-hidden rounded-2xl border border-border/40 bg-background/95 py-1 shadow-elevation-4 backdrop-blur-xl transition-colors duration-200 ease-out">
197
+ <span className="px-3 py-2 font-medium text-[10px] text-muted-foreground uppercase tracking-wider">
198
+ Levels
199
+ </span>
200
+ <div className="flex flex-col">
201
+ {levels.map((lvl) => {
202
+ const isSelected = lvl.id === selection.levelId
203
+ return (
204
+ <button
205
+ className={cn(
206
+ 'group/row relative flex h-8 w-full cursor-pointer select-none items-center border-border/50 border-r border-r-transparent border-b px-3 text-sm transition-all duration-200',
207
+ isSelected
208
+ ? 'border-r-3 border-r-white bg-accent/50 text-foreground'
209
+ : 'text-muted-foreground hover:bg-accent/30 hover:text-foreground',
210
+ )}
211
+ key={lvl.id}
212
+ onClick={() => handleLevelClick(lvl.id)}
213
+ >
214
+ <div className="flex min-w-0 flex-1 items-center gap-2">
215
+ <span
216
+ className={cn(
217
+ 'flex h-4 w-4 shrink-0 items-center justify-center transition-all duration-200',
218
+ !isSelected && 'opacity-60 grayscale',
219
+ )}
220
+ >
221
+ <Layers className="h-3.5 w-3.5" />
222
+ </span>
223
+ <div className="min-w-0 flex-1 truncate text-left">
224
+ {getLevelDisplayName(lvl)}
225
+ </div>
226
+ </div>
227
+ </button>
228
+ )
229
+ })}
230
+ </div>
231
+ </div>
232
+ )}
233
+ </div>
234
+ )
235
+ }