@joewinke/jatui 0.1.11 → 0.1.20
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 +2 -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 +8 -3
- 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 +59 -19
- 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/GPSTracker.svelte +202 -0
- 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 +6 -2
- package/src/lib/components/InputDialog.svelte +327 -0
- package/src/lib/components/KeyboardShortcutsOverlay.svelte +296 -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/LocationMap.svelte +186 -0
- package/src/lib/components/MapView.svelte +341 -0
- 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 +1 -1
- 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/linked-columns/LinkedColumns.svelte +520 -0
- 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/components/replay/ChapterTimeline.svelte +326 -0
- package/src/lib/components/session-nav/transcriptModel.ts +352 -0
- package/src/lib/index.ts +138 -0
- 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/googleMaps.d.ts +51 -0
- package/src/lib/types/maps.ts +43 -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/googleMapsLoader.ts +84 -0
- package/src/lib/utils/taskUtils.ts +21 -7
package/src/lib/index.ts
CHANGED
|
@@ -1,9 +1,44 @@
|
|
|
1
|
+
// Actions
|
|
2
|
+
export { railNav, createRailNav, cycle } from './actions/railNav';
|
|
3
|
+
export type { RailNavOptions, RailNavController } from './actions/railNav';
|
|
4
|
+
|
|
5
|
+
// Components — Session primitives (from JAT IDE, jat-jj79f.16 G1)
|
|
6
|
+
export { default as LinkedColumns } from './components/linked-columns/LinkedColumns.svelte';
|
|
7
|
+
export type { ColumnLink } from './components/linked-columns/LinkedColumns.svelte';
|
|
8
|
+
|
|
9
|
+
export { default as ChapterTimeline } from './components/replay/ChapterTimeline.svelte';
|
|
10
|
+
export type { ChapterBlock } from './components/replay/ChapterTimeline.svelte';
|
|
11
|
+
|
|
12
|
+
// Data models — Session transcript (from JAT IDE, jat-jj79f.16 G1)
|
|
13
|
+
export {
|
|
14
|
+
buildLinkedSession,
|
|
15
|
+
buildLinkedSessionFromWork,
|
|
16
|
+
rawTypeToState,
|
|
17
|
+
sampleLinkedSession,
|
|
18
|
+
SAMPLE_TRANSCRIPT_LINES,
|
|
19
|
+
} from './components/session-nav/transcriptModel';
|
|
20
|
+
export type {
|
|
21
|
+
ItemKind,
|
|
22
|
+
TranscriptItem,
|
|
23
|
+
FileTouchedEntry,
|
|
24
|
+
SignalNode,
|
|
25
|
+
LinkedSession,
|
|
26
|
+
WorkBlockClient,
|
|
27
|
+
WorkTranscriptItemClient,
|
|
28
|
+
} from './components/session-nav/transcriptModel';
|
|
29
|
+
|
|
1
30
|
// Components — Universal
|
|
2
31
|
export { default as UserAvatar } from './components/UserAvatar.svelte';
|
|
3
32
|
export { default as SidebarUserFooter } from './components/SidebarUserFooter.svelte';
|
|
4
33
|
export { default as TaskTypeIcon } from './components/TaskTypeIcon.svelte';
|
|
5
34
|
|
|
6
35
|
// Components — from JAT IDE
|
|
36
|
+
export { default as ConfirmDialog } from './components/ConfirmDialog.svelte';
|
|
37
|
+
export { showConfirm, confirmDialog, cancelDialog, getPending } from './stores/confirmDialog.svelte';
|
|
38
|
+
export type { ConfirmOptions } from './stores/confirmDialog.svelte';
|
|
39
|
+
export { default as InputDialog } from './components/InputDialog.svelte';
|
|
40
|
+
export { showInput, submitInput, cancelInput, getPendingInput } from './stores/inputDialog.svelte';
|
|
41
|
+
export type { InputOptions } from './stores/inputDialog.svelte';
|
|
7
42
|
export { default as SearchDropdown } from './components/SearchDropdown.svelte';
|
|
8
43
|
export { default as FilterDropdown } from './components/FilterDropdown.svelte';
|
|
9
44
|
export { default as ContextMenu } from './components/ContextMenu.svelte';
|
|
@@ -13,6 +48,7 @@ export { default as DateRangePicker } from './components/DateRangePicker.svelte'
|
|
|
13
48
|
export { default as SortDropdown } from './components/SortDropdown.svelte';
|
|
14
49
|
export { default as InlineEdit } from './components/InlineEdit.svelte';
|
|
15
50
|
export { default as FloatingActionBar } from './components/FloatingActionBar.svelte';
|
|
51
|
+
export { default as HunkDiffView } from './components/HunkDiffView.svelte';
|
|
16
52
|
|
|
17
53
|
// Components — from Flush
|
|
18
54
|
export { default as Button } from './components/Button.svelte';
|
|
@@ -34,6 +70,7 @@ export { default as ColorSelector } from './components/ColorSelector.svelte';
|
|
|
34
70
|
export { default as ConfirmModal } from './components/ConfirmModal.svelte';
|
|
35
71
|
export { default as CountdownTimer } from './components/CountdownTimer.svelte';
|
|
36
72
|
export { default as ImageUpload } from './components/ImageUpload.svelte';
|
|
73
|
+
export { default as SmartImageEditor } from './components/SmartImageEditor.svelte';
|
|
37
74
|
export { default as LazyImage } from './components/LazyImage.svelte';
|
|
38
75
|
export { default as ResizablePanel } from './components/ResizablePanel.svelte';
|
|
39
76
|
export { default as VoicePlayer } from './components/VoicePlayer.svelte';
|
|
@@ -42,15 +79,31 @@ export { default as SpeechForm } from './components/SpeechForm.svelte';
|
|
|
42
79
|
// Components — from Headcount
|
|
43
80
|
export { default as ThemeSelector } from './components/ThemeSelector.svelte';
|
|
44
81
|
export { default as Sparkline } from './components/Sparkline.svelte';
|
|
82
|
+
export { default as BurndownChart } from './components/BurndownChart.svelte';
|
|
45
83
|
|
|
46
84
|
// Components — from JST
|
|
47
85
|
export { default as AvatarUpload } from './components/AvatarUpload.svelte';
|
|
48
86
|
export { default as PdfThumbnail } from './components/PdfThumbnail.svelte';
|
|
49
87
|
export { default as FilePicker } from './components/FilePicker.svelte';
|
|
88
|
+
export { default as FileDropzone } from './components/FileDropzone.svelte';
|
|
89
|
+
export { default as FileThumbnail } from './components/FileThumbnail.svelte';
|
|
90
|
+
export { default as EntityPreviewCard } from './components/EntityPreviewCard.svelte';
|
|
91
|
+
|
|
92
|
+
// EntityPreviewCard types
|
|
93
|
+
export type { EntityPreviewData, EntityPreviewStat, EntityPreviewBadge } from './types/entityPreview';
|
|
50
94
|
|
|
51
95
|
// FilePicker types
|
|
52
96
|
export type { FilePickerFile, FilePickerSelection } from './types/filePicker';
|
|
53
97
|
|
|
98
|
+
// FileDropzone types
|
|
99
|
+
export type { FileDropzoneFile } from './components/FileDropzone.svelte';
|
|
100
|
+
|
|
101
|
+
// Components — Document Templates (JST)
|
|
102
|
+
export { default as VariablePicker } from './components/VariablePicker.svelte';
|
|
103
|
+
|
|
104
|
+
// Template variable types
|
|
105
|
+
export type { TemplateVar, TemplateVarGroup } from './types/templateVars';
|
|
106
|
+
|
|
54
107
|
// Components — Emoji
|
|
55
108
|
export { default as EmojiPicker } from './components/EmojiPicker.svelte';
|
|
56
109
|
|
|
@@ -61,6 +114,9 @@ export type { EmojiEntry, EmojiGroup } from './data/emojis';
|
|
|
61
114
|
// Components — from Meadow
|
|
62
115
|
export { default as LinkShortener } from './components/LinkShortener.svelte';
|
|
63
116
|
|
|
117
|
+
// Components — Media
|
|
118
|
+
export { default as MediaWorkbench } from './components/MediaWorkbench.svelte';
|
|
119
|
+
|
|
64
120
|
// Components — from Marduk
|
|
65
121
|
export { default as SignaturePad } from './components/SignaturePad.svelte';
|
|
66
122
|
export { default as MilestoneCard } from './components/MilestoneCard.svelte';
|
|
@@ -110,6 +166,14 @@ export {
|
|
|
110
166
|
LOCATION_ICONS
|
|
111
167
|
} from './types/booking';
|
|
112
168
|
|
|
169
|
+
// SmartImageEditor types
|
|
170
|
+
export type {
|
|
171
|
+
ImageVariantSpec,
|
|
172
|
+
ImageProcessingSpec,
|
|
173
|
+
VariantResult,
|
|
174
|
+
VariantState,
|
|
175
|
+
} from './types/smartImageEditor';
|
|
176
|
+
|
|
113
177
|
// Utilities — date formatting
|
|
114
178
|
export {
|
|
115
179
|
normalizeTimestamp,
|
|
@@ -262,3 +326,77 @@ export {
|
|
|
262
326
|
countMentions,
|
|
263
327
|
setMentionUrlGenerator
|
|
264
328
|
} from './utils/mentionParser';
|
|
329
|
+
|
|
330
|
+
// Components — Comments (universal threaded comments)
|
|
331
|
+
export { default as CommentThread } from './components/CommentThread.svelte';
|
|
332
|
+
|
|
333
|
+
// Comment types
|
|
334
|
+
export type {
|
|
335
|
+
Comment,
|
|
336
|
+
CommentEntityType,
|
|
337
|
+
CommentVisibility,
|
|
338
|
+
CommentCreateInput,
|
|
339
|
+
CommentCallbacks
|
|
340
|
+
} from './types/comments';
|
|
341
|
+
export { COMMENT_ENTITY_TYPES, COMMENT_VISIBILITIES } from './types/comments';
|
|
342
|
+
|
|
343
|
+
// Components — PDF Viewer (server-side PDFium WASM rendering, no client pdf.js)
|
|
344
|
+
export { default as PDFViewer } from './components/PDFViewer.svelte';
|
|
345
|
+
|
|
346
|
+
// Components — Document annotation suite (from Steelbridge FileManager)
|
|
347
|
+
export { default as MarkupEditor } from './components/MarkupEditor.svelte';
|
|
348
|
+
export { default as MarkupOverlay } from './components/MarkupOverlay.svelte';
|
|
349
|
+
export { default as ImageLightbox } from './components/ImageLightbox.svelte';
|
|
350
|
+
export { default as AnnotationLayer } from './components/AnnotationLayer.svelte';
|
|
351
|
+
export { default as AnnotationPanel } from './components/AnnotationPanel.svelte';
|
|
352
|
+
|
|
353
|
+
// Markup drawing utilities
|
|
354
|
+
export {
|
|
355
|
+
MARKUP_COLORS,
|
|
356
|
+
DEFAULT_STROKE_WIDTH,
|
|
357
|
+
renderShape,
|
|
358
|
+
renderAllShapes,
|
|
359
|
+
nextShapeId,
|
|
360
|
+
} from './components/markup';
|
|
361
|
+
|
|
362
|
+
// Markup + annotation types
|
|
363
|
+
export type {
|
|
364
|
+
MarkupTool,
|
|
365
|
+
Point,
|
|
366
|
+
MarkupShape,
|
|
367
|
+
ArrowShape,
|
|
368
|
+
RectangleShape,
|
|
369
|
+
EllipseShape,
|
|
370
|
+
CloudShape,
|
|
371
|
+
FreehandShape,
|
|
372
|
+
TextShape,
|
|
373
|
+
} from './components/markup';
|
|
374
|
+
|
|
375
|
+
export type {
|
|
376
|
+
Annotation,
|
|
377
|
+
AnnotationProfile,
|
|
378
|
+
AnnotationThread,
|
|
379
|
+
AnnotationCallbacks,
|
|
380
|
+
} from './types/annotation';
|
|
381
|
+
|
|
382
|
+
// Components — Keyboard Navigation
|
|
383
|
+
export { default as KeyboardShortcutsOverlay } from './components/KeyboardShortcutsOverlay.svelte';
|
|
384
|
+
export type {
|
|
385
|
+
KeyboardShortcut,
|
|
386
|
+
SectionEntry as KeyboardShortcutSectionEntry,
|
|
387
|
+
ShortcutSection as KeyboardShortcutSection,
|
|
388
|
+
} from './components/KeyboardShortcutsOverlay.svelte';
|
|
389
|
+
|
|
390
|
+
// Components — Google Maps (DispatchMap fork + location primitives)
|
|
391
|
+
export { default as MapView } from './components/MapView.svelte';
|
|
392
|
+
export { default as LocationMap } from './components/LocationMap.svelte';
|
|
393
|
+
export { default as GPSTracker } from './components/GPSTracker.svelte';
|
|
394
|
+
export type { MapJob, MapAgent } from './types/maps';
|
|
395
|
+
|
|
396
|
+
// Utilities — Google Maps loader
|
|
397
|
+
export {
|
|
398
|
+
loadGoogleMapsAPI,
|
|
399
|
+
isGoogleMapsLoaded,
|
|
400
|
+
isPlacesLoaded,
|
|
401
|
+
isGeometryLoaded,
|
|
402
|
+
} from './utils/googleMapsLoader';
|
|
@@ -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,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ambient declarations for Google Maps JS API loaded at runtime.
|
|
3
|
+
* Prevents "Property 'google' does not exist on Window" TS errors.
|
|
4
|
+
* Components using the Google Maps API operate with `any` for advanced
|
|
5
|
+
* marker types until @types/google.maps is added as a dev dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare namespace google {
|
|
9
|
+
namespace maps {
|
|
10
|
+
class Map {
|
|
11
|
+
constructor(el: HTMLElement, options: object);
|
|
12
|
+
fitBounds(bounds: any): void;
|
|
13
|
+
getZoom(): number | undefined;
|
|
14
|
+
setZoom(zoom: number): void;
|
|
15
|
+
setCenter(center: object): void;
|
|
16
|
+
}
|
|
17
|
+
class InfoWindow {
|
|
18
|
+
constructor(options: object);
|
|
19
|
+
open(map: any, marker: any): void;
|
|
20
|
+
}
|
|
21
|
+
class Circle {
|
|
22
|
+
constructor(options: object);
|
|
23
|
+
setMap(map: any): void;
|
|
24
|
+
}
|
|
25
|
+
class LatLngBounds {
|
|
26
|
+
extend(position: object): void;
|
|
27
|
+
}
|
|
28
|
+
const MapTypeId: { ROADMAP: string };
|
|
29
|
+
const event: {
|
|
30
|
+
addListener(instance: any, event: string, fn: () => void): any;
|
|
31
|
+
removeListener(listener: any): void;
|
|
32
|
+
clearInstanceListeners(instance: any): void;
|
|
33
|
+
};
|
|
34
|
+
namespace marker {
|
|
35
|
+
class AdvancedMarkerElement {
|
|
36
|
+
constructor(options: object);
|
|
37
|
+
map: any;
|
|
38
|
+
position: any;
|
|
39
|
+
addListener(event: string, fn: () => void): void;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
const places: any;
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const geometry: any;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface Window {
|
|
50
|
+
google?: typeof google;
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for MapView, LocationMap, and GPSTracker components.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface MapJob {
|
|
6
|
+
id: string;
|
|
7
|
+
status: string;
|
|
8
|
+
type?: string;
|
|
9
|
+
priority?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
scheduled_at?: string;
|
|
12
|
+
customer?: {
|
|
13
|
+
name?: string;
|
|
14
|
+
phone?: string;
|
|
15
|
+
};
|
|
16
|
+
location?: {
|
|
17
|
+
address?: string;
|
|
18
|
+
latitude?: number;
|
|
19
|
+
longitude?: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MapAgent {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
avatarUrl?: string;
|
|
28
|
+
status?: string;
|
|
29
|
+
capacity?: string;
|
|
30
|
+
specializations?: string[];
|
|
31
|
+
vehicle?: { name?: string };
|
|
32
|
+
/** Most recent GPS fix */
|
|
33
|
+
location?: {
|
|
34
|
+
latitude?: number;
|
|
35
|
+
longitude?: number;
|
|
36
|
+
age_seconds?: number;
|
|
37
|
+
};
|
|
38
|
+
/** Active dispatches used as fallback location */
|
|
39
|
+
dispatches?: Array<{
|
|
40
|
+
job_id: string;
|
|
41
|
+
status: string;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
@@ -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 }
|