@joewinke/jatui 0.1.10 → 0.1.19

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 (91) hide show
  1. package/README.md +123 -0
  2. package/package.json +3 -1
  3. package/src/lib/actions/railNav.ts +473 -0
  4. package/src/lib/components/AnnotationLayer.svelte +108 -0
  5. package/src/lib/components/AnnotationPanel.svelte +319 -0
  6. package/src/lib/components/AudioWaveform.svelte +9 -5
  7. package/src/lib/components/AvailabilityModal.svelte +7 -3
  8. package/src/lib/components/AvatarUpload.svelte +27 -4
  9. package/src/lib/components/BookingForm.svelte +11 -9
  10. package/src/lib/components/BurndownChart.svelte +778 -0
  11. package/src/lib/components/Button.svelte +10 -1
  12. package/src/lib/components/CalendarPicker.svelte +3 -3
  13. package/src/lib/components/Card.svelte +2 -2
  14. package/src/lib/components/ChipInput.svelte +21 -15
  15. package/src/lib/components/ColorSelector.svelte +17 -13
  16. package/src/lib/components/CommentThread.svelte +773 -0
  17. package/src/lib/components/ConfirmDialog.svelte +348 -0
  18. package/src/lib/components/ConfirmModal.svelte +78 -11
  19. package/src/lib/components/ContextMenu.svelte +188 -0
  20. package/src/lib/components/CountdownTimer.svelte +1 -1
  21. package/src/lib/components/DateRangePicker.svelte +6 -4
  22. package/src/lib/components/Drawer.svelte +36 -3
  23. package/src/lib/components/EntityPreviewCard.svelte +104 -0
  24. package/src/lib/components/FileDropzone.svelte +493 -0
  25. package/src/lib/components/FilePicker.svelte +83 -14
  26. package/src/lib/components/FileThumbnail.svelte +80 -0
  27. package/src/lib/components/FilterDropdown.svelte +11 -11
  28. package/src/lib/components/HunkDiffView.svelte +348 -0
  29. package/src/lib/components/ImageLightbox.svelte +274 -0
  30. package/src/lib/components/ImageUpload.svelte +58 -9
  31. package/src/lib/components/InlineEdit.svelte +15 -9
  32. package/src/lib/components/InputDialog.svelte +327 -0
  33. package/src/lib/components/LazyImage.svelte +1 -0
  34. package/src/lib/components/LinkShortener.svelte +1 -1
  35. package/src/lib/components/LoadingSpinner.svelte +6 -2
  36. package/src/lib/components/MarkupEditor.svelte +485 -0
  37. package/src/lib/components/MarkupOverlay.svelte +55 -0
  38. package/src/lib/components/MediaWorkbench.svelte +871 -0
  39. package/src/lib/components/MilestoneCard.svelte +1 -1
  40. package/src/lib/components/MilestoneTimeline.svelte +1 -1
  41. package/src/lib/components/Modal.svelte +39 -4
  42. package/src/lib/components/PDFViewer.svelte +105 -0
  43. package/src/lib/components/PdfThumbnail.svelte +3 -1
  44. package/src/lib/components/PhoneInput.svelte +183 -63
  45. package/src/lib/components/ResizablePanel.svelte +4 -4
  46. package/src/lib/components/SearchDropdown.svelte +26 -13
  47. package/src/lib/components/SelectInput.svelte +26 -4
  48. package/src/lib/components/SidebarUserFooter.svelte +1 -1
  49. package/src/lib/components/SignaturePad.svelte +8 -4
  50. package/src/lib/components/SmartImageEditor.svelte +720 -0
  51. package/src/lib/components/SortDropdown.svelte +9 -3
  52. package/src/lib/components/Sparkline.svelte +9 -0
  53. package/src/lib/components/StatusBadge.svelte +20 -18
  54. package/src/lib/components/TextArea.svelte +24 -5
  55. package/src/lib/components/TextInput.svelte +29 -6
  56. package/src/lib/components/ThemeSelector.svelte +15 -4
  57. package/src/lib/components/TimeSlotPicker.svelte +7 -7
  58. package/src/lib/components/UserAvatar.svelte +14 -1
  59. package/src/lib/components/VariablePicker.svelte +170 -0
  60. package/src/lib/components/VoicePlayer.svelte +4 -3
  61. package/src/lib/components/markup.ts +287 -0
  62. package/src/lib/components/messaging/ChannelInfoModal.svelte +9 -9
  63. package/src/lib/components/messaging/ChannelList.svelte +1 -1
  64. package/src/lib/components/messaging/ChannelMembersModal.svelte +1 -1
  65. package/src/lib/components/messaging/CreateChannelModal.svelte +1 -1
  66. package/src/lib/components/messaging/DirectMessageList.svelte +1 -1
  67. package/src/lib/components/messaging/EmojiSelector.svelte +2 -1
  68. package/src/lib/components/messaging/MentionAutocomplete.svelte +1 -1
  69. package/src/lib/components/messaging/MessageAttachment.svelte +3 -3
  70. package/src/lib/components/messaging/MessageAttachmentUpload.svelte +3 -3
  71. package/src/lib/components/messaging/MessageInput.svelte +1 -1
  72. package/src/lib/components/messaging/MessageItem.svelte +6 -3
  73. package/src/lib/components/messaging/NotificationSettingsModal.svelte +1 -1
  74. package/src/lib/components/messaging/QuotedMessageDisplay.svelte +6 -1
  75. package/src/lib/components/messaging/StartDMModal.svelte +1 -1
  76. package/src/lib/components/pipeline/Pipeline.svelte +4 -4
  77. package/src/lib/components/pipeline/PipelineCard.svelte +1 -1
  78. package/src/lib/components/pipeline/PipelineColumn.svelte +8 -3
  79. package/src/lib/index.ts +105 -1
  80. package/src/lib/stores/confirmDialog.svelte.ts +48 -0
  81. package/src/lib/stores/inputDialog.svelte.ts +51 -0
  82. package/src/lib/styles/rail.css +63 -0
  83. package/src/lib/types/annotation.ts +38 -0
  84. package/src/lib/types/comments.ts +97 -0
  85. package/src/lib/types/entityPreview.ts +45 -0
  86. package/src/lib/types/filePicker.ts +2 -0
  87. package/src/lib/types/smartImageEditor.ts +39 -0
  88. package/src/lib/types/templateVars.ts +36 -0
  89. package/src/lib/utils/dateFormatters.ts +12 -10
  90. package/src/lib/utils/phone.ts +80 -0
  91. package/src/lib/utils/taskUtils.ts +21 -7
