@joewinke/jatui 0.1.25 → 0.1.26

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 (242) hide show
  1. package/dist/actions/collapse.d.ts +80 -0
  2. package/dist/actions/collapse.js +134 -0
  3. package/dist/actions/railNav.d.ts +167 -0
  4. package/dist/actions/railNav.js +351 -0
  5. package/dist/components/AnnotationLayer.svelte +108 -0
  6. package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
  7. package/dist/components/AnnotationPanel.svelte +319 -0
  8. package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
  9. package/dist/components/AudioWaveform.svelte +701 -0
  10. package/dist/components/AudioWaveform.svelte.d.ts +19 -0
  11. package/dist/components/AvailabilityModal.svelte +177 -0
  12. package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
  13. package/dist/components/AvatarUpload.svelte +234 -0
  14. package/dist/components/AvatarUpload.svelte.d.ts +23 -0
  15. package/dist/components/Badge.svelte +38 -0
  16. package/dist/components/Badge.svelte.d.ts +14 -0
  17. package/dist/components/BookingForm.svelte +278 -0
  18. package/dist/components/BookingForm.svelte.d.ts +37 -0
  19. package/dist/components/BurndownChart.svelte +778 -0
  20. package/dist/components/BurndownChart.svelte.d.ts +17 -0
  21. package/dist/components/Button.svelte +81 -0
  22. package/dist/components/Button.svelte.d.ts +21 -0
  23. package/dist/components/CalendarPicker.svelte +284 -0
  24. package/dist/components/CalendarPicker.svelte.d.ts +22 -0
  25. package/dist/components/Card.svelte +67 -0
  26. package/dist/components/Card.svelte.d.ts +17 -0
  27. package/dist/components/CharacterCounter.svelte +82 -0
  28. package/dist/components/CharacterCounter.svelte.d.ts +10 -0
  29. package/dist/components/ChipInput.svelte +609 -0
  30. package/dist/components/ChipInput.svelte.d.ts +55 -0
  31. package/dist/components/Collapse.svelte +91 -0
  32. package/dist/components/Collapse.svelte.d.ts +18 -0
  33. package/dist/components/ColorSelector.svelte +167 -0
  34. package/dist/components/ColorSelector.svelte.d.ts +9 -0
  35. package/dist/components/CommentThread.svelte +773 -0
  36. package/dist/components/CommentThread.svelte.d.ts +32 -0
  37. package/dist/components/ConfirmDialog.svelte +348 -0
  38. package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
  39. package/dist/components/ConfirmModal.svelte +142 -0
  40. package/dist/components/ConfirmModal.svelte.d.ts +15 -0
  41. package/dist/components/ContextMenu.svelte +188 -0
  42. package/dist/components/ContextMenu.svelte.d.ts +31 -0
  43. package/dist/components/CountdownTimer.svelte +94 -0
  44. package/dist/components/CountdownTimer.svelte.d.ts +10 -0
  45. package/dist/components/DateRangePicker.svelte +194 -0
  46. package/dist/components/DateRangePicker.svelte.d.ts +10 -0
  47. package/dist/components/Drawer.svelte +143 -0
  48. package/dist/components/Drawer.svelte.d.ts +33 -0
  49. package/dist/components/EmojiPicker.svelte +159 -0
  50. package/dist/components/EmojiPicker.svelte.d.ts +8 -0
  51. package/dist/components/EntityPreviewCard.svelte +104 -0
  52. package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
  53. package/dist/components/FileDropzone.svelte +493 -0
  54. package/dist/components/FileDropzone.svelte.d.ts +40 -0
  55. package/dist/components/FilePicker.svelte +488 -0
  56. package/dist/components/FilePicker.svelte.d.ts +20 -0
  57. package/dist/components/FileThumbnail.svelte +80 -0
  58. package/dist/components/FileThumbnail.svelte.d.ts +12 -0
  59. package/dist/components/FilterDropdown.svelte +203 -0
  60. package/dist/components/FilterDropdown.svelte.d.ts +48 -0
  61. package/dist/components/FloatingActionBar.svelte +354 -0
  62. package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
  63. package/dist/components/GPSTracker.svelte +202 -0
  64. package/dist/components/GPSTracker.svelte.d.ts +34 -0
  65. package/dist/components/HunkDiffView.svelte +348 -0
  66. package/dist/components/HunkDiffView.svelte.d.ts +9 -0
  67. package/dist/components/ImageLightbox.svelte +274 -0
  68. package/dist/components/ImageLightbox.svelte.d.ts +25 -0
  69. package/dist/components/ImageUpload.svelte +147 -0
  70. package/dist/components/ImageUpload.svelte.d.ts +10 -0
  71. package/dist/components/InlineEdit.svelte +291 -0
  72. package/dist/components/InlineEdit.svelte.d.ts +45 -0
  73. package/dist/components/InputDialog.svelte +327 -0
  74. package/dist/components/InputDialog.svelte.d.ts +3 -0
  75. package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
  76. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
  77. package/dist/components/LazyImage.svelte +123 -0
  78. package/dist/components/LazyImage.svelte.d.ts +14 -0
  79. package/dist/components/LinkShortener.svelte +274 -0
  80. package/dist/components/LinkShortener.svelte.d.ts +27 -0
  81. package/dist/components/LoadingSpinner.svelte +106 -0
  82. package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
  83. package/dist/components/LocationMap.svelte +186 -0
  84. package/dist/components/LocationMap.svelte.d.ts +11 -0
  85. package/dist/components/MapView.svelte +341 -0
  86. package/dist/components/MapView.svelte.d.ts +20 -0
  87. package/dist/components/MarkupEditor.svelte +485 -0
  88. package/dist/components/MarkupEditor.svelte.d.ts +10 -0
  89. package/dist/components/MarkupOverlay.svelte +55 -0
  90. package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
  91. package/dist/components/MediaWorkbench.svelte +871 -0
  92. package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
  93. package/dist/components/MilestoneCard.svelte +98 -0
  94. package/dist/components/MilestoneCard.svelte.d.ts +16 -0
  95. package/dist/components/MilestoneTimeline.svelte +91 -0
  96. package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
  97. package/dist/components/Modal.svelte +244 -0
  98. package/dist/components/Modal.svelte.d.ts +35 -0
  99. package/dist/components/PDFViewer.svelte +105 -0
  100. package/dist/components/PDFViewer.svelte.d.ts +9 -0
  101. package/dist/components/PdfThumbnail.svelte +63 -0
  102. package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
  103. package/dist/components/PhoneInput.svelte +212 -0
  104. package/dist/components/PhoneInput.svelte.d.ts +25 -0
  105. package/dist/components/ResizableDivider.svelte +307 -0
  106. package/dist/components/ResizableDivider.svelte.d.ts +26 -0
  107. package/dist/components/ResizablePanel.svelte +305 -0
  108. package/dist/components/ResizablePanel.svelte.d.ts +18 -0
  109. package/dist/components/SearchDropdown.svelte +421 -0
  110. package/dist/components/SearchDropdown.svelte.d.ts +24 -0
  111. package/dist/components/SelectInput.svelte +237 -0
  112. package/dist/components/SelectInput.svelte.d.ts +26 -0
  113. package/dist/components/SidebarUserFooter.svelte +34 -0
  114. package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
  115. package/dist/components/SignaturePad.svelte +175 -0
  116. package/dist/components/SignaturePad.svelte.d.ts +17 -0
  117. package/dist/components/SmartImageEditor.svelte +720 -0
  118. package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
  119. package/dist/components/SortDropdown.svelte +156 -0
  120. package/dist/components/SortDropdown.svelte.d.ts +40 -0
  121. package/dist/components/Sparkline.svelte +116 -0
  122. package/dist/components/Sparkline.svelte.d.ts +12 -0
  123. package/dist/components/SpeechForm.svelte +114 -0
  124. package/dist/components/SpeechForm.svelte.d.ts +17 -0
  125. package/dist/components/StatusBadge.svelte +157 -0
  126. package/dist/components/StatusBadge.svelte.d.ts +24 -0
  127. package/dist/components/TaskTypeIcon.svelte +54 -0
  128. package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
  129. package/dist/components/TextArea.svelte +162 -0
  130. package/dist/components/TextArea.svelte.d.ts +30 -0
  131. package/dist/components/TextInput.svelte +131 -0
  132. package/dist/components/TextInput.svelte.d.ts +29 -0
  133. package/dist/components/ThemeSelector.svelte +206 -0
  134. package/dist/components/ThemeSelector.svelte.d.ts +14 -0
  135. package/dist/components/TimeSlotPicker.svelte +162 -0
  136. package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
  137. package/dist/components/UserAvatar.svelte +72 -0
  138. package/dist/components/UserAvatar.svelte.d.ts +11 -0
  139. package/dist/components/VariablePicker.svelte +170 -0
  140. package/dist/components/VariablePicker.svelte.d.ts +40 -0
  141. package/dist/components/VoicePlayer.svelte +422 -0
  142. package/dist/components/VoicePlayer.svelte.d.ts +18 -0
  143. package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
  144. package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
  145. package/dist/components/markup.d.ts +47 -0
  146. package/dist/components/markup.js +201 -0
  147. package/dist/components/messaging/Avatar.svelte +81 -0
  148. package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
  149. package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
  150. package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
  151. package/dist/components/messaging/ChannelList.svelte +107 -0
  152. package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
  153. package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
  154. package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
  155. package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
  156. package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
  157. package/dist/components/messaging/CreateChannelModal.svelte +190 -0
  158. package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
  159. package/dist/components/messaging/DirectMessageList.svelte +145 -0
  160. package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
  161. package/dist/components/messaging/EmojiSelector.svelte +261 -0
  162. package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
  163. package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
  164. package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
  165. package/dist/components/messaging/MessageAttachment.svelte +273 -0
  166. package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
  167. package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
  168. package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
  169. package/dist/components/messaging/MessageInput.svelte +452 -0
  170. package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
  171. package/dist/components/messaging/MessageItem.svelte +341 -0
  172. package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
  173. package/dist/components/messaging/MessageThread.svelte +306 -0
  174. package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
  175. package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
  176. package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
  177. package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
  178. package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
  179. package/dist/components/messaging/StartDMModal.svelte +100 -0
  180. package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
  181. package/dist/components/messaging/ThreadPanel.svelte +153 -0
  182. package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
  183. package/dist/components/pipeline/DESIGN.md +361 -0
  184. package/dist/components/pipeline/Pipeline.svelte +391 -0
  185. package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
  186. package/dist/components/pipeline/PipelineCard.svelte +94 -0
  187. package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
  188. package/dist/components/pipeline/PipelineColumn.svelte +163 -0
  189. package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
  190. package/dist/components/replay/ChapterTimeline.svelte +326 -0
  191. package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
  192. package/dist/components/session-nav/transcriptModel.d.ts +132 -0
  193. package/dist/components/session-nav/transcriptModel.js +285 -0
  194. package/dist/data/emojis.d.ts +18 -0
  195. package/dist/data/emojis.js +1196 -0
  196. package/dist/index.d.ts +142 -0
  197. package/dist/index.js +142 -0
  198. package/dist/stores/confirmDialog.svelte.d.ts +21 -0
  199. package/dist/stores/confirmDialog.svelte.js +31 -0
  200. package/dist/stores/inputDialog.svelte.d.ts +24 -0
  201. package/dist/stores/inputDialog.svelte.js +32 -0
  202. package/dist/styles/rail.css +63 -0
  203. package/dist/types/annotation.d.ts +32 -0
  204. package/dist/types/annotation.js +3 -0
  205. package/dist/types/booking.d.ts +115 -0
  206. package/dist/types/booking.js +24 -0
  207. package/dist/types/comments.d.ts +65 -0
  208. package/dist/types/comments.js +22 -0
  209. package/dist/types/entityPreview.d.ts +42 -0
  210. package/dist/types/entityPreview.js +10 -0
  211. package/dist/types/filePicker.d.ts +15 -0
  212. package/dist/types/filePicker.js +1 -0
  213. package/dist/types/googleMaps.d.ts +51 -0
  214. package/dist/types/maps.d.ts +43 -0
  215. package/dist/types/maps.js +4 -0
  216. package/dist/types/messaging.d.ts +282 -0
  217. package/dist/types/messaging.js +100 -0
  218. package/dist/types/milestone.d.ts +20 -0
  219. package/dist/types/milestone.js +7 -0
  220. package/dist/types/pipeline.d.ts +115 -0
  221. package/dist/types/pipeline.js +15 -0
  222. package/dist/types/smartImageEditor.d.ts +42 -0
  223. package/dist/types/smartImageEditor.js +1 -0
  224. package/dist/types/templateVars.d.ts +34 -0
  225. package/dist/types/templateVars.js +12 -0
  226. package/dist/utils/currency.d.ts +8 -0
  227. package/dist/utils/currency.js +17 -0
  228. package/dist/utils/daisyuiColors.d.ts +37 -0
  229. package/dist/utils/daisyuiColors.js +152 -0
  230. package/dist/utils/dateFormatters.d.ts +69 -0
  231. package/dist/utils/dateFormatters.js +199 -0
  232. package/dist/utils/googleMapsLoader.d.ts +19 -0
  233. package/dist/utils/googleMapsLoader.js +121 -0
  234. package/dist/utils/mentionParser.d.ts +67 -0
  235. package/dist/utils/mentionParser.js +132 -0
  236. package/dist/utils/phone.d.ts +13 -0
  237. package/dist/utils/phone.js +67 -0
  238. package/dist/utils/phoneFormat.d.ts +26 -0
  239. package/dist/utils/phoneFormat.js +69 -0
  240. package/dist/utils/taskUtils.d.ts +37 -0
  241. package/dist/utils/taskUtils.js +71 -0
  242. package/package.json +3 -2
