@joewinke/jatui 0.1.23 → 0.1.24

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
package/CLAUDE.md ADDED
@@ -0,0 +1,244 @@
1
+ # jatui — Shared Svelte 5 + DaisyUI Component Library
2
+
3
+ Reusable UI components extracted from JAT projects. Svelte 5 runes syntax, DaisyUI + Tailwind CSS v4.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install ../jatui # or "file:../jatui" in package.json
9
+ ```
10
+
11
+ Then import:
12
+ ```svelte
13
+ <script>
14
+ import { SearchDropdown, Button, Modal } from "jatui"
15
+ </script>
16
+ ```
17
+
18
+ ## Component Catalog
19
+
20
+ ### Form Inputs
21
+
22
+ | Component | Purpose | Key Props |
23
+ |-----------|---------|-----------|
24
+ | `TextInput` | Text input with label, error, icon slots | `label`, `value` (bindable), `placeholder`, `error`, `disabled` |
25
+ | `TextArea` | Multiline text input | `label`, `value` (bindable), `placeholder`, `rows`, `error`, `maxlength` |
26
+ | `SelectInput` | Dropdown select | `label`, `value` (bindable), `options: SelectOption[]`, `placeholder` |
27
+ | `PhoneInput` | Intl phone (country dropdown + tel input, outputs E.164) | `value` (bindable), `name`, `defaultCountry`, `required` |
28
+ | `ChipInput` | Tag/chip entry with suggestions | `chips` (bindable), `suggestions: SuggestionGroup[]`, `placeholder` |
29
+ | `SearchDropdown` | Searchable dropdown with grouped results | `groups: SearchDropdownGroup[]`, `placeholder`, `onSelect` |
30
+ | `FilterDropdown` | Multi-select filter dropdown | `label`, `options: string[]`, `selected: Set<string>` (bindable) |
31
+ | `SortDropdown` | Sort by column with direction | `options: SortOption[]`, `sortBy` (bindable), `sortDir` (bindable) |
32
+ | `InlineEdit` | Click-to-edit text | `value` (bindable), `segments: DisplaySegment[]`, `onSave` |
33
+ | `VariablePicker` | Browse/insert template merge vars at caret | `groups: TemplateVarGroup[]`, `targetEl`, `onInsert` |
34
+ | `CharacterCounter` | Shows chars remaining | `current: number`, `max: number` |
35
+ | `ColorSelector` | Color picker from palette | `value` (bindable), `colors: string[]` |
36
+ | `ImageUpload` | Drag-drop image upload | `value` (bindable), `accept`, `maxSize` |
37
+ | `SmartImageEditor` | AI-powered image upload with background removal and multi-variant processing pipeline. **Preferred over `ImageUpload` in editor contexts.** Two modes: simple (drop-in replacement, outputs blob URL) and editor (POST multipart to backend → variant grid). | `value` (bindable), `spec?: ImageProcessingSpec`, `uploadEndpoint?`, `onComplete?`, `accept?`, `maxSizeMb?` |
38
+ | `FileDropzone` | Multi-file/folder/ZIP drag-drop upload zone. Handles FileSystem API folder recursion, MIME/size validation, and shows a pending-file grid. Calls `onFilesSelected(files, errors)` with validated `File[]`. Accepts already-uploaded files via `uploadedFiles: FileDropzoneFile[]` for post-upload display. | `accept?: string[]`, `maxSize?`, `maxFiles?`, `multiple?`, `uploadedFiles?`, `isUploading?`, `isExtracting?`, `dropLabel?`, `dropSubtext?`, `onFilesSelected?` |
39
+ | `FileThumbnail` | File thumbnail with MIME-aware icon fallback. Shows an `<img>` for image files (with loading/error states) and typed icons for PDF, ZIP, CAD, and generic files. Size variants: `sm`, `md`, `lg`, `xl`. | `name`, `mimeType`, `url?`, `size?` |
40
+ | `SignaturePad` | Draw signature on canvas | `onSave: (dataUrl: string) => void` |
41
+
42
+ ### Scheduling & Time
43
+
44
+ | Component | Purpose | Key Props |
45
+ |-----------|---------|-----------|
46
+ | `CalendarPicker` | Month calendar with day selection | `selectedDate` (bindable), `availabilityCounts`, `onDateSelect` |
47
+ | `DateRangePicker` | Start/end date range | `selectedRange` (bindable), `onRangeChange` |
48
+ | `TimeSlotPicker` | Time slot grid selector | `slots: TimeSlot[]`, `selectedSlot` (bindable), `onSlotSelect` |
49
+ | `AvailabilityModal` | Date override editor (modal) | `date`, `overrides`, `onSave`, `onClose` |
50
+ | `BookingForm` | Guest info form for booking | `onSubmit`, `submitting`, `requirePhone`, `accentColor`, `showAttendeeOption` |
51
+ | `CountdownTimer` | Countdown to target date | `targetDate: Date`, `label` |
52
+
53
+ ### Display & Feedback
54
+
55
+ | Component | Purpose | Key Props |
56
+ |-----------|---------|-----------|
57
+ | `Button` | Styled button with variants | `variant`, `size`, `loading`, `disabled`, `onclick` |
58
+ | `Badge` | Status/label badge | `variant`, `size`, `children` (snippet) |
59
+ | `StatusBadge` | Status indicator with dot | `status: StatusValue`, `label` |
60
+ | `Card` | Content card container | `title`, `children` (snippet) |
61
+ | `Modal` | Dialog overlay | `open` (bindable), `title`, `children` (snippet) |
62
+ | `ConfirmModal` | Yes/no confirmation dialog | `open` (bindable), `title`, `message`, `onConfirm`, `onCancel` |
63
+ | `Drawer` | Slide-out side panel | `open` (bindable), `side`, `children` (snippet) |
64
+ | `LoadingSpinner` | Spinner animation | `size`, `color` |
65
+ | `Sparkline` | Inline mini chart | `data: number[]`, `width`, `height`, `color` |
66
+ | `LazyImage` | Image with lazy loading + placeholder | `src`, `alt`, `placeholder` |
67
+ | `HunkDiffView` | Side-by-side diff with line numbers, red/green backgrounds, strikethrough on removed lines, inline word-level highlights via the `diff` lib. Used for reviewing YAML/text changes per hunk. | `oldText: string \| null`, `newText: string \| null`, `unified?: boolean` |
68
+
69
+ ### Media
70
+
71
+ | Component | Purpose | Key Props |
72
+ |-----------|---------|-----------|
73
+ | `MediaWorkbench` | Full-featured image editor modal — upload, AI generate, AI edit, background removal, annotation canvas. **Preferred over `SmartImageEditor` for new image fields.** Two-column modal: preview left, 4 action tabs right (Generate, Edit, Remove BG, Annotate). Save uploads blob to `uploadEndpoint` or emits `onComplete`. Requires proxy endpoints from jst-23w1l.2. | `value` (bindable `string \| null`), `uploadEndpoint?` (default `/api/media`), `onComplete?`, `accept?` |
74
+
75
+ ### Layout
76
+
77
+ | Component | Purpose | Key Props |
78
+ |-----------|---------|-----------|
79
+ | `ResizablePanel` | Resizable container | `minWidth`, `maxWidth`, `defaultWidth` |
80
+ | `ResizableDivider` | Drag handle between panels | `onResize` |
81
+
82
+ ### Audio & Voice
83
+
84
+ | Component | Purpose | Key Props |
85
+ |-----------|---------|-----------|
86
+ | `AudioWaveform` | Audio waveform visualizer | `audioUrl`, `isPlaying` |
87
+ | `VoicePlayer` | Audio playback with controls | `src`, `autoplay` |
88
+ | `SpeechForm` | Voice-to-text form input | `onTranscript` |
89
+
90
+ ### PDF
91
+
92
+ | Component | Purpose | Key Props |
93
+ |-----------|---------|-----------|
94
+ | `PDFViewer` | Server-rendered PDF viewer — pages rendered as `<img>` via PDFium WASM (no client pdf.js). Cache-first: rendered PNGs stored in MinIO. Requires host app to expose pdf-info + pdf-page API routes (JST provides `/api/files/[id]/pdf-info` and `/api/files/[id]/pdf-page/[page]`). | `fileId`, `fileName`, `pdfUrl` (raw PDF URL for Open/Download), `infoUrl?` (custom info endpoint), `pageUrlTemplate?` (custom page template with `{page}` and `{scale}` placeholders) |
95
+
96
+ **Usage (JST default routes):**
97
+ ```svelte
98
+ <PDFViewer fileId={file.id} fileName={file.fileName} pdfUrl="/api/files/{file.id}?download=1" />
99
+ ```
100
+
101
+ **Server-side setup (JST):** `src/lib/server/pdfToImage.ts` + routes at `/api/files/[id]/pdf-info` and `/api/files/[id]/pdf-page/[page]`. Requires `@hyzyla/pdfium`, `unpdf`, `sharp` in host app.
102
+
103
+ ### Document Annotation Suite
104
+
105
+ | Component | Purpose | Key Props |
106
+ |-----------|---------|-----------|
107
+ | `MarkupEditor` | Fullscreen canvas markup tool — draw arrows, boxes, ellipses, revision clouds, freehand, text over an image | `imageUrl`, `initialShapes?: MarkupShape[]`, `onsave: (shapes) => void`, `oncancel: () => void` |
108
+ | `MarkupOverlay` | Read-only canvas layer that renders saved `MarkupShape[]` over an image | `shapes: MarkupShape[]`, `imageUrl` |
109
+ | `ImageLightbox` | Inline image viewer with fullscreen modal, zoom controls, optional markup + annotation overlays | `imageUrl`, `filename?`, `markupShapes?`, `annotations?`, `activeAnnotationId?`, `annotationMode?`, `onAnnotationClick?`, `onImageClick?`, `onClose?` |
110
+ | `AnnotationLayer` | Absolute-positioned pin overlay — renders threaded annotation pins at `x_percent`/`y_percent` coordinates | `annotations: Annotation[]`, `activeAnnotationId?`, `annotationMode?`, `showResolved?`, `onAnnotationClick?`, `onImageClick?` |
111
+ | `AnnotationPanel` | Sidebar thread list for annotations — reply, resolve, edit, delete. Uses callback props for async ops. | `annotations`, `profiles`, `activeAnnotationId?`, `currentUserId`, `callbacks: AnnotationCallbacks`, `onAnnotationSelect?`, `onAnnotationsChanged?` |
112
+
113
+ **Types & utilities:**
114
+ ```typescript
115
+ import type { MarkupShape, MarkupTool, Point, Annotation, AnnotationProfile, AnnotationCallbacks } from "jatui"
116
+ import { MARKUP_COLORS, DEFAULT_STROKE_WIDTH, renderShape, renderAllShapes, nextShapeId } from "jatui"
117
+ ```
118
+
119
+ **Usage pattern** — `AnnotationPanel` is decoupled from HTTP: pass an `AnnotationCallbacks` object:
120
+ ```typescript
121
+ const callbacks: AnnotationCallbacks = {
122
+ onAddReply: async (parentId, content) => { /* POST /api/.../annotations */ },
123
+ onResolve: async (id, resolved) => { /* PATCH */ },
124
+ onDelete: async (id) => { /* DELETE */ },
125
+ onEdit: async (id, content) => { /* PATCH */ },
126
+ }
127
+ ```
128
+
129
+ **`MarkupShape` is JSON-serializable** — store in a JSONB column and pass back as `initialShapes`.
130
+
131
+ ### Theme
132
+
133
+ | Component | Purpose | Key Props |
134
+ |-----------|---------|-----------|
135
+ | `ThemeSelector` | DaisyUI theme switcher with preview dots | `themes`, `defaultTheme`, `dropUp`, `storageKey`, `onThemeChange` |
136
+
137
+ ### Keyboard Navigation
138
+
139
+ | Component | Purpose | Key Props |
140
+ |-----------|---------|-----------|
141
+ | `KeyboardShortcutsOverlay` | `?`-triggered modal listing keyboard shortcuts. Ignores typing targets and Ctrl/Meta/Alt combos. Supports a flat `shortcuts` list or grouped `sections` with optional per-key color tinting. | `shortcuts?: KeyboardShortcut[]`, `sections?: KeyboardShortcutSection[]`, `title?`, `open` (bindable) |
142
+
143
+ ### Maps (Google Maps)
144
+
145
+ Require a `PUBLIC_GOOGLE_MAPS_API_KEY` env var passed explicitly (no SvelteKit `$env` dependency in jatui).
146
+
147
+ | Component | Purpose | Key Props |
148
+ |-----------|---------|-----------|
149
+ | `MapView` | Multi-marker dispatch map — job pins (colored by status) + agent avatars with GPS or active-job fallback position. Auto-fits bounds. | `apiKey`, `jobs?: MapJob[]`, `agents?: MapAgent[]`, `defaultCenter?`, `showLegend?`, `onJobClick?`, `onAgentClick?` |
150
+ | `LocationMap` | Single-location embed with pulsing dot + accuracy circle. Shows "Waiting for GPS signal…" while no coords. | `apiKey`, `latitude?`, `longitude?`, `accuracy?`, `showAccuracyCircle?`, `height?` |
151
+ | `GPSTracker` | Headless GPS tracker — watches device position, fires `onLocationUpdate(pos)` on each fix. Host app sends coords to server. Mobile: `watchPosition`; desktop: interval polling. Renders a small fixed status indicator. | `enabled?`, `updateInterval?`, `onLocationUpdate?`, `onError?` |
152
+
153
+ **MapView types:**
154
+ ```typescript
155
+ import type { MapJob, MapAgent } from 'jatui'
156
+ // MapJob — id, status, type?, customer?, location? (address/lat/lng)
157
+ // MapAgent — id, name, email?, avatarUrl?, status?, location? (lat/lng/age_seconds), dispatches?
158
+ ```
159
+
160
+ **Google Maps loader (util):**
161
+ ```typescript
162
+ import { loadGoogleMapsAPI, isGoogleMapsLoaded } from 'jatui'
163
+ await loadGoogleMapsAPI({ apiKey, libraries: ['marker', 'geometry'] })
164
+ ```
165
+
166
+ ## Utilities
167
+
168
+ ```typescript
169
+ // Date formatting
170
+ import { formatRelativeTime, formatDate, formatShortDate, formatDateTime } from "jatui"
171
+
172
+ // Task age — color-coded staleness indicator for task rows
173
+ // Returns { label: "3d", color: "oklch(...)" } — green→blue→amber→orange→red
174
+ import { taskAge } from "jatui"
175
+
176
+ // Task display helpers (project_tasks domain)
177
+ import { statusColor, priorityColor, typeIcon, statusLabel } from "jatui"
178
+ // statusColor(status) → DaisyUI badge class ("badge-info", "badge-warning", ...)
179
+ // priorityColor(priority) → Tailwind text class ("text-error font-bold", ...)
180
+ // typeIcon(type) → emoji ("🐛", "✨", "📋", "🏔️", "📄")
181
+ // statusLabel(status) → "in_progress" → "In Progress"
182
+
183
+ // Phone formatting
184
+ import { formatPhoneNumber, isValidPhoneNumber } from "jatui"
185
+
186
+ // Currency
187
+ import { formatCurrency } from "jatui"
188
+
189
+ // DaisyUI color access (for Canvas/JS)
190
+ import { getDaisyUIColor, isDarkTheme, getCurrentTheme } from "jatui"
191
+ ```
192
+
193
+ ## Types
194
+
195
+ ```typescript
196
+ // Component types
197
+ import type {
198
+ SearchDropdownOption, SearchDropdownGroup,
199
+ ChipSuggestion, SuggestionGroup, ChipInfo,
200
+ SortOption, SelectOption, StatusValue,
201
+ DisplaySegment
202
+ } from "jatui"
203
+
204
+ // Booking types (from src/lib/types/booking.ts)
205
+ import type {
206
+ LocationType, BookingStatus, OverrideType,
207
+ Host, EventType, WeeklyAvailability, DateOverride,
208
+ Booking, BookingAttendee, TimeSlot, BookingFormData,
209
+ AvailabilityCounts
210
+ } from "jatui"
211
+
212
+ // Booking constants
213
+ import { DAYS_OF_WEEK, DAYS_SHORT, LOCATION_LABELS, LOCATION_ICONS } from "jatui"
214
+
215
+ // FileDropzone types
216
+ import type { FileDropzoneFile } from "jatui"
217
+ // FileDropzoneFile — { file?: File, name, mimeType, size?, url?, meta? }
218
+
219
+ // SmartImageEditor types
220
+ import type {
221
+ ImageProcessingSpec, ImageVariantSpec,
222
+ VariantResult, VariantState
223
+ } from "jatui"
224
+ // ImageProcessingSpec — { removeBackground?, aspectRatio?, variants: ImageVariantSpec[] }
225
+ // ImageVariantSpec — { name, width, height, fit?, background? }
226
+ // VariantResult — { name, url, width, height, backgroundRemovalFailed? }
227
+ // VariantState — union: idle | loading | done (VariantResult) | error (message)
228
+ ```
229
+
230
+ ## Live Demos
231
+
232
+ If the project uses JST (Joe's Software Template), browse component demos at `/components/{component-name}` (kebab-case).
233
+
234
+ ## Notes for Agents
235
+
236
+ - All components use **Svelte 5 runes** (`$state`, `$derived`, `$props`, `$bindable`)
237
+ - All components use **DaisyUI classes** (not custom CSS) — they inherit the project's theme
238
+ - Bindable props use `$bindable()` — bind with `bind:value={myVar}`
239
+ - Check the component source in `src/lib/components/` for full prop types
240
+ - Prefer jatui components over building custom ones when there's a match
241
+
242
+ # Accessibility (a11y)
243
+
244
+ @~/code/jat/shared/a11y-patterns.md
package/package.json CHANGED
@@ -1,38 +1,51 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",
7
- "svelte": "./src/lib/index.ts",
8
- "types": "./src/lib/index.ts",
7
+ "svelte": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./src/lib/index.ts",
12
- "svelte": "./src/lib/index.ts",
13
- "default": "./src/lib/index.ts"
14
- }
11
+ "types": "./dist/index.d.ts",
12
+ "svelte": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./styles/rail.css": "./dist/styles/rail.css"
15
16
  },
16
17
  "files": [
17
- "src/lib"
18
+ "dist",
19
+ "CLAUDE.md",
20
+ "README.md"
18
21
  ],
19
22
  "scripts": {
20
23
  "dev": "vite dev --port 5180",
21
24
  "build": "vite build",
22
- "preview": "vite preview"
25
+ "preview": "vite preview",
26
+ "package": "svelte-package --input src/lib --output dist"
23
27
  },
24
28
  "peerDependencies": {
25
29
  "d3": "^7.0.0",
26
- "svelte": "^5.0.0"
30
+ "daisyui": "^5.0.0",
31
+ "svelte": "^5.0.0",
32
+ "tailwindcss": "^4.0.0"
27
33
  },
28
34
  "peerDependenciesMeta": {
29
35
  "d3": {
30
36
  "optional": true
37
+ },
38
+ "daisyui": {
39
+ "optional": false
40
+ },
41
+ "tailwindcss": {
42
+ "optional": false
31
43
  }
32
44
  },
33
45
  "devDependencies": {
34
46
  "@sveltejs/adapter-auto": "^6.0.0",
35
47
  "@sveltejs/kit": "^2.21.0",
48
+ "@sveltejs/package": "^2.5.8",
36
49
  "@sveltejs/vite-plugin-svelte": "^5.0.0",
37
50
  "@tailwindcss/vite": "^4.0.0",
38
51
  "@types/d3": "^7.4.3",
@@ -1,136 +0,0 @@
1
- /**
2
- * Tall-content collapse — Svelte action.
3
- *
4
- * Caps an arbitrarily-tall block at a max height with a fade-out gradient so it
5
- * shows a preview instead of overflowing its container. The consumer renders the
6
- * "Show more / Show less" toggle and owns the `expanded` boolean; the action does
7
- * the measuring and applies the collapse styling.
8
- *
9
- * Why an action (not a wrapper-only component): most adoption sites are existing
10
- * elements (`<p>`, a `prose` div, a `<ul>`, a `<pre>`), and an action attaches
11
- * with zero DOM restructuring. A batteries-included `<Collapse>` wrapper that
12
- * uses this action is also provided for greenfield use.
13
- *
14
- * ─── Why inline styles, not a CSS class ───────────────────────────────────────
15
- *
16
- * The action writes `max-height` / `overflow` / `mask-image` directly on the node
17
- * when collapsed. That means it needs NO accompanying stylesheet — it works
18
- * identically in Tailwind-utility components and scoped-CSS components, and
19
- * consumers don't have to copy a `.collapse-*` rule into their app.css (the same
20
- * trap rail.css has). Expanding clears the inline styles.
21
- *
22
- * ─── Why ResizeObserver, not a one-shot rAF ───────────────────────────────────
23
- *
24
- * Streaming chat answers and `{@html}` markdown bodies grow AFTER mount. A
25
- * one-shot measurement on mount would miss them (and would wrongly hide the
26
- * toggle on content that hasn't streamed in yet). A ResizeObserver re-measures
27
- * whenever the content's natural height changes, so `collapsible` stays correct.
28
- *
29
- * `scrollHeight` reports the FULL content height even while `max-height` +
30
- * `overflow:hidden` are applied, so the action can keep measuring without ever
31
- * having to un-collapse first.
32
- *
33
- * ─── Usage — action form (most sites) ─────────────────────────────────────────
34
- *
35
- * <script>
36
- * import { collapse } from 'jatui'
37
- * let collapsible = $state(false)
38
- * let expanded = $state(false)
39
- * </script>
40
- *
41
- * <div use:collapse={{ expanded, onCollapsible: (c) => (collapsible = c) }}>
42
- * {content}
43
- * </div>
44
- * {#if collapsible}
45
- * <button onclick={() => (expanded = !expanded)}>
46
- * {expanded ? 'Show less ↑' : 'Show more ↓'}
47
- * </button>
48
- * {/if}
49
- *
50
- * For mapped lists, give each row its own `expanded` / `collapsible` state
51
- * (e.g. a `SvelteSet` of ids) — one action instance attaches per row element.
52
- */
53
-
54
- export interface CollapseOptions {
55
- /** Max collapsed height in px. Content taller than this collapses. Default 120. */
56
- threshold?: number;
57
- /** Controlled expand state. When true, the node is never collapsed. Default false. */
58
- expanded?: boolean;
59
- /** Where the fade-to-transparent begins, as a %. Default 55. */
60
- fadeStart?: number;
61
- /** Slack (px) added to the threshold so a near-fit doesn't toggle. Default 4. */
62
- epsilon?: number;
63
- /**
64
- * Called whenever collapsibility changes (content crosses the threshold).
65
- * Drive the toggle button's visibility from this — it self-hides when the
66
- * content is shorter than the threshold.
67
- */
68
- onCollapsible?: (collapsible: boolean) => void;
69
- }
70
-
71
- const DEFAULTS = { threshold: 120, expanded: false, fadeStart: 55, epsilon: 4 } as const;
72
-
73
- export function collapse(node: HTMLElement, options: CollapseOptions = {}) {
74
- let opts = { ...DEFAULTS, ...options };
75
- let collapsible = false;
76
- let rafId = 0;
77
- let applied = false;
78
-
79
- function gradient() {
80
- return `linear-gradient(to bottom, black ${opts.fadeStart}%, transparent 100%)`;
81
- }
82
-
83
- function apply() {
84
- const shouldCollapse = collapsible && !opts.expanded;
85
- if (shouldCollapse === applied) return; // idempotent — avoids RO feedback loops
86
- applied = shouldCollapse;
87
- if (shouldCollapse) {
88
- node.style.maxHeight = `${opts.threshold}px`;
89
- node.style.overflow = 'hidden';
90
- node.style.maskImage = gradient();
91
- node.style.webkitMaskImage = gradient();
92
- } else {
93
- node.style.maxHeight = '';
94
- node.style.overflow = '';
95
- node.style.maskImage = '';
96
- node.style.webkitMaskImage = '';
97
- }
98
- }
99
-
100
- function measure() {
101
- // scrollHeight is the full content height even while collapsed.
102
- const next = node.scrollHeight > opts.threshold + opts.epsilon;
103
- if (next !== collapsible) {
104
- collapsible = next;
105
- opts.onCollapsible?.(collapsible);
106
- }
107
- apply();
108
- }
109
-
110
- function scheduleMeasure() {
111
- cancelAnimationFrame(rafId);
112
- rafId = requestAnimationFrame(measure);
113
- }
114
-
115
- // Re-measure when the content's natural size changes (streaming, {@html}, lazy images).
116
- const ro = new ResizeObserver(scheduleMeasure);
117
- ro.observe(node);
118
-
119
- scheduleMeasure();
120
-
121
- return {
122
- update(next: CollapseOptions = {}) {
123
- opts = { ...DEFAULTS, ...next };
124
- // expanded/threshold/fade may have changed — re-measure and let
125
- // apply() compare the freshly computed shouldCollapse against the
126
- // real last-applied state. (Force-flipping `applied` here used to make
127
- // that comparison spuriously match on a plain expand/collapse toggle,
128
- // silently skipping the style update.)
129
- scheduleMeasure();
130
- },
131
- destroy() {
132
- cancelAnimationFrame(rafId);
133
- ro.disconnect();
134
- },
135
- };
136
- }