@@ -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>
@@ -25,6 +25,7 @@
25
25
 
26
26
  let loaded = $state(false)
27
27
  let error = $state(false)
28
+ // svelte-ignore state_referenced_locally
28
29
  let shouldLoad = $state(preload || priority)
29
30
  let containerRef: HTMLDivElement
30
31
 
@@ -77,7 +77,7 @@
77
77
  <div class="space-y-4 {className}">
78
78
  {#if showHeader}
79
79
  <div class="flex items-center justify-between">
80
- <h2 class="text-xl font-bold">Short Links</h2>
80
+ <h2 class="text-lg font-semibold">Short Links</h2>
81
81
  {#if !showForm}
82
82
  <button class="btn btn-primary btn-sm" onclick={() => showForm = true}>
83
83
  <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -61,7 +61,7 @@
61
61
 
62
62
  const containerClass = $derived(
63
63
  [
64
- overlay ? 'fixed inset-0 bg-black/50 flex items-center justify-center z-50' : '',
64
+ overlay ? 'fixed inset-0 flex items-center justify-center z-50' : '',
65
65
  centered && !overlay ? 'flex items-center justify-center' : '',
66
66
  textPosition === 'left' || textPosition === 'right' ? 'flex items-center' : '',
67
67
  textPosition === 'top' || textPosition === 'bottom' ? 'flex flex-col items-center' : ''
@@ -70,6 +70,10 @@
70
70
  .join(' ')
71
71
  );
72
72
 
73
+ const containerStyle = $derived(
74
+ overlay ? 'background: color-mix(in oklch, #180042 55%, transparent)' : ''
75
+ );
76
+
73
77
  const textClass = $derived(
74
78
  ['text-sm text-base-content/70', { top: 'mb-2', bottom: 'mt-2', left: 'mr-2', right: 'ml-2' }[textPosition]]
75
79
  .filter(Boolean)
@@ -80,7 +84,7 @@
80
84
  </script>
81
85
 
82
86
  {#if visible}
83
- <div class={containerClass} aria-label={ariaLabel} role="status" aria-live="polite">
87
+ <div class={containerClass} style={containerStyle} aria-label={ariaLabel} role="status" aria-live="polite">
84
88
  {#if text && textPosition === 'top'}
85
89
  <div class={textClass}>{text}</div>
86
90
  {/if}