@@ -0,0 +1,291 @@
1
+ <script module lang="ts">
2
+ // Declared in the module script so `import type { DisplaySegment } from
3
+ // './InlineEdit.svelte'` resolves (instance-script exports are not module members).
4
+ /** A display segment for formula-aware rendering */
5
+ export interface DisplaySegment {
6
+ type: 'text' | 'formula';
7
+ display: string;
8
+ tooltip?: string;
9
+ }
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ /**
14
+ * InlineEdit Component
15
+ *
16
+ * A reusable inline editing component for text and textarea inputs.
17
+ * Shows display value when not editing, converts to input on click.
18
+ *
19
+ * Features:
20
+ * - Auto-saves on blur or Enter (Enter only for text type)
21
+ * - Cancels on Escape, reverting to original value
22
+ * - Hover state to indicate editability
23
+ * - Auto-focus when editing starts
24
+ * - Disabled state during saves
25
+ * - Optional formula-aware display: segments with type/display/tooltip
26
+ */
27
+
28
+ interface Props {
29
+ /** Current value */
30
+ value: string;
31
+ /** Callback when value is saved */
32
+ onSave: (newValue: string) => Promise<void> | void;
33
+ /** Input type: 'text' or 'textarea' */
34
+ type?: 'text' | 'textarea';
35
+ /** Placeholder text when empty */
36
+ placeholder?: string;
37
+ /** Disable editing */
38
+ disabled?: boolean;
39
+ /** Additional CSS classes for the container */
40
+ class?: string;
41
+ /** Number of rows for textarea (default: 3) */
42
+ rows?: number;
43
+ /** Show save/cancel buttons instead of auto-save on blur */
44
+ showButtons?: boolean;
45
+ /** Truncate long text with ellipsis (shows full text on hover) */
46
+ truncate?: boolean;
47
+ /** Optional display segments for formula-aware rendering */
48
+ displaySegments?: DisplaySegment[];
49
+ }
50
+
51
+ let {
52
+ value,
53
+ onSave,
54
+ type = 'text',
55
+ placeholder = 'Click to edit...',
56
+ disabled = false,
57
+ class: className = '',
58
+ rows = 3,
59
+ showButtons = false,
60
+ truncate = false,
61
+ displaySegments = []
62
+ }: Props = $props();
63
+
64
+ // --- Formula rendering ---
65
+ let hasFormulas = $derived(displaySegments.length > 0);
66
+
67
+ // Internal state
68
+ let isEditing = $state(false);
69
+ // svelte-ignore state_referenced_locally
70
+ let editValue = $state(value);
71
+ let isSaving = $state(false);
72
+ let inputElement = $state<HTMLInputElement | HTMLTextAreaElement | null>(null);
73
+
74
+ // Sync editValue when value prop changes (only when not editing)
75
+ $effect(() => {
76
+ if (!isEditing) {
77
+ editValue = value;
78
+ }
79
+ });
80
+
81
+ // Start editing
82
+ function startEditing() {
83
+ if (disabled || isSaving) return;
84
+ editValue = value;
85
+ isEditing = true;
86
+ }
87
+
88
+ // Cancel editing
89
+ function cancelEditing() {
90
+ editValue = value;
91
+ isEditing = false;
92
+ }
93
+
94
+ // Save the edited value
95
+ async function saveValue() {
96
+ // Don't save if value hasn't changed
97
+ if (editValue === value) {
98
+ isEditing = false;
99
+ return;
100
+ }
101
+
102
+ isSaving = true;
103
+ try {
104
+ await onSave(editValue);
105
+ isEditing = false;
106
+ } catch (err) {
107
+ console.error('Failed to save:', err);
108
+ // Keep editing mode open on error
109
+ } finally {
110
+ isSaving = false;
111
+ }
112
+ }
113
+
114
+ // Handle keydown events
115
+ function handleKeyDown(event: KeyboardEvent) {
116
+ if (event.key === 'Escape') {
117
+ event.preventDefault();
118
+ cancelEditing();
119
+ } else if (event.key === 'Enter') {
120
+ if (type === 'text' || event.metaKey || event.ctrlKey) {
121
+ event.preventDefault();
122
+ saveValue();
123
+ }
124
+ }
125
+ }
126
+
127
+ // Handle blur (auto-save when not using buttons)
128
+ function handleBlur() {
129
+ if (!showButtons) {
130
+ saveValue();
131
+ }
132
+ }
133
+
134
+ // Auto-focus action
135
+ function autofocus(node: HTMLElement) {
136
+ requestAnimationFrame(() => {
137
+ node.focus();
138
+ if (node instanceof HTMLInputElement) {
139
+ node.select();
140
+ }
141
+ });
142
+ }
143
+
144
+ import { fade } from 'svelte/transition';
145
+
146
+ // Auto-resize textarea to fit content
147
+ function autoresize(node: HTMLTextAreaElement) {
148
+ function resize() {
149
+ node.style.height = 'auto';
150
+ node.style.height = node.scrollHeight + 'px';
151
+ }
152
+
153
+ requestAnimationFrame(resize);
154
+
155
+ node.addEventListener('input', resize);
156
+
157
+ return {
158
+ destroy() {
159
+ node.removeEventListener('input', resize);
160
+ }
161
+ };
162
+ }
163
+ </script>
164
+
165
+ {#if isEditing}
166
+ <!-- Edit mode -->
167
+ <div class="inline-edit-container w-full {className}" in:fade={{ duration: 120 }}>
168
+ {#if type === 'textarea'}
169
+ <textarea
170
+ bind:this={inputElement}
171
+ bind:value={editValue}
172
+ {placeholder}
173
+ {rows}
174
+ class="textarea textarea-bordered w-full text-[0.9375rem] resize-none overflow-hidden"
175
+ style="min-height: {rows * 1.5}rem;"
176
+ disabled={isSaving}
177
+ onkeydown={handleKeyDown}
178
+ onblur={handleBlur}
179
+ use:autofocus
180
+ use:autoresize
181
+ ></textarea>
182
+ {:else}
183
+ <input
184
+ bind:this={inputElement}
185
+ bind:value={editValue}
186
+ type="text"
187
+ {placeholder}
188
+ class="input input-bordered input-sm w-full"
189
+ disabled={isSaving}
190
+ onkeydown={handleKeyDown}
191
+ onblur={handleBlur}
192
+ use:autofocus
193
+ />
194
+ {/if}
195
+
196
+ {#if showButtons}
197
+ <div class="flex gap-1 mt-1">
198
+ <button
199
+ class="btn btn-xs btn-success"
200
+ onclick={saveValue}
201
+ disabled={isSaving}
202
+ >
203
+ {#if isSaving}
204
+ <span class="loading loading-spinner loading-xs"></span>
205
+ {:else}
206
+ Save
207
+ {/if}
208
+ </button>
209
+ <button
210
+ class="btn btn-xs btn-ghost"
211
+ onclick={cancelEditing}
212
+ disabled={isSaving}
213
+ >
214
+ Cancel
215
+ </button>
216
+ </div>
217
+ {:else if isSaving}
218
+ <div class="absolute right-2 top-1/2 -translate-y-1/2">
219
+ <span class="loading loading-spinner loading-xs"></span>
220
+ </div>
221
+ {/if}
222
+ </div>
223
+ {:else}
224
+ <!-- Display mode -->
225
+ <button
226
+ class="inline-edit-display text-left w-full rounded px-2 py-1 transition-colors {disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer hover:bg-base-200'} {truncate ? 'inline-edit-truncate' : ''} {className}"
227
+ in:fade={{ duration: 120 }}
228
+ onclick={startEditing}
229
+ disabled={disabled}
230
+ type="button"
231
+ title={truncate && value ? value : undefined}
232
+ >
233
+ {#if value}
234
+ {#if hasFormulas}
235
+ <span class={type === 'textarea' ? 'whitespace-pre-wrap' : (truncate ? 'truncate-text' : '')}>
236
+ {#each displaySegments as seg}
237
+ {#if seg.type === 'formula'}
238
+ <span class="inline-fx-result" title={seg.tooltip}>{seg.display}</span>
239
+ {:else}
240
+ {seg.display}
241
+ {/if}
242
+ {/each}
243
+ </span>
244
+ {:else if type === 'textarea'}
245
+ <span class="whitespace-pre-wrap">{value}</span>
246
+ {:else}
247
+ <span class={truncate ? 'truncate-text' : ''}>{value}</span>
248
+ {/if}
249
+ {:else}
250
+ <span class="text-base-content/50 italic">{placeholder}</span>
251
+ {/if}
252
+ </button>
253
+ {/if}
254
+
255
+ <style>
256
+ .inline-edit-container {
257
+ position: relative;
258
+ }
259
+
260
+ .inline-edit-display {
261
+ min-height: 2rem;
262
+ display: flex;
263
+ align-items: center;
264
+ }
265
+
266
+ /* Truncation styles */
267
+ .inline-edit-truncate {
268
+ max-width: 100%;
269
+ overflow: hidden;
270
+ }
271
+
272
+ .truncate-text {
273
+ display: block;
274
+ white-space: nowrap;
275
+ overflow: hidden;
276
+ text-overflow: ellipsis;
277
+ max-width: 100%;
278
+ }
279
+
280
+ .inline-fx-result {
281
+ display: inline;
282
+ padding: 0 4px;
283
+ border-radius: 3px;
284
+ background: oklch(0.25 0.06 80 / 0.4);
285
+ border: 1px solid oklch(0.35 0.08 80 / 0.3);
286
+ color: oklch(0.82 0.10 80);
287
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
288
+ font-size: 0.85em;
289
+ cursor: help;
290
+ }
291
+ </style>
@@ -0,0 +1,45 @@
1
+ /** A display segment for formula-aware rendering */
2
+ export interface DisplaySegment {
3
+ type: 'text' | 'formula';
4
+ display: string;
5
+ tooltip?: string;
6
+ }
7
+ /**
8
+ * InlineEdit Component
9
+ *
10
+ * A reusable inline editing component for text and textarea inputs.
11
+ * Shows display value when not editing, converts to input on click.
12
+ *
13
+ * Features:
14
+ * - Auto-saves on blur or Enter (Enter only for text type)
15
+ * - Cancels on Escape, reverting to original value
16
+ * - Hover state to indicate editability
17
+ * - Auto-focus when editing starts
18
+ * - Disabled state during saves
19
+ * - Optional formula-aware display: segments with type/display/tooltip
20
+ */
21
+ interface Props {
22
+ /** Current value */
23
+ value: string;
24
+ /** Callback when value is saved */
25
+ onSave: (newValue: string) => Promise<void> | void;
26
+ /** Input type: 'text' or 'textarea' */
27
+ type?: 'text' | 'textarea';
28
+ /** Placeholder text when empty */
29
+ placeholder?: string;
30
+ /** Disable editing */
31
+ disabled?: boolean;
32
+ /** Additional CSS classes for the container */
33
+ class?: string;
34
+ /** Number of rows for textarea (default: 3) */
35
+ rows?: number;
36
+ /** Show save/cancel buttons instead of auto-save on blur */
37
+ showButtons?: boolean;
38
+ /** Truncate long text with ellipsis (shows full text on hover) */
39
+ truncate?: boolean;
40
+ /** Optional display segments for formula-aware rendering */
41
+ displaySegments?: DisplaySegment[];
42
+ }
43
+ declare const InlineEdit: import("svelte").Component<Props, {}, "">;
44
+ type InlineEdit = ReturnType<typeof InlineEdit>;
45
+ export default InlineEdit;
@@ -0,0 +1,327 @@
1
+ <script lang="ts">
2
+ import { fly, fade } from 'svelte/transition';
3
+ import { cubicOut } from 'svelte/easing';
4
+ import { tick } from 'svelte';
5
+ import { getPendingInput, submitInput, cancelInput } from '../stores/inputDialog.svelte';
6
+
7
+ const pending = $derived(getPendingInput());
8
+ const opts = $derived(pending?.options ?? null);
9
+ const visible = $derived(pending !== null);
10
+
11
+ let value = $state('');
12
+ let error = $state<string | null>(null);
13
+ let inputRef = $state<HTMLInputElement | null>(null);
14
+
15
+ $effect(() => {
16
+ if (visible && opts) {
17
+ value = '';
18
+ error = null;
19
+ tick().then(() => inputRef?.focus());
20
+ }
21
+ });
22
+
23
+ function handleSubmit() {
24
+ if (opts?.validate) {
25
+ const msg = opts.validate(value);
26
+ if (msg !== null) { error = msg; return; }
27
+ }
28
+ error = null;
29
+ submitInput(value);
30
+ }
31
+
32
+ function handleKeydown(e: KeyboardEvent) {
33
+ if (!visible) return;
34
+ if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); cancelInput(); }
35
+ if (e.key === 'Enter') { e.preventDefault(); e.stopPropagation(); handleSubmit(); }
36
+ }
37
+
38
+ $effect(() => {
39
+ window.addEventListener('keydown', handleKeydown, true);
40
+ return () => window.removeEventListener('keydown', handleKeydown, true);
41
+ });
42
+ </script>
43
+
44
+ {#if visible && opts}
45
+ <!-- Scrim (above ConfirmDialog z-9990/9991) -->
46
+ <div
47
+ class="id-scrim"
48
+ transition:fade={{ duration: 120 }}
49
+ role="presentation"
50
+ onclick={cancelInput}
51
+ ></div>
52
+
53
+ <!-- Dialog -->
54
+ <div
55
+ class="id-wrap"
56
+ role="dialog"
57
+ aria-modal="true"
58
+ aria-label="{opts.title}. Press Enter to confirm or Escape to cancel."
59
+ in:fly={{ y: -20, duration: 200, easing: cubicOut }}
60
+ out:fade={{ duration: 120 }}
61
+ >
62
+ <div class="id-box">
63
+ <!-- Icon + title -->
64
+ <div class="id-header">
65
+ <div class="id-icon">
66
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
67
+ <path d="M12 20h9"/>
68
+ <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
69
+ </svg>
70
+ </div>
71
+ <div class="id-title-group">
72
+ <h3 class="id-title">{opts.title}</h3>
73
+ {#if opts.body}
74
+ <p class="id-body">{opts.body}</p>
75
+ {/if}
76
+ </div>
77
+ </div>
78
+
79
+ <!-- Input field -->
80
+ <input
81
+ bind:this={inputRef}
82
+ bind:value
83
+ oninput={() => { if (error) error = null; }}
84
+ type="text"
85
+ class="id-input"
86
+ class:id-input-error={error !== null}
87
+ placeholder={opts.placeholder ?? ''}
88
+ maxlength={opts.maxLength}
89
+ autocomplete="off"
90
+ spellcheck="false"
91
+ />
92
+ {#if error !== null || opts.maxLength}
93
+ <div class="id-field-footer">
94
+ {#if error !== null}
95
+ <p class="id-error">{error}</p>
96
+ {:else}
97
+ <span></span>
98
+ {/if}
99
+ {#if opts.maxLength}
100
+ <span class="id-char-count">{value.length}/{opts.maxLength}</span>
101
+ {/if}
102
+ </div>
103
+ {/if}
104
+
105
+ <!-- Actions -->
106
+ <div class="id-actions">
107
+ <button
108
+ type="button"
109
+ class="id-btn id-btn-cancel"
110
+ onclick={cancelInput}
111
+ >
112
+ <kbd class="id-kbd">Esc</kbd>
113
+ <span>{opts.cancelLabel ?? 'Cancel'}</span>
114
+ </button>
115
+
116
+ <button
117
+ type="button"
118
+ class="id-btn id-btn-confirm"
119
+ onclick={handleSubmit}
120
+ >
121
+ <kbd class="id-kbd">⏎</kbd>
122
+ <span>{opts.confirmLabel ?? 'OK'}</span>
123
+ </button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ {/if}
128
+
129
+ <style>
130
+ .id-scrim {
131
+ position: fixed;
132
+ inset: 0;
133
+ background: oklch(0 0 0 / 0.45);
134
+ z-index: 9991;
135
+ backdrop-filter: blur(2px);
136
+ }
137
+
138
+ .id-wrap {
139
+ position: fixed;
140
+ top: 5rem;
141
+ left: 50%;
142
+ transform: translateX(-50%);
143
+ z-index: 9992;
144
+ pointer-events: auto;
145
+ width: max-content;
146
+ max-width: min(92vw, 460px);
147
+ }
148
+
149
+ .id-box {
150
+ display: flex;
151
+ flex-direction: column;
152
+ gap: 0.625rem;
153
+ padding: 1rem 1.125rem 0.625rem;
154
+ background: oklch(0.17 0.02 250 / 0.98);
155
+ border: 1px solid oklch(0.32 0.03 250);
156
+ border-radius: 0.875rem;
157
+ box-shadow:
158
+ 0 20px 60px oklch(0 0 0 / 0.55),
159
+ 0 0 0 1px oklch(0.28 0.03 250 / 0.5);
160
+ backdrop-filter: blur(16px);
161
+ }
162
+
163
+ .id-header {
164
+ display: flex;
165
+ align-items: flex-start;
166
+ gap: 0.875rem;
167
+ }
168
+
169
+ .id-icon {
170
+ flex-shrink: 0;
171
+ width: 2.25rem;
172
+ height: 2.25rem;
173
+ border-radius: 0.5rem;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ background: oklch(0.30 0.06 200 / 0.35);
178
+ border: 1px solid oklch(0.45 0.10 200 / 0.4);
179
+ color: oklch(0.75 0.12 200);
180
+ }
181
+
182
+ .id-icon svg {
183
+ width: 1.125rem;
184
+ height: 1.125rem;
185
+ }
186
+
187
+ .id-title-group {
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 0.25rem;
191
+ flex: 1;
192
+ min-width: 0;
193
+ }
194
+
195
+ .id-title {
196
+ margin: 0;
197
+ font-size: 0.9375rem;
198
+ font-weight: 600;
199
+ color: oklch(0.92 0.04 250);
200
+ line-height: 1.3;
201
+ }
202
+
203
+ .id-body {
204
+ margin: 0;
205
+ font-size: 0.8rem;
206
+ color: oklch(0.60 0.03 250);
207
+ line-height: 1.5;
208
+ }
209
+
210
+ .id-input {
211
+ width: 100%;
212
+ padding: 0.4375rem 0.75rem;
213
+ font-size: 0.875rem;
214
+ background: oklch(0.13 0.02 250);
215
+ border: 1px solid oklch(0.32 0.03 250);
216
+ border-radius: 0.5rem;
217
+ color: oklch(0.92 0.04 250);
218
+ outline: none;
219
+ transition: border-color 0.1s ease;
220
+ box-sizing: border-box;
221
+ }
222
+
223
+ .id-input::placeholder {
224
+ color: oklch(0.45 0.03 250);
225
+ }
226
+
227
+ .id-input:focus {
228
+ border-color: oklch(0.55 0.12 250);
229
+ box-shadow: 0 0 0 2px oklch(0.55 0.12 250 / 0.2);
230
+ }
231
+
232
+ .id-input-error,
233
+ .id-input-error:focus {
234
+ border-color: oklch(0.55 0.20 25);
235
+ box-shadow: 0 0 0 2px oklch(0.55 0.20 25 / 0.2);
236
+ }
237
+
238
+ .id-field-footer {
239
+ display: flex;
240
+ align-items: center;
241
+ justify-content: space-between;
242
+ gap: 0.5rem;
243
+ margin-top: -0.125rem;
244
+ min-height: 1rem;
245
+ }
246
+
247
+ .id-error {
248
+ margin: 0;
249
+ font-size: 0.75rem;
250
+ color: oklch(0.65 0.18 25);
251
+ line-height: 1.4;
252
+ }
253
+
254
+ .id-char-count {
255
+ margin-left: auto;
256
+ font-size: 0.75rem;
257
+ color: oklch(0.50 0.03 250);
258
+ font-variant-numeric: tabular-nums;
259
+ white-space: nowrap;
260
+ flex-shrink: 0;
261
+ }
262
+
263
+ .id-actions {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: 0.375rem;
267
+ padding-top: 0.125rem;
268
+ }
269
+
270
+ .id-btn {
271
+ display: inline-flex;
272
+ align-items: center;
273
+ gap: 0.375rem;
274
+ padding: 0.3125rem 0.75rem;
275
+ font-size: 0.8rem;
276
+ font-weight: 500;
277
+ border-radius: 0.5rem;
278
+ border: 1px solid transparent;
279
+ background: transparent;
280
+ color: oklch(0.80 0.04 250);
281
+ cursor: pointer;
282
+ transition: background 0.1s ease, border-color 0.1s ease, color 0.1s ease;
283
+ white-space: nowrap;
284
+ }
285
+
286
+ .id-btn:hover {
287
+ background: oklch(0.24 0.03 250);
288
+ border-color: oklch(0.32 0.04 250);
289
+ }
290
+
291
+ .id-btn-cancel:focus-visible {
292
+ outline: 2px solid oklch(0.60 0.10 250);
293
+ outline-offset: 2px;
294
+ }
295
+
296
+ .id-btn-confirm {
297
+ background: oklch(0.28 0.04 250 / 0.6);
298
+ border-color: oklch(0.40 0.05 250);
299
+ }
300
+
301
+ .id-btn-confirm:hover {
302
+ background: oklch(0.34 0.06 250);
303
+ border-color: oklch(0.50 0.08 250);
304
+ }
305
+
306
+ .id-btn-confirm:focus-visible {
307
+ outline: 2px solid oklch(0.60 0.10 250);
308
+ outline-offset: 2px;
309
+ }
310
+
311
+ .id-kbd {
312
+ font-family: ui-monospace, monospace;
313
+ font-size: 0.65rem;
314
+ padding: 0.0625rem 0.3125rem;
315
+ background: oklch(0.22 0.02 250);
316
+ border: 1px solid oklch(0.30 0.03 250);
317
+ border-bottom-width: 2px;
318
+ border-radius: 0.25rem;
319
+ color: oklch(0.75 0.04 250);
320
+ line-height: 1.6;
321
+ }
322
+
323
+ @media (prefers-reduced-motion: reduce) {
324
+ .id-input { transition: none; }
325
+ .id-btn { transition: none; }
326
+ }
327
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const InputDialog: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type InputDialog = ReturnType<typeof InputDialog>;
3
+ export default InputDialog;