@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.
- package/README.md +123 -0
- package/package.json +3 -1
- package/src/lib/actions/railNav.ts +473 -0
- package/src/lib/components/AnnotationLayer.svelte +108 -0
- package/src/lib/components/AnnotationPanel.svelte +319 -0
- package/src/lib/components/AudioWaveform.svelte +9 -5
- package/src/lib/components/AvailabilityModal.svelte +7 -3
- package/src/lib/components/AvatarUpload.svelte +27 -4
- package/src/lib/components/BookingForm.svelte +11 -9
- package/src/lib/components/BurndownChart.svelte +778 -0
- package/src/lib/components/Button.svelte +10 -1
- package/src/lib/components/CalendarPicker.svelte +3 -3
- package/src/lib/components/Card.svelte +2 -2
- package/src/lib/components/ChipInput.svelte +21 -15
- package/src/lib/components/ColorSelector.svelte +17 -13
- package/src/lib/components/CommentThread.svelte +773 -0
- package/src/lib/components/ConfirmDialog.svelte +348 -0
- package/src/lib/components/ConfirmModal.svelte +78 -11
- package/src/lib/components/ContextMenu.svelte +188 -0
- package/src/lib/components/CountdownTimer.svelte +1 -1
- package/src/lib/components/DateRangePicker.svelte +6 -4
- package/src/lib/components/Drawer.svelte +36 -3
- package/src/lib/components/EntityPreviewCard.svelte +104 -0
- package/src/lib/components/FileDropzone.svelte +493 -0
- package/src/lib/components/FilePicker.svelte +83 -14
- package/src/lib/components/FileThumbnail.svelte +80 -0
- package/src/lib/components/FilterDropdown.svelte +11 -11
- package/src/lib/components/HunkDiffView.svelte +348 -0
- package/src/lib/components/ImageLightbox.svelte +274 -0
- package/src/lib/components/ImageUpload.svelte +58 -9
- package/src/lib/components/InlineEdit.svelte +15 -9
- package/src/lib/components/InputDialog.svelte +327 -0
- package/src/lib/components/LazyImage.svelte +1 -0
- package/src/lib/components/LinkShortener.svelte +1 -1
- package/src/lib/components/LoadingSpinner.svelte +6 -2
- package/src/lib/components/MarkupEditor.svelte +485 -0
- package/src/lib/components/MarkupOverlay.svelte +55 -0
- package/src/lib/components/MediaWorkbench.svelte +871 -0
- package/src/lib/components/MilestoneCard.svelte +1 -1
- package/src/lib/components/MilestoneTimeline.svelte +1 -1
- package/src/lib/components/Modal.svelte +39 -4
- package/src/lib/components/PDFViewer.svelte +105 -0
- package/src/lib/components/PdfThumbnail.svelte +3 -1
- package/src/lib/components/PhoneInput.svelte +183 -63
- package/src/lib/components/ResizablePanel.svelte +4 -4
- package/src/lib/components/SearchDropdown.svelte +26 -13
- package/src/lib/components/SelectInput.svelte +26 -4
- package/src/lib/components/SidebarUserFooter.svelte +1 -1
- package/src/lib/components/SignaturePad.svelte +8 -4
- package/src/lib/components/SmartImageEditor.svelte +720 -0
- package/src/lib/components/SortDropdown.svelte +9 -3
- package/src/lib/components/Sparkline.svelte +9 -0
- package/src/lib/components/StatusBadge.svelte +20 -18
- package/src/lib/components/TextArea.svelte +24 -5
- package/src/lib/components/TextInput.svelte +29 -6
- package/src/lib/components/ThemeSelector.svelte +15 -4
- package/src/lib/components/TimeSlotPicker.svelte +7 -7
- package/src/lib/components/UserAvatar.svelte +14 -1
- package/src/lib/components/VariablePicker.svelte +170 -0
- package/src/lib/components/VoicePlayer.svelte +4 -3
- package/src/lib/components/markup.ts +287 -0
- package/src/lib/components/messaging/ChannelInfoModal.svelte +9 -9
- package/src/lib/components/messaging/ChannelList.svelte +1 -1
- package/src/lib/components/messaging/ChannelMembersModal.svelte +1 -1
- package/src/lib/components/messaging/CreateChannelModal.svelte +1 -1
- package/src/lib/components/messaging/DirectMessageList.svelte +1 -1
- package/src/lib/components/messaging/EmojiSelector.svelte +2 -1
- package/src/lib/components/messaging/MentionAutocomplete.svelte +1 -1
- package/src/lib/components/messaging/MessageAttachment.svelte +3 -3
- package/src/lib/components/messaging/MessageAttachmentUpload.svelte +3 -3
- package/src/lib/components/messaging/MessageInput.svelte +1 -1
- package/src/lib/components/messaging/MessageItem.svelte +6 -3
- package/src/lib/components/messaging/NotificationSettingsModal.svelte +1 -1
- package/src/lib/components/messaging/QuotedMessageDisplay.svelte +6 -1
- package/src/lib/components/messaging/StartDMModal.svelte +1 -1
- package/src/lib/components/pipeline/Pipeline.svelte +4 -4
- package/src/lib/components/pipeline/PipelineCard.svelte +1 -1
- package/src/lib/components/pipeline/PipelineColumn.svelte +8 -3
- package/src/lib/index.ts +105 -1
- package/src/lib/stores/confirmDialog.svelte.ts +48 -0
- package/src/lib/stores/inputDialog.svelte.ts +51 -0
- package/src/lib/styles/rail.css +63 -0
- package/src/lib/types/annotation.ts +38 -0
- package/src/lib/types/comments.ts +97 -0
- package/src/lib/types/entityPreview.ts +45 -0
- package/src/lib/types/filePicker.ts +2 -0
- package/src/lib/types/smartImageEditor.ts +39 -0
- package/src/lib/types/templateVars.ts +36 -0
- package/src/lib/utils/dateFormatters.ts +12 -10
- package/src/lib/utils/phone.ts +80 -0
- package/src/lib/utils/taskUtils.ts +21 -7
package/src/lib/index.ts
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
|
+
// Actions
|
|
2
|
+
export { railNav, createRailNav, cycle } from './actions/railNav';
|
|
3
|
+
export type { RailNavOptions, RailNavController } from './actions/railNav';
|
|
4
|
+
|
|
1
5
|
// Components — Universal
|
|
2
6
|
export { default as UserAvatar } from './components/UserAvatar.svelte';
|
|
3
7
|
export { default as SidebarUserFooter } from './components/SidebarUserFooter.svelte';
|
|
4
8
|
export { default as TaskTypeIcon } from './components/TaskTypeIcon.svelte';
|
|
5
9
|
|
|
6
10
|
// Components — from JAT IDE
|
|
11
|
+
export { default as ConfirmDialog } from './components/ConfirmDialog.svelte';
|
|
12
|
+
export { showConfirm, confirmDialog, cancelDialog, getPending } from './stores/confirmDialog.svelte';
|
|
13
|
+
export type { ConfirmOptions } from './stores/confirmDialog.svelte';
|
|
14
|
+
export { default as InputDialog } from './components/InputDialog.svelte';
|
|
15
|
+
export { showInput, submitInput, cancelInput, getPendingInput } from './stores/inputDialog.svelte';
|
|
16
|
+
export type { InputOptions } from './stores/inputDialog.svelte';
|
|
7
17
|
export { default as SearchDropdown } from './components/SearchDropdown.svelte';
|
|
8
18
|
export { default as FilterDropdown } from './components/FilterDropdown.svelte';
|
|
19
|
+
export { default as ContextMenu } from './components/ContextMenu.svelte';
|
|
9
20
|
export { default as ChipInput } from './components/ChipInput.svelte';
|
|
10
21
|
export { default as ResizableDivider } from './components/ResizableDivider.svelte';
|
|
11
22
|
export { default as DateRangePicker } from './components/DateRangePicker.svelte';
|
|
12
23
|
export { default as SortDropdown } from './components/SortDropdown.svelte';
|
|
13
24
|
export { default as InlineEdit } from './components/InlineEdit.svelte';
|
|
14
25
|
export { default as FloatingActionBar } from './components/FloatingActionBar.svelte';
|
|
26
|
+
export { default as HunkDiffView } from './components/HunkDiffView.svelte';
|
|
15
27
|
|
|
16
28
|
// Components — from Flush
|
|
17
29
|
export { default as Button } from './components/Button.svelte';
|
|
@@ -33,6 +45,7 @@ export { default as ColorSelector } from './components/ColorSelector.svelte';
|
|
|
33
45
|
export { default as ConfirmModal } from './components/ConfirmModal.svelte';
|
|
34
46
|
export { default as CountdownTimer } from './components/CountdownTimer.svelte';
|
|
35
47
|
export { default as ImageUpload } from './components/ImageUpload.svelte';
|
|
48
|
+
export { default as SmartImageEditor } from './components/SmartImageEditor.svelte';
|
|
36
49
|
export { default as LazyImage } from './components/LazyImage.svelte';
|
|
37
50
|
export { default as ResizablePanel } from './components/ResizablePanel.svelte';
|
|
38
51
|
export { default as VoicePlayer } from './components/VoicePlayer.svelte';
|
|
@@ -41,15 +54,31 @@ export { default as SpeechForm } from './components/SpeechForm.svelte';
|
|
|
41
54
|
// Components — from Headcount
|
|
42
55
|
export { default as ThemeSelector } from './components/ThemeSelector.svelte';
|
|
43
56
|
export { default as Sparkline } from './components/Sparkline.svelte';
|
|
57
|
+
export { default as BurndownChart } from './components/BurndownChart.svelte';
|
|
44
58
|
|
|
45
59
|
// Components — from JST
|
|
46
60
|
export { default as AvatarUpload } from './components/AvatarUpload.svelte';
|
|
47
61
|
export { default as PdfThumbnail } from './components/PdfThumbnail.svelte';
|
|
48
62
|
export { default as FilePicker } from './components/FilePicker.svelte';
|
|
63
|
+
export { default as FileDropzone } from './components/FileDropzone.svelte';
|
|
64
|
+
export { default as FileThumbnail } from './components/FileThumbnail.svelte';
|
|
65
|
+
export { default as EntityPreviewCard } from './components/EntityPreviewCard.svelte';
|
|
66
|
+
|
|
67
|
+
// EntityPreviewCard types
|
|
68
|
+
export type { EntityPreviewData, EntityPreviewStat, EntityPreviewBadge } from './types/entityPreview';
|
|
49
69
|
|
|
50
70
|
// FilePicker types
|
|
51
71
|
export type { FilePickerFile, FilePickerSelection } from './types/filePicker';
|
|
52
72
|
|
|
73
|
+
// FileDropzone types
|
|
74
|
+
export type { FileDropzoneFile } from './components/FileDropzone.svelte';
|
|
75
|
+
|
|
76
|
+
// Components — Document Templates (JST)
|
|
77
|
+
export { default as VariablePicker } from './components/VariablePicker.svelte';
|
|
78
|
+
|
|
79
|
+
// Template variable types
|
|
80
|
+
export type { TemplateVar, TemplateVarGroup } from './types/templateVars';
|
|
81
|
+
|
|
53
82
|
// Components — Emoji
|
|
54
83
|
export { default as EmojiPicker } from './components/EmojiPicker.svelte';
|
|
55
84
|
|
|
@@ -60,6 +89,9 @@ export type { EmojiEntry, EmojiGroup } from './data/emojis';
|
|
|
60
89
|
// Components — from Meadow
|
|
61
90
|
export { default as LinkShortener } from './components/LinkShortener.svelte';
|
|
62
91
|
|
|
92
|
+
// Components — Media
|
|
93
|
+
export { default as MediaWorkbench } from './components/MediaWorkbench.svelte';
|
|
94
|
+
|
|
63
95
|
// Components — from Marduk
|
|
64
96
|
export { default as SignaturePad } from './components/SignaturePad.svelte';
|
|
65
97
|
export { default as MilestoneCard } from './components/MilestoneCard.svelte';
|
|
@@ -76,6 +108,7 @@ export type { ShortenedLink } from './components/LinkShortener.svelte';
|
|
|
76
108
|
export type { SearchDropdownOption, SearchDropdownGroup } from './components/SearchDropdown.svelte';
|
|
77
109
|
export type { ChipSuggestion, SuggestionGroup, ChipInfo } from './components/ChipInput.svelte';
|
|
78
110
|
export type { SortOption } from './components/SortDropdown.svelte';
|
|
111
|
+
export type { ContextMenuItem } from './components/ContextMenu.svelte';
|
|
79
112
|
export type { DisplaySegment } from './components/InlineEdit.svelte';
|
|
80
113
|
export type { ActionButton, ActionDropdown, DropdownItem } from './components/FloatingActionBar.svelte';
|
|
81
114
|
|
|
@@ -108,6 +141,14 @@ export {
|
|
|
108
141
|
LOCATION_ICONS
|
|
109
142
|
} from './types/booking';
|
|
110
143
|
|
|
144
|
+
// SmartImageEditor types
|
|
145
|
+
export type {
|
|
146
|
+
ImageVariantSpec,
|
|
147
|
+
ImageProcessingSpec,
|
|
148
|
+
VariantResult,
|
|
149
|
+
VariantState,
|
|
150
|
+
} from './types/smartImageEditor';
|
|
151
|
+
|
|
111
152
|
// Utilities — date formatting
|
|
112
153
|
export {
|
|
113
154
|
normalizeTimestamp,
|
|
@@ -131,7 +172,7 @@ export {
|
|
|
131
172
|
statusLabel,
|
|
132
173
|
} from './utils/taskUtils';
|
|
133
174
|
|
|
134
|
-
// Utilities — phone formatting
|
|
175
|
+
// Utilities — phone formatting (legacy US-only)
|
|
135
176
|
export {
|
|
136
177
|
stripPhoneNumber,
|
|
137
178
|
formatPhoneNumber,
|
|
@@ -140,6 +181,17 @@ export {
|
|
|
140
181
|
updatePhoneInput
|
|
141
182
|
} from './utils/phoneFormat';
|
|
142
183
|
|
|
184
|
+
// Utilities — international phone (E.164) — pairs with PhoneInput
|
|
185
|
+
export {
|
|
186
|
+
toE164,
|
|
187
|
+
formatPhoneForDisplay,
|
|
188
|
+
isValidPhone,
|
|
189
|
+
listCountries,
|
|
190
|
+
DEFAULT_COUNTRY,
|
|
191
|
+
PRIORITY_COUNTRIES,
|
|
192
|
+
type CountryOption,
|
|
193
|
+
} from './utils/phone';
|
|
194
|
+
|
|
143
195
|
// Utilities — currency
|
|
144
196
|
export { formatCurrency } from './utils/currency';
|
|
145
197
|
|
|
@@ -249,3 +301,55 @@ export {
|
|
|
249
301
|
countMentions,
|
|
250
302
|
setMentionUrlGenerator
|
|
251
303
|
} from './utils/mentionParser';
|
|
304
|
+
|
|
305
|
+
// Components — Comments (universal threaded comments)
|
|
306
|
+
export { default as CommentThread } from './components/CommentThread.svelte';
|
|
307
|
+
|
|
308
|
+
// Comment types
|
|
309
|
+
export type {
|
|
310
|
+
Comment,
|
|
311
|
+
CommentEntityType,
|
|
312
|
+
CommentVisibility,
|
|
313
|
+
CommentCreateInput,
|
|
314
|
+
CommentCallbacks
|
|
315
|
+
} from './types/comments';
|
|
316
|
+
export { COMMENT_ENTITY_TYPES, COMMENT_VISIBILITIES } from './types/comments';
|
|
317
|
+
|
|
318
|
+
// Components — PDF Viewer (server-side PDFium WASM rendering, no client pdf.js)
|
|
319
|
+
export { default as PDFViewer } from './components/PDFViewer.svelte';
|
|
320
|
+
|
|
321
|
+
// Components — Document annotation suite (from Steelbridge FileManager)
|
|
322
|
+
export { default as MarkupEditor } from './components/MarkupEditor.svelte';
|
|
323
|
+
export { default as MarkupOverlay } from './components/MarkupOverlay.svelte';
|
|
324
|
+
export { default as ImageLightbox } from './components/ImageLightbox.svelte';
|
|
325
|
+
export { default as AnnotationLayer } from './components/AnnotationLayer.svelte';
|
|
326
|
+
export { default as AnnotationPanel } from './components/AnnotationPanel.svelte';
|
|
327
|
+
|
|
328
|
+
// Markup drawing utilities
|
|
329
|
+
export {
|
|
330
|
+
MARKUP_COLORS,
|
|
331
|
+
DEFAULT_STROKE_WIDTH,
|
|
332
|
+
renderShape,
|
|
333
|
+
renderAllShapes,
|
|
334
|
+
nextShapeId,
|
|
335
|
+
} from './components/markup';
|
|
336
|
+
|
|
337
|
+
// Markup + annotation types
|
|
338
|
+
export type {
|
|
339
|
+
MarkupTool,
|
|
340
|
+
Point,
|
|
341
|
+
MarkupShape,
|
|
342
|
+
ArrowShape,
|
|
343
|
+
RectangleShape,
|
|
344
|
+
EllipseShape,
|
|
345
|
+
CloudShape,
|
|
346
|
+
FreehandShape,
|
|
347
|
+
TextShape,
|
|
348
|
+
} from './components/markup';
|
|
349
|
+
|
|
350
|
+
export type {
|
|
351
|
+
Annotation,
|
|
352
|
+
AnnotationProfile,
|
|
353
|
+
AnnotationThread,
|
|
354
|
+
AnnotationCallbacks,
|
|
355
|
+
} from './types/annotation';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global promise-based confirm dialog.
|
|
3
|
+
* Usage: const ok = await showConfirm({ title: '...', body: '...' })
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type ConfirmOptions = {
|
|
7
|
+
title: string;
|
|
8
|
+
body?: string;
|
|
9
|
+
confirmLabel?: string;
|
|
10
|
+
cancelLabel?: string;
|
|
11
|
+
danger?: boolean;
|
|
12
|
+
timeoutMs?: number; // auto-cancel after this ms; 0 = no timeout (default 8000)
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type Pending = {
|
|
16
|
+
options: ConfirmOptions;
|
|
17
|
+
resolve: (result: boolean) => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
let pending = $state<Pending | null>(null);
|
|
21
|
+
|
|
22
|
+
export function getPending(): Pending | null {
|
|
23
|
+
return pending;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function showConfirm(options: ConfirmOptions): Promise<boolean> {
|
|
27
|
+
// If a dialog is already open, cancel it first.
|
|
28
|
+
if (pending) {
|
|
29
|
+
pending.resolve(false);
|
|
30
|
+
}
|
|
31
|
+
return new Promise<boolean>((resolve) => {
|
|
32
|
+
pending = { options, resolve };
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function confirmDialog(): void {
|
|
37
|
+
if (!pending) return;
|
|
38
|
+
const p = pending;
|
|
39
|
+
pending = null;
|
|
40
|
+
p.resolve(true);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function cancelDialog(): void {
|
|
44
|
+
if (!pending) return;
|
|
45
|
+
const p = pending;
|
|
46
|
+
pending = null;
|
|
47
|
+
p.resolve(false);
|
|
48
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global promise-based input dialog.
|
|
3
|
+
* Usage: const value = await showInput({ title: '...', placeholder: '...' })
|
|
4
|
+
* Returns the typed string if confirmed, null if cancelled.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type InputOptions = {
|
|
8
|
+
title: string;
|
|
9
|
+
body?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
confirmLabel?: string;
|
|
12
|
+
cancelLabel?: string;
|
|
13
|
+
timeoutMs?: number; // reserved; InputDialog never auto-cancels (user is typing)
|
|
14
|
+
validate?: (value: string) => string | null; // null = valid, string = error message
|
|
15
|
+
maxLength?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type Pending = {
|
|
19
|
+
options: InputOptions;
|
|
20
|
+
resolve: (result: string | null) => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let pending = $state<Pending | null>(null);
|
|
24
|
+
|
|
25
|
+
export function getPendingInput(): Pending | null {
|
|
26
|
+
return pending;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function showInput(options: InputOptions): Promise<string | null> {
|
|
30
|
+
// If a dialog is already open, cancel it first.
|
|
31
|
+
if (pending) {
|
|
32
|
+
pending.resolve(null);
|
|
33
|
+
}
|
|
34
|
+
return new Promise<string | null>((resolve) => {
|
|
35
|
+
pending = { options, resolve };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function submitInput(value: string): void {
|
|
40
|
+
if (!pending) return;
|
|
41
|
+
const p = pending;
|
|
42
|
+
pending = null;
|
|
43
|
+
p.resolve(value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function cancelInput(): void {
|
|
47
|
+
if (!pending) return;
|
|
48
|
+
const p = pending;
|
|
49
|
+
pending = null;
|
|
50
|
+
p.resolve(null);
|
|
51
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rail.css — keyboard rail-navigation primitives
|
|
3
|
+
*
|
|
4
|
+
* Provides focus-ring and value-change-pulse treatments for rail-nav
|
|
5
|
+
* components (vertical or horizontal keyboard-navigable lists where each
|
|
6
|
+
* rung carries a [data-rail-id] attribute).
|
|
7
|
+
*
|
|
8
|
+
* Usage: import once in your app's root layout or global stylesheet:
|
|
9
|
+
* @import '@joewinke/jatui/src/lib/styles/rail.css';
|
|
10
|
+
*
|
|
11
|
+
* Token: var(--color-primary) — DaisyUI v5 primary (resolves to the
|
|
12
|
+
* consuming app's active theme primary hue).
|
|
13
|
+
*
|
|
14
|
+
* Naming contract (must match the companion railNav Svelte action):
|
|
15
|
+
* - Active-rung focus treatment: [data-rail-id]:focus-visible selector
|
|
16
|
+
* AND .rail-focus utility class (both route to the same ring + tint).
|
|
17
|
+
* - Value-change one-shot pulse: .rail-cycled (added by the action,
|
|
18
|
+
* removed after the animation fires).
|
|
19
|
+
*
|
|
20
|
+
* Design rules enforced:
|
|
21
|
+
* - Ring + tint only — NO colored side-stripe (§6 Don't: border-l/r > 1px).
|
|
22
|
+
* - No box-shadow at rest; shadow only during the pulse keyframe.
|
|
23
|
+
* - Respects prefers-reduced-motion.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/* ─── Active-rung focus ring ───────────────────────────────────────────────
|
|
27
|
+
Applied via :focus-visible on the rung element (attribute-based, works
|
|
28
|
+
without adding a class) AND via .rail-focus (explicit class, same visual).
|
|
29
|
+
The ring is primary-tinted at 65% opacity; border-radius matches DaisyUI's
|
|
30
|
+
field radius token (falls back to 0.25rem). No side-stripe. No shadow at
|
|
31
|
+
rest (Flat-by-Default rule). ─────────────────────────────────────────── */
|
|
32
|
+
|
|
33
|
+
[data-rail-id]:focus-visible,
|
|
34
|
+
[data-rail-id]:has(:focus-visible),
|
|
35
|
+
.rail-focus {
|
|
36
|
+
outline: 2px solid color-mix(in oklch, var(--color-primary) 65%, transparent);
|
|
37
|
+
outline-offset: 2px;
|
|
38
|
+
border-radius: var(--radius-field, 0.25rem);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ─── Value-change pulse ────────────────────────────────────────────────────
|
|
42
|
+
One-shot animation applied by the railNav action when the rung's bound
|
|
43
|
+
value changes. Scales up 2.5% and blooms a brief primary-hued glow ring,
|
|
44
|
+
then returns to rest. Duration 260ms — fast enough to feel snappy,
|
|
45
|
+
long enough to register as intentional feedback.
|
|
46
|
+
Box-shadow (not outline) is used here so it can animate smoothly;
|
|
47
|
+
outline transitions are not interpolated by CSS. ─────────────────────── */
|
|
48
|
+
|
|
49
|
+
@keyframes rail-cycled {
|
|
50
|
+
0% { transform: scale(1); box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-primary) 0%, transparent); }
|
|
51
|
+
35% { transform: scale(1.025); box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 32%, transparent); }
|
|
52
|
+
100% { transform: scale(1); box-shadow: 0 0 0 0 color-mix(in oklch, var(--color-primary) 0%, transparent); }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.rail-cycled {
|
|
56
|
+
animation: rail-cycled 0.26s cubic-bezier(0.22, 1, 0.36, 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@media (prefers-reduced-motion: reduce) {
|
|
60
|
+
.rail-cycled {
|
|
61
|
+
animation: none;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Annotation types for document/image annotation suite
|
|
2
|
+
// Used by AnnotationLayer, AnnotationPanel, ImageLightbox
|
|
3
|
+
|
|
4
|
+
export interface Annotation {
|
|
5
|
+
id: string
|
|
6
|
+
user_id: string
|
|
7
|
+
x_percent: number
|
|
8
|
+
y_percent: number
|
|
9
|
+
content: string
|
|
10
|
+
parent_annotation_id: string | null
|
|
11
|
+
resolved: boolean
|
|
12
|
+
resolved_by: string | null
|
|
13
|
+
resolved_at: string | null
|
|
14
|
+
created_at: string
|
|
15
|
+
updated_at: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AnnotationProfile {
|
|
19
|
+
full_name: string | null
|
|
20
|
+
avatar_url: string | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AnnotationThread {
|
|
24
|
+
root: Annotation
|
|
25
|
+
replies: Annotation[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Callbacks for AnnotationPanel async operations */
|
|
29
|
+
export interface AnnotationCallbacks {
|
|
30
|
+
/** Post a reply to a root annotation. Must return the new annotation or throw. */
|
|
31
|
+
onAddReply: (parentId: string, content: string) => Promise<void>
|
|
32
|
+
/** Toggle resolved state. */
|
|
33
|
+
onResolve: (annotationId: string, resolved: boolean) => Promise<void>
|
|
34
|
+
/** Delete an annotation (and its replies). */
|
|
35
|
+
onDelete: (annotationId: string) => Promise<void>
|
|
36
|
+
/** Edit annotation content. */
|
|
37
|
+
onEdit: (annotationId: string, content: string) => Promise<void>
|
|
38
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Threaded Comments Types
|
|
2
|
+
// Generic types for the CommentThread UI component — no framework or backend
|
|
3
|
+
// dependencies. Mirrors the JST `$lib/types/comments` projection so the
|
|
4
|
+
// component is a drop-in for any app whose server speaks the same shape.
|
|
5
|
+
//
|
|
6
|
+
// Threading is ONE level deep on the data side: a comment is either
|
|
7
|
+
// top-level (`parent_id === null`) or a single-level reply. The UI renders
|
|
8
|
+
// this as two visual levels (a comment and its replies).
|
|
9
|
+
|
|
10
|
+
/** Entity kinds a comment thread can hang off — mirrors the host DB CHECK. */
|
|
11
|
+
export type CommentEntityType =
|
|
12
|
+
| 'project'
|
|
13
|
+
| 'invoice'
|
|
14
|
+
| 'contract'
|
|
15
|
+
| 'asset'
|
|
16
|
+
| 'task'
|
|
17
|
+
| 'form_submission'
|
|
18
|
+
| 'milestone'
|
|
19
|
+
| 'contract_term'
|
|
20
|
+
|
|
21
|
+
export const COMMENT_ENTITY_TYPES: readonly CommentEntityType[] = [
|
|
22
|
+
'project',
|
|
23
|
+
'invoice',
|
|
24
|
+
'contract',
|
|
25
|
+
'asset',
|
|
26
|
+
'task',
|
|
27
|
+
'form_submission',
|
|
28
|
+
'milestone',
|
|
29
|
+
'contract_term'
|
|
30
|
+
] as const
|
|
31
|
+
|
|
32
|
+
/** Internal = staff-only. Client = also visible in the customer portal. */
|
|
33
|
+
export type CommentVisibility = 'internal' | 'client'
|
|
34
|
+
|
|
35
|
+
export const COMMENT_VISIBILITIES: readonly CommentVisibility[] = [
|
|
36
|
+
'internal',
|
|
37
|
+
'client'
|
|
38
|
+
] as const
|
|
39
|
+
|
|
40
|
+
export interface Comment {
|
|
41
|
+
id: string
|
|
42
|
+
team_id: string
|
|
43
|
+
entity_type: CommentEntityType
|
|
44
|
+
entity_id: string
|
|
45
|
+
author_id: string
|
|
46
|
+
/** Joined author display name (null if the profile is gone). */
|
|
47
|
+
author_name: string | null
|
|
48
|
+
/** Joined author avatar URL. */
|
|
49
|
+
author_avatar_url: string | null
|
|
50
|
+
body: string
|
|
51
|
+
/** null = top-level; otherwise the id of the top-level comment it replies to. */
|
|
52
|
+
parent_id: string | null
|
|
53
|
+
visibility: CommentVisibility
|
|
54
|
+
/** Set the first time the body is edited. */
|
|
55
|
+
edited_at: string | null
|
|
56
|
+
/** Set on soft-delete; body is replaced with "[deleted]". */
|
|
57
|
+
deleted_at: string | null
|
|
58
|
+
created_at: string
|
|
59
|
+
/** Number of replies (top-level rows only; always 0 for replies). */
|
|
60
|
+
reply_count: number
|
|
61
|
+
/**
|
|
62
|
+
* Replies are loaded on demand; this is [] in the paginated top-level
|
|
63
|
+
* listing and populated by the component when a thread is expanded.
|
|
64
|
+
*/
|
|
65
|
+
replies: Comment[]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface CommentCreateInput {
|
|
69
|
+
entityType: CommentEntityType
|
|
70
|
+
entityId: string
|
|
71
|
+
body: string
|
|
72
|
+
/** When set, this comment is a reply to the given TOP-LEVEL comment. */
|
|
73
|
+
parentId?: string | null
|
|
74
|
+
/** Defaults to "internal". Clamped to the parent's visibility for replies. */
|
|
75
|
+
visibility?: CommentVisibility
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Transport-agnostic data layer for `<CommentThread>`. The consuming app
|
|
80
|
+
* wires these to a server load / form action / fetch endpoint. The
|
|
81
|
+
* component never imports server code or knows how data is fetched.
|
|
82
|
+
*/
|
|
83
|
+
export interface CommentCallbacks {
|
|
84
|
+
/** Persist a new comment (top-level or reply) and return the created row. */
|
|
85
|
+
submitComment: (input: CommentCreateInput) => Promise<Comment>
|
|
86
|
+
/** Edit an existing comment body; returns the updated row. */
|
|
87
|
+
editComment: (id: string, body: string) => Promise<Comment>
|
|
88
|
+
/** Soft-delete a comment. The row stays so replies remain anchored. */
|
|
89
|
+
deleteComment: (id: string) => Promise<void>
|
|
90
|
+
/** Fetch the replies for a top-level comment (oldest-first). */
|
|
91
|
+
loadReplies: (parentId: string) => Promise<Comment[]>
|
|
92
|
+
/**
|
|
93
|
+
* Optional: fetch the next page of top-level comments. When omitted, the
|
|
94
|
+
* "Load more" affordance is hidden.
|
|
95
|
+
*/
|
|
96
|
+
loadMore?: (offset: number) => Promise<{ comments: Comment[]; total: number }>
|
|
97
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity preview card — the uniform shape an EntityPreviewCard renders and that
|
|
3
|
+
* a server-side resolver returns for any domain entity. One shape for every
|
|
4
|
+
* entity type so the card and hover-action stay generic and the same card backs
|
|
5
|
+
* hover-previews, @-mention unfurls, and link previews.
|
|
6
|
+
*
|
|
7
|
+
* Pure types — safe to import from both server (the resolver) and client (the
|
|
8
|
+
* card + action).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** A small labelled stat shown in the card's footer grid (e.g. "Submissions" → 42). */
|
|
12
|
+
export interface EntityPreviewStat {
|
|
13
|
+
label: string
|
|
14
|
+
value: string | number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A coloured pill (status, type, …). `tone` maps to a DaisyUI semantic token. */
|
|
18
|
+
export interface EntityPreviewBadge {
|
|
19
|
+
label: string
|
|
20
|
+
tone?: "neutral" | "primary" | "info" | "success" | "warning" | "error"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface EntityPreviewData {
|
|
24
|
+
/** Canonical entity type slug (e.g. "base", "vendor", "form", "task"). */
|
|
25
|
+
type: string
|
|
26
|
+
id: string
|
|
27
|
+
/** Human label for the type, shown as the card's kicker (e.g. "Knowledge base"). */
|
|
28
|
+
typeLabel: string
|
|
29
|
+
/** Main title. */
|
|
30
|
+
title: string
|
|
31
|
+
/** Optional one-line subtitle under the title (e.g. a vendor's company). */
|
|
32
|
+
subtitle?: string | null
|
|
33
|
+
/** Optional longer summary / description (clamped in the card). */
|
|
34
|
+
summary?: string | null
|
|
35
|
+
/** og:image / avatar / thumbnail URL, when the entity has one. */
|
|
36
|
+
imageUrl?: string | null
|
|
37
|
+
/** A short emoji/text glyph fallback when there's no image. */
|
|
38
|
+
icon?: string | null
|
|
39
|
+
/** Up to ~4 labelled stats. */
|
|
40
|
+
stats?: EntityPreviewStat[]
|
|
41
|
+
/** Up to ~3 status/type pills. */
|
|
42
|
+
badges?: EntityPreviewBadge[]
|
|
43
|
+
/** The admin detail route for the entity (the card links here). */
|
|
44
|
+
href?: string | null
|
|
45
|
+
}
|
|
@@ -5,6 +5,8 @@ export interface FilePickerFile {
|
|
|
5
5
|
updated_at: string
|
|
6
6
|
metadata: Record<string, any>
|
|
7
7
|
scope: "personal" | "team"
|
|
8
|
+
/** Publicly accessible URL (no auth required). When present, used instead of the /api/files proxy URL. */
|
|
9
|
+
publicUrl?: string
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export interface FilePickerSelection {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ImageVariantSpec {
|
|
2
|
+
name: string
|
|
3
|
+
width: number
|
|
4
|
+
height: number
|
|
5
|
+
fit?: "contain" | "cover" | "fill"
|
|
6
|
+
/** CSS hex color or "transparent". Transparent background when omitted. */
|
|
7
|
+
background?: string
|
|
8
|
+
/**
|
|
9
|
+
* Optional Gemini edit instruction run on the (background-removed) source
|
|
10
|
+
* before Sharp's deterministic resize. Used to intelligently re-frame the
|
|
11
|
+
* variant — e.g. crop a logo down to just the brand mark, or compose an OG
|
|
12
|
+
* card. Falls back to Sharp-only if the Gemini call fails.
|
|
13
|
+
*/
|
|
14
|
+
aiPrompt?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ImageProcessingSpec {
|
|
18
|
+
removeBackground?: boolean
|
|
19
|
+
aspectRatio?: string
|
|
20
|
+
variants: ImageVariantSpec[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface VariantResult {
|
|
24
|
+
name: string
|
|
25
|
+
url: string
|
|
26
|
+
width: number
|
|
27
|
+
height: number
|
|
28
|
+
backgroundRemovalFailed?: boolean
|
|
29
|
+
/** True when this variant had an aiPrompt but the Gemini edit call failed
|
|
30
|
+
* and we fell back to Sharp-only. The result is still uploaded; this is a
|
|
31
|
+
* non-blocking signal that the smart re-frame didn't happen. */
|
|
32
|
+
aiEditFailed?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type VariantState =
|
|
36
|
+
| { status: "idle" }
|
|
37
|
+
| { status: "loading" }
|
|
38
|
+
| { status: "done"; result: VariantResult }
|
|
39
|
+
| { status: "error"; message: string }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template variable types — shared shape between the JST server-side registry
|
|
3
|
+
* (`src/lib/server/template-vars.ts`, `getVarsForCategory`) and the
|
|
4
|
+
* VariablePicker component.
|
|
5
|
+
*
|
|
6
|
+
* jatui cannot import the host app's `$lib/server` module (server-only), so
|
|
7
|
+
* these types are mirrored here. They are kept structurally identical to the
|
|
8
|
+
* registry's output so `getVarsForCategory(category, availableVars)` can be
|
|
9
|
+
* passed straight into `<VariablePicker groups={...} />` with no mapping —
|
|
10
|
+
* same jst↔jatui structural-compatibility pattern used by CommentThread.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** A single insertable merge variable. */
|
|
14
|
+
export interface TemplateVar {
|
|
15
|
+
/**
|
|
16
|
+
* Dotted variable path inserted into the template as `{{name}}`.
|
|
17
|
+
* e.g. `contact.name`, `org.logo_url`, `total`.
|
|
18
|
+
*/
|
|
19
|
+
name: string
|
|
20
|
+
/** Human label shown in the picker, e.g. "Contact name". */
|
|
21
|
+
label: string
|
|
22
|
+
/** Optional one-line help text describing the value. */
|
|
23
|
+
description?: string
|
|
24
|
+
/** Optional sample value shown as a hint, e.g. "Acme Inc.". */
|
|
25
|
+
example?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Variable context group, e.g. all client/org/financial/custom vars. */
|
|
29
|
+
export interface TemplateVarGroup {
|
|
30
|
+
/** Stable group id — matches VarGroupId in the host app's template-vars registry. */
|
|
31
|
+
id: string
|
|
32
|
+
/** Group heading shown in the picker, e.g. "Client". */
|
|
33
|
+
label: string
|
|
34
|
+
/** Variables in this group (never empty — empty groups are omitted). */
|
|
35
|
+
vars: TemplateVar[]
|
|
36
|
+
}
|