@joewinke/jatui 0.1.23 → 0.1.25

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 (124) hide show
  1. package/CLAUDE.md +244 -0
  2. package/package.json +23 -10
  3. package/src/lib/actions/collapse.ts +0 -136
  4. package/src/lib/actions/railNav.ts +0 -473
  5. package/src/lib/components/AnnotationLayer.svelte +0 -108
  6. package/src/lib/components/AnnotationPanel.svelte +0 -319
  7. package/src/lib/components/AudioWaveform.svelte +0 -698
  8. package/src/lib/components/AvailabilityModal.svelte +0 -177
  9. package/src/lib/components/AvatarUpload.svelte +0 -234
  10. package/src/lib/components/Badge.svelte +0 -38
  11. package/src/lib/components/BookingForm.svelte +0 -278
  12. package/src/lib/components/BurndownChart.svelte +0 -778
  13. package/src/lib/components/Button.svelte +0 -81
  14. package/src/lib/components/CalendarPicker.svelte +0 -284
  15. package/src/lib/components/Card.svelte +0 -67
  16. package/src/lib/components/CharacterCounter.svelte +0 -82
  17. package/src/lib/components/ChipInput.svelte +0 -609
  18. package/src/lib/components/Collapse.svelte +0 -82
  19. package/src/lib/components/ColorSelector.svelte +0 -167
  20. package/src/lib/components/CommentThread.svelte +0 -773
  21. package/src/lib/components/ConfirmDialog.svelte +0 -348
  22. package/src/lib/components/ConfirmModal.svelte +0 -142
  23. package/src/lib/components/ContextMenu.svelte +0 -188
  24. package/src/lib/components/CountdownTimer.svelte +0 -94
  25. package/src/lib/components/DateRangePicker.svelte +0 -194
  26. package/src/lib/components/Drawer.svelte +0 -143
  27. package/src/lib/components/EmojiPicker.svelte +0 -159
  28. package/src/lib/components/EntityPreviewCard.svelte +0 -104
  29. package/src/lib/components/FileDropzone.svelte +0 -493
  30. package/src/lib/components/FilePicker.svelte +0 -488
  31. package/src/lib/components/FileThumbnail.svelte +0 -80
  32. package/src/lib/components/FilterDropdown.svelte +0 -203
  33. package/src/lib/components/FloatingActionBar.svelte +0 -354
  34. package/src/lib/components/GPSTracker.svelte +0 -202
  35. package/src/lib/components/HunkDiffView.svelte +0 -348
  36. package/src/lib/components/ImageLightbox.svelte +0 -274
  37. package/src/lib/components/ImageUpload.svelte +0 -147
  38. package/src/lib/components/InlineEdit.svelte +0 -291
  39. package/src/lib/components/InputDialog.svelte +0 -327
  40. package/src/lib/components/KeyboardShortcutsOverlay.svelte +0 -296
  41. package/src/lib/components/LazyImage.svelte +0 -123
  42. package/src/lib/components/LinkShortener.svelte +0 -274
  43. package/src/lib/components/LoadingSpinner.svelte +0 -106
  44. package/src/lib/components/LocationMap.svelte +0 -186
  45. package/src/lib/components/MapView.svelte +0 -341
  46. package/src/lib/components/MarkupEditor.svelte +0 -485
  47. package/src/lib/components/MarkupOverlay.svelte +0 -55
  48. package/src/lib/components/MediaWorkbench.svelte +0 -871
  49. package/src/lib/components/MilestoneCard.svelte +0 -98
  50. package/src/lib/components/MilestoneTimeline.svelte +0 -91
  51. package/src/lib/components/Modal.svelte +0 -244
  52. package/src/lib/components/PDFViewer.svelte +0 -105
  53. package/src/lib/components/PdfThumbnail.svelte +0 -63
  54. package/src/lib/components/PhoneInput.svelte +0 -212
  55. package/src/lib/components/ResizableDivider.svelte +0 -307
  56. package/src/lib/components/ResizablePanel.svelte +0 -305
  57. package/src/lib/components/SearchDropdown.svelte +0 -421
  58. package/src/lib/components/SelectInput.svelte +0 -237
  59. package/src/lib/components/SidebarUserFooter.svelte +0 -34
  60. package/src/lib/components/SignaturePad.svelte +0 -175
  61. package/src/lib/components/SmartImageEditor.svelte +0 -720
  62. package/src/lib/components/SortDropdown.svelte +0 -156
  63. package/src/lib/components/Sparkline.svelte +0 -116
  64. package/src/lib/components/SpeechForm.svelte +0 -114
  65. package/src/lib/components/StatusBadge.svelte +0 -157
  66. package/src/lib/components/TaskTypeIcon.svelte +0 -54
  67. package/src/lib/components/TextArea.svelte +0 -162
  68. package/src/lib/components/TextInput.svelte +0 -131
  69. package/src/lib/components/ThemeSelector.svelte +0 -206
  70. package/src/lib/components/TimeSlotPicker.svelte +0 -162
  71. package/src/lib/components/UserAvatar.svelte +0 -72
  72. package/src/lib/components/VariablePicker.svelte +0 -170
  73. package/src/lib/components/VoicePlayer.svelte +0 -422
  74. package/src/lib/components/linked-columns/LinkedColumns.svelte +0 -526
  75. package/src/lib/components/markup.ts +0 -287
  76. package/src/lib/components/messaging/Avatar.svelte +0 -81
  77. package/src/lib/components/messaging/ChannelInfoModal.svelte +0 -163
  78. package/src/lib/components/messaging/ChannelList.svelte +0 -107
  79. package/src/lib/components/messaging/ChannelMemberAvatarStack.svelte +0 -69
  80. package/src/lib/components/messaging/ChannelMembersModal.svelte +0 -182
  81. package/src/lib/components/messaging/CreateChannelModal.svelte +0 -190
  82. package/src/lib/components/messaging/DirectMessageList.svelte +0 -145
  83. package/src/lib/components/messaging/EmojiSelector.svelte +0 -261
  84. package/src/lib/components/messaging/MentionAutocomplete.svelte +0 -193
  85. package/src/lib/components/messaging/MessageAttachment.svelte +0 -273
  86. package/src/lib/components/messaging/MessageAttachmentUpload.svelte +0 -243
  87. package/src/lib/components/messaging/MessageInput.svelte +0 -452
  88. package/src/lib/components/messaging/MessageItem.svelte +0 -341
  89. package/src/lib/components/messaging/MessageThread.svelte +0 -306
  90. package/src/lib/components/messaging/NotificationSettingsModal.svelte +0 -234
  91. package/src/lib/components/messaging/QuotedMessageDisplay.svelte +0 -123
  92. package/src/lib/components/messaging/StartDMModal.svelte +0 -100
  93. package/src/lib/components/messaging/ThreadPanel.svelte +0 -153
  94. package/src/lib/components/pipeline/DESIGN.md +0 -361
  95. package/src/lib/components/pipeline/Pipeline.svelte +0 -391
  96. package/src/lib/components/pipeline/PipelineCard.svelte +0 -94
  97. package/src/lib/components/pipeline/PipelineColumn.svelte +0 -163
  98. package/src/lib/components/replay/ChapterTimeline.svelte +0 -326
  99. package/src/lib/components/session-nav/transcriptModel.ts +0 -352
  100. package/src/lib/data/emojis.ts +0 -1208
  101. package/src/lib/index.ts +0 -405
  102. package/src/lib/stores/confirmDialog.svelte.ts +0 -48
  103. package/src/lib/stores/inputDialog.svelte.ts +0 -51
  104. package/src/lib/styles/rail.css +0 -63
  105. package/src/lib/types/annotation.ts +0 -38
  106. package/src/lib/types/booking.ts +0 -143
  107. package/src/lib/types/comments.ts +0 -97
  108. package/src/lib/types/entityPreview.ts +0 -45
  109. package/src/lib/types/filePicker.ts +0 -16
  110. package/src/lib/types/googleMaps.d.ts +0 -51
  111. package/src/lib/types/maps.ts +0 -43
  112. package/src/lib/types/messaging.ts +0 -459
  113. package/src/lib/types/milestone.ts +0 -22
  114. package/src/lib/types/pipeline.ts +0 -150
  115. package/src/lib/types/smartImageEditor.ts +0 -39
  116. package/src/lib/types/templateVars.ts +0 -36
  117. package/src/lib/utils/currency.ts +0 -20
  118. package/src/lib/utils/daisyuiColors.ts +0 -243
  119. package/src/lib/utils/dateFormatters.ts +0 -196
  120. package/src/lib/utils/googleMapsLoader.ts +0 -84
  121. package/src/lib/utils/mentionParser.ts +0 -188
  122. package/src/lib/utils/phone.ts +0 -80
  123. package/src/lib/utils/phoneFormat.ts +0 -74
  124. package/src/lib/utils/taskUtils.ts +0 -75
@@ -1,261 +0,0 @@
1
- <!--
2
- EmojiSelector Component
3
-
4
- Emoji picker with categories, search, skin tones, and custom emoji support.
5
- Custom emojis and preferences are loaded via optional callbacks.
6
- -->
7
-
8
- <script lang="ts">
9
- import { onMount, onDestroy } from 'svelte'
10
- import type {
11
- EmojiSelection,
12
- EmojiCategory,
13
- SkinTone,
14
- StandardEmoji,
15
- CustomEmoji,
16
- EmojiPreferences,
17
- EmojiCallbacks,
18
- } from '../../types/messaging'
19
- import { EMOJI_CATEGORIES, SKIN_TONES } from '../../types/messaging'
20
-
21
- interface Props {
22
- visible: boolean
23
- position: { x: number; y: number }
24
- onselect: (selection: EmojiSelection) => void
25
- onclose: () => void
26
- emojis?: StandardEmoji[]
27
- customEmojis?: CustomEmoji[]
28
- preferences?: EmojiPreferences
29
- callbacks?: EmojiCallbacks
30
- searchPlaceholder?: string
31
- maxRecent?: number
32
- }
33
-
34
- let {
35
- visible = false,
36
- position = { x: 0, y: 0 },
37
- onselect,
38
- onclose,
39
- emojis = [],
40
- customEmojis = [],
41
- preferences,
42
- callbacks,
43
- searchPlaceholder = 'Search emojis...',
44
- maxRecent = 24,
45
- }: Props = $props()
46
-
47
- let searchQuery = $state('')
48
- let activeCategory: EmojiCategory = $state('smileys_people')
49
- // svelte-ignore state_referenced_locally
50
- let selectedSkinTone: SkinTone = $state(preferences?.skinTone || 'default')
51
- let hoveredEmoji = $state<{ emoji: string; name: string; shortcode: string } | null>(null)
52
- let loading = $state(false)
53
- let selectorElement = $state<HTMLElement | null>(null)
54
- let searchInput = $state<HTMLInputElement | null>(null)
55
-
56
- // Category icons using emoji characters
57
- const categoryEmojis: Record<string, string> = {
58
- search: '🔍',
59
- frequently_used: '🕐',
60
- smileys_people: '😀',
61
- animals_nature: '🐻',
62
- food_drink: '🍎',
63
- travel_places: '🏠',
64
- activities: '⚽',
65
- objects: '🔨',
66
- symbols: '❤️',
67
- flags: '🏳️',
68
- custom: '🎨',
69
- }
70
-
71
- // Filter emojis by category
72
- function getEmojisByCategory(category: EmojiCategory): StandardEmoji[] {
73
- return emojis.filter((e) => e.category === category)
74
- }
75
-
76
- // Search emojis
77
- function searchEmojis(query: string): StandardEmoji[] {
78
- const q = query.toLowerCase()
79
- return emojis.filter(
80
- (e) =>
81
- e.name.toLowerCase().includes(q) ||
82
- e.shortcode.toLowerCase().includes(q) ||
83
- e.keywords.some((k) => k.toLowerCase().includes(q)),
84
- )
85
- }
86
-
87
- const displayEmojis = $derived(
88
- searchQuery ? searchEmojis(searchQuery) : getEmojisByCategory(activeCategory),
89
- )
90
-
91
- function handleSelect(emoji: StandardEmoji) {
92
- const selection: EmojiSelection = {
93
- type: 'standard',
94
- unicode: emoji.unicode,
95
- name: emoji.name,
96
- shortcode: emoji.shortcode,
97
- skinTone: selectedSkinTone,
98
- }
99
- onselect(selection)
100
- }
101
-
102
- function handleCustomSelect(custom: CustomEmoji) {
103
- const selection: EmojiSelection = {
104
- type: 'custom',
105
- customEmojiId: custom.id,
106
- name: custom.displayName,
107
- shortcode: custom.name,
108
- }
109
- onselect(selection)
110
- }
111
-
112
- // Close on click outside
113
- function handleClickOutside(event: MouseEvent) {
114
- if (selectorElement && !selectorElement.contains(event.target as Node)) {
115
- onclose()
116
- }
117
- }
118
-
119
- // Close on escape
120
- function handleKeydown(event: KeyboardEvent) {
121
- if (event.key === 'Escape') onclose()
122
- }
123
-
124
- // Load custom emojis and preferences from callbacks on mount
125
- $effect(() => {
126
- if (visible && callbacks) {
127
- loading = true
128
- Promise.all([
129
- callbacks.loadCustomEmojis?.().catch(() => []),
130
- callbacks.loadPreferences?.().catch(() => null),
131
- ]).then(([loadedCustom, loadedPrefs]) => {
132
- if (loadedCustom?.length) customEmojis = loadedCustom
133
- if (loadedPrefs) {
134
- selectedSkinTone = loadedPrefs.skinTone || 'default'
135
- }
136
- loading = false
137
- })
138
- }
139
- })
140
-
141
- $effect(() => {
142
- if (visible) {
143
- document.addEventListener('click', handleClickOutside)
144
- document.addEventListener('keydown', handleKeydown)
145
- // Focus search on open
146
- setTimeout(() => searchInput?.focus(), 100)
147
- return () => {
148
- document.removeEventListener('click', handleClickOutside)
149
- document.removeEventListener('keydown', handleKeydown)
150
- }
151
- }
152
- })
153
-
154
- // Visible categories (skip search and custom if no custom emojis)
155
- const visibleCategories = $derived(
156
- EMOJI_CATEGORIES.filter((cat) => {
157
- if (cat.id === 'search') return false
158
- if (cat.id === 'custom' && customEmojis.length === 0) return false
159
- if (cat.id === 'frequently_used') return false // TODO: track frequent
160
- return true
161
- }),
162
- )
163
- </script>
164
-
165
- {#if visible}
166
- <div
167
- bind:this={selectorElement}
168
- class="fixed z-50 bg-base-200 border border-base-300 rounded-box w-88 flex flex-col"
169
- style="left: {position.x}px; top: {position.y}px; max-height: 420px;"
170
- >
171
- <!-- Search -->
172
- <div class="p-2 border-b border-base-300">
173
- <input
174
- bind:this={searchInput}
175
- bind:value={searchQuery}
176
- type="text"
177
- placeholder={searchPlaceholder}
178
- class="input input-sm input-bordered w-full"
179
- />
180
- </div>
181
-
182
- <!-- Category tabs -->
183
- {#if !searchQuery}
184
- <div class="flex items-center gap-0.5 px-2 py-1 border-b border-base-300 overflow-x-auto">
185
- {#each visibleCategories as cat}
186
- <button
187
- class="btn btn-ghost btn-xs btn-square {activeCategory === cat.id ? 'bg-primary/10 text-primary' : ''}"
188
- onclick={() => (activeCategory = cat.id)}
189
- title={cat.name}
190
- >
191
- {categoryEmojis[cat.id] || '❓'}
192
- </button>
193
- {/each}
194
- </div>
195
- {/if}
196
-
197
- <!-- Emoji grid -->
198
- <div class="flex-1 overflow-y-auto p-2 min-h-[200px]">
199
- {#if loading}
200
- <div class="flex items-center justify-center h-full">
201
- <span class="loading loading-spinner loading-sm"></span>
202
- </div>
203
- {:else if searchQuery && displayEmojis.length === 0}
204
- <div class="flex items-center justify-center h-full text-base-content/50 text-sm">
205
- No emojis found
206
- </div>
207
- {:else if activeCategory === 'custom' && !searchQuery}
208
- <!-- Custom emojis grid -->
209
- <div class="grid grid-cols-8 gap-1">
210
- {#each customEmojis as custom}
211
- <button
212
- class="w-8 h-8 flex items-center justify-center rounded hover:bg-base-200 transition-colors text-lg"
213
- onclick={() => handleCustomSelect(custom)}
214
- onmouseenter={() => (hoveredEmoji = { emoji: '🎨', name: custom.displayName, shortcode: custom.name })}
215
- onmouseleave={() => (hoveredEmoji = null)}
216
- title={custom.displayName}
217
- >
218
- 🎨
219
- </button>
220
- {/each}
221
- </div>
222
- {:else}
223
- <div class="grid grid-cols-8 gap-1">
224
- {#each displayEmojis as emoji}
225
- <button
226
- class="w-8 h-8 flex items-center justify-center rounded hover:bg-base-200 transition-colors text-lg"
227
- onclick={() => handleSelect(emoji)}
228
- onmouseenter={() => (hoveredEmoji = { emoji: emoji.unicode, name: emoji.name, shortcode: emoji.shortcode })}
229
- onmouseleave={() => (hoveredEmoji = null)}
230
- title="{emoji.name} :{emoji.shortcode}:"
231
- >
232
- {emoji.unicode}
233
- </button>
234
- {/each}
235
- </div>
236
- {/if}
237
- </div>
238
-
239
- <!-- Skin tone selector -->
240
- <div class="flex items-center justify-between px-2 py-1 border-t border-base-300">
241
- <div class="flex items-center gap-1">
242
- {#each SKIN_TONES as tone}
243
- <button
244
- class="w-6 h-6 flex items-center justify-center rounded text-sm {selectedSkinTone === tone.id ? 'bg-primary/20 ring-1 ring-primary' : 'hover:bg-base-200'}"
245
- onclick={() => (selectedSkinTone = tone.id)}
246
- title={tone.name}
247
- >
248
- {tone.preview}
249
- </button>
250
- {/each}
251
- </div>
252
-
253
- <!-- Hovered emoji info -->
254
- {#if hoveredEmoji}
255
- <div class="text-xs text-base-content/60 truncate max-w-[150px]">
256
- {hoveredEmoji.emoji} :{hoveredEmoji.shortcode}:
257
- </div>
258
- {/if}
259
- </div>
260
- </div>
261
- {/if}
@@ -1,193 +0,0 @@
1
- <!--
2
- MentionAutocomplete Component
3
-
4
- Shows search results for @-mention autocomplete.
5
- Receives search results via callback — no direct API calls.
6
- -->
7
-
8
- <script lang="ts">
9
- import { onMount, onDestroy } from 'svelte'
10
- import type { MentionItem } from '../../types/messaging'
11
-
12
- interface Props {
13
- visible: boolean
14
- query: string
15
- position: { x: number; y: number }
16
- onsearch: (query: string) => Promise<MentionItem[]>
17
- onselect?: (data: { result: MentionItem }) => void
18
- onclose?: () => void
19
- }
20
-
21
- let { visible, query, position, onsearch, onselect, onclose }: Props = $props()
22
-
23
- let results = $state<MentionItem[]>([])
24
- let loading = $state(false)
25
- let selectedIndex = $state(0)
26
- let dropdownElement = $state<HTMLElement | undefined>(undefined)
27
- let searchTimeout: ReturnType<typeof setTimeout>
28
-
29
- // Debounced search
30
- $effect(() => {
31
- if (visible && query && query.length >= 2) {
32
- clearTimeout(searchTimeout)
33
- searchTimeout = setTimeout(async () => {
34
- loading = true
35
- try {
36
- results = await onsearch(query)
37
- selectedIndex = 0
38
- } catch {
39
- results = []
40
- } finally {
41
- loading = false
42
- }
43
- }, 300)
44
- } else if (!visible || query.length < 2) {
45
- results = []
46
- }
47
-
48
- return () => clearTimeout(searchTimeout)
49
- })
50
-
51
- function handleKeyDown(event: KeyboardEvent) {
52
- if (!visible || results.length === 0) return
53
-
54
- switch (event.key) {
55
- case 'ArrowDown':
56
- event.preventDefault()
57
- selectedIndex = Math.min(selectedIndex + 1, results.length - 1)
58
- break
59
- case 'ArrowUp':
60
- event.preventDefault()
61
- selectedIndex = Math.max(selectedIndex - 1, 0)
62
- break
63
- case 'Enter':
64
- case 'Tab':
65
- event.preventDefault()
66
- if (results[selectedIndex]) {
67
- onselect?.({ result: results[selectedIndex] })
68
- }
69
- break
70
- case 'Escape':
71
- event.preventDefault()
72
- onclose?.()
73
- break
74
- }
75
- }
76
-
77
- function handleSelect(result: MentionItem) {
78
- onselect?.({ result })
79
- }
80
-
81
- function handleClickOutside(event: MouseEvent) {
82
- const target = event.target as Node
83
- if (dropdownElement?.contains(target)) return
84
- if (
85
- target instanceof HTMLTextAreaElement ||
86
- target instanceof HTMLInputElement ||
87
- (target as Element).closest?.('[contenteditable]')
88
- ) {
89
- return
90
- }
91
- onclose?.()
92
- }
93
-
94
- onMount(() => {
95
- document.addEventListener('keydown', handleKeyDown)
96
- })
97
-
98
- onDestroy(() => {
99
- document.removeEventListener('keydown', handleKeyDown)
100
- document.removeEventListener('click', handleClickOutside)
101
- })
102
-
103
- $effect(() => {
104
- if (visible) {
105
- const timeoutId = setTimeout(() => {
106
- document.addEventListener('click', handleClickOutside)
107
- }, 100)
108
- return () => {
109
- clearTimeout(timeoutId)
110
- document.removeEventListener('click', handleClickOutside)
111
- }
112
- } else {
113
- document.removeEventListener('click', handleClickOutside)
114
- }
115
- })
116
-
117
- let dropdownStyle = $state('')
118
-
119
- $effect(() => {
120
- if (!visible) {
121
- dropdownStyle = 'display: none;'
122
- return
123
- }
124
-
125
- const maxHeight = 400
126
- const itemHeight = 60
127
- const footerHeight = results.length > 0 ? 40 : 0
128
- const estimatedHeight = Math.min(
129
- maxHeight,
130
- Math.max(120, results.length * itemHeight + footerHeight + 20),
131
- )
132
-
133
- let top = position.y - estimatedHeight - 24
134
- const left = position.x
135
-
136
- if (top < 10) {
137
- top = position.y + 20
138
- }
139
-
140
- dropdownStyle = `position: fixed; top: ${top}px; left: ${left}px; z-index: 1000; max-height: ${maxHeight}px; width: 320px;`
141
- })
142
- </script>
143
-
144
- {#if visible}
145
- <div
146
- bind:this={dropdownElement}
147
- class="bg-base-200 border border-base-300 rounded-box overflow-y-auto overflow-x-hidden flex flex-col"
148
- style={dropdownStyle}
149
- >
150
- {#if loading}
151
- <div class="p-4 flex items-center justify-center">
152
- <span class="loading loading-spinner loading-sm mr-2"></span>
153
- <span class="text-sm text-base-content/70">Searching...</span>
154
- </div>
155
- {:else if results.length === 0}
156
- <div class="p-4 text-center">
157
- <span class="text-sm text-base-content/50">
158
- {query.length < 2 ? 'Type to search...' : `No results found for "${query}"`}
159
- </span>
160
- </div>
161
- {:else}
162
- {#each results as result, index}
163
- <button
164
- type="button"
165
- class="flex items-center gap-3 p-4 text-left hover:bg-base-200 transition-colors w-full border-b border-base-200 last:border-b-0 min-h-[60px] flex-shrink-0
166
- {selectedIndex === index ? 'bg-primary/10 text-primary' : 'text-base-content'}"
167
- onclick={() => handleSelect(result)}
168
- >
169
- <div class="flex-1 min-w-0">
170
- <div class="font-medium text-sm truncate">{result.name}</div>
171
- {#if result.secondary}
172
- <div class="text-xs text-base-content/60 truncate">{result.secondary}</div>
173
- {/if}
174
- </div>
175
-
176
- <div class="flex-shrink-0">
177
- <span class="badge badge-ghost badge-xs">{result.type}</span>
178
- </div>
179
- </button>
180
- {/each}
181
- {/if}
182
-
183
- {#if results.length > 0}
184
- <div class="px-3 py-2 border-t border-base-300 bg-base-50 flex-shrink-0">
185
- <div class="text-xs text-base-content/50 flex justify-between">
186
- <span>↑↓ Navigate</span>
187
- <span>Tab/Enter Select</span>
188
- <span>Esc Close</span>
189
- </div>
190
- </div>
191
- {/if}
192
- </div>
193
- {/